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