cregit-Linux how code gets into the kernel

Release 4.7 sound/soc/soc-core.c

Directory: sound/soc
/*
 * soc-core.c  --  ALSA SoC Audio Layer
 *
 * Copyright 2005 Wolfson Microelectronics PLC.
 * Copyright 2005 Openedhand Ltd.
 * Copyright (C) 2010 Slimlogic Ltd.
 * Copyright (C) 2010 Texas Instruments Inc.
 *
 * Author: Liam Girdwood <lrg@slimlogic.co.uk>
 *         with code, comments and ideas from :-
 *         Richard Purdie <richard@openedhand.com>
 *
 *  This program is free software; you can redistribute  it and/or modify it
 *  under  the terms of  the GNU General  Public License as published by the
 *  Free Software Foundation;  either version 2 of the  License, or (at your
 *  option) any later version.
 *
 *  TODO:
 *   o Add hw rules to enforce rates, etc.
 *   o More testing with other codecs/machines.
 *   o Add more codecs and platforms to ensure good API coverage.
 *   o Support TDM on PCM and I2S
 */

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/bitops.h>
#include <linux/debugfs.h>
#include <linux/platform_device.h>
#include <linux/pinctrl/consumer.h>
#include <linux/ctype.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dpcm.h>
#include <sound/soc-topology.h>
#include <sound/initval.h>


#define CREATE_TRACE_POINTS
#include <trace/events/asoc.h>


#define NAME_SIZE	32

#ifdef CONFIG_DEBUG_FS

struct dentry *snd_soc_debugfs_root;

EXPORT_SYMBOL_GPL(snd_soc_debugfs_root);
#endif

static DEFINE_MUTEX(client_mutex);
static LIST_HEAD(platform_list);
static LIST_HEAD(codec_list);
static LIST_HEAD(component_list);

/*
 * This is a timeout to do a DAPM powerdown after a stream is closed().
 * It can be used to eliminate pops between different playback streams, e.g.
 * between two audio tracks.
 */

static int pmdown_time = 5000;
module_param(pmdown_time, int, 0);
MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)");

/* returns the minimum number of bytes needed to represent
 * a particular given value */

static int min_bytes_needed(unsigned long val) { int c = 0; int i; for (i = (sizeof val * 8) - 1; i >= 0; --i, ++c) if (val & (1UL << i)) break; c = (sizeof val * 8) - c; if (!c || (c % 8)) c = (c + 8) / 8; else c /= 8; return c; }

Contributors

PersonTokensPropCommitsCommitProp
dimitris papastamosdimitris papastamos92100.00%1100.00%
Total92100.00%1100.00%

/* fill buf which is 'len' bytes with a formatted * string of the form 'reg: value\n' */
static int format_register_str(struct snd_soc_codec *codec, unsigned int reg, char *buf, size_t len) { int wordsize = min_bytes_needed(codec->driver->reg_cache_size) * 2; int regsize = codec->driver->reg_word_size * 2; int ret; /* +2 for ': ' and + 1 for '\n' */ if (wordsize + regsize + 2 + 1 != len) return -EINVAL; sprintf(buf, "%.*x: ", wordsize, reg); buf += wordsize + 2; ret = snd_soc_read(codec, reg); if (ret < 0) memset(buf, 'X', regsize); else sprintf(buf, "%.*x", regsize, ret); buf[regsize] = '\n'; /* no NUL-termination needed */ return 0; }

Contributors

PersonTokensPropCommitsCommitProp
dimitris papastamosdimitris papastamos10076.34%133.33%
takashi iwaitakashi iwai2619.85%133.33%
stephen warrenstephen warren53.82%133.33%
Total131100.00%3100.00%

