xref: /openbmc/linux/sound/soc/soc-core.c (revision bc2632140435cc84f9817f1c362479b23dbdfebc)
1db2a4165SFrank Mandarino /*
2db2a4165SFrank Mandarino  * soc-core.c  --  ALSA SoC Audio Layer
3db2a4165SFrank Mandarino  *
4db2a4165SFrank Mandarino  * Copyright 2005 Wolfson Microelectronics PLC.
50664d888SLiam Girdwood  * Copyright 2005 Openedhand Ltd.
6f0fba2adSLiam Girdwood  * Copyright (C) 2010 Slimlogic Ltd.
7f0fba2adSLiam Girdwood  * Copyright (C) 2010 Texas Instruments Inc.
80664d888SLiam Girdwood  *
9d331124dSLiam Girdwood  * Author: Liam Girdwood <lrg@slimlogic.co.uk>
100664d888SLiam Girdwood  *         with code, comments and ideas from :-
110664d888SLiam Girdwood  *         Richard Purdie <richard@openedhand.com>
12db2a4165SFrank Mandarino  *
13db2a4165SFrank Mandarino  *  This program is free software; you can redistribute  it and/or modify it
14db2a4165SFrank Mandarino  *  under  the terms of  the GNU General  Public License as published by the
15db2a4165SFrank Mandarino  *  Free Software Foundation;  either version 2 of the  License, or (at your
16db2a4165SFrank Mandarino  *  option) any later version.
17db2a4165SFrank Mandarino  *
18db2a4165SFrank Mandarino  *  TODO:
19db2a4165SFrank Mandarino  *   o Add hw rules to enforce rates, etc.
20db2a4165SFrank Mandarino  *   o More testing with other codecs/machines.
21db2a4165SFrank Mandarino  *   o Add more codecs and platforms to ensure good API coverage.
22db2a4165SFrank Mandarino  *   o Support TDM on PCM and I2S
23db2a4165SFrank Mandarino  */
24db2a4165SFrank Mandarino 
25db2a4165SFrank Mandarino #include <linux/module.h>
26db2a4165SFrank Mandarino #include <linux/moduleparam.h>
27db2a4165SFrank Mandarino #include <linux/init.h>
28db2a4165SFrank Mandarino #include <linux/delay.h>
29db2a4165SFrank Mandarino #include <linux/pm.h>
30db2a4165SFrank Mandarino #include <linux/bitops.h>
3112ef193dSTroy Kisky #include <linux/debugfs.h>
32db2a4165SFrank Mandarino #include <linux/platform_device.h>
33741a509fSMarkus Pargmann #include <linux/pinctrl/consumer.h>
34f0e8ed85SMark Brown #include <linux/ctype.h>
355a0e3ad6STejun Heo #include <linux/slab.h>
36bec4fa05SStephen Warren #include <linux/of.h>
37db2a4165SFrank Mandarino #include <sound/core.h>
383028eb8cSMark Brown #include <sound/jack.h>
39db2a4165SFrank Mandarino #include <sound/pcm.h>
40db2a4165SFrank Mandarino #include <sound/pcm_params.h>
41db2a4165SFrank Mandarino #include <sound/soc.h>
4201d7584cSLiam Girdwood #include <sound/soc-dpcm.h>
43db2a4165SFrank Mandarino #include <sound/initval.h>
44db2a4165SFrank Mandarino 
45a8b1d34fSMark Brown #define CREATE_TRACE_POINTS
46a8b1d34fSMark Brown #include <trace/events/asoc.h>
47a8b1d34fSMark Brown 
48f0fba2adSLiam Girdwood #define NAME_SIZE	32
49f0fba2adSLiam Girdwood 
50384c89e2SMark Brown #ifdef CONFIG_DEBUG_FS
518a9dab1aSMark Brown struct dentry *snd_soc_debugfs_root;
528a9dab1aSMark Brown EXPORT_SYMBOL_GPL(snd_soc_debugfs_root);
53384c89e2SMark Brown #endif
54384c89e2SMark Brown 
55c5af3a2eSMark Brown static DEFINE_MUTEX(client_mutex);
5612a48a8cSMark Brown static LIST_HEAD(platform_list);
570d0cf00aSMark Brown static LIST_HEAD(codec_list);
58030e79f6SKuninori Morimoto static LIST_HEAD(component_list);
59c5af3a2eSMark Brown 
60db2a4165SFrank Mandarino /*
61db2a4165SFrank Mandarino  * This is a timeout to do a DAPM powerdown after a stream is closed().
62db2a4165SFrank Mandarino  * It can be used to eliminate pops between different playback streams, e.g.
63db2a4165SFrank Mandarino  * between two audio tracks.
64db2a4165SFrank Mandarino  */
65db2a4165SFrank Mandarino static int pmdown_time = 5000;
66db2a4165SFrank Mandarino module_param(pmdown_time, int, 0);
67db2a4165SFrank Mandarino MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)");
68db2a4165SFrank Mandarino 
692bc9a81eSDimitris Papastamos /* returns the minimum number of bytes needed to represent
702bc9a81eSDimitris Papastamos  * a particular given value */
712bc9a81eSDimitris Papastamos static int min_bytes_needed(unsigned long val)
722bc9a81eSDimitris Papastamos {
732bc9a81eSDimitris Papastamos 	int c = 0;
742bc9a81eSDimitris Papastamos 	int i;
752bc9a81eSDimitris Papastamos 
762bc9a81eSDimitris Papastamos 	for (i = (sizeof val * 8) - 1; i >= 0; --i, ++c)
772bc9a81eSDimitris Papastamos 		if (val & (1UL << i))
782bc9a81eSDimitris Papastamos 			break;
792bc9a81eSDimitris Papastamos 	c = (sizeof val * 8) - c;
802bc9a81eSDimitris Papastamos 	if (!c || (c % 8))
812bc9a81eSDimitris Papastamos 		c = (c + 8) / 8;
822bc9a81eSDimitris Papastamos 	else
832bc9a81eSDimitris Papastamos 		c /= 8;
842bc9a81eSDimitris Papastamos 	return c;
852bc9a81eSDimitris Papastamos }
862bc9a81eSDimitris Papastamos 
8713fd179fSDimitris Papastamos /* fill buf which is 'len' bytes with a formatted
8813fd179fSDimitris Papastamos  * string of the form 'reg: value\n' */
8913fd179fSDimitris Papastamos static int format_register_str(struct snd_soc_codec *codec,
9013fd179fSDimitris Papastamos 			       unsigned int reg, char *buf, size_t len)
912624d5faSMark Brown {
9200b317a4SStephen Warren 	int wordsize = min_bytes_needed(codec->driver->reg_cache_size) * 2;
9300b317a4SStephen Warren 	int regsize = codec->driver->reg_word_size * 2;
9413fd179fSDimitris Papastamos 	int ret;
9513fd179fSDimitris Papastamos 	char tmpbuf[len + 1];
9613fd179fSDimitris Papastamos 	char regbuf[regsize + 1];
9713fd179fSDimitris Papastamos 
9813fd179fSDimitris Papastamos 	/* since tmpbuf is allocated on the stack, warn the callers if they
9913fd179fSDimitris Papastamos 	 * try to abuse this function */
10013fd179fSDimitris Papastamos 	WARN_ON(len > 63);
10113fd179fSDimitris Papastamos 
10213fd179fSDimitris Papastamos 	/* +2 for ': ' and + 1 for '\n' */
10313fd179fSDimitris Papastamos 	if (wordsize + regsize + 2 + 1 != len)
10413fd179fSDimitris Papastamos 		return -EINVAL;
10513fd179fSDimitris Papastamos 
10613fd179fSDimitris Papastamos 	ret = snd_soc_read(codec, reg);
10713fd179fSDimitris Papastamos 	if (ret < 0) {
10813fd179fSDimitris Papastamos 		memset(regbuf, 'X', regsize);
10913fd179fSDimitris Papastamos 		regbuf[regsize] = '\0';
11013fd179fSDimitris Papastamos 	} else {
11113fd179fSDimitris Papastamos 		snprintf(regbuf, regsize + 1, "%.*x", regsize, ret);
11213fd179fSDimitris Papastamos 	}
11313fd179fSDimitris Papastamos 
11413fd179fSDimitris Papastamos 	/* prepare the buffer */
11513fd179fSDimitris Papastamos 	snprintf(tmpbuf, len + 1, "%.*x: %s\n", wordsize, reg, regbuf);
11613fd179fSDimitris Papastamos 	/* copy it back to the caller without the '\0' */
11713fd179fSDimitris Papastamos 	memcpy(buf, tmpbuf, len);
11813fd179fSDimitris Papastamos 
11913fd179fSDimitris Papastamos 	return 0;
12013fd179fSDimitris Papastamos }
12113fd179fSDimitris Papastamos 
12213fd179fSDimitris Papastamos /* codec register dump */
12313fd179fSDimitris Papastamos static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf,
12413fd179fSDimitris Papastamos 				  size_t count, loff_t pos)
12513fd179fSDimitris Papastamos {
12613fd179fSDimitris Papastamos 	int i, step = 1;
1272bc9a81eSDimitris Papastamos 	int wordsize, regsize;
12813fd179fSDimitris Papastamos 	int len;
12913fd179fSDimitris Papastamos 	size_t total = 0;
13013fd179fSDimitris Papastamos 	loff_t p = 0;
1312bc9a81eSDimitris Papastamos 
13200b317a4SStephen Warren 	wordsize = min_bytes_needed(codec->driver->reg_cache_size) * 2;
13300b317a4SStephen Warren 	regsize = codec->driver->reg_word_size * 2;
1342624d5faSMark Brown 
13513fd179fSDimitris Papastamos 	len = wordsize + regsize + 2 + 1;
13613fd179fSDimitris Papastamos 
137f0fba2adSLiam Girdwood 	if (!codec->driver->reg_cache_size)
1382624d5faSMark Brown 		return 0;
1392624d5faSMark Brown 
140f0fba2adSLiam Girdwood 	if (codec->driver->reg_cache_step)
141f0fba2adSLiam Girdwood 		step = codec->driver->reg_cache_step;
1422624d5faSMark Brown 
143f0fba2adSLiam Girdwood 	for (i = 0; i < codec->driver->reg_cache_size; i += step) {
14413fd179fSDimitris Papastamos 		/* only support larger than PAGE_SIZE bytes debugfs
14513fd179fSDimitris Papastamos 		 * entries for the default case */
14613fd179fSDimitris Papastamos 		if (p >= pos) {
14713fd179fSDimitris Papastamos 			if (total + len >= count - 1)
1482624d5faSMark Brown 				break;
14913fd179fSDimitris Papastamos 			format_register_str(codec, i, buf + total, len);
15013fd179fSDimitris Papastamos 			total += len;
15113fd179fSDimitris Papastamos 		}
15213fd179fSDimitris Papastamos 		p += len;
15313fd179fSDimitris Papastamos 	}
1542624d5faSMark Brown 
15513fd179fSDimitris Papastamos 	total = min(total, count - 1);
1562624d5faSMark Brown 
15713fd179fSDimitris Papastamos 	return total;
1582624d5faSMark Brown }
15913fd179fSDimitris Papastamos 
1602624d5faSMark Brown static ssize_t codec_reg_show(struct device *dev,
1612624d5faSMark Brown 	struct device_attribute *attr, char *buf)
1622624d5faSMark Brown {
16336ae1a96SMark Brown 	struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev);
164f0fba2adSLiam Girdwood 
16513fd179fSDimitris Papastamos 	return soc_codec_reg_show(rtd->codec, buf, PAGE_SIZE, 0);
1662624d5faSMark Brown }
1672624d5faSMark Brown 
1682624d5faSMark Brown static DEVICE_ATTR(codec_reg, 0444, codec_reg_show, NULL);
1692624d5faSMark Brown 
170dbe21408SMark Brown static ssize_t pmdown_time_show(struct device *dev,
171dbe21408SMark Brown 				struct device_attribute *attr, char *buf)
172dbe21408SMark Brown {
17336ae1a96SMark Brown 	struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev);
174dbe21408SMark Brown 
175f0fba2adSLiam Girdwood 	return sprintf(buf, "%ld\n", rtd->pmdown_time);
176dbe21408SMark Brown }
177dbe21408SMark Brown 
178dbe21408SMark Brown static ssize_t pmdown_time_set(struct device *dev,
179dbe21408SMark Brown 			       struct device_attribute *attr,
180dbe21408SMark Brown 			       const char *buf, size_t count)
181dbe21408SMark Brown {
18236ae1a96SMark Brown 	struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev);
183c593b520SMark Brown 	int ret;
184dbe21408SMark Brown 
185b785a492SJingoo Han 	ret = kstrtol(buf, 10, &rtd->pmdown_time);
186c593b520SMark Brown 	if (ret)
187c593b520SMark Brown 		return ret;
188dbe21408SMark Brown 
189dbe21408SMark Brown 	return count;
190dbe21408SMark Brown }
191dbe21408SMark Brown 
192dbe21408SMark Brown static DEVICE_ATTR(pmdown_time, 0644, pmdown_time_show, pmdown_time_set);
193dbe21408SMark Brown 
1942624d5faSMark Brown #ifdef CONFIG_DEBUG_FS
1952624d5faSMark Brown static ssize_t codec_reg_read_file(struct file *file, char __user *user_buf,
1962624d5faSMark Brown 				   size_t count, loff_t *ppos)
1972624d5faSMark Brown {
1982624d5faSMark Brown 	ssize_t ret;
1992624d5faSMark Brown 	struct snd_soc_codec *codec = file->private_data;
20013fd179fSDimitris Papastamos 	char *buf;
20113fd179fSDimitris Papastamos 
20213fd179fSDimitris Papastamos 	if (*ppos < 0 || !count)
20313fd179fSDimitris Papastamos 		return -EINVAL;
20413fd179fSDimitris Papastamos 
20513fd179fSDimitris Papastamos 	buf = kmalloc(count, GFP_KERNEL);
2062624d5faSMark Brown 	if (!buf)
2072624d5faSMark Brown 		return -ENOMEM;
20813fd179fSDimitris Papastamos 
20913fd179fSDimitris Papastamos 	ret = soc_codec_reg_show(codec, buf, count, *ppos);
21013fd179fSDimitris Papastamos 	if (ret >= 0) {
21113fd179fSDimitris Papastamos 		if (copy_to_user(user_buf, buf, ret)) {
21213fd179fSDimitris Papastamos 			kfree(buf);
21313fd179fSDimitris Papastamos 			return -EFAULT;
21413fd179fSDimitris Papastamos 		}
21513fd179fSDimitris Papastamos 		*ppos += ret;
21613fd179fSDimitris Papastamos 	}
21713fd179fSDimitris Papastamos 
2182624d5faSMark Brown 	kfree(buf);
2192624d5faSMark Brown 	return ret;
2202624d5faSMark Brown }
2212624d5faSMark Brown 
2222624d5faSMark Brown static ssize_t codec_reg_write_file(struct file *file,
2232624d5faSMark Brown 		const char __user *user_buf, size_t count, loff_t *ppos)
2242624d5faSMark Brown {
2252624d5faSMark Brown 	char buf[32];
22634e268d8SStephen Boyd 	size_t buf_size;
2272624d5faSMark Brown 	char *start = buf;
2282624d5faSMark Brown 	unsigned long reg, value;
2292624d5faSMark Brown 	struct snd_soc_codec *codec = file->private_data;
230b785a492SJingoo Han 	int ret;
2312624d5faSMark Brown 
2322624d5faSMark Brown 	buf_size = min(count, (sizeof(buf)-1));
2332624d5faSMark Brown 	if (copy_from_user(buf, user_buf, buf_size))
2342624d5faSMark Brown 		return -EFAULT;
2352624d5faSMark Brown 	buf[buf_size] = 0;
2362624d5faSMark Brown 
2372624d5faSMark Brown 	while (*start == ' ')
2382624d5faSMark Brown 		start++;
2392624d5faSMark Brown 	reg = simple_strtoul(start, &start, 16);
2402624d5faSMark Brown 	while (*start == ' ')
2412624d5faSMark Brown 		start++;
242b785a492SJingoo Han 	ret = kstrtoul(start, 16, &value);
243b785a492SJingoo Han 	if (ret)
244b785a492SJingoo Han 		return ret;
2450d51a9cbSMark Brown 
2460d51a9cbSMark Brown 	/* Userspace has been fiddling around behind the kernel's back */
247373d4d09SRusty Russell 	add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE);
2480d51a9cbSMark Brown 
249e4f078d8SDimitris Papastamos 	snd_soc_write(codec, reg, value);
2502624d5faSMark Brown 	return buf_size;
2512624d5faSMark Brown }
2522624d5faSMark Brown 
2532624d5faSMark Brown static const struct file_operations codec_reg_fops = {
254234e3405SStephen Boyd 	.open = simple_open,
2552624d5faSMark Brown 	.read = codec_reg_read_file,
2562624d5faSMark Brown 	.write = codec_reg_write_file,
2576038f373SArnd Bergmann 	.llseek = default_llseek,
2582624d5faSMark Brown };
2592624d5faSMark Brown 
26081c7cfd1SLars-Peter Clausen static void soc_init_component_debugfs(struct snd_soc_component *component)
261e73f3de5SRussell King {
26281c7cfd1SLars-Peter Clausen 	if (component->debugfs_prefix) {
26381c7cfd1SLars-Peter Clausen 		char *name;
264e73f3de5SRussell King 
26581c7cfd1SLars-Peter Clausen 		name = kasprintf(GFP_KERNEL, "%s:%s",
26681c7cfd1SLars-Peter Clausen 			component->debugfs_prefix, component->name);
26781c7cfd1SLars-Peter Clausen 		if (name) {
26881c7cfd1SLars-Peter Clausen 			component->debugfs_root = debugfs_create_dir(name,
26981c7cfd1SLars-Peter Clausen 				component->card->debugfs_card_root);
27081c7cfd1SLars-Peter Clausen 			kfree(name);
27181c7cfd1SLars-Peter Clausen 		}
27281c7cfd1SLars-Peter Clausen 	} else {
27381c7cfd1SLars-Peter Clausen 		component->debugfs_root = debugfs_create_dir(component->name,
27481c7cfd1SLars-Peter Clausen 				component->card->debugfs_card_root);
275e73f3de5SRussell King 	}
276e73f3de5SRussell King 
27781c7cfd1SLars-Peter Clausen 	if (!component->debugfs_root) {
27881c7cfd1SLars-Peter Clausen 		dev_warn(component->dev,
27981c7cfd1SLars-Peter Clausen 			"ASoC: Failed to create component debugfs directory\n");
2802624d5faSMark Brown 		return;
2812624d5faSMark Brown 	}
2822624d5faSMark Brown 
28381c7cfd1SLars-Peter Clausen 	snd_soc_dapm_debugfs_init(snd_soc_component_get_dapm(component),
28481c7cfd1SLars-Peter Clausen 		component->debugfs_root);
28581c7cfd1SLars-Peter Clausen 
28681c7cfd1SLars-Peter Clausen 	if (component->init_debugfs)
28781c7cfd1SLars-Peter Clausen 		component->init_debugfs(component);
28881c7cfd1SLars-Peter Clausen }
28981c7cfd1SLars-Peter Clausen 
29081c7cfd1SLars-Peter Clausen static void soc_cleanup_component_debugfs(struct snd_soc_component *component)
29181c7cfd1SLars-Peter Clausen {
29281c7cfd1SLars-Peter Clausen 	debugfs_remove_recursive(component->debugfs_root);
29381c7cfd1SLars-Peter Clausen }
29481c7cfd1SLars-Peter Clausen 
29581c7cfd1SLars-Peter Clausen static void soc_init_codec_debugfs(struct snd_soc_component *component)
29681c7cfd1SLars-Peter Clausen {
29781c7cfd1SLars-Peter Clausen 	struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
29881c7cfd1SLars-Peter Clausen 
29981c7cfd1SLars-Peter Clausen 	debugfs_create_bool("cache_sync", 0444, codec->component.debugfs_root,
300aaee8ef1SMark Brown 			    &codec->cache_sync);
301aaee8ef1SMark Brown 
3022624d5faSMark Brown 	codec->debugfs_reg = debugfs_create_file("codec_reg", 0644,
30381c7cfd1SLars-Peter Clausen 						 codec->component.debugfs_root,
3042624d5faSMark Brown 						 codec, &codec_reg_fops);
3052624d5faSMark Brown 	if (!codec->debugfs_reg)
30610e8aa9aSMichał Mirosław 		dev_warn(codec->dev,
30710e8aa9aSMichał Mirosław 			"ASoC: Failed to create codec register debugfs file\n");
308731f1ab2SSebastien Guiriec }
309731f1ab2SSebastien Guiriec 
310c3c5a19aSMark Brown static ssize_t codec_list_read_file(struct file *file, char __user *user_buf,
311c3c5a19aSMark Brown 				    size_t count, loff_t *ppos)
312c3c5a19aSMark Brown {
313c3c5a19aSMark Brown 	char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
3142b194f9dSMark Brown 	ssize_t len, ret = 0;
315c3c5a19aSMark Brown 	struct snd_soc_codec *codec;
316c3c5a19aSMark Brown 
317c3c5a19aSMark Brown 	if (!buf)
318c3c5a19aSMark Brown 		return -ENOMEM;
319c3c5a19aSMark Brown 
3202b194f9dSMark Brown 	list_for_each_entry(codec, &codec_list, list) {
3212b194f9dSMark Brown 		len = snprintf(buf + ret, PAGE_SIZE - ret, "%s\n",
322f4333203SLars-Peter Clausen 			       codec->component.name);
3232b194f9dSMark Brown 		if (len >= 0)
3242b194f9dSMark Brown 			ret += len;
3252b194f9dSMark Brown 		if (ret > PAGE_SIZE) {
3262b194f9dSMark Brown 			ret = PAGE_SIZE;
3272b194f9dSMark Brown 			break;
3282b194f9dSMark Brown 		}
3292b194f9dSMark Brown 	}
330c3c5a19aSMark Brown 
331c3c5a19aSMark Brown 	if (ret >= 0)
332c3c5a19aSMark Brown 		ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
333c3c5a19aSMark Brown 
334c3c5a19aSMark Brown 	kfree(buf);
335c3c5a19aSMark Brown 
336c3c5a19aSMark Brown 	return ret;
337c3c5a19aSMark Brown }
338c3c5a19aSMark Brown 
339c3c5a19aSMark Brown static const struct file_operations codec_list_fops = {
340c3c5a19aSMark Brown 	.read = codec_list_read_file,
341c3c5a19aSMark Brown 	.llseek = default_llseek,/* read accesses f_pos */
342c3c5a19aSMark Brown };
343c3c5a19aSMark Brown 
344f3208780SMark Brown static ssize_t dai_list_read_file(struct file *file, char __user *user_buf,
345f3208780SMark Brown 				  size_t count, loff_t *ppos)
346f3208780SMark Brown {
347f3208780SMark Brown 	char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
3482b194f9dSMark Brown 	ssize_t len, ret = 0;
3491438c2f6SLars-Peter Clausen 	struct snd_soc_component *component;
350f3208780SMark Brown 	struct snd_soc_dai *dai;
351f3208780SMark Brown 
352f3208780SMark Brown 	if (!buf)
353f3208780SMark Brown 		return -ENOMEM;
354f3208780SMark Brown 
3551438c2f6SLars-Peter Clausen 	list_for_each_entry(component, &component_list, list) {
3561438c2f6SLars-Peter Clausen 		list_for_each_entry(dai, &component->dai_list, list) {
3571438c2f6SLars-Peter Clausen 			len = snprintf(buf + ret, PAGE_SIZE - ret, "%s\n",
3581438c2f6SLars-Peter Clausen 				dai->name);
3592b194f9dSMark Brown 			if (len >= 0)
3602b194f9dSMark Brown 				ret += len;
3612b194f9dSMark Brown 			if (ret > PAGE_SIZE) {
3622b194f9dSMark Brown 				ret = PAGE_SIZE;
3632b194f9dSMark Brown 				break;
3642b194f9dSMark Brown 			}
3652b194f9dSMark Brown 		}
3661438c2f6SLars-Peter Clausen 	}
367f3208780SMark Brown 
368f3208780SMark Brown 	ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
369f3208780SMark Brown 
370f3208780SMark Brown 	kfree(buf);
371f3208780SMark Brown 
372f3208780SMark Brown 	return ret;
373f3208780SMark Brown }
374f3208780SMark Brown 
375f3208780SMark Brown static const struct file_operations dai_list_fops = {
376f3208780SMark Brown 	.read = dai_list_read_file,
377f3208780SMark Brown 	.llseek = default_llseek,/* read accesses f_pos */
378f3208780SMark Brown };
379f3208780SMark Brown 
38019c7ac27SMark Brown static ssize_t platform_list_read_file(struct file *file,
38119c7ac27SMark Brown 				       char __user *user_buf,
38219c7ac27SMark Brown 				       size_t count, loff_t *ppos)
38319c7ac27SMark Brown {
38419c7ac27SMark Brown 	char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
3852b194f9dSMark Brown 	ssize_t len, ret = 0;
38619c7ac27SMark Brown 	struct snd_soc_platform *platform;
38719c7ac27SMark Brown 
38819c7ac27SMark Brown 	if (!buf)
38919c7ac27SMark Brown 		return -ENOMEM;
39019c7ac27SMark Brown 
3912b194f9dSMark Brown 	list_for_each_entry(platform, &platform_list, list) {
3922b194f9dSMark Brown 		len = snprintf(buf + ret, PAGE_SIZE - ret, "%s\n",
393f4333203SLars-Peter Clausen 			       platform->component.name);
3942b194f9dSMark Brown 		if (len >= 0)
3952b194f9dSMark Brown 			ret += len;
3962b194f9dSMark Brown 		if (ret > PAGE_SIZE) {
3972b194f9dSMark Brown 			ret = PAGE_SIZE;
3982b194f9dSMark Brown 			break;
3992b194f9dSMark Brown 		}
4002b194f9dSMark Brown 	}
40119c7ac27SMark Brown 
40219c7ac27SMark Brown 	ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
40319c7ac27SMark Brown 
40419c7ac27SMark Brown 	kfree(buf);
40519c7ac27SMark Brown 
40619c7ac27SMark Brown 	return ret;
40719c7ac27SMark Brown }
40819c7ac27SMark Brown 
40919c7ac27SMark Brown static const struct file_operations platform_list_fops = {
41019c7ac27SMark Brown 	.read = platform_list_read_file,
41119c7ac27SMark Brown 	.llseek = default_llseek,/* read accesses f_pos */
41219c7ac27SMark Brown };
41319c7ac27SMark Brown 
414a6052154SJarkko Nikula static void soc_init_card_debugfs(struct snd_soc_card *card)
415a6052154SJarkko Nikula {
416a6052154SJarkko Nikula 	card->debugfs_card_root = debugfs_create_dir(card->name,
4178a9dab1aSMark Brown 						     snd_soc_debugfs_root);
4183a45b867SJarkko Nikula 	if (!card->debugfs_card_root) {
419a6052154SJarkko Nikula 		dev_warn(card->dev,
4207c08be84SLothar Waßmann 			 "ASoC: Failed to create card debugfs directory\n");
4213a45b867SJarkko Nikula 		return;
4223a45b867SJarkko Nikula 	}
4233a45b867SJarkko Nikula 
4243a45b867SJarkko Nikula 	card->debugfs_pop_time = debugfs_create_u32("dapm_pop_time", 0644,
4253a45b867SJarkko Nikula 						    card->debugfs_card_root,
4263a45b867SJarkko Nikula 						    &card->pop_time);
4273a45b867SJarkko Nikula 	if (!card->debugfs_pop_time)
4283a45b867SJarkko Nikula 		dev_warn(card->dev,
429f110bfc7SLiam Girdwood 		       "ASoC: Failed to create pop time debugfs file\n");
430a6052154SJarkko Nikula }
431a6052154SJarkko Nikula 
432a6052154SJarkko Nikula static void soc_cleanup_card_debugfs(struct snd_soc_card *card)
433a6052154SJarkko Nikula {
434a6052154SJarkko Nikula 	debugfs_remove_recursive(card->debugfs_card_root);
435a6052154SJarkko Nikula }
436a6052154SJarkko Nikula 
4372624d5faSMark Brown #else
4382624d5faSMark Brown 
43981c7cfd1SLars-Peter Clausen #define soc_init_codec_debugfs NULL
44081c7cfd1SLars-Peter Clausen 
44181c7cfd1SLars-Peter Clausen static inline void soc_init_component_debugfs(
44281c7cfd1SLars-Peter Clausen 	struct snd_soc_component *component)
4432624d5faSMark Brown {
4442624d5faSMark Brown }
4452624d5faSMark Brown 
44681c7cfd1SLars-Peter Clausen static inline void soc_cleanup_component_debugfs(
44781c7cfd1SLars-Peter Clausen 	struct snd_soc_component *component)
448731f1ab2SSebastien Guiriec {
449731f1ab2SSebastien Guiriec }
450731f1ab2SSebastien Guiriec 
451b95fccbcSAxel Lin static inline void soc_init_card_debugfs(struct snd_soc_card *card)
452b95fccbcSAxel Lin {
453b95fccbcSAxel Lin }
454b95fccbcSAxel Lin 
455b95fccbcSAxel Lin static inline void soc_cleanup_card_debugfs(struct snd_soc_card *card)
456b95fccbcSAxel Lin {
457b95fccbcSAxel Lin }
4582624d5faSMark Brown #endif
4592624d5faSMark Brown 
46047c88fffSLiam Girdwood struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card,
46147c88fffSLiam Girdwood 		const char *dai_link, int stream)
46247c88fffSLiam Girdwood {
46347c88fffSLiam Girdwood 	int i;
46447c88fffSLiam Girdwood 
46547c88fffSLiam Girdwood 	for (i = 0; i < card->num_links; i++) {
46647c88fffSLiam Girdwood 		if (card->rtd[i].dai_link->no_pcm &&
46747c88fffSLiam Girdwood 			!strcmp(card->rtd[i].dai_link->name, dai_link))
46847c88fffSLiam Girdwood 			return card->rtd[i].pcm->streams[stream].substream;
46947c88fffSLiam Girdwood 	}
470f110bfc7SLiam Girdwood 	dev_dbg(card->dev, "ASoC: failed to find dai link %s\n", dai_link);
47147c88fffSLiam Girdwood 	return NULL;
47247c88fffSLiam Girdwood }
47347c88fffSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_get_dai_substream);
47447c88fffSLiam Girdwood 
47547c88fffSLiam Girdwood struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card,
47647c88fffSLiam Girdwood 		const char *dai_link)
47747c88fffSLiam Girdwood {
47847c88fffSLiam Girdwood 	int i;
47947c88fffSLiam Girdwood 
48047c88fffSLiam Girdwood 	for (i = 0; i < card->num_links; i++) {
48147c88fffSLiam Girdwood 		if (!strcmp(card->rtd[i].dai_link->name, dai_link))
48247c88fffSLiam Girdwood 			return &card->rtd[i];
48347c88fffSLiam Girdwood 	}
484f110bfc7SLiam Girdwood 	dev_dbg(card->dev, "ASoC: failed to find rtd %s\n", dai_link);
48547c88fffSLiam Girdwood 	return NULL;
48647c88fffSLiam Girdwood }
48747c88fffSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_get_pcm_runtime);
48847c88fffSLiam Girdwood 
4899d58a077SRichard Fitzgerald static void codec2codec_close_delayed_work(struct work_struct *work)
4909d58a077SRichard Fitzgerald {
4919d58a077SRichard Fitzgerald 	/* Currently nothing to do for c2c links
4929d58a077SRichard Fitzgerald 	 * Since c2c links are internal nodes in the DAPM graph and
4939d58a077SRichard Fitzgerald 	 * don't interface with the outside world or application layer
4949d58a077SRichard Fitzgerald 	 * we don't have to do any special handling on close.
4959d58a077SRichard Fitzgerald 	 */
4969d58a077SRichard Fitzgerald }
4979d58a077SRichard Fitzgerald 
4986f8ab4acSMark Brown #ifdef CONFIG_PM_SLEEP
499db2a4165SFrank Mandarino /* powers down audio subsystem for suspend */
5006f8ab4acSMark Brown int snd_soc_suspend(struct device *dev)
501db2a4165SFrank Mandarino {
5026f8ab4acSMark Brown 	struct snd_soc_card *card = dev_get_drvdata(dev);
5032eea392dSJarkko Nikula 	struct snd_soc_codec *codec;
50488bd870fSBenoit Cousson 	int i, j;
505db2a4165SFrank Mandarino 
506c5599b87SLars-Peter Clausen 	/* If the card is not initialized yet there is nothing to do */
507c5599b87SLars-Peter Clausen 	if (!card->instantiated)
508e3509ff0SDaniel Mack 		return 0;
509e3509ff0SDaniel Mack 
5106ed25978SAndy Green 	/* Due to the resume being scheduled into a workqueue we could
5116ed25978SAndy Green 	* suspend before that's finished - wait for it to complete.
5126ed25978SAndy Green 	 */
513f0fba2adSLiam Girdwood 	snd_power_lock(card->snd_card);
514f0fba2adSLiam Girdwood 	snd_power_wait(card->snd_card, SNDRV_CTL_POWER_D0);
515f0fba2adSLiam Girdwood 	snd_power_unlock(card->snd_card);
5166ed25978SAndy Green 
5176ed25978SAndy Green 	/* we're going to block userspace touching us until resume completes */
518f0fba2adSLiam Girdwood 	snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D3hot);
5196ed25978SAndy Green 
520a00f90f9SMark Brown 	/* mute any active DACs */
521f0fba2adSLiam Girdwood 	for (i = 0; i < card->num_rtd; i++) {
5223efab7dcSMark Brown 
523f0fba2adSLiam Girdwood 		if (card->rtd[i].dai_link->ignore_suspend)
5243efab7dcSMark Brown 			continue;
5253efab7dcSMark Brown 
52688bd870fSBenoit Cousson 		for (j = 0; j < card->rtd[i].num_codecs; j++) {
52788bd870fSBenoit Cousson 			struct snd_soc_dai *dai = card->rtd[i].codec_dais[j];
52888bd870fSBenoit Cousson 			struct snd_soc_dai_driver *drv = dai->driver;
52988bd870fSBenoit Cousson 
530f0fba2adSLiam Girdwood 			if (drv->ops->digital_mute && dai->playback_active)
531f0fba2adSLiam Girdwood 				drv->ops->digital_mute(dai, 1);
532db2a4165SFrank Mandarino 		}
53388bd870fSBenoit Cousson 	}
534db2a4165SFrank Mandarino 
5354ccab3e7SLiam Girdwood 	/* suspend all pcms */
536f0fba2adSLiam Girdwood 	for (i = 0; i < card->num_rtd; i++) {
537f0fba2adSLiam Girdwood 		if (card->rtd[i].dai_link->ignore_suspend)
5383efab7dcSMark Brown 			continue;
5393efab7dcSMark Brown 
540f0fba2adSLiam Girdwood 		snd_pcm_suspend_all(card->rtd[i].pcm);
5413efab7dcSMark Brown 	}
5424ccab3e7SLiam Girdwood 
54387506549SMark Brown 	if (card->suspend_pre)
54470b2ac12SMark Brown 		card->suspend_pre(card);
545db2a4165SFrank Mandarino 
546f0fba2adSLiam Girdwood 	for (i = 0; i < card->num_rtd; i++) {
547f0fba2adSLiam Girdwood 		struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
548f0fba2adSLiam Girdwood 		struct snd_soc_platform *platform = card->rtd[i].platform;
5493efab7dcSMark Brown 
550f0fba2adSLiam Girdwood 		if (card->rtd[i].dai_link->ignore_suspend)
5513efab7dcSMark Brown 			continue;
5523efab7dcSMark Brown 
553*bc263214SLars-Peter Clausen 		if (cpu_dai->driver->suspend && !cpu_dai->driver->bus_control)
554f0fba2adSLiam Girdwood 			cpu_dai->driver->suspend(cpu_dai);
555f0fba2adSLiam Girdwood 		if (platform->driver->suspend && !platform->suspended) {
556f0fba2adSLiam Girdwood 			platform->driver->suspend(cpu_dai);
557f0fba2adSLiam Girdwood 			platform->suspended = 1;
558f0fba2adSLiam Girdwood 		}
559db2a4165SFrank Mandarino 	}
560db2a4165SFrank Mandarino 
561db2a4165SFrank Mandarino 	/* close any waiting streams and save state */
562f0fba2adSLiam Girdwood 	for (i = 0; i < card->num_rtd; i++) {
56388bd870fSBenoit Cousson 		struct snd_soc_dai **codec_dais = card->rtd[i].codec_dais;
56443829731STejun Heo 		flush_delayed_work(&card->rtd[i].delayed_work);
56588bd870fSBenoit Cousson 		for (j = 0; j < card->rtd[i].num_codecs; j++) {
56688bd870fSBenoit Cousson 			codec_dais[j]->codec->dapm.suspend_bias_level =
56788bd870fSBenoit Cousson 					codec_dais[j]->codec->dapm.bias_level;
56888bd870fSBenoit Cousson 		}
569f0fba2adSLiam Girdwood 	}
570db2a4165SFrank Mandarino 
571f0fba2adSLiam Girdwood 	for (i = 0; i < card->num_rtd; i++) {
5723efab7dcSMark Brown 
573f0fba2adSLiam Girdwood 		if (card->rtd[i].dai_link->ignore_suspend)
5743efab7dcSMark Brown 			continue;
5753efab7dcSMark Brown 
5767bd3a6f3SMark Brown 		snd_soc_dapm_stream_event(&card->rtd[i],
5777bd3a6f3SMark Brown 					  SNDRV_PCM_STREAM_PLAYBACK,
578db2a4165SFrank Mandarino 					  SND_SOC_DAPM_STREAM_SUSPEND);
579f0fba2adSLiam Girdwood 
5807bd3a6f3SMark Brown 		snd_soc_dapm_stream_event(&card->rtd[i],
5817bd3a6f3SMark Brown 					  SNDRV_PCM_STREAM_CAPTURE,
582db2a4165SFrank Mandarino 					  SND_SOC_DAPM_STREAM_SUSPEND);
583db2a4165SFrank Mandarino 	}
584db2a4165SFrank Mandarino 
585e2d32ff6SMark Brown 	/* Recheck all analogue paths too */
586e2d32ff6SMark Brown 	dapm_mark_io_dirty(&card->dapm);
587e2d32ff6SMark Brown 	snd_soc_dapm_sync(&card->dapm);
588e2d32ff6SMark Brown 
589f0fba2adSLiam Girdwood 	/* suspend all CODECs */
5902eea392dSJarkko Nikula 	list_for_each_entry(codec, &card->codec_dev_list, card_list) {
5911547aba9SMark Brown 		/* If there are paths active then the CODEC will be held with
5921547aba9SMark Brown 		 * bias _ON and should not be suspended. */
593a8093297SLars-Peter Clausen 		if (!codec->suspended) {
594ce6120ccSLiam Girdwood 			switch (codec->dapm.bias_level) {
5951547aba9SMark Brown 			case SND_SOC_BIAS_STANDBY:
596125a25daSMark Brown 				/*
597125a25daSMark Brown 				 * If the CODEC is capable of idle
598125a25daSMark Brown 				 * bias off then being in STANDBY
599125a25daSMark Brown 				 * means it's doing something,
600125a25daSMark Brown 				 * otherwise fall through.
601125a25daSMark Brown 				 */
602125a25daSMark Brown 				if (codec->dapm.idle_bias_off) {
603125a25daSMark Brown 					dev_dbg(codec->dev,
60410e8aa9aSMichał Mirosław 						"ASoC: idle_bias_off CODEC on over suspend\n");
605125a25daSMark Brown 					break;
606125a25daSMark Brown 				}
607a8093297SLars-Peter Clausen 
6081547aba9SMark Brown 			case SND_SOC_BIAS_OFF:
609a8093297SLars-Peter Clausen 				if (codec->driver->suspend)
61084b315eeSLars-Peter Clausen 					codec->driver->suspend(codec);
611f0fba2adSLiam Girdwood 				codec->suspended = 1;
6127be4ba24SMark Brown 				codec->cache_sync = 1;
613e2c330b9SLars-Peter Clausen 				if (codec->component.regmap)
614e2c330b9SLars-Peter Clausen 					regcache_mark_dirty(codec->component.regmap);
615988e8cc4SNicolin Chen 				/* deactivate pins to sleep state */
616988e8cc4SNicolin Chen 				pinctrl_pm_select_sleep_state(codec->dev);
6171547aba9SMark Brown 				break;
6181547aba9SMark Brown 			default:
61910e8aa9aSMichał Mirosław 				dev_dbg(codec->dev,
62010e8aa9aSMichał Mirosław 					"ASoC: CODEC is on over suspend\n");
6211547aba9SMark Brown 				break;
6221547aba9SMark Brown 			}
6231547aba9SMark Brown 		}
624f0fba2adSLiam Girdwood 	}
625db2a4165SFrank Mandarino 
626f0fba2adSLiam Girdwood 	for (i = 0; i < card->num_rtd; i++) {
627f0fba2adSLiam Girdwood 		struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
6283efab7dcSMark Brown 
629f0fba2adSLiam Girdwood 		if (card->rtd[i].dai_link->ignore_suspend)
6303efab7dcSMark Brown 			continue;
6313efab7dcSMark Brown 
632*bc263214SLars-Peter Clausen 		if (cpu_dai->driver->suspend && cpu_dai->driver->bus_control)
633f0fba2adSLiam Girdwood 			cpu_dai->driver->suspend(cpu_dai);
634988e8cc4SNicolin Chen 
635988e8cc4SNicolin Chen 		/* deactivate pins to sleep state */
636988e8cc4SNicolin Chen 		pinctrl_pm_select_sleep_state(cpu_dai->dev);
637db2a4165SFrank Mandarino 	}
638db2a4165SFrank Mandarino 
63987506549SMark Brown 	if (card->suspend_post)
64070b2ac12SMark Brown 		card->suspend_post(card);
641db2a4165SFrank Mandarino 
642db2a4165SFrank Mandarino 	return 0;
643db2a4165SFrank Mandarino }
6446f8ab4acSMark Brown EXPORT_SYMBOL_GPL(snd_soc_suspend);
645db2a4165SFrank Mandarino 
6466ed25978SAndy Green /* deferred resume work, so resume can complete before we finished
6476ed25978SAndy Green  * setting our codec back up, which can be very slow on I2C
6486ed25978SAndy Green  */
6496ed25978SAndy Green static void soc_resume_deferred(struct work_struct *work)
650db2a4165SFrank Mandarino {
651f0fba2adSLiam Girdwood 	struct snd_soc_card *card =
652f0fba2adSLiam Girdwood 			container_of(work, struct snd_soc_card, deferred_resume_work);
6532eea392dSJarkko Nikula 	struct snd_soc_codec *codec;
65488bd870fSBenoit Cousson 	int i, j;
655db2a4165SFrank Mandarino 
6566ed25978SAndy Green 	/* our power state is still SNDRV_CTL_POWER_D3hot from suspend time,
6576ed25978SAndy Green 	 * so userspace apps are blocked from touching us
6586ed25978SAndy Green 	 */
6596ed25978SAndy Green 
660f110bfc7SLiam Girdwood 	dev_dbg(card->dev, "ASoC: starting resume work\n");
6616ed25978SAndy Green 
6629949788bSMark Brown 	/* Bring us up into D2 so that DAPM starts enabling things */
663f0fba2adSLiam Girdwood 	snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D2);
6649949788bSMark Brown 
66587506549SMark Brown 	if (card->resume_pre)
66670b2ac12SMark Brown 		card->resume_pre(card);
667db2a4165SFrank Mandarino 
668*bc263214SLars-Peter Clausen 	/* resume control bus DAIs */
669f0fba2adSLiam Girdwood 	for (i = 0; i < card->num_rtd; i++) {
670f0fba2adSLiam Girdwood 		struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
6713efab7dcSMark Brown 
672f0fba2adSLiam Girdwood 		if (card->rtd[i].dai_link->ignore_suspend)
6733efab7dcSMark Brown 			continue;
6743efab7dcSMark Brown 
675*bc263214SLars-Peter Clausen 		if (cpu_dai->driver->resume && cpu_dai->driver->bus_control)
676f0fba2adSLiam Girdwood 			cpu_dai->driver->resume(cpu_dai);
677db2a4165SFrank Mandarino 	}
678db2a4165SFrank Mandarino 
6792eea392dSJarkko Nikula 	list_for_each_entry(codec, &card->codec_dev_list, card_list) {
6801547aba9SMark Brown 		/* If the CODEC was idle over suspend then it will have been
6811547aba9SMark Brown 		 * left with bias OFF or STANDBY and suspended so we must now
6821547aba9SMark Brown 		 * resume.  Otherwise the suspend was suppressed.
6831547aba9SMark Brown 		 */
684a8093297SLars-Peter Clausen 		if (codec->suspended) {
685ce6120ccSLiam Girdwood 			switch (codec->dapm.bias_level) {
6861547aba9SMark Brown 			case SND_SOC_BIAS_STANDBY:
6871547aba9SMark Brown 			case SND_SOC_BIAS_OFF:
688a8093297SLars-Peter Clausen 				if (codec->driver->resume)
689f0fba2adSLiam Girdwood 					codec->driver->resume(codec);
690f0fba2adSLiam Girdwood 				codec->suspended = 0;
6911547aba9SMark Brown 				break;
6921547aba9SMark Brown 			default:
69310e8aa9aSMichał Mirosław 				dev_dbg(codec->dev,
69410e8aa9aSMichał Mirosław 					"ASoC: CODEC was on over suspend\n");
6951547aba9SMark Brown 				break;
6961547aba9SMark Brown 			}
6971547aba9SMark Brown 		}
698f0fba2adSLiam Girdwood 	}
699db2a4165SFrank Mandarino 
700f0fba2adSLiam Girdwood 	for (i = 0; i < card->num_rtd; i++) {
7013efab7dcSMark Brown 
702f0fba2adSLiam Girdwood 		if (card->rtd[i].dai_link->ignore_suspend)
7033efab7dcSMark Brown 			continue;
7043efab7dcSMark Brown 
7057bd3a6f3SMark Brown 		snd_soc_dapm_stream_event(&card->rtd[i],
706d9b0951bSLiam Girdwood 					  SNDRV_PCM_STREAM_PLAYBACK,
707db2a4165SFrank Mandarino 					  SND_SOC_DAPM_STREAM_RESUME);
708f0fba2adSLiam Girdwood 
7097bd3a6f3SMark Brown 		snd_soc_dapm_stream_event(&card->rtd[i],
710d9b0951bSLiam Girdwood 					  SNDRV_PCM_STREAM_CAPTURE,
711db2a4165SFrank Mandarino 					  SND_SOC_DAPM_STREAM_RESUME);
712db2a4165SFrank Mandarino 	}
713db2a4165SFrank Mandarino 
7143ff3f64bSMark Brown 	/* unmute any active DACs */
715f0fba2adSLiam Girdwood 	for (i = 0; i < card->num_rtd; i++) {
7163efab7dcSMark Brown 
717f0fba2adSLiam Girdwood 		if (card->rtd[i].dai_link->ignore_suspend)
7183efab7dcSMark Brown 			continue;
7193efab7dcSMark Brown 
72088bd870fSBenoit Cousson 		for (j = 0; j < card->rtd[i].num_codecs; j++) {
72188bd870fSBenoit Cousson 			struct snd_soc_dai *dai = card->rtd[i].codec_dais[j];
72288bd870fSBenoit Cousson 			struct snd_soc_dai_driver *drv = dai->driver;
72388bd870fSBenoit Cousson 
724f0fba2adSLiam Girdwood 			if (drv->ops->digital_mute && dai->playback_active)
725f0fba2adSLiam Girdwood 				drv->ops->digital_mute(dai, 0);
726db2a4165SFrank Mandarino 		}
72788bd870fSBenoit Cousson 	}
728db2a4165SFrank Mandarino 
729f0fba2adSLiam Girdwood 	for (i = 0; i < card->num_rtd; i++) {
730f0fba2adSLiam Girdwood 		struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
731f0fba2adSLiam Girdwood 		struct snd_soc_platform *platform = card->rtd[i].platform;
7323efab7dcSMark Brown 
733f0fba2adSLiam Girdwood 		if (card->rtd[i].dai_link->ignore_suspend)
7343efab7dcSMark Brown 			continue;
7353efab7dcSMark Brown 
736*bc263214SLars-Peter Clausen 		if (cpu_dai->driver->resume && !cpu_dai->driver->bus_control)
737f0fba2adSLiam Girdwood 			cpu_dai->driver->resume(cpu_dai);
738f0fba2adSLiam Girdwood 		if (platform->driver->resume && platform->suspended) {
739f0fba2adSLiam Girdwood 			platform->driver->resume(cpu_dai);
740f0fba2adSLiam Girdwood 			platform->suspended = 0;
741f0fba2adSLiam Girdwood 		}
742db2a4165SFrank Mandarino 	}
743db2a4165SFrank Mandarino 
74487506549SMark Brown 	if (card->resume_post)
74570b2ac12SMark Brown 		card->resume_post(card);
746db2a4165SFrank Mandarino 
747f110bfc7SLiam Girdwood 	dev_dbg(card->dev, "ASoC: resume work completed\n");
7486ed25978SAndy Green 
7496ed25978SAndy Green 	/* userspace can access us now we are back as we were before */
750f0fba2adSLiam Girdwood 	snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D0);
751e2d32ff6SMark Brown 
752e2d32ff6SMark Brown 	/* Recheck all analogue paths too */
753e2d32ff6SMark Brown 	dapm_mark_io_dirty(&card->dapm);
754e2d32ff6SMark Brown 	snd_soc_dapm_sync(&card->dapm);
7556ed25978SAndy Green }
7566ed25978SAndy Green 
7576ed25978SAndy Green /* powers up audio subsystem after a suspend */
7586f8ab4acSMark Brown int snd_soc_resume(struct device *dev)
7596ed25978SAndy Green {
7606f8ab4acSMark Brown 	struct snd_soc_card *card = dev_get_drvdata(dev);
761*bc263214SLars-Peter Clausen 	bool bus_control = false;
762*bc263214SLars-Peter Clausen 	int i;
763b9dd94a8SPeter Ujfalusi 
764c5599b87SLars-Peter Clausen 	/* If the card is not initialized yet there is nothing to do */
765c5599b87SLars-Peter Clausen 	if (!card->instantiated)
7665ff1ddf2SEric Miao 		return 0;
7675ff1ddf2SEric Miao 
768988e8cc4SNicolin Chen 	/* activate pins from sleep state */
769988e8cc4SNicolin Chen 	for (i = 0; i < card->num_rtd; i++) {
77088bd870fSBenoit Cousson 		struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
77188bd870fSBenoit Cousson 		struct snd_soc_dai **codec_dais = rtd->codec_dais;
77288bd870fSBenoit Cousson 		struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
77388bd870fSBenoit Cousson 		int j;
77488bd870fSBenoit Cousson 
775988e8cc4SNicolin Chen 		if (cpu_dai->active)
776988e8cc4SNicolin Chen 			pinctrl_pm_select_default_state(cpu_dai->dev);
77788bd870fSBenoit Cousson 
77888bd870fSBenoit Cousson 		for (j = 0; j < rtd->num_codecs; j++) {
77988bd870fSBenoit Cousson 			struct snd_soc_dai *codec_dai = codec_dais[j];
780988e8cc4SNicolin Chen 			if (codec_dai->active)
781988e8cc4SNicolin Chen 				pinctrl_pm_select_default_state(codec_dai->dev);
782988e8cc4SNicolin Chen 		}
78388bd870fSBenoit Cousson 	}
784988e8cc4SNicolin Chen 
785*bc263214SLars-Peter Clausen 	/*
786*bc263214SLars-Peter Clausen 	 * DAIs that also act as the control bus master might have other drivers
787*bc263214SLars-Peter Clausen 	 * hanging off them so need to resume immediately. Other drivers don't
788*bc263214SLars-Peter Clausen 	 * have that problem and may take a substantial amount of time to resume
78964ab9baaSMark Brown 	 * due to I/O costs and anti-pop so handle them out of line.
79064ab9baaSMark Brown 	 */
791f0fba2adSLiam Girdwood 	for (i = 0; i < card->num_rtd; i++) {
792f0fba2adSLiam Girdwood 		struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
793*bc263214SLars-Peter Clausen 		bus_control |= cpu_dai->driver->bus_control;
79482e14e8bSStephen Warren 	}
795*bc263214SLars-Peter Clausen 	if (bus_control) {
796*bc263214SLars-Peter Clausen 		dev_dbg(dev, "ASoC: Resuming control bus master immediately\n");
79764ab9baaSMark Brown 		soc_resume_deferred(&card->deferred_resume_work);
79864ab9baaSMark Brown 	} else {
799f110bfc7SLiam Girdwood 		dev_dbg(dev, "ASoC: Scheduling resume work\n");
8006308419aSMark Brown 		if (!schedule_work(&card->deferred_resume_work))
801f110bfc7SLiam Girdwood 			dev_err(dev, "ASoC: resume work item may be lost\n");
802f0fba2adSLiam Girdwood 	}
8036ed25978SAndy Green 
804db2a4165SFrank Mandarino 	return 0;
805db2a4165SFrank Mandarino }
8066f8ab4acSMark Brown EXPORT_SYMBOL_GPL(snd_soc_resume);
807db2a4165SFrank Mandarino #else
8086f8ab4acSMark Brown #define snd_soc_suspend NULL
8096f8ab4acSMark Brown #define snd_soc_resume NULL
810db2a4165SFrank Mandarino #endif
811db2a4165SFrank Mandarino 
81285e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops null_dai_ops = {
81302a06d30SBarry Song };
81402a06d30SBarry Song 
81565d9361fSLars-Peter Clausen static struct snd_soc_component *soc_find_component(
81665d9361fSLars-Peter Clausen 	const struct device_node *of_node, const char *name)
81712023a9aSMisael Lopez Cruz {
81865d9361fSLars-Peter Clausen 	struct snd_soc_component *component;
81912023a9aSMisael Lopez Cruz 
82065d9361fSLars-Peter Clausen 	list_for_each_entry(component, &component_list, list) {
82165d9361fSLars-Peter Clausen 		if (of_node) {
82265d9361fSLars-Peter Clausen 			if (component->dev->of_node == of_node)
82365d9361fSLars-Peter Clausen 				return component;
82465d9361fSLars-Peter Clausen 		} else if (strcmp(component->name, name) == 0) {
82565d9361fSLars-Peter Clausen 			return component;
82612023a9aSMisael Lopez Cruz 		}
82712023a9aSMisael Lopez Cruz 	}
82812023a9aSMisael Lopez Cruz 
82912023a9aSMisael Lopez Cruz 	return NULL;
83012023a9aSMisael Lopez Cruz }
83112023a9aSMisael Lopez Cruz 
83214621c7eSLars-Peter Clausen static struct snd_soc_dai *snd_soc_find_dai(
83314621c7eSLars-Peter Clausen 	const struct snd_soc_dai_link_component *dlc)
83412023a9aSMisael Lopez Cruz {
83514621c7eSLars-Peter Clausen 	struct snd_soc_component *component;
83614621c7eSLars-Peter Clausen 	struct snd_soc_dai *dai;
83712023a9aSMisael Lopez Cruz 
83814621c7eSLars-Peter Clausen 	/* Find CPU DAI from registered DAIs*/
83914621c7eSLars-Peter Clausen 	list_for_each_entry(component, &component_list, list) {
84014621c7eSLars-Peter Clausen 		if (dlc->of_node && component->dev->of_node != dlc->of_node)
84112023a9aSMisael Lopez Cruz 			continue;
84214621c7eSLars-Peter Clausen 		if (dlc->name && strcmp(dev_name(component->dev), dlc->name))
84312023a9aSMisael Lopez Cruz 			continue;
84414621c7eSLars-Peter Clausen 		list_for_each_entry(dai, &component->dai_list, list) {
84514621c7eSLars-Peter Clausen 			if (dlc->dai_name && strcmp(dai->name, dlc->dai_name))
84614621c7eSLars-Peter Clausen 				continue;
84712023a9aSMisael Lopez Cruz 
84814621c7eSLars-Peter Clausen 			return dai;
84912023a9aSMisael Lopez Cruz 		}
85012023a9aSMisael Lopez Cruz 	}
85112023a9aSMisael Lopez Cruz 
85212023a9aSMisael Lopez Cruz 	return NULL;
85312023a9aSMisael Lopez Cruz }
85412023a9aSMisael Lopez Cruz 
855f0fba2adSLiam Girdwood static int soc_bind_dai_link(struct snd_soc_card *card, int num)
856db2a4165SFrank Mandarino {
857f0fba2adSLiam Girdwood 	struct snd_soc_dai_link *dai_link = &card->dai_link[num];
858f0fba2adSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
85988bd870fSBenoit Cousson 	struct snd_soc_dai_link_component *codecs = dai_link->codecs;
86014621c7eSLars-Peter Clausen 	struct snd_soc_dai_link_component cpu_dai_component;
86188bd870fSBenoit Cousson 	struct snd_soc_dai **codec_dais = rtd->codec_dais;
862435c5e25SMark Brown 	struct snd_soc_platform *platform;
863848dd8beSMark Brown 	const char *platform_name;
86488bd870fSBenoit Cousson 	int i;
865db2a4165SFrank Mandarino 
866f110bfc7SLiam Girdwood 	dev_dbg(card->dev, "ASoC: binding %s at idx %d\n", dai_link->name, num);
8676308419aSMark Brown 
86814621c7eSLars-Peter Clausen 	cpu_dai_component.name = dai_link->cpu_name;
86914621c7eSLars-Peter Clausen 	cpu_dai_component.of_node = dai_link->cpu_of_node;
87014621c7eSLars-Peter Clausen 	cpu_dai_component.dai_name = dai_link->cpu_dai_name;
87114621c7eSLars-Peter Clausen 	rtd->cpu_dai = snd_soc_find_dai(&cpu_dai_component);
872b19e6e7bSMark Brown 	if (!rtd->cpu_dai) {
873f110bfc7SLiam Girdwood 		dev_err(card->dev, "ASoC: CPU DAI %s not registered\n",
874f0fba2adSLiam Girdwood 			dai_link->cpu_dai_name);
875b19e6e7bSMark Brown 		return -EPROBE_DEFER;
876c5af3a2eSMark Brown 	}
877c5af3a2eSMark Brown 
87888bd870fSBenoit Cousson 	rtd->num_codecs = dai_link->num_codecs;
87988bd870fSBenoit Cousson 
88088bd870fSBenoit Cousson 	/* Find CODEC from registered CODECs */
88188bd870fSBenoit Cousson 	for (i = 0; i < rtd->num_codecs; i++) {
88214621c7eSLars-Peter Clausen 		codec_dais[i] = snd_soc_find_dai(&codecs[i]);
88388bd870fSBenoit Cousson 		if (!codec_dais[i]) {
88412023a9aSMisael Lopez Cruz 			dev_err(card->dev, "ASoC: CODEC DAI %s not registered\n",
88588bd870fSBenoit Cousson 				codecs[i].dai_name);
88612023a9aSMisael Lopez Cruz 			return -EPROBE_DEFER;
88712023a9aSMisael Lopez Cruz 		}
88888bd870fSBenoit Cousson 	}
88988bd870fSBenoit Cousson 
89088bd870fSBenoit Cousson 	/* Single codec links expect codec and codec_dai in runtime data */
89188bd870fSBenoit Cousson 	rtd->codec_dai = codec_dais[0];
89288bd870fSBenoit Cousson 	rtd->codec = rtd->codec_dai->codec;
89312023a9aSMisael Lopez Cruz 
894848dd8beSMark Brown 	/* if there's no platform we match on the empty platform */
895848dd8beSMark Brown 	platform_name = dai_link->platform_name;
8965a504963SStephen Warren 	if (!platform_name && !dai_link->platform_of_node)
897848dd8beSMark Brown 		platform_name = "snd-soc-dummy";
898848dd8beSMark Brown 
899b19e6e7bSMark Brown 	/* find one from the set of registered platforms */
900f0fba2adSLiam Girdwood 	list_for_each_entry(platform, &platform_list, list) {
9015a504963SStephen Warren 		if (dai_link->platform_of_node) {
9025a504963SStephen Warren 			if (platform->dev->of_node !=
9035a504963SStephen Warren 			    dai_link->platform_of_node)
9045a504963SStephen Warren 				continue;
9055a504963SStephen Warren 		} else {
906f4333203SLars-Peter Clausen 			if (strcmp(platform->component.name, platform_name))
9072610ab77SStephen Warren 				continue;
9085a504963SStephen Warren 		}
9092610ab77SStephen Warren 
910f0fba2adSLiam Girdwood 		rtd->platform = platform;
911f0fba2adSLiam Girdwood 	}
912b19e6e7bSMark Brown 	if (!rtd->platform) {
913f110bfc7SLiam Girdwood 		dev_err(card->dev, "ASoC: platform %s not registered\n",
914f0fba2adSLiam Girdwood 			dai_link->platform_name);
915b19e6e7bSMark Brown 		return -EPROBE_DEFER;
916f0fba2adSLiam Girdwood 	}
917b19e6e7bSMark Brown 
918b19e6e7bSMark Brown 	card->num_rtd++;
919b19e6e7bSMark Brown 
920b19e6e7bSMark Brown 	return 0;
9216b05eda6SMark Brown }
9226b05eda6SMark Brown 
923f1d45cc3SLars-Peter Clausen static void soc_remove_component(struct snd_soc_component *component)
924d12cd198SStephen Warren {
92570090bbbSLars-Peter Clausen 	if (!component->probed)
92670090bbbSLars-Peter Clausen 		return;
927d12cd198SStephen Warren 
928f1d45cc3SLars-Peter Clausen 	/* This is a HACK and will be removed soon */
929f1d45cc3SLars-Peter Clausen 	if (component->codec)
930f1d45cc3SLars-Peter Clausen 		list_del(&component->codec->card_list);
931d12cd198SStephen Warren 
932f1d45cc3SLars-Peter Clausen 	if (component->remove)
933f1d45cc3SLars-Peter Clausen 		component->remove(component);
934d12cd198SStephen Warren 
935f1d45cc3SLars-Peter Clausen 	snd_soc_dapm_free(snd_soc_component_get_dapm(component));
936d12cd198SStephen Warren 
937f1d45cc3SLars-Peter Clausen 	soc_cleanup_component_debugfs(component);
938f1d45cc3SLars-Peter Clausen 	component->probed = 0;
939f1d45cc3SLars-Peter Clausen 	module_put(component->dev->driver->owner);
940d12cd198SStephen Warren }
941d12cd198SStephen Warren 
942e60cd14fSLars-Peter Clausen static void soc_remove_dai(struct snd_soc_dai *dai, int order)
943589c3563SJarkko Nikula {
944589c3563SJarkko Nikula 	int err;
945589c3563SJarkko Nikula 
946e60cd14fSLars-Peter Clausen 	if (dai && dai->probed &&
947e60cd14fSLars-Peter Clausen 			dai->driver->remove_order == order) {
948e60cd14fSLars-Peter Clausen 		if (dai->driver->remove) {
949e60cd14fSLars-Peter Clausen 			err = dai->driver->remove(dai);
950589c3563SJarkko Nikula 			if (err < 0)
951e60cd14fSLars-Peter Clausen 				dev_err(dai->dev,
952b0aa88afSMisael Lopez Cruz 					"ASoC: failed to remove %s: %d\n",
953e60cd14fSLars-Peter Clausen 					dai->name, err);
954b0aa88afSMisael Lopez Cruz 		}
955e60cd14fSLars-Peter Clausen 		dai->probed = 0;
956b0aa88afSMisael Lopez Cruz 	}
957b0aa88afSMisael Lopez Cruz }
958b0aa88afSMisael Lopez Cruz 
95962ae68faSStephen Warren static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order)
960f0fba2adSLiam Girdwood {
961f0fba2adSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
962e60cd14fSLars-Peter Clausen 	int i;
963f0fba2adSLiam Girdwood 
964f0fba2adSLiam Girdwood 	/* unregister the rtd device */
965f0fba2adSLiam Girdwood 	if (rtd->dev_registered) {
96636ae1a96SMark Brown 		device_remove_file(rtd->dev, &dev_attr_pmdown_time);
96736ae1a96SMark Brown 		device_remove_file(rtd->dev, &dev_attr_codec_reg);
96836ae1a96SMark Brown 		device_unregister(rtd->dev);
969f0fba2adSLiam Girdwood 		rtd->dev_registered = 0;
97002a06d30SBarry Song 	}
97102a06d30SBarry Song 
972f0fba2adSLiam Girdwood 	/* remove the CODEC DAI */
97388bd870fSBenoit Cousson 	for (i = 0; i < rtd->num_codecs; i++)
974e60cd14fSLars-Peter Clausen 		soc_remove_dai(rtd->codec_dais[i], order);
975f0fba2adSLiam Girdwood 
976e60cd14fSLars-Peter Clausen 	soc_remove_dai(rtd->cpu_dai, order);
977f0fba2adSLiam Girdwood }
978f0fba2adSLiam Girdwood 
97962ae68faSStephen Warren static void soc_remove_link_components(struct snd_soc_card *card, int num,
98062ae68faSStephen Warren 				       int order)
98162ae68faSStephen Warren {
98262ae68faSStephen Warren 	struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
98362ae68faSStephen Warren 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
98462ae68faSStephen Warren 	struct snd_soc_platform *platform = rtd->platform;
98561aca564SLars-Peter Clausen 	struct snd_soc_component *component;
98688bd870fSBenoit Cousson 	int i;
98762ae68faSStephen Warren 
98862ae68faSStephen Warren 	/* remove the platform */
98970090bbbSLars-Peter Clausen 	if (platform && platform->component.driver->remove_order == order)
990f1d45cc3SLars-Peter Clausen 		soc_remove_component(&platform->component);
99162ae68faSStephen Warren 
99262ae68faSStephen Warren 	/* remove the CODEC-side CODEC */
99388bd870fSBenoit Cousson 	for (i = 0; i < rtd->num_codecs; i++) {
99461aca564SLars-Peter Clausen 		component = rtd->codec_dais[i]->component;
99570090bbbSLars-Peter Clausen 		if (component->driver->remove_order == order)
99661aca564SLars-Peter Clausen 			soc_remove_component(component);
99762ae68faSStephen Warren 	}
99862ae68faSStephen Warren 
99962ae68faSStephen Warren 	/* remove any CPU-side CODEC */
100062ae68faSStephen Warren 	if (cpu_dai) {
100170090bbbSLars-Peter Clausen 		if (cpu_dai->component->driver->remove_order == order)
100261aca564SLars-Peter Clausen 			soc_remove_component(cpu_dai->component);
100362ae68faSStephen Warren 	}
100462ae68faSStephen Warren }
100562ae68faSStephen Warren 
10060671fd8eSKuninori Morimoto static void soc_remove_dai_links(struct snd_soc_card *card)
10070671fd8eSKuninori Morimoto {
10080168bf0dSLiam Girdwood 	int dai, order;
10090671fd8eSKuninori Morimoto 
10100168bf0dSLiam Girdwood 	for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
10110168bf0dSLiam Girdwood 			order++) {
10120168bf0dSLiam Girdwood 		for (dai = 0; dai < card->num_rtd; dai++)
101362ae68faSStephen Warren 			soc_remove_link_dais(card, dai, order);
10140168bf0dSLiam Girdwood 	}
101562ae68faSStephen Warren 
101662ae68faSStephen Warren 	for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
101762ae68faSStephen Warren 			order++) {
101862ae68faSStephen Warren 		for (dai = 0; dai < card->num_rtd; dai++)
101962ae68faSStephen Warren 			soc_remove_link_components(card, dai, order);
102062ae68faSStephen Warren 	}
102162ae68faSStephen Warren 
10220671fd8eSKuninori Morimoto 	card->num_rtd = 0;
10230671fd8eSKuninori Morimoto }
10240671fd8eSKuninori Morimoto 
1025ead9b919SJarkko Nikula static void soc_set_name_prefix(struct snd_soc_card *card,
102694f99c87SLars-Peter Clausen 				struct snd_soc_component *component)
1027ead9b919SJarkko Nikula {
1028ead9b919SJarkko Nikula 	int i;
1029ead9b919SJarkko Nikula 
1030ff819b83SDimitris Papastamos 	if (card->codec_conf == NULL)
1031ead9b919SJarkko Nikula 		return;
1032ead9b919SJarkko Nikula 
1033ff819b83SDimitris Papastamos 	for (i = 0; i < card->num_configs; i++) {
1034ff819b83SDimitris Papastamos 		struct snd_soc_codec_conf *map = &card->codec_conf[i];
103594f99c87SLars-Peter Clausen 		if (map->of_node && component->dev->of_node != map->of_node)
10363ca041edSSebastian Reichel 			continue;
103794f99c87SLars-Peter Clausen 		if (map->dev_name && strcmp(component->name, map->dev_name))
10383ca041edSSebastian Reichel 			continue;
103994f99c87SLars-Peter Clausen 		component->name_prefix = map->name_prefix;
1040ead9b919SJarkko Nikula 		break;
1041ead9b919SJarkko Nikula 	}
1042ead9b919SJarkko Nikula }
1043ead9b919SJarkko Nikula 
1044f1d45cc3SLars-Peter Clausen static int soc_probe_component(struct snd_soc_card *card,
1045f1d45cc3SLars-Peter Clausen 	struct snd_soc_component *component)
1046589c3563SJarkko Nikula {
1047f1d45cc3SLars-Peter Clausen 	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
1048888df395SMark Brown 	struct snd_soc_dai *dai;
1049f1d45cc3SLars-Peter Clausen 	int ret;
1050589c3563SJarkko Nikula 
105170090bbbSLars-Peter Clausen 	if (component->probed)
105270090bbbSLars-Peter Clausen 		return 0;
1053589c3563SJarkko Nikula 
1054f1d45cc3SLars-Peter Clausen 	component->card = card;
1055f1d45cc3SLars-Peter Clausen 	dapm->card = card;
1056f1d45cc3SLars-Peter Clausen 	soc_set_name_prefix(card, component);
1057589c3563SJarkko Nikula 
1058f1d45cc3SLars-Peter Clausen 	if (!try_module_get(component->dev->driver->owner))
105970d29331SJarkko Nikula 		return -ENODEV;
106070d29331SJarkko Nikula 
1061f1d45cc3SLars-Peter Clausen 	soc_init_component_debugfs(component);
1062d5d1e0beSLars-Peter Clausen 
1063f1d45cc3SLars-Peter Clausen 	if (component->dapm_widgets) {
1064f1d45cc3SLars-Peter Clausen 		ret = snd_soc_dapm_new_controls(dapm, component->dapm_widgets,
1065f1d45cc3SLars-Peter Clausen 			component->num_dapm_widgets);
106677530150SLars-Peter Clausen 
1067b318ad50SNariman Poushin 		if (ret != 0) {
1068f1d45cc3SLars-Peter Clausen 			dev_err(component->dev,
1069b318ad50SNariman Poushin 				"Failed to create new controls %d\n", ret);
1070b318ad50SNariman Poushin 			goto err_probe;
1071b318ad50SNariman Poushin 		}
1072b318ad50SNariman Poushin 	}
1073b318ad50SNariman Poushin 
10740634814fSLars-Peter Clausen 	list_for_each_entry(dai, &component->dai_list, list) {
10750634814fSLars-Peter Clausen 		ret = snd_soc_dapm_new_dai_widgets(dapm, dai);
1076261edc70SNariman Poushin 		if (ret != 0) {
1077f1d45cc3SLars-Peter Clausen 			dev_err(component->dev,
1078261edc70SNariman Poushin 				"Failed to create DAI widgets %d\n", ret);
1079261edc70SNariman Poushin 			goto err_probe;
1080261edc70SNariman Poushin 		}
1081261edc70SNariman Poushin 	}
1082888df395SMark Brown 
1083f1d45cc3SLars-Peter Clausen 	if (component->probe) {
1084f1d45cc3SLars-Peter Clausen 		ret = component->probe(component);
1085589c3563SJarkko Nikula 		if (ret < 0) {
1086f1d45cc3SLars-Peter Clausen 			dev_err(component->dev,
1087f1d45cc3SLars-Peter Clausen 				"ASoC: failed to probe component %d\n", ret);
108870d29331SJarkko Nikula 			goto err_probe;
1089589c3563SJarkko Nikula 		}
1090cb2cf612SLiam Girdwood 
1091f1d45cc3SLars-Peter Clausen 		WARN(dapm->idle_bias_off &&
1092f1d45cc3SLars-Peter Clausen 			dapm->bias_level != SND_SOC_BIAS_OFF,
1093956245e9SLiam Girdwood 			"codec %s can not start from non-off bias with idle_bias_off==1\n",
1094f1d45cc3SLars-Peter Clausen 			component->name);
1095956245e9SLiam Girdwood 	}
1096956245e9SLiam Girdwood 
1097f1d45cc3SLars-Peter Clausen 	if (component->controls)
1098f1d45cc3SLars-Peter Clausen 		snd_soc_add_component_controls(component, component->controls,
1099f1d45cc3SLars-Peter Clausen 				     component->num_controls);
1100f1d45cc3SLars-Peter Clausen 	if (component->dapm_routes)
1101f1d45cc3SLars-Peter Clausen 		snd_soc_dapm_add_routes(dapm, component->dapm_routes,
1102f1d45cc3SLars-Peter Clausen 					component->num_dapm_routes);
1103956245e9SLiam Girdwood 
1104f1d45cc3SLars-Peter Clausen 	component->probed = 1;
1105f1d45cc3SLars-Peter Clausen 	list_add(&dapm->list, &card->dapm_list);
1106f1d45cc3SLars-Peter Clausen 
1107f1d45cc3SLars-Peter Clausen 	/* This is a HACK and will be removed soon */
1108f1d45cc3SLars-Peter Clausen 	if (component->codec)
1109f1d45cc3SLars-Peter Clausen 		list_add(&component->codec->card_list, &card->codec_dev_list);
1110956245e9SLiam Girdwood 
1111956245e9SLiam Girdwood 	return 0;
1112956245e9SLiam Girdwood 
1113956245e9SLiam Girdwood err_probe:
1114f1d45cc3SLars-Peter Clausen 	soc_cleanup_component_debugfs(component);
1115f1d45cc3SLars-Peter Clausen 	module_put(component->dev->driver->owner);
1116956245e9SLiam Girdwood 
1117956245e9SLiam Girdwood 	return ret;
1118956245e9SLiam Girdwood }
1119956245e9SLiam Girdwood 
112036ae1a96SMark Brown static void rtd_release(struct device *dev)
112136ae1a96SMark Brown {
112236ae1a96SMark Brown 	kfree(dev);
112336ae1a96SMark Brown }
1124f0fba2adSLiam Girdwood 
11255f3484acSLars-Peter Clausen static int soc_post_component_init(struct snd_soc_pcm_runtime *rtd,
11265f3484acSLars-Peter Clausen 	const char *name)
1127503ae5e0SMisael Lopez Cruz {
1128589c3563SJarkko Nikula 	int ret = 0;
1129589c3563SJarkko Nikula 
1130589c3563SJarkko Nikula 	/* register the rtd device */
113136ae1a96SMark Brown 	rtd->dev = kzalloc(sizeof(struct device), GFP_KERNEL);
113236ae1a96SMark Brown 	if (!rtd->dev)
113336ae1a96SMark Brown 		return -ENOMEM;
113436ae1a96SMark Brown 	device_initialize(rtd->dev);
11355f3484acSLars-Peter Clausen 	rtd->dev->parent = rtd->card->dev;
113636ae1a96SMark Brown 	rtd->dev->release = rtd_release;
1137f294afedSLars-Peter Clausen 	dev_set_name(rtd->dev, "%s", name);
113836ae1a96SMark Brown 	dev_set_drvdata(rtd->dev, rtd);
1139b8c0dab9SLiam Girdwood 	mutex_init(&rtd->pcm_mutex);
114001d7584cSLiam Girdwood 	INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].be_clients);
114101d7584cSLiam Girdwood 	INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].be_clients);
114201d7584cSLiam Girdwood 	INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].fe_clients);
114301d7584cSLiam Girdwood 	INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].fe_clients);
114436ae1a96SMark Brown 	ret = device_add(rtd->dev);
1145589c3563SJarkko Nikula 	if (ret < 0) {
1146865df9cbSChuansheng Liu 		/* calling put_device() here to free the rtd->dev */
1147865df9cbSChuansheng Liu 		put_device(rtd->dev);
11485f3484acSLars-Peter Clausen 		dev_err(rtd->card->dev,
1149f110bfc7SLiam Girdwood 			"ASoC: failed to register runtime device: %d\n", ret);
1150589c3563SJarkko Nikula 		return ret;
1151589c3563SJarkko Nikula 	}
1152589c3563SJarkko Nikula 	rtd->dev_registered = 1;
1153589c3563SJarkko Nikula 
115493c3ce76SLars-Peter Clausen 	if (rtd->codec) {
1155589c3563SJarkko Nikula 		/* add DAPM sysfs entries for this codec */
115636ae1a96SMark Brown 		ret = snd_soc_dapm_sys_add(rtd->dev);
1157589c3563SJarkko Nikula 		if (ret < 0)
115844c69bb1SLars-Peter Clausen 			dev_err(rtd->dev,
115993c3ce76SLars-Peter Clausen 				"ASoC: failed to add codec dapm sysfs entries: %d\n",
116093c3ce76SLars-Peter Clausen 				ret);
1161589c3563SJarkko Nikula 
1162589c3563SJarkko Nikula 		/* add codec sysfs entries */
116336ae1a96SMark Brown 		ret = device_create_file(rtd->dev, &dev_attr_codec_reg);
1164589c3563SJarkko Nikula 		if (ret < 0)
116544c69bb1SLars-Peter Clausen 			dev_err(rtd->dev,
116693c3ce76SLars-Peter Clausen 				"ASoC: failed to add codec sysfs files: %d\n",
116793c3ce76SLars-Peter Clausen 				ret);
116893c3ce76SLars-Peter Clausen 	}
1169589c3563SJarkko Nikula 
1170589c3563SJarkko Nikula 	return 0;
1171589c3563SJarkko Nikula }
1172589c3563SJarkko Nikula 
117362ae68faSStephen Warren static int soc_probe_link_components(struct snd_soc_card *card, int num,
117462ae68faSStephen Warren 				     int order)
117562ae68faSStephen Warren {
117662ae68faSStephen Warren 	struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
117762ae68faSStephen Warren 	struct snd_soc_platform *platform = rtd->platform;
1178f1d45cc3SLars-Peter Clausen 	struct snd_soc_component *component;
117988bd870fSBenoit Cousson 	int i, ret;
118062ae68faSStephen Warren 
118162ae68faSStephen Warren 	/* probe the CPU-side component, if it is a CODEC */
118261aca564SLars-Peter Clausen 	component = rtd->cpu_dai->component;
118370090bbbSLars-Peter Clausen 	if (component->driver->probe_order == order) {
1184f1d45cc3SLars-Peter Clausen 		ret = soc_probe_component(card, component);
118562ae68faSStephen Warren 		if (ret < 0)
118662ae68faSStephen Warren 			return ret;
118762ae68faSStephen Warren 	}
118862ae68faSStephen Warren 
118988bd870fSBenoit Cousson 	/* probe the CODEC-side components */
119088bd870fSBenoit Cousson 	for (i = 0; i < rtd->num_codecs; i++) {
119161aca564SLars-Peter Clausen 		component = rtd->codec_dais[i]->component;
119270090bbbSLars-Peter Clausen 		if (component->driver->probe_order == order) {
1193f1d45cc3SLars-Peter Clausen 			ret = soc_probe_component(card, component);
119462ae68faSStephen Warren 			if (ret < 0)
119562ae68faSStephen Warren 				return ret;
119662ae68faSStephen Warren 		}
119788bd870fSBenoit Cousson 	}
119862ae68faSStephen Warren 
119962ae68faSStephen Warren 	/* probe the platform */
120070090bbbSLars-Peter Clausen 	if (platform->component.driver->probe_order == order) {
1201f1d45cc3SLars-Peter Clausen 		ret = soc_probe_component(card, &platform->component);
120262ae68faSStephen Warren 		if (ret < 0)
120362ae68faSStephen Warren 			return ret;
120462ae68faSStephen Warren 	}
120562ae68faSStephen Warren 
120662ae68faSStephen Warren 	return 0;
120762ae68faSStephen Warren }
120862ae68faSStephen Warren 
1209b0aa88afSMisael Lopez Cruz static int soc_probe_codec_dai(struct snd_soc_card *card,
1210b0aa88afSMisael Lopez Cruz 			       struct snd_soc_dai *codec_dai,
1211b0aa88afSMisael Lopez Cruz 			       int order)
1212b0aa88afSMisael Lopez Cruz {
1213b0aa88afSMisael Lopez Cruz 	int ret;
1214b0aa88afSMisael Lopez Cruz 
1215b0aa88afSMisael Lopez Cruz 	if (!codec_dai->probed && codec_dai->driver->probe_order == order) {
1216b0aa88afSMisael Lopez Cruz 		if (codec_dai->driver->probe) {
1217b0aa88afSMisael Lopez Cruz 			ret = codec_dai->driver->probe(codec_dai);
1218b0aa88afSMisael Lopez Cruz 			if (ret < 0) {
1219b0aa88afSMisael Lopez Cruz 				dev_err(codec_dai->dev,
1220b0aa88afSMisael Lopez Cruz 					"ASoC: failed to probe CODEC DAI %s: %d\n",
1221b0aa88afSMisael Lopez Cruz 					codec_dai->name, ret);
1222b0aa88afSMisael Lopez Cruz 				return ret;
1223b0aa88afSMisael Lopez Cruz 			}
1224b0aa88afSMisael Lopez Cruz 		}
1225b0aa88afSMisael Lopez Cruz 
1226b0aa88afSMisael Lopez Cruz 		/* mark codec_dai as probed and add to card dai list */
1227b0aa88afSMisael Lopez Cruz 		codec_dai->probed = 1;
1228b0aa88afSMisael Lopez Cruz 	}
1229b0aa88afSMisael Lopez Cruz 
1230b0aa88afSMisael Lopez Cruz 	return 0;
1231b0aa88afSMisael Lopez Cruz }
1232b0aa88afSMisael Lopez Cruz 
12332436a723SMisael Lopez Cruz static int soc_link_dai_widgets(struct snd_soc_card *card,
12342436a723SMisael Lopez Cruz 				struct snd_soc_dai_link *dai_link,
12353f901a02SBenoit Cousson 				struct snd_soc_pcm_runtime *rtd)
12362436a723SMisael Lopez Cruz {
12373f901a02SBenoit Cousson 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
12383f901a02SBenoit Cousson 	struct snd_soc_dai *codec_dai = rtd->codec_dai;
12392436a723SMisael Lopez Cruz 	struct snd_soc_dapm_widget *play_w, *capture_w;
12402436a723SMisael Lopez Cruz 	int ret;
12412436a723SMisael Lopez Cruz 
124288bd870fSBenoit Cousson 	if (rtd->num_codecs > 1)
124388bd870fSBenoit Cousson 		dev_warn(card->dev, "ASoC: Multiple codecs not supported yet\n");
124488bd870fSBenoit Cousson 
12452436a723SMisael Lopez Cruz 	/* link the DAI widgets */
12462436a723SMisael Lopez Cruz 	play_w = codec_dai->playback_widget;
12472436a723SMisael Lopez Cruz 	capture_w = cpu_dai->capture_widget;
12482436a723SMisael Lopez Cruz 	if (play_w && capture_w) {
12492436a723SMisael Lopez Cruz 		ret = snd_soc_dapm_new_pcm(card, dai_link->params,
12502436a723SMisael Lopez Cruz 					   capture_w, play_w);
12512436a723SMisael Lopez Cruz 		if (ret != 0) {
12522436a723SMisael Lopez Cruz 			dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n",
12532436a723SMisael Lopez Cruz 				play_w->name, capture_w->name, ret);
12542436a723SMisael Lopez Cruz 			return ret;
12552436a723SMisael Lopez Cruz 		}
12562436a723SMisael Lopez Cruz 	}
12572436a723SMisael Lopez Cruz 
12582436a723SMisael Lopez Cruz 	play_w = cpu_dai->playback_widget;
12592436a723SMisael Lopez Cruz 	capture_w = codec_dai->capture_widget;
12602436a723SMisael Lopez Cruz 	if (play_w && capture_w) {
12612436a723SMisael Lopez Cruz 		ret = snd_soc_dapm_new_pcm(card, dai_link->params,
12622436a723SMisael Lopez Cruz 					   capture_w, play_w);
12632436a723SMisael Lopez Cruz 		if (ret != 0) {
12642436a723SMisael Lopez Cruz 			dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n",
12652436a723SMisael Lopez Cruz 				play_w->name, capture_w->name, ret);
12662436a723SMisael Lopez Cruz 			return ret;
12672436a723SMisael Lopez Cruz 		}
12682436a723SMisael Lopez Cruz 	}
12692436a723SMisael Lopez Cruz 
12702436a723SMisael Lopez Cruz 	return 0;
12712436a723SMisael Lopez Cruz }
12722436a723SMisael Lopez Cruz 
127362ae68faSStephen Warren static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
1274f0fba2adSLiam Girdwood {
1275f0fba2adSLiam Girdwood 	struct snd_soc_dai_link *dai_link = &card->dai_link[num];
1276f0fba2adSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
1277f0fba2adSLiam Girdwood 	struct snd_soc_platform *platform = rtd->platform;
1278c74184edSMark Brown 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
127988bd870fSBenoit Cousson 	int i, ret;
1280f0fba2adSLiam Girdwood 
1281f110bfc7SLiam Girdwood 	dev_dbg(card->dev, "ASoC: probe %s dai link %d late %d\n",
12820168bf0dSLiam Girdwood 			card->name, num, order);
1283f0fba2adSLiam Girdwood 
1284f0fba2adSLiam Girdwood 	/* config components */
1285f0fba2adSLiam Girdwood 	cpu_dai->platform = platform;
1286f0fba2adSLiam Girdwood 	cpu_dai->card = card;
128788bd870fSBenoit Cousson 	for (i = 0; i < rtd->num_codecs; i++)
128888bd870fSBenoit Cousson 		rtd->codec_dais[i]->card = card;
1289f0fba2adSLiam Girdwood 
1290f0fba2adSLiam Girdwood 	/* set default power off timeout */
1291f0fba2adSLiam Girdwood 	rtd->pmdown_time = pmdown_time;
1292f0fba2adSLiam Girdwood 
1293f0fba2adSLiam Girdwood 	/* probe the cpu_dai */
12940168bf0dSLiam Girdwood 	if (!cpu_dai->probed &&
12950168bf0dSLiam Girdwood 			cpu_dai->driver->probe_order == order) {
1296f0fba2adSLiam Girdwood 		if (cpu_dai->driver->probe) {
1297f0fba2adSLiam Girdwood 			ret = cpu_dai->driver->probe(cpu_dai);
1298f0fba2adSLiam Girdwood 			if (ret < 0) {
1299f110bfc7SLiam Girdwood 				dev_err(cpu_dai->dev,
1300f110bfc7SLiam Girdwood 					"ASoC: failed to probe CPU DAI %s: %d\n",
13010837fc62SFabio Estevam 					cpu_dai->name, ret);
1302f0fba2adSLiam Girdwood 				return ret;
1303f0fba2adSLiam Girdwood 			}
1304f0fba2adSLiam Girdwood 		}
1305f0fba2adSLiam Girdwood 		cpu_dai->probed = 1;
1306f0fba2adSLiam Girdwood 	}
1307f0fba2adSLiam Girdwood 
1308f0fba2adSLiam Girdwood 	/* probe the CODEC DAI */
130988bd870fSBenoit Cousson 	for (i = 0; i < rtd->num_codecs; i++) {
131088bd870fSBenoit Cousson 		ret = soc_probe_codec_dai(card, rtd->codec_dais[i], order);
1311b0aa88afSMisael Lopez Cruz 		if (ret)
1312f0fba2adSLiam Girdwood 			return ret;
131388bd870fSBenoit Cousson 	}
1314f0fba2adSLiam Girdwood 
13150168bf0dSLiam Girdwood 	/* complete DAI probe during last probe */
13160168bf0dSLiam Girdwood 	if (order != SND_SOC_COMP_ORDER_LAST)
13170168bf0dSLiam Girdwood 		return 0;
13180168bf0dSLiam Girdwood 
13195f3484acSLars-Peter Clausen 	/* do machine specific initialization */
13205f3484acSLars-Peter Clausen 	if (dai_link->init) {
13215f3484acSLars-Peter Clausen 		ret = dai_link->init(rtd);
13225f3484acSLars-Peter Clausen 		if (ret < 0) {
13235f3484acSLars-Peter Clausen 			dev_err(card->dev, "ASoC: failed to init %s: %d\n",
13245f3484acSLars-Peter Clausen 				dai_link->name, ret);
13255f3484acSLars-Peter Clausen 			return ret;
13265f3484acSLars-Peter Clausen 		}
13275f3484acSLars-Peter Clausen 	}
13285f3484acSLars-Peter Clausen 
13295f3484acSLars-Peter Clausen 	ret = soc_post_component_init(rtd, dai_link->name);
1330589c3563SJarkko Nikula 	if (ret)
1331f0fba2adSLiam Girdwood 		return ret;
1332f0fba2adSLiam Girdwood 
13335f3484acSLars-Peter Clausen #ifdef CONFIG_DEBUG_FS
13345f3484acSLars-Peter Clausen 	/* add DPCM sysfs entries */
13355f3484acSLars-Peter Clausen 	if (dai_link->dynamic) {
13365f3484acSLars-Peter Clausen 		ret = soc_dpcm_debugfs_add(rtd);
13375f3484acSLars-Peter Clausen 		if (ret < 0) {
13385f3484acSLars-Peter Clausen 			dev_err(rtd->dev,
13395f3484acSLars-Peter Clausen 				"ASoC: failed to add dpcm sysfs entries: %d\n",
13405f3484acSLars-Peter Clausen 				ret);
13415f3484acSLars-Peter Clausen 			return ret;
13425f3484acSLars-Peter Clausen 		}
13435f3484acSLars-Peter Clausen 	}
13445f3484acSLars-Peter Clausen #endif
13455f3484acSLars-Peter Clausen 
134636ae1a96SMark Brown 	ret = device_create_file(rtd->dev, &dev_attr_pmdown_time);
1347f0fba2adSLiam Girdwood 	if (ret < 0)
1348f110bfc7SLiam Girdwood 		dev_warn(rtd->dev, "ASoC: failed to add pmdown_time sysfs: %d\n",
1349f110bfc7SLiam Girdwood 			ret);
1350f0fba2adSLiam Girdwood 
13511245b700SNamarta Kohli 	if (cpu_dai->driver->compress_dai) {
13521245b700SNamarta Kohli 		/*create compress_device"*/
13531245b700SNamarta Kohli 		ret = soc_new_compress(rtd, num);
13541245b700SNamarta Kohli 		if (ret < 0) {
1355f110bfc7SLiam Girdwood 			dev_err(card->dev, "ASoC: can't create compress %s\n",
13561245b700SNamarta Kohli 					 dai_link->stream_name);
13571245b700SNamarta Kohli 			return ret;
13581245b700SNamarta Kohli 		}
13591245b700SNamarta Kohli 	} else {
13601245b700SNamarta Kohli 
1361c74184edSMark Brown 		if (!dai_link->params) {
1362f0fba2adSLiam Girdwood 			/* create the pcm */
1363f0fba2adSLiam Girdwood 			ret = soc_new_pcm(rtd, num);
1364f0fba2adSLiam Girdwood 			if (ret < 0) {
1365f110bfc7SLiam Girdwood 				dev_err(card->dev, "ASoC: can't create pcm %s :%d\n",
13660837fc62SFabio Estevam 				       dai_link->stream_name, ret);
1367f0fba2adSLiam Girdwood 				return ret;
1368f0fba2adSLiam Girdwood 			}
1369c74184edSMark Brown 		} else {
13709d58a077SRichard Fitzgerald 			INIT_DELAYED_WORK(&rtd->delayed_work,
13719d58a077SRichard Fitzgerald 						codec2codec_close_delayed_work);
13729d58a077SRichard Fitzgerald 
1373c74184edSMark Brown 			/* link the DAI widgets */
13743f901a02SBenoit Cousson 			ret = soc_link_dai_widgets(card, dai_link, rtd);
13752436a723SMisael Lopez Cruz 			if (ret)
1376c74184edSMark Brown 				return ret;
1377c74184edSMark Brown 		}
1378c74184edSMark Brown 	}
1379c74184edSMark Brown 
1380f0fba2adSLiam Girdwood 	return 0;
1381f0fba2adSLiam Girdwood }
1382f0fba2adSLiam Girdwood 
138344c69bb1SLars-Peter Clausen static int soc_bind_aux_dev(struct snd_soc_card *card, int num)
1384b19e6e7bSMark Brown {
138544c69bb1SLars-Peter Clausen 	struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num];
13863ca041edSSebastian Reichel 	struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
138765d9361fSLars-Peter Clausen 	const char *name = aux_dev->codec_name;
13883ca041edSSebastian Reichel 
138965d9361fSLars-Peter Clausen 	rtd->component = soc_find_component(aux_dev->codec_of_node, name);
139065d9361fSLars-Peter Clausen 	if (!rtd->component) {
13913ca041edSSebastian Reichel 		if (aux_dev->codec_of_node)
139265d9361fSLars-Peter Clausen 			name = of_node_full_name(aux_dev->codec_of_node);
13933ca041edSSebastian Reichel 
139465d9361fSLars-Peter Clausen 		dev_err(card->dev, "ASoC: %s not registered\n", name);
1395b19e6e7bSMark Brown 		return -EPROBE_DEFER;
1396b19e6e7bSMark Brown 	}
1397b19e6e7bSMark Brown 
139865d9361fSLars-Peter Clausen 	/*
139965d9361fSLars-Peter Clausen 	 * Some places still reference rtd->codec, so we have to keep that
140065d9361fSLars-Peter Clausen 	 * initialized if the component is a CODEC. Once all those references
140165d9361fSLars-Peter Clausen 	 * have been removed, this code can be removed as well.
140265d9361fSLars-Peter Clausen 	 */
140365d9361fSLars-Peter Clausen 	 rtd->codec = rtd->component->codec;
140465d9361fSLars-Peter Clausen 
140544c69bb1SLars-Peter Clausen 	return 0;
14063ca041edSSebastian Reichel }
14072eea392dSJarkko Nikula 
140844c69bb1SLars-Peter Clausen static int soc_probe_aux_dev(struct snd_soc_card *card, int num)
140944c69bb1SLars-Peter Clausen {
141044c69bb1SLars-Peter Clausen 	struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num];
14115f3484acSLars-Peter Clausen 	struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
141244c69bb1SLars-Peter Clausen 	int ret;
141344c69bb1SLars-Peter Clausen 
141465d9361fSLars-Peter Clausen 	ret = soc_probe_component(card, rtd->component);
14152eea392dSJarkko Nikula 	if (ret < 0)
1416589c3563SJarkko Nikula 		return ret;
14172eea392dSJarkko Nikula 
14185f3484acSLars-Peter Clausen 	/* do machine specific initialization */
14195f3484acSLars-Peter Clausen 	if (aux_dev->init) {
142057bf7726SLars-Peter Clausen 		ret = aux_dev->init(rtd->component);
14215f3484acSLars-Peter Clausen 		if (ret < 0) {
14225f3484acSLars-Peter Clausen 			dev_err(card->dev, "ASoC: failed to init %s: %d\n",
14235f3484acSLars-Peter Clausen 				aux_dev->name, ret);
14242eea392dSJarkko Nikula 			return ret;
14252eea392dSJarkko Nikula 		}
14265f3484acSLars-Peter Clausen 	}
14275f3484acSLars-Peter Clausen 
14285f3484acSLars-Peter Clausen 	return soc_post_component_init(rtd, aux_dev->name);
14292eea392dSJarkko Nikula }
14302eea392dSJarkko Nikula 
14312eea392dSJarkko Nikula static void soc_remove_aux_dev(struct snd_soc_card *card, int num)
14322eea392dSJarkko Nikula {
14332eea392dSJarkko Nikula 	struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num];
143465d9361fSLars-Peter Clausen 	struct snd_soc_component *component = rtd->component;
14352eea392dSJarkko Nikula 
14362eea392dSJarkko Nikula 	/* unregister the rtd device */
14372eea392dSJarkko Nikula 	if (rtd->dev_registered) {
143836ae1a96SMark Brown 		device_remove_file(rtd->dev, &dev_attr_codec_reg);
1439d3bf1561SChuansheng Liu 		device_unregister(rtd->dev);
14402eea392dSJarkko Nikula 		rtd->dev_registered = 0;
14412eea392dSJarkko Nikula 	}
14422eea392dSJarkko Nikula 
144365d9361fSLars-Peter Clausen 	if (component && component->probed)
144465d9361fSLars-Peter Clausen 		soc_remove_component(component);
14452eea392dSJarkko Nikula }
14462eea392dSJarkko Nikula 
1447f90fb3f7SLars-Peter Clausen static int snd_soc_init_codec_cache(struct snd_soc_codec *codec)
1448fdf0f54dSDimitris Papastamos {
1449fdf0f54dSDimitris Papastamos 	int ret;
1450fdf0f54dSDimitris Papastamos 
1451fdf0f54dSDimitris Papastamos 	if (codec->cache_init)
1452fdf0f54dSDimitris Papastamos 		return 0;
1453fdf0f54dSDimitris Papastamos 
1454fdf0f54dSDimitris Papastamos 	ret = snd_soc_cache_init(codec);
1455fdf0f54dSDimitris Papastamos 	if (ret < 0) {
145610e8aa9aSMichał Mirosław 		dev_err(codec->dev,
145710e8aa9aSMichał Mirosław 			"ASoC: Failed to set cache compression type: %d\n",
145810e8aa9aSMichał Mirosław 			ret);
1459fdf0f54dSDimitris Papastamos 		return ret;
1460fdf0f54dSDimitris Papastamos 	}
1461fdf0f54dSDimitris Papastamos 	codec->cache_init = 1;
1462fdf0f54dSDimitris Papastamos 	return 0;
1463fdf0f54dSDimitris Papastamos }
1464fdf0f54dSDimitris Papastamos 
1465b19e6e7bSMark Brown static int snd_soc_instantiate_card(struct snd_soc_card *card)
1466f0fba2adSLiam Girdwood {
1467fdf0f54dSDimitris Papastamos 	struct snd_soc_codec *codec;
146875d9ac46SMark Brown 	struct snd_soc_dai_link *dai_link;
1469f04209a7SMark Brown 	int ret, i, order, dai_fmt;
147096dd3622SMark Brown 
147101b9d99aSLiam Girdwood 	mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT);
1472f0fba2adSLiam Girdwood 
1473b19e6e7bSMark Brown 	/* bind DAIs */
1474b19e6e7bSMark Brown 	for (i = 0; i < card->num_links; i++) {
1475b19e6e7bSMark Brown 		ret = soc_bind_dai_link(card, i);
1476b19e6e7bSMark Brown 		if (ret != 0)
1477b19e6e7bSMark Brown 			goto base_error;
1478db2a4165SFrank Mandarino 	}
1479db2a4165SFrank Mandarino 
148044c69bb1SLars-Peter Clausen 	/* bind aux_devs too */
1481b19e6e7bSMark Brown 	for (i = 0; i < card->num_aux_devs; i++) {
148244c69bb1SLars-Peter Clausen 		ret = soc_bind_aux_dev(card, i);
1483b19e6e7bSMark Brown 		if (ret != 0)
1484b19e6e7bSMark Brown 			goto base_error;
1485db2a4165SFrank Mandarino 	}
1486db2a4165SFrank Mandarino 
1487fdf0f54dSDimitris Papastamos 	/* initialize the register cache for each available codec */
1488fdf0f54dSDimitris Papastamos 	list_for_each_entry(codec, &codec_list, list) {
1489fdf0f54dSDimitris Papastamos 		if (codec->cache_init)
1490fdf0f54dSDimitris Papastamos 			continue;
1491f90fb3f7SLars-Peter Clausen 		ret = snd_soc_init_codec_cache(codec);
1492b19e6e7bSMark Brown 		if (ret < 0)
1493b19e6e7bSMark Brown 			goto base_error;
1494fdf0f54dSDimitris Papastamos 	}
1495fdf0f54dSDimitris Papastamos 
1496f0fba2adSLiam Girdwood 	/* card bind complete so register a sound card */
1497102b5a8dSTakashi Iwai 	ret = snd_card_new(card->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
1498f0fba2adSLiam Girdwood 			card->owner, 0, &card->snd_card);
1499f0fba2adSLiam Girdwood 	if (ret < 0) {
150010e8aa9aSMichał Mirosław 		dev_err(card->dev,
150110e8aa9aSMichał Mirosław 			"ASoC: can't create sound card for card %s: %d\n",
150210e8aa9aSMichał Mirosław 			card->name, ret);
1503b19e6e7bSMark Brown 		goto base_error;
1504db2a4165SFrank Mandarino 	}
1505db2a4165SFrank Mandarino 
1506e37a4970SMark Brown 	card->dapm.bias_level = SND_SOC_BIAS_OFF;
1507e37a4970SMark Brown 	card->dapm.dev = card->dev;
1508e37a4970SMark Brown 	card->dapm.card = card;
1509e37a4970SMark Brown 	list_add(&card->dapm.list, &card->dapm_list);
1510e37a4970SMark Brown 
1511d5d1e0beSLars-Peter Clausen #ifdef CONFIG_DEBUG_FS
1512d5d1e0beSLars-Peter Clausen 	snd_soc_dapm_debugfs_init(&card->dapm, card->debugfs_card_root);
1513d5d1e0beSLars-Peter Clausen #endif
1514d5d1e0beSLars-Peter Clausen 
151588ee1c61SMark Brown #ifdef CONFIG_PM_SLEEP
15166ed25978SAndy Green 	/* deferred resume work */
15176308419aSMark Brown 	INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);
15181301a964SRandy Dunlap #endif
15196ed25978SAndy Green 
15209a841ebbSMark Brown 	if (card->dapm_widgets)
15219a841ebbSMark Brown 		snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets,
15229a841ebbSMark Brown 					  card->num_dapm_widgets);
15239a841ebbSMark Brown 
1524f0fba2adSLiam Girdwood 	/* initialise the sound card only once */
1525f0fba2adSLiam Girdwood 	if (card->probe) {
1526e7361ec4SMark Brown 		ret = card->probe(card);
1527f0fba2adSLiam Girdwood 		if (ret < 0)
1528f0fba2adSLiam Girdwood 			goto card_probe_error;
1529f0fba2adSLiam Girdwood 	}
1530f0fba2adSLiam Girdwood 
153162ae68faSStephen Warren 	/* probe all components used by DAI links on this card */
15320168bf0dSLiam Girdwood 	for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
15330168bf0dSLiam Girdwood 			order++) {
1534fe3e78e0SMark Brown 		for (i = 0; i < card->num_links; i++) {
153562ae68faSStephen Warren 			ret = soc_probe_link_components(card, i, order);
153662ae68faSStephen Warren 			if (ret < 0) {
1537f110bfc7SLiam Girdwood 				dev_err(card->dev,
1538f110bfc7SLiam Girdwood 					"ASoC: failed to instantiate card %d\n",
1539f110bfc7SLiam Girdwood 					ret);
154062ae68faSStephen Warren 				goto probe_dai_err;
154162ae68faSStephen Warren 			}
154262ae68faSStephen Warren 		}
154362ae68faSStephen Warren 	}
154462ae68faSStephen Warren 
154562ae68faSStephen Warren 	/* probe all DAI links on this card */
154662ae68faSStephen Warren 	for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
154762ae68faSStephen Warren 			order++) {
154862ae68faSStephen Warren 		for (i = 0; i < card->num_links; i++) {
154962ae68faSStephen Warren 			ret = soc_probe_link_dais(card, i, order);
1550fe3e78e0SMark Brown 			if (ret < 0) {
1551f110bfc7SLiam Girdwood 				dev_err(card->dev,
1552f110bfc7SLiam Girdwood 					"ASoC: failed to instantiate card %d\n",
1553f110bfc7SLiam Girdwood 					ret);
1554f0fba2adSLiam Girdwood 				goto probe_dai_err;
1555fe3e78e0SMark Brown 			}
1556fe3e78e0SMark Brown 		}
15570168bf0dSLiam Girdwood 	}
1558fe3e78e0SMark Brown 
15592eea392dSJarkko Nikula 	for (i = 0; i < card->num_aux_devs; i++) {
15602eea392dSJarkko Nikula 		ret = soc_probe_aux_dev(card, i);
15612eea392dSJarkko Nikula 		if (ret < 0) {
1562f110bfc7SLiam Girdwood 			dev_err(card->dev,
1563f110bfc7SLiam Girdwood 				"ASoC: failed to add auxiliary devices %d\n",
1564f110bfc7SLiam Girdwood 				ret);
15652eea392dSJarkko Nikula 			goto probe_aux_dev_err;
15662eea392dSJarkko Nikula 		}
15672eea392dSJarkko Nikula 	}
15682eea392dSJarkko Nikula 
1569888df395SMark Brown 	snd_soc_dapm_link_dai_widgets(card);
1570b893ea5fSLiam Girdwood 	snd_soc_dapm_connect_dai_link_widgets(card);
1571888df395SMark Brown 
1572b7af1dafSMark Brown 	if (card->controls)
1573022658beSLiam Girdwood 		snd_soc_add_card_controls(card, card->controls, card->num_controls);
1574b7af1dafSMark Brown 
1575b8ad29deSMark Brown 	if (card->dapm_routes)
1576b8ad29deSMark Brown 		snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes,
1577b8ad29deSMark Brown 					card->num_dapm_routes);
1578b8ad29deSMark Brown 
157975d9ac46SMark Brown 	for (i = 0; i < card->num_links; i++) {
158088bd870fSBenoit Cousson 		struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
158175d9ac46SMark Brown 		dai_link = &card->dai_link[i];
1582f04209a7SMark Brown 		dai_fmt = dai_link->dai_fmt;
158375d9ac46SMark Brown 
1584f04209a7SMark Brown 		if (dai_fmt) {
158588bd870fSBenoit Cousson 			struct snd_soc_dai **codec_dais = rtd->codec_dais;
158688bd870fSBenoit Cousson 			int j;
158788bd870fSBenoit Cousson 
158888bd870fSBenoit Cousson 			for (j = 0; j < rtd->num_codecs; j++) {
158988bd870fSBenoit Cousson 				struct snd_soc_dai *codec_dai = codec_dais[j];
159088bd870fSBenoit Cousson 
159188bd870fSBenoit Cousson 				ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt);
15925e4ba569SShawn Guo 				if (ret != 0 && ret != -ENOTSUPP)
159388bd870fSBenoit Cousson 					dev_warn(codec_dai->dev,
1594f110bfc7SLiam Girdwood 						 "ASoC: Failed to set DAI format: %d\n",
159575d9ac46SMark Brown 						 ret);
1596f04209a7SMark Brown 			}
159788bd870fSBenoit Cousson 		}
1598f04209a7SMark Brown 
1599f04209a7SMark Brown 		/* If this is a regular CPU link there will be a platform */
1600fe33d4c5SStephen Warren 		if (dai_fmt &&
1601fe33d4c5SStephen Warren 		    (dai_link->platform_name || dai_link->platform_of_node)) {
1602f04209a7SMark Brown 			ret = snd_soc_dai_set_fmt(card->rtd[i].cpu_dai,
1603f04209a7SMark Brown 						  dai_fmt);
1604f04209a7SMark Brown 			if (ret != 0 && ret != -ENOTSUPP)
1605f04209a7SMark Brown 				dev_warn(card->rtd[i].cpu_dai->dev,
1606f110bfc7SLiam Girdwood 					 "ASoC: Failed to set DAI format: %d\n",
1607f04209a7SMark Brown 					 ret);
1608f04209a7SMark Brown 		} else if (dai_fmt) {
1609f04209a7SMark Brown 			/* Flip the polarity for the "CPU" end */
1610f04209a7SMark Brown 			dai_fmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
1611f04209a7SMark Brown 			switch (dai_link->dai_fmt &
1612f04209a7SMark Brown 				SND_SOC_DAIFMT_MASTER_MASK) {
1613f04209a7SMark Brown 			case SND_SOC_DAIFMT_CBM_CFM:
1614f04209a7SMark Brown 				dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
1615f04209a7SMark Brown 				break;
1616f04209a7SMark Brown 			case SND_SOC_DAIFMT_CBM_CFS:
1617f04209a7SMark Brown 				dai_fmt |= SND_SOC_DAIFMT_CBS_CFM;
1618f04209a7SMark Brown 				break;
1619f04209a7SMark Brown 			case SND_SOC_DAIFMT_CBS_CFM:
1620f04209a7SMark Brown 				dai_fmt |= SND_SOC_DAIFMT_CBM_CFS;
1621f04209a7SMark Brown 				break;
1622f04209a7SMark Brown 			case SND_SOC_DAIFMT_CBS_CFS:
1623f04209a7SMark Brown 				dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
1624f04209a7SMark Brown 				break;
1625f04209a7SMark Brown 			}
162675d9ac46SMark Brown 
162775d9ac46SMark Brown 			ret = snd_soc_dai_set_fmt(card->rtd[i].cpu_dai,
1628f04209a7SMark Brown 						  dai_fmt);
16295e4ba569SShawn Guo 			if (ret != 0 && ret != -ENOTSUPP)
163075d9ac46SMark Brown 				dev_warn(card->rtd[i].cpu_dai->dev,
1631f110bfc7SLiam Girdwood 					 "ASoC: Failed to set DAI format: %d\n",
163275d9ac46SMark Brown 					 ret);
163375d9ac46SMark Brown 		}
163475d9ac46SMark Brown 	}
163575d9ac46SMark Brown 
1636f0fba2adSLiam Girdwood 	snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname),
1637fe3e78e0SMark Brown 		 "%s", card->name);
1638f0fba2adSLiam Girdwood 	snprintf(card->snd_card->longname, sizeof(card->snd_card->longname),
163922de71baSLiam Girdwood 		 "%s", card->long_name ? card->long_name : card->name);
1640f0e8ed85SMark Brown 	snprintf(card->snd_card->driver, sizeof(card->snd_card->driver),
1641f0e8ed85SMark Brown 		 "%s", card->driver_name ? card->driver_name : card->name);
1642f0e8ed85SMark Brown 	for (i = 0; i < ARRAY_SIZE(card->snd_card->driver); i++) {
1643f0e8ed85SMark Brown 		switch (card->snd_card->driver[i]) {
1644f0e8ed85SMark Brown 		case '_':
1645f0e8ed85SMark Brown 		case '-':
1646f0e8ed85SMark Brown 		case '\0':
1647f0e8ed85SMark Brown 			break;
1648f0e8ed85SMark Brown 		default:
1649f0e8ed85SMark Brown 			if (!isalnum(card->snd_card->driver[i]))
1650f0e8ed85SMark Brown 				card->snd_card->driver[i] = '_';
1651f0e8ed85SMark Brown 			break;
1652f0e8ed85SMark Brown 		}
1653f0e8ed85SMark Brown 	}
1654fe3e78e0SMark Brown 
165528e9ad92SMark Brown 	if (card->late_probe) {
165628e9ad92SMark Brown 		ret = card->late_probe(card);
165728e9ad92SMark Brown 		if (ret < 0) {
1658f110bfc7SLiam Girdwood 			dev_err(card->dev, "ASoC: %s late_probe() failed: %d\n",
165928e9ad92SMark Brown 				card->name, ret);
166028e9ad92SMark Brown 			goto probe_aux_dev_err;
166128e9ad92SMark Brown 		}
166228e9ad92SMark Brown 	}
166328e9ad92SMark Brown 
16641633281bSStephen Warren 	if (card->fully_routed)
16657df37884SLars-Peter Clausen 		snd_soc_dapm_auto_nc_pins(card);
16661633281bSStephen Warren 
1667824ef826SLars-Peter Clausen 	snd_soc_dapm_new_widgets(card);
16688c193b8dSLars-Peter Clausen 
1669f0fba2adSLiam Girdwood 	ret = snd_card_register(card->snd_card);
1670fe3e78e0SMark Brown 	if (ret < 0) {
1671f110bfc7SLiam Girdwood 		dev_err(card->dev, "ASoC: failed to register soundcard %d\n",
1672f110bfc7SLiam Girdwood 				ret);
16736b3ed785SAxel Lin 		goto probe_aux_dev_err;
1674fe3e78e0SMark Brown 	}
1675fe3e78e0SMark Brown 
1676435c5e25SMark Brown 	card->instantiated = 1;
16774f4c0072SMark Brown 	snd_soc_dapm_sync(&card->dapm);
1678f0fba2adSLiam Girdwood 	mutex_unlock(&card->mutex);
1679b19e6e7bSMark Brown 
1680b19e6e7bSMark Brown 	return 0;
1681db2a4165SFrank Mandarino 
16822eea392dSJarkko Nikula probe_aux_dev_err:
16832eea392dSJarkko Nikula 	for (i = 0; i < card->num_aux_devs; i++)
16842eea392dSJarkko Nikula 		soc_remove_aux_dev(card, i);
16852eea392dSJarkko Nikula 
1686f0fba2adSLiam Girdwood probe_dai_err:
16870671fd8eSKuninori Morimoto 	soc_remove_dai_links(card);
1688fe3e78e0SMark Brown 
1689f0fba2adSLiam Girdwood card_probe_error:
169087506549SMark Brown 	if (card->remove)
1691e7361ec4SMark Brown 		card->remove(card);
1692f0fba2adSLiam Girdwood 
1693f0fba2adSLiam Girdwood 	snd_card_free(card->snd_card);
1694f0fba2adSLiam Girdwood 
1695b19e6e7bSMark Brown base_error:
1696f0fba2adSLiam Girdwood 	mutex_unlock(&card->mutex);
1697db2a4165SFrank Mandarino 
1698b19e6e7bSMark Brown 	return ret;
1699435c5e25SMark Brown }
1700435c5e25SMark Brown 
1701435c5e25SMark Brown /* probes a new socdev */
1702435c5e25SMark Brown static int soc_probe(struct platform_device *pdev)
1703435c5e25SMark Brown {
1704f0fba2adSLiam Girdwood 	struct snd_soc_card *card = platform_get_drvdata(pdev);
1705435c5e25SMark Brown 
170670a7ca34SVinod Koul 	/*
170770a7ca34SVinod Koul 	 * no card, so machine driver should be registering card
170870a7ca34SVinod Koul 	 * we should not be here in that case so ret error
170970a7ca34SVinod Koul 	 */
171070a7ca34SVinod Koul 	if (!card)
171170a7ca34SVinod Koul 		return -EINVAL;
171270a7ca34SVinod Koul 
1713fe4085e8SMark Brown 	dev_warn(&pdev->dev,
1714f110bfc7SLiam Girdwood 		 "ASoC: machine %s should use snd_soc_register_card()\n",
1715fe4085e8SMark Brown 		 card->name);
1716fe4085e8SMark Brown 
1717435c5e25SMark Brown 	/* Bodge while we unpick instantiation */
1718435c5e25SMark Brown 	card->dev = &pdev->dev;
1719f0fba2adSLiam Girdwood 
172028d528c8SMark Brown 	return snd_soc_register_card(card);
1721435c5e25SMark Brown }
1722435c5e25SMark Brown 
1723b0e26485SVinod Koul static int soc_cleanup_card_resources(struct snd_soc_card *card)
1724db2a4165SFrank Mandarino {
1725db2a4165SFrank Mandarino 	int i;
1726db2a4165SFrank Mandarino 
1727f0fba2adSLiam Girdwood 	/* make sure any delayed work runs */
1728f0fba2adSLiam Girdwood 	for (i = 0; i < card->num_rtd; i++) {
1729f0fba2adSLiam Girdwood 		struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
173043829731STejun Heo 		flush_delayed_work(&rtd->delayed_work);
1731db2a4165SFrank Mandarino 	}
1732db2a4165SFrank Mandarino 
17332eea392dSJarkko Nikula 	/* remove auxiliary devices */
17342eea392dSJarkko Nikula 	for (i = 0; i < card->num_aux_devs; i++)
17352eea392dSJarkko Nikula 		soc_remove_aux_dev(card, i);
17362eea392dSJarkko Nikula 
1737f0fba2adSLiam Girdwood 	/* remove and free each DAI */
17380671fd8eSKuninori Morimoto 	soc_remove_dai_links(card);
1739f0fba2adSLiam Girdwood 
1740a6052154SJarkko Nikula 	soc_cleanup_card_debugfs(card);
1741a6052154SJarkko Nikula 
1742f0fba2adSLiam Girdwood 	/* remove the card */
174387506549SMark Brown 	if (card->remove)
1744e7361ec4SMark Brown 		card->remove(card);
1745f0fba2adSLiam Girdwood 
17460aaae527SLars-Peter Clausen 	snd_soc_dapm_free(&card->dapm);
17470aaae527SLars-Peter Clausen 
1748f0fba2adSLiam Girdwood 	snd_card_free(card->snd_card);
1749b0e26485SVinod Koul 	return 0;
1750b0e26485SVinod Koul 
1751b2dfa62cSGuennadi Liakhovetski }
1752b0e26485SVinod Koul 
1753b0e26485SVinod Koul /* removes a socdev */
1754b0e26485SVinod Koul static int soc_remove(struct platform_device *pdev)
1755b0e26485SVinod Koul {
1756b0e26485SVinod Koul 	struct snd_soc_card *card = platform_get_drvdata(pdev);
1757b0e26485SVinod Koul 
1758c5af3a2eSMark Brown 	snd_soc_unregister_card(card);
1759db2a4165SFrank Mandarino 	return 0;
1760db2a4165SFrank Mandarino }
1761db2a4165SFrank Mandarino 
17626f8ab4acSMark Brown int snd_soc_poweroff(struct device *dev)
176351737470SMark Brown {
17646f8ab4acSMark Brown 	struct snd_soc_card *card = dev_get_drvdata(dev);
1765f0fba2adSLiam Girdwood 	int i;
176651737470SMark Brown 
176751737470SMark Brown 	if (!card->instantiated)
1768416356fcSMark Brown 		return 0;
176951737470SMark Brown 
177051737470SMark Brown 	/* Flush out pmdown_time work - we actually do want to run it
177151737470SMark Brown 	 * now, we're shutting down so no imminent restart. */
1772f0fba2adSLiam Girdwood 	for (i = 0; i < card->num_rtd; i++) {
1773f0fba2adSLiam Girdwood 		struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
177443829731STejun Heo 		flush_delayed_work(&rtd->delayed_work);
1775f0fba2adSLiam Girdwood 	}
177651737470SMark Brown 
1777f0fba2adSLiam Girdwood 	snd_soc_dapm_shutdown(card);
1778416356fcSMark Brown 
1779988e8cc4SNicolin Chen 	/* deactivate pins to sleep state */
1780988e8cc4SNicolin Chen 	for (i = 0; i < card->num_rtd; i++) {
178188bd870fSBenoit Cousson 		struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
178288bd870fSBenoit Cousson 		struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
178388bd870fSBenoit Cousson 		int j;
178488bd870fSBenoit Cousson 
1785988e8cc4SNicolin Chen 		pinctrl_pm_select_sleep_state(cpu_dai->dev);
178688bd870fSBenoit Cousson 		for (j = 0; j < rtd->num_codecs; j++) {
178788bd870fSBenoit Cousson 			struct snd_soc_dai *codec_dai = rtd->codec_dais[j];
178888bd870fSBenoit Cousson 			pinctrl_pm_select_sleep_state(codec_dai->dev);
178988bd870fSBenoit Cousson 		}
1790988e8cc4SNicolin Chen 	}
1791988e8cc4SNicolin Chen 
1792416356fcSMark Brown 	return 0;
179351737470SMark Brown }
17946f8ab4acSMark Brown EXPORT_SYMBOL_GPL(snd_soc_poweroff);
179551737470SMark Brown 
17966f8ab4acSMark Brown const struct dev_pm_ops snd_soc_pm_ops = {
1797b1dd5897SViresh Kumar 	.suspend = snd_soc_suspend,
1798b1dd5897SViresh Kumar 	.resume = snd_soc_resume,
1799b1dd5897SViresh Kumar 	.freeze = snd_soc_suspend,
1800b1dd5897SViresh Kumar 	.thaw = snd_soc_resume,
18016f8ab4acSMark Brown 	.poweroff = snd_soc_poweroff,
1802b1dd5897SViresh Kumar 	.restore = snd_soc_resume,
1803416356fcSMark Brown };
1804deb2607eSStephen Warren EXPORT_SYMBOL_GPL(snd_soc_pm_ops);
1805416356fcSMark Brown 
1806db2a4165SFrank Mandarino /* ASoC platform driver */
1807db2a4165SFrank Mandarino static struct platform_driver soc_driver = {
1808db2a4165SFrank Mandarino 	.driver		= {
1809db2a4165SFrank Mandarino 		.name		= "soc-audio",
18108b45a209SKay Sievers 		.owner		= THIS_MODULE,
18116f8ab4acSMark Brown 		.pm		= &snd_soc_pm_ops,
1812db2a4165SFrank Mandarino 	},
1813db2a4165SFrank Mandarino 	.probe		= soc_probe,
1814db2a4165SFrank Mandarino 	.remove		= soc_remove,
1815db2a4165SFrank Mandarino };
1816db2a4165SFrank Mandarino 
1817db2a4165SFrank Mandarino /**
1818db2a4165SFrank Mandarino  * snd_soc_cnew - create new control
1819db2a4165SFrank Mandarino  * @_template: control template
1820db2a4165SFrank Mandarino  * @data: control private data
1821ac11a2b3SMark Brown  * @long_name: control long name
1822efb7ac3fSMark Brown  * @prefix: control name prefix
1823db2a4165SFrank Mandarino  *
1824db2a4165SFrank Mandarino  * Create a new mixer control from a template control.
1825db2a4165SFrank Mandarino  *
1826db2a4165SFrank Mandarino  * Returns 0 for success, else error.
1827db2a4165SFrank Mandarino  */
1828db2a4165SFrank Mandarino struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template,
18293056557fSMark Brown 				  void *data, const char *long_name,
1830efb7ac3fSMark Brown 				  const char *prefix)
1831db2a4165SFrank Mandarino {
1832db2a4165SFrank Mandarino 	struct snd_kcontrol_new template;
1833efb7ac3fSMark Brown 	struct snd_kcontrol *kcontrol;
1834efb7ac3fSMark Brown 	char *name = NULL;
1835db2a4165SFrank Mandarino 
1836db2a4165SFrank Mandarino 	memcpy(&template, _template, sizeof(template));
1837db2a4165SFrank Mandarino 	template.index = 0;
1838db2a4165SFrank Mandarino 
1839efb7ac3fSMark Brown 	if (!long_name)
1840efb7ac3fSMark Brown 		long_name = template.name;
1841efb7ac3fSMark Brown 
1842efb7ac3fSMark Brown 	if (prefix) {
18432b581074SLars-Peter Clausen 		name = kasprintf(GFP_KERNEL, "%s %s", prefix, long_name);
1844efb7ac3fSMark Brown 		if (!name)
1845efb7ac3fSMark Brown 			return NULL;
1846efb7ac3fSMark Brown 
1847efb7ac3fSMark Brown 		template.name = name;
1848efb7ac3fSMark Brown 	} else {
1849efb7ac3fSMark Brown 		template.name = long_name;
1850efb7ac3fSMark Brown 	}
1851efb7ac3fSMark Brown 
1852efb7ac3fSMark Brown 	kcontrol = snd_ctl_new1(&template, data);
1853efb7ac3fSMark Brown 
1854efb7ac3fSMark Brown 	kfree(name);
1855efb7ac3fSMark Brown 
1856efb7ac3fSMark Brown 	return kcontrol;
1857db2a4165SFrank Mandarino }
1858db2a4165SFrank Mandarino EXPORT_SYMBOL_GPL(snd_soc_cnew);
1859db2a4165SFrank Mandarino 
1860022658beSLiam Girdwood static int snd_soc_add_controls(struct snd_card *card, struct device *dev,
1861022658beSLiam Girdwood 	const struct snd_kcontrol_new *controls, int num_controls,
1862022658beSLiam Girdwood 	const char *prefix, void *data)
1863022658beSLiam Girdwood {
1864022658beSLiam Girdwood 	int err, i;
1865022658beSLiam Girdwood 
1866022658beSLiam Girdwood 	for (i = 0; i < num_controls; i++) {
1867022658beSLiam Girdwood 		const struct snd_kcontrol_new *control = &controls[i];
1868022658beSLiam Girdwood 		err = snd_ctl_add(card, snd_soc_cnew(control, data,
1869022658beSLiam Girdwood 						     control->name, prefix));
1870022658beSLiam Girdwood 		if (err < 0) {
1871f110bfc7SLiam Girdwood 			dev_err(dev, "ASoC: Failed to add %s: %d\n",
1872f110bfc7SLiam Girdwood 				control->name, err);
1873022658beSLiam Girdwood 			return err;
1874022658beSLiam Girdwood 		}
1875022658beSLiam Girdwood 	}
1876022658beSLiam Girdwood 
1877022658beSLiam Girdwood 	return 0;
1878022658beSLiam Girdwood }
1879022658beSLiam Girdwood 
18804fefd698SDimitris Papastamos struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card,
18814fefd698SDimitris Papastamos 					       const char *name)
18824fefd698SDimitris Papastamos {
18834fefd698SDimitris Papastamos 	struct snd_card *card = soc_card->snd_card;
18844fefd698SDimitris Papastamos 	struct snd_kcontrol *kctl;
18854fefd698SDimitris Papastamos 
18864fefd698SDimitris Papastamos 	if (unlikely(!name))
18874fefd698SDimitris Papastamos 		return NULL;
18884fefd698SDimitris Papastamos 
18894fefd698SDimitris Papastamos 	list_for_each_entry(kctl, &card->controls, list)
18904fefd698SDimitris Papastamos 		if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name)))
18914fefd698SDimitris Papastamos 			return kctl;
18924fefd698SDimitris Papastamos 	return NULL;
18934fefd698SDimitris Papastamos }
18944fefd698SDimitris Papastamos EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol);
18954fefd698SDimitris Papastamos 
1896db2a4165SFrank Mandarino /**
18970f2780adSLars-Peter Clausen  * snd_soc_add_component_controls - Add an array of controls to a component.
18980f2780adSLars-Peter Clausen  *
18990f2780adSLars-Peter Clausen  * @component: Component to add controls to
19000f2780adSLars-Peter Clausen  * @controls: Array of controls to add
19010f2780adSLars-Peter Clausen  * @num_controls: Number of elements in the array
19020f2780adSLars-Peter Clausen  *
19030f2780adSLars-Peter Clausen  * Return: 0 for success, else error.
19040f2780adSLars-Peter Clausen  */
19050f2780adSLars-Peter Clausen int snd_soc_add_component_controls(struct snd_soc_component *component,
19060f2780adSLars-Peter Clausen 	const struct snd_kcontrol_new *controls, unsigned int num_controls)
19070f2780adSLars-Peter Clausen {
19080f2780adSLars-Peter Clausen 	struct snd_card *card = component->card->snd_card;
19090f2780adSLars-Peter Clausen 
19100f2780adSLars-Peter Clausen 	return snd_soc_add_controls(card, component->dev, controls,
19110f2780adSLars-Peter Clausen 			num_controls, component->name_prefix, component);
19120f2780adSLars-Peter Clausen }
19130f2780adSLars-Peter Clausen EXPORT_SYMBOL_GPL(snd_soc_add_component_controls);
19140f2780adSLars-Peter Clausen 
19150f2780adSLars-Peter Clausen /**
1916022658beSLiam Girdwood  * snd_soc_add_codec_controls - add an array of controls to a codec.
1917022658beSLiam Girdwood  * Convenience function to add a list of controls. Many codecs were
19183e8e1952SIan Molton  * duplicating this code.
19193e8e1952SIan Molton  *
19203e8e1952SIan Molton  * @codec: codec to add controls to
19213e8e1952SIan Molton  * @controls: array of controls to add
19223e8e1952SIan Molton  * @num_controls: number of elements in the array
19233e8e1952SIan Molton  *
19243e8e1952SIan Molton  * Return 0 for success, else error.
19253e8e1952SIan Molton  */
1926022658beSLiam Girdwood int snd_soc_add_codec_controls(struct snd_soc_codec *codec,
19270f2780adSLars-Peter Clausen 	const struct snd_kcontrol_new *controls, unsigned int num_controls)
19283e8e1952SIan Molton {
19290f2780adSLars-Peter Clausen 	return snd_soc_add_component_controls(&codec->component, controls,
19300f2780adSLars-Peter Clausen 		num_controls);
19313e8e1952SIan Molton }
1932022658beSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_add_codec_controls);
19333e8e1952SIan Molton 
19343e8e1952SIan Molton /**
1935a491a5c8SLiam Girdwood  * snd_soc_add_platform_controls - add an array of controls to a platform.
1936022658beSLiam Girdwood  * Convenience function to add a list of controls.
1937a491a5c8SLiam Girdwood  *
1938a491a5c8SLiam Girdwood  * @platform: platform to add controls to
1939a491a5c8SLiam Girdwood  * @controls: array of controls to add
1940a491a5c8SLiam Girdwood  * @num_controls: number of elements in the array
1941a491a5c8SLiam Girdwood  *
1942a491a5c8SLiam Girdwood  * Return 0 for success, else error.
1943a491a5c8SLiam Girdwood  */
1944a491a5c8SLiam Girdwood int snd_soc_add_platform_controls(struct snd_soc_platform *platform,
19450f2780adSLars-Peter Clausen 	const struct snd_kcontrol_new *controls, unsigned int num_controls)
1946a491a5c8SLiam Girdwood {
19470f2780adSLars-Peter Clausen 	return snd_soc_add_component_controls(&platform->component, controls,
19480f2780adSLars-Peter Clausen 		num_controls);
1949a491a5c8SLiam Girdwood }
1950a491a5c8SLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_add_platform_controls);
1951a491a5c8SLiam Girdwood 
1952a491a5c8SLiam Girdwood /**
1953022658beSLiam Girdwood  * snd_soc_add_card_controls - add an array of controls to a SoC card.
1954022658beSLiam Girdwood  * Convenience function to add a list of controls.
1955022658beSLiam Girdwood  *
1956022658beSLiam Girdwood  * @soc_card: SoC card to add controls to
1957022658beSLiam Girdwood  * @controls: array of controls to add
1958022658beSLiam Girdwood  * @num_controls: number of elements in the array
1959022658beSLiam Girdwood  *
1960022658beSLiam Girdwood  * Return 0 for success, else error.
1961022658beSLiam Girdwood  */
1962022658beSLiam Girdwood int snd_soc_add_card_controls(struct snd_soc_card *soc_card,
1963022658beSLiam Girdwood 	const struct snd_kcontrol_new *controls, int num_controls)
1964022658beSLiam Girdwood {
1965022658beSLiam Girdwood 	struct snd_card *card = soc_card->snd_card;
1966022658beSLiam Girdwood 
1967022658beSLiam Girdwood 	return snd_soc_add_controls(card, soc_card->dev, controls, num_controls,
1968022658beSLiam Girdwood 			NULL, soc_card);
1969022658beSLiam Girdwood }
1970022658beSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_add_card_controls);
1971022658beSLiam Girdwood 
1972022658beSLiam Girdwood /**
1973022658beSLiam Girdwood  * snd_soc_add_dai_controls - add an array of controls to a DAI.
1974022658beSLiam Girdwood  * Convienience function to add a list of controls.
1975022658beSLiam Girdwood  *
1976022658beSLiam Girdwood  * @dai: DAI to add controls to
1977022658beSLiam Girdwood  * @controls: array of controls to add
1978022658beSLiam Girdwood  * @num_controls: number of elements in the array
1979022658beSLiam Girdwood  *
1980022658beSLiam Girdwood  * Return 0 for success, else error.
1981022658beSLiam Girdwood  */
1982022658beSLiam Girdwood int snd_soc_add_dai_controls(struct snd_soc_dai *dai,
1983022658beSLiam Girdwood 	const struct snd_kcontrol_new *controls, int num_controls)
1984022658beSLiam Girdwood {
1985022658beSLiam Girdwood 	struct snd_card *card = dai->card->snd_card;
1986022658beSLiam Girdwood 
1987022658beSLiam Girdwood 	return snd_soc_add_controls(card, dai->dev, controls, num_controls,
1988022658beSLiam Girdwood 			NULL, dai);
1989022658beSLiam Girdwood }
1990022658beSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_add_dai_controls);
1991022658beSLiam Girdwood 
1992022658beSLiam Girdwood /**
1993db2a4165SFrank Mandarino  * snd_soc_info_enum_double - enumerated double mixer info callback
1994db2a4165SFrank Mandarino  * @kcontrol: mixer control
1995db2a4165SFrank Mandarino  * @uinfo: control element information
1996db2a4165SFrank Mandarino  *
1997db2a4165SFrank Mandarino  * Callback to provide information about a double enumerated
1998db2a4165SFrank Mandarino  * mixer control.
1999db2a4165SFrank Mandarino  *
2000db2a4165SFrank Mandarino  * Returns 0 for success.
2001db2a4165SFrank Mandarino  */
2002db2a4165SFrank Mandarino int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,
2003db2a4165SFrank Mandarino 	struct snd_ctl_elem_info *uinfo)
2004db2a4165SFrank Mandarino {
2005db2a4165SFrank Mandarino 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
2006db2a4165SFrank Mandarino 
2007db2a4165SFrank Mandarino 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2008db2a4165SFrank Mandarino 	uinfo->count = e->shift_l == e->shift_r ? 1 : 2;
20099a8d38dbSTakashi Iwai 	uinfo->value.enumerated.items = e->items;
2010db2a4165SFrank Mandarino 
20119a8d38dbSTakashi Iwai 	if (uinfo->value.enumerated.item >= e->items)
20129a8d38dbSTakashi Iwai 		uinfo->value.enumerated.item = e->items - 1;
2013a19685cbSTakashi Iwai 	strlcpy(uinfo->value.enumerated.name,
2014a19685cbSTakashi Iwai 		e->texts[uinfo->value.enumerated.item],
2015a19685cbSTakashi Iwai 		sizeof(uinfo->value.enumerated.name));
2016db2a4165SFrank Mandarino 	return 0;
2017db2a4165SFrank Mandarino }
2018db2a4165SFrank Mandarino EXPORT_SYMBOL_GPL(snd_soc_info_enum_double);
2019db2a4165SFrank Mandarino 
2020db2a4165SFrank Mandarino /**
2021db2a4165SFrank Mandarino  * snd_soc_get_enum_double - enumerated double mixer get callback
2022db2a4165SFrank Mandarino  * @kcontrol: mixer control
2023ac11a2b3SMark Brown  * @ucontrol: control element information
2024db2a4165SFrank Mandarino  *
2025db2a4165SFrank Mandarino  * Callback to get the value of a double enumerated mixer.
2026db2a4165SFrank Mandarino  *
2027db2a4165SFrank Mandarino  * Returns 0 for success.
2028db2a4165SFrank Mandarino  */
2029db2a4165SFrank Mandarino int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,
2030db2a4165SFrank Mandarino 	struct snd_ctl_elem_value *ucontrol)
2031db2a4165SFrank Mandarino {
2032907fe36aSLars-Peter Clausen 	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
2033db2a4165SFrank Mandarino 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
203429ae2fa5SLars-Peter Clausen 	unsigned int val, item;
203529ae2fa5SLars-Peter Clausen 	unsigned int reg_val;
2036907fe36aSLars-Peter Clausen 	int ret;
2037db2a4165SFrank Mandarino 
2038907fe36aSLars-Peter Clausen 	ret = snd_soc_component_read(component, e->reg, &reg_val);
2039907fe36aSLars-Peter Clausen 	if (ret)
2040907fe36aSLars-Peter Clausen 		return ret;
204129ae2fa5SLars-Peter Clausen 	val = (reg_val >> e->shift_l) & e->mask;
204229ae2fa5SLars-Peter Clausen 	item = snd_soc_enum_val_to_item(e, val);
204329ae2fa5SLars-Peter Clausen 	ucontrol->value.enumerated.item[0] = item;
204429ae2fa5SLars-Peter Clausen 	if (e->shift_l != e->shift_r) {
204529ae2fa5SLars-Peter Clausen 		val = (reg_val >> e->shift_l) & e->mask;
204629ae2fa5SLars-Peter Clausen 		item = snd_soc_enum_val_to_item(e, val);
204729ae2fa5SLars-Peter Clausen 		ucontrol->value.enumerated.item[1] = item;
204829ae2fa5SLars-Peter Clausen 	}
2049db2a4165SFrank Mandarino 
2050db2a4165SFrank Mandarino 	return 0;
2051db2a4165SFrank Mandarino }
2052db2a4165SFrank Mandarino EXPORT_SYMBOL_GPL(snd_soc_get_enum_double);
2053db2a4165SFrank Mandarino 
2054db2a4165SFrank Mandarino /**
2055db2a4165SFrank Mandarino  * snd_soc_put_enum_double - enumerated double mixer put callback
2056db2a4165SFrank Mandarino  * @kcontrol: mixer control
2057ac11a2b3SMark Brown  * @ucontrol: control element information
2058db2a4165SFrank Mandarino  *
2059db2a4165SFrank Mandarino  * Callback to set the value of a double enumerated mixer.
2060db2a4165SFrank Mandarino  *
2061db2a4165SFrank Mandarino  * Returns 0 for success.
2062db2a4165SFrank Mandarino  */
2063db2a4165SFrank Mandarino int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
2064db2a4165SFrank Mandarino 	struct snd_ctl_elem_value *ucontrol)
2065db2a4165SFrank Mandarino {
2066907fe36aSLars-Peter Clausen 	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
2067db2a4165SFrank Mandarino 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
206829ae2fa5SLars-Peter Clausen 	unsigned int *item = ucontrol->value.enumerated.item;
206946f5822fSDaniel Ribeiro 	unsigned int val;
207086767b7dSLars-Peter Clausen 	unsigned int mask;
2071db2a4165SFrank Mandarino 
207229ae2fa5SLars-Peter Clausen 	if (item[0] >= e->items)
2073db2a4165SFrank Mandarino 		return -EINVAL;
207429ae2fa5SLars-Peter Clausen 	val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
207586767b7dSLars-Peter Clausen 	mask = e->mask << e->shift_l;
2076db2a4165SFrank Mandarino 	if (e->shift_l != e->shift_r) {
207729ae2fa5SLars-Peter Clausen 		if (item[1] >= e->items)
2078db2a4165SFrank Mandarino 			return -EINVAL;
207929ae2fa5SLars-Peter Clausen 		val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r;
208086767b7dSLars-Peter Clausen 		mask |= e->mask << e->shift_r;
2081db2a4165SFrank Mandarino 	}
2082db2a4165SFrank Mandarino 
2083907fe36aSLars-Peter Clausen 	return snd_soc_component_update_bits(component, e->reg, mask, val);
2084db2a4165SFrank Mandarino }
2085db2a4165SFrank Mandarino EXPORT_SYMBOL_GPL(snd_soc_put_enum_double);
2086db2a4165SFrank Mandarino 
2087db2a4165SFrank Mandarino /**
2088f227b88fSMarkus Pargmann  * snd_soc_read_signed - Read a codec register and interprete as signed value
2089907fe36aSLars-Peter Clausen  * @component: component
2090f227b88fSMarkus Pargmann  * @reg: Register to read
2091f227b88fSMarkus Pargmann  * @mask: Mask to use after shifting the register value
2092f227b88fSMarkus Pargmann  * @shift: Right shift of register value
2093f227b88fSMarkus Pargmann  * @sign_bit: Bit that describes if a number is negative or not.
2094907fe36aSLars-Peter Clausen  * @signed_val: Pointer to where the read value should be stored
20952e72f8e3SPeter Ujfalusi  *
2096f227b88fSMarkus Pargmann  * This functions reads a codec register. The register value is shifted right
2097f227b88fSMarkus Pargmann  * by 'shift' bits and masked with the given 'mask'. Afterwards it translates
2098f227b88fSMarkus Pargmann  * the given registervalue into a signed integer if sign_bit is non-zero.
20992e72f8e3SPeter Ujfalusi  *
2100907fe36aSLars-Peter Clausen  * Returns 0 on sucess, otherwise an error value
21012e72f8e3SPeter Ujfalusi  */
2102907fe36aSLars-Peter Clausen static int snd_soc_read_signed(struct snd_soc_component *component,
2103907fe36aSLars-Peter Clausen 	unsigned int reg, unsigned int mask, unsigned int shift,
2104907fe36aSLars-Peter Clausen 	unsigned int sign_bit, int *signed_val)
21052e72f8e3SPeter Ujfalusi {
2106f227b88fSMarkus Pargmann 	int ret;
210746f5822fSDaniel Ribeiro 	unsigned int val;
21082e72f8e3SPeter Ujfalusi 
2109907fe36aSLars-Peter Clausen 	ret = snd_soc_component_read(component, reg, &val);
2110907fe36aSLars-Peter Clausen 	if (ret < 0)
2111907fe36aSLars-Peter Clausen 		return ret;
21122e72f8e3SPeter Ujfalusi 
2113907fe36aSLars-Peter Clausen 	val = (val >> shift) & mask;
2114907fe36aSLars-Peter Clausen 
2115907fe36aSLars-Peter Clausen 	if (!sign_bit) {
2116907fe36aSLars-Peter Clausen 		*signed_val = val;
2117907fe36aSLars-Peter Clausen 		return 0;
2118907fe36aSLars-Peter Clausen 	}
2119f227b88fSMarkus Pargmann 
2120f227b88fSMarkus Pargmann 	/* non-negative number */
2121907fe36aSLars-Peter Clausen 	if (!(val & BIT(sign_bit))) {
2122907fe36aSLars-Peter Clausen 		*signed_val = val;
2123907fe36aSLars-Peter Clausen 		return 0;
2124907fe36aSLars-Peter Clausen 	}
2125f227b88fSMarkus Pargmann 
2126f227b88fSMarkus Pargmann 	ret = val;
2127f227b88fSMarkus Pargmann 
2128f227b88fSMarkus Pargmann 	/*
2129f227b88fSMarkus Pargmann 	 * The register most probably does not contain a full-sized int.
2130f227b88fSMarkus Pargmann 	 * Instead we have an arbitrary number of bits in a signed
2131f227b88fSMarkus Pargmann 	 * representation which has to be translated into a full-sized int.
2132f227b88fSMarkus Pargmann 	 * This is done by filling up all bits above the sign-bit.
2133f227b88fSMarkus Pargmann 	 */
2134f227b88fSMarkus Pargmann 	ret |= ~((int)(BIT(sign_bit) - 1));
2135f227b88fSMarkus Pargmann 
2136907fe36aSLars-Peter Clausen 	*signed_val = ret;
2137907fe36aSLars-Peter Clausen 
2138907fe36aSLars-Peter Clausen 	return 0;
21392e72f8e3SPeter Ujfalusi }
21402e72f8e3SPeter Ujfalusi 
21412e72f8e3SPeter Ujfalusi /**
2142db2a4165SFrank Mandarino  * snd_soc_info_volsw - single mixer info callback
2143db2a4165SFrank Mandarino  * @kcontrol: mixer control
2144db2a4165SFrank Mandarino  * @uinfo: control element information
2145db2a4165SFrank Mandarino  *
2146e8f5a103SPeter Ujfalusi  * Callback to provide information about a single mixer control, or a double
2147e8f5a103SPeter Ujfalusi  * mixer control that spans 2 registers.
2148db2a4165SFrank Mandarino  *
2149db2a4165SFrank Mandarino  * Returns 0 for success.
2150db2a4165SFrank Mandarino  */
2151db2a4165SFrank Mandarino int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
2152db2a4165SFrank Mandarino 	struct snd_ctl_elem_info *uinfo)
2153db2a4165SFrank Mandarino {
21544eaa9819SJon Smirl 	struct soc_mixer_control *mc =
21554eaa9819SJon Smirl 		(struct soc_mixer_control *)kcontrol->private_value;
2156d11bb4a9SPeter Ujfalusi 	int platform_max;
2157db2a4165SFrank Mandarino 
2158d11bb4a9SPeter Ujfalusi 	if (!mc->platform_max)
2159d11bb4a9SPeter Ujfalusi 		mc->platform_max = mc->max;
2160d11bb4a9SPeter Ujfalusi 	platform_max = mc->platform_max;
2161d11bb4a9SPeter Ujfalusi 
2162d11bb4a9SPeter Ujfalusi 	if (platform_max == 1 && !strstr(kcontrol->id.name, " Volume"))
2163a7a4ac86SPhilipp Zabel 		uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
2164a7a4ac86SPhilipp Zabel 	else
2165a7a4ac86SPhilipp Zabel 		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
2166a7a4ac86SPhilipp Zabel 
2167e8f5a103SPeter Ujfalusi 	uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1;
2168db2a4165SFrank Mandarino 	uinfo->value.integer.min = 0;
2169f227b88fSMarkus Pargmann 	uinfo->value.integer.max = platform_max - mc->min;
2170db2a4165SFrank Mandarino 	return 0;
2171db2a4165SFrank Mandarino }
2172db2a4165SFrank Mandarino EXPORT_SYMBOL_GPL(snd_soc_info_volsw);
2173db2a4165SFrank Mandarino 
2174db2a4165SFrank Mandarino /**
2175db2a4165SFrank Mandarino  * snd_soc_get_volsw - single mixer get callback
2176db2a4165SFrank Mandarino  * @kcontrol: mixer control
2177ac11a2b3SMark Brown  * @ucontrol: control element information
2178db2a4165SFrank Mandarino  *
2179f7915d99SPeter Ujfalusi  * Callback to get the value of a single mixer control, or a double mixer
2180f7915d99SPeter Ujfalusi  * control that spans 2 registers.
2181db2a4165SFrank Mandarino  *
2182db2a4165SFrank Mandarino  * Returns 0 for success.
2183db2a4165SFrank Mandarino  */
2184db2a4165SFrank Mandarino int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
2185db2a4165SFrank Mandarino 	struct snd_ctl_elem_value *ucontrol)
2186db2a4165SFrank Mandarino {
2187907fe36aSLars-Peter Clausen 	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
21884eaa9819SJon Smirl 	struct soc_mixer_control *mc =
21894eaa9819SJon Smirl 		(struct soc_mixer_control *)kcontrol->private_value;
2190815ecf8dSJon Smirl 	unsigned int reg = mc->reg;
2191f7915d99SPeter Ujfalusi 	unsigned int reg2 = mc->rreg;
2192815ecf8dSJon Smirl 	unsigned int shift = mc->shift;
2193815ecf8dSJon Smirl 	unsigned int rshift = mc->rshift;
21944eaa9819SJon Smirl 	int max = mc->max;
2195f227b88fSMarkus Pargmann 	int min = mc->min;
2196f227b88fSMarkus Pargmann 	int sign_bit = mc->sign_bit;
2197815ecf8dSJon Smirl 	unsigned int mask = (1 << fls(max)) - 1;
2198815ecf8dSJon Smirl 	unsigned int invert = mc->invert;
2199907fe36aSLars-Peter Clausen 	int val;
2200907fe36aSLars-Peter Clausen 	int ret;
2201db2a4165SFrank Mandarino 
2202f227b88fSMarkus Pargmann 	if (sign_bit)
2203f227b88fSMarkus Pargmann 		mask = BIT(sign_bit + 1) - 1;
2204f227b88fSMarkus Pargmann 
2205907fe36aSLars-Peter Clausen 	ret = snd_soc_read_signed(component, reg, mask, shift, sign_bit, &val);
2206907fe36aSLars-Peter Clausen 	if (ret)
2207907fe36aSLars-Peter Clausen 		return ret;
2208907fe36aSLars-Peter Clausen 
2209907fe36aSLars-Peter Clausen 	ucontrol->value.integer.value[0] = val - min;
2210f7915d99SPeter Ujfalusi 	if (invert)
2211db2a4165SFrank Mandarino 		ucontrol->value.integer.value[0] =
2212a7a4ac86SPhilipp Zabel 			max - ucontrol->value.integer.value[0];
2213f7915d99SPeter Ujfalusi 
2214f7915d99SPeter Ujfalusi 	if (snd_soc_volsw_is_stereo(mc)) {
2215f7915d99SPeter Ujfalusi 		if (reg == reg2)
2216907fe36aSLars-Peter Clausen 			ret = snd_soc_read_signed(component, reg, mask, rshift,
2217907fe36aSLars-Peter Clausen 				sign_bit, &val);
2218f7915d99SPeter Ujfalusi 		else
2219907fe36aSLars-Peter Clausen 			ret = snd_soc_read_signed(component, reg2, mask, shift,
2220907fe36aSLars-Peter Clausen 				sign_bit, &val);
2221907fe36aSLars-Peter Clausen 		if (ret)
2222907fe36aSLars-Peter Clausen 			return ret;
2223907fe36aSLars-Peter Clausen 
2224907fe36aSLars-Peter Clausen 		ucontrol->value.integer.value[1] = val - min;
2225f7915d99SPeter Ujfalusi 		if (invert)
2226db2a4165SFrank Mandarino 			ucontrol->value.integer.value[1] =
2227a7a4ac86SPhilipp Zabel 				max - ucontrol->value.integer.value[1];
2228db2a4165SFrank Mandarino 	}
2229db2a4165SFrank Mandarino 
2230db2a4165SFrank Mandarino 	return 0;
2231db2a4165SFrank Mandarino }
2232db2a4165SFrank Mandarino EXPORT_SYMBOL_GPL(snd_soc_get_volsw);
2233db2a4165SFrank Mandarino 
2234db2a4165SFrank Mandarino /**
2235db2a4165SFrank Mandarino  * snd_soc_put_volsw - single mixer put callback
2236db2a4165SFrank Mandarino  * @kcontrol: mixer control
2237ac11a2b3SMark Brown  * @ucontrol: control element information
2238db2a4165SFrank Mandarino  *
2239974815baSPeter Ujfalusi  * Callback to set the value of a single mixer control, or a double mixer
2240974815baSPeter Ujfalusi  * control that spans 2 registers.
2241db2a4165SFrank Mandarino  *
2242db2a4165SFrank Mandarino  * Returns 0 for success.
2243db2a4165SFrank Mandarino  */
2244db2a4165SFrank Mandarino int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
2245db2a4165SFrank Mandarino 	struct snd_ctl_elem_value *ucontrol)
2246db2a4165SFrank Mandarino {
2247907fe36aSLars-Peter Clausen 	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
22484eaa9819SJon Smirl 	struct soc_mixer_control *mc =
22494eaa9819SJon Smirl 		(struct soc_mixer_control *)kcontrol->private_value;
2250815ecf8dSJon Smirl 	unsigned int reg = mc->reg;
2251974815baSPeter Ujfalusi 	unsigned int reg2 = mc->rreg;
2252815ecf8dSJon Smirl 	unsigned int shift = mc->shift;
2253815ecf8dSJon Smirl 	unsigned int rshift = mc->rshift;
22544eaa9819SJon Smirl 	int max = mc->max;
2255f227b88fSMarkus Pargmann 	int min = mc->min;
2256f227b88fSMarkus Pargmann 	unsigned int sign_bit = mc->sign_bit;
2257815ecf8dSJon Smirl 	unsigned int mask = (1 << fls(max)) - 1;
2258815ecf8dSJon Smirl 	unsigned int invert = mc->invert;
2259974815baSPeter Ujfalusi 	int err;
2260939d9f16SNenghua Cao 	bool type_2r = false;
2261974815baSPeter Ujfalusi 	unsigned int val2 = 0;
2262974815baSPeter Ujfalusi 	unsigned int val, val_mask;
2263db2a4165SFrank Mandarino 
2264f227b88fSMarkus Pargmann 	if (sign_bit)
2265f227b88fSMarkus Pargmann 		mask = BIT(sign_bit + 1) - 1;
2266f227b88fSMarkus Pargmann 
2267f227b88fSMarkus Pargmann 	val = ((ucontrol->value.integer.value[0] + min) & mask);
2268db2a4165SFrank Mandarino 	if (invert)
2269a7a4ac86SPhilipp Zabel 		val = max - val;
2270db2a4165SFrank Mandarino 	val_mask = mask << shift;
2271db2a4165SFrank Mandarino 	val = val << shift;
2272974815baSPeter Ujfalusi 	if (snd_soc_volsw_is_stereo(mc)) {
2273f227b88fSMarkus Pargmann 		val2 = ((ucontrol->value.integer.value[1] + min) & mask);
2274db2a4165SFrank Mandarino 		if (invert)
2275a7a4ac86SPhilipp Zabel 			val2 = max - val2;
2276974815baSPeter Ujfalusi 		if (reg == reg2) {
2277db2a4165SFrank Mandarino 			val_mask |= mask << rshift;
2278db2a4165SFrank Mandarino 			val |= val2 << rshift;
2279974815baSPeter Ujfalusi 		} else {
2280db2a4165SFrank Mandarino 			val2 = val2 << shift;
2281939d9f16SNenghua Cao 			type_2r = true;
2282974815baSPeter Ujfalusi 		}
2283974815baSPeter Ujfalusi 	}
2284907fe36aSLars-Peter Clausen 	err = snd_soc_component_update_bits(component, reg, val_mask, val);
22853ff3f64bSMark Brown 	if (err < 0)
2286db2a4165SFrank Mandarino 		return err;
2287db2a4165SFrank Mandarino 
2288974815baSPeter Ujfalusi 	if (type_2r)
2289907fe36aSLars-Peter Clausen 		err = snd_soc_component_update_bits(component, reg2, val_mask,
2290907fe36aSLars-Peter Clausen 			val2);
2291974815baSPeter Ujfalusi 
2292db2a4165SFrank Mandarino 	return err;
2293db2a4165SFrank Mandarino }
2294974815baSPeter Ujfalusi EXPORT_SYMBOL_GPL(snd_soc_put_volsw);
2295db2a4165SFrank Mandarino 
2296e13ac2e9SMark Brown /**
22971d99f243SBrian Austin  * snd_soc_get_volsw_sx - single mixer get callback
22981d99f243SBrian Austin  * @kcontrol: mixer control
22991d99f243SBrian Austin  * @ucontrol: control element information
23001d99f243SBrian Austin  *
23011d99f243SBrian Austin  * Callback to get the value of a single mixer control, or a double mixer
23021d99f243SBrian Austin  * control that spans 2 registers.
23031d99f243SBrian Austin  *
23041d99f243SBrian Austin  * Returns 0 for success.
23051d99f243SBrian Austin  */
23061d99f243SBrian Austin int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol,
23071d99f243SBrian Austin 		      struct snd_ctl_elem_value *ucontrol)
23081d99f243SBrian Austin {
2309907fe36aSLars-Peter Clausen 	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
23101d99f243SBrian Austin 	struct soc_mixer_control *mc =
23111d99f243SBrian Austin 	    (struct soc_mixer_control *)kcontrol->private_value;
23121d99f243SBrian Austin 	unsigned int reg = mc->reg;
23131d99f243SBrian Austin 	unsigned int reg2 = mc->rreg;
23141d99f243SBrian Austin 	unsigned int shift = mc->shift;
23151d99f243SBrian Austin 	unsigned int rshift = mc->rshift;
23161d99f243SBrian Austin 	int max = mc->max;
23171d99f243SBrian Austin 	int min = mc->min;
23181d99f243SBrian Austin 	int mask = (1 << (fls(min + max) - 1)) - 1;
2319907fe36aSLars-Peter Clausen 	unsigned int val;
2320907fe36aSLars-Peter Clausen 	int ret;
23211d99f243SBrian Austin 
2322907fe36aSLars-Peter Clausen 	ret = snd_soc_component_read(component, reg, &val);
2323907fe36aSLars-Peter Clausen 	if (ret < 0)
2324907fe36aSLars-Peter Clausen 		return ret;
23251d99f243SBrian Austin 
2326907fe36aSLars-Peter Clausen 	ucontrol->value.integer.value[0] = ((val >> shift) - min) & mask;
2327907fe36aSLars-Peter Clausen 
2328907fe36aSLars-Peter Clausen 	if (snd_soc_volsw_is_stereo(mc)) {
2329907fe36aSLars-Peter Clausen 		ret = snd_soc_component_read(component, reg2, &val);
2330907fe36aSLars-Peter Clausen 		if (ret < 0)
2331907fe36aSLars-Peter Clausen 			return ret;
2332907fe36aSLars-Peter Clausen 
2333907fe36aSLars-Peter Clausen 		val = ((val >> rshift) - min) & mask;
2334907fe36aSLars-Peter Clausen 		ucontrol->value.integer.value[1] = val;
2335907fe36aSLars-Peter Clausen 	}
23361d99f243SBrian Austin 
23371d99f243SBrian Austin 	return 0;
23381d99f243SBrian Austin }
23391d99f243SBrian Austin EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx);
23401d99f243SBrian Austin 
23411d99f243SBrian Austin /**
23421d99f243SBrian Austin  * snd_soc_put_volsw_sx - double mixer set callback
23431d99f243SBrian Austin  * @kcontrol: mixer control
23441d99f243SBrian Austin  * @uinfo: control element information
23451d99f243SBrian Austin  *
23461d99f243SBrian Austin  * Callback to set the value of a double mixer control that spans 2 registers.
23471d99f243SBrian Austin  *
23481d99f243SBrian Austin  * Returns 0 for success.
23491d99f243SBrian Austin  */
23501d99f243SBrian Austin int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol,
23511d99f243SBrian Austin 			 struct snd_ctl_elem_value *ucontrol)
23521d99f243SBrian Austin {
2353907fe36aSLars-Peter Clausen 	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
23541d99f243SBrian Austin 	struct soc_mixer_control *mc =
23551d99f243SBrian Austin 	    (struct soc_mixer_control *)kcontrol->private_value;
23561d99f243SBrian Austin 
23571d99f243SBrian Austin 	unsigned int reg = mc->reg;
23581d99f243SBrian Austin 	unsigned int reg2 = mc->rreg;
23591d99f243SBrian Austin 	unsigned int shift = mc->shift;
23601d99f243SBrian Austin 	unsigned int rshift = mc->rshift;
23611d99f243SBrian Austin 	int max = mc->max;
23621d99f243SBrian Austin 	int min = mc->min;
23631d99f243SBrian Austin 	int mask = (1 << (fls(min + max) - 1)) - 1;
236427f1d759SBrian Austin 	int err = 0;
23651a39019eSBard Liao 	unsigned int val, val_mask, val2 = 0;
23661d99f243SBrian Austin 
23671d99f243SBrian Austin 	val_mask = mask << shift;
23681d99f243SBrian Austin 	val = (ucontrol->value.integer.value[0] + min) & mask;
23691d99f243SBrian Austin 	val = val << shift;
23701d99f243SBrian Austin 
2371907fe36aSLars-Peter Clausen 	err = snd_soc_component_update_bits(component, reg, val_mask, val);
2372d055852eSMukund Navada 	if (err < 0)
23731d99f243SBrian Austin 		return err;
23741d99f243SBrian Austin 
23751d99f243SBrian Austin 	if (snd_soc_volsw_is_stereo(mc)) {
23761d99f243SBrian Austin 		val_mask = mask << rshift;
23771d99f243SBrian Austin 		val2 = (ucontrol->value.integer.value[1] + min) & mask;
23781d99f243SBrian Austin 		val2 = val2 << rshift;
23791d99f243SBrian Austin 
2380907fe36aSLars-Peter Clausen 		err = snd_soc_component_update_bits(component, reg2, val_mask,
2381907fe36aSLars-Peter Clausen 			val2);
23821d99f243SBrian Austin 	}
2383907fe36aSLars-Peter Clausen 	return err;
23841d99f243SBrian Austin }
23851d99f243SBrian Austin EXPORT_SYMBOL_GPL(snd_soc_put_volsw_sx);
23861d99f243SBrian Austin 
23871d99f243SBrian Austin /**
2388e13ac2e9SMark Brown  * snd_soc_info_volsw_s8 - signed mixer info callback
2389e13ac2e9SMark Brown  * @kcontrol: mixer control
2390e13ac2e9SMark Brown  * @uinfo: control element information
2391e13ac2e9SMark Brown  *
2392e13ac2e9SMark Brown  * Callback to provide information about a signed mixer control.
2393e13ac2e9SMark Brown  *
2394e13ac2e9SMark Brown  * Returns 0 for success.
2395e13ac2e9SMark Brown  */
2396e13ac2e9SMark Brown int snd_soc_info_volsw_s8(struct snd_kcontrol *kcontrol,
2397e13ac2e9SMark Brown 	struct snd_ctl_elem_info *uinfo)
2398e13ac2e9SMark Brown {
23994eaa9819SJon Smirl 	struct soc_mixer_control *mc =
24004eaa9819SJon Smirl 		(struct soc_mixer_control *)kcontrol->private_value;
2401d11bb4a9SPeter Ujfalusi 	int platform_max;
24024eaa9819SJon Smirl 	int min = mc->min;
2403e13ac2e9SMark Brown 
2404d11bb4a9SPeter Ujfalusi 	if (!mc->platform_max)
2405d11bb4a9SPeter Ujfalusi 		mc->platform_max = mc->max;
2406d11bb4a9SPeter Ujfalusi 	platform_max = mc->platform_max;
2407d11bb4a9SPeter Ujfalusi 
2408e13ac2e9SMark Brown 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
2409e13ac2e9SMark Brown 	uinfo->count = 2;
2410e13ac2e9SMark Brown 	uinfo->value.integer.min = 0;
2411d11bb4a9SPeter Ujfalusi 	uinfo->value.integer.max = platform_max - min;
2412e13ac2e9SMark Brown 	return 0;
2413e13ac2e9SMark Brown }
2414e13ac2e9SMark Brown EXPORT_SYMBOL_GPL(snd_soc_info_volsw_s8);
2415e13ac2e9SMark Brown 
2416e13ac2e9SMark Brown /**
2417e13ac2e9SMark Brown  * snd_soc_get_volsw_s8 - signed mixer get callback
2418e13ac2e9SMark Brown  * @kcontrol: mixer control
2419ac11a2b3SMark Brown  * @ucontrol: control element information
2420e13ac2e9SMark Brown  *
2421e13ac2e9SMark Brown  * Callback to get the value of a signed mixer control.
2422e13ac2e9SMark Brown  *
2423e13ac2e9SMark Brown  * Returns 0 for success.
2424e13ac2e9SMark Brown  */
2425e13ac2e9SMark Brown int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol,
2426e13ac2e9SMark Brown 	struct snd_ctl_elem_value *ucontrol)
2427e13ac2e9SMark Brown {
24284eaa9819SJon Smirl 	struct soc_mixer_control *mc =
24294eaa9819SJon Smirl 		(struct soc_mixer_control *)kcontrol->private_value;
2430907fe36aSLars-Peter Clausen 	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
2431815ecf8dSJon Smirl 	unsigned int reg = mc->reg;
2432907fe36aSLars-Peter Clausen 	unsigned int val;
24334eaa9819SJon Smirl 	int min = mc->min;
2434907fe36aSLars-Peter Clausen 	int ret;
2435907fe36aSLars-Peter Clausen 
2436907fe36aSLars-Peter Clausen 	ret = snd_soc_component_read(component, reg, &val);
2437907fe36aSLars-Peter Clausen 	if (ret)
2438907fe36aSLars-Peter Clausen 		return ret;
2439e13ac2e9SMark Brown 
2440e13ac2e9SMark Brown 	ucontrol->value.integer.value[0] =
2441e13ac2e9SMark Brown 		((signed char)(val & 0xff))-min;
2442e13ac2e9SMark Brown 	ucontrol->value.integer.value[1] =
2443e13ac2e9SMark Brown 		((signed char)((val >> 8) & 0xff))-min;
2444e13ac2e9SMark Brown 	return 0;
2445e13ac2e9SMark Brown }
2446e13ac2e9SMark Brown EXPORT_SYMBOL_GPL(snd_soc_get_volsw_s8);
2447e13ac2e9SMark Brown 
2448e13ac2e9SMark Brown /**
2449e13ac2e9SMark Brown  * snd_soc_put_volsw_sgn - signed mixer put callback
2450e13ac2e9SMark Brown  * @kcontrol: mixer control
2451ac11a2b3SMark Brown  * @ucontrol: control element information
2452e13ac2e9SMark Brown  *
2453e13ac2e9SMark Brown  * Callback to set the value of a signed mixer control.
2454e13ac2e9SMark Brown  *
2455e13ac2e9SMark Brown  * Returns 0 for success.
2456e13ac2e9SMark Brown  */
2457e13ac2e9SMark Brown int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
2458e13ac2e9SMark Brown 	struct snd_ctl_elem_value *ucontrol)
2459e13ac2e9SMark Brown {
24604eaa9819SJon Smirl 	struct soc_mixer_control *mc =
24614eaa9819SJon Smirl 		(struct soc_mixer_control *)kcontrol->private_value;
2462907fe36aSLars-Peter Clausen 	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
2463815ecf8dSJon Smirl 	unsigned int reg = mc->reg;
24644eaa9819SJon Smirl 	int min = mc->min;
246546f5822fSDaniel Ribeiro 	unsigned int val;
2466e13ac2e9SMark Brown 
2467e13ac2e9SMark Brown 	val = (ucontrol->value.integer.value[0]+min) & 0xff;
2468e13ac2e9SMark Brown 	val |= ((ucontrol->value.integer.value[1]+min) & 0xff) << 8;
2469e13ac2e9SMark Brown 
2470907fe36aSLars-Peter Clausen 	return snd_soc_component_update_bits(component, reg, 0xffff, val);
2471e13ac2e9SMark Brown }
2472e13ac2e9SMark Brown EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8);
2473e13ac2e9SMark Brown 
24748c6529dbSLiam Girdwood /**
24756c9d8cf6SAdam Thomson  * snd_soc_info_volsw_range - single mixer info callback with range.
24766c9d8cf6SAdam Thomson  * @kcontrol: mixer control
24776c9d8cf6SAdam Thomson  * @uinfo: control element information
24786c9d8cf6SAdam Thomson  *
24796c9d8cf6SAdam Thomson  * Callback to provide information, within a range, about a single
24806c9d8cf6SAdam Thomson  * mixer control.
24816c9d8cf6SAdam Thomson  *
24826c9d8cf6SAdam Thomson  * returns 0 for success.
24836c9d8cf6SAdam Thomson  */
24846c9d8cf6SAdam Thomson int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol,
24856c9d8cf6SAdam Thomson 	struct snd_ctl_elem_info *uinfo)
24866c9d8cf6SAdam Thomson {
24876c9d8cf6SAdam Thomson 	struct soc_mixer_control *mc =
24886c9d8cf6SAdam Thomson 		(struct soc_mixer_control *)kcontrol->private_value;
24896c9d8cf6SAdam Thomson 	int platform_max;
24906c9d8cf6SAdam Thomson 	int min = mc->min;
24916c9d8cf6SAdam Thomson 
24926c9d8cf6SAdam Thomson 	if (!mc->platform_max)
24936c9d8cf6SAdam Thomson 		mc->platform_max = mc->max;
24946c9d8cf6SAdam Thomson 	platform_max = mc->platform_max;
24956c9d8cf6SAdam Thomson 
24966c9d8cf6SAdam Thomson 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
24979bde4f0bSMark Brown 	uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1;
24986c9d8cf6SAdam Thomson 	uinfo->value.integer.min = 0;
24996c9d8cf6SAdam Thomson 	uinfo->value.integer.max = platform_max - min;
25006c9d8cf6SAdam Thomson 
25016c9d8cf6SAdam Thomson 	return 0;
25026c9d8cf6SAdam Thomson }
25036c9d8cf6SAdam Thomson EXPORT_SYMBOL_GPL(snd_soc_info_volsw_range);
25046c9d8cf6SAdam Thomson 
25056c9d8cf6SAdam Thomson /**
25066c9d8cf6SAdam Thomson  * snd_soc_put_volsw_range - single mixer put value callback with range.
25076c9d8cf6SAdam Thomson  * @kcontrol: mixer control
25086c9d8cf6SAdam Thomson  * @ucontrol: control element information
25096c9d8cf6SAdam Thomson  *
25106c9d8cf6SAdam Thomson  * Callback to set the value, within a range, for a single mixer control.
25116c9d8cf6SAdam Thomson  *
25126c9d8cf6SAdam Thomson  * Returns 0 for success.
25136c9d8cf6SAdam Thomson  */
25146c9d8cf6SAdam Thomson int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol,
25156c9d8cf6SAdam Thomson 	struct snd_ctl_elem_value *ucontrol)
25166c9d8cf6SAdam Thomson {
25176c9d8cf6SAdam Thomson 	struct soc_mixer_control *mc =
25186c9d8cf6SAdam Thomson 		(struct soc_mixer_control *)kcontrol->private_value;
2519907fe36aSLars-Peter Clausen 	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
25206c9d8cf6SAdam Thomson 	unsigned int reg = mc->reg;
25219bde4f0bSMark Brown 	unsigned int rreg = mc->rreg;
25226c9d8cf6SAdam Thomson 	unsigned int shift = mc->shift;
25236c9d8cf6SAdam Thomson 	int min = mc->min;
25246c9d8cf6SAdam Thomson 	int max = mc->max;
25256c9d8cf6SAdam Thomson 	unsigned int mask = (1 << fls(max)) - 1;
25266c9d8cf6SAdam Thomson 	unsigned int invert = mc->invert;
25276c9d8cf6SAdam Thomson 	unsigned int val, val_mask;
25289bde4f0bSMark Brown 	int ret;
25296c9d8cf6SAdam Thomson 
25306c9d8cf6SAdam Thomson 	if (invert)
25315c7c343aSHoward Mitchell 		val = (max - ucontrol->value.integer.value[0]) & mask;
25325c7c343aSHoward Mitchell 	else
25335c7c343aSHoward Mitchell 		val = ((ucontrol->value.integer.value[0] + min) & mask);
25346c9d8cf6SAdam Thomson 	val_mask = mask << shift;
25356c9d8cf6SAdam Thomson 	val = val << shift;
25366c9d8cf6SAdam Thomson 
2537907fe36aSLars-Peter Clausen 	ret = snd_soc_component_update_bits(component, reg, val_mask, val);
25380eaa6ccaSJoonyoung Shim 	if (ret < 0)
25399bde4f0bSMark Brown 		return ret;
25409bde4f0bSMark Brown 
25419bde4f0bSMark Brown 	if (snd_soc_volsw_is_stereo(mc)) {
25429bde4f0bSMark Brown 		if (invert)
25435c7c343aSHoward Mitchell 			val = (max - ucontrol->value.integer.value[1]) & mask;
25445c7c343aSHoward Mitchell 		else
25455c7c343aSHoward Mitchell 			val = ((ucontrol->value.integer.value[1] + min) & mask);
25469bde4f0bSMark Brown 		val_mask = mask << shift;
25479bde4f0bSMark Brown 		val = val << shift;
25489bde4f0bSMark Brown 
2549907fe36aSLars-Peter Clausen 		ret = snd_soc_component_update_bits(component, rreg, val_mask,
2550907fe36aSLars-Peter Clausen 			val);
25519bde4f0bSMark Brown 	}
25529bde4f0bSMark Brown 
25539bde4f0bSMark Brown 	return ret;
25546c9d8cf6SAdam Thomson }
25556c9d8cf6SAdam Thomson EXPORT_SYMBOL_GPL(snd_soc_put_volsw_range);
25566c9d8cf6SAdam Thomson 
25576c9d8cf6SAdam Thomson /**
25586c9d8cf6SAdam Thomson  * snd_soc_get_volsw_range - single mixer get callback with range
25596c9d8cf6SAdam Thomson  * @kcontrol: mixer control
25606c9d8cf6SAdam Thomson  * @ucontrol: control element information
25616c9d8cf6SAdam Thomson  *
25626c9d8cf6SAdam Thomson  * Callback to get the value, within a range, of a single mixer control.
25636c9d8cf6SAdam Thomson  *
25646c9d8cf6SAdam Thomson  * Returns 0 for success.
25656c9d8cf6SAdam Thomson  */
25666c9d8cf6SAdam Thomson int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol,
25676c9d8cf6SAdam Thomson 	struct snd_ctl_elem_value *ucontrol)
25686c9d8cf6SAdam Thomson {
2569907fe36aSLars-Peter Clausen 	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
25706c9d8cf6SAdam Thomson 	struct soc_mixer_control *mc =
25716c9d8cf6SAdam Thomson 		(struct soc_mixer_control *)kcontrol->private_value;
25726c9d8cf6SAdam Thomson 	unsigned int reg = mc->reg;
25739bde4f0bSMark Brown 	unsigned int rreg = mc->rreg;
25746c9d8cf6SAdam Thomson 	unsigned int shift = mc->shift;
25756c9d8cf6SAdam Thomson 	int min = mc->min;
25766c9d8cf6SAdam Thomson 	int max = mc->max;
25776c9d8cf6SAdam Thomson 	unsigned int mask = (1 << fls(max)) - 1;
25786c9d8cf6SAdam Thomson 	unsigned int invert = mc->invert;
2579907fe36aSLars-Peter Clausen 	unsigned int val;
2580907fe36aSLars-Peter Clausen 	int ret;
25816c9d8cf6SAdam Thomson 
2582907fe36aSLars-Peter Clausen 	ret = snd_soc_component_read(component, reg, &val);
2583907fe36aSLars-Peter Clausen 	if (ret)
2584907fe36aSLars-Peter Clausen 		return ret;
2585907fe36aSLars-Peter Clausen 
2586907fe36aSLars-Peter Clausen 	ucontrol->value.integer.value[0] = (val >> shift) & mask;
25876c9d8cf6SAdam Thomson 	if (invert)
25886c9d8cf6SAdam Thomson 		ucontrol->value.integer.value[0] =
25896c9d8cf6SAdam Thomson 			max - ucontrol->value.integer.value[0];
25905c7c343aSHoward Mitchell 	else
25916c9d8cf6SAdam Thomson 		ucontrol->value.integer.value[0] =
25926c9d8cf6SAdam Thomson 			ucontrol->value.integer.value[0] - min;
25936c9d8cf6SAdam Thomson 
25949bde4f0bSMark Brown 	if (snd_soc_volsw_is_stereo(mc)) {
2595907fe36aSLars-Peter Clausen 		ret = snd_soc_component_read(component, rreg, &val);
2596907fe36aSLars-Peter Clausen 		if (ret)
2597907fe36aSLars-Peter Clausen 			return ret;
2598907fe36aSLars-Peter Clausen 
2599907fe36aSLars-Peter Clausen 		ucontrol->value.integer.value[1] = (val >> shift) & mask;
26009bde4f0bSMark Brown 		if (invert)
26019bde4f0bSMark Brown 			ucontrol->value.integer.value[1] =
26029bde4f0bSMark Brown 				max - ucontrol->value.integer.value[1];
26035c7c343aSHoward Mitchell 		else
26049bde4f0bSMark Brown 			ucontrol->value.integer.value[1] =
26059bde4f0bSMark Brown 				ucontrol->value.integer.value[1] - min;
26069bde4f0bSMark Brown 	}
26079bde4f0bSMark Brown 
26086c9d8cf6SAdam Thomson 	return 0;
26096c9d8cf6SAdam Thomson }
26106c9d8cf6SAdam Thomson EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range);
26116c9d8cf6SAdam Thomson 
26126c9d8cf6SAdam Thomson /**
2613637d3847SPeter Ujfalusi  * snd_soc_limit_volume - Set new limit to an existing volume control.
2614637d3847SPeter Ujfalusi  *
2615637d3847SPeter Ujfalusi  * @codec: where to look for the control
2616637d3847SPeter Ujfalusi  * @name: Name of the control
2617637d3847SPeter Ujfalusi  * @max: new maximum limit
2618637d3847SPeter Ujfalusi  *
2619637d3847SPeter Ujfalusi  * Return 0 for success, else error.
2620637d3847SPeter Ujfalusi  */
2621637d3847SPeter Ujfalusi int snd_soc_limit_volume(struct snd_soc_codec *codec,
2622637d3847SPeter Ujfalusi 	const char *name, int max)
2623637d3847SPeter Ujfalusi {
262400200107SLars-Peter Clausen 	struct snd_card *card = codec->component.card->snd_card;
2625637d3847SPeter Ujfalusi 	struct snd_kcontrol *kctl;
2626637d3847SPeter Ujfalusi 	struct soc_mixer_control *mc;
2627637d3847SPeter Ujfalusi 	int found = 0;
2628637d3847SPeter Ujfalusi 	int ret = -EINVAL;
2629637d3847SPeter Ujfalusi 
2630637d3847SPeter Ujfalusi 	/* Sanity check for name and max */
2631637d3847SPeter Ujfalusi 	if (unlikely(!name || max <= 0))
2632637d3847SPeter Ujfalusi 		return -EINVAL;
2633637d3847SPeter Ujfalusi 
2634637d3847SPeter Ujfalusi 	list_for_each_entry(kctl, &card->controls, list) {
2635637d3847SPeter Ujfalusi 		if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) {
2636637d3847SPeter Ujfalusi 			found = 1;
2637637d3847SPeter Ujfalusi 			break;
2638637d3847SPeter Ujfalusi 		}
2639637d3847SPeter Ujfalusi 	}
2640637d3847SPeter Ujfalusi 	if (found) {
2641637d3847SPeter Ujfalusi 		mc = (struct soc_mixer_control *)kctl->private_value;
2642637d3847SPeter Ujfalusi 		if (max <= mc->max) {
2643d11bb4a9SPeter Ujfalusi 			mc->platform_max = max;
2644637d3847SPeter Ujfalusi 			ret = 0;
2645637d3847SPeter Ujfalusi 		}
2646637d3847SPeter Ujfalusi 	}
2647637d3847SPeter Ujfalusi 	return ret;
2648637d3847SPeter Ujfalusi }
2649637d3847SPeter Ujfalusi EXPORT_SYMBOL_GPL(snd_soc_limit_volume);
2650637d3847SPeter Ujfalusi 
265171d08516SMark Brown int snd_soc_bytes_info(struct snd_kcontrol *kcontrol,
265271d08516SMark Brown 		       struct snd_ctl_elem_info *uinfo)
265371d08516SMark Brown {
2654907fe36aSLars-Peter Clausen 	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
265571d08516SMark Brown 	struct soc_bytes *params = (void *)kcontrol->private_value;
265671d08516SMark Brown 
265771d08516SMark Brown 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
2658907fe36aSLars-Peter Clausen 	uinfo->count = params->num_regs * component->val_bytes;
265971d08516SMark Brown 
266071d08516SMark Brown 	return 0;
266171d08516SMark Brown }
266271d08516SMark Brown EXPORT_SYMBOL_GPL(snd_soc_bytes_info);
266371d08516SMark Brown 
266471d08516SMark Brown int snd_soc_bytes_get(struct snd_kcontrol *kcontrol,
266571d08516SMark Brown 		      struct snd_ctl_elem_value *ucontrol)
266671d08516SMark Brown {
2667907fe36aSLars-Peter Clausen 	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
266871d08516SMark Brown 	struct soc_bytes *params = (void *)kcontrol->private_value;
266971d08516SMark Brown 	int ret;
267071d08516SMark Brown 
2671907fe36aSLars-Peter Clausen 	if (component->regmap)
2672907fe36aSLars-Peter Clausen 		ret = regmap_raw_read(component->regmap, params->base,
267371d08516SMark Brown 				      ucontrol->value.bytes.data,
2674907fe36aSLars-Peter Clausen 				      params->num_regs * component->val_bytes);
267571d08516SMark Brown 	else
267671d08516SMark Brown 		ret = -EINVAL;
267771d08516SMark Brown 
2678f831b055SMark Brown 	/* Hide any masked bytes to ensure consistent data reporting */
2679f831b055SMark Brown 	if (ret == 0 && params->mask) {
2680907fe36aSLars-Peter Clausen 		switch (component->val_bytes) {
2681f831b055SMark Brown 		case 1:
2682f831b055SMark Brown 			ucontrol->value.bytes.data[0] &= ~params->mask;
2683f831b055SMark Brown 			break;
2684f831b055SMark Brown 		case 2:
2685f831b055SMark Brown 			((u16 *)(&ucontrol->value.bytes.data))[0]
26868f1ec93aSCharles Keepax 				&= cpu_to_be16(~params->mask);
2687f831b055SMark Brown 			break;
2688f831b055SMark Brown 		case 4:
2689f831b055SMark Brown 			((u32 *)(&ucontrol->value.bytes.data))[0]
26908f1ec93aSCharles Keepax 				&= cpu_to_be32(~params->mask);
2691f831b055SMark Brown 			break;
2692f831b055SMark Brown 		default:
2693f831b055SMark Brown 			return -EINVAL;
2694f831b055SMark Brown 		}
2695f831b055SMark Brown 	}
2696f831b055SMark Brown 
269771d08516SMark Brown 	return ret;
269871d08516SMark Brown }
269971d08516SMark Brown EXPORT_SYMBOL_GPL(snd_soc_bytes_get);
270071d08516SMark Brown 
270171d08516SMark Brown int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
270271d08516SMark Brown 		      struct snd_ctl_elem_value *ucontrol)
270371d08516SMark Brown {
2704907fe36aSLars-Peter Clausen 	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
270571d08516SMark Brown 	struct soc_bytes *params = (void *)kcontrol->private_value;
2706f831b055SMark Brown 	int ret, len;
2707a1a564edSNenghua Cao 	unsigned int val, mask;
2708f831b055SMark Brown 	void *data;
270971d08516SMark Brown 
27106596aa04SXiubo Li 	if (!component->regmap || !params->num_regs)
2711f831b055SMark Brown 		return -EINVAL;
2712f831b055SMark Brown 
2713907fe36aSLars-Peter Clausen 	len = params->num_regs * component->val_bytes;
2714f831b055SMark Brown 
2715b5a8fe43SMark Brown 	data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA);
2716b5a8fe43SMark Brown 	if (!data)
2717b5a8fe43SMark Brown 		return -ENOMEM;
2718b5a8fe43SMark Brown 
2719f831b055SMark Brown 	/*
2720f831b055SMark Brown 	 * If we've got a mask then we need to preserve the register
2721f831b055SMark Brown 	 * bits.  We shouldn't modify the incoming data so take a
2722f831b055SMark Brown 	 * copy.
2723f831b055SMark Brown 	 */
2724f831b055SMark Brown 	if (params->mask) {
2725907fe36aSLars-Peter Clausen 		ret = regmap_read(component->regmap, params->base, &val);
2726f831b055SMark Brown 		if (ret != 0)
2727e8b18addSWei Yongjun 			goto out;
2728f831b055SMark Brown 
2729f831b055SMark Brown 		val &= params->mask;
2730f831b055SMark Brown 
2731907fe36aSLars-Peter Clausen 		switch (component->val_bytes) {
2732f831b055SMark Brown 		case 1:
2733f831b055SMark Brown 			((u8 *)data)[0] &= ~params->mask;
2734f831b055SMark Brown 			((u8 *)data)[0] |= val;
2735f831b055SMark Brown 			break;
2736f831b055SMark Brown 		case 2:
2737a1a564edSNenghua Cao 			mask = ~params->mask;
2738907fe36aSLars-Peter Clausen 			ret = regmap_parse_val(component->regmap,
2739a1a564edSNenghua Cao 							&mask, &mask);
2740a1a564edSNenghua Cao 			if (ret != 0)
2741a1a564edSNenghua Cao 				goto out;
2742a1a564edSNenghua Cao 
2743a1a564edSNenghua Cao 			((u16 *)data)[0] &= mask;
2744a1a564edSNenghua Cao 
2745907fe36aSLars-Peter Clausen 			ret = regmap_parse_val(component->regmap,
2746a1a564edSNenghua Cao 							&val, &val);
2747a1a564edSNenghua Cao 			if (ret != 0)
2748a1a564edSNenghua Cao 				goto out;
2749a1a564edSNenghua Cao 
2750a1a564edSNenghua Cao 			((u16 *)data)[0] |= val;
2751f831b055SMark Brown 			break;
2752f831b055SMark Brown 		case 4:
2753a1a564edSNenghua Cao 			mask = ~params->mask;
2754907fe36aSLars-Peter Clausen 			ret = regmap_parse_val(component->regmap,
2755a1a564edSNenghua Cao 							&mask, &mask);
2756a1a564edSNenghua Cao 			if (ret != 0)
2757a1a564edSNenghua Cao 				goto out;
2758a1a564edSNenghua Cao 
2759a1a564edSNenghua Cao 			((u32 *)data)[0] &= mask;
2760a1a564edSNenghua Cao 
2761907fe36aSLars-Peter Clausen 			ret = regmap_parse_val(component->regmap,
2762a1a564edSNenghua Cao 							&val, &val);
2763a1a564edSNenghua Cao 			if (ret != 0)
2764a1a564edSNenghua Cao 				goto out;
2765a1a564edSNenghua Cao 
2766a1a564edSNenghua Cao 			((u32 *)data)[0] |= val;
2767f831b055SMark Brown 			break;
2768f831b055SMark Brown 		default:
2769e8b18addSWei Yongjun 			ret = -EINVAL;
2770e8b18addSWei Yongjun 			goto out;
2771f831b055SMark Brown 		}
2772f831b055SMark Brown 	}
2773f831b055SMark Brown 
2774907fe36aSLars-Peter Clausen 	ret = regmap_raw_write(component->regmap, params->base,
2775f831b055SMark Brown 			       data, len);
2776f831b055SMark Brown 
2777e8b18addSWei Yongjun out:
2778f831b055SMark Brown 	kfree(data);
277971d08516SMark Brown 
278071d08516SMark Brown 	return ret;
278171d08516SMark Brown }
278271d08516SMark Brown EXPORT_SYMBOL_GPL(snd_soc_bytes_put);
278371d08516SMark Brown 
2784d9881208SVinod Koul int snd_soc_bytes_info_ext(struct snd_kcontrol *kcontrol,
2785d9881208SVinod Koul 			struct snd_ctl_elem_info *ucontrol)
2786d9881208SVinod Koul {
2787d9881208SVinod Koul 	struct soc_bytes_ext *params = (void *)kcontrol->private_value;
2788d9881208SVinod Koul 
2789d9881208SVinod Koul 	ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES;
2790d9881208SVinod Koul 	ucontrol->count = params->max;
2791d9881208SVinod Koul 
2792d9881208SVinod Koul 	return 0;
2793d9881208SVinod Koul }
2794d9881208SVinod Koul EXPORT_SYMBOL_GPL(snd_soc_bytes_info_ext);
2795d9881208SVinod Koul 
27967523a271SOmair Mohammed Abdullah int snd_soc_bytes_tlv_callback(struct snd_kcontrol *kcontrol, int op_flag,
27977523a271SOmair Mohammed Abdullah 				unsigned int size, unsigned int __user *tlv)
27987523a271SOmair Mohammed Abdullah {
27997523a271SOmair Mohammed Abdullah 	struct soc_bytes_ext *params = (void *)kcontrol->private_value;
28007523a271SOmair Mohammed Abdullah 	unsigned int count = size < params->max ? size : params->max;
28017523a271SOmair Mohammed Abdullah 	int ret = -ENXIO;
28027523a271SOmair Mohammed Abdullah 
28037523a271SOmair Mohammed Abdullah 	switch (op_flag) {
28047523a271SOmair Mohammed Abdullah 	case SNDRV_CTL_TLV_OP_READ:
28057523a271SOmair Mohammed Abdullah 		if (params->get)
28067523a271SOmair Mohammed Abdullah 			ret = params->get(tlv, count);
28077523a271SOmair Mohammed Abdullah 		break;
28087523a271SOmair Mohammed Abdullah 	case SNDRV_CTL_TLV_OP_WRITE:
28097523a271SOmair Mohammed Abdullah 		if (params->put)
28107523a271SOmair Mohammed Abdullah 			ret = params->put(tlv, count);
28117523a271SOmair Mohammed Abdullah 		break;
28127523a271SOmair Mohammed Abdullah 	}
28137523a271SOmair Mohammed Abdullah 	return ret;
28147523a271SOmair Mohammed Abdullah }
28157523a271SOmair Mohammed Abdullah EXPORT_SYMBOL_GPL(snd_soc_bytes_tlv_callback);
28167523a271SOmair Mohammed Abdullah 
2817b6f4bb38Sapatard@mandriva.com /**
28184183eed2SKristoffer KARLSSON  * snd_soc_info_xr_sx - signed multi register info callback
28194183eed2SKristoffer KARLSSON  * @kcontrol: mreg control
28204183eed2SKristoffer KARLSSON  * @uinfo: control element information
28214183eed2SKristoffer KARLSSON  *
28224183eed2SKristoffer KARLSSON  * Callback to provide information of a control that can
28234183eed2SKristoffer KARLSSON  * span multiple codec registers which together
28244183eed2SKristoffer KARLSSON  * forms a single signed value in a MSB/LSB manner.
28254183eed2SKristoffer KARLSSON  *
28264183eed2SKristoffer KARLSSON  * Returns 0 for success.
28274183eed2SKristoffer KARLSSON  */
28284183eed2SKristoffer KARLSSON int snd_soc_info_xr_sx(struct snd_kcontrol *kcontrol,
28294183eed2SKristoffer KARLSSON 	struct snd_ctl_elem_info *uinfo)
28304183eed2SKristoffer KARLSSON {
28314183eed2SKristoffer KARLSSON 	struct soc_mreg_control *mc =
28324183eed2SKristoffer KARLSSON 		(struct soc_mreg_control *)kcontrol->private_value;
28334183eed2SKristoffer KARLSSON 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
28344183eed2SKristoffer KARLSSON 	uinfo->count = 1;
28354183eed2SKristoffer KARLSSON 	uinfo->value.integer.min = mc->min;
28364183eed2SKristoffer KARLSSON 	uinfo->value.integer.max = mc->max;
28374183eed2SKristoffer KARLSSON 
28384183eed2SKristoffer KARLSSON 	return 0;
28394183eed2SKristoffer KARLSSON }
28404183eed2SKristoffer KARLSSON EXPORT_SYMBOL_GPL(snd_soc_info_xr_sx);
28414183eed2SKristoffer KARLSSON 
28424183eed2SKristoffer KARLSSON /**
28434183eed2SKristoffer KARLSSON  * snd_soc_get_xr_sx - signed multi register get callback
28444183eed2SKristoffer KARLSSON  * @kcontrol: mreg control
28454183eed2SKristoffer KARLSSON  * @ucontrol: control element information
28464183eed2SKristoffer KARLSSON  *
28474183eed2SKristoffer KARLSSON  * Callback to get the value of a control that can span
28484183eed2SKristoffer KARLSSON  * multiple codec registers which together forms a single
28494183eed2SKristoffer KARLSSON  * signed value in a MSB/LSB manner. The control supports
28504183eed2SKristoffer KARLSSON  * specifying total no of bits used to allow for bitfields
28514183eed2SKristoffer KARLSSON  * across the multiple codec registers.
28524183eed2SKristoffer KARLSSON  *
28534183eed2SKristoffer KARLSSON  * Returns 0 for success.
28544183eed2SKristoffer KARLSSON  */
28554183eed2SKristoffer KARLSSON int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol,
28564183eed2SKristoffer KARLSSON 	struct snd_ctl_elem_value *ucontrol)
28574183eed2SKristoffer KARLSSON {
2858907fe36aSLars-Peter Clausen 	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
28594183eed2SKristoffer KARLSSON 	struct soc_mreg_control *mc =
28604183eed2SKristoffer KARLSSON 		(struct soc_mreg_control *)kcontrol->private_value;
28614183eed2SKristoffer KARLSSON 	unsigned int regbase = mc->regbase;
28624183eed2SKristoffer KARLSSON 	unsigned int regcount = mc->regcount;
2863907fe36aSLars-Peter Clausen 	unsigned int regwshift = component->val_bytes * BITS_PER_BYTE;
28644183eed2SKristoffer KARLSSON 	unsigned int regwmask = (1<<regwshift)-1;
28654183eed2SKristoffer KARLSSON 	unsigned int invert = mc->invert;
28664183eed2SKristoffer KARLSSON 	unsigned long mask = (1UL<<mc->nbits)-1;
28674183eed2SKristoffer KARLSSON 	long min = mc->min;
28684183eed2SKristoffer KARLSSON 	long max = mc->max;
28694183eed2SKristoffer KARLSSON 	long val = 0;
2870907fe36aSLars-Peter Clausen 	unsigned int regval;
28714183eed2SKristoffer KARLSSON 	unsigned int i;
2872907fe36aSLars-Peter Clausen 	int ret;
28734183eed2SKristoffer KARLSSON 
28744183eed2SKristoffer KARLSSON 	for (i = 0; i < regcount; i++) {
2875907fe36aSLars-Peter Clausen 		ret = snd_soc_component_read(component, regbase+i, &regval);
2876907fe36aSLars-Peter Clausen 		if (ret)
2877907fe36aSLars-Peter Clausen 			return ret;
2878907fe36aSLars-Peter Clausen 		val |= (regval & regwmask) << (regwshift*(regcount-i-1));
28794183eed2SKristoffer KARLSSON 	}
28804183eed2SKristoffer KARLSSON 	val &= mask;
28814183eed2SKristoffer KARLSSON 	if (min < 0 && val > max)
28824183eed2SKristoffer KARLSSON 		val |= ~mask;
28834183eed2SKristoffer KARLSSON 	if (invert)
28844183eed2SKristoffer KARLSSON 		val = max - val;
28854183eed2SKristoffer KARLSSON 	ucontrol->value.integer.value[0] = val;
28864183eed2SKristoffer KARLSSON 
28874183eed2SKristoffer KARLSSON 	return 0;
28884183eed2SKristoffer KARLSSON }
28894183eed2SKristoffer KARLSSON EXPORT_SYMBOL_GPL(snd_soc_get_xr_sx);
28904183eed2SKristoffer KARLSSON 
28914183eed2SKristoffer KARLSSON /**
28924183eed2SKristoffer KARLSSON  * snd_soc_put_xr_sx - signed multi register get callback
28934183eed2SKristoffer KARLSSON  * @kcontrol: mreg control
28944183eed2SKristoffer KARLSSON  * @ucontrol: control element information
28954183eed2SKristoffer KARLSSON  *
28964183eed2SKristoffer KARLSSON  * Callback to set the value of a control that can span
28974183eed2SKristoffer KARLSSON  * multiple codec registers which together forms a single
28984183eed2SKristoffer KARLSSON  * signed value in a MSB/LSB manner. The control supports
28994183eed2SKristoffer KARLSSON  * specifying total no of bits used to allow for bitfields
29004183eed2SKristoffer KARLSSON  * across the multiple codec registers.
29014183eed2SKristoffer KARLSSON  *
29024183eed2SKristoffer KARLSSON  * Returns 0 for success.
29034183eed2SKristoffer KARLSSON  */
29044183eed2SKristoffer KARLSSON int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol,
29054183eed2SKristoffer KARLSSON 	struct snd_ctl_elem_value *ucontrol)
29064183eed2SKristoffer KARLSSON {
2907907fe36aSLars-Peter Clausen 	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
29084183eed2SKristoffer KARLSSON 	struct soc_mreg_control *mc =
29094183eed2SKristoffer KARLSSON 		(struct soc_mreg_control *)kcontrol->private_value;
29104183eed2SKristoffer KARLSSON 	unsigned int regbase = mc->regbase;
29114183eed2SKristoffer KARLSSON 	unsigned int regcount = mc->regcount;
2912907fe36aSLars-Peter Clausen 	unsigned int regwshift = component->val_bytes * BITS_PER_BYTE;
29134183eed2SKristoffer KARLSSON 	unsigned int regwmask = (1<<regwshift)-1;
29144183eed2SKristoffer KARLSSON 	unsigned int invert = mc->invert;
29154183eed2SKristoffer KARLSSON 	unsigned long mask = (1UL<<mc->nbits)-1;
29164183eed2SKristoffer KARLSSON 	long max = mc->max;
29174183eed2SKristoffer KARLSSON 	long val = ucontrol->value.integer.value[0];
29184183eed2SKristoffer KARLSSON 	unsigned int i, regval, regmask;
29194183eed2SKristoffer KARLSSON 	int err;
29204183eed2SKristoffer KARLSSON 
29214183eed2SKristoffer KARLSSON 	if (invert)
29224183eed2SKristoffer KARLSSON 		val = max - val;
29234183eed2SKristoffer KARLSSON 	val &= mask;
29244183eed2SKristoffer KARLSSON 	for (i = 0; i < regcount; i++) {
29254183eed2SKristoffer KARLSSON 		regval = (val >> (regwshift*(regcount-i-1))) & regwmask;
29264183eed2SKristoffer KARLSSON 		regmask = (mask >> (regwshift*(regcount-i-1))) & regwmask;
2927907fe36aSLars-Peter Clausen 		err = snd_soc_component_update_bits(component, regbase+i,
29284183eed2SKristoffer KARLSSON 				regmask, regval);
29294183eed2SKristoffer KARLSSON 		if (err < 0)
29304183eed2SKristoffer KARLSSON 			return err;
29314183eed2SKristoffer KARLSSON 	}
29324183eed2SKristoffer KARLSSON 
29334183eed2SKristoffer KARLSSON 	return 0;
29344183eed2SKristoffer KARLSSON }
29354183eed2SKristoffer KARLSSON EXPORT_SYMBOL_GPL(snd_soc_put_xr_sx);
29364183eed2SKristoffer KARLSSON 
29374183eed2SKristoffer KARLSSON /**
2938dd7b10b3SKristoffer KARLSSON  * snd_soc_get_strobe - strobe get callback
2939dd7b10b3SKristoffer KARLSSON  * @kcontrol: mixer control
2940dd7b10b3SKristoffer KARLSSON  * @ucontrol: control element information
2941dd7b10b3SKristoffer KARLSSON  *
2942dd7b10b3SKristoffer KARLSSON  * Callback get the value of a strobe mixer control.
2943dd7b10b3SKristoffer KARLSSON  *
2944dd7b10b3SKristoffer KARLSSON  * Returns 0 for success.
2945dd7b10b3SKristoffer KARLSSON  */
2946dd7b10b3SKristoffer KARLSSON int snd_soc_get_strobe(struct snd_kcontrol *kcontrol,
2947dd7b10b3SKristoffer KARLSSON 	struct snd_ctl_elem_value *ucontrol)
2948dd7b10b3SKristoffer KARLSSON {
2949907fe36aSLars-Peter Clausen 	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
2950dd7b10b3SKristoffer KARLSSON 	struct soc_mixer_control *mc =
2951dd7b10b3SKristoffer KARLSSON 		(struct soc_mixer_control *)kcontrol->private_value;
2952dd7b10b3SKristoffer KARLSSON 	unsigned int reg = mc->reg;
2953dd7b10b3SKristoffer KARLSSON 	unsigned int shift = mc->shift;
2954dd7b10b3SKristoffer KARLSSON 	unsigned int mask = 1 << shift;
2955dd7b10b3SKristoffer KARLSSON 	unsigned int invert = mc->invert != 0;
2956907fe36aSLars-Peter Clausen 	unsigned int val;
2957907fe36aSLars-Peter Clausen 	int ret;
2958907fe36aSLars-Peter Clausen 
2959907fe36aSLars-Peter Clausen 	ret = snd_soc_component_read(component, reg, &val);
2960907fe36aSLars-Peter Clausen 	if (ret)
2961907fe36aSLars-Peter Clausen 		return ret;
2962907fe36aSLars-Peter Clausen 
2963907fe36aSLars-Peter Clausen 	val &= mask;
2964dd7b10b3SKristoffer KARLSSON 
2965dd7b10b3SKristoffer KARLSSON 	if (shift != 0 && val != 0)
2966dd7b10b3SKristoffer KARLSSON 		val = val >> shift;
2967dd7b10b3SKristoffer KARLSSON 	ucontrol->value.enumerated.item[0] = val ^ invert;
2968dd7b10b3SKristoffer KARLSSON 
2969dd7b10b3SKristoffer KARLSSON 	return 0;
2970dd7b10b3SKristoffer KARLSSON }
2971dd7b10b3SKristoffer KARLSSON EXPORT_SYMBOL_GPL(snd_soc_get_strobe);
2972dd7b10b3SKristoffer KARLSSON 
2973dd7b10b3SKristoffer KARLSSON /**
2974dd7b10b3SKristoffer KARLSSON  * snd_soc_put_strobe - strobe put callback
2975dd7b10b3SKristoffer KARLSSON  * @kcontrol: mixer control
2976dd7b10b3SKristoffer KARLSSON  * @ucontrol: control element information
2977dd7b10b3SKristoffer KARLSSON  *
2978dd7b10b3SKristoffer KARLSSON  * Callback strobe a register bit to high then low (or the inverse)
2979dd7b10b3SKristoffer KARLSSON  * in one pass of a single mixer enum control.
2980dd7b10b3SKristoffer KARLSSON  *
2981dd7b10b3SKristoffer KARLSSON  * Returns 1 for success.
2982dd7b10b3SKristoffer KARLSSON  */
2983dd7b10b3SKristoffer KARLSSON int snd_soc_put_strobe(struct snd_kcontrol *kcontrol,
2984dd7b10b3SKristoffer KARLSSON 	struct snd_ctl_elem_value *ucontrol)
2985dd7b10b3SKristoffer KARLSSON {
2986907fe36aSLars-Peter Clausen 	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
2987dd7b10b3SKristoffer KARLSSON 	struct soc_mixer_control *mc =
2988dd7b10b3SKristoffer KARLSSON 		(struct soc_mixer_control *)kcontrol->private_value;
2989dd7b10b3SKristoffer KARLSSON 	unsigned int reg = mc->reg;
2990dd7b10b3SKristoffer KARLSSON 	unsigned int shift = mc->shift;
2991dd7b10b3SKristoffer KARLSSON 	unsigned int mask = 1 << shift;
2992dd7b10b3SKristoffer KARLSSON 	unsigned int invert = mc->invert != 0;
2993dd7b10b3SKristoffer KARLSSON 	unsigned int strobe = ucontrol->value.enumerated.item[0] != 0;
2994dd7b10b3SKristoffer KARLSSON 	unsigned int val1 = (strobe ^ invert) ? mask : 0;
2995dd7b10b3SKristoffer KARLSSON 	unsigned int val2 = (strobe ^ invert) ? 0 : mask;
2996dd7b10b3SKristoffer KARLSSON 	int err;
2997dd7b10b3SKristoffer KARLSSON 
2998907fe36aSLars-Peter Clausen 	err = snd_soc_component_update_bits(component, reg, mask, val1);
2999dd7b10b3SKristoffer KARLSSON 	if (err < 0)
3000dd7b10b3SKristoffer KARLSSON 		return err;
3001dd7b10b3SKristoffer KARLSSON 
3002907fe36aSLars-Peter Clausen 	return snd_soc_component_update_bits(component, reg, mask, val2);
3003dd7b10b3SKristoffer KARLSSON }
3004dd7b10b3SKristoffer KARLSSON EXPORT_SYMBOL_GPL(snd_soc_put_strobe);
3005dd7b10b3SKristoffer KARLSSON 
3006dd7b10b3SKristoffer KARLSSON /**
30078c6529dbSLiam Girdwood  * snd_soc_dai_set_sysclk - configure DAI system or master clock.
30088c6529dbSLiam Girdwood  * @dai: DAI
30098c6529dbSLiam Girdwood  * @clk_id: DAI specific clock ID
30108c6529dbSLiam Girdwood  * @freq: new clock frequency in Hz
30118c6529dbSLiam Girdwood  * @dir: new clock direction - input/output.
30128c6529dbSLiam Girdwood  *
30138c6529dbSLiam Girdwood  * Configures the DAI master (MCLK) or system (SYSCLK) clocking.
30148c6529dbSLiam Girdwood  */
30158c6529dbSLiam Girdwood int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
30168c6529dbSLiam Girdwood 	unsigned int freq, int dir)
30178c6529dbSLiam Girdwood {
3018f0fba2adSLiam Girdwood 	if (dai->driver && dai->driver->ops->set_sysclk)
3019f0fba2adSLiam Girdwood 		return dai->driver->ops->set_sysclk(dai, clk_id, freq, dir);
3020ec4ee52aSMark Brown 	else if (dai->codec && dai->codec->driver->set_sysclk)
3021da1c6ea6SMark Brown 		return dai->codec->driver->set_sysclk(dai->codec, clk_id, 0,
3022ec4ee52aSMark Brown 						      freq, dir);
30238c6529dbSLiam Girdwood 	else
30241104a9c8SMark Brown 		return -ENOTSUPP;
30258c6529dbSLiam Girdwood }
30268c6529dbSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk);
30278c6529dbSLiam Girdwood 
30288c6529dbSLiam Girdwood /**
3029ec4ee52aSMark Brown  * snd_soc_codec_set_sysclk - configure CODEC system or master clock.
3030ec4ee52aSMark Brown  * @codec: CODEC
3031ec4ee52aSMark Brown  * @clk_id: DAI specific clock ID
3032da1c6ea6SMark Brown  * @source: Source for the clock
3033ec4ee52aSMark Brown  * @freq: new clock frequency in Hz
3034ec4ee52aSMark Brown  * @dir: new clock direction - input/output.
3035ec4ee52aSMark Brown  *
3036ec4ee52aSMark Brown  * Configures the CODEC master (MCLK) or system (SYSCLK) clocking.
3037ec4ee52aSMark Brown  */
3038ec4ee52aSMark Brown int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id,
3039da1c6ea6SMark Brown 			     int source, unsigned int freq, int dir)
3040ec4ee52aSMark Brown {
3041ec4ee52aSMark Brown 	if (codec->driver->set_sysclk)
3042da1c6ea6SMark Brown 		return codec->driver->set_sysclk(codec, clk_id, source,
3043da1c6ea6SMark Brown 						 freq, dir);
3044ec4ee52aSMark Brown 	else
30451104a9c8SMark Brown 		return -ENOTSUPP;
3046ec4ee52aSMark Brown }
3047ec4ee52aSMark Brown EXPORT_SYMBOL_GPL(snd_soc_codec_set_sysclk);
3048ec4ee52aSMark Brown 
3049ec4ee52aSMark Brown /**
30508c6529dbSLiam Girdwood  * snd_soc_dai_set_clkdiv - configure DAI clock dividers.
30518c6529dbSLiam Girdwood  * @dai: DAI
3052ac11a2b3SMark Brown  * @div_id: DAI specific clock divider ID
30538c6529dbSLiam Girdwood  * @div: new clock divisor.
30548c6529dbSLiam Girdwood  *
30558c6529dbSLiam Girdwood  * Configures the clock dividers. This is used to derive the best DAI bit and
30568c6529dbSLiam Girdwood  * frame clocks from the system or master clock. It's best to set the DAI bit
30578c6529dbSLiam Girdwood  * and frame clocks as low as possible to save system power.
30588c6529dbSLiam Girdwood  */
30598c6529dbSLiam Girdwood int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
30608c6529dbSLiam Girdwood 	int div_id, int div)
30618c6529dbSLiam Girdwood {
3062f0fba2adSLiam Girdwood 	if (dai->driver && dai->driver->ops->set_clkdiv)
3063f0fba2adSLiam Girdwood 		return dai->driver->ops->set_clkdiv(dai, div_id, div);
30648c6529dbSLiam Girdwood 	else
30658c6529dbSLiam Girdwood 		return -EINVAL;
30668c6529dbSLiam Girdwood }
30678c6529dbSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv);
30688c6529dbSLiam Girdwood 
30698c6529dbSLiam Girdwood /**
30708c6529dbSLiam Girdwood  * snd_soc_dai_set_pll - configure DAI PLL.
30718c6529dbSLiam Girdwood  * @dai: DAI
30728c6529dbSLiam Girdwood  * @pll_id: DAI specific PLL ID
307385488037SMark Brown  * @source: DAI specific source for the PLL
30748c6529dbSLiam Girdwood  * @freq_in: PLL input clock frequency in Hz
30758c6529dbSLiam Girdwood  * @freq_out: requested PLL output clock frequency in Hz
30768c6529dbSLiam Girdwood  *
30778c6529dbSLiam Girdwood  * Configures and enables PLL to generate output clock based on input clock.
30788c6529dbSLiam Girdwood  */
307985488037SMark Brown int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
308085488037SMark Brown 	unsigned int freq_in, unsigned int freq_out)
30818c6529dbSLiam Girdwood {
3082f0fba2adSLiam Girdwood 	if (dai->driver && dai->driver->ops->set_pll)
3083f0fba2adSLiam Girdwood 		return dai->driver->ops->set_pll(dai, pll_id, source,
308485488037SMark Brown 					 freq_in, freq_out);
3085ec4ee52aSMark Brown 	else if (dai->codec && dai->codec->driver->set_pll)
3086ec4ee52aSMark Brown 		return dai->codec->driver->set_pll(dai->codec, pll_id, source,
3087ec4ee52aSMark Brown 						   freq_in, freq_out);
30888c6529dbSLiam Girdwood 	else
30898c6529dbSLiam Girdwood 		return -EINVAL;
30908c6529dbSLiam Girdwood }
30918c6529dbSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll);
30928c6529dbSLiam Girdwood 
3093ec4ee52aSMark Brown /*
3094ec4ee52aSMark Brown  * snd_soc_codec_set_pll - configure codec PLL.
3095ec4ee52aSMark Brown  * @codec: CODEC
3096ec4ee52aSMark Brown  * @pll_id: DAI specific PLL ID
3097ec4ee52aSMark Brown  * @source: DAI specific source for the PLL
3098ec4ee52aSMark Brown  * @freq_in: PLL input clock frequency in Hz
3099ec4ee52aSMark Brown  * @freq_out: requested PLL output clock frequency in Hz
3100ec4ee52aSMark Brown  *
3101ec4ee52aSMark Brown  * Configures and enables PLL to generate output clock based on input clock.
3102ec4ee52aSMark Brown  */
3103ec4ee52aSMark Brown int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
3104ec4ee52aSMark Brown 			  unsigned int freq_in, unsigned int freq_out)
3105ec4ee52aSMark Brown {
3106ec4ee52aSMark Brown 	if (codec->driver->set_pll)
3107ec4ee52aSMark Brown 		return codec->driver->set_pll(codec, pll_id, source,
3108ec4ee52aSMark Brown 					      freq_in, freq_out);
3109ec4ee52aSMark Brown 	else
3110ec4ee52aSMark Brown 		return -EINVAL;
3111ec4ee52aSMark Brown }
3112ec4ee52aSMark Brown EXPORT_SYMBOL_GPL(snd_soc_codec_set_pll);
3113ec4ee52aSMark Brown 
31148c6529dbSLiam Girdwood /**
3115e54cf76bSLiam Girdwood  * snd_soc_dai_set_bclk_ratio - configure BCLK to sample rate ratio.
3116e54cf76bSLiam Girdwood  * @dai: DAI
3117e54cf76bSLiam Girdwood  * @ratio Ratio of BCLK to Sample rate.
3118e54cf76bSLiam Girdwood  *
3119e54cf76bSLiam Girdwood  * Configures the DAI for a preset BCLK to sample rate ratio.
3120e54cf76bSLiam Girdwood  */
3121e54cf76bSLiam Girdwood int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
3122e54cf76bSLiam Girdwood {
3123e54cf76bSLiam Girdwood 	if (dai->driver && dai->driver->ops->set_bclk_ratio)
3124e54cf76bSLiam Girdwood 		return dai->driver->ops->set_bclk_ratio(dai, ratio);
3125e54cf76bSLiam Girdwood 	else
3126e54cf76bSLiam Girdwood 		return -EINVAL;
3127e54cf76bSLiam Girdwood }
3128e54cf76bSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dai_set_bclk_ratio);
3129e54cf76bSLiam Girdwood 
3130e54cf76bSLiam Girdwood /**
31318c6529dbSLiam Girdwood  * snd_soc_dai_set_fmt - configure DAI hardware audio format.
31328c6529dbSLiam Girdwood  * @dai: DAI
31338c6529dbSLiam Girdwood  * @fmt: SND_SOC_DAIFMT_ format value.
31348c6529dbSLiam Girdwood  *
31358c6529dbSLiam Girdwood  * Configures the DAI hardware format and clocking.
31368c6529dbSLiam Girdwood  */
31378c6529dbSLiam Girdwood int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
31388c6529dbSLiam Girdwood {
31395e4ba569SShawn Guo 	if (dai->driver == NULL)
31408c6529dbSLiam Girdwood 		return -EINVAL;
31415e4ba569SShawn Guo 	if (dai->driver->ops->set_fmt == NULL)
31425e4ba569SShawn Guo 		return -ENOTSUPP;
31435e4ba569SShawn Guo 	return dai->driver->ops->set_fmt(dai, fmt);
31448c6529dbSLiam Girdwood }
31458c6529dbSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt);
31468c6529dbSLiam Girdwood 
31478c6529dbSLiam Girdwood /**
3148e5c21514SXiubo Li  * snd_soc_xlate_tdm_slot - generate tx/rx slot mask.
314989c67857SXiubo Li  * @slots: Number of slots in use.
315089c67857SXiubo Li  * @tx_mask: bitmask representing active TX slots.
315189c67857SXiubo Li  * @rx_mask: bitmask representing active RX slots.
315289c67857SXiubo Li  *
315389c67857SXiubo Li  * Generates the TDM tx and rx slot default masks for DAI.
315489c67857SXiubo Li  */
3155e5c21514SXiubo Li static int snd_soc_xlate_tdm_slot_mask(unsigned int slots,
315689c67857SXiubo Li 					  unsigned int *tx_mask,
315789c67857SXiubo Li 					  unsigned int *rx_mask)
315889c67857SXiubo Li {
315989c67857SXiubo Li 	if (*tx_mask || *rx_mask)
316089c67857SXiubo Li 		return 0;
316189c67857SXiubo Li 
316289c67857SXiubo Li 	if (!slots)
316389c67857SXiubo Li 		return -EINVAL;
316489c67857SXiubo Li 
316589c67857SXiubo Li 	*tx_mask = (1 << slots) - 1;
316689c67857SXiubo Li 	*rx_mask = (1 << slots) - 1;
316789c67857SXiubo Li 
316889c67857SXiubo Li 	return 0;
316989c67857SXiubo Li }
317089c67857SXiubo Li 
317189c67857SXiubo Li /**
31728c6529dbSLiam Girdwood  * snd_soc_dai_set_tdm_slot - configure DAI TDM.
31738c6529dbSLiam Girdwood  * @dai: DAI
3174a5479e38SDaniel Ribeiro  * @tx_mask: bitmask representing active TX slots.
3175a5479e38SDaniel Ribeiro  * @rx_mask: bitmask representing active RX slots.
31768c6529dbSLiam Girdwood  * @slots: Number of slots in use.
3177a5479e38SDaniel Ribeiro  * @slot_width: Width in bits for each slot.
31788c6529dbSLiam Girdwood  *
31798c6529dbSLiam Girdwood  * Configures a DAI for TDM operation. Both mask and slots are codec and DAI
31808c6529dbSLiam Girdwood  * specific.
31818c6529dbSLiam Girdwood  */
31828c6529dbSLiam Girdwood int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
3183a5479e38SDaniel Ribeiro 	unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
31848c6529dbSLiam Girdwood {
3185e5c21514SXiubo Li 	if (dai->driver && dai->driver->ops->xlate_tdm_slot_mask)
3186e5c21514SXiubo Li 		dai->driver->ops->xlate_tdm_slot_mask(slots,
318789c67857SXiubo Li 						&tx_mask, &rx_mask);
318889c67857SXiubo Li 	else
3189e5c21514SXiubo Li 		snd_soc_xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask);
319089c67857SXiubo Li 
319188bd870fSBenoit Cousson 	dai->tx_mask = tx_mask;
319288bd870fSBenoit Cousson 	dai->rx_mask = rx_mask;
319388bd870fSBenoit Cousson 
3194f0fba2adSLiam Girdwood 	if (dai->driver && dai->driver->ops->set_tdm_slot)
3195f0fba2adSLiam Girdwood 		return dai->driver->ops->set_tdm_slot(dai, tx_mask, rx_mask,
3196a5479e38SDaniel Ribeiro 				slots, slot_width);
31978c6529dbSLiam Girdwood 	else
3198b2cbb6e1SXiubo Li 		return -ENOTSUPP;
31998c6529dbSLiam Girdwood }
32008c6529dbSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot);
32018c6529dbSLiam Girdwood 
32028c6529dbSLiam Girdwood /**
3203472df3cbSBarry Song  * snd_soc_dai_set_channel_map - configure DAI audio channel map
3204472df3cbSBarry Song  * @dai: DAI
3205472df3cbSBarry Song  * @tx_num: how many TX channels
3206472df3cbSBarry Song  * @tx_slot: pointer to an array which imply the TX slot number channel
3207472df3cbSBarry Song  *           0~num-1 uses
3208472df3cbSBarry Song  * @rx_num: how many RX channels
3209472df3cbSBarry Song  * @rx_slot: pointer to an array which imply the RX slot number channel
3210472df3cbSBarry Song  *           0~num-1 uses
3211472df3cbSBarry Song  *
3212472df3cbSBarry Song  * configure the relationship between channel number and TDM slot number.
3213472df3cbSBarry Song  */
3214472df3cbSBarry Song int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai,
3215472df3cbSBarry Song 	unsigned int tx_num, unsigned int *tx_slot,
3216472df3cbSBarry Song 	unsigned int rx_num, unsigned int *rx_slot)
3217472df3cbSBarry Song {
3218f0fba2adSLiam Girdwood 	if (dai->driver && dai->driver->ops->set_channel_map)
3219f0fba2adSLiam Girdwood 		return dai->driver->ops->set_channel_map(dai, tx_num, tx_slot,
3220472df3cbSBarry Song 			rx_num, rx_slot);
3221472df3cbSBarry Song 	else
3222472df3cbSBarry Song 		return -EINVAL;
3223472df3cbSBarry Song }
3224472df3cbSBarry Song EXPORT_SYMBOL_GPL(snd_soc_dai_set_channel_map);
3225472df3cbSBarry Song 
3226472df3cbSBarry Song /**
32278c6529dbSLiam Girdwood  * snd_soc_dai_set_tristate - configure DAI system or master clock.
32288c6529dbSLiam Girdwood  * @dai: DAI
32298c6529dbSLiam Girdwood  * @tristate: tristate enable
32308c6529dbSLiam Girdwood  *
32318c6529dbSLiam Girdwood  * Tristates the DAI so that others can use it.
32328c6529dbSLiam Girdwood  */
32338c6529dbSLiam Girdwood int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate)
32348c6529dbSLiam Girdwood {
3235f0fba2adSLiam Girdwood 	if (dai->driver && dai->driver->ops->set_tristate)
3236f0fba2adSLiam Girdwood 		return dai->driver->ops->set_tristate(dai, tristate);
32378c6529dbSLiam Girdwood 	else
32388c6529dbSLiam Girdwood 		return -EINVAL;
32398c6529dbSLiam Girdwood }
32408c6529dbSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dai_set_tristate);
32418c6529dbSLiam Girdwood 
32428c6529dbSLiam Girdwood /**
32438c6529dbSLiam Girdwood  * snd_soc_dai_digital_mute - configure DAI system or master clock.
32448c6529dbSLiam Girdwood  * @dai: DAI
32458c6529dbSLiam Girdwood  * @mute: mute enable
3246da18396fSMark Brown  * @direction: stream to mute
32478c6529dbSLiam Girdwood  *
32488c6529dbSLiam Girdwood  * Mutes the DAI DAC.
32498c6529dbSLiam Girdwood  */
3250da18396fSMark Brown int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute,
3251da18396fSMark Brown 			     int direction)
32528c6529dbSLiam Girdwood {
3253da18396fSMark Brown 	if (!dai->driver)
3254da18396fSMark Brown 		return -ENOTSUPP;
3255da18396fSMark Brown 
3256da18396fSMark Brown 	if (dai->driver->ops->mute_stream)
3257da18396fSMark Brown 		return dai->driver->ops->mute_stream(dai, mute, direction);
3258da18396fSMark Brown 	else if (direction == SNDRV_PCM_STREAM_PLAYBACK &&
3259da18396fSMark Brown 		 dai->driver->ops->digital_mute)
3260f0fba2adSLiam Girdwood 		return dai->driver->ops->digital_mute(dai, mute);
32618c6529dbSLiam Girdwood 	else
326204570c62SMark Brown 		return -ENOTSUPP;
32638c6529dbSLiam Girdwood }
32648c6529dbSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute);
32658c6529dbSLiam Girdwood 
326688bd870fSBenoit Cousson static int snd_soc_init_multicodec(struct snd_soc_card *card,
326788bd870fSBenoit Cousson 				   struct snd_soc_dai_link *dai_link)
326888bd870fSBenoit Cousson {
326988bd870fSBenoit Cousson 	/* Legacy codec/codec_dai link is a single entry in multicodec */
327088bd870fSBenoit Cousson 	if (dai_link->codec_name || dai_link->codec_of_node ||
327188bd870fSBenoit Cousson 	    dai_link->codec_dai_name) {
327288bd870fSBenoit Cousson 		dai_link->num_codecs = 1;
327388bd870fSBenoit Cousson 
327488bd870fSBenoit Cousson 		dai_link->codecs = devm_kzalloc(card->dev,
327588bd870fSBenoit Cousson 				sizeof(struct snd_soc_dai_link_component),
327688bd870fSBenoit Cousson 				GFP_KERNEL);
327788bd870fSBenoit Cousson 		if (!dai_link->codecs)
327888bd870fSBenoit Cousson 			return -ENOMEM;
327988bd870fSBenoit Cousson 
328088bd870fSBenoit Cousson 		dai_link->codecs[0].name = dai_link->codec_name;
328188bd870fSBenoit Cousson 		dai_link->codecs[0].of_node = dai_link->codec_of_node;
328288bd870fSBenoit Cousson 		dai_link->codecs[0].dai_name = dai_link->codec_dai_name;
328388bd870fSBenoit Cousson 	}
328488bd870fSBenoit Cousson 
328588bd870fSBenoit Cousson 	if (!dai_link->codecs) {
328688bd870fSBenoit Cousson 		dev_err(card->dev, "ASoC: DAI link has no CODECs\n");
328788bd870fSBenoit Cousson 		return -EINVAL;
328888bd870fSBenoit Cousson 	}
328988bd870fSBenoit Cousson 
329088bd870fSBenoit Cousson 	return 0;
329188bd870fSBenoit Cousson }
329288bd870fSBenoit Cousson 
3293c5af3a2eSMark Brown /**
3294c5af3a2eSMark Brown  * snd_soc_register_card - Register a card with the ASoC core
3295c5af3a2eSMark Brown  *
3296ac11a2b3SMark Brown  * @card: Card to register
3297c5af3a2eSMark Brown  *
3298c5af3a2eSMark Brown  */
329970a7ca34SVinod Koul int snd_soc_register_card(struct snd_soc_card *card)
3300c5af3a2eSMark Brown {
330188bd870fSBenoit Cousson 	int i, j, ret;
3302f0fba2adSLiam Girdwood 
3303c5af3a2eSMark Brown 	if (!card->name || !card->dev)
3304c5af3a2eSMark Brown 		return -EINVAL;
3305c5af3a2eSMark Brown 
33065a504963SStephen Warren 	for (i = 0; i < card->num_links; i++) {
33075a504963SStephen Warren 		struct snd_soc_dai_link *link = &card->dai_link[i];
33085a504963SStephen Warren 
330988bd870fSBenoit Cousson 		ret = snd_soc_init_multicodec(card, link);
331088bd870fSBenoit Cousson 		if (ret) {
331188bd870fSBenoit Cousson 			dev_err(card->dev, "ASoC: failed to init multicodec\n");
331288bd870fSBenoit Cousson 			return ret;
331388bd870fSBenoit Cousson 		}
331488bd870fSBenoit Cousson 
331588bd870fSBenoit Cousson 		for (j = 0; j < link->num_codecs; j++) {
33165a504963SStephen Warren 			/*
33175a504963SStephen Warren 			 * Codec must be specified by 1 of name or OF node,
33185a504963SStephen Warren 			 * not both or neither.
33195a504963SStephen Warren 			 */
332088bd870fSBenoit Cousson 			if (!!link->codecs[j].name ==
332188bd870fSBenoit Cousson 			    !!link->codecs[j].of_node) {
332288bd870fSBenoit Cousson 				dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n",
332310e8aa9aSMichał Mirosław 					link->name);
33245a504963SStephen Warren 				return -EINVAL;
33255a504963SStephen Warren 			}
3326bc92657aSStephen Warren 			/* Codec DAI name must be specified */
332788bd870fSBenoit Cousson 			if (!link->codecs[j].dai_name) {
332888bd870fSBenoit Cousson 				dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n",
332910e8aa9aSMichał Mirosław 					link->name);
3330bc92657aSStephen Warren 				return -EINVAL;
3331bc92657aSStephen Warren 			}
333288bd870fSBenoit Cousson 		}
33335a504963SStephen Warren 
33345a504963SStephen Warren 		/*
33355a504963SStephen Warren 		 * Platform may be specified by either name or OF node, but
33365a504963SStephen Warren 		 * can be left unspecified, and a dummy platform will be used.
33375a504963SStephen Warren 		 */
33385a504963SStephen Warren 		if (link->platform_name && link->platform_of_node) {
333910e8aa9aSMichał Mirosław 			dev_err(card->dev,
334010e8aa9aSMichał Mirosław 				"ASoC: Both platform name/of_node are set for %s\n",
334110e8aa9aSMichał Mirosław 				link->name);
33425a504963SStephen Warren 			return -EINVAL;
33435a504963SStephen Warren 		}
33445a504963SStephen Warren 
33455a504963SStephen Warren 		/*
3346bc92657aSStephen Warren 		 * CPU device may be specified by either name or OF node, but
3347bc92657aSStephen Warren 		 * can be left unspecified, and will be matched based on DAI
3348bc92657aSStephen Warren 		 * name alone..
33495a504963SStephen Warren 		 */
3350bc92657aSStephen Warren 		if (link->cpu_name && link->cpu_of_node) {
335110e8aa9aSMichał Mirosław 			dev_err(card->dev,
335210e8aa9aSMichał Mirosław 				"ASoC: Neither/both cpu name/of_node are set for %s\n",
335310e8aa9aSMichał Mirosław 				link->name);
3354bc92657aSStephen Warren 			return -EINVAL;
3355bc92657aSStephen Warren 		}
3356bc92657aSStephen Warren 		/*
3357bc92657aSStephen Warren 		 * At least one of CPU DAI name or CPU device name/node must be
3358bc92657aSStephen Warren 		 * specified
3359bc92657aSStephen Warren 		 */
3360bc92657aSStephen Warren 		if (!link->cpu_dai_name &&
3361bc92657aSStephen Warren 		    !(link->cpu_name || link->cpu_of_node)) {
336210e8aa9aSMichał Mirosław 			dev_err(card->dev,
336310e8aa9aSMichał Mirosław 				"ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n",
336410e8aa9aSMichał Mirosław 				link->name);
33655a504963SStephen Warren 			return -EINVAL;
33665a504963SStephen Warren 		}
33675a504963SStephen Warren 	}
33685a504963SStephen Warren 
3369ed77cc12SMark Brown 	dev_set_drvdata(card->dev, card);
3370ed77cc12SMark Brown 
3371111c6419SStephen Warren 	snd_soc_initialize_card_lists(card);
3372111c6419SStephen Warren 
3373150dd2f8SVinod Koul 	soc_init_card_debugfs(card);
3374150dd2f8SVinod Koul 
3375181a6892SMark Brown 	card->rtd = devm_kzalloc(card->dev,
3376181a6892SMark Brown 				 sizeof(struct snd_soc_pcm_runtime) *
33772eea392dSJarkko Nikula 				 (card->num_links + card->num_aux_devs),
3378f0fba2adSLiam Girdwood 				 GFP_KERNEL);
3379f0fba2adSLiam Girdwood 	if (card->rtd == NULL)
3380f0fba2adSLiam Girdwood 		return -ENOMEM;
3381a7dbb603SLiam Girdwood 	card->num_rtd = 0;
33822eea392dSJarkko Nikula 	card->rtd_aux = &card->rtd[card->num_links];
3383f0fba2adSLiam Girdwood 
33845f3484acSLars-Peter Clausen 	for (i = 0; i < card->num_links; i++) {
33855f3484acSLars-Peter Clausen 		card->rtd[i].card = card;
3386f0fba2adSLiam Girdwood 		card->rtd[i].dai_link = &card->dai_link[i];
338788bd870fSBenoit Cousson 		card->rtd[i].codec_dais = devm_kzalloc(card->dev,
338888bd870fSBenoit Cousson 					sizeof(struct snd_soc_dai *) *
338988bd870fSBenoit Cousson 					(card->rtd[i].dai_link->num_codecs),
339088bd870fSBenoit Cousson 					GFP_KERNEL);
339188bd870fSBenoit Cousson 		if (card->rtd[i].codec_dais == NULL)
339288bd870fSBenoit Cousson 			return -ENOMEM;
33935f3484acSLars-Peter Clausen 	}
33945f3484acSLars-Peter Clausen 
33955f3484acSLars-Peter Clausen 	for (i = 0; i < card->num_aux_devs; i++)
33965f3484acSLars-Peter Clausen 		card->rtd_aux[i].card = card;
3397f0fba2adSLiam Girdwood 
3398db432b41SMark Brown 	INIT_LIST_HEAD(&card->dapm_dirty);
3399c5af3a2eSMark Brown 	card->instantiated = 0;
3400f0fba2adSLiam Girdwood 	mutex_init(&card->mutex);
3401a73fb2dfSLiam Girdwood 	mutex_init(&card->dapm_mutex);
3402c5af3a2eSMark Brown 
3403b19e6e7bSMark Brown 	ret = snd_soc_instantiate_card(card);
3404b19e6e7bSMark Brown 	if (ret != 0)
3405b19e6e7bSMark Brown 		soc_cleanup_card_debugfs(card);
3406c5af3a2eSMark Brown 
3407988e8cc4SNicolin Chen 	/* deactivate pins to sleep state */
3408988e8cc4SNicolin Chen 	for (i = 0; i < card->num_rtd; i++) {
340988bd870fSBenoit Cousson 		struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
341088bd870fSBenoit Cousson 		struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
341188bd870fSBenoit Cousson 		int j;
341288bd870fSBenoit Cousson 
341388bd870fSBenoit Cousson 		for (j = 0; j < rtd->num_codecs; j++) {
341488bd870fSBenoit Cousson 			struct snd_soc_dai *codec_dai = rtd->codec_dais[j];
3415988e8cc4SNicolin Chen 			if (!codec_dai->active)
3416988e8cc4SNicolin Chen 				pinctrl_pm_select_sleep_state(codec_dai->dev);
341788bd870fSBenoit Cousson 		}
341888bd870fSBenoit Cousson 
3419988e8cc4SNicolin Chen 		if (!cpu_dai->active)
3420988e8cc4SNicolin Chen 			pinctrl_pm_select_sleep_state(cpu_dai->dev);
3421988e8cc4SNicolin Chen 	}
3422988e8cc4SNicolin Chen 
3423b19e6e7bSMark Brown 	return ret;
3424c5af3a2eSMark Brown }
342570a7ca34SVinod Koul EXPORT_SYMBOL_GPL(snd_soc_register_card);
3426c5af3a2eSMark Brown 
3427c5af3a2eSMark Brown /**
3428c5af3a2eSMark Brown  * snd_soc_unregister_card - Unregister a card with the ASoC core
3429c5af3a2eSMark Brown  *
3430ac11a2b3SMark Brown  * @card: Card to unregister
3431c5af3a2eSMark Brown  *
3432c5af3a2eSMark Brown  */
343370a7ca34SVinod Koul int snd_soc_unregister_card(struct snd_soc_card *card)
3434c5af3a2eSMark Brown {
343501e0df66SLars-Peter Clausen 	if (card->instantiated) {
343601e0df66SLars-Peter Clausen 		card->instantiated = false;
34371c325f77SLars-Peter Clausen 		snd_soc_dapm_shutdown(card);
3438b0e26485SVinod Koul 		soc_cleanup_card_resources(card);
343901e0df66SLars-Peter Clausen 	}
3440f110bfc7SLiam Girdwood 	dev_dbg(card->dev, "ASoC: Unregistered card '%s'\n", card->name);
3441c5af3a2eSMark Brown 
3442c5af3a2eSMark Brown 	return 0;
3443c5af3a2eSMark Brown }
344470a7ca34SVinod Koul EXPORT_SYMBOL_GPL(snd_soc_unregister_card);
3445c5af3a2eSMark Brown 
3446f0fba2adSLiam Girdwood /*
3447f0fba2adSLiam Girdwood  * Simplify DAI link configuration by removing ".-1" from device names
3448f0fba2adSLiam Girdwood  * and sanitizing names.
3449f0fba2adSLiam Girdwood  */
34500b9a214aSDimitris Papastamos static char *fmt_single_name(struct device *dev, int *id)
3451f0fba2adSLiam Girdwood {
3452f0fba2adSLiam Girdwood 	char *found, name[NAME_SIZE];
3453f0fba2adSLiam Girdwood 	int id1, id2;
3454f0fba2adSLiam Girdwood 
3455f0fba2adSLiam Girdwood 	if (dev_name(dev) == NULL)
3456f0fba2adSLiam Girdwood 		return NULL;
3457f0fba2adSLiam Girdwood 
345858818a77SDimitris Papastamos 	strlcpy(name, dev_name(dev), NAME_SIZE);
3459f0fba2adSLiam Girdwood 
3460f0fba2adSLiam Girdwood 	/* are we a "%s.%d" name (platform and SPI components) */
3461f0fba2adSLiam Girdwood 	found = strstr(name, dev->driver->name);
3462f0fba2adSLiam Girdwood 	if (found) {
3463f0fba2adSLiam Girdwood 		/* get ID */
3464f0fba2adSLiam Girdwood 		if (sscanf(&found[strlen(dev->driver->name)], ".%d", id) == 1) {
3465f0fba2adSLiam Girdwood 
3466f0fba2adSLiam Girdwood 			/* discard ID from name if ID == -1 */
3467f0fba2adSLiam Girdwood 			if (*id == -1)
3468f0fba2adSLiam Girdwood 				found[strlen(dev->driver->name)] = '\0';
3469f0fba2adSLiam Girdwood 		}
3470f0fba2adSLiam Girdwood 
3471f0fba2adSLiam Girdwood 	} else {
3472f0fba2adSLiam Girdwood 		/* I2C component devices are named "bus-addr"  */
3473f0fba2adSLiam Girdwood 		if (sscanf(name, "%x-%x", &id1, &id2) == 2) {
3474f0fba2adSLiam Girdwood 			char tmp[NAME_SIZE];
3475f0fba2adSLiam Girdwood 
3476f0fba2adSLiam Girdwood 			/* create unique ID number from I2C addr and bus */
347705899446SJarkko Nikula 			*id = ((id1 & 0xffff) << 16) + id2;
3478f0fba2adSLiam Girdwood 
3479f0fba2adSLiam Girdwood 			/* sanitize component name for DAI link creation */
3480f0fba2adSLiam Girdwood 			snprintf(tmp, NAME_SIZE, "%s.%s", dev->driver->name, name);
348158818a77SDimitris Papastamos 			strlcpy(name, tmp, NAME_SIZE);
3482f0fba2adSLiam Girdwood 		} else
3483f0fba2adSLiam Girdwood 			*id = 0;
3484f0fba2adSLiam Girdwood 	}
3485f0fba2adSLiam Girdwood 
3486f0fba2adSLiam Girdwood 	return kstrdup(name, GFP_KERNEL);
3487f0fba2adSLiam Girdwood }
3488f0fba2adSLiam Girdwood 
3489f0fba2adSLiam Girdwood /*
3490f0fba2adSLiam Girdwood  * Simplify DAI link naming for single devices with multiple DAIs by removing
3491f0fba2adSLiam Girdwood  * any ".-1" and using the DAI name (instead of device name).
3492f0fba2adSLiam Girdwood  */
3493f0fba2adSLiam Girdwood static inline char *fmt_multiple_name(struct device *dev,
3494f0fba2adSLiam Girdwood 		struct snd_soc_dai_driver *dai_drv)
3495f0fba2adSLiam Girdwood {
3496f0fba2adSLiam Girdwood 	if (dai_drv->name == NULL) {
349710e8aa9aSMichał Mirosław 		dev_err(dev,
349810e8aa9aSMichał Mirosław 			"ASoC: error - multiple DAI %s registered with no name\n",
349910e8aa9aSMichał Mirosław 			dev_name(dev));
3500f0fba2adSLiam Girdwood 		return NULL;
3501f0fba2adSLiam Girdwood 	}
3502f0fba2adSLiam Girdwood 
3503f0fba2adSLiam Girdwood 	return kstrdup(dai_drv->name, GFP_KERNEL);
3504f0fba2adSLiam Girdwood }
3505f0fba2adSLiam Girdwood 
35069115171aSMark Brown /**
350732c9ba54SLars-Peter Clausen  * snd_soc_unregister_dai - Unregister DAIs from the ASoC core
35089115171aSMark Brown  *
350932c9ba54SLars-Peter Clausen  * @component: The component for which the DAIs should be unregistered
35109115171aSMark Brown  */
351132c9ba54SLars-Peter Clausen static void snd_soc_unregister_dais(struct snd_soc_component *component)
35129115171aSMark Brown {
35135c1d5f09SLars-Peter Clausen 	struct snd_soc_dai *dai, *_dai;
35149115171aSMark Brown 
35155c1d5f09SLars-Peter Clausen 	list_for_each_entry_safe(dai, _dai, &component->dai_list, list) {
351632c9ba54SLars-Peter Clausen 		dev_dbg(component->dev, "ASoC: Unregistered DAI '%s'\n",
351732c9ba54SLars-Peter Clausen 			dai->name);
35189115171aSMark Brown 		list_del(&dai->list);
3519f0fba2adSLiam Girdwood 		kfree(dai->name);
3520f0fba2adSLiam Girdwood 		kfree(dai);
35219115171aSMark Brown 	}
352232c9ba54SLars-Peter Clausen }
35239115171aSMark Brown 
35249115171aSMark Brown /**
352532c9ba54SLars-Peter Clausen  * snd_soc_register_dais - Register a DAI with the ASoC core
35269115171aSMark Brown  *
35276106d129SLars-Peter Clausen  * @component: The component the DAIs are registered for
35286106d129SLars-Peter Clausen  * @dai_drv: DAI driver to use for the DAIs
3529ac11a2b3SMark Brown  * @count: Number of DAIs
353032c9ba54SLars-Peter Clausen  * @legacy_dai_naming: Use the legacy naming scheme and let the DAI inherit the
353132c9ba54SLars-Peter Clausen  *                     parent's name.
35329115171aSMark Brown  */
353332c9ba54SLars-Peter Clausen static int snd_soc_register_dais(struct snd_soc_component *component,
3534bb13109dSLars-Peter Clausen 	struct snd_soc_dai_driver *dai_drv, size_t count,
3535bb13109dSLars-Peter Clausen 	bool legacy_dai_naming)
35369115171aSMark Brown {
35376106d129SLars-Peter Clausen 	struct device *dev = component->dev;
3538f0fba2adSLiam Girdwood 	struct snd_soc_dai *dai;
353932c9ba54SLars-Peter Clausen 	unsigned int i;
354032c9ba54SLars-Peter Clausen 	int ret;
3541f0fba2adSLiam Girdwood 
3542f110bfc7SLiam Girdwood 	dev_dbg(dev, "ASoC: dai register %s #%Zu\n", dev_name(dev), count);
35439115171aSMark Brown 
3544bb13109dSLars-Peter Clausen 	component->dai_drv = dai_drv;
3545bb13109dSLars-Peter Clausen 	component->num_dai = count;
3546bb13109dSLars-Peter Clausen 
35479115171aSMark Brown 	for (i = 0; i < count; i++) {
3548f0fba2adSLiam Girdwood 
3549f0fba2adSLiam Girdwood 		dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);
3550c46e0079SAxel Lin 		if (dai == NULL) {
3551c46e0079SAxel Lin 			ret = -ENOMEM;
3552c46e0079SAxel Lin 			goto err;
3553c46e0079SAxel Lin 		}
3554f0fba2adSLiam Girdwood 
355532c9ba54SLars-Peter Clausen 		/*
355632c9ba54SLars-Peter Clausen 		 * Back in the old days when we still had component-less DAIs,
355732c9ba54SLars-Peter Clausen 		 * instead of having a static name, component-less DAIs would
355832c9ba54SLars-Peter Clausen 		 * inherit the name of the parent device so it is possible to
355932c9ba54SLars-Peter Clausen 		 * register multiple instances of the DAI. We still need to keep
356032c9ba54SLars-Peter Clausen 		 * the same naming style even though those DAIs are not
356132c9ba54SLars-Peter Clausen 		 * component-less anymore.
356232c9ba54SLars-Peter Clausen 		 */
356332c9ba54SLars-Peter Clausen 		if (count == 1 && legacy_dai_naming) {
3564db2a4165SFrank Mandarino 			dai->name = fmt_single_name(dev, &dai->id);
356532c9ba54SLars-Peter Clausen 		} else {
3566f0fba2adSLiam Girdwood 			dai->name = fmt_multiple_name(dev, &dai_drv[i]);
356732c9ba54SLars-Peter Clausen 			if (dai_drv[i].id)
356832c9ba54SLars-Peter Clausen 				dai->id = dai_drv[i].id;
356932c9ba54SLars-Peter Clausen 			else
357032c9ba54SLars-Peter Clausen 				dai->id = i;
357132c9ba54SLars-Peter Clausen 		}
3572f0fba2adSLiam Girdwood 		if (dai->name == NULL) {
3573f0fba2adSLiam Girdwood 			kfree(dai);
357432c9ba54SLars-Peter Clausen 			ret = -ENOMEM;
35759115171aSMark Brown 			goto err;
35769115171aSMark Brown 		}
35779115171aSMark Brown 
35786106d129SLars-Peter Clausen 		dai->component = component;
3579f0fba2adSLiam Girdwood 		dai->dev = dev;
3580f0fba2adSLiam Girdwood 		dai->driver = &dai_drv[i];
3581f0fba2adSLiam Girdwood 		if (!dai->driver->ops)
3582f0fba2adSLiam Girdwood 			dai->driver->ops = &null_dai_ops;
3583f0fba2adSLiam Girdwood 
35841438c2f6SLars-Peter Clausen 		list_add(&dai->list, &component->dai_list);
3585054880feSMark Brown 
35869115171aSMark Brown 		dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name);
3587f0fba2adSLiam Girdwood 	}
3588f0fba2adSLiam Girdwood 
35899115171aSMark Brown 	return 0;
35909115171aSMark Brown 
35919115171aSMark Brown err:
359232c9ba54SLars-Peter Clausen 	snd_soc_unregister_dais(component);
35939115171aSMark Brown 
35949115171aSMark Brown 	return ret;
35959115171aSMark Brown }
35969115171aSMark Brown 
359714e8bdebSLars-Peter Clausen static void snd_soc_component_seq_notifier(struct snd_soc_dapm_context *dapm,
359814e8bdebSLars-Peter Clausen 	enum snd_soc_dapm_type type, int subseq)
3599d191bd8dSKuninori Morimoto {
360014e8bdebSLars-Peter Clausen 	struct snd_soc_component *component = dapm->component;
3601d191bd8dSKuninori Morimoto 
360214e8bdebSLars-Peter Clausen 	component->driver->seq_notifier(component, type, subseq);
360314e8bdebSLars-Peter Clausen }
3604d191bd8dSKuninori Morimoto 
360514e8bdebSLars-Peter Clausen static int snd_soc_component_stream_event(struct snd_soc_dapm_context *dapm,
360614e8bdebSLars-Peter Clausen 	int event)
360714e8bdebSLars-Peter Clausen {
360814e8bdebSLars-Peter Clausen 	struct snd_soc_component *component = dapm->component;
360914e8bdebSLars-Peter Clausen 
361014e8bdebSLars-Peter Clausen 	return component->driver->stream_event(component, event);
361114e8bdebSLars-Peter Clausen }
361214e8bdebSLars-Peter Clausen 
3613bb13109dSLars-Peter Clausen static int snd_soc_component_initialize(struct snd_soc_component *component,
3614bb13109dSLars-Peter Clausen 	const struct snd_soc_component_driver *driver, struct device *dev)
3615d191bd8dSKuninori Morimoto {
3616ce0fc93aSLars-Peter Clausen 	struct snd_soc_dapm_context *dapm;
3617ce0fc93aSLars-Peter Clausen 
3618bb13109dSLars-Peter Clausen 	component->name = fmt_single_name(dev, &component->id);
3619bb13109dSLars-Peter Clausen 	if (!component->name) {
3620bb13109dSLars-Peter Clausen 		dev_err(dev, "ASoC: Failed to allocate name\n");
3621d191bd8dSKuninori Morimoto 		return -ENOMEM;
3622d191bd8dSKuninori Morimoto 	}
3623d191bd8dSKuninori Morimoto 
3624bb13109dSLars-Peter Clausen 	component->dev = dev;
3625bb13109dSLars-Peter Clausen 	component->driver = driver;
362661aca564SLars-Peter Clausen 	component->probe = component->driver->probe;
362761aca564SLars-Peter Clausen 	component->remove = component->driver->remove;
3628e2c330b9SLars-Peter Clausen 
3629ce0fc93aSLars-Peter Clausen 	if (!component->dapm_ptr)
3630ce0fc93aSLars-Peter Clausen 		component->dapm_ptr = &component->dapm;
3631ce0fc93aSLars-Peter Clausen 
3632ce0fc93aSLars-Peter Clausen 	dapm = component->dapm_ptr;
3633ce0fc93aSLars-Peter Clausen 	dapm->dev = dev;
3634ce0fc93aSLars-Peter Clausen 	dapm->component = component;
3635ce0fc93aSLars-Peter Clausen 	dapm->bias_level = SND_SOC_BIAS_OFF;
36365819c2faSLars-Peter Clausen 	dapm->idle_bias_off = true;
363714e8bdebSLars-Peter Clausen 	if (driver->seq_notifier)
363814e8bdebSLars-Peter Clausen 		dapm->seq_notifier = snd_soc_component_seq_notifier;
363914e8bdebSLars-Peter Clausen 	if (driver->stream_event)
364014e8bdebSLars-Peter Clausen 		dapm->stream_event = snd_soc_component_stream_event;
3641ce0fc93aSLars-Peter Clausen 
364261aca564SLars-Peter Clausen 	component->controls = driver->controls;
364361aca564SLars-Peter Clausen 	component->num_controls = driver->num_controls;
364461aca564SLars-Peter Clausen 	component->dapm_widgets = driver->dapm_widgets;
364561aca564SLars-Peter Clausen 	component->num_dapm_widgets = driver->num_dapm_widgets;
364661aca564SLars-Peter Clausen 	component->dapm_routes = driver->dapm_routes;
364761aca564SLars-Peter Clausen 	component->num_dapm_routes = driver->num_dapm_routes;
364861aca564SLars-Peter Clausen 
3649bb13109dSLars-Peter Clausen 	INIT_LIST_HEAD(&component->dai_list);
3650bb13109dSLars-Peter Clausen 	mutex_init(&component->io_mutex);
3651bb13109dSLars-Peter Clausen 
3652bb13109dSLars-Peter Clausen 	return 0;
3653d191bd8dSKuninori Morimoto }
3654d191bd8dSKuninori Morimoto 
3655886f5692SLars-Peter Clausen static void snd_soc_component_init_regmap(struct snd_soc_component *component)
3656886f5692SLars-Peter Clausen {
3657886f5692SLars-Peter Clausen 	if (!component->regmap)
3658886f5692SLars-Peter Clausen 		component->regmap = dev_get_regmap(component->dev, NULL);
3659886f5692SLars-Peter Clausen 	if (component->regmap) {
3660886f5692SLars-Peter Clausen 		int val_bytes = regmap_get_val_bytes(component->regmap);
3661886f5692SLars-Peter Clausen 		/* Errors are legitimate for non-integer byte multiples */
3662886f5692SLars-Peter Clausen 		if (val_bytes > 0)
3663886f5692SLars-Peter Clausen 			component->val_bytes = val_bytes;
3664886f5692SLars-Peter Clausen 	}
3665886f5692SLars-Peter Clausen }
3666886f5692SLars-Peter Clausen 
3667bb13109dSLars-Peter Clausen static void snd_soc_component_add_unlocked(struct snd_soc_component *component)
3668bb13109dSLars-Peter Clausen {
3669886f5692SLars-Peter Clausen 	if (!component->write && !component->read)
3670886f5692SLars-Peter Clausen 		snd_soc_component_init_regmap(component);
3671886f5692SLars-Peter Clausen 
3672bb13109dSLars-Peter Clausen 	list_add(&component->list, &component_list);
3673d191bd8dSKuninori Morimoto }
3674d191bd8dSKuninori Morimoto 
3675bb13109dSLars-Peter Clausen static void snd_soc_component_add(struct snd_soc_component *component)
3676bb13109dSLars-Peter Clausen {
3677d191bd8dSKuninori Morimoto 	mutex_lock(&client_mutex);
3678bb13109dSLars-Peter Clausen 	snd_soc_component_add_unlocked(component);
3679d191bd8dSKuninori Morimoto 	mutex_unlock(&client_mutex);
3680bb13109dSLars-Peter Clausen }
3681d191bd8dSKuninori Morimoto 
3682bb13109dSLars-Peter Clausen static void snd_soc_component_cleanup(struct snd_soc_component *component)
3683bb13109dSLars-Peter Clausen {
3684bb13109dSLars-Peter Clausen 	snd_soc_unregister_dais(component);
3685bb13109dSLars-Peter Clausen 	kfree(component->name);
3686bb13109dSLars-Peter Clausen }
3687d191bd8dSKuninori Morimoto 
3688bb13109dSLars-Peter Clausen static void snd_soc_component_del_unlocked(struct snd_soc_component *component)
3689bb13109dSLars-Peter Clausen {
3690bb13109dSLars-Peter Clausen 	list_del(&component->list);
3691bb13109dSLars-Peter Clausen }
3692d191bd8dSKuninori Morimoto 
3693bb13109dSLars-Peter Clausen static void snd_soc_component_del(struct snd_soc_component *component)
3694bb13109dSLars-Peter Clausen {
3695bb13109dSLars-Peter Clausen 	mutex_lock(&client_mutex);
3696bb13109dSLars-Peter Clausen 	snd_soc_component_del_unlocked(component);
3697bb13109dSLars-Peter Clausen 	mutex_unlock(&client_mutex);
3698d191bd8dSKuninori Morimoto }
3699d191bd8dSKuninori Morimoto 
3700d191bd8dSKuninori Morimoto int snd_soc_register_component(struct device *dev,
3701d191bd8dSKuninori Morimoto 			       const struct snd_soc_component_driver *cmpnt_drv,
3702d191bd8dSKuninori Morimoto 			       struct snd_soc_dai_driver *dai_drv,
3703d191bd8dSKuninori Morimoto 			       int num_dai)
3704d191bd8dSKuninori Morimoto {
3705d191bd8dSKuninori Morimoto 	struct snd_soc_component *cmpnt;
3706bb13109dSLars-Peter Clausen 	int ret;
3707d191bd8dSKuninori Morimoto 
3708bb13109dSLars-Peter Clausen 	cmpnt = kzalloc(sizeof(*cmpnt), GFP_KERNEL);
3709d191bd8dSKuninori Morimoto 	if (!cmpnt) {
3710d191bd8dSKuninori Morimoto 		dev_err(dev, "ASoC: Failed to allocate memory\n");
3711d191bd8dSKuninori Morimoto 		return -ENOMEM;
3712d191bd8dSKuninori Morimoto 	}
3713d191bd8dSKuninori Morimoto 
3714bb13109dSLars-Peter Clausen 	ret = snd_soc_component_initialize(cmpnt, cmpnt_drv, dev);
3715bb13109dSLars-Peter Clausen 	if (ret)
3716bb13109dSLars-Peter Clausen 		goto err_free;
3717bb13109dSLars-Peter Clausen 
37183d59400fSLars-Peter Clausen 	cmpnt->ignore_pmdown_time = true;
371998e639fbSLars-Peter Clausen 	cmpnt->registered_as_component = true;
37203d59400fSLars-Peter Clausen 
3721bb13109dSLars-Peter Clausen 	ret = snd_soc_register_dais(cmpnt, dai_drv, num_dai, true);
3722bb13109dSLars-Peter Clausen 	if (ret < 0) {
3723bb13109dSLars-Peter Clausen 		dev_err(dev, "ASoC: Failed to regster DAIs: %d\n", ret);
3724bb13109dSLars-Peter Clausen 		goto err_cleanup;
3725bb13109dSLars-Peter Clausen 	}
3726bb13109dSLars-Peter Clausen 
3727bb13109dSLars-Peter Clausen 	snd_soc_component_add(cmpnt);
3728bb13109dSLars-Peter Clausen 
3729bb13109dSLars-Peter Clausen 	return 0;
3730bb13109dSLars-Peter Clausen 
3731bb13109dSLars-Peter Clausen err_cleanup:
3732bb13109dSLars-Peter Clausen 	snd_soc_component_cleanup(cmpnt);
3733bb13109dSLars-Peter Clausen err_free:
3734bb13109dSLars-Peter Clausen 	kfree(cmpnt);
3735bb13109dSLars-Peter Clausen 	return ret;
3736d191bd8dSKuninori Morimoto }
3737d191bd8dSKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_register_component);
3738d191bd8dSKuninori Morimoto 
3739d191bd8dSKuninori Morimoto /**
3740d191bd8dSKuninori Morimoto  * snd_soc_unregister_component - Unregister a component from the ASoC core
3741d191bd8dSKuninori Morimoto  *
3742d191bd8dSKuninori Morimoto  */
3743d191bd8dSKuninori Morimoto void snd_soc_unregister_component(struct device *dev)
3744d191bd8dSKuninori Morimoto {
3745d191bd8dSKuninori Morimoto 	struct snd_soc_component *cmpnt;
3746d191bd8dSKuninori Morimoto 
3747d191bd8dSKuninori Morimoto 	list_for_each_entry(cmpnt, &component_list, list) {
374898e639fbSLars-Peter Clausen 		if (dev == cmpnt->dev && cmpnt->registered_as_component)
3749d191bd8dSKuninori Morimoto 			goto found;
3750d191bd8dSKuninori Morimoto 	}
3751d191bd8dSKuninori Morimoto 	return;
3752d191bd8dSKuninori Morimoto 
3753d191bd8dSKuninori Morimoto found:
3754bb13109dSLars-Peter Clausen 	snd_soc_component_del(cmpnt);
3755bb13109dSLars-Peter Clausen 	snd_soc_component_cleanup(cmpnt);
3756bb13109dSLars-Peter Clausen 	kfree(cmpnt);
3757d191bd8dSKuninori Morimoto }
3758d191bd8dSKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_unregister_component);
3759d191bd8dSKuninori Morimoto 
3760f1d45cc3SLars-Peter Clausen static int snd_soc_platform_drv_probe(struct snd_soc_component *component)
3761e2c330b9SLars-Peter Clausen {
3762e2c330b9SLars-Peter Clausen 	struct snd_soc_platform *platform = snd_soc_component_to_platform(component);
3763e2c330b9SLars-Peter Clausen 
3764f1d45cc3SLars-Peter Clausen 	return platform->driver->probe(platform);
3765e2c330b9SLars-Peter Clausen }
3766e2c330b9SLars-Peter Clausen 
3767f1d45cc3SLars-Peter Clausen static void snd_soc_platform_drv_remove(struct snd_soc_component *component)
3768e2c330b9SLars-Peter Clausen {
3769e2c330b9SLars-Peter Clausen 	struct snd_soc_platform *platform = snd_soc_component_to_platform(component);
3770e2c330b9SLars-Peter Clausen 
3771f1d45cc3SLars-Peter Clausen 	platform->driver->remove(platform);
3772e2c330b9SLars-Peter Clausen }
3773e2c330b9SLars-Peter Clausen 
3774d191bd8dSKuninori Morimoto /**
377571a45cdaSLars-Peter Clausen  * snd_soc_add_platform - Add a platform to the ASoC core
377671a45cdaSLars-Peter Clausen  * @dev: The parent device for the platform
377771a45cdaSLars-Peter Clausen  * @platform: The platform to add
377871a45cdaSLars-Peter Clausen  * @platform_driver: The driver for the platform
377912a48a8cSMark Brown  */
378071a45cdaSLars-Peter Clausen int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform,
3781d79e57dbSLars-Peter Clausen 		const struct snd_soc_platform_driver *platform_drv)
378212a48a8cSMark Brown {
3783b37f1d12SLars-Peter Clausen 	int ret;
3784b37f1d12SLars-Peter Clausen 
3785bb13109dSLars-Peter Clausen 	ret = snd_soc_component_initialize(&platform->component,
3786bb13109dSLars-Peter Clausen 			&platform_drv->component_driver, dev);
3787bb13109dSLars-Peter Clausen 	if (ret)
3788bb13109dSLars-Peter Clausen 		return ret;
3789f0fba2adSLiam Girdwood 
3790f0fba2adSLiam Girdwood 	platform->dev = dev;
3791f0fba2adSLiam Girdwood 	platform->driver = platform_drv;
3792f1d45cc3SLars-Peter Clausen 
3793f1d45cc3SLars-Peter Clausen 	if (platform_drv->probe)
3794f1d45cc3SLars-Peter Clausen 		platform->component.probe = snd_soc_platform_drv_probe;
3795f1d45cc3SLars-Peter Clausen 	if (platform_drv->remove)
3796f1d45cc3SLars-Peter Clausen 		platform->component.remove = snd_soc_platform_drv_remove;
379712a48a8cSMark Brown 
379881c7cfd1SLars-Peter Clausen #ifdef CONFIG_DEBUG_FS
379981c7cfd1SLars-Peter Clausen 	platform->component.debugfs_prefix = "platform";
380081c7cfd1SLars-Peter Clausen #endif
380112a48a8cSMark Brown 
380212a48a8cSMark Brown 	mutex_lock(&client_mutex);
3803bb13109dSLars-Peter Clausen 	snd_soc_component_add_unlocked(&platform->component);
380412a48a8cSMark Brown 	list_add(&platform->list, &platform_list);
380512a48a8cSMark Brown 	mutex_unlock(&client_mutex);
380612a48a8cSMark Brown 
3807f4333203SLars-Peter Clausen 	dev_dbg(dev, "ASoC: Registered platform '%s'\n",
3808f4333203SLars-Peter Clausen 		platform->component.name);
380912a48a8cSMark Brown 
381012a48a8cSMark Brown 	return 0;
381112a48a8cSMark Brown }
381271a45cdaSLars-Peter Clausen EXPORT_SYMBOL_GPL(snd_soc_add_platform);
381371a45cdaSLars-Peter Clausen 
381471a45cdaSLars-Peter Clausen /**
381571a45cdaSLars-Peter Clausen  * snd_soc_register_platform - Register a platform with the ASoC core
381671a45cdaSLars-Peter Clausen  *
381771a45cdaSLars-Peter Clausen  * @platform: platform to register
381871a45cdaSLars-Peter Clausen  */
381971a45cdaSLars-Peter Clausen int snd_soc_register_platform(struct device *dev,
382071a45cdaSLars-Peter Clausen 		const struct snd_soc_platform_driver *platform_drv)
382171a45cdaSLars-Peter Clausen {
382271a45cdaSLars-Peter Clausen 	struct snd_soc_platform *platform;
382371a45cdaSLars-Peter Clausen 	int ret;
382471a45cdaSLars-Peter Clausen 
382571a45cdaSLars-Peter Clausen 	dev_dbg(dev, "ASoC: platform register %s\n", dev_name(dev));
382671a45cdaSLars-Peter Clausen 
382771a45cdaSLars-Peter Clausen 	platform = kzalloc(sizeof(struct snd_soc_platform), GFP_KERNEL);
382871a45cdaSLars-Peter Clausen 	if (platform == NULL)
382971a45cdaSLars-Peter Clausen 		return -ENOMEM;
383071a45cdaSLars-Peter Clausen 
383171a45cdaSLars-Peter Clausen 	ret = snd_soc_add_platform(dev, platform, platform_drv);
383271a45cdaSLars-Peter Clausen 	if (ret)
383371a45cdaSLars-Peter Clausen 		kfree(platform);
383471a45cdaSLars-Peter Clausen 
383571a45cdaSLars-Peter Clausen 	return ret;
383671a45cdaSLars-Peter Clausen }
383712a48a8cSMark Brown EXPORT_SYMBOL_GPL(snd_soc_register_platform);
383812a48a8cSMark Brown 
383912a48a8cSMark Brown /**
384071a45cdaSLars-Peter Clausen  * snd_soc_remove_platform - Remove a platform from the ASoC core
384171a45cdaSLars-Peter Clausen  * @platform: the platform to remove
384271a45cdaSLars-Peter Clausen  */
384371a45cdaSLars-Peter Clausen void snd_soc_remove_platform(struct snd_soc_platform *platform)
384471a45cdaSLars-Peter Clausen {
3845b37f1d12SLars-Peter Clausen 
384671a45cdaSLars-Peter Clausen 	mutex_lock(&client_mutex);
384771a45cdaSLars-Peter Clausen 	list_del(&platform->list);
3848bb13109dSLars-Peter Clausen 	snd_soc_component_del_unlocked(&platform->component);
384971a45cdaSLars-Peter Clausen 	mutex_unlock(&client_mutex);
385071a45cdaSLars-Peter Clausen 
385171a45cdaSLars-Peter Clausen 	dev_dbg(platform->dev, "ASoC: Unregistered platform '%s'\n",
3852f4333203SLars-Peter Clausen 		platform->component.name);
3853decc27b0SDaniel Mack 
3854decc27b0SDaniel Mack 	snd_soc_component_cleanup(&platform->component);
385571a45cdaSLars-Peter Clausen }
385671a45cdaSLars-Peter Clausen EXPORT_SYMBOL_GPL(snd_soc_remove_platform);
385771a45cdaSLars-Peter Clausen 
385871a45cdaSLars-Peter Clausen struct snd_soc_platform *snd_soc_lookup_platform(struct device *dev)
385971a45cdaSLars-Peter Clausen {
386071a45cdaSLars-Peter Clausen 	struct snd_soc_platform *platform;
386171a45cdaSLars-Peter Clausen 
386271a45cdaSLars-Peter Clausen 	list_for_each_entry(platform, &platform_list, list) {
386371a45cdaSLars-Peter Clausen 		if (dev == platform->dev)
386471a45cdaSLars-Peter Clausen 			return platform;
386571a45cdaSLars-Peter Clausen 	}
386671a45cdaSLars-Peter Clausen 
386771a45cdaSLars-Peter Clausen 	return NULL;
386871a45cdaSLars-Peter Clausen }
386971a45cdaSLars-Peter Clausen EXPORT_SYMBOL_GPL(snd_soc_lookup_platform);
387071a45cdaSLars-Peter Clausen 
387171a45cdaSLars-Peter Clausen /**
387212a48a8cSMark Brown  * snd_soc_unregister_platform - Unregister a platform from the ASoC core
387312a48a8cSMark Brown  *
3874ac11a2b3SMark Brown  * @platform: platform to unregister
387512a48a8cSMark Brown  */
3876f0fba2adSLiam Girdwood void snd_soc_unregister_platform(struct device *dev)
387712a48a8cSMark Brown {
3878f0fba2adSLiam Girdwood 	struct snd_soc_platform *platform;
3879f0fba2adSLiam Girdwood 
388071a45cdaSLars-Peter Clausen 	platform = snd_soc_lookup_platform(dev);
388171a45cdaSLars-Peter Clausen 	if (!platform)
3882f0fba2adSLiam Girdwood 		return;
3883f0fba2adSLiam Girdwood 
388471a45cdaSLars-Peter Clausen 	snd_soc_remove_platform(platform);
3885f0fba2adSLiam Girdwood 	kfree(platform);
388612a48a8cSMark Brown }
388712a48a8cSMark Brown EXPORT_SYMBOL_GPL(snd_soc_unregister_platform);
388812a48a8cSMark Brown 
3889151ab22cSMark Brown static u64 codec_format_map[] = {
3890151ab22cSMark Brown 	SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE,
3891151ab22cSMark Brown 	SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE,
3892151ab22cSMark Brown 	SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE,
3893151ab22cSMark Brown 	SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_U24_BE,
3894151ab22cSMark Brown 	SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE,
3895151ab22cSMark Brown 	SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_U32_BE,
3896151ab22cSMark Brown 	SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3BE,
3897151ab22cSMark Brown 	SNDRV_PCM_FMTBIT_U24_3LE | SNDRV_PCM_FMTBIT_U24_3BE,
3898151ab22cSMark Brown 	SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE,
3899151ab22cSMark Brown 	SNDRV_PCM_FMTBIT_U20_3LE | SNDRV_PCM_FMTBIT_U20_3BE,
3900151ab22cSMark Brown 	SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE,
3901151ab22cSMark Brown 	SNDRV_PCM_FMTBIT_U18_3LE | SNDRV_PCM_FMTBIT_U18_3BE,
3902151ab22cSMark Brown 	SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE,
3903151ab22cSMark Brown 	SNDRV_PCM_FMTBIT_FLOAT64_LE | SNDRV_PCM_FMTBIT_FLOAT64_BE,
3904151ab22cSMark Brown 	SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE
3905151ab22cSMark Brown 	| SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE,
3906151ab22cSMark Brown };
3907151ab22cSMark Brown 
3908151ab22cSMark Brown /* Fix up the DAI formats for endianness: codecs don't actually see
3909151ab22cSMark Brown  * the endianness of the data but we're using the CPU format
3910151ab22cSMark Brown  * definitions which do need to include endianness so we ensure that
3911151ab22cSMark Brown  * codec DAIs always have both big and little endian variants set.
3912151ab22cSMark Brown  */
3913151ab22cSMark Brown static void fixup_codec_formats(struct snd_soc_pcm_stream *stream)
3914151ab22cSMark Brown {
3915151ab22cSMark Brown 	int i;
3916151ab22cSMark Brown 
3917151ab22cSMark Brown 	for (i = 0; i < ARRAY_SIZE(codec_format_map); i++)
3918151ab22cSMark Brown 		if (stream->formats & codec_format_map[i])
3919151ab22cSMark Brown 			stream->formats |= codec_format_map[i];
3920151ab22cSMark Brown }
3921151ab22cSMark Brown 
3922f1d45cc3SLars-Peter Clausen static int snd_soc_codec_drv_probe(struct snd_soc_component *component)
3923f1d45cc3SLars-Peter Clausen {
3924f1d45cc3SLars-Peter Clausen 	struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
3925f1d45cc3SLars-Peter Clausen 
3926f1d45cc3SLars-Peter Clausen 	return codec->driver->probe(codec);
3927f1d45cc3SLars-Peter Clausen }
3928f1d45cc3SLars-Peter Clausen 
3929f1d45cc3SLars-Peter Clausen static void snd_soc_codec_drv_remove(struct snd_soc_component *component)
3930f1d45cc3SLars-Peter Clausen {
3931f1d45cc3SLars-Peter Clausen 	struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
3932f1d45cc3SLars-Peter Clausen 
3933f1d45cc3SLars-Peter Clausen 	codec->driver->remove(codec);
3934f1d45cc3SLars-Peter Clausen }
3935f1d45cc3SLars-Peter Clausen 
3936e2c330b9SLars-Peter Clausen static int snd_soc_codec_drv_write(struct snd_soc_component *component,
3937e2c330b9SLars-Peter Clausen 	unsigned int reg, unsigned int val)
3938e2c330b9SLars-Peter Clausen {
3939e2c330b9SLars-Peter Clausen 	struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
3940e2c330b9SLars-Peter Clausen 
3941e2c330b9SLars-Peter Clausen 	return codec->driver->write(codec, reg, val);
3942e2c330b9SLars-Peter Clausen }
3943e2c330b9SLars-Peter Clausen 
3944e2c330b9SLars-Peter Clausen static int snd_soc_codec_drv_read(struct snd_soc_component *component,
3945e2c330b9SLars-Peter Clausen 	unsigned int reg, unsigned int *val)
3946e2c330b9SLars-Peter Clausen {
3947e2c330b9SLars-Peter Clausen 	struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
3948e2c330b9SLars-Peter Clausen 
3949e2c330b9SLars-Peter Clausen 	*val = codec->driver->read(codec, reg);
3950e2c330b9SLars-Peter Clausen 
3951e2c330b9SLars-Peter Clausen 	return 0;
3952e2c330b9SLars-Peter Clausen }
3953e2c330b9SLars-Peter Clausen 
395468f831c2SLars-Peter Clausen static int snd_soc_codec_set_bias_level(struct snd_soc_dapm_context *dapm,
395568f831c2SLars-Peter Clausen 	enum snd_soc_bias_level level)
395668f831c2SLars-Peter Clausen {
395768f831c2SLars-Peter Clausen 	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
395868f831c2SLars-Peter Clausen 
395968f831c2SLars-Peter Clausen 	return codec->driver->set_bias_level(codec, level);
396068f831c2SLars-Peter Clausen }
396168f831c2SLars-Peter Clausen 
39620d0cf00aSMark Brown /**
39630d0cf00aSMark Brown  * snd_soc_register_codec - Register a codec with the ASoC core
39640d0cf00aSMark Brown  *
3965ac11a2b3SMark Brown  * @codec: codec to register
39660d0cf00aSMark Brown  */
3967f0fba2adSLiam Girdwood int snd_soc_register_codec(struct device *dev,
3968001ae4c0SMark Brown 			   const struct snd_soc_codec_driver *codec_drv,
3969001ae4c0SMark Brown 			   struct snd_soc_dai_driver *dai_drv,
3970001ae4c0SMark Brown 			   int num_dai)
39710d0cf00aSMark Brown {
3972f0fba2adSLiam Girdwood 	struct snd_soc_codec *codec;
3973bb13109dSLars-Peter Clausen 	struct snd_soc_dai *dai;
3974f0fba2adSLiam Girdwood 	int ret, i;
3975151ab22cSMark Brown 
3976f0fba2adSLiam Girdwood 	dev_dbg(dev, "codec register %s\n", dev_name(dev));
39770d0cf00aSMark Brown 
3978f0fba2adSLiam Girdwood 	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
3979f0fba2adSLiam Girdwood 	if (codec == NULL)
3980f0fba2adSLiam Girdwood 		return -ENOMEM;
39810d0cf00aSMark Brown 
3982ce0fc93aSLars-Peter Clausen 	codec->component.dapm_ptr = &codec->dapm;
3983f1d45cc3SLars-Peter Clausen 	codec->component.codec = codec;
3984ce0fc93aSLars-Peter Clausen 
3985bb13109dSLars-Peter Clausen 	ret = snd_soc_component_initialize(&codec->component,
3986bb13109dSLars-Peter Clausen 			&codec_drv->component_driver, dev);
3987bb13109dSLars-Peter Clausen 	if (ret)
3988bb13109dSLars-Peter Clausen 		goto err_free;
3989151ab22cSMark Brown 
3990f1d45cc3SLars-Peter Clausen 	if (codec_drv->controls) {
3991f1d45cc3SLars-Peter Clausen 		codec->component.controls = codec_drv->controls;
3992f1d45cc3SLars-Peter Clausen 		codec->component.num_controls = codec_drv->num_controls;
3993f1d45cc3SLars-Peter Clausen 	}
3994f1d45cc3SLars-Peter Clausen 	if (codec_drv->dapm_widgets) {
3995f1d45cc3SLars-Peter Clausen 		codec->component.dapm_widgets = codec_drv->dapm_widgets;
3996f1d45cc3SLars-Peter Clausen 		codec->component.num_dapm_widgets = codec_drv->num_dapm_widgets;
3997f1d45cc3SLars-Peter Clausen 	}
3998f1d45cc3SLars-Peter Clausen 	if (codec_drv->dapm_routes) {
3999f1d45cc3SLars-Peter Clausen 		codec->component.dapm_routes = codec_drv->dapm_routes;
4000f1d45cc3SLars-Peter Clausen 		codec->component.num_dapm_routes = codec_drv->num_dapm_routes;
4001f1d45cc3SLars-Peter Clausen 	}
4002f1d45cc3SLars-Peter Clausen 
4003f1d45cc3SLars-Peter Clausen 	if (codec_drv->probe)
4004f1d45cc3SLars-Peter Clausen 		codec->component.probe = snd_soc_codec_drv_probe;
4005f1d45cc3SLars-Peter Clausen 	if (codec_drv->remove)
4006f1d45cc3SLars-Peter Clausen 		codec->component.remove = snd_soc_codec_drv_remove;
4007e2c330b9SLars-Peter Clausen 	if (codec_drv->write)
4008e2c330b9SLars-Peter Clausen 		codec->component.write = snd_soc_codec_drv_write;
4009e2c330b9SLars-Peter Clausen 	if (codec_drv->read)
4010e2c330b9SLars-Peter Clausen 		codec->component.read = snd_soc_codec_drv_read;
40113d59400fSLars-Peter Clausen 	codec->component.ignore_pmdown_time = codec_drv->ignore_pmdown_time;
40125819c2faSLars-Peter Clausen 	codec->dapm.idle_bias_off = codec_drv->idle_bias_off;
401386dbf2acSLars-Peter Clausen 	codec->dapm.suspend_bias_off = codec_drv->suspend_bias_off;
401414e8bdebSLars-Peter Clausen 	if (codec_drv->seq_notifier)
4015474b62d6SMark Brown 		codec->dapm.seq_notifier = codec_drv->seq_notifier;
401668f831c2SLars-Peter Clausen 	if (codec_drv->set_bias_level)
401768f831c2SLars-Peter Clausen 		codec->dapm.set_bias_level = snd_soc_codec_set_bias_level;
4018f0fba2adSLiam Girdwood 	codec->dev = dev;
4019f0fba2adSLiam Girdwood 	codec->driver = codec_drv;
4020e2c330b9SLars-Peter Clausen 	codec->component.val_bytes = codec_drv->reg_word_size;
4021f0fba2adSLiam Girdwood 	mutex_init(&codec->mutex);
4022f0fba2adSLiam Girdwood 
402381c7cfd1SLars-Peter Clausen #ifdef CONFIG_DEBUG_FS
402481c7cfd1SLars-Peter Clausen 	codec->component.init_debugfs = soc_init_codec_debugfs;
402581c7cfd1SLars-Peter Clausen 	codec->component.debugfs_prefix = "codec";
402681c7cfd1SLars-Peter Clausen #endif
4027a39f75f7SXiubo Li 
4028a39f75f7SXiubo Li 	if (codec_drv->get_regmap)
4029886f5692SLars-Peter Clausen 		codec->component.regmap = codec_drv->get_regmap(dev);
4030a39f75f7SXiubo Li 
4031f0fba2adSLiam Girdwood 	for (i = 0; i < num_dai; i++) {
4032f0fba2adSLiam Girdwood 		fixup_codec_formats(&dai_drv[i].playback);
4033f0fba2adSLiam Girdwood 		fixup_codec_formats(&dai_drv[i].capture);
4034f0fba2adSLiam Girdwood 	}
4035f0fba2adSLiam Girdwood 
4036bb13109dSLars-Peter Clausen 	ret = snd_soc_register_dais(&codec->component, dai_drv, num_dai, false);
4037bb13109dSLars-Peter Clausen 	if (ret < 0) {
4038bb13109dSLars-Peter Clausen 		dev_err(dev, "ASoC: Failed to regster DAIs: %d\n", ret);
4039bb13109dSLars-Peter Clausen 		goto err_cleanup;
4040bb13109dSLars-Peter Clausen 	}
4041bb13109dSLars-Peter Clausen 
4042bb13109dSLars-Peter Clausen 	list_for_each_entry(dai, &codec->component.dai_list, list)
4043bb13109dSLars-Peter Clausen 		dai->codec = codec;
4044bb13109dSLars-Peter Clausen 
4045054880feSMark Brown 	mutex_lock(&client_mutex);
4046bb13109dSLars-Peter Clausen 	snd_soc_component_add_unlocked(&codec->component);
4047054880feSMark Brown 	list_add(&codec->list, &codec_list);
4048054880feSMark Brown 	mutex_unlock(&client_mutex);
4049054880feSMark Brown 
4050f4333203SLars-Peter Clausen 	dev_dbg(codec->dev, "ASoC: Registered codec '%s'\n",
4051f4333203SLars-Peter Clausen 		codec->component.name);
40520d0cf00aSMark Brown 	return 0;
4053f0fba2adSLiam Girdwood 
4054bb13109dSLars-Peter Clausen err_cleanup:
4055bb13109dSLars-Peter Clausen 	snd_soc_component_cleanup(&codec->component);
4056bb13109dSLars-Peter Clausen err_free:
4057f0fba2adSLiam Girdwood 	kfree(codec);
4058f0fba2adSLiam Girdwood 	return ret;
40590d0cf00aSMark Brown }
40600d0cf00aSMark Brown EXPORT_SYMBOL_GPL(snd_soc_register_codec);
40610d0cf00aSMark Brown 
40620d0cf00aSMark Brown /**
40630d0cf00aSMark Brown  * snd_soc_unregister_codec - Unregister a codec from the ASoC core
40640d0cf00aSMark Brown  *
4065ac11a2b3SMark Brown  * @codec: codec to unregister
40660d0cf00aSMark Brown  */
4067f0fba2adSLiam Girdwood void snd_soc_unregister_codec(struct device *dev)
40680d0cf00aSMark Brown {
4069f0fba2adSLiam Girdwood 	struct snd_soc_codec *codec;
4070f0fba2adSLiam Girdwood 
4071f0fba2adSLiam Girdwood 	list_for_each_entry(codec, &codec_list, list) {
4072f0fba2adSLiam Girdwood 		if (dev == codec->dev)
4073f0fba2adSLiam Girdwood 			goto found;
4074f0fba2adSLiam Girdwood 	}
4075f0fba2adSLiam Girdwood 	return;
4076f0fba2adSLiam Girdwood 
4077f0fba2adSLiam Girdwood found:
4078f0fba2adSLiam Girdwood 
40790d0cf00aSMark Brown 	mutex_lock(&client_mutex);
40800d0cf00aSMark Brown 	list_del(&codec->list);
4081bb13109dSLars-Peter Clausen 	snd_soc_component_del_unlocked(&codec->component);
40820d0cf00aSMark Brown 	mutex_unlock(&client_mutex);
40830d0cf00aSMark Brown 
4084f4333203SLars-Peter Clausen 	dev_dbg(codec->dev, "ASoC: Unregistered codec '%s'\n",
4085f4333203SLars-Peter Clausen 			codec->component.name);
4086f0fba2adSLiam Girdwood 
4087bb13109dSLars-Peter Clausen 	snd_soc_component_cleanup(&codec->component);
40887a30a3dbSDimitris Papastamos 	snd_soc_cache_exit(codec);
4089f0fba2adSLiam Girdwood 	kfree(codec);
40900d0cf00aSMark Brown }
40910d0cf00aSMark Brown EXPORT_SYMBOL_GPL(snd_soc_unregister_codec);
40920d0cf00aSMark Brown 
4093bec4fa05SStephen Warren /* Retrieve a card's name from device tree */
4094bec4fa05SStephen Warren int snd_soc_of_parse_card_name(struct snd_soc_card *card,
4095bec4fa05SStephen Warren 			       const char *propname)
4096bec4fa05SStephen Warren {
40977e07e7c0STushar Behera 	struct device_node *np;
4098bec4fa05SStephen Warren 	int ret;
4099bec4fa05SStephen Warren 
41007e07e7c0STushar Behera 	if (!card->dev) {
41017e07e7c0STushar Behera 		pr_err("card->dev is not set before calling %s\n", __func__);
41027e07e7c0STushar Behera 		return -EINVAL;
41037e07e7c0STushar Behera 	}
41047e07e7c0STushar Behera 
41057e07e7c0STushar Behera 	np = card->dev->of_node;
41067e07e7c0STushar Behera 
4107bec4fa05SStephen Warren 	ret = of_property_read_string_index(np, propname, 0, &card->name);
4108bec4fa05SStephen Warren 	/*
4109bec4fa05SStephen Warren 	 * EINVAL means the property does not exist. This is fine providing
4110bec4fa05SStephen Warren 	 * card->name was previously set, which is checked later in
4111bec4fa05SStephen Warren 	 * snd_soc_register_card.
4112bec4fa05SStephen Warren 	 */
4113bec4fa05SStephen Warren 	if (ret < 0 && ret != -EINVAL) {
4114bec4fa05SStephen Warren 		dev_err(card->dev,
4115f110bfc7SLiam Girdwood 			"ASoC: Property '%s' could not be read: %d\n",
4116bec4fa05SStephen Warren 			propname, ret);
4117bec4fa05SStephen Warren 		return ret;
4118bec4fa05SStephen Warren 	}
4119bec4fa05SStephen Warren 
4120bec4fa05SStephen Warren 	return 0;
4121bec4fa05SStephen Warren }
4122bec4fa05SStephen Warren EXPORT_SYMBOL_GPL(snd_soc_of_parse_card_name);
4123bec4fa05SStephen Warren 
41249a6d4860SXiubo Li static const struct snd_soc_dapm_widget simple_widgets[] = {
41259a6d4860SXiubo Li 	SND_SOC_DAPM_MIC("Microphone", NULL),
41269a6d4860SXiubo Li 	SND_SOC_DAPM_LINE("Line", NULL),
41279a6d4860SXiubo Li 	SND_SOC_DAPM_HP("Headphone", NULL),
41289a6d4860SXiubo Li 	SND_SOC_DAPM_SPK("Speaker", NULL),
41299a6d4860SXiubo Li };
41309a6d4860SXiubo Li 
41319a6d4860SXiubo Li int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card,
41329a6d4860SXiubo Li 					  const char *propname)
41339a6d4860SXiubo Li {
41349a6d4860SXiubo Li 	struct device_node *np = card->dev->of_node;
41359a6d4860SXiubo Li 	struct snd_soc_dapm_widget *widgets;
41369a6d4860SXiubo Li 	const char *template, *wname;
41379a6d4860SXiubo Li 	int i, j, num_widgets, ret;
41389a6d4860SXiubo Li 
41399a6d4860SXiubo Li 	num_widgets = of_property_count_strings(np, propname);
41409a6d4860SXiubo Li 	if (num_widgets < 0) {
41419a6d4860SXiubo Li 		dev_err(card->dev,
41429a6d4860SXiubo Li 			"ASoC: Property '%s' does not exist\n",	propname);
41439a6d4860SXiubo Li 		return -EINVAL;
41449a6d4860SXiubo Li 	}
41459a6d4860SXiubo Li 	if (num_widgets & 1) {
41469a6d4860SXiubo Li 		dev_err(card->dev,
41479a6d4860SXiubo Li 			"ASoC: Property '%s' length is not even\n", propname);
41489a6d4860SXiubo Li 		return -EINVAL;
41499a6d4860SXiubo Li 	}
41509a6d4860SXiubo Li 
41519a6d4860SXiubo Li 	num_widgets /= 2;
41529a6d4860SXiubo Li 	if (!num_widgets) {
41539a6d4860SXiubo Li 		dev_err(card->dev, "ASoC: Property '%s's length is zero\n",
41549a6d4860SXiubo Li 			propname);
41559a6d4860SXiubo Li 		return -EINVAL;
41569a6d4860SXiubo Li 	}
41579a6d4860SXiubo Li 
41589a6d4860SXiubo Li 	widgets = devm_kcalloc(card->dev, num_widgets, sizeof(*widgets),
41599a6d4860SXiubo Li 			       GFP_KERNEL);
41609a6d4860SXiubo Li 	if (!widgets) {
41619a6d4860SXiubo Li 		dev_err(card->dev,
41629a6d4860SXiubo Li 			"ASoC: Could not allocate memory for widgets\n");
41639a6d4860SXiubo Li 		return -ENOMEM;
41649a6d4860SXiubo Li 	}
41659a6d4860SXiubo Li 
41669a6d4860SXiubo Li 	for (i = 0; i < num_widgets; i++) {
41679a6d4860SXiubo Li 		ret = of_property_read_string_index(np, propname,
41689a6d4860SXiubo Li 			2 * i, &template);
41699a6d4860SXiubo Li 		if (ret) {
41709a6d4860SXiubo Li 			dev_err(card->dev,
41719a6d4860SXiubo Li 				"ASoC: Property '%s' index %d read error:%d\n",
41729a6d4860SXiubo Li 				propname, 2 * i, ret);
41739a6d4860SXiubo Li 			return -EINVAL;
41749a6d4860SXiubo Li 		}
41759a6d4860SXiubo Li 
41769a6d4860SXiubo Li 		for (j = 0; j < ARRAY_SIZE(simple_widgets); j++) {
41779a6d4860SXiubo Li 			if (!strncmp(template, simple_widgets[j].name,
41789a6d4860SXiubo Li 				     strlen(simple_widgets[j].name))) {
41799a6d4860SXiubo Li 				widgets[i] = simple_widgets[j];
41809a6d4860SXiubo Li 				break;
41819a6d4860SXiubo Li 			}
41829a6d4860SXiubo Li 		}
41839a6d4860SXiubo Li 
41849a6d4860SXiubo Li 		if (j >= ARRAY_SIZE(simple_widgets)) {
41859a6d4860SXiubo Li 			dev_err(card->dev,
41869a6d4860SXiubo Li 				"ASoC: DAPM widget '%s' is not supported\n",
41879a6d4860SXiubo Li 				template);
41889a6d4860SXiubo Li 			return -EINVAL;
41899a6d4860SXiubo Li 		}
41909a6d4860SXiubo Li 
41919a6d4860SXiubo Li 		ret = of_property_read_string_index(np, propname,
41929a6d4860SXiubo Li 						    (2 * i) + 1,
41939a6d4860SXiubo Li 						    &wname);
41949a6d4860SXiubo Li 		if (ret) {
41959a6d4860SXiubo Li 			dev_err(card->dev,
41969a6d4860SXiubo Li 				"ASoC: Property '%s' index %d read error:%d\n",
41979a6d4860SXiubo Li 				propname, (2 * i) + 1, ret);
41989a6d4860SXiubo Li 			return -EINVAL;
41999a6d4860SXiubo Li 		}
42009a6d4860SXiubo Li 
42019a6d4860SXiubo Li 		widgets[i].name = wname;
42029a6d4860SXiubo Li 	}
42039a6d4860SXiubo Li 
42049a6d4860SXiubo Li 	card->dapm_widgets = widgets;
42059a6d4860SXiubo Li 	card->num_dapm_widgets = num_widgets;
42069a6d4860SXiubo Li 
42079a6d4860SXiubo Li 	return 0;
42089a6d4860SXiubo Li }
42099a6d4860SXiubo Li EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_simple_widgets);
42109a6d4860SXiubo Li 
421189c67857SXiubo Li int snd_soc_of_parse_tdm_slot(struct device_node *np,
421289c67857SXiubo Li 			      unsigned int *slots,
421389c67857SXiubo Li 			      unsigned int *slot_width)
421489c67857SXiubo Li {
421589c67857SXiubo Li 	u32 val;
421689c67857SXiubo Li 	int ret;
421789c67857SXiubo Li 
421889c67857SXiubo Li 	if (of_property_read_bool(np, "dai-tdm-slot-num")) {
421989c67857SXiubo Li 		ret = of_property_read_u32(np, "dai-tdm-slot-num", &val);
422089c67857SXiubo Li 		if (ret)
422189c67857SXiubo Li 			return ret;
422289c67857SXiubo Li 
422389c67857SXiubo Li 		if (slots)
422489c67857SXiubo Li 			*slots = val;
422589c67857SXiubo Li 	}
422689c67857SXiubo Li 
422789c67857SXiubo Li 	if (of_property_read_bool(np, "dai-tdm-slot-width")) {
422889c67857SXiubo Li 		ret = of_property_read_u32(np, "dai-tdm-slot-width", &val);
422989c67857SXiubo Li 		if (ret)
423089c67857SXiubo Li 			return ret;
423189c67857SXiubo Li 
423289c67857SXiubo Li 		if (slot_width)
423389c67857SXiubo Li 			*slot_width = val;
423489c67857SXiubo Li 	}
423589c67857SXiubo Li 
423689c67857SXiubo Li 	return 0;
423789c67857SXiubo Li }
423889c67857SXiubo Li EXPORT_SYMBOL_GPL(snd_soc_of_parse_tdm_slot);
423989c67857SXiubo Li 
4240a4a54dd5SStephen Warren int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
4241a4a54dd5SStephen Warren 				   const char *propname)
4242a4a54dd5SStephen Warren {
4243a4a54dd5SStephen Warren 	struct device_node *np = card->dev->of_node;
4244a4a54dd5SStephen Warren 	int num_routes;
4245a4a54dd5SStephen Warren 	struct snd_soc_dapm_route *routes;
4246a4a54dd5SStephen Warren 	int i, ret;
4247a4a54dd5SStephen Warren 
4248a4a54dd5SStephen Warren 	num_routes = of_property_count_strings(np, propname);
4249c34ce320SRichard Zhao 	if (num_routes < 0 || num_routes & 1) {
425010e8aa9aSMichał Mirosław 		dev_err(card->dev,
425110e8aa9aSMichał Mirosław 			"ASoC: Property '%s' does not exist or its length is not even\n",
425210e8aa9aSMichał Mirosław 			propname);
4253a4a54dd5SStephen Warren 		return -EINVAL;
4254a4a54dd5SStephen Warren 	}
4255a4a54dd5SStephen Warren 	num_routes /= 2;
4256a4a54dd5SStephen Warren 	if (!num_routes) {
4257f110bfc7SLiam Girdwood 		dev_err(card->dev, "ASoC: Property '%s's length is zero\n",
4258a4a54dd5SStephen Warren 			propname);
4259a4a54dd5SStephen Warren 		return -EINVAL;
4260a4a54dd5SStephen Warren 	}
4261a4a54dd5SStephen Warren 
4262a4a54dd5SStephen Warren 	routes = devm_kzalloc(card->dev, num_routes * sizeof(*routes),
4263a4a54dd5SStephen Warren 			      GFP_KERNEL);
4264a4a54dd5SStephen Warren 	if (!routes) {
4265a4a54dd5SStephen Warren 		dev_err(card->dev,
4266f110bfc7SLiam Girdwood 			"ASoC: Could not allocate DAPM route table\n");
4267a4a54dd5SStephen Warren 		return -EINVAL;
4268a4a54dd5SStephen Warren 	}
4269a4a54dd5SStephen Warren 
4270a4a54dd5SStephen Warren 	for (i = 0; i < num_routes; i++) {
4271a4a54dd5SStephen Warren 		ret = of_property_read_string_index(np, propname,
4272a4a54dd5SStephen Warren 			2 * i, &routes[i].sink);
4273a4a54dd5SStephen Warren 		if (ret) {
4274c871bd0bSMark Brown 			dev_err(card->dev,
4275c871bd0bSMark Brown 				"ASoC: Property '%s' index %d could not be read: %d\n",
4276c871bd0bSMark Brown 				propname, 2 * i, ret);
4277a4a54dd5SStephen Warren 			return -EINVAL;
4278a4a54dd5SStephen Warren 		}
4279a4a54dd5SStephen Warren 		ret = of_property_read_string_index(np, propname,
4280a4a54dd5SStephen Warren 			(2 * i) + 1, &routes[i].source);
4281a4a54dd5SStephen Warren 		if (ret) {
4282a4a54dd5SStephen Warren 			dev_err(card->dev,
4283c871bd0bSMark Brown 				"ASoC: Property '%s' index %d could not be read: %d\n",
4284c871bd0bSMark Brown 				propname, (2 * i) + 1, ret);
4285a4a54dd5SStephen Warren 			return -EINVAL;
4286a4a54dd5SStephen Warren 		}
4287a4a54dd5SStephen Warren 	}
4288a4a54dd5SStephen Warren 
4289a4a54dd5SStephen Warren 	card->num_dapm_routes = num_routes;
4290a4a54dd5SStephen Warren 	card->dapm_routes = routes;
4291a4a54dd5SStephen Warren 
4292a4a54dd5SStephen Warren 	return 0;
4293a4a54dd5SStephen Warren }
4294a4a54dd5SStephen Warren EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_routing);
4295a4a54dd5SStephen Warren 
4296a7930ed4SKuninori Morimoto unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
4297389cb834SJyri Sarha 				     const char *prefix,
4298389cb834SJyri Sarha 				     struct device_node **bitclkmaster,
4299389cb834SJyri Sarha 				     struct device_node **framemaster)
4300a7930ed4SKuninori Morimoto {
4301a7930ed4SKuninori Morimoto 	int ret, i;
4302a7930ed4SKuninori Morimoto 	char prop[128];
4303a7930ed4SKuninori Morimoto 	unsigned int format = 0;
4304a7930ed4SKuninori Morimoto 	int bit, frame;
4305a7930ed4SKuninori Morimoto 	const char *str;
4306a7930ed4SKuninori Morimoto 	struct {
4307a7930ed4SKuninori Morimoto 		char *name;
4308a7930ed4SKuninori Morimoto 		unsigned int val;
4309a7930ed4SKuninori Morimoto 	} of_fmt_table[] = {
4310a7930ed4SKuninori Morimoto 		{ "i2s",	SND_SOC_DAIFMT_I2S },
4311a7930ed4SKuninori Morimoto 		{ "right_j",	SND_SOC_DAIFMT_RIGHT_J },
4312a7930ed4SKuninori Morimoto 		{ "left_j",	SND_SOC_DAIFMT_LEFT_J },
4313a7930ed4SKuninori Morimoto 		{ "dsp_a",	SND_SOC_DAIFMT_DSP_A },
4314a7930ed4SKuninori Morimoto 		{ "dsp_b",	SND_SOC_DAIFMT_DSP_B },
4315a7930ed4SKuninori Morimoto 		{ "ac97",	SND_SOC_DAIFMT_AC97 },
4316a7930ed4SKuninori Morimoto 		{ "pdm",	SND_SOC_DAIFMT_PDM},
4317a7930ed4SKuninori Morimoto 		{ "msb",	SND_SOC_DAIFMT_MSB },
4318a7930ed4SKuninori Morimoto 		{ "lsb",	SND_SOC_DAIFMT_LSB },
4319a7930ed4SKuninori Morimoto 	};
4320a7930ed4SKuninori Morimoto 
4321a7930ed4SKuninori Morimoto 	if (!prefix)
4322a7930ed4SKuninori Morimoto 		prefix = "";
4323a7930ed4SKuninori Morimoto 
4324a7930ed4SKuninori Morimoto 	/*
4325a7930ed4SKuninori Morimoto 	 * check "[prefix]format = xxx"
4326a7930ed4SKuninori Morimoto 	 * SND_SOC_DAIFMT_FORMAT_MASK area
4327a7930ed4SKuninori Morimoto 	 */
4328a7930ed4SKuninori Morimoto 	snprintf(prop, sizeof(prop), "%sformat", prefix);
4329a7930ed4SKuninori Morimoto 	ret = of_property_read_string(np, prop, &str);
4330a7930ed4SKuninori Morimoto 	if (ret == 0) {
4331a7930ed4SKuninori Morimoto 		for (i = 0; i < ARRAY_SIZE(of_fmt_table); i++) {
4332a7930ed4SKuninori Morimoto 			if (strcmp(str, of_fmt_table[i].name) == 0) {
4333a7930ed4SKuninori Morimoto 				format |= of_fmt_table[i].val;
4334a7930ed4SKuninori Morimoto 				break;
4335a7930ed4SKuninori Morimoto 			}
4336a7930ed4SKuninori Morimoto 		}
4337a7930ed4SKuninori Morimoto 	}
4338a7930ed4SKuninori Morimoto 
4339a7930ed4SKuninori Morimoto 	/*
43408c2d6a9fSKuninori Morimoto 	 * check "[prefix]continuous-clock"
4341a7930ed4SKuninori Morimoto 	 * SND_SOC_DAIFMT_CLOCK_MASK area
4342a7930ed4SKuninori Morimoto 	 */
43438c2d6a9fSKuninori Morimoto 	snprintf(prop, sizeof(prop), "%scontinuous-clock", prefix);
43448c2d6a9fSKuninori Morimoto 	if (of_get_property(np, prop, NULL))
43458c2d6a9fSKuninori Morimoto 		format |= SND_SOC_DAIFMT_CONT;
43468c2d6a9fSKuninori Morimoto 	else
43478c2d6a9fSKuninori Morimoto 		format |= SND_SOC_DAIFMT_GATED;
4348a7930ed4SKuninori Morimoto 
4349a7930ed4SKuninori Morimoto 	/*
4350a7930ed4SKuninori Morimoto 	 * check "[prefix]bitclock-inversion"
4351a7930ed4SKuninori Morimoto 	 * check "[prefix]frame-inversion"
4352a7930ed4SKuninori Morimoto 	 * SND_SOC_DAIFMT_INV_MASK area
4353a7930ed4SKuninori Morimoto 	 */
4354a7930ed4SKuninori Morimoto 	snprintf(prop, sizeof(prop), "%sbitclock-inversion", prefix);
4355a7930ed4SKuninori Morimoto 	bit = !!of_get_property(np, prop, NULL);
4356a7930ed4SKuninori Morimoto 
4357a7930ed4SKuninori Morimoto 	snprintf(prop, sizeof(prop), "%sframe-inversion", prefix);
4358a7930ed4SKuninori Morimoto 	frame = !!of_get_property(np, prop, NULL);
4359a7930ed4SKuninori Morimoto 
4360a7930ed4SKuninori Morimoto 	switch ((bit << 4) + frame) {
4361a7930ed4SKuninori Morimoto 	case 0x11:
4362a7930ed4SKuninori Morimoto 		format |= SND_SOC_DAIFMT_IB_IF;
4363a7930ed4SKuninori Morimoto 		break;
4364a7930ed4SKuninori Morimoto 	case 0x10:
4365a7930ed4SKuninori Morimoto 		format |= SND_SOC_DAIFMT_IB_NF;
4366a7930ed4SKuninori Morimoto 		break;
4367a7930ed4SKuninori Morimoto 	case 0x01:
4368a7930ed4SKuninori Morimoto 		format |= SND_SOC_DAIFMT_NB_IF;
4369a7930ed4SKuninori Morimoto 		break;
4370a7930ed4SKuninori Morimoto 	default:
4371a7930ed4SKuninori Morimoto 		/* SND_SOC_DAIFMT_NB_NF is default */
4372a7930ed4SKuninori Morimoto 		break;
4373a7930ed4SKuninori Morimoto 	}
4374a7930ed4SKuninori Morimoto 
4375a7930ed4SKuninori Morimoto 	/*
4376a7930ed4SKuninori Morimoto 	 * check "[prefix]bitclock-master"
4377a7930ed4SKuninori Morimoto 	 * check "[prefix]frame-master"
4378a7930ed4SKuninori Morimoto 	 * SND_SOC_DAIFMT_MASTER_MASK area
4379a7930ed4SKuninori Morimoto 	 */
4380a7930ed4SKuninori Morimoto 	snprintf(prop, sizeof(prop), "%sbitclock-master", prefix);
4381a7930ed4SKuninori Morimoto 	bit = !!of_get_property(np, prop, NULL);
4382389cb834SJyri Sarha 	if (bit && bitclkmaster)
4383389cb834SJyri Sarha 		*bitclkmaster = of_parse_phandle(np, prop, 0);
4384a7930ed4SKuninori Morimoto 
4385a7930ed4SKuninori Morimoto 	snprintf(prop, sizeof(prop), "%sframe-master", prefix);
4386a7930ed4SKuninori Morimoto 	frame = !!of_get_property(np, prop, NULL);
4387389cb834SJyri Sarha 	if (frame && framemaster)
4388389cb834SJyri Sarha 		*framemaster = of_parse_phandle(np, prop, 0);
4389a7930ed4SKuninori Morimoto 
4390a7930ed4SKuninori Morimoto 	switch ((bit << 4) + frame) {
4391a7930ed4SKuninori Morimoto 	case 0x11:
4392a7930ed4SKuninori Morimoto 		format |= SND_SOC_DAIFMT_CBM_CFM;
4393a7930ed4SKuninori Morimoto 		break;
4394a7930ed4SKuninori Morimoto 	case 0x10:
4395a7930ed4SKuninori Morimoto 		format |= SND_SOC_DAIFMT_CBM_CFS;
4396a7930ed4SKuninori Morimoto 		break;
4397a7930ed4SKuninori Morimoto 	case 0x01:
4398a7930ed4SKuninori Morimoto 		format |= SND_SOC_DAIFMT_CBS_CFM;
4399a7930ed4SKuninori Morimoto 		break;
4400a7930ed4SKuninori Morimoto 	default:
4401a7930ed4SKuninori Morimoto 		format |= SND_SOC_DAIFMT_CBS_CFS;
4402a7930ed4SKuninori Morimoto 		break;
4403a7930ed4SKuninori Morimoto 	}
4404a7930ed4SKuninori Morimoto 
4405a7930ed4SKuninori Morimoto 	return format;
4406a7930ed4SKuninori Morimoto }
4407a7930ed4SKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_of_parse_daifmt);
4408a7930ed4SKuninori Morimoto 
4409cb470087SKuninori Morimoto int snd_soc_of_get_dai_name(struct device_node *of_node,
4410cb470087SKuninori Morimoto 			    const char **dai_name)
4411cb470087SKuninori Morimoto {
4412cb470087SKuninori Morimoto 	struct snd_soc_component *pos;
4413cb470087SKuninori Morimoto 	struct of_phandle_args args;
4414cb470087SKuninori Morimoto 	int ret;
4415cb470087SKuninori Morimoto 
4416cb470087SKuninori Morimoto 	ret = of_parse_phandle_with_args(of_node, "sound-dai",
4417cb470087SKuninori Morimoto 					 "#sound-dai-cells", 0, &args);
4418cb470087SKuninori Morimoto 	if (ret)
4419cb470087SKuninori Morimoto 		return ret;
4420cb470087SKuninori Morimoto 
4421cb470087SKuninori Morimoto 	ret = -EPROBE_DEFER;
4422cb470087SKuninori Morimoto 
4423cb470087SKuninori Morimoto 	mutex_lock(&client_mutex);
4424cb470087SKuninori Morimoto 	list_for_each_entry(pos, &component_list, list) {
4425cb470087SKuninori Morimoto 		if (pos->dev->of_node != args.np)
4426cb470087SKuninori Morimoto 			continue;
4427cb470087SKuninori Morimoto 
44286833c452SKuninori Morimoto 		if (pos->driver->of_xlate_dai_name) {
44296833c452SKuninori Morimoto 			ret = pos->driver->of_xlate_dai_name(pos, &args, dai_name);
44306833c452SKuninori Morimoto 		} else {
44316833c452SKuninori Morimoto 			int id = -1;
44326833c452SKuninori Morimoto 
44336833c452SKuninori Morimoto 			switch (args.args_count) {
44346833c452SKuninori Morimoto 			case 0:
44356833c452SKuninori Morimoto 				id = 0; /* same as dai_drv[0] */
44366833c452SKuninori Morimoto 				break;
44376833c452SKuninori Morimoto 			case 1:
44386833c452SKuninori Morimoto 				id = args.args[0];
44396833c452SKuninori Morimoto 				break;
44406833c452SKuninori Morimoto 			default:
44416833c452SKuninori Morimoto 				/* not supported */
4442cb470087SKuninori Morimoto 				break;
4443cb470087SKuninori Morimoto 			}
4444cb470087SKuninori Morimoto 
44456833c452SKuninori Morimoto 			if (id < 0 || id >= pos->num_dai) {
44466833c452SKuninori Morimoto 				ret = -EINVAL;
44473dcba280SNicolin Chen 				continue;
44486833c452SKuninori Morimoto 			}
4449e41975edSXiubo Li 
4450e41975edSXiubo Li 			ret = 0;
4451e41975edSXiubo Li 
4452e41975edSXiubo Li 			*dai_name = pos->dai_drv[id].name;
4453e41975edSXiubo Li 			if (!*dai_name)
4454e41975edSXiubo Li 				*dai_name = pos->name;
44556833c452SKuninori Morimoto 		}
44566833c452SKuninori Morimoto 
4457cb470087SKuninori Morimoto 		break;
4458cb470087SKuninori Morimoto 	}
4459cb470087SKuninori Morimoto 	mutex_unlock(&client_mutex);
4460cb470087SKuninori Morimoto 
4461cb470087SKuninori Morimoto 	of_node_put(args.np);
4462cb470087SKuninori Morimoto 
4463cb470087SKuninori Morimoto 	return ret;
4464cb470087SKuninori Morimoto }
4465cb470087SKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_name);
4466cb470087SKuninori Morimoto 
4467c9b3a40fSTakashi Iwai static int __init snd_soc_init(void)
4468db2a4165SFrank Mandarino {
4469db2a4165SFrank Mandarino #ifdef CONFIG_DEBUG_FS
44708a9dab1aSMark Brown 	snd_soc_debugfs_root = debugfs_create_dir("asoc", NULL);
44718a9dab1aSMark Brown 	if (IS_ERR(snd_soc_debugfs_root) || !snd_soc_debugfs_root) {
44720837fc62SFabio Estevam 		pr_warn("ASoC: Failed to create debugfs directory\n");
44738a9dab1aSMark Brown 		snd_soc_debugfs_root = NULL;
4474db2a4165SFrank Mandarino 	}
4475c3c5a19aSMark Brown 
44768a9dab1aSMark Brown 	if (!debugfs_create_file("codecs", 0444, snd_soc_debugfs_root, NULL,
4477c3c5a19aSMark Brown 				 &codec_list_fops))
4478c3c5a19aSMark Brown 		pr_warn("ASoC: Failed to create CODEC list debugfs file\n");
4479c3c5a19aSMark Brown 
44808a9dab1aSMark Brown 	if (!debugfs_create_file("dais", 0444, snd_soc_debugfs_root, NULL,
4481f3208780SMark Brown 				 &dai_list_fops))
4482f3208780SMark Brown 		pr_warn("ASoC: Failed to create DAI list debugfs file\n");
448319c7ac27SMark Brown 
44848a9dab1aSMark Brown 	if (!debugfs_create_file("platforms", 0444, snd_soc_debugfs_root, NULL,
448519c7ac27SMark Brown 				 &platform_list_fops))
448619c7ac27SMark Brown 		pr_warn("ASoC: Failed to create platform list debugfs file\n");
4487db2a4165SFrank Mandarino #endif
4488db2a4165SFrank Mandarino 
4489fb257897SMark Brown 	snd_soc_util_init();
4490fb257897SMark Brown 
4491db2a4165SFrank Mandarino 	return platform_driver_register(&soc_driver);
4492db2a4165SFrank Mandarino }
44934abe8e16SMark Brown module_init(snd_soc_init);
4494db2a4165SFrank Mandarino 
4495db2a4165SFrank Mandarino static void __exit snd_soc_exit(void)
4496db2a4165SFrank Mandarino {
4497fb257897SMark Brown 	snd_soc_util_exit();
4498fb257897SMark Brown 
4499db2a4165SFrank Mandarino #ifdef CONFIG_DEBUG_FS
45008a9dab1aSMark Brown 	debugfs_remove_recursive(snd_soc_debugfs_root);
4501db2a4165SFrank Mandarino #endif
4502db2a4165SFrank Mandarino 	platform_driver_unregister(&soc_driver);
4503db2a4165SFrank Mandarino }
4504db2a4165SFrank Mandarino module_exit(snd_soc_exit);
4505db2a4165SFrank Mandarino 
4506db2a4165SFrank Mandarino /* Module information */
4507db2a4165SFrank Mandarino MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk");
4508db2a4165SFrank Mandarino MODULE_DESCRIPTION("ALSA SoC Core");
4509db2a4165SFrank Mandarino MODULE_LICENSE("GPL");
4510db2a4165SFrank Mandarino MODULE_ALIAS("platform:soc-audio");
4511