18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// soc-core.c -- ALSA SoC Audio Layer 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Copyright 2005 Wolfson Microelectronics PLC. 68c2ecf20Sopenharmony_ci// Copyright 2005 Openedhand Ltd. 78c2ecf20Sopenharmony_ci// Copyright (C) 2010 Slimlogic Ltd. 88c2ecf20Sopenharmony_ci// Copyright (C) 2010 Texas Instruments Inc. 98c2ecf20Sopenharmony_ci// 108c2ecf20Sopenharmony_ci// Author: Liam Girdwood <lrg@slimlogic.co.uk> 118c2ecf20Sopenharmony_ci// with code, comments and ideas from :- 128c2ecf20Sopenharmony_ci// Richard Purdie <richard@openedhand.com> 138c2ecf20Sopenharmony_ci// 148c2ecf20Sopenharmony_ci// TODO: 158c2ecf20Sopenharmony_ci// o Add hw rules to enforce rates, etc. 168c2ecf20Sopenharmony_ci// o More testing with other codecs/machines. 178c2ecf20Sopenharmony_ci// o Add more codecs and platforms to ensure good API coverage. 188c2ecf20Sopenharmony_ci// o Support TDM on PCM and I2S 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/module.h> 218c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 228c2ecf20Sopenharmony_ci#include <linux/init.h> 238c2ecf20Sopenharmony_ci#include <linux/delay.h> 248c2ecf20Sopenharmony_ci#include <linux/pm.h> 258c2ecf20Sopenharmony_ci#include <linux/bitops.h> 268c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 278c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 288c2ecf20Sopenharmony_ci#include <linux/pinctrl/consumer.h> 298c2ecf20Sopenharmony_ci#include <linux/ctype.h> 308c2ecf20Sopenharmony_ci#include <linux/slab.h> 318c2ecf20Sopenharmony_ci#include <linux/of.h> 328c2ecf20Sopenharmony_ci#include <linux/of_graph.h> 338c2ecf20Sopenharmony_ci#include <linux/dmi.h> 348c2ecf20Sopenharmony_ci#include <linux/acpi.h> 358c2ecf20Sopenharmony_ci#include <sound/core.h> 368c2ecf20Sopenharmony_ci#include <sound/jack.h> 378c2ecf20Sopenharmony_ci#include <sound/pcm.h> 388c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 398c2ecf20Sopenharmony_ci#include <sound/soc.h> 408c2ecf20Sopenharmony_ci#include <sound/soc-dpcm.h> 418c2ecf20Sopenharmony_ci#include <sound/soc-topology.h> 428c2ecf20Sopenharmony_ci#include <sound/soc-link.h> 438c2ecf20Sopenharmony_ci#include <sound/initval.h> 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define CREATE_TRACE_POINTS 468c2ecf20Sopenharmony_ci#include <trace/events/asoc.h> 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(client_mutex); 498c2ecf20Sopenharmony_cistatic LIST_HEAD(component_list); 508c2ecf20Sopenharmony_cistatic LIST_HEAD(unbind_card_list); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define for_each_component(component) \ 538c2ecf20Sopenharmony_ci list_for_each_entry(component, &component_list, list) 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* 568c2ecf20Sopenharmony_ci * This is used if driver don't need to have CPU/Codec/Platform 578c2ecf20Sopenharmony_ci * dai_link. see soc.h 588c2ecf20Sopenharmony_ci */ 598c2ecf20Sopenharmony_cistruct snd_soc_dai_link_component null_dailink_component[0]; 608c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(null_dailink_component); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* 638c2ecf20Sopenharmony_ci * This is a timeout to do a DAPM powerdown after a stream is closed(). 648c2ecf20Sopenharmony_ci * It can be used to eliminate pops between different playback streams, e.g. 658c2ecf20Sopenharmony_ci * between two audio tracks. 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_cistatic int pmdown_time = 5000; 688c2ecf20Sopenharmony_cimodule_param(pmdown_time, int, 0); 698c2ecf20Sopenharmony_ciMODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)"); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic ssize_t pmdown_time_show(struct device *dev, 728c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci return sprintf(buf, "%ld\n", rtd->pmdown_time); 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic ssize_t pmdown_time_set(struct device *dev, 808c2ecf20Sopenharmony_ci struct device_attribute *attr, 818c2ecf20Sopenharmony_ci const char *buf, size_t count) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev); 848c2ecf20Sopenharmony_ci int ret; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci ret = kstrtol(buf, 10, &rtd->pmdown_time); 878c2ecf20Sopenharmony_ci if (ret) 888c2ecf20Sopenharmony_ci return ret; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci return count; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic DEVICE_ATTR(pmdown_time, 0644, pmdown_time_show, pmdown_time_set); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic struct attribute *soc_dev_attrs[] = { 968c2ecf20Sopenharmony_ci &dev_attr_pmdown_time.attr, 978c2ecf20Sopenharmony_ci NULL 988c2ecf20Sopenharmony_ci}; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic umode_t soc_dev_attr_is_visible(struct kobject *kobj, 1018c2ecf20Sopenharmony_ci struct attribute *attr, int idx) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 1048c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (!rtd) 1078c2ecf20Sopenharmony_ci return 0; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (attr == &dev_attr_pmdown_time.attr) 1108c2ecf20Sopenharmony_ci return attr->mode; /* always visible */ 1118c2ecf20Sopenharmony_ci return rtd->num_codecs ? attr->mode : 0; /* enabled only with codec */ 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic const struct attribute_group soc_dapm_dev_group = { 1158c2ecf20Sopenharmony_ci .attrs = soc_dapm_dev_attrs, 1168c2ecf20Sopenharmony_ci .is_visible = soc_dev_attr_is_visible, 1178c2ecf20Sopenharmony_ci}; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic const struct attribute_group soc_dev_group = { 1208c2ecf20Sopenharmony_ci .attrs = soc_dev_attrs, 1218c2ecf20Sopenharmony_ci .is_visible = soc_dev_attr_is_visible, 1228c2ecf20Sopenharmony_ci}; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic const struct attribute_group *soc_dev_attr_groups[] = { 1258c2ecf20Sopenharmony_ci &soc_dapm_dev_group, 1268c2ecf20Sopenharmony_ci &soc_dev_group, 1278c2ecf20Sopenharmony_ci NULL 1288c2ecf20Sopenharmony_ci}; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 1318c2ecf20Sopenharmony_cistruct dentry *snd_soc_debugfs_root; 1328c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_debugfs_root); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic void soc_init_component_debugfs(struct snd_soc_component *component) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci if (!component->card->debugfs_card_root) 1378c2ecf20Sopenharmony_ci return; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (component->debugfs_prefix) { 1408c2ecf20Sopenharmony_ci char *name; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci name = kasprintf(GFP_KERNEL, "%s:%s", 1438c2ecf20Sopenharmony_ci component->debugfs_prefix, component->name); 1448c2ecf20Sopenharmony_ci if (name) { 1458c2ecf20Sopenharmony_ci component->debugfs_root = debugfs_create_dir(name, 1468c2ecf20Sopenharmony_ci component->card->debugfs_card_root); 1478c2ecf20Sopenharmony_ci kfree(name); 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci } else { 1508c2ecf20Sopenharmony_ci component->debugfs_root = debugfs_create_dir(component->name, 1518c2ecf20Sopenharmony_ci component->card->debugfs_card_root); 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci snd_soc_dapm_debugfs_init(snd_soc_component_get_dapm(component), 1558c2ecf20Sopenharmony_ci component->debugfs_root); 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic void soc_cleanup_component_debugfs(struct snd_soc_component *component) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci if (!component->debugfs_root) 1618c2ecf20Sopenharmony_ci return; 1628c2ecf20Sopenharmony_ci debugfs_remove_recursive(component->debugfs_root); 1638c2ecf20Sopenharmony_ci component->debugfs_root = NULL; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic int dai_list_show(struct seq_file *m, void *v) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct snd_soc_component *component; 1698c2ecf20Sopenharmony_ci struct snd_soc_dai *dai; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci mutex_lock(&client_mutex); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci for_each_component(component) 1748c2ecf20Sopenharmony_ci for_each_component_dais(component, dai) 1758c2ecf20Sopenharmony_ci seq_printf(m, "%s\n", dai->name); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci mutex_unlock(&client_mutex); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci return 0; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(dai_list); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic int component_list_show(struct seq_file *m, void *v) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci struct snd_soc_component *component; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci mutex_lock(&client_mutex); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci for_each_component(component) 1908c2ecf20Sopenharmony_ci seq_printf(m, "%s\n", component->name); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci mutex_unlock(&client_mutex); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci return 0; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(component_list); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic void soc_init_card_debugfs(struct snd_soc_card *card) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci card->debugfs_card_root = debugfs_create_dir(card->name, 2018c2ecf20Sopenharmony_ci snd_soc_debugfs_root); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci debugfs_create_u32("dapm_pop_time", 0644, card->debugfs_card_root, 2048c2ecf20Sopenharmony_ci &card->pop_time); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci snd_soc_dapm_debugfs_init(&card->dapm, card->debugfs_card_root); 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic void soc_cleanup_card_debugfs(struct snd_soc_card *card) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci debugfs_remove_recursive(card->debugfs_card_root); 2128c2ecf20Sopenharmony_ci card->debugfs_card_root = NULL; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic void snd_soc_debugfs_init(void) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci snd_soc_debugfs_root = debugfs_create_dir("asoc", NULL); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci debugfs_create_file("dais", 0444, snd_soc_debugfs_root, NULL, 2208c2ecf20Sopenharmony_ci &dai_list_fops); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci debugfs_create_file("components", 0444, snd_soc_debugfs_root, NULL, 2238c2ecf20Sopenharmony_ci &component_list_fops); 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic void snd_soc_debugfs_exit(void) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci debugfs_remove_recursive(snd_soc_debugfs_root); 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci#else 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic inline void soc_init_component_debugfs( 2348c2ecf20Sopenharmony_ci struct snd_soc_component *component) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic inline void soc_cleanup_component_debugfs( 2398c2ecf20Sopenharmony_ci struct snd_soc_component *component) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic inline void soc_init_card_debugfs(struct snd_soc_card *card) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic inline void soc_cleanup_card_debugfs(struct snd_soc_card *card) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic inline void snd_soc_debugfs_init(void) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic inline void snd_soc_debugfs_exit(void) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci#endif 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic int snd_soc_rtd_add_component(struct snd_soc_pcm_runtime *rtd, 2628c2ecf20Sopenharmony_ci struct snd_soc_component *component) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci struct snd_soc_component *comp; 2658c2ecf20Sopenharmony_ci int i; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci for_each_rtd_components(rtd, i, comp) { 2688c2ecf20Sopenharmony_ci /* already connected */ 2698c2ecf20Sopenharmony_ci if (comp == component) 2708c2ecf20Sopenharmony_ci return 0; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* see for_each_rtd_components */ 2748c2ecf20Sopenharmony_ci rtd->components[rtd->num_components] = component; 2758c2ecf20Sopenharmony_ci rtd->num_components++; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci return 0; 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistruct snd_soc_component *snd_soc_rtdcom_lookup(struct snd_soc_pcm_runtime *rtd, 2818c2ecf20Sopenharmony_ci const char *driver_name) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci struct snd_soc_component *component; 2848c2ecf20Sopenharmony_ci int i; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if (!driver_name) 2878c2ecf20Sopenharmony_ci return NULL; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci /* 2908c2ecf20Sopenharmony_ci * NOTE 2918c2ecf20Sopenharmony_ci * 2928c2ecf20Sopenharmony_ci * snd_soc_rtdcom_lookup() will find component from rtd by using 2938c2ecf20Sopenharmony_ci * specified driver name. 2948c2ecf20Sopenharmony_ci * But, if many components which have same driver name are connected 2958c2ecf20Sopenharmony_ci * to 1 rtd, this function will return 1st found component. 2968c2ecf20Sopenharmony_ci */ 2978c2ecf20Sopenharmony_ci for_each_rtd_components(rtd, i, component) { 2988c2ecf20Sopenharmony_ci const char *component_name = component->driver->name; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (!component_name) 3018c2ecf20Sopenharmony_ci continue; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci if ((component_name == driver_name) || 3048c2ecf20Sopenharmony_ci strcmp(component_name, driver_name) == 0) 3058c2ecf20Sopenharmony_ci return component; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci return NULL; 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_rtdcom_lookup); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistruct snd_soc_component 3138c2ecf20Sopenharmony_ci*snd_soc_lookup_component_nolocked(struct device *dev, const char *driver_name) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct snd_soc_component *component; 3168c2ecf20Sopenharmony_ci struct snd_soc_component *found_component; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci found_component = NULL; 3198c2ecf20Sopenharmony_ci for_each_component(component) { 3208c2ecf20Sopenharmony_ci if ((dev == component->dev) && 3218c2ecf20Sopenharmony_ci (!driver_name || 3228c2ecf20Sopenharmony_ci (driver_name == component->driver->name) || 3238c2ecf20Sopenharmony_ci (strcmp(component->driver->name, driver_name) == 0))) { 3248c2ecf20Sopenharmony_ci found_component = component; 3258c2ecf20Sopenharmony_ci break; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci return found_component; 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_lookup_component_nolocked); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistruct snd_soc_component *snd_soc_lookup_component(struct device *dev, 3348c2ecf20Sopenharmony_ci const char *driver_name) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci struct snd_soc_component *component; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci mutex_lock(&client_mutex); 3398c2ecf20Sopenharmony_ci component = snd_soc_lookup_component_nolocked(dev, driver_name); 3408c2ecf20Sopenharmony_ci mutex_unlock(&client_mutex); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci return component; 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_lookup_component); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistruct snd_soc_pcm_runtime 3478c2ecf20Sopenharmony_ci*snd_soc_get_pcm_runtime(struct snd_soc_card *card, 3488c2ecf20Sopenharmony_ci struct snd_soc_dai_link *dai_link) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci for_each_card_rtds(card, rtd) { 3538c2ecf20Sopenharmony_ci if (rtd->dai_link == dai_link) 3548c2ecf20Sopenharmony_ci return rtd; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci dev_dbg(card->dev, "ASoC: failed to find rtd %s\n", dai_link->name); 3578c2ecf20Sopenharmony_ci return NULL; 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_get_pcm_runtime); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci/* 3628c2ecf20Sopenharmony_ci * Power down the audio subsystem pmdown_time msecs after close is called. 3638c2ecf20Sopenharmony_ci * This is to ensure there are no pops or clicks in between any music tracks 3648c2ecf20Sopenharmony_ci * due to DAPM power cycling. 3658c2ecf20Sopenharmony_ci */ 3668c2ecf20Sopenharmony_civoid snd_soc_close_delayed_work(struct snd_soc_pcm_runtime *rtd) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); 3698c2ecf20Sopenharmony_ci int playback = SNDRV_PCM_STREAM_PLAYBACK; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci dev_dbg(rtd->dev, 3748c2ecf20Sopenharmony_ci "ASoC: pop wq checking: %s status: %s waiting: %s\n", 3758c2ecf20Sopenharmony_ci codec_dai->driver->playback.stream_name, 3768c2ecf20Sopenharmony_ci snd_soc_dai_stream_active(codec_dai, playback) ? 3778c2ecf20Sopenharmony_ci "active" : "inactive", 3788c2ecf20Sopenharmony_ci rtd->pop_wait ? "yes" : "no"); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci /* are we waiting on this codec DAI stream */ 3818c2ecf20Sopenharmony_ci if (rtd->pop_wait == 1) { 3828c2ecf20Sopenharmony_ci rtd->pop_wait = 0; 3838c2ecf20Sopenharmony_ci snd_soc_dapm_stream_event(rtd, playback, 3848c2ecf20Sopenharmony_ci SND_SOC_DAPM_STREAM_STOP); 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci mutex_unlock(&rtd->card->pcm_mutex); 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_close_delayed_work); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic void soc_release_rtd_dev(struct device *dev) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci /* "dev" means "rtd->dev" */ 3948c2ecf20Sopenharmony_ci kfree(dev); 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic void soc_free_pcm_runtime(struct snd_soc_pcm_runtime *rtd) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci if (!rtd) 4008c2ecf20Sopenharmony_ci return; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci list_del(&rtd->list); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci if (delayed_work_pending(&rtd->delayed_work)) 4058c2ecf20Sopenharmony_ci flush_delayed_work(&rtd->delayed_work); 4068c2ecf20Sopenharmony_ci snd_soc_pcm_component_free(rtd); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /* 4098c2ecf20Sopenharmony_ci * we don't need to call kfree() for rtd->dev 4108c2ecf20Sopenharmony_ci * see 4118c2ecf20Sopenharmony_ci * soc_release_rtd_dev() 4128c2ecf20Sopenharmony_ci * 4138c2ecf20Sopenharmony_ci * We don't need rtd->dev NULL check, because 4148c2ecf20Sopenharmony_ci * it is alloced *before* rtd. 4158c2ecf20Sopenharmony_ci * see 4168c2ecf20Sopenharmony_ci * soc_new_pcm_runtime() 4178c2ecf20Sopenharmony_ci */ 4188c2ecf20Sopenharmony_ci device_unregister(rtd->dev); 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cistatic void close_delayed_work(struct work_struct *work) { 4228c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = 4238c2ecf20Sopenharmony_ci container_of(work, struct snd_soc_pcm_runtime, 4248c2ecf20Sopenharmony_ci delayed_work.work); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci if (rtd->close_delayed_work_func) 4278c2ecf20Sopenharmony_ci rtd->close_delayed_work_func(rtd); 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic struct snd_soc_pcm_runtime *soc_new_pcm_runtime( 4318c2ecf20Sopenharmony_ci struct snd_soc_card *card, struct snd_soc_dai_link *dai_link) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 4348c2ecf20Sopenharmony_ci struct snd_soc_component *component; 4358c2ecf20Sopenharmony_ci struct device *dev; 4368c2ecf20Sopenharmony_ci int ret; 4378c2ecf20Sopenharmony_ci int stream; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci /* 4408c2ecf20Sopenharmony_ci * for rtd->dev 4418c2ecf20Sopenharmony_ci */ 4428c2ecf20Sopenharmony_ci dev = kzalloc(sizeof(struct device), GFP_KERNEL); 4438c2ecf20Sopenharmony_ci if (!dev) 4448c2ecf20Sopenharmony_ci return NULL; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci dev->parent = card->dev; 4478c2ecf20Sopenharmony_ci dev->release = soc_release_rtd_dev; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci dev_set_name(dev, "%s", dai_link->name); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci ret = device_register(dev); 4528c2ecf20Sopenharmony_ci if (ret < 0) { 4538c2ecf20Sopenharmony_ci put_device(dev); /* soc_release_rtd_dev */ 4548c2ecf20Sopenharmony_ci return NULL; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci /* 4588c2ecf20Sopenharmony_ci * for rtd 4598c2ecf20Sopenharmony_ci */ 4608c2ecf20Sopenharmony_ci rtd = devm_kzalloc(dev, 4618c2ecf20Sopenharmony_ci sizeof(*rtd) + 4628c2ecf20Sopenharmony_ci sizeof(*component) * (dai_link->num_cpus + 4638c2ecf20Sopenharmony_ci dai_link->num_codecs + 4648c2ecf20Sopenharmony_ci dai_link->num_platforms), 4658c2ecf20Sopenharmony_ci GFP_KERNEL); 4668c2ecf20Sopenharmony_ci if (!rtd) 4678c2ecf20Sopenharmony_ci goto free_rtd; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci rtd->dev = dev; 4708c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&rtd->list); 4718c2ecf20Sopenharmony_ci for_each_pcm_streams(stream) { 4728c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&rtd->dpcm[stream].be_clients); 4738c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&rtd->dpcm[stream].fe_clients); 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci dev_set_drvdata(dev, rtd); 4768c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci /* 4798c2ecf20Sopenharmony_ci * for rtd->dais 4808c2ecf20Sopenharmony_ci */ 4818c2ecf20Sopenharmony_ci rtd->dais = devm_kcalloc(dev, dai_link->num_cpus + dai_link->num_codecs, 4828c2ecf20Sopenharmony_ci sizeof(struct snd_soc_dai *), 4838c2ecf20Sopenharmony_ci GFP_KERNEL); 4848c2ecf20Sopenharmony_ci if (!rtd->dais) 4858c2ecf20Sopenharmony_ci goto free_rtd; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci /* 4888c2ecf20Sopenharmony_ci * dais = [][][][][][][][][][][][][][][][][][] 4898c2ecf20Sopenharmony_ci * ^cpu_dais ^codec_dais 4908c2ecf20Sopenharmony_ci * |--- num_cpus ---|--- num_codecs --| 4918c2ecf20Sopenharmony_ci * see 4928c2ecf20Sopenharmony_ci * asoc_rtd_to_cpu() 4938c2ecf20Sopenharmony_ci * asoc_rtd_to_codec() 4948c2ecf20Sopenharmony_ci */ 4958c2ecf20Sopenharmony_ci rtd->num_cpus = dai_link->num_cpus; 4968c2ecf20Sopenharmony_ci rtd->num_codecs = dai_link->num_codecs; 4978c2ecf20Sopenharmony_ci rtd->card = card; 4988c2ecf20Sopenharmony_ci rtd->dai_link = dai_link; 4998c2ecf20Sopenharmony_ci rtd->num = card->num_rtd++; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci /* see for_each_card_rtds */ 5028c2ecf20Sopenharmony_ci list_add_tail(&rtd->list, &card->rtd_list); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci ret = device_add_groups(dev, soc_dev_attr_groups); 5058c2ecf20Sopenharmony_ci if (ret < 0) 5068c2ecf20Sopenharmony_ci goto free_rtd; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci return rtd; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cifree_rtd: 5118c2ecf20Sopenharmony_ci soc_free_pcm_runtime(rtd); 5128c2ecf20Sopenharmony_ci return NULL; 5138c2ecf20Sopenharmony_ci} 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic void snd_soc_flush_all_delayed_work(struct snd_soc_card *card) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci for_each_card_rtds(card, rtd) 5208c2ecf20Sopenharmony_ci flush_delayed_work(&rtd->delayed_work); 5218c2ecf20Sopenharmony_ci} 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 5248c2ecf20Sopenharmony_ci/* powers down audio subsystem for suspend */ 5258c2ecf20Sopenharmony_ciint snd_soc_suspend(struct device *dev) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci struct snd_soc_card *card = dev_get_drvdata(dev); 5288c2ecf20Sopenharmony_ci struct snd_soc_component *component; 5298c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 5308c2ecf20Sopenharmony_ci int playback = SNDRV_PCM_STREAM_PLAYBACK; 5318c2ecf20Sopenharmony_ci int i; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci /* If the card is not initialized yet there is nothing to do */ 5348c2ecf20Sopenharmony_ci if (!card->instantiated) 5358c2ecf20Sopenharmony_ci return 0; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci /* 5388c2ecf20Sopenharmony_ci * Due to the resume being scheduled into a workqueue we could 5398c2ecf20Sopenharmony_ci * suspend before that's finished - wait for it to complete. 5408c2ecf20Sopenharmony_ci */ 5418c2ecf20Sopenharmony_ci snd_power_wait(card->snd_card, SNDRV_CTL_POWER_D0); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci /* we're going to block userspace touching us until resume completes */ 5448c2ecf20Sopenharmony_ci snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D3hot); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci /* mute any active DACs */ 5478c2ecf20Sopenharmony_ci for_each_card_rtds(card, rtd) { 5488c2ecf20Sopenharmony_ci struct snd_soc_dai *dai; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci if (rtd->dai_link->ignore_suspend) 5518c2ecf20Sopenharmony_ci continue; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci for_each_rtd_dais(rtd, i, dai) { 5548c2ecf20Sopenharmony_ci if (snd_soc_dai_stream_active(dai, playback)) 5558c2ecf20Sopenharmony_ci snd_soc_dai_digital_mute(dai, 1, playback); 5568c2ecf20Sopenharmony_ci } 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci /* suspend all pcms */ 5608c2ecf20Sopenharmony_ci for_each_card_rtds(card, rtd) { 5618c2ecf20Sopenharmony_ci if (rtd->dai_link->ignore_suspend) 5628c2ecf20Sopenharmony_ci continue; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci snd_pcm_suspend_all(rtd->pcm); 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci snd_soc_card_suspend_pre(card); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci /* close any waiting streams */ 5708c2ecf20Sopenharmony_ci snd_soc_flush_all_delayed_work(card); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci for_each_card_rtds(card, rtd) { 5738c2ecf20Sopenharmony_ci int stream; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci if (rtd->dai_link->ignore_suspend) 5768c2ecf20Sopenharmony_ci continue; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci for_each_pcm_streams(stream) 5798c2ecf20Sopenharmony_ci snd_soc_dapm_stream_event(rtd, stream, 5808c2ecf20Sopenharmony_ci SND_SOC_DAPM_STREAM_SUSPEND); 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci /* Recheck all endpoints too, their state is affected by suspend */ 5848c2ecf20Sopenharmony_ci dapm_mark_endpoints_dirty(card); 5858c2ecf20Sopenharmony_ci snd_soc_dapm_sync(&card->dapm); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci /* suspend all COMPONENTs */ 5888c2ecf20Sopenharmony_ci for_each_card_rtds(card, rtd) { 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci if (rtd->dai_link->ignore_suspend) 5918c2ecf20Sopenharmony_ci continue; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci for_each_rtd_components(rtd, i, component) { 5948c2ecf20Sopenharmony_ci struct snd_soc_dapm_context *dapm = 5958c2ecf20Sopenharmony_ci snd_soc_component_get_dapm(component); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci /* 5988c2ecf20Sopenharmony_ci * ignore if component was already suspended 5998c2ecf20Sopenharmony_ci */ 6008c2ecf20Sopenharmony_ci if (snd_soc_component_is_suspended(component)) 6018c2ecf20Sopenharmony_ci continue; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci /* 6048c2ecf20Sopenharmony_ci * If there are paths active then the COMPONENT will be 6058c2ecf20Sopenharmony_ci * held with bias _ON and should not be suspended. 6068c2ecf20Sopenharmony_ci */ 6078c2ecf20Sopenharmony_ci switch (snd_soc_dapm_get_bias_level(dapm)) { 6088c2ecf20Sopenharmony_ci case SND_SOC_BIAS_STANDBY: 6098c2ecf20Sopenharmony_ci /* 6108c2ecf20Sopenharmony_ci * If the COMPONENT is capable of idle 6118c2ecf20Sopenharmony_ci * bias off then being in STANDBY 6128c2ecf20Sopenharmony_ci * means it's doing something, 6138c2ecf20Sopenharmony_ci * otherwise fall through. 6148c2ecf20Sopenharmony_ci */ 6158c2ecf20Sopenharmony_ci if (dapm->idle_bias_off) { 6168c2ecf20Sopenharmony_ci dev_dbg(component->dev, 6178c2ecf20Sopenharmony_ci "ASoC: idle_bias_off CODEC on over suspend\n"); 6188c2ecf20Sopenharmony_ci break; 6198c2ecf20Sopenharmony_ci } 6208c2ecf20Sopenharmony_ci fallthrough; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci case SND_SOC_BIAS_OFF: 6238c2ecf20Sopenharmony_ci snd_soc_component_suspend(component); 6248c2ecf20Sopenharmony_ci if (component->regmap) 6258c2ecf20Sopenharmony_ci regcache_mark_dirty(component->regmap); 6268c2ecf20Sopenharmony_ci /* deactivate pins to sleep state */ 6278c2ecf20Sopenharmony_ci pinctrl_pm_select_sleep_state(component->dev); 6288c2ecf20Sopenharmony_ci break; 6298c2ecf20Sopenharmony_ci default: 6308c2ecf20Sopenharmony_ci dev_dbg(component->dev, 6318c2ecf20Sopenharmony_ci "ASoC: COMPONENT is on over suspend\n"); 6328c2ecf20Sopenharmony_ci break; 6338c2ecf20Sopenharmony_ci } 6348c2ecf20Sopenharmony_ci } 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci snd_soc_card_suspend_post(card); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci return 0; 6408c2ecf20Sopenharmony_ci} 6418c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_suspend); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci/* 6448c2ecf20Sopenharmony_ci * deferred resume work, so resume can complete before we finished 6458c2ecf20Sopenharmony_ci * setting our codec back up, which can be very slow on I2C 6468c2ecf20Sopenharmony_ci */ 6478c2ecf20Sopenharmony_cistatic void soc_resume_deferred(struct work_struct *work) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci struct snd_soc_card *card = 6508c2ecf20Sopenharmony_ci container_of(work, struct snd_soc_card, 6518c2ecf20Sopenharmony_ci deferred_resume_work); 6528c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 6538c2ecf20Sopenharmony_ci struct snd_soc_component *component; 6548c2ecf20Sopenharmony_ci int i; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci /* 6578c2ecf20Sopenharmony_ci * our power state is still SNDRV_CTL_POWER_D3hot from suspend time, 6588c2ecf20Sopenharmony_ci * so userspace apps are blocked from touching us 6598c2ecf20Sopenharmony_ci */ 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci dev_dbg(card->dev, "ASoC: starting resume work\n"); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci /* Bring us up into D2 so that DAPM starts enabling things */ 6648c2ecf20Sopenharmony_ci snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D2); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci snd_soc_card_resume_pre(card); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci for_each_card_components(card, component) { 6698c2ecf20Sopenharmony_ci if (snd_soc_component_is_suspended(component)) 6708c2ecf20Sopenharmony_ci snd_soc_component_resume(component); 6718c2ecf20Sopenharmony_ci } 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci for_each_card_rtds(card, rtd) { 6748c2ecf20Sopenharmony_ci int stream; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci if (rtd->dai_link->ignore_suspend) 6778c2ecf20Sopenharmony_ci continue; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci for_each_pcm_streams(stream) 6808c2ecf20Sopenharmony_ci snd_soc_dapm_stream_event(rtd, stream, 6818c2ecf20Sopenharmony_ci SND_SOC_DAPM_STREAM_RESUME); 6828c2ecf20Sopenharmony_ci } 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci /* unmute any active DACs */ 6858c2ecf20Sopenharmony_ci for_each_card_rtds(card, rtd) { 6868c2ecf20Sopenharmony_ci struct snd_soc_dai *dai; 6878c2ecf20Sopenharmony_ci int playback = SNDRV_PCM_STREAM_PLAYBACK; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci if (rtd->dai_link->ignore_suspend) 6908c2ecf20Sopenharmony_ci continue; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci for_each_rtd_dais(rtd, i, dai) { 6938c2ecf20Sopenharmony_ci if (snd_soc_dai_stream_active(dai, playback)) 6948c2ecf20Sopenharmony_ci snd_soc_dai_digital_mute(dai, 0, playback); 6958c2ecf20Sopenharmony_ci } 6968c2ecf20Sopenharmony_ci } 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci snd_soc_card_resume_post(card); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci dev_dbg(card->dev, "ASoC: resume work completed\n"); 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci /* Recheck all endpoints too, their state is affected by suspend */ 7038c2ecf20Sopenharmony_ci dapm_mark_endpoints_dirty(card); 7048c2ecf20Sopenharmony_ci snd_soc_dapm_sync(&card->dapm); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci /* userspace can access us now we are back as we were before */ 7078c2ecf20Sopenharmony_ci snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D0); 7088c2ecf20Sopenharmony_ci} 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci/* powers up audio subsystem after a suspend */ 7118c2ecf20Sopenharmony_ciint snd_soc_resume(struct device *dev) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci struct snd_soc_card *card = dev_get_drvdata(dev); 7148c2ecf20Sopenharmony_ci struct snd_soc_component *component; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci /* If the card is not initialized yet there is nothing to do */ 7178c2ecf20Sopenharmony_ci if (!card->instantiated) 7188c2ecf20Sopenharmony_ci return 0; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci /* activate pins from sleep state */ 7218c2ecf20Sopenharmony_ci for_each_card_components(card, component) 7228c2ecf20Sopenharmony_ci if (snd_soc_component_active(component)) 7238c2ecf20Sopenharmony_ci pinctrl_pm_select_default_state(component->dev); 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci dev_dbg(dev, "ASoC: Scheduling resume work\n"); 7268c2ecf20Sopenharmony_ci if (!schedule_work(&card->deferred_resume_work)) 7278c2ecf20Sopenharmony_ci dev_err(dev, "ASoC: resume work item may be lost\n"); 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci return 0; 7308c2ecf20Sopenharmony_ci} 7318c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_resume); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_cistatic void soc_resume_init(struct snd_soc_card *card) 7348c2ecf20Sopenharmony_ci{ 7358c2ecf20Sopenharmony_ci /* deferred resume work */ 7368c2ecf20Sopenharmony_ci INIT_WORK(&card->deferred_resume_work, soc_resume_deferred); 7378c2ecf20Sopenharmony_ci} 7388c2ecf20Sopenharmony_ci#else 7398c2ecf20Sopenharmony_ci#define snd_soc_suspend NULL 7408c2ecf20Sopenharmony_ci#define snd_soc_resume NULL 7418c2ecf20Sopenharmony_cistatic inline void soc_resume_init(struct snd_soc_card *card) 7428c2ecf20Sopenharmony_ci{ 7438c2ecf20Sopenharmony_ci} 7448c2ecf20Sopenharmony_ci#endif 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_cistatic struct device_node 7478c2ecf20Sopenharmony_ci*soc_component_to_node(struct snd_soc_component *component) 7488c2ecf20Sopenharmony_ci{ 7498c2ecf20Sopenharmony_ci struct device_node *of_node; 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci of_node = component->dev->of_node; 7528c2ecf20Sopenharmony_ci if (!of_node && component->dev->parent) 7538c2ecf20Sopenharmony_ci of_node = component->dev->parent->of_node; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci return of_node; 7568c2ecf20Sopenharmony_ci} 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_cistatic int snd_soc_is_matching_component( 7598c2ecf20Sopenharmony_ci const struct snd_soc_dai_link_component *dlc, 7608c2ecf20Sopenharmony_ci struct snd_soc_component *component) 7618c2ecf20Sopenharmony_ci{ 7628c2ecf20Sopenharmony_ci struct device_node *component_of_node; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci if (!dlc) 7658c2ecf20Sopenharmony_ci return 0; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci component_of_node = soc_component_to_node(component); 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci if (dlc->of_node && component_of_node != dlc->of_node) 7708c2ecf20Sopenharmony_ci return 0; 7718c2ecf20Sopenharmony_ci if (dlc->name && strcmp(component->name, dlc->name)) 7728c2ecf20Sopenharmony_ci return 0; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci return 1; 7758c2ecf20Sopenharmony_ci} 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_cistatic struct snd_soc_component *soc_find_component( 7788c2ecf20Sopenharmony_ci const struct snd_soc_dai_link_component *dlc) 7798c2ecf20Sopenharmony_ci{ 7808c2ecf20Sopenharmony_ci struct snd_soc_component *component; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci lockdep_assert_held(&client_mutex); 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci /* 7858c2ecf20Sopenharmony_ci * NOTE 7868c2ecf20Sopenharmony_ci * 7878c2ecf20Sopenharmony_ci * It returns *1st* found component, but some driver 7888c2ecf20Sopenharmony_ci * has few components by same of_node/name 7898c2ecf20Sopenharmony_ci * ex) 7908c2ecf20Sopenharmony_ci * CPU component and generic DMAEngine component 7918c2ecf20Sopenharmony_ci */ 7928c2ecf20Sopenharmony_ci for_each_component(component) 7938c2ecf20Sopenharmony_ci if (snd_soc_is_matching_component(dlc, component)) 7948c2ecf20Sopenharmony_ci return component; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci return NULL; 7978c2ecf20Sopenharmony_ci} 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci/** 8008c2ecf20Sopenharmony_ci * snd_soc_find_dai - Find a registered DAI 8018c2ecf20Sopenharmony_ci * 8028c2ecf20Sopenharmony_ci * @dlc: name of the DAI or the DAI driver and optional component info to match 8038c2ecf20Sopenharmony_ci * 8048c2ecf20Sopenharmony_ci * This function will search all registered components and their DAIs to 8058c2ecf20Sopenharmony_ci * find the DAI of the same name. The component's of_node and name 8068c2ecf20Sopenharmony_ci * should also match if being specified. 8078c2ecf20Sopenharmony_ci * 8088c2ecf20Sopenharmony_ci * Return: pointer of DAI, or NULL if not found. 8098c2ecf20Sopenharmony_ci */ 8108c2ecf20Sopenharmony_cistruct snd_soc_dai *snd_soc_find_dai( 8118c2ecf20Sopenharmony_ci const struct snd_soc_dai_link_component *dlc) 8128c2ecf20Sopenharmony_ci{ 8138c2ecf20Sopenharmony_ci struct snd_soc_component *component; 8148c2ecf20Sopenharmony_ci struct snd_soc_dai *dai; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci lockdep_assert_held(&client_mutex); 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci /* Find CPU DAI from registered DAIs */ 8198c2ecf20Sopenharmony_ci for_each_component(component) { 8208c2ecf20Sopenharmony_ci if (!snd_soc_is_matching_component(dlc, component)) 8218c2ecf20Sopenharmony_ci continue; 8228c2ecf20Sopenharmony_ci for_each_component_dais(component, dai) { 8238c2ecf20Sopenharmony_ci if (dlc->dai_name && strcmp(dai->name, dlc->dai_name) 8248c2ecf20Sopenharmony_ci && (!dai->driver->name 8258c2ecf20Sopenharmony_ci || strcmp(dai->driver->name, dlc->dai_name))) 8268c2ecf20Sopenharmony_ci continue; 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci return dai; 8298c2ecf20Sopenharmony_ci } 8308c2ecf20Sopenharmony_ci } 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci return NULL; 8338c2ecf20Sopenharmony_ci} 8348c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_find_dai); 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_cistruct snd_soc_dai *snd_soc_find_dai_with_mutex( 8378c2ecf20Sopenharmony_ci const struct snd_soc_dai_link_component *dlc) 8388c2ecf20Sopenharmony_ci{ 8398c2ecf20Sopenharmony_ci struct snd_soc_dai *dai; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci mutex_lock(&client_mutex); 8428c2ecf20Sopenharmony_ci dai = snd_soc_find_dai(dlc); 8438c2ecf20Sopenharmony_ci mutex_unlock(&client_mutex); 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci return dai; 8468c2ecf20Sopenharmony_ci} 8478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_find_dai_with_mutex); 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_cistatic int soc_dai_link_sanity_check(struct snd_soc_card *card, 8508c2ecf20Sopenharmony_ci struct snd_soc_dai_link *link) 8518c2ecf20Sopenharmony_ci{ 8528c2ecf20Sopenharmony_ci int i; 8538c2ecf20Sopenharmony_ci struct snd_soc_dai_link_component *cpu, *codec, *platform; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci for_each_link_codecs(link, i, codec) { 8568c2ecf20Sopenharmony_ci /* 8578c2ecf20Sopenharmony_ci * Codec must be specified by 1 of name or OF node, 8588c2ecf20Sopenharmony_ci * not both or neither. 8598c2ecf20Sopenharmony_ci */ 8608c2ecf20Sopenharmony_ci if (!!codec->name == !!codec->of_node) { 8618c2ecf20Sopenharmony_ci dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n", 8628c2ecf20Sopenharmony_ci link->name); 8638c2ecf20Sopenharmony_ci return -EINVAL; 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci /* Codec DAI name must be specified */ 8678c2ecf20Sopenharmony_ci if (!codec->dai_name) { 8688c2ecf20Sopenharmony_ci dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n", 8698c2ecf20Sopenharmony_ci link->name); 8708c2ecf20Sopenharmony_ci return -EINVAL; 8718c2ecf20Sopenharmony_ci } 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci /* 8748c2ecf20Sopenharmony_ci * Defer card registration if codec component is not added to 8758c2ecf20Sopenharmony_ci * component list. 8768c2ecf20Sopenharmony_ci */ 8778c2ecf20Sopenharmony_ci if (!soc_find_component(codec)) { 8788c2ecf20Sopenharmony_ci dev_dbg(card->dev, 8798c2ecf20Sopenharmony_ci "ASoC: codec component %s not found for link %s\n", 8808c2ecf20Sopenharmony_ci codec->name, link->name); 8818c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 8828c2ecf20Sopenharmony_ci } 8838c2ecf20Sopenharmony_ci } 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci for_each_link_platforms(link, i, platform) { 8868c2ecf20Sopenharmony_ci /* 8878c2ecf20Sopenharmony_ci * Platform may be specified by either name or OF node, but it 8888c2ecf20Sopenharmony_ci * can be left unspecified, then no components will be inserted 8898c2ecf20Sopenharmony_ci * in the rtdcom list 8908c2ecf20Sopenharmony_ci */ 8918c2ecf20Sopenharmony_ci if (!!platform->name == !!platform->of_node) { 8928c2ecf20Sopenharmony_ci dev_err(card->dev, 8938c2ecf20Sopenharmony_ci "ASoC: Neither/both platform name/of_node are set for %s\n", 8948c2ecf20Sopenharmony_ci link->name); 8958c2ecf20Sopenharmony_ci return -EINVAL; 8968c2ecf20Sopenharmony_ci } 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci /* 8998c2ecf20Sopenharmony_ci * Defer card registration if platform component is not added to 9008c2ecf20Sopenharmony_ci * component list. 9018c2ecf20Sopenharmony_ci */ 9028c2ecf20Sopenharmony_ci if (!soc_find_component(platform)) { 9038c2ecf20Sopenharmony_ci dev_dbg(card->dev, 9048c2ecf20Sopenharmony_ci "ASoC: platform component %s not found for link %s\n", 9058c2ecf20Sopenharmony_ci platform->name, link->name); 9068c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 9078c2ecf20Sopenharmony_ci } 9088c2ecf20Sopenharmony_ci } 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci for_each_link_cpus(link, i, cpu) { 9118c2ecf20Sopenharmony_ci /* 9128c2ecf20Sopenharmony_ci * CPU device may be specified by either name or OF node, but 9138c2ecf20Sopenharmony_ci * can be left unspecified, and will be matched based on DAI 9148c2ecf20Sopenharmony_ci * name alone.. 9158c2ecf20Sopenharmony_ci */ 9168c2ecf20Sopenharmony_ci if (cpu->name && cpu->of_node) { 9178c2ecf20Sopenharmony_ci dev_err(card->dev, 9188c2ecf20Sopenharmony_ci "ASoC: Neither/both cpu name/of_node are set for %s\n", 9198c2ecf20Sopenharmony_ci link->name); 9208c2ecf20Sopenharmony_ci return -EINVAL; 9218c2ecf20Sopenharmony_ci } 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci /* 9248c2ecf20Sopenharmony_ci * Defer card registration if cpu dai component is not added to 9258c2ecf20Sopenharmony_ci * component list. 9268c2ecf20Sopenharmony_ci */ 9278c2ecf20Sopenharmony_ci if ((cpu->of_node || cpu->name) && 9288c2ecf20Sopenharmony_ci !soc_find_component(cpu)) { 9298c2ecf20Sopenharmony_ci dev_dbg(card->dev, 9308c2ecf20Sopenharmony_ci "ASoC: cpu component %s not found for link %s\n", 9318c2ecf20Sopenharmony_ci cpu->name, link->name); 9328c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 9338c2ecf20Sopenharmony_ci } 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci /* 9368c2ecf20Sopenharmony_ci * At least one of CPU DAI name or CPU device name/node must be 9378c2ecf20Sopenharmony_ci * specified 9388c2ecf20Sopenharmony_ci */ 9398c2ecf20Sopenharmony_ci if (!cpu->dai_name && 9408c2ecf20Sopenharmony_ci !(cpu->name || cpu->of_node)) { 9418c2ecf20Sopenharmony_ci dev_err(card->dev, 9428c2ecf20Sopenharmony_ci "ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n", 9438c2ecf20Sopenharmony_ci link->name); 9448c2ecf20Sopenharmony_ci return -EINVAL; 9458c2ecf20Sopenharmony_ci } 9468c2ecf20Sopenharmony_ci } 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci return 0; 9498c2ecf20Sopenharmony_ci} 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci/** 9528c2ecf20Sopenharmony_ci * snd_soc_remove_pcm_runtime - Remove a pcm_runtime from card 9538c2ecf20Sopenharmony_ci * @card: The ASoC card to which the pcm_runtime has 9548c2ecf20Sopenharmony_ci * @rtd: The pcm_runtime to remove 9558c2ecf20Sopenharmony_ci * 9568c2ecf20Sopenharmony_ci * This function removes a pcm_runtime from the ASoC card. 9578c2ecf20Sopenharmony_ci */ 9588c2ecf20Sopenharmony_civoid snd_soc_remove_pcm_runtime(struct snd_soc_card *card, 9598c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd) 9608c2ecf20Sopenharmony_ci{ 9618c2ecf20Sopenharmony_ci lockdep_assert_held(&client_mutex); 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci /* release machine specific resources */ 9648c2ecf20Sopenharmony_ci snd_soc_link_exit(rtd); 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci /* 9678c2ecf20Sopenharmony_ci * Notify the machine driver for extra destruction 9688c2ecf20Sopenharmony_ci */ 9698c2ecf20Sopenharmony_ci snd_soc_card_remove_dai_link(card, rtd->dai_link); 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci soc_free_pcm_runtime(rtd); 9728c2ecf20Sopenharmony_ci} 9738c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_remove_pcm_runtime); 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci/** 9768c2ecf20Sopenharmony_ci * snd_soc_add_pcm_runtime - Add a pcm_runtime dynamically via dai_link 9778c2ecf20Sopenharmony_ci * @card: The ASoC card to which the pcm_runtime is added 9788c2ecf20Sopenharmony_ci * @dai_link: The DAI link to find pcm_runtime 9798c2ecf20Sopenharmony_ci * 9808c2ecf20Sopenharmony_ci * This function adds a pcm_runtime ASoC card by using dai_link. 9818c2ecf20Sopenharmony_ci * 9828c2ecf20Sopenharmony_ci * Note: Topology can use this API to add pcm_runtime when probing the 9838c2ecf20Sopenharmony_ci * topology component. And machine drivers can still define static 9848c2ecf20Sopenharmony_ci * DAI links in dai_link array. 9858c2ecf20Sopenharmony_ci */ 9868c2ecf20Sopenharmony_ciint snd_soc_add_pcm_runtime(struct snd_soc_card *card, 9878c2ecf20Sopenharmony_ci struct snd_soc_dai_link *dai_link) 9888c2ecf20Sopenharmony_ci{ 9898c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 9908c2ecf20Sopenharmony_ci struct snd_soc_dai_link_component *codec, *platform, *cpu; 9918c2ecf20Sopenharmony_ci struct snd_soc_component *component; 9928c2ecf20Sopenharmony_ci int i, ret; 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci lockdep_assert_held(&client_mutex); 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci /* 9978c2ecf20Sopenharmony_ci * Notify the machine driver for extra initialization 9988c2ecf20Sopenharmony_ci */ 9998c2ecf20Sopenharmony_ci ret = snd_soc_card_add_dai_link(card, dai_link); 10008c2ecf20Sopenharmony_ci if (ret < 0) 10018c2ecf20Sopenharmony_ci return ret; 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci if (dai_link->ignore) 10048c2ecf20Sopenharmony_ci return 0; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci dev_dbg(card->dev, "ASoC: binding %s\n", dai_link->name); 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci ret = soc_dai_link_sanity_check(card, dai_link); 10098c2ecf20Sopenharmony_ci if (ret < 0) 10108c2ecf20Sopenharmony_ci return ret; 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci rtd = soc_new_pcm_runtime(card, dai_link); 10138c2ecf20Sopenharmony_ci if (!rtd) 10148c2ecf20Sopenharmony_ci return -ENOMEM; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci for_each_link_cpus(dai_link, i, cpu) { 10178c2ecf20Sopenharmony_ci asoc_rtd_to_cpu(rtd, i) = snd_soc_find_dai(cpu); 10188c2ecf20Sopenharmony_ci if (!asoc_rtd_to_cpu(rtd, i)) { 10198c2ecf20Sopenharmony_ci dev_info(card->dev, "ASoC: CPU DAI %s not registered\n", 10208c2ecf20Sopenharmony_ci cpu->dai_name); 10218c2ecf20Sopenharmony_ci goto _err_defer; 10228c2ecf20Sopenharmony_ci } 10238c2ecf20Sopenharmony_ci snd_soc_rtd_add_component(rtd, asoc_rtd_to_cpu(rtd, i)->component); 10248c2ecf20Sopenharmony_ci } 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci /* Find CODEC from registered CODECs */ 10278c2ecf20Sopenharmony_ci for_each_link_codecs(dai_link, i, codec) { 10288c2ecf20Sopenharmony_ci asoc_rtd_to_codec(rtd, i) = snd_soc_find_dai(codec); 10298c2ecf20Sopenharmony_ci if (!asoc_rtd_to_codec(rtd, i)) { 10308c2ecf20Sopenharmony_ci dev_info(card->dev, "ASoC: CODEC DAI %s not registered\n", 10318c2ecf20Sopenharmony_ci codec->dai_name); 10328c2ecf20Sopenharmony_ci goto _err_defer; 10338c2ecf20Sopenharmony_ci } 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci snd_soc_rtd_add_component(rtd, asoc_rtd_to_codec(rtd, i)->component); 10368c2ecf20Sopenharmony_ci } 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci /* Find PLATFORM from registered PLATFORMs */ 10398c2ecf20Sopenharmony_ci for_each_link_platforms(dai_link, i, platform) { 10408c2ecf20Sopenharmony_ci for_each_component(component) { 10418c2ecf20Sopenharmony_ci if (!snd_soc_is_matching_component(platform, component)) 10428c2ecf20Sopenharmony_ci continue; 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci snd_soc_rtd_add_component(rtd, component); 10458c2ecf20Sopenharmony_ci } 10468c2ecf20Sopenharmony_ci } 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci return 0; 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci_err_defer: 10518c2ecf20Sopenharmony_ci snd_soc_remove_pcm_runtime(card, rtd); 10528c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 10538c2ecf20Sopenharmony_ci} 10548c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_add_pcm_runtime); 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_cistatic int soc_init_pcm_runtime(struct snd_soc_card *card, 10578c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd) 10588c2ecf20Sopenharmony_ci{ 10598c2ecf20Sopenharmony_ci struct snd_soc_dai_link *dai_link = rtd->dai_link; 10608c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); 10618c2ecf20Sopenharmony_ci struct snd_soc_component *component; 10628c2ecf20Sopenharmony_ci int ret, num, i; 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci /* set default power off timeout */ 10658c2ecf20Sopenharmony_ci rtd->pmdown_time = pmdown_time; 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci /* do machine specific initialization */ 10688c2ecf20Sopenharmony_ci ret = snd_soc_link_init(rtd); 10698c2ecf20Sopenharmony_ci if (ret < 0) 10708c2ecf20Sopenharmony_ci return ret; 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci if (dai_link->dai_fmt) { 10738c2ecf20Sopenharmony_ci ret = snd_soc_runtime_set_dai_fmt(rtd, dai_link->dai_fmt); 10748c2ecf20Sopenharmony_ci if (ret) 10758c2ecf20Sopenharmony_ci return ret; 10768c2ecf20Sopenharmony_ci } 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci /* add DPCM sysfs entries */ 10798c2ecf20Sopenharmony_ci soc_dpcm_debugfs_add(rtd); 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci num = rtd->num; 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci /* 10848c2ecf20Sopenharmony_ci * most drivers will register their PCMs using DAI link ordering but 10858c2ecf20Sopenharmony_ci * topology based drivers can use the DAI link id field to set PCM 10868c2ecf20Sopenharmony_ci * device number and then use rtd + a base offset of the BEs. 10878c2ecf20Sopenharmony_ci */ 10888c2ecf20Sopenharmony_ci for_each_rtd_components(rtd, i, component) { 10898c2ecf20Sopenharmony_ci if (!component->driver->use_dai_pcm_id) 10908c2ecf20Sopenharmony_ci continue; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci if (rtd->dai_link->no_pcm) 10938c2ecf20Sopenharmony_ci num += component->driver->be_pcm_base; 10948c2ecf20Sopenharmony_ci else 10958c2ecf20Sopenharmony_ci num = rtd->dai_link->id; 10968c2ecf20Sopenharmony_ci } 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci /* create compress_device if possible */ 10998c2ecf20Sopenharmony_ci ret = snd_soc_dai_compress_new(cpu_dai, rtd, num); 11008c2ecf20Sopenharmony_ci if (ret != -ENOTSUPP) { 11018c2ecf20Sopenharmony_ci if (ret < 0) 11028c2ecf20Sopenharmony_ci dev_err(card->dev, "ASoC: can't create compress %s\n", 11038c2ecf20Sopenharmony_ci dai_link->stream_name); 11048c2ecf20Sopenharmony_ci return ret; 11058c2ecf20Sopenharmony_ci } 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci /* create the pcm */ 11088c2ecf20Sopenharmony_ci ret = soc_new_pcm(rtd, num); 11098c2ecf20Sopenharmony_ci if (ret < 0) { 11108c2ecf20Sopenharmony_ci dev_err(card->dev, "ASoC: can't create pcm %s :%d\n", 11118c2ecf20Sopenharmony_ci dai_link->stream_name, ret); 11128c2ecf20Sopenharmony_ci return ret; 11138c2ecf20Sopenharmony_ci } 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci return snd_soc_pcm_dai_new(rtd); 11168c2ecf20Sopenharmony_ci} 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_cistatic void soc_set_name_prefix(struct snd_soc_card *card, 11198c2ecf20Sopenharmony_ci struct snd_soc_component *component) 11208c2ecf20Sopenharmony_ci{ 11218c2ecf20Sopenharmony_ci struct device_node *of_node = soc_component_to_node(component); 11228c2ecf20Sopenharmony_ci const char *str; 11238c2ecf20Sopenharmony_ci int ret, i; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci for (i = 0; i < card->num_configs; i++) { 11268c2ecf20Sopenharmony_ci struct snd_soc_codec_conf *map = &card->codec_conf[i]; 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci if (snd_soc_is_matching_component(&map->dlc, component)) { 11298c2ecf20Sopenharmony_ci component->name_prefix = map->name_prefix; 11308c2ecf20Sopenharmony_ci return; 11318c2ecf20Sopenharmony_ci } 11328c2ecf20Sopenharmony_ci } 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci /* 11358c2ecf20Sopenharmony_ci * If there is no configuration table or no match in the table, 11368c2ecf20Sopenharmony_ci * check if a prefix is provided in the node 11378c2ecf20Sopenharmony_ci */ 11388c2ecf20Sopenharmony_ci ret = of_property_read_string(of_node, "sound-name-prefix", &str); 11398c2ecf20Sopenharmony_ci if (ret < 0) 11408c2ecf20Sopenharmony_ci return; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci component->name_prefix = str; 11438c2ecf20Sopenharmony_ci} 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_cistatic void soc_remove_component(struct snd_soc_component *component, 11468c2ecf20Sopenharmony_ci int probed) 11478c2ecf20Sopenharmony_ci{ 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci if (!component->card) 11508c2ecf20Sopenharmony_ci return; 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci if (probed) 11538c2ecf20Sopenharmony_ci snd_soc_component_remove(component); 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci /* For framework level robustness */ 11568c2ecf20Sopenharmony_ci snd_soc_component_set_jack(component, NULL, NULL); 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci list_del_init(&component->card_list); 11598c2ecf20Sopenharmony_ci snd_soc_dapm_free(snd_soc_component_get_dapm(component)); 11608c2ecf20Sopenharmony_ci soc_cleanup_component_debugfs(component); 11618c2ecf20Sopenharmony_ci component->card = NULL; 11628c2ecf20Sopenharmony_ci snd_soc_component_module_put_when_remove(component); 11638c2ecf20Sopenharmony_ci} 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_cistatic int soc_probe_component(struct snd_soc_card *card, 11668c2ecf20Sopenharmony_ci struct snd_soc_component *component) 11678c2ecf20Sopenharmony_ci{ 11688c2ecf20Sopenharmony_ci struct snd_soc_dapm_context *dapm = 11698c2ecf20Sopenharmony_ci snd_soc_component_get_dapm(component); 11708c2ecf20Sopenharmony_ci struct snd_soc_dai *dai; 11718c2ecf20Sopenharmony_ci int probed = 0; 11728c2ecf20Sopenharmony_ci int ret; 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci if (!strcmp(component->name, "snd-soc-dummy")) 11758c2ecf20Sopenharmony_ci return 0; 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci if (component->card) { 11788c2ecf20Sopenharmony_ci if (component->card != card) { 11798c2ecf20Sopenharmony_ci dev_err(component->dev, 11808c2ecf20Sopenharmony_ci "Trying to bind component to card \"%s\" but is already bound to card \"%s\"\n", 11818c2ecf20Sopenharmony_ci card->name, component->card->name); 11828c2ecf20Sopenharmony_ci return -ENODEV; 11838c2ecf20Sopenharmony_ci } 11848c2ecf20Sopenharmony_ci return 0; 11858c2ecf20Sopenharmony_ci } 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci ret = snd_soc_component_module_get_when_probe(component); 11888c2ecf20Sopenharmony_ci if (ret < 0) 11898c2ecf20Sopenharmony_ci return ret; 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci component->card = card; 11928c2ecf20Sopenharmony_ci soc_set_name_prefix(card, component); 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci soc_init_component_debugfs(component); 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci snd_soc_dapm_init(dapm, card, component); 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci ret = snd_soc_dapm_new_controls(dapm, 11998c2ecf20Sopenharmony_ci component->driver->dapm_widgets, 12008c2ecf20Sopenharmony_ci component->driver->num_dapm_widgets); 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci if (ret != 0) { 12038c2ecf20Sopenharmony_ci dev_err(component->dev, 12048c2ecf20Sopenharmony_ci "Failed to create new controls %d\n", ret); 12058c2ecf20Sopenharmony_ci goto err_probe; 12068c2ecf20Sopenharmony_ci } 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci for_each_component_dais(component, dai) { 12098c2ecf20Sopenharmony_ci ret = snd_soc_dapm_new_dai_widgets(dapm, dai); 12108c2ecf20Sopenharmony_ci if (ret != 0) { 12118c2ecf20Sopenharmony_ci dev_err(component->dev, 12128c2ecf20Sopenharmony_ci "Failed to create DAI widgets %d\n", ret); 12138c2ecf20Sopenharmony_ci goto err_probe; 12148c2ecf20Sopenharmony_ci } 12158c2ecf20Sopenharmony_ci } 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci ret = snd_soc_component_probe(component); 12188c2ecf20Sopenharmony_ci if (ret < 0) { 12198c2ecf20Sopenharmony_ci dev_err(component->dev, 12208c2ecf20Sopenharmony_ci "ASoC: failed to probe component %d\n", ret); 12218c2ecf20Sopenharmony_ci goto err_probe; 12228c2ecf20Sopenharmony_ci } 12238c2ecf20Sopenharmony_ci WARN(dapm->idle_bias_off && 12248c2ecf20Sopenharmony_ci dapm->bias_level != SND_SOC_BIAS_OFF, 12258c2ecf20Sopenharmony_ci "codec %s can not start from non-off bias with idle_bias_off==1\n", 12268c2ecf20Sopenharmony_ci component->name); 12278c2ecf20Sopenharmony_ci probed = 1; 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci /* 12308c2ecf20Sopenharmony_ci * machine specific init 12318c2ecf20Sopenharmony_ci * see 12328c2ecf20Sopenharmony_ci * snd_soc_component_set_aux() 12338c2ecf20Sopenharmony_ci */ 12348c2ecf20Sopenharmony_ci ret = snd_soc_component_init(component); 12358c2ecf20Sopenharmony_ci if (ret < 0) 12368c2ecf20Sopenharmony_ci goto err_probe; 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci ret = snd_soc_add_component_controls(component, 12398c2ecf20Sopenharmony_ci component->driver->controls, 12408c2ecf20Sopenharmony_ci component->driver->num_controls); 12418c2ecf20Sopenharmony_ci if (ret < 0) 12428c2ecf20Sopenharmony_ci goto err_probe; 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci ret = snd_soc_dapm_add_routes(dapm, 12458c2ecf20Sopenharmony_ci component->driver->dapm_routes, 12468c2ecf20Sopenharmony_ci component->driver->num_dapm_routes); 12478c2ecf20Sopenharmony_ci if (ret < 0) { 12488c2ecf20Sopenharmony_ci if (card->disable_route_checks) { 12498c2ecf20Sopenharmony_ci dev_info(card->dev, 12508c2ecf20Sopenharmony_ci "%s: disable_route_checks set, ignoring errors on add_routes\n", 12518c2ecf20Sopenharmony_ci __func__); 12528c2ecf20Sopenharmony_ci } else { 12538c2ecf20Sopenharmony_ci dev_err(card->dev, 12548c2ecf20Sopenharmony_ci "%s: snd_soc_dapm_add_routes failed: %d\n", 12558c2ecf20Sopenharmony_ci __func__, ret); 12568c2ecf20Sopenharmony_ci goto err_probe; 12578c2ecf20Sopenharmony_ci } 12588c2ecf20Sopenharmony_ci } 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci /* see for_each_card_components */ 12618c2ecf20Sopenharmony_ci list_add(&component->card_list, &card->component_dev_list); 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_cierr_probe: 12648c2ecf20Sopenharmony_ci if (ret < 0) 12658c2ecf20Sopenharmony_ci soc_remove_component(component, probed); 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci return ret; 12688c2ecf20Sopenharmony_ci} 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_cistatic void soc_remove_link_dais(struct snd_soc_card *card) 12718c2ecf20Sopenharmony_ci{ 12728c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 12738c2ecf20Sopenharmony_ci int order; 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci for_each_comp_order(order) { 12768c2ecf20Sopenharmony_ci for_each_card_rtds(card, rtd) { 12778c2ecf20Sopenharmony_ci /* remove all rtd connected DAIs in good order */ 12788c2ecf20Sopenharmony_ci snd_soc_pcm_dai_remove(rtd, order); 12798c2ecf20Sopenharmony_ci } 12808c2ecf20Sopenharmony_ci } 12818c2ecf20Sopenharmony_ci} 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_cistatic int soc_probe_link_dais(struct snd_soc_card *card) 12848c2ecf20Sopenharmony_ci{ 12858c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 12868c2ecf20Sopenharmony_ci int order, ret; 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci for_each_comp_order(order) { 12898c2ecf20Sopenharmony_ci for_each_card_rtds(card, rtd) { 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci dev_dbg(card->dev, 12928c2ecf20Sopenharmony_ci "ASoC: probe %s dai link %d late %d\n", 12938c2ecf20Sopenharmony_ci card->name, rtd->num, order); 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci /* probe all rtd connected DAIs in good order */ 12968c2ecf20Sopenharmony_ci ret = snd_soc_pcm_dai_probe(rtd, order); 12978c2ecf20Sopenharmony_ci if (ret) 12988c2ecf20Sopenharmony_ci return ret; 12998c2ecf20Sopenharmony_ci } 13008c2ecf20Sopenharmony_ci } 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci return 0; 13038c2ecf20Sopenharmony_ci} 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_cistatic void soc_remove_link_components(struct snd_soc_card *card) 13068c2ecf20Sopenharmony_ci{ 13078c2ecf20Sopenharmony_ci struct snd_soc_component *component; 13088c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 13098c2ecf20Sopenharmony_ci int i, order; 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci for_each_comp_order(order) { 13128c2ecf20Sopenharmony_ci for_each_card_rtds(card, rtd) { 13138c2ecf20Sopenharmony_ci for_each_rtd_components(rtd, i, component) { 13148c2ecf20Sopenharmony_ci if (component->driver->remove_order != order) 13158c2ecf20Sopenharmony_ci continue; 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci soc_remove_component(component, 1); 13188c2ecf20Sopenharmony_ci } 13198c2ecf20Sopenharmony_ci } 13208c2ecf20Sopenharmony_ci } 13218c2ecf20Sopenharmony_ci} 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_cistatic int soc_probe_link_components(struct snd_soc_card *card) 13248c2ecf20Sopenharmony_ci{ 13258c2ecf20Sopenharmony_ci struct snd_soc_component *component; 13268c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 13278c2ecf20Sopenharmony_ci int i, ret, order; 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci for_each_comp_order(order) { 13308c2ecf20Sopenharmony_ci for_each_card_rtds(card, rtd) { 13318c2ecf20Sopenharmony_ci for_each_rtd_components(rtd, i, component) { 13328c2ecf20Sopenharmony_ci if (component->driver->probe_order != order) 13338c2ecf20Sopenharmony_ci continue; 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci ret = soc_probe_component(card, component); 13368c2ecf20Sopenharmony_ci if (ret < 0) 13378c2ecf20Sopenharmony_ci return ret; 13388c2ecf20Sopenharmony_ci } 13398c2ecf20Sopenharmony_ci } 13408c2ecf20Sopenharmony_ci } 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci return 0; 13438c2ecf20Sopenharmony_ci} 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_cistatic void soc_unbind_aux_dev(struct snd_soc_card *card) 13468c2ecf20Sopenharmony_ci{ 13478c2ecf20Sopenharmony_ci struct snd_soc_component *component, *_component; 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci for_each_card_auxs_safe(card, component, _component) { 13508c2ecf20Sopenharmony_ci /* for snd_soc_component_init() */ 13518c2ecf20Sopenharmony_ci snd_soc_component_set_aux(component, NULL); 13528c2ecf20Sopenharmony_ci list_del(&component->card_aux_list); 13538c2ecf20Sopenharmony_ci } 13548c2ecf20Sopenharmony_ci} 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_cistatic int soc_bind_aux_dev(struct snd_soc_card *card) 13578c2ecf20Sopenharmony_ci{ 13588c2ecf20Sopenharmony_ci struct snd_soc_component *component; 13598c2ecf20Sopenharmony_ci struct snd_soc_aux_dev *aux; 13608c2ecf20Sopenharmony_ci int i; 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci for_each_card_pre_auxs(card, i, aux) { 13638c2ecf20Sopenharmony_ci /* codecs, usually analog devices */ 13648c2ecf20Sopenharmony_ci component = soc_find_component(&aux->dlc); 13658c2ecf20Sopenharmony_ci if (!component) 13668c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci /* for snd_soc_component_init() */ 13698c2ecf20Sopenharmony_ci snd_soc_component_set_aux(component, aux); 13708c2ecf20Sopenharmony_ci /* see for_each_card_auxs */ 13718c2ecf20Sopenharmony_ci list_add(&component->card_aux_list, &card->aux_comp_list); 13728c2ecf20Sopenharmony_ci } 13738c2ecf20Sopenharmony_ci return 0; 13748c2ecf20Sopenharmony_ci} 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_cistatic int soc_probe_aux_devices(struct snd_soc_card *card) 13778c2ecf20Sopenharmony_ci{ 13788c2ecf20Sopenharmony_ci struct snd_soc_component *component; 13798c2ecf20Sopenharmony_ci int order; 13808c2ecf20Sopenharmony_ci int ret; 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci for_each_comp_order(order) { 13838c2ecf20Sopenharmony_ci for_each_card_auxs(card, component) { 13848c2ecf20Sopenharmony_ci if (component->driver->probe_order != order) 13858c2ecf20Sopenharmony_ci continue; 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci ret = soc_probe_component(card, component); 13888c2ecf20Sopenharmony_ci if (ret < 0) 13898c2ecf20Sopenharmony_ci return ret; 13908c2ecf20Sopenharmony_ci } 13918c2ecf20Sopenharmony_ci } 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci return 0; 13948c2ecf20Sopenharmony_ci} 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_cistatic void soc_remove_aux_devices(struct snd_soc_card *card) 13978c2ecf20Sopenharmony_ci{ 13988c2ecf20Sopenharmony_ci struct snd_soc_component *comp, *_comp; 13998c2ecf20Sopenharmony_ci int order; 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci for_each_comp_order(order) { 14028c2ecf20Sopenharmony_ci for_each_card_auxs_safe(card, comp, _comp) { 14038c2ecf20Sopenharmony_ci if (comp->driver->remove_order == order) 14048c2ecf20Sopenharmony_ci soc_remove_component(comp, 1); 14058c2ecf20Sopenharmony_ci } 14068c2ecf20Sopenharmony_ci } 14078c2ecf20Sopenharmony_ci} 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci/** 14108c2ecf20Sopenharmony_ci * snd_soc_runtime_set_dai_fmt() - Change DAI link format for a ASoC runtime 14118c2ecf20Sopenharmony_ci * @rtd: The runtime for which the DAI link format should be changed 14128c2ecf20Sopenharmony_ci * @dai_fmt: The new DAI link format 14138c2ecf20Sopenharmony_ci * 14148c2ecf20Sopenharmony_ci * This function updates the DAI link format for all DAIs connected to the DAI 14158c2ecf20Sopenharmony_ci * link for the specified runtime. 14168c2ecf20Sopenharmony_ci * 14178c2ecf20Sopenharmony_ci * Note: For setups with a static format set the dai_fmt field in the 14188c2ecf20Sopenharmony_ci * corresponding snd_dai_link struct instead of using this function. 14198c2ecf20Sopenharmony_ci * 14208c2ecf20Sopenharmony_ci * Returns 0 on success, otherwise a negative error code. 14218c2ecf20Sopenharmony_ci */ 14228c2ecf20Sopenharmony_ciint snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd, 14238c2ecf20Sopenharmony_ci unsigned int dai_fmt) 14248c2ecf20Sopenharmony_ci{ 14258c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai; 14268c2ecf20Sopenharmony_ci struct snd_soc_dai *codec_dai; 14278c2ecf20Sopenharmony_ci unsigned int inv_dai_fmt; 14288c2ecf20Sopenharmony_ci unsigned int i; 14298c2ecf20Sopenharmony_ci int ret; 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci for_each_rtd_codec_dais(rtd, i, codec_dai) { 14328c2ecf20Sopenharmony_ci ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt); 14338c2ecf20Sopenharmony_ci if (ret != 0 && ret != -ENOTSUPP) { 14348c2ecf20Sopenharmony_ci dev_warn(codec_dai->dev, 14358c2ecf20Sopenharmony_ci "ASoC: Failed to set DAI format: %d\n", ret); 14368c2ecf20Sopenharmony_ci return ret; 14378c2ecf20Sopenharmony_ci } 14388c2ecf20Sopenharmony_ci } 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci /* 14418c2ecf20Sopenharmony_ci * Flip the polarity for the "CPU" end of a CODEC<->CODEC link 14428c2ecf20Sopenharmony_ci * the component which has non_legacy_dai_naming is Codec 14438c2ecf20Sopenharmony_ci */ 14448c2ecf20Sopenharmony_ci inv_dai_fmt = dai_fmt & ~SND_SOC_DAIFMT_MASTER_MASK; 14458c2ecf20Sopenharmony_ci switch (dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) { 14468c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFM: 14478c2ecf20Sopenharmony_ci inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; 14488c2ecf20Sopenharmony_ci break; 14498c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFS: 14508c2ecf20Sopenharmony_ci inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFM; 14518c2ecf20Sopenharmony_ci break; 14528c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFM: 14538c2ecf20Sopenharmony_ci inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFS; 14548c2ecf20Sopenharmony_ci break; 14558c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFS: 14568c2ecf20Sopenharmony_ci inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; 14578c2ecf20Sopenharmony_ci break; 14588c2ecf20Sopenharmony_ci } 14598c2ecf20Sopenharmony_ci for_each_rtd_cpu_dais(rtd, i, cpu_dai) { 14608c2ecf20Sopenharmony_ci unsigned int fmt = dai_fmt; 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci if (cpu_dai->component->driver->non_legacy_dai_naming) 14638c2ecf20Sopenharmony_ci fmt = inv_dai_fmt; 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci ret = snd_soc_dai_set_fmt(cpu_dai, fmt); 14668c2ecf20Sopenharmony_ci if (ret != 0 && ret != -ENOTSUPP) { 14678c2ecf20Sopenharmony_ci dev_warn(cpu_dai->dev, 14688c2ecf20Sopenharmony_ci "ASoC: Failed to set DAI format: %d\n", ret); 14698c2ecf20Sopenharmony_ci return ret; 14708c2ecf20Sopenharmony_ci } 14718c2ecf20Sopenharmony_ci } 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ci return 0; 14748c2ecf20Sopenharmony_ci} 14758c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_runtime_set_dai_fmt); 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci#ifdef CONFIG_DMI 14788c2ecf20Sopenharmony_ci/* 14798c2ecf20Sopenharmony_ci * If a DMI filed contain strings in this blacklist (e.g. 14808c2ecf20Sopenharmony_ci * "Type2 - Board Manufacturer" or "Type1 - TBD by OEM"), it will be taken 14818c2ecf20Sopenharmony_ci * as invalid and dropped when setting the card long name from DMI info. 14828c2ecf20Sopenharmony_ci */ 14838c2ecf20Sopenharmony_cistatic const char * const dmi_blacklist[] = { 14848c2ecf20Sopenharmony_ci "To be filled by OEM", 14858c2ecf20Sopenharmony_ci "TBD by OEM", 14868c2ecf20Sopenharmony_ci "Default String", 14878c2ecf20Sopenharmony_ci "Board Manufacturer", 14888c2ecf20Sopenharmony_ci "Board Vendor Name", 14898c2ecf20Sopenharmony_ci "Board Product Name", 14908c2ecf20Sopenharmony_ci NULL, /* terminator */ 14918c2ecf20Sopenharmony_ci}; 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci/* 14948c2ecf20Sopenharmony_ci * Trim special characters, and replace '-' with '_' since '-' is used to 14958c2ecf20Sopenharmony_ci * separate different DMI fields in the card long name. Only number and 14968c2ecf20Sopenharmony_ci * alphabet characters and a few separator characters are kept. 14978c2ecf20Sopenharmony_ci */ 14988c2ecf20Sopenharmony_cistatic void cleanup_dmi_name(char *name) 14998c2ecf20Sopenharmony_ci{ 15008c2ecf20Sopenharmony_ci int i, j = 0; 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_ci for (i = 0; name[i]; i++) { 15038c2ecf20Sopenharmony_ci if (isalnum(name[i]) || (name[i] == '.') 15048c2ecf20Sopenharmony_ci || (name[i] == '_')) 15058c2ecf20Sopenharmony_ci name[j++] = name[i]; 15068c2ecf20Sopenharmony_ci else if (name[i] == '-') 15078c2ecf20Sopenharmony_ci name[j++] = '_'; 15088c2ecf20Sopenharmony_ci } 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_ci name[j] = '\0'; 15118c2ecf20Sopenharmony_ci} 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci/* 15148c2ecf20Sopenharmony_ci * Check if a DMI field is valid, i.e. not containing any string 15158c2ecf20Sopenharmony_ci * in the black list. 15168c2ecf20Sopenharmony_ci */ 15178c2ecf20Sopenharmony_cistatic int is_dmi_valid(const char *field) 15188c2ecf20Sopenharmony_ci{ 15198c2ecf20Sopenharmony_ci int i = 0; 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci while (dmi_blacklist[i]) { 15228c2ecf20Sopenharmony_ci if (strstr(field, dmi_blacklist[i])) 15238c2ecf20Sopenharmony_ci return 0; 15248c2ecf20Sopenharmony_ci i++; 15258c2ecf20Sopenharmony_ci } 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci return 1; 15288c2ecf20Sopenharmony_ci} 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci/* 15318c2ecf20Sopenharmony_ci * Append a string to card->dmi_longname with character cleanups. 15328c2ecf20Sopenharmony_ci */ 15338c2ecf20Sopenharmony_cistatic void append_dmi_string(struct snd_soc_card *card, const char *str) 15348c2ecf20Sopenharmony_ci{ 15358c2ecf20Sopenharmony_ci char *dst = card->dmi_longname; 15368c2ecf20Sopenharmony_ci size_t dst_len = sizeof(card->dmi_longname); 15378c2ecf20Sopenharmony_ci size_t len; 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci len = strlen(dst); 15408c2ecf20Sopenharmony_ci snprintf(dst + len, dst_len - len, "-%s", str); 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci len++; /* skip the separator "-" */ 15438c2ecf20Sopenharmony_ci if (len < dst_len) 15448c2ecf20Sopenharmony_ci cleanup_dmi_name(dst + len); 15458c2ecf20Sopenharmony_ci} 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci/** 15488c2ecf20Sopenharmony_ci * snd_soc_set_dmi_name() - Register DMI names to card 15498c2ecf20Sopenharmony_ci * @card: The card to register DMI names 15508c2ecf20Sopenharmony_ci * @flavour: The flavour "differentiator" for the card amongst its peers. 15518c2ecf20Sopenharmony_ci * 15528c2ecf20Sopenharmony_ci * An Intel machine driver may be used by many different devices but are 15538c2ecf20Sopenharmony_ci * difficult for userspace to differentiate, since machine drivers ususally 15548c2ecf20Sopenharmony_ci * use their own name as the card short name and leave the card long name 15558c2ecf20Sopenharmony_ci * blank. To differentiate such devices and fix bugs due to lack of 15568c2ecf20Sopenharmony_ci * device-specific configurations, this function allows DMI info to be used 15578c2ecf20Sopenharmony_ci * as the sound card long name, in the format of 15588c2ecf20Sopenharmony_ci * "vendor-product-version-board" 15598c2ecf20Sopenharmony_ci * (Character '-' is used to separate different DMI fields here). 15608c2ecf20Sopenharmony_ci * This will help the user space to load the device-specific Use Case Manager 15618c2ecf20Sopenharmony_ci * (UCM) configurations for the card. 15628c2ecf20Sopenharmony_ci * 15638c2ecf20Sopenharmony_ci * Possible card long names may be: 15648c2ecf20Sopenharmony_ci * DellInc.-XPS139343-01-0310JH 15658c2ecf20Sopenharmony_ci * ASUSTeKCOMPUTERINC.-T100TA-1.0-T100TA 15668c2ecf20Sopenharmony_ci * Circuitco-MinnowboardMaxD0PLATFORM-D0-MinnowBoardMAX 15678c2ecf20Sopenharmony_ci * 15688c2ecf20Sopenharmony_ci * This function also supports flavoring the card longname to provide 15698c2ecf20Sopenharmony_ci * the extra differentiation, like "vendor-product-version-board-flavor". 15708c2ecf20Sopenharmony_ci * 15718c2ecf20Sopenharmony_ci * We only keep number and alphabet characters and a few separator characters 15728c2ecf20Sopenharmony_ci * in the card long name since UCM in the user space uses the card long names 15738c2ecf20Sopenharmony_ci * as card configuration directory names and AudoConf cannot support special 15748c2ecf20Sopenharmony_ci * charactors like SPACE. 15758c2ecf20Sopenharmony_ci * 15768c2ecf20Sopenharmony_ci * Returns 0 on success, otherwise a negative error code. 15778c2ecf20Sopenharmony_ci */ 15788c2ecf20Sopenharmony_ciint snd_soc_set_dmi_name(struct snd_soc_card *card, const char *flavour) 15798c2ecf20Sopenharmony_ci{ 15808c2ecf20Sopenharmony_ci const char *vendor, *product, *product_version, *board; 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci if (card->long_name) 15838c2ecf20Sopenharmony_ci return 0; /* long name already set by driver or from DMI */ 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci if (!is_acpi_device_node(card->dev->fwnode)) 15868c2ecf20Sopenharmony_ci return 0; 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci /* make up dmi long name as: vendor-product-version-board */ 15898c2ecf20Sopenharmony_ci vendor = dmi_get_system_info(DMI_BOARD_VENDOR); 15908c2ecf20Sopenharmony_ci if (!vendor || !is_dmi_valid(vendor)) { 15918c2ecf20Sopenharmony_ci dev_warn(card->dev, "ASoC: no DMI vendor name!\n"); 15928c2ecf20Sopenharmony_ci return 0; 15938c2ecf20Sopenharmony_ci } 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci snprintf(card->dmi_longname, sizeof(card->dmi_longname), "%s", vendor); 15968c2ecf20Sopenharmony_ci cleanup_dmi_name(card->dmi_longname); 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci product = dmi_get_system_info(DMI_PRODUCT_NAME); 15998c2ecf20Sopenharmony_ci if (product && is_dmi_valid(product)) { 16008c2ecf20Sopenharmony_ci append_dmi_string(card, product); 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci /* 16038c2ecf20Sopenharmony_ci * some vendors like Lenovo may only put a self-explanatory 16048c2ecf20Sopenharmony_ci * name in the product version field 16058c2ecf20Sopenharmony_ci */ 16068c2ecf20Sopenharmony_ci product_version = dmi_get_system_info(DMI_PRODUCT_VERSION); 16078c2ecf20Sopenharmony_ci if (product_version && is_dmi_valid(product_version)) 16088c2ecf20Sopenharmony_ci append_dmi_string(card, product_version); 16098c2ecf20Sopenharmony_ci } 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci board = dmi_get_system_info(DMI_BOARD_NAME); 16128c2ecf20Sopenharmony_ci if (board && is_dmi_valid(board)) { 16138c2ecf20Sopenharmony_ci if (!product || strcasecmp(board, product)) 16148c2ecf20Sopenharmony_ci append_dmi_string(card, board); 16158c2ecf20Sopenharmony_ci } else if (!product) { 16168c2ecf20Sopenharmony_ci /* fall back to using legacy name */ 16178c2ecf20Sopenharmony_ci dev_warn(card->dev, "ASoC: no DMI board/product name!\n"); 16188c2ecf20Sopenharmony_ci return 0; 16198c2ecf20Sopenharmony_ci } 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ci /* Add flavour to dmi long name */ 16228c2ecf20Sopenharmony_ci if (flavour) 16238c2ecf20Sopenharmony_ci append_dmi_string(card, flavour); 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_ci /* set the card long name */ 16268c2ecf20Sopenharmony_ci card->long_name = card->dmi_longname; 16278c2ecf20Sopenharmony_ci 16288c2ecf20Sopenharmony_ci return 0; 16298c2ecf20Sopenharmony_ci} 16308c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_set_dmi_name); 16318c2ecf20Sopenharmony_ci#endif /* CONFIG_DMI */ 16328c2ecf20Sopenharmony_ci 16338c2ecf20Sopenharmony_cistatic void soc_check_tplg_fes(struct snd_soc_card *card) 16348c2ecf20Sopenharmony_ci{ 16358c2ecf20Sopenharmony_ci struct snd_soc_component *component; 16368c2ecf20Sopenharmony_ci const struct snd_soc_component_driver *comp_drv; 16378c2ecf20Sopenharmony_ci struct snd_soc_dai_link *dai_link; 16388c2ecf20Sopenharmony_ci int i; 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci for_each_component(component) { 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_ci /* does this component override BEs ? */ 16438c2ecf20Sopenharmony_ci if (!component->driver->ignore_machine) 16448c2ecf20Sopenharmony_ci continue; 16458c2ecf20Sopenharmony_ci 16468c2ecf20Sopenharmony_ci /* for this machine ? */ 16478c2ecf20Sopenharmony_ci if (!strcmp(component->driver->ignore_machine, 16488c2ecf20Sopenharmony_ci card->dev->driver->name)) 16498c2ecf20Sopenharmony_ci goto match; 16508c2ecf20Sopenharmony_ci if (strcmp(component->driver->ignore_machine, 16518c2ecf20Sopenharmony_ci dev_name(card->dev))) 16528c2ecf20Sopenharmony_ci continue; 16538c2ecf20Sopenharmony_cimatch: 16548c2ecf20Sopenharmony_ci /* machine matches, so override the rtd data */ 16558c2ecf20Sopenharmony_ci for_each_card_prelinks(card, i, dai_link) { 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci /* ignore this FE */ 16588c2ecf20Sopenharmony_ci if (dai_link->dynamic) { 16598c2ecf20Sopenharmony_ci dai_link->ignore = true; 16608c2ecf20Sopenharmony_ci continue; 16618c2ecf20Sopenharmony_ci } 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci dev_dbg(card->dev, "info: override BE DAI link %s\n", 16648c2ecf20Sopenharmony_ci card->dai_link[i].name); 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci /* override platform component */ 16678c2ecf20Sopenharmony_ci if (!dai_link->platforms) { 16688c2ecf20Sopenharmony_ci dev_err(card->dev, "init platform error"); 16698c2ecf20Sopenharmony_ci continue; 16708c2ecf20Sopenharmony_ci } 16718c2ecf20Sopenharmony_ci dai_link->platforms->name = component->name; 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci /* convert non BE into BE */ 16748c2ecf20Sopenharmony_ci if (!dai_link->no_pcm) { 16758c2ecf20Sopenharmony_ci dai_link->no_pcm = 1; 16768c2ecf20Sopenharmony_ci 16778c2ecf20Sopenharmony_ci if (dai_link->dpcm_playback) 16788c2ecf20Sopenharmony_ci dev_warn(card->dev, 16798c2ecf20Sopenharmony_ci "invalid configuration, dailink %s has flags no_pcm=0 and dpcm_playback=1\n", 16808c2ecf20Sopenharmony_ci dai_link->name); 16818c2ecf20Sopenharmony_ci if (dai_link->dpcm_capture) 16828c2ecf20Sopenharmony_ci dev_warn(card->dev, 16838c2ecf20Sopenharmony_ci "invalid configuration, dailink %s has flags no_pcm=0 and dpcm_capture=1\n", 16848c2ecf20Sopenharmony_ci dai_link->name); 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_ci /* convert normal link into DPCM one */ 16878c2ecf20Sopenharmony_ci if (!(dai_link->dpcm_playback || 16888c2ecf20Sopenharmony_ci dai_link->dpcm_capture)) { 16898c2ecf20Sopenharmony_ci dai_link->dpcm_playback = !dai_link->capture_only; 16908c2ecf20Sopenharmony_ci dai_link->dpcm_capture = !dai_link->playback_only; 16918c2ecf20Sopenharmony_ci } 16928c2ecf20Sopenharmony_ci } 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_ci /* 16958c2ecf20Sopenharmony_ci * override any BE fixups 16968c2ecf20Sopenharmony_ci * see 16978c2ecf20Sopenharmony_ci * snd_soc_link_be_hw_params_fixup() 16988c2ecf20Sopenharmony_ci */ 16998c2ecf20Sopenharmony_ci dai_link->be_hw_params_fixup = 17008c2ecf20Sopenharmony_ci component->driver->be_hw_params_fixup; 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_ci /* 17038c2ecf20Sopenharmony_ci * most BE links don't set stream name, so set it to 17048c2ecf20Sopenharmony_ci * dai link name if it's NULL to help bind widgets. 17058c2ecf20Sopenharmony_ci */ 17068c2ecf20Sopenharmony_ci if (!dai_link->stream_name) 17078c2ecf20Sopenharmony_ci dai_link->stream_name = dai_link->name; 17088c2ecf20Sopenharmony_ci } 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_ci /* Inform userspace we are using alternate topology */ 17118c2ecf20Sopenharmony_ci if (component->driver->topology_name_prefix) { 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci /* topology shortname created? */ 17148c2ecf20Sopenharmony_ci if (!card->topology_shortname_created) { 17158c2ecf20Sopenharmony_ci comp_drv = component->driver; 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci snprintf(card->topology_shortname, 32, "%s-%s", 17188c2ecf20Sopenharmony_ci comp_drv->topology_name_prefix, 17198c2ecf20Sopenharmony_ci card->name); 17208c2ecf20Sopenharmony_ci card->topology_shortname_created = true; 17218c2ecf20Sopenharmony_ci } 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci /* use topology shortname */ 17248c2ecf20Sopenharmony_ci card->name = card->topology_shortname; 17258c2ecf20Sopenharmony_ci } 17268c2ecf20Sopenharmony_ci } 17278c2ecf20Sopenharmony_ci} 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_ci#define soc_setup_card_name(name, name1, name2, norm) \ 17308c2ecf20Sopenharmony_ci __soc_setup_card_name(name, sizeof(name), name1, name2, norm) 17318c2ecf20Sopenharmony_cistatic void __soc_setup_card_name(char *name, int len, 17328c2ecf20Sopenharmony_ci const char *name1, const char *name2, 17338c2ecf20Sopenharmony_ci int normalization) 17348c2ecf20Sopenharmony_ci{ 17358c2ecf20Sopenharmony_ci int i; 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci snprintf(name, len, "%s", name1 ? name1 : name2); 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_ci if (!normalization) 17408c2ecf20Sopenharmony_ci return; 17418c2ecf20Sopenharmony_ci 17428c2ecf20Sopenharmony_ci /* 17438c2ecf20Sopenharmony_ci * Name normalization 17448c2ecf20Sopenharmony_ci * 17458c2ecf20Sopenharmony_ci * The driver name is somewhat special, as it's used as a key for 17468c2ecf20Sopenharmony_ci * searches in the user-space. 17478c2ecf20Sopenharmony_ci * 17488c2ecf20Sopenharmony_ci * ex) 17498c2ecf20Sopenharmony_ci * "abcd??efg" -> "abcd__efg" 17508c2ecf20Sopenharmony_ci */ 17518c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 17528c2ecf20Sopenharmony_ci switch (name[i]) { 17538c2ecf20Sopenharmony_ci case '_': 17548c2ecf20Sopenharmony_ci case '-': 17558c2ecf20Sopenharmony_ci case '\0': 17568c2ecf20Sopenharmony_ci break; 17578c2ecf20Sopenharmony_ci default: 17588c2ecf20Sopenharmony_ci if (!isalnum(name[i])) 17598c2ecf20Sopenharmony_ci name[i] = '_'; 17608c2ecf20Sopenharmony_ci break; 17618c2ecf20Sopenharmony_ci } 17628c2ecf20Sopenharmony_ci } 17638c2ecf20Sopenharmony_ci} 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_cistatic void soc_cleanup_card_resources(struct snd_soc_card *card) 17668c2ecf20Sopenharmony_ci{ 17678c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd, *n; 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_ci if (card->snd_card) 17708c2ecf20Sopenharmony_ci snd_card_disconnect_sync(card->snd_card); 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci snd_soc_dapm_shutdown(card); 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci /* remove and free each DAI */ 17758c2ecf20Sopenharmony_ci soc_remove_link_dais(card); 17768c2ecf20Sopenharmony_ci soc_remove_link_components(card); 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_ci for_each_card_rtds_safe(card, rtd, n) 17798c2ecf20Sopenharmony_ci snd_soc_remove_pcm_runtime(card, rtd); 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_ci /* remove auxiliary devices */ 17828c2ecf20Sopenharmony_ci soc_remove_aux_devices(card); 17838c2ecf20Sopenharmony_ci soc_unbind_aux_dev(card); 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci snd_soc_dapm_free(&card->dapm); 17868c2ecf20Sopenharmony_ci soc_cleanup_card_debugfs(card); 17878c2ecf20Sopenharmony_ci 17888c2ecf20Sopenharmony_ci /* remove the card */ 17898c2ecf20Sopenharmony_ci snd_soc_card_remove(card); 17908c2ecf20Sopenharmony_ci 17918c2ecf20Sopenharmony_ci if (card->snd_card) { 17928c2ecf20Sopenharmony_ci snd_card_free(card->snd_card); 17938c2ecf20Sopenharmony_ci card->snd_card = NULL; 17948c2ecf20Sopenharmony_ci } 17958c2ecf20Sopenharmony_ci} 17968c2ecf20Sopenharmony_ci 17978c2ecf20Sopenharmony_cistatic void snd_soc_unbind_card(struct snd_soc_card *card, bool unregister) 17988c2ecf20Sopenharmony_ci{ 17998c2ecf20Sopenharmony_ci if (card->instantiated) { 18008c2ecf20Sopenharmony_ci card->instantiated = false; 18018c2ecf20Sopenharmony_ci snd_soc_flush_all_delayed_work(card); 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci soc_cleanup_card_resources(card); 18048c2ecf20Sopenharmony_ci if (!unregister) 18058c2ecf20Sopenharmony_ci list_add(&card->list, &unbind_card_list); 18068c2ecf20Sopenharmony_ci } else { 18078c2ecf20Sopenharmony_ci if (unregister) 18088c2ecf20Sopenharmony_ci list_del(&card->list); 18098c2ecf20Sopenharmony_ci } 18108c2ecf20Sopenharmony_ci} 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_cistatic int snd_soc_bind_card(struct snd_soc_card *card) 18138c2ecf20Sopenharmony_ci{ 18148c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 18158c2ecf20Sopenharmony_ci struct snd_soc_component *component; 18168c2ecf20Sopenharmony_ci struct snd_soc_dai_link *dai_link; 18178c2ecf20Sopenharmony_ci int ret, i; 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_ci mutex_lock(&client_mutex); 18208c2ecf20Sopenharmony_ci mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT); 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci snd_soc_dapm_init(&card->dapm, card, NULL); 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_ci /* check whether any platform is ignore machine FE and using topology */ 18258c2ecf20Sopenharmony_ci soc_check_tplg_fes(card); 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci /* bind aux_devs too */ 18288c2ecf20Sopenharmony_ci ret = soc_bind_aux_dev(card); 18298c2ecf20Sopenharmony_ci if (ret < 0) 18308c2ecf20Sopenharmony_ci goto probe_end; 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ci /* add predefined DAI links to the list */ 18338c2ecf20Sopenharmony_ci card->num_rtd = 0; 18348c2ecf20Sopenharmony_ci for_each_card_prelinks(card, i, dai_link) { 18358c2ecf20Sopenharmony_ci ret = snd_soc_add_pcm_runtime(card, dai_link); 18368c2ecf20Sopenharmony_ci if (ret < 0) 18378c2ecf20Sopenharmony_ci goto probe_end; 18388c2ecf20Sopenharmony_ci } 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_ci /* card bind complete so register a sound card */ 18418c2ecf20Sopenharmony_ci ret = snd_card_new(card->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, 18428c2ecf20Sopenharmony_ci card->owner, 0, &card->snd_card); 18438c2ecf20Sopenharmony_ci if (ret < 0) { 18448c2ecf20Sopenharmony_ci dev_err(card->dev, 18458c2ecf20Sopenharmony_ci "ASoC: can't create sound card for card %s: %d\n", 18468c2ecf20Sopenharmony_ci card->name, ret); 18478c2ecf20Sopenharmony_ci goto probe_end; 18488c2ecf20Sopenharmony_ci } 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_ci soc_init_card_debugfs(card); 18518c2ecf20Sopenharmony_ci 18528c2ecf20Sopenharmony_ci soc_resume_init(card); 18538c2ecf20Sopenharmony_ci 18548c2ecf20Sopenharmony_ci ret = snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets, 18558c2ecf20Sopenharmony_ci card->num_dapm_widgets); 18568c2ecf20Sopenharmony_ci if (ret < 0) 18578c2ecf20Sopenharmony_ci goto probe_end; 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_ci ret = snd_soc_dapm_new_controls(&card->dapm, card->of_dapm_widgets, 18608c2ecf20Sopenharmony_ci card->num_of_dapm_widgets); 18618c2ecf20Sopenharmony_ci if (ret < 0) 18628c2ecf20Sopenharmony_ci goto probe_end; 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_ci /* initialise the sound card only once */ 18658c2ecf20Sopenharmony_ci ret = snd_soc_card_probe(card); 18668c2ecf20Sopenharmony_ci if (ret < 0) 18678c2ecf20Sopenharmony_ci goto probe_end; 18688c2ecf20Sopenharmony_ci 18698c2ecf20Sopenharmony_ci /* probe all components used by DAI links on this card */ 18708c2ecf20Sopenharmony_ci ret = soc_probe_link_components(card); 18718c2ecf20Sopenharmony_ci if (ret < 0) { 18728c2ecf20Sopenharmony_ci dev_err(card->dev, 18738c2ecf20Sopenharmony_ci "ASoC: failed to instantiate card %d\n", ret); 18748c2ecf20Sopenharmony_ci goto probe_end; 18758c2ecf20Sopenharmony_ci } 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_ci /* probe auxiliary components */ 18788c2ecf20Sopenharmony_ci ret = soc_probe_aux_devices(card); 18798c2ecf20Sopenharmony_ci if (ret < 0) { 18808c2ecf20Sopenharmony_ci dev_err(card->dev, 18818c2ecf20Sopenharmony_ci "ASoC: failed to probe aux component %d\n", ret); 18828c2ecf20Sopenharmony_ci goto probe_end; 18838c2ecf20Sopenharmony_ci } 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci /* probe all DAI links on this card */ 18868c2ecf20Sopenharmony_ci ret = soc_probe_link_dais(card); 18878c2ecf20Sopenharmony_ci if (ret < 0) { 18888c2ecf20Sopenharmony_ci dev_err(card->dev, 18898c2ecf20Sopenharmony_ci "ASoC: failed to instantiate card %d\n", ret); 18908c2ecf20Sopenharmony_ci goto probe_end; 18918c2ecf20Sopenharmony_ci } 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci for_each_card_rtds(card, rtd) { 18948c2ecf20Sopenharmony_ci ret = soc_init_pcm_runtime(card, rtd); 18958c2ecf20Sopenharmony_ci if (ret < 0) 18968c2ecf20Sopenharmony_ci goto probe_end; 18978c2ecf20Sopenharmony_ci } 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_ci snd_soc_dapm_link_dai_widgets(card); 19008c2ecf20Sopenharmony_ci snd_soc_dapm_connect_dai_link_widgets(card); 19018c2ecf20Sopenharmony_ci 19028c2ecf20Sopenharmony_ci ret = snd_soc_add_card_controls(card, card->controls, 19038c2ecf20Sopenharmony_ci card->num_controls); 19048c2ecf20Sopenharmony_ci if (ret < 0) 19058c2ecf20Sopenharmony_ci goto probe_end; 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_ci ret = snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes, 19088c2ecf20Sopenharmony_ci card->num_dapm_routes); 19098c2ecf20Sopenharmony_ci if (ret < 0) { 19108c2ecf20Sopenharmony_ci if (card->disable_route_checks) { 19118c2ecf20Sopenharmony_ci dev_info(card->dev, 19128c2ecf20Sopenharmony_ci "%s: disable_route_checks set, ignoring errors on add_routes\n", 19138c2ecf20Sopenharmony_ci __func__); 19148c2ecf20Sopenharmony_ci } else { 19158c2ecf20Sopenharmony_ci dev_err(card->dev, 19168c2ecf20Sopenharmony_ci "%s: snd_soc_dapm_add_routes failed: %d\n", 19178c2ecf20Sopenharmony_ci __func__, ret); 19188c2ecf20Sopenharmony_ci goto probe_end; 19198c2ecf20Sopenharmony_ci } 19208c2ecf20Sopenharmony_ci } 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_ci ret = snd_soc_dapm_add_routes(&card->dapm, card->of_dapm_routes, 19238c2ecf20Sopenharmony_ci card->num_of_dapm_routes); 19248c2ecf20Sopenharmony_ci if (ret < 0) 19258c2ecf20Sopenharmony_ci goto probe_end; 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_ci /* try to set some sane longname if DMI is available */ 19288c2ecf20Sopenharmony_ci snd_soc_set_dmi_name(card, NULL); 19298c2ecf20Sopenharmony_ci 19308c2ecf20Sopenharmony_ci soc_setup_card_name(card->snd_card->shortname, 19318c2ecf20Sopenharmony_ci card->name, NULL, 0); 19328c2ecf20Sopenharmony_ci soc_setup_card_name(card->snd_card->longname, 19338c2ecf20Sopenharmony_ci card->long_name, card->name, 0); 19348c2ecf20Sopenharmony_ci soc_setup_card_name(card->snd_card->driver, 19358c2ecf20Sopenharmony_ci card->driver_name, card->name, 1); 19368c2ecf20Sopenharmony_ci 19378c2ecf20Sopenharmony_ci if (card->components) { 19388c2ecf20Sopenharmony_ci /* the current implementation of snd_component_add() accepts */ 19398c2ecf20Sopenharmony_ci /* multiple components in the string separated by space, */ 19408c2ecf20Sopenharmony_ci /* but the string collision (identical string) check might */ 19418c2ecf20Sopenharmony_ci /* not work correctly */ 19428c2ecf20Sopenharmony_ci ret = snd_component_add(card->snd_card, card->components); 19438c2ecf20Sopenharmony_ci if (ret < 0) { 19448c2ecf20Sopenharmony_ci dev_err(card->dev, "ASoC: %s snd_component_add() failed: %d\n", 19458c2ecf20Sopenharmony_ci card->name, ret); 19468c2ecf20Sopenharmony_ci goto probe_end; 19478c2ecf20Sopenharmony_ci } 19488c2ecf20Sopenharmony_ci } 19498c2ecf20Sopenharmony_ci 19508c2ecf20Sopenharmony_ci ret = snd_soc_card_late_probe(card); 19518c2ecf20Sopenharmony_ci if (ret < 0) 19528c2ecf20Sopenharmony_ci goto probe_end; 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_ci snd_soc_dapm_new_widgets(card); 19558c2ecf20Sopenharmony_ci 19568c2ecf20Sopenharmony_ci ret = snd_card_register(card->snd_card); 19578c2ecf20Sopenharmony_ci if (ret < 0) { 19588c2ecf20Sopenharmony_ci dev_err(card->dev, "ASoC: failed to register soundcard %d\n", 19598c2ecf20Sopenharmony_ci ret); 19608c2ecf20Sopenharmony_ci goto probe_end; 19618c2ecf20Sopenharmony_ci } 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_ci card->instantiated = 1; 19648c2ecf20Sopenharmony_ci dapm_mark_endpoints_dirty(card); 19658c2ecf20Sopenharmony_ci snd_soc_dapm_sync(&card->dapm); 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_ci /* deactivate pins to sleep state */ 19688c2ecf20Sopenharmony_ci for_each_card_components(card, component) 19698c2ecf20Sopenharmony_ci if (!snd_soc_component_active(component)) 19708c2ecf20Sopenharmony_ci pinctrl_pm_select_sleep_state(component->dev); 19718c2ecf20Sopenharmony_ci 19728c2ecf20Sopenharmony_ciprobe_end: 19738c2ecf20Sopenharmony_ci if (ret < 0) 19748c2ecf20Sopenharmony_ci soc_cleanup_card_resources(card); 19758c2ecf20Sopenharmony_ci 19768c2ecf20Sopenharmony_ci mutex_unlock(&card->mutex); 19778c2ecf20Sopenharmony_ci mutex_unlock(&client_mutex); 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci return ret; 19808c2ecf20Sopenharmony_ci} 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_ci/* probes a new socdev */ 19838c2ecf20Sopenharmony_cistatic int soc_probe(struct platform_device *pdev) 19848c2ecf20Sopenharmony_ci{ 19858c2ecf20Sopenharmony_ci struct snd_soc_card *card = platform_get_drvdata(pdev); 19868c2ecf20Sopenharmony_ci 19878c2ecf20Sopenharmony_ci /* 19888c2ecf20Sopenharmony_ci * no card, so machine driver should be registering card 19898c2ecf20Sopenharmony_ci * we should not be here in that case so ret error 19908c2ecf20Sopenharmony_ci */ 19918c2ecf20Sopenharmony_ci if (!card) 19928c2ecf20Sopenharmony_ci return -EINVAL; 19938c2ecf20Sopenharmony_ci 19948c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, 19958c2ecf20Sopenharmony_ci "ASoC: machine %s should use snd_soc_register_card()\n", 19968c2ecf20Sopenharmony_ci card->name); 19978c2ecf20Sopenharmony_ci 19988c2ecf20Sopenharmony_ci /* Bodge while we unpick instantiation */ 19998c2ecf20Sopenharmony_ci card->dev = &pdev->dev; 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_ci return devm_snd_soc_register_card(&pdev->dev, card); 20028c2ecf20Sopenharmony_ci} 20038c2ecf20Sopenharmony_ci 20048c2ecf20Sopenharmony_ciint snd_soc_poweroff(struct device *dev) 20058c2ecf20Sopenharmony_ci{ 20068c2ecf20Sopenharmony_ci struct snd_soc_card *card = dev_get_drvdata(dev); 20078c2ecf20Sopenharmony_ci struct snd_soc_component *component; 20088c2ecf20Sopenharmony_ci 20098c2ecf20Sopenharmony_ci if (!card->instantiated) 20108c2ecf20Sopenharmony_ci return 0; 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_ci /* 20138c2ecf20Sopenharmony_ci * Flush out pmdown_time work - we actually do want to run it 20148c2ecf20Sopenharmony_ci * now, we're shutting down so no imminent restart. 20158c2ecf20Sopenharmony_ci */ 20168c2ecf20Sopenharmony_ci snd_soc_flush_all_delayed_work(card); 20178c2ecf20Sopenharmony_ci 20188c2ecf20Sopenharmony_ci snd_soc_dapm_shutdown(card); 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_ci /* deactivate pins to sleep state */ 20218c2ecf20Sopenharmony_ci for_each_card_components(card, component) 20228c2ecf20Sopenharmony_ci pinctrl_pm_select_sleep_state(component->dev); 20238c2ecf20Sopenharmony_ci 20248c2ecf20Sopenharmony_ci return 0; 20258c2ecf20Sopenharmony_ci} 20268c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_poweroff); 20278c2ecf20Sopenharmony_ci 20288c2ecf20Sopenharmony_ciconst struct dev_pm_ops snd_soc_pm_ops = { 20298c2ecf20Sopenharmony_ci .suspend = snd_soc_suspend, 20308c2ecf20Sopenharmony_ci .resume = snd_soc_resume, 20318c2ecf20Sopenharmony_ci .freeze = snd_soc_suspend, 20328c2ecf20Sopenharmony_ci .thaw = snd_soc_resume, 20338c2ecf20Sopenharmony_ci .poweroff = snd_soc_poweroff, 20348c2ecf20Sopenharmony_ci .restore = snd_soc_resume, 20358c2ecf20Sopenharmony_ci}; 20368c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_pm_ops); 20378c2ecf20Sopenharmony_ci 20388c2ecf20Sopenharmony_ci/* ASoC platform driver */ 20398c2ecf20Sopenharmony_cistatic struct platform_driver soc_driver = { 20408c2ecf20Sopenharmony_ci .driver = { 20418c2ecf20Sopenharmony_ci .name = "soc-audio", 20428c2ecf20Sopenharmony_ci .pm = &snd_soc_pm_ops, 20438c2ecf20Sopenharmony_ci }, 20448c2ecf20Sopenharmony_ci .probe = soc_probe, 20458c2ecf20Sopenharmony_ci}; 20468c2ecf20Sopenharmony_ci 20478c2ecf20Sopenharmony_ci/** 20488c2ecf20Sopenharmony_ci * snd_soc_cnew - create new control 20498c2ecf20Sopenharmony_ci * @_template: control template 20508c2ecf20Sopenharmony_ci * @data: control private data 20518c2ecf20Sopenharmony_ci * @long_name: control long name 20528c2ecf20Sopenharmony_ci * @prefix: control name prefix 20538c2ecf20Sopenharmony_ci * 20548c2ecf20Sopenharmony_ci * Create a new mixer control from a template control. 20558c2ecf20Sopenharmony_ci * 20568c2ecf20Sopenharmony_ci * Returns 0 for success, else error. 20578c2ecf20Sopenharmony_ci */ 20588c2ecf20Sopenharmony_cistruct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template, 20598c2ecf20Sopenharmony_ci void *data, const char *long_name, 20608c2ecf20Sopenharmony_ci const char *prefix) 20618c2ecf20Sopenharmony_ci{ 20628c2ecf20Sopenharmony_ci struct snd_kcontrol_new template; 20638c2ecf20Sopenharmony_ci struct snd_kcontrol *kcontrol; 20648c2ecf20Sopenharmony_ci char *name = NULL; 20658c2ecf20Sopenharmony_ci 20668c2ecf20Sopenharmony_ci memcpy(&template, _template, sizeof(template)); 20678c2ecf20Sopenharmony_ci template.index = 0; 20688c2ecf20Sopenharmony_ci 20698c2ecf20Sopenharmony_ci if (!long_name) 20708c2ecf20Sopenharmony_ci long_name = template.name; 20718c2ecf20Sopenharmony_ci 20728c2ecf20Sopenharmony_ci if (prefix) { 20738c2ecf20Sopenharmony_ci name = kasprintf(GFP_KERNEL, "%s %s", prefix, long_name); 20748c2ecf20Sopenharmony_ci if (!name) 20758c2ecf20Sopenharmony_ci return NULL; 20768c2ecf20Sopenharmony_ci 20778c2ecf20Sopenharmony_ci template.name = name; 20788c2ecf20Sopenharmony_ci } else { 20798c2ecf20Sopenharmony_ci template.name = long_name; 20808c2ecf20Sopenharmony_ci } 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_ci kcontrol = snd_ctl_new1(&template, data); 20838c2ecf20Sopenharmony_ci 20848c2ecf20Sopenharmony_ci kfree(name); 20858c2ecf20Sopenharmony_ci 20868c2ecf20Sopenharmony_ci return kcontrol; 20878c2ecf20Sopenharmony_ci} 20888c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_cnew); 20898c2ecf20Sopenharmony_ci 20908c2ecf20Sopenharmony_cistatic int snd_soc_add_controls(struct snd_card *card, struct device *dev, 20918c2ecf20Sopenharmony_ci const struct snd_kcontrol_new *controls, int num_controls, 20928c2ecf20Sopenharmony_ci const char *prefix, void *data) 20938c2ecf20Sopenharmony_ci{ 20948c2ecf20Sopenharmony_ci int err, i; 20958c2ecf20Sopenharmony_ci 20968c2ecf20Sopenharmony_ci for (i = 0; i < num_controls; i++) { 20978c2ecf20Sopenharmony_ci const struct snd_kcontrol_new *control = &controls[i]; 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_ci err = snd_ctl_add(card, snd_soc_cnew(control, data, 21008c2ecf20Sopenharmony_ci control->name, prefix)); 21018c2ecf20Sopenharmony_ci if (err < 0) { 21028c2ecf20Sopenharmony_ci dev_err(dev, "ASoC: Failed to add %s: %d\n", 21038c2ecf20Sopenharmony_ci control->name, err); 21048c2ecf20Sopenharmony_ci return err; 21058c2ecf20Sopenharmony_ci } 21068c2ecf20Sopenharmony_ci } 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_ci return 0; 21098c2ecf20Sopenharmony_ci} 21108c2ecf20Sopenharmony_ci 21118c2ecf20Sopenharmony_ci/** 21128c2ecf20Sopenharmony_ci * snd_soc_add_component_controls - Add an array of controls to a component. 21138c2ecf20Sopenharmony_ci * 21148c2ecf20Sopenharmony_ci * @component: Component to add controls to 21158c2ecf20Sopenharmony_ci * @controls: Array of controls to add 21168c2ecf20Sopenharmony_ci * @num_controls: Number of elements in the array 21178c2ecf20Sopenharmony_ci * 21188c2ecf20Sopenharmony_ci * Return: 0 for success, else error. 21198c2ecf20Sopenharmony_ci */ 21208c2ecf20Sopenharmony_ciint snd_soc_add_component_controls(struct snd_soc_component *component, 21218c2ecf20Sopenharmony_ci const struct snd_kcontrol_new *controls, unsigned int num_controls) 21228c2ecf20Sopenharmony_ci{ 21238c2ecf20Sopenharmony_ci struct snd_card *card = component->card->snd_card; 21248c2ecf20Sopenharmony_ci 21258c2ecf20Sopenharmony_ci return snd_soc_add_controls(card, component->dev, controls, 21268c2ecf20Sopenharmony_ci num_controls, component->name_prefix, component); 21278c2ecf20Sopenharmony_ci} 21288c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_add_component_controls); 21298c2ecf20Sopenharmony_ci 21308c2ecf20Sopenharmony_ci/** 21318c2ecf20Sopenharmony_ci * snd_soc_add_card_controls - add an array of controls to a SoC card. 21328c2ecf20Sopenharmony_ci * Convenience function to add a list of controls. 21338c2ecf20Sopenharmony_ci * 21348c2ecf20Sopenharmony_ci * @soc_card: SoC card to add controls to 21358c2ecf20Sopenharmony_ci * @controls: array of controls to add 21368c2ecf20Sopenharmony_ci * @num_controls: number of elements in the array 21378c2ecf20Sopenharmony_ci * 21388c2ecf20Sopenharmony_ci * Return 0 for success, else error. 21398c2ecf20Sopenharmony_ci */ 21408c2ecf20Sopenharmony_ciint snd_soc_add_card_controls(struct snd_soc_card *soc_card, 21418c2ecf20Sopenharmony_ci const struct snd_kcontrol_new *controls, int num_controls) 21428c2ecf20Sopenharmony_ci{ 21438c2ecf20Sopenharmony_ci struct snd_card *card = soc_card->snd_card; 21448c2ecf20Sopenharmony_ci 21458c2ecf20Sopenharmony_ci return snd_soc_add_controls(card, soc_card->dev, controls, num_controls, 21468c2ecf20Sopenharmony_ci NULL, soc_card); 21478c2ecf20Sopenharmony_ci} 21488c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_add_card_controls); 21498c2ecf20Sopenharmony_ci 21508c2ecf20Sopenharmony_ci/** 21518c2ecf20Sopenharmony_ci * snd_soc_add_dai_controls - add an array of controls to a DAI. 21528c2ecf20Sopenharmony_ci * Convienience function to add a list of controls. 21538c2ecf20Sopenharmony_ci * 21548c2ecf20Sopenharmony_ci * @dai: DAI to add controls to 21558c2ecf20Sopenharmony_ci * @controls: array of controls to add 21568c2ecf20Sopenharmony_ci * @num_controls: number of elements in the array 21578c2ecf20Sopenharmony_ci * 21588c2ecf20Sopenharmony_ci * Return 0 for success, else error. 21598c2ecf20Sopenharmony_ci */ 21608c2ecf20Sopenharmony_ciint snd_soc_add_dai_controls(struct snd_soc_dai *dai, 21618c2ecf20Sopenharmony_ci const struct snd_kcontrol_new *controls, int num_controls) 21628c2ecf20Sopenharmony_ci{ 21638c2ecf20Sopenharmony_ci struct snd_card *card = dai->component->card->snd_card; 21648c2ecf20Sopenharmony_ci 21658c2ecf20Sopenharmony_ci return snd_soc_add_controls(card, dai->dev, controls, num_controls, 21668c2ecf20Sopenharmony_ci NULL, dai); 21678c2ecf20Sopenharmony_ci} 21688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_add_dai_controls); 21698c2ecf20Sopenharmony_ci 21708c2ecf20Sopenharmony_ci/** 21718c2ecf20Sopenharmony_ci * snd_soc_register_card - Register a card with the ASoC core 21728c2ecf20Sopenharmony_ci * 21738c2ecf20Sopenharmony_ci * @card: Card to register 21748c2ecf20Sopenharmony_ci * 21758c2ecf20Sopenharmony_ci */ 21768c2ecf20Sopenharmony_ciint snd_soc_register_card(struct snd_soc_card *card) 21778c2ecf20Sopenharmony_ci{ 21788c2ecf20Sopenharmony_ci if (!card->name || !card->dev) 21798c2ecf20Sopenharmony_ci return -EINVAL; 21808c2ecf20Sopenharmony_ci 21818c2ecf20Sopenharmony_ci dev_set_drvdata(card->dev, card); 21828c2ecf20Sopenharmony_ci 21838c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&card->widgets); 21848c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&card->paths); 21858c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&card->dapm_list); 21868c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&card->aux_comp_list); 21878c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&card->component_dev_list); 21888c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&card->list); 21898c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&card->rtd_list); 21908c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&card->dapm_dirty); 21918c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&card->dobj_list); 21928c2ecf20Sopenharmony_ci 21938c2ecf20Sopenharmony_ci card->instantiated = 0; 21948c2ecf20Sopenharmony_ci mutex_init(&card->mutex); 21958c2ecf20Sopenharmony_ci mutex_init(&card->dapm_mutex); 21968c2ecf20Sopenharmony_ci mutex_init(&card->pcm_mutex); 21978c2ecf20Sopenharmony_ci spin_lock_init(&card->dpcm_lock); 21988c2ecf20Sopenharmony_ci 21998c2ecf20Sopenharmony_ci return snd_soc_bind_card(card); 22008c2ecf20Sopenharmony_ci} 22018c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_register_card); 22028c2ecf20Sopenharmony_ci 22038c2ecf20Sopenharmony_ci/** 22048c2ecf20Sopenharmony_ci * snd_soc_unregister_card - Unregister a card with the ASoC core 22058c2ecf20Sopenharmony_ci * 22068c2ecf20Sopenharmony_ci * @card: Card to unregister 22078c2ecf20Sopenharmony_ci * 22088c2ecf20Sopenharmony_ci */ 22098c2ecf20Sopenharmony_ciint snd_soc_unregister_card(struct snd_soc_card *card) 22108c2ecf20Sopenharmony_ci{ 22118c2ecf20Sopenharmony_ci mutex_lock(&client_mutex); 22128c2ecf20Sopenharmony_ci snd_soc_unbind_card(card, true); 22138c2ecf20Sopenharmony_ci mutex_unlock(&client_mutex); 22148c2ecf20Sopenharmony_ci dev_dbg(card->dev, "ASoC: Unregistered card '%s'\n", card->name); 22158c2ecf20Sopenharmony_ci 22168c2ecf20Sopenharmony_ci return 0; 22178c2ecf20Sopenharmony_ci} 22188c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_unregister_card); 22198c2ecf20Sopenharmony_ci 22208c2ecf20Sopenharmony_ci/* 22218c2ecf20Sopenharmony_ci * Simplify DAI link configuration by removing ".-1" from device names 22228c2ecf20Sopenharmony_ci * and sanitizing names. 22238c2ecf20Sopenharmony_ci */ 22248c2ecf20Sopenharmony_cistatic char *fmt_single_name(struct device *dev, int *id) 22258c2ecf20Sopenharmony_ci{ 22268c2ecf20Sopenharmony_ci const char *devname = dev_name(dev); 22278c2ecf20Sopenharmony_ci char *found, *name; 22288c2ecf20Sopenharmony_ci int id1, id2; 22298c2ecf20Sopenharmony_ci 22308c2ecf20Sopenharmony_ci if (devname == NULL) 22318c2ecf20Sopenharmony_ci return NULL; 22328c2ecf20Sopenharmony_ci 22338c2ecf20Sopenharmony_ci name = devm_kstrdup(dev, devname, GFP_KERNEL); 22348c2ecf20Sopenharmony_ci if (!name) 22358c2ecf20Sopenharmony_ci return NULL; 22368c2ecf20Sopenharmony_ci 22378c2ecf20Sopenharmony_ci /* are we a "%s.%d" name (platform and SPI components) */ 22388c2ecf20Sopenharmony_ci found = strstr(name, dev->driver->name); 22398c2ecf20Sopenharmony_ci if (found) { 22408c2ecf20Sopenharmony_ci /* get ID */ 22418c2ecf20Sopenharmony_ci if (sscanf(&found[strlen(dev->driver->name)], ".%d", id) == 1) { 22428c2ecf20Sopenharmony_ci 22438c2ecf20Sopenharmony_ci /* discard ID from name if ID == -1 */ 22448c2ecf20Sopenharmony_ci if (*id == -1) 22458c2ecf20Sopenharmony_ci found[strlen(dev->driver->name)] = '\0'; 22468c2ecf20Sopenharmony_ci } 22478c2ecf20Sopenharmony_ci 22488c2ecf20Sopenharmony_ci /* I2C component devices are named "bus-addr" */ 22498c2ecf20Sopenharmony_ci } else if (sscanf(name, "%x-%x", &id1, &id2) == 2) { 22508c2ecf20Sopenharmony_ci 22518c2ecf20Sopenharmony_ci /* create unique ID number from I2C addr and bus */ 22528c2ecf20Sopenharmony_ci *id = ((id1 & 0xffff) << 16) + id2; 22538c2ecf20Sopenharmony_ci 22548c2ecf20Sopenharmony_ci devm_kfree(dev, name); 22558c2ecf20Sopenharmony_ci 22568c2ecf20Sopenharmony_ci /* sanitize component name for DAI link creation */ 22578c2ecf20Sopenharmony_ci name = devm_kasprintf(dev, GFP_KERNEL, "%s.%s", dev->driver->name, devname); 22588c2ecf20Sopenharmony_ci } else { 22598c2ecf20Sopenharmony_ci *id = 0; 22608c2ecf20Sopenharmony_ci } 22618c2ecf20Sopenharmony_ci 22628c2ecf20Sopenharmony_ci return name; 22638c2ecf20Sopenharmony_ci} 22648c2ecf20Sopenharmony_ci 22658c2ecf20Sopenharmony_ci/* 22668c2ecf20Sopenharmony_ci * Simplify DAI link naming for single devices with multiple DAIs by removing 22678c2ecf20Sopenharmony_ci * any ".-1" and using the DAI name (instead of device name). 22688c2ecf20Sopenharmony_ci */ 22698c2ecf20Sopenharmony_cistatic inline char *fmt_multiple_name(struct device *dev, 22708c2ecf20Sopenharmony_ci struct snd_soc_dai_driver *dai_drv) 22718c2ecf20Sopenharmony_ci{ 22728c2ecf20Sopenharmony_ci if (dai_drv->name == NULL) { 22738c2ecf20Sopenharmony_ci dev_err(dev, 22748c2ecf20Sopenharmony_ci "ASoC: error - multiple DAI %s registered with no name\n", 22758c2ecf20Sopenharmony_ci dev_name(dev)); 22768c2ecf20Sopenharmony_ci return NULL; 22778c2ecf20Sopenharmony_ci } 22788c2ecf20Sopenharmony_ci 22798c2ecf20Sopenharmony_ci return devm_kstrdup(dev, dai_drv->name, GFP_KERNEL); 22808c2ecf20Sopenharmony_ci} 22818c2ecf20Sopenharmony_ci 22828c2ecf20Sopenharmony_civoid snd_soc_unregister_dai(struct snd_soc_dai *dai) 22838c2ecf20Sopenharmony_ci{ 22848c2ecf20Sopenharmony_ci dev_dbg(dai->dev, "ASoC: Unregistered DAI '%s'\n", dai->name); 22858c2ecf20Sopenharmony_ci list_del(&dai->list); 22868c2ecf20Sopenharmony_ci} 22878c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_unregister_dai); 22888c2ecf20Sopenharmony_ci 22898c2ecf20Sopenharmony_ci/** 22908c2ecf20Sopenharmony_ci * snd_soc_register_dai - Register a DAI dynamically & create its widgets 22918c2ecf20Sopenharmony_ci * 22928c2ecf20Sopenharmony_ci * @component: The component the DAIs are registered for 22938c2ecf20Sopenharmony_ci * @dai_drv: DAI driver to use for the DAI 22948c2ecf20Sopenharmony_ci * @legacy_dai_naming: if %true, use legacy single-name format; 22958c2ecf20Sopenharmony_ci * if %false, use multiple-name format; 22968c2ecf20Sopenharmony_ci * 22978c2ecf20Sopenharmony_ci * Topology can use this API to register DAIs when probing a component. 22988c2ecf20Sopenharmony_ci * These DAIs's widgets will be freed in the card cleanup and the DAIs 22998c2ecf20Sopenharmony_ci * will be freed in the component cleanup. 23008c2ecf20Sopenharmony_ci */ 23018c2ecf20Sopenharmony_cistruct snd_soc_dai *snd_soc_register_dai(struct snd_soc_component *component, 23028c2ecf20Sopenharmony_ci struct snd_soc_dai_driver *dai_drv, 23038c2ecf20Sopenharmony_ci bool legacy_dai_naming) 23048c2ecf20Sopenharmony_ci{ 23058c2ecf20Sopenharmony_ci struct device *dev = component->dev; 23068c2ecf20Sopenharmony_ci struct snd_soc_dai *dai; 23078c2ecf20Sopenharmony_ci 23088c2ecf20Sopenharmony_ci dev_dbg(dev, "ASoC: dynamically register DAI %s\n", dev_name(dev)); 23098c2ecf20Sopenharmony_ci 23108c2ecf20Sopenharmony_ci lockdep_assert_held(&client_mutex); 23118c2ecf20Sopenharmony_ci 23128c2ecf20Sopenharmony_ci dai = devm_kzalloc(dev, sizeof(*dai), GFP_KERNEL); 23138c2ecf20Sopenharmony_ci if (dai == NULL) 23148c2ecf20Sopenharmony_ci return NULL; 23158c2ecf20Sopenharmony_ci 23168c2ecf20Sopenharmony_ci /* 23178c2ecf20Sopenharmony_ci * Back in the old days when we still had component-less DAIs, 23188c2ecf20Sopenharmony_ci * instead of having a static name, component-less DAIs would 23198c2ecf20Sopenharmony_ci * inherit the name of the parent device so it is possible to 23208c2ecf20Sopenharmony_ci * register multiple instances of the DAI. We still need to keep 23218c2ecf20Sopenharmony_ci * the same naming style even though those DAIs are not 23228c2ecf20Sopenharmony_ci * component-less anymore. 23238c2ecf20Sopenharmony_ci */ 23248c2ecf20Sopenharmony_ci if (legacy_dai_naming && 23258c2ecf20Sopenharmony_ci (dai_drv->id == 0 || dai_drv->name == NULL)) { 23268c2ecf20Sopenharmony_ci dai->name = fmt_single_name(dev, &dai->id); 23278c2ecf20Sopenharmony_ci } else { 23288c2ecf20Sopenharmony_ci dai->name = fmt_multiple_name(dev, dai_drv); 23298c2ecf20Sopenharmony_ci if (dai_drv->id) 23308c2ecf20Sopenharmony_ci dai->id = dai_drv->id; 23318c2ecf20Sopenharmony_ci else 23328c2ecf20Sopenharmony_ci dai->id = component->num_dai; 23338c2ecf20Sopenharmony_ci } 23348c2ecf20Sopenharmony_ci if (!dai->name) 23358c2ecf20Sopenharmony_ci return NULL; 23368c2ecf20Sopenharmony_ci 23378c2ecf20Sopenharmony_ci dai->component = component; 23388c2ecf20Sopenharmony_ci dai->dev = dev; 23398c2ecf20Sopenharmony_ci dai->driver = dai_drv; 23408c2ecf20Sopenharmony_ci 23418c2ecf20Sopenharmony_ci /* see for_each_component_dais */ 23428c2ecf20Sopenharmony_ci list_add_tail(&dai->list, &component->dai_list); 23438c2ecf20Sopenharmony_ci component->num_dai++; 23448c2ecf20Sopenharmony_ci 23458c2ecf20Sopenharmony_ci dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name); 23468c2ecf20Sopenharmony_ci return dai; 23478c2ecf20Sopenharmony_ci} 23488c2ecf20Sopenharmony_ci 23498c2ecf20Sopenharmony_ci/** 23508c2ecf20Sopenharmony_ci * snd_soc_unregister_dais - Unregister DAIs from the ASoC core 23518c2ecf20Sopenharmony_ci * 23528c2ecf20Sopenharmony_ci * @component: The component for which the DAIs should be unregistered 23538c2ecf20Sopenharmony_ci */ 23548c2ecf20Sopenharmony_cistatic void snd_soc_unregister_dais(struct snd_soc_component *component) 23558c2ecf20Sopenharmony_ci{ 23568c2ecf20Sopenharmony_ci struct snd_soc_dai *dai, *_dai; 23578c2ecf20Sopenharmony_ci 23588c2ecf20Sopenharmony_ci for_each_component_dais_safe(component, dai, _dai) 23598c2ecf20Sopenharmony_ci snd_soc_unregister_dai(dai); 23608c2ecf20Sopenharmony_ci} 23618c2ecf20Sopenharmony_ci 23628c2ecf20Sopenharmony_ci/** 23638c2ecf20Sopenharmony_ci * snd_soc_register_dais - Register a DAI with the ASoC core 23648c2ecf20Sopenharmony_ci * 23658c2ecf20Sopenharmony_ci * @component: The component the DAIs are registered for 23668c2ecf20Sopenharmony_ci * @dai_drv: DAI driver to use for the DAIs 23678c2ecf20Sopenharmony_ci * @count: Number of DAIs 23688c2ecf20Sopenharmony_ci */ 23698c2ecf20Sopenharmony_cistatic int snd_soc_register_dais(struct snd_soc_component *component, 23708c2ecf20Sopenharmony_ci struct snd_soc_dai_driver *dai_drv, 23718c2ecf20Sopenharmony_ci size_t count) 23728c2ecf20Sopenharmony_ci{ 23738c2ecf20Sopenharmony_ci struct snd_soc_dai *dai; 23748c2ecf20Sopenharmony_ci unsigned int i; 23758c2ecf20Sopenharmony_ci int ret; 23768c2ecf20Sopenharmony_ci 23778c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 23788c2ecf20Sopenharmony_ci dai = snd_soc_register_dai(component, dai_drv + i, count == 1 && 23798c2ecf20Sopenharmony_ci !component->driver->non_legacy_dai_naming); 23808c2ecf20Sopenharmony_ci if (dai == NULL) { 23818c2ecf20Sopenharmony_ci ret = -ENOMEM; 23828c2ecf20Sopenharmony_ci goto err; 23838c2ecf20Sopenharmony_ci } 23848c2ecf20Sopenharmony_ci } 23858c2ecf20Sopenharmony_ci 23868c2ecf20Sopenharmony_ci return 0; 23878c2ecf20Sopenharmony_ci 23888c2ecf20Sopenharmony_cierr: 23898c2ecf20Sopenharmony_ci snd_soc_unregister_dais(component); 23908c2ecf20Sopenharmony_ci 23918c2ecf20Sopenharmony_ci return ret; 23928c2ecf20Sopenharmony_ci} 23938c2ecf20Sopenharmony_ci 23948c2ecf20Sopenharmony_ci#define ENDIANNESS_MAP(name) \ 23958c2ecf20Sopenharmony_ci (SNDRV_PCM_FMTBIT_##name##LE | SNDRV_PCM_FMTBIT_##name##BE) 23968c2ecf20Sopenharmony_cistatic u64 endianness_format_map[] = { 23978c2ecf20Sopenharmony_ci ENDIANNESS_MAP(S16_), 23988c2ecf20Sopenharmony_ci ENDIANNESS_MAP(U16_), 23998c2ecf20Sopenharmony_ci ENDIANNESS_MAP(S24_), 24008c2ecf20Sopenharmony_ci ENDIANNESS_MAP(U24_), 24018c2ecf20Sopenharmony_ci ENDIANNESS_MAP(S32_), 24028c2ecf20Sopenharmony_ci ENDIANNESS_MAP(U32_), 24038c2ecf20Sopenharmony_ci ENDIANNESS_MAP(S24_3), 24048c2ecf20Sopenharmony_ci ENDIANNESS_MAP(U24_3), 24058c2ecf20Sopenharmony_ci ENDIANNESS_MAP(S20_3), 24068c2ecf20Sopenharmony_ci ENDIANNESS_MAP(U20_3), 24078c2ecf20Sopenharmony_ci ENDIANNESS_MAP(S18_3), 24088c2ecf20Sopenharmony_ci ENDIANNESS_MAP(U18_3), 24098c2ecf20Sopenharmony_ci ENDIANNESS_MAP(FLOAT_), 24108c2ecf20Sopenharmony_ci ENDIANNESS_MAP(FLOAT64_), 24118c2ecf20Sopenharmony_ci ENDIANNESS_MAP(IEC958_SUBFRAME_), 24128c2ecf20Sopenharmony_ci}; 24138c2ecf20Sopenharmony_ci 24148c2ecf20Sopenharmony_ci/* 24158c2ecf20Sopenharmony_ci * Fix up the DAI formats for endianness: codecs don't actually see 24168c2ecf20Sopenharmony_ci * the endianness of the data but we're using the CPU format 24178c2ecf20Sopenharmony_ci * definitions which do need to include endianness so we ensure that 24188c2ecf20Sopenharmony_ci * codec DAIs always have both big and little endian variants set. 24198c2ecf20Sopenharmony_ci */ 24208c2ecf20Sopenharmony_cistatic void convert_endianness_formats(struct snd_soc_pcm_stream *stream) 24218c2ecf20Sopenharmony_ci{ 24228c2ecf20Sopenharmony_ci int i; 24238c2ecf20Sopenharmony_ci 24248c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(endianness_format_map); i++) 24258c2ecf20Sopenharmony_ci if (stream->formats & endianness_format_map[i]) 24268c2ecf20Sopenharmony_ci stream->formats |= endianness_format_map[i]; 24278c2ecf20Sopenharmony_ci} 24288c2ecf20Sopenharmony_ci 24298c2ecf20Sopenharmony_cistatic void snd_soc_try_rebind_card(void) 24308c2ecf20Sopenharmony_ci{ 24318c2ecf20Sopenharmony_ci struct snd_soc_card *card, *c; 24328c2ecf20Sopenharmony_ci 24338c2ecf20Sopenharmony_ci list_for_each_entry_safe(card, c, &unbind_card_list, list) 24348c2ecf20Sopenharmony_ci if (!snd_soc_bind_card(card)) 24358c2ecf20Sopenharmony_ci list_del(&card->list); 24368c2ecf20Sopenharmony_ci} 24378c2ecf20Sopenharmony_ci 24388c2ecf20Sopenharmony_cistatic void snd_soc_del_component_unlocked(struct snd_soc_component *component) 24398c2ecf20Sopenharmony_ci{ 24408c2ecf20Sopenharmony_ci struct snd_soc_card *card = component->card; 24418c2ecf20Sopenharmony_ci 24428c2ecf20Sopenharmony_ci snd_soc_unregister_dais(component); 24438c2ecf20Sopenharmony_ci 24448c2ecf20Sopenharmony_ci if (card) 24458c2ecf20Sopenharmony_ci snd_soc_unbind_card(card, false); 24468c2ecf20Sopenharmony_ci 24478c2ecf20Sopenharmony_ci list_del(&component->list); 24488c2ecf20Sopenharmony_ci} 24498c2ecf20Sopenharmony_ci 24508c2ecf20Sopenharmony_ciint snd_soc_component_initialize(struct snd_soc_component *component, 24518c2ecf20Sopenharmony_ci const struct snd_soc_component_driver *driver, 24528c2ecf20Sopenharmony_ci struct device *dev) 24538c2ecf20Sopenharmony_ci{ 24548c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&component->dai_list); 24558c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&component->dobj_list); 24568c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&component->card_list); 24578c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&component->list); 24588c2ecf20Sopenharmony_ci mutex_init(&component->io_mutex); 24598c2ecf20Sopenharmony_ci 24608c2ecf20Sopenharmony_ci component->name = fmt_single_name(dev, &component->id); 24618c2ecf20Sopenharmony_ci if (!component->name) { 24628c2ecf20Sopenharmony_ci dev_err(dev, "ASoC: Failed to allocate name\n"); 24638c2ecf20Sopenharmony_ci return -ENOMEM; 24648c2ecf20Sopenharmony_ci } 24658c2ecf20Sopenharmony_ci 24668c2ecf20Sopenharmony_ci component->dev = dev; 24678c2ecf20Sopenharmony_ci component->driver = driver; 24688c2ecf20Sopenharmony_ci 24698c2ecf20Sopenharmony_ci return 0; 24708c2ecf20Sopenharmony_ci} 24718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_component_initialize); 24728c2ecf20Sopenharmony_ci 24738c2ecf20Sopenharmony_ciint snd_soc_add_component(struct snd_soc_component *component, 24748c2ecf20Sopenharmony_ci struct snd_soc_dai_driver *dai_drv, 24758c2ecf20Sopenharmony_ci int num_dai) 24768c2ecf20Sopenharmony_ci{ 24778c2ecf20Sopenharmony_ci int ret; 24788c2ecf20Sopenharmony_ci int i; 24798c2ecf20Sopenharmony_ci 24808c2ecf20Sopenharmony_ci mutex_lock(&client_mutex); 24818c2ecf20Sopenharmony_ci 24828c2ecf20Sopenharmony_ci if (component->driver->endianness) { 24838c2ecf20Sopenharmony_ci for (i = 0; i < num_dai; i++) { 24848c2ecf20Sopenharmony_ci convert_endianness_formats(&dai_drv[i].playback); 24858c2ecf20Sopenharmony_ci convert_endianness_formats(&dai_drv[i].capture); 24868c2ecf20Sopenharmony_ci } 24878c2ecf20Sopenharmony_ci } 24888c2ecf20Sopenharmony_ci 24898c2ecf20Sopenharmony_ci ret = snd_soc_register_dais(component, dai_drv, num_dai); 24908c2ecf20Sopenharmony_ci if (ret < 0) { 24918c2ecf20Sopenharmony_ci dev_err(component->dev, "ASoC: Failed to register DAIs: %d\n", 24928c2ecf20Sopenharmony_ci ret); 24938c2ecf20Sopenharmony_ci goto err_cleanup; 24948c2ecf20Sopenharmony_ci } 24958c2ecf20Sopenharmony_ci 24968c2ecf20Sopenharmony_ci if (!component->driver->write && !component->driver->read) { 24978c2ecf20Sopenharmony_ci if (!component->regmap) 24988c2ecf20Sopenharmony_ci component->regmap = dev_get_regmap(component->dev, 24998c2ecf20Sopenharmony_ci NULL); 25008c2ecf20Sopenharmony_ci if (component->regmap) 25018c2ecf20Sopenharmony_ci snd_soc_component_setup_regmap(component); 25028c2ecf20Sopenharmony_ci } 25038c2ecf20Sopenharmony_ci 25048c2ecf20Sopenharmony_ci /* see for_each_component */ 25058c2ecf20Sopenharmony_ci list_add(&component->list, &component_list); 25068c2ecf20Sopenharmony_ci 25078c2ecf20Sopenharmony_cierr_cleanup: 25088c2ecf20Sopenharmony_ci if (ret < 0) 25098c2ecf20Sopenharmony_ci snd_soc_del_component_unlocked(component); 25108c2ecf20Sopenharmony_ci 25118c2ecf20Sopenharmony_ci mutex_unlock(&client_mutex); 25128c2ecf20Sopenharmony_ci 25138c2ecf20Sopenharmony_ci if (ret == 0) 25148c2ecf20Sopenharmony_ci snd_soc_try_rebind_card(); 25158c2ecf20Sopenharmony_ci 25168c2ecf20Sopenharmony_ci return ret; 25178c2ecf20Sopenharmony_ci} 25188c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_add_component); 25198c2ecf20Sopenharmony_ci 25208c2ecf20Sopenharmony_ciint snd_soc_register_component(struct device *dev, 25218c2ecf20Sopenharmony_ci const struct snd_soc_component_driver *component_driver, 25228c2ecf20Sopenharmony_ci struct snd_soc_dai_driver *dai_drv, 25238c2ecf20Sopenharmony_ci int num_dai) 25248c2ecf20Sopenharmony_ci{ 25258c2ecf20Sopenharmony_ci struct snd_soc_component *component; 25268c2ecf20Sopenharmony_ci int ret; 25278c2ecf20Sopenharmony_ci 25288c2ecf20Sopenharmony_ci component = devm_kzalloc(dev, sizeof(*component), GFP_KERNEL); 25298c2ecf20Sopenharmony_ci if (!component) 25308c2ecf20Sopenharmony_ci return -ENOMEM; 25318c2ecf20Sopenharmony_ci 25328c2ecf20Sopenharmony_ci ret = snd_soc_component_initialize(component, component_driver, dev); 25338c2ecf20Sopenharmony_ci if (ret < 0) 25348c2ecf20Sopenharmony_ci return ret; 25358c2ecf20Sopenharmony_ci 25368c2ecf20Sopenharmony_ci return snd_soc_add_component(component, dai_drv, num_dai); 25378c2ecf20Sopenharmony_ci} 25388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_register_component); 25398c2ecf20Sopenharmony_ci 25408c2ecf20Sopenharmony_ci/** 25418c2ecf20Sopenharmony_ci * snd_soc_unregister_component_by_driver - Unregister component using a given driver 25428c2ecf20Sopenharmony_ci * from the ASoC core 25438c2ecf20Sopenharmony_ci * 25448c2ecf20Sopenharmony_ci * @dev: The device to unregister 25458c2ecf20Sopenharmony_ci * @component_driver: The component driver to unregister 25468c2ecf20Sopenharmony_ci */ 25478c2ecf20Sopenharmony_civoid snd_soc_unregister_component_by_driver(struct device *dev, 25488c2ecf20Sopenharmony_ci const struct snd_soc_component_driver *component_driver) 25498c2ecf20Sopenharmony_ci{ 25508c2ecf20Sopenharmony_ci struct snd_soc_component *component; 25518c2ecf20Sopenharmony_ci 25528c2ecf20Sopenharmony_ci if (!component_driver) 25538c2ecf20Sopenharmony_ci return; 25548c2ecf20Sopenharmony_ci 25558c2ecf20Sopenharmony_ci mutex_lock(&client_mutex); 25568c2ecf20Sopenharmony_ci component = snd_soc_lookup_component_nolocked(dev, component_driver->name); 25578c2ecf20Sopenharmony_ci if (!component) 25588c2ecf20Sopenharmony_ci goto out; 25598c2ecf20Sopenharmony_ci 25608c2ecf20Sopenharmony_ci snd_soc_del_component_unlocked(component); 25618c2ecf20Sopenharmony_ci 25628c2ecf20Sopenharmony_ciout: 25638c2ecf20Sopenharmony_ci mutex_unlock(&client_mutex); 25648c2ecf20Sopenharmony_ci} 25658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_unregister_component_by_driver); 25668c2ecf20Sopenharmony_ci 25678c2ecf20Sopenharmony_ci/** 25688c2ecf20Sopenharmony_ci * snd_soc_unregister_component - Unregister all related component 25698c2ecf20Sopenharmony_ci * from the ASoC core 25708c2ecf20Sopenharmony_ci * 25718c2ecf20Sopenharmony_ci * @dev: The device to unregister 25728c2ecf20Sopenharmony_ci */ 25738c2ecf20Sopenharmony_civoid snd_soc_unregister_component(struct device *dev) 25748c2ecf20Sopenharmony_ci{ 25758c2ecf20Sopenharmony_ci struct snd_soc_component *component; 25768c2ecf20Sopenharmony_ci 25778c2ecf20Sopenharmony_ci mutex_lock(&client_mutex); 25788c2ecf20Sopenharmony_ci while (1) { 25798c2ecf20Sopenharmony_ci component = snd_soc_lookup_component_nolocked(dev, NULL); 25808c2ecf20Sopenharmony_ci if (!component) 25818c2ecf20Sopenharmony_ci break; 25828c2ecf20Sopenharmony_ci 25838c2ecf20Sopenharmony_ci snd_soc_del_component_unlocked(component); 25848c2ecf20Sopenharmony_ci } 25858c2ecf20Sopenharmony_ci mutex_unlock(&client_mutex); 25868c2ecf20Sopenharmony_ci} 25878c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_unregister_component); 25888c2ecf20Sopenharmony_ci 25898c2ecf20Sopenharmony_ci/* Retrieve a card's name from device tree */ 25908c2ecf20Sopenharmony_ciint snd_soc_of_parse_card_name(struct snd_soc_card *card, 25918c2ecf20Sopenharmony_ci const char *propname) 25928c2ecf20Sopenharmony_ci{ 25938c2ecf20Sopenharmony_ci struct device_node *np; 25948c2ecf20Sopenharmony_ci int ret; 25958c2ecf20Sopenharmony_ci 25968c2ecf20Sopenharmony_ci if (!card->dev) { 25978c2ecf20Sopenharmony_ci pr_err("card->dev is not set before calling %s\n", __func__); 25988c2ecf20Sopenharmony_ci return -EINVAL; 25998c2ecf20Sopenharmony_ci } 26008c2ecf20Sopenharmony_ci 26018c2ecf20Sopenharmony_ci np = card->dev->of_node; 26028c2ecf20Sopenharmony_ci 26038c2ecf20Sopenharmony_ci ret = of_property_read_string_index(np, propname, 0, &card->name); 26048c2ecf20Sopenharmony_ci /* 26058c2ecf20Sopenharmony_ci * EINVAL means the property does not exist. This is fine providing 26068c2ecf20Sopenharmony_ci * card->name was previously set, which is checked later in 26078c2ecf20Sopenharmony_ci * snd_soc_register_card. 26088c2ecf20Sopenharmony_ci */ 26098c2ecf20Sopenharmony_ci if (ret < 0 && ret != -EINVAL) { 26108c2ecf20Sopenharmony_ci dev_err(card->dev, 26118c2ecf20Sopenharmony_ci "ASoC: Property '%s' could not be read: %d\n", 26128c2ecf20Sopenharmony_ci propname, ret); 26138c2ecf20Sopenharmony_ci return ret; 26148c2ecf20Sopenharmony_ci } 26158c2ecf20Sopenharmony_ci 26168c2ecf20Sopenharmony_ci return 0; 26178c2ecf20Sopenharmony_ci} 26188c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_of_parse_card_name); 26198c2ecf20Sopenharmony_ci 26208c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget simple_widgets[] = { 26218c2ecf20Sopenharmony_ci SND_SOC_DAPM_MIC("Microphone", NULL), 26228c2ecf20Sopenharmony_ci SND_SOC_DAPM_LINE("Line", NULL), 26238c2ecf20Sopenharmony_ci SND_SOC_DAPM_HP("Headphone", NULL), 26248c2ecf20Sopenharmony_ci SND_SOC_DAPM_SPK("Speaker", NULL), 26258c2ecf20Sopenharmony_ci}; 26268c2ecf20Sopenharmony_ci 26278c2ecf20Sopenharmony_ciint snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card, 26288c2ecf20Sopenharmony_ci const char *propname) 26298c2ecf20Sopenharmony_ci{ 26308c2ecf20Sopenharmony_ci struct device_node *np = card->dev->of_node; 26318c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *widgets; 26328c2ecf20Sopenharmony_ci const char *template, *wname; 26338c2ecf20Sopenharmony_ci int i, j, num_widgets, ret; 26348c2ecf20Sopenharmony_ci 26358c2ecf20Sopenharmony_ci num_widgets = of_property_count_strings(np, propname); 26368c2ecf20Sopenharmony_ci if (num_widgets < 0) { 26378c2ecf20Sopenharmony_ci dev_err(card->dev, 26388c2ecf20Sopenharmony_ci "ASoC: Property '%s' does not exist\n", propname); 26398c2ecf20Sopenharmony_ci return -EINVAL; 26408c2ecf20Sopenharmony_ci } 26418c2ecf20Sopenharmony_ci if (num_widgets & 1) { 26428c2ecf20Sopenharmony_ci dev_err(card->dev, 26438c2ecf20Sopenharmony_ci "ASoC: Property '%s' length is not even\n", propname); 26448c2ecf20Sopenharmony_ci return -EINVAL; 26458c2ecf20Sopenharmony_ci } 26468c2ecf20Sopenharmony_ci 26478c2ecf20Sopenharmony_ci num_widgets /= 2; 26488c2ecf20Sopenharmony_ci if (!num_widgets) { 26498c2ecf20Sopenharmony_ci dev_err(card->dev, "ASoC: Property '%s's length is zero\n", 26508c2ecf20Sopenharmony_ci propname); 26518c2ecf20Sopenharmony_ci return -EINVAL; 26528c2ecf20Sopenharmony_ci } 26538c2ecf20Sopenharmony_ci 26548c2ecf20Sopenharmony_ci widgets = devm_kcalloc(card->dev, num_widgets, sizeof(*widgets), 26558c2ecf20Sopenharmony_ci GFP_KERNEL); 26568c2ecf20Sopenharmony_ci if (!widgets) { 26578c2ecf20Sopenharmony_ci dev_err(card->dev, 26588c2ecf20Sopenharmony_ci "ASoC: Could not allocate memory for widgets\n"); 26598c2ecf20Sopenharmony_ci return -ENOMEM; 26608c2ecf20Sopenharmony_ci } 26618c2ecf20Sopenharmony_ci 26628c2ecf20Sopenharmony_ci for (i = 0; i < num_widgets; i++) { 26638c2ecf20Sopenharmony_ci ret = of_property_read_string_index(np, propname, 26648c2ecf20Sopenharmony_ci 2 * i, &template); 26658c2ecf20Sopenharmony_ci if (ret) { 26668c2ecf20Sopenharmony_ci dev_err(card->dev, 26678c2ecf20Sopenharmony_ci "ASoC: Property '%s' index %d read error:%d\n", 26688c2ecf20Sopenharmony_ci propname, 2 * i, ret); 26698c2ecf20Sopenharmony_ci return -EINVAL; 26708c2ecf20Sopenharmony_ci } 26718c2ecf20Sopenharmony_ci 26728c2ecf20Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(simple_widgets); j++) { 26738c2ecf20Sopenharmony_ci if (!strncmp(template, simple_widgets[j].name, 26748c2ecf20Sopenharmony_ci strlen(simple_widgets[j].name))) { 26758c2ecf20Sopenharmony_ci widgets[i] = simple_widgets[j]; 26768c2ecf20Sopenharmony_ci break; 26778c2ecf20Sopenharmony_ci } 26788c2ecf20Sopenharmony_ci } 26798c2ecf20Sopenharmony_ci 26808c2ecf20Sopenharmony_ci if (j >= ARRAY_SIZE(simple_widgets)) { 26818c2ecf20Sopenharmony_ci dev_err(card->dev, 26828c2ecf20Sopenharmony_ci "ASoC: DAPM widget '%s' is not supported\n", 26838c2ecf20Sopenharmony_ci template); 26848c2ecf20Sopenharmony_ci return -EINVAL; 26858c2ecf20Sopenharmony_ci } 26868c2ecf20Sopenharmony_ci 26878c2ecf20Sopenharmony_ci ret = of_property_read_string_index(np, propname, 26888c2ecf20Sopenharmony_ci (2 * i) + 1, 26898c2ecf20Sopenharmony_ci &wname); 26908c2ecf20Sopenharmony_ci if (ret) { 26918c2ecf20Sopenharmony_ci dev_err(card->dev, 26928c2ecf20Sopenharmony_ci "ASoC: Property '%s' index %d read error:%d\n", 26938c2ecf20Sopenharmony_ci propname, (2 * i) + 1, ret); 26948c2ecf20Sopenharmony_ci return -EINVAL; 26958c2ecf20Sopenharmony_ci } 26968c2ecf20Sopenharmony_ci 26978c2ecf20Sopenharmony_ci widgets[i].name = wname; 26988c2ecf20Sopenharmony_ci } 26998c2ecf20Sopenharmony_ci 27008c2ecf20Sopenharmony_ci card->of_dapm_widgets = widgets; 27018c2ecf20Sopenharmony_ci card->num_of_dapm_widgets = num_widgets; 27028c2ecf20Sopenharmony_ci 27038c2ecf20Sopenharmony_ci return 0; 27048c2ecf20Sopenharmony_ci} 27058c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_simple_widgets); 27068c2ecf20Sopenharmony_ci 27078c2ecf20Sopenharmony_ciint snd_soc_of_get_slot_mask(struct device_node *np, 27088c2ecf20Sopenharmony_ci const char *prop_name, 27098c2ecf20Sopenharmony_ci unsigned int *mask) 27108c2ecf20Sopenharmony_ci{ 27118c2ecf20Sopenharmony_ci u32 val; 27128c2ecf20Sopenharmony_ci const __be32 *of_slot_mask = of_get_property(np, prop_name, &val); 27138c2ecf20Sopenharmony_ci int i; 27148c2ecf20Sopenharmony_ci 27158c2ecf20Sopenharmony_ci if (!of_slot_mask) 27168c2ecf20Sopenharmony_ci return 0; 27178c2ecf20Sopenharmony_ci val /= sizeof(u32); 27188c2ecf20Sopenharmony_ci for (i = 0; i < val; i++) 27198c2ecf20Sopenharmony_ci if (be32_to_cpup(&of_slot_mask[i])) 27208c2ecf20Sopenharmony_ci *mask |= (1 << i); 27218c2ecf20Sopenharmony_ci 27228c2ecf20Sopenharmony_ci return val; 27238c2ecf20Sopenharmony_ci} 27248c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_of_get_slot_mask); 27258c2ecf20Sopenharmony_ci 27268c2ecf20Sopenharmony_ciint snd_soc_of_parse_tdm_slot(struct device_node *np, 27278c2ecf20Sopenharmony_ci unsigned int *tx_mask, 27288c2ecf20Sopenharmony_ci unsigned int *rx_mask, 27298c2ecf20Sopenharmony_ci unsigned int *slots, 27308c2ecf20Sopenharmony_ci unsigned int *slot_width) 27318c2ecf20Sopenharmony_ci{ 27328c2ecf20Sopenharmony_ci u32 val; 27338c2ecf20Sopenharmony_ci int ret; 27348c2ecf20Sopenharmony_ci 27358c2ecf20Sopenharmony_ci if (tx_mask) 27368c2ecf20Sopenharmony_ci snd_soc_of_get_slot_mask(np, "dai-tdm-slot-tx-mask", tx_mask); 27378c2ecf20Sopenharmony_ci if (rx_mask) 27388c2ecf20Sopenharmony_ci snd_soc_of_get_slot_mask(np, "dai-tdm-slot-rx-mask", rx_mask); 27398c2ecf20Sopenharmony_ci 27408c2ecf20Sopenharmony_ci if (of_property_read_bool(np, "dai-tdm-slot-num")) { 27418c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "dai-tdm-slot-num", &val); 27428c2ecf20Sopenharmony_ci if (ret) 27438c2ecf20Sopenharmony_ci return ret; 27448c2ecf20Sopenharmony_ci 27458c2ecf20Sopenharmony_ci if (slots) 27468c2ecf20Sopenharmony_ci *slots = val; 27478c2ecf20Sopenharmony_ci } 27488c2ecf20Sopenharmony_ci 27498c2ecf20Sopenharmony_ci if (of_property_read_bool(np, "dai-tdm-slot-width")) { 27508c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "dai-tdm-slot-width", &val); 27518c2ecf20Sopenharmony_ci if (ret) 27528c2ecf20Sopenharmony_ci return ret; 27538c2ecf20Sopenharmony_ci 27548c2ecf20Sopenharmony_ci if (slot_width) 27558c2ecf20Sopenharmony_ci *slot_width = val; 27568c2ecf20Sopenharmony_ci } 27578c2ecf20Sopenharmony_ci 27588c2ecf20Sopenharmony_ci return 0; 27598c2ecf20Sopenharmony_ci} 27608c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_of_parse_tdm_slot); 27618c2ecf20Sopenharmony_ci 27628c2ecf20Sopenharmony_civoid snd_soc_of_parse_node_prefix(struct device_node *np, 27638c2ecf20Sopenharmony_ci struct snd_soc_codec_conf *codec_conf, 27648c2ecf20Sopenharmony_ci struct device_node *of_node, 27658c2ecf20Sopenharmony_ci const char *propname) 27668c2ecf20Sopenharmony_ci{ 27678c2ecf20Sopenharmony_ci const char *str; 27688c2ecf20Sopenharmony_ci int ret; 27698c2ecf20Sopenharmony_ci 27708c2ecf20Sopenharmony_ci ret = of_property_read_string(np, propname, &str); 27718c2ecf20Sopenharmony_ci if (ret < 0) { 27728c2ecf20Sopenharmony_ci /* no prefix is not error */ 27738c2ecf20Sopenharmony_ci return; 27748c2ecf20Sopenharmony_ci } 27758c2ecf20Sopenharmony_ci 27768c2ecf20Sopenharmony_ci codec_conf->dlc.of_node = of_node; 27778c2ecf20Sopenharmony_ci codec_conf->name_prefix = str; 27788c2ecf20Sopenharmony_ci} 27798c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_of_parse_node_prefix); 27808c2ecf20Sopenharmony_ci 27818c2ecf20Sopenharmony_ciint snd_soc_of_parse_audio_routing(struct snd_soc_card *card, 27828c2ecf20Sopenharmony_ci const char *propname) 27838c2ecf20Sopenharmony_ci{ 27848c2ecf20Sopenharmony_ci struct device_node *np = card->dev->of_node; 27858c2ecf20Sopenharmony_ci int num_routes; 27868c2ecf20Sopenharmony_ci struct snd_soc_dapm_route *routes; 27878c2ecf20Sopenharmony_ci int i, ret; 27888c2ecf20Sopenharmony_ci 27898c2ecf20Sopenharmony_ci num_routes = of_property_count_strings(np, propname); 27908c2ecf20Sopenharmony_ci if (num_routes < 0 || num_routes & 1) { 27918c2ecf20Sopenharmony_ci dev_err(card->dev, 27928c2ecf20Sopenharmony_ci "ASoC: Property '%s' does not exist or its length is not even\n", 27938c2ecf20Sopenharmony_ci propname); 27948c2ecf20Sopenharmony_ci return -EINVAL; 27958c2ecf20Sopenharmony_ci } 27968c2ecf20Sopenharmony_ci num_routes /= 2; 27978c2ecf20Sopenharmony_ci if (!num_routes) { 27988c2ecf20Sopenharmony_ci dev_err(card->dev, "ASoC: Property '%s's length is zero\n", 27998c2ecf20Sopenharmony_ci propname); 28008c2ecf20Sopenharmony_ci return -EINVAL; 28018c2ecf20Sopenharmony_ci } 28028c2ecf20Sopenharmony_ci 28038c2ecf20Sopenharmony_ci routes = devm_kcalloc(card->dev, num_routes, sizeof(*routes), 28048c2ecf20Sopenharmony_ci GFP_KERNEL); 28058c2ecf20Sopenharmony_ci if (!routes) { 28068c2ecf20Sopenharmony_ci dev_err(card->dev, 28078c2ecf20Sopenharmony_ci "ASoC: Could not allocate DAPM route table\n"); 28088c2ecf20Sopenharmony_ci return -ENOMEM; 28098c2ecf20Sopenharmony_ci } 28108c2ecf20Sopenharmony_ci 28118c2ecf20Sopenharmony_ci for (i = 0; i < num_routes; i++) { 28128c2ecf20Sopenharmony_ci ret = of_property_read_string_index(np, propname, 28138c2ecf20Sopenharmony_ci 2 * i, &routes[i].sink); 28148c2ecf20Sopenharmony_ci if (ret) { 28158c2ecf20Sopenharmony_ci dev_err(card->dev, 28168c2ecf20Sopenharmony_ci "ASoC: Property '%s' index %d could not be read: %d\n", 28178c2ecf20Sopenharmony_ci propname, 2 * i, ret); 28188c2ecf20Sopenharmony_ci return -EINVAL; 28198c2ecf20Sopenharmony_ci } 28208c2ecf20Sopenharmony_ci ret = of_property_read_string_index(np, propname, 28218c2ecf20Sopenharmony_ci (2 * i) + 1, &routes[i].source); 28228c2ecf20Sopenharmony_ci if (ret) { 28238c2ecf20Sopenharmony_ci dev_err(card->dev, 28248c2ecf20Sopenharmony_ci "ASoC: Property '%s' index %d could not be read: %d\n", 28258c2ecf20Sopenharmony_ci propname, (2 * i) + 1, ret); 28268c2ecf20Sopenharmony_ci return -EINVAL; 28278c2ecf20Sopenharmony_ci } 28288c2ecf20Sopenharmony_ci } 28298c2ecf20Sopenharmony_ci 28308c2ecf20Sopenharmony_ci card->num_of_dapm_routes = num_routes; 28318c2ecf20Sopenharmony_ci card->of_dapm_routes = routes; 28328c2ecf20Sopenharmony_ci 28338c2ecf20Sopenharmony_ci return 0; 28348c2ecf20Sopenharmony_ci} 28358c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_routing); 28368c2ecf20Sopenharmony_ci 28378c2ecf20Sopenharmony_ciint snd_soc_of_parse_aux_devs(struct snd_soc_card *card, const char *propname) 28388c2ecf20Sopenharmony_ci{ 28398c2ecf20Sopenharmony_ci struct device_node *node = card->dev->of_node; 28408c2ecf20Sopenharmony_ci struct snd_soc_aux_dev *aux; 28418c2ecf20Sopenharmony_ci int num, i; 28428c2ecf20Sopenharmony_ci 28438c2ecf20Sopenharmony_ci num = of_count_phandle_with_args(node, propname, NULL); 28448c2ecf20Sopenharmony_ci if (num == -ENOENT) { 28458c2ecf20Sopenharmony_ci return 0; 28468c2ecf20Sopenharmony_ci } else if (num < 0) { 28478c2ecf20Sopenharmony_ci dev_err(card->dev, "ASOC: Property '%s' could not be read: %d\n", 28488c2ecf20Sopenharmony_ci propname, num); 28498c2ecf20Sopenharmony_ci return num; 28508c2ecf20Sopenharmony_ci } 28518c2ecf20Sopenharmony_ci 28528c2ecf20Sopenharmony_ci aux = devm_kcalloc(card->dev, num, sizeof(*aux), GFP_KERNEL); 28538c2ecf20Sopenharmony_ci if (!aux) 28548c2ecf20Sopenharmony_ci return -ENOMEM; 28558c2ecf20Sopenharmony_ci card->aux_dev = aux; 28568c2ecf20Sopenharmony_ci card->num_aux_devs = num; 28578c2ecf20Sopenharmony_ci 28588c2ecf20Sopenharmony_ci for_each_card_pre_auxs(card, i, aux) { 28598c2ecf20Sopenharmony_ci aux->dlc.of_node = of_parse_phandle(node, propname, i); 28608c2ecf20Sopenharmony_ci if (!aux->dlc.of_node) 28618c2ecf20Sopenharmony_ci return -EINVAL; 28628c2ecf20Sopenharmony_ci } 28638c2ecf20Sopenharmony_ci 28648c2ecf20Sopenharmony_ci return 0; 28658c2ecf20Sopenharmony_ci} 28668c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_of_parse_aux_devs); 28678c2ecf20Sopenharmony_ci 28688c2ecf20Sopenharmony_ciunsigned int snd_soc_of_parse_daifmt(struct device_node *np, 28698c2ecf20Sopenharmony_ci const char *prefix, 28708c2ecf20Sopenharmony_ci struct device_node **bitclkmaster, 28718c2ecf20Sopenharmony_ci struct device_node **framemaster) 28728c2ecf20Sopenharmony_ci{ 28738c2ecf20Sopenharmony_ci int ret, i; 28748c2ecf20Sopenharmony_ci char prop[128]; 28758c2ecf20Sopenharmony_ci unsigned int format = 0; 28768c2ecf20Sopenharmony_ci int bit, frame; 28778c2ecf20Sopenharmony_ci const char *str; 28788c2ecf20Sopenharmony_ci struct { 28798c2ecf20Sopenharmony_ci char *name; 28808c2ecf20Sopenharmony_ci unsigned int val; 28818c2ecf20Sopenharmony_ci } of_fmt_table[] = { 28828c2ecf20Sopenharmony_ci { "i2s", SND_SOC_DAIFMT_I2S }, 28838c2ecf20Sopenharmony_ci { "right_j", SND_SOC_DAIFMT_RIGHT_J }, 28848c2ecf20Sopenharmony_ci { "left_j", SND_SOC_DAIFMT_LEFT_J }, 28858c2ecf20Sopenharmony_ci { "dsp_a", SND_SOC_DAIFMT_DSP_A }, 28868c2ecf20Sopenharmony_ci { "dsp_b", SND_SOC_DAIFMT_DSP_B }, 28878c2ecf20Sopenharmony_ci { "ac97", SND_SOC_DAIFMT_AC97 }, 28888c2ecf20Sopenharmony_ci { "pdm", SND_SOC_DAIFMT_PDM}, 28898c2ecf20Sopenharmony_ci { "msb", SND_SOC_DAIFMT_MSB }, 28908c2ecf20Sopenharmony_ci { "lsb", SND_SOC_DAIFMT_LSB }, 28918c2ecf20Sopenharmony_ci }; 28928c2ecf20Sopenharmony_ci 28938c2ecf20Sopenharmony_ci if (!prefix) 28948c2ecf20Sopenharmony_ci prefix = ""; 28958c2ecf20Sopenharmony_ci 28968c2ecf20Sopenharmony_ci /* 28978c2ecf20Sopenharmony_ci * check "dai-format = xxx" 28988c2ecf20Sopenharmony_ci * or "[prefix]format = xxx" 28998c2ecf20Sopenharmony_ci * SND_SOC_DAIFMT_FORMAT_MASK area 29008c2ecf20Sopenharmony_ci */ 29018c2ecf20Sopenharmony_ci ret = of_property_read_string(np, "dai-format", &str); 29028c2ecf20Sopenharmony_ci if (ret < 0) { 29038c2ecf20Sopenharmony_ci snprintf(prop, sizeof(prop), "%sformat", prefix); 29048c2ecf20Sopenharmony_ci ret = of_property_read_string(np, prop, &str); 29058c2ecf20Sopenharmony_ci } 29068c2ecf20Sopenharmony_ci if (ret == 0) { 29078c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(of_fmt_table); i++) { 29088c2ecf20Sopenharmony_ci if (strcmp(str, of_fmt_table[i].name) == 0) { 29098c2ecf20Sopenharmony_ci format |= of_fmt_table[i].val; 29108c2ecf20Sopenharmony_ci break; 29118c2ecf20Sopenharmony_ci } 29128c2ecf20Sopenharmony_ci } 29138c2ecf20Sopenharmony_ci } 29148c2ecf20Sopenharmony_ci 29158c2ecf20Sopenharmony_ci /* 29168c2ecf20Sopenharmony_ci * check "[prefix]continuous-clock" 29178c2ecf20Sopenharmony_ci * SND_SOC_DAIFMT_CLOCK_MASK area 29188c2ecf20Sopenharmony_ci */ 29198c2ecf20Sopenharmony_ci snprintf(prop, sizeof(prop), "%scontinuous-clock", prefix); 29208c2ecf20Sopenharmony_ci if (of_property_read_bool(np, prop)) 29218c2ecf20Sopenharmony_ci format |= SND_SOC_DAIFMT_CONT; 29228c2ecf20Sopenharmony_ci else 29238c2ecf20Sopenharmony_ci format |= SND_SOC_DAIFMT_GATED; 29248c2ecf20Sopenharmony_ci 29258c2ecf20Sopenharmony_ci /* 29268c2ecf20Sopenharmony_ci * check "[prefix]bitclock-inversion" 29278c2ecf20Sopenharmony_ci * check "[prefix]frame-inversion" 29288c2ecf20Sopenharmony_ci * SND_SOC_DAIFMT_INV_MASK area 29298c2ecf20Sopenharmony_ci */ 29308c2ecf20Sopenharmony_ci snprintf(prop, sizeof(prop), "%sbitclock-inversion", prefix); 29318c2ecf20Sopenharmony_ci bit = !!of_get_property(np, prop, NULL); 29328c2ecf20Sopenharmony_ci 29338c2ecf20Sopenharmony_ci snprintf(prop, sizeof(prop), "%sframe-inversion", prefix); 29348c2ecf20Sopenharmony_ci frame = !!of_get_property(np, prop, NULL); 29358c2ecf20Sopenharmony_ci 29368c2ecf20Sopenharmony_ci switch ((bit << 4) + frame) { 29378c2ecf20Sopenharmony_ci case 0x11: 29388c2ecf20Sopenharmony_ci format |= SND_SOC_DAIFMT_IB_IF; 29398c2ecf20Sopenharmony_ci break; 29408c2ecf20Sopenharmony_ci case 0x10: 29418c2ecf20Sopenharmony_ci format |= SND_SOC_DAIFMT_IB_NF; 29428c2ecf20Sopenharmony_ci break; 29438c2ecf20Sopenharmony_ci case 0x01: 29448c2ecf20Sopenharmony_ci format |= SND_SOC_DAIFMT_NB_IF; 29458c2ecf20Sopenharmony_ci break; 29468c2ecf20Sopenharmony_ci default: 29478c2ecf20Sopenharmony_ci /* SND_SOC_DAIFMT_NB_NF is default */ 29488c2ecf20Sopenharmony_ci break; 29498c2ecf20Sopenharmony_ci } 29508c2ecf20Sopenharmony_ci 29518c2ecf20Sopenharmony_ci /* 29528c2ecf20Sopenharmony_ci * check "[prefix]bitclock-master" 29538c2ecf20Sopenharmony_ci * check "[prefix]frame-master" 29548c2ecf20Sopenharmony_ci * SND_SOC_DAIFMT_MASTER_MASK area 29558c2ecf20Sopenharmony_ci */ 29568c2ecf20Sopenharmony_ci snprintf(prop, sizeof(prop), "%sbitclock-master", prefix); 29578c2ecf20Sopenharmony_ci bit = !!of_get_property(np, prop, NULL); 29588c2ecf20Sopenharmony_ci if (bit && bitclkmaster) 29598c2ecf20Sopenharmony_ci *bitclkmaster = of_parse_phandle(np, prop, 0); 29608c2ecf20Sopenharmony_ci 29618c2ecf20Sopenharmony_ci snprintf(prop, sizeof(prop), "%sframe-master", prefix); 29628c2ecf20Sopenharmony_ci frame = !!of_get_property(np, prop, NULL); 29638c2ecf20Sopenharmony_ci if (frame && framemaster) 29648c2ecf20Sopenharmony_ci *framemaster = of_parse_phandle(np, prop, 0); 29658c2ecf20Sopenharmony_ci 29668c2ecf20Sopenharmony_ci switch ((bit << 4) + frame) { 29678c2ecf20Sopenharmony_ci case 0x11: 29688c2ecf20Sopenharmony_ci format |= SND_SOC_DAIFMT_CBM_CFM; 29698c2ecf20Sopenharmony_ci break; 29708c2ecf20Sopenharmony_ci case 0x10: 29718c2ecf20Sopenharmony_ci format |= SND_SOC_DAIFMT_CBM_CFS; 29728c2ecf20Sopenharmony_ci break; 29738c2ecf20Sopenharmony_ci case 0x01: 29748c2ecf20Sopenharmony_ci format |= SND_SOC_DAIFMT_CBS_CFM; 29758c2ecf20Sopenharmony_ci break; 29768c2ecf20Sopenharmony_ci default: 29778c2ecf20Sopenharmony_ci format |= SND_SOC_DAIFMT_CBS_CFS; 29788c2ecf20Sopenharmony_ci break; 29798c2ecf20Sopenharmony_ci } 29808c2ecf20Sopenharmony_ci 29818c2ecf20Sopenharmony_ci return format; 29828c2ecf20Sopenharmony_ci} 29838c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_of_parse_daifmt); 29848c2ecf20Sopenharmony_ci 29858c2ecf20Sopenharmony_ciint snd_soc_get_dai_id(struct device_node *ep) 29868c2ecf20Sopenharmony_ci{ 29878c2ecf20Sopenharmony_ci struct snd_soc_component *component; 29888c2ecf20Sopenharmony_ci struct snd_soc_dai_link_component dlc; 29898c2ecf20Sopenharmony_ci int ret; 29908c2ecf20Sopenharmony_ci 29918c2ecf20Sopenharmony_ci dlc.of_node = of_graph_get_port_parent(ep); 29928c2ecf20Sopenharmony_ci dlc.name = NULL; 29938c2ecf20Sopenharmony_ci /* 29948c2ecf20Sopenharmony_ci * For example HDMI case, HDMI has video/sound port, 29958c2ecf20Sopenharmony_ci * but ALSA SoC needs sound port number only. 29968c2ecf20Sopenharmony_ci * Thus counting HDMI DT port/endpoint doesn't work. 29978c2ecf20Sopenharmony_ci * Then, it should have .of_xlate_dai_id 29988c2ecf20Sopenharmony_ci */ 29998c2ecf20Sopenharmony_ci ret = -ENOTSUPP; 30008c2ecf20Sopenharmony_ci mutex_lock(&client_mutex); 30018c2ecf20Sopenharmony_ci component = soc_find_component(&dlc); 30028c2ecf20Sopenharmony_ci if (component) 30038c2ecf20Sopenharmony_ci ret = snd_soc_component_of_xlate_dai_id(component, ep); 30048c2ecf20Sopenharmony_ci mutex_unlock(&client_mutex); 30058c2ecf20Sopenharmony_ci 30068c2ecf20Sopenharmony_ci of_node_put(dlc.of_node); 30078c2ecf20Sopenharmony_ci 30088c2ecf20Sopenharmony_ci return ret; 30098c2ecf20Sopenharmony_ci} 30108c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_get_dai_id); 30118c2ecf20Sopenharmony_ci 30128c2ecf20Sopenharmony_ciint snd_soc_get_dai_name(struct of_phandle_args *args, 30138c2ecf20Sopenharmony_ci const char **dai_name) 30148c2ecf20Sopenharmony_ci{ 30158c2ecf20Sopenharmony_ci struct snd_soc_component *pos; 30168c2ecf20Sopenharmony_ci struct device_node *component_of_node; 30178c2ecf20Sopenharmony_ci int ret = -EPROBE_DEFER; 30188c2ecf20Sopenharmony_ci 30198c2ecf20Sopenharmony_ci mutex_lock(&client_mutex); 30208c2ecf20Sopenharmony_ci for_each_component(pos) { 30218c2ecf20Sopenharmony_ci component_of_node = soc_component_to_node(pos); 30228c2ecf20Sopenharmony_ci 30238c2ecf20Sopenharmony_ci if (component_of_node != args->np || !pos->num_dai) 30248c2ecf20Sopenharmony_ci continue; 30258c2ecf20Sopenharmony_ci 30268c2ecf20Sopenharmony_ci ret = snd_soc_component_of_xlate_dai_name(pos, args, dai_name); 30278c2ecf20Sopenharmony_ci if (ret == -ENOTSUPP) { 30288c2ecf20Sopenharmony_ci struct snd_soc_dai *dai; 30298c2ecf20Sopenharmony_ci int id = -1; 30308c2ecf20Sopenharmony_ci 30318c2ecf20Sopenharmony_ci switch (args->args_count) { 30328c2ecf20Sopenharmony_ci case 0: 30338c2ecf20Sopenharmony_ci id = 0; /* same as dai_drv[0] */ 30348c2ecf20Sopenharmony_ci break; 30358c2ecf20Sopenharmony_ci case 1: 30368c2ecf20Sopenharmony_ci id = args->args[0]; 30378c2ecf20Sopenharmony_ci break; 30388c2ecf20Sopenharmony_ci default: 30398c2ecf20Sopenharmony_ci /* not supported */ 30408c2ecf20Sopenharmony_ci break; 30418c2ecf20Sopenharmony_ci } 30428c2ecf20Sopenharmony_ci 30438c2ecf20Sopenharmony_ci if (id < 0 || id >= pos->num_dai) { 30448c2ecf20Sopenharmony_ci ret = -EINVAL; 30458c2ecf20Sopenharmony_ci continue; 30468c2ecf20Sopenharmony_ci } 30478c2ecf20Sopenharmony_ci 30488c2ecf20Sopenharmony_ci ret = 0; 30498c2ecf20Sopenharmony_ci 30508c2ecf20Sopenharmony_ci /* find target DAI */ 30518c2ecf20Sopenharmony_ci for_each_component_dais(pos, dai) { 30528c2ecf20Sopenharmony_ci if (id == 0) 30538c2ecf20Sopenharmony_ci break; 30548c2ecf20Sopenharmony_ci id--; 30558c2ecf20Sopenharmony_ci } 30568c2ecf20Sopenharmony_ci 30578c2ecf20Sopenharmony_ci *dai_name = dai->driver->name; 30588c2ecf20Sopenharmony_ci if (!*dai_name) 30598c2ecf20Sopenharmony_ci *dai_name = pos->name; 30608c2ecf20Sopenharmony_ci } else if (ret) { 30618c2ecf20Sopenharmony_ci /* 30628c2ecf20Sopenharmony_ci * if another error than ENOTSUPP is returned go on and 30638c2ecf20Sopenharmony_ci * check if another component is provided with the same 30648c2ecf20Sopenharmony_ci * node. This may happen if a device provides several 30658c2ecf20Sopenharmony_ci * components 30668c2ecf20Sopenharmony_ci */ 30678c2ecf20Sopenharmony_ci continue; 30688c2ecf20Sopenharmony_ci } 30698c2ecf20Sopenharmony_ci 30708c2ecf20Sopenharmony_ci break; 30718c2ecf20Sopenharmony_ci } 30728c2ecf20Sopenharmony_ci mutex_unlock(&client_mutex); 30738c2ecf20Sopenharmony_ci return ret; 30748c2ecf20Sopenharmony_ci} 30758c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_get_dai_name); 30768c2ecf20Sopenharmony_ci 30778c2ecf20Sopenharmony_ciint snd_soc_of_get_dai_name(struct device_node *of_node, 30788c2ecf20Sopenharmony_ci const char **dai_name) 30798c2ecf20Sopenharmony_ci{ 30808c2ecf20Sopenharmony_ci struct of_phandle_args args; 30818c2ecf20Sopenharmony_ci int ret; 30828c2ecf20Sopenharmony_ci 30838c2ecf20Sopenharmony_ci ret = of_parse_phandle_with_args(of_node, "sound-dai", 30848c2ecf20Sopenharmony_ci "#sound-dai-cells", 0, &args); 30858c2ecf20Sopenharmony_ci if (ret) 30868c2ecf20Sopenharmony_ci return ret; 30878c2ecf20Sopenharmony_ci 30888c2ecf20Sopenharmony_ci ret = snd_soc_get_dai_name(&args, dai_name); 30898c2ecf20Sopenharmony_ci 30908c2ecf20Sopenharmony_ci of_node_put(args.np); 30918c2ecf20Sopenharmony_ci 30928c2ecf20Sopenharmony_ci return ret; 30938c2ecf20Sopenharmony_ci} 30948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_of_get_dai_name); 30958c2ecf20Sopenharmony_ci 30968c2ecf20Sopenharmony_ci/* 30978c2ecf20Sopenharmony_ci * snd_soc_of_put_dai_link_codecs - Dereference device nodes in the codecs array 30988c2ecf20Sopenharmony_ci * @dai_link: DAI link 30998c2ecf20Sopenharmony_ci * 31008c2ecf20Sopenharmony_ci * Dereference device nodes acquired by snd_soc_of_get_dai_link_codecs(). 31018c2ecf20Sopenharmony_ci */ 31028c2ecf20Sopenharmony_civoid snd_soc_of_put_dai_link_codecs(struct snd_soc_dai_link *dai_link) 31038c2ecf20Sopenharmony_ci{ 31048c2ecf20Sopenharmony_ci struct snd_soc_dai_link_component *component; 31058c2ecf20Sopenharmony_ci int index; 31068c2ecf20Sopenharmony_ci 31078c2ecf20Sopenharmony_ci for_each_link_codecs(dai_link, index, component) { 31088c2ecf20Sopenharmony_ci if (!component->of_node) 31098c2ecf20Sopenharmony_ci break; 31108c2ecf20Sopenharmony_ci of_node_put(component->of_node); 31118c2ecf20Sopenharmony_ci component->of_node = NULL; 31128c2ecf20Sopenharmony_ci } 31138c2ecf20Sopenharmony_ci} 31148c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_of_put_dai_link_codecs); 31158c2ecf20Sopenharmony_ci 31168c2ecf20Sopenharmony_ci/* 31178c2ecf20Sopenharmony_ci * snd_soc_of_get_dai_link_codecs - Parse a list of CODECs in the devicetree 31188c2ecf20Sopenharmony_ci * @dev: Card device 31198c2ecf20Sopenharmony_ci * @of_node: Device node 31208c2ecf20Sopenharmony_ci * @dai_link: DAI link 31218c2ecf20Sopenharmony_ci * 31228c2ecf20Sopenharmony_ci * Builds an array of CODEC DAI components from the DAI link property 31238c2ecf20Sopenharmony_ci * 'sound-dai'. 31248c2ecf20Sopenharmony_ci * The array is set in the DAI link and the number of DAIs is set accordingly. 31258c2ecf20Sopenharmony_ci * The device nodes in the array (of_node) must be dereferenced by calling 31268c2ecf20Sopenharmony_ci * snd_soc_of_put_dai_link_codecs() on @dai_link. 31278c2ecf20Sopenharmony_ci * 31288c2ecf20Sopenharmony_ci * Returns 0 for success 31298c2ecf20Sopenharmony_ci */ 31308c2ecf20Sopenharmony_ciint snd_soc_of_get_dai_link_codecs(struct device *dev, 31318c2ecf20Sopenharmony_ci struct device_node *of_node, 31328c2ecf20Sopenharmony_ci struct snd_soc_dai_link *dai_link) 31338c2ecf20Sopenharmony_ci{ 31348c2ecf20Sopenharmony_ci struct of_phandle_args args; 31358c2ecf20Sopenharmony_ci struct snd_soc_dai_link_component *component; 31368c2ecf20Sopenharmony_ci char *name; 31378c2ecf20Sopenharmony_ci int index, num_codecs, ret; 31388c2ecf20Sopenharmony_ci 31398c2ecf20Sopenharmony_ci /* Count the number of CODECs */ 31408c2ecf20Sopenharmony_ci name = "sound-dai"; 31418c2ecf20Sopenharmony_ci num_codecs = of_count_phandle_with_args(of_node, name, 31428c2ecf20Sopenharmony_ci "#sound-dai-cells"); 31438c2ecf20Sopenharmony_ci if (num_codecs <= 0) { 31448c2ecf20Sopenharmony_ci if (num_codecs == -ENOENT) 31458c2ecf20Sopenharmony_ci dev_err(dev, "No 'sound-dai' property\n"); 31468c2ecf20Sopenharmony_ci else 31478c2ecf20Sopenharmony_ci dev_err(dev, "Bad phandle in 'sound-dai'\n"); 31488c2ecf20Sopenharmony_ci return num_codecs; 31498c2ecf20Sopenharmony_ci } 31508c2ecf20Sopenharmony_ci component = devm_kcalloc(dev, 31518c2ecf20Sopenharmony_ci num_codecs, sizeof(*component), 31528c2ecf20Sopenharmony_ci GFP_KERNEL); 31538c2ecf20Sopenharmony_ci if (!component) 31548c2ecf20Sopenharmony_ci return -ENOMEM; 31558c2ecf20Sopenharmony_ci dai_link->codecs = component; 31568c2ecf20Sopenharmony_ci dai_link->num_codecs = num_codecs; 31578c2ecf20Sopenharmony_ci 31588c2ecf20Sopenharmony_ci /* Parse the list */ 31598c2ecf20Sopenharmony_ci for_each_link_codecs(dai_link, index, component) { 31608c2ecf20Sopenharmony_ci ret = of_parse_phandle_with_args(of_node, name, 31618c2ecf20Sopenharmony_ci "#sound-dai-cells", 31628c2ecf20Sopenharmony_ci index, &args); 31638c2ecf20Sopenharmony_ci if (ret) 31648c2ecf20Sopenharmony_ci goto err; 31658c2ecf20Sopenharmony_ci component->of_node = args.np; 31668c2ecf20Sopenharmony_ci ret = snd_soc_get_dai_name(&args, &component->dai_name); 31678c2ecf20Sopenharmony_ci if (ret < 0) 31688c2ecf20Sopenharmony_ci goto err; 31698c2ecf20Sopenharmony_ci } 31708c2ecf20Sopenharmony_ci return 0; 31718c2ecf20Sopenharmony_cierr: 31728c2ecf20Sopenharmony_ci snd_soc_of_put_dai_link_codecs(dai_link); 31738c2ecf20Sopenharmony_ci dai_link->codecs = NULL; 31748c2ecf20Sopenharmony_ci dai_link->num_codecs = 0; 31758c2ecf20Sopenharmony_ci return ret; 31768c2ecf20Sopenharmony_ci} 31778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_of_get_dai_link_codecs); 31788c2ecf20Sopenharmony_ci 31798c2ecf20Sopenharmony_cistatic int __init snd_soc_init(void) 31808c2ecf20Sopenharmony_ci{ 31818c2ecf20Sopenharmony_ci int ret; 31828c2ecf20Sopenharmony_ci 31838c2ecf20Sopenharmony_ci snd_soc_debugfs_init(); 31848c2ecf20Sopenharmony_ci ret = snd_soc_util_init(); 31858c2ecf20Sopenharmony_ci if (ret) 31868c2ecf20Sopenharmony_ci goto err_util_init; 31878c2ecf20Sopenharmony_ci 31888c2ecf20Sopenharmony_ci ret = platform_driver_register(&soc_driver); 31898c2ecf20Sopenharmony_ci if (ret) 31908c2ecf20Sopenharmony_ci goto err_register; 31918c2ecf20Sopenharmony_ci return 0; 31928c2ecf20Sopenharmony_ci 31938c2ecf20Sopenharmony_cierr_register: 31948c2ecf20Sopenharmony_ci snd_soc_util_exit(); 31958c2ecf20Sopenharmony_cierr_util_init: 31968c2ecf20Sopenharmony_ci snd_soc_debugfs_exit(); 31978c2ecf20Sopenharmony_ci return ret; 31988c2ecf20Sopenharmony_ci} 31998c2ecf20Sopenharmony_cimodule_init(snd_soc_init); 32008c2ecf20Sopenharmony_ci 32018c2ecf20Sopenharmony_cistatic void __exit snd_soc_exit(void) 32028c2ecf20Sopenharmony_ci{ 32038c2ecf20Sopenharmony_ci snd_soc_util_exit(); 32048c2ecf20Sopenharmony_ci snd_soc_debugfs_exit(); 32058c2ecf20Sopenharmony_ci 32068c2ecf20Sopenharmony_ci platform_driver_unregister(&soc_driver); 32078c2ecf20Sopenharmony_ci} 32088c2ecf20Sopenharmony_cimodule_exit(snd_soc_exit); 32098c2ecf20Sopenharmony_ci 32108c2ecf20Sopenharmony_ci/* Module information */ 32118c2ecf20Sopenharmony_ciMODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk"); 32128c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ALSA SoC Core"); 32138c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 32148c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:soc-audio"); 3215