/* codec register dump */
static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf, size_t count, loff_t pos) { int i, step = 1; int wordsize, regsize; int len; size_t total = 0; loff_t p = 0; wordsize = min_bytes_needed(codec->driver->reg_cache_size) * 2; regsize = codec->driver->reg_word_size * 2; len = wordsize + regsize + 2 + 1; if (!codec->driver->reg_cache_size) return 0; if (codec->driver->reg_cache_step) step = codec->driver->reg_cache_step; for (i = 0; i < codec->driver->reg_cache_size; i += step) { /* only support larger than PAGE_SIZE bytes debugfs * entries for the default case */ if (p >= pos) { if (total + len >= count - 1) break; format_register_str(codec, i, buf + total, len); total += len; } p += len; } total = min(total, count - 1); return total; }

Contributors

PersonTokensPropCommitsCommitProp
dimitris papastamosdimitris papastamos8345.60%225.00%
mark brownmark brown5128.02%225.00%
frank mandarinofrank mandarino3418.68%112.50%
liam girdwoodliam girdwood84.40%112.50%
stephen warrenstephen warren52.75%112.50%
lars-peter clausenlars-peter clausen10.55%112.50%
Total182100.00%8100.00%


static ssize_t codec_reg_show(struct device *dev, struct device_attribute *attr, char *buf) { struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev); return soc_codec_reg_show(rtd->codec, buf, PAGE_SIZE, 0); }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown3784.09%360.00%
dimitris papastamosdimitris papastamos49.09%120.00%
liam girdwoodliam girdwood36.82%120.00%
Total44100.00%5100.00%

static DEVICE_ATTR(codec_reg, 0444, codec_reg_show, NULL);
static ssize_t pmdown_time_show(struct device *dev, struct device_attribute *attr, char *buf) { struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev); return sprintf(buf, "%ld\n", rtd->pmdown_time); }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown3992.86%375.00%
liam girdwoodliam girdwood37.14%125.00%
Total42100.00%4100.00%


static ssize_t pmdown_time_set(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev); int ret; ret = kstrtol(buf, 10, &rtd->pmdown_time); if (ret) return ret; return count; }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown5793.44%360.00%
liam girdwoodliam girdwood34.92%120.00%
jingoo hanjingoo han11.64%120.00%
Total61100.00%5100.00%

static DEVICE_ATTR(pmdown_time, 0644, pmdown_time_show, pmdown_time_set); static struct attribute *soc_dev_attrs[] = { &dev_attr_codec_reg.attr, &dev_attr_pmdown_time.attr, NULL };
static umode_t soc_dev_attr_is_visible(struct kobject *kobj, struct attribute *attr, int idx) { struct device *dev = kobj_to_dev(kobj); struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev); if (attr == &dev_attr_pmdown_time.attr) return attr->mode; /* always visible */ return rtd->codec ? attr->mode : 0; /* enabled only with codec */ }

Contributors

PersonTokensPropCommitsCommitProp
takashi iwaitakashi iwai66100.00%1100.00%
Total66100.00%1100.00%

