1873486edSKuninori Morimoto // SPDX-License-Identifier: GPL-2.0+ 2873486edSKuninori Morimoto // 3873486edSKuninori Morimoto // soc-core.c -- ALSA SoC Audio Layer 4873486edSKuninori Morimoto // 5873486edSKuninori Morimoto // Copyright 2005 Wolfson Microelectronics PLC. 6873486edSKuninori Morimoto // Copyright 2005 Openedhand Ltd. 7873486edSKuninori Morimoto // Copyright (C) 2010 Slimlogic Ltd. 8873486edSKuninori Morimoto // Copyright (C) 2010 Texas Instruments Inc. 9873486edSKuninori Morimoto // 10873486edSKuninori Morimoto // Author: Liam Girdwood <lrg@slimlogic.co.uk> 11873486edSKuninori Morimoto // with code, comments and ideas from :- 12873486edSKuninori Morimoto // Richard Purdie <richard@openedhand.com> 13873486edSKuninori Morimoto // 14873486edSKuninori Morimoto // TODO: 15873486edSKuninori Morimoto // o Add hw rules to enforce rates, etc. 16873486edSKuninori Morimoto // o More testing with other codecs/machines. 17873486edSKuninori Morimoto // o Add more codecs and platforms to ensure good API coverage. 18873486edSKuninori Morimoto // o Support TDM on PCM and I2S 19db2a4165SFrank Mandarino 20db2a4165SFrank Mandarino #include <linux/module.h> 21db2a4165SFrank Mandarino #include <linux/moduleparam.h> 22db2a4165SFrank Mandarino #include <linux/init.h> 23db2a4165SFrank Mandarino #include <linux/delay.h> 24db2a4165SFrank Mandarino #include <linux/pm.h> 25db2a4165SFrank Mandarino #include <linux/bitops.h> 2612ef193dSTroy Kisky #include <linux/debugfs.h> 27db2a4165SFrank Mandarino #include <linux/platform_device.h> 28741a509fSMarkus Pargmann #include <linux/pinctrl/consumer.h> 29f0e8ed85SMark Brown #include <linux/ctype.h> 305a0e3ad6STejun Heo #include <linux/slab.h> 31bec4fa05SStephen Warren #include <linux/of.h> 32a180e8b9SKuninori Morimoto #include <linux/of_graph.h> 33345233d7SLiam Girdwood #include <linux/dmi.h> 34db2a4165SFrank Mandarino #include <sound/core.h> 353028eb8cSMark Brown #include <sound/jack.h> 36db2a4165SFrank Mandarino #include <sound/pcm.h> 37db2a4165SFrank Mandarino #include <sound/pcm_params.h> 38db2a4165SFrank Mandarino #include <sound/soc.h> 3901d7584cSLiam Girdwood #include <sound/soc-dpcm.h> 408a978234SLiam Girdwood #include <sound/soc-topology.h> 41db2a4165SFrank Mandarino #include <sound/initval.h> 42db2a4165SFrank Mandarino 43a8b1d34fSMark Brown #define CREATE_TRACE_POINTS 44a8b1d34fSMark Brown #include <trace/events/asoc.h> 45a8b1d34fSMark Brown 46f0fba2adSLiam Girdwood #define NAME_SIZE 32 47f0fba2adSLiam Girdwood 48384c89e2SMark Brown #ifdef CONFIG_DEBUG_FS 498a9dab1aSMark Brown struct dentry *snd_soc_debugfs_root; 508a9dab1aSMark Brown EXPORT_SYMBOL_GPL(snd_soc_debugfs_root); 51384c89e2SMark Brown #endif 52384c89e2SMark Brown 53c5af3a2eSMark Brown static DEFINE_MUTEX(client_mutex); 54030e79f6SKuninori Morimoto static LIST_HEAD(component_list); 55c5af3a2eSMark Brown 56db2a4165SFrank Mandarino /* 57db2a4165SFrank Mandarino * This is a timeout to do a DAPM powerdown after a stream is closed(). 58db2a4165SFrank Mandarino * It can be used to eliminate pops between different playback streams, e.g. 59db2a4165SFrank Mandarino * between two audio tracks. 60db2a4165SFrank Mandarino */ 61db2a4165SFrank Mandarino static int pmdown_time = 5000; 62db2a4165SFrank Mandarino module_param(pmdown_time, int, 0); 63db2a4165SFrank Mandarino MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)"); 64db2a4165SFrank Mandarino 6598faf436SMengdong Lin /* If a DMI filed contain strings in this blacklist (e.g. 6698faf436SMengdong Lin * "Type2 - Board Manufacturer" or "Type1 - TBD by OEM"), it will be taken 6798faf436SMengdong Lin * as invalid and dropped when setting the card long name from DMI info. 6898faf436SMengdong Lin */ 6998faf436SMengdong Lin static const char * const dmi_blacklist[] = { 7098faf436SMengdong Lin "To be filled by OEM", 7198faf436SMengdong Lin "TBD by OEM", 7298faf436SMengdong Lin "Default String", 7398faf436SMengdong Lin "Board Manufacturer", 7498faf436SMengdong Lin "Board Vendor Name", 7598faf436SMengdong Lin "Board Product Name", 7698faf436SMengdong Lin NULL, /* terminator */ 7798faf436SMengdong Lin }; 7898faf436SMengdong Lin 79dbe21408SMark Brown static ssize_t pmdown_time_show(struct device *dev, 80dbe21408SMark Brown struct device_attribute *attr, char *buf) 81dbe21408SMark Brown { 8236ae1a96SMark Brown struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev); 83dbe21408SMark Brown 84f0fba2adSLiam Girdwood return sprintf(buf, "%ld\n", rtd->pmdown_time); 85dbe21408SMark Brown } 86dbe21408SMark Brown 87dbe21408SMark Brown static ssize_t pmdown_time_set(struct device *dev, 88dbe21408SMark Brown struct device_attribute *attr, 89dbe21408SMark Brown const char *buf, size_t count) 90dbe21408SMark Brown { 9136ae1a96SMark Brown struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev); 92c593b520SMark Brown int ret; 93dbe21408SMark Brown 94b785a492SJingoo Han ret = kstrtol(buf, 10, &rtd->pmdown_time); 95c593b520SMark Brown if (ret) 96c593b520SMark Brown return ret; 97dbe21408SMark Brown 98dbe21408SMark Brown return count; 99dbe21408SMark Brown } 100dbe21408SMark Brown 101dbe21408SMark Brown static DEVICE_ATTR(pmdown_time, 0644, pmdown_time_show, pmdown_time_set); 102dbe21408SMark Brown 103d29697dcSTakashi Iwai static struct attribute *soc_dev_attrs[] = { 104d29697dcSTakashi Iwai &dev_attr_pmdown_time.attr, 105d29697dcSTakashi Iwai NULL 106d29697dcSTakashi Iwai }; 107d29697dcSTakashi Iwai 108d29697dcSTakashi Iwai static umode_t soc_dev_attr_is_visible(struct kobject *kobj, 109d29697dcSTakashi Iwai struct attribute *attr, int idx) 110d29697dcSTakashi Iwai { 111d29697dcSTakashi Iwai struct device *dev = kobj_to_dev(kobj); 112d29697dcSTakashi Iwai struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev); 113d29697dcSTakashi Iwai 114d29697dcSTakashi Iwai if (attr == &dev_attr_pmdown_time.attr) 115d29697dcSTakashi Iwai return attr->mode; /* always visible */ 1163b6eed8dSKuninori Morimoto return rtd->num_codecs ? attr->mode : 0; /* enabled only with codec */ 117d29697dcSTakashi Iwai } 118d29697dcSTakashi Iwai 119d29697dcSTakashi Iwai static const struct attribute_group soc_dapm_dev_group = { 120d29697dcSTakashi Iwai .attrs = soc_dapm_dev_attrs, 121d29697dcSTakashi Iwai .is_visible = soc_dev_attr_is_visible, 122d29697dcSTakashi Iwai }; 123d29697dcSTakashi Iwai 124f7e73b26SMark Brown static const struct attribute_group soc_dev_group = { 125d29697dcSTakashi Iwai .attrs = soc_dev_attrs, 126d29697dcSTakashi Iwai .is_visible = soc_dev_attr_is_visible, 127d29697dcSTakashi Iwai }; 128d29697dcSTakashi Iwai 129d29697dcSTakashi Iwai static const struct attribute_group *soc_dev_attr_groups[] = { 130d29697dcSTakashi Iwai &soc_dapm_dev_group, 131f7e73b26SMark Brown &soc_dev_group, 132d29697dcSTakashi Iwai NULL 133d29697dcSTakashi Iwai }; 134d29697dcSTakashi Iwai 1352624d5faSMark Brown #ifdef CONFIG_DEBUG_FS 13681c7cfd1SLars-Peter Clausen static void soc_init_component_debugfs(struct snd_soc_component *component) 137e73f3de5SRussell King { 1386553bf06SLars-Peter Clausen if (!component->card->debugfs_card_root) 1396553bf06SLars-Peter Clausen return; 1406553bf06SLars-Peter Clausen 14181c7cfd1SLars-Peter Clausen if (component->debugfs_prefix) { 14281c7cfd1SLars-Peter Clausen char *name; 143e73f3de5SRussell King 14481c7cfd1SLars-Peter Clausen name = kasprintf(GFP_KERNEL, "%s:%s", 14581c7cfd1SLars-Peter Clausen component->debugfs_prefix, component->name); 14681c7cfd1SLars-Peter Clausen if (name) { 14781c7cfd1SLars-Peter Clausen component->debugfs_root = debugfs_create_dir(name, 14881c7cfd1SLars-Peter Clausen component->card->debugfs_card_root); 14981c7cfd1SLars-Peter Clausen kfree(name); 15081c7cfd1SLars-Peter Clausen } 15181c7cfd1SLars-Peter Clausen } else { 15281c7cfd1SLars-Peter Clausen component->debugfs_root = debugfs_create_dir(component->name, 15381c7cfd1SLars-Peter Clausen component->card->debugfs_card_root); 154e73f3de5SRussell King } 155e73f3de5SRussell King 15681c7cfd1SLars-Peter Clausen if (!component->debugfs_root) { 15781c7cfd1SLars-Peter Clausen dev_warn(component->dev, 15881c7cfd1SLars-Peter Clausen "ASoC: Failed to create component debugfs directory\n"); 1592624d5faSMark Brown return; 1602624d5faSMark Brown } 1612624d5faSMark Brown 16281c7cfd1SLars-Peter Clausen snd_soc_dapm_debugfs_init(snd_soc_component_get_dapm(component), 16381c7cfd1SLars-Peter Clausen component->debugfs_root); 16481c7cfd1SLars-Peter Clausen } 16581c7cfd1SLars-Peter Clausen 16681c7cfd1SLars-Peter Clausen static void soc_cleanup_component_debugfs(struct snd_soc_component *component) 16781c7cfd1SLars-Peter Clausen { 16881c7cfd1SLars-Peter Clausen debugfs_remove_recursive(component->debugfs_root); 16981c7cfd1SLars-Peter Clausen } 17081c7cfd1SLars-Peter Clausen 171c15b2a1dSPeng Donglin static int dai_list_show(struct seq_file *m, void *v) 172f3208780SMark Brown { 1731438c2f6SLars-Peter Clausen struct snd_soc_component *component; 174f3208780SMark Brown struct snd_soc_dai *dai; 175f3208780SMark Brown 17634e81ab4SLars-Peter Clausen mutex_lock(&client_mutex); 17734e81ab4SLars-Peter Clausen 178700c17caSDonglin Peng list_for_each_entry(component, &component_list, list) 179700c17caSDonglin Peng list_for_each_entry(dai, &component->dai_list, list) 180700c17caSDonglin Peng seq_printf(m, "%s\n", dai->name); 181f3208780SMark Brown 18234e81ab4SLars-Peter Clausen mutex_unlock(&client_mutex); 18334e81ab4SLars-Peter Clausen 184700c17caSDonglin Peng return 0; 185700c17caSDonglin Peng } 186c15b2a1dSPeng Donglin DEFINE_SHOW_ATTRIBUTE(dai_list); 187f3208780SMark Brown 188db795f9bSKuninori Morimoto static int component_list_show(struct seq_file *m, void *v) 189db795f9bSKuninori Morimoto { 190db795f9bSKuninori Morimoto struct snd_soc_component *component; 191db795f9bSKuninori Morimoto 192db795f9bSKuninori Morimoto mutex_lock(&client_mutex); 193db795f9bSKuninori Morimoto 194db795f9bSKuninori Morimoto list_for_each_entry(component, &component_list, list) 195db795f9bSKuninori Morimoto seq_printf(m, "%s\n", component->name); 196db795f9bSKuninori Morimoto 197db795f9bSKuninori Morimoto mutex_unlock(&client_mutex); 198db795f9bSKuninori Morimoto 199db795f9bSKuninori Morimoto return 0; 200db795f9bSKuninori Morimoto } 201db795f9bSKuninori Morimoto DEFINE_SHOW_ATTRIBUTE(component_list); 202db795f9bSKuninori Morimoto 203a6052154SJarkko Nikula static void soc_init_card_debugfs(struct snd_soc_card *card) 204a6052154SJarkko Nikula { 2056553bf06SLars-Peter Clausen if (!snd_soc_debugfs_root) 2066553bf06SLars-Peter Clausen return; 2076553bf06SLars-Peter Clausen 208a6052154SJarkko Nikula card->debugfs_card_root = debugfs_create_dir(card->name, 2098a9dab1aSMark Brown snd_soc_debugfs_root); 2103a45b867SJarkko Nikula if (!card->debugfs_card_root) { 211a6052154SJarkko Nikula dev_warn(card->dev, 2127c08be84SLothar Waßmann "ASoC: Failed to create card debugfs directory\n"); 2133a45b867SJarkko Nikula return; 2143a45b867SJarkko Nikula } 2153a45b867SJarkko Nikula 2163a45b867SJarkko Nikula card->debugfs_pop_time = debugfs_create_u32("dapm_pop_time", 0644, 2173a45b867SJarkko Nikula card->debugfs_card_root, 2183a45b867SJarkko Nikula &card->pop_time); 2193a45b867SJarkko Nikula if (!card->debugfs_pop_time) 2203a45b867SJarkko Nikula dev_warn(card->dev, 221f110bfc7SLiam Girdwood "ASoC: Failed to create pop time debugfs file\n"); 222a6052154SJarkko Nikula } 223a6052154SJarkko Nikula 224a6052154SJarkko Nikula static void soc_cleanup_card_debugfs(struct snd_soc_card *card) 225a6052154SJarkko Nikula { 226a6052154SJarkko Nikula debugfs_remove_recursive(card->debugfs_card_root); 227a6052154SJarkko Nikula } 228a6052154SJarkko Nikula 2296553bf06SLars-Peter Clausen static void snd_soc_debugfs_init(void) 2306553bf06SLars-Peter Clausen { 2316553bf06SLars-Peter Clausen snd_soc_debugfs_root = debugfs_create_dir("asoc", NULL); 232d9a02c55SFabio Estevam if (IS_ERR_OR_NULL(snd_soc_debugfs_root)) { 2336553bf06SLars-Peter Clausen pr_warn("ASoC: Failed to create debugfs directory\n"); 2346553bf06SLars-Peter Clausen snd_soc_debugfs_root = NULL; 2356553bf06SLars-Peter Clausen return; 2366553bf06SLars-Peter Clausen } 2376553bf06SLars-Peter Clausen 2386553bf06SLars-Peter Clausen if (!debugfs_create_file("dais", 0444, snd_soc_debugfs_root, NULL, 2396553bf06SLars-Peter Clausen &dai_list_fops)) 2406553bf06SLars-Peter Clausen pr_warn("ASoC: Failed to create DAI list debugfs file\n"); 241db795f9bSKuninori Morimoto 242db795f9bSKuninori Morimoto if (!debugfs_create_file("components", 0444, snd_soc_debugfs_root, NULL, 243db795f9bSKuninori Morimoto &component_list_fops)) 244db795f9bSKuninori Morimoto pr_warn("ASoC: Failed to create component list debugfs file\n"); 2456553bf06SLars-Peter Clausen } 2466553bf06SLars-Peter Clausen 2476553bf06SLars-Peter Clausen static void snd_soc_debugfs_exit(void) 2486553bf06SLars-Peter Clausen { 2496553bf06SLars-Peter Clausen debugfs_remove_recursive(snd_soc_debugfs_root); 2506553bf06SLars-Peter Clausen } 2516553bf06SLars-Peter Clausen 2522624d5faSMark Brown #else 2532624d5faSMark Brown 25481c7cfd1SLars-Peter Clausen static inline void soc_init_component_debugfs( 25581c7cfd1SLars-Peter Clausen struct snd_soc_component *component) 2562624d5faSMark Brown { 2572624d5faSMark Brown } 2582624d5faSMark Brown 25981c7cfd1SLars-Peter Clausen static inline void soc_cleanup_component_debugfs( 26081c7cfd1SLars-Peter Clausen struct snd_soc_component *component) 261731f1ab2SSebastien Guiriec { 262731f1ab2SSebastien Guiriec } 263731f1ab2SSebastien Guiriec 264b95fccbcSAxel Lin static inline void soc_init_card_debugfs(struct snd_soc_card *card) 265b95fccbcSAxel Lin { 266b95fccbcSAxel Lin } 267b95fccbcSAxel Lin 268b95fccbcSAxel Lin static inline void soc_cleanup_card_debugfs(struct snd_soc_card *card) 269b95fccbcSAxel Lin { 270b95fccbcSAxel Lin } 2716553bf06SLars-Peter Clausen 2726553bf06SLars-Peter Clausen static inline void snd_soc_debugfs_init(void) 2736553bf06SLars-Peter Clausen { 2746553bf06SLars-Peter Clausen } 2756553bf06SLars-Peter Clausen 2766553bf06SLars-Peter Clausen static inline void snd_soc_debugfs_exit(void) 2776553bf06SLars-Peter Clausen { 2786553bf06SLars-Peter Clausen } 2796553bf06SLars-Peter Clausen 2802624d5faSMark Brown #endif 2812624d5faSMark Brown 282a0ac4411SKuninori Morimoto static int snd_soc_rtdcom_add(struct snd_soc_pcm_runtime *rtd, 283a0ac4411SKuninori Morimoto struct snd_soc_component *component) 284a0ac4411SKuninori Morimoto { 285a0ac4411SKuninori Morimoto struct snd_soc_rtdcom_list *rtdcom; 286a0ac4411SKuninori Morimoto struct snd_soc_rtdcom_list *new_rtdcom; 287a0ac4411SKuninori Morimoto 288a0ac4411SKuninori Morimoto for_each_rtdcom(rtd, rtdcom) { 289a0ac4411SKuninori Morimoto /* already connected */ 290a0ac4411SKuninori Morimoto if (rtdcom->component == component) 291a0ac4411SKuninori Morimoto return 0; 292a0ac4411SKuninori Morimoto } 293a0ac4411SKuninori Morimoto 294a0ac4411SKuninori Morimoto new_rtdcom = kmalloc(sizeof(*new_rtdcom), GFP_KERNEL); 295a0ac4411SKuninori Morimoto if (!new_rtdcom) 296a0ac4411SKuninori Morimoto return -ENOMEM; 297a0ac4411SKuninori Morimoto 298a0ac4411SKuninori Morimoto new_rtdcom->component = component; 299a0ac4411SKuninori Morimoto INIT_LIST_HEAD(&new_rtdcom->list); 300a0ac4411SKuninori Morimoto 301a0ac4411SKuninori Morimoto list_add_tail(&new_rtdcom->list, &rtd->component_list); 302a0ac4411SKuninori Morimoto 303a0ac4411SKuninori Morimoto return 0; 304a0ac4411SKuninori Morimoto } 305a0ac4411SKuninori Morimoto 306a0ac4411SKuninori Morimoto static void snd_soc_rtdcom_del_all(struct snd_soc_pcm_runtime *rtd) 307a0ac4411SKuninori Morimoto { 308a0ac4411SKuninori Morimoto struct snd_soc_rtdcom_list *rtdcom1, *rtdcom2; 309a0ac4411SKuninori Morimoto 310a0ac4411SKuninori Morimoto for_each_rtdcom_safe(rtd, rtdcom1, rtdcom2) 311a0ac4411SKuninori Morimoto kfree(rtdcom1); 312a0ac4411SKuninori Morimoto 313a0ac4411SKuninori Morimoto INIT_LIST_HEAD(&rtd->component_list); 314a0ac4411SKuninori Morimoto } 315a0ac4411SKuninori Morimoto 316a0ac4411SKuninori Morimoto struct snd_soc_component *snd_soc_rtdcom_lookup(struct snd_soc_pcm_runtime *rtd, 317a0ac4411SKuninori Morimoto const char *driver_name) 318a0ac4411SKuninori Morimoto { 319a0ac4411SKuninori Morimoto struct snd_soc_rtdcom_list *rtdcom; 320a0ac4411SKuninori Morimoto 321971da24cSKuninori Morimoto if (!driver_name) 322971da24cSKuninori Morimoto return NULL; 323971da24cSKuninori Morimoto 324a0ac4411SKuninori Morimoto for_each_rtdcom(rtd, rtdcom) { 325971da24cSKuninori Morimoto const char *component_name = rtdcom->component->driver->name; 326971da24cSKuninori Morimoto 327971da24cSKuninori Morimoto if (!component_name) 328971da24cSKuninori Morimoto continue; 329971da24cSKuninori Morimoto 330971da24cSKuninori Morimoto if ((component_name == driver_name) || 331971da24cSKuninori Morimoto strcmp(component_name, driver_name) == 0) 332a0ac4411SKuninori Morimoto return rtdcom->component; 333a0ac4411SKuninori Morimoto } 334a0ac4411SKuninori Morimoto 335a0ac4411SKuninori Morimoto return NULL; 336a0ac4411SKuninori Morimoto } 337031734b7SKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_rtdcom_lookup); 338a0ac4411SKuninori Morimoto 33947c88fffSLiam Girdwood struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card, 34047c88fffSLiam Girdwood const char *dai_link, int stream) 34147c88fffSLiam Girdwood { 3421a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd; 34347c88fffSLiam Girdwood 3441a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 3451a497983SMengdong Lin if (rtd->dai_link->no_pcm && 3461a497983SMengdong Lin !strcmp(rtd->dai_link->name, dai_link)) 3471a497983SMengdong Lin return rtd->pcm->streams[stream].substream; 34847c88fffSLiam Girdwood } 349f110bfc7SLiam Girdwood dev_dbg(card->dev, "ASoC: failed to find dai link %s\n", dai_link); 35047c88fffSLiam Girdwood return NULL; 35147c88fffSLiam Girdwood } 35247c88fffSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_get_dai_substream); 35347c88fffSLiam Girdwood 35475ab9eb6SKuninori Morimoto static const struct snd_soc_ops null_snd_soc_ops; 35575ab9eb6SKuninori Morimoto 3561a497983SMengdong Lin static struct snd_soc_pcm_runtime *soc_new_pcm_runtime( 3571a497983SMengdong Lin struct snd_soc_card *card, struct snd_soc_dai_link *dai_link) 3581a497983SMengdong Lin { 3591a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd; 3601a497983SMengdong Lin 3611a497983SMengdong Lin rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime), GFP_KERNEL); 3621a497983SMengdong Lin if (!rtd) 3631a497983SMengdong Lin return NULL; 3641a497983SMengdong Lin 365a0ac4411SKuninori Morimoto INIT_LIST_HEAD(&rtd->component_list); 3661a497983SMengdong Lin rtd->card = card; 3671a497983SMengdong Lin rtd->dai_link = dai_link; 36875ab9eb6SKuninori Morimoto if (!rtd->dai_link->ops) 36975ab9eb6SKuninori Morimoto rtd->dai_link->ops = &null_snd_soc_ops; 37075ab9eb6SKuninori Morimoto 3716396bb22SKees Cook rtd->codec_dais = kcalloc(dai_link->num_codecs, 3726396bb22SKees Cook sizeof(struct snd_soc_dai *), 3731a497983SMengdong Lin GFP_KERNEL); 3741a497983SMengdong Lin if (!rtd->codec_dais) { 3751a497983SMengdong Lin kfree(rtd); 3761a497983SMengdong Lin return NULL; 3771a497983SMengdong Lin } 3781a497983SMengdong Lin 3791a497983SMengdong Lin return rtd; 3801a497983SMengdong Lin } 3811a497983SMengdong Lin 3821a497983SMengdong Lin static void soc_free_pcm_runtime(struct snd_soc_pcm_runtime *rtd) 3831a497983SMengdong Lin { 3841a497983SMengdong Lin kfree(rtd->codec_dais); 385a0ac4411SKuninori Morimoto snd_soc_rtdcom_del_all(rtd); 3861a497983SMengdong Lin kfree(rtd); 3871a497983SMengdong Lin } 3881a497983SMengdong Lin 3891a497983SMengdong Lin static void soc_add_pcm_runtime(struct snd_soc_card *card, 3901a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd) 3911a497983SMengdong Lin { 3921a497983SMengdong Lin list_add_tail(&rtd->list, &card->rtd_list); 3931a497983SMengdong Lin rtd->num = card->num_rtd; 3941a497983SMengdong Lin card->num_rtd++; 3951a497983SMengdong Lin } 3961a497983SMengdong Lin 3971a497983SMengdong Lin static void soc_remove_pcm_runtimes(struct snd_soc_card *card) 3981a497983SMengdong Lin { 3991a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd, *_rtd; 4001a497983SMengdong Lin 4011a497983SMengdong Lin list_for_each_entry_safe(rtd, _rtd, &card->rtd_list, list) { 4021a497983SMengdong Lin list_del(&rtd->list); 4031a497983SMengdong Lin soc_free_pcm_runtime(rtd); 4041a497983SMengdong Lin } 4051a497983SMengdong Lin 4061a497983SMengdong Lin card->num_rtd = 0; 4071a497983SMengdong Lin } 4081a497983SMengdong Lin 40947c88fffSLiam Girdwood struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card, 41047c88fffSLiam Girdwood const char *dai_link) 41147c88fffSLiam Girdwood { 4121a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd; 41347c88fffSLiam Girdwood 4141a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 4151a497983SMengdong Lin if (!strcmp(rtd->dai_link->name, dai_link)) 4161a497983SMengdong Lin return rtd; 41747c88fffSLiam Girdwood } 418f110bfc7SLiam Girdwood dev_dbg(card->dev, "ASoC: failed to find rtd %s\n", dai_link); 41947c88fffSLiam Girdwood return NULL; 42047c88fffSLiam Girdwood } 42147c88fffSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_get_pcm_runtime); 42247c88fffSLiam Girdwood 4239d58a077SRichard Fitzgerald static void codec2codec_close_delayed_work(struct work_struct *work) 4249d58a077SRichard Fitzgerald { 4259d58a077SRichard Fitzgerald /* Currently nothing to do for c2c links 4269d58a077SRichard Fitzgerald * Since c2c links are internal nodes in the DAPM graph and 4279d58a077SRichard Fitzgerald * don't interface with the outside world or application layer 4289d58a077SRichard Fitzgerald * we don't have to do any special handling on close. 4299d58a077SRichard Fitzgerald */ 4309d58a077SRichard Fitzgerald } 4319d58a077SRichard Fitzgerald 4326f8ab4acSMark Brown #ifdef CONFIG_PM_SLEEP 433db2a4165SFrank Mandarino /* powers down audio subsystem for suspend */ 4346f8ab4acSMark Brown int snd_soc_suspend(struct device *dev) 435db2a4165SFrank Mandarino { 4366f8ab4acSMark Brown struct snd_soc_card *card = dev_get_drvdata(dev); 437d9fc4063SKuninori Morimoto struct snd_soc_component *component; 4381a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd; 4391a497983SMengdong Lin int i; 440db2a4165SFrank Mandarino 441c5599b87SLars-Peter Clausen /* If the card is not initialized yet there is nothing to do */ 442c5599b87SLars-Peter Clausen if (!card->instantiated) 443e3509ff0SDaniel Mack return 0; 444e3509ff0SDaniel Mack 4456ed25978SAndy Green /* Due to the resume being scheduled into a workqueue we could 4466ed25978SAndy Green * suspend before that's finished - wait for it to complete. 4476ed25978SAndy Green */ 448f0fba2adSLiam Girdwood snd_power_wait(card->snd_card, SNDRV_CTL_POWER_D0); 4496ed25978SAndy Green 4506ed25978SAndy Green /* we're going to block userspace touching us until resume completes */ 451f0fba2adSLiam Girdwood snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D3hot); 4526ed25978SAndy Green 453a00f90f9SMark Brown /* mute any active DACs */ 4541a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 4550b7990e3SKuninori Morimoto struct snd_soc_dai *dai; 4563efab7dcSMark Brown 4571a497983SMengdong Lin if (rtd->dai_link->ignore_suspend) 4583efab7dcSMark Brown continue; 4593efab7dcSMark Brown 4600b7990e3SKuninori Morimoto for_each_rtd_codec_dai(rtd, i, dai) { 46188bd870fSBenoit Cousson struct snd_soc_dai_driver *drv = dai->driver; 46288bd870fSBenoit Cousson 463f0fba2adSLiam Girdwood if (drv->ops->digital_mute && dai->playback_active) 464f0fba2adSLiam Girdwood drv->ops->digital_mute(dai, 1); 465db2a4165SFrank Mandarino } 46688bd870fSBenoit Cousson } 467db2a4165SFrank Mandarino 4684ccab3e7SLiam Girdwood /* suspend all pcms */ 4691a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 4701a497983SMengdong Lin if (rtd->dai_link->ignore_suspend) 4713efab7dcSMark Brown continue; 4723efab7dcSMark Brown 4731a497983SMengdong Lin snd_pcm_suspend_all(rtd->pcm); 4743efab7dcSMark Brown } 4754ccab3e7SLiam Girdwood 47687506549SMark Brown if (card->suspend_pre) 47770b2ac12SMark Brown card->suspend_pre(card); 478db2a4165SFrank Mandarino 4791a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 4801a497983SMengdong Lin struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 4813efab7dcSMark Brown 4821a497983SMengdong Lin if (rtd->dai_link->ignore_suspend) 4833efab7dcSMark Brown continue; 4843efab7dcSMark Brown 485bc263214SLars-Peter Clausen if (cpu_dai->driver->suspend && !cpu_dai->driver->bus_control) 486f0fba2adSLiam Girdwood cpu_dai->driver->suspend(cpu_dai); 487db2a4165SFrank Mandarino } 488db2a4165SFrank Mandarino 48937660b6dSLars-Peter Clausen /* close any waiting streams */ 4901a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) 4911a497983SMengdong Lin flush_delayed_work(&rtd->delayed_work); 492db2a4165SFrank Mandarino 4931a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 4943efab7dcSMark Brown 4951a497983SMengdong Lin if (rtd->dai_link->ignore_suspend) 4963efab7dcSMark Brown continue; 4973efab7dcSMark Brown 4981a497983SMengdong Lin snd_soc_dapm_stream_event(rtd, 4997bd3a6f3SMark Brown SNDRV_PCM_STREAM_PLAYBACK, 500db2a4165SFrank Mandarino SND_SOC_DAPM_STREAM_SUSPEND); 501f0fba2adSLiam Girdwood 5021a497983SMengdong Lin snd_soc_dapm_stream_event(rtd, 5037bd3a6f3SMark Brown SNDRV_PCM_STREAM_CAPTURE, 504db2a4165SFrank Mandarino SND_SOC_DAPM_STREAM_SUSPEND); 505db2a4165SFrank Mandarino } 506db2a4165SFrank Mandarino 5078be4da29SLars-Peter Clausen /* Recheck all endpoints too, their state is affected by suspend */ 5088be4da29SLars-Peter Clausen dapm_mark_endpoints_dirty(card); 509e2d32ff6SMark Brown snd_soc_dapm_sync(&card->dapm); 510e2d32ff6SMark Brown 5119178feb4SKuninori Morimoto /* suspend all COMPONENTs */ 512d9fc4063SKuninori Morimoto list_for_each_entry(component, &card->component_dev_list, card_list) { 513d9fc4063SKuninori Morimoto struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); 514d9fc4063SKuninori Morimoto 5159178feb4SKuninori Morimoto /* If there are paths active then the COMPONENT will be held with 5161547aba9SMark Brown * bias _ON and should not be suspended. */ 5179178feb4SKuninori Morimoto if (!component->suspended) { 5184890140fSLars-Peter Clausen switch (snd_soc_dapm_get_bias_level(dapm)) { 5191547aba9SMark Brown case SND_SOC_BIAS_STANDBY: 520125a25daSMark Brown /* 5219178feb4SKuninori Morimoto * If the COMPONENT is capable of idle 522125a25daSMark Brown * bias off then being in STANDBY 523125a25daSMark Brown * means it's doing something, 524125a25daSMark Brown * otherwise fall through. 525125a25daSMark Brown */ 5264890140fSLars-Peter Clausen if (dapm->idle_bias_off) { 5279178feb4SKuninori Morimoto dev_dbg(component->dev, 52810e8aa9aSMichał Mirosław "ASoC: idle_bias_off CODEC on over suspend\n"); 529125a25daSMark Brown break; 530125a25daSMark Brown } 5311a12d5dcSGustavo A. R. Silva /* fall through */ 532a8093297SLars-Peter Clausen 5331547aba9SMark Brown case SND_SOC_BIAS_OFF: 534999f7f5aSKuninori Morimoto if (component->driver->suspend) 535999f7f5aSKuninori Morimoto component->driver->suspend(component); 5369178feb4SKuninori Morimoto component->suspended = 1; 5379178feb4SKuninori Morimoto if (component->regmap) 5389178feb4SKuninori Morimoto regcache_mark_dirty(component->regmap); 539988e8cc4SNicolin Chen /* deactivate pins to sleep state */ 5409178feb4SKuninori Morimoto pinctrl_pm_select_sleep_state(component->dev); 5411547aba9SMark Brown break; 5421547aba9SMark Brown default: 5439178feb4SKuninori Morimoto dev_dbg(component->dev, 5449178feb4SKuninori Morimoto "ASoC: COMPONENT is on over suspend\n"); 5451547aba9SMark Brown break; 5461547aba9SMark Brown } 5471547aba9SMark Brown } 548f0fba2adSLiam Girdwood } 549db2a4165SFrank Mandarino 5501a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 5511a497983SMengdong Lin struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 5523efab7dcSMark Brown 5531a497983SMengdong Lin if (rtd->dai_link->ignore_suspend) 5543efab7dcSMark Brown continue; 5553efab7dcSMark Brown 556bc263214SLars-Peter Clausen if (cpu_dai->driver->suspend && cpu_dai->driver->bus_control) 557f0fba2adSLiam Girdwood cpu_dai->driver->suspend(cpu_dai); 558988e8cc4SNicolin Chen 559988e8cc4SNicolin Chen /* deactivate pins to sleep state */ 560988e8cc4SNicolin Chen pinctrl_pm_select_sleep_state(cpu_dai->dev); 561db2a4165SFrank Mandarino } 562db2a4165SFrank Mandarino 56387506549SMark Brown if (card->suspend_post) 56470b2ac12SMark Brown card->suspend_post(card); 565db2a4165SFrank Mandarino 566db2a4165SFrank Mandarino return 0; 567db2a4165SFrank Mandarino } 5686f8ab4acSMark Brown EXPORT_SYMBOL_GPL(snd_soc_suspend); 569db2a4165SFrank Mandarino 5706ed25978SAndy Green /* deferred resume work, so resume can complete before we finished 5716ed25978SAndy Green * setting our codec back up, which can be very slow on I2C 5726ed25978SAndy Green */ 5736ed25978SAndy Green static void soc_resume_deferred(struct work_struct *work) 574db2a4165SFrank Mandarino { 575f0fba2adSLiam Girdwood struct snd_soc_card *card = 576f0fba2adSLiam Girdwood container_of(work, struct snd_soc_card, deferred_resume_work); 5771a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd; 578d9fc4063SKuninori Morimoto struct snd_soc_component *component; 5791a497983SMengdong Lin int i; 580db2a4165SFrank Mandarino 5816ed25978SAndy Green /* our power state is still SNDRV_CTL_POWER_D3hot from suspend time, 5826ed25978SAndy Green * so userspace apps are blocked from touching us 5836ed25978SAndy Green */ 5846ed25978SAndy Green 585f110bfc7SLiam Girdwood dev_dbg(card->dev, "ASoC: starting resume work\n"); 5866ed25978SAndy Green 5879949788bSMark Brown /* Bring us up into D2 so that DAPM starts enabling things */ 588f0fba2adSLiam Girdwood snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D2); 5899949788bSMark Brown 59087506549SMark Brown if (card->resume_pre) 59170b2ac12SMark Brown card->resume_pre(card); 592db2a4165SFrank Mandarino 593bc263214SLars-Peter Clausen /* resume control bus DAIs */ 5941a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 5951a497983SMengdong Lin struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 5963efab7dcSMark Brown 5971a497983SMengdong Lin if (rtd->dai_link->ignore_suspend) 5983efab7dcSMark Brown continue; 5993efab7dcSMark Brown 600bc263214SLars-Peter Clausen if (cpu_dai->driver->resume && cpu_dai->driver->bus_control) 601f0fba2adSLiam Girdwood cpu_dai->driver->resume(cpu_dai); 602db2a4165SFrank Mandarino } 603db2a4165SFrank Mandarino 604d9fc4063SKuninori Morimoto list_for_each_entry(component, &card->component_dev_list, card_list) { 6059178feb4SKuninori Morimoto if (component->suspended) { 606999f7f5aSKuninori Morimoto if (component->driver->resume) 607999f7f5aSKuninori Morimoto component->driver->resume(component); 6089178feb4SKuninori Morimoto component->suspended = 0; 6091547aba9SMark Brown } 610f0fba2adSLiam Girdwood } 611db2a4165SFrank Mandarino 6121a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 6133efab7dcSMark Brown 6141a497983SMengdong Lin if (rtd->dai_link->ignore_suspend) 6153efab7dcSMark Brown continue; 6163efab7dcSMark Brown 6171a497983SMengdong Lin snd_soc_dapm_stream_event(rtd, 618d9b0951bSLiam Girdwood SNDRV_PCM_STREAM_PLAYBACK, 619db2a4165SFrank Mandarino SND_SOC_DAPM_STREAM_RESUME); 620f0fba2adSLiam Girdwood 6211a497983SMengdong Lin snd_soc_dapm_stream_event(rtd, 622d9b0951bSLiam Girdwood SNDRV_PCM_STREAM_CAPTURE, 623db2a4165SFrank Mandarino SND_SOC_DAPM_STREAM_RESUME); 624db2a4165SFrank Mandarino } 625db2a4165SFrank Mandarino 6263ff3f64bSMark Brown /* unmute any active DACs */ 6271a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 6280b7990e3SKuninori Morimoto struct snd_soc_dai *dai; 6293efab7dcSMark Brown 6301a497983SMengdong Lin if (rtd->dai_link->ignore_suspend) 6313efab7dcSMark Brown continue; 6323efab7dcSMark Brown 6330b7990e3SKuninori Morimoto for_each_rtd_codec_dai(rtd, i, dai) { 63488bd870fSBenoit Cousson struct snd_soc_dai_driver *drv = dai->driver; 63588bd870fSBenoit Cousson 636f0fba2adSLiam Girdwood if (drv->ops->digital_mute && dai->playback_active) 637f0fba2adSLiam Girdwood drv->ops->digital_mute(dai, 0); 638db2a4165SFrank Mandarino } 63988bd870fSBenoit Cousson } 640db2a4165SFrank Mandarino 6411a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 6421a497983SMengdong Lin struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 6433efab7dcSMark Brown 6441a497983SMengdong Lin if (rtd->dai_link->ignore_suspend) 6453efab7dcSMark Brown continue; 6463efab7dcSMark Brown 647bc263214SLars-Peter Clausen if (cpu_dai->driver->resume && !cpu_dai->driver->bus_control) 648f0fba2adSLiam Girdwood cpu_dai->driver->resume(cpu_dai); 649db2a4165SFrank Mandarino } 650db2a4165SFrank Mandarino 65187506549SMark Brown if (card->resume_post) 65270b2ac12SMark Brown card->resume_post(card); 653db2a4165SFrank Mandarino 654f110bfc7SLiam Girdwood dev_dbg(card->dev, "ASoC: resume work completed\n"); 6556ed25978SAndy Green 6568be4da29SLars-Peter Clausen /* Recheck all endpoints too, their state is affected by suspend */ 6578be4da29SLars-Peter Clausen dapm_mark_endpoints_dirty(card); 658e2d32ff6SMark Brown snd_soc_dapm_sync(&card->dapm); 6591a7aaa58SJeeja KP 6601a7aaa58SJeeja KP /* userspace can access us now we are back as we were before */ 6611a7aaa58SJeeja KP snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D0); 6626ed25978SAndy Green } 6636ed25978SAndy Green 6646ed25978SAndy Green /* powers up audio subsystem after a suspend */ 6656f8ab4acSMark Brown int snd_soc_resume(struct device *dev) 6666ed25978SAndy Green { 6676f8ab4acSMark Brown struct snd_soc_card *card = dev_get_drvdata(dev); 668bc263214SLars-Peter Clausen bool bus_control = false; 6691a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd; 670b9dd94a8SPeter Ujfalusi 671c5599b87SLars-Peter Clausen /* If the card is not initialized yet there is nothing to do */ 672c5599b87SLars-Peter Clausen if (!card->instantiated) 6735ff1ddf2SEric Miao return 0; 6745ff1ddf2SEric Miao 675988e8cc4SNicolin Chen /* activate pins from sleep state */ 6761a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 6770b7990e3SKuninori Morimoto struct snd_soc_dai *codec_dai; 67888bd870fSBenoit Cousson struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 67988bd870fSBenoit Cousson int j; 68088bd870fSBenoit Cousson 681988e8cc4SNicolin Chen if (cpu_dai->active) 682988e8cc4SNicolin Chen pinctrl_pm_select_default_state(cpu_dai->dev); 68388bd870fSBenoit Cousson 6840b7990e3SKuninori Morimoto for_each_rtd_codec_dai(rtd, j, codec_dai) { 685988e8cc4SNicolin Chen if (codec_dai->active) 686988e8cc4SNicolin Chen pinctrl_pm_select_default_state(codec_dai->dev); 687988e8cc4SNicolin Chen } 68888bd870fSBenoit Cousson } 689988e8cc4SNicolin Chen 690bc263214SLars-Peter Clausen /* 691bc263214SLars-Peter Clausen * DAIs that also act as the control bus master might have other drivers 692bc263214SLars-Peter Clausen * hanging off them so need to resume immediately. Other drivers don't 693bc263214SLars-Peter Clausen * have that problem and may take a substantial amount of time to resume 69464ab9baaSMark Brown * due to I/O costs and anti-pop so handle them out of line. 69564ab9baaSMark Brown */ 6961a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 6971a497983SMengdong Lin struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 698bc263214SLars-Peter Clausen bus_control |= cpu_dai->driver->bus_control; 69982e14e8bSStephen Warren } 700bc263214SLars-Peter Clausen if (bus_control) { 701bc263214SLars-Peter Clausen dev_dbg(dev, "ASoC: Resuming control bus master immediately\n"); 70264ab9baaSMark Brown soc_resume_deferred(&card->deferred_resume_work); 70364ab9baaSMark Brown } else { 704f110bfc7SLiam Girdwood dev_dbg(dev, "ASoC: Scheduling resume work\n"); 7056308419aSMark Brown if (!schedule_work(&card->deferred_resume_work)) 706f110bfc7SLiam Girdwood dev_err(dev, "ASoC: resume work item may be lost\n"); 707f0fba2adSLiam Girdwood } 7086ed25978SAndy Green 709db2a4165SFrank Mandarino return 0; 710db2a4165SFrank Mandarino } 7116f8ab4acSMark Brown EXPORT_SYMBOL_GPL(snd_soc_resume); 712db2a4165SFrank Mandarino #else 7136f8ab4acSMark Brown #define snd_soc_suspend NULL 7146f8ab4acSMark Brown #define snd_soc_resume NULL 715db2a4165SFrank Mandarino #endif 716db2a4165SFrank Mandarino 71785e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops null_dai_ops = { 71802a06d30SBarry Song }; 71902a06d30SBarry Song 72065d9361fSLars-Peter Clausen static struct snd_soc_component *soc_find_component( 72165d9361fSLars-Peter Clausen const struct device_node *of_node, const char *name) 72212023a9aSMisael Lopez Cruz { 72365d9361fSLars-Peter Clausen struct snd_soc_component *component; 72412023a9aSMisael Lopez Cruz 72534e81ab4SLars-Peter Clausen lockdep_assert_held(&client_mutex); 72634e81ab4SLars-Peter Clausen 72765d9361fSLars-Peter Clausen list_for_each_entry(component, &component_list, list) { 72865d9361fSLars-Peter Clausen if (of_node) { 72965d9361fSLars-Peter Clausen if (component->dev->of_node == of_node) 73065d9361fSLars-Peter Clausen return component; 73165d9361fSLars-Peter Clausen } else if (strcmp(component->name, name) == 0) { 73265d9361fSLars-Peter Clausen return component; 73312023a9aSMisael Lopez Cruz } 73412023a9aSMisael Lopez Cruz } 73512023a9aSMisael Lopez Cruz 73612023a9aSMisael Lopez Cruz return NULL; 73712023a9aSMisael Lopez Cruz } 73812023a9aSMisael Lopez Cruz 739fbb88b5cSMengdong Lin /** 740fbb88b5cSMengdong Lin * snd_soc_find_dai - Find a registered DAI 741fbb88b5cSMengdong Lin * 7424958471bSJeffy Chen * @dlc: name of the DAI or the DAI driver and optional component info to match 743fbb88b5cSMengdong Lin * 744ad61dd30SStephen Boyd * This function will search all registered components and their DAIs to 745fbb88b5cSMengdong Lin * find the DAI of the same name. The component's of_node and name 746fbb88b5cSMengdong Lin * should also match if being specified. 747fbb88b5cSMengdong Lin * 748fbb88b5cSMengdong Lin * Return: pointer of DAI, or NULL if not found. 749fbb88b5cSMengdong Lin */ 750305e9020SMengdong Lin struct snd_soc_dai *snd_soc_find_dai( 75114621c7eSLars-Peter Clausen const struct snd_soc_dai_link_component *dlc) 75212023a9aSMisael Lopez Cruz { 75314621c7eSLars-Peter Clausen struct snd_soc_component *component; 75414621c7eSLars-Peter Clausen struct snd_soc_dai *dai; 7553e0aa8d8SJyri Sarha struct device_node *component_of_node; 75612023a9aSMisael Lopez Cruz 75734e81ab4SLars-Peter Clausen lockdep_assert_held(&client_mutex); 75834e81ab4SLars-Peter Clausen 75914621c7eSLars-Peter Clausen /* Find CPU DAI from registered DAIs*/ 76014621c7eSLars-Peter Clausen list_for_each_entry(component, &component_list, list) { 7613e0aa8d8SJyri Sarha component_of_node = component->dev->of_node; 7623e0aa8d8SJyri Sarha if (!component_of_node && component->dev->parent) 7633e0aa8d8SJyri Sarha component_of_node = component->dev->parent->of_node; 7643e0aa8d8SJyri Sarha 7653e0aa8d8SJyri Sarha if (dlc->of_node && component_of_node != dlc->of_node) 76612023a9aSMisael Lopez Cruz continue; 7671ffae361SLars-Peter Clausen if (dlc->name && strcmp(component->name, dlc->name)) 76812023a9aSMisael Lopez Cruz continue; 76914621c7eSLars-Peter Clausen list_for_each_entry(dai, &component->dai_list, list) { 7704958471bSJeffy Chen if (dlc->dai_name && strcmp(dai->name, dlc->dai_name) 7716a6dafdaSJeffy Chen && (!dai->driver->name 7726a6dafdaSJeffy Chen || strcmp(dai->driver->name, dlc->dai_name))) 77314621c7eSLars-Peter Clausen continue; 77412023a9aSMisael Lopez Cruz 77514621c7eSLars-Peter Clausen return dai; 77612023a9aSMisael Lopez Cruz } 77712023a9aSMisael Lopez Cruz } 77812023a9aSMisael Lopez Cruz 77912023a9aSMisael Lopez Cruz return NULL; 78012023a9aSMisael Lopez Cruz } 781305e9020SMengdong Lin EXPORT_SYMBOL_GPL(snd_soc_find_dai); 78212023a9aSMisael Lopez Cruz 78317fb1755SMengdong Lin 78417fb1755SMengdong Lin /** 78517fb1755SMengdong Lin * snd_soc_find_dai_link - Find a DAI link 78617fb1755SMengdong Lin * 78717fb1755SMengdong Lin * @card: soc card 78817fb1755SMengdong Lin * @id: DAI link ID to match 78917fb1755SMengdong Lin * @name: DAI link name to match, optional 7908abab35fSCharles Keepax * @stream_name: DAI link stream name to match, optional 79117fb1755SMengdong Lin * 79217fb1755SMengdong Lin * This function will search all existing DAI links of the soc card to 79317fb1755SMengdong Lin * find the link of the same ID. Since DAI links may not have their 79417fb1755SMengdong Lin * unique ID, so name and stream name should also match if being 79517fb1755SMengdong Lin * specified. 79617fb1755SMengdong Lin * 79717fb1755SMengdong Lin * Return: pointer of DAI link, or NULL if not found. 79817fb1755SMengdong Lin */ 79917fb1755SMengdong Lin struct snd_soc_dai_link *snd_soc_find_dai_link(struct snd_soc_card *card, 80017fb1755SMengdong Lin int id, const char *name, 80117fb1755SMengdong Lin const char *stream_name) 80217fb1755SMengdong Lin { 80317fb1755SMengdong Lin struct snd_soc_dai_link *link, *_link; 80417fb1755SMengdong Lin 80517fb1755SMengdong Lin lockdep_assert_held(&client_mutex); 80617fb1755SMengdong Lin 80717fb1755SMengdong Lin list_for_each_entry_safe(link, _link, &card->dai_link_list, list) { 80817fb1755SMengdong Lin if (link->id != id) 80917fb1755SMengdong Lin continue; 81017fb1755SMengdong Lin 81117fb1755SMengdong Lin if (name && (!link->name || strcmp(name, link->name))) 81217fb1755SMengdong Lin continue; 81317fb1755SMengdong Lin 81417fb1755SMengdong Lin if (stream_name && (!link->stream_name 81517fb1755SMengdong Lin || strcmp(stream_name, link->stream_name))) 81617fb1755SMengdong Lin continue; 81717fb1755SMengdong Lin 81817fb1755SMengdong Lin return link; 81917fb1755SMengdong Lin } 82017fb1755SMengdong Lin 82117fb1755SMengdong Lin return NULL; 82217fb1755SMengdong Lin } 82317fb1755SMengdong Lin EXPORT_SYMBOL_GPL(snd_soc_find_dai_link); 82417fb1755SMengdong Lin 82549a5ba1cSMengdong Lin static bool soc_is_dai_link_bound(struct snd_soc_card *card, 82649a5ba1cSMengdong Lin struct snd_soc_dai_link *dai_link) 827db2a4165SFrank Mandarino { 82849a5ba1cSMengdong Lin struct snd_soc_pcm_runtime *rtd; 82949a5ba1cSMengdong Lin 83049a5ba1cSMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 83149a5ba1cSMengdong Lin if (rtd->dai_link == dai_link) 83249a5ba1cSMengdong Lin return true; 83349a5ba1cSMengdong Lin } 83449a5ba1cSMengdong Lin 83549a5ba1cSMengdong Lin return false; 83649a5ba1cSMengdong Lin } 83749a5ba1cSMengdong Lin 8386f2f1ff0SMengdong Lin static int soc_bind_dai_link(struct snd_soc_card *card, 8396f2f1ff0SMengdong Lin struct snd_soc_dai_link *dai_link) 840db2a4165SFrank Mandarino { 8411a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd; 84288bd870fSBenoit Cousson struct snd_soc_dai_link_component *codecs = dai_link->codecs; 84314621c7eSLars-Peter Clausen struct snd_soc_dai_link_component cpu_dai_component; 84490be711eSKuninori Morimoto struct snd_soc_component *component; 8451a497983SMengdong Lin struct snd_soc_dai **codec_dais; 84606859fcaSCharles Keepax struct device_node *platform_of_node; 847848dd8beSMark Brown const char *platform_name; 84888bd870fSBenoit Cousson int i; 849db2a4165SFrank Mandarino 850a655de80SLiam Girdwood if (dai_link->ignore) 851a655de80SLiam Girdwood return 0; 852a655de80SLiam Girdwood 8536f2f1ff0SMengdong Lin dev_dbg(card->dev, "ASoC: binding %s\n", dai_link->name); 8546308419aSMark Brown 85549a5ba1cSMengdong Lin if (soc_is_dai_link_bound(card, dai_link)) { 85649a5ba1cSMengdong Lin dev_dbg(card->dev, "ASoC: dai link %s already bound\n", 85749a5ba1cSMengdong Lin dai_link->name); 85849a5ba1cSMengdong Lin return 0; 85949a5ba1cSMengdong Lin } 860db2a4165SFrank Mandarino 861513cb311SSudip Mukherjee rtd = soc_new_pcm_runtime(card, dai_link); 862513cb311SSudip Mukherjee if (!rtd) 863513cb311SSudip Mukherjee return -ENOMEM; 864513cb311SSudip Mukherjee 86514621c7eSLars-Peter Clausen cpu_dai_component.name = dai_link->cpu_name; 86614621c7eSLars-Peter Clausen cpu_dai_component.of_node = dai_link->cpu_of_node; 86714621c7eSLars-Peter Clausen cpu_dai_component.dai_name = dai_link->cpu_dai_name; 86814621c7eSLars-Peter Clausen rtd->cpu_dai = snd_soc_find_dai(&cpu_dai_component); 869b19e6e7bSMark Brown if (!rtd->cpu_dai) { 8706b490879SMartin Hundebøll dev_info(card->dev, "ASoC: CPU DAI %s not registered\n", 871f0fba2adSLiam Girdwood dai_link->cpu_dai_name); 8721a497983SMengdong Lin goto _err_defer; 873c5af3a2eSMark Brown } 87490be711eSKuninori Morimoto snd_soc_rtdcom_add(rtd, rtd->cpu_dai->component); 875c5af3a2eSMark Brown 87688bd870fSBenoit Cousson rtd->num_codecs = dai_link->num_codecs; 87788bd870fSBenoit Cousson 87888bd870fSBenoit Cousson /* Find CODEC from registered CODECs */ 8790b7990e3SKuninori Morimoto /* we can use for_each_rtd_codec_dai() after this */ 8801a497983SMengdong Lin codec_dais = rtd->codec_dais; 88188bd870fSBenoit Cousson for (i = 0; i < rtd->num_codecs; i++) { 88214621c7eSLars-Peter Clausen codec_dais[i] = snd_soc_find_dai(&codecs[i]); 88388bd870fSBenoit Cousson if (!codec_dais[i]) { 88412023a9aSMisael Lopez Cruz dev_err(card->dev, "ASoC: CODEC DAI %s not registered\n", 88588bd870fSBenoit Cousson codecs[i].dai_name); 8861a497983SMengdong Lin goto _err_defer; 88712023a9aSMisael Lopez Cruz } 88890be711eSKuninori Morimoto snd_soc_rtdcom_add(rtd, codec_dais[i]->component); 88988bd870fSBenoit Cousson } 89088bd870fSBenoit Cousson 89188bd870fSBenoit Cousson /* Single codec links expect codec and codec_dai in runtime data */ 89288bd870fSBenoit Cousson rtd->codec_dai = codec_dais[0]; 89312023a9aSMisael Lopez Cruz 894848dd8beSMark Brown /* if there's no platform we match on the empty platform */ 895daecf46eSKuninori Morimoto platform_name = dai_link->platform->name; 896daecf46eSKuninori Morimoto if (!platform_name && !dai_link->platform->of_node) 897848dd8beSMark Brown platform_name = "snd-soc-dummy"; 898848dd8beSMark Brown 899b19e6e7bSMark Brown /* find one from the set of registered platforms */ 90090be711eSKuninori Morimoto list_for_each_entry(component, &component_list, list) { 90190be711eSKuninori Morimoto platform_of_node = component->dev->of_node; 90290be711eSKuninori Morimoto if (!platform_of_node && component->dev->parent->of_node) 90390be711eSKuninori Morimoto platform_of_node = component->dev->parent->of_node; 90490be711eSKuninori Morimoto 905daecf46eSKuninori Morimoto if (dai_link->platform->of_node) { 906daecf46eSKuninori Morimoto if (platform_of_node != dai_link->platform->of_node) 90790be711eSKuninori Morimoto continue; 90890be711eSKuninori Morimoto } else { 90990be711eSKuninori Morimoto if (strcmp(component->name, platform_name)) 91090be711eSKuninori Morimoto continue; 91190be711eSKuninori Morimoto } 91290be711eSKuninori Morimoto 91390be711eSKuninori Morimoto snd_soc_rtdcom_add(rtd, component); 91490be711eSKuninori Morimoto } 91590be711eSKuninori Morimoto 9161a497983SMengdong Lin soc_add_pcm_runtime(card, rtd); 917b19e6e7bSMark Brown return 0; 9181a497983SMengdong Lin 9191a497983SMengdong Lin _err_defer: 9201a497983SMengdong Lin soc_free_pcm_runtime(rtd); 9211a497983SMengdong Lin return -EPROBE_DEFER; 9226b05eda6SMark Brown } 9236b05eda6SMark Brown 924f1d45cc3SLars-Peter Clausen static void soc_remove_component(struct snd_soc_component *component) 925d12cd198SStephen Warren { 926abd31b32SLars-Peter Clausen if (!component->card) 92770090bbbSLars-Peter Clausen return; 928d12cd198SStephen Warren 929d9fc4063SKuninori Morimoto list_del(&component->card_list); 930d12cd198SStephen Warren 931999f7f5aSKuninori Morimoto if (component->driver->remove) 932999f7f5aSKuninori Morimoto component->driver->remove(component); 933d12cd198SStephen Warren 934f1d45cc3SLars-Peter Clausen snd_soc_dapm_free(snd_soc_component_get_dapm(component)); 935d12cd198SStephen Warren 936f1d45cc3SLars-Peter Clausen soc_cleanup_component_debugfs(component); 937abd31b32SLars-Peter Clausen component->card = NULL; 938f1d45cc3SLars-Peter Clausen module_put(component->dev->driver->owner); 939d12cd198SStephen Warren } 940d12cd198SStephen Warren 941e60cd14fSLars-Peter Clausen static void soc_remove_dai(struct snd_soc_dai *dai, int order) 942589c3563SJarkko Nikula { 943589c3563SJarkko Nikula int err; 944589c3563SJarkko Nikula 945*2eda3cb1SKuninori Morimoto if (!dai || !dai->probed || 946*2eda3cb1SKuninori Morimoto dai->driver->remove_order != order) 947*2eda3cb1SKuninori Morimoto return; 948*2eda3cb1SKuninori Morimoto 949e60cd14fSLars-Peter Clausen if (dai->driver->remove) { 950e60cd14fSLars-Peter Clausen err = dai->driver->remove(dai); 951589c3563SJarkko Nikula if (err < 0) 952e60cd14fSLars-Peter Clausen dev_err(dai->dev, 953b0aa88afSMisael Lopez Cruz "ASoC: failed to remove %s: %d\n", 954e60cd14fSLars-Peter Clausen dai->name, err); 955b0aa88afSMisael Lopez Cruz } 956e60cd14fSLars-Peter Clausen dai->probed = 0; 957b0aa88afSMisael Lopez Cruz } 958b0aa88afSMisael Lopez Cruz 9591a497983SMengdong Lin static void soc_remove_link_dais(struct snd_soc_card *card, 9601a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd, int order) 961f0fba2adSLiam Girdwood { 962e60cd14fSLars-Peter Clausen int i; 9630b7990e3SKuninori Morimoto struct snd_soc_dai *codec_dai; 964f0fba2adSLiam Girdwood 965f0fba2adSLiam Girdwood /* unregister the rtd device */ 966f0fba2adSLiam Girdwood if (rtd->dev_registered) { 96736ae1a96SMark Brown device_unregister(rtd->dev); 968f0fba2adSLiam Girdwood rtd->dev_registered = 0; 96902a06d30SBarry Song } 97002a06d30SBarry Song 971f0fba2adSLiam Girdwood /* remove the CODEC DAI */ 9720b7990e3SKuninori Morimoto for_each_rtd_codec_dai(rtd, i, codec_dai) 9730b7990e3SKuninori Morimoto soc_remove_dai(codec_dai, order); 974f0fba2adSLiam Girdwood 975e60cd14fSLars-Peter Clausen soc_remove_dai(rtd->cpu_dai, order); 976f0fba2adSLiam Girdwood } 977f0fba2adSLiam Girdwood 9781a497983SMengdong Lin static void soc_remove_link_components(struct snd_soc_card *card, 9791a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd, int order) 98062ae68faSStephen Warren { 98161aca564SLars-Peter Clausen struct snd_soc_component *component; 98290be711eSKuninori Morimoto struct snd_soc_rtdcom_list *rtdcom; 98362ae68faSStephen Warren 98490be711eSKuninori Morimoto for_each_rtdcom(rtd, rtdcom) { 98590be711eSKuninori Morimoto component = rtdcom->component; 98662ae68faSStephen Warren 98770090bbbSLars-Peter Clausen if (component->driver->remove_order == order) 98861aca564SLars-Peter Clausen soc_remove_component(component); 98962ae68faSStephen Warren } 99062ae68faSStephen Warren } 99162ae68faSStephen Warren 9920671fd8eSKuninori Morimoto static void soc_remove_dai_links(struct snd_soc_card *card) 9930671fd8eSKuninori Morimoto { 9941a497983SMengdong Lin int order; 9951a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd; 996f8f80361SMengdong Lin struct snd_soc_dai_link *link, *_link; 9970671fd8eSKuninori Morimoto 9980168bf0dSLiam Girdwood for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; 9990168bf0dSLiam Girdwood order++) { 10001a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) 10011a497983SMengdong Lin soc_remove_link_dais(card, rtd, order); 10020168bf0dSLiam Girdwood } 100362ae68faSStephen Warren 100462ae68faSStephen Warren for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; 100562ae68faSStephen Warren order++) { 10061a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) 10071a497983SMengdong Lin soc_remove_link_components(card, rtd, order); 100862ae68faSStephen Warren } 100962ae68faSStephen Warren 1010f8f80361SMengdong Lin list_for_each_entry_safe(link, _link, &card->dai_link_list, list) { 1011f8f80361SMengdong Lin if (link->dobj.type == SND_SOC_DOBJ_DAI_LINK) 1012f8f80361SMengdong Lin dev_warn(card->dev, "Topology forgot to remove link %s?\n", 1013f8f80361SMengdong Lin link->name); 1014f8f80361SMengdong Lin 1015f8f80361SMengdong Lin list_del(&link->list); 1016f8f80361SMengdong Lin card->num_dai_links--; 10170671fd8eSKuninori Morimoto } 10180671fd8eSKuninori Morimoto } 10190671fd8eSKuninori Morimoto 1020daecf46eSKuninori Morimoto static int snd_soc_init_platform(struct snd_soc_card *card, 1021daecf46eSKuninori Morimoto struct snd_soc_dai_link *dai_link) 1022daecf46eSKuninori Morimoto { 1023daecf46eSKuninori Morimoto /* 1024daecf46eSKuninori Morimoto * FIXME 1025daecf46eSKuninori Morimoto * 1026daecf46eSKuninori Morimoto * this function should be removed in the future 1027daecf46eSKuninori Morimoto */ 1028daecf46eSKuninori Morimoto /* convert Legacy platform link */ 1029daecf46eSKuninori Morimoto if (dai_link->platform) 1030daecf46eSKuninori Morimoto return 0; 1031daecf46eSKuninori Morimoto 1032daecf46eSKuninori Morimoto dai_link->platform = devm_kzalloc(card->dev, 1033daecf46eSKuninori Morimoto sizeof(struct snd_soc_dai_link_component), 1034daecf46eSKuninori Morimoto GFP_KERNEL); 1035daecf46eSKuninori Morimoto if (!dai_link->platform) 1036daecf46eSKuninori Morimoto return -ENOMEM; 1037daecf46eSKuninori Morimoto 1038daecf46eSKuninori Morimoto dai_link->platform->name = dai_link->platform_name; 1039daecf46eSKuninori Morimoto dai_link->platform->of_node = dai_link->platform_of_node; 1040daecf46eSKuninori Morimoto dai_link->platform->dai_name = NULL; 1041daecf46eSKuninori Morimoto 1042daecf46eSKuninori Morimoto return 0; 1043daecf46eSKuninori Morimoto } 1044daecf46eSKuninori Morimoto 1045923c5e61SMengdong Lin static int snd_soc_init_multicodec(struct snd_soc_card *card, 1046923c5e61SMengdong Lin struct snd_soc_dai_link *dai_link) 1047923c5e61SMengdong Lin { 1048923c5e61SMengdong Lin /* Legacy codec/codec_dai link is a single entry in multicodec */ 1049923c5e61SMengdong Lin if (dai_link->codec_name || dai_link->codec_of_node || 1050923c5e61SMengdong Lin dai_link->codec_dai_name) { 1051923c5e61SMengdong Lin dai_link->num_codecs = 1; 1052923c5e61SMengdong Lin 1053923c5e61SMengdong Lin dai_link->codecs = devm_kzalloc(card->dev, 1054923c5e61SMengdong Lin sizeof(struct snd_soc_dai_link_component), 1055923c5e61SMengdong Lin GFP_KERNEL); 1056923c5e61SMengdong Lin if (!dai_link->codecs) 1057923c5e61SMengdong Lin return -ENOMEM; 1058923c5e61SMengdong Lin 1059923c5e61SMengdong Lin dai_link->codecs[0].name = dai_link->codec_name; 1060923c5e61SMengdong Lin dai_link->codecs[0].of_node = dai_link->codec_of_node; 1061923c5e61SMengdong Lin dai_link->codecs[0].dai_name = dai_link->codec_dai_name; 1062923c5e61SMengdong Lin } 1063923c5e61SMengdong Lin 1064923c5e61SMengdong Lin if (!dai_link->codecs) { 1065923c5e61SMengdong Lin dev_err(card->dev, "ASoC: DAI link has no CODECs\n"); 1066923c5e61SMengdong Lin return -EINVAL; 1067923c5e61SMengdong Lin } 1068923c5e61SMengdong Lin 1069923c5e61SMengdong Lin return 0; 1070923c5e61SMengdong Lin } 1071923c5e61SMengdong Lin 1072923c5e61SMengdong Lin static int soc_init_dai_link(struct snd_soc_card *card, 1073923c5e61SMengdong Lin struct snd_soc_dai_link *link) 1074923c5e61SMengdong Lin { 1075923c5e61SMengdong Lin int i, ret; 10763db769f1SKuninori Morimoto struct snd_soc_dai_link_component *codec; 1077923c5e61SMengdong Lin 1078daecf46eSKuninori Morimoto ret = snd_soc_init_platform(card, link); 1079daecf46eSKuninori Morimoto if (ret) { 1080daecf46eSKuninori Morimoto dev_err(card->dev, "ASoC: failed to init multiplatform\n"); 1081daecf46eSKuninori Morimoto return ret; 1082daecf46eSKuninori Morimoto } 1083daecf46eSKuninori Morimoto 1084923c5e61SMengdong Lin ret = snd_soc_init_multicodec(card, link); 1085923c5e61SMengdong Lin if (ret) { 1086923c5e61SMengdong Lin dev_err(card->dev, "ASoC: failed to init multicodec\n"); 1087923c5e61SMengdong Lin return ret; 1088923c5e61SMengdong Lin } 1089923c5e61SMengdong Lin 10903db769f1SKuninori Morimoto for_each_link_codecs(link, i, codec) { 1091923c5e61SMengdong Lin /* 1092923c5e61SMengdong Lin * Codec must be specified by 1 of name or OF node, 1093923c5e61SMengdong Lin * not both or neither. 1094923c5e61SMengdong Lin */ 10953db769f1SKuninori Morimoto if (!!codec->name == 10963db769f1SKuninori Morimoto !!codec->of_node) { 1097923c5e61SMengdong Lin dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n", 1098923c5e61SMengdong Lin link->name); 1099923c5e61SMengdong Lin return -EINVAL; 1100923c5e61SMengdong Lin } 1101923c5e61SMengdong Lin /* Codec DAI name must be specified */ 11023db769f1SKuninori Morimoto if (!codec->dai_name) { 1103923c5e61SMengdong Lin dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n", 1104923c5e61SMengdong Lin link->name); 1105923c5e61SMengdong Lin return -EINVAL; 1106923c5e61SMengdong Lin } 1107923c5e61SMengdong Lin } 1108923c5e61SMengdong Lin 1109923c5e61SMengdong Lin /* 1110923c5e61SMengdong Lin * Platform may be specified by either name or OF node, but 1111923c5e61SMengdong Lin * can be left unspecified, and a dummy platform will be used. 1112923c5e61SMengdong Lin */ 1113daecf46eSKuninori Morimoto if (link->platform->name && link->platform->of_node) { 1114923c5e61SMengdong Lin dev_err(card->dev, 1115923c5e61SMengdong Lin "ASoC: Both platform name/of_node are set for %s\n", 1116923c5e61SMengdong Lin link->name); 1117923c5e61SMengdong Lin return -EINVAL; 1118923c5e61SMengdong Lin } 1119923c5e61SMengdong Lin /* 1120923c5e61SMengdong Lin * CPU device may be specified by either name or OF node, but 1121923c5e61SMengdong Lin * can be left unspecified, and will be matched based on DAI 1122923c5e61SMengdong Lin * name alone.. 1123923c5e61SMengdong Lin */ 1124923c5e61SMengdong Lin if (link->cpu_name && link->cpu_of_node) { 1125923c5e61SMengdong Lin dev_err(card->dev, 1126923c5e61SMengdong Lin "ASoC: Neither/both cpu name/of_node are set for %s\n", 1127923c5e61SMengdong Lin link->name); 1128923c5e61SMengdong Lin return -EINVAL; 1129923c5e61SMengdong Lin } 1130923c5e61SMengdong Lin /* 1131923c5e61SMengdong Lin * At least one of CPU DAI name or CPU device name/node must be 1132923c5e61SMengdong Lin * specified 1133923c5e61SMengdong Lin */ 1134923c5e61SMengdong Lin if (!link->cpu_dai_name && 1135923c5e61SMengdong Lin !(link->cpu_name || link->cpu_of_node)) { 1136923c5e61SMengdong Lin dev_err(card->dev, 1137923c5e61SMengdong Lin "ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n", 1138923c5e61SMengdong Lin link->name); 1139923c5e61SMengdong Lin return -EINVAL; 1140923c5e61SMengdong Lin } 1141923c5e61SMengdong Lin 1142923c5e61SMengdong Lin return 0; 1143923c5e61SMengdong Lin } 1144923c5e61SMengdong Lin 1145ef2e8175SKuninori Morimoto void snd_soc_disconnect_sync(struct device *dev) 1146ef2e8175SKuninori Morimoto { 1147ef2e8175SKuninori Morimoto struct snd_soc_component *component = snd_soc_lookup_component(dev, NULL); 1148ef2e8175SKuninori Morimoto 1149ef2e8175SKuninori Morimoto if (!component || !component->card) 1150ef2e8175SKuninori Morimoto return; 1151ef2e8175SKuninori Morimoto 1152ef2e8175SKuninori Morimoto snd_card_disconnect_sync(component->card->snd_card); 1153ef2e8175SKuninori Morimoto } 1154df532185SKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_disconnect_sync); 1155ef2e8175SKuninori Morimoto 1156f8f80361SMengdong Lin /** 1157f8f80361SMengdong Lin * snd_soc_add_dai_link - Add a DAI link dynamically 1158f8f80361SMengdong Lin * @card: The ASoC card to which the DAI link is added 1159f8f80361SMengdong Lin * @dai_link: The new DAI link to add 1160f8f80361SMengdong Lin * 1161f8f80361SMengdong Lin * This function adds a DAI link to the ASoC card's link list. 1162f8f80361SMengdong Lin * 1163f8f80361SMengdong Lin * Note: Topology can use this API to add DAI links when probing the 1164f8f80361SMengdong Lin * topology component. And machine drivers can still define static 1165f8f80361SMengdong Lin * DAI links in dai_link array. 1166f8f80361SMengdong Lin */ 1167f8f80361SMengdong Lin int snd_soc_add_dai_link(struct snd_soc_card *card, 1168f8f80361SMengdong Lin struct snd_soc_dai_link *dai_link) 1169f8f80361SMengdong Lin { 1170f8f80361SMengdong Lin if (dai_link->dobj.type 1171f8f80361SMengdong Lin && dai_link->dobj.type != SND_SOC_DOBJ_DAI_LINK) { 1172f8f80361SMengdong Lin dev_err(card->dev, "Invalid dai link type %d\n", 1173f8f80361SMengdong Lin dai_link->dobj.type); 1174f8f80361SMengdong Lin return -EINVAL; 1175f8f80361SMengdong Lin } 1176f8f80361SMengdong Lin 1177f8f80361SMengdong Lin lockdep_assert_held(&client_mutex); 1178d6f220eaSMengdong Lin /* Notify the machine driver for extra initialization 1179d6f220eaSMengdong Lin * on the link created by topology. 1180d6f220eaSMengdong Lin */ 1181d6f220eaSMengdong Lin if (dai_link->dobj.type && card->add_dai_link) 1182d6f220eaSMengdong Lin card->add_dai_link(card, dai_link); 1183d6f220eaSMengdong Lin 1184f8f80361SMengdong Lin list_add_tail(&dai_link->list, &card->dai_link_list); 1185f8f80361SMengdong Lin card->num_dai_links++; 1186f8f80361SMengdong Lin 1187f8f80361SMengdong Lin return 0; 1188f8f80361SMengdong Lin } 1189f8f80361SMengdong Lin EXPORT_SYMBOL_GPL(snd_soc_add_dai_link); 1190f8f80361SMengdong Lin 1191f8f80361SMengdong Lin /** 1192f8f80361SMengdong Lin * snd_soc_remove_dai_link - Remove a DAI link from the list 1193f8f80361SMengdong Lin * @card: The ASoC card that owns the link 1194f8f80361SMengdong Lin * @dai_link: The DAI link to remove 1195f8f80361SMengdong Lin * 1196f8f80361SMengdong Lin * This function removes a DAI link from the ASoC card's link list. 1197f8f80361SMengdong Lin * 1198f8f80361SMengdong Lin * For DAI links previously added by topology, topology should 1199f8f80361SMengdong Lin * remove them by using the dobj embedded in the link. 1200f8f80361SMengdong Lin */ 1201f8f80361SMengdong Lin void snd_soc_remove_dai_link(struct snd_soc_card *card, 1202f8f80361SMengdong Lin struct snd_soc_dai_link *dai_link) 1203f8f80361SMengdong Lin { 1204f8f80361SMengdong Lin struct snd_soc_dai_link *link, *_link; 1205f8f80361SMengdong Lin 1206f8f80361SMengdong Lin if (dai_link->dobj.type 1207f8f80361SMengdong Lin && dai_link->dobj.type != SND_SOC_DOBJ_DAI_LINK) { 1208f8f80361SMengdong Lin dev_err(card->dev, "Invalid dai link type %d\n", 1209f8f80361SMengdong Lin dai_link->dobj.type); 1210f8f80361SMengdong Lin return; 1211f8f80361SMengdong Lin } 1212f8f80361SMengdong Lin 1213f8f80361SMengdong Lin lockdep_assert_held(&client_mutex); 1214d6f220eaSMengdong Lin /* Notify the machine driver for extra destruction 1215d6f220eaSMengdong Lin * on the link created by topology. 1216d6f220eaSMengdong Lin */ 1217d6f220eaSMengdong Lin if (dai_link->dobj.type && card->remove_dai_link) 1218d6f220eaSMengdong Lin card->remove_dai_link(card, dai_link); 1219d6f220eaSMengdong Lin 1220f8f80361SMengdong Lin list_for_each_entry_safe(link, _link, &card->dai_link_list, list) { 1221f8f80361SMengdong Lin if (link == dai_link) { 1222f8f80361SMengdong Lin list_del(&link->list); 1223f8f80361SMengdong Lin card->num_dai_links--; 1224f8f80361SMengdong Lin return; 1225f8f80361SMengdong Lin } 1226f8f80361SMengdong Lin } 1227f8f80361SMengdong Lin } 1228f8f80361SMengdong Lin EXPORT_SYMBOL_GPL(snd_soc_remove_dai_link); 1229f0fba2adSLiam Girdwood 1230aefba455SJerome Brunet static void soc_set_of_name_prefix(struct snd_soc_component *component) 1231aefba455SJerome Brunet { 1232aefba455SJerome Brunet struct device_node *component_of_node = component->dev->of_node; 1233aefba455SJerome Brunet const char *str; 1234aefba455SJerome Brunet int ret; 1235aefba455SJerome Brunet 1236aefba455SJerome Brunet if (!component_of_node && component->dev->parent) 1237aefba455SJerome Brunet component_of_node = component->dev->parent->of_node; 1238aefba455SJerome Brunet 1239aefba455SJerome Brunet ret = of_property_read_string(component_of_node, "sound-name-prefix", 1240aefba455SJerome Brunet &str); 1241aefba455SJerome Brunet if (!ret) 1242aefba455SJerome Brunet component->name_prefix = str; 1243aefba455SJerome Brunet } 1244aefba455SJerome Brunet 1245ead9b919SJarkko Nikula static void soc_set_name_prefix(struct snd_soc_card *card, 124694f99c87SLars-Peter Clausen struct snd_soc_component *component) 1247ead9b919SJarkko Nikula { 1248ead9b919SJarkko Nikula int i; 1249ead9b919SJarkko Nikula 1250aefba455SJerome Brunet for (i = 0; i < card->num_configs && card->codec_conf; i++) { 1251ff819b83SDimitris Papastamos struct snd_soc_codec_conf *map = &card->codec_conf[i]; 1252b24c539bSCharles Keepax struct device_node *component_of_node = component->dev->of_node; 1253b24c539bSCharles Keepax 1254b24c539bSCharles Keepax if (!component_of_node && component->dev->parent) 1255b24c539bSCharles Keepax component_of_node = component->dev->parent->of_node; 1256b24c539bSCharles Keepax 1257b24c539bSCharles Keepax if (map->of_node && component_of_node != map->of_node) 12583ca041edSSebastian Reichel continue; 125994f99c87SLars-Peter Clausen if (map->dev_name && strcmp(component->name, map->dev_name)) 12603ca041edSSebastian Reichel continue; 126194f99c87SLars-Peter Clausen component->name_prefix = map->name_prefix; 1262aefba455SJerome Brunet return; 1263ead9b919SJarkko Nikula } 1264aefba455SJerome Brunet 1265aefba455SJerome Brunet /* 1266aefba455SJerome Brunet * If there is no configuration table or no match in the table, 1267aefba455SJerome Brunet * check if a prefix is provided in the node 1268aefba455SJerome Brunet */ 1269aefba455SJerome Brunet soc_set_of_name_prefix(component); 1270ead9b919SJarkko Nikula } 1271ead9b919SJarkko Nikula 1272f1d45cc3SLars-Peter Clausen static int soc_probe_component(struct snd_soc_card *card, 1273f1d45cc3SLars-Peter Clausen struct snd_soc_component *component) 1274589c3563SJarkko Nikula { 1275f1d45cc3SLars-Peter Clausen struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); 1276888df395SMark Brown struct snd_soc_dai *dai; 1277f1d45cc3SLars-Peter Clausen int ret; 1278589c3563SJarkko Nikula 12791b7c1231SLars-Peter Clausen if (!strcmp(component->name, "snd-soc-dummy")) 128070090bbbSLars-Peter Clausen return 0; 1281589c3563SJarkko Nikula 1282abd31b32SLars-Peter Clausen if (component->card) { 12831b7c1231SLars-Peter Clausen if (component->card != card) { 12841b7c1231SLars-Peter Clausen dev_err(component->dev, 12851b7c1231SLars-Peter Clausen "Trying to bind component to card \"%s\" but is already bound to card \"%s\"\n", 12861b7c1231SLars-Peter Clausen card->name, component->card->name); 12871b7c1231SLars-Peter Clausen return -ENODEV; 12881b7c1231SLars-Peter Clausen } 12891b7c1231SLars-Peter Clausen return 0; 12901b7c1231SLars-Peter Clausen } 12911b7c1231SLars-Peter Clausen 1292abd31b32SLars-Peter Clausen if (!try_module_get(component->dev->driver->owner)) 1293abd31b32SLars-Peter Clausen return -ENODEV; 1294abd31b32SLars-Peter Clausen 1295f1d45cc3SLars-Peter Clausen component->card = card; 1296f1d45cc3SLars-Peter Clausen dapm->card = card; 1297f1d45cc3SLars-Peter Clausen soc_set_name_prefix(card, component); 1298589c3563SJarkko Nikula 1299f1d45cc3SLars-Peter Clausen soc_init_component_debugfs(component); 1300d5d1e0beSLars-Peter Clausen 1301688d0ebfSKuninori Morimoto if (component->driver->dapm_widgets) { 1302688d0ebfSKuninori Morimoto ret = snd_soc_dapm_new_controls(dapm, 1303688d0ebfSKuninori Morimoto component->driver->dapm_widgets, 1304688d0ebfSKuninori Morimoto component->driver->num_dapm_widgets); 130577530150SLars-Peter Clausen 1306b318ad50SNariman Poushin if (ret != 0) { 1307f1d45cc3SLars-Peter Clausen dev_err(component->dev, 1308b318ad50SNariman Poushin "Failed to create new controls %d\n", ret); 1309b318ad50SNariman Poushin goto err_probe; 1310b318ad50SNariman Poushin } 1311b318ad50SNariman Poushin } 1312b318ad50SNariman Poushin 13130634814fSLars-Peter Clausen list_for_each_entry(dai, &component->dai_list, list) { 13140634814fSLars-Peter Clausen ret = snd_soc_dapm_new_dai_widgets(dapm, dai); 1315261edc70SNariman Poushin if (ret != 0) { 1316f1d45cc3SLars-Peter Clausen dev_err(component->dev, 1317261edc70SNariman Poushin "Failed to create DAI widgets %d\n", ret); 1318261edc70SNariman Poushin goto err_probe; 1319261edc70SNariman Poushin } 1320261edc70SNariman Poushin } 1321888df395SMark Brown 1322999f7f5aSKuninori Morimoto if (component->driver->probe) { 1323999f7f5aSKuninori Morimoto ret = component->driver->probe(component); 1324589c3563SJarkko Nikula if (ret < 0) { 1325f1d45cc3SLars-Peter Clausen dev_err(component->dev, 1326f1d45cc3SLars-Peter Clausen "ASoC: failed to probe component %d\n", ret); 132770d29331SJarkko Nikula goto err_probe; 1328589c3563SJarkko Nikula } 1329cb2cf612SLiam Girdwood 1330f1d45cc3SLars-Peter Clausen WARN(dapm->idle_bias_off && 1331f1d45cc3SLars-Peter Clausen dapm->bias_level != SND_SOC_BIAS_OFF, 1332956245e9SLiam Girdwood "codec %s can not start from non-off bias with idle_bias_off==1\n", 1333f1d45cc3SLars-Peter Clausen component->name); 1334956245e9SLiam Girdwood } 1335956245e9SLiam Girdwood 1336f2ed6b07SMengdong Lin /* machine specific init */ 1337f2ed6b07SMengdong Lin if (component->init) { 1338f2ed6b07SMengdong Lin ret = component->init(component); 1339f2ed6b07SMengdong Lin if (ret < 0) { 1340f2ed6b07SMengdong Lin dev_err(component->dev, 1341f2ed6b07SMengdong Lin "Failed to do machine specific init %d\n", ret); 1342f2ed6b07SMengdong Lin goto err_probe; 1343f2ed6b07SMengdong Lin } 1344f2ed6b07SMengdong Lin } 1345f2ed6b07SMengdong Lin 1346b8972bf0SKuninori Morimoto if (component->driver->controls) 1347b8972bf0SKuninori Morimoto snd_soc_add_component_controls(component, 1348b8972bf0SKuninori Morimoto component->driver->controls, 1349b8972bf0SKuninori Morimoto component->driver->num_controls); 13506969b2baSKuninori Morimoto if (component->driver->dapm_routes) 13516969b2baSKuninori Morimoto snd_soc_dapm_add_routes(dapm, 13526969b2baSKuninori Morimoto component->driver->dapm_routes, 13536969b2baSKuninori Morimoto component->driver->num_dapm_routes); 1354956245e9SLiam Girdwood 1355f1d45cc3SLars-Peter Clausen list_add(&dapm->list, &card->dapm_list); 1356d9fc4063SKuninori Morimoto list_add(&component->card_list, &card->component_dev_list); 1357956245e9SLiam Girdwood 1358956245e9SLiam Girdwood return 0; 1359956245e9SLiam Girdwood 1360956245e9SLiam Girdwood err_probe: 1361f1d45cc3SLars-Peter Clausen soc_cleanup_component_debugfs(component); 1362abd31b32SLars-Peter Clausen component->card = NULL; 1363f1d45cc3SLars-Peter Clausen module_put(component->dev->driver->owner); 1364956245e9SLiam Girdwood 1365956245e9SLiam Girdwood return ret; 1366956245e9SLiam Girdwood } 1367956245e9SLiam Girdwood 136836ae1a96SMark Brown static void rtd_release(struct device *dev) 136936ae1a96SMark Brown { 137036ae1a96SMark Brown kfree(dev); 137136ae1a96SMark Brown } 1372f0fba2adSLiam Girdwood 13735f3484acSLars-Peter Clausen static int soc_post_component_init(struct snd_soc_pcm_runtime *rtd, 13745f3484acSLars-Peter Clausen const char *name) 1375503ae5e0SMisael Lopez Cruz { 1376589c3563SJarkko Nikula int ret = 0; 1377589c3563SJarkko Nikula 1378589c3563SJarkko Nikula /* register the rtd device */ 137936ae1a96SMark Brown rtd->dev = kzalloc(sizeof(struct device), GFP_KERNEL); 138036ae1a96SMark Brown if (!rtd->dev) 138136ae1a96SMark Brown return -ENOMEM; 138236ae1a96SMark Brown device_initialize(rtd->dev); 13835f3484acSLars-Peter Clausen rtd->dev->parent = rtd->card->dev; 138436ae1a96SMark Brown rtd->dev->release = rtd_release; 1385d29697dcSTakashi Iwai rtd->dev->groups = soc_dev_attr_groups; 1386f294afedSLars-Peter Clausen dev_set_name(rtd->dev, "%s", name); 138736ae1a96SMark Brown dev_set_drvdata(rtd->dev, rtd); 1388b8c0dab9SLiam Girdwood mutex_init(&rtd->pcm_mutex); 138901d7584cSLiam Girdwood INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].be_clients); 139001d7584cSLiam Girdwood INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].be_clients); 139101d7584cSLiam Girdwood INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].fe_clients); 139201d7584cSLiam Girdwood INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].fe_clients); 139336ae1a96SMark Brown ret = device_add(rtd->dev); 1394589c3563SJarkko Nikula if (ret < 0) { 1395865df9cbSChuansheng Liu /* calling put_device() here to free the rtd->dev */ 1396865df9cbSChuansheng Liu put_device(rtd->dev); 13975f3484acSLars-Peter Clausen dev_err(rtd->card->dev, 1398f110bfc7SLiam Girdwood "ASoC: failed to register runtime device: %d\n", ret); 1399589c3563SJarkko Nikula return ret; 1400589c3563SJarkko Nikula } 1401589c3563SJarkko Nikula rtd->dev_registered = 1; 1402589c3563SJarkko Nikula return 0; 1403589c3563SJarkko Nikula } 1404589c3563SJarkko Nikula 14051a497983SMengdong Lin static int soc_probe_link_components(struct snd_soc_card *card, 14061a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd, 140762ae68faSStephen Warren int order) 140862ae68faSStephen Warren { 1409f1d45cc3SLars-Peter Clausen struct snd_soc_component *component; 141090be711eSKuninori Morimoto struct snd_soc_rtdcom_list *rtdcom; 141190be711eSKuninori Morimoto int ret; 141262ae68faSStephen Warren 141390be711eSKuninori Morimoto for_each_rtdcom(rtd, rtdcom) { 141490be711eSKuninori Morimoto component = rtdcom->component; 141590be711eSKuninori Morimoto 141670090bbbSLars-Peter Clausen if (component->driver->probe_order == order) { 1417f1d45cc3SLars-Peter Clausen ret = soc_probe_component(card, component); 141862ae68faSStephen Warren if (ret < 0) 141962ae68faSStephen Warren return ret; 142062ae68faSStephen Warren } 142162ae68faSStephen Warren } 142262ae68faSStephen Warren 142362ae68faSStephen Warren return 0; 142462ae68faSStephen Warren } 142562ae68faSStephen Warren 14268e2be562SLars-Peter Clausen static int soc_probe_dai(struct snd_soc_dai *dai, int order) 1427b0aa88afSMisael Lopez Cruz { 14287a2ccad5SKuninori Morimoto if (dai->probed || 14297a2ccad5SKuninori Morimoto dai->driver->probe_order != order) 14307a2ccad5SKuninori Morimoto return 0; 1431b0aa88afSMisael Lopez Cruz 14328e2be562SLars-Peter Clausen if (dai->driver->probe) { 14337a2ccad5SKuninori Morimoto int ret = dai->driver->probe(dai); 1434b0aa88afSMisael Lopez Cruz if (ret < 0) { 14357a2ccad5SKuninori Morimoto dev_err(dai->dev, "ASoC: failed to probe DAI %s: %d\n", 14368e2be562SLars-Peter Clausen dai->name, ret); 1437b0aa88afSMisael Lopez Cruz return ret; 1438b0aa88afSMisael Lopez Cruz } 1439b0aa88afSMisael Lopez Cruz } 1440b0aa88afSMisael Lopez Cruz 14418e2be562SLars-Peter Clausen dai->probed = 1; 1442b0aa88afSMisael Lopez Cruz 1443b0aa88afSMisael Lopez Cruz return 0; 1444b0aa88afSMisael Lopez Cruz } 1445b0aa88afSMisael Lopez Cruz 144625f7b701SArnaud Pouliquen static int soc_link_dai_pcm_new(struct snd_soc_dai **dais, int num_dais, 144725f7b701SArnaud Pouliquen struct snd_soc_pcm_runtime *rtd) 144825f7b701SArnaud Pouliquen { 144925f7b701SArnaud Pouliquen int i, ret = 0; 145025f7b701SArnaud Pouliquen 145125f7b701SArnaud Pouliquen for (i = 0; i < num_dais; ++i) { 145225f7b701SArnaud Pouliquen struct snd_soc_dai_driver *drv = dais[i]->driver; 145325f7b701SArnaud Pouliquen 145425f7b701SArnaud Pouliquen if (!rtd->dai_link->no_pcm && drv->pcm_new) 145525f7b701SArnaud Pouliquen ret = drv->pcm_new(rtd, dais[i]); 145625f7b701SArnaud Pouliquen if (ret < 0) { 145725f7b701SArnaud Pouliquen dev_err(dais[i]->dev, 145825f7b701SArnaud Pouliquen "ASoC: Failed to bind %s with pcm device\n", 145925f7b701SArnaud Pouliquen dais[i]->name); 146025f7b701SArnaud Pouliquen return ret; 146125f7b701SArnaud Pouliquen } 146225f7b701SArnaud Pouliquen } 146325f7b701SArnaud Pouliquen 146425f7b701SArnaud Pouliquen return 0; 146525f7b701SArnaud Pouliquen } 146625f7b701SArnaud Pouliquen 14671a497983SMengdong Lin static int soc_probe_link_dais(struct snd_soc_card *card, 14681a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd, int order) 1469f0fba2adSLiam Girdwood { 14701a497983SMengdong Lin struct snd_soc_dai_link *dai_link = rtd->dai_link; 1471c74184edSMark Brown struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 1472a655de80SLiam Girdwood struct snd_soc_rtdcom_list *rtdcom; 1473a655de80SLiam Girdwood struct snd_soc_component *component; 14740b7990e3SKuninori Morimoto struct snd_soc_dai *codec_dai; 1475a655de80SLiam Girdwood int i, ret, num; 1476f0fba2adSLiam Girdwood 1477f110bfc7SLiam Girdwood dev_dbg(card->dev, "ASoC: probe %s dai link %d late %d\n", 14781a497983SMengdong Lin card->name, rtd->num, order); 1479f0fba2adSLiam Girdwood 1480f0fba2adSLiam Girdwood /* set default power off timeout */ 1481f0fba2adSLiam Girdwood rtd->pmdown_time = pmdown_time; 1482f0fba2adSLiam Girdwood 14838e2be562SLars-Peter Clausen ret = soc_probe_dai(cpu_dai, order); 14848e2be562SLars-Peter Clausen if (ret) 1485f0fba2adSLiam Girdwood return ret; 1486f0fba2adSLiam Girdwood 1487f0fba2adSLiam Girdwood /* probe the CODEC DAI */ 14880b7990e3SKuninori Morimoto for_each_rtd_codec_dai(rtd, i, codec_dai) { 14890b7990e3SKuninori Morimoto ret = soc_probe_dai(codec_dai, order); 1490b0aa88afSMisael Lopez Cruz if (ret) 1491f0fba2adSLiam Girdwood return ret; 149288bd870fSBenoit Cousson } 1493f0fba2adSLiam Girdwood 14940168bf0dSLiam Girdwood /* complete DAI probe during last probe */ 14950168bf0dSLiam Girdwood if (order != SND_SOC_COMP_ORDER_LAST) 14960168bf0dSLiam Girdwood return 0; 14970168bf0dSLiam Girdwood 14985f3484acSLars-Peter Clausen /* do machine specific initialization */ 14995f3484acSLars-Peter Clausen if (dai_link->init) { 15005f3484acSLars-Peter Clausen ret = dai_link->init(rtd); 15015f3484acSLars-Peter Clausen if (ret < 0) { 15025f3484acSLars-Peter Clausen dev_err(card->dev, "ASoC: failed to init %s: %d\n", 15035f3484acSLars-Peter Clausen dai_link->name, ret); 15045f3484acSLars-Peter Clausen return ret; 15055f3484acSLars-Peter Clausen } 15065f3484acSLars-Peter Clausen } 15075f3484acSLars-Peter Clausen 1508a5053a8eSKuninori Morimoto if (dai_link->dai_fmt) 1509a5053a8eSKuninori Morimoto snd_soc_runtime_set_dai_fmt(rtd, dai_link->dai_fmt); 1510a5053a8eSKuninori Morimoto 15115f3484acSLars-Peter Clausen ret = soc_post_component_init(rtd, dai_link->name); 1512589c3563SJarkko Nikula if (ret) 1513f0fba2adSLiam Girdwood return ret; 1514f0fba2adSLiam Girdwood 15155f3484acSLars-Peter Clausen #ifdef CONFIG_DEBUG_FS 15165f3484acSLars-Peter Clausen /* add DPCM sysfs entries */ 15172e55b90aSLars-Peter Clausen if (dai_link->dynamic) 15182e55b90aSLars-Peter Clausen soc_dpcm_debugfs_add(rtd); 15195f3484acSLars-Peter Clausen #endif 15205f3484acSLars-Peter Clausen 1521a655de80SLiam Girdwood num = rtd->num; 1522a655de80SLiam Girdwood 1523a655de80SLiam Girdwood /* 1524a655de80SLiam Girdwood * most drivers will register their PCMs using DAI link ordering but 1525a655de80SLiam Girdwood * topology based drivers can use the DAI link id field to set PCM 1526a655de80SLiam Girdwood * device number and then use rtd + a base offset of the BEs. 1527a655de80SLiam Girdwood */ 1528a655de80SLiam Girdwood for_each_rtdcom(rtd, rtdcom) { 1529a655de80SLiam Girdwood component = rtdcom->component; 1530a655de80SLiam Girdwood 1531a655de80SLiam Girdwood if (!component->driver->use_dai_pcm_id) 1532a655de80SLiam Girdwood continue; 1533a655de80SLiam Girdwood 1534a655de80SLiam Girdwood if (rtd->dai_link->no_pcm) 1535a655de80SLiam Girdwood num += component->driver->be_pcm_base; 1536a655de80SLiam Girdwood else 1537a655de80SLiam Girdwood num = rtd->dai_link->id; 1538a655de80SLiam Girdwood } 1539a655de80SLiam Girdwood 15406f0c4226SJie Yang if (cpu_dai->driver->compress_new) { 15411245b700SNamarta Kohli /*create compress_device"*/ 1542a655de80SLiam Girdwood ret = cpu_dai->driver->compress_new(rtd, num); 15431245b700SNamarta Kohli if (ret < 0) { 1544f110bfc7SLiam Girdwood dev_err(card->dev, "ASoC: can't create compress %s\n", 15451245b700SNamarta Kohli dai_link->stream_name); 15461245b700SNamarta Kohli return ret; 15471245b700SNamarta Kohli } 15481245b700SNamarta Kohli } else { 15491245b700SNamarta Kohli 1550c74184edSMark Brown if (!dai_link->params) { 1551f0fba2adSLiam Girdwood /* create the pcm */ 1552a655de80SLiam Girdwood ret = soc_new_pcm(rtd, num); 1553f0fba2adSLiam Girdwood if (ret < 0) { 1554f110bfc7SLiam Girdwood dev_err(card->dev, "ASoC: can't create pcm %s :%d\n", 15550837fc62SFabio Estevam dai_link->stream_name, ret); 1556f0fba2adSLiam Girdwood return ret; 1557f0fba2adSLiam Girdwood } 155825f7b701SArnaud Pouliquen ret = soc_link_dai_pcm_new(&cpu_dai, 1, rtd); 155925f7b701SArnaud Pouliquen if (ret < 0) 156025f7b701SArnaud Pouliquen return ret; 156125f7b701SArnaud Pouliquen ret = soc_link_dai_pcm_new(rtd->codec_dais, 156225f7b701SArnaud Pouliquen rtd->num_codecs, rtd); 156325f7b701SArnaud Pouliquen if (ret < 0) 156425f7b701SArnaud Pouliquen return ret; 1565c74184edSMark Brown } else { 15669d58a077SRichard Fitzgerald INIT_DELAYED_WORK(&rtd->delayed_work, 15679d58a077SRichard Fitzgerald codec2codec_close_delayed_work); 1568c74184edSMark Brown } 1569c74184edSMark Brown } 1570c74184edSMark Brown 1571f0fba2adSLiam Girdwood return 0; 1572f0fba2adSLiam Girdwood } 1573f0fba2adSLiam Girdwood 157444c69bb1SLars-Peter Clausen static int soc_bind_aux_dev(struct snd_soc_card *card, int num) 1575b19e6e7bSMark Brown { 15763ca041edSSebastian Reichel struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num]; 1577f2ed6b07SMengdong Lin struct snd_soc_component *component; 1578f2ed6b07SMengdong Lin const char *name; 1579f2ed6b07SMengdong Lin struct device_node *codec_of_node; 15803ca041edSSebastian Reichel 1581f2ed6b07SMengdong Lin if (aux_dev->codec_of_node || aux_dev->codec_name) { 1582f2ed6b07SMengdong Lin /* codecs, usually analog devices */ 1583f2ed6b07SMengdong Lin name = aux_dev->codec_name; 1584f2ed6b07SMengdong Lin codec_of_node = aux_dev->codec_of_node; 1585f2ed6b07SMengdong Lin component = soc_find_component(codec_of_node, name); 1586f2ed6b07SMengdong Lin if (!component) { 1587f2ed6b07SMengdong Lin if (codec_of_node) 1588f2ed6b07SMengdong Lin name = of_node_full_name(codec_of_node); 1589f2ed6b07SMengdong Lin goto err_defer; 1590f2ed6b07SMengdong Lin } 1591f2ed6b07SMengdong Lin } else if (aux_dev->name) { 1592f2ed6b07SMengdong Lin /* generic components */ 1593f2ed6b07SMengdong Lin name = aux_dev->name; 1594f2ed6b07SMengdong Lin component = soc_find_component(NULL, name); 1595f2ed6b07SMengdong Lin if (!component) 1596f2ed6b07SMengdong Lin goto err_defer; 1597f2ed6b07SMengdong Lin } else { 1598f2ed6b07SMengdong Lin dev_err(card->dev, "ASoC: Invalid auxiliary device\n"); 1599f2ed6b07SMengdong Lin return -EINVAL; 1600f2ed6b07SMengdong Lin } 16013ca041edSSebastian Reichel 1602f2ed6b07SMengdong Lin component->init = aux_dev->init; 1603d2e3a135SSylwester Nawrocki list_add(&component->card_aux_list, &card->aux_comp_list); 16041a653aa4SKuninori Morimoto 1605f2ed6b07SMengdong Lin return 0; 1606f2ed6b07SMengdong Lin 1607f2ed6b07SMengdong Lin err_defer: 160865d9361fSLars-Peter Clausen dev_err(card->dev, "ASoC: %s not registered\n", name); 1609b19e6e7bSMark Brown return -EPROBE_DEFER; 1610b19e6e7bSMark Brown } 1611b19e6e7bSMark Brown 1612f2ed6b07SMengdong Lin static int soc_probe_aux_devices(struct snd_soc_card *card) 1613f2ed6b07SMengdong Lin { 1614991454e1SKuninori Morimoto struct snd_soc_component *comp; 1615f2ed6b07SMengdong Lin int order; 1616f2ed6b07SMengdong Lin int ret; 1617f2ed6b07SMengdong Lin 1618f2ed6b07SMengdong Lin for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; 1619f2ed6b07SMengdong Lin order++) { 1620991454e1SKuninori Morimoto list_for_each_entry(comp, &card->aux_comp_list, card_aux_list) { 1621f2ed6b07SMengdong Lin if (comp->driver->probe_order == order) { 1622f2ed6b07SMengdong Lin ret = soc_probe_component(card, comp); 1623f2ed6b07SMengdong Lin if (ret < 0) { 1624f2ed6b07SMengdong Lin dev_err(card->dev, 1625f2ed6b07SMengdong Lin "ASoC: failed to probe aux component %s %d\n", 1626f2ed6b07SMengdong Lin comp->name, ret); 1627f2ed6b07SMengdong Lin return ret; 1628f2ed6b07SMengdong Lin } 1629f2ed6b07SMengdong Lin } 1630f2ed6b07SMengdong Lin } 1631f2ed6b07SMengdong Lin } 163265d9361fSLars-Peter Clausen 163344c69bb1SLars-Peter Clausen return 0; 16343ca041edSSebastian Reichel } 16352eea392dSJarkko Nikula 1636f2ed6b07SMengdong Lin static void soc_remove_aux_devices(struct snd_soc_card *card) 163744c69bb1SLars-Peter Clausen { 1638f2ed6b07SMengdong Lin struct snd_soc_component *comp, *_comp; 1639f2ed6b07SMengdong Lin int order; 164044c69bb1SLars-Peter Clausen 1641f2ed6b07SMengdong Lin for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; 1642f2ed6b07SMengdong Lin order++) { 1643f2ed6b07SMengdong Lin list_for_each_entry_safe(comp, _comp, 1644991454e1SKuninori Morimoto &card->aux_comp_list, card_aux_list) { 16451a653aa4SKuninori Morimoto 1646f2ed6b07SMengdong Lin if (comp->driver->remove_order == order) { 1647f2ed6b07SMengdong Lin soc_remove_component(comp); 1648991454e1SKuninori Morimoto /* remove it from the card's aux_comp_list */ 1649991454e1SKuninori Morimoto list_del(&comp->card_aux_list); 16502eea392dSJarkko Nikula } 16515f3484acSLars-Peter Clausen } 16522eea392dSJarkko Nikula } 16532eea392dSJarkko Nikula } 16542eea392dSJarkko Nikula 1655ce64c8b9SLars-Peter Clausen /** 1656ce64c8b9SLars-Peter Clausen * snd_soc_runtime_set_dai_fmt() - Change DAI link format for a ASoC runtime 1657ce64c8b9SLars-Peter Clausen * @rtd: The runtime for which the DAI link format should be changed 1658ce64c8b9SLars-Peter Clausen * @dai_fmt: The new DAI link format 1659ce64c8b9SLars-Peter Clausen * 1660ce64c8b9SLars-Peter Clausen * This function updates the DAI link format for all DAIs connected to the DAI 1661ce64c8b9SLars-Peter Clausen * link for the specified runtime. 1662ce64c8b9SLars-Peter Clausen * 1663ce64c8b9SLars-Peter Clausen * Note: For setups with a static format set the dai_fmt field in the 1664ce64c8b9SLars-Peter Clausen * corresponding snd_dai_link struct instead of using this function. 1665ce64c8b9SLars-Peter Clausen * 1666ce64c8b9SLars-Peter Clausen * Returns 0 on success, otherwise a negative error code. 1667ce64c8b9SLars-Peter Clausen */ 1668ce64c8b9SLars-Peter Clausen int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd, 1669ce64c8b9SLars-Peter Clausen unsigned int dai_fmt) 1670ce64c8b9SLars-Peter Clausen { 1671ce64c8b9SLars-Peter Clausen struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 16720b7990e3SKuninori Morimoto struct snd_soc_dai *codec_dai; 1673ce64c8b9SLars-Peter Clausen unsigned int i; 1674ce64c8b9SLars-Peter Clausen int ret; 1675ce64c8b9SLars-Peter Clausen 16760b7990e3SKuninori Morimoto for_each_rtd_codec_dai(rtd, i, codec_dai) { 1677ce64c8b9SLars-Peter Clausen ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt); 1678ce64c8b9SLars-Peter Clausen if (ret != 0 && ret != -ENOTSUPP) { 1679ce64c8b9SLars-Peter Clausen dev_warn(codec_dai->dev, 1680ce64c8b9SLars-Peter Clausen "ASoC: Failed to set DAI format: %d\n", ret); 1681ce64c8b9SLars-Peter Clausen return ret; 1682ce64c8b9SLars-Peter Clausen } 1683ce64c8b9SLars-Peter Clausen } 1684ce64c8b9SLars-Peter Clausen 1685ce64c8b9SLars-Peter Clausen /* Flip the polarity for the "CPU" end of a CODEC<->CODEC link */ 1686cb2cf0deSKuninori Morimoto /* the component which has non_legacy_dai_naming is Codec */ 1687999f7f5aSKuninori Morimoto if (cpu_dai->component->driver->non_legacy_dai_naming) { 1688ce64c8b9SLars-Peter Clausen unsigned int inv_dai_fmt; 1689ce64c8b9SLars-Peter Clausen 1690ce64c8b9SLars-Peter Clausen inv_dai_fmt = dai_fmt & ~SND_SOC_DAIFMT_MASTER_MASK; 1691ce64c8b9SLars-Peter Clausen switch (dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) { 1692ce64c8b9SLars-Peter Clausen case SND_SOC_DAIFMT_CBM_CFM: 1693ce64c8b9SLars-Peter Clausen inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; 1694ce64c8b9SLars-Peter Clausen break; 1695ce64c8b9SLars-Peter Clausen case SND_SOC_DAIFMT_CBM_CFS: 1696ce64c8b9SLars-Peter Clausen inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFM; 1697ce64c8b9SLars-Peter Clausen break; 1698ce64c8b9SLars-Peter Clausen case SND_SOC_DAIFMT_CBS_CFM: 1699ce64c8b9SLars-Peter Clausen inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFS; 1700ce64c8b9SLars-Peter Clausen break; 1701ce64c8b9SLars-Peter Clausen case SND_SOC_DAIFMT_CBS_CFS: 1702ce64c8b9SLars-Peter Clausen inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; 1703ce64c8b9SLars-Peter Clausen break; 1704ce64c8b9SLars-Peter Clausen } 1705ce64c8b9SLars-Peter Clausen 1706ce64c8b9SLars-Peter Clausen dai_fmt = inv_dai_fmt; 1707ce64c8b9SLars-Peter Clausen } 1708ce64c8b9SLars-Peter Clausen 1709ce64c8b9SLars-Peter Clausen ret = snd_soc_dai_set_fmt(cpu_dai, dai_fmt); 1710ce64c8b9SLars-Peter Clausen if (ret != 0 && ret != -ENOTSUPP) { 1711ce64c8b9SLars-Peter Clausen dev_warn(cpu_dai->dev, 1712ce64c8b9SLars-Peter Clausen "ASoC: Failed to set DAI format: %d\n", ret); 1713ce64c8b9SLars-Peter Clausen return ret; 1714ce64c8b9SLars-Peter Clausen } 1715ce64c8b9SLars-Peter Clausen 1716ce64c8b9SLars-Peter Clausen return 0; 1717ce64c8b9SLars-Peter Clausen } 1718ddaca25aSLars-Peter Clausen EXPORT_SYMBOL_GPL(snd_soc_runtime_set_dai_fmt); 1719ce64c8b9SLars-Peter Clausen 1720345233d7SLiam Girdwood 17211f5a4535STakashi Iwai #ifdef CONFIG_DMI 1722345233d7SLiam Girdwood /* Trim special characters, and replace '-' with '_' since '-' is used to 1723345233d7SLiam Girdwood * separate different DMI fields in the card long name. Only number and 1724345233d7SLiam Girdwood * alphabet characters and a few separator characters are kept. 1725345233d7SLiam Girdwood */ 1726345233d7SLiam Girdwood static void cleanup_dmi_name(char *name) 1727345233d7SLiam Girdwood { 1728345233d7SLiam Girdwood int i, j = 0; 1729345233d7SLiam Girdwood 1730345233d7SLiam Girdwood for (i = 0; name[i]; i++) { 1731345233d7SLiam Girdwood if (isalnum(name[i]) || (name[i] == '.') 1732345233d7SLiam Girdwood || (name[i] == '_')) 1733345233d7SLiam Girdwood name[j++] = name[i]; 1734345233d7SLiam Girdwood else if (name[i] == '-') 1735345233d7SLiam Girdwood name[j++] = '_'; 1736345233d7SLiam Girdwood } 1737345233d7SLiam Girdwood 1738345233d7SLiam Girdwood name[j] = '\0'; 1739345233d7SLiam Girdwood } 1740345233d7SLiam Girdwood 174198faf436SMengdong Lin /* Check if a DMI field is valid, i.e. not containing any string 174298faf436SMengdong Lin * in the black list. 174398faf436SMengdong Lin */ 174498faf436SMengdong Lin static int is_dmi_valid(const char *field) 174598faf436SMengdong Lin { 174698faf436SMengdong Lin int i = 0; 174798faf436SMengdong Lin 174898faf436SMengdong Lin while (dmi_blacklist[i]) { 174998faf436SMengdong Lin if (strstr(field, dmi_blacklist[i])) 175098faf436SMengdong Lin return 0; 175198faf436SMengdong Lin i++; 175246b5a4d2SWu Fengguang } 175398faf436SMengdong Lin 175498faf436SMengdong Lin return 1; 175598faf436SMengdong Lin } 175698faf436SMengdong Lin 1757345233d7SLiam Girdwood /** 1758345233d7SLiam Girdwood * snd_soc_set_dmi_name() - Register DMI names to card 1759345233d7SLiam Girdwood * @card: The card to register DMI names 1760345233d7SLiam Girdwood * @flavour: The flavour "differentiator" for the card amongst its peers. 1761345233d7SLiam Girdwood * 1762345233d7SLiam Girdwood * An Intel machine driver may be used by many different devices but are 1763345233d7SLiam Girdwood * difficult for userspace to differentiate, since machine drivers ususally 1764345233d7SLiam Girdwood * use their own name as the card short name and leave the card long name 1765345233d7SLiam Girdwood * blank. To differentiate such devices and fix bugs due to lack of 1766345233d7SLiam Girdwood * device-specific configurations, this function allows DMI info to be used 1767345233d7SLiam Girdwood * as the sound card long name, in the format of 1768345233d7SLiam Girdwood * "vendor-product-version-board" 1769345233d7SLiam Girdwood * (Character '-' is used to separate different DMI fields here). 1770345233d7SLiam Girdwood * This will help the user space to load the device-specific Use Case Manager 1771345233d7SLiam Girdwood * (UCM) configurations for the card. 1772345233d7SLiam Girdwood * 1773345233d7SLiam Girdwood * Possible card long names may be: 1774345233d7SLiam Girdwood * DellInc.-XPS139343-01-0310JH 1775345233d7SLiam Girdwood * ASUSTeKCOMPUTERINC.-T100TA-1.0-T100TA 1776345233d7SLiam Girdwood * Circuitco-MinnowboardMaxD0PLATFORM-D0-MinnowBoardMAX 1777345233d7SLiam Girdwood * 1778345233d7SLiam Girdwood * This function also supports flavoring the card longname to provide 1779345233d7SLiam Girdwood * the extra differentiation, like "vendor-product-version-board-flavor". 1780345233d7SLiam Girdwood * 1781345233d7SLiam Girdwood * We only keep number and alphabet characters and a few separator characters 1782345233d7SLiam Girdwood * in the card long name since UCM in the user space uses the card long names 1783345233d7SLiam Girdwood * as card configuration directory names and AudoConf cannot support special 1784345233d7SLiam Girdwood * charactors like SPACE. 1785345233d7SLiam Girdwood * 1786345233d7SLiam Girdwood * Returns 0 on success, otherwise a negative error code. 1787345233d7SLiam Girdwood */ 1788345233d7SLiam Girdwood int snd_soc_set_dmi_name(struct snd_soc_card *card, const char *flavour) 1789345233d7SLiam Girdwood { 1790345233d7SLiam Girdwood const char *vendor, *product, *product_version, *board; 1791345233d7SLiam Girdwood size_t longname_buf_size = sizeof(card->snd_card->longname); 1792345233d7SLiam Girdwood size_t len; 1793345233d7SLiam Girdwood 1794345233d7SLiam Girdwood if (card->long_name) 1795345233d7SLiam Girdwood return 0; /* long name already set by driver or from DMI */ 1796345233d7SLiam Girdwood 1797345233d7SLiam Girdwood /* make up dmi long name as: vendor.product.version.board */ 1798345233d7SLiam Girdwood vendor = dmi_get_system_info(DMI_BOARD_VENDOR); 179998faf436SMengdong Lin if (!vendor || !is_dmi_valid(vendor)) { 1800345233d7SLiam Girdwood dev_warn(card->dev, "ASoC: no DMI vendor name!\n"); 1801345233d7SLiam Girdwood return 0; 1802345233d7SLiam Girdwood } 1803345233d7SLiam Girdwood 180498faf436SMengdong Lin 1805345233d7SLiam Girdwood snprintf(card->dmi_longname, sizeof(card->snd_card->longname), 1806345233d7SLiam Girdwood "%s", vendor); 1807345233d7SLiam Girdwood cleanup_dmi_name(card->dmi_longname); 1808345233d7SLiam Girdwood 1809345233d7SLiam Girdwood product = dmi_get_system_info(DMI_PRODUCT_NAME); 181098faf436SMengdong Lin if (product && is_dmi_valid(product)) { 1811345233d7SLiam Girdwood len = strlen(card->dmi_longname); 1812345233d7SLiam Girdwood snprintf(card->dmi_longname + len, 1813345233d7SLiam Girdwood longname_buf_size - len, 1814345233d7SLiam Girdwood "-%s", product); 1815345233d7SLiam Girdwood 1816345233d7SLiam Girdwood len++; /* skip the separator "-" */ 1817345233d7SLiam Girdwood if (len < longname_buf_size) 1818345233d7SLiam Girdwood cleanup_dmi_name(card->dmi_longname + len); 1819345233d7SLiam Girdwood 1820345233d7SLiam Girdwood /* some vendors like Lenovo may only put a self-explanatory 1821345233d7SLiam Girdwood * name in the product version field 1822345233d7SLiam Girdwood */ 1823345233d7SLiam Girdwood product_version = dmi_get_system_info(DMI_PRODUCT_VERSION); 182498faf436SMengdong Lin if (product_version && is_dmi_valid(product_version)) { 1825345233d7SLiam Girdwood len = strlen(card->dmi_longname); 1826345233d7SLiam Girdwood snprintf(card->dmi_longname + len, 1827345233d7SLiam Girdwood longname_buf_size - len, 1828345233d7SLiam Girdwood "-%s", product_version); 1829345233d7SLiam Girdwood 1830345233d7SLiam Girdwood len++; 1831345233d7SLiam Girdwood if (len < longname_buf_size) 1832345233d7SLiam Girdwood cleanup_dmi_name(card->dmi_longname + len); 1833345233d7SLiam Girdwood } 1834345233d7SLiam Girdwood } 1835345233d7SLiam Girdwood 1836345233d7SLiam Girdwood board = dmi_get_system_info(DMI_BOARD_NAME); 183798faf436SMengdong Lin if (board && is_dmi_valid(board)) { 1838345233d7SLiam Girdwood len = strlen(card->dmi_longname); 1839345233d7SLiam Girdwood snprintf(card->dmi_longname + len, 1840345233d7SLiam Girdwood longname_buf_size - len, 1841345233d7SLiam Girdwood "-%s", board); 1842345233d7SLiam Girdwood 1843345233d7SLiam Girdwood len++; 1844345233d7SLiam Girdwood if (len < longname_buf_size) 1845345233d7SLiam Girdwood cleanup_dmi_name(card->dmi_longname + len); 1846345233d7SLiam Girdwood } else if (!product) { 1847345233d7SLiam Girdwood /* fall back to using legacy name */ 1848345233d7SLiam Girdwood dev_warn(card->dev, "ASoC: no DMI board/product name!\n"); 1849345233d7SLiam Girdwood return 0; 1850345233d7SLiam Girdwood } 1851345233d7SLiam Girdwood 1852345233d7SLiam Girdwood /* Add flavour to dmi long name */ 1853345233d7SLiam Girdwood if (flavour) { 1854345233d7SLiam Girdwood len = strlen(card->dmi_longname); 1855345233d7SLiam Girdwood snprintf(card->dmi_longname + len, 1856345233d7SLiam Girdwood longname_buf_size - len, 1857345233d7SLiam Girdwood "-%s", flavour); 1858345233d7SLiam Girdwood 1859345233d7SLiam Girdwood len++; 1860345233d7SLiam Girdwood if (len < longname_buf_size) 1861345233d7SLiam Girdwood cleanup_dmi_name(card->dmi_longname + len); 1862345233d7SLiam Girdwood } 1863345233d7SLiam Girdwood 1864345233d7SLiam Girdwood /* set the card long name */ 1865345233d7SLiam Girdwood card->long_name = card->dmi_longname; 1866345233d7SLiam Girdwood 1867345233d7SLiam Girdwood return 0; 1868345233d7SLiam Girdwood } 1869345233d7SLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_set_dmi_name); 18701f5a4535STakashi Iwai #endif /* CONFIG_DMI */ 1871345233d7SLiam Girdwood 1872a655de80SLiam Girdwood static void soc_check_tplg_fes(struct snd_soc_card *card) 1873a655de80SLiam Girdwood { 1874a655de80SLiam Girdwood struct snd_soc_component *component; 1875a655de80SLiam Girdwood const struct snd_soc_component_driver *comp_drv; 1876a655de80SLiam Girdwood struct snd_soc_dai_link *dai_link; 1877a655de80SLiam Girdwood int i; 1878a655de80SLiam Girdwood 1879a655de80SLiam Girdwood list_for_each_entry(component, &component_list, list) { 1880a655de80SLiam Girdwood 1881a655de80SLiam Girdwood /* does this component override FEs ? */ 1882a655de80SLiam Girdwood if (!component->driver->ignore_machine) 1883a655de80SLiam Girdwood continue; 1884a655de80SLiam Girdwood 1885a655de80SLiam Girdwood /* for this machine ? */ 1886a655de80SLiam Girdwood if (strcmp(component->driver->ignore_machine, 1887a655de80SLiam Girdwood card->dev->driver->name)) 1888a655de80SLiam Girdwood continue; 1889a655de80SLiam Girdwood 1890a655de80SLiam Girdwood /* machine matches, so override the rtd data */ 1891a655de80SLiam Girdwood for (i = 0; i < card->num_links; i++) { 1892a655de80SLiam Girdwood 1893a655de80SLiam Girdwood dai_link = &card->dai_link[i]; 1894a655de80SLiam Girdwood 1895a655de80SLiam Girdwood /* ignore this FE */ 1896a655de80SLiam Girdwood if (dai_link->dynamic) { 1897a655de80SLiam Girdwood dai_link->ignore = true; 1898a655de80SLiam Girdwood continue; 1899a655de80SLiam Girdwood } 1900a655de80SLiam Girdwood 1901a655de80SLiam Girdwood dev_info(card->dev, "info: override FE DAI link %s\n", 1902a655de80SLiam Girdwood card->dai_link[i].name); 1903a655de80SLiam Girdwood 1904a655de80SLiam Girdwood /* override platform component */ 1905daecf46eSKuninori Morimoto if (snd_soc_init_platform(card, dai_link) < 0) { 1906daecf46eSKuninori Morimoto dev_err(card->dev, "init platform error"); 1907daecf46eSKuninori Morimoto continue; 1908daecf46eSKuninori Morimoto } 1909daecf46eSKuninori Morimoto dai_link->platform->name = component->name; 1910a655de80SLiam Girdwood 1911a655de80SLiam Girdwood /* convert non BE into BE */ 1912a655de80SLiam Girdwood dai_link->no_pcm = 1; 1913a655de80SLiam Girdwood 1914a655de80SLiam Girdwood /* override any BE fixups */ 1915a655de80SLiam Girdwood dai_link->be_hw_params_fixup = 1916a655de80SLiam Girdwood component->driver->be_hw_params_fixup; 1917a655de80SLiam Girdwood 1918a655de80SLiam Girdwood /* most BE links don't set stream name, so set it to 1919a655de80SLiam Girdwood * dai link name if it's NULL to help bind widgets. 1920a655de80SLiam Girdwood */ 1921a655de80SLiam Girdwood if (!dai_link->stream_name) 1922a655de80SLiam Girdwood dai_link->stream_name = dai_link->name; 1923a655de80SLiam Girdwood } 1924a655de80SLiam Girdwood 1925a655de80SLiam Girdwood /* Inform userspace we are using alternate topology */ 1926a655de80SLiam Girdwood if (component->driver->topology_name_prefix) { 1927a655de80SLiam Girdwood 1928a655de80SLiam Girdwood /* topology shortname created ? */ 1929a655de80SLiam Girdwood if (!card->topology_shortname_created) { 1930a655de80SLiam Girdwood comp_drv = component->driver; 1931a655de80SLiam Girdwood 1932a655de80SLiam Girdwood snprintf(card->topology_shortname, 32, "%s-%s", 1933a655de80SLiam Girdwood comp_drv->topology_name_prefix, 1934a655de80SLiam Girdwood card->name); 1935a655de80SLiam Girdwood card->topology_shortname_created = true; 1936a655de80SLiam Girdwood } 1937a655de80SLiam Girdwood 1938a655de80SLiam Girdwood /* use topology shortname */ 1939a655de80SLiam Girdwood card->name = card->topology_shortname; 1940a655de80SLiam Girdwood } 1941a655de80SLiam Girdwood } 1942a655de80SLiam Girdwood } 1943a655de80SLiam Girdwood 1944b19e6e7bSMark Brown static int snd_soc_instantiate_card(struct snd_soc_card *card) 1945f0fba2adSLiam Girdwood { 19461a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd; 194761b0088bSMengdong Lin struct snd_soc_dai_link *dai_link; 1948ce64c8b9SLars-Peter Clausen int ret, i, order; 194996dd3622SMark Brown 195034e81ab4SLars-Peter Clausen mutex_lock(&client_mutex); 195101b9d99aSLiam Girdwood mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT); 1952f0fba2adSLiam Girdwood 1953a655de80SLiam Girdwood /* check whether any platform is ignore machine FE and using topology */ 1954a655de80SLiam Girdwood soc_check_tplg_fes(card); 1955a655de80SLiam Girdwood 1956b19e6e7bSMark Brown /* bind DAIs */ 1957b19e6e7bSMark Brown for (i = 0; i < card->num_links; i++) { 19586f2f1ff0SMengdong Lin ret = soc_bind_dai_link(card, &card->dai_link[i]); 1959b19e6e7bSMark Brown if (ret != 0) 1960b19e6e7bSMark Brown goto base_error; 1961db2a4165SFrank Mandarino } 1962db2a4165SFrank Mandarino 196344c69bb1SLars-Peter Clausen /* bind aux_devs too */ 1964b19e6e7bSMark Brown for (i = 0; i < card->num_aux_devs; i++) { 196544c69bb1SLars-Peter Clausen ret = soc_bind_aux_dev(card, i); 1966b19e6e7bSMark Brown if (ret != 0) 1967b19e6e7bSMark Brown goto base_error; 1968db2a4165SFrank Mandarino } 1969db2a4165SFrank Mandarino 1970f8f80361SMengdong Lin /* add predefined DAI links to the list */ 1971f8f80361SMengdong Lin for (i = 0; i < card->num_links; i++) 1972f8f80361SMengdong Lin snd_soc_add_dai_link(card, card->dai_link+i); 1973f8f80361SMengdong Lin 1974f0fba2adSLiam Girdwood /* card bind complete so register a sound card */ 1975102b5a8dSTakashi Iwai ret = snd_card_new(card->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, 1976f0fba2adSLiam Girdwood card->owner, 0, &card->snd_card); 1977f0fba2adSLiam Girdwood if (ret < 0) { 197810e8aa9aSMichał Mirosław dev_err(card->dev, 197910e8aa9aSMichał Mirosław "ASoC: can't create sound card for card %s: %d\n", 198010e8aa9aSMichał Mirosław card->name, ret); 1981b19e6e7bSMark Brown goto base_error; 1982db2a4165SFrank Mandarino } 1983db2a4165SFrank Mandarino 19840757d834SLars-Peter Clausen soc_init_card_debugfs(card); 19850757d834SLars-Peter Clausen 1986e37a4970SMark Brown card->dapm.bias_level = SND_SOC_BIAS_OFF; 1987e37a4970SMark Brown card->dapm.dev = card->dev; 1988e37a4970SMark Brown card->dapm.card = card; 1989e37a4970SMark Brown list_add(&card->dapm.list, &card->dapm_list); 1990e37a4970SMark Brown 1991d5d1e0beSLars-Peter Clausen #ifdef CONFIG_DEBUG_FS 1992d5d1e0beSLars-Peter Clausen snd_soc_dapm_debugfs_init(&card->dapm, card->debugfs_card_root); 1993d5d1e0beSLars-Peter Clausen #endif 1994d5d1e0beSLars-Peter Clausen 199588ee1c61SMark Brown #ifdef CONFIG_PM_SLEEP 19966ed25978SAndy Green /* deferred resume work */ 19976308419aSMark Brown INIT_WORK(&card->deferred_resume_work, soc_resume_deferred); 19981301a964SRandy Dunlap #endif 19996ed25978SAndy Green 20009a841ebbSMark Brown if (card->dapm_widgets) 20019a841ebbSMark Brown snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets, 20029a841ebbSMark Brown card->num_dapm_widgets); 20039a841ebbSMark Brown 2004f23e860eSNicolin Chen if (card->of_dapm_widgets) 2005f23e860eSNicolin Chen snd_soc_dapm_new_controls(&card->dapm, card->of_dapm_widgets, 2006f23e860eSNicolin Chen card->num_of_dapm_widgets); 2007f23e860eSNicolin Chen 2008f0fba2adSLiam Girdwood /* initialise the sound card only once */ 2009f0fba2adSLiam Girdwood if (card->probe) { 2010e7361ec4SMark Brown ret = card->probe(card); 2011f0fba2adSLiam Girdwood if (ret < 0) 2012f0fba2adSLiam Girdwood goto card_probe_error; 2013f0fba2adSLiam Girdwood } 2014f0fba2adSLiam Girdwood 201562ae68faSStephen Warren /* probe all components used by DAI links on this card */ 20160168bf0dSLiam Girdwood for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; 20170168bf0dSLiam Girdwood order++) { 20181a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 20191a497983SMengdong Lin ret = soc_probe_link_components(card, rtd, order); 202062ae68faSStephen Warren if (ret < 0) { 2021f110bfc7SLiam Girdwood dev_err(card->dev, 2022f110bfc7SLiam Girdwood "ASoC: failed to instantiate card %d\n", 2023f110bfc7SLiam Girdwood ret); 202462ae68faSStephen Warren goto probe_dai_err; 202562ae68faSStephen Warren } 202662ae68faSStephen Warren } 202762ae68faSStephen Warren } 202862ae68faSStephen Warren 2029f2ed6b07SMengdong Lin /* probe auxiliary components */ 2030f2ed6b07SMengdong Lin ret = soc_probe_aux_devices(card); 2031f2ed6b07SMengdong Lin if (ret < 0) 2032f2ed6b07SMengdong Lin goto probe_dai_err; 2033f2ed6b07SMengdong Lin 203461b0088bSMengdong Lin /* Find new DAI links added during probing components and bind them. 203561b0088bSMengdong Lin * Components with topology may bring new DAIs and DAI links. 203661b0088bSMengdong Lin */ 203761b0088bSMengdong Lin list_for_each_entry(dai_link, &card->dai_link_list, list) { 203861b0088bSMengdong Lin if (soc_is_dai_link_bound(card, dai_link)) 203961b0088bSMengdong Lin continue; 204061b0088bSMengdong Lin 204161b0088bSMengdong Lin ret = soc_init_dai_link(card, dai_link); 204261b0088bSMengdong Lin if (ret) 204361b0088bSMengdong Lin goto probe_dai_err; 204461b0088bSMengdong Lin ret = soc_bind_dai_link(card, dai_link); 204561b0088bSMengdong Lin if (ret) 204661b0088bSMengdong Lin goto probe_dai_err; 204761b0088bSMengdong Lin } 204861b0088bSMengdong Lin 204962ae68faSStephen Warren /* probe all DAI links on this card */ 205062ae68faSStephen Warren for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; 205162ae68faSStephen Warren order++) { 20521a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 20531a497983SMengdong Lin ret = soc_probe_link_dais(card, rtd, order); 2054fe3e78e0SMark Brown if (ret < 0) { 2055f110bfc7SLiam Girdwood dev_err(card->dev, 2056f110bfc7SLiam Girdwood "ASoC: failed to instantiate card %d\n", 2057f110bfc7SLiam Girdwood ret); 2058f0fba2adSLiam Girdwood goto probe_dai_err; 2059fe3e78e0SMark Brown } 2060fe3e78e0SMark Brown } 20610168bf0dSLiam Girdwood } 2062fe3e78e0SMark Brown 2063888df395SMark Brown snd_soc_dapm_link_dai_widgets(card); 2064b893ea5fSLiam Girdwood snd_soc_dapm_connect_dai_link_widgets(card); 2065888df395SMark Brown 2066b7af1dafSMark Brown if (card->controls) 2067022658beSLiam Girdwood snd_soc_add_card_controls(card, card->controls, card->num_controls); 2068b7af1dafSMark Brown 2069b8ad29deSMark Brown if (card->dapm_routes) 2070b8ad29deSMark Brown snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes, 2071b8ad29deSMark Brown card->num_dapm_routes); 2072b8ad29deSMark Brown 2073f23e860eSNicolin Chen if (card->of_dapm_routes) 2074f23e860eSNicolin Chen snd_soc_dapm_add_routes(&card->dapm, card->of_dapm_routes, 2075f23e860eSNicolin Chen card->num_of_dapm_routes); 207675d9ac46SMark Brown 2077861886d3STakashi Iwai /* try to set some sane longname if DMI is available */ 2078861886d3STakashi Iwai snd_soc_set_dmi_name(card, NULL); 2079861886d3STakashi Iwai 2080f0fba2adSLiam Girdwood snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname), 2081fe3e78e0SMark Brown "%s", card->name); 2082f0fba2adSLiam Girdwood snprintf(card->snd_card->longname, sizeof(card->snd_card->longname), 208322de71baSLiam Girdwood "%s", card->long_name ? card->long_name : card->name); 2084f0e8ed85SMark Brown snprintf(card->snd_card->driver, sizeof(card->snd_card->driver), 2085f0e8ed85SMark Brown "%s", card->driver_name ? card->driver_name : card->name); 2086f0e8ed85SMark Brown for (i = 0; i < ARRAY_SIZE(card->snd_card->driver); i++) { 2087f0e8ed85SMark Brown switch (card->snd_card->driver[i]) { 2088f0e8ed85SMark Brown case '_': 2089f0e8ed85SMark Brown case '-': 2090f0e8ed85SMark Brown case '\0': 2091f0e8ed85SMark Brown break; 2092f0e8ed85SMark Brown default: 2093f0e8ed85SMark Brown if (!isalnum(card->snd_card->driver[i])) 2094f0e8ed85SMark Brown card->snd_card->driver[i] = '_'; 2095f0e8ed85SMark Brown break; 2096f0e8ed85SMark Brown } 2097f0e8ed85SMark Brown } 2098fe3e78e0SMark Brown 209928e9ad92SMark Brown if (card->late_probe) { 210028e9ad92SMark Brown ret = card->late_probe(card); 210128e9ad92SMark Brown if (ret < 0) { 2102f110bfc7SLiam Girdwood dev_err(card->dev, "ASoC: %s late_probe() failed: %d\n", 210328e9ad92SMark Brown card->name, ret); 210428e9ad92SMark Brown goto probe_aux_dev_err; 210528e9ad92SMark Brown } 210628e9ad92SMark Brown } 210728e9ad92SMark Brown 2108824ef826SLars-Peter Clausen snd_soc_dapm_new_widgets(card); 21098c193b8dSLars-Peter Clausen 2110f0fba2adSLiam Girdwood ret = snd_card_register(card->snd_card); 2111fe3e78e0SMark Brown if (ret < 0) { 2112f110bfc7SLiam Girdwood dev_err(card->dev, "ASoC: failed to register soundcard %d\n", 2113f110bfc7SLiam Girdwood ret); 21146b3ed785SAxel Lin goto probe_aux_dev_err; 2115fe3e78e0SMark Brown } 2116fe3e78e0SMark Brown 2117435c5e25SMark Brown card->instantiated = 1; 21184f4c0072SMark Brown snd_soc_dapm_sync(&card->dapm); 2119f0fba2adSLiam Girdwood mutex_unlock(&card->mutex); 212034e81ab4SLars-Peter Clausen mutex_unlock(&client_mutex); 2121b19e6e7bSMark Brown 2122b19e6e7bSMark Brown return 0; 2123db2a4165SFrank Mandarino 21242eea392dSJarkko Nikula probe_aux_dev_err: 2125f2ed6b07SMengdong Lin soc_remove_aux_devices(card); 21262eea392dSJarkko Nikula 2127f0fba2adSLiam Girdwood probe_dai_err: 21280671fd8eSKuninori Morimoto soc_remove_dai_links(card); 2129fe3e78e0SMark Brown 2130f0fba2adSLiam Girdwood card_probe_error: 213187506549SMark Brown if (card->remove) 2132e7361ec4SMark Brown card->remove(card); 2133f0fba2adSLiam Girdwood 21342210438bSLars-Peter Clausen snd_soc_dapm_free(&card->dapm); 21350757d834SLars-Peter Clausen soc_cleanup_card_debugfs(card); 2136f0fba2adSLiam Girdwood snd_card_free(card->snd_card); 2137f0fba2adSLiam Girdwood 2138b19e6e7bSMark Brown base_error: 21391a497983SMengdong Lin soc_remove_pcm_runtimes(card); 2140f0fba2adSLiam Girdwood mutex_unlock(&card->mutex); 214134e81ab4SLars-Peter Clausen mutex_unlock(&client_mutex); 2142db2a4165SFrank Mandarino 2143b19e6e7bSMark Brown return ret; 2144435c5e25SMark Brown } 2145435c5e25SMark Brown 2146435c5e25SMark Brown /* probes a new socdev */ 2147435c5e25SMark Brown static int soc_probe(struct platform_device *pdev) 2148435c5e25SMark Brown { 2149f0fba2adSLiam Girdwood struct snd_soc_card *card = platform_get_drvdata(pdev); 2150435c5e25SMark Brown 215170a7ca34SVinod Koul /* 215270a7ca34SVinod Koul * no card, so machine driver should be registering card 215370a7ca34SVinod Koul * we should not be here in that case so ret error 215470a7ca34SVinod Koul */ 215570a7ca34SVinod Koul if (!card) 215670a7ca34SVinod Koul return -EINVAL; 215770a7ca34SVinod Koul 2158fe4085e8SMark Brown dev_warn(&pdev->dev, 2159f110bfc7SLiam Girdwood "ASoC: machine %s should use snd_soc_register_card()\n", 2160fe4085e8SMark Brown card->name); 2161fe4085e8SMark Brown 2162435c5e25SMark Brown /* Bodge while we unpick instantiation */ 2163435c5e25SMark Brown card->dev = &pdev->dev; 2164f0fba2adSLiam Girdwood 216528d528c8SMark Brown return snd_soc_register_card(card); 2166435c5e25SMark Brown } 2167435c5e25SMark Brown 2168b0e26485SVinod Koul static int soc_cleanup_card_resources(struct snd_soc_card *card) 2169db2a4165SFrank Mandarino { 21701a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd; 2171db2a4165SFrank Mandarino 2172f0fba2adSLiam Girdwood /* make sure any delayed work runs */ 21731a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) 217443829731STejun Heo flush_delayed_work(&rtd->delayed_work); 2175db2a4165SFrank Mandarino 21764efda5f2STakashi Iwai /* free the ALSA card at first; this syncs with pending operations */ 21774efda5f2STakashi Iwai snd_card_free(card->snd_card); 21784efda5f2STakashi Iwai 2179f0fba2adSLiam Girdwood /* remove and free each DAI */ 21800671fd8eSKuninori Morimoto soc_remove_dai_links(card); 21811a497983SMengdong Lin soc_remove_pcm_runtimes(card); 2182f0fba2adSLiam Girdwood 2183f2ed6b07SMengdong Lin /* remove auxiliary devices */ 2184f2ed6b07SMengdong Lin soc_remove_aux_devices(card); 2185f2ed6b07SMengdong Lin 2186d1e81428SMark Brown snd_soc_dapm_free(&card->dapm); 2187a6052154SJarkko Nikula soc_cleanup_card_debugfs(card); 2188a6052154SJarkko Nikula 2189f0fba2adSLiam Girdwood /* remove the card */ 219087506549SMark Brown if (card->remove) 2191e7361ec4SMark Brown card->remove(card); 2192f0fba2adSLiam Girdwood 2193b0e26485SVinod Koul return 0; 2194b2dfa62cSGuennadi Liakhovetski } 2195b0e26485SVinod Koul 2196b0e26485SVinod Koul /* removes a socdev */ 2197b0e26485SVinod Koul static int soc_remove(struct platform_device *pdev) 2198b0e26485SVinod Koul { 2199b0e26485SVinod Koul struct snd_soc_card *card = platform_get_drvdata(pdev); 2200b0e26485SVinod Koul 2201c5af3a2eSMark Brown snd_soc_unregister_card(card); 2202db2a4165SFrank Mandarino return 0; 2203db2a4165SFrank Mandarino } 2204db2a4165SFrank Mandarino 22056f8ab4acSMark Brown int snd_soc_poweroff(struct device *dev) 220651737470SMark Brown { 22076f8ab4acSMark Brown struct snd_soc_card *card = dev_get_drvdata(dev); 22081a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd; 220951737470SMark Brown 221051737470SMark Brown if (!card->instantiated) 2211416356fcSMark Brown return 0; 221251737470SMark Brown 221351737470SMark Brown /* Flush out pmdown_time work - we actually do want to run it 221451737470SMark Brown * now, we're shutting down so no imminent restart. */ 22151a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) 221643829731STejun Heo flush_delayed_work(&rtd->delayed_work); 221751737470SMark Brown 2218f0fba2adSLiam Girdwood snd_soc_dapm_shutdown(card); 2219416356fcSMark Brown 2220988e8cc4SNicolin Chen /* deactivate pins to sleep state */ 22211a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 222288bd870fSBenoit Cousson struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 22230b7990e3SKuninori Morimoto struct snd_soc_dai *codec_dai; 22241a497983SMengdong Lin int i; 222588bd870fSBenoit Cousson 2226988e8cc4SNicolin Chen pinctrl_pm_select_sleep_state(cpu_dai->dev); 22270b7990e3SKuninori Morimoto for_each_rtd_codec_dai(rtd, i, codec_dai) { 222888bd870fSBenoit Cousson pinctrl_pm_select_sleep_state(codec_dai->dev); 222988bd870fSBenoit Cousson } 2230988e8cc4SNicolin Chen } 2231988e8cc4SNicolin Chen 2232416356fcSMark Brown return 0; 223351737470SMark Brown } 22346f8ab4acSMark Brown EXPORT_SYMBOL_GPL(snd_soc_poweroff); 223551737470SMark Brown 22366f8ab4acSMark Brown const struct dev_pm_ops snd_soc_pm_ops = { 2237b1dd5897SViresh Kumar .suspend = snd_soc_suspend, 2238b1dd5897SViresh Kumar .resume = snd_soc_resume, 2239b1dd5897SViresh Kumar .freeze = snd_soc_suspend, 2240b1dd5897SViresh Kumar .thaw = snd_soc_resume, 22416f8ab4acSMark Brown .poweroff = snd_soc_poweroff, 2242b1dd5897SViresh Kumar .restore = snd_soc_resume, 2243416356fcSMark Brown }; 2244deb2607eSStephen Warren EXPORT_SYMBOL_GPL(snd_soc_pm_ops); 2245416356fcSMark Brown 2246db2a4165SFrank Mandarino /* ASoC platform driver */ 2247db2a4165SFrank Mandarino static struct platform_driver soc_driver = { 2248db2a4165SFrank Mandarino .driver = { 2249db2a4165SFrank Mandarino .name = "soc-audio", 22506f8ab4acSMark Brown .pm = &snd_soc_pm_ops, 2251db2a4165SFrank Mandarino }, 2252db2a4165SFrank Mandarino .probe = soc_probe, 2253db2a4165SFrank Mandarino .remove = soc_remove, 2254db2a4165SFrank Mandarino }; 2255db2a4165SFrank Mandarino 2256096e49d5SMark Brown /** 2257db2a4165SFrank Mandarino * snd_soc_cnew - create new control 2258db2a4165SFrank Mandarino * @_template: control template 2259db2a4165SFrank Mandarino * @data: control private data 2260ac11a2b3SMark Brown * @long_name: control long name 2261efb7ac3fSMark Brown * @prefix: control name prefix 2262db2a4165SFrank Mandarino * 2263db2a4165SFrank Mandarino * Create a new mixer control from a template control. 2264db2a4165SFrank Mandarino * 2265db2a4165SFrank Mandarino * Returns 0 for success, else error. 2266db2a4165SFrank Mandarino */ 2267db2a4165SFrank Mandarino struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template, 22683056557fSMark Brown void *data, const char *long_name, 2269efb7ac3fSMark Brown const char *prefix) 2270db2a4165SFrank Mandarino { 2271db2a4165SFrank Mandarino struct snd_kcontrol_new template; 2272efb7ac3fSMark Brown struct snd_kcontrol *kcontrol; 2273efb7ac3fSMark Brown char *name = NULL; 2274db2a4165SFrank Mandarino 2275db2a4165SFrank Mandarino memcpy(&template, _template, sizeof(template)); 2276db2a4165SFrank Mandarino template.index = 0; 2277db2a4165SFrank Mandarino 2278efb7ac3fSMark Brown if (!long_name) 2279efb7ac3fSMark Brown long_name = template.name; 2280efb7ac3fSMark Brown 2281efb7ac3fSMark Brown if (prefix) { 22822b581074SLars-Peter Clausen name = kasprintf(GFP_KERNEL, "%s %s", prefix, long_name); 2283efb7ac3fSMark Brown if (!name) 2284efb7ac3fSMark Brown return NULL; 2285efb7ac3fSMark Brown 2286efb7ac3fSMark Brown template.name = name; 2287efb7ac3fSMark Brown } else { 2288efb7ac3fSMark Brown template.name = long_name; 2289efb7ac3fSMark Brown } 2290efb7ac3fSMark Brown 2291efb7ac3fSMark Brown kcontrol = snd_ctl_new1(&template, data); 2292efb7ac3fSMark Brown 2293efb7ac3fSMark Brown kfree(name); 2294efb7ac3fSMark Brown 2295efb7ac3fSMark Brown return kcontrol; 2296db2a4165SFrank Mandarino } 2297db2a4165SFrank Mandarino EXPORT_SYMBOL_GPL(snd_soc_cnew); 2298db2a4165SFrank Mandarino 2299022658beSLiam Girdwood static int snd_soc_add_controls(struct snd_card *card, struct device *dev, 2300022658beSLiam Girdwood const struct snd_kcontrol_new *controls, int num_controls, 2301022658beSLiam Girdwood const char *prefix, void *data) 2302022658beSLiam Girdwood { 2303022658beSLiam Girdwood int err, i; 2304022658beSLiam Girdwood 2305022658beSLiam Girdwood for (i = 0; i < num_controls; i++) { 2306022658beSLiam Girdwood const struct snd_kcontrol_new *control = &controls[i]; 2307022658beSLiam Girdwood err = snd_ctl_add(card, snd_soc_cnew(control, data, 2308022658beSLiam Girdwood control->name, prefix)); 2309022658beSLiam Girdwood if (err < 0) { 2310f110bfc7SLiam Girdwood dev_err(dev, "ASoC: Failed to add %s: %d\n", 2311f110bfc7SLiam Girdwood control->name, err); 2312022658beSLiam Girdwood return err; 2313022658beSLiam Girdwood } 2314022658beSLiam Girdwood } 2315022658beSLiam Girdwood 2316022658beSLiam Girdwood return 0; 2317022658beSLiam Girdwood } 2318022658beSLiam Girdwood 23194fefd698SDimitris Papastamos struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card, 23204fefd698SDimitris Papastamos const char *name) 23214fefd698SDimitris Papastamos { 23224fefd698SDimitris Papastamos struct snd_card *card = soc_card->snd_card; 23234fefd698SDimitris Papastamos struct snd_kcontrol *kctl; 23244fefd698SDimitris Papastamos 23254fefd698SDimitris Papastamos if (unlikely(!name)) 23264fefd698SDimitris Papastamos return NULL; 23274fefd698SDimitris Papastamos 23284fefd698SDimitris Papastamos list_for_each_entry(kctl, &card->controls, list) 23294fefd698SDimitris Papastamos if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) 23304fefd698SDimitris Papastamos return kctl; 23314fefd698SDimitris Papastamos return NULL; 23324fefd698SDimitris Papastamos } 23334fefd698SDimitris Papastamos EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol); 23344fefd698SDimitris Papastamos 2335db2a4165SFrank Mandarino /** 23360f2780adSLars-Peter Clausen * snd_soc_add_component_controls - Add an array of controls to a component. 23370f2780adSLars-Peter Clausen * 23380f2780adSLars-Peter Clausen * @component: Component to add controls to 23390f2780adSLars-Peter Clausen * @controls: Array of controls to add 23400f2780adSLars-Peter Clausen * @num_controls: Number of elements in the array 23410f2780adSLars-Peter Clausen * 23420f2780adSLars-Peter Clausen * Return: 0 for success, else error. 23430f2780adSLars-Peter Clausen */ 23440f2780adSLars-Peter Clausen int snd_soc_add_component_controls(struct snd_soc_component *component, 23450f2780adSLars-Peter Clausen const struct snd_kcontrol_new *controls, unsigned int num_controls) 23460f2780adSLars-Peter Clausen { 23470f2780adSLars-Peter Clausen struct snd_card *card = component->card->snd_card; 23480f2780adSLars-Peter Clausen 23490f2780adSLars-Peter Clausen return snd_soc_add_controls(card, component->dev, controls, 23500f2780adSLars-Peter Clausen num_controls, component->name_prefix, component); 23510f2780adSLars-Peter Clausen } 23520f2780adSLars-Peter Clausen EXPORT_SYMBOL_GPL(snd_soc_add_component_controls); 23530f2780adSLars-Peter Clausen 23540f2780adSLars-Peter Clausen /** 2355022658beSLiam Girdwood * snd_soc_add_card_controls - add an array of controls to a SoC card. 2356022658beSLiam Girdwood * Convenience function to add a list of controls. 2357022658beSLiam Girdwood * 2358022658beSLiam Girdwood * @soc_card: SoC card to add controls to 2359022658beSLiam Girdwood * @controls: array of controls to add 2360022658beSLiam Girdwood * @num_controls: number of elements in the array 2361022658beSLiam Girdwood * 2362022658beSLiam Girdwood * Return 0 for success, else error. 2363022658beSLiam Girdwood */ 2364022658beSLiam Girdwood int snd_soc_add_card_controls(struct snd_soc_card *soc_card, 2365022658beSLiam Girdwood const struct snd_kcontrol_new *controls, int num_controls) 2366022658beSLiam Girdwood { 2367022658beSLiam Girdwood struct snd_card *card = soc_card->snd_card; 2368022658beSLiam Girdwood 2369022658beSLiam Girdwood return snd_soc_add_controls(card, soc_card->dev, controls, num_controls, 2370022658beSLiam Girdwood NULL, soc_card); 2371022658beSLiam Girdwood } 2372022658beSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_add_card_controls); 2373022658beSLiam Girdwood 2374022658beSLiam Girdwood /** 2375022658beSLiam Girdwood * snd_soc_add_dai_controls - add an array of controls to a DAI. 2376022658beSLiam Girdwood * Convienience function to add a list of controls. 2377022658beSLiam Girdwood * 2378022658beSLiam Girdwood * @dai: DAI to add controls to 2379022658beSLiam Girdwood * @controls: array of controls to add 2380022658beSLiam Girdwood * @num_controls: number of elements in the array 2381022658beSLiam Girdwood * 2382022658beSLiam Girdwood * Return 0 for success, else error. 2383022658beSLiam Girdwood */ 2384022658beSLiam Girdwood int snd_soc_add_dai_controls(struct snd_soc_dai *dai, 2385022658beSLiam Girdwood const struct snd_kcontrol_new *controls, int num_controls) 2386022658beSLiam Girdwood { 2387313665b9SLars-Peter Clausen struct snd_card *card = dai->component->card->snd_card; 2388022658beSLiam Girdwood 2389022658beSLiam Girdwood return snd_soc_add_controls(card, dai->dev, controls, num_controls, 2390022658beSLiam Girdwood NULL, dai); 2391022658beSLiam Girdwood } 2392022658beSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_add_dai_controls); 2393022658beSLiam Girdwood 2394022658beSLiam Girdwood /** 23958c6529dbSLiam Girdwood * snd_soc_dai_set_sysclk - configure DAI system or master clock. 23968c6529dbSLiam Girdwood * @dai: DAI 23978c6529dbSLiam Girdwood * @clk_id: DAI specific clock ID 23988c6529dbSLiam Girdwood * @freq: new clock frequency in Hz 23998c6529dbSLiam Girdwood * @dir: new clock direction - input/output. 24008c6529dbSLiam Girdwood * 24018c6529dbSLiam Girdwood * Configures the DAI master (MCLK) or system (SYSCLK) clocking. 24028c6529dbSLiam Girdwood */ 24038c6529dbSLiam Girdwood int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, 24048c6529dbSLiam Girdwood unsigned int freq, int dir) 24058c6529dbSLiam Girdwood { 240646471925SKuninori Morimoto if (dai->driver->ops->set_sysclk) 2407f0fba2adSLiam Girdwood return dai->driver->ops->set_sysclk(dai, clk_id, freq, dir); 240871ccef0dSKuninori Morimoto 240971ccef0dSKuninori Morimoto return snd_soc_component_set_sysclk(dai->component, clk_id, 0, 2410ec4ee52aSMark Brown freq, dir); 24118c6529dbSLiam Girdwood } 24128c6529dbSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk); 24138c6529dbSLiam Girdwood 24148c6529dbSLiam Girdwood /** 241571ccef0dSKuninori Morimoto * snd_soc_component_set_sysclk - configure COMPONENT system or master clock. 241671ccef0dSKuninori Morimoto * @component: COMPONENT 241771ccef0dSKuninori Morimoto * @clk_id: DAI specific clock ID 241871ccef0dSKuninori Morimoto * @source: Source for the clock 241971ccef0dSKuninori Morimoto * @freq: new clock frequency in Hz 242071ccef0dSKuninori Morimoto * @dir: new clock direction - input/output. 242171ccef0dSKuninori Morimoto * 242271ccef0dSKuninori Morimoto * Configures the CODEC master (MCLK) or system (SYSCLK) clocking. 242371ccef0dSKuninori Morimoto */ 242471ccef0dSKuninori Morimoto int snd_soc_component_set_sysclk(struct snd_soc_component *component, int clk_id, 242571ccef0dSKuninori Morimoto int source, unsigned int freq, int dir) 242671ccef0dSKuninori Morimoto { 242771ccef0dSKuninori Morimoto if (component->driver->set_sysclk) 242871ccef0dSKuninori Morimoto return component->driver->set_sysclk(component, clk_id, source, 242971ccef0dSKuninori Morimoto freq, dir); 243071ccef0dSKuninori Morimoto 243171ccef0dSKuninori Morimoto return -ENOTSUPP; 243271ccef0dSKuninori Morimoto } 243371ccef0dSKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_component_set_sysclk); 243471ccef0dSKuninori Morimoto 243571ccef0dSKuninori Morimoto /** 24368c6529dbSLiam Girdwood * snd_soc_dai_set_clkdiv - configure DAI clock dividers. 24378c6529dbSLiam Girdwood * @dai: DAI 2438ac11a2b3SMark Brown * @div_id: DAI specific clock divider ID 24398c6529dbSLiam Girdwood * @div: new clock divisor. 24408c6529dbSLiam Girdwood * 24418c6529dbSLiam Girdwood * Configures the clock dividers. This is used to derive the best DAI bit and 24428c6529dbSLiam Girdwood * frame clocks from the system or master clock. It's best to set the DAI bit 24438c6529dbSLiam Girdwood * and frame clocks as low as possible to save system power. 24448c6529dbSLiam Girdwood */ 24458c6529dbSLiam Girdwood int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai, 24468c6529dbSLiam Girdwood int div_id, int div) 24478c6529dbSLiam Girdwood { 244846471925SKuninori Morimoto if (dai->driver->ops->set_clkdiv) 2449f0fba2adSLiam Girdwood return dai->driver->ops->set_clkdiv(dai, div_id, div); 24508c6529dbSLiam Girdwood else 24518c6529dbSLiam Girdwood return -EINVAL; 24528c6529dbSLiam Girdwood } 24538c6529dbSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv); 24548c6529dbSLiam Girdwood 24558c6529dbSLiam Girdwood /** 24568c6529dbSLiam Girdwood * snd_soc_dai_set_pll - configure DAI PLL. 24578c6529dbSLiam Girdwood * @dai: DAI 24588c6529dbSLiam Girdwood * @pll_id: DAI specific PLL ID 245985488037SMark Brown * @source: DAI specific source for the PLL 24608c6529dbSLiam Girdwood * @freq_in: PLL input clock frequency in Hz 24618c6529dbSLiam Girdwood * @freq_out: requested PLL output clock frequency in Hz 24628c6529dbSLiam Girdwood * 24638c6529dbSLiam Girdwood * Configures and enables PLL to generate output clock based on input clock. 24648c6529dbSLiam Girdwood */ 246585488037SMark Brown int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source, 246685488037SMark Brown unsigned int freq_in, unsigned int freq_out) 24678c6529dbSLiam Girdwood { 246846471925SKuninori Morimoto if (dai->driver->ops->set_pll) 2469f0fba2adSLiam Girdwood return dai->driver->ops->set_pll(dai, pll_id, source, 247085488037SMark Brown freq_in, freq_out); 2471ef641e5dSKuninori Morimoto 2472ef641e5dSKuninori Morimoto return snd_soc_component_set_pll(dai->component, pll_id, source, 2473ec4ee52aSMark Brown freq_in, freq_out); 24748c6529dbSLiam Girdwood } 24758c6529dbSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll); 24768c6529dbSLiam Girdwood 2477ec4ee52aSMark Brown /* 2478ef641e5dSKuninori Morimoto * snd_soc_component_set_pll - configure component PLL. 2479ef641e5dSKuninori Morimoto * @component: COMPONENT 2480ef641e5dSKuninori Morimoto * @pll_id: DAI specific PLL ID 2481ef641e5dSKuninori Morimoto * @source: DAI specific source for the PLL 2482ef641e5dSKuninori Morimoto * @freq_in: PLL input clock frequency in Hz 2483ef641e5dSKuninori Morimoto * @freq_out: requested PLL output clock frequency in Hz 2484ef641e5dSKuninori Morimoto * 2485ef641e5dSKuninori Morimoto * Configures and enables PLL to generate output clock based on input clock. 2486ef641e5dSKuninori Morimoto */ 2487ef641e5dSKuninori Morimoto int snd_soc_component_set_pll(struct snd_soc_component *component, int pll_id, 2488ef641e5dSKuninori Morimoto int source, unsigned int freq_in, 2489ef641e5dSKuninori Morimoto unsigned int freq_out) 2490ef641e5dSKuninori Morimoto { 2491ef641e5dSKuninori Morimoto if (component->driver->set_pll) 2492ef641e5dSKuninori Morimoto return component->driver->set_pll(component, pll_id, source, 2493ef641e5dSKuninori Morimoto freq_in, freq_out); 2494ef641e5dSKuninori Morimoto 2495ef641e5dSKuninori Morimoto return -EINVAL; 2496ef641e5dSKuninori Morimoto } 2497ef641e5dSKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_component_set_pll); 2498ef641e5dSKuninori Morimoto 24998c6529dbSLiam Girdwood /** 2500e54cf76bSLiam Girdwood * snd_soc_dai_set_bclk_ratio - configure BCLK to sample rate ratio. 2501e54cf76bSLiam Girdwood * @dai: DAI 2502231b86b1SMasanari Iida * @ratio: Ratio of BCLK to Sample rate. 2503e54cf76bSLiam Girdwood * 2504e54cf76bSLiam Girdwood * Configures the DAI for a preset BCLK to sample rate ratio. 2505e54cf76bSLiam Girdwood */ 2506e54cf76bSLiam Girdwood int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) 2507e54cf76bSLiam Girdwood { 250846471925SKuninori Morimoto if (dai->driver->ops->set_bclk_ratio) 2509e54cf76bSLiam Girdwood return dai->driver->ops->set_bclk_ratio(dai, ratio); 2510e54cf76bSLiam Girdwood else 2511e54cf76bSLiam Girdwood return -EINVAL; 2512e54cf76bSLiam Girdwood } 2513e54cf76bSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dai_set_bclk_ratio); 2514e54cf76bSLiam Girdwood 2515e54cf76bSLiam Girdwood /** 25168c6529dbSLiam Girdwood * snd_soc_dai_set_fmt - configure DAI hardware audio format. 25178c6529dbSLiam Girdwood * @dai: DAI 2518bb19ba2aSRandy Dunlap * @fmt: SND_SOC_DAIFMT_* format value. 25198c6529dbSLiam Girdwood * 25208c6529dbSLiam Girdwood * Configures the DAI hardware format and clocking. 25218c6529dbSLiam Girdwood */ 25228c6529dbSLiam Girdwood int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 25238c6529dbSLiam Girdwood { 25245e4ba569SShawn Guo if (dai->driver == NULL) 25258c6529dbSLiam Girdwood return -EINVAL; 25265e4ba569SShawn Guo if (dai->driver->ops->set_fmt == NULL) 25275e4ba569SShawn Guo return -ENOTSUPP; 25285e4ba569SShawn Guo return dai->driver->ops->set_fmt(dai, fmt); 25298c6529dbSLiam Girdwood } 25308c6529dbSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt); 25318c6529dbSLiam Girdwood 25328c6529dbSLiam Girdwood /** 2533e5c21514SXiubo Li * snd_soc_xlate_tdm_slot - generate tx/rx slot mask. 253489c67857SXiubo Li * @slots: Number of slots in use. 253589c67857SXiubo Li * @tx_mask: bitmask representing active TX slots. 253689c67857SXiubo Li * @rx_mask: bitmask representing active RX slots. 253789c67857SXiubo Li * 253889c67857SXiubo Li * Generates the TDM tx and rx slot default masks for DAI. 253989c67857SXiubo Li */ 2540e5c21514SXiubo Li static int snd_soc_xlate_tdm_slot_mask(unsigned int slots, 254189c67857SXiubo Li unsigned int *tx_mask, 254289c67857SXiubo Li unsigned int *rx_mask) 254389c67857SXiubo Li { 254489c67857SXiubo Li if (*tx_mask || *rx_mask) 254589c67857SXiubo Li return 0; 254689c67857SXiubo Li 254789c67857SXiubo Li if (!slots) 254889c67857SXiubo Li return -EINVAL; 254989c67857SXiubo Li 255089c67857SXiubo Li *tx_mask = (1 << slots) - 1; 255189c67857SXiubo Li *rx_mask = (1 << slots) - 1; 255289c67857SXiubo Li 255389c67857SXiubo Li return 0; 255489c67857SXiubo Li } 255589c67857SXiubo Li 255689c67857SXiubo Li /** 2557e46c9366SLars-Peter Clausen * snd_soc_dai_set_tdm_slot() - Configures a DAI for TDM operation 2558e46c9366SLars-Peter Clausen * @dai: The DAI to configure 2559a5479e38SDaniel Ribeiro * @tx_mask: bitmask representing active TX slots. 2560a5479e38SDaniel Ribeiro * @rx_mask: bitmask representing active RX slots. 25618c6529dbSLiam Girdwood * @slots: Number of slots in use. 2562a5479e38SDaniel Ribeiro * @slot_width: Width in bits for each slot. 25638c6529dbSLiam Girdwood * 2564e46c9366SLars-Peter Clausen * This function configures the specified DAI for TDM operation. @slot contains 2565e46c9366SLars-Peter Clausen * the total number of slots of the TDM stream and @slot_with the width of each 2566e46c9366SLars-Peter Clausen * slot in bit clock cycles. @tx_mask and @rx_mask are bitmasks specifying the 2567e46c9366SLars-Peter Clausen * active slots of the TDM stream for the specified DAI, i.e. which slots the 2568e46c9366SLars-Peter Clausen * DAI should write to or read from. If a bit is set the corresponding slot is 2569e46c9366SLars-Peter Clausen * active, if a bit is cleared the corresponding slot is inactive. Bit 0 maps to 2570e46c9366SLars-Peter Clausen * the first slot, bit 1 to the second slot and so on. The first active slot 2571e46c9366SLars-Peter Clausen * maps to the first channel of the DAI, the second active slot to the second 2572e46c9366SLars-Peter Clausen * channel and so on. 2573e46c9366SLars-Peter Clausen * 2574e46c9366SLars-Peter Clausen * TDM mode can be disabled by passing 0 for @slots. In this case @tx_mask, 2575e46c9366SLars-Peter Clausen * @rx_mask and @slot_width will be ignored. 2576e46c9366SLars-Peter Clausen * 2577e46c9366SLars-Peter Clausen * Returns 0 on success, a negative error code otherwise. 25788c6529dbSLiam Girdwood */ 25798c6529dbSLiam Girdwood int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, 2580a5479e38SDaniel Ribeiro unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) 25818c6529dbSLiam Girdwood { 258246471925SKuninori Morimoto if (dai->driver->ops->xlate_tdm_slot_mask) 2583e5c21514SXiubo Li dai->driver->ops->xlate_tdm_slot_mask(slots, 258489c67857SXiubo Li &tx_mask, &rx_mask); 258589c67857SXiubo Li else 2586e5c21514SXiubo Li snd_soc_xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask); 258789c67857SXiubo Li 258888bd870fSBenoit Cousson dai->tx_mask = tx_mask; 258988bd870fSBenoit Cousson dai->rx_mask = rx_mask; 259088bd870fSBenoit Cousson 259146471925SKuninori Morimoto if (dai->driver->ops->set_tdm_slot) 2592f0fba2adSLiam Girdwood return dai->driver->ops->set_tdm_slot(dai, tx_mask, rx_mask, 2593a5479e38SDaniel Ribeiro slots, slot_width); 25948c6529dbSLiam Girdwood else 2595b2cbb6e1SXiubo Li return -ENOTSUPP; 25968c6529dbSLiam Girdwood } 25978c6529dbSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot); 25988c6529dbSLiam Girdwood 25998c6529dbSLiam Girdwood /** 2600472df3cbSBarry Song * snd_soc_dai_set_channel_map - configure DAI audio channel map 2601472df3cbSBarry Song * @dai: DAI 2602472df3cbSBarry Song * @tx_num: how many TX channels 2603472df3cbSBarry Song * @tx_slot: pointer to an array which imply the TX slot number channel 2604472df3cbSBarry Song * 0~num-1 uses 2605472df3cbSBarry Song * @rx_num: how many RX channels 2606472df3cbSBarry Song * @rx_slot: pointer to an array which imply the RX slot number channel 2607472df3cbSBarry Song * 0~num-1 uses 2608472df3cbSBarry Song * 2609472df3cbSBarry Song * configure the relationship between channel number and TDM slot number. 2610472df3cbSBarry Song */ 2611472df3cbSBarry Song int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai, 2612472df3cbSBarry Song unsigned int tx_num, unsigned int *tx_slot, 2613472df3cbSBarry Song unsigned int rx_num, unsigned int *rx_slot) 2614472df3cbSBarry Song { 261546471925SKuninori Morimoto if (dai->driver->ops->set_channel_map) 2616f0fba2adSLiam Girdwood return dai->driver->ops->set_channel_map(dai, tx_num, tx_slot, 2617472df3cbSBarry Song rx_num, rx_slot); 2618472df3cbSBarry Song else 2619472df3cbSBarry Song return -EINVAL; 2620472df3cbSBarry Song } 2621472df3cbSBarry Song EXPORT_SYMBOL_GPL(snd_soc_dai_set_channel_map); 2622472df3cbSBarry Song 2623472df3cbSBarry Song /** 2624467b061fSSrinivas Kandagatla * snd_soc_dai_get_channel_map - Get DAI audio channel map 2625467b061fSSrinivas Kandagatla * @dai: DAI 2626467b061fSSrinivas Kandagatla * @tx_num: how many TX channels 2627467b061fSSrinivas Kandagatla * @tx_slot: pointer to an array which imply the TX slot number channel 2628467b061fSSrinivas Kandagatla * 0~num-1 uses 2629467b061fSSrinivas Kandagatla * @rx_num: how many RX channels 2630467b061fSSrinivas Kandagatla * @rx_slot: pointer to an array which imply the RX slot number channel 2631467b061fSSrinivas Kandagatla * 0~num-1 uses 2632467b061fSSrinivas Kandagatla */ 2633467b061fSSrinivas Kandagatla int snd_soc_dai_get_channel_map(struct snd_soc_dai *dai, 2634467b061fSSrinivas Kandagatla unsigned int *tx_num, unsigned int *tx_slot, 2635467b061fSSrinivas Kandagatla unsigned int *rx_num, unsigned int *rx_slot) 2636467b061fSSrinivas Kandagatla { 2637467b061fSSrinivas Kandagatla if (dai->driver->ops->get_channel_map) 2638467b061fSSrinivas Kandagatla return dai->driver->ops->get_channel_map(dai, tx_num, tx_slot, 2639467b061fSSrinivas Kandagatla rx_num, rx_slot); 2640467b061fSSrinivas Kandagatla else 2641467b061fSSrinivas Kandagatla return -ENOTSUPP; 2642467b061fSSrinivas Kandagatla } 2643467b061fSSrinivas Kandagatla EXPORT_SYMBOL_GPL(snd_soc_dai_get_channel_map); 2644467b061fSSrinivas Kandagatla 2645467b061fSSrinivas Kandagatla /** 26468c6529dbSLiam Girdwood * snd_soc_dai_set_tristate - configure DAI system or master clock. 26478c6529dbSLiam Girdwood * @dai: DAI 26488c6529dbSLiam Girdwood * @tristate: tristate enable 26498c6529dbSLiam Girdwood * 26508c6529dbSLiam Girdwood * Tristates the DAI so that others can use it. 26518c6529dbSLiam Girdwood */ 26528c6529dbSLiam Girdwood int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate) 26538c6529dbSLiam Girdwood { 265446471925SKuninori Morimoto if (dai->driver->ops->set_tristate) 2655f0fba2adSLiam Girdwood return dai->driver->ops->set_tristate(dai, tristate); 26568c6529dbSLiam Girdwood else 26578c6529dbSLiam Girdwood return -EINVAL; 26588c6529dbSLiam Girdwood } 26598c6529dbSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dai_set_tristate); 26608c6529dbSLiam Girdwood 26618c6529dbSLiam Girdwood /** 26628c6529dbSLiam Girdwood * snd_soc_dai_digital_mute - configure DAI system or master clock. 26638c6529dbSLiam Girdwood * @dai: DAI 26648c6529dbSLiam Girdwood * @mute: mute enable 2665da18396fSMark Brown * @direction: stream to mute 26668c6529dbSLiam Girdwood * 26678c6529dbSLiam Girdwood * Mutes the DAI DAC. 26688c6529dbSLiam Girdwood */ 2669da18396fSMark Brown int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute, 2670da18396fSMark Brown int direction) 26718c6529dbSLiam Girdwood { 2672da18396fSMark Brown if (!dai->driver) 2673da18396fSMark Brown return -ENOTSUPP; 2674da18396fSMark Brown 2675da18396fSMark Brown if (dai->driver->ops->mute_stream) 2676da18396fSMark Brown return dai->driver->ops->mute_stream(dai, mute, direction); 2677da18396fSMark Brown else if (direction == SNDRV_PCM_STREAM_PLAYBACK && 2678da18396fSMark Brown dai->driver->ops->digital_mute) 2679f0fba2adSLiam Girdwood return dai->driver->ops->digital_mute(dai, mute); 26808c6529dbSLiam Girdwood else 268104570c62SMark Brown return -ENOTSUPP; 26828c6529dbSLiam Girdwood } 26838c6529dbSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute); 26848c6529dbSLiam Girdwood 2685c5af3a2eSMark Brown /** 2686c5af3a2eSMark Brown * snd_soc_register_card - Register a card with the ASoC core 2687c5af3a2eSMark Brown * 2688ac11a2b3SMark Brown * @card: Card to register 2689c5af3a2eSMark Brown * 2690c5af3a2eSMark Brown */ 269170a7ca34SVinod Koul int snd_soc_register_card(struct snd_soc_card *card) 2692c5af3a2eSMark Brown { 2693923c5e61SMengdong Lin int i, ret; 26941a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd; 2695f0fba2adSLiam Girdwood 2696c5af3a2eSMark Brown if (!card->name || !card->dev) 2697c5af3a2eSMark Brown return -EINVAL; 2698c5af3a2eSMark Brown 26995a504963SStephen Warren for (i = 0; i < card->num_links; i++) { 27005a504963SStephen Warren struct snd_soc_dai_link *link = &card->dai_link[i]; 27015a504963SStephen Warren 2702923c5e61SMengdong Lin ret = soc_init_dai_link(card, link); 270388bd870fSBenoit Cousson if (ret) { 2704923c5e61SMengdong Lin dev_err(card->dev, "ASoC: failed to init link %s\n", 2705923c5e61SMengdong Lin link->name); 270688bd870fSBenoit Cousson return ret; 270788bd870fSBenoit Cousson } 27085a504963SStephen Warren } 27095a504963SStephen Warren 2710ed77cc12SMark Brown dev_set_drvdata(card->dev, card); 2711ed77cc12SMark Brown 2712111c6419SStephen Warren snd_soc_initialize_card_lists(card); 2713111c6419SStephen Warren 2714f8f80361SMengdong Lin INIT_LIST_HEAD(&card->dai_link_list); 2715f8f80361SMengdong Lin card->num_dai_links = 0; 2716f0fba2adSLiam Girdwood 27171a497983SMengdong Lin INIT_LIST_HEAD(&card->rtd_list); 27189115171aSMark Brown card->num_rtd = 0; 2719db2a4165SFrank Mandarino 2720db432b41SMark Brown INIT_LIST_HEAD(&card->dapm_dirty); 27218a978234SLiam Girdwood INIT_LIST_HEAD(&card->dobj_list); 2722db2a4165SFrank Mandarino card->instantiated = 0; 2723db2a4165SFrank Mandarino mutex_init(&card->mutex); 2724a73fb2dfSLiam Girdwood mutex_init(&card->dapm_mutex); 2725db2a4165SFrank Mandarino 2726b19e6e7bSMark Brown ret = snd_soc_instantiate_card(card); 2727b19e6e7bSMark Brown if (ret != 0) 27284e2576bdSKuninori Morimoto return ret; 2729db2a4165SFrank Mandarino 2730988e8cc4SNicolin Chen /* deactivate pins to sleep state */ 27311a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 273288bd870fSBenoit Cousson struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 27330b7990e3SKuninori Morimoto struct snd_soc_dai *codec_dai; 273488bd870fSBenoit Cousson int j; 273588bd870fSBenoit Cousson 27360b7990e3SKuninori Morimoto for_each_rtd_codec_dai(rtd, j, codec_dai) { 2737988e8cc4SNicolin Chen if (!codec_dai->active) 2738988e8cc4SNicolin Chen pinctrl_pm_select_sleep_state(codec_dai->dev); 273988bd870fSBenoit Cousson } 274088bd870fSBenoit Cousson 2741988e8cc4SNicolin Chen if (!cpu_dai->active) 2742988e8cc4SNicolin Chen pinctrl_pm_select_sleep_state(cpu_dai->dev); 2743988e8cc4SNicolin Chen } 2744988e8cc4SNicolin Chen 2745b19e6e7bSMark Brown return ret; 2746db2a4165SFrank Mandarino } 274770a7ca34SVinod Koul EXPORT_SYMBOL_GPL(snd_soc_register_card); 2748db2a4165SFrank Mandarino 2749db2a4165SFrank Mandarino /** 2750db2a4165SFrank Mandarino * snd_soc_unregister_card - Unregister a card with the ASoC core 2751db2a4165SFrank Mandarino * 2752db2a4165SFrank Mandarino * @card: Card to unregister 2753db2a4165SFrank Mandarino * 2754db2a4165SFrank Mandarino */ 275570a7ca34SVinod Koul int snd_soc_unregister_card(struct snd_soc_card *card) 2756db2a4165SFrank Mandarino { 275701e0df66SLars-Peter Clausen if (card->instantiated) { 275801e0df66SLars-Peter Clausen card->instantiated = false; 27591c325f77SLars-Peter Clausen snd_soc_dapm_shutdown(card); 2760b0e26485SVinod Koul soc_cleanup_card_resources(card); 2761f110bfc7SLiam Girdwood dev_dbg(card->dev, "ASoC: Unregistered card '%s'\n", card->name); 27628f6f9b29SKuninori Morimoto } 2763db2a4165SFrank Mandarino 2764db2a4165SFrank Mandarino return 0; 2765db2a4165SFrank Mandarino } 276670a7ca34SVinod Koul EXPORT_SYMBOL_GPL(snd_soc_unregister_card); 2767db2a4165SFrank Mandarino 2768db2a4165SFrank Mandarino /* 2769db2a4165SFrank Mandarino * Simplify DAI link configuration by removing ".-1" from device names 2770db2a4165SFrank Mandarino * and sanitizing names. 2771db2a4165SFrank Mandarino */ 27720b9a214aSDimitris Papastamos static char *fmt_single_name(struct device *dev, int *id) 2773db2a4165SFrank Mandarino { 2774db2a4165SFrank Mandarino char *found, name[NAME_SIZE]; 2775db2a4165SFrank Mandarino int id1, id2; 2776db2a4165SFrank Mandarino 2777db2a4165SFrank Mandarino if (dev_name(dev) == NULL) 2778db2a4165SFrank Mandarino return NULL; 2779db2a4165SFrank Mandarino 278058818a77SDimitris Papastamos strlcpy(name, dev_name(dev), NAME_SIZE); 2781db2a4165SFrank Mandarino 2782db2a4165SFrank Mandarino /* are we a "%s.%d" name (platform and SPI components) */ 2783c5af3a2eSMark Brown found = strstr(name, dev->driver->name); 2784c5af3a2eSMark Brown if (found) { 2785c5af3a2eSMark Brown /* get ID */ 2786c5af3a2eSMark Brown if (sscanf(&found[strlen(dev->driver->name)], ".%d", id) == 1) { 2787c5af3a2eSMark Brown 2788c5af3a2eSMark Brown /* discard ID from name if ID == -1 */ 2789c5af3a2eSMark Brown if (*id == -1) 2790c5af3a2eSMark Brown found[strlen(dev->driver->name)] = '\0'; 2791c5af3a2eSMark Brown } 2792c5af3a2eSMark Brown 2793c5af3a2eSMark Brown } else { 2794c5af3a2eSMark Brown /* I2C component devices are named "bus-addr" */ 2795c5af3a2eSMark Brown if (sscanf(name, "%x-%x", &id1, &id2) == 2) { 2796c5af3a2eSMark Brown char tmp[NAME_SIZE]; 2797c5af3a2eSMark Brown 2798c5af3a2eSMark Brown /* create unique ID number from I2C addr and bus */ 2799c5af3a2eSMark Brown *id = ((id1 & 0xffff) << 16) + id2; 2800c5af3a2eSMark Brown 2801c5af3a2eSMark Brown /* sanitize component name for DAI link creation */ 2802c5af3a2eSMark Brown snprintf(tmp, NAME_SIZE, "%s.%s", dev->driver->name, name); 280358818a77SDimitris Papastamos strlcpy(name, tmp, NAME_SIZE); 2804c5af3a2eSMark Brown } else 2805c5af3a2eSMark Brown *id = 0; 2806c5af3a2eSMark Brown } 2807c5af3a2eSMark Brown 2808c5af3a2eSMark Brown return kstrdup(name, GFP_KERNEL); 2809c5af3a2eSMark Brown } 2810c5af3a2eSMark Brown 2811c5af3a2eSMark Brown /* 2812c5af3a2eSMark Brown * Simplify DAI link naming for single devices with multiple DAIs by removing 2813c5af3a2eSMark Brown * any ".-1" and using the DAI name (instead of device name). 2814c5af3a2eSMark Brown */ 2815c5af3a2eSMark Brown static inline char *fmt_multiple_name(struct device *dev, 2816c5af3a2eSMark Brown struct snd_soc_dai_driver *dai_drv) 2817c5af3a2eSMark Brown { 2818c5af3a2eSMark Brown if (dai_drv->name == NULL) { 281910e8aa9aSMichał Mirosław dev_err(dev, 282010e8aa9aSMichał Mirosław "ASoC: error - multiple DAI %s registered with no name\n", 282110e8aa9aSMichał Mirosław dev_name(dev)); 2822c5af3a2eSMark Brown return NULL; 2823c5af3a2eSMark Brown } 2824c5af3a2eSMark Brown 2825c5af3a2eSMark Brown return kstrdup(dai_drv->name, GFP_KERNEL); 2826c5af3a2eSMark Brown } 2827c5af3a2eSMark Brown 2828c5af3a2eSMark Brown /** 282932c9ba54SLars-Peter Clausen * snd_soc_unregister_dai - Unregister DAIs from the ASoC core 28309115171aSMark Brown * 283132c9ba54SLars-Peter Clausen * @component: The component for which the DAIs should be unregistered 28329115171aSMark Brown */ 283332c9ba54SLars-Peter Clausen static void snd_soc_unregister_dais(struct snd_soc_component *component) 28349115171aSMark Brown { 28355c1d5f09SLars-Peter Clausen struct snd_soc_dai *dai, *_dai; 28369115171aSMark Brown 28375c1d5f09SLars-Peter Clausen list_for_each_entry_safe(dai, _dai, &component->dai_list, list) { 283832c9ba54SLars-Peter Clausen dev_dbg(component->dev, "ASoC: Unregistered DAI '%s'\n", 283932c9ba54SLars-Peter Clausen dai->name); 28409115171aSMark Brown list_del(&dai->list); 2841f0fba2adSLiam Girdwood kfree(dai->name); 2842f0fba2adSLiam Girdwood kfree(dai); 28439115171aSMark Brown } 284432c9ba54SLars-Peter Clausen } 28459115171aSMark Brown 28465e4fb372SMengdong Lin /* Create a DAI and add it to the component's DAI list */ 28475e4fb372SMengdong Lin static struct snd_soc_dai *soc_add_dai(struct snd_soc_component *component, 28485e4fb372SMengdong Lin struct snd_soc_dai_driver *dai_drv, 28495e4fb372SMengdong Lin bool legacy_dai_naming) 28505e4fb372SMengdong Lin { 28515e4fb372SMengdong Lin struct device *dev = component->dev; 28525e4fb372SMengdong Lin struct snd_soc_dai *dai; 28535e4fb372SMengdong Lin 28545e4fb372SMengdong Lin dev_dbg(dev, "ASoC: dynamically register DAI %s\n", dev_name(dev)); 28555e4fb372SMengdong Lin 28565e4fb372SMengdong Lin dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL); 28575e4fb372SMengdong Lin if (dai == NULL) 28585e4fb372SMengdong Lin return NULL; 28595e4fb372SMengdong Lin 28605e4fb372SMengdong Lin /* 28615e4fb372SMengdong Lin * Back in the old days when we still had component-less DAIs, 28625e4fb372SMengdong Lin * instead of having a static name, component-less DAIs would 28635e4fb372SMengdong Lin * inherit the name of the parent device so it is possible to 28645e4fb372SMengdong Lin * register multiple instances of the DAI. We still need to keep 28655e4fb372SMengdong Lin * the same naming style even though those DAIs are not 28665e4fb372SMengdong Lin * component-less anymore. 28675e4fb372SMengdong Lin */ 28685e4fb372SMengdong Lin if (legacy_dai_naming && 28695e4fb372SMengdong Lin (dai_drv->id == 0 || dai_drv->name == NULL)) { 28705e4fb372SMengdong Lin dai->name = fmt_single_name(dev, &dai->id); 28715e4fb372SMengdong Lin } else { 28725e4fb372SMengdong Lin dai->name = fmt_multiple_name(dev, dai_drv); 28735e4fb372SMengdong Lin if (dai_drv->id) 28745e4fb372SMengdong Lin dai->id = dai_drv->id; 28755e4fb372SMengdong Lin else 28765e4fb372SMengdong Lin dai->id = component->num_dai; 28775e4fb372SMengdong Lin } 28785e4fb372SMengdong Lin if (dai->name == NULL) { 28795e4fb372SMengdong Lin kfree(dai); 28805e4fb372SMengdong Lin return NULL; 28815e4fb372SMengdong Lin } 28825e4fb372SMengdong Lin 28835e4fb372SMengdong Lin dai->component = component; 28845e4fb372SMengdong Lin dai->dev = dev; 28855e4fb372SMengdong Lin dai->driver = dai_drv; 28865e4fb372SMengdong Lin if (!dai->driver->ops) 28875e4fb372SMengdong Lin dai->driver->ops = &null_dai_ops; 28885e4fb372SMengdong Lin 288958bf4179SKuninori Morimoto list_add_tail(&dai->list, &component->dai_list); 28905e4fb372SMengdong Lin component->num_dai++; 28915e4fb372SMengdong Lin 28925e4fb372SMengdong Lin dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name); 28935e4fb372SMengdong Lin return dai; 28945e4fb372SMengdong Lin } 28955e4fb372SMengdong Lin 28969115171aSMark Brown /** 289732c9ba54SLars-Peter Clausen * snd_soc_register_dais - Register a DAI with the ASoC core 28989115171aSMark Brown * 28996106d129SLars-Peter Clausen * @component: The component the DAIs are registered for 29006106d129SLars-Peter Clausen * @dai_drv: DAI driver to use for the DAIs 2901ac11a2b3SMark Brown * @count: Number of DAIs 29029115171aSMark Brown */ 290332c9ba54SLars-Peter Clausen static int snd_soc_register_dais(struct snd_soc_component *component, 29040e7b25c6SKuninori Morimoto struct snd_soc_dai_driver *dai_drv, size_t count) 29059115171aSMark Brown { 29066106d129SLars-Peter Clausen struct device *dev = component->dev; 2907f0fba2adSLiam Girdwood struct snd_soc_dai *dai; 290832c9ba54SLars-Peter Clausen unsigned int i; 290932c9ba54SLars-Peter Clausen int ret; 2910f0fba2adSLiam Girdwood 29115b5e0928SAlexey Dobriyan dev_dbg(dev, "ASoC: dai register %s #%zu\n", dev_name(dev), count); 29129115171aSMark Brown 29139115171aSMark Brown for (i = 0; i < count; i++) { 2914f0fba2adSLiam Girdwood 29155e4fb372SMengdong Lin dai = soc_add_dai(component, dai_drv + i, 29160e7b25c6SKuninori Morimoto count == 1 && !component->driver->non_legacy_dai_naming); 2917c46e0079SAxel Lin if (dai == NULL) { 2918c46e0079SAxel Lin ret = -ENOMEM; 2919c46e0079SAxel Lin goto err; 2920c46e0079SAxel Lin } 2921f0fba2adSLiam Girdwood } 2922f0fba2adSLiam Girdwood 29239115171aSMark Brown return 0; 29249115171aSMark Brown 29259115171aSMark Brown err: 292632c9ba54SLars-Peter Clausen snd_soc_unregister_dais(component); 29279115171aSMark Brown 29289115171aSMark Brown return ret; 29299115171aSMark Brown } 29309115171aSMark Brown 293168003e6cSMengdong Lin /** 293268003e6cSMengdong Lin * snd_soc_register_dai - Register a DAI dynamically & create its widgets 293368003e6cSMengdong Lin * 293468003e6cSMengdong Lin * @component: The component the DAIs are registered for 293568003e6cSMengdong Lin * @dai_drv: DAI driver to use for the DAI 293668003e6cSMengdong Lin * 293768003e6cSMengdong Lin * Topology can use this API to register DAIs when probing a component. 293868003e6cSMengdong Lin * These DAIs's widgets will be freed in the card cleanup and the DAIs 293968003e6cSMengdong Lin * will be freed in the component cleanup. 294068003e6cSMengdong Lin */ 294168003e6cSMengdong Lin int snd_soc_register_dai(struct snd_soc_component *component, 294268003e6cSMengdong Lin struct snd_soc_dai_driver *dai_drv) 294368003e6cSMengdong Lin { 294468003e6cSMengdong Lin struct snd_soc_dapm_context *dapm = 294568003e6cSMengdong Lin snd_soc_component_get_dapm(component); 294668003e6cSMengdong Lin struct snd_soc_dai *dai; 294768003e6cSMengdong Lin int ret; 294868003e6cSMengdong Lin 294968003e6cSMengdong Lin if (dai_drv->dobj.type != SND_SOC_DOBJ_PCM) { 295068003e6cSMengdong Lin dev_err(component->dev, "Invalid dai type %d\n", 295168003e6cSMengdong Lin dai_drv->dobj.type); 295268003e6cSMengdong Lin return -EINVAL; 295368003e6cSMengdong Lin } 295468003e6cSMengdong Lin 295568003e6cSMengdong Lin lockdep_assert_held(&client_mutex); 295668003e6cSMengdong Lin dai = soc_add_dai(component, dai_drv, false); 295768003e6cSMengdong Lin if (!dai) 295868003e6cSMengdong Lin return -ENOMEM; 295968003e6cSMengdong Lin 296068003e6cSMengdong Lin /* Create the DAI widgets here. After adding DAIs, topology may 296168003e6cSMengdong Lin * also add routes that need these widgets as source or sink. 296268003e6cSMengdong Lin */ 296368003e6cSMengdong Lin ret = snd_soc_dapm_new_dai_widgets(dapm, dai); 296468003e6cSMengdong Lin if (ret != 0) { 296568003e6cSMengdong Lin dev_err(component->dev, 296668003e6cSMengdong Lin "Failed to create DAI widgets %d\n", ret); 296768003e6cSMengdong Lin } 296868003e6cSMengdong Lin 296968003e6cSMengdong Lin return ret; 297068003e6cSMengdong Lin } 297168003e6cSMengdong Lin EXPORT_SYMBOL_GPL(snd_soc_register_dai); 297268003e6cSMengdong Lin 297314e8bdebSLars-Peter Clausen static void snd_soc_component_seq_notifier(struct snd_soc_dapm_context *dapm, 297414e8bdebSLars-Peter Clausen enum snd_soc_dapm_type type, int subseq) 2975d191bd8dSKuninori Morimoto { 297614e8bdebSLars-Peter Clausen struct snd_soc_component *component = dapm->component; 2977d191bd8dSKuninori Morimoto 297814e8bdebSLars-Peter Clausen component->driver->seq_notifier(component, type, subseq); 297914e8bdebSLars-Peter Clausen } 2980d191bd8dSKuninori Morimoto 298114e8bdebSLars-Peter Clausen static int snd_soc_component_stream_event(struct snd_soc_dapm_context *dapm, 298214e8bdebSLars-Peter Clausen int event) 298314e8bdebSLars-Peter Clausen { 298414e8bdebSLars-Peter Clausen struct snd_soc_component *component = dapm->component; 298514e8bdebSLars-Peter Clausen 298614e8bdebSLars-Peter Clausen return component->driver->stream_event(component, event); 298714e8bdebSLars-Peter Clausen } 298814e8bdebSLars-Peter Clausen 29897ba236ceSKuninori Morimoto static int snd_soc_component_set_bias_level(struct snd_soc_dapm_context *dapm, 29907ba236ceSKuninori Morimoto enum snd_soc_bias_level level) 29917ba236ceSKuninori Morimoto { 29927ba236ceSKuninori Morimoto struct snd_soc_component *component = dapm->component; 29937ba236ceSKuninori Morimoto 29947ba236ceSKuninori Morimoto return component->driver->set_bias_level(component, level); 29957ba236ceSKuninori Morimoto } 29967ba236ceSKuninori Morimoto 2997bb13109dSLars-Peter Clausen static int snd_soc_component_initialize(struct snd_soc_component *component, 2998bb13109dSLars-Peter Clausen const struct snd_soc_component_driver *driver, struct device *dev) 2999d191bd8dSKuninori Morimoto { 3000ce0fc93aSLars-Peter Clausen struct snd_soc_dapm_context *dapm; 3001ce0fc93aSLars-Peter Clausen 3002bb13109dSLars-Peter Clausen component->name = fmt_single_name(dev, &component->id); 3003bb13109dSLars-Peter Clausen if (!component->name) { 3004bb13109dSLars-Peter Clausen dev_err(dev, "ASoC: Failed to allocate name\n"); 3005d191bd8dSKuninori Morimoto return -ENOMEM; 3006d191bd8dSKuninori Morimoto } 3007d191bd8dSKuninori Morimoto 3008bb13109dSLars-Peter Clausen component->dev = dev; 3009bb13109dSLars-Peter Clausen component->driver = driver; 3010e2c330b9SLars-Peter Clausen 301188c27465SKuninori Morimoto dapm = snd_soc_component_get_dapm(component); 3012ce0fc93aSLars-Peter Clausen dapm->dev = dev; 3013ce0fc93aSLars-Peter Clausen dapm->component = component; 3014ce0fc93aSLars-Peter Clausen dapm->bias_level = SND_SOC_BIAS_OFF; 30157ba236ceSKuninori Morimoto dapm->idle_bias_off = !driver->idle_bias_on; 30167ba236ceSKuninori Morimoto dapm->suspend_bias_off = driver->suspend_bias_off; 301714e8bdebSLars-Peter Clausen if (driver->seq_notifier) 301814e8bdebSLars-Peter Clausen dapm->seq_notifier = snd_soc_component_seq_notifier; 301914e8bdebSLars-Peter Clausen if (driver->stream_event) 302014e8bdebSLars-Peter Clausen dapm->stream_event = snd_soc_component_stream_event; 30217ba236ceSKuninori Morimoto if (driver->set_bias_level) 30227ba236ceSKuninori Morimoto dapm->set_bias_level = snd_soc_component_set_bias_level; 3023ce0fc93aSLars-Peter Clausen 3024bb13109dSLars-Peter Clausen INIT_LIST_HEAD(&component->dai_list); 3025bb13109dSLars-Peter Clausen mutex_init(&component->io_mutex); 3026bb13109dSLars-Peter Clausen 3027bb13109dSLars-Peter Clausen return 0; 3028d191bd8dSKuninori Morimoto } 3029d191bd8dSKuninori Morimoto 303020feb881SLars-Peter Clausen static void snd_soc_component_setup_regmap(struct snd_soc_component *component) 3031886f5692SLars-Peter Clausen { 3032886f5692SLars-Peter Clausen int val_bytes = regmap_get_val_bytes(component->regmap); 303320feb881SLars-Peter Clausen 3034886f5692SLars-Peter Clausen /* Errors are legitimate for non-integer byte multiples */ 3035886f5692SLars-Peter Clausen if (val_bytes > 0) 3036886f5692SLars-Peter Clausen component->val_bytes = val_bytes; 3037886f5692SLars-Peter Clausen } 303820feb881SLars-Peter Clausen 3039e874bf5fSLars-Peter Clausen #ifdef CONFIG_REGMAP 3040e874bf5fSLars-Peter Clausen 304120feb881SLars-Peter Clausen /** 304220feb881SLars-Peter Clausen * snd_soc_component_init_regmap() - Initialize regmap instance for the component 304320feb881SLars-Peter Clausen * @component: The component for which to initialize the regmap instance 304420feb881SLars-Peter Clausen * @regmap: The regmap instance that should be used by the component 304520feb881SLars-Peter Clausen * 304620feb881SLars-Peter Clausen * This function allows deferred assignment of the regmap instance that is 304720feb881SLars-Peter Clausen * associated with the component. Only use this if the regmap instance is not 304820feb881SLars-Peter Clausen * yet ready when the component is registered. The function must also be called 304920feb881SLars-Peter Clausen * before the first IO attempt of the component. 305020feb881SLars-Peter Clausen */ 305120feb881SLars-Peter Clausen void snd_soc_component_init_regmap(struct snd_soc_component *component, 305220feb881SLars-Peter Clausen struct regmap *regmap) 305320feb881SLars-Peter Clausen { 305420feb881SLars-Peter Clausen component->regmap = regmap; 305520feb881SLars-Peter Clausen snd_soc_component_setup_regmap(component); 3056886f5692SLars-Peter Clausen } 305720feb881SLars-Peter Clausen EXPORT_SYMBOL_GPL(snd_soc_component_init_regmap); 305820feb881SLars-Peter Clausen 305920feb881SLars-Peter Clausen /** 306020feb881SLars-Peter Clausen * snd_soc_component_exit_regmap() - De-initialize regmap instance for the component 306120feb881SLars-Peter Clausen * @component: The component for which to de-initialize the regmap instance 306220feb881SLars-Peter Clausen * 306320feb881SLars-Peter Clausen * Calls regmap_exit() on the regmap instance associated to the component and 306420feb881SLars-Peter Clausen * removes the regmap instance from the component. 306520feb881SLars-Peter Clausen * 306620feb881SLars-Peter Clausen * This function should only be used if snd_soc_component_init_regmap() was used 306720feb881SLars-Peter Clausen * to initialize the regmap instance. 306820feb881SLars-Peter Clausen */ 306920feb881SLars-Peter Clausen void snd_soc_component_exit_regmap(struct snd_soc_component *component) 307020feb881SLars-Peter Clausen { 307120feb881SLars-Peter Clausen regmap_exit(component->regmap); 307220feb881SLars-Peter Clausen component->regmap = NULL; 307320feb881SLars-Peter Clausen } 307420feb881SLars-Peter Clausen EXPORT_SYMBOL_GPL(snd_soc_component_exit_regmap); 3075886f5692SLars-Peter Clausen 3076e874bf5fSLars-Peter Clausen #endif 3077d191bd8dSKuninori Morimoto 3078359c71eeSKuninori Morimoto static void snd_soc_component_add(struct snd_soc_component *component) 3079bb13109dSLars-Peter Clausen { 3080359c71eeSKuninori Morimoto mutex_lock(&client_mutex); 3081359c71eeSKuninori Morimoto 3082999f7f5aSKuninori Morimoto if (!component->driver->write && !component->driver->read) { 308320feb881SLars-Peter Clausen if (!component->regmap) 308420feb881SLars-Peter Clausen component->regmap = dev_get_regmap(component->dev, NULL); 308520feb881SLars-Peter Clausen if (component->regmap) 308620feb881SLars-Peter Clausen snd_soc_component_setup_regmap(component); 308720feb881SLars-Peter Clausen } 3088886f5692SLars-Peter Clausen 3089bb13109dSLars-Peter Clausen list_add(&component->list, &component_list); 30908a978234SLiam Girdwood INIT_LIST_HEAD(&component->dobj_list); 3091d191bd8dSKuninori Morimoto 3092d191bd8dSKuninori Morimoto mutex_unlock(&client_mutex); 3093bb13109dSLars-Peter Clausen } 3094d191bd8dSKuninori Morimoto 3095bb13109dSLars-Peter Clausen static void snd_soc_component_cleanup(struct snd_soc_component *component) 3096bb13109dSLars-Peter Clausen { 3097bb13109dSLars-Peter Clausen snd_soc_unregister_dais(component); 3098bb13109dSLars-Peter Clausen kfree(component->name); 3099bb13109dSLars-Peter Clausen } 3100d191bd8dSKuninori Morimoto 3101bb13109dSLars-Peter Clausen static void snd_soc_component_del_unlocked(struct snd_soc_component *component) 3102bb13109dSLars-Peter Clausen { 3103c12c1aadSKuninori Morimoto struct snd_soc_card *card = component->card; 3104c12c1aadSKuninori Morimoto 3105c12c1aadSKuninori Morimoto if (card) 3106c12c1aadSKuninori Morimoto snd_soc_unregister_card(card); 3107c12c1aadSKuninori Morimoto 3108bb13109dSLars-Peter Clausen list_del(&component->list); 3109bb13109dSLars-Peter Clausen } 3110d191bd8dSKuninori Morimoto 3111273d778eSKuninori Morimoto #define ENDIANNESS_MAP(name) \ 3112273d778eSKuninori Morimoto (SNDRV_PCM_FMTBIT_##name##LE | SNDRV_PCM_FMTBIT_##name##BE) 3113273d778eSKuninori Morimoto static u64 endianness_format_map[] = { 3114273d778eSKuninori Morimoto ENDIANNESS_MAP(S16_), 3115273d778eSKuninori Morimoto ENDIANNESS_MAP(U16_), 3116273d778eSKuninori Morimoto ENDIANNESS_MAP(S24_), 3117273d778eSKuninori Morimoto ENDIANNESS_MAP(U24_), 3118273d778eSKuninori Morimoto ENDIANNESS_MAP(S32_), 3119273d778eSKuninori Morimoto ENDIANNESS_MAP(U32_), 3120273d778eSKuninori Morimoto ENDIANNESS_MAP(S24_3), 3121273d778eSKuninori Morimoto ENDIANNESS_MAP(U24_3), 3122273d778eSKuninori Morimoto ENDIANNESS_MAP(S20_3), 3123273d778eSKuninori Morimoto ENDIANNESS_MAP(U20_3), 3124273d778eSKuninori Morimoto ENDIANNESS_MAP(S18_3), 3125273d778eSKuninori Morimoto ENDIANNESS_MAP(U18_3), 3126273d778eSKuninori Morimoto ENDIANNESS_MAP(FLOAT_), 3127273d778eSKuninori Morimoto ENDIANNESS_MAP(FLOAT64_), 3128273d778eSKuninori Morimoto ENDIANNESS_MAP(IEC958_SUBFRAME_), 3129273d778eSKuninori Morimoto }; 3130273d778eSKuninori Morimoto 3131273d778eSKuninori Morimoto /* 3132273d778eSKuninori Morimoto * Fix up the DAI formats for endianness: codecs don't actually see 3133273d778eSKuninori Morimoto * the endianness of the data but we're using the CPU format 3134273d778eSKuninori Morimoto * definitions which do need to include endianness so we ensure that 3135273d778eSKuninori Morimoto * codec DAIs always have both big and little endian variants set. 3136273d778eSKuninori Morimoto */ 3137273d778eSKuninori Morimoto static void convert_endianness_formats(struct snd_soc_pcm_stream *stream) 3138273d778eSKuninori Morimoto { 3139273d778eSKuninori Morimoto int i; 3140273d778eSKuninori Morimoto 3141273d778eSKuninori Morimoto for (i = 0; i < ARRAY_SIZE(endianness_format_map); i++) 3142273d778eSKuninori Morimoto if (stream->formats & endianness_format_map[i]) 3143273d778eSKuninori Morimoto stream->formats |= endianness_format_map[i]; 3144273d778eSKuninori Morimoto } 3145273d778eSKuninori Morimoto 3146e0dac41bSKuninori Morimoto int snd_soc_add_component(struct device *dev, 3147e0dac41bSKuninori Morimoto struct snd_soc_component *component, 3148cf9e829eSKuninori Morimoto const struct snd_soc_component_driver *component_driver, 3149d191bd8dSKuninori Morimoto struct snd_soc_dai_driver *dai_drv, 3150d191bd8dSKuninori Morimoto int num_dai) 3151d191bd8dSKuninori Morimoto { 3152bb13109dSLars-Peter Clausen int ret; 3153273d778eSKuninori Morimoto int i; 3154d191bd8dSKuninori Morimoto 3155cf9e829eSKuninori Morimoto ret = snd_soc_component_initialize(component, component_driver, dev); 3156bb13109dSLars-Peter Clausen if (ret) 3157bb13109dSLars-Peter Clausen goto err_free; 3158bb13109dSLars-Peter Clausen 3159273d778eSKuninori Morimoto if (component_driver->endianness) { 3160273d778eSKuninori Morimoto for (i = 0; i < num_dai; i++) { 3161273d778eSKuninori Morimoto convert_endianness_formats(&dai_drv[i].playback); 3162273d778eSKuninori Morimoto convert_endianness_formats(&dai_drv[i].capture); 3163273d778eSKuninori Morimoto } 3164273d778eSKuninori Morimoto } 3165273d778eSKuninori Morimoto 31660e7b25c6SKuninori Morimoto ret = snd_soc_register_dais(component, dai_drv, num_dai); 3167bb13109dSLars-Peter Clausen if (ret < 0) { 3168f42cf8d6SMasanari Iida dev_err(dev, "ASoC: Failed to register DAIs: %d\n", ret); 3169bb13109dSLars-Peter Clausen goto err_cleanup; 3170bb13109dSLars-Peter Clausen } 3171bb13109dSLars-Peter Clausen 3172cf9e829eSKuninori Morimoto snd_soc_component_add(component); 3173bb13109dSLars-Peter Clausen 3174bb13109dSLars-Peter Clausen return 0; 3175bb13109dSLars-Peter Clausen 3176bb13109dSLars-Peter Clausen err_cleanup: 3177cf9e829eSKuninori Morimoto snd_soc_component_cleanup(component); 3178bb13109dSLars-Peter Clausen err_free: 3179bb13109dSLars-Peter Clausen return ret; 3180d191bd8dSKuninori Morimoto } 3181e0dac41bSKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_add_component); 3182e0dac41bSKuninori Morimoto 3183e0dac41bSKuninori Morimoto int snd_soc_register_component(struct device *dev, 3184e0dac41bSKuninori Morimoto const struct snd_soc_component_driver *component_driver, 3185e0dac41bSKuninori Morimoto struct snd_soc_dai_driver *dai_drv, 3186e0dac41bSKuninori Morimoto int num_dai) 3187e0dac41bSKuninori Morimoto { 3188e0dac41bSKuninori Morimoto struct snd_soc_component *component; 3189e0dac41bSKuninori Morimoto 31907ecbd6a9SKuninori Morimoto component = devm_kzalloc(dev, sizeof(*component), GFP_KERNEL); 319108e61d03SKuninori Morimoto if (!component) 3192e0dac41bSKuninori Morimoto return -ENOMEM; 3193e0dac41bSKuninori Morimoto 3194e0dac41bSKuninori Morimoto return snd_soc_add_component(dev, component, component_driver, 3195e0dac41bSKuninori Morimoto dai_drv, num_dai); 3196e0dac41bSKuninori Morimoto } 3197d191bd8dSKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_register_component); 3198d191bd8dSKuninori Morimoto 3199d191bd8dSKuninori Morimoto /** 32002eccea8cSKuninori Morimoto * snd_soc_unregister_component - Unregister all related component 32012eccea8cSKuninori Morimoto * from the ASoC core 3202d191bd8dSKuninori Morimoto * 3203628536eaSJonathan Corbet * @dev: The device to unregister 3204d191bd8dSKuninori Morimoto */ 32052eccea8cSKuninori Morimoto static int __snd_soc_unregister_component(struct device *dev) 3206d191bd8dSKuninori Morimoto { 3207cf9e829eSKuninori Morimoto struct snd_soc_component *component; 320821a03528SKuninori Morimoto int found = 0; 3209d191bd8dSKuninori Morimoto 321034e81ab4SLars-Peter Clausen mutex_lock(&client_mutex); 3211cf9e829eSKuninori Morimoto list_for_each_entry(component, &component_list, list) { 3212999f7f5aSKuninori Morimoto if (dev != component->dev) 321321a03528SKuninori Morimoto continue; 3214d191bd8dSKuninori Morimoto 3215cf9e829eSKuninori Morimoto snd_soc_tplg_component_remove(component, SND_SOC_TPLG_INDEX_ALL); 3216cf9e829eSKuninori Morimoto snd_soc_component_del_unlocked(component); 321721a03528SKuninori Morimoto found = 1; 321821a03528SKuninori Morimoto break; 3219d191bd8dSKuninori Morimoto } 3220d191bd8dSKuninori Morimoto mutex_unlock(&client_mutex); 3221d191bd8dSKuninori Morimoto 322221a03528SKuninori Morimoto if (found) { 3223cf9e829eSKuninori Morimoto snd_soc_component_cleanup(component); 3224d191bd8dSKuninori Morimoto } 32252eccea8cSKuninori Morimoto 32262eccea8cSKuninori Morimoto return found; 32272eccea8cSKuninori Morimoto } 32282eccea8cSKuninori Morimoto 32292eccea8cSKuninori Morimoto void snd_soc_unregister_component(struct device *dev) 32302eccea8cSKuninori Morimoto { 32312eccea8cSKuninori Morimoto while (__snd_soc_unregister_component(dev)); 3232d191bd8dSKuninori Morimoto } 3233d191bd8dSKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_unregister_component); 3234d191bd8dSKuninori Morimoto 32357dd5d0d9SKuninori Morimoto struct snd_soc_component *snd_soc_lookup_component(struct device *dev, 32367dd5d0d9SKuninori Morimoto const char *driver_name) 32377dd5d0d9SKuninori Morimoto { 32387dd5d0d9SKuninori Morimoto struct snd_soc_component *component; 32397dd5d0d9SKuninori Morimoto struct snd_soc_component *ret; 32407dd5d0d9SKuninori Morimoto 32417dd5d0d9SKuninori Morimoto ret = NULL; 32427dd5d0d9SKuninori Morimoto mutex_lock(&client_mutex); 32437dd5d0d9SKuninori Morimoto list_for_each_entry(component, &component_list, list) { 32447dd5d0d9SKuninori Morimoto if (dev != component->dev) 32457dd5d0d9SKuninori Morimoto continue; 32467dd5d0d9SKuninori Morimoto 32477dd5d0d9SKuninori Morimoto if (driver_name && 32487dd5d0d9SKuninori Morimoto (driver_name != component->driver->name) && 32497dd5d0d9SKuninori Morimoto (strcmp(component->driver->name, driver_name) != 0)) 32507dd5d0d9SKuninori Morimoto continue; 32517dd5d0d9SKuninori Morimoto 32527dd5d0d9SKuninori Morimoto ret = component; 32537dd5d0d9SKuninori Morimoto break; 32547dd5d0d9SKuninori Morimoto } 32557dd5d0d9SKuninori Morimoto mutex_unlock(&client_mutex); 32567dd5d0d9SKuninori Morimoto 32577dd5d0d9SKuninori Morimoto return ret; 32587dd5d0d9SKuninori Morimoto } 32597dd5d0d9SKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_lookup_component); 32607dd5d0d9SKuninori Morimoto 3261bec4fa05SStephen Warren /* Retrieve a card's name from device tree */ 3262b07609ceSKuninori Morimoto int snd_soc_of_parse_card_name(struct snd_soc_card *card, 3263bec4fa05SStephen Warren const char *propname) 3264bec4fa05SStephen Warren { 3265b07609ceSKuninori Morimoto struct device_node *np; 3266bec4fa05SStephen Warren int ret; 3267bec4fa05SStephen Warren 32687e07e7c0STushar Behera if (!card->dev) { 32697e07e7c0STushar Behera pr_err("card->dev is not set before calling %s\n", __func__); 32707e07e7c0STushar Behera return -EINVAL; 32717e07e7c0STushar Behera } 32727e07e7c0STushar Behera 32737e07e7c0STushar Behera np = card->dev->of_node; 32747e07e7c0STushar Behera 3275bec4fa05SStephen Warren ret = of_property_read_string_index(np, propname, 0, &card->name); 3276bec4fa05SStephen Warren /* 3277bec4fa05SStephen Warren * EINVAL means the property does not exist. This is fine providing 3278bec4fa05SStephen Warren * card->name was previously set, which is checked later in 3279bec4fa05SStephen Warren * snd_soc_register_card. 3280bec4fa05SStephen Warren */ 3281bec4fa05SStephen Warren if (ret < 0 && ret != -EINVAL) { 3282bec4fa05SStephen Warren dev_err(card->dev, 3283f110bfc7SLiam Girdwood "ASoC: Property '%s' could not be read: %d\n", 3284bec4fa05SStephen Warren propname, ret); 3285bec4fa05SStephen Warren return ret; 3286bec4fa05SStephen Warren } 3287bec4fa05SStephen Warren 3288bec4fa05SStephen Warren return 0; 3289bec4fa05SStephen Warren } 3290b07609ceSKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_of_parse_card_name); 3291bec4fa05SStephen Warren 32929a6d4860SXiubo Li static const struct snd_soc_dapm_widget simple_widgets[] = { 32939a6d4860SXiubo Li SND_SOC_DAPM_MIC("Microphone", NULL), 32949a6d4860SXiubo Li SND_SOC_DAPM_LINE("Line", NULL), 32959a6d4860SXiubo Li SND_SOC_DAPM_HP("Headphone", NULL), 32969a6d4860SXiubo Li SND_SOC_DAPM_SPK("Speaker", NULL), 32979a6d4860SXiubo Li }; 32989a6d4860SXiubo Li 329921efde50SKuninori Morimoto int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card, 33009a6d4860SXiubo Li const char *propname) 33019a6d4860SXiubo Li { 330221efde50SKuninori Morimoto struct device_node *np = card->dev->of_node; 33039a6d4860SXiubo Li struct snd_soc_dapm_widget *widgets; 33049a6d4860SXiubo Li const char *template, *wname; 33059a6d4860SXiubo Li int i, j, num_widgets, ret; 33069a6d4860SXiubo Li 33079a6d4860SXiubo Li num_widgets = of_property_count_strings(np, propname); 33089a6d4860SXiubo Li if (num_widgets < 0) { 33099a6d4860SXiubo Li dev_err(card->dev, 33109a6d4860SXiubo Li "ASoC: Property '%s' does not exist\n", propname); 33119a6d4860SXiubo Li return -EINVAL; 33129a6d4860SXiubo Li } 33139a6d4860SXiubo Li if (num_widgets & 1) { 33149a6d4860SXiubo Li dev_err(card->dev, 33159a6d4860SXiubo Li "ASoC: Property '%s' length is not even\n", propname); 33169a6d4860SXiubo Li return -EINVAL; 33179a6d4860SXiubo Li } 33189a6d4860SXiubo Li 33199a6d4860SXiubo Li num_widgets /= 2; 33209a6d4860SXiubo Li if (!num_widgets) { 33219a6d4860SXiubo Li dev_err(card->dev, "ASoC: Property '%s's length is zero\n", 33229a6d4860SXiubo Li propname); 33239a6d4860SXiubo Li return -EINVAL; 33249a6d4860SXiubo Li } 33259a6d4860SXiubo Li 33269a6d4860SXiubo Li widgets = devm_kcalloc(card->dev, num_widgets, sizeof(*widgets), 33279a6d4860SXiubo Li GFP_KERNEL); 33289a6d4860SXiubo Li if (!widgets) { 33299a6d4860SXiubo Li dev_err(card->dev, 33309a6d4860SXiubo Li "ASoC: Could not allocate memory for widgets\n"); 33319a6d4860SXiubo Li return -ENOMEM; 33329a6d4860SXiubo Li } 33339a6d4860SXiubo Li 33349a6d4860SXiubo Li for (i = 0; i < num_widgets; i++) { 33359a6d4860SXiubo Li ret = of_property_read_string_index(np, propname, 33369a6d4860SXiubo Li 2 * i, &template); 33379a6d4860SXiubo Li if (ret) { 33389a6d4860SXiubo Li dev_err(card->dev, 33399a6d4860SXiubo Li "ASoC: Property '%s' index %d read error:%d\n", 33409a6d4860SXiubo Li propname, 2 * i, ret); 33419a6d4860SXiubo Li return -EINVAL; 33429a6d4860SXiubo Li } 33439a6d4860SXiubo Li 33449a6d4860SXiubo Li for (j = 0; j < ARRAY_SIZE(simple_widgets); j++) { 33459a6d4860SXiubo Li if (!strncmp(template, simple_widgets[j].name, 33469a6d4860SXiubo Li strlen(simple_widgets[j].name))) { 33479a6d4860SXiubo Li widgets[i] = simple_widgets[j]; 33489a6d4860SXiubo Li break; 33499a6d4860SXiubo Li } 33509a6d4860SXiubo Li } 33519a6d4860SXiubo Li 33529a6d4860SXiubo Li if (j >= ARRAY_SIZE(simple_widgets)) { 33539a6d4860SXiubo Li dev_err(card->dev, 33549a6d4860SXiubo Li "ASoC: DAPM widget '%s' is not supported\n", 33559a6d4860SXiubo Li template); 33569a6d4860SXiubo Li return -EINVAL; 33579a6d4860SXiubo Li } 33589a6d4860SXiubo Li 33599a6d4860SXiubo Li ret = of_property_read_string_index(np, propname, 33609a6d4860SXiubo Li (2 * i) + 1, 33619a6d4860SXiubo Li &wname); 33629a6d4860SXiubo Li if (ret) { 33639a6d4860SXiubo Li dev_err(card->dev, 33649a6d4860SXiubo Li "ASoC: Property '%s' index %d read error:%d\n", 33659a6d4860SXiubo Li propname, (2 * i) + 1, ret); 33669a6d4860SXiubo Li return -EINVAL; 33679a6d4860SXiubo Li } 33689a6d4860SXiubo Li 33699a6d4860SXiubo Li widgets[i].name = wname; 33709a6d4860SXiubo Li } 33719a6d4860SXiubo Li 3372f23e860eSNicolin Chen card->of_dapm_widgets = widgets; 3373f23e860eSNicolin Chen card->num_of_dapm_widgets = num_widgets; 33749a6d4860SXiubo Li 33759a6d4860SXiubo Li return 0; 33769a6d4860SXiubo Li } 337721efde50SKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_simple_widgets); 33789a6d4860SXiubo Li 3379cbdfab3bSJerome Brunet int snd_soc_of_get_slot_mask(struct device_node *np, 33806131084aSJyri Sarha const char *prop_name, 33816131084aSJyri Sarha unsigned int *mask) 33826131084aSJyri Sarha { 33836131084aSJyri Sarha u32 val; 33846c84e591SJyri Sarha const __be32 *of_slot_mask = of_get_property(np, prop_name, &val); 33856131084aSJyri Sarha int i; 33866131084aSJyri Sarha 33876131084aSJyri Sarha if (!of_slot_mask) 33886131084aSJyri Sarha return 0; 33896131084aSJyri Sarha val /= sizeof(u32); 33906131084aSJyri Sarha for (i = 0; i < val; i++) 33916131084aSJyri Sarha if (be32_to_cpup(&of_slot_mask[i])) 33926131084aSJyri Sarha *mask |= (1 << i); 33936131084aSJyri Sarha 33946131084aSJyri Sarha return val; 33956131084aSJyri Sarha } 3396cbdfab3bSJerome Brunet EXPORT_SYMBOL_GPL(snd_soc_of_get_slot_mask); 33976131084aSJyri Sarha 339889c67857SXiubo Li int snd_soc_of_parse_tdm_slot(struct device_node *np, 33996131084aSJyri Sarha unsigned int *tx_mask, 34006131084aSJyri Sarha unsigned int *rx_mask, 340189c67857SXiubo Li unsigned int *slots, 340289c67857SXiubo Li unsigned int *slot_width) 340389c67857SXiubo Li { 340489c67857SXiubo Li u32 val; 340589c67857SXiubo Li int ret; 340689c67857SXiubo Li 34076131084aSJyri Sarha if (tx_mask) 34086131084aSJyri Sarha snd_soc_of_get_slot_mask(np, "dai-tdm-slot-tx-mask", tx_mask); 34096131084aSJyri Sarha if (rx_mask) 34106131084aSJyri Sarha snd_soc_of_get_slot_mask(np, "dai-tdm-slot-rx-mask", rx_mask); 34116131084aSJyri Sarha 341289c67857SXiubo Li if (of_property_read_bool(np, "dai-tdm-slot-num")) { 341389c67857SXiubo Li ret = of_property_read_u32(np, "dai-tdm-slot-num", &val); 341489c67857SXiubo Li if (ret) 341589c67857SXiubo Li return ret; 341689c67857SXiubo Li 341789c67857SXiubo Li if (slots) 341889c67857SXiubo Li *slots = val; 341989c67857SXiubo Li } 342089c67857SXiubo Li 342189c67857SXiubo Li if (of_property_read_bool(np, "dai-tdm-slot-width")) { 342289c67857SXiubo Li ret = of_property_read_u32(np, "dai-tdm-slot-width", &val); 342389c67857SXiubo Li if (ret) 342489c67857SXiubo Li return ret; 342589c67857SXiubo Li 342689c67857SXiubo Li if (slot_width) 342789c67857SXiubo Li *slot_width = val; 342889c67857SXiubo Li } 342989c67857SXiubo Li 343089c67857SXiubo Li return 0; 343189c67857SXiubo Li } 343289c67857SXiubo Li EXPORT_SYMBOL_GPL(snd_soc_of_parse_tdm_slot); 343389c67857SXiubo Li 3434440a3006SKuninori Morimoto void snd_soc_of_parse_audio_prefix(struct snd_soc_card *card, 34355e3cdaa2SKuninori Morimoto struct snd_soc_codec_conf *codec_conf, 34365e3cdaa2SKuninori Morimoto struct device_node *of_node, 34375e3cdaa2SKuninori Morimoto const char *propname) 34385e3cdaa2SKuninori Morimoto { 3439440a3006SKuninori Morimoto struct device_node *np = card->dev->of_node; 34405e3cdaa2SKuninori Morimoto const char *str; 34415e3cdaa2SKuninori Morimoto int ret; 34425e3cdaa2SKuninori Morimoto 34435e3cdaa2SKuninori Morimoto ret = of_property_read_string(np, propname, &str); 34445e3cdaa2SKuninori Morimoto if (ret < 0) { 34455e3cdaa2SKuninori Morimoto /* no prefix is not error */ 34465e3cdaa2SKuninori Morimoto return; 34475e3cdaa2SKuninori Morimoto } 34485e3cdaa2SKuninori Morimoto 34495e3cdaa2SKuninori Morimoto codec_conf->of_node = of_node; 34505e3cdaa2SKuninori Morimoto codec_conf->name_prefix = str; 34515e3cdaa2SKuninori Morimoto } 3452440a3006SKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_prefix); 34535e3cdaa2SKuninori Morimoto 34542bc644afSKuninori Morimoto int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, 3455a4a54dd5SStephen Warren const char *propname) 3456a4a54dd5SStephen Warren { 34572bc644afSKuninori Morimoto struct device_node *np = card->dev->of_node; 3458e3b1e6a1SMark Brown int num_routes; 3459a4a54dd5SStephen Warren struct snd_soc_dapm_route *routes; 3460a4a54dd5SStephen Warren int i, ret; 3461a4a54dd5SStephen Warren 3462a4a54dd5SStephen Warren num_routes = of_property_count_strings(np, propname); 3463c34ce320SRichard Zhao if (num_routes < 0 || num_routes & 1) { 346410e8aa9aSMichał Mirosław dev_err(card->dev, 346510e8aa9aSMichał Mirosław "ASoC: Property '%s' does not exist or its length is not even\n", 346610e8aa9aSMichał Mirosław propname); 3467a4a54dd5SStephen Warren return -EINVAL; 3468a4a54dd5SStephen Warren } 3469a4a54dd5SStephen Warren num_routes /= 2; 3470a4a54dd5SStephen Warren if (!num_routes) { 3471f110bfc7SLiam Girdwood dev_err(card->dev, "ASoC: Property '%s's length is zero\n", 3472a4a54dd5SStephen Warren propname); 3473a4a54dd5SStephen Warren return -EINVAL; 3474a4a54dd5SStephen Warren } 3475a4a54dd5SStephen Warren 3476a86854d0SKees Cook routes = devm_kcalloc(card->dev, num_routes, sizeof(*routes), 3477a4a54dd5SStephen Warren GFP_KERNEL); 3478a4a54dd5SStephen Warren if (!routes) { 3479a4a54dd5SStephen Warren dev_err(card->dev, 3480f110bfc7SLiam Girdwood "ASoC: Could not allocate DAPM route table\n"); 3481a4a54dd5SStephen Warren return -EINVAL; 3482a4a54dd5SStephen Warren } 3483a4a54dd5SStephen Warren 3484a4a54dd5SStephen Warren for (i = 0; i < num_routes; i++) { 3485a4a54dd5SStephen Warren ret = of_property_read_string_index(np, propname, 3486e3b1e6a1SMark Brown 2 * i, &routes[i].sink); 3487a4a54dd5SStephen Warren if (ret) { 3488c871bd0bSMark Brown dev_err(card->dev, 3489c871bd0bSMark Brown "ASoC: Property '%s' index %d could not be read: %d\n", 3490c871bd0bSMark Brown propname, 2 * i, ret); 3491a4a54dd5SStephen Warren return -EINVAL; 3492a4a54dd5SStephen Warren } 3493a4a54dd5SStephen Warren ret = of_property_read_string_index(np, propname, 3494e3b1e6a1SMark Brown (2 * i) + 1, &routes[i].source); 3495a4a54dd5SStephen Warren if (ret) { 3496a4a54dd5SStephen Warren dev_err(card->dev, 3497c871bd0bSMark Brown "ASoC: Property '%s' index %d could not be read: %d\n", 3498c871bd0bSMark Brown propname, (2 * i) + 1, ret); 3499a4a54dd5SStephen Warren return -EINVAL; 3500a4a54dd5SStephen Warren } 3501a4a54dd5SStephen Warren } 3502a4a54dd5SStephen Warren 3503f23e860eSNicolin Chen card->num_of_dapm_routes = num_routes; 3504f23e860eSNicolin Chen card->of_dapm_routes = routes; 3505a4a54dd5SStephen Warren 3506a4a54dd5SStephen Warren return 0; 3507a4a54dd5SStephen Warren } 35082bc644afSKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_routing); 3509a4a54dd5SStephen Warren 3510a7930ed4SKuninori Morimoto unsigned int snd_soc_of_parse_daifmt(struct device_node *np, 3511389cb834SJyri Sarha const char *prefix, 3512389cb834SJyri Sarha struct device_node **bitclkmaster, 3513389cb834SJyri Sarha struct device_node **framemaster) 3514a7930ed4SKuninori Morimoto { 3515a7930ed4SKuninori Morimoto int ret, i; 3516a7930ed4SKuninori Morimoto char prop[128]; 3517a7930ed4SKuninori Morimoto unsigned int format = 0; 3518a7930ed4SKuninori Morimoto int bit, frame; 3519a7930ed4SKuninori Morimoto const char *str; 3520a7930ed4SKuninori Morimoto struct { 3521a7930ed4SKuninori Morimoto char *name; 3522a7930ed4SKuninori Morimoto unsigned int val; 3523a7930ed4SKuninori Morimoto } of_fmt_table[] = { 3524a7930ed4SKuninori Morimoto { "i2s", SND_SOC_DAIFMT_I2S }, 3525a7930ed4SKuninori Morimoto { "right_j", SND_SOC_DAIFMT_RIGHT_J }, 3526a7930ed4SKuninori Morimoto { "left_j", SND_SOC_DAIFMT_LEFT_J }, 3527a7930ed4SKuninori Morimoto { "dsp_a", SND_SOC_DAIFMT_DSP_A }, 3528a7930ed4SKuninori Morimoto { "dsp_b", SND_SOC_DAIFMT_DSP_B }, 3529a7930ed4SKuninori Morimoto { "ac97", SND_SOC_DAIFMT_AC97 }, 3530a7930ed4SKuninori Morimoto { "pdm", SND_SOC_DAIFMT_PDM}, 3531a7930ed4SKuninori Morimoto { "msb", SND_SOC_DAIFMT_MSB }, 3532a7930ed4SKuninori Morimoto { "lsb", SND_SOC_DAIFMT_LSB }, 3533a7930ed4SKuninori Morimoto }; 3534a7930ed4SKuninori Morimoto 3535a7930ed4SKuninori Morimoto if (!prefix) 3536a7930ed4SKuninori Morimoto prefix = ""; 3537a7930ed4SKuninori Morimoto 3538a7930ed4SKuninori Morimoto /* 35395711c979SKuninori Morimoto * check "dai-format = xxx" 35405711c979SKuninori Morimoto * or "[prefix]format = xxx" 3541a7930ed4SKuninori Morimoto * SND_SOC_DAIFMT_FORMAT_MASK area 3542a7930ed4SKuninori Morimoto */ 35435711c979SKuninori Morimoto ret = of_property_read_string(np, "dai-format", &str); 35445711c979SKuninori Morimoto if (ret < 0) { 3545a7930ed4SKuninori Morimoto snprintf(prop, sizeof(prop), "%sformat", prefix); 3546a7930ed4SKuninori Morimoto ret = of_property_read_string(np, prop, &str); 35475711c979SKuninori Morimoto } 3548a7930ed4SKuninori Morimoto if (ret == 0) { 3549a7930ed4SKuninori Morimoto for (i = 0; i < ARRAY_SIZE(of_fmt_table); i++) { 3550a7930ed4SKuninori Morimoto if (strcmp(str, of_fmt_table[i].name) == 0) { 3551a7930ed4SKuninori Morimoto format |= of_fmt_table[i].val; 3552a7930ed4SKuninori Morimoto break; 3553a7930ed4SKuninori Morimoto } 3554a7930ed4SKuninori Morimoto } 3555a7930ed4SKuninori Morimoto } 3556a7930ed4SKuninori Morimoto 3557a7930ed4SKuninori Morimoto /* 35588c2d6a9fSKuninori Morimoto * check "[prefix]continuous-clock" 3559a7930ed4SKuninori Morimoto * SND_SOC_DAIFMT_CLOCK_MASK area 3560a7930ed4SKuninori Morimoto */ 35618c2d6a9fSKuninori Morimoto snprintf(prop, sizeof(prop), "%scontinuous-clock", prefix); 356251930295SJulia Lawall if (of_property_read_bool(np, prop)) 35638c2d6a9fSKuninori Morimoto format |= SND_SOC_DAIFMT_CONT; 35648c2d6a9fSKuninori Morimoto else 35658c2d6a9fSKuninori Morimoto format |= SND_SOC_DAIFMT_GATED; 3566a7930ed4SKuninori Morimoto 3567a7930ed4SKuninori Morimoto /* 3568a7930ed4SKuninori Morimoto * check "[prefix]bitclock-inversion" 3569a7930ed4SKuninori Morimoto * check "[prefix]frame-inversion" 3570a7930ed4SKuninori Morimoto * SND_SOC_DAIFMT_INV_MASK area 3571a7930ed4SKuninori Morimoto */ 3572a7930ed4SKuninori Morimoto snprintf(prop, sizeof(prop), "%sbitclock-inversion", prefix); 3573a7930ed4SKuninori Morimoto bit = !!of_get_property(np, prop, NULL); 3574a7930ed4SKuninori Morimoto 3575a7930ed4SKuninori Morimoto snprintf(prop, sizeof(prop), "%sframe-inversion", prefix); 3576a7930ed4SKuninori Morimoto frame = !!of_get_property(np, prop, NULL); 3577a7930ed4SKuninori Morimoto 3578a7930ed4SKuninori Morimoto switch ((bit << 4) + frame) { 3579a7930ed4SKuninori Morimoto case 0x11: 3580a7930ed4SKuninori Morimoto format |= SND_SOC_DAIFMT_IB_IF; 3581a7930ed4SKuninori Morimoto break; 3582a7930ed4SKuninori Morimoto case 0x10: 3583a7930ed4SKuninori Morimoto format |= SND_SOC_DAIFMT_IB_NF; 3584a7930ed4SKuninori Morimoto break; 3585a7930ed4SKuninori Morimoto case 0x01: 3586a7930ed4SKuninori Morimoto format |= SND_SOC_DAIFMT_NB_IF; 3587a7930ed4SKuninori Morimoto break; 3588a7930ed4SKuninori Morimoto default: 3589a7930ed4SKuninori Morimoto /* SND_SOC_DAIFMT_NB_NF is default */ 3590a7930ed4SKuninori Morimoto break; 3591a7930ed4SKuninori Morimoto } 3592a7930ed4SKuninori Morimoto 3593a7930ed4SKuninori Morimoto /* 3594a7930ed4SKuninori Morimoto * check "[prefix]bitclock-master" 3595a7930ed4SKuninori Morimoto * check "[prefix]frame-master" 3596a7930ed4SKuninori Morimoto * SND_SOC_DAIFMT_MASTER_MASK area 3597a7930ed4SKuninori Morimoto */ 3598a7930ed4SKuninori Morimoto snprintf(prop, sizeof(prop), "%sbitclock-master", prefix); 3599a7930ed4SKuninori Morimoto bit = !!of_get_property(np, prop, NULL); 3600389cb834SJyri Sarha if (bit && bitclkmaster) 3601389cb834SJyri Sarha *bitclkmaster = of_parse_phandle(np, prop, 0); 3602a7930ed4SKuninori Morimoto 3603a7930ed4SKuninori Morimoto snprintf(prop, sizeof(prop), "%sframe-master", prefix); 3604a7930ed4SKuninori Morimoto frame = !!of_get_property(np, prop, NULL); 3605389cb834SJyri Sarha if (frame && framemaster) 3606389cb834SJyri Sarha *framemaster = of_parse_phandle(np, prop, 0); 3607a7930ed4SKuninori Morimoto 3608a7930ed4SKuninori Morimoto switch ((bit << 4) + frame) { 3609a7930ed4SKuninori Morimoto case 0x11: 3610a7930ed4SKuninori Morimoto format |= SND_SOC_DAIFMT_CBM_CFM; 3611a7930ed4SKuninori Morimoto break; 3612a7930ed4SKuninori Morimoto case 0x10: 3613a7930ed4SKuninori Morimoto format |= SND_SOC_DAIFMT_CBM_CFS; 3614a7930ed4SKuninori Morimoto break; 3615a7930ed4SKuninori Morimoto case 0x01: 3616a7930ed4SKuninori Morimoto format |= SND_SOC_DAIFMT_CBS_CFM; 3617a7930ed4SKuninori Morimoto break; 3618a7930ed4SKuninori Morimoto default: 3619a7930ed4SKuninori Morimoto format |= SND_SOC_DAIFMT_CBS_CFS; 3620a7930ed4SKuninori Morimoto break; 3621a7930ed4SKuninori Morimoto } 3622a7930ed4SKuninori Morimoto 3623a7930ed4SKuninori Morimoto return format; 3624a7930ed4SKuninori Morimoto } 3625a7930ed4SKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_of_parse_daifmt); 3626a7930ed4SKuninori Morimoto 3627a180e8b9SKuninori Morimoto int snd_soc_get_dai_id(struct device_node *ep) 3628a180e8b9SKuninori Morimoto { 3629a180e8b9SKuninori Morimoto struct snd_soc_component *pos; 3630a180e8b9SKuninori Morimoto struct device_node *node; 3631a180e8b9SKuninori Morimoto int ret; 3632a180e8b9SKuninori Morimoto 3633a180e8b9SKuninori Morimoto node = of_graph_get_port_parent(ep); 3634a180e8b9SKuninori Morimoto 3635a180e8b9SKuninori Morimoto /* 3636a180e8b9SKuninori Morimoto * For example HDMI case, HDMI has video/sound port, 3637a180e8b9SKuninori Morimoto * but ALSA SoC needs sound port number only. 3638a180e8b9SKuninori Morimoto * Thus counting HDMI DT port/endpoint doesn't work. 3639a180e8b9SKuninori Morimoto * Then, it should have .of_xlate_dai_id 3640a180e8b9SKuninori Morimoto */ 3641a180e8b9SKuninori Morimoto ret = -ENOTSUPP; 3642a180e8b9SKuninori Morimoto mutex_lock(&client_mutex); 3643a180e8b9SKuninori Morimoto list_for_each_entry(pos, &component_list, list) { 3644a180e8b9SKuninori Morimoto struct device_node *component_of_node = pos->dev->of_node; 3645a180e8b9SKuninori Morimoto 3646a180e8b9SKuninori Morimoto if (!component_of_node && pos->dev->parent) 3647a180e8b9SKuninori Morimoto component_of_node = pos->dev->parent->of_node; 3648a180e8b9SKuninori Morimoto 3649a180e8b9SKuninori Morimoto if (component_of_node != node) 3650a180e8b9SKuninori Morimoto continue; 3651a180e8b9SKuninori Morimoto 3652a180e8b9SKuninori Morimoto if (pos->driver->of_xlate_dai_id) 3653a180e8b9SKuninori Morimoto ret = pos->driver->of_xlate_dai_id(pos, ep); 3654a180e8b9SKuninori Morimoto 3655a180e8b9SKuninori Morimoto break; 3656a180e8b9SKuninori Morimoto } 3657a180e8b9SKuninori Morimoto mutex_unlock(&client_mutex); 3658a180e8b9SKuninori Morimoto 3659c0a480d1STony Lindgren of_node_put(node); 3660c0a480d1STony Lindgren 3661a180e8b9SKuninori Morimoto return ret; 3662a180e8b9SKuninori Morimoto } 3663a180e8b9SKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_get_dai_id); 3664a180e8b9SKuninori Morimoto 36651ad8ec53SKuninori Morimoto int snd_soc_get_dai_name(struct of_phandle_args *args, 3666cb470087SKuninori Morimoto const char **dai_name) 3667cb470087SKuninori Morimoto { 3668cb470087SKuninori Morimoto struct snd_soc_component *pos; 36693e0aa8d8SJyri Sarha struct device_node *component_of_node; 367093b0f3eeSJean-Francois Moine int ret = -EPROBE_DEFER; 3671cb470087SKuninori Morimoto 3672cb470087SKuninori Morimoto mutex_lock(&client_mutex); 3673cb470087SKuninori Morimoto list_for_each_entry(pos, &component_list, list) { 36743e0aa8d8SJyri Sarha component_of_node = pos->dev->of_node; 36753e0aa8d8SJyri Sarha if (!component_of_node && pos->dev->parent) 36763e0aa8d8SJyri Sarha component_of_node = pos->dev->parent->of_node; 36773e0aa8d8SJyri Sarha 36783e0aa8d8SJyri Sarha if (component_of_node != args->np) 3679cb470087SKuninori Morimoto continue; 3680cb470087SKuninori Morimoto 36816833c452SKuninori Morimoto if (pos->driver->of_xlate_dai_name) { 368293b0f3eeSJean-Francois Moine ret = pos->driver->of_xlate_dai_name(pos, 368393b0f3eeSJean-Francois Moine args, 368493b0f3eeSJean-Francois Moine dai_name); 36856833c452SKuninori Morimoto } else { 368658bf4179SKuninori Morimoto struct snd_soc_dai *dai; 36876833c452SKuninori Morimoto int id = -1; 36886833c452SKuninori Morimoto 368993b0f3eeSJean-Francois Moine switch (args->args_count) { 36906833c452SKuninori Morimoto case 0: 36916833c452SKuninori Morimoto id = 0; /* same as dai_drv[0] */ 36926833c452SKuninori Morimoto break; 36936833c452SKuninori Morimoto case 1: 369493b0f3eeSJean-Francois Moine id = args->args[0]; 36956833c452SKuninori Morimoto break; 36966833c452SKuninori Morimoto default: 36976833c452SKuninori Morimoto /* not supported */ 3698cb470087SKuninori Morimoto break; 3699cb470087SKuninori Morimoto } 3700cb470087SKuninori Morimoto 37016833c452SKuninori Morimoto if (id < 0 || id >= pos->num_dai) { 37026833c452SKuninori Morimoto ret = -EINVAL; 37033dcba280SNicolin Chen continue; 37046833c452SKuninori Morimoto } 3705e41975edSXiubo Li 3706e41975edSXiubo Li ret = 0; 3707e41975edSXiubo Li 370858bf4179SKuninori Morimoto /* find target DAI */ 370958bf4179SKuninori Morimoto list_for_each_entry(dai, &pos->dai_list, list) { 371058bf4179SKuninori Morimoto if (id == 0) 371158bf4179SKuninori Morimoto break; 371258bf4179SKuninori Morimoto id--; 371358bf4179SKuninori Morimoto } 371458bf4179SKuninori Morimoto 371558bf4179SKuninori Morimoto *dai_name = dai->driver->name; 3716e41975edSXiubo Li if (!*dai_name) 3717e41975edSXiubo Li *dai_name = pos->name; 37186833c452SKuninori Morimoto } 37196833c452SKuninori Morimoto 3720cb470087SKuninori Morimoto break; 3721cb470087SKuninori Morimoto } 3722cb470087SKuninori Morimoto mutex_unlock(&client_mutex); 372393b0f3eeSJean-Francois Moine return ret; 372493b0f3eeSJean-Francois Moine } 37251ad8ec53SKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_get_dai_name); 372693b0f3eeSJean-Francois Moine 372793b0f3eeSJean-Francois Moine int snd_soc_of_get_dai_name(struct device_node *of_node, 372893b0f3eeSJean-Francois Moine const char **dai_name) 372993b0f3eeSJean-Francois Moine { 373093b0f3eeSJean-Francois Moine struct of_phandle_args args; 373193b0f3eeSJean-Francois Moine int ret; 373293b0f3eeSJean-Francois Moine 373393b0f3eeSJean-Francois Moine ret = of_parse_phandle_with_args(of_node, "sound-dai", 373493b0f3eeSJean-Francois Moine "#sound-dai-cells", 0, &args); 373593b0f3eeSJean-Francois Moine if (ret) 373693b0f3eeSJean-Francois Moine return ret; 373793b0f3eeSJean-Francois Moine 373893b0f3eeSJean-Francois Moine ret = snd_soc_get_dai_name(&args, dai_name); 3739cb470087SKuninori Morimoto 3740cb470087SKuninori Morimoto of_node_put(args.np); 3741cb470087SKuninori Morimoto 3742cb470087SKuninori Morimoto return ret; 3743cb470087SKuninori Morimoto } 3744cb470087SKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_name); 3745cb470087SKuninori Morimoto 374693b0f3eeSJean-Francois Moine /* 374794685763SSylwester Nawrocki * snd_soc_of_put_dai_link_codecs - Dereference device nodes in the codecs array 374894685763SSylwester Nawrocki * @dai_link: DAI link 374994685763SSylwester Nawrocki * 375094685763SSylwester Nawrocki * Dereference device nodes acquired by snd_soc_of_get_dai_link_codecs(). 375194685763SSylwester Nawrocki */ 375294685763SSylwester Nawrocki void snd_soc_of_put_dai_link_codecs(struct snd_soc_dai_link *dai_link) 375394685763SSylwester Nawrocki { 37543db769f1SKuninori Morimoto struct snd_soc_dai_link_component *component; 375594685763SSylwester Nawrocki int index; 375694685763SSylwester Nawrocki 37573db769f1SKuninori Morimoto for_each_link_codecs(dai_link, index, component) { 375894685763SSylwester Nawrocki if (!component->of_node) 375994685763SSylwester Nawrocki break; 376094685763SSylwester Nawrocki of_node_put(component->of_node); 376194685763SSylwester Nawrocki component->of_node = NULL; 376294685763SSylwester Nawrocki } 376394685763SSylwester Nawrocki } 376494685763SSylwester Nawrocki EXPORT_SYMBOL_GPL(snd_soc_of_put_dai_link_codecs); 376594685763SSylwester Nawrocki 376694685763SSylwester Nawrocki /* 376793b0f3eeSJean-Francois Moine * snd_soc_of_get_dai_link_codecs - Parse a list of CODECs in the devicetree 376893b0f3eeSJean-Francois Moine * @dev: Card device 376993b0f3eeSJean-Francois Moine * @of_node: Device node 377093b0f3eeSJean-Francois Moine * @dai_link: DAI link 377193b0f3eeSJean-Francois Moine * 377293b0f3eeSJean-Francois Moine * Builds an array of CODEC DAI components from the DAI link property 377393b0f3eeSJean-Francois Moine * 'sound-dai'. 377493b0f3eeSJean-Francois Moine * The array is set in the DAI link and the number of DAIs is set accordingly. 377594685763SSylwester Nawrocki * The device nodes in the array (of_node) must be dereferenced by calling 377694685763SSylwester Nawrocki * snd_soc_of_put_dai_link_codecs() on @dai_link. 377793b0f3eeSJean-Francois Moine * 377893b0f3eeSJean-Francois Moine * Returns 0 for success 377993b0f3eeSJean-Francois Moine */ 378093b0f3eeSJean-Francois Moine int snd_soc_of_get_dai_link_codecs(struct device *dev, 378193b0f3eeSJean-Francois Moine struct device_node *of_node, 378293b0f3eeSJean-Francois Moine struct snd_soc_dai_link *dai_link) 378393b0f3eeSJean-Francois Moine { 378493b0f3eeSJean-Francois Moine struct of_phandle_args args; 378593b0f3eeSJean-Francois Moine struct snd_soc_dai_link_component *component; 378693b0f3eeSJean-Francois Moine char *name; 378793b0f3eeSJean-Francois Moine int index, num_codecs, ret; 378893b0f3eeSJean-Francois Moine 378993b0f3eeSJean-Francois Moine /* Count the number of CODECs */ 379093b0f3eeSJean-Francois Moine name = "sound-dai"; 379193b0f3eeSJean-Francois Moine num_codecs = of_count_phandle_with_args(of_node, name, 379293b0f3eeSJean-Francois Moine "#sound-dai-cells"); 379393b0f3eeSJean-Francois Moine if (num_codecs <= 0) { 379493b0f3eeSJean-Francois Moine if (num_codecs == -ENOENT) 379593b0f3eeSJean-Francois Moine dev_err(dev, "No 'sound-dai' property\n"); 379693b0f3eeSJean-Francois Moine else 379793b0f3eeSJean-Francois Moine dev_err(dev, "Bad phandle in 'sound-dai'\n"); 379893b0f3eeSJean-Francois Moine return num_codecs; 379993b0f3eeSJean-Francois Moine } 3800a86854d0SKees Cook component = devm_kcalloc(dev, 3801a86854d0SKees Cook num_codecs, sizeof(*component), 380293b0f3eeSJean-Francois Moine GFP_KERNEL); 380393b0f3eeSJean-Francois Moine if (!component) 380493b0f3eeSJean-Francois Moine return -ENOMEM; 380593b0f3eeSJean-Francois Moine dai_link->codecs = component; 380693b0f3eeSJean-Francois Moine dai_link->num_codecs = num_codecs; 380793b0f3eeSJean-Francois Moine 380893b0f3eeSJean-Francois Moine /* Parse the list */ 38093db769f1SKuninori Morimoto for_each_link_codecs(dai_link, index, component) { 381093b0f3eeSJean-Francois Moine ret = of_parse_phandle_with_args(of_node, name, 381193b0f3eeSJean-Francois Moine "#sound-dai-cells", 381293b0f3eeSJean-Francois Moine index, &args); 381393b0f3eeSJean-Francois Moine if (ret) 381493b0f3eeSJean-Francois Moine goto err; 381593b0f3eeSJean-Francois Moine component->of_node = args.np; 381693b0f3eeSJean-Francois Moine ret = snd_soc_get_dai_name(&args, &component->dai_name); 381793b0f3eeSJean-Francois Moine if (ret < 0) 381893b0f3eeSJean-Francois Moine goto err; 381993b0f3eeSJean-Francois Moine } 382093b0f3eeSJean-Francois Moine return 0; 382193b0f3eeSJean-Francois Moine err: 382294685763SSylwester Nawrocki snd_soc_of_put_dai_link_codecs(dai_link); 382393b0f3eeSJean-Francois Moine dai_link->codecs = NULL; 382493b0f3eeSJean-Francois Moine dai_link->num_codecs = 0; 382593b0f3eeSJean-Francois Moine return ret; 382693b0f3eeSJean-Francois Moine } 382793b0f3eeSJean-Francois Moine EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_link_codecs); 382893b0f3eeSJean-Francois Moine 3829c9b3a40fSTakashi Iwai static int __init snd_soc_init(void) 3830db2a4165SFrank Mandarino { 38316553bf06SLars-Peter Clausen snd_soc_debugfs_init(); 3832fb257897SMark Brown snd_soc_util_init(); 3833fb257897SMark Brown 3834db2a4165SFrank Mandarino return platform_driver_register(&soc_driver); 3835db2a4165SFrank Mandarino } 38364abe8e16SMark Brown module_init(snd_soc_init); 3837db2a4165SFrank Mandarino 38387d8c16a6SMark Brown static void __exit snd_soc_exit(void) 3839db2a4165SFrank Mandarino { 3840fb257897SMark Brown snd_soc_util_exit(); 38416553bf06SLars-Peter Clausen snd_soc_debugfs_exit(); 3842fb257897SMark Brown 3843db2a4165SFrank Mandarino platform_driver_unregister(&soc_driver); 3844db2a4165SFrank Mandarino } 3845db2a4165SFrank Mandarino module_exit(snd_soc_exit); 3846db2a4165SFrank Mandarino 3847db2a4165SFrank Mandarino /* Module information */ 3848d331124dSLiam Girdwood MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk"); 3849db2a4165SFrank Mandarino MODULE_DESCRIPTION("ALSA SoC Core"); 3850db2a4165SFrank Mandarino MODULE_LICENSE("GPL"); 3851db2a4165SFrank Mandarino MODULE_ALIAS("platform:soc-audio"); 3852