Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ASoC: core: Complete support for card rebinding

Since commit e894efef9ac7 ("ASoC: core: add support to card rebind")
there is a support for card rebind. The support is only partial though.
Let's consider the following scenarios both of which aim to enumerate a
sound card:

1)
snd_soc_add_component(comp1);
(...)
snd_soc_register_card(card1);

2)
snd_soc_register_card(card1);
(...)
snd_soc_add_component(comp1);

For the sake of simplicity, let comp1 be the last dependency needed for
the card1 to enumerate.
Case 1) will end up succeeding whereas 2) is a certain fail -
snd_soc_bind_card() does not honor unbind_card_list so even a non-fatal
return code of EPROBE_DEFER will cause the card to collapse. Given the
typical usecase of platform_device serving as a card->dev and its
probe() ending with:

int carddev_probe(struct platform_device *pdev)
{
(...)
return devm_snd_soc_register_card(dev, card);
}

failure to register card triggers device_unbind_cleanup() -
really_probe() in dd.c.

To allow for card registration to be deferred while being friendly
towards existing users of devm_snd_soc_register_card(), add new
card->devres_dev field, and devm_xxx() variants for card registration:

devm_snd_soc_register_deferrable_card() (external)
devm_snd_soc_bind_card() (internal)

In essence, if requested, devm_snd_soc_bind_card() replaces
snd_soc_bind_card(). The rebind procedure takes care of destroying
old devres before attempting the new bind. This makes sure nothing is
left hanging if binding fails and card becomes unbound but is still
registered to the ASoC framework.

To allow snd_soc_bind_card() to be reused by the deferrable friends,
move 'client_mutex' locking to the function's callers and select between
devm_xxx and non-devm_xxx variants of snd_soc_bind_card() based on
card->devres_dev.

On top of the feature, the refactoring brings two benefits:
a) single lock/unlock of 'client_mutex' in snd_soc_add_component()
instead of ambiguous unlock and immediate lock in
snd_soc_try_rebind_card()
b) all unbind_card_list manipulations done under 'client_mutex'

Reviewed-by: Amadeusz Sławiński <amadeuszx.slawinski@linux.intel.com>
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
Link: https://patch.msgid.link/20250404101622.3673850-1-cezary.rojewski@intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Cezary Rojewski and committed by
Mark Brown
a3375522 d01131e3