static const struct attribute_group soc_dapm_dev_group = { .attrs = soc_dapm_dev_attrs, .is_visible = soc_dev_attr_is_visible, }; static const struct attribute_group soc_dev_roup = { .attrs = soc_dev_attrs, .is_visible = soc_dev_attr_is_visible, }; static const struct attribute_group *soc_dev_attr_groups[] = { &soc_dapm_dev_group, &soc_dev_roup, NULL }; #ifdef CONFIG_DEBUG_FS
static ssize_t codec_reg_read_file(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { ssize_t ret; struct snd_soc_codec *codec = file->private_data; char *buf; if (*ppos < 0 || !count) return -EINVAL; buf = kmalloc(count, GFP_KERNEL); if (!buf) return -ENOMEM; ret = soc_codec_reg_show(codec, buf, count, *ppos); if (ret >= 0) { if (copy_to_user(user_buf, buf, ret)) { kfree(buf); return -EFAULT; } *ppos += ret; } kfree(buf); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown8364.84%266.67%
dimitris papastamosdimitris papastamos4535.16%133.33%
Total128100.00%3100.00%


static ssize_t codec_reg_write_file(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { char buf[32]; size_t buf_size; char *start = buf; unsigned long reg, value; struct snd_soc_codec *codec = file->private_data; int ret; buf_size = min(count, (sizeof(buf)-1)); if (copy_from_user(buf, user_buf, buf_size)) return -EFAULT; buf[buf_size] = 0; while (*start == ' ') start++; reg = simple_strtoul(start, &start, 16); while (*start == ' ') start++; ret = kstrtoul(start, 16, &value); if (ret) return ret; /* Userspace has been fiddling around behind the kernel's back */ add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE); snd_soc_write(codec, reg, value); return buf_size; }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown11770.48%440.00%
frank mandarinofrank mandarino2213.25%110.00%
liam girdwoodliam girdwood127.23%110.00%
jingoo hanjingoo han116.63%110.00%
rusty russellrusty russell21.20%110.00%
dimitris papastamosdimitris papastamos10.60%110.00%
stephen boydstephen boyd10.60%110.00%
Total166100.00%10100.00%

static const struct file_operations codec_reg_fops = { .open = simple_open, .read = codec_reg_read_file, .write = codec_reg_write_file, .llseek = default_llseek, };
static void soc_init_component_debugfs(struct snd_soc_component *component) { if (!component->card->debugfs_card_root) return; if (component->debugfs_prefix) { char *name; name = kasprintf(GFP_KERNEL, "%s:%s", component->debugfs_prefix, component->name); if (name) { component->debugfs_root = debugfs_create_dir(name, component->card->debugfs_card_root); kfree(name); } } else { component->debugfs_root = debugfs_create_dir(component->name, component->card->debugfs_card_root); } if (!component->debugfs_root) { dev_warn(component->dev, "ASoC: Failed to create component debugfs directory\n"); return; } snd_soc_dapm_debugfs_init(snd_soc_component_get_dapm(component), component->debugfs_root); if (component->init_debugfs) component->init_debugfs(component); }

Contributors

PersonTokensPropCommitsCommitProp
lars-peter clausenlars-peter clausen7352.14%541.67%
russell kingrussell king3021.43%18.33%
mark brownmark brown1510.71%325.00%
liam girdwoodliam girdwood128.57%216.67%
frank mandarinofrank mandarino107.14%18.33%
Total140100.00%12100.00%


static void soc_cleanup_component_debugfs(struct snd_soc_component *component) { debugfs_remove_recursive(component->debugfs_root); }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown950.00%125.00%
lars-peter clausenlars-peter clausen527.78%125.00%
frank mandarinofrank mandarino316.67%125.00%
liam girdwoodliam girdwood15.56%125.00%
Total18100.00%4100.00%


static void soc_init_codec_debugfs(struct snd_soc_component *component) { struct snd_soc_codec *codec = snd_soc_component_to_codec(component); codec->debugfs_reg = debugfs_create_file("codec_reg", 0644, codec->component.debugfs_root, codec, &codec_reg_fops); if (!codec->debugfs_reg) dev_warn(codec->dev, "ASoC: Failed to create codec register debugfs file\n"); }

Contributors

PersonTokensPropCommitsCommitProp
sebastien guiriecsebastien guiriec3152.54%120.00%
lars-peter clausenlars-peter clausen2644.07%360.00%
russell kingrussell king23.39%120.00%
Total59100.00%5100.00%


static ssize_t codec_list_read_file(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL); ssize_t len, ret = 0; struct snd_soc_codec *codec; if (!buf) return -ENOMEM; mutex_lock(&client_mutex); list_for_each_entry(codec, &codec_list, list) { len = snprintf(buf + ret, PAGE_SIZE - ret, "%s\n", codec->component.name); if (len >= 0) ret += len; if (ret > PAGE_SIZE) { ret = PAGE_SIZE; break; } } mutex_unlock(&client_mutex); if (ret >= 0) ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); kfree(buf); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown13388.67%240.00%
lars-peter clausenlars-peter clausen149.33%240.00%
sebastien guiriecsebastien guiriec32.00%120.00%
Total150100.00%5100.00%

static const struct file_operations codec_list_fops = { .read = codec_list_read_file, .llseek = default_llseek,/* read accesses f_pos */ };
static ssize_t dai_list_read_file(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL); ssize_t len, ret = 0; struct snd_soc_component *component; struct snd_soc_dai *dai; if (!buf) return -ENOMEM; mutex_lock(&client_mutex); list_for_each_entry(component, &component_list, list) { list_for_each_entry(dai, &component->dai_list, list) { len = snprintf(buf + ret, PAGE_SIZE - ret, "%s\n", dai->name); if (len >= 0) ret += len; if (ret > PAGE_SIZE) { ret = PAGE_SIZE; break; } } } mutex_unlock(&client_mutex); ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); kfree(buf); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown12982.17%250.00%
lars-peter clausenlars-peter clausen2817.83%250.00%
Total157100.00%4100.00%

