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) { 4553efab7dcSMark Brown 4561a497983SMengdong Lin if (rtd->dai_link->ignore_suspend) 4573efab7dcSMark Brown continue; 4583efab7dcSMark Brown 4591a497983SMengdong Lin for (i = 0; i < rtd->num_codecs; i++) { 4601a497983SMengdong Lin struct snd_soc_dai *dai = rtd->codec_dais[i]; 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 } 531a8093297SLars-Peter Clausen 5321547aba9SMark Brown case SND_SOC_BIAS_OFF: 533999f7f5aSKuninori Morimoto if (component->driver->suspend) 534999f7f5aSKuninori Morimoto component->driver->suspend(component); 5359178feb4SKuninori Morimoto component->suspended = 1; 5369178feb4SKuninori Morimoto if (component->regmap) 5379178feb4SKuninori Morimoto regcache_mark_dirty(component->regmap); 538988e8cc4SNicolin Chen /* deactivate pins to sleep state */ 5399178feb4SKuninori Morimoto pinctrl_pm_select_sleep_state(component->dev); 5401547aba9SMark Brown break; 5411547aba9SMark Brown default: 5429178feb4SKuninori Morimoto dev_dbg(component->dev, 5439178feb4SKuninori Morimoto "ASoC: COMPONENT is on over suspend\n"); 5441547aba9SMark Brown break; 5451547aba9SMark Brown } 5461547aba9SMark Brown } 547f0fba2adSLiam Girdwood } 548db2a4165SFrank Mandarino 5491a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 5501a497983SMengdong Lin struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 5513efab7dcSMark Brown 5521a497983SMengdong Lin if (rtd->dai_link->ignore_suspend) 5533efab7dcSMark Brown continue; 5543efab7dcSMark Brown 555bc263214SLars-Peter Clausen if (cpu_dai->driver->suspend && cpu_dai->driver->bus_control) 556f0fba2adSLiam Girdwood cpu_dai->driver->suspend(cpu_dai); 557988e8cc4SNicolin Chen 558988e8cc4SNicolin Chen /* deactivate pins to sleep state */ 559988e8cc4SNicolin Chen pinctrl_pm_select_sleep_state(cpu_dai->dev); 560db2a4165SFrank Mandarino } 561db2a4165SFrank Mandarino 56287506549SMark Brown if (card->suspend_post) 56370b2ac12SMark Brown card->suspend_post(card); 564db2a4165SFrank Mandarino 565db2a4165SFrank Mandarino return 0; 566db2a4165SFrank Mandarino } 5676f8ab4acSMark Brown EXPORT_SYMBOL_GPL(snd_soc_suspend); 568db2a4165SFrank Mandarino 5696ed25978SAndy Green /* deferred resume work, so resume can complete before we finished 5706ed25978SAndy Green * setting our codec back up, which can be very slow on I2C 5716ed25978SAndy Green */ 5726ed25978SAndy Green static void soc_resume_deferred(struct work_struct *work) 573db2a4165SFrank Mandarino { 574f0fba2adSLiam Girdwood struct snd_soc_card *card = 575f0fba2adSLiam Girdwood container_of(work, struct snd_soc_card, deferred_resume_work); 5761a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd; 577d9fc4063SKuninori Morimoto struct snd_soc_component *component; 5781a497983SMengdong Lin int i; 579db2a4165SFrank Mandarino 5806ed25978SAndy Green /* our power state is still SNDRV_CTL_POWER_D3hot from suspend time, 5816ed25978SAndy Green * so userspace apps are blocked from touching us 5826ed25978SAndy Green */ 5836ed25978SAndy Green 584f110bfc7SLiam Girdwood dev_dbg(card->dev, "ASoC: starting resume work\n"); 5856ed25978SAndy Green 5869949788bSMark Brown /* Bring us up into D2 so that DAPM starts enabling things */ 587f0fba2adSLiam Girdwood snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D2); 5889949788bSMark Brown 58987506549SMark Brown if (card->resume_pre) 59070b2ac12SMark Brown card->resume_pre(card); 591db2a4165SFrank Mandarino 592bc263214SLars-Peter Clausen /* resume control bus DAIs */ 5931a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 5941a497983SMengdong Lin struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 5953efab7dcSMark Brown 5961a497983SMengdong Lin if (rtd->dai_link->ignore_suspend) 5973efab7dcSMark Brown continue; 5983efab7dcSMark Brown 599bc263214SLars-Peter Clausen if (cpu_dai->driver->resume && cpu_dai->driver->bus_control) 600f0fba2adSLiam Girdwood cpu_dai->driver->resume(cpu_dai); 601db2a4165SFrank Mandarino } 602db2a4165SFrank Mandarino 603d9fc4063SKuninori Morimoto list_for_each_entry(component, &card->component_dev_list, card_list) { 6049178feb4SKuninori Morimoto if (component->suspended) { 605999f7f5aSKuninori Morimoto if (component->driver->resume) 606999f7f5aSKuninori Morimoto component->driver->resume(component); 6079178feb4SKuninori Morimoto component->suspended = 0; 6081547aba9SMark Brown } 609f0fba2adSLiam Girdwood } 610db2a4165SFrank Mandarino 6111a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 6123efab7dcSMark Brown 6131a497983SMengdong Lin if (rtd->dai_link->ignore_suspend) 6143efab7dcSMark Brown continue; 6153efab7dcSMark Brown 6161a497983SMengdong Lin snd_soc_dapm_stream_event(rtd, 617d9b0951bSLiam Girdwood SNDRV_PCM_STREAM_PLAYBACK, 618db2a4165SFrank Mandarino SND_SOC_DAPM_STREAM_RESUME); 619f0fba2adSLiam Girdwood 6201a497983SMengdong Lin snd_soc_dapm_stream_event(rtd, 621d9b0951bSLiam Girdwood SNDRV_PCM_STREAM_CAPTURE, 622db2a4165SFrank Mandarino SND_SOC_DAPM_STREAM_RESUME); 623db2a4165SFrank Mandarino } 624db2a4165SFrank Mandarino 6253ff3f64bSMark Brown /* unmute any active DACs */ 6261a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 6273efab7dcSMark Brown 6281a497983SMengdong Lin if (rtd->dai_link->ignore_suspend) 6293efab7dcSMark Brown continue; 6303efab7dcSMark Brown 6311a497983SMengdong Lin for (i = 0; i < rtd->num_codecs; i++) { 6321a497983SMengdong Lin struct snd_soc_dai *dai = rtd->codec_dais[i]; 63388bd870fSBenoit Cousson struct snd_soc_dai_driver *drv = dai->driver; 63488bd870fSBenoit Cousson 635f0fba2adSLiam Girdwood if (drv->ops->digital_mute && dai->playback_active) 636f0fba2adSLiam Girdwood drv->ops->digital_mute(dai, 0); 637db2a4165SFrank Mandarino } 63888bd870fSBenoit Cousson } 639db2a4165SFrank Mandarino 6401a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 6411a497983SMengdong Lin struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 6423efab7dcSMark Brown 6431a497983SMengdong Lin if (rtd->dai_link->ignore_suspend) 6443efab7dcSMark Brown continue; 6453efab7dcSMark Brown 646bc263214SLars-Peter Clausen if (cpu_dai->driver->resume && !cpu_dai->driver->bus_control) 647f0fba2adSLiam Girdwood cpu_dai->driver->resume(cpu_dai); 648db2a4165SFrank Mandarino } 649db2a4165SFrank Mandarino 65087506549SMark Brown if (card->resume_post) 65170b2ac12SMark Brown card->resume_post(card); 652db2a4165SFrank Mandarino 653f110bfc7SLiam Girdwood dev_dbg(card->dev, "ASoC: resume work completed\n"); 6546ed25978SAndy Green 6558be4da29SLars-Peter Clausen /* Recheck all endpoints too, their state is affected by suspend */ 6568be4da29SLars-Peter Clausen dapm_mark_endpoints_dirty(card); 657e2d32ff6SMark Brown snd_soc_dapm_sync(&card->dapm); 6581a7aaa58SJeeja KP 6591a7aaa58SJeeja KP /* userspace can access us now we are back as we were before */ 6601a7aaa58SJeeja KP snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D0); 6616ed25978SAndy Green } 6626ed25978SAndy Green 6636ed25978SAndy Green /* powers up audio subsystem after a suspend */ 6646f8ab4acSMark Brown int snd_soc_resume(struct device *dev) 6656ed25978SAndy Green { 6666f8ab4acSMark Brown struct snd_soc_card *card = dev_get_drvdata(dev); 667bc263214SLars-Peter Clausen bool bus_control = false; 6681a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd; 669b9dd94a8SPeter Ujfalusi 670c5599b87SLars-Peter Clausen /* If the card is not initialized yet there is nothing to do */ 671c5599b87SLars-Peter Clausen if (!card->instantiated) 6725ff1ddf2SEric Miao return 0; 6735ff1ddf2SEric Miao 674988e8cc4SNicolin Chen /* activate pins from sleep state */ 6751a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 67688bd870fSBenoit Cousson struct snd_soc_dai **codec_dais = rtd->codec_dais; 67788bd870fSBenoit Cousson struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 67888bd870fSBenoit Cousson int j; 67988bd870fSBenoit Cousson 680988e8cc4SNicolin Chen if (cpu_dai->active) 681988e8cc4SNicolin Chen pinctrl_pm_select_default_state(cpu_dai->dev); 68288bd870fSBenoit Cousson 68388bd870fSBenoit Cousson for (j = 0; j < rtd->num_codecs; j++) { 68488bd870fSBenoit Cousson struct snd_soc_dai *codec_dai = codec_dais[j]; 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 850*a655de80SLiam Girdwood if (dai_link->ignore) 851*a655de80SLiam Girdwood return 0; 852*a655de80SLiam 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 */ 8791a497983SMengdong Lin codec_dais = rtd->codec_dais; 88088bd870fSBenoit Cousson for (i = 0; i < rtd->num_codecs; i++) { 88114621c7eSLars-Peter Clausen codec_dais[i] = snd_soc_find_dai(&codecs[i]); 88288bd870fSBenoit Cousson if (!codec_dais[i]) { 88312023a9aSMisael Lopez Cruz dev_err(card->dev, "ASoC: CODEC DAI %s not registered\n", 88488bd870fSBenoit Cousson codecs[i].dai_name); 8851a497983SMengdong Lin goto _err_defer; 88612023a9aSMisael Lopez Cruz } 88790be711eSKuninori Morimoto snd_soc_rtdcom_add(rtd, codec_dais[i]->component); 88888bd870fSBenoit Cousson } 88988bd870fSBenoit Cousson 89088bd870fSBenoit Cousson /* Single codec links expect codec and codec_dai in runtime data */ 89188bd870fSBenoit Cousson rtd->codec_dai = codec_dais[0]; 89212023a9aSMisael Lopez Cruz 893848dd8beSMark Brown /* if there's no platform we match on the empty platform */ 894848dd8beSMark Brown platform_name = dai_link->platform_name; 8955a504963SStephen Warren if (!platform_name && !dai_link->platform_of_node) 896848dd8beSMark Brown platform_name = "snd-soc-dummy"; 897848dd8beSMark Brown 898b19e6e7bSMark Brown /* find one from the set of registered platforms */ 89990be711eSKuninori Morimoto list_for_each_entry(component, &component_list, list) { 90090be711eSKuninori Morimoto platform_of_node = component->dev->of_node; 90190be711eSKuninori Morimoto if (!platform_of_node && component->dev->parent->of_node) 90290be711eSKuninori Morimoto platform_of_node = component->dev->parent->of_node; 90390be711eSKuninori Morimoto 90490be711eSKuninori Morimoto if (dai_link->platform_of_node) { 90590be711eSKuninori Morimoto if (platform_of_node != dai_link->platform_of_node) 90690be711eSKuninori Morimoto continue; 90790be711eSKuninori Morimoto } else { 90890be711eSKuninori Morimoto if (strcmp(component->name, platform_name)) 90990be711eSKuninori Morimoto continue; 91090be711eSKuninori Morimoto } 91190be711eSKuninori Morimoto 91290be711eSKuninori Morimoto snd_soc_rtdcom_add(rtd, component); 91390be711eSKuninori Morimoto } 91490be711eSKuninori Morimoto 9151a497983SMengdong Lin soc_add_pcm_runtime(card, rtd); 916b19e6e7bSMark Brown return 0; 9171a497983SMengdong Lin 9181a497983SMengdong Lin _err_defer: 9191a497983SMengdong Lin soc_free_pcm_runtime(rtd); 9201a497983SMengdong Lin return -EPROBE_DEFER; 9216b05eda6SMark Brown } 9226b05eda6SMark Brown 923f1d45cc3SLars-Peter Clausen static void soc_remove_component(struct snd_soc_component *component) 924d12cd198SStephen Warren { 925abd31b32SLars-Peter Clausen if (!component->card) 92670090bbbSLars-Peter Clausen return; 927d12cd198SStephen Warren 928d9fc4063SKuninori Morimoto list_del(&component->card_list); 929d12cd198SStephen Warren 930999f7f5aSKuninori Morimoto if (component->driver->remove) 931999f7f5aSKuninori Morimoto component->driver->remove(component); 932d12cd198SStephen Warren 933f1d45cc3SLars-Peter Clausen snd_soc_dapm_free(snd_soc_component_get_dapm(component)); 934d12cd198SStephen Warren 935f1d45cc3SLars-Peter Clausen soc_cleanup_component_debugfs(component); 936abd31b32SLars-Peter Clausen component->card = NULL; 937f1d45cc3SLars-Peter Clausen module_put(component->dev->driver->owner); 938d12cd198SStephen Warren } 939d12cd198SStephen Warren 940e60cd14fSLars-Peter Clausen static void soc_remove_dai(struct snd_soc_dai *dai, int order) 941589c3563SJarkko Nikula { 942589c3563SJarkko Nikula int err; 943589c3563SJarkko Nikula 944e60cd14fSLars-Peter Clausen if (dai && dai->probed && 945e60cd14fSLars-Peter Clausen dai->driver->remove_order == order) { 946e60cd14fSLars-Peter Clausen if (dai->driver->remove) { 947e60cd14fSLars-Peter Clausen err = dai->driver->remove(dai); 948589c3563SJarkko Nikula if (err < 0) 949e60cd14fSLars-Peter Clausen dev_err(dai->dev, 950b0aa88afSMisael Lopez Cruz "ASoC: failed to remove %s: %d\n", 951e60cd14fSLars-Peter Clausen dai->name, err); 952b0aa88afSMisael Lopez Cruz } 953e60cd14fSLars-Peter Clausen dai->probed = 0; 954b0aa88afSMisael Lopez Cruz } 955b0aa88afSMisael Lopez Cruz } 956b0aa88afSMisael Lopez Cruz 9571a497983SMengdong Lin static void soc_remove_link_dais(struct snd_soc_card *card, 9581a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd, int order) 959f0fba2adSLiam Girdwood { 960e60cd14fSLars-Peter Clausen int i; 961f0fba2adSLiam Girdwood 962f0fba2adSLiam Girdwood /* unregister the rtd device */ 963f0fba2adSLiam Girdwood if (rtd->dev_registered) { 96436ae1a96SMark Brown device_unregister(rtd->dev); 965f0fba2adSLiam Girdwood rtd->dev_registered = 0; 96602a06d30SBarry Song } 96702a06d30SBarry Song 968f0fba2adSLiam Girdwood /* remove the CODEC DAI */ 96988bd870fSBenoit Cousson for (i = 0; i < rtd->num_codecs; i++) 970e60cd14fSLars-Peter Clausen soc_remove_dai(rtd->codec_dais[i], order); 971f0fba2adSLiam Girdwood 972e60cd14fSLars-Peter Clausen soc_remove_dai(rtd->cpu_dai, order); 973f0fba2adSLiam Girdwood } 974f0fba2adSLiam Girdwood 9751a497983SMengdong Lin static void soc_remove_link_components(struct snd_soc_card *card, 9761a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd, int order) 97762ae68faSStephen Warren { 97861aca564SLars-Peter Clausen struct snd_soc_component *component; 97990be711eSKuninori Morimoto struct snd_soc_rtdcom_list *rtdcom; 98062ae68faSStephen Warren 98190be711eSKuninori Morimoto for_each_rtdcom(rtd, rtdcom) { 98290be711eSKuninori Morimoto component = rtdcom->component; 98362ae68faSStephen Warren 98470090bbbSLars-Peter Clausen if (component->driver->remove_order == order) 98561aca564SLars-Peter Clausen soc_remove_component(component); 98662ae68faSStephen Warren } 98762ae68faSStephen Warren } 98862ae68faSStephen Warren 9890671fd8eSKuninori Morimoto static void soc_remove_dai_links(struct snd_soc_card *card) 9900671fd8eSKuninori Morimoto { 9911a497983SMengdong Lin int order; 9921a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd; 993f8f80361SMengdong Lin struct snd_soc_dai_link *link, *_link; 9940671fd8eSKuninori Morimoto 9950168bf0dSLiam Girdwood for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; 9960168bf0dSLiam Girdwood order++) { 9971a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) 9981a497983SMengdong Lin soc_remove_link_dais(card, rtd, order); 9990168bf0dSLiam Girdwood } 100062ae68faSStephen Warren 100162ae68faSStephen Warren for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; 100262ae68faSStephen Warren order++) { 10031a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) 10041a497983SMengdong Lin soc_remove_link_components(card, rtd, order); 100562ae68faSStephen Warren } 100662ae68faSStephen Warren 1007f8f80361SMengdong Lin list_for_each_entry_safe(link, _link, &card->dai_link_list, list) { 1008f8f80361SMengdong Lin if (link->dobj.type == SND_SOC_DOBJ_DAI_LINK) 1009f8f80361SMengdong Lin dev_warn(card->dev, "Topology forgot to remove link %s?\n", 1010f8f80361SMengdong Lin link->name); 1011f8f80361SMengdong Lin 1012f8f80361SMengdong Lin list_del(&link->list); 1013f8f80361SMengdong Lin card->num_dai_links--; 10140671fd8eSKuninori Morimoto } 10150671fd8eSKuninori Morimoto } 10160671fd8eSKuninori Morimoto 1017923c5e61SMengdong Lin static int snd_soc_init_multicodec(struct snd_soc_card *card, 1018923c5e61SMengdong Lin struct snd_soc_dai_link *dai_link) 1019923c5e61SMengdong Lin { 1020923c5e61SMengdong Lin /* Legacy codec/codec_dai link is a single entry in multicodec */ 1021923c5e61SMengdong Lin if (dai_link->codec_name || dai_link->codec_of_node || 1022923c5e61SMengdong Lin dai_link->codec_dai_name) { 1023923c5e61SMengdong Lin dai_link->num_codecs = 1; 1024923c5e61SMengdong Lin 1025923c5e61SMengdong Lin dai_link->codecs = devm_kzalloc(card->dev, 1026923c5e61SMengdong Lin sizeof(struct snd_soc_dai_link_component), 1027923c5e61SMengdong Lin GFP_KERNEL); 1028923c5e61SMengdong Lin if (!dai_link->codecs) 1029923c5e61SMengdong Lin return -ENOMEM; 1030923c5e61SMengdong Lin 1031923c5e61SMengdong Lin dai_link->codecs[0].name = dai_link->codec_name; 1032923c5e61SMengdong Lin dai_link->codecs[0].of_node = dai_link->codec_of_node; 1033923c5e61SMengdong Lin dai_link->codecs[0].dai_name = dai_link->codec_dai_name; 1034923c5e61SMengdong Lin } 1035923c5e61SMengdong Lin 1036923c5e61SMengdong Lin if (!dai_link->codecs) { 1037923c5e61SMengdong Lin dev_err(card->dev, "ASoC: DAI link has no CODECs\n"); 1038923c5e61SMengdong Lin return -EINVAL; 1039923c5e61SMengdong Lin } 1040923c5e61SMengdong Lin 1041923c5e61SMengdong Lin return 0; 1042923c5e61SMengdong Lin } 1043923c5e61SMengdong Lin 1044923c5e61SMengdong Lin static int soc_init_dai_link(struct snd_soc_card *card, 1045923c5e61SMengdong Lin struct snd_soc_dai_link *link) 1046923c5e61SMengdong Lin { 1047923c5e61SMengdong Lin int i, ret; 1048923c5e61SMengdong Lin 1049923c5e61SMengdong Lin ret = snd_soc_init_multicodec(card, link); 1050923c5e61SMengdong Lin if (ret) { 1051923c5e61SMengdong Lin dev_err(card->dev, "ASoC: failed to init multicodec\n"); 1052923c5e61SMengdong Lin return ret; 1053923c5e61SMengdong Lin } 1054923c5e61SMengdong Lin 1055923c5e61SMengdong Lin for (i = 0; i < link->num_codecs; i++) { 1056923c5e61SMengdong Lin /* 1057923c5e61SMengdong Lin * Codec must be specified by 1 of name or OF node, 1058923c5e61SMengdong Lin * not both or neither. 1059923c5e61SMengdong Lin */ 1060923c5e61SMengdong Lin if (!!link->codecs[i].name == 1061923c5e61SMengdong Lin !!link->codecs[i].of_node) { 1062923c5e61SMengdong Lin dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n", 1063923c5e61SMengdong Lin link->name); 1064923c5e61SMengdong Lin return -EINVAL; 1065923c5e61SMengdong Lin } 1066923c5e61SMengdong Lin /* Codec DAI name must be specified */ 1067923c5e61SMengdong Lin if (!link->codecs[i].dai_name) { 1068923c5e61SMengdong Lin dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n", 1069923c5e61SMengdong Lin link->name); 1070923c5e61SMengdong Lin return -EINVAL; 1071923c5e61SMengdong Lin } 1072923c5e61SMengdong Lin } 1073923c5e61SMengdong Lin 1074923c5e61SMengdong Lin /* 1075923c5e61SMengdong Lin * Platform may be specified by either name or OF node, but 1076923c5e61SMengdong Lin * can be left unspecified, and a dummy platform will be used. 1077923c5e61SMengdong Lin */ 1078923c5e61SMengdong Lin if (link->platform_name && link->platform_of_node) { 1079923c5e61SMengdong Lin dev_err(card->dev, 1080923c5e61SMengdong Lin "ASoC: Both platform name/of_node are set for %s\n", 1081923c5e61SMengdong Lin link->name); 1082923c5e61SMengdong Lin return -EINVAL; 1083923c5e61SMengdong Lin } 1084923c5e61SMengdong Lin 1085923c5e61SMengdong Lin /* 1086923c5e61SMengdong Lin * CPU device may be specified by either name or OF node, but 1087923c5e61SMengdong Lin * can be left unspecified, and will be matched based on DAI 1088923c5e61SMengdong Lin * name alone.. 1089923c5e61SMengdong Lin */ 1090923c5e61SMengdong Lin if (link->cpu_name && link->cpu_of_node) { 1091923c5e61SMengdong Lin dev_err(card->dev, 1092923c5e61SMengdong Lin "ASoC: Neither/both cpu name/of_node are set for %s\n", 1093923c5e61SMengdong Lin link->name); 1094923c5e61SMengdong Lin return -EINVAL; 1095923c5e61SMengdong Lin } 1096923c5e61SMengdong Lin /* 1097923c5e61SMengdong Lin * At least one of CPU DAI name or CPU device name/node must be 1098923c5e61SMengdong Lin * specified 1099923c5e61SMengdong Lin */ 1100923c5e61SMengdong Lin if (!link->cpu_dai_name && 1101923c5e61SMengdong Lin !(link->cpu_name || link->cpu_of_node)) { 1102923c5e61SMengdong Lin dev_err(card->dev, 1103923c5e61SMengdong Lin "ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n", 1104923c5e61SMengdong Lin link->name); 1105923c5e61SMengdong Lin return -EINVAL; 1106923c5e61SMengdong Lin } 1107923c5e61SMengdong Lin 1108923c5e61SMengdong Lin return 0; 1109923c5e61SMengdong Lin } 1110923c5e61SMengdong Lin 1111ef2e8175SKuninori Morimoto void snd_soc_disconnect_sync(struct device *dev) 1112ef2e8175SKuninori Morimoto { 1113ef2e8175SKuninori Morimoto struct snd_soc_component *component = snd_soc_lookup_component(dev, NULL); 1114ef2e8175SKuninori Morimoto 1115ef2e8175SKuninori Morimoto if (!component || !component->card) 1116ef2e8175SKuninori Morimoto return; 1117ef2e8175SKuninori Morimoto 1118ef2e8175SKuninori Morimoto snd_card_disconnect_sync(component->card->snd_card); 1119ef2e8175SKuninori Morimoto } 1120df532185SKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_disconnect_sync); 1121ef2e8175SKuninori Morimoto 1122f8f80361SMengdong Lin /** 1123f8f80361SMengdong Lin * snd_soc_add_dai_link - Add a DAI link dynamically 1124f8f80361SMengdong Lin * @card: The ASoC card to which the DAI link is added 1125f8f80361SMengdong Lin * @dai_link: The new DAI link to add 1126f8f80361SMengdong Lin * 1127f8f80361SMengdong Lin * This function adds a DAI link to the ASoC card's link list. 1128f8f80361SMengdong Lin * 1129f8f80361SMengdong Lin * Note: Topology can use this API to add DAI links when probing the 1130f8f80361SMengdong Lin * topology component. And machine drivers can still define static 1131f8f80361SMengdong Lin * DAI links in dai_link array. 1132f8f80361SMengdong Lin */ 1133f8f80361SMengdong Lin int snd_soc_add_dai_link(struct snd_soc_card *card, 1134f8f80361SMengdong Lin struct snd_soc_dai_link *dai_link) 1135f8f80361SMengdong Lin { 1136f8f80361SMengdong Lin if (dai_link->dobj.type 1137f8f80361SMengdong Lin && dai_link->dobj.type != SND_SOC_DOBJ_DAI_LINK) { 1138f8f80361SMengdong Lin dev_err(card->dev, "Invalid dai link type %d\n", 1139f8f80361SMengdong Lin dai_link->dobj.type); 1140f8f80361SMengdong Lin return -EINVAL; 1141f8f80361SMengdong Lin } 1142f8f80361SMengdong Lin 1143f8f80361SMengdong Lin lockdep_assert_held(&client_mutex); 1144d6f220eaSMengdong Lin /* Notify the machine driver for extra initialization 1145d6f220eaSMengdong Lin * on the link created by topology. 1146d6f220eaSMengdong Lin */ 1147d6f220eaSMengdong Lin if (dai_link->dobj.type && card->add_dai_link) 1148d6f220eaSMengdong Lin card->add_dai_link(card, dai_link); 1149d6f220eaSMengdong Lin 1150f8f80361SMengdong Lin list_add_tail(&dai_link->list, &card->dai_link_list); 1151f8f80361SMengdong Lin card->num_dai_links++; 1152f8f80361SMengdong Lin 1153f8f80361SMengdong Lin return 0; 1154f8f80361SMengdong Lin } 1155f8f80361SMengdong Lin EXPORT_SYMBOL_GPL(snd_soc_add_dai_link); 1156f8f80361SMengdong Lin 1157f8f80361SMengdong Lin /** 1158f8f80361SMengdong Lin * snd_soc_remove_dai_link - Remove a DAI link from the list 1159f8f80361SMengdong Lin * @card: The ASoC card that owns the link 1160f8f80361SMengdong Lin * @dai_link: The DAI link to remove 1161f8f80361SMengdong Lin * 1162f8f80361SMengdong Lin * This function removes a DAI link from the ASoC card's link list. 1163f8f80361SMengdong Lin * 1164f8f80361SMengdong Lin * For DAI links previously added by topology, topology should 1165f8f80361SMengdong Lin * remove them by using the dobj embedded in the link. 1166f8f80361SMengdong Lin */ 1167f8f80361SMengdong Lin void snd_soc_remove_dai_link(struct snd_soc_card *card, 1168f8f80361SMengdong Lin struct snd_soc_dai_link *dai_link) 1169f8f80361SMengdong Lin { 1170f8f80361SMengdong Lin struct snd_soc_dai_link *link, *_link; 1171f8f80361SMengdong Lin 1172f8f80361SMengdong Lin if (dai_link->dobj.type 1173f8f80361SMengdong Lin && dai_link->dobj.type != SND_SOC_DOBJ_DAI_LINK) { 1174f8f80361SMengdong Lin dev_err(card->dev, "Invalid dai link type %d\n", 1175f8f80361SMengdong Lin dai_link->dobj.type); 1176f8f80361SMengdong Lin return; 1177f8f80361SMengdong Lin } 1178f8f80361SMengdong Lin 1179f8f80361SMengdong Lin lockdep_assert_held(&client_mutex); 1180d6f220eaSMengdong Lin /* Notify the machine driver for extra destruction 1181d6f220eaSMengdong Lin * on the link created by topology. 1182d6f220eaSMengdong Lin */ 1183d6f220eaSMengdong Lin if (dai_link->dobj.type && card->remove_dai_link) 1184d6f220eaSMengdong Lin card->remove_dai_link(card, dai_link); 1185d6f220eaSMengdong Lin 1186f8f80361SMengdong Lin list_for_each_entry_safe(link, _link, &card->dai_link_list, list) { 1187f8f80361SMengdong Lin if (link == dai_link) { 1188f8f80361SMengdong Lin list_del(&link->list); 1189f8f80361SMengdong Lin card->num_dai_links--; 1190f8f80361SMengdong Lin return; 1191f8f80361SMengdong Lin } 1192f8f80361SMengdong Lin } 1193f8f80361SMengdong Lin } 1194f8f80361SMengdong Lin EXPORT_SYMBOL_GPL(snd_soc_remove_dai_link); 1195f0fba2adSLiam Girdwood 1196ead9b919SJarkko Nikula static void soc_set_name_prefix(struct snd_soc_card *card, 119794f99c87SLars-Peter Clausen struct snd_soc_component *component) 1198ead9b919SJarkko Nikula { 1199ead9b919SJarkko Nikula int i; 1200ead9b919SJarkko Nikula 1201ff819b83SDimitris Papastamos if (card->codec_conf == NULL) 1202ead9b919SJarkko Nikula return; 1203ead9b919SJarkko Nikula 1204ff819b83SDimitris Papastamos for (i = 0; i < card->num_configs; i++) { 1205ff819b83SDimitris Papastamos struct snd_soc_codec_conf *map = &card->codec_conf[i]; 1206b24c539bSCharles Keepax struct device_node *component_of_node = component->dev->of_node; 1207b24c539bSCharles Keepax 1208b24c539bSCharles Keepax if (!component_of_node && component->dev->parent) 1209b24c539bSCharles Keepax component_of_node = component->dev->parent->of_node; 1210b24c539bSCharles Keepax 1211b24c539bSCharles Keepax if (map->of_node && component_of_node != map->of_node) 12123ca041edSSebastian Reichel continue; 121394f99c87SLars-Peter Clausen if (map->dev_name && strcmp(component->name, map->dev_name)) 12143ca041edSSebastian Reichel continue; 121594f99c87SLars-Peter Clausen component->name_prefix = map->name_prefix; 1216ead9b919SJarkko Nikula break; 1217ead9b919SJarkko Nikula } 1218ead9b919SJarkko Nikula } 1219ead9b919SJarkko Nikula 1220f1d45cc3SLars-Peter Clausen static int soc_probe_component(struct snd_soc_card *card, 1221f1d45cc3SLars-Peter Clausen struct snd_soc_component *component) 1222589c3563SJarkko Nikula { 1223f1d45cc3SLars-Peter Clausen struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); 1224888df395SMark Brown struct snd_soc_dai *dai; 1225f1d45cc3SLars-Peter Clausen int ret; 1226589c3563SJarkko Nikula 12271b7c1231SLars-Peter Clausen if (!strcmp(component->name, "snd-soc-dummy")) 122870090bbbSLars-Peter Clausen return 0; 1229589c3563SJarkko Nikula 1230abd31b32SLars-Peter Clausen if (component->card) { 12311b7c1231SLars-Peter Clausen if (component->card != card) { 12321b7c1231SLars-Peter Clausen dev_err(component->dev, 12331b7c1231SLars-Peter Clausen "Trying to bind component to card \"%s\" but is already bound to card \"%s\"\n", 12341b7c1231SLars-Peter Clausen card->name, component->card->name); 12351b7c1231SLars-Peter Clausen return -ENODEV; 12361b7c1231SLars-Peter Clausen } 12371b7c1231SLars-Peter Clausen return 0; 12381b7c1231SLars-Peter Clausen } 12391b7c1231SLars-Peter Clausen 1240abd31b32SLars-Peter Clausen if (!try_module_get(component->dev->driver->owner)) 1241abd31b32SLars-Peter Clausen return -ENODEV; 1242abd31b32SLars-Peter Clausen 1243f1d45cc3SLars-Peter Clausen component->card = card; 1244f1d45cc3SLars-Peter Clausen dapm->card = card; 1245f1d45cc3SLars-Peter Clausen soc_set_name_prefix(card, component); 1246589c3563SJarkko Nikula 1247f1d45cc3SLars-Peter Clausen soc_init_component_debugfs(component); 1248d5d1e0beSLars-Peter Clausen 1249688d0ebfSKuninori Morimoto if (component->driver->dapm_widgets) { 1250688d0ebfSKuninori Morimoto ret = snd_soc_dapm_new_controls(dapm, 1251688d0ebfSKuninori Morimoto component->driver->dapm_widgets, 1252688d0ebfSKuninori Morimoto component->driver->num_dapm_widgets); 125377530150SLars-Peter Clausen 1254b318ad50SNariman Poushin if (ret != 0) { 1255f1d45cc3SLars-Peter Clausen dev_err(component->dev, 1256b318ad50SNariman Poushin "Failed to create new controls %d\n", ret); 1257b318ad50SNariman Poushin goto err_probe; 1258b318ad50SNariman Poushin } 1259b318ad50SNariman Poushin } 1260b318ad50SNariman Poushin 12610634814fSLars-Peter Clausen list_for_each_entry(dai, &component->dai_list, list) { 12620634814fSLars-Peter Clausen ret = snd_soc_dapm_new_dai_widgets(dapm, dai); 1263261edc70SNariman Poushin if (ret != 0) { 1264f1d45cc3SLars-Peter Clausen dev_err(component->dev, 1265261edc70SNariman Poushin "Failed to create DAI widgets %d\n", ret); 1266261edc70SNariman Poushin goto err_probe; 1267261edc70SNariman Poushin } 1268261edc70SNariman Poushin } 1269888df395SMark Brown 1270999f7f5aSKuninori Morimoto if (component->driver->probe) { 1271999f7f5aSKuninori Morimoto ret = component->driver->probe(component); 1272589c3563SJarkko Nikula if (ret < 0) { 1273f1d45cc3SLars-Peter Clausen dev_err(component->dev, 1274f1d45cc3SLars-Peter Clausen "ASoC: failed to probe component %d\n", ret); 127570d29331SJarkko Nikula goto err_probe; 1276589c3563SJarkko Nikula } 1277cb2cf612SLiam Girdwood 1278f1d45cc3SLars-Peter Clausen WARN(dapm->idle_bias_off && 1279f1d45cc3SLars-Peter Clausen dapm->bias_level != SND_SOC_BIAS_OFF, 1280956245e9SLiam Girdwood "codec %s can not start from non-off bias with idle_bias_off==1\n", 1281f1d45cc3SLars-Peter Clausen component->name); 1282956245e9SLiam Girdwood } 1283956245e9SLiam Girdwood 1284f2ed6b07SMengdong Lin /* machine specific init */ 1285f2ed6b07SMengdong Lin if (component->init) { 1286f2ed6b07SMengdong Lin ret = component->init(component); 1287f2ed6b07SMengdong Lin if (ret < 0) { 1288f2ed6b07SMengdong Lin dev_err(component->dev, 1289f2ed6b07SMengdong Lin "Failed to do machine specific init %d\n", ret); 1290f2ed6b07SMengdong Lin goto err_probe; 1291f2ed6b07SMengdong Lin } 1292f2ed6b07SMengdong Lin } 1293f2ed6b07SMengdong Lin 1294b8972bf0SKuninori Morimoto if (component->driver->controls) 1295b8972bf0SKuninori Morimoto snd_soc_add_component_controls(component, 1296b8972bf0SKuninori Morimoto component->driver->controls, 1297b8972bf0SKuninori Morimoto component->driver->num_controls); 12986969b2baSKuninori Morimoto if (component->driver->dapm_routes) 12996969b2baSKuninori Morimoto snd_soc_dapm_add_routes(dapm, 13006969b2baSKuninori Morimoto component->driver->dapm_routes, 13016969b2baSKuninori Morimoto component->driver->num_dapm_routes); 1302956245e9SLiam Girdwood 1303f1d45cc3SLars-Peter Clausen list_add(&dapm->list, &card->dapm_list); 1304d9fc4063SKuninori Morimoto list_add(&component->card_list, &card->component_dev_list); 1305956245e9SLiam Girdwood 1306956245e9SLiam Girdwood return 0; 1307956245e9SLiam Girdwood 1308956245e9SLiam Girdwood err_probe: 1309f1d45cc3SLars-Peter Clausen soc_cleanup_component_debugfs(component); 1310abd31b32SLars-Peter Clausen component->card = NULL; 1311f1d45cc3SLars-Peter Clausen module_put(component->dev->driver->owner); 1312956245e9SLiam Girdwood 1313956245e9SLiam Girdwood return ret; 1314956245e9SLiam Girdwood } 1315956245e9SLiam Girdwood 131636ae1a96SMark Brown static void rtd_release(struct device *dev) 131736ae1a96SMark Brown { 131836ae1a96SMark Brown kfree(dev); 131936ae1a96SMark Brown } 1320f0fba2adSLiam Girdwood 13215f3484acSLars-Peter Clausen static int soc_post_component_init(struct snd_soc_pcm_runtime *rtd, 13225f3484acSLars-Peter Clausen const char *name) 1323503ae5e0SMisael Lopez Cruz { 1324589c3563SJarkko Nikula int ret = 0; 1325589c3563SJarkko Nikula 1326589c3563SJarkko Nikula /* register the rtd device */ 132736ae1a96SMark Brown rtd->dev = kzalloc(sizeof(struct device), GFP_KERNEL); 132836ae1a96SMark Brown if (!rtd->dev) 132936ae1a96SMark Brown return -ENOMEM; 133036ae1a96SMark Brown device_initialize(rtd->dev); 13315f3484acSLars-Peter Clausen rtd->dev->parent = rtd->card->dev; 133236ae1a96SMark Brown rtd->dev->release = rtd_release; 1333d29697dcSTakashi Iwai rtd->dev->groups = soc_dev_attr_groups; 1334f294afedSLars-Peter Clausen dev_set_name(rtd->dev, "%s", name); 133536ae1a96SMark Brown dev_set_drvdata(rtd->dev, rtd); 1336b8c0dab9SLiam Girdwood mutex_init(&rtd->pcm_mutex); 133701d7584cSLiam Girdwood INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].be_clients); 133801d7584cSLiam Girdwood INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].be_clients); 133901d7584cSLiam Girdwood INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].fe_clients); 134001d7584cSLiam Girdwood INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].fe_clients); 134136ae1a96SMark Brown ret = device_add(rtd->dev); 1342589c3563SJarkko Nikula if (ret < 0) { 1343865df9cbSChuansheng Liu /* calling put_device() here to free the rtd->dev */ 1344865df9cbSChuansheng Liu put_device(rtd->dev); 13455f3484acSLars-Peter Clausen dev_err(rtd->card->dev, 1346f110bfc7SLiam Girdwood "ASoC: failed to register runtime device: %d\n", ret); 1347589c3563SJarkko Nikula return ret; 1348589c3563SJarkko Nikula } 1349589c3563SJarkko Nikula rtd->dev_registered = 1; 1350589c3563SJarkko Nikula return 0; 1351589c3563SJarkko Nikula } 1352589c3563SJarkko Nikula 13531a497983SMengdong Lin static int soc_probe_link_components(struct snd_soc_card *card, 13541a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd, 135562ae68faSStephen Warren int order) 135662ae68faSStephen Warren { 1357f1d45cc3SLars-Peter Clausen struct snd_soc_component *component; 135890be711eSKuninori Morimoto struct snd_soc_rtdcom_list *rtdcom; 135990be711eSKuninori Morimoto int ret; 136062ae68faSStephen Warren 136190be711eSKuninori Morimoto for_each_rtdcom(rtd, rtdcom) { 136290be711eSKuninori Morimoto component = rtdcom->component; 136390be711eSKuninori Morimoto 136470090bbbSLars-Peter Clausen if (component->driver->probe_order == order) { 1365f1d45cc3SLars-Peter Clausen ret = soc_probe_component(card, component); 136662ae68faSStephen Warren if (ret < 0) 136762ae68faSStephen Warren return ret; 136862ae68faSStephen Warren } 136962ae68faSStephen Warren } 137062ae68faSStephen Warren 137162ae68faSStephen Warren return 0; 137262ae68faSStephen Warren } 137362ae68faSStephen Warren 13748e2be562SLars-Peter Clausen static int soc_probe_dai(struct snd_soc_dai *dai, int order) 1375b0aa88afSMisael Lopez Cruz { 13767a2ccad5SKuninori Morimoto if (dai->probed || 13777a2ccad5SKuninori Morimoto dai->driver->probe_order != order) 13787a2ccad5SKuninori Morimoto return 0; 1379b0aa88afSMisael Lopez Cruz 13808e2be562SLars-Peter Clausen if (dai->driver->probe) { 13817a2ccad5SKuninori Morimoto int ret = dai->driver->probe(dai); 1382b0aa88afSMisael Lopez Cruz if (ret < 0) { 13837a2ccad5SKuninori Morimoto dev_err(dai->dev, "ASoC: failed to probe DAI %s: %d\n", 13848e2be562SLars-Peter Clausen dai->name, ret); 1385b0aa88afSMisael Lopez Cruz return ret; 1386b0aa88afSMisael Lopez Cruz } 1387b0aa88afSMisael Lopez Cruz } 1388b0aa88afSMisael Lopez Cruz 13898e2be562SLars-Peter Clausen dai->probed = 1; 1390b0aa88afSMisael Lopez Cruz 1391b0aa88afSMisael Lopez Cruz return 0; 1392b0aa88afSMisael Lopez Cruz } 1393b0aa88afSMisael Lopez Cruz 139425f7b701SArnaud Pouliquen static int soc_link_dai_pcm_new(struct snd_soc_dai **dais, int num_dais, 139525f7b701SArnaud Pouliquen struct snd_soc_pcm_runtime *rtd) 139625f7b701SArnaud Pouliquen { 139725f7b701SArnaud Pouliquen int i, ret = 0; 139825f7b701SArnaud Pouliquen 139925f7b701SArnaud Pouliquen for (i = 0; i < num_dais; ++i) { 140025f7b701SArnaud Pouliquen struct snd_soc_dai_driver *drv = dais[i]->driver; 140125f7b701SArnaud Pouliquen 140225f7b701SArnaud Pouliquen if (!rtd->dai_link->no_pcm && drv->pcm_new) 140325f7b701SArnaud Pouliquen ret = drv->pcm_new(rtd, dais[i]); 140425f7b701SArnaud Pouliquen if (ret < 0) { 140525f7b701SArnaud Pouliquen dev_err(dais[i]->dev, 140625f7b701SArnaud Pouliquen "ASoC: Failed to bind %s with pcm device\n", 140725f7b701SArnaud Pouliquen dais[i]->name); 140825f7b701SArnaud Pouliquen return ret; 140925f7b701SArnaud Pouliquen } 141025f7b701SArnaud Pouliquen } 141125f7b701SArnaud Pouliquen 141225f7b701SArnaud Pouliquen return 0; 141325f7b701SArnaud Pouliquen } 141425f7b701SArnaud Pouliquen 14152436a723SMisael Lopez Cruz static int soc_link_dai_widgets(struct snd_soc_card *card, 14162436a723SMisael Lopez Cruz struct snd_soc_dai_link *dai_link, 14173f901a02SBenoit Cousson struct snd_soc_pcm_runtime *rtd) 14182436a723SMisael Lopez Cruz { 14193f901a02SBenoit Cousson struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 14203f901a02SBenoit Cousson struct snd_soc_dai *codec_dai = rtd->codec_dai; 14213de7c420SVinod Koul struct snd_soc_dapm_widget *sink, *source; 14222436a723SMisael Lopez Cruz int ret; 14232436a723SMisael Lopez Cruz 142488bd870fSBenoit Cousson if (rtd->num_codecs > 1) 142588bd870fSBenoit Cousson dev_warn(card->dev, "ASoC: Multiple codecs not supported yet\n"); 142688bd870fSBenoit Cousson 14272436a723SMisael Lopez Cruz /* link the DAI widgets */ 14283de7c420SVinod Koul sink = codec_dai->playback_widget; 14293de7c420SVinod Koul source = cpu_dai->capture_widget; 14303de7c420SVinod Koul if (sink && source) { 14312436a723SMisael Lopez Cruz ret = snd_soc_dapm_new_pcm(card, dai_link->params, 14323de7c420SVinod Koul dai_link->num_params, 14333de7c420SVinod Koul source, sink); 14342436a723SMisael Lopez Cruz if (ret != 0) { 14352436a723SMisael Lopez Cruz dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n", 14363de7c420SVinod Koul sink->name, source->name, ret); 14372436a723SMisael Lopez Cruz return ret; 14382436a723SMisael Lopez Cruz } 14392436a723SMisael Lopez Cruz } 14402436a723SMisael Lopez Cruz 14413de7c420SVinod Koul sink = cpu_dai->playback_widget; 14423de7c420SVinod Koul source = codec_dai->capture_widget; 14433de7c420SVinod Koul if (sink && source) { 14442436a723SMisael Lopez Cruz ret = snd_soc_dapm_new_pcm(card, dai_link->params, 14453de7c420SVinod Koul dai_link->num_params, 14463de7c420SVinod Koul source, sink); 14472436a723SMisael Lopez Cruz if (ret != 0) { 14482436a723SMisael Lopez Cruz dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n", 14493de7c420SVinod Koul sink->name, source->name, ret); 14502436a723SMisael Lopez Cruz return ret; 14512436a723SMisael Lopez Cruz } 14522436a723SMisael Lopez Cruz } 14532436a723SMisael Lopez Cruz 14542436a723SMisael Lopez Cruz return 0; 14552436a723SMisael Lopez Cruz } 14562436a723SMisael Lopez Cruz 14571a497983SMengdong Lin static int soc_probe_link_dais(struct snd_soc_card *card, 14581a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd, int order) 1459f0fba2adSLiam Girdwood { 14601a497983SMengdong Lin struct snd_soc_dai_link *dai_link = rtd->dai_link; 1461c74184edSMark Brown struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 1462*a655de80SLiam Girdwood struct snd_soc_rtdcom_list *rtdcom; 1463*a655de80SLiam Girdwood struct snd_soc_component *component; 1464*a655de80SLiam Girdwood int i, ret, num; 1465f0fba2adSLiam Girdwood 1466f110bfc7SLiam Girdwood dev_dbg(card->dev, "ASoC: probe %s dai link %d late %d\n", 14671a497983SMengdong Lin card->name, rtd->num, order); 1468f0fba2adSLiam Girdwood 1469f0fba2adSLiam Girdwood /* set default power off timeout */ 1470f0fba2adSLiam Girdwood rtd->pmdown_time = pmdown_time; 1471f0fba2adSLiam Girdwood 14728e2be562SLars-Peter Clausen ret = soc_probe_dai(cpu_dai, order); 14738e2be562SLars-Peter Clausen if (ret) 1474f0fba2adSLiam Girdwood return ret; 1475f0fba2adSLiam Girdwood 1476f0fba2adSLiam Girdwood /* probe the CODEC DAI */ 147788bd870fSBenoit Cousson for (i = 0; i < rtd->num_codecs; i++) { 14788e2be562SLars-Peter Clausen ret = soc_probe_dai(rtd->codec_dais[i], order); 1479b0aa88afSMisael Lopez Cruz if (ret) 1480f0fba2adSLiam Girdwood return ret; 148188bd870fSBenoit Cousson } 1482f0fba2adSLiam Girdwood 14830168bf0dSLiam Girdwood /* complete DAI probe during last probe */ 14840168bf0dSLiam Girdwood if (order != SND_SOC_COMP_ORDER_LAST) 14850168bf0dSLiam Girdwood return 0; 14860168bf0dSLiam Girdwood 14875f3484acSLars-Peter Clausen /* do machine specific initialization */ 14885f3484acSLars-Peter Clausen if (dai_link->init) { 14895f3484acSLars-Peter Clausen ret = dai_link->init(rtd); 14905f3484acSLars-Peter Clausen if (ret < 0) { 14915f3484acSLars-Peter Clausen dev_err(card->dev, "ASoC: failed to init %s: %d\n", 14925f3484acSLars-Peter Clausen dai_link->name, ret); 14935f3484acSLars-Peter Clausen return ret; 14945f3484acSLars-Peter Clausen } 14955f3484acSLars-Peter Clausen } 14965f3484acSLars-Peter Clausen 1497a5053a8eSKuninori Morimoto if (dai_link->dai_fmt) 1498a5053a8eSKuninori Morimoto snd_soc_runtime_set_dai_fmt(rtd, dai_link->dai_fmt); 1499a5053a8eSKuninori Morimoto 15005f3484acSLars-Peter Clausen ret = soc_post_component_init(rtd, dai_link->name); 1501589c3563SJarkko Nikula if (ret) 1502f0fba2adSLiam Girdwood return ret; 1503f0fba2adSLiam Girdwood 15045f3484acSLars-Peter Clausen #ifdef CONFIG_DEBUG_FS 15055f3484acSLars-Peter Clausen /* add DPCM sysfs entries */ 15062e55b90aSLars-Peter Clausen if (dai_link->dynamic) 15072e55b90aSLars-Peter Clausen soc_dpcm_debugfs_add(rtd); 15085f3484acSLars-Peter Clausen #endif 15095f3484acSLars-Peter Clausen 1510*a655de80SLiam Girdwood num = rtd->num; 1511*a655de80SLiam Girdwood 1512*a655de80SLiam Girdwood /* 1513*a655de80SLiam Girdwood * most drivers will register their PCMs using DAI link ordering but 1514*a655de80SLiam Girdwood * topology based drivers can use the DAI link id field to set PCM 1515*a655de80SLiam Girdwood * device number and then use rtd + a base offset of the BEs. 1516*a655de80SLiam Girdwood */ 1517*a655de80SLiam Girdwood for_each_rtdcom(rtd, rtdcom) { 1518*a655de80SLiam Girdwood component = rtdcom->component; 1519*a655de80SLiam Girdwood 1520*a655de80SLiam Girdwood if (!component->driver->use_dai_pcm_id) 1521*a655de80SLiam Girdwood continue; 1522*a655de80SLiam Girdwood 1523*a655de80SLiam Girdwood if (rtd->dai_link->no_pcm) 1524*a655de80SLiam Girdwood num += component->driver->be_pcm_base; 1525*a655de80SLiam Girdwood else 1526*a655de80SLiam Girdwood num = rtd->dai_link->id; 1527*a655de80SLiam Girdwood } 1528*a655de80SLiam Girdwood 15296f0c4226SJie Yang if (cpu_dai->driver->compress_new) { 15301245b700SNamarta Kohli /*create compress_device"*/ 1531*a655de80SLiam Girdwood ret = cpu_dai->driver->compress_new(rtd, num); 15321245b700SNamarta Kohli if (ret < 0) { 1533f110bfc7SLiam Girdwood dev_err(card->dev, "ASoC: can't create compress %s\n", 15341245b700SNamarta Kohli dai_link->stream_name); 15351245b700SNamarta Kohli return ret; 15361245b700SNamarta Kohli } 15371245b700SNamarta Kohli } else { 15381245b700SNamarta Kohli 1539c74184edSMark Brown if (!dai_link->params) { 1540f0fba2adSLiam Girdwood /* create the pcm */ 1541*a655de80SLiam Girdwood ret = soc_new_pcm(rtd, num); 1542f0fba2adSLiam Girdwood if (ret < 0) { 1543f110bfc7SLiam Girdwood dev_err(card->dev, "ASoC: can't create pcm %s :%d\n", 15440837fc62SFabio Estevam dai_link->stream_name, ret); 1545f0fba2adSLiam Girdwood return ret; 1546f0fba2adSLiam Girdwood } 154725f7b701SArnaud Pouliquen ret = soc_link_dai_pcm_new(&cpu_dai, 1, rtd); 154825f7b701SArnaud Pouliquen if (ret < 0) 154925f7b701SArnaud Pouliquen return ret; 155025f7b701SArnaud Pouliquen ret = soc_link_dai_pcm_new(rtd->codec_dais, 155125f7b701SArnaud Pouliquen rtd->num_codecs, rtd); 155225f7b701SArnaud Pouliquen if (ret < 0) 155325f7b701SArnaud Pouliquen return ret; 1554c74184edSMark Brown } else { 15559d58a077SRichard Fitzgerald INIT_DELAYED_WORK(&rtd->delayed_work, 15569d58a077SRichard Fitzgerald codec2codec_close_delayed_work); 15579d58a077SRichard Fitzgerald 1558c74184edSMark Brown /* link the DAI widgets */ 15593f901a02SBenoit Cousson ret = soc_link_dai_widgets(card, dai_link, rtd); 15602436a723SMisael Lopez Cruz if (ret) 1561c74184edSMark Brown return ret; 1562c74184edSMark Brown } 1563c74184edSMark Brown } 1564c74184edSMark Brown 1565f0fba2adSLiam Girdwood return 0; 1566f0fba2adSLiam Girdwood } 1567f0fba2adSLiam Girdwood 156844c69bb1SLars-Peter Clausen static int soc_bind_aux_dev(struct snd_soc_card *card, int num) 1569b19e6e7bSMark Brown { 15703ca041edSSebastian Reichel struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num]; 1571f2ed6b07SMengdong Lin struct snd_soc_component *component; 1572f2ed6b07SMengdong Lin const char *name; 1573f2ed6b07SMengdong Lin struct device_node *codec_of_node; 15743ca041edSSebastian Reichel 1575f2ed6b07SMengdong Lin if (aux_dev->codec_of_node || aux_dev->codec_name) { 1576f2ed6b07SMengdong Lin /* codecs, usually analog devices */ 1577f2ed6b07SMengdong Lin name = aux_dev->codec_name; 1578f2ed6b07SMengdong Lin codec_of_node = aux_dev->codec_of_node; 1579f2ed6b07SMengdong Lin component = soc_find_component(codec_of_node, name); 1580f2ed6b07SMengdong Lin if (!component) { 1581f2ed6b07SMengdong Lin if (codec_of_node) 1582f2ed6b07SMengdong Lin name = of_node_full_name(codec_of_node); 1583f2ed6b07SMengdong Lin goto err_defer; 1584f2ed6b07SMengdong Lin } 1585f2ed6b07SMengdong Lin } else if (aux_dev->name) { 1586f2ed6b07SMengdong Lin /* generic components */ 1587f2ed6b07SMengdong Lin name = aux_dev->name; 1588f2ed6b07SMengdong Lin component = soc_find_component(NULL, name); 1589f2ed6b07SMengdong Lin if (!component) 1590f2ed6b07SMengdong Lin goto err_defer; 1591f2ed6b07SMengdong Lin } else { 1592f2ed6b07SMengdong Lin dev_err(card->dev, "ASoC: Invalid auxiliary device\n"); 1593f2ed6b07SMengdong Lin return -EINVAL; 1594f2ed6b07SMengdong Lin } 15953ca041edSSebastian Reichel 1596f2ed6b07SMengdong Lin component->init = aux_dev->init; 1597d2e3a135SSylwester Nawrocki list_add(&component->card_aux_list, &card->aux_comp_list); 15981a653aa4SKuninori Morimoto 1599f2ed6b07SMengdong Lin return 0; 1600f2ed6b07SMengdong Lin 1601f2ed6b07SMengdong Lin err_defer: 160265d9361fSLars-Peter Clausen dev_err(card->dev, "ASoC: %s not registered\n", name); 1603b19e6e7bSMark Brown return -EPROBE_DEFER; 1604b19e6e7bSMark Brown } 1605b19e6e7bSMark Brown 1606f2ed6b07SMengdong Lin static int soc_probe_aux_devices(struct snd_soc_card *card) 1607f2ed6b07SMengdong Lin { 1608991454e1SKuninori Morimoto struct snd_soc_component *comp; 1609f2ed6b07SMengdong Lin int order; 1610f2ed6b07SMengdong Lin int ret; 1611f2ed6b07SMengdong Lin 1612f2ed6b07SMengdong Lin for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; 1613f2ed6b07SMengdong Lin order++) { 1614991454e1SKuninori Morimoto list_for_each_entry(comp, &card->aux_comp_list, card_aux_list) { 1615f2ed6b07SMengdong Lin if (comp->driver->probe_order == order) { 1616f2ed6b07SMengdong Lin ret = soc_probe_component(card, comp); 1617f2ed6b07SMengdong Lin if (ret < 0) { 1618f2ed6b07SMengdong Lin dev_err(card->dev, 1619f2ed6b07SMengdong Lin "ASoC: failed to probe aux component %s %d\n", 1620f2ed6b07SMengdong Lin comp->name, ret); 1621f2ed6b07SMengdong Lin return ret; 1622f2ed6b07SMengdong Lin } 1623f2ed6b07SMengdong Lin } 1624f2ed6b07SMengdong Lin } 1625f2ed6b07SMengdong Lin } 162665d9361fSLars-Peter Clausen 162744c69bb1SLars-Peter Clausen return 0; 16283ca041edSSebastian Reichel } 16292eea392dSJarkko Nikula 1630f2ed6b07SMengdong Lin static void soc_remove_aux_devices(struct snd_soc_card *card) 163144c69bb1SLars-Peter Clausen { 1632f2ed6b07SMengdong Lin struct snd_soc_component *comp, *_comp; 1633f2ed6b07SMengdong Lin int order; 163444c69bb1SLars-Peter Clausen 1635f2ed6b07SMengdong Lin for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; 1636f2ed6b07SMengdong Lin order++) { 1637f2ed6b07SMengdong Lin list_for_each_entry_safe(comp, _comp, 1638991454e1SKuninori Morimoto &card->aux_comp_list, card_aux_list) { 16391a653aa4SKuninori Morimoto 1640f2ed6b07SMengdong Lin if (comp->driver->remove_order == order) { 1641f2ed6b07SMengdong Lin soc_remove_component(comp); 1642991454e1SKuninori Morimoto /* remove it from the card's aux_comp_list */ 1643991454e1SKuninori Morimoto list_del(&comp->card_aux_list); 16442eea392dSJarkko Nikula } 16455f3484acSLars-Peter Clausen } 16462eea392dSJarkko Nikula } 16472eea392dSJarkko Nikula } 16482eea392dSJarkko Nikula 1649ce64c8b9SLars-Peter Clausen /** 1650ce64c8b9SLars-Peter Clausen * snd_soc_runtime_set_dai_fmt() - Change DAI link format for a ASoC runtime 1651ce64c8b9SLars-Peter Clausen * @rtd: The runtime for which the DAI link format should be changed 1652ce64c8b9SLars-Peter Clausen * @dai_fmt: The new DAI link format 1653ce64c8b9SLars-Peter Clausen * 1654ce64c8b9SLars-Peter Clausen * This function updates the DAI link format for all DAIs connected to the DAI 1655ce64c8b9SLars-Peter Clausen * link for the specified runtime. 1656ce64c8b9SLars-Peter Clausen * 1657ce64c8b9SLars-Peter Clausen * Note: For setups with a static format set the dai_fmt field in the 1658ce64c8b9SLars-Peter Clausen * corresponding snd_dai_link struct instead of using this function. 1659ce64c8b9SLars-Peter Clausen * 1660ce64c8b9SLars-Peter Clausen * Returns 0 on success, otherwise a negative error code. 1661ce64c8b9SLars-Peter Clausen */ 1662ce64c8b9SLars-Peter Clausen int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd, 1663ce64c8b9SLars-Peter Clausen unsigned int dai_fmt) 1664ce64c8b9SLars-Peter Clausen { 1665ce64c8b9SLars-Peter Clausen struct snd_soc_dai **codec_dais = rtd->codec_dais; 1666ce64c8b9SLars-Peter Clausen struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 1667ce64c8b9SLars-Peter Clausen unsigned int i; 1668ce64c8b9SLars-Peter Clausen int ret; 1669ce64c8b9SLars-Peter Clausen 1670ce64c8b9SLars-Peter Clausen for (i = 0; i < rtd->num_codecs; i++) { 1671ce64c8b9SLars-Peter Clausen struct snd_soc_dai *codec_dai = codec_dais[i]; 1672ce64c8b9SLars-Peter Clausen 1673ce64c8b9SLars-Peter Clausen ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt); 1674ce64c8b9SLars-Peter Clausen if (ret != 0 && ret != -ENOTSUPP) { 1675ce64c8b9SLars-Peter Clausen dev_warn(codec_dai->dev, 1676ce64c8b9SLars-Peter Clausen "ASoC: Failed to set DAI format: %d\n", ret); 1677ce64c8b9SLars-Peter Clausen return ret; 1678ce64c8b9SLars-Peter Clausen } 1679ce64c8b9SLars-Peter Clausen } 1680ce64c8b9SLars-Peter Clausen 1681ce64c8b9SLars-Peter Clausen /* Flip the polarity for the "CPU" end of a CODEC<->CODEC link */ 1682cb2cf0deSKuninori Morimoto /* the component which has non_legacy_dai_naming is Codec */ 1683999f7f5aSKuninori Morimoto if (cpu_dai->component->driver->non_legacy_dai_naming) { 1684ce64c8b9SLars-Peter Clausen unsigned int inv_dai_fmt; 1685ce64c8b9SLars-Peter Clausen 1686ce64c8b9SLars-Peter Clausen inv_dai_fmt = dai_fmt & ~SND_SOC_DAIFMT_MASTER_MASK; 1687ce64c8b9SLars-Peter Clausen switch (dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) { 1688ce64c8b9SLars-Peter Clausen case SND_SOC_DAIFMT_CBM_CFM: 1689ce64c8b9SLars-Peter Clausen inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; 1690ce64c8b9SLars-Peter Clausen break; 1691ce64c8b9SLars-Peter Clausen case SND_SOC_DAIFMT_CBM_CFS: 1692ce64c8b9SLars-Peter Clausen inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFM; 1693ce64c8b9SLars-Peter Clausen break; 1694ce64c8b9SLars-Peter Clausen case SND_SOC_DAIFMT_CBS_CFM: 1695ce64c8b9SLars-Peter Clausen inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFS; 1696ce64c8b9SLars-Peter Clausen break; 1697ce64c8b9SLars-Peter Clausen case SND_SOC_DAIFMT_CBS_CFS: 1698ce64c8b9SLars-Peter Clausen inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; 1699ce64c8b9SLars-Peter Clausen break; 1700ce64c8b9SLars-Peter Clausen } 1701ce64c8b9SLars-Peter Clausen 1702ce64c8b9SLars-Peter Clausen dai_fmt = inv_dai_fmt; 1703ce64c8b9SLars-Peter Clausen } 1704ce64c8b9SLars-Peter Clausen 1705ce64c8b9SLars-Peter Clausen ret = snd_soc_dai_set_fmt(cpu_dai, dai_fmt); 1706ce64c8b9SLars-Peter Clausen if (ret != 0 && ret != -ENOTSUPP) { 1707ce64c8b9SLars-Peter Clausen dev_warn(cpu_dai->dev, 1708ce64c8b9SLars-Peter Clausen "ASoC: Failed to set DAI format: %d\n", ret); 1709ce64c8b9SLars-Peter Clausen return ret; 1710ce64c8b9SLars-Peter Clausen } 1711ce64c8b9SLars-Peter Clausen 1712ce64c8b9SLars-Peter Clausen return 0; 1713ce64c8b9SLars-Peter Clausen } 1714ddaca25aSLars-Peter Clausen EXPORT_SYMBOL_GPL(snd_soc_runtime_set_dai_fmt); 1715ce64c8b9SLars-Peter Clausen 1716345233d7SLiam Girdwood 17171f5a4535STakashi Iwai #ifdef CONFIG_DMI 1718345233d7SLiam Girdwood /* Trim special characters, and replace '-' with '_' since '-' is used to 1719345233d7SLiam Girdwood * separate different DMI fields in the card long name. Only number and 1720345233d7SLiam Girdwood * alphabet characters and a few separator characters are kept. 1721345233d7SLiam Girdwood */ 1722345233d7SLiam Girdwood static void cleanup_dmi_name(char *name) 1723345233d7SLiam Girdwood { 1724345233d7SLiam Girdwood int i, j = 0; 1725345233d7SLiam Girdwood 1726345233d7SLiam Girdwood for (i = 0; name[i]; i++) { 1727345233d7SLiam Girdwood if (isalnum(name[i]) || (name[i] == '.') 1728345233d7SLiam Girdwood || (name[i] == '_')) 1729345233d7SLiam Girdwood name[j++] = name[i]; 1730345233d7SLiam Girdwood else if (name[i] == '-') 1731345233d7SLiam Girdwood name[j++] = '_'; 1732345233d7SLiam Girdwood } 1733345233d7SLiam Girdwood 1734345233d7SLiam Girdwood name[j] = '\0'; 1735345233d7SLiam Girdwood } 1736345233d7SLiam Girdwood 173798faf436SMengdong Lin /* Check if a DMI field is valid, i.e. not containing any string 173898faf436SMengdong Lin * in the black list. 173998faf436SMengdong Lin */ 174098faf436SMengdong Lin static int is_dmi_valid(const char *field) 174198faf436SMengdong Lin { 174298faf436SMengdong Lin int i = 0; 174398faf436SMengdong Lin 174498faf436SMengdong Lin while (dmi_blacklist[i]) { 174598faf436SMengdong Lin if (strstr(field, dmi_blacklist[i])) 174698faf436SMengdong Lin return 0; 174798faf436SMengdong Lin i++; 174846b5a4d2SWu Fengguang } 174998faf436SMengdong Lin 175098faf436SMengdong Lin return 1; 175198faf436SMengdong Lin } 175298faf436SMengdong Lin 1753345233d7SLiam Girdwood /** 1754345233d7SLiam Girdwood * snd_soc_set_dmi_name() - Register DMI names to card 1755345233d7SLiam Girdwood * @card: The card to register DMI names 1756345233d7SLiam Girdwood * @flavour: The flavour "differentiator" for the card amongst its peers. 1757345233d7SLiam Girdwood * 1758345233d7SLiam Girdwood * An Intel machine driver may be used by many different devices but are 1759345233d7SLiam Girdwood * difficult for userspace to differentiate, since machine drivers ususally 1760345233d7SLiam Girdwood * use their own name as the card short name and leave the card long name 1761345233d7SLiam Girdwood * blank. To differentiate such devices and fix bugs due to lack of 1762345233d7SLiam Girdwood * device-specific configurations, this function allows DMI info to be used 1763345233d7SLiam Girdwood * as the sound card long name, in the format of 1764345233d7SLiam Girdwood * "vendor-product-version-board" 1765345233d7SLiam Girdwood * (Character '-' is used to separate different DMI fields here). 1766345233d7SLiam Girdwood * This will help the user space to load the device-specific Use Case Manager 1767345233d7SLiam Girdwood * (UCM) configurations for the card. 1768345233d7SLiam Girdwood * 1769345233d7SLiam Girdwood * Possible card long names may be: 1770345233d7SLiam Girdwood * DellInc.-XPS139343-01-0310JH 1771345233d7SLiam Girdwood * ASUSTeKCOMPUTERINC.-T100TA-1.0-T100TA 1772345233d7SLiam Girdwood * Circuitco-MinnowboardMaxD0PLATFORM-D0-MinnowBoardMAX 1773345233d7SLiam Girdwood * 1774345233d7SLiam Girdwood * This function also supports flavoring the card longname to provide 1775345233d7SLiam Girdwood * the extra differentiation, like "vendor-product-version-board-flavor". 1776345233d7SLiam Girdwood * 1777345233d7SLiam Girdwood * We only keep number and alphabet characters and a few separator characters 1778345233d7SLiam Girdwood * in the card long name since UCM in the user space uses the card long names 1779345233d7SLiam Girdwood * as card configuration directory names and AudoConf cannot support special 1780345233d7SLiam Girdwood * charactors like SPACE. 1781345233d7SLiam Girdwood * 1782345233d7SLiam Girdwood * Returns 0 on success, otherwise a negative error code. 1783345233d7SLiam Girdwood */ 1784345233d7SLiam Girdwood int snd_soc_set_dmi_name(struct snd_soc_card *card, const char *flavour) 1785345233d7SLiam Girdwood { 1786345233d7SLiam Girdwood const char *vendor, *product, *product_version, *board; 1787345233d7SLiam Girdwood size_t longname_buf_size = sizeof(card->snd_card->longname); 1788345233d7SLiam Girdwood size_t len; 1789345233d7SLiam Girdwood 1790345233d7SLiam Girdwood if (card->long_name) 1791345233d7SLiam Girdwood return 0; /* long name already set by driver or from DMI */ 1792345233d7SLiam Girdwood 1793345233d7SLiam Girdwood /* make up dmi long name as: vendor.product.version.board */ 1794345233d7SLiam Girdwood vendor = dmi_get_system_info(DMI_BOARD_VENDOR); 179598faf436SMengdong Lin if (!vendor || !is_dmi_valid(vendor)) { 1796345233d7SLiam Girdwood dev_warn(card->dev, "ASoC: no DMI vendor name!\n"); 1797345233d7SLiam Girdwood return 0; 1798345233d7SLiam Girdwood } 1799345233d7SLiam Girdwood 180098faf436SMengdong Lin 1801345233d7SLiam Girdwood snprintf(card->dmi_longname, sizeof(card->snd_card->longname), 1802345233d7SLiam Girdwood "%s", vendor); 1803345233d7SLiam Girdwood cleanup_dmi_name(card->dmi_longname); 1804345233d7SLiam Girdwood 1805345233d7SLiam Girdwood product = dmi_get_system_info(DMI_PRODUCT_NAME); 180698faf436SMengdong Lin if (product && is_dmi_valid(product)) { 1807345233d7SLiam Girdwood len = strlen(card->dmi_longname); 1808345233d7SLiam Girdwood snprintf(card->dmi_longname + len, 1809345233d7SLiam Girdwood longname_buf_size - len, 1810345233d7SLiam Girdwood "-%s", product); 1811345233d7SLiam Girdwood 1812345233d7SLiam Girdwood len++; /* skip the separator "-" */ 1813345233d7SLiam Girdwood if (len < longname_buf_size) 1814345233d7SLiam Girdwood cleanup_dmi_name(card->dmi_longname + len); 1815345233d7SLiam Girdwood 1816345233d7SLiam Girdwood /* some vendors like Lenovo may only put a self-explanatory 1817345233d7SLiam Girdwood * name in the product version field 1818345233d7SLiam Girdwood */ 1819345233d7SLiam Girdwood product_version = dmi_get_system_info(DMI_PRODUCT_VERSION); 182098faf436SMengdong Lin if (product_version && is_dmi_valid(product_version)) { 1821345233d7SLiam Girdwood len = strlen(card->dmi_longname); 1822345233d7SLiam Girdwood snprintf(card->dmi_longname + len, 1823345233d7SLiam Girdwood longname_buf_size - len, 1824345233d7SLiam Girdwood "-%s", product_version); 1825345233d7SLiam Girdwood 1826345233d7SLiam Girdwood len++; 1827345233d7SLiam Girdwood if (len < longname_buf_size) 1828345233d7SLiam Girdwood cleanup_dmi_name(card->dmi_longname + len); 1829345233d7SLiam Girdwood } 1830345233d7SLiam Girdwood } 1831345233d7SLiam Girdwood 1832345233d7SLiam Girdwood board = dmi_get_system_info(DMI_BOARD_NAME); 183398faf436SMengdong Lin if (board && is_dmi_valid(board)) { 1834345233d7SLiam Girdwood len = strlen(card->dmi_longname); 1835345233d7SLiam Girdwood snprintf(card->dmi_longname + len, 1836345233d7SLiam Girdwood longname_buf_size - len, 1837345233d7SLiam Girdwood "-%s", board); 1838345233d7SLiam Girdwood 1839345233d7SLiam Girdwood len++; 1840345233d7SLiam Girdwood if (len < longname_buf_size) 1841345233d7SLiam Girdwood cleanup_dmi_name(card->dmi_longname + len); 1842345233d7SLiam Girdwood } else if (!product) { 1843345233d7SLiam Girdwood /* fall back to using legacy name */ 1844345233d7SLiam Girdwood dev_warn(card->dev, "ASoC: no DMI board/product name!\n"); 1845345233d7SLiam Girdwood return 0; 1846345233d7SLiam Girdwood } 1847345233d7SLiam Girdwood 1848345233d7SLiam Girdwood /* Add flavour to dmi long name */ 1849345233d7SLiam Girdwood if (flavour) { 1850345233d7SLiam Girdwood len = strlen(card->dmi_longname); 1851345233d7SLiam Girdwood snprintf(card->dmi_longname + len, 1852345233d7SLiam Girdwood longname_buf_size - len, 1853345233d7SLiam Girdwood "-%s", flavour); 1854345233d7SLiam Girdwood 1855345233d7SLiam Girdwood len++; 1856345233d7SLiam Girdwood if (len < longname_buf_size) 1857345233d7SLiam Girdwood cleanup_dmi_name(card->dmi_longname + len); 1858345233d7SLiam Girdwood } 1859345233d7SLiam Girdwood 1860345233d7SLiam Girdwood /* set the card long name */ 1861345233d7SLiam Girdwood card->long_name = card->dmi_longname; 1862345233d7SLiam Girdwood 1863345233d7SLiam Girdwood return 0; 1864345233d7SLiam Girdwood } 1865345233d7SLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_set_dmi_name); 18661f5a4535STakashi Iwai #endif /* CONFIG_DMI */ 1867345233d7SLiam Girdwood 1868*a655de80SLiam Girdwood static void soc_check_tplg_fes(struct snd_soc_card *card) 1869*a655de80SLiam Girdwood { 1870*a655de80SLiam Girdwood struct snd_soc_component *component; 1871*a655de80SLiam Girdwood const struct snd_soc_component_driver *comp_drv; 1872*a655de80SLiam Girdwood struct snd_soc_dai_link *dai_link; 1873*a655de80SLiam Girdwood int i; 1874*a655de80SLiam Girdwood 1875*a655de80SLiam Girdwood list_for_each_entry(component, &component_list, list) { 1876*a655de80SLiam Girdwood 1877*a655de80SLiam Girdwood /* does this component override FEs ? */ 1878*a655de80SLiam Girdwood if (!component->driver->ignore_machine) 1879*a655de80SLiam Girdwood continue; 1880*a655de80SLiam Girdwood 1881*a655de80SLiam Girdwood /* for this machine ? */ 1882*a655de80SLiam Girdwood if (strcmp(component->driver->ignore_machine, 1883*a655de80SLiam Girdwood card->dev->driver->name)) 1884*a655de80SLiam Girdwood continue; 1885*a655de80SLiam Girdwood 1886*a655de80SLiam Girdwood /* machine matches, so override the rtd data */ 1887*a655de80SLiam Girdwood for (i = 0; i < card->num_links; i++) { 1888*a655de80SLiam Girdwood 1889*a655de80SLiam Girdwood dai_link = &card->dai_link[i]; 1890*a655de80SLiam Girdwood 1891*a655de80SLiam Girdwood /* ignore this FE */ 1892*a655de80SLiam Girdwood if (dai_link->dynamic) { 1893*a655de80SLiam Girdwood dai_link->ignore = true; 1894*a655de80SLiam Girdwood continue; 1895*a655de80SLiam Girdwood } 1896*a655de80SLiam Girdwood 1897*a655de80SLiam Girdwood dev_info(card->dev, "info: override FE DAI link %s\n", 1898*a655de80SLiam Girdwood card->dai_link[i].name); 1899*a655de80SLiam Girdwood 1900*a655de80SLiam Girdwood /* override platform component */ 1901*a655de80SLiam Girdwood dai_link->platform_name = component->name; 1902*a655de80SLiam Girdwood 1903*a655de80SLiam Girdwood /* convert non BE into BE */ 1904*a655de80SLiam Girdwood dai_link->no_pcm = 1; 1905*a655de80SLiam Girdwood 1906*a655de80SLiam Girdwood /* override any BE fixups */ 1907*a655de80SLiam Girdwood dai_link->be_hw_params_fixup = 1908*a655de80SLiam Girdwood component->driver->be_hw_params_fixup; 1909*a655de80SLiam Girdwood 1910*a655de80SLiam Girdwood /* most BE links don't set stream name, so set it to 1911*a655de80SLiam Girdwood * dai link name if it's NULL to help bind widgets. 1912*a655de80SLiam Girdwood */ 1913*a655de80SLiam Girdwood if (!dai_link->stream_name) 1914*a655de80SLiam Girdwood dai_link->stream_name = dai_link->name; 1915*a655de80SLiam Girdwood } 1916*a655de80SLiam Girdwood 1917*a655de80SLiam Girdwood /* Inform userspace we are using alternate topology */ 1918*a655de80SLiam Girdwood if (component->driver->topology_name_prefix) { 1919*a655de80SLiam Girdwood 1920*a655de80SLiam Girdwood /* topology shortname created ? */ 1921*a655de80SLiam Girdwood if (!card->topology_shortname_created) { 1922*a655de80SLiam Girdwood comp_drv = component->driver; 1923*a655de80SLiam Girdwood 1924*a655de80SLiam Girdwood snprintf(card->topology_shortname, 32, "%s-%s", 1925*a655de80SLiam Girdwood comp_drv->topology_name_prefix, 1926*a655de80SLiam Girdwood card->name); 1927*a655de80SLiam Girdwood card->topology_shortname_created = true; 1928*a655de80SLiam Girdwood } 1929*a655de80SLiam Girdwood 1930*a655de80SLiam Girdwood /* use topology shortname */ 1931*a655de80SLiam Girdwood card->name = card->topology_shortname; 1932*a655de80SLiam Girdwood } 1933*a655de80SLiam Girdwood } 1934*a655de80SLiam Girdwood } 1935*a655de80SLiam Girdwood 1936b19e6e7bSMark Brown static int snd_soc_instantiate_card(struct snd_soc_card *card) 1937f0fba2adSLiam Girdwood { 19381a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd; 193961b0088bSMengdong Lin struct snd_soc_dai_link *dai_link; 1940ce64c8b9SLars-Peter Clausen int ret, i, order; 194196dd3622SMark Brown 194234e81ab4SLars-Peter Clausen mutex_lock(&client_mutex); 194301b9d99aSLiam Girdwood mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT); 1944f0fba2adSLiam Girdwood 1945*a655de80SLiam Girdwood /* check whether any platform is ignore machine FE and using topology */ 1946*a655de80SLiam Girdwood soc_check_tplg_fes(card); 1947*a655de80SLiam Girdwood 1948b19e6e7bSMark Brown /* bind DAIs */ 1949b19e6e7bSMark Brown for (i = 0; i < card->num_links; i++) { 19506f2f1ff0SMengdong Lin ret = soc_bind_dai_link(card, &card->dai_link[i]); 1951b19e6e7bSMark Brown if (ret != 0) 1952b19e6e7bSMark Brown goto base_error; 1953db2a4165SFrank Mandarino } 1954db2a4165SFrank Mandarino 195544c69bb1SLars-Peter Clausen /* bind aux_devs too */ 1956b19e6e7bSMark Brown for (i = 0; i < card->num_aux_devs; i++) { 195744c69bb1SLars-Peter Clausen ret = soc_bind_aux_dev(card, i); 1958b19e6e7bSMark Brown if (ret != 0) 1959b19e6e7bSMark Brown goto base_error; 1960db2a4165SFrank Mandarino } 1961db2a4165SFrank Mandarino 1962f8f80361SMengdong Lin /* add predefined DAI links to the list */ 1963f8f80361SMengdong Lin for (i = 0; i < card->num_links; i++) 1964f8f80361SMengdong Lin snd_soc_add_dai_link(card, card->dai_link+i); 1965f8f80361SMengdong Lin 1966f0fba2adSLiam Girdwood /* card bind complete so register a sound card */ 1967102b5a8dSTakashi Iwai ret = snd_card_new(card->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, 1968f0fba2adSLiam Girdwood card->owner, 0, &card->snd_card); 1969f0fba2adSLiam Girdwood if (ret < 0) { 197010e8aa9aSMichał Mirosław dev_err(card->dev, 197110e8aa9aSMichał Mirosław "ASoC: can't create sound card for card %s: %d\n", 197210e8aa9aSMichał Mirosław card->name, ret); 1973b19e6e7bSMark Brown goto base_error; 1974db2a4165SFrank Mandarino } 1975db2a4165SFrank Mandarino 19760757d834SLars-Peter Clausen soc_init_card_debugfs(card); 19770757d834SLars-Peter Clausen 1978e37a4970SMark Brown card->dapm.bias_level = SND_SOC_BIAS_OFF; 1979e37a4970SMark Brown card->dapm.dev = card->dev; 1980e37a4970SMark Brown card->dapm.card = card; 1981e37a4970SMark Brown list_add(&card->dapm.list, &card->dapm_list); 1982e37a4970SMark Brown 1983d5d1e0beSLars-Peter Clausen #ifdef CONFIG_DEBUG_FS 1984d5d1e0beSLars-Peter Clausen snd_soc_dapm_debugfs_init(&card->dapm, card->debugfs_card_root); 1985d5d1e0beSLars-Peter Clausen #endif 1986d5d1e0beSLars-Peter Clausen 198788ee1c61SMark Brown #ifdef CONFIG_PM_SLEEP 19886ed25978SAndy Green /* deferred resume work */ 19896308419aSMark Brown INIT_WORK(&card->deferred_resume_work, soc_resume_deferred); 19901301a964SRandy Dunlap #endif 19916ed25978SAndy Green 19929a841ebbSMark Brown if (card->dapm_widgets) 19939a841ebbSMark Brown snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets, 19949a841ebbSMark Brown card->num_dapm_widgets); 19959a841ebbSMark Brown 1996f23e860eSNicolin Chen if (card->of_dapm_widgets) 1997f23e860eSNicolin Chen snd_soc_dapm_new_controls(&card->dapm, card->of_dapm_widgets, 1998f23e860eSNicolin Chen card->num_of_dapm_widgets); 1999f23e860eSNicolin Chen 2000f0fba2adSLiam Girdwood /* initialise the sound card only once */ 2001f0fba2adSLiam Girdwood if (card->probe) { 2002e7361ec4SMark Brown ret = card->probe(card); 2003f0fba2adSLiam Girdwood if (ret < 0) 2004f0fba2adSLiam Girdwood goto card_probe_error; 2005f0fba2adSLiam Girdwood } 2006f0fba2adSLiam Girdwood 200762ae68faSStephen Warren /* probe all components used by DAI links on this card */ 20080168bf0dSLiam Girdwood for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; 20090168bf0dSLiam Girdwood order++) { 20101a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 20111a497983SMengdong Lin ret = soc_probe_link_components(card, rtd, order); 201262ae68faSStephen Warren if (ret < 0) { 2013f110bfc7SLiam Girdwood dev_err(card->dev, 2014f110bfc7SLiam Girdwood "ASoC: failed to instantiate card %d\n", 2015f110bfc7SLiam Girdwood ret); 201662ae68faSStephen Warren goto probe_dai_err; 201762ae68faSStephen Warren } 201862ae68faSStephen Warren } 201962ae68faSStephen Warren } 202062ae68faSStephen Warren 2021f2ed6b07SMengdong Lin /* probe auxiliary components */ 2022f2ed6b07SMengdong Lin ret = soc_probe_aux_devices(card); 2023f2ed6b07SMengdong Lin if (ret < 0) 2024f2ed6b07SMengdong Lin goto probe_dai_err; 2025f2ed6b07SMengdong Lin 202661b0088bSMengdong Lin /* Find new DAI links added during probing components and bind them. 202761b0088bSMengdong Lin * Components with topology may bring new DAIs and DAI links. 202861b0088bSMengdong Lin */ 202961b0088bSMengdong Lin list_for_each_entry(dai_link, &card->dai_link_list, list) { 203061b0088bSMengdong Lin if (soc_is_dai_link_bound(card, dai_link)) 203161b0088bSMengdong Lin continue; 203261b0088bSMengdong Lin 203361b0088bSMengdong Lin ret = soc_init_dai_link(card, dai_link); 203461b0088bSMengdong Lin if (ret) 203561b0088bSMengdong Lin goto probe_dai_err; 203661b0088bSMengdong Lin ret = soc_bind_dai_link(card, dai_link); 203761b0088bSMengdong Lin if (ret) 203861b0088bSMengdong Lin goto probe_dai_err; 203961b0088bSMengdong Lin } 204061b0088bSMengdong Lin 204162ae68faSStephen Warren /* probe all DAI links on this card */ 204262ae68faSStephen Warren for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; 204362ae68faSStephen Warren order++) { 20441a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 20451a497983SMengdong Lin ret = soc_probe_link_dais(card, rtd, order); 2046fe3e78e0SMark Brown if (ret < 0) { 2047f110bfc7SLiam Girdwood dev_err(card->dev, 2048f110bfc7SLiam Girdwood "ASoC: failed to instantiate card %d\n", 2049f110bfc7SLiam Girdwood ret); 2050f0fba2adSLiam Girdwood goto probe_dai_err; 2051fe3e78e0SMark Brown } 2052fe3e78e0SMark Brown } 20530168bf0dSLiam Girdwood } 2054fe3e78e0SMark Brown 2055888df395SMark Brown snd_soc_dapm_link_dai_widgets(card); 2056b893ea5fSLiam Girdwood snd_soc_dapm_connect_dai_link_widgets(card); 2057888df395SMark Brown 2058b7af1dafSMark Brown if (card->controls) 2059022658beSLiam Girdwood snd_soc_add_card_controls(card, card->controls, card->num_controls); 2060b7af1dafSMark Brown 2061b8ad29deSMark Brown if (card->dapm_routes) 2062b8ad29deSMark Brown snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes, 2063b8ad29deSMark Brown card->num_dapm_routes); 2064b8ad29deSMark Brown 2065f23e860eSNicolin Chen if (card->of_dapm_routes) 2066f23e860eSNicolin Chen snd_soc_dapm_add_routes(&card->dapm, card->of_dapm_routes, 2067f23e860eSNicolin Chen card->num_of_dapm_routes); 206875d9ac46SMark Brown 2069861886d3STakashi Iwai /* try to set some sane longname if DMI is available */ 2070861886d3STakashi Iwai snd_soc_set_dmi_name(card, NULL); 2071861886d3STakashi Iwai 2072f0fba2adSLiam Girdwood snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname), 2073fe3e78e0SMark Brown "%s", card->name); 2074f0fba2adSLiam Girdwood snprintf(card->snd_card->longname, sizeof(card->snd_card->longname), 207522de71baSLiam Girdwood "%s", card->long_name ? card->long_name : card->name); 2076f0e8ed85SMark Brown snprintf(card->snd_card->driver, sizeof(card->snd_card->driver), 2077f0e8ed85SMark Brown "%s", card->driver_name ? card->driver_name : card->name); 2078f0e8ed85SMark Brown for (i = 0; i < ARRAY_SIZE(card->snd_card->driver); i++) { 2079f0e8ed85SMark Brown switch (card->snd_card->driver[i]) { 2080f0e8ed85SMark Brown case '_': 2081f0e8ed85SMark Brown case '-': 2082f0e8ed85SMark Brown case '\0': 2083f0e8ed85SMark Brown break; 2084f0e8ed85SMark Brown default: 2085f0e8ed85SMark Brown if (!isalnum(card->snd_card->driver[i])) 2086f0e8ed85SMark Brown card->snd_card->driver[i] = '_'; 2087f0e8ed85SMark Brown break; 2088f0e8ed85SMark Brown } 2089f0e8ed85SMark Brown } 2090fe3e78e0SMark Brown 209128e9ad92SMark Brown if (card->late_probe) { 209228e9ad92SMark Brown ret = card->late_probe(card); 209328e9ad92SMark Brown if (ret < 0) { 2094f110bfc7SLiam Girdwood dev_err(card->dev, "ASoC: %s late_probe() failed: %d\n", 209528e9ad92SMark Brown card->name, ret); 209628e9ad92SMark Brown goto probe_aux_dev_err; 209728e9ad92SMark Brown } 209828e9ad92SMark Brown } 209928e9ad92SMark Brown 2100824ef826SLars-Peter Clausen snd_soc_dapm_new_widgets(card); 21018c193b8dSLars-Peter Clausen 2102f0fba2adSLiam Girdwood ret = snd_card_register(card->snd_card); 2103fe3e78e0SMark Brown if (ret < 0) { 2104f110bfc7SLiam Girdwood dev_err(card->dev, "ASoC: failed to register soundcard %d\n", 2105f110bfc7SLiam Girdwood ret); 21066b3ed785SAxel Lin goto probe_aux_dev_err; 2107fe3e78e0SMark Brown } 2108fe3e78e0SMark Brown 2109435c5e25SMark Brown card->instantiated = 1; 21104f4c0072SMark Brown snd_soc_dapm_sync(&card->dapm); 2111f0fba2adSLiam Girdwood mutex_unlock(&card->mutex); 211234e81ab4SLars-Peter Clausen mutex_unlock(&client_mutex); 2113b19e6e7bSMark Brown 2114b19e6e7bSMark Brown return 0; 2115db2a4165SFrank Mandarino 21162eea392dSJarkko Nikula probe_aux_dev_err: 2117f2ed6b07SMengdong Lin soc_remove_aux_devices(card); 21182eea392dSJarkko Nikula 2119f0fba2adSLiam Girdwood probe_dai_err: 21200671fd8eSKuninori Morimoto soc_remove_dai_links(card); 2121fe3e78e0SMark Brown 2122f0fba2adSLiam Girdwood card_probe_error: 212387506549SMark Brown if (card->remove) 2124e7361ec4SMark Brown card->remove(card); 2125f0fba2adSLiam Girdwood 21262210438bSLars-Peter Clausen snd_soc_dapm_free(&card->dapm); 21270757d834SLars-Peter Clausen soc_cleanup_card_debugfs(card); 2128f0fba2adSLiam Girdwood snd_card_free(card->snd_card); 2129f0fba2adSLiam Girdwood 2130b19e6e7bSMark Brown base_error: 21311a497983SMengdong Lin soc_remove_pcm_runtimes(card); 2132f0fba2adSLiam Girdwood mutex_unlock(&card->mutex); 213334e81ab4SLars-Peter Clausen mutex_unlock(&client_mutex); 2134db2a4165SFrank Mandarino 2135b19e6e7bSMark Brown return ret; 2136435c5e25SMark Brown } 2137435c5e25SMark Brown 2138435c5e25SMark Brown /* probes a new socdev */ 2139435c5e25SMark Brown static int soc_probe(struct platform_device *pdev) 2140435c5e25SMark Brown { 2141f0fba2adSLiam Girdwood struct snd_soc_card *card = platform_get_drvdata(pdev); 2142435c5e25SMark Brown 214370a7ca34SVinod Koul /* 214470a7ca34SVinod Koul * no card, so machine driver should be registering card 214570a7ca34SVinod Koul * we should not be here in that case so ret error 214670a7ca34SVinod Koul */ 214770a7ca34SVinod Koul if (!card) 214870a7ca34SVinod Koul return -EINVAL; 214970a7ca34SVinod Koul 2150fe4085e8SMark Brown dev_warn(&pdev->dev, 2151f110bfc7SLiam Girdwood "ASoC: machine %s should use snd_soc_register_card()\n", 2152fe4085e8SMark Brown card->name); 2153fe4085e8SMark Brown 2154435c5e25SMark Brown /* Bodge while we unpick instantiation */ 2155435c5e25SMark Brown card->dev = &pdev->dev; 2156f0fba2adSLiam Girdwood 215728d528c8SMark Brown return snd_soc_register_card(card); 2158435c5e25SMark Brown } 2159435c5e25SMark Brown 2160b0e26485SVinod Koul static int soc_cleanup_card_resources(struct snd_soc_card *card) 2161db2a4165SFrank Mandarino { 21621a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd; 2163db2a4165SFrank Mandarino 2164f0fba2adSLiam Girdwood /* make sure any delayed work runs */ 21651a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) 216643829731STejun Heo flush_delayed_work(&rtd->delayed_work); 2167db2a4165SFrank Mandarino 21684efda5f2STakashi Iwai /* free the ALSA card at first; this syncs with pending operations */ 21694efda5f2STakashi Iwai snd_card_free(card->snd_card); 21704efda5f2STakashi Iwai 2171f0fba2adSLiam Girdwood /* remove and free each DAI */ 21720671fd8eSKuninori Morimoto soc_remove_dai_links(card); 21731a497983SMengdong Lin soc_remove_pcm_runtimes(card); 2174f0fba2adSLiam Girdwood 2175f2ed6b07SMengdong Lin /* remove auxiliary devices */ 2176f2ed6b07SMengdong Lin soc_remove_aux_devices(card); 2177f2ed6b07SMengdong Lin 2178d1e81428SMark Brown snd_soc_dapm_free(&card->dapm); 2179a6052154SJarkko Nikula soc_cleanup_card_debugfs(card); 2180a6052154SJarkko Nikula 2181f0fba2adSLiam Girdwood /* remove the card */ 218287506549SMark Brown if (card->remove) 2183e7361ec4SMark Brown card->remove(card); 2184f0fba2adSLiam Girdwood 2185b0e26485SVinod Koul return 0; 2186b2dfa62cSGuennadi Liakhovetski } 2187b0e26485SVinod Koul 2188b0e26485SVinod Koul /* removes a socdev */ 2189b0e26485SVinod Koul static int soc_remove(struct platform_device *pdev) 2190b0e26485SVinod Koul { 2191b0e26485SVinod Koul struct snd_soc_card *card = platform_get_drvdata(pdev); 2192b0e26485SVinod Koul 2193c5af3a2eSMark Brown snd_soc_unregister_card(card); 2194db2a4165SFrank Mandarino return 0; 2195db2a4165SFrank Mandarino } 2196db2a4165SFrank Mandarino 21976f8ab4acSMark Brown int snd_soc_poweroff(struct device *dev) 219851737470SMark Brown { 21996f8ab4acSMark Brown struct snd_soc_card *card = dev_get_drvdata(dev); 22001a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd; 220151737470SMark Brown 220251737470SMark Brown if (!card->instantiated) 2203416356fcSMark Brown return 0; 220451737470SMark Brown 220551737470SMark Brown /* Flush out pmdown_time work - we actually do want to run it 220651737470SMark Brown * now, we're shutting down so no imminent restart. */ 22071a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) 220843829731STejun Heo flush_delayed_work(&rtd->delayed_work); 220951737470SMark Brown 2210f0fba2adSLiam Girdwood snd_soc_dapm_shutdown(card); 2211416356fcSMark Brown 2212988e8cc4SNicolin Chen /* deactivate pins to sleep state */ 22131a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 221488bd870fSBenoit Cousson struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 22151a497983SMengdong Lin int i; 221688bd870fSBenoit Cousson 2217988e8cc4SNicolin Chen pinctrl_pm_select_sleep_state(cpu_dai->dev); 22181a497983SMengdong Lin for (i = 0; i < rtd->num_codecs; i++) { 22191a497983SMengdong Lin struct snd_soc_dai *codec_dai = rtd->codec_dais[i]; 222088bd870fSBenoit Cousson pinctrl_pm_select_sleep_state(codec_dai->dev); 222188bd870fSBenoit Cousson } 2222988e8cc4SNicolin Chen } 2223988e8cc4SNicolin Chen 2224416356fcSMark Brown return 0; 222551737470SMark Brown } 22266f8ab4acSMark Brown EXPORT_SYMBOL_GPL(snd_soc_poweroff); 222751737470SMark Brown 22286f8ab4acSMark Brown const struct dev_pm_ops snd_soc_pm_ops = { 2229b1dd5897SViresh Kumar .suspend = snd_soc_suspend, 2230b1dd5897SViresh Kumar .resume = snd_soc_resume, 2231b1dd5897SViresh Kumar .freeze = snd_soc_suspend, 2232b1dd5897SViresh Kumar .thaw = snd_soc_resume, 22336f8ab4acSMark Brown .poweroff = snd_soc_poweroff, 2234b1dd5897SViresh Kumar .restore = snd_soc_resume, 2235416356fcSMark Brown }; 2236deb2607eSStephen Warren EXPORT_SYMBOL_GPL(snd_soc_pm_ops); 2237416356fcSMark Brown 2238db2a4165SFrank Mandarino /* ASoC platform driver */ 2239db2a4165SFrank Mandarino static struct platform_driver soc_driver = { 2240db2a4165SFrank Mandarino .driver = { 2241db2a4165SFrank Mandarino .name = "soc-audio", 22426f8ab4acSMark Brown .pm = &snd_soc_pm_ops, 2243db2a4165SFrank Mandarino }, 2244db2a4165SFrank Mandarino .probe = soc_probe, 2245db2a4165SFrank Mandarino .remove = soc_remove, 2246db2a4165SFrank Mandarino }; 2247db2a4165SFrank Mandarino 2248096e49d5SMark Brown /** 2249db2a4165SFrank Mandarino * snd_soc_cnew - create new control 2250db2a4165SFrank Mandarino * @_template: control template 2251db2a4165SFrank Mandarino * @data: control private data 2252ac11a2b3SMark Brown * @long_name: control long name 2253efb7ac3fSMark Brown * @prefix: control name prefix 2254db2a4165SFrank Mandarino * 2255db2a4165SFrank Mandarino * Create a new mixer control from a template control. 2256db2a4165SFrank Mandarino * 2257db2a4165SFrank Mandarino * Returns 0 for success, else error. 2258db2a4165SFrank Mandarino */ 2259db2a4165SFrank Mandarino struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template, 22603056557fSMark Brown void *data, const char *long_name, 2261efb7ac3fSMark Brown const char *prefix) 2262db2a4165SFrank Mandarino { 2263db2a4165SFrank Mandarino struct snd_kcontrol_new template; 2264efb7ac3fSMark Brown struct snd_kcontrol *kcontrol; 2265efb7ac3fSMark Brown char *name = NULL; 2266db2a4165SFrank Mandarino 2267db2a4165SFrank Mandarino memcpy(&template, _template, sizeof(template)); 2268db2a4165SFrank Mandarino template.index = 0; 2269db2a4165SFrank Mandarino 2270efb7ac3fSMark Brown if (!long_name) 2271efb7ac3fSMark Brown long_name = template.name; 2272efb7ac3fSMark Brown 2273efb7ac3fSMark Brown if (prefix) { 22742b581074SLars-Peter Clausen name = kasprintf(GFP_KERNEL, "%s %s", prefix, long_name); 2275efb7ac3fSMark Brown if (!name) 2276efb7ac3fSMark Brown return NULL; 2277efb7ac3fSMark Brown 2278efb7ac3fSMark Brown template.name = name; 2279efb7ac3fSMark Brown } else { 2280efb7ac3fSMark Brown template.name = long_name; 2281efb7ac3fSMark Brown } 2282efb7ac3fSMark Brown 2283efb7ac3fSMark Brown kcontrol = snd_ctl_new1(&template, data); 2284efb7ac3fSMark Brown 2285efb7ac3fSMark Brown kfree(name); 2286efb7ac3fSMark Brown 2287efb7ac3fSMark Brown return kcontrol; 2288db2a4165SFrank Mandarino } 2289db2a4165SFrank Mandarino EXPORT_SYMBOL_GPL(snd_soc_cnew); 2290db2a4165SFrank Mandarino 2291022658beSLiam Girdwood static int snd_soc_add_controls(struct snd_card *card, struct device *dev, 2292022658beSLiam Girdwood const struct snd_kcontrol_new *controls, int num_controls, 2293022658beSLiam Girdwood const char *prefix, void *data) 2294022658beSLiam Girdwood { 2295022658beSLiam Girdwood int err, i; 2296022658beSLiam Girdwood 2297022658beSLiam Girdwood for (i = 0; i < num_controls; i++) { 2298022658beSLiam Girdwood const struct snd_kcontrol_new *control = &controls[i]; 2299022658beSLiam Girdwood err = snd_ctl_add(card, snd_soc_cnew(control, data, 2300022658beSLiam Girdwood control->name, prefix)); 2301022658beSLiam Girdwood if (err < 0) { 2302f110bfc7SLiam Girdwood dev_err(dev, "ASoC: Failed to add %s: %d\n", 2303f110bfc7SLiam Girdwood control->name, err); 2304022658beSLiam Girdwood return err; 2305022658beSLiam Girdwood } 2306022658beSLiam Girdwood } 2307022658beSLiam Girdwood 2308022658beSLiam Girdwood return 0; 2309022658beSLiam Girdwood } 2310022658beSLiam Girdwood 23114fefd698SDimitris Papastamos struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card, 23124fefd698SDimitris Papastamos const char *name) 23134fefd698SDimitris Papastamos { 23144fefd698SDimitris Papastamos struct snd_card *card = soc_card->snd_card; 23154fefd698SDimitris Papastamos struct snd_kcontrol *kctl; 23164fefd698SDimitris Papastamos 23174fefd698SDimitris Papastamos if (unlikely(!name)) 23184fefd698SDimitris Papastamos return NULL; 23194fefd698SDimitris Papastamos 23204fefd698SDimitris Papastamos list_for_each_entry(kctl, &card->controls, list) 23214fefd698SDimitris Papastamos if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) 23224fefd698SDimitris Papastamos return kctl; 23234fefd698SDimitris Papastamos return NULL; 23244fefd698SDimitris Papastamos } 23254fefd698SDimitris Papastamos EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol); 23264fefd698SDimitris Papastamos 2327db2a4165SFrank Mandarino /** 23280f2780adSLars-Peter Clausen * snd_soc_add_component_controls - Add an array of controls to a component. 23290f2780adSLars-Peter Clausen * 23300f2780adSLars-Peter Clausen * @component: Component to add controls to 23310f2780adSLars-Peter Clausen * @controls: Array of controls to add 23320f2780adSLars-Peter Clausen * @num_controls: Number of elements in the array 23330f2780adSLars-Peter Clausen * 23340f2780adSLars-Peter Clausen * Return: 0 for success, else error. 23350f2780adSLars-Peter Clausen */ 23360f2780adSLars-Peter Clausen int snd_soc_add_component_controls(struct snd_soc_component *component, 23370f2780adSLars-Peter Clausen const struct snd_kcontrol_new *controls, unsigned int num_controls) 23380f2780adSLars-Peter Clausen { 23390f2780adSLars-Peter Clausen struct snd_card *card = component->card->snd_card; 23400f2780adSLars-Peter Clausen 23410f2780adSLars-Peter Clausen return snd_soc_add_controls(card, component->dev, controls, 23420f2780adSLars-Peter Clausen num_controls, component->name_prefix, component); 23430f2780adSLars-Peter Clausen } 23440f2780adSLars-Peter Clausen EXPORT_SYMBOL_GPL(snd_soc_add_component_controls); 23450f2780adSLars-Peter Clausen 23460f2780adSLars-Peter Clausen /** 2347022658beSLiam Girdwood * snd_soc_add_card_controls - add an array of controls to a SoC card. 2348022658beSLiam Girdwood * Convenience function to add a list of controls. 2349022658beSLiam Girdwood * 2350022658beSLiam Girdwood * @soc_card: SoC card to add controls to 2351022658beSLiam Girdwood * @controls: array of controls to add 2352022658beSLiam Girdwood * @num_controls: number of elements in the array 2353022658beSLiam Girdwood * 2354022658beSLiam Girdwood * Return 0 for success, else error. 2355022658beSLiam Girdwood */ 2356022658beSLiam Girdwood int snd_soc_add_card_controls(struct snd_soc_card *soc_card, 2357022658beSLiam Girdwood const struct snd_kcontrol_new *controls, int num_controls) 2358022658beSLiam Girdwood { 2359022658beSLiam Girdwood struct snd_card *card = soc_card->snd_card; 2360022658beSLiam Girdwood 2361022658beSLiam Girdwood return snd_soc_add_controls(card, soc_card->dev, controls, num_controls, 2362022658beSLiam Girdwood NULL, soc_card); 2363022658beSLiam Girdwood } 2364022658beSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_add_card_controls); 2365022658beSLiam Girdwood 2366022658beSLiam Girdwood /** 2367022658beSLiam Girdwood * snd_soc_add_dai_controls - add an array of controls to a DAI. 2368022658beSLiam Girdwood * Convienience function to add a list of controls. 2369022658beSLiam Girdwood * 2370022658beSLiam Girdwood * @dai: DAI to add controls to 2371022658beSLiam Girdwood * @controls: array of controls to add 2372022658beSLiam Girdwood * @num_controls: number of elements in the array 2373022658beSLiam Girdwood * 2374022658beSLiam Girdwood * Return 0 for success, else error. 2375022658beSLiam Girdwood */ 2376022658beSLiam Girdwood int snd_soc_add_dai_controls(struct snd_soc_dai *dai, 2377022658beSLiam Girdwood const struct snd_kcontrol_new *controls, int num_controls) 2378022658beSLiam Girdwood { 2379313665b9SLars-Peter Clausen struct snd_card *card = dai->component->card->snd_card; 2380022658beSLiam Girdwood 2381022658beSLiam Girdwood return snd_soc_add_controls(card, dai->dev, controls, num_controls, 2382022658beSLiam Girdwood NULL, dai); 2383022658beSLiam Girdwood } 2384022658beSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_add_dai_controls); 2385022658beSLiam Girdwood 2386022658beSLiam Girdwood /** 23878c6529dbSLiam Girdwood * snd_soc_dai_set_sysclk - configure DAI system or master clock. 23888c6529dbSLiam Girdwood * @dai: DAI 23898c6529dbSLiam Girdwood * @clk_id: DAI specific clock ID 23908c6529dbSLiam Girdwood * @freq: new clock frequency in Hz 23918c6529dbSLiam Girdwood * @dir: new clock direction - input/output. 23928c6529dbSLiam Girdwood * 23938c6529dbSLiam Girdwood * Configures the DAI master (MCLK) or system (SYSCLK) clocking. 23948c6529dbSLiam Girdwood */ 23958c6529dbSLiam Girdwood int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, 23968c6529dbSLiam Girdwood unsigned int freq, int dir) 23978c6529dbSLiam Girdwood { 239846471925SKuninori Morimoto if (dai->driver->ops->set_sysclk) 2399f0fba2adSLiam Girdwood return dai->driver->ops->set_sysclk(dai, clk_id, freq, dir); 240071ccef0dSKuninori Morimoto 240171ccef0dSKuninori Morimoto return snd_soc_component_set_sysclk(dai->component, clk_id, 0, 2402ec4ee52aSMark Brown freq, dir); 24038c6529dbSLiam Girdwood } 24048c6529dbSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk); 24058c6529dbSLiam Girdwood 24068c6529dbSLiam Girdwood /** 240771ccef0dSKuninori Morimoto * snd_soc_component_set_sysclk - configure COMPONENT system or master clock. 240871ccef0dSKuninori Morimoto * @component: COMPONENT 240971ccef0dSKuninori Morimoto * @clk_id: DAI specific clock ID 241071ccef0dSKuninori Morimoto * @source: Source for the clock 241171ccef0dSKuninori Morimoto * @freq: new clock frequency in Hz 241271ccef0dSKuninori Morimoto * @dir: new clock direction - input/output. 241371ccef0dSKuninori Morimoto * 241471ccef0dSKuninori Morimoto * Configures the CODEC master (MCLK) or system (SYSCLK) clocking. 241571ccef0dSKuninori Morimoto */ 241671ccef0dSKuninori Morimoto int snd_soc_component_set_sysclk(struct snd_soc_component *component, int clk_id, 241771ccef0dSKuninori Morimoto int source, unsigned int freq, int dir) 241871ccef0dSKuninori Morimoto { 241971ccef0dSKuninori Morimoto if (component->driver->set_sysclk) 242071ccef0dSKuninori Morimoto return component->driver->set_sysclk(component, clk_id, source, 242171ccef0dSKuninori Morimoto freq, dir); 242271ccef0dSKuninori Morimoto 242371ccef0dSKuninori Morimoto return -ENOTSUPP; 242471ccef0dSKuninori Morimoto } 242571ccef0dSKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_component_set_sysclk); 242671ccef0dSKuninori Morimoto 242771ccef0dSKuninori Morimoto /** 24288c6529dbSLiam Girdwood * snd_soc_dai_set_clkdiv - configure DAI clock dividers. 24298c6529dbSLiam Girdwood * @dai: DAI 2430ac11a2b3SMark Brown * @div_id: DAI specific clock divider ID 24318c6529dbSLiam Girdwood * @div: new clock divisor. 24328c6529dbSLiam Girdwood * 24338c6529dbSLiam Girdwood * Configures the clock dividers. This is used to derive the best DAI bit and 24348c6529dbSLiam Girdwood * frame clocks from the system or master clock. It's best to set the DAI bit 24358c6529dbSLiam Girdwood * and frame clocks as low as possible to save system power. 24368c6529dbSLiam Girdwood */ 24378c6529dbSLiam Girdwood int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai, 24388c6529dbSLiam Girdwood int div_id, int div) 24398c6529dbSLiam Girdwood { 244046471925SKuninori Morimoto if (dai->driver->ops->set_clkdiv) 2441f0fba2adSLiam Girdwood return dai->driver->ops->set_clkdiv(dai, div_id, div); 24428c6529dbSLiam Girdwood else 24438c6529dbSLiam Girdwood return -EINVAL; 24448c6529dbSLiam Girdwood } 24458c6529dbSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv); 24468c6529dbSLiam Girdwood 24478c6529dbSLiam Girdwood /** 24488c6529dbSLiam Girdwood * snd_soc_dai_set_pll - configure DAI PLL. 24498c6529dbSLiam Girdwood * @dai: DAI 24508c6529dbSLiam Girdwood * @pll_id: DAI specific PLL ID 245185488037SMark Brown * @source: DAI specific source for the PLL 24528c6529dbSLiam Girdwood * @freq_in: PLL input clock frequency in Hz 24538c6529dbSLiam Girdwood * @freq_out: requested PLL output clock frequency in Hz 24548c6529dbSLiam Girdwood * 24558c6529dbSLiam Girdwood * Configures and enables PLL to generate output clock based on input clock. 24568c6529dbSLiam Girdwood */ 245785488037SMark Brown int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source, 245885488037SMark Brown unsigned int freq_in, unsigned int freq_out) 24598c6529dbSLiam Girdwood { 246046471925SKuninori Morimoto if (dai->driver->ops->set_pll) 2461f0fba2adSLiam Girdwood return dai->driver->ops->set_pll(dai, pll_id, source, 246285488037SMark Brown freq_in, freq_out); 2463ef641e5dSKuninori Morimoto 2464ef641e5dSKuninori Morimoto return snd_soc_component_set_pll(dai->component, pll_id, source, 2465ec4ee52aSMark Brown freq_in, freq_out); 24668c6529dbSLiam Girdwood } 24678c6529dbSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll); 24688c6529dbSLiam Girdwood 2469ec4ee52aSMark Brown /* 2470ef641e5dSKuninori Morimoto * snd_soc_component_set_pll - configure component PLL. 2471ef641e5dSKuninori Morimoto * @component: COMPONENT 2472ef641e5dSKuninori Morimoto * @pll_id: DAI specific PLL ID 2473ef641e5dSKuninori Morimoto * @source: DAI specific source for the PLL 2474ef641e5dSKuninori Morimoto * @freq_in: PLL input clock frequency in Hz 2475ef641e5dSKuninori Morimoto * @freq_out: requested PLL output clock frequency in Hz 2476ef641e5dSKuninori Morimoto * 2477ef641e5dSKuninori Morimoto * Configures and enables PLL to generate output clock based on input clock. 2478ef641e5dSKuninori Morimoto */ 2479ef641e5dSKuninori Morimoto int snd_soc_component_set_pll(struct snd_soc_component *component, int pll_id, 2480ef641e5dSKuninori Morimoto int source, unsigned int freq_in, 2481ef641e5dSKuninori Morimoto unsigned int freq_out) 2482ef641e5dSKuninori Morimoto { 2483ef641e5dSKuninori Morimoto if (component->driver->set_pll) 2484ef641e5dSKuninori Morimoto return component->driver->set_pll(component, pll_id, source, 2485ef641e5dSKuninori Morimoto freq_in, freq_out); 2486ef641e5dSKuninori Morimoto 2487ef641e5dSKuninori Morimoto return -EINVAL; 2488ef641e5dSKuninori Morimoto } 2489ef641e5dSKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_component_set_pll); 2490ef641e5dSKuninori Morimoto 24918c6529dbSLiam Girdwood /** 2492e54cf76bSLiam Girdwood * snd_soc_dai_set_bclk_ratio - configure BCLK to sample rate ratio. 2493e54cf76bSLiam Girdwood * @dai: DAI 2494231b86b1SMasanari Iida * @ratio: Ratio of BCLK to Sample rate. 2495e54cf76bSLiam Girdwood * 2496e54cf76bSLiam Girdwood * Configures the DAI for a preset BCLK to sample rate ratio. 2497e54cf76bSLiam Girdwood */ 2498e54cf76bSLiam Girdwood int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) 2499e54cf76bSLiam Girdwood { 250046471925SKuninori Morimoto if (dai->driver->ops->set_bclk_ratio) 2501e54cf76bSLiam Girdwood return dai->driver->ops->set_bclk_ratio(dai, ratio); 2502e54cf76bSLiam Girdwood else 2503e54cf76bSLiam Girdwood return -EINVAL; 2504e54cf76bSLiam Girdwood } 2505e54cf76bSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dai_set_bclk_ratio); 2506e54cf76bSLiam Girdwood 2507e54cf76bSLiam Girdwood /** 25088c6529dbSLiam Girdwood * snd_soc_dai_set_fmt - configure DAI hardware audio format. 25098c6529dbSLiam Girdwood * @dai: DAI 2510bb19ba2aSRandy Dunlap * @fmt: SND_SOC_DAIFMT_* format value. 25118c6529dbSLiam Girdwood * 25128c6529dbSLiam Girdwood * Configures the DAI hardware format and clocking. 25138c6529dbSLiam Girdwood */ 25148c6529dbSLiam Girdwood int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 25158c6529dbSLiam Girdwood { 25165e4ba569SShawn Guo if (dai->driver == NULL) 25178c6529dbSLiam Girdwood return -EINVAL; 25185e4ba569SShawn Guo if (dai->driver->ops->set_fmt == NULL) 25195e4ba569SShawn Guo return -ENOTSUPP; 25205e4ba569SShawn Guo return dai->driver->ops->set_fmt(dai, fmt); 25218c6529dbSLiam Girdwood } 25228c6529dbSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt); 25238c6529dbSLiam Girdwood 25248c6529dbSLiam Girdwood /** 2525e5c21514SXiubo Li * snd_soc_xlate_tdm_slot - generate tx/rx slot mask. 252689c67857SXiubo Li * @slots: Number of slots in use. 252789c67857SXiubo Li * @tx_mask: bitmask representing active TX slots. 252889c67857SXiubo Li * @rx_mask: bitmask representing active RX slots. 252989c67857SXiubo Li * 253089c67857SXiubo Li * Generates the TDM tx and rx slot default masks for DAI. 253189c67857SXiubo Li */ 2532e5c21514SXiubo Li static int snd_soc_xlate_tdm_slot_mask(unsigned int slots, 253389c67857SXiubo Li unsigned int *tx_mask, 253489c67857SXiubo Li unsigned int *rx_mask) 253589c67857SXiubo Li { 253689c67857SXiubo Li if (*tx_mask || *rx_mask) 253789c67857SXiubo Li return 0; 253889c67857SXiubo Li 253989c67857SXiubo Li if (!slots) 254089c67857SXiubo Li return -EINVAL; 254189c67857SXiubo Li 254289c67857SXiubo Li *tx_mask = (1 << slots) - 1; 254389c67857SXiubo Li *rx_mask = (1 << slots) - 1; 254489c67857SXiubo Li 254589c67857SXiubo Li return 0; 254689c67857SXiubo Li } 254789c67857SXiubo Li 254889c67857SXiubo Li /** 2549e46c9366SLars-Peter Clausen * snd_soc_dai_set_tdm_slot() - Configures a DAI for TDM operation 2550e46c9366SLars-Peter Clausen * @dai: The DAI to configure 2551a5479e38SDaniel Ribeiro * @tx_mask: bitmask representing active TX slots. 2552a5479e38SDaniel Ribeiro * @rx_mask: bitmask representing active RX slots. 25538c6529dbSLiam Girdwood * @slots: Number of slots in use. 2554a5479e38SDaniel Ribeiro * @slot_width: Width in bits for each slot. 25558c6529dbSLiam Girdwood * 2556e46c9366SLars-Peter Clausen * This function configures the specified DAI for TDM operation. @slot contains 2557e46c9366SLars-Peter Clausen * the total number of slots of the TDM stream and @slot_with the width of each 2558e46c9366SLars-Peter Clausen * slot in bit clock cycles. @tx_mask and @rx_mask are bitmasks specifying the 2559e46c9366SLars-Peter Clausen * active slots of the TDM stream for the specified DAI, i.e. which slots the 2560e46c9366SLars-Peter Clausen * DAI should write to or read from. If a bit is set the corresponding slot is 2561e46c9366SLars-Peter Clausen * active, if a bit is cleared the corresponding slot is inactive. Bit 0 maps to 2562e46c9366SLars-Peter Clausen * the first slot, bit 1 to the second slot and so on. The first active slot 2563e46c9366SLars-Peter Clausen * maps to the first channel of the DAI, the second active slot to the second 2564e46c9366SLars-Peter Clausen * channel and so on. 2565e46c9366SLars-Peter Clausen * 2566e46c9366SLars-Peter Clausen * TDM mode can be disabled by passing 0 for @slots. In this case @tx_mask, 2567e46c9366SLars-Peter Clausen * @rx_mask and @slot_width will be ignored. 2568e46c9366SLars-Peter Clausen * 2569e46c9366SLars-Peter Clausen * Returns 0 on success, a negative error code otherwise. 25708c6529dbSLiam Girdwood */ 25718c6529dbSLiam Girdwood int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, 2572a5479e38SDaniel Ribeiro unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) 25738c6529dbSLiam Girdwood { 257446471925SKuninori Morimoto if (dai->driver->ops->xlate_tdm_slot_mask) 2575e5c21514SXiubo Li dai->driver->ops->xlate_tdm_slot_mask(slots, 257689c67857SXiubo Li &tx_mask, &rx_mask); 257789c67857SXiubo Li else 2578e5c21514SXiubo Li snd_soc_xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask); 257989c67857SXiubo Li 258088bd870fSBenoit Cousson dai->tx_mask = tx_mask; 258188bd870fSBenoit Cousson dai->rx_mask = rx_mask; 258288bd870fSBenoit Cousson 258346471925SKuninori Morimoto if (dai->driver->ops->set_tdm_slot) 2584f0fba2adSLiam Girdwood return dai->driver->ops->set_tdm_slot(dai, tx_mask, rx_mask, 2585a5479e38SDaniel Ribeiro slots, slot_width); 25868c6529dbSLiam Girdwood else 2587b2cbb6e1SXiubo Li return -ENOTSUPP; 25888c6529dbSLiam Girdwood } 25898c6529dbSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot); 25908c6529dbSLiam Girdwood 25918c6529dbSLiam Girdwood /** 2592472df3cbSBarry Song * snd_soc_dai_set_channel_map - configure DAI audio channel map 2593472df3cbSBarry Song * @dai: DAI 2594472df3cbSBarry Song * @tx_num: how many TX channels 2595472df3cbSBarry Song * @tx_slot: pointer to an array which imply the TX slot number channel 2596472df3cbSBarry Song * 0~num-1 uses 2597472df3cbSBarry Song * @rx_num: how many RX channels 2598472df3cbSBarry Song * @rx_slot: pointer to an array which imply the RX slot number channel 2599472df3cbSBarry Song * 0~num-1 uses 2600472df3cbSBarry Song * 2601472df3cbSBarry Song * configure the relationship between channel number and TDM slot number. 2602472df3cbSBarry Song */ 2603472df3cbSBarry Song int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai, 2604472df3cbSBarry Song unsigned int tx_num, unsigned int *tx_slot, 2605472df3cbSBarry Song unsigned int rx_num, unsigned int *rx_slot) 2606472df3cbSBarry Song { 260746471925SKuninori Morimoto if (dai->driver->ops->set_channel_map) 2608f0fba2adSLiam Girdwood return dai->driver->ops->set_channel_map(dai, tx_num, tx_slot, 2609472df3cbSBarry Song rx_num, rx_slot); 2610472df3cbSBarry Song else 2611472df3cbSBarry Song return -EINVAL; 2612472df3cbSBarry Song } 2613472df3cbSBarry Song EXPORT_SYMBOL_GPL(snd_soc_dai_set_channel_map); 2614472df3cbSBarry Song 2615472df3cbSBarry Song /** 26168c6529dbSLiam Girdwood * snd_soc_dai_set_tristate - configure DAI system or master clock. 26178c6529dbSLiam Girdwood * @dai: DAI 26188c6529dbSLiam Girdwood * @tristate: tristate enable 26198c6529dbSLiam Girdwood * 26208c6529dbSLiam Girdwood * Tristates the DAI so that others can use it. 26218c6529dbSLiam Girdwood */ 26228c6529dbSLiam Girdwood int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate) 26238c6529dbSLiam Girdwood { 262446471925SKuninori Morimoto if (dai->driver->ops->set_tristate) 2625f0fba2adSLiam Girdwood return dai->driver->ops->set_tristate(dai, tristate); 26268c6529dbSLiam Girdwood else 26278c6529dbSLiam Girdwood return -EINVAL; 26288c6529dbSLiam Girdwood } 26298c6529dbSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dai_set_tristate); 26308c6529dbSLiam Girdwood 26318c6529dbSLiam Girdwood /** 26328c6529dbSLiam Girdwood * snd_soc_dai_digital_mute - configure DAI system or master clock. 26338c6529dbSLiam Girdwood * @dai: DAI 26348c6529dbSLiam Girdwood * @mute: mute enable 2635da18396fSMark Brown * @direction: stream to mute 26368c6529dbSLiam Girdwood * 26378c6529dbSLiam Girdwood * Mutes the DAI DAC. 26388c6529dbSLiam Girdwood */ 2639da18396fSMark Brown int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute, 2640da18396fSMark Brown int direction) 26418c6529dbSLiam Girdwood { 2642da18396fSMark Brown if (!dai->driver) 2643da18396fSMark Brown return -ENOTSUPP; 2644da18396fSMark Brown 2645da18396fSMark Brown if (dai->driver->ops->mute_stream) 2646da18396fSMark Brown return dai->driver->ops->mute_stream(dai, mute, direction); 2647da18396fSMark Brown else if (direction == SNDRV_PCM_STREAM_PLAYBACK && 2648da18396fSMark Brown dai->driver->ops->digital_mute) 2649f0fba2adSLiam Girdwood return dai->driver->ops->digital_mute(dai, mute); 26508c6529dbSLiam Girdwood else 265104570c62SMark Brown return -ENOTSUPP; 26528c6529dbSLiam Girdwood } 26538c6529dbSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute); 26548c6529dbSLiam Girdwood 2655c5af3a2eSMark Brown /** 2656c5af3a2eSMark Brown * snd_soc_register_card - Register a card with the ASoC core 2657c5af3a2eSMark Brown * 2658ac11a2b3SMark Brown * @card: Card to register 2659c5af3a2eSMark Brown * 2660c5af3a2eSMark Brown */ 266170a7ca34SVinod Koul int snd_soc_register_card(struct snd_soc_card *card) 2662c5af3a2eSMark Brown { 2663923c5e61SMengdong Lin int i, ret; 26641a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd; 2665f0fba2adSLiam Girdwood 2666c5af3a2eSMark Brown if (!card->name || !card->dev) 2667c5af3a2eSMark Brown return -EINVAL; 2668c5af3a2eSMark Brown 26695a504963SStephen Warren for (i = 0; i < card->num_links; i++) { 26705a504963SStephen Warren struct snd_soc_dai_link *link = &card->dai_link[i]; 26715a504963SStephen Warren 2672923c5e61SMengdong Lin ret = soc_init_dai_link(card, link); 267388bd870fSBenoit Cousson if (ret) { 2674923c5e61SMengdong Lin dev_err(card->dev, "ASoC: failed to init link %s\n", 2675923c5e61SMengdong Lin link->name); 267688bd870fSBenoit Cousson return ret; 267788bd870fSBenoit Cousson } 26785a504963SStephen Warren } 26795a504963SStephen Warren 2680ed77cc12SMark Brown dev_set_drvdata(card->dev, card); 2681ed77cc12SMark Brown 2682111c6419SStephen Warren snd_soc_initialize_card_lists(card); 2683111c6419SStephen Warren 2684f8f80361SMengdong Lin INIT_LIST_HEAD(&card->dai_link_list); 2685f8f80361SMengdong Lin card->num_dai_links = 0; 2686f0fba2adSLiam Girdwood 26871a497983SMengdong Lin INIT_LIST_HEAD(&card->rtd_list); 26889115171aSMark Brown card->num_rtd = 0; 2689db2a4165SFrank Mandarino 2690db432b41SMark Brown INIT_LIST_HEAD(&card->dapm_dirty); 26918a978234SLiam Girdwood INIT_LIST_HEAD(&card->dobj_list); 2692db2a4165SFrank Mandarino card->instantiated = 0; 2693db2a4165SFrank Mandarino mutex_init(&card->mutex); 2694a73fb2dfSLiam Girdwood mutex_init(&card->dapm_mutex); 2695db2a4165SFrank Mandarino 2696b19e6e7bSMark Brown ret = snd_soc_instantiate_card(card); 2697b19e6e7bSMark Brown if (ret != 0) 26984e2576bdSKuninori Morimoto return ret; 2699db2a4165SFrank Mandarino 2700988e8cc4SNicolin Chen /* deactivate pins to sleep state */ 27011a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 270288bd870fSBenoit Cousson struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 270388bd870fSBenoit Cousson int j; 270488bd870fSBenoit Cousson 270588bd870fSBenoit Cousson for (j = 0; j < rtd->num_codecs; j++) { 270688bd870fSBenoit Cousson struct snd_soc_dai *codec_dai = rtd->codec_dais[j]; 2707988e8cc4SNicolin Chen if (!codec_dai->active) 2708988e8cc4SNicolin Chen pinctrl_pm_select_sleep_state(codec_dai->dev); 270988bd870fSBenoit Cousson } 271088bd870fSBenoit Cousson 2711988e8cc4SNicolin Chen if (!cpu_dai->active) 2712988e8cc4SNicolin Chen pinctrl_pm_select_sleep_state(cpu_dai->dev); 2713988e8cc4SNicolin Chen } 2714988e8cc4SNicolin Chen 2715b19e6e7bSMark Brown return ret; 2716db2a4165SFrank Mandarino } 271770a7ca34SVinod Koul EXPORT_SYMBOL_GPL(snd_soc_register_card); 2718db2a4165SFrank Mandarino 2719db2a4165SFrank Mandarino /** 2720db2a4165SFrank Mandarino * snd_soc_unregister_card - Unregister a card with the ASoC core 2721db2a4165SFrank Mandarino * 2722db2a4165SFrank Mandarino * @card: Card to unregister 2723db2a4165SFrank Mandarino * 2724db2a4165SFrank Mandarino */ 272570a7ca34SVinod Koul int snd_soc_unregister_card(struct snd_soc_card *card) 2726db2a4165SFrank Mandarino { 272701e0df66SLars-Peter Clausen if (card->instantiated) { 272801e0df66SLars-Peter Clausen card->instantiated = false; 27291c325f77SLars-Peter Clausen snd_soc_dapm_shutdown(card); 2730b0e26485SVinod Koul soc_cleanup_card_resources(card); 2731f110bfc7SLiam Girdwood dev_dbg(card->dev, "ASoC: Unregistered card '%s'\n", card->name); 27328f6f9b29SKuninori Morimoto } 2733db2a4165SFrank Mandarino 2734db2a4165SFrank Mandarino return 0; 2735db2a4165SFrank Mandarino } 273670a7ca34SVinod Koul EXPORT_SYMBOL_GPL(snd_soc_unregister_card); 2737db2a4165SFrank Mandarino 2738db2a4165SFrank Mandarino /* 2739db2a4165SFrank Mandarino * Simplify DAI link configuration by removing ".-1" from device names 2740db2a4165SFrank Mandarino * and sanitizing names. 2741db2a4165SFrank Mandarino */ 27420b9a214aSDimitris Papastamos static char *fmt_single_name(struct device *dev, int *id) 2743db2a4165SFrank Mandarino { 2744db2a4165SFrank Mandarino char *found, name[NAME_SIZE]; 2745db2a4165SFrank Mandarino int id1, id2; 2746db2a4165SFrank Mandarino 2747db2a4165SFrank Mandarino if (dev_name(dev) == NULL) 2748db2a4165SFrank Mandarino return NULL; 2749db2a4165SFrank Mandarino 275058818a77SDimitris Papastamos strlcpy(name, dev_name(dev), NAME_SIZE); 2751db2a4165SFrank Mandarino 2752db2a4165SFrank Mandarino /* are we a "%s.%d" name (platform and SPI components) */ 2753c5af3a2eSMark Brown found = strstr(name, dev->driver->name); 2754c5af3a2eSMark Brown if (found) { 2755c5af3a2eSMark Brown /* get ID */ 2756c5af3a2eSMark Brown if (sscanf(&found[strlen(dev->driver->name)], ".%d", id) == 1) { 2757c5af3a2eSMark Brown 2758c5af3a2eSMark Brown /* discard ID from name if ID == -1 */ 2759c5af3a2eSMark Brown if (*id == -1) 2760c5af3a2eSMark Brown found[strlen(dev->driver->name)] = '\0'; 2761c5af3a2eSMark Brown } 2762c5af3a2eSMark Brown 2763c5af3a2eSMark Brown } else { 2764c5af3a2eSMark Brown /* I2C component devices are named "bus-addr" */ 2765c5af3a2eSMark Brown if (sscanf(name, "%x-%x", &id1, &id2) == 2) { 2766c5af3a2eSMark Brown char tmp[NAME_SIZE]; 2767c5af3a2eSMark Brown 2768c5af3a2eSMark Brown /* create unique ID number from I2C addr and bus */ 2769c5af3a2eSMark Brown *id = ((id1 & 0xffff) << 16) + id2; 2770c5af3a2eSMark Brown 2771c5af3a2eSMark Brown /* sanitize component name for DAI link creation */ 2772c5af3a2eSMark Brown snprintf(tmp, NAME_SIZE, "%s.%s", dev->driver->name, name); 277358818a77SDimitris Papastamos strlcpy(name, tmp, NAME_SIZE); 2774c5af3a2eSMark Brown } else 2775c5af3a2eSMark Brown *id = 0; 2776c5af3a2eSMark Brown } 2777c5af3a2eSMark Brown 2778c5af3a2eSMark Brown return kstrdup(name, GFP_KERNEL); 2779c5af3a2eSMark Brown } 2780c5af3a2eSMark Brown 2781c5af3a2eSMark Brown /* 2782c5af3a2eSMark Brown * Simplify DAI link naming for single devices with multiple DAIs by removing 2783c5af3a2eSMark Brown * any ".-1" and using the DAI name (instead of device name). 2784c5af3a2eSMark Brown */ 2785c5af3a2eSMark Brown static inline char *fmt_multiple_name(struct device *dev, 2786c5af3a2eSMark Brown struct snd_soc_dai_driver *dai_drv) 2787c5af3a2eSMark Brown { 2788c5af3a2eSMark Brown if (dai_drv->name == NULL) { 278910e8aa9aSMichał Mirosław dev_err(dev, 279010e8aa9aSMichał Mirosław "ASoC: error - multiple DAI %s registered with no name\n", 279110e8aa9aSMichał Mirosław dev_name(dev)); 2792c5af3a2eSMark Brown return NULL; 2793c5af3a2eSMark Brown } 2794c5af3a2eSMark Brown 2795c5af3a2eSMark Brown return kstrdup(dai_drv->name, GFP_KERNEL); 2796c5af3a2eSMark Brown } 2797c5af3a2eSMark Brown 2798c5af3a2eSMark Brown /** 279932c9ba54SLars-Peter Clausen * snd_soc_unregister_dai - Unregister DAIs from the ASoC core 28009115171aSMark Brown * 280132c9ba54SLars-Peter Clausen * @component: The component for which the DAIs should be unregistered 28029115171aSMark Brown */ 280332c9ba54SLars-Peter Clausen static void snd_soc_unregister_dais(struct snd_soc_component *component) 28049115171aSMark Brown { 28055c1d5f09SLars-Peter Clausen struct snd_soc_dai *dai, *_dai; 28069115171aSMark Brown 28075c1d5f09SLars-Peter Clausen list_for_each_entry_safe(dai, _dai, &component->dai_list, list) { 280832c9ba54SLars-Peter Clausen dev_dbg(component->dev, "ASoC: Unregistered DAI '%s'\n", 280932c9ba54SLars-Peter Clausen dai->name); 28109115171aSMark Brown list_del(&dai->list); 2811f0fba2adSLiam Girdwood kfree(dai->name); 2812f0fba2adSLiam Girdwood kfree(dai); 28139115171aSMark Brown } 281432c9ba54SLars-Peter Clausen } 28159115171aSMark Brown 28165e4fb372SMengdong Lin /* Create a DAI and add it to the component's DAI list */ 28175e4fb372SMengdong Lin static struct snd_soc_dai *soc_add_dai(struct snd_soc_component *component, 28185e4fb372SMengdong Lin struct snd_soc_dai_driver *dai_drv, 28195e4fb372SMengdong Lin bool legacy_dai_naming) 28205e4fb372SMengdong Lin { 28215e4fb372SMengdong Lin struct device *dev = component->dev; 28225e4fb372SMengdong Lin struct snd_soc_dai *dai; 28235e4fb372SMengdong Lin 28245e4fb372SMengdong Lin dev_dbg(dev, "ASoC: dynamically register DAI %s\n", dev_name(dev)); 28255e4fb372SMengdong Lin 28265e4fb372SMengdong Lin dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL); 28275e4fb372SMengdong Lin if (dai == NULL) 28285e4fb372SMengdong Lin return NULL; 28295e4fb372SMengdong Lin 28305e4fb372SMengdong Lin /* 28315e4fb372SMengdong Lin * Back in the old days when we still had component-less DAIs, 28325e4fb372SMengdong Lin * instead of having a static name, component-less DAIs would 28335e4fb372SMengdong Lin * inherit the name of the parent device so it is possible to 28345e4fb372SMengdong Lin * register multiple instances of the DAI. We still need to keep 28355e4fb372SMengdong Lin * the same naming style even though those DAIs are not 28365e4fb372SMengdong Lin * component-less anymore. 28375e4fb372SMengdong Lin */ 28385e4fb372SMengdong Lin if (legacy_dai_naming && 28395e4fb372SMengdong Lin (dai_drv->id == 0 || dai_drv->name == NULL)) { 28405e4fb372SMengdong Lin dai->name = fmt_single_name(dev, &dai->id); 28415e4fb372SMengdong Lin } else { 28425e4fb372SMengdong Lin dai->name = fmt_multiple_name(dev, dai_drv); 28435e4fb372SMengdong Lin if (dai_drv->id) 28445e4fb372SMengdong Lin dai->id = dai_drv->id; 28455e4fb372SMengdong Lin else 28465e4fb372SMengdong Lin dai->id = component->num_dai; 28475e4fb372SMengdong Lin } 28485e4fb372SMengdong Lin if (dai->name == NULL) { 28495e4fb372SMengdong Lin kfree(dai); 28505e4fb372SMengdong Lin return NULL; 28515e4fb372SMengdong Lin } 28525e4fb372SMengdong Lin 28535e4fb372SMengdong Lin dai->component = component; 28545e4fb372SMengdong Lin dai->dev = dev; 28555e4fb372SMengdong Lin dai->driver = dai_drv; 28565e4fb372SMengdong Lin if (!dai->driver->ops) 28575e4fb372SMengdong Lin dai->driver->ops = &null_dai_ops; 28585e4fb372SMengdong Lin 285958bf4179SKuninori Morimoto list_add_tail(&dai->list, &component->dai_list); 28605e4fb372SMengdong Lin component->num_dai++; 28615e4fb372SMengdong Lin 28625e4fb372SMengdong Lin dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name); 28635e4fb372SMengdong Lin return dai; 28645e4fb372SMengdong Lin } 28655e4fb372SMengdong Lin 28669115171aSMark Brown /** 286732c9ba54SLars-Peter Clausen * snd_soc_register_dais - Register a DAI with the ASoC core 28689115171aSMark Brown * 28696106d129SLars-Peter Clausen * @component: The component the DAIs are registered for 28706106d129SLars-Peter Clausen * @dai_drv: DAI driver to use for the DAIs 2871ac11a2b3SMark Brown * @count: Number of DAIs 287232c9ba54SLars-Peter Clausen * @legacy_dai_naming: Use the legacy naming scheme and let the DAI inherit the 287332c9ba54SLars-Peter Clausen * parent's name. 28749115171aSMark Brown */ 287532c9ba54SLars-Peter Clausen static int snd_soc_register_dais(struct snd_soc_component *component, 28760e7b25c6SKuninori Morimoto struct snd_soc_dai_driver *dai_drv, size_t count) 28779115171aSMark Brown { 28786106d129SLars-Peter Clausen struct device *dev = component->dev; 2879f0fba2adSLiam Girdwood struct snd_soc_dai *dai; 288032c9ba54SLars-Peter Clausen unsigned int i; 288132c9ba54SLars-Peter Clausen int ret; 2882f0fba2adSLiam Girdwood 28835b5e0928SAlexey Dobriyan dev_dbg(dev, "ASoC: dai register %s #%zu\n", dev_name(dev), count); 28849115171aSMark Brown 28859115171aSMark Brown for (i = 0; i < count; i++) { 2886f0fba2adSLiam Girdwood 28875e4fb372SMengdong Lin dai = soc_add_dai(component, dai_drv + i, 28880e7b25c6SKuninori Morimoto count == 1 && !component->driver->non_legacy_dai_naming); 2889c46e0079SAxel Lin if (dai == NULL) { 2890c46e0079SAxel Lin ret = -ENOMEM; 2891c46e0079SAxel Lin goto err; 2892c46e0079SAxel Lin } 2893f0fba2adSLiam Girdwood } 2894f0fba2adSLiam Girdwood 28959115171aSMark Brown return 0; 28969115171aSMark Brown 28979115171aSMark Brown err: 289832c9ba54SLars-Peter Clausen snd_soc_unregister_dais(component); 28999115171aSMark Brown 29009115171aSMark Brown return ret; 29019115171aSMark Brown } 29029115171aSMark Brown 290368003e6cSMengdong Lin /** 290468003e6cSMengdong Lin * snd_soc_register_dai - Register a DAI dynamically & create its widgets 290568003e6cSMengdong Lin * 290668003e6cSMengdong Lin * @component: The component the DAIs are registered for 290768003e6cSMengdong Lin * @dai_drv: DAI driver to use for the DAI 290868003e6cSMengdong Lin * 290968003e6cSMengdong Lin * Topology can use this API to register DAIs when probing a component. 291068003e6cSMengdong Lin * These DAIs's widgets will be freed in the card cleanup and the DAIs 291168003e6cSMengdong Lin * will be freed in the component cleanup. 291268003e6cSMengdong Lin */ 291368003e6cSMengdong Lin int snd_soc_register_dai(struct snd_soc_component *component, 291468003e6cSMengdong Lin struct snd_soc_dai_driver *dai_drv) 291568003e6cSMengdong Lin { 291668003e6cSMengdong Lin struct snd_soc_dapm_context *dapm = 291768003e6cSMengdong Lin snd_soc_component_get_dapm(component); 291868003e6cSMengdong Lin struct snd_soc_dai *dai; 291968003e6cSMengdong Lin int ret; 292068003e6cSMengdong Lin 292168003e6cSMengdong Lin if (dai_drv->dobj.type != SND_SOC_DOBJ_PCM) { 292268003e6cSMengdong Lin dev_err(component->dev, "Invalid dai type %d\n", 292368003e6cSMengdong Lin dai_drv->dobj.type); 292468003e6cSMengdong Lin return -EINVAL; 292568003e6cSMengdong Lin } 292668003e6cSMengdong Lin 292768003e6cSMengdong Lin lockdep_assert_held(&client_mutex); 292868003e6cSMengdong Lin dai = soc_add_dai(component, dai_drv, false); 292968003e6cSMengdong Lin if (!dai) 293068003e6cSMengdong Lin return -ENOMEM; 293168003e6cSMengdong Lin 293268003e6cSMengdong Lin /* Create the DAI widgets here. After adding DAIs, topology may 293368003e6cSMengdong Lin * also add routes that need these widgets as source or sink. 293468003e6cSMengdong Lin */ 293568003e6cSMengdong Lin ret = snd_soc_dapm_new_dai_widgets(dapm, dai); 293668003e6cSMengdong Lin if (ret != 0) { 293768003e6cSMengdong Lin dev_err(component->dev, 293868003e6cSMengdong Lin "Failed to create DAI widgets %d\n", ret); 293968003e6cSMengdong Lin } 294068003e6cSMengdong Lin 294168003e6cSMengdong Lin return ret; 294268003e6cSMengdong Lin } 294368003e6cSMengdong Lin EXPORT_SYMBOL_GPL(snd_soc_register_dai); 294468003e6cSMengdong Lin 294514e8bdebSLars-Peter Clausen static void snd_soc_component_seq_notifier(struct snd_soc_dapm_context *dapm, 294614e8bdebSLars-Peter Clausen enum snd_soc_dapm_type type, int subseq) 2947d191bd8dSKuninori Morimoto { 294814e8bdebSLars-Peter Clausen struct snd_soc_component *component = dapm->component; 2949d191bd8dSKuninori Morimoto 295014e8bdebSLars-Peter Clausen component->driver->seq_notifier(component, type, subseq); 295114e8bdebSLars-Peter Clausen } 2952d191bd8dSKuninori Morimoto 295314e8bdebSLars-Peter Clausen static int snd_soc_component_stream_event(struct snd_soc_dapm_context *dapm, 295414e8bdebSLars-Peter Clausen int event) 295514e8bdebSLars-Peter Clausen { 295614e8bdebSLars-Peter Clausen struct snd_soc_component *component = dapm->component; 295714e8bdebSLars-Peter Clausen 295814e8bdebSLars-Peter Clausen return component->driver->stream_event(component, event); 295914e8bdebSLars-Peter Clausen } 296014e8bdebSLars-Peter Clausen 29617ba236ceSKuninori Morimoto static int snd_soc_component_set_bias_level(struct snd_soc_dapm_context *dapm, 29627ba236ceSKuninori Morimoto enum snd_soc_bias_level level) 29637ba236ceSKuninori Morimoto { 29647ba236ceSKuninori Morimoto struct snd_soc_component *component = dapm->component; 29657ba236ceSKuninori Morimoto 29667ba236ceSKuninori Morimoto return component->driver->set_bias_level(component, level); 29677ba236ceSKuninori Morimoto } 29687ba236ceSKuninori Morimoto 2969bb13109dSLars-Peter Clausen static int snd_soc_component_initialize(struct snd_soc_component *component, 2970bb13109dSLars-Peter Clausen const struct snd_soc_component_driver *driver, struct device *dev) 2971d191bd8dSKuninori Morimoto { 2972ce0fc93aSLars-Peter Clausen struct snd_soc_dapm_context *dapm; 2973ce0fc93aSLars-Peter Clausen 2974bb13109dSLars-Peter Clausen component->name = fmt_single_name(dev, &component->id); 2975bb13109dSLars-Peter Clausen if (!component->name) { 2976bb13109dSLars-Peter Clausen dev_err(dev, "ASoC: Failed to allocate name\n"); 2977d191bd8dSKuninori Morimoto return -ENOMEM; 2978d191bd8dSKuninori Morimoto } 2979d191bd8dSKuninori Morimoto 2980bb13109dSLars-Peter Clausen component->dev = dev; 2981bb13109dSLars-Peter Clausen component->driver = driver; 2982e2c330b9SLars-Peter Clausen 298388c27465SKuninori Morimoto dapm = snd_soc_component_get_dapm(component); 2984ce0fc93aSLars-Peter Clausen dapm->dev = dev; 2985ce0fc93aSLars-Peter Clausen dapm->component = component; 2986ce0fc93aSLars-Peter Clausen dapm->bias_level = SND_SOC_BIAS_OFF; 29877ba236ceSKuninori Morimoto dapm->idle_bias_off = !driver->idle_bias_on; 29887ba236ceSKuninori Morimoto dapm->suspend_bias_off = driver->suspend_bias_off; 298914e8bdebSLars-Peter Clausen if (driver->seq_notifier) 299014e8bdebSLars-Peter Clausen dapm->seq_notifier = snd_soc_component_seq_notifier; 299114e8bdebSLars-Peter Clausen if (driver->stream_event) 299214e8bdebSLars-Peter Clausen dapm->stream_event = snd_soc_component_stream_event; 29937ba236ceSKuninori Morimoto if (driver->set_bias_level) 29947ba236ceSKuninori Morimoto dapm->set_bias_level = snd_soc_component_set_bias_level; 2995ce0fc93aSLars-Peter Clausen 2996bb13109dSLars-Peter Clausen INIT_LIST_HEAD(&component->dai_list); 2997bb13109dSLars-Peter Clausen mutex_init(&component->io_mutex); 2998bb13109dSLars-Peter Clausen 2999bb13109dSLars-Peter Clausen return 0; 3000d191bd8dSKuninori Morimoto } 3001d191bd8dSKuninori Morimoto 300220feb881SLars-Peter Clausen static void snd_soc_component_setup_regmap(struct snd_soc_component *component) 3003886f5692SLars-Peter Clausen { 3004886f5692SLars-Peter Clausen int val_bytes = regmap_get_val_bytes(component->regmap); 300520feb881SLars-Peter Clausen 3006886f5692SLars-Peter Clausen /* Errors are legitimate for non-integer byte multiples */ 3007886f5692SLars-Peter Clausen if (val_bytes > 0) 3008886f5692SLars-Peter Clausen component->val_bytes = val_bytes; 3009886f5692SLars-Peter Clausen } 301020feb881SLars-Peter Clausen 3011e874bf5fSLars-Peter Clausen #ifdef CONFIG_REGMAP 3012e874bf5fSLars-Peter Clausen 301320feb881SLars-Peter Clausen /** 301420feb881SLars-Peter Clausen * snd_soc_component_init_regmap() - Initialize regmap instance for the component 301520feb881SLars-Peter Clausen * @component: The component for which to initialize the regmap instance 301620feb881SLars-Peter Clausen * @regmap: The regmap instance that should be used by the component 301720feb881SLars-Peter Clausen * 301820feb881SLars-Peter Clausen * This function allows deferred assignment of the regmap instance that is 301920feb881SLars-Peter Clausen * associated with the component. Only use this if the regmap instance is not 302020feb881SLars-Peter Clausen * yet ready when the component is registered. The function must also be called 302120feb881SLars-Peter Clausen * before the first IO attempt of the component. 302220feb881SLars-Peter Clausen */ 302320feb881SLars-Peter Clausen void snd_soc_component_init_regmap(struct snd_soc_component *component, 302420feb881SLars-Peter Clausen struct regmap *regmap) 302520feb881SLars-Peter Clausen { 302620feb881SLars-Peter Clausen component->regmap = regmap; 302720feb881SLars-Peter Clausen snd_soc_component_setup_regmap(component); 3028886f5692SLars-Peter Clausen } 302920feb881SLars-Peter Clausen EXPORT_SYMBOL_GPL(snd_soc_component_init_regmap); 303020feb881SLars-Peter Clausen 303120feb881SLars-Peter Clausen /** 303220feb881SLars-Peter Clausen * snd_soc_component_exit_regmap() - De-initialize regmap instance for the component 303320feb881SLars-Peter Clausen * @component: The component for which to de-initialize the regmap instance 303420feb881SLars-Peter Clausen * 303520feb881SLars-Peter Clausen * Calls regmap_exit() on the regmap instance associated to the component and 303620feb881SLars-Peter Clausen * removes the regmap instance from the component. 303720feb881SLars-Peter Clausen * 303820feb881SLars-Peter Clausen * This function should only be used if snd_soc_component_init_regmap() was used 303920feb881SLars-Peter Clausen * to initialize the regmap instance. 304020feb881SLars-Peter Clausen */ 304120feb881SLars-Peter Clausen void snd_soc_component_exit_regmap(struct snd_soc_component *component) 304220feb881SLars-Peter Clausen { 304320feb881SLars-Peter Clausen regmap_exit(component->regmap); 304420feb881SLars-Peter Clausen component->regmap = NULL; 304520feb881SLars-Peter Clausen } 304620feb881SLars-Peter Clausen EXPORT_SYMBOL_GPL(snd_soc_component_exit_regmap); 3047886f5692SLars-Peter Clausen 3048e874bf5fSLars-Peter Clausen #endif 3049d191bd8dSKuninori Morimoto 3050359c71eeSKuninori Morimoto static void snd_soc_component_add(struct snd_soc_component *component) 3051bb13109dSLars-Peter Clausen { 3052359c71eeSKuninori Morimoto mutex_lock(&client_mutex); 3053359c71eeSKuninori Morimoto 3054999f7f5aSKuninori Morimoto if (!component->driver->write && !component->driver->read) { 305520feb881SLars-Peter Clausen if (!component->regmap) 305620feb881SLars-Peter Clausen component->regmap = dev_get_regmap(component->dev, NULL); 305720feb881SLars-Peter Clausen if (component->regmap) 305820feb881SLars-Peter Clausen snd_soc_component_setup_regmap(component); 305920feb881SLars-Peter Clausen } 3060886f5692SLars-Peter Clausen 3061bb13109dSLars-Peter Clausen list_add(&component->list, &component_list); 30628a978234SLiam Girdwood INIT_LIST_HEAD(&component->dobj_list); 3063d191bd8dSKuninori Morimoto 3064d191bd8dSKuninori Morimoto mutex_unlock(&client_mutex); 3065bb13109dSLars-Peter Clausen } 3066d191bd8dSKuninori Morimoto 3067bb13109dSLars-Peter Clausen static void snd_soc_component_cleanup(struct snd_soc_component *component) 3068bb13109dSLars-Peter Clausen { 3069bb13109dSLars-Peter Clausen snd_soc_unregister_dais(component); 3070bb13109dSLars-Peter Clausen kfree(component->name); 3071bb13109dSLars-Peter Clausen } 3072d191bd8dSKuninori Morimoto 3073bb13109dSLars-Peter Clausen static void snd_soc_component_del_unlocked(struct snd_soc_component *component) 3074bb13109dSLars-Peter Clausen { 3075c12c1aadSKuninori Morimoto struct snd_soc_card *card = component->card; 3076c12c1aadSKuninori Morimoto 3077c12c1aadSKuninori Morimoto if (card) 3078c12c1aadSKuninori Morimoto snd_soc_unregister_card(card); 3079c12c1aadSKuninori Morimoto 3080bb13109dSLars-Peter Clausen list_del(&component->list); 3081bb13109dSLars-Peter Clausen } 3082d191bd8dSKuninori Morimoto 3083273d778eSKuninori Morimoto #define ENDIANNESS_MAP(name) \ 3084273d778eSKuninori Morimoto (SNDRV_PCM_FMTBIT_##name##LE | SNDRV_PCM_FMTBIT_##name##BE) 3085273d778eSKuninori Morimoto static u64 endianness_format_map[] = { 3086273d778eSKuninori Morimoto ENDIANNESS_MAP(S16_), 3087273d778eSKuninori Morimoto ENDIANNESS_MAP(U16_), 3088273d778eSKuninori Morimoto ENDIANNESS_MAP(S24_), 3089273d778eSKuninori Morimoto ENDIANNESS_MAP(U24_), 3090273d778eSKuninori Morimoto ENDIANNESS_MAP(S32_), 3091273d778eSKuninori Morimoto ENDIANNESS_MAP(U32_), 3092273d778eSKuninori Morimoto ENDIANNESS_MAP(S24_3), 3093273d778eSKuninori Morimoto ENDIANNESS_MAP(U24_3), 3094273d778eSKuninori Morimoto ENDIANNESS_MAP(S20_3), 3095273d778eSKuninori Morimoto ENDIANNESS_MAP(U20_3), 3096273d778eSKuninori Morimoto ENDIANNESS_MAP(S18_3), 3097273d778eSKuninori Morimoto ENDIANNESS_MAP(U18_3), 3098273d778eSKuninori Morimoto ENDIANNESS_MAP(FLOAT_), 3099273d778eSKuninori Morimoto ENDIANNESS_MAP(FLOAT64_), 3100273d778eSKuninori Morimoto ENDIANNESS_MAP(IEC958_SUBFRAME_), 3101273d778eSKuninori Morimoto }; 3102273d778eSKuninori Morimoto 3103273d778eSKuninori Morimoto /* 3104273d778eSKuninori Morimoto * Fix up the DAI formats for endianness: codecs don't actually see 3105273d778eSKuninori Morimoto * the endianness of the data but we're using the CPU format 3106273d778eSKuninori Morimoto * definitions which do need to include endianness so we ensure that 3107273d778eSKuninori Morimoto * codec DAIs always have both big and little endian variants set. 3108273d778eSKuninori Morimoto */ 3109273d778eSKuninori Morimoto static void convert_endianness_formats(struct snd_soc_pcm_stream *stream) 3110273d778eSKuninori Morimoto { 3111273d778eSKuninori Morimoto int i; 3112273d778eSKuninori Morimoto 3113273d778eSKuninori Morimoto for (i = 0; i < ARRAY_SIZE(endianness_format_map); i++) 3114273d778eSKuninori Morimoto if (stream->formats & endianness_format_map[i]) 3115273d778eSKuninori Morimoto stream->formats |= endianness_format_map[i]; 3116273d778eSKuninori Morimoto } 3117273d778eSKuninori Morimoto 3118e0dac41bSKuninori Morimoto int snd_soc_add_component(struct device *dev, 3119e0dac41bSKuninori Morimoto struct snd_soc_component *component, 3120cf9e829eSKuninori Morimoto const struct snd_soc_component_driver *component_driver, 3121d191bd8dSKuninori Morimoto struct snd_soc_dai_driver *dai_drv, 3122d191bd8dSKuninori Morimoto int num_dai) 3123d191bd8dSKuninori Morimoto { 3124bb13109dSLars-Peter Clausen int ret; 3125273d778eSKuninori Morimoto int i; 3126d191bd8dSKuninori Morimoto 3127cf9e829eSKuninori Morimoto ret = snd_soc_component_initialize(component, component_driver, dev); 3128bb13109dSLars-Peter Clausen if (ret) 3129bb13109dSLars-Peter Clausen goto err_free; 3130bb13109dSLars-Peter Clausen 3131273d778eSKuninori Morimoto if (component_driver->endianness) { 3132273d778eSKuninori Morimoto for (i = 0; i < num_dai; i++) { 3133273d778eSKuninori Morimoto convert_endianness_formats(&dai_drv[i].playback); 3134273d778eSKuninori Morimoto convert_endianness_formats(&dai_drv[i].capture); 3135273d778eSKuninori Morimoto } 3136273d778eSKuninori Morimoto } 3137273d778eSKuninori Morimoto 31380e7b25c6SKuninori Morimoto ret = snd_soc_register_dais(component, dai_drv, num_dai); 3139bb13109dSLars-Peter Clausen if (ret < 0) { 3140f42cf8d6SMasanari Iida dev_err(dev, "ASoC: Failed to register DAIs: %d\n", ret); 3141bb13109dSLars-Peter Clausen goto err_cleanup; 3142bb13109dSLars-Peter Clausen } 3143bb13109dSLars-Peter Clausen 3144cf9e829eSKuninori Morimoto snd_soc_component_add(component); 3145bb13109dSLars-Peter Clausen 3146bb13109dSLars-Peter Clausen return 0; 3147bb13109dSLars-Peter Clausen 3148bb13109dSLars-Peter Clausen err_cleanup: 3149cf9e829eSKuninori Morimoto snd_soc_component_cleanup(component); 3150bb13109dSLars-Peter Clausen err_free: 3151bb13109dSLars-Peter Clausen return ret; 3152d191bd8dSKuninori Morimoto } 3153e0dac41bSKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_add_component); 3154e0dac41bSKuninori Morimoto 3155e0dac41bSKuninori Morimoto int snd_soc_register_component(struct device *dev, 3156e0dac41bSKuninori Morimoto const struct snd_soc_component_driver *component_driver, 3157e0dac41bSKuninori Morimoto struct snd_soc_dai_driver *dai_drv, 3158e0dac41bSKuninori Morimoto int num_dai) 3159e0dac41bSKuninori Morimoto { 3160e0dac41bSKuninori Morimoto struct snd_soc_component *component; 3161e0dac41bSKuninori Morimoto 31627ecbd6a9SKuninori Morimoto component = devm_kzalloc(dev, sizeof(*component), GFP_KERNEL); 316308e61d03SKuninori Morimoto if (!component) 3164e0dac41bSKuninori Morimoto return -ENOMEM; 3165e0dac41bSKuninori Morimoto 3166e0dac41bSKuninori Morimoto return snd_soc_add_component(dev, component, component_driver, 3167e0dac41bSKuninori Morimoto dai_drv, num_dai); 3168e0dac41bSKuninori Morimoto } 3169d191bd8dSKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_register_component); 3170d191bd8dSKuninori Morimoto 3171d191bd8dSKuninori Morimoto /** 31722eccea8cSKuninori Morimoto * snd_soc_unregister_component - Unregister all related component 31732eccea8cSKuninori Morimoto * from the ASoC core 3174d191bd8dSKuninori Morimoto * 3175628536eaSJonathan Corbet * @dev: The device to unregister 3176d191bd8dSKuninori Morimoto */ 31772eccea8cSKuninori Morimoto static int __snd_soc_unregister_component(struct device *dev) 3178d191bd8dSKuninori Morimoto { 3179cf9e829eSKuninori Morimoto struct snd_soc_component *component; 318021a03528SKuninori Morimoto int found = 0; 3181d191bd8dSKuninori Morimoto 318234e81ab4SLars-Peter Clausen mutex_lock(&client_mutex); 3183cf9e829eSKuninori Morimoto list_for_each_entry(component, &component_list, list) { 3184999f7f5aSKuninori Morimoto if (dev != component->dev) 318521a03528SKuninori Morimoto continue; 3186d191bd8dSKuninori Morimoto 3187cf9e829eSKuninori Morimoto snd_soc_tplg_component_remove(component, SND_SOC_TPLG_INDEX_ALL); 3188cf9e829eSKuninori Morimoto snd_soc_component_del_unlocked(component); 318921a03528SKuninori Morimoto found = 1; 319021a03528SKuninori Morimoto break; 3191d191bd8dSKuninori Morimoto } 3192d191bd8dSKuninori Morimoto mutex_unlock(&client_mutex); 3193d191bd8dSKuninori Morimoto 319421a03528SKuninori Morimoto if (found) { 3195cf9e829eSKuninori Morimoto snd_soc_component_cleanup(component); 3196d191bd8dSKuninori Morimoto } 31972eccea8cSKuninori Morimoto 31982eccea8cSKuninori Morimoto return found; 31992eccea8cSKuninori Morimoto } 32002eccea8cSKuninori Morimoto 32012eccea8cSKuninori Morimoto void snd_soc_unregister_component(struct device *dev) 32022eccea8cSKuninori Morimoto { 32032eccea8cSKuninori Morimoto while (__snd_soc_unregister_component(dev)); 3204d191bd8dSKuninori Morimoto } 3205d191bd8dSKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_unregister_component); 3206d191bd8dSKuninori Morimoto 32077dd5d0d9SKuninori Morimoto struct snd_soc_component *snd_soc_lookup_component(struct device *dev, 32087dd5d0d9SKuninori Morimoto const char *driver_name) 32097dd5d0d9SKuninori Morimoto { 32107dd5d0d9SKuninori Morimoto struct snd_soc_component *component; 32117dd5d0d9SKuninori Morimoto struct snd_soc_component *ret; 32127dd5d0d9SKuninori Morimoto 32137dd5d0d9SKuninori Morimoto ret = NULL; 32147dd5d0d9SKuninori Morimoto mutex_lock(&client_mutex); 32157dd5d0d9SKuninori Morimoto list_for_each_entry(component, &component_list, list) { 32167dd5d0d9SKuninori Morimoto if (dev != component->dev) 32177dd5d0d9SKuninori Morimoto continue; 32187dd5d0d9SKuninori Morimoto 32197dd5d0d9SKuninori Morimoto if (driver_name && 32207dd5d0d9SKuninori Morimoto (driver_name != component->driver->name) && 32217dd5d0d9SKuninori Morimoto (strcmp(component->driver->name, driver_name) != 0)) 32227dd5d0d9SKuninori Morimoto continue; 32237dd5d0d9SKuninori Morimoto 32247dd5d0d9SKuninori Morimoto ret = component; 32257dd5d0d9SKuninori Morimoto break; 32267dd5d0d9SKuninori Morimoto } 32277dd5d0d9SKuninori Morimoto mutex_unlock(&client_mutex); 32287dd5d0d9SKuninori Morimoto 32297dd5d0d9SKuninori Morimoto return ret; 32307dd5d0d9SKuninori Morimoto } 32317dd5d0d9SKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_lookup_component); 32327dd5d0d9SKuninori Morimoto 3233bec4fa05SStephen Warren /* Retrieve a card's name from device tree */ 3234b07609ceSKuninori Morimoto int snd_soc_of_parse_card_name(struct snd_soc_card *card, 3235bec4fa05SStephen Warren const char *propname) 3236bec4fa05SStephen Warren { 3237b07609ceSKuninori Morimoto struct device_node *np; 3238bec4fa05SStephen Warren int ret; 3239bec4fa05SStephen Warren 32407e07e7c0STushar Behera if (!card->dev) { 32417e07e7c0STushar Behera pr_err("card->dev is not set before calling %s\n", __func__); 32427e07e7c0STushar Behera return -EINVAL; 32437e07e7c0STushar Behera } 32447e07e7c0STushar Behera 32457e07e7c0STushar Behera np = card->dev->of_node; 32467e07e7c0STushar Behera 3247bec4fa05SStephen Warren ret = of_property_read_string_index(np, propname, 0, &card->name); 3248bec4fa05SStephen Warren /* 3249bec4fa05SStephen Warren * EINVAL means the property does not exist. This is fine providing 3250bec4fa05SStephen Warren * card->name was previously set, which is checked later in 3251bec4fa05SStephen Warren * snd_soc_register_card. 3252bec4fa05SStephen Warren */ 3253bec4fa05SStephen Warren if (ret < 0 && ret != -EINVAL) { 3254bec4fa05SStephen Warren dev_err(card->dev, 3255f110bfc7SLiam Girdwood "ASoC: Property '%s' could not be read: %d\n", 3256bec4fa05SStephen Warren propname, ret); 3257bec4fa05SStephen Warren return ret; 3258bec4fa05SStephen Warren } 3259bec4fa05SStephen Warren 3260bec4fa05SStephen Warren return 0; 3261bec4fa05SStephen Warren } 3262b07609ceSKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_of_parse_card_name); 3263bec4fa05SStephen Warren 32649a6d4860SXiubo Li static const struct snd_soc_dapm_widget simple_widgets[] = { 32659a6d4860SXiubo Li SND_SOC_DAPM_MIC("Microphone", NULL), 32669a6d4860SXiubo Li SND_SOC_DAPM_LINE("Line", NULL), 32679a6d4860SXiubo Li SND_SOC_DAPM_HP("Headphone", NULL), 32689a6d4860SXiubo Li SND_SOC_DAPM_SPK("Speaker", NULL), 32699a6d4860SXiubo Li }; 32709a6d4860SXiubo Li 327121efde50SKuninori Morimoto int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card, 32729a6d4860SXiubo Li const char *propname) 32739a6d4860SXiubo Li { 327421efde50SKuninori Morimoto struct device_node *np = card->dev->of_node; 32759a6d4860SXiubo Li struct snd_soc_dapm_widget *widgets; 32769a6d4860SXiubo Li const char *template, *wname; 32779a6d4860SXiubo Li int i, j, num_widgets, ret; 32789a6d4860SXiubo Li 32799a6d4860SXiubo Li num_widgets = of_property_count_strings(np, propname); 32809a6d4860SXiubo Li if (num_widgets < 0) { 32819a6d4860SXiubo Li dev_err(card->dev, 32829a6d4860SXiubo Li "ASoC: Property '%s' does not exist\n", propname); 32839a6d4860SXiubo Li return -EINVAL; 32849a6d4860SXiubo Li } 32859a6d4860SXiubo Li if (num_widgets & 1) { 32869a6d4860SXiubo Li dev_err(card->dev, 32879a6d4860SXiubo Li "ASoC: Property '%s' length is not even\n", propname); 32889a6d4860SXiubo Li return -EINVAL; 32899a6d4860SXiubo Li } 32909a6d4860SXiubo Li 32919a6d4860SXiubo Li num_widgets /= 2; 32929a6d4860SXiubo Li if (!num_widgets) { 32939a6d4860SXiubo Li dev_err(card->dev, "ASoC: Property '%s's length is zero\n", 32949a6d4860SXiubo Li propname); 32959a6d4860SXiubo Li return -EINVAL; 32969a6d4860SXiubo Li } 32979a6d4860SXiubo Li 32989a6d4860SXiubo Li widgets = devm_kcalloc(card->dev, num_widgets, sizeof(*widgets), 32999a6d4860SXiubo Li GFP_KERNEL); 33009a6d4860SXiubo Li if (!widgets) { 33019a6d4860SXiubo Li dev_err(card->dev, 33029a6d4860SXiubo Li "ASoC: Could not allocate memory for widgets\n"); 33039a6d4860SXiubo Li return -ENOMEM; 33049a6d4860SXiubo Li } 33059a6d4860SXiubo Li 33069a6d4860SXiubo Li for (i = 0; i < num_widgets; i++) { 33079a6d4860SXiubo Li ret = of_property_read_string_index(np, propname, 33089a6d4860SXiubo Li 2 * i, &template); 33099a6d4860SXiubo Li if (ret) { 33109a6d4860SXiubo Li dev_err(card->dev, 33119a6d4860SXiubo Li "ASoC: Property '%s' index %d read error:%d\n", 33129a6d4860SXiubo Li propname, 2 * i, ret); 33139a6d4860SXiubo Li return -EINVAL; 33149a6d4860SXiubo Li } 33159a6d4860SXiubo Li 33169a6d4860SXiubo Li for (j = 0; j < ARRAY_SIZE(simple_widgets); j++) { 33179a6d4860SXiubo Li if (!strncmp(template, simple_widgets[j].name, 33189a6d4860SXiubo Li strlen(simple_widgets[j].name))) { 33199a6d4860SXiubo Li widgets[i] = simple_widgets[j]; 33209a6d4860SXiubo Li break; 33219a6d4860SXiubo Li } 33229a6d4860SXiubo Li } 33239a6d4860SXiubo Li 33249a6d4860SXiubo Li if (j >= ARRAY_SIZE(simple_widgets)) { 33259a6d4860SXiubo Li dev_err(card->dev, 33269a6d4860SXiubo Li "ASoC: DAPM widget '%s' is not supported\n", 33279a6d4860SXiubo Li template); 33289a6d4860SXiubo Li return -EINVAL; 33299a6d4860SXiubo Li } 33309a6d4860SXiubo Li 33319a6d4860SXiubo Li ret = of_property_read_string_index(np, propname, 33329a6d4860SXiubo Li (2 * i) + 1, 33339a6d4860SXiubo Li &wname); 33349a6d4860SXiubo Li if (ret) { 33359a6d4860SXiubo Li dev_err(card->dev, 33369a6d4860SXiubo Li "ASoC: Property '%s' index %d read error:%d\n", 33379a6d4860SXiubo Li propname, (2 * i) + 1, ret); 33389a6d4860SXiubo Li return -EINVAL; 33399a6d4860SXiubo Li } 33409a6d4860SXiubo Li 33419a6d4860SXiubo Li widgets[i].name = wname; 33429a6d4860SXiubo Li } 33439a6d4860SXiubo Li 3344f23e860eSNicolin Chen card->of_dapm_widgets = widgets; 3345f23e860eSNicolin Chen card->num_of_dapm_widgets = num_widgets; 33469a6d4860SXiubo Li 33479a6d4860SXiubo Li return 0; 33489a6d4860SXiubo Li } 334921efde50SKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_simple_widgets); 33509a6d4860SXiubo Li 33516131084aSJyri Sarha static int snd_soc_of_get_slot_mask(struct device_node *np, 33526131084aSJyri Sarha const char *prop_name, 33536131084aSJyri Sarha unsigned int *mask) 33546131084aSJyri Sarha { 33556131084aSJyri Sarha u32 val; 33566c84e591SJyri Sarha const __be32 *of_slot_mask = of_get_property(np, prop_name, &val); 33576131084aSJyri Sarha int i; 33586131084aSJyri Sarha 33596131084aSJyri Sarha if (!of_slot_mask) 33606131084aSJyri Sarha return 0; 33616131084aSJyri Sarha val /= sizeof(u32); 33626131084aSJyri Sarha for (i = 0; i < val; i++) 33636131084aSJyri Sarha if (be32_to_cpup(&of_slot_mask[i])) 33646131084aSJyri Sarha *mask |= (1 << i); 33656131084aSJyri Sarha 33666131084aSJyri Sarha return val; 33676131084aSJyri Sarha } 33686131084aSJyri Sarha 336989c67857SXiubo Li int snd_soc_of_parse_tdm_slot(struct device_node *np, 33706131084aSJyri Sarha unsigned int *tx_mask, 33716131084aSJyri Sarha unsigned int *rx_mask, 337289c67857SXiubo Li unsigned int *slots, 337389c67857SXiubo Li unsigned int *slot_width) 337489c67857SXiubo Li { 337589c67857SXiubo Li u32 val; 337689c67857SXiubo Li int ret; 337789c67857SXiubo Li 33786131084aSJyri Sarha if (tx_mask) 33796131084aSJyri Sarha snd_soc_of_get_slot_mask(np, "dai-tdm-slot-tx-mask", tx_mask); 33806131084aSJyri Sarha if (rx_mask) 33816131084aSJyri Sarha snd_soc_of_get_slot_mask(np, "dai-tdm-slot-rx-mask", rx_mask); 33826131084aSJyri Sarha 338389c67857SXiubo Li if (of_property_read_bool(np, "dai-tdm-slot-num")) { 338489c67857SXiubo Li ret = of_property_read_u32(np, "dai-tdm-slot-num", &val); 338589c67857SXiubo Li if (ret) 338689c67857SXiubo Li return ret; 338789c67857SXiubo Li 338889c67857SXiubo Li if (slots) 338989c67857SXiubo Li *slots = val; 339089c67857SXiubo Li } 339189c67857SXiubo Li 339289c67857SXiubo Li if (of_property_read_bool(np, "dai-tdm-slot-width")) { 339389c67857SXiubo Li ret = of_property_read_u32(np, "dai-tdm-slot-width", &val); 339489c67857SXiubo Li if (ret) 339589c67857SXiubo Li return ret; 339689c67857SXiubo Li 339789c67857SXiubo Li if (slot_width) 339889c67857SXiubo Li *slot_width = val; 339989c67857SXiubo Li } 340089c67857SXiubo Li 340189c67857SXiubo Li return 0; 340289c67857SXiubo Li } 340389c67857SXiubo Li EXPORT_SYMBOL_GPL(snd_soc_of_parse_tdm_slot); 340489c67857SXiubo Li 3405440a3006SKuninori Morimoto void snd_soc_of_parse_audio_prefix(struct snd_soc_card *card, 34065e3cdaa2SKuninori Morimoto struct snd_soc_codec_conf *codec_conf, 34075e3cdaa2SKuninori Morimoto struct device_node *of_node, 34085e3cdaa2SKuninori Morimoto const char *propname) 34095e3cdaa2SKuninori Morimoto { 3410440a3006SKuninori Morimoto struct device_node *np = card->dev->of_node; 34115e3cdaa2SKuninori Morimoto const char *str; 34125e3cdaa2SKuninori Morimoto int ret; 34135e3cdaa2SKuninori Morimoto 34145e3cdaa2SKuninori Morimoto ret = of_property_read_string(np, propname, &str); 34155e3cdaa2SKuninori Morimoto if (ret < 0) { 34165e3cdaa2SKuninori Morimoto /* no prefix is not error */ 34175e3cdaa2SKuninori Morimoto return; 34185e3cdaa2SKuninori Morimoto } 34195e3cdaa2SKuninori Morimoto 34205e3cdaa2SKuninori Morimoto codec_conf->of_node = of_node; 34215e3cdaa2SKuninori Morimoto codec_conf->name_prefix = str; 34225e3cdaa2SKuninori Morimoto } 3423440a3006SKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_prefix); 34245e3cdaa2SKuninori Morimoto 34252bc644afSKuninori Morimoto int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, 3426a4a54dd5SStephen Warren const char *propname) 3427a4a54dd5SStephen Warren { 34282bc644afSKuninori Morimoto struct device_node *np = card->dev->of_node; 3429e3b1e6a1SMark Brown int num_routes; 3430a4a54dd5SStephen Warren struct snd_soc_dapm_route *routes; 3431a4a54dd5SStephen Warren int i, ret; 3432a4a54dd5SStephen Warren 3433a4a54dd5SStephen Warren num_routes = of_property_count_strings(np, propname); 3434c34ce320SRichard Zhao if (num_routes < 0 || num_routes & 1) { 343510e8aa9aSMichał Mirosław dev_err(card->dev, 343610e8aa9aSMichał Mirosław "ASoC: Property '%s' does not exist or its length is not even\n", 343710e8aa9aSMichał Mirosław propname); 3438a4a54dd5SStephen Warren return -EINVAL; 3439a4a54dd5SStephen Warren } 3440a4a54dd5SStephen Warren num_routes /= 2; 3441a4a54dd5SStephen Warren if (!num_routes) { 3442f110bfc7SLiam Girdwood dev_err(card->dev, "ASoC: Property '%s's length is zero\n", 3443a4a54dd5SStephen Warren propname); 3444a4a54dd5SStephen Warren return -EINVAL; 3445a4a54dd5SStephen Warren } 3446a4a54dd5SStephen Warren 3447a86854d0SKees Cook routes = devm_kcalloc(card->dev, num_routes, sizeof(*routes), 3448a4a54dd5SStephen Warren GFP_KERNEL); 3449a4a54dd5SStephen Warren if (!routes) { 3450a4a54dd5SStephen Warren dev_err(card->dev, 3451f110bfc7SLiam Girdwood "ASoC: Could not allocate DAPM route table\n"); 3452a4a54dd5SStephen Warren return -EINVAL; 3453a4a54dd5SStephen Warren } 3454a4a54dd5SStephen Warren 3455a4a54dd5SStephen Warren for (i = 0; i < num_routes; i++) { 3456a4a54dd5SStephen Warren ret = of_property_read_string_index(np, propname, 3457e3b1e6a1SMark Brown 2 * i, &routes[i].sink); 3458a4a54dd5SStephen Warren if (ret) { 3459c871bd0bSMark Brown dev_err(card->dev, 3460c871bd0bSMark Brown "ASoC: Property '%s' index %d could not be read: %d\n", 3461c871bd0bSMark Brown propname, 2 * i, ret); 3462a4a54dd5SStephen Warren return -EINVAL; 3463a4a54dd5SStephen Warren } 3464a4a54dd5SStephen Warren ret = of_property_read_string_index(np, propname, 3465e3b1e6a1SMark Brown (2 * i) + 1, &routes[i].source); 3466a4a54dd5SStephen Warren if (ret) { 3467a4a54dd5SStephen Warren dev_err(card->dev, 3468c871bd0bSMark Brown "ASoC: Property '%s' index %d could not be read: %d\n", 3469c871bd0bSMark Brown propname, (2 * i) + 1, ret); 3470a4a54dd5SStephen Warren return -EINVAL; 3471a4a54dd5SStephen Warren } 3472a4a54dd5SStephen Warren } 3473a4a54dd5SStephen Warren 3474f23e860eSNicolin Chen card->num_of_dapm_routes = num_routes; 3475f23e860eSNicolin Chen card->of_dapm_routes = routes; 3476a4a54dd5SStephen Warren 3477a4a54dd5SStephen Warren return 0; 3478a4a54dd5SStephen Warren } 34792bc644afSKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_routing); 3480a4a54dd5SStephen Warren 3481a7930ed4SKuninori Morimoto unsigned int snd_soc_of_parse_daifmt(struct device_node *np, 3482389cb834SJyri Sarha const char *prefix, 3483389cb834SJyri Sarha struct device_node **bitclkmaster, 3484389cb834SJyri Sarha struct device_node **framemaster) 3485a7930ed4SKuninori Morimoto { 3486a7930ed4SKuninori Morimoto int ret, i; 3487a7930ed4SKuninori Morimoto char prop[128]; 3488a7930ed4SKuninori Morimoto unsigned int format = 0; 3489a7930ed4SKuninori Morimoto int bit, frame; 3490a7930ed4SKuninori Morimoto const char *str; 3491a7930ed4SKuninori Morimoto struct { 3492a7930ed4SKuninori Morimoto char *name; 3493a7930ed4SKuninori Morimoto unsigned int val; 3494a7930ed4SKuninori Morimoto } of_fmt_table[] = { 3495a7930ed4SKuninori Morimoto { "i2s", SND_SOC_DAIFMT_I2S }, 3496a7930ed4SKuninori Morimoto { "right_j", SND_SOC_DAIFMT_RIGHT_J }, 3497a7930ed4SKuninori Morimoto { "left_j", SND_SOC_DAIFMT_LEFT_J }, 3498a7930ed4SKuninori Morimoto { "dsp_a", SND_SOC_DAIFMT_DSP_A }, 3499a7930ed4SKuninori Morimoto { "dsp_b", SND_SOC_DAIFMT_DSP_B }, 3500a7930ed4SKuninori Morimoto { "ac97", SND_SOC_DAIFMT_AC97 }, 3501a7930ed4SKuninori Morimoto { "pdm", SND_SOC_DAIFMT_PDM}, 3502a7930ed4SKuninori Morimoto { "msb", SND_SOC_DAIFMT_MSB }, 3503a7930ed4SKuninori Morimoto { "lsb", SND_SOC_DAIFMT_LSB }, 3504a7930ed4SKuninori Morimoto }; 3505a7930ed4SKuninori Morimoto 3506a7930ed4SKuninori Morimoto if (!prefix) 3507a7930ed4SKuninori Morimoto prefix = ""; 3508a7930ed4SKuninori Morimoto 3509a7930ed4SKuninori Morimoto /* 35105711c979SKuninori Morimoto * check "dai-format = xxx" 35115711c979SKuninori Morimoto * or "[prefix]format = xxx" 3512a7930ed4SKuninori Morimoto * SND_SOC_DAIFMT_FORMAT_MASK area 3513a7930ed4SKuninori Morimoto */ 35145711c979SKuninori Morimoto ret = of_property_read_string(np, "dai-format", &str); 35155711c979SKuninori Morimoto if (ret < 0) { 3516a7930ed4SKuninori Morimoto snprintf(prop, sizeof(prop), "%sformat", prefix); 3517a7930ed4SKuninori Morimoto ret = of_property_read_string(np, prop, &str); 35185711c979SKuninori Morimoto } 3519a7930ed4SKuninori Morimoto if (ret == 0) { 3520a7930ed4SKuninori Morimoto for (i = 0; i < ARRAY_SIZE(of_fmt_table); i++) { 3521a7930ed4SKuninori Morimoto if (strcmp(str, of_fmt_table[i].name) == 0) { 3522a7930ed4SKuninori Morimoto format |= of_fmt_table[i].val; 3523a7930ed4SKuninori Morimoto break; 3524a7930ed4SKuninori Morimoto } 3525a7930ed4SKuninori Morimoto } 3526a7930ed4SKuninori Morimoto } 3527a7930ed4SKuninori Morimoto 3528a7930ed4SKuninori Morimoto /* 35298c2d6a9fSKuninori Morimoto * check "[prefix]continuous-clock" 3530a7930ed4SKuninori Morimoto * SND_SOC_DAIFMT_CLOCK_MASK area 3531a7930ed4SKuninori Morimoto */ 35328c2d6a9fSKuninori Morimoto snprintf(prop, sizeof(prop), "%scontinuous-clock", prefix); 353351930295SJulia Lawall if (of_property_read_bool(np, prop)) 35348c2d6a9fSKuninori Morimoto format |= SND_SOC_DAIFMT_CONT; 35358c2d6a9fSKuninori Morimoto else 35368c2d6a9fSKuninori Morimoto format |= SND_SOC_DAIFMT_GATED; 3537a7930ed4SKuninori Morimoto 3538a7930ed4SKuninori Morimoto /* 3539a7930ed4SKuninori Morimoto * check "[prefix]bitclock-inversion" 3540a7930ed4SKuninori Morimoto * check "[prefix]frame-inversion" 3541a7930ed4SKuninori Morimoto * SND_SOC_DAIFMT_INV_MASK area 3542a7930ed4SKuninori Morimoto */ 3543a7930ed4SKuninori Morimoto snprintf(prop, sizeof(prop), "%sbitclock-inversion", prefix); 3544a7930ed4SKuninori Morimoto bit = !!of_get_property(np, prop, NULL); 3545a7930ed4SKuninori Morimoto 3546a7930ed4SKuninori Morimoto snprintf(prop, sizeof(prop), "%sframe-inversion", prefix); 3547a7930ed4SKuninori Morimoto frame = !!of_get_property(np, prop, NULL); 3548a7930ed4SKuninori Morimoto 3549a7930ed4SKuninori Morimoto switch ((bit << 4) + frame) { 3550a7930ed4SKuninori Morimoto case 0x11: 3551a7930ed4SKuninori Morimoto format |= SND_SOC_DAIFMT_IB_IF; 3552a7930ed4SKuninori Morimoto break; 3553a7930ed4SKuninori Morimoto case 0x10: 3554a7930ed4SKuninori Morimoto format |= SND_SOC_DAIFMT_IB_NF; 3555a7930ed4SKuninori Morimoto break; 3556a7930ed4SKuninori Morimoto case 0x01: 3557a7930ed4SKuninori Morimoto format |= SND_SOC_DAIFMT_NB_IF; 3558a7930ed4SKuninori Morimoto break; 3559a7930ed4SKuninori Morimoto default: 3560a7930ed4SKuninori Morimoto /* SND_SOC_DAIFMT_NB_NF is default */ 3561a7930ed4SKuninori Morimoto break; 3562a7930ed4SKuninori Morimoto } 3563a7930ed4SKuninori Morimoto 3564a7930ed4SKuninori Morimoto /* 3565a7930ed4SKuninori Morimoto * check "[prefix]bitclock-master" 3566a7930ed4SKuninori Morimoto * check "[prefix]frame-master" 3567a7930ed4SKuninori Morimoto * SND_SOC_DAIFMT_MASTER_MASK area 3568a7930ed4SKuninori Morimoto */ 3569a7930ed4SKuninori Morimoto snprintf(prop, sizeof(prop), "%sbitclock-master", prefix); 3570a7930ed4SKuninori Morimoto bit = !!of_get_property(np, prop, NULL); 3571389cb834SJyri Sarha if (bit && bitclkmaster) 3572389cb834SJyri Sarha *bitclkmaster = of_parse_phandle(np, prop, 0); 3573a7930ed4SKuninori Morimoto 3574a7930ed4SKuninori Morimoto snprintf(prop, sizeof(prop), "%sframe-master", prefix); 3575a7930ed4SKuninori Morimoto frame = !!of_get_property(np, prop, NULL); 3576389cb834SJyri Sarha if (frame && framemaster) 3577389cb834SJyri Sarha *framemaster = of_parse_phandle(np, prop, 0); 3578a7930ed4SKuninori Morimoto 3579a7930ed4SKuninori Morimoto switch ((bit << 4) + frame) { 3580a7930ed4SKuninori Morimoto case 0x11: 3581a7930ed4SKuninori Morimoto format |= SND_SOC_DAIFMT_CBM_CFM; 3582a7930ed4SKuninori Morimoto break; 3583a7930ed4SKuninori Morimoto case 0x10: 3584a7930ed4SKuninori Morimoto format |= SND_SOC_DAIFMT_CBM_CFS; 3585a7930ed4SKuninori Morimoto break; 3586a7930ed4SKuninori Morimoto case 0x01: 3587a7930ed4SKuninori Morimoto format |= SND_SOC_DAIFMT_CBS_CFM; 3588a7930ed4SKuninori Morimoto break; 3589a7930ed4SKuninori Morimoto default: 3590a7930ed4SKuninori Morimoto format |= SND_SOC_DAIFMT_CBS_CFS; 3591a7930ed4SKuninori Morimoto break; 3592a7930ed4SKuninori Morimoto } 3593a7930ed4SKuninori Morimoto 3594a7930ed4SKuninori Morimoto return format; 3595a7930ed4SKuninori Morimoto } 3596a7930ed4SKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_of_parse_daifmt); 3597a7930ed4SKuninori Morimoto 3598a180e8b9SKuninori Morimoto int snd_soc_get_dai_id(struct device_node *ep) 3599a180e8b9SKuninori Morimoto { 3600a180e8b9SKuninori Morimoto struct snd_soc_component *pos; 3601a180e8b9SKuninori Morimoto struct device_node *node; 3602a180e8b9SKuninori Morimoto int ret; 3603a180e8b9SKuninori Morimoto 3604a180e8b9SKuninori Morimoto node = of_graph_get_port_parent(ep); 3605a180e8b9SKuninori Morimoto 3606a180e8b9SKuninori Morimoto /* 3607a180e8b9SKuninori Morimoto * For example HDMI case, HDMI has video/sound port, 3608a180e8b9SKuninori Morimoto * but ALSA SoC needs sound port number only. 3609a180e8b9SKuninori Morimoto * Thus counting HDMI DT port/endpoint doesn't work. 3610a180e8b9SKuninori Morimoto * Then, it should have .of_xlate_dai_id 3611a180e8b9SKuninori Morimoto */ 3612a180e8b9SKuninori Morimoto ret = -ENOTSUPP; 3613a180e8b9SKuninori Morimoto mutex_lock(&client_mutex); 3614a180e8b9SKuninori Morimoto list_for_each_entry(pos, &component_list, list) { 3615a180e8b9SKuninori Morimoto struct device_node *component_of_node = pos->dev->of_node; 3616a180e8b9SKuninori Morimoto 3617a180e8b9SKuninori Morimoto if (!component_of_node && pos->dev->parent) 3618a180e8b9SKuninori Morimoto component_of_node = pos->dev->parent->of_node; 3619a180e8b9SKuninori Morimoto 3620a180e8b9SKuninori Morimoto if (component_of_node != node) 3621a180e8b9SKuninori Morimoto continue; 3622a180e8b9SKuninori Morimoto 3623a180e8b9SKuninori Morimoto if (pos->driver->of_xlate_dai_id) 3624a180e8b9SKuninori Morimoto ret = pos->driver->of_xlate_dai_id(pos, ep); 3625a180e8b9SKuninori Morimoto 3626a180e8b9SKuninori Morimoto break; 3627a180e8b9SKuninori Morimoto } 3628a180e8b9SKuninori Morimoto mutex_unlock(&client_mutex); 3629a180e8b9SKuninori Morimoto 3630c0a480d1STony Lindgren of_node_put(node); 3631c0a480d1STony Lindgren 3632a180e8b9SKuninori Morimoto return ret; 3633a180e8b9SKuninori Morimoto } 3634a180e8b9SKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_get_dai_id); 3635a180e8b9SKuninori Morimoto 36361ad8ec53SKuninori Morimoto int snd_soc_get_dai_name(struct of_phandle_args *args, 3637cb470087SKuninori Morimoto const char **dai_name) 3638cb470087SKuninori Morimoto { 3639cb470087SKuninori Morimoto struct snd_soc_component *pos; 36403e0aa8d8SJyri Sarha struct device_node *component_of_node; 364193b0f3eeSJean-Francois Moine int ret = -EPROBE_DEFER; 3642cb470087SKuninori Morimoto 3643cb470087SKuninori Morimoto mutex_lock(&client_mutex); 3644cb470087SKuninori Morimoto list_for_each_entry(pos, &component_list, list) { 36453e0aa8d8SJyri Sarha component_of_node = pos->dev->of_node; 36463e0aa8d8SJyri Sarha if (!component_of_node && pos->dev->parent) 36473e0aa8d8SJyri Sarha component_of_node = pos->dev->parent->of_node; 36483e0aa8d8SJyri Sarha 36493e0aa8d8SJyri Sarha if (component_of_node != args->np) 3650cb470087SKuninori Morimoto continue; 3651cb470087SKuninori Morimoto 36526833c452SKuninori Morimoto if (pos->driver->of_xlate_dai_name) { 365393b0f3eeSJean-Francois Moine ret = pos->driver->of_xlate_dai_name(pos, 365493b0f3eeSJean-Francois Moine args, 365593b0f3eeSJean-Francois Moine dai_name); 36566833c452SKuninori Morimoto } else { 365758bf4179SKuninori Morimoto struct snd_soc_dai *dai; 36586833c452SKuninori Morimoto int id = -1; 36596833c452SKuninori Morimoto 366093b0f3eeSJean-Francois Moine switch (args->args_count) { 36616833c452SKuninori Morimoto case 0: 36626833c452SKuninori Morimoto id = 0; /* same as dai_drv[0] */ 36636833c452SKuninori Morimoto break; 36646833c452SKuninori Morimoto case 1: 366593b0f3eeSJean-Francois Moine id = args->args[0]; 36666833c452SKuninori Morimoto break; 36676833c452SKuninori Morimoto default: 36686833c452SKuninori Morimoto /* not supported */ 3669cb470087SKuninori Morimoto break; 3670cb470087SKuninori Morimoto } 3671cb470087SKuninori Morimoto 36726833c452SKuninori Morimoto if (id < 0 || id >= pos->num_dai) { 36736833c452SKuninori Morimoto ret = -EINVAL; 36743dcba280SNicolin Chen continue; 36756833c452SKuninori Morimoto } 3676e41975edSXiubo Li 3677e41975edSXiubo Li ret = 0; 3678e41975edSXiubo Li 367958bf4179SKuninori Morimoto /* find target DAI */ 368058bf4179SKuninori Morimoto list_for_each_entry(dai, &pos->dai_list, list) { 368158bf4179SKuninori Morimoto if (id == 0) 368258bf4179SKuninori Morimoto break; 368358bf4179SKuninori Morimoto id--; 368458bf4179SKuninori Morimoto } 368558bf4179SKuninori Morimoto 368658bf4179SKuninori Morimoto *dai_name = dai->driver->name; 3687e41975edSXiubo Li if (!*dai_name) 3688e41975edSXiubo Li *dai_name = pos->name; 36896833c452SKuninori Morimoto } 36906833c452SKuninori Morimoto 3691cb470087SKuninori Morimoto break; 3692cb470087SKuninori Morimoto } 3693cb470087SKuninori Morimoto mutex_unlock(&client_mutex); 369493b0f3eeSJean-Francois Moine return ret; 369593b0f3eeSJean-Francois Moine } 36961ad8ec53SKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_get_dai_name); 369793b0f3eeSJean-Francois Moine 369893b0f3eeSJean-Francois Moine int snd_soc_of_get_dai_name(struct device_node *of_node, 369993b0f3eeSJean-Francois Moine const char **dai_name) 370093b0f3eeSJean-Francois Moine { 370193b0f3eeSJean-Francois Moine struct of_phandle_args args; 370293b0f3eeSJean-Francois Moine int ret; 370393b0f3eeSJean-Francois Moine 370493b0f3eeSJean-Francois Moine ret = of_parse_phandle_with_args(of_node, "sound-dai", 370593b0f3eeSJean-Francois Moine "#sound-dai-cells", 0, &args); 370693b0f3eeSJean-Francois Moine if (ret) 370793b0f3eeSJean-Francois Moine return ret; 370893b0f3eeSJean-Francois Moine 370993b0f3eeSJean-Francois Moine ret = snd_soc_get_dai_name(&args, dai_name); 3710cb470087SKuninori Morimoto 3711cb470087SKuninori Morimoto of_node_put(args.np); 3712cb470087SKuninori Morimoto 3713cb470087SKuninori Morimoto return ret; 3714cb470087SKuninori Morimoto } 3715cb470087SKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_name); 3716cb470087SKuninori Morimoto 371793b0f3eeSJean-Francois Moine /* 371894685763SSylwester Nawrocki * snd_soc_of_put_dai_link_codecs - Dereference device nodes in the codecs array 371994685763SSylwester Nawrocki * @dai_link: DAI link 372094685763SSylwester Nawrocki * 372194685763SSylwester Nawrocki * Dereference device nodes acquired by snd_soc_of_get_dai_link_codecs(). 372294685763SSylwester Nawrocki */ 372394685763SSylwester Nawrocki void snd_soc_of_put_dai_link_codecs(struct snd_soc_dai_link *dai_link) 372494685763SSylwester Nawrocki { 372594685763SSylwester Nawrocki struct snd_soc_dai_link_component *component = dai_link->codecs; 372694685763SSylwester Nawrocki int index; 372794685763SSylwester Nawrocki 372894685763SSylwester Nawrocki for (index = 0; index < dai_link->num_codecs; index++, component++) { 372994685763SSylwester Nawrocki if (!component->of_node) 373094685763SSylwester Nawrocki break; 373194685763SSylwester Nawrocki of_node_put(component->of_node); 373294685763SSylwester Nawrocki component->of_node = NULL; 373394685763SSylwester Nawrocki } 373494685763SSylwester Nawrocki } 373594685763SSylwester Nawrocki EXPORT_SYMBOL_GPL(snd_soc_of_put_dai_link_codecs); 373694685763SSylwester Nawrocki 373794685763SSylwester Nawrocki /* 373893b0f3eeSJean-Francois Moine * snd_soc_of_get_dai_link_codecs - Parse a list of CODECs in the devicetree 373993b0f3eeSJean-Francois Moine * @dev: Card device 374093b0f3eeSJean-Francois Moine * @of_node: Device node 374193b0f3eeSJean-Francois Moine * @dai_link: DAI link 374293b0f3eeSJean-Francois Moine * 374393b0f3eeSJean-Francois Moine * Builds an array of CODEC DAI components from the DAI link property 374493b0f3eeSJean-Francois Moine * 'sound-dai'. 374593b0f3eeSJean-Francois Moine * The array is set in the DAI link and the number of DAIs is set accordingly. 374694685763SSylwester Nawrocki * The device nodes in the array (of_node) must be dereferenced by calling 374794685763SSylwester Nawrocki * snd_soc_of_put_dai_link_codecs() on @dai_link. 374893b0f3eeSJean-Francois Moine * 374993b0f3eeSJean-Francois Moine * Returns 0 for success 375093b0f3eeSJean-Francois Moine */ 375193b0f3eeSJean-Francois Moine int snd_soc_of_get_dai_link_codecs(struct device *dev, 375293b0f3eeSJean-Francois Moine struct device_node *of_node, 375393b0f3eeSJean-Francois Moine struct snd_soc_dai_link *dai_link) 375493b0f3eeSJean-Francois Moine { 375593b0f3eeSJean-Francois Moine struct of_phandle_args args; 375693b0f3eeSJean-Francois Moine struct snd_soc_dai_link_component *component; 375793b0f3eeSJean-Francois Moine char *name; 375893b0f3eeSJean-Francois Moine int index, num_codecs, ret; 375993b0f3eeSJean-Francois Moine 376093b0f3eeSJean-Francois Moine /* Count the number of CODECs */ 376193b0f3eeSJean-Francois Moine name = "sound-dai"; 376293b0f3eeSJean-Francois Moine num_codecs = of_count_phandle_with_args(of_node, name, 376393b0f3eeSJean-Francois Moine "#sound-dai-cells"); 376493b0f3eeSJean-Francois Moine if (num_codecs <= 0) { 376593b0f3eeSJean-Francois Moine if (num_codecs == -ENOENT) 376693b0f3eeSJean-Francois Moine dev_err(dev, "No 'sound-dai' property\n"); 376793b0f3eeSJean-Francois Moine else 376893b0f3eeSJean-Francois Moine dev_err(dev, "Bad phandle in 'sound-dai'\n"); 376993b0f3eeSJean-Francois Moine return num_codecs; 377093b0f3eeSJean-Francois Moine } 3771a86854d0SKees Cook component = devm_kcalloc(dev, 3772a86854d0SKees Cook num_codecs, sizeof(*component), 377393b0f3eeSJean-Francois Moine GFP_KERNEL); 377493b0f3eeSJean-Francois Moine if (!component) 377593b0f3eeSJean-Francois Moine return -ENOMEM; 377693b0f3eeSJean-Francois Moine dai_link->codecs = component; 377793b0f3eeSJean-Francois Moine dai_link->num_codecs = num_codecs; 377893b0f3eeSJean-Francois Moine 377993b0f3eeSJean-Francois Moine /* Parse the list */ 378093b0f3eeSJean-Francois Moine for (index = 0, component = dai_link->codecs; 378193b0f3eeSJean-Francois Moine index < dai_link->num_codecs; 378293b0f3eeSJean-Francois Moine index++, component++) { 378393b0f3eeSJean-Francois Moine ret = of_parse_phandle_with_args(of_node, name, 378493b0f3eeSJean-Francois Moine "#sound-dai-cells", 378593b0f3eeSJean-Francois Moine index, &args); 378693b0f3eeSJean-Francois Moine if (ret) 378793b0f3eeSJean-Francois Moine goto err; 378893b0f3eeSJean-Francois Moine component->of_node = args.np; 378993b0f3eeSJean-Francois Moine ret = snd_soc_get_dai_name(&args, &component->dai_name); 379093b0f3eeSJean-Francois Moine if (ret < 0) 379193b0f3eeSJean-Francois Moine goto err; 379293b0f3eeSJean-Francois Moine } 379393b0f3eeSJean-Francois Moine return 0; 379493b0f3eeSJean-Francois Moine err: 379594685763SSylwester Nawrocki snd_soc_of_put_dai_link_codecs(dai_link); 379693b0f3eeSJean-Francois Moine dai_link->codecs = NULL; 379793b0f3eeSJean-Francois Moine dai_link->num_codecs = 0; 379893b0f3eeSJean-Francois Moine return ret; 379993b0f3eeSJean-Francois Moine } 380093b0f3eeSJean-Francois Moine EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_link_codecs); 380193b0f3eeSJean-Francois Moine 3802c9b3a40fSTakashi Iwai static int __init snd_soc_init(void) 3803db2a4165SFrank Mandarino { 38046553bf06SLars-Peter Clausen snd_soc_debugfs_init(); 3805fb257897SMark Brown snd_soc_util_init(); 3806fb257897SMark Brown 3807db2a4165SFrank Mandarino return platform_driver_register(&soc_driver); 3808db2a4165SFrank Mandarino } 38094abe8e16SMark Brown module_init(snd_soc_init); 3810db2a4165SFrank Mandarino 38117d8c16a6SMark Brown static void __exit snd_soc_exit(void) 3812db2a4165SFrank Mandarino { 3813fb257897SMark Brown snd_soc_util_exit(); 38146553bf06SLars-Peter Clausen snd_soc_debugfs_exit(); 3815fb257897SMark Brown 3816db2a4165SFrank Mandarino platform_driver_unregister(&soc_driver); 3817db2a4165SFrank Mandarino } 3818db2a4165SFrank Mandarino module_exit(snd_soc_exit); 3819db2a4165SFrank Mandarino 3820db2a4165SFrank Mandarino /* Module information */ 3821d331124dSLiam Girdwood MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk"); 3822db2a4165SFrank Mandarino MODULE_DESCRIPTION("ALSA SoC Core"); 3823db2a4165SFrank Mandarino MODULE_LICENSE("GPL"); 3824db2a4165SFrank Mandarino MODULE_ALIAS("platform:soc-audio"); 3825