ALSA: hda: Move common codec driver into sound/hda/common directory
authorTakashi Iwai <tiwai@suse.de>
Wed, 9 Jul 2025 16:04:06 +0000 (18:04 +0200)
committerTakashi Iwai <tiwai@suse.de>
Fri, 11 Jul 2025 07:55:37 +0000 (09:55 +0200)
The snd-hda-codec module contains the most of common code used by both
HD-audio controller and codec drivers, and it's basically independent
from PCI.  Let's move the code to sound/hda/common directory as a part
of code reorganization.

The hda_ prefix is dropped from the most of file names as it's rather
superfluous.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
Link: https://patch.msgid.link/20250709160434.1859-4-tiwai@suse.de
36 files changed:
sound/hda/Kconfig
sound/hda/Makefile
sound/hda/common/Kconfig [new file with mode: 0644]
sound/hda/common/Makefile [new file with mode: 0644]
sound/hda/common/auto_parser.c [new file with mode: 0644]
sound/hda/common/beep.c [new file with mode: 0644]
sound/hda/common/bind.c [new file with mode: 0644]
sound/hda/common/codec.c [new file with mode: 0644]
sound/hda/common/controller.c [new file with mode: 0644]
sound/hda/common/controller_trace.h [new file with mode: 0644]
sound/hda/common/hda_auto_parser.h [new file with mode: 0644]
sound/hda/common/hda_beep.h [new file with mode: 0644]
sound/hda/common/hda_controller.h [new file with mode: 0644]
sound/hda/common/hda_jack.h [new file with mode: 0644]
sound/hda/common/hda_local.h [new file with mode: 0644]
sound/hda/common/hwdep.c [new file with mode: 0644]
sound/hda/common/jack.c [new file with mode: 0644]
sound/hda/common/proc.c [new file with mode: 0644]
sound/hda/common/sysfs.c [new file with mode: 0644]
sound/pci/hda/Kconfig
sound/pci/hda/Makefile
sound/pci/hda/hda_auto_parser.c [deleted file]
sound/pci/hda/hda_auto_parser.h [deleted file]
sound/pci/hda/hda_beep.c [deleted file]
sound/pci/hda/hda_beep.h [deleted file]
sound/pci/hda/hda_bind.c [deleted file]
sound/pci/hda/hda_codec.c [deleted file]
sound/pci/hda/hda_controller.c [deleted file]
sound/pci/hda/hda_controller.h [deleted file]
sound/pci/hda/hda_controller_trace.h [deleted file]
sound/pci/hda/hda_hwdep.c [deleted file]
sound/pci/hda/hda_jack.c [deleted file]
sound/pci/hda/hda_jack.h [deleted file]
sound/pci/hda/hda_local.h [deleted file]
sound/pci/hda/hda_proc.c [deleted file]
sound/pci/hda/hda_sysfs.c [deleted file]

index e380146560772f0db18cb1a3c9812b4b275a4548..2928cb570ec635f1da3cfddd33a880e77ae0c715 100644 (file)
@@ -1,2 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
+menu "HD-Audio"
+
+source "sound/hda/common/Kconfig"
 source "sound/hda/core/Kconfig"
+
+endmenu
index 3fdbc22b153041bd514a9651d81312dc9f03dff1..e7596bf736a467f821bc7b604b5627c8c33eae65 100644 (file)
@@ -1,2 +1,3 @@
 # SPDX-License-Identifier: GPL-2.0
 obj-y += core/
+obj-$(CONFIG_SND_HDA) += common/
diff --git a/sound/hda/common/Kconfig b/sound/hda/common/Kconfig
new file mode 100644 (file)
index 0000000..d1e2bfd
--- /dev/null
@@ -0,0 +1,80 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config SND_HDA
+       tristate
+       select SND_PCM
+       select SND_VMASTER
+       select SND_JACK
+       select SND_HDA_CORE
+
+if SND_HDA
+
+config SND_HDA_HWDEP
+       bool "Build hwdep interface for HD-audio driver"
+       select SND_HWDEP
+       help
+         Say Y here to build a hwdep interface for HD-audio driver.
+         This interface can be used for out-of-band communication
+         with codecs for debugging purposes.
+
+config SND_HDA_RECONFIG
+       bool "Allow dynamic codec reconfiguration"
+       help
+         Say Y here to enable the HD-audio codec re-configuration feature.
+         It allows user to clear the whole codec configuration, change the
+         codec setup, add extra verbs, and re-configure the codec dynamically.
+
+         Note that this item alone doesn't provide the sysfs interface, but
+         enables the feature just for the patch loader below.
+         If you need the traditional sysfs entries for the manual interaction,
+         turn on CONFIG_SND_HDA_HWDEP as well.
+
+config SND_HDA_INPUT_BEEP
+       bool "Support digital beep via input layer"
+       depends on INPUT=y || INPUT=SND_HDA
+       help
+         Say Y here to build a digital beep interface for HD-audio
+         driver. This interface is used to generate digital beeps.
+
+config SND_HDA_INPUT_BEEP_MODE
+       int "Digital beep registration mode (0=off, 1=on)"
+       depends on SND_HDA_INPUT_BEEP=y
+       default "1"
+       range 0 1
+       help
+         Set 0 to disable the digital beep interface for HD-audio by default.
+         Set 1 to always enable the digital beep interface for HD-audio by
+         default.
+
+config SND_HDA_PATCH_LOADER
+       bool "Support initialization patch loading for HD-audio"
+       select FW_LOADER
+       select SND_HDA_RECONFIG
+       help
+         Say Y here to allow the HD-audio driver to load a pseudo
+         firmware file ("patch") for overriding the BIOS setup at
+         start up.  The "patch" file can be specified via patch module
+         option, such as patch=hda-init.
+
+config SND_HDA_POWER_SAVE_DEFAULT
+       int "Default time-out for HD-audio power-save mode"
+       depends on PM
+       default 0
+       help
+         The default time-out value in seconds for HD-audio automatic
+         power-save mode.  0 means to disable the power-save mode.
+
+config SND_HDA_CTL_DEV_ID
+       bool "Use the device identifier field for controls"
+       depends on SND_HDA_INTEL
+       help
+         Say Y to use the device identifier field for (mixer)
+         controls (old behaviour until this option is available).
+
+         When enabled, the multiple HDA codecs may set the device
+         field in control (mixer) element identifiers. The use
+         of this field is not recommended and defined for mixer controls.
+
+         The old behaviour (Y) is obsolete and will be removed. Consider
+         to not enable this option.
+endif
diff --git a/sound/hda/common/Makefile b/sound/hda/common/Makefile
new file mode 100644 (file)
index 0000000..3344fa0
--- /dev/null
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
+snd-hda-codec-y := bind.o codec.o jack.o auto_parser.o sysfs.o
+snd-hda-codec-y += controller.o
+snd-hda-codec-$(CONFIG_SND_PROC_FS) += proc.o
+
+snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hwdep.o
+snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += beep.o
+
+# for trace-points
+CFLAGS_controller.o := -I$(src)
+
+# common driver
+obj-$(CONFIG_SND_HDA) := snd-hda-codec.o
diff --git a/sound/hda/common/auto_parser.c b/sound/hda/common/auto_parser.c
new file mode 100644 (file)
index 0000000..8923813
--- /dev/null
@@ -0,0 +1,1104 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * BIOS auto-parser helper functions for HD-audio
+ *
+ * Copyright (c) 2012 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <linux/sort.h>
+#include <sound/core.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+
+/*
+ * 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;
+}
+
+/* a pair of input pin and its sequence */
+struct auto_out_pin {
+       hda_nid_t pin;
+       short seq;
+};
+
+static int compare_seq(const void *ap, const void *bp)
+{
+       const struct auto_out_pin *a = ap;
+       const struct auto_out_pin *b = bp;
+       return (int)(a->seq - b->seq);
+}
+
+/*
+ * Sort an associated group of pins according to their sequence numbers.
+ * then store it to a pin array.
+ */
+static void sort_pins_by_sequence(hda_nid_t *pins, struct auto_out_pin *list,
+                                 int num_pins)
+{
+       int i;
+       sort(list, num_pins, sizeof(list[0]), compare_seq, NULL);
+       for (i = 0; i < num_pins; i++)
+               pins[i] = list[i].pin;
+}
+
+
+/* add the found input-pin to the cfg->inputs[] table */
+static void add_auto_cfg_input_pin(struct hda_codec *codec, 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->inputs[cfg->num_inputs].has_boost_on_pin =
+                       nid_has_volume(codec, nid, HDA_INPUT);
+               cfg->num_inputs++;
+       }
+}
+
+static int compare_input_type(const void *ap, const void *bp)
+{
+       const struct auto_pin_cfg_item *a = ap;
+       const struct auto_pin_cfg_item *b = bp;
+       if (a->type != b->type)
+               return (int)(a->type - b->type);
+
+       /* If has both hs_mic and hp_mic, pick the hs_mic ahead of hp_mic. */
+       if (a->is_headset_mic && b->is_headphone_mic)
+               return -1; /* don't swap */
+       else if (a->is_headphone_mic && b->is_headset_mic)
+               return 1; /* swap */
+
+       /* In case one has boost and the other one has not,
+          pick the one with boost first. */
+       if (a->has_boost_on_pin != b->has_boost_on_pin)
+               return (int)(b->has_boost_on_pin - a->has_boost_on_pin);
+
+       /* Keep the original order */
+       return a->order - b->order;
+}
+
+/* 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)
+{
+       switch (nums) {
+       case 3:
+       case 4:
+               swap(pins[1], pins[2]);
+               break;
+       }
+}
+
+/* check whether the given pin has a proper pin I/O capability bit */
+static bool check_pincap_validity(struct hda_codec *codec, hda_nid_t pin,
+                                 unsigned int dev)
+{
+       unsigned int pincap = snd_hda_query_pin_caps(codec, pin);
+
+       /* some old hardware don't return the proper pincaps */
+       if (!pincap)
+               return true;
+
+       switch (dev) {
+       case AC_JACK_LINE_OUT:
+       case AC_JACK_SPEAKER:
+       case AC_JACK_HP_OUT:
+       case AC_JACK_SPDIF_OUT:
+       case AC_JACK_DIG_OTHER_OUT:
+               return !!(pincap & AC_PINCAP_OUT);
+       default:
+               return !!(pincap & AC_PINCAP_IN);
+       }
+}
+
+static bool can_be_headset_mic(struct hda_codec *codec,
+                              struct auto_pin_cfg_item *item,
+                              int seq_number)
+{
+       int attr;
+       unsigned int def_conf;
+       if (item->type != AUTO_PIN_MIC)
+               return false;
+
+       if (item->is_headset_mic || item->is_headphone_mic)
+               return false; /* Already assigned */
+
+       def_conf = snd_hda_codec_get_pincfg(codec, item->pin);
+       attr = snd_hda_get_input_pin_attr(def_conf);
+       if (attr <= INPUT_PIN_ATTR_DOCK)
+               return false;
+
+       if (seq_number >= 0) {
+               int seq = get_defcfg_sequence(def_conf);
+               if (seq != seq_number)
+                       return false;
+       }
+
+       return true;
+}
+
+/*
+ * 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;
+       short seq, assoc_line_out;
+       struct auto_out_pin line_out[ARRAY_SIZE(cfg->line_out_pins)];
+       struct auto_out_pin speaker_out[ARRAY_SIZE(cfg->speaker_pins)];
+       struct auto_out_pin hp_out[ARRAY_SIZE(cfg->hp_pins)];
+       int i;
+
+       if (!snd_hda_get_int_hint(codec, "parser_flags", &i))
+               cond_flags = i;
+
+       memset(cfg, 0, sizeof(*cfg));
+
+       memset(line_out, 0, sizeof(line_out));
+       memset(speaker_out, 0, sizeof(speaker_out));
+       memset(hp_out, 0, sizeof(hp_out));
+       assoc_line_out = 0;
+
+       for_each_hda_codec_node(nid, codec) {
+               unsigned int wid_caps = get_wcaps(codec, nid);
+               unsigned int wid_type = get_wcaps_type(wid_caps);
+               unsigned int def_conf;
+               short assoc, loc, 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);
+               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 ||
+                           conn == AC_JACK_PORT_BOTH)
+                               dev = AC_JACK_SPEAKER;
+               }
+
+               if (!check_pincap_validity(codec, nid, dev))
+                       continue;
+
+               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) {
+                               codec_info(codec,
+                                          "ignore pin 0x%x with mismatching assoc# 0x%x vs 0x%x\n",
+                                          nid, assoc, assoc_line_out);
+                               continue;
+                       }
+                       if (cfg->line_outs >= ARRAY_SIZE(cfg->line_out_pins)) {
+                               codec_info(codec,
+                                          "ignore pin 0x%x, too many assigned pins\n",
+                                          nid);
+                               continue;
+                       }
+                       line_out[cfg->line_outs].pin = nid;
+                       line_out[cfg->line_outs].seq = 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)) {
+                               codec_info(codec,
+                                          "ignore pin 0x%x, too many assigned pins\n",
+                                          nid);
+                               continue;
+                       }
+                       speaker_out[cfg->speaker_outs].pin = nid;
+                       speaker_out[cfg->speaker_outs].seq = (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)) {
+                               codec_info(codec,
+                                          "ignore pin 0x%x, too many assigned pins\n",
+                                          nid);
+                               continue;
+                       }
+                       hp_out[cfg->hp_outs].pin = nid;
+                       hp_out[cfg->hp_outs].seq = (assoc << 4) | seq;
+                       cfg->hp_outs++;
+                       break;
+               case AC_JACK_MIC_IN:
+                       add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_MIC);
+                       break;
+               case AC_JACK_LINE_IN:
+                       add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_LINE_IN);
+                       break;
+               case AC_JACK_CD:
+                       add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_CD);
+                       break;
+               case AC_JACK_AUX:
+                       add_auto_cfg_input_pin(codec, 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)) {
+                               codec_info(codec,
+                                          "ignore pin 0x%x, too many assigned pins\n",
+                                          nid);
+                               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;
+               }
+       }
+
+       /* Find a pin that could be a headset or headphone mic */
+       if (cond_flags & HDA_PINCFG_HEADSET_MIC || cond_flags & HDA_PINCFG_HEADPHONE_MIC) {
+               bool hsmic = !!(cond_flags & HDA_PINCFG_HEADSET_MIC);
+               bool hpmic = !!(cond_flags & HDA_PINCFG_HEADPHONE_MIC);
+               for (i = 0; (hsmic || hpmic) && (i < cfg->num_inputs); i++)
+                       if (hsmic && can_be_headset_mic(codec, &cfg->inputs[i], 0xc)) {
+                               cfg->inputs[i].is_headset_mic = 1;
+                               hsmic = false;
+                       } else if (hpmic && can_be_headset_mic(codec, &cfg->inputs[i], 0xd)) {
+                               cfg->inputs[i].is_headphone_mic = 1;
+                               hpmic = false;
+                       }
+
+               /* If we didn't find our sequence number mark, fall back to any sequence number */
+               for (i = 0; (hsmic || hpmic) && (i < cfg->num_inputs); i++) {
+                       if (!can_be_headset_mic(codec, &cfg->inputs[i], -1))
+                               continue;
+                       if (hsmic) {
+                               cfg->inputs[i].is_headset_mic = 1;
+                               hsmic = false;
+                       } else if (hpmic) {
+                               cfg->inputs[i].is_headphone_mic = 1;
+                               hpmic = false;
+                       }
+               }
+
+               if (hsmic)
+                       codec_dbg(codec, "Told to look for a headset mic, but didn't find any.\n");
+               if (hpmic)
+                       codec_dbg(codec, "Told to look for a headphone mic, but didn't find any.\n");
+       }
+
+       /* 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)) {
+               i = 0;
+               while (i < cfg->hp_outs) {
+                       /* The real HPs should have the sequence 0x0f */
+                       if ((hp_out[i].seq & 0x0f) == 0x0f) {
+                               i++;
+                               continue;
+                       }
+                       /* Move it to the line-out table */
+                       line_out[cfg->line_outs++] = hp_out[i];
+                       cfg->hp_outs--;
+                       memmove(hp_out + i, hp_out + i + 1,
+                               sizeof(hp_out[0]) * (cfg->hp_outs - i));
+               }
+               memset(hp_out + cfg->hp_outs, 0,
+                      sizeof(hp_out[0]) * (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, line_out, cfg->line_outs);
+       sort_pins_by_sequence(cfg->speaker_pins, speaker_out,
+                             cfg->speaker_outs);
+       sort_pins_by_sequence(cfg->hp_pins, hp_out, 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 inputs in the order of AUTO_PIN_* type */
+       for (i = 0; i < cfg->num_inputs; i++)
+               cfg->inputs[i].order = i;
+       sort(cfg->inputs, cfg->num_inputs, sizeof(cfg->inputs[0]),
+            compare_input_type, NULL);
+
+       /*
+        * debug prints of the parsed results
+        */
+       codec_info(codec, "autoconfig for %s: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x) type:%s\n",
+                  codec->core.chip_name, 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"));
+       codec_info(codec, "   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]);
+       codec_info(codec, "   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]);
+       codec_info(codec, "   mono: mono_out=0x%x\n", cfg->mono_out_pin);
+       if (cfg->dig_outs)
+               codec_info(codec, "   dig-out=0x%x/0x%x\n",
+                          cfg->dig_out_pins[0], cfg->dig_out_pins[1]);
+       codec_info(codec, "   inputs:\n");
+       for (i = 0; i < cfg->num_inputs; i++) {
+               codec_info(codec, "     %s=0x%x\n",
+                           hda_get_autocfg_input_label(codec, cfg, i),
+                           cfg->inputs[i].pin);
+       }
+       if (cfg->dig_in_pin)
+               codec_info(codec, "   dig-in=0x%x\n", cfg->dig_in_pin);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_parse_pin_defcfg);
+
+/**
+ * snd_hda_get_input_pin_attr - Get the input pin attribute from pin config
+ * @def_conf: pin configuration value
+ *
+ * Guess the input pin attribute (INPUT_PIN_ATTR_XXX) from the given
+ * default pin configuration value.
+ */
+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_GPL(snd_hda_get_input_pin_attr);
+
+/**
+ * hda_get_input_pin_label - Give a label for the given input pin
+ * @codec: the HDA codec
+ * @item: ping config item to refer
+ * @pin: the pin NID
+ * @check_location: flag to add the jack location prefix
+ *
+ * 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,
+                                          const struct auto_pin_cfg_item *item,
+                                          hda_nid_t pin, bool check_location)
+{
+       unsigned int def_conf;
+       static const char * const mic_names[] = {
+               "Internal Mic", "Dock Mic", "Mic", "Rear Mic", "Front Mic"
+       };
+       int attr;
+
+       def_conf = snd_hda_codec_get_pincfg(codec, pin);
+
+       switch (get_defcfg_device(def_conf)) {
+       case AC_JACK_MIC_IN:
+               if (item && item->is_headset_mic)
+                       return "Headset Mic";
+               if (item && item->is_headphone_mic)
+                       return "Headphone Mic";
+               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";
+       case AC_JACK_HP_OUT:
+               return "Headphone Mic";
+       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
+ * @codec: the HDA codec
+ * @cfg: the parsed pin configuration
+ * @input: the input index number
+ *
+ * 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);
+       has_multiple_pins |= codec->force_pin_prefix;
+       return hda_get_input_pin_label(codec, &cfg->inputs[input],
+                                      cfg->inputs[input].pin,
+                                      has_multiple_pins);
+}
+EXPORT_SYMBOL_GPL(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 const char *check_output_pfx(struct hda_codec *codec, hda_nid_t nid)
+{
+       unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+       int attr = snd_hda_get_input_pin_attr(def_conf);
+
+       /* check the location */
+       switch (attr) {
+       case INPUT_PIN_ATTR_DOCK:
+               return "Dock ";
+       case INPUT_PIN_ATTR_FRONT:
+               return "Front ";
+       }
+       return "";
+}
+
+static int get_hp_label_index(struct hda_codec *codec, hda_nid_t nid,
+                             const hda_nid_t *pins, int num_pins)
+{
+       int i, j, idx = 0;
+
+       const char *pfx = check_output_pfx(codec, nid);
+
+       i = find_idx_in_nid_list(nid, pins, num_pins);
+       if (i < 0)
+               return -1;
+       for (j = 0; j < i; j++)
+               if (pfx == check_output_pfx(codec, pins[j]))
+                       idx++;
+
+       return idx;
+}
+
+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";
+       pfx = check_output_pfx(codec, nid);
+
+       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 = get_hp_label_index(codec, nid, cfg->hp_pins,
+                                                    cfg->hp_outs);
+                       if (idx >= 0 && indexp)
+                               *indexp = idx;
+                       sfx = "";
+               }
+       }
+       snprintf(label, maxlen, "%s%s%s", pfx, name, sfx);
+       return 1;
+}
+
+#define is_hdmi_cfg(conf) \
+       (get_defcfg_location(conf) == AC_JACK_LOC_HDMI)
+
+/**
+ * snd_hda_get_pin_label - Get a label for the given I/O pin
+ * @codec: the HDA codec
+ * @nid: pin NID
+ * @cfg: the parsed pin configuration
+ * @label: the string buffer to store
+ * @maxlen: the max length of string buffer (including termination)
+ * @indexp: the pointer to return the index number (for multiple ctls)
+ *
+ * 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;
+       bool hdmi;
+
+       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:
+               hdmi = is_hdmi_cfg(def_conf);
+               name = hdmi ? "HDMI" : "SPDIF";
+               if (cfg && indexp)
+                       for (i = 0; i < cfg->dig_outs; i++) {
+                               hda_nid_t pin = cfg->dig_out_pins[i];
+                               unsigned int c;
+                               if (pin == nid)
+                                       break;
+                               c = snd_hda_codec_get_pincfg(codec, pin);
+                               if (hdmi == is_hdmi_cfg(c))
+                                       (*indexp)++;
+                       }
+               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, NULL, nid, true);
+               break;
+       }
+       if (!name)
+               return 0;
+       strscpy(label, name, maxlen);
+       return 1;
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_pin_label);
+
+/**
+ * snd_hda_add_verbs - Add verbs to the init list
+ * @codec: the HDA codec
+ * @list: zero-terminated verb list to add
+ *
+ * Append the given verb list to the execution list.  The verbs will be
+ * performed at init and resume time via snd_hda_apply_verbs().
+ */
+int snd_hda_add_verbs(struct hda_codec *codec,
+                     const struct hda_verb *list)
+{
+       const struct hda_verb **v;
+       v = snd_array_new(&codec->verbs);
+       if (!v)
+               return -ENOMEM;
+       *v = list;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_add_verbs);
+
+/**
+ * snd_hda_apply_verbs - Execute the init verb lists
+ * @codec: the HDA codec
+ */
+void snd_hda_apply_verbs(struct hda_codec *codec)
+{
+       const struct hda_verb **v;
+       int i;
+
+       snd_array_for_each(&codec->verbs, i, v)
+               snd_hda_sequence_write(codec, *v);
+}
+EXPORT_SYMBOL_GPL(snd_hda_apply_verbs);
+
+/**
+ * snd_hda_apply_pincfgs - Set each pin config in the given list
+ * @codec: the HDA codec
+ * @cfg: NULL-terminated pin config table
+ */
+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_GPL(snd_hda_apply_pincfgs);
+
+static void set_pin_targets(struct hda_codec *codec,
+                           const struct hda_pintbl *cfg)
+{
+       for (; cfg->nid; cfg++)
+               snd_hda_set_pin_ctl_cache(codec, cfg->nid, cfg->val);
+}
+
+void __snd_hda_apply_fixup(struct hda_codec *codec, int id, int action, int depth)
+{
+       const char *modelname = codec->fixup_name;
+
+       while (id >= 0) {
+               const struct hda_fixup *fix = codec->fixup_list + id;
+
+               if (++depth > 10)
+                       break;
+               if (fix->chained_before)
+                       __snd_hda_apply_fixup(codec, fix->chain_id, action, depth + 1);
+
+               switch (fix->type) {
+               case HDA_FIXUP_PINS:
+                       if (action != HDA_FIXUP_ACT_PRE_PROBE || !fix->v.pins)
+                               break;
+                       codec_dbg(codec, "%s: Apply pincfg for %s\n",
+                                   codec->core.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;
+                       codec_dbg(codec, "%s: Apply fix-verbs for %s\n",
+                                   codec->core.chip_name, modelname);
+                       snd_hda_add_verbs(codec, fix->v.verbs);
+                       break;
+               case HDA_FIXUP_FUNC:
+                       if (!fix->v.func)
+                               break;
+                       codec_dbg(codec, "%s: Apply fix-func for %s\n",
+                                   codec->core.chip_name, modelname);
+                       fix->v.func(codec, fix, action);
+                       break;
+               case HDA_FIXUP_PINCTLS:
+                       if (action != HDA_FIXUP_ACT_PROBE || !fix->v.pins)
+                               break;
+                       codec_dbg(codec, "%s: Apply pinctl for %s\n",
+                                   codec->core.chip_name, modelname);
+                       set_pin_targets(codec, fix->v.pins);
+                       break;
+               default:
+                       codec_err(codec, "%s: Invalid fixup type %d\n",
+                                  codec->core.chip_name, fix->type);
+                       break;
+               }
+               if (!fix->chained || fix->chained_before)
+                       break;
+               id = fix->chain_id;
+       }
+}
+EXPORT_SYMBOL_GPL(__snd_hda_apply_fixup);
+
+/**
+ * snd_hda_apply_fixup - Apply the fixup chain with the given action
+ * @codec: the HDA codec
+ * @action: fixup action (HDA_FIXUP_ACT_XXX)
+ */
+void snd_hda_apply_fixup(struct hda_codec *codec, int action)
+{
+       if (codec->fixup_list)
+               __snd_hda_apply_fixup(codec, codec->fixup_id, action, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hda_apply_fixup);
+
+#define IGNORE_SEQ_ASSOC (~(AC_DEFCFG_SEQUENCE | AC_DEFCFG_DEF_ASSOC))
+
+static bool pin_config_match(struct hda_codec *codec,
+                            const struct hda_pintbl *pins,
+                            bool match_all_pins)
+{
+       const struct hda_pincfg *pin;
+       int i;
+
+       snd_array_for_each(&codec->init_pins, i, pin) {
+               hda_nid_t nid = pin->nid;
+               u32 cfg = pin->cfg;
+               const struct hda_pintbl *t_pins;
+               int found;
+
+               t_pins = pins;
+               found = 0;
+               for (; t_pins->nid; t_pins++) {
+                       if (t_pins->nid == nid) {
+                               found = 1;
+                               if ((t_pins->val & IGNORE_SEQ_ASSOC) == (cfg & IGNORE_SEQ_ASSOC))
+                                       break;
+                               else if ((cfg & 0xf0000000) == 0x40000000 && (t_pins->val & 0xf0000000) == 0x40000000)
+                                       break;
+                               else
+                                       return false;
+                       }
+               }
+               if (match_all_pins &&
+                   !found && (cfg & 0xf0000000) != 0x40000000)
+                       return false;
+       }
+
+       return true;
+}
+
+/**
+ * snd_hda_pick_pin_fixup - Pick up a fixup matching with the pin quirk list
+ * @codec: the HDA codec
+ * @pin_quirk: zero-terminated pin quirk list
+ * @fixlist: the fixup list
+ * @match_all_pins: all valid pins must match with the table entries
+ */
+void snd_hda_pick_pin_fixup(struct hda_codec *codec,
+                           const struct snd_hda_pin_quirk *pin_quirk,
+                           const struct hda_fixup *fixlist,
+                           bool match_all_pins)
+{
+       const struct snd_hda_pin_quirk *pq;
+       const char *name = NULL;
+
+       if (codec->fixup_id != HDA_FIXUP_ID_NOT_SET)
+               return;
+
+       for (pq = pin_quirk; pq->subvendor; pq++) {
+               if ((codec->core.subsystem_id & 0xffff0000) != (pq->subvendor << 16))
+                       continue;
+               if (codec->core.vendor_id != pq->codec)
+                       continue;
+               if (pin_config_match(codec, pq->pins, match_all_pins)) {
+                       codec->fixup_id = pq->value;
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+                       codec->fixup_name = pq->name;
+                       name = pq->name;
+#endif
+                       codec_info(codec, "%s: picked fixup %s (pin match)\n",
+                                  codec->core.chip_name, name ? name : "");
+                       codec->fixup_list = fixlist;
+                       return;
+               }
+       }
+}
+EXPORT_SYMBOL_GPL(snd_hda_pick_pin_fixup);
+
+/* check whether the given quirk entry matches with vendor/device pair */
+static bool hda_quirk_match(u16 vendor, u16 device, const struct hda_quirk *q)
+{
+       if (q->subvendor != vendor)
+               return false;
+       return !q->subdevice ||
+               (device & q->subdevice_mask) == q->subdevice;
+}
+
+/* look through the quirk list and return the matching entry */
+static const struct hda_quirk *
+hda_quirk_lookup_id(u16 vendor, u16 device, const struct hda_quirk *list)
+{
+       const struct hda_quirk *q;
+
+       for (q = list; q->subvendor || q->subdevice; q++) {
+               if (hda_quirk_match(vendor, device, q))
+                       return q;
+       }
+       return NULL;
+}
+
+/**
+ * snd_hda_pick_fixup - Pick up a fixup matching with PCI/codec SSID or model string
+ * @codec: the HDA codec
+ * @models: NULL-terminated model string list
+ * @quirk: zero-terminated PCI/codec SSID quirk list
+ * @fixlist: the fixup list
+ *
+ * Pick up a fixup entry matching with the given model string or SSID.
+ * If a fixup was already set beforehand, the function doesn't do anything.
+ * When a special model string "nofixup" is given, also no fixup is applied.
+ *
+ * The function tries to find the matching model name at first, if given.
+ * If the model string contains the SSID alias, try to look up with the given
+ * alias ID.
+ * If nothing matched, try to look up the PCI SSID.
+ * If still nothing matched, try to look up the codec SSID.
+ */
+void snd_hda_pick_fixup(struct hda_codec *codec,
+                       const struct hda_model_fixup *models,
+                       const struct hda_quirk *quirk,
+                       const struct hda_fixup *fixlist)
+{
+       const struct hda_quirk *q;
+       int id = HDA_FIXUP_ID_NOT_SET;
+       const char *name = NULL;
+       const char *type = NULL;
+       unsigned int vendor, device;
+       u16 pci_vendor, pci_device;
+       u16 codec_vendor, codec_device;
+
+       if (codec->fixup_id != HDA_FIXUP_ID_NOT_SET)
+               return;
+
+       /* when model=nofixup is given, don't pick up any fixups */
+       if (codec->modelname && !strcmp(codec->modelname, "nofixup")) {
+               id = HDA_FIXUP_ID_NO_FIXUP;
+               fixlist = NULL;
+               codec_info(codec, "%s: picked no fixup (nofixup specified)\n",
+                          codec->core.chip_name);
+               goto found;
+       }
+
+       /* match with the model name string */
+       if (codec->modelname && models) {
+               while (models->name) {
+                       if (!strcmp(codec->modelname, models->name)) {
+                               id = models->id;
+                               name = models->name;
+                               codec_info(codec, "%s: picked fixup %s (model specified)\n",
+                                          codec->core.chip_name, name);
+                               goto found;
+                       }
+                       models++;
+               }
+       }
+
+       if (!quirk)
+               return;
+
+       if (codec->bus->pci) {
+               pci_vendor = codec->bus->pci->subsystem_vendor;
+               pci_device = codec->bus->pci->subsystem_device;
+       }
+
+       codec_vendor = codec->core.subsystem_id >> 16;
+       codec_device = codec->core.subsystem_id & 0xffff;
+
+       /* match with the SSID alias given by the model string "XXXX:YYYY" */
+       if (codec->modelname &&
+           sscanf(codec->modelname, "%04x:%04x", &vendor, &device) == 2) {
+               q = hda_quirk_lookup_id(vendor, device, quirk);
+               if (q) {
+                       type = "alias SSID";
+                       goto found_device;
+               }
+       }
+
+       /* match primarily with the PCI SSID */
+       for (q = quirk; q->subvendor || q->subdevice; q++) {
+               /* if the entry is specific to codec SSID, check with it */
+               if (!codec->bus->pci || q->match_codec_ssid) {
+                       if (hda_quirk_match(codec_vendor, codec_device, q)) {
+                               type = "codec SSID";
+                               goto found_device;
+                       }
+               } else {
+                       if (hda_quirk_match(pci_vendor, pci_device, q)) {
+                               type = "PCI SSID";
+                               goto found_device;
+                       }
+               }
+       }
+
+       /* match with the codec SSID */
+       q = hda_quirk_lookup_id(codec_vendor, codec_device, quirk);
+       if (q) {
+               type = "codec SSID";
+               goto found_device;
+       }
+
+       return; /* no matching */
+
+ found_device:
+       id = q->value;
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+       name = q->name;
+#endif
+       codec_info(codec, "%s: picked fixup %s for %s %04x:%04x\n",
+                  codec->core.chip_name, name ? name : "",
+                  type, q->subvendor, q->subdevice);
+ found:
+       codec->fixup_id = id;
+       codec->fixup_list = fixlist;
+       codec->fixup_name = name;
+}
+EXPORT_SYMBOL_GPL(snd_hda_pick_fixup);
diff --git a/sound/hda/common/beep.c b/sound/hda/common/beep.c
new file mode 100644 (file)
index 0000000..13a7d92
--- /dev/null
@@ -0,0 +1,345 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Digital Beep Input Interface for HD-audio codec
+ *
+ * Author: Matt Ranostay <matt.ranostay@konsulko.com>
+ * Copyright (c) 2008 Embedded Alley Solutions Inc
+ */
+
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/export.h>
+#include <sound/core.h>
+#include "hda_beep.h"
+#include "hda_local.h"
+
+enum {
+       DIGBEEP_HZ_STEP = 46875,        /* 46.875 Hz */
+       DIGBEEP_HZ_MIN = 93750,         /* 93.750 Hz */
+       DIGBEEP_HZ_MAX = 12000000,      /* 12 KHz */
+};
+
+/* generate or stop tone */
+static void generate_tone(struct hda_beep *beep, int tone)
+{
+       struct hda_codec *codec = beep->codec;
+
+       if (tone && !beep->playing) {
+               snd_hda_power_up(codec);
+               if (beep->power_hook)
+                       beep->power_hook(beep, true);
+               beep->playing = 1;
+       }
+       if (!codec->beep_just_power_on)
+               snd_hda_codec_write(codec, beep->nid, 0,
+                                   AC_VERB_SET_BEEP_CONTROL, tone);
+       if (!tone && beep->playing) {
+               beep->playing = 0;
+               if (beep->power_hook)
+                       beep->power_hook(beep, false);
+               snd_hda_power_down(codec);
+       }
+}
+
+static void snd_hda_generate_beep(struct work_struct *work)
+{
+       struct hda_beep *beep =
+               container_of(work, struct hda_beep, beep_work);
+
+       if (beep->enabled)
+               generate_tone(beep, beep->tone);
+}
+
+/* (non-standard) Linear beep tone calculation for IDT/STAC codecs 
+ *
+ * The tone frequency of beep generator on IDT/STAC codecs is
+ * defined from the 8bit tone parameter, in Hz,
+ *    freq = 48000 * (257 - tone) / 1024
+ * that is from 12kHz to 93.75Hz in steps of 46.875 Hz
+ */
+static int beep_linear_tone(struct hda_beep *beep, int hz)
+{
+       if (hz <= 0)
+               return 0;
+       hz *= 1000; /* fixed point */
+       hz = hz - DIGBEEP_HZ_MIN
+               + DIGBEEP_HZ_STEP / 2; /* round to nearest step */
+       if (hz < 0)
+               hz = 0; /* turn off PC beep*/
+       else if (hz >= (DIGBEEP_HZ_MAX - DIGBEEP_HZ_MIN))
+               hz = 1; /* max frequency */
+       else {
+               hz /= DIGBEEP_HZ_STEP;
+               hz = 255 - hz;
+       }
+       return hz;
+}
+
+/* HD-audio standard beep tone parameter calculation
+ *
+ * The tone frequency in Hz is calculated as
+ *   freq = 48000 / (tone * 4)
+ * from 47Hz to 12kHz
+ */
+static int beep_standard_tone(struct hda_beep *beep, int hz)
+{
+       if (hz <= 0)
+               return 0; /* disabled */
+       hz = 12000 / hz;
+       if (hz > 0xff)
+               return 0xff;
+       if (hz <= 0)
+               return 1;
+       return hz;
+}
+
+static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
+                               unsigned int code, int hz)
+{
+       struct hda_beep *beep = input_get_drvdata(dev);
+
+       switch (code) {
+       case SND_BELL:
+               if (hz)
+                       hz = 1000;
+               fallthrough;
+       case SND_TONE:
+               if (beep->linear_tone)
+                       beep->tone = beep_linear_tone(beep, hz);
+               else
+                       beep->tone = beep_standard_tone(beep, hz);
+               break;
+       default:
+               return -1;
+       }
+
+       /* schedule beep event */
+       schedule_work(&beep->beep_work);
+       return 0;
+}
+
+static void turn_on_beep(struct hda_beep *beep)
+{
+       if (beep->keep_power_at_enable)
+               snd_hda_power_up_pm(beep->codec);
+}
+
+static void turn_off_beep(struct hda_beep *beep)
+{
+       cancel_work_sync(&beep->beep_work);
+       if (beep->playing) {
+               /* turn off beep */
+               generate_tone(beep, 0);
+       }
+       if (beep->keep_power_at_enable)
+               snd_hda_power_down_pm(beep->codec);
+}
+
+/**
+ * snd_hda_enable_beep_device - Turn on/off beep sound
+ * @codec: the HDA codec
+ * @enable: flag to turn on/off
+ */
+int snd_hda_enable_beep_device(struct hda_codec *codec, int enable)
+{
+       struct hda_beep *beep = codec->beep;
+       if (!beep)
+               return 0;
+       enable = !!enable;
+       if (beep->enabled != enable) {
+               beep->enabled = enable;
+               if (enable)
+                       turn_on_beep(beep);
+               else
+                       turn_off_beep(beep);
+               return 1;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_enable_beep_device);
+
+static int beep_dev_register(struct snd_device *device)
+{
+       struct hda_beep *beep = device->device_data;
+       int err;
+
+       err = input_register_device(beep->dev);
+       if (!err)
+               beep->registered = true;
+       return err;
+}
+
+static int beep_dev_disconnect(struct snd_device *device)
+{
+       struct hda_beep *beep = device->device_data;
+
+       if (beep->registered)
+               input_unregister_device(beep->dev);
+       else
+               input_free_device(beep->dev);
+       if (beep->enabled)
+               turn_off_beep(beep);
+       return 0;
+}
+
+static int beep_dev_free(struct snd_device *device)
+{
+       struct hda_beep *beep = device->device_data;
+
+       beep->codec->beep = NULL;
+       kfree(beep);
+       return 0;
+}
+
+/**
+ * snd_hda_attach_beep_device - Attach a beep input device
+ * @codec: the HDA codec
+ * @nid: beep NID
+ *
+ * Attach a beep object to the given widget.  If beep hint is turned off
+ * explicitly or beep_mode of the codec is turned off, this doesn't nothing.
+ *
+ * Currently, only one beep device is allowed to each codec.
+ */
+int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
+{
+       static const struct snd_device_ops ops = {
+               .dev_register = beep_dev_register,
+               .dev_disconnect = beep_dev_disconnect,
+               .dev_free = beep_dev_free,
+       };
+       struct input_dev *input_dev;
+       struct hda_beep *beep;
+       int err;
+
+       if (!codec->beep_just_power_on) {
+               if (!snd_hda_get_bool_hint(codec, "beep"))
+                       return 0; /* disabled explicitly by hints */
+               if (codec->beep_mode == HDA_BEEP_MODE_OFF)
+                       return 0; /* disabled by module option */
+       }
+
+       beep = kzalloc(sizeof(*beep), GFP_KERNEL);
+       if (beep == NULL)
+               return -ENOMEM;
+       snprintf(beep->phys, sizeof(beep->phys),
+               "card%d/codec#%d/beep0", codec->card->number, codec->addr);
+       /* enable linear scale */
+       snd_hda_codec_write_cache(codec, nid, 0,
+               AC_VERB_SET_DIGI_CONVERT_2, 0x01);
+
+       beep->nid = nid;
+       beep->codec = codec;
+       codec->beep = beep;
+
+       INIT_WORK(&beep->beep_work, &snd_hda_generate_beep);
+
+       input_dev = input_allocate_device();
+       if (!input_dev) {
+               err = -ENOMEM;
+               goto err_free;
+       }
+
+       /* setup digital beep device */
+       input_dev->name = "HDA Digital PCBeep";
+       input_dev->phys = beep->phys;
+       input_dev->id.bustype = BUS_PCI;
+       input_dev->dev.parent = &codec->card->card_dev;
+
+       input_dev->id.vendor = codec->core.vendor_id >> 16;
+       input_dev->id.product = codec->core.vendor_id & 0xffff;
+       input_dev->id.version = 0x01;
+
+       input_dev->evbit[0] = BIT_MASK(EV_SND);
+       input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
+       input_dev->event = snd_hda_beep_event;
+       input_set_drvdata(input_dev, beep);
+
+       beep->dev = input_dev;
+
+       err = snd_device_new(codec->card, SNDRV_DEV_JACK, beep, &ops);
+       if (err < 0)
+               goto err_input;
+
+       return 0;
+
+ err_input:
+       input_free_device(beep->dev);
+ err_free:
+       kfree(beep);
+       codec->beep = NULL;
+       return err;
+}
+EXPORT_SYMBOL_GPL(snd_hda_attach_beep_device);
+
+/**
+ * snd_hda_detach_beep_device - Detach the beep device
+ * @codec: the HDA codec
+ */
+void snd_hda_detach_beep_device(struct hda_codec *codec)
+{
+       if (!codec->bus->shutdown && codec->beep)
+               snd_device_free(codec->card, codec->beep);
+}
+EXPORT_SYMBOL_GPL(snd_hda_detach_beep_device);
+
+static bool ctl_has_mute(struct snd_kcontrol *kcontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       return query_amp_caps(codec, get_amp_nid(kcontrol),
+                             get_amp_direction(kcontrol)) & AC_AMPCAP_MUTE;
+}
+
+/* get/put callbacks for beep mute mixer switches */
+
+/**
+ * snd_hda_mixer_amp_switch_get_beep - Get callback for beep controls
+ * @kcontrol: ctl element
+ * @ucontrol: pointer to get/store the data
+ */
+int snd_hda_mixer_amp_switch_get_beep(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct hda_beep *beep = codec->beep;
+       int chs = get_amp_channels(kcontrol);
+
+       if (beep && (!beep->enabled || !ctl_has_mute(kcontrol))) {
+               if (chs & 1)
+                       ucontrol->value.integer.value[0] = beep->enabled;
+               if (chs & 2)
+                       ucontrol->value.integer.value[1] = beep->enabled;
+               return 0;
+       }
+       return snd_hda_mixer_amp_switch_get(kcontrol, ucontrol);
+}
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_get_beep);
+
+/**
+ * snd_hda_mixer_amp_switch_put_beep - Put callback for beep controls
+ * @kcontrol: ctl element
+ * @ucontrol: pointer to get/store the data
+ */
+int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct hda_beep *beep = codec->beep;
+       if (beep) {
+               u8 chs = get_amp_channels(kcontrol);
+               int enable = 0;
+               long *valp = ucontrol->value.integer.value;
+               if (chs & 1) {
+                       enable |= *valp;
+                       valp++;
+               }
+               if (chs & 2)
+                       enable |= *valp;
+               snd_hda_enable_beep_device(codec, enable);
+       }
+       if (!ctl_has_mute(kcontrol))
+               return 0;
+       return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+}
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_put_beep);
diff --git a/sound/hda/common/bind.c b/sound/hda/common/bind.c
new file mode 100644 (file)
index 0000000..df8f88b
--- /dev/null
@@ -0,0 +1,343 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * HD-audio codec driver binding
+ * Copyright (c) Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/pm.h>
+#include <sound/core.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_jack.h"
+
+/*
+ * find a matching codec id
+ */
+static int hda_codec_match(struct hdac_device *dev, const struct hdac_driver *drv)
+{
+       struct hda_codec *codec = container_of(dev, struct hda_codec, core);
+       const struct hda_codec_driver *driver =
+               container_of(drv, struct hda_codec_driver, core);
+       const struct hda_device_id *list;
+       /* check probe_id instead of vendor_id if set */
+       u32 id = codec->probe_id ? codec->probe_id : codec->core.vendor_id;
+       u32 rev_id = codec->core.revision_id;
+
+       for (list = driver->id; list->vendor_id; list++) {
+               if (list->vendor_id == id &&
+                   (!list->rev_id || list->rev_id == rev_id)) {
+                       codec->preset = list;
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+/* process an unsolicited event */
+static void hda_codec_unsol_event(struct hdac_device *dev, unsigned int ev)
+{
+       struct hda_codec *codec = container_of(dev, struct hda_codec, core);
+
+       /* ignore unsol events during shutdown */
+       if (codec->card->shutdown || codec->bus->shutdown)
+               return;
+
+       /* ignore unsol events during system suspend/resume */
+       if (codec->core.dev.power.power_state.event != PM_EVENT_ON)
+               return;
+
+       if (codec->patch_ops.unsol_event)
+               codec->patch_ops.unsol_event(codec, ev);
+}
+
+/**
+ * snd_hda_codec_set_name - set the codec name
+ * @codec: the HDA codec
+ * @name: name string to set
+ */
+int snd_hda_codec_set_name(struct hda_codec *codec, const char *name)
+{
+       int err;
+
+       if (!name)
+               return 0;
+       err = snd_hdac_device_set_chip_name(&codec->core, name);
+       if (err < 0)
+               return err;
+
+       /* update the mixer name */
+       if (!*codec->card->mixername ||
+           codec->bus->mixer_assigned >= codec->core.addr) {
+               snprintf(codec->card->mixername,
+                        sizeof(codec->card->mixername), "%s %s",
+                        codec->core.vendor_name, codec->core.chip_name);
+               codec->bus->mixer_assigned = codec->core.addr;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_set_name);
+
+static int hda_codec_driver_probe(struct device *dev)
+{
+       struct hda_codec *codec = dev_to_hda_codec(dev);
+       struct module *owner = dev->driver->owner;
+       hda_codec_patch_t patch;
+       int err;
+
+       if (codec->bus->core.ext_ops) {
+               if (WARN_ON(!codec->bus->core.ext_ops->hdev_attach))
+                       return -EINVAL;
+               return codec->bus->core.ext_ops->hdev_attach(&codec->core);
+       }
+
+       if (WARN_ON(!codec->preset))
+               return -EINVAL;
+
+       err = snd_hda_codec_set_name(codec, codec->preset->name);
+       if (err < 0)
+               goto error;
+       err = snd_hdac_regmap_init(&codec->core);
+       if (err < 0)
+               goto error;
+
+       if (!try_module_get(owner)) {
+               err = -EINVAL;
+               goto error;
+       }
+
+       patch = (hda_codec_patch_t)codec->preset->driver_data;
+       if (patch) {
+               err = patch(codec);
+               if (err < 0)
+                       goto error_module_put;
+       }
+
+       err = snd_hda_codec_build_pcms(codec);
+       if (err < 0)
+               goto error_module;
+       err = snd_hda_codec_build_controls(codec);
+       if (err < 0)
+               goto error_module;
+       /* only register after the bus probe finished; otherwise it's racy */
+       if (!codec->bus->bus_probing && codec->card->registered) {
+               err = snd_card_register(codec->card);
+               if (err < 0)
+                       goto error_module;
+               snd_hda_codec_register(codec);
+       }
+
+       codec->core.lazy_cache = true;
+       return 0;
+
+ error_module:
+       if (codec->patch_ops.free)
+               codec->patch_ops.free(codec);
+ error_module_put:
+       module_put(owner);
+
+ error:
+       snd_hda_codec_cleanup_for_unbind(codec);
+       codec->preset = NULL;
+       return err;
+}
+
+static int hda_codec_driver_remove(struct device *dev)
+{
+       struct hda_codec *codec = dev_to_hda_codec(dev);
+
+       if (codec->bus->core.ext_ops) {
+               if (WARN_ON(!codec->bus->core.ext_ops->hdev_detach))
+                       return -EINVAL;
+               return codec->bus->core.ext_ops->hdev_detach(&codec->core);
+       }
+
+       snd_hda_codec_disconnect_pcms(codec);
+       snd_hda_jack_tbl_disconnect(codec);
+       if (!refcount_dec_and_test(&codec->pcm_ref))
+               wait_event(codec->remove_sleep, !refcount_read(&codec->pcm_ref));
+       snd_power_sync_ref(codec->bus->card);
+
+       if (codec->patch_ops.free)
+               codec->patch_ops.free(codec);
+       snd_hda_codec_cleanup_for_unbind(codec);
+       codec->preset = NULL;
+       module_put(dev->driver->owner);
+       return 0;
+}
+
+static void hda_codec_driver_shutdown(struct device *dev)
+{
+       snd_hda_codec_shutdown(dev_to_hda_codec(dev));
+}
+
+int __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name,
+                              struct module *owner)
+{
+       drv->core.driver.name = name;
+       drv->core.driver.owner = owner;
+       drv->core.driver.bus = &snd_hda_bus_type;
+       drv->core.driver.probe = hda_codec_driver_probe;
+       drv->core.driver.remove = hda_codec_driver_remove;
+       drv->core.driver.shutdown = hda_codec_driver_shutdown;
+       drv->core.driver.pm = pm_ptr(&hda_codec_driver_pm);
+       drv->core.type = HDA_DEV_LEGACY;
+       drv->core.match = hda_codec_match;
+       drv->core.unsol_event = hda_codec_unsol_event;
+       return driver_register(&drv->core.driver);
+}
+EXPORT_SYMBOL_GPL(__hda_codec_driver_register);
+
+void hda_codec_driver_unregister(struct hda_codec_driver *drv)
+{
+       driver_unregister(&drv->core.driver);
+}
+EXPORT_SYMBOL_GPL(hda_codec_driver_unregister);
+
+static inline bool codec_probed(struct hda_codec *codec)
+{
+       return device_attach(hda_codec_dev(codec)) > 0 && codec->preset;
+}
+
+/* try to auto-load codec module */
+static void request_codec_module(struct hda_codec *codec)
+{
+#ifdef MODULE
+       char modalias[32];
+       const char *mod = NULL;
+
+       switch (codec->probe_id) {
+       case HDA_CODEC_ID_GENERIC_HDMI:
+#if IS_MODULE(CONFIG_SND_HDA_CODEC_HDMI)
+               mod = "snd-hda-codec-hdmi";
+#endif
+               break;
+       case HDA_CODEC_ID_GENERIC:
+#if IS_MODULE(CONFIG_SND_HDA_GENERIC)
+               mod = "snd-hda-codec-generic";
+#endif
+               break;
+       default:
+               snd_hdac_codec_modalias(&codec->core, modalias, sizeof(modalias));
+               mod = modalias;
+               break;
+       }
+
+       if (mod)
+               request_module(mod);
+#endif /* MODULE */
+}
+
+/* try to auto-load and bind the codec module */
+static void codec_bind_module(struct hda_codec *codec)
+{
+#ifdef MODULE
+       request_codec_module(codec);
+       if (codec_probed(codec))
+               return;
+#endif
+}
+
+#if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI)
+/* if all audio out widgets are digital, let's assume the codec as a HDMI/DP */
+static bool is_likely_hdmi_codec(struct hda_codec *codec)
+{
+       hda_nid_t nid;
+
+       /*
+        * For ASoC users, if snd_hda_hdmi_codec module is denylisted and any
+        * event causes i915 enumeration to fail, ->wcaps remains uninitialized.
+        */
+       if (!codec->wcaps)
+               return true;
+
+       for_each_hda_codec_node(nid, codec) {
+               unsigned int wcaps = get_wcaps(codec, nid);
+               switch (get_wcaps_type(wcaps)) {
+               case AC_WID_AUD_IN:
+                       return false; /* HDMI parser supports only HDMI out */
+               case AC_WID_AUD_OUT:
+                       if (!(wcaps & AC_WCAP_DIGITAL))
+                               return false;
+                       break;
+               }
+       }
+       return true;
+}
+#else
+/* no HDMI codec parser support */
+#define is_likely_hdmi_codec(codec)    false
+#endif /* CONFIG_SND_HDA_CODEC_HDMI */
+
+static int codec_bind_generic(struct hda_codec *codec)
+{
+       if (codec->probe_id)
+               return -ENODEV;
+
+       if (is_likely_hdmi_codec(codec)) {
+               codec->probe_id = HDA_CODEC_ID_GENERIC_HDMI;
+               request_codec_module(codec);
+               if (codec_probed(codec))
+                       return 0;
+       }
+
+       codec->probe_id = HDA_CODEC_ID_GENERIC;
+       request_codec_module(codec);
+       if (codec_probed(codec))
+               return 0;
+       return -ENODEV;
+}
+
+#if IS_ENABLED(CONFIG_SND_HDA_GENERIC)
+#define is_generic_config(codec) \
+       (codec->modelname && !strcmp(codec->modelname, "generic"))
+#else
+#define is_generic_config(codec)       0
+#endif
+
+/**
+ * snd_hda_codec_configure - (Re-)configure the HD-audio codec
+ * @codec: the HDA codec
+ *
+ * Start parsing of the given codec tree and (re-)initialize the whole
+ * patch instance.
+ *
+ * Returns 0 if successful or a negative error code.
+ */
+int snd_hda_codec_configure(struct hda_codec *codec)
+{
+       int err;
+
+       if (codec->configured)
+               return 0;
+
+       if (is_generic_config(codec))
+               codec->probe_id = HDA_CODEC_ID_GENERIC;
+       else
+               codec->probe_id = 0;
+
+       if (!device_is_registered(&codec->core.dev)) {
+               err = snd_hdac_device_register(&codec->core);
+               if (err < 0)
+                       return err;
+       }
+
+       if (!codec->preset)
+               codec_bind_module(codec);
+       if (!codec->preset) {
+               err = codec_bind_generic(codec);
+               if (err < 0) {
+                       codec_dbg(codec, "Unable to bind the codec\n");
+                       return err;
+               }
+       }
+
+       codec->configured = 1;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_configure);
diff --git a/sound/hda/common/codec.c b/sound/hda/common/codec.c
new file mode 100644 (file)
index 0000000..cb72e96
--- /dev/null
@@ -0,0 +1,4049 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <sound/core.h>
+#include <sound/hda_codec.h>
+#include <sound/asoundef.h>
+#include <sound/tlv.h>
+#include <sound/initval.h>
+#include <sound/jack.h>
+#include "hda_local.h"
+#include "hda_beep.h"
+#include "hda_jack.h"
+#include <sound/hda_hwdep.h>
+#include <sound/hda_component.h>
+
+#define codec_in_pm(codec)             snd_hdac_is_in_pm(&codec->core)
+#define hda_codec_is_power_on(codec)   snd_hdac_is_power_on(&codec->core)
+#define codec_has_epss(codec) \
+       ((codec)->core.power_caps & AC_PWRST_EPSS)
+#define codec_has_clkstop(codec) \
+       ((codec)->core.power_caps & AC_PWRST_CLKSTOP)
+
+/*
+ * Send and receive a verb - passed to exec_verb override for hdac_device
+ */
+static int codec_exec_verb(struct hdac_device *dev, unsigned int cmd,
+                          unsigned int flags, unsigned int *res)
+{
+       struct hda_codec *codec = container_of(dev, struct hda_codec, core);
+       struct hda_bus *bus = codec->bus;
+       int err;
+
+       if (cmd == ~0)
+               return -1;
+
+ again:
+       snd_hda_power_up_pm(codec);
+       mutex_lock(&bus->core.cmd_mutex);
+       if (flags & HDA_RW_NO_RESPONSE_FALLBACK)
+               bus->no_response_fallback = 1;
+       err = snd_hdac_bus_exec_verb_unlocked(&bus->core, codec->core.addr,
+                                             cmd, res);
+       bus->no_response_fallback = 0;
+       mutex_unlock(&bus->core.cmd_mutex);
+       snd_hda_power_down_pm(codec);
+       if (!codec_in_pm(codec) && res && err == -EAGAIN) {
+               if (bus->response_reset) {
+                       codec_dbg(codec,
+                                 "resetting BUS due to fatal communication error\n");
+                       snd_hda_bus_reset(bus);
+               }
+               goto again;
+       }
+       /* clear reset-flag when the communication gets recovered */
+       if (!err || codec_in_pm(codec))
+               bus->response_reset = 0;
+       return err;
+}
+
+/**
+ * snd_hda_sequence_write - sequence writes
+ * @codec: the HDA codec
+ * @seq: VERB array to send
+ *
+ * Send the commands sequentially from the given array.
+ * The array must be terminated with NID=0.
+ */
+void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq)
+{
+       for (; seq->nid; seq++)
+               snd_hda_codec_write(codec, seq->nid, 0, seq->verb, seq->param);
+}
+EXPORT_SYMBOL_GPL(snd_hda_sequence_write);
+
+/* connection list element */
+struct hda_conn_list {
+       struct list_head list;
+       int len;
+       hda_nid_t nid;
+       hda_nid_t conns[] __counted_by(len);
+};
+
+/* look up the cached results */
+static struct hda_conn_list *
+lookup_conn_list(struct hda_codec *codec, hda_nid_t nid)
+{
+       struct hda_conn_list *p;
+       list_for_each_entry(p, &codec->conn_list, list) {
+               if (p->nid == nid)
+                       return p;
+       }
+       return NULL;
+}
+
+static int add_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
+                        const hda_nid_t *list)
+{
+       struct hda_conn_list *p;
+
+       p = kmalloc(struct_size(p, conns, len), GFP_KERNEL);
+       if (!p)
+               return -ENOMEM;
+       p->len = len;
+       p->nid = nid;
+       memcpy(p->conns, list, len * sizeof(hda_nid_t));
+       list_add(&p->list, &codec->conn_list);
+       return 0;
+}
+
+static void remove_conn_list(struct hda_codec *codec)
+{
+       while (!list_empty(&codec->conn_list)) {
+               struct hda_conn_list *p;
+               p = list_first_entry(&codec->conn_list, typeof(*p), list);
+               list_del(&p->list);
+               kfree(p);
+       }
+}
+
+/* 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[32];
+       hda_nid_t *result = list;
+       int len;
+
+       len = snd_hda_get_raw_connections(codec, nid, list, ARRAY_SIZE(list));
+       if (len == -ENOSPC) {
+               len = snd_hda_get_num_raw_conns(codec, nid);
+               result = kmalloc_array(len, sizeof(hda_nid_t), GFP_KERNEL);
+               if (!result)
+                       return -ENOMEM;
+               len = snd_hda_get_raw_connections(codec, nid, result, len);
+       }
+       if (len >= 0)
+               len = snd_hda_override_conn_list(codec, nid, len, result);
+       if (result != list)
+               kfree(result);
+       return len;
+}
+
+/**
+ * snd_hda_get_conn_list - get connection list
+ * @codec: the HDA codec
+ * @nid: NID to parse
+ * @listp: the pointer to store NID list
+ *
+ * Parses the connection list of the given widget and stores the pointer
+ * to the list of NIDs.
+ *
+ * Returns the number of connections, or a negative error code.
+ *
+ * Note that the returned pointer isn't protected against the list
+ * modification.  If snd_hda_override_conn_list() might be called
+ * concurrently, protect with a mutex appropriately.
+ */
+int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
+                         const hda_nid_t **listp)
+{
+       bool added = false;
+
+       for (;;) {
+               int err;
+               const struct hda_conn_list *p;
+
+               /* if the connection-list is already cached, read it */
+               p = lookup_conn_list(codec, nid);
+               if (p) {
+                       if (listp)
+                               *listp = p->conns;
+                       return p->len;
+               }
+               if (snd_BUG_ON(added))
+                       return -EINVAL;
+
+               err = read_and_add_raw_conns(codec, nid);
+               if (err < 0)
+                       return err;
+               added = true;
+       }
+}
+EXPORT_SYMBOL_GPL(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; 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_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 && conn_list) {
+               if (len > max_conns) {
+                       codec_err(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_GPL(snd_hda_get_connections);
+
+/**
+ * snd_hda_override_conn_list - add/modify the connection-list to cache
+ * @codec: the HDA codec
+ * @nid: NID to parse
+ * @len: number of connection list entries
+ * @list: the list of connection entries
+ *
+ * Add or modify the given connection-list to the cache.  If the corresponding
+ * cache already exists, invalidate it and append a new one.
+ *
+ * Returns zero or a negative error code.
+ */
+int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
+                              const hda_nid_t *list)
+{
+       struct hda_conn_list *p;
+
+       p = lookup_conn_list(codec, nid);
+       if (p) {
+               list_del(&p->list);
+               kfree(p);
+       }
+
+       return add_conn_list(codec, nid, len, list);
+}
+EXPORT_SYMBOL_GPL(snd_hda_override_conn_list);
+
+/**
+ * snd_hda_get_conn_index - get the connection index of the given NID
+ * @codec: the HDA codec
+ * @mux: NID containing the list
+ * @nid: NID to select
+ * @recursive: 1 when searching NID recursively, otherwise 0
+ *
+ * Parses the connection list of the widget @mux and checks whether the
+ * widget @nid is present.  If it is, return the connection index.
+ * Otherwise it returns -1.
+ */
+int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
+                          hda_nid_t nid, int recursive)
+{
+       const hda_nid_t *conn;
+       int i, nums;
+
+       nums = snd_hda_get_conn_list(codec, mux, &conn);
+       for (i = 0; i < nums; i++)
+               if (conn[i] == nid)
+                       return i;
+       if (!recursive)
+               return -1;
+       if (recursive > 10) {
+               codec_dbg(codec, "too deep connection for 0x%x\n", nid);
+               return -1;
+       }
+       recursive++;
+       for (i = 0; i < nums; i++) {
+               unsigned int type = get_wcaps_type(get_wcaps(codec, conn[i]));
+               if (type == AC_WID_PIN || type == AC_WID_AUD_OUT)
+                       continue;
+               if (snd_hda_get_conn_index(codec, conn[i], nid, recursive) >= 0)
+                       return i;
+       }
+       return -1;
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_conn_index);
+
+/**
+ * snd_hda_get_num_devices - get DEVLIST_LEN parameter of the given widget
+ *  @codec: the HDA codec
+ *  @nid: NID of the pin to parse
+ *
+ * Get the device entry number on the given widget. This is a feature of
+ * DP MST audio. Each pin can have several device entries in it.
+ */
+unsigned int snd_hda_get_num_devices(struct hda_codec *codec, hda_nid_t nid)
+{
+       unsigned int wcaps = get_wcaps(codec, nid);
+       unsigned int parm;
+
+       if (!codec->dp_mst || !(wcaps & AC_WCAP_DIGITAL) ||
+           get_wcaps_type(wcaps) != AC_WID_PIN)
+               return 0;
+
+       parm = snd_hdac_read_parm_uncached(&codec->core, nid, AC_PAR_DEVLIST_LEN);
+       if (parm == -1)
+               parm = 0;
+       return parm & AC_DEV_LIST_LEN_MASK;
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_num_devices);
+
+/**
+ * snd_hda_get_devices - copy device list without cache
+ * @codec: the HDA codec
+ * @nid: NID of the pin to parse
+ * @dev_list: device list array
+ * @max_devices: max. number of devices to store
+ *
+ * Copy the device list. This info is dynamic and so not cached.
+ * Currently called only from hda_proc.c, so not exported.
+ */
+int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid,
+                       u8 *dev_list, int max_devices)
+{
+       unsigned int parm;
+       int i, dev_len, devices;
+
+       parm = snd_hda_get_num_devices(codec, nid);
+       if (!parm)      /* not multi-stream capable */
+               return 0;
+
+       dev_len = parm + 1;
+       dev_len = dev_len < max_devices ? dev_len : max_devices;
+
+       devices = 0;
+       while (devices < dev_len) {
+               if (snd_hdac_read(&codec->core, nid,
+                                 AC_VERB_GET_DEVICE_LIST, devices, &parm))
+                       break; /* error */
+
+               for (i = 0; i < 8; i++) {
+                       dev_list[devices] = (u8)parm;
+                       parm >>= 4;
+                       devices++;
+                       if (devices >= dev_len)
+                               break;
+               }
+       }
+       return devices;
+}
+
+/**
+ * snd_hda_get_dev_select - get device entry select on the pin
+ * @codec: the HDA codec
+ * @nid: NID of the pin to get device entry select
+ *
+ * Get the devcie entry select on the pin. Return the device entry
+ * id selected on the pin. Return 0 means the first device entry
+ * is selected or MST is not supported.
+ */
+int snd_hda_get_dev_select(struct hda_codec *codec, hda_nid_t nid)
+{
+       /* not support dp_mst will always return 0, using first dev_entry */
+       if (!codec->dp_mst)
+               return 0;
+
+       return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DEVICE_SEL, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_dev_select);
+
+/**
+ * snd_hda_set_dev_select - set device entry select on the pin
+ * @codec: the HDA codec
+ * @nid: NID of the pin to set device entry select
+ * @dev_id: device entry id to be set
+ *
+ * Set the device entry select on the pin nid.
+ */
+int snd_hda_set_dev_select(struct hda_codec *codec, hda_nid_t nid, int dev_id)
+{
+       int ret, num_devices;
+
+       /* not support dp_mst will always return 0, using first dev_entry */
+       if (!codec->dp_mst)
+               return 0;
+
+       /* AC_PAR_DEVLIST_LEN is 0 based. */
+       num_devices = snd_hda_get_num_devices(codec, nid) + 1;
+       /* If Device List Length is 0 (num_device = 1),
+        * the pin is not multi stream capable.
+        * Do nothing in this case.
+        */
+       if (num_devices == 1)
+               return 0;
+
+       /* Behavior of setting index being equal to or greater than
+        * Device List Length is not predictable
+        */
+       if (num_devices <= dev_id)
+               return -EINVAL;
+
+       ret = snd_hda_codec_write(codec, nid, 0,
+                       AC_VERB_SET_DEVICE_SEL, dev_id);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hda_set_dev_select);
+
+/*
+ * read widget caps for each widget and store in cache
+ */
+static int read_widget_caps(struct hda_codec *codec, hda_nid_t fg_node)
+{
+       int i;
+       hda_nid_t nid;
+
+       codec->wcaps = kmalloc_array(codec->core.num_nodes, 4, GFP_KERNEL);
+       if (!codec->wcaps)
+               return -ENOMEM;
+       nid = codec->core.start_nid;
+       for (i = 0; i < codec->core.num_nodes; i++, nid++)
+               codec->wcaps[i] = snd_hdac_read_parm_uncached(&codec->core,
+                                       nid, AC_PAR_AUDIO_WIDGET_CAP);
+       return 0;
+}
+
+/* read all pin default configurations and save codec->init_pins */
+static int read_pin_defaults(struct hda_codec *codec)
+{
+       hda_nid_t nid;
+
+       for_each_hda_codec_node(nid, codec) {
+               struct hda_pincfg *pin;
+               unsigned int wcaps = get_wcaps(codec, nid);
+               unsigned int wid_type = get_wcaps_type(wcaps);
+               if (wid_type != AC_WID_PIN)
+                       continue;
+               pin = snd_array_new(&codec->init_pins);
+               if (!pin)
+                       return -ENOMEM;
+               pin->nid = nid;
+               pin->cfg = snd_hda_codec_read(codec, nid, 0,
+                                             AC_VERB_GET_CONFIG_DEFAULT, 0);
+               /*
+                * all device entries are the same widget control so far
+                * fixme: if any codec is different, need fix here
+                */
+               pin->ctrl = snd_hda_codec_read(codec, nid, 0,
+                                              AC_VERB_GET_PIN_WIDGET_CONTROL,
+                                              0);
+       }
+       return 0;
+}
+
+/* look up the given pin config list and return the item matching with NID */
+static struct hda_pincfg *look_up_pincfg(struct hda_codec *codec,
+                                        struct snd_array *array,
+                                        hda_nid_t nid)
+{
+       struct hda_pincfg *pin;
+       int i;
+
+       snd_array_for_each(array, i, pin) {
+               if (pin->nid == nid)
+                       return pin;
+       }
+       return NULL;
+}
+
+/* set the current pin config value for the given NID.
+ * the value is cached, and read via snd_hda_codec_get_pincfg()
+ */
+int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list,
+                      hda_nid_t nid, unsigned int cfg)
+{
+       struct hda_pincfg *pin;
+
+       pin = look_up_pincfg(codec, list, nid);
+       if (!pin) {
+               pin = snd_array_new(list);
+               if (!pin)
+                       return -ENOMEM;
+               pin->nid = nid;
+       }
+       pin->cfg = cfg;
+       return 0;
+}
+
+/**
+ * snd_hda_codec_set_pincfg - Override a pin default configuration
+ * @codec: the HDA codec
+ * @nid: NID to set the pin config
+ * @cfg: the pin default config value
+ *
+ * Override a pin default configuration value in the cache.
+ * This value can be read by snd_hda_codec_get_pincfg() in a higher
+ * priority than the real hardware value.
+ */
+int snd_hda_codec_set_pincfg(struct hda_codec *codec,
+                            hda_nid_t nid, unsigned int cfg)
+{
+       return snd_hda_add_pincfg(codec, &codec->driver_pins, nid, cfg);
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_set_pincfg);
+
+/**
+ * snd_hda_codec_get_pincfg - Obtain a pin-default configuration
+ * @codec: the HDA codec
+ * @nid: NID to get the pin config
+ *
+ * Get the current pin config value of the given pin NID.
+ * If the pincfg value is cached or overridden via sysfs or driver,
+ * returns the cached value.
+ */
+unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid)
+{
+       struct hda_pincfg *pin;
+
+#ifdef CONFIG_SND_HDA_RECONFIG
+       {
+               unsigned int cfg = 0;
+               mutex_lock(&codec->user_mutex);
+               pin = look_up_pincfg(codec, &codec->user_pins, nid);
+               if (pin)
+                       cfg = pin->cfg;
+               mutex_unlock(&codec->user_mutex);
+               if (cfg)
+                       return cfg;
+       }
+#endif
+       pin = look_up_pincfg(codec, &codec->driver_pins, nid);
+       if (pin)
+               return pin->cfg;
+       pin = look_up_pincfg(codec, &codec->init_pins, nid);
+       if (pin)
+               return pin->cfg;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_get_pincfg);
+
+/**
+ * snd_hda_codec_set_pin_target - remember the current pinctl target value
+ * @codec: the HDA codec
+ * @nid: pin NID
+ * @val: assigned pinctl value
+ *
+ * This function stores the given value to a pinctl target value in the
+ * pincfg table.  This isn't always as same as the actually written value
+ * but can be referred at any time via snd_hda_codec_get_pin_target().
+ */
+int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid,
+                                unsigned int val)
+{
+       struct hda_pincfg *pin;
+
+       pin = look_up_pincfg(codec, &codec->init_pins, nid);
+       if (!pin)
+               return -EINVAL;
+       pin->target = val;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_set_pin_target);
+
+/**
+ * snd_hda_codec_get_pin_target - return the current pinctl target value
+ * @codec: the HDA codec
+ * @nid: pin NID
+ */
+int snd_hda_codec_get_pin_target(struct hda_codec *codec, hda_nid_t nid)
+{
+       struct hda_pincfg *pin;
+
+       pin = look_up_pincfg(codec, &codec->init_pins, nid);
+       if (!pin)
+               return 0;
+       return pin->target;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_get_pin_target);
+
+/**
+ * snd_hda_shutup_pins - Shut up all pins
+ * @codec: the HDA codec
+ *
+ * Clear all pin controls to shup up before suspend for avoiding click noise.
+ * The controls aren't cached so that they can be resumed properly.
+ */
+void snd_hda_shutup_pins(struct hda_codec *codec)
+{
+       const struct hda_pincfg *pin;
+       int i;
+
+       /* don't shut up pins when unloading the driver; otherwise it breaks
+        * the default pin setup at the next load of the driver
+        */
+       if (codec->bus->shutdown)
+               return;
+       snd_array_for_each(&codec->init_pins, i, pin) {
+               /* use read here for syncing after issuing each verb */
+               snd_hda_codec_read(codec, pin->nid, 0,
+                                  AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
+       }
+       codec->pins_shutup = 1;
+}
+EXPORT_SYMBOL_GPL(snd_hda_shutup_pins);
+
+/* Restore the pin controls cleared previously via snd_hda_shutup_pins() */
+static void restore_shutup_pins(struct hda_codec *codec)
+{
+       const struct hda_pincfg *pin;
+       int i;
+
+       if (!codec->pins_shutup)
+               return;
+       if (codec->bus->shutdown)
+               return;
+       snd_array_for_each(&codec->init_pins, i, pin) {
+               snd_hda_codec_write(codec, pin->nid, 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
+                                   pin->ctrl);
+       }
+       codec->pins_shutup = 0;
+}
+
+static void hda_jackpoll_work(struct work_struct *work)
+{
+       struct hda_codec *codec =
+               container_of(work, struct hda_codec, jackpoll_work.work);
+
+       if (!codec->jackpoll_interval)
+               return;
+
+       /* the power-up/down sequence triggers the runtime resume */
+       snd_hda_power_up(codec);
+       /* update jacks manually if polling is required, too */
+       snd_hda_jack_set_dirty_all(codec);
+       snd_hda_jack_poll_all(codec);
+       schedule_delayed_work(&codec->jackpoll_work, codec->jackpoll_interval);
+       snd_hda_power_down(codec);
+}
+
+/* release all pincfg lists */
+static void free_init_pincfgs(struct hda_codec *codec)
+{
+       snd_array_free(&codec->driver_pins);
+#ifdef CONFIG_SND_HDA_RECONFIG
+       snd_array_free(&codec->user_pins);
+#endif
+       snd_array_free(&codec->init_pins);
+}
+
+/*
+ * audio-converter setup caches
+ */
+struct hda_cvt_setup {
+       hda_nid_t nid;
+       u8 stream_tag;
+       u8 channel_id;
+       u16 format_id;
+       unsigned char active;   /* cvt is currently used */
+       unsigned char dirty;    /* setups should be cleared */
+};
+
+/* get or create a cache entry for the given audio converter NID */
+static struct hda_cvt_setup *
+get_hda_cvt_setup(struct hda_codec *codec, hda_nid_t nid)
+{
+       struct hda_cvt_setup *p;
+       int i;
+
+       snd_array_for_each(&codec->cvt_setups, i, p) {
+               if (p->nid == nid)
+                       return p;
+       }
+       p = snd_array_new(&codec->cvt_setups);
+       if (p)
+               p->nid = nid;
+       return p;
+}
+
+/*
+ * PCM device
+ */
+void snd_hda_codec_pcm_put(struct hda_pcm *pcm)
+{
+       if (refcount_dec_and_test(&pcm->codec->pcm_ref))
+               wake_up(&pcm->codec->remove_sleep);
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_put);
+
+struct hda_pcm *snd_hda_codec_pcm_new(struct hda_codec *codec,
+                                     const char *fmt, ...)
+{
+       struct hda_pcm *pcm;
+       va_list args;
+
+       pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
+       if (!pcm)
+               return NULL;
+
+       pcm->codec = codec;
+       va_start(args, fmt);
+       pcm->name = kvasprintf(GFP_KERNEL, fmt, args);
+       va_end(args);
+       if (!pcm->name) {
+               kfree(pcm);
+               return NULL;
+       }
+
+       list_add_tail(&pcm->list, &codec->pcm_list_head);
+       refcount_inc(&codec->pcm_ref);
+       return pcm;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_new);
+
+/*
+ * codec destructor
+ */
+void snd_hda_codec_disconnect_pcms(struct hda_codec *codec)
+{
+       struct hda_pcm *pcm;
+
+       list_for_each_entry(pcm, &codec->pcm_list_head, list) {
+               if (pcm->disconnected)
+                       continue;
+               if (pcm->pcm)
+                       snd_device_disconnect(codec->card, pcm->pcm);
+               snd_hda_codec_pcm_put(pcm);
+               pcm->disconnected = 1;
+       }
+}
+
+static void codec_release_pcms(struct hda_codec *codec)
+{
+       struct hda_pcm *pcm, *n;
+
+       list_for_each_entry_safe(pcm, n, &codec->pcm_list_head, list) {
+               list_del(&pcm->list);
+               if (pcm->pcm)
+                       snd_device_free(pcm->codec->card, pcm->pcm);
+               clear_bit(pcm->device, pcm->codec->bus->pcm_dev_bits);
+               kfree(pcm->name);
+               kfree(pcm);
+       }
+}
+
+/**
+ * snd_hda_codec_cleanup_for_unbind - Prepare codec for removal
+ * @codec: codec device to cleanup
+ */
+void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec)
+{
+       if (codec->core.registered) {
+               /* pm_runtime_put() is called in snd_hdac_device_exit() */
+               pm_runtime_get_noresume(hda_codec_dev(codec));
+               pm_runtime_disable(hda_codec_dev(codec));
+               codec->core.registered = 0;
+       }
+
+       snd_hda_codec_disconnect_pcms(codec);
+       cancel_delayed_work_sync(&codec->jackpoll_work);
+       if (!codec->in_freeing)
+               snd_hda_ctls_clear(codec);
+       codec_release_pcms(codec);
+       snd_hda_detach_beep_device(codec);
+       memset(&codec->patch_ops, 0, sizeof(codec->patch_ops));
+       snd_hda_jack_tbl_clear(codec);
+       codec->proc_widget_hook = NULL;
+       codec->spec = NULL;
+
+       /* free only driver_pins so that init_pins + user_pins are restored */
+       snd_array_free(&codec->driver_pins);
+       snd_array_free(&codec->cvt_setups);
+       snd_array_free(&codec->spdif_out);
+       snd_array_free(&codec->verbs);
+       codec->follower_dig_outs = NULL;
+       codec->spdif_status_reset = 0;
+       snd_array_free(&codec->mixers);
+       snd_array_free(&codec->nids);
+       remove_conn_list(codec);
+       snd_hdac_regmap_exit(&codec->core);
+       codec->configured = 0;
+       refcount_set(&codec->pcm_ref, 1); /* reset refcount */
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_cleanup_for_unbind);
+
+static unsigned int hda_set_power_state(struct hda_codec *codec,
+                               unsigned int power_state);
+
+/* enable/disable display power per codec */
+void snd_hda_codec_display_power(struct hda_codec *codec, bool enable)
+{
+       if (codec->display_power_control)
+               snd_hdac_display_power(&codec->bus->core, codec->addr, enable);
+}
+
+/**
+ * snd_hda_codec_register - Finalize codec initialization
+ * @codec: codec device to register
+ *
+ * Also called from hda_bind.c
+ */
+void snd_hda_codec_register(struct hda_codec *codec)
+{
+       if (codec->core.registered)
+               return;
+       if (device_is_registered(hda_codec_dev(codec))) {
+               snd_hda_codec_display_power(codec, true);
+               pm_runtime_enable(hda_codec_dev(codec));
+               /* it was powered up in snd_hda_codec_new(), now all done */
+               snd_hda_power_down(codec);
+               codec->core.registered = 1;
+       }
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_register);
+
+static int snd_hda_codec_dev_register(struct snd_device *device)
+{
+       snd_hda_codec_register(device->device_data);
+       return 0;
+}
+
+/**
+ * snd_hda_codec_unregister - Unregister specified codec device
+ * @codec: codec device to unregister
+ */
+void snd_hda_codec_unregister(struct hda_codec *codec)
+{
+       codec->in_freeing = 1;
+       /*
+        * snd_hda_codec_device_new() is used by legacy HDA and ASoC driver.
+        * We can't unregister ASoC device since it will be unregistered in
+        * snd_hdac_ext_bus_device_remove().
+        */
+       if (codec->core.type == HDA_DEV_LEGACY)
+               snd_hdac_device_unregister(&codec->core);
+       snd_hda_codec_display_power(codec, false);
+
+       /*
+        * In the case of ASoC HD-audio bus, the device refcount is released in
+        * snd_hdac_ext_bus_device_remove() explicitly.
+        */
+       if (codec->core.type == HDA_DEV_LEGACY)
+               put_device(hda_codec_dev(codec));
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_unregister);
+
+static int snd_hda_codec_dev_free(struct snd_device *device)
+{
+       snd_hda_codec_unregister(device->device_data);
+       return 0;
+}
+
+static void snd_hda_codec_dev_release(struct device *dev)
+{
+       struct hda_codec *codec = dev_to_hda_codec(dev);
+
+       free_init_pincfgs(codec);
+       snd_hdac_device_exit(&codec->core);
+       snd_hda_sysfs_clear(codec);
+       kfree(codec->modelname);
+       kfree(codec->wcaps);
+       kfree(codec);
+}
+
+#define DEV_NAME_LEN 31
+
+/**
+ * snd_hda_codec_device_init - allocate HDA codec device
+ * @bus: codec's parent bus
+ * @codec_addr: the codec address on the parent bus
+ * @fmt: format string for the device's name
+ *
+ * Returns newly allocated codec device or ERR_PTR() on failure.
+ */
+struct hda_codec *
+snd_hda_codec_device_init(struct hda_bus *bus, unsigned int codec_addr,
+                         const char *fmt, ...)
+{
+       va_list vargs;
+       char name[DEV_NAME_LEN];
+       struct hda_codec *codec;
+       int err;
+
+       if (snd_BUG_ON(!bus))
+               return ERR_PTR(-EINVAL);
+       if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS))
+               return ERR_PTR(-EINVAL);
+
+       codec = kzalloc(sizeof(*codec), GFP_KERNEL);
+       if (!codec)
+               return ERR_PTR(-ENOMEM);
+
+       va_start(vargs, fmt);
+       vsprintf(name, fmt, vargs);
+       va_end(vargs);
+
+       err = snd_hdac_device_init(&codec->core, &bus->core, name, codec_addr);
+       if (err < 0) {
+               kfree(codec);
+               return ERR_PTR(err);
+       }
+
+       codec->bus = bus;
+       codec->depop_delay = -1;
+       codec->fixup_id = HDA_FIXUP_ID_NOT_SET;
+       codec->core.dev.release = snd_hda_codec_dev_release;
+       codec->core.type = HDA_DEV_LEGACY;
+
+       mutex_init(&codec->spdif_mutex);
+       mutex_init(&codec->control_mutex);
+       snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32);
+       snd_array_init(&codec->nids, sizeof(struct hda_nid_item), 32);
+       snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16);
+       snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16);
+       snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8);
+       snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16);
+       snd_array_init(&codec->jacktbl, sizeof(struct hda_jack_tbl), 16);
+       snd_array_init(&codec->verbs, sizeof(struct hda_verb *), 8);
+       INIT_LIST_HEAD(&codec->conn_list);
+       INIT_LIST_HEAD(&codec->pcm_list_head);
+       INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work);
+       refcount_set(&codec->pcm_ref, 1);
+       init_waitqueue_head(&codec->remove_sleep);
+
+       return codec;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_device_init);
+
+/**
+ * snd_hda_codec_new - create a HDA codec
+ * @bus: the bus to assign
+ * @card: card for this codec
+ * @codec_addr: the codec address
+ * @codecp: the pointer to store the generated codec
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card,
+                     unsigned int codec_addr, struct hda_codec **codecp)
+{
+       struct hda_codec *codec;
+       int ret;
+
+       codec = snd_hda_codec_device_init(bus, codec_addr, "hdaudioC%dD%d",
+                                         card->number, codec_addr);
+       if (IS_ERR(codec))
+               return PTR_ERR(codec);
+       *codecp = codec;
+
+       ret = snd_hda_codec_device_new(bus, card, codec_addr, *codecp, true);
+       if (ret)
+               put_device(hda_codec_dev(*codecp));
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_new);
+
+int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card,
+                       unsigned int codec_addr, struct hda_codec *codec,
+                       bool snddev_managed)
+{
+       char component[31];
+       hda_nid_t fg;
+       int err;
+       static const struct snd_device_ops dev_ops = {
+               .dev_register = snd_hda_codec_dev_register,
+               .dev_free = snd_hda_codec_dev_free,
+       };
+
+       dev_dbg(card->dev, "%s: entry\n", __func__);
+
+       if (snd_BUG_ON(!bus))
+               return -EINVAL;
+       if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS))
+               return -EINVAL;
+
+       codec->core.exec_verb = codec_exec_verb;
+       codec->card = card;
+       codec->addr = codec_addr;
+
+       codec->power_jiffies = jiffies;
+
+       snd_hda_sysfs_init(codec);
+
+       if (codec->bus->modelname) {
+               codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
+               if (!codec->modelname)
+                       return -ENOMEM;
+       }
+
+       fg = codec->core.afg ? codec->core.afg : codec->core.mfg;
+       err = read_widget_caps(codec, fg);
+       if (err < 0)
+               return err;
+       err = read_pin_defaults(codec);
+       if (err < 0)
+               return err;
+
+       /* power-up all before initialization */
+       hda_set_power_state(codec, AC_PWRST_D0);
+       codec->core.dev.power.power_state = PMSG_ON;
+
+       snd_hda_codec_proc_new(codec);
+
+       snd_hda_create_hwdep(codec);
+
+       sprintf(component, "HDA:%08x,%08x,%08x", codec->core.vendor_id,
+               codec->core.subsystem_id, codec->core.revision_id);
+       snd_component_add(card, component);
+
+       if (snddev_managed) {
+               /* ASoC features component management instead */
+               err = snd_device_new(card, SNDRV_DEV_CODEC, codec, &dev_ops);
+               if (err < 0)
+                       return err;
+       }
+
+#ifdef CONFIG_PM
+       /* PM runtime needs to be enabled later after binding codec */
+       if (codec->core.dev.power.runtime_auto)
+               pm_runtime_forbid(&codec->core.dev);
+       else
+               /* Keep the usage_count consistent across subsequent probing */
+               pm_runtime_get_noresume(&codec->core.dev);
+#endif
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_device_new);
+
+/**
+ * snd_hda_codec_update_widgets - Refresh widget caps and pin defaults
+ * @codec: the HDA codec
+ *
+ * Forcibly refresh the all widget caps and the init pin configurations of
+ * the given codec.
+ */
+int snd_hda_codec_update_widgets(struct hda_codec *codec)
+{
+       hda_nid_t fg;
+       int err;
+
+       err = snd_hdac_refresh_widgets(&codec->core);
+       if (err < 0)
+               return err;
+
+       /* Assume the function group node does not change,
+        * only the widget nodes may change.
+        */
+       kfree(codec->wcaps);
+       fg = codec->core.afg ? codec->core.afg : codec->core.mfg;
+       err = read_widget_caps(codec, fg);
+       if (err < 0)
+               return err;
+
+       snd_array_free(&codec->init_pins);
+       err = read_pin_defaults(codec);
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_update_widgets);
+
+/* update the stream-id if changed */
+static void update_pcm_stream_id(struct hda_codec *codec,
+                                struct hda_cvt_setup *p, hda_nid_t nid,
+                                u32 stream_tag, int channel_id)
+{
+       unsigned int oldval, newval;
+
+       if (p->stream_tag != stream_tag || p->channel_id != channel_id) {
+               oldval = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
+               newval = (stream_tag << 4) | channel_id;
+               if (oldval != newval)
+                       snd_hda_codec_write(codec, nid, 0,
+                                           AC_VERB_SET_CHANNEL_STREAMID,
+                                           newval);
+               p->stream_tag = stream_tag;
+               p->channel_id = channel_id;
+       }
+}
+
+/* update the format-id if changed */
+static void update_pcm_format(struct hda_codec *codec, struct hda_cvt_setup *p,
+                             hda_nid_t nid, int format)
+{
+       unsigned int oldval;
+
+       if (p->format_id != format) {
+               oldval = snd_hda_codec_read(codec, nid, 0,
+                                           AC_VERB_GET_STREAM_FORMAT, 0);
+               if (oldval != format) {
+                       msleep(1);
+                       snd_hda_codec_write(codec, nid, 0,
+                                           AC_VERB_SET_STREAM_FORMAT,
+                                           format);
+               }
+               p->format_id = format;
+       }
+}
+
+/**
+ * snd_hda_codec_setup_stream - set up the codec for streaming
+ * @codec: the CODEC to set up
+ * @nid: the NID to set up
+ * @stream_tag: stream tag to pass, it's between 0x1 and 0xf.
+ * @channel_id: channel id to pass, zero based.
+ * @format: stream format.
+ */
+void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
+                               u32 stream_tag,
+                               int channel_id, int format)
+{
+       struct hda_codec *c;
+       struct hda_cvt_setup *p;
+       int type;
+       int i;
+
+       if (!nid)
+               return;
+
+       codec_dbg(codec,
+                 "hda_codec_setup_stream: NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n",
+                 nid, stream_tag, channel_id, format);
+       p = get_hda_cvt_setup(codec, nid);
+       if (!p)
+               return;
+
+       if (codec->patch_ops.stream_pm)
+               codec->patch_ops.stream_pm(codec, nid, true);
+       if (codec->pcm_format_first)
+               update_pcm_format(codec, p, nid, format);
+       update_pcm_stream_id(codec, p, nid, stream_tag, channel_id);
+       if (!codec->pcm_format_first)
+               update_pcm_format(codec, p, nid, format);
+
+       p->active = 1;
+       p->dirty = 0;
+
+       /* make other inactive cvts with the same stream-tag dirty */
+       type = get_wcaps_type(get_wcaps(codec, nid));
+       list_for_each_codec(c, codec->bus) {
+               snd_array_for_each(&c->cvt_setups, i, p) {
+                       if (!p->active && p->stream_tag == stream_tag &&
+                           get_wcaps_type(get_wcaps(c, p->nid)) == type)
+                               p->dirty = 1;
+               }
+       }
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_setup_stream);
+
+static void really_cleanup_stream(struct hda_codec *codec,
+                                 struct hda_cvt_setup *q);
+
+/**
+ * __snd_hda_codec_cleanup_stream - clean up the codec for closing
+ * @codec: the CODEC to clean up
+ * @nid: the NID to clean up
+ * @do_now: really clean up the stream instead of clearing the active flag
+ */
+void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid,
+                                   int do_now)
+{
+       struct hda_cvt_setup *p;
+
+       if (!nid)
+               return;
+
+       if (codec->no_sticky_stream)
+               do_now = 1;
+
+       codec_dbg(codec, "hda_codec_cleanup_stream: NID=0x%x\n", nid);
+       p = get_hda_cvt_setup(codec, nid);
+       if (p) {
+               /* here we just clear the active flag when do_now isn't set;
+                * actual clean-ups will be done later in
+                * purify_inactive_streams() called from snd_hda_codec_prpapre()
+                */
+               if (do_now)
+                       really_cleanup_stream(codec, p);
+               else
+                       p->active = 0;
+       }
+}
+EXPORT_SYMBOL_GPL(__snd_hda_codec_cleanup_stream);
+
+static void really_cleanup_stream(struct hda_codec *codec,
+                                 struct hda_cvt_setup *q)
+{
+       hda_nid_t nid = q->nid;
+       if (q->stream_tag || q->channel_id)
+               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
+       if (q->format_id)
+               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0
+);
+       memset(q, 0, sizeof(*q));
+       q->nid = nid;
+       if (codec->patch_ops.stream_pm)
+               codec->patch_ops.stream_pm(codec, nid, false);
+}
+
+/* clean up the all conflicting obsolete streams */
+static void purify_inactive_streams(struct hda_codec *codec)
+{
+       struct hda_codec *c;
+       struct hda_cvt_setup *p;
+       int i;
+
+       list_for_each_codec(c, codec->bus) {
+               snd_array_for_each(&c->cvt_setups, i, p) {
+                       if (p->dirty)
+                               really_cleanup_stream(c, p);
+               }
+       }
+}
+
+/* clean up all streams; called from suspend */
+static void hda_cleanup_all_streams(struct hda_codec *codec)
+{
+       struct hda_cvt_setup *p;
+       int i;
+
+       snd_array_for_each(&codec->cvt_setups, i, p) {
+               if (p->stream_tag)
+                       really_cleanup_stream(codec, p);
+       }
+}
+
+/*
+ * amp access functions
+ */
+
+/**
+ * query_amp_caps - query AMP capabilities
+ * @codec: the HD-auio codec
+ * @nid: the NID to query
+ * @direction: either #HDA_INPUT or #HDA_OUTPUT
+ *
+ * Query AMP capabilities for the given widget and direction.
+ * Returns the obtained capability bits.
+ *
+ * When cap bits have been already read, this doesn't read again but
+ * returns the cached value.
+ */
+u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
+{
+       if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD))
+               nid = codec->core.afg;
+       return snd_hda_param_read(codec, nid,
+                                 direction == HDA_OUTPUT ?
+                                 AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
+}
+EXPORT_SYMBOL_GPL(query_amp_caps);
+
+/**
+ * snd_hda_check_amp_caps - query AMP capabilities
+ * @codec: the HD-audio codec
+ * @nid: the NID to query
+ * @dir: either #HDA_INPUT or #HDA_OUTPUT
+ * @bits: bit mask to check the result
+ *
+ * Check whether the widget has the given amp capability for the direction.
+ */
+bool snd_hda_check_amp_caps(struct hda_codec *codec, hda_nid_t nid,
+                          int dir, unsigned int bits)
+{
+       if (!nid)
+               return false;
+       if (get_wcaps(codec, nid) & (1 << (dir + 1)))
+               if (query_amp_caps(codec, nid, dir) & bits)
+                       return true;
+       return false;
+}
+EXPORT_SYMBOL_GPL(snd_hda_check_amp_caps);
+
+/**
+ * snd_hda_override_amp_caps - Override the AMP capabilities
+ * @codec: the CODEC to clean up
+ * @nid: the NID to clean up
+ * @dir: either #HDA_INPUT or #HDA_OUTPUT
+ * @caps: the capability bits to set
+ *
+ * Override the cached AMP caps bits value by the given one.
+ * This function is useful if the driver needs to adjust the AMP ranges,
+ * e.g. limit to 0dB, etc.
+ *
+ * Returns zero if successful or a negative error code.
+ */
+int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
+                             unsigned int caps)
+{
+       unsigned int parm;
+
+       snd_hda_override_wcaps(codec, nid,
+                              get_wcaps(codec, nid) | AC_WCAP_AMP_OVRD);
+       parm = dir == HDA_OUTPUT ? AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP;
+       return snd_hdac_override_parm(&codec->core, nid, parm, caps);
+}
+EXPORT_SYMBOL_GPL(snd_hda_override_amp_caps);
+
+static unsigned int encode_amp(struct hda_codec *codec, hda_nid_t nid,
+                              int ch, int dir, int idx)
+{
+       unsigned int cmd = snd_hdac_regmap_encode_amp(nid, ch, dir, idx);
+
+       /* enable fake mute if no h/w mute but min=mute */
+       if ((query_amp_caps(codec, nid, dir) &
+            (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) == AC_AMPCAP_MIN_MUTE)
+               cmd |= AC_AMP_FAKE_MUTE;
+       return cmd;
+}
+
+/**
+ * snd_hda_codec_amp_update - update the AMP mono value
+ * @codec: HD-audio codec
+ * @nid: NID to read the AMP value
+ * @ch: channel to update (0 or 1)
+ * @dir: #HDA_INPUT or #HDA_OUTPUT
+ * @idx: the index value (only for input direction)
+ * @mask: bit mask to set
+ * @val: the bits value to set
+ *
+ * Update the AMP values for the given channel, direction and index.
+ */
+int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid,
+                            int ch, int dir, int idx, int mask, int val)
+{
+       unsigned int cmd = encode_amp(codec, nid, ch, dir, idx);
+
+       return snd_hdac_regmap_update_raw(&codec->core, cmd, mask, val);
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_amp_update);
+
+/**
+ * snd_hda_codec_amp_stereo - update the AMP stereo values
+ * @codec: HD-audio codec
+ * @nid: NID to read the AMP value
+ * @direction: #HDA_INPUT or #HDA_OUTPUT
+ * @idx: the index value (only for input direction)
+ * @mask: bit mask to set
+ * @val: the bits value to set
+ *
+ * Update the AMP values like snd_hda_codec_amp_update(), but for a
+ * stereo widget with the same mask and value.
+ */
+int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
+                            int direction, int idx, int mask, int val)
+{
+       int ch, ret = 0;
+
+       if (snd_BUG_ON(mask & ~0xff))
+               mask &= 0xff;
+       for (ch = 0; ch < 2; ch++)
+               ret |= snd_hda_codec_amp_update(codec, nid, ch, direction,
+                                               idx, mask, val);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_amp_stereo);
+
+/**
+ * snd_hda_codec_amp_init - initialize the AMP value
+ * @codec: the HDA codec
+ * @nid: NID to read the AMP value
+ * @ch: channel (left=0 or right=1)
+ * @dir: #HDA_INPUT or #HDA_OUTPUT
+ * @idx: the index value (only for input direction)
+ * @mask: bit mask to set
+ * @val: the bits value to set
+ *
+ * Works like snd_hda_codec_amp_update() but it writes the value only at
+ * the first access.  If the amp was already initialized / updated beforehand,
+ * this does nothing.
+ */
+int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch,
+                          int dir, int idx, int mask, int val)
+{
+       unsigned int cmd = encode_amp(codec, nid, ch, dir, idx);
+
+       if (!codec->core.regmap)
+               return -EINVAL;
+       return snd_hdac_regmap_update_raw_once(&codec->core, cmd, mask, val);
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_amp_init);
+
+/**
+ * snd_hda_codec_amp_init_stereo - initialize the stereo AMP value
+ * @codec: the HDA codec
+ * @nid: NID to read the AMP value
+ * @dir: #HDA_INPUT or #HDA_OUTPUT
+ * @idx: the index value (only for input direction)
+ * @mask: bit mask to set
+ * @val: the bits value to set
+ *
+ * Call snd_hda_codec_amp_init() for both stereo channels.
+ */
+int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid,
+                                 int dir, int idx, int mask, int val)
+{
+       int ch, ret = 0;
+
+       if (snd_BUG_ON(mask & ~0xff))
+               mask &= 0xff;
+       for (ch = 0; ch < 2; ch++)
+               ret |= snd_hda_codec_amp_init(codec, nid, ch, dir,
+                                             idx, mask, val);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_amp_init_stereo);
+
+static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir,
+                            unsigned int ofs)
+{
+       u32 caps = query_amp_caps(codec, nid, dir);
+       /* get num steps */
+       caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
+       if (ofs < caps)
+               caps -= ofs;
+       return caps;
+}
+
+/**
+ * snd_hda_mixer_amp_volume_info - Info callback for a standard AMP mixer
+ * @kcontrol: referred ctl element
+ * @uinfo: pointer to get/store the data
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
+int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_info *uinfo)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       u16 nid = get_amp_nid(kcontrol);
+       u8 chs = get_amp_channels(kcontrol);
+       int dir = get_amp_direction(kcontrol);
+       unsigned int ofs = get_amp_offset(kcontrol);
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = chs == 3 ? 2 : 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = get_amp_max_value(codec, nid, dir, ofs);
+       if (!uinfo->value.integer.max) {
+               codec_warn(codec,
+                          "num_steps = 0 for NID=0x%x (ctl = %s)\n",
+                          nid, kcontrol->id.name);
+               return -EINVAL;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_info);
+
+
+static inline unsigned int
+read_amp_value(struct hda_codec *codec, hda_nid_t nid,
+              int ch, int dir, int idx, unsigned int ofs)
+{
+       unsigned int val;
+       val = snd_hda_codec_amp_read(codec, nid, ch, dir, idx);
+       val &= HDA_AMP_VOLMASK;
+       if (val >= ofs)
+               val -= ofs;
+       else
+               val = 0;
+       return val;
+}
+
+static inline int
+update_amp_value(struct hda_codec *codec, hda_nid_t nid,
+                int ch, int dir, int idx, unsigned int ofs,
+                unsigned int val)
+{
+       unsigned int maxval;
+
+       if (val > 0)
+               val += ofs;
+       /* ofs = 0: raw max value */
+       maxval = get_amp_max_value(codec, nid, dir, 0);
+       if (val > maxval)
+               return -EINVAL;
+       return snd_hda_codec_amp_update(codec, nid, ch, dir, idx,
+                                       HDA_AMP_VOLMASK, val);
+}
+
+/**
+ * snd_hda_mixer_amp_volume_get - Get callback for a standard AMP mixer volume
+ * @kcontrol: ctl element
+ * @ucontrol: pointer to get/store the data
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
+int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       hda_nid_t nid = get_amp_nid(kcontrol);
+       int chs = get_amp_channels(kcontrol);
+       int dir = get_amp_direction(kcontrol);
+       int idx = get_amp_index(kcontrol);
+       unsigned int ofs = get_amp_offset(kcontrol);
+       long *valp = ucontrol->value.integer.value;
+
+       if (chs & 1)
+               *valp++ = read_amp_value(codec, nid, 0, dir, idx, ofs);
+       if (chs & 2)
+               *valp = read_amp_value(codec, nid, 1, dir, idx, ofs);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_get);
+
+/**
+ * snd_hda_mixer_amp_volume_put - Put callback for a standard AMP mixer volume
+ * @kcontrol: ctl element
+ * @ucontrol: pointer to get/store the data
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
+int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       hda_nid_t nid = get_amp_nid(kcontrol);
+       int chs = get_amp_channels(kcontrol);
+       int dir = get_amp_direction(kcontrol);
+       int idx = get_amp_index(kcontrol);
+       unsigned int ofs = get_amp_offset(kcontrol);
+       long *valp = ucontrol->value.integer.value;
+       int change = 0;
+       int err;
+
+       if (chs & 1) {
+               err = update_amp_value(codec, nid, 0, dir, idx, ofs, *valp);
+               if (err < 0)
+                       return err;
+               change |= err;
+               valp++;
+       }
+       if (chs & 2) {
+               err = update_amp_value(codec, nid, 1, dir, idx, ofs, *valp);
+               if (err < 0)
+                       return err;
+               change |= err;
+       }
+       return change;
+}
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_put);
+
+/* inquiry the amp caps and convert to TLV */
+static void get_ctl_amp_tlv(struct snd_kcontrol *kcontrol, unsigned int *tlv)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       hda_nid_t nid = get_amp_nid(kcontrol);
+       int dir = get_amp_direction(kcontrol);
+       unsigned int ofs = get_amp_offset(kcontrol);
+       bool min_mute = get_amp_min_mute(kcontrol);
+       u32 caps, val1, val2;
+
+       caps = query_amp_caps(codec, nid, dir);
+       val2 = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT;
+       val2 = (val2 + 1) * 25;
+       val1 = -((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT);
+       val1 += ofs;
+       val1 = ((int)val1) * ((int)val2);
+       if (min_mute || (caps & AC_AMPCAP_MIN_MUTE))
+               val2 |= TLV_DB_SCALE_MUTE;
+       tlv[SNDRV_CTL_TLVO_TYPE] = SNDRV_CTL_TLVT_DB_SCALE;
+       tlv[SNDRV_CTL_TLVO_LEN] = 2 * sizeof(unsigned int);
+       tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] = val1;
+       tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] = val2;
+}
+
+/**
+ * snd_hda_mixer_amp_tlv - TLV callback for a standard AMP mixer volume
+ * @kcontrol: ctl element
+ * @op_flag: operation flag
+ * @size: byte size of input TLV
+ * @_tlv: TLV data
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
+int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+                         unsigned int size, unsigned int __user *_tlv)
+{
+       unsigned int tlv[4];
+
+       if (size < 4 * sizeof(unsigned int))
+               return -ENOMEM;
+       get_ctl_amp_tlv(kcontrol, tlv);
+       if (copy_to_user(_tlv, tlv, sizeof(tlv)))
+               return -EFAULT;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_tlv);
+
+/**
+ * snd_hda_set_vmaster_tlv - Set TLV for a virtual master control
+ * @codec: HD-audio codec
+ * @nid: NID of a reference widget
+ * @dir: #HDA_INPUT or #HDA_OUTPUT
+ * @tlv: TLV data to be stored, at least 4 elements
+ *
+ * Set (static) TLV data for a virtual master volume using the AMP caps
+ * obtained from the reference NID.
+ * The volume range is recalculated as if the max volume is 0dB.
+ */
+void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
+                            unsigned int *tlv)
+{
+       u32 caps;
+       int nums, step;
+
+       caps = query_amp_caps(codec, nid, dir);
+       nums = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
+       step = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT;
+       step = (step + 1) * 25;
+       tlv[SNDRV_CTL_TLVO_TYPE] = SNDRV_CTL_TLVT_DB_SCALE;
+       tlv[SNDRV_CTL_TLVO_LEN] = 2 * sizeof(unsigned int);
+       tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] = -nums * step;
+       tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] = step;
+}
+EXPORT_SYMBOL_GPL(snd_hda_set_vmaster_tlv);
+
+/* find a mixer control element with the given name */
+static struct snd_kcontrol *
+find_mixer_ctl(struct hda_codec *codec, const char *name, int dev, int idx)
+{
+       struct snd_ctl_elem_id id;
+       memset(&id, 0, sizeof(id));
+       id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+       id.device = dev;
+       id.index = idx;
+       if (snd_BUG_ON(strlen(name) >= sizeof(id.name)))
+               return NULL;
+       strcpy(id.name, name);
+       return snd_ctl_find_id(codec->card, &id);
+}
+
+/**
+ * snd_hda_find_mixer_ctl - Find a mixer control element with the given name
+ * @codec: HD-audio codec
+ * @name: ctl id name string
+ *
+ * Get the control element with the given id string and IFACE_MIXER.
+ */
+struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
+                                           const char *name)
+{
+       return find_mixer_ctl(codec, name, 0, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hda_find_mixer_ctl);
+
+static int find_empty_mixer_ctl_idx(struct hda_codec *codec, const char *name,
+                                   int start_idx)
+{
+       int i, idx;
+       /* 16 ctlrs should be large enough */
+       for (i = 0, idx = start_idx; i < 16; i++, idx++) {
+               if (!find_mixer_ctl(codec, name, 0, idx))
+                       return idx;
+       }
+       return -EBUSY;
+}
+
+/**
+ * snd_hda_ctl_add - Add a control element and assign to the codec
+ * @codec: HD-audio codec
+ * @nid: corresponding NID (optional)
+ * @kctl: the control element to assign
+ *
+ * Add the given control element to an array inside the codec instance.
+ * All control elements belonging to a codec are supposed to be added
+ * by this function so that a proper clean-up works at the free or
+ * reconfiguration time.
+ *
+ * If non-zero @nid is passed, the NID is assigned to the control element.
+ * The assignment is shown in the codec proc file.
+ *
+ * snd_hda_ctl_add() checks the control subdev id field whether
+ * #HDA_SUBDEV_NID_FLAG bit is set.  If set (and @nid is zero), the lower
+ * bits value is taken as the NID to assign. The #HDA_NID_ITEM_AMP bit
+ * specifies if kctl->private_value is a HDA amplifier value.
+ */
+int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid,
+                   struct snd_kcontrol *kctl)
+{
+       int err;
+       unsigned short flags = 0;
+       struct hda_nid_item *item;
+
+       if (kctl->id.subdevice & HDA_SUBDEV_AMP_FLAG) {
+               flags |= HDA_NID_ITEM_AMP;
+               if (nid == 0)
+                       nid = get_amp_nid_(kctl->private_value);
+       }
+       if ((kctl->id.subdevice & HDA_SUBDEV_NID_FLAG) != 0 && nid == 0)
+               nid = kctl->id.subdevice & 0xffff;
+       if (kctl->id.subdevice & (HDA_SUBDEV_NID_FLAG|HDA_SUBDEV_AMP_FLAG))
+               kctl->id.subdevice = 0;
+       err = snd_ctl_add(codec->card, kctl);
+       if (err < 0)
+               return err;
+       item = snd_array_new(&codec->mixers);
+       if (!item)
+               return -ENOMEM;
+       item->kctl = kctl;
+       item->nid = nid;
+       item->flags = flags;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_ctl_add);
+
+/**
+ * snd_hda_ctls_clear - Clear all controls assigned to the given codec
+ * @codec: HD-audio codec
+ */
+void snd_hda_ctls_clear(struct hda_codec *codec)
+{
+       int i;
+       struct hda_nid_item *items = codec->mixers.list;
+
+       for (i = 0; i < codec->mixers.used; i++)
+               snd_ctl_remove(codec->card, items[i].kctl);
+       snd_array_free(&codec->mixers);
+       snd_array_free(&codec->nids);
+}
+
+/**
+ * snd_hda_lock_devices - pseudo device locking
+ * @bus: the BUS
+ *
+ * toggle card->shutdown to allow/disallow the device access (as a hack)
+ */
+int snd_hda_lock_devices(struct hda_bus *bus)
+{
+       struct snd_card *card = bus->card;
+       struct hda_codec *codec;
+
+       spin_lock(&card->files_lock);
+       if (card->shutdown)
+               goto err_unlock;
+       card->shutdown = 1;
+       if (!list_empty(&card->ctl_files))
+               goto err_clear;
+
+       list_for_each_codec(codec, bus) {
+               struct hda_pcm *cpcm;
+               list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
+                       if (!cpcm->pcm)
+                               continue;
+                       if (cpcm->pcm->streams[0].substream_opened ||
+                           cpcm->pcm->streams[1].substream_opened)
+                               goto err_clear;
+               }
+       }
+       spin_unlock(&card->files_lock);
+       return 0;
+
+ err_clear:
+       card->shutdown = 0;
+ err_unlock:
+       spin_unlock(&card->files_lock);
+       return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_hda_lock_devices);
+
+/**
+ * snd_hda_unlock_devices - pseudo device unlocking
+ * @bus: the BUS
+ */
+void snd_hda_unlock_devices(struct hda_bus *bus)
+{
+       struct snd_card *card = bus->card;
+
+       spin_lock(&card->files_lock);
+       card->shutdown = 0;
+       spin_unlock(&card->files_lock);
+}
+EXPORT_SYMBOL_GPL(snd_hda_unlock_devices);
+
+/**
+ * snd_hda_codec_reset - Clear all objects assigned to the codec
+ * @codec: HD-audio codec
+ *
+ * This frees the all PCM and control elements assigned to the codec, and
+ * clears the caches and restores the pin default configurations.
+ *
+ * When a device is being used, it returns -EBSY.  If successfully freed,
+ * returns zero.
+ */
+int snd_hda_codec_reset(struct hda_codec *codec)
+{
+       struct hda_bus *bus = codec->bus;
+
+       if (snd_hda_lock_devices(bus) < 0)
+               return -EBUSY;
+
+       /* OK, let it free */
+       device_release_driver(hda_codec_dev(codec));
+
+       /* allow device access again */
+       snd_hda_unlock_devices(bus);
+       return 0;
+}
+
+typedef int (*map_follower_func_t)(struct hda_codec *, void *, struct snd_kcontrol *);
+
+/* apply the function to all matching follower ctls in the mixer list */
+static int map_followers(struct hda_codec *codec, const char * const *followers,
+                        const char *suffix, map_follower_func_t func, void *data)
+{
+       struct hda_nid_item *items;
+       const char * const *s;
+       int i, err;
+
+       items = codec->mixers.list;
+       for (i = 0; i < codec->mixers.used; i++) {
+               struct snd_kcontrol *sctl = items[i].kctl;
+               if (!sctl || sctl->id.iface != SNDRV_CTL_ELEM_IFACE_MIXER)
+                       continue;
+               for (s = followers; *s; s++) {
+                       char tmpname[sizeof(sctl->id.name)];
+                       const char *name = *s;
+                       if (suffix) {
+                               snprintf(tmpname, sizeof(tmpname), "%s %s",
+                                        name, suffix);
+                               name = tmpname;
+                       }
+                       if (!strcmp(sctl->id.name, name)) {
+                               err = func(codec, data, sctl);
+                               if (err)
+                                       return err;
+                               break;
+                       }
+               }
+       }
+       return 0;
+}
+
+static int check_follower_present(struct hda_codec *codec,
+                                 void *data, struct snd_kcontrol *sctl)
+{
+       return 1;
+}
+
+/* call kctl->put with the given value(s) */
+static int put_kctl_with_value(struct snd_kcontrol *kctl, int val)
+{
+       struct snd_ctl_elem_value *ucontrol;
+       ucontrol = kzalloc(sizeof(*ucontrol), GFP_KERNEL);
+       if (!ucontrol)
+               return -ENOMEM;
+       ucontrol->value.integer.value[0] = val;
+       ucontrol->value.integer.value[1] = val;
+       kctl->put(kctl, ucontrol);
+       kfree(ucontrol);
+       return 0;
+}
+
+struct follower_init_arg {
+       struct hda_codec *codec;
+       int step;
+};
+
+/* initialize the follower volume with 0dB via snd_ctl_apply_vmaster_followers() */
+static int init_follower_0dB(struct snd_kcontrol *follower,
+                            struct snd_kcontrol *kctl,
+                            void *_arg)
+{
+       struct follower_init_arg *arg = _arg;
+       int _tlv[4];
+       const int *tlv = NULL;
+       int step;
+       int val;
+
+       if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
+               if (kctl->tlv.c != snd_hda_mixer_amp_tlv) {
+                       codec_err(arg->codec,
+                                 "Unexpected TLV callback for follower %s:%d\n",
+                                 kctl->id.name, kctl->id.index);
+                       return 0; /* ignore */
+               }
+               get_ctl_amp_tlv(kctl, _tlv);
+               tlv = _tlv;
+       } else if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_READ)
+               tlv = kctl->tlv.p;
+
+       if (!tlv || tlv[SNDRV_CTL_TLVO_TYPE] != SNDRV_CTL_TLVT_DB_SCALE)
+               return 0;
+
+       step = tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP];
+       step &= ~TLV_DB_SCALE_MUTE;
+       if (!step)
+               return 0;
+       if (arg->step && arg->step != step) {
+               codec_err(arg->codec,
+                         "Mismatching dB step for vmaster follower (%d!=%d)\n",
+                         arg->step, step);
+               return 0;
+       }
+
+       arg->step = step;
+       val = -tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] / step;
+       if (val > 0) {
+               put_kctl_with_value(follower, val);
+               return val;
+       }
+
+       return 0;
+}
+
+/* unmute the follower via snd_ctl_apply_vmaster_followers() */
+static int init_follower_unmute(struct snd_kcontrol *follower,
+                               struct snd_kcontrol *kctl,
+                               void *_arg)
+{
+       return put_kctl_with_value(follower, 1);
+}
+
+static int add_follower(struct hda_codec *codec,
+                       void *data, struct snd_kcontrol *follower)
+{
+       return snd_ctl_add_follower(data, follower);
+}
+
+/**
+ * __snd_hda_add_vmaster - create a virtual master control and add followers
+ * @codec: HD-audio codec
+ * @name: vmaster control name
+ * @tlv: TLV data (optional)
+ * @followers: follower control names (optional)
+ * @suffix: suffix string to each follower name (optional)
+ * @init_follower_vol: initialize followers to unmute/0dB
+ * @access: kcontrol access rights
+ * @ctl_ret: store the vmaster kcontrol in return
+ *
+ * Create a virtual master control with the given name.  The TLV data
+ * must be either NULL or a valid data.
+ *
+ * @followers is a NULL-terminated array of strings, each of which is a
+ * follower control name.  All controls with these names are assigned to
+ * the new virtual master control.
+ *
+ * This function returns zero if successful or a negative error code.
+ */
+int __snd_hda_add_vmaster(struct hda_codec *codec, char *name,
+                         unsigned int *tlv, const char * const *followers,
+                         const char *suffix, bool init_follower_vol,
+                         unsigned int access, struct snd_kcontrol **ctl_ret)
+{
+       struct snd_kcontrol *kctl;
+       int err;
+
+       if (ctl_ret)
+               *ctl_ret = NULL;
+
+       err = map_followers(codec, followers, suffix, check_follower_present, NULL);
+       if (err != 1) {
+               codec_dbg(codec, "No follower found for %s\n", name);
+               return 0;
+       }
+       kctl = snd_ctl_make_virtual_master(name, tlv);
+       if (!kctl)
+               return -ENOMEM;
+       kctl->vd[0].access |= access;
+       err = snd_hda_ctl_add(codec, 0, kctl);
+       if (err < 0)
+               return err;
+
+       err = map_followers(codec, followers, suffix, add_follower, kctl);
+       if (err < 0)
+               return err;
+
+       /* init with master mute & zero volume */
+       put_kctl_with_value(kctl, 0);
+       if (init_follower_vol) {
+               struct follower_init_arg arg = {
+                       .codec = codec,
+                       .step = 0,
+               };
+               snd_ctl_apply_vmaster_followers(kctl,
+                                               tlv ? init_follower_0dB : init_follower_unmute,
+                                               &arg);
+       }
+
+       if (ctl_ret)
+               *ctl_ret = kctl;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(__snd_hda_add_vmaster);
+
+/* meta hook to call each driver's vmaster hook */
+static void vmaster_hook(void *private_data, int enabled)
+{
+       struct hda_vmaster_mute_hook *hook = private_data;
+
+       hook->hook(hook->codec, enabled);
+}
+
+/**
+ * snd_hda_add_vmaster_hook - Add a vmaster hw specific hook
+ * @codec: the HDA codec
+ * @hook: the vmaster hook object
+ *
+ * Add a hw specific hook (like EAPD) with the given vmaster switch kctl.
+ */
+int snd_hda_add_vmaster_hook(struct hda_codec *codec,
+                            struct hda_vmaster_mute_hook *hook)
+{
+       if (!hook->hook || !hook->sw_kctl)
+               return 0;
+       hook->codec = codec;
+       snd_ctl_add_vmaster_hook(hook->sw_kctl, vmaster_hook, hook);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_add_vmaster_hook);
+
+/**
+ * snd_hda_sync_vmaster_hook - Sync vmaster hook
+ * @hook: the vmaster hook
+ *
+ * Call the hook with the current value for synchronization.
+ * Should be called in init callback.
+ */
+void snd_hda_sync_vmaster_hook(struct hda_vmaster_mute_hook *hook)
+{
+       if (!hook->hook || !hook->codec)
+               return;
+       /* don't call vmaster hook in the destructor since it might have
+        * been already destroyed
+        */
+       if (hook->codec->bus->shutdown)
+               return;
+       snd_ctl_sync_vmaster_hook(hook->sw_kctl);
+}
+EXPORT_SYMBOL_GPL(snd_hda_sync_vmaster_hook);
+
+
+/**
+ * snd_hda_mixer_amp_switch_info - Info callback for a standard AMP mixer switch
+ * @kcontrol: referred ctl element
+ * @uinfo: pointer to get/store the data
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
+int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_info *uinfo)
+{
+       int chs = get_amp_channels(kcontrol);
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       uinfo->count = chs == 3 ? 2 : 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 1;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_info);
+
+/**
+ * snd_hda_mixer_amp_switch_get - Get callback for a standard AMP mixer switch
+ * @kcontrol: ctl element
+ * @ucontrol: pointer to get/store the data
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
+int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       hda_nid_t nid = get_amp_nid(kcontrol);
+       int chs = get_amp_channels(kcontrol);
+       int dir = get_amp_direction(kcontrol);
+       int idx = get_amp_index(kcontrol);
+       long *valp = ucontrol->value.integer.value;
+
+       if (chs & 1)
+               *valp++ = (snd_hda_codec_amp_read(codec, nid, 0, dir, idx) &
+                          HDA_AMP_MUTE) ? 0 : 1;
+       if (chs & 2)
+               *valp = (snd_hda_codec_amp_read(codec, nid, 1, dir, idx) &
+                        HDA_AMP_MUTE) ? 0 : 1;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_get);
+
+/**
+ * snd_hda_mixer_amp_switch_put - Put callback for a standard AMP mixer switch
+ * @kcontrol: ctl element
+ * @ucontrol: pointer to get/store the data
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
+int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       hda_nid_t nid = get_amp_nid(kcontrol);
+       int chs = get_amp_channels(kcontrol);
+       int dir = get_amp_direction(kcontrol);
+       int idx = get_amp_index(kcontrol);
+       long *valp = ucontrol->value.integer.value;
+       int change = 0;
+
+       if (chs & 1) {
+               if (*valp < 0 || *valp > 1)
+                       return -EINVAL;
+               change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx,
+                                                 HDA_AMP_MUTE,
+                                                 *valp ? 0 : HDA_AMP_MUTE);
+               valp++;
+       }
+       if (chs & 2) {
+               if (*valp < 0 || *valp > 1)
+                       return -EINVAL;
+               change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx,
+                                                  HDA_AMP_MUTE,
+                                                  *valp ? 0 : HDA_AMP_MUTE);
+       }
+       hda_call_check_power_status(codec, nid);
+       return change;
+}
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_put);
+
+/*
+ * SPDIF out controls
+ */
+
+static int snd_hda_spdif_mask_info(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+       uinfo->count = 1;
+       return 0;
+}
+
+static int snd_hda_spdif_cmask_get(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL |
+                                          IEC958_AES0_NONAUDIO |
+                                          IEC958_AES0_CON_EMPHASIS_5015 |
+                                          IEC958_AES0_CON_NOT_COPYRIGHT;
+       ucontrol->value.iec958.status[1] = IEC958_AES1_CON_CATEGORY |
+                                          IEC958_AES1_CON_ORIGINAL;
+       return 0;
+}
+
+static int snd_hda_spdif_pmask_get(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL |
+                                          IEC958_AES0_NONAUDIO |
+                                          IEC958_AES0_PRO_EMPHASIS_5015;
+       return 0;
+}
+
+static int snd_hda_spdif_default_get(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       int idx = kcontrol->private_value;
+       struct hda_spdif_out *spdif;
+
+       if (WARN_ON(codec->spdif_out.used <= idx))
+               return -EINVAL;
+       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;
+}
+
+/* convert from SPDIF status bits to HDA SPDIF bits
+ * bit 0 (DigEn) is always set zero (to be filled later)
+ */
+static unsigned short convert_from_spdif_status(unsigned int sbits)
+{
+       unsigned short val = 0;
+
+       if (sbits & IEC958_AES0_PROFESSIONAL)
+               val |= AC_DIG1_PROFESSIONAL;
+       if (sbits & IEC958_AES0_NONAUDIO)
+               val |= AC_DIG1_NONAUDIO;
+       if (sbits & IEC958_AES0_PROFESSIONAL) {
+               if ((sbits & IEC958_AES0_PRO_EMPHASIS) ==
+                   IEC958_AES0_PRO_EMPHASIS_5015)
+                       val |= AC_DIG1_EMPHASIS;
+       } else {
+               if ((sbits & IEC958_AES0_CON_EMPHASIS) ==
+                   IEC958_AES0_CON_EMPHASIS_5015)
+                       val |= AC_DIG1_EMPHASIS;
+               if (!(sbits & IEC958_AES0_CON_NOT_COPYRIGHT))
+                       val |= AC_DIG1_COPYRIGHT;
+               if (sbits & (IEC958_AES1_CON_ORIGINAL << 8))
+                       val |= AC_DIG1_LEVEL;
+               val |= sbits & (IEC958_AES1_CON_CATEGORY << 8);
+       }
+       return val;
+}
+
+/* convert to SPDIF status bits from HDA SPDIF bits
+ */
+static unsigned int convert_to_spdif_status(unsigned short val)
+{
+       unsigned int sbits = 0;
+
+       if (val & AC_DIG1_NONAUDIO)
+               sbits |= IEC958_AES0_NONAUDIO;
+       if (val & AC_DIG1_PROFESSIONAL)
+               sbits |= IEC958_AES0_PROFESSIONAL;
+       if (sbits & IEC958_AES0_PROFESSIONAL) {
+               if (val & AC_DIG1_EMPHASIS)
+                       sbits |= IEC958_AES0_PRO_EMPHASIS_5015;
+       } else {
+               if (val & AC_DIG1_EMPHASIS)
+                       sbits |= IEC958_AES0_CON_EMPHASIS_5015;
+               if (!(val & AC_DIG1_COPYRIGHT))
+                       sbits |= IEC958_AES0_CON_NOT_COPYRIGHT;
+               if (val & AC_DIG1_LEVEL)
+                       sbits |= (IEC958_AES1_CON_ORIGINAL << 8);
+               sbits |= val & (0x7f << 8);
+       }
+       return sbits;
+}
+
+/* set digital convert verbs both for the given NID and its followers */
+static void set_dig_out(struct hda_codec *codec, hda_nid_t nid,
+                       int mask, int val)
+{
+       const hda_nid_t *d;
+
+       snd_hdac_regmap_update(&codec->core, nid, AC_VERB_SET_DIGI_CONVERT_1,
+                              mask, val);
+       d = codec->follower_dig_outs;
+       if (!d)
+               return;
+       for (; *d; d++)
+               snd_hdac_regmap_update(&codec->core, *d,
+                                      AC_VERB_SET_DIGI_CONVERT_1, mask, val);
+}
+
+static inline void set_dig_out_convert(struct hda_codec *codec, hda_nid_t nid,
+                                      int dig1, int dig2)
+{
+       unsigned int mask = 0;
+       unsigned int val = 0;
+
+       if (dig1 != -1) {
+               mask |= 0xff;
+               val = dig1;
+       }
+       if (dig2 != -1) {
+               mask |= 0xff00;
+               val |= dig2 << 8;
+       }
+       set_dig_out(codec, nid, mask, val);
+}
+
+static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       int idx = kcontrol->private_value;
+       struct hda_spdif_out *spdif;
+       hda_nid_t nid;
+       unsigned short val;
+       int change;
+
+       if (WARN_ON(codec->spdif_out.used <= idx))
+               return -EINVAL;
+       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) |
+               ((unsigned int)ucontrol->value.iec958.status[3] << 24);
+       val = convert_from_spdif_status(spdif->status);
+       val |= spdif->ctls & 1;
+       change = spdif->ctls != val;
+       spdif->ctls = val;
+       if (change && nid != (u16)-1)
+               set_dig_out_convert(codec, nid, val & 0xff, (val >> 8) & 0xff);
+       mutex_unlock(&codec->spdif_mutex);
+       return change;
+}
+
+#define snd_hda_spdif_out_switch_info  snd_ctl_boolean_mono_info
+
+static int snd_hda_spdif_out_switch_get(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       int idx = kcontrol->private_value;
+       struct hda_spdif_out *spdif;
+
+       if (WARN_ON(codec->spdif_out.used <= idx))
+               return -EINVAL;
+       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;
+}
+
+static inline void set_spdif_ctls(struct hda_codec *codec, hda_nid_t nid,
+                                 int dig1, int dig2)
+{
+       set_dig_out_convert(codec, nid, dig1, dig2);
+       /* unmute amp switch (if any) */
+       if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) &&
+           (dig1 & AC_DIG1_ENABLE))
+               snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
+                                           HDA_AMP_MUTE, 0);
+}
+
+static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       int idx = kcontrol->private_value;
+       struct hda_spdif_out *spdif;
+       hda_nid_t nid;
+       unsigned short val;
+       int change;
+
+       if (WARN_ON(codec->spdif_out.used <= idx))
+               return -EINVAL;
+       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;
+       change = spdif->ctls != val;
+       spdif->ctls = val;
+       if (change && nid != (u16)-1)
+               set_spdif_ctls(codec, nid, val & 0xff, -1);
+       mutex_unlock(&codec->spdif_mutex);
+       return change;
+}
+
+static const struct snd_kcontrol_new dig_mixes[] = {
+       {
+               .access = SNDRV_CTL_ELEM_ACCESS_READ,
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),
+               .info = snd_hda_spdif_mask_info,
+               .get = snd_hda_spdif_cmask_get,
+       },
+       {
+               .access = SNDRV_CTL_ELEM_ACCESS_READ,
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PRO_MASK),
+               .info = snd_hda_spdif_mask_info,
+               .get = snd_hda_spdif_pmask_get,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
+               .info = snd_hda_spdif_mask_info,
+               .get = snd_hda_spdif_default_get,
+               .put = snd_hda_spdif_default_put,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH),
+               .info = snd_hda_spdif_out_switch_info,
+               .get = snd_hda_spdif_out_switch_get,
+               .put = snd_hda_spdif_out_switch_put,
+       },
+       { } /* end */
+};
+
+/**
+ * snd_hda_create_dig_out_ctls - create Output SPDIF-related controls
+ * @codec: the HDA codec
+ * @associated_nid: NID that new ctls associated with
+ * @cvt_nid: converter NID
+ * @type: HDA_PCM_TYPE_*
+ * Creates controls related with the digital output.
+ * Called from each patch supporting the digital out.
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hda_create_dig_out_ctls(struct hda_codec *codec,
+                               hda_nid_t associated_nid,
+                               hda_nid_t cvt_nid,
+                               int type)
+{
+       int err;
+       struct snd_kcontrol *kctl;
+       const struct snd_kcontrol_new *dig_mix;
+       int idx = 0;
+       int val = 0;
+       const int spdif_index = 16;
+       struct hda_spdif_out *spdif;
+       struct hda_bus *bus = codec->bus;
+
+       if (bus->primary_dig_out_type == HDA_PCM_TYPE_HDMI &&
+           type == HDA_PCM_TYPE_SPDIF) {
+               idx = spdif_index;
+       } else if (bus->primary_dig_out_type == HDA_PCM_TYPE_SPDIF &&
+                  type == HDA_PCM_TYPE_HDMI) {
+               /* suppose a single SPDIF device */
+               for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) {
+                       struct snd_ctl_elem_id id;
+
+                       kctl = find_mixer_ctl(codec, dig_mix->name, 0, 0);
+                       if (!kctl)
+                               break;
+                       id = kctl->id;
+                       id.index = spdif_index;
+                       err = snd_ctl_rename_id(codec->card, &kctl->id, &id);
+                       if (err < 0)
+                               return err;
+               }
+               bus->primary_dig_out_type = HDA_PCM_TYPE_HDMI;
+       }
+       if (!bus->primary_dig_out_type)
+               bus->primary_dig_out_type = type;
+
+       idx = find_empty_mixer_ctl_idx(codec, "IEC958 Playback Switch", idx);
+       if (idx < 0) {
+               codec_err(codec, "too many IEC958 outputs\n");
+               return -EBUSY;
+       }
+       spdif = snd_array_new(&codec->spdif_out);
+       if (!spdif)
+               return -ENOMEM;
+       for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) {
+               kctl = snd_ctl_new1(dig_mix, codec);
+               if (!kctl)
+                       return -ENOMEM;
+               kctl->id.index = idx;
+               kctl->private_value = codec->spdif_out.used - 1;
+               err = snd_hda_ctl_add(codec, associated_nid, kctl);
+               if (err < 0)
+                       return err;
+       }
+       spdif->nid = cvt_nid;
+       snd_hdac_regmap_read(&codec->core, cvt_nid,
+                            AC_VERB_GET_DIGI_CONVERT_1, &val);
+       spdif->ctls = val;
+       spdif->status = convert_to_spdif_status(spdif->ctls);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_create_dig_out_ctls);
+
+/**
+ * snd_hda_spdif_out_of_nid - get the hda_spdif_out entry from the given NID
+ * @codec: the HDA codec
+ * @nid: widget NID
+ *
+ * call within spdif_mutex lock
+ */
+struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec,
+                                              hda_nid_t nid)
+{
+       struct hda_spdif_out *spdif;
+       int i;
+
+       snd_array_for_each(&codec->spdif_out, i, spdif) {
+               if (spdif->nid == nid)
+                       return spdif;
+       }
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hda_spdif_out_of_nid);
+
+/**
+ * snd_hda_spdif_ctls_unassign - Unassign the given SPDIF ctl
+ * @codec: the HDA codec
+ * @idx: the SPDIF ctl index
+ *
+ * Unassign the widget from the given SPDIF control.
+ */
+void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx)
+{
+       struct hda_spdif_out *spdif;
+
+       if (WARN_ON(codec->spdif_out.used <= idx))
+               return;
+       mutex_lock(&codec->spdif_mutex);
+       spdif = snd_array_elem(&codec->spdif_out, idx);
+       spdif->nid = (u16)-1;
+       mutex_unlock(&codec->spdif_mutex);
+}
+EXPORT_SYMBOL_GPL(snd_hda_spdif_ctls_unassign);
+
+/**
+ * snd_hda_spdif_ctls_assign - Assign the SPDIF controls to the given NID
+ * @codec: the HDA codec
+ * @idx: the SPDIF ctl idx
+ * @nid: widget NID
+ *
+ * Assign the widget to the SPDIF control with the given index.
+ */
+void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid)
+{
+       struct hda_spdif_out *spdif;
+       unsigned short val;
+
+       if (WARN_ON(codec->spdif_out.used <= idx))
+               return;
+       mutex_lock(&codec->spdif_mutex);
+       spdif = snd_array_elem(&codec->spdif_out, idx);
+       if (spdif->nid != nid) {
+               spdif->nid = nid;
+               val = spdif->ctls;
+               set_spdif_ctls(codec, nid, val & 0xff, (val >> 8) & 0xff);
+       }
+       mutex_unlock(&codec->spdif_mutex);
+}
+EXPORT_SYMBOL_GPL(snd_hda_spdif_ctls_assign);
+
+/*
+ * SPDIF sharing with analog output
+ */
+static int spdif_share_sw_get(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol);
+       ucontrol->value.integer.value[0] = mout->share_spdif;
+       return 0;
+}
+
+static int spdif_share_sw_put(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol);
+       mout->share_spdif = !!ucontrol->value.integer.value[0];
+       return 0;
+}
+
+static const struct snd_kcontrol_new spdif_share_sw = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "IEC958 Default PCM Playback Switch",
+       .info = snd_ctl_boolean_mono_info,
+       .get = spdif_share_sw_get,
+       .put = spdif_share_sw_put,
+};
+
+/**
+ * snd_hda_create_spdif_share_sw - create Default PCM switch
+ * @codec: the HDA codec
+ * @mout: multi-out instance
+ */
+int snd_hda_create_spdif_share_sw(struct hda_codec *codec,
+                                 struct hda_multi_out *mout)
+{
+       struct snd_kcontrol *kctl;
+
+       if (!mout->dig_out_nid)
+               return 0;
+
+       kctl = snd_ctl_new1(&spdif_share_sw, mout);
+       if (!kctl)
+               return -ENOMEM;
+       /* ATTENTION: here mout is passed as private_data, instead of codec */
+       return snd_hda_ctl_add(codec, mout->dig_out_nid, kctl);
+}
+EXPORT_SYMBOL_GPL(snd_hda_create_spdif_share_sw);
+
+/*
+ * SPDIF input
+ */
+
+#define snd_hda_spdif_in_switch_info   snd_hda_spdif_out_switch_info
+
+static int snd_hda_spdif_in_switch_get(struct snd_kcontrol *kcontrol,
+                                      struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.integer.value[0] = codec->spdif_in_enable;
+       return 0;
+}
+
+static int snd_hda_spdif_in_switch_put(struct snd_kcontrol *kcontrol,
+                                      struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       hda_nid_t nid = kcontrol->private_value;
+       unsigned int val = !!ucontrol->value.integer.value[0];
+       int change;
+
+       mutex_lock(&codec->spdif_mutex);
+       change = codec->spdif_in_enable != val;
+       if (change) {
+               codec->spdif_in_enable = val;
+               snd_hdac_regmap_write(&codec->core, nid,
+                                     AC_VERB_SET_DIGI_CONVERT_1, val);
+       }
+       mutex_unlock(&codec->spdif_mutex);
+       return change;
+}
+
+static int snd_hda_spdif_in_status_get(struct snd_kcontrol *kcontrol,
+                                      struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       hda_nid_t nid = kcontrol->private_value;
+       unsigned int val;
+       unsigned int sbits;
+
+       snd_hdac_regmap_read(&codec->core, nid,
+                            AC_VERB_GET_DIGI_CONVERT_1, &val);
+       sbits = convert_to_spdif_status(val);
+       ucontrol->value.iec958.status[0] = sbits;
+       ucontrol->value.iec958.status[1] = sbits >> 8;
+       ucontrol->value.iec958.status[2] = sbits >> 16;
+       ucontrol->value.iec958.status[3] = sbits >> 24;
+       return 0;
+}
+
+static const struct snd_kcontrol_new dig_in_ctls[] = {
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, SWITCH),
+               .info = snd_hda_spdif_in_switch_info,
+               .get = snd_hda_spdif_in_switch_get,
+               .put = snd_hda_spdif_in_switch_put,
+       },
+       {
+               .access = SNDRV_CTL_ELEM_ACCESS_READ,
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
+               .info = snd_hda_spdif_mask_info,
+               .get = snd_hda_spdif_in_status_get,
+       },
+       { } /* end */
+};
+
+/**
+ * snd_hda_create_spdif_in_ctls - create Input SPDIF-related controls
+ * @codec: the HDA codec
+ * @nid: audio in widget NID
+ *
+ * Creates controls related with the SPDIF input.
+ * Called from each patch supporting the SPDIF in.
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
+{
+       int err;
+       struct snd_kcontrol *kctl;
+       const struct snd_kcontrol_new *dig_mix;
+       int idx;
+
+       idx = find_empty_mixer_ctl_idx(codec, "IEC958 Capture Switch", 0);
+       if (idx < 0) {
+               codec_err(codec, "too many IEC958 inputs\n");
+               return -EBUSY;
+       }
+       for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) {
+               kctl = snd_ctl_new1(dig_mix, codec);
+               if (!kctl)
+                       return -ENOMEM;
+               kctl->private_value = nid;
+               err = snd_hda_ctl_add(codec, nid, kctl);
+               if (err < 0)
+                       return err;
+       }
+       codec->spdif_in_enable =
+               snd_hda_codec_read(codec, nid, 0,
+                                  AC_VERB_GET_DIGI_CONVERT_1, 0) &
+               AC_DIG1_ENABLE;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_create_spdif_in_ctls);
+
+/**
+ * snd_hda_codec_set_power_to_all - Set the power state to all widgets
+ * @codec: the HDA codec
+ * @fg: function group (not used now)
+ * @power_state: the power state to set (AC_PWRST_*)
+ *
+ * Set the given power state to all widgets that have the power control.
+ * If the codec has power_filter set, it evaluates the power state and
+ * filter out if it's unchanged as D3.
+ */
+void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
+                                   unsigned int power_state)
+{
+       hda_nid_t nid;
+
+       for_each_hda_codec_node(nid, codec) {
+               unsigned int wcaps = get_wcaps(codec, nid);
+               unsigned int state = power_state;
+               if (!(wcaps & AC_WCAP_POWER))
+                       continue;
+               if (codec->power_filter) {
+                       state = codec->power_filter(codec, nid, power_state);
+                       if (state != power_state && power_state == AC_PWRST_D3)
+                               continue;
+               }
+               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE,
+                                   state);
+       }
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_set_power_to_all);
+
+/**
+ * snd_hda_codec_eapd_power_filter - A power filter callback for EAPD
+ * @codec: the HDA codec
+ * @nid: widget NID
+ * @power_state: power state to evalue
+ *
+ * Don't power down the widget if it controls eapd and EAPD_BTLENABLE is set.
+ * This can be used a codec power_filter callback.
+ */
+unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec,
+                                            hda_nid_t nid,
+                                            unsigned int power_state)
+{
+       if (nid == codec->core.afg || nid == codec->core.mfg)
+               return power_state;
+       if (power_state == AC_PWRST_D3 &&
+           get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_PIN &&
+           (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) {
+               int eapd = snd_hda_codec_read(codec, nid, 0,
+                                             AC_VERB_GET_EAPD_BTLENABLE, 0);
+               if (eapd & 0x02)
+                       return AC_PWRST_D0;
+       }
+       return power_state;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_eapd_power_filter);
+
+/*
+ * set power state of the codec, and return the power state
+ */
+static unsigned int hda_set_power_state(struct hda_codec *codec,
+                                       unsigned int power_state)
+{
+       hda_nid_t fg = codec->core.afg ? codec->core.afg : codec->core.mfg;
+       int count;
+       unsigned int state;
+       int flags = 0;
+
+       /* this delay seems necessary to avoid click noise at power-down */
+       if (power_state == AC_PWRST_D3) {
+               if (codec->depop_delay < 0)
+                       msleep(codec_has_epss(codec) ? 10 : 100);
+               else if (codec->depop_delay > 0)
+                       msleep(codec->depop_delay);
+               flags = HDA_RW_NO_RESPONSE_FALLBACK;
+       }
+
+       /* repeat power states setting at most 10 times*/
+       for (count = 0; count < 10; count++) {
+               if (codec->patch_ops.set_power_state)
+                       codec->patch_ops.set_power_state(codec, fg,
+                                                        power_state);
+               else {
+                       state = power_state;
+                       if (codec->power_filter)
+                               state = codec->power_filter(codec, fg, state);
+                       if (state == power_state || power_state != AC_PWRST_D3)
+                               snd_hda_codec_read(codec, fg, flags,
+                                                  AC_VERB_SET_POWER_STATE,
+                                                  state);
+                       snd_hda_codec_set_power_to_all(codec, fg, power_state);
+               }
+               state = snd_hda_sync_power_state(codec, fg, power_state);
+               if (!(state & AC_PWRST_ERROR))
+                       break;
+       }
+
+       return state;
+}
+
+/* sync power states of all widgets;
+ * this is called at the end of codec parsing
+ */
+static void sync_power_up_states(struct hda_codec *codec)
+{
+       hda_nid_t nid;
+
+       /* don't care if no filter is used */
+       if (!codec->power_filter)
+               return;
+
+       for_each_hda_codec_node(nid, codec) {
+               unsigned int wcaps = get_wcaps(codec, nid);
+               unsigned int target;
+               if (!(wcaps & AC_WCAP_POWER))
+                       continue;
+               target = codec->power_filter(codec, nid, AC_PWRST_D0);
+               if (target == AC_PWRST_D0)
+                       continue;
+               if (!snd_hda_check_power_state(codec, nid, target))
+                       snd_hda_codec_write(codec, nid, 0,
+                                           AC_VERB_SET_POWER_STATE, target);
+       }
+}
+
+#ifdef CONFIG_SND_HDA_RECONFIG
+/* execute additional init verbs */
+static void hda_exec_init_verbs(struct hda_codec *codec)
+{
+       if (codec->init_verbs.list)
+               snd_hda_sequence_write(codec, codec->init_verbs.list);
+}
+#else
+static inline void hda_exec_init_verbs(struct hda_codec *codec) {}
+#endif
+
+/* update the power on/off account with the current jiffies */
+static void update_power_acct(struct hda_codec *codec, bool on)
+{
+       unsigned long delta = jiffies - codec->power_jiffies;
+
+       if (on)
+               codec->power_on_acct += delta;
+       else
+               codec->power_off_acct += delta;
+       codec->power_jiffies += delta;
+}
+
+void snd_hda_update_power_acct(struct hda_codec *codec)
+{
+       update_power_acct(codec, hda_codec_is_power_on(codec));
+}
+
+/*
+ * call suspend and power-down; used both from PM and power-save
+ * this function returns the power state in the end
+ */
+static unsigned int hda_call_codec_suspend(struct hda_codec *codec)
+{
+       unsigned int state;
+
+       snd_hdac_enter_pm(&codec->core);
+       if (codec->patch_ops.suspend)
+               codec->patch_ops.suspend(codec);
+       if (!codec->no_stream_clean_at_suspend)
+               hda_cleanup_all_streams(codec);
+       state = hda_set_power_state(codec, AC_PWRST_D3);
+       update_power_acct(codec, true);
+       snd_hdac_leave_pm(&codec->core);
+       return state;
+}
+
+/*
+ * kick up codec; used both from PM and power-save
+ */
+static void hda_call_codec_resume(struct hda_codec *codec)
+{
+       snd_hdac_enter_pm(&codec->core);
+       if (codec->core.regmap)
+               regcache_mark_dirty(codec->core.regmap);
+
+       codec->power_jiffies = jiffies;
+
+       hda_set_power_state(codec, AC_PWRST_D0);
+       restore_shutup_pins(codec);
+       hda_exec_init_verbs(codec);
+       snd_hda_jack_set_dirty_all(codec);
+       if (codec->patch_ops.resume)
+               codec->patch_ops.resume(codec);
+       else {
+               if (codec->patch_ops.init)
+                       codec->patch_ops.init(codec);
+               snd_hda_regmap_sync(codec);
+       }
+
+       snd_hda_jack_report_sync(codec);
+       codec->core.dev.power.power_state = PMSG_ON;
+       snd_hdac_leave_pm(&codec->core);
+       if (codec->jackpoll_interval)
+               schedule_delayed_work(&codec->jackpoll_work,
+                                     codec->jackpoll_interval);
+}
+
+static int hda_codec_runtime_suspend(struct device *dev)
+{
+       struct hda_codec *codec = dev_to_hda_codec(dev);
+       unsigned int state;
+
+       /* Nothing to do if card registration fails and the component driver never probes */
+       if (!codec->card)
+               return 0;
+
+       state = hda_call_codec_suspend(codec);
+       if (codec->link_down_at_suspend ||
+           (codec_has_clkstop(codec) && codec_has_epss(codec) &&
+            (state & AC_PWRST_CLK_STOP_OK)))
+               snd_hdac_codec_link_down(&codec->core);
+       snd_hda_codec_display_power(codec, false);
+
+       return 0;
+}
+
+static int hda_codec_runtime_resume(struct device *dev)
+{
+       struct hda_codec *codec = dev_to_hda_codec(dev);
+
+       /* Nothing to do if card registration fails and the component driver never probes */
+       if (!codec->card)
+               return 0;
+
+       snd_hda_codec_display_power(codec, true);
+       snd_hdac_codec_link_up(&codec->core);
+       hda_call_codec_resume(codec);
+       pm_runtime_mark_last_busy(dev);
+       return 0;
+}
+
+static int hda_codec_runtime_idle(struct device *dev)
+{
+       struct hda_codec *codec = dev_to_hda_codec(dev);
+
+       if (codec->jackpoll_interval && !codec->bus->jackpoll_in_suspend)
+               return -EBUSY;
+       return 0;
+}
+
+static int hda_codec_pm_prepare(struct device *dev)
+{
+       struct hda_codec *codec = dev_to_hda_codec(dev);
+
+       cancel_delayed_work_sync(&codec->jackpoll_work);
+       dev->power.power_state = PMSG_SUSPEND;
+       return pm_runtime_suspended(dev);
+}
+
+static void hda_codec_pm_complete(struct device *dev)
+{
+       struct hda_codec *codec = dev_to_hda_codec(dev);
+
+       /* If no other pm-functions are called between prepare() and complete() */
+       if (dev->power.power_state.event == PM_EVENT_SUSPEND)
+               dev->power.power_state = PMSG_RESUME;
+
+       if (pm_runtime_suspended(dev) && (codec->jackpoll_interval ||
+           hda_codec_need_resume(codec) || codec->forced_resume))
+               pm_request_resume(dev);
+}
+
+static int hda_codec_pm_suspend(struct device *dev)
+{
+       dev->power.power_state = PMSG_SUSPEND;
+       return pm_runtime_force_suspend(dev);
+}
+
+static int hda_codec_pm_resume(struct device *dev)
+{
+       dev->power.power_state = PMSG_RESUME;
+       return pm_runtime_force_resume(dev);
+}
+
+static int hda_codec_pm_freeze(struct device *dev)
+{
+       struct hda_codec *codec = dev_to_hda_codec(dev);
+
+       cancel_delayed_work_sync(&codec->jackpoll_work);
+       dev->power.power_state = PMSG_FREEZE;
+       return pm_runtime_force_suspend(dev);
+}
+
+static int hda_codec_pm_thaw(struct device *dev)
+{
+       dev->power.power_state = PMSG_THAW;
+       return pm_runtime_force_resume(dev);
+}
+
+static int hda_codec_pm_restore(struct device *dev)
+{
+       dev->power.power_state = PMSG_RESTORE;
+       return pm_runtime_force_resume(dev);
+}
+
+/* referred in hda_bind.c */
+const struct dev_pm_ops hda_codec_driver_pm = {
+       .prepare = pm_sleep_ptr(hda_codec_pm_prepare),
+       .complete = pm_sleep_ptr(hda_codec_pm_complete),
+       .suspend = pm_sleep_ptr(hda_codec_pm_suspend),
+       .resume = pm_sleep_ptr(hda_codec_pm_resume),
+       .freeze = pm_sleep_ptr(hda_codec_pm_freeze),
+       .thaw = pm_sleep_ptr(hda_codec_pm_thaw),
+       .poweroff = pm_sleep_ptr(hda_codec_pm_suspend),
+       .restore = pm_sleep_ptr(hda_codec_pm_restore),
+       RUNTIME_PM_OPS(hda_codec_runtime_suspend, hda_codec_runtime_resume,
+                      hda_codec_runtime_idle)
+};
+
+/* suspend the codec at shutdown; called from driver's shutdown callback */
+void snd_hda_codec_shutdown(struct hda_codec *codec)
+{
+       struct hda_pcm *cpcm;
+
+       /* Skip the shutdown if codec is not registered */
+       if (!codec->core.registered)
+               return;
+
+       codec->jackpoll_interval = 0; /* don't poll any longer */
+       cancel_delayed_work_sync(&codec->jackpoll_work);
+       list_for_each_entry(cpcm, &codec->pcm_list_head, list)
+               snd_pcm_suspend_all(cpcm->pcm);
+
+       pm_runtime_force_suspend(hda_codec_dev(codec));
+       pm_runtime_disable(hda_codec_dev(codec));
+}
+
+/*
+ * add standard channel maps if not specified
+ */
+static int add_std_chmaps(struct hda_codec *codec)
+{
+       struct hda_pcm *pcm;
+       int str, err;
+
+       list_for_each_entry(pcm, &codec->pcm_list_head, list) {
+               for (str = 0; str < 2; str++) {
+                       struct hda_pcm_stream *hinfo = &pcm->stream[str];
+                       struct snd_pcm_chmap *chmap;
+                       const struct snd_pcm_chmap_elem *elem;
+
+                       if (!pcm->pcm || pcm->own_chmap || !hinfo->substreams)
+                               continue;
+                       elem = hinfo->chmap ? hinfo->chmap : snd_pcm_std_chmaps;
+                       err = snd_pcm_add_chmap_ctls(pcm->pcm, str, elem,
+                                                    hinfo->channels_max,
+                                                    0, &chmap);
+                       if (err < 0)
+                               return err;
+                       chmap->channel_mask = SND_PCM_CHMAP_MASK_2468;
+               }
+       }
+       return 0;
+}
+
+/* default channel maps for 2.1 speakers;
+ * since HD-audio supports only stereo, odd number channels are omitted
+ */
+const struct snd_pcm_chmap_elem snd_pcm_2_1_chmaps[] = {
+       { .channels = 2,
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
+       { .channels = 4,
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+                  SNDRV_CHMAP_LFE, SNDRV_CHMAP_LFE } },
+       { }
+};
+EXPORT_SYMBOL_GPL(snd_pcm_2_1_chmaps);
+
+int snd_hda_codec_build_controls(struct hda_codec *codec)
+{
+       int err = 0;
+       hda_exec_init_verbs(codec);
+       /* continue to initialize... */
+       if (codec->patch_ops.init)
+               err = codec->patch_ops.init(codec);
+       if (!err && codec->patch_ops.build_controls)
+               err = codec->patch_ops.build_controls(codec);
+       if (err < 0)
+               return err;
+
+       /* we create chmaps here instead of build_pcms */
+       err = add_std_chmaps(codec);
+       if (err < 0)
+               return err;
+
+       snd_hda_jack_report_sync(codec); /* call at the last init point */
+       if (codec->jackpoll_interval)
+               schedule_delayed_work(&codec->jackpoll_work,
+                                     codec->jackpoll_interval);
+
+       sync_power_up_states(codec);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_build_controls);
+
+/*
+ * PCM stuff
+ */
+static int hda_pcm_default_open_close(struct hda_pcm_stream *hinfo,
+                                     struct hda_codec *codec,
+                                     struct snd_pcm_substream *substream)
+{
+       return 0;
+}
+
+static int hda_pcm_default_prepare(struct hda_pcm_stream *hinfo,
+                                  struct hda_codec *codec,
+                                  unsigned int stream_tag,
+                                  unsigned int format,
+                                  struct snd_pcm_substream *substream)
+{
+       snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format);
+       return 0;
+}
+
+static int hda_pcm_default_cleanup(struct hda_pcm_stream *hinfo,
+                                  struct hda_codec *codec,
+                                  struct snd_pcm_substream *substream)
+{
+       snd_hda_codec_cleanup_stream(codec, hinfo->nid);
+       return 0;
+}
+
+static int set_pcm_default_values(struct hda_codec *codec,
+                                 struct hda_pcm_stream *info)
+{
+       int err;
+
+       /* query support PCM information from the given NID */
+       if (info->nid && (!info->rates || !info->formats)) {
+               err = snd_hda_query_supported_pcm(codec, info->nid,
+                               info->rates ? NULL : &info->rates,
+                               info->formats ? NULL : &info->formats,
+                               info->subformats ? NULL : &info->subformats,
+                               info->maxbps ? NULL : &info->maxbps);
+               if (err < 0)
+                       return err;
+       }
+       if (info->ops.open == NULL)
+               info->ops.open = hda_pcm_default_open_close;
+       if (info->ops.close == NULL)
+               info->ops.close = hda_pcm_default_open_close;
+       if (info->ops.prepare == NULL) {
+               if (snd_BUG_ON(!info->nid))
+                       return -EINVAL;
+               info->ops.prepare = hda_pcm_default_prepare;
+       }
+       if (info->ops.cleanup == NULL) {
+               if (snd_BUG_ON(!info->nid))
+                       return -EINVAL;
+               info->ops.cleanup = hda_pcm_default_cleanup;
+       }
+       return 0;
+}
+
+/*
+ * codec prepare/cleanup entries
+ */
+/**
+ * snd_hda_codec_prepare - Prepare a stream
+ * @codec: the HDA codec
+ * @hinfo: PCM information
+ * @stream: stream tag to assign
+ * @format: format id to assign
+ * @substream: PCM substream to assign
+ *
+ * Calls the prepare callback set by the codec with the given arguments.
+ * Clean up the inactive streams when successful.
+ */
+int snd_hda_codec_prepare(struct hda_codec *codec,
+                         struct hda_pcm_stream *hinfo,
+                         unsigned int stream,
+                         unsigned int format,
+                         struct snd_pcm_substream *substream)
+{
+       int ret;
+       mutex_lock(&codec->bus->prepare_mutex);
+       if (hinfo->ops.prepare)
+               ret = hinfo->ops.prepare(hinfo, codec, stream, format,
+                                        substream);
+       else
+               ret = -ENODEV;
+       if (ret >= 0)
+               purify_inactive_streams(codec);
+       mutex_unlock(&codec->bus->prepare_mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_prepare);
+
+/**
+ * snd_hda_codec_cleanup - Clean up stream resources
+ * @codec: the HDA codec
+ * @hinfo: PCM information
+ * @substream: PCM substream
+ *
+ * Calls the cleanup callback set by the codec with the given arguments.
+ */
+void snd_hda_codec_cleanup(struct hda_codec *codec,
+                          struct hda_pcm_stream *hinfo,
+                          struct snd_pcm_substream *substream)
+{
+       mutex_lock(&codec->bus->prepare_mutex);
+       if (hinfo->ops.cleanup)
+               hinfo->ops.cleanup(hinfo, codec, substream);
+       mutex_unlock(&codec->bus->prepare_mutex);
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_cleanup);
+
+/* global */
+const char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = {
+       "Audio", "SPDIF", "HDMI", "Modem"
+};
+
+/*
+ * get the empty PCM device number to assign
+ */
+static int get_empty_pcm_device(struct hda_bus *bus, unsigned int type)
+{
+       /* audio device indices; not linear to keep compatibility */
+       /* assigned to static slots up to dev#10; if more needed, assign
+        * the later slot dynamically (when CONFIG_SND_DYNAMIC_MINORS=y)
+        */
+       static const int audio_idx[HDA_PCM_NTYPES][5] = {
+               [HDA_PCM_TYPE_AUDIO] = { 0, 2, 4, 5, -1 },
+               [HDA_PCM_TYPE_SPDIF] = { 1, -1 },
+               [HDA_PCM_TYPE_HDMI]  = { 3, 7, 8, 9, -1 },
+               [HDA_PCM_TYPE_MODEM] = { 6, -1 },
+       };
+       int i;
+
+       if (type >= HDA_PCM_NTYPES) {
+               dev_err(bus->card->dev, "Invalid PCM type %d\n", type);
+               return -EINVAL;
+       }
+
+       for (i = 0; audio_idx[type][i] >= 0; i++) {
+#ifndef CONFIG_SND_DYNAMIC_MINORS
+               if (audio_idx[type][i] >= 8)
+                       break;
+#endif
+               if (!test_and_set_bit(audio_idx[type][i], bus->pcm_dev_bits))
+                       return audio_idx[type][i];
+       }
+
+#ifdef CONFIG_SND_DYNAMIC_MINORS
+       /* non-fixed slots starting from 10 */
+       for (i = 10; i < 32; i++) {
+               if (!test_and_set_bit(i, bus->pcm_dev_bits))
+                       return i;
+       }
+#endif
+
+       dev_warn(bus->card->dev, "Too many %s devices\n",
+               snd_hda_pcm_type_name[type]);
+#ifndef CONFIG_SND_DYNAMIC_MINORS
+       dev_warn(bus->card->dev,
+                "Consider building the kernel with CONFIG_SND_DYNAMIC_MINORS=y\n");
+#endif
+       return -EAGAIN;
+}
+
+/* call build_pcms ops of the given codec and set up the default parameters */
+int snd_hda_codec_parse_pcms(struct hda_codec *codec)
+{
+       struct hda_pcm *cpcm;
+       int err;
+
+       if (!list_empty(&codec->pcm_list_head))
+               return 0; /* already parsed */
+
+       if (!codec->patch_ops.build_pcms)
+               return 0;
+
+       err = codec->patch_ops.build_pcms(codec);
+       if (err < 0) {
+               codec_err(codec, "cannot build PCMs for #%d (error %d)\n",
+                         codec->core.addr, err);
+               return err;
+       }
+
+       list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
+               int stream;
+
+               for_each_pcm_streams(stream) {
+                       struct hda_pcm_stream *info = &cpcm->stream[stream];
+
+                       if (!info->substreams)
+                               continue;
+                       err = set_pcm_default_values(codec, info);
+                       if (err < 0) {
+                               codec_warn(codec,
+                                          "fail to setup default for PCM %s\n",
+                                          cpcm->name);
+                               return err;
+                       }
+               }
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_parse_pcms);
+
+/* assign all PCMs of the given codec */
+int snd_hda_codec_build_pcms(struct hda_codec *codec)
+{
+       struct hda_bus *bus = codec->bus;
+       struct hda_pcm *cpcm;
+       int dev, err;
+
+       err = snd_hda_codec_parse_pcms(codec);
+       if (err < 0)
+               return err;
+
+       /* attach a new PCM streams */
+       list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
+               if (cpcm->pcm)
+                       continue; /* already attached */
+               if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams)
+                       continue; /* no substreams assigned */
+
+               dev = get_empty_pcm_device(bus, cpcm->pcm_type);
+               if (dev < 0) {
+                       cpcm->device = SNDRV_PCM_INVALID_DEVICE;
+                       continue; /* no fatal error */
+               }
+               cpcm->device = dev;
+               err =  snd_hda_attach_pcm_stream(bus, codec, cpcm);
+               if (err < 0) {
+                       codec_err(codec,
+                                 "cannot attach PCM stream %d for codec #%d\n",
+                                 dev, codec->core.addr);
+                       continue; /* no fatal error */
+               }
+       }
+
+       return 0;
+}
+
+/**
+ * snd_hda_add_new_ctls - create controls from the array
+ * @codec: the HDA codec
+ * @knew: the array of struct snd_kcontrol_new
+ *
+ * This helper function creates and add new controls in the given array.
+ * The array must be terminated with an empty entry as terminator.
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hda_add_new_ctls(struct hda_codec *codec,
+                        const struct snd_kcontrol_new *knew)
+{
+       int err;
+
+       for (; knew->name; knew++) {
+               struct snd_kcontrol *kctl;
+               int addr = 0, idx = 0;
+               if (knew->iface == (__force snd_ctl_elem_iface_t)-1)
+                       continue; /* skip this codec private value */
+               for (;;) {
+                       kctl = snd_ctl_new1(knew, codec);
+                       if (!kctl)
+                               return -ENOMEM;
+                       /* Do not use the id.device field for MIXER elements.
+                        * This field is for real device numbers (like PCM) but codecs
+                        * are hidden components from the user space view (unrelated
+                        * to the mixer element identification).
+                        */
+                       if (addr > 0 && codec->ctl_dev_id)
+                               kctl->id.device = addr;
+                       if (idx > 0)
+                               kctl->id.index = idx;
+                       err = snd_hda_ctl_add(codec, 0, kctl);
+                       if (!err)
+                               break;
+                       /* try first with another device index corresponding to
+                        * the codec addr; if it still fails (or it's the
+                        * primary codec), then try another control index
+                        */
+                       if (!addr && codec->core.addr) {
+                               addr = codec->core.addr;
+                               if (!codec->ctl_dev_id)
+                                       idx += 10 * addr;
+                       } else if (!idx && !knew->index) {
+                               idx = find_empty_mixer_ctl_idx(codec,
+                                                              knew->name, 0);
+                               if (idx <= 0)
+                                       return err;
+                       } else
+                               return err;
+               }
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_add_new_ctls);
+
+/**
+ * snd_hda_codec_set_power_save - Configure codec's runtime PM
+ * @codec: codec device to configure
+ * @delay: autosuspend delay
+ */
+void snd_hda_codec_set_power_save(struct hda_codec *codec, int delay)
+{
+       struct device *dev = hda_codec_dev(codec);
+
+       if (delay == 0 && codec->auto_runtime_pm)
+               delay = 3000;
+
+       if (delay > 0) {
+               pm_runtime_set_autosuspend_delay(dev, delay);
+               pm_runtime_use_autosuspend(dev);
+               pm_runtime_allow(dev);
+               if (!pm_runtime_suspended(dev))
+                       pm_runtime_mark_last_busy(dev);
+       } else {
+               pm_runtime_dont_use_autosuspend(dev);
+               pm_runtime_forbid(dev);
+       }
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_set_power_save);
+
+/**
+ * snd_hda_set_power_save - reprogram autosuspend for the given delay
+ * @bus: HD-audio bus
+ * @delay: autosuspend delay in msec, 0 = off
+ *
+ * Synchronize the runtime PM autosuspend state from the power_save option.
+ */
+void snd_hda_set_power_save(struct hda_bus *bus, int delay)
+{
+       struct hda_codec *c;
+
+       list_for_each_codec(c, bus)
+               snd_hda_codec_set_power_save(c, delay);
+}
+EXPORT_SYMBOL_GPL(snd_hda_set_power_save);
+
+/**
+ * snd_hda_check_amp_list_power - Check the amp list and update the power
+ * @codec: HD-audio codec
+ * @check: the object containing an AMP list and the status
+ * @nid: NID to check / update
+ *
+ * Check whether the given NID is in the amp list.  If it's in the list,
+ * check the current AMP status, and update the power-status according
+ * to the mute status.
+ *
+ * This function is supposed to be set or called from the check_power_status
+ * patch ops.
+ */
+int snd_hda_check_amp_list_power(struct hda_codec *codec,
+                                struct hda_loopback_check *check,
+                                hda_nid_t nid)
+{
+       const struct hda_amp_list *p;
+       int ch, v;
+
+       if (!check->amplist)
+               return 0;
+       for (p = check->amplist; p->nid; p++) {
+               if (p->nid == nid)
+                       break;
+       }
+       if (!p->nid)
+               return 0; /* nothing changed */
+
+       for (p = check->amplist; p->nid; p++) {
+               for (ch = 0; ch < 2; ch++) {
+                       v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
+                                                  p->idx);
+                       if (!(v & HDA_AMP_MUTE) && v > 0) {
+                               if (!check->power_on) {
+                                       check->power_on = 1;
+                                       snd_hda_power_up_pm(codec);
+                               }
+                               return 1;
+                       }
+               }
+       }
+       if (check->power_on) {
+               check->power_on = 0;
+               snd_hda_power_down_pm(codec);
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_check_amp_list_power);
+
+/*
+ * input MUX helper
+ */
+
+/**
+ * snd_hda_input_mux_info - Info callback helper for the input-mux enum
+ * @imux: imux helper object
+ * @uinfo: pointer to get/store the data
+ */
+int snd_hda_input_mux_info(const struct hda_input_mux *imux,
+                          struct snd_ctl_elem_info *uinfo)
+{
+       unsigned int index;
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = imux->num_items;
+       if (!imux->num_items)
+               return 0;
+       index = uinfo->value.enumerated.item;
+       if (index >= imux->num_items)
+               index = imux->num_items - 1;
+       strcpy(uinfo->value.enumerated.name, imux->items[index].label);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_input_mux_info);
+
+/**
+ * snd_hda_input_mux_put - Put callback helper for the input-mux enum
+ * @codec: the HDA codec
+ * @imux: imux helper object
+ * @ucontrol: pointer to get/store the data
+ * @nid: input mux NID
+ * @cur_val: pointer to get/store the current imux value
+ */
+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)
+{
+       unsigned int idx;
+
+       if (!imux->num_items)
+               return 0;
+       idx = ucontrol->value.enumerated.item[0];
+       if (idx >= imux->num_items)
+               idx = imux->num_items - 1;
+       if (*cur_val == idx)
+               return 0;
+       snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_CONNECT_SEL,
+                                 imux->items[idx].index);
+       *cur_val = idx;
+       return 1;
+}
+EXPORT_SYMBOL_GPL(snd_hda_input_mux_put);
+
+
+/**
+ * snd_hda_enum_helper_info - Helper for simple enum ctls
+ * @kcontrol: ctl element
+ * @uinfo: pointer to get/store the data
+ * @num_items: number of enum items
+ * @texts: enum item string array
+ *
+ * process kcontrol info callback of a simple string enum array
+ * when @num_items is 0 or @texts is NULL, assume a boolean enum array
+ */
+int snd_hda_enum_helper_info(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_info *uinfo,
+                            int num_items, const char * const *texts)
+{
+       static const char * const texts_default[] = {
+               "Disabled", "Enabled"
+       };
+
+       if (!texts || !num_items) {
+               num_items = 2;
+               texts = texts_default;
+       }
+
+       return snd_ctl_enum_info(uinfo, 1, num_items, texts);
+}
+EXPORT_SYMBOL_GPL(snd_hda_enum_helper_info);
+
+/*
+ * Multi-channel / digital-out PCM helper functions
+ */
+
+/* setup SPDIF output stream */
+static void setup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid,
+                                unsigned int stream_tag, unsigned int format)
+{
+       struct hda_spdif_out *spdif;
+       unsigned int curr_fmt;
+       bool reset;
+
+       spdif = snd_hda_spdif_out_of_nid(codec, nid);
+       /* Add sanity check to pass klockwork check.
+        * This should never happen.
+        */
+       if (WARN_ON(spdif == NULL))
+               return;
+
+       curr_fmt = snd_hda_codec_read(codec, nid, 0,
+                                     AC_VERB_GET_STREAM_FORMAT, 0);
+       reset = codec->spdif_status_reset &&
+               (spdif->ctls & AC_DIG1_ENABLE) &&
+               curr_fmt != format;
+
+       /* turn off SPDIF if needed; otherwise the IEC958 bits won't be
+          updated */
+       if (reset)
+               set_dig_out_convert(codec, nid,
+                                   spdif->ctls & ~AC_DIG1_ENABLE & 0xff,
+                                   -1);
+       snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
+       if (codec->follower_dig_outs) {
+               const hda_nid_t *d;
+               for (d = codec->follower_dig_outs; *d; d++)
+                       snd_hda_codec_setup_stream(codec, *d, stream_tag, 0,
+                                                  format);
+       }
+       /* turn on again (if needed) */
+       if (reset)
+               set_dig_out_convert(codec, nid,
+                                   spdif->ctls & 0xff, -1);
+}
+
+static void cleanup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid)
+{
+       snd_hda_codec_cleanup_stream(codec, nid);
+       if (codec->follower_dig_outs) {
+               const hda_nid_t *d;
+               for (d = codec->follower_dig_outs; *d; d++)
+                       snd_hda_codec_cleanup_stream(codec, *d);
+       }
+}
+
+/**
+ * snd_hda_multi_out_dig_open - open the digital out in the exclusive mode
+ * @codec: the HDA codec
+ * @mout: hda_multi_out object
+ */
+int snd_hda_multi_out_dig_open(struct hda_codec *codec,
+                              struct hda_multi_out *mout)
+{
+       mutex_lock(&codec->spdif_mutex);
+       if (mout->dig_out_used == HDA_DIG_ANALOG_DUP)
+               /* already opened as analog dup; reset it once */
+               cleanup_dig_out_stream(codec, mout->dig_out_nid);
+       mout->dig_out_used = HDA_DIG_EXCLUSIVE;
+       mutex_unlock(&codec->spdif_mutex);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_open);
+
+/**
+ * snd_hda_multi_out_dig_prepare - prepare the digital out stream
+ * @codec: the HDA codec
+ * @mout: hda_multi_out object
+ * @stream_tag: stream tag to assign
+ * @format: format id to assign
+ * @substream: PCM substream to assign
+ */
+int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
+                                 struct hda_multi_out *mout,
+                                 unsigned int stream_tag,
+                                 unsigned int format,
+                                 struct snd_pcm_substream *substream)
+{
+       mutex_lock(&codec->spdif_mutex);
+       setup_dig_out_stream(codec, mout->dig_out_nid, stream_tag, format);
+       mutex_unlock(&codec->spdif_mutex);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_prepare);
+
+/**
+ * snd_hda_multi_out_dig_cleanup - clean-up the digital out stream
+ * @codec: the HDA codec
+ * @mout: hda_multi_out object
+ */
+int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec,
+                                 struct hda_multi_out *mout)
+{
+       mutex_lock(&codec->spdif_mutex);
+       cleanup_dig_out_stream(codec, mout->dig_out_nid);
+       mutex_unlock(&codec->spdif_mutex);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_cleanup);
+
+/**
+ * snd_hda_multi_out_dig_close - release the digital out stream
+ * @codec: the HDA codec
+ * @mout: hda_multi_out object
+ */
+int snd_hda_multi_out_dig_close(struct hda_codec *codec,
+                               struct hda_multi_out *mout)
+{
+       mutex_lock(&codec->spdif_mutex);
+       mout->dig_out_used = 0;
+       mutex_unlock(&codec->spdif_mutex);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_close);
+
+/**
+ * snd_hda_multi_out_analog_open - open analog outputs
+ * @codec: the HDA codec
+ * @mout: hda_multi_out object
+ * @substream: PCM substream to assign
+ * @hinfo: PCM information to assign
+ *
+ * Open analog outputs and set up the hw-constraints.
+ * If the digital outputs can be opened as follower, open the digital
+ * outputs, too.
+ */
+int snd_hda_multi_out_analog_open(struct hda_codec *codec,
+                                 struct hda_multi_out *mout,
+                                 struct snd_pcm_substream *substream,
+                                 struct hda_pcm_stream *hinfo)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       runtime->hw.channels_max = mout->max_channels;
+       if (mout->dig_out_nid) {
+               if (!mout->analog_rates) {
+                       mout->analog_rates = hinfo->rates;
+                       mout->analog_formats = hinfo->formats;
+                       mout->analog_maxbps = hinfo->maxbps;
+               } else {
+                       runtime->hw.rates = mout->analog_rates;
+                       runtime->hw.formats = mout->analog_formats;
+                       hinfo->maxbps = mout->analog_maxbps;
+               }
+               if (!mout->spdif_rates) {
+                       snd_hda_query_supported_pcm(codec, mout->dig_out_nid,
+                                                   &mout->spdif_rates,
+                                                   &mout->spdif_formats,
+                                                   NULL,
+                                                   &mout->spdif_maxbps);
+               }
+               mutex_lock(&codec->spdif_mutex);
+               if (mout->share_spdif) {
+                       if ((runtime->hw.rates & mout->spdif_rates) &&
+                           (runtime->hw.formats & mout->spdif_formats)) {
+                               runtime->hw.rates &= mout->spdif_rates;
+                               runtime->hw.formats &= mout->spdif_formats;
+                               if (mout->spdif_maxbps < hinfo->maxbps)
+                                       hinfo->maxbps = mout->spdif_maxbps;
+                       } else {
+                               mout->share_spdif = 0;
+                               /* FIXME: need notify? */
+                       }
+               }
+               mutex_unlock(&codec->spdif_mutex);
+       }
+       return snd_pcm_hw_constraint_step(substream->runtime, 0,
+                                         SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+}
+EXPORT_SYMBOL_GPL(snd_hda_multi_out_analog_open);
+
+/**
+ * snd_hda_multi_out_analog_prepare - Preapre the analog outputs.
+ * @codec: the HDA codec
+ * @mout: hda_multi_out object
+ * @stream_tag: stream tag to assign
+ * @format: format id to assign
+ * @substream: PCM substream to assign
+ *
+ * Set up the i/o for analog out.
+ * When the digital out is available, copy the front out to digital out, too.
+ */
+int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
+                                    struct hda_multi_out *mout,
+                                    unsigned int stream_tag,
+                                    unsigned int format,
+                                    struct snd_pcm_substream *substream)
+{
+       const hda_nid_t *nids = mout->dac_nids;
+       int chs = substream->runtime->channels;
+       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 && spdif != NULL &&
+                   snd_hda_is_supported_format(codec, mout->dig_out_nid,
+                                               format) &&
+                   !(spdif->status & IEC958_AES0_NONAUDIO)) {
+                       mout->dig_out_used = HDA_DIG_ANALOG_DUP;
+                       setup_dig_out_stream(codec, mout->dig_out_nid,
+                                            stream_tag, format);
+               } else {
+                       mout->dig_out_used = 0;
+                       cleanup_dig_out_stream(codec, mout->dig_out_nid);
+               }
+       }
+       mutex_unlock(&codec->spdif_mutex);
+
+       /* front */
+       snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag,
+                                  0, format);
+       if (!mout->no_share_stream &&
+           mout->hp_nid && mout->hp_nid != nids[HDA_FRONT])
+               /* headphone out will just decode front left/right (stereo) */
+               snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag,
+                                          0, format);
+       /* extra outputs copied from front */
+       for (i = 0; i < ARRAY_SIZE(mout->hp_out_nid); i++)
+               if (!mout->no_share_stream && mout->hp_out_nid[i])
+                       snd_hda_codec_setup_stream(codec,
+                                                  mout->hp_out_nid[i],
+                                                  stream_tag, 0, format);
+
+       /* surrounds */
+       for (i = 1; i < mout->num_dacs; i++) {
+               if (chs >= (i + 1) * 2) /* independent out */
+                       snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
+                                                  i * 2, format);
+               else if (!mout->no_share_stream) /* copy front */
+                       snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
+                                                  0, format);
+       }
+
+       /* extra surrounds */
+       for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) {
+               int ch = 0;
+               if (!mout->extra_out_nid[i])
+                       break;
+               if (chs >= (i + 1) * 2)
+                       ch = i * 2;
+               else if (!mout->no_share_stream)
+                       break;
+               snd_hda_codec_setup_stream(codec, mout->extra_out_nid[i],
+                                          stream_tag, ch, format);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_multi_out_analog_prepare);
+
+/**
+ * snd_hda_multi_out_analog_cleanup - clean up the setting for analog out
+ * @codec: the HDA codec
+ * @mout: hda_multi_out object
+ */
+int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
+                                    struct hda_multi_out *mout)
+{
+       const hda_nid_t *nids = mout->dac_nids;
+       int i;
+
+       for (i = 0; i < mout->num_dacs; i++)
+               snd_hda_codec_cleanup_stream(codec, nids[i]);
+       if (mout->hp_nid)
+               snd_hda_codec_cleanup_stream(codec, mout->hp_nid);
+       for (i = 0; i < ARRAY_SIZE(mout->hp_out_nid); i++)
+               if (mout->hp_out_nid[i])
+                       snd_hda_codec_cleanup_stream(codec,
+                                                    mout->hp_out_nid[i]);
+       for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
+               if (mout->extra_out_nid[i])
+                       snd_hda_codec_cleanup_stream(codec,
+                                                    mout->extra_out_nid[i]);
+       mutex_lock(&codec->spdif_mutex);
+       if (mout->dig_out_nid && mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
+               cleanup_dig_out_stream(codec, mout->dig_out_nid);
+               mout->dig_out_used = 0;
+       }
+       mutex_unlock(&codec->spdif_mutex);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_multi_out_analog_cleanup);
+
+/**
+ * snd_hda_get_default_vref - Get the default (mic) VREF pin bits
+ * @codec: the HDA codec
+ * @pin: referred pin NID
+ *
+ * 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_GPL(snd_hda_get_default_vref);
+
+/**
+ * snd_hda_correct_pin_ctl - correct the pin ctl value for matching with the pin cap
+ * @codec: the HDA codec
+ * @pin: referred pin NID
+ * @val: pin ctl value to audit
+ */
+unsigned int snd_hda_correct_pin_ctl(struct hda_codec *codec,
+                                    hda_nid_t pin, unsigned int val)
+{
+       static const unsigned int cap_lists[][2] = {
+               { AC_PINCTL_VREF_100, AC_PINCAP_VREF_100 },
+               { AC_PINCTL_VREF_80, AC_PINCAP_VREF_80 },
+               { AC_PINCTL_VREF_50, AC_PINCAP_VREF_50 },
+               { AC_PINCTL_VREF_GRD, AC_PINCAP_VREF_GRD },
+       };
+       unsigned int cap;
+
+       if (!val)
+               return 0;
+       cap = snd_hda_query_pin_caps(codec, pin);
+       if (!cap)
+               return val; /* don't know what to do... */
+
+       if (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;
+       }
+
+       if (val & AC_PINCTL_IN_EN) {
+               if (!(cap & AC_PINCAP_IN))
+                       val &= ~(AC_PINCTL_IN_EN | AC_PINCTL_VREFEN);
+               else {
+                       unsigned int vcap, vref;
+                       int i;
+                       vcap = (cap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
+                       vref = val & AC_PINCTL_VREFEN;
+                       for (i = 0; i < ARRAY_SIZE(cap_lists); i++) {
+                               if (vref == cap_lists[i][0] &&
+                                   !(vcap & cap_lists[i][1])) {
+                                       if (i == ARRAY_SIZE(cap_lists) - 1)
+                                               vref = AC_PINCTL_VREF_HIZ;
+                                       else
+                                               vref = cap_lists[i + 1][0];
+                               }
+                       }
+                       val &= ~AC_PINCTL_VREFEN;
+                       val |= vref;
+               }
+       }
+
+       return val;
+}
+EXPORT_SYMBOL_GPL(snd_hda_correct_pin_ctl);
+
+/**
+ * _snd_hda_set_pin_ctl - Helper to set pin ctl value
+ * @codec: the HDA codec
+ * @pin: referred pin NID
+ * @val: pin control value to set
+ * @cached: access over codec pinctl cache or direct write
+ *
+ * This function is a helper to set a pin ctl value more safely.
+ * It corrects the pin ctl value via snd_hda_correct_pin_ctl(), stores the
+ * value in pin target array via snd_hda_codec_set_pin_target(), then
+ * actually writes the value via either snd_hda_codec_write_cache() or
+ * snd_hda_codec_write() depending on @cached flag.
+ */
+int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin,
+                        unsigned int val, bool cached)
+{
+       val = snd_hda_correct_pin_ctl(codec, pin, val);
+       snd_hda_codec_set_pin_target(codec, pin, val);
+       if (cached)
+               return snd_hda_codec_write_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_GPL(_snd_hda_set_pin_ctl);
+
+/**
+ * snd_hda_add_imux_item - Add an item to input_mux
+ * @codec: the HDA codec
+ * @imux: imux helper object
+ * @label: the name of imux item to assign
+ * @index: index number of imux item to assign
+ * @type_idx: pointer to store the resultant label index
+ *
+ * When the same label is used already in the existing items, the number
+ * suffix is appended to the label.  This label index number is stored
+ * to type_idx when non-NULL pointer is given.
+ */
+int snd_hda_add_imux_item(struct hda_codec *codec,
+                         struct hda_input_mux *imux, const char *label,
+                         int index, int *type_idx)
+{
+       int i, label_idx = 0;
+       if (imux->num_items >= HDA_MAX_NUM_INPUTS) {
+               codec_err(codec, "hda_codec: Too many imux items!\n");
+               return -EINVAL;
+       }
+       for (i = 0; i < imux->num_items; i++) {
+               if (!strncmp(label, imux->items[i].label, strlen(label)))
+                       label_idx++;
+       }
+       if (type_idx)
+               *type_idx = label_idx;
+       if (label_idx > 0)
+               snprintf(imux->items[imux->num_items].label,
+                        sizeof(imux->items[imux->num_items].label),
+                        "%s %d", label, label_idx);
+       else
+               strscpy(imux->items[imux->num_items].label, label,
+                       sizeof(imux->items[imux->num_items].label));
+       imux->items[imux->num_items].index = index;
+       imux->num_items++;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_add_imux_item);
+
+/**
+ * snd_hda_bus_reset_codecs - Reset the bus
+ * @bus: HD-audio bus
+ */
+void snd_hda_bus_reset_codecs(struct hda_bus *bus)
+{
+       struct hda_codec *codec;
+
+       list_for_each_codec(codec, bus) {
+               /* FIXME: maybe a better way needed for forced reset */
+               if (current_work() != &codec->jackpoll_work.work)
+                       cancel_delayed_work_sync(&codec->jackpoll_work);
+               if (hda_codec_is_power_on(codec)) {
+                       hda_call_codec_suspend(codec);
+                       hda_call_codec_resume(codec);
+               }
+       }
+}
+
+/**
+ * snd_print_pcm_bits - Print the supported PCM fmt bits to the string buffer
+ * @pcm: PCM caps bits
+ * @buf: the string buffer to write
+ * @buflen: the max buffer length
+ *
+ * used by hda_proc.c and hda_eld.c
+ */
+void snd_print_pcm_bits(int pcm, char *buf, int buflen)
+{
+       static const unsigned int bits[] = { 8, 16, 20, 24, 32 };
+       int i, j;
+
+       for (i = 0, j = 0; i < ARRAY_SIZE(bits); i++)
+               if (pcm & (AC_SUPPCM_BITS_8 << i))
+                       j += scnprintf(buf + j, buflen - j,  " %d", bits[i]);
+
+       buf[j] = '\0'; /* necessary when j == 0 */
+}
+EXPORT_SYMBOL_GPL(snd_print_pcm_bits);
+
+MODULE_DESCRIPTION("HDA codec core");
+MODULE_LICENSE("GPL");
diff --git a/sound/hda/common/controller.c b/sound/hda/common/controller.c
new file mode 100644 (file)
index 0000000..84387ed
--- /dev/null
@@ -0,0 +1,1336 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ *  Implementation of primary alsa driver code base for Intel HD Audio.
+ *
+ *  Copyright(c) 2004 Intel Corporation
+ *
+ *  Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ *                     PeiSen Hou <pshou@realtek.com.tw>
+ */
+
+#include <linux/clocksource.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+
+#ifdef CONFIG_X86
+/* for art-tsc conversion */
+#include <asm/tsc.h>
+#endif
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm_params.h>
+#include "hda_controller.h"
+#include "hda_local.h"
+
+#define CREATE_TRACE_POINTS
+#include "controller_trace.h"
+
+/* DSP lock helpers */
+#define dsp_lock(dev)          snd_hdac_dsp_lock(azx_stream(dev))
+#define dsp_unlock(dev)                snd_hdac_dsp_unlock(azx_stream(dev))
+#define dsp_is_locked(dev)     snd_hdac_stream_is_locked(azx_stream(dev))
+
+/* assign a stream for the PCM */
+static inline struct azx_dev *
+azx_assign_device(struct azx *chip, struct snd_pcm_substream *substream)
+{
+       struct hdac_stream *s;
+
+       s = snd_hdac_stream_assign(azx_bus(chip), substream);
+       if (!s)
+               return NULL;
+       return stream_to_azx_dev(s);
+}
+
+/* release the assigned stream */
+static inline void azx_release_device(struct azx_dev *azx_dev)
+{
+       snd_hdac_stream_release(azx_stream(azx_dev));
+}
+
+static inline struct hda_pcm_stream *
+to_hda_pcm_stream(struct snd_pcm_substream *substream)
+{
+       struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+       return &apcm->info->stream[substream->stream];
+}
+
+static u64 azx_adjust_codec_delay(struct snd_pcm_substream *substream,
+                               u64 nsec)
+{
+       struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+       struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
+       u64 codec_frames, codec_nsecs;
+
+       if (!hinfo->ops.get_delay)
+               return nsec;
+
+       codec_frames = hinfo->ops.get_delay(hinfo, apcm->codec, substream);
+       codec_nsecs = div_u64(codec_frames * 1000000000LL,
+                             substream->runtime->rate);
+
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+               return nsec + codec_nsecs;
+
+       return (nsec > codec_nsecs) ? nsec - codec_nsecs : 0;
+}
+
+/*
+ * PCM ops
+ */
+
+static int azx_pcm_close(struct snd_pcm_substream *substream)
+{
+       struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+       struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
+       struct azx *chip = apcm->chip;
+       struct azx_dev *azx_dev = get_azx_dev(substream);
+
+       trace_azx_pcm_close(chip, azx_dev);
+       mutex_lock(&chip->open_mutex);
+       azx_release_device(azx_dev);
+       if (hinfo->ops.close)
+               hinfo->ops.close(hinfo, apcm->codec, substream);
+       snd_hda_power_down(apcm->codec);
+       mutex_unlock(&chip->open_mutex);
+       snd_hda_codec_pcm_put(apcm->info);
+       return 0;
+}
+
+static int azx_pcm_hw_params(struct snd_pcm_substream *substream,
+                            struct snd_pcm_hw_params *hw_params)
+{
+       struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+       struct azx *chip = apcm->chip;
+       struct azx_dev *azx_dev = get_azx_dev(substream);
+       struct hdac_stream *hdas = azx_stream(azx_dev);
+       int ret = 0;
+
+       trace_azx_pcm_hw_params(chip, azx_dev);
+       dsp_lock(azx_dev);
+       if (dsp_is_locked(azx_dev)) {
+               ret = -EBUSY;
+               goto unlock;
+       }
+
+       /* Set up BDLEs here, return -ENOMEM if too many BDLEs are required */
+       hdas->bufsize = params_buffer_bytes(hw_params);
+       hdas->period_bytes = params_period_bytes(hw_params);
+       hdas->format_val = 0;
+       hdas->no_period_wakeup =
+               (hw_params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) &&
+               (hw_params->flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP);
+       if (snd_hdac_stream_setup_periods(hdas) < 0)
+               ret = -ENOMEM;
+
+unlock:
+       dsp_unlock(azx_dev);
+       return ret;
+}
+
+static int azx_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+       struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+       struct azx_dev *azx_dev = get_azx_dev(substream);
+       struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
+
+       /* reset BDL address */
+       dsp_lock(azx_dev);
+       if (!dsp_is_locked(azx_dev))
+               snd_hdac_stream_cleanup(azx_stream(azx_dev));
+
+       snd_hda_codec_cleanup(apcm->codec, hinfo, substream);
+
+       azx_stream(azx_dev)->prepared = 0;
+       dsp_unlock(azx_dev);
+       return 0;
+}
+
+static int azx_pcm_prepare(struct snd_pcm_substream *substream)
+{
+       struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+       struct azx *chip = apcm->chip;
+       struct azx_dev *azx_dev = get_azx_dev(substream);
+       struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       unsigned int format_val, stream_tag, bits;
+       int err;
+       struct hda_spdif_out *spdif =
+               snd_hda_spdif_out_of_nid(apcm->codec, hinfo->nid);
+       unsigned short ctls = spdif ? spdif->ctls : 0;
+
+       trace_azx_pcm_prepare(chip, azx_dev);
+       dsp_lock(azx_dev);
+       if (dsp_is_locked(azx_dev)) {
+               err = -EBUSY;
+               goto unlock;
+       }
+
+       snd_hdac_stream_reset(azx_stream(azx_dev));
+       bits = snd_hdac_stream_format_bits(runtime->format, SNDRV_PCM_SUBFORMAT_STD, hinfo->maxbps);
+
+       format_val = snd_hdac_spdif_stream_format(runtime->channels, bits, runtime->rate, ctls);
+       if (!format_val) {
+               dev_err(chip->card->dev,
+                       "invalid format_val, rate=%d, ch=%d, format=%d\n",
+                       runtime->rate, runtime->channels, runtime->format);
+               err = -EINVAL;
+               goto unlock;
+       }
+
+       err = snd_hdac_stream_set_params(azx_stream(azx_dev), format_val);
+       if (err < 0)
+               goto unlock;
+
+       snd_hdac_stream_setup(azx_stream(azx_dev), false);
+
+       stream_tag = azx_dev->core.stream_tag;
+       /* CA-IBG chips need the playback stream starting from 1 */
+       if ((chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND) &&
+           stream_tag > chip->capture_streams)
+               stream_tag -= chip->capture_streams;
+       err = snd_hda_codec_prepare(apcm->codec, hinfo, stream_tag,
+                                    azx_dev->core.format_val, substream);
+
+ unlock:
+       if (!err)
+               azx_stream(azx_dev)->prepared = 1;
+       dsp_unlock(azx_dev);
+       return err;
+}
+
+static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+       struct azx *chip = apcm->chip;
+       struct hdac_bus *bus = azx_bus(chip);
+       struct azx_dev *azx_dev;
+       struct snd_pcm_substream *s;
+       struct hdac_stream *hstr;
+       bool start;
+       int sbits = 0;
+       int sync_reg;
+
+       azx_dev = get_azx_dev(substream);
+       trace_azx_pcm_trigger(chip, azx_dev, cmd);
+
+       hstr = azx_stream(azx_dev);
+       if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC)
+               sync_reg = AZX_REG_OLD_SSYNC;
+       else
+               sync_reg = AZX_REG_SSYNC;
+
+       if (dsp_is_locked(azx_dev) || !hstr->prepared)
+               return -EPIPE;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+       case SNDRV_PCM_TRIGGER_RESUME:
+               start = true;
+               break;
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_STOP:
+               start = false;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_pcm_group_for_each_entry(s, substream) {
+               if (s->pcm->card != substream->pcm->card)
+                       continue;
+               azx_dev = get_azx_dev(s);
+               sbits |= 1 << azx_dev->core.index;
+               snd_pcm_trigger_done(s, substream);
+       }
+
+       spin_lock(&bus->reg_lock);
+
+       /* first, set SYNC bits of corresponding streams */
+       snd_hdac_stream_sync_trigger(hstr, true, sbits, sync_reg);
+
+       snd_pcm_group_for_each_entry(s, substream) {
+               if (s->pcm->card != substream->pcm->card)
+                       continue;
+               azx_dev = get_azx_dev(s);
+               if (start) {
+                       azx_dev->insufficient = 1;
+                       snd_hdac_stream_start(azx_stream(azx_dev));
+               } else {
+                       snd_hdac_stream_stop(azx_stream(azx_dev));
+               }
+       }
+       spin_unlock(&bus->reg_lock);
+
+       snd_hdac_stream_sync(hstr, start, sbits);
+
+       spin_lock(&bus->reg_lock);
+       /* reset SYNC bits */
+       snd_hdac_stream_sync_trigger(hstr, false, sbits, sync_reg);
+       snd_hdac_stream_timecounter_init(hstr, sbits, start);
+       spin_unlock(&bus->reg_lock);
+       return 0;
+}
+
+unsigned int azx_get_pos_lpib(struct azx *chip, struct azx_dev *azx_dev)
+{
+       return snd_hdac_stream_get_pos_lpib(azx_stream(azx_dev));
+}
+EXPORT_SYMBOL_GPL(azx_get_pos_lpib);
+
+unsigned int azx_get_pos_posbuf(struct azx *chip, struct azx_dev *azx_dev)
+{
+       return snd_hdac_stream_get_pos_posbuf(azx_stream(azx_dev));
+}
+EXPORT_SYMBOL_GPL(azx_get_pos_posbuf);
+
+unsigned int azx_get_position(struct azx *chip,
+                             struct azx_dev *azx_dev)
+{
+       struct snd_pcm_substream *substream = azx_dev->core.substream;
+       unsigned int pos;
+       int stream = substream->stream;
+       int delay = 0;
+
+       if (chip->get_position[stream])
+               pos = chip->get_position[stream](chip, azx_dev);
+       else /* use the position buffer as default */
+               pos = azx_get_pos_posbuf(chip, azx_dev);
+
+       if (pos >= azx_dev->core.bufsize)
+               pos = 0;
+
+       if (substream->runtime) {
+               struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+               struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
+
+               if (chip->get_delay[stream])
+                       delay += chip->get_delay[stream](chip, azx_dev, pos);
+               if (hinfo->ops.get_delay)
+                       delay += hinfo->ops.get_delay(hinfo, apcm->codec,
+                                                     substream);
+               substream->runtime->delay = delay;
+       }
+
+       trace_azx_get_position(chip, azx_dev, pos, delay);
+       return pos;
+}
+EXPORT_SYMBOL_GPL(azx_get_position);
+
+static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream)
+{
+       struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+       struct azx *chip = apcm->chip;
+       struct azx_dev *azx_dev = get_azx_dev(substream);
+       return bytes_to_frames(substream->runtime,
+                              azx_get_position(chip, azx_dev));
+}
+
+/*
+ * azx_scale64: Scale base by mult/div while not overflowing sanely
+ *
+ * Derived from scale64_check_overflow in kernel/time/timekeeping.c
+ *
+ * The tmestamps for a 48Khz stream can overflow after (2^64/10^9)/48K which
+ * is about 384307 ie ~4.5 days.
+ *
+ * This scales the calculation so that overflow will happen but after 2^64 /
+ * 48000 secs, which is pretty large!
+ *
+ * In caln below:
+ *     base may overflow, but since there isn’t any additional division
+ *     performed on base it’s OK
+ *     rem can’t overflow because both are 32-bit values
+ */
+
+#ifdef CONFIG_X86
+static u64 azx_scale64(u64 base, u32 num, u32 den)
+{
+       u64 rem;
+
+       rem = do_div(base, den);
+
+       base *= num;
+       rem *= num;
+
+       do_div(rem, den);
+
+       return base + rem;
+}
+
+static int azx_get_sync_time(ktime_t *device,
+               struct system_counterval_t *system, void *ctx)
+{
+       struct snd_pcm_substream *substream = ctx;
+       struct azx_dev *azx_dev = get_azx_dev(substream);
+       struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+       struct azx *chip = apcm->chip;
+       struct snd_pcm_runtime *runtime;
+       u64 ll_counter, ll_counter_l, ll_counter_h;
+       u64 tsc_counter, tsc_counter_l, tsc_counter_h;
+       u32 wallclk_ctr, wallclk_cycles;
+       bool direction;
+       u32 dma_select;
+       u32 timeout;
+       u32 retry_count = 0;
+
+       runtime = substream->runtime;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               direction = 1;
+       else
+               direction = 0;
+
+       /* 0th stream tag is not used, so DMA ch 0 is for 1st stream tag */
+       do {
+               timeout = 100;
+               dma_select = (direction << GTSCC_CDMAS_DMA_DIR_SHIFT) |
+                                       (azx_dev->core.stream_tag - 1);
+               snd_hdac_chip_writel(azx_bus(chip), GTSCC, dma_select);
+
+               /* Enable the capture */
+               snd_hdac_chip_updatel(azx_bus(chip), GTSCC, 0, GTSCC_TSCCI_MASK);
+
+               while (timeout) {
+                       if (snd_hdac_chip_readl(azx_bus(chip), GTSCC) &
+                                               GTSCC_TSCCD_MASK)
+                               break;
+
+                       timeout--;
+               }
+
+               if (!timeout) {
+                       dev_err(chip->card->dev, "GTSCC capture Timedout!\n");
+                       return -EIO;
+               }
+
+               /* Read wall clock counter */
+               wallclk_ctr = snd_hdac_chip_readl(azx_bus(chip), WALFCC);
+
+               /* Read TSC counter */
+               tsc_counter_l = snd_hdac_chip_readl(azx_bus(chip), TSCCL);
+               tsc_counter_h = snd_hdac_chip_readl(azx_bus(chip), TSCCU);
+
+               /* Read Link counter */
+               ll_counter_l = snd_hdac_chip_readl(azx_bus(chip), LLPCL);
+               ll_counter_h = snd_hdac_chip_readl(azx_bus(chip), LLPCU);
+
+               /* Ack: registers read done */
+               snd_hdac_chip_writel(azx_bus(chip), GTSCC, GTSCC_TSCCD_SHIFT);
+
+               tsc_counter = (tsc_counter_h << TSCCU_CCU_SHIFT) |
+                                               tsc_counter_l;
+
+               ll_counter = (ll_counter_h << LLPC_CCU_SHIFT) | ll_counter_l;
+               wallclk_cycles = wallclk_ctr & WALFCC_CIF_MASK;
+
+               /*
+                * An error occurs near frame "rollover". The clocks in
+                * frame value indicates whether this error may have
+                * occurred. Here we use the value of 10 i.e.,
+                * HDA_MAX_CYCLE_OFFSET
+                */
+               if (wallclk_cycles < HDA_MAX_CYCLE_VALUE - HDA_MAX_CYCLE_OFFSET
+                                       && wallclk_cycles > HDA_MAX_CYCLE_OFFSET)
+                       break;
+
+               /*
+                * Sleep before we read again, else we may again get
+                * value near to MAX_CYCLE. Try to sleep for different
+                * amount of time so we dont hit the same number again
+                */
+               udelay(retry_count++);
+
+       } while (retry_count != HDA_MAX_CYCLE_READ_RETRY);
+
+       if (retry_count == HDA_MAX_CYCLE_READ_RETRY) {
+               dev_err_ratelimited(chip->card->dev,
+                       "Error in WALFCC cycle count\n");
+               return -EIO;
+       }
+
+       *device = ns_to_ktime(azx_scale64(ll_counter,
+                               NSEC_PER_SEC, runtime->rate));
+       *device = ktime_add_ns(*device, (wallclk_cycles * NSEC_PER_SEC) /
+                              ((HDA_MAX_CYCLE_VALUE + 1) * runtime->rate));
+
+       system->cycles = tsc_counter;
+       system->cs_id = CSID_X86_ART;
+
+       return 0;
+}
+
+#else
+static int azx_get_sync_time(ktime_t *device,
+               struct system_counterval_t *system, void *ctx)
+{
+       return -ENXIO;
+}
+#endif
+
+static int azx_get_crosststamp(struct snd_pcm_substream *substream,
+                             struct system_device_crosststamp *xtstamp)
+{
+       return get_device_system_crosststamp(azx_get_sync_time,
+                                       substream, NULL, xtstamp);
+}
+
+static inline bool is_link_time_supported(struct snd_pcm_runtime *runtime,
+                               struct snd_pcm_audio_tstamp_config *ts)
+{
+       if (runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME)
+               if (ts->type_requested == SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED)
+                       return true;
+
+       return false;
+}
+
+static int azx_get_time_info(struct snd_pcm_substream *substream,
+                       struct timespec64 *system_ts, struct timespec64 *audio_ts,
+                       struct snd_pcm_audio_tstamp_config *audio_tstamp_config,
+                       struct snd_pcm_audio_tstamp_report *audio_tstamp_report)
+{
+       struct azx_dev *azx_dev = get_azx_dev(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct system_device_crosststamp xtstamp;
+       int ret;
+       u64 nsec;
+
+       if ((substream->runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_ATIME) &&
+               (audio_tstamp_config->type_requested == SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK)) {
+
+               snd_pcm_gettime(substream->runtime, system_ts);
+
+               nsec = timecounter_read(&azx_dev->core.tc);
+               if (audio_tstamp_config->report_delay)
+                       nsec = azx_adjust_codec_delay(substream, nsec);
+
+               *audio_ts = ns_to_timespec64(nsec);
+
+               audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK;
+               audio_tstamp_report->accuracy_report = 1; /* rest of structure is valid */
+               audio_tstamp_report->accuracy = 42; /* 24 MHz WallClock == 42ns resolution */
+
+       } else if (is_link_time_supported(runtime, audio_tstamp_config)) {
+
+               ret = azx_get_crosststamp(substream, &xtstamp);
+               if (ret)
+                       return ret;
+
+               switch (runtime->tstamp_type) {
+               case SNDRV_PCM_TSTAMP_TYPE_MONOTONIC:
+                       return -EINVAL;
+
+               case SNDRV_PCM_TSTAMP_TYPE_MONOTONIC_RAW:
+                       *system_ts = ktime_to_timespec64(xtstamp.sys_monoraw);
+                       break;
+
+               default:
+                       *system_ts = ktime_to_timespec64(xtstamp.sys_realtime);
+                       break;
+
+               }
+
+               *audio_ts = ktime_to_timespec64(xtstamp.device);
+
+               audio_tstamp_report->actual_type =
+                       SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED;
+               audio_tstamp_report->accuracy_report = 1;
+               /* 24 MHz WallClock == 42ns resolution */
+               audio_tstamp_report->accuracy = 42;
+
+       } else {
+               audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT;
+       }
+
+       return 0;
+}
+
+static const struct snd_pcm_hardware azx_pcm_hw = {
+       .info =                 (SNDRV_PCM_INFO_MMAP |
+                                SNDRV_PCM_INFO_INTERLEAVED |
+                                SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                SNDRV_PCM_INFO_MMAP_VALID |
+                                /* No full-resume yet implemented */
+                                /* SNDRV_PCM_INFO_RESUME |*/
+                                SNDRV_PCM_INFO_PAUSE |
+                                SNDRV_PCM_INFO_SYNC_START |
+                                SNDRV_PCM_INFO_HAS_WALL_CLOCK | /* legacy */
+                                SNDRV_PCM_INFO_HAS_LINK_ATIME |
+                                SNDRV_PCM_INFO_NO_PERIOD_WAKEUP),
+       .formats =              SNDRV_PCM_FMTBIT_S16_LE,
+       .rates =                SNDRV_PCM_RATE_48000,
+       .rate_min =             48000,
+       .rate_max =             48000,
+       .channels_min =         2,
+       .channels_max =         2,
+       .buffer_bytes_max =     AZX_MAX_BUF_SIZE,
+       .period_bytes_min =     128,
+       .period_bytes_max =     AZX_MAX_BUF_SIZE / 2,
+       .periods_min =          2,
+       .periods_max =          AZX_MAX_FRAG,
+       .fifo_size =            0,
+};
+
+static int azx_pcm_open(struct snd_pcm_substream *substream)
+{
+       struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+       struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
+       struct azx *chip = apcm->chip;
+       struct azx_dev *azx_dev;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int err;
+       int buff_step;
+
+       snd_hda_codec_pcm_get(apcm->info);
+       mutex_lock(&chip->open_mutex);
+       azx_dev = azx_assign_device(chip, substream);
+       trace_azx_pcm_open(chip, azx_dev);
+       if (azx_dev == NULL) {
+               err = -EBUSY;
+               goto unlock;
+       }
+       runtime->private_data = azx_dev;
+
+       runtime->hw = azx_pcm_hw;
+       if (chip->gts_present)
+               runtime->hw.info |= SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME;
+       runtime->hw.channels_min = hinfo->channels_min;
+       runtime->hw.channels_max = hinfo->channels_max;
+       runtime->hw.formats = hinfo->formats;
+       runtime->hw.rates = hinfo->rates;
+       snd_pcm_limit_hw_rates(runtime);
+       snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+
+       /* avoid wrap-around with wall-clock */
+       snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_TIME,
+                                    20,
+                                    178000000);
+
+       if (chip->align_buffer_size)
+               /* constrain buffer sizes to be multiple of 128
+                  bytes. This is more efficient in terms of memory
+                  access but isn't required by the HDA spec and
+                  prevents users from specifying exact period/buffer
+                  sizes. For example for 44.1kHz, a period size set
+                  to 20ms will be rounded to 19.59ms. */
+               buff_step = 128;
+       else
+               /* Don't enforce steps on buffer sizes, still need to
+                  be multiple of 4 bytes (HDA spec). Tested on Intel
+                  HDA controllers, may not work on all devices where
+                  option needs to be disabled */
+               buff_step = 4;
+
+       snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+                                  buff_step);
+       snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+                                  buff_step);
+       snd_hda_power_up(apcm->codec);
+       if (hinfo->ops.open)
+               err = hinfo->ops.open(hinfo, apcm->codec, substream);
+       else
+               err = -ENODEV;
+       if (err < 0) {
+               azx_release_device(azx_dev);
+               goto powerdown;
+       }
+       snd_pcm_limit_hw_rates(runtime);
+       /* sanity check */
+       if (snd_BUG_ON(!runtime->hw.channels_min) ||
+           snd_BUG_ON(!runtime->hw.channels_max) ||
+           snd_BUG_ON(!runtime->hw.formats) ||
+           snd_BUG_ON(!runtime->hw.rates)) {
+               azx_release_device(azx_dev);
+               if (hinfo->ops.close)
+                       hinfo->ops.close(hinfo, apcm->codec, substream);
+               err = -EINVAL;
+               goto powerdown;
+       }
+
+       /* disable LINK_ATIME timestamps for capture streams
+          until we figure out how to handle digital inputs */
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+               runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_WALL_CLOCK; /* legacy */
+               runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_LINK_ATIME;
+       }
+
+       snd_pcm_set_sync(substream);
+       mutex_unlock(&chip->open_mutex);
+       return 0;
+
+ powerdown:
+       snd_hda_power_down(apcm->codec);
+ unlock:
+       mutex_unlock(&chip->open_mutex);
+       snd_hda_codec_pcm_put(apcm->info);
+       return err;
+}
+
+static const struct snd_pcm_ops azx_pcm_ops = {
+       .open = azx_pcm_open,
+       .close = azx_pcm_close,
+       .hw_params = azx_pcm_hw_params,
+       .hw_free = azx_pcm_hw_free,
+       .prepare = azx_pcm_prepare,
+       .trigger = azx_pcm_trigger,
+       .pointer = azx_pcm_pointer,
+       .get_time_info =  azx_get_time_info,
+};
+
+static void azx_pcm_free(struct snd_pcm *pcm)
+{
+       struct azx_pcm *apcm = pcm->private_data;
+       if (apcm) {
+               list_del(&apcm->list);
+               apcm->info->pcm = NULL;
+               kfree(apcm);
+       }
+}
+
+#define MAX_PREALLOC_SIZE      (32 * 1024 * 1024)
+
+int snd_hda_attach_pcm_stream(struct hda_bus *_bus, struct hda_codec *codec,
+                             struct hda_pcm *cpcm)
+{
+       struct hdac_bus *bus = &_bus->core;
+       struct azx *chip = bus_to_azx(bus);
+       struct snd_pcm *pcm;
+       struct azx_pcm *apcm;
+       int pcm_dev = cpcm->device;
+       unsigned int size;
+       int s, err;
+       int type = SNDRV_DMA_TYPE_DEV_SG;
+
+       list_for_each_entry(apcm, &chip->pcm_list, list) {
+               if (apcm->pcm->device == pcm_dev) {
+                       dev_err(chip->card->dev, "PCM %d already exists\n",
+                               pcm_dev);
+                       return -EBUSY;
+               }
+       }
+       err = snd_pcm_new(chip->card, cpcm->name, pcm_dev,
+                         cpcm->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams,
+                         cpcm->stream[SNDRV_PCM_STREAM_CAPTURE].substreams,
+                         &pcm);
+       if (err < 0)
+               return err;
+       strscpy(pcm->name, cpcm->name, sizeof(pcm->name));
+       apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
+       if (apcm == NULL) {
+               snd_device_free(chip->card, pcm);
+               return -ENOMEM;
+       }
+       apcm->chip = chip;
+       apcm->pcm = pcm;
+       apcm->codec = codec;
+       apcm->info = cpcm;
+       pcm->private_data = apcm;
+       pcm->private_free = azx_pcm_free;
+       if (cpcm->pcm_type == HDA_PCM_TYPE_MODEM)
+               pcm->dev_class = SNDRV_PCM_CLASS_MODEM;
+       list_add_tail(&apcm->list, &chip->pcm_list);
+       cpcm->pcm = pcm;
+       for (s = 0; s < 2; s++) {
+               if (cpcm->stream[s].substreams)
+                       snd_pcm_set_ops(pcm, s, &azx_pcm_ops);
+       }
+       /* buffer pre-allocation */
+       size = CONFIG_SND_HDA_PREALLOC_SIZE * 1024;
+       if (size > MAX_PREALLOC_SIZE)
+               size = MAX_PREALLOC_SIZE;
+       if (chip->uc_buffer)
+               type = SNDRV_DMA_TYPE_DEV_WC_SG;
+       snd_pcm_set_managed_buffer_all(pcm, type, chip->card->dev,
+                                      size, MAX_PREALLOC_SIZE);
+       return 0;
+}
+
+static unsigned int azx_command_addr(u32 cmd)
+{
+       unsigned int addr = cmd >> 28;
+
+       if (addr >= AZX_MAX_CODECS) {
+               snd_BUG();
+               addr = 0;
+       }
+
+       return addr;
+}
+
+/* receive a response */
+static int azx_rirb_get_response(struct hdac_bus *bus, unsigned int addr,
+                                unsigned int *res)
+{
+       struct azx *chip = bus_to_azx(bus);
+       struct hda_bus *hbus = &chip->bus;
+       int err;
+
+ again:
+       err = snd_hdac_bus_get_response(bus, addr, res);
+       if (!err)
+               return 0;
+
+       if (hbus->no_response_fallback)
+               return -EIO;
+
+       if (!bus->polling_mode) {
+               dev_warn(chip->card->dev,
+                        "azx_get_response timeout, switching to polling mode: last cmd=0x%08x\n",
+                        bus->last_cmd[addr]);
+               bus->polling_mode = 1;
+               goto again;
+       }
+
+       if (chip->msi) {
+               dev_warn(chip->card->dev,
+                        "No response from codec, disabling MSI: last cmd=0x%08x\n",
+                        bus->last_cmd[addr]);
+               if (chip->ops->disable_msi_reset_irq &&
+                   chip->ops->disable_msi_reset_irq(chip) < 0)
+                       return -EIO;
+               goto again;
+       }
+
+       if (chip->probing) {
+               /* If this critical timeout happens during the codec probing
+                * phase, this is likely an access to a non-existing codec
+                * slot.  Better to return an error and reset the system.
+                */
+               return -EIO;
+       }
+
+       /* no fallback mechanism? */
+       if (!chip->fallback_to_single_cmd)
+               return -EIO;
+
+       /* a fatal communication error; need either to reset or to fallback
+        * to the single_cmd mode
+        */
+       if (hbus->allow_bus_reset && !hbus->response_reset && !hbus->in_reset) {
+               hbus->response_reset = 1;
+               dev_err(chip->card->dev,
+                       "No response from codec, resetting bus: last cmd=0x%08x\n",
+                       bus->last_cmd[addr]);
+               return -EAGAIN; /* give a chance to retry */
+       }
+
+       dev_err(chip->card->dev,
+               "azx_get_response timeout, switching to single_cmd mode: last cmd=0x%08x\n",
+               bus->last_cmd[addr]);
+       chip->single_cmd = 1;
+       hbus->response_reset = 0;
+       snd_hdac_bus_stop_cmd_io(bus);
+       return -EIO;
+}
+
+/*
+ * Use the single immediate command instead of CORB/RIRB for simplicity
+ *
+ * Note: according to Intel, this is not preferred use.  The command was
+ *       intended for the BIOS only, and may get confused with unsolicited
+ *       responses.  So, we shouldn't use it for normal operation from the
+ *       driver.
+ *       I left the codes, however, for debugging/testing purposes.
+ */
+
+/* receive a response */
+static int azx_single_wait_for_response(struct azx *chip, unsigned int addr)
+{
+       int timeout = 50;
+
+       while (timeout--) {
+               /* check IRV busy bit */
+               if (azx_readw(chip, IRS) & AZX_IRS_VALID) {
+                       /* reuse rirb.res as the response return value */
+                       azx_bus(chip)->rirb.res[addr] = azx_readl(chip, IR);
+                       return 0;
+               }
+               udelay(1);
+       }
+       if (printk_ratelimit())
+               dev_dbg(chip->card->dev, "get_response timeout: IRS=0x%x\n",
+                       azx_readw(chip, IRS));
+       azx_bus(chip)->rirb.res[addr] = -1;
+       return -EIO;
+}
+
+/* send a command */
+static int azx_single_send_cmd(struct hdac_bus *bus, u32 val)
+{
+       struct azx *chip = bus_to_azx(bus);
+       unsigned int addr = azx_command_addr(val);
+       int timeout = 50;
+
+       bus->last_cmd[azx_command_addr(val)] = val;
+       while (timeout--) {
+               /* check ICB busy bit */
+               if (!((azx_readw(chip, IRS) & AZX_IRS_BUSY))) {
+                       /* Clear IRV valid bit */
+                       azx_writew(chip, IRS, azx_readw(chip, IRS) |
+                                  AZX_IRS_VALID);
+                       azx_writel(chip, IC, val);
+                       azx_writew(chip, IRS, azx_readw(chip, IRS) |
+                                  AZX_IRS_BUSY);
+                       return azx_single_wait_for_response(chip, addr);
+               }
+               udelay(1);
+       }
+       if (printk_ratelimit())
+               dev_dbg(chip->card->dev,
+                       "send_cmd timeout: IRS=0x%x, val=0x%x\n",
+                       azx_readw(chip, IRS), val);
+       return -EIO;
+}
+
+/* receive a response */
+static int azx_single_get_response(struct hdac_bus *bus, unsigned int addr,
+                                  unsigned int *res)
+{
+       if (res)
+               *res = bus->rirb.res[addr];
+       return 0;
+}
+
+/*
+ * The below are the main callbacks from hda_codec.
+ *
+ * They are just the skeleton to call sub-callbacks according to the
+ * current setting of chip->single_cmd.
+ */
+
+/* send a command */
+static int azx_send_cmd(struct hdac_bus *bus, unsigned int val)
+{
+       struct azx *chip = bus_to_azx(bus);
+
+       if (chip->disabled)
+               return 0;
+       if (chip->single_cmd || bus->use_pio_for_commands)
+               return azx_single_send_cmd(bus, val);
+       else
+               return snd_hdac_bus_send_cmd(bus, val);
+}
+
+/* get a response */
+static int azx_get_response(struct hdac_bus *bus, unsigned int addr,
+                           unsigned int *res)
+{
+       struct azx *chip = bus_to_azx(bus);
+
+       if (chip->disabled)
+               return 0;
+       if (chip->single_cmd || bus->use_pio_for_commands)
+               return azx_single_get_response(bus, addr, res);
+       else
+               return azx_rirb_get_response(bus, addr, res);
+}
+
+static const struct hdac_bus_ops bus_core_ops = {
+       .command = azx_send_cmd,
+       .get_response = azx_get_response,
+};
+
+#ifdef CONFIG_SND_HDA_DSP_LOADER
+/*
+ * DSP loading code (e.g. for CA0132)
+ */
+
+/* use the first stream for loading DSP */
+static struct azx_dev *
+azx_get_dsp_loader_dev(struct azx *chip)
+{
+       struct hdac_bus *bus = azx_bus(chip);
+       struct hdac_stream *s;
+
+       list_for_each_entry(s, &bus->stream_list, list)
+               if (s->index == chip->playback_index_offset)
+                       return stream_to_azx_dev(s);
+
+       return NULL;
+}
+
+int snd_hda_codec_load_dsp_prepare(struct hda_codec *codec, unsigned int format,
+                                  unsigned int byte_size,
+                                  struct snd_dma_buffer *bufp)
+{
+       struct hdac_bus *bus = &codec->bus->core;
+       struct azx *chip = bus_to_azx(bus);
+       struct azx_dev *azx_dev;
+       struct hdac_stream *hstr;
+       bool saved = false;
+       int err;
+
+       azx_dev = azx_get_dsp_loader_dev(chip);
+       hstr = azx_stream(azx_dev);
+       spin_lock_irq(&bus->reg_lock);
+       if (hstr->opened) {
+               chip->saved_azx_dev = *azx_dev;
+               saved = true;
+       }
+       spin_unlock_irq(&bus->reg_lock);
+
+       err = snd_hdac_dsp_prepare(hstr, format, byte_size, bufp);
+       if (err < 0) {
+               spin_lock_irq(&bus->reg_lock);
+               if (saved)
+                       *azx_dev = chip->saved_azx_dev;
+               spin_unlock_irq(&bus->reg_lock);
+               return err;
+       }
+
+       hstr->prepared = 0;
+       return err;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_load_dsp_prepare);
+
+void snd_hda_codec_load_dsp_trigger(struct hda_codec *codec, bool start)
+{
+       struct hdac_bus *bus = &codec->bus->core;
+       struct azx *chip = bus_to_azx(bus);
+       struct azx_dev *azx_dev = azx_get_dsp_loader_dev(chip);
+
+       snd_hdac_dsp_trigger(azx_stream(azx_dev), start);
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_load_dsp_trigger);
+
+void snd_hda_codec_load_dsp_cleanup(struct hda_codec *codec,
+                                   struct snd_dma_buffer *dmab)
+{
+       struct hdac_bus *bus = &codec->bus->core;
+       struct azx *chip = bus_to_azx(bus);
+       struct azx_dev *azx_dev = azx_get_dsp_loader_dev(chip);
+       struct hdac_stream *hstr = azx_stream(azx_dev);
+
+       if (!dmab->area || !hstr->locked)
+               return;
+
+       snd_hdac_dsp_cleanup(hstr, dmab);
+       spin_lock_irq(&bus->reg_lock);
+       if (hstr->opened)
+               *azx_dev = chip->saved_azx_dev;
+       hstr->locked = false;
+       spin_unlock_irq(&bus->reg_lock);
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_load_dsp_cleanup);
+#endif /* CONFIG_SND_HDA_DSP_LOADER */
+
+/*
+ * reset and start the controller registers
+ */
+void azx_init_chip(struct azx *chip, bool full_reset)
+{
+       if (snd_hdac_bus_init_chip(azx_bus(chip), full_reset)) {
+               /* correct RINTCNT for CXT */
+               if (chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND)
+                       azx_writew(chip, RINTCNT, 0xc0);
+       }
+}
+EXPORT_SYMBOL_GPL(azx_init_chip);
+
+void azx_stop_all_streams(struct azx *chip)
+{
+       struct hdac_bus *bus = azx_bus(chip);
+
+       snd_hdac_stop_streams(bus);
+}
+EXPORT_SYMBOL_GPL(azx_stop_all_streams);
+
+void azx_stop_chip(struct azx *chip)
+{
+       snd_hdac_bus_stop_chip(azx_bus(chip));
+}
+EXPORT_SYMBOL_GPL(azx_stop_chip);
+
+/*
+ * interrupt handler
+ */
+static void stream_update(struct hdac_bus *bus, struct hdac_stream *s)
+{
+       struct azx *chip = bus_to_azx(bus);
+       struct azx_dev *azx_dev = stream_to_azx_dev(s);
+
+       /* check whether this IRQ is really acceptable */
+       if (!chip->ops->position_check ||
+           chip->ops->position_check(chip, azx_dev)) {
+               spin_unlock(&bus->reg_lock);
+               snd_pcm_period_elapsed(azx_stream(azx_dev)->substream);
+               spin_lock(&bus->reg_lock);
+       }
+}
+
+irqreturn_t azx_interrupt(int irq, void *dev_id)
+{
+       struct azx *chip = dev_id;
+       struct hdac_bus *bus = azx_bus(chip);
+       u32 status;
+       bool active, handled = false;
+       int repeat = 0; /* count for avoiding endless loop */
+
+       if (azx_has_pm_runtime(chip))
+               if (!pm_runtime_active(chip->card->dev))
+                       return IRQ_NONE;
+
+       spin_lock(&bus->reg_lock);
+
+       if (chip->disabled)
+               goto unlock;
+
+       do {
+               status = azx_readl(chip, INTSTS);
+               if (status == 0 || status == 0xffffffff)
+                       break;
+
+               handled = true;
+               active = false;
+               if (snd_hdac_bus_handle_stream_irq(bus, status, stream_update))
+                       active = true;
+
+               status = azx_readb(chip, RIRBSTS);
+               if (status & RIRB_INT_MASK) {
+                       /*
+                        * Clearing the interrupt status here ensures that no
+                        * interrupt gets masked after the RIRB wp is read in
+                        * snd_hdac_bus_update_rirb. This avoids a possible
+                        * race condition where codec response in RIRB may
+                        * remain unserviced by IRQ, eventually falling back
+                        * to polling mode in azx_rirb_get_response.
+                        */
+                       azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
+                       active = true;
+                       if (status & RIRB_INT_RESPONSE) {
+                               if (chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND)
+                                       udelay(80);
+                               snd_hdac_bus_update_rirb(bus);
+                       }
+               }
+       } while (active && ++repeat < 10);
+
+ unlock:
+       spin_unlock(&bus->reg_lock);
+
+       return IRQ_RETVAL(handled);
+}
+EXPORT_SYMBOL_GPL(azx_interrupt);
+
+/*
+ * Codec initerface
+ */
+
+/*
+ * Probe the given codec address
+ */
+static int probe_codec(struct azx *chip, int addr)
+{
+       unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) |
+               (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID;
+       struct hdac_bus *bus = azx_bus(chip);
+       int err;
+       unsigned int res = -1;
+
+       mutex_lock(&bus->cmd_mutex);
+       chip->probing = 1;
+       azx_send_cmd(bus, cmd);
+       err = azx_get_response(bus, addr, &res);
+       chip->probing = 0;
+       mutex_unlock(&bus->cmd_mutex);
+       if (err < 0 || res == -1)
+               return -EIO;
+       dev_dbg(chip->card->dev, "codec #%d probed OK\n", addr);
+       return 0;
+}
+
+void snd_hda_bus_reset(struct hda_bus *bus)
+{
+       struct azx *chip = bus_to_azx(&bus->core);
+
+       bus->in_reset = 1;
+       azx_stop_chip(chip);
+       azx_init_chip(chip, true);
+       if (bus->core.chip_init)
+               snd_hda_bus_reset_codecs(bus);
+       bus->in_reset = 0;
+}
+
+/* HD-audio bus initialization */
+int azx_bus_init(struct azx *chip, const char *model)
+{
+       struct hda_bus *bus = &chip->bus;
+       int err;
+
+       err = snd_hdac_bus_init(&bus->core, chip->card->dev, &bus_core_ops);
+       if (err < 0)
+               return err;
+
+       bus->card = chip->card;
+       mutex_init(&bus->prepare_mutex);
+       bus->pci = chip->pci;
+       bus->modelname = model;
+       bus->mixer_assigned = -1;
+       bus->core.snoop = azx_snoop(chip);
+       if (chip->get_position[0] != azx_get_pos_lpib ||
+           chip->get_position[1] != azx_get_pos_lpib)
+               bus->core.use_posbuf = true;
+       bus->core.bdl_pos_adj = chip->bdl_pos_adj;
+       if (chip->driver_caps & AZX_DCAPS_CORBRP_SELF_CLEAR)
+               bus->core.corbrp_self_clear = true;
+
+       if (chip->driver_caps & AZX_DCAPS_4K_BDLE_BOUNDARY)
+               bus->core.align_bdle_4k = true;
+
+       if (chip->driver_caps & AZX_DCAPS_PIO_COMMANDS)
+               bus->core.use_pio_for_commands = true;
+
+       /* enable sync_write flag for stable communication as default */
+       bus->core.sync_write = 1;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(azx_bus_init);
+
+/* Probe codecs */
+int azx_probe_codecs(struct azx *chip, unsigned int max_slots)
+{
+       struct hdac_bus *bus = azx_bus(chip);
+       int c, codecs, err;
+
+       codecs = 0;
+       if (!max_slots)
+               max_slots = AZX_DEFAULT_CODECS;
+
+       /* First try to probe all given codec slots */
+       for (c = 0; c < max_slots; c++) {
+               if ((bus->codec_mask & (1 << c)) & chip->codec_probe_mask) {
+                       if (probe_codec(chip, c) < 0) {
+                               /* Some BIOSen give you wrong codec addresses
+                                * that don't exist
+                                */
+                               dev_warn(chip->card->dev,
+                                        "Codec #%d probe error; disabling it...\n", c);
+                               bus->codec_mask &= ~(1 << c);
+                               /* no codecs */
+                               if (bus->codec_mask == 0)
+                                       break;
+                               /* More badly, accessing to a non-existing
+                                * codec often screws up the controller chip,
+                                * and disturbs the further communications.
+                                * Thus if an error occurs during probing,
+                                * better to reset the controller chip to
+                                * get back to the sanity state.
+                                */
+                               azx_stop_chip(chip);
+                               azx_init_chip(chip, true);
+                       }
+               }
+       }
+
+       /* Then create codec instances */
+       for (c = 0; c < max_slots; c++) {
+               if ((bus->codec_mask & (1 << c)) & chip->codec_probe_mask) {
+                       struct hda_codec *codec;
+                       err = snd_hda_codec_new(&chip->bus, chip->card, c, &codec);
+                       if (err < 0)
+                               continue;
+                       codec->jackpoll_interval = chip->jackpoll_interval;
+                       codec->beep_mode = chip->beep_mode;
+                       codec->ctl_dev_id = chip->ctl_dev_id;
+                       codecs++;
+               }
+       }
+       if (!codecs) {
+               dev_err(chip->card->dev, "no codecs initialized\n");
+               return -ENXIO;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(azx_probe_codecs);
+
+/* configure each codec instance */
+int azx_codec_configure(struct azx *chip)
+{
+       struct hda_codec *codec, *next;
+       int success = 0;
+
+       list_for_each_codec(codec, &chip->bus) {
+               if (!snd_hda_codec_configure(codec))
+                       success++;
+       }
+
+       if (success) {
+               /* unregister failed codecs if any codec has been probed */
+               list_for_each_codec_safe(codec, next, &chip->bus) {
+                       if (!codec->configured) {
+                               codec_err(codec, "Unable to configure, disabling\n");
+                               snd_hdac_device_unregister(&codec->core);
+                       }
+               }
+       }
+
+       return success ? 0 : -ENODEV;
+}
+EXPORT_SYMBOL_GPL(azx_codec_configure);
+
+static int stream_direction(struct azx *chip, unsigned char index)
+{
+       if (index >= chip->capture_index_offset &&
+           index < chip->capture_index_offset + chip->capture_streams)
+               return SNDRV_PCM_STREAM_CAPTURE;
+       return SNDRV_PCM_STREAM_PLAYBACK;
+}
+
+/* initialize SD streams */
+int azx_init_streams(struct azx *chip)
+{
+       int i;
+       int stream_tags[2] = { 0, 0 };
+
+       /* initialize each stream (aka device)
+        * assign the starting bdl address to each stream (device)
+        * and initialize
+        */
+       for (i = 0; i < chip->num_streams; i++) {
+               struct azx_dev *azx_dev = kzalloc(sizeof(*azx_dev), GFP_KERNEL);
+               int dir, tag;
+
+               if (!azx_dev)
+                       return -ENOMEM;
+
+               dir = stream_direction(chip, i);
+               /* stream tag must be unique throughout
+                * the stream direction group,
+                * valid values 1...15
+                * use separate stream tag if the flag
+                * AZX_DCAPS_SEPARATE_STREAM_TAG is used
+                */
+               if (chip->driver_caps & AZX_DCAPS_SEPARATE_STREAM_TAG)
+                       tag = ++stream_tags[dir];
+               else
+                       tag = i + 1;
+               snd_hdac_stream_init(azx_bus(chip), azx_stream(azx_dev),
+                                    i, dir, tag);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(azx_init_streams);
+
+void azx_free_streams(struct azx *chip)
+{
+       struct hdac_bus *bus = azx_bus(chip);
+       struct hdac_stream *s;
+
+       while (!list_empty(&bus->stream_list)) {
+               s = list_first_entry(&bus->stream_list, struct hdac_stream, list);
+               list_del(&s->list);
+               kfree(stream_to_azx_dev(s));
+       }
+}
+EXPORT_SYMBOL_GPL(azx_free_streams);
diff --git a/sound/hda/common/controller_trace.h b/sound/hda/common/controller_trace.h
new file mode 100644 (file)
index 0000000..7f5841f
--- /dev/null
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM hda_controller
+#define TRACE_INCLUDE_FILE controller_trace
+
+#if !defined(_TRACE_HDA_CONTROLLER_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_HDA_CONTROLLER_H
+
+#include <linux/tracepoint.h>
+
+struct azx;
+struct azx_dev;
+
+TRACE_EVENT(azx_pcm_trigger,
+
+       TP_PROTO(struct azx *chip, struct azx_dev *dev, int cmd),
+
+       TP_ARGS(chip, dev, cmd),
+
+       TP_STRUCT__entry(
+               __field( int, card )
+               __field( int, idx )
+               __field( int, cmd )
+       ),
+
+       TP_fast_assign(
+               __entry->card = (chip)->card->number;
+               __entry->idx = (dev)->core.index;
+               __entry->cmd = cmd;
+       ),
+
+       TP_printk("[%d:%d] cmd=%d", __entry->card, __entry->idx, __entry->cmd)
+);
+
+TRACE_EVENT(azx_get_position,
+
+    TP_PROTO(struct azx *chip, struct azx_dev *dev, unsigned int pos, unsigned int delay),
+
+           TP_ARGS(chip, dev, pos, delay),
+
+       TP_STRUCT__entry(
+               __field( int, card )
+               __field( int, idx )
+               __field( unsigned int, pos )
+               __field( unsigned int, delay )
+       ),
+
+       TP_fast_assign(
+               __entry->card = (chip)->card->number;
+               __entry->idx = (dev)->core.index;
+               __entry->pos = pos;
+               __entry->delay = delay;
+       ),
+
+       TP_printk("[%d:%d] pos=%u, delay=%u", __entry->card, __entry->idx, __entry->pos, __entry->delay)
+);
+
+DECLARE_EVENT_CLASS(azx_pcm,
+       TP_PROTO(struct azx *chip, struct azx_dev *azx_dev),
+
+       TP_ARGS(chip, azx_dev),
+
+       TP_STRUCT__entry(
+               __field( unsigned char, stream_tag )
+       ),
+
+       TP_fast_assign(
+               __entry->stream_tag = (azx_dev)->core.stream_tag;
+       ),
+
+       TP_printk("stream_tag: %d", __entry->stream_tag)
+);
+
+DEFINE_EVENT(azx_pcm, azx_pcm_open,
+       TP_PROTO(struct azx *chip, struct azx_dev *azx_dev),
+       TP_ARGS(chip, azx_dev)
+);
+
+DEFINE_EVENT(azx_pcm, azx_pcm_close,
+       TP_PROTO(struct azx *chip, struct azx_dev *azx_dev),
+       TP_ARGS(chip, azx_dev)
+);
+
+DEFINE_EVENT(azx_pcm, azx_pcm_hw_params,
+       TP_PROTO(struct azx *chip, struct azx_dev *azx_dev),
+       TP_ARGS(chip, azx_dev)
+);
+
+DEFINE_EVENT(azx_pcm, azx_pcm_prepare,
+       TP_PROTO(struct azx *chip, struct azx_dev *azx_dev),
+       TP_ARGS(chip, azx_dev)
+);
+
+#endif /* _TRACE_HDA_CONTROLLER_H */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#include <trace/define_trace.h>
diff --git a/sound/hda/common/hda_auto_parser.h b/sound/hda/common/hda_auto_parser.h
new file mode 100644 (file)
index 0000000..87af3d8
--- /dev/null
@@ -0,0 +1,118 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * BIOS auto-parser helper functions for HD-audio
+ *
+ * Copyright (c) 2012 Takashi Iwai <tiwai@suse.de>
+ */
+
+#ifndef __SOUND_HDA_AUTO_PARSER_H
+#define __SOUND_HDA_AUTO_PARSER_H
+
+#include "hda_local.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       18
+
+struct auto_pin_cfg_item {
+       hda_nid_t pin;
+       int type;
+       unsigned int is_headset_mic:1;
+       unsigned int is_headphone_mic:1; /* Mic-only in headphone jack */
+       unsigned int has_boost_on_pin:1;
+       int order;
+};
+
+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_REAR,    /* mic/line-in jack in rear */
+       INPUT_PIN_ATTR_FRONT,   /* mic/line-in jack in front */
+       INPUT_PIN_ATTR_LAST = INPUT_PIN_ATTR_FRONT,
+};
+
+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 */
+#define HDA_PINCFG_HEADSET_MIC   (1 << 2) /* Try to find headset mic; mark seq number as 0xc to trigger */
+#define HDA_PINCFG_HEADPHONE_MIC (1 << 3) /* Try to find headphone mic; mark seq number as 0xd to trigger */
+
+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)
+
+static inline int auto_cfg_hp_outs(const struct auto_pin_cfg *cfg)
+{
+       return (cfg->line_out_type == AUTO_PIN_HP_OUT) ?
+              cfg->line_outs : cfg->hp_outs;
+}
+static inline const hda_nid_t *auto_cfg_hp_pins(const struct auto_pin_cfg *cfg)
+{
+       return (cfg->line_out_type == AUTO_PIN_HP_OUT) ?
+              cfg->line_out_pins : cfg->hp_pins;
+}
+static inline int auto_cfg_speaker_outs(const struct auto_pin_cfg *cfg)
+{
+       return (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) ?
+              cfg->line_outs : cfg->speaker_outs;
+}
+static inline const hda_nid_t *auto_cfg_speaker_pins(const struct auto_pin_cfg *cfg)
+{
+       return (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) ?
+              cfg->line_out_pins : cfg->speaker_pins;
+}
+
+#endif /* __SOUND_HDA_AUTO_PARSER_H */
diff --git a/sound/hda/common/hda_beep.h b/sound/hda/common/hda_beep.h
new file mode 100644 (file)
index 0000000..923ea86
--- /dev/null
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Digital Beep Input Interface for HD-audio codec
+ *
+ * Author: Matt Ranostay <matt.ranostay@konsulko.com>
+ * Copyright (c) 2008 Embedded Alley Solutions Inc
+ */
+
+#ifndef __SOUND_HDA_BEEP_H
+#define __SOUND_HDA_BEEP_H
+
+#include <sound/hda_codec.h>
+
+#define HDA_BEEP_MODE_OFF      0
+#define HDA_BEEP_MODE_ON       1
+
+/* beep information */
+struct hda_beep {
+       struct input_dev *dev;
+       struct hda_codec *codec;
+       char phys[32];
+       int tone;
+       hda_nid_t nid;
+       unsigned int registered:1;
+       unsigned int enabled:1;
+       unsigned int linear_tone:1;     /* linear tone for IDT/STAC codec */
+       unsigned int playing:1;
+       unsigned int keep_power_at_enable:1;    /* set by driver */
+       struct work_struct beep_work; /* scheduled task for beep event */
+       void (*power_hook)(struct hda_beep *beep, bool on);
+};
+
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+int snd_hda_enable_beep_device(struct hda_codec *codec, int enable);
+int snd_hda_attach_beep_device(struct hda_codec *codec, int nid);
+void snd_hda_detach_beep_device(struct hda_codec *codec);
+#else
+static inline int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
+{
+       return 0;
+}
+static inline void snd_hda_detach_beep_device(struct hda_codec *codec)
+{
+}
+#endif
+#endif
diff --git a/sound/hda/common/hda_controller.h b/sound/hda/common/hda_controller.h
new file mode 100644 (file)
index 0000000..c2d0109
--- /dev/null
@@ -0,0 +1,215 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ *  Common functionality for the alsa driver code base for HD Audio.
+ */
+
+#ifndef __SOUND_HDA_CONTROLLER_H
+#define __SOUND_HDA_CONTROLLER_H
+
+#include <linux/timecounter.h>
+#include <linux/interrupt.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/hda_codec.h>
+#include <sound/hda_register.h>
+
+#define AZX_MAX_CODECS         HDA_MAX_CODECS
+#define AZX_DEFAULT_CODECS     4
+
+/* driver quirks (capabilities) */
+/* bits 0-7 are used for indicating driver type */
+#define AZX_DCAPS_NO_TCSEL     (1 << 8)        /* No Intel TCSEL bit */
+#define AZX_DCAPS_NO_MSI       (1 << 9)        /* No MSI support */
+#define AZX_DCAPS_SNOOP_MASK   (3 << 10)       /* snoop type mask */
+#define AZX_DCAPS_SNOOP_OFF    (1 << 12)       /* snoop default off */
+#ifdef CONFIG_SND_HDA_I915
+#define AZX_DCAPS_I915_COMPONENT (1 << 13)     /* bind with i915 gfx */
+#else
+#define AZX_DCAPS_I915_COMPONENT 0             /* NOP */
+#endif
+/* 14 unused */
+#define AZX_DCAPS_CTX_WORKAROUND (1 << 15)     /* X-Fi workaround */
+#define AZX_DCAPS_POSFIX_LPIB  (1 << 16)       /* Use LPIB as default */
+#define AZX_DCAPS_AMD_WORKAROUND (1 << 17)     /* AMD-specific workaround */
+#define AZX_DCAPS_NO_64BIT     (1 << 18)       /* No 64bit address */
+/* 19 unused */
+#define AZX_DCAPS_OLD_SSYNC    (1 << 20)       /* Old SSYNC reg for ICH */
+#define AZX_DCAPS_NO_ALIGN_BUFSIZE (1 << 21)   /* no buffer size alignment */
+/* 22 unused */
+#define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23)   /* BDLE in 4k boundary */
+/* 24 unused */
+#define AZX_DCAPS_COUNT_LPIB_DELAY  (1 << 25)  /* Take LPIB as delay */
+#define AZX_DCAPS_PM_RUNTIME   (1 << 26)       /* runtime PM support */
+#define AZX_DCAPS_RETRY_PROBE  (1 << 27)       /* retry probe if no codec is configured */
+#define AZX_DCAPS_CORBRP_SELF_CLEAR (1 << 28)  /* CORBRP clears itself after reset */
+#define AZX_DCAPS_NO_MSI64      (1 << 29)      /* Stick to 32-bit MSIs */
+#define AZX_DCAPS_SEPARATE_STREAM_TAG  (1 << 30) /* capture and playback use separate stream tag */
+#define AZX_DCAPS_PIO_COMMANDS (1 << 31)       /* Use PIO instead of CORB for commands */
+
+enum {
+       AZX_SNOOP_TYPE_NONE,
+       AZX_SNOOP_TYPE_SCH,
+       AZX_SNOOP_TYPE_ATI,
+       AZX_SNOOP_TYPE_NVIDIA,
+};
+
+struct azx_dev {
+       struct hdac_stream core;
+
+       unsigned int irq_pending:1;
+       /*
+        * For VIA:
+        *  A flag to ensure DMA position is 0
+        *  when link position is not greater than FIFO size
+        */
+       unsigned int insufficient:1;
+};
+
+#define azx_stream(dev)                (&(dev)->core)
+#define stream_to_azx_dev(s)   container_of(s, struct azx_dev, core)
+
+struct azx;
+
+/* Functions to read/write to hda registers. */
+struct hda_controller_ops {
+       /* Disable msi if supported, PCI only */
+       int (*disable_msi_reset_irq)(struct azx *);
+       /* Check if current position is acceptable */
+       int (*position_check)(struct azx *chip, struct azx_dev *azx_dev);
+       /* enable/disable the link power */
+       int (*link_power)(struct azx *chip, bool enable);
+};
+
+struct azx_pcm {
+       struct azx *chip;
+       struct snd_pcm *pcm;
+       struct hda_codec *codec;
+       struct hda_pcm *info;
+       struct list_head list;
+};
+
+typedef unsigned int (*azx_get_pos_callback_t)(struct azx *, struct azx_dev *);
+typedef int (*azx_get_delay_callback_t)(struct azx *, struct azx_dev *, unsigned int pos);
+
+struct azx {
+       struct hda_bus bus;
+
+       struct snd_card *card;
+       struct pci_dev *pci;
+       int dev_index;
+
+       /* chip type specific */
+       int driver_type;
+       unsigned int driver_caps;
+       int playback_streams;
+       int playback_index_offset;
+       int capture_streams;
+       int capture_index_offset;
+       int num_streams;
+       int jackpoll_interval; /* jack poll interval in jiffies */
+
+       /* Register interaction. */
+       const struct hda_controller_ops *ops;
+
+       /* position adjustment callbacks */
+       azx_get_pos_callback_t get_position[2];
+       azx_get_delay_callback_t get_delay[2];
+
+       /* locks */
+       struct mutex open_mutex; /* Prevents concurrent open/close operations */
+
+       /* PCM */
+       struct list_head pcm_list; /* azx_pcm list */
+
+       /* HD codec */
+       int  codec_probe_mask; /* copied from probe_mask option */
+       unsigned int beep_mode;
+       bool ctl_dev_id;
+
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+       const struct firmware *fw;
+#endif
+
+       /* flags */
+       int bdl_pos_adj;
+       unsigned int running:1;
+       unsigned int fallback_to_single_cmd:1;
+       unsigned int single_cmd:1;
+       unsigned int msi:1;
+       unsigned int probing:1; /* codec probing phase */
+       unsigned int snoop:1;
+       unsigned int uc_buffer:1; /* non-cached pages for stream buffers */
+       unsigned int align_buffer_size:1;
+       unsigned int disabled:1; /* disabled by vga_switcheroo */
+       unsigned int pm_prepared:1;
+
+       /* GTS present */
+       unsigned int gts_present:1;
+
+#ifdef CONFIG_SND_HDA_DSP_LOADER
+       struct azx_dev saved_azx_dev;
+#endif
+};
+
+#define azx_bus(chip)  (&(chip)->bus.core)
+#define bus_to_azx(_bus)       container_of(_bus, struct azx, bus.core)
+
+static inline bool azx_snoop(struct azx *chip)
+{
+       return !IS_ENABLED(CONFIG_X86) || chip->snoop;
+}
+
+/*
+ * macros for easy use
+ */
+
+#define azx_writel(chip, reg, value) \
+       snd_hdac_chip_writel(azx_bus(chip), reg, value)
+#define azx_readl(chip, reg) \
+       snd_hdac_chip_readl(azx_bus(chip), reg)
+#define azx_writew(chip, reg, value) \
+       snd_hdac_chip_writew(azx_bus(chip), reg, value)
+#define azx_readw(chip, reg) \
+       snd_hdac_chip_readw(azx_bus(chip), reg)
+#define azx_writeb(chip, reg, value) \
+       snd_hdac_chip_writeb(azx_bus(chip), reg, value)
+#define azx_readb(chip, reg) \
+       snd_hdac_chip_readb(azx_bus(chip), reg)
+
+#define azx_has_pm_runtime(chip) \
+       ((chip)->driver_caps & AZX_DCAPS_PM_RUNTIME)
+
+/* PCM setup */
+static inline struct azx_dev *get_azx_dev(struct snd_pcm_substream *substream)
+{
+       return substream->runtime->private_data;
+}
+unsigned int azx_get_position(struct azx *chip, struct azx_dev *azx_dev);
+unsigned int azx_get_pos_lpib(struct azx *chip, struct azx_dev *azx_dev);
+unsigned int azx_get_pos_posbuf(struct azx *chip, struct azx_dev *azx_dev);
+
+/* Stream control. */
+void azx_stop_all_streams(struct azx *chip);
+
+/* Allocation functions. */
+#define azx_alloc_stream_pages(chip) \
+       snd_hdac_bus_alloc_stream_pages(azx_bus(chip))
+#define azx_free_stream_pages(chip) \
+       snd_hdac_bus_free_stream_pages(azx_bus(chip))
+
+/* Low level azx interface */
+void azx_init_chip(struct azx *chip, bool full_reset);
+void azx_stop_chip(struct azx *chip);
+#define azx_enter_link_reset(chip) \
+       snd_hdac_bus_enter_link_reset(azx_bus(chip))
+irqreturn_t azx_interrupt(int irq, void *dev_id);
+
+/* Codec interface */
+int azx_bus_init(struct azx *chip, const char *model);
+int azx_probe_codecs(struct azx *chip, unsigned int max_slots);
+int azx_codec_configure(struct azx *chip);
+int azx_init_streams(struct azx *chip);
+void azx_free_streams(struct azx *chip);
+
+#endif /* __SOUND_HDA_CONTROLLER_H */
diff --git a/sound/hda/common/hda_jack.h b/sound/hda/common/hda_jack.h
new file mode 100644 (file)
index 0000000..ff7d289
--- /dev/null
@@ -0,0 +1,195 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Jack-detection handling for HD-audio
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ */
+
+#ifndef __SOUND_HDA_JACK_H
+#define __SOUND_HDA_JACK_H
+
+#include <linux/err.h>
+#include <sound/jack.h>
+
+struct auto_pin_cfg;
+struct hda_jack_tbl;
+struct hda_jack_callback;
+
+typedef void (*hda_jack_callback_fn) (struct hda_codec *, struct hda_jack_callback *);
+
+struct hda_jack_callback {
+       hda_nid_t nid;
+       int dev_id;
+       hda_jack_callback_fn func;
+       unsigned int private_data;      /* arbitrary data */
+       unsigned int unsol_res;         /* unsolicited event bits */
+       struct hda_jack_tbl *jack;      /* associated jack entry */
+       struct hda_jack_callback *next;
+};
+
+struct hda_jack_tbl {
+       hda_nid_t nid;
+       int dev_id;
+       unsigned char tag;              /* unsol event tag */
+       struct hda_jack_callback *callback;
+       /* jack-detection stuff */
+       unsigned int pin_sense;         /* cached pin-sense value */
+       unsigned int jack_detect:1;     /* capable of jack-detection? */
+       unsigned int jack_dirty:1;      /* needs to update? */
+       unsigned int phantom_jack:1;    /* a fixed, always present port? */
+       unsigned int block_report:1;    /* in a transitional state - do not report to userspace */
+       hda_nid_t gating_jack;          /* valid when gating jack plugged */
+       hda_nid_t gated_jack;           /* gated is dependent on this jack */
+       hda_nid_t key_report_jack;      /* key reports to this jack */
+       int type;
+       int button_state;
+       struct snd_jack *jack;
+};
+
+struct hda_jack_keymap {
+       enum snd_jack_types type;
+       int key;
+};
+
+struct hda_jack_tbl *
+snd_hda_jack_tbl_get_mst(struct hda_codec *codec, hda_nid_t nid, int dev_id);
+
+/**
+ * snd_hda_jack_tbl_get - query the jack-table entry for the given NID
+ * @codec: the HDA codec
+ * @nid: pin NID to refer to
+ */
+static inline struct hda_jack_tbl *
+snd_hda_jack_tbl_get(struct hda_codec *codec, hda_nid_t nid)
+{
+       return snd_hda_jack_tbl_get_mst(codec, nid, 0);
+}
+
+struct hda_jack_tbl *
+snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec,
+                             unsigned char tag, int dev_id);
+
+void snd_hda_jack_tbl_disconnect(struct hda_codec *codec);
+void snd_hda_jack_tbl_clear(struct hda_codec *codec);
+
+void snd_hda_jack_set_dirty_all(struct hda_codec *codec);
+
+int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid,
+                              int dev_id);
+
+struct hda_jack_callback *
+snd_hda_jack_detect_enable_callback_mst(struct hda_codec *codec, hda_nid_t nid,
+                                       int dev_id, hda_jack_callback_fn func);
+
+/**
+ * snd_hda_jack_detect_enable - enable the jack-detection
+ * @codec: the HDA codec
+ * @nid: pin NID to enable
+ * @func: callback function to register
+ *
+ * In the case of error, the return value will be a pointer embedded with
+ * errno.  Check and handle the return value appropriately with standard
+ * macros such as @IS_ERR() and @PTR_ERR().
+ */
+static inline struct hda_jack_callback *
+snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid,
+                                   hda_jack_callback_fn cb)
+{
+       return snd_hda_jack_detect_enable_callback_mst(codec, nid, 0, cb);
+}
+
+int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
+                                hda_nid_t gating_nid);
+
+int snd_hda_jack_bind_keymap(struct hda_codec *codec, hda_nid_t key_nid,
+                            const struct hda_jack_keymap *keymap,
+                            hda_nid_t jack_nid);
+
+void snd_hda_jack_set_button_state(struct hda_codec *codec, hda_nid_t jack_nid,
+                                  int button_state);
+
+u32 snd_hda_jack_pin_sense(struct hda_codec *codec, hda_nid_t nid, int dev_id);
+
+/* the jack state returned from snd_hda_jack_detect_state() */
+enum {
+       HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT, HDA_JACK_PHANTOM,
+};
+
+int snd_hda_jack_detect_state_mst(struct hda_codec *codec, hda_nid_t nid,
+                                 int dev_id);
+
+/**
+ * snd_hda_jack_detect_state - query pin Presence Detect status
+ * @codec: the CODEC to sense
+ * @nid: the pin NID to sense
+ *
+ * Query and return the pin's Presence Detect status, as either
+ * HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT or HDA_JACK_PHANTOM.
+ */
+static inline int
+snd_hda_jack_detect_state(struct hda_codec *codec, hda_nid_t nid)
+{
+       return snd_hda_jack_detect_state_mst(codec, nid, 0);
+}
+
+/**
+ * snd_hda_jack_detect_mst - Detect the jack
+ * @codec: the HDA codec
+ * @nid: pin NID to check jack detection
+ * @dev_id: pin device entry id
+ */
+static inline bool
+snd_hda_jack_detect_mst(struct hda_codec *codec, hda_nid_t nid, int dev_id)
+{
+       return snd_hda_jack_detect_state_mst(codec, nid, dev_id) !=
+                       HDA_JACK_NOT_PRESENT;
+}
+
+/**
+ * snd_hda_jack_detect - Detect the jack
+ * @codec: the HDA codec
+ * @nid: pin NID to check jack detection
+ */
+static inline bool
+snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid)
+{
+       return snd_hda_jack_detect_mst(codec, nid, 0);
+}
+
+bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid);
+
+int snd_hda_jack_add_kctl_mst(struct hda_codec *codec, hda_nid_t nid,
+                             int dev_id, const char *name, bool phantom_jack,
+                             int type, const struct hda_jack_keymap *keymap);
+
+/**
+ * snd_hda_jack_add_kctl - Add a kctl for the given pin
+ * @codec: the HDA codec
+ * @nid: pin NID to assign
+ * @name: string name for the jack
+ * @phantom_jack: flag to deal as a phantom jack
+ * @type: jack type bits to be reported, 0 for guessing from pincfg
+ * @keymap: optional jack / key mapping
+ *
+ * This assigns a jack-detection kctl to the given pin.  The kcontrol
+ * will have the given name and index.
+ */
+static inline int
+snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
+                     const char *name, bool phantom_jack,
+                     int type, const struct hda_jack_keymap *keymap)
+{
+       return snd_hda_jack_add_kctl_mst(codec, nid, 0,
+                                        name, phantom_jack, type, keymap);
+}
+
+int snd_hda_jack_add_kctls(struct hda_codec *codec,
+                          const struct auto_pin_cfg *cfg);
+
+void snd_hda_jack_report_sync(struct hda_codec *codec);
+
+void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res);
+
+void snd_hda_jack_poll_all(struct hda_codec *codec);
+
+#endif /* __SOUND_HDA_JACK_H */
diff --git a/sound/hda/common/hda_local.h b/sound/hda/common/hda_local.h
new file mode 100644 (file)
index 0000000..68c31f5
--- /dev/null
@@ -0,0 +1,720 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * Local helper functions
+ *
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ */
+
+#ifndef __SOUND_HDA_LOCAL_H
+#define __SOUND_HDA_LOCAL_H
+
+#include <sound/pcm_drm_eld.h>
+
+/* We abuse kcontrol_new.subdev field to pass the NID corresponding to
+ * the given new control.  If id.subdev has a bit flag HDA_SUBDEV_NID_FLAG,
+ * snd_hda_ctl_add() takes the lower-bit subdev value as a valid NID.
+ * 
+ * Note that the subdevice field is cleared again before the real registration
+ * in snd_hda_ctl_add(), so that this value won't appear in the outside.
+ */
+#define HDA_SUBDEV_NID_FLAG    (1U << 31)
+#define HDA_SUBDEV_AMP_FLAG    (1U << 30)
+
+/*
+ * for mixer controls
+ */
+#define HDA_COMPOSE_AMP_VAL_OFS(nid,chs,idx,dir,ofs)           \
+       ((nid) | ((chs)<<16) | ((dir)<<18) | ((idx)<<19) | ((ofs)<<23))
+#define HDA_AMP_VAL_MIN_MUTE (1<<29)
+#define HDA_COMPOSE_AMP_VAL(nid,chs,idx,dir) \
+       HDA_COMPOSE_AMP_VAL_OFS(nid, chs, idx, dir, 0)
+/* mono volume with index (index=0,1,...) (channel=1,2) */
+#define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, dir, flags) \
+       { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx,  \
+         .subdevice = HDA_SUBDEV_AMP_FLAG, \
+         .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+                   SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+                   SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \
+         .info = snd_hda_mixer_amp_volume_info, \
+         .get = snd_hda_mixer_amp_volume_get, \
+         .put = snd_hda_mixer_amp_volume_put, \
+         .tlv = { .c = snd_hda_mixer_amp_tlv },                \
+         .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, dir) | flags }
+/* stereo volume with index */
+#define HDA_CODEC_VOLUME_IDX(xname, xcidx, nid, xindex, direction) \
+       HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, 3, xindex, direction, 0)
+/* mono volume */
+#define HDA_CODEC_VOLUME_MONO(xname, nid, channel, xindex, direction) \
+       HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, channel, xindex, direction, 0)
+/* stereo volume */
+#define HDA_CODEC_VOLUME(xname, nid, xindex, direction) \
+       HDA_CODEC_VOLUME_MONO(xname, nid, 3, xindex, direction)
+/* stereo volume with min=mute */
+#define HDA_CODEC_VOLUME_MIN_MUTE(xname, nid, xindex, direction) \
+       HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, 3, xindex, direction, \
+                                 HDA_AMP_VAL_MIN_MUTE)
+/* mono mute switch with index (index=0,1,...) (channel=1,2) */
+#define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
+       { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
+         .subdevice = HDA_SUBDEV_AMP_FLAG, \
+         .info = snd_hda_mixer_amp_switch_info, \
+         .get = snd_hda_mixer_amp_switch_get, \
+         .put = snd_hda_mixer_amp_switch_put, \
+         .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) }
+/* stereo mute switch with index */
+#define HDA_CODEC_MUTE_IDX(xname, xcidx, nid, xindex, direction) \
+       HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, 3, xindex, direction)
+/* mono mute switch */
+#define HDA_CODEC_MUTE_MONO(xname, nid, channel, xindex, direction) \
+       HDA_CODEC_MUTE_MONO_IDX(xname, 0, nid, channel, xindex, direction)
+/* stereo mute switch */
+#define HDA_CODEC_MUTE(xname, nid, xindex, direction) \
+       HDA_CODEC_MUTE_MONO(xname, nid, 3, xindex, direction)
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+/* special beep mono mute switch with index (index=0,1,...) (channel=1,2) */
+#define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
+       { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
+         .subdevice = HDA_SUBDEV_AMP_FLAG, \
+         .info = snd_hda_mixer_amp_switch_info, \
+         .get = snd_hda_mixer_amp_switch_get_beep, \
+         .put = snd_hda_mixer_amp_switch_put_beep, \
+         .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) }
+#else
+/* no digital beep - just the standard one */
+#define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, ch, xidx, dir) \
+       HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, ch, xidx, dir)
+#endif /* CONFIG_SND_HDA_INPUT_BEEP */
+/* special beep mono mute switch */
+#define HDA_CODEC_MUTE_BEEP_MONO(xname, nid, channel, xindex, direction) \
+       HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, 0, nid, channel, xindex, direction)
+/* special beep stereo mute switch */
+#define HDA_CODEC_MUTE_BEEP(xname, nid, xindex, direction) \
+       HDA_CODEC_MUTE_BEEP_MONO(xname, nid, 3, xindex, direction)
+
+extern const char *snd_hda_pcm_type_name[];
+
+int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_info *uinfo);
+int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol);
+int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol);
+int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+                         unsigned int size, unsigned int __user *_tlv);
+int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_info *uinfo);
+int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol);
+int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol);
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+int snd_hda_mixer_amp_switch_get_beep(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_value *ucontrol);
+int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_value *ucontrol);
+#endif
+/* lowlevel accessor with caching; use carefully */
+#define snd_hda_codec_amp_read(codec, nid, ch, dir, idx) \
+       snd_hdac_regmap_get_amp(&(codec)->core, nid, ch, dir, idx)
+int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid,
+                            int ch, int dir, int idx, int mask, int val);
+int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
+                            int direction, int idx, int mask, int val);
+int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch,
+                          int direction, int idx, int mask, int val);
+int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid,
+                                 int dir, int idx, int mask, int val);
+void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
+                            unsigned int *tlv);
+struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
+                                           const char *name);
+int __snd_hda_add_vmaster(struct hda_codec *codec, char *name,
+                         unsigned int *tlv, const char * const *followers,
+                         const char *suffix, bool init_follower_vol,
+                         unsigned int access, struct snd_kcontrol **ctl_ret);
+#define snd_hda_add_vmaster(codec, name, tlv, followers, suffix, access) \
+       __snd_hda_add_vmaster(codec, name, tlv, followers, suffix, true, access, NULL)
+int snd_hda_codec_reset(struct hda_codec *codec);
+void snd_hda_codec_disconnect_pcms(struct hda_codec *codec);
+
+#define snd_hda_regmap_sync(codec)     snd_hdac_regmap_sync(&(codec)->core)
+
+struct hda_vmaster_mute_hook {
+       /* below two fields must be filled by the caller of
+        * snd_hda_add_vmaster_hook() beforehand
+        */
+       struct snd_kcontrol *sw_kctl;
+       void (*hook)(void *, int);
+       /* below are initialized automatically */
+       struct hda_codec *codec;
+};
+
+int snd_hda_add_vmaster_hook(struct hda_codec *codec,
+                            struct hda_vmaster_mute_hook *hook);
+void snd_hda_sync_vmaster_hook(struct hda_vmaster_mute_hook *hook);
+
+/* amp value bits */
+#define HDA_AMP_MUTE   0x80
+#define HDA_AMP_UNMUTE 0x00
+#define HDA_AMP_VOLMASK        0x7f
+
+/*
+ * SPDIF I/O
+ */
+int snd_hda_create_dig_out_ctls(struct hda_codec *codec,
+                               hda_nid_t associated_nid,
+                               hda_nid_t cvt_nid, int type);
+#define snd_hda_create_spdif_out_ctls(codec, anid, cnid) \
+       snd_hda_create_dig_out_ctls(codec, anid, cnid, HDA_PCM_TYPE_SPDIF)
+int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid);
+
+/*
+ * input MUX helper
+ */
+#define HDA_MAX_NUM_INPUTS     36
+struct hda_input_mux_item {
+       char label[32];
+       unsigned int index;
+};
+struct hda_input_mux {
+       unsigned int num_items;
+       struct hda_input_mux_item items[HDA_MAX_NUM_INPUTS];
+};
+
+int snd_hda_input_mux_info(const struct hda_input_mux *imux,
+                          struct snd_ctl_elem_info *uinfo);
+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_codec *codec,
+                         struct hda_input_mux *imux, const char *label,
+                         int index, int *type_idx);
+
+/*
+ * Multi-channel / digital-out PCM helper
+ */
+
+enum { HDA_FRONT, HDA_REAR, HDA_CLFE, HDA_SIDE }; /* index for dac_nidx */
+enum { HDA_DIG_NONE, HDA_DIG_EXCLUSIVE, HDA_DIG_ANALOG_DUP }; /* dig_out_used */
+
+#define HDA_MAX_OUTS   5
+
+struct hda_multi_out {
+       int num_dacs;           /* # of DACs, must be more than 1 */
+       const hda_nid_t *dac_nids;      /* DAC list */
+       hda_nid_t hp_nid;       /* optional DAC for HP, 0 when not exists */
+       hda_nid_t hp_out_nid[HDA_MAX_OUTS];     /* DACs for multiple HPs */
+       hda_nid_t extra_out_nid[HDA_MAX_OUTS];  /* other (e.g. speaker) DACs */
+       hda_nid_t dig_out_nid;  /* digital out audio widget */
+       const hda_nid_t *follower_dig_outs;
+       int max_channels;       /* currently supported analog channels */
+       int dig_out_used;       /* current usage of digital out (HDA_DIG_XXX) */
+       int no_share_stream;    /* don't share a stream with multiple pins */
+       int share_spdif;        /* share SPDIF pin */
+       /* PCM information for both analog and SPDIF DACs */
+       unsigned int analog_rates;
+       unsigned int analog_maxbps;
+       u64 analog_formats;
+       unsigned int spdif_rates;
+       unsigned int spdif_maxbps;
+       u64 spdif_formats;
+};
+
+int snd_hda_create_spdif_share_sw(struct hda_codec *codec,
+                                 struct hda_multi_out *mout);
+int snd_hda_multi_out_dig_open(struct hda_codec *codec,
+                              struct hda_multi_out *mout);
+int snd_hda_multi_out_dig_close(struct hda_codec *codec,
+                               struct hda_multi_out *mout);
+int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
+                                 struct hda_multi_out *mout,
+                                 unsigned int stream_tag,
+                                 unsigned int format,
+                                 struct snd_pcm_substream *substream);
+int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec,
+                                 struct hda_multi_out *mout);
+int snd_hda_multi_out_analog_open(struct hda_codec *codec,
+                                 struct hda_multi_out *mout,
+                                 struct snd_pcm_substream *substream,
+                                 struct hda_pcm_stream *hinfo);
+int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
+                                    struct hda_multi_out *mout,
+                                    unsigned int stream_tag,
+                                    unsigned int format,
+                                    struct snd_pcm_substream *substream);
+int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
+                                    struct hda_multi_out *mout);
+
+/*
+ * generic proc interface
+ */
+#ifdef CONFIG_SND_PROC_FS
+int snd_hda_codec_proc_new(struct hda_codec *codec);
+#else
+static inline int snd_hda_codec_proc_new(struct hda_codec *codec) { return 0; }
+#endif
+
+#define SND_PRINT_BITS_ADVISED_BUFSIZE 16
+void snd_print_pcm_bits(int pcm, char *buf, int buflen);
+
+/*
+ * Misc
+ */
+int snd_hda_add_new_ctls(struct hda_codec *codec,
+                        const struct snd_kcontrol_new *knew);
+
+/*
+ * 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:1;         /* call the chained fixup(s) after this */
+       bool chained_before:1;  /* call the chained fixup(s) before this */
+       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;
+};
+
+/*
+ * extended form of snd_pci_quirk:
+ * for PCI SSID matching, use SND_PCI_QUIRK() like before;
+ * for codec SSID matching, use the new HDA_CODEC_QUIRK() instead
+ */
+struct hda_quirk {
+       unsigned short subvendor;       /* PCI subvendor ID */
+       unsigned short subdevice;       /* PCI subdevice ID */
+       unsigned short subdevice_mask;  /* bitmask to match */
+       bool match_codec_ssid;          /* match only with codec SSID */
+       int value;                      /* value */
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+       const char *name;               /* name of the device (optional) */
+#endif
+};
+
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+#define HDA_CODEC_QUIRK(vend, dev, xname, val) \
+       { _SND_PCI_QUIRK_ID(vend, dev), .value = (val), .name = (xname),\
+                       .match_codec_ssid = true }
+#else
+#define HDA_CODEC_QUIRK(vend, dev, xname, val) \
+       { _SND_PCI_QUIRK_ID(vend, dev), .value = (val), \
+                       .match_codec_ssid = true }
+#endif
+
+struct snd_hda_pin_quirk {
+       unsigned int codec;             /* Codec vendor/device ID */
+       unsigned short subvendor;       /* PCI subvendor ID */
+       const struct hda_pintbl *pins;  /* list of matching pins */
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+       const char *name;
+#endif
+       int value;                      /* quirk value */
+};
+
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+
+#define SND_HDA_PIN_QUIRK(_codec, _subvendor, _name, _value, _pins...) \
+       { .codec = _codec,\
+         .subvendor = _subvendor,\
+         .name = _name,\
+         .value = _value,\
+         .pins = (const struct hda_pintbl[]) { _pins, {0, 0}} \
+       }
+#else
+
+#define SND_HDA_PIN_QUIRK(_codec, _subvendor, _name, _value, _pins...) \
+       { .codec = _codec,\
+         .subvendor = _subvendor,\
+         .value = _value,\
+         .pins = (const struct hda_pintbl[]) { _pins, {0, 0}} \
+       }
+
+#endif
+
+#define HDA_FIXUP_ID_NOT_SET -1
+#define HDA_FIXUP_ID_NO_FIXUP -2
+
+/* fixup types */
+enum {
+       HDA_FIXUP_INVALID,
+       HDA_FIXUP_PINS,
+       HDA_FIXUP_VERBS,
+       HDA_FIXUP_FUNC,
+       HDA_FIXUP_PINCTLS,
+};
+
+/* fixup action definitions */
+enum {
+       HDA_FIXUP_ACT_PRE_PROBE,
+       HDA_FIXUP_ACT_PROBE,
+       HDA_FIXUP_ACT_INIT,
+       HDA_FIXUP_ACT_BUILD,
+       HDA_FIXUP_ACT_FREE,
+};
+
+int snd_hda_add_verbs(struct hda_codec *codec, const struct hda_verb *list);
+void snd_hda_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_apply_fixup(struct hda_codec *codec, int id, int action, int depth);
+void snd_hda_pick_fixup(struct hda_codec *codec,
+                       const struct hda_model_fixup *models,
+                       const struct hda_quirk *quirk,
+                       const struct hda_fixup *fixlist);
+void snd_hda_pick_pin_fixup(struct hda_codec *codec,
+                           const struct snd_hda_pin_quirk *pin_quirk,
+                           const struct hda_fixup *fixlist,
+                           bool match_all_pins);
+
+/* 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) \
+       ((cfg & AC_DEFCFG_DEF_ASSOC) >> AC_DEFCFG_ASSOC_SHIFT)
+#define get_defcfg_location(cfg) \
+       ((cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT)
+#define get_defcfg_sequence(cfg) \
+       (cfg & AC_DEFCFG_SEQUENCE)
+#define get_defcfg_device(cfg) \
+       ((cfg & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT)
+#define get_defcfg_misc(cfg) \
+       ((cfg & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT)
+
+/* amp values */
+#define AMP_IN_MUTE(idx)       (0x7080 | ((idx)<<8))
+#define AMP_IN_UNMUTE(idx)     (0x7000 | ((idx)<<8))
+#define AMP_OUT_MUTE           0xb080
+#define AMP_OUT_UNMUTE         0xb000
+#define AMP_OUT_ZERO           0xb000
+/* pinctl values */
+#define PIN_IN                 (AC_PINCTL_IN_EN)
+#define PIN_VREFHIZ            (AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ)
+#define PIN_VREF50             (AC_PINCTL_IN_EN | AC_PINCTL_VREF_50)
+#define PIN_VREFGRD            (AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD)
+#define PIN_VREF80             (AC_PINCTL_IN_EN | AC_PINCTL_VREF_80)
+#define PIN_VREF100            (AC_PINCTL_IN_EN | AC_PINCTL_VREF_100)
+#define PIN_OUT                        (AC_PINCTL_OUT_EN)
+#define PIN_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);
+unsigned int snd_hda_correct_pin_ctl(struct hda_codec *codec,
+                                    hda_nid_t pin, unsigned int val);
+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);
+}
+
+int snd_hda_codec_get_pin_target(struct hda_codec *codec, hda_nid_t nid);
+int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid,
+                                unsigned int val);
+
+#define for_each_hda_codec_node(nid, codec) \
+       for ((nid) = (codec)->core.start_nid; (nid) < (codec)->core.end_nid; (nid)++)
+
+/* Set the codec power_state flag to indicate to allow unsol event handling;
+ * see hda_codec_unsol_event() in hda_bind.c.  Calling this might confuse the
+ * state tracking, so use with care.
+ */
+static inline void snd_hda_codec_allow_unsol_events(struct hda_codec *codec)
+{
+       codec->core.dev.power.power_state = PMSG_ON;
+}
+
+/*
+ * get widget capabilities
+ */
+static inline u32 get_wcaps(struct hda_codec *codec, hda_nid_t nid)
+{
+       if (nid < codec->core.start_nid ||
+           nid >= codec->core.start_nid + codec->core.num_nodes)
+               return 0;
+       return codec->wcaps[nid - codec->core.start_nid];
+}
+
+/* get the widget type from widget capability bits */
+static inline int get_wcaps_type(unsigned int wcaps)
+{
+       if (!wcaps)
+               return -1; /* invalid type */
+       return (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+}
+
+static inline unsigned int get_wcaps_channels(u32 wcaps)
+{
+       unsigned int chans;
+
+       chans = (wcaps & AC_WCAP_CHAN_CNT_EXT) >> 13;
+       chans = ((chans << 1) | 1) + 1;
+
+       return chans;
+}
+
+static inline void snd_hda_override_wcaps(struct hda_codec *codec,
+                                         hda_nid_t nid, u32 val)
+{
+       if (nid >= codec->core.start_nid &&
+           nid < codec->core.start_nid + codec->core.num_nodes)
+               codec->wcaps[nid - codec->core.start_nid] = val;
+}
+
+u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction);
+int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
+                             unsigned int caps);
+/**
+ * snd_hda_query_pin_caps - Query PIN capabilities
+ * @codec: the HD-auio codec
+ * @nid: the NID to query
+ *
+ * Query PIN capabilities for the given widget.
+ * Returns the obtained capability bits.
+ *
+ * When cap bits have been already read, this doesn't read again but
+ * returns the cached value.
+ */
+static inline u32
+snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
+{
+       return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+
+}
+
+/**
+ * snd_hda_override_pin_caps - Override the pin capabilities
+ * @codec: the CODEC
+ * @nid: the NID to override
+ * @caps: the capability bits to set
+ *
+ * Override the cached PIN capabilitiy bits value by the given one.
+ *
+ * Returns zero if successful or a negative error code.
+ */
+static inline int
+snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid,
+                         unsigned int caps)
+{
+       return snd_hdac_override_parm(&codec->core, nid, AC_PAR_PIN_CAP, caps);
+}
+
+bool snd_hda_check_amp_caps(struct hda_codec *codec, hda_nid_t nid,
+                          int dir, unsigned int bits);
+
+#define nid_has_mute(codec, nid, dir) \
+       snd_hda_check_amp_caps(codec, nid, dir, (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE))
+#define nid_has_volume(codec, nid, dir) \
+       snd_hda_check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS)
+
+
+/* flags for hda_nid_item */
+#define HDA_NID_ITEM_AMP       (1<<0)
+
+struct hda_nid_item {
+       struct snd_kcontrol *kctl;
+       unsigned int index;
+       hda_nid_t nid;
+       unsigned short flags;
+};
+
+int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid,
+                   struct snd_kcontrol *kctl);
+void snd_hda_ctls_clear(struct hda_codec *codec);
+
+/*
+ * hwdep interface
+ */
+#ifdef CONFIG_SND_HDA_HWDEP
+int snd_hda_create_hwdep(struct hda_codec *codec);
+#else
+static inline int snd_hda_create_hwdep(struct hda_codec *codec) { return 0; }
+#endif
+
+void snd_hda_sysfs_init(struct hda_codec *codec);
+void snd_hda_sysfs_clear(struct hda_codec *codec);
+
+extern const struct attribute_group *snd_hda_dev_attr_groups[];
+
+#ifdef CONFIG_SND_HDA_RECONFIG
+const char *snd_hda_get_hint(struct hda_codec *codec, const char *key);
+int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key);
+int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp);
+#else
+static inline
+const char *snd_hda_get_hint(struct hda_codec *codec, const char *key)
+{
+       return NULL;
+}
+
+static inline
+int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
+{
+       return -ENOENT;
+}
+
+static inline
+int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp)
+{
+       return -ENOENT;
+}
+#endif
+
+/*
+ * power-management
+ */
+
+void snd_hda_schedule_power_save(struct hda_codec *codec);
+
+struct hda_amp_list {
+       hda_nid_t nid;
+       unsigned char dir;
+       unsigned char idx;
+};
+
+struct hda_loopback_check {
+       const struct hda_amp_list *amplist;
+       int power_on;
+};
+
+int snd_hda_check_amp_list_power(struct hda_codec *codec,
+                                struct hda_loopback_check *check,
+                                hda_nid_t nid);
+
+/* check whether the actual power state matches with the target state */
+static inline bool
+snd_hda_check_power_state(struct hda_codec *codec, hda_nid_t nid,
+                         unsigned int target_state)
+{
+       return snd_hdac_check_power_state(&codec->core, nid, target_state);
+}
+
+static inline unsigned int snd_hda_sync_power_state(struct hda_codec *codec,
+                                                   hda_nid_t nid,
+                                                   unsigned int target_state)
+{
+       return snd_hdac_sync_power_state(&codec->core, nid, target_state);
+}
+unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec,
+                                            hda_nid_t nid,
+                                            unsigned int power_state);
+
+void snd_hda_codec_shutdown(struct hda_codec *codec);
+
+/*
+ * AMP control callbacks
+ */
+/* retrieve parameters from private_value */
+#define get_amp_nid_(pv)       ((pv) & 0xffff)
+#define get_amp_nid(kc)                get_amp_nid_((kc)->private_value)
+#define get_amp_channels(kc)   (((kc)->private_value >> 16) & 0x3)
+#define get_amp_direction_(pv) (((pv) >> 18) & 0x1)
+#define get_amp_direction(kc)  get_amp_direction_((kc)->private_value)
+#define get_amp_index_(pv)     (((pv) >> 19) & 0xf)
+#define get_amp_index(kc)      get_amp_index_((kc)->private_value)
+#define get_amp_offset(kc)     (((kc)->private_value >> 23) & 0x3f)
+#define get_amp_min_mute(kc)   (((kc)->private_value >> 29) & 0x1)
+
+/*
+ * enum control helper
+ */
+int snd_hda_enum_helper_info(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_info *uinfo,
+                            int num_items, const char * const *texts);
+#define snd_hda_enum_bool_helper_info(kcontrol, uinfo) \
+       snd_hda_enum_helper_info(kcontrol, uinfo, 0, NULL)
+
+struct hdmi_eld {
+       bool    monitor_present;
+       bool    eld_valid;
+       int     eld_size;
+       char    eld_buffer[ELD_MAX_SIZE];
+       struct snd_parsed_hdmi_eld info;
+};
+
+int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid);
+int snd_hdmi_get_eld(struct hda_codec *codec, hda_nid_t nid,
+                    unsigned char *buf, int *eld_size);
+void snd_hdmi_eld_update_pcm_info(struct snd_parsed_hdmi_eld *e,
+                             struct hda_pcm_stream *hinfo);
+
+int snd_hdmi_get_eld_ati(struct hda_codec *codec, hda_nid_t nid,
+                        unsigned char *buf, int *eld_size,
+                        bool rev3_or_later);
+
+#ifdef CONFIG_SND_PROC_FS
+void snd_hdmi_print_eld_info(struct hdmi_eld *eld,
+                            struct snd_info_buffer *buffer,
+                            hda_nid_t pin_nid, int dev_id, hda_nid_t cvt_nid);
+void snd_hdmi_write_eld_info(struct hdmi_eld *eld,
+                            struct snd_info_buffer *buffer);
+#endif
+
+#define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80
+void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen);
+
+void snd_hda_codec_display_power(struct hda_codec *codec, bool enable);
+
+/*
+ */
+#define codec_err(codec, fmt, args...) \
+       dev_err(hda_codec_dev(codec), fmt, ##args)
+#define codec_warn(codec, fmt, args...) \
+       dev_warn(hda_codec_dev(codec), fmt, ##args)
+#define codec_info(codec, fmt, args...) \
+       dev_info(hda_codec_dev(codec), fmt, ##args)
+#define codec_dbg(codec, fmt, args...) \
+       dev_dbg(hda_codec_dev(codec), fmt, ##args)
+
+#endif /* __SOUND_HDA_LOCAL_H */
diff --git a/sound/hda/common/hwdep.c b/sound/hda/common/hwdep.c
new file mode 100644 (file)
index 0000000..9325e5c
--- /dev/null
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HWDEP Interface for HD-audio codec
+ *
+ * Copyright (c) 2007 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/compat.h>
+#include <linux/nospec.h>
+#include <sound/core.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include <sound/hda_hwdep.h>
+#include <sound/minors.h>
+
+/*
+ * write/read an out-of-bound verb
+ */
+static int verb_write_ioctl(struct hda_codec *codec,
+                           struct hda_verb_ioctl __user *arg)
+{
+       u32 verb, res;
+
+       if (get_user(verb, &arg->verb))
+               return -EFAULT;
+       res = snd_hda_codec_read(codec, verb >> 24, 0,
+                                (verb >> 8) & 0xffff, verb & 0xff);
+       if (put_user(res, &arg->res))
+               return -EFAULT;
+       return 0;
+}
+
+static int get_wcap_ioctl(struct hda_codec *codec,
+                         struct hda_verb_ioctl __user *arg)
+{
+       u32 verb, res;
+       
+       if (get_user(verb, &arg->verb))
+               return -EFAULT;
+       /* open-code get_wcaps(verb>>24) with nospec */
+       verb >>= 24;
+       if (verb < codec->core.start_nid ||
+           verb >= codec->core.start_nid + codec->core.num_nodes) {
+               res = 0;
+       } else {
+               verb -= codec->core.start_nid;
+               verb = array_index_nospec(verb, codec->core.num_nodes);
+               res = codec->wcaps[verb];
+       }
+       if (put_user(res, &arg->res))
+               return -EFAULT;
+       return 0;
+}
+
+
+/*
+ */
+static int hda_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
+                          unsigned int cmd, unsigned long arg)
+{
+       struct hda_codec *codec = hw->private_data;
+       void __user *argp = (void __user *)arg;
+
+       switch (cmd) {
+       case HDA_IOCTL_PVERSION:
+               return put_user(HDA_HWDEP_VERSION, (int __user *)argp);
+       case HDA_IOCTL_VERB_WRITE:
+               return verb_write_ioctl(codec, argp);
+       case HDA_IOCTL_GET_WCAP:
+               return get_wcap_ioctl(codec, argp);
+       }
+       return -ENOIOCTLCMD;
+}
+
+#ifdef CONFIG_COMPAT
+static int hda_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file,
+                                 unsigned int cmd, unsigned long arg)
+{
+       return hda_hwdep_ioctl(hw, file, cmd, (unsigned long)compat_ptr(arg));
+}
+#endif
+
+static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file)
+{
+       if (!capable(CAP_SYS_RAWIO))
+               return -EACCES;
+       return 0;
+}
+
+int snd_hda_create_hwdep(struct hda_codec *codec)
+{
+       char hwname[16];
+       struct snd_hwdep *hwdep;
+       int err;
+
+       sprintf(hwname, "HDA Codec %d", codec->addr);
+       err = snd_hwdep_new(codec->card, hwname, codec->addr, &hwdep);
+       if (err < 0)
+               return err;
+       codec->hwdep = hwdep;
+       sprintf(hwdep->name, "HDA Codec %d", codec->addr);
+       hwdep->iface = SNDRV_HWDEP_IFACE_HDA;
+       hwdep->private_data = codec;
+       hwdep->exclusive = 1;
+
+       hwdep->ops.open = hda_hwdep_open;
+       hwdep->ops.ioctl = hda_hwdep_ioctl;
+#ifdef CONFIG_COMPAT
+       hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat;
+#endif
+
+       /* for sysfs */
+       hwdep->dev->groups = snd_hda_dev_attr_groups;
+       dev_set_drvdata(hwdep->dev, codec);
+
+       return 0;
+}
diff --git a/sound/hda/common/jack.c b/sound/hda/common/jack.c
new file mode 100644 (file)
index 0000000..7d7786d
--- /dev/null
@@ -0,0 +1,770 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Jack-detection handling for HD-audio
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/jack.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+
+/**
+ * is_jack_detectable - Check whether the given pin is jack-detectable
+ * @codec: the HDA codec
+ * @nid: pin NID
+ *
+ * Check whether the given pin is capable to report the jack detection.
+ * The jack detection might not work by various reasons, e.g. the jack
+ * detection is prohibited in the codec level, the pin config has
+ * AC_DEFCFG_MISC_NO_PRESENCE bit, no unsol support, etc.
+ */
+bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid)
+{
+       if (codec->no_jack_detect)
+               return false;
+       if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_PRES_DETECT))
+               return false;
+       if (get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) &
+            AC_DEFCFG_MISC_NO_PRESENCE)
+               return false;
+       if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) &&
+           !codec->jackpoll_interval)
+               return false;
+       return true;
+}
+EXPORT_SYMBOL_GPL(is_jack_detectable);
+
+/* execute pin sense measurement */
+static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid, int dev_id)
+{
+       u32 pincap;
+       u32 val;
+
+       if (!codec->no_trigger_sense) {
+               pincap = snd_hda_query_pin_caps(codec, nid);
+               if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */
+                       snd_hda_codec_read(codec, nid, 0,
+                                       AC_VERB_SET_PIN_SENSE, 0);
+       }
+       val = snd_hda_codec_read(codec, nid, 0,
+                                 AC_VERB_GET_PIN_SENSE, dev_id);
+       if (codec->inv_jack_detect)
+               val ^= AC_PINSENSE_PRESENCE;
+       return val;
+}
+
+/**
+ * snd_hda_jack_tbl_get_mst - query the jack-table entry for the given NID
+ * @codec: the HDA codec
+ * @nid: pin NID to refer to
+ * @dev_id: pin device entry id
+ */
+struct hda_jack_tbl *
+snd_hda_jack_tbl_get_mst(struct hda_codec *codec, hda_nid_t nid, int dev_id)
+{
+       struct hda_jack_tbl *jack = codec->jacktbl.list;
+       int i;
+
+       if (!nid || !jack)
+               return NULL;
+       for (i = 0; i < codec->jacktbl.used; i++, jack++)
+               if (jack->nid == nid && jack->dev_id == dev_id)
+                       return jack;
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_get_mst);
+
+/**
+ * snd_hda_jack_tbl_get_from_tag - query the jack-table entry for the given tag
+ * @codec: the HDA codec
+ * @tag: tag value to refer to
+ * @dev_id: pin device entry id
+ */
+struct hda_jack_tbl *
+snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec,
+                             unsigned char tag, int dev_id)
+{
+       struct hda_jack_tbl *jack = codec->jacktbl.list;
+       int i;
+
+       if (!tag || !jack)
+               return NULL;
+       for (i = 0; i < codec->jacktbl.used; i++, jack++)
+               if (jack->tag == tag && jack->dev_id == dev_id)
+                       return jack;
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_get_from_tag);
+
+static struct hda_jack_tbl *
+any_jack_tbl_get_from_nid(struct hda_codec *codec, hda_nid_t nid)
+{
+       struct hda_jack_tbl *jack = codec->jacktbl.list;
+       int i;
+
+       if (!nid || !jack)
+               return NULL;
+       for (i = 0; i < codec->jacktbl.used; i++, jack++)
+               if (jack->nid == nid)
+                       return jack;
+       return NULL;
+}
+
+/**
+ * snd_hda_jack_tbl_new - create a jack-table entry for the given NID
+ * @codec: the HDA codec
+ * @nid: pin NID to assign
+ * @dev_id: pin device entry id
+ */
+static struct hda_jack_tbl *
+snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid, int dev_id)
+{
+       struct hda_jack_tbl *jack =
+               snd_hda_jack_tbl_get_mst(codec, nid, dev_id);
+       struct hda_jack_tbl *existing_nid_jack =
+               any_jack_tbl_get_from_nid(codec, nid);
+
+       WARN_ON(dev_id != 0 && !codec->dp_mst);
+
+       if (jack)
+               return jack;
+       jack = snd_array_new(&codec->jacktbl);
+       if (!jack)
+               return NULL;
+       jack->nid = nid;
+       jack->dev_id = dev_id;
+       jack->jack_dirty = 1;
+       if (existing_nid_jack) {
+               jack->tag = existing_nid_jack->tag;
+
+               /*
+                * Copy jack_detect from existing_nid_jack to avoid
+                * snd_hda_jack_detect_enable_callback_mst() making multiple
+                * SET_UNSOLICITED_ENABLE calls on the same pin.
+                */
+               jack->jack_detect = existing_nid_jack->jack_detect;
+       } else {
+               jack->tag = codec->jacktbl.used;
+       }
+
+       return jack;
+}
+
+void snd_hda_jack_tbl_disconnect(struct hda_codec *codec)
+{
+       struct hda_jack_tbl *jack = codec->jacktbl.list;
+       int i;
+
+       for (i = 0; i < codec->jacktbl.used; i++, jack++) {
+               if (!codec->bus->shutdown && jack->jack)
+                       snd_device_disconnect(codec->card, jack->jack);
+       }
+}
+
+void snd_hda_jack_tbl_clear(struct hda_codec *codec)
+{
+       struct hda_jack_tbl *jack = codec->jacktbl.list;
+       int i;
+
+       for (i = 0; i < codec->jacktbl.used; i++, jack++) {
+               struct hda_jack_callback *cb, *next;
+
+               /* free jack instances manually when clearing/reconfiguring */
+               if (!codec->bus->shutdown && jack->jack)
+                       snd_device_free(codec->card, jack->jack);
+
+               for (cb = jack->callback; cb; cb = next) {
+                       next = cb->next;
+                       kfree(cb);
+               }
+       }
+       snd_array_free(&codec->jacktbl);
+}
+
+#define get_jack_plug_state(sense) !!(sense & AC_PINSENSE_PRESENCE)
+
+/* update the cached value and notification flag if needed */
+static void jack_detect_update(struct hda_codec *codec,
+                              struct hda_jack_tbl *jack)
+{
+       if (!jack->jack_dirty)
+               return;
+
+       if (jack->phantom_jack)
+               jack->pin_sense = AC_PINSENSE_PRESENCE;
+       else
+               jack->pin_sense = read_pin_sense(codec, jack->nid,
+                                                jack->dev_id);
+
+       /* A gating jack indicates the jack is invalid if gating is unplugged */
+       if (jack->gating_jack &&
+           !snd_hda_jack_detect_mst(codec, jack->gating_jack, jack->dev_id))
+               jack->pin_sense &= ~AC_PINSENSE_PRESENCE;
+
+       jack->jack_dirty = 0;
+
+       /* If a jack is gated by this one update it. */
+       if (jack->gated_jack) {
+               struct hda_jack_tbl *gated =
+                       snd_hda_jack_tbl_get_mst(codec, jack->gated_jack,
+                                                jack->dev_id);
+               if (gated) {
+                       gated->jack_dirty = 1;
+                       jack_detect_update(codec, gated);
+               }
+       }
+}
+
+/**
+ * snd_hda_jack_set_dirty_all - Mark all the cached as dirty
+ * @codec: the HDA codec
+ *
+ * This function sets the dirty flag to all entries of jack table.
+ * It's called from the resume path in hda_codec.c.
+ */
+void snd_hda_jack_set_dirty_all(struct hda_codec *codec)
+{
+       struct hda_jack_tbl *jack = codec->jacktbl.list;
+       int i;
+
+       for (i = 0; i < codec->jacktbl.used; i++, jack++)
+               if (jack->nid)
+                       jack->jack_dirty = 1;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_set_dirty_all);
+
+/**
+ * snd_hda_jack_pin_sense - execute pin sense measurement
+ * @codec: the CODEC to sense
+ * @nid: the pin NID to sense
+ * @dev_id: pin device entry id
+ *
+ * Execute necessary pin sense measurement and return its Presence Detect,
+ * Impedance, ELD Valid etc. status bits.
+ */
+u32 snd_hda_jack_pin_sense(struct hda_codec *codec, hda_nid_t nid, int dev_id)
+{
+       struct hda_jack_tbl *jack =
+               snd_hda_jack_tbl_get_mst(codec, nid, dev_id);
+       if (jack) {
+               jack_detect_update(codec, jack);
+               return jack->pin_sense;
+       }
+       return read_pin_sense(codec, nid, dev_id);
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_pin_sense);
+
+/**
+ * snd_hda_jack_detect_state_mst - query pin Presence Detect status
+ * @codec: the CODEC to sense
+ * @nid: the pin NID to sense
+ * @dev_id: pin device entry id
+ *
+ * Query and return the pin's Presence Detect status, as either
+ * HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT or HDA_JACK_PHANTOM.
+ */
+int snd_hda_jack_detect_state_mst(struct hda_codec *codec,
+                                 hda_nid_t nid, int dev_id)
+{
+       struct hda_jack_tbl *jack =
+               snd_hda_jack_tbl_get_mst(codec, nid, dev_id);
+       if (jack && jack->phantom_jack)
+               return HDA_JACK_PHANTOM;
+       else if (snd_hda_jack_pin_sense(codec, nid, dev_id) &
+                AC_PINSENSE_PRESENCE)
+               return HDA_JACK_PRESENT;
+       else
+               return HDA_JACK_NOT_PRESENT;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_detect_state_mst);
+
+static struct hda_jack_callback *
+find_callback_from_list(struct hda_jack_tbl *jack,
+                       hda_jack_callback_fn func)
+{
+       struct hda_jack_callback *cb;
+
+       if (!func)
+               return NULL;
+
+       for (cb = jack->callback; cb; cb = cb->next) {
+               if (cb->func == func)
+                       return cb;
+       }
+
+       return NULL;
+}
+
+/**
+ * snd_hda_jack_detect_enable_callback_mst - enable the jack-detection
+ * @codec: the HDA codec
+ * @nid: pin NID to enable
+ * @func: callback function to register
+ * @dev_id: pin device entry id
+ *
+ * In the case of error, the return value will be a pointer embedded with
+ * errno.  Check and handle the return value appropriately with standard
+ * macros such as @IS_ERR() and @PTR_ERR().
+ */
+struct hda_jack_callback *
+snd_hda_jack_detect_enable_callback_mst(struct hda_codec *codec, hda_nid_t nid,
+                                       int dev_id, hda_jack_callback_fn func)
+{
+       struct hda_jack_tbl *jack;
+       struct hda_jack_callback *callback = NULL;
+       int err;
+
+       jack = snd_hda_jack_tbl_new(codec, nid, dev_id);
+       if (!jack)
+               return ERR_PTR(-ENOMEM);
+
+       callback = find_callback_from_list(jack, func);
+
+       if (func && !callback) {
+               callback = kzalloc(sizeof(*callback), GFP_KERNEL);
+               if (!callback)
+                       return ERR_PTR(-ENOMEM);
+               callback->func = func;
+               callback->nid = jack->nid;
+               callback->dev_id = jack->dev_id;
+               callback->next = jack->callback;
+               jack->callback = callback;
+       }
+
+       if (jack->jack_detect)
+               return callback; /* already registered */
+       jack->jack_detect = 1;
+       if (codec->jackpoll_interval > 0)
+               return callback; /* No unsol if we're polling instead */
+       err = snd_hda_codec_write_cache(codec, nid, 0,
+                                        AC_VERB_SET_UNSOLICITED_ENABLE,
+                                        AC_USRSP_EN | jack->tag);
+       if (err < 0)
+               return ERR_PTR(err);
+       return callback;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable_callback_mst);
+
+/**
+ * snd_hda_jack_detect_enable - Enable the jack detection on the given pin
+ * @codec: the HDA codec
+ * @nid: pin NID to enable jack detection
+ * @dev_id: pin device entry id
+ *
+ * Enable the jack detection with the default callback.  Returns zero if
+ * successful or a negative error code.
+ */
+int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid,
+                              int dev_id)
+{
+       return PTR_ERR_OR_ZERO(snd_hda_jack_detect_enable_callback_mst(codec,
+                                                                      nid,
+                                                                      dev_id,
+                                                                      NULL));
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable);
+
+/**
+ * snd_hda_jack_set_gating_jack - Set gating jack.
+ * @codec: the HDA codec
+ * @gated_nid: gated pin NID
+ * @gating_nid: gating pin NID
+ *
+ * Indicates the gated jack is only valid when the gating jack is plugged.
+ */
+int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
+                                hda_nid_t gating_nid)
+{
+       struct hda_jack_tbl *gated = snd_hda_jack_tbl_new(codec, gated_nid, 0);
+       struct hda_jack_tbl *gating =
+               snd_hda_jack_tbl_new(codec, gating_nid, 0);
+
+       WARN_ON(codec->dp_mst);
+
+       if (!gated || !gating)
+               return -EINVAL;
+
+       gated->gating_jack = gating_nid;
+       gating->gated_jack = gated_nid;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_set_gating_jack);
+
+/**
+ * snd_hda_jack_bind_keymap - bind keys generated from one NID to another jack.
+ * @codec: the HDA codec
+ * @key_nid: key event is generated by this pin NID
+ * @keymap: map of key type and key code
+ * @jack_nid: key reports to the jack of this pin NID
+ *
+ * This function is used in the case of key is generated from one NID while is
+ * reported to the jack of another NID.
+ */
+int snd_hda_jack_bind_keymap(struct hda_codec *codec, hda_nid_t key_nid,
+                            const struct hda_jack_keymap *keymap,
+                            hda_nid_t jack_nid)
+{
+       const struct hda_jack_keymap *map;
+       struct hda_jack_tbl *key_gen = snd_hda_jack_tbl_get(codec, key_nid);
+       struct hda_jack_tbl *report_to = snd_hda_jack_tbl_get(codec, jack_nid);
+
+       WARN_ON(codec->dp_mst);
+
+       if (!key_gen || !report_to || !report_to->jack)
+               return -EINVAL;
+
+       key_gen->key_report_jack = jack_nid;
+
+       if (keymap)
+               for (map = keymap; map->type; map++)
+                       snd_jack_set_key(report_to->jack, map->type, map->key);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_bind_keymap);
+
+/**
+ * snd_hda_jack_set_button_state - report button event to the hda_jack_tbl button_state.
+ * @codec: the HDA codec
+ * @jack_nid: the button event reports to the jack_tbl of this NID
+ * @button_state: the button event captured by codec
+ *
+ * Codec driver calls this function to report the button event.
+ */
+void snd_hda_jack_set_button_state(struct hda_codec *codec, hda_nid_t jack_nid,
+                                  int button_state)
+{
+       struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, jack_nid);
+
+       if (!jack)
+               return;
+
+       if (jack->key_report_jack) {
+               struct hda_jack_tbl *report_to =
+                       snd_hda_jack_tbl_get(codec, jack->key_report_jack);
+
+               if (report_to) {
+                       report_to->button_state = button_state;
+                       return;
+               }
+       }
+
+       jack->button_state = button_state;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_set_button_state);
+
+/**
+ * snd_hda_jack_report_sync - sync the states of all jacks and report if changed
+ * @codec: the HDA codec
+ */
+void snd_hda_jack_report_sync(struct hda_codec *codec)
+{
+       struct hda_jack_tbl *jack;
+       int i, state;
+
+       /* update all jacks at first */
+       jack = codec->jacktbl.list;
+       for (i = 0; i < codec->jacktbl.used; i++, jack++)
+               if (jack->nid)
+                       jack_detect_update(codec, jack);
+
+       /* report the updated jacks; it's done after updating all jacks
+        * to make sure that all gating jacks properly have been set
+        */
+       jack = codec->jacktbl.list;
+       for (i = 0; i < codec->jacktbl.used; i++, jack++)
+               if (jack->nid) {
+                       if (!jack->jack || jack->block_report)
+                               continue;
+                       state = jack->button_state;
+                       if (get_jack_plug_state(jack->pin_sense))
+                               state |= jack->type;
+                       snd_jack_report(jack->jack, state);
+                       if (jack->button_state) {
+                               snd_jack_report(jack->jack,
+                                               state & ~jack->button_state);
+                               jack->button_state = 0; /* button released */
+                       }
+               }
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_report_sync);
+
+/* guess the jack type from the pin-config */
+static int get_input_jack_type(struct hda_codec *codec, hda_nid_t nid)
+{
+       unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+       switch (get_defcfg_device(def_conf)) {
+       case AC_JACK_LINE_OUT:
+       case AC_JACK_SPEAKER:
+               return SND_JACK_LINEOUT;
+       case AC_JACK_HP_OUT:
+               return SND_JACK_HEADPHONE;
+       case AC_JACK_SPDIF_OUT:
+       case AC_JACK_DIG_OTHER_OUT:
+               return SND_JACK_AVOUT;
+       case AC_JACK_MIC_IN:
+               return SND_JACK_MICROPHONE;
+       default:
+               return SND_JACK_LINEIN;
+       }
+}
+
+static void hda_free_jack_priv(struct snd_jack *jack)
+{
+       struct hda_jack_tbl *jacks = jack->private_data;
+       jacks->nid = 0;
+       jacks->jack = NULL;
+}
+
+/**
+ * snd_hda_jack_add_kctl_mst - Add a kctl for the given pin
+ * @codec: the HDA codec
+ * @nid: pin NID to assign
+ * @dev_id : pin device entry id
+ * @name: string name for the jack
+ * @phantom_jack: flag to deal as a phantom jack
+ * @type: jack type bits to be reported, 0 for guessing from pincfg
+ * @keymap: optional jack / key mapping
+ *
+ * This assigns a jack-detection kctl to the given pin.  The kcontrol
+ * will have the given name and index.
+ */
+int snd_hda_jack_add_kctl_mst(struct hda_codec *codec, hda_nid_t nid,
+                             int dev_id, const char *name, bool phantom_jack,
+                             int type, const struct hda_jack_keymap *keymap)
+{
+       struct hda_jack_tbl *jack;
+       const struct hda_jack_keymap *map;
+       int err, state, buttons;
+
+       jack = snd_hda_jack_tbl_new(codec, nid, dev_id);
+       if (!jack)
+               return 0;
+       if (jack->jack)
+               return 0; /* already created */
+
+       if (!type)
+               type = get_input_jack_type(codec, nid);
+
+       buttons = 0;
+       if (keymap) {
+               for (map = keymap; map->type; map++)
+                       buttons |= map->type;
+       }
+
+       err = snd_jack_new(codec->card, name, type | buttons,
+                          &jack->jack, true, phantom_jack);
+       if (err < 0)
+               return err;
+
+       jack->phantom_jack = !!phantom_jack;
+       jack->type = type;
+       jack->button_state = 0;
+       jack->jack->private_data = jack;
+       jack->jack->private_free = hda_free_jack_priv;
+       if (keymap) {
+               for (map = keymap; map->type; map++)
+                       snd_jack_set_key(jack->jack, map->type, map->key);
+       }
+
+       state = snd_hda_jack_detect_mst(codec, nid, dev_id);
+       snd_jack_report(jack->jack, state ? jack->type : 0);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_add_kctl_mst);
+
+static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid,
+                        const struct auto_pin_cfg *cfg,
+                        const char *base_name)
+{
+       unsigned int def_conf, conn;
+       char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+       int err;
+       bool phantom_jack;
+
+       WARN_ON(codec->dp_mst);
+
+       if (!nid)
+               return 0;
+       def_conf = snd_hda_codec_get_pincfg(codec, nid);
+       conn = get_defcfg_connect(def_conf);
+       if (conn == AC_JACK_PORT_NONE)
+               return 0;
+       phantom_jack = (conn != AC_JACK_PORT_COMPLEX) ||
+                      !is_jack_detectable(codec, nid);
+
+       if (base_name)
+               strscpy(name, base_name, sizeof(name));
+       else
+               snd_hda_get_pin_label(codec, nid, cfg, name, sizeof(name), NULL);
+       if (phantom_jack)
+               /* Example final name: "Internal Mic Phantom Jack" */
+               strncat(name, " Phantom", sizeof(name) - strlen(name) - 1);
+       err = snd_hda_jack_add_kctl(codec, nid, name, phantom_jack, 0, NULL);
+       if (err < 0)
+               return err;
+
+       if (!phantom_jack)
+               return snd_hda_jack_detect_enable(codec, nid, 0);
+       return 0;
+}
+
+/**
+ * snd_hda_jack_add_kctls - Add kctls for all pins included in the given pincfg
+ * @codec: the HDA codec
+ * @cfg: pin config table to parse
+ */
+int snd_hda_jack_add_kctls(struct hda_codec *codec,
+                          const struct auto_pin_cfg *cfg)
+{
+       const hda_nid_t *p;
+       int i, err;
+
+       for (i = 0; i < cfg->num_inputs; i++) {
+               /* If we have headphone mics; make sure they get the right name
+                  before grabbed by output pins */
+               if (cfg->inputs[i].is_headphone_mic) {
+                       if (auto_cfg_hp_outs(cfg) == 1)
+                               err = add_jack_kctl(codec, auto_cfg_hp_pins(cfg)[0],
+                                                   cfg, "Headphone Mic");
+                       else
+                               err = add_jack_kctl(codec, cfg->inputs[i].pin,
+                                                   cfg, "Headphone Mic");
+               } else
+                       err = add_jack_kctl(codec, cfg->inputs[i].pin, cfg,
+                                           NULL);
+               if (err < 0)
+                       return err;
+       }
+
+       for (i = 0, p = cfg->line_out_pins; i < cfg->line_outs; i++, p++) {
+               err = add_jack_kctl(codec, *p, cfg, NULL);
+               if (err < 0)
+                       return err;
+       }
+       for (i = 0, p = cfg->hp_pins; i < cfg->hp_outs; i++, p++) {
+               if (*p == *cfg->line_out_pins) /* might be duplicated */
+                       break;
+               err = add_jack_kctl(codec, *p, cfg, NULL);
+               if (err < 0)
+                       return err;
+       }
+       for (i = 0, p = cfg->speaker_pins; i < cfg->speaker_outs; i++, p++) {
+               if (*p == *cfg->line_out_pins) /* might be duplicated */
+                       break;
+               err = add_jack_kctl(codec, *p, cfg, NULL);
+               if (err < 0)
+                       return err;
+       }
+       for (i = 0, p = cfg->dig_out_pins; i < cfg->dig_outs; i++, p++) {
+               err = add_jack_kctl(codec, *p, cfg, NULL);
+               if (err < 0)
+                       return err;
+       }
+       err = add_jack_kctl(codec, cfg->dig_in_pin, cfg, NULL);
+       if (err < 0)
+               return err;
+       err = add_jack_kctl(codec, cfg->mono_out_pin, cfg, NULL);
+       if (err < 0)
+               return err;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_add_kctls);
+
+static void call_jack_callback(struct hda_codec *codec, unsigned int res,
+                              struct hda_jack_tbl *jack)
+{
+       struct hda_jack_callback *cb;
+
+       for (cb = jack->callback; cb; cb = cb->next) {
+               cb->jack = jack;
+               cb->unsol_res = res;
+               cb->func(codec, cb);
+       }
+       if (jack->gated_jack) {
+               struct hda_jack_tbl *gated =
+                       snd_hda_jack_tbl_get_mst(codec, jack->gated_jack,
+                                                jack->dev_id);
+               if (gated) {
+                       for (cb = gated->callback; cb; cb = cb->next) {
+                               cb->jack = gated;
+                               cb->unsol_res = res;
+                               cb->func(codec, cb);
+                       }
+               }
+       }
+}
+
+/**
+ * snd_hda_jack_unsol_event - Handle an unsolicited event
+ * @codec: the HDA codec
+ * @res: the unsolicited event data
+ */
+void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+       struct hda_jack_tbl *event;
+       int tag = (res & AC_UNSOL_RES_TAG) >> AC_UNSOL_RES_TAG_SHIFT;
+
+       if (codec->dp_mst) {
+               int dev_entry =
+                       (res & AC_UNSOL_RES_DE) >> AC_UNSOL_RES_DE_SHIFT;
+
+               event = snd_hda_jack_tbl_get_from_tag(codec, tag, dev_entry);
+       } else {
+               event = snd_hda_jack_tbl_get_from_tag(codec, tag, 0);
+       }
+       if (!event)
+               return;
+
+       if (event->key_report_jack) {
+               struct hda_jack_tbl *report_to =
+                       snd_hda_jack_tbl_get_mst(codec, event->key_report_jack,
+                                                event->dev_id);
+               if (report_to)
+                       report_to->jack_dirty = 1;
+       } else
+               event->jack_dirty = 1;
+
+       call_jack_callback(codec, res, event);
+       snd_hda_jack_report_sync(codec);
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_unsol_event);
+
+/**
+ * snd_hda_jack_poll_all - Poll all jacks
+ * @codec: the HDA codec
+ *
+ * Poll all detectable jacks with dirty flag, update the status, call
+ * callbacks and call snd_hda_jack_report_sync() if any changes are found.
+ */
+void snd_hda_jack_poll_all(struct hda_codec *codec)
+{
+       struct hda_jack_tbl *jack = codec->jacktbl.list;
+       int i, changes = 0;
+
+       for (i = 0; i < codec->jacktbl.used; i++, jack++) {
+               unsigned int old_sense;
+               if (!jack->nid || !jack->jack_dirty || jack->phantom_jack)
+                       continue;
+               old_sense = get_jack_plug_state(jack->pin_sense);
+               jack_detect_update(codec, jack);
+               if (old_sense == get_jack_plug_state(jack->pin_sense))
+                       continue;
+               changes = 1;
+               call_jack_callback(codec, 0, jack);
+       }
+       if (changes)
+               snd_hda_jack_report_sync(codec);
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_poll_all);
+
diff --git a/sound/hda/common/proc.c b/sound/hda/common/proc.c
new file mode 100644 (file)
index 0000000..00c2eeb
--- /dev/null
@@ -0,0 +1,948 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ * 
+ * Generic proc interface
+ *
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <linux/module.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+
+static int dump_coef = -1;
+module_param(dump_coef, int, 0644);
+MODULE_PARM_DESC(dump_coef, "Dump processing coefficients in codec proc file (-1=auto, 0=disable, 1=enable)");
+
+/* always use noncached version */
+#define param_read(codec, nid, parm) \
+       snd_hdac_read_parm_uncached(&(codec)->core, nid, parm)
+
+static const char *get_wid_type_name(unsigned int wid_value)
+{
+       static const char * const names[16] = {
+               [AC_WID_AUD_OUT] = "Audio Output",
+               [AC_WID_AUD_IN] = "Audio Input",
+               [AC_WID_AUD_MIX] = "Audio Mixer",
+               [AC_WID_AUD_SEL] = "Audio Selector",
+               [AC_WID_PIN] = "Pin Complex",
+               [AC_WID_POWER] = "Power Widget",
+               [AC_WID_VOL_KNB] = "Volume Knob Widget",
+               [AC_WID_BEEP] = "Beep Generator Widget",
+               [AC_WID_VENDOR] = "Vendor Defined Widget",
+       };
+       if (wid_value == -1)
+               return "UNKNOWN Widget";
+       wid_value &= 0xf;
+       if (names[wid_value])
+               return names[wid_value];
+       else
+               return "UNKNOWN Widget";
+}
+
+static void print_nid_array(struct snd_info_buffer *buffer,
+                           struct hda_codec *codec, hda_nid_t nid,
+                           struct snd_array *array)
+{
+       int i;
+       struct hda_nid_item *items = array->list, *item;
+       struct snd_kcontrol *kctl;
+       for (i = 0; i < array->used; i++) {
+               item = &items[i];
+               if (item->nid == nid) {
+                       kctl = item->kctl;
+                       snd_iprintf(buffer,
+                         "  Control: name=\"%s\", index=%i, device=%i\n",
+                         kctl->id.name, kctl->id.index + item->index,
+                         kctl->id.device);
+                       if (item->flags & HDA_NID_ITEM_AMP)
+                               snd_iprintf(buffer,
+                                 "    ControlAmp: chs=%lu, dir=%s, "
+                                 "idx=%lu, ofs=%lu\n",
+                                 get_amp_channels(kctl),
+                                 get_amp_direction(kctl) ? "Out" : "In",
+                                 get_amp_index(kctl),
+                                 get_amp_offset(kctl));
+               }
+       }
+}
+
+static void print_nid_pcms(struct snd_info_buffer *buffer,
+                          struct hda_codec *codec, hda_nid_t nid)
+{
+       int type;
+       struct hda_pcm *cpcm;
+
+       list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
+               for (type = 0; type < 2; type++) {
+                       if (cpcm->stream[type].nid != nid || cpcm->pcm == NULL)
+                               continue;
+                       snd_iprintf(buffer, "  Device: name=\"%s\", "
+                                   "type=\"%s\", device=%i\n",
+                                   cpcm->name,
+                                   snd_hda_pcm_type_name[cpcm->pcm_type],
+                                   cpcm->pcm->device);
+               }
+       }
+}
+
+static void print_amp_caps(struct snd_info_buffer *buffer,
+                          struct hda_codec *codec, hda_nid_t nid, int dir)
+{
+       unsigned int caps;
+       caps = param_read(codec, nid, dir == HDA_OUTPUT ?
+                         AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
+       if (caps == -1 || caps == 0) {
+               snd_iprintf(buffer, "N/A\n");
+               return;
+       }
+       snd_iprintf(buffer, "ofs=0x%02x, nsteps=0x%02x, stepsize=0x%02x, "
+                   "mute=%x\n",
+                   caps & AC_AMPCAP_OFFSET,
+                   (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT,
+                   (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT,
+                   (caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT);
+}
+
+/* is this a stereo widget or a stereo-to-mono mix? */
+static bool is_stereo_amps(struct hda_codec *codec, hda_nid_t nid,
+                          int dir, unsigned int wcaps, int indices)
+{
+       hda_nid_t conn;
+
+       if (wcaps & AC_WCAP_STEREO)
+               return true;
+       /* check for a stereo-to-mono mix; it must be:
+        * only a single connection, only for input, and only a mixer widget
+        */
+       if (indices != 1 || dir != HDA_INPUT ||
+           get_wcaps_type(wcaps) != AC_WID_AUD_MIX)
+               return false;
+
+       if (snd_hda_get_raw_connections(codec, nid, &conn, 1) < 0)
+               return false;
+       /* the connection source is a stereo? */
+       wcaps = snd_hda_param_read(codec, conn, AC_PAR_AUDIO_WIDGET_CAP);
+       return !!(wcaps & AC_WCAP_STEREO);
+}
+
+static void print_amp_vals(struct snd_info_buffer *buffer,
+                          struct hda_codec *codec, hda_nid_t nid,
+                          int dir, unsigned int wcaps, int indices)
+{
+       unsigned int val;
+       bool stereo;
+       int i;
+
+       stereo = is_stereo_amps(codec, nid, dir, wcaps, indices);
+
+       dir = dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
+       for (i = 0; i < indices; i++) {
+               snd_iprintf(buffer, " [");
+               val = snd_hda_codec_read(codec, nid, 0,
+                                        AC_VERB_GET_AMP_GAIN_MUTE,
+                                        AC_AMP_GET_LEFT | dir | i);
+               snd_iprintf(buffer, "0x%02x", val);
+               if (stereo) {
+                       val = snd_hda_codec_read(codec, nid, 0,
+                                                AC_VERB_GET_AMP_GAIN_MUTE,
+                                                AC_AMP_GET_RIGHT | dir | i);
+                       snd_iprintf(buffer, " 0x%02x", val);
+               }
+               snd_iprintf(buffer, "]");
+       }
+       snd_iprintf(buffer, "\n");
+}
+
+static void print_pcm_rates(struct snd_info_buffer *buffer, unsigned int pcm)
+{
+       static const unsigned int rates[] = {
+               8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200,
+               96000, 176400, 192000, 384000
+       };
+       int i;
+
+       pcm &= AC_SUPPCM_RATES;
+       snd_iprintf(buffer, "    rates [0x%x]:", pcm);
+       for (i = 0; i < ARRAY_SIZE(rates); i++)
+               if (pcm & (1 << i))
+                       snd_iprintf(buffer,  " %d", rates[i]);
+       snd_iprintf(buffer, "\n");
+}
+
+static void print_pcm_bits(struct snd_info_buffer *buffer, unsigned int pcm)
+{
+       char buf[SND_PRINT_BITS_ADVISED_BUFSIZE];
+
+       snd_iprintf(buffer, "    bits [0x%x]:", (pcm >> 16) & 0xff);
+       snd_print_pcm_bits(pcm, buf, sizeof(buf));
+       snd_iprintf(buffer, "%s\n", buf);
+}
+
+static void print_pcm_formats(struct snd_info_buffer *buffer,
+                             unsigned int streams)
+{
+       snd_iprintf(buffer, "    formats [0x%x]:", streams & 0xf);
+       if (streams & AC_SUPFMT_PCM)
+               snd_iprintf(buffer, " PCM");
+       if (streams & AC_SUPFMT_FLOAT32)
+               snd_iprintf(buffer, " FLOAT");
+       if (streams & AC_SUPFMT_AC3)
+               snd_iprintf(buffer, " AC3");
+       snd_iprintf(buffer, "\n");
+}
+
+static void print_pcm_caps(struct snd_info_buffer *buffer,
+                          struct hda_codec *codec, hda_nid_t nid)
+{
+       unsigned int pcm = param_read(codec, nid, AC_PAR_PCM);
+       unsigned int stream = param_read(codec, nid, AC_PAR_STREAM);
+       if (pcm == -1 || stream == -1) {
+               snd_iprintf(buffer, "N/A\n");
+               return;
+       }
+       print_pcm_rates(buffer, pcm);
+       print_pcm_bits(buffer, pcm);
+       print_pcm_formats(buffer, stream);
+}
+
+static const char *get_jack_connection(u32 cfg)
+{
+       static const char * const names[16] = {
+               "Unknown", "1/8", "1/4", "ATAPI",
+               "RCA", "Optical","Digital", "Analog",
+               "DIN", "XLR", "RJ11", "Comb",
+               NULL, NULL, NULL, "Other"
+       };
+       cfg = (cfg & AC_DEFCFG_CONN_TYPE) >> AC_DEFCFG_CONN_TYPE_SHIFT;
+       if (names[cfg])
+               return names[cfg];
+       else
+               return "UNKNOWN";
+}
+
+static const char *get_jack_color(u32 cfg)
+{
+       static const char * const names[16] = {
+               "Unknown", "Black", "Grey", "Blue",
+               "Green", "Red", "Orange", "Yellow",
+               "Purple", "Pink", NULL, NULL,
+               NULL, NULL, "White", "Other",
+       };
+       cfg = (cfg & AC_DEFCFG_COLOR) >> AC_DEFCFG_COLOR_SHIFT;
+       if (names[cfg])
+               return names[cfg];
+       else
+               return "UNKNOWN";
+}
+
+/*
+ * Parse the pin default config value and returns the string of the
+ * jack location, e.g. "Rear", "Front", etc.
+ */
+static const char *get_jack_location(u32 cfg)
+{
+       static const char * const bases[7] = {
+               "N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom",
+       };
+       static const unsigned char specials_idx[] = {
+               0x07, 0x08,
+               0x17, 0x18, 0x19,
+               0x37, 0x38
+       };
+       static const char * const specials[] = {
+               "Rear Panel", "Drive Bar",
+               "Riser", "HDMI", "ATAPI",
+               "Mobile-In", "Mobile-Out"
+       };
+       int i;
+
+       cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT;
+       if ((cfg & 0x0f) < 7)
+               return bases[cfg & 0x0f];
+       for (i = 0; i < ARRAY_SIZE(specials_idx); i++) {
+               if (cfg == specials_idx[i])
+                       return specials[i];
+       }
+       return "UNKNOWN";
+}
+
+/*
+ * Parse the pin default config value and returns the string of the
+ * jack connectivity, i.e. external or internal connection.
+ */
+static const char *get_jack_connectivity(u32 cfg)
+{
+       static const char * const jack_locations[4] = {
+               "Ext", "Int", "Sep", "Oth"
+       };
+
+       return jack_locations[(cfg >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3];
+}
+
+/*
+ * Parse the pin default config value and returns the string of the
+ * jack type, i.e. the purpose of the jack, such as Line-Out or CD.
+ */
+static const char *get_jack_type(u32 cfg)
+{
+       static const char * const jack_types[16] = {
+               "Line Out", "Speaker", "HP Out", "CD",
+               "SPDIF Out", "Digital Out", "Modem Line", "Modem Hand",
+               "Line In", "Aux", "Mic", "Telephony",
+               "SPDIF In", "Digital In", "Reserved", "Other"
+       };
+
+       return jack_types[(cfg & AC_DEFCFG_DEVICE)
+                               >> AC_DEFCFG_DEVICE_SHIFT];
+}
+
+static void print_pin_caps(struct snd_info_buffer *buffer,
+                          struct hda_codec *codec, hda_nid_t nid,
+                          int *supports_vref)
+{
+       static const char * const jack_conns[4] = {
+               "Jack", "N/A", "Fixed", "Both"
+       };
+       unsigned int caps, val;
+
+       caps = param_read(codec, nid, AC_PAR_PIN_CAP);
+       snd_iprintf(buffer, "  Pincap 0x%08x:", caps);
+       if (caps & AC_PINCAP_IN)
+               snd_iprintf(buffer, " IN");
+       if (caps & AC_PINCAP_OUT)
+               snd_iprintf(buffer, " OUT");
+       if (caps & AC_PINCAP_HP_DRV)
+               snd_iprintf(buffer, " HP");
+       if (caps & AC_PINCAP_EAPD)
+               snd_iprintf(buffer, " EAPD");
+       if (caps & AC_PINCAP_PRES_DETECT)
+               snd_iprintf(buffer, " Detect");
+       if (caps & AC_PINCAP_BALANCE)
+               snd_iprintf(buffer, " Balanced");
+       if (caps & AC_PINCAP_HDMI) {
+               /* Realtek uses this bit as a different meaning */
+               if ((codec->core.vendor_id >> 16) == 0x10ec)
+                       snd_iprintf(buffer, " R/L");
+               else {
+                       if (caps & AC_PINCAP_HBR)
+                               snd_iprintf(buffer, " HBR");
+                       snd_iprintf(buffer, " HDMI");
+               }
+       }
+       if (caps & AC_PINCAP_DP)
+               snd_iprintf(buffer, " DP");
+       if (caps & AC_PINCAP_TRIG_REQ)
+               snd_iprintf(buffer, " Trigger");
+       if (caps & AC_PINCAP_IMP_SENSE)
+               snd_iprintf(buffer, " ImpSense");
+       snd_iprintf(buffer, "\n");
+       if (caps & AC_PINCAP_VREF) {
+               unsigned int vref =
+                       (caps & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
+               snd_iprintf(buffer, "    Vref caps:");
+               if (vref & AC_PINCAP_VREF_HIZ)
+                       snd_iprintf(buffer, " HIZ");
+               if (vref & AC_PINCAP_VREF_50)
+                       snd_iprintf(buffer, " 50");
+               if (vref & AC_PINCAP_VREF_GRD)
+                       snd_iprintf(buffer, " GRD");
+               if (vref & AC_PINCAP_VREF_80)
+                       snd_iprintf(buffer, " 80");
+               if (vref & AC_PINCAP_VREF_100)
+                       snd_iprintf(buffer, " 100");
+               snd_iprintf(buffer, "\n");
+               *supports_vref = 1;
+       } else
+               *supports_vref = 0;
+       if (caps & AC_PINCAP_EAPD) {
+               val = snd_hda_codec_read(codec, nid, 0,
+                                        AC_VERB_GET_EAPD_BTLENABLE, 0);
+               snd_iprintf(buffer, "  EAPD 0x%x:", val);
+               if (val & AC_EAPDBTL_BALANCED)
+                       snd_iprintf(buffer, " BALANCED");
+               if (val & AC_EAPDBTL_EAPD)
+                       snd_iprintf(buffer, " EAPD");
+               if (val & AC_EAPDBTL_LR_SWAP)
+                       snd_iprintf(buffer, " R/L");
+               snd_iprintf(buffer, "\n");
+       }
+       caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+       snd_iprintf(buffer, "  Pin Default 0x%08x: [%s] %s at %s %s\n", caps,
+                   jack_conns[(caps & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT],
+                   get_jack_type(caps),
+                   get_jack_connectivity(caps),
+                   get_jack_location(caps));
+       snd_iprintf(buffer, "    Conn = %s, Color = %s\n",
+                   get_jack_connection(caps),
+                   get_jack_color(caps));
+       /* Default association and sequence values refer to default grouping
+        * of pin complexes and their sequence within the group. This is used
+        * for priority and resource allocation.
+        */
+       snd_iprintf(buffer, "    DefAssociation = 0x%x, Sequence = 0x%x\n",
+                   (caps & AC_DEFCFG_DEF_ASSOC) >> AC_DEFCFG_ASSOC_SHIFT,
+                   caps & AC_DEFCFG_SEQUENCE);
+       if (((caps & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT) &
+           AC_DEFCFG_MISC_NO_PRESENCE) {
+               /* Miscellaneous bit indicates external hardware does not
+                * support presence detection even if the pin complex
+                * indicates it is supported.
+                */
+               snd_iprintf(buffer, "    Misc = NO_PRESENCE\n");
+       }
+}
+
+static void print_pin_ctls(struct snd_info_buffer *buffer,
+                          struct hda_codec *codec, hda_nid_t nid,
+                          int supports_vref)
+{
+       unsigned int pinctls;
+
+       pinctls = snd_hda_codec_read(codec, nid, 0,
+                                    AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+       snd_iprintf(buffer, "  Pin-ctls: 0x%02x:", pinctls);
+       if (pinctls & AC_PINCTL_IN_EN)
+               snd_iprintf(buffer, " IN");
+       if (pinctls & AC_PINCTL_OUT_EN)
+               snd_iprintf(buffer, " OUT");
+       if (pinctls & AC_PINCTL_HP_EN)
+               snd_iprintf(buffer, " HP");
+       if (supports_vref) {
+               int vref = pinctls & AC_PINCTL_VREFEN;
+               switch (vref) {
+               case AC_PINCTL_VREF_HIZ:
+                       snd_iprintf(buffer, " VREF_HIZ");
+                       break;
+               case AC_PINCTL_VREF_50:
+                       snd_iprintf(buffer, " VREF_50");
+                       break;
+               case AC_PINCTL_VREF_GRD:
+                       snd_iprintf(buffer, " VREF_GRD");
+                       break;
+               case AC_PINCTL_VREF_80:
+                       snd_iprintf(buffer, " VREF_80");
+                       break;
+               case AC_PINCTL_VREF_100:
+                       snd_iprintf(buffer, " VREF_100");
+                       break;
+               }
+       }
+       snd_iprintf(buffer, "\n");
+}
+
+static void print_vol_knob(struct snd_info_buffer *buffer,
+                          struct hda_codec *codec, hda_nid_t nid)
+{
+       unsigned int cap = param_read(codec, nid, AC_PAR_VOL_KNB_CAP);
+       snd_iprintf(buffer, "  Volume-Knob: delta=%d, steps=%d, ",
+                   (cap >> 7) & 1, cap & 0x7f);
+       cap = snd_hda_codec_read(codec, nid, 0,
+                                AC_VERB_GET_VOLUME_KNOB_CONTROL, 0);
+       snd_iprintf(buffer, "direct=%d, val=%d\n",
+                   (cap >> 7) & 1, cap & 0x7f);
+}
+
+static void print_audio_io(struct snd_info_buffer *buffer,
+                          struct hda_codec *codec, hda_nid_t nid,
+                          unsigned int wid_type)
+{
+       int conv = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
+       snd_iprintf(buffer,
+                   "  Converter: stream=%d, channel=%d\n",
+                   (conv & AC_CONV_STREAM) >> AC_CONV_STREAM_SHIFT,
+                   conv & AC_CONV_CHANNEL);
+
+       if (wid_type == AC_WID_AUD_IN && (conv & AC_CONV_CHANNEL) == 0) {
+               int sdi = snd_hda_codec_read(codec, nid, 0,
+                                            AC_VERB_GET_SDI_SELECT, 0);
+               snd_iprintf(buffer, "  SDI-Select: %d\n",
+                           sdi & AC_SDI_SELECT);
+       }
+}
+
+static void print_digital_conv(struct snd_info_buffer *buffer,
+                              struct hda_codec *codec, hda_nid_t nid)
+{
+       unsigned int digi1 = snd_hda_codec_read(codec, nid, 0,
+                                               AC_VERB_GET_DIGI_CONVERT_1, 0);
+       unsigned char digi2 = digi1 >> 8;
+       unsigned char digi3 = digi1 >> 16;
+
+       snd_iprintf(buffer, "  Digital:");
+       if (digi1 & AC_DIG1_ENABLE)
+               snd_iprintf(buffer, " Enabled");
+       if (digi1 & AC_DIG1_V)
+               snd_iprintf(buffer, " Validity");
+       if (digi1 & AC_DIG1_VCFG)
+               snd_iprintf(buffer, " ValidityCfg");
+       if (digi1 & AC_DIG1_EMPHASIS)
+               snd_iprintf(buffer, " Preemphasis");
+       if (digi1 & AC_DIG1_COPYRIGHT)
+               snd_iprintf(buffer, " Non-Copyright");
+       if (digi1 & AC_DIG1_NONAUDIO)
+               snd_iprintf(buffer, " Non-Audio");
+       if (digi1 & AC_DIG1_PROFESSIONAL)
+               snd_iprintf(buffer, " Pro");
+       if (digi1 & AC_DIG1_LEVEL)
+               snd_iprintf(buffer, " GenLevel");
+       if (digi3 & AC_DIG3_KAE)
+               snd_iprintf(buffer, " KAE");
+       snd_iprintf(buffer, "\n");
+       snd_iprintf(buffer, "  Digital category: 0x%x\n",
+                   digi2 & AC_DIG2_CC);
+       snd_iprintf(buffer, "  IEC Coding Type: 0x%x\n",
+                       digi3 & AC_DIG3_ICT);
+}
+
+static const char *get_pwr_state(u32 state)
+{
+       static const char * const buf[] = {
+               "D0", "D1", "D2", "D3", "D3cold"
+       };
+       if (state < ARRAY_SIZE(buf))
+               return buf[state];
+       return "UNKNOWN";
+}
+
+static void print_power_state(struct snd_info_buffer *buffer,
+                             struct hda_codec *codec, hda_nid_t nid)
+{
+       static const char * const names[] = {
+               [ilog2(AC_PWRST_D0SUP)]         = "D0",
+               [ilog2(AC_PWRST_D1SUP)]         = "D1",
+               [ilog2(AC_PWRST_D2SUP)]         = "D2",
+               [ilog2(AC_PWRST_D3SUP)]         = "D3",
+               [ilog2(AC_PWRST_D3COLDSUP)]     = "D3cold",
+               [ilog2(AC_PWRST_S3D3COLDSUP)]   = "S3D3cold",
+               [ilog2(AC_PWRST_CLKSTOP)]       = "CLKSTOP",
+               [ilog2(AC_PWRST_EPSS)]          = "EPSS",
+       };
+
+       int sup = param_read(codec, nid, AC_PAR_POWER_STATE);
+       int pwr = snd_hda_codec_read(codec, nid, 0,
+                                    AC_VERB_GET_POWER_STATE, 0);
+       if (sup != -1) {
+               int i;
+
+               snd_iprintf(buffer, "  Power states: ");
+               for (i = 0; i < ARRAY_SIZE(names); i++) {
+                       if (sup & (1U << i))
+                               snd_iprintf(buffer, " %s", names[i]);
+               }
+               snd_iprintf(buffer, "\n");
+       }
+
+       snd_iprintf(buffer, "  Power: setting=%s, actual=%s",
+                   get_pwr_state(pwr & AC_PWRST_SETTING),
+                   get_pwr_state((pwr & AC_PWRST_ACTUAL) >>
+                                 AC_PWRST_ACTUAL_SHIFT));
+       if (pwr & AC_PWRST_ERROR)
+               snd_iprintf(buffer, ", Error");
+       if (pwr & AC_PWRST_CLK_STOP_OK)
+               snd_iprintf(buffer, ", Clock-stop-OK");
+       if (pwr & AC_PWRST_SETTING_RESET)
+               snd_iprintf(buffer, ", Setting-reset");
+       snd_iprintf(buffer, "\n");
+}
+
+static void print_unsol_cap(struct snd_info_buffer *buffer,
+                             struct hda_codec *codec, hda_nid_t nid)
+{
+       int unsol = snd_hda_codec_read(codec, nid, 0,
+                                      AC_VERB_GET_UNSOLICITED_RESPONSE, 0);
+       snd_iprintf(buffer,
+                   "  Unsolicited: tag=%02x, enabled=%d\n",
+                   unsol & AC_UNSOL_TAG,
+                   (unsol & AC_UNSOL_ENABLED) ? 1 : 0);
+}
+
+static inline bool can_dump_coef(struct hda_codec *codec)
+{
+       switch (dump_coef) {
+       case 0: return false;
+       case 1: return true;
+       default: return codec->dump_coef;
+       }
+}
+
+static void print_proc_caps(struct snd_info_buffer *buffer,
+                           struct hda_codec *codec, hda_nid_t nid)
+{
+       unsigned int i, ncoeff, oldindex;
+       unsigned int proc_caps = param_read(codec, nid, AC_PAR_PROC_CAP);
+       ncoeff = (proc_caps & AC_PCAP_NUM_COEF) >> AC_PCAP_NUM_COEF_SHIFT;
+       snd_iprintf(buffer, "  Processing caps: benign=%d, ncoeff=%d\n",
+                   proc_caps & AC_PCAP_BENIGN, ncoeff);
+
+       if (!can_dump_coef(codec))
+               return;
+
+       /* Note: This is racy - another process could run in parallel and change
+          the coef index too. */
+       oldindex = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_COEF_INDEX, 0);
+       for (i = 0; i < ncoeff; i++) {
+               unsigned int val;
+               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, i);
+               val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PROC_COEF,
+                                        0);
+               snd_iprintf(buffer, "    Coeff 0x%02x: 0x%04x\n", i, val);
+       }
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, oldindex);
+}
+
+static void print_conn_list(struct snd_info_buffer *buffer,
+                           struct hda_codec *codec, hda_nid_t nid,
+                           unsigned int wid_type, hda_nid_t *conn,
+                           int conn_len)
+{
+       int c, curr = -1;
+       const hda_nid_t *list;
+       int cache_len;
+
+       if (conn_len > 1 &&
+           wid_type != AC_WID_AUD_MIX &&
+           wid_type != AC_WID_VOL_KNB &&
+           wid_type != AC_WID_POWER)
+               curr = snd_hda_codec_read(codec, nid, 0,
+                                         AC_VERB_GET_CONNECT_SEL, 0);
+       snd_iprintf(buffer, "  Connection: %d\n", conn_len);
+       if (conn_len > 0) {
+               snd_iprintf(buffer, "    ");
+               for (c = 0; c < conn_len; c++) {
+                       snd_iprintf(buffer, " 0x%02x", conn[c]);
+                       if (c == curr)
+                               snd_iprintf(buffer, "*");
+               }
+               snd_iprintf(buffer, "\n");
+       }
+
+       /* Get Cache connections info */
+       cache_len = snd_hda_get_conn_list(codec, nid, &list);
+       if (cache_len >= 0 && (cache_len != conn_len ||
+                             memcmp(list, conn, conn_len) != 0)) {
+               snd_iprintf(buffer, "  In-driver Connection: %d\n", cache_len);
+               if (cache_len > 0) {
+                       snd_iprintf(buffer, "    ");
+                       for (c = 0; c < cache_len; c++)
+                               snd_iprintf(buffer, " 0x%02x", list[c]);
+                       snd_iprintf(buffer, "\n");
+               }
+       }
+}
+
+static void print_gpio(struct snd_info_buffer *buffer,
+                      struct hda_codec *codec, hda_nid_t nid)
+{
+       unsigned int gpio =
+               param_read(codec, codec->core.afg, AC_PAR_GPIO_CAP);
+       unsigned int enable, direction, wake, unsol, sticky, data;
+       int i, max;
+       snd_iprintf(buffer, "GPIO: io=%d, o=%d, i=%d, "
+                   "unsolicited=%d, wake=%d\n",
+                   gpio & AC_GPIO_IO_COUNT,
+                   (gpio & AC_GPIO_O_COUNT) >> AC_GPIO_O_COUNT_SHIFT,
+                   (gpio & AC_GPIO_I_COUNT) >> AC_GPIO_I_COUNT_SHIFT,
+                   (gpio & AC_GPIO_UNSOLICITED) ? 1 : 0,
+                   (gpio & AC_GPIO_WAKE) ? 1 : 0);
+       max = gpio & AC_GPIO_IO_COUNT;
+       if (!max || max > 8)
+               return;
+       enable = snd_hda_codec_read(codec, nid, 0,
+                                   AC_VERB_GET_GPIO_MASK, 0);
+       direction = snd_hda_codec_read(codec, nid, 0,
+                                      AC_VERB_GET_GPIO_DIRECTION, 0);
+       wake = snd_hda_codec_read(codec, nid, 0,
+                                 AC_VERB_GET_GPIO_WAKE_MASK, 0);
+       unsol  = snd_hda_codec_read(codec, nid, 0,
+                                   AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK, 0);
+       sticky = snd_hda_codec_read(codec, nid, 0,
+                                   AC_VERB_GET_GPIO_STICKY_MASK, 0);
+       data = snd_hda_codec_read(codec, nid, 0,
+                                 AC_VERB_GET_GPIO_DATA, 0);
+       for (i = 0; i < max; ++i)
+               snd_iprintf(buffer,
+                           "  IO[%d]: enable=%d, dir=%d, wake=%d, "
+                           "sticky=%d, data=%d, unsol=%d\n", i,
+                           (enable & (1<<i)) ? 1 : 0,
+                           (direction & (1<<i)) ? 1 : 0,
+                           (wake & (1<<i)) ? 1 : 0,
+                           (sticky & (1<<i)) ? 1 : 0,
+                           (data & (1<<i)) ? 1 : 0,
+                           (unsol & (1<<i)) ? 1 : 0);
+       /* FIXME: add GPO and GPI pin information */
+       print_nid_array(buffer, codec, nid, &codec->mixers);
+       print_nid_array(buffer, codec, nid, &codec->nids);
+}
+
+static void print_dpmst_connections(struct snd_info_buffer *buffer, struct hda_codec *codec,
+                                   hda_nid_t nid, int dev_num)
+{
+       int c, conn_len, curr, dev_id_saved;
+       hda_nid_t *conn;
+
+       conn_len = snd_hda_get_num_raw_conns(codec, nid);
+       if (conn_len <= 0)
+               return;
+
+       conn = kmalloc_array(conn_len, sizeof(hda_nid_t), GFP_KERNEL);
+       if (!conn)
+               return;
+
+       dev_id_saved = snd_hda_get_dev_select(codec, nid);
+
+       snd_hda_set_dev_select(codec, nid, dev_num);
+       curr = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0);
+       if (snd_hda_get_raw_connections(codec, nid, conn, conn_len) < 0)
+               goto out;
+
+       for (c = 0; c < conn_len; c++) {
+               snd_iprintf(buffer, " 0x%02x", conn[c]);
+               if (c == curr)
+                       snd_iprintf(buffer, "*");
+       }
+
+out:
+       kfree(conn);
+       snd_hda_set_dev_select(codec, nid, dev_id_saved);
+}
+
+static void print_device_list(struct snd_info_buffer *buffer,
+                           struct hda_codec *codec, hda_nid_t nid)
+{
+       int i, curr = -1;
+       u8 dev_list[AC_MAX_DEV_LIST_LEN];
+       int devlist_len;
+
+       devlist_len = snd_hda_get_devices(codec, nid, dev_list,
+                                       AC_MAX_DEV_LIST_LEN);
+       snd_iprintf(buffer, "  Devices: %d\n", devlist_len);
+       if (devlist_len <= 0)
+               return;
+
+       curr = snd_hda_codec_read(codec, nid, 0,
+                               AC_VERB_GET_DEVICE_SEL, 0);
+
+       for (i = 0; i < devlist_len; i++) {
+               if (i == curr)
+                       snd_iprintf(buffer, "    *");
+               else
+                       snd_iprintf(buffer, "     ");
+
+               snd_iprintf(buffer,
+                       "Dev %02d: PD = %d, ELDV = %d, IA = %d, Connections [", i,
+                       !!(dev_list[i] & AC_DE_PD),
+                       !!(dev_list[i] & AC_DE_ELDV),
+                       !!(dev_list[i] & AC_DE_IA));
+
+               print_dpmst_connections(buffer, codec, nid, i);
+
+               snd_iprintf(buffer, " ]\n");
+       }
+}
+
+static void print_codec_core_info(struct hdac_device *codec,
+                                 struct snd_info_buffer *buffer)
+{
+       snd_iprintf(buffer, "Codec: ");
+       if (codec->vendor_name && codec->chip_name)
+               snd_iprintf(buffer, "%s %s\n",
+                           codec->vendor_name, codec->chip_name);
+       else
+               snd_iprintf(buffer, "Not Set\n");
+       snd_iprintf(buffer, "Address: %d\n", codec->addr);
+       if (codec->afg)
+               snd_iprintf(buffer, "AFG Function Id: 0x%x (unsol %u)\n",
+                       codec->afg_function_id, codec->afg_unsol);
+       if (codec->mfg)
+               snd_iprintf(buffer, "MFG Function Id: 0x%x (unsol %u)\n",
+                       codec->mfg_function_id, codec->mfg_unsol);
+       snd_iprintf(buffer, "Vendor Id: 0x%08x\n", codec->vendor_id);
+       snd_iprintf(buffer, "Subsystem Id: 0x%08x\n", codec->subsystem_id);
+       snd_iprintf(buffer, "Revision Id: 0x%x\n", codec->revision_id);
+
+       if (codec->mfg)
+               snd_iprintf(buffer, "Modem Function Group: 0x%x\n", codec->mfg);
+       else
+               snd_iprintf(buffer, "No Modem Function Group found\n");
+}
+
+static void print_codec_info(struct snd_info_entry *entry,
+                            struct snd_info_buffer *buffer)
+{
+       struct hda_codec *codec = entry->private_data;
+       hda_nid_t nid, fg;
+       int i, nodes;
+
+       print_codec_core_info(&codec->core, buffer);
+       fg = codec->core.afg;
+       if (!fg)
+               return;
+       snd_hda_power_up(codec);
+       snd_iprintf(buffer, "Default PCM:\n");
+       print_pcm_caps(buffer, codec, fg);
+       snd_iprintf(buffer, "Default Amp-In caps: ");
+       print_amp_caps(buffer, codec, fg, HDA_INPUT);
+       snd_iprintf(buffer, "Default Amp-Out caps: ");
+       print_amp_caps(buffer, codec, fg, HDA_OUTPUT);
+       snd_iprintf(buffer, "State of AFG node 0x%02x:\n", fg);
+       print_power_state(buffer, codec, fg);
+
+       nodes = snd_hda_get_sub_nodes(codec, fg, &nid);
+       if (! nid || nodes < 0) {
+               snd_iprintf(buffer, "Invalid AFG subtree\n");
+               snd_hda_power_down(codec);
+               return;
+       }
+
+       print_gpio(buffer, codec, fg);
+       if (codec->proc_widget_hook)
+               codec->proc_widget_hook(buffer, codec, fg);
+
+       for (i = 0; i < nodes; i++, nid++) {
+               unsigned int wid_caps =
+                       param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
+               unsigned int wid_type = get_wcaps_type(wid_caps);
+               hda_nid_t *conn = NULL;
+               int conn_len = 0;
+
+               snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid,
+                           get_wid_type_name(wid_type), wid_caps);
+               if (wid_caps & AC_WCAP_STEREO) {
+                       unsigned int chans = get_wcaps_channels(wid_caps);
+                       if (chans == 2)
+                               snd_iprintf(buffer, " Stereo");
+                       else
+                               snd_iprintf(buffer, " %d-Channels", chans);
+               } else
+                       snd_iprintf(buffer, " Mono");
+               if (wid_caps & AC_WCAP_DIGITAL)
+                       snd_iprintf(buffer, " Digital");
+               if (wid_caps & AC_WCAP_IN_AMP)
+                       snd_iprintf(buffer, " Amp-In");
+               if (wid_caps & AC_WCAP_OUT_AMP)
+                       snd_iprintf(buffer, " Amp-Out");
+               if (wid_caps & AC_WCAP_STRIPE)
+                       snd_iprintf(buffer, " Stripe");
+               if (wid_caps & AC_WCAP_LR_SWAP)
+                       snd_iprintf(buffer, " R/L");
+               if (wid_caps & AC_WCAP_CP_CAPS)
+                       snd_iprintf(buffer, " CP");
+               snd_iprintf(buffer, "\n");
+
+               print_nid_array(buffer, codec, nid, &codec->mixers);
+               print_nid_array(buffer, codec, nid, &codec->nids);
+               print_nid_pcms(buffer, codec, nid);
+
+               /* volume knob is a special widget that always have connection
+                * list
+                */
+               if (wid_type == AC_WID_VOL_KNB)
+                       wid_caps |= AC_WCAP_CONN_LIST;
+
+               if (wid_caps & AC_WCAP_CONN_LIST) {
+                       conn_len = snd_hda_get_num_raw_conns(codec, nid);
+                       if (conn_len > 0) {
+                               conn = kmalloc_array(conn_len,
+                                                    sizeof(hda_nid_t),
+                                                    GFP_KERNEL);
+                               if (!conn)
+                                       return;
+                               if (snd_hda_get_raw_connections(codec, nid, conn,
+                                                               conn_len) < 0)
+                                       conn_len = 0;
+                       }
+               }
+
+               if (wid_caps & AC_WCAP_IN_AMP) {
+                       snd_iprintf(buffer, "  Amp-In caps: ");
+                       print_amp_caps(buffer, codec, nid, HDA_INPUT);
+                       snd_iprintf(buffer, "  Amp-In vals: ");
+                       if (wid_type == AC_WID_PIN ||
+                           (codec->single_adc_amp &&
+                            wid_type == AC_WID_AUD_IN))
+                               print_amp_vals(buffer, codec, nid, HDA_INPUT,
+                                              wid_caps, 1);
+                       else
+                               print_amp_vals(buffer, codec, nid, HDA_INPUT,
+                                              wid_caps, conn_len);
+               }
+               if (wid_caps & AC_WCAP_OUT_AMP) {
+                       snd_iprintf(buffer, "  Amp-Out caps: ");
+                       print_amp_caps(buffer, codec, nid, HDA_OUTPUT);
+                       snd_iprintf(buffer, "  Amp-Out vals: ");
+                       if (wid_type == AC_WID_PIN &&
+                           codec->pin_amp_workaround)
+                               print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
+                                              wid_caps, conn_len);
+                       else
+                               print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
+                                              wid_caps, 1);
+               }
+
+               switch (wid_type) {
+               case AC_WID_PIN: {
+                       int supports_vref;
+                       print_pin_caps(buffer, codec, nid, &supports_vref);
+                       print_pin_ctls(buffer, codec, nid, supports_vref);
+                       break;
+               }
+               case AC_WID_VOL_KNB:
+                       print_vol_knob(buffer, codec, nid);
+                       break;
+               case AC_WID_AUD_OUT:
+               case AC_WID_AUD_IN:
+                       print_audio_io(buffer, codec, nid, wid_type);
+                       if (wid_caps & AC_WCAP_DIGITAL)
+                               print_digital_conv(buffer, codec, nid);
+                       if (wid_caps & AC_WCAP_FORMAT_OVRD) {
+                               snd_iprintf(buffer, "  PCM:\n");
+                               print_pcm_caps(buffer, codec, nid);
+                       }
+                       break;
+               }
+
+               if (wid_caps & AC_WCAP_UNSOL_CAP)
+                       print_unsol_cap(buffer, codec, nid);
+
+               if (wid_caps & AC_WCAP_POWER)
+                       print_power_state(buffer, codec, nid);
+
+               if (wid_caps & AC_WCAP_DELAY)
+                       snd_iprintf(buffer, "  Delay: %d samples\n",
+                                   (wid_caps & AC_WCAP_DELAY) >>
+                                   AC_WCAP_DELAY_SHIFT);
+
+               if (wid_type == AC_WID_PIN && codec->dp_mst)
+                       print_device_list(buffer, codec, nid);
+
+               if (wid_caps & AC_WCAP_CONN_LIST)
+                       print_conn_list(buffer, codec, nid, wid_type,
+                                       conn, conn_len);
+
+               if (wid_caps & AC_WCAP_PROC_WID)
+                       print_proc_caps(buffer, codec, nid);
+
+               if (codec->proc_widget_hook)
+                       codec->proc_widget_hook(buffer, codec, nid);
+
+               kfree(conn);
+       }
+       snd_hda_power_down(codec);
+}
+
+/*
+ * create a proc read
+ */
+int snd_hda_codec_proc_new(struct hda_codec *codec)
+{
+       char name[32];
+
+       snprintf(name, sizeof(name), "codec#%d", codec->core.addr);
+       return snd_card_ro_proc_new(codec->card, name, codec, print_codec_info);
+}
+
diff --git a/sound/hda/common/sysfs.c b/sound/hda/common/sysfs.c
new file mode 100644 (file)
index 0000000..140e24b
--- /dev/null
@@ -0,0 +1,792 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * sysfs interface for HD-audio codec
+ *
+ * Copyright (c) 2014 Takashi Iwai <tiwai@suse.de>
+ *
+ * split from hda_hwdep.c
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/compat.h>
+#include <linux/mutex.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/export.h>
+#include <sound/core.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include <sound/hda_hwdep.h>
+#include <sound/minors.h>
+
+/* hint string pair */
+struct hda_hint {
+       const char *key;
+       const char *val;        /* contained in the same alloc as key */
+};
+
+static ssize_t power_on_acct_show(struct device *dev,
+                                 struct device_attribute *attr,
+                                 char *buf)
+{
+       struct hda_codec *codec = dev_get_drvdata(dev);
+       snd_hda_update_power_acct(codec);
+       return sysfs_emit(buf, "%u\n", jiffies_to_msecs(codec->power_on_acct));
+}
+
+static ssize_t power_off_acct_show(struct device *dev,
+                                  struct device_attribute *attr,
+                                  char *buf)
+{
+       struct hda_codec *codec = dev_get_drvdata(dev);
+       snd_hda_update_power_acct(codec);
+       return sysfs_emit(buf, "%u\n", jiffies_to_msecs(codec->power_off_acct));
+}
+
+static DEVICE_ATTR_RO(power_on_acct);
+static DEVICE_ATTR_RO(power_off_acct);
+
+#define CODEC_INFO_SHOW(type, field)                           \
+static ssize_t type##_show(struct device *dev,                 \
+                          struct device_attribute *attr,       \
+                          char *buf)                           \
+{                                                              \
+       struct hda_codec *codec = dev_get_drvdata(dev);         \
+       return sysfs_emit(buf, "0x%x\n", codec->field);         \
+}
+
+#define CODEC_INFO_STR_SHOW(type, field)                       \
+static ssize_t type##_show(struct device *dev,                 \
+                            struct device_attribute *attr,     \
+                                       char *buf)              \
+{                                                              \
+       struct hda_codec *codec = dev_get_drvdata(dev);         \
+       return sysfs_emit(buf, "%s\n",                          \
+                         codec->field ? codec->field : "");    \
+}
+
+CODEC_INFO_SHOW(vendor_id, core.vendor_id);
+CODEC_INFO_SHOW(subsystem_id, core.subsystem_id);
+CODEC_INFO_SHOW(revision_id, core.revision_id);
+CODEC_INFO_SHOW(afg, core.afg);
+CODEC_INFO_SHOW(mfg, core.mfg);
+CODEC_INFO_STR_SHOW(vendor_name, core.vendor_name);
+CODEC_INFO_STR_SHOW(chip_name, core.chip_name);
+CODEC_INFO_STR_SHOW(modelname, modelname);
+
+static ssize_t pin_configs_show(struct hda_codec *codec,
+                               struct snd_array *list,
+                               char *buf)
+{
+       const struct hda_pincfg *pin;
+       int i, len = 0;
+       mutex_lock(&codec->user_mutex);
+       snd_array_for_each(list, i, pin) {
+               len += sysfs_emit_at(buf, len, "0x%02x 0x%08x\n",
+                                    pin->nid, pin->cfg);
+       }
+       mutex_unlock(&codec->user_mutex);
+       return len;
+}
+
+static ssize_t init_pin_configs_show(struct device *dev,
+                                    struct device_attribute *attr,
+                                    char *buf)
+{
+       struct hda_codec *codec = dev_get_drvdata(dev);
+       return pin_configs_show(codec, &codec->init_pins, buf);
+}
+
+static ssize_t driver_pin_configs_show(struct device *dev,
+                                      struct device_attribute *attr,
+                                      char *buf)
+{
+       struct hda_codec *codec = dev_get_drvdata(dev);
+       return pin_configs_show(codec, &codec->driver_pins, buf);
+}
+
+#ifdef CONFIG_SND_HDA_RECONFIG
+
+/*
+ * sysfs interface
+ */
+
+static int clear_codec(struct hda_codec *codec)
+{
+       int err;
+
+       err = snd_hda_codec_reset(codec);
+       if (err < 0) {
+               codec_err(codec, "The codec is being used, can't free.\n");
+               return err;
+       }
+       snd_hda_sysfs_clear(codec);
+       return 0;
+}
+
+static int reconfig_codec(struct hda_codec *codec)
+{
+       int err;
+
+       snd_hda_power_up(codec);
+       codec_info(codec, "hda-codec: reconfiguring\n");
+       err = snd_hda_codec_reset(codec);
+       if (err < 0) {
+               codec_err(codec,
+                          "The codec is being used, can't reconfigure.\n");
+               goto error;
+       }
+       err = device_reprobe(hda_codec_dev(codec));
+       if (err < 0)
+               goto error;
+       err = snd_card_register(codec->card);
+ error:
+       snd_hda_power_down(codec);
+       return err;
+}
+
+/*
+ * allocate a string at most len chars, and remove the trailing EOL
+ */
+static char *kstrndup_noeol(const char *src, size_t len)
+{
+       char *s = kstrndup(src, len, GFP_KERNEL);
+       char *p;
+       if (!s)
+               return NULL;
+       p = strchr(s, '\n');
+       if (p)
+               *p = 0;
+       return s;
+}
+
+#define CODEC_INFO_STORE(type, field)                          \
+static ssize_t type##_store(struct device *dev,                        \
+                           struct device_attribute *attr,      \
+                           const char *buf, size_t count)      \
+{                                                              \
+       struct hda_codec *codec = dev_get_drvdata(dev);         \
+       unsigned long val;                                      \
+       int err = kstrtoul(buf, 0, &val);                       \
+       if (err < 0)                                            \
+               return err;                                     \
+       codec->field = val;                                     \
+       return count;                                           \
+}
+
+#define CODEC_INFO_STR_STORE(type, field)                      \
+static ssize_t type##_store(struct device *dev,                        \
+                           struct device_attribute *attr,      \
+                           const char *buf, size_t count)      \
+{                                                              \
+       struct hda_codec *codec = dev_get_drvdata(dev);         \
+       char *s = kstrndup_noeol(buf, 64);                      \
+       if (!s)                                                 \
+               return -ENOMEM;                                 \
+       kfree(codec->field);                                    \
+       codec->field = s;                                       \
+       return count;                                           \
+}
+
+CODEC_INFO_STORE(vendor_id, core.vendor_id);
+CODEC_INFO_STORE(subsystem_id, core.subsystem_id);
+CODEC_INFO_STORE(revision_id, core.revision_id);
+CODEC_INFO_STR_STORE(vendor_name, core.vendor_name);
+CODEC_INFO_STR_STORE(chip_name, core.chip_name);
+CODEC_INFO_STR_STORE(modelname, modelname);
+
+#define CODEC_ACTION_STORE(type)                               \
+static ssize_t type##_store(struct device *dev,                        \
+                           struct device_attribute *attr,      \
+                           const char *buf, size_t count)      \
+{                                                              \
+       struct hda_codec *codec = dev_get_drvdata(dev);         \
+       int err = 0;                                            \
+       if (*buf)                                               \
+               err = type##_codec(codec);                      \
+       return err < 0 ? err : count;                           \
+}
+
+CODEC_ACTION_STORE(reconfig);
+CODEC_ACTION_STORE(clear);
+
+static ssize_t init_verbs_show(struct device *dev,
+                              struct device_attribute *attr,
+                              char *buf)
+{
+       struct hda_codec *codec = dev_get_drvdata(dev);
+       const struct hda_verb *v;
+       int i, len = 0;
+       mutex_lock(&codec->user_mutex);
+       snd_array_for_each(&codec->init_verbs, i, v) {
+               len += sysfs_emit_at(buf, len, "0x%02x 0x%03x 0x%04x\n",
+                                    v->nid, v->verb, v->param);
+       }
+       mutex_unlock(&codec->user_mutex);
+       return len;
+}
+
+static int parse_init_verbs(struct hda_codec *codec, const char *buf)
+{
+       struct hda_verb *v;
+       int nid, verb, param;
+
+       if (sscanf(buf, "%i %i %i", &nid, &verb, &param) != 3)
+               return -EINVAL;
+       if (!nid || !verb)
+               return -EINVAL;
+       mutex_lock(&codec->user_mutex);
+       v = snd_array_new(&codec->init_verbs);
+       if (!v) {
+               mutex_unlock(&codec->user_mutex);
+               return -ENOMEM;
+       }
+       v->nid = nid;
+       v->verb = verb;
+       v->param = param;
+       mutex_unlock(&codec->user_mutex);
+       return 0;
+}
+
+static ssize_t init_verbs_store(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t count)
+{
+       struct hda_codec *codec = dev_get_drvdata(dev);
+       int err = parse_init_verbs(codec, buf);
+       if (err < 0)
+               return err;
+       return count;
+}
+
+static ssize_t hints_show(struct device *dev,
+                         struct device_attribute *attr,
+                         char *buf)
+{
+       struct hda_codec *codec = dev_get_drvdata(dev);
+       const struct hda_hint *hint;
+       int i, len = 0;
+       mutex_lock(&codec->user_mutex);
+       snd_array_for_each(&codec->hints, i, hint) {
+               len += sysfs_emit_at(buf, len, "%s = %s\n",
+                                    hint->key, hint->val);
+       }
+       mutex_unlock(&codec->user_mutex);
+       return len;
+}
+
+static struct hda_hint *get_hint(struct hda_codec *codec, const char *key)
+{
+       struct hda_hint *hint;
+       int i;
+
+       snd_array_for_each(&codec->hints, i, hint) {
+               if (!strcmp(hint->key, key))
+                       return hint;
+       }
+       return NULL;
+}
+
+static void remove_trail_spaces(char *str)
+{
+       char *p;
+       if (!*str)
+               return;
+       p = str + strlen(str) - 1;
+       for (; isspace(*p); p--) {
+               *p = 0;
+               if (p == str)
+                       return;
+       }
+}
+
+#define MAX_HINTS      1024
+
+static int parse_hints(struct hda_codec *codec, const char *buf)
+{
+       char *key, *val;
+       struct hda_hint *hint;
+       int err = 0;
+
+       buf = skip_spaces(buf);
+       if (!*buf || *buf == '#' || *buf == '\n')
+               return 0;
+       if (*buf == '=')
+               return -EINVAL;
+       key = kstrndup_noeol(buf, 1024);
+       if (!key)
+               return -ENOMEM;
+       /* extract key and val */
+       val = strchr(key, '=');
+       if (!val) {
+               kfree(key);
+               return -EINVAL;
+       }
+       *val++ = 0;
+       val = skip_spaces(val);
+       remove_trail_spaces(key);
+       remove_trail_spaces(val);
+       mutex_lock(&codec->user_mutex);
+       hint = get_hint(codec, key);
+       if (hint) {
+               /* replace */
+               kfree(hint->key);
+               hint->key = key;
+               hint->val = val;
+               goto unlock;
+       }
+       /* allocate a new hint entry */
+       if (codec->hints.used >= MAX_HINTS)
+               hint = NULL;
+       else
+               hint = snd_array_new(&codec->hints);
+       if (hint) {
+               hint->key = key;
+               hint->val = val;
+       } else {
+               err = -ENOMEM;
+       }
+ unlock:
+       mutex_unlock(&codec->user_mutex);
+       if (err)
+               kfree(key);
+       return err;
+}
+
+static ssize_t hints_store(struct device *dev,
+                          struct device_attribute *attr,
+                          const char *buf, size_t count)
+{
+       struct hda_codec *codec = dev_get_drvdata(dev);
+       int err = parse_hints(codec, buf);
+       if (err < 0)
+               return err;
+       return count;
+}
+
+static ssize_t user_pin_configs_show(struct device *dev,
+                                    struct device_attribute *attr,
+                                    char *buf)
+{
+       struct hda_codec *codec = dev_get_drvdata(dev);
+       return pin_configs_show(codec, &codec->user_pins, buf);
+}
+
+static int parse_user_pin_configs(struct hda_codec *codec, const char *buf)
+{
+       int nid, cfg, err;
+
+       if (sscanf(buf, "%i %i", &nid, &cfg) != 2)
+               return -EINVAL;
+       if (!nid)
+               return -EINVAL;
+       mutex_lock(&codec->user_mutex);
+       err = snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg);
+       mutex_unlock(&codec->user_mutex);
+       return err;
+}
+
+static ssize_t user_pin_configs_store(struct device *dev,
+                                     struct device_attribute *attr,
+                                     const char *buf, size_t count)
+{
+       struct hda_codec *codec = dev_get_drvdata(dev);
+       int err = parse_user_pin_configs(codec, buf);
+       if (err < 0)
+               return err;
+       return count;
+}
+
+/* sysfs attributes exposed only when CONFIG_SND_HDA_RECONFIG=y */
+static DEVICE_ATTR_RW(init_verbs);
+static DEVICE_ATTR_RW(hints);
+static DEVICE_ATTR_RW(user_pin_configs);
+static DEVICE_ATTR_WO(reconfig);
+static DEVICE_ATTR_WO(clear);
+
+/**
+ * snd_hda_get_hint - Look for hint string
+ * @codec: the HDA codec
+ * @key: the hint key string
+ *
+ * Look for a hint key/value pair matching with the given key string
+ * and returns the value string.  If nothing found, returns NULL.
+ */
+const char *snd_hda_get_hint(struct hda_codec *codec, const char *key)
+{
+       struct hda_hint *hint = get_hint(codec, key);
+       return hint ? hint->val : NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_hint);
+
+/**
+ * snd_hda_get_bool_hint - Get a boolean hint value
+ * @codec: the HDA codec
+ * @key: the hint key string
+ *
+ * Look for a hint key/value pair matching with the given key string
+ * and returns a boolean value parsed from the value.  If no matching
+ * key is found, return a negative value.
+ */
+int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
+{
+       const char *p;
+       int ret;
+
+       mutex_lock(&codec->user_mutex);
+       p = snd_hda_get_hint(codec, key);
+       if (!p || !*p)
+               ret = -ENOENT;
+       else {
+               switch (toupper(*p)) {
+               case 'T': /* true */
+               case 'Y': /* yes */
+               case '1':
+                       ret = 1;
+                       break;
+               default:
+                       ret = 0;
+                       break;
+               }
+       }
+       mutex_unlock(&codec->user_mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_bool_hint);
+
+/**
+ * snd_hda_get_int_hint - Get an integer hint value
+ * @codec: the HDA codec
+ * @key: the hint key string
+ * @valp: pointer to store a value
+ *
+ * Look for a hint key/value pair matching with the given key string
+ * and stores the integer value to @valp.  If no matching key is found,
+ * return a negative error code.  Otherwise it returns zero.
+ */
+int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp)
+{
+       const char *p;
+       unsigned long val;
+       int ret;
+
+       mutex_lock(&codec->user_mutex);
+       p = snd_hda_get_hint(codec, key);
+       if (!p)
+               ret = -ENOENT;
+       else if (kstrtoul(p, 0, &val))
+               ret = -EINVAL;
+       else {
+               *valp = val;
+               ret = 0;
+       }
+       mutex_unlock(&codec->user_mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_int_hint);
+#endif /* CONFIG_SND_HDA_RECONFIG */
+
+/*
+ * common sysfs attributes
+ */
+#ifdef CONFIG_SND_HDA_RECONFIG
+#define RECONFIG_DEVICE_ATTR(name)     DEVICE_ATTR_RW(name)
+#else
+#define RECONFIG_DEVICE_ATTR(name)     DEVICE_ATTR_RO(name)
+#endif
+static RECONFIG_DEVICE_ATTR(vendor_id);
+static RECONFIG_DEVICE_ATTR(subsystem_id);
+static RECONFIG_DEVICE_ATTR(revision_id);
+static DEVICE_ATTR_RO(afg);
+static DEVICE_ATTR_RO(mfg);
+static RECONFIG_DEVICE_ATTR(vendor_name);
+static RECONFIG_DEVICE_ATTR(chip_name);
+static RECONFIG_DEVICE_ATTR(modelname);
+static DEVICE_ATTR_RO(init_pin_configs);
+static DEVICE_ATTR_RO(driver_pin_configs);
+
+
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+
+/* parser mode */
+enum {
+       LINE_MODE_NONE,
+       LINE_MODE_CODEC,
+       LINE_MODE_MODEL,
+       LINE_MODE_PINCFG,
+       LINE_MODE_VERB,
+       LINE_MODE_HINT,
+       LINE_MODE_VENDOR_ID,
+       LINE_MODE_SUBSYSTEM_ID,
+       LINE_MODE_REVISION_ID,
+       LINE_MODE_CHIP_NAME,
+       NUM_LINE_MODES,
+};
+
+static inline int strmatch(const char *a, const char *b)
+{
+       return strncasecmp(a, b, strlen(b)) == 0;
+}
+
+/* parse the contents after the line "[codec]"
+ * accept only the line with three numbers, and assign the current codec
+ */
+static void parse_codec_mode(char *buf, struct hda_bus *bus,
+                            struct hda_codec **codecp)
+{
+       int vendorid, subid, caddr;
+       struct hda_codec *codec;
+
+       *codecp = NULL;
+       if (sscanf(buf, "%i %i %i", &vendorid, &subid, &caddr) == 3) {
+               list_for_each_codec(codec, bus) {
+                       if ((vendorid <= 0 || codec->core.vendor_id == vendorid) &&
+                           (subid <= 0 || codec->core.subsystem_id == subid) &&
+                           codec->core.addr == caddr) {
+                               *codecp = codec;
+                               break;
+                       }
+               }
+       }
+}
+
+/* parse the contents after the other command tags, [pincfg], [verb],
+ * [vendor_id], [subsystem_id], [revision_id], [chip_name], [hint] and [model]
+ * just pass to the sysfs helper (only when any codec was specified)
+ */
+static void parse_pincfg_mode(char *buf, struct hda_bus *bus,
+                             struct hda_codec **codecp)
+{
+       parse_user_pin_configs(*codecp, buf);
+}
+
+static void parse_verb_mode(char *buf, struct hda_bus *bus,
+                           struct hda_codec **codecp)
+{
+       parse_init_verbs(*codecp, buf);
+}
+
+static void parse_hint_mode(char *buf, struct hda_bus *bus,
+                           struct hda_codec **codecp)
+{
+       parse_hints(*codecp, buf);
+}
+
+static void parse_model_mode(char *buf, struct hda_bus *bus,
+                            struct hda_codec **codecp)
+{
+       kfree((*codecp)->modelname);
+       (*codecp)->modelname = kstrdup(buf, GFP_KERNEL);
+}
+
+static void parse_chip_name_mode(char *buf, struct hda_bus *bus,
+                                struct hda_codec **codecp)
+{
+       snd_hda_codec_set_name(*codecp, buf);
+}
+
+#define DEFINE_PARSE_ID_MODE(name) \
+static void parse_##name##_mode(char *buf, struct hda_bus *bus, \
+                                struct hda_codec **codecp) \
+{ \
+       unsigned long val; \
+       if (!kstrtoul(buf, 0, &val)) \
+               (*codecp)->core.name = val; \
+}
+
+DEFINE_PARSE_ID_MODE(vendor_id);
+DEFINE_PARSE_ID_MODE(subsystem_id);
+DEFINE_PARSE_ID_MODE(revision_id);
+
+
+struct hda_patch_item {
+       const char *tag;
+       const char *alias;
+       void (*parser)(char *buf, struct hda_bus *bus, struct hda_codec **retc);
+};
+
+static const struct hda_patch_item patch_items[NUM_LINE_MODES] = {
+       [LINE_MODE_CODEC] = {
+               .tag = "[codec]",
+               .parser = parse_codec_mode,
+       },
+       [LINE_MODE_MODEL] = {
+               .tag = "[model]",
+               .parser = parse_model_mode,
+       },
+       [LINE_MODE_VERB] = {
+               .tag = "[verb]",
+               .alias = "[init_verbs]",
+               .parser = parse_verb_mode,
+       },
+       [LINE_MODE_PINCFG] = {
+               .tag = "[pincfg]",
+               .alias = "[user_pin_configs]",
+               .parser = parse_pincfg_mode,
+       },
+       [LINE_MODE_HINT] = {
+               .tag = "[hint]",
+               .alias = "[hints]",
+               .parser = parse_hint_mode
+       },
+       [LINE_MODE_VENDOR_ID] = {
+               .tag = "[vendor_id]",
+               .parser = parse_vendor_id_mode,
+       },
+       [LINE_MODE_SUBSYSTEM_ID] = {
+               .tag = "[subsystem_id]",
+               .parser = parse_subsystem_id_mode,
+       },
+       [LINE_MODE_REVISION_ID] = {
+               .tag = "[revision_id]",
+               .parser = parse_revision_id_mode,
+       },
+       [LINE_MODE_CHIP_NAME] = {
+               .tag = "[chip_name]",
+               .parser = parse_chip_name_mode,
+       },
+};
+
+/* check the line starting with '[' -- change the parser mode accordingly */
+static int parse_line_mode(char *buf, struct hda_bus *bus)
+{
+       int i;
+       for (i = 0; i < ARRAY_SIZE(patch_items); i++) {
+               if (!patch_items[i].tag)
+                       continue;
+               if (strmatch(buf, patch_items[i].tag))
+                       return i;
+               if (patch_items[i].alias && strmatch(buf, patch_items[i].alias))
+                       return i;
+       }
+       return LINE_MODE_NONE;
+}
+
+/* copy one line from the buffer in fw, and update the fields in fw
+ * return zero if it reaches to the end of the buffer, or non-zero
+ * if successfully copied a line
+ *
+ * the spaces at the beginning and the end of the line are stripped
+ */
+static int get_line_from_fw(char *buf, int size, size_t *fw_size_p,
+                           const void **fw_data_p)
+{
+       int len;
+       size_t fw_size = *fw_size_p;
+       const char *p = *fw_data_p;
+
+       while (isspace(*p) && fw_size) {
+               p++;
+               fw_size--;
+       }
+       if (!fw_size)
+               return 0;
+
+       for (len = 0; len < fw_size; len++) {
+               if (!*p)
+                       break;
+               if (*p == '\n') {
+                       p++;
+                       len++;
+                       break;
+               }
+               if (len < size)
+                       *buf++ = *p++;
+       }
+       *buf = 0;
+       *fw_size_p = fw_size - len;
+       *fw_data_p = p;
+       remove_trail_spaces(buf);
+       return 1;
+}
+
+/**
+ * snd_hda_load_patch - load a "patch" firmware file and parse it
+ * @bus: HD-audio bus
+ * @fw_size: the firmware byte size
+ * @fw_buf: the firmware data
+ */
+int snd_hda_load_patch(struct hda_bus *bus, size_t fw_size, const void *fw_buf)
+{
+       char buf[128];
+       struct hda_codec *codec;
+       int line_mode;
+
+       line_mode = LINE_MODE_NONE;
+       codec = NULL;
+       while (get_line_from_fw(buf, sizeof(buf) - 1, &fw_size, &fw_buf)) {
+               if (!*buf || *buf == '#' || *buf == '\n')
+                       continue;
+               if (*buf == '[')
+                       line_mode = parse_line_mode(buf, bus);
+               else if (patch_items[line_mode].parser &&
+                        (codec || line_mode <= LINE_MODE_CODEC))
+                       patch_items[line_mode].parser(buf, bus, &codec);
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_load_patch);
+#endif /* CONFIG_SND_HDA_PATCH_LOADER */
+
+/*
+ * sysfs entries
+ */
+static struct attribute *hda_dev_attrs[] = {
+       &dev_attr_vendor_id.attr,
+       &dev_attr_subsystem_id.attr,
+       &dev_attr_revision_id.attr,
+       &dev_attr_afg.attr,
+       &dev_attr_mfg.attr,
+       &dev_attr_vendor_name.attr,
+       &dev_attr_chip_name.attr,
+       &dev_attr_modelname.attr,
+       &dev_attr_init_pin_configs.attr,
+       &dev_attr_driver_pin_configs.attr,
+       &dev_attr_power_on_acct.attr,
+       &dev_attr_power_off_acct.attr,
+#ifdef CONFIG_SND_HDA_RECONFIG
+       &dev_attr_init_verbs.attr,
+       &dev_attr_hints.attr,
+       &dev_attr_user_pin_configs.attr,
+       &dev_attr_reconfig.attr,
+       &dev_attr_clear.attr,
+#endif
+       NULL
+};
+
+static const struct attribute_group hda_dev_attr_group = {
+       .attrs  = hda_dev_attrs,
+};
+
+const struct attribute_group *snd_hda_dev_attr_groups[] = {
+       &hda_dev_attr_group,
+       NULL
+};
+
+void snd_hda_sysfs_init(struct hda_codec *codec)
+{
+       mutex_init(&codec->user_mutex);
+#ifdef CONFIG_SND_HDA_RECONFIG
+       snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32);
+       snd_array_init(&codec->hints, sizeof(struct hda_hint), 32);
+       snd_array_init(&codec->user_pins, sizeof(struct hda_pincfg), 16);
+#endif
+}
+
+void snd_hda_sysfs_clear(struct hda_codec *codec)
+{
+#ifdef CONFIG_SND_HDA_RECONFIG
+       struct hda_hint *hint;
+       int i;
+
+       /* clear init verbs */
+       snd_array_free(&codec->init_verbs);
+       /* clear hints */
+       snd_array_for_each(&codec->hints, i, hint) {
+               kfree(hint->key); /* we don't need to free hint->val */
+       }
+       snd_array_free(&codec->hints);
+       snd_array_free(&codec->user_pins);
+#endif
+}
index 745f120a5cee2f418a11d375aa8a2c6bac8694b1..1dfd56493d3997ef52318537ad39b870b27a4a1e 100644 (file)
@@ -1,13 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0-only
 menu "HD-Audio"
 
-config SND_HDA
-       tristate
-       select SND_PCM
-       select SND_VMASTER
-       select SND_JACK
-       select SND_HDA_CORE
-
 config SND_HDA_GENERIC_LEDS
        bool
 
@@ -55,53 +48,6 @@ config SND_HDA_ACPI
 
 if SND_HDA
 
-config SND_HDA_HWDEP
-       bool "Build hwdep interface for HD-audio driver"
-       select SND_HWDEP
-       help
-         Say Y here to build a hwdep interface for HD-audio driver.
-         This interface can be used for out-of-band communication
-         with codecs for debugging purposes.
-
-config SND_HDA_RECONFIG
-       bool "Allow dynamic codec reconfiguration"
-       help
-         Say Y here to enable the HD-audio codec re-configuration feature.
-         It allows user to clear the whole codec configuration, change the
-         codec setup, add extra verbs, and re-configure the codec dynamically.
-
-         Note that this item alone doesn't provide the sysfs interface, but
-         enables the feature just for the patch loader below.
-         If you need the traditional sysfs entries for the manual interaction,
-         turn on CONFIG_SND_HDA_HWDEP as well.
-
-config SND_HDA_INPUT_BEEP
-       bool "Support digital beep via input layer"
-       depends on INPUT=y || INPUT=SND_HDA
-       help
-         Say Y here to build a digital beep interface for HD-audio
-         driver. This interface is used to generate digital beeps.
-
-config SND_HDA_INPUT_BEEP_MODE
-       int "Digital beep registration mode (0=off, 1=on)"
-       depends on SND_HDA_INPUT_BEEP=y
-       default "1"
-       range 0 1
-       help
-         Set 0 to disable the digital beep interface for HD-audio by default.
-         Set 1 to always enable the digital beep interface for HD-audio by
-         default.
-
-config SND_HDA_PATCH_LOADER
-       bool "Support initialization patch loading for HD-audio"
-       select FW_LOADER
-       select SND_HDA_RECONFIG
-       help
-         Say Y here to allow the HD-audio driver to load a pseudo
-         firmware file ("patch") for overriding the BIOS setup at
-         start up.  The "patch" file can be specified via patch module
-         option, such as patch=hda-init.
-
 config SND_HDA_CIRRUS_SCODEC
        tristate
 
@@ -394,14 +340,6 @@ config SND_HDA_GENERIC
 comment "Set to Y if you want auto-loading the codec driver"
        depends on SND_HDA=y && SND_HDA_GENERIC=m
 
-config SND_HDA_POWER_SAVE_DEFAULT
-       int "Default time-out for HD-audio power-save mode"
-       depends on PM
-       default 0
-       help
-         The default time-out value in seconds for HD-audio automatic
-         power-save mode.  0 means to disable the power-save mode.
-
 config SND_HDA_INTEL_HDMI_SILENT_STREAM
        bool "Enable Silent Stream always for HDMI"
        depends on SND_HDA_INTEL
@@ -417,20 +355,6 @@ config SND_HDA_INTEL_HDMI_SILENT_STREAM
          This feature can impact power consumption as resources
          are kept reserved both at transmitter and receiver.
 
-config SND_HDA_CTL_DEV_ID
-       bool "Use the device identifier field for controls"
-       depends on SND_HDA_INTEL
-       help
-         Say Y to use the device identifier field for (mixer)
-         controls (old behaviour until this option is available).
-
-         When enabled, the multiple HDA codecs may set the device
-         field in control (mixer) element identifiers. The use
-         of this field is not recommended and defined for mixer controls.
-
-         The old behaviour (Y) is obsolete and will be removed. Consider
-         to not enable this option.
-
 endif
 
 endmenu
index a5ab8ee2d7f941bc600a8893ca8739a294999ad0..920011bb1b06c01b15ee36dcbd04510a0eb443a0 100644 (file)
@@ -3,17 +3,11 @@ snd-hda-intel-y := hda_intel.o
 snd-hda-tegra-y := hda_tegra.o
 snd-hda-acpi-y := hda_acpi.o
 
-snd-hda-codec-y := hda_bind.o hda_codec.o hda_jack.o hda_auto_parser.o hda_sysfs.o
-snd-hda-codec-y += hda_controller.o
-snd-hda-codec-$(CONFIG_SND_PROC_FS) += hda_proc.o
-
-snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
-snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o
-
 # for trace-points
-CFLAGS_hda_controller.o := -I$(src)
 CFLAGS_hda_intel.o := -I$(src)
 
+subdir-ccflags-y += -I$(src)/../../hda/common
+
 snd-hda-codec-generic-y :=     hda_generic.o
 snd-hda-codec-realtek-y :=     patch_realtek.o
 snd-hda-codec-cmedia-y :=      patch_cmedia.o
@@ -43,9 +37,6 @@ snd-hda-scodec-tas2781-y :=   tas2781_hda.o
 snd-hda-scodec-tas2781-i2c-y :=        tas2781_hda_i2c.o
 snd-hda-scodec-tas2781-spi-y :=        tas2781_hda_spi.o
 
-# common driver
-obj-$(CONFIG_SND_HDA) := snd-hda-codec.o
-
 # codec drivers
 obj-$(CONFIG_SND_HDA_GENERIC) += snd-hda-codec-generic.o
 obj-$(CONFIG_SND_HDA_CODEC_REALTEK) += snd-hda-codec-realtek.o
diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c
deleted file mode 100644 (file)
index 8923813..0000000
+++ /dev/null
@@ -1,1104 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * BIOS auto-parser helper functions for HD-audio
- *
- * Copyright (c) 2012 Takashi Iwai <tiwai@suse.de>
- */
-
-#include <linux/slab.h>
-#include <linux/export.h>
-#include <linux/sort.h>
-#include <sound/core.h>
-#include <sound/hda_codec.h>
-#include "hda_local.h"
-#include "hda_auto_parser.h"
-
-/*
- * 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;
-}
-
-/* a pair of input pin and its sequence */
-struct auto_out_pin {
-       hda_nid_t pin;
-       short seq;
-};
-
-static int compare_seq(const void *ap, const void *bp)
-{
-       const struct auto_out_pin *a = ap;
-       const struct auto_out_pin *b = bp;
-       return (int)(a->seq - b->seq);
-}
-
-/*
- * Sort an associated group of pins according to their sequence numbers.
- * then store it to a pin array.
- */
-static void sort_pins_by_sequence(hda_nid_t *pins, struct auto_out_pin *list,
-                                 int num_pins)
-{
-       int i;
-       sort(list, num_pins, sizeof(list[0]), compare_seq, NULL);
-       for (i = 0; i < num_pins; i++)
-               pins[i] = list[i].pin;
-}
-
-
-/* add the found input-pin to the cfg->inputs[] table */
-static void add_auto_cfg_input_pin(struct hda_codec *codec, 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->inputs[cfg->num_inputs].has_boost_on_pin =
-                       nid_has_volume(codec, nid, HDA_INPUT);
-               cfg->num_inputs++;
-       }
-}
-
-static int compare_input_type(const void *ap, const void *bp)
-{
-       const struct auto_pin_cfg_item *a = ap;
-       const struct auto_pin_cfg_item *b = bp;
-       if (a->type != b->type)
-               return (int)(a->type - b->type);
-
-       /* If has both hs_mic and hp_mic, pick the hs_mic ahead of hp_mic. */
-       if (a->is_headset_mic && b->is_headphone_mic)
-               return -1; /* don't swap */
-       else if (a->is_headphone_mic && b->is_headset_mic)
-               return 1; /* swap */
-
-       /* In case one has boost and the other one has not,
-          pick the one with boost first. */
-       if (a->has_boost_on_pin != b->has_boost_on_pin)
-               return (int)(b->has_boost_on_pin - a->has_boost_on_pin);
-
-       /* Keep the original order */
-       return a->order - b->order;
-}
-
-/* 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)
-{
-       switch (nums) {
-       case 3:
-       case 4:
-               swap(pins[1], pins[2]);
-               break;
-       }
-}
-
-/* check whether the given pin has a proper pin I/O capability bit */
-static bool check_pincap_validity(struct hda_codec *codec, hda_nid_t pin,
-                                 unsigned int dev)
-{
-       unsigned int pincap = snd_hda_query_pin_caps(codec, pin);
-
-       /* some old hardware don't return the proper pincaps */
-       if (!pincap)
-               return true;
-
-       switch (dev) {
-       case AC_JACK_LINE_OUT:
-       case AC_JACK_SPEAKER:
-       case AC_JACK_HP_OUT:
-       case AC_JACK_SPDIF_OUT:
-       case AC_JACK_DIG_OTHER_OUT:
-               return !!(pincap & AC_PINCAP_OUT);
-       default:
-               return !!(pincap & AC_PINCAP_IN);
-       }
-}
-
-static bool can_be_headset_mic(struct hda_codec *codec,
-                              struct auto_pin_cfg_item *item,
-                              int seq_number)
-{
-       int attr;
-       unsigned int def_conf;
-       if (item->type != AUTO_PIN_MIC)
-               return false;
-
-       if (item->is_headset_mic || item->is_headphone_mic)
-               return false; /* Already assigned */
-
-       def_conf = snd_hda_codec_get_pincfg(codec, item->pin);
-       attr = snd_hda_get_input_pin_attr(def_conf);
-       if (attr <= INPUT_PIN_ATTR_DOCK)
-               return false;
-
-       if (seq_number >= 0) {
-               int seq = get_defcfg_sequence(def_conf);
-               if (seq != seq_number)
-                       return false;
-       }
-
-       return true;
-}
-
-/*
- * 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;
-       short seq, assoc_line_out;
-       struct auto_out_pin line_out[ARRAY_SIZE(cfg->line_out_pins)];
-       struct auto_out_pin speaker_out[ARRAY_SIZE(cfg->speaker_pins)];
-       struct auto_out_pin hp_out[ARRAY_SIZE(cfg->hp_pins)];
-       int i;
-
-       if (!snd_hda_get_int_hint(codec, "parser_flags", &i))
-               cond_flags = i;
-
-       memset(cfg, 0, sizeof(*cfg));
-
-       memset(line_out, 0, sizeof(line_out));
-       memset(speaker_out, 0, sizeof(speaker_out));
-       memset(hp_out, 0, sizeof(hp_out));
-       assoc_line_out = 0;
-
-       for_each_hda_codec_node(nid, codec) {
-               unsigned int wid_caps = get_wcaps(codec, nid);
-               unsigned int wid_type = get_wcaps_type(wid_caps);
-               unsigned int def_conf;
-               short assoc, loc, 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);
-               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 ||
-                           conn == AC_JACK_PORT_BOTH)
-                               dev = AC_JACK_SPEAKER;
-               }
-
-               if (!check_pincap_validity(codec, nid, dev))
-                       continue;
-
-               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) {
-                               codec_info(codec,
-                                          "ignore pin 0x%x with mismatching assoc# 0x%x vs 0x%x\n",
-                                          nid, assoc, assoc_line_out);
-                               continue;
-                       }
-                       if (cfg->line_outs >= ARRAY_SIZE(cfg->line_out_pins)) {
-                               codec_info(codec,
-                                          "ignore pin 0x%x, too many assigned pins\n",
-                                          nid);
-                               continue;
-                       }
-                       line_out[cfg->line_outs].pin = nid;
-                       line_out[cfg->line_outs].seq = 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)) {
-                               codec_info(codec,
-                                          "ignore pin 0x%x, too many assigned pins\n",
-                                          nid);
-                               continue;
-                       }
-                       speaker_out[cfg->speaker_outs].pin = nid;
-                       speaker_out[cfg->speaker_outs].seq = (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)) {
-                               codec_info(codec,
-                                          "ignore pin 0x%x, too many assigned pins\n",
-                                          nid);
-                               continue;
-                       }
-                       hp_out[cfg->hp_outs].pin = nid;
-                       hp_out[cfg->hp_outs].seq = (assoc << 4) | seq;
-                       cfg->hp_outs++;
-                       break;
-               case AC_JACK_MIC_IN:
-                       add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_MIC);
-                       break;
-               case AC_JACK_LINE_IN:
-                       add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_LINE_IN);
-                       break;
-               case AC_JACK_CD:
-                       add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_CD);
-                       break;
-               case AC_JACK_AUX:
-                       add_auto_cfg_input_pin(codec, 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)) {
-                               codec_info(codec,
-                                          "ignore pin 0x%x, too many assigned pins\n",
-                                          nid);
-                               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;
-               }
-       }
-
-       /* Find a pin that could be a headset or headphone mic */
-       if (cond_flags & HDA_PINCFG_HEADSET_MIC || cond_flags & HDA_PINCFG_HEADPHONE_MIC) {
-               bool hsmic = !!(cond_flags & HDA_PINCFG_HEADSET_MIC);
-               bool hpmic = !!(cond_flags & HDA_PINCFG_HEADPHONE_MIC);
-               for (i = 0; (hsmic || hpmic) && (i < cfg->num_inputs); i++)
-                       if (hsmic && can_be_headset_mic(codec, &cfg->inputs[i], 0xc)) {
-                               cfg->inputs[i].is_headset_mic = 1;
-                               hsmic = false;
-                       } else if (hpmic && can_be_headset_mic(codec, &cfg->inputs[i], 0xd)) {
-                               cfg->inputs[i].is_headphone_mic = 1;
-                               hpmic = false;
-                       }
-
-               /* If we didn't find our sequence number mark, fall back to any sequence number */
-               for (i = 0; (hsmic || hpmic) && (i < cfg->num_inputs); i++) {
-                       if (!can_be_headset_mic(codec, &cfg->inputs[i], -1))
-                               continue;
-                       if (hsmic) {
-                               cfg->inputs[i].is_headset_mic = 1;
-                               hsmic = false;
-                       } else if (hpmic) {
-                               cfg->inputs[i].is_headphone_mic = 1;
-                               hpmic = false;
-                       }
-               }
-
-               if (hsmic)
-                       codec_dbg(codec, "Told to look for a headset mic, but didn't find any.\n");
-               if (hpmic)
-                       codec_dbg(codec, "Told to look for a headphone mic, but didn't find any.\n");
-       }
-
-       /* 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)) {
-               i = 0;
-               while (i < cfg->hp_outs) {
-                       /* The real HPs should have the sequence 0x0f */
-                       if ((hp_out[i].seq & 0x0f) == 0x0f) {
-                               i++;
-                               continue;
-                       }
-                       /* Move it to the line-out table */
-                       line_out[cfg->line_outs++] = hp_out[i];
-                       cfg->hp_outs--;
-                       memmove(hp_out + i, hp_out + i + 1,
-                               sizeof(hp_out[0]) * (cfg->hp_outs - i));
-               }
-               memset(hp_out + cfg->hp_outs, 0,
-                      sizeof(hp_out[0]) * (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, line_out, cfg->line_outs);
-       sort_pins_by_sequence(cfg->speaker_pins, speaker_out,
-                             cfg->speaker_outs);
-       sort_pins_by_sequence(cfg->hp_pins, hp_out, 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 inputs in the order of AUTO_PIN_* type */
-       for (i = 0; i < cfg->num_inputs; i++)
-               cfg->inputs[i].order = i;
-       sort(cfg->inputs, cfg->num_inputs, sizeof(cfg->inputs[0]),
-            compare_input_type, NULL);
-
-       /*
-        * debug prints of the parsed results
-        */
-       codec_info(codec, "autoconfig for %s: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x) type:%s\n",
-                  codec->core.chip_name, 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"));
-       codec_info(codec, "   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]);
-       codec_info(codec, "   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]);
-       codec_info(codec, "   mono: mono_out=0x%x\n", cfg->mono_out_pin);
-       if (cfg->dig_outs)
-               codec_info(codec, "   dig-out=0x%x/0x%x\n",
-                          cfg->dig_out_pins[0], cfg->dig_out_pins[1]);
-       codec_info(codec, "   inputs:\n");
-       for (i = 0; i < cfg->num_inputs; i++) {
-               codec_info(codec, "     %s=0x%x\n",
-                           hda_get_autocfg_input_label(codec, cfg, i),
-                           cfg->inputs[i].pin);
-       }
-       if (cfg->dig_in_pin)
-               codec_info(codec, "   dig-in=0x%x\n", cfg->dig_in_pin);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_parse_pin_defcfg);
-
-/**
- * snd_hda_get_input_pin_attr - Get the input pin attribute from pin config
- * @def_conf: pin configuration value
- *
- * Guess the input pin attribute (INPUT_PIN_ATTR_XXX) from the given
- * default pin configuration value.
- */
-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_GPL(snd_hda_get_input_pin_attr);
-
-/**
- * hda_get_input_pin_label - Give a label for the given input pin
- * @codec: the HDA codec
- * @item: ping config item to refer
- * @pin: the pin NID
- * @check_location: flag to add the jack location prefix
- *
- * 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,
-                                          const struct auto_pin_cfg_item *item,
-                                          hda_nid_t pin, bool check_location)
-{
-       unsigned int def_conf;
-       static const char * const mic_names[] = {
-               "Internal Mic", "Dock Mic", "Mic", "Rear Mic", "Front Mic"
-       };
-       int attr;
-
-       def_conf = snd_hda_codec_get_pincfg(codec, pin);
-
-       switch (get_defcfg_device(def_conf)) {
-       case AC_JACK_MIC_IN:
-               if (item && item->is_headset_mic)
-                       return "Headset Mic";
-               if (item && item->is_headphone_mic)
-                       return "Headphone Mic";
-               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";
-       case AC_JACK_HP_OUT:
-               return "Headphone Mic";
-       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
- * @codec: the HDA codec
- * @cfg: the parsed pin configuration
- * @input: the input index number
- *
- * 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);
-       has_multiple_pins |= codec->force_pin_prefix;
-       return hda_get_input_pin_label(codec, &cfg->inputs[input],
-                                      cfg->inputs[input].pin,
-                                      has_multiple_pins);
-}
-EXPORT_SYMBOL_GPL(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 const char *check_output_pfx(struct hda_codec *codec, hda_nid_t nid)
-{
-       unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
-       int attr = snd_hda_get_input_pin_attr(def_conf);
-
-       /* check the location */
-       switch (attr) {
-       case INPUT_PIN_ATTR_DOCK:
-               return "Dock ";
-       case INPUT_PIN_ATTR_FRONT:
-               return "Front ";
-       }
-       return "";
-}
-
-static int get_hp_label_index(struct hda_codec *codec, hda_nid_t nid,
-                             const hda_nid_t *pins, int num_pins)
-{
-       int i, j, idx = 0;
-
-       const char *pfx = check_output_pfx(codec, nid);
-
-       i = find_idx_in_nid_list(nid, pins, num_pins);
-       if (i < 0)
-               return -1;
-       for (j = 0; j < i; j++)
-               if (pfx == check_output_pfx(codec, pins[j]))
-                       idx++;
-
-       return idx;
-}
-
-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";
-       pfx = check_output_pfx(codec, nid);
-
-       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 = get_hp_label_index(codec, nid, cfg->hp_pins,
-                                                    cfg->hp_outs);
-                       if (idx >= 0 && indexp)
-                               *indexp = idx;
-                       sfx = "";
-               }
-       }
-       snprintf(label, maxlen, "%s%s%s", pfx, name, sfx);
-       return 1;
-}
-
-#define is_hdmi_cfg(conf) \
-       (get_defcfg_location(conf) == AC_JACK_LOC_HDMI)
-
-/**
- * snd_hda_get_pin_label - Get a label for the given I/O pin
- * @codec: the HDA codec
- * @nid: pin NID
- * @cfg: the parsed pin configuration
- * @label: the string buffer to store
- * @maxlen: the max length of string buffer (including termination)
- * @indexp: the pointer to return the index number (for multiple ctls)
- *
- * 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;
-       bool hdmi;
-
-       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:
-               hdmi = is_hdmi_cfg(def_conf);
-               name = hdmi ? "HDMI" : "SPDIF";
-               if (cfg && indexp)
-                       for (i = 0; i < cfg->dig_outs; i++) {
-                               hda_nid_t pin = cfg->dig_out_pins[i];
-                               unsigned int c;
-                               if (pin == nid)
-                                       break;
-                               c = snd_hda_codec_get_pincfg(codec, pin);
-                               if (hdmi == is_hdmi_cfg(c))
-                                       (*indexp)++;
-                       }
-               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, NULL, nid, true);
-               break;
-       }
-       if (!name)
-               return 0;
-       strscpy(label, name, maxlen);
-       return 1;
-}
-EXPORT_SYMBOL_GPL(snd_hda_get_pin_label);
-
-/**
- * snd_hda_add_verbs - Add verbs to the init list
- * @codec: the HDA codec
- * @list: zero-terminated verb list to add
- *
- * Append the given verb list to the execution list.  The verbs will be
- * performed at init and resume time via snd_hda_apply_verbs().
- */
-int snd_hda_add_verbs(struct hda_codec *codec,
-                     const struct hda_verb *list)
-{
-       const struct hda_verb **v;
-       v = snd_array_new(&codec->verbs);
-       if (!v)
-               return -ENOMEM;
-       *v = list;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_add_verbs);
-
-/**
- * snd_hda_apply_verbs - Execute the init verb lists
- * @codec: the HDA codec
- */
-void snd_hda_apply_verbs(struct hda_codec *codec)
-{
-       const struct hda_verb **v;
-       int i;
-
-       snd_array_for_each(&codec->verbs, i, v)
-               snd_hda_sequence_write(codec, *v);
-}
-EXPORT_SYMBOL_GPL(snd_hda_apply_verbs);
-
-/**
- * snd_hda_apply_pincfgs - Set each pin config in the given list
- * @codec: the HDA codec
- * @cfg: NULL-terminated pin config table
- */
-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_GPL(snd_hda_apply_pincfgs);
-
-static void set_pin_targets(struct hda_codec *codec,
-                           const struct hda_pintbl *cfg)
-{
-       for (; cfg->nid; cfg++)
-               snd_hda_set_pin_ctl_cache(codec, cfg->nid, cfg->val);
-}
-
-void __snd_hda_apply_fixup(struct hda_codec *codec, int id, int action, int depth)
-{
-       const char *modelname = codec->fixup_name;
-
-       while (id >= 0) {
-               const struct hda_fixup *fix = codec->fixup_list + id;
-
-               if (++depth > 10)
-                       break;
-               if (fix->chained_before)
-                       __snd_hda_apply_fixup(codec, fix->chain_id, action, depth + 1);
-
-               switch (fix->type) {
-               case HDA_FIXUP_PINS:
-                       if (action != HDA_FIXUP_ACT_PRE_PROBE || !fix->v.pins)
-                               break;
-                       codec_dbg(codec, "%s: Apply pincfg for %s\n",
-                                   codec->core.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;
-                       codec_dbg(codec, "%s: Apply fix-verbs for %s\n",
-                                   codec->core.chip_name, modelname);
-                       snd_hda_add_verbs(codec, fix->v.verbs);
-                       break;
-               case HDA_FIXUP_FUNC:
-                       if (!fix->v.func)
-                               break;
-                       codec_dbg(codec, "%s: Apply fix-func for %s\n",
-                                   codec->core.chip_name, modelname);
-                       fix->v.func(codec, fix, action);
-                       break;
-               case HDA_FIXUP_PINCTLS:
-                       if (action != HDA_FIXUP_ACT_PROBE || !fix->v.pins)
-                               break;
-                       codec_dbg(codec, "%s: Apply pinctl for %s\n",
-                                   codec->core.chip_name, modelname);
-                       set_pin_targets(codec, fix->v.pins);
-                       break;
-               default:
-                       codec_err(codec, "%s: Invalid fixup type %d\n",
-                                  codec->core.chip_name, fix->type);
-                       break;
-               }
-               if (!fix->chained || fix->chained_before)
-                       break;
-               id = fix->chain_id;
-       }
-}
-EXPORT_SYMBOL_GPL(__snd_hda_apply_fixup);
-
-/**
- * snd_hda_apply_fixup - Apply the fixup chain with the given action
- * @codec: the HDA codec
- * @action: fixup action (HDA_FIXUP_ACT_XXX)
- */
-void snd_hda_apply_fixup(struct hda_codec *codec, int action)
-{
-       if (codec->fixup_list)
-               __snd_hda_apply_fixup(codec, codec->fixup_id, action, 0);
-}
-EXPORT_SYMBOL_GPL(snd_hda_apply_fixup);
-
-#define IGNORE_SEQ_ASSOC (~(AC_DEFCFG_SEQUENCE | AC_DEFCFG_DEF_ASSOC))
-
-static bool pin_config_match(struct hda_codec *codec,
-                            const struct hda_pintbl *pins,
-                            bool match_all_pins)
-{
-       const struct hda_pincfg *pin;
-       int i;
-
-       snd_array_for_each(&codec->init_pins, i, pin) {
-               hda_nid_t nid = pin->nid;
-               u32 cfg = pin->cfg;
-               const struct hda_pintbl *t_pins;
-               int found;
-
-               t_pins = pins;
-               found = 0;
-               for (; t_pins->nid; t_pins++) {
-                       if (t_pins->nid == nid) {
-                               found = 1;
-                               if ((t_pins->val & IGNORE_SEQ_ASSOC) == (cfg & IGNORE_SEQ_ASSOC))
-                                       break;
-                               else if ((cfg & 0xf0000000) == 0x40000000 && (t_pins->val & 0xf0000000) == 0x40000000)
-                                       break;
-                               else
-                                       return false;
-                       }
-               }
-               if (match_all_pins &&
-                   !found && (cfg & 0xf0000000) != 0x40000000)
-                       return false;
-       }
-
-       return true;
-}
-
-/**
- * snd_hda_pick_pin_fixup - Pick up a fixup matching with the pin quirk list
- * @codec: the HDA codec
- * @pin_quirk: zero-terminated pin quirk list
- * @fixlist: the fixup list
- * @match_all_pins: all valid pins must match with the table entries
- */
-void snd_hda_pick_pin_fixup(struct hda_codec *codec,
-                           const struct snd_hda_pin_quirk *pin_quirk,
-                           const struct hda_fixup *fixlist,
-                           bool match_all_pins)
-{
-       const struct snd_hda_pin_quirk *pq;
-       const char *name = NULL;
-
-       if (codec->fixup_id != HDA_FIXUP_ID_NOT_SET)
-               return;
-
-       for (pq = pin_quirk; pq->subvendor; pq++) {
-               if ((codec->core.subsystem_id & 0xffff0000) != (pq->subvendor << 16))
-                       continue;
-               if (codec->core.vendor_id != pq->codec)
-                       continue;
-               if (pin_config_match(codec, pq->pins, match_all_pins)) {
-                       codec->fixup_id = pq->value;
-#ifdef CONFIG_SND_DEBUG_VERBOSE
-                       codec->fixup_name = pq->name;
-                       name = pq->name;
-#endif
-                       codec_info(codec, "%s: picked fixup %s (pin match)\n",
-                                  codec->core.chip_name, name ? name : "");
-                       codec->fixup_list = fixlist;
-                       return;
-               }
-       }
-}
-EXPORT_SYMBOL_GPL(snd_hda_pick_pin_fixup);
-
-/* check whether the given quirk entry matches with vendor/device pair */
-static bool hda_quirk_match(u16 vendor, u16 device, const struct hda_quirk *q)
-{
-       if (q->subvendor != vendor)
-               return false;
-       return !q->subdevice ||
-               (device & q->subdevice_mask) == q->subdevice;
-}
-
-/* look through the quirk list and return the matching entry */
-static const struct hda_quirk *
-hda_quirk_lookup_id(u16 vendor, u16 device, const struct hda_quirk *list)
-{
-       const struct hda_quirk *q;
-
-       for (q = list; q->subvendor || q->subdevice; q++) {
-               if (hda_quirk_match(vendor, device, q))
-                       return q;
-       }
-       return NULL;
-}
-
-/**
- * snd_hda_pick_fixup - Pick up a fixup matching with PCI/codec SSID or model string
- * @codec: the HDA codec
- * @models: NULL-terminated model string list
- * @quirk: zero-terminated PCI/codec SSID quirk list
- * @fixlist: the fixup list
- *
- * Pick up a fixup entry matching with the given model string or SSID.
- * If a fixup was already set beforehand, the function doesn't do anything.
- * When a special model string "nofixup" is given, also no fixup is applied.
- *
- * The function tries to find the matching model name at first, if given.
- * If the model string contains the SSID alias, try to look up with the given
- * alias ID.
- * If nothing matched, try to look up the PCI SSID.
- * If still nothing matched, try to look up the codec SSID.
- */
-void snd_hda_pick_fixup(struct hda_codec *codec,
-                       const struct hda_model_fixup *models,
-                       const struct hda_quirk *quirk,
-                       const struct hda_fixup *fixlist)
-{
-       const struct hda_quirk *q;
-       int id = HDA_FIXUP_ID_NOT_SET;
-       const char *name = NULL;
-       const char *type = NULL;
-       unsigned int vendor, device;
-       u16 pci_vendor, pci_device;
-       u16 codec_vendor, codec_device;
-
-       if (codec->fixup_id != HDA_FIXUP_ID_NOT_SET)
-               return;
-
-       /* when model=nofixup is given, don't pick up any fixups */
-       if (codec->modelname && !strcmp(codec->modelname, "nofixup")) {
-               id = HDA_FIXUP_ID_NO_FIXUP;
-               fixlist = NULL;
-               codec_info(codec, "%s: picked no fixup (nofixup specified)\n",
-                          codec->core.chip_name);
-               goto found;
-       }
-
-       /* match with the model name string */
-       if (codec->modelname && models) {
-               while (models->name) {
-                       if (!strcmp(codec->modelname, models->name)) {
-                               id = models->id;
-                               name = models->name;
-                               codec_info(codec, "%s: picked fixup %s (model specified)\n",
-                                          codec->core.chip_name, name);
-                               goto found;
-                       }
-                       models++;
-               }
-       }
-
-       if (!quirk)
-               return;
-
-       if (codec->bus->pci) {
-               pci_vendor = codec->bus->pci->subsystem_vendor;
-               pci_device = codec->bus->pci->subsystem_device;
-       }
-
-       codec_vendor = codec->core.subsystem_id >> 16;
-       codec_device = codec->core.subsystem_id & 0xffff;
-
-       /* match with the SSID alias given by the model string "XXXX:YYYY" */
-       if (codec->modelname &&
-           sscanf(codec->modelname, "%04x:%04x", &vendor, &device) == 2) {
-               q = hda_quirk_lookup_id(vendor, device, quirk);
-               if (q) {
-                       type = "alias SSID";
-                       goto found_device;
-               }
-       }
-
-       /* match primarily with the PCI SSID */
-       for (q = quirk; q->subvendor || q->subdevice; q++) {
-               /* if the entry is specific to codec SSID, check with it */
-               if (!codec->bus->pci || q->match_codec_ssid) {
-                       if (hda_quirk_match(codec_vendor, codec_device, q)) {
-                               type = "codec SSID";
-                               goto found_device;
-                       }
-               } else {
-                       if (hda_quirk_match(pci_vendor, pci_device, q)) {
-                               type = "PCI SSID";
-                               goto found_device;
-                       }
-               }
-       }
-
-       /* match with the codec SSID */
-       q = hda_quirk_lookup_id(codec_vendor, codec_device, quirk);
-       if (q) {
-               type = "codec SSID";
-               goto found_device;
-       }
-
-       return; /* no matching */
-
- found_device:
-       id = q->value;
-#ifdef CONFIG_SND_DEBUG_VERBOSE
-       name = q->name;
-#endif
-       codec_info(codec, "%s: picked fixup %s for %s %04x:%04x\n",
-                  codec->core.chip_name, name ? name : "",
-                  type, q->subvendor, q->subdevice);
- found:
-       codec->fixup_id = id;
-       codec->fixup_list = fixlist;
-       codec->fixup_name = name;
-}
-EXPORT_SYMBOL_GPL(snd_hda_pick_fixup);
diff --git a/sound/pci/hda/hda_auto_parser.h b/sound/pci/hda/hda_auto_parser.h
deleted file mode 100644 (file)
index 87af3d8..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * BIOS auto-parser helper functions for HD-audio
- *
- * Copyright (c) 2012 Takashi Iwai <tiwai@suse.de>
- */
-
-#ifndef __SOUND_HDA_AUTO_PARSER_H
-#define __SOUND_HDA_AUTO_PARSER_H
-
-#include "hda_local.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       18
-
-struct auto_pin_cfg_item {
-       hda_nid_t pin;
-       int type;
-       unsigned int is_headset_mic:1;
-       unsigned int is_headphone_mic:1; /* Mic-only in headphone jack */
-       unsigned int has_boost_on_pin:1;
-       int order;
-};
-
-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_REAR,    /* mic/line-in jack in rear */
-       INPUT_PIN_ATTR_FRONT,   /* mic/line-in jack in front */
-       INPUT_PIN_ATTR_LAST = INPUT_PIN_ATTR_FRONT,
-};
-
-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 */
-#define HDA_PINCFG_HEADSET_MIC   (1 << 2) /* Try to find headset mic; mark seq number as 0xc to trigger */
-#define HDA_PINCFG_HEADPHONE_MIC (1 << 3) /* Try to find headphone mic; mark seq number as 0xd to trigger */
-
-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)
-
-static inline int auto_cfg_hp_outs(const struct auto_pin_cfg *cfg)
-{
-       return (cfg->line_out_type == AUTO_PIN_HP_OUT) ?
-              cfg->line_outs : cfg->hp_outs;
-}
-static inline const hda_nid_t *auto_cfg_hp_pins(const struct auto_pin_cfg *cfg)
-{
-       return (cfg->line_out_type == AUTO_PIN_HP_OUT) ?
-              cfg->line_out_pins : cfg->hp_pins;
-}
-static inline int auto_cfg_speaker_outs(const struct auto_pin_cfg *cfg)
-{
-       return (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) ?
-              cfg->line_outs : cfg->speaker_outs;
-}
-static inline const hda_nid_t *auto_cfg_speaker_pins(const struct auto_pin_cfg *cfg)
-{
-       return (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) ?
-              cfg->line_out_pins : cfg->speaker_pins;
-}
-
-#endif /* __SOUND_HDA_AUTO_PARSER_H */
diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c
deleted file mode 100644 (file)
index 13a7d92..0000000
+++ /dev/null
@@ -1,345 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Digital Beep Input Interface for HD-audio codec
- *
- * Author: Matt Ranostay <matt.ranostay@konsulko.com>
- * Copyright (c) 2008 Embedded Alley Solutions Inc
- */
-
-#include <linux/input.h>
-#include <linux/slab.h>
-#include <linux/workqueue.h>
-#include <linux/export.h>
-#include <sound/core.h>
-#include "hda_beep.h"
-#include "hda_local.h"
-
-enum {
-       DIGBEEP_HZ_STEP = 46875,        /* 46.875 Hz */
-       DIGBEEP_HZ_MIN = 93750,         /* 93.750 Hz */
-       DIGBEEP_HZ_MAX = 12000000,      /* 12 KHz */
-};
-
-/* generate or stop tone */
-static void generate_tone(struct hda_beep *beep, int tone)
-{
-       struct hda_codec *codec = beep->codec;
-
-       if (tone && !beep->playing) {
-               snd_hda_power_up(codec);
-               if (beep->power_hook)
-                       beep->power_hook(beep, true);
-               beep->playing = 1;
-       }
-       if (!codec->beep_just_power_on)
-               snd_hda_codec_write(codec, beep->nid, 0,
-                                   AC_VERB_SET_BEEP_CONTROL, tone);
-       if (!tone && beep->playing) {
-               beep->playing = 0;
-               if (beep->power_hook)
-                       beep->power_hook(beep, false);
-               snd_hda_power_down(codec);
-       }
-}
-
-static void snd_hda_generate_beep(struct work_struct *work)
-{
-       struct hda_beep *beep =
-               container_of(work, struct hda_beep, beep_work);
-
-       if (beep->enabled)
-               generate_tone(beep, beep->tone);
-}
-
-/* (non-standard) Linear beep tone calculation for IDT/STAC codecs 
- *
- * The tone frequency of beep generator on IDT/STAC codecs is
- * defined from the 8bit tone parameter, in Hz,
- *    freq = 48000 * (257 - tone) / 1024
- * that is from 12kHz to 93.75Hz in steps of 46.875 Hz
- */
-static int beep_linear_tone(struct hda_beep *beep, int hz)
-{
-       if (hz <= 0)
-               return 0;
-       hz *= 1000; /* fixed point */
-       hz = hz - DIGBEEP_HZ_MIN
-               + DIGBEEP_HZ_STEP / 2; /* round to nearest step */
-       if (hz < 0)
-               hz = 0; /* turn off PC beep*/
-       else if (hz >= (DIGBEEP_HZ_MAX - DIGBEEP_HZ_MIN))
-               hz = 1; /* max frequency */
-       else {
-               hz /= DIGBEEP_HZ_STEP;
-               hz = 255 - hz;
-       }
-       return hz;
-}
-
-/* HD-audio standard beep tone parameter calculation
- *
- * The tone frequency in Hz is calculated as
- *   freq = 48000 / (tone * 4)
- * from 47Hz to 12kHz
- */
-static int beep_standard_tone(struct hda_beep *beep, int hz)
-{
-       if (hz <= 0)
-               return 0; /* disabled */
-       hz = 12000 / hz;
-       if (hz > 0xff)
-               return 0xff;
-       if (hz <= 0)
-               return 1;
-       return hz;
-}
-
-static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
-                               unsigned int code, int hz)
-{
-       struct hda_beep *beep = input_get_drvdata(dev);
-
-       switch (code) {
-       case SND_BELL:
-               if (hz)
-                       hz = 1000;
-               fallthrough;
-       case SND_TONE:
-               if (beep->linear_tone)
-                       beep->tone = beep_linear_tone(beep, hz);
-               else
-                       beep->tone = beep_standard_tone(beep, hz);
-               break;
-       default:
-               return -1;
-       }
-
-       /* schedule beep event */
-       schedule_work(&beep->beep_work);
-       return 0;
-}
-
-static void turn_on_beep(struct hda_beep *beep)
-{
-       if (beep->keep_power_at_enable)
-               snd_hda_power_up_pm(beep->codec);
-}
-
-static void turn_off_beep(struct hda_beep *beep)
-{
-       cancel_work_sync(&beep->beep_work);
-       if (beep->playing) {
-               /* turn off beep */
-               generate_tone(beep, 0);
-       }
-       if (beep->keep_power_at_enable)
-               snd_hda_power_down_pm(beep->codec);
-}
-
-/**
- * snd_hda_enable_beep_device - Turn on/off beep sound
- * @codec: the HDA codec
- * @enable: flag to turn on/off
- */
-int snd_hda_enable_beep_device(struct hda_codec *codec, int enable)
-{
-       struct hda_beep *beep = codec->beep;
-       if (!beep)
-               return 0;
-       enable = !!enable;
-       if (beep->enabled != enable) {
-               beep->enabled = enable;
-               if (enable)
-                       turn_on_beep(beep);
-               else
-                       turn_off_beep(beep);
-               return 1;
-       }
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_enable_beep_device);
-
-static int beep_dev_register(struct snd_device *device)
-{
-       struct hda_beep *beep = device->device_data;
-       int err;
-
-       err = input_register_device(beep->dev);
-       if (!err)
-               beep->registered = true;
-       return err;
-}
-
-static int beep_dev_disconnect(struct snd_device *device)
-{
-       struct hda_beep *beep = device->device_data;
-
-       if (beep->registered)
-               input_unregister_device(beep->dev);
-       else
-               input_free_device(beep->dev);
-       if (beep->enabled)
-               turn_off_beep(beep);
-       return 0;
-}
-
-static int beep_dev_free(struct snd_device *device)
-{
-       struct hda_beep *beep = device->device_data;
-
-       beep->codec->beep = NULL;
-       kfree(beep);
-       return 0;
-}
-
-/**
- * snd_hda_attach_beep_device - Attach a beep input device
- * @codec: the HDA codec
- * @nid: beep NID
- *
- * Attach a beep object to the given widget.  If beep hint is turned off
- * explicitly or beep_mode of the codec is turned off, this doesn't nothing.
- *
- * Currently, only one beep device is allowed to each codec.
- */
-int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
-{
-       static const struct snd_device_ops ops = {
-               .dev_register = beep_dev_register,
-               .dev_disconnect = beep_dev_disconnect,
-               .dev_free = beep_dev_free,
-       };
-       struct input_dev *input_dev;
-       struct hda_beep *beep;
-       int err;
-
-       if (!codec->beep_just_power_on) {
-               if (!snd_hda_get_bool_hint(codec, "beep"))
-                       return 0; /* disabled explicitly by hints */
-               if (codec->beep_mode == HDA_BEEP_MODE_OFF)
-                       return 0; /* disabled by module option */
-       }
-
-       beep = kzalloc(sizeof(*beep), GFP_KERNEL);
-       if (beep == NULL)
-               return -ENOMEM;
-       snprintf(beep->phys, sizeof(beep->phys),
-               "card%d/codec#%d/beep0", codec->card->number, codec->addr);
-       /* enable linear scale */
-       snd_hda_codec_write_cache(codec, nid, 0,
-               AC_VERB_SET_DIGI_CONVERT_2, 0x01);
-
-       beep->nid = nid;
-       beep->codec = codec;
-       codec->beep = beep;
-
-       INIT_WORK(&beep->beep_work, &snd_hda_generate_beep);
-
-       input_dev = input_allocate_device();
-       if (!input_dev) {
-               err = -ENOMEM;
-               goto err_free;
-       }
-
-       /* setup digital beep device */
-       input_dev->name = "HDA Digital PCBeep";
-       input_dev->phys = beep->phys;
-       input_dev->id.bustype = BUS_PCI;
-       input_dev->dev.parent = &codec->card->card_dev;
-
-       input_dev->id.vendor = codec->core.vendor_id >> 16;
-       input_dev->id.product = codec->core.vendor_id & 0xffff;
-       input_dev->id.version = 0x01;
-
-       input_dev->evbit[0] = BIT_MASK(EV_SND);
-       input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
-       input_dev->event = snd_hda_beep_event;
-       input_set_drvdata(input_dev, beep);
-
-       beep->dev = input_dev;
-
-       err = snd_device_new(codec->card, SNDRV_DEV_JACK, beep, &ops);
-       if (err < 0)
-               goto err_input;
-
-       return 0;
-
- err_input:
-       input_free_device(beep->dev);
- err_free:
-       kfree(beep);
-       codec->beep = NULL;
-       return err;
-}
-EXPORT_SYMBOL_GPL(snd_hda_attach_beep_device);
-
-/**
- * snd_hda_detach_beep_device - Detach the beep device
- * @codec: the HDA codec
- */
-void snd_hda_detach_beep_device(struct hda_codec *codec)
-{
-       if (!codec->bus->shutdown && codec->beep)
-               snd_device_free(codec->card, codec->beep);
-}
-EXPORT_SYMBOL_GPL(snd_hda_detach_beep_device);
-
-static bool ctl_has_mute(struct snd_kcontrol *kcontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       return query_amp_caps(codec, get_amp_nid(kcontrol),
-                             get_amp_direction(kcontrol)) & AC_AMPCAP_MUTE;
-}
-
-/* get/put callbacks for beep mute mixer switches */
-
-/**
- * snd_hda_mixer_amp_switch_get_beep - Get callback for beep controls
- * @kcontrol: ctl element
- * @ucontrol: pointer to get/store the data
- */
-int snd_hda_mixer_amp_switch_get_beep(struct snd_kcontrol *kcontrol,
-                                     struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct hda_beep *beep = codec->beep;
-       int chs = get_amp_channels(kcontrol);
-
-       if (beep && (!beep->enabled || !ctl_has_mute(kcontrol))) {
-               if (chs & 1)
-                       ucontrol->value.integer.value[0] = beep->enabled;
-               if (chs & 2)
-                       ucontrol->value.integer.value[1] = beep->enabled;
-               return 0;
-       }
-       return snd_hda_mixer_amp_switch_get(kcontrol, ucontrol);
-}
-EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_get_beep);
-
-/**
- * snd_hda_mixer_amp_switch_put_beep - Put callback for beep controls
- * @kcontrol: ctl element
- * @ucontrol: pointer to get/store the data
- */
-int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
-                                     struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct hda_beep *beep = codec->beep;
-       if (beep) {
-               u8 chs = get_amp_channels(kcontrol);
-               int enable = 0;
-               long *valp = ucontrol->value.integer.value;
-               if (chs & 1) {
-                       enable |= *valp;
-                       valp++;
-               }
-               if (chs & 2)
-                       enable |= *valp;
-               snd_hda_enable_beep_device(codec, enable);
-       }
-       if (!ctl_has_mute(kcontrol))
-               return 0;
-       return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
-}
-EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_put_beep);
diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h
deleted file mode 100644 (file)
index 923ea86..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- * Digital Beep Input Interface for HD-audio codec
- *
- * Author: Matt Ranostay <matt.ranostay@konsulko.com>
- * Copyright (c) 2008 Embedded Alley Solutions Inc
- */
-
-#ifndef __SOUND_HDA_BEEP_H
-#define __SOUND_HDA_BEEP_H
-
-#include <sound/hda_codec.h>
-
-#define HDA_BEEP_MODE_OFF      0
-#define HDA_BEEP_MODE_ON       1
-
-/* beep information */
-struct hda_beep {
-       struct input_dev *dev;
-       struct hda_codec *codec;
-       char phys[32];
-       int tone;
-       hda_nid_t nid;
-       unsigned int registered:1;
-       unsigned int enabled:1;
-       unsigned int linear_tone:1;     /* linear tone for IDT/STAC codec */
-       unsigned int playing:1;
-       unsigned int keep_power_at_enable:1;    /* set by driver */
-       struct work_struct beep_work; /* scheduled task for beep event */
-       void (*power_hook)(struct hda_beep *beep, bool on);
-};
-
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-int snd_hda_enable_beep_device(struct hda_codec *codec, int enable);
-int snd_hda_attach_beep_device(struct hda_codec *codec, int nid);
-void snd_hda_detach_beep_device(struct hda_codec *codec);
-#else
-static inline int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
-{
-       return 0;
-}
-static inline void snd_hda_detach_beep_device(struct hda_codec *codec)
-{
-}
-#endif
-#endif
diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c
deleted file mode 100644 (file)
index df8f88b..0000000
+++ /dev/null
@@ -1,343 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * HD-audio codec driver binding
- * Copyright (c) Takashi Iwai <tiwai@suse.de>
- */
-
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/mutex.h>
-#include <linux/module.h>
-#include <linux/export.h>
-#include <linux/pm.h>
-#include <sound/core.h>
-#include <sound/hda_codec.h>
-#include "hda_local.h"
-#include "hda_jack.h"
-
-/*
- * find a matching codec id
- */
-static int hda_codec_match(struct hdac_device *dev, const struct hdac_driver *drv)
-{
-       struct hda_codec *codec = container_of(dev, struct hda_codec, core);
-       const struct hda_codec_driver *driver =
-               container_of(drv, struct hda_codec_driver, core);
-       const struct hda_device_id *list;
-       /* check probe_id instead of vendor_id if set */
-       u32 id = codec->probe_id ? codec->probe_id : codec->core.vendor_id;
-       u32 rev_id = codec->core.revision_id;
-
-       for (list = driver->id; list->vendor_id; list++) {
-               if (list->vendor_id == id &&
-                   (!list->rev_id || list->rev_id == rev_id)) {
-                       codec->preset = list;
-                       return 1;
-               }
-       }
-       return 0;
-}
-
-/* process an unsolicited event */
-static void hda_codec_unsol_event(struct hdac_device *dev, unsigned int ev)
-{
-       struct hda_codec *codec = container_of(dev, struct hda_codec, core);
-
-       /* ignore unsol events during shutdown */
-       if (codec->card->shutdown || codec->bus->shutdown)
-               return;
-
-       /* ignore unsol events during system suspend/resume */
-       if (codec->core.dev.power.power_state.event != PM_EVENT_ON)
-               return;
-
-       if (codec->patch_ops.unsol_event)
-               codec->patch_ops.unsol_event(codec, ev);
-}
-
-/**
- * snd_hda_codec_set_name - set the codec name
- * @codec: the HDA codec
- * @name: name string to set
- */
-int snd_hda_codec_set_name(struct hda_codec *codec, const char *name)
-{
-       int err;
-
-       if (!name)
-               return 0;
-       err = snd_hdac_device_set_chip_name(&codec->core, name);
-       if (err < 0)
-               return err;
-
-       /* update the mixer name */
-       if (!*codec->card->mixername ||
-           codec->bus->mixer_assigned >= codec->core.addr) {
-               snprintf(codec->card->mixername,
-                        sizeof(codec->card->mixername), "%s %s",
-                        codec->core.vendor_name, codec->core.chip_name);
-               codec->bus->mixer_assigned = codec->core.addr;
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_set_name);
-
-static int hda_codec_driver_probe(struct device *dev)
-{
-       struct hda_codec *codec = dev_to_hda_codec(dev);
-       struct module *owner = dev->driver->owner;
-       hda_codec_patch_t patch;
-       int err;
-
-       if (codec->bus->core.ext_ops) {
-               if (WARN_ON(!codec->bus->core.ext_ops->hdev_attach))
-                       return -EINVAL;
-               return codec->bus->core.ext_ops->hdev_attach(&codec->core);
-       }
-
-       if (WARN_ON(!codec->preset))
-               return -EINVAL;
-
-       err = snd_hda_codec_set_name(codec, codec->preset->name);
-       if (err < 0)
-               goto error;
-       err = snd_hdac_regmap_init(&codec->core);
-       if (err < 0)
-               goto error;
-
-       if (!try_module_get(owner)) {
-               err = -EINVAL;
-               goto error;
-       }
-
-       patch = (hda_codec_patch_t)codec->preset->driver_data;
-       if (patch) {
-               err = patch(codec);
-               if (err < 0)
-                       goto error_module_put;
-       }
-
-       err = snd_hda_codec_build_pcms(codec);
-       if (err < 0)
-               goto error_module;
-       err = snd_hda_codec_build_controls(codec);
-       if (err < 0)
-               goto error_module;
-       /* only register after the bus probe finished; otherwise it's racy */
-       if (!codec->bus->bus_probing && codec->card->registered) {
-               err = snd_card_register(codec->card);
-               if (err < 0)
-                       goto error_module;
-               snd_hda_codec_register(codec);
-       }
-
-       codec->core.lazy_cache = true;
-       return 0;
-
- error_module:
-       if (codec->patch_ops.free)
-               codec->patch_ops.free(codec);
- error_module_put:
-       module_put(owner);
-
- error:
-       snd_hda_codec_cleanup_for_unbind(codec);
-       codec->preset = NULL;
-       return err;
-}
-
-static int hda_codec_driver_remove(struct device *dev)
-{
-       struct hda_codec *codec = dev_to_hda_codec(dev);
-
-       if (codec->bus->core.ext_ops) {
-               if (WARN_ON(!codec->bus->core.ext_ops->hdev_detach))
-                       return -EINVAL;
-               return codec->bus->core.ext_ops->hdev_detach(&codec->core);
-       }
-
-       snd_hda_codec_disconnect_pcms(codec);
-       snd_hda_jack_tbl_disconnect(codec);
-       if (!refcount_dec_and_test(&codec->pcm_ref))
-               wait_event(codec->remove_sleep, !refcount_read(&codec->pcm_ref));
-       snd_power_sync_ref(codec->bus->card);
-
-       if (codec->patch_ops.free)
-               codec->patch_ops.free(codec);
-       snd_hda_codec_cleanup_for_unbind(codec);
-       codec->preset = NULL;
-       module_put(dev->driver->owner);
-       return 0;
-}
-
-static void hda_codec_driver_shutdown(struct device *dev)
-{
-       snd_hda_codec_shutdown(dev_to_hda_codec(dev));
-}
-
-int __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name,
-                              struct module *owner)
-{
-       drv->core.driver.name = name;
-       drv->core.driver.owner = owner;
-       drv->core.driver.bus = &snd_hda_bus_type;
-       drv->core.driver.probe = hda_codec_driver_probe;
-       drv->core.driver.remove = hda_codec_driver_remove;
-       drv->core.driver.shutdown = hda_codec_driver_shutdown;
-       drv->core.driver.pm = pm_ptr(&hda_codec_driver_pm);
-       drv->core.type = HDA_DEV_LEGACY;
-       drv->core.match = hda_codec_match;
-       drv->core.unsol_event = hda_codec_unsol_event;
-       return driver_register(&drv->core.driver);
-}
-EXPORT_SYMBOL_GPL(__hda_codec_driver_register);
-
-void hda_codec_driver_unregister(struct hda_codec_driver *drv)
-{
-       driver_unregister(&drv->core.driver);
-}
-EXPORT_SYMBOL_GPL(hda_codec_driver_unregister);
-
-static inline bool codec_probed(struct hda_codec *codec)
-{
-       return device_attach(hda_codec_dev(codec)) > 0 && codec->preset;
-}
-
-/* try to auto-load codec module */
-static void request_codec_module(struct hda_codec *codec)
-{
-#ifdef MODULE
-       char modalias[32];
-       const char *mod = NULL;
-
-       switch (codec->probe_id) {
-       case HDA_CODEC_ID_GENERIC_HDMI:
-#if IS_MODULE(CONFIG_SND_HDA_CODEC_HDMI)
-               mod = "snd-hda-codec-hdmi";
-#endif
-               break;
-       case HDA_CODEC_ID_GENERIC:
-#if IS_MODULE(CONFIG_SND_HDA_GENERIC)
-               mod = "snd-hda-codec-generic";
-#endif
-               break;
-       default:
-               snd_hdac_codec_modalias(&codec->core, modalias, sizeof(modalias));
-               mod = modalias;
-               break;
-       }
-
-       if (mod)
-               request_module(mod);
-#endif /* MODULE */
-}
-
-/* try to auto-load and bind the codec module */
-static void codec_bind_module(struct hda_codec *codec)
-{
-#ifdef MODULE
-       request_codec_module(codec);
-       if (codec_probed(codec))
-               return;
-#endif
-}
-
-#if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI)
-/* if all audio out widgets are digital, let's assume the codec as a HDMI/DP */
-static bool is_likely_hdmi_codec(struct hda_codec *codec)
-{
-       hda_nid_t nid;
-
-       /*
-        * For ASoC users, if snd_hda_hdmi_codec module is denylisted and any
-        * event causes i915 enumeration to fail, ->wcaps remains uninitialized.
-        */
-       if (!codec->wcaps)
-               return true;
-
-       for_each_hda_codec_node(nid, codec) {
-               unsigned int wcaps = get_wcaps(codec, nid);
-               switch (get_wcaps_type(wcaps)) {
-               case AC_WID_AUD_IN:
-                       return false; /* HDMI parser supports only HDMI out */
-               case AC_WID_AUD_OUT:
-                       if (!(wcaps & AC_WCAP_DIGITAL))
-                               return false;
-                       break;
-               }
-       }
-       return true;
-}
-#else
-/* no HDMI codec parser support */
-#define is_likely_hdmi_codec(codec)    false
-#endif /* CONFIG_SND_HDA_CODEC_HDMI */
-
-static int codec_bind_generic(struct hda_codec *codec)
-{
-       if (codec->probe_id)
-               return -ENODEV;
-
-       if (is_likely_hdmi_codec(codec)) {
-               codec->probe_id = HDA_CODEC_ID_GENERIC_HDMI;
-               request_codec_module(codec);
-               if (codec_probed(codec))
-                       return 0;
-       }
-
-       codec->probe_id = HDA_CODEC_ID_GENERIC;
-       request_codec_module(codec);
-       if (codec_probed(codec))
-               return 0;
-       return -ENODEV;
-}
-
-#if IS_ENABLED(CONFIG_SND_HDA_GENERIC)
-#define is_generic_config(codec) \
-       (codec->modelname && !strcmp(codec->modelname, "generic"))
-#else
-#define is_generic_config(codec)       0
-#endif
-
-/**
- * snd_hda_codec_configure - (Re-)configure the HD-audio codec
- * @codec: the HDA codec
- *
- * Start parsing of the given codec tree and (re-)initialize the whole
- * patch instance.
- *
- * Returns 0 if successful or a negative error code.
- */
-int snd_hda_codec_configure(struct hda_codec *codec)
-{
-       int err;
-
-       if (codec->configured)
-               return 0;
-
-       if (is_generic_config(codec))
-               codec->probe_id = HDA_CODEC_ID_GENERIC;
-       else
-               codec->probe_id = 0;
-
-       if (!device_is_registered(&codec->core.dev)) {
-               err = snd_hdac_device_register(&codec->core);
-               if (err < 0)
-                       return err;
-       }
-
-       if (!codec->preset)
-               codec_bind_module(codec);
-       if (!codec->preset) {
-               err = codec_bind_generic(codec);
-               if (err < 0) {
-                       codec_dbg(codec, "Unable to bind the codec\n");
-                       return err;
-               }
-       }
-
-       codec->configured = 1;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_configure);
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
deleted file mode 100644 (file)
index cb72e96..0000000
+++ /dev/null
@@ -1,4049 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Universal Interface for Intel High Definition Audio Codec
- *
- * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- */
-
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/mutex.h>
-#include <linux/module.h>
-#include <linux/pm.h>
-#include <linux/pm_runtime.h>
-#include <sound/core.h>
-#include <sound/hda_codec.h>
-#include <sound/asoundef.h>
-#include <sound/tlv.h>
-#include <sound/initval.h>
-#include <sound/jack.h>
-#include "hda_local.h"
-#include "hda_beep.h"
-#include "hda_jack.h"
-#include <sound/hda_hwdep.h>
-#include <sound/hda_component.h>
-
-#define codec_in_pm(codec)             snd_hdac_is_in_pm(&codec->core)
-#define hda_codec_is_power_on(codec)   snd_hdac_is_power_on(&codec->core)
-#define codec_has_epss(codec) \
-       ((codec)->core.power_caps & AC_PWRST_EPSS)
-#define codec_has_clkstop(codec) \
-       ((codec)->core.power_caps & AC_PWRST_CLKSTOP)
-
-/*
- * Send and receive a verb - passed to exec_verb override for hdac_device
- */
-static int codec_exec_verb(struct hdac_device *dev, unsigned int cmd,
-                          unsigned int flags, unsigned int *res)
-{
-       struct hda_codec *codec = container_of(dev, struct hda_codec, core);
-       struct hda_bus *bus = codec->bus;
-       int err;
-
-       if (cmd == ~0)
-               return -1;
-
- again:
-       snd_hda_power_up_pm(codec);
-       mutex_lock(&bus->core.cmd_mutex);
-       if (flags & HDA_RW_NO_RESPONSE_FALLBACK)
-               bus->no_response_fallback = 1;
-       err = snd_hdac_bus_exec_verb_unlocked(&bus->core, codec->core.addr,
-                                             cmd, res);
-       bus->no_response_fallback = 0;
-       mutex_unlock(&bus->core.cmd_mutex);
-       snd_hda_power_down_pm(codec);
-       if (!codec_in_pm(codec) && res && err == -EAGAIN) {
-               if (bus->response_reset) {
-                       codec_dbg(codec,
-                                 "resetting BUS due to fatal communication error\n");
-                       snd_hda_bus_reset(bus);
-               }
-               goto again;
-       }
-       /* clear reset-flag when the communication gets recovered */
-       if (!err || codec_in_pm(codec))
-               bus->response_reset = 0;
-       return err;
-}
-
-/**
- * snd_hda_sequence_write - sequence writes
- * @codec: the HDA codec
- * @seq: VERB array to send
- *
- * Send the commands sequentially from the given array.
- * The array must be terminated with NID=0.
- */
-void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq)
-{
-       for (; seq->nid; seq++)
-               snd_hda_codec_write(codec, seq->nid, 0, seq->verb, seq->param);
-}
-EXPORT_SYMBOL_GPL(snd_hda_sequence_write);
-
-/* connection list element */
-struct hda_conn_list {
-       struct list_head list;
-       int len;
-       hda_nid_t nid;
-       hda_nid_t conns[] __counted_by(len);
-};
-
-/* look up the cached results */
-static struct hda_conn_list *
-lookup_conn_list(struct hda_codec *codec, hda_nid_t nid)
-{
-       struct hda_conn_list *p;
-       list_for_each_entry(p, &codec->conn_list, list) {
-               if (p->nid == nid)
-                       return p;
-       }
-       return NULL;
-}
-
-static int add_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
-                        const hda_nid_t *list)
-{
-       struct hda_conn_list *p;
-
-       p = kmalloc(struct_size(p, conns, len), GFP_KERNEL);
-       if (!p)
-               return -ENOMEM;
-       p->len = len;
-       p->nid = nid;
-       memcpy(p->conns, list, len * sizeof(hda_nid_t));
-       list_add(&p->list, &codec->conn_list);
-       return 0;
-}
-
-static void remove_conn_list(struct hda_codec *codec)
-{
-       while (!list_empty(&codec->conn_list)) {
-               struct hda_conn_list *p;
-               p = list_first_entry(&codec->conn_list, typeof(*p), list);
-               list_del(&p->list);
-               kfree(p);
-       }
-}
-
-/* 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[32];
-       hda_nid_t *result = list;
-       int len;
-
-       len = snd_hda_get_raw_connections(codec, nid, list, ARRAY_SIZE(list));
-       if (len == -ENOSPC) {
-               len = snd_hda_get_num_raw_conns(codec, nid);
-               result = kmalloc_array(len, sizeof(hda_nid_t), GFP_KERNEL);
-               if (!result)
-                       return -ENOMEM;
-               len = snd_hda_get_raw_connections(codec, nid, result, len);
-       }
-       if (len >= 0)
-               len = snd_hda_override_conn_list(codec, nid, len, result);
-       if (result != list)
-               kfree(result);
-       return len;
-}
-
-/**
- * snd_hda_get_conn_list - get connection list
- * @codec: the HDA codec
- * @nid: NID to parse
- * @listp: the pointer to store NID list
- *
- * Parses the connection list of the given widget and stores the pointer
- * to the list of NIDs.
- *
- * Returns the number of connections, or a negative error code.
- *
- * Note that the returned pointer isn't protected against the list
- * modification.  If snd_hda_override_conn_list() might be called
- * concurrently, protect with a mutex appropriately.
- */
-int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
-                         const hda_nid_t **listp)
-{
-       bool added = false;
-
-       for (;;) {
-               int err;
-               const struct hda_conn_list *p;
-
-               /* if the connection-list is already cached, read it */
-               p = lookup_conn_list(codec, nid);
-               if (p) {
-                       if (listp)
-                               *listp = p->conns;
-                       return p->len;
-               }
-               if (snd_BUG_ON(added))
-                       return -EINVAL;
-
-               err = read_and_add_raw_conns(codec, nid);
-               if (err < 0)
-                       return err;
-               added = true;
-       }
-}
-EXPORT_SYMBOL_GPL(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; 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_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 && conn_list) {
-               if (len > max_conns) {
-                       codec_err(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_GPL(snd_hda_get_connections);
-
-/**
- * snd_hda_override_conn_list - add/modify the connection-list to cache
- * @codec: the HDA codec
- * @nid: NID to parse
- * @len: number of connection list entries
- * @list: the list of connection entries
- *
- * Add or modify the given connection-list to the cache.  If the corresponding
- * cache already exists, invalidate it and append a new one.
- *
- * Returns zero or a negative error code.
- */
-int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
-                              const hda_nid_t *list)
-{
-       struct hda_conn_list *p;
-
-       p = lookup_conn_list(codec, nid);
-       if (p) {
-               list_del(&p->list);
-               kfree(p);
-       }
-
-       return add_conn_list(codec, nid, len, list);
-}
-EXPORT_SYMBOL_GPL(snd_hda_override_conn_list);
-
-/**
- * snd_hda_get_conn_index - get the connection index of the given NID
- * @codec: the HDA codec
- * @mux: NID containing the list
- * @nid: NID to select
- * @recursive: 1 when searching NID recursively, otherwise 0
- *
- * Parses the connection list of the widget @mux and checks whether the
- * widget @nid is present.  If it is, return the connection index.
- * Otherwise it returns -1.
- */
-int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
-                          hda_nid_t nid, int recursive)
-{
-       const hda_nid_t *conn;
-       int i, nums;
-
-       nums = snd_hda_get_conn_list(codec, mux, &conn);
-       for (i = 0; i < nums; i++)
-               if (conn[i] == nid)
-                       return i;
-       if (!recursive)
-               return -1;
-       if (recursive > 10) {
-               codec_dbg(codec, "too deep connection for 0x%x\n", nid);
-               return -1;
-       }
-       recursive++;
-       for (i = 0; i < nums; i++) {
-               unsigned int type = get_wcaps_type(get_wcaps(codec, conn[i]));
-               if (type == AC_WID_PIN || type == AC_WID_AUD_OUT)
-                       continue;
-               if (snd_hda_get_conn_index(codec, conn[i], nid, recursive) >= 0)
-                       return i;
-       }
-       return -1;
-}
-EXPORT_SYMBOL_GPL(snd_hda_get_conn_index);
-
-/**
- * snd_hda_get_num_devices - get DEVLIST_LEN parameter of the given widget
- *  @codec: the HDA codec
- *  @nid: NID of the pin to parse
- *
- * Get the device entry number on the given widget. This is a feature of
- * DP MST audio. Each pin can have several device entries in it.
- */
-unsigned int snd_hda_get_num_devices(struct hda_codec *codec, hda_nid_t nid)
-{
-       unsigned int wcaps = get_wcaps(codec, nid);
-       unsigned int parm;
-
-       if (!codec->dp_mst || !(wcaps & AC_WCAP_DIGITAL) ||
-           get_wcaps_type(wcaps) != AC_WID_PIN)
-               return 0;
-
-       parm = snd_hdac_read_parm_uncached(&codec->core, nid, AC_PAR_DEVLIST_LEN);
-       if (parm == -1)
-               parm = 0;
-       return parm & AC_DEV_LIST_LEN_MASK;
-}
-EXPORT_SYMBOL_GPL(snd_hda_get_num_devices);
-
-/**
- * snd_hda_get_devices - copy device list without cache
- * @codec: the HDA codec
- * @nid: NID of the pin to parse
- * @dev_list: device list array
- * @max_devices: max. number of devices to store
- *
- * Copy the device list. This info is dynamic and so not cached.
- * Currently called only from hda_proc.c, so not exported.
- */
-int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid,
-                       u8 *dev_list, int max_devices)
-{
-       unsigned int parm;
-       int i, dev_len, devices;
-
-       parm = snd_hda_get_num_devices(codec, nid);
-       if (!parm)      /* not multi-stream capable */
-               return 0;
-
-       dev_len = parm + 1;
-       dev_len = dev_len < max_devices ? dev_len : max_devices;
-
-       devices = 0;
-       while (devices < dev_len) {
-               if (snd_hdac_read(&codec->core, nid,
-                                 AC_VERB_GET_DEVICE_LIST, devices, &parm))
-                       break; /* error */
-
-               for (i = 0; i < 8; i++) {
-                       dev_list[devices] = (u8)parm;
-                       parm >>= 4;
-                       devices++;
-                       if (devices >= dev_len)
-                               break;
-               }
-       }
-       return devices;
-}
-
-/**
- * snd_hda_get_dev_select - get device entry select on the pin
- * @codec: the HDA codec
- * @nid: NID of the pin to get device entry select
- *
- * Get the devcie entry select on the pin. Return the device entry
- * id selected on the pin. Return 0 means the first device entry
- * is selected or MST is not supported.
- */
-int snd_hda_get_dev_select(struct hda_codec *codec, hda_nid_t nid)
-{
-       /* not support dp_mst will always return 0, using first dev_entry */
-       if (!codec->dp_mst)
-               return 0;
-
-       return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DEVICE_SEL, 0);
-}
-EXPORT_SYMBOL_GPL(snd_hda_get_dev_select);
-
-/**
- * snd_hda_set_dev_select - set device entry select on the pin
- * @codec: the HDA codec
- * @nid: NID of the pin to set device entry select
- * @dev_id: device entry id to be set
- *
- * Set the device entry select on the pin nid.
- */
-int snd_hda_set_dev_select(struct hda_codec *codec, hda_nid_t nid, int dev_id)
-{
-       int ret, num_devices;
-
-       /* not support dp_mst will always return 0, using first dev_entry */
-       if (!codec->dp_mst)
-               return 0;
-
-       /* AC_PAR_DEVLIST_LEN is 0 based. */
-       num_devices = snd_hda_get_num_devices(codec, nid) + 1;
-       /* If Device List Length is 0 (num_device = 1),
-        * the pin is not multi stream capable.
-        * Do nothing in this case.
-        */
-       if (num_devices == 1)
-               return 0;
-
-       /* Behavior of setting index being equal to or greater than
-        * Device List Length is not predictable
-        */
-       if (num_devices <= dev_id)
-               return -EINVAL;
-
-       ret = snd_hda_codec_write(codec, nid, 0,
-                       AC_VERB_SET_DEVICE_SEL, dev_id);
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(snd_hda_set_dev_select);
-
-/*
- * read widget caps for each widget and store in cache
- */
-static int read_widget_caps(struct hda_codec *codec, hda_nid_t fg_node)
-{
-       int i;
-       hda_nid_t nid;
-
-       codec->wcaps = kmalloc_array(codec->core.num_nodes, 4, GFP_KERNEL);
-       if (!codec->wcaps)
-               return -ENOMEM;
-       nid = codec->core.start_nid;
-       for (i = 0; i < codec->core.num_nodes; i++, nid++)
-               codec->wcaps[i] = snd_hdac_read_parm_uncached(&codec->core,
-                                       nid, AC_PAR_AUDIO_WIDGET_CAP);
-       return 0;
-}
-
-/* read all pin default configurations and save codec->init_pins */
-static int read_pin_defaults(struct hda_codec *codec)
-{
-       hda_nid_t nid;
-
-       for_each_hda_codec_node(nid, codec) {
-               struct hda_pincfg *pin;
-               unsigned int wcaps = get_wcaps(codec, nid);
-               unsigned int wid_type = get_wcaps_type(wcaps);
-               if (wid_type != AC_WID_PIN)
-                       continue;
-               pin = snd_array_new(&codec->init_pins);
-               if (!pin)
-                       return -ENOMEM;
-               pin->nid = nid;
-               pin->cfg = snd_hda_codec_read(codec, nid, 0,
-                                             AC_VERB_GET_CONFIG_DEFAULT, 0);
-               /*
-                * all device entries are the same widget control so far
-                * fixme: if any codec is different, need fix here
-                */
-               pin->ctrl = snd_hda_codec_read(codec, nid, 0,
-                                              AC_VERB_GET_PIN_WIDGET_CONTROL,
-                                              0);
-       }
-       return 0;
-}
-
-/* look up the given pin config list and return the item matching with NID */
-static struct hda_pincfg *look_up_pincfg(struct hda_codec *codec,
-                                        struct snd_array *array,
-                                        hda_nid_t nid)
-{
-       struct hda_pincfg *pin;
-       int i;
-
-       snd_array_for_each(array, i, pin) {
-               if (pin->nid == nid)
-                       return pin;
-       }
-       return NULL;
-}
-
-/* set the current pin config value for the given NID.
- * the value is cached, and read via snd_hda_codec_get_pincfg()
- */
-int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list,
-                      hda_nid_t nid, unsigned int cfg)
-{
-       struct hda_pincfg *pin;
-
-       pin = look_up_pincfg(codec, list, nid);
-       if (!pin) {
-               pin = snd_array_new(list);
-               if (!pin)
-                       return -ENOMEM;
-               pin->nid = nid;
-       }
-       pin->cfg = cfg;
-       return 0;
-}
-
-/**
- * snd_hda_codec_set_pincfg - Override a pin default configuration
- * @codec: the HDA codec
- * @nid: NID to set the pin config
- * @cfg: the pin default config value
- *
- * Override a pin default configuration value in the cache.
- * This value can be read by snd_hda_codec_get_pincfg() in a higher
- * priority than the real hardware value.
- */
-int snd_hda_codec_set_pincfg(struct hda_codec *codec,
-                            hda_nid_t nid, unsigned int cfg)
-{
-       return snd_hda_add_pincfg(codec, &codec->driver_pins, nid, cfg);
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_set_pincfg);
-
-/**
- * snd_hda_codec_get_pincfg - Obtain a pin-default configuration
- * @codec: the HDA codec
- * @nid: NID to get the pin config
- *
- * Get the current pin config value of the given pin NID.
- * If the pincfg value is cached or overridden via sysfs or driver,
- * returns the cached value.
- */
-unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid)
-{
-       struct hda_pincfg *pin;
-
-#ifdef CONFIG_SND_HDA_RECONFIG
-       {
-               unsigned int cfg = 0;
-               mutex_lock(&codec->user_mutex);
-               pin = look_up_pincfg(codec, &codec->user_pins, nid);
-               if (pin)
-                       cfg = pin->cfg;
-               mutex_unlock(&codec->user_mutex);
-               if (cfg)
-                       return cfg;
-       }
-#endif
-       pin = look_up_pincfg(codec, &codec->driver_pins, nid);
-       if (pin)
-               return pin->cfg;
-       pin = look_up_pincfg(codec, &codec->init_pins, nid);
-       if (pin)
-               return pin->cfg;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_get_pincfg);
-
-/**
- * snd_hda_codec_set_pin_target - remember the current pinctl target value
- * @codec: the HDA codec
- * @nid: pin NID
- * @val: assigned pinctl value
- *
- * This function stores the given value to a pinctl target value in the
- * pincfg table.  This isn't always as same as the actually written value
- * but can be referred at any time via snd_hda_codec_get_pin_target().
- */
-int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid,
-                                unsigned int val)
-{
-       struct hda_pincfg *pin;
-
-       pin = look_up_pincfg(codec, &codec->init_pins, nid);
-       if (!pin)
-               return -EINVAL;
-       pin->target = val;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_set_pin_target);
-
-/**
- * snd_hda_codec_get_pin_target - return the current pinctl target value
- * @codec: the HDA codec
- * @nid: pin NID
- */
-int snd_hda_codec_get_pin_target(struct hda_codec *codec, hda_nid_t nid)
-{
-       struct hda_pincfg *pin;
-
-       pin = look_up_pincfg(codec, &codec->init_pins, nid);
-       if (!pin)
-               return 0;
-       return pin->target;
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_get_pin_target);
-
-/**
- * snd_hda_shutup_pins - Shut up all pins
- * @codec: the HDA codec
- *
- * Clear all pin controls to shup up before suspend for avoiding click noise.
- * The controls aren't cached so that they can be resumed properly.
- */
-void snd_hda_shutup_pins(struct hda_codec *codec)
-{
-       const struct hda_pincfg *pin;
-       int i;
-
-       /* don't shut up pins when unloading the driver; otherwise it breaks
-        * the default pin setup at the next load of the driver
-        */
-       if (codec->bus->shutdown)
-               return;
-       snd_array_for_each(&codec->init_pins, i, pin) {
-               /* use read here for syncing after issuing each verb */
-               snd_hda_codec_read(codec, pin->nid, 0,
-                                  AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
-       }
-       codec->pins_shutup = 1;
-}
-EXPORT_SYMBOL_GPL(snd_hda_shutup_pins);
-
-/* Restore the pin controls cleared previously via snd_hda_shutup_pins() */
-static void restore_shutup_pins(struct hda_codec *codec)
-{
-       const struct hda_pincfg *pin;
-       int i;
-
-       if (!codec->pins_shutup)
-               return;
-       if (codec->bus->shutdown)
-               return;
-       snd_array_for_each(&codec->init_pins, i, pin) {
-               snd_hda_codec_write(codec, pin->nid, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                   pin->ctrl);
-       }
-       codec->pins_shutup = 0;
-}
-
-static void hda_jackpoll_work(struct work_struct *work)
-{
-       struct hda_codec *codec =
-               container_of(work, struct hda_codec, jackpoll_work.work);
-
-       if (!codec->jackpoll_interval)
-               return;
-
-       /* the power-up/down sequence triggers the runtime resume */
-       snd_hda_power_up(codec);
-       /* update jacks manually if polling is required, too */
-       snd_hda_jack_set_dirty_all(codec);
-       snd_hda_jack_poll_all(codec);
-       schedule_delayed_work(&codec->jackpoll_work, codec->jackpoll_interval);
-       snd_hda_power_down(codec);
-}
-
-/* release all pincfg lists */
-static void free_init_pincfgs(struct hda_codec *codec)
-{
-       snd_array_free(&codec->driver_pins);
-#ifdef CONFIG_SND_HDA_RECONFIG
-       snd_array_free(&codec->user_pins);
-#endif
-       snd_array_free(&codec->init_pins);
-}
-
-/*
- * audio-converter setup caches
- */
-struct hda_cvt_setup {
-       hda_nid_t nid;
-       u8 stream_tag;
-       u8 channel_id;
-       u16 format_id;
-       unsigned char active;   /* cvt is currently used */
-       unsigned char dirty;    /* setups should be cleared */
-};
-
-/* get or create a cache entry for the given audio converter NID */
-static struct hda_cvt_setup *
-get_hda_cvt_setup(struct hda_codec *codec, hda_nid_t nid)
-{
-       struct hda_cvt_setup *p;
-       int i;
-
-       snd_array_for_each(&codec->cvt_setups, i, p) {
-               if (p->nid == nid)
-                       return p;
-       }
-       p = snd_array_new(&codec->cvt_setups);
-       if (p)
-               p->nid = nid;
-       return p;
-}
-
-/*
- * PCM device
- */
-void snd_hda_codec_pcm_put(struct hda_pcm *pcm)
-{
-       if (refcount_dec_and_test(&pcm->codec->pcm_ref))
-               wake_up(&pcm->codec->remove_sleep);
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_put);
-
-struct hda_pcm *snd_hda_codec_pcm_new(struct hda_codec *codec,
-                                     const char *fmt, ...)
-{
-       struct hda_pcm *pcm;
-       va_list args;
-
-       pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
-       if (!pcm)
-               return NULL;
-
-       pcm->codec = codec;
-       va_start(args, fmt);
-       pcm->name = kvasprintf(GFP_KERNEL, fmt, args);
-       va_end(args);
-       if (!pcm->name) {
-               kfree(pcm);
-               return NULL;
-       }
-
-       list_add_tail(&pcm->list, &codec->pcm_list_head);
-       refcount_inc(&codec->pcm_ref);
-       return pcm;
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_new);
-
-/*
- * codec destructor
- */
-void snd_hda_codec_disconnect_pcms(struct hda_codec *codec)
-{
-       struct hda_pcm *pcm;
-
-       list_for_each_entry(pcm, &codec->pcm_list_head, list) {
-               if (pcm->disconnected)
-                       continue;
-               if (pcm->pcm)
-                       snd_device_disconnect(codec->card, pcm->pcm);
-               snd_hda_codec_pcm_put(pcm);
-               pcm->disconnected = 1;
-       }
-}
-
-static void codec_release_pcms(struct hda_codec *codec)
-{
-       struct hda_pcm *pcm, *n;
-
-       list_for_each_entry_safe(pcm, n, &codec->pcm_list_head, list) {
-               list_del(&pcm->list);
-               if (pcm->pcm)
-                       snd_device_free(pcm->codec->card, pcm->pcm);
-               clear_bit(pcm->device, pcm->codec->bus->pcm_dev_bits);
-               kfree(pcm->name);
-               kfree(pcm);
-       }
-}
-
-/**
- * snd_hda_codec_cleanup_for_unbind - Prepare codec for removal
- * @codec: codec device to cleanup
- */
-void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec)
-{
-       if (codec->core.registered) {
-               /* pm_runtime_put() is called in snd_hdac_device_exit() */
-               pm_runtime_get_noresume(hda_codec_dev(codec));
-               pm_runtime_disable(hda_codec_dev(codec));
-               codec->core.registered = 0;
-       }
-
-       snd_hda_codec_disconnect_pcms(codec);
-       cancel_delayed_work_sync(&codec->jackpoll_work);
-       if (!codec->in_freeing)
-               snd_hda_ctls_clear(codec);
-       codec_release_pcms(codec);
-       snd_hda_detach_beep_device(codec);
-       memset(&codec->patch_ops, 0, sizeof(codec->patch_ops));
-       snd_hda_jack_tbl_clear(codec);
-       codec->proc_widget_hook = NULL;
-       codec->spec = NULL;
-
-       /* free only driver_pins so that init_pins + user_pins are restored */
-       snd_array_free(&codec->driver_pins);
-       snd_array_free(&codec->cvt_setups);
-       snd_array_free(&codec->spdif_out);
-       snd_array_free(&codec->verbs);
-       codec->follower_dig_outs = NULL;
-       codec->spdif_status_reset = 0;
-       snd_array_free(&codec->mixers);
-       snd_array_free(&codec->nids);
-       remove_conn_list(codec);
-       snd_hdac_regmap_exit(&codec->core);
-       codec->configured = 0;
-       refcount_set(&codec->pcm_ref, 1); /* reset refcount */
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_cleanup_for_unbind);
-
-static unsigned int hda_set_power_state(struct hda_codec *codec,
-                               unsigned int power_state);
-
-/* enable/disable display power per codec */
-void snd_hda_codec_display_power(struct hda_codec *codec, bool enable)
-{
-       if (codec->display_power_control)
-               snd_hdac_display_power(&codec->bus->core, codec->addr, enable);
-}
-
-/**
- * snd_hda_codec_register - Finalize codec initialization
- * @codec: codec device to register
- *
- * Also called from hda_bind.c
- */
-void snd_hda_codec_register(struct hda_codec *codec)
-{
-       if (codec->core.registered)
-               return;
-       if (device_is_registered(hda_codec_dev(codec))) {
-               snd_hda_codec_display_power(codec, true);
-               pm_runtime_enable(hda_codec_dev(codec));
-               /* it was powered up in snd_hda_codec_new(), now all done */
-               snd_hda_power_down(codec);
-               codec->core.registered = 1;
-       }
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_register);
-
-static int snd_hda_codec_dev_register(struct snd_device *device)
-{
-       snd_hda_codec_register(device->device_data);
-       return 0;
-}
-
-/**
- * snd_hda_codec_unregister - Unregister specified codec device
- * @codec: codec device to unregister
- */
-void snd_hda_codec_unregister(struct hda_codec *codec)
-{
-       codec->in_freeing = 1;
-       /*
-        * snd_hda_codec_device_new() is used by legacy HDA and ASoC driver.
-        * We can't unregister ASoC device since it will be unregistered in
-        * snd_hdac_ext_bus_device_remove().
-        */
-       if (codec->core.type == HDA_DEV_LEGACY)
-               snd_hdac_device_unregister(&codec->core);
-       snd_hda_codec_display_power(codec, false);
-
-       /*
-        * In the case of ASoC HD-audio bus, the device refcount is released in
-        * snd_hdac_ext_bus_device_remove() explicitly.
-        */
-       if (codec->core.type == HDA_DEV_LEGACY)
-               put_device(hda_codec_dev(codec));
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_unregister);
-
-static int snd_hda_codec_dev_free(struct snd_device *device)
-{
-       snd_hda_codec_unregister(device->device_data);
-       return 0;
-}
-
-static void snd_hda_codec_dev_release(struct device *dev)
-{
-       struct hda_codec *codec = dev_to_hda_codec(dev);
-
-       free_init_pincfgs(codec);
-       snd_hdac_device_exit(&codec->core);
-       snd_hda_sysfs_clear(codec);
-       kfree(codec->modelname);
-       kfree(codec->wcaps);
-       kfree(codec);
-}
-
-#define DEV_NAME_LEN 31
-
-/**
- * snd_hda_codec_device_init - allocate HDA codec device
- * @bus: codec's parent bus
- * @codec_addr: the codec address on the parent bus
- * @fmt: format string for the device's name
- *
- * Returns newly allocated codec device or ERR_PTR() on failure.
- */
-struct hda_codec *
-snd_hda_codec_device_init(struct hda_bus *bus, unsigned int codec_addr,
-                         const char *fmt, ...)
-{
-       va_list vargs;
-       char name[DEV_NAME_LEN];
-       struct hda_codec *codec;
-       int err;
-
-       if (snd_BUG_ON(!bus))
-               return ERR_PTR(-EINVAL);
-       if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS))
-               return ERR_PTR(-EINVAL);
-
-       codec = kzalloc(sizeof(*codec), GFP_KERNEL);
-       if (!codec)
-               return ERR_PTR(-ENOMEM);
-
-       va_start(vargs, fmt);
-       vsprintf(name, fmt, vargs);
-       va_end(vargs);
-
-       err = snd_hdac_device_init(&codec->core, &bus->core, name, codec_addr);
-       if (err < 0) {
-               kfree(codec);
-               return ERR_PTR(err);
-       }
-
-       codec->bus = bus;
-       codec->depop_delay = -1;
-       codec->fixup_id = HDA_FIXUP_ID_NOT_SET;
-       codec->core.dev.release = snd_hda_codec_dev_release;
-       codec->core.type = HDA_DEV_LEGACY;
-
-       mutex_init(&codec->spdif_mutex);
-       mutex_init(&codec->control_mutex);
-       snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32);
-       snd_array_init(&codec->nids, sizeof(struct hda_nid_item), 32);
-       snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16);
-       snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16);
-       snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8);
-       snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16);
-       snd_array_init(&codec->jacktbl, sizeof(struct hda_jack_tbl), 16);
-       snd_array_init(&codec->verbs, sizeof(struct hda_verb *), 8);
-       INIT_LIST_HEAD(&codec->conn_list);
-       INIT_LIST_HEAD(&codec->pcm_list_head);
-       INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work);
-       refcount_set(&codec->pcm_ref, 1);
-       init_waitqueue_head(&codec->remove_sleep);
-
-       return codec;
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_device_init);
-
-/**
- * snd_hda_codec_new - create a HDA codec
- * @bus: the bus to assign
- * @card: card for this codec
- * @codec_addr: the codec address
- * @codecp: the pointer to store the generated codec
- *
- * Returns 0 if successful, or a negative error code.
- */
-int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card,
-                     unsigned int codec_addr, struct hda_codec **codecp)
-{
-       struct hda_codec *codec;
-       int ret;
-
-       codec = snd_hda_codec_device_init(bus, codec_addr, "hdaudioC%dD%d",
-                                         card->number, codec_addr);
-       if (IS_ERR(codec))
-               return PTR_ERR(codec);
-       *codecp = codec;
-
-       ret = snd_hda_codec_device_new(bus, card, codec_addr, *codecp, true);
-       if (ret)
-               put_device(hda_codec_dev(*codecp));
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_new);
-
-int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card,
-                       unsigned int codec_addr, struct hda_codec *codec,
-                       bool snddev_managed)
-{
-       char component[31];
-       hda_nid_t fg;
-       int err;
-       static const struct snd_device_ops dev_ops = {
-               .dev_register = snd_hda_codec_dev_register,
-               .dev_free = snd_hda_codec_dev_free,
-       };
-
-       dev_dbg(card->dev, "%s: entry\n", __func__);
-
-       if (snd_BUG_ON(!bus))
-               return -EINVAL;
-       if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS))
-               return -EINVAL;
-
-       codec->core.exec_verb = codec_exec_verb;
-       codec->card = card;
-       codec->addr = codec_addr;
-
-       codec->power_jiffies = jiffies;
-
-       snd_hda_sysfs_init(codec);
-
-       if (codec->bus->modelname) {
-               codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
-               if (!codec->modelname)
-                       return -ENOMEM;
-       }
-
-       fg = codec->core.afg ? codec->core.afg : codec->core.mfg;
-       err = read_widget_caps(codec, fg);
-       if (err < 0)
-               return err;
-       err = read_pin_defaults(codec);
-       if (err < 0)
-               return err;
-
-       /* power-up all before initialization */
-       hda_set_power_state(codec, AC_PWRST_D0);
-       codec->core.dev.power.power_state = PMSG_ON;
-
-       snd_hda_codec_proc_new(codec);
-
-       snd_hda_create_hwdep(codec);
-
-       sprintf(component, "HDA:%08x,%08x,%08x", codec->core.vendor_id,
-               codec->core.subsystem_id, codec->core.revision_id);
-       snd_component_add(card, component);
-
-       if (snddev_managed) {
-               /* ASoC features component management instead */
-               err = snd_device_new(card, SNDRV_DEV_CODEC, codec, &dev_ops);
-               if (err < 0)
-                       return err;
-       }
-
-#ifdef CONFIG_PM
-       /* PM runtime needs to be enabled later after binding codec */
-       if (codec->core.dev.power.runtime_auto)
-               pm_runtime_forbid(&codec->core.dev);
-       else
-               /* Keep the usage_count consistent across subsequent probing */
-               pm_runtime_get_noresume(&codec->core.dev);
-#endif
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_device_new);
-
-/**
- * snd_hda_codec_update_widgets - Refresh widget caps and pin defaults
- * @codec: the HDA codec
- *
- * Forcibly refresh the all widget caps and the init pin configurations of
- * the given codec.
- */
-int snd_hda_codec_update_widgets(struct hda_codec *codec)
-{
-       hda_nid_t fg;
-       int err;
-
-       err = snd_hdac_refresh_widgets(&codec->core);
-       if (err < 0)
-               return err;
-
-       /* Assume the function group node does not change,
-        * only the widget nodes may change.
-        */
-       kfree(codec->wcaps);
-       fg = codec->core.afg ? codec->core.afg : codec->core.mfg;
-       err = read_widget_caps(codec, fg);
-       if (err < 0)
-               return err;
-
-       snd_array_free(&codec->init_pins);
-       err = read_pin_defaults(codec);
-
-       return err;
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_update_widgets);
-
-/* update the stream-id if changed */
-static void update_pcm_stream_id(struct hda_codec *codec,
-                                struct hda_cvt_setup *p, hda_nid_t nid,
-                                u32 stream_tag, int channel_id)
-{
-       unsigned int oldval, newval;
-
-       if (p->stream_tag != stream_tag || p->channel_id != channel_id) {
-               oldval = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
-               newval = (stream_tag << 4) | channel_id;
-               if (oldval != newval)
-                       snd_hda_codec_write(codec, nid, 0,
-                                           AC_VERB_SET_CHANNEL_STREAMID,
-                                           newval);
-               p->stream_tag = stream_tag;
-               p->channel_id = channel_id;
-       }
-}
-
-/* update the format-id if changed */
-static void update_pcm_format(struct hda_codec *codec, struct hda_cvt_setup *p,
-                             hda_nid_t nid, int format)
-{
-       unsigned int oldval;
-
-       if (p->format_id != format) {
-               oldval = snd_hda_codec_read(codec, nid, 0,
-                                           AC_VERB_GET_STREAM_FORMAT, 0);
-               if (oldval != format) {
-                       msleep(1);
-                       snd_hda_codec_write(codec, nid, 0,
-                                           AC_VERB_SET_STREAM_FORMAT,
-                                           format);
-               }
-               p->format_id = format;
-       }
-}
-
-/**
- * snd_hda_codec_setup_stream - set up the codec for streaming
- * @codec: the CODEC to set up
- * @nid: the NID to set up
- * @stream_tag: stream tag to pass, it's between 0x1 and 0xf.
- * @channel_id: channel id to pass, zero based.
- * @format: stream format.
- */
-void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
-                               u32 stream_tag,
-                               int channel_id, int format)
-{
-       struct hda_codec *c;
-       struct hda_cvt_setup *p;
-       int type;
-       int i;
-
-       if (!nid)
-               return;
-
-       codec_dbg(codec,
-                 "hda_codec_setup_stream: NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n",
-                 nid, stream_tag, channel_id, format);
-       p = get_hda_cvt_setup(codec, nid);
-       if (!p)
-               return;
-
-       if (codec->patch_ops.stream_pm)
-               codec->patch_ops.stream_pm(codec, nid, true);
-       if (codec->pcm_format_first)
-               update_pcm_format(codec, p, nid, format);
-       update_pcm_stream_id(codec, p, nid, stream_tag, channel_id);
-       if (!codec->pcm_format_first)
-               update_pcm_format(codec, p, nid, format);
-
-       p->active = 1;
-       p->dirty = 0;
-
-       /* make other inactive cvts with the same stream-tag dirty */
-       type = get_wcaps_type(get_wcaps(codec, nid));
-       list_for_each_codec(c, codec->bus) {
-               snd_array_for_each(&c->cvt_setups, i, p) {
-                       if (!p->active && p->stream_tag == stream_tag &&
-                           get_wcaps_type(get_wcaps(c, p->nid)) == type)
-                               p->dirty = 1;
-               }
-       }
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_setup_stream);
-
-static void really_cleanup_stream(struct hda_codec *codec,
-                                 struct hda_cvt_setup *q);
-
-/**
- * __snd_hda_codec_cleanup_stream - clean up the codec for closing
- * @codec: the CODEC to clean up
- * @nid: the NID to clean up
- * @do_now: really clean up the stream instead of clearing the active flag
- */
-void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid,
-                                   int do_now)
-{
-       struct hda_cvt_setup *p;
-
-       if (!nid)
-               return;
-
-       if (codec->no_sticky_stream)
-               do_now = 1;
-
-       codec_dbg(codec, "hda_codec_cleanup_stream: NID=0x%x\n", nid);
-       p = get_hda_cvt_setup(codec, nid);
-       if (p) {
-               /* here we just clear the active flag when do_now isn't set;
-                * actual clean-ups will be done later in
-                * purify_inactive_streams() called from snd_hda_codec_prpapre()
-                */
-               if (do_now)
-                       really_cleanup_stream(codec, p);
-               else
-                       p->active = 0;
-       }
-}
-EXPORT_SYMBOL_GPL(__snd_hda_codec_cleanup_stream);
-
-static void really_cleanup_stream(struct hda_codec *codec,
-                                 struct hda_cvt_setup *q)
-{
-       hda_nid_t nid = q->nid;
-       if (q->stream_tag || q->channel_id)
-               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
-       if (q->format_id)
-               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0
-);
-       memset(q, 0, sizeof(*q));
-       q->nid = nid;
-       if (codec->patch_ops.stream_pm)
-               codec->patch_ops.stream_pm(codec, nid, false);
-}
-
-/* clean up the all conflicting obsolete streams */
-static void purify_inactive_streams(struct hda_codec *codec)
-{
-       struct hda_codec *c;
-       struct hda_cvt_setup *p;
-       int i;
-
-       list_for_each_codec(c, codec->bus) {
-               snd_array_for_each(&c->cvt_setups, i, p) {
-                       if (p->dirty)
-                               really_cleanup_stream(c, p);
-               }
-       }
-}
-
-/* clean up all streams; called from suspend */
-static void hda_cleanup_all_streams(struct hda_codec *codec)
-{
-       struct hda_cvt_setup *p;
-       int i;
-
-       snd_array_for_each(&codec->cvt_setups, i, p) {
-               if (p->stream_tag)
-                       really_cleanup_stream(codec, p);
-       }
-}
-
-/*
- * amp access functions
- */
-
-/**
- * query_amp_caps - query AMP capabilities
- * @codec: the HD-auio codec
- * @nid: the NID to query
- * @direction: either #HDA_INPUT or #HDA_OUTPUT
- *
- * Query AMP capabilities for the given widget and direction.
- * Returns the obtained capability bits.
- *
- * When cap bits have been already read, this doesn't read again but
- * returns the cached value.
- */
-u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
-{
-       if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD))
-               nid = codec->core.afg;
-       return snd_hda_param_read(codec, nid,
-                                 direction == HDA_OUTPUT ?
-                                 AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
-}
-EXPORT_SYMBOL_GPL(query_amp_caps);
-
-/**
- * snd_hda_check_amp_caps - query AMP capabilities
- * @codec: the HD-audio codec
- * @nid: the NID to query
- * @dir: either #HDA_INPUT or #HDA_OUTPUT
- * @bits: bit mask to check the result
- *
- * Check whether the widget has the given amp capability for the direction.
- */
-bool snd_hda_check_amp_caps(struct hda_codec *codec, hda_nid_t nid,
-                          int dir, unsigned int bits)
-{
-       if (!nid)
-               return false;
-       if (get_wcaps(codec, nid) & (1 << (dir + 1)))
-               if (query_amp_caps(codec, nid, dir) & bits)
-                       return true;
-       return false;
-}
-EXPORT_SYMBOL_GPL(snd_hda_check_amp_caps);
-
-/**
- * snd_hda_override_amp_caps - Override the AMP capabilities
- * @codec: the CODEC to clean up
- * @nid: the NID to clean up
- * @dir: either #HDA_INPUT or #HDA_OUTPUT
- * @caps: the capability bits to set
- *
- * Override the cached AMP caps bits value by the given one.
- * This function is useful if the driver needs to adjust the AMP ranges,
- * e.g. limit to 0dB, etc.
- *
- * Returns zero if successful or a negative error code.
- */
-int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
-                             unsigned int caps)
-{
-       unsigned int parm;
-
-       snd_hda_override_wcaps(codec, nid,
-                              get_wcaps(codec, nid) | AC_WCAP_AMP_OVRD);
-       parm = dir == HDA_OUTPUT ? AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP;
-       return snd_hdac_override_parm(&codec->core, nid, parm, caps);
-}
-EXPORT_SYMBOL_GPL(snd_hda_override_amp_caps);
-
-static unsigned int encode_amp(struct hda_codec *codec, hda_nid_t nid,
-                              int ch, int dir, int idx)
-{
-       unsigned int cmd = snd_hdac_regmap_encode_amp(nid, ch, dir, idx);
-
-       /* enable fake mute if no h/w mute but min=mute */
-       if ((query_amp_caps(codec, nid, dir) &
-            (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) == AC_AMPCAP_MIN_MUTE)
-               cmd |= AC_AMP_FAKE_MUTE;
-       return cmd;
-}
-
-/**
- * snd_hda_codec_amp_update - update the AMP mono value
- * @codec: HD-audio codec
- * @nid: NID to read the AMP value
- * @ch: channel to update (0 or 1)
- * @dir: #HDA_INPUT or #HDA_OUTPUT
- * @idx: the index value (only for input direction)
- * @mask: bit mask to set
- * @val: the bits value to set
- *
- * Update the AMP values for the given channel, direction and index.
- */
-int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid,
-                            int ch, int dir, int idx, int mask, int val)
-{
-       unsigned int cmd = encode_amp(codec, nid, ch, dir, idx);
-
-       return snd_hdac_regmap_update_raw(&codec->core, cmd, mask, val);
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_amp_update);
-
-/**
- * snd_hda_codec_amp_stereo - update the AMP stereo values
- * @codec: HD-audio codec
- * @nid: NID to read the AMP value
- * @direction: #HDA_INPUT or #HDA_OUTPUT
- * @idx: the index value (only for input direction)
- * @mask: bit mask to set
- * @val: the bits value to set
- *
- * Update the AMP values like snd_hda_codec_amp_update(), but for a
- * stereo widget with the same mask and value.
- */
-int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
-                            int direction, int idx, int mask, int val)
-{
-       int ch, ret = 0;
-
-       if (snd_BUG_ON(mask & ~0xff))
-               mask &= 0xff;
-       for (ch = 0; ch < 2; ch++)
-               ret |= snd_hda_codec_amp_update(codec, nid, ch, direction,
-                                               idx, mask, val);
-       return ret;
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_amp_stereo);
-
-/**
- * snd_hda_codec_amp_init - initialize the AMP value
- * @codec: the HDA codec
- * @nid: NID to read the AMP value
- * @ch: channel (left=0 or right=1)
- * @dir: #HDA_INPUT or #HDA_OUTPUT
- * @idx: the index value (only for input direction)
- * @mask: bit mask to set
- * @val: the bits value to set
- *
- * Works like snd_hda_codec_amp_update() but it writes the value only at
- * the first access.  If the amp was already initialized / updated beforehand,
- * this does nothing.
- */
-int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch,
-                          int dir, int idx, int mask, int val)
-{
-       unsigned int cmd = encode_amp(codec, nid, ch, dir, idx);
-
-       if (!codec->core.regmap)
-               return -EINVAL;
-       return snd_hdac_regmap_update_raw_once(&codec->core, cmd, mask, val);
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_amp_init);
-
-/**
- * snd_hda_codec_amp_init_stereo - initialize the stereo AMP value
- * @codec: the HDA codec
- * @nid: NID to read the AMP value
- * @dir: #HDA_INPUT or #HDA_OUTPUT
- * @idx: the index value (only for input direction)
- * @mask: bit mask to set
- * @val: the bits value to set
- *
- * Call snd_hda_codec_amp_init() for both stereo channels.
- */
-int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid,
-                                 int dir, int idx, int mask, int val)
-{
-       int ch, ret = 0;
-
-       if (snd_BUG_ON(mask & ~0xff))
-               mask &= 0xff;
-       for (ch = 0; ch < 2; ch++)
-               ret |= snd_hda_codec_amp_init(codec, nid, ch, dir,
-                                             idx, mask, val);
-       return ret;
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_amp_init_stereo);
-
-static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir,
-                            unsigned int ofs)
-{
-       u32 caps = query_amp_caps(codec, nid, dir);
-       /* get num steps */
-       caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
-       if (ofs < caps)
-               caps -= ofs;
-       return caps;
-}
-
-/**
- * snd_hda_mixer_amp_volume_info - Info callback for a standard AMP mixer
- * @kcontrol: referred ctl element
- * @uinfo: pointer to get/store the data
- *
- * The control element is supposed to have the private_value field
- * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
- */
-int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
-                                 struct snd_ctl_elem_info *uinfo)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       u16 nid = get_amp_nid(kcontrol);
-       u8 chs = get_amp_channels(kcontrol);
-       int dir = get_amp_direction(kcontrol);
-       unsigned int ofs = get_amp_offset(kcontrol);
-
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-       uinfo->count = chs == 3 ? 2 : 1;
-       uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = get_amp_max_value(codec, nid, dir, ofs);
-       if (!uinfo->value.integer.max) {
-               codec_warn(codec,
-                          "num_steps = 0 for NID=0x%x (ctl = %s)\n",
-                          nid, kcontrol->id.name);
-               return -EINVAL;
-       }
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_info);
-
-
-static inline unsigned int
-read_amp_value(struct hda_codec *codec, hda_nid_t nid,
-              int ch, int dir, int idx, unsigned int ofs)
-{
-       unsigned int val;
-       val = snd_hda_codec_amp_read(codec, nid, ch, dir, idx);
-       val &= HDA_AMP_VOLMASK;
-       if (val >= ofs)
-               val -= ofs;
-       else
-               val = 0;
-       return val;
-}
-
-static inline int
-update_amp_value(struct hda_codec *codec, hda_nid_t nid,
-                int ch, int dir, int idx, unsigned int ofs,
-                unsigned int val)
-{
-       unsigned int maxval;
-
-       if (val > 0)
-               val += ofs;
-       /* ofs = 0: raw max value */
-       maxval = get_amp_max_value(codec, nid, dir, 0);
-       if (val > maxval)
-               return -EINVAL;
-       return snd_hda_codec_amp_update(codec, nid, ch, dir, idx,
-                                       HDA_AMP_VOLMASK, val);
-}
-
-/**
- * snd_hda_mixer_amp_volume_get - Get callback for a standard AMP mixer volume
- * @kcontrol: ctl element
- * @ucontrol: pointer to get/store the data
- *
- * The control element is supposed to have the private_value field
- * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
- */
-int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
-                                struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       hda_nid_t nid = get_amp_nid(kcontrol);
-       int chs = get_amp_channels(kcontrol);
-       int dir = get_amp_direction(kcontrol);
-       int idx = get_amp_index(kcontrol);
-       unsigned int ofs = get_amp_offset(kcontrol);
-       long *valp = ucontrol->value.integer.value;
-
-       if (chs & 1)
-               *valp++ = read_amp_value(codec, nid, 0, dir, idx, ofs);
-       if (chs & 2)
-               *valp = read_amp_value(codec, nid, 1, dir, idx, ofs);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_get);
-
-/**
- * snd_hda_mixer_amp_volume_put - Put callback for a standard AMP mixer volume
- * @kcontrol: ctl element
- * @ucontrol: pointer to get/store the data
- *
- * The control element is supposed to have the private_value field
- * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
- */
-int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
-                                struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       hda_nid_t nid = get_amp_nid(kcontrol);
-       int chs = get_amp_channels(kcontrol);
-       int dir = get_amp_direction(kcontrol);
-       int idx = get_amp_index(kcontrol);
-       unsigned int ofs = get_amp_offset(kcontrol);
-       long *valp = ucontrol->value.integer.value;
-       int change = 0;
-       int err;
-
-       if (chs & 1) {
-               err = update_amp_value(codec, nid, 0, dir, idx, ofs, *valp);
-               if (err < 0)
-                       return err;
-               change |= err;
-               valp++;
-       }
-       if (chs & 2) {
-               err = update_amp_value(codec, nid, 1, dir, idx, ofs, *valp);
-               if (err < 0)
-                       return err;
-               change |= err;
-       }
-       return change;
-}
-EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_put);
-
-/* inquiry the amp caps and convert to TLV */
-static void get_ctl_amp_tlv(struct snd_kcontrol *kcontrol, unsigned int *tlv)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       hda_nid_t nid = get_amp_nid(kcontrol);
-       int dir = get_amp_direction(kcontrol);
-       unsigned int ofs = get_amp_offset(kcontrol);
-       bool min_mute = get_amp_min_mute(kcontrol);
-       u32 caps, val1, val2;
-
-       caps = query_amp_caps(codec, nid, dir);
-       val2 = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT;
-       val2 = (val2 + 1) * 25;
-       val1 = -((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT);
-       val1 += ofs;
-       val1 = ((int)val1) * ((int)val2);
-       if (min_mute || (caps & AC_AMPCAP_MIN_MUTE))
-               val2 |= TLV_DB_SCALE_MUTE;
-       tlv[SNDRV_CTL_TLVO_TYPE] = SNDRV_CTL_TLVT_DB_SCALE;
-       tlv[SNDRV_CTL_TLVO_LEN] = 2 * sizeof(unsigned int);
-       tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] = val1;
-       tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] = val2;
-}
-
-/**
- * snd_hda_mixer_amp_tlv - TLV callback for a standard AMP mixer volume
- * @kcontrol: ctl element
- * @op_flag: operation flag
- * @size: byte size of input TLV
- * @_tlv: TLV data
- *
- * The control element is supposed to have the private_value field
- * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
- */
-int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
-                         unsigned int size, unsigned int __user *_tlv)
-{
-       unsigned int tlv[4];
-
-       if (size < 4 * sizeof(unsigned int))
-               return -ENOMEM;
-       get_ctl_amp_tlv(kcontrol, tlv);
-       if (copy_to_user(_tlv, tlv, sizeof(tlv)))
-               return -EFAULT;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_tlv);
-
-/**
- * snd_hda_set_vmaster_tlv - Set TLV for a virtual master control
- * @codec: HD-audio codec
- * @nid: NID of a reference widget
- * @dir: #HDA_INPUT or #HDA_OUTPUT
- * @tlv: TLV data to be stored, at least 4 elements
- *
- * Set (static) TLV data for a virtual master volume using the AMP caps
- * obtained from the reference NID.
- * The volume range is recalculated as if the max volume is 0dB.
- */
-void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
-                            unsigned int *tlv)
-{
-       u32 caps;
-       int nums, step;
-
-       caps = query_amp_caps(codec, nid, dir);
-       nums = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
-       step = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT;
-       step = (step + 1) * 25;
-       tlv[SNDRV_CTL_TLVO_TYPE] = SNDRV_CTL_TLVT_DB_SCALE;
-       tlv[SNDRV_CTL_TLVO_LEN] = 2 * sizeof(unsigned int);
-       tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] = -nums * step;
-       tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] = step;
-}
-EXPORT_SYMBOL_GPL(snd_hda_set_vmaster_tlv);
-
-/* find a mixer control element with the given name */
-static struct snd_kcontrol *
-find_mixer_ctl(struct hda_codec *codec, const char *name, int dev, int idx)
-{
-       struct snd_ctl_elem_id id;
-       memset(&id, 0, sizeof(id));
-       id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-       id.device = dev;
-       id.index = idx;
-       if (snd_BUG_ON(strlen(name) >= sizeof(id.name)))
-               return NULL;
-       strcpy(id.name, name);
-       return snd_ctl_find_id(codec->card, &id);
-}
-
-/**
- * snd_hda_find_mixer_ctl - Find a mixer control element with the given name
- * @codec: HD-audio codec
- * @name: ctl id name string
- *
- * Get the control element with the given id string and IFACE_MIXER.
- */
-struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
-                                           const char *name)
-{
-       return find_mixer_ctl(codec, name, 0, 0);
-}
-EXPORT_SYMBOL_GPL(snd_hda_find_mixer_ctl);
-
-static int find_empty_mixer_ctl_idx(struct hda_codec *codec, const char *name,
-                                   int start_idx)
-{
-       int i, idx;
-       /* 16 ctlrs should be large enough */
-       for (i = 0, idx = start_idx; i < 16; i++, idx++) {
-               if (!find_mixer_ctl(codec, name, 0, idx))
-                       return idx;
-       }
-       return -EBUSY;
-}
-
-/**
- * snd_hda_ctl_add - Add a control element and assign to the codec
- * @codec: HD-audio codec
- * @nid: corresponding NID (optional)
- * @kctl: the control element to assign
- *
- * Add the given control element to an array inside the codec instance.
- * All control elements belonging to a codec are supposed to be added
- * by this function so that a proper clean-up works at the free or
- * reconfiguration time.
- *
- * If non-zero @nid is passed, the NID is assigned to the control element.
- * The assignment is shown in the codec proc file.
- *
- * snd_hda_ctl_add() checks the control subdev id field whether
- * #HDA_SUBDEV_NID_FLAG bit is set.  If set (and @nid is zero), the lower
- * bits value is taken as the NID to assign. The #HDA_NID_ITEM_AMP bit
- * specifies if kctl->private_value is a HDA amplifier value.
- */
-int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid,
-                   struct snd_kcontrol *kctl)
-{
-       int err;
-       unsigned short flags = 0;
-       struct hda_nid_item *item;
-
-       if (kctl->id.subdevice & HDA_SUBDEV_AMP_FLAG) {
-               flags |= HDA_NID_ITEM_AMP;
-               if (nid == 0)
-                       nid = get_amp_nid_(kctl->private_value);
-       }
-       if ((kctl->id.subdevice & HDA_SUBDEV_NID_FLAG) != 0 && nid == 0)
-               nid = kctl->id.subdevice & 0xffff;
-       if (kctl->id.subdevice & (HDA_SUBDEV_NID_FLAG|HDA_SUBDEV_AMP_FLAG))
-               kctl->id.subdevice = 0;
-       err = snd_ctl_add(codec->card, kctl);
-       if (err < 0)
-               return err;
-       item = snd_array_new(&codec->mixers);
-       if (!item)
-               return -ENOMEM;
-       item->kctl = kctl;
-       item->nid = nid;
-       item->flags = flags;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_ctl_add);
-
-/**
- * snd_hda_ctls_clear - Clear all controls assigned to the given codec
- * @codec: HD-audio codec
- */
-void snd_hda_ctls_clear(struct hda_codec *codec)
-{
-       int i;
-       struct hda_nid_item *items = codec->mixers.list;
-
-       for (i = 0; i < codec->mixers.used; i++)
-               snd_ctl_remove(codec->card, items[i].kctl);
-       snd_array_free(&codec->mixers);
-       snd_array_free(&codec->nids);
-}
-
-/**
- * snd_hda_lock_devices - pseudo device locking
- * @bus: the BUS
- *
- * toggle card->shutdown to allow/disallow the device access (as a hack)
- */
-int snd_hda_lock_devices(struct hda_bus *bus)
-{
-       struct snd_card *card = bus->card;
-       struct hda_codec *codec;
-
-       spin_lock(&card->files_lock);
-       if (card->shutdown)
-               goto err_unlock;
-       card->shutdown = 1;
-       if (!list_empty(&card->ctl_files))
-               goto err_clear;
-
-       list_for_each_codec(codec, bus) {
-               struct hda_pcm *cpcm;
-               list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
-                       if (!cpcm->pcm)
-                               continue;
-                       if (cpcm->pcm->streams[0].substream_opened ||
-                           cpcm->pcm->streams[1].substream_opened)
-                               goto err_clear;
-               }
-       }
-       spin_unlock(&card->files_lock);
-       return 0;
-
- err_clear:
-       card->shutdown = 0;
- err_unlock:
-       spin_unlock(&card->files_lock);
-       return -EINVAL;
-}
-EXPORT_SYMBOL_GPL(snd_hda_lock_devices);
-
-/**
- * snd_hda_unlock_devices - pseudo device unlocking
- * @bus: the BUS
- */
-void snd_hda_unlock_devices(struct hda_bus *bus)
-{
-       struct snd_card *card = bus->card;
-
-       spin_lock(&card->files_lock);
-       card->shutdown = 0;
-       spin_unlock(&card->files_lock);
-}
-EXPORT_SYMBOL_GPL(snd_hda_unlock_devices);
-
-/**
- * snd_hda_codec_reset - Clear all objects assigned to the codec
- * @codec: HD-audio codec
- *
- * This frees the all PCM and control elements assigned to the codec, and
- * clears the caches and restores the pin default configurations.
- *
- * When a device is being used, it returns -EBSY.  If successfully freed,
- * returns zero.
- */
-int snd_hda_codec_reset(struct hda_codec *codec)
-{
-       struct hda_bus *bus = codec->bus;
-
-       if (snd_hda_lock_devices(bus) < 0)
-               return -EBUSY;
-
-       /* OK, let it free */
-       device_release_driver(hda_codec_dev(codec));
-
-       /* allow device access again */
-       snd_hda_unlock_devices(bus);
-       return 0;
-}
-
-typedef int (*map_follower_func_t)(struct hda_codec *, void *, struct snd_kcontrol *);
-
-/* apply the function to all matching follower ctls in the mixer list */
-static int map_followers(struct hda_codec *codec, const char * const *followers,
-                        const char *suffix, map_follower_func_t func, void *data)
-{
-       struct hda_nid_item *items;
-       const char * const *s;
-       int i, err;
-
-       items = codec->mixers.list;
-       for (i = 0; i < codec->mixers.used; i++) {
-               struct snd_kcontrol *sctl = items[i].kctl;
-               if (!sctl || sctl->id.iface != SNDRV_CTL_ELEM_IFACE_MIXER)
-                       continue;
-               for (s = followers; *s; s++) {
-                       char tmpname[sizeof(sctl->id.name)];
-                       const char *name = *s;
-                       if (suffix) {
-                               snprintf(tmpname, sizeof(tmpname), "%s %s",
-                                        name, suffix);
-                               name = tmpname;
-                       }
-                       if (!strcmp(sctl->id.name, name)) {
-                               err = func(codec, data, sctl);
-                               if (err)
-                                       return err;
-                               break;
-                       }
-               }
-       }
-       return 0;
-}
-
-static int check_follower_present(struct hda_codec *codec,
-                                 void *data, struct snd_kcontrol *sctl)
-{
-       return 1;
-}
-
-/* call kctl->put with the given value(s) */
-static int put_kctl_with_value(struct snd_kcontrol *kctl, int val)
-{
-       struct snd_ctl_elem_value *ucontrol;
-       ucontrol = kzalloc(sizeof(*ucontrol), GFP_KERNEL);
-       if (!ucontrol)
-               return -ENOMEM;
-       ucontrol->value.integer.value[0] = val;
-       ucontrol->value.integer.value[1] = val;
-       kctl->put(kctl, ucontrol);
-       kfree(ucontrol);
-       return 0;
-}
-
-struct follower_init_arg {
-       struct hda_codec *codec;
-       int step;
-};
-
-/* initialize the follower volume with 0dB via snd_ctl_apply_vmaster_followers() */
-static int init_follower_0dB(struct snd_kcontrol *follower,
-                            struct snd_kcontrol *kctl,
-                            void *_arg)
-{
-       struct follower_init_arg *arg = _arg;
-       int _tlv[4];
-       const int *tlv = NULL;
-       int step;
-       int val;
-
-       if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
-               if (kctl->tlv.c != snd_hda_mixer_amp_tlv) {
-                       codec_err(arg->codec,
-                                 "Unexpected TLV callback for follower %s:%d\n",
-                                 kctl->id.name, kctl->id.index);
-                       return 0; /* ignore */
-               }
-               get_ctl_amp_tlv(kctl, _tlv);
-               tlv = _tlv;
-       } else if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_READ)
-               tlv = kctl->tlv.p;
-
-       if (!tlv || tlv[SNDRV_CTL_TLVO_TYPE] != SNDRV_CTL_TLVT_DB_SCALE)
-               return 0;
-
-       step = tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP];
-       step &= ~TLV_DB_SCALE_MUTE;
-       if (!step)
-               return 0;
-       if (arg->step && arg->step != step) {
-               codec_err(arg->codec,
-                         "Mismatching dB step for vmaster follower (%d!=%d)\n",
-                         arg->step, step);
-               return 0;
-       }
-
-       arg->step = step;
-       val = -tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] / step;
-       if (val > 0) {
-               put_kctl_with_value(follower, val);
-               return val;
-       }
-
-       return 0;
-}
-
-/* unmute the follower via snd_ctl_apply_vmaster_followers() */
-static int init_follower_unmute(struct snd_kcontrol *follower,
-                               struct snd_kcontrol *kctl,
-                               void *_arg)
-{
-       return put_kctl_with_value(follower, 1);
-}
-
-static int add_follower(struct hda_codec *codec,
-                       void *data, struct snd_kcontrol *follower)
-{
-       return snd_ctl_add_follower(data, follower);
-}
-
-/**
- * __snd_hda_add_vmaster - create a virtual master control and add followers
- * @codec: HD-audio codec
- * @name: vmaster control name
- * @tlv: TLV data (optional)
- * @followers: follower control names (optional)
- * @suffix: suffix string to each follower name (optional)
- * @init_follower_vol: initialize followers to unmute/0dB
- * @access: kcontrol access rights
- * @ctl_ret: store the vmaster kcontrol in return
- *
- * Create a virtual master control with the given name.  The TLV data
- * must be either NULL or a valid data.
- *
- * @followers is a NULL-terminated array of strings, each of which is a
- * follower control name.  All controls with these names are assigned to
- * the new virtual master control.
- *
- * This function returns zero if successful or a negative error code.
- */
-int __snd_hda_add_vmaster(struct hda_codec *codec, char *name,
-                         unsigned int *tlv, const char * const *followers,
-                         const char *suffix, bool init_follower_vol,
-                         unsigned int access, struct snd_kcontrol **ctl_ret)
-{
-       struct snd_kcontrol *kctl;
-       int err;
-
-       if (ctl_ret)
-               *ctl_ret = NULL;
-
-       err = map_followers(codec, followers, suffix, check_follower_present, NULL);
-       if (err != 1) {
-               codec_dbg(codec, "No follower found for %s\n", name);
-               return 0;
-       }
-       kctl = snd_ctl_make_virtual_master(name, tlv);
-       if (!kctl)
-               return -ENOMEM;
-       kctl->vd[0].access |= access;
-       err = snd_hda_ctl_add(codec, 0, kctl);
-       if (err < 0)
-               return err;
-
-       err = map_followers(codec, followers, suffix, add_follower, kctl);
-       if (err < 0)
-               return err;
-
-       /* init with master mute & zero volume */
-       put_kctl_with_value(kctl, 0);
-       if (init_follower_vol) {
-               struct follower_init_arg arg = {
-                       .codec = codec,
-                       .step = 0,
-               };
-               snd_ctl_apply_vmaster_followers(kctl,
-                                               tlv ? init_follower_0dB : init_follower_unmute,
-                                               &arg);
-       }
-
-       if (ctl_ret)
-               *ctl_ret = kctl;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(__snd_hda_add_vmaster);
-
-/* meta hook to call each driver's vmaster hook */
-static void vmaster_hook(void *private_data, int enabled)
-{
-       struct hda_vmaster_mute_hook *hook = private_data;
-
-       hook->hook(hook->codec, enabled);
-}
-
-/**
- * snd_hda_add_vmaster_hook - Add a vmaster hw specific hook
- * @codec: the HDA codec
- * @hook: the vmaster hook object
- *
- * Add a hw specific hook (like EAPD) with the given vmaster switch kctl.
- */
-int snd_hda_add_vmaster_hook(struct hda_codec *codec,
-                            struct hda_vmaster_mute_hook *hook)
-{
-       if (!hook->hook || !hook->sw_kctl)
-               return 0;
-       hook->codec = codec;
-       snd_ctl_add_vmaster_hook(hook->sw_kctl, vmaster_hook, hook);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_add_vmaster_hook);
-
-/**
- * snd_hda_sync_vmaster_hook - Sync vmaster hook
- * @hook: the vmaster hook
- *
- * Call the hook with the current value for synchronization.
- * Should be called in init callback.
- */
-void snd_hda_sync_vmaster_hook(struct hda_vmaster_mute_hook *hook)
-{
-       if (!hook->hook || !hook->codec)
-               return;
-       /* don't call vmaster hook in the destructor since it might have
-        * been already destroyed
-        */
-       if (hook->codec->bus->shutdown)
-               return;
-       snd_ctl_sync_vmaster_hook(hook->sw_kctl);
-}
-EXPORT_SYMBOL_GPL(snd_hda_sync_vmaster_hook);
-
-
-/**
- * snd_hda_mixer_amp_switch_info - Info callback for a standard AMP mixer switch
- * @kcontrol: referred ctl element
- * @uinfo: pointer to get/store the data
- *
- * The control element is supposed to have the private_value field
- * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
- */
-int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
-                                 struct snd_ctl_elem_info *uinfo)
-{
-       int chs = get_amp_channels(kcontrol);
-
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
-       uinfo->count = chs == 3 ? 2 : 1;
-       uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = 1;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_info);
-
-/**
- * snd_hda_mixer_amp_switch_get - Get callback for a standard AMP mixer switch
- * @kcontrol: ctl element
- * @ucontrol: pointer to get/store the data
- *
- * The control element is supposed to have the private_value field
- * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
- */
-int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
-                                struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       hda_nid_t nid = get_amp_nid(kcontrol);
-       int chs = get_amp_channels(kcontrol);
-       int dir = get_amp_direction(kcontrol);
-       int idx = get_amp_index(kcontrol);
-       long *valp = ucontrol->value.integer.value;
-
-       if (chs & 1)
-               *valp++ = (snd_hda_codec_amp_read(codec, nid, 0, dir, idx) &
-                          HDA_AMP_MUTE) ? 0 : 1;
-       if (chs & 2)
-               *valp = (snd_hda_codec_amp_read(codec, nid, 1, dir, idx) &
-                        HDA_AMP_MUTE) ? 0 : 1;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_get);
-
-/**
- * snd_hda_mixer_amp_switch_put - Put callback for a standard AMP mixer switch
- * @kcontrol: ctl element
- * @ucontrol: pointer to get/store the data
- *
- * The control element is supposed to have the private_value field
- * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
- */
-int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
-                                struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       hda_nid_t nid = get_amp_nid(kcontrol);
-       int chs = get_amp_channels(kcontrol);
-       int dir = get_amp_direction(kcontrol);
-       int idx = get_amp_index(kcontrol);
-       long *valp = ucontrol->value.integer.value;
-       int change = 0;
-
-       if (chs & 1) {
-               if (*valp < 0 || *valp > 1)
-                       return -EINVAL;
-               change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx,
-                                                 HDA_AMP_MUTE,
-                                                 *valp ? 0 : HDA_AMP_MUTE);
-               valp++;
-       }
-       if (chs & 2) {
-               if (*valp < 0 || *valp > 1)
-                       return -EINVAL;
-               change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx,
-                                                  HDA_AMP_MUTE,
-                                                  *valp ? 0 : HDA_AMP_MUTE);
-       }
-       hda_call_check_power_status(codec, nid);
-       return change;
-}
-EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_put);
-
-/*
- * SPDIF out controls
- */
-
-static int snd_hda_spdif_mask_info(struct snd_kcontrol *kcontrol,
-                                  struct snd_ctl_elem_info *uinfo)
-{
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
-       uinfo->count = 1;
-       return 0;
-}
-
-static int snd_hda_spdif_cmask_get(struct snd_kcontrol *kcontrol,
-                                  struct snd_ctl_elem_value *ucontrol)
-{
-       ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL |
-                                          IEC958_AES0_NONAUDIO |
-                                          IEC958_AES0_CON_EMPHASIS_5015 |
-                                          IEC958_AES0_CON_NOT_COPYRIGHT;
-       ucontrol->value.iec958.status[1] = IEC958_AES1_CON_CATEGORY |
-                                          IEC958_AES1_CON_ORIGINAL;
-       return 0;
-}
-
-static int snd_hda_spdif_pmask_get(struct snd_kcontrol *kcontrol,
-                                  struct snd_ctl_elem_value *ucontrol)
-{
-       ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL |
-                                          IEC958_AES0_NONAUDIO |
-                                          IEC958_AES0_PRO_EMPHASIS_5015;
-       return 0;
-}
-
-static int snd_hda_spdif_default_get(struct snd_kcontrol *kcontrol,
-                                    struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       int idx = kcontrol->private_value;
-       struct hda_spdif_out *spdif;
-
-       if (WARN_ON(codec->spdif_out.used <= idx))
-               return -EINVAL;
-       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;
-}
-
-/* convert from SPDIF status bits to HDA SPDIF bits
- * bit 0 (DigEn) is always set zero (to be filled later)
- */
-static unsigned short convert_from_spdif_status(unsigned int sbits)
-{
-       unsigned short val = 0;
-
-       if (sbits & IEC958_AES0_PROFESSIONAL)
-               val |= AC_DIG1_PROFESSIONAL;
-       if (sbits & IEC958_AES0_NONAUDIO)
-               val |= AC_DIG1_NONAUDIO;
-       if (sbits & IEC958_AES0_PROFESSIONAL) {
-               if ((sbits & IEC958_AES0_PRO_EMPHASIS) ==
-                   IEC958_AES0_PRO_EMPHASIS_5015)
-                       val |= AC_DIG1_EMPHASIS;
-       } else {
-               if ((sbits & IEC958_AES0_CON_EMPHASIS) ==
-                   IEC958_AES0_CON_EMPHASIS_5015)
-                       val |= AC_DIG1_EMPHASIS;
-               if (!(sbits & IEC958_AES0_CON_NOT_COPYRIGHT))
-                       val |= AC_DIG1_COPYRIGHT;
-               if (sbits & (IEC958_AES1_CON_ORIGINAL << 8))
-                       val |= AC_DIG1_LEVEL;
-               val |= sbits & (IEC958_AES1_CON_CATEGORY << 8);
-       }
-       return val;
-}
-
-/* convert to SPDIF status bits from HDA SPDIF bits
- */
-static unsigned int convert_to_spdif_status(unsigned short val)
-{
-       unsigned int sbits = 0;
-
-       if (val & AC_DIG1_NONAUDIO)
-               sbits |= IEC958_AES0_NONAUDIO;
-       if (val & AC_DIG1_PROFESSIONAL)
-               sbits |= IEC958_AES0_PROFESSIONAL;
-       if (sbits & IEC958_AES0_PROFESSIONAL) {
-               if (val & AC_DIG1_EMPHASIS)
-                       sbits |= IEC958_AES0_PRO_EMPHASIS_5015;
-       } else {
-               if (val & AC_DIG1_EMPHASIS)
-                       sbits |= IEC958_AES0_CON_EMPHASIS_5015;
-               if (!(val & AC_DIG1_COPYRIGHT))
-                       sbits |= IEC958_AES0_CON_NOT_COPYRIGHT;
-               if (val & AC_DIG1_LEVEL)
-                       sbits |= (IEC958_AES1_CON_ORIGINAL << 8);
-               sbits |= val & (0x7f << 8);
-       }
-       return sbits;
-}
-
-/* set digital convert verbs both for the given NID and its followers */
-static void set_dig_out(struct hda_codec *codec, hda_nid_t nid,
-                       int mask, int val)
-{
-       const hda_nid_t *d;
-
-       snd_hdac_regmap_update(&codec->core, nid, AC_VERB_SET_DIGI_CONVERT_1,
-                              mask, val);
-       d = codec->follower_dig_outs;
-       if (!d)
-               return;
-       for (; *d; d++)
-               snd_hdac_regmap_update(&codec->core, *d,
-                                      AC_VERB_SET_DIGI_CONVERT_1, mask, val);
-}
-
-static inline void set_dig_out_convert(struct hda_codec *codec, hda_nid_t nid,
-                                      int dig1, int dig2)
-{
-       unsigned int mask = 0;
-       unsigned int val = 0;
-
-       if (dig1 != -1) {
-               mask |= 0xff;
-               val = dig1;
-       }
-       if (dig2 != -1) {
-               mask |= 0xff00;
-               val |= dig2 << 8;
-       }
-       set_dig_out(codec, nid, mask, val);
-}
-
-static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,
-                                    struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       int idx = kcontrol->private_value;
-       struct hda_spdif_out *spdif;
-       hda_nid_t nid;
-       unsigned short val;
-       int change;
-
-       if (WARN_ON(codec->spdif_out.used <= idx))
-               return -EINVAL;
-       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) |
-               ((unsigned int)ucontrol->value.iec958.status[3] << 24);
-       val = convert_from_spdif_status(spdif->status);
-       val |= spdif->ctls & 1;
-       change = spdif->ctls != val;
-       spdif->ctls = val;
-       if (change && nid != (u16)-1)
-               set_dig_out_convert(codec, nid, val & 0xff, (val >> 8) & 0xff);
-       mutex_unlock(&codec->spdif_mutex);
-       return change;
-}
-
-#define snd_hda_spdif_out_switch_info  snd_ctl_boolean_mono_info
-
-static int snd_hda_spdif_out_switch_get(struct snd_kcontrol *kcontrol,
-                                       struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       int idx = kcontrol->private_value;
-       struct hda_spdif_out *spdif;
-
-       if (WARN_ON(codec->spdif_out.used <= idx))
-               return -EINVAL;
-       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;
-}
-
-static inline void set_spdif_ctls(struct hda_codec *codec, hda_nid_t nid,
-                                 int dig1, int dig2)
-{
-       set_dig_out_convert(codec, nid, dig1, dig2);
-       /* unmute amp switch (if any) */
-       if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) &&
-           (dig1 & AC_DIG1_ENABLE))
-               snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
-                                           HDA_AMP_MUTE, 0);
-}
-
-static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol,
-                                       struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       int idx = kcontrol->private_value;
-       struct hda_spdif_out *spdif;
-       hda_nid_t nid;
-       unsigned short val;
-       int change;
-
-       if (WARN_ON(codec->spdif_out.used <= idx))
-               return -EINVAL;
-       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;
-       change = spdif->ctls != val;
-       spdif->ctls = val;
-       if (change && nid != (u16)-1)
-               set_spdif_ctls(codec, nid, val & 0xff, -1);
-       mutex_unlock(&codec->spdif_mutex);
-       return change;
-}
-
-static const struct snd_kcontrol_new dig_mixes[] = {
-       {
-               .access = SNDRV_CTL_ELEM_ACCESS_READ,
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),
-               .info = snd_hda_spdif_mask_info,
-               .get = snd_hda_spdif_cmask_get,
-       },
-       {
-               .access = SNDRV_CTL_ELEM_ACCESS_READ,
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PRO_MASK),
-               .info = snd_hda_spdif_mask_info,
-               .get = snd_hda_spdif_pmask_get,
-       },
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
-               .info = snd_hda_spdif_mask_info,
-               .get = snd_hda_spdif_default_get,
-               .put = snd_hda_spdif_default_put,
-       },
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH),
-               .info = snd_hda_spdif_out_switch_info,
-               .get = snd_hda_spdif_out_switch_get,
-               .put = snd_hda_spdif_out_switch_put,
-       },
-       { } /* end */
-};
-
-/**
- * snd_hda_create_dig_out_ctls - create Output SPDIF-related controls
- * @codec: the HDA codec
- * @associated_nid: NID that new ctls associated with
- * @cvt_nid: converter NID
- * @type: HDA_PCM_TYPE_*
- * Creates controls related with the digital output.
- * Called from each patch supporting the digital out.
- *
- * Returns 0 if successful, or a negative error code.
- */
-int snd_hda_create_dig_out_ctls(struct hda_codec *codec,
-                               hda_nid_t associated_nid,
-                               hda_nid_t cvt_nid,
-                               int type)
-{
-       int err;
-       struct snd_kcontrol *kctl;
-       const struct snd_kcontrol_new *dig_mix;
-       int idx = 0;
-       int val = 0;
-       const int spdif_index = 16;
-       struct hda_spdif_out *spdif;
-       struct hda_bus *bus = codec->bus;
-
-       if (bus->primary_dig_out_type == HDA_PCM_TYPE_HDMI &&
-           type == HDA_PCM_TYPE_SPDIF) {
-               idx = spdif_index;
-       } else if (bus->primary_dig_out_type == HDA_PCM_TYPE_SPDIF &&
-                  type == HDA_PCM_TYPE_HDMI) {
-               /* suppose a single SPDIF device */
-               for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) {
-                       struct snd_ctl_elem_id id;
-
-                       kctl = find_mixer_ctl(codec, dig_mix->name, 0, 0);
-                       if (!kctl)
-                               break;
-                       id = kctl->id;
-                       id.index = spdif_index;
-                       err = snd_ctl_rename_id(codec->card, &kctl->id, &id);
-                       if (err < 0)
-                               return err;
-               }
-               bus->primary_dig_out_type = HDA_PCM_TYPE_HDMI;
-       }
-       if (!bus->primary_dig_out_type)
-               bus->primary_dig_out_type = type;
-
-       idx = find_empty_mixer_ctl_idx(codec, "IEC958 Playback Switch", idx);
-       if (idx < 0) {
-               codec_err(codec, "too many IEC958 outputs\n");
-               return -EBUSY;
-       }
-       spdif = snd_array_new(&codec->spdif_out);
-       if (!spdif)
-               return -ENOMEM;
-       for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) {
-               kctl = snd_ctl_new1(dig_mix, codec);
-               if (!kctl)
-                       return -ENOMEM;
-               kctl->id.index = idx;
-               kctl->private_value = codec->spdif_out.used - 1;
-               err = snd_hda_ctl_add(codec, associated_nid, kctl);
-               if (err < 0)
-                       return err;
-       }
-       spdif->nid = cvt_nid;
-       snd_hdac_regmap_read(&codec->core, cvt_nid,
-                            AC_VERB_GET_DIGI_CONVERT_1, &val);
-       spdif->ctls = val;
-       spdif->status = convert_to_spdif_status(spdif->ctls);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_create_dig_out_ctls);
-
-/**
- * snd_hda_spdif_out_of_nid - get the hda_spdif_out entry from the given NID
- * @codec: the HDA codec
- * @nid: widget NID
- *
- * call within spdif_mutex lock
- */
-struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec,
-                                              hda_nid_t nid)
-{
-       struct hda_spdif_out *spdif;
-       int i;
-
-       snd_array_for_each(&codec->spdif_out, i, spdif) {
-               if (spdif->nid == nid)
-                       return spdif;
-       }
-       return NULL;
-}
-EXPORT_SYMBOL_GPL(snd_hda_spdif_out_of_nid);
-
-/**
- * snd_hda_spdif_ctls_unassign - Unassign the given SPDIF ctl
- * @codec: the HDA codec
- * @idx: the SPDIF ctl index
- *
- * Unassign the widget from the given SPDIF control.
- */
-void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx)
-{
-       struct hda_spdif_out *spdif;
-
-       if (WARN_ON(codec->spdif_out.used <= idx))
-               return;
-       mutex_lock(&codec->spdif_mutex);
-       spdif = snd_array_elem(&codec->spdif_out, idx);
-       spdif->nid = (u16)-1;
-       mutex_unlock(&codec->spdif_mutex);
-}
-EXPORT_SYMBOL_GPL(snd_hda_spdif_ctls_unassign);
-
-/**
- * snd_hda_spdif_ctls_assign - Assign the SPDIF controls to the given NID
- * @codec: the HDA codec
- * @idx: the SPDIF ctl idx
- * @nid: widget NID
- *
- * Assign the widget to the SPDIF control with the given index.
- */
-void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid)
-{
-       struct hda_spdif_out *spdif;
-       unsigned short val;
-
-       if (WARN_ON(codec->spdif_out.used <= idx))
-               return;
-       mutex_lock(&codec->spdif_mutex);
-       spdif = snd_array_elem(&codec->spdif_out, idx);
-       if (spdif->nid != nid) {
-               spdif->nid = nid;
-               val = spdif->ctls;
-               set_spdif_ctls(codec, nid, val & 0xff, (val >> 8) & 0xff);
-       }
-       mutex_unlock(&codec->spdif_mutex);
-}
-EXPORT_SYMBOL_GPL(snd_hda_spdif_ctls_assign);
-
-/*
- * SPDIF sharing with analog output
- */
-static int spdif_share_sw_get(struct snd_kcontrol *kcontrol,
-                             struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol);
-       ucontrol->value.integer.value[0] = mout->share_spdif;
-       return 0;
-}
-
-static int spdif_share_sw_put(struct snd_kcontrol *kcontrol,
-                             struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol);
-       mout->share_spdif = !!ucontrol->value.integer.value[0];
-       return 0;
-}
-
-static const struct snd_kcontrol_new spdif_share_sw = {
-       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-       .name = "IEC958 Default PCM Playback Switch",
-       .info = snd_ctl_boolean_mono_info,
-       .get = spdif_share_sw_get,
-       .put = spdif_share_sw_put,
-};
-
-/**
- * snd_hda_create_spdif_share_sw - create Default PCM switch
- * @codec: the HDA codec
- * @mout: multi-out instance
- */
-int snd_hda_create_spdif_share_sw(struct hda_codec *codec,
-                                 struct hda_multi_out *mout)
-{
-       struct snd_kcontrol *kctl;
-
-       if (!mout->dig_out_nid)
-               return 0;
-
-       kctl = snd_ctl_new1(&spdif_share_sw, mout);
-       if (!kctl)
-               return -ENOMEM;
-       /* ATTENTION: here mout is passed as private_data, instead of codec */
-       return snd_hda_ctl_add(codec, mout->dig_out_nid, kctl);
-}
-EXPORT_SYMBOL_GPL(snd_hda_create_spdif_share_sw);
-
-/*
- * SPDIF input
- */
-
-#define snd_hda_spdif_in_switch_info   snd_hda_spdif_out_switch_info
-
-static int snd_hda_spdif_in_switch_get(struct snd_kcontrol *kcontrol,
-                                      struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-
-       ucontrol->value.integer.value[0] = codec->spdif_in_enable;
-       return 0;
-}
-
-static int snd_hda_spdif_in_switch_put(struct snd_kcontrol *kcontrol,
-                                      struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       hda_nid_t nid = kcontrol->private_value;
-       unsigned int val = !!ucontrol->value.integer.value[0];
-       int change;
-
-       mutex_lock(&codec->spdif_mutex);
-       change = codec->spdif_in_enable != val;
-       if (change) {
-               codec->spdif_in_enable = val;
-               snd_hdac_regmap_write(&codec->core, nid,
-                                     AC_VERB_SET_DIGI_CONVERT_1, val);
-       }
-       mutex_unlock(&codec->spdif_mutex);
-       return change;
-}
-
-static int snd_hda_spdif_in_status_get(struct snd_kcontrol *kcontrol,
-                                      struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       hda_nid_t nid = kcontrol->private_value;
-       unsigned int val;
-       unsigned int sbits;
-
-       snd_hdac_regmap_read(&codec->core, nid,
-                            AC_VERB_GET_DIGI_CONVERT_1, &val);
-       sbits = convert_to_spdif_status(val);
-       ucontrol->value.iec958.status[0] = sbits;
-       ucontrol->value.iec958.status[1] = sbits >> 8;
-       ucontrol->value.iec958.status[2] = sbits >> 16;
-       ucontrol->value.iec958.status[3] = sbits >> 24;
-       return 0;
-}
-
-static const struct snd_kcontrol_new dig_in_ctls[] = {
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, SWITCH),
-               .info = snd_hda_spdif_in_switch_info,
-               .get = snd_hda_spdif_in_switch_get,
-               .put = snd_hda_spdif_in_switch_put,
-       },
-       {
-               .access = SNDRV_CTL_ELEM_ACCESS_READ,
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
-               .info = snd_hda_spdif_mask_info,
-               .get = snd_hda_spdif_in_status_get,
-       },
-       { } /* end */
-};
-
-/**
- * snd_hda_create_spdif_in_ctls - create Input SPDIF-related controls
- * @codec: the HDA codec
- * @nid: audio in widget NID
- *
- * Creates controls related with the SPDIF input.
- * Called from each patch supporting the SPDIF in.
- *
- * Returns 0 if successful, or a negative error code.
- */
-int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
-{
-       int err;
-       struct snd_kcontrol *kctl;
-       const struct snd_kcontrol_new *dig_mix;
-       int idx;
-
-       idx = find_empty_mixer_ctl_idx(codec, "IEC958 Capture Switch", 0);
-       if (idx < 0) {
-               codec_err(codec, "too many IEC958 inputs\n");
-               return -EBUSY;
-       }
-       for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) {
-               kctl = snd_ctl_new1(dig_mix, codec);
-               if (!kctl)
-                       return -ENOMEM;
-               kctl->private_value = nid;
-               err = snd_hda_ctl_add(codec, nid, kctl);
-               if (err < 0)
-                       return err;
-       }
-       codec->spdif_in_enable =
-               snd_hda_codec_read(codec, nid, 0,
-                                  AC_VERB_GET_DIGI_CONVERT_1, 0) &
-               AC_DIG1_ENABLE;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_create_spdif_in_ctls);
-
-/**
- * snd_hda_codec_set_power_to_all - Set the power state to all widgets
- * @codec: the HDA codec
- * @fg: function group (not used now)
- * @power_state: the power state to set (AC_PWRST_*)
- *
- * Set the given power state to all widgets that have the power control.
- * If the codec has power_filter set, it evaluates the power state and
- * filter out if it's unchanged as D3.
- */
-void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
-                                   unsigned int power_state)
-{
-       hda_nid_t nid;
-
-       for_each_hda_codec_node(nid, codec) {
-               unsigned int wcaps = get_wcaps(codec, nid);
-               unsigned int state = power_state;
-               if (!(wcaps & AC_WCAP_POWER))
-                       continue;
-               if (codec->power_filter) {
-                       state = codec->power_filter(codec, nid, power_state);
-                       if (state != power_state && power_state == AC_PWRST_D3)
-                               continue;
-               }
-               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE,
-                                   state);
-       }
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_set_power_to_all);
-
-/**
- * snd_hda_codec_eapd_power_filter - A power filter callback for EAPD
- * @codec: the HDA codec
- * @nid: widget NID
- * @power_state: power state to evalue
- *
- * Don't power down the widget if it controls eapd and EAPD_BTLENABLE is set.
- * This can be used a codec power_filter callback.
- */
-unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec,
-                                            hda_nid_t nid,
-                                            unsigned int power_state)
-{
-       if (nid == codec->core.afg || nid == codec->core.mfg)
-               return power_state;
-       if (power_state == AC_PWRST_D3 &&
-           get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_PIN &&
-           (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) {
-               int eapd = snd_hda_codec_read(codec, nid, 0,
-                                             AC_VERB_GET_EAPD_BTLENABLE, 0);
-               if (eapd & 0x02)
-                       return AC_PWRST_D0;
-       }
-       return power_state;
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_eapd_power_filter);
-
-/*
- * set power state of the codec, and return the power state
- */
-static unsigned int hda_set_power_state(struct hda_codec *codec,
-                                       unsigned int power_state)
-{
-       hda_nid_t fg = codec->core.afg ? codec->core.afg : codec->core.mfg;
-       int count;
-       unsigned int state;
-       int flags = 0;
-
-       /* this delay seems necessary to avoid click noise at power-down */
-       if (power_state == AC_PWRST_D3) {
-               if (codec->depop_delay < 0)
-                       msleep(codec_has_epss(codec) ? 10 : 100);
-               else if (codec->depop_delay > 0)
-                       msleep(codec->depop_delay);
-               flags = HDA_RW_NO_RESPONSE_FALLBACK;
-       }
-
-       /* repeat power states setting at most 10 times*/
-       for (count = 0; count < 10; count++) {
-               if (codec->patch_ops.set_power_state)
-                       codec->patch_ops.set_power_state(codec, fg,
-                                                        power_state);
-               else {
-                       state = power_state;
-                       if (codec->power_filter)
-                               state = codec->power_filter(codec, fg, state);
-                       if (state == power_state || power_state != AC_PWRST_D3)
-                               snd_hda_codec_read(codec, fg, flags,
-                                                  AC_VERB_SET_POWER_STATE,
-                                                  state);
-                       snd_hda_codec_set_power_to_all(codec, fg, power_state);
-               }
-               state = snd_hda_sync_power_state(codec, fg, power_state);
-               if (!(state & AC_PWRST_ERROR))
-                       break;
-       }
-
-       return state;
-}
-
-/* sync power states of all widgets;
- * this is called at the end of codec parsing
- */
-static void sync_power_up_states(struct hda_codec *codec)
-{
-       hda_nid_t nid;
-
-       /* don't care if no filter is used */
-       if (!codec->power_filter)
-               return;
-
-       for_each_hda_codec_node(nid, codec) {
-               unsigned int wcaps = get_wcaps(codec, nid);
-               unsigned int target;
-               if (!(wcaps & AC_WCAP_POWER))
-                       continue;
-               target = codec->power_filter(codec, nid, AC_PWRST_D0);
-               if (target == AC_PWRST_D0)
-                       continue;
-               if (!snd_hda_check_power_state(codec, nid, target))
-                       snd_hda_codec_write(codec, nid, 0,
-                                           AC_VERB_SET_POWER_STATE, target);
-       }
-}
-
-#ifdef CONFIG_SND_HDA_RECONFIG
-/* execute additional init verbs */
-static void hda_exec_init_verbs(struct hda_codec *codec)
-{
-       if (codec->init_verbs.list)
-               snd_hda_sequence_write(codec, codec->init_verbs.list);
-}
-#else
-static inline void hda_exec_init_verbs(struct hda_codec *codec) {}
-#endif
-
-/* update the power on/off account with the current jiffies */
-static void update_power_acct(struct hda_codec *codec, bool on)
-{
-       unsigned long delta = jiffies - codec->power_jiffies;
-
-       if (on)
-               codec->power_on_acct += delta;
-       else
-               codec->power_off_acct += delta;
-       codec->power_jiffies += delta;
-}
-
-void snd_hda_update_power_acct(struct hda_codec *codec)
-{
-       update_power_acct(codec, hda_codec_is_power_on(codec));
-}
-
-/*
- * call suspend and power-down; used both from PM and power-save
- * this function returns the power state in the end
- */
-static unsigned int hda_call_codec_suspend(struct hda_codec *codec)
-{
-       unsigned int state;
-
-       snd_hdac_enter_pm(&codec->core);
-       if (codec->patch_ops.suspend)
-               codec->patch_ops.suspend(codec);
-       if (!codec->no_stream_clean_at_suspend)
-               hda_cleanup_all_streams(codec);
-       state = hda_set_power_state(codec, AC_PWRST_D3);
-       update_power_acct(codec, true);
-       snd_hdac_leave_pm(&codec->core);
-       return state;
-}
-
-/*
- * kick up codec; used both from PM and power-save
- */
-static void hda_call_codec_resume(struct hda_codec *codec)
-{
-       snd_hdac_enter_pm(&codec->core);
-       if (codec->core.regmap)
-               regcache_mark_dirty(codec->core.regmap);
-
-       codec->power_jiffies = jiffies;
-
-       hda_set_power_state(codec, AC_PWRST_D0);
-       restore_shutup_pins(codec);
-       hda_exec_init_verbs(codec);
-       snd_hda_jack_set_dirty_all(codec);
-       if (codec->patch_ops.resume)
-               codec->patch_ops.resume(codec);
-       else {
-               if (codec->patch_ops.init)
-                       codec->patch_ops.init(codec);
-               snd_hda_regmap_sync(codec);
-       }
-
-       snd_hda_jack_report_sync(codec);
-       codec->core.dev.power.power_state = PMSG_ON;
-       snd_hdac_leave_pm(&codec->core);
-       if (codec->jackpoll_interval)
-               schedule_delayed_work(&codec->jackpoll_work,
-                                     codec->jackpoll_interval);
-}
-
-static int hda_codec_runtime_suspend(struct device *dev)
-{
-       struct hda_codec *codec = dev_to_hda_codec(dev);
-       unsigned int state;
-
-       /* Nothing to do if card registration fails and the component driver never probes */
-       if (!codec->card)
-               return 0;
-
-       state = hda_call_codec_suspend(codec);
-       if (codec->link_down_at_suspend ||
-           (codec_has_clkstop(codec) && codec_has_epss(codec) &&
-            (state & AC_PWRST_CLK_STOP_OK)))
-               snd_hdac_codec_link_down(&codec->core);
-       snd_hda_codec_display_power(codec, false);
-
-       return 0;
-}
-
-static int hda_codec_runtime_resume(struct device *dev)
-{
-       struct hda_codec *codec = dev_to_hda_codec(dev);
-
-       /* Nothing to do if card registration fails and the component driver never probes */
-       if (!codec->card)
-               return 0;
-
-       snd_hda_codec_display_power(codec, true);
-       snd_hdac_codec_link_up(&codec->core);
-       hda_call_codec_resume(codec);
-       pm_runtime_mark_last_busy(dev);
-       return 0;
-}
-
-static int hda_codec_runtime_idle(struct device *dev)
-{
-       struct hda_codec *codec = dev_to_hda_codec(dev);
-
-       if (codec->jackpoll_interval && !codec->bus->jackpoll_in_suspend)
-               return -EBUSY;
-       return 0;
-}
-
-static int hda_codec_pm_prepare(struct device *dev)
-{
-       struct hda_codec *codec = dev_to_hda_codec(dev);
-
-       cancel_delayed_work_sync(&codec->jackpoll_work);
-       dev->power.power_state = PMSG_SUSPEND;
-       return pm_runtime_suspended(dev);
-}
-
-static void hda_codec_pm_complete(struct device *dev)
-{
-       struct hda_codec *codec = dev_to_hda_codec(dev);
-
-       /* If no other pm-functions are called between prepare() and complete() */
-       if (dev->power.power_state.event == PM_EVENT_SUSPEND)
-               dev->power.power_state = PMSG_RESUME;
-
-       if (pm_runtime_suspended(dev) && (codec->jackpoll_interval ||
-           hda_codec_need_resume(codec) || codec->forced_resume))
-               pm_request_resume(dev);
-}
-
-static int hda_codec_pm_suspend(struct device *dev)
-{
-       dev->power.power_state = PMSG_SUSPEND;
-       return pm_runtime_force_suspend(dev);
-}
-
-static int hda_codec_pm_resume(struct device *dev)
-{
-       dev->power.power_state = PMSG_RESUME;
-       return pm_runtime_force_resume(dev);
-}
-
-static int hda_codec_pm_freeze(struct device *dev)
-{
-       struct hda_codec *codec = dev_to_hda_codec(dev);
-
-       cancel_delayed_work_sync(&codec->jackpoll_work);
-       dev->power.power_state = PMSG_FREEZE;
-       return pm_runtime_force_suspend(dev);
-}
-
-static int hda_codec_pm_thaw(struct device *dev)
-{
-       dev->power.power_state = PMSG_THAW;
-       return pm_runtime_force_resume(dev);
-}
-
-static int hda_codec_pm_restore(struct device *dev)
-{
-       dev->power.power_state = PMSG_RESTORE;
-       return pm_runtime_force_resume(dev);
-}
-
-/* referred in hda_bind.c */
-const struct dev_pm_ops hda_codec_driver_pm = {
-       .prepare = pm_sleep_ptr(hda_codec_pm_prepare),
-       .complete = pm_sleep_ptr(hda_codec_pm_complete),
-       .suspend = pm_sleep_ptr(hda_codec_pm_suspend),
-       .resume = pm_sleep_ptr(hda_codec_pm_resume),
-       .freeze = pm_sleep_ptr(hda_codec_pm_freeze),
-       .thaw = pm_sleep_ptr(hda_codec_pm_thaw),
-       .poweroff = pm_sleep_ptr(hda_codec_pm_suspend),
-       .restore = pm_sleep_ptr(hda_codec_pm_restore),
-       RUNTIME_PM_OPS(hda_codec_runtime_suspend, hda_codec_runtime_resume,
-                      hda_codec_runtime_idle)
-};
-
-/* suspend the codec at shutdown; called from driver's shutdown callback */
-void snd_hda_codec_shutdown(struct hda_codec *codec)
-{
-       struct hda_pcm *cpcm;
-
-       /* Skip the shutdown if codec is not registered */
-       if (!codec->core.registered)
-               return;
-
-       codec->jackpoll_interval = 0; /* don't poll any longer */
-       cancel_delayed_work_sync(&codec->jackpoll_work);
-       list_for_each_entry(cpcm, &codec->pcm_list_head, list)
-               snd_pcm_suspend_all(cpcm->pcm);
-
-       pm_runtime_force_suspend(hda_codec_dev(codec));
-       pm_runtime_disable(hda_codec_dev(codec));
-}
-
-/*
- * add standard channel maps if not specified
- */
-static int add_std_chmaps(struct hda_codec *codec)
-{
-       struct hda_pcm *pcm;
-       int str, err;
-
-       list_for_each_entry(pcm, &codec->pcm_list_head, list) {
-               for (str = 0; str < 2; str++) {
-                       struct hda_pcm_stream *hinfo = &pcm->stream[str];
-                       struct snd_pcm_chmap *chmap;
-                       const struct snd_pcm_chmap_elem *elem;
-
-                       if (!pcm->pcm || pcm->own_chmap || !hinfo->substreams)
-                               continue;
-                       elem = hinfo->chmap ? hinfo->chmap : snd_pcm_std_chmaps;
-                       err = snd_pcm_add_chmap_ctls(pcm->pcm, str, elem,
-                                                    hinfo->channels_max,
-                                                    0, &chmap);
-                       if (err < 0)
-                               return err;
-                       chmap->channel_mask = SND_PCM_CHMAP_MASK_2468;
-               }
-       }
-       return 0;
-}
-
-/* default channel maps for 2.1 speakers;
- * since HD-audio supports only stereo, odd number channels are omitted
- */
-const struct snd_pcm_chmap_elem snd_pcm_2_1_chmaps[] = {
-       { .channels = 2,
-         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
-       { .channels = 4,
-         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
-                  SNDRV_CHMAP_LFE, SNDRV_CHMAP_LFE } },
-       { }
-};
-EXPORT_SYMBOL_GPL(snd_pcm_2_1_chmaps);
-
-int snd_hda_codec_build_controls(struct hda_codec *codec)
-{
-       int err = 0;
-       hda_exec_init_verbs(codec);
-       /* continue to initialize... */
-       if (codec->patch_ops.init)
-               err = codec->patch_ops.init(codec);
-       if (!err && codec->patch_ops.build_controls)
-               err = codec->patch_ops.build_controls(codec);
-       if (err < 0)
-               return err;
-
-       /* we create chmaps here instead of build_pcms */
-       err = add_std_chmaps(codec);
-       if (err < 0)
-               return err;
-
-       snd_hda_jack_report_sync(codec); /* call at the last init point */
-       if (codec->jackpoll_interval)
-               schedule_delayed_work(&codec->jackpoll_work,
-                                     codec->jackpoll_interval);
-
-       sync_power_up_states(codec);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_build_controls);
-
-/*
- * PCM stuff
- */
-static int hda_pcm_default_open_close(struct hda_pcm_stream *hinfo,
-                                     struct hda_codec *codec,
-                                     struct snd_pcm_substream *substream)
-{
-       return 0;
-}
-
-static int hda_pcm_default_prepare(struct hda_pcm_stream *hinfo,
-                                  struct hda_codec *codec,
-                                  unsigned int stream_tag,
-                                  unsigned int format,
-                                  struct snd_pcm_substream *substream)
-{
-       snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format);
-       return 0;
-}
-
-static int hda_pcm_default_cleanup(struct hda_pcm_stream *hinfo,
-                                  struct hda_codec *codec,
-                                  struct snd_pcm_substream *substream)
-{
-       snd_hda_codec_cleanup_stream(codec, hinfo->nid);
-       return 0;
-}
-
-static int set_pcm_default_values(struct hda_codec *codec,
-                                 struct hda_pcm_stream *info)
-{
-       int err;
-
-       /* query support PCM information from the given NID */
-       if (info->nid && (!info->rates || !info->formats)) {
-               err = snd_hda_query_supported_pcm(codec, info->nid,
-                               info->rates ? NULL : &info->rates,
-                               info->formats ? NULL : &info->formats,
-                               info->subformats ? NULL : &info->subformats,
-                               info->maxbps ? NULL : &info->maxbps);
-               if (err < 0)
-                       return err;
-       }
-       if (info->ops.open == NULL)
-               info->ops.open = hda_pcm_default_open_close;
-       if (info->ops.close == NULL)
-               info->ops.close = hda_pcm_default_open_close;
-       if (info->ops.prepare == NULL) {
-               if (snd_BUG_ON(!info->nid))
-                       return -EINVAL;
-               info->ops.prepare = hda_pcm_default_prepare;
-       }
-       if (info->ops.cleanup == NULL) {
-               if (snd_BUG_ON(!info->nid))
-                       return -EINVAL;
-               info->ops.cleanup = hda_pcm_default_cleanup;
-       }
-       return 0;
-}
-
-/*
- * codec prepare/cleanup entries
- */
-/**
- * snd_hda_codec_prepare - Prepare a stream
- * @codec: the HDA codec
- * @hinfo: PCM information
- * @stream: stream tag to assign
- * @format: format id to assign
- * @substream: PCM substream to assign
- *
- * Calls the prepare callback set by the codec with the given arguments.
- * Clean up the inactive streams when successful.
- */
-int snd_hda_codec_prepare(struct hda_codec *codec,
-                         struct hda_pcm_stream *hinfo,
-                         unsigned int stream,
-                         unsigned int format,
-                         struct snd_pcm_substream *substream)
-{
-       int ret;
-       mutex_lock(&codec->bus->prepare_mutex);
-       if (hinfo->ops.prepare)
-               ret = hinfo->ops.prepare(hinfo, codec, stream, format,
-                                        substream);
-       else
-               ret = -ENODEV;
-       if (ret >= 0)
-               purify_inactive_streams(codec);
-       mutex_unlock(&codec->bus->prepare_mutex);
-       return ret;
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_prepare);
-
-/**
- * snd_hda_codec_cleanup - Clean up stream resources
- * @codec: the HDA codec
- * @hinfo: PCM information
- * @substream: PCM substream
- *
- * Calls the cleanup callback set by the codec with the given arguments.
- */
-void snd_hda_codec_cleanup(struct hda_codec *codec,
-                          struct hda_pcm_stream *hinfo,
-                          struct snd_pcm_substream *substream)
-{
-       mutex_lock(&codec->bus->prepare_mutex);
-       if (hinfo->ops.cleanup)
-               hinfo->ops.cleanup(hinfo, codec, substream);
-       mutex_unlock(&codec->bus->prepare_mutex);
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_cleanup);
-
-/* global */
-const char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = {
-       "Audio", "SPDIF", "HDMI", "Modem"
-};
-
-/*
- * get the empty PCM device number to assign
- */
-static int get_empty_pcm_device(struct hda_bus *bus, unsigned int type)
-{
-       /* audio device indices; not linear to keep compatibility */
-       /* assigned to static slots up to dev#10; if more needed, assign
-        * the later slot dynamically (when CONFIG_SND_DYNAMIC_MINORS=y)
-        */
-       static const int audio_idx[HDA_PCM_NTYPES][5] = {
-               [HDA_PCM_TYPE_AUDIO] = { 0, 2, 4, 5, -1 },
-               [HDA_PCM_TYPE_SPDIF] = { 1, -1 },
-               [HDA_PCM_TYPE_HDMI]  = { 3, 7, 8, 9, -1 },
-               [HDA_PCM_TYPE_MODEM] = { 6, -1 },
-       };
-       int i;
-
-       if (type >= HDA_PCM_NTYPES) {
-               dev_err(bus->card->dev, "Invalid PCM type %d\n", type);
-               return -EINVAL;
-       }
-
-       for (i = 0; audio_idx[type][i] >= 0; i++) {
-#ifndef CONFIG_SND_DYNAMIC_MINORS
-               if (audio_idx[type][i] >= 8)
-                       break;
-#endif
-               if (!test_and_set_bit(audio_idx[type][i], bus->pcm_dev_bits))
-                       return audio_idx[type][i];
-       }
-
-#ifdef CONFIG_SND_DYNAMIC_MINORS
-       /* non-fixed slots starting from 10 */
-       for (i = 10; i < 32; i++) {
-               if (!test_and_set_bit(i, bus->pcm_dev_bits))
-                       return i;
-       }
-#endif
-
-       dev_warn(bus->card->dev, "Too many %s devices\n",
-               snd_hda_pcm_type_name[type]);
-#ifndef CONFIG_SND_DYNAMIC_MINORS
-       dev_warn(bus->card->dev,
-                "Consider building the kernel with CONFIG_SND_DYNAMIC_MINORS=y\n");
-#endif
-       return -EAGAIN;
-}
-
-/* call build_pcms ops of the given codec and set up the default parameters */
-int snd_hda_codec_parse_pcms(struct hda_codec *codec)
-{
-       struct hda_pcm *cpcm;
-       int err;
-
-       if (!list_empty(&codec->pcm_list_head))
-               return 0; /* already parsed */
-
-       if (!codec->patch_ops.build_pcms)
-               return 0;
-
-       err = codec->patch_ops.build_pcms(codec);
-       if (err < 0) {
-               codec_err(codec, "cannot build PCMs for #%d (error %d)\n",
-                         codec->core.addr, err);
-               return err;
-       }
-
-       list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
-               int stream;
-
-               for_each_pcm_streams(stream) {
-                       struct hda_pcm_stream *info = &cpcm->stream[stream];
-
-                       if (!info->substreams)
-                               continue;
-                       err = set_pcm_default_values(codec, info);
-                       if (err < 0) {
-                               codec_warn(codec,
-                                          "fail to setup default for PCM %s\n",
-                                          cpcm->name);
-                               return err;
-                       }
-               }
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_parse_pcms);
-
-/* assign all PCMs of the given codec */
-int snd_hda_codec_build_pcms(struct hda_codec *codec)
-{
-       struct hda_bus *bus = codec->bus;
-       struct hda_pcm *cpcm;
-       int dev, err;
-
-       err = snd_hda_codec_parse_pcms(codec);
-       if (err < 0)
-               return err;
-
-       /* attach a new PCM streams */
-       list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
-               if (cpcm->pcm)
-                       continue; /* already attached */
-               if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams)
-                       continue; /* no substreams assigned */
-
-               dev = get_empty_pcm_device(bus, cpcm->pcm_type);
-               if (dev < 0) {
-                       cpcm->device = SNDRV_PCM_INVALID_DEVICE;
-                       continue; /* no fatal error */
-               }
-               cpcm->device = dev;
-               err =  snd_hda_attach_pcm_stream(bus, codec, cpcm);
-               if (err < 0) {
-                       codec_err(codec,
-                                 "cannot attach PCM stream %d for codec #%d\n",
-                                 dev, codec->core.addr);
-                       continue; /* no fatal error */
-               }
-       }
-
-       return 0;
-}
-
-/**
- * snd_hda_add_new_ctls - create controls from the array
- * @codec: the HDA codec
- * @knew: the array of struct snd_kcontrol_new
- *
- * This helper function creates and add new controls in the given array.
- * The array must be terminated with an empty entry as terminator.
- *
- * Returns 0 if successful, or a negative error code.
- */
-int snd_hda_add_new_ctls(struct hda_codec *codec,
-                        const struct snd_kcontrol_new *knew)
-{
-       int err;
-
-       for (; knew->name; knew++) {
-               struct snd_kcontrol *kctl;
-               int addr = 0, idx = 0;
-               if (knew->iface == (__force snd_ctl_elem_iface_t)-1)
-                       continue; /* skip this codec private value */
-               for (;;) {
-                       kctl = snd_ctl_new1(knew, codec);
-                       if (!kctl)
-                               return -ENOMEM;
-                       /* Do not use the id.device field for MIXER elements.
-                        * This field is for real device numbers (like PCM) but codecs
-                        * are hidden components from the user space view (unrelated
-                        * to the mixer element identification).
-                        */
-                       if (addr > 0 && codec->ctl_dev_id)
-                               kctl->id.device = addr;
-                       if (idx > 0)
-                               kctl->id.index = idx;
-                       err = snd_hda_ctl_add(codec, 0, kctl);
-                       if (!err)
-                               break;
-                       /* try first with another device index corresponding to
-                        * the codec addr; if it still fails (or it's the
-                        * primary codec), then try another control index
-                        */
-                       if (!addr && codec->core.addr) {
-                               addr = codec->core.addr;
-                               if (!codec->ctl_dev_id)
-                                       idx += 10 * addr;
-                       } else if (!idx && !knew->index) {
-                               idx = find_empty_mixer_ctl_idx(codec,
-                                                              knew->name, 0);
-                               if (idx <= 0)
-                                       return err;
-                       } else
-                               return err;
-               }
-       }
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_add_new_ctls);
-
-/**
- * snd_hda_codec_set_power_save - Configure codec's runtime PM
- * @codec: codec device to configure
- * @delay: autosuspend delay
- */
-void snd_hda_codec_set_power_save(struct hda_codec *codec, int delay)
-{
-       struct device *dev = hda_codec_dev(codec);
-
-       if (delay == 0 && codec->auto_runtime_pm)
-               delay = 3000;
-
-       if (delay > 0) {
-               pm_runtime_set_autosuspend_delay(dev, delay);
-               pm_runtime_use_autosuspend(dev);
-               pm_runtime_allow(dev);
-               if (!pm_runtime_suspended(dev))
-                       pm_runtime_mark_last_busy(dev);
-       } else {
-               pm_runtime_dont_use_autosuspend(dev);
-               pm_runtime_forbid(dev);
-       }
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_set_power_save);
-
-/**
- * snd_hda_set_power_save - reprogram autosuspend for the given delay
- * @bus: HD-audio bus
- * @delay: autosuspend delay in msec, 0 = off
- *
- * Synchronize the runtime PM autosuspend state from the power_save option.
- */
-void snd_hda_set_power_save(struct hda_bus *bus, int delay)
-{
-       struct hda_codec *c;
-
-       list_for_each_codec(c, bus)
-               snd_hda_codec_set_power_save(c, delay);
-}
-EXPORT_SYMBOL_GPL(snd_hda_set_power_save);
-
-/**
- * snd_hda_check_amp_list_power - Check the amp list and update the power
- * @codec: HD-audio codec
- * @check: the object containing an AMP list and the status
- * @nid: NID to check / update
- *
- * Check whether the given NID is in the amp list.  If it's in the list,
- * check the current AMP status, and update the power-status according
- * to the mute status.
- *
- * This function is supposed to be set or called from the check_power_status
- * patch ops.
- */
-int snd_hda_check_amp_list_power(struct hda_codec *codec,
-                                struct hda_loopback_check *check,
-                                hda_nid_t nid)
-{
-       const struct hda_amp_list *p;
-       int ch, v;
-
-       if (!check->amplist)
-               return 0;
-       for (p = check->amplist; p->nid; p++) {
-               if (p->nid == nid)
-                       break;
-       }
-       if (!p->nid)
-               return 0; /* nothing changed */
-
-       for (p = check->amplist; p->nid; p++) {
-               for (ch = 0; ch < 2; ch++) {
-                       v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
-                                                  p->idx);
-                       if (!(v & HDA_AMP_MUTE) && v > 0) {
-                               if (!check->power_on) {
-                                       check->power_on = 1;
-                                       snd_hda_power_up_pm(codec);
-                               }
-                               return 1;
-                       }
-               }
-       }
-       if (check->power_on) {
-               check->power_on = 0;
-               snd_hda_power_down_pm(codec);
-       }
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_check_amp_list_power);
-
-/*
- * input MUX helper
- */
-
-/**
- * snd_hda_input_mux_info - Info callback helper for the input-mux enum
- * @imux: imux helper object
- * @uinfo: pointer to get/store the data
- */
-int snd_hda_input_mux_info(const struct hda_input_mux *imux,
-                          struct snd_ctl_elem_info *uinfo)
-{
-       unsigned int index;
-
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-       uinfo->count = 1;
-       uinfo->value.enumerated.items = imux->num_items;
-       if (!imux->num_items)
-               return 0;
-       index = uinfo->value.enumerated.item;
-       if (index >= imux->num_items)
-               index = imux->num_items - 1;
-       strcpy(uinfo->value.enumerated.name, imux->items[index].label);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_input_mux_info);
-
-/**
- * snd_hda_input_mux_put - Put callback helper for the input-mux enum
- * @codec: the HDA codec
- * @imux: imux helper object
- * @ucontrol: pointer to get/store the data
- * @nid: input mux NID
- * @cur_val: pointer to get/store the current imux value
- */
-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)
-{
-       unsigned int idx;
-
-       if (!imux->num_items)
-               return 0;
-       idx = ucontrol->value.enumerated.item[0];
-       if (idx >= imux->num_items)
-               idx = imux->num_items - 1;
-       if (*cur_val == idx)
-               return 0;
-       snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_CONNECT_SEL,
-                                 imux->items[idx].index);
-       *cur_val = idx;
-       return 1;
-}
-EXPORT_SYMBOL_GPL(snd_hda_input_mux_put);
-
-
-/**
- * snd_hda_enum_helper_info - Helper for simple enum ctls
- * @kcontrol: ctl element
- * @uinfo: pointer to get/store the data
- * @num_items: number of enum items
- * @texts: enum item string array
- *
- * process kcontrol info callback of a simple string enum array
- * when @num_items is 0 or @texts is NULL, assume a boolean enum array
- */
-int snd_hda_enum_helper_info(struct snd_kcontrol *kcontrol,
-                            struct snd_ctl_elem_info *uinfo,
-                            int num_items, const char * const *texts)
-{
-       static const char * const texts_default[] = {
-               "Disabled", "Enabled"
-       };
-
-       if (!texts || !num_items) {
-               num_items = 2;
-               texts = texts_default;
-       }
-
-       return snd_ctl_enum_info(uinfo, 1, num_items, texts);
-}
-EXPORT_SYMBOL_GPL(snd_hda_enum_helper_info);
-
-/*
- * Multi-channel / digital-out PCM helper functions
- */
-
-/* setup SPDIF output stream */
-static void setup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid,
-                                unsigned int stream_tag, unsigned int format)
-{
-       struct hda_spdif_out *spdif;
-       unsigned int curr_fmt;
-       bool reset;
-
-       spdif = snd_hda_spdif_out_of_nid(codec, nid);
-       /* Add sanity check to pass klockwork check.
-        * This should never happen.
-        */
-       if (WARN_ON(spdif == NULL))
-               return;
-
-       curr_fmt = snd_hda_codec_read(codec, nid, 0,
-                                     AC_VERB_GET_STREAM_FORMAT, 0);
-       reset = codec->spdif_status_reset &&
-               (spdif->ctls & AC_DIG1_ENABLE) &&
-               curr_fmt != format;
-
-       /* turn off SPDIF if needed; otherwise the IEC958 bits won't be
-          updated */
-       if (reset)
-               set_dig_out_convert(codec, nid,
-                                   spdif->ctls & ~AC_DIG1_ENABLE & 0xff,
-                                   -1);
-       snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
-       if (codec->follower_dig_outs) {
-               const hda_nid_t *d;
-               for (d = codec->follower_dig_outs; *d; d++)
-                       snd_hda_codec_setup_stream(codec, *d, stream_tag, 0,
-                                                  format);
-       }
-       /* turn on again (if needed) */
-       if (reset)
-               set_dig_out_convert(codec, nid,
-                                   spdif->ctls & 0xff, -1);
-}
-
-static void cleanup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid)
-{
-       snd_hda_codec_cleanup_stream(codec, nid);
-       if (codec->follower_dig_outs) {
-               const hda_nid_t *d;
-               for (d = codec->follower_dig_outs; *d; d++)
-                       snd_hda_codec_cleanup_stream(codec, *d);
-       }
-}
-
-/**
- * snd_hda_multi_out_dig_open - open the digital out in the exclusive mode
- * @codec: the HDA codec
- * @mout: hda_multi_out object
- */
-int snd_hda_multi_out_dig_open(struct hda_codec *codec,
-                              struct hda_multi_out *mout)
-{
-       mutex_lock(&codec->spdif_mutex);
-       if (mout->dig_out_used == HDA_DIG_ANALOG_DUP)
-               /* already opened as analog dup; reset it once */
-               cleanup_dig_out_stream(codec, mout->dig_out_nid);
-       mout->dig_out_used = HDA_DIG_EXCLUSIVE;
-       mutex_unlock(&codec->spdif_mutex);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_open);
-
-/**
- * snd_hda_multi_out_dig_prepare - prepare the digital out stream
- * @codec: the HDA codec
- * @mout: hda_multi_out object
- * @stream_tag: stream tag to assign
- * @format: format id to assign
- * @substream: PCM substream to assign
- */
-int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
-                                 struct hda_multi_out *mout,
-                                 unsigned int stream_tag,
-                                 unsigned int format,
-                                 struct snd_pcm_substream *substream)
-{
-       mutex_lock(&codec->spdif_mutex);
-       setup_dig_out_stream(codec, mout->dig_out_nid, stream_tag, format);
-       mutex_unlock(&codec->spdif_mutex);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_prepare);
-
-/**
- * snd_hda_multi_out_dig_cleanup - clean-up the digital out stream
- * @codec: the HDA codec
- * @mout: hda_multi_out object
- */
-int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec,
-                                 struct hda_multi_out *mout)
-{
-       mutex_lock(&codec->spdif_mutex);
-       cleanup_dig_out_stream(codec, mout->dig_out_nid);
-       mutex_unlock(&codec->spdif_mutex);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_cleanup);
-
-/**
- * snd_hda_multi_out_dig_close - release the digital out stream
- * @codec: the HDA codec
- * @mout: hda_multi_out object
- */
-int snd_hda_multi_out_dig_close(struct hda_codec *codec,
-                               struct hda_multi_out *mout)
-{
-       mutex_lock(&codec->spdif_mutex);
-       mout->dig_out_used = 0;
-       mutex_unlock(&codec->spdif_mutex);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_close);
-
-/**
- * snd_hda_multi_out_analog_open - open analog outputs
- * @codec: the HDA codec
- * @mout: hda_multi_out object
- * @substream: PCM substream to assign
- * @hinfo: PCM information to assign
- *
- * Open analog outputs and set up the hw-constraints.
- * If the digital outputs can be opened as follower, open the digital
- * outputs, too.
- */
-int snd_hda_multi_out_analog_open(struct hda_codec *codec,
-                                 struct hda_multi_out *mout,
-                                 struct snd_pcm_substream *substream,
-                                 struct hda_pcm_stream *hinfo)
-{
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       runtime->hw.channels_max = mout->max_channels;
-       if (mout->dig_out_nid) {
-               if (!mout->analog_rates) {
-                       mout->analog_rates = hinfo->rates;
-                       mout->analog_formats = hinfo->formats;
-                       mout->analog_maxbps = hinfo->maxbps;
-               } else {
-                       runtime->hw.rates = mout->analog_rates;
-                       runtime->hw.formats = mout->analog_formats;
-                       hinfo->maxbps = mout->analog_maxbps;
-               }
-               if (!mout->spdif_rates) {
-                       snd_hda_query_supported_pcm(codec, mout->dig_out_nid,
-                                                   &mout->spdif_rates,
-                                                   &mout->spdif_formats,
-                                                   NULL,
-                                                   &mout->spdif_maxbps);
-               }
-               mutex_lock(&codec->spdif_mutex);
-               if (mout->share_spdif) {
-                       if ((runtime->hw.rates & mout->spdif_rates) &&
-                           (runtime->hw.formats & mout->spdif_formats)) {
-                               runtime->hw.rates &= mout->spdif_rates;
-                               runtime->hw.formats &= mout->spdif_formats;
-                               if (mout->spdif_maxbps < hinfo->maxbps)
-                                       hinfo->maxbps = mout->spdif_maxbps;
-                       } else {
-                               mout->share_spdif = 0;
-                               /* FIXME: need notify? */
-                       }
-               }
-               mutex_unlock(&codec->spdif_mutex);
-       }
-       return snd_pcm_hw_constraint_step(substream->runtime, 0,
-                                         SNDRV_PCM_HW_PARAM_CHANNELS, 2);
-}
-EXPORT_SYMBOL_GPL(snd_hda_multi_out_analog_open);
-
-/**
- * snd_hda_multi_out_analog_prepare - Preapre the analog outputs.
- * @codec: the HDA codec
- * @mout: hda_multi_out object
- * @stream_tag: stream tag to assign
- * @format: format id to assign
- * @substream: PCM substream to assign
- *
- * Set up the i/o for analog out.
- * When the digital out is available, copy the front out to digital out, too.
- */
-int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
-                                    struct hda_multi_out *mout,
-                                    unsigned int stream_tag,
-                                    unsigned int format,
-                                    struct snd_pcm_substream *substream)
-{
-       const hda_nid_t *nids = mout->dac_nids;
-       int chs = substream->runtime->channels;
-       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 && spdif != NULL &&
-                   snd_hda_is_supported_format(codec, mout->dig_out_nid,
-                                               format) &&
-                   !(spdif->status & IEC958_AES0_NONAUDIO)) {
-                       mout->dig_out_used = HDA_DIG_ANALOG_DUP;
-                       setup_dig_out_stream(codec, mout->dig_out_nid,
-                                            stream_tag, format);
-               } else {
-                       mout->dig_out_used = 0;
-                       cleanup_dig_out_stream(codec, mout->dig_out_nid);
-               }
-       }
-       mutex_unlock(&codec->spdif_mutex);
-
-       /* front */
-       snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag,
-                                  0, format);
-       if (!mout->no_share_stream &&
-           mout->hp_nid && mout->hp_nid != nids[HDA_FRONT])
-               /* headphone out will just decode front left/right (stereo) */
-               snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag,
-                                          0, format);
-       /* extra outputs copied from front */
-       for (i = 0; i < ARRAY_SIZE(mout->hp_out_nid); i++)
-               if (!mout->no_share_stream && mout->hp_out_nid[i])
-                       snd_hda_codec_setup_stream(codec,
-                                                  mout->hp_out_nid[i],
-                                                  stream_tag, 0, format);
-
-       /* surrounds */
-       for (i = 1; i < mout->num_dacs; i++) {
-               if (chs >= (i + 1) * 2) /* independent out */
-                       snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
-                                                  i * 2, format);
-               else if (!mout->no_share_stream) /* copy front */
-                       snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
-                                                  0, format);
-       }
-
-       /* extra surrounds */
-       for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) {
-               int ch = 0;
-               if (!mout->extra_out_nid[i])
-                       break;
-               if (chs >= (i + 1) * 2)
-                       ch = i * 2;
-               else if (!mout->no_share_stream)
-                       break;
-               snd_hda_codec_setup_stream(codec, mout->extra_out_nid[i],
-                                          stream_tag, ch, format);
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_multi_out_analog_prepare);
-
-/**
- * snd_hda_multi_out_analog_cleanup - clean up the setting for analog out
- * @codec: the HDA codec
- * @mout: hda_multi_out object
- */
-int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
-                                    struct hda_multi_out *mout)
-{
-       const hda_nid_t *nids = mout->dac_nids;
-       int i;
-
-       for (i = 0; i < mout->num_dacs; i++)
-               snd_hda_codec_cleanup_stream(codec, nids[i]);
-       if (mout->hp_nid)
-               snd_hda_codec_cleanup_stream(codec, mout->hp_nid);
-       for (i = 0; i < ARRAY_SIZE(mout->hp_out_nid); i++)
-               if (mout->hp_out_nid[i])
-                       snd_hda_codec_cleanup_stream(codec,
-                                                    mout->hp_out_nid[i]);
-       for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
-               if (mout->extra_out_nid[i])
-                       snd_hda_codec_cleanup_stream(codec,
-                                                    mout->extra_out_nid[i]);
-       mutex_lock(&codec->spdif_mutex);
-       if (mout->dig_out_nid && mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
-               cleanup_dig_out_stream(codec, mout->dig_out_nid);
-               mout->dig_out_used = 0;
-       }
-       mutex_unlock(&codec->spdif_mutex);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_multi_out_analog_cleanup);
-
-/**
- * snd_hda_get_default_vref - Get the default (mic) VREF pin bits
- * @codec: the HDA codec
- * @pin: referred pin NID
- *
- * 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_GPL(snd_hda_get_default_vref);
-
-/**
- * snd_hda_correct_pin_ctl - correct the pin ctl value for matching with the pin cap
- * @codec: the HDA codec
- * @pin: referred pin NID
- * @val: pin ctl value to audit
- */
-unsigned int snd_hda_correct_pin_ctl(struct hda_codec *codec,
-                                    hda_nid_t pin, unsigned int val)
-{
-       static const unsigned int cap_lists[][2] = {
-               { AC_PINCTL_VREF_100, AC_PINCAP_VREF_100 },
-               { AC_PINCTL_VREF_80, AC_PINCAP_VREF_80 },
-               { AC_PINCTL_VREF_50, AC_PINCAP_VREF_50 },
-               { AC_PINCTL_VREF_GRD, AC_PINCAP_VREF_GRD },
-       };
-       unsigned int cap;
-
-       if (!val)
-               return 0;
-       cap = snd_hda_query_pin_caps(codec, pin);
-       if (!cap)
-               return val; /* don't know what to do... */
-
-       if (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;
-       }
-
-       if (val & AC_PINCTL_IN_EN) {
-               if (!(cap & AC_PINCAP_IN))
-                       val &= ~(AC_PINCTL_IN_EN | AC_PINCTL_VREFEN);
-               else {
-                       unsigned int vcap, vref;
-                       int i;
-                       vcap = (cap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
-                       vref = val & AC_PINCTL_VREFEN;
-                       for (i = 0; i < ARRAY_SIZE(cap_lists); i++) {
-                               if (vref == cap_lists[i][0] &&
-                                   !(vcap & cap_lists[i][1])) {
-                                       if (i == ARRAY_SIZE(cap_lists) - 1)
-                                               vref = AC_PINCTL_VREF_HIZ;
-                                       else
-                                               vref = cap_lists[i + 1][0];
-                               }
-                       }
-                       val &= ~AC_PINCTL_VREFEN;
-                       val |= vref;
-               }
-       }
-
-       return val;
-}
-EXPORT_SYMBOL_GPL(snd_hda_correct_pin_ctl);
-
-/**
- * _snd_hda_set_pin_ctl - Helper to set pin ctl value
- * @codec: the HDA codec
- * @pin: referred pin NID
- * @val: pin control value to set
- * @cached: access over codec pinctl cache or direct write
- *
- * This function is a helper to set a pin ctl value more safely.
- * It corrects the pin ctl value via snd_hda_correct_pin_ctl(), stores the
- * value in pin target array via snd_hda_codec_set_pin_target(), then
- * actually writes the value via either snd_hda_codec_write_cache() or
- * snd_hda_codec_write() depending on @cached flag.
- */
-int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin,
-                        unsigned int val, bool cached)
-{
-       val = snd_hda_correct_pin_ctl(codec, pin, val);
-       snd_hda_codec_set_pin_target(codec, pin, val);
-       if (cached)
-               return snd_hda_codec_write_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_GPL(_snd_hda_set_pin_ctl);
-
-/**
- * snd_hda_add_imux_item - Add an item to input_mux
- * @codec: the HDA codec
- * @imux: imux helper object
- * @label: the name of imux item to assign
- * @index: index number of imux item to assign
- * @type_idx: pointer to store the resultant label index
- *
- * When the same label is used already in the existing items, the number
- * suffix is appended to the label.  This label index number is stored
- * to type_idx when non-NULL pointer is given.
- */
-int snd_hda_add_imux_item(struct hda_codec *codec,
-                         struct hda_input_mux *imux, const char *label,
-                         int index, int *type_idx)
-{
-       int i, label_idx = 0;
-       if (imux->num_items >= HDA_MAX_NUM_INPUTS) {
-               codec_err(codec, "hda_codec: Too many imux items!\n");
-               return -EINVAL;
-       }
-       for (i = 0; i < imux->num_items; i++) {
-               if (!strncmp(label, imux->items[i].label, strlen(label)))
-                       label_idx++;
-       }
-       if (type_idx)
-               *type_idx = label_idx;
-       if (label_idx > 0)
-               snprintf(imux->items[imux->num_items].label,
-                        sizeof(imux->items[imux->num_items].label),
-                        "%s %d", label, label_idx);
-       else
-               strscpy(imux->items[imux->num_items].label, label,
-                       sizeof(imux->items[imux->num_items].label));
-       imux->items[imux->num_items].index = index;
-       imux->num_items++;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_add_imux_item);
-
-/**
- * snd_hda_bus_reset_codecs - Reset the bus
- * @bus: HD-audio bus
- */
-void snd_hda_bus_reset_codecs(struct hda_bus *bus)
-{
-       struct hda_codec *codec;
-
-       list_for_each_codec(codec, bus) {
-               /* FIXME: maybe a better way needed for forced reset */
-               if (current_work() != &codec->jackpoll_work.work)
-                       cancel_delayed_work_sync(&codec->jackpoll_work);
-               if (hda_codec_is_power_on(codec)) {
-                       hda_call_codec_suspend(codec);
-                       hda_call_codec_resume(codec);
-               }
-       }
-}
-
-/**
- * snd_print_pcm_bits - Print the supported PCM fmt bits to the string buffer
- * @pcm: PCM caps bits
- * @buf: the string buffer to write
- * @buflen: the max buffer length
- *
- * used by hda_proc.c and hda_eld.c
- */
-void snd_print_pcm_bits(int pcm, char *buf, int buflen)
-{
-       static const unsigned int bits[] = { 8, 16, 20, 24, 32 };
-       int i, j;
-
-       for (i = 0, j = 0; i < ARRAY_SIZE(bits); i++)
-               if (pcm & (AC_SUPPCM_BITS_8 << i))
-                       j += scnprintf(buf + j, buflen - j,  " %d", bits[i]);
-
-       buf[j] = '\0'; /* necessary when j == 0 */
-}
-EXPORT_SYMBOL_GPL(snd_print_pcm_bits);
-
-MODULE_DESCRIPTION("HDA codec core");
-MODULE_LICENSE("GPL");
diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c
deleted file mode 100644 (file)
index f3330b7..0000000
+++ /dev/null
@@ -1,1336 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- *
- *  Implementation of primary alsa driver code base for Intel HD Audio.
- *
- *  Copyright(c) 2004 Intel Corporation
- *
- *  Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- *                     PeiSen Hou <pshou@realtek.com.tw>
- */
-
-#include <linux/clocksource.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/pm_runtime.h>
-#include <linux/slab.h>
-
-#ifdef CONFIG_X86
-/* for art-tsc conversion */
-#include <asm/tsc.h>
-#endif
-
-#include <sound/core.h>
-#include <sound/initval.h>
-#include <sound/pcm_params.h>
-#include "hda_controller.h"
-#include "hda_local.h"
-
-#define CREATE_TRACE_POINTS
-#include "hda_controller_trace.h"
-
-/* DSP lock helpers */
-#define dsp_lock(dev)          snd_hdac_dsp_lock(azx_stream(dev))
-#define dsp_unlock(dev)                snd_hdac_dsp_unlock(azx_stream(dev))
-#define dsp_is_locked(dev)     snd_hdac_stream_is_locked(azx_stream(dev))
-
-/* assign a stream for the PCM */
-static inline struct azx_dev *
-azx_assign_device(struct azx *chip, struct snd_pcm_substream *substream)
-{
-       struct hdac_stream *s;
-
-       s = snd_hdac_stream_assign(azx_bus(chip), substream);
-       if (!s)
-               return NULL;
-       return stream_to_azx_dev(s);
-}
-
-/* release the assigned stream */
-static inline void azx_release_device(struct azx_dev *azx_dev)
-{
-       snd_hdac_stream_release(azx_stream(azx_dev));
-}
-
-static inline struct hda_pcm_stream *
-to_hda_pcm_stream(struct snd_pcm_substream *substream)
-{
-       struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
-       return &apcm->info->stream[substream->stream];
-}
-
-static u64 azx_adjust_codec_delay(struct snd_pcm_substream *substream,
-                               u64 nsec)
-{
-       struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
-       struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
-       u64 codec_frames, codec_nsecs;
-
-       if (!hinfo->ops.get_delay)
-               return nsec;
-
-       codec_frames = hinfo->ops.get_delay(hinfo, apcm->codec, substream);
-       codec_nsecs = div_u64(codec_frames * 1000000000LL,
-                             substream->runtime->rate);
-
-       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
-               return nsec + codec_nsecs;
-
-       return (nsec > codec_nsecs) ? nsec - codec_nsecs : 0;
-}
-
-/*
- * PCM ops
- */
-
-static int azx_pcm_close(struct snd_pcm_substream *substream)
-{
-       struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
-       struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
-       struct azx *chip = apcm->chip;
-       struct azx_dev *azx_dev = get_azx_dev(substream);
-
-       trace_azx_pcm_close(chip, azx_dev);
-       mutex_lock(&chip->open_mutex);
-       azx_release_device(azx_dev);
-       if (hinfo->ops.close)
-               hinfo->ops.close(hinfo, apcm->codec, substream);
-       snd_hda_power_down(apcm->codec);
-       mutex_unlock(&chip->open_mutex);
-       snd_hda_codec_pcm_put(apcm->info);
-       return 0;
-}
-
-static int azx_pcm_hw_params(struct snd_pcm_substream *substream,
-                            struct snd_pcm_hw_params *hw_params)
-{
-       struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
-       struct azx *chip = apcm->chip;
-       struct azx_dev *azx_dev = get_azx_dev(substream);
-       struct hdac_stream *hdas = azx_stream(azx_dev);
-       int ret = 0;
-
-       trace_azx_pcm_hw_params(chip, azx_dev);
-       dsp_lock(azx_dev);
-       if (dsp_is_locked(azx_dev)) {
-               ret = -EBUSY;
-               goto unlock;
-       }
-
-       /* Set up BDLEs here, return -ENOMEM if too many BDLEs are required */
-       hdas->bufsize = params_buffer_bytes(hw_params);
-       hdas->period_bytes = params_period_bytes(hw_params);
-       hdas->format_val = 0;
-       hdas->no_period_wakeup =
-               (hw_params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) &&
-               (hw_params->flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP);
-       if (snd_hdac_stream_setup_periods(hdas) < 0)
-               ret = -ENOMEM;
-
-unlock:
-       dsp_unlock(azx_dev);
-       return ret;
-}
-
-static int azx_pcm_hw_free(struct snd_pcm_substream *substream)
-{
-       struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
-       struct azx_dev *azx_dev = get_azx_dev(substream);
-       struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
-
-       /* reset BDL address */
-       dsp_lock(azx_dev);
-       if (!dsp_is_locked(azx_dev))
-               snd_hdac_stream_cleanup(azx_stream(azx_dev));
-
-       snd_hda_codec_cleanup(apcm->codec, hinfo, substream);
-
-       azx_stream(azx_dev)->prepared = 0;
-       dsp_unlock(azx_dev);
-       return 0;
-}
-
-static int azx_pcm_prepare(struct snd_pcm_substream *substream)
-{
-       struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
-       struct azx *chip = apcm->chip;
-       struct azx_dev *azx_dev = get_azx_dev(substream);
-       struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       unsigned int format_val, stream_tag, bits;
-       int err;
-       struct hda_spdif_out *spdif =
-               snd_hda_spdif_out_of_nid(apcm->codec, hinfo->nid);
-       unsigned short ctls = spdif ? spdif->ctls : 0;
-
-       trace_azx_pcm_prepare(chip, azx_dev);
-       dsp_lock(azx_dev);
-       if (dsp_is_locked(azx_dev)) {
-               err = -EBUSY;
-               goto unlock;
-       }
-
-       snd_hdac_stream_reset(azx_stream(azx_dev));
-       bits = snd_hdac_stream_format_bits(runtime->format, SNDRV_PCM_SUBFORMAT_STD, hinfo->maxbps);
-
-       format_val = snd_hdac_spdif_stream_format(runtime->channels, bits, runtime->rate, ctls);
-       if (!format_val) {
-               dev_err(chip->card->dev,
-                       "invalid format_val, rate=%d, ch=%d, format=%d\n",
-                       runtime->rate, runtime->channels, runtime->format);
-               err = -EINVAL;
-               goto unlock;
-       }
-
-       err = snd_hdac_stream_set_params(azx_stream(azx_dev), format_val);
-       if (err < 0)
-               goto unlock;
-
-       snd_hdac_stream_setup(azx_stream(azx_dev), false);
-
-       stream_tag = azx_dev->core.stream_tag;
-       /* CA-IBG chips need the playback stream starting from 1 */
-       if ((chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND) &&
-           stream_tag > chip->capture_streams)
-               stream_tag -= chip->capture_streams;
-       err = snd_hda_codec_prepare(apcm->codec, hinfo, stream_tag,
-                                    azx_dev->core.format_val, substream);
-
- unlock:
-       if (!err)
-               azx_stream(azx_dev)->prepared = 1;
-       dsp_unlock(azx_dev);
-       return err;
-}
-
-static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
-       struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
-       struct azx *chip = apcm->chip;
-       struct hdac_bus *bus = azx_bus(chip);
-       struct azx_dev *azx_dev;
-       struct snd_pcm_substream *s;
-       struct hdac_stream *hstr;
-       bool start;
-       int sbits = 0;
-       int sync_reg;
-
-       azx_dev = get_azx_dev(substream);
-       trace_azx_pcm_trigger(chip, azx_dev, cmd);
-
-       hstr = azx_stream(azx_dev);
-       if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC)
-               sync_reg = AZX_REG_OLD_SSYNC;
-       else
-               sync_reg = AZX_REG_SSYNC;
-
-       if (dsp_is_locked(azx_dev) || !hstr->prepared)
-               return -EPIPE;
-
-       switch (cmd) {
-       case SNDRV_PCM_TRIGGER_START:
-       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-       case SNDRV_PCM_TRIGGER_RESUME:
-               start = true;
-               break;
-       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-       case SNDRV_PCM_TRIGGER_SUSPEND:
-       case SNDRV_PCM_TRIGGER_STOP:
-               start = false;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       snd_pcm_group_for_each_entry(s, substream) {
-               if (s->pcm->card != substream->pcm->card)
-                       continue;
-               azx_dev = get_azx_dev(s);
-               sbits |= 1 << azx_dev->core.index;
-               snd_pcm_trigger_done(s, substream);
-       }
-
-       spin_lock(&bus->reg_lock);
-
-       /* first, set SYNC bits of corresponding streams */
-       snd_hdac_stream_sync_trigger(hstr, true, sbits, sync_reg);
-
-       snd_pcm_group_for_each_entry(s, substream) {
-               if (s->pcm->card != substream->pcm->card)
-                       continue;
-               azx_dev = get_azx_dev(s);
-               if (start) {
-                       azx_dev->insufficient = 1;
-                       snd_hdac_stream_start(azx_stream(azx_dev));
-               } else {
-                       snd_hdac_stream_stop(azx_stream(azx_dev));
-               }
-       }
-       spin_unlock(&bus->reg_lock);
-
-       snd_hdac_stream_sync(hstr, start, sbits);
-
-       spin_lock(&bus->reg_lock);
-       /* reset SYNC bits */
-       snd_hdac_stream_sync_trigger(hstr, false, sbits, sync_reg);
-       snd_hdac_stream_timecounter_init(hstr, sbits, start);
-       spin_unlock(&bus->reg_lock);
-       return 0;
-}
-
-unsigned int azx_get_pos_lpib(struct azx *chip, struct azx_dev *azx_dev)
-{
-       return snd_hdac_stream_get_pos_lpib(azx_stream(azx_dev));
-}
-EXPORT_SYMBOL_GPL(azx_get_pos_lpib);
-
-unsigned int azx_get_pos_posbuf(struct azx *chip, struct azx_dev *azx_dev)
-{
-       return snd_hdac_stream_get_pos_posbuf(azx_stream(azx_dev));
-}
-EXPORT_SYMBOL_GPL(azx_get_pos_posbuf);
-
-unsigned int azx_get_position(struct azx *chip,
-                             struct azx_dev *azx_dev)
-{
-       struct snd_pcm_substream *substream = azx_dev->core.substream;
-       unsigned int pos;
-       int stream = substream->stream;
-       int delay = 0;
-
-       if (chip->get_position[stream])
-               pos = chip->get_position[stream](chip, azx_dev);
-       else /* use the position buffer as default */
-               pos = azx_get_pos_posbuf(chip, azx_dev);
-
-       if (pos >= azx_dev->core.bufsize)
-               pos = 0;
-
-       if (substream->runtime) {
-               struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
-               struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
-
-               if (chip->get_delay[stream])
-                       delay += chip->get_delay[stream](chip, azx_dev, pos);
-               if (hinfo->ops.get_delay)
-                       delay += hinfo->ops.get_delay(hinfo, apcm->codec,
-                                                     substream);
-               substream->runtime->delay = delay;
-       }
-
-       trace_azx_get_position(chip, azx_dev, pos, delay);
-       return pos;
-}
-EXPORT_SYMBOL_GPL(azx_get_position);
-
-static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream)
-{
-       struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
-       struct azx *chip = apcm->chip;
-       struct azx_dev *azx_dev = get_azx_dev(substream);
-       return bytes_to_frames(substream->runtime,
-                              azx_get_position(chip, azx_dev));
-}
-
-/*
- * azx_scale64: Scale base by mult/div while not overflowing sanely
- *
- * Derived from scale64_check_overflow in kernel/time/timekeeping.c
- *
- * The tmestamps for a 48Khz stream can overflow after (2^64/10^9)/48K which
- * is about 384307 ie ~4.5 days.
- *
- * This scales the calculation so that overflow will happen but after 2^64 /
- * 48000 secs, which is pretty large!
- *
- * In caln below:
- *     base may overflow, but since there isn’t any additional division
- *     performed on base it’s OK
- *     rem can’t overflow because both are 32-bit values
- */
-
-#ifdef CONFIG_X86
-static u64 azx_scale64(u64 base, u32 num, u32 den)
-{
-       u64 rem;
-
-       rem = do_div(base, den);
-
-       base *= num;
-       rem *= num;
-
-       do_div(rem, den);
-
-       return base + rem;
-}
-
-static int azx_get_sync_time(ktime_t *device,
-               struct system_counterval_t *system, void *ctx)
-{
-       struct snd_pcm_substream *substream = ctx;
-       struct azx_dev *azx_dev = get_azx_dev(substream);
-       struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
-       struct azx *chip = apcm->chip;
-       struct snd_pcm_runtime *runtime;
-       u64 ll_counter, ll_counter_l, ll_counter_h;
-       u64 tsc_counter, tsc_counter_l, tsc_counter_h;
-       u32 wallclk_ctr, wallclk_cycles;
-       bool direction;
-       u32 dma_select;
-       u32 timeout;
-       u32 retry_count = 0;
-
-       runtime = substream->runtime;
-
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               direction = 1;
-       else
-               direction = 0;
-
-       /* 0th stream tag is not used, so DMA ch 0 is for 1st stream tag */
-       do {
-               timeout = 100;
-               dma_select = (direction << GTSCC_CDMAS_DMA_DIR_SHIFT) |
-                                       (azx_dev->core.stream_tag - 1);
-               snd_hdac_chip_writel(azx_bus(chip), GTSCC, dma_select);
-
-               /* Enable the capture */
-               snd_hdac_chip_updatel(azx_bus(chip), GTSCC, 0, GTSCC_TSCCI_MASK);
-
-               while (timeout) {
-                       if (snd_hdac_chip_readl(azx_bus(chip), GTSCC) &
-                                               GTSCC_TSCCD_MASK)
-                               break;
-
-                       timeout--;
-               }
-
-               if (!timeout) {
-                       dev_err(chip->card->dev, "GTSCC capture Timedout!\n");
-                       return -EIO;
-               }
-
-               /* Read wall clock counter */
-               wallclk_ctr = snd_hdac_chip_readl(azx_bus(chip), WALFCC);
-
-               /* Read TSC counter */
-               tsc_counter_l = snd_hdac_chip_readl(azx_bus(chip), TSCCL);
-               tsc_counter_h = snd_hdac_chip_readl(azx_bus(chip), TSCCU);
-
-               /* Read Link counter */
-               ll_counter_l = snd_hdac_chip_readl(azx_bus(chip), LLPCL);
-               ll_counter_h = snd_hdac_chip_readl(azx_bus(chip), LLPCU);
-
-               /* Ack: registers read done */
-               snd_hdac_chip_writel(azx_bus(chip), GTSCC, GTSCC_TSCCD_SHIFT);
-
-               tsc_counter = (tsc_counter_h << TSCCU_CCU_SHIFT) |
-                                               tsc_counter_l;
-
-               ll_counter = (ll_counter_h << LLPC_CCU_SHIFT) | ll_counter_l;
-               wallclk_cycles = wallclk_ctr & WALFCC_CIF_MASK;
-
-               /*
-                * An error occurs near frame "rollover". The clocks in
-                * frame value indicates whether this error may have
-                * occurred. Here we use the value of 10 i.e.,
-                * HDA_MAX_CYCLE_OFFSET
-                */
-               if (wallclk_cycles < HDA_MAX_CYCLE_VALUE - HDA_MAX_CYCLE_OFFSET
-                                       && wallclk_cycles > HDA_MAX_CYCLE_OFFSET)
-                       break;
-
-               /*
-                * Sleep before we read again, else we may again get
-                * value near to MAX_CYCLE. Try to sleep for different
-                * amount of time so we dont hit the same number again
-                */
-               udelay(retry_count++);
-
-       } while (retry_count != HDA_MAX_CYCLE_READ_RETRY);
-
-       if (retry_count == HDA_MAX_CYCLE_READ_RETRY) {
-               dev_err_ratelimited(chip->card->dev,
-                       "Error in WALFCC cycle count\n");
-               return -EIO;
-       }
-
-       *device = ns_to_ktime(azx_scale64(ll_counter,
-                               NSEC_PER_SEC, runtime->rate));
-       *device = ktime_add_ns(*device, (wallclk_cycles * NSEC_PER_SEC) /
-                              ((HDA_MAX_CYCLE_VALUE + 1) * runtime->rate));
-
-       system->cycles = tsc_counter;
-       system->cs_id = CSID_X86_ART;
-
-       return 0;
-}
-
-#else
-static int azx_get_sync_time(ktime_t *device,
-               struct system_counterval_t *system, void *ctx)
-{
-       return -ENXIO;
-}
-#endif
-
-static int azx_get_crosststamp(struct snd_pcm_substream *substream,
-                             struct system_device_crosststamp *xtstamp)
-{
-       return get_device_system_crosststamp(azx_get_sync_time,
-                                       substream, NULL, xtstamp);
-}
-
-static inline bool is_link_time_supported(struct snd_pcm_runtime *runtime,
-                               struct snd_pcm_audio_tstamp_config *ts)
-{
-       if (runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME)
-               if (ts->type_requested == SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED)
-                       return true;
-
-       return false;
-}
-
-static int azx_get_time_info(struct snd_pcm_substream *substream,
-                       struct timespec64 *system_ts, struct timespec64 *audio_ts,
-                       struct snd_pcm_audio_tstamp_config *audio_tstamp_config,
-                       struct snd_pcm_audio_tstamp_report *audio_tstamp_report)
-{
-       struct azx_dev *azx_dev = get_azx_dev(substream);
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct system_device_crosststamp xtstamp;
-       int ret;
-       u64 nsec;
-
-       if ((substream->runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_ATIME) &&
-               (audio_tstamp_config->type_requested == SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK)) {
-
-               snd_pcm_gettime(substream->runtime, system_ts);
-
-               nsec = timecounter_read(&azx_dev->core.tc);
-               if (audio_tstamp_config->report_delay)
-                       nsec = azx_adjust_codec_delay(substream, nsec);
-
-               *audio_ts = ns_to_timespec64(nsec);
-
-               audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK;
-               audio_tstamp_report->accuracy_report = 1; /* rest of structure is valid */
-               audio_tstamp_report->accuracy = 42; /* 24 MHz WallClock == 42ns resolution */
-
-       } else if (is_link_time_supported(runtime, audio_tstamp_config)) {
-
-               ret = azx_get_crosststamp(substream, &xtstamp);
-               if (ret)
-                       return ret;
-
-               switch (runtime->tstamp_type) {
-               case SNDRV_PCM_TSTAMP_TYPE_MONOTONIC:
-                       return -EINVAL;
-
-               case SNDRV_PCM_TSTAMP_TYPE_MONOTONIC_RAW:
-                       *system_ts = ktime_to_timespec64(xtstamp.sys_monoraw);
-                       break;
-
-               default:
-                       *system_ts = ktime_to_timespec64(xtstamp.sys_realtime);
-                       break;
-
-               }
-
-               *audio_ts = ktime_to_timespec64(xtstamp.device);
-
-               audio_tstamp_report->actual_type =
-                       SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED;
-               audio_tstamp_report->accuracy_report = 1;
-               /* 24 MHz WallClock == 42ns resolution */
-               audio_tstamp_report->accuracy = 42;
-
-       } else {
-               audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT;
-       }
-
-       return 0;
-}
-
-static const struct snd_pcm_hardware azx_pcm_hw = {
-       .info =                 (SNDRV_PCM_INFO_MMAP |
-                                SNDRV_PCM_INFO_INTERLEAVED |
-                                SNDRV_PCM_INFO_BLOCK_TRANSFER |
-                                SNDRV_PCM_INFO_MMAP_VALID |
-                                /* No full-resume yet implemented */
-                                /* SNDRV_PCM_INFO_RESUME |*/
-                                SNDRV_PCM_INFO_PAUSE |
-                                SNDRV_PCM_INFO_SYNC_START |
-                                SNDRV_PCM_INFO_HAS_WALL_CLOCK | /* legacy */
-                                SNDRV_PCM_INFO_HAS_LINK_ATIME |
-                                SNDRV_PCM_INFO_NO_PERIOD_WAKEUP),
-       .formats =              SNDRV_PCM_FMTBIT_S16_LE,
-       .rates =                SNDRV_PCM_RATE_48000,
-       .rate_min =             48000,
-       .rate_max =             48000,
-       .channels_min =         2,
-       .channels_max =         2,
-       .buffer_bytes_max =     AZX_MAX_BUF_SIZE,
-       .period_bytes_min =     128,
-       .period_bytes_max =     AZX_MAX_BUF_SIZE / 2,
-       .periods_min =          2,
-       .periods_max =          AZX_MAX_FRAG,
-       .fifo_size =            0,
-};
-
-static int azx_pcm_open(struct snd_pcm_substream *substream)
-{
-       struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
-       struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
-       struct azx *chip = apcm->chip;
-       struct azx_dev *azx_dev;
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       int err;
-       int buff_step;
-
-       snd_hda_codec_pcm_get(apcm->info);
-       mutex_lock(&chip->open_mutex);
-       azx_dev = azx_assign_device(chip, substream);
-       trace_azx_pcm_open(chip, azx_dev);
-       if (azx_dev == NULL) {
-               err = -EBUSY;
-               goto unlock;
-       }
-       runtime->private_data = azx_dev;
-
-       runtime->hw = azx_pcm_hw;
-       if (chip->gts_present)
-               runtime->hw.info |= SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME;
-       runtime->hw.channels_min = hinfo->channels_min;
-       runtime->hw.channels_max = hinfo->channels_max;
-       runtime->hw.formats = hinfo->formats;
-       runtime->hw.rates = hinfo->rates;
-       snd_pcm_limit_hw_rates(runtime);
-       snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
-
-       /* avoid wrap-around with wall-clock */
-       snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_TIME,
-                                    20,
-                                    178000000);
-
-       if (chip->align_buffer_size)
-               /* constrain buffer sizes to be multiple of 128
-                  bytes. This is more efficient in terms of memory
-                  access but isn't required by the HDA spec and
-                  prevents users from specifying exact period/buffer
-                  sizes. For example for 44.1kHz, a period size set
-                  to 20ms will be rounded to 19.59ms. */
-               buff_step = 128;
-       else
-               /* Don't enforce steps on buffer sizes, still need to
-                  be multiple of 4 bytes (HDA spec). Tested on Intel
-                  HDA controllers, may not work on all devices where
-                  option needs to be disabled */
-               buff_step = 4;
-
-       snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
-                                  buff_step);
-       snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
-                                  buff_step);
-       snd_hda_power_up(apcm->codec);
-       if (hinfo->ops.open)
-               err = hinfo->ops.open(hinfo, apcm->codec, substream);
-       else
-               err = -ENODEV;
-       if (err < 0) {
-               azx_release_device(azx_dev);
-               goto powerdown;
-       }
-       snd_pcm_limit_hw_rates(runtime);
-       /* sanity check */
-       if (snd_BUG_ON(!runtime->hw.channels_min) ||
-           snd_BUG_ON(!runtime->hw.channels_max) ||
-           snd_BUG_ON(!runtime->hw.formats) ||
-           snd_BUG_ON(!runtime->hw.rates)) {
-               azx_release_device(azx_dev);
-               if (hinfo->ops.close)
-                       hinfo->ops.close(hinfo, apcm->codec, substream);
-               err = -EINVAL;
-               goto powerdown;
-       }
-
-       /* disable LINK_ATIME timestamps for capture streams
-          until we figure out how to handle digital inputs */
-       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
-               runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_WALL_CLOCK; /* legacy */
-               runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_LINK_ATIME;
-       }
-
-       snd_pcm_set_sync(substream);
-       mutex_unlock(&chip->open_mutex);
-       return 0;
-
- powerdown:
-       snd_hda_power_down(apcm->codec);
- unlock:
-       mutex_unlock(&chip->open_mutex);
-       snd_hda_codec_pcm_put(apcm->info);
-       return err;
-}
-
-static const struct snd_pcm_ops azx_pcm_ops = {
-       .open = azx_pcm_open,
-       .close = azx_pcm_close,
-       .hw_params = azx_pcm_hw_params,
-       .hw_free = azx_pcm_hw_free,
-       .prepare = azx_pcm_prepare,
-       .trigger = azx_pcm_trigger,
-       .pointer = azx_pcm_pointer,
-       .get_time_info =  azx_get_time_info,
-};
-
-static void azx_pcm_free(struct snd_pcm *pcm)
-{
-       struct azx_pcm *apcm = pcm->private_data;
-       if (apcm) {
-               list_del(&apcm->list);
-               apcm->info->pcm = NULL;
-               kfree(apcm);
-       }
-}
-
-#define MAX_PREALLOC_SIZE      (32 * 1024 * 1024)
-
-int snd_hda_attach_pcm_stream(struct hda_bus *_bus, struct hda_codec *codec,
-                             struct hda_pcm *cpcm)
-{
-       struct hdac_bus *bus = &_bus->core;
-       struct azx *chip = bus_to_azx(bus);
-       struct snd_pcm *pcm;
-       struct azx_pcm *apcm;
-       int pcm_dev = cpcm->device;
-       unsigned int size;
-       int s, err;
-       int type = SNDRV_DMA_TYPE_DEV_SG;
-
-       list_for_each_entry(apcm, &chip->pcm_list, list) {
-               if (apcm->pcm->device == pcm_dev) {
-                       dev_err(chip->card->dev, "PCM %d already exists\n",
-                               pcm_dev);
-                       return -EBUSY;
-               }
-       }
-       err = snd_pcm_new(chip->card, cpcm->name, pcm_dev,
-                         cpcm->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams,
-                         cpcm->stream[SNDRV_PCM_STREAM_CAPTURE].substreams,
-                         &pcm);
-       if (err < 0)
-               return err;
-       strscpy(pcm->name, cpcm->name, sizeof(pcm->name));
-       apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
-       if (apcm == NULL) {
-               snd_device_free(chip->card, pcm);
-               return -ENOMEM;
-       }
-       apcm->chip = chip;
-       apcm->pcm = pcm;
-       apcm->codec = codec;
-       apcm->info = cpcm;
-       pcm->private_data = apcm;
-       pcm->private_free = azx_pcm_free;
-       if (cpcm->pcm_type == HDA_PCM_TYPE_MODEM)
-               pcm->dev_class = SNDRV_PCM_CLASS_MODEM;
-       list_add_tail(&apcm->list, &chip->pcm_list);
-       cpcm->pcm = pcm;
-       for (s = 0; s < 2; s++) {
-               if (cpcm->stream[s].substreams)
-                       snd_pcm_set_ops(pcm, s, &azx_pcm_ops);
-       }
-       /* buffer pre-allocation */
-       size = CONFIG_SND_HDA_PREALLOC_SIZE * 1024;
-       if (size > MAX_PREALLOC_SIZE)
-               size = MAX_PREALLOC_SIZE;
-       if (chip->uc_buffer)
-               type = SNDRV_DMA_TYPE_DEV_WC_SG;
-       snd_pcm_set_managed_buffer_all(pcm, type, chip->card->dev,
-                                      size, MAX_PREALLOC_SIZE);
-       return 0;
-}
-
-static unsigned int azx_command_addr(u32 cmd)
-{
-       unsigned int addr = cmd >> 28;
-
-       if (addr >= AZX_MAX_CODECS) {
-               snd_BUG();
-               addr = 0;
-       }
-
-       return addr;
-}
-
-/* receive a response */
-static int azx_rirb_get_response(struct hdac_bus *bus, unsigned int addr,
-                                unsigned int *res)
-{
-       struct azx *chip = bus_to_azx(bus);
-       struct hda_bus *hbus = &chip->bus;
-       int err;
-
- again:
-       err = snd_hdac_bus_get_response(bus, addr, res);
-       if (!err)
-               return 0;
-
-       if (hbus->no_response_fallback)
-               return -EIO;
-
-       if (!bus->polling_mode) {
-               dev_warn(chip->card->dev,
-                        "azx_get_response timeout, switching to polling mode: last cmd=0x%08x\n",
-                        bus->last_cmd[addr]);
-               bus->polling_mode = 1;
-               goto again;
-       }
-
-       if (chip->msi) {
-               dev_warn(chip->card->dev,
-                        "No response from codec, disabling MSI: last cmd=0x%08x\n",
-                        bus->last_cmd[addr]);
-               if (chip->ops->disable_msi_reset_irq &&
-                   chip->ops->disable_msi_reset_irq(chip) < 0)
-                       return -EIO;
-               goto again;
-       }
-
-       if (chip->probing) {
-               /* If this critical timeout happens during the codec probing
-                * phase, this is likely an access to a non-existing codec
-                * slot.  Better to return an error and reset the system.
-                */
-               return -EIO;
-       }
-
-       /* no fallback mechanism? */
-       if (!chip->fallback_to_single_cmd)
-               return -EIO;
-
-       /* a fatal communication error; need either to reset or to fallback
-        * to the single_cmd mode
-        */
-       if (hbus->allow_bus_reset && !hbus->response_reset && !hbus->in_reset) {
-               hbus->response_reset = 1;
-               dev_err(chip->card->dev,
-                       "No response from codec, resetting bus: last cmd=0x%08x\n",
-                       bus->last_cmd[addr]);
-               return -EAGAIN; /* give a chance to retry */
-       }
-
-       dev_err(chip->card->dev,
-               "azx_get_response timeout, switching to single_cmd mode: last cmd=0x%08x\n",
-               bus->last_cmd[addr]);
-       chip->single_cmd = 1;
-       hbus->response_reset = 0;
-       snd_hdac_bus_stop_cmd_io(bus);
-       return -EIO;
-}
-
-/*
- * Use the single immediate command instead of CORB/RIRB for simplicity
- *
- * Note: according to Intel, this is not preferred use.  The command was
- *       intended for the BIOS only, and may get confused with unsolicited
- *       responses.  So, we shouldn't use it for normal operation from the
- *       driver.
- *       I left the codes, however, for debugging/testing purposes.
- */
-
-/* receive a response */
-static int azx_single_wait_for_response(struct azx *chip, unsigned int addr)
-{
-       int timeout = 50;
-
-       while (timeout--) {
-               /* check IRV busy bit */
-               if (azx_readw(chip, IRS) & AZX_IRS_VALID) {
-                       /* reuse rirb.res as the response return value */
-                       azx_bus(chip)->rirb.res[addr] = azx_readl(chip, IR);
-                       return 0;
-               }
-               udelay(1);
-       }
-       if (printk_ratelimit())
-               dev_dbg(chip->card->dev, "get_response timeout: IRS=0x%x\n",
-                       azx_readw(chip, IRS));
-       azx_bus(chip)->rirb.res[addr] = -1;
-       return -EIO;
-}
-
-/* send a command */
-static int azx_single_send_cmd(struct hdac_bus *bus, u32 val)
-{
-       struct azx *chip = bus_to_azx(bus);
-       unsigned int addr = azx_command_addr(val);
-       int timeout = 50;
-
-       bus->last_cmd[azx_command_addr(val)] = val;
-       while (timeout--) {
-               /* check ICB busy bit */
-               if (!((azx_readw(chip, IRS) & AZX_IRS_BUSY))) {
-                       /* Clear IRV valid bit */
-                       azx_writew(chip, IRS, azx_readw(chip, IRS) |
-                                  AZX_IRS_VALID);
-                       azx_writel(chip, IC, val);
-                       azx_writew(chip, IRS, azx_readw(chip, IRS) |
-                                  AZX_IRS_BUSY);
-                       return azx_single_wait_for_response(chip, addr);
-               }
-               udelay(1);
-       }
-       if (printk_ratelimit())
-               dev_dbg(chip->card->dev,
-                       "send_cmd timeout: IRS=0x%x, val=0x%x\n",
-                       azx_readw(chip, IRS), val);
-       return -EIO;
-}
-
-/* receive a response */
-static int azx_single_get_response(struct hdac_bus *bus, unsigned int addr,
-                                  unsigned int *res)
-{
-       if (res)
-               *res = bus->rirb.res[addr];
-       return 0;
-}
-
-/*
- * The below are the main callbacks from hda_codec.
- *
- * They are just the skeleton to call sub-callbacks according to the
- * current setting of chip->single_cmd.
- */
-
-/* send a command */
-static int azx_send_cmd(struct hdac_bus *bus, unsigned int val)
-{
-       struct azx *chip = bus_to_azx(bus);
-
-       if (chip->disabled)
-               return 0;
-       if (chip->single_cmd || bus->use_pio_for_commands)
-               return azx_single_send_cmd(bus, val);
-       else
-               return snd_hdac_bus_send_cmd(bus, val);
-}
-
-/* get a response */
-static int azx_get_response(struct hdac_bus *bus, unsigned int addr,
-                           unsigned int *res)
-{
-       struct azx *chip = bus_to_azx(bus);
-
-       if (chip->disabled)
-               return 0;
-       if (chip->single_cmd || bus->use_pio_for_commands)
-               return azx_single_get_response(bus, addr, res);
-       else
-               return azx_rirb_get_response(bus, addr, res);
-}
-
-static const struct hdac_bus_ops bus_core_ops = {
-       .command = azx_send_cmd,
-       .get_response = azx_get_response,
-};
-
-#ifdef CONFIG_SND_HDA_DSP_LOADER
-/*
- * DSP loading code (e.g. for CA0132)
- */
-
-/* use the first stream for loading DSP */
-static struct azx_dev *
-azx_get_dsp_loader_dev(struct azx *chip)
-{
-       struct hdac_bus *bus = azx_bus(chip);
-       struct hdac_stream *s;
-
-       list_for_each_entry(s, &bus->stream_list, list)
-               if (s->index == chip->playback_index_offset)
-                       return stream_to_azx_dev(s);
-
-       return NULL;
-}
-
-int snd_hda_codec_load_dsp_prepare(struct hda_codec *codec, unsigned int format,
-                                  unsigned int byte_size,
-                                  struct snd_dma_buffer *bufp)
-{
-       struct hdac_bus *bus = &codec->bus->core;
-       struct azx *chip = bus_to_azx(bus);
-       struct azx_dev *azx_dev;
-       struct hdac_stream *hstr;
-       bool saved = false;
-       int err;
-
-       azx_dev = azx_get_dsp_loader_dev(chip);
-       hstr = azx_stream(azx_dev);
-       spin_lock_irq(&bus->reg_lock);
-       if (hstr->opened) {
-               chip->saved_azx_dev = *azx_dev;
-               saved = true;
-       }
-       spin_unlock_irq(&bus->reg_lock);
-
-       err = snd_hdac_dsp_prepare(hstr, format, byte_size, bufp);
-       if (err < 0) {
-               spin_lock_irq(&bus->reg_lock);
-               if (saved)
-                       *azx_dev = chip->saved_azx_dev;
-               spin_unlock_irq(&bus->reg_lock);
-               return err;
-       }
-
-       hstr->prepared = 0;
-       return err;
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_load_dsp_prepare);
-
-void snd_hda_codec_load_dsp_trigger(struct hda_codec *codec, bool start)
-{
-       struct hdac_bus *bus = &codec->bus->core;
-       struct azx *chip = bus_to_azx(bus);
-       struct azx_dev *azx_dev = azx_get_dsp_loader_dev(chip);
-
-       snd_hdac_dsp_trigger(azx_stream(azx_dev), start);
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_load_dsp_trigger);
-
-void snd_hda_codec_load_dsp_cleanup(struct hda_codec *codec,
-                                   struct snd_dma_buffer *dmab)
-{
-       struct hdac_bus *bus = &codec->bus->core;
-       struct azx *chip = bus_to_azx(bus);
-       struct azx_dev *azx_dev = azx_get_dsp_loader_dev(chip);
-       struct hdac_stream *hstr = azx_stream(azx_dev);
-
-       if (!dmab->area || !hstr->locked)
-               return;
-
-       snd_hdac_dsp_cleanup(hstr, dmab);
-       spin_lock_irq(&bus->reg_lock);
-       if (hstr->opened)
-               *azx_dev = chip->saved_azx_dev;
-       hstr->locked = false;
-       spin_unlock_irq(&bus->reg_lock);
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_load_dsp_cleanup);
-#endif /* CONFIG_SND_HDA_DSP_LOADER */
-
-/*
- * reset and start the controller registers
- */
-void azx_init_chip(struct azx *chip, bool full_reset)
-{
-       if (snd_hdac_bus_init_chip(azx_bus(chip), full_reset)) {
-               /* correct RINTCNT for CXT */
-               if (chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND)
-                       azx_writew(chip, RINTCNT, 0xc0);
-       }
-}
-EXPORT_SYMBOL_GPL(azx_init_chip);
-
-void azx_stop_all_streams(struct azx *chip)
-{
-       struct hdac_bus *bus = azx_bus(chip);
-
-       snd_hdac_stop_streams(bus);
-}
-EXPORT_SYMBOL_GPL(azx_stop_all_streams);
-
-void azx_stop_chip(struct azx *chip)
-{
-       snd_hdac_bus_stop_chip(azx_bus(chip));
-}
-EXPORT_SYMBOL_GPL(azx_stop_chip);
-
-/*
- * interrupt handler
- */
-static void stream_update(struct hdac_bus *bus, struct hdac_stream *s)
-{
-       struct azx *chip = bus_to_azx(bus);
-       struct azx_dev *azx_dev = stream_to_azx_dev(s);
-
-       /* check whether this IRQ is really acceptable */
-       if (!chip->ops->position_check ||
-           chip->ops->position_check(chip, azx_dev)) {
-               spin_unlock(&bus->reg_lock);
-               snd_pcm_period_elapsed(azx_stream(azx_dev)->substream);
-               spin_lock(&bus->reg_lock);
-       }
-}
-
-irqreturn_t azx_interrupt(int irq, void *dev_id)
-{
-       struct azx *chip = dev_id;
-       struct hdac_bus *bus = azx_bus(chip);
-       u32 status;
-       bool active, handled = false;
-       int repeat = 0; /* count for avoiding endless loop */
-
-       if (azx_has_pm_runtime(chip))
-               if (!pm_runtime_active(chip->card->dev))
-                       return IRQ_NONE;
-
-       spin_lock(&bus->reg_lock);
-
-       if (chip->disabled)
-               goto unlock;
-
-       do {
-               status = azx_readl(chip, INTSTS);
-               if (status == 0 || status == 0xffffffff)
-                       break;
-
-               handled = true;
-               active = false;
-               if (snd_hdac_bus_handle_stream_irq(bus, status, stream_update))
-                       active = true;
-
-               status = azx_readb(chip, RIRBSTS);
-               if (status & RIRB_INT_MASK) {
-                       /*
-                        * Clearing the interrupt status here ensures that no
-                        * interrupt gets masked after the RIRB wp is read in
-                        * snd_hdac_bus_update_rirb. This avoids a possible
-                        * race condition where codec response in RIRB may
-                        * remain unserviced by IRQ, eventually falling back
-                        * to polling mode in azx_rirb_get_response.
-                        */
-                       azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
-                       active = true;
-                       if (status & RIRB_INT_RESPONSE) {
-                               if (chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND)
-                                       udelay(80);
-                               snd_hdac_bus_update_rirb(bus);
-                       }
-               }
-       } while (active && ++repeat < 10);
-
- unlock:
-       spin_unlock(&bus->reg_lock);
-
-       return IRQ_RETVAL(handled);
-}
-EXPORT_SYMBOL_GPL(azx_interrupt);
-
-/*
- * Codec initerface
- */
-
-/*
- * Probe the given codec address
- */
-static int probe_codec(struct azx *chip, int addr)
-{
-       unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) |
-               (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID;
-       struct hdac_bus *bus = azx_bus(chip);
-       int err;
-       unsigned int res = -1;
-
-       mutex_lock(&bus->cmd_mutex);
-       chip->probing = 1;
-       azx_send_cmd(bus, cmd);
-       err = azx_get_response(bus, addr, &res);
-       chip->probing = 0;
-       mutex_unlock(&bus->cmd_mutex);
-       if (err < 0 || res == -1)
-               return -EIO;
-       dev_dbg(chip->card->dev, "codec #%d probed OK\n", addr);
-       return 0;
-}
-
-void snd_hda_bus_reset(struct hda_bus *bus)
-{
-       struct azx *chip = bus_to_azx(&bus->core);
-
-       bus->in_reset = 1;
-       azx_stop_chip(chip);
-       azx_init_chip(chip, true);
-       if (bus->core.chip_init)
-               snd_hda_bus_reset_codecs(bus);
-       bus->in_reset = 0;
-}
-
-/* HD-audio bus initialization */
-int azx_bus_init(struct azx *chip, const char *model)
-{
-       struct hda_bus *bus = &chip->bus;
-       int err;
-
-       err = snd_hdac_bus_init(&bus->core, chip->card->dev, &bus_core_ops);
-       if (err < 0)
-               return err;
-
-       bus->card = chip->card;
-       mutex_init(&bus->prepare_mutex);
-       bus->pci = chip->pci;
-       bus->modelname = model;
-       bus->mixer_assigned = -1;
-       bus->core.snoop = azx_snoop(chip);
-       if (chip->get_position[0] != azx_get_pos_lpib ||
-           chip->get_position[1] != azx_get_pos_lpib)
-               bus->core.use_posbuf = true;
-       bus->core.bdl_pos_adj = chip->bdl_pos_adj;
-       if (chip->driver_caps & AZX_DCAPS_CORBRP_SELF_CLEAR)
-               bus->core.corbrp_self_clear = true;
-
-       if (chip->driver_caps & AZX_DCAPS_4K_BDLE_BOUNDARY)
-               bus->core.align_bdle_4k = true;
-
-       if (chip->driver_caps & AZX_DCAPS_PIO_COMMANDS)
-               bus->core.use_pio_for_commands = true;
-
-       /* enable sync_write flag for stable communication as default */
-       bus->core.sync_write = 1;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(azx_bus_init);
-
-/* Probe codecs */
-int azx_probe_codecs(struct azx *chip, unsigned int max_slots)
-{
-       struct hdac_bus *bus = azx_bus(chip);
-       int c, codecs, err;
-
-       codecs = 0;
-       if (!max_slots)
-               max_slots = AZX_DEFAULT_CODECS;
-
-       /* First try to probe all given codec slots */
-       for (c = 0; c < max_slots; c++) {
-               if ((bus->codec_mask & (1 << c)) & chip->codec_probe_mask) {
-                       if (probe_codec(chip, c) < 0) {
-                               /* Some BIOSen give you wrong codec addresses
-                                * that don't exist
-                                */
-                               dev_warn(chip->card->dev,
-                                        "Codec #%d probe error; disabling it...\n", c);
-                               bus->codec_mask &= ~(1 << c);
-                               /* no codecs */
-                               if (bus->codec_mask == 0)
-                                       break;
-                               /* More badly, accessing to a non-existing
-                                * codec often screws up the controller chip,
-                                * and disturbs the further communications.
-                                * Thus if an error occurs during probing,
-                                * better to reset the controller chip to
-                                * get back to the sanity state.
-                                */
-                               azx_stop_chip(chip);
-                               azx_init_chip(chip, true);
-                       }
-               }
-       }
-
-       /* Then create codec instances */
-       for (c = 0; c < max_slots; c++) {
-               if ((bus->codec_mask & (1 << c)) & chip->codec_probe_mask) {
-                       struct hda_codec *codec;
-                       err = snd_hda_codec_new(&chip->bus, chip->card, c, &codec);
-                       if (err < 0)
-                               continue;
-                       codec->jackpoll_interval = chip->jackpoll_interval;
-                       codec->beep_mode = chip->beep_mode;
-                       codec->ctl_dev_id = chip->ctl_dev_id;
-                       codecs++;
-               }
-       }
-       if (!codecs) {
-               dev_err(chip->card->dev, "no codecs initialized\n");
-               return -ENXIO;
-       }
-       return 0;
-}
-EXPORT_SYMBOL_GPL(azx_probe_codecs);
-
-/* configure each codec instance */
-int azx_codec_configure(struct azx *chip)
-{
-       struct hda_codec *codec, *next;
-       int success = 0;
-
-       list_for_each_codec(codec, &chip->bus) {
-               if (!snd_hda_codec_configure(codec))
-                       success++;
-       }
-
-       if (success) {
-               /* unregister failed codecs if any codec has been probed */
-               list_for_each_codec_safe(codec, next, &chip->bus) {
-                       if (!codec->configured) {
-                               codec_err(codec, "Unable to configure, disabling\n");
-                               snd_hdac_device_unregister(&codec->core);
-                       }
-               }
-       }
-
-       return success ? 0 : -ENODEV;
-}
-EXPORT_SYMBOL_GPL(azx_codec_configure);
-
-static int stream_direction(struct azx *chip, unsigned char index)
-{
-       if (index >= chip->capture_index_offset &&
-           index < chip->capture_index_offset + chip->capture_streams)
-               return SNDRV_PCM_STREAM_CAPTURE;
-       return SNDRV_PCM_STREAM_PLAYBACK;
-}
-
-/* initialize SD streams */
-int azx_init_streams(struct azx *chip)
-{
-       int i;
-       int stream_tags[2] = { 0, 0 };
-
-       /* initialize each stream (aka device)
-        * assign the starting bdl address to each stream (device)
-        * and initialize
-        */
-       for (i = 0; i < chip->num_streams; i++) {
-               struct azx_dev *azx_dev = kzalloc(sizeof(*azx_dev), GFP_KERNEL);
-               int dir, tag;
-
-               if (!azx_dev)
-                       return -ENOMEM;
-
-               dir = stream_direction(chip, i);
-               /* stream tag must be unique throughout
-                * the stream direction group,
-                * valid values 1...15
-                * use separate stream tag if the flag
-                * AZX_DCAPS_SEPARATE_STREAM_TAG is used
-                */
-               if (chip->driver_caps & AZX_DCAPS_SEPARATE_STREAM_TAG)
-                       tag = ++stream_tags[dir];
-               else
-                       tag = i + 1;
-               snd_hdac_stream_init(azx_bus(chip), azx_stream(azx_dev),
-                                    i, dir, tag);
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(azx_init_streams);
-
-void azx_free_streams(struct azx *chip)
-{
-       struct hdac_bus *bus = azx_bus(chip);
-       struct hdac_stream *s;
-
-       while (!list_empty(&bus->stream_list)) {
-               s = list_first_entry(&bus->stream_list, struct hdac_stream, list);
-               list_del(&s->list);
-               kfree(stream_to_azx_dev(s));
-       }
-}
-EXPORT_SYMBOL_GPL(azx_free_streams);
diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h
deleted file mode 100644 (file)
index c2d0109..0000000
+++ /dev/null
@@ -1,215 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- *  Common functionality for the alsa driver code base for HD Audio.
- */
-
-#ifndef __SOUND_HDA_CONTROLLER_H
-#define __SOUND_HDA_CONTROLLER_H
-
-#include <linux/timecounter.h>
-#include <linux/interrupt.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/initval.h>
-#include <sound/hda_codec.h>
-#include <sound/hda_register.h>
-
-#define AZX_MAX_CODECS         HDA_MAX_CODECS
-#define AZX_DEFAULT_CODECS     4
-
-/* driver quirks (capabilities) */
-/* bits 0-7 are used for indicating driver type */
-#define AZX_DCAPS_NO_TCSEL     (1 << 8)        /* No Intel TCSEL bit */
-#define AZX_DCAPS_NO_MSI       (1 << 9)        /* No MSI support */
-#define AZX_DCAPS_SNOOP_MASK   (3 << 10)       /* snoop type mask */
-#define AZX_DCAPS_SNOOP_OFF    (1 << 12)       /* snoop default off */
-#ifdef CONFIG_SND_HDA_I915
-#define AZX_DCAPS_I915_COMPONENT (1 << 13)     /* bind with i915 gfx */
-#else
-#define AZX_DCAPS_I915_COMPONENT 0             /* NOP */
-#endif
-/* 14 unused */
-#define AZX_DCAPS_CTX_WORKAROUND (1 << 15)     /* X-Fi workaround */
-#define AZX_DCAPS_POSFIX_LPIB  (1 << 16)       /* Use LPIB as default */
-#define AZX_DCAPS_AMD_WORKAROUND (1 << 17)     /* AMD-specific workaround */
-#define AZX_DCAPS_NO_64BIT     (1 << 18)       /* No 64bit address */
-/* 19 unused */
-#define AZX_DCAPS_OLD_SSYNC    (1 << 20)       /* Old SSYNC reg for ICH */
-#define AZX_DCAPS_NO_ALIGN_BUFSIZE (1 << 21)   /* no buffer size alignment */
-/* 22 unused */
-#define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23)   /* BDLE in 4k boundary */
-/* 24 unused */
-#define AZX_DCAPS_COUNT_LPIB_DELAY  (1 << 25)  /* Take LPIB as delay */
-#define AZX_DCAPS_PM_RUNTIME   (1 << 26)       /* runtime PM support */
-#define AZX_DCAPS_RETRY_PROBE  (1 << 27)       /* retry probe if no codec is configured */
-#define AZX_DCAPS_CORBRP_SELF_CLEAR (1 << 28)  /* CORBRP clears itself after reset */
-#define AZX_DCAPS_NO_MSI64      (1 << 29)      /* Stick to 32-bit MSIs */
-#define AZX_DCAPS_SEPARATE_STREAM_TAG  (1 << 30) /* capture and playback use separate stream tag */
-#define AZX_DCAPS_PIO_COMMANDS (1 << 31)       /* Use PIO instead of CORB for commands */
-
-enum {
-       AZX_SNOOP_TYPE_NONE,
-       AZX_SNOOP_TYPE_SCH,
-       AZX_SNOOP_TYPE_ATI,
-       AZX_SNOOP_TYPE_NVIDIA,
-};
-
-struct azx_dev {
-       struct hdac_stream core;
-
-       unsigned int irq_pending:1;
-       /*
-        * For VIA:
-        *  A flag to ensure DMA position is 0
-        *  when link position is not greater than FIFO size
-        */
-       unsigned int insufficient:1;
-};
-
-#define azx_stream(dev)                (&(dev)->core)
-#define stream_to_azx_dev(s)   container_of(s, struct azx_dev, core)
-
-struct azx;
-
-/* Functions to read/write to hda registers. */
-struct hda_controller_ops {
-       /* Disable msi if supported, PCI only */
-       int (*disable_msi_reset_irq)(struct azx *);
-       /* Check if current position is acceptable */
-       int (*position_check)(struct azx *chip, struct azx_dev *azx_dev);
-       /* enable/disable the link power */
-       int (*link_power)(struct azx *chip, bool enable);
-};
-
-struct azx_pcm {
-       struct azx *chip;
-       struct snd_pcm *pcm;
-       struct hda_codec *codec;
-       struct hda_pcm *info;
-       struct list_head list;
-};
-
-typedef unsigned int (*azx_get_pos_callback_t)(struct azx *, struct azx_dev *);
-typedef int (*azx_get_delay_callback_t)(struct azx *, struct azx_dev *, unsigned int pos);
-
-struct azx {
-       struct hda_bus bus;
-
-       struct snd_card *card;
-       struct pci_dev *pci;
-       int dev_index;
-
-       /* chip type specific */
-       int driver_type;
-       unsigned int driver_caps;
-       int playback_streams;
-       int playback_index_offset;
-       int capture_streams;
-       int capture_index_offset;
-       int num_streams;
-       int jackpoll_interval; /* jack poll interval in jiffies */
-
-       /* Register interaction. */
-       const struct hda_controller_ops *ops;
-
-       /* position adjustment callbacks */
-       azx_get_pos_callback_t get_position[2];
-       azx_get_delay_callback_t get_delay[2];
-
-       /* locks */
-       struct mutex open_mutex; /* Prevents concurrent open/close operations */
-
-       /* PCM */
-       struct list_head pcm_list; /* azx_pcm list */
-
-       /* HD codec */
-       int  codec_probe_mask; /* copied from probe_mask option */
-       unsigned int beep_mode;
-       bool ctl_dev_id;
-
-#ifdef CONFIG_SND_HDA_PATCH_LOADER
-       const struct firmware *fw;
-#endif
-
-       /* flags */
-       int bdl_pos_adj;
-       unsigned int running:1;
-       unsigned int fallback_to_single_cmd:1;
-       unsigned int single_cmd:1;
-       unsigned int msi:1;
-       unsigned int probing:1; /* codec probing phase */
-       unsigned int snoop:1;
-       unsigned int uc_buffer:1; /* non-cached pages for stream buffers */
-       unsigned int align_buffer_size:1;
-       unsigned int disabled:1; /* disabled by vga_switcheroo */
-       unsigned int pm_prepared:1;
-
-       /* GTS present */
-       unsigned int gts_present:1;
-
-#ifdef CONFIG_SND_HDA_DSP_LOADER
-       struct azx_dev saved_azx_dev;
-#endif
-};
-
-#define azx_bus(chip)  (&(chip)->bus.core)
-#define bus_to_azx(_bus)       container_of(_bus, struct azx, bus.core)
-
-static inline bool azx_snoop(struct azx *chip)
-{
-       return !IS_ENABLED(CONFIG_X86) || chip->snoop;
-}
-
-/*
- * macros for easy use
- */
-
-#define azx_writel(chip, reg, value) \
-       snd_hdac_chip_writel(azx_bus(chip), reg, value)
-#define azx_readl(chip, reg) \
-       snd_hdac_chip_readl(azx_bus(chip), reg)
-#define azx_writew(chip, reg, value) \
-       snd_hdac_chip_writew(azx_bus(chip), reg, value)
-#define azx_readw(chip, reg) \
-       snd_hdac_chip_readw(azx_bus(chip), reg)
-#define azx_writeb(chip, reg, value) \
-       snd_hdac_chip_writeb(azx_bus(chip), reg, value)
-#define azx_readb(chip, reg) \
-       snd_hdac_chip_readb(azx_bus(chip), reg)
-
-#define azx_has_pm_runtime(chip) \
-       ((chip)->driver_caps & AZX_DCAPS_PM_RUNTIME)
-
-/* PCM setup */
-static inline struct azx_dev *get_azx_dev(struct snd_pcm_substream *substream)
-{
-       return substream->runtime->private_data;
-}
-unsigned int azx_get_position(struct azx *chip, struct azx_dev *azx_dev);
-unsigned int azx_get_pos_lpib(struct azx *chip, struct azx_dev *azx_dev);
-unsigned int azx_get_pos_posbuf(struct azx *chip, struct azx_dev *azx_dev);
-
-/* Stream control. */
-void azx_stop_all_streams(struct azx *chip);
-
-/* Allocation functions. */
-#define azx_alloc_stream_pages(chip) \
-       snd_hdac_bus_alloc_stream_pages(azx_bus(chip))
-#define azx_free_stream_pages(chip) \
-       snd_hdac_bus_free_stream_pages(azx_bus(chip))
-
-/* Low level azx interface */
-void azx_init_chip(struct azx *chip, bool full_reset);
-void azx_stop_chip(struct azx *chip);
-#define azx_enter_link_reset(chip) \
-       snd_hdac_bus_enter_link_reset(azx_bus(chip))
-irqreturn_t azx_interrupt(int irq, void *dev_id);
-
-/* Codec interface */
-int azx_bus_init(struct azx *chip, const char *model);
-int azx_probe_codecs(struct azx *chip, unsigned int max_slots);
-int azx_codec_configure(struct azx *chip);
-int azx_init_streams(struct azx *chip);
-void azx_free_streams(struct azx *chip);
-
-#endif /* __SOUND_HDA_CONTROLLER_H */
diff --git a/sound/pci/hda/hda_controller_trace.h b/sound/pci/hda/hda_controller_trace.h
deleted file mode 100644 (file)
index bf48304..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#undef TRACE_SYSTEM
-#define TRACE_SYSTEM hda_controller
-#define TRACE_INCLUDE_FILE hda_controller_trace
-
-#if !defined(_TRACE_HDA_CONTROLLER_H) || defined(TRACE_HEADER_MULTI_READ)
-#define _TRACE_HDA_CONTROLLER_H
-
-#include <linux/tracepoint.h>
-
-struct azx;
-struct azx_dev;
-
-TRACE_EVENT(azx_pcm_trigger,
-
-       TP_PROTO(struct azx *chip, struct azx_dev *dev, int cmd),
-
-       TP_ARGS(chip, dev, cmd),
-
-       TP_STRUCT__entry(
-               __field( int, card )
-               __field( int, idx )
-               __field( int, cmd )
-       ),
-
-       TP_fast_assign(
-               __entry->card = (chip)->card->number;
-               __entry->idx = (dev)->core.index;
-               __entry->cmd = cmd;
-       ),
-
-       TP_printk("[%d:%d] cmd=%d", __entry->card, __entry->idx, __entry->cmd)
-);
-
-TRACE_EVENT(azx_get_position,
-
-    TP_PROTO(struct azx *chip, struct azx_dev *dev, unsigned int pos, unsigned int delay),
-
-           TP_ARGS(chip, dev, pos, delay),
-
-       TP_STRUCT__entry(
-               __field( int, card )
-               __field( int, idx )
-               __field( unsigned int, pos )
-               __field( unsigned int, delay )
-       ),
-
-       TP_fast_assign(
-               __entry->card = (chip)->card->number;
-               __entry->idx = (dev)->core.index;
-               __entry->pos = pos;
-               __entry->delay = delay;
-       ),
-
-       TP_printk("[%d:%d] pos=%u, delay=%u", __entry->card, __entry->idx, __entry->pos, __entry->delay)
-);
-
-DECLARE_EVENT_CLASS(azx_pcm,
-       TP_PROTO(struct azx *chip, struct azx_dev *azx_dev),
-
-       TP_ARGS(chip, azx_dev),
-
-       TP_STRUCT__entry(
-               __field( unsigned char, stream_tag )
-       ),
-
-       TP_fast_assign(
-               __entry->stream_tag = (azx_dev)->core.stream_tag;
-       ),
-
-       TP_printk("stream_tag: %d", __entry->stream_tag)
-);
-
-DEFINE_EVENT(azx_pcm, azx_pcm_open,
-       TP_PROTO(struct azx *chip, struct azx_dev *azx_dev),
-       TP_ARGS(chip, azx_dev)
-);
-
-DEFINE_EVENT(azx_pcm, azx_pcm_close,
-       TP_PROTO(struct azx *chip, struct azx_dev *azx_dev),
-       TP_ARGS(chip, azx_dev)
-);
-
-DEFINE_EVENT(azx_pcm, azx_pcm_hw_params,
-       TP_PROTO(struct azx *chip, struct azx_dev *azx_dev),
-       TP_ARGS(chip, azx_dev)
-);
-
-DEFINE_EVENT(azx_pcm, azx_pcm_prepare,
-       TP_PROTO(struct azx *chip, struct azx_dev *azx_dev),
-       TP_ARGS(chip, azx_dev)
-);
-
-#endif /* _TRACE_HDA_CONTROLLER_H */
-
-/* This part must be outside protection */
-#undef TRACE_INCLUDE_PATH
-#define TRACE_INCLUDE_PATH .
-#include <trace/define_trace.h>
diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c
deleted file mode 100644 (file)
index 9325e5c..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * HWDEP Interface for HD-audio codec
- *
- * Copyright (c) 2007 Takashi Iwai <tiwai@suse.de>
- */
-
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/compat.h>
-#include <linux/nospec.h>
-#include <sound/core.h>
-#include <sound/hda_codec.h>
-#include "hda_local.h"
-#include <sound/hda_hwdep.h>
-#include <sound/minors.h>
-
-/*
- * write/read an out-of-bound verb
- */
-static int verb_write_ioctl(struct hda_codec *codec,
-                           struct hda_verb_ioctl __user *arg)
-{
-       u32 verb, res;
-
-       if (get_user(verb, &arg->verb))
-               return -EFAULT;
-       res = snd_hda_codec_read(codec, verb >> 24, 0,
-                                (verb >> 8) & 0xffff, verb & 0xff);
-       if (put_user(res, &arg->res))
-               return -EFAULT;
-       return 0;
-}
-
-static int get_wcap_ioctl(struct hda_codec *codec,
-                         struct hda_verb_ioctl __user *arg)
-{
-       u32 verb, res;
-       
-       if (get_user(verb, &arg->verb))
-               return -EFAULT;
-       /* open-code get_wcaps(verb>>24) with nospec */
-       verb >>= 24;
-       if (verb < codec->core.start_nid ||
-           verb >= codec->core.start_nid + codec->core.num_nodes) {
-               res = 0;
-       } else {
-               verb -= codec->core.start_nid;
-               verb = array_index_nospec(verb, codec->core.num_nodes);
-               res = codec->wcaps[verb];
-       }
-       if (put_user(res, &arg->res))
-               return -EFAULT;
-       return 0;
-}
-
-
-/*
- */
-static int hda_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
-                          unsigned int cmd, unsigned long arg)
-{
-       struct hda_codec *codec = hw->private_data;
-       void __user *argp = (void __user *)arg;
-
-       switch (cmd) {
-       case HDA_IOCTL_PVERSION:
-               return put_user(HDA_HWDEP_VERSION, (int __user *)argp);
-       case HDA_IOCTL_VERB_WRITE:
-               return verb_write_ioctl(codec, argp);
-       case HDA_IOCTL_GET_WCAP:
-               return get_wcap_ioctl(codec, argp);
-       }
-       return -ENOIOCTLCMD;
-}
-
-#ifdef CONFIG_COMPAT
-static int hda_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file,
-                                 unsigned int cmd, unsigned long arg)
-{
-       return hda_hwdep_ioctl(hw, file, cmd, (unsigned long)compat_ptr(arg));
-}
-#endif
-
-static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file)
-{
-       if (!capable(CAP_SYS_RAWIO))
-               return -EACCES;
-       return 0;
-}
-
-int snd_hda_create_hwdep(struct hda_codec *codec)
-{
-       char hwname[16];
-       struct snd_hwdep *hwdep;
-       int err;
-
-       sprintf(hwname, "HDA Codec %d", codec->addr);
-       err = snd_hwdep_new(codec->card, hwname, codec->addr, &hwdep);
-       if (err < 0)
-               return err;
-       codec->hwdep = hwdep;
-       sprintf(hwdep->name, "HDA Codec %d", codec->addr);
-       hwdep->iface = SNDRV_HWDEP_IFACE_HDA;
-       hwdep->private_data = codec;
-       hwdep->exclusive = 1;
-
-       hwdep->ops.open = hda_hwdep_open;
-       hwdep->ops.ioctl = hda_hwdep_ioctl;
-#ifdef CONFIG_COMPAT
-       hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat;
-#endif
-
-       /* for sysfs */
-       hwdep->dev->groups = snd_hda_dev_attr_groups;
-       dev_set_drvdata(hwdep->dev, codec);
-
-       return 0;
-}
diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c
deleted file mode 100644 (file)
index 7d7786d..0000000
+++ /dev/null
@@ -1,770 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Jack-detection handling for HD-audio
- *
- * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
- */
-
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/export.h>
-#include <sound/core.h>
-#include <sound/control.h>
-#include <sound/jack.h>
-#include <sound/hda_codec.h>
-#include "hda_local.h"
-#include "hda_auto_parser.h"
-#include "hda_jack.h"
-
-/**
- * is_jack_detectable - Check whether the given pin is jack-detectable
- * @codec: the HDA codec
- * @nid: pin NID
- *
- * Check whether the given pin is capable to report the jack detection.
- * The jack detection might not work by various reasons, e.g. the jack
- * detection is prohibited in the codec level, the pin config has
- * AC_DEFCFG_MISC_NO_PRESENCE bit, no unsol support, etc.
- */
-bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid)
-{
-       if (codec->no_jack_detect)
-               return false;
-       if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_PRES_DETECT))
-               return false;
-       if (get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) &
-            AC_DEFCFG_MISC_NO_PRESENCE)
-               return false;
-       if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) &&
-           !codec->jackpoll_interval)
-               return false;
-       return true;
-}
-EXPORT_SYMBOL_GPL(is_jack_detectable);
-
-/* execute pin sense measurement */
-static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid, int dev_id)
-{
-       u32 pincap;
-       u32 val;
-
-       if (!codec->no_trigger_sense) {
-               pincap = snd_hda_query_pin_caps(codec, nid);
-               if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */
-                       snd_hda_codec_read(codec, nid, 0,
-                                       AC_VERB_SET_PIN_SENSE, 0);
-       }
-       val = snd_hda_codec_read(codec, nid, 0,
-                                 AC_VERB_GET_PIN_SENSE, dev_id);
-       if (codec->inv_jack_detect)
-               val ^= AC_PINSENSE_PRESENCE;
-       return val;
-}
-
-/**
- * snd_hda_jack_tbl_get_mst - query the jack-table entry for the given NID
- * @codec: the HDA codec
- * @nid: pin NID to refer to
- * @dev_id: pin device entry id
- */
-struct hda_jack_tbl *
-snd_hda_jack_tbl_get_mst(struct hda_codec *codec, hda_nid_t nid, int dev_id)
-{
-       struct hda_jack_tbl *jack = codec->jacktbl.list;
-       int i;
-
-       if (!nid || !jack)
-               return NULL;
-       for (i = 0; i < codec->jacktbl.used; i++, jack++)
-               if (jack->nid == nid && jack->dev_id == dev_id)
-                       return jack;
-       return NULL;
-}
-EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_get_mst);
-
-/**
- * snd_hda_jack_tbl_get_from_tag - query the jack-table entry for the given tag
- * @codec: the HDA codec
- * @tag: tag value to refer to
- * @dev_id: pin device entry id
- */
-struct hda_jack_tbl *
-snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec,
-                             unsigned char tag, int dev_id)
-{
-       struct hda_jack_tbl *jack = codec->jacktbl.list;
-       int i;
-
-       if (!tag || !jack)
-               return NULL;
-       for (i = 0; i < codec->jacktbl.used; i++, jack++)
-               if (jack->tag == tag && jack->dev_id == dev_id)
-                       return jack;
-       return NULL;
-}
-EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_get_from_tag);
-
-static struct hda_jack_tbl *
-any_jack_tbl_get_from_nid(struct hda_codec *codec, hda_nid_t nid)
-{
-       struct hda_jack_tbl *jack = codec->jacktbl.list;
-       int i;
-
-       if (!nid || !jack)
-               return NULL;
-       for (i = 0; i < codec->jacktbl.used; i++, jack++)
-               if (jack->nid == nid)
-                       return jack;
-       return NULL;
-}
-
-/**
- * snd_hda_jack_tbl_new - create a jack-table entry for the given NID
- * @codec: the HDA codec
- * @nid: pin NID to assign
- * @dev_id: pin device entry id
- */
-static struct hda_jack_tbl *
-snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid, int dev_id)
-{
-       struct hda_jack_tbl *jack =
-               snd_hda_jack_tbl_get_mst(codec, nid, dev_id);
-       struct hda_jack_tbl *existing_nid_jack =
-               any_jack_tbl_get_from_nid(codec, nid);
-
-       WARN_ON(dev_id != 0 && !codec->dp_mst);
-
-       if (jack)
-               return jack;
-       jack = snd_array_new(&codec->jacktbl);
-       if (!jack)
-               return NULL;
-       jack->nid = nid;
-       jack->dev_id = dev_id;
-       jack->jack_dirty = 1;
-       if (existing_nid_jack) {
-               jack->tag = existing_nid_jack->tag;
-
-               /*
-                * Copy jack_detect from existing_nid_jack to avoid
-                * snd_hda_jack_detect_enable_callback_mst() making multiple
-                * SET_UNSOLICITED_ENABLE calls on the same pin.
-                */
-               jack->jack_detect = existing_nid_jack->jack_detect;
-       } else {
-               jack->tag = codec->jacktbl.used;
-       }
-
-       return jack;
-}
-
-void snd_hda_jack_tbl_disconnect(struct hda_codec *codec)
-{
-       struct hda_jack_tbl *jack = codec->jacktbl.list;
-       int i;
-
-       for (i = 0; i < codec->jacktbl.used; i++, jack++) {
-               if (!codec->bus->shutdown && jack->jack)
-                       snd_device_disconnect(codec->card, jack->jack);
-       }
-}
-
-void snd_hda_jack_tbl_clear(struct hda_codec *codec)
-{
-       struct hda_jack_tbl *jack = codec->jacktbl.list;
-       int i;
-
-       for (i = 0; i < codec->jacktbl.used; i++, jack++) {
-               struct hda_jack_callback *cb, *next;
-
-               /* free jack instances manually when clearing/reconfiguring */
-               if (!codec->bus->shutdown && jack->jack)
-                       snd_device_free(codec->card, jack->jack);
-
-               for (cb = jack->callback; cb; cb = next) {
-                       next = cb->next;
-                       kfree(cb);
-               }
-       }
-       snd_array_free(&codec->jacktbl);
-}
-
-#define get_jack_plug_state(sense) !!(sense & AC_PINSENSE_PRESENCE)
-
-/* update the cached value and notification flag if needed */
-static void jack_detect_update(struct hda_codec *codec,
-                              struct hda_jack_tbl *jack)
-{
-       if (!jack->jack_dirty)
-               return;
-
-       if (jack->phantom_jack)
-               jack->pin_sense = AC_PINSENSE_PRESENCE;
-       else
-               jack->pin_sense = read_pin_sense(codec, jack->nid,
-                                                jack->dev_id);
-
-       /* A gating jack indicates the jack is invalid if gating is unplugged */
-       if (jack->gating_jack &&
-           !snd_hda_jack_detect_mst(codec, jack->gating_jack, jack->dev_id))
-               jack->pin_sense &= ~AC_PINSENSE_PRESENCE;
-
-       jack->jack_dirty = 0;
-
-       /* If a jack is gated by this one update it. */
-       if (jack->gated_jack) {
-               struct hda_jack_tbl *gated =
-                       snd_hda_jack_tbl_get_mst(codec, jack->gated_jack,
-                                                jack->dev_id);
-               if (gated) {
-                       gated->jack_dirty = 1;
-                       jack_detect_update(codec, gated);
-               }
-       }
-}
-
-/**
- * snd_hda_jack_set_dirty_all - Mark all the cached as dirty
- * @codec: the HDA codec
- *
- * This function sets the dirty flag to all entries of jack table.
- * It's called from the resume path in hda_codec.c.
- */
-void snd_hda_jack_set_dirty_all(struct hda_codec *codec)
-{
-       struct hda_jack_tbl *jack = codec->jacktbl.list;
-       int i;
-
-       for (i = 0; i < codec->jacktbl.used; i++, jack++)
-               if (jack->nid)
-                       jack->jack_dirty = 1;
-}
-EXPORT_SYMBOL_GPL(snd_hda_jack_set_dirty_all);
-
-/**
- * snd_hda_jack_pin_sense - execute pin sense measurement
- * @codec: the CODEC to sense
- * @nid: the pin NID to sense
- * @dev_id: pin device entry id
- *
- * Execute necessary pin sense measurement and return its Presence Detect,
- * Impedance, ELD Valid etc. status bits.
- */
-u32 snd_hda_jack_pin_sense(struct hda_codec *codec, hda_nid_t nid, int dev_id)
-{
-       struct hda_jack_tbl *jack =
-               snd_hda_jack_tbl_get_mst(codec, nid, dev_id);
-       if (jack) {
-               jack_detect_update(codec, jack);
-               return jack->pin_sense;
-       }
-       return read_pin_sense(codec, nid, dev_id);
-}
-EXPORT_SYMBOL_GPL(snd_hda_jack_pin_sense);
-
-/**
- * snd_hda_jack_detect_state_mst - query pin Presence Detect status
- * @codec: the CODEC to sense
- * @nid: the pin NID to sense
- * @dev_id: pin device entry id
- *
- * Query and return the pin's Presence Detect status, as either
- * HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT or HDA_JACK_PHANTOM.
- */
-int snd_hda_jack_detect_state_mst(struct hda_codec *codec,
-                                 hda_nid_t nid, int dev_id)
-{
-       struct hda_jack_tbl *jack =
-               snd_hda_jack_tbl_get_mst(codec, nid, dev_id);
-       if (jack && jack->phantom_jack)
-               return HDA_JACK_PHANTOM;
-       else if (snd_hda_jack_pin_sense(codec, nid, dev_id) &
-                AC_PINSENSE_PRESENCE)
-               return HDA_JACK_PRESENT;
-       else
-               return HDA_JACK_NOT_PRESENT;
-}
-EXPORT_SYMBOL_GPL(snd_hda_jack_detect_state_mst);
-
-static struct hda_jack_callback *
-find_callback_from_list(struct hda_jack_tbl *jack,
-                       hda_jack_callback_fn func)
-{
-       struct hda_jack_callback *cb;
-
-       if (!func)
-               return NULL;
-
-       for (cb = jack->callback; cb; cb = cb->next) {
-               if (cb->func == func)
-                       return cb;
-       }
-
-       return NULL;
-}
-
-/**
- * snd_hda_jack_detect_enable_callback_mst - enable the jack-detection
- * @codec: the HDA codec
- * @nid: pin NID to enable
- * @func: callback function to register
- * @dev_id: pin device entry id
- *
- * In the case of error, the return value will be a pointer embedded with
- * errno.  Check and handle the return value appropriately with standard
- * macros such as @IS_ERR() and @PTR_ERR().
- */
-struct hda_jack_callback *
-snd_hda_jack_detect_enable_callback_mst(struct hda_codec *codec, hda_nid_t nid,
-                                       int dev_id, hda_jack_callback_fn func)
-{
-       struct hda_jack_tbl *jack;
-       struct hda_jack_callback *callback = NULL;
-       int err;
-
-       jack = snd_hda_jack_tbl_new(codec, nid, dev_id);
-       if (!jack)
-               return ERR_PTR(-ENOMEM);
-
-       callback = find_callback_from_list(jack, func);
-
-       if (func && !callback) {
-               callback = kzalloc(sizeof(*callback), GFP_KERNEL);
-               if (!callback)
-                       return ERR_PTR(-ENOMEM);
-               callback->func = func;
-               callback->nid = jack->nid;
-               callback->dev_id = jack->dev_id;
-               callback->next = jack->callback;
-               jack->callback = callback;
-       }
-
-       if (jack->jack_detect)
-               return callback; /* already registered */
-       jack->jack_detect = 1;
-       if (codec->jackpoll_interval > 0)
-               return callback; /* No unsol if we're polling instead */
-       err = snd_hda_codec_write_cache(codec, nid, 0,
-                                        AC_VERB_SET_UNSOLICITED_ENABLE,
-                                        AC_USRSP_EN | jack->tag);
-       if (err < 0)
-               return ERR_PTR(err);
-       return callback;
-}
-EXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable_callback_mst);
-
-/**
- * snd_hda_jack_detect_enable - Enable the jack detection on the given pin
- * @codec: the HDA codec
- * @nid: pin NID to enable jack detection
- * @dev_id: pin device entry id
- *
- * Enable the jack detection with the default callback.  Returns zero if
- * successful or a negative error code.
- */
-int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid,
-                              int dev_id)
-{
-       return PTR_ERR_OR_ZERO(snd_hda_jack_detect_enable_callback_mst(codec,
-                                                                      nid,
-                                                                      dev_id,
-                                                                      NULL));
-}
-EXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable);
-
-/**
- * snd_hda_jack_set_gating_jack - Set gating jack.
- * @codec: the HDA codec
- * @gated_nid: gated pin NID
- * @gating_nid: gating pin NID
- *
- * Indicates the gated jack is only valid when the gating jack is plugged.
- */
-int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
-                                hda_nid_t gating_nid)
-{
-       struct hda_jack_tbl *gated = snd_hda_jack_tbl_new(codec, gated_nid, 0);
-       struct hda_jack_tbl *gating =
-               snd_hda_jack_tbl_new(codec, gating_nid, 0);
-
-       WARN_ON(codec->dp_mst);
-
-       if (!gated || !gating)
-               return -EINVAL;
-
-       gated->gating_jack = gating_nid;
-       gating->gated_jack = gated_nid;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_jack_set_gating_jack);
-
-/**
- * snd_hda_jack_bind_keymap - bind keys generated from one NID to another jack.
- * @codec: the HDA codec
- * @key_nid: key event is generated by this pin NID
- * @keymap: map of key type and key code
- * @jack_nid: key reports to the jack of this pin NID
- *
- * This function is used in the case of key is generated from one NID while is
- * reported to the jack of another NID.
- */
-int snd_hda_jack_bind_keymap(struct hda_codec *codec, hda_nid_t key_nid,
-                            const struct hda_jack_keymap *keymap,
-                            hda_nid_t jack_nid)
-{
-       const struct hda_jack_keymap *map;
-       struct hda_jack_tbl *key_gen = snd_hda_jack_tbl_get(codec, key_nid);
-       struct hda_jack_tbl *report_to = snd_hda_jack_tbl_get(codec, jack_nid);
-
-       WARN_ON(codec->dp_mst);
-
-       if (!key_gen || !report_to || !report_to->jack)
-               return -EINVAL;
-
-       key_gen->key_report_jack = jack_nid;
-
-       if (keymap)
-               for (map = keymap; map->type; map++)
-                       snd_jack_set_key(report_to->jack, map->type, map->key);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_jack_bind_keymap);
-
-/**
- * snd_hda_jack_set_button_state - report button event to the hda_jack_tbl button_state.
- * @codec: the HDA codec
- * @jack_nid: the button event reports to the jack_tbl of this NID
- * @button_state: the button event captured by codec
- *
- * Codec driver calls this function to report the button event.
- */
-void snd_hda_jack_set_button_state(struct hda_codec *codec, hda_nid_t jack_nid,
-                                  int button_state)
-{
-       struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, jack_nid);
-
-       if (!jack)
-               return;
-
-       if (jack->key_report_jack) {
-               struct hda_jack_tbl *report_to =
-                       snd_hda_jack_tbl_get(codec, jack->key_report_jack);
-
-               if (report_to) {
-                       report_to->button_state = button_state;
-                       return;
-               }
-       }
-
-       jack->button_state = button_state;
-}
-EXPORT_SYMBOL_GPL(snd_hda_jack_set_button_state);
-
-/**
- * snd_hda_jack_report_sync - sync the states of all jacks and report if changed
- * @codec: the HDA codec
- */
-void snd_hda_jack_report_sync(struct hda_codec *codec)
-{
-       struct hda_jack_tbl *jack;
-       int i, state;
-
-       /* update all jacks at first */
-       jack = codec->jacktbl.list;
-       for (i = 0; i < codec->jacktbl.used; i++, jack++)
-               if (jack->nid)
-                       jack_detect_update(codec, jack);
-
-       /* report the updated jacks; it's done after updating all jacks
-        * to make sure that all gating jacks properly have been set
-        */
-       jack = codec->jacktbl.list;
-       for (i = 0; i < codec->jacktbl.used; i++, jack++)
-               if (jack->nid) {
-                       if (!jack->jack || jack->block_report)
-                               continue;
-                       state = jack->button_state;
-                       if (get_jack_plug_state(jack->pin_sense))
-                               state |= jack->type;
-                       snd_jack_report(jack->jack, state);
-                       if (jack->button_state) {
-                               snd_jack_report(jack->jack,
-                                               state & ~jack->button_state);
-                               jack->button_state = 0; /* button released */
-                       }
-               }
-}
-EXPORT_SYMBOL_GPL(snd_hda_jack_report_sync);
-
-/* guess the jack type from the pin-config */
-static int get_input_jack_type(struct hda_codec *codec, hda_nid_t nid)
-{
-       unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
-       switch (get_defcfg_device(def_conf)) {
-       case AC_JACK_LINE_OUT:
-       case AC_JACK_SPEAKER:
-               return SND_JACK_LINEOUT;
-       case AC_JACK_HP_OUT:
-               return SND_JACK_HEADPHONE;
-       case AC_JACK_SPDIF_OUT:
-       case AC_JACK_DIG_OTHER_OUT:
-               return SND_JACK_AVOUT;
-       case AC_JACK_MIC_IN:
-               return SND_JACK_MICROPHONE;
-       default:
-               return SND_JACK_LINEIN;
-       }
-}
-
-static void hda_free_jack_priv(struct snd_jack *jack)
-{
-       struct hda_jack_tbl *jacks = jack->private_data;
-       jacks->nid = 0;
-       jacks->jack = NULL;
-}
-
-/**
- * snd_hda_jack_add_kctl_mst - Add a kctl for the given pin
- * @codec: the HDA codec
- * @nid: pin NID to assign
- * @dev_id : pin device entry id
- * @name: string name for the jack
- * @phantom_jack: flag to deal as a phantom jack
- * @type: jack type bits to be reported, 0 for guessing from pincfg
- * @keymap: optional jack / key mapping
- *
- * This assigns a jack-detection kctl to the given pin.  The kcontrol
- * will have the given name and index.
- */
-int snd_hda_jack_add_kctl_mst(struct hda_codec *codec, hda_nid_t nid,
-                             int dev_id, const char *name, bool phantom_jack,
-                             int type, const struct hda_jack_keymap *keymap)
-{
-       struct hda_jack_tbl *jack;
-       const struct hda_jack_keymap *map;
-       int err, state, buttons;
-
-       jack = snd_hda_jack_tbl_new(codec, nid, dev_id);
-       if (!jack)
-               return 0;
-       if (jack->jack)
-               return 0; /* already created */
-
-       if (!type)
-               type = get_input_jack_type(codec, nid);
-
-       buttons = 0;
-       if (keymap) {
-               for (map = keymap; map->type; map++)
-                       buttons |= map->type;
-       }
-
-       err = snd_jack_new(codec->card, name, type | buttons,
-                          &jack->jack, true, phantom_jack);
-       if (err < 0)
-               return err;
-
-       jack->phantom_jack = !!phantom_jack;
-       jack->type = type;
-       jack->button_state = 0;
-       jack->jack->private_data = jack;
-       jack->jack->private_free = hda_free_jack_priv;
-       if (keymap) {
-               for (map = keymap; map->type; map++)
-                       snd_jack_set_key(jack->jack, map->type, map->key);
-       }
-
-       state = snd_hda_jack_detect_mst(codec, nid, dev_id);
-       snd_jack_report(jack->jack, state ? jack->type : 0);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_jack_add_kctl_mst);
-
-static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid,
-                        const struct auto_pin_cfg *cfg,
-                        const char *base_name)
-{
-       unsigned int def_conf, conn;
-       char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
-       int err;
-       bool phantom_jack;
-
-       WARN_ON(codec->dp_mst);
-
-       if (!nid)
-               return 0;
-       def_conf = snd_hda_codec_get_pincfg(codec, nid);
-       conn = get_defcfg_connect(def_conf);
-       if (conn == AC_JACK_PORT_NONE)
-               return 0;
-       phantom_jack = (conn != AC_JACK_PORT_COMPLEX) ||
-                      !is_jack_detectable(codec, nid);
-
-       if (base_name)
-               strscpy(name, base_name, sizeof(name));
-       else
-               snd_hda_get_pin_label(codec, nid, cfg, name, sizeof(name), NULL);
-       if (phantom_jack)
-               /* Example final name: "Internal Mic Phantom Jack" */
-               strncat(name, " Phantom", sizeof(name) - strlen(name) - 1);
-       err = snd_hda_jack_add_kctl(codec, nid, name, phantom_jack, 0, NULL);
-       if (err < 0)
-               return err;
-
-       if (!phantom_jack)
-               return snd_hda_jack_detect_enable(codec, nid, 0);
-       return 0;
-}
-
-/**
- * snd_hda_jack_add_kctls - Add kctls for all pins included in the given pincfg
- * @codec: the HDA codec
- * @cfg: pin config table to parse
- */
-int snd_hda_jack_add_kctls(struct hda_codec *codec,
-                          const struct auto_pin_cfg *cfg)
-{
-       const hda_nid_t *p;
-       int i, err;
-
-       for (i = 0; i < cfg->num_inputs; i++) {
-               /* If we have headphone mics; make sure they get the right name
-                  before grabbed by output pins */
-               if (cfg->inputs[i].is_headphone_mic) {
-                       if (auto_cfg_hp_outs(cfg) == 1)
-                               err = add_jack_kctl(codec, auto_cfg_hp_pins(cfg)[0],
-                                                   cfg, "Headphone Mic");
-                       else
-                               err = add_jack_kctl(codec, cfg->inputs[i].pin,
-                                                   cfg, "Headphone Mic");
-               } else
-                       err = add_jack_kctl(codec, cfg->inputs[i].pin, cfg,
-                                           NULL);
-               if (err < 0)
-                       return err;
-       }
-
-       for (i = 0, p = cfg->line_out_pins; i < cfg->line_outs; i++, p++) {
-               err = add_jack_kctl(codec, *p, cfg, NULL);
-               if (err < 0)
-                       return err;
-       }
-       for (i = 0, p = cfg->hp_pins; i < cfg->hp_outs; i++, p++) {
-               if (*p == *cfg->line_out_pins) /* might be duplicated */
-                       break;
-               err = add_jack_kctl(codec, *p, cfg, NULL);
-               if (err < 0)
-                       return err;
-       }
-       for (i = 0, p = cfg->speaker_pins; i < cfg->speaker_outs; i++, p++) {
-               if (*p == *cfg->line_out_pins) /* might be duplicated */
-                       break;
-               err = add_jack_kctl(codec, *p, cfg, NULL);
-               if (err < 0)
-                       return err;
-       }
-       for (i = 0, p = cfg->dig_out_pins; i < cfg->dig_outs; i++, p++) {
-               err = add_jack_kctl(codec, *p, cfg, NULL);
-               if (err < 0)
-                       return err;
-       }
-       err = add_jack_kctl(codec, cfg->dig_in_pin, cfg, NULL);
-       if (err < 0)
-               return err;
-       err = add_jack_kctl(codec, cfg->mono_out_pin, cfg, NULL);
-       if (err < 0)
-               return err;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_jack_add_kctls);
-
-static void call_jack_callback(struct hda_codec *codec, unsigned int res,
-                              struct hda_jack_tbl *jack)
-{
-       struct hda_jack_callback *cb;
-
-       for (cb = jack->callback; cb; cb = cb->next) {
-               cb->jack = jack;
-               cb->unsol_res = res;
-               cb->func(codec, cb);
-       }
-       if (jack->gated_jack) {
-               struct hda_jack_tbl *gated =
-                       snd_hda_jack_tbl_get_mst(codec, jack->gated_jack,
-                                                jack->dev_id);
-               if (gated) {
-                       for (cb = gated->callback; cb; cb = cb->next) {
-                               cb->jack = gated;
-                               cb->unsol_res = res;
-                               cb->func(codec, cb);
-                       }
-               }
-       }
-}
-
-/**
- * snd_hda_jack_unsol_event - Handle an unsolicited event
- * @codec: the HDA codec
- * @res: the unsolicited event data
- */
-void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res)
-{
-       struct hda_jack_tbl *event;
-       int tag = (res & AC_UNSOL_RES_TAG) >> AC_UNSOL_RES_TAG_SHIFT;
-
-       if (codec->dp_mst) {
-               int dev_entry =
-                       (res & AC_UNSOL_RES_DE) >> AC_UNSOL_RES_DE_SHIFT;
-
-               event = snd_hda_jack_tbl_get_from_tag(codec, tag, dev_entry);
-       } else {
-               event = snd_hda_jack_tbl_get_from_tag(codec, tag, 0);
-       }
-       if (!event)
-               return;
-
-       if (event->key_report_jack) {
-               struct hda_jack_tbl *report_to =
-                       snd_hda_jack_tbl_get_mst(codec, event->key_report_jack,
-                                                event->dev_id);
-               if (report_to)
-                       report_to->jack_dirty = 1;
-       } else
-               event->jack_dirty = 1;
-
-       call_jack_callback(codec, res, event);
-       snd_hda_jack_report_sync(codec);
-}
-EXPORT_SYMBOL_GPL(snd_hda_jack_unsol_event);
-
-/**
- * snd_hda_jack_poll_all - Poll all jacks
- * @codec: the HDA codec
- *
- * Poll all detectable jacks with dirty flag, update the status, call
- * callbacks and call snd_hda_jack_report_sync() if any changes are found.
- */
-void snd_hda_jack_poll_all(struct hda_codec *codec)
-{
-       struct hda_jack_tbl *jack = codec->jacktbl.list;
-       int i, changes = 0;
-
-       for (i = 0; i < codec->jacktbl.used; i++, jack++) {
-               unsigned int old_sense;
-               if (!jack->nid || !jack->jack_dirty || jack->phantom_jack)
-                       continue;
-               old_sense = get_jack_plug_state(jack->pin_sense);
-               jack_detect_update(codec, jack);
-               if (old_sense == get_jack_plug_state(jack->pin_sense))
-                       continue;
-               changes = 1;
-               call_jack_callback(codec, 0, jack);
-       }
-       if (changes)
-               snd_hda_jack_report_sync(codec);
-}
-EXPORT_SYMBOL_GPL(snd_hda_jack_poll_all);
-
diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h
deleted file mode 100644 (file)
index ff7d289..0000000
+++ /dev/null
@@ -1,195 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Jack-detection handling for HD-audio
- *
- * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
- */
-
-#ifndef __SOUND_HDA_JACK_H
-#define __SOUND_HDA_JACK_H
-
-#include <linux/err.h>
-#include <sound/jack.h>
-
-struct auto_pin_cfg;
-struct hda_jack_tbl;
-struct hda_jack_callback;
-
-typedef void (*hda_jack_callback_fn) (struct hda_codec *, struct hda_jack_callback *);
-
-struct hda_jack_callback {
-       hda_nid_t nid;
-       int dev_id;
-       hda_jack_callback_fn func;
-       unsigned int private_data;      /* arbitrary data */
-       unsigned int unsol_res;         /* unsolicited event bits */
-       struct hda_jack_tbl *jack;      /* associated jack entry */
-       struct hda_jack_callback *next;
-};
-
-struct hda_jack_tbl {
-       hda_nid_t nid;
-       int dev_id;
-       unsigned char tag;              /* unsol event tag */
-       struct hda_jack_callback *callback;
-       /* jack-detection stuff */
-       unsigned int pin_sense;         /* cached pin-sense value */
-       unsigned int jack_detect:1;     /* capable of jack-detection? */
-       unsigned int jack_dirty:1;      /* needs to update? */
-       unsigned int phantom_jack:1;    /* a fixed, always present port? */
-       unsigned int block_report:1;    /* in a transitional state - do not report to userspace */
-       hda_nid_t gating_jack;          /* valid when gating jack plugged */
-       hda_nid_t gated_jack;           /* gated is dependent on this jack */
-       hda_nid_t key_report_jack;      /* key reports to this jack */
-       int type;
-       int button_state;
-       struct snd_jack *jack;
-};
-
-struct hda_jack_keymap {
-       enum snd_jack_types type;
-       int key;
-};
-
-struct hda_jack_tbl *
-snd_hda_jack_tbl_get_mst(struct hda_codec *codec, hda_nid_t nid, int dev_id);
-
-/**
- * snd_hda_jack_tbl_get - query the jack-table entry for the given NID
- * @codec: the HDA codec
- * @nid: pin NID to refer to
- */
-static inline struct hda_jack_tbl *
-snd_hda_jack_tbl_get(struct hda_codec *codec, hda_nid_t nid)
-{
-       return snd_hda_jack_tbl_get_mst(codec, nid, 0);
-}
-
-struct hda_jack_tbl *
-snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec,
-                             unsigned char tag, int dev_id);
-
-void snd_hda_jack_tbl_disconnect(struct hda_codec *codec);
-void snd_hda_jack_tbl_clear(struct hda_codec *codec);
-
-void snd_hda_jack_set_dirty_all(struct hda_codec *codec);
-
-int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid,
-                              int dev_id);
-
-struct hda_jack_callback *
-snd_hda_jack_detect_enable_callback_mst(struct hda_codec *codec, hda_nid_t nid,
-                                       int dev_id, hda_jack_callback_fn func);
-
-/**
- * snd_hda_jack_detect_enable - enable the jack-detection
- * @codec: the HDA codec
- * @nid: pin NID to enable
- * @func: callback function to register
- *
- * In the case of error, the return value will be a pointer embedded with
- * errno.  Check and handle the return value appropriately with standard
- * macros such as @IS_ERR() and @PTR_ERR().
- */
-static inline struct hda_jack_callback *
-snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid,
-                                   hda_jack_callback_fn cb)
-{
-       return snd_hda_jack_detect_enable_callback_mst(codec, nid, 0, cb);
-}
-
-int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
-                                hda_nid_t gating_nid);
-
-int snd_hda_jack_bind_keymap(struct hda_codec *codec, hda_nid_t key_nid,
-                            const struct hda_jack_keymap *keymap,
-                            hda_nid_t jack_nid);
-
-void snd_hda_jack_set_button_state(struct hda_codec *codec, hda_nid_t jack_nid,
-                                  int button_state);
-
-u32 snd_hda_jack_pin_sense(struct hda_codec *codec, hda_nid_t nid, int dev_id);
-
-/* the jack state returned from snd_hda_jack_detect_state() */
-enum {
-       HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT, HDA_JACK_PHANTOM,
-};
-
-int snd_hda_jack_detect_state_mst(struct hda_codec *codec, hda_nid_t nid,
-                                 int dev_id);
-
-/**
- * snd_hda_jack_detect_state - query pin Presence Detect status
- * @codec: the CODEC to sense
- * @nid: the pin NID to sense
- *
- * Query and return the pin's Presence Detect status, as either
- * HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT or HDA_JACK_PHANTOM.
- */
-static inline int
-snd_hda_jack_detect_state(struct hda_codec *codec, hda_nid_t nid)
-{
-       return snd_hda_jack_detect_state_mst(codec, nid, 0);
-}
-
-/**
- * snd_hda_jack_detect_mst - Detect the jack
- * @codec: the HDA codec
- * @nid: pin NID to check jack detection
- * @dev_id: pin device entry id
- */
-static inline bool
-snd_hda_jack_detect_mst(struct hda_codec *codec, hda_nid_t nid, int dev_id)
-{
-       return snd_hda_jack_detect_state_mst(codec, nid, dev_id) !=
-                       HDA_JACK_NOT_PRESENT;
-}
-
-/**
- * snd_hda_jack_detect - Detect the jack
- * @codec: the HDA codec
- * @nid: pin NID to check jack detection
- */
-static inline bool
-snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid)
-{
-       return snd_hda_jack_detect_mst(codec, nid, 0);
-}
-
-bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid);
-
-int snd_hda_jack_add_kctl_mst(struct hda_codec *codec, hda_nid_t nid,
-                             int dev_id, const char *name, bool phantom_jack,
-                             int type, const struct hda_jack_keymap *keymap);
-
-/**
- * snd_hda_jack_add_kctl - Add a kctl for the given pin
- * @codec: the HDA codec
- * @nid: pin NID to assign
- * @name: string name for the jack
- * @phantom_jack: flag to deal as a phantom jack
- * @type: jack type bits to be reported, 0 for guessing from pincfg
- * @keymap: optional jack / key mapping
- *
- * This assigns a jack-detection kctl to the given pin.  The kcontrol
- * will have the given name and index.
- */
-static inline int
-snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
-                     const char *name, bool phantom_jack,
-                     int type, const struct hda_jack_keymap *keymap)
-{
-       return snd_hda_jack_add_kctl_mst(codec, nid, 0,
-                                        name, phantom_jack, type, keymap);
-}
-
-int snd_hda_jack_add_kctls(struct hda_codec *codec,
-                          const struct auto_pin_cfg *cfg);
-
-void snd_hda_jack_report_sync(struct hda_codec *codec);
-
-void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res);
-
-void snd_hda_jack_poll_all(struct hda_codec *codec);
-
-#endif /* __SOUND_HDA_JACK_H */
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
deleted file mode 100644 (file)
index 68c31f5..0000000
+++ /dev/null
@@ -1,720 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Universal Interface for Intel High Definition Audio Codec
- *
- * Local helper functions
- *
- * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- */
-
-#ifndef __SOUND_HDA_LOCAL_H
-#define __SOUND_HDA_LOCAL_H
-
-#include <sound/pcm_drm_eld.h>
-
-/* We abuse kcontrol_new.subdev field to pass the NID corresponding to
- * the given new control.  If id.subdev has a bit flag HDA_SUBDEV_NID_FLAG,
- * snd_hda_ctl_add() takes the lower-bit subdev value as a valid NID.
- * 
- * Note that the subdevice field is cleared again before the real registration
- * in snd_hda_ctl_add(), so that this value won't appear in the outside.
- */
-#define HDA_SUBDEV_NID_FLAG    (1U << 31)
-#define HDA_SUBDEV_AMP_FLAG    (1U << 30)
-
-/*
- * for mixer controls
- */
-#define HDA_COMPOSE_AMP_VAL_OFS(nid,chs,idx,dir,ofs)           \
-       ((nid) | ((chs)<<16) | ((dir)<<18) | ((idx)<<19) | ((ofs)<<23))
-#define HDA_AMP_VAL_MIN_MUTE (1<<29)
-#define HDA_COMPOSE_AMP_VAL(nid,chs,idx,dir) \
-       HDA_COMPOSE_AMP_VAL_OFS(nid, chs, idx, dir, 0)
-/* mono volume with index (index=0,1,...) (channel=1,2) */
-#define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, dir, flags) \
-       { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx,  \
-         .subdevice = HDA_SUBDEV_AMP_FLAG, \
-         .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
-                   SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
-                   SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \
-         .info = snd_hda_mixer_amp_volume_info, \
-         .get = snd_hda_mixer_amp_volume_get, \
-         .put = snd_hda_mixer_amp_volume_put, \
-         .tlv = { .c = snd_hda_mixer_amp_tlv },                \
-         .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, dir) | flags }
-/* stereo volume with index */
-#define HDA_CODEC_VOLUME_IDX(xname, xcidx, nid, xindex, direction) \
-       HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, 3, xindex, direction, 0)
-/* mono volume */
-#define HDA_CODEC_VOLUME_MONO(xname, nid, channel, xindex, direction) \
-       HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, channel, xindex, direction, 0)
-/* stereo volume */
-#define HDA_CODEC_VOLUME(xname, nid, xindex, direction) \
-       HDA_CODEC_VOLUME_MONO(xname, nid, 3, xindex, direction)
-/* stereo volume with min=mute */
-#define HDA_CODEC_VOLUME_MIN_MUTE(xname, nid, xindex, direction) \
-       HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, 3, xindex, direction, \
-                                 HDA_AMP_VAL_MIN_MUTE)
-/* mono mute switch with index (index=0,1,...) (channel=1,2) */
-#define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
-       { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
-         .subdevice = HDA_SUBDEV_AMP_FLAG, \
-         .info = snd_hda_mixer_amp_switch_info, \
-         .get = snd_hda_mixer_amp_switch_get, \
-         .put = snd_hda_mixer_amp_switch_put, \
-         .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) }
-/* stereo mute switch with index */
-#define HDA_CODEC_MUTE_IDX(xname, xcidx, nid, xindex, direction) \
-       HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, 3, xindex, direction)
-/* mono mute switch */
-#define HDA_CODEC_MUTE_MONO(xname, nid, channel, xindex, direction) \
-       HDA_CODEC_MUTE_MONO_IDX(xname, 0, nid, channel, xindex, direction)
-/* stereo mute switch */
-#define HDA_CODEC_MUTE(xname, nid, xindex, direction) \
-       HDA_CODEC_MUTE_MONO(xname, nid, 3, xindex, direction)
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-/* special beep mono mute switch with index (index=0,1,...) (channel=1,2) */
-#define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
-       { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
-         .subdevice = HDA_SUBDEV_AMP_FLAG, \
-         .info = snd_hda_mixer_amp_switch_info, \
-         .get = snd_hda_mixer_amp_switch_get_beep, \
-         .put = snd_hda_mixer_amp_switch_put_beep, \
-         .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) }
-#else
-/* no digital beep - just the standard one */
-#define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, ch, xidx, dir) \
-       HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, ch, xidx, dir)
-#endif /* CONFIG_SND_HDA_INPUT_BEEP */
-/* special beep mono mute switch */
-#define HDA_CODEC_MUTE_BEEP_MONO(xname, nid, channel, xindex, direction) \
-       HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, 0, nid, channel, xindex, direction)
-/* special beep stereo mute switch */
-#define HDA_CODEC_MUTE_BEEP(xname, nid, xindex, direction) \
-       HDA_CODEC_MUTE_BEEP_MONO(xname, nid, 3, xindex, direction)
-
-extern const char *snd_hda_pcm_type_name[];
-
-int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
-                                 struct snd_ctl_elem_info *uinfo);
-int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
-                                struct snd_ctl_elem_value *ucontrol);
-int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
-                                struct snd_ctl_elem_value *ucontrol);
-int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
-                         unsigned int size, unsigned int __user *_tlv);
-int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
-                                 struct snd_ctl_elem_info *uinfo);
-int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
-                                struct snd_ctl_elem_value *ucontrol);
-int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
-                                struct snd_ctl_elem_value *ucontrol);
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-int snd_hda_mixer_amp_switch_get_beep(struct snd_kcontrol *kcontrol,
-                                     struct snd_ctl_elem_value *ucontrol);
-int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
-                                     struct snd_ctl_elem_value *ucontrol);
-#endif
-/* lowlevel accessor with caching; use carefully */
-#define snd_hda_codec_amp_read(codec, nid, ch, dir, idx) \
-       snd_hdac_regmap_get_amp(&(codec)->core, nid, ch, dir, idx)
-int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid,
-                            int ch, int dir, int idx, int mask, int val);
-int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
-                            int direction, int idx, int mask, int val);
-int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch,
-                          int direction, int idx, int mask, int val);
-int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid,
-                                 int dir, int idx, int mask, int val);
-void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
-                            unsigned int *tlv);
-struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
-                                           const char *name);
-int __snd_hda_add_vmaster(struct hda_codec *codec, char *name,
-                         unsigned int *tlv, const char * const *followers,
-                         const char *suffix, bool init_follower_vol,
-                         unsigned int access, struct snd_kcontrol **ctl_ret);
-#define snd_hda_add_vmaster(codec, name, tlv, followers, suffix, access) \
-       __snd_hda_add_vmaster(codec, name, tlv, followers, suffix, true, access, NULL)
-int snd_hda_codec_reset(struct hda_codec *codec);
-void snd_hda_codec_disconnect_pcms(struct hda_codec *codec);
-
-#define snd_hda_regmap_sync(codec)     snd_hdac_regmap_sync(&(codec)->core)
-
-struct hda_vmaster_mute_hook {
-       /* below two fields must be filled by the caller of
-        * snd_hda_add_vmaster_hook() beforehand
-        */
-       struct snd_kcontrol *sw_kctl;
-       void (*hook)(void *, int);
-       /* below are initialized automatically */
-       struct hda_codec *codec;
-};
-
-int snd_hda_add_vmaster_hook(struct hda_codec *codec,
-                            struct hda_vmaster_mute_hook *hook);
-void snd_hda_sync_vmaster_hook(struct hda_vmaster_mute_hook *hook);
-
-/* amp value bits */
-#define HDA_AMP_MUTE   0x80
-#define HDA_AMP_UNMUTE 0x00
-#define HDA_AMP_VOLMASK        0x7f
-
-/*
- * SPDIF I/O
- */
-int snd_hda_create_dig_out_ctls(struct hda_codec *codec,
-                               hda_nid_t associated_nid,
-                               hda_nid_t cvt_nid, int type);
-#define snd_hda_create_spdif_out_ctls(codec, anid, cnid) \
-       snd_hda_create_dig_out_ctls(codec, anid, cnid, HDA_PCM_TYPE_SPDIF)
-int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid);
-
-/*
- * input MUX helper
- */
-#define HDA_MAX_NUM_INPUTS     36
-struct hda_input_mux_item {
-       char label[32];
-       unsigned int index;
-};
-struct hda_input_mux {
-       unsigned int num_items;
-       struct hda_input_mux_item items[HDA_MAX_NUM_INPUTS];
-};
-
-int snd_hda_input_mux_info(const struct hda_input_mux *imux,
-                          struct snd_ctl_elem_info *uinfo);
-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_codec *codec,
-                         struct hda_input_mux *imux, const char *label,
-                         int index, int *type_idx);
-
-/*
- * Multi-channel / digital-out PCM helper
- */
-
-enum { HDA_FRONT, HDA_REAR, HDA_CLFE, HDA_SIDE }; /* index for dac_nidx */
-enum { HDA_DIG_NONE, HDA_DIG_EXCLUSIVE, HDA_DIG_ANALOG_DUP }; /* dig_out_used */
-
-#define HDA_MAX_OUTS   5
-
-struct hda_multi_out {
-       int num_dacs;           /* # of DACs, must be more than 1 */
-       const hda_nid_t *dac_nids;      /* DAC list */
-       hda_nid_t hp_nid;       /* optional DAC for HP, 0 when not exists */
-       hda_nid_t hp_out_nid[HDA_MAX_OUTS];     /* DACs for multiple HPs */
-       hda_nid_t extra_out_nid[HDA_MAX_OUTS];  /* other (e.g. speaker) DACs */
-       hda_nid_t dig_out_nid;  /* digital out audio widget */
-       const hda_nid_t *follower_dig_outs;
-       int max_channels;       /* currently supported analog channels */
-       int dig_out_used;       /* current usage of digital out (HDA_DIG_XXX) */
-       int no_share_stream;    /* don't share a stream with multiple pins */
-       int share_spdif;        /* share SPDIF pin */
-       /* PCM information for both analog and SPDIF DACs */
-       unsigned int analog_rates;
-       unsigned int analog_maxbps;
-       u64 analog_formats;
-       unsigned int spdif_rates;
-       unsigned int spdif_maxbps;
-       u64 spdif_formats;
-};
-
-int snd_hda_create_spdif_share_sw(struct hda_codec *codec,
-                                 struct hda_multi_out *mout);
-int snd_hda_multi_out_dig_open(struct hda_codec *codec,
-                              struct hda_multi_out *mout);
-int snd_hda_multi_out_dig_close(struct hda_codec *codec,
-                               struct hda_multi_out *mout);
-int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
-                                 struct hda_multi_out *mout,
-                                 unsigned int stream_tag,
-                                 unsigned int format,
-                                 struct snd_pcm_substream *substream);
-int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec,
-                                 struct hda_multi_out *mout);
-int snd_hda_multi_out_analog_open(struct hda_codec *codec,
-                                 struct hda_multi_out *mout,
-                                 struct snd_pcm_substream *substream,
-                                 struct hda_pcm_stream *hinfo);
-int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
-                                    struct hda_multi_out *mout,
-                                    unsigned int stream_tag,
-                                    unsigned int format,
-                                    struct snd_pcm_substream *substream);
-int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
-                                    struct hda_multi_out *mout);
-
-/*
- * generic proc interface
- */
-#ifdef CONFIG_SND_PROC_FS
-int snd_hda_codec_proc_new(struct hda_codec *codec);
-#else
-static inline int snd_hda_codec_proc_new(struct hda_codec *codec) { return 0; }
-#endif
-
-#define SND_PRINT_BITS_ADVISED_BUFSIZE 16
-void snd_print_pcm_bits(int pcm, char *buf, int buflen);
-
-/*
- * Misc
- */
-int snd_hda_add_new_ctls(struct hda_codec *codec,
-                        const struct snd_kcontrol_new *knew);
-
-/*
- * 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:1;         /* call the chained fixup(s) after this */
-       bool chained_before:1;  /* call the chained fixup(s) before this */
-       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;
-};
-
-/*
- * extended form of snd_pci_quirk:
- * for PCI SSID matching, use SND_PCI_QUIRK() like before;
- * for codec SSID matching, use the new HDA_CODEC_QUIRK() instead
- */
-struct hda_quirk {
-       unsigned short subvendor;       /* PCI subvendor ID */
-       unsigned short subdevice;       /* PCI subdevice ID */
-       unsigned short subdevice_mask;  /* bitmask to match */
-       bool match_codec_ssid;          /* match only with codec SSID */
-       int value;                      /* value */
-#ifdef CONFIG_SND_DEBUG_VERBOSE
-       const char *name;               /* name of the device (optional) */
-#endif
-};
-
-#ifdef CONFIG_SND_DEBUG_VERBOSE
-#define HDA_CODEC_QUIRK(vend, dev, xname, val) \
-       { _SND_PCI_QUIRK_ID(vend, dev), .value = (val), .name = (xname),\
-                       .match_codec_ssid = true }
-#else
-#define HDA_CODEC_QUIRK(vend, dev, xname, val) \
-       { _SND_PCI_QUIRK_ID(vend, dev), .value = (val), \
-                       .match_codec_ssid = true }
-#endif
-
-struct snd_hda_pin_quirk {
-       unsigned int codec;             /* Codec vendor/device ID */
-       unsigned short subvendor;       /* PCI subvendor ID */
-       const struct hda_pintbl *pins;  /* list of matching pins */
-#ifdef CONFIG_SND_DEBUG_VERBOSE
-       const char *name;
-#endif
-       int value;                      /* quirk value */
-};
-
-#ifdef CONFIG_SND_DEBUG_VERBOSE
-
-#define SND_HDA_PIN_QUIRK(_codec, _subvendor, _name, _value, _pins...) \
-       { .codec = _codec,\
-         .subvendor = _subvendor,\
-         .name = _name,\
-         .value = _value,\
-         .pins = (const struct hda_pintbl[]) { _pins, {0, 0}} \
-       }
-#else
-
-#define SND_HDA_PIN_QUIRK(_codec, _subvendor, _name, _value, _pins...) \
-       { .codec = _codec,\
-         .subvendor = _subvendor,\
-         .value = _value,\
-         .pins = (const struct hda_pintbl[]) { _pins, {0, 0}} \
-       }
-
-#endif
-
-#define HDA_FIXUP_ID_NOT_SET -1
-#define HDA_FIXUP_ID_NO_FIXUP -2
-
-/* fixup types */
-enum {
-       HDA_FIXUP_INVALID,
-       HDA_FIXUP_PINS,
-       HDA_FIXUP_VERBS,
-       HDA_FIXUP_FUNC,
-       HDA_FIXUP_PINCTLS,
-};
-
-/* fixup action definitions */
-enum {
-       HDA_FIXUP_ACT_PRE_PROBE,
-       HDA_FIXUP_ACT_PROBE,
-       HDA_FIXUP_ACT_INIT,
-       HDA_FIXUP_ACT_BUILD,
-       HDA_FIXUP_ACT_FREE,
-};
-
-int snd_hda_add_verbs(struct hda_codec *codec, const struct hda_verb *list);
-void snd_hda_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_apply_fixup(struct hda_codec *codec, int id, int action, int depth);
-void snd_hda_pick_fixup(struct hda_codec *codec,
-                       const struct hda_model_fixup *models,
-                       const struct hda_quirk *quirk,
-                       const struct hda_fixup *fixlist);
-void snd_hda_pick_pin_fixup(struct hda_codec *codec,
-                           const struct snd_hda_pin_quirk *pin_quirk,
-                           const struct hda_fixup *fixlist,
-                           bool match_all_pins);
-
-/* 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) \
-       ((cfg & AC_DEFCFG_DEF_ASSOC) >> AC_DEFCFG_ASSOC_SHIFT)
-#define get_defcfg_location(cfg) \
-       ((cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT)
-#define get_defcfg_sequence(cfg) \
-       (cfg & AC_DEFCFG_SEQUENCE)
-#define get_defcfg_device(cfg) \
-       ((cfg & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT)
-#define get_defcfg_misc(cfg) \
-       ((cfg & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT)
-
-/* amp values */
-#define AMP_IN_MUTE(idx)       (0x7080 | ((idx)<<8))
-#define AMP_IN_UNMUTE(idx)     (0x7000 | ((idx)<<8))
-#define AMP_OUT_MUTE           0xb080
-#define AMP_OUT_UNMUTE         0xb000
-#define AMP_OUT_ZERO           0xb000
-/* pinctl values */
-#define PIN_IN                 (AC_PINCTL_IN_EN)
-#define PIN_VREFHIZ            (AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ)
-#define PIN_VREF50             (AC_PINCTL_IN_EN | AC_PINCTL_VREF_50)
-#define PIN_VREFGRD            (AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD)
-#define PIN_VREF80             (AC_PINCTL_IN_EN | AC_PINCTL_VREF_80)
-#define PIN_VREF100            (AC_PINCTL_IN_EN | AC_PINCTL_VREF_100)
-#define PIN_OUT                        (AC_PINCTL_OUT_EN)
-#define PIN_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);
-unsigned int snd_hda_correct_pin_ctl(struct hda_codec *codec,
-                                    hda_nid_t pin, unsigned int val);
-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);
-}
-
-int snd_hda_codec_get_pin_target(struct hda_codec *codec, hda_nid_t nid);
-int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid,
-                                unsigned int val);
-
-#define for_each_hda_codec_node(nid, codec) \
-       for ((nid) = (codec)->core.start_nid; (nid) < (codec)->core.end_nid; (nid)++)
-
-/* Set the codec power_state flag to indicate to allow unsol event handling;
- * see hda_codec_unsol_event() in hda_bind.c.  Calling this might confuse the
- * state tracking, so use with care.
- */
-static inline void snd_hda_codec_allow_unsol_events(struct hda_codec *codec)
-{
-       codec->core.dev.power.power_state = PMSG_ON;
-}
-
-/*
- * get widget capabilities
- */
-static inline u32 get_wcaps(struct hda_codec *codec, hda_nid_t nid)
-{
-       if (nid < codec->core.start_nid ||
-           nid >= codec->core.start_nid + codec->core.num_nodes)
-               return 0;
-       return codec->wcaps[nid - codec->core.start_nid];
-}
-
-/* get the widget type from widget capability bits */
-static inline int get_wcaps_type(unsigned int wcaps)
-{
-       if (!wcaps)
-               return -1; /* invalid type */
-       return (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
-}
-
-static inline unsigned int get_wcaps_channels(u32 wcaps)
-{
-       unsigned int chans;
-
-       chans = (wcaps & AC_WCAP_CHAN_CNT_EXT) >> 13;
-       chans = ((chans << 1) | 1) + 1;
-
-       return chans;
-}
-
-static inline void snd_hda_override_wcaps(struct hda_codec *codec,
-                                         hda_nid_t nid, u32 val)
-{
-       if (nid >= codec->core.start_nid &&
-           nid < codec->core.start_nid + codec->core.num_nodes)
-               codec->wcaps[nid - codec->core.start_nid] = val;
-}
-
-u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction);
-int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
-                             unsigned int caps);
-/**
- * snd_hda_query_pin_caps - Query PIN capabilities
- * @codec: the HD-auio codec
- * @nid: the NID to query
- *
- * Query PIN capabilities for the given widget.
- * Returns the obtained capability bits.
- *
- * When cap bits have been already read, this doesn't read again but
- * returns the cached value.
- */
-static inline u32
-snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
-{
-       return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
-
-}
-
-/**
- * snd_hda_override_pin_caps - Override the pin capabilities
- * @codec: the CODEC
- * @nid: the NID to override
- * @caps: the capability bits to set
- *
- * Override the cached PIN capabilitiy bits value by the given one.
- *
- * Returns zero if successful or a negative error code.
- */
-static inline int
-snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid,
-                         unsigned int caps)
-{
-       return snd_hdac_override_parm(&codec->core, nid, AC_PAR_PIN_CAP, caps);
-}
-
-bool snd_hda_check_amp_caps(struct hda_codec *codec, hda_nid_t nid,
-                          int dir, unsigned int bits);
-
-#define nid_has_mute(codec, nid, dir) \
-       snd_hda_check_amp_caps(codec, nid, dir, (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE))
-#define nid_has_volume(codec, nid, dir) \
-       snd_hda_check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS)
-
-
-/* flags for hda_nid_item */
-#define HDA_NID_ITEM_AMP       (1<<0)
-
-struct hda_nid_item {
-       struct snd_kcontrol *kctl;
-       unsigned int index;
-       hda_nid_t nid;
-       unsigned short flags;
-};
-
-int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid,
-                   struct snd_kcontrol *kctl);
-void snd_hda_ctls_clear(struct hda_codec *codec);
-
-/*
- * hwdep interface
- */
-#ifdef CONFIG_SND_HDA_HWDEP
-int snd_hda_create_hwdep(struct hda_codec *codec);
-#else
-static inline int snd_hda_create_hwdep(struct hda_codec *codec) { return 0; }
-#endif
-
-void snd_hda_sysfs_init(struct hda_codec *codec);
-void snd_hda_sysfs_clear(struct hda_codec *codec);
-
-extern const struct attribute_group *snd_hda_dev_attr_groups[];
-
-#ifdef CONFIG_SND_HDA_RECONFIG
-const char *snd_hda_get_hint(struct hda_codec *codec, const char *key);
-int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key);
-int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp);
-#else
-static inline
-const char *snd_hda_get_hint(struct hda_codec *codec, const char *key)
-{
-       return NULL;
-}
-
-static inline
-int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
-{
-       return -ENOENT;
-}
-
-static inline
-int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp)
-{
-       return -ENOENT;
-}
-#endif
-
-/*
- * power-management
- */
-
-void snd_hda_schedule_power_save(struct hda_codec *codec);
-
-struct hda_amp_list {
-       hda_nid_t nid;
-       unsigned char dir;
-       unsigned char idx;
-};
-
-struct hda_loopback_check {
-       const struct hda_amp_list *amplist;
-       int power_on;
-};
-
-int snd_hda_check_amp_list_power(struct hda_codec *codec,
-                                struct hda_loopback_check *check,
-                                hda_nid_t nid);
-
-/* check whether the actual power state matches with the target state */
-static inline bool
-snd_hda_check_power_state(struct hda_codec *codec, hda_nid_t nid,
-                         unsigned int target_state)
-{
-       return snd_hdac_check_power_state(&codec->core, nid, target_state);
-}
-
-static inline unsigned int snd_hda_sync_power_state(struct hda_codec *codec,
-                                                   hda_nid_t nid,
-                                                   unsigned int target_state)
-{
-       return snd_hdac_sync_power_state(&codec->core, nid, target_state);
-}
-unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec,
-                                            hda_nid_t nid,
-                                            unsigned int power_state);
-
-void snd_hda_codec_shutdown(struct hda_codec *codec);
-
-/*
- * AMP control callbacks
- */
-/* retrieve parameters from private_value */
-#define get_amp_nid_(pv)       ((pv) & 0xffff)
-#define get_amp_nid(kc)                get_amp_nid_((kc)->private_value)
-#define get_amp_channels(kc)   (((kc)->private_value >> 16) & 0x3)
-#define get_amp_direction_(pv) (((pv) >> 18) & 0x1)
-#define get_amp_direction(kc)  get_amp_direction_((kc)->private_value)
-#define get_amp_index_(pv)     (((pv) >> 19) & 0xf)
-#define get_amp_index(kc)      get_amp_index_((kc)->private_value)
-#define get_amp_offset(kc)     (((kc)->private_value >> 23) & 0x3f)
-#define get_amp_min_mute(kc)   (((kc)->private_value >> 29) & 0x1)
-
-/*
- * enum control helper
- */
-int snd_hda_enum_helper_info(struct snd_kcontrol *kcontrol,
-                            struct snd_ctl_elem_info *uinfo,
-                            int num_items, const char * const *texts);
-#define snd_hda_enum_bool_helper_info(kcontrol, uinfo) \
-       snd_hda_enum_helper_info(kcontrol, uinfo, 0, NULL)
-
-struct hdmi_eld {
-       bool    monitor_present;
-       bool    eld_valid;
-       int     eld_size;
-       char    eld_buffer[ELD_MAX_SIZE];
-       struct snd_parsed_hdmi_eld info;
-};
-
-int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid);
-int snd_hdmi_get_eld(struct hda_codec *codec, hda_nid_t nid,
-                    unsigned char *buf, int *eld_size);
-void snd_hdmi_eld_update_pcm_info(struct snd_parsed_hdmi_eld *e,
-                             struct hda_pcm_stream *hinfo);
-
-int snd_hdmi_get_eld_ati(struct hda_codec *codec, hda_nid_t nid,
-                        unsigned char *buf, int *eld_size,
-                        bool rev3_or_later);
-
-#ifdef CONFIG_SND_PROC_FS
-void snd_hdmi_print_eld_info(struct hdmi_eld *eld,
-                            struct snd_info_buffer *buffer,
-                            hda_nid_t pin_nid, int dev_id, hda_nid_t cvt_nid);
-void snd_hdmi_write_eld_info(struct hdmi_eld *eld,
-                            struct snd_info_buffer *buffer);
-#endif
-
-#define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80
-void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen);
-
-void snd_hda_codec_display_power(struct hda_codec *codec, bool enable);
-
-/*
- */
-#define codec_err(codec, fmt, args...) \
-       dev_err(hda_codec_dev(codec), fmt, ##args)
-#define codec_warn(codec, fmt, args...) \
-       dev_warn(hda_codec_dev(codec), fmt, ##args)
-#define codec_info(codec, fmt, args...) \
-       dev_info(hda_codec_dev(codec), fmt, ##args)
-#define codec_dbg(codec, fmt, args...) \
-       dev_dbg(hda_codec_dev(codec), fmt, ##args)
-
-#endif /* __SOUND_HDA_LOCAL_H */
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
deleted file mode 100644 (file)
index 00c2eeb..0000000
+++ /dev/null
@@ -1,948 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Universal Interface for Intel High Definition Audio Codec
- * 
- * Generic proc interface
- *
- * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- */
-
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include <linux/module.h>
-#include <sound/hda_codec.h>
-#include "hda_local.h"
-
-static int dump_coef = -1;
-module_param(dump_coef, int, 0644);
-MODULE_PARM_DESC(dump_coef, "Dump processing coefficients in codec proc file (-1=auto, 0=disable, 1=enable)");
-
-/* always use noncached version */
-#define param_read(codec, nid, parm) \
-       snd_hdac_read_parm_uncached(&(codec)->core, nid, parm)
-
-static const char *get_wid_type_name(unsigned int wid_value)
-{
-       static const char * const names[16] = {
-               [AC_WID_AUD_OUT] = "Audio Output",
-               [AC_WID_AUD_IN] = "Audio Input",
-               [AC_WID_AUD_MIX] = "Audio Mixer",
-               [AC_WID_AUD_SEL] = "Audio Selector",
-               [AC_WID_PIN] = "Pin Complex",
-               [AC_WID_POWER] = "Power Widget",
-               [AC_WID_VOL_KNB] = "Volume Knob Widget",
-               [AC_WID_BEEP] = "Beep Generator Widget",
-               [AC_WID_VENDOR] = "Vendor Defined Widget",
-       };
-       if (wid_value == -1)
-               return "UNKNOWN Widget";
-       wid_value &= 0xf;
-       if (names[wid_value])
-               return names[wid_value];
-       else
-               return "UNKNOWN Widget";
-}
-
-static void print_nid_array(struct snd_info_buffer *buffer,
-                           struct hda_codec *codec, hda_nid_t nid,
-                           struct snd_array *array)
-{
-       int i;
-       struct hda_nid_item *items = array->list, *item;
-       struct snd_kcontrol *kctl;
-       for (i = 0; i < array->used; i++) {
-               item = &items[i];
-               if (item->nid == nid) {
-                       kctl = item->kctl;
-                       snd_iprintf(buffer,
-                         "  Control: name=\"%s\", index=%i, device=%i\n",
-                         kctl->id.name, kctl->id.index + item->index,
-                         kctl->id.device);
-                       if (item->flags & HDA_NID_ITEM_AMP)
-                               snd_iprintf(buffer,
-                                 "    ControlAmp: chs=%lu, dir=%s, "
-                                 "idx=%lu, ofs=%lu\n",
-                                 get_amp_channels(kctl),
-                                 get_amp_direction(kctl) ? "Out" : "In",
-                                 get_amp_index(kctl),
-                                 get_amp_offset(kctl));
-               }
-       }
-}
-
-static void print_nid_pcms(struct snd_info_buffer *buffer,
-                          struct hda_codec *codec, hda_nid_t nid)
-{
-       int type;
-       struct hda_pcm *cpcm;
-
-       list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
-               for (type = 0; type < 2; type++) {
-                       if (cpcm->stream[type].nid != nid || cpcm->pcm == NULL)
-                               continue;
-                       snd_iprintf(buffer, "  Device: name=\"%s\", "
-                                   "type=\"%s\", device=%i\n",
-                                   cpcm->name,
-                                   snd_hda_pcm_type_name[cpcm->pcm_type],
-                                   cpcm->pcm->device);
-               }
-       }
-}
-
-static void print_amp_caps(struct snd_info_buffer *buffer,
-                          struct hda_codec *codec, hda_nid_t nid, int dir)
-{
-       unsigned int caps;
-       caps = param_read(codec, nid, dir == HDA_OUTPUT ?
-                         AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
-       if (caps == -1 || caps == 0) {
-               snd_iprintf(buffer, "N/A\n");
-               return;
-       }
-       snd_iprintf(buffer, "ofs=0x%02x, nsteps=0x%02x, stepsize=0x%02x, "
-                   "mute=%x\n",
-                   caps & AC_AMPCAP_OFFSET,
-                   (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT,
-                   (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT,
-                   (caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT);
-}
-
-/* is this a stereo widget or a stereo-to-mono mix? */
-static bool is_stereo_amps(struct hda_codec *codec, hda_nid_t nid,
-                          int dir, unsigned int wcaps, int indices)
-{
-       hda_nid_t conn;
-
-       if (wcaps & AC_WCAP_STEREO)
-               return true;
-       /* check for a stereo-to-mono mix; it must be:
-        * only a single connection, only for input, and only a mixer widget
-        */
-       if (indices != 1 || dir != HDA_INPUT ||
-           get_wcaps_type(wcaps) != AC_WID_AUD_MIX)
-               return false;
-
-       if (snd_hda_get_raw_connections(codec, nid, &conn, 1) < 0)
-               return false;
-       /* the connection source is a stereo? */
-       wcaps = snd_hda_param_read(codec, conn, AC_PAR_AUDIO_WIDGET_CAP);
-       return !!(wcaps & AC_WCAP_STEREO);
-}
-
-static void print_amp_vals(struct snd_info_buffer *buffer,
-                          struct hda_codec *codec, hda_nid_t nid,
-                          int dir, unsigned int wcaps, int indices)
-{
-       unsigned int val;
-       bool stereo;
-       int i;
-
-       stereo = is_stereo_amps(codec, nid, dir, wcaps, indices);
-
-       dir = dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
-       for (i = 0; i < indices; i++) {
-               snd_iprintf(buffer, " [");
-               val = snd_hda_codec_read(codec, nid, 0,
-                                        AC_VERB_GET_AMP_GAIN_MUTE,
-                                        AC_AMP_GET_LEFT | dir | i);
-               snd_iprintf(buffer, "0x%02x", val);
-               if (stereo) {
-                       val = snd_hda_codec_read(codec, nid, 0,
-                                                AC_VERB_GET_AMP_GAIN_MUTE,
-                                                AC_AMP_GET_RIGHT | dir | i);
-                       snd_iprintf(buffer, " 0x%02x", val);
-               }
-               snd_iprintf(buffer, "]");
-       }
-       snd_iprintf(buffer, "\n");
-}
-
-static void print_pcm_rates(struct snd_info_buffer *buffer, unsigned int pcm)
-{
-       static const unsigned int rates[] = {
-               8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200,
-               96000, 176400, 192000, 384000
-       };
-       int i;
-
-       pcm &= AC_SUPPCM_RATES;
-       snd_iprintf(buffer, "    rates [0x%x]:", pcm);
-       for (i = 0; i < ARRAY_SIZE(rates); i++)
-               if (pcm & (1 << i))
-                       snd_iprintf(buffer,  " %d", rates[i]);
-       snd_iprintf(buffer, "\n");
-}
-
-static void print_pcm_bits(struct snd_info_buffer *buffer, unsigned int pcm)
-{
-       char buf[SND_PRINT_BITS_ADVISED_BUFSIZE];
-
-       snd_iprintf(buffer, "    bits [0x%x]:", (pcm >> 16) & 0xff);
-       snd_print_pcm_bits(pcm, buf, sizeof(buf));
-       snd_iprintf(buffer, "%s\n", buf);
-}
-
-static void print_pcm_formats(struct snd_info_buffer *buffer,
-                             unsigned int streams)
-{
-       snd_iprintf(buffer, "    formats [0x%x]:", streams & 0xf);
-       if (streams & AC_SUPFMT_PCM)
-               snd_iprintf(buffer, " PCM");
-       if (streams & AC_SUPFMT_FLOAT32)
-               snd_iprintf(buffer, " FLOAT");
-       if (streams & AC_SUPFMT_AC3)
-               snd_iprintf(buffer, " AC3");
-       snd_iprintf(buffer, "\n");
-}
-
-static void print_pcm_caps(struct snd_info_buffer *buffer,
-                          struct hda_codec *codec, hda_nid_t nid)
-{
-       unsigned int pcm = param_read(codec, nid, AC_PAR_PCM);
-       unsigned int stream = param_read(codec, nid, AC_PAR_STREAM);
-       if (pcm == -1 || stream == -1) {
-               snd_iprintf(buffer, "N/A\n");
-               return;
-       }
-       print_pcm_rates(buffer, pcm);
-       print_pcm_bits(buffer, pcm);
-       print_pcm_formats(buffer, stream);
-}
-
-static const char *get_jack_connection(u32 cfg)
-{
-       static const char * const names[16] = {
-               "Unknown", "1/8", "1/4", "ATAPI",
-               "RCA", "Optical","Digital", "Analog",
-               "DIN", "XLR", "RJ11", "Comb",
-               NULL, NULL, NULL, "Other"
-       };
-       cfg = (cfg & AC_DEFCFG_CONN_TYPE) >> AC_DEFCFG_CONN_TYPE_SHIFT;
-       if (names[cfg])
-               return names[cfg];
-       else
-               return "UNKNOWN";
-}
-
-static const char *get_jack_color(u32 cfg)
-{
-       static const char * const names[16] = {
-               "Unknown", "Black", "Grey", "Blue",
-               "Green", "Red", "Orange", "Yellow",
-               "Purple", "Pink", NULL, NULL,
-               NULL, NULL, "White", "Other",
-       };
-       cfg = (cfg & AC_DEFCFG_COLOR) >> AC_DEFCFG_COLOR_SHIFT;
-       if (names[cfg])
-               return names[cfg];
-       else
-               return "UNKNOWN";
-}
-
-/*
- * Parse the pin default config value and returns the string of the
- * jack location, e.g. "Rear", "Front", etc.
- */
-static const char *get_jack_location(u32 cfg)
-{
-       static const char * const bases[7] = {
-               "N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom",
-       };
-       static const unsigned char specials_idx[] = {
-               0x07, 0x08,
-               0x17, 0x18, 0x19,
-               0x37, 0x38
-       };
-       static const char * const specials[] = {
-               "Rear Panel", "Drive Bar",
-               "Riser", "HDMI", "ATAPI",
-               "Mobile-In", "Mobile-Out"
-       };
-       int i;
-
-       cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT;
-       if ((cfg & 0x0f) < 7)
-               return bases[cfg & 0x0f];
-       for (i = 0; i < ARRAY_SIZE(specials_idx); i++) {
-               if (cfg == specials_idx[i])
-                       return specials[i];
-       }
-       return "UNKNOWN";
-}
-
-/*
- * Parse the pin default config value and returns the string of the
- * jack connectivity, i.e. external or internal connection.
- */
-static const char *get_jack_connectivity(u32 cfg)
-{
-       static const char * const jack_locations[4] = {
-               "Ext", "Int", "Sep", "Oth"
-       };
-
-       return jack_locations[(cfg >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3];
-}
-
-/*
- * Parse the pin default config value and returns the string of the
- * jack type, i.e. the purpose of the jack, such as Line-Out or CD.
- */
-static const char *get_jack_type(u32 cfg)
-{
-       static const char * const jack_types[16] = {
-               "Line Out", "Speaker", "HP Out", "CD",
-               "SPDIF Out", "Digital Out", "Modem Line", "Modem Hand",
-               "Line In", "Aux", "Mic", "Telephony",
-               "SPDIF In", "Digital In", "Reserved", "Other"
-       };
-
-       return jack_types[(cfg & AC_DEFCFG_DEVICE)
-                               >> AC_DEFCFG_DEVICE_SHIFT];
-}
-
-static void print_pin_caps(struct snd_info_buffer *buffer,
-                          struct hda_codec *codec, hda_nid_t nid,
-                          int *supports_vref)
-{
-       static const char * const jack_conns[4] = {
-               "Jack", "N/A", "Fixed", "Both"
-       };
-       unsigned int caps, val;
-
-       caps = param_read(codec, nid, AC_PAR_PIN_CAP);
-       snd_iprintf(buffer, "  Pincap 0x%08x:", caps);
-       if (caps & AC_PINCAP_IN)
-               snd_iprintf(buffer, " IN");
-       if (caps & AC_PINCAP_OUT)
-               snd_iprintf(buffer, " OUT");
-       if (caps & AC_PINCAP_HP_DRV)
-               snd_iprintf(buffer, " HP");
-       if (caps & AC_PINCAP_EAPD)
-               snd_iprintf(buffer, " EAPD");
-       if (caps & AC_PINCAP_PRES_DETECT)
-               snd_iprintf(buffer, " Detect");
-       if (caps & AC_PINCAP_BALANCE)
-               snd_iprintf(buffer, " Balanced");
-       if (caps & AC_PINCAP_HDMI) {
-               /* Realtek uses this bit as a different meaning */
-               if ((codec->core.vendor_id >> 16) == 0x10ec)
-                       snd_iprintf(buffer, " R/L");
-               else {
-                       if (caps & AC_PINCAP_HBR)
-                               snd_iprintf(buffer, " HBR");
-                       snd_iprintf(buffer, " HDMI");
-               }
-       }
-       if (caps & AC_PINCAP_DP)
-               snd_iprintf(buffer, " DP");
-       if (caps & AC_PINCAP_TRIG_REQ)
-               snd_iprintf(buffer, " Trigger");
-       if (caps & AC_PINCAP_IMP_SENSE)
-               snd_iprintf(buffer, " ImpSense");
-       snd_iprintf(buffer, "\n");
-       if (caps & AC_PINCAP_VREF) {
-               unsigned int vref =
-                       (caps & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
-               snd_iprintf(buffer, "    Vref caps:");
-               if (vref & AC_PINCAP_VREF_HIZ)
-                       snd_iprintf(buffer, " HIZ");
-               if (vref & AC_PINCAP_VREF_50)
-                       snd_iprintf(buffer, " 50");
-               if (vref & AC_PINCAP_VREF_GRD)
-                       snd_iprintf(buffer, " GRD");
-               if (vref & AC_PINCAP_VREF_80)
-                       snd_iprintf(buffer, " 80");
-               if (vref & AC_PINCAP_VREF_100)
-                       snd_iprintf(buffer, " 100");
-               snd_iprintf(buffer, "\n");
-               *supports_vref = 1;
-       } else
-               *supports_vref = 0;
-       if (caps & AC_PINCAP_EAPD) {
-               val = snd_hda_codec_read(codec, nid, 0,
-                                        AC_VERB_GET_EAPD_BTLENABLE, 0);
-               snd_iprintf(buffer, "  EAPD 0x%x:", val);
-               if (val & AC_EAPDBTL_BALANCED)
-                       snd_iprintf(buffer, " BALANCED");
-               if (val & AC_EAPDBTL_EAPD)
-                       snd_iprintf(buffer, " EAPD");
-               if (val & AC_EAPDBTL_LR_SWAP)
-                       snd_iprintf(buffer, " R/L");
-               snd_iprintf(buffer, "\n");
-       }
-       caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
-       snd_iprintf(buffer, "  Pin Default 0x%08x: [%s] %s at %s %s\n", caps,
-                   jack_conns[(caps & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT],
-                   get_jack_type(caps),
-                   get_jack_connectivity(caps),
-                   get_jack_location(caps));
-       snd_iprintf(buffer, "    Conn = %s, Color = %s\n",
-                   get_jack_connection(caps),
-                   get_jack_color(caps));
-       /* Default association and sequence values refer to default grouping
-        * of pin complexes and their sequence within the group. This is used
-        * for priority and resource allocation.
-        */
-       snd_iprintf(buffer, "    DefAssociation = 0x%x, Sequence = 0x%x\n",
-                   (caps & AC_DEFCFG_DEF_ASSOC) >> AC_DEFCFG_ASSOC_SHIFT,
-                   caps & AC_DEFCFG_SEQUENCE);
-       if (((caps & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT) &
-           AC_DEFCFG_MISC_NO_PRESENCE) {
-               /* Miscellaneous bit indicates external hardware does not
-                * support presence detection even if the pin complex
-                * indicates it is supported.
-                */
-               snd_iprintf(buffer, "    Misc = NO_PRESENCE\n");
-       }
-}
-
-static void print_pin_ctls(struct snd_info_buffer *buffer,
-                          struct hda_codec *codec, hda_nid_t nid,
-                          int supports_vref)
-{
-       unsigned int pinctls;
-
-       pinctls = snd_hda_codec_read(codec, nid, 0,
-                                    AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
-       snd_iprintf(buffer, "  Pin-ctls: 0x%02x:", pinctls);
-       if (pinctls & AC_PINCTL_IN_EN)
-               snd_iprintf(buffer, " IN");
-       if (pinctls & AC_PINCTL_OUT_EN)
-               snd_iprintf(buffer, " OUT");
-       if (pinctls & AC_PINCTL_HP_EN)
-               snd_iprintf(buffer, " HP");
-       if (supports_vref) {
-               int vref = pinctls & AC_PINCTL_VREFEN;
-               switch (vref) {
-               case AC_PINCTL_VREF_HIZ:
-                       snd_iprintf(buffer, " VREF_HIZ");
-                       break;
-               case AC_PINCTL_VREF_50:
-                       snd_iprintf(buffer, " VREF_50");
-                       break;
-               case AC_PINCTL_VREF_GRD:
-                       snd_iprintf(buffer, " VREF_GRD");
-                       break;
-               case AC_PINCTL_VREF_80:
-                       snd_iprintf(buffer, " VREF_80");
-                       break;
-               case AC_PINCTL_VREF_100:
-                       snd_iprintf(buffer, " VREF_100");
-                       break;
-               }
-       }
-       snd_iprintf(buffer, "\n");
-}
-
-static void print_vol_knob(struct snd_info_buffer *buffer,
-                          struct hda_codec *codec, hda_nid_t nid)
-{
-       unsigned int cap = param_read(codec, nid, AC_PAR_VOL_KNB_CAP);
-       snd_iprintf(buffer, "  Volume-Knob: delta=%d, steps=%d, ",
-                   (cap >> 7) & 1, cap & 0x7f);
-       cap = snd_hda_codec_read(codec, nid, 0,
-                                AC_VERB_GET_VOLUME_KNOB_CONTROL, 0);
-       snd_iprintf(buffer, "direct=%d, val=%d\n",
-                   (cap >> 7) & 1, cap & 0x7f);
-}
-
-static void print_audio_io(struct snd_info_buffer *buffer,
-                          struct hda_codec *codec, hda_nid_t nid,
-                          unsigned int wid_type)
-{
-       int conv = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
-       snd_iprintf(buffer,
-                   "  Converter: stream=%d, channel=%d\n",
-                   (conv & AC_CONV_STREAM) >> AC_CONV_STREAM_SHIFT,
-                   conv & AC_CONV_CHANNEL);
-
-       if (wid_type == AC_WID_AUD_IN && (conv & AC_CONV_CHANNEL) == 0) {
-               int sdi = snd_hda_codec_read(codec, nid, 0,
-                                            AC_VERB_GET_SDI_SELECT, 0);
-               snd_iprintf(buffer, "  SDI-Select: %d\n",
-                           sdi & AC_SDI_SELECT);
-       }
-}
-
-static void print_digital_conv(struct snd_info_buffer *buffer,
-                              struct hda_codec *codec, hda_nid_t nid)
-{
-       unsigned int digi1 = snd_hda_codec_read(codec, nid, 0,
-                                               AC_VERB_GET_DIGI_CONVERT_1, 0);
-       unsigned char digi2 = digi1 >> 8;
-       unsigned char digi3 = digi1 >> 16;
-
-       snd_iprintf(buffer, "  Digital:");
-       if (digi1 & AC_DIG1_ENABLE)
-               snd_iprintf(buffer, " Enabled");
-       if (digi1 & AC_DIG1_V)
-               snd_iprintf(buffer, " Validity");
-       if (digi1 & AC_DIG1_VCFG)
-               snd_iprintf(buffer, " ValidityCfg");
-       if (digi1 & AC_DIG1_EMPHASIS)
-               snd_iprintf(buffer, " Preemphasis");
-       if (digi1 & AC_DIG1_COPYRIGHT)
-               snd_iprintf(buffer, " Non-Copyright");
-       if (digi1 & AC_DIG1_NONAUDIO)
-               snd_iprintf(buffer, " Non-Audio");
-       if (digi1 & AC_DIG1_PROFESSIONAL)
-               snd_iprintf(buffer, " Pro");
-       if (digi1 & AC_DIG1_LEVEL)
-               snd_iprintf(buffer, " GenLevel");
-       if (digi3 & AC_DIG3_KAE)
-               snd_iprintf(buffer, " KAE");
-       snd_iprintf(buffer, "\n");
-       snd_iprintf(buffer, "  Digital category: 0x%x\n",
-                   digi2 & AC_DIG2_CC);
-       snd_iprintf(buffer, "  IEC Coding Type: 0x%x\n",
-                       digi3 & AC_DIG3_ICT);
-}
-
-static const char *get_pwr_state(u32 state)
-{
-       static const char * const buf[] = {
-               "D0", "D1", "D2", "D3", "D3cold"
-       };
-       if (state < ARRAY_SIZE(buf))
-               return buf[state];
-       return "UNKNOWN";
-}
-
-static void print_power_state(struct snd_info_buffer *buffer,
-                             struct hda_codec *codec, hda_nid_t nid)
-{
-       static const char * const names[] = {
-               [ilog2(AC_PWRST_D0SUP)]         = "D0",
-               [ilog2(AC_PWRST_D1SUP)]         = "D1",
-               [ilog2(AC_PWRST_D2SUP)]         = "D2",
-               [ilog2(AC_PWRST_D3SUP)]         = "D3",
-               [ilog2(AC_PWRST_D3COLDSUP)]     = "D3cold",
-               [ilog2(AC_PWRST_S3D3COLDSUP)]   = "S3D3cold",
-               [ilog2(AC_PWRST_CLKSTOP)]       = "CLKSTOP",
-               [ilog2(AC_PWRST_EPSS)]          = "EPSS",
-       };
-
-       int sup = param_read(codec, nid, AC_PAR_POWER_STATE);
-       int pwr = snd_hda_codec_read(codec, nid, 0,
-                                    AC_VERB_GET_POWER_STATE, 0);
-       if (sup != -1) {
-               int i;
-
-               snd_iprintf(buffer, "  Power states: ");
-               for (i = 0; i < ARRAY_SIZE(names); i++) {
-                       if (sup & (1U << i))
-                               snd_iprintf(buffer, " %s", names[i]);
-               }
-               snd_iprintf(buffer, "\n");
-       }
-
-       snd_iprintf(buffer, "  Power: setting=%s, actual=%s",
-                   get_pwr_state(pwr & AC_PWRST_SETTING),
-                   get_pwr_state((pwr & AC_PWRST_ACTUAL) >>
-                                 AC_PWRST_ACTUAL_SHIFT));
-       if (pwr & AC_PWRST_ERROR)
-               snd_iprintf(buffer, ", Error");
-       if (pwr & AC_PWRST_CLK_STOP_OK)
-               snd_iprintf(buffer, ", Clock-stop-OK");
-       if (pwr & AC_PWRST_SETTING_RESET)
-               snd_iprintf(buffer, ", Setting-reset");
-       snd_iprintf(buffer, "\n");
-}
-
-static void print_unsol_cap(struct snd_info_buffer *buffer,
-                             struct hda_codec *codec, hda_nid_t nid)
-{
-       int unsol = snd_hda_codec_read(codec, nid, 0,
-                                      AC_VERB_GET_UNSOLICITED_RESPONSE, 0);
-       snd_iprintf(buffer,
-                   "  Unsolicited: tag=%02x, enabled=%d\n",
-                   unsol & AC_UNSOL_TAG,
-                   (unsol & AC_UNSOL_ENABLED) ? 1 : 0);
-}
-
-static inline bool can_dump_coef(struct hda_codec *codec)
-{
-       switch (dump_coef) {
-       case 0: return false;
-       case 1: return true;
-       default: return codec->dump_coef;
-       }
-}
-
-static void print_proc_caps(struct snd_info_buffer *buffer,
-                           struct hda_codec *codec, hda_nid_t nid)
-{
-       unsigned int i, ncoeff, oldindex;
-       unsigned int proc_caps = param_read(codec, nid, AC_PAR_PROC_CAP);
-       ncoeff = (proc_caps & AC_PCAP_NUM_COEF) >> AC_PCAP_NUM_COEF_SHIFT;
-       snd_iprintf(buffer, "  Processing caps: benign=%d, ncoeff=%d\n",
-                   proc_caps & AC_PCAP_BENIGN, ncoeff);
-
-       if (!can_dump_coef(codec))
-               return;
-
-       /* Note: This is racy - another process could run in parallel and change
-          the coef index too. */
-       oldindex = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_COEF_INDEX, 0);
-       for (i = 0; i < ncoeff; i++) {
-               unsigned int val;
-               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, i);
-               val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PROC_COEF,
-                                        0);
-               snd_iprintf(buffer, "    Coeff 0x%02x: 0x%04x\n", i, val);
-       }
-       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, oldindex);
-}
-
-static void print_conn_list(struct snd_info_buffer *buffer,
-                           struct hda_codec *codec, hda_nid_t nid,
-                           unsigned int wid_type, hda_nid_t *conn,
-                           int conn_len)
-{
-       int c, curr = -1;
-       const hda_nid_t *list;
-       int cache_len;
-
-       if (conn_len > 1 &&
-           wid_type != AC_WID_AUD_MIX &&
-           wid_type != AC_WID_VOL_KNB &&
-           wid_type != AC_WID_POWER)
-               curr = snd_hda_codec_read(codec, nid, 0,
-                                         AC_VERB_GET_CONNECT_SEL, 0);
-       snd_iprintf(buffer, "  Connection: %d\n", conn_len);
-       if (conn_len > 0) {
-               snd_iprintf(buffer, "    ");
-               for (c = 0; c < conn_len; c++) {
-                       snd_iprintf(buffer, " 0x%02x", conn[c]);
-                       if (c == curr)
-                               snd_iprintf(buffer, "*");
-               }
-               snd_iprintf(buffer, "\n");
-       }
-
-       /* Get Cache connections info */
-       cache_len = snd_hda_get_conn_list(codec, nid, &list);
-       if (cache_len >= 0 && (cache_len != conn_len ||
-                             memcmp(list, conn, conn_len) != 0)) {
-               snd_iprintf(buffer, "  In-driver Connection: %d\n", cache_len);
-               if (cache_len > 0) {
-                       snd_iprintf(buffer, "    ");
-                       for (c = 0; c < cache_len; c++)
-                               snd_iprintf(buffer, " 0x%02x", list[c]);
-                       snd_iprintf(buffer, "\n");
-               }
-       }
-}
-
-static void print_gpio(struct snd_info_buffer *buffer,
-                      struct hda_codec *codec, hda_nid_t nid)
-{
-       unsigned int gpio =
-               param_read(codec, codec->core.afg, AC_PAR_GPIO_CAP);
-       unsigned int enable, direction, wake, unsol, sticky, data;
-       int i, max;
-       snd_iprintf(buffer, "GPIO: io=%d, o=%d, i=%d, "
-                   "unsolicited=%d, wake=%d\n",
-                   gpio & AC_GPIO_IO_COUNT,
-                   (gpio & AC_GPIO_O_COUNT) >> AC_GPIO_O_COUNT_SHIFT,
-                   (gpio & AC_GPIO_I_COUNT) >> AC_GPIO_I_COUNT_SHIFT,
-                   (gpio & AC_GPIO_UNSOLICITED) ? 1 : 0,
-                   (gpio & AC_GPIO_WAKE) ? 1 : 0);
-       max = gpio & AC_GPIO_IO_COUNT;
-       if (!max || max > 8)
-               return;
-       enable = snd_hda_codec_read(codec, nid, 0,
-                                   AC_VERB_GET_GPIO_MASK, 0);
-       direction = snd_hda_codec_read(codec, nid, 0,
-                                      AC_VERB_GET_GPIO_DIRECTION, 0);
-       wake = snd_hda_codec_read(codec, nid, 0,
-                                 AC_VERB_GET_GPIO_WAKE_MASK, 0);
-       unsol  = snd_hda_codec_read(codec, nid, 0,
-                                   AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK, 0);
-       sticky = snd_hda_codec_read(codec, nid, 0,
-                                   AC_VERB_GET_GPIO_STICKY_MASK, 0);
-       data = snd_hda_codec_read(codec, nid, 0,
-                                 AC_VERB_GET_GPIO_DATA, 0);
-       for (i = 0; i < max; ++i)
-               snd_iprintf(buffer,
-                           "  IO[%d]: enable=%d, dir=%d, wake=%d, "
-                           "sticky=%d, data=%d, unsol=%d\n", i,
-                           (enable & (1<<i)) ? 1 : 0,
-                           (direction & (1<<i)) ? 1 : 0,
-                           (wake & (1<<i)) ? 1 : 0,
-                           (sticky & (1<<i)) ? 1 : 0,
-                           (data & (1<<i)) ? 1 : 0,
-                           (unsol & (1<<i)) ? 1 : 0);
-       /* FIXME: add GPO and GPI pin information */
-       print_nid_array(buffer, codec, nid, &codec->mixers);
-       print_nid_array(buffer, codec, nid, &codec->nids);
-}
-
-static void print_dpmst_connections(struct snd_info_buffer *buffer, struct hda_codec *codec,
-                                   hda_nid_t nid, int dev_num)
-{
-       int c, conn_len, curr, dev_id_saved;
-       hda_nid_t *conn;
-
-       conn_len = snd_hda_get_num_raw_conns(codec, nid);
-       if (conn_len <= 0)
-               return;
-
-       conn = kmalloc_array(conn_len, sizeof(hda_nid_t), GFP_KERNEL);
-       if (!conn)
-               return;
-
-       dev_id_saved = snd_hda_get_dev_select(codec, nid);
-
-       snd_hda_set_dev_select(codec, nid, dev_num);
-       curr = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0);
-       if (snd_hda_get_raw_connections(codec, nid, conn, conn_len) < 0)
-               goto out;
-
-       for (c = 0; c < conn_len; c++) {
-               snd_iprintf(buffer, " 0x%02x", conn[c]);
-               if (c == curr)
-                       snd_iprintf(buffer, "*");
-       }
-
-out:
-       kfree(conn);
-       snd_hda_set_dev_select(codec, nid, dev_id_saved);
-}
-
-static void print_device_list(struct snd_info_buffer *buffer,
-                           struct hda_codec *codec, hda_nid_t nid)
-{
-       int i, curr = -1;
-       u8 dev_list[AC_MAX_DEV_LIST_LEN];
-       int devlist_len;
-
-       devlist_len = snd_hda_get_devices(codec, nid, dev_list,
-                                       AC_MAX_DEV_LIST_LEN);
-       snd_iprintf(buffer, "  Devices: %d\n", devlist_len);
-       if (devlist_len <= 0)
-               return;
-
-       curr = snd_hda_codec_read(codec, nid, 0,
-                               AC_VERB_GET_DEVICE_SEL, 0);
-
-       for (i = 0; i < devlist_len; i++) {
-               if (i == curr)
-                       snd_iprintf(buffer, "    *");
-               else
-                       snd_iprintf(buffer, "     ");
-
-               snd_iprintf(buffer,
-                       "Dev %02d: PD = %d, ELDV = %d, IA = %d, Connections [", i,
-                       !!(dev_list[i] & AC_DE_PD),
-                       !!(dev_list[i] & AC_DE_ELDV),
-                       !!(dev_list[i] & AC_DE_IA));
-
-               print_dpmst_connections(buffer, codec, nid, i);
-
-               snd_iprintf(buffer, " ]\n");
-       }
-}
-
-static void print_codec_core_info(struct hdac_device *codec,
-                                 struct snd_info_buffer *buffer)
-{
-       snd_iprintf(buffer, "Codec: ");
-       if (codec->vendor_name && codec->chip_name)
-               snd_iprintf(buffer, "%s %s\n",
-                           codec->vendor_name, codec->chip_name);
-       else
-               snd_iprintf(buffer, "Not Set\n");
-       snd_iprintf(buffer, "Address: %d\n", codec->addr);
-       if (codec->afg)
-               snd_iprintf(buffer, "AFG Function Id: 0x%x (unsol %u)\n",
-                       codec->afg_function_id, codec->afg_unsol);
-       if (codec->mfg)
-               snd_iprintf(buffer, "MFG Function Id: 0x%x (unsol %u)\n",
-                       codec->mfg_function_id, codec->mfg_unsol);
-       snd_iprintf(buffer, "Vendor Id: 0x%08x\n", codec->vendor_id);
-       snd_iprintf(buffer, "Subsystem Id: 0x%08x\n", codec->subsystem_id);
-       snd_iprintf(buffer, "Revision Id: 0x%x\n", codec->revision_id);
-
-       if (codec->mfg)
-               snd_iprintf(buffer, "Modem Function Group: 0x%x\n", codec->mfg);
-       else
-               snd_iprintf(buffer, "No Modem Function Group found\n");
-}
-
-static void print_codec_info(struct snd_info_entry *entry,
-                            struct snd_info_buffer *buffer)
-{
-       struct hda_codec *codec = entry->private_data;
-       hda_nid_t nid, fg;
-       int i, nodes;
-
-       print_codec_core_info(&codec->core, buffer);
-       fg = codec->core.afg;
-       if (!fg)
-               return;
-       snd_hda_power_up(codec);
-       snd_iprintf(buffer, "Default PCM:\n");
-       print_pcm_caps(buffer, codec, fg);
-       snd_iprintf(buffer, "Default Amp-In caps: ");
-       print_amp_caps(buffer, codec, fg, HDA_INPUT);
-       snd_iprintf(buffer, "Default Amp-Out caps: ");
-       print_amp_caps(buffer, codec, fg, HDA_OUTPUT);
-       snd_iprintf(buffer, "State of AFG node 0x%02x:\n", fg);
-       print_power_state(buffer, codec, fg);
-
-       nodes = snd_hda_get_sub_nodes(codec, fg, &nid);
-       if (! nid || nodes < 0) {
-               snd_iprintf(buffer, "Invalid AFG subtree\n");
-               snd_hda_power_down(codec);
-               return;
-       }
-
-       print_gpio(buffer, codec, fg);
-       if (codec->proc_widget_hook)
-               codec->proc_widget_hook(buffer, codec, fg);
-
-       for (i = 0; i < nodes; i++, nid++) {
-               unsigned int wid_caps =
-                       param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
-               unsigned int wid_type = get_wcaps_type(wid_caps);
-               hda_nid_t *conn = NULL;
-               int conn_len = 0;
-
-               snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid,
-                           get_wid_type_name(wid_type), wid_caps);
-               if (wid_caps & AC_WCAP_STEREO) {
-                       unsigned int chans = get_wcaps_channels(wid_caps);
-                       if (chans == 2)
-                               snd_iprintf(buffer, " Stereo");
-                       else
-                               snd_iprintf(buffer, " %d-Channels", chans);
-               } else
-                       snd_iprintf(buffer, " Mono");
-               if (wid_caps & AC_WCAP_DIGITAL)
-                       snd_iprintf(buffer, " Digital");
-               if (wid_caps & AC_WCAP_IN_AMP)
-                       snd_iprintf(buffer, " Amp-In");
-               if (wid_caps & AC_WCAP_OUT_AMP)
-                       snd_iprintf(buffer, " Amp-Out");
-               if (wid_caps & AC_WCAP_STRIPE)
-                       snd_iprintf(buffer, " Stripe");
-               if (wid_caps & AC_WCAP_LR_SWAP)
-                       snd_iprintf(buffer, " R/L");
-               if (wid_caps & AC_WCAP_CP_CAPS)
-                       snd_iprintf(buffer, " CP");
-               snd_iprintf(buffer, "\n");
-
-               print_nid_array(buffer, codec, nid, &codec->mixers);
-               print_nid_array(buffer, codec, nid, &codec->nids);
-               print_nid_pcms(buffer, codec, nid);
-
-               /* volume knob is a special widget that always have connection
-                * list
-                */
-               if (wid_type == AC_WID_VOL_KNB)
-                       wid_caps |= AC_WCAP_CONN_LIST;
-
-               if (wid_caps & AC_WCAP_CONN_LIST) {
-                       conn_len = snd_hda_get_num_raw_conns(codec, nid);
-                       if (conn_len > 0) {
-                               conn = kmalloc_array(conn_len,
-                                                    sizeof(hda_nid_t),
-                                                    GFP_KERNEL);
-                               if (!conn)
-                                       return;
-                               if (snd_hda_get_raw_connections(codec, nid, conn,
-                                                               conn_len) < 0)
-                                       conn_len = 0;
-                       }
-               }
-
-               if (wid_caps & AC_WCAP_IN_AMP) {
-                       snd_iprintf(buffer, "  Amp-In caps: ");
-                       print_amp_caps(buffer, codec, nid, HDA_INPUT);
-                       snd_iprintf(buffer, "  Amp-In vals: ");
-                       if (wid_type == AC_WID_PIN ||
-                           (codec->single_adc_amp &&
-                            wid_type == AC_WID_AUD_IN))
-                               print_amp_vals(buffer, codec, nid, HDA_INPUT,
-                                              wid_caps, 1);
-                       else
-                               print_amp_vals(buffer, codec, nid, HDA_INPUT,
-                                              wid_caps, conn_len);
-               }
-               if (wid_caps & AC_WCAP_OUT_AMP) {
-                       snd_iprintf(buffer, "  Amp-Out caps: ");
-                       print_amp_caps(buffer, codec, nid, HDA_OUTPUT);
-                       snd_iprintf(buffer, "  Amp-Out vals: ");
-                       if (wid_type == AC_WID_PIN &&
-                           codec->pin_amp_workaround)
-                               print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
-                                              wid_caps, conn_len);
-                       else
-                               print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
-                                              wid_caps, 1);
-               }
-
-               switch (wid_type) {
-               case AC_WID_PIN: {
-                       int supports_vref;
-                       print_pin_caps(buffer, codec, nid, &supports_vref);
-                       print_pin_ctls(buffer, codec, nid, supports_vref);
-                       break;
-               }
-               case AC_WID_VOL_KNB:
-                       print_vol_knob(buffer, codec, nid);
-                       break;
-               case AC_WID_AUD_OUT:
-               case AC_WID_AUD_IN:
-                       print_audio_io(buffer, codec, nid, wid_type);
-                       if (wid_caps & AC_WCAP_DIGITAL)
-                               print_digital_conv(buffer, codec, nid);
-                       if (wid_caps & AC_WCAP_FORMAT_OVRD) {
-                               snd_iprintf(buffer, "  PCM:\n");
-                               print_pcm_caps(buffer, codec, nid);
-                       }
-                       break;
-               }
-
-               if (wid_caps & AC_WCAP_UNSOL_CAP)
-                       print_unsol_cap(buffer, codec, nid);
-
-               if (wid_caps & AC_WCAP_POWER)
-                       print_power_state(buffer, codec, nid);
-
-               if (wid_caps & AC_WCAP_DELAY)
-                       snd_iprintf(buffer, "  Delay: %d samples\n",
-                                   (wid_caps & AC_WCAP_DELAY) >>
-                                   AC_WCAP_DELAY_SHIFT);
-
-               if (wid_type == AC_WID_PIN && codec->dp_mst)
-                       print_device_list(buffer, codec, nid);
-
-               if (wid_caps & AC_WCAP_CONN_LIST)
-                       print_conn_list(buffer, codec, nid, wid_type,
-                                       conn, conn_len);
-
-               if (wid_caps & AC_WCAP_PROC_WID)
-                       print_proc_caps(buffer, codec, nid);
-
-               if (codec->proc_widget_hook)
-                       codec->proc_widget_hook(buffer, codec, nid);
-
-               kfree(conn);
-       }
-       snd_hda_power_down(codec);
-}
-
-/*
- * create a proc read
- */
-int snd_hda_codec_proc_new(struct hda_codec *codec)
-{
-       char name[32];
-
-       snprintf(name, sizeof(name), "codec#%d", codec->core.addr);
-       return snd_card_ro_proc_new(codec->card, name, codec, print_codec_info);
-}
-
diff --git a/sound/pci/hda/hda_sysfs.c b/sound/pci/hda/hda_sysfs.c
deleted file mode 100644 (file)
index 140e24b..0000000
+++ /dev/null
@@ -1,792 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * sysfs interface for HD-audio codec
- *
- * Copyright (c) 2014 Takashi Iwai <tiwai@suse.de>
- *
- * split from hda_hwdep.c
- */
-
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/compat.h>
-#include <linux/mutex.h>
-#include <linux/ctype.h>
-#include <linux/string.h>
-#include <linux/export.h>
-#include <sound/core.h>
-#include <sound/hda_codec.h>
-#include "hda_local.h"
-#include <sound/hda_hwdep.h>
-#include <sound/minors.h>
-
-/* hint string pair */
-struct hda_hint {
-       const char *key;
-       const char *val;        /* contained in the same alloc as key */
-};
-
-static ssize_t power_on_acct_show(struct device *dev,
-                                 struct device_attribute *attr,
-                                 char *buf)
-{
-       struct hda_codec *codec = dev_get_drvdata(dev);
-       snd_hda_update_power_acct(codec);
-       return sysfs_emit(buf, "%u\n", jiffies_to_msecs(codec->power_on_acct));
-}
-
-static ssize_t power_off_acct_show(struct device *dev,
-                                  struct device_attribute *attr,
-                                  char *buf)
-{
-       struct hda_codec *codec = dev_get_drvdata(dev);
-       snd_hda_update_power_acct(codec);
-       return sysfs_emit(buf, "%u\n", jiffies_to_msecs(codec->power_off_acct));
-}
-
-static DEVICE_ATTR_RO(power_on_acct);
-static DEVICE_ATTR_RO(power_off_acct);
-
-#define CODEC_INFO_SHOW(type, field)                           \
-static ssize_t type##_show(struct device *dev,                 \
-                          struct device_attribute *attr,       \
-                          char *buf)                           \
-{                                                              \
-       struct hda_codec *codec = dev_get_drvdata(dev);         \
-       return sysfs_emit(buf, "0x%x\n", codec->field);         \
-}
-
-#define CODEC_INFO_STR_SHOW(type, field)                       \
-static ssize_t type##_show(struct device *dev,                 \
-                            struct device_attribute *attr,     \
-                                       char *buf)              \
-{                                                              \
-       struct hda_codec *codec = dev_get_drvdata(dev);         \
-       return sysfs_emit(buf, "%s\n",                          \
-                         codec->field ? codec->field : "");    \
-}
-
-CODEC_INFO_SHOW(vendor_id, core.vendor_id);
-CODEC_INFO_SHOW(subsystem_id, core.subsystem_id);
-CODEC_INFO_SHOW(revision_id, core.revision_id);
-CODEC_INFO_SHOW(afg, core.afg);
-CODEC_INFO_SHOW(mfg, core.mfg);
-CODEC_INFO_STR_SHOW(vendor_name, core.vendor_name);
-CODEC_INFO_STR_SHOW(chip_name, core.chip_name);
-CODEC_INFO_STR_SHOW(modelname, modelname);
-
-static ssize_t pin_configs_show(struct hda_codec *codec,
-                               struct snd_array *list,
-                               char *buf)
-{
-       const struct hda_pincfg *pin;
-       int i, len = 0;
-       mutex_lock(&codec->user_mutex);
-       snd_array_for_each(list, i, pin) {
-               len += sysfs_emit_at(buf, len, "0x%02x 0x%08x\n",
-                                    pin->nid, pin->cfg);
-       }
-       mutex_unlock(&codec->user_mutex);
-       return len;
-}
-
-static ssize_t init_pin_configs_show(struct device *dev,
-                                    struct device_attribute *attr,
-                                    char *buf)
-{
-       struct hda_codec *codec = dev_get_drvdata(dev);
-       return pin_configs_show(codec, &codec->init_pins, buf);
-}
-
-static ssize_t driver_pin_configs_show(struct device *dev,
-                                      struct device_attribute *attr,
-                                      char *buf)
-{
-       struct hda_codec *codec = dev_get_drvdata(dev);
-       return pin_configs_show(codec, &codec->driver_pins, buf);
-}
-
-#ifdef CONFIG_SND_HDA_RECONFIG
-
-/*
- * sysfs interface
- */
-
-static int clear_codec(struct hda_codec *codec)
-{
-       int err;
-
-       err = snd_hda_codec_reset(codec);
-       if (err < 0) {
-               codec_err(codec, "The codec is being used, can't free.\n");
-               return err;
-       }
-       snd_hda_sysfs_clear(codec);
-       return 0;
-}
-
-static int reconfig_codec(struct hda_codec *codec)
-{
-       int err;
-
-       snd_hda_power_up(codec);
-       codec_info(codec, "hda-codec: reconfiguring\n");
-       err = snd_hda_codec_reset(codec);
-       if (err < 0) {
-               codec_err(codec,
-                          "The codec is being used, can't reconfigure.\n");
-               goto error;
-       }
-       err = device_reprobe(hda_codec_dev(codec));
-       if (err < 0)
-               goto error;
-       err = snd_card_register(codec->card);
- error:
-       snd_hda_power_down(codec);
-       return err;
-}
-
-/*
- * allocate a string at most len chars, and remove the trailing EOL
- */
-static char *kstrndup_noeol(const char *src, size_t len)
-{
-       char *s = kstrndup(src, len, GFP_KERNEL);
-       char *p;
-       if (!s)
-               return NULL;
-       p = strchr(s, '\n');
-       if (p)
-               *p = 0;
-       return s;
-}
-
-#define CODEC_INFO_STORE(type, field)                          \
-static ssize_t type##_store(struct device *dev,                        \
-                           struct device_attribute *attr,      \
-                           const char *buf, size_t count)      \
-{                                                              \
-       struct hda_codec *codec = dev_get_drvdata(dev);         \
-       unsigned long val;                                      \
-       int err = kstrtoul(buf, 0, &val);                       \
-       if (err < 0)                                            \
-               return err;                                     \
-       codec->field = val;                                     \
-       return count;                                           \
-}
-
-#define CODEC_INFO_STR_STORE(type, field)                      \
-static ssize_t type##_store(struct device *dev,                        \
-                           struct device_attribute *attr,      \
-                           const char *buf, size_t count)      \
-{                                                              \
-       struct hda_codec *codec = dev_get_drvdata(dev);         \
-       char *s = kstrndup_noeol(buf, 64);                      \
-       if (!s)                                                 \
-               return -ENOMEM;                                 \
-       kfree(codec->field);                                    \
-       codec->field = s;                                       \
-       return count;                                           \
-}
-
-CODEC_INFO_STORE(vendor_id, core.vendor_id);
-CODEC_INFO_STORE(subsystem_id, core.subsystem_id);
-CODEC_INFO_STORE(revision_id, core.revision_id);
-CODEC_INFO_STR_STORE(vendor_name, core.vendor_name);
-CODEC_INFO_STR_STORE(chip_name, core.chip_name);
-CODEC_INFO_STR_STORE(modelname, modelname);
-
-#define CODEC_ACTION_STORE(type)                               \
-static ssize_t type##_store(struct device *dev,                        \
-                           struct device_attribute *attr,      \
-                           const char *buf, size_t count)      \
-{                                                              \
-       struct hda_codec *codec = dev_get_drvdata(dev);         \
-       int err = 0;                                            \
-       if (*buf)                                               \
-               err = type##_codec(codec);                      \
-       return err < 0 ? err : count;                           \
-}
-
-CODEC_ACTION_STORE(reconfig);
-CODEC_ACTION_STORE(clear);
-
-static ssize_t init_verbs_show(struct device *dev,
-                              struct device_attribute *attr,
-                              char *buf)
-{
-       struct hda_codec *codec = dev_get_drvdata(dev);
-       const struct hda_verb *v;
-       int i, len = 0;
-       mutex_lock(&codec->user_mutex);
-       snd_array_for_each(&codec->init_verbs, i, v) {
-               len += sysfs_emit_at(buf, len, "0x%02x 0x%03x 0x%04x\n",
-                                    v->nid, v->verb, v->param);
-       }
-       mutex_unlock(&codec->user_mutex);
-       return len;
-}
-
-static int parse_init_verbs(struct hda_codec *codec, const char *buf)
-{
-       struct hda_verb *v;
-       int nid, verb, param;
-
-       if (sscanf(buf, "%i %i %i", &nid, &verb, &param) != 3)
-               return -EINVAL;
-       if (!nid || !verb)
-               return -EINVAL;
-       mutex_lock(&codec->user_mutex);
-       v = snd_array_new(&codec->init_verbs);
-       if (!v) {
-               mutex_unlock(&codec->user_mutex);
-               return -ENOMEM;
-       }
-       v->nid = nid;
-       v->verb = verb;
-       v->param = param;
-       mutex_unlock(&codec->user_mutex);
-       return 0;
-}
-
-static ssize_t init_verbs_store(struct device *dev,
-                               struct device_attribute *attr,
-                               const char *buf, size_t count)
-{
-       struct hda_codec *codec = dev_get_drvdata(dev);
-       int err = parse_init_verbs(codec, buf);
-       if (err < 0)
-               return err;
-       return count;
-}
-
-static ssize_t hints_show(struct device *dev,
-                         struct device_attribute *attr,
-                         char *buf)
-{
-       struct hda_codec *codec = dev_get_drvdata(dev);
-       const struct hda_hint *hint;
-       int i, len = 0;
-       mutex_lock(&codec->user_mutex);
-       snd_array_for_each(&codec->hints, i, hint) {
-               len += sysfs_emit_at(buf, len, "%s = %s\n",
-                                    hint->key, hint->val);
-       }
-       mutex_unlock(&codec->user_mutex);
-       return len;
-}
-
-static struct hda_hint *get_hint(struct hda_codec *codec, const char *key)
-{
-       struct hda_hint *hint;
-       int i;
-
-       snd_array_for_each(&codec->hints, i, hint) {
-               if (!strcmp(hint->key, key))
-                       return hint;
-       }
-       return NULL;
-}
-
-static void remove_trail_spaces(char *str)
-{
-       char *p;
-       if (!*str)
-               return;
-       p = str + strlen(str) - 1;
-       for (; isspace(*p); p--) {
-               *p = 0;
-               if (p == str)
-                       return;
-       }
-}
-
-#define MAX_HINTS      1024
-
-static int parse_hints(struct hda_codec *codec, const char *buf)
-{
-       char *key, *val;
-       struct hda_hint *hint;
-       int err = 0;
-
-       buf = skip_spaces(buf);
-       if (!*buf || *buf == '#' || *buf == '\n')
-               return 0;
-       if (*buf == '=')
-               return -EINVAL;
-       key = kstrndup_noeol(buf, 1024);
-       if (!key)
-               return -ENOMEM;
-       /* extract key and val */
-       val = strchr(key, '=');
-       if (!val) {
-               kfree(key);
-               return -EINVAL;
-       }
-       *val++ = 0;
-       val = skip_spaces(val);
-       remove_trail_spaces(key);
-       remove_trail_spaces(val);
-       mutex_lock(&codec->user_mutex);
-       hint = get_hint(codec, key);
-       if (hint) {
-               /* replace */
-               kfree(hint->key);
-               hint->key = key;
-               hint->val = val;
-               goto unlock;
-       }
-       /* allocate a new hint entry */
-       if (codec->hints.used >= MAX_HINTS)
-               hint = NULL;
-       else
-               hint = snd_array_new(&codec->hints);
-       if (hint) {
-               hint->key = key;
-               hint->val = val;
-       } else {
-               err = -ENOMEM;
-       }
- unlock:
-       mutex_unlock(&codec->user_mutex);
-       if (err)
-               kfree(key);
-       return err;
-}
-
-static ssize_t hints_store(struct device *dev,
-                          struct device_attribute *attr,
-                          const char *buf, size_t count)
-{
-       struct hda_codec *codec = dev_get_drvdata(dev);
-       int err = parse_hints(codec, buf);
-       if (err < 0)
-               return err;
-       return count;
-}
-
-static ssize_t user_pin_configs_show(struct device *dev,
-                                    struct device_attribute *attr,
-                                    char *buf)
-{
-       struct hda_codec *codec = dev_get_drvdata(dev);
-       return pin_configs_show(codec, &codec->user_pins, buf);
-}
-
-static int parse_user_pin_configs(struct hda_codec *codec, const char *buf)
-{
-       int nid, cfg, err;
-
-       if (sscanf(buf, "%i %i", &nid, &cfg) != 2)
-               return -EINVAL;
-       if (!nid)
-               return -EINVAL;
-       mutex_lock(&codec->user_mutex);
-       err = snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg);
-       mutex_unlock(&codec->user_mutex);
-       return err;
-}
-
-static ssize_t user_pin_configs_store(struct device *dev,
-                                     struct device_attribute *attr,
-                                     const char *buf, size_t count)
-{
-       struct hda_codec *codec = dev_get_drvdata(dev);
-       int err = parse_user_pin_configs(codec, buf);
-       if (err < 0)
-               return err;
-       return count;
-}
-
-/* sysfs attributes exposed only when CONFIG_SND_HDA_RECONFIG=y */
-static DEVICE_ATTR_RW(init_verbs);
-static DEVICE_ATTR_RW(hints);
-static DEVICE_ATTR_RW(user_pin_configs);
-static DEVICE_ATTR_WO(reconfig);
-static DEVICE_ATTR_WO(clear);
-
-/**
- * snd_hda_get_hint - Look for hint string
- * @codec: the HDA codec
- * @key: the hint key string
- *
- * Look for a hint key/value pair matching with the given key string
- * and returns the value string.  If nothing found, returns NULL.
- */
-const char *snd_hda_get_hint(struct hda_codec *codec, const char *key)
-{
-       struct hda_hint *hint = get_hint(codec, key);
-       return hint ? hint->val : NULL;
-}
-EXPORT_SYMBOL_GPL(snd_hda_get_hint);
-
-/**
- * snd_hda_get_bool_hint - Get a boolean hint value
- * @codec: the HDA codec
- * @key: the hint key string
- *
- * Look for a hint key/value pair matching with the given key string
- * and returns a boolean value parsed from the value.  If no matching
- * key is found, return a negative value.
- */
-int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
-{
-       const char *p;
-       int ret;
-
-       mutex_lock(&codec->user_mutex);
-       p = snd_hda_get_hint(codec, key);
-       if (!p || !*p)
-               ret = -ENOENT;
-       else {
-               switch (toupper(*p)) {
-               case 'T': /* true */
-               case 'Y': /* yes */
-               case '1':
-                       ret = 1;
-                       break;
-               default:
-                       ret = 0;
-                       break;
-               }
-       }
-       mutex_unlock(&codec->user_mutex);
-       return ret;
-}
-EXPORT_SYMBOL_GPL(snd_hda_get_bool_hint);
-
-/**
- * snd_hda_get_int_hint - Get an integer hint value
- * @codec: the HDA codec
- * @key: the hint key string
- * @valp: pointer to store a value
- *
- * Look for a hint key/value pair matching with the given key string
- * and stores the integer value to @valp.  If no matching key is found,
- * return a negative error code.  Otherwise it returns zero.
- */
-int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp)
-{
-       const char *p;
-       unsigned long val;
-       int ret;
-
-       mutex_lock(&codec->user_mutex);
-       p = snd_hda_get_hint(codec, key);
-       if (!p)
-               ret = -ENOENT;
-       else if (kstrtoul(p, 0, &val))
-               ret = -EINVAL;
-       else {
-               *valp = val;
-               ret = 0;
-       }
-       mutex_unlock(&codec->user_mutex);
-       return ret;
-}
-EXPORT_SYMBOL_GPL(snd_hda_get_int_hint);
-#endif /* CONFIG_SND_HDA_RECONFIG */
-
-/*
- * common sysfs attributes
- */
-#ifdef CONFIG_SND_HDA_RECONFIG
-#define RECONFIG_DEVICE_ATTR(name)     DEVICE_ATTR_RW(name)
-#else
-#define RECONFIG_DEVICE_ATTR(name)     DEVICE_ATTR_RO(name)
-#endif
-static RECONFIG_DEVICE_ATTR(vendor_id);
-static RECONFIG_DEVICE_ATTR(subsystem_id);
-static RECONFIG_DEVICE_ATTR(revision_id);
-static DEVICE_ATTR_RO(afg);
-static DEVICE_ATTR_RO(mfg);
-static RECONFIG_DEVICE_ATTR(vendor_name);
-static RECONFIG_DEVICE_ATTR(chip_name);
-static RECONFIG_DEVICE_ATTR(modelname);
-static DEVICE_ATTR_RO(init_pin_configs);
-static DEVICE_ATTR_RO(driver_pin_configs);
-
-
-#ifdef CONFIG_SND_HDA_PATCH_LOADER
-
-/* parser mode */
-enum {
-       LINE_MODE_NONE,
-       LINE_MODE_CODEC,
-       LINE_MODE_MODEL,
-       LINE_MODE_PINCFG,
-       LINE_MODE_VERB,
-       LINE_MODE_HINT,
-       LINE_MODE_VENDOR_ID,
-       LINE_MODE_SUBSYSTEM_ID,
-       LINE_MODE_REVISION_ID,
-       LINE_MODE_CHIP_NAME,
-       NUM_LINE_MODES,
-};
-
-static inline int strmatch(const char *a, const char *b)
-{
-       return strncasecmp(a, b, strlen(b)) == 0;
-}
-
-/* parse the contents after the line "[codec]"
- * accept only the line with three numbers, and assign the current codec
- */
-static void parse_codec_mode(char *buf, struct hda_bus *bus,
-                            struct hda_codec **codecp)
-{
-       int vendorid, subid, caddr;
-       struct hda_codec *codec;
-
-       *codecp = NULL;
-       if (sscanf(buf, "%i %i %i", &vendorid, &subid, &caddr) == 3) {
-               list_for_each_codec(codec, bus) {
-                       if ((vendorid <= 0 || codec->core.vendor_id == vendorid) &&
-                           (subid <= 0 || codec->core.subsystem_id == subid) &&
-                           codec->core.addr == caddr) {
-                               *codecp = codec;
-                               break;
-                       }
-               }
-       }
-}
-
-/* parse the contents after the other command tags, [pincfg], [verb],
- * [vendor_id], [subsystem_id], [revision_id], [chip_name], [hint] and [model]
- * just pass to the sysfs helper (only when any codec was specified)
- */
-static void parse_pincfg_mode(char *buf, struct hda_bus *bus,
-                             struct hda_codec **codecp)
-{
-       parse_user_pin_configs(*codecp, buf);
-}
-
-static void parse_verb_mode(char *buf, struct hda_bus *bus,
-                           struct hda_codec **codecp)
-{
-       parse_init_verbs(*codecp, buf);
-}
-
-static void parse_hint_mode(char *buf, struct hda_bus *bus,
-                           struct hda_codec **codecp)
-{
-       parse_hints(*codecp, buf);
-}
-
-static void parse_model_mode(char *buf, struct hda_bus *bus,
-                            struct hda_codec **codecp)
-{
-       kfree((*codecp)->modelname);
-       (*codecp)->modelname = kstrdup(buf, GFP_KERNEL);
-}
-
-static void parse_chip_name_mode(char *buf, struct hda_bus *bus,
-                                struct hda_codec **codecp)
-{
-       snd_hda_codec_set_name(*codecp, buf);
-}
-
-#define DEFINE_PARSE_ID_MODE(name) \
-static void parse_##name##_mode(char *buf, struct hda_bus *bus, \
-                                struct hda_codec **codecp) \
-{ \
-       unsigned long val; \
-       if (!kstrtoul(buf, 0, &val)) \
-               (*codecp)->core.name = val; \
-}
-
-DEFINE_PARSE_ID_MODE(vendor_id);
-DEFINE_PARSE_ID_MODE(subsystem_id);
-DEFINE_PARSE_ID_MODE(revision_id);
-
-
-struct hda_patch_item {
-       const char *tag;
-       const char *alias;
-       void (*parser)(char *buf, struct hda_bus *bus, struct hda_codec **retc);
-};
-
-static const struct hda_patch_item patch_items[NUM_LINE_MODES] = {
-       [LINE_MODE_CODEC] = {
-               .tag = "[codec]",
-               .parser = parse_codec_mode,
-       },
-       [LINE_MODE_MODEL] = {
-               .tag = "[model]",
-               .parser = parse_model_mode,
-       },
-       [LINE_MODE_VERB] = {
-               .tag = "[verb]",
-               .alias = "[init_verbs]",
-               .parser = parse_verb_mode,
-       },
-       [LINE_MODE_PINCFG] = {
-               .tag = "[pincfg]",
-               .alias = "[user_pin_configs]",
-               .parser = parse_pincfg_mode,
-       },
-       [LINE_MODE_HINT] = {
-               .tag = "[hint]",
-               .alias = "[hints]",
-               .parser = parse_hint_mode
-       },
-       [LINE_MODE_VENDOR_ID] = {
-               .tag = "[vendor_id]",
-               .parser = parse_vendor_id_mode,
-       },
-       [LINE_MODE_SUBSYSTEM_ID] = {
-               .tag = "[subsystem_id]",
-               .parser = parse_subsystem_id_mode,
-       },
-       [LINE_MODE_REVISION_ID] = {
-               .tag = "[revision_id]",
-               .parser = parse_revision_id_mode,
-       },
-       [LINE_MODE_CHIP_NAME] = {
-               .tag = "[chip_name]",
-               .parser = parse_chip_name_mode,
-       },
-};
-
-/* check the line starting with '[' -- change the parser mode accordingly */
-static int parse_line_mode(char *buf, struct hda_bus *bus)
-{
-       int i;
-       for (i = 0; i < ARRAY_SIZE(patch_items); i++) {
-               if (!patch_items[i].tag)
-                       continue;
-               if (strmatch(buf, patch_items[i].tag))
-                       return i;
-               if (patch_items[i].alias && strmatch(buf, patch_items[i].alias))
-                       return i;
-       }
-       return LINE_MODE_NONE;
-}
-
-/* copy one line from the buffer in fw, and update the fields in fw
- * return zero if it reaches to the end of the buffer, or non-zero
- * if successfully copied a line
- *
- * the spaces at the beginning and the end of the line are stripped
- */
-static int get_line_from_fw(char *buf, int size, size_t *fw_size_p,
-                           const void **fw_data_p)
-{
-       int len;
-       size_t fw_size = *fw_size_p;
-       const char *p = *fw_data_p;
-
-       while (isspace(*p) && fw_size) {
-               p++;
-               fw_size--;
-       }
-       if (!fw_size)
-               return 0;
-
-       for (len = 0; len < fw_size; len++) {
-               if (!*p)
-                       break;
-               if (*p == '\n') {
-                       p++;
-                       len++;
-                       break;
-               }
-               if (len < size)
-                       *buf++ = *p++;
-       }
-       *buf = 0;
-       *fw_size_p = fw_size - len;
-       *fw_data_p = p;
-       remove_trail_spaces(buf);
-       return 1;
-}
-
-/**
- * snd_hda_load_patch - load a "patch" firmware file and parse it
- * @bus: HD-audio bus
- * @fw_size: the firmware byte size
- * @fw_buf: the firmware data
- */
-int snd_hda_load_patch(struct hda_bus *bus, size_t fw_size, const void *fw_buf)
-{
-       char buf[128];
-       struct hda_codec *codec;
-       int line_mode;
-
-       line_mode = LINE_MODE_NONE;
-       codec = NULL;
-       while (get_line_from_fw(buf, sizeof(buf) - 1, &fw_size, &fw_buf)) {
-               if (!*buf || *buf == '#' || *buf == '\n')
-                       continue;
-               if (*buf == '[')
-                       line_mode = parse_line_mode(buf, bus);
-               else if (patch_items[line_mode].parser &&
-                        (codec || line_mode <= LINE_MODE_CODEC))
-                       patch_items[line_mode].parser(buf, bus, &codec);
-       }
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_load_patch);
-#endif /* CONFIG_SND_HDA_PATCH_LOADER */
-
-/*
- * sysfs entries
- */
-static struct attribute *hda_dev_attrs[] = {
-       &dev_attr_vendor_id.attr,
-       &dev_attr_subsystem_id.attr,
-       &dev_attr_revision_id.attr,
-       &dev_attr_afg.attr,
-       &dev_attr_mfg.attr,
-       &dev_attr_vendor_name.attr,
-       &dev_attr_chip_name.attr,
-       &dev_attr_modelname.attr,
-       &dev_attr_init_pin_configs.attr,
-       &dev_attr_driver_pin_configs.attr,
-       &dev_attr_power_on_acct.attr,
-       &dev_attr_power_off_acct.attr,
-#ifdef CONFIG_SND_HDA_RECONFIG
-       &dev_attr_init_verbs.attr,
-       &dev_attr_hints.attr,
-       &dev_attr_user_pin_configs.attr,
-       &dev_attr_reconfig.attr,
-       &dev_attr_clear.attr,
-#endif
-       NULL
-};
-
-static const struct attribute_group hda_dev_attr_group = {
-       .attrs  = hda_dev_attrs,
-};
-
-const struct attribute_group *snd_hda_dev_attr_groups[] = {
-       &hda_dev_attr_group,
-       NULL
-};
-
-void snd_hda_sysfs_init(struct hda_codec *codec)
-{
-       mutex_init(&codec->user_mutex);
-#ifdef CONFIG_SND_HDA_RECONFIG
-       snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32);
-       snd_array_init(&codec->hints, sizeof(struct hda_hint), 32);
-       snd_array_init(&codec->user_pins, sizeof(struct hda_pincfg), 16);
-#endif
-}
-
-void snd_hda_sysfs_clear(struct hda_codec *codec)
-{
-#ifdef CONFIG_SND_HDA_RECONFIG
-       struct hda_hint *hint;
-       int i;
-
-       /* clear init verbs */
-       snd_array_free(&codec->init_verbs);
-       /* clear hints */
-       snd_array_for_each(&codec->hints, i, hint) {
-               kfree(hint->key); /* we don't need to free hint->val */
-       }
-       snd_array_free(&codec->hints);
-       snd_array_free(&codec->user_pins);
-#endif
-}