static const struct file_operations dai_list_fops = { .read = dai_list_read_file, .llseek = default_llseek,/* read accesses f_pos */ };
static ssize_t platform_list_read_file(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL); ssize_t len, ret = 0; struct snd_soc_platform *platform; if (!buf) return -ENOMEM; mutex_lock(&client_mutex); list_for_each_entry(platform, &platform_list, list) { len = snprintf(buf + ret, PAGE_SIZE - ret, "%s\n", platform->component.name); if (len >= 0) ret += len; if (ret > PAGE_SIZE) { ret = PAGE_SIZE; break; } } mutex_unlock(&client_mutex); ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); kfree(buf); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown13090.28%250.00%
lars-peter clausenlars-peter clausen149.72%250.00%
Total144100.00%4100.00%

static const struct file_operations platform_list_fops = { .read = platform_list_read_file, .llseek = default_llseek,/* read accesses f_pos */ };
static void soc_init_card_debugfs(struct snd_soc_card *card) { if (!snd_soc_debugfs_root) return; card->debugfs_card_root = debugfs_create_dir(card->name, snd_soc_debugfs_root); if (!card->debugfs_card_root) { dev_warn(card->dev, "ASoC: Failed to create card debugfs directory\n"); return; } card->debugfs_pop_time = debugfs_create_u32("dapm_pop_time", 0644, card->debugfs_card_root, &card->pop_time); if (!card->debugfs_pop_time) dev_warn(card->dev, "ASoC: Failed to create pop time debugfs file\n"); }

Contributors

PersonTokensPropCommitsCommitProp
jarkko nikulajarkko nikula7689.41%233.33%
lars-peter clausenlars-peter clausen67.06%116.67%
mark brownmark brown11.18%116.67%
liam girdwoodliam girdwood11.18%116.67%
lothar wassmannlothar wassmann11.18%116.67%
Total85100.00%6100.00%


static void soc_cleanup_card_debugfs(struct snd_soc_card *card) { debugfs_remove_recursive(card->debugfs_card_root); }

Contributors

PersonTokensPropCommitsCommitProp
jarkko nikulajarkko nikula18100.00%1100.00%
Total18100.00%1100.00%


static void snd_soc_debugfs_init(void) { snd_soc_debugfs_root = debugfs_create_dir("asoc", NULL); if (IS_ERR(snd_soc_debugfs_root) || !snd_soc_debugfs_root) { pr_warn("ASoC: Failed to create debugfs directory\n"); snd_soc_debugfs_root = NULL; return; } if (!debugfs_create_file("codecs", 0444, snd_soc_debugfs_root, NULL, &codec_list_fops)) pr_warn("ASoC: Failed to create CODEC list debugfs file\n"); if (!debugfs_create_file("dais", 0444, snd_soc_debugfs_root, NULL, &dai_list_fops)) pr_warn("ASoC: Failed to create DAI list debugfs file\n"); if (!debugfs_create_file("platforms", 0444, snd_soc_debugfs_root, NULL, &platform_list_fops)) pr_warn("ASoC: Failed to create platform list debugfs file\n"); }

