xref: /openbmc/linux/sound/soc/soc-core.c (revision a655de808cbde6c58b3298e704d786b53f59fb5d)
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