diff options
author | Takashi Iwai <tiwai@suse.de> | 2020-01-09 10:01:04 +0100 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2020-01-13 13:40:41 +0100 |
commit | 1a462be52f4505a2719631fb5aa7bfdbd37bfd8d (patch) | |
tree | d93981fbfa1a8514aedf01a4c4a85f2ed99bae64 /sound/pci/hda/hda_codec.c | |
parent | 73ac9f5e5b43a5dbadb61f27dae7a971f7ec0d22 (diff) |
ALSA: hda: Manage concurrent reg access more properly
In the commit 8e85def5723e ("ALSA: hda: enable regmap internal
locking"), we re-enabled the regmap lock due to the reported
regression that showed the possible concurrent accesses. It was a
temporary workaround, and there are still a few opened races even
after the revert. In this patch, we cover those still opened windows
with a proper mutex lock and disable the regmap internal lock again.
First off, the patch introduces a new snd_hdac_device.regmap_lock
mutex that is applied for each snd_hdac_regmap_*() call, including
read, write and update helpers. The mutex is applied carefully so
that it won't block the self-power-up procedure in the helper
function. Also, this assures the protection for the accesses without
regmap, too.
The snd_hdac_regmap_update_raw() is refactored to use the standard
regmap_update_bits_check() function instead of the open-code. The
non-regmap case is still open-coded but it's an easy part. The all
read and write operations are in the single mutex protection, so it's
now race-free.
In addition, a couple of new helper functions are added:
snd_hdac_regmap_update_raw_once() and snd_hdac_regmap_sync(). Both
are called from HD-audio legacy driver. The former is to initialize
the given verb bits but only once when it's not initialized yet. Due
to this condition, the function invokes regcache_cache_only(), and
it's now performed inside the regmap_lock (formerly it was racy) too.
The latter function is for simply invoking regcache_sync() inside the
regmap_lock, which is called from the codec resume call path.
Along with that, the HD-audio codec driver code is slightly modified /
simplified to adapt those new functions.
And finally, snd_hdac_regmap_read_raw(), *_write_raw(), etc are
rewritten with the helper macro. It's just for simplification because
the code logic is identical among all those functions.
Tested-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Link: https://lore.kernel.org/r/20200109090104.26073-1-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda/hda_codec.c')
-rw-r--r-- | sound/pci/hda/hda_codec.c | 30 |
1 files changed, 16 insertions, 14 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 4377b2aba835..83aa4c1015d2 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1267,6 +1267,18 @@ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, } EXPORT_SYMBOL_GPL(snd_hda_override_amp_caps); +static unsigned int encode_amp(struct hda_codec *codec, hda_nid_t nid, + int ch, int dir, int idx) +{ + unsigned int cmd = snd_hdac_regmap_encode_amp(nid, ch, dir, idx); + + /* enable fake mute if no h/w mute but min=mute */ + if ((query_amp_caps(codec, nid, dir) & + (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) == AC_AMPCAP_MIN_MUTE) + cmd |= AC_AMP_FAKE_MUTE; + return cmd; +} + /** * snd_hda_codec_amp_update - update the AMP mono value * @codec: HD-audio codec @@ -1282,12 +1294,8 @@ EXPORT_SYMBOL_GPL(snd_hda_override_amp_caps); int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, int dir, int idx, int mask, int val) { - unsigned int cmd = snd_hdac_regmap_encode_amp(nid, ch, dir, idx); + unsigned int cmd = encode_amp(codec, nid, ch, dir, idx); - /* enable fake mute if no h/w mute but min=mute */ - if ((query_amp_caps(codec, nid, dir) & - (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) == AC_AMPCAP_MIN_MUTE) - cmd |= AC_AMP_FAKE_MUTE; return snd_hdac_regmap_update_raw(&codec->core, cmd, mask, val); } EXPORT_SYMBOL_GPL(snd_hda_codec_amp_update); @@ -1335,16 +1343,11 @@ 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) { - int orig; + unsigned int cmd = encode_amp(codec, nid, ch, dir, idx); 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); + return snd_hdac_regmap_update_raw_once(&codec->core, cmd, mask, val); } EXPORT_SYMBOL_GPL(snd_hda_codec_amp_init); @@ -2905,8 +2908,7 @@ static void hda_call_codec_resume(struct hda_codec *codec) else { if (codec->patch_ops.init) codec->patch_ops.init(codec); - if (codec->core.regmap) - regcache_sync(codec->core.regmap); + snd_hda_regmap_sync(codec); } if (codec->jackpoll_interval) |