@@ -1016,6 +1016,9 @@ struct snd_soc_dai_link {
struct snd_soc_dai_link_component *codecs;
unsigned int num_codecs;
+ struct snd_soc_dai_link_component *cpu_dai;
+ unsigned int num_cpu_dai;
+
/*
* You MAY specify the link's platform/PCM/DMA driver, either by
* device name, or by DT/OF node, but not both. Some forms of link
@@ -1239,6 +1242,9 @@ struct snd_soc_pcm_runtime {
struct snd_soc_dai **codec_dais;
unsigned int num_codecs;
+ struct snd_soc_dai **cpu_dais;
+ unsigned int num_cpu_dai;
+
struct delayed_work delayed_work;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_dpcm_root;
@@ -558,12 +558,21 @@ static struct snd_soc_pcm_runtime *soc_new_pcm_runtime(
return NULL;
}
+ rtd->cpu_dais = kzalloc(sizeof(struct snd_soc_dai *) *
+ dai_link->num_cpu_dai, GFP_KERNEL);
+ if (!rtd->cpu_dais) {
+ kfree(rtd->codec_dais);
+ kfree(rtd);
+ return NULL;
+ }
+
return rtd;
}
static void soc_free_pcm_runtime(struct snd_soc_pcm_runtime *rtd)
{
kfree(rtd->codec_dais);
+ kfree(rtd->cpu_dais);
snd_soc_rtdcom_del_all(rtd);
kfree(rtd);
}
@@ -659,13 +668,17 @@ int snd_soc_suspend(struct device *dev)
card->suspend_pre(card);
list_for_each_entry(rtd, &card->rtd_list, list) {
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *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);
+ for (i = 0; i < rtd->num_cpu_dai; i++) {
+ cpu_dai = rtd->cpu_dais[i];
+ if (cpu_dai->driver->suspend &&
+ !cpu_dai->driver->bus_control)
+ cpu_dai->driver->suspend(cpu_dai);
+ }
}
/* close any waiting streams */
@@ -729,16 +742,21 @@ int snd_soc_suspend(struct device *dev)
}
list_for_each_entry(rtd, &card->rtd_list, list) {
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *cpu_dai;
- if (rtd->dai_link->ignore_suspend)
- continue;
+ for (i = 0; i < rtd->num_cpu_dai; i++) {
+ cpu_dai = rtd->cpu_dais[i];
+
+ if (rtd->dai_link->ignore_suspend)
+ continue;
- if (cpu_dai->driver->suspend && cpu_dai->driver->bus_control)
- cpu_dai->driver->suspend(cpu_dai);
+ if (cpu_dai->driver->suspend &&
+ cpu_dai->driver->bus_control)
+ cpu_dai->driver->suspend(cpu_dai);
- /* deactivate pins to sleep state */
- pinctrl_pm_select_sleep_state(cpu_dai->dev);
+ /* deactivate pins to sleep state */
+ pinctrl_pm_select_sleep_state(cpu_dai->dev);
+ }
}
if (card->suspend_post)
@@ -773,13 +791,18 @@ static void soc_resume_deferred(struct work_struct *work)
/* resume control bus DAIs */
list_for_each_entry(rtd, &card->rtd_list, list) {
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *cpu_dai;
if (rtd->dai_link->ignore_suspend)
continue;
- if (cpu_dai->driver->resume && cpu_dai->driver->bus_control)
- cpu_dai->driver->resume(cpu_dai);
+ for (i = 0; i < rtd->num_cpu_dai; i++) {
+ cpu_dai = rtd->cpu_dais[i];
+
+ if (cpu_dai->driver->resume &&
+ cpu_dai->driver->bus_control)
+ cpu_dai->driver->resume(cpu_dai);
+ }
}
list_for_each_entry(component, &card->component_dev_list, card_list) {
@@ -825,8 +848,13 @@ static void soc_resume_deferred(struct work_struct *work)
if (rtd->dai_link->ignore_suspend)
continue;
- if (cpu_dai->driver->resume && !cpu_dai->driver->bus_control)
- cpu_dai->driver->resume(cpu_dai);
+ for (i = 0; i < rtd->num_cpu_dai; i++) {
+ cpu_dai = rtd->cpu_dais[i];
+
+ if (cpu_dai->driver->resume &&
+ !cpu_dai->driver->bus_control)
+ cpu_dai->driver->resume(cpu_dai);
+ }
}
if (card->resume_post)
@@ -848,6 +876,7 @@ int snd_soc_resume(struct device *dev)
struct snd_soc_card *card = dev_get_drvdata(dev);
bool bus_control = false;
struct snd_soc_pcm_runtime *rtd;
+ int i;
/* If the card is not initialized yet there is nothing to do */
if (!card->instantiated)
@@ -856,11 +885,16 @@ int snd_soc_resume(struct device *dev)
/* activate pins from sleep state */
list_for_each_entry(rtd, &card->rtd_list, list) {
struct snd_soc_dai **codec_dais = rtd->codec_dais;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai **cpu_dais = rtd->cpu_dais;
+ struct snd_soc_dai *cpu_dai;
int j;
- if (cpu_dai->active)
- pinctrl_pm_select_default_state(cpu_dai->dev);
+ for (j = 0; j < rtd->num_cpu_dai; j++) {
+ cpu_dai = cpu_dais[j];
+
+ if (cpu_dai->active)
+ pinctrl_pm_select_default_state(cpu_dai->dev);
+ }
for (j = 0; j < rtd->num_codecs; j++) {
struct snd_soc_dai *codec_dai = codec_dais[j];
@@ -876,8 +910,11 @@ int snd_soc_resume(struct device *dev)
* due to I/O costs and anti-pop so handle them out of line.
*/
list_for_each_entry(rtd, &card->rtd_list, list) {
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- bus_control |= cpu_dai->driver->bus_control;
+ for (i = 0; i < rtd->num_cpu_dai; i++) {
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dais[i];
+
+ bus_control |= cpu_dai->driver->bus_control;
+ }
}
if (bus_control) {
dev_dbg(dev, "ASoC: Resuming control bus master immediately\n");
@@ -1022,35 +1059,47 @@ static int soc_bind_dai_link(struct snd_soc_card *card,
{
struct snd_soc_pcm_runtime *rtd;
struct snd_soc_dai_link_component *codecs = dai_link->codecs;
- struct snd_soc_dai_link_component cpu_dai_component;
+ struct snd_soc_dai_link_component *cpu_dai_component;
struct snd_soc_component *component;
- struct snd_soc_dai **codec_dais;
+ struct snd_soc_dai **codec_dais, **cpu_dais;
struct device_node *platform_of_node;
const char *platform_name;
int i;
dev_dbg(card->dev, "ASoC: binding %s\n", dai_link->name);
+ cpu_dai_component = dai_link->cpu_dai;
+
if (soc_is_dai_link_bound(card, dai_link)) {
dev_dbg(card->dev, "ASoC: dai link %s already bound\n",
dai_link->name);
return 0;
}
+ if (dai_link->dynamic && dai_link->num_cpu_dai > 1) {
+ dev_err(card->dev, "ASoC: Multi CPU DAI not supported for FE");
+ return -EINVAL;
+ }
+
rtd = soc_new_pcm_runtime(card, dai_link);
if (!rtd)
return -ENOMEM;
- cpu_dai_component.name = dai_link->cpu_name;
- cpu_dai_component.of_node = dai_link->cpu_of_node;
- cpu_dai_component.dai_name = dai_link->cpu_dai_name;
- rtd->cpu_dai = snd_soc_find_dai(&cpu_dai_component);
- if (!rtd->cpu_dai) {
- dev_info(card->dev, "ASoC: CPU DAI %s not registered\n",
- dai_link->cpu_dai_name);
- goto _err_defer;
+ rtd->num_cpu_dai = dai_link->num_cpu_dai;
+
+ cpu_dais = rtd->cpu_dais;
+ for (i = 0; i < rtd->num_cpu_dai; i++) {
+ cpu_dais[i] = snd_soc_find_dai(&cpu_dai_component[i]);
+ if (!cpu_dais[i]) {
+ dev_err(card->dev, "ASoC: CPU DAI %s not registered\n",
+ cpu_dai_component[i].dai_name);
+ goto _err_defer;
+ }
+ snd_soc_rtdcom_add(rtd, cpu_dais[i]->component);
}
- snd_soc_rtdcom_add(rtd, rtd->cpu_dai->component);
+
+ /* Fill cpu_dai in the runtime data */
+ rtd->cpu_dai = cpu_dais[0];
rtd->num_codecs = dai_link->num_codecs;
@@ -1149,7 +1198,8 @@ static void soc_remove_link_dais(struct snd_soc_card *card,
for (i = 0; i < rtd->num_codecs; i++)
soc_remove_dai(rtd->codec_dais[i], order);
- soc_remove_dai(rtd->cpu_dai, order);
+ for (i = 0; i < rtd->num_cpu_dai; i++)
+ soc_remove_dai(rtd->cpu_dais[i], order);
}
static void soc_remove_link_components(struct snd_soc_card *card,
@@ -1221,6 +1271,30 @@ static int snd_soc_init_multicodec(struct snd_soc_card *card,
return 0;
}
+static int snd_soc_init_single_cpu_dai(struct snd_soc_card *card,
+ struct snd_soc_dai_link *dai_link)
+{
+ if (dai_link->cpu_name || dai_link->cpu_of_node ||
+ dai_link->cpu_dai_name) {
+ dai_link->num_cpu_dai = 1;
+ dai_link->cpu_dai = devm_kzalloc(card->dev,
+ sizeof(struct snd_soc_dai_link_component),
+ GFP_KERNEL);
+
+ if (!dai_link->cpu_dai)
+ return -ENOMEM;
+
+ dai_link->cpu_dai[0].name = dai_link->cpu_name;
+ dai_link->cpu_dai[0].of_node = dai_link->cpu_of_node;
+ dai_link->cpu_dai[0].dai_name = dai_link->cpu_dai_name;
+ } else if (!dai_link->cpu_dai) {
+ dev_err(card->dev, "ASoC: DAI link has no DAIs\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int soc_init_dai_link(struct snd_soc_card *card,
struct snd_soc_dai_link *link)
{
@@ -1232,6 +1306,12 @@ static int soc_init_dai_link(struct snd_soc_card *card,
return ret;
}
+ ret = snd_soc_init_single_cpu_dai(card, link);
+ if (ret) {
+ dev_err(card->dev, "ASoC: failed to init cpu\n");
+ return ret;
+ }
+
for (i = 0; i < link->num_codecs; i++) {
/*
* Codec must be specified by 1 of name or OF node,
@@ -1267,24 +1347,28 @@ static int soc_init_dai_link(struct snd_soc_card *card,
* can be left unspecified, and will be matched based on DAI
* name alone..
*/
- if (link->cpu_name && link->cpu_of_node) {
- dev_err(card->dev,
- "ASoC: Neither/both cpu name/of_node are set for %s\n",
- link->name);
- return -EINVAL;
- }
- /*
- * At least one of CPU DAI name or CPU device name/node must be
- * specified
- */
- if (!link->cpu_dai_name &&
- !(link->cpu_name || link->cpu_of_node)) {
- dev_err(card->dev,
- "ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n",
- link->name);
- return -EINVAL;
- }
+ for (i = 0; i < link->num_cpu_dai; i++) {
+ if (link->cpu_dai[i].name &&
+ link->cpu_dai[i].of_node) {
+ dev_err(card->dev,
+ "ASoC: Neither/both cpu name/of_node are set for %s\n",
+ link->cpu_dai[i].name);
+ return -EINVAL;
+ }
+
+ /*
+ * At least one of CPU DAI name or CPU device name/node must be
+ * specified
+ */
+ if (!link->cpu_dai[i].dai_name &&
+ !(link->cpu_dai[i].name || link->cpu_dai[i].of_node)) {
+ dev_err(card->dev,
+ "ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n",
+ link->name);
+ return -EINVAL;
+ }
+ }
return 0;
}
@@ -1604,6 +1688,9 @@ static int soc_link_dai_widgets(struct snd_soc_card *card,
if (rtd->num_codecs > 1)
dev_warn(card->dev, "ASoC: Multiple codecs not supported yet\n");
+ if (rtd->num_cpu_dai > 1)
+ dev_warn(card->dev, "ASoC: Multiple CPU DAIs not supported yet\n");
+
/* link the DAI widgets */
sink = codec_dai->playback_widget;
source = cpu_dai->capture_widget;
@@ -1638,7 +1725,6 @@ static int soc_probe_link_dais(struct snd_soc_card *card,
struct snd_soc_pcm_runtime *rtd, int order)
{
struct snd_soc_dai_link *dai_link = rtd->dai_link;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
int i, ret;
dev_dbg(card->dev, "ASoC: probe %s dai link %d late %d\n",
@@ -1647,9 +1733,11 @@ static int soc_probe_link_dais(struct snd_soc_card *card,
/* set default power off timeout */
rtd->pmdown_time = pmdown_time;
- ret = soc_probe_dai(cpu_dai, order);
- if (ret)
- return ret;
+ for (i = 0; i < rtd->num_cpu_dai; i++) {
+ ret = soc_probe_dai(rtd->cpu_dais[i], order);
+ if (ret)
+ return ret;
+ }
/* probe the CODEC DAI */
for (i = 0; i < rtd->num_codecs; i++) {
@@ -1685,9 +1773,13 @@ static int soc_probe_link_dais(struct snd_soc_card *card,
soc_dpcm_debugfs_add(rtd);
#endif
- if (cpu_dai->driver->compress_new) {
+ if (rtd->cpu_dais[0]->driver->compress_new) {
+ if (rtd->num_cpu_dai > 1)
+ dev_warn(card->dev,
+ "ASoC: multi-cpu compress dais not supported");
+
/*create compress_device"*/
- ret = cpu_dai->driver->compress_new(rtd, rtd->num);
+ ret = rtd->cpu_dais[0]->driver->compress_new(rtd, rtd->num);
if (ret < 0) {
dev_err(card->dev, "ASoC: can't create compress %s\n",
dai_link->stream_name);
@@ -1703,7 +1795,8 @@ static int soc_probe_link_dais(struct snd_soc_card *card,
dai_link->stream_name, ret);
return ret;
}
- ret = soc_link_dai_pcm_new(&cpu_dai, 1, rtd);
+ ret = soc_link_dai_pcm_new(rtd->cpu_dais,
+ rtd->num_cpu_dai, rtd);
if (ret < 0)
return ret;
ret = soc_link_dai_pcm_new(rtd->codec_dais,
@@ -1840,7 +1933,7 @@ int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd,
unsigned int dai_fmt)
{
struct snd_soc_dai **codec_dais = rtd->codec_dais;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai **cpu_dais = rtd->cpu_dais;
unsigned int i;
int ret;
@@ -1857,34 +1950,44 @@ int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd,
/* Flip the polarity for the "CPU" end of a CODEC<->CODEC link */
/* the component which has non_legacy_dai_naming is Codec */
- if (cpu_dai->codec ||
- cpu_dai->component->driver->non_legacy_dai_naming) {
- unsigned int inv_dai_fmt;
-
- inv_dai_fmt = dai_fmt & ~SND_SOC_DAIFMT_MASTER_MASK;
- switch (dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
- break;
- case SND_SOC_DAIFMT_CBM_CFS:
- inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFM;
- break;
- case SND_SOC_DAIFMT_CBS_CFM:
- inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFS;
- break;
- case SND_SOC_DAIFMT_CBS_CFS:
- inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
- break;
- }
+ for (i = 0; i < rtd->num_cpu_dai; i++) {
+ struct snd_soc_dai *cpu_dai = cpu_dais[i];
+ unsigned int inv_dai_fmt, temp_dai_fmt;
- dai_fmt = inv_dai_fmt;
- }
+ temp_dai_fmt = dai_fmt;
+ if (cpu_dai->codec ||
+ cpu_dai->component->driver->non_legacy_dai_naming) {
- ret = snd_soc_dai_set_fmt(cpu_dai, dai_fmt);
- if (ret != 0 && ret != -ENOTSUPP) {
- dev_warn(cpu_dai->dev,
- "ASoC: Failed to set DAI format: %d\n", ret);
- return ret;
+ inv_dai_fmt = dai_fmt & ~SND_SOC_DAIFMT_MASTER_MASK;
+
+ switch (dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
+ break;
+
+ case SND_SOC_DAIFMT_CBM_CFS:
+ inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFM;
+ break;
+
+ case SND_SOC_DAIFMT_CBS_CFM:
+ inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFS;
+ break;
+
+ case SND_SOC_DAIFMT_CBS_CFS:
+ inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
+ break;
+
+ }
+
+ temp_dai_fmt = inv_dai_fmt;
+ }
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, temp_dai_fmt);
+ if (ret != 0 && ret != -ENOTSUPP) {
+ dev_warn(cpu_dai->dev,
+ "ASoC: Failed to set DAI format: %d\n", ret);
+ return ret;
+ }
}
return 0;
@@ -2328,10 +2431,11 @@ int snd_soc_poweroff(struct device *dev)
/* deactivate pins to sleep state */
list_for_each_entry(rtd, &card->rtd_list, list) {
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
int i;
- pinctrl_pm_select_sleep_state(cpu_dai->dev);
+ for (i = 0; i < rtd->num_cpu_dai; i++)
+ pinctrl_pm_select_sleep_state(rtd->cpu_dais[i]->dev);
+
for (i = 0; i < rtd->num_codecs; i++) {
struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
pinctrl_pm_select_sleep_state(codec_dai->dev);
@@ -2887,7 +2991,7 @@ int snd_soc_register_card(struct snd_soc_card *card)
/* deactivate pins to sleep state */
list_for_each_entry(rtd, &card->rtd_list, list) {
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *cpu_dai;
int j;
for (j = 0; j < rtd->num_codecs; j++) {
@@ -2896,8 +3000,11 @@ int snd_soc_register_card(struct snd_soc_card *card)
pinctrl_pm_select_sleep_state(codec_dai->dev);
}
- if (!cpu_dai->active)
- pinctrl_pm_select_sleep_state(cpu_dai->dev);
+ for (j = 0; j < rtd->num_cpu_dai; j++) {
+ cpu_dai = rtd->cpu_dais[j];
+ if (!cpu_dai->active)
+ pinctrl_pm_select_sleep_state(cpu_dai->dev);
+ }
}
return ret;