[SRU][R][PATCH v8 2/3] UBUNTU: SAUCE: ASoC: SDCA: Bind jack interrupts to their control after card probe

Chris Chiu chris.chiu at canonical.com
Wed Sep 23 01:33:25 UTC 2026


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

sdca_irq_populate() requests the jack interrupts from the component
probe, which soc_probe_component() runs at snd_soc_component_probe()
time. The "Selected Mode" kcontrol the jack handler drives is a DAPM
control, and DAPM controls are not created until snd_soc_bind_card()
calls snd_soc_dapm_new_widgets(), much later.

So every interrupt that arrives in that window - including the one that
latches whatever was already plugged in at boot - finds no control.
sdca_jack_process() falls back to writing Selected Mode directly, which
updates the hardware but not DAPM, so the card stays routed for the
wrong endpoint until the user unplugs and replugs. sdca_jack_set_jack()
re-reports the jack state afterwards, which is why the jack status seen
by userspace is right while the audio path is not.

Use the new component fixup_controls callback, which runs immediately
after snd_soc_dapm_new_widgets(), to bind each jack interrupt to its
control and re-drive the current hardware state through it. The lookup
itself is the one sdca_jack_process() already performs lazily; this just
makes it happen once, at the first point where it can succeed.

A failure here is not fatal - the card is usable and the next jack event
picks the control up - so warn and carry on rather than fail the bind.

This is a local fix rather than upstream b8f71f16134f ("ASoC: SDCA:
Switch to fixup_controls callback for IRQ registration") and its
dependencies, which solve the same problem by moving interrupt
registration and the whole interrupt-lifetime model. That series is a
better end state but a much larger change than this fix needs.

Signed-off-by: Chris Chiu <chris.chiu at canonical.com>
Tested-by: Chris Chiu <chris.chiu at canonical.com>
---
 include/sound/sdca_jack.h            |  1 +
 sound/soc/sdca/sdca_class_function.c | 11 +++++++
 sound/soc/sdca/sdca_jack.c           | 45 ++++++++++++++++++++++++++++
 3 files changed, 57 insertions(+)

diff --git a/include/sound/sdca_jack.h b/include/sound/sdca_jack.h
index 3ec22046d3eb..623116460938 100644
--- a/include/sound/sdca_jack.h
+++ b/include/sound/sdca_jack.h
@@ -27,6 +27,7 @@ struct jack_state {
 int sdca_jack_alloc_state(struct sdca_interrupt *interrupt);
 int sdca_jack_process(struct sdca_interrupt *interrupt);
 int sdca_jack_set_jack(struct sdca_interrupt_info *info, struct snd_soc_jack *jack);
+void sdca_jack_fixup_controls(struct sdca_interrupt_info *info);
 int sdca_jack_report(struct sdca_interrupt *interrupt);
 
 #endif // __SDCA_JACK_H__
diff --git a/sound/soc/sdca/sdca_class_function.c b/sound/soc/sdca/sdca_class_function.c
index 730103120514..e0ce4fc55138 100644
--- a/sound/soc/sdca/sdca_class_function.c
+++ b/sound/soc/sdca/sdca_class_function.c
@@ -198,6 +198,16 @@ static int class_function_component_probe(struct snd_soc_component *component)
 	return sdca_irq_populate(drv->function, component, core->irq_info);
 }
 
+static int class_function_component_fixup_controls(struct snd_soc_component *component)
+{
+	struct class_function_drv *drv = snd_soc_component_get_drvdata(component);
+	struct sdca_class_drv *core = drv->core;
+
+	sdca_jack_fixup_controls(core->irq_info);
+
+	return 0;
+}
+
 static void class_function_component_remove(struct snd_soc_component *component)
 {
 	struct class_function_drv *drv = snd_soc_component_get_drvdata(component);
@@ -217,6 +227,7 @@ static int class_function_set_jack(struct snd_soc_component *component,
 
 static const struct snd_soc_component_driver class_function_component_drv = {
 	.probe			= class_function_component_probe,
+	.fixup_controls		= class_function_component_fixup_controls,
 	.remove			= class_function_component_remove,
 	.endianness		= 1,
 };
diff --git a/sound/soc/sdca/sdca_jack.c b/sound/soc/sdca/sdca_jack.c
index 49d317d3b8c8..98374ae2aa88 100644
--- a/sound/soc/sdca/sdca_jack.c
+++ b/sound/soc/sdca/sdca_jack.c
@@ -186,6 +186,51 @@ int sdca_jack_set_jack(struct sdca_interrupt_info *info, struct snd_soc_jack *ja
 }
 EXPORT_SYMBOL_NS_GPL(sdca_jack_set_jack, "SND_SOC_SDCA");
 
+/**
+ * sdca_jack_fixup_controls - Bind jack interrupts to their ALSA controls
+ * @info: SDCA interrupt info structure.
+ *
+ * The jack interrupts are requested from the component probe, which the
+ * soundcard runs before it creates the DAPM kcontrols.  An interrupt
+ * arriving in that window, including the one that latches the state the
+ * jack was already in at boot, therefore finds no "Selected Mode" control
+ * to drive and falls back to writing the register directly, leaving DAPM
+ * routed for whatever was plugged in at the time.
+ *
+ * Call this once the card has created its controls to bind each jack to
+ * its control and re-drive the current hardware state through it.  A
+ * failure here is not fatal: the card is usable, the next jack event will
+ * pick the control up, so warn and carry on rather than fail the bind.
+ */
+void sdca_jack_fixup_controls(struct sdca_interrupt_info *info)
+{
+	int i, ret;
+
+	guard(mutex)(&info->irq_lock);
+
+	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;
+
+		if (!interrupt->irq)
+			continue;
+
+		switch (SDCA_CTL_TYPE(entity->type, control->sel)) {
+		case SDCA_CTL_TYPE_S(GE, DETECTED_MODE):
+			ret = sdca_jack_process(interrupt);
+			if (ret)
+				dev_warn(interrupt->dev,
+					 "failed to sync %s with its control: %d\n",
+					 interrupt->name, ret);
+			break;
+		default:
+			break;
+		}
+	}
+}
+EXPORT_SYMBOL_NS_GPL(sdca_jack_fixup_controls, "SND_SOC_SDCA");
+
 int sdca_jack_report(struct sdca_interrupt *interrupt)
 {
 	struct jack_state *jack_state = interrupt->priv;
-- 
2.43.0




More information about the kernel-team mailing list