[alsa-devel] [PATCH 0/7] HD-audio regmap support
Hi,
this patch adds the regmap support to HD-audio and convert the house-made amp/verb caches to regmap. Because of the asymmetrical nature of HD-audio verbs, the adaption of regmap isn't straightforward at all, and has many limitations. But this still reduces the complexity in struct hda_codec a bit.
The patches are applied on top of the previous patch series (found in topic/hda-bus branch of sound git tree). This patchset is also found in sound git tree topic/hda-regmap branch.
Takashi
===
Takashi Iwai (7): ALSA: hda - Add regmap support ALSA: hda - Use regmap for codec parameter reads ALSA: hda - Use regmap for amp accesses ALSA: hda - Use regmap for parameter caches, too ALSA: hda - Allow driver to add vendor-specific verbs for regmap ALSA: hda - Use regmap for command verb caches, too ALSA: hda - Reduce verbs during generic parser initialization
sound/pci/hda/Kconfig | 1 + sound/pci/hda/Makefile | 3 +- sound/pci/hda/hda_beep.c | 3 +- sound/pci/hda/hda_bind.c | 6 + sound/pci/hda/hda_codec.c | 645 +++++++---------------------------------- sound/pci/hda/hda_codec.h | 45 +-- sound/pci/hda/hda_generic.c | 32 +- sound/pci/hda/hda_jack.c | 6 +- sound/pci/hda/hda_local.h | 31 +- sound/pci/hda/hda_regmap.c | 409 ++++++++++++++++++++++++++ sound/pci/hda/hda_regmap.h | 38 +++ sound/pci/hda/patch_analog.c | 30 +- sound/pci/hda/patch_conexant.c | 12 +- sound/pci/hda/patch_hdmi.c | 24 +- sound/pci/hda/patch_realtek.c | 11 +- sound/pci/hda/patch_si3054.c | 5 +- sound/pci/hda/patch_sigmatel.c | 38 ++- 17 files changed, 660 insertions(+), 679 deletions(-) create mode 100644 sound/pci/hda/hda_regmap.c create mode 100644 sound/pci/hda/hda_regmap.h
This patch adds an infrastructure to support regmap-based verb accesses. Because o the asymmetric nature of HD-audio verbs, especially the amp verbs, we need to translate the verbs as a sort of pseudo registers to be mapped uniquely in regmap.
In this patch, a pseudo register is built from the NID, the AC_VERB_GET_* and 8bit parameters, i.e. almost in the form to be sent to HD-audio bus but without codec address field. OTOH, for writing, the same pseudo register is translated to AC_VERB_SET_* automatically. The AC_VERB_SET_AMP_* verb is re-encoded from the corresponding AC_VERB_GET_AMP_* verb and parameter at writing.
Some verbs has a single command for read but multiple for writes. A write for such a verb is split automatically to multiple verbs.
The patch provides also a few handy helper functions. They are designed to be accessible even without regmap. When no regmap is set up (e.g. before the codec device instantiation), the direct hardware access is used. Also, it tries to avoid the unnecessary power-up. The power up/down sequence is performed only on demand.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/Kconfig | 1 + sound/pci/hda/Makefile | 3 +- sound/pci/hda/hda_bind.c | 6 + sound/pci/hda/hda_codec.c | 8 +- sound/pci/hda/hda_codec.h | 6 + sound/pci/hda/hda_regmap.c | 375 +++++++++++++++++++++++++++++++++++++++++++++ sound/pci/hda/hda_regmap.h | 36 +++++ 7 files changed, 430 insertions(+), 5 deletions(-) create mode 100644 sound/pci/hda/hda_regmap.c create mode 100644 sound/pci/hda/hda_regmap.h
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 7f0f2c5a4e97..eda62d9fb2ea 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -5,6 +5,7 @@ config SND_HDA select SND_PCM select SND_VMASTER select SND_KCTL_JACK + select REGMAP
config SND_HDA_INTEL tristate "HD Audio PCI" diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index 96caaebfc19d..1f2c626c4ae6 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -4,7 +4,8 @@ snd-hda-tegra-objs := hda_tegra.o # for haswell power well snd-hda-intel-$(CONFIG_SND_HDA_I915) += hda_i915.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_bind.o hda_codec.o hda_jack.o hda_auto_parser.o hda_sysfs.o \ + hda_regmap.o snd-hda-codec-$(CONFIG_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 diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c index ce2dd7b0dc07..8be97ffe10b2 100644 --- a/sound/pci/hda/hda_bind.c +++ b/sound/pci/hda/hda_bind.c @@ -11,6 +11,7 @@ #include <linux/pm.h> #include <sound/core.h> #include "hda_codec.h" +#include "hda_regmap.h" #include "hda_local.h"
/* codec vendor labels */ @@ -105,6 +106,9 @@ static int hda_codec_driver_probe(struct device *dev) goto error; }
+ err = snd_hda_regmap_init(codec); + if (err < 0) + goto error; err = codec->preset->patch(codec); if (err < 0) { module_put(owner); @@ -116,6 +120,7 @@ static int hda_codec_driver_probe(struct device *dev) error: codec->preset = NULL; memset(&codec->patch_ops, 0, sizeof(codec->patch_ops)); + snd_hda_regmap_exit(codec); return err; }
@@ -127,6 +132,7 @@ static int hda_codec_driver_remove(struct device *dev) codec->patch_ops.free(codec); codec->preset = NULL; memset(&codec->patch_ops, 0, sizeof(codec->patch_ops)); + snd_hda_regmap_exit(codec); module_put(dev->driver->owner); return 0; } diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index db86b446743c..0fd71131193a 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -147,8 +147,8 @@ make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int flags, /* * Send and receive a verb */ -static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd, - int flags, unsigned int *res) +int snd_hda_codec_exec_verb(struct hda_codec *codec, unsigned int cmd, + int flags, unsigned int *res) { struct hda_bus *bus = codec->bus; int err; @@ -211,7 +211,7 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, { unsigned cmd = make_codec_cmd(codec, nid, flags, verb, parm); unsigned int res; - if (codec_exec_verb(codec, cmd, flags, &res)) + if (snd_hda_codec_exec_verb(codec, cmd, flags, &res)) return -1; return res; } @@ -234,7 +234,7 @@ int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags, { unsigned int cmd = make_codec_cmd(codec, nid, flags, verb, parm); unsigned int res; - return codec_exec_verb(codec, cmd, flags, + return snd_hda_codec_exec_verb(codec, cmd, flags, codec->bus->sync_write ? &res : NULL); } EXPORT_SYMBOL_GPL(snd_hda_codec_write); diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 457fc589eb46..3be4e1040036 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -21,6 +21,7 @@ #ifndef __SOUND_HDA_CODEC_H #define __SOUND_HDA_CODEC_H
+#include <linux/regmap.h> #include <sound/info.h> #include <sound/control.h> #include <sound/pcm.h> @@ -401,6 +402,9 @@ struct hda_codec {
/* additional init verbs */ struct snd_array verbs; + + /* regmap */ + struct regmap *regmap; };
#define dev_to_hda_codec(_dev) container_of(_dev, struct hda_codec, dev) @@ -428,6 +432,8 @@ int snd_hda_codec_update_widgets(struct hda_codec *codec); /* * low level functions */ +int snd_hda_codec_exec_verb(struct hda_codec *codec, unsigned int cmd, + int flags, unsigned int *res); unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, int flags, unsigned int verb, unsigned int parm); diff --git a/sound/pci/hda/hda_regmap.c b/sound/pci/hda/hda_regmap.c new file mode 100644 index 000000000000..715d770901b5 --- /dev/null +++ b/sound/pci/hda/hda_regmap.c @@ -0,0 +1,375 @@ +/* + * 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 "hda_codec.h" +#include "hda_regmap.h" + +#ifdef CONFIG_PM +#define codec_is_running(codec) \ + (atomic_read(&(codec)->in_pm) || \ + !pm_runtime_suspended(hda_codec_dev(codec))) +#else +#define codec_is_running(codec) true +#endif + +#define get_verb(reg) (((reg) >> 8) & 0xfff) + +static bool hda_writeable_reg(struct device *dev, unsigned int reg) +{ + unsigned int verb = get_verb(reg); + + switch (verb & 0xf00) { + case AC_VERB_GET_STREAM_FORMAT: + case AC_VERB_GET_AMP_GAIN_MUTE: + return true; + case 0xf00: + break; + default: + return false; + } + + switch (verb) { + case AC_VERB_GET_CONNECT_SEL: + case AC_VERB_GET_SDI_SELECT: + case AC_VERB_GET_CONV: + 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_CONFIG_DEFAULT: + 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_UNSOLICITED_RSP_MASK: + case AC_VERB_GET_CVT_CHAN_COUNT: + return true; + } + + return false; +} + +static bool hda_readable_reg(struct device *dev, unsigned int reg) +{ + unsigned int verb = get_verb(reg); + + switch (verb) { + case AC_VERB_PARAMETERS: + case AC_VERB_GET_CONNECT_LIST: + case AC_VERB_GET_SUBSYSTEM_ID: + return true; + } + + return hda_writeable_reg(dev, reg); +} + +static int hda_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct hda_codec *codec = context; + + if (!codec_is_running(codec)) + return -EAGAIN; + + reg |= (codec->addr << 28); + if (snd_hda_codec_exec_verb(codec, reg, 0, val) < 0) + return -EIO; + return 0; +} + +static int hda_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct hda_codec *codec = context; + unsigned int verb; + int i, bytes; + + if (!codec_is_running(codec)) + return 0; /* skip the h/w write, it'll be synced later */ + + reg &= ~0x00080000U; /* drop GET bit */ + reg |= (codec->addr << 28); + verb = get_verb(reg); + + 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); + if (snd_hda_codec_exec_verb(codec, reg, 0, NULL) < 0) + return -EIO; + } + + 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, + .cache_type = REGCACHE_RBTREE, +}; + +static const struct regmap_bus hda_regmap_bus = { + .reg_read = hda_reg_read, + .reg_write = hda_reg_write, +}; + +int snd_hda_regmap_init(struct hda_codec *codec) +{ + struct regmap *regmap; + + regmap = regmap_init(hda_codec_dev(codec), &hda_regmap_bus, + codec, &hda_regmap_cfg); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + codec->regmap = regmap; + return 0; +} + +void snd_hda_regmap_exit(struct hda_codec *codec) +{ + if (codec->regmap) { + regmap_exit(codec->regmap); + codec->regmap = NULL; + } +} + +/* + * helper functions + */ + +/* write a pseudo-register value (w/o power sequence) */ +int snd_hda_reg_raw_write(struct hda_codec *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); +} + +static int reg_raw_read(struct hda_codec *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); +} + +/* read a pseudo-register value; + * this function powers up the codec if necessary + */ +int snd_hda_reg_raw_read(struct hda_codec *codec, unsigned int reg, + unsigned int *val) +{ + int err; + + err = reg_raw_read(codec, reg, val); + if (err == -EAGAIN) { + snd_hda_power_up(codec); + err = reg_raw_read(codec, reg, val); + snd_hda_power_down(codec); + } + return err; +} + +/* update a pseudo-register value; + * this function powers up the codec if necessary + */ +int snd_hda_reg_raw_update(struct hda_codec *codec, unsigned int reg, + unsigned int mask, unsigned int val) +{ + unsigned int orig; + int err; + + err = snd_hda_reg_raw_read(codec, reg, &orig); + if (err < 0) + return err; + if ((orig & mask) == val) + return 0; + orig &= ~mask; + orig |= val; + err = snd_hda_reg_raw_write(codec, reg, val); + if (err < 0) + return err; + return 1; +} + +static inline unsigned int encode_reg(hda_nid_t nid, unsigned int verb) +{ + verb |= 0x800; + if (snd_BUG_ON((verb & 0xf00) == AC_VERB_GET_AMP_GAIN_MUTE)) + verb = 0; + verb = (verb << 8) | (nid << 20); + return verb; +} + +/** + * snd_hda_regmap_write - Write a verb with caching + * @nid: codec NID + * @reg: verb to write + * @val: value to write + * + * This function doesn't power up the codec but only updates cache + * if the device is suspended. Wrap with snd_hda_power_up() and + * snd_hda_power_down() accordingly in the caller side if necessary. + * + * For writing an amp value, use snd_hda_regmap_amp_update(). + */ +int snd_hda_regmap_write(struct hda_codec *codec, hda_nid_t nid, + unsigned int verb, unsigned int val) +{ + return snd_hda_reg_raw_write(codec, encode_reg(nid, verb), val); +} +EXPORT_SYMBOL_GPL(snd_hda_regmap_write); + +/** + * snd_hda_regmap_update_bits - Update a verb value with caching + * @nid: codec NID + * @verb: verb to update + * @mask: bit mask to update + * @val: value to update + * + * This function doesn't always power up the codec but only updates cache + * if the device is suspended. Wrap with snd_hda_power_up() and + * snd_hda_power_down() accordingly in the caller side if necessary. + * + * For updating an amp value, use snd_hda_regmap_amp_update(). + */ +int snd_hda_regmap_update_bits(struct hda_codec *codec, hda_nid_t nid, + unsigned int verb, unsigned int mask, + unsigned int val) +{ + return snd_hda_reg_raw_update(codec, encode_reg(nid, verb), mask, val); +} +EXPORT_SYMBOL_GPL(snd_hda_regmap_update_bits); + +/** + * 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(). + */ +int snd_hda_regmap_read(struct hda_codec *codec, hda_nid_t nid, + unsigned int verb, unsigned int *val) +{ + return snd_hda_reg_raw_read(codec, encode_reg(nid, verb), val); +} +EXPORT_SYMBOL_GPL(snd_hda_regmap_read); + +/* encode to a pseudo-register from amp attributes */ +static inline unsigned int encode_amp(hda_nid_t nid, int ch, int dir, int idx) +{ + unsigned int val; + + val = (nid << 20) | (AC_VERB_GET_AMP_GAIN_MUTE << 8); + if (ch) + val |= AC_AMP_GET_RIGHT; + else + val |= AC_AMP_GET_LEFT; + if (dir == HDA_OUTPUT) + val |= AC_AMP_GET_OUTPUT; + else + val |= AC_AMP_GET_INPUT; + val |= idx; + return val; +} + +/** + * snd_hda_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. + */ +int snd_hda_regmap_amp_get(struct hda_codec *codec, hda_nid_t nid, + int ch, int dir, int idx) +{ + int err, val; + + err = snd_hda_reg_raw_read(codec, + encode_amp(nid, ch, dir, idx), &val); + return err < 0 ? err : val; +} +EXPORT_SYMBOL_GPL(snd_hda_regmap_amp_get); + +/** + * snd_hda_regmap_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, or a negative error. + * + * This function doesn't always power up the codec but only updates cache + * if the device is suspended. Wrap with snd_hda_power_up() and + * snd_hda_power_down() accordingly in the caller side if necessary. + */ +int snd_hda_regmap_amp_update(struct hda_codec *codec, hda_nid_t nid, + int ch, int dir, int idx, int mask, int val) +{ + return snd_hda_reg_raw_update(codec, + encode_amp(nid, ch, dir, idx), + mask, val); +} +EXPORT_SYMBOL_GPL(snd_hda_regmap_amp_update); diff --git a/sound/pci/hda/hda_regmap.h b/sound/pci/hda/hda_regmap.h new file mode 100644 index 000000000000..7d4b8be58975 --- /dev/null +++ b/sound/pci/hda/hda_regmap.h @@ -0,0 +1,36 @@ +/* + * HD-audio regmap helpers + */ + +#ifndef __HDA_REGMAP_H +#define __HDA_REGMAP_H + +#include <linux/regmap.h> +#include <sound/core.h> +#include <sound/hda_verbs.h> +#include "hda_codec.h" + +int snd_hda_regmap_init(struct hda_codec *codec); +void snd_hda_regmap_exit(struct hda_codec *codec); + +int snd_hda_regmap_write(struct hda_codec *codec, hda_nid_t nid, + unsigned int verb, unsigned int val); +int snd_hda_regmap_update_bits(struct hda_codec *codec, hda_nid_t nid, + unsigned int verb, unsigned int mask, + unsigned int val); +int snd_hda_regmap_read(struct hda_codec *codec, hda_nid_t nid, + unsigned int verb, unsigned int *val); + +int snd_hda_regmap_amp_get(struct hda_codec *codec, hda_nid_t nid, + int ch, int dir, int idx); +int snd_hda_regmap_amp_update(struct hda_codec *codec, hda_nid_t nid, + int ch, int dir, int idx, int mask, int val); + +int snd_hda_reg_raw_read(struct hda_codec *codec, unsigned int reg, + unsigned int *val); +int snd_hda_reg_raw_write(struct hda_codec *codec, unsigned int reg, + unsigned int val); +int snd_hda_reg_raw_update(struct hda_codec *codec, unsigned int reg, + unsigned int mask, unsigned int val); + +#endif /* __HDA_REGMAP_H */
Let's start converting the access functions to regmap. The first one is the simplest, just converting the codec parameter read helper function snd_hda_param_read().
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_codec.c | 18 ++++++++++++++++++ sound/pci/hda/hda_codec.h | 3 +-- 2 files changed, 19 insertions(+), 2 deletions(-)
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 0fd71131193a..3a209e8b6130 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -34,6 +34,7 @@ #include <sound/tlv.h> #include <sound/initval.h> #include <sound/jack.h> +#include "hda_regmap.h" #include "hda_local.h" #include "hda_beep.h" #include "hda_jack.h" @@ -255,6 +256,23 @@ void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq) EXPORT_SYMBOL_GPL(snd_hda_sequence_write);
/** + * snd_hda_param_read - read a parameter + * @codec: the HDA codec + * @nid: NID to read + * @parm: parameter value + */ +int snd_hda_param_read(struct hda_codec *codec, hda_nid_t nid, int parm) +{ + unsigned int verb = (AC_VERB_PARAMETERS << 8) | (nid << 20) | parm; + unsigned int val; + + if (snd_hda_reg_raw_read(codec, verb, &val) < 0) + return -1; + return val; +} +EXPORT_SYMBOL_GPL(snd_hda_param_read); + +/** * snd_hda_get_sub_nodes - get the range of sub nodes * @codec: the HDA codec * @nid: NID to parse diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 3be4e1040036..db310e9c53ff 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -439,8 +439,7 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, unsigned int verb, unsigned int parm); 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) +int snd_hda_param_read(struct hda_codec *codec, hda_nid_t nid, int parm); int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *start_id); int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
This patch converts the amp access functions to the regmap helpers. The amp values were formerly cached in the own hash table. Now it's dropped by the regmap's cache.
The only tricky conversion is snd_hda_codec_amp_init(). This function shouldn't do anything if the amp was already initialized. For achieving this behavior, a value is read once at first temporarily in the cache-only mode. Only if it returns an error, i.e. the item still doesn't exist in the cache, it proceeds to the update.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_codec.c | 214 +++++------------------------------------- sound/pci/hda/hda_local.h | 12 +-- sound/pci/hda/patch_hdmi.c | 2 +- sound/pci/hda/patch_realtek.c | 4 +- 4 files changed, 33 insertions(+), 199 deletions(-)
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 3a209e8b6130..3e6976b52231 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1793,139 +1793,6 @@ int snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid, } 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; -} - -/** - * 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); -} -EXPORT_SYMBOL_GPL(snd_hda_codec_amp_update); - /** * snd_hda_codec_amp_stereo - update the AMP stereo values * @codec: HD-audio codec @@ -1969,8 +1836,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->regmap) + return -EINVAL; + regcache_cache_only(codec->regmap, true); + orig = snd_hda_regmap_amp_get(codec, nid, ch, dir, idx); + regcache_cache_only(codec->regmap, false); + if (orig >= 0) + return 0; + return snd_hda_regmap_amp_update(codec, nid, ch, dir, idx, mask, val); } EXPORT_SYMBOL_GPL(snd_hda_codec_amp_init);
@@ -1999,49 +1874,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) { @@ -2112,8 +1944,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_regmap_amp_update(codec, nid, ch, dir, idx, + HDA_AMP_VOLMASK, val); }
/** @@ -2832,17 +2664,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_regmap_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_regmap_amp_update(codec, nid, 1, dir, idx, + HDA_AMP_MUTE, + *valp ? 0 : HDA_AMP_MUTE); hda_call_check_power_status(codec, nid); return change; } @@ -3703,7 +3533,8 @@ EXPORT_SYMBOL_GPL(snd_hda_sequence_write_cache); */ void snd_hda_codec_flush_cache(struct hda_codec *codec) { - snd_hda_codec_resume_amp(codec); + if (codec->regmap) + regcache_sync(codec->regmap); snd_hda_codec_resume_cache(codec); } EXPORT_SYMBOL_GPL(snd_hda_codec_flush_cache); @@ -3951,6 +3782,8 @@ static void hda_call_codec_resume(struct hda_codec *codec) atomic_inc(&codec->in_pm);
trace_hda_power_up(codec); + if (codec->regmap) + regcache_mark_dirty(codec->regmap); hda_mark_cmd_cache_dirty(codec);
codec->power_jiffies = jiffies; @@ -3964,7 +3797,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); + if (codec->regmap) + regcache_sync(codec->regmap); snd_hda_codec_resume_cache(codec); }
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 8588813163e3..be0654173d45 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -23,6 +23,8 @@ #ifndef __SOUND_HDA_LOCAL_H #define __SOUND_HDA_LOCAL_H
+#include "hda_regmap.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. @@ -127,18 +129,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_hda_regmap_amp_get(codec, nid, ch, dir, idx) +#define snd_hda_codec_amp_update(codec, nid, ch, dir, idx, mask, val) \ + snd_hda_regmap_amp_update(codec, 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, diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index f1812aabd63e..a930408d2bb8 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -2220,7 +2220,7 @@ static int generic_hdmi_resume(struct hda_codec *codec) int pin_idx;
codec->patch_ops.init(codec); - snd_hda_codec_resume_amp(codec); + regcache_sync(codec->regmap); snd_hda_codec_resume_cache(codec);
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 70808f92276a..3b495ec41e33 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -799,7 +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); + regcache_sync(codec->regmap); snd_hda_codec_resume_cache(codec); hda_call_check_power_status(codec, 0x01); return 0; @@ -3099,7 +3099,7 @@ static int alc269_resume(struct hda_codec *codec) msleep(200); }
- snd_hda_codec_resume_amp(codec); + regcache_sync(codec->regmap); snd_hda_codec_resume_cache(codec); hda_call_check_power_status(codec, 0x01);
The amp hash table was used for recording the cached reads of some capability values like pin caps or amp caps. Now all these are moved to regmap as well.
One addition to the regmap helper is codec->caps_overwriting flag. This is set in override_parameters(), and the regmap helper accepts any register while this flag is set, so that it can overwrite even the read-only verb like AC_VERB_PARAMETERS. The flag is cleared immediately in override_parameters(), as it's a once-off flag.
Along with these changes, the no longer needed amp hash and relevant fields are removed from hda_codec struct now.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_codec.c | 133 ++++++++------------------------------------- sound/pci/hda/hda_codec.h | 8 +-- sound/pci/hda/hda_local.h | 19 ++++++- sound/pci/hda/hda_regmap.c | 8 +++ 4 files changed, 51 insertions(+), 117 deletions(-)
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 3e6976b52231..8781632dff96 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1158,7 +1158,6 @@ static void snd_hda_codec_free(struct hda_codec *codec) codec->bus->caddr_tbl[codec->addr] = NULL; clear_bit(codec->addr, &codec->bus->codec_powered); snd_hda_sysfs_clear(codec); - free_hda_cache(&codec->amp_cache); free_hda_cache(&codec->cmd_cache); kfree(codec->vendor_name); kfree(codec->chip_name); @@ -1262,7 +1261,6 @@ int snd_hda_codec_new(struct hda_bus *bus, 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); @@ -1630,65 +1628,20 @@ static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache, 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) +/* override the parameter */ +static int override_parameter(struct hda_codec *codec, hda_nid_t nid, + unsigned int parm, unsigned int val) { - 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; + unsigned int verb = (AC_VERB_PARAMETERS << 8) | (nid << 20) | parm; + int err;
- mutex_lock(&codec->hash_mutex); - info = get_alloc_amp_hash(codec, key); - if (!info) { - mutex_unlock(&codec->hash_mutex); + if (!codec->regmap) return -EINVAL; - } - info->amp_caps = val; - info->head.val |= INFO_AMP_CAPS; - mutex_unlock(&codec->hash_mutex); - return 0; -} - -/* query the value from the caps hash; if not found, fetch the current - * value from the given function and store in the hash - */ -static unsigned int -query_caps_hash(struct hda_codec *codec, hda_nid_t nid, int dir, u32 key, - unsigned int (*func)(struct hda_codec *, hda_nid_t, int)) -{ - struct hda_amp_info *info; - unsigned int val;
- mutex_lock(&codec->hash_mutex); - info = get_alloc_amp_hash(codec, key); - if (!info) { - mutex_unlock(&codec->hash_mutex); - return 0; - } - if (!(info->head.val & INFO_AMP_CAPS)) { - mutex_unlock(&codec->hash_mutex); /* for reentrance */ - val = func(codec, nid, dir); - write_caps_hash(codec, key, val); - } else { - val = info->amp_caps; - mutex_unlock(&codec->hash_mutex); - } - return val; -} - -static unsigned int read_amp_cap(struct hda_codec *codec, hda_nid_t nid, - int direction) -{ - if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD)) - nid = codec->afg; - return snd_hda_param_read(codec, nid, - direction == HDA_OUTPUT ? - AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP); + codec->caps_overwriting = true; + err = snd_hda_reg_raw_write(codec, verb, val); + codec->caps_overwriting = false; + return err; }
/** @@ -1705,9 +1658,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->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);
@@ -1748,33 +1703,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); -} + unsigned int parm;
-/** - * 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); + 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 override_parameter(codec, nid, parm, caps); } -EXPORT_SYMBOL_GPL(snd_hda_query_pin_caps); +EXPORT_SYMBOL_GPL(snd_hda_override_amp_caps);
/** * snd_hda_override_pin_caps - Override the pin capabilities @@ -1789,7 +1725,7 @@ EXPORT_SYMBOL_GPL(snd_hda_query_pin_caps); 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); + return override_parameter(codec, nid, AC_PAR_PIN_CAP, caps); } EXPORT_SYMBOL_GPL(snd_hda_override_pin_caps);
@@ -2303,9 +2239,7 @@ int snd_hda_codec_reset(struct hda_codec *codec) snd_hda_jack_tbl_clear(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); @@ -3767,11 +3701,6 @@ static void hda_mark_cmd_cache_dirty(struct hda_codec *codec) 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; - } }
/* @@ -4049,8 +3978,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->afg && @@ -4063,14 +3991,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) @@ -4080,12 +4001,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 diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index db310e9c53ff..953625c85ee4 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -212,12 +212,6 @@ struct hda_cache_head { 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 */ @@ -319,7 +313,6 @@ 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 */ @@ -405,6 +398,7 @@ struct hda_codec {
/* regmap */ struct regmap *regmap; + bool caps_overwriting; /* caps overwrite being in process */ };
#define dev_to_hda_codec(_dev) container_of(_dev, struct hda_codec, dev) diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index be0654173d45..0da5796b4690 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -554,7 +554,24 @@ 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); +/** + * 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); + +} + int snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid, unsigned int caps); bool snd_hda_check_amp_caps(struct hda_codec *codec, hda_nid_t nid, diff --git a/sound/pci/hda/hda_regmap.c b/sound/pci/hda/hda_regmap.c index 715d770901b5..8f12c23f3489 100644 --- a/sound/pci/hda/hda_regmap.c +++ b/sound/pci/hda/hda_regmap.c @@ -33,8 +33,12 @@
static bool hda_writeable_reg(struct device *dev, unsigned int reg) { + struct hda_codec *codec = dev_to_hda_codec(dev); unsigned int verb = get_verb(reg);
+ if (codec->caps_overwriting) + return true; + switch (verb & 0xf00) { case AC_VERB_GET_STREAM_FORMAT: case AC_VERB_GET_AMP_GAIN_MUTE: @@ -70,8 +74,12 @@ static bool hda_writeable_reg(struct device *dev, unsigned int reg)
static bool hda_readable_reg(struct device *dev, unsigned int reg) { + struct hda_codec *codec = dev_to_hda_codec(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:
Codecs may have own vendor-specific verbs, and we need to allow each driver to give such verbs for cached accesses. Here a verb can be put into a single array and looked through it at readable and writeable callbacks.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_codec.h | 1 + sound/pci/hda/hda_regmap.c | 20 ++++++++++++++++++++ sound/pci/hda/hda_regmap.h | 2 ++ 3 files changed, 23 insertions(+)
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 953625c85ee4..8cf7b2870dd1 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -398,6 +398,7 @@ struct hda_codec {
/* regmap */ struct regmap *regmap; + struct snd_array vendor_verbs; bool caps_overwriting; /* caps overwrite being in process */ };
diff --git a/sound/pci/hda/hda_regmap.c b/sound/pci/hda/hda_regmap.c index 8f12c23f3489..38934c06a813 100644 --- a/sound/pci/hda/hda_regmap.c +++ b/sound/pci/hda/hda_regmap.c @@ -35,10 +35,17 @@ static bool hda_writeable_reg(struct device *dev, unsigned int reg) { struct hda_codec *codec = dev_to_hda_codec(dev); unsigned int verb = get_verb(reg); + int i;
if (codec->caps_overwriting) return true;
+ for (i = 0; i < codec->vendor_verbs.used; i++) { + unsigned int *v = snd_array_elem(&codec->vendor_verbs, i); + if (verb == *v) + return true; + } + switch (verb & 0xf00) { case AC_VERB_GET_STREAM_FORMAT: case AC_VERB_GET_AMP_GAIN_MUTE: @@ -178,6 +185,7 @@ int snd_hda_regmap_init(struct hda_codec *codec) if (IS_ERR(regmap)) return PTR_ERR(regmap); codec->regmap = regmap; + snd_array_init(&codec->vendor_verbs, sizeof(unsigned int), 8); return 0; }
@@ -187,7 +195,19 @@ void snd_hda_regmap_exit(struct hda_codec *codec) regmap_exit(codec->regmap); codec->regmap = NULL; } + snd_array_free(&codec->vendor_verbs); +} + +int snd_hda_regmap_add_vendor_verb(struct hda_codec *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_hda_regmap_add_vendor_verb);
/* * helper functions diff --git a/sound/pci/hda/hda_regmap.h b/sound/pci/hda/hda_regmap.h index 7d4b8be58975..562c58c4cbc1 100644 --- a/sound/pci/hda/hda_regmap.h +++ b/sound/pci/hda/hda_regmap.h @@ -13,6 +13,8 @@ int snd_hda_regmap_init(struct hda_codec *codec); void snd_hda_regmap_exit(struct hda_codec *codec);
+int snd_hda_regmap_add_vendor_verb(struct hda_codec *codec, unsigned int verb); + int snd_hda_regmap_write(struct hda_codec *codec, hda_nid_t nid, unsigned int verb, unsigned int val); int snd_hda_regmap_update_bits(struct hda_codec *codec, hda_nid_t nid,
Like the previous patches, this patch converts also to the regmap, at this time, the cached verb writes are the target. But this conversion needs a bit more caution than before.
- In the old code, we just record any verbs as is, and restore them at resume. For the regmap scheme, this doesn't work, since a few verbs like AMP or DIGI_CONVERT are asymmetrical. Such verbs are converted either to the dedicated function (snd_hda_regmap_xxx_amp()) or changed to the unified verb.
- Some verbs have to be declared as vendor-specific ones before accessing via regmap.
Also, the minor optimization with codec->cached_write flag is dropped in a few places, as this would confuse the operation. Further optimizations will be brought in the later patches, if any.
This conversion ends up with a drop of significant amount of codes, mostly the helper codes that are no longer used.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_beep.c | 3 +- sound/pci/hda/hda_codec.c | 276 +++++------------------------------------ sound/pci/hda/hda_codec.h | 27 ---- sound/pci/hda/hda_generic.c | 32 ++--- sound/pci/hda/hda_jack.c | 6 +- sound/pci/hda/patch_analog.c | 30 ++--- sound/pci/hda/patch_conexant.c | 12 +- sound/pci/hda/patch_hdmi.c | 22 ++-- sound/pci/hda/patch_realtek.c | 7 +- sound/pci/hda/patch_si3054.c | 5 +- sound/pci/hda/patch_sigmatel.c | 38 +++--- 11 files changed, 98 insertions(+), 360 deletions(-)
diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index e98438e95e79..45a3d7ae6f56 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -226,8 +226,7 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) snprintf(beep->phys, sizeof(beep->phys), "card%d/codec#%d/beep0", codec->bus->card->number, codec->addr); /* enable linear scale */ - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_DIGI_CONVERT_2, 0x01); + snd_hda_regmap_write(codec, nid, AC_VERB_SET_DIGI_CONVERT_2, 0x01);
beep->nid = nid; beep->codec = codec; diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 8781632dff96..26d45268647d 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1092,10 +1092,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) { @@ -1158,7 +1154,6 @@ static void snd_hda_codec_free(struct hda_codec *codec) codec->bus->caddr_tbl[codec->addr] = NULL; clear_bit(codec->addr, &codec->bus->codec_powered); snd_hda_sysfs_clear(codec); - free_hda_cache(&codec->cmd_cache); kfree(codec->vendor_name); kfree(codec->chip_name); kfree(codec->modelname); @@ -1260,8 +1255,6 @@ int snd_hda_codec_new(struct hda_bus *bus, codec->addr = codec_addr; mutex_init(&codec->spdif_mutex); mutex_init(&codec->control_mutex); - mutex_init(&codec->hash_mutex); - init_hda_cache(&codec->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); @@ -1568,66 +1561,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; -} - /* override the parameter */ static int override_parameter(struct hda_codec *codec, hda_nid_t nid, unsigned int parm, unsigned int val) @@ -2239,8 +2172,6 @@ int snd_hda_codec_reset(struct hda_codec *codec) snd_hda_jack_tbl_clear(codec); codec->proc_widget_hook = NULL; codec->spec = NULL; - free_hda_cache(&codec->cmd_cache); - 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); @@ -2908,25 +2839,36 @@ 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_hda_regmap_update_bits(codec, 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_hda_regmap_update_bits(codec, 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, @@ -3059,6 +3001,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; @@ -3099,8 +3042,8 @@ 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_hda_regmap_read(codec, cvt_nid, AC_VERB_GET_DIGI_CONVERT_1, &val); + spdif->ctls = val; spdif->status = convert_to_spdif_status(spdif->ctls); return 0; } @@ -3244,8 +3187,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_hda_regmap_write(codec, nid, + AC_VERB_SET_DIGI_CONVERT_1, val); } mutex_unlock(&codec->spdif_mutex); return change; @@ -3256,10 +3199,10 @@ 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_hda_regmap_read(codec, 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; @@ -3325,154 +3268,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->cmd_mutex); - c = get_alloc_hash(&codec->cmd_cache, key); - if (c) { - c->val = parm; - c->dirty = cache_only; - } - mutex_unlock(&codec->bus->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->cmd_mutex); - c = get_hash(&codec->cmd_cache, key); - if (c && c->val == parm) { - mutex_unlock(&codec->bus->cmd_mutex); - return 0; - } - mutex_unlock(&codec->bus->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) -{ - if (codec->regmap) - regcache_sync(codec->regmap); - 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 @@ -3692,17 +3487,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; - } -} - /* * kick up codec; used both from PM and power-save */ @@ -3713,7 +3497,6 @@ static void hda_call_codec_resume(struct hda_codec *codec) trace_hda_power_up(codec); if (codec->regmap) regcache_mark_dirty(codec->regmap); - hda_mark_cmd_cache_dirty(codec);
codec->power_jiffies = jiffies;
@@ -3728,7 +3511,6 @@ static void hda_call_codec_resume(struct hda_codec *codec) codec->patch_ops.init(codec); if (codec->regmap) regcache_sync(codec->regmap); - snd_hda_codec_resume_cache(codec); }
if (codec->jackpoll_interval) @@ -4660,8 +4442,8 @@ int snd_hda_input_mux_put(struct hda_codec *codec, 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); + snd_hda_regmap_write(codec, nid, AC_VERB_SET_CONNECT_SEL, + imux->items[idx].index); *cur_val = idx; return 1; } @@ -5103,8 +4885,8 @@ int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, val = snd_hda_correct_pin_ctl(codec, pin, val); snd_hda_codec_set_pin_target(codec, pin, val); if (cached) - return snd_hda_codec_update_cache(codec, pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, val); + return snd_hda_regmap_write(codec, pin, + AC_VERB_SET_PIN_WIDGET_CONTROL, val); else return snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, val); diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 8cf7b2870dd1..634d07c2634b 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -204,19 +204,6 @@ struct hda_codec_ops { void (*reboot_notify)(struct hda_codec *codec); };
-/* 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_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, @@ -313,13 +300,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 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 */ @@ -470,17 +454,6 @@ void snd_hda_sequence_write(struct hda_codec *codec, /* unsolicited event */ int 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); - /* the struct for codec->pin_configs */ struct hda_pincfg { hda_nid_t nid; diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 947d1a50f384..33de31c83d6e 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -203,8 +203,7 @@ static void parse_user_hints(struct hda_codec *codec) */
#define update_pin_ctl(codec, pin, val) \ - snd_hda_codec_update_cache(codec, pin, 0, \ - AC_VERB_SET_PIN_WIDGET_CONTROL, val) + snd_hda_regmap_write(codec, pin, AC_VERB_SET_PIN_WIDGET_CONTROL, val)
/* restore the pinctl based on the cached value */ static inline void restore_pin_ctl(struct hda_codec *codec, hda_nid_t pin) @@ -802,9 +801,9 @@ void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, AC_PWRST_D0); } if (enable && path->multi[i]) - snd_hda_codec_update_cache(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, - path->idx[i]); + snd_hda_regmap_write(codec, nid, + AC_VERB_SET_CONNECT_SEL, + path->idx[i]); if (has_amp_in(codec, path, i)) activate_amp_in(codec, path, i, enable, add_aamix); if (has_amp_out(codec, path, i)) @@ -855,9 +854,8 @@ static void set_pin_eapd(struct hda_codec *codec, hda_nid_t pin, bool enable) return; if (codec->inv_eapd) enable = !enable; - snd_hda_codec_update_cache(codec, pin, 0, - AC_VERB_SET_EAPD_BTLENABLE, - enable ? 0x02 : 0x00); + snd_hda_regmap_write(codec, pin, AC_VERB_SET_EAPD_BTLENABLE, + enable ? 0x02 : 0x00); }
/* re-initialize the path specified by the given path index */ @@ -3313,11 +3311,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]) @@ -3325,12 +3318,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; @@ -5401,8 +5391,8 @@ static void clear_unsol_on_unused_pins(struct hda_codec *codec) hda_nid_t nid = pin->nid; if (is_jack_detectable(codec, nid) && !snd_hda_jack_tbl_get(codec, nid)) - snd_hda_codec_update_cache(codec, nid, 0, - AC_VERB_SET_UNSOLICITED_ENABLE, 0); + snd_hda_regmap_write(codec, nid, + AC_VERB_SET_UNSOLICITED_ENABLE, 0); } }
@@ -5421,8 +5411,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); @@ -5436,7 +5424,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->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_jack.c b/sound/pci/hda/hda_jack.c index e664307617bd..2e933783c83f 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -269,9 +269,9 @@ snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid, 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); + err = snd_hda_regmap_write(codec, nid, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | jack->tag); if (err < 0) return ERR_PTR(err); return callback; diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index af4c7be86c27..628f479abb4f 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -148,9 +148,9 @@ static void ad_vmaster_eapd_hook(void *private_data, int enabled) return; if (codec->inv_eapd) enabled = !enabled; - snd_hda_codec_update_cache(codec, spec->eapd_nid, 0, - AC_VERB_SET_EAPD_BTLENABLE, - enabled ? 0x02 : 0x00); + snd_hda_regmap_write(codec, spec->eapd_nid, + AC_VERB_SET_EAPD_BTLENABLE, + enabled ? 0x02 : 0x00); }
/* @@ -501,8 +501,7 @@ static int ad1983_auto_smux_enum_put(struct snd_kcontrol *kcontrol, if (spec->cur_smux == val) return 0; spec->cur_smux = val; - snd_hda_codec_write_cache(codec, dig_out, 0, - AC_VERB_SET_CONNECT_SEL, val); + snd_hda_regmap_write(codec, dig_out, AC_VERB_SET_CONNECT_SEL, val); return 1; }
@@ -777,7 +776,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 +784,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; }
@@ -995,27 +991,25 @@ static void ad1884_vmaster_hp_gpio_hook(void *private_data, int enabled)
if (spec->eapd_nid) ad_vmaster_eapd_hook(private_data, enabled); - snd_hda_codec_update_cache(codec, 0x01, 0, - AC_VERB_SET_GPIO_DATA, - enabled ? 0x00 : 0x02); + snd_hda_regmap_write(codec, 0x01, AC_VERB_SET_GPIO_DATA, + enabled ? 0x00 : 0x02); }
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_regmap_write(codec, 0x01, + AC_VERB_SET_GPIO_MASK, 0x02); + snd_hda_regmap_write(codec, 0x01, + AC_VERB_SET_GPIO_DIRECTION, 0x02); + snd_hda_regmap_write(codec, 0x01, + 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 15a0a7d38c35..382b71cd28dc 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -278,10 +278,10 @@ static void cxt_update_headset_mode(struct hda_codec *codec) }
if (mic_mode) { - snd_hda_codec_write_cache(codec, 0x1c, 0, 0x410, 0x7c); /* enable merged mode for analog int-mic */ + snd_hda_regmap_write(codec, 0x1c, 0x410, 0x7c); /* enable merged mode for analog int-mic */ spec->gen.hp_jack_present = false; } else { - snd_hda_codec_write_cache(codec, 0x1c, 0, 0x410, 0x54); /* disable merged mode for analog int-mic */ + snd_hda_regmap_write(codec, 0x1c, 0x410, 0x54); /* disable merged mode for analog int-mic */ spec->gen.hp_jack_present = snd_hda_jack_detect(codec, spec->gen.autocfg.hp_pins[0]); }
@@ -303,6 +303,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_hda_regmap_add_vendor_verb(codec, 0x410); break; case HDA_FIXUP_ACT_PROBE: spec->gen.cap_sync_hook = cxt_update_headset_mode_hook; @@ -324,8 +325,7 @@ static void cxt_fixup_headphone_mic(struct hda_codec *codec, * control. */
#define update_mic_pin(codec, nid, val) \ - snd_hda_codec_update_cache(codec, nid, 0, \ - AC_VERB_SET_PIN_WIDGET_CONTROL, val) + snd_hda_regmap_write(codec, nid, AC_VERB_SET_PIN_WIDGET_CONTROL, val)
static const struct hda_input_mux olpc_xo_dc_bias = { .num_items = 3, @@ -410,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 a930408d2bb8..78f8c2ed86f9 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1375,9 +1375,8 @@ static void intel_verify_pin_cvt_connect(struct hda_codec *codec, curr = snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_CONNECT_SEL, 0); if (curr != mux_idx) - snd_hda_codec_write_cache(codec, pin_nid, 0, - AC_VERB_SET_CONNECT_SEL, - mux_idx); + snd_hda_regmap_write(codec, pin_nid, + AC_VERB_SET_CONNECT_SEL, mux_idx); }
/* Intel HDMI workaround to fix audio routing issue: @@ -1423,9 +1422,9 @@ static void intel_not_share_assigned_cvt(struct hda_codec *codec, codec_dbg(codec, "choose cvt %d for pin nid %d\n", cvt_idx, nid); - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, - cvt_idx); + snd_hda_regmap_write(codec, nid, + AC_VERB_SET_CONNECT_SEL, + cvt_idx); break; } } @@ -1464,9 +1463,8 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, per_pin->cvt_nid = per_cvt->cvt_nid; hinfo->nid = per_cvt->cvt_nid;
- snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0, - AC_VERB_SET_CONNECT_SEL, - mux_idx); + snd_hda_regmap_write(codec, per_pin->pin_nid, + AC_VERB_SET_CONNECT_SEL, mux_idx);
/* configure unused pins to choose other converters */ if (is_haswell_plus(codec) || is_valleyview_plus(codec)) @@ -2221,7 +2219,6 @@ static int generic_hdmi_resume(struct hda_codec *codec)
codec->patch_ops.init(codec); regcache_sync(codec->regmap); - snd_hda_codec_resume_cache(codec);
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); @@ -2308,8 +2305,9 @@ static void intel_haswell_fixup_enable_dp12(struct hda_codec *codec)
/* enable DP1.2 mode */ vendor_param |= INTEL_EN_DP12; - snd_hda_codec_write_cache(codec, INTEL_VENDOR_NID, 0, - INTEL_SET_VENDOR_VERB, vendor_param); + snd_hda_regmap_add_vendor_verb(codec, INTEL_VENDOR_NID); + snd_hda_regmap_write(codec, INTEL_VENDOR_NID, + INTEL_SET_VENDOR_VERB, vendor_param); }
/* Haswell needs to re-issue the vendor-specific verbs before turning to D0. diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 3b495ec41e33..f035ed376c23 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -800,7 +800,6 @@ static int alc_resume(struct hda_codec *codec) msleep(150); /* to avoid pop noise */ codec->patch_ops.init(codec); regcache_sync(codec->regmap); - snd_hda_codec_resume_cache(codec); hda_call_check_power_status(codec, 0x01); return 0; } @@ -3100,7 +3099,6 @@ static int alc269_resume(struct hda_codec *codec) }
regcache_sync(codec->regmap); - snd_hda_codec_resume_cache(codec); hda_call_check_power_status(codec, 0x01);
/* on some machine, the BIOS will clear the codec gpio data when enter @@ -3520,8 +3518,9 @@ static void alc280_fixup_hp_gpio2_mic_hotkey(struct hda_codec *codec, }
snd_hda_add_verbs(codec, gpio_init); - snd_hda_codec_write_cache(codec, codec->afg, 0, - AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x04); + snd_hda_regmap_write(codec, codec->afg, + AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, + 0x04); snd_hda_jack_detect_enable_callback(codec, codec->afg, gpio2_mic_hotkey_event);
diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c index 38a477333321..3179479d1f43 100644 --- a/sound/pci/hda/patch_si3054.c +++ b/sound/pci/hda/patch_si3054.c @@ -78,7 +78,7 @@ #define GET_REG(codec,reg) (snd_hda_codec_read(codec,reg,0,SI3054_VERB_READ_NODE,0)) #define SET_REG(codec,reg,val) (snd_hda_codec_write(codec,reg,0,SI3054_VERB_WRITE_NODE,val)) #define SET_REG_CACHE(codec,reg,val) \ - snd_hda_codec_write_cache(codec,reg,0,SI3054_VERB_WRITE_NODE,val) + snd_hda_regmap_write(codec, reg, SI3054_VERB_WRITE_NODE, val)
struct si3054_spec { @@ -223,6 +223,9 @@ static int si3054_init(struct hda_codec *codec) unsigned wait_count; u16 val;
+ if (snd_hda_regmap_add_vendor_verb(codec, 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->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 2956a6ba6bf0..124e40256e6c 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -634,8 +634,8 @@ static int stac_aloopback_put(struct snd_kcontrol *kcontrol, dac_mode &= ~idx_val; }
- snd_hda_codec_write_cache(codec, codec->afg, 0, - kcontrol->private_value >> 16, dac_mode); + snd_hda_regmap_write(codec, codec->afg, + kcontrol->private_value >> 16, dac_mode);
return 1; } @@ -1048,12 +1048,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[] = { @@ -3031,8 +3028,8 @@ static void stac92hd71bxx_fixup_hp_m4(struct hda_codec *codec, return;
/* Enable VREF power saving on GPIO1 detect */ - snd_hda_codec_write_cache(codec, codec->afg, 0, - AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02); + snd_hda_regmap_write(codec, codec->afg, + AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02); jack = snd_hda_jack_detect_enable_callback(codec, codec->afg, stac_vref_event); if (!IS_ERR(jack)) @@ -4053,8 +4050,9 @@ static void stac9205_fixup_dell_m43(struct hda_codec *codec, snd_hda_apply_pincfgs(codec, dell_9205_m43_pin_configs);
/* Enable unsol response for GPIO4/Dock HP connection */ - snd_hda_codec_write_cache(codec, codec->afg, 0, - AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10); + snd_hda_regmap_write(codec, codec->afg, + AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, + 0x10); jack = snd_hda_jack_detect_enable_callback(codec, codec->afg, stac_vref_event); if (!IS_ERR(jack)) @@ -4261,6 +4259,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_hda_regmap_add_vendor_verb(codec, wr_verb)) + return -ENOMEM; if (!snd_hda_gen_add_kctl(&spec->gen, NULL, spec->aloopback_ctl)) return -ENOMEM; } @@ -4674,7 +4676,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); @@ -4698,7 +4700,7 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) switch (codec->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->revision_id & 0xf) == 0 || @@ -4706,7 +4708,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; @@ -4720,8 +4722,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;
For reducing the number of verbs performed during the initialization and the resume of the generic parser, this patch (re-)introduces the mechanism to cache everything in init and sync later.
However, it became a bit tricky in the end: we can't use regcache's cache_only flag straightforwardly here because we do want the actual hardware access for reads if needed, but forbids only the writes. So, instead, the regmap reg_write callback checks the own flag (reusing the existing codec->cached_write) and skips the actual access. This works by assumption of regmap dumping the whole registers via regcache_sync() at a later point.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 4 ++++ sound/pci/hda/hda_regmap.c | 6 ++++++ 2 files changed, 10 insertions(+)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 33de31c83d6e..bcaed93f8795 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -5411,6 +5411,8 @@ 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); @@ -5424,6 +5426,8 @@ int snd_hda_gen_init(struct hda_codec *codec) /* call init functions of standard auto-mute helpers */ update_automute_all(codec);
+ codec->cached_write = 0; + regcache_sync(codec->regmap);
if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook) diff --git a/sound/pci/hda/hda_regmap.c b/sound/pci/hda/hda_regmap.c index 38934c06a813..79da06da286d 100644 --- a/sound/pci/hda/hda_regmap.c +++ b/sound/pci/hda/hda_regmap.c @@ -116,6 +116,12 @@ static int hda_reg_write(void *context, unsigned int reg, unsigned int val) unsigned int verb; int i, bytes;
+ /* skip h/w write when cached_write flag is set, assuming that + * regcache_sync() will dump the all registers at a later point + */ + if (codec->cached_write) + return 0; + if (!codec_is_running(codec)) return 0; /* skip the h/w write, it'll be synced later */
On 02/27/2015 10:28 PM, Takashi Iwai wrote:
For reducing the number of verbs performed during the initialization and the resume of the generic parser, this patch (re-)introduces the mechanism to cache everything in init and sync later.
However, it became a bit tricky in the end: we can't use regcache's cache_only flag straightforwardly here because we do want the actual hardware access for reads if needed, but forbids only the writes. So, instead, the regmap reg_write callback checks the own flag (reusing the existing codec->cached_write) and skips the actual access. This works by assumption of regmap dumping the whole registers via regcache_sync() at a later point.
Signed-off-by: Takashi Iwai tiwai@suse.de
sound/pci/hda/hda_generic.c | 4 ++++ sound/pci/hda/hda_regmap.c | 6 ++++++ 2 files changed, 10 insertions(+)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 33de31c83d6e..bcaed93f8795 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -5411,6 +5411,8 @@ int snd_hda_gen_init(struct hda_codec *codec)
snd_hda_apply_verbs(codec);
- codec->cached_write = 1;
How about using regcache_cache_only(), this handles this internally in regmap and will also make sure that the cache gets marked as dirty so that a regcache_sync() will properly write out the dirty registers.
init_multi_out(codec); init_extra_out(codec); init_multi_io(codec);
[...]
At Sat, 28 Feb 2015 10:51:29 +0100, Lars-Peter Clausen wrote:
On 02/27/2015 10:28 PM, Takashi Iwai wrote:
For reducing the number of verbs performed during the initialization and the resume of the generic parser, this patch (re-)introduces the mechanism to cache everything in init and sync later.
However, it became a bit tricky in the end: we can't use regcache's cache_only flag straightforwardly here because we do want the actual hardware access for reads if needed, but forbids only the writes. So, instead, the regmap reg_write callback checks the own flag (reusing the existing codec->cached_write) and skips the actual access. This works by assumption of regmap dumping the whole registers via regcache_sync() at a later point.
Signed-off-by: Takashi Iwai tiwai@suse.de
sound/pci/hda/hda_generic.c | 4 ++++ sound/pci/hda/hda_regmap.c | 6 ++++++ 2 files changed, 10 insertions(+)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 33de31c83d6e..bcaed93f8795 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -5411,6 +5411,8 @@ int snd_hda_gen_init(struct hda_codec *codec)
snd_hda_apply_verbs(codec);
- codec->cached_write = 1;
How about using regcache_cache_only(), this handles this internally in regmap and will also make sure that the cache gets marked as dirty so that a regcache_sync() will properly write out the dirty registers.
I tried this as a first option, too, but it doesn't work because we still want reading the value from hardware if not read beforehand. regcache_cache_only() prevents also from reading a h/w value. It's the point I mentioned this being tricky.
thanks,
Takashi
On 02/28/2015 11:01 AM, Takashi Iwai wrote:
At Sat, 28 Feb 2015 10:51:29 +0100, Lars-Peter Clausen wrote:
On 02/27/2015 10:28 PM, Takashi Iwai wrote:
For reducing the number of verbs performed during the initialization and the resume of the generic parser, this patch (re-)introduces the mechanism to cache everything in init and sync later.
However, it became a bit tricky in the end: we can't use regcache's cache_only flag straightforwardly here because we do want the actual hardware access for reads if needed, but forbids only the writes. So, instead, the regmap reg_write callback checks the own flag (reusing the existing codec->cached_write) and skips the actual access. This works by assumption of regmap dumping the whole registers via regcache_sync() at a later point.
Signed-off-by: Takashi Iwai tiwai@suse.de
sound/pci/hda/hda_generic.c | 4 ++++ sound/pci/hda/hda_regmap.c | 6 ++++++ 2 files changed, 10 insertions(+)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 33de31c83d6e..bcaed93f8795 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -5411,6 +5411,8 @@ int snd_hda_gen_init(struct hda_codec *codec)
snd_hda_apply_verbs(codec);
- codec->cached_write = 1;
How about using regcache_cache_only(), this handles this internally in regmap and will also make sure that the cache gets marked as dirty so that a regcache_sync() will properly write out the dirty registers.
I tried this as a first option, too, but it doesn't work because we still want reading the value from hardware if not read beforehand. regcache_cache_only() prevents also from reading a h/w value. It's the point I mentioned this being tricky.
Hm, right, I skipped right past that. But I think it will still be better to implement this write-only caching in the regmap layer.
- Lars
At Sat, 28 Feb 2015 11:27:42 +0100, Lars-Peter Clausen wrote:
On 02/28/2015 11:01 AM, Takashi Iwai wrote:
At Sat, 28 Feb 2015 10:51:29 +0100, Lars-Peter Clausen wrote:
On 02/27/2015 10:28 PM, Takashi Iwai wrote:
For reducing the number of verbs performed during the initialization and the resume of the generic parser, this patch (re-)introduces the mechanism to cache everything in init and sync later.
However, it became a bit tricky in the end: we can't use regcache's cache_only flag straightforwardly here because we do want the actual hardware access for reads if needed, but forbids only the writes. So, instead, the regmap reg_write callback checks the own flag (reusing the existing codec->cached_write) and skips the actual access. This works by assumption of regmap dumping the whole registers via regcache_sync() at a later point.
Signed-off-by: Takashi Iwai tiwai@suse.de
sound/pci/hda/hda_generic.c | 4 ++++ sound/pci/hda/hda_regmap.c | 6 ++++++ 2 files changed, 10 insertions(+)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 33de31c83d6e..bcaed93f8795 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -5411,6 +5411,8 @@ int snd_hda_gen_init(struct hda_codec *codec)
snd_hda_apply_verbs(codec);
- codec->cached_write = 1;
How about using regcache_cache_only(), this handles this internally in regmap and will also make sure that the cache gets marked as dirty so that a regcache_sync() will properly write out the dirty registers.
I tried this as a first option, too, but it doesn't work because we still want reading the value from hardware if not read beforehand. regcache_cache_only() prevents also from reading a h/w value. It's the point I mentioned this being tricky.
Hm, right, I skipped right past that. But I think it will still be better to implement this write-only caching in the regmap layer.
Yeah, that would make sense, I'm not against it :)
Takashi
participants (2)
-
Lars-Peter Clausen
-
Takashi Iwai