162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// soc-core.c -- ALSA SoC Audio Layer 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright 2005 Wolfson Microelectronics PLC. 662306a36Sopenharmony_ci// Copyright 2005 Openedhand Ltd. 762306a36Sopenharmony_ci// Copyright (C) 2010 Slimlogic Ltd. 862306a36Sopenharmony_ci// Copyright (C) 2010 Texas Instruments Inc. 962306a36Sopenharmony_ci// 1062306a36Sopenharmony_ci// Author: Liam Girdwood <lrg@slimlogic.co.uk> 1162306a36Sopenharmony_ci// with code, comments and ideas from :- 1262306a36Sopenharmony_ci// Richard Purdie <richard@openedhand.com> 1362306a36Sopenharmony_ci// 1462306a36Sopenharmony_ci// TODO: 1562306a36Sopenharmony_ci// o Add hw rules to enforce rates, etc. 1662306a36Sopenharmony_ci// o More testing with other codecs/machines. 1762306a36Sopenharmony_ci// o Add more codecs and platforms to ensure good API coverage. 1862306a36Sopenharmony_ci// o Support TDM on PCM and I2S 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/module.h> 2162306a36Sopenharmony_ci#include <linux/moduleparam.h> 2262306a36Sopenharmony_ci#include <linux/init.h> 2362306a36Sopenharmony_ci#include <linux/delay.h> 2462306a36Sopenharmony_ci#include <linux/pm.h> 2562306a36Sopenharmony_ci#include <linux/bitops.h> 2662306a36Sopenharmony_ci#include <linux/debugfs.h> 2762306a36Sopenharmony_ci#include <linux/platform_device.h> 2862306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h> 2962306a36Sopenharmony_ci#include <linux/ctype.h> 3062306a36Sopenharmony_ci#include <linux/slab.h> 3162306a36Sopenharmony_ci#include <linux/of.h> 3262306a36Sopenharmony_ci#include <linux/of_graph.h> 3362306a36Sopenharmony_ci#include <linux/dmi.h> 3462306a36Sopenharmony_ci#include <linux/acpi.h> 3562306a36Sopenharmony_ci#include <sound/core.h> 3662306a36Sopenharmony_ci#include <sound/pcm.h> 3762306a36Sopenharmony_ci#include <sound/pcm_params.h> 3862306a36Sopenharmony_ci#include <sound/soc.h> 3962306a36Sopenharmony_ci#include <sound/soc-dpcm.h> 4062306a36Sopenharmony_ci#include <sound/soc-topology.h> 4162306a36Sopenharmony_ci#include <sound/soc-link.h> 4262306a36Sopenharmony_ci#include <sound/initval.h> 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define CREATE_TRACE_POINTS 4562306a36Sopenharmony_ci#include <trace/events/asoc.h> 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic DEFINE_MUTEX(client_mutex); 4862306a36Sopenharmony_cistatic LIST_HEAD(component_list); 4962306a36Sopenharmony_cistatic LIST_HEAD(unbind_card_list); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define for_each_component(component) \ 5262306a36Sopenharmony_ci list_for_each_entry(component, &component_list, list) 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* 5562306a36Sopenharmony_ci * This is used if driver don't need to have CPU/Codec/Platform 5662306a36Sopenharmony_ci * dai_link. see soc.h 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_cistruct snd_soc_dai_link_component null_dailink_component[0]; 5962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(null_dailink_component); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* 6262306a36Sopenharmony_ci * This is a timeout to do a DAPM powerdown after a stream is closed(). 6362306a36Sopenharmony_ci * It can be used to eliminate pops between different playback streams, e.g. 6462306a36Sopenharmony_ci * between two audio tracks. 6562306a36Sopenharmony_ci */ 6662306a36Sopenharmony_cistatic int pmdown_time = 5000; 6762306a36Sopenharmony_cimodule_param(pmdown_time, int, 0); 6862306a36Sopenharmony_ciMODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)"); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic ssize_t pmdown_time_show(struct device *dev, 7162306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci return sysfs_emit(buf, "%ld\n", rtd->pmdown_time); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic ssize_t pmdown_time_store(struct device *dev, 7962306a36Sopenharmony_ci struct device_attribute *attr, 8062306a36Sopenharmony_ci const char *buf, size_t count) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev); 8362306a36Sopenharmony_ci int ret; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci ret = kstrtol(buf, 10, &rtd->pmdown_time); 8662306a36Sopenharmony_ci if (ret) 8762306a36Sopenharmony_ci return ret; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci return count; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic DEVICE_ATTR_RW(pmdown_time); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic struct attribute *soc_dev_attrs[] = { 9562306a36Sopenharmony_ci &dev_attr_pmdown_time.attr, 9662306a36Sopenharmony_ci NULL 9762306a36Sopenharmony_ci}; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic umode_t soc_dev_attr_is_visible(struct kobject *kobj, 10062306a36Sopenharmony_ci struct attribute *attr, int idx) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 10362306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (!rtd) 10662306a36Sopenharmony_ci return 0; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (attr == &dev_attr_pmdown_time.attr) 10962306a36Sopenharmony_ci return attr->mode; /* always visible */ 11062306a36Sopenharmony_ci return rtd->dai_link->num_codecs ? attr->mode : 0; /* enabled only with codec */ 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic const struct attribute_group soc_dapm_dev_group = { 11462306a36Sopenharmony_ci .attrs = soc_dapm_dev_attrs, 11562306a36Sopenharmony_ci .is_visible = soc_dev_attr_is_visible, 11662306a36Sopenharmony_ci}; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic const struct attribute_group soc_dev_group = { 11962306a36Sopenharmony_ci .attrs = soc_dev_attrs, 12062306a36Sopenharmony_ci .is_visible = soc_dev_attr_is_visible, 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic const struct attribute_group *soc_dev_attr_groups[] = { 12462306a36Sopenharmony_ci &soc_dapm_dev_group, 12562306a36Sopenharmony_ci &soc_dev_group, 12662306a36Sopenharmony_ci NULL 12762306a36Sopenharmony_ci}; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 13062306a36Sopenharmony_cistruct dentry *snd_soc_debugfs_root; 13162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_debugfs_root); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic void soc_init_component_debugfs(struct snd_soc_component *component) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci if (!component->card->debugfs_card_root) 13662306a36Sopenharmony_ci return; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (component->debugfs_prefix) { 13962306a36Sopenharmony_ci char *name; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci name = kasprintf(GFP_KERNEL, "%s:%s", 14262306a36Sopenharmony_ci component->debugfs_prefix, component->name); 14362306a36Sopenharmony_ci if (name) { 14462306a36Sopenharmony_ci component->debugfs_root = debugfs_create_dir(name, 14562306a36Sopenharmony_ci component->card->debugfs_card_root); 14662306a36Sopenharmony_ci kfree(name); 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci } else { 14962306a36Sopenharmony_ci component->debugfs_root = debugfs_create_dir(component->name, 15062306a36Sopenharmony_ci component->card->debugfs_card_root); 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci snd_soc_dapm_debugfs_init(snd_soc_component_get_dapm(component), 15462306a36Sopenharmony_ci component->debugfs_root); 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic void soc_cleanup_component_debugfs(struct snd_soc_component *component) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci if (!component->debugfs_root) 16062306a36Sopenharmony_ci return; 16162306a36Sopenharmony_ci debugfs_remove_recursive(component->debugfs_root); 16262306a36Sopenharmony_ci component->debugfs_root = NULL; 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic int dai_list_show(struct seq_file *m, void *v) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci struct snd_soc_component *component; 16862306a36Sopenharmony_ci struct snd_soc_dai *dai; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci mutex_lock(&client_mutex); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci for_each_component(component) 17362306a36Sopenharmony_ci for_each_component_dais(component, dai) 17462306a36Sopenharmony_ci seq_printf(m, "%s\n", dai->name); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci mutex_unlock(&client_mutex); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci return 0; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(dai_list); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic int component_list_show(struct seq_file *m, void *v) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct snd_soc_component *component; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci mutex_lock(&client_mutex); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci for_each_component(component) 18962306a36Sopenharmony_ci seq_printf(m, "%s\n", component->name); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci mutex_unlock(&client_mutex); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci return 0; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(component_list); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic void soc_init_card_debugfs(struct snd_soc_card *card) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci card->debugfs_card_root = debugfs_create_dir(card->name, 20062306a36Sopenharmony_ci snd_soc_debugfs_root); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci debugfs_create_u32("dapm_pop_time", 0644, card->debugfs_card_root, 20362306a36Sopenharmony_ci &card->pop_time); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci snd_soc_dapm_debugfs_init(&card->dapm, card->debugfs_card_root); 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic void soc_cleanup_card_debugfs(struct snd_soc_card *card) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci debugfs_remove_recursive(card->debugfs_card_root); 21162306a36Sopenharmony_ci card->debugfs_card_root = NULL; 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic void snd_soc_debugfs_init(void) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci snd_soc_debugfs_root = debugfs_create_dir("asoc", NULL); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci debugfs_create_file("dais", 0444, snd_soc_debugfs_root, NULL, 21962306a36Sopenharmony_ci &dai_list_fops); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci debugfs_create_file("components", 0444, snd_soc_debugfs_root, NULL, 22262306a36Sopenharmony_ci &component_list_fops); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic void snd_soc_debugfs_exit(void) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci debugfs_remove_recursive(snd_soc_debugfs_root); 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci#else 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic inline void soc_init_component_debugfs(struct snd_soc_component *component) { } 23362306a36Sopenharmony_cistatic inline void soc_cleanup_component_debugfs(struct snd_soc_component *component) { } 23462306a36Sopenharmony_cistatic inline void soc_init_card_debugfs(struct snd_soc_card *card) { } 23562306a36Sopenharmony_cistatic inline void soc_cleanup_card_debugfs(struct snd_soc_card *card) { } 23662306a36Sopenharmony_cistatic inline void snd_soc_debugfs_init(void) { } 23762306a36Sopenharmony_cistatic inline void snd_soc_debugfs_exit(void) { } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci#endif 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic int snd_soc_is_match_dai_args(struct of_phandle_args *args1, 24262306a36Sopenharmony_ci struct of_phandle_args *args2) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci if (!args1 || !args2) 24562306a36Sopenharmony_ci return 0; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (args1->np != args2->np) 24862306a36Sopenharmony_ci return 0; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci for (int i = 0; i < args1->args_count; i++) 25162306a36Sopenharmony_ci if (args1->args[i] != args2->args[i]) 25262306a36Sopenharmony_ci return 0; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci return 1; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic inline int snd_soc_dlc_component_is_empty(struct snd_soc_dai_link_component *dlc) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci return !(dlc->dai_args || dlc->name || dlc->of_node); 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic inline int snd_soc_dlc_component_is_invalid(struct snd_soc_dai_link_component *dlc) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci return (dlc->name && dlc->of_node); 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic inline int snd_soc_dlc_dai_is_empty(struct snd_soc_dai_link_component *dlc) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci return !(dlc->dai_args || dlc->dai_name); 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic int snd_soc_is_matching_dai(const struct snd_soc_dai_link_component *dlc, 27362306a36Sopenharmony_ci struct snd_soc_dai *dai) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci if (!dlc) 27662306a36Sopenharmony_ci return 0; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (dlc->dai_args) 27962306a36Sopenharmony_ci return snd_soc_is_match_dai_args(dai->driver->dai_args, dlc->dai_args); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (!dlc->dai_name) 28262306a36Sopenharmony_ci return 1; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* see snd_soc_dai_name_get() */ 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (strcmp(dlc->dai_name, dai->name) == 0) 28762306a36Sopenharmony_ci return 1; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (dai->driver->name && 29062306a36Sopenharmony_ci strcmp(dai->driver->name, dlc->dai_name) == 0) 29162306a36Sopenharmony_ci return 1; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (dai->component->name && 29462306a36Sopenharmony_ci strcmp(dlc->dai_name, dai->component->name) == 0) 29562306a36Sopenharmony_ci return 1; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci return 0; 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ciconst char *snd_soc_dai_name_get(struct snd_soc_dai *dai) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci /* see snd_soc_is_matching_dai() */ 30362306a36Sopenharmony_ci if (dai->name) 30462306a36Sopenharmony_ci return dai->name; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (dai->driver->name) 30762306a36Sopenharmony_ci return dai->driver->name; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (dai->component->name) 31062306a36Sopenharmony_ci return dai->component->name; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci return NULL; 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dai_name_get); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic int snd_soc_rtd_add_component(struct snd_soc_pcm_runtime *rtd, 31762306a36Sopenharmony_ci struct snd_soc_component *component) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci struct snd_soc_component *comp; 32062306a36Sopenharmony_ci int i; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci for_each_rtd_components(rtd, i, comp) { 32362306a36Sopenharmony_ci /* already connected */ 32462306a36Sopenharmony_ci if (comp == component) 32562306a36Sopenharmony_ci return 0; 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* see for_each_rtd_components */ 32962306a36Sopenharmony_ci rtd->components[rtd->num_components] = component; 33062306a36Sopenharmony_ci rtd->num_components++; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci return 0; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistruct snd_soc_component *snd_soc_rtdcom_lookup(struct snd_soc_pcm_runtime *rtd, 33662306a36Sopenharmony_ci const char *driver_name) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci struct snd_soc_component *component; 33962306a36Sopenharmony_ci int i; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (!driver_name) 34262306a36Sopenharmony_ci return NULL; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* 34562306a36Sopenharmony_ci * NOTE 34662306a36Sopenharmony_ci * 34762306a36Sopenharmony_ci * snd_soc_rtdcom_lookup() will find component from rtd by using 34862306a36Sopenharmony_ci * specified driver name. 34962306a36Sopenharmony_ci * But, if many components which have same driver name are connected 35062306a36Sopenharmony_ci * to 1 rtd, this function will return 1st found component. 35162306a36Sopenharmony_ci */ 35262306a36Sopenharmony_ci for_each_rtd_components(rtd, i, component) { 35362306a36Sopenharmony_ci const char *component_name = component->driver->name; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (!component_name) 35662306a36Sopenharmony_ci continue; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if ((component_name == driver_name) || 35962306a36Sopenharmony_ci strcmp(component_name, driver_name) == 0) 36062306a36Sopenharmony_ci return component; 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci return NULL; 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_rtdcom_lookup); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistruct snd_soc_component 36862306a36Sopenharmony_ci*snd_soc_lookup_component_nolocked(struct device *dev, const char *driver_name) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci struct snd_soc_component *component; 37162306a36Sopenharmony_ci struct snd_soc_component *found_component; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci found_component = NULL; 37462306a36Sopenharmony_ci for_each_component(component) { 37562306a36Sopenharmony_ci if ((dev == component->dev) && 37662306a36Sopenharmony_ci (!driver_name || 37762306a36Sopenharmony_ci (driver_name == component->driver->name) || 37862306a36Sopenharmony_ci (strcmp(component->driver->name, driver_name) == 0))) { 37962306a36Sopenharmony_ci found_component = component; 38062306a36Sopenharmony_ci break; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci return found_component; 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_lookup_component_nolocked); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistruct snd_soc_component *snd_soc_lookup_component(struct device *dev, 38962306a36Sopenharmony_ci const char *driver_name) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci struct snd_soc_component *component; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci mutex_lock(&client_mutex); 39462306a36Sopenharmony_ci component = snd_soc_lookup_component_nolocked(dev, driver_name); 39562306a36Sopenharmony_ci mutex_unlock(&client_mutex); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci return component; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_lookup_component); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistruct snd_soc_pcm_runtime 40262306a36Sopenharmony_ci*snd_soc_get_pcm_runtime(struct snd_soc_card *card, 40362306a36Sopenharmony_ci struct snd_soc_dai_link *dai_link) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci for_each_card_rtds(card, rtd) { 40862306a36Sopenharmony_ci if (rtd->dai_link == dai_link) 40962306a36Sopenharmony_ci return rtd; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci dev_dbg(card->dev, "ASoC: failed to find rtd %s\n", dai_link->name); 41262306a36Sopenharmony_ci return NULL; 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_get_pcm_runtime); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci/* 41762306a36Sopenharmony_ci * Power down the audio subsystem pmdown_time msecs after close is called. 41862306a36Sopenharmony_ci * This is to ensure there are no pops or clicks in between any music tracks 41962306a36Sopenharmony_ci * due to DAPM power cycling. 42062306a36Sopenharmony_ci */ 42162306a36Sopenharmony_civoid snd_soc_close_delayed_work(struct snd_soc_pcm_runtime *rtd) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); 42462306a36Sopenharmony_ci int playback = SNDRV_PCM_STREAM_PLAYBACK; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci snd_soc_dpcm_mutex_lock(rtd); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci dev_dbg(rtd->dev, 42962306a36Sopenharmony_ci "ASoC: pop wq checking: %s status: %s waiting: %s\n", 43062306a36Sopenharmony_ci codec_dai->driver->playback.stream_name, 43162306a36Sopenharmony_ci snd_soc_dai_stream_active(codec_dai, playback) ? 43262306a36Sopenharmony_ci "active" : "inactive", 43362306a36Sopenharmony_ci rtd->pop_wait ? "yes" : "no"); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci /* are we waiting on this codec DAI stream */ 43662306a36Sopenharmony_ci if (rtd->pop_wait == 1) { 43762306a36Sopenharmony_ci rtd->pop_wait = 0; 43862306a36Sopenharmony_ci snd_soc_dapm_stream_event(rtd, playback, 43962306a36Sopenharmony_ci SND_SOC_DAPM_STREAM_STOP); 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci snd_soc_dpcm_mutex_unlock(rtd); 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_close_delayed_work); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic void soc_release_rtd_dev(struct device *dev) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci /* "dev" means "rtd->dev" */ 44962306a36Sopenharmony_ci kfree(dev); 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistatic void soc_free_pcm_runtime(struct snd_soc_pcm_runtime *rtd) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci if (!rtd) 45562306a36Sopenharmony_ci return; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci list_del(&rtd->list); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (delayed_work_pending(&rtd->delayed_work)) 46062306a36Sopenharmony_ci flush_delayed_work(&rtd->delayed_work); 46162306a36Sopenharmony_ci snd_soc_pcm_component_free(rtd); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci /* 46462306a36Sopenharmony_ci * we don't need to call kfree() for rtd->dev 46562306a36Sopenharmony_ci * see 46662306a36Sopenharmony_ci * soc_release_rtd_dev() 46762306a36Sopenharmony_ci * 46862306a36Sopenharmony_ci * We don't need rtd->dev NULL check, because 46962306a36Sopenharmony_ci * it is alloced *before* rtd. 47062306a36Sopenharmony_ci * see 47162306a36Sopenharmony_ci * soc_new_pcm_runtime() 47262306a36Sopenharmony_ci * 47362306a36Sopenharmony_ci * We don't need to mind freeing for rtd, 47462306a36Sopenharmony_ci * because it was created from dev (= rtd->dev) 47562306a36Sopenharmony_ci * see 47662306a36Sopenharmony_ci * soc_new_pcm_runtime() 47762306a36Sopenharmony_ci * 47862306a36Sopenharmony_ci * rtd = devm_kzalloc(dev, ...); 47962306a36Sopenharmony_ci * rtd->dev = dev 48062306a36Sopenharmony_ci */ 48162306a36Sopenharmony_ci device_unregister(rtd->dev); 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cistatic void close_delayed_work(struct work_struct *work) { 48562306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = 48662306a36Sopenharmony_ci container_of(work, struct snd_soc_pcm_runtime, 48762306a36Sopenharmony_ci delayed_work.work); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if (rtd->close_delayed_work_func) 49062306a36Sopenharmony_ci rtd->close_delayed_work_func(rtd); 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_cistatic struct snd_soc_pcm_runtime *soc_new_pcm_runtime( 49462306a36Sopenharmony_ci struct snd_soc_card *card, struct snd_soc_dai_link *dai_link) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 49762306a36Sopenharmony_ci struct snd_soc_component *component; 49862306a36Sopenharmony_ci struct device *dev; 49962306a36Sopenharmony_ci int ret; 50062306a36Sopenharmony_ci int stream; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci /* 50362306a36Sopenharmony_ci * for rtd->dev 50462306a36Sopenharmony_ci */ 50562306a36Sopenharmony_ci dev = kzalloc(sizeof(struct device), GFP_KERNEL); 50662306a36Sopenharmony_ci if (!dev) 50762306a36Sopenharmony_ci return NULL; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci dev->parent = card->dev; 51062306a36Sopenharmony_ci dev->release = soc_release_rtd_dev; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci dev_set_name(dev, "%s", dai_link->name); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci ret = device_register(dev); 51562306a36Sopenharmony_ci if (ret < 0) { 51662306a36Sopenharmony_ci put_device(dev); /* soc_release_rtd_dev */ 51762306a36Sopenharmony_ci return NULL; 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci /* 52162306a36Sopenharmony_ci * for rtd 52262306a36Sopenharmony_ci */ 52362306a36Sopenharmony_ci rtd = devm_kzalloc(dev, 52462306a36Sopenharmony_ci sizeof(*rtd) + 52562306a36Sopenharmony_ci sizeof(component) * (dai_link->num_cpus + 52662306a36Sopenharmony_ci dai_link->num_codecs + 52762306a36Sopenharmony_ci dai_link->num_platforms), 52862306a36Sopenharmony_ci GFP_KERNEL); 52962306a36Sopenharmony_ci if (!rtd) { 53062306a36Sopenharmony_ci device_unregister(dev); 53162306a36Sopenharmony_ci return NULL; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci rtd->dev = dev; 53562306a36Sopenharmony_ci INIT_LIST_HEAD(&rtd->list); 53662306a36Sopenharmony_ci for_each_pcm_streams(stream) { 53762306a36Sopenharmony_ci INIT_LIST_HEAD(&rtd->dpcm[stream].be_clients); 53862306a36Sopenharmony_ci INIT_LIST_HEAD(&rtd->dpcm[stream].fe_clients); 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci dev_set_drvdata(dev, rtd); 54162306a36Sopenharmony_ci INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci /* 54462306a36Sopenharmony_ci * for rtd->dais 54562306a36Sopenharmony_ci */ 54662306a36Sopenharmony_ci rtd->dais = devm_kcalloc(dev, dai_link->num_cpus + dai_link->num_codecs, 54762306a36Sopenharmony_ci sizeof(struct snd_soc_dai *), 54862306a36Sopenharmony_ci GFP_KERNEL); 54962306a36Sopenharmony_ci if (!rtd->dais) 55062306a36Sopenharmony_ci goto free_rtd; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci /* 55362306a36Sopenharmony_ci * dais = [][][][][][][][][][][][][][][][][][] 55462306a36Sopenharmony_ci * ^cpu_dais ^codec_dais 55562306a36Sopenharmony_ci * |--- num_cpus ---|--- num_codecs --| 55662306a36Sopenharmony_ci * see 55762306a36Sopenharmony_ci * asoc_rtd_to_cpu() 55862306a36Sopenharmony_ci * asoc_rtd_to_codec() 55962306a36Sopenharmony_ci */ 56062306a36Sopenharmony_ci rtd->card = card; 56162306a36Sopenharmony_ci rtd->dai_link = dai_link; 56262306a36Sopenharmony_ci rtd->num = card->num_rtd++; 56362306a36Sopenharmony_ci rtd->pmdown_time = pmdown_time; /* default power off timeout */ 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci /* see for_each_card_rtds */ 56662306a36Sopenharmony_ci list_add_tail(&rtd->list, &card->rtd_list); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci ret = device_add_groups(dev, soc_dev_attr_groups); 56962306a36Sopenharmony_ci if (ret < 0) 57062306a36Sopenharmony_ci goto free_rtd; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci return rtd; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_cifree_rtd: 57562306a36Sopenharmony_ci soc_free_pcm_runtime(rtd); 57662306a36Sopenharmony_ci return NULL; 57762306a36Sopenharmony_ci} 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_cistatic void snd_soc_flush_all_delayed_work(struct snd_soc_card *card) 58062306a36Sopenharmony_ci{ 58162306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci for_each_card_rtds(card, rtd) 58462306a36Sopenharmony_ci flush_delayed_work(&rtd->delayed_work); 58562306a36Sopenharmony_ci} 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 58862306a36Sopenharmony_cistatic void soc_playback_digital_mute(struct snd_soc_card *card, int mute) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 59162306a36Sopenharmony_ci struct snd_soc_dai *dai; 59262306a36Sopenharmony_ci int playback = SNDRV_PCM_STREAM_PLAYBACK; 59362306a36Sopenharmony_ci int i; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci for_each_card_rtds(card, rtd) { 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (rtd->dai_link->ignore_suspend) 59862306a36Sopenharmony_ci continue; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci for_each_rtd_dais(rtd, i, dai) { 60162306a36Sopenharmony_ci if (snd_soc_dai_stream_active(dai, playback)) 60262306a36Sopenharmony_ci snd_soc_dai_digital_mute(dai, mute, playback); 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic void soc_dapm_suspend_resume(struct snd_soc_card *card, int event) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 61062306a36Sopenharmony_ci int stream; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci for_each_card_rtds(card, rtd) { 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci if (rtd->dai_link->ignore_suspend) 61562306a36Sopenharmony_ci continue; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci for_each_pcm_streams(stream) 61862306a36Sopenharmony_ci snd_soc_dapm_stream_event(rtd, stream, event); 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci} 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci/* powers down audio subsystem for suspend */ 62362306a36Sopenharmony_ciint snd_soc_suspend(struct device *dev) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci struct snd_soc_card *card = dev_get_drvdata(dev); 62662306a36Sopenharmony_ci struct snd_soc_component *component; 62762306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 62862306a36Sopenharmony_ci int i; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci /* If the card is not initialized yet there is nothing to do */ 63162306a36Sopenharmony_ci if (!snd_soc_card_is_instantiated(card)) 63262306a36Sopenharmony_ci return 0; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci /* 63562306a36Sopenharmony_ci * Due to the resume being scheduled into a workqueue we could 63662306a36Sopenharmony_ci * suspend before that's finished - wait for it to complete. 63762306a36Sopenharmony_ci */ 63862306a36Sopenharmony_ci snd_power_wait(card->snd_card); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci /* we're going to block userspace touching us until resume completes */ 64162306a36Sopenharmony_ci snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D3hot); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci /* mute any active DACs */ 64462306a36Sopenharmony_ci soc_playback_digital_mute(card, 1); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci /* suspend all pcms */ 64762306a36Sopenharmony_ci for_each_card_rtds(card, rtd) { 64862306a36Sopenharmony_ci if (rtd->dai_link->ignore_suspend) 64962306a36Sopenharmony_ci continue; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci snd_pcm_suspend_all(rtd->pcm); 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci snd_soc_card_suspend_pre(card); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci /* close any waiting streams */ 65762306a36Sopenharmony_ci snd_soc_flush_all_delayed_work(card); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci soc_dapm_suspend_resume(card, SND_SOC_DAPM_STREAM_SUSPEND); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci /* Recheck all endpoints too, their state is affected by suspend */ 66262306a36Sopenharmony_ci dapm_mark_endpoints_dirty(card); 66362306a36Sopenharmony_ci snd_soc_dapm_sync(&card->dapm); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci /* suspend all COMPONENTs */ 66662306a36Sopenharmony_ci for_each_card_rtds(card, rtd) { 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci if (rtd->dai_link->ignore_suspend) 66962306a36Sopenharmony_ci continue; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci for_each_rtd_components(rtd, i, component) { 67262306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm = 67362306a36Sopenharmony_ci snd_soc_component_get_dapm(component); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci /* 67662306a36Sopenharmony_ci * ignore if component was already suspended 67762306a36Sopenharmony_ci */ 67862306a36Sopenharmony_ci if (snd_soc_component_is_suspended(component)) 67962306a36Sopenharmony_ci continue; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci /* 68262306a36Sopenharmony_ci * If there are paths active then the COMPONENT will be 68362306a36Sopenharmony_ci * held with bias _ON and should not be suspended. 68462306a36Sopenharmony_ci */ 68562306a36Sopenharmony_ci switch (snd_soc_dapm_get_bias_level(dapm)) { 68662306a36Sopenharmony_ci case SND_SOC_BIAS_STANDBY: 68762306a36Sopenharmony_ci /* 68862306a36Sopenharmony_ci * If the COMPONENT is capable of idle 68962306a36Sopenharmony_ci * bias off then being in STANDBY 69062306a36Sopenharmony_ci * means it's doing something, 69162306a36Sopenharmony_ci * otherwise fall through. 69262306a36Sopenharmony_ci */ 69362306a36Sopenharmony_ci if (dapm->idle_bias_off) { 69462306a36Sopenharmony_ci dev_dbg(component->dev, 69562306a36Sopenharmony_ci "ASoC: idle_bias_off CODEC on over suspend\n"); 69662306a36Sopenharmony_ci break; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci fallthrough; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci case SND_SOC_BIAS_OFF: 70162306a36Sopenharmony_ci snd_soc_component_suspend(component); 70262306a36Sopenharmony_ci if (component->regmap) 70362306a36Sopenharmony_ci regcache_mark_dirty(component->regmap); 70462306a36Sopenharmony_ci /* deactivate pins to sleep state */ 70562306a36Sopenharmony_ci pinctrl_pm_select_sleep_state(component->dev); 70662306a36Sopenharmony_ci break; 70762306a36Sopenharmony_ci default: 70862306a36Sopenharmony_ci dev_dbg(component->dev, 70962306a36Sopenharmony_ci "ASoC: COMPONENT is on over suspend\n"); 71062306a36Sopenharmony_ci break; 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci snd_soc_card_suspend_post(card); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci return 0; 71862306a36Sopenharmony_ci} 71962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_suspend); 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci/* 72262306a36Sopenharmony_ci * deferred resume work, so resume can complete before we finished 72362306a36Sopenharmony_ci * setting our codec back up, which can be very slow on I2C 72462306a36Sopenharmony_ci */ 72562306a36Sopenharmony_cistatic void soc_resume_deferred(struct work_struct *work) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci struct snd_soc_card *card = 72862306a36Sopenharmony_ci container_of(work, struct snd_soc_card, 72962306a36Sopenharmony_ci deferred_resume_work); 73062306a36Sopenharmony_ci struct snd_soc_component *component; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci /* 73362306a36Sopenharmony_ci * our power state is still SNDRV_CTL_POWER_D3hot from suspend time, 73462306a36Sopenharmony_ci * so userspace apps are blocked from touching us 73562306a36Sopenharmony_ci */ 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci dev_dbg(card->dev, "ASoC: starting resume work\n"); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci /* Bring us up into D2 so that DAPM starts enabling things */ 74062306a36Sopenharmony_ci snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D2); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci snd_soc_card_resume_pre(card); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci for_each_card_components(card, component) { 74562306a36Sopenharmony_ci if (snd_soc_component_is_suspended(component)) 74662306a36Sopenharmony_ci snd_soc_component_resume(component); 74762306a36Sopenharmony_ci } 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci soc_dapm_suspend_resume(card, SND_SOC_DAPM_STREAM_RESUME); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci /* unmute any active DACs */ 75262306a36Sopenharmony_ci soc_playback_digital_mute(card, 0); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci snd_soc_card_resume_post(card); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci dev_dbg(card->dev, "ASoC: resume work completed\n"); 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci /* Recheck all endpoints too, their state is affected by suspend */ 75962306a36Sopenharmony_ci dapm_mark_endpoints_dirty(card); 76062306a36Sopenharmony_ci snd_soc_dapm_sync(&card->dapm); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci /* userspace can access us now we are back as we were before */ 76362306a36Sopenharmony_ci snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D0); 76462306a36Sopenharmony_ci} 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci/* powers up audio subsystem after a suspend */ 76762306a36Sopenharmony_ciint snd_soc_resume(struct device *dev) 76862306a36Sopenharmony_ci{ 76962306a36Sopenharmony_ci struct snd_soc_card *card = dev_get_drvdata(dev); 77062306a36Sopenharmony_ci struct snd_soc_component *component; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci /* If the card is not initialized yet there is nothing to do */ 77362306a36Sopenharmony_ci if (!snd_soc_card_is_instantiated(card)) 77462306a36Sopenharmony_ci return 0; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci /* activate pins from sleep state */ 77762306a36Sopenharmony_ci for_each_card_components(card, component) 77862306a36Sopenharmony_ci if (snd_soc_component_active(component)) 77962306a36Sopenharmony_ci pinctrl_pm_select_default_state(component->dev); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci dev_dbg(dev, "ASoC: Scheduling resume work\n"); 78262306a36Sopenharmony_ci if (!schedule_work(&card->deferred_resume_work)) 78362306a36Sopenharmony_ci dev_err(dev, "ASoC: resume work item may be lost\n"); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci return 0; 78662306a36Sopenharmony_ci} 78762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_resume); 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_cistatic void soc_resume_init(struct snd_soc_card *card) 79062306a36Sopenharmony_ci{ 79162306a36Sopenharmony_ci /* deferred resume work */ 79262306a36Sopenharmony_ci INIT_WORK(&card->deferred_resume_work, soc_resume_deferred); 79362306a36Sopenharmony_ci} 79462306a36Sopenharmony_ci#else 79562306a36Sopenharmony_ci#define snd_soc_suspend NULL 79662306a36Sopenharmony_ci#define snd_soc_resume NULL 79762306a36Sopenharmony_cistatic inline void soc_resume_init(struct snd_soc_card *card) { } 79862306a36Sopenharmony_ci#endif 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_cistatic struct device_node 80162306a36Sopenharmony_ci*soc_component_to_node(struct snd_soc_component *component) 80262306a36Sopenharmony_ci{ 80362306a36Sopenharmony_ci struct device_node *of_node; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci of_node = component->dev->of_node; 80662306a36Sopenharmony_ci if (!of_node && component->dev->parent) 80762306a36Sopenharmony_ci of_node = component->dev->parent->of_node; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci return of_node; 81062306a36Sopenharmony_ci} 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_cistruct of_phandle_args *snd_soc_copy_dai_args(struct device *dev, struct of_phandle_args *args) 81362306a36Sopenharmony_ci{ 81462306a36Sopenharmony_ci struct of_phandle_args *ret = devm_kzalloc(dev, sizeof(*ret), GFP_KERNEL); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci if (!ret) 81762306a36Sopenharmony_ci return NULL; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci *ret = *args; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci return ret; 82262306a36Sopenharmony_ci} 82362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_copy_dai_args); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_cistatic int snd_soc_is_matching_component( 82662306a36Sopenharmony_ci const struct snd_soc_dai_link_component *dlc, 82762306a36Sopenharmony_ci struct snd_soc_component *component) 82862306a36Sopenharmony_ci{ 82962306a36Sopenharmony_ci struct device_node *component_of_node; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci if (!dlc) 83262306a36Sopenharmony_ci return 0; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci if (dlc->dai_args) { 83562306a36Sopenharmony_ci struct snd_soc_dai *dai; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci for_each_component_dais(component, dai) 83862306a36Sopenharmony_ci if (snd_soc_is_matching_dai(dlc, dai)) 83962306a36Sopenharmony_ci return 1; 84062306a36Sopenharmony_ci return 0; 84162306a36Sopenharmony_ci } 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci component_of_node = soc_component_to_node(component); 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci if (dlc->of_node && component_of_node != dlc->of_node) 84662306a36Sopenharmony_ci return 0; 84762306a36Sopenharmony_ci if (dlc->name && strcmp(component->name, dlc->name)) 84862306a36Sopenharmony_ci return 0; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci return 1; 85162306a36Sopenharmony_ci} 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_cistatic struct snd_soc_component *soc_find_component( 85462306a36Sopenharmony_ci const struct snd_soc_dai_link_component *dlc) 85562306a36Sopenharmony_ci{ 85662306a36Sopenharmony_ci struct snd_soc_component *component; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci lockdep_assert_held(&client_mutex); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci /* 86162306a36Sopenharmony_ci * NOTE 86262306a36Sopenharmony_ci * 86362306a36Sopenharmony_ci * It returns *1st* found component, but some driver 86462306a36Sopenharmony_ci * has few components by same of_node/name 86562306a36Sopenharmony_ci * ex) 86662306a36Sopenharmony_ci * CPU component and generic DMAEngine component 86762306a36Sopenharmony_ci */ 86862306a36Sopenharmony_ci for_each_component(component) 86962306a36Sopenharmony_ci if (snd_soc_is_matching_component(dlc, component)) 87062306a36Sopenharmony_ci return component; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci return NULL; 87362306a36Sopenharmony_ci} 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci/** 87662306a36Sopenharmony_ci * snd_soc_find_dai - Find a registered DAI 87762306a36Sopenharmony_ci * 87862306a36Sopenharmony_ci * @dlc: name of the DAI or the DAI driver and optional component info to match 87962306a36Sopenharmony_ci * 88062306a36Sopenharmony_ci * This function will search all registered components and their DAIs to 88162306a36Sopenharmony_ci * find the DAI of the same name. The component's of_node and name 88262306a36Sopenharmony_ci * should also match if being specified. 88362306a36Sopenharmony_ci * 88462306a36Sopenharmony_ci * Return: pointer of DAI, or NULL if not found. 88562306a36Sopenharmony_ci */ 88662306a36Sopenharmony_cistruct snd_soc_dai *snd_soc_find_dai( 88762306a36Sopenharmony_ci const struct snd_soc_dai_link_component *dlc) 88862306a36Sopenharmony_ci{ 88962306a36Sopenharmony_ci struct snd_soc_component *component; 89062306a36Sopenharmony_ci struct snd_soc_dai *dai; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci lockdep_assert_held(&client_mutex); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci /* Find CPU DAI from registered DAIs */ 89562306a36Sopenharmony_ci for_each_component(component) 89662306a36Sopenharmony_ci if (snd_soc_is_matching_component(dlc, component)) 89762306a36Sopenharmony_ci for_each_component_dais(component, dai) 89862306a36Sopenharmony_ci if (snd_soc_is_matching_dai(dlc, dai)) 89962306a36Sopenharmony_ci return dai; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci return NULL; 90262306a36Sopenharmony_ci} 90362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_find_dai); 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_cistruct snd_soc_dai *snd_soc_find_dai_with_mutex( 90662306a36Sopenharmony_ci const struct snd_soc_dai_link_component *dlc) 90762306a36Sopenharmony_ci{ 90862306a36Sopenharmony_ci struct snd_soc_dai *dai; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci mutex_lock(&client_mutex); 91162306a36Sopenharmony_ci dai = snd_soc_find_dai(dlc); 91262306a36Sopenharmony_ci mutex_unlock(&client_mutex); 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci return dai; 91562306a36Sopenharmony_ci} 91662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_find_dai_with_mutex); 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_cistatic int soc_dai_link_sanity_check(struct snd_soc_card *card, 91962306a36Sopenharmony_ci struct snd_soc_dai_link *link) 92062306a36Sopenharmony_ci{ 92162306a36Sopenharmony_ci int i; 92262306a36Sopenharmony_ci struct snd_soc_dai_link_component *dlc; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci /* Codec check */ 92562306a36Sopenharmony_ci for_each_link_codecs(link, i, dlc) { 92662306a36Sopenharmony_ci /* 92762306a36Sopenharmony_ci * Codec must be specified by 1 of name or OF node, 92862306a36Sopenharmony_ci * not both or neither. 92962306a36Sopenharmony_ci */ 93062306a36Sopenharmony_ci if (snd_soc_dlc_component_is_invalid(dlc)) 93162306a36Sopenharmony_ci goto component_invalid; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci if (snd_soc_dlc_component_is_empty(dlc)) 93462306a36Sopenharmony_ci goto component_empty; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci /* Codec DAI name must be specified */ 93762306a36Sopenharmony_ci if (snd_soc_dlc_dai_is_empty(dlc)) 93862306a36Sopenharmony_ci goto dai_empty; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci /* 94162306a36Sopenharmony_ci * Defer card registration if codec component is not added to 94262306a36Sopenharmony_ci * component list. 94362306a36Sopenharmony_ci */ 94462306a36Sopenharmony_ci if (!soc_find_component(dlc)) 94562306a36Sopenharmony_ci goto component_not_found; 94662306a36Sopenharmony_ci } 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci /* Platform check */ 94962306a36Sopenharmony_ci for_each_link_platforms(link, i, dlc) { 95062306a36Sopenharmony_ci /* 95162306a36Sopenharmony_ci * Platform may be specified by either name or OF node, but it 95262306a36Sopenharmony_ci * can be left unspecified, then no components will be inserted 95362306a36Sopenharmony_ci * in the rtdcom list 95462306a36Sopenharmony_ci */ 95562306a36Sopenharmony_ci if (snd_soc_dlc_component_is_invalid(dlc)) 95662306a36Sopenharmony_ci goto component_invalid; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci if (snd_soc_dlc_component_is_empty(dlc)) 95962306a36Sopenharmony_ci goto component_empty; 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci /* 96262306a36Sopenharmony_ci * Defer card registration if platform component is not added to 96362306a36Sopenharmony_ci * component list. 96462306a36Sopenharmony_ci */ 96562306a36Sopenharmony_ci if (!soc_find_component(dlc)) 96662306a36Sopenharmony_ci goto component_not_found; 96762306a36Sopenharmony_ci } 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci /* CPU check */ 97062306a36Sopenharmony_ci for_each_link_cpus(link, i, dlc) { 97162306a36Sopenharmony_ci /* 97262306a36Sopenharmony_ci * CPU device may be specified by either name or OF node, but 97362306a36Sopenharmony_ci * can be left unspecified, and will be matched based on DAI 97462306a36Sopenharmony_ci * name alone.. 97562306a36Sopenharmony_ci */ 97662306a36Sopenharmony_ci if (snd_soc_dlc_component_is_invalid(dlc)) 97762306a36Sopenharmony_ci goto component_invalid; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci if (snd_soc_dlc_component_is_empty(dlc)) { 98162306a36Sopenharmony_ci /* 98262306a36Sopenharmony_ci * At least one of CPU DAI name or CPU device name/node must be specified 98362306a36Sopenharmony_ci */ 98462306a36Sopenharmony_ci if (snd_soc_dlc_dai_is_empty(dlc)) 98562306a36Sopenharmony_ci goto component_dai_empty; 98662306a36Sopenharmony_ci } else { 98762306a36Sopenharmony_ci /* 98862306a36Sopenharmony_ci * Defer card registration if Component is not added 98962306a36Sopenharmony_ci */ 99062306a36Sopenharmony_ci if (!soc_find_component(dlc)) 99162306a36Sopenharmony_ci goto component_not_found; 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci } 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci return 0; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_cicomponent_invalid: 99862306a36Sopenharmony_ci dev_err(card->dev, "ASoC: Both Component name/of_node are set for %s\n", link->name); 99962306a36Sopenharmony_ci return -EINVAL; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_cicomponent_empty: 100262306a36Sopenharmony_ci dev_err(card->dev, "ASoC: Neither Component name/of_node are set for %s\n", link->name); 100362306a36Sopenharmony_ci return -EINVAL; 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_cicomponent_not_found: 100662306a36Sopenharmony_ci dev_dbg(card->dev, "ASoC: Component %s not found for link %s\n", dlc->name, link->name); 100762306a36Sopenharmony_ci return -EPROBE_DEFER; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_cidai_empty: 101062306a36Sopenharmony_ci dev_err(card->dev, "ASoC: DAI name is not set for %s\n", link->name); 101162306a36Sopenharmony_ci return -EINVAL; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_cicomponent_dai_empty: 101462306a36Sopenharmony_ci dev_err(card->dev, "ASoC: Neither DAI/Component name/of_node are set for %s\n", link->name); 101562306a36Sopenharmony_ci return -EINVAL; 101662306a36Sopenharmony_ci} 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci/** 101962306a36Sopenharmony_ci * snd_soc_remove_pcm_runtime - Remove a pcm_runtime from card 102062306a36Sopenharmony_ci * @card: The ASoC card to which the pcm_runtime has 102162306a36Sopenharmony_ci * @rtd: The pcm_runtime to remove 102262306a36Sopenharmony_ci * 102362306a36Sopenharmony_ci * This function removes a pcm_runtime from the ASoC card. 102462306a36Sopenharmony_ci */ 102562306a36Sopenharmony_civoid snd_soc_remove_pcm_runtime(struct snd_soc_card *card, 102662306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd) 102762306a36Sopenharmony_ci{ 102862306a36Sopenharmony_ci lockdep_assert_held(&client_mutex); 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci /* 103162306a36Sopenharmony_ci * Notify the machine driver for extra destruction 103262306a36Sopenharmony_ci */ 103362306a36Sopenharmony_ci snd_soc_card_remove_dai_link(card, rtd->dai_link); 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci soc_free_pcm_runtime(rtd); 103662306a36Sopenharmony_ci} 103762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_remove_pcm_runtime); 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci/** 104062306a36Sopenharmony_ci * snd_soc_add_pcm_runtime - Add a pcm_runtime dynamically via dai_link 104162306a36Sopenharmony_ci * @card: The ASoC card to which the pcm_runtime is added 104262306a36Sopenharmony_ci * @dai_link: The DAI link to find pcm_runtime 104362306a36Sopenharmony_ci * 104462306a36Sopenharmony_ci * This function adds a pcm_runtime ASoC card by using dai_link. 104562306a36Sopenharmony_ci * 104662306a36Sopenharmony_ci * Note: Topology can use this API to add pcm_runtime when probing the 104762306a36Sopenharmony_ci * topology component. And machine drivers can still define static 104862306a36Sopenharmony_ci * DAI links in dai_link array. 104962306a36Sopenharmony_ci */ 105062306a36Sopenharmony_cistatic int snd_soc_add_pcm_runtime(struct snd_soc_card *card, 105162306a36Sopenharmony_ci struct snd_soc_dai_link *dai_link) 105262306a36Sopenharmony_ci{ 105362306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 105462306a36Sopenharmony_ci struct snd_soc_dai_link_component *codec, *platform, *cpu; 105562306a36Sopenharmony_ci struct snd_soc_component *component; 105662306a36Sopenharmony_ci int i, ret; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci lockdep_assert_held(&client_mutex); 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci /* 106162306a36Sopenharmony_ci * Notify the machine driver for extra initialization 106262306a36Sopenharmony_ci */ 106362306a36Sopenharmony_ci ret = snd_soc_card_add_dai_link(card, dai_link); 106462306a36Sopenharmony_ci if (ret < 0) 106562306a36Sopenharmony_ci return ret; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci if (dai_link->ignore) 106862306a36Sopenharmony_ci return 0; 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci dev_dbg(card->dev, "ASoC: binding %s\n", dai_link->name); 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci ret = soc_dai_link_sanity_check(card, dai_link); 107362306a36Sopenharmony_ci if (ret < 0) 107462306a36Sopenharmony_ci return ret; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci rtd = soc_new_pcm_runtime(card, dai_link); 107762306a36Sopenharmony_ci if (!rtd) 107862306a36Sopenharmony_ci return -ENOMEM; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci for_each_link_cpus(dai_link, i, cpu) { 108162306a36Sopenharmony_ci asoc_rtd_to_cpu(rtd, i) = snd_soc_find_dai(cpu); 108262306a36Sopenharmony_ci if (!asoc_rtd_to_cpu(rtd, i)) { 108362306a36Sopenharmony_ci dev_info(card->dev, "ASoC: CPU DAI %s not registered\n", 108462306a36Sopenharmony_ci cpu->dai_name); 108562306a36Sopenharmony_ci goto _err_defer; 108662306a36Sopenharmony_ci } 108762306a36Sopenharmony_ci snd_soc_rtd_add_component(rtd, asoc_rtd_to_cpu(rtd, i)->component); 108862306a36Sopenharmony_ci } 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci /* Find CODEC from registered CODECs */ 109162306a36Sopenharmony_ci for_each_link_codecs(dai_link, i, codec) { 109262306a36Sopenharmony_ci asoc_rtd_to_codec(rtd, i) = snd_soc_find_dai(codec); 109362306a36Sopenharmony_ci if (!asoc_rtd_to_codec(rtd, i)) { 109462306a36Sopenharmony_ci dev_info(card->dev, "ASoC: CODEC DAI %s not registered\n", 109562306a36Sopenharmony_ci codec->dai_name); 109662306a36Sopenharmony_ci goto _err_defer; 109762306a36Sopenharmony_ci } 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci snd_soc_rtd_add_component(rtd, asoc_rtd_to_codec(rtd, i)->component); 110062306a36Sopenharmony_ci } 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci /* Find PLATFORM from registered PLATFORMs */ 110362306a36Sopenharmony_ci for_each_link_platforms(dai_link, i, platform) { 110462306a36Sopenharmony_ci for_each_component(component) { 110562306a36Sopenharmony_ci if (!snd_soc_is_matching_component(platform, component)) 110662306a36Sopenharmony_ci continue; 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci snd_soc_rtd_add_component(rtd, component); 110962306a36Sopenharmony_ci } 111062306a36Sopenharmony_ci } 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci return 0; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci_err_defer: 111562306a36Sopenharmony_ci snd_soc_remove_pcm_runtime(card, rtd); 111662306a36Sopenharmony_ci return -EPROBE_DEFER; 111762306a36Sopenharmony_ci} 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ciint snd_soc_add_pcm_runtimes(struct snd_soc_card *card, 112062306a36Sopenharmony_ci struct snd_soc_dai_link *dai_link, 112162306a36Sopenharmony_ci int num_dai_link) 112262306a36Sopenharmony_ci{ 112362306a36Sopenharmony_ci for (int i = 0; i < num_dai_link; i++) { 112462306a36Sopenharmony_ci int ret = snd_soc_add_pcm_runtime(card, dai_link + i); 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci if (ret < 0) 112762306a36Sopenharmony_ci return ret; 112862306a36Sopenharmony_ci } 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci return 0; 113162306a36Sopenharmony_ci} 113262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_add_pcm_runtimes); 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_cistatic void snd_soc_runtime_get_dai_fmt(struct snd_soc_pcm_runtime *rtd) 113562306a36Sopenharmony_ci{ 113662306a36Sopenharmony_ci struct snd_soc_dai_link *dai_link = rtd->dai_link; 113762306a36Sopenharmony_ci struct snd_soc_dai *dai, *not_used; 113862306a36Sopenharmony_ci u64 pos, possible_fmt; 113962306a36Sopenharmony_ci unsigned int mask = 0, dai_fmt = 0; 114062306a36Sopenharmony_ci int i, j, priority, pri, until; 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci /* 114362306a36Sopenharmony_ci * Get selectable format from each DAIs. 114462306a36Sopenharmony_ci * 114562306a36Sopenharmony_ci **************************** 114662306a36Sopenharmony_ci * NOTE 114762306a36Sopenharmony_ci * Using .auto_selectable_formats is not mandatory, 114862306a36Sopenharmony_ci * we can select format manually from Sound Card. 114962306a36Sopenharmony_ci * When use it, driver should list well tested format only. 115062306a36Sopenharmony_ci **************************** 115162306a36Sopenharmony_ci * 115262306a36Sopenharmony_ci * ex) 115362306a36Sopenharmony_ci * auto_selectable_formats (= SND_SOC_POSSIBLE_xxx) 115462306a36Sopenharmony_ci * (A) (B) (C) 115562306a36Sopenharmony_ci * DAI0_: { 0x000F, 0x00F0, 0x0F00 }; 115662306a36Sopenharmony_ci * DAI1 : { 0xF000, 0x0F00 }; 115762306a36Sopenharmony_ci * (X) (Y) 115862306a36Sopenharmony_ci * 115962306a36Sopenharmony_ci * "until" will be 3 in this case (MAX array size from DAI0 and DAI1) 116062306a36Sopenharmony_ci * Here is dev_dbg() message and comments 116162306a36Sopenharmony_ci * 116262306a36Sopenharmony_ci * priority = 1 116362306a36Sopenharmony_ci * DAI0: (pri, fmt) = (1, 000000000000000F) // 1st check (A) DAI1 is not selected 116462306a36Sopenharmony_ci * DAI1: (pri, fmt) = (0, 0000000000000000) // Necessary Waste 116562306a36Sopenharmony_ci * DAI0: (pri, fmt) = (1, 000000000000000F) // 2nd check (A) 116662306a36Sopenharmony_ci * DAI1: (pri, fmt) = (1, 000000000000F000) // (X) 116762306a36Sopenharmony_ci * priority = 2 116862306a36Sopenharmony_ci * DAI0: (pri, fmt) = (2, 00000000000000FF) // 3rd check (A) + (B) 116962306a36Sopenharmony_ci * DAI1: (pri, fmt) = (1, 000000000000F000) // (X) 117062306a36Sopenharmony_ci * DAI0: (pri, fmt) = (2, 00000000000000FF) // 4th check (A) + (B) 117162306a36Sopenharmony_ci * DAI1: (pri, fmt) = (2, 000000000000FF00) // (X) + (Y) 117262306a36Sopenharmony_ci * priority = 3 117362306a36Sopenharmony_ci * DAI0: (pri, fmt) = (3, 0000000000000FFF) // 5th check (A) + (B) + (C) 117462306a36Sopenharmony_ci * DAI1: (pri, fmt) = (2, 000000000000FF00) // (X) + (Y) 117562306a36Sopenharmony_ci * found auto selected format: 0000000000000F00 117662306a36Sopenharmony_ci */ 117762306a36Sopenharmony_ci until = snd_soc_dai_get_fmt_max_priority(rtd); 117862306a36Sopenharmony_ci for (priority = 1; priority <= until; priority++) { 117962306a36Sopenharmony_ci for_each_rtd_dais(rtd, j, not_used) { 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci possible_fmt = ULLONG_MAX; 118262306a36Sopenharmony_ci for_each_rtd_dais(rtd, i, dai) { 118362306a36Sopenharmony_ci u64 fmt = 0; 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci pri = (j >= i) ? priority : priority - 1; 118662306a36Sopenharmony_ci fmt = snd_soc_dai_get_fmt(dai, pri); 118762306a36Sopenharmony_ci possible_fmt &= fmt; 118862306a36Sopenharmony_ci } 118962306a36Sopenharmony_ci if (possible_fmt) 119062306a36Sopenharmony_ci goto found; 119162306a36Sopenharmony_ci } 119262306a36Sopenharmony_ci } 119362306a36Sopenharmony_ci /* Not Found */ 119462306a36Sopenharmony_ci return; 119562306a36Sopenharmony_cifound: 119662306a36Sopenharmony_ci /* 119762306a36Sopenharmony_ci * convert POSSIBLE_DAIFMT to DAIFMT 119862306a36Sopenharmony_ci * 119962306a36Sopenharmony_ci * Some basic/default settings on each is defined as 0. 120062306a36Sopenharmony_ci * see 120162306a36Sopenharmony_ci * SND_SOC_DAIFMT_NB_NF 120262306a36Sopenharmony_ci * SND_SOC_DAIFMT_GATED 120362306a36Sopenharmony_ci * 120462306a36Sopenharmony_ci * SND_SOC_DAIFMT_xxx_MASK can't notice it if Sound Card specify 120562306a36Sopenharmony_ci * these value, and will be overwrite to auto selected value. 120662306a36Sopenharmony_ci * 120762306a36Sopenharmony_ci * To avoid such issue, loop from 63 to 0 here. 120862306a36Sopenharmony_ci * Small number of SND_SOC_POSSIBLE_xxx will be Hi priority. 120962306a36Sopenharmony_ci * Basic/Default settings of each part and aboves are defined 121062306a36Sopenharmony_ci * as Hi priority (= small number) of SND_SOC_POSSIBLE_xxx. 121162306a36Sopenharmony_ci */ 121262306a36Sopenharmony_ci for (i = 63; i >= 0; i--) { 121362306a36Sopenharmony_ci pos = 1ULL << i; 121462306a36Sopenharmony_ci switch (possible_fmt & pos) { 121562306a36Sopenharmony_ci /* 121662306a36Sopenharmony_ci * for format 121762306a36Sopenharmony_ci */ 121862306a36Sopenharmony_ci case SND_SOC_POSSIBLE_DAIFMT_I2S: 121962306a36Sopenharmony_ci case SND_SOC_POSSIBLE_DAIFMT_RIGHT_J: 122062306a36Sopenharmony_ci case SND_SOC_POSSIBLE_DAIFMT_LEFT_J: 122162306a36Sopenharmony_ci case SND_SOC_POSSIBLE_DAIFMT_DSP_A: 122262306a36Sopenharmony_ci case SND_SOC_POSSIBLE_DAIFMT_DSP_B: 122362306a36Sopenharmony_ci case SND_SOC_POSSIBLE_DAIFMT_AC97: 122462306a36Sopenharmony_ci case SND_SOC_POSSIBLE_DAIFMT_PDM: 122562306a36Sopenharmony_ci dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_FORMAT_MASK) | i; 122662306a36Sopenharmony_ci break; 122762306a36Sopenharmony_ci /* 122862306a36Sopenharmony_ci * for clock 122962306a36Sopenharmony_ci */ 123062306a36Sopenharmony_ci case SND_SOC_POSSIBLE_DAIFMT_CONT: 123162306a36Sopenharmony_ci dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_CLOCK_MASK) | SND_SOC_DAIFMT_CONT; 123262306a36Sopenharmony_ci break; 123362306a36Sopenharmony_ci case SND_SOC_POSSIBLE_DAIFMT_GATED: 123462306a36Sopenharmony_ci dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_CLOCK_MASK) | SND_SOC_DAIFMT_GATED; 123562306a36Sopenharmony_ci break; 123662306a36Sopenharmony_ci /* 123762306a36Sopenharmony_ci * for clock invert 123862306a36Sopenharmony_ci */ 123962306a36Sopenharmony_ci case SND_SOC_POSSIBLE_DAIFMT_NB_NF: 124062306a36Sopenharmony_ci dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_INV_MASK) | SND_SOC_DAIFMT_NB_NF; 124162306a36Sopenharmony_ci break; 124262306a36Sopenharmony_ci case SND_SOC_POSSIBLE_DAIFMT_NB_IF: 124362306a36Sopenharmony_ci dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_INV_MASK) | SND_SOC_DAIFMT_NB_IF; 124462306a36Sopenharmony_ci break; 124562306a36Sopenharmony_ci case SND_SOC_POSSIBLE_DAIFMT_IB_NF: 124662306a36Sopenharmony_ci dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_INV_MASK) | SND_SOC_DAIFMT_IB_NF; 124762306a36Sopenharmony_ci break; 124862306a36Sopenharmony_ci case SND_SOC_POSSIBLE_DAIFMT_IB_IF: 124962306a36Sopenharmony_ci dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_INV_MASK) | SND_SOC_DAIFMT_IB_IF; 125062306a36Sopenharmony_ci break; 125162306a36Sopenharmony_ci /* 125262306a36Sopenharmony_ci * for clock provider / consumer 125362306a36Sopenharmony_ci */ 125462306a36Sopenharmony_ci case SND_SOC_POSSIBLE_DAIFMT_CBP_CFP: 125562306a36Sopenharmony_ci dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) | SND_SOC_DAIFMT_CBP_CFP; 125662306a36Sopenharmony_ci break; 125762306a36Sopenharmony_ci case SND_SOC_POSSIBLE_DAIFMT_CBC_CFP: 125862306a36Sopenharmony_ci dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) | SND_SOC_DAIFMT_CBC_CFP; 125962306a36Sopenharmony_ci break; 126062306a36Sopenharmony_ci case SND_SOC_POSSIBLE_DAIFMT_CBP_CFC: 126162306a36Sopenharmony_ci dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) | SND_SOC_DAIFMT_CBP_CFC; 126262306a36Sopenharmony_ci break; 126362306a36Sopenharmony_ci case SND_SOC_POSSIBLE_DAIFMT_CBC_CFC: 126462306a36Sopenharmony_ci dai_fmt = (dai_fmt & ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) | SND_SOC_DAIFMT_CBC_CFC; 126562306a36Sopenharmony_ci break; 126662306a36Sopenharmony_ci } 126762306a36Sopenharmony_ci } 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci /* 127062306a36Sopenharmony_ci * Some driver might have very complex limitation. 127162306a36Sopenharmony_ci * In such case, user want to auto-select non-limitation part, 127262306a36Sopenharmony_ci * and want to manually specify complex part. 127362306a36Sopenharmony_ci * 127462306a36Sopenharmony_ci * Or for example, if both CPU and Codec can be clock provider, 127562306a36Sopenharmony_ci * but because of its quality, user want to specify it manually. 127662306a36Sopenharmony_ci * 127762306a36Sopenharmony_ci * Use manually specified settings if sound card did. 127862306a36Sopenharmony_ci */ 127962306a36Sopenharmony_ci if (!(dai_link->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK)) 128062306a36Sopenharmony_ci mask |= SND_SOC_DAIFMT_FORMAT_MASK; 128162306a36Sopenharmony_ci if (!(dai_link->dai_fmt & SND_SOC_DAIFMT_CLOCK_MASK)) 128262306a36Sopenharmony_ci mask |= SND_SOC_DAIFMT_CLOCK_MASK; 128362306a36Sopenharmony_ci if (!(dai_link->dai_fmt & SND_SOC_DAIFMT_INV_MASK)) 128462306a36Sopenharmony_ci mask |= SND_SOC_DAIFMT_INV_MASK; 128562306a36Sopenharmony_ci if (!(dai_link->dai_fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK)) 128662306a36Sopenharmony_ci mask |= SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK; 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci dai_link->dai_fmt |= (dai_fmt & mask); 128962306a36Sopenharmony_ci} 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci/** 129262306a36Sopenharmony_ci * snd_soc_runtime_set_dai_fmt() - Change DAI link format for a ASoC runtime 129362306a36Sopenharmony_ci * @rtd: The runtime for which the DAI link format should be changed 129462306a36Sopenharmony_ci * @dai_fmt: The new DAI link format 129562306a36Sopenharmony_ci * 129662306a36Sopenharmony_ci * This function updates the DAI link format for all DAIs connected to the DAI 129762306a36Sopenharmony_ci * link for the specified runtime. 129862306a36Sopenharmony_ci * 129962306a36Sopenharmony_ci * Note: For setups with a static format set the dai_fmt field in the 130062306a36Sopenharmony_ci * corresponding snd_dai_link struct instead of using this function. 130162306a36Sopenharmony_ci * 130262306a36Sopenharmony_ci * Returns 0 on success, otherwise a negative error code. 130362306a36Sopenharmony_ci */ 130462306a36Sopenharmony_ciint snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd, 130562306a36Sopenharmony_ci unsigned int dai_fmt) 130662306a36Sopenharmony_ci{ 130762306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai; 130862306a36Sopenharmony_ci struct snd_soc_dai *codec_dai; 130962306a36Sopenharmony_ci unsigned int i; 131062306a36Sopenharmony_ci int ret; 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci if (!dai_fmt) 131362306a36Sopenharmony_ci return 0; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci for_each_rtd_codec_dais(rtd, i, codec_dai) { 131662306a36Sopenharmony_ci ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt); 131762306a36Sopenharmony_ci if (ret != 0 && ret != -ENOTSUPP) 131862306a36Sopenharmony_ci return ret; 131962306a36Sopenharmony_ci } 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci /* Flip the polarity for the "CPU" end of link */ 132262306a36Sopenharmony_ci dai_fmt = snd_soc_daifmt_clock_provider_flipped(dai_fmt); 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci for_each_rtd_cpu_dais(rtd, i, cpu_dai) { 132562306a36Sopenharmony_ci ret = snd_soc_dai_set_fmt(cpu_dai, dai_fmt); 132662306a36Sopenharmony_ci if (ret != 0 && ret != -ENOTSUPP) 132762306a36Sopenharmony_ci return ret; 132862306a36Sopenharmony_ci } 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci return 0; 133162306a36Sopenharmony_ci} 133262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_runtime_set_dai_fmt); 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_cistatic int soc_init_pcm_runtime(struct snd_soc_card *card, 133562306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd) 133662306a36Sopenharmony_ci{ 133762306a36Sopenharmony_ci struct snd_soc_dai_link *dai_link = rtd->dai_link; 133862306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); 133962306a36Sopenharmony_ci struct snd_soc_component *component; 134062306a36Sopenharmony_ci int ret, num, i; 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci /* do machine specific initialization */ 134362306a36Sopenharmony_ci ret = snd_soc_link_init(rtd); 134462306a36Sopenharmony_ci if (ret < 0) 134562306a36Sopenharmony_ci return ret; 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci snd_soc_runtime_get_dai_fmt(rtd); 134862306a36Sopenharmony_ci ret = snd_soc_runtime_set_dai_fmt(rtd, dai_link->dai_fmt); 134962306a36Sopenharmony_ci if (ret) 135062306a36Sopenharmony_ci goto err; 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci /* add DPCM sysfs entries */ 135362306a36Sopenharmony_ci soc_dpcm_debugfs_add(rtd); 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci num = rtd->num; 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci /* 135862306a36Sopenharmony_ci * most drivers will register their PCMs using DAI link ordering but 135962306a36Sopenharmony_ci * topology based drivers can use the DAI link id field to set PCM 136062306a36Sopenharmony_ci * device number and then use rtd + a base offset of the BEs. 136162306a36Sopenharmony_ci */ 136262306a36Sopenharmony_ci for_each_rtd_components(rtd, i, component) { 136362306a36Sopenharmony_ci if (!component->driver->use_dai_pcm_id) 136462306a36Sopenharmony_ci continue; 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci if (rtd->dai_link->no_pcm) 136762306a36Sopenharmony_ci num += component->driver->be_pcm_base; 136862306a36Sopenharmony_ci else 136962306a36Sopenharmony_ci num = rtd->dai_link->id; 137062306a36Sopenharmony_ci } 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci /* create compress_device if possible */ 137362306a36Sopenharmony_ci ret = snd_soc_dai_compress_new(cpu_dai, rtd, num); 137462306a36Sopenharmony_ci if (ret != -ENOTSUPP) 137562306a36Sopenharmony_ci goto err; 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci /* create the pcm */ 137862306a36Sopenharmony_ci ret = soc_new_pcm(rtd, num); 137962306a36Sopenharmony_ci if (ret < 0) { 138062306a36Sopenharmony_ci dev_err(card->dev, "ASoC: can't create pcm %s :%d\n", 138162306a36Sopenharmony_ci dai_link->stream_name, ret); 138262306a36Sopenharmony_ci goto err; 138362306a36Sopenharmony_ci } 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci ret = snd_soc_pcm_dai_new(rtd); 138662306a36Sopenharmony_ci if (ret < 0) 138762306a36Sopenharmony_ci goto err; 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci rtd->initialized = true; 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci return 0; 139262306a36Sopenharmony_cierr: 139362306a36Sopenharmony_ci snd_soc_link_exit(rtd); 139462306a36Sopenharmony_ci return ret; 139562306a36Sopenharmony_ci} 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_cistatic void soc_set_name_prefix(struct snd_soc_card *card, 139862306a36Sopenharmony_ci struct snd_soc_component *component) 139962306a36Sopenharmony_ci{ 140062306a36Sopenharmony_ci struct device_node *of_node = soc_component_to_node(component); 140162306a36Sopenharmony_ci const char *str; 140262306a36Sopenharmony_ci int ret, i; 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci for (i = 0; i < card->num_configs; i++) { 140562306a36Sopenharmony_ci struct snd_soc_codec_conf *map = &card->codec_conf[i]; 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci if (snd_soc_is_matching_component(&map->dlc, component) && 140862306a36Sopenharmony_ci map->name_prefix) { 140962306a36Sopenharmony_ci component->name_prefix = map->name_prefix; 141062306a36Sopenharmony_ci return; 141162306a36Sopenharmony_ci } 141262306a36Sopenharmony_ci } 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci /* 141562306a36Sopenharmony_ci * If there is no configuration table or no match in the table, 141662306a36Sopenharmony_ci * check if a prefix is provided in the node 141762306a36Sopenharmony_ci */ 141862306a36Sopenharmony_ci ret = of_property_read_string(of_node, "sound-name-prefix", &str); 141962306a36Sopenharmony_ci if (ret < 0) 142062306a36Sopenharmony_ci return; 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci component->name_prefix = str; 142362306a36Sopenharmony_ci} 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_cistatic void soc_remove_component(struct snd_soc_component *component, 142662306a36Sopenharmony_ci int probed) 142762306a36Sopenharmony_ci{ 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci if (!component->card) 143062306a36Sopenharmony_ci return; 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci if (probed) 143362306a36Sopenharmony_ci snd_soc_component_remove(component); 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci list_del_init(&component->card_list); 143662306a36Sopenharmony_ci snd_soc_dapm_free(snd_soc_component_get_dapm(component)); 143762306a36Sopenharmony_ci soc_cleanup_component_debugfs(component); 143862306a36Sopenharmony_ci component->card = NULL; 143962306a36Sopenharmony_ci snd_soc_component_module_put_when_remove(component); 144062306a36Sopenharmony_ci} 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_cistatic int soc_probe_component(struct snd_soc_card *card, 144362306a36Sopenharmony_ci struct snd_soc_component *component) 144462306a36Sopenharmony_ci{ 144562306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm = 144662306a36Sopenharmony_ci snd_soc_component_get_dapm(component); 144762306a36Sopenharmony_ci struct snd_soc_dai *dai; 144862306a36Sopenharmony_ci int probed = 0; 144962306a36Sopenharmony_ci int ret; 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci if (snd_soc_component_is_dummy(component)) 145262306a36Sopenharmony_ci return 0; 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci if (component->card) { 145562306a36Sopenharmony_ci if (component->card != card) { 145662306a36Sopenharmony_ci dev_err(component->dev, 145762306a36Sopenharmony_ci "Trying to bind component \"%s\" to card \"%s\" but is already bound to card \"%s\"\n", 145862306a36Sopenharmony_ci component->name, card->name, component->card->name); 145962306a36Sopenharmony_ci return -ENODEV; 146062306a36Sopenharmony_ci } 146162306a36Sopenharmony_ci return 0; 146262306a36Sopenharmony_ci } 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci ret = snd_soc_component_module_get_when_probe(component); 146562306a36Sopenharmony_ci if (ret < 0) 146662306a36Sopenharmony_ci return ret; 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci component->card = card; 146962306a36Sopenharmony_ci soc_set_name_prefix(card, component); 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci soc_init_component_debugfs(component); 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_ci snd_soc_dapm_init(dapm, card, component); 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci ret = snd_soc_dapm_new_controls(dapm, 147662306a36Sopenharmony_ci component->driver->dapm_widgets, 147762306a36Sopenharmony_ci component->driver->num_dapm_widgets); 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci if (ret != 0) { 148062306a36Sopenharmony_ci dev_err(component->dev, 148162306a36Sopenharmony_ci "Failed to create new controls %d\n", ret); 148262306a36Sopenharmony_ci goto err_probe; 148362306a36Sopenharmony_ci } 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci for_each_component_dais(component, dai) { 148662306a36Sopenharmony_ci ret = snd_soc_dapm_new_dai_widgets(dapm, dai); 148762306a36Sopenharmony_ci if (ret != 0) { 148862306a36Sopenharmony_ci dev_err(component->dev, 148962306a36Sopenharmony_ci "Failed to create DAI widgets %d\n", ret); 149062306a36Sopenharmony_ci goto err_probe; 149162306a36Sopenharmony_ci } 149262306a36Sopenharmony_ci } 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci ret = snd_soc_component_probe(component); 149562306a36Sopenharmony_ci if (ret < 0) 149662306a36Sopenharmony_ci goto err_probe; 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci WARN(dapm->idle_bias_off && 149962306a36Sopenharmony_ci dapm->bias_level != SND_SOC_BIAS_OFF, 150062306a36Sopenharmony_ci "codec %s can not start from non-off bias with idle_bias_off==1\n", 150162306a36Sopenharmony_ci component->name); 150262306a36Sopenharmony_ci probed = 1; 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci /* 150562306a36Sopenharmony_ci * machine specific init 150662306a36Sopenharmony_ci * see 150762306a36Sopenharmony_ci * snd_soc_component_set_aux() 150862306a36Sopenharmony_ci */ 150962306a36Sopenharmony_ci ret = snd_soc_component_init(component); 151062306a36Sopenharmony_ci if (ret < 0) 151162306a36Sopenharmony_ci goto err_probe; 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci ret = snd_soc_add_component_controls(component, 151462306a36Sopenharmony_ci component->driver->controls, 151562306a36Sopenharmony_ci component->driver->num_controls); 151662306a36Sopenharmony_ci if (ret < 0) 151762306a36Sopenharmony_ci goto err_probe; 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci ret = snd_soc_dapm_add_routes(dapm, 152062306a36Sopenharmony_ci component->driver->dapm_routes, 152162306a36Sopenharmony_ci component->driver->num_dapm_routes); 152262306a36Sopenharmony_ci if (ret < 0) { 152362306a36Sopenharmony_ci if (card->disable_route_checks) { 152462306a36Sopenharmony_ci dev_info(card->dev, 152562306a36Sopenharmony_ci "%s: disable_route_checks set, ignoring errors on add_routes\n", 152662306a36Sopenharmony_ci __func__); 152762306a36Sopenharmony_ci } else { 152862306a36Sopenharmony_ci dev_err(card->dev, 152962306a36Sopenharmony_ci "%s: snd_soc_dapm_add_routes failed: %d\n", 153062306a36Sopenharmony_ci __func__, ret); 153162306a36Sopenharmony_ci goto err_probe; 153262306a36Sopenharmony_ci } 153362306a36Sopenharmony_ci } 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci /* see for_each_card_components */ 153662306a36Sopenharmony_ci list_add(&component->card_list, &card->component_dev_list); 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_cierr_probe: 153962306a36Sopenharmony_ci if (ret < 0) 154062306a36Sopenharmony_ci soc_remove_component(component, probed); 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci return ret; 154362306a36Sopenharmony_ci} 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_cistatic void soc_remove_link_dais(struct snd_soc_card *card) 154662306a36Sopenharmony_ci{ 154762306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 154862306a36Sopenharmony_ci int order; 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci for_each_comp_order(order) { 155162306a36Sopenharmony_ci for_each_card_rtds(card, rtd) { 155262306a36Sopenharmony_ci /* remove all rtd connected DAIs in good order */ 155362306a36Sopenharmony_ci snd_soc_pcm_dai_remove(rtd, order); 155462306a36Sopenharmony_ci } 155562306a36Sopenharmony_ci } 155662306a36Sopenharmony_ci} 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_cistatic int soc_probe_link_dais(struct snd_soc_card *card) 155962306a36Sopenharmony_ci{ 156062306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 156162306a36Sopenharmony_ci int order, ret; 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci for_each_comp_order(order) { 156462306a36Sopenharmony_ci for_each_card_rtds(card, rtd) { 156562306a36Sopenharmony_ci /* probe all rtd connected DAIs in good order */ 156662306a36Sopenharmony_ci ret = snd_soc_pcm_dai_probe(rtd, order); 156762306a36Sopenharmony_ci if (ret) 156862306a36Sopenharmony_ci return ret; 156962306a36Sopenharmony_ci } 157062306a36Sopenharmony_ci } 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci return 0; 157362306a36Sopenharmony_ci} 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_cistatic void soc_remove_link_components(struct snd_soc_card *card) 157662306a36Sopenharmony_ci{ 157762306a36Sopenharmony_ci struct snd_soc_component *component; 157862306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 157962306a36Sopenharmony_ci int i, order; 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci for_each_comp_order(order) { 158262306a36Sopenharmony_ci for_each_card_rtds(card, rtd) { 158362306a36Sopenharmony_ci for_each_rtd_components(rtd, i, component) { 158462306a36Sopenharmony_ci if (component->driver->remove_order != order) 158562306a36Sopenharmony_ci continue; 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci soc_remove_component(component, 1); 158862306a36Sopenharmony_ci } 158962306a36Sopenharmony_ci } 159062306a36Sopenharmony_ci } 159162306a36Sopenharmony_ci} 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_cistatic int soc_probe_link_components(struct snd_soc_card *card) 159462306a36Sopenharmony_ci{ 159562306a36Sopenharmony_ci struct snd_soc_component *component; 159662306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 159762306a36Sopenharmony_ci int i, ret, order; 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci for_each_comp_order(order) { 160062306a36Sopenharmony_ci for_each_card_rtds(card, rtd) { 160162306a36Sopenharmony_ci for_each_rtd_components(rtd, i, component) { 160262306a36Sopenharmony_ci if (component->driver->probe_order != order) 160362306a36Sopenharmony_ci continue; 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_ci ret = soc_probe_component(card, component); 160662306a36Sopenharmony_ci if (ret < 0) 160762306a36Sopenharmony_ci return ret; 160862306a36Sopenharmony_ci } 160962306a36Sopenharmony_ci } 161062306a36Sopenharmony_ci } 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci return 0; 161362306a36Sopenharmony_ci} 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_cistatic void soc_unbind_aux_dev(struct snd_soc_card *card) 161662306a36Sopenharmony_ci{ 161762306a36Sopenharmony_ci struct snd_soc_component *component, *_component; 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci for_each_card_auxs_safe(card, component, _component) { 162062306a36Sopenharmony_ci /* for snd_soc_component_init() */ 162162306a36Sopenharmony_ci snd_soc_component_set_aux(component, NULL); 162262306a36Sopenharmony_ci list_del(&component->card_aux_list); 162362306a36Sopenharmony_ci } 162462306a36Sopenharmony_ci} 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_cistatic int soc_bind_aux_dev(struct snd_soc_card *card) 162762306a36Sopenharmony_ci{ 162862306a36Sopenharmony_ci struct snd_soc_component *component; 162962306a36Sopenharmony_ci struct snd_soc_aux_dev *aux; 163062306a36Sopenharmony_ci int i; 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci for_each_card_pre_auxs(card, i, aux) { 163362306a36Sopenharmony_ci /* codecs, usually analog devices */ 163462306a36Sopenharmony_ci component = soc_find_component(&aux->dlc); 163562306a36Sopenharmony_ci if (!component) 163662306a36Sopenharmony_ci return -EPROBE_DEFER; 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci /* for snd_soc_component_init() */ 163962306a36Sopenharmony_ci snd_soc_component_set_aux(component, aux); 164062306a36Sopenharmony_ci /* see for_each_card_auxs */ 164162306a36Sopenharmony_ci list_add(&component->card_aux_list, &card->aux_comp_list); 164262306a36Sopenharmony_ci } 164362306a36Sopenharmony_ci return 0; 164462306a36Sopenharmony_ci} 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_cistatic int soc_probe_aux_devices(struct snd_soc_card *card) 164762306a36Sopenharmony_ci{ 164862306a36Sopenharmony_ci struct snd_soc_component *component; 164962306a36Sopenharmony_ci int order; 165062306a36Sopenharmony_ci int ret; 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_ci for_each_comp_order(order) { 165362306a36Sopenharmony_ci for_each_card_auxs(card, component) { 165462306a36Sopenharmony_ci if (component->driver->probe_order != order) 165562306a36Sopenharmony_ci continue; 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci ret = soc_probe_component(card, component); 165862306a36Sopenharmony_ci if (ret < 0) 165962306a36Sopenharmony_ci return ret; 166062306a36Sopenharmony_ci } 166162306a36Sopenharmony_ci } 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci return 0; 166462306a36Sopenharmony_ci} 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_cistatic void soc_remove_aux_devices(struct snd_soc_card *card) 166762306a36Sopenharmony_ci{ 166862306a36Sopenharmony_ci struct snd_soc_component *comp, *_comp; 166962306a36Sopenharmony_ci int order; 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_ci for_each_comp_order(order) { 167262306a36Sopenharmony_ci for_each_card_auxs_safe(card, comp, _comp) { 167362306a36Sopenharmony_ci if (comp->driver->remove_order == order) 167462306a36Sopenharmony_ci soc_remove_component(comp, 1); 167562306a36Sopenharmony_ci } 167662306a36Sopenharmony_ci } 167762306a36Sopenharmony_ci} 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ci#ifdef CONFIG_DMI 168062306a36Sopenharmony_ci/* 168162306a36Sopenharmony_ci * If a DMI filed contain strings in this blacklist (e.g. 168262306a36Sopenharmony_ci * "Type2 - Board Manufacturer" or "Type1 - TBD by OEM"), it will be taken 168362306a36Sopenharmony_ci * as invalid and dropped when setting the card long name from DMI info. 168462306a36Sopenharmony_ci */ 168562306a36Sopenharmony_cistatic const char * const dmi_blacklist[] = { 168662306a36Sopenharmony_ci "To be filled by OEM", 168762306a36Sopenharmony_ci "TBD by OEM", 168862306a36Sopenharmony_ci "Default String", 168962306a36Sopenharmony_ci "Board Manufacturer", 169062306a36Sopenharmony_ci "Board Vendor Name", 169162306a36Sopenharmony_ci "Board Product Name", 169262306a36Sopenharmony_ci NULL, /* terminator */ 169362306a36Sopenharmony_ci}; 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci/* 169662306a36Sopenharmony_ci * Trim special characters, and replace '-' with '_' since '-' is used to 169762306a36Sopenharmony_ci * separate different DMI fields in the card long name. Only number and 169862306a36Sopenharmony_ci * alphabet characters and a few separator characters are kept. 169962306a36Sopenharmony_ci */ 170062306a36Sopenharmony_cistatic void cleanup_dmi_name(char *name) 170162306a36Sopenharmony_ci{ 170262306a36Sopenharmony_ci int i, j = 0; 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci for (i = 0; name[i]; i++) { 170562306a36Sopenharmony_ci if (isalnum(name[i]) || (name[i] == '.') 170662306a36Sopenharmony_ci || (name[i] == '_')) 170762306a36Sopenharmony_ci name[j++] = name[i]; 170862306a36Sopenharmony_ci else if (name[i] == '-') 170962306a36Sopenharmony_ci name[j++] = '_'; 171062306a36Sopenharmony_ci } 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci name[j] = '\0'; 171362306a36Sopenharmony_ci} 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci/* 171662306a36Sopenharmony_ci * Check if a DMI field is valid, i.e. not containing any string 171762306a36Sopenharmony_ci * in the black list. 171862306a36Sopenharmony_ci */ 171962306a36Sopenharmony_cistatic int is_dmi_valid(const char *field) 172062306a36Sopenharmony_ci{ 172162306a36Sopenharmony_ci int i = 0; 172262306a36Sopenharmony_ci 172362306a36Sopenharmony_ci while (dmi_blacklist[i]) { 172462306a36Sopenharmony_ci if (strstr(field, dmi_blacklist[i])) 172562306a36Sopenharmony_ci return 0; 172662306a36Sopenharmony_ci i++; 172762306a36Sopenharmony_ci } 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ci return 1; 173062306a36Sopenharmony_ci} 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ci/* 173362306a36Sopenharmony_ci * Append a string to card->dmi_longname with character cleanups. 173462306a36Sopenharmony_ci */ 173562306a36Sopenharmony_cistatic void append_dmi_string(struct snd_soc_card *card, const char *str) 173662306a36Sopenharmony_ci{ 173762306a36Sopenharmony_ci char *dst = card->dmi_longname; 173862306a36Sopenharmony_ci size_t dst_len = sizeof(card->dmi_longname); 173962306a36Sopenharmony_ci size_t len; 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci len = strlen(dst); 174262306a36Sopenharmony_ci snprintf(dst + len, dst_len - len, "-%s", str); 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci len++; /* skip the separator "-" */ 174562306a36Sopenharmony_ci if (len < dst_len) 174662306a36Sopenharmony_ci cleanup_dmi_name(dst + len); 174762306a36Sopenharmony_ci} 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_ci/** 175062306a36Sopenharmony_ci * snd_soc_set_dmi_name() - Register DMI names to card 175162306a36Sopenharmony_ci * @card: The card to register DMI names 175262306a36Sopenharmony_ci * @flavour: The flavour "differentiator" for the card amongst its peers. 175362306a36Sopenharmony_ci * 175462306a36Sopenharmony_ci * An Intel machine driver may be used by many different devices but are 175562306a36Sopenharmony_ci * difficult for userspace to differentiate, since machine drivers ususally 175662306a36Sopenharmony_ci * use their own name as the card short name and leave the card long name 175762306a36Sopenharmony_ci * blank. To differentiate such devices and fix bugs due to lack of 175862306a36Sopenharmony_ci * device-specific configurations, this function allows DMI info to be used 175962306a36Sopenharmony_ci * as the sound card long name, in the format of 176062306a36Sopenharmony_ci * "vendor-product-version-board" 176162306a36Sopenharmony_ci * (Character '-' is used to separate different DMI fields here). 176262306a36Sopenharmony_ci * This will help the user space to load the device-specific Use Case Manager 176362306a36Sopenharmony_ci * (UCM) configurations for the card. 176462306a36Sopenharmony_ci * 176562306a36Sopenharmony_ci * Possible card long names may be: 176662306a36Sopenharmony_ci * DellInc.-XPS139343-01-0310JH 176762306a36Sopenharmony_ci * ASUSTeKCOMPUTERINC.-T100TA-1.0-T100TA 176862306a36Sopenharmony_ci * Circuitco-MinnowboardMaxD0PLATFORM-D0-MinnowBoardMAX 176962306a36Sopenharmony_ci * 177062306a36Sopenharmony_ci * This function also supports flavoring the card longname to provide 177162306a36Sopenharmony_ci * the extra differentiation, like "vendor-product-version-board-flavor". 177262306a36Sopenharmony_ci * 177362306a36Sopenharmony_ci * We only keep number and alphabet characters and a few separator characters 177462306a36Sopenharmony_ci * in the card long name since UCM in the user space uses the card long names 177562306a36Sopenharmony_ci * as card configuration directory names and AudoConf cannot support special 177662306a36Sopenharmony_ci * charactors like SPACE. 177762306a36Sopenharmony_ci * 177862306a36Sopenharmony_ci * Returns 0 on success, otherwise a negative error code. 177962306a36Sopenharmony_ci */ 178062306a36Sopenharmony_ciint snd_soc_set_dmi_name(struct snd_soc_card *card, const char *flavour) 178162306a36Sopenharmony_ci{ 178262306a36Sopenharmony_ci const char *vendor, *product, *board; 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ci if (card->long_name) 178562306a36Sopenharmony_ci return 0; /* long name already set by driver or from DMI */ 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_ci if (!dmi_available) 178862306a36Sopenharmony_ci return 0; 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci /* make up dmi long name as: vendor-product-version-board */ 179162306a36Sopenharmony_ci vendor = dmi_get_system_info(DMI_BOARD_VENDOR); 179262306a36Sopenharmony_ci if (!vendor || !is_dmi_valid(vendor)) { 179362306a36Sopenharmony_ci dev_warn(card->dev, "ASoC: no DMI vendor name!\n"); 179462306a36Sopenharmony_ci return 0; 179562306a36Sopenharmony_ci } 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_ci snprintf(card->dmi_longname, sizeof(card->dmi_longname), "%s", vendor); 179862306a36Sopenharmony_ci cleanup_dmi_name(card->dmi_longname); 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci product = dmi_get_system_info(DMI_PRODUCT_NAME); 180162306a36Sopenharmony_ci if (product && is_dmi_valid(product)) { 180262306a36Sopenharmony_ci const char *product_version = dmi_get_system_info(DMI_PRODUCT_VERSION); 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_ci append_dmi_string(card, product); 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_ci /* 180762306a36Sopenharmony_ci * some vendors like Lenovo may only put a self-explanatory 180862306a36Sopenharmony_ci * name in the product version field 180962306a36Sopenharmony_ci */ 181062306a36Sopenharmony_ci if (product_version && is_dmi_valid(product_version)) 181162306a36Sopenharmony_ci append_dmi_string(card, product_version); 181262306a36Sopenharmony_ci } 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_ci board = dmi_get_system_info(DMI_BOARD_NAME); 181562306a36Sopenharmony_ci if (board && is_dmi_valid(board)) { 181662306a36Sopenharmony_ci if (!product || strcasecmp(board, product)) 181762306a36Sopenharmony_ci append_dmi_string(card, board); 181862306a36Sopenharmony_ci } else if (!product) { 181962306a36Sopenharmony_ci /* fall back to using legacy name */ 182062306a36Sopenharmony_ci dev_warn(card->dev, "ASoC: no DMI board/product name!\n"); 182162306a36Sopenharmony_ci return 0; 182262306a36Sopenharmony_ci } 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_ci /* Add flavour to dmi long name */ 182562306a36Sopenharmony_ci if (flavour) 182662306a36Sopenharmony_ci append_dmi_string(card, flavour); 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_ci /* set the card long name */ 182962306a36Sopenharmony_ci card->long_name = card->dmi_longname; 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_ci return 0; 183262306a36Sopenharmony_ci} 183362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_set_dmi_name); 183462306a36Sopenharmony_ci#endif /* CONFIG_DMI */ 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_cistatic void soc_check_tplg_fes(struct snd_soc_card *card) 183762306a36Sopenharmony_ci{ 183862306a36Sopenharmony_ci struct snd_soc_component *component; 183962306a36Sopenharmony_ci const struct snd_soc_component_driver *comp_drv; 184062306a36Sopenharmony_ci struct snd_soc_dai_link *dai_link; 184162306a36Sopenharmony_ci int i; 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_ci for_each_component(component) { 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci /* does this component override BEs ? */ 184662306a36Sopenharmony_ci if (!component->driver->ignore_machine) 184762306a36Sopenharmony_ci continue; 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ci /* for this machine ? */ 185062306a36Sopenharmony_ci if (!strcmp(component->driver->ignore_machine, 185162306a36Sopenharmony_ci card->dev->driver->name)) 185262306a36Sopenharmony_ci goto match; 185362306a36Sopenharmony_ci if (strcmp(component->driver->ignore_machine, 185462306a36Sopenharmony_ci dev_name(card->dev))) 185562306a36Sopenharmony_ci continue; 185662306a36Sopenharmony_cimatch: 185762306a36Sopenharmony_ci /* machine matches, so override the rtd data */ 185862306a36Sopenharmony_ci for_each_card_prelinks(card, i, dai_link) { 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci /* ignore this FE */ 186162306a36Sopenharmony_ci if (dai_link->dynamic) { 186262306a36Sopenharmony_ci dai_link->ignore = true; 186362306a36Sopenharmony_ci continue; 186462306a36Sopenharmony_ci } 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci dev_dbg(card->dev, "info: override BE DAI link %s\n", 186762306a36Sopenharmony_ci card->dai_link[i].name); 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_ci /* override platform component */ 187062306a36Sopenharmony_ci if (!dai_link->platforms) { 187162306a36Sopenharmony_ci dev_err(card->dev, "init platform error"); 187262306a36Sopenharmony_ci continue; 187362306a36Sopenharmony_ci } 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ci if (component->dev->of_node) 187662306a36Sopenharmony_ci dai_link->platforms->of_node = component->dev->of_node; 187762306a36Sopenharmony_ci else 187862306a36Sopenharmony_ci dai_link->platforms->name = component->name; 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_ci /* convert non BE into BE */ 188162306a36Sopenharmony_ci if (!dai_link->no_pcm) { 188262306a36Sopenharmony_ci dai_link->no_pcm = 1; 188362306a36Sopenharmony_ci 188462306a36Sopenharmony_ci if (dai_link->dpcm_playback) 188562306a36Sopenharmony_ci dev_warn(card->dev, 188662306a36Sopenharmony_ci "invalid configuration, dailink %s has flags no_pcm=0 and dpcm_playback=1\n", 188762306a36Sopenharmony_ci dai_link->name); 188862306a36Sopenharmony_ci if (dai_link->dpcm_capture) 188962306a36Sopenharmony_ci dev_warn(card->dev, 189062306a36Sopenharmony_ci "invalid configuration, dailink %s has flags no_pcm=0 and dpcm_capture=1\n", 189162306a36Sopenharmony_ci dai_link->name); 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci /* convert normal link into DPCM one */ 189462306a36Sopenharmony_ci if (!(dai_link->dpcm_playback || 189562306a36Sopenharmony_ci dai_link->dpcm_capture)) { 189662306a36Sopenharmony_ci dai_link->dpcm_playback = !dai_link->capture_only; 189762306a36Sopenharmony_ci dai_link->dpcm_capture = !dai_link->playback_only; 189862306a36Sopenharmony_ci } 189962306a36Sopenharmony_ci } 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_ci /* 190262306a36Sopenharmony_ci * override any BE fixups 190362306a36Sopenharmony_ci * see 190462306a36Sopenharmony_ci * snd_soc_link_be_hw_params_fixup() 190562306a36Sopenharmony_ci */ 190662306a36Sopenharmony_ci dai_link->be_hw_params_fixup = 190762306a36Sopenharmony_ci component->driver->be_hw_params_fixup; 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_ci /* 191062306a36Sopenharmony_ci * most BE links don't set stream name, so set it to 191162306a36Sopenharmony_ci * dai link name if it's NULL to help bind widgets. 191262306a36Sopenharmony_ci */ 191362306a36Sopenharmony_ci if (!dai_link->stream_name) 191462306a36Sopenharmony_ci dai_link->stream_name = dai_link->name; 191562306a36Sopenharmony_ci } 191662306a36Sopenharmony_ci 191762306a36Sopenharmony_ci /* Inform userspace we are using alternate topology */ 191862306a36Sopenharmony_ci if (component->driver->topology_name_prefix) { 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci /* topology shortname created? */ 192162306a36Sopenharmony_ci if (!card->topology_shortname_created) { 192262306a36Sopenharmony_ci comp_drv = component->driver; 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_ci snprintf(card->topology_shortname, 32, "%s-%s", 192562306a36Sopenharmony_ci comp_drv->topology_name_prefix, 192662306a36Sopenharmony_ci card->name); 192762306a36Sopenharmony_ci card->topology_shortname_created = true; 192862306a36Sopenharmony_ci } 192962306a36Sopenharmony_ci 193062306a36Sopenharmony_ci /* use topology shortname */ 193162306a36Sopenharmony_ci card->name = card->topology_shortname; 193262306a36Sopenharmony_ci } 193362306a36Sopenharmony_ci } 193462306a36Sopenharmony_ci} 193562306a36Sopenharmony_ci 193662306a36Sopenharmony_ci#define soc_setup_card_name(card, name, name1, name2) \ 193762306a36Sopenharmony_ci __soc_setup_card_name(card, name, sizeof(name), name1, name2) 193862306a36Sopenharmony_cistatic void __soc_setup_card_name(struct snd_soc_card *card, 193962306a36Sopenharmony_ci char *name, int len, 194062306a36Sopenharmony_ci const char *name1, const char *name2) 194162306a36Sopenharmony_ci{ 194262306a36Sopenharmony_ci const char *src = name1 ? name1 : name2; 194362306a36Sopenharmony_ci int i; 194462306a36Sopenharmony_ci 194562306a36Sopenharmony_ci snprintf(name, len, "%s", src); 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_ci if (name != card->snd_card->driver) 194862306a36Sopenharmony_ci return; 194962306a36Sopenharmony_ci 195062306a36Sopenharmony_ci /* 195162306a36Sopenharmony_ci * Name normalization (driver field) 195262306a36Sopenharmony_ci * 195362306a36Sopenharmony_ci * The driver name is somewhat special, as it's used as a key for 195462306a36Sopenharmony_ci * searches in the user-space. 195562306a36Sopenharmony_ci * 195662306a36Sopenharmony_ci * ex) 195762306a36Sopenharmony_ci * "abcd??efg" -> "abcd__efg" 195862306a36Sopenharmony_ci */ 195962306a36Sopenharmony_ci for (i = 0; i < len; i++) { 196062306a36Sopenharmony_ci switch (name[i]) { 196162306a36Sopenharmony_ci case '_': 196262306a36Sopenharmony_ci case '-': 196362306a36Sopenharmony_ci case '\0': 196462306a36Sopenharmony_ci break; 196562306a36Sopenharmony_ci default: 196662306a36Sopenharmony_ci if (!isalnum(name[i])) 196762306a36Sopenharmony_ci name[i] = '_'; 196862306a36Sopenharmony_ci break; 196962306a36Sopenharmony_ci } 197062306a36Sopenharmony_ci } 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci /* 197362306a36Sopenharmony_ci * The driver field should contain a valid string from the user view. 197462306a36Sopenharmony_ci * The wrapping usually does not work so well here. Set a smaller string 197562306a36Sopenharmony_ci * in the specific ASoC driver. 197662306a36Sopenharmony_ci */ 197762306a36Sopenharmony_ci if (strlen(src) > len - 1) 197862306a36Sopenharmony_ci dev_err(card->dev, "ASoC: driver name too long '%s' -> '%s'\n", src, name); 197962306a36Sopenharmony_ci} 198062306a36Sopenharmony_ci 198162306a36Sopenharmony_cistatic void soc_cleanup_card_resources(struct snd_soc_card *card) 198262306a36Sopenharmony_ci{ 198362306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd, *n; 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_ci if (card->snd_card) 198662306a36Sopenharmony_ci snd_card_disconnect_sync(card->snd_card); 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_ci snd_soc_dapm_shutdown(card); 198962306a36Sopenharmony_ci 199062306a36Sopenharmony_ci /* release machine specific resources */ 199162306a36Sopenharmony_ci for_each_card_rtds(card, rtd) 199262306a36Sopenharmony_ci if (rtd->initialized) 199362306a36Sopenharmony_ci snd_soc_link_exit(rtd); 199462306a36Sopenharmony_ci /* remove and free each DAI */ 199562306a36Sopenharmony_ci soc_remove_link_dais(card); 199662306a36Sopenharmony_ci soc_remove_link_components(card); 199762306a36Sopenharmony_ci 199862306a36Sopenharmony_ci for_each_card_rtds_safe(card, rtd, n) 199962306a36Sopenharmony_ci snd_soc_remove_pcm_runtime(card, rtd); 200062306a36Sopenharmony_ci 200162306a36Sopenharmony_ci /* remove auxiliary devices */ 200262306a36Sopenharmony_ci soc_remove_aux_devices(card); 200362306a36Sopenharmony_ci soc_unbind_aux_dev(card); 200462306a36Sopenharmony_ci 200562306a36Sopenharmony_ci snd_soc_dapm_free(&card->dapm); 200662306a36Sopenharmony_ci soc_cleanup_card_debugfs(card); 200762306a36Sopenharmony_ci 200862306a36Sopenharmony_ci /* remove the card */ 200962306a36Sopenharmony_ci snd_soc_card_remove(card); 201062306a36Sopenharmony_ci 201162306a36Sopenharmony_ci if (card->snd_card) { 201262306a36Sopenharmony_ci snd_card_free(card->snd_card); 201362306a36Sopenharmony_ci card->snd_card = NULL; 201462306a36Sopenharmony_ci } 201562306a36Sopenharmony_ci} 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_cistatic void snd_soc_unbind_card(struct snd_soc_card *card, bool unregister) 201862306a36Sopenharmony_ci{ 201962306a36Sopenharmony_ci if (snd_soc_card_is_instantiated(card)) { 202062306a36Sopenharmony_ci card->instantiated = false; 202162306a36Sopenharmony_ci snd_soc_flush_all_delayed_work(card); 202262306a36Sopenharmony_ci 202362306a36Sopenharmony_ci soc_cleanup_card_resources(card); 202462306a36Sopenharmony_ci if (!unregister) 202562306a36Sopenharmony_ci list_add(&card->list, &unbind_card_list); 202662306a36Sopenharmony_ci } else { 202762306a36Sopenharmony_ci if (unregister) 202862306a36Sopenharmony_ci list_del(&card->list); 202962306a36Sopenharmony_ci } 203062306a36Sopenharmony_ci} 203162306a36Sopenharmony_ci 203262306a36Sopenharmony_cistatic int snd_soc_bind_card(struct snd_soc_card *card) 203362306a36Sopenharmony_ci{ 203462306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 203562306a36Sopenharmony_ci struct snd_soc_component *component; 203662306a36Sopenharmony_ci int ret; 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_ci mutex_lock(&client_mutex); 203962306a36Sopenharmony_ci snd_soc_card_mutex_lock_root(card); 204062306a36Sopenharmony_ci 204162306a36Sopenharmony_ci snd_soc_dapm_init(&card->dapm, card, NULL); 204262306a36Sopenharmony_ci 204362306a36Sopenharmony_ci /* check whether any platform is ignore machine FE and using topology */ 204462306a36Sopenharmony_ci soc_check_tplg_fes(card); 204562306a36Sopenharmony_ci 204662306a36Sopenharmony_ci /* bind aux_devs too */ 204762306a36Sopenharmony_ci ret = soc_bind_aux_dev(card); 204862306a36Sopenharmony_ci if (ret < 0) 204962306a36Sopenharmony_ci goto probe_end; 205062306a36Sopenharmony_ci 205162306a36Sopenharmony_ci /* add predefined DAI links to the list */ 205262306a36Sopenharmony_ci card->num_rtd = 0; 205362306a36Sopenharmony_ci ret = snd_soc_add_pcm_runtimes(card, card->dai_link, card->num_links); 205462306a36Sopenharmony_ci if (ret < 0) 205562306a36Sopenharmony_ci goto probe_end; 205662306a36Sopenharmony_ci 205762306a36Sopenharmony_ci /* card bind complete so register a sound card */ 205862306a36Sopenharmony_ci ret = snd_card_new(card->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, 205962306a36Sopenharmony_ci card->owner, 0, &card->snd_card); 206062306a36Sopenharmony_ci if (ret < 0) { 206162306a36Sopenharmony_ci dev_err(card->dev, 206262306a36Sopenharmony_ci "ASoC: can't create sound card for card %s: %d\n", 206362306a36Sopenharmony_ci card->name, ret); 206462306a36Sopenharmony_ci goto probe_end; 206562306a36Sopenharmony_ci } 206662306a36Sopenharmony_ci 206762306a36Sopenharmony_ci soc_init_card_debugfs(card); 206862306a36Sopenharmony_ci 206962306a36Sopenharmony_ci soc_resume_init(card); 207062306a36Sopenharmony_ci 207162306a36Sopenharmony_ci ret = snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets, 207262306a36Sopenharmony_ci card->num_dapm_widgets); 207362306a36Sopenharmony_ci if (ret < 0) 207462306a36Sopenharmony_ci goto probe_end; 207562306a36Sopenharmony_ci 207662306a36Sopenharmony_ci ret = snd_soc_dapm_new_controls(&card->dapm, card->of_dapm_widgets, 207762306a36Sopenharmony_ci card->num_of_dapm_widgets); 207862306a36Sopenharmony_ci if (ret < 0) 207962306a36Sopenharmony_ci goto probe_end; 208062306a36Sopenharmony_ci 208162306a36Sopenharmony_ci /* initialise the sound card only once */ 208262306a36Sopenharmony_ci ret = snd_soc_card_probe(card); 208362306a36Sopenharmony_ci if (ret < 0) 208462306a36Sopenharmony_ci goto probe_end; 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_ci /* probe all components used by DAI links on this card */ 208762306a36Sopenharmony_ci ret = soc_probe_link_components(card); 208862306a36Sopenharmony_ci if (ret < 0) { 208962306a36Sopenharmony_ci if (ret != -EPROBE_DEFER) { 209062306a36Sopenharmony_ci dev_err(card->dev, 209162306a36Sopenharmony_ci "ASoC: failed to instantiate card %d\n", ret); 209262306a36Sopenharmony_ci } 209362306a36Sopenharmony_ci goto probe_end; 209462306a36Sopenharmony_ci } 209562306a36Sopenharmony_ci 209662306a36Sopenharmony_ci /* probe auxiliary components */ 209762306a36Sopenharmony_ci ret = soc_probe_aux_devices(card); 209862306a36Sopenharmony_ci if (ret < 0) { 209962306a36Sopenharmony_ci dev_err(card->dev, 210062306a36Sopenharmony_ci "ASoC: failed to probe aux component %d\n", ret); 210162306a36Sopenharmony_ci goto probe_end; 210262306a36Sopenharmony_ci } 210362306a36Sopenharmony_ci 210462306a36Sopenharmony_ci /* probe all DAI links on this card */ 210562306a36Sopenharmony_ci ret = soc_probe_link_dais(card); 210662306a36Sopenharmony_ci if (ret < 0) { 210762306a36Sopenharmony_ci dev_err(card->dev, 210862306a36Sopenharmony_ci "ASoC: failed to instantiate card %d\n", ret); 210962306a36Sopenharmony_ci goto probe_end; 211062306a36Sopenharmony_ci } 211162306a36Sopenharmony_ci 211262306a36Sopenharmony_ci for_each_card_rtds(card, rtd) { 211362306a36Sopenharmony_ci ret = soc_init_pcm_runtime(card, rtd); 211462306a36Sopenharmony_ci if (ret < 0) 211562306a36Sopenharmony_ci goto probe_end; 211662306a36Sopenharmony_ci } 211762306a36Sopenharmony_ci 211862306a36Sopenharmony_ci snd_soc_dapm_link_dai_widgets(card); 211962306a36Sopenharmony_ci snd_soc_dapm_connect_dai_link_widgets(card); 212062306a36Sopenharmony_ci 212162306a36Sopenharmony_ci ret = snd_soc_add_card_controls(card, card->controls, 212262306a36Sopenharmony_ci card->num_controls); 212362306a36Sopenharmony_ci if (ret < 0) 212462306a36Sopenharmony_ci goto probe_end; 212562306a36Sopenharmony_ci 212662306a36Sopenharmony_ci ret = snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes, 212762306a36Sopenharmony_ci card->num_dapm_routes); 212862306a36Sopenharmony_ci if (ret < 0) { 212962306a36Sopenharmony_ci if (card->disable_route_checks) { 213062306a36Sopenharmony_ci dev_info(card->dev, 213162306a36Sopenharmony_ci "%s: disable_route_checks set, ignoring errors on add_routes\n", 213262306a36Sopenharmony_ci __func__); 213362306a36Sopenharmony_ci } else { 213462306a36Sopenharmony_ci dev_err(card->dev, 213562306a36Sopenharmony_ci "%s: snd_soc_dapm_add_routes failed: %d\n", 213662306a36Sopenharmony_ci __func__, ret); 213762306a36Sopenharmony_ci goto probe_end; 213862306a36Sopenharmony_ci } 213962306a36Sopenharmony_ci } 214062306a36Sopenharmony_ci 214162306a36Sopenharmony_ci ret = snd_soc_dapm_add_routes(&card->dapm, card->of_dapm_routes, 214262306a36Sopenharmony_ci card->num_of_dapm_routes); 214362306a36Sopenharmony_ci if (ret < 0) 214462306a36Sopenharmony_ci goto probe_end; 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci /* try to set some sane longname if DMI is available */ 214762306a36Sopenharmony_ci snd_soc_set_dmi_name(card, NULL); 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci soc_setup_card_name(card, card->snd_card->shortname, 215062306a36Sopenharmony_ci card->name, NULL); 215162306a36Sopenharmony_ci soc_setup_card_name(card, card->snd_card->longname, 215262306a36Sopenharmony_ci card->long_name, card->name); 215362306a36Sopenharmony_ci soc_setup_card_name(card, card->snd_card->driver, 215462306a36Sopenharmony_ci card->driver_name, card->name); 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_ci if (card->components) { 215762306a36Sopenharmony_ci /* the current implementation of snd_component_add() accepts */ 215862306a36Sopenharmony_ci /* multiple components in the string separated by space, */ 215962306a36Sopenharmony_ci /* but the string collision (identical string) check might */ 216062306a36Sopenharmony_ci /* not work correctly */ 216162306a36Sopenharmony_ci ret = snd_component_add(card->snd_card, card->components); 216262306a36Sopenharmony_ci if (ret < 0) { 216362306a36Sopenharmony_ci dev_err(card->dev, "ASoC: %s snd_component_add() failed: %d\n", 216462306a36Sopenharmony_ci card->name, ret); 216562306a36Sopenharmony_ci goto probe_end; 216662306a36Sopenharmony_ci } 216762306a36Sopenharmony_ci } 216862306a36Sopenharmony_ci 216962306a36Sopenharmony_ci ret = snd_soc_card_late_probe(card); 217062306a36Sopenharmony_ci if (ret < 0) 217162306a36Sopenharmony_ci goto probe_end; 217262306a36Sopenharmony_ci 217362306a36Sopenharmony_ci snd_soc_dapm_new_widgets(card); 217462306a36Sopenharmony_ci snd_soc_card_fixup_controls(card); 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_ci ret = snd_card_register(card->snd_card); 217762306a36Sopenharmony_ci if (ret < 0) { 217862306a36Sopenharmony_ci dev_err(card->dev, "ASoC: failed to register soundcard %d\n", 217962306a36Sopenharmony_ci ret); 218062306a36Sopenharmony_ci goto probe_end; 218162306a36Sopenharmony_ci } 218262306a36Sopenharmony_ci 218362306a36Sopenharmony_ci card->instantiated = 1; 218462306a36Sopenharmony_ci dapm_mark_endpoints_dirty(card); 218562306a36Sopenharmony_ci snd_soc_dapm_sync(&card->dapm); 218662306a36Sopenharmony_ci 218762306a36Sopenharmony_ci /* deactivate pins to sleep state */ 218862306a36Sopenharmony_ci for_each_card_components(card, component) 218962306a36Sopenharmony_ci if (!snd_soc_component_active(component)) 219062306a36Sopenharmony_ci pinctrl_pm_select_sleep_state(component->dev); 219162306a36Sopenharmony_ci 219262306a36Sopenharmony_ciprobe_end: 219362306a36Sopenharmony_ci if (ret < 0) 219462306a36Sopenharmony_ci soc_cleanup_card_resources(card); 219562306a36Sopenharmony_ci 219662306a36Sopenharmony_ci snd_soc_card_mutex_unlock(card); 219762306a36Sopenharmony_ci mutex_unlock(&client_mutex); 219862306a36Sopenharmony_ci 219962306a36Sopenharmony_ci return ret; 220062306a36Sopenharmony_ci} 220162306a36Sopenharmony_ci 220262306a36Sopenharmony_ci/* probes a new socdev */ 220362306a36Sopenharmony_cistatic int soc_probe(struct platform_device *pdev) 220462306a36Sopenharmony_ci{ 220562306a36Sopenharmony_ci struct snd_soc_card *card = platform_get_drvdata(pdev); 220662306a36Sopenharmony_ci 220762306a36Sopenharmony_ci /* 220862306a36Sopenharmony_ci * no card, so machine driver should be registering card 220962306a36Sopenharmony_ci * we should not be here in that case so ret error 221062306a36Sopenharmony_ci */ 221162306a36Sopenharmony_ci if (!card) 221262306a36Sopenharmony_ci return -EINVAL; 221362306a36Sopenharmony_ci 221462306a36Sopenharmony_ci dev_warn(&pdev->dev, 221562306a36Sopenharmony_ci "ASoC: machine %s should use snd_soc_register_card()\n", 221662306a36Sopenharmony_ci card->name); 221762306a36Sopenharmony_ci 221862306a36Sopenharmony_ci /* Bodge while we unpick instantiation */ 221962306a36Sopenharmony_ci card->dev = &pdev->dev; 222062306a36Sopenharmony_ci 222162306a36Sopenharmony_ci return devm_snd_soc_register_card(&pdev->dev, card); 222262306a36Sopenharmony_ci} 222362306a36Sopenharmony_ci 222462306a36Sopenharmony_ciint snd_soc_poweroff(struct device *dev) 222562306a36Sopenharmony_ci{ 222662306a36Sopenharmony_ci struct snd_soc_card *card = dev_get_drvdata(dev); 222762306a36Sopenharmony_ci struct snd_soc_component *component; 222862306a36Sopenharmony_ci 222962306a36Sopenharmony_ci if (!snd_soc_card_is_instantiated(card)) 223062306a36Sopenharmony_ci return 0; 223162306a36Sopenharmony_ci 223262306a36Sopenharmony_ci /* 223362306a36Sopenharmony_ci * Flush out pmdown_time work - we actually do want to run it 223462306a36Sopenharmony_ci * now, we're shutting down so no imminent restart. 223562306a36Sopenharmony_ci */ 223662306a36Sopenharmony_ci snd_soc_flush_all_delayed_work(card); 223762306a36Sopenharmony_ci 223862306a36Sopenharmony_ci snd_soc_dapm_shutdown(card); 223962306a36Sopenharmony_ci 224062306a36Sopenharmony_ci /* deactivate pins to sleep state */ 224162306a36Sopenharmony_ci for_each_card_components(card, component) 224262306a36Sopenharmony_ci pinctrl_pm_select_sleep_state(component->dev); 224362306a36Sopenharmony_ci 224462306a36Sopenharmony_ci return 0; 224562306a36Sopenharmony_ci} 224662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_poweroff); 224762306a36Sopenharmony_ci 224862306a36Sopenharmony_ciconst struct dev_pm_ops snd_soc_pm_ops = { 224962306a36Sopenharmony_ci .suspend = snd_soc_suspend, 225062306a36Sopenharmony_ci .resume = snd_soc_resume, 225162306a36Sopenharmony_ci .freeze = snd_soc_suspend, 225262306a36Sopenharmony_ci .thaw = snd_soc_resume, 225362306a36Sopenharmony_ci .poweroff = snd_soc_poweroff, 225462306a36Sopenharmony_ci .restore = snd_soc_resume, 225562306a36Sopenharmony_ci}; 225662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_pm_ops); 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_ci/* ASoC platform driver */ 225962306a36Sopenharmony_cistatic struct platform_driver soc_driver = { 226062306a36Sopenharmony_ci .driver = { 226162306a36Sopenharmony_ci .name = "soc-audio", 226262306a36Sopenharmony_ci .pm = &snd_soc_pm_ops, 226362306a36Sopenharmony_ci }, 226462306a36Sopenharmony_ci .probe = soc_probe, 226562306a36Sopenharmony_ci}; 226662306a36Sopenharmony_ci 226762306a36Sopenharmony_ci/** 226862306a36Sopenharmony_ci * snd_soc_cnew - create new control 226962306a36Sopenharmony_ci * @_template: control template 227062306a36Sopenharmony_ci * @data: control private data 227162306a36Sopenharmony_ci * @long_name: control long name 227262306a36Sopenharmony_ci * @prefix: control name prefix 227362306a36Sopenharmony_ci * 227462306a36Sopenharmony_ci * Create a new mixer control from a template control. 227562306a36Sopenharmony_ci * 227662306a36Sopenharmony_ci * Returns 0 for success, else error. 227762306a36Sopenharmony_ci */ 227862306a36Sopenharmony_cistruct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template, 227962306a36Sopenharmony_ci void *data, const char *long_name, 228062306a36Sopenharmony_ci const char *prefix) 228162306a36Sopenharmony_ci{ 228262306a36Sopenharmony_ci struct snd_kcontrol_new template; 228362306a36Sopenharmony_ci struct snd_kcontrol *kcontrol; 228462306a36Sopenharmony_ci char *name = NULL; 228562306a36Sopenharmony_ci 228662306a36Sopenharmony_ci memcpy(&template, _template, sizeof(template)); 228762306a36Sopenharmony_ci template.index = 0; 228862306a36Sopenharmony_ci 228962306a36Sopenharmony_ci if (!long_name) 229062306a36Sopenharmony_ci long_name = template.name; 229162306a36Sopenharmony_ci 229262306a36Sopenharmony_ci if (prefix) { 229362306a36Sopenharmony_ci name = kasprintf(GFP_KERNEL, "%s %s", prefix, long_name); 229462306a36Sopenharmony_ci if (!name) 229562306a36Sopenharmony_ci return NULL; 229662306a36Sopenharmony_ci 229762306a36Sopenharmony_ci template.name = name; 229862306a36Sopenharmony_ci } else { 229962306a36Sopenharmony_ci template.name = long_name; 230062306a36Sopenharmony_ci } 230162306a36Sopenharmony_ci 230262306a36Sopenharmony_ci kcontrol = snd_ctl_new1(&template, data); 230362306a36Sopenharmony_ci 230462306a36Sopenharmony_ci kfree(name); 230562306a36Sopenharmony_ci 230662306a36Sopenharmony_ci return kcontrol; 230762306a36Sopenharmony_ci} 230862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_cnew); 230962306a36Sopenharmony_ci 231062306a36Sopenharmony_cistatic int snd_soc_add_controls(struct snd_card *card, struct device *dev, 231162306a36Sopenharmony_ci const struct snd_kcontrol_new *controls, int num_controls, 231262306a36Sopenharmony_ci const char *prefix, void *data) 231362306a36Sopenharmony_ci{ 231462306a36Sopenharmony_ci int i; 231562306a36Sopenharmony_ci 231662306a36Sopenharmony_ci for (i = 0; i < num_controls; i++) { 231762306a36Sopenharmony_ci const struct snd_kcontrol_new *control = &controls[i]; 231862306a36Sopenharmony_ci int err = snd_ctl_add(card, snd_soc_cnew(control, data, 231962306a36Sopenharmony_ci control->name, prefix)); 232062306a36Sopenharmony_ci if (err < 0) { 232162306a36Sopenharmony_ci dev_err(dev, "ASoC: Failed to add %s: %d\n", 232262306a36Sopenharmony_ci control->name, err); 232362306a36Sopenharmony_ci return err; 232462306a36Sopenharmony_ci } 232562306a36Sopenharmony_ci } 232662306a36Sopenharmony_ci 232762306a36Sopenharmony_ci return 0; 232862306a36Sopenharmony_ci} 232962306a36Sopenharmony_ci 233062306a36Sopenharmony_ci/** 233162306a36Sopenharmony_ci * snd_soc_add_component_controls - Add an array of controls to a component. 233262306a36Sopenharmony_ci * 233362306a36Sopenharmony_ci * @component: Component to add controls to 233462306a36Sopenharmony_ci * @controls: Array of controls to add 233562306a36Sopenharmony_ci * @num_controls: Number of elements in the array 233662306a36Sopenharmony_ci * 233762306a36Sopenharmony_ci * Return: 0 for success, else error. 233862306a36Sopenharmony_ci */ 233962306a36Sopenharmony_ciint snd_soc_add_component_controls(struct snd_soc_component *component, 234062306a36Sopenharmony_ci const struct snd_kcontrol_new *controls, unsigned int num_controls) 234162306a36Sopenharmony_ci{ 234262306a36Sopenharmony_ci struct snd_card *card = component->card->snd_card; 234362306a36Sopenharmony_ci 234462306a36Sopenharmony_ci return snd_soc_add_controls(card, component->dev, controls, 234562306a36Sopenharmony_ci num_controls, component->name_prefix, component); 234662306a36Sopenharmony_ci} 234762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_add_component_controls); 234862306a36Sopenharmony_ci 234962306a36Sopenharmony_ci/** 235062306a36Sopenharmony_ci * snd_soc_add_card_controls - add an array of controls to a SoC card. 235162306a36Sopenharmony_ci * Convenience function to add a list of controls. 235262306a36Sopenharmony_ci * 235362306a36Sopenharmony_ci * @soc_card: SoC card to add controls to 235462306a36Sopenharmony_ci * @controls: array of controls to add 235562306a36Sopenharmony_ci * @num_controls: number of elements in the array 235662306a36Sopenharmony_ci * 235762306a36Sopenharmony_ci * Return 0 for success, else error. 235862306a36Sopenharmony_ci */ 235962306a36Sopenharmony_ciint snd_soc_add_card_controls(struct snd_soc_card *soc_card, 236062306a36Sopenharmony_ci const struct snd_kcontrol_new *controls, int num_controls) 236162306a36Sopenharmony_ci{ 236262306a36Sopenharmony_ci struct snd_card *card = soc_card->snd_card; 236362306a36Sopenharmony_ci 236462306a36Sopenharmony_ci return snd_soc_add_controls(card, soc_card->dev, controls, num_controls, 236562306a36Sopenharmony_ci NULL, soc_card); 236662306a36Sopenharmony_ci} 236762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_add_card_controls); 236862306a36Sopenharmony_ci 236962306a36Sopenharmony_ci/** 237062306a36Sopenharmony_ci * snd_soc_add_dai_controls - add an array of controls to a DAI. 237162306a36Sopenharmony_ci * Convienience function to add a list of controls. 237262306a36Sopenharmony_ci * 237362306a36Sopenharmony_ci * @dai: DAI to add controls to 237462306a36Sopenharmony_ci * @controls: array of controls to add 237562306a36Sopenharmony_ci * @num_controls: number of elements in the array 237662306a36Sopenharmony_ci * 237762306a36Sopenharmony_ci * Return 0 for success, else error. 237862306a36Sopenharmony_ci */ 237962306a36Sopenharmony_ciint snd_soc_add_dai_controls(struct snd_soc_dai *dai, 238062306a36Sopenharmony_ci const struct snd_kcontrol_new *controls, int num_controls) 238162306a36Sopenharmony_ci{ 238262306a36Sopenharmony_ci struct snd_card *card = dai->component->card->snd_card; 238362306a36Sopenharmony_ci 238462306a36Sopenharmony_ci return snd_soc_add_controls(card, dai->dev, controls, num_controls, 238562306a36Sopenharmony_ci NULL, dai); 238662306a36Sopenharmony_ci} 238762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_add_dai_controls); 238862306a36Sopenharmony_ci 238962306a36Sopenharmony_ci/** 239062306a36Sopenharmony_ci * snd_soc_register_card - Register a card with the ASoC core 239162306a36Sopenharmony_ci * 239262306a36Sopenharmony_ci * @card: Card to register 239362306a36Sopenharmony_ci * 239462306a36Sopenharmony_ci */ 239562306a36Sopenharmony_ciint snd_soc_register_card(struct snd_soc_card *card) 239662306a36Sopenharmony_ci{ 239762306a36Sopenharmony_ci if (!card->name || !card->dev) 239862306a36Sopenharmony_ci return -EINVAL; 239962306a36Sopenharmony_ci 240062306a36Sopenharmony_ci dev_set_drvdata(card->dev, card); 240162306a36Sopenharmony_ci 240262306a36Sopenharmony_ci INIT_LIST_HEAD(&card->widgets); 240362306a36Sopenharmony_ci INIT_LIST_HEAD(&card->paths); 240462306a36Sopenharmony_ci INIT_LIST_HEAD(&card->dapm_list); 240562306a36Sopenharmony_ci INIT_LIST_HEAD(&card->aux_comp_list); 240662306a36Sopenharmony_ci INIT_LIST_HEAD(&card->component_dev_list); 240762306a36Sopenharmony_ci INIT_LIST_HEAD(&card->list); 240862306a36Sopenharmony_ci INIT_LIST_HEAD(&card->rtd_list); 240962306a36Sopenharmony_ci INIT_LIST_HEAD(&card->dapm_dirty); 241062306a36Sopenharmony_ci INIT_LIST_HEAD(&card->dobj_list); 241162306a36Sopenharmony_ci 241262306a36Sopenharmony_ci card->instantiated = 0; 241362306a36Sopenharmony_ci mutex_init(&card->mutex); 241462306a36Sopenharmony_ci mutex_init(&card->dapm_mutex); 241562306a36Sopenharmony_ci mutex_init(&card->pcm_mutex); 241662306a36Sopenharmony_ci 241762306a36Sopenharmony_ci return snd_soc_bind_card(card); 241862306a36Sopenharmony_ci} 241962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_register_card); 242062306a36Sopenharmony_ci 242162306a36Sopenharmony_ci/** 242262306a36Sopenharmony_ci * snd_soc_unregister_card - Unregister a card with the ASoC core 242362306a36Sopenharmony_ci * 242462306a36Sopenharmony_ci * @card: Card to unregister 242562306a36Sopenharmony_ci * 242662306a36Sopenharmony_ci */ 242762306a36Sopenharmony_civoid snd_soc_unregister_card(struct snd_soc_card *card) 242862306a36Sopenharmony_ci{ 242962306a36Sopenharmony_ci mutex_lock(&client_mutex); 243062306a36Sopenharmony_ci snd_soc_unbind_card(card, true); 243162306a36Sopenharmony_ci mutex_unlock(&client_mutex); 243262306a36Sopenharmony_ci dev_dbg(card->dev, "ASoC: Unregistered card '%s'\n", card->name); 243362306a36Sopenharmony_ci} 243462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_unregister_card); 243562306a36Sopenharmony_ci 243662306a36Sopenharmony_ci/* 243762306a36Sopenharmony_ci * Simplify DAI link configuration by removing ".-1" from device names 243862306a36Sopenharmony_ci * and sanitizing names. 243962306a36Sopenharmony_ci */ 244062306a36Sopenharmony_cistatic char *fmt_single_name(struct device *dev, int *id) 244162306a36Sopenharmony_ci{ 244262306a36Sopenharmony_ci const char *devname = dev_name(dev); 244362306a36Sopenharmony_ci char *found, *name; 244462306a36Sopenharmony_ci unsigned int id1, id2; 244562306a36Sopenharmony_ci 244662306a36Sopenharmony_ci if (devname == NULL) 244762306a36Sopenharmony_ci return NULL; 244862306a36Sopenharmony_ci 244962306a36Sopenharmony_ci name = devm_kstrdup(dev, devname, GFP_KERNEL); 245062306a36Sopenharmony_ci if (!name) 245162306a36Sopenharmony_ci return NULL; 245262306a36Sopenharmony_ci 245362306a36Sopenharmony_ci /* are we a "%s.%d" name (platform and SPI components) */ 245462306a36Sopenharmony_ci found = strstr(name, dev->driver->name); 245562306a36Sopenharmony_ci if (found) { 245662306a36Sopenharmony_ci /* get ID */ 245762306a36Sopenharmony_ci if (sscanf(&found[strlen(dev->driver->name)], ".%d", id) == 1) { 245862306a36Sopenharmony_ci 245962306a36Sopenharmony_ci /* discard ID from name if ID == -1 */ 246062306a36Sopenharmony_ci if (*id == -1) 246162306a36Sopenharmony_ci found[strlen(dev->driver->name)] = '\0'; 246262306a36Sopenharmony_ci } 246362306a36Sopenharmony_ci 246462306a36Sopenharmony_ci /* I2C component devices are named "bus-addr" */ 246562306a36Sopenharmony_ci } else if (sscanf(name, "%x-%x", &id1, &id2) == 2) { 246662306a36Sopenharmony_ci 246762306a36Sopenharmony_ci /* create unique ID number from I2C addr and bus */ 246862306a36Sopenharmony_ci *id = ((id1 & 0xffff) << 16) + id2; 246962306a36Sopenharmony_ci 247062306a36Sopenharmony_ci devm_kfree(dev, name); 247162306a36Sopenharmony_ci 247262306a36Sopenharmony_ci /* sanitize component name for DAI link creation */ 247362306a36Sopenharmony_ci name = devm_kasprintf(dev, GFP_KERNEL, "%s.%s", dev->driver->name, devname); 247462306a36Sopenharmony_ci } else { 247562306a36Sopenharmony_ci *id = 0; 247662306a36Sopenharmony_ci } 247762306a36Sopenharmony_ci 247862306a36Sopenharmony_ci return name; 247962306a36Sopenharmony_ci} 248062306a36Sopenharmony_ci 248162306a36Sopenharmony_ci/* 248262306a36Sopenharmony_ci * Simplify DAI link naming for single devices with multiple DAIs by removing 248362306a36Sopenharmony_ci * any ".-1" and using the DAI name (instead of device name). 248462306a36Sopenharmony_ci */ 248562306a36Sopenharmony_cistatic inline char *fmt_multiple_name(struct device *dev, 248662306a36Sopenharmony_ci struct snd_soc_dai_driver *dai_drv) 248762306a36Sopenharmony_ci{ 248862306a36Sopenharmony_ci if (dai_drv->name == NULL) { 248962306a36Sopenharmony_ci dev_err(dev, 249062306a36Sopenharmony_ci "ASoC: error - multiple DAI %s registered with no name\n", 249162306a36Sopenharmony_ci dev_name(dev)); 249262306a36Sopenharmony_ci return NULL; 249362306a36Sopenharmony_ci } 249462306a36Sopenharmony_ci 249562306a36Sopenharmony_ci return devm_kstrdup(dev, dai_drv->name, GFP_KERNEL); 249662306a36Sopenharmony_ci} 249762306a36Sopenharmony_ci 249862306a36Sopenharmony_civoid snd_soc_unregister_dai(struct snd_soc_dai *dai) 249962306a36Sopenharmony_ci{ 250062306a36Sopenharmony_ci dev_dbg(dai->dev, "ASoC: Unregistered DAI '%s'\n", dai->name); 250162306a36Sopenharmony_ci list_del(&dai->list); 250262306a36Sopenharmony_ci} 250362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_unregister_dai); 250462306a36Sopenharmony_ci 250562306a36Sopenharmony_ci/** 250662306a36Sopenharmony_ci * snd_soc_register_dai - Register a DAI dynamically & create its widgets 250762306a36Sopenharmony_ci * 250862306a36Sopenharmony_ci * @component: The component the DAIs are registered for 250962306a36Sopenharmony_ci * @dai_drv: DAI driver to use for the DAI 251062306a36Sopenharmony_ci * @legacy_dai_naming: if %true, use legacy single-name format; 251162306a36Sopenharmony_ci * if %false, use multiple-name format; 251262306a36Sopenharmony_ci * 251362306a36Sopenharmony_ci * Topology can use this API to register DAIs when probing a component. 251462306a36Sopenharmony_ci * These DAIs's widgets will be freed in the card cleanup and the DAIs 251562306a36Sopenharmony_ci * will be freed in the component cleanup. 251662306a36Sopenharmony_ci */ 251762306a36Sopenharmony_cistruct snd_soc_dai *snd_soc_register_dai(struct snd_soc_component *component, 251862306a36Sopenharmony_ci struct snd_soc_dai_driver *dai_drv, 251962306a36Sopenharmony_ci bool legacy_dai_naming) 252062306a36Sopenharmony_ci{ 252162306a36Sopenharmony_ci struct device *dev = component->dev; 252262306a36Sopenharmony_ci struct snd_soc_dai *dai; 252362306a36Sopenharmony_ci 252462306a36Sopenharmony_ci lockdep_assert_held(&client_mutex); 252562306a36Sopenharmony_ci 252662306a36Sopenharmony_ci dai = devm_kzalloc(dev, sizeof(*dai), GFP_KERNEL); 252762306a36Sopenharmony_ci if (dai == NULL) 252862306a36Sopenharmony_ci return NULL; 252962306a36Sopenharmony_ci 253062306a36Sopenharmony_ci /* 253162306a36Sopenharmony_ci * Back in the old days when we still had component-less DAIs, 253262306a36Sopenharmony_ci * instead of having a static name, component-less DAIs would 253362306a36Sopenharmony_ci * inherit the name of the parent device so it is possible to 253462306a36Sopenharmony_ci * register multiple instances of the DAI. We still need to keep 253562306a36Sopenharmony_ci * the same naming style even though those DAIs are not 253662306a36Sopenharmony_ci * component-less anymore. 253762306a36Sopenharmony_ci */ 253862306a36Sopenharmony_ci if (legacy_dai_naming && 253962306a36Sopenharmony_ci (dai_drv->id == 0 || dai_drv->name == NULL)) { 254062306a36Sopenharmony_ci dai->name = fmt_single_name(dev, &dai->id); 254162306a36Sopenharmony_ci } else { 254262306a36Sopenharmony_ci dai->name = fmt_multiple_name(dev, dai_drv); 254362306a36Sopenharmony_ci if (dai_drv->id) 254462306a36Sopenharmony_ci dai->id = dai_drv->id; 254562306a36Sopenharmony_ci else 254662306a36Sopenharmony_ci dai->id = component->num_dai; 254762306a36Sopenharmony_ci } 254862306a36Sopenharmony_ci if (!dai->name) 254962306a36Sopenharmony_ci return NULL; 255062306a36Sopenharmony_ci 255162306a36Sopenharmony_ci dai->component = component; 255262306a36Sopenharmony_ci dai->dev = dev; 255362306a36Sopenharmony_ci dai->driver = dai_drv; 255462306a36Sopenharmony_ci 255562306a36Sopenharmony_ci /* see for_each_component_dais */ 255662306a36Sopenharmony_ci list_add_tail(&dai->list, &component->dai_list); 255762306a36Sopenharmony_ci component->num_dai++; 255862306a36Sopenharmony_ci 255962306a36Sopenharmony_ci dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name); 256062306a36Sopenharmony_ci return dai; 256162306a36Sopenharmony_ci} 256262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_register_dai); 256362306a36Sopenharmony_ci 256462306a36Sopenharmony_ci/** 256562306a36Sopenharmony_ci * snd_soc_unregister_dais - Unregister DAIs from the ASoC core 256662306a36Sopenharmony_ci * 256762306a36Sopenharmony_ci * @component: The component for which the DAIs should be unregistered 256862306a36Sopenharmony_ci */ 256962306a36Sopenharmony_cistatic void snd_soc_unregister_dais(struct snd_soc_component *component) 257062306a36Sopenharmony_ci{ 257162306a36Sopenharmony_ci struct snd_soc_dai *dai, *_dai; 257262306a36Sopenharmony_ci 257362306a36Sopenharmony_ci for_each_component_dais_safe(component, dai, _dai) 257462306a36Sopenharmony_ci snd_soc_unregister_dai(dai); 257562306a36Sopenharmony_ci} 257662306a36Sopenharmony_ci 257762306a36Sopenharmony_ci/** 257862306a36Sopenharmony_ci * snd_soc_register_dais - Register a DAI with the ASoC core 257962306a36Sopenharmony_ci * 258062306a36Sopenharmony_ci * @component: The component the DAIs are registered for 258162306a36Sopenharmony_ci * @dai_drv: DAI driver to use for the DAIs 258262306a36Sopenharmony_ci * @count: Number of DAIs 258362306a36Sopenharmony_ci */ 258462306a36Sopenharmony_cistatic int snd_soc_register_dais(struct snd_soc_component *component, 258562306a36Sopenharmony_ci struct snd_soc_dai_driver *dai_drv, 258662306a36Sopenharmony_ci size_t count) 258762306a36Sopenharmony_ci{ 258862306a36Sopenharmony_ci struct snd_soc_dai *dai; 258962306a36Sopenharmony_ci unsigned int i; 259062306a36Sopenharmony_ci int ret; 259162306a36Sopenharmony_ci 259262306a36Sopenharmony_ci for (i = 0; i < count; i++) { 259362306a36Sopenharmony_ci dai = snd_soc_register_dai(component, dai_drv + i, count == 1 && 259462306a36Sopenharmony_ci component->driver->legacy_dai_naming); 259562306a36Sopenharmony_ci if (dai == NULL) { 259662306a36Sopenharmony_ci ret = -ENOMEM; 259762306a36Sopenharmony_ci goto err; 259862306a36Sopenharmony_ci } 259962306a36Sopenharmony_ci } 260062306a36Sopenharmony_ci 260162306a36Sopenharmony_ci return 0; 260262306a36Sopenharmony_ci 260362306a36Sopenharmony_cierr: 260462306a36Sopenharmony_ci snd_soc_unregister_dais(component); 260562306a36Sopenharmony_ci 260662306a36Sopenharmony_ci return ret; 260762306a36Sopenharmony_ci} 260862306a36Sopenharmony_ci 260962306a36Sopenharmony_ci#define ENDIANNESS_MAP(name) \ 261062306a36Sopenharmony_ci (SNDRV_PCM_FMTBIT_##name##LE | SNDRV_PCM_FMTBIT_##name##BE) 261162306a36Sopenharmony_cistatic u64 endianness_format_map[] = { 261262306a36Sopenharmony_ci ENDIANNESS_MAP(S16_), 261362306a36Sopenharmony_ci ENDIANNESS_MAP(U16_), 261462306a36Sopenharmony_ci ENDIANNESS_MAP(S24_), 261562306a36Sopenharmony_ci ENDIANNESS_MAP(U24_), 261662306a36Sopenharmony_ci ENDIANNESS_MAP(S32_), 261762306a36Sopenharmony_ci ENDIANNESS_MAP(U32_), 261862306a36Sopenharmony_ci ENDIANNESS_MAP(S24_3), 261962306a36Sopenharmony_ci ENDIANNESS_MAP(U24_3), 262062306a36Sopenharmony_ci ENDIANNESS_MAP(S20_3), 262162306a36Sopenharmony_ci ENDIANNESS_MAP(U20_3), 262262306a36Sopenharmony_ci ENDIANNESS_MAP(S18_3), 262362306a36Sopenharmony_ci ENDIANNESS_MAP(U18_3), 262462306a36Sopenharmony_ci ENDIANNESS_MAP(FLOAT_), 262562306a36Sopenharmony_ci ENDIANNESS_MAP(FLOAT64_), 262662306a36Sopenharmony_ci ENDIANNESS_MAP(IEC958_SUBFRAME_), 262762306a36Sopenharmony_ci}; 262862306a36Sopenharmony_ci 262962306a36Sopenharmony_ci/* 263062306a36Sopenharmony_ci * Fix up the DAI formats for endianness: codecs don't actually see 263162306a36Sopenharmony_ci * the endianness of the data but we're using the CPU format 263262306a36Sopenharmony_ci * definitions which do need to include endianness so we ensure that 263362306a36Sopenharmony_ci * codec DAIs always have both big and little endian variants set. 263462306a36Sopenharmony_ci */ 263562306a36Sopenharmony_cistatic void convert_endianness_formats(struct snd_soc_pcm_stream *stream) 263662306a36Sopenharmony_ci{ 263762306a36Sopenharmony_ci int i; 263862306a36Sopenharmony_ci 263962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(endianness_format_map); i++) 264062306a36Sopenharmony_ci if (stream->formats & endianness_format_map[i]) 264162306a36Sopenharmony_ci stream->formats |= endianness_format_map[i]; 264262306a36Sopenharmony_ci} 264362306a36Sopenharmony_ci 264462306a36Sopenharmony_cistatic void snd_soc_try_rebind_card(void) 264562306a36Sopenharmony_ci{ 264662306a36Sopenharmony_ci struct snd_soc_card *card, *c; 264762306a36Sopenharmony_ci 264862306a36Sopenharmony_ci list_for_each_entry_safe(card, c, &unbind_card_list, list) 264962306a36Sopenharmony_ci if (!snd_soc_bind_card(card)) 265062306a36Sopenharmony_ci list_del(&card->list); 265162306a36Sopenharmony_ci} 265262306a36Sopenharmony_ci 265362306a36Sopenharmony_cistatic void snd_soc_del_component_unlocked(struct snd_soc_component *component) 265462306a36Sopenharmony_ci{ 265562306a36Sopenharmony_ci struct snd_soc_card *card = component->card; 265662306a36Sopenharmony_ci 265762306a36Sopenharmony_ci snd_soc_unregister_dais(component); 265862306a36Sopenharmony_ci 265962306a36Sopenharmony_ci if (card) 266062306a36Sopenharmony_ci snd_soc_unbind_card(card, false); 266162306a36Sopenharmony_ci 266262306a36Sopenharmony_ci list_del(&component->list); 266362306a36Sopenharmony_ci} 266462306a36Sopenharmony_ci 266562306a36Sopenharmony_ciint snd_soc_component_initialize(struct snd_soc_component *component, 266662306a36Sopenharmony_ci const struct snd_soc_component_driver *driver, 266762306a36Sopenharmony_ci struct device *dev) 266862306a36Sopenharmony_ci{ 266962306a36Sopenharmony_ci INIT_LIST_HEAD(&component->dai_list); 267062306a36Sopenharmony_ci INIT_LIST_HEAD(&component->dobj_list); 267162306a36Sopenharmony_ci INIT_LIST_HEAD(&component->card_list); 267262306a36Sopenharmony_ci INIT_LIST_HEAD(&component->list); 267362306a36Sopenharmony_ci mutex_init(&component->io_mutex); 267462306a36Sopenharmony_ci 267562306a36Sopenharmony_ci component->name = fmt_single_name(dev, &component->id); 267662306a36Sopenharmony_ci if (!component->name) { 267762306a36Sopenharmony_ci dev_err(dev, "ASoC: Failed to allocate name\n"); 267862306a36Sopenharmony_ci return -ENOMEM; 267962306a36Sopenharmony_ci } 268062306a36Sopenharmony_ci 268162306a36Sopenharmony_ci component->dev = dev; 268262306a36Sopenharmony_ci component->driver = driver; 268362306a36Sopenharmony_ci 268462306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 268562306a36Sopenharmony_ci if (!component->debugfs_prefix) 268662306a36Sopenharmony_ci component->debugfs_prefix = driver->debugfs_prefix; 268762306a36Sopenharmony_ci#endif 268862306a36Sopenharmony_ci 268962306a36Sopenharmony_ci return 0; 269062306a36Sopenharmony_ci} 269162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_component_initialize); 269262306a36Sopenharmony_ci 269362306a36Sopenharmony_ciint snd_soc_add_component(struct snd_soc_component *component, 269462306a36Sopenharmony_ci struct snd_soc_dai_driver *dai_drv, 269562306a36Sopenharmony_ci int num_dai) 269662306a36Sopenharmony_ci{ 269762306a36Sopenharmony_ci int ret; 269862306a36Sopenharmony_ci int i; 269962306a36Sopenharmony_ci 270062306a36Sopenharmony_ci mutex_lock(&client_mutex); 270162306a36Sopenharmony_ci 270262306a36Sopenharmony_ci if (component->driver->endianness) { 270362306a36Sopenharmony_ci for (i = 0; i < num_dai; i++) { 270462306a36Sopenharmony_ci convert_endianness_formats(&dai_drv[i].playback); 270562306a36Sopenharmony_ci convert_endianness_formats(&dai_drv[i].capture); 270662306a36Sopenharmony_ci } 270762306a36Sopenharmony_ci } 270862306a36Sopenharmony_ci 270962306a36Sopenharmony_ci ret = snd_soc_register_dais(component, dai_drv, num_dai); 271062306a36Sopenharmony_ci if (ret < 0) { 271162306a36Sopenharmony_ci dev_err(component->dev, "ASoC: Failed to register DAIs: %d\n", 271262306a36Sopenharmony_ci ret); 271362306a36Sopenharmony_ci goto err_cleanup; 271462306a36Sopenharmony_ci } 271562306a36Sopenharmony_ci 271662306a36Sopenharmony_ci if (!component->driver->write && !component->driver->read) { 271762306a36Sopenharmony_ci if (!component->regmap) 271862306a36Sopenharmony_ci component->regmap = dev_get_regmap(component->dev, 271962306a36Sopenharmony_ci NULL); 272062306a36Sopenharmony_ci if (component->regmap) 272162306a36Sopenharmony_ci snd_soc_component_setup_regmap(component); 272262306a36Sopenharmony_ci } 272362306a36Sopenharmony_ci 272462306a36Sopenharmony_ci /* see for_each_component */ 272562306a36Sopenharmony_ci list_add(&component->list, &component_list); 272662306a36Sopenharmony_ci 272762306a36Sopenharmony_cierr_cleanup: 272862306a36Sopenharmony_ci if (ret < 0) 272962306a36Sopenharmony_ci snd_soc_del_component_unlocked(component); 273062306a36Sopenharmony_ci 273162306a36Sopenharmony_ci mutex_unlock(&client_mutex); 273262306a36Sopenharmony_ci 273362306a36Sopenharmony_ci if (ret == 0) 273462306a36Sopenharmony_ci snd_soc_try_rebind_card(); 273562306a36Sopenharmony_ci 273662306a36Sopenharmony_ci return ret; 273762306a36Sopenharmony_ci} 273862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_add_component); 273962306a36Sopenharmony_ci 274062306a36Sopenharmony_ciint snd_soc_register_component(struct device *dev, 274162306a36Sopenharmony_ci const struct snd_soc_component_driver *component_driver, 274262306a36Sopenharmony_ci struct snd_soc_dai_driver *dai_drv, 274362306a36Sopenharmony_ci int num_dai) 274462306a36Sopenharmony_ci{ 274562306a36Sopenharmony_ci struct snd_soc_component *component; 274662306a36Sopenharmony_ci int ret; 274762306a36Sopenharmony_ci 274862306a36Sopenharmony_ci component = devm_kzalloc(dev, sizeof(*component), GFP_KERNEL); 274962306a36Sopenharmony_ci if (!component) 275062306a36Sopenharmony_ci return -ENOMEM; 275162306a36Sopenharmony_ci 275262306a36Sopenharmony_ci ret = snd_soc_component_initialize(component, component_driver, dev); 275362306a36Sopenharmony_ci if (ret < 0) 275462306a36Sopenharmony_ci return ret; 275562306a36Sopenharmony_ci 275662306a36Sopenharmony_ci return snd_soc_add_component(component, dai_drv, num_dai); 275762306a36Sopenharmony_ci} 275862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_register_component); 275962306a36Sopenharmony_ci 276062306a36Sopenharmony_ci/** 276162306a36Sopenharmony_ci * snd_soc_unregister_component_by_driver - Unregister component using a given driver 276262306a36Sopenharmony_ci * from the ASoC core 276362306a36Sopenharmony_ci * 276462306a36Sopenharmony_ci * @dev: The device to unregister 276562306a36Sopenharmony_ci * @component_driver: The component driver to unregister 276662306a36Sopenharmony_ci */ 276762306a36Sopenharmony_civoid snd_soc_unregister_component_by_driver(struct device *dev, 276862306a36Sopenharmony_ci const struct snd_soc_component_driver *component_driver) 276962306a36Sopenharmony_ci{ 277062306a36Sopenharmony_ci struct snd_soc_component *component; 277162306a36Sopenharmony_ci 277262306a36Sopenharmony_ci if (!component_driver) 277362306a36Sopenharmony_ci return; 277462306a36Sopenharmony_ci 277562306a36Sopenharmony_ci mutex_lock(&client_mutex); 277662306a36Sopenharmony_ci component = snd_soc_lookup_component_nolocked(dev, component_driver->name); 277762306a36Sopenharmony_ci if (!component) 277862306a36Sopenharmony_ci goto out; 277962306a36Sopenharmony_ci 278062306a36Sopenharmony_ci snd_soc_del_component_unlocked(component); 278162306a36Sopenharmony_ci 278262306a36Sopenharmony_ciout: 278362306a36Sopenharmony_ci mutex_unlock(&client_mutex); 278462306a36Sopenharmony_ci} 278562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_unregister_component_by_driver); 278662306a36Sopenharmony_ci 278762306a36Sopenharmony_ci/** 278862306a36Sopenharmony_ci * snd_soc_unregister_component - Unregister all related component 278962306a36Sopenharmony_ci * from the ASoC core 279062306a36Sopenharmony_ci * 279162306a36Sopenharmony_ci * @dev: The device to unregister 279262306a36Sopenharmony_ci */ 279362306a36Sopenharmony_civoid snd_soc_unregister_component(struct device *dev) 279462306a36Sopenharmony_ci{ 279562306a36Sopenharmony_ci mutex_lock(&client_mutex); 279662306a36Sopenharmony_ci while (1) { 279762306a36Sopenharmony_ci struct snd_soc_component *component = snd_soc_lookup_component_nolocked(dev, NULL); 279862306a36Sopenharmony_ci 279962306a36Sopenharmony_ci if (!component) 280062306a36Sopenharmony_ci break; 280162306a36Sopenharmony_ci 280262306a36Sopenharmony_ci snd_soc_del_component_unlocked(component); 280362306a36Sopenharmony_ci } 280462306a36Sopenharmony_ci mutex_unlock(&client_mutex); 280562306a36Sopenharmony_ci} 280662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_unregister_component); 280762306a36Sopenharmony_ci 280862306a36Sopenharmony_ci/* Retrieve a card's name from device tree */ 280962306a36Sopenharmony_ciint snd_soc_of_parse_card_name(struct snd_soc_card *card, 281062306a36Sopenharmony_ci const char *propname) 281162306a36Sopenharmony_ci{ 281262306a36Sopenharmony_ci struct device_node *np; 281362306a36Sopenharmony_ci int ret; 281462306a36Sopenharmony_ci 281562306a36Sopenharmony_ci if (!card->dev) { 281662306a36Sopenharmony_ci pr_err("card->dev is not set before calling %s\n", __func__); 281762306a36Sopenharmony_ci return -EINVAL; 281862306a36Sopenharmony_ci } 281962306a36Sopenharmony_ci 282062306a36Sopenharmony_ci np = card->dev->of_node; 282162306a36Sopenharmony_ci 282262306a36Sopenharmony_ci ret = of_property_read_string_index(np, propname, 0, &card->name); 282362306a36Sopenharmony_ci /* 282462306a36Sopenharmony_ci * EINVAL means the property does not exist. This is fine providing 282562306a36Sopenharmony_ci * card->name was previously set, which is checked later in 282662306a36Sopenharmony_ci * snd_soc_register_card. 282762306a36Sopenharmony_ci */ 282862306a36Sopenharmony_ci if (ret < 0 && ret != -EINVAL) { 282962306a36Sopenharmony_ci dev_err(card->dev, 283062306a36Sopenharmony_ci "ASoC: Property '%s' could not be read: %d\n", 283162306a36Sopenharmony_ci propname, ret); 283262306a36Sopenharmony_ci return ret; 283362306a36Sopenharmony_ci } 283462306a36Sopenharmony_ci 283562306a36Sopenharmony_ci return 0; 283662306a36Sopenharmony_ci} 283762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_of_parse_card_name); 283862306a36Sopenharmony_ci 283962306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget simple_widgets[] = { 284062306a36Sopenharmony_ci SND_SOC_DAPM_MIC("Microphone", NULL), 284162306a36Sopenharmony_ci SND_SOC_DAPM_LINE("Line", NULL), 284262306a36Sopenharmony_ci SND_SOC_DAPM_HP("Headphone", NULL), 284362306a36Sopenharmony_ci SND_SOC_DAPM_SPK("Speaker", NULL), 284462306a36Sopenharmony_ci}; 284562306a36Sopenharmony_ci 284662306a36Sopenharmony_ciint snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card, 284762306a36Sopenharmony_ci const char *propname) 284862306a36Sopenharmony_ci{ 284962306a36Sopenharmony_ci struct device_node *np = card->dev->of_node; 285062306a36Sopenharmony_ci struct snd_soc_dapm_widget *widgets; 285162306a36Sopenharmony_ci const char *template, *wname; 285262306a36Sopenharmony_ci int i, j, num_widgets; 285362306a36Sopenharmony_ci 285462306a36Sopenharmony_ci num_widgets = of_property_count_strings(np, propname); 285562306a36Sopenharmony_ci if (num_widgets < 0) { 285662306a36Sopenharmony_ci dev_err(card->dev, 285762306a36Sopenharmony_ci "ASoC: Property '%s' does not exist\n", propname); 285862306a36Sopenharmony_ci return -EINVAL; 285962306a36Sopenharmony_ci } 286062306a36Sopenharmony_ci if (!num_widgets) { 286162306a36Sopenharmony_ci dev_err(card->dev, "ASoC: Property '%s's length is zero\n", 286262306a36Sopenharmony_ci propname); 286362306a36Sopenharmony_ci return -EINVAL; 286462306a36Sopenharmony_ci } 286562306a36Sopenharmony_ci if (num_widgets & 1) { 286662306a36Sopenharmony_ci dev_err(card->dev, 286762306a36Sopenharmony_ci "ASoC: Property '%s' length is not even\n", propname); 286862306a36Sopenharmony_ci return -EINVAL; 286962306a36Sopenharmony_ci } 287062306a36Sopenharmony_ci 287162306a36Sopenharmony_ci num_widgets /= 2; 287262306a36Sopenharmony_ci 287362306a36Sopenharmony_ci widgets = devm_kcalloc(card->dev, num_widgets, sizeof(*widgets), 287462306a36Sopenharmony_ci GFP_KERNEL); 287562306a36Sopenharmony_ci if (!widgets) { 287662306a36Sopenharmony_ci dev_err(card->dev, 287762306a36Sopenharmony_ci "ASoC: Could not allocate memory for widgets\n"); 287862306a36Sopenharmony_ci return -ENOMEM; 287962306a36Sopenharmony_ci } 288062306a36Sopenharmony_ci 288162306a36Sopenharmony_ci for (i = 0; i < num_widgets; i++) { 288262306a36Sopenharmony_ci int ret = of_property_read_string_index(np, propname, 288362306a36Sopenharmony_ci 2 * i, &template); 288462306a36Sopenharmony_ci if (ret) { 288562306a36Sopenharmony_ci dev_err(card->dev, 288662306a36Sopenharmony_ci "ASoC: Property '%s' index %d read error:%d\n", 288762306a36Sopenharmony_ci propname, 2 * i, ret); 288862306a36Sopenharmony_ci return -EINVAL; 288962306a36Sopenharmony_ci } 289062306a36Sopenharmony_ci 289162306a36Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(simple_widgets); j++) { 289262306a36Sopenharmony_ci if (!strncmp(template, simple_widgets[j].name, 289362306a36Sopenharmony_ci strlen(simple_widgets[j].name))) { 289462306a36Sopenharmony_ci widgets[i] = simple_widgets[j]; 289562306a36Sopenharmony_ci break; 289662306a36Sopenharmony_ci } 289762306a36Sopenharmony_ci } 289862306a36Sopenharmony_ci 289962306a36Sopenharmony_ci if (j >= ARRAY_SIZE(simple_widgets)) { 290062306a36Sopenharmony_ci dev_err(card->dev, 290162306a36Sopenharmony_ci "ASoC: DAPM widget '%s' is not supported\n", 290262306a36Sopenharmony_ci template); 290362306a36Sopenharmony_ci return -EINVAL; 290462306a36Sopenharmony_ci } 290562306a36Sopenharmony_ci 290662306a36Sopenharmony_ci ret = of_property_read_string_index(np, propname, 290762306a36Sopenharmony_ci (2 * i) + 1, 290862306a36Sopenharmony_ci &wname); 290962306a36Sopenharmony_ci if (ret) { 291062306a36Sopenharmony_ci dev_err(card->dev, 291162306a36Sopenharmony_ci "ASoC: Property '%s' index %d read error:%d\n", 291262306a36Sopenharmony_ci propname, (2 * i) + 1, ret); 291362306a36Sopenharmony_ci return -EINVAL; 291462306a36Sopenharmony_ci } 291562306a36Sopenharmony_ci 291662306a36Sopenharmony_ci widgets[i].name = wname; 291762306a36Sopenharmony_ci } 291862306a36Sopenharmony_ci 291962306a36Sopenharmony_ci card->of_dapm_widgets = widgets; 292062306a36Sopenharmony_ci card->num_of_dapm_widgets = num_widgets; 292162306a36Sopenharmony_ci 292262306a36Sopenharmony_ci return 0; 292362306a36Sopenharmony_ci} 292462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_simple_widgets); 292562306a36Sopenharmony_ci 292662306a36Sopenharmony_ciint snd_soc_of_parse_pin_switches(struct snd_soc_card *card, const char *prop) 292762306a36Sopenharmony_ci{ 292862306a36Sopenharmony_ci const unsigned int nb_controls_max = 16; 292962306a36Sopenharmony_ci const char **strings, *control_name; 293062306a36Sopenharmony_ci struct snd_kcontrol_new *controls; 293162306a36Sopenharmony_ci struct device *dev = card->dev; 293262306a36Sopenharmony_ci unsigned int i, nb_controls; 293362306a36Sopenharmony_ci int ret; 293462306a36Sopenharmony_ci 293562306a36Sopenharmony_ci if (!of_property_read_bool(dev->of_node, prop)) 293662306a36Sopenharmony_ci return 0; 293762306a36Sopenharmony_ci 293862306a36Sopenharmony_ci strings = devm_kcalloc(dev, nb_controls_max, 293962306a36Sopenharmony_ci sizeof(*strings), GFP_KERNEL); 294062306a36Sopenharmony_ci if (!strings) 294162306a36Sopenharmony_ci return -ENOMEM; 294262306a36Sopenharmony_ci 294362306a36Sopenharmony_ci ret = of_property_read_string_array(dev->of_node, prop, 294462306a36Sopenharmony_ci strings, nb_controls_max); 294562306a36Sopenharmony_ci if (ret < 0) 294662306a36Sopenharmony_ci return ret; 294762306a36Sopenharmony_ci 294862306a36Sopenharmony_ci nb_controls = (unsigned int)ret; 294962306a36Sopenharmony_ci 295062306a36Sopenharmony_ci controls = devm_kcalloc(dev, nb_controls, 295162306a36Sopenharmony_ci sizeof(*controls), GFP_KERNEL); 295262306a36Sopenharmony_ci if (!controls) 295362306a36Sopenharmony_ci return -ENOMEM; 295462306a36Sopenharmony_ci 295562306a36Sopenharmony_ci for (i = 0; i < nb_controls; i++) { 295662306a36Sopenharmony_ci control_name = devm_kasprintf(dev, GFP_KERNEL, 295762306a36Sopenharmony_ci "%s Switch", strings[i]); 295862306a36Sopenharmony_ci if (!control_name) 295962306a36Sopenharmony_ci return -ENOMEM; 296062306a36Sopenharmony_ci 296162306a36Sopenharmony_ci controls[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; 296262306a36Sopenharmony_ci controls[i].name = control_name; 296362306a36Sopenharmony_ci controls[i].info = snd_soc_dapm_info_pin_switch; 296462306a36Sopenharmony_ci controls[i].get = snd_soc_dapm_get_pin_switch; 296562306a36Sopenharmony_ci controls[i].put = snd_soc_dapm_put_pin_switch; 296662306a36Sopenharmony_ci controls[i].private_value = (unsigned long)strings[i]; 296762306a36Sopenharmony_ci } 296862306a36Sopenharmony_ci 296962306a36Sopenharmony_ci card->controls = controls; 297062306a36Sopenharmony_ci card->num_controls = nb_controls; 297162306a36Sopenharmony_ci 297262306a36Sopenharmony_ci return 0; 297362306a36Sopenharmony_ci} 297462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_of_parse_pin_switches); 297562306a36Sopenharmony_ci 297662306a36Sopenharmony_ciint snd_soc_of_get_slot_mask(struct device_node *np, 297762306a36Sopenharmony_ci const char *prop_name, 297862306a36Sopenharmony_ci unsigned int *mask) 297962306a36Sopenharmony_ci{ 298062306a36Sopenharmony_ci u32 val; 298162306a36Sopenharmony_ci const __be32 *of_slot_mask = of_get_property(np, prop_name, &val); 298262306a36Sopenharmony_ci int i; 298362306a36Sopenharmony_ci 298462306a36Sopenharmony_ci if (!of_slot_mask) 298562306a36Sopenharmony_ci return 0; 298662306a36Sopenharmony_ci val /= sizeof(u32); 298762306a36Sopenharmony_ci for (i = 0; i < val; i++) 298862306a36Sopenharmony_ci if (be32_to_cpup(&of_slot_mask[i])) 298962306a36Sopenharmony_ci *mask |= (1 << i); 299062306a36Sopenharmony_ci 299162306a36Sopenharmony_ci return val; 299262306a36Sopenharmony_ci} 299362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_of_get_slot_mask); 299462306a36Sopenharmony_ci 299562306a36Sopenharmony_ciint snd_soc_of_parse_tdm_slot(struct device_node *np, 299662306a36Sopenharmony_ci unsigned int *tx_mask, 299762306a36Sopenharmony_ci unsigned int *rx_mask, 299862306a36Sopenharmony_ci unsigned int *slots, 299962306a36Sopenharmony_ci unsigned int *slot_width) 300062306a36Sopenharmony_ci{ 300162306a36Sopenharmony_ci u32 val; 300262306a36Sopenharmony_ci int ret; 300362306a36Sopenharmony_ci 300462306a36Sopenharmony_ci if (tx_mask) 300562306a36Sopenharmony_ci snd_soc_of_get_slot_mask(np, "dai-tdm-slot-tx-mask", tx_mask); 300662306a36Sopenharmony_ci if (rx_mask) 300762306a36Sopenharmony_ci snd_soc_of_get_slot_mask(np, "dai-tdm-slot-rx-mask", rx_mask); 300862306a36Sopenharmony_ci 300962306a36Sopenharmony_ci if (of_property_read_bool(np, "dai-tdm-slot-num")) { 301062306a36Sopenharmony_ci ret = of_property_read_u32(np, "dai-tdm-slot-num", &val); 301162306a36Sopenharmony_ci if (ret) 301262306a36Sopenharmony_ci return ret; 301362306a36Sopenharmony_ci 301462306a36Sopenharmony_ci if (slots) 301562306a36Sopenharmony_ci *slots = val; 301662306a36Sopenharmony_ci } 301762306a36Sopenharmony_ci 301862306a36Sopenharmony_ci if (of_property_read_bool(np, "dai-tdm-slot-width")) { 301962306a36Sopenharmony_ci ret = of_property_read_u32(np, "dai-tdm-slot-width", &val); 302062306a36Sopenharmony_ci if (ret) 302162306a36Sopenharmony_ci return ret; 302262306a36Sopenharmony_ci 302362306a36Sopenharmony_ci if (slot_width) 302462306a36Sopenharmony_ci *slot_width = val; 302562306a36Sopenharmony_ci } 302662306a36Sopenharmony_ci 302762306a36Sopenharmony_ci return 0; 302862306a36Sopenharmony_ci} 302962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_of_parse_tdm_slot); 303062306a36Sopenharmony_ci 303162306a36Sopenharmony_civoid snd_soc_dlc_use_cpu_as_platform(struct snd_soc_dai_link_component *platforms, 303262306a36Sopenharmony_ci struct snd_soc_dai_link_component *cpus) 303362306a36Sopenharmony_ci{ 303462306a36Sopenharmony_ci platforms->of_node = cpus->of_node; 303562306a36Sopenharmony_ci platforms->dai_args = cpus->dai_args; 303662306a36Sopenharmony_ci} 303762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dlc_use_cpu_as_platform); 303862306a36Sopenharmony_ci 303962306a36Sopenharmony_civoid snd_soc_of_parse_node_prefix(struct device_node *np, 304062306a36Sopenharmony_ci struct snd_soc_codec_conf *codec_conf, 304162306a36Sopenharmony_ci struct device_node *of_node, 304262306a36Sopenharmony_ci const char *propname) 304362306a36Sopenharmony_ci{ 304462306a36Sopenharmony_ci const char *str; 304562306a36Sopenharmony_ci int ret; 304662306a36Sopenharmony_ci 304762306a36Sopenharmony_ci ret = of_property_read_string(np, propname, &str); 304862306a36Sopenharmony_ci if (ret < 0) { 304962306a36Sopenharmony_ci /* no prefix is not error */ 305062306a36Sopenharmony_ci return; 305162306a36Sopenharmony_ci } 305262306a36Sopenharmony_ci 305362306a36Sopenharmony_ci codec_conf->dlc.of_node = of_node; 305462306a36Sopenharmony_ci codec_conf->name_prefix = str; 305562306a36Sopenharmony_ci} 305662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_of_parse_node_prefix); 305762306a36Sopenharmony_ci 305862306a36Sopenharmony_ciint snd_soc_of_parse_audio_routing(struct snd_soc_card *card, 305962306a36Sopenharmony_ci const char *propname) 306062306a36Sopenharmony_ci{ 306162306a36Sopenharmony_ci struct device_node *np = card->dev->of_node; 306262306a36Sopenharmony_ci int num_routes; 306362306a36Sopenharmony_ci struct snd_soc_dapm_route *routes; 306462306a36Sopenharmony_ci int i; 306562306a36Sopenharmony_ci 306662306a36Sopenharmony_ci num_routes = of_property_count_strings(np, propname); 306762306a36Sopenharmony_ci if (num_routes < 0 || num_routes & 1) { 306862306a36Sopenharmony_ci dev_err(card->dev, 306962306a36Sopenharmony_ci "ASoC: Property '%s' does not exist or its length is not even\n", 307062306a36Sopenharmony_ci propname); 307162306a36Sopenharmony_ci return -EINVAL; 307262306a36Sopenharmony_ci } 307362306a36Sopenharmony_ci num_routes /= 2; 307462306a36Sopenharmony_ci 307562306a36Sopenharmony_ci routes = devm_kcalloc(card->dev, num_routes, sizeof(*routes), 307662306a36Sopenharmony_ci GFP_KERNEL); 307762306a36Sopenharmony_ci if (!routes) { 307862306a36Sopenharmony_ci dev_err(card->dev, 307962306a36Sopenharmony_ci "ASoC: Could not allocate DAPM route table\n"); 308062306a36Sopenharmony_ci return -ENOMEM; 308162306a36Sopenharmony_ci } 308262306a36Sopenharmony_ci 308362306a36Sopenharmony_ci for (i = 0; i < num_routes; i++) { 308462306a36Sopenharmony_ci int ret = of_property_read_string_index(np, propname, 308562306a36Sopenharmony_ci 2 * i, &routes[i].sink); 308662306a36Sopenharmony_ci if (ret) { 308762306a36Sopenharmony_ci dev_err(card->dev, 308862306a36Sopenharmony_ci "ASoC: Property '%s' index %d could not be read: %d\n", 308962306a36Sopenharmony_ci propname, 2 * i, ret); 309062306a36Sopenharmony_ci return -EINVAL; 309162306a36Sopenharmony_ci } 309262306a36Sopenharmony_ci ret = of_property_read_string_index(np, propname, 309362306a36Sopenharmony_ci (2 * i) + 1, &routes[i].source); 309462306a36Sopenharmony_ci if (ret) { 309562306a36Sopenharmony_ci dev_err(card->dev, 309662306a36Sopenharmony_ci "ASoC: Property '%s' index %d could not be read: %d\n", 309762306a36Sopenharmony_ci propname, (2 * i) + 1, ret); 309862306a36Sopenharmony_ci return -EINVAL; 309962306a36Sopenharmony_ci } 310062306a36Sopenharmony_ci } 310162306a36Sopenharmony_ci 310262306a36Sopenharmony_ci card->num_of_dapm_routes = num_routes; 310362306a36Sopenharmony_ci card->of_dapm_routes = routes; 310462306a36Sopenharmony_ci 310562306a36Sopenharmony_ci return 0; 310662306a36Sopenharmony_ci} 310762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_routing); 310862306a36Sopenharmony_ci 310962306a36Sopenharmony_ciint snd_soc_of_parse_aux_devs(struct snd_soc_card *card, const char *propname) 311062306a36Sopenharmony_ci{ 311162306a36Sopenharmony_ci struct device_node *node = card->dev->of_node; 311262306a36Sopenharmony_ci struct snd_soc_aux_dev *aux; 311362306a36Sopenharmony_ci int num, i; 311462306a36Sopenharmony_ci 311562306a36Sopenharmony_ci num = of_count_phandle_with_args(node, propname, NULL); 311662306a36Sopenharmony_ci if (num == -ENOENT) { 311762306a36Sopenharmony_ci return 0; 311862306a36Sopenharmony_ci } else if (num < 0) { 311962306a36Sopenharmony_ci dev_err(card->dev, "ASOC: Property '%s' could not be read: %d\n", 312062306a36Sopenharmony_ci propname, num); 312162306a36Sopenharmony_ci return num; 312262306a36Sopenharmony_ci } 312362306a36Sopenharmony_ci 312462306a36Sopenharmony_ci aux = devm_kcalloc(card->dev, num, sizeof(*aux), GFP_KERNEL); 312562306a36Sopenharmony_ci if (!aux) 312662306a36Sopenharmony_ci return -ENOMEM; 312762306a36Sopenharmony_ci card->aux_dev = aux; 312862306a36Sopenharmony_ci card->num_aux_devs = num; 312962306a36Sopenharmony_ci 313062306a36Sopenharmony_ci for_each_card_pre_auxs(card, i, aux) { 313162306a36Sopenharmony_ci aux->dlc.of_node = of_parse_phandle(node, propname, i); 313262306a36Sopenharmony_ci if (!aux->dlc.of_node) 313362306a36Sopenharmony_ci return -EINVAL; 313462306a36Sopenharmony_ci } 313562306a36Sopenharmony_ci 313662306a36Sopenharmony_ci return 0; 313762306a36Sopenharmony_ci} 313862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_of_parse_aux_devs); 313962306a36Sopenharmony_ci 314062306a36Sopenharmony_ciunsigned int snd_soc_daifmt_clock_provider_flipped(unsigned int dai_fmt) 314162306a36Sopenharmony_ci{ 314262306a36Sopenharmony_ci unsigned int inv_dai_fmt = dai_fmt & ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK; 314362306a36Sopenharmony_ci 314462306a36Sopenharmony_ci switch (dai_fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { 314562306a36Sopenharmony_ci case SND_SOC_DAIFMT_CBP_CFP: 314662306a36Sopenharmony_ci inv_dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; 314762306a36Sopenharmony_ci break; 314862306a36Sopenharmony_ci case SND_SOC_DAIFMT_CBP_CFC: 314962306a36Sopenharmony_ci inv_dai_fmt |= SND_SOC_DAIFMT_CBC_CFP; 315062306a36Sopenharmony_ci break; 315162306a36Sopenharmony_ci case SND_SOC_DAIFMT_CBC_CFP: 315262306a36Sopenharmony_ci inv_dai_fmt |= SND_SOC_DAIFMT_CBP_CFC; 315362306a36Sopenharmony_ci break; 315462306a36Sopenharmony_ci case SND_SOC_DAIFMT_CBC_CFC: 315562306a36Sopenharmony_ci inv_dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; 315662306a36Sopenharmony_ci break; 315762306a36Sopenharmony_ci } 315862306a36Sopenharmony_ci 315962306a36Sopenharmony_ci return inv_dai_fmt; 316062306a36Sopenharmony_ci} 316162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_daifmt_clock_provider_flipped); 316262306a36Sopenharmony_ci 316362306a36Sopenharmony_ciunsigned int snd_soc_daifmt_clock_provider_from_bitmap(unsigned int bit_frame) 316462306a36Sopenharmony_ci{ 316562306a36Sopenharmony_ci /* 316662306a36Sopenharmony_ci * bit_frame is return value from 316762306a36Sopenharmony_ci * snd_soc_daifmt_parse_clock_provider_raw() 316862306a36Sopenharmony_ci */ 316962306a36Sopenharmony_ci 317062306a36Sopenharmony_ci /* Codec base */ 317162306a36Sopenharmony_ci switch (bit_frame) { 317262306a36Sopenharmony_ci case 0x11: 317362306a36Sopenharmony_ci return SND_SOC_DAIFMT_CBP_CFP; 317462306a36Sopenharmony_ci case 0x10: 317562306a36Sopenharmony_ci return SND_SOC_DAIFMT_CBP_CFC; 317662306a36Sopenharmony_ci case 0x01: 317762306a36Sopenharmony_ci return SND_SOC_DAIFMT_CBC_CFP; 317862306a36Sopenharmony_ci default: 317962306a36Sopenharmony_ci return SND_SOC_DAIFMT_CBC_CFC; 318062306a36Sopenharmony_ci } 318162306a36Sopenharmony_ci 318262306a36Sopenharmony_ci return 0; 318362306a36Sopenharmony_ci} 318462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_daifmt_clock_provider_from_bitmap); 318562306a36Sopenharmony_ci 318662306a36Sopenharmony_ciunsigned int snd_soc_daifmt_parse_format(struct device_node *np, 318762306a36Sopenharmony_ci const char *prefix) 318862306a36Sopenharmony_ci{ 318962306a36Sopenharmony_ci int ret; 319062306a36Sopenharmony_ci char prop[128]; 319162306a36Sopenharmony_ci unsigned int format = 0; 319262306a36Sopenharmony_ci int bit, frame; 319362306a36Sopenharmony_ci const char *str; 319462306a36Sopenharmony_ci struct { 319562306a36Sopenharmony_ci char *name; 319662306a36Sopenharmony_ci unsigned int val; 319762306a36Sopenharmony_ci } of_fmt_table[] = { 319862306a36Sopenharmony_ci { "i2s", SND_SOC_DAIFMT_I2S }, 319962306a36Sopenharmony_ci { "right_j", SND_SOC_DAIFMT_RIGHT_J }, 320062306a36Sopenharmony_ci { "left_j", SND_SOC_DAIFMT_LEFT_J }, 320162306a36Sopenharmony_ci { "dsp_a", SND_SOC_DAIFMT_DSP_A }, 320262306a36Sopenharmony_ci { "dsp_b", SND_SOC_DAIFMT_DSP_B }, 320362306a36Sopenharmony_ci { "ac97", SND_SOC_DAIFMT_AC97 }, 320462306a36Sopenharmony_ci { "pdm", SND_SOC_DAIFMT_PDM}, 320562306a36Sopenharmony_ci { "msb", SND_SOC_DAIFMT_MSB }, 320662306a36Sopenharmony_ci { "lsb", SND_SOC_DAIFMT_LSB }, 320762306a36Sopenharmony_ci }; 320862306a36Sopenharmony_ci 320962306a36Sopenharmony_ci if (!prefix) 321062306a36Sopenharmony_ci prefix = ""; 321162306a36Sopenharmony_ci 321262306a36Sopenharmony_ci /* 321362306a36Sopenharmony_ci * check "dai-format = xxx" 321462306a36Sopenharmony_ci * or "[prefix]format = xxx" 321562306a36Sopenharmony_ci * SND_SOC_DAIFMT_FORMAT_MASK area 321662306a36Sopenharmony_ci */ 321762306a36Sopenharmony_ci ret = of_property_read_string(np, "dai-format", &str); 321862306a36Sopenharmony_ci if (ret < 0) { 321962306a36Sopenharmony_ci snprintf(prop, sizeof(prop), "%sformat", prefix); 322062306a36Sopenharmony_ci ret = of_property_read_string(np, prop, &str); 322162306a36Sopenharmony_ci } 322262306a36Sopenharmony_ci if (ret == 0) { 322362306a36Sopenharmony_ci int i; 322462306a36Sopenharmony_ci 322562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(of_fmt_table); i++) { 322662306a36Sopenharmony_ci if (strcmp(str, of_fmt_table[i].name) == 0) { 322762306a36Sopenharmony_ci format |= of_fmt_table[i].val; 322862306a36Sopenharmony_ci break; 322962306a36Sopenharmony_ci } 323062306a36Sopenharmony_ci } 323162306a36Sopenharmony_ci } 323262306a36Sopenharmony_ci 323362306a36Sopenharmony_ci /* 323462306a36Sopenharmony_ci * check "[prefix]continuous-clock" 323562306a36Sopenharmony_ci * SND_SOC_DAIFMT_CLOCK_MASK area 323662306a36Sopenharmony_ci */ 323762306a36Sopenharmony_ci snprintf(prop, sizeof(prop), "%scontinuous-clock", prefix); 323862306a36Sopenharmony_ci if (of_property_read_bool(np, prop)) 323962306a36Sopenharmony_ci format |= SND_SOC_DAIFMT_CONT; 324062306a36Sopenharmony_ci else 324162306a36Sopenharmony_ci format |= SND_SOC_DAIFMT_GATED; 324262306a36Sopenharmony_ci 324362306a36Sopenharmony_ci /* 324462306a36Sopenharmony_ci * check "[prefix]bitclock-inversion" 324562306a36Sopenharmony_ci * check "[prefix]frame-inversion" 324662306a36Sopenharmony_ci * SND_SOC_DAIFMT_INV_MASK area 324762306a36Sopenharmony_ci */ 324862306a36Sopenharmony_ci snprintf(prop, sizeof(prop), "%sbitclock-inversion", prefix); 324962306a36Sopenharmony_ci bit = !!of_get_property(np, prop, NULL); 325062306a36Sopenharmony_ci 325162306a36Sopenharmony_ci snprintf(prop, sizeof(prop), "%sframe-inversion", prefix); 325262306a36Sopenharmony_ci frame = !!of_get_property(np, prop, NULL); 325362306a36Sopenharmony_ci 325462306a36Sopenharmony_ci switch ((bit << 4) + frame) { 325562306a36Sopenharmony_ci case 0x11: 325662306a36Sopenharmony_ci format |= SND_SOC_DAIFMT_IB_IF; 325762306a36Sopenharmony_ci break; 325862306a36Sopenharmony_ci case 0x10: 325962306a36Sopenharmony_ci format |= SND_SOC_DAIFMT_IB_NF; 326062306a36Sopenharmony_ci break; 326162306a36Sopenharmony_ci case 0x01: 326262306a36Sopenharmony_ci format |= SND_SOC_DAIFMT_NB_IF; 326362306a36Sopenharmony_ci break; 326462306a36Sopenharmony_ci default: 326562306a36Sopenharmony_ci /* SND_SOC_DAIFMT_NB_NF is default */ 326662306a36Sopenharmony_ci break; 326762306a36Sopenharmony_ci } 326862306a36Sopenharmony_ci 326962306a36Sopenharmony_ci return format; 327062306a36Sopenharmony_ci} 327162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_daifmt_parse_format); 327262306a36Sopenharmony_ci 327362306a36Sopenharmony_ciunsigned int snd_soc_daifmt_parse_clock_provider_raw(struct device_node *np, 327462306a36Sopenharmony_ci const char *prefix, 327562306a36Sopenharmony_ci struct device_node **bitclkmaster, 327662306a36Sopenharmony_ci struct device_node **framemaster) 327762306a36Sopenharmony_ci{ 327862306a36Sopenharmony_ci char prop[128]; 327962306a36Sopenharmony_ci unsigned int bit, frame; 328062306a36Sopenharmony_ci 328162306a36Sopenharmony_ci if (!prefix) 328262306a36Sopenharmony_ci prefix = ""; 328362306a36Sopenharmony_ci 328462306a36Sopenharmony_ci /* 328562306a36Sopenharmony_ci * check "[prefix]bitclock-master" 328662306a36Sopenharmony_ci * check "[prefix]frame-master" 328762306a36Sopenharmony_ci */ 328862306a36Sopenharmony_ci snprintf(prop, sizeof(prop), "%sbitclock-master", prefix); 328962306a36Sopenharmony_ci bit = !!of_get_property(np, prop, NULL); 329062306a36Sopenharmony_ci if (bit && bitclkmaster) 329162306a36Sopenharmony_ci *bitclkmaster = of_parse_phandle(np, prop, 0); 329262306a36Sopenharmony_ci 329362306a36Sopenharmony_ci snprintf(prop, sizeof(prop), "%sframe-master", prefix); 329462306a36Sopenharmony_ci frame = !!of_get_property(np, prop, NULL); 329562306a36Sopenharmony_ci if (frame && framemaster) 329662306a36Sopenharmony_ci *framemaster = of_parse_phandle(np, prop, 0); 329762306a36Sopenharmony_ci 329862306a36Sopenharmony_ci /* 329962306a36Sopenharmony_ci * return bitmap. 330062306a36Sopenharmony_ci * It will be parameter of 330162306a36Sopenharmony_ci * snd_soc_daifmt_clock_provider_from_bitmap() 330262306a36Sopenharmony_ci */ 330362306a36Sopenharmony_ci return (bit << 4) + frame; 330462306a36Sopenharmony_ci} 330562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_daifmt_parse_clock_provider_raw); 330662306a36Sopenharmony_ci 330762306a36Sopenharmony_ciint snd_soc_get_stream_cpu(struct snd_soc_dai_link *dai_link, int stream) 330862306a36Sopenharmony_ci{ 330962306a36Sopenharmony_ci /* 331062306a36Sopenharmony_ci * [Normal] 331162306a36Sopenharmony_ci * 331262306a36Sopenharmony_ci * Playback 331362306a36Sopenharmony_ci * CPU : SNDRV_PCM_STREAM_PLAYBACK 331462306a36Sopenharmony_ci * Codec: SNDRV_PCM_STREAM_PLAYBACK 331562306a36Sopenharmony_ci * 331662306a36Sopenharmony_ci * Capture 331762306a36Sopenharmony_ci * CPU : SNDRV_PCM_STREAM_CAPTURE 331862306a36Sopenharmony_ci * Codec: SNDRV_PCM_STREAM_CAPTURE 331962306a36Sopenharmony_ci */ 332062306a36Sopenharmony_ci if (!dai_link->c2c_params) 332162306a36Sopenharmony_ci return stream; 332262306a36Sopenharmony_ci 332362306a36Sopenharmony_ci /* 332462306a36Sopenharmony_ci * [Codec2Codec] 332562306a36Sopenharmony_ci * 332662306a36Sopenharmony_ci * Playback 332762306a36Sopenharmony_ci * CPU : SNDRV_PCM_STREAM_CAPTURE 332862306a36Sopenharmony_ci * Codec: SNDRV_PCM_STREAM_PLAYBACK 332962306a36Sopenharmony_ci * 333062306a36Sopenharmony_ci * Capture 333162306a36Sopenharmony_ci * CPU : SNDRV_PCM_STREAM_PLAYBACK 333262306a36Sopenharmony_ci * Codec: SNDRV_PCM_STREAM_CAPTURE 333362306a36Sopenharmony_ci */ 333462306a36Sopenharmony_ci if (stream == SNDRV_PCM_STREAM_CAPTURE) 333562306a36Sopenharmony_ci return SNDRV_PCM_STREAM_PLAYBACK; 333662306a36Sopenharmony_ci 333762306a36Sopenharmony_ci return SNDRV_PCM_STREAM_CAPTURE; 333862306a36Sopenharmony_ci} 333962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_get_stream_cpu); 334062306a36Sopenharmony_ci 334162306a36Sopenharmony_ciint snd_soc_get_dai_id(struct device_node *ep) 334262306a36Sopenharmony_ci{ 334362306a36Sopenharmony_ci struct snd_soc_component *component; 334462306a36Sopenharmony_ci struct snd_soc_dai_link_component dlc = { 334562306a36Sopenharmony_ci .of_node = of_graph_get_port_parent(ep), 334662306a36Sopenharmony_ci }; 334762306a36Sopenharmony_ci int ret; 334862306a36Sopenharmony_ci 334962306a36Sopenharmony_ci 335062306a36Sopenharmony_ci /* 335162306a36Sopenharmony_ci * For example HDMI case, HDMI has video/sound port, 335262306a36Sopenharmony_ci * but ALSA SoC needs sound port number only. 335362306a36Sopenharmony_ci * Thus counting HDMI DT port/endpoint doesn't work. 335462306a36Sopenharmony_ci * Then, it should have .of_xlate_dai_id 335562306a36Sopenharmony_ci */ 335662306a36Sopenharmony_ci ret = -ENOTSUPP; 335762306a36Sopenharmony_ci mutex_lock(&client_mutex); 335862306a36Sopenharmony_ci component = soc_find_component(&dlc); 335962306a36Sopenharmony_ci if (component) 336062306a36Sopenharmony_ci ret = snd_soc_component_of_xlate_dai_id(component, ep); 336162306a36Sopenharmony_ci mutex_unlock(&client_mutex); 336262306a36Sopenharmony_ci 336362306a36Sopenharmony_ci of_node_put(dlc.of_node); 336462306a36Sopenharmony_ci 336562306a36Sopenharmony_ci return ret; 336662306a36Sopenharmony_ci} 336762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_get_dai_id); 336862306a36Sopenharmony_ci 336962306a36Sopenharmony_ciint snd_soc_get_dlc(const struct of_phandle_args *args, struct snd_soc_dai_link_component *dlc) 337062306a36Sopenharmony_ci{ 337162306a36Sopenharmony_ci struct snd_soc_component *pos; 337262306a36Sopenharmony_ci int ret = -EPROBE_DEFER; 337362306a36Sopenharmony_ci 337462306a36Sopenharmony_ci mutex_lock(&client_mutex); 337562306a36Sopenharmony_ci for_each_component(pos) { 337662306a36Sopenharmony_ci struct device_node *component_of_node = soc_component_to_node(pos); 337762306a36Sopenharmony_ci 337862306a36Sopenharmony_ci if (component_of_node != args->np || !pos->num_dai) 337962306a36Sopenharmony_ci continue; 338062306a36Sopenharmony_ci 338162306a36Sopenharmony_ci ret = snd_soc_component_of_xlate_dai_name(pos, args, &dlc->dai_name); 338262306a36Sopenharmony_ci if (ret == -ENOTSUPP) { 338362306a36Sopenharmony_ci struct snd_soc_dai *dai; 338462306a36Sopenharmony_ci int id = -1; 338562306a36Sopenharmony_ci 338662306a36Sopenharmony_ci switch (args->args_count) { 338762306a36Sopenharmony_ci case 0: 338862306a36Sopenharmony_ci id = 0; /* same as dai_drv[0] */ 338962306a36Sopenharmony_ci break; 339062306a36Sopenharmony_ci case 1: 339162306a36Sopenharmony_ci id = args->args[0]; 339262306a36Sopenharmony_ci break; 339362306a36Sopenharmony_ci default: 339462306a36Sopenharmony_ci /* not supported */ 339562306a36Sopenharmony_ci break; 339662306a36Sopenharmony_ci } 339762306a36Sopenharmony_ci 339862306a36Sopenharmony_ci if (id < 0 || id >= pos->num_dai) { 339962306a36Sopenharmony_ci ret = -EINVAL; 340062306a36Sopenharmony_ci continue; 340162306a36Sopenharmony_ci } 340262306a36Sopenharmony_ci 340362306a36Sopenharmony_ci ret = 0; 340462306a36Sopenharmony_ci 340562306a36Sopenharmony_ci /* find target DAI */ 340662306a36Sopenharmony_ci for_each_component_dais(pos, dai) { 340762306a36Sopenharmony_ci if (id == 0) 340862306a36Sopenharmony_ci break; 340962306a36Sopenharmony_ci id--; 341062306a36Sopenharmony_ci } 341162306a36Sopenharmony_ci 341262306a36Sopenharmony_ci dlc->dai_name = snd_soc_dai_name_get(dai); 341362306a36Sopenharmony_ci } else if (ret) { 341462306a36Sopenharmony_ci /* 341562306a36Sopenharmony_ci * if another error than ENOTSUPP is returned go on and 341662306a36Sopenharmony_ci * check if another component is provided with the same 341762306a36Sopenharmony_ci * node. This may happen if a device provides several 341862306a36Sopenharmony_ci * components 341962306a36Sopenharmony_ci */ 342062306a36Sopenharmony_ci continue; 342162306a36Sopenharmony_ci } 342262306a36Sopenharmony_ci 342362306a36Sopenharmony_ci break; 342462306a36Sopenharmony_ci } 342562306a36Sopenharmony_ci 342662306a36Sopenharmony_ci if (ret == 0) 342762306a36Sopenharmony_ci dlc->of_node = args->np; 342862306a36Sopenharmony_ci 342962306a36Sopenharmony_ci mutex_unlock(&client_mutex); 343062306a36Sopenharmony_ci return ret; 343162306a36Sopenharmony_ci} 343262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_get_dlc); 343362306a36Sopenharmony_ci 343462306a36Sopenharmony_ciint snd_soc_of_get_dlc(struct device_node *of_node, 343562306a36Sopenharmony_ci struct of_phandle_args *args, 343662306a36Sopenharmony_ci struct snd_soc_dai_link_component *dlc, 343762306a36Sopenharmony_ci int index) 343862306a36Sopenharmony_ci{ 343962306a36Sopenharmony_ci struct of_phandle_args __args; 344062306a36Sopenharmony_ci int ret; 344162306a36Sopenharmony_ci 344262306a36Sopenharmony_ci if (!args) 344362306a36Sopenharmony_ci args = &__args; 344462306a36Sopenharmony_ci 344562306a36Sopenharmony_ci ret = of_parse_phandle_with_args(of_node, "sound-dai", 344662306a36Sopenharmony_ci "#sound-dai-cells", index, args); 344762306a36Sopenharmony_ci if (ret) 344862306a36Sopenharmony_ci return ret; 344962306a36Sopenharmony_ci 345062306a36Sopenharmony_ci return snd_soc_get_dlc(args, dlc); 345162306a36Sopenharmony_ci} 345262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_of_get_dlc); 345362306a36Sopenharmony_ci 345462306a36Sopenharmony_ciint snd_soc_get_dai_name(const struct of_phandle_args *args, 345562306a36Sopenharmony_ci const char **dai_name) 345662306a36Sopenharmony_ci{ 345762306a36Sopenharmony_ci struct snd_soc_dai_link_component dlc; 345862306a36Sopenharmony_ci int ret = snd_soc_get_dlc(args, &dlc); 345962306a36Sopenharmony_ci 346062306a36Sopenharmony_ci if (ret == 0) 346162306a36Sopenharmony_ci *dai_name = dlc.dai_name; 346262306a36Sopenharmony_ci 346362306a36Sopenharmony_ci return ret; 346462306a36Sopenharmony_ci} 346562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_get_dai_name); 346662306a36Sopenharmony_ci 346762306a36Sopenharmony_ciint snd_soc_of_get_dai_name(struct device_node *of_node, 346862306a36Sopenharmony_ci const char **dai_name, int index) 346962306a36Sopenharmony_ci{ 347062306a36Sopenharmony_ci struct snd_soc_dai_link_component dlc; 347162306a36Sopenharmony_ci int ret = snd_soc_of_get_dlc(of_node, NULL, &dlc, index); 347262306a36Sopenharmony_ci 347362306a36Sopenharmony_ci if (ret == 0) 347462306a36Sopenharmony_ci *dai_name = dlc.dai_name; 347562306a36Sopenharmony_ci 347662306a36Sopenharmony_ci return ret; 347762306a36Sopenharmony_ci} 347862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_of_get_dai_name); 347962306a36Sopenharmony_ci 348062306a36Sopenharmony_cistruct snd_soc_dai *snd_soc_get_dai_via_args(struct of_phandle_args *dai_args) 348162306a36Sopenharmony_ci{ 348262306a36Sopenharmony_ci struct snd_soc_dai *dai; 348362306a36Sopenharmony_ci struct snd_soc_component *component; 348462306a36Sopenharmony_ci 348562306a36Sopenharmony_ci mutex_lock(&client_mutex); 348662306a36Sopenharmony_ci for_each_component(component) { 348762306a36Sopenharmony_ci for_each_component_dais(component, dai) 348862306a36Sopenharmony_ci if (snd_soc_is_match_dai_args(dai->driver->dai_args, dai_args)) 348962306a36Sopenharmony_ci goto found; 349062306a36Sopenharmony_ci } 349162306a36Sopenharmony_ci dai = NULL; 349262306a36Sopenharmony_cifound: 349362306a36Sopenharmony_ci mutex_unlock(&client_mutex); 349462306a36Sopenharmony_ci return dai; 349562306a36Sopenharmony_ci} 349662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_get_dai_via_args); 349762306a36Sopenharmony_ci 349862306a36Sopenharmony_cistatic void __snd_soc_of_put_component(struct snd_soc_dai_link_component *component) 349962306a36Sopenharmony_ci{ 350062306a36Sopenharmony_ci if (component->of_node) { 350162306a36Sopenharmony_ci of_node_put(component->of_node); 350262306a36Sopenharmony_ci component->of_node = NULL; 350362306a36Sopenharmony_ci } 350462306a36Sopenharmony_ci} 350562306a36Sopenharmony_ci 350662306a36Sopenharmony_cistatic int __snd_soc_of_get_dai_link_component_alloc( 350762306a36Sopenharmony_ci struct device *dev, struct device_node *of_node, 350862306a36Sopenharmony_ci struct snd_soc_dai_link_component **ret_component, 350962306a36Sopenharmony_ci int *ret_num) 351062306a36Sopenharmony_ci{ 351162306a36Sopenharmony_ci struct snd_soc_dai_link_component *component; 351262306a36Sopenharmony_ci int num; 351362306a36Sopenharmony_ci 351462306a36Sopenharmony_ci /* Count the number of CPUs/CODECs */ 351562306a36Sopenharmony_ci num = of_count_phandle_with_args(of_node, "sound-dai", "#sound-dai-cells"); 351662306a36Sopenharmony_ci if (num <= 0) { 351762306a36Sopenharmony_ci if (num == -ENOENT) 351862306a36Sopenharmony_ci dev_err(dev, "No 'sound-dai' property\n"); 351962306a36Sopenharmony_ci else 352062306a36Sopenharmony_ci dev_err(dev, "Bad phandle in 'sound-dai'\n"); 352162306a36Sopenharmony_ci return num; 352262306a36Sopenharmony_ci } 352362306a36Sopenharmony_ci component = devm_kcalloc(dev, num, sizeof(*component), GFP_KERNEL); 352462306a36Sopenharmony_ci if (!component) 352562306a36Sopenharmony_ci return -ENOMEM; 352662306a36Sopenharmony_ci 352762306a36Sopenharmony_ci *ret_component = component; 352862306a36Sopenharmony_ci *ret_num = num; 352962306a36Sopenharmony_ci 353062306a36Sopenharmony_ci return 0; 353162306a36Sopenharmony_ci} 353262306a36Sopenharmony_ci 353362306a36Sopenharmony_ci/* 353462306a36Sopenharmony_ci * snd_soc_of_put_dai_link_codecs - Dereference device nodes in the codecs array 353562306a36Sopenharmony_ci * @dai_link: DAI link 353662306a36Sopenharmony_ci * 353762306a36Sopenharmony_ci * Dereference device nodes acquired by snd_soc_of_get_dai_link_codecs(). 353862306a36Sopenharmony_ci */ 353962306a36Sopenharmony_civoid snd_soc_of_put_dai_link_codecs(struct snd_soc_dai_link *dai_link) 354062306a36Sopenharmony_ci{ 354162306a36Sopenharmony_ci struct snd_soc_dai_link_component *component; 354262306a36Sopenharmony_ci int index; 354362306a36Sopenharmony_ci 354462306a36Sopenharmony_ci for_each_link_codecs(dai_link, index, component) 354562306a36Sopenharmony_ci __snd_soc_of_put_component(component); 354662306a36Sopenharmony_ci} 354762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_of_put_dai_link_codecs); 354862306a36Sopenharmony_ci 354962306a36Sopenharmony_ci/* 355062306a36Sopenharmony_ci * snd_soc_of_get_dai_link_codecs - Parse a list of CODECs in the devicetree 355162306a36Sopenharmony_ci * @dev: Card device 355262306a36Sopenharmony_ci * @of_node: Device node 355362306a36Sopenharmony_ci * @dai_link: DAI link 355462306a36Sopenharmony_ci * 355562306a36Sopenharmony_ci * Builds an array of CODEC DAI components from the DAI link property 355662306a36Sopenharmony_ci * 'sound-dai'. 355762306a36Sopenharmony_ci * The array is set in the DAI link and the number of DAIs is set accordingly. 355862306a36Sopenharmony_ci * The device nodes in the array (of_node) must be dereferenced by calling 355962306a36Sopenharmony_ci * snd_soc_of_put_dai_link_codecs() on @dai_link. 356062306a36Sopenharmony_ci * 356162306a36Sopenharmony_ci * Returns 0 for success 356262306a36Sopenharmony_ci */ 356362306a36Sopenharmony_ciint snd_soc_of_get_dai_link_codecs(struct device *dev, 356462306a36Sopenharmony_ci struct device_node *of_node, 356562306a36Sopenharmony_ci struct snd_soc_dai_link *dai_link) 356662306a36Sopenharmony_ci{ 356762306a36Sopenharmony_ci struct snd_soc_dai_link_component *component; 356862306a36Sopenharmony_ci int index, ret; 356962306a36Sopenharmony_ci 357062306a36Sopenharmony_ci ret = __snd_soc_of_get_dai_link_component_alloc(dev, of_node, 357162306a36Sopenharmony_ci &dai_link->codecs, &dai_link->num_codecs); 357262306a36Sopenharmony_ci if (ret < 0) 357362306a36Sopenharmony_ci return ret; 357462306a36Sopenharmony_ci 357562306a36Sopenharmony_ci /* Parse the list */ 357662306a36Sopenharmony_ci for_each_link_codecs(dai_link, index, component) { 357762306a36Sopenharmony_ci ret = snd_soc_of_get_dlc(of_node, NULL, component, index); 357862306a36Sopenharmony_ci if (ret) 357962306a36Sopenharmony_ci goto err; 358062306a36Sopenharmony_ci } 358162306a36Sopenharmony_ci return 0; 358262306a36Sopenharmony_cierr: 358362306a36Sopenharmony_ci snd_soc_of_put_dai_link_codecs(dai_link); 358462306a36Sopenharmony_ci dai_link->codecs = NULL; 358562306a36Sopenharmony_ci dai_link->num_codecs = 0; 358662306a36Sopenharmony_ci return ret; 358762306a36Sopenharmony_ci} 358862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_of_get_dai_link_codecs); 358962306a36Sopenharmony_ci 359062306a36Sopenharmony_ci/* 359162306a36Sopenharmony_ci * snd_soc_of_put_dai_link_cpus - Dereference device nodes in the codecs array 359262306a36Sopenharmony_ci * @dai_link: DAI link 359362306a36Sopenharmony_ci * 359462306a36Sopenharmony_ci * Dereference device nodes acquired by snd_soc_of_get_dai_link_cpus(). 359562306a36Sopenharmony_ci */ 359662306a36Sopenharmony_civoid snd_soc_of_put_dai_link_cpus(struct snd_soc_dai_link *dai_link) 359762306a36Sopenharmony_ci{ 359862306a36Sopenharmony_ci struct snd_soc_dai_link_component *component; 359962306a36Sopenharmony_ci int index; 360062306a36Sopenharmony_ci 360162306a36Sopenharmony_ci for_each_link_cpus(dai_link, index, component) 360262306a36Sopenharmony_ci __snd_soc_of_put_component(component); 360362306a36Sopenharmony_ci} 360462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_of_put_dai_link_cpus); 360562306a36Sopenharmony_ci 360662306a36Sopenharmony_ci/* 360762306a36Sopenharmony_ci * snd_soc_of_get_dai_link_cpus - Parse a list of CPU DAIs in the devicetree 360862306a36Sopenharmony_ci * @dev: Card device 360962306a36Sopenharmony_ci * @of_node: Device node 361062306a36Sopenharmony_ci * @dai_link: DAI link 361162306a36Sopenharmony_ci * 361262306a36Sopenharmony_ci * Is analogous to snd_soc_of_get_dai_link_codecs but parses a list of CPU DAIs 361362306a36Sopenharmony_ci * instead. 361462306a36Sopenharmony_ci * 361562306a36Sopenharmony_ci * Returns 0 for success 361662306a36Sopenharmony_ci */ 361762306a36Sopenharmony_ciint snd_soc_of_get_dai_link_cpus(struct device *dev, 361862306a36Sopenharmony_ci struct device_node *of_node, 361962306a36Sopenharmony_ci struct snd_soc_dai_link *dai_link) 362062306a36Sopenharmony_ci{ 362162306a36Sopenharmony_ci struct snd_soc_dai_link_component *component; 362262306a36Sopenharmony_ci int index, ret; 362362306a36Sopenharmony_ci 362462306a36Sopenharmony_ci /* Count the number of CPUs */ 362562306a36Sopenharmony_ci ret = __snd_soc_of_get_dai_link_component_alloc(dev, of_node, 362662306a36Sopenharmony_ci &dai_link->cpus, &dai_link->num_cpus); 362762306a36Sopenharmony_ci if (ret < 0) 362862306a36Sopenharmony_ci return ret; 362962306a36Sopenharmony_ci 363062306a36Sopenharmony_ci /* Parse the list */ 363162306a36Sopenharmony_ci for_each_link_cpus(dai_link, index, component) { 363262306a36Sopenharmony_ci ret = snd_soc_of_get_dlc(of_node, NULL, component, index); 363362306a36Sopenharmony_ci if (ret) 363462306a36Sopenharmony_ci goto err; 363562306a36Sopenharmony_ci } 363662306a36Sopenharmony_ci return 0; 363762306a36Sopenharmony_cierr: 363862306a36Sopenharmony_ci snd_soc_of_put_dai_link_cpus(dai_link); 363962306a36Sopenharmony_ci dai_link->cpus = NULL; 364062306a36Sopenharmony_ci dai_link->num_cpus = 0; 364162306a36Sopenharmony_ci return ret; 364262306a36Sopenharmony_ci} 364362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_of_get_dai_link_cpus); 364462306a36Sopenharmony_ci 364562306a36Sopenharmony_cistatic int __init snd_soc_init(void) 364662306a36Sopenharmony_ci{ 364762306a36Sopenharmony_ci int ret; 364862306a36Sopenharmony_ci 364962306a36Sopenharmony_ci snd_soc_debugfs_init(); 365062306a36Sopenharmony_ci ret = snd_soc_util_init(); 365162306a36Sopenharmony_ci if (ret) 365262306a36Sopenharmony_ci goto err_util_init; 365362306a36Sopenharmony_ci 365462306a36Sopenharmony_ci ret = platform_driver_register(&soc_driver); 365562306a36Sopenharmony_ci if (ret) 365662306a36Sopenharmony_ci goto err_register; 365762306a36Sopenharmony_ci return 0; 365862306a36Sopenharmony_ci 365962306a36Sopenharmony_cierr_register: 366062306a36Sopenharmony_ci snd_soc_util_exit(); 366162306a36Sopenharmony_cierr_util_init: 366262306a36Sopenharmony_ci snd_soc_debugfs_exit(); 366362306a36Sopenharmony_ci return ret; 366462306a36Sopenharmony_ci} 366562306a36Sopenharmony_cimodule_init(snd_soc_init); 366662306a36Sopenharmony_ci 366762306a36Sopenharmony_cistatic void __exit snd_soc_exit(void) 366862306a36Sopenharmony_ci{ 366962306a36Sopenharmony_ci snd_soc_util_exit(); 367062306a36Sopenharmony_ci snd_soc_debugfs_exit(); 367162306a36Sopenharmony_ci 367262306a36Sopenharmony_ci platform_driver_unregister(&soc_driver); 367362306a36Sopenharmony_ci} 367462306a36Sopenharmony_cimodule_exit(snd_soc_exit); 367562306a36Sopenharmony_ci 367662306a36Sopenharmony_ci/* Module information */ 367762306a36Sopenharmony_ciMODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk"); 367862306a36Sopenharmony_ciMODULE_DESCRIPTION("ALSA SoC Core"); 367962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 368062306a36Sopenharmony_ciMODULE_ALIAS("platform:soc-audio"); 3681