diff mbox series

[SRU,R,v2,14/14] ASoC: SDCA: Move kcontrol search out of IRQ

Message ID 20260826082456.892463-15-chris.chiu@canonical.com
State New
Headers show
Series Fix SDCA jack detection race and reprobe issues on Dell Slate Max platform with CirrusLogic audio solution | expand

Commit Message

Chris Chiu Aug. 26, 2026, 8:24 a.m. UTC
From: Charles Keepax <ckeepax@opensource.cirrus.com>

BugLink: https://bugs.launchpad.net/bugs/2163215

Now that the IRQs are always registered after all the ALSA
controls are created it is possible to search for the control
at the point the IRQ is requested. Move the control search out
of the IRQ handler and do it at IRQ request time.

This also fixes a potential issue when the card was torn down
and reprobed without destroying the codec device, the kctl
pointer stored by the IRQ handler would not be updated to the
new control on the second probe.

Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
Link: https://patch.msgid.link/20260721143636.361814-8-ckeepax@opensource.cirrus.com
Signed-off-by: Mark Brown <broonie@kernel.org>
(cherry picked from commit bf1b7821f85383a8804441f8fd5165be148f3ec8)
Signed-off-by: Chris Chiu <chris.chiu@canonical.com>
---
 include/sound/sdca_jack.h        |  1 +
 sound/soc/sdca/sdca_interrupts.c | 12 +++++++++
 sound/soc/sdca/sdca_jack.c       | 43 +++++++++++++++++++-------------
 3 files changed, 39 insertions(+), 17 deletions(-)
diff mbox series

Patch

diff --git a/include/sound/sdca_jack.h b/include/sound/sdca_jack.h
index 59de40b7d7d0..871ba2d8146a 100644
--- a/include/sound/sdca_jack.h
+++ b/include/sound/sdca_jack.h
@@ -28,6 +28,7 @@  struct jack_state {
 };
 
 int sdca_jack_alloc_state(struct sdca_interrupt *interrupt);
+int sdca_jack_init_state(struct sdca_interrupt *interrupt);
 void sdca_jack_free_state(struct sdca_interrupt *interrupt);
 
 int sdca_jack_process(struct sdca_interrupt *interrupt);
diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c
index 79a17d827f99..15da7c2aea50 100644
--- a/sound/soc/sdca/sdca_interrupts.c
+++ b/sound/soc/sdca/sdca_interrupts.c
@@ -512,6 +512,8 @@  int sdca_irq_populate(struct sdca_function_data *function,
 
 	for (i = 0; i < SDCA_MAX_INTERRUPTS; i++) {
 		struct sdca_interrupt *interrupt = &info->irqs[i];
+		struct sdca_control *control = interrupt->control;
+		struct sdca_entity *entity = interrupt->entity;
 		int irq;
 
 		if (interrupt->function != function || interrupt->irq)
@@ -519,6 +521,16 @@  int sdca_irq_populate(struct sdca_function_data *function,
 
 		interrupt->component = component;
 
+		switch (SDCA_CTL_TYPE(entity->type, control->sel)) {
+		case SDCA_CTL_TYPE_S(GE, DETECTED_MODE):
+			ret = sdca_jack_init_state(interrupt);
+			if (ret)
+				return ret;
+			break;
+		default:
+			break;
+		}
+
 		irq = interrupt->control->interrupt_position;
 		ret = sdca_irq_request_locked(dev, info, irq, interrupt->name,
 					      interrupt->handler, interrupt);
diff --git a/sound/soc/sdca/sdca_jack.c b/sound/soc/sdca/sdca_jack.c
index 3c84d17244a2..73f8067906a9 100644
--- a/sound/soc/sdca/sdca_jack.c
+++ b/sound/soc/sdca/sdca_jack.c
@@ -47,23 +47,6 @@  int sdca_jack_process(struct sdca_interrupt *interrupt)
 
 	guard(rwsem_write)(rwsem);
 
-	if (!kctl) {
-		const char *name __free(kfree) = kasprintf(GFP_KERNEL, "%s %s",
-							   interrupt->entity->label,
-							   SDCA_CTL_SELECTED_MODE_NAME);
-
-		if (!name)
-			return -ENOMEM;
-
-		kctl = snd_soc_component_get_kcontrol(component, name);
-		if (!kctl) {
-			dev_err(dev, "control not found: %s\n", name);
-			return -ENODEV;
-		}
-
-		state->kctl = kctl;
-	}
-
 	reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id,
 			   interrupt->control->sel, 0);
 
@@ -148,6 +131,32 @@  void sdca_jack_free_state(struct sdca_interrupt *interrupt)
 }
 EXPORT_SYMBOL_NS_GPL(sdca_jack_free_state, "SND_SOC_SDCA");
 
+/**
+ * sdca_jack_init_state - Initialise transient state for a jack interrupt
+ * @interrupt: SDCA interrupt structure.
+ *
+ * Return: Zero on success or a negative error code.
+ */
+int sdca_jack_init_state(struct sdca_interrupt *interrupt)
+{
+	struct jack_state *jack_state = interrupt->priv;
+	const char *name __free(kfree) = kasprintf(GFP_KERNEL, "%s %s",
+						   interrupt->entity->label,
+						   SDCA_CTL_SELECTED_MODE_NAME);
+
+	if (!name)
+		return -ENOMEM;
+
+	jack_state->kctl = snd_soc_component_get_kcontrol(interrupt->component, name);
+	if (!jack_state->kctl) {
+		dev_err(interrupt->dev, "control not found: %s\n", name);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_NS_GPL(sdca_jack_init_state, "SND_SOC_SDCA");
+
 static int type_get_mask(enum sdca_terminal_type type)
 {
 	switch (type) {