From c7420dd9213de36a25e03c06e6d3367735970f31 Mon Sep 17 00:00:00 2001 From: Chancel Liu Date: Sun, 13 Sep 2026 19:24:59 +0900 Subject: ASoC: simple-card-utils: Add simple_util_remove_jack() simple_util_init_jack() acquires a GPIO descriptor with gpiod_get_optional() and hands it to snd_soc_jack_add_gpios(), which additionally installs an IRQ handler, a PM notifier, a delayed work and a sysfs export for it. simple-card-utils has no counterpart that releases any of this again. Add simple_util_remove_jack(), the counterpart of simple_util_init_jack(). It releases the jack GPIO and everything snd_soc_jack_add_gpios() installed for it via snd_soc_jack_free_gpios(), and clears sjack->gpio.desc so the descriptor is not left dangling after it has been put. This gives the generic card drivers a card->remove() path to release the jack when the card goes away. The devres cleanup alone is not enough: it is tied to the platform device, so it does not run when the card is unregistered and re-registered while the platform device stays bound - for example when a codec component is unbound and bound again. Signed-off-by: Chancel Liu Link: https://patch.msgid.link/20260913102502.2805912-2-chancel.liu@oss.nxp.com Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index bd8c3a033577..b0e00555092e 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -219,6 +219,7 @@ static inline int simple_util_parse_aux_devs(struct simple_util_priv *priv, char int simple_util_init_jack(struct snd_soc_card *card, struct simple_util_jack *sjack, int is_hp, char *prefix, char *pin); +void simple_util_remove_jack(struct simple_util_jack *sjack); int simple_util_init_aux_jacks(struct snd_soc_card *card, char *prefix); int simple_util_init_priv(struct simple_util_priv *priv, struct link_info *li); -- cgit v1.2.3 From 698fdd31522c42b486979054b195647d07cd71e8 Mon Sep 17 00:00:00 2001 From: Chancel Liu Date: Sun, 13 Sep 2026 19:25:01 +0900 Subject: ASoC: audio-graph-card: Free jack GPIOs on card remove graph_util_card_probe() sets up the headphone and microphone jacks, which takes a reference on the jack detection GPIO and registers an IRQ, a PM notifier, a delayed work and a sysfs export for it. audio-graph-card has no card->remove() callback, so none of this is released when the card is torn down. This goes unnoticed while the card is only torn down together with its platform device, because the devres cleanup registered by snd_soc_jack_add_gpios() is tied to that platform device. It becomes a problem once the card alone is unregistered and registered again while the platform device stays bound - for example when a codec component is unbound and bound again. card->probe() then runs a second time and gpiod_get_optional() fails with -EBUSY, because the descriptor is still held by the previous bind. The card is not registered again and audio stays broken until the platform device itself is unbound. Add graph_util_card_remove() as the counterpart of graph_util_card_probe() and hook it to card->remove. It lives in simple-card-utils.c so that audio-graph-card2 can use it as well. Signed-off-by: Chancel Liu Acked-by: Kuninori Morimoto Link: https://patch.msgid.link/20260913102502.2805912-4-chancel.liu@oss.nxp.com Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 1 + sound/soc/generic/audio-graph-card.c | 1 + sound/soc/generic/simple-card-utils.c | 11 +++++++++++ 3 files changed, 13 insertions(+) (limited to 'include') diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index b0e00555092e..68faec56970e 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -226,6 +226,7 @@ int simple_util_init_priv(struct simple_util_priv *priv, void simple_util_remove(struct platform_device *pdev); int graph_util_card_probe(struct snd_soc_card *card); +int graph_util_card_remove(struct snd_soc_card *card); int graph_util_is_ports0(struct device_node *port); int graph_util_parse_dai(struct simple_util_priv *priv, struct device_node *ep, struct snd_soc_dai_link_component *dlc, int *is_single_link); diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 0a8a6d891d1f..127c2e6b308e 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -629,6 +629,7 @@ static int graph_probe(struct platform_device *pdev) card->dapm_widgets = graph_dapm_widgets; card->num_dapm_widgets = ARRAY_SIZE(graph_dapm_widgets); card->probe = graph_util_card_probe; + card->remove = graph_util_card_remove; if (of_device_get_match_data(dev)) priv->dpcm_selectable = 1; diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 50842627434e..98e1b419105b 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -1028,6 +1028,17 @@ end: } EXPORT_SYMBOL_GPL(graph_util_card_probe); +int graph_util_card_remove(struct snd_soc_card *card) +{ + struct simple_util_priv *priv = snd_soc_card_get_drvdata(card); + + simple_util_remove_jack(&priv->hp_jack); + simple_util_remove_jack(&priv->mic_jack); + + return 0; +} +EXPORT_SYMBOL_GPL(graph_util_card_remove); + int graph_util_is_ports0(struct device_node *np) { struct device_node *parent __free(device_node) = of_get_parent(np); -- cgit v1.2.3