[SRU][R][PATCH v5 8/8] ASoC: SDCA: Move kcontrol search out of IRQ

Chris Chiu chris.chiu at canonical.com
Fri Sep 11 08:44:55 UTC 2026


From: Charles Keepax <ckeepax at 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 at opensource.cirrus.com>
Link: https://patch.msgid.link/20260721143636.361814-8-ckeepax@opensource.cirrus.com
Signed-off-by: Mark Brown <broonie at kernel.org>
(backported from commit bf1b7821f85383a8804441f8fd5165be148f3ec8)
[ChrisChiu: Remove duplicated struct members in struct jack_state.
 Remove dead helper 'type_get_mask' from this series and implement
 local mapping in sdca_jack_report(). Place sdca_jack_free_state/init
 before sdca_jack_set_jack and remove unused locals]
Signed-off-by: Chris Chiu <chris.chiu at canonical.com>
---
v5 change:
 - Remove dead helper 'type_get_mask' from this series
 - lace sdca_jack_free_state/init before sdca_jack_set_jack and remove unused locals

 include/sound/sdca_jack.h        |  3 ++
 sound/soc/sdca/sdca_interrupts.c | 12 ++++++
 sound/soc/sdca/sdca_jack.c       | 66 ++++++++++++++++++++------------
 3 files changed, 56 insertions(+), 25 deletions(-)

diff --git a/include/sound/sdca_jack.h b/include/sound/sdca_jack.h
index 8624c51eea26..a55a1bad1904 100644
--- a/include/sound/sdca_jack.h
+++ b/include/sound/sdca_jack.h
@@ -22,9 +22,12 @@ struct snd_soc_jack;
 struct jack_state {
 	struct snd_kcontrol *kctl;
 	struct snd_soc_jack *jack;
+	/* legacy mask field; wiring of proper masks (cb3c257e0824) is out of scope */
+	unsigned int mask;
 };
 
 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 2a77e28ba43e..c6b9cb3fcc2f 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);
 
@@ -143,16 +126,36 @@ EXPORT_SYMBOL_NS_GPL(sdca_jack_alloc_state, "SND_SOC_SDCA");
  * @interrupt: SDCA interrupt structure.
  */
 void sdca_jack_free_state(struct sdca_interrupt *interrupt)
+{
+	kfree(interrupt->priv);
+}
+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 (!jack_state)
-		return;
+	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;
+	}
 
-	/* devm allocation: just clear the pointer */
-	interrupt->priv = NULL;
+	return 0;
 }
-EXPORT_SYMBOL_NS_GPL(sdca_jack_free_state, "SND_SOC_SDCA");
+EXPORT_SYMBOL_NS_GPL(sdca_jack_init_state, "SND_SOC_SDCA");
 
 
 /**
@@ -164,7 +167,8 @@ EXPORT_SYMBOL_NS_GPL(sdca_jack_free_state, "SND_SOC_SDCA");
  */
 int sdca_jack_set_jack(struct sdca_interrupt_info *info, struct snd_soc_jack *jack)
 {
-	int i, ret;
+	int i, j;
+	int ret;
 
 	guard(mutex)(&info->irq_lock);
 
@@ -172,6 +176,7 @@ int sdca_jack_set_jack(struct sdca_interrupt_info *info, struct snd_soc_jack *ja
 		struct sdca_interrupt *interrupt = &info->irqs[i];
 		struct sdca_control *control = interrupt->control;
 		struct sdca_entity *entity = interrupt->entity;
+		struct sdca_control_range *range;
 		struct jack_state *jack_state;
 
 		if (!interrupt->dev)
@@ -179,7 +184,14 @@ int sdca_jack_set_jack(struct sdca_interrupt_info *info, struct snd_soc_jack *ja
 
 		switch (SDCA_CTL_TYPE(entity->type, control->sel)) {
 		case SDCA_CTL_TYPE_S(GE, DETECTED_MODE):
+			/* Keep scope small: do not backport cb3c257e0824 which wires
+			 * type_get_mask into jack_state->mask. Use legacy behaviour
+			 * and report a full mask (0xFFFF) here.
+			 */
 			jack_state = interrupt->priv;
+
+			jack_state->mask = 0xFFFF;
+
 			jack_state->jack = jack;
 
 			/* Report initial state in case IRQ was already handled */
@@ -201,7 +213,6 @@ int sdca_jack_report(struct sdca_interrupt *interrupt)
 	struct jack_state *jack_state = interrupt->priv;
 	struct sdca_control_range *range;
 	enum sdca_terminal_type type;
-	unsigned int report = 0;
 	unsigned int reg, val;
 	int ret;
 
@@ -223,6 +234,11 @@ int sdca_jack_report(struct sdca_interrupt *interrupt)
 	type = sdca_range_search(range, SDCA_SELECTED_MODE_INDEX,
 				 val, SDCA_SELECTED_MODE_TERM_TYPE);
 
+	/* Map the terminal type to a jack report without relying on the
+	 * helper that was introduced upstream in cb3c257e0824 (out of scope).
+	 */
+	unsigned int report = 0;
+
 	switch (type) {
 	case SDCA_TERM_TYPE_LINEIN_STEREO:
 	case SDCA_TERM_TYPE_LINEIN_FRONT_LR:
@@ -251,7 +267,7 @@ int sdca_jack_report(struct sdca_interrupt *interrupt)
 		break;
 	}
 
-	snd_soc_jack_report(jack_state->jack, report, 0xFFFF);
+	snd_soc_jack_report(jack_state->jack, report, jack_state->mask);
 
 	return 0;
 }
-- 
2.43.0




More information about the kernel-team mailing list