Contributors

PersonTokensPropCommitsCommitProp
lars-peter clausenlars-peter clausen105100.00%1100.00%
Total105100.00%1100.00%


static void snd_soc_debugfs_exit(void) { debugfs_remove_recursive(snd_soc_debugfs_root); }

Contributors

PersonTokensPropCommitsCommitProp
lars-peter clausenlars-peter clausen13100.00%1100.00%
Total13100.00%1100.00%

#else #define soc_init_codec_debugfs NULL
static inline void soc_init_component_debugfs( struct snd_soc_component *component) { }

Contributors

PersonTokensPropCommitsCommitProp
sebastien guiriecsebastien guiriec872.73%150.00%
lars-peter clausenlars-peter clausen327.27%150.00%
Total11100.00%2100.00%


static inline void soc_cleanup_component_debugfs( struct snd_soc_component *component) { }

Contributors

PersonTokensPropCommitsCommitProp
sebastien guiriecsebastien guiriec872.73%150.00%
lars-peter clausenlars-peter clausen327.27%150.00%
Total11100.00%2100.00%


static inline void soc_init_card_debugfs(struct snd_soc_card *card) { }

Contributors

PersonTokensPropCommitsCommitProp
axel linaxel lin11100.00%1100.00%
Total11100.00%1100.00%


static inline void soc_cleanup_card_debugfs(struct snd_soc_card *card) { }

Contributors

PersonTokensPropCommitsCommitProp
axel linaxel lin11100.00%1100.00%
Total11100.00%1100.00%


static inline void snd_soc_debugfs_init(void) { }

Contributors

PersonTokensPropCommitsCommitProp
lars-peter clausenlars-peter clausen8100.00%1100.00%
Total8100.00%1100.00%


static inline void snd_soc_debugfs_exit(void) { }

Contributors

PersonTokensPropCommitsCommitProp
lars-peter clausenlars-peter clausen8100.00%1100.00%
Total8100.00%1100.00%

#endif
struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card, const char *dai_link, int stream) { struct snd_soc_pcm_runtime *rtd; list_for_each_entry(rtd, &card->rtd_list, list) { if (rtd->dai_link->no_pcm && !strcmp(rtd->dai_link->name, dai_link)) return rtd->pcm->streams[stream].substream; } dev_dbg(card->dev, "ASoC: failed to find dai link %s\n", dai_link); return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
liam girdwoodliam girdwood6681.48%266.67%
mengdong linmengdong lin1518.52%133.33%
Total81100.00%3100.00%

EXPORT_SYMBOL_GPL(snd_soc_get_dai_substream);
static struct snd_soc_pcm_runtime *soc_new_pcm_runtime( struct snd_soc_card *card, struct snd_soc_dai_link *dai_link) { struct snd_soc_pcm_runtime *rtd; rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime), GFP_KERNEL); if (!rtd) return NULL; rtd->card = card; rtd->dai_link = dai_link; rtd->codec_dais = kzalloc(sizeof(struct snd_soc_dai *) * dai_link->num_codecs, GFP_KERNEL); if (!rtd->codec_dais) { kfree(rtd); return NULL; } return rtd; }

Contributors

PersonTokensPropCommitsCommitProp
mengdong linmengdong lin6264.58%150.00%
liam girdwoodliam girdwood3435.42%150.00%
Total96100.00%2100.00%


static void soc_free_pcm_runtime(struct snd_soc_pcm_runtime *rtd) { if (rtd && rtd->codec_dais) kfree(rtd->codec_dais); kfree(rtd); }

Contributors

