[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