summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Iwai2015-04-04 12:15:32 +0200
committerTakashi Iwai2015-04-04 12:16:30 +0200
commit664bc5c55923712a8aabd2a390ed7477325e32df (patch)
tree6382fd31d0fab7699fb2ff6b0501275700dd5b85
parentALSA: hda - Fix subsystem ID read regression (diff)
parentALSA: hda - Set use_single_rw flag for regmap (diff)
downloadkernel-qcow2-linux-664bc5c55923712a8aabd2a390ed7477325e32df.tar.gz
kernel-qcow2-linux-664bc5c55923712a8aabd2a390ed7477325e32df.tar.xz
kernel-qcow2-linux-664bc5c55923712a8aabd2a390ed7477325e32df.zip
Merge branch 'topic/hda-regmap' into for-next
This merges the support of regmap in HD-audio infrastructure. Many in-house cache codes in HD-audio driver are relaced with the more standard regmap base now. Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r--include/sound/hda_regmap.h205
-rw-r--r--include/sound/hdaudio.h64
-rw-r--r--sound/hda/Kconfig1
-rw-r--r--sound/hda/Makefile3
-rw-r--r--sound/hda/array.c49
-rw-r--r--sound/hda/hdac_device.c56
-rw-r--r--sound/hda/hdac_regmap.c472
-rw-r--r--sound/pci/hda/hda_bind.c4
-rw-r--r--sound/pci/hda/hda_codec.c675
-rw-r--r--sound/pci/hda/hda_codec.h74
-rw-r--r--sound/pci/hda/hda_generic.c14
-rw-r--r--sound/pci/hda/hda_local.h48
-rw-r--r--sound/pci/hda/hda_proc.c28
-rw-r--r--sound/pci/hda/patch_analog.c16
-rw-r--r--sound/pci/hda/patch_conexant.c5
-rw-r--r--sound/pci/hda/patch_hdmi.c4
-rw-r--r--sound/pci/hda/patch_realtek.c6
-rw-r--r--sound/pci/hda/patch_si3054.c4
-rw-r--r--sound/pci/hda/patch_sigmatel.c25
19 files changed, 1006 insertions, 747 deletions
diff --git a/include/sound/hda_regmap.h b/include/sound/hda_regmap.h
new file mode 100644
index 000000000000..76648ccfbbf8
--- /dev/null
+++ b/include/sound/hda_regmap.h
@@ -0,0 +1,205 @@
+/*
+ * HD-audio regmap helpers
+ */
+
+#ifndef __SOUND_HDA_REGMAP_H
+#define __SOUND_HDA_REGMAP_H
+
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/hdaudio.h>
+
+int snd_hdac_regmap_init(struct hdac_device *codec);
+void snd_hdac_regmap_exit(struct hdac_device *codec);
+int snd_hdac_regmap_add_vendor_verb(struct hdac_device *codec,
+ unsigned int verb);
+int snd_hdac_regmap_read_raw(struct hdac_device *codec, unsigned int reg,
+ unsigned int *val);
+int snd_hdac_regmap_write_raw(struct hdac_device *codec, unsigned int reg,
+ unsigned int val);
+int snd_hdac_regmap_update_raw(struct hdac_device *codec, unsigned int reg,
+ unsigned int mask, unsigned int val);
+
+/**
+ * snd_hdac_regmap_encode_verb - encode the verb to a pseudo register
+ * @nid: widget NID
+ * @verb: codec verb
+ *
+ * Returns an encoded pseudo register.
+ */
+#define snd_hdac_regmap_encode_verb(nid, verb) \
+ (((verb) << 8) | 0x80000 | ((unsigned int)(nid) << 20))
+
+/**
+ * snd_hdac_regmap_encode_amp - encode the AMP verb to a pseudo register
+ * @nid: widget NID
+ * @ch: channel (left = 0, right = 1)
+ * @dir: direction (#HDA_INPUT, #HDA_OUTPUT)
+ * @idx: input index value
+ *
+ * Returns an encoded pseudo register.
+ */
+#define snd_hdac_regmap_encode_amp(nid, ch, dir, idx) \
+ (snd_hdac_regmap_encode_verb(nid, AC_VERB_GET_AMP_GAIN_MUTE) | \
+ ((ch) ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT) | \
+ ((dir) == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT) | \
+ (idx))
+
+/**
+ * snd_hdac_regmap_encode_amp_stereo - encode a pseudo register for stereo AMPs
+ * @nid: widget NID
+ * @dir: direction (#HDA_INPUT, #HDA_OUTPUT)
+ * @idx: input index value
+ *
+ * Returns an encoded pseudo register.
+ */
+#define snd_hdac_regmap_encode_amp_stereo(nid, dir, idx) \
+ (snd_hdac_regmap_encode_verb(nid, AC_VERB_GET_AMP_GAIN_MUTE) | \
+ AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT | /* both bits set! */ \
+ ((dir) == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT) | \
+ (idx))
+
+/**
+ * snd_hdac_regmap_write - Write a verb with caching
+ * @nid: codec NID
+ * @reg: verb to write
+ * @val: value to write
+ *
+ * For writing an amp value, use snd_hda_regmap_amp_update().
+ */
+static inline int
+snd_hdac_regmap_write(struct hdac_device *codec, hda_nid_t nid,
+ unsigned int verb, unsigned int val)
+{
+ unsigned int cmd = snd_hdac_regmap_encode_verb(nid, verb);
+
+ return snd_hdac_regmap_write_raw(codec, cmd, val);
+}
+
+/**
+ * snd_hda_regmap_update - Update a verb value with caching
+ * @nid: codec NID
+ * @verb: verb to update
+ * @mask: bit mask to update
+ * @val: value to update
+ *
+ * For updating an amp value, use snd_hda_regmap_amp_update().
+ */
+static inline int
+snd_hdac_regmap_update(struct hdac_device *codec, hda_nid_t nid,
+ unsigned int verb, unsigned int mask,
+ unsigned int val)
+{
+ unsigned int cmd = snd_hdac_regmap_encode_verb(nid, verb);
+
+ return snd_hdac_regmap_update_raw(codec, cmd, mask, val);
+}
+
+/**
+ * snd_hda_regmap_read - Read a verb with caching
+ * @nid: codec NID
+ * @verb: verb to read
+ * @val: pointer to store the value
+ *
+ * For reading an amp value, use snd_hda_regmap_get_amp().
+ */
+static inline int
+snd_hdac_regmap_read(struct hdac_device *codec, hda_nid_t nid,
+ unsigned int verb, unsigned int *val)
+{
+ unsigned int cmd = snd_hdac_regmap_encode_verb(nid, verb);
+
+ return snd_hdac_regmap_read_raw(codec, cmd, val);
+}
+
+/**
+ * snd_hdac_regmap_get_amp - Read AMP value
+ * @codec: HD-audio codec
+ * @nid: NID to read the AMP value
+ * @ch: channel (left=0 or right=1)
+ * @direction: #HDA_INPUT or #HDA_OUTPUT
+ * @index: the index value (only for input direction)
+ * @val: the pointer to store the value
+ *
+ * Read AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit.
+ * Returns the value or a negative error.
+ */
+static inline int
+snd_hdac_regmap_get_amp(struct hdac_device *codec, hda_nid_t nid,
+ int ch, int dir, int idx)
+{
+ unsigned int cmd = snd_hdac_regmap_encode_amp(nid, ch, dir, idx);
+ int err, val;
+
+ err = snd_hdac_regmap_read_raw(codec, cmd, &val);
+ return err < 0 ? err : val;
+}
+
+/**
+ * snd_hdac_regmap_update_amp - update the AMP value
+ * @codec: HD-audio codec
+ * @nid: NID to read the AMP value
+ * @ch: channel (left=0 or right=1)
+ * @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 value with a bit mask.
+ * Returns 0 if the value is unchanged, 1 if changed, or a negative error.
+ */
+static inline int
+snd_hdac_regmap_update_amp(struct hdac_device *codec, hda_nid_t nid,
+ int ch, int dir, int idx, int mask, int val)
+{
+ unsigned int cmd = snd_hdac_regmap_encode_amp(nid, ch, dir, idx);
+
+ return snd_hdac_regmap_update_raw(codec, cmd, mask, val);
+}
+
+/**
+ * snd_hdac_regmap_get_amp_stereo - Read stereo AMP values
+ * @codec: HD-audio codec
+ * @nid: NID to read the AMP value
+ * @ch: channel (left=0 or right=1)
+ * @direction: #HDA_INPUT or #HDA_OUTPUT
+ * @index: the index value (only for input direction)
+ * @val: the pointer to store the value
+ *
+ * Read stereo AMP values. The lower byte is left, the upper byte is right.
+ * Returns the value or a negative error.
+ */
+static inline int
+snd_hdac_regmap_get_amp_stereo(struct hdac_device *codec, hda_nid_t nid,
+ int dir, int idx)
+{
+ unsigned int cmd = snd_hdac_regmap_encode_amp_stereo(nid, dir, idx);
+ int err, val;
+
+ err = snd_hdac_regmap_read_raw(codec, cmd, &val);
+ return err < 0 ? err : val;
+}
+
+/**
+ * snd_hdac_regmap_update_amp_stereo - update the stereo AMP value
+ * @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 stereo AMP value with a bit mask.
+ * The lower byte is left, the upper byte is right.
+ * Returns 0 if the value is unchanged, 1 if changed, or a negative error.
+ */
+static inline int
+snd_hdac_regmap_update_amp_stereo(struct hdac_device *codec, hda_nid_t nid,
+ int dir, int idx, int mask, int val)
+{
+ unsigned int cmd = snd_hdac_regmap_encode_amp_stereo(nid, dir, idx);
+
+ return snd_hdac_regmap_update_raw(codec, cmd, mask, val);
+}
+
+#endif /* __SOUND_HDA_REGMAP_H */
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h
index 675614dc2b88..95acc337aea5 100644
--- a/include/sound/hdaudio.h
+++ b/include/sound/hdaudio.h
@@ -22,6 +22,17 @@ struct hdac_widget_tree;
extern struct bus_type snd_hda_bus_type;
/*
+ * generic arrays
+ */
+struct snd_array {
+ unsigned int used;
+ unsigned int alloced;
+ unsigned int elem_size;
+ unsigned int alloc_align;
+ void *list;
+};
+
+/*
* HD-audio codec base device
*/
struct hdac_device {
@@ -61,6 +72,13 @@ struct hdac_device {
/* sysfs */
struct hdac_widget_tree *widgets;
+
+ /* regmap */
+ struct regmap *regmap;
+ struct snd_array vendor_verbs;
+ bool lazy_cache:1; /* don't wake up for writes */
+ bool caps_overwriting:1; /* caps overwrite being in process */
+ bool cache_coef:1; /* cache COEF read/write too */
};
/* device/driver type used for matching */
@@ -90,12 +108,34 @@ int snd_hdac_exec_verb(struct hdac_device *codec, unsigned int cmd,
unsigned int flags, unsigned int *res);
int snd_hdac_read(struct hdac_device *codec, hda_nid_t nid,
unsigned int verb, unsigned int parm, unsigned int *res);
-int snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm);
+int _snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm,
+ unsigned int *res);
+int snd_hdac_read_parm_uncached(struct hdac_device *codec, hda_nid_t nid,
+ int parm);
+int snd_hdac_override_parm(struct hdac_device *codec, hda_nid_t nid,
+ unsigned int parm, unsigned int val);
int snd_hdac_get_connections(struct hdac_device *codec, hda_nid_t nid,
hda_nid_t *conn_list, int max_conns);
int snd_hdac_get_sub_nodes(struct hdac_device *codec, hda_nid_t nid,
hda_nid_t *start_id);
+/**
+ * snd_hdac_read_parm - read a codec parameter
+ * @codec: the codec object
+ * @nid: NID to read a parameter
+ * @parm: parameter to read
+ *
+ * Returns -1 for error. If you need to distinguish the error more
+ * strictly, use _snd_hdac_read_parm() directly.
+ */
+static inline int snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid,
+ int parm)
+{
+ unsigned int val;
+
+ return _snd_hdac_read_parm(codec, nid, parm, &val) < 0 ? -1 : val;
+}
+
#ifdef CONFIG_PM
void snd_hdac_power_up(struct hdac_device *codec);
void snd_hdac_power_down(struct hdac_device *codec);
@@ -178,4 +218,26 @@ static inline void snd_hdac_codec_link_down(struct hdac_device *codec)
clear_bit(codec->addr, &codec->bus->codec_powered);
}
+/*
+ * generic array helpers
+ */
+void *snd_array_new(struct snd_array *array);
+void snd_array_free(struct snd_array *array);
+static inline void snd_array_init(struct snd_array *array, unsigned int size,
+ unsigned int align)
+{
+ array->elem_size = size;
+ array->alloc_align = align;
+}
+
+static inline void *snd_array_elem(struct snd_array *array, unsigned int idx)
+{
+ return array->list + idx * array->elem_size;
+}
+
+static inline unsigned int snd_array_index(struct snd_array *array, void *ptr)
+{
+ return (unsigned long)(ptr - array->list) / array->elem_size;
+}
+
#endif /* __SOUND_HDAUDIO_H */
diff --git a/sound/hda/Kconfig b/sound/hda/Kconfig
index 4f428ccf64ad..001c6588a5ff 100644
--- a/sound/hda/Kconfig
+++ b/sound/hda/Kconfig
@@ -1,2 +1,3 @@
config SND_HDA_CORE
tristate
+ select REGMAP
diff --git a/sound/hda/Makefile b/sound/hda/Makefile
index eec5da03b41f..7a359f5b7e25 100644
--- a/sound/hda/Makefile
+++ b/sound/hda/Makefile
@@ -1,4 +1,5 @@
-snd-hda-core-objs := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o
+snd-hda-core-objs := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o \
+ hdac_regmap.o array.o
snd-hda-core-objs += trace.o
CFLAGS_trace.o := -I$(src)
diff --git a/sound/hda/array.c b/sound/hda/array.c
new file mode 100644
index 000000000000..516795baa7db
--- /dev/null
+++ b/sound/hda/array.c
@@ -0,0 +1,49 @@
+/*
+ * generic arrays
+ */
+
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/hdaudio.h>
+
+/**
+ * snd_array_new - get a new element from the given array
+ * @array: the array object
+ *
+ * Get a new element from the given array. If it exceeds the
+ * pre-allocated array size, re-allocate the array.
+ *
+ * Returns NULL if allocation failed.
+ */
+void *snd_array_new(struct snd_array *array)
+{
+ if (snd_BUG_ON(!array->elem_size))
+ return NULL;
+ if (array->used >= array->alloced) {
+ int num = array->alloced + array->alloc_align;
+ int size = (num + 1) * array->elem_size;
+ void *nlist;
+ if (snd_BUG_ON(num >= 4096))
+ return NULL;
+ nlist = krealloc(array->list, size, GFP_KERNEL | __GFP_ZERO);
+ if (!nlist)
+ return NULL;
+ array->list = nlist;
+ array->alloced = num;
+ }
+ return snd_array_elem(array, array->used++);
+}
+EXPORT_SYMBOL_GPL(snd_array_new);
+
+/**
+ * snd_array_free - free the given array elements
+ * @array: the array object
+ */
+void snd_array_free(struct snd_array *array)
+{
+ kfree(array->list);
+ array->used = 0;
+ array->alloced = 0;
+ array->list = NULL;
+}
+EXPORT_SYMBOL_GPL(snd_array_free);
diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c
index 53b6b95ff8cd..d4a0e723af2c 100644
--- a/sound/hda/hdac_device.c
+++ b/sound/hda/hdac_device.c
@@ -9,6 +9,7 @@
#include <linux/export.h>
#include <linux/pm_runtime.h>
#include <sound/hdaudio.h>
+#include <sound/hda_regmap.h>
#include "local.h"
static void setup_fg_nodes(struct hdac_device *codec);
@@ -234,7 +235,22 @@ int snd_hdac_read(struct hdac_device *codec, hda_nid_t nid,
EXPORT_SYMBOL_GPL(snd_hdac_read);
/**
- * snd_hdac_read_parm - read a codec parameter
+ * _snd_hdac_read_parm - read a parmeter
+ *
+ * This function returns zero or an error unlike snd_hdac_read_parm().
+ */
+int _snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm,
+ unsigned int *res)
+{
+ unsigned int cmd;
+
+ cmd = snd_hdac_regmap_encode_verb(nid, AC_VERB_PARAMETERS) | parm;
+ return snd_hdac_regmap_read_raw(codec, cmd, res);
+}
+EXPORT_SYMBOL_GPL(_snd_hdac_read_parm);
+
+/**
+ * snd_hdac_read_parm_uncached - read a codec parameter without caching
* @codec: the codec object
* @nid: NID to read a parameter
* @parm: parameter to read
@@ -242,15 +258,42 @@ EXPORT_SYMBOL_GPL(snd_hdac_read);
* Returns -1 for error. If you need to distinguish the error more
* strictly, use snd_hdac_read() directly.
*/
-int snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm)
+int snd_hdac_read_parm_uncached(struct hdac_device *codec, hda_nid_t nid,
+ int parm)
{
int val;
- if (snd_hdac_read(codec, nid, AC_VERB_PARAMETERS, parm, &val))
- return -1;
+ if (codec->regmap)
+ regcache_cache_bypass(codec->regmap, true);
+ val = snd_hdac_read_parm(codec, nid, parm);
+ if (codec->regmap)
+ regcache_cache_bypass(codec->regmap, false);
return val;
}
-EXPORT_SYMBOL_GPL(snd_hdac_read_parm);
+EXPORT_SYMBOL_GPL(snd_hdac_read_parm_uncached);
+
+/**
+ * snd_hdac_override_parm - override read-only parameters
+ * @codec: the codec object
+ * @nid: NID for the parameter
+ * @parm: the parameter to change
+ * @val: the parameter value to overwrite
+ */
+int snd_hdac_override_parm(struct hdac_device *codec, hda_nid_t nid,
+ unsigned int parm, unsigned int val)
+{
+ unsigned int verb = (AC_VERB_PARAMETERS << 8) | (nid << 20) | parm;
+ int err;
+
+ if (!codec->regmap)
+ return -EINVAL;
+
+ codec->caps_overwriting = true;
+ err = snd_hdac_regmap_write_raw(codec, verb, val);
+ codec->caps_overwriting = false;
+ return err;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_override_parm);
/**
* snd_hdac_get_sub_nodes - get start NID and number of subtree nodes
@@ -259,13 +302,14 @@ EXPORT_SYMBOL_GPL(snd_hdac_read_parm);
* @start_id: the pointer to store the starting NID
*
* Returns the number of subtree nodes or zero if not found.
+ * This function reads parameters always without caching.
*/
int snd_hdac_get_sub_nodes(struct hdac_device *codec, hda_nid_t nid,
hda_nid_t *start_id)
{
unsigned int parm;
- parm = snd_hdac_read_parm(codec, nid, AC_PAR_NODE_COUNT);
+ parm = snd_hdac_read_parm_uncached(codec, nid, AC_PAR_NODE_COUNT);
if (parm == -1) {
*start_id = 0;
return 0;
diff --git a/sound/hda/hdac_regmap.c b/sound/hda/hdac_regmap.c
new file mode 100644
index 000000000000..1eb43209fe2c
--- /dev/null
+++ b/sound/hda/hdac_regmap.c
@@ -0,0 +1,472 @@
+/*
+ * Regmap support for HD-audio verbs
+ *
+ * A virtual register is translated to one or more hda verbs for write,
+ * vice versa for read.
+ *
+ * A few limitations:
+ * - Provided for not all verbs but only subset standard non-volatile verbs.
+ * - For reading, only AC_VERB_GET_* variants can be used.
+ * - For writing, mapped to the *corresponding* AC_VERB_SET_* variants,
+ * so can't handle asymmetric verbs for read and write
+ */
+
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <linux/export.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <sound/core.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_regmap.h>
+
+#ifdef CONFIG_PM
+#define codec_is_running(codec) \
+ (atomic_read(&(codec)->in_pm) || \
+ !pm_runtime_suspended(&(codec)->dev))
+#else
+#define codec_is_running(codec) true
+#endif
+
+#define get_verb(reg) (((reg) >> 8) & 0xfff)
+
+static bool hda_volatile_reg(struct device *dev, unsigned int reg)
+{
+ struct hdac_device *codec = dev_to_hdac_dev(dev);
+ unsigned int verb = get_verb(reg);
+
+ switch (verb) {
+ case AC_VERB_GET_PROC_COEF:
+ return !codec->cache_coef;
+ case AC_VERB_GET_COEF_INDEX:
+ case AC_VERB_GET_PROC_STATE:
+ case AC_VERB_GET_POWER_STATE:
+ case AC_VERB_GET_PIN_SENSE:
+ case AC_VERB_GET_HDMI_DIP_SIZE:
+ case AC_VERB_GET_HDMI_ELDD:
+ case AC_VERB_GET_HDMI_DIP_INDEX:
+ case AC_VERB_GET_HDMI_DIP_DATA:
+ case AC_VERB_GET_HDMI_DIP_XMIT:
+ case AC_VERB_GET_HDMI_CP_CTRL:
+ case AC_VERB_GET_HDMI_CHAN_SLOT:
+ case AC_VERB_GET_DEVICE_SEL:
+ case AC_VERB_GET_DEVICE_LIST: /* read-only volatile */
+ return true;
+ }
+
+ return false;
+}
+
+static bool hda_writeable_reg(struct device *dev, unsigned int reg)
+{
+ struct hdac_device *codec = dev_to_hdac_dev(dev);
+ unsigned int verb = get_verb(reg);
+ int i;
+
+ for (i = 0; i < codec->vendor_verbs.used; i++) {
+ unsigned int *v = snd_array_elem(&codec->vendor_verbs, i);
+ if (verb == *v)
+ return true;
+ }
+
+ if (codec->caps_overwriting)
+ return true;
+
+ switch (verb & 0xf00) {
+ case AC_VERB_GET_STREAM_FORMAT:
+ case AC_VERB_GET_AMP_GAIN_MUTE:
+ return true;
+ case AC_VERB_GET_PROC_COEF:
+ return codec->cache_coef;
+ case 0xf00:
+ break;
+ default:
+ return false;
+ }
+
+ switch (verb) {
+ case AC_VERB_GET_CONNECT_SEL:
+ case AC_VERB_GET_SDI_SELECT:
+ case AC_VERB_GET_PIN_WIDGET_CONTROL:
+ case AC_VERB_GET_UNSOLICITED_RESPONSE: /* only as SET_UNSOLICITED_ENABLE */
+ case AC_VERB_GET_BEEP_CONTROL:
+ case AC_VERB_GET_EAPD_BTLENABLE:
+ case AC_VERB_GET_DIGI_CONVERT_1:
+ case AC_VERB_GET_DIGI_CONVERT_2: /* only for beep control */
+ case AC_VERB_GET_VOLUME_KNOB_CONTROL:
+ case AC_VERB_GET_GPIO_MASK:
+ case AC_VERB_GET_GPIO_DIRECTION:
+ case AC_VERB_GET_GPIO_DATA: /* not for volatile read */
+ case AC_VERB_GET_GPIO_WAKE_MASK:
+ case AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK:
+ case AC_VERB_GET_GPIO_STICKY_MASK:
+ return true;
+ }
+
+ return false;
+}
+
+static bool hda_readable_reg(struct device *dev, unsigned int reg)
+{
+ struct hdac_device *codec = dev_to_hdac_dev(dev);
+ unsigned int verb = get_verb(reg);
+
+ if (codec->caps_overwriting)
+ return true;
+
+ switch (verb) {
+ case AC_VERB_PARAMETERS:
+ case AC_VERB_GET_CONNECT_LIST:
+ case AC_VERB_GET_SUBSYSTEM_ID:
+ return true;
+ /* below are basically writable, but disabled for reducing unnecessary
+ * writes at sync
+ */
+ case AC_VERB_GET_CONFIG_DEFAULT: /* usually just read */
+ case AC_VERB_GET_CONV: /* managed in PCM code */
+ case AC_VERB_GET_CVT_CHAN_COUNT: /* managed in HDMI CA code */
+ return true;
+ }
+
+ return hda_writeable_reg(dev, reg);
+}
+
+/*
+ * Stereo amp pseudo register:
+ * for making easier to handle the stereo volume control, we provide a
+ * fake register to deal both left and right channels by a single
+ * (pseudo) register access. A verb consisting of SET_AMP_GAIN with
+ * *both* SET_LEFT and SET_RIGHT bits takes a 16bit value, the lower 8bit
+ * for the left and the upper 8bit for the right channel.
+ */
+static bool is_stereo_amp_verb(unsigned int reg)
+{
+ if (((reg >> 8) & 0x700) != AC_VERB_SET_AMP_GAIN_MUTE)
+ return false;
+ return (reg & (AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT)) ==
+ (AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT);
+}
+
+/* read a pseudo stereo amp register (16bit left+right) */
+static int hda_reg_read_stereo_amp(struct hdac_device *codec,
+ unsigned int reg, unsigned int *val)
+{
+ unsigned int left, right;
+ int err;
+
+ reg &= ~(AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT);
+ err = snd_hdac_exec_verb(codec, reg | AC_AMP_GET_LEFT, 0, &left);
+ if (err < 0)
+ return err;
+ err = snd_hdac_exec_verb(codec, reg | AC_AMP_GET_RIGHT, 0, &right);
+ if (err < 0)
+ return err;
+ *val = left | (right << 8);
+ return 0;
+}
+
+/* write a pseudo stereo amp register (16bit left+right) */
+static int hda_reg_write_stereo_amp(struct hdac_device *codec,
+ unsigned int reg, unsigned int val)
+{
+ int err;
+ unsigned int verb, left, right;
+
+ verb = AC_VERB_SET_AMP_GAIN_MUTE << 8;
+ if (reg & AC_AMP_GET_OUTPUT)
+ verb |= AC_AMP_SET_OUTPUT;
+ else
+ verb |= AC_AMP_SET_INPUT | ((reg & 0xf) << 8);
+ reg = (reg & ~0xfffff) | verb;
+
+ left = val & 0xff;
+ right = (val >> 8) & 0xff;
+ if (left == right) {
+ reg |= AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT;
+ return snd_hdac_exec_verb(codec, reg | left, 0, NULL);
+ }
+
+ err = snd_hdac_exec_verb(codec, reg | AC_AMP_SET_LEFT | left, 0, NULL);
+ if (err < 0)
+ return err;
+ err = snd_hdac_exec_verb(codec, reg | AC_AMP_SET_RIGHT | right, 0, NULL);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+/* read a pseudo coef register (16bit) */
+static int hda_reg_read_coef(struct hdac_device *codec, unsigned int reg,
+ unsigned int *val)
+{
+ unsigned int verb;
+ int err;
+
+ if (!codec->cache_coef)
+ return -EINVAL;
+ /* LSB 8bit = coef index */
+ verb = (reg & ~0xfff00) | (AC_VERB_SET_COEF_INDEX << 8);
+ err = snd_hdac_exec_verb(codec, verb, 0, NULL);
+ if (err < 0)
+ return err;
+ verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8);
+ return snd_hdac_exec_verb(codec, verb, 0, val);
+}
+
+/* write a pseudo coef register (16bit) */
+static int hda_reg_write_coef(struct hdac_device *codec, unsigned int reg,
+ unsigned int val)
+{
+ unsigned int verb;
+ int err;
+
+ if (!codec->cache_coef)
+ return -EINVAL;
+ /* LSB 8bit = coef index */
+ verb = (reg & ~0xfff00) | (AC_VERB_SET_COEF_INDEX << 8);
+ err = snd_hdac_exec_verb(codec, verb, 0, NULL);
+ if (err < 0)
+ return err;
+ verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8) |
+ (val & 0xffff);
+ return snd_hdac_exec_verb(codec, verb, 0, NULL);
+}
+
+static int hda_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct hdac_device *codec = context;
+ int verb = get_verb(reg);
+ int err;
+
+ if (!codec_is_running(codec))
+ return -EAGAIN;
+ reg |= (codec->addr << 28);
+ if (is_stereo_amp_verb(reg))
+ return hda_reg_read_stereo_amp(codec, reg, val);
+ if (verb == AC_VERB_GET_PROC_COEF)
+ return hda_reg_read_coef(codec, reg, val);
+ err = snd_hdac_exec_verb(codec, reg, 0, val);
+ if (err < 0)
+ return err;
+ /* special handling for asymmetric reads */
+ if (verb == AC_VERB_GET_POWER_STATE) {
+ if (*val & AC_PWRST_ERROR)
+ *val = -1;
+ else /* take only the actual state */
+ *val = (*val >> 4) & 0x0f;
+ }
+ return 0;
+}
+
+static int hda_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct hdac_device *codec = context;
+ unsigned int verb;
+ int i, bytes, err;
+
+ if (!codec_is_running(codec))
+ return codec->lazy_cache ? 0 : -EAGAIN;
+
+ reg &= ~0x00080000U; /* drop GET bit */
+ reg |= (codec->addr << 28);
+
+ if (is_stereo_amp_verb(reg))
+ return hda_reg_write_stereo_amp(codec, reg, val);
+
+ verb = get_verb(reg);
+ if (verb == AC_VERB_SET_PROC_COEF)
+ return hda_reg_write_coef(codec, reg, val);
+
+ switch (verb & 0xf00) {
+ case AC_VERB_SET_AMP_GAIN_MUTE:
+ verb = AC_VERB_SET_AMP_GAIN_MUTE;
+ if (reg & AC_AMP_GET_LEFT)
+ verb |= AC_AMP_SET_LEFT >> 8;
+ else
+ verb |= AC_AMP_SET_RIGHT >> 8;
+ if (reg & AC_AMP_GET_OUTPUT) {
+ verb |= AC_AMP_SET_OUTPUT >> 8;
+ } else {
+ verb |= AC_AMP_SET_INPUT >> 8;
+ verb |= reg & 0xf;
+ }
+ break;
+ }
+
+ switch (verb) {
+ case AC_VERB_SET_DIGI_CONVERT_1:
+ bytes = 2;
+ break;
+ case AC_VERB_SET_CONFIG_DEFAULT_BYTES_0:
+ bytes = 4;
+ break;
+ default:
+ bytes = 1;
+ break;
+ }
+
+ for (i = 0; i < bytes; i++) {
+ reg &= ~0xfffff;
+ reg |= (verb + i) << 8 | ((val >> (8 * i)) & 0xff);
+ err = snd_hdac_exec_verb(codec, reg, 0, NULL);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct regmap_config hda_regmap_cfg = {
+ .name = "hdaudio",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .max_register = 0xfffffff,
+ .writeable_reg = hda_writeable_reg,
+ .readable_reg = hda_readable_reg,
+ .volatile_reg = hda_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+ .reg_read = hda_reg_read,
+ .reg_write = hda_reg_write,
+ .use_single_rw = true,
+};
+
+int snd_hdac_regmap_init(struct hdac_device *codec)
+{
+ struct regmap *regmap;
+
+ regmap = regmap_init(&codec->dev, NULL, codec, &hda_regmap_cfg);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+ codec->regmap = regmap;
+ snd_array_init(&codec->vendor_verbs, sizeof(unsigned int), 8);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_init);
+
+void snd_hdac_regmap_exit(struct hdac_device *codec)
+{
+ if (codec->regmap) {
+ regmap_exit(codec->regmap);
+ codec->regmap = NULL;
+ snd_array_free(&codec->vendor_verbs);
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_exit);
+
+/**
+ * snd_hdac_regmap_add_vendor_verb - add a vendor-specific verb to regmap
+ * @codec: the codec object
+ * @verb: verb to allow accessing via regmap
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_regmap_add_vendor_verb(struct hdac_device *codec,
+ unsigned int verb)
+{
+ unsigned int *p = snd_array_new(&codec->vendor_verbs);
+
+ if (!p)
+ return -ENOMEM;
+ *p = verb;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_add_vendor_verb);
+
+/*
+ * helper functions
+ */
+
+/* write a pseudo-register value (w/o power sequence) */
+static int reg_raw_write(struct hdac_device *codec, unsigned int reg,
+ unsigned int val)
+{
+ if (!codec->regmap)
+ return hda_reg_write(codec, reg, val);
+ else
+ return regmap_write(codec->regmap, reg, val);
+}
+
+/**
+ * snd_hdac_regmap_write_raw - write a pseudo register with power mgmt
+ * @codec: the codec object
+ * @reg: pseudo register
+ * @val: value to write
+ *
+ * Returns zero if successful or a negative error code.
+ */
+int snd_hdac_regmap_write_raw(struct hdac_device *codec, unsigned int reg,
+ unsigned int val)
+{
+ int err;
+
+ err = reg_raw_write(codec, reg, val);
+ if (err == -EAGAIN) {
+ snd_hdac_power_up(codec);
+ err = reg_raw_write(codec, reg, val);
+ snd_hdac_power_down(codec);
+ }
+ return err;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_write_raw);
+
+static int reg_raw_read(struct hdac_device *codec, unsigned int reg,
+ unsigned int *val)
+{
+ if (!codec->regmap)
+ return hda_reg_read(codec, reg, val);
+ else
+ return regmap_read(codec->regmap, reg, val);
+}
+
+/**
+ * snd_hdac_regmap_read_raw - read a pseudo register with power mgmt
+ * @codec: the codec object
+ * @reg: pseudo register
+ * @val: pointer to store the read value
+ *
+ * Returns zero if successful or a negative error code.
+ */
+int snd_hdac_regmap_read_raw(struct hdac_device *codec, unsigned int reg,
+ unsigned int *val)
+{
+ int err;
+
+ err = reg_raw_read(codec, reg, val);
+ if (err == -EAGAIN) {
+ snd_hdac_power_up(codec);
+ err = reg_raw_read(codec, reg, val);
+ snd_hdac_power_down(codec);
+ }
+ return err;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_read_raw);
+
+/**
+ * snd_hdac_regmap_update_raw - update a pseudo register with power mgmt
+ * @codec: the codec object
+ * @reg: pseudo register
+ * @mask: bit mask to udpate
+ * @val: value to update
+ *
+ * Returns zero if successful or a negative error code.
+ */
+int snd_hdac_regmap_update_raw(struct hdac_device *codec, unsigned int reg,
+ unsigned int mask, unsigned int val)
+{
+ unsigned int orig;
+ int err;
+
+ val &= mask;
+ err = snd_hdac_regmap_read_raw(codec, reg, &orig);
+ if (err < 0)
+ return err;
+ val |= orig & ~mask;
+ if (val == orig)
+ return 0;
+ err = snd_hdac_regmap_write_raw(codec, reg, val);
+ if (err < 0)
+ return err;
+ return 1;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw);
diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c
index 7b269c3237e3..00aa31c5f08e 100644
--- a/sound/pci/hda/hda_bind.c
+++ b/sound/pci/hda/hda_bind.c
@@ -75,6 +75,9 @@ static int hda_codec_driver_probe(struct device *dev)
err = codec_refresh_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;
@@ -98,6 +101,7 @@ static int hda_codec_driver_probe(struct device *dev)
snd_hda_codec_register(codec);
}
+ codec->core.lazy_cache = true;
return 0;
error_module:
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 145cae7903b6..41851f9b48c1 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -586,8 +586,8 @@ static int read_widget_caps(struct hda_codec *codec, hda_nid_t fg_node)
return -ENOMEM;
nid = codec->core.start_nid;
for (i = 0; i < codec->core.num_nodes; i++, nid++)
- codec->wcaps[i] = snd_hda_param_read(codec, nid,
- AC_PAR_AUDIO_WIDGET_CAP);
+ codec->wcaps[i] = snd_hdac_read_parm_uncached(&codec->core,
+ nid, AC_PAR_AUDIO_WIDGET_CAP);
return 0;
}
@@ -807,10 +807,6 @@ static void hda_jackpoll_work(struct work_struct *work)
codec->jackpoll_interval);
}
-static void init_hda_cache(struct hda_cache_rec *cache,
- unsigned int record_size);
-static void free_hda_cache(struct hda_cache_rec *cache);
-
/* release all pincfg lists */
static void free_init_pincfgs(struct hda_codec *codec)
{
@@ -929,11 +925,6 @@ void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec)
codec->proc_widget_hook = NULL;
codec->spec = NULL;
- free_hda_cache(&codec->amp_cache);
- free_hda_cache(&codec->cmd_cache);
- init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
- init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
-
/* free only driver_pins so that init_pins + user_pins are restored */
snd_array_free(&codec->driver_pins);
snd_array_free(&codec->cvt_setups);
@@ -945,6 +936,7 @@ void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec)
snd_array_free(&codec->mixers);
snd_array_free(&codec->nids);
remove_conn_list(codec);
+ snd_hdac_regmap_exit(&codec->core);
}
static unsigned int hda_set_power_state(struct hda_codec *codec,
@@ -995,8 +987,6 @@ static void snd_hda_codec_dev_release(struct device *dev)
free_init_pincfgs(codec);
snd_hdac_device_exit(&codec->core);
snd_hda_sysfs_clear(codec);
- free_hda_cache(&codec->amp_cache);
- free_hda_cache(&codec->cmd_cache);
kfree(codec->modelname);
kfree(codec->wcaps);
kfree(codec);
@@ -1049,9 +1039,6 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card,
codec->addr = codec_addr;
mutex_init(&codec->spdif_mutex);
mutex_init(&codec->control_mutex);
- mutex_init(&codec->hash_mutex);
- init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
- init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32);
snd_array_init(&codec->nids, sizeof(struct hda_nid_item), 32);
snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16);
@@ -1319,127 +1306,6 @@ static void hda_cleanup_all_streams(struct hda_codec *codec)
* amp access functions
*/
-/* FIXME: more better hash key? */
-#define HDA_HASH_KEY(nid, dir, idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24))
-#define HDA_HASH_PINCAP_KEY(nid) (u32)((nid) + (0x02 << 24))
-#define HDA_HASH_PARPCM_KEY(nid) (u32)((nid) + (0x03 << 24))
-#define HDA_HASH_PARSTR_KEY(nid) (u32)((nid) + (0x04 << 24))
-#define INFO_AMP_CAPS (1<<0)
-#define INFO_AMP_VOL(ch) (1 << (1 + (ch)))
-
-/* initialize the hash table */
-static void init_hda_cache(struct hda_cache_rec *cache,
- unsigned int record_size)
-{
- memset(cache, 0, sizeof(*cache));
- memset(cache->hash, 0xff, sizeof(cache->hash));
- snd_array_init(&cache->buf, record_size, 64);
-}
-
-static void free_hda_cache(struct hda_cache_rec *cache)
-{
- snd_array_free(&cache->buf);
-}
-
-/* query the hash. allocate an entry if not found. */
-static struct hda_cache_head *get_hash(struct hda_cache_rec *cache, u32 key)
-{
- u16 idx = key % (u16)ARRAY_SIZE(cache->hash);
- u16 cur = cache->hash[idx];
- struct hda_cache_head *info;
-
- while (cur != 0xffff) {
- info = snd_array_elem(&cache->buf, cur);
- if (info->key == key)
- return info;
- cur = info->next;
- }
- return NULL;
-}
-
-/* query the hash. allocate an entry if not found. */
-static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache,
- u32 key)
-{
- struct hda_cache_head *info = get_hash(cache, key);
- if (!info) {
- u16 idx, cur;
- /* add a new hash entry */
- info = snd_array_new(&cache->buf);
- if (!info)
- return NULL;
- cur = snd_array_index(&cache->buf, info);
- info->key = key;
- info->val = 0;
- info->dirty = 0;
- idx = key % (u16)ARRAY_SIZE(cache->hash);
- info->next = cache->hash[idx];
- cache->hash[idx] = cur;
- }
- return info;
-}
-
-/* query and allocate an amp hash entry */
-static inline struct hda_amp_info *
-get_alloc_amp_hash(struct hda_codec *codec, u32 key)
-{
- return (struct hda_amp_info *)get_alloc_hash(&codec->amp_cache, key);
-}
-
-/* overwrite the value with the key in the caps hash */
-static int write_caps_hash(struct hda_codec *codec, u32 key, unsigned int val)
-{
- struct hda_amp_info *info;
-
- mutex_lock(&codec->hash_mutex);
- info = get_alloc_amp_hash(codec, key);
- if (!info) {
- mutex_unlock(&codec->hash_mutex);
- return -EINVAL;
- }
- info->amp_caps = val;
- info->head.val |= INFO_AMP_CAPS;
- mutex_unlock(&codec->hash_mutex);
- return 0;
-}
-
-/* query the value from the caps hash; if not found, fetch the current
- * value from the given function and store in the hash
- */
-static unsigned int
-query_caps_hash(struct hda_codec *codec, hda_nid_t nid, int dir, u32 key,
- unsigned int (*func)(struct hda_codec *, hda_nid_t, int))
-{
- struct hda_amp_info *info;
- unsigned int val;
-
- mutex_lock(&codec->hash_mutex);
- info = get_alloc_amp_hash(codec, key);
- if (!info) {
- mutex_unlock(&codec->hash_mutex);
- return 0;
- }
- if (!(info->head.val & INFO_AMP_CAPS)) {
- mutex_unlock(&codec->hash_mutex); /* for reentrance */
- val = func(codec, nid, dir);
- write_caps_hash(codec, key, val);
- } else {
- val = info->amp_caps;
- mutex_unlock(&codec->hash_mutex);
- }
- return val;
-}
-
-static unsigned int read_amp_cap(struct hda_codec *codec, hda_nid_t nid,
- int direction)
-{
- if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD))
- nid = codec->core.afg;
- return snd_hda_param_read(codec, nid,
- direction == HDA_OUTPUT ?
- AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
-}
-
/**
* query_amp_caps - query AMP capabilities
* @codec: the HD-auio codec
@@ -1454,9 +1320,11 @@ static unsigned int read_amp_cap(struct hda_codec *codec, hda_nid_t nid,
*/
u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
{
- return query_caps_hash(codec, nid, direction,
- HDA_HASH_KEY(nid, direction, 0),
- read_amp_cap);
+ 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);
@@ -1497,183 +1365,14 @@ EXPORT_SYMBOL_GPL(snd_hda_check_amp_caps);
int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int caps)
{
- return write_caps_hash(codec, HDA_HASH_KEY(nid, dir, 0), caps);
-}
-EXPORT_SYMBOL_GPL(snd_hda_override_amp_caps);
-
-static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid,
- int dir)
-{
- return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
-}
-
-/**
- * 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.
- */
-u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
-{
- return query_caps_hash(codec, nid, 0, HDA_HASH_PINCAP_KEY(nid),
- read_pin_cap);
-}
-EXPORT_SYMBOL_GPL(snd_hda_query_pin_caps);
-
-/**
- * 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.
- */
-int snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid,
- unsigned int caps)
-{
- return write_caps_hash(codec, HDA_HASH_PINCAP_KEY(nid), caps);
-}
-EXPORT_SYMBOL_GPL(snd_hda_override_pin_caps);
-
-/* read or sync the hash value with the current value;
- * call within hash_mutex
- */
-static struct hda_amp_info *
-update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch,
- int direction, int index, bool init_only)
-{
- struct hda_amp_info *info;
- unsigned int parm, val = 0;
- bool val_read = false;
-
- retry:
- info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index));
- if (!info)
- return NULL;
- if (!(info->head.val & INFO_AMP_VOL(ch))) {
- if (!val_read) {
- mutex_unlock(&codec->hash_mutex);
- parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT;
- parm |= direction == HDA_OUTPUT ?
- AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
- parm |= index;
- val = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_AMP_GAIN_MUTE, parm);
- val &= 0xff;
- val_read = true;
- mutex_lock(&codec->hash_mutex);
- goto retry;
- }
- info->vol[ch] = val;
- info->head.val |= INFO_AMP_VOL(ch);
- } else if (init_only)
- return NULL;
- return info;
-}
-
-/*
- * write the current volume in info to the h/w
- */
-static void put_vol_mute(struct hda_codec *codec, unsigned int amp_caps,
- hda_nid_t nid, int ch, int direction, int index,
- int val)
-{
- u32 parm;
-
- parm = ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT;
- parm |= direction == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT;
- parm |= index << AC_AMP_SET_INDEX_SHIFT;
- if ((val & HDA_AMP_MUTE) && !(amp_caps & AC_AMPCAP_MUTE) &&
- (amp_caps & AC_AMPCAP_MIN_MUTE))
- ; /* set the zero value as a fake mute */
- else
- parm |= val;
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, parm);
-}
-
-/**
- * snd_hda_codec_amp_read - Read AMP value
- * @codec: HD-audio codec
- * @nid: NID to read the AMP value
- * @ch: channel (left=0 or right=1)
- * @direction: #HDA_INPUT or #HDA_OUTPUT
- * @index: the index value (only for input direction)
- *
- * Read AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit.
- */
-int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
- int direction, int index)
-{
- struct hda_amp_info *info;
- unsigned int val = 0;
-
- mutex_lock(&codec->hash_mutex);
- info = update_amp_hash(codec, nid, ch, direction, index, false);
- if (info)
- val = info->vol[ch];
- mutex_unlock(&codec->hash_mutex);
- return val;
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_amp_read);
-
-static int codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
- int direction, int idx, int mask, int val,
- bool init_only, bool cache_only)
-{
- struct hda_amp_info *info;
- unsigned int caps;
-
- if (snd_BUG_ON(mask & ~0xff))
- mask &= 0xff;
- val &= mask;
-
- mutex_lock(&codec->hash_mutex);
- info = update_amp_hash(codec, nid, ch, direction, idx, init_only);
- if (!info) {
- mutex_unlock(&codec->hash_mutex);
- return 0;
- }
- val |= info->vol[ch] & ~mask;
- if (info->vol[ch] == val) {
- mutex_unlock(&codec->hash_mutex);
- return 0;
- }
- info->vol[ch] = val;
- info->head.dirty |= cache_only;
- caps = info->amp_caps;
- mutex_unlock(&codec->hash_mutex);
- if (!cache_only)
- put_vol_mute(codec, caps, nid, ch, direction, idx, val);
- return 1;
-}
+ unsigned int parm;
-/**
- * snd_hda_codec_amp_update - update the AMP value
- * @codec: HD-audio codec
- * @nid: NID to read the AMP value
- * @ch: channel (left=0 or right=1)
- * @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 value with a bit mask.
- * Returns 0 if the value is unchanged, 1 if changed.
- */
-int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
- int direction, int idx, int mask, int val)
-{
- return codec_amp_update(codec, nid, ch, direction, idx, mask, val,
- false, codec->cached_write);
+ 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_codec_amp_update);
+EXPORT_SYMBOL_GPL(snd_hda_override_amp_caps);
/**
* snd_hda_codec_amp_stereo - update the AMP stereo values
@@ -1718,8 +1417,16 @@ EXPORT_SYMBOL_GPL(snd_hda_codec_amp_stereo);
int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch,
int dir, int idx, int mask, int val)
{
- return codec_amp_update(codec, nid, ch, dir, idx, mask, val, true,
- codec->cached_write);
+ int orig;
+
+ if (!codec->core.regmap)
+ return -EINVAL;
+ regcache_cache_only(codec->core.regmap, true);
+ orig = snd_hda_codec_amp_read(codec, nid, ch, dir, idx);
+ regcache_cache_only(codec->core.regmap, false);
+ if (orig >= 0)
+ return 0;
+ return snd_hda_codec_amp_update(codec, nid, ch, dir, idx, mask, val);
}
EXPORT_SYMBOL_GPL(snd_hda_codec_amp_init);
@@ -1748,49 +1455,6 @@ int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid,
}
EXPORT_SYMBOL_GPL(snd_hda_codec_amp_init_stereo);
-/**
- * snd_hda_codec_resume_amp - Resume all AMP commands from the cache
- * @codec: HD-audio codec
- *
- * Resume the all amp commands from the cache.
- */
-void snd_hda_codec_resume_amp(struct hda_codec *codec)
-{
- int i;
-
- mutex_lock(&codec->hash_mutex);
- codec->cached_write = 0;
- for (i = 0; i < codec->amp_cache.buf.used; i++) {
- struct hda_amp_info *buffer;
- u32 key;
- hda_nid_t nid;
- unsigned int idx, dir, ch;
- struct hda_amp_info info;
-
- buffer = snd_array_elem(&codec->amp_cache.buf, i);
- if (!buffer->head.dirty)
- continue;
- buffer->head.dirty = 0;
- info = *buffer;
- key = info.head.key;
- if (!key)
- continue;
- nid = key & 0xff;
- idx = (key >> 16) & 0xff;
- dir = (key >> 24) & 0xff;
- for (ch = 0; ch < 2; ch++) {
- if (!(info.head.val & INFO_AMP_VOL(ch)))
- continue;
- mutex_unlock(&codec->hash_mutex);
- put_vol_mute(codec, info.amp_caps, nid, ch, dir, idx,
- info.vol[ch]);
- mutex_lock(&codec->hash_mutex);
- }
- }
- mutex_unlock(&codec->hash_mutex);
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_resume_amp);
-
static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int ofs)
{
@@ -1861,8 +1525,8 @@ update_amp_value(struct hda_codec *codec, hda_nid_t nid,
maxval = get_amp_max_value(codec, nid, dir, 0);
if (val > maxval)
val = maxval;
- return codec_amp_update(codec, nid, ch, dir, idx, HDA_AMP_VOLMASK, val,
- false, !hda_codec_is_power_on(codec));
+ return snd_hda_codec_amp_update(codec, nid, ch, dir, idx,
+ HDA_AMP_VOLMASK, val);
}
/**
@@ -2545,17 +2209,15 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
int change = 0;
if (chs & 1) {
- change = codec_amp_update(codec, nid, 0, dir, idx,
- HDA_AMP_MUTE,
- *valp ? 0 : HDA_AMP_MUTE, false,
- !hda_codec_is_power_on(codec));
+ change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx,
+ HDA_AMP_MUTE,
+ *valp ? 0 : HDA_AMP_MUTE);
valp++;
}
if (chs & 2)
- change |= codec_amp_update(codec, nid, 1, dir, idx,
- HDA_AMP_MUTE,
- *valp ? 0 : HDA_AMP_MUTE, false,
- !hda_codec_is_power_on(codec));
+ 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;
}
@@ -2857,25 +2519,35 @@ static unsigned int convert_to_spdif_status(unsigned short val)
/* set digital convert verbs both for the given NID and its slaves */
static void set_dig_out(struct hda_codec *codec, hda_nid_t nid,
- int verb, int val)
+ int mask, int val)
{
const hda_nid_t *d;
- snd_hda_codec_write_cache(codec, nid, 0, verb, val);
+ snd_hdac_regmap_update(&codec->core, nid, AC_VERB_SET_DIGI_CONVERT_1,
+ mask, val);
d = codec->slave_dig_outs;
if (!d)
return;
for (; *d; d++)
- snd_hda_codec_write_cache(codec, *d, 0, verb, val);
+ snd_hdac_regmap_update(&codec->core, nid,
+ 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)
{
- if (dig1 != -1)
- set_dig_out(codec, nid, AC_VERB_SET_DIGI_CONVERT_1, dig1);
- if (dig2 != -1)
- set_dig_out(codec, nid, AC_VERB_SET_DIGI_CONVERT_2, 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,
@@ -3008,6 +2680,7 @@ int snd_hda_create_dig_out_ctls(struct hda_codec *codec,
struct snd_kcontrol *kctl;
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;
@@ -3048,8 +2721,9 @@ int snd_hda_create_dig_out_ctls(struct hda_codec *codec,
return err;
}
spdif->nid = cvt_nid;
- spdif->ctls = snd_hda_codec_read(codec, cvt_nid, 0,
- AC_VERB_GET_DIGI_CONVERT_1, 0);
+ 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;
}
@@ -3193,8 +2867,8 @@ static int snd_hda_spdif_in_switch_put(struct snd_kcontrol *kcontrol,
change = codec->spdif_in_enable != val;
if (change) {
codec->spdif_in_enable = val;
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_DIGI_CONVERT_1, val);
+ snd_hdac_regmap_write(&codec->core, nid,
+ AC_VERB_SET_DIGI_CONVERT_1, val);
}
mutex_unlock(&codec->spdif_mutex);
return change;
@@ -3205,10 +2879,11 @@ static int snd_hda_spdif_in_status_get(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = kcontrol->private_value;
- unsigned short val;
+ unsigned int val;
unsigned int sbits;
- val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT_1, 0);
+ 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;
@@ -3274,153 +2949,6 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
}
EXPORT_SYMBOL_GPL(snd_hda_create_spdif_in_ctls);
-/*
- * command cache
- */
-
-/* build a 31bit cache key with the widget id and the command parameter */
-#define build_cmd_cache_key(nid, verb) ((verb << 8) | nid)
-#define get_cmd_cache_nid(key) ((key) & 0xff)
-#define get_cmd_cache_cmd(key) (((key) >> 8) & 0xffff)
-
-/**
- * snd_hda_codec_write_cache - send a single command with caching
- * @codec: the HDA codec
- * @nid: NID to send the command
- * @flags: optional bit flags
- * @verb: the verb to send
- * @parm: the parameter for the verb
- *
- * Send a single command without waiting for response.
- *
- * Returns 0 if successful, or a negative error code.
- */
-int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
- int flags, unsigned int verb, unsigned int parm)
-{
- int err;
- struct hda_cache_head *c;
- u32 key;
- unsigned int cache_only;
-
- cache_only = codec->cached_write;
- if (!cache_only) {
- err = snd_hda_codec_write(codec, nid, flags, verb, parm);
- if (err < 0)
- return err;
- }
-
- /* parm may contain the verb stuff for get/set amp */
- verb = verb | (parm >> 8);
- parm &= 0xff;
- key = build_cmd_cache_key(nid, verb);
- mutex_lock(&codec->bus->core.cmd_mutex);
- c = get_alloc_hash(&codec->cmd_cache, key);
- if (c) {
- c->val = parm;
- c->dirty = cache_only;
- }
- mutex_unlock(&codec->bus->core.cmd_mutex);
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_write_cache);
-
-/**
- * snd_hda_codec_update_cache - check cache and write the cmd only when needed
- * @codec: the HDA codec
- * @nid: NID to send the command
- * @flags: optional bit flags
- * @verb: the verb to send
- * @parm: the parameter for the verb
- *
- * This function works like snd_hda_codec_write_cache(), but it doesn't send
- * command if the parameter is already identical with the cached value.
- * If not, it sends the command and refreshes the cache.
- *
- * Returns 0 if successful, or a negative error code.
- */
-int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid,
- int flags, unsigned int verb, unsigned int parm)
-{
- struct hda_cache_head *c;
- u32 key;
-
- /* parm may contain the verb stuff for get/set amp */
- verb = verb | (parm >> 8);
- parm &= 0xff;
- key = build_cmd_cache_key(nid, verb);
- mutex_lock(&codec->bus->core.cmd_mutex);
- c = get_hash(&codec->cmd_cache, key);
- if (c && c->val == parm) {
- mutex_unlock(&codec->bus->core.cmd_mutex);
- return 0;
- }
- mutex_unlock(&codec->bus->core.cmd_mutex);
- return snd_hda_codec_write_cache(codec, nid, flags, verb, parm);
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_update_cache);
-
-/**
- * snd_hda_codec_resume_cache - Resume the all commands from the cache
- * @codec: HD-audio codec
- *
- * Execute all verbs recorded in the command caches to resume.
- */
-void snd_hda_codec_resume_cache(struct hda_codec *codec)
-{
- int i;
-
- mutex_lock(&codec->hash_mutex);
- codec->cached_write = 0;
- for (i = 0; i < codec->cmd_cache.buf.used; i++) {
- struct hda_cache_head *buffer;
- u32 key;
-
- buffer = snd_array_elem(&codec->cmd_cache.buf, i);
- key = buffer->key;
- if (!key)
- continue;
- if (!buffer->dirty)
- continue;
- buffer->dirty = 0;
- mutex_unlock(&codec->hash_mutex);
- snd_hda_codec_write(codec, get_cmd_cache_nid(key), 0,
- get_cmd_cache_cmd(key), buffer->val);
- mutex_lock(&codec->hash_mutex);
- }
- mutex_unlock(&codec->hash_mutex);
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_resume_cache);
-
-/**
- * snd_hda_sequence_write_cache - sequence writes with caching
- * @codec: the HDA codec
- * @seq: VERB array to send
- *
- * Send the commands sequentially from the given array.
- * Thte commands are recorded on cache for power-save and resume.
- * The array must be terminated with NID=0.
- */
-void snd_hda_sequence_write_cache(struct hda_codec *codec,
- const struct hda_verb *seq)
-{
- for (; seq->nid; seq++)
- snd_hda_codec_write_cache(codec, seq->nid, 0, seq->verb,
- seq->param);
-}
-EXPORT_SYMBOL_GPL(snd_hda_sequence_write_cache);
-
-/**
- * snd_hda_codec_flush_cache - Execute all pending (cached) amps / verbs
- * @codec: HD-audio codec
- */
-void snd_hda_codec_flush_cache(struct hda_codec *codec)
-{
- snd_hda_codec_resume_amp(codec);
- snd_hda_codec_resume_cache(codec);
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_flush_cache);
-
/**
* snd_hda_codec_set_power_to_all - Set the power state to all widgets
* @codec: the HDA codec
@@ -3621,22 +3149,6 @@ static unsigned int hda_call_codec_suspend(struct hda_codec *codec)
return state;
}
-/* mark all entries of cmd and amp caches dirty */
-static void hda_mark_cmd_cache_dirty(struct hda_codec *codec)
-{
- int i;
- for (i = 0; i < codec->cmd_cache.buf.used; i++) {
- struct hda_cache_head *cmd;
- cmd = snd_array_elem(&codec->cmd_cache.buf, i);
- cmd->dirty = 1;
- }
- for (i = 0; i < codec->amp_cache.buf.used; i++) {
- struct hda_amp_info *amp;
- amp = snd_array_elem(&codec->amp_cache.buf, i);
- amp->head.dirty = 1;
- }
-}
-
/*
* kick up codec; used both from PM and power-save
*/
@@ -3644,7 +3156,8 @@ static void hda_call_codec_resume(struct hda_codec *codec)
{
atomic_inc(&codec->core.in_pm);
- hda_mark_cmd_cache_dirty(codec);
+ if (codec->core.regmap)
+ regcache_mark_dirty(codec->core.regmap);
codec->power_jiffies = jiffies;
@@ -3657,8 +3170,8 @@ static void hda_call_codec_resume(struct hda_codec *codec)
else {
if (codec->patch_ops.init)
codec->patch_ops.init(codec);
- snd_hda_codec_resume_amp(codec);
- snd_hda_codec_resume_cache(codec);
+ if (codec->core.regmap)
+ regcache_sync(codec->core.regmap);
}
if (codec->jackpoll_interval)
@@ -3878,8 +3391,7 @@ unsigned int snd_hda_calc_stream_format(struct hda_codec *codec,
}
EXPORT_SYMBOL_GPL(snd_hda_calc_stream_format);
-static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid,
- int dir)
+static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid)
{
unsigned int val = 0;
if (nid != codec->core.afg &&
@@ -3892,14 +3404,7 @@ static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid,
return val;
}
-static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid)
-{
- return query_caps_hash(codec, nid, 0, HDA_HASH_PARPCM_KEY(nid),
- get_pcm_param);
-}
-
-static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid,
- int dir)
+static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid)
{
unsigned int streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
if (!streams || streams == -1)
@@ -3909,12 +3414,6 @@ static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid,
return streams;
}
-static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid)
-{
- return query_caps_hash(codec, nid, 0, HDA_HASH_PARSTR_KEY(nid),
- get_stream_param);
-}
-
/**
* snd_hda_query_supported_pcm - query the supported PCM rates and formats
* @codec: the HDA codec
@@ -5012,52 +4511,6 @@ void snd_hda_bus_reset(struct hda_bus *bus)
}
EXPORT_SYMBOL_GPL(snd_hda_bus_reset);
-/*
- * generic arrays
- */
-
-/**
- * snd_array_new - get a new element from the given array
- * @array: the array object
- *
- * Get a new element from the given array. If it exceeds the
- * pre-allocated array size, re-allocate the array.
- *
- * Returns NULL if allocation failed.
- */
-void *snd_array_new(struct snd_array *array)
-{
- if (snd_BUG_ON(!array->elem_size))
- return NULL;
- if (array->used >= array->alloced) {
- int num = array->alloced + array->alloc_align;
- int size = (num + 1) * array->elem_size;
- void *nlist;
- if (snd_BUG_ON(num >= 4096))
- return NULL;
- nlist = krealloc(array->list, size, GFP_KERNEL | __GFP_ZERO);
- if (!nlist)
- return NULL;
- array->list = nlist;
- array->alloced = num;
- }
- return snd_array_elem(array, array->used++);
-}
-EXPORT_SYMBOL_GPL(snd_array_new);
-
-/**
- * snd_array_free - free the given array elements
- * @array: the array object
- */
-void snd_array_free(struct snd_array *array)
-{
- kfree(array->list);
- array->used = 0;
- array->alloced = 0;
- array->list = NULL;
-}
-EXPORT_SYMBOL_GPL(snd_array_free);
-
/**
* snd_print_pcm_bits - Print the supported PCM fmt bits to the string buffer
* @pcm: PCM caps bits
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 21632174744b..acf868c6a785 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -28,36 +28,7 @@
#include <sound/hwdep.h>
#include <sound/hdaudio.h>
#include <sound/hda_verbs.h>
-
-/*
- * generic arrays
- */
-struct snd_array {
- unsigned int used;
- unsigned int alloced;
- unsigned int elem_size;
- unsigned int alloc_align;
- void *list;
-};
-
-void *snd_array_new(struct snd_array *array);
-void snd_array_free(struct snd_array *array);
-static inline void snd_array_init(struct snd_array *array, unsigned int size,
- unsigned int align)
-{
- array->elem_size = size;
- array->alloc_align = align;
-}
-
-static inline void *snd_array_elem(struct snd_array *array, unsigned int idx)
-{
- return array->list + idx * array->elem_size;
-}
-
-static inline unsigned int snd_array_index(struct snd_array *array, void *ptr)
-{
- return (unsigned long)(ptr - array->list) / array->elem_size;
-}
+#include <sound/hda_regmap.h>
/*
* Structures
@@ -181,25 +152,6 @@ struct hda_codec_ops {
void (*stream_pm)(struct hda_codec *codec, hda_nid_t nid, bool on);
};
-/* record for amp information cache */
-struct hda_cache_head {
- u32 key:31; /* hash key */
- u32 dirty:1;
- u16 val; /* assigned value */
- u16 next;
-};
-
-struct hda_amp_info {
- struct hda_cache_head head;
- u32 amp_caps; /* amp capabilities */
- u16 vol[2]; /* current volume & mute */
-};
-
-struct hda_cache_rec {
- u16 hash[64]; /* hash table for index */
- struct snd_array buf; /* record entries */
-};
-
/* PCM callbacks */
struct hda_pcm_ops {
int (*open)(struct hda_pcm_stream *info, struct hda_codec *codec,
@@ -283,14 +235,10 @@ struct hda_codec {
struct snd_array mixers; /* list of assigned mixer elements */
struct snd_array nids; /* list of mapped mixer elements */
- struct hda_cache_rec amp_cache; /* cache for amp access */
- struct hda_cache_rec cmd_cache; /* cache for other commands */
-
struct list_head conn_list; /* linked-list of connection-list */
struct mutex spdif_mutex;
struct mutex control_mutex;
- struct mutex hash_mutex;
struct snd_array spdif_out;
unsigned int spdif_in_enable; /* SPDIF input enable? */
const hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */
@@ -395,7 +343,7 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags,
unsigned int verb, unsigned int parm);
#define snd_hda_param_read(codec, nid, param) \
- snd_hda_codec_read(codec, nid, 0, AC_VERB_PARAMETERS, param)
+ snd_hdac_read_parm(&(codec)->core, nid, param)
#define snd_hda_get_sub_nodes(codec, nid, start_nid) \
snd_hdac_get_sub_nodes(&(codec)->core, nid, start_nid)
int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
@@ -439,15 +387,15 @@ snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex)
}
/* cached write */
-int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
- int flags, unsigned int verb, unsigned int parm);
-void snd_hda_sequence_write_cache(struct hda_codec *codec,
- const struct hda_verb *seq);
-int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid,
- int flags, unsigned int verb, unsigned int parm);
-void snd_hda_codec_resume_cache(struct hda_codec *codec);
-/* both for cmd & amp caches */
-void snd_hda_codec_flush_cache(struct hda_codec *codec);
+static inline int
+snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
+ int flags, unsigned int verb, unsigned int parm)
+{
+ return snd_hdac_regmap_write(&codec->core, nid, verb, parm);
+}
+
+#define snd_hda_codec_update_cache(codec, nid, flags, verb, parm) \
+ snd_hda_codec_write_cache(codec, nid, flags, verb, parm)
/* the struct for codec->pin_configs */
struct hda_pincfg {
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 4850f92c89c4..f7ccef5559de 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -3381,11 +3381,6 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol,
imux = &spec->input_mux;
adc_idx = kcontrol->id.index;
mutex_lock(&codec->control_mutex);
- /* we use the cache-only update at first since multiple input paths
- * may shared the same amp; by updating only caches, the redundant
- * writes to hardware can be reduced.
- */
- codec->cached_write = 1;
for (i = 0; i < imux->num_items; i++) {
path = get_input_path(codec, adc_idx, i);
if (!path || !path->ctls[type])
@@ -3393,12 +3388,9 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol,
kcontrol->private_value = path->ctls[type];
err = func(kcontrol, ucontrol);
if (err < 0)
- goto error;
+ break;
}
- error:
- codec->cached_write = 0;
mutex_unlock(&codec->control_mutex);
- snd_hda_codec_flush_cache(codec); /* flush the updates */
if (err >= 0 && spec->cap_sync_hook)
spec->cap_sync_hook(codec, kcontrol, ucontrol);
return err;
@@ -5760,8 +5752,6 @@ int snd_hda_gen_init(struct hda_codec *codec)
snd_hda_apply_verbs(codec);
- codec->cached_write = 1;
-
init_multi_out(codec);
init_extra_out(codec);
init_multi_io(codec);
@@ -5777,7 +5767,7 @@ int snd_hda_gen_init(struct hda_codec *codec)
/* call init functions of standard auto-mute helpers */
update_automute_all(codec);
- snd_hda_codec_flush_cache(codec);
+ regcache_sync(codec->core.regmap);
if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook)
snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 8a83775e0e27..3b567f42296b 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -127,18 +127,16 @@ 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 */
-int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
- int direction, int index);
-int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
- int direction, int idx, int mask, int val);
+#define snd_hda_codec_amp_read(codec, nid, ch, dir, idx) \
+ snd_hdac_regmap_get_amp(&(codec)->core, nid, ch, dir, idx)
+#define snd_hda_codec_amp_update(codec, nid, ch, dir, idx, mask, val) \
+ snd_hdac_regmap_update_amp(&(codec)->core, nid, ch, dir, idx, mask, val)
int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
int dir, 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_codec_resume_amp(struct hda_codec *codec);
-
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,
@@ -559,9 +557,41 @@ static inline void snd_hda_override_wcaps(struct hda_codec *codec,
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);
-u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid);
-int snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid,
- 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);
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
index a4f5a30f1d41..ee6230767c64 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
@@ -32,6 +32,10 @@ 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 char *bits_names(unsigned int bits, char *names[], int size)
{
int i, n;
@@ -119,9 +123,8 @@ static void print_amp_caps(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid, int dir)
{
unsigned int caps;
- caps = snd_hda_param_read(codec, nid,
- dir == HDA_OUTPUT ?
- AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
+ 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;
@@ -225,8 +228,8 @@ static void print_pcm_formats(struct snd_info_buffer *buffer,
static void print_pcm_caps(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
- unsigned int pcm = snd_hda_param_read(codec, nid, AC_PAR_PCM);
- unsigned int stream = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
+ 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;
@@ -273,7 +276,7 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" };
unsigned int caps, val;
- caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+ 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");
@@ -401,8 +404,7 @@ static void print_pin_ctls(struct snd_info_buffer *buffer,
static void print_vol_knob(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
- unsigned int cap = snd_hda_param_read(codec, nid,
- AC_PAR_VOL_KNB_CAP);
+ 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,
@@ -487,7 +489,7 @@ static void print_power_state(struct snd_info_buffer *buffer,
[ilog2(AC_PWRST_EPSS)] = "EPSS",
};
- int sup = snd_hda_param_read(codec, nid, AC_PAR_POWER_STATE);
+ 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)
@@ -531,8 +533,7 @@ 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 = snd_hda_param_read(codec, nid,
- AC_PAR_PROC_CAP);
+ 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);
@@ -597,7 +598,7 @@ static void print_gpio(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
unsigned int gpio =
- snd_hda_param_read(codec, codec->core.afg, AC_PAR_GPIO_CAP);
+ 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, "
@@ -727,8 +728,7 @@ static void print_codec_info(struct snd_info_entry *entry,
for (i = 0; i < nodes; i++, nid++) {
unsigned int wid_caps =
- snd_hda_param_read(codec, nid,
- AC_PAR_AUDIO_WIDGET_CAP);
+ 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;
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index 2278e83234b5..231f89029779 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -777,7 +777,6 @@ static int ad1988_auto_smux_enum_put(struct snd_kcontrol *kcontrol,
return 0;
mutex_lock(&codec->control_mutex);
- codec->cached_write = 1;
path = snd_hda_get_path_from_idx(codec,
spec->smux_paths[spec->cur_smux]);
if (path)
@@ -786,9 +785,7 @@ static int ad1988_auto_smux_enum_put(struct snd_kcontrol *kcontrol,
if (path)
snd_hda_activate_path(codec, path, true, true);
spec->cur_smux = val;
- codec->cached_write = 0;
mutex_unlock(&codec->control_mutex);
- snd_hda_codec_flush_cache(codec); /* flush the updates */
return 1;
}
@@ -1004,18 +1001,17 @@ static void ad1884_fixup_hp_eapd(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
struct ad198x_spec *spec = codec->spec;
- static const struct hda_verb gpio_init_verbs[] = {
- {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x02},
- {},
- };
switch (action) {
case HDA_FIXUP_ACT_PRE_PROBE:
spec->gen.vmaster_mute.hook = ad1884_vmaster_hp_gpio_hook;
spec->gen.own_eapd_ctl = 1;
- snd_hda_sequence_write_cache(codec, gpio_init_verbs);
+ snd_hda_codec_write_cache(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_MASK, 0x02);
+ snd_hda_codec_write_cache(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DIRECTION, 0x02);
+ snd_hda_codec_write_cache(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DATA, 0x02);
break;
case HDA_FIXUP_ACT_PROBE:
if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 1e21f9fbd54b..f8f0dfbef149 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -302,6 +302,7 @@ static void cxt_fixup_headphone_mic(struct hda_codec *codec,
switch (action) {
case HDA_FIXUP_ACT_PRE_PROBE:
spec->parse_flags |= HDA_PINCFG_HEADPHONE_MIC;
+ snd_hdac_regmap_add_vendor_verb(&codec->core, 0x410);
break;
case HDA_FIXUP_ACT_PROBE:
spec->gen.cap_sync_hook = cxt_update_headset_mode_hook;
@@ -409,15 +410,11 @@ static void olpc_xo_automic(struct hda_codec *codec,
struct hda_jack_callback *jack)
{
struct conexant_spec *spec = codec->spec;
- int saved_cached_write = codec->cached_write;
- codec->cached_write = 1;
/* in DC mode, we don't handle automic */
if (!spec->dc_enable)
snd_hda_gen_mic_autoswitch(codec, jack);
olpc_xo_update_mic_pins(codec);
- snd_hda_codec_flush_cache(codec);
- codec->cached_write = saved_cached_write;
if (spec->dc_enable)
olpc_xo_update_mic_boost(codec);
}
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 35d92a8a99ce..ca0c05e1c42e 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -2211,8 +2211,7 @@ static int generic_hdmi_resume(struct hda_codec *codec)
int pin_idx;
codec->patch_ops.init(codec);
- snd_hda_codec_resume_amp(codec);
- snd_hda_codec_resume_cache(codec);
+ regcache_sync(codec->core.regmap);
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
@@ -2299,6 +2298,7 @@ static void intel_haswell_fixup_enable_dp12(struct hda_codec *codec)
/* enable DP1.2 mode */
vendor_param |= INTEL_EN_DP12;
+ snd_hdac_regmap_add_vendor_verb(&codec->core, INTEL_SET_VENDOR_VERB);
snd_hda_codec_write_cache(codec, INTEL_VENDOR_NID, 0,
INTEL_SET_VENDOR_VERB, vendor_param);
}
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index eee4532ab5f6..d44cb7e37094 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -799,8 +799,7 @@ static int alc_resume(struct hda_codec *codec)
if (!spec->no_depop_delay)
msleep(150); /* to avoid pop noise */
codec->patch_ops.init(codec);
- snd_hda_codec_resume_amp(codec);
- snd_hda_codec_resume_cache(codec);
+ regcache_sync(codec->core.regmap);
hda_call_check_power_status(codec, 0x01);
return 0;
}
@@ -3058,8 +3057,7 @@ static int alc269_resume(struct hda_codec *codec)
msleep(200);
}
- snd_hda_codec_resume_amp(codec);
- snd_hda_codec_resume_cache(codec);
+ regcache_sync(codec->core.regmap);
hda_call_check_power_status(codec, 0x01);
/* on some machine, the BIOS will clear the codec gpio data when enter
diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c
index 49b4868797a5..5104bebb2286 100644
--- a/sound/pci/hda/patch_si3054.c
+++ b/sound/pci/hda/patch_si3054.c
@@ -222,6 +222,10 @@ static int si3054_init(struct hda_codec *codec)
unsigned wait_count;
u16 val;
+ if (snd_hdac_regmap_add_vendor_verb(&codec->core,
+ SI3054_VERB_WRITE_NODE))
+ return -ENOMEM;
+
snd_hda_codec_write(codec, AC_NODE_ROOT, 0, AC_VERB_SET_CODEC_RESET, 0);
snd_hda_codec_write(codec, codec->core.mfg, 0, AC_VERB_SET_STREAM_FORMAT, 0);
SET_REG(codec, SI3054_LINE_RATE, 9600);
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index b314551749f1..43c99ce4a520 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -1050,12 +1050,9 @@ static const struct hda_verb stac92hd71bxx_core_init[] = {
{}
};
-static const struct hda_verb stac92hd71bxx_unmute_core_init[] = {
+static const hda_nid_t stac92hd71bxx_unmute_nids[] = {
/* unmute right and left channels for nodes 0x0f, 0xa, 0x0d */
- { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {}
+ 0x0f, 0x0a, 0x0d, 0
};
static const struct hda_verb stac925x_core_init[] = {
@@ -4269,6 +4266,10 @@ static int stac_parse_auto_config(struct hda_codec *codec)
if (spec->aloopback_ctl &&
snd_hda_get_bool_hint(codec, "loopback") == 1) {
+ unsigned int wr_verb =
+ spec->aloopback_ctl->private_value >> 16;
+ if (snd_hdac_regmap_add_vendor_verb(&codec->core, wr_verb))
+ return -ENOMEM;
if (!snd_hda_gen_add_kctl(&spec->gen, NULL, spec->aloopback_ctl))
return -ENOMEM;
}
@@ -4688,7 +4689,7 @@ static int patch_stac92hd95(struct hda_codec *codec)
static int patch_stac92hd71bxx(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
- const struct hda_verb *unmute_init = stac92hd71bxx_unmute_core_init;
+ const hda_nid_t *unmute_nids = stac92hd71bxx_unmute_nids;
int err;
err = alloc_stac_spec(codec);
@@ -4713,7 +4714,7 @@ static int patch_stac92hd71bxx(struct hda_codec *codec)
switch (codec->core.vendor_id) {
case 0x111d76b6: /* 4 Port without Analog Mixer */
case 0x111d76b7:
- unmute_init++;
+ unmute_nids++;
break;
case 0x111d7608: /* 5 Port with Analog Mixer */
if ((codec->core.revision_id & 0xf) == 0 ||
@@ -4721,7 +4722,7 @@ static int patch_stac92hd71bxx(struct hda_codec *codec)
spec->stream_delay = 40; /* 40 milliseconds */
/* disable VSW */
- unmute_init++;
+ unmute_nids++;
snd_hda_codec_set_pincfg(codec, 0x0f, 0x40f000f0);
snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3);
break;
@@ -4735,8 +4736,12 @@ static int patch_stac92hd71bxx(struct hda_codec *codec)
if (get_wcaps_type(get_wcaps(codec, 0x28)) == AC_WID_VOL_KNB)
snd_hda_add_verbs(codec, stac92hd71bxx_core_init);
- if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP)
- snd_hda_sequence_write_cache(codec, unmute_init);
+ if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP) {
+ const hda_nid_t *p;
+ for (p = unmute_nids; *p; p++)
+ snd_hda_codec_amp_init_stereo(codec, *p, HDA_INPUT, 0,
+ 0xff, 0x00);
+ }
spec->aloopback_ctl = &stac92hd71bxx_loopback;
spec->aloopback_mask = 0x50;