@@ -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__
@@ -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,
};
@@ -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;
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@canonical.com> Tested-by: Chris Chiu <chris.chiu@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(+)