PersonTokensPropCommitsCommitProp
mengdong linmengdong lin2374.19%150.00%
richard fitzgeraldrichard fitzgerald825.81%150.00%
Total31100.00%2100.00%


static void soc_add_pcm_runtime(struct snd_soc_card *card, struct snd_soc_pcm_runtime *rtd) { list_add_tail(&rtd->list, &card->rtd_list); rtd->num = card->num_rtd; card->num_rtd++; }

Contributors

PersonTokensPropCommitsCommitProp
mengdong linmengdong lin2969.05%114.29%
mark brownmark brown511.90%228.57%
liam girdwoodliam girdwood49.52%228.57%
frank mandarinofrank mandarino37.14%114.29%
andrew mortonandrew morton12.38%114.29%
Total42100.00%7100.00%


static void soc_remove_pcm_runtimes(struct snd_soc_card *card) { struct snd_soc_pcm_runtime *rtd, *_rtd; list_for_each_entry_safe(rtd, _rtd, &card->rtd_list, list) { list_del(&rtd->list); soc_free_pcm_runtime(rtd); } card->num_rtd = 0; }

Contributors

PersonTokensPropCommitsCommitProp
mengdong linmengdong lin50100.00%1100.00%
Total50100.00%1100.00%


struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card, const char *dai_link) { struct snd_soc_pcm_runtime *rtd; list_for_each_entry(rtd, &card->rtd_list, list) { if (!strcmp(rtd->dai_link->name, dai_link)) return rtd; } dev_dbg(card->dev, "ASoC: failed to find rtd %s\n", dai_link); return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
liam girdwoodliam girdwood3352.38%266.67%
mengdong linmengdong lin3047.62%133.33%
Total63100.00%3100.00%

EXPORT_SYMBOL_GPL(snd_soc_get_pcm_runtime);
static void codec2codec_close_delayed_work(struct work_struct *work) { /* Currently nothing to do for c2c links * Since c2c links are internal nodes in the DAPM graph and * don't interface with the outside world or application layer * we don't have to do any special handling on close. */ }

Contributors

PersonTokensPropCommitsCommitProp
richard fitzgeraldrichard fitzgerald12100.00%1100.00%
Total12100.00%1100.00%

#ifdef CONFIG_PM_SLEEP /* powers down audio subsystem for suspend */
int snd_soc_suspend(struct device *dev) { struct snd_soc_card *card = dev_get_drvdata(dev); struct snd_soc_codec *codec; struct snd_soc_pcm_runtime *rtd; int i; /* If the card is not initialized yet there is nothing to do */ if (!card->instantiated) return 0; /* Due to the resume being scheduled into a workqueue we could * suspend before that's finished - wait for it to complete. */ snd_power_lock(card->snd_card); snd_power_wait(card->snd_card, SNDRV_CTL_POWER_D0); snd_power_unlock(card->snd_card); /* we're going to block userspace touching us until resume completes */ snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D3hot); /* mute any active DACs */ list_for_each_entry(rtd, &card->rtd_list, list) { if (rtd->dai_link->ignore_suspend) continue; for (i = 0; i < rtd->num_codecs; i++) { struct snd_soc_dai *dai = rtd->codec_dais[i]; struct snd_soc_dai_driver *drv = dai->driver; if (drv->ops->digital_mute && dai->playback_active) drv->ops->digital_mute(dai, 1); } } /* suspend all pcms */ list_for_each_entry(rtd, &card->rtd_list, list) { if (rtd->dai_link->ignore_suspend) continue; snd_pcm_suspend_all(rtd->pcm); } if (card->suspend_pre) card->suspend_pre(card); list_for_each_entry(rtd, &card->rtd_list, list) { struct snd_soc_dai *cpu_dai = rtd->cpu_dai; if (rtd->dai_link->ignore_suspend) continue; if (cpu_dai->driver->suspend && !cpu_dai->driver->bus_control) cpu_dai->driver->suspend(cpu_dai); } /* close any waiting streams */