Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

ASoC: cs42l43: Cache shutter IRQ control pointers

The microphone/speaker privacy shutter ALSA control handlers need to
call pm_runtime_resume, since the hardware needs to be powered up to
check the hardware state of the shutter. The IRQ handler for the
shutters also needs to notify the ALSA control to inform user-space
the shutters updated. However this leads to a mutex inversion,
between the sdw_dev_lock and the controls_rwsem.

To avoid this mutex inversion cache the kctl pointers before the IRQ
handler, which avoids the need to lookup the control and take the
controls_rwsem.

Suggested-by: Jaroslav Kysela <perex@perex.cz>
Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
Link: https://patch.msgid.link/20240802105734.2309788-5-ckeepax@opensource.cirrus.com
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Charles Keepax and committed by
Mark Brown
93afd028 4791c422

+58 -17
+56 -17
sound/soc/codecs/cs42l43.c
··· 7 7 8 8 #include <linux/bitops.h> 9 9 #include <linux/bits.h> 10 + #include <linux/build_bug.h> 10 11 #include <linux/clk.h> 11 12 #include <linux/device.h> 12 13 #include <linux/err.h> ··· 253 252 static irqreturn_t cs42l43_mic_shutter(int irq, void *data) 254 253 { 255 254 struct cs42l43_codec *priv = data; 256 - static const char * const controls[] = { 257 - "Decimator 1 Switch", 258 - "Decimator 2 Switch", 259 - "Decimator 3 Switch", 260 - "Decimator 4 Switch", 261 - }; 262 - int i, ret; 255 + struct snd_soc_component *component = priv->component; 256 + int i; 263 257 264 258 dev_dbg(priv->dev, "Microphone shutter changed\n"); 265 259 266 - if (!priv->component) 260 + if (!component) 267 261 return IRQ_NONE; 268 262 269 - for (i = 0; i < ARRAY_SIZE(controls); i++) { 270 - ret = snd_soc_component_notify_control(priv->component, 271 - controls[i]); 272 - if (ret) 263 + for (i = 1; i < ARRAY_SIZE(priv->kctl); i++) { 264 + if (!priv->kctl[i]) 273 265 return IRQ_NONE; 266 + 267 + snd_ctl_notify(component->card->snd_card, 268 + SNDRV_CTL_EVENT_MASK_VALUE, &priv->kctl[i]->id); 274 269 } 275 270 276 271 return IRQ_HANDLED; ··· 275 278 static irqreturn_t cs42l43_spk_shutter(int irq, void *data) 276 279 { 277 280 struct cs42l43_codec *priv = data; 278 - int ret; 281 + struct snd_soc_component *component = priv->component; 279 282 280 283 dev_dbg(priv->dev, "Speaker shutter changed\n"); 281 284 282 - if (!priv->component) 285 + if (!component) 283 286 return IRQ_NONE; 284 287 285 - ret = snd_soc_component_notify_control(priv->component, 286 - "Speaker Digital Switch"); 287 - if (ret) 288 + if (!priv->kctl[0]) 288 289 return IRQ_NONE; 290 + 291 + snd_ctl_notify(component->card->snd_card, 292 + SNDRV_CTL_EVENT_MASK_VALUE, &priv->kctl[0]->id); 289 293 290 294 return IRQ_HANDLED; 291 295 } ··· 588 590 return 0; 589 591 } 590 592 593 + static int cs42l43_dai_probe(struct snd_soc_dai *dai) 594 + { 595 + struct snd_soc_component *component = dai->component; 596 + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); 597 + static const char * const controls[] = { 598 + "Speaker Digital Switch", 599 + "Decimator 1 Switch", 600 + "Decimator 2 Switch", 601 + "Decimator 3 Switch", 602 + "Decimator 4 Switch", 603 + }; 604 + int i; 605 + 606 + static_assert(ARRAY_SIZE(controls) == ARRAY_SIZE(priv->kctl)); 607 + 608 + for (i = 0; i < ARRAY_SIZE(controls); i++) { 609 + if (priv->kctl[i]) 610 + continue; 611 + 612 + priv->kctl[i] = snd_soc_component_get_kcontrol(component, controls[i]); 613 + } 614 + 615 + return 0; 616 + } 617 + 618 + static int cs42l43_dai_remove(struct snd_soc_dai *dai) 619 + { 620 + struct snd_soc_component *component = dai->component; 621 + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); 622 + int i; 623 + 624 + for (i = 0; i < ARRAY_SIZE(priv->kctl); i++) 625 + priv->kctl[i] = NULL; 626 + 627 + return 0; 628 + } 629 + 591 630 static const struct snd_soc_dai_ops cs42l43_asp_ops = { 631 + .probe = cs42l43_dai_probe, 632 + .remove = cs42l43_dai_remove, 592 633 .startup = cs42l43_startup, 593 634 .hw_params = cs42l43_asp_hw_params, 594 635 .set_fmt = cs42l43_asp_set_fmt, ··· 648 611 } 649 612 650 613 static const struct snd_soc_dai_ops cs42l43_sdw_ops = { 614 + .probe = cs42l43_dai_probe, 615 + .remove = cs42l43_dai_remove, 651 616 .startup = cs42l43_startup, 652 617 .set_stream = cs42l43_sdw_set_stream, 653 618 .hw_params = cs42l43_sdw_hw_params,
+2
sound/soc/codecs/cs42l43.h
··· 100 100 struct delayed_work hp_ilimit_clear_work; 101 101 bool hp_ilimited; 102 102 int hp_ilimit_count; 103 + 104 + struct snd_kcontrol *kctl[5]; 103 105 }; 104 106 105 107 #if IS_REACHABLE(CONFIG_SND_SOC_CS42L43_SDW)