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> 37a180e8b9SKuninori Morimoto #include <linux/of_graph.h> 38345233d7SLiam Girdwood #include <linux/dmi.h> 39db2a4165SFrank Mandarino #include <sound/core.h> 403028eb8cSMark Brown #include <sound/jack.h> 41db2a4165SFrank Mandarino #include <sound/pcm.h> 42db2a4165SFrank Mandarino #include <sound/pcm_params.h> 43db2a4165SFrank Mandarino #include <sound/soc.h> 4401d7584cSLiam Girdwood #include <sound/soc-dpcm.h> 458a978234SLiam Girdwood #include <sound/soc-topology.h> 46db2a4165SFrank Mandarino #include <sound/initval.h> 47db2a4165SFrank Mandarino 48a8b1d34fSMark Brown #define CREATE_TRACE_POINTS 49a8b1d34fSMark Brown #include <trace/events/asoc.h> 50a8b1d34fSMark Brown 51f0fba2adSLiam Girdwood #define NAME_SIZE 32 52f0fba2adSLiam Girdwood 53384c89e2SMark Brown #ifdef CONFIG_DEBUG_FS 548a9dab1aSMark Brown struct dentry *snd_soc_debugfs_root; 558a9dab1aSMark Brown EXPORT_SYMBOL_GPL(snd_soc_debugfs_root); 56384c89e2SMark Brown #endif 57384c89e2SMark Brown 58c5af3a2eSMark Brown static DEFINE_MUTEX(client_mutex); 590d0cf00aSMark Brown static LIST_HEAD(codec_list); 60030e79f6SKuninori Morimoto static LIST_HEAD(component_list); 61c5af3a2eSMark Brown 62db2a4165SFrank Mandarino /* 63db2a4165SFrank Mandarino * This is a timeout to do a DAPM powerdown after a stream is closed(). 64db2a4165SFrank Mandarino * It can be used to eliminate pops between different playback streams, e.g. 65db2a4165SFrank Mandarino * between two audio tracks. 66db2a4165SFrank Mandarino */ 67db2a4165SFrank Mandarino static int pmdown_time = 5000; 68db2a4165SFrank Mandarino module_param(pmdown_time, int, 0); 69db2a4165SFrank Mandarino MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)"); 70db2a4165SFrank Mandarino 7198faf436SMengdong Lin /* If a DMI filed contain strings in this blacklist (e.g. 7298faf436SMengdong Lin * "Type2 - Board Manufacturer" or "Type1 - TBD by OEM"), it will be taken 7398faf436SMengdong Lin * as invalid and dropped when setting the card long name from DMI info. 7498faf436SMengdong Lin */ 7598faf436SMengdong Lin static const char * const dmi_blacklist[] = { 7698faf436SMengdong Lin "To be filled by OEM", 7798faf436SMengdong Lin "TBD by OEM", 7898faf436SMengdong Lin "Default String", 7998faf436SMengdong Lin "Board Manufacturer", 8098faf436SMengdong Lin "Board Vendor Name", 8198faf436SMengdong Lin "Board Product Name", 8298faf436SMengdong Lin NULL, /* terminator */ 8398faf436SMengdong Lin }; 8498faf436SMengdong Lin 852bc9a81eSDimitris Papastamos /* returns the minimum number of bytes needed to represent 862bc9a81eSDimitris Papastamos * a particular given value */ 872bc9a81eSDimitris Papastamos static int min_bytes_needed(unsigned long val) 882bc9a81eSDimitris Papastamos { 892bc9a81eSDimitris Papastamos int c = 0; 902bc9a81eSDimitris Papastamos int i; 912bc9a81eSDimitris Papastamos 922bc9a81eSDimitris Papastamos for (i = (sizeof val * 8) - 1; i >= 0; --i, ++c) 932bc9a81eSDimitris Papastamos if (val & (1UL << i)) 942bc9a81eSDimitris Papastamos break; 952bc9a81eSDimitris Papastamos c = (sizeof val * 8) - c; 962bc9a81eSDimitris Papastamos if (!c || (c % 8)) 972bc9a81eSDimitris Papastamos c = (c + 8) / 8; 982bc9a81eSDimitris Papastamos else 992bc9a81eSDimitris Papastamos c /= 8; 1002bc9a81eSDimitris Papastamos return c; 1012bc9a81eSDimitris Papastamos } 1022bc9a81eSDimitris Papastamos 10313fd179fSDimitris Papastamos /* fill buf which is 'len' bytes with a formatted 10413fd179fSDimitris Papastamos * string of the form 'reg: value\n' */ 10513fd179fSDimitris Papastamos static int format_register_str(struct snd_soc_codec *codec, 10613fd179fSDimitris Papastamos unsigned int reg, char *buf, size_t len) 1072624d5faSMark Brown { 10800b317a4SStephen Warren int wordsize = min_bytes_needed(codec->driver->reg_cache_size) * 2; 10900b317a4SStephen Warren int regsize = codec->driver->reg_word_size * 2; 11013fd179fSDimitris Papastamos int ret; 11113fd179fSDimitris Papastamos 11213fd179fSDimitris Papastamos /* +2 for ': ' and + 1 for '\n' */ 11313fd179fSDimitris Papastamos if (wordsize + regsize + 2 + 1 != len) 11413fd179fSDimitris Papastamos return -EINVAL; 11513fd179fSDimitris Papastamos 116d6b6c2caSTakashi Iwai sprintf(buf, "%.*x: ", wordsize, reg); 117d6b6c2caSTakashi Iwai buf += wordsize + 2; 118d6b6c2caSTakashi Iwai 11913fd179fSDimitris Papastamos ret = snd_soc_read(codec, reg); 120d6b6c2caSTakashi Iwai if (ret < 0) 121d6b6c2caSTakashi Iwai memset(buf, 'X', regsize); 122d6b6c2caSTakashi Iwai else 123d6b6c2caSTakashi Iwai sprintf(buf, "%.*x", regsize, ret); 124d6b6c2caSTakashi Iwai buf[regsize] = '\n'; 125d6b6c2caSTakashi Iwai /* no NUL-termination needed */ 12613fd179fSDimitris Papastamos return 0; 12713fd179fSDimitris Papastamos } 12813fd179fSDimitris Papastamos 12913fd179fSDimitris Papastamos /* codec register dump */ 13013fd179fSDimitris Papastamos static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf, 13113fd179fSDimitris Papastamos size_t count, loff_t pos) 13213fd179fSDimitris Papastamos { 13313fd179fSDimitris Papastamos int i, step = 1; 1342bc9a81eSDimitris Papastamos int wordsize, regsize; 13513fd179fSDimitris Papastamos int len; 13613fd179fSDimitris Papastamos size_t total = 0; 13713fd179fSDimitris Papastamos loff_t p = 0; 1382bc9a81eSDimitris Papastamos 13900b317a4SStephen Warren wordsize = min_bytes_needed(codec->driver->reg_cache_size) * 2; 14000b317a4SStephen Warren regsize = codec->driver->reg_word_size * 2; 1412624d5faSMark Brown 14213fd179fSDimitris Papastamos len = wordsize + regsize + 2 + 1; 14313fd179fSDimitris Papastamos 144f0fba2adSLiam Girdwood if (!codec->driver->reg_cache_size) 1452624d5faSMark Brown return 0; 1462624d5faSMark Brown 147f0fba2adSLiam Girdwood if (codec->driver->reg_cache_step) 148f0fba2adSLiam Girdwood step = codec->driver->reg_cache_step; 1492624d5faSMark Brown 150f0fba2adSLiam Girdwood for (i = 0; i < codec->driver->reg_cache_size; i += step) { 15113fd179fSDimitris Papastamos /* only support larger than PAGE_SIZE bytes debugfs 15213fd179fSDimitris Papastamos * entries for the default case */ 15313fd179fSDimitris Papastamos if (p >= pos) { 15413fd179fSDimitris Papastamos if (total + len >= count - 1) 1552624d5faSMark Brown break; 15613fd179fSDimitris Papastamos format_register_str(codec, i, buf + total, len); 15713fd179fSDimitris Papastamos total += len; 15813fd179fSDimitris Papastamos } 15913fd179fSDimitris Papastamos p += len; 16013fd179fSDimitris Papastamos } 1612624d5faSMark Brown 16213fd179fSDimitris Papastamos total = min(total, count - 1); 1632624d5faSMark Brown 16413fd179fSDimitris Papastamos return total; 1652624d5faSMark Brown } 16613fd179fSDimitris Papastamos 1672624d5faSMark Brown static ssize_t codec_reg_show(struct device *dev, 1682624d5faSMark Brown struct device_attribute *attr, char *buf) 1692624d5faSMark Brown { 17036ae1a96SMark Brown struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev); 171f0fba2adSLiam Girdwood 17213fd179fSDimitris Papastamos return soc_codec_reg_show(rtd->codec, buf, PAGE_SIZE, 0); 1732624d5faSMark Brown } 1742624d5faSMark Brown 175c828a892SJoe Perches static DEVICE_ATTR_RO(codec_reg); 1762624d5faSMark Brown 177dbe21408SMark Brown static ssize_t pmdown_time_show(struct device *dev, 178dbe21408SMark Brown struct device_attribute *attr, char *buf) 179dbe21408SMark Brown { 18036ae1a96SMark Brown struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev); 181dbe21408SMark Brown 182f0fba2adSLiam Girdwood return sprintf(buf, "%ld\n", rtd->pmdown_time); 183dbe21408SMark Brown } 184dbe21408SMark Brown 185dbe21408SMark Brown static ssize_t pmdown_time_set(struct device *dev, 186dbe21408SMark Brown struct device_attribute *attr, 187dbe21408SMark Brown const char *buf, size_t count) 188dbe21408SMark Brown { 18936ae1a96SMark Brown struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev); 190c593b520SMark Brown int ret; 191dbe21408SMark Brown 192b785a492SJingoo Han ret = kstrtol(buf, 10, &rtd->pmdown_time); 193c593b520SMark Brown if (ret) 194c593b520SMark Brown return ret; 195dbe21408SMark Brown 196dbe21408SMark Brown return count; 197dbe21408SMark Brown } 198dbe21408SMark Brown 199dbe21408SMark Brown static DEVICE_ATTR(pmdown_time, 0644, pmdown_time_show, pmdown_time_set); 200dbe21408SMark Brown 201d29697dcSTakashi Iwai static struct attribute *soc_dev_attrs[] = { 202d29697dcSTakashi Iwai &dev_attr_codec_reg.attr, 203d29697dcSTakashi Iwai &dev_attr_pmdown_time.attr, 204d29697dcSTakashi Iwai NULL 205d29697dcSTakashi Iwai }; 206d29697dcSTakashi Iwai 207d29697dcSTakashi Iwai static umode_t soc_dev_attr_is_visible(struct kobject *kobj, 208d29697dcSTakashi Iwai struct attribute *attr, int idx) 209d29697dcSTakashi Iwai { 210d29697dcSTakashi Iwai struct device *dev = kobj_to_dev(kobj); 211d29697dcSTakashi Iwai struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev); 212d29697dcSTakashi Iwai 213d29697dcSTakashi Iwai if (attr == &dev_attr_pmdown_time.attr) 214d29697dcSTakashi Iwai return attr->mode; /* always visible */ 2153b6eed8dSKuninori Morimoto return rtd->num_codecs ? attr->mode : 0; /* enabled only with codec */ 216d29697dcSTakashi Iwai } 217d29697dcSTakashi Iwai 218d29697dcSTakashi Iwai static const struct attribute_group soc_dapm_dev_group = { 219d29697dcSTakashi Iwai .attrs = soc_dapm_dev_attrs, 220d29697dcSTakashi Iwai .is_visible = soc_dev_attr_is_visible, 221d29697dcSTakashi Iwai }; 222d29697dcSTakashi Iwai 223f7e73b26SMark Brown static const struct attribute_group soc_dev_group = { 224d29697dcSTakashi Iwai .attrs = soc_dev_attrs, 225d29697dcSTakashi Iwai .is_visible = soc_dev_attr_is_visible, 226d29697dcSTakashi Iwai }; 227d29697dcSTakashi Iwai 228d29697dcSTakashi Iwai static const struct attribute_group *soc_dev_attr_groups[] = { 229d29697dcSTakashi Iwai &soc_dapm_dev_group, 230f7e73b26SMark Brown &soc_dev_group, 231d29697dcSTakashi Iwai NULL 232d29697dcSTakashi Iwai }; 233d29697dcSTakashi Iwai 2342624d5faSMark Brown #ifdef CONFIG_DEBUG_FS 2352624d5faSMark Brown static ssize_t codec_reg_read_file(struct file *file, char __user *user_buf, 2362624d5faSMark Brown size_t count, loff_t *ppos) 2372624d5faSMark Brown { 2382624d5faSMark Brown ssize_t ret; 2392624d5faSMark Brown struct snd_soc_codec *codec = file->private_data; 24013fd179fSDimitris Papastamos char *buf; 24113fd179fSDimitris Papastamos 24213fd179fSDimitris Papastamos if (*ppos < 0 || !count) 24313fd179fSDimitris Papastamos return -EINVAL; 24413fd179fSDimitris Papastamos 24513fd179fSDimitris Papastamos buf = kmalloc(count, GFP_KERNEL); 2462624d5faSMark Brown if (!buf) 2472624d5faSMark Brown return -ENOMEM; 24813fd179fSDimitris Papastamos 24913fd179fSDimitris Papastamos ret = soc_codec_reg_show(codec, buf, count, *ppos); 25013fd179fSDimitris Papastamos if (ret >= 0) { 25113fd179fSDimitris Papastamos if (copy_to_user(user_buf, buf, ret)) { 25213fd179fSDimitris Papastamos kfree(buf); 25313fd179fSDimitris Papastamos return -EFAULT; 25413fd179fSDimitris Papastamos } 25513fd179fSDimitris Papastamos *ppos += ret; 25613fd179fSDimitris Papastamos } 25713fd179fSDimitris Papastamos 2582624d5faSMark Brown kfree(buf); 2592624d5faSMark Brown return ret; 2602624d5faSMark Brown } 2612624d5faSMark Brown 2622624d5faSMark Brown static ssize_t codec_reg_write_file(struct file *file, 2632624d5faSMark Brown const char __user *user_buf, size_t count, loff_t *ppos) 2642624d5faSMark Brown { 2652624d5faSMark Brown char buf[32]; 26634e268d8SStephen Boyd size_t buf_size; 2672624d5faSMark Brown char *start = buf; 2682624d5faSMark Brown unsigned long reg, value; 2692624d5faSMark Brown struct snd_soc_codec *codec = file->private_data; 270b785a492SJingoo Han int ret; 2712624d5faSMark Brown 2722624d5faSMark Brown buf_size = min(count, (sizeof(buf)-1)); 2732624d5faSMark Brown if (copy_from_user(buf, user_buf, buf_size)) 2742624d5faSMark Brown return -EFAULT; 2752624d5faSMark Brown buf[buf_size] = 0; 2762624d5faSMark Brown 2772624d5faSMark Brown while (*start == ' ') 2782624d5faSMark Brown start++; 2792624d5faSMark Brown reg = simple_strtoul(start, &start, 16); 2802624d5faSMark Brown while (*start == ' ') 2812624d5faSMark Brown start++; 282b785a492SJingoo Han ret = kstrtoul(start, 16, &value); 283b785a492SJingoo Han if (ret) 284b785a492SJingoo Han return ret; 2850d51a9cbSMark Brown 2860d51a9cbSMark Brown /* Userspace has been fiddling around behind the kernel's back */ 287373d4d09SRusty Russell add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE); 2880d51a9cbSMark Brown 289e4f078d8SDimitris Papastamos snd_soc_write(codec, reg, value); 2902624d5faSMark Brown return buf_size; 2912624d5faSMark Brown } 2922624d5faSMark Brown 2932624d5faSMark Brown static const struct file_operations codec_reg_fops = { 294234e3405SStephen Boyd .open = simple_open, 2952624d5faSMark Brown .read = codec_reg_read_file, 2962624d5faSMark Brown .write = codec_reg_write_file, 2976038f373SArnd Bergmann .llseek = default_llseek, 2982624d5faSMark Brown }; 2992624d5faSMark Brown 30081c7cfd1SLars-Peter Clausen static void soc_init_component_debugfs(struct snd_soc_component *component) 301e73f3de5SRussell King { 3026553bf06SLars-Peter Clausen if (!component->card->debugfs_card_root) 3036553bf06SLars-Peter Clausen return; 3046553bf06SLars-Peter Clausen 30581c7cfd1SLars-Peter Clausen if (component->debugfs_prefix) { 30681c7cfd1SLars-Peter Clausen char *name; 307e73f3de5SRussell King 30881c7cfd1SLars-Peter Clausen name = kasprintf(GFP_KERNEL, "%s:%s", 30981c7cfd1SLars-Peter Clausen component->debugfs_prefix, component->name); 31081c7cfd1SLars-Peter Clausen if (name) { 31181c7cfd1SLars-Peter Clausen component->debugfs_root = debugfs_create_dir(name, 31281c7cfd1SLars-Peter Clausen component->card->debugfs_card_root); 31381c7cfd1SLars-Peter Clausen kfree(name); 31481c7cfd1SLars-Peter Clausen } 31581c7cfd1SLars-Peter Clausen } else { 31681c7cfd1SLars-Peter Clausen component->debugfs_root = debugfs_create_dir(component->name, 31781c7cfd1SLars-Peter Clausen component->card->debugfs_card_root); 318e73f3de5SRussell King } 319e73f3de5SRussell King 32081c7cfd1SLars-Peter Clausen if (!component->debugfs_root) { 32181c7cfd1SLars-Peter Clausen dev_warn(component->dev, 32281c7cfd1SLars-Peter Clausen "ASoC: Failed to create component debugfs directory\n"); 3232624d5faSMark Brown return; 3242624d5faSMark Brown } 3252624d5faSMark Brown 32681c7cfd1SLars-Peter Clausen snd_soc_dapm_debugfs_init(snd_soc_component_get_dapm(component), 32781c7cfd1SLars-Peter Clausen component->debugfs_root); 32881c7cfd1SLars-Peter Clausen 32981c7cfd1SLars-Peter Clausen if (component->init_debugfs) 33081c7cfd1SLars-Peter Clausen component->init_debugfs(component); 33181c7cfd1SLars-Peter Clausen } 33281c7cfd1SLars-Peter Clausen 33381c7cfd1SLars-Peter Clausen static void soc_cleanup_component_debugfs(struct snd_soc_component *component) 33481c7cfd1SLars-Peter Clausen { 33581c7cfd1SLars-Peter Clausen debugfs_remove_recursive(component->debugfs_root); 33681c7cfd1SLars-Peter Clausen } 33781c7cfd1SLars-Peter Clausen 33881c7cfd1SLars-Peter Clausen static void soc_init_codec_debugfs(struct snd_soc_component *component) 33981c7cfd1SLars-Peter Clausen { 34081c7cfd1SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_component_to_codec(component); 341353c64ddSFabio Estevam struct dentry *debugfs_reg; 34281c7cfd1SLars-Peter Clausen 343353c64ddSFabio Estevam debugfs_reg = debugfs_create_file("codec_reg", 0644, 34481c7cfd1SLars-Peter Clausen codec->component.debugfs_root, 3452624d5faSMark Brown codec, &codec_reg_fops); 346353c64ddSFabio Estevam if (!debugfs_reg) 34710e8aa9aSMichał Mirosław dev_warn(codec->dev, 34810e8aa9aSMichał Mirosław "ASoC: Failed to create codec register debugfs file\n"); 349731f1ab2SSebastien Guiriec } 350731f1ab2SSebastien Guiriec 351c15b2a1dSPeng Donglin static int codec_list_show(struct seq_file *m, void *v) 352c3c5a19aSMark Brown { 353c3c5a19aSMark Brown struct snd_soc_codec *codec; 354c3c5a19aSMark Brown 35534e81ab4SLars-Peter Clausen mutex_lock(&client_mutex); 35634e81ab4SLars-Peter Clausen 357700c17caSDonglin Peng list_for_each_entry(codec, &codec_list, list) 358700c17caSDonglin Peng seq_printf(m, "%s\n", codec->component.name); 359c3c5a19aSMark Brown 36034e81ab4SLars-Peter Clausen mutex_unlock(&client_mutex); 36134e81ab4SLars-Peter Clausen 362700c17caSDonglin Peng return 0; 363700c17caSDonglin Peng } 364c15b2a1dSPeng Donglin DEFINE_SHOW_ATTRIBUTE(codec_list); 365c3c5a19aSMark Brown 366c15b2a1dSPeng Donglin static int dai_list_show(struct seq_file *m, void *v) 367f3208780SMark Brown { 3681438c2f6SLars-Peter Clausen struct snd_soc_component *component; 369f3208780SMark Brown struct snd_soc_dai *dai; 370f3208780SMark Brown 37134e81ab4SLars-Peter Clausen mutex_lock(&client_mutex); 37234e81ab4SLars-Peter Clausen 373700c17caSDonglin Peng list_for_each_entry(component, &component_list, list) 374700c17caSDonglin Peng list_for_each_entry(dai, &component->dai_list, list) 375700c17caSDonglin Peng seq_printf(m, "%s\n", dai->name); 376f3208780SMark Brown 37734e81ab4SLars-Peter Clausen mutex_unlock(&client_mutex); 37834e81ab4SLars-Peter Clausen 379700c17caSDonglin Peng return 0; 380700c17caSDonglin Peng } 381c15b2a1dSPeng Donglin DEFINE_SHOW_ATTRIBUTE(dai_list); 382f3208780SMark Brown 383a6052154SJarkko Nikula static void soc_init_card_debugfs(struct snd_soc_card *card) 384a6052154SJarkko Nikula { 3856553bf06SLars-Peter Clausen if (!snd_soc_debugfs_root) 3866553bf06SLars-Peter Clausen return; 3876553bf06SLars-Peter Clausen 388a6052154SJarkko Nikula card->debugfs_card_root = debugfs_create_dir(card->name, 3898a9dab1aSMark Brown snd_soc_debugfs_root); 3903a45b867SJarkko Nikula if (!card->debugfs_card_root) { 391a6052154SJarkko Nikula dev_warn(card->dev, 3927c08be84SLothar Waßmann "ASoC: Failed to create card debugfs directory\n"); 3933a45b867SJarkko Nikula return; 3943a45b867SJarkko Nikula } 3953a45b867SJarkko Nikula 3963a45b867SJarkko Nikula card->debugfs_pop_time = debugfs_create_u32("dapm_pop_time", 0644, 3973a45b867SJarkko Nikula card->debugfs_card_root, 3983a45b867SJarkko Nikula &card->pop_time); 3993a45b867SJarkko Nikula if (!card->debugfs_pop_time) 4003a45b867SJarkko Nikula dev_warn(card->dev, 401f110bfc7SLiam Girdwood "ASoC: Failed to create pop time debugfs file\n"); 402a6052154SJarkko Nikula } 403a6052154SJarkko Nikula 404a6052154SJarkko Nikula static void soc_cleanup_card_debugfs(struct snd_soc_card *card) 405a6052154SJarkko Nikula { 406a6052154SJarkko Nikula debugfs_remove_recursive(card->debugfs_card_root); 407a6052154SJarkko Nikula } 408a6052154SJarkko Nikula 4096553bf06SLars-Peter Clausen static void snd_soc_debugfs_init(void) 4106553bf06SLars-Peter Clausen { 4116553bf06SLars-Peter Clausen snd_soc_debugfs_root = debugfs_create_dir("asoc", NULL); 412d9a02c55SFabio Estevam if (IS_ERR_OR_NULL(snd_soc_debugfs_root)) { 4136553bf06SLars-Peter Clausen pr_warn("ASoC: Failed to create debugfs directory\n"); 4146553bf06SLars-Peter Clausen snd_soc_debugfs_root = NULL; 4156553bf06SLars-Peter Clausen return; 4166553bf06SLars-Peter Clausen } 4176553bf06SLars-Peter Clausen 4186553bf06SLars-Peter Clausen if (!debugfs_create_file("codecs", 0444, snd_soc_debugfs_root, NULL, 4196553bf06SLars-Peter Clausen &codec_list_fops)) 4206553bf06SLars-Peter Clausen pr_warn("ASoC: Failed to create CODEC list debugfs file\n"); 4216553bf06SLars-Peter Clausen 4226553bf06SLars-Peter Clausen if (!debugfs_create_file("dais", 0444, snd_soc_debugfs_root, NULL, 4236553bf06SLars-Peter Clausen &dai_list_fops)) 4246553bf06SLars-Peter Clausen pr_warn("ASoC: Failed to create DAI list debugfs file\n"); 4256553bf06SLars-Peter Clausen } 4266553bf06SLars-Peter Clausen 4276553bf06SLars-Peter Clausen static void snd_soc_debugfs_exit(void) 4286553bf06SLars-Peter Clausen { 4296553bf06SLars-Peter Clausen debugfs_remove_recursive(snd_soc_debugfs_root); 4306553bf06SLars-Peter Clausen } 4316553bf06SLars-Peter Clausen 4322624d5faSMark Brown #else 4332624d5faSMark Brown 43481c7cfd1SLars-Peter Clausen #define soc_init_codec_debugfs NULL 43581c7cfd1SLars-Peter Clausen 43681c7cfd1SLars-Peter Clausen static inline void soc_init_component_debugfs( 43781c7cfd1SLars-Peter Clausen struct snd_soc_component *component) 4382624d5faSMark Brown { 4392624d5faSMark Brown } 4402624d5faSMark Brown 44181c7cfd1SLars-Peter Clausen static inline void soc_cleanup_component_debugfs( 44281c7cfd1SLars-Peter Clausen struct snd_soc_component *component) 443731f1ab2SSebastien Guiriec { 444731f1ab2SSebastien Guiriec } 445731f1ab2SSebastien Guiriec 446b95fccbcSAxel Lin static inline void soc_init_card_debugfs(struct snd_soc_card *card) 447b95fccbcSAxel Lin { 448b95fccbcSAxel Lin } 449b95fccbcSAxel Lin 450b95fccbcSAxel Lin static inline void soc_cleanup_card_debugfs(struct snd_soc_card *card) 451b95fccbcSAxel Lin { 452b95fccbcSAxel Lin } 4536553bf06SLars-Peter Clausen 4546553bf06SLars-Peter Clausen static inline void snd_soc_debugfs_init(void) 4556553bf06SLars-Peter Clausen { 4566553bf06SLars-Peter Clausen } 4576553bf06SLars-Peter Clausen 4586553bf06SLars-Peter Clausen static inline void snd_soc_debugfs_exit(void) 4596553bf06SLars-Peter Clausen { 4606553bf06SLars-Peter Clausen } 4616553bf06SLars-Peter Clausen 4622624d5faSMark Brown #endif 4632624d5faSMark Brown 464a0ac4411SKuninori Morimoto static int snd_soc_rtdcom_add(struct snd_soc_pcm_runtime *rtd, 465a0ac4411SKuninori Morimoto struct snd_soc_component *component) 466a0ac4411SKuninori Morimoto { 467a0ac4411SKuninori Morimoto struct snd_soc_rtdcom_list *rtdcom; 468a0ac4411SKuninori Morimoto struct snd_soc_rtdcom_list *new_rtdcom; 469a0ac4411SKuninori Morimoto 470a0ac4411SKuninori Morimoto for_each_rtdcom(rtd, rtdcom) { 471a0ac4411SKuninori Morimoto /* already connected */ 472a0ac4411SKuninori Morimoto if (rtdcom->component == component) 473a0ac4411SKuninori Morimoto return 0; 474a0ac4411SKuninori Morimoto } 475a0ac4411SKuninori Morimoto 476a0ac4411SKuninori Morimoto new_rtdcom = kmalloc(sizeof(*new_rtdcom), GFP_KERNEL); 477a0ac4411SKuninori Morimoto if (!new_rtdcom) 478a0ac4411SKuninori Morimoto return -ENOMEM; 479a0ac4411SKuninori Morimoto 480a0ac4411SKuninori Morimoto new_rtdcom->component = component; 481a0ac4411SKuninori Morimoto INIT_LIST_HEAD(&new_rtdcom->list); 482a0ac4411SKuninori Morimoto 483a0ac4411SKuninori Morimoto list_add_tail(&new_rtdcom->list, &rtd->component_list); 484a0ac4411SKuninori Morimoto 485a0ac4411SKuninori Morimoto return 0; 486a0ac4411SKuninori Morimoto } 487a0ac4411SKuninori Morimoto 488a0ac4411SKuninori Morimoto static void snd_soc_rtdcom_del_all(struct snd_soc_pcm_runtime *rtd) 489a0ac4411SKuninori Morimoto { 490a0ac4411SKuninori Morimoto struct snd_soc_rtdcom_list *rtdcom1, *rtdcom2; 491a0ac4411SKuninori Morimoto 492a0ac4411SKuninori Morimoto for_each_rtdcom_safe(rtd, rtdcom1, rtdcom2) 493a0ac4411SKuninori Morimoto kfree(rtdcom1); 494a0ac4411SKuninori Morimoto 495a0ac4411SKuninori Morimoto INIT_LIST_HEAD(&rtd->component_list); 496a0ac4411SKuninori Morimoto } 497a0ac4411SKuninori Morimoto 498a0ac4411SKuninori Morimoto struct snd_soc_component *snd_soc_rtdcom_lookup(struct snd_soc_pcm_runtime *rtd, 499a0ac4411SKuninori Morimoto const char *driver_name) 500a0ac4411SKuninori Morimoto { 501a0ac4411SKuninori Morimoto struct snd_soc_rtdcom_list *rtdcom; 502a0ac4411SKuninori Morimoto 503971da24cSKuninori Morimoto if (!driver_name) 504971da24cSKuninori Morimoto return NULL; 505971da24cSKuninori Morimoto 506a0ac4411SKuninori Morimoto for_each_rtdcom(rtd, rtdcom) { 507971da24cSKuninori Morimoto const char *component_name = rtdcom->component->driver->name; 508971da24cSKuninori Morimoto 509971da24cSKuninori Morimoto if (!component_name) 510971da24cSKuninori Morimoto continue; 511971da24cSKuninori Morimoto 512971da24cSKuninori Morimoto if ((component_name == driver_name) || 513971da24cSKuninori Morimoto strcmp(component_name, driver_name) == 0) 514a0ac4411SKuninori Morimoto return rtdcom->component; 515a0ac4411SKuninori Morimoto } 516a0ac4411SKuninori Morimoto 517a0ac4411SKuninori Morimoto return NULL; 518a0ac4411SKuninori Morimoto } 519031734b7SKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_rtdcom_lookup); 520a0ac4411SKuninori Morimoto 52147c88fffSLiam Girdwood struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card, 52247c88fffSLiam Girdwood const char *dai_link, int stream) 52347c88fffSLiam Girdwood { 5241a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd; 52547c88fffSLiam Girdwood 5261a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 5271a497983SMengdong Lin if (rtd->dai_link->no_pcm && 5281a497983SMengdong Lin !strcmp(rtd->dai_link->name, dai_link)) 5291a497983SMengdong Lin return rtd->pcm->streams[stream].substream; 53047c88fffSLiam Girdwood } 531f110bfc7SLiam Girdwood dev_dbg(card->dev, "ASoC: failed to find dai link %s\n", dai_link); 53247c88fffSLiam Girdwood return NULL; 53347c88fffSLiam Girdwood } 53447c88fffSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_get_dai_substream); 53547c88fffSLiam Girdwood 53675ab9eb6SKuninori Morimoto static const struct snd_soc_ops null_snd_soc_ops; 53775ab9eb6SKuninori Morimoto 5381a497983SMengdong Lin static struct snd_soc_pcm_runtime *soc_new_pcm_runtime( 5391a497983SMengdong Lin struct snd_soc_card *card, struct snd_soc_dai_link *dai_link) 5401a497983SMengdong Lin { 5411a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd; 5421a497983SMengdong Lin 5431a497983SMengdong Lin rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime), GFP_KERNEL); 5441a497983SMengdong Lin if (!rtd) 5451a497983SMengdong Lin return NULL; 5461a497983SMengdong Lin 547a0ac4411SKuninori Morimoto INIT_LIST_HEAD(&rtd->component_list); 5481a497983SMengdong Lin rtd->card = card; 5491a497983SMengdong Lin rtd->dai_link = dai_link; 55075ab9eb6SKuninori Morimoto if (!rtd->dai_link->ops) 55175ab9eb6SKuninori Morimoto rtd->dai_link->ops = &null_snd_soc_ops; 55275ab9eb6SKuninori Morimoto 5531a497983SMengdong Lin rtd->codec_dais = kzalloc(sizeof(struct snd_soc_dai *) * 5541a497983SMengdong Lin dai_link->num_codecs, 5551a497983SMengdong Lin GFP_KERNEL); 5561a497983SMengdong Lin if (!rtd->codec_dais) { 5571a497983SMengdong Lin kfree(rtd); 5581a497983SMengdong Lin return NULL; 5591a497983SMengdong Lin } 5601a497983SMengdong Lin 5611a497983SMengdong Lin return rtd; 5621a497983SMengdong Lin } 5631a497983SMengdong Lin 5641a497983SMengdong Lin static void soc_free_pcm_runtime(struct snd_soc_pcm_runtime *rtd) 5651a497983SMengdong Lin { 5661a497983SMengdong Lin kfree(rtd->codec_dais); 567a0ac4411SKuninori Morimoto snd_soc_rtdcom_del_all(rtd); 5681a497983SMengdong Lin kfree(rtd); 5691a497983SMengdong Lin } 5701a497983SMengdong Lin 5711a497983SMengdong Lin static void soc_add_pcm_runtime(struct snd_soc_card *card, 5721a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd) 5731a497983SMengdong Lin { 5741a497983SMengdong Lin list_add_tail(&rtd->list, &card->rtd_list); 5751a497983SMengdong Lin rtd->num = card->num_rtd; 5761a497983SMengdong Lin card->num_rtd++; 5771a497983SMengdong Lin } 5781a497983SMengdong Lin 5791a497983SMengdong Lin static void soc_remove_pcm_runtimes(struct snd_soc_card *card) 5801a497983SMengdong Lin { 5811a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd, *_rtd; 5821a497983SMengdong Lin 5831a497983SMengdong Lin list_for_each_entry_safe(rtd, _rtd, &card->rtd_list, list) { 5841a497983SMengdong Lin list_del(&rtd->list); 5851a497983SMengdong Lin soc_free_pcm_runtime(rtd); 5861a497983SMengdong Lin } 5871a497983SMengdong Lin 5881a497983SMengdong Lin card->num_rtd = 0; 5891a497983SMengdong Lin } 5901a497983SMengdong Lin 59147c88fffSLiam Girdwood struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card, 59247c88fffSLiam Girdwood const char *dai_link) 59347c88fffSLiam Girdwood { 5941a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd; 59547c88fffSLiam Girdwood 5961a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 5971a497983SMengdong Lin if (!strcmp(rtd->dai_link->name, dai_link)) 5981a497983SMengdong Lin return rtd; 59947c88fffSLiam Girdwood } 600f110bfc7SLiam Girdwood dev_dbg(card->dev, "ASoC: failed to find rtd %s\n", dai_link); 60147c88fffSLiam Girdwood return NULL; 60247c88fffSLiam Girdwood } 60347c88fffSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_get_pcm_runtime); 60447c88fffSLiam Girdwood 6059d58a077SRichard Fitzgerald static void codec2codec_close_delayed_work(struct work_struct *work) 6069d58a077SRichard Fitzgerald { 6079d58a077SRichard Fitzgerald /* Currently nothing to do for c2c links 6089d58a077SRichard Fitzgerald * Since c2c links are internal nodes in the DAPM graph and 6099d58a077SRichard Fitzgerald * don't interface with the outside world or application layer 6109d58a077SRichard Fitzgerald * we don't have to do any special handling on close. 6119d58a077SRichard Fitzgerald */ 6129d58a077SRichard Fitzgerald } 6139d58a077SRichard Fitzgerald 6146f8ab4acSMark Brown #ifdef CONFIG_PM_SLEEP 615db2a4165SFrank Mandarino /* powers down audio subsystem for suspend */ 6166f8ab4acSMark Brown int snd_soc_suspend(struct device *dev) 617db2a4165SFrank Mandarino { 6186f8ab4acSMark Brown struct snd_soc_card *card = dev_get_drvdata(dev); 619d9fc4063SKuninori Morimoto struct snd_soc_component *component; 6201a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd; 6211a497983SMengdong Lin int i; 622db2a4165SFrank Mandarino 623c5599b87SLars-Peter Clausen /* If the card is not initialized yet there is nothing to do */ 624c5599b87SLars-Peter Clausen if (!card->instantiated) 625e3509ff0SDaniel Mack return 0; 626e3509ff0SDaniel Mack 6276ed25978SAndy Green /* Due to the resume being scheduled into a workqueue we could 6286ed25978SAndy Green * suspend before that's finished - wait for it to complete. 6296ed25978SAndy Green */ 630f0fba2adSLiam Girdwood snd_power_wait(card->snd_card, SNDRV_CTL_POWER_D0); 6316ed25978SAndy Green 6326ed25978SAndy Green /* we're going to block userspace touching us until resume completes */ 633f0fba2adSLiam Girdwood snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D3hot); 6346ed25978SAndy Green 635a00f90f9SMark Brown /* mute any active DACs */ 6361a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 6373efab7dcSMark Brown 6381a497983SMengdong Lin if (rtd->dai_link->ignore_suspend) 6393efab7dcSMark Brown continue; 6403efab7dcSMark Brown 6411a497983SMengdong Lin for (i = 0; i < rtd->num_codecs; i++) { 6421a497983SMengdong Lin struct snd_soc_dai *dai = rtd->codec_dais[i]; 64388bd870fSBenoit Cousson struct snd_soc_dai_driver *drv = dai->driver; 64488bd870fSBenoit Cousson 645f0fba2adSLiam Girdwood if (drv->ops->digital_mute && dai->playback_active) 646f0fba2adSLiam Girdwood drv->ops->digital_mute(dai, 1); 647db2a4165SFrank Mandarino } 64888bd870fSBenoit Cousson } 649db2a4165SFrank Mandarino 6504ccab3e7SLiam Girdwood /* suspend all pcms */ 6511a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 6521a497983SMengdong Lin if (rtd->dai_link->ignore_suspend) 6533efab7dcSMark Brown continue; 6543efab7dcSMark Brown 6551a497983SMengdong Lin snd_pcm_suspend_all(rtd->pcm); 6563efab7dcSMark Brown } 6574ccab3e7SLiam Girdwood 65887506549SMark Brown if (card->suspend_pre) 65970b2ac12SMark Brown card->suspend_pre(card); 660db2a4165SFrank Mandarino 6611a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 6621a497983SMengdong Lin struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 6633efab7dcSMark Brown 6641a497983SMengdong Lin if (rtd->dai_link->ignore_suspend) 6653efab7dcSMark Brown continue; 6663efab7dcSMark Brown 667bc263214SLars-Peter Clausen if (cpu_dai->driver->suspend && !cpu_dai->driver->bus_control) 668f0fba2adSLiam Girdwood cpu_dai->driver->suspend(cpu_dai); 669db2a4165SFrank Mandarino } 670db2a4165SFrank Mandarino 67137660b6dSLars-Peter Clausen /* close any waiting streams */ 6721a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) 6731a497983SMengdong Lin flush_delayed_work(&rtd->delayed_work); 674db2a4165SFrank Mandarino 6751a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 6763efab7dcSMark Brown 6771a497983SMengdong Lin if (rtd->dai_link->ignore_suspend) 6783efab7dcSMark Brown continue; 6793efab7dcSMark Brown 6801a497983SMengdong Lin snd_soc_dapm_stream_event(rtd, 6817bd3a6f3SMark Brown SNDRV_PCM_STREAM_PLAYBACK, 682db2a4165SFrank Mandarino SND_SOC_DAPM_STREAM_SUSPEND); 683f0fba2adSLiam Girdwood 6841a497983SMengdong Lin snd_soc_dapm_stream_event(rtd, 6857bd3a6f3SMark Brown SNDRV_PCM_STREAM_CAPTURE, 686db2a4165SFrank Mandarino SND_SOC_DAPM_STREAM_SUSPEND); 687db2a4165SFrank Mandarino } 688db2a4165SFrank Mandarino 6898be4da29SLars-Peter Clausen /* Recheck all endpoints too, their state is affected by suspend */ 6908be4da29SLars-Peter Clausen dapm_mark_endpoints_dirty(card); 691e2d32ff6SMark Brown snd_soc_dapm_sync(&card->dapm); 692e2d32ff6SMark Brown 6939178feb4SKuninori Morimoto /* suspend all COMPONENTs */ 694d9fc4063SKuninori Morimoto list_for_each_entry(component, &card->component_dev_list, card_list) { 695d9fc4063SKuninori Morimoto struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); 696d9fc4063SKuninori Morimoto 6979178feb4SKuninori Morimoto /* If there are paths active then the COMPONENT will be held with 6981547aba9SMark Brown * bias _ON and should not be suspended. */ 6999178feb4SKuninori Morimoto if (!component->suspended) { 7004890140fSLars-Peter Clausen switch (snd_soc_dapm_get_bias_level(dapm)) { 7011547aba9SMark Brown case SND_SOC_BIAS_STANDBY: 702125a25daSMark Brown /* 7039178feb4SKuninori Morimoto * If the COMPONENT is capable of idle 704125a25daSMark Brown * bias off then being in STANDBY 705125a25daSMark Brown * means it's doing something, 706125a25daSMark Brown * otherwise fall through. 707125a25daSMark Brown */ 7084890140fSLars-Peter Clausen if (dapm->idle_bias_off) { 7099178feb4SKuninori Morimoto dev_dbg(component->dev, 71010e8aa9aSMichał Mirosław "ASoC: idle_bias_off CODEC on over suspend\n"); 711125a25daSMark Brown break; 712125a25daSMark Brown } 713a8093297SLars-Peter Clausen 7141547aba9SMark Brown case SND_SOC_BIAS_OFF: 7159178feb4SKuninori Morimoto if (component->suspend) 7169178feb4SKuninori Morimoto component->suspend(component); 7179178feb4SKuninori Morimoto component->suspended = 1; 7189178feb4SKuninori Morimoto if (component->regmap) 7199178feb4SKuninori Morimoto regcache_mark_dirty(component->regmap); 720988e8cc4SNicolin Chen /* deactivate pins to sleep state */ 7219178feb4SKuninori Morimoto pinctrl_pm_select_sleep_state(component->dev); 7221547aba9SMark Brown break; 7231547aba9SMark Brown default: 7249178feb4SKuninori Morimoto dev_dbg(component->dev, 7259178feb4SKuninori Morimoto "ASoC: COMPONENT is on over suspend\n"); 7261547aba9SMark Brown break; 7271547aba9SMark Brown } 7281547aba9SMark Brown } 729f0fba2adSLiam Girdwood } 730db2a4165SFrank Mandarino 7311a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 7321a497983SMengdong Lin struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 7333efab7dcSMark Brown 7341a497983SMengdong Lin if (rtd->dai_link->ignore_suspend) 7353efab7dcSMark Brown continue; 7363efab7dcSMark Brown 737bc263214SLars-Peter Clausen if (cpu_dai->driver->suspend && cpu_dai->driver->bus_control) 738f0fba2adSLiam Girdwood cpu_dai->driver->suspend(cpu_dai); 739988e8cc4SNicolin Chen 740988e8cc4SNicolin Chen /* deactivate pins to sleep state */ 741988e8cc4SNicolin Chen pinctrl_pm_select_sleep_state(cpu_dai->dev); 742db2a4165SFrank Mandarino } 743db2a4165SFrank Mandarino 74487506549SMark Brown if (card->suspend_post) 74570b2ac12SMark Brown card->suspend_post(card); 746db2a4165SFrank Mandarino 747db2a4165SFrank Mandarino return 0; 748db2a4165SFrank Mandarino } 7496f8ab4acSMark Brown EXPORT_SYMBOL_GPL(snd_soc_suspend); 750db2a4165SFrank Mandarino 7516ed25978SAndy Green /* deferred resume work, so resume can complete before we finished 7526ed25978SAndy Green * setting our codec back up, which can be very slow on I2C 7536ed25978SAndy Green */ 7546ed25978SAndy Green static void soc_resume_deferred(struct work_struct *work) 755db2a4165SFrank Mandarino { 756f0fba2adSLiam Girdwood struct snd_soc_card *card = 757f0fba2adSLiam Girdwood container_of(work, struct snd_soc_card, deferred_resume_work); 7581a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd; 759d9fc4063SKuninori Morimoto struct snd_soc_component *component; 7601a497983SMengdong Lin int i; 761db2a4165SFrank Mandarino 7626ed25978SAndy Green /* our power state is still SNDRV_CTL_POWER_D3hot from suspend time, 7636ed25978SAndy Green * so userspace apps are blocked from touching us 7646ed25978SAndy Green */ 7656ed25978SAndy Green 766f110bfc7SLiam Girdwood dev_dbg(card->dev, "ASoC: starting resume work\n"); 7676ed25978SAndy Green 7689949788bSMark Brown /* Bring us up into D2 so that DAPM starts enabling things */ 769f0fba2adSLiam Girdwood snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D2); 7709949788bSMark Brown 77187506549SMark Brown if (card->resume_pre) 77270b2ac12SMark Brown card->resume_pre(card); 773db2a4165SFrank Mandarino 774bc263214SLars-Peter Clausen /* resume control bus DAIs */ 7751a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 7761a497983SMengdong Lin struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 7773efab7dcSMark Brown 7781a497983SMengdong Lin if (rtd->dai_link->ignore_suspend) 7793efab7dcSMark Brown continue; 7803efab7dcSMark Brown 781bc263214SLars-Peter Clausen if (cpu_dai->driver->resume && cpu_dai->driver->bus_control) 782f0fba2adSLiam Girdwood cpu_dai->driver->resume(cpu_dai); 783db2a4165SFrank Mandarino } 784db2a4165SFrank Mandarino 785d9fc4063SKuninori Morimoto list_for_each_entry(component, &card->component_dev_list, card_list) { 7869178feb4SKuninori Morimoto if (component->suspended) { 7879178feb4SKuninori Morimoto if (component->resume) 7889178feb4SKuninori Morimoto component->resume(component); 7899178feb4SKuninori Morimoto component->suspended = 0; 7901547aba9SMark Brown } 791f0fba2adSLiam Girdwood } 792db2a4165SFrank Mandarino 7931a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 7943efab7dcSMark Brown 7951a497983SMengdong Lin if (rtd->dai_link->ignore_suspend) 7963efab7dcSMark Brown continue; 7973efab7dcSMark Brown 7981a497983SMengdong Lin snd_soc_dapm_stream_event(rtd, 799d9b0951bSLiam Girdwood SNDRV_PCM_STREAM_PLAYBACK, 800db2a4165SFrank Mandarino SND_SOC_DAPM_STREAM_RESUME); 801f0fba2adSLiam Girdwood 8021a497983SMengdong Lin snd_soc_dapm_stream_event(rtd, 803d9b0951bSLiam Girdwood SNDRV_PCM_STREAM_CAPTURE, 804db2a4165SFrank Mandarino SND_SOC_DAPM_STREAM_RESUME); 805db2a4165SFrank Mandarino } 806db2a4165SFrank Mandarino 8073ff3f64bSMark Brown /* unmute any active DACs */ 8081a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 8093efab7dcSMark Brown 8101a497983SMengdong Lin if (rtd->dai_link->ignore_suspend) 8113efab7dcSMark Brown continue; 8123efab7dcSMark Brown 8131a497983SMengdong Lin for (i = 0; i < rtd->num_codecs; i++) { 8141a497983SMengdong Lin struct snd_soc_dai *dai = rtd->codec_dais[i]; 81588bd870fSBenoit Cousson struct snd_soc_dai_driver *drv = dai->driver; 81688bd870fSBenoit Cousson 817f0fba2adSLiam Girdwood if (drv->ops->digital_mute && dai->playback_active) 818f0fba2adSLiam Girdwood drv->ops->digital_mute(dai, 0); 819db2a4165SFrank Mandarino } 82088bd870fSBenoit Cousson } 821db2a4165SFrank Mandarino 8221a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 8231a497983SMengdong Lin struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 8243efab7dcSMark Brown 8251a497983SMengdong Lin if (rtd->dai_link->ignore_suspend) 8263efab7dcSMark Brown continue; 8273efab7dcSMark Brown 828bc263214SLars-Peter Clausen if (cpu_dai->driver->resume && !cpu_dai->driver->bus_control) 829f0fba2adSLiam Girdwood cpu_dai->driver->resume(cpu_dai); 830db2a4165SFrank Mandarino } 831db2a4165SFrank Mandarino 83287506549SMark Brown if (card->resume_post) 83370b2ac12SMark Brown card->resume_post(card); 834db2a4165SFrank Mandarino 835f110bfc7SLiam Girdwood dev_dbg(card->dev, "ASoC: resume work completed\n"); 8366ed25978SAndy Green 8378be4da29SLars-Peter Clausen /* Recheck all endpoints too, their state is affected by suspend */ 8388be4da29SLars-Peter Clausen dapm_mark_endpoints_dirty(card); 839e2d32ff6SMark Brown snd_soc_dapm_sync(&card->dapm); 8401a7aaa58SJeeja KP 8411a7aaa58SJeeja KP /* userspace can access us now we are back as we were before */ 8421a7aaa58SJeeja KP snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D0); 8436ed25978SAndy Green } 8446ed25978SAndy Green 8456ed25978SAndy Green /* powers up audio subsystem after a suspend */ 8466f8ab4acSMark Brown int snd_soc_resume(struct device *dev) 8476ed25978SAndy Green { 8486f8ab4acSMark Brown struct snd_soc_card *card = dev_get_drvdata(dev); 849bc263214SLars-Peter Clausen bool bus_control = false; 8501a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd; 851b9dd94a8SPeter Ujfalusi 852c5599b87SLars-Peter Clausen /* If the card is not initialized yet there is nothing to do */ 853c5599b87SLars-Peter Clausen if (!card->instantiated) 8545ff1ddf2SEric Miao return 0; 8555ff1ddf2SEric Miao 856988e8cc4SNicolin Chen /* activate pins from sleep state */ 8571a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 85888bd870fSBenoit Cousson struct snd_soc_dai **codec_dais = rtd->codec_dais; 85988bd870fSBenoit Cousson struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 86088bd870fSBenoit Cousson int j; 86188bd870fSBenoit Cousson 862988e8cc4SNicolin Chen if (cpu_dai->active) 863988e8cc4SNicolin Chen pinctrl_pm_select_default_state(cpu_dai->dev); 86488bd870fSBenoit Cousson 86588bd870fSBenoit Cousson for (j = 0; j < rtd->num_codecs; j++) { 86688bd870fSBenoit Cousson struct snd_soc_dai *codec_dai = codec_dais[j]; 867988e8cc4SNicolin Chen if (codec_dai->active) 868988e8cc4SNicolin Chen pinctrl_pm_select_default_state(codec_dai->dev); 869988e8cc4SNicolin Chen } 87088bd870fSBenoit Cousson } 871988e8cc4SNicolin Chen 872bc263214SLars-Peter Clausen /* 873bc263214SLars-Peter Clausen * DAIs that also act as the control bus master might have other drivers 874bc263214SLars-Peter Clausen * hanging off them so need to resume immediately. Other drivers don't 875bc263214SLars-Peter Clausen * have that problem and may take a substantial amount of time to resume 87664ab9baaSMark Brown * due to I/O costs and anti-pop so handle them out of line. 87764ab9baaSMark Brown */ 8781a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 8791a497983SMengdong Lin struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 880bc263214SLars-Peter Clausen bus_control |= cpu_dai->driver->bus_control; 88182e14e8bSStephen Warren } 882bc263214SLars-Peter Clausen if (bus_control) { 883bc263214SLars-Peter Clausen dev_dbg(dev, "ASoC: Resuming control bus master immediately\n"); 88464ab9baaSMark Brown soc_resume_deferred(&card->deferred_resume_work); 88564ab9baaSMark Brown } else { 886f110bfc7SLiam Girdwood dev_dbg(dev, "ASoC: Scheduling resume work\n"); 8876308419aSMark Brown if (!schedule_work(&card->deferred_resume_work)) 888f110bfc7SLiam Girdwood dev_err(dev, "ASoC: resume work item may be lost\n"); 889f0fba2adSLiam Girdwood } 8906ed25978SAndy Green 891db2a4165SFrank Mandarino return 0; 892db2a4165SFrank Mandarino } 8936f8ab4acSMark Brown EXPORT_SYMBOL_GPL(snd_soc_resume); 894db2a4165SFrank Mandarino #else 8956f8ab4acSMark Brown #define snd_soc_suspend NULL 8966f8ab4acSMark Brown #define snd_soc_resume NULL 897db2a4165SFrank Mandarino #endif 898db2a4165SFrank Mandarino 89985e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops null_dai_ops = { 90002a06d30SBarry Song }; 90102a06d30SBarry Song 90265d9361fSLars-Peter Clausen static struct snd_soc_component *soc_find_component( 90365d9361fSLars-Peter Clausen const struct device_node *of_node, const char *name) 90412023a9aSMisael Lopez Cruz { 90565d9361fSLars-Peter Clausen struct snd_soc_component *component; 90612023a9aSMisael Lopez Cruz 90734e81ab4SLars-Peter Clausen lockdep_assert_held(&client_mutex); 90834e81ab4SLars-Peter Clausen 90965d9361fSLars-Peter Clausen list_for_each_entry(component, &component_list, list) { 91065d9361fSLars-Peter Clausen if (of_node) { 91165d9361fSLars-Peter Clausen if (component->dev->of_node == of_node) 91265d9361fSLars-Peter Clausen return component; 91365d9361fSLars-Peter Clausen } else if (strcmp(component->name, name) == 0) { 91465d9361fSLars-Peter Clausen return component; 91512023a9aSMisael Lopez Cruz } 91612023a9aSMisael Lopez Cruz } 91712023a9aSMisael Lopez Cruz 91812023a9aSMisael Lopez Cruz return NULL; 91912023a9aSMisael Lopez Cruz } 92012023a9aSMisael Lopez Cruz 921fbb88b5cSMengdong Lin /** 922fbb88b5cSMengdong Lin * snd_soc_find_dai - Find a registered DAI 923fbb88b5cSMengdong Lin * 9244958471bSJeffy Chen * @dlc: name of the DAI or the DAI driver and optional component info to match 925fbb88b5cSMengdong Lin * 926ad61dd30SStephen Boyd * This function will search all registered components and their DAIs to 927fbb88b5cSMengdong Lin * find the DAI of the same name. The component's of_node and name 928fbb88b5cSMengdong Lin * should also match if being specified. 929fbb88b5cSMengdong Lin * 930fbb88b5cSMengdong Lin * Return: pointer of DAI, or NULL if not found. 931fbb88b5cSMengdong Lin */ 932305e9020SMengdong Lin struct snd_soc_dai *snd_soc_find_dai( 93314621c7eSLars-Peter Clausen const struct snd_soc_dai_link_component *dlc) 93412023a9aSMisael Lopez Cruz { 93514621c7eSLars-Peter Clausen struct snd_soc_component *component; 93614621c7eSLars-Peter Clausen struct snd_soc_dai *dai; 9373e0aa8d8SJyri Sarha struct device_node *component_of_node; 93812023a9aSMisael Lopez Cruz 93934e81ab4SLars-Peter Clausen lockdep_assert_held(&client_mutex); 94034e81ab4SLars-Peter Clausen 94114621c7eSLars-Peter Clausen /* Find CPU DAI from registered DAIs*/ 94214621c7eSLars-Peter Clausen list_for_each_entry(component, &component_list, list) { 9433e0aa8d8SJyri Sarha component_of_node = component->dev->of_node; 9443e0aa8d8SJyri Sarha if (!component_of_node && component->dev->parent) 9453e0aa8d8SJyri Sarha component_of_node = component->dev->parent->of_node; 9463e0aa8d8SJyri Sarha 9473e0aa8d8SJyri Sarha if (dlc->of_node && component_of_node != dlc->of_node) 94812023a9aSMisael Lopez Cruz continue; 9491ffae361SLars-Peter Clausen if (dlc->name && strcmp(component->name, dlc->name)) 95012023a9aSMisael Lopez Cruz continue; 95114621c7eSLars-Peter Clausen list_for_each_entry(dai, &component->dai_list, list) { 9524958471bSJeffy Chen if (dlc->dai_name && strcmp(dai->name, dlc->dai_name) 9536a6dafdaSJeffy Chen && (!dai->driver->name 9546a6dafdaSJeffy Chen || strcmp(dai->driver->name, dlc->dai_name))) 95514621c7eSLars-Peter Clausen continue; 95612023a9aSMisael Lopez Cruz 95714621c7eSLars-Peter Clausen return dai; 95812023a9aSMisael Lopez Cruz } 95912023a9aSMisael Lopez Cruz } 96012023a9aSMisael Lopez Cruz 96112023a9aSMisael Lopez Cruz return NULL; 96212023a9aSMisael Lopez Cruz } 963305e9020SMengdong Lin EXPORT_SYMBOL_GPL(snd_soc_find_dai); 96412023a9aSMisael Lopez Cruz 96517fb1755SMengdong Lin 96617fb1755SMengdong Lin /** 96717fb1755SMengdong Lin * snd_soc_find_dai_link - Find a DAI link 96817fb1755SMengdong Lin * 96917fb1755SMengdong Lin * @card: soc card 97017fb1755SMengdong Lin * @id: DAI link ID to match 97117fb1755SMengdong Lin * @name: DAI link name to match, optional 9728abab35fSCharles Keepax * @stream_name: DAI link stream name to match, optional 97317fb1755SMengdong Lin * 97417fb1755SMengdong Lin * This function will search all existing DAI links of the soc card to 97517fb1755SMengdong Lin * find the link of the same ID. Since DAI links may not have their 97617fb1755SMengdong Lin * unique ID, so name and stream name should also match if being 97717fb1755SMengdong Lin * specified. 97817fb1755SMengdong Lin * 97917fb1755SMengdong Lin * Return: pointer of DAI link, or NULL if not found. 98017fb1755SMengdong Lin */ 98117fb1755SMengdong Lin struct snd_soc_dai_link *snd_soc_find_dai_link(struct snd_soc_card *card, 98217fb1755SMengdong Lin int id, const char *name, 98317fb1755SMengdong Lin const char *stream_name) 98417fb1755SMengdong Lin { 98517fb1755SMengdong Lin struct snd_soc_dai_link *link, *_link; 98617fb1755SMengdong Lin 98717fb1755SMengdong Lin lockdep_assert_held(&client_mutex); 98817fb1755SMengdong Lin 98917fb1755SMengdong Lin list_for_each_entry_safe(link, _link, &card->dai_link_list, list) { 99017fb1755SMengdong Lin if (link->id != id) 99117fb1755SMengdong Lin continue; 99217fb1755SMengdong Lin 99317fb1755SMengdong Lin if (name && (!link->name || strcmp(name, link->name))) 99417fb1755SMengdong Lin continue; 99517fb1755SMengdong Lin 99617fb1755SMengdong Lin if (stream_name && (!link->stream_name 99717fb1755SMengdong Lin || strcmp(stream_name, link->stream_name))) 99817fb1755SMengdong Lin continue; 99917fb1755SMengdong Lin 100017fb1755SMengdong Lin return link; 100117fb1755SMengdong Lin } 100217fb1755SMengdong Lin 100317fb1755SMengdong Lin return NULL; 100417fb1755SMengdong Lin } 100517fb1755SMengdong Lin EXPORT_SYMBOL_GPL(snd_soc_find_dai_link); 100617fb1755SMengdong Lin 100749a5ba1cSMengdong Lin static bool soc_is_dai_link_bound(struct snd_soc_card *card, 100849a5ba1cSMengdong Lin struct snd_soc_dai_link *dai_link) 1009db2a4165SFrank Mandarino { 101049a5ba1cSMengdong Lin struct snd_soc_pcm_runtime *rtd; 101149a5ba1cSMengdong Lin 101249a5ba1cSMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 101349a5ba1cSMengdong Lin if (rtd->dai_link == dai_link) 101449a5ba1cSMengdong Lin return true; 101549a5ba1cSMengdong Lin } 101649a5ba1cSMengdong Lin 101749a5ba1cSMengdong Lin return false; 101849a5ba1cSMengdong Lin } 101949a5ba1cSMengdong Lin 10206f2f1ff0SMengdong Lin static int soc_bind_dai_link(struct snd_soc_card *card, 10216f2f1ff0SMengdong Lin struct snd_soc_dai_link *dai_link) 1022db2a4165SFrank Mandarino { 10231a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd; 102488bd870fSBenoit Cousson struct snd_soc_dai_link_component *codecs = dai_link->codecs; 102514621c7eSLars-Peter Clausen struct snd_soc_dai_link_component cpu_dai_component; 102690be711eSKuninori Morimoto struct snd_soc_component *component; 10271a497983SMengdong Lin struct snd_soc_dai **codec_dais; 102806859fcaSCharles Keepax struct device_node *platform_of_node; 1029848dd8beSMark Brown const char *platform_name; 103088bd870fSBenoit Cousson int i; 1031db2a4165SFrank Mandarino 10326f2f1ff0SMengdong Lin dev_dbg(card->dev, "ASoC: binding %s\n", dai_link->name); 10336308419aSMark Brown 103449a5ba1cSMengdong Lin if (soc_is_dai_link_bound(card, dai_link)) { 103549a5ba1cSMengdong Lin dev_dbg(card->dev, "ASoC: dai link %s already bound\n", 103649a5ba1cSMengdong Lin dai_link->name); 103749a5ba1cSMengdong Lin return 0; 103849a5ba1cSMengdong Lin } 1039db2a4165SFrank Mandarino 1040513cb311SSudip Mukherjee rtd = soc_new_pcm_runtime(card, dai_link); 1041513cb311SSudip Mukherjee if (!rtd) 1042513cb311SSudip Mukherjee return -ENOMEM; 1043513cb311SSudip Mukherjee 104414621c7eSLars-Peter Clausen cpu_dai_component.name = dai_link->cpu_name; 104514621c7eSLars-Peter Clausen cpu_dai_component.of_node = dai_link->cpu_of_node; 104614621c7eSLars-Peter Clausen cpu_dai_component.dai_name = dai_link->cpu_dai_name; 104714621c7eSLars-Peter Clausen rtd->cpu_dai = snd_soc_find_dai(&cpu_dai_component); 1048b19e6e7bSMark Brown if (!rtd->cpu_dai) { 10496b490879SMartin Hundebøll dev_info(card->dev, "ASoC: CPU DAI %s not registered\n", 1050f0fba2adSLiam Girdwood dai_link->cpu_dai_name); 10511a497983SMengdong Lin goto _err_defer; 1052c5af3a2eSMark Brown } 105390be711eSKuninori Morimoto snd_soc_rtdcom_add(rtd, rtd->cpu_dai->component); 1054c5af3a2eSMark Brown 105588bd870fSBenoit Cousson rtd->num_codecs = dai_link->num_codecs; 105688bd870fSBenoit Cousson 105788bd870fSBenoit Cousson /* Find CODEC from registered CODECs */ 10581a497983SMengdong Lin codec_dais = rtd->codec_dais; 105988bd870fSBenoit Cousson for (i = 0; i < rtd->num_codecs; i++) { 106014621c7eSLars-Peter Clausen codec_dais[i] = snd_soc_find_dai(&codecs[i]); 106188bd870fSBenoit Cousson if (!codec_dais[i]) { 106212023a9aSMisael Lopez Cruz dev_err(card->dev, "ASoC: CODEC DAI %s not registered\n", 106388bd870fSBenoit Cousson codecs[i].dai_name); 10641a497983SMengdong Lin goto _err_defer; 106512023a9aSMisael Lopez Cruz } 106690be711eSKuninori Morimoto snd_soc_rtdcom_add(rtd, codec_dais[i]->component); 106788bd870fSBenoit Cousson } 106888bd870fSBenoit Cousson 106988bd870fSBenoit Cousson /* Single codec links expect codec and codec_dai in runtime data */ 107088bd870fSBenoit Cousson rtd->codec_dai = codec_dais[0]; 107188bd870fSBenoit Cousson rtd->codec = rtd->codec_dai->codec; 107212023a9aSMisael Lopez Cruz 1073848dd8beSMark Brown /* if there's no platform we match on the empty platform */ 1074848dd8beSMark Brown platform_name = dai_link->platform_name; 10755a504963SStephen Warren if (!platform_name && !dai_link->platform_of_node) 1076848dd8beSMark Brown platform_name = "snd-soc-dummy"; 1077848dd8beSMark Brown 1078b19e6e7bSMark Brown /* find one from the set of registered platforms */ 107990be711eSKuninori Morimoto list_for_each_entry(component, &component_list, list) { 108090be711eSKuninori Morimoto platform_of_node = component->dev->of_node; 108190be711eSKuninori Morimoto if (!platform_of_node && component->dev->parent->of_node) 108290be711eSKuninori Morimoto platform_of_node = component->dev->parent->of_node; 108390be711eSKuninori Morimoto 108490be711eSKuninori Morimoto if (dai_link->platform_of_node) { 108590be711eSKuninori Morimoto if (platform_of_node != dai_link->platform_of_node) 108690be711eSKuninori Morimoto continue; 108790be711eSKuninori Morimoto } else { 108890be711eSKuninori Morimoto if (strcmp(component->name, platform_name)) 108990be711eSKuninori Morimoto continue; 109090be711eSKuninori Morimoto } 109190be711eSKuninori Morimoto 109290be711eSKuninori Morimoto snd_soc_rtdcom_add(rtd, component); 109390be711eSKuninori Morimoto } 109490be711eSKuninori Morimoto 10951a497983SMengdong Lin soc_add_pcm_runtime(card, rtd); 1096b19e6e7bSMark Brown return 0; 10971a497983SMengdong Lin 10981a497983SMengdong Lin _err_defer: 10991a497983SMengdong Lin soc_free_pcm_runtime(rtd); 11001a497983SMengdong Lin return -EPROBE_DEFER; 11016b05eda6SMark Brown } 11026b05eda6SMark Brown 1103f1d45cc3SLars-Peter Clausen static void soc_remove_component(struct snd_soc_component *component) 1104d12cd198SStephen Warren { 1105abd31b32SLars-Peter Clausen if (!component->card) 110670090bbbSLars-Peter Clausen return; 1107d12cd198SStephen Warren 1108d9fc4063SKuninori Morimoto list_del(&component->card_list); 1109d12cd198SStephen Warren 1110f1d45cc3SLars-Peter Clausen if (component->remove) 1111f1d45cc3SLars-Peter Clausen component->remove(component); 1112d12cd198SStephen Warren 1113f1d45cc3SLars-Peter Clausen snd_soc_dapm_free(snd_soc_component_get_dapm(component)); 1114d12cd198SStephen Warren 1115f1d45cc3SLars-Peter Clausen soc_cleanup_component_debugfs(component); 1116abd31b32SLars-Peter Clausen component->card = NULL; 1117f1d45cc3SLars-Peter Clausen module_put(component->dev->driver->owner); 1118d12cd198SStephen Warren } 1119d12cd198SStephen Warren 1120e60cd14fSLars-Peter Clausen static void soc_remove_dai(struct snd_soc_dai *dai, int order) 1121589c3563SJarkko Nikula { 1122589c3563SJarkko Nikula int err; 1123589c3563SJarkko Nikula 1124e60cd14fSLars-Peter Clausen if (dai && dai->probed && 1125e60cd14fSLars-Peter Clausen dai->driver->remove_order == order) { 1126e60cd14fSLars-Peter Clausen if (dai->driver->remove) { 1127e60cd14fSLars-Peter Clausen err = dai->driver->remove(dai); 1128589c3563SJarkko Nikula if (err < 0) 1129e60cd14fSLars-Peter Clausen dev_err(dai->dev, 1130b0aa88afSMisael Lopez Cruz "ASoC: failed to remove %s: %d\n", 1131e60cd14fSLars-Peter Clausen dai->name, err); 1132b0aa88afSMisael Lopez Cruz } 1133e60cd14fSLars-Peter Clausen dai->probed = 0; 1134b0aa88afSMisael Lopez Cruz } 1135b0aa88afSMisael Lopez Cruz } 1136b0aa88afSMisael Lopez Cruz 11371a497983SMengdong Lin static void soc_remove_link_dais(struct snd_soc_card *card, 11381a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd, int order) 1139f0fba2adSLiam Girdwood { 1140e60cd14fSLars-Peter Clausen int i; 1141f0fba2adSLiam Girdwood 1142f0fba2adSLiam Girdwood /* unregister the rtd device */ 1143f0fba2adSLiam Girdwood if (rtd->dev_registered) { 114436ae1a96SMark Brown device_unregister(rtd->dev); 1145f0fba2adSLiam Girdwood rtd->dev_registered = 0; 114602a06d30SBarry Song } 114702a06d30SBarry Song 1148f0fba2adSLiam Girdwood /* remove the CODEC DAI */ 114988bd870fSBenoit Cousson for (i = 0; i < rtd->num_codecs; i++) 1150e60cd14fSLars-Peter Clausen soc_remove_dai(rtd->codec_dais[i], order); 1151f0fba2adSLiam Girdwood 1152e60cd14fSLars-Peter Clausen soc_remove_dai(rtd->cpu_dai, order); 1153f0fba2adSLiam Girdwood } 1154f0fba2adSLiam Girdwood 11551a497983SMengdong Lin static void soc_remove_link_components(struct snd_soc_card *card, 11561a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd, int order) 115762ae68faSStephen Warren { 115861aca564SLars-Peter Clausen struct snd_soc_component *component; 115990be711eSKuninori Morimoto struct snd_soc_rtdcom_list *rtdcom; 116062ae68faSStephen Warren 116190be711eSKuninori Morimoto for_each_rtdcom(rtd, rtdcom) { 116290be711eSKuninori Morimoto component = rtdcom->component; 116362ae68faSStephen Warren 116470090bbbSLars-Peter Clausen if (component->driver->remove_order == order) 116561aca564SLars-Peter Clausen soc_remove_component(component); 116662ae68faSStephen Warren } 116762ae68faSStephen Warren } 116862ae68faSStephen Warren 11690671fd8eSKuninori Morimoto static void soc_remove_dai_links(struct snd_soc_card *card) 11700671fd8eSKuninori Morimoto { 11711a497983SMengdong Lin int order; 11721a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd; 1173f8f80361SMengdong Lin struct snd_soc_dai_link *link, *_link; 11740671fd8eSKuninori Morimoto 11750168bf0dSLiam Girdwood for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; 11760168bf0dSLiam Girdwood order++) { 11771a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) 11781a497983SMengdong Lin soc_remove_link_dais(card, rtd, order); 11790168bf0dSLiam Girdwood } 118062ae68faSStephen Warren 118162ae68faSStephen Warren for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; 118262ae68faSStephen Warren order++) { 11831a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) 11841a497983SMengdong Lin soc_remove_link_components(card, rtd, order); 118562ae68faSStephen Warren } 118662ae68faSStephen Warren 1187f8f80361SMengdong Lin list_for_each_entry_safe(link, _link, &card->dai_link_list, list) { 1188f8f80361SMengdong Lin if (link->dobj.type == SND_SOC_DOBJ_DAI_LINK) 1189f8f80361SMengdong Lin dev_warn(card->dev, "Topology forgot to remove link %s?\n", 1190f8f80361SMengdong Lin link->name); 1191f8f80361SMengdong Lin 1192f8f80361SMengdong Lin list_del(&link->list); 1193f8f80361SMengdong Lin card->num_dai_links--; 11940671fd8eSKuninori Morimoto } 11950671fd8eSKuninori Morimoto } 11960671fd8eSKuninori Morimoto 1197923c5e61SMengdong Lin static int snd_soc_init_multicodec(struct snd_soc_card *card, 1198923c5e61SMengdong Lin struct snd_soc_dai_link *dai_link) 1199923c5e61SMengdong Lin { 1200923c5e61SMengdong Lin /* Legacy codec/codec_dai link is a single entry in multicodec */ 1201923c5e61SMengdong Lin if (dai_link->codec_name || dai_link->codec_of_node || 1202923c5e61SMengdong Lin dai_link->codec_dai_name) { 1203923c5e61SMengdong Lin dai_link->num_codecs = 1; 1204923c5e61SMengdong Lin 1205923c5e61SMengdong Lin dai_link->codecs = devm_kzalloc(card->dev, 1206923c5e61SMengdong Lin sizeof(struct snd_soc_dai_link_component), 1207923c5e61SMengdong Lin GFP_KERNEL); 1208923c5e61SMengdong Lin if (!dai_link->codecs) 1209923c5e61SMengdong Lin return -ENOMEM; 1210923c5e61SMengdong Lin 1211923c5e61SMengdong Lin dai_link->codecs[0].name = dai_link->codec_name; 1212923c5e61SMengdong Lin dai_link->codecs[0].of_node = dai_link->codec_of_node; 1213923c5e61SMengdong Lin dai_link->codecs[0].dai_name = dai_link->codec_dai_name; 1214923c5e61SMengdong Lin } 1215923c5e61SMengdong Lin 1216923c5e61SMengdong Lin if (!dai_link->codecs) { 1217923c5e61SMengdong Lin dev_err(card->dev, "ASoC: DAI link has no CODECs\n"); 1218923c5e61SMengdong Lin return -EINVAL; 1219923c5e61SMengdong Lin } 1220923c5e61SMengdong Lin 1221923c5e61SMengdong Lin return 0; 1222923c5e61SMengdong Lin } 1223923c5e61SMengdong Lin 1224923c5e61SMengdong Lin static int soc_init_dai_link(struct snd_soc_card *card, 1225923c5e61SMengdong Lin struct snd_soc_dai_link *link) 1226923c5e61SMengdong Lin { 1227923c5e61SMengdong Lin int i, ret; 1228923c5e61SMengdong Lin 1229923c5e61SMengdong Lin ret = snd_soc_init_multicodec(card, link); 1230923c5e61SMengdong Lin if (ret) { 1231923c5e61SMengdong Lin dev_err(card->dev, "ASoC: failed to init multicodec\n"); 1232923c5e61SMengdong Lin return ret; 1233923c5e61SMengdong Lin } 1234923c5e61SMengdong Lin 1235923c5e61SMengdong Lin for (i = 0; i < link->num_codecs; i++) { 1236923c5e61SMengdong Lin /* 1237923c5e61SMengdong Lin * Codec must be specified by 1 of name or OF node, 1238923c5e61SMengdong Lin * not both or neither. 1239923c5e61SMengdong Lin */ 1240923c5e61SMengdong Lin if (!!link->codecs[i].name == 1241923c5e61SMengdong Lin !!link->codecs[i].of_node) { 1242923c5e61SMengdong Lin dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n", 1243923c5e61SMengdong Lin link->name); 1244923c5e61SMengdong Lin return -EINVAL; 1245923c5e61SMengdong Lin } 1246923c5e61SMengdong Lin /* Codec DAI name must be specified */ 1247923c5e61SMengdong Lin if (!link->codecs[i].dai_name) { 1248923c5e61SMengdong Lin dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n", 1249923c5e61SMengdong Lin link->name); 1250923c5e61SMengdong Lin return -EINVAL; 1251923c5e61SMengdong Lin } 1252923c5e61SMengdong Lin } 1253923c5e61SMengdong Lin 1254923c5e61SMengdong Lin /* 1255923c5e61SMengdong Lin * Platform may be specified by either name or OF node, but 1256923c5e61SMengdong Lin * can be left unspecified, and a dummy platform will be used. 1257923c5e61SMengdong Lin */ 1258923c5e61SMengdong Lin if (link->platform_name && link->platform_of_node) { 1259923c5e61SMengdong Lin dev_err(card->dev, 1260923c5e61SMengdong Lin "ASoC: Both platform name/of_node are set for %s\n", 1261923c5e61SMengdong Lin link->name); 1262923c5e61SMengdong Lin return -EINVAL; 1263923c5e61SMengdong Lin } 1264923c5e61SMengdong Lin 1265923c5e61SMengdong Lin /* 1266923c5e61SMengdong Lin * CPU device may be specified by either name or OF node, but 1267923c5e61SMengdong Lin * can be left unspecified, and will be matched based on DAI 1268923c5e61SMengdong Lin * name alone.. 1269923c5e61SMengdong Lin */ 1270923c5e61SMengdong Lin if (link->cpu_name && link->cpu_of_node) { 1271923c5e61SMengdong Lin dev_err(card->dev, 1272923c5e61SMengdong Lin "ASoC: Neither/both cpu name/of_node are set for %s\n", 1273923c5e61SMengdong Lin link->name); 1274923c5e61SMengdong Lin return -EINVAL; 1275923c5e61SMengdong Lin } 1276923c5e61SMengdong Lin /* 1277923c5e61SMengdong Lin * At least one of CPU DAI name or CPU device name/node must be 1278923c5e61SMengdong Lin * specified 1279923c5e61SMengdong Lin */ 1280923c5e61SMengdong Lin if (!link->cpu_dai_name && 1281923c5e61SMengdong Lin !(link->cpu_name || link->cpu_of_node)) { 1282923c5e61SMengdong Lin dev_err(card->dev, 1283923c5e61SMengdong Lin "ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n", 1284923c5e61SMengdong Lin link->name); 1285923c5e61SMengdong Lin return -EINVAL; 1286923c5e61SMengdong Lin } 1287923c5e61SMengdong Lin 1288923c5e61SMengdong Lin return 0; 1289923c5e61SMengdong Lin } 1290923c5e61SMengdong Lin 1291ef2e8175SKuninori Morimoto void snd_soc_disconnect_sync(struct device *dev) 1292ef2e8175SKuninori Morimoto { 1293ef2e8175SKuninori Morimoto struct snd_soc_component *component = snd_soc_lookup_component(dev, NULL); 1294ef2e8175SKuninori Morimoto 1295ef2e8175SKuninori Morimoto if (!component || !component->card) 1296ef2e8175SKuninori Morimoto return; 1297ef2e8175SKuninori Morimoto 1298ef2e8175SKuninori Morimoto snd_card_disconnect_sync(component->card->snd_card); 1299ef2e8175SKuninori Morimoto } 1300df532185SKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_disconnect_sync); 1301ef2e8175SKuninori Morimoto 1302f8f80361SMengdong Lin /** 1303f8f80361SMengdong Lin * snd_soc_add_dai_link - Add a DAI link dynamically 1304f8f80361SMengdong Lin * @card: The ASoC card to which the DAI link is added 1305f8f80361SMengdong Lin * @dai_link: The new DAI link to add 1306f8f80361SMengdong Lin * 1307f8f80361SMengdong Lin * This function adds a DAI link to the ASoC card's link list. 1308f8f80361SMengdong Lin * 1309f8f80361SMengdong Lin * Note: Topology can use this API to add DAI links when probing the 1310f8f80361SMengdong Lin * topology component. And machine drivers can still define static 1311f8f80361SMengdong Lin * DAI links in dai_link array. 1312f8f80361SMengdong Lin */ 1313f8f80361SMengdong Lin int snd_soc_add_dai_link(struct snd_soc_card *card, 1314f8f80361SMengdong Lin struct snd_soc_dai_link *dai_link) 1315f8f80361SMengdong Lin { 1316f8f80361SMengdong Lin if (dai_link->dobj.type 1317f8f80361SMengdong Lin && dai_link->dobj.type != SND_SOC_DOBJ_DAI_LINK) { 1318f8f80361SMengdong Lin dev_err(card->dev, "Invalid dai link type %d\n", 1319f8f80361SMengdong Lin dai_link->dobj.type); 1320f8f80361SMengdong Lin return -EINVAL; 1321f8f80361SMengdong Lin } 1322f8f80361SMengdong Lin 1323f8f80361SMengdong Lin lockdep_assert_held(&client_mutex); 1324d6f220eaSMengdong Lin /* Notify the machine driver for extra initialization 1325d6f220eaSMengdong Lin * on the link created by topology. 1326d6f220eaSMengdong Lin */ 1327d6f220eaSMengdong Lin if (dai_link->dobj.type && card->add_dai_link) 1328d6f220eaSMengdong Lin card->add_dai_link(card, dai_link); 1329d6f220eaSMengdong Lin 1330f8f80361SMengdong Lin list_add_tail(&dai_link->list, &card->dai_link_list); 1331f8f80361SMengdong Lin card->num_dai_links++; 1332f8f80361SMengdong Lin 1333f8f80361SMengdong Lin return 0; 1334f8f80361SMengdong Lin } 1335f8f80361SMengdong Lin EXPORT_SYMBOL_GPL(snd_soc_add_dai_link); 1336f8f80361SMengdong Lin 1337f8f80361SMengdong Lin /** 1338f8f80361SMengdong Lin * snd_soc_remove_dai_link - Remove a DAI link from the list 1339f8f80361SMengdong Lin * @card: The ASoC card that owns the link 1340f8f80361SMengdong Lin * @dai_link: The DAI link to remove 1341f8f80361SMengdong Lin * 1342f8f80361SMengdong Lin * This function removes a DAI link from the ASoC card's link list. 1343f8f80361SMengdong Lin * 1344f8f80361SMengdong Lin * For DAI links previously added by topology, topology should 1345f8f80361SMengdong Lin * remove them by using the dobj embedded in the link. 1346f8f80361SMengdong Lin */ 1347f8f80361SMengdong Lin void snd_soc_remove_dai_link(struct snd_soc_card *card, 1348f8f80361SMengdong Lin struct snd_soc_dai_link *dai_link) 1349f8f80361SMengdong Lin { 1350f8f80361SMengdong Lin struct snd_soc_dai_link *link, *_link; 1351f8f80361SMengdong Lin 1352f8f80361SMengdong Lin if (dai_link->dobj.type 1353f8f80361SMengdong Lin && dai_link->dobj.type != SND_SOC_DOBJ_DAI_LINK) { 1354f8f80361SMengdong Lin dev_err(card->dev, "Invalid dai link type %d\n", 1355f8f80361SMengdong Lin dai_link->dobj.type); 1356f8f80361SMengdong Lin return; 1357f8f80361SMengdong Lin } 1358f8f80361SMengdong Lin 1359f8f80361SMengdong Lin lockdep_assert_held(&client_mutex); 1360d6f220eaSMengdong Lin /* Notify the machine driver for extra destruction 1361d6f220eaSMengdong Lin * on the link created by topology. 1362d6f220eaSMengdong Lin */ 1363d6f220eaSMengdong Lin if (dai_link->dobj.type && card->remove_dai_link) 1364d6f220eaSMengdong Lin card->remove_dai_link(card, dai_link); 1365d6f220eaSMengdong Lin 1366f8f80361SMengdong Lin list_for_each_entry_safe(link, _link, &card->dai_link_list, list) { 1367f8f80361SMengdong Lin if (link == dai_link) { 1368f8f80361SMengdong Lin list_del(&link->list); 1369f8f80361SMengdong Lin card->num_dai_links--; 1370f8f80361SMengdong Lin return; 1371f8f80361SMengdong Lin } 1372f8f80361SMengdong Lin } 1373f8f80361SMengdong Lin } 1374f8f80361SMengdong Lin EXPORT_SYMBOL_GPL(snd_soc_remove_dai_link); 1375f0fba2adSLiam Girdwood 1376ead9b919SJarkko Nikula static void soc_set_name_prefix(struct snd_soc_card *card, 137794f99c87SLars-Peter Clausen struct snd_soc_component *component) 1378ead9b919SJarkko Nikula { 1379ead9b919SJarkko Nikula int i; 1380ead9b919SJarkko Nikula 1381ff819b83SDimitris Papastamos if (card->codec_conf == NULL) 1382ead9b919SJarkko Nikula return; 1383ead9b919SJarkko Nikula 1384ff819b83SDimitris Papastamos for (i = 0; i < card->num_configs; i++) { 1385ff819b83SDimitris Papastamos struct snd_soc_codec_conf *map = &card->codec_conf[i]; 1386*b24c539bSCharles Keepax struct device_node *component_of_node = component->dev->of_node; 1387*b24c539bSCharles Keepax 1388*b24c539bSCharles Keepax if (!component_of_node && component->dev->parent) 1389*b24c539bSCharles Keepax component_of_node = component->dev->parent->of_node; 1390*b24c539bSCharles Keepax 1391*b24c539bSCharles Keepax if (map->of_node && component_of_node != map->of_node) 13923ca041edSSebastian Reichel continue; 139394f99c87SLars-Peter Clausen if (map->dev_name && strcmp(component->name, map->dev_name)) 13943ca041edSSebastian Reichel continue; 139594f99c87SLars-Peter Clausen component->name_prefix = map->name_prefix; 1396ead9b919SJarkko Nikula break; 1397ead9b919SJarkko Nikula } 1398ead9b919SJarkko Nikula } 1399ead9b919SJarkko Nikula 1400f1d45cc3SLars-Peter Clausen static int soc_probe_component(struct snd_soc_card *card, 1401f1d45cc3SLars-Peter Clausen struct snd_soc_component *component) 1402589c3563SJarkko Nikula { 1403f1d45cc3SLars-Peter Clausen struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); 1404888df395SMark Brown struct snd_soc_dai *dai; 1405f1d45cc3SLars-Peter Clausen int ret; 1406589c3563SJarkko Nikula 14071b7c1231SLars-Peter Clausen if (!strcmp(component->name, "snd-soc-dummy")) 140870090bbbSLars-Peter Clausen return 0; 1409589c3563SJarkko Nikula 1410abd31b32SLars-Peter Clausen if (component->card) { 14111b7c1231SLars-Peter Clausen if (component->card != card) { 14121b7c1231SLars-Peter Clausen dev_err(component->dev, 14131b7c1231SLars-Peter Clausen "Trying to bind component to card \"%s\" but is already bound to card \"%s\"\n", 14141b7c1231SLars-Peter Clausen card->name, component->card->name); 14151b7c1231SLars-Peter Clausen return -ENODEV; 14161b7c1231SLars-Peter Clausen } 14171b7c1231SLars-Peter Clausen return 0; 14181b7c1231SLars-Peter Clausen } 14191b7c1231SLars-Peter Clausen 1420abd31b32SLars-Peter Clausen if (!try_module_get(component->dev->driver->owner)) 1421abd31b32SLars-Peter Clausen return -ENODEV; 1422abd31b32SLars-Peter Clausen 1423f1d45cc3SLars-Peter Clausen component->card = card; 1424f1d45cc3SLars-Peter Clausen dapm->card = card; 1425f1d45cc3SLars-Peter Clausen soc_set_name_prefix(card, component); 1426589c3563SJarkko Nikula 1427f1d45cc3SLars-Peter Clausen soc_init_component_debugfs(component); 1428d5d1e0beSLars-Peter Clausen 1429688d0ebfSKuninori Morimoto if (component->driver->dapm_widgets) { 1430688d0ebfSKuninori Morimoto ret = snd_soc_dapm_new_controls(dapm, 1431688d0ebfSKuninori Morimoto component->driver->dapm_widgets, 1432688d0ebfSKuninori Morimoto component->driver->num_dapm_widgets); 143377530150SLars-Peter Clausen 1434b318ad50SNariman Poushin if (ret != 0) { 1435f1d45cc3SLars-Peter Clausen dev_err(component->dev, 1436b318ad50SNariman Poushin "Failed to create new controls %d\n", ret); 1437b318ad50SNariman Poushin goto err_probe; 1438b318ad50SNariman Poushin } 1439b318ad50SNariman Poushin } 1440b318ad50SNariman Poushin 14410634814fSLars-Peter Clausen list_for_each_entry(dai, &component->dai_list, list) { 14420634814fSLars-Peter Clausen ret = snd_soc_dapm_new_dai_widgets(dapm, dai); 1443261edc70SNariman Poushin if (ret != 0) { 1444f1d45cc3SLars-Peter Clausen dev_err(component->dev, 1445261edc70SNariman Poushin "Failed to create DAI widgets %d\n", ret); 1446261edc70SNariman Poushin goto err_probe; 1447261edc70SNariman Poushin } 1448261edc70SNariman Poushin } 1449888df395SMark Brown 1450f1d45cc3SLars-Peter Clausen if (component->probe) { 1451f1d45cc3SLars-Peter Clausen ret = component->probe(component); 1452589c3563SJarkko Nikula if (ret < 0) { 1453f1d45cc3SLars-Peter Clausen dev_err(component->dev, 1454f1d45cc3SLars-Peter Clausen "ASoC: failed to probe component %d\n", ret); 145570d29331SJarkko Nikula goto err_probe; 1456589c3563SJarkko Nikula } 1457cb2cf612SLiam Girdwood 1458f1d45cc3SLars-Peter Clausen WARN(dapm->idle_bias_off && 1459f1d45cc3SLars-Peter Clausen dapm->bias_level != SND_SOC_BIAS_OFF, 1460956245e9SLiam Girdwood "codec %s can not start from non-off bias with idle_bias_off==1\n", 1461f1d45cc3SLars-Peter Clausen component->name); 1462956245e9SLiam Girdwood } 1463956245e9SLiam Girdwood 1464f2ed6b07SMengdong Lin /* machine specific init */ 1465f2ed6b07SMengdong Lin if (component->init) { 1466f2ed6b07SMengdong Lin ret = component->init(component); 1467f2ed6b07SMengdong Lin if (ret < 0) { 1468f2ed6b07SMengdong Lin dev_err(component->dev, 1469f2ed6b07SMengdong Lin "Failed to do machine specific init %d\n", ret); 1470f2ed6b07SMengdong Lin goto err_probe; 1471f2ed6b07SMengdong Lin } 1472f2ed6b07SMengdong Lin } 1473f2ed6b07SMengdong Lin 1474b8972bf0SKuninori Morimoto if (component->driver->controls) 1475b8972bf0SKuninori Morimoto snd_soc_add_component_controls(component, 1476b8972bf0SKuninori Morimoto component->driver->controls, 1477b8972bf0SKuninori Morimoto component->driver->num_controls); 14786969b2baSKuninori Morimoto if (component->driver->dapm_routes) 14796969b2baSKuninori Morimoto snd_soc_dapm_add_routes(dapm, 14806969b2baSKuninori Morimoto component->driver->dapm_routes, 14816969b2baSKuninori Morimoto component->driver->num_dapm_routes); 1482956245e9SLiam Girdwood 1483f1d45cc3SLars-Peter Clausen list_add(&dapm->list, &card->dapm_list); 1484d9fc4063SKuninori Morimoto list_add(&component->card_list, &card->component_dev_list); 1485956245e9SLiam Girdwood 1486956245e9SLiam Girdwood return 0; 1487956245e9SLiam Girdwood 1488956245e9SLiam Girdwood err_probe: 1489f1d45cc3SLars-Peter Clausen soc_cleanup_component_debugfs(component); 1490abd31b32SLars-Peter Clausen component->card = NULL; 1491f1d45cc3SLars-Peter Clausen module_put(component->dev->driver->owner); 1492956245e9SLiam Girdwood 1493956245e9SLiam Girdwood return ret; 1494956245e9SLiam Girdwood } 1495956245e9SLiam Girdwood 149636ae1a96SMark Brown static void rtd_release(struct device *dev) 149736ae1a96SMark Brown { 149836ae1a96SMark Brown kfree(dev); 149936ae1a96SMark Brown } 1500f0fba2adSLiam Girdwood 15015f3484acSLars-Peter Clausen static int soc_post_component_init(struct snd_soc_pcm_runtime *rtd, 15025f3484acSLars-Peter Clausen const char *name) 1503503ae5e0SMisael Lopez Cruz { 1504589c3563SJarkko Nikula int ret = 0; 1505589c3563SJarkko Nikula 1506589c3563SJarkko Nikula /* register the rtd device */ 150736ae1a96SMark Brown rtd->dev = kzalloc(sizeof(struct device), GFP_KERNEL); 150836ae1a96SMark Brown if (!rtd->dev) 150936ae1a96SMark Brown return -ENOMEM; 151036ae1a96SMark Brown device_initialize(rtd->dev); 15115f3484acSLars-Peter Clausen rtd->dev->parent = rtd->card->dev; 151236ae1a96SMark Brown rtd->dev->release = rtd_release; 1513d29697dcSTakashi Iwai rtd->dev->groups = soc_dev_attr_groups; 1514f294afedSLars-Peter Clausen dev_set_name(rtd->dev, "%s", name); 151536ae1a96SMark Brown dev_set_drvdata(rtd->dev, rtd); 1516b8c0dab9SLiam Girdwood mutex_init(&rtd->pcm_mutex); 151701d7584cSLiam Girdwood INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].be_clients); 151801d7584cSLiam Girdwood INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].be_clients); 151901d7584cSLiam Girdwood INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].fe_clients); 152001d7584cSLiam Girdwood INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].fe_clients); 152136ae1a96SMark Brown ret = device_add(rtd->dev); 1522589c3563SJarkko Nikula if (ret < 0) { 1523865df9cbSChuansheng Liu /* calling put_device() here to free the rtd->dev */ 1524865df9cbSChuansheng Liu put_device(rtd->dev); 15255f3484acSLars-Peter Clausen dev_err(rtd->card->dev, 1526f110bfc7SLiam Girdwood "ASoC: failed to register runtime device: %d\n", ret); 1527589c3563SJarkko Nikula return ret; 1528589c3563SJarkko Nikula } 1529589c3563SJarkko Nikula rtd->dev_registered = 1; 1530589c3563SJarkko Nikula return 0; 1531589c3563SJarkko Nikula } 1532589c3563SJarkko Nikula 15331a497983SMengdong Lin static int soc_probe_link_components(struct snd_soc_card *card, 15341a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd, 153562ae68faSStephen Warren int order) 153662ae68faSStephen Warren { 1537f1d45cc3SLars-Peter Clausen struct snd_soc_component *component; 153890be711eSKuninori Morimoto struct snd_soc_rtdcom_list *rtdcom; 153990be711eSKuninori Morimoto int ret; 154062ae68faSStephen Warren 154190be711eSKuninori Morimoto for_each_rtdcom(rtd, rtdcom) { 154290be711eSKuninori Morimoto component = rtdcom->component; 154390be711eSKuninori Morimoto 154470090bbbSLars-Peter Clausen if (component->driver->probe_order == order) { 1545f1d45cc3SLars-Peter Clausen ret = soc_probe_component(card, component); 154662ae68faSStephen Warren if (ret < 0) 154762ae68faSStephen Warren return ret; 154862ae68faSStephen Warren } 154962ae68faSStephen Warren } 155062ae68faSStephen Warren 155162ae68faSStephen Warren return 0; 155262ae68faSStephen Warren } 155362ae68faSStephen Warren 15548e2be562SLars-Peter Clausen static int soc_probe_dai(struct snd_soc_dai *dai, int order) 1555b0aa88afSMisael Lopez Cruz { 15567a2ccad5SKuninori Morimoto if (dai->probed || 15577a2ccad5SKuninori Morimoto dai->driver->probe_order != order) 15587a2ccad5SKuninori Morimoto return 0; 1559b0aa88afSMisael Lopez Cruz 15608e2be562SLars-Peter Clausen if (dai->driver->probe) { 15617a2ccad5SKuninori Morimoto int ret = dai->driver->probe(dai); 1562b0aa88afSMisael Lopez Cruz if (ret < 0) { 15637a2ccad5SKuninori Morimoto dev_err(dai->dev, "ASoC: failed to probe DAI %s: %d\n", 15648e2be562SLars-Peter Clausen dai->name, ret); 1565b0aa88afSMisael Lopez Cruz return ret; 1566b0aa88afSMisael Lopez Cruz } 1567b0aa88afSMisael Lopez Cruz } 1568b0aa88afSMisael Lopez Cruz 15698e2be562SLars-Peter Clausen dai->probed = 1; 1570b0aa88afSMisael Lopez Cruz 1571b0aa88afSMisael Lopez Cruz return 0; 1572b0aa88afSMisael Lopez Cruz } 1573b0aa88afSMisael Lopez Cruz 157425f7b701SArnaud Pouliquen static int soc_link_dai_pcm_new(struct snd_soc_dai **dais, int num_dais, 157525f7b701SArnaud Pouliquen struct snd_soc_pcm_runtime *rtd) 157625f7b701SArnaud Pouliquen { 157725f7b701SArnaud Pouliquen int i, ret = 0; 157825f7b701SArnaud Pouliquen 157925f7b701SArnaud Pouliquen for (i = 0; i < num_dais; ++i) { 158025f7b701SArnaud Pouliquen struct snd_soc_dai_driver *drv = dais[i]->driver; 158125f7b701SArnaud Pouliquen 158225f7b701SArnaud Pouliquen if (!rtd->dai_link->no_pcm && drv->pcm_new) 158325f7b701SArnaud Pouliquen ret = drv->pcm_new(rtd, dais[i]); 158425f7b701SArnaud Pouliquen if (ret < 0) { 158525f7b701SArnaud Pouliquen dev_err(dais[i]->dev, 158625f7b701SArnaud Pouliquen "ASoC: Failed to bind %s with pcm device\n", 158725f7b701SArnaud Pouliquen dais[i]->name); 158825f7b701SArnaud Pouliquen return ret; 158925f7b701SArnaud Pouliquen } 159025f7b701SArnaud Pouliquen } 159125f7b701SArnaud Pouliquen 159225f7b701SArnaud Pouliquen return 0; 159325f7b701SArnaud Pouliquen } 159425f7b701SArnaud Pouliquen 15952436a723SMisael Lopez Cruz static int soc_link_dai_widgets(struct snd_soc_card *card, 15962436a723SMisael Lopez Cruz struct snd_soc_dai_link *dai_link, 15973f901a02SBenoit Cousson struct snd_soc_pcm_runtime *rtd) 15982436a723SMisael Lopez Cruz { 15993f901a02SBenoit Cousson struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 16003f901a02SBenoit Cousson struct snd_soc_dai *codec_dai = rtd->codec_dai; 16013de7c420SVinod Koul struct snd_soc_dapm_widget *sink, *source; 16022436a723SMisael Lopez Cruz int ret; 16032436a723SMisael Lopez Cruz 160488bd870fSBenoit Cousson if (rtd->num_codecs > 1) 160588bd870fSBenoit Cousson dev_warn(card->dev, "ASoC: Multiple codecs not supported yet\n"); 160688bd870fSBenoit Cousson 16072436a723SMisael Lopez Cruz /* link the DAI widgets */ 16083de7c420SVinod Koul sink = codec_dai->playback_widget; 16093de7c420SVinod Koul source = cpu_dai->capture_widget; 16103de7c420SVinod Koul if (sink && source) { 16112436a723SMisael Lopez Cruz ret = snd_soc_dapm_new_pcm(card, dai_link->params, 16123de7c420SVinod Koul dai_link->num_params, 16133de7c420SVinod Koul source, sink); 16142436a723SMisael Lopez Cruz if (ret != 0) { 16152436a723SMisael Lopez Cruz dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n", 16163de7c420SVinod Koul sink->name, source->name, ret); 16172436a723SMisael Lopez Cruz return ret; 16182436a723SMisael Lopez Cruz } 16192436a723SMisael Lopez Cruz } 16202436a723SMisael Lopez Cruz 16213de7c420SVinod Koul sink = cpu_dai->playback_widget; 16223de7c420SVinod Koul source = codec_dai->capture_widget; 16233de7c420SVinod Koul if (sink && source) { 16242436a723SMisael Lopez Cruz ret = snd_soc_dapm_new_pcm(card, dai_link->params, 16253de7c420SVinod Koul dai_link->num_params, 16263de7c420SVinod Koul source, sink); 16272436a723SMisael Lopez Cruz if (ret != 0) { 16282436a723SMisael Lopez Cruz dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n", 16293de7c420SVinod Koul sink->name, source->name, ret); 16302436a723SMisael Lopez Cruz return ret; 16312436a723SMisael Lopez Cruz } 16322436a723SMisael Lopez Cruz } 16332436a723SMisael Lopez Cruz 16342436a723SMisael Lopez Cruz return 0; 16352436a723SMisael Lopez Cruz } 16362436a723SMisael Lopez Cruz 16371a497983SMengdong Lin static int soc_probe_link_dais(struct snd_soc_card *card, 16381a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd, int order) 1639f0fba2adSLiam Girdwood { 16401a497983SMengdong Lin struct snd_soc_dai_link *dai_link = rtd->dai_link; 1641c74184edSMark Brown struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 1642291bfb92SMark Brown int i, ret; 1643f0fba2adSLiam Girdwood 1644f110bfc7SLiam Girdwood dev_dbg(card->dev, "ASoC: probe %s dai link %d late %d\n", 16451a497983SMengdong Lin card->name, rtd->num, order); 1646f0fba2adSLiam Girdwood 1647f0fba2adSLiam Girdwood /* set default power off timeout */ 1648f0fba2adSLiam Girdwood rtd->pmdown_time = pmdown_time; 1649f0fba2adSLiam Girdwood 16508e2be562SLars-Peter Clausen ret = soc_probe_dai(cpu_dai, order); 16518e2be562SLars-Peter Clausen if (ret) 1652f0fba2adSLiam Girdwood return ret; 1653f0fba2adSLiam Girdwood 1654f0fba2adSLiam Girdwood /* probe the CODEC DAI */ 165588bd870fSBenoit Cousson for (i = 0; i < rtd->num_codecs; i++) { 16568e2be562SLars-Peter Clausen ret = soc_probe_dai(rtd->codec_dais[i], order); 1657b0aa88afSMisael Lopez Cruz if (ret) 1658f0fba2adSLiam Girdwood return ret; 165988bd870fSBenoit Cousson } 1660f0fba2adSLiam Girdwood 16610168bf0dSLiam Girdwood /* complete DAI probe during last probe */ 16620168bf0dSLiam Girdwood if (order != SND_SOC_COMP_ORDER_LAST) 16630168bf0dSLiam Girdwood return 0; 16640168bf0dSLiam Girdwood 16655f3484acSLars-Peter Clausen /* do machine specific initialization */ 16665f3484acSLars-Peter Clausen if (dai_link->init) { 16675f3484acSLars-Peter Clausen ret = dai_link->init(rtd); 16685f3484acSLars-Peter Clausen if (ret < 0) { 16695f3484acSLars-Peter Clausen dev_err(card->dev, "ASoC: failed to init %s: %d\n", 16705f3484acSLars-Peter Clausen dai_link->name, ret); 16715f3484acSLars-Peter Clausen return ret; 16725f3484acSLars-Peter Clausen } 16735f3484acSLars-Peter Clausen } 16745f3484acSLars-Peter Clausen 1675a5053a8eSKuninori Morimoto if (dai_link->dai_fmt) 1676a5053a8eSKuninori Morimoto snd_soc_runtime_set_dai_fmt(rtd, dai_link->dai_fmt); 1677a5053a8eSKuninori Morimoto 16785f3484acSLars-Peter Clausen ret = soc_post_component_init(rtd, dai_link->name); 1679589c3563SJarkko Nikula if (ret) 1680f0fba2adSLiam Girdwood return ret; 1681f0fba2adSLiam Girdwood 16825f3484acSLars-Peter Clausen #ifdef CONFIG_DEBUG_FS 16835f3484acSLars-Peter Clausen /* add DPCM sysfs entries */ 16842e55b90aSLars-Peter Clausen if (dai_link->dynamic) 16852e55b90aSLars-Peter Clausen soc_dpcm_debugfs_add(rtd); 16865f3484acSLars-Peter Clausen #endif 16875f3484acSLars-Peter Clausen 16886f0c4226SJie Yang if (cpu_dai->driver->compress_new) { 16891245b700SNamarta Kohli /*create compress_device"*/ 1690291bfb92SMark Brown ret = cpu_dai->driver->compress_new(rtd, rtd->num); 16911245b700SNamarta Kohli if (ret < 0) { 1692f110bfc7SLiam Girdwood dev_err(card->dev, "ASoC: can't create compress %s\n", 16931245b700SNamarta Kohli dai_link->stream_name); 16941245b700SNamarta Kohli return ret; 16951245b700SNamarta Kohli } 16961245b700SNamarta Kohli } else { 16971245b700SNamarta Kohli 1698c74184edSMark Brown if (!dai_link->params) { 1699f0fba2adSLiam Girdwood /* create the pcm */ 1700291bfb92SMark Brown ret = soc_new_pcm(rtd, rtd->num); 1701f0fba2adSLiam Girdwood if (ret < 0) { 1702f110bfc7SLiam Girdwood dev_err(card->dev, "ASoC: can't create pcm %s :%d\n", 17030837fc62SFabio Estevam dai_link->stream_name, ret); 1704f0fba2adSLiam Girdwood return ret; 1705f0fba2adSLiam Girdwood } 170625f7b701SArnaud Pouliquen ret = soc_link_dai_pcm_new(&cpu_dai, 1, rtd); 170725f7b701SArnaud Pouliquen if (ret < 0) 170825f7b701SArnaud Pouliquen return ret; 170925f7b701SArnaud Pouliquen ret = soc_link_dai_pcm_new(rtd->codec_dais, 171025f7b701SArnaud Pouliquen rtd->num_codecs, rtd); 171125f7b701SArnaud Pouliquen if (ret < 0) 171225f7b701SArnaud Pouliquen return ret; 1713c74184edSMark Brown } else { 17149d58a077SRichard Fitzgerald INIT_DELAYED_WORK(&rtd->delayed_work, 17159d58a077SRichard Fitzgerald codec2codec_close_delayed_work); 17169d58a077SRichard Fitzgerald 1717c74184edSMark Brown /* link the DAI widgets */ 17183f901a02SBenoit Cousson ret = soc_link_dai_widgets(card, dai_link, rtd); 17192436a723SMisael Lopez Cruz if (ret) 1720c74184edSMark Brown return ret; 1721c74184edSMark Brown } 1722c74184edSMark Brown } 1723c74184edSMark Brown 1724f0fba2adSLiam Girdwood return 0; 1725f0fba2adSLiam Girdwood } 1726f0fba2adSLiam Girdwood 172744c69bb1SLars-Peter Clausen static int soc_bind_aux_dev(struct snd_soc_card *card, int num) 1728b19e6e7bSMark Brown { 17293ca041edSSebastian Reichel struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num]; 1730f2ed6b07SMengdong Lin struct snd_soc_component *component; 1731f2ed6b07SMengdong Lin const char *name; 1732f2ed6b07SMengdong Lin struct device_node *codec_of_node; 17333ca041edSSebastian Reichel 1734f2ed6b07SMengdong Lin if (aux_dev->codec_of_node || aux_dev->codec_name) { 1735f2ed6b07SMengdong Lin /* codecs, usually analog devices */ 1736f2ed6b07SMengdong Lin name = aux_dev->codec_name; 1737f2ed6b07SMengdong Lin codec_of_node = aux_dev->codec_of_node; 1738f2ed6b07SMengdong Lin component = soc_find_component(codec_of_node, name); 1739f2ed6b07SMengdong Lin if (!component) { 1740f2ed6b07SMengdong Lin if (codec_of_node) 1741f2ed6b07SMengdong Lin name = of_node_full_name(codec_of_node); 1742f2ed6b07SMengdong Lin goto err_defer; 1743f2ed6b07SMengdong Lin } 1744f2ed6b07SMengdong Lin } else if (aux_dev->name) { 1745f2ed6b07SMengdong Lin /* generic components */ 1746f2ed6b07SMengdong Lin name = aux_dev->name; 1747f2ed6b07SMengdong Lin component = soc_find_component(NULL, name); 1748f2ed6b07SMengdong Lin if (!component) 1749f2ed6b07SMengdong Lin goto err_defer; 1750f2ed6b07SMengdong Lin } else { 1751f2ed6b07SMengdong Lin dev_err(card->dev, "ASoC: Invalid auxiliary device\n"); 1752f2ed6b07SMengdong Lin return -EINVAL; 1753f2ed6b07SMengdong Lin } 17543ca041edSSebastian Reichel 1755f2ed6b07SMengdong Lin component->init = aux_dev->init; 1756d2e3a135SSylwester Nawrocki list_add(&component->card_aux_list, &card->aux_comp_list); 17571a653aa4SKuninori Morimoto 1758f2ed6b07SMengdong Lin return 0; 1759f2ed6b07SMengdong Lin 1760f2ed6b07SMengdong Lin err_defer: 176165d9361fSLars-Peter Clausen dev_err(card->dev, "ASoC: %s not registered\n", name); 1762b19e6e7bSMark Brown return -EPROBE_DEFER; 1763b19e6e7bSMark Brown } 1764b19e6e7bSMark Brown 1765f2ed6b07SMengdong Lin static int soc_probe_aux_devices(struct snd_soc_card *card) 1766f2ed6b07SMengdong Lin { 1767991454e1SKuninori Morimoto struct snd_soc_component *comp; 1768f2ed6b07SMengdong Lin int order; 1769f2ed6b07SMengdong Lin int ret; 1770f2ed6b07SMengdong Lin 1771f2ed6b07SMengdong Lin for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; 1772f2ed6b07SMengdong Lin order++) { 1773991454e1SKuninori Morimoto list_for_each_entry(comp, &card->aux_comp_list, card_aux_list) { 1774f2ed6b07SMengdong Lin if (comp->driver->probe_order == order) { 1775f2ed6b07SMengdong Lin ret = soc_probe_component(card, comp); 1776f2ed6b07SMengdong Lin if (ret < 0) { 1777f2ed6b07SMengdong Lin dev_err(card->dev, 1778f2ed6b07SMengdong Lin "ASoC: failed to probe aux component %s %d\n", 1779f2ed6b07SMengdong Lin comp->name, ret); 1780f2ed6b07SMengdong Lin return ret; 1781f2ed6b07SMengdong Lin } 1782f2ed6b07SMengdong Lin } 1783f2ed6b07SMengdong Lin } 1784f2ed6b07SMengdong Lin } 178565d9361fSLars-Peter Clausen 178644c69bb1SLars-Peter Clausen return 0; 17873ca041edSSebastian Reichel } 17882eea392dSJarkko Nikula 1789f2ed6b07SMengdong Lin static void soc_remove_aux_devices(struct snd_soc_card *card) 179044c69bb1SLars-Peter Clausen { 1791f2ed6b07SMengdong Lin struct snd_soc_component *comp, *_comp; 1792f2ed6b07SMengdong Lin int order; 179344c69bb1SLars-Peter Clausen 1794f2ed6b07SMengdong Lin for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; 1795f2ed6b07SMengdong Lin order++) { 1796f2ed6b07SMengdong Lin list_for_each_entry_safe(comp, _comp, 1797991454e1SKuninori Morimoto &card->aux_comp_list, card_aux_list) { 17981a653aa4SKuninori Morimoto 1799f2ed6b07SMengdong Lin if (comp->driver->remove_order == order) { 1800f2ed6b07SMengdong Lin soc_remove_component(comp); 1801991454e1SKuninori Morimoto /* remove it from the card's aux_comp_list */ 1802991454e1SKuninori Morimoto list_del(&comp->card_aux_list); 18032eea392dSJarkko Nikula } 18045f3484acSLars-Peter Clausen } 18052eea392dSJarkko Nikula } 18062eea392dSJarkko Nikula } 18072eea392dSJarkko Nikula 1808f90fb3f7SLars-Peter Clausen static int snd_soc_init_codec_cache(struct snd_soc_codec *codec) 1809fdf0f54dSDimitris Papastamos { 1810fdf0f54dSDimitris Papastamos int ret; 1811fdf0f54dSDimitris Papastamos 1812fdf0f54dSDimitris Papastamos if (codec->cache_init) 1813fdf0f54dSDimitris Papastamos return 0; 1814fdf0f54dSDimitris Papastamos 1815fdf0f54dSDimitris Papastamos ret = snd_soc_cache_init(codec); 1816fdf0f54dSDimitris Papastamos if (ret < 0) { 181710e8aa9aSMichał Mirosław dev_err(codec->dev, 181810e8aa9aSMichał Mirosław "ASoC: Failed to set cache compression type: %d\n", 181910e8aa9aSMichał Mirosław ret); 1820fdf0f54dSDimitris Papastamos return ret; 1821fdf0f54dSDimitris Papastamos } 1822fdf0f54dSDimitris Papastamos codec->cache_init = 1; 1823fdf0f54dSDimitris Papastamos return 0; 1824fdf0f54dSDimitris Papastamos } 1825fdf0f54dSDimitris Papastamos 1826ce64c8b9SLars-Peter Clausen /** 1827ce64c8b9SLars-Peter Clausen * snd_soc_runtime_set_dai_fmt() - Change DAI link format for a ASoC runtime 1828ce64c8b9SLars-Peter Clausen * @rtd: The runtime for which the DAI link format should be changed 1829ce64c8b9SLars-Peter Clausen * @dai_fmt: The new DAI link format 1830ce64c8b9SLars-Peter Clausen * 1831ce64c8b9SLars-Peter Clausen * This function updates the DAI link format for all DAIs connected to the DAI 1832ce64c8b9SLars-Peter Clausen * link for the specified runtime. 1833ce64c8b9SLars-Peter Clausen * 1834ce64c8b9SLars-Peter Clausen * Note: For setups with a static format set the dai_fmt field in the 1835ce64c8b9SLars-Peter Clausen * corresponding snd_dai_link struct instead of using this function. 1836ce64c8b9SLars-Peter Clausen * 1837ce64c8b9SLars-Peter Clausen * Returns 0 on success, otherwise a negative error code. 1838ce64c8b9SLars-Peter Clausen */ 1839ce64c8b9SLars-Peter Clausen int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd, 1840ce64c8b9SLars-Peter Clausen unsigned int dai_fmt) 1841ce64c8b9SLars-Peter Clausen { 1842ce64c8b9SLars-Peter Clausen struct snd_soc_dai **codec_dais = rtd->codec_dais; 1843ce64c8b9SLars-Peter Clausen struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 1844ce64c8b9SLars-Peter Clausen unsigned int i; 1845ce64c8b9SLars-Peter Clausen int ret; 1846ce64c8b9SLars-Peter Clausen 1847ce64c8b9SLars-Peter Clausen for (i = 0; i < rtd->num_codecs; i++) { 1848ce64c8b9SLars-Peter Clausen struct snd_soc_dai *codec_dai = codec_dais[i]; 1849ce64c8b9SLars-Peter Clausen 1850ce64c8b9SLars-Peter Clausen ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt); 1851ce64c8b9SLars-Peter Clausen if (ret != 0 && ret != -ENOTSUPP) { 1852ce64c8b9SLars-Peter Clausen dev_warn(codec_dai->dev, 1853ce64c8b9SLars-Peter Clausen "ASoC: Failed to set DAI format: %d\n", ret); 1854ce64c8b9SLars-Peter Clausen return ret; 1855ce64c8b9SLars-Peter Clausen } 1856ce64c8b9SLars-Peter Clausen } 1857ce64c8b9SLars-Peter Clausen 1858ce64c8b9SLars-Peter Clausen /* Flip the polarity for the "CPU" end of a CODEC<->CODEC link */ 1859cb2cf0deSKuninori Morimoto /* the component which has non_legacy_dai_naming is Codec */ 1860cb2cf0deSKuninori Morimoto if (cpu_dai->codec || 1861cb2cf0deSKuninori Morimoto cpu_dai->component->driver->non_legacy_dai_naming) { 1862ce64c8b9SLars-Peter Clausen unsigned int inv_dai_fmt; 1863ce64c8b9SLars-Peter Clausen 1864ce64c8b9SLars-Peter Clausen inv_dai_fmt = dai_fmt & ~SND_SOC_DAIFMT_MASTER_MASK; 1865ce64c8b9SLars-Peter Clausen switch (dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) { 1866ce64c8b9SLars-Peter Clausen case SND_SOC_DAIFMT_CBM_CFM: 1867ce64c8b9SLars-Peter Clausen inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; 1868ce64c8b9SLars-Peter Clausen break; 1869ce64c8b9SLars-Peter Clausen case SND_SOC_DAIFMT_CBM_CFS: 1870ce64c8b9SLars-Peter Clausen inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFM; 1871ce64c8b9SLars-Peter Clausen break; 1872ce64c8b9SLars-Peter Clausen case SND_SOC_DAIFMT_CBS_CFM: 1873ce64c8b9SLars-Peter Clausen inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFS; 1874ce64c8b9SLars-Peter Clausen break; 1875ce64c8b9SLars-Peter Clausen case SND_SOC_DAIFMT_CBS_CFS: 1876ce64c8b9SLars-Peter Clausen inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; 1877ce64c8b9SLars-Peter Clausen break; 1878ce64c8b9SLars-Peter Clausen } 1879ce64c8b9SLars-Peter Clausen 1880ce64c8b9SLars-Peter Clausen dai_fmt = inv_dai_fmt; 1881ce64c8b9SLars-Peter Clausen } 1882ce64c8b9SLars-Peter Clausen 1883ce64c8b9SLars-Peter Clausen ret = snd_soc_dai_set_fmt(cpu_dai, dai_fmt); 1884ce64c8b9SLars-Peter Clausen if (ret != 0 && ret != -ENOTSUPP) { 1885ce64c8b9SLars-Peter Clausen dev_warn(cpu_dai->dev, 1886ce64c8b9SLars-Peter Clausen "ASoC: Failed to set DAI format: %d\n", ret); 1887ce64c8b9SLars-Peter Clausen return ret; 1888ce64c8b9SLars-Peter Clausen } 1889ce64c8b9SLars-Peter Clausen 1890ce64c8b9SLars-Peter Clausen return 0; 1891ce64c8b9SLars-Peter Clausen } 1892ddaca25aSLars-Peter Clausen EXPORT_SYMBOL_GPL(snd_soc_runtime_set_dai_fmt); 1893ce64c8b9SLars-Peter Clausen 1894345233d7SLiam Girdwood 18951f5a4535STakashi Iwai #ifdef CONFIG_DMI 1896345233d7SLiam Girdwood /* Trim special characters, and replace '-' with '_' since '-' is used to 1897345233d7SLiam Girdwood * separate different DMI fields in the card long name. Only number and 1898345233d7SLiam Girdwood * alphabet characters and a few separator characters are kept. 1899345233d7SLiam Girdwood */ 1900345233d7SLiam Girdwood static void cleanup_dmi_name(char *name) 1901345233d7SLiam Girdwood { 1902345233d7SLiam Girdwood int i, j = 0; 1903345233d7SLiam Girdwood 1904345233d7SLiam Girdwood for (i = 0; name[i]; i++) { 1905345233d7SLiam Girdwood if (isalnum(name[i]) || (name[i] == '.') 1906345233d7SLiam Girdwood || (name[i] == '_')) 1907345233d7SLiam Girdwood name[j++] = name[i]; 1908345233d7SLiam Girdwood else if (name[i] == '-') 1909345233d7SLiam Girdwood name[j++] = '_'; 1910345233d7SLiam Girdwood } 1911345233d7SLiam Girdwood 1912345233d7SLiam Girdwood name[j] = '\0'; 1913345233d7SLiam Girdwood } 1914345233d7SLiam Girdwood 191598faf436SMengdong Lin /* Check if a DMI field is valid, i.e. not containing any string 191698faf436SMengdong Lin * in the black list. 191798faf436SMengdong Lin */ 191898faf436SMengdong Lin static int is_dmi_valid(const char *field) 191998faf436SMengdong Lin { 192098faf436SMengdong Lin int i = 0; 192198faf436SMengdong Lin 192298faf436SMengdong Lin while (dmi_blacklist[i]) { 192398faf436SMengdong Lin if (strstr(field, dmi_blacklist[i])) 192498faf436SMengdong Lin return 0; 192598faf436SMengdong Lin i++; 192646b5a4d2SWu Fengguang } 192798faf436SMengdong Lin 192898faf436SMengdong Lin return 1; 192998faf436SMengdong Lin } 193098faf436SMengdong Lin 1931345233d7SLiam Girdwood /** 1932345233d7SLiam Girdwood * snd_soc_set_dmi_name() - Register DMI names to card 1933345233d7SLiam Girdwood * @card: The card to register DMI names 1934345233d7SLiam Girdwood * @flavour: The flavour "differentiator" for the card amongst its peers. 1935345233d7SLiam Girdwood * 1936345233d7SLiam Girdwood * An Intel machine driver may be used by many different devices but are 1937345233d7SLiam Girdwood * difficult for userspace to differentiate, since machine drivers ususally 1938345233d7SLiam Girdwood * use their own name as the card short name and leave the card long name 1939345233d7SLiam Girdwood * blank. To differentiate such devices and fix bugs due to lack of 1940345233d7SLiam Girdwood * device-specific configurations, this function allows DMI info to be used 1941345233d7SLiam Girdwood * as the sound card long name, in the format of 1942345233d7SLiam Girdwood * "vendor-product-version-board" 1943345233d7SLiam Girdwood * (Character '-' is used to separate different DMI fields here). 1944345233d7SLiam Girdwood * This will help the user space to load the device-specific Use Case Manager 1945345233d7SLiam Girdwood * (UCM) configurations for the card. 1946345233d7SLiam Girdwood * 1947345233d7SLiam Girdwood * Possible card long names may be: 1948345233d7SLiam Girdwood * DellInc.-XPS139343-01-0310JH 1949345233d7SLiam Girdwood * ASUSTeKCOMPUTERINC.-T100TA-1.0-T100TA 1950345233d7SLiam Girdwood * Circuitco-MinnowboardMaxD0PLATFORM-D0-MinnowBoardMAX 1951345233d7SLiam Girdwood * 1952345233d7SLiam Girdwood * This function also supports flavoring the card longname to provide 1953345233d7SLiam Girdwood * the extra differentiation, like "vendor-product-version-board-flavor". 1954345233d7SLiam Girdwood * 1955345233d7SLiam Girdwood * We only keep number and alphabet characters and a few separator characters 1956345233d7SLiam Girdwood * in the card long name since UCM in the user space uses the card long names 1957345233d7SLiam Girdwood * as card configuration directory names and AudoConf cannot support special 1958345233d7SLiam Girdwood * charactors like SPACE. 1959345233d7SLiam Girdwood * 1960345233d7SLiam Girdwood * Returns 0 on success, otherwise a negative error code. 1961345233d7SLiam Girdwood */ 1962345233d7SLiam Girdwood int snd_soc_set_dmi_name(struct snd_soc_card *card, const char *flavour) 1963345233d7SLiam Girdwood { 1964345233d7SLiam Girdwood const char *vendor, *product, *product_version, *board; 1965345233d7SLiam Girdwood size_t longname_buf_size = sizeof(card->snd_card->longname); 1966345233d7SLiam Girdwood size_t len; 1967345233d7SLiam Girdwood 1968345233d7SLiam Girdwood if (card->long_name) 1969345233d7SLiam Girdwood return 0; /* long name already set by driver or from DMI */ 1970345233d7SLiam Girdwood 1971345233d7SLiam Girdwood /* make up dmi long name as: vendor.product.version.board */ 1972345233d7SLiam Girdwood vendor = dmi_get_system_info(DMI_BOARD_VENDOR); 197398faf436SMengdong Lin if (!vendor || !is_dmi_valid(vendor)) { 1974345233d7SLiam Girdwood dev_warn(card->dev, "ASoC: no DMI vendor name!\n"); 1975345233d7SLiam Girdwood return 0; 1976345233d7SLiam Girdwood } 1977345233d7SLiam Girdwood 197898faf436SMengdong Lin 1979345233d7SLiam Girdwood snprintf(card->dmi_longname, sizeof(card->snd_card->longname), 1980345233d7SLiam Girdwood "%s", vendor); 1981345233d7SLiam Girdwood cleanup_dmi_name(card->dmi_longname); 1982345233d7SLiam Girdwood 1983345233d7SLiam Girdwood product = dmi_get_system_info(DMI_PRODUCT_NAME); 198498faf436SMengdong Lin if (product && is_dmi_valid(product)) { 1985345233d7SLiam Girdwood len = strlen(card->dmi_longname); 1986345233d7SLiam Girdwood snprintf(card->dmi_longname + len, 1987345233d7SLiam Girdwood longname_buf_size - len, 1988345233d7SLiam Girdwood "-%s", product); 1989345233d7SLiam Girdwood 1990345233d7SLiam Girdwood len++; /* skip the separator "-" */ 1991345233d7SLiam Girdwood if (len < longname_buf_size) 1992345233d7SLiam Girdwood cleanup_dmi_name(card->dmi_longname + len); 1993345233d7SLiam Girdwood 1994345233d7SLiam Girdwood /* some vendors like Lenovo may only put a self-explanatory 1995345233d7SLiam Girdwood * name in the product version field 1996345233d7SLiam Girdwood */ 1997345233d7SLiam Girdwood product_version = dmi_get_system_info(DMI_PRODUCT_VERSION); 199898faf436SMengdong Lin if (product_version && is_dmi_valid(product_version)) { 1999345233d7SLiam Girdwood len = strlen(card->dmi_longname); 2000345233d7SLiam Girdwood snprintf(card->dmi_longname + len, 2001345233d7SLiam Girdwood longname_buf_size - len, 2002345233d7SLiam Girdwood "-%s", product_version); 2003345233d7SLiam Girdwood 2004345233d7SLiam Girdwood len++; 2005345233d7SLiam Girdwood if (len < longname_buf_size) 2006345233d7SLiam Girdwood cleanup_dmi_name(card->dmi_longname + len); 2007345233d7SLiam Girdwood } 2008345233d7SLiam Girdwood } 2009345233d7SLiam Girdwood 2010345233d7SLiam Girdwood board = dmi_get_system_info(DMI_BOARD_NAME); 201198faf436SMengdong Lin if (board && is_dmi_valid(board)) { 2012345233d7SLiam Girdwood len = strlen(card->dmi_longname); 2013345233d7SLiam Girdwood snprintf(card->dmi_longname + len, 2014345233d7SLiam Girdwood longname_buf_size - len, 2015345233d7SLiam Girdwood "-%s", board); 2016345233d7SLiam Girdwood 2017345233d7SLiam Girdwood len++; 2018345233d7SLiam Girdwood if (len < longname_buf_size) 2019345233d7SLiam Girdwood cleanup_dmi_name(card->dmi_longname + len); 2020345233d7SLiam Girdwood } else if (!product) { 2021345233d7SLiam Girdwood /* fall back to using legacy name */ 2022345233d7SLiam Girdwood dev_warn(card->dev, "ASoC: no DMI board/product name!\n"); 2023345233d7SLiam Girdwood return 0; 2024345233d7SLiam Girdwood } 2025345233d7SLiam Girdwood 2026345233d7SLiam Girdwood /* Add flavour to dmi long name */ 2027345233d7SLiam Girdwood if (flavour) { 2028345233d7SLiam Girdwood len = strlen(card->dmi_longname); 2029345233d7SLiam Girdwood snprintf(card->dmi_longname + len, 2030345233d7SLiam Girdwood longname_buf_size - len, 2031345233d7SLiam Girdwood "-%s", flavour); 2032345233d7SLiam Girdwood 2033345233d7SLiam Girdwood len++; 2034345233d7SLiam Girdwood if (len < longname_buf_size) 2035345233d7SLiam Girdwood cleanup_dmi_name(card->dmi_longname + len); 2036345233d7SLiam Girdwood } 2037345233d7SLiam Girdwood 2038345233d7SLiam Girdwood /* set the card long name */ 2039345233d7SLiam Girdwood card->long_name = card->dmi_longname; 2040345233d7SLiam Girdwood 2041345233d7SLiam Girdwood return 0; 2042345233d7SLiam Girdwood } 2043345233d7SLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_set_dmi_name); 20441f5a4535STakashi Iwai #endif /* CONFIG_DMI */ 2045345233d7SLiam Girdwood 2046b19e6e7bSMark Brown static int snd_soc_instantiate_card(struct snd_soc_card *card) 2047f0fba2adSLiam Girdwood { 2048fdf0f54dSDimitris Papastamos struct snd_soc_codec *codec; 20491a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd; 205061b0088bSMengdong Lin struct snd_soc_dai_link *dai_link; 2051ce64c8b9SLars-Peter Clausen int ret, i, order; 205296dd3622SMark Brown 205334e81ab4SLars-Peter Clausen mutex_lock(&client_mutex); 205401b9d99aSLiam Girdwood mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT); 2055f0fba2adSLiam Girdwood 2056b19e6e7bSMark Brown /* bind DAIs */ 2057b19e6e7bSMark Brown for (i = 0; i < card->num_links; i++) { 20586f2f1ff0SMengdong Lin ret = soc_bind_dai_link(card, &card->dai_link[i]); 2059b19e6e7bSMark Brown if (ret != 0) 2060b19e6e7bSMark Brown goto base_error; 2061db2a4165SFrank Mandarino } 2062db2a4165SFrank Mandarino 206344c69bb1SLars-Peter Clausen /* bind aux_devs too */ 2064b19e6e7bSMark Brown for (i = 0; i < card->num_aux_devs; i++) { 206544c69bb1SLars-Peter Clausen ret = soc_bind_aux_dev(card, i); 2066b19e6e7bSMark Brown if (ret != 0) 2067b19e6e7bSMark Brown goto base_error; 2068db2a4165SFrank Mandarino } 2069db2a4165SFrank Mandarino 2070f8f80361SMengdong Lin /* add predefined DAI links to the list */ 2071f8f80361SMengdong Lin for (i = 0; i < card->num_links; i++) 2072f8f80361SMengdong Lin snd_soc_add_dai_link(card, card->dai_link+i); 2073f8f80361SMengdong Lin 2074fdf0f54dSDimitris Papastamos /* initialize the register cache for each available codec */ 2075fdf0f54dSDimitris Papastamos list_for_each_entry(codec, &codec_list, list) { 2076fdf0f54dSDimitris Papastamos if (codec->cache_init) 2077fdf0f54dSDimitris Papastamos continue; 2078f90fb3f7SLars-Peter Clausen ret = snd_soc_init_codec_cache(codec); 2079b19e6e7bSMark Brown if (ret < 0) 2080b19e6e7bSMark Brown goto base_error; 2081fdf0f54dSDimitris Papastamos } 2082fdf0f54dSDimitris Papastamos 2083f0fba2adSLiam Girdwood /* card bind complete so register a sound card */ 2084102b5a8dSTakashi Iwai ret = snd_card_new(card->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, 2085f0fba2adSLiam Girdwood card->owner, 0, &card->snd_card); 2086f0fba2adSLiam Girdwood if (ret < 0) { 208710e8aa9aSMichał Mirosław dev_err(card->dev, 208810e8aa9aSMichał Mirosław "ASoC: can't create sound card for card %s: %d\n", 208910e8aa9aSMichał Mirosław card->name, ret); 2090b19e6e7bSMark Brown goto base_error; 2091db2a4165SFrank Mandarino } 2092db2a4165SFrank Mandarino 20930757d834SLars-Peter Clausen soc_init_card_debugfs(card); 20940757d834SLars-Peter Clausen 2095e37a4970SMark Brown card->dapm.bias_level = SND_SOC_BIAS_OFF; 2096e37a4970SMark Brown card->dapm.dev = card->dev; 2097e37a4970SMark Brown card->dapm.card = card; 2098e37a4970SMark Brown list_add(&card->dapm.list, &card->dapm_list); 2099e37a4970SMark Brown 2100d5d1e0beSLars-Peter Clausen #ifdef CONFIG_DEBUG_FS 2101d5d1e0beSLars-Peter Clausen snd_soc_dapm_debugfs_init(&card->dapm, card->debugfs_card_root); 2102d5d1e0beSLars-Peter Clausen #endif 2103d5d1e0beSLars-Peter Clausen 210488ee1c61SMark Brown #ifdef CONFIG_PM_SLEEP 21056ed25978SAndy Green /* deferred resume work */ 21066308419aSMark Brown INIT_WORK(&card->deferred_resume_work, soc_resume_deferred); 21071301a964SRandy Dunlap #endif 21086ed25978SAndy Green 21099a841ebbSMark Brown if (card->dapm_widgets) 21109a841ebbSMark Brown snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets, 21119a841ebbSMark Brown card->num_dapm_widgets); 21129a841ebbSMark Brown 2113f23e860eSNicolin Chen if (card->of_dapm_widgets) 2114f23e860eSNicolin Chen snd_soc_dapm_new_controls(&card->dapm, card->of_dapm_widgets, 2115f23e860eSNicolin Chen card->num_of_dapm_widgets); 2116f23e860eSNicolin Chen 2117f0fba2adSLiam Girdwood /* initialise the sound card only once */ 2118f0fba2adSLiam Girdwood if (card->probe) { 2119e7361ec4SMark Brown ret = card->probe(card); 2120f0fba2adSLiam Girdwood if (ret < 0) 2121f0fba2adSLiam Girdwood goto card_probe_error; 2122f0fba2adSLiam Girdwood } 2123f0fba2adSLiam Girdwood 212462ae68faSStephen Warren /* probe all components used by DAI links on this card */ 21250168bf0dSLiam Girdwood for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; 21260168bf0dSLiam Girdwood order++) { 21271a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 21281a497983SMengdong Lin ret = soc_probe_link_components(card, rtd, order); 212962ae68faSStephen Warren if (ret < 0) { 2130f110bfc7SLiam Girdwood dev_err(card->dev, 2131f110bfc7SLiam Girdwood "ASoC: failed to instantiate card %d\n", 2132f110bfc7SLiam Girdwood ret); 213362ae68faSStephen Warren goto probe_dai_err; 213462ae68faSStephen Warren } 213562ae68faSStephen Warren } 213662ae68faSStephen Warren } 213762ae68faSStephen Warren 2138f2ed6b07SMengdong Lin /* probe auxiliary components */ 2139f2ed6b07SMengdong Lin ret = soc_probe_aux_devices(card); 2140f2ed6b07SMengdong Lin if (ret < 0) 2141f2ed6b07SMengdong Lin goto probe_dai_err; 2142f2ed6b07SMengdong Lin 214361b0088bSMengdong Lin /* Find new DAI links added during probing components and bind them. 214461b0088bSMengdong Lin * Components with topology may bring new DAIs and DAI links. 214561b0088bSMengdong Lin */ 214661b0088bSMengdong Lin list_for_each_entry(dai_link, &card->dai_link_list, list) { 214761b0088bSMengdong Lin if (soc_is_dai_link_bound(card, dai_link)) 214861b0088bSMengdong Lin continue; 214961b0088bSMengdong Lin 215061b0088bSMengdong Lin ret = soc_init_dai_link(card, dai_link); 215161b0088bSMengdong Lin if (ret) 215261b0088bSMengdong Lin goto probe_dai_err; 215361b0088bSMengdong Lin ret = soc_bind_dai_link(card, dai_link); 215461b0088bSMengdong Lin if (ret) 215561b0088bSMengdong Lin goto probe_dai_err; 215661b0088bSMengdong Lin } 215761b0088bSMengdong Lin 215862ae68faSStephen Warren /* probe all DAI links on this card */ 215962ae68faSStephen Warren for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST; 216062ae68faSStephen Warren order++) { 21611a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 21621a497983SMengdong Lin ret = soc_probe_link_dais(card, rtd, order); 2163fe3e78e0SMark Brown if (ret < 0) { 2164f110bfc7SLiam Girdwood dev_err(card->dev, 2165f110bfc7SLiam Girdwood "ASoC: failed to instantiate card %d\n", 2166f110bfc7SLiam Girdwood ret); 2167f0fba2adSLiam Girdwood goto probe_dai_err; 2168fe3e78e0SMark Brown } 2169fe3e78e0SMark Brown } 21700168bf0dSLiam Girdwood } 2171fe3e78e0SMark Brown 2172888df395SMark Brown snd_soc_dapm_link_dai_widgets(card); 2173b893ea5fSLiam Girdwood snd_soc_dapm_connect_dai_link_widgets(card); 2174888df395SMark Brown 2175b7af1dafSMark Brown if (card->controls) 2176022658beSLiam Girdwood snd_soc_add_card_controls(card, card->controls, card->num_controls); 2177b7af1dafSMark Brown 2178b8ad29deSMark Brown if (card->dapm_routes) 2179b8ad29deSMark Brown snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes, 2180b8ad29deSMark Brown card->num_dapm_routes); 2181b8ad29deSMark Brown 2182f23e860eSNicolin Chen if (card->of_dapm_routes) 2183f23e860eSNicolin Chen snd_soc_dapm_add_routes(&card->dapm, card->of_dapm_routes, 2184f23e860eSNicolin Chen card->num_of_dapm_routes); 218575d9ac46SMark Brown 2186861886d3STakashi Iwai /* try to set some sane longname if DMI is available */ 2187861886d3STakashi Iwai snd_soc_set_dmi_name(card, NULL); 2188861886d3STakashi Iwai 2189f0fba2adSLiam Girdwood snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname), 2190fe3e78e0SMark Brown "%s", card->name); 2191f0fba2adSLiam Girdwood snprintf(card->snd_card->longname, sizeof(card->snd_card->longname), 219222de71baSLiam Girdwood "%s", card->long_name ? card->long_name : card->name); 2193f0e8ed85SMark Brown snprintf(card->snd_card->driver, sizeof(card->snd_card->driver), 2194f0e8ed85SMark Brown "%s", card->driver_name ? card->driver_name : card->name); 2195f0e8ed85SMark Brown for (i = 0; i < ARRAY_SIZE(card->snd_card->driver); i++) { 2196f0e8ed85SMark Brown switch (card->snd_card->driver[i]) { 2197f0e8ed85SMark Brown case '_': 2198f0e8ed85SMark Brown case '-': 2199f0e8ed85SMark Brown case '\0': 2200f0e8ed85SMark Brown break; 2201f0e8ed85SMark Brown default: 2202f0e8ed85SMark Brown if (!isalnum(card->snd_card->driver[i])) 2203f0e8ed85SMark Brown card->snd_card->driver[i] = '_'; 2204f0e8ed85SMark Brown break; 2205f0e8ed85SMark Brown } 2206f0e8ed85SMark Brown } 2207fe3e78e0SMark Brown 220828e9ad92SMark Brown if (card->late_probe) { 220928e9ad92SMark Brown ret = card->late_probe(card); 221028e9ad92SMark Brown if (ret < 0) { 2211f110bfc7SLiam Girdwood dev_err(card->dev, "ASoC: %s late_probe() failed: %d\n", 221228e9ad92SMark Brown card->name, ret); 221328e9ad92SMark Brown goto probe_aux_dev_err; 221428e9ad92SMark Brown } 221528e9ad92SMark Brown } 221628e9ad92SMark Brown 2217824ef826SLars-Peter Clausen snd_soc_dapm_new_widgets(card); 22188c193b8dSLars-Peter Clausen 2219f0fba2adSLiam Girdwood ret = snd_card_register(card->snd_card); 2220fe3e78e0SMark Brown if (ret < 0) { 2221f110bfc7SLiam Girdwood dev_err(card->dev, "ASoC: failed to register soundcard %d\n", 2222f110bfc7SLiam Girdwood ret); 22236b3ed785SAxel Lin goto probe_aux_dev_err; 2224fe3e78e0SMark Brown } 2225fe3e78e0SMark Brown 2226435c5e25SMark Brown card->instantiated = 1; 22274f4c0072SMark Brown snd_soc_dapm_sync(&card->dapm); 2228f0fba2adSLiam Girdwood mutex_unlock(&card->mutex); 222934e81ab4SLars-Peter Clausen mutex_unlock(&client_mutex); 2230b19e6e7bSMark Brown 2231b19e6e7bSMark Brown return 0; 2232db2a4165SFrank Mandarino 22332eea392dSJarkko Nikula probe_aux_dev_err: 2234f2ed6b07SMengdong Lin soc_remove_aux_devices(card); 22352eea392dSJarkko Nikula 2236f0fba2adSLiam Girdwood probe_dai_err: 22370671fd8eSKuninori Morimoto soc_remove_dai_links(card); 2238fe3e78e0SMark Brown 2239f0fba2adSLiam Girdwood card_probe_error: 224087506549SMark Brown if (card->remove) 2241e7361ec4SMark Brown card->remove(card); 2242f0fba2adSLiam Girdwood 22432210438bSLars-Peter Clausen snd_soc_dapm_free(&card->dapm); 22440757d834SLars-Peter Clausen soc_cleanup_card_debugfs(card); 2245f0fba2adSLiam Girdwood snd_card_free(card->snd_card); 2246f0fba2adSLiam Girdwood 2247b19e6e7bSMark Brown base_error: 22481a497983SMengdong Lin soc_remove_pcm_runtimes(card); 2249f0fba2adSLiam Girdwood mutex_unlock(&card->mutex); 225034e81ab4SLars-Peter Clausen mutex_unlock(&client_mutex); 2251db2a4165SFrank Mandarino 2252b19e6e7bSMark Brown return ret; 2253435c5e25SMark Brown } 2254435c5e25SMark Brown 2255435c5e25SMark Brown /* probes a new socdev */ 2256435c5e25SMark Brown static int soc_probe(struct platform_device *pdev) 2257435c5e25SMark Brown { 2258f0fba2adSLiam Girdwood struct snd_soc_card *card = platform_get_drvdata(pdev); 2259435c5e25SMark Brown 226070a7ca34SVinod Koul /* 226170a7ca34SVinod Koul * no card, so machine driver should be registering card 226270a7ca34SVinod Koul * we should not be here in that case so ret error 226370a7ca34SVinod Koul */ 226470a7ca34SVinod Koul if (!card) 226570a7ca34SVinod Koul return -EINVAL; 226670a7ca34SVinod Koul 2267fe4085e8SMark Brown dev_warn(&pdev->dev, 2268f110bfc7SLiam Girdwood "ASoC: machine %s should use snd_soc_register_card()\n", 2269fe4085e8SMark Brown card->name); 2270fe4085e8SMark Brown 2271435c5e25SMark Brown /* Bodge while we unpick instantiation */ 2272435c5e25SMark Brown card->dev = &pdev->dev; 2273f0fba2adSLiam Girdwood 227428d528c8SMark Brown return snd_soc_register_card(card); 2275435c5e25SMark Brown } 2276435c5e25SMark Brown 2277b0e26485SVinod Koul static int soc_cleanup_card_resources(struct snd_soc_card *card) 2278db2a4165SFrank Mandarino { 22791a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd; 2280db2a4165SFrank Mandarino 2281f0fba2adSLiam Girdwood /* make sure any delayed work runs */ 22821a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) 228343829731STejun Heo flush_delayed_work(&rtd->delayed_work); 2284db2a4165SFrank Mandarino 22854efda5f2STakashi Iwai /* free the ALSA card at first; this syncs with pending operations */ 22864efda5f2STakashi Iwai snd_card_free(card->snd_card); 22874efda5f2STakashi Iwai 2288f0fba2adSLiam Girdwood /* remove and free each DAI */ 22890671fd8eSKuninori Morimoto soc_remove_dai_links(card); 22901a497983SMengdong Lin soc_remove_pcm_runtimes(card); 2291f0fba2adSLiam Girdwood 2292f2ed6b07SMengdong Lin /* remove auxiliary devices */ 2293f2ed6b07SMengdong Lin soc_remove_aux_devices(card); 2294f2ed6b07SMengdong Lin 2295d1e81428SMark Brown snd_soc_dapm_free(&card->dapm); 2296a6052154SJarkko Nikula soc_cleanup_card_debugfs(card); 2297a6052154SJarkko Nikula 2298f0fba2adSLiam Girdwood /* remove the card */ 229987506549SMark Brown if (card->remove) 2300e7361ec4SMark Brown card->remove(card); 2301f0fba2adSLiam Girdwood 2302b0e26485SVinod Koul return 0; 2303b2dfa62cSGuennadi Liakhovetski } 2304b0e26485SVinod Koul 2305b0e26485SVinod Koul /* removes a socdev */ 2306b0e26485SVinod Koul static int soc_remove(struct platform_device *pdev) 2307b0e26485SVinod Koul { 2308b0e26485SVinod Koul struct snd_soc_card *card = platform_get_drvdata(pdev); 2309b0e26485SVinod Koul 2310c5af3a2eSMark Brown snd_soc_unregister_card(card); 2311db2a4165SFrank Mandarino return 0; 2312db2a4165SFrank Mandarino } 2313db2a4165SFrank Mandarino 23146f8ab4acSMark Brown int snd_soc_poweroff(struct device *dev) 231551737470SMark Brown { 23166f8ab4acSMark Brown struct snd_soc_card *card = dev_get_drvdata(dev); 23171a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd; 231851737470SMark Brown 231951737470SMark Brown if (!card->instantiated) 2320416356fcSMark Brown return 0; 232151737470SMark Brown 232251737470SMark Brown /* Flush out pmdown_time work - we actually do want to run it 232351737470SMark Brown * now, we're shutting down so no imminent restart. */ 23241a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) 232543829731STejun Heo flush_delayed_work(&rtd->delayed_work); 232651737470SMark Brown 2327f0fba2adSLiam Girdwood snd_soc_dapm_shutdown(card); 2328416356fcSMark Brown 2329988e8cc4SNicolin Chen /* deactivate pins to sleep state */ 23301a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 233188bd870fSBenoit Cousson struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 23321a497983SMengdong Lin int i; 233388bd870fSBenoit Cousson 2334988e8cc4SNicolin Chen pinctrl_pm_select_sleep_state(cpu_dai->dev); 23351a497983SMengdong Lin for (i = 0; i < rtd->num_codecs; i++) { 23361a497983SMengdong Lin struct snd_soc_dai *codec_dai = rtd->codec_dais[i]; 233788bd870fSBenoit Cousson pinctrl_pm_select_sleep_state(codec_dai->dev); 233888bd870fSBenoit Cousson } 2339988e8cc4SNicolin Chen } 2340988e8cc4SNicolin Chen 2341416356fcSMark Brown return 0; 234251737470SMark Brown } 23436f8ab4acSMark Brown EXPORT_SYMBOL_GPL(snd_soc_poweroff); 234451737470SMark Brown 23456f8ab4acSMark Brown const struct dev_pm_ops snd_soc_pm_ops = { 2346b1dd5897SViresh Kumar .suspend = snd_soc_suspend, 2347b1dd5897SViresh Kumar .resume = snd_soc_resume, 2348b1dd5897SViresh Kumar .freeze = snd_soc_suspend, 2349b1dd5897SViresh Kumar .thaw = snd_soc_resume, 23506f8ab4acSMark Brown .poweroff = snd_soc_poweroff, 2351b1dd5897SViresh Kumar .restore = snd_soc_resume, 2352416356fcSMark Brown }; 2353deb2607eSStephen Warren EXPORT_SYMBOL_GPL(snd_soc_pm_ops); 2354416356fcSMark Brown 2355db2a4165SFrank Mandarino /* ASoC platform driver */ 2356db2a4165SFrank Mandarino static struct platform_driver soc_driver = { 2357db2a4165SFrank Mandarino .driver = { 2358db2a4165SFrank Mandarino .name = "soc-audio", 23596f8ab4acSMark Brown .pm = &snd_soc_pm_ops, 2360db2a4165SFrank Mandarino }, 2361db2a4165SFrank Mandarino .probe = soc_probe, 2362db2a4165SFrank Mandarino .remove = soc_remove, 2363db2a4165SFrank Mandarino }; 2364db2a4165SFrank Mandarino 2365096e49d5SMark Brown /** 2366db2a4165SFrank Mandarino * snd_soc_cnew - create new control 2367db2a4165SFrank Mandarino * @_template: control template 2368db2a4165SFrank Mandarino * @data: control private data 2369ac11a2b3SMark Brown * @long_name: control long name 2370efb7ac3fSMark Brown * @prefix: control name prefix 2371db2a4165SFrank Mandarino * 2372db2a4165SFrank Mandarino * Create a new mixer control from a template control. 2373db2a4165SFrank Mandarino * 2374db2a4165SFrank Mandarino * Returns 0 for success, else error. 2375db2a4165SFrank Mandarino */ 2376db2a4165SFrank Mandarino struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template, 23773056557fSMark Brown void *data, const char *long_name, 2378efb7ac3fSMark Brown const char *prefix) 2379db2a4165SFrank Mandarino { 2380db2a4165SFrank Mandarino struct snd_kcontrol_new template; 2381efb7ac3fSMark Brown struct snd_kcontrol *kcontrol; 2382efb7ac3fSMark Brown char *name = NULL; 2383db2a4165SFrank Mandarino 2384db2a4165SFrank Mandarino memcpy(&template, _template, sizeof(template)); 2385db2a4165SFrank Mandarino template.index = 0; 2386db2a4165SFrank Mandarino 2387efb7ac3fSMark Brown if (!long_name) 2388efb7ac3fSMark Brown long_name = template.name; 2389efb7ac3fSMark Brown 2390efb7ac3fSMark Brown if (prefix) { 23912b581074SLars-Peter Clausen name = kasprintf(GFP_KERNEL, "%s %s", prefix, long_name); 2392efb7ac3fSMark Brown if (!name) 2393efb7ac3fSMark Brown return NULL; 2394efb7ac3fSMark Brown 2395efb7ac3fSMark Brown template.name = name; 2396efb7ac3fSMark Brown } else { 2397efb7ac3fSMark Brown template.name = long_name; 2398efb7ac3fSMark Brown } 2399efb7ac3fSMark Brown 2400efb7ac3fSMark Brown kcontrol = snd_ctl_new1(&template, data); 2401efb7ac3fSMark Brown 2402efb7ac3fSMark Brown kfree(name); 2403efb7ac3fSMark Brown 2404efb7ac3fSMark Brown return kcontrol; 2405db2a4165SFrank Mandarino } 2406db2a4165SFrank Mandarino EXPORT_SYMBOL_GPL(snd_soc_cnew); 2407db2a4165SFrank Mandarino 2408022658beSLiam Girdwood static int snd_soc_add_controls(struct snd_card *card, struct device *dev, 2409022658beSLiam Girdwood const struct snd_kcontrol_new *controls, int num_controls, 2410022658beSLiam Girdwood const char *prefix, void *data) 2411022658beSLiam Girdwood { 2412022658beSLiam Girdwood int err, i; 2413022658beSLiam Girdwood 2414022658beSLiam Girdwood for (i = 0; i < num_controls; i++) { 2415022658beSLiam Girdwood const struct snd_kcontrol_new *control = &controls[i]; 2416022658beSLiam Girdwood err = snd_ctl_add(card, snd_soc_cnew(control, data, 2417022658beSLiam Girdwood control->name, prefix)); 2418022658beSLiam Girdwood if (err < 0) { 2419f110bfc7SLiam Girdwood dev_err(dev, "ASoC: Failed to add %s: %d\n", 2420f110bfc7SLiam Girdwood control->name, err); 2421022658beSLiam Girdwood return err; 2422022658beSLiam Girdwood } 2423022658beSLiam Girdwood } 2424022658beSLiam Girdwood 2425022658beSLiam Girdwood return 0; 2426022658beSLiam Girdwood } 2427022658beSLiam Girdwood 24284fefd698SDimitris Papastamos struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card, 24294fefd698SDimitris Papastamos const char *name) 24304fefd698SDimitris Papastamos { 24314fefd698SDimitris Papastamos struct snd_card *card = soc_card->snd_card; 24324fefd698SDimitris Papastamos struct snd_kcontrol *kctl; 24334fefd698SDimitris Papastamos 24344fefd698SDimitris Papastamos if (unlikely(!name)) 24354fefd698SDimitris Papastamos return NULL; 24364fefd698SDimitris Papastamos 24374fefd698SDimitris Papastamos list_for_each_entry(kctl, &card->controls, list) 24384fefd698SDimitris Papastamos if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) 24394fefd698SDimitris Papastamos return kctl; 24404fefd698SDimitris Papastamos return NULL; 24414fefd698SDimitris Papastamos } 24424fefd698SDimitris Papastamos EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol); 24434fefd698SDimitris Papastamos 2444db2a4165SFrank Mandarino /** 24450f2780adSLars-Peter Clausen * snd_soc_add_component_controls - Add an array of controls to a component. 24460f2780adSLars-Peter Clausen * 24470f2780adSLars-Peter Clausen * @component: Component to add controls to 24480f2780adSLars-Peter Clausen * @controls: Array of controls to add 24490f2780adSLars-Peter Clausen * @num_controls: Number of elements in the array 24500f2780adSLars-Peter Clausen * 24510f2780adSLars-Peter Clausen * Return: 0 for success, else error. 24520f2780adSLars-Peter Clausen */ 24530f2780adSLars-Peter Clausen int snd_soc_add_component_controls(struct snd_soc_component *component, 24540f2780adSLars-Peter Clausen const struct snd_kcontrol_new *controls, unsigned int num_controls) 24550f2780adSLars-Peter Clausen { 24560f2780adSLars-Peter Clausen struct snd_card *card = component->card->snd_card; 24570f2780adSLars-Peter Clausen 24580f2780adSLars-Peter Clausen return snd_soc_add_controls(card, component->dev, controls, 24590f2780adSLars-Peter Clausen num_controls, component->name_prefix, component); 24600f2780adSLars-Peter Clausen } 24610f2780adSLars-Peter Clausen EXPORT_SYMBOL_GPL(snd_soc_add_component_controls); 24620f2780adSLars-Peter Clausen 24630f2780adSLars-Peter Clausen /** 2464022658beSLiam Girdwood * snd_soc_add_codec_controls - add an array of controls to a codec. 2465022658beSLiam Girdwood * Convenience function to add a list of controls. Many codecs were 24663e8e1952SIan Molton * duplicating this code. 24673e8e1952SIan Molton * 24683e8e1952SIan Molton * @codec: codec to add controls to 24693e8e1952SIan Molton * @controls: array of controls to add 24703e8e1952SIan Molton * @num_controls: number of elements in the array 24713e8e1952SIan Molton * 24723e8e1952SIan Molton * Return 0 for success, else error. 24733e8e1952SIan Molton */ 2474022658beSLiam Girdwood int snd_soc_add_codec_controls(struct snd_soc_codec *codec, 24750f2780adSLars-Peter Clausen const struct snd_kcontrol_new *controls, unsigned int num_controls) 24763e8e1952SIan Molton { 24770f2780adSLars-Peter Clausen return snd_soc_add_component_controls(&codec->component, controls, 24780f2780adSLars-Peter Clausen num_controls); 24793e8e1952SIan Molton } 2480022658beSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_add_codec_controls); 24813e8e1952SIan Molton 24823e8e1952SIan Molton /** 2483022658beSLiam Girdwood * snd_soc_add_card_controls - add an array of controls to a SoC card. 2484022658beSLiam Girdwood * Convenience function to add a list of controls. 2485022658beSLiam Girdwood * 2486022658beSLiam Girdwood * @soc_card: SoC card to add controls to 2487022658beSLiam Girdwood * @controls: array of controls to add 2488022658beSLiam Girdwood * @num_controls: number of elements in the array 2489022658beSLiam Girdwood * 2490022658beSLiam Girdwood * Return 0 for success, else error. 2491022658beSLiam Girdwood */ 2492022658beSLiam Girdwood int snd_soc_add_card_controls(struct snd_soc_card *soc_card, 2493022658beSLiam Girdwood const struct snd_kcontrol_new *controls, int num_controls) 2494022658beSLiam Girdwood { 2495022658beSLiam Girdwood struct snd_card *card = soc_card->snd_card; 2496022658beSLiam Girdwood 2497022658beSLiam Girdwood return snd_soc_add_controls(card, soc_card->dev, controls, num_controls, 2498022658beSLiam Girdwood NULL, soc_card); 2499022658beSLiam Girdwood } 2500022658beSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_add_card_controls); 2501022658beSLiam Girdwood 2502022658beSLiam Girdwood /** 2503022658beSLiam Girdwood * snd_soc_add_dai_controls - add an array of controls to a DAI. 2504022658beSLiam Girdwood * Convienience function to add a list of controls. 2505022658beSLiam Girdwood * 2506022658beSLiam Girdwood * @dai: DAI to add controls to 2507022658beSLiam Girdwood * @controls: array of controls to add 2508022658beSLiam Girdwood * @num_controls: number of elements in the array 2509022658beSLiam Girdwood * 2510022658beSLiam Girdwood * Return 0 for success, else error. 2511022658beSLiam Girdwood */ 2512022658beSLiam Girdwood int snd_soc_add_dai_controls(struct snd_soc_dai *dai, 2513022658beSLiam Girdwood const struct snd_kcontrol_new *controls, int num_controls) 2514022658beSLiam Girdwood { 2515313665b9SLars-Peter Clausen struct snd_card *card = dai->component->card->snd_card; 2516022658beSLiam Girdwood 2517022658beSLiam Girdwood return snd_soc_add_controls(card, dai->dev, controls, num_controls, 2518022658beSLiam Girdwood NULL, dai); 2519022658beSLiam Girdwood } 2520022658beSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_add_dai_controls); 2521022658beSLiam Girdwood 2522022658beSLiam Girdwood /** 25238c6529dbSLiam Girdwood * snd_soc_dai_set_sysclk - configure DAI system or master clock. 25248c6529dbSLiam Girdwood * @dai: DAI 25258c6529dbSLiam Girdwood * @clk_id: DAI specific clock ID 25268c6529dbSLiam Girdwood * @freq: new clock frequency in Hz 25278c6529dbSLiam Girdwood * @dir: new clock direction - input/output. 25288c6529dbSLiam Girdwood * 25298c6529dbSLiam Girdwood * Configures the DAI master (MCLK) or system (SYSCLK) clocking. 25308c6529dbSLiam Girdwood */ 25318c6529dbSLiam Girdwood int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, 25328c6529dbSLiam Girdwood unsigned int freq, int dir) 25338c6529dbSLiam Girdwood { 253446471925SKuninori Morimoto if (dai->driver->ops->set_sysclk) 2535f0fba2adSLiam Girdwood return dai->driver->ops->set_sysclk(dai, clk_id, freq, dir); 253671ccef0dSKuninori Morimoto 253771ccef0dSKuninori Morimoto return snd_soc_component_set_sysclk(dai->component, clk_id, 0, 2538ec4ee52aSMark Brown freq, dir); 25398c6529dbSLiam Girdwood } 25408c6529dbSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk); 25418c6529dbSLiam Girdwood 25428c6529dbSLiam Girdwood /** 2543ec4ee52aSMark Brown * snd_soc_codec_set_sysclk - configure CODEC system or master clock. 2544ec4ee52aSMark Brown * @codec: CODEC 2545ec4ee52aSMark Brown * @clk_id: DAI specific clock ID 2546da1c6ea6SMark Brown * @source: Source for the clock 2547ec4ee52aSMark Brown * @freq: new clock frequency in Hz 2548ec4ee52aSMark Brown * @dir: new clock direction - input/output. 2549ec4ee52aSMark Brown * 2550ec4ee52aSMark Brown * Configures the CODEC master (MCLK) or system (SYSCLK) clocking. 2551ec4ee52aSMark Brown */ 2552ec4ee52aSMark Brown int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id, 2553da1c6ea6SMark Brown int source, unsigned int freq, int dir) 2554ec4ee52aSMark Brown { 2555ec4ee52aSMark Brown if (codec->driver->set_sysclk) 2556da1c6ea6SMark Brown return codec->driver->set_sysclk(codec, clk_id, source, 2557da1c6ea6SMark Brown freq, dir); 2558ec4ee52aSMark Brown else 25591104a9c8SMark Brown return -ENOTSUPP; 2560ec4ee52aSMark Brown } 2561ec4ee52aSMark Brown EXPORT_SYMBOL_GPL(snd_soc_codec_set_sysclk); 2562ec4ee52aSMark Brown 2563ec4ee52aSMark Brown /** 256471ccef0dSKuninori Morimoto * snd_soc_component_set_sysclk - configure COMPONENT system or master clock. 256571ccef0dSKuninori Morimoto * @component: COMPONENT 256671ccef0dSKuninori Morimoto * @clk_id: DAI specific clock ID 256771ccef0dSKuninori Morimoto * @source: Source for the clock 256871ccef0dSKuninori Morimoto * @freq: new clock frequency in Hz 256971ccef0dSKuninori Morimoto * @dir: new clock direction - input/output. 257071ccef0dSKuninori Morimoto * 257171ccef0dSKuninori Morimoto * Configures the CODEC master (MCLK) or system (SYSCLK) clocking. 257271ccef0dSKuninori Morimoto */ 257371ccef0dSKuninori Morimoto int snd_soc_component_set_sysclk(struct snd_soc_component *component, int clk_id, 257471ccef0dSKuninori Morimoto int source, unsigned int freq, int dir) 257571ccef0dSKuninori Morimoto { 257671ccef0dSKuninori Morimoto /* will be removed */ 257771ccef0dSKuninori Morimoto if (component->set_sysclk) 257871ccef0dSKuninori Morimoto return component->set_sysclk(component, clk_id, source, 257971ccef0dSKuninori Morimoto freq, dir); 258071ccef0dSKuninori Morimoto 258171ccef0dSKuninori Morimoto if (component->driver->set_sysclk) 258271ccef0dSKuninori Morimoto return component->driver->set_sysclk(component, clk_id, source, 258371ccef0dSKuninori Morimoto freq, dir); 258471ccef0dSKuninori Morimoto 258571ccef0dSKuninori Morimoto return -ENOTSUPP; 258671ccef0dSKuninori Morimoto } 258771ccef0dSKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_component_set_sysclk); 258871ccef0dSKuninori Morimoto 258971ccef0dSKuninori Morimoto /** 25908c6529dbSLiam Girdwood * snd_soc_dai_set_clkdiv - configure DAI clock dividers. 25918c6529dbSLiam Girdwood * @dai: DAI 2592ac11a2b3SMark Brown * @div_id: DAI specific clock divider ID 25938c6529dbSLiam Girdwood * @div: new clock divisor. 25948c6529dbSLiam Girdwood * 25958c6529dbSLiam Girdwood * Configures the clock dividers. This is used to derive the best DAI bit and 25968c6529dbSLiam Girdwood * frame clocks from the system or master clock. It's best to set the DAI bit 25978c6529dbSLiam Girdwood * and frame clocks as low as possible to save system power. 25988c6529dbSLiam Girdwood */ 25998c6529dbSLiam Girdwood int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai, 26008c6529dbSLiam Girdwood int div_id, int div) 26018c6529dbSLiam Girdwood { 260246471925SKuninori Morimoto if (dai->driver->ops->set_clkdiv) 2603f0fba2adSLiam Girdwood return dai->driver->ops->set_clkdiv(dai, div_id, div); 26048c6529dbSLiam Girdwood else 26058c6529dbSLiam Girdwood return -EINVAL; 26068c6529dbSLiam Girdwood } 26078c6529dbSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv); 26088c6529dbSLiam Girdwood 26098c6529dbSLiam Girdwood /** 26108c6529dbSLiam Girdwood * snd_soc_dai_set_pll - configure DAI PLL. 26118c6529dbSLiam Girdwood * @dai: DAI 26128c6529dbSLiam Girdwood * @pll_id: DAI specific PLL ID 261385488037SMark Brown * @source: DAI specific source for the PLL 26148c6529dbSLiam Girdwood * @freq_in: PLL input clock frequency in Hz 26158c6529dbSLiam Girdwood * @freq_out: requested PLL output clock frequency in Hz 26168c6529dbSLiam Girdwood * 26178c6529dbSLiam Girdwood * Configures and enables PLL to generate output clock based on input clock. 26188c6529dbSLiam Girdwood */ 261985488037SMark Brown int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source, 262085488037SMark Brown unsigned int freq_in, unsigned int freq_out) 26218c6529dbSLiam Girdwood { 262246471925SKuninori Morimoto if (dai->driver->ops->set_pll) 2623f0fba2adSLiam Girdwood return dai->driver->ops->set_pll(dai, pll_id, source, 262485488037SMark Brown freq_in, freq_out); 2625ef641e5dSKuninori Morimoto 2626ef641e5dSKuninori Morimoto return snd_soc_component_set_pll(dai->component, pll_id, source, 2627ec4ee52aSMark Brown freq_in, freq_out); 26288c6529dbSLiam Girdwood } 26298c6529dbSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll); 26308c6529dbSLiam Girdwood 2631ec4ee52aSMark Brown /* 2632ec4ee52aSMark Brown * snd_soc_codec_set_pll - configure codec PLL. 2633ec4ee52aSMark Brown * @codec: CODEC 2634ec4ee52aSMark Brown * @pll_id: DAI specific PLL ID 2635ec4ee52aSMark Brown * @source: DAI specific source for the PLL 2636ec4ee52aSMark Brown * @freq_in: PLL input clock frequency in Hz 2637ec4ee52aSMark Brown * @freq_out: requested PLL output clock frequency in Hz 2638ec4ee52aSMark Brown * 2639ec4ee52aSMark Brown * Configures and enables PLL to generate output clock based on input clock. 2640ec4ee52aSMark Brown */ 2641ec4ee52aSMark Brown int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source, 2642ec4ee52aSMark Brown unsigned int freq_in, unsigned int freq_out) 2643ec4ee52aSMark Brown { 2644ec4ee52aSMark Brown if (codec->driver->set_pll) 2645ec4ee52aSMark Brown return codec->driver->set_pll(codec, pll_id, source, 2646ec4ee52aSMark Brown freq_in, freq_out); 2647ec4ee52aSMark Brown else 2648ec4ee52aSMark Brown return -EINVAL; 2649ec4ee52aSMark Brown } 2650ec4ee52aSMark Brown EXPORT_SYMBOL_GPL(snd_soc_codec_set_pll); 2651ec4ee52aSMark Brown 2652ef641e5dSKuninori Morimoto /* 2653ef641e5dSKuninori Morimoto * snd_soc_component_set_pll - configure component PLL. 2654ef641e5dSKuninori Morimoto * @component: COMPONENT 2655ef641e5dSKuninori Morimoto * @pll_id: DAI specific PLL ID 2656ef641e5dSKuninori Morimoto * @source: DAI specific source for the PLL 2657ef641e5dSKuninori Morimoto * @freq_in: PLL input clock frequency in Hz 2658ef641e5dSKuninori Morimoto * @freq_out: requested PLL output clock frequency in Hz 2659ef641e5dSKuninori Morimoto * 2660ef641e5dSKuninori Morimoto * Configures and enables PLL to generate output clock based on input clock. 2661ef641e5dSKuninori Morimoto */ 2662ef641e5dSKuninori Morimoto int snd_soc_component_set_pll(struct snd_soc_component *component, int pll_id, 2663ef641e5dSKuninori Morimoto int source, unsigned int freq_in, 2664ef641e5dSKuninori Morimoto unsigned int freq_out) 2665ef641e5dSKuninori Morimoto { 2666ef641e5dSKuninori Morimoto /* will be removed */ 2667ef641e5dSKuninori Morimoto if (component->set_pll) 2668ef641e5dSKuninori Morimoto return component->set_pll(component, pll_id, source, 2669ef641e5dSKuninori Morimoto freq_in, freq_out); 2670ef641e5dSKuninori Morimoto 2671ef641e5dSKuninori Morimoto if (component->driver->set_pll) 2672ef641e5dSKuninori Morimoto return component->driver->set_pll(component, pll_id, source, 2673ef641e5dSKuninori Morimoto freq_in, freq_out); 2674ef641e5dSKuninori Morimoto 2675ef641e5dSKuninori Morimoto return -EINVAL; 2676ef641e5dSKuninori Morimoto } 2677ef641e5dSKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_component_set_pll); 2678ef641e5dSKuninori Morimoto 26798c6529dbSLiam Girdwood /** 2680e54cf76bSLiam Girdwood * snd_soc_dai_set_bclk_ratio - configure BCLK to sample rate ratio. 2681e54cf76bSLiam Girdwood * @dai: DAI 2682231b86b1SMasanari Iida * @ratio: Ratio of BCLK to Sample rate. 2683e54cf76bSLiam Girdwood * 2684e54cf76bSLiam Girdwood * Configures the DAI for a preset BCLK to sample rate ratio. 2685e54cf76bSLiam Girdwood */ 2686e54cf76bSLiam Girdwood int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) 2687e54cf76bSLiam Girdwood { 268846471925SKuninori Morimoto if (dai->driver->ops->set_bclk_ratio) 2689e54cf76bSLiam Girdwood return dai->driver->ops->set_bclk_ratio(dai, ratio); 2690e54cf76bSLiam Girdwood else 2691e54cf76bSLiam Girdwood return -EINVAL; 2692e54cf76bSLiam Girdwood } 2693e54cf76bSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dai_set_bclk_ratio); 2694e54cf76bSLiam Girdwood 2695e54cf76bSLiam Girdwood /** 26968c6529dbSLiam Girdwood * snd_soc_dai_set_fmt - configure DAI hardware audio format. 26978c6529dbSLiam Girdwood * @dai: DAI 2698bb19ba2aSRandy Dunlap * @fmt: SND_SOC_DAIFMT_* format value. 26998c6529dbSLiam Girdwood * 27008c6529dbSLiam Girdwood * Configures the DAI hardware format and clocking. 27018c6529dbSLiam Girdwood */ 27028c6529dbSLiam Girdwood int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 27038c6529dbSLiam Girdwood { 27045e4ba569SShawn Guo if (dai->driver == NULL) 27058c6529dbSLiam Girdwood return -EINVAL; 27065e4ba569SShawn Guo if (dai->driver->ops->set_fmt == NULL) 27075e4ba569SShawn Guo return -ENOTSUPP; 27085e4ba569SShawn Guo return dai->driver->ops->set_fmt(dai, fmt); 27098c6529dbSLiam Girdwood } 27108c6529dbSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt); 27118c6529dbSLiam Girdwood 27128c6529dbSLiam Girdwood /** 2713e5c21514SXiubo Li * snd_soc_xlate_tdm_slot - generate tx/rx slot mask. 271489c67857SXiubo Li * @slots: Number of slots in use. 271589c67857SXiubo Li * @tx_mask: bitmask representing active TX slots. 271689c67857SXiubo Li * @rx_mask: bitmask representing active RX slots. 271789c67857SXiubo Li * 271889c67857SXiubo Li * Generates the TDM tx and rx slot default masks for DAI. 271989c67857SXiubo Li */ 2720e5c21514SXiubo Li static int snd_soc_xlate_tdm_slot_mask(unsigned int slots, 272189c67857SXiubo Li unsigned int *tx_mask, 272289c67857SXiubo Li unsigned int *rx_mask) 272389c67857SXiubo Li { 272489c67857SXiubo Li if (*tx_mask || *rx_mask) 272589c67857SXiubo Li return 0; 272689c67857SXiubo Li 272789c67857SXiubo Li if (!slots) 272889c67857SXiubo Li return -EINVAL; 272989c67857SXiubo Li 273089c67857SXiubo Li *tx_mask = (1 << slots) - 1; 273189c67857SXiubo Li *rx_mask = (1 << slots) - 1; 273289c67857SXiubo Li 273389c67857SXiubo Li return 0; 273489c67857SXiubo Li } 273589c67857SXiubo Li 273689c67857SXiubo Li /** 2737e46c9366SLars-Peter Clausen * snd_soc_dai_set_tdm_slot() - Configures a DAI for TDM operation 2738e46c9366SLars-Peter Clausen * @dai: The DAI to configure 2739a5479e38SDaniel Ribeiro * @tx_mask: bitmask representing active TX slots. 2740a5479e38SDaniel Ribeiro * @rx_mask: bitmask representing active RX slots. 27418c6529dbSLiam Girdwood * @slots: Number of slots in use. 2742a5479e38SDaniel Ribeiro * @slot_width: Width in bits for each slot. 27438c6529dbSLiam Girdwood * 2744e46c9366SLars-Peter Clausen * This function configures the specified DAI for TDM operation. @slot contains 2745e46c9366SLars-Peter Clausen * the total number of slots of the TDM stream and @slot_with the width of each 2746e46c9366SLars-Peter Clausen * slot in bit clock cycles. @tx_mask and @rx_mask are bitmasks specifying the 2747e46c9366SLars-Peter Clausen * active slots of the TDM stream for the specified DAI, i.e. which slots the 2748e46c9366SLars-Peter Clausen * DAI should write to or read from. If a bit is set the corresponding slot is 2749e46c9366SLars-Peter Clausen * active, if a bit is cleared the corresponding slot is inactive. Bit 0 maps to 2750e46c9366SLars-Peter Clausen * the first slot, bit 1 to the second slot and so on. The first active slot 2751e46c9366SLars-Peter Clausen * maps to the first channel of the DAI, the second active slot to the second 2752e46c9366SLars-Peter Clausen * channel and so on. 2753e46c9366SLars-Peter Clausen * 2754e46c9366SLars-Peter Clausen * TDM mode can be disabled by passing 0 for @slots. In this case @tx_mask, 2755e46c9366SLars-Peter Clausen * @rx_mask and @slot_width will be ignored. 2756e46c9366SLars-Peter Clausen * 2757e46c9366SLars-Peter Clausen * Returns 0 on success, a negative error code otherwise. 27588c6529dbSLiam Girdwood */ 27598c6529dbSLiam Girdwood int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, 2760a5479e38SDaniel Ribeiro unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) 27618c6529dbSLiam Girdwood { 276246471925SKuninori Morimoto if (dai->driver->ops->xlate_tdm_slot_mask) 2763e5c21514SXiubo Li dai->driver->ops->xlate_tdm_slot_mask(slots, 276489c67857SXiubo Li &tx_mask, &rx_mask); 276589c67857SXiubo Li else 2766e5c21514SXiubo Li snd_soc_xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask); 276789c67857SXiubo Li 276888bd870fSBenoit Cousson dai->tx_mask = tx_mask; 276988bd870fSBenoit Cousson dai->rx_mask = rx_mask; 277088bd870fSBenoit Cousson 277146471925SKuninori Morimoto if (dai->driver->ops->set_tdm_slot) 2772f0fba2adSLiam Girdwood return dai->driver->ops->set_tdm_slot(dai, tx_mask, rx_mask, 2773a5479e38SDaniel Ribeiro slots, slot_width); 27748c6529dbSLiam Girdwood else 2775b2cbb6e1SXiubo Li return -ENOTSUPP; 27768c6529dbSLiam Girdwood } 27778c6529dbSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot); 27788c6529dbSLiam Girdwood 27798c6529dbSLiam Girdwood /** 2780472df3cbSBarry Song * snd_soc_dai_set_channel_map - configure DAI audio channel map 2781472df3cbSBarry Song * @dai: DAI 2782472df3cbSBarry Song * @tx_num: how many TX channels 2783472df3cbSBarry Song * @tx_slot: pointer to an array which imply the TX slot number channel 2784472df3cbSBarry Song * 0~num-1 uses 2785472df3cbSBarry Song * @rx_num: how many RX channels 2786472df3cbSBarry Song * @rx_slot: pointer to an array which imply the RX slot number channel 2787472df3cbSBarry Song * 0~num-1 uses 2788472df3cbSBarry Song * 2789472df3cbSBarry Song * configure the relationship between channel number and TDM slot number. 2790472df3cbSBarry Song */ 2791472df3cbSBarry Song int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai, 2792472df3cbSBarry Song unsigned int tx_num, unsigned int *tx_slot, 2793472df3cbSBarry Song unsigned int rx_num, unsigned int *rx_slot) 2794472df3cbSBarry Song { 279546471925SKuninori Morimoto if (dai->driver->ops->set_channel_map) 2796f0fba2adSLiam Girdwood return dai->driver->ops->set_channel_map(dai, tx_num, tx_slot, 2797472df3cbSBarry Song rx_num, rx_slot); 2798472df3cbSBarry Song else 2799472df3cbSBarry Song return -EINVAL; 2800472df3cbSBarry Song } 2801472df3cbSBarry Song EXPORT_SYMBOL_GPL(snd_soc_dai_set_channel_map); 2802472df3cbSBarry Song 2803472df3cbSBarry Song /** 28048c6529dbSLiam Girdwood * snd_soc_dai_set_tristate - configure DAI system or master clock. 28058c6529dbSLiam Girdwood * @dai: DAI 28068c6529dbSLiam Girdwood * @tristate: tristate enable 28078c6529dbSLiam Girdwood * 28088c6529dbSLiam Girdwood * Tristates the DAI so that others can use it. 28098c6529dbSLiam Girdwood */ 28108c6529dbSLiam Girdwood int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate) 28118c6529dbSLiam Girdwood { 281246471925SKuninori Morimoto if (dai->driver->ops->set_tristate) 2813f0fba2adSLiam Girdwood return dai->driver->ops->set_tristate(dai, tristate); 28148c6529dbSLiam Girdwood else 28158c6529dbSLiam Girdwood return -EINVAL; 28168c6529dbSLiam Girdwood } 28178c6529dbSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dai_set_tristate); 28188c6529dbSLiam Girdwood 28198c6529dbSLiam Girdwood /** 28208c6529dbSLiam Girdwood * snd_soc_dai_digital_mute - configure DAI system or master clock. 28218c6529dbSLiam Girdwood * @dai: DAI 28228c6529dbSLiam Girdwood * @mute: mute enable 2823da18396fSMark Brown * @direction: stream to mute 28248c6529dbSLiam Girdwood * 28258c6529dbSLiam Girdwood * Mutes the DAI DAC. 28268c6529dbSLiam Girdwood */ 2827da18396fSMark Brown int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute, 2828da18396fSMark Brown int direction) 28298c6529dbSLiam Girdwood { 2830da18396fSMark Brown if (!dai->driver) 2831da18396fSMark Brown return -ENOTSUPP; 2832da18396fSMark Brown 2833da18396fSMark Brown if (dai->driver->ops->mute_stream) 2834da18396fSMark Brown return dai->driver->ops->mute_stream(dai, mute, direction); 2835da18396fSMark Brown else if (direction == SNDRV_PCM_STREAM_PLAYBACK && 2836da18396fSMark Brown dai->driver->ops->digital_mute) 2837f0fba2adSLiam Girdwood return dai->driver->ops->digital_mute(dai, mute); 28388c6529dbSLiam Girdwood else 283904570c62SMark Brown return -ENOTSUPP; 28408c6529dbSLiam Girdwood } 28418c6529dbSLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute); 28428c6529dbSLiam Girdwood 2843c5af3a2eSMark Brown /** 2844c5af3a2eSMark Brown * snd_soc_register_card - Register a card with the ASoC core 2845c5af3a2eSMark Brown * 2846ac11a2b3SMark Brown * @card: Card to register 2847c5af3a2eSMark Brown * 2848c5af3a2eSMark Brown */ 284970a7ca34SVinod Koul int snd_soc_register_card(struct snd_soc_card *card) 2850c5af3a2eSMark Brown { 2851923c5e61SMengdong Lin int i, ret; 28521a497983SMengdong Lin struct snd_soc_pcm_runtime *rtd; 2853f0fba2adSLiam Girdwood 2854c5af3a2eSMark Brown if (!card->name || !card->dev) 2855c5af3a2eSMark Brown return -EINVAL; 2856c5af3a2eSMark Brown 28575a504963SStephen Warren for (i = 0; i < card->num_links; i++) { 28585a504963SStephen Warren struct snd_soc_dai_link *link = &card->dai_link[i]; 28595a504963SStephen Warren 2860923c5e61SMengdong Lin ret = soc_init_dai_link(card, link); 286188bd870fSBenoit Cousson if (ret) { 2862923c5e61SMengdong Lin dev_err(card->dev, "ASoC: failed to init link %s\n", 2863923c5e61SMengdong Lin link->name); 286488bd870fSBenoit Cousson return ret; 286588bd870fSBenoit Cousson } 28665a504963SStephen Warren } 28675a504963SStephen Warren 2868ed77cc12SMark Brown dev_set_drvdata(card->dev, card); 2869ed77cc12SMark Brown 2870111c6419SStephen Warren snd_soc_initialize_card_lists(card); 2871111c6419SStephen Warren 2872f8f80361SMengdong Lin INIT_LIST_HEAD(&card->dai_link_list); 2873f8f80361SMengdong Lin card->num_dai_links = 0; 2874f0fba2adSLiam Girdwood 28751a497983SMengdong Lin INIT_LIST_HEAD(&card->rtd_list); 28769115171aSMark Brown card->num_rtd = 0; 2877db2a4165SFrank Mandarino 2878db432b41SMark Brown INIT_LIST_HEAD(&card->dapm_dirty); 28798a978234SLiam Girdwood INIT_LIST_HEAD(&card->dobj_list); 2880db2a4165SFrank Mandarino card->instantiated = 0; 2881db2a4165SFrank Mandarino mutex_init(&card->mutex); 2882a73fb2dfSLiam Girdwood mutex_init(&card->dapm_mutex); 2883db2a4165SFrank Mandarino 2884b19e6e7bSMark Brown ret = snd_soc_instantiate_card(card); 2885b19e6e7bSMark Brown if (ret != 0) 28864e2576bdSKuninori Morimoto return ret; 2887db2a4165SFrank Mandarino 2888988e8cc4SNicolin Chen /* deactivate pins to sleep state */ 28891a497983SMengdong Lin list_for_each_entry(rtd, &card->rtd_list, list) { 289088bd870fSBenoit Cousson struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 289188bd870fSBenoit Cousson int j; 289288bd870fSBenoit Cousson 289388bd870fSBenoit Cousson for (j = 0; j < rtd->num_codecs; j++) { 289488bd870fSBenoit Cousson struct snd_soc_dai *codec_dai = rtd->codec_dais[j]; 2895988e8cc4SNicolin Chen if (!codec_dai->active) 2896988e8cc4SNicolin Chen pinctrl_pm_select_sleep_state(codec_dai->dev); 289788bd870fSBenoit Cousson } 289888bd870fSBenoit Cousson 2899988e8cc4SNicolin Chen if (!cpu_dai->active) 2900988e8cc4SNicolin Chen pinctrl_pm_select_sleep_state(cpu_dai->dev); 2901988e8cc4SNicolin Chen } 2902988e8cc4SNicolin Chen 2903b19e6e7bSMark Brown return ret; 2904db2a4165SFrank Mandarino } 290570a7ca34SVinod Koul EXPORT_SYMBOL_GPL(snd_soc_register_card); 2906db2a4165SFrank Mandarino 2907db2a4165SFrank Mandarino /** 2908db2a4165SFrank Mandarino * snd_soc_unregister_card - Unregister a card with the ASoC core 2909db2a4165SFrank Mandarino * 2910db2a4165SFrank Mandarino * @card: Card to unregister 2911db2a4165SFrank Mandarino * 2912db2a4165SFrank Mandarino */ 291370a7ca34SVinod Koul int snd_soc_unregister_card(struct snd_soc_card *card) 2914db2a4165SFrank Mandarino { 291501e0df66SLars-Peter Clausen if (card->instantiated) { 291601e0df66SLars-Peter Clausen card->instantiated = false; 29171c325f77SLars-Peter Clausen snd_soc_dapm_shutdown(card); 2918b0e26485SVinod Koul soc_cleanup_card_resources(card); 2919f110bfc7SLiam Girdwood dev_dbg(card->dev, "ASoC: Unregistered card '%s'\n", card->name); 29208f6f9b29SKuninori Morimoto } 2921db2a4165SFrank Mandarino 2922db2a4165SFrank Mandarino return 0; 2923db2a4165SFrank Mandarino } 292470a7ca34SVinod Koul EXPORT_SYMBOL_GPL(snd_soc_unregister_card); 2925db2a4165SFrank Mandarino 2926db2a4165SFrank Mandarino /* 2927db2a4165SFrank Mandarino * Simplify DAI link configuration by removing ".-1" from device names 2928db2a4165SFrank Mandarino * and sanitizing names. 2929db2a4165SFrank Mandarino */ 29300b9a214aSDimitris Papastamos static char *fmt_single_name(struct device *dev, int *id) 2931db2a4165SFrank Mandarino { 2932db2a4165SFrank Mandarino char *found, name[NAME_SIZE]; 2933db2a4165SFrank Mandarino int id1, id2; 2934db2a4165SFrank Mandarino 2935db2a4165SFrank Mandarino if (dev_name(dev) == NULL) 2936db2a4165SFrank Mandarino return NULL; 2937db2a4165SFrank Mandarino 293858818a77SDimitris Papastamos strlcpy(name, dev_name(dev), NAME_SIZE); 2939db2a4165SFrank Mandarino 2940db2a4165SFrank Mandarino /* are we a "%s.%d" name (platform and SPI components) */ 2941c5af3a2eSMark Brown found = strstr(name, dev->driver->name); 2942c5af3a2eSMark Brown if (found) { 2943c5af3a2eSMark Brown /* get ID */ 2944c5af3a2eSMark Brown if (sscanf(&found[strlen(dev->driver->name)], ".%d", id) == 1) { 2945c5af3a2eSMark Brown 2946c5af3a2eSMark Brown /* discard ID from name if ID == -1 */ 2947c5af3a2eSMark Brown if (*id == -1) 2948c5af3a2eSMark Brown found[strlen(dev->driver->name)] = '\0'; 2949c5af3a2eSMark Brown } 2950c5af3a2eSMark Brown 2951c5af3a2eSMark Brown } else { 2952c5af3a2eSMark Brown /* I2C component devices are named "bus-addr" */ 2953c5af3a2eSMark Brown if (sscanf(name, "%x-%x", &id1, &id2) == 2) { 2954c5af3a2eSMark Brown char tmp[NAME_SIZE]; 2955c5af3a2eSMark Brown 2956c5af3a2eSMark Brown /* create unique ID number from I2C addr and bus */ 2957c5af3a2eSMark Brown *id = ((id1 & 0xffff) << 16) + id2; 2958c5af3a2eSMark Brown 2959c5af3a2eSMark Brown /* sanitize component name for DAI link creation */ 2960c5af3a2eSMark Brown snprintf(tmp, NAME_SIZE, "%s.%s", dev->driver->name, name); 296158818a77SDimitris Papastamos strlcpy(name, tmp, NAME_SIZE); 2962c5af3a2eSMark Brown } else 2963c5af3a2eSMark Brown *id = 0; 2964c5af3a2eSMark Brown } 2965c5af3a2eSMark Brown 2966c5af3a2eSMark Brown return kstrdup(name, GFP_KERNEL); 2967c5af3a2eSMark Brown } 2968c5af3a2eSMark Brown 2969c5af3a2eSMark Brown /* 2970c5af3a2eSMark Brown * Simplify DAI link naming for single devices with multiple DAIs by removing 2971c5af3a2eSMark Brown * any ".-1" and using the DAI name (instead of device name). 2972c5af3a2eSMark Brown */ 2973c5af3a2eSMark Brown static inline char *fmt_multiple_name(struct device *dev, 2974c5af3a2eSMark Brown struct snd_soc_dai_driver *dai_drv) 2975c5af3a2eSMark Brown { 2976c5af3a2eSMark Brown if (dai_drv->name == NULL) { 297710e8aa9aSMichał Mirosław dev_err(dev, 297810e8aa9aSMichał Mirosław "ASoC: error - multiple DAI %s registered with no name\n", 297910e8aa9aSMichał Mirosław dev_name(dev)); 2980c5af3a2eSMark Brown return NULL; 2981c5af3a2eSMark Brown } 2982c5af3a2eSMark Brown 2983c5af3a2eSMark Brown return kstrdup(dai_drv->name, GFP_KERNEL); 2984c5af3a2eSMark Brown } 2985c5af3a2eSMark Brown 2986c5af3a2eSMark Brown /** 298732c9ba54SLars-Peter Clausen * snd_soc_unregister_dai - Unregister DAIs from the ASoC core 29889115171aSMark Brown * 298932c9ba54SLars-Peter Clausen * @component: The component for which the DAIs should be unregistered 29909115171aSMark Brown */ 299132c9ba54SLars-Peter Clausen static void snd_soc_unregister_dais(struct snd_soc_component *component) 29929115171aSMark Brown { 29935c1d5f09SLars-Peter Clausen struct snd_soc_dai *dai, *_dai; 29949115171aSMark Brown 29955c1d5f09SLars-Peter Clausen list_for_each_entry_safe(dai, _dai, &component->dai_list, list) { 299632c9ba54SLars-Peter Clausen dev_dbg(component->dev, "ASoC: Unregistered DAI '%s'\n", 299732c9ba54SLars-Peter Clausen dai->name); 29989115171aSMark Brown list_del(&dai->list); 2999f0fba2adSLiam Girdwood kfree(dai->name); 3000f0fba2adSLiam Girdwood kfree(dai); 30019115171aSMark Brown } 300232c9ba54SLars-Peter Clausen } 30039115171aSMark Brown 30045e4fb372SMengdong Lin /* Create a DAI and add it to the component's DAI list */ 30055e4fb372SMengdong Lin static struct snd_soc_dai *soc_add_dai(struct snd_soc_component *component, 30065e4fb372SMengdong Lin struct snd_soc_dai_driver *dai_drv, 30075e4fb372SMengdong Lin bool legacy_dai_naming) 30085e4fb372SMengdong Lin { 30095e4fb372SMengdong Lin struct device *dev = component->dev; 30105e4fb372SMengdong Lin struct snd_soc_dai *dai; 30115e4fb372SMengdong Lin 30125e4fb372SMengdong Lin dev_dbg(dev, "ASoC: dynamically register DAI %s\n", dev_name(dev)); 30135e4fb372SMengdong Lin 30145e4fb372SMengdong Lin dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL); 30155e4fb372SMengdong Lin if (dai == NULL) 30165e4fb372SMengdong Lin return NULL; 30175e4fb372SMengdong Lin 30185e4fb372SMengdong Lin /* 30195e4fb372SMengdong Lin * Back in the old days when we still had component-less DAIs, 30205e4fb372SMengdong Lin * instead of having a static name, component-less DAIs would 30215e4fb372SMengdong Lin * inherit the name of the parent device so it is possible to 30225e4fb372SMengdong Lin * register multiple instances of the DAI. We still need to keep 30235e4fb372SMengdong Lin * the same naming style even though those DAIs are not 30245e4fb372SMengdong Lin * component-less anymore. 30255e4fb372SMengdong Lin */ 30265e4fb372SMengdong Lin if (legacy_dai_naming && 30275e4fb372SMengdong Lin (dai_drv->id == 0 || dai_drv->name == NULL)) { 30285e4fb372SMengdong Lin dai->name = fmt_single_name(dev, &dai->id); 30295e4fb372SMengdong Lin } else { 30305e4fb372SMengdong Lin dai->name = fmt_multiple_name(dev, dai_drv); 30315e4fb372SMengdong Lin if (dai_drv->id) 30325e4fb372SMengdong Lin dai->id = dai_drv->id; 30335e4fb372SMengdong Lin else 30345e4fb372SMengdong Lin dai->id = component->num_dai; 30355e4fb372SMengdong Lin } 30365e4fb372SMengdong Lin if (dai->name == NULL) { 30375e4fb372SMengdong Lin kfree(dai); 30385e4fb372SMengdong Lin return NULL; 30395e4fb372SMengdong Lin } 30405e4fb372SMengdong Lin 30415e4fb372SMengdong Lin dai->component = component; 30425e4fb372SMengdong Lin dai->dev = dev; 30435e4fb372SMengdong Lin dai->driver = dai_drv; 30445e4fb372SMengdong Lin if (!dai->driver->ops) 30455e4fb372SMengdong Lin dai->driver->ops = &null_dai_ops; 30465e4fb372SMengdong Lin 304758bf4179SKuninori Morimoto list_add_tail(&dai->list, &component->dai_list); 30485e4fb372SMengdong Lin component->num_dai++; 30495e4fb372SMengdong Lin 30505e4fb372SMengdong Lin dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name); 30515e4fb372SMengdong Lin return dai; 30525e4fb372SMengdong Lin } 30535e4fb372SMengdong Lin 30549115171aSMark Brown /** 305532c9ba54SLars-Peter Clausen * snd_soc_register_dais - Register a DAI with the ASoC core 30569115171aSMark Brown * 30576106d129SLars-Peter Clausen * @component: The component the DAIs are registered for 30586106d129SLars-Peter Clausen * @dai_drv: DAI driver to use for the DAIs 3059ac11a2b3SMark Brown * @count: Number of DAIs 306032c9ba54SLars-Peter Clausen * @legacy_dai_naming: Use the legacy naming scheme and let the DAI inherit the 306132c9ba54SLars-Peter Clausen * parent's name. 30629115171aSMark Brown */ 306332c9ba54SLars-Peter Clausen static int snd_soc_register_dais(struct snd_soc_component *component, 3064bb13109dSLars-Peter Clausen struct snd_soc_dai_driver *dai_drv, size_t count, 3065bb13109dSLars-Peter Clausen bool legacy_dai_naming) 30669115171aSMark Brown { 30676106d129SLars-Peter Clausen struct device *dev = component->dev; 3068f0fba2adSLiam Girdwood struct snd_soc_dai *dai; 306932c9ba54SLars-Peter Clausen unsigned int i; 307032c9ba54SLars-Peter Clausen int ret; 3071f0fba2adSLiam Girdwood 30725b5e0928SAlexey Dobriyan dev_dbg(dev, "ASoC: dai register %s #%zu\n", dev_name(dev), count); 30739115171aSMark Brown 30749115171aSMark Brown for (i = 0; i < count; i++) { 3075f0fba2adSLiam Girdwood 30765e4fb372SMengdong Lin dai = soc_add_dai(component, dai_drv + i, 30775e4fb372SMengdong Lin count == 1 && legacy_dai_naming); 3078c46e0079SAxel Lin if (dai == NULL) { 3079c46e0079SAxel Lin ret = -ENOMEM; 3080c46e0079SAxel Lin goto err; 3081c46e0079SAxel Lin } 3082f0fba2adSLiam Girdwood } 3083f0fba2adSLiam Girdwood 30849115171aSMark Brown return 0; 30859115171aSMark Brown 30869115171aSMark Brown err: 308732c9ba54SLars-Peter Clausen snd_soc_unregister_dais(component); 30889115171aSMark Brown 30899115171aSMark Brown return ret; 30909115171aSMark Brown } 30919115171aSMark Brown 309268003e6cSMengdong Lin /** 309368003e6cSMengdong Lin * snd_soc_register_dai - Register a DAI dynamically & create its widgets 309468003e6cSMengdong Lin * 309568003e6cSMengdong Lin * @component: The component the DAIs are registered for 309668003e6cSMengdong Lin * @dai_drv: DAI driver to use for the DAI 309768003e6cSMengdong Lin * 309868003e6cSMengdong Lin * Topology can use this API to register DAIs when probing a component. 309968003e6cSMengdong Lin * These DAIs's widgets will be freed in the card cleanup and the DAIs 310068003e6cSMengdong Lin * will be freed in the component cleanup. 310168003e6cSMengdong Lin */ 310268003e6cSMengdong Lin int snd_soc_register_dai(struct snd_soc_component *component, 310368003e6cSMengdong Lin struct snd_soc_dai_driver *dai_drv) 310468003e6cSMengdong Lin { 310568003e6cSMengdong Lin struct snd_soc_dapm_context *dapm = 310668003e6cSMengdong Lin snd_soc_component_get_dapm(component); 310768003e6cSMengdong Lin struct snd_soc_dai *dai; 310868003e6cSMengdong Lin int ret; 310968003e6cSMengdong Lin 311068003e6cSMengdong Lin if (dai_drv->dobj.type != SND_SOC_DOBJ_PCM) { 311168003e6cSMengdong Lin dev_err(component->dev, "Invalid dai type %d\n", 311268003e6cSMengdong Lin dai_drv->dobj.type); 311368003e6cSMengdong Lin return -EINVAL; 311468003e6cSMengdong Lin } 311568003e6cSMengdong Lin 311668003e6cSMengdong Lin lockdep_assert_held(&client_mutex); 311768003e6cSMengdong Lin dai = soc_add_dai(component, dai_drv, false); 311868003e6cSMengdong Lin if (!dai) 311968003e6cSMengdong Lin return -ENOMEM; 312068003e6cSMengdong Lin 312168003e6cSMengdong Lin /* Create the DAI widgets here. After adding DAIs, topology may 312268003e6cSMengdong Lin * also add routes that need these widgets as source or sink. 312368003e6cSMengdong Lin */ 312468003e6cSMengdong Lin ret = snd_soc_dapm_new_dai_widgets(dapm, dai); 312568003e6cSMengdong Lin if (ret != 0) { 312668003e6cSMengdong Lin dev_err(component->dev, 312768003e6cSMengdong Lin "Failed to create DAI widgets %d\n", ret); 312868003e6cSMengdong Lin } 312968003e6cSMengdong Lin 313068003e6cSMengdong Lin return ret; 313168003e6cSMengdong Lin } 313268003e6cSMengdong Lin EXPORT_SYMBOL_GPL(snd_soc_register_dai); 313368003e6cSMengdong Lin 313414e8bdebSLars-Peter Clausen static void snd_soc_component_seq_notifier(struct snd_soc_dapm_context *dapm, 313514e8bdebSLars-Peter Clausen enum snd_soc_dapm_type type, int subseq) 3136d191bd8dSKuninori Morimoto { 313714e8bdebSLars-Peter Clausen struct snd_soc_component *component = dapm->component; 3138d191bd8dSKuninori Morimoto 313914e8bdebSLars-Peter Clausen component->driver->seq_notifier(component, type, subseq); 314014e8bdebSLars-Peter Clausen } 3141d191bd8dSKuninori Morimoto 314214e8bdebSLars-Peter Clausen static int snd_soc_component_stream_event(struct snd_soc_dapm_context *dapm, 314314e8bdebSLars-Peter Clausen int event) 314414e8bdebSLars-Peter Clausen { 314514e8bdebSLars-Peter Clausen struct snd_soc_component *component = dapm->component; 314614e8bdebSLars-Peter Clausen 314714e8bdebSLars-Peter Clausen return component->driver->stream_event(component, event); 314814e8bdebSLars-Peter Clausen } 314914e8bdebSLars-Peter Clausen 3150f523acebSKuninori Morimoto static int snd_soc_component_drv_pcm_new(struct snd_soc_component *component, 3151f523acebSKuninori Morimoto struct snd_soc_pcm_runtime *rtd) 3152f523acebSKuninori Morimoto { 3153f523acebSKuninori Morimoto if (component->driver->pcm_new) 3154f523acebSKuninori Morimoto return component->driver->pcm_new(rtd); 3155f523acebSKuninori Morimoto 3156f523acebSKuninori Morimoto return 0; 3157f523acebSKuninori Morimoto } 3158f523acebSKuninori Morimoto 3159f523acebSKuninori Morimoto static void snd_soc_component_drv_pcm_free(struct snd_soc_component *component, 3160f523acebSKuninori Morimoto struct snd_pcm *pcm) 3161f523acebSKuninori Morimoto { 3162f523acebSKuninori Morimoto if (component->driver->pcm_free) 3163f523acebSKuninori Morimoto component->driver->pcm_free(pcm); 3164f523acebSKuninori Morimoto } 3165f523acebSKuninori Morimoto 31667ba236ceSKuninori Morimoto static int snd_soc_component_set_bias_level(struct snd_soc_dapm_context *dapm, 31677ba236ceSKuninori Morimoto enum snd_soc_bias_level level) 31687ba236ceSKuninori Morimoto { 31697ba236ceSKuninori Morimoto struct snd_soc_component *component = dapm->component; 31707ba236ceSKuninori Morimoto 31717ba236ceSKuninori Morimoto return component->driver->set_bias_level(component, level); 31727ba236ceSKuninori Morimoto } 31737ba236ceSKuninori Morimoto 3174bb13109dSLars-Peter Clausen static int snd_soc_component_initialize(struct snd_soc_component *component, 3175bb13109dSLars-Peter Clausen const struct snd_soc_component_driver *driver, struct device *dev) 3176d191bd8dSKuninori Morimoto { 3177ce0fc93aSLars-Peter Clausen struct snd_soc_dapm_context *dapm; 3178ce0fc93aSLars-Peter Clausen 3179bb13109dSLars-Peter Clausen component->name = fmt_single_name(dev, &component->id); 3180bb13109dSLars-Peter Clausen if (!component->name) { 3181bb13109dSLars-Peter Clausen dev_err(dev, "ASoC: Failed to allocate name\n"); 3182d191bd8dSKuninori Morimoto return -ENOMEM; 3183d191bd8dSKuninori Morimoto } 3184d191bd8dSKuninori Morimoto 3185bb13109dSLars-Peter Clausen component->dev = dev; 3186bb13109dSLars-Peter Clausen component->driver = driver; 318761aca564SLars-Peter Clausen component->probe = component->driver->probe; 318861aca564SLars-Peter Clausen component->remove = component->driver->remove; 31899178feb4SKuninori Morimoto component->suspend = component->driver->suspend; 31909178feb4SKuninori Morimoto component->resume = component->driver->resume; 319171ccef0dSKuninori Morimoto component->set_sysclk = component->driver->set_sysclk; 3192ef641e5dSKuninori Morimoto component->set_pll = component->driver->set_pll; 319344c07365SKuninori Morimoto component->set_jack = component->driver->set_jack; 3194f523acebSKuninori Morimoto component->pcm_new = snd_soc_component_drv_pcm_new; 3195f523acebSKuninori Morimoto component->pcm_free = snd_soc_component_drv_pcm_free; 3196e2c330b9SLars-Peter Clausen 319788c27465SKuninori Morimoto dapm = snd_soc_component_get_dapm(component); 3198ce0fc93aSLars-Peter Clausen dapm->dev = dev; 3199ce0fc93aSLars-Peter Clausen dapm->component = component; 3200ce0fc93aSLars-Peter Clausen dapm->bias_level = SND_SOC_BIAS_OFF; 32017ba236ceSKuninori Morimoto dapm->idle_bias_off = !driver->idle_bias_on; 32027ba236ceSKuninori Morimoto dapm->suspend_bias_off = driver->suspend_bias_off; 320314e8bdebSLars-Peter Clausen if (driver->seq_notifier) 320414e8bdebSLars-Peter Clausen dapm->seq_notifier = snd_soc_component_seq_notifier; 320514e8bdebSLars-Peter Clausen if (driver->stream_event) 320614e8bdebSLars-Peter Clausen dapm->stream_event = snd_soc_component_stream_event; 32077ba236ceSKuninori Morimoto if (driver->set_bias_level) 32087ba236ceSKuninori Morimoto dapm->set_bias_level = snd_soc_component_set_bias_level; 3209ce0fc93aSLars-Peter Clausen 3210bb13109dSLars-Peter Clausen INIT_LIST_HEAD(&component->dai_list); 3211bb13109dSLars-Peter Clausen mutex_init(&component->io_mutex); 3212bb13109dSLars-Peter Clausen 3213bb13109dSLars-Peter Clausen return 0; 3214d191bd8dSKuninori Morimoto } 3215d191bd8dSKuninori Morimoto 321620feb881SLars-Peter Clausen static void snd_soc_component_setup_regmap(struct snd_soc_component *component) 3217886f5692SLars-Peter Clausen { 3218886f5692SLars-Peter Clausen int val_bytes = regmap_get_val_bytes(component->regmap); 321920feb881SLars-Peter Clausen 3220886f5692SLars-Peter Clausen /* Errors are legitimate for non-integer byte multiples */ 3221886f5692SLars-Peter Clausen if (val_bytes > 0) 3222886f5692SLars-Peter Clausen component->val_bytes = val_bytes; 3223886f5692SLars-Peter Clausen } 322420feb881SLars-Peter Clausen 3225e874bf5fSLars-Peter Clausen #ifdef CONFIG_REGMAP 3226e874bf5fSLars-Peter Clausen 322720feb881SLars-Peter Clausen /** 322820feb881SLars-Peter Clausen * snd_soc_component_init_regmap() - Initialize regmap instance for the component 322920feb881SLars-Peter Clausen * @component: The component for which to initialize the regmap instance 323020feb881SLars-Peter Clausen * @regmap: The regmap instance that should be used by the component 323120feb881SLars-Peter Clausen * 323220feb881SLars-Peter Clausen * This function allows deferred assignment of the regmap instance that is 323320feb881SLars-Peter Clausen * associated with the component. Only use this if the regmap instance is not 323420feb881SLars-Peter Clausen * yet ready when the component is registered. The function must also be called 323520feb881SLars-Peter Clausen * before the first IO attempt of the component. 323620feb881SLars-Peter Clausen */ 323720feb881SLars-Peter Clausen void snd_soc_component_init_regmap(struct snd_soc_component *component, 323820feb881SLars-Peter Clausen struct regmap *regmap) 323920feb881SLars-Peter Clausen { 324020feb881SLars-Peter Clausen component->regmap = regmap; 324120feb881SLars-Peter Clausen snd_soc_component_setup_regmap(component); 3242886f5692SLars-Peter Clausen } 324320feb881SLars-Peter Clausen EXPORT_SYMBOL_GPL(snd_soc_component_init_regmap); 324420feb881SLars-Peter Clausen 324520feb881SLars-Peter Clausen /** 324620feb881SLars-Peter Clausen * snd_soc_component_exit_regmap() - De-initialize regmap instance for the component 324720feb881SLars-Peter Clausen * @component: The component for which to de-initialize the regmap instance 324820feb881SLars-Peter Clausen * 324920feb881SLars-Peter Clausen * Calls regmap_exit() on the regmap instance associated to the component and 325020feb881SLars-Peter Clausen * removes the regmap instance from the component. 325120feb881SLars-Peter Clausen * 325220feb881SLars-Peter Clausen * This function should only be used if snd_soc_component_init_regmap() was used 325320feb881SLars-Peter Clausen * to initialize the regmap instance. 325420feb881SLars-Peter Clausen */ 325520feb881SLars-Peter Clausen void snd_soc_component_exit_regmap(struct snd_soc_component *component) 325620feb881SLars-Peter Clausen { 325720feb881SLars-Peter Clausen regmap_exit(component->regmap); 325820feb881SLars-Peter Clausen component->regmap = NULL; 325920feb881SLars-Peter Clausen } 326020feb881SLars-Peter Clausen EXPORT_SYMBOL_GPL(snd_soc_component_exit_regmap); 3261886f5692SLars-Peter Clausen 3262e874bf5fSLars-Peter Clausen #endif 3263d191bd8dSKuninori Morimoto 3264bb13109dSLars-Peter Clausen static void snd_soc_component_add_unlocked(struct snd_soc_component *component) 3265bb13109dSLars-Peter Clausen { 326620feb881SLars-Peter Clausen if (!component->write && !component->read) { 326720feb881SLars-Peter Clausen if (!component->regmap) 326820feb881SLars-Peter Clausen component->regmap = dev_get_regmap(component->dev, NULL); 326920feb881SLars-Peter Clausen if (component->regmap) 327020feb881SLars-Peter Clausen snd_soc_component_setup_regmap(component); 327120feb881SLars-Peter Clausen } 3272886f5692SLars-Peter Clausen 3273bb13109dSLars-Peter Clausen list_add(&component->list, &component_list); 32748a978234SLiam Girdwood INIT_LIST_HEAD(&component->dobj_list); 3275d191bd8dSKuninori Morimoto } 3276d191bd8dSKuninori Morimoto 3277bb13109dSLars-Peter Clausen static void snd_soc_component_add(struct snd_soc_component *component) 3278bb13109dSLars-Peter Clausen { 3279d191bd8dSKuninori Morimoto mutex_lock(&client_mutex); 3280bb13109dSLars-Peter Clausen snd_soc_component_add_unlocked(component); 3281d191bd8dSKuninori Morimoto mutex_unlock(&client_mutex); 3282bb13109dSLars-Peter Clausen } 3283d191bd8dSKuninori Morimoto 3284bb13109dSLars-Peter Clausen static void snd_soc_component_cleanup(struct snd_soc_component *component) 3285bb13109dSLars-Peter Clausen { 3286bb13109dSLars-Peter Clausen snd_soc_unregister_dais(component); 3287bb13109dSLars-Peter Clausen kfree(component->name); 3288bb13109dSLars-Peter Clausen } 3289d191bd8dSKuninori Morimoto 3290bb13109dSLars-Peter Clausen static void snd_soc_component_del_unlocked(struct snd_soc_component *component) 3291bb13109dSLars-Peter Clausen { 3292c12c1aadSKuninori Morimoto struct snd_soc_card *card = component->card; 3293c12c1aadSKuninori Morimoto 3294c12c1aadSKuninori Morimoto if (card) 3295c12c1aadSKuninori Morimoto snd_soc_unregister_card(card); 3296c12c1aadSKuninori Morimoto 3297bb13109dSLars-Peter Clausen list_del(&component->list); 3298bb13109dSLars-Peter Clausen } 3299d191bd8dSKuninori Morimoto 3300273d778eSKuninori Morimoto #define ENDIANNESS_MAP(name) \ 3301273d778eSKuninori Morimoto (SNDRV_PCM_FMTBIT_##name##LE | SNDRV_PCM_FMTBIT_##name##BE) 3302273d778eSKuninori Morimoto static u64 endianness_format_map[] = { 3303273d778eSKuninori Morimoto ENDIANNESS_MAP(S16_), 3304273d778eSKuninori Morimoto ENDIANNESS_MAP(U16_), 3305273d778eSKuninori Morimoto ENDIANNESS_MAP(S24_), 3306273d778eSKuninori Morimoto ENDIANNESS_MAP(U24_), 3307273d778eSKuninori Morimoto ENDIANNESS_MAP(S32_), 3308273d778eSKuninori Morimoto ENDIANNESS_MAP(U32_), 3309273d778eSKuninori Morimoto ENDIANNESS_MAP(S24_3), 3310273d778eSKuninori Morimoto ENDIANNESS_MAP(U24_3), 3311273d778eSKuninori Morimoto ENDIANNESS_MAP(S20_3), 3312273d778eSKuninori Morimoto ENDIANNESS_MAP(U20_3), 3313273d778eSKuninori Morimoto ENDIANNESS_MAP(S18_3), 3314273d778eSKuninori Morimoto ENDIANNESS_MAP(U18_3), 3315273d778eSKuninori Morimoto ENDIANNESS_MAP(FLOAT_), 3316273d778eSKuninori Morimoto ENDIANNESS_MAP(FLOAT64_), 3317273d778eSKuninori Morimoto ENDIANNESS_MAP(IEC958_SUBFRAME_), 3318273d778eSKuninori Morimoto }; 3319273d778eSKuninori Morimoto 3320273d778eSKuninori Morimoto /* 3321273d778eSKuninori Morimoto * Fix up the DAI formats for endianness: codecs don't actually see 3322273d778eSKuninori Morimoto * the endianness of the data but we're using the CPU format 3323273d778eSKuninori Morimoto * definitions which do need to include endianness so we ensure that 3324273d778eSKuninori Morimoto * codec DAIs always have both big and little endian variants set. 3325273d778eSKuninori Morimoto */ 3326273d778eSKuninori Morimoto static void convert_endianness_formats(struct snd_soc_pcm_stream *stream) 3327273d778eSKuninori Morimoto { 3328273d778eSKuninori Morimoto int i; 3329273d778eSKuninori Morimoto 3330273d778eSKuninori Morimoto for (i = 0; i < ARRAY_SIZE(endianness_format_map); i++) 3331273d778eSKuninori Morimoto if (stream->formats & endianness_format_map[i]) 3332273d778eSKuninori Morimoto stream->formats |= endianness_format_map[i]; 3333273d778eSKuninori Morimoto } 3334273d778eSKuninori Morimoto 3335e0dac41bSKuninori Morimoto int snd_soc_add_component(struct device *dev, 3336e0dac41bSKuninori Morimoto struct snd_soc_component *component, 3337cf9e829eSKuninori Morimoto const struct snd_soc_component_driver *component_driver, 3338d191bd8dSKuninori Morimoto struct snd_soc_dai_driver *dai_drv, 3339d191bd8dSKuninori Morimoto int num_dai) 3340d191bd8dSKuninori Morimoto { 3341bb13109dSLars-Peter Clausen int ret; 3342273d778eSKuninori Morimoto int i; 3343d191bd8dSKuninori Morimoto 3344cf9e829eSKuninori Morimoto ret = snd_soc_component_initialize(component, component_driver, dev); 3345bb13109dSLars-Peter Clausen if (ret) 3346bb13109dSLars-Peter Clausen goto err_free; 3347bb13109dSLars-Peter Clausen 3348cf9e829eSKuninori Morimoto component->ignore_pmdown_time = true; 3349cf9e829eSKuninori Morimoto component->registered_as_component = true; 33503d59400fSLars-Peter Clausen 3351273d778eSKuninori Morimoto if (component_driver->endianness) { 3352273d778eSKuninori Morimoto for (i = 0; i < num_dai; i++) { 3353273d778eSKuninori Morimoto convert_endianness_formats(&dai_drv[i].playback); 3354273d778eSKuninori Morimoto convert_endianness_formats(&dai_drv[i].capture); 3355273d778eSKuninori Morimoto } 3356273d778eSKuninori Morimoto } 3357273d778eSKuninori Morimoto 335869941babSKuninori Morimoto ret = snd_soc_register_dais(component, dai_drv, num_dai, 335969941babSKuninori Morimoto !component_driver->non_legacy_dai_naming); 3360bb13109dSLars-Peter Clausen if (ret < 0) { 3361f42cf8d6SMasanari Iida dev_err(dev, "ASoC: Failed to register DAIs: %d\n", ret); 3362bb13109dSLars-Peter Clausen goto err_cleanup; 3363bb13109dSLars-Peter Clausen } 3364bb13109dSLars-Peter Clausen 3365cf9e829eSKuninori Morimoto snd_soc_component_add(component); 3366bb13109dSLars-Peter Clausen 3367bb13109dSLars-Peter Clausen return 0; 3368bb13109dSLars-Peter Clausen 3369bb13109dSLars-Peter Clausen err_cleanup: 3370cf9e829eSKuninori Morimoto snd_soc_component_cleanup(component); 3371bb13109dSLars-Peter Clausen err_free: 3372bb13109dSLars-Peter Clausen return ret; 3373d191bd8dSKuninori Morimoto } 3374e0dac41bSKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_add_component); 3375e0dac41bSKuninori Morimoto 3376e0dac41bSKuninori Morimoto int snd_soc_register_component(struct device *dev, 3377e0dac41bSKuninori Morimoto const struct snd_soc_component_driver *component_driver, 3378e0dac41bSKuninori Morimoto struct snd_soc_dai_driver *dai_drv, 3379e0dac41bSKuninori Morimoto int num_dai) 3380e0dac41bSKuninori Morimoto { 3381e0dac41bSKuninori Morimoto struct snd_soc_component *component; 3382e0dac41bSKuninori Morimoto 33837ecbd6a9SKuninori Morimoto component = devm_kzalloc(dev, sizeof(*component), GFP_KERNEL); 338408e61d03SKuninori Morimoto if (!component) 3385e0dac41bSKuninori Morimoto return -ENOMEM; 3386e0dac41bSKuninori Morimoto 3387e0dac41bSKuninori Morimoto return snd_soc_add_component(dev, component, component_driver, 3388e0dac41bSKuninori Morimoto dai_drv, num_dai); 3389e0dac41bSKuninori Morimoto } 3390d191bd8dSKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_register_component); 3391d191bd8dSKuninori Morimoto 3392d191bd8dSKuninori Morimoto /** 33932eccea8cSKuninori Morimoto * snd_soc_unregister_component - Unregister all related component 33942eccea8cSKuninori Morimoto * from the ASoC core 3395d191bd8dSKuninori Morimoto * 3396628536eaSJonathan Corbet * @dev: The device to unregister 3397d191bd8dSKuninori Morimoto */ 33982eccea8cSKuninori Morimoto static int __snd_soc_unregister_component(struct device *dev) 3399d191bd8dSKuninori Morimoto { 3400cf9e829eSKuninori Morimoto struct snd_soc_component *component; 340121a03528SKuninori Morimoto int found = 0; 3402d191bd8dSKuninori Morimoto 340334e81ab4SLars-Peter Clausen mutex_lock(&client_mutex); 3404cf9e829eSKuninori Morimoto list_for_each_entry(component, &component_list, list) { 340521a03528SKuninori Morimoto if (dev != component->dev || 340621a03528SKuninori Morimoto !component->registered_as_component) 340721a03528SKuninori Morimoto continue; 3408d191bd8dSKuninori Morimoto 3409cf9e829eSKuninori Morimoto snd_soc_tplg_component_remove(component, SND_SOC_TPLG_INDEX_ALL); 3410cf9e829eSKuninori Morimoto snd_soc_component_del_unlocked(component); 341121a03528SKuninori Morimoto found = 1; 341221a03528SKuninori Morimoto break; 3413d191bd8dSKuninori Morimoto } 3414d191bd8dSKuninori Morimoto mutex_unlock(&client_mutex); 3415d191bd8dSKuninori Morimoto 341621a03528SKuninori Morimoto if (found) { 3417cf9e829eSKuninori Morimoto snd_soc_component_cleanup(component); 3418d191bd8dSKuninori Morimoto } 34192eccea8cSKuninori Morimoto 34202eccea8cSKuninori Morimoto return found; 34212eccea8cSKuninori Morimoto } 34222eccea8cSKuninori Morimoto 34232eccea8cSKuninori Morimoto void snd_soc_unregister_component(struct device *dev) 34242eccea8cSKuninori Morimoto { 34252eccea8cSKuninori Morimoto while (__snd_soc_unregister_component(dev)); 3426d191bd8dSKuninori Morimoto } 3427d191bd8dSKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_unregister_component); 3428d191bd8dSKuninori Morimoto 34297dd5d0d9SKuninori Morimoto struct snd_soc_component *snd_soc_lookup_component(struct device *dev, 34307dd5d0d9SKuninori Morimoto const char *driver_name) 34317dd5d0d9SKuninori Morimoto { 34327dd5d0d9SKuninori Morimoto struct snd_soc_component *component; 34337dd5d0d9SKuninori Morimoto struct snd_soc_component *ret; 34347dd5d0d9SKuninori Morimoto 34357dd5d0d9SKuninori Morimoto ret = NULL; 34367dd5d0d9SKuninori Morimoto mutex_lock(&client_mutex); 34377dd5d0d9SKuninori Morimoto list_for_each_entry(component, &component_list, list) { 34387dd5d0d9SKuninori Morimoto if (dev != component->dev) 34397dd5d0d9SKuninori Morimoto continue; 34407dd5d0d9SKuninori Morimoto 34417dd5d0d9SKuninori Morimoto if (driver_name && 34427dd5d0d9SKuninori Morimoto (driver_name != component->driver->name) && 34437dd5d0d9SKuninori Morimoto (strcmp(component->driver->name, driver_name) != 0)) 34447dd5d0d9SKuninori Morimoto continue; 34457dd5d0d9SKuninori Morimoto 34467dd5d0d9SKuninori Morimoto ret = component; 34477dd5d0d9SKuninori Morimoto break; 34487dd5d0d9SKuninori Morimoto } 34497dd5d0d9SKuninori Morimoto mutex_unlock(&client_mutex); 34507dd5d0d9SKuninori Morimoto 34517dd5d0d9SKuninori Morimoto return ret; 34527dd5d0d9SKuninori Morimoto } 34537dd5d0d9SKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_lookup_component); 34547dd5d0d9SKuninori Morimoto 3455f1d45cc3SLars-Peter Clausen static int snd_soc_codec_drv_probe(struct snd_soc_component *component) 3456f1d45cc3SLars-Peter Clausen { 3457f1d45cc3SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_component_to_codec(component); 3458f1d45cc3SLars-Peter Clausen 3459f1d45cc3SLars-Peter Clausen return codec->driver->probe(codec); 3460f1d45cc3SLars-Peter Clausen } 3461f1d45cc3SLars-Peter Clausen 3462f1d45cc3SLars-Peter Clausen static void snd_soc_codec_drv_remove(struct snd_soc_component *component) 3463f1d45cc3SLars-Peter Clausen { 3464f1d45cc3SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_component_to_codec(component); 3465f1d45cc3SLars-Peter Clausen 3466f1d45cc3SLars-Peter Clausen codec->driver->remove(codec); 3467f1d45cc3SLars-Peter Clausen } 3468f1d45cc3SLars-Peter Clausen 34699178feb4SKuninori Morimoto static int snd_soc_codec_drv_suspend(struct snd_soc_component *component) 34709178feb4SKuninori Morimoto { 34719178feb4SKuninori Morimoto struct snd_soc_codec *codec = snd_soc_component_to_codec(component); 34729178feb4SKuninori Morimoto 34739178feb4SKuninori Morimoto return codec->driver->suspend(codec); 34749178feb4SKuninori Morimoto } 34759178feb4SKuninori Morimoto 34769178feb4SKuninori Morimoto static int snd_soc_codec_drv_resume(struct snd_soc_component *component) 34779178feb4SKuninori Morimoto { 34789178feb4SKuninori Morimoto struct snd_soc_codec *codec = snd_soc_component_to_codec(component); 34799178feb4SKuninori Morimoto 34809178feb4SKuninori Morimoto return codec->driver->resume(codec); 34819178feb4SKuninori Morimoto } 34829178feb4SKuninori Morimoto 3483e2c330b9SLars-Peter Clausen static int snd_soc_codec_drv_write(struct snd_soc_component *component, 3484e2c330b9SLars-Peter Clausen unsigned int reg, unsigned int val) 3485e2c330b9SLars-Peter Clausen { 3486e2c330b9SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_component_to_codec(component); 3487e2c330b9SLars-Peter Clausen 3488e2c330b9SLars-Peter Clausen return codec->driver->write(codec, reg, val); 3489e2c330b9SLars-Peter Clausen } 3490e2c330b9SLars-Peter Clausen 3491e2c330b9SLars-Peter Clausen static int snd_soc_codec_drv_read(struct snd_soc_component *component, 3492e2c330b9SLars-Peter Clausen unsigned int reg, unsigned int *val) 3493e2c330b9SLars-Peter Clausen { 3494e2c330b9SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_component_to_codec(component); 3495e2c330b9SLars-Peter Clausen 3496e2c330b9SLars-Peter Clausen *val = codec->driver->read(codec, reg); 3497e2c330b9SLars-Peter Clausen 3498e2c330b9SLars-Peter Clausen return 0; 3499e2c330b9SLars-Peter Clausen } 3500e2c330b9SLars-Peter Clausen 350171ccef0dSKuninori Morimoto static int snd_soc_codec_set_sysclk_(struct snd_soc_component *component, 350271ccef0dSKuninori Morimoto int clk_id, int source, unsigned int freq, int dir) 350371ccef0dSKuninori Morimoto { 350471ccef0dSKuninori Morimoto struct snd_soc_codec *codec = snd_soc_component_to_codec(component); 350571ccef0dSKuninori Morimoto 350671ccef0dSKuninori Morimoto return snd_soc_codec_set_sysclk(codec, clk_id, source, freq, dir); 350771ccef0dSKuninori Morimoto } 350871ccef0dSKuninori Morimoto 3509ef641e5dSKuninori Morimoto static int snd_soc_codec_set_pll_(struct snd_soc_component *component, 3510ef641e5dSKuninori Morimoto int pll_id, int source, unsigned int freq_in, 3511ef641e5dSKuninori Morimoto unsigned int freq_out) 3512ef641e5dSKuninori Morimoto { 3513ef641e5dSKuninori Morimoto struct snd_soc_codec *codec = snd_soc_component_to_codec(component); 3514ef641e5dSKuninori Morimoto 3515ef641e5dSKuninori Morimoto return snd_soc_codec_set_pll(codec, pll_id, source, freq_in, freq_out); 3516ef641e5dSKuninori Morimoto } 3517ef641e5dSKuninori Morimoto 351844c07365SKuninori Morimoto static int snd_soc_codec_set_jack_(struct snd_soc_component *component, 351944c07365SKuninori Morimoto struct snd_soc_jack *jack, void *data) 352044c07365SKuninori Morimoto { 352144c07365SKuninori Morimoto struct snd_soc_codec *codec = snd_soc_component_to_codec(component); 352244c07365SKuninori Morimoto 352344c07365SKuninori Morimoto return snd_soc_codec_set_jack(codec, jack, data); 352444c07365SKuninori Morimoto } 352544c07365SKuninori Morimoto 352668f831c2SLars-Peter Clausen static int snd_soc_codec_set_bias_level(struct snd_soc_dapm_context *dapm, 352768f831c2SLars-Peter Clausen enum snd_soc_bias_level level) 352868f831c2SLars-Peter Clausen { 352968f831c2SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); 353068f831c2SLars-Peter Clausen 353168f831c2SLars-Peter Clausen return codec->driver->set_bias_level(codec, level); 353268f831c2SLars-Peter Clausen } 353368f831c2SLars-Peter Clausen 35340d0cf00aSMark Brown /** 35350d0cf00aSMark Brown * snd_soc_register_codec - Register a codec with the ASoC core 35360d0cf00aSMark Brown * 3537628536eaSJonathan Corbet * @dev: The parent device for this codec 3538628536eaSJonathan Corbet * @codec_drv: Codec driver 3539628536eaSJonathan Corbet * @dai_drv: The associated DAI driver 3540628536eaSJonathan Corbet * @num_dai: Number of DAIs 35410d0cf00aSMark Brown */ 3542f0fba2adSLiam Girdwood int snd_soc_register_codec(struct device *dev, 3543001ae4c0SMark Brown const struct snd_soc_codec_driver *codec_drv, 3544001ae4c0SMark Brown struct snd_soc_dai_driver *dai_drv, 3545001ae4c0SMark Brown int num_dai) 35460d0cf00aSMark Brown { 35474890140fSLars-Peter Clausen struct snd_soc_dapm_context *dapm; 3548f0fba2adSLiam Girdwood struct snd_soc_codec *codec; 3549bb13109dSLars-Peter Clausen struct snd_soc_dai *dai; 3550f0fba2adSLiam Girdwood int ret, i; 3551151ab22cSMark Brown 3552f0fba2adSLiam Girdwood dev_dbg(dev, "codec register %s\n", dev_name(dev)); 35530d0cf00aSMark Brown 3554f0fba2adSLiam Girdwood codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); 3555f0fba2adSLiam Girdwood if (codec == NULL) 3556f0fba2adSLiam Girdwood return -ENOMEM; 35570d0cf00aSMark Brown 3558f1d45cc3SLars-Peter Clausen codec->component.codec = codec; 3559ce0fc93aSLars-Peter Clausen 3560bb13109dSLars-Peter Clausen ret = snd_soc_component_initialize(&codec->component, 3561bb13109dSLars-Peter Clausen &codec_drv->component_driver, dev); 3562bb13109dSLars-Peter Clausen if (ret) 3563bb13109dSLars-Peter Clausen goto err_free; 3564151ab22cSMark Brown 3565f1d45cc3SLars-Peter Clausen if (codec_drv->probe) 3566f1d45cc3SLars-Peter Clausen codec->component.probe = snd_soc_codec_drv_probe; 3567f1d45cc3SLars-Peter Clausen if (codec_drv->remove) 3568f1d45cc3SLars-Peter Clausen codec->component.remove = snd_soc_codec_drv_remove; 35699178feb4SKuninori Morimoto if (codec_drv->suspend) 35709178feb4SKuninori Morimoto codec->component.suspend = snd_soc_codec_drv_suspend; 35719178feb4SKuninori Morimoto if (codec_drv->resume) 35729178feb4SKuninori Morimoto codec->component.resume = snd_soc_codec_drv_resume; 3573e2c330b9SLars-Peter Clausen if (codec_drv->write) 3574e2c330b9SLars-Peter Clausen codec->component.write = snd_soc_codec_drv_write; 3575e2c330b9SLars-Peter Clausen if (codec_drv->read) 3576e2c330b9SLars-Peter Clausen codec->component.read = snd_soc_codec_drv_read; 357771ccef0dSKuninori Morimoto if (codec_drv->set_sysclk) 357871ccef0dSKuninori Morimoto codec->component.set_sysclk = snd_soc_codec_set_sysclk_; 3579ef641e5dSKuninori Morimoto if (codec_drv->set_pll) 3580ef641e5dSKuninori Morimoto codec->component.set_pll = snd_soc_codec_set_pll_; 358144c07365SKuninori Morimoto if (codec_drv->set_jack) 358244c07365SKuninori Morimoto codec->component.set_jack = snd_soc_codec_set_jack_; 35833d59400fSLars-Peter Clausen codec->component.ignore_pmdown_time = codec_drv->ignore_pmdown_time; 35844890140fSLars-Peter Clausen 35854890140fSLars-Peter Clausen dapm = snd_soc_codec_get_dapm(codec); 35864890140fSLars-Peter Clausen dapm->idle_bias_off = codec_drv->idle_bias_off; 35874890140fSLars-Peter Clausen dapm->suspend_bias_off = codec_drv->suspend_bias_off; 358814e8bdebSLars-Peter Clausen if (codec_drv->seq_notifier) 35894890140fSLars-Peter Clausen dapm->seq_notifier = codec_drv->seq_notifier; 359068f831c2SLars-Peter Clausen if (codec_drv->set_bias_level) 35914890140fSLars-Peter Clausen dapm->set_bias_level = snd_soc_codec_set_bias_level; 3592f0fba2adSLiam Girdwood codec->dev = dev; 3593f0fba2adSLiam Girdwood codec->driver = codec_drv; 3594e2c330b9SLars-Peter Clausen codec->component.val_bytes = codec_drv->reg_word_size; 3595f0fba2adSLiam Girdwood 359681c7cfd1SLars-Peter Clausen #ifdef CONFIG_DEBUG_FS 359781c7cfd1SLars-Peter Clausen codec->component.init_debugfs = soc_init_codec_debugfs; 359881c7cfd1SLars-Peter Clausen codec->component.debugfs_prefix = "codec"; 359981c7cfd1SLars-Peter Clausen #endif 3600a39f75f7SXiubo Li 3601a39f75f7SXiubo Li if (codec_drv->get_regmap) 3602886f5692SLars-Peter Clausen codec->component.regmap = codec_drv->get_regmap(dev); 3603a39f75f7SXiubo Li 3604f0fba2adSLiam Girdwood for (i = 0; i < num_dai; i++) { 3605273d778eSKuninori Morimoto convert_endianness_formats(&dai_drv[i].playback); 3606273d778eSKuninori Morimoto convert_endianness_formats(&dai_drv[i].capture); 3607f0fba2adSLiam Girdwood } 3608f0fba2adSLiam Girdwood 3609bb13109dSLars-Peter Clausen ret = snd_soc_register_dais(&codec->component, dai_drv, num_dai, false); 3610bb13109dSLars-Peter Clausen if (ret < 0) { 3611f42cf8d6SMasanari Iida dev_err(dev, "ASoC: Failed to register DAIs: %d\n", ret); 3612bb13109dSLars-Peter Clausen goto err_cleanup; 3613bb13109dSLars-Peter Clausen } 3614bb13109dSLars-Peter Clausen 3615bb13109dSLars-Peter Clausen list_for_each_entry(dai, &codec->component.dai_list, list) 3616bb13109dSLars-Peter Clausen dai->codec = codec; 3617bb13109dSLars-Peter Clausen 3618054880feSMark Brown mutex_lock(&client_mutex); 3619bb13109dSLars-Peter Clausen snd_soc_component_add_unlocked(&codec->component); 3620054880feSMark Brown list_add(&codec->list, &codec_list); 3621054880feSMark Brown mutex_unlock(&client_mutex); 3622054880feSMark Brown 3623f4333203SLars-Peter Clausen dev_dbg(codec->dev, "ASoC: Registered codec '%s'\n", 3624f4333203SLars-Peter Clausen codec->component.name); 36250d0cf00aSMark Brown return 0; 3626f0fba2adSLiam Girdwood 3627bb13109dSLars-Peter Clausen err_cleanup: 3628bb13109dSLars-Peter Clausen snd_soc_component_cleanup(&codec->component); 3629bb13109dSLars-Peter Clausen err_free: 3630f0fba2adSLiam Girdwood kfree(codec); 3631f0fba2adSLiam Girdwood return ret; 36320d0cf00aSMark Brown } 36330d0cf00aSMark Brown EXPORT_SYMBOL_GPL(snd_soc_register_codec); 36340d0cf00aSMark Brown 36350d0cf00aSMark Brown /** 36360d0cf00aSMark Brown * snd_soc_unregister_codec - Unregister a codec from the ASoC core 36370d0cf00aSMark Brown * 3638628536eaSJonathan Corbet * @dev: codec to unregister 36390d0cf00aSMark Brown */ 3640f0fba2adSLiam Girdwood void snd_soc_unregister_codec(struct device *dev) 36410d0cf00aSMark Brown { 3642f0fba2adSLiam Girdwood struct snd_soc_codec *codec; 3643f0fba2adSLiam Girdwood 364434e81ab4SLars-Peter Clausen mutex_lock(&client_mutex); 3645f0fba2adSLiam Girdwood list_for_each_entry(codec, &codec_list, list) { 3646f0fba2adSLiam Girdwood if (dev == codec->dev) 3647f0fba2adSLiam Girdwood goto found; 3648f0fba2adSLiam Girdwood } 364934e81ab4SLars-Peter Clausen mutex_unlock(&client_mutex); 3650f0fba2adSLiam Girdwood return; 3651f0fba2adSLiam Girdwood 3652f0fba2adSLiam Girdwood found: 36530d0cf00aSMark Brown list_del(&codec->list); 3654bb13109dSLars-Peter Clausen snd_soc_component_del_unlocked(&codec->component); 36550d0cf00aSMark Brown mutex_unlock(&client_mutex); 36560d0cf00aSMark Brown 3657f4333203SLars-Peter Clausen dev_dbg(codec->dev, "ASoC: Unregistered codec '%s'\n", 3658f4333203SLars-Peter Clausen codec->component.name); 3659f0fba2adSLiam Girdwood 3660bb13109dSLars-Peter Clausen snd_soc_component_cleanup(&codec->component); 36617a30a3dbSDimitris Papastamos snd_soc_cache_exit(codec); 3662f0fba2adSLiam Girdwood kfree(codec); 36630d0cf00aSMark Brown } 36640d0cf00aSMark Brown EXPORT_SYMBOL_GPL(snd_soc_unregister_codec); 36650d0cf00aSMark Brown 3666bec4fa05SStephen Warren /* Retrieve a card's name from device tree */ 3667b07609ceSKuninori Morimoto int snd_soc_of_parse_card_name(struct snd_soc_card *card, 3668bec4fa05SStephen Warren const char *propname) 3669bec4fa05SStephen Warren { 3670b07609ceSKuninori Morimoto struct device_node *np; 3671bec4fa05SStephen Warren int ret; 3672bec4fa05SStephen Warren 36737e07e7c0STushar Behera if (!card->dev) { 36747e07e7c0STushar Behera pr_err("card->dev is not set before calling %s\n", __func__); 36757e07e7c0STushar Behera return -EINVAL; 36767e07e7c0STushar Behera } 36777e07e7c0STushar Behera 36787e07e7c0STushar Behera np = card->dev->of_node; 36797e07e7c0STushar Behera 3680bec4fa05SStephen Warren ret = of_property_read_string_index(np, propname, 0, &card->name); 3681bec4fa05SStephen Warren /* 3682bec4fa05SStephen Warren * EINVAL means the property does not exist. This is fine providing 3683bec4fa05SStephen Warren * card->name was previously set, which is checked later in 3684bec4fa05SStephen Warren * snd_soc_register_card. 3685bec4fa05SStephen Warren */ 3686bec4fa05SStephen Warren if (ret < 0 && ret != -EINVAL) { 3687bec4fa05SStephen Warren dev_err(card->dev, 3688f110bfc7SLiam Girdwood "ASoC: Property '%s' could not be read: %d\n", 3689bec4fa05SStephen Warren propname, ret); 3690bec4fa05SStephen Warren return ret; 3691bec4fa05SStephen Warren } 3692bec4fa05SStephen Warren 3693bec4fa05SStephen Warren return 0; 3694bec4fa05SStephen Warren } 3695b07609ceSKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_of_parse_card_name); 3696bec4fa05SStephen Warren 36979a6d4860SXiubo Li static const struct snd_soc_dapm_widget simple_widgets[] = { 36989a6d4860SXiubo Li SND_SOC_DAPM_MIC("Microphone", NULL), 36999a6d4860SXiubo Li SND_SOC_DAPM_LINE("Line", NULL), 37009a6d4860SXiubo Li SND_SOC_DAPM_HP("Headphone", NULL), 37019a6d4860SXiubo Li SND_SOC_DAPM_SPK("Speaker", NULL), 37029a6d4860SXiubo Li }; 37039a6d4860SXiubo Li 370421efde50SKuninori Morimoto int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card, 37059a6d4860SXiubo Li const char *propname) 37069a6d4860SXiubo Li { 370721efde50SKuninori Morimoto struct device_node *np = card->dev->of_node; 37089a6d4860SXiubo Li struct snd_soc_dapm_widget *widgets; 37099a6d4860SXiubo Li const char *template, *wname; 37109a6d4860SXiubo Li int i, j, num_widgets, ret; 37119a6d4860SXiubo Li 37129a6d4860SXiubo Li num_widgets = of_property_count_strings(np, propname); 37139a6d4860SXiubo Li if (num_widgets < 0) { 37149a6d4860SXiubo Li dev_err(card->dev, 37159a6d4860SXiubo Li "ASoC: Property '%s' does not exist\n", propname); 37169a6d4860SXiubo Li return -EINVAL; 37179a6d4860SXiubo Li } 37189a6d4860SXiubo Li if (num_widgets & 1) { 37199a6d4860SXiubo Li dev_err(card->dev, 37209a6d4860SXiubo Li "ASoC: Property '%s' length is not even\n", propname); 37219a6d4860SXiubo Li return -EINVAL; 37229a6d4860SXiubo Li } 37239a6d4860SXiubo Li 37249a6d4860SXiubo Li num_widgets /= 2; 37259a6d4860SXiubo Li if (!num_widgets) { 37269a6d4860SXiubo Li dev_err(card->dev, "ASoC: Property '%s's length is zero\n", 37279a6d4860SXiubo Li propname); 37289a6d4860SXiubo Li return -EINVAL; 37299a6d4860SXiubo Li } 37309a6d4860SXiubo Li 37319a6d4860SXiubo Li widgets = devm_kcalloc(card->dev, num_widgets, sizeof(*widgets), 37329a6d4860SXiubo Li GFP_KERNEL); 37339a6d4860SXiubo Li if (!widgets) { 37349a6d4860SXiubo Li dev_err(card->dev, 37359a6d4860SXiubo Li "ASoC: Could not allocate memory for widgets\n"); 37369a6d4860SXiubo Li return -ENOMEM; 37379a6d4860SXiubo Li } 37389a6d4860SXiubo Li 37399a6d4860SXiubo Li for (i = 0; i < num_widgets; i++) { 37409a6d4860SXiubo Li ret = of_property_read_string_index(np, propname, 37419a6d4860SXiubo Li 2 * i, &template); 37429a6d4860SXiubo Li if (ret) { 37439a6d4860SXiubo Li dev_err(card->dev, 37449a6d4860SXiubo Li "ASoC: Property '%s' index %d read error:%d\n", 37459a6d4860SXiubo Li propname, 2 * i, ret); 37469a6d4860SXiubo Li return -EINVAL; 37479a6d4860SXiubo Li } 37489a6d4860SXiubo Li 37499a6d4860SXiubo Li for (j = 0; j < ARRAY_SIZE(simple_widgets); j++) { 37509a6d4860SXiubo Li if (!strncmp(template, simple_widgets[j].name, 37519a6d4860SXiubo Li strlen(simple_widgets[j].name))) { 37529a6d4860SXiubo Li widgets[i] = simple_widgets[j]; 37539a6d4860SXiubo Li break; 37549a6d4860SXiubo Li } 37559a6d4860SXiubo Li } 37569a6d4860SXiubo Li 37579a6d4860SXiubo Li if (j >= ARRAY_SIZE(simple_widgets)) { 37589a6d4860SXiubo Li dev_err(card->dev, 37599a6d4860SXiubo Li "ASoC: DAPM widget '%s' is not supported\n", 37609a6d4860SXiubo Li template); 37619a6d4860SXiubo Li return -EINVAL; 37629a6d4860SXiubo Li } 37639a6d4860SXiubo Li 37649a6d4860SXiubo Li ret = of_property_read_string_index(np, propname, 37659a6d4860SXiubo Li (2 * i) + 1, 37669a6d4860SXiubo Li &wname); 37679a6d4860SXiubo Li if (ret) { 37689a6d4860SXiubo Li dev_err(card->dev, 37699a6d4860SXiubo Li "ASoC: Property '%s' index %d read error:%d\n", 37709a6d4860SXiubo Li propname, (2 * i) + 1, ret); 37719a6d4860SXiubo Li return -EINVAL; 37729a6d4860SXiubo Li } 37739a6d4860SXiubo Li 37749a6d4860SXiubo Li widgets[i].name = wname; 37759a6d4860SXiubo Li } 37769a6d4860SXiubo Li 3777f23e860eSNicolin Chen card->of_dapm_widgets = widgets; 3778f23e860eSNicolin Chen card->num_of_dapm_widgets = num_widgets; 37799a6d4860SXiubo Li 37809a6d4860SXiubo Li return 0; 37819a6d4860SXiubo Li } 378221efde50SKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_simple_widgets); 37839a6d4860SXiubo Li 37846131084aSJyri Sarha static int snd_soc_of_get_slot_mask(struct device_node *np, 37856131084aSJyri Sarha const char *prop_name, 37866131084aSJyri Sarha unsigned int *mask) 37876131084aSJyri Sarha { 37886131084aSJyri Sarha u32 val; 37896c84e591SJyri Sarha const __be32 *of_slot_mask = of_get_property(np, prop_name, &val); 37906131084aSJyri Sarha int i; 37916131084aSJyri Sarha 37926131084aSJyri Sarha if (!of_slot_mask) 37936131084aSJyri Sarha return 0; 37946131084aSJyri Sarha val /= sizeof(u32); 37956131084aSJyri Sarha for (i = 0; i < val; i++) 37966131084aSJyri Sarha if (be32_to_cpup(&of_slot_mask[i])) 37976131084aSJyri Sarha *mask |= (1 << i); 37986131084aSJyri Sarha 37996131084aSJyri Sarha return val; 38006131084aSJyri Sarha } 38016131084aSJyri Sarha 380289c67857SXiubo Li int snd_soc_of_parse_tdm_slot(struct device_node *np, 38036131084aSJyri Sarha unsigned int *tx_mask, 38046131084aSJyri Sarha unsigned int *rx_mask, 380589c67857SXiubo Li unsigned int *slots, 380689c67857SXiubo Li unsigned int *slot_width) 380789c67857SXiubo Li { 380889c67857SXiubo Li u32 val; 380989c67857SXiubo Li int ret; 381089c67857SXiubo Li 38116131084aSJyri Sarha if (tx_mask) 38126131084aSJyri Sarha snd_soc_of_get_slot_mask(np, "dai-tdm-slot-tx-mask", tx_mask); 38136131084aSJyri Sarha if (rx_mask) 38146131084aSJyri Sarha snd_soc_of_get_slot_mask(np, "dai-tdm-slot-rx-mask", rx_mask); 38156131084aSJyri Sarha 381689c67857SXiubo Li if (of_property_read_bool(np, "dai-tdm-slot-num")) { 381789c67857SXiubo Li ret = of_property_read_u32(np, "dai-tdm-slot-num", &val); 381889c67857SXiubo Li if (ret) 381989c67857SXiubo Li return ret; 382089c67857SXiubo Li 382189c67857SXiubo Li if (slots) 382289c67857SXiubo Li *slots = val; 382389c67857SXiubo Li } 382489c67857SXiubo Li 382589c67857SXiubo Li if (of_property_read_bool(np, "dai-tdm-slot-width")) { 382689c67857SXiubo Li ret = of_property_read_u32(np, "dai-tdm-slot-width", &val); 382789c67857SXiubo Li if (ret) 382889c67857SXiubo Li return ret; 382989c67857SXiubo Li 383089c67857SXiubo Li if (slot_width) 383189c67857SXiubo Li *slot_width = val; 383289c67857SXiubo Li } 383389c67857SXiubo Li 383489c67857SXiubo Li return 0; 383589c67857SXiubo Li } 383689c67857SXiubo Li EXPORT_SYMBOL_GPL(snd_soc_of_parse_tdm_slot); 383789c67857SXiubo Li 3838440a3006SKuninori Morimoto void snd_soc_of_parse_audio_prefix(struct snd_soc_card *card, 38395e3cdaa2SKuninori Morimoto struct snd_soc_codec_conf *codec_conf, 38405e3cdaa2SKuninori Morimoto struct device_node *of_node, 38415e3cdaa2SKuninori Morimoto const char *propname) 38425e3cdaa2SKuninori Morimoto { 3843440a3006SKuninori Morimoto struct device_node *np = card->dev->of_node; 38445e3cdaa2SKuninori Morimoto const char *str; 38455e3cdaa2SKuninori Morimoto int ret; 38465e3cdaa2SKuninori Morimoto 38475e3cdaa2SKuninori Morimoto ret = of_property_read_string(np, propname, &str); 38485e3cdaa2SKuninori Morimoto if (ret < 0) { 38495e3cdaa2SKuninori Morimoto /* no prefix is not error */ 38505e3cdaa2SKuninori Morimoto return; 38515e3cdaa2SKuninori Morimoto } 38525e3cdaa2SKuninori Morimoto 38535e3cdaa2SKuninori Morimoto codec_conf->of_node = of_node; 38545e3cdaa2SKuninori Morimoto codec_conf->name_prefix = str; 38555e3cdaa2SKuninori Morimoto } 3856440a3006SKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_prefix); 38575e3cdaa2SKuninori Morimoto 38582bc644afSKuninori Morimoto int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, 3859a4a54dd5SStephen Warren const char *propname) 3860a4a54dd5SStephen Warren { 38612bc644afSKuninori Morimoto struct device_node *np = card->dev->of_node; 3862e3b1e6a1SMark Brown int num_routes; 3863a4a54dd5SStephen Warren struct snd_soc_dapm_route *routes; 3864a4a54dd5SStephen Warren int i, ret; 3865a4a54dd5SStephen Warren 3866a4a54dd5SStephen Warren num_routes = of_property_count_strings(np, propname); 3867c34ce320SRichard Zhao if (num_routes < 0 || num_routes & 1) { 386810e8aa9aSMichał Mirosław dev_err(card->dev, 386910e8aa9aSMichał Mirosław "ASoC: Property '%s' does not exist or its length is not even\n", 387010e8aa9aSMichał Mirosław propname); 3871a4a54dd5SStephen Warren return -EINVAL; 3872a4a54dd5SStephen Warren } 3873a4a54dd5SStephen Warren num_routes /= 2; 3874a4a54dd5SStephen Warren if (!num_routes) { 3875f110bfc7SLiam Girdwood dev_err(card->dev, "ASoC: Property '%s's length is zero\n", 3876a4a54dd5SStephen Warren propname); 3877a4a54dd5SStephen Warren return -EINVAL; 3878a4a54dd5SStephen Warren } 3879a4a54dd5SStephen Warren 3880e3b1e6a1SMark Brown routes = devm_kzalloc(card->dev, num_routes * sizeof(*routes), 3881a4a54dd5SStephen Warren GFP_KERNEL); 3882a4a54dd5SStephen Warren if (!routes) { 3883a4a54dd5SStephen Warren dev_err(card->dev, 3884f110bfc7SLiam Girdwood "ASoC: Could not allocate DAPM route table\n"); 3885a4a54dd5SStephen Warren return -EINVAL; 3886a4a54dd5SStephen Warren } 3887a4a54dd5SStephen Warren 3888a4a54dd5SStephen Warren for (i = 0; i < num_routes; i++) { 3889a4a54dd5SStephen Warren ret = of_property_read_string_index(np, propname, 3890e3b1e6a1SMark Brown 2 * i, &routes[i].sink); 3891a4a54dd5SStephen Warren if (ret) { 3892c871bd0bSMark Brown dev_err(card->dev, 3893c871bd0bSMark Brown "ASoC: Property '%s' index %d could not be read: %d\n", 3894c871bd0bSMark Brown propname, 2 * i, ret); 3895a4a54dd5SStephen Warren return -EINVAL; 3896a4a54dd5SStephen Warren } 3897a4a54dd5SStephen Warren ret = of_property_read_string_index(np, propname, 3898e3b1e6a1SMark Brown (2 * i) + 1, &routes[i].source); 3899a4a54dd5SStephen Warren if (ret) { 3900a4a54dd5SStephen Warren dev_err(card->dev, 3901c871bd0bSMark Brown "ASoC: Property '%s' index %d could not be read: %d\n", 3902c871bd0bSMark Brown propname, (2 * i) + 1, ret); 3903a4a54dd5SStephen Warren return -EINVAL; 3904a4a54dd5SStephen Warren } 3905a4a54dd5SStephen Warren } 3906a4a54dd5SStephen Warren 3907f23e860eSNicolin Chen card->num_of_dapm_routes = num_routes; 3908f23e860eSNicolin Chen card->of_dapm_routes = routes; 3909a4a54dd5SStephen Warren 3910a4a54dd5SStephen Warren return 0; 3911a4a54dd5SStephen Warren } 39122bc644afSKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_routing); 3913a4a54dd5SStephen Warren 3914a7930ed4SKuninori Morimoto unsigned int snd_soc_of_parse_daifmt(struct device_node *np, 3915389cb834SJyri Sarha const char *prefix, 3916389cb834SJyri Sarha struct device_node **bitclkmaster, 3917389cb834SJyri Sarha struct device_node **framemaster) 3918a7930ed4SKuninori Morimoto { 3919a7930ed4SKuninori Morimoto int ret, i; 3920a7930ed4SKuninori Morimoto char prop[128]; 3921a7930ed4SKuninori Morimoto unsigned int format = 0; 3922a7930ed4SKuninori Morimoto int bit, frame; 3923a7930ed4SKuninori Morimoto const char *str; 3924a7930ed4SKuninori Morimoto struct { 3925a7930ed4SKuninori Morimoto char *name; 3926a7930ed4SKuninori Morimoto unsigned int val; 3927a7930ed4SKuninori Morimoto } of_fmt_table[] = { 3928a7930ed4SKuninori Morimoto { "i2s", SND_SOC_DAIFMT_I2S }, 3929a7930ed4SKuninori Morimoto { "right_j", SND_SOC_DAIFMT_RIGHT_J }, 3930a7930ed4SKuninori Morimoto { "left_j", SND_SOC_DAIFMT_LEFT_J }, 3931a7930ed4SKuninori Morimoto { "dsp_a", SND_SOC_DAIFMT_DSP_A }, 3932a7930ed4SKuninori Morimoto { "dsp_b", SND_SOC_DAIFMT_DSP_B }, 3933a7930ed4SKuninori Morimoto { "ac97", SND_SOC_DAIFMT_AC97 }, 3934a7930ed4SKuninori Morimoto { "pdm", SND_SOC_DAIFMT_PDM}, 3935a7930ed4SKuninori Morimoto { "msb", SND_SOC_DAIFMT_MSB }, 3936a7930ed4SKuninori Morimoto { "lsb", SND_SOC_DAIFMT_LSB }, 3937a7930ed4SKuninori Morimoto }; 3938a7930ed4SKuninori Morimoto 3939a7930ed4SKuninori Morimoto if (!prefix) 3940a7930ed4SKuninori Morimoto prefix = ""; 3941a7930ed4SKuninori Morimoto 3942a7930ed4SKuninori Morimoto /* 39435711c979SKuninori Morimoto * check "dai-format = xxx" 39445711c979SKuninori Morimoto * or "[prefix]format = xxx" 3945a7930ed4SKuninori Morimoto * SND_SOC_DAIFMT_FORMAT_MASK area 3946a7930ed4SKuninori Morimoto */ 39475711c979SKuninori Morimoto ret = of_property_read_string(np, "dai-format", &str); 39485711c979SKuninori Morimoto if (ret < 0) { 3949a7930ed4SKuninori Morimoto snprintf(prop, sizeof(prop), "%sformat", prefix); 3950a7930ed4SKuninori Morimoto ret = of_property_read_string(np, prop, &str); 39515711c979SKuninori Morimoto } 3952a7930ed4SKuninori Morimoto if (ret == 0) { 3953a7930ed4SKuninori Morimoto for (i = 0; i < ARRAY_SIZE(of_fmt_table); i++) { 3954a7930ed4SKuninori Morimoto if (strcmp(str, of_fmt_table[i].name) == 0) { 3955a7930ed4SKuninori Morimoto format |= of_fmt_table[i].val; 3956a7930ed4SKuninori Morimoto break; 3957a7930ed4SKuninori Morimoto } 3958a7930ed4SKuninori Morimoto } 3959a7930ed4SKuninori Morimoto } 3960a7930ed4SKuninori Morimoto 3961a7930ed4SKuninori Morimoto /* 39628c2d6a9fSKuninori Morimoto * check "[prefix]continuous-clock" 3963a7930ed4SKuninori Morimoto * SND_SOC_DAIFMT_CLOCK_MASK area 3964a7930ed4SKuninori Morimoto */ 39658c2d6a9fSKuninori Morimoto snprintf(prop, sizeof(prop), "%scontinuous-clock", prefix); 396651930295SJulia Lawall if (of_property_read_bool(np, prop)) 39678c2d6a9fSKuninori Morimoto format |= SND_SOC_DAIFMT_CONT; 39688c2d6a9fSKuninori Morimoto else 39698c2d6a9fSKuninori Morimoto format |= SND_SOC_DAIFMT_GATED; 3970a7930ed4SKuninori Morimoto 3971a7930ed4SKuninori Morimoto /* 3972a7930ed4SKuninori Morimoto * check "[prefix]bitclock-inversion" 3973a7930ed4SKuninori Morimoto * check "[prefix]frame-inversion" 3974a7930ed4SKuninori Morimoto * SND_SOC_DAIFMT_INV_MASK area 3975a7930ed4SKuninori Morimoto */ 3976a7930ed4SKuninori Morimoto snprintf(prop, sizeof(prop), "%sbitclock-inversion", prefix); 3977a7930ed4SKuninori Morimoto bit = !!of_get_property(np, prop, NULL); 3978a7930ed4SKuninori Morimoto 3979a7930ed4SKuninori Morimoto snprintf(prop, sizeof(prop), "%sframe-inversion", prefix); 3980a7930ed4SKuninori Morimoto frame = !!of_get_property(np, prop, NULL); 3981a7930ed4SKuninori Morimoto 3982a7930ed4SKuninori Morimoto switch ((bit << 4) + frame) { 3983a7930ed4SKuninori Morimoto case 0x11: 3984a7930ed4SKuninori Morimoto format |= SND_SOC_DAIFMT_IB_IF; 3985a7930ed4SKuninori Morimoto break; 3986a7930ed4SKuninori Morimoto case 0x10: 3987a7930ed4SKuninori Morimoto format |= SND_SOC_DAIFMT_IB_NF; 3988a7930ed4SKuninori Morimoto break; 3989a7930ed4SKuninori Morimoto case 0x01: 3990a7930ed4SKuninori Morimoto format |= SND_SOC_DAIFMT_NB_IF; 3991a7930ed4SKuninori Morimoto break; 3992a7930ed4SKuninori Morimoto default: 3993a7930ed4SKuninori Morimoto /* SND_SOC_DAIFMT_NB_NF is default */ 3994a7930ed4SKuninori Morimoto break; 3995a7930ed4SKuninori Morimoto } 3996a7930ed4SKuninori Morimoto 3997a7930ed4SKuninori Morimoto /* 3998a7930ed4SKuninori Morimoto * check "[prefix]bitclock-master" 3999a7930ed4SKuninori Morimoto * check "[prefix]frame-master" 4000a7930ed4SKuninori Morimoto * SND_SOC_DAIFMT_MASTER_MASK area 4001a7930ed4SKuninori Morimoto */ 4002a7930ed4SKuninori Morimoto snprintf(prop, sizeof(prop), "%sbitclock-master", prefix); 4003a7930ed4SKuninori Morimoto bit = !!of_get_property(np, prop, NULL); 4004389cb834SJyri Sarha if (bit && bitclkmaster) 4005389cb834SJyri Sarha *bitclkmaster = of_parse_phandle(np, prop, 0); 4006a7930ed4SKuninori Morimoto 4007a7930ed4SKuninori Morimoto snprintf(prop, sizeof(prop), "%sframe-master", prefix); 4008a7930ed4SKuninori Morimoto frame = !!of_get_property(np, prop, NULL); 4009389cb834SJyri Sarha if (frame && framemaster) 4010389cb834SJyri Sarha *framemaster = of_parse_phandle(np, prop, 0); 4011a7930ed4SKuninori Morimoto 4012a7930ed4SKuninori Morimoto switch ((bit << 4) + frame) { 4013a7930ed4SKuninori Morimoto case 0x11: 4014a7930ed4SKuninori Morimoto format |= SND_SOC_DAIFMT_CBM_CFM; 4015a7930ed4SKuninori Morimoto break; 4016a7930ed4SKuninori Morimoto case 0x10: 4017a7930ed4SKuninori Morimoto format |= SND_SOC_DAIFMT_CBM_CFS; 4018a7930ed4SKuninori Morimoto break; 4019a7930ed4SKuninori Morimoto case 0x01: 4020a7930ed4SKuninori Morimoto format |= SND_SOC_DAIFMT_CBS_CFM; 4021a7930ed4SKuninori Morimoto break; 4022a7930ed4SKuninori Morimoto default: 4023a7930ed4SKuninori Morimoto format |= SND_SOC_DAIFMT_CBS_CFS; 4024a7930ed4SKuninori Morimoto break; 4025a7930ed4SKuninori Morimoto } 4026a7930ed4SKuninori Morimoto 4027a7930ed4SKuninori Morimoto return format; 4028a7930ed4SKuninori Morimoto } 4029a7930ed4SKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_of_parse_daifmt); 4030a7930ed4SKuninori Morimoto 4031a180e8b9SKuninori Morimoto int snd_soc_get_dai_id(struct device_node *ep) 4032a180e8b9SKuninori Morimoto { 4033a180e8b9SKuninori Morimoto struct snd_soc_component *pos; 4034a180e8b9SKuninori Morimoto struct device_node *node; 4035a180e8b9SKuninori Morimoto int ret; 4036a180e8b9SKuninori Morimoto 4037a180e8b9SKuninori Morimoto node = of_graph_get_port_parent(ep); 4038a180e8b9SKuninori Morimoto 4039a180e8b9SKuninori Morimoto /* 4040a180e8b9SKuninori Morimoto * For example HDMI case, HDMI has video/sound port, 4041a180e8b9SKuninori Morimoto * but ALSA SoC needs sound port number only. 4042a180e8b9SKuninori Morimoto * Thus counting HDMI DT port/endpoint doesn't work. 4043a180e8b9SKuninori Morimoto * Then, it should have .of_xlate_dai_id 4044a180e8b9SKuninori Morimoto */ 4045a180e8b9SKuninori Morimoto ret = -ENOTSUPP; 4046a180e8b9SKuninori Morimoto mutex_lock(&client_mutex); 4047a180e8b9SKuninori Morimoto list_for_each_entry(pos, &component_list, list) { 4048a180e8b9SKuninori Morimoto struct device_node *component_of_node = pos->dev->of_node; 4049a180e8b9SKuninori Morimoto 4050a180e8b9SKuninori Morimoto if (!component_of_node && pos->dev->parent) 4051a180e8b9SKuninori Morimoto component_of_node = pos->dev->parent->of_node; 4052a180e8b9SKuninori Morimoto 4053a180e8b9SKuninori Morimoto if (component_of_node != node) 4054a180e8b9SKuninori Morimoto continue; 4055a180e8b9SKuninori Morimoto 4056a180e8b9SKuninori Morimoto if (pos->driver->of_xlate_dai_id) 4057a180e8b9SKuninori Morimoto ret = pos->driver->of_xlate_dai_id(pos, ep); 4058a180e8b9SKuninori Morimoto 4059a180e8b9SKuninori Morimoto break; 4060a180e8b9SKuninori Morimoto } 4061a180e8b9SKuninori Morimoto mutex_unlock(&client_mutex); 4062a180e8b9SKuninori Morimoto 4063c0a480d1STony Lindgren of_node_put(node); 4064c0a480d1STony Lindgren 4065a180e8b9SKuninori Morimoto return ret; 4066a180e8b9SKuninori Morimoto } 4067a180e8b9SKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_get_dai_id); 4068a180e8b9SKuninori Morimoto 40691ad8ec53SKuninori Morimoto int snd_soc_get_dai_name(struct of_phandle_args *args, 4070cb470087SKuninori Morimoto const char **dai_name) 4071cb470087SKuninori Morimoto { 4072cb470087SKuninori Morimoto struct snd_soc_component *pos; 40733e0aa8d8SJyri Sarha struct device_node *component_of_node; 407493b0f3eeSJean-Francois Moine int ret = -EPROBE_DEFER; 4075cb470087SKuninori Morimoto 4076cb470087SKuninori Morimoto mutex_lock(&client_mutex); 4077cb470087SKuninori Morimoto list_for_each_entry(pos, &component_list, list) { 40783e0aa8d8SJyri Sarha component_of_node = pos->dev->of_node; 40793e0aa8d8SJyri Sarha if (!component_of_node && pos->dev->parent) 40803e0aa8d8SJyri Sarha component_of_node = pos->dev->parent->of_node; 40813e0aa8d8SJyri Sarha 40823e0aa8d8SJyri Sarha if (component_of_node != args->np) 4083cb470087SKuninori Morimoto continue; 4084cb470087SKuninori Morimoto 40856833c452SKuninori Morimoto if (pos->driver->of_xlate_dai_name) { 408693b0f3eeSJean-Francois Moine ret = pos->driver->of_xlate_dai_name(pos, 408793b0f3eeSJean-Francois Moine args, 408893b0f3eeSJean-Francois Moine dai_name); 40896833c452SKuninori Morimoto } else { 409058bf4179SKuninori Morimoto struct snd_soc_dai *dai; 40916833c452SKuninori Morimoto int id = -1; 40926833c452SKuninori Morimoto 409393b0f3eeSJean-Francois Moine switch (args->args_count) { 40946833c452SKuninori Morimoto case 0: 40956833c452SKuninori Morimoto id = 0; /* same as dai_drv[0] */ 40966833c452SKuninori Morimoto break; 40976833c452SKuninori Morimoto case 1: 409893b0f3eeSJean-Francois Moine id = args->args[0]; 40996833c452SKuninori Morimoto break; 41006833c452SKuninori Morimoto default: 41016833c452SKuninori Morimoto /* not supported */ 4102cb470087SKuninori Morimoto break; 4103cb470087SKuninori Morimoto } 4104cb470087SKuninori Morimoto 41056833c452SKuninori Morimoto if (id < 0 || id >= pos->num_dai) { 41066833c452SKuninori Morimoto ret = -EINVAL; 41073dcba280SNicolin Chen continue; 41086833c452SKuninori Morimoto } 4109e41975edSXiubo Li 4110e41975edSXiubo Li ret = 0; 4111e41975edSXiubo Li 411258bf4179SKuninori Morimoto /* find target DAI */ 411358bf4179SKuninori Morimoto list_for_each_entry(dai, &pos->dai_list, list) { 411458bf4179SKuninori Morimoto if (id == 0) 411558bf4179SKuninori Morimoto break; 411658bf4179SKuninori Morimoto id--; 411758bf4179SKuninori Morimoto } 411858bf4179SKuninori Morimoto 411958bf4179SKuninori Morimoto *dai_name = dai->driver->name; 4120e41975edSXiubo Li if (!*dai_name) 4121e41975edSXiubo Li *dai_name = pos->name; 41226833c452SKuninori Morimoto } 41236833c452SKuninori Morimoto 4124cb470087SKuninori Morimoto break; 4125cb470087SKuninori Morimoto } 4126cb470087SKuninori Morimoto mutex_unlock(&client_mutex); 412793b0f3eeSJean-Francois Moine return ret; 412893b0f3eeSJean-Francois Moine } 41291ad8ec53SKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_get_dai_name); 413093b0f3eeSJean-Francois Moine 413193b0f3eeSJean-Francois Moine int snd_soc_of_get_dai_name(struct device_node *of_node, 413293b0f3eeSJean-Francois Moine const char **dai_name) 413393b0f3eeSJean-Francois Moine { 413493b0f3eeSJean-Francois Moine struct of_phandle_args args; 413593b0f3eeSJean-Francois Moine int ret; 413693b0f3eeSJean-Francois Moine 413793b0f3eeSJean-Francois Moine ret = of_parse_phandle_with_args(of_node, "sound-dai", 413893b0f3eeSJean-Francois Moine "#sound-dai-cells", 0, &args); 413993b0f3eeSJean-Francois Moine if (ret) 414093b0f3eeSJean-Francois Moine return ret; 414193b0f3eeSJean-Francois Moine 414293b0f3eeSJean-Francois Moine ret = snd_soc_get_dai_name(&args, dai_name); 4143cb470087SKuninori Morimoto 4144cb470087SKuninori Morimoto of_node_put(args.np); 4145cb470087SKuninori Morimoto 4146cb470087SKuninori Morimoto return ret; 4147cb470087SKuninori Morimoto } 4148cb470087SKuninori Morimoto EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_name); 4149cb470087SKuninori Morimoto 415093b0f3eeSJean-Francois Moine /* 415194685763SSylwester Nawrocki * snd_soc_of_put_dai_link_codecs - Dereference device nodes in the codecs array 415294685763SSylwester Nawrocki * @dai_link: DAI link 415394685763SSylwester Nawrocki * 415494685763SSylwester Nawrocki * Dereference device nodes acquired by snd_soc_of_get_dai_link_codecs(). 415594685763SSylwester Nawrocki */ 415694685763SSylwester Nawrocki void snd_soc_of_put_dai_link_codecs(struct snd_soc_dai_link *dai_link) 415794685763SSylwester Nawrocki { 415894685763SSylwester Nawrocki struct snd_soc_dai_link_component *component = dai_link->codecs; 415994685763SSylwester Nawrocki int index; 416094685763SSylwester Nawrocki 416194685763SSylwester Nawrocki for (index = 0; index < dai_link->num_codecs; index++, component++) { 416294685763SSylwester Nawrocki if (!component->of_node) 416394685763SSylwester Nawrocki break; 416494685763SSylwester Nawrocki of_node_put(component->of_node); 416594685763SSylwester Nawrocki component->of_node = NULL; 416694685763SSylwester Nawrocki } 416794685763SSylwester Nawrocki } 416894685763SSylwester Nawrocki EXPORT_SYMBOL_GPL(snd_soc_of_put_dai_link_codecs); 416994685763SSylwester Nawrocki 417094685763SSylwester Nawrocki /* 417193b0f3eeSJean-Francois Moine * snd_soc_of_get_dai_link_codecs - Parse a list of CODECs in the devicetree 417293b0f3eeSJean-Francois Moine * @dev: Card device 417393b0f3eeSJean-Francois Moine * @of_node: Device node 417493b0f3eeSJean-Francois Moine * @dai_link: DAI link 417593b0f3eeSJean-Francois Moine * 417693b0f3eeSJean-Francois Moine * Builds an array of CODEC DAI components from the DAI link property 417793b0f3eeSJean-Francois Moine * 'sound-dai'. 417893b0f3eeSJean-Francois Moine * The array is set in the DAI link and the number of DAIs is set accordingly. 417994685763SSylwester Nawrocki * The device nodes in the array (of_node) must be dereferenced by calling 418094685763SSylwester Nawrocki * snd_soc_of_put_dai_link_codecs() on @dai_link. 418193b0f3eeSJean-Francois Moine * 418293b0f3eeSJean-Francois Moine * Returns 0 for success 418393b0f3eeSJean-Francois Moine */ 418493b0f3eeSJean-Francois Moine int snd_soc_of_get_dai_link_codecs(struct device *dev, 418593b0f3eeSJean-Francois Moine struct device_node *of_node, 418693b0f3eeSJean-Francois Moine struct snd_soc_dai_link *dai_link) 418793b0f3eeSJean-Francois Moine { 418893b0f3eeSJean-Francois Moine struct of_phandle_args args; 418993b0f3eeSJean-Francois Moine struct snd_soc_dai_link_component *component; 419093b0f3eeSJean-Francois Moine char *name; 419193b0f3eeSJean-Francois Moine int index, num_codecs, ret; 419293b0f3eeSJean-Francois Moine 419393b0f3eeSJean-Francois Moine /* Count the number of CODECs */ 419493b0f3eeSJean-Francois Moine name = "sound-dai"; 419593b0f3eeSJean-Francois Moine num_codecs = of_count_phandle_with_args(of_node, name, 419693b0f3eeSJean-Francois Moine "#sound-dai-cells"); 419793b0f3eeSJean-Francois Moine if (num_codecs <= 0) { 419893b0f3eeSJean-Francois Moine if (num_codecs == -ENOENT) 419993b0f3eeSJean-Francois Moine dev_err(dev, "No 'sound-dai' property\n"); 420093b0f3eeSJean-Francois Moine else 420193b0f3eeSJean-Francois Moine dev_err(dev, "Bad phandle in 'sound-dai'\n"); 420293b0f3eeSJean-Francois Moine return num_codecs; 420393b0f3eeSJean-Francois Moine } 420493b0f3eeSJean-Francois Moine component = devm_kzalloc(dev, 420593b0f3eeSJean-Francois Moine sizeof *component * num_codecs, 420693b0f3eeSJean-Francois Moine GFP_KERNEL); 420793b0f3eeSJean-Francois Moine if (!component) 420893b0f3eeSJean-Francois Moine return -ENOMEM; 420993b0f3eeSJean-Francois Moine dai_link->codecs = component; 421093b0f3eeSJean-Francois Moine dai_link->num_codecs = num_codecs; 421193b0f3eeSJean-Francois Moine 421293b0f3eeSJean-Francois Moine /* Parse the list */ 421393b0f3eeSJean-Francois Moine for (index = 0, component = dai_link->codecs; 421493b0f3eeSJean-Francois Moine index < dai_link->num_codecs; 421593b0f3eeSJean-Francois Moine index++, component++) { 421693b0f3eeSJean-Francois Moine ret = of_parse_phandle_with_args(of_node, name, 421793b0f3eeSJean-Francois Moine "#sound-dai-cells", 421893b0f3eeSJean-Francois Moine index, &args); 421993b0f3eeSJean-Francois Moine if (ret) 422093b0f3eeSJean-Francois Moine goto err; 422193b0f3eeSJean-Francois Moine component->of_node = args.np; 422293b0f3eeSJean-Francois Moine ret = snd_soc_get_dai_name(&args, &component->dai_name); 422393b0f3eeSJean-Francois Moine if (ret < 0) 422493b0f3eeSJean-Francois Moine goto err; 422593b0f3eeSJean-Francois Moine } 422693b0f3eeSJean-Francois Moine return 0; 422793b0f3eeSJean-Francois Moine err: 422894685763SSylwester Nawrocki snd_soc_of_put_dai_link_codecs(dai_link); 422993b0f3eeSJean-Francois Moine dai_link->codecs = NULL; 423093b0f3eeSJean-Francois Moine dai_link->num_codecs = 0; 423193b0f3eeSJean-Francois Moine return ret; 423293b0f3eeSJean-Francois Moine } 423393b0f3eeSJean-Francois Moine EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_link_codecs); 423493b0f3eeSJean-Francois Moine 4235c9b3a40fSTakashi Iwai static int __init snd_soc_init(void) 4236db2a4165SFrank Mandarino { 42376553bf06SLars-Peter Clausen snd_soc_debugfs_init(); 4238fb257897SMark Brown snd_soc_util_init(); 4239fb257897SMark Brown 4240db2a4165SFrank Mandarino return platform_driver_register(&soc_driver); 4241db2a4165SFrank Mandarino } 42424abe8e16SMark Brown module_init(snd_soc_init); 4243db2a4165SFrank Mandarino 42447d8c16a6SMark Brown static void __exit snd_soc_exit(void) 4245db2a4165SFrank Mandarino { 4246fb257897SMark Brown snd_soc_util_exit(); 42476553bf06SLars-Peter Clausen snd_soc_debugfs_exit(); 4248fb257897SMark Brown 4249db2a4165SFrank Mandarino platform_driver_unregister(&soc_driver); 4250db2a4165SFrank Mandarino } 4251db2a4165SFrank Mandarino module_exit(snd_soc_exit); 4252db2a4165SFrank Mandarino 4253db2a4165SFrank Mandarino /* Module information */ 4254d331124dSLiam Girdwood MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk"); 4255db2a4165SFrank Mandarino MODULE_DESCRIPTION("ALSA SoC Core"); 4256db2a4165SFrank Mandarino MODULE_LICENSE("GPL"); 4257db2a4165SFrank Mandarino MODULE_ALIAS("platform:soc-audio"); 4258