+82 -27
+2
include/sound/soc.h
··· 423 423 int snd_soc_register_card(struct snd_soc_card *card); 424 424 void snd_soc_unregister_card(struct snd_soc_card *card); 425 425 int devm_snd_soc_register_card(struct device *dev, struct snd_soc_card *card); 426 + int devm_snd_soc_register_deferrable_card(struct device *dev, struct snd_soc_card *card); 426 427 #ifdef CONFIG_PM_SLEEP 427 428 int snd_soc_suspend(struct device *dev); 428 429 int snd_soc_resume(struct device *dev); ··· 1088 1087 unsigned int fully_routed:1; 1089 1088 unsigned int probed:1; 1090 1089 unsigned int component_chaining:1; 1090 + struct device *devres_dev; 1091 1091 1092 1092 void *drvdata; 1093 1093 };
+73 -27
sound/soc/soc-core.c
··· 2134 2134 } 2135 2135 } 2136 2136 2137 - static void snd_soc_unbind_card(struct snd_soc_card *card, bool unregister) 2137 + static void snd_soc_unbind_card(struct snd_soc_card *card) 2138 2138 { 2139 2139 if (snd_soc_card_is_instantiated(card)) { 2140 2140 card->instantiated = false; 2141 2141 snd_soc_flush_all_delayed_work(card); 2142 2142 2143 2143 soc_cleanup_card_resources(card); 2144 - if (!unregister) 2145 - list_add(&card->list, &unbind_card_list); 2146 - } else { 2147 - if (unregister) 2148 - list_del(&card->list); 2149 2144 } 2150 2145 } 2151 2146 ··· 2150 2155 struct snd_soc_component *component; 2151 2156 int ret; 2152 2157 2153 - mutex_lock(&client_mutex); 2154 2158 snd_soc_card_mutex_lock_root(card); 2155 - 2156 2159 snd_soc_fill_dummy_dai(card); 2157 2160 2158 2161 snd_soc_dapm_init(&card->dapm, card, NULL); ··· 2297 2304 probe_end: 2298 2305 if (ret < 0) 2299 2306 soc_cleanup_card_resources(card); 2300 - 2301 2307 snd_soc_card_mutex_unlock(card); 2302 - mutex_unlock(&client_mutex); 2308 + 2309 + return ret; 2310 + } 2311 + 2312 + static void devm_card_bind_release(struct device *dev, void *res) 2313 + { 2314 + snd_soc_unregister_card(*(struct snd_soc_card **)res); 2315 + } 2316 + 2317 + static int devm_snd_soc_bind_card(struct device *dev, struct snd_soc_card *card) 2318 + { 2319 + struct snd_soc_card **ptr; 2320 + int ret; 2321 + 2322 + ptr = devres_alloc(devm_card_bind_release, sizeof(*ptr), GFP_KERNEL); 2323 + if (!ptr) 2324 + return -ENOMEM; 2325 + 2326 + ret = snd_soc_bind_card(card); 2327 + if (ret == 0 || ret == -EPROBE_DEFER) { 2328 + *ptr = card; 2329 + devres_add(dev, ptr); 2330 + } else { 2331 + devres_free(ptr); 2332 + } 2333 + 2334 + return ret; 2335 + } 2336 + 2337 + static int snd_soc_rebind_card(struct snd_soc_card *card) 2338 + { 2339 + int ret; 2340 + 2341 + if (card->devres_dev) { 2342 + devres_destroy(card->devres_dev, devm_card_bind_release, NULL, NULL); 2343 + ret = devm_snd_soc_bind_card(card->devres_dev, card); 2344 + } else { 2345 + ret = snd_soc_bind_card(card); 2346 + } 2347 + 2348 + if (ret != -EPROBE_DEFER) 2349 + list_del_init(&card->list); 2303 2350 2304 2351 return ret; 2305 2352 } ··· 2539 2506 */ 2540 2507 int snd_soc_register_card(struct snd_soc_card *card) 2541 2508 { 2509 + int ret; 2510 + 2542 2511 if (!card->name || !card->dev) 2543 2512 return -EINVAL; 2544 2513 ··· 2561 2526 mutex_init(&card->dapm_mutex); 2562 2527 mutex_init(&card->pcm_mutex); 2563 2528 2564 - return snd_soc_bind_card(card); 2529 + mutex_lock(&client_mutex); 2530 + 2531 + if (card->devres_dev) { 2532 + ret = devm_snd_soc_bind_card(card->devres_dev, card); 2533 + if (ret == -EPROBE_DEFER) { 2534 + list_add(&card->list, &unbind_card_list); 2535 + ret = 0; 2536 + } 2537 + } else { 2538 + ret = snd_soc_bind_card(card); 2539 + } 2540 + 2541 + mutex_unlock(&client_mutex); 2542 + 2543 + return ret; 2565 2544 } 2566 2545 EXPORT_SYMBOL_GPL(snd_soc_register_card); 2567 2546 ··· 2588 2539 void snd_soc_unregister_card(struct snd_soc_card *card) 2589 2540 { 2590 2541 mutex_lock(&client_mutex); 2591 - snd_soc_unbind_card(card, true); 2542 + snd_soc_unbind_card(card); 2543 + list_del(&card->list); 2592 2544 mutex_unlock(&client_mutex); 2593 2545 dev_dbg(card->dev, "ASoC: Unregistered card '%s'\n", card->name); 2594 2546 } ··· 2803 2753 stream->formats |= endianness_format_map[i]; 2804 2754 } 2805 2755 2806 - static void snd_soc_try_rebind_card(void) 2807 - { 2808 - struct snd_soc_card *card, *c; 2809 - 2810 - list_for_each_entry_safe(card, c, &unbind_card_list, list) 2811 - if (!snd_soc_bind_card(card)) 2812 - list_del(&card->list); 2813 - } 2814 - 2815 2756 static void snd_soc_del_component_unlocked(struct snd_soc_component *component) 2816 2757 { 2817 2758 struct snd_soc_card *card = component->card; 2759 + bool instantiated; 2818 2760 2819 2761 snd_soc_unregister_dais(component); 2820 2762 2821 - if (card) 2822 - snd_soc_unbind_card(card, false); 2763 + if (card) { 2764 + instantiated = card->instantiated; 2765 + snd_soc_unbind_card(card); 2766 + if (instantiated) 2767 + list_add(&card->list, &unbind_card_list); 2768 + } 2823 2769 2824 2770 list_del(&component->list); 2825 2771 } ··· 2854 2808 struct snd_soc_dai_driver *dai_drv, 2855 2809 int num_dai) 2856 2810 { 2811 + struct snd_soc_card *card, *c; 2857 2812 int ret; 2858 2813 int i; 2859 2814 ··· 2885 2838 /* see for_each_component */ 2886 2839 list_add(&component->list, &component_list); 2887 2840 2841 + list_for_each_entry_safe(card, c, &unbind_card_list, list) 2842 + snd_soc_rebind_card(card); 2843 + 2888 2844 err_cleanup: 2889 2845 if (ret < 0) 2890 2846 snd_soc_del_component_unlocked(component); 2891 2847 2892 2848 mutex_unlock(&client_mutex); 2893 - 2894 - if (ret == 0) 2895 - snd_soc_try_rebind_card(); 2896 - 2897 2849 return ret; 2898 2850 } 2899 2851 EXPORT_SYMBOL_GPL(snd_soc_add_component);
+7
sound/soc/soc-devres.c
··· 83 83 } 84 84 EXPORT_SYMBOL_GPL(devm_snd_soc_register_card); 85 85 86 + int devm_snd_soc_register_deferrable_card(struct device *dev, struct snd_soc_card *card) 87 + { 88 + card->devres_dev = dev; 89 + return snd_soc_register_card(card); 90 + } 91 + EXPORT_SYMBOL_GPL(devm_snd_soc_register_deferrable_card); 92 + 86 93 #ifdef CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM 87 94 88 95 static void devm_dmaengine_pcm_release(struct device *dev, void *res)