|
@@ -537,26 +537,75 @@ static inline void snd_soc_debugfs_exit(void)
|
|
|
struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card,
|
|
|
const char *dai_link, int stream)
|
|
|
{
|
|
|
- int i;
|
|
|
+ struct snd_soc_pcm_runtime *rtd;
|
|
|
|
|
|
- for (i = 0; i < card->num_links; i++) {
|
|
|
- if (card->rtd[i].dai_link->no_pcm &&
|
|
|
- !strcmp(card->rtd[i].dai_link->name, dai_link))
|
|
|
- return card->rtd[i].pcm->streams[stream].substream;
|
|
|
+ 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;
|
|
|
}
|
|
|
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;
|
|
|
+}
|
|
|
+
|
|
|
+static void soc_free_pcm_runtime(struct snd_soc_pcm_runtime *rtd)
|
|
|
+{
|
|
|
+ if (rtd && rtd->codec_dais)
|
|
|
+ kfree(rtd->codec_dais);
|
|
|
+ kfree(rtd);
|
|
|
+}
|
|
|
+
|
|
|
+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++;
|
|
|
+}
|
|
|
+
|
|
|
+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;
|
|
|
+}
|
|
|
+
|
|
|
struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card,
|
|
|
const char *dai_link)
|
|
|
{
|
|
|
- int i;
|
|
|
+ struct snd_soc_pcm_runtime *rtd;
|
|
|
|
|
|
- for (i = 0; i < card->num_links; i++) {
|
|
|
- if (!strcmp(card->rtd[i].dai_link->name, dai_link))
|
|
|
- return &card->rtd[i];
|
|
|
+ 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;
|
|
@@ -578,7 +627,8 @@ int snd_soc_suspend(struct device *dev)
|
|
|
{
|
|
|
struct snd_soc_card *card = dev_get_drvdata(dev);
|
|
|
struct snd_soc_codec *codec;
|
|
|
- int i, j;
|
|
|
+ struct snd_soc_pcm_runtime *rtd;
|
|
|
+ int i;
|
|
|
|
|
|
/* If the card is not initialized yet there is nothing to do */
|
|
|
if (!card->instantiated)
|
|
@@ -595,13 +645,13 @@ int snd_soc_suspend(struct device *dev)
|
|
|
snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D3hot);
|
|
|
|
|
|
/* mute any active DACs */
|
|
|
- for (i = 0; i < card->num_rtd; i++) {
|
|
|
+ list_for_each_entry(rtd, &card->rtd_list, list) {
|
|
|
|
|
|
- if (card->rtd[i].dai_link->ignore_suspend)
|
|
|
+ if (rtd->dai_link->ignore_suspend)
|
|
|
continue;
|
|
|
|
|
|
- for (j = 0; j < card->rtd[i].num_codecs; j++) {
|
|
|
- struct snd_soc_dai *dai = card->rtd[i].codec_dais[j];
|
|
|
+ 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)
|
|
@@ -610,20 +660,20 @@ int snd_soc_suspend(struct device *dev)
|
|
|
}
|
|
|
|
|
|
/* suspend all pcms */
|
|
|
- for (i = 0; i < card->num_rtd; i++) {
|
|
|
- if (card->rtd[i].dai_link->ignore_suspend)
|
|
|
+ list_for_each_entry(rtd, &card->rtd_list, list) {
|
|
|
+ if (rtd->dai_link->ignore_suspend)
|
|
|
continue;
|
|
|
|
|
|
- snd_pcm_suspend_all(card->rtd[i].pcm);
|
|
|
+ snd_pcm_suspend_all(rtd->pcm);
|
|
|
}
|
|
|
|
|
|
if (card->suspend_pre)
|
|
|
card->suspend_pre(card);
|
|
|
|
|
|
- for (i = 0; i < card->num_rtd; i++) {
|
|
|
- struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
|
|
|
+ list_for_each_entry(rtd, &card->rtd_list, list) {
|
|
|
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
|
|
|
|
|
- if (card->rtd[i].dai_link->ignore_suspend)
|
|
|
+ if (rtd->dai_link->ignore_suspend)
|
|
|
continue;
|
|
|
|
|
|
if (cpu_dai->driver->suspend && !cpu_dai->driver->bus_control)
|
|
@@ -631,19 +681,19 @@ int snd_soc_suspend(struct device *dev)
|
|
|
}
|
|
|
|
|
|
/* close any waiting streams */
|
|
|
- for (i = 0; i < card->num_rtd; i++)
|
|
|
- flush_delayed_work(&card->rtd[i].delayed_work);
|
|
|
+ list_for_each_entry(rtd, &card->rtd_list, list)
|
|
|
+ flush_delayed_work(&rtd->delayed_work);
|
|
|
|
|
|
- for (i = 0; i < card->num_rtd; i++) {
|
|
|
+ list_for_each_entry(rtd, &card->rtd_list, list) {
|
|
|
|
|
|
- if (card->rtd[i].dai_link->ignore_suspend)
|
|
|
+ if (rtd->dai_link->ignore_suspend)
|
|
|
continue;
|
|
|
|
|
|
- snd_soc_dapm_stream_event(&card->rtd[i],
|
|
|
+ snd_soc_dapm_stream_event(rtd,
|
|
|
SNDRV_PCM_STREAM_PLAYBACK,
|
|
|
SND_SOC_DAPM_STREAM_SUSPEND);
|
|
|
|
|
|
- snd_soc_dapm_stream_event(&card->rtd[i],
|
|
|
+ snd_soc_dapm_stream_event(rtd,
|
|
|
SNDRV_PCM_STREAM_CAPTURE,
|
|
|
SND_SOC_DAPM_STREAM_SUSPEND);
|
|
|
}
|
|
@@ -690,10 +740,10 @@ int snd_soc_suspend(struct device *dev)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- for (i = 0; i < card->num_rtd; i++) {
|
|
|
- struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
|
|
|
+ list_for_each_entry(rtd, &card->rtd_list, list) {
|
|
|
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
|
|
|
|
|
- if (card->rtd[i].dai_link->ignore_suspend)
|
|
|
+ if (rtd->dai_link->ignore_suspend)
|
|
|
continue;
|
|
|
|
|
|
if (cpu_dai->driver->suspend && cpu_dai->driver->bus_control)
|
|
@@ -717,8 +767,9 @@ static void soc_resume_deferred(struct work_struct *work)
|
|
|
{
|
|
|
struct snd_soc_card *card =
|
|
|
container_of(work, struct snd_soc_card, deferred_resume_work);
|
|
|
+ struct snd_soc_pcm_runtime *rtd;
|
|
|
struct snd_soc_codec *codec;
|
|
|
- int i, j;
|
|
|
+ int i;
|
|
|
|
|
|
/* our power state is still SNDRV_CTL_POWER_D3hot from suspend time,
|
|
|
* so userspace apps are blocked from touching us
|
|
@@ -733,10 +784,10 @@ static void soc_resume_deferred(struct work_struct *work)
|
|
|
card->resume_pre(card);
|
|
|
|
|
|
/* resume control bus DAIs */
|
|
|
- for (i = 0; i < card->num_rtd; i++) {
|
|
|
- struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
|
|
|
+ list_for_each_entry(rtd, &card->rtd_list, list) {
|
|
|
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
|
|
|
|
|
- if (card->rtd[i].dai_link->ignore_suspend)
|
|
|
+ if (rtd->dai_link->ignore_suspend)
|
|
|
continue;
|
|
|
|
|
|
if (cpu_dai->driver->resume && cpu_dai->driver->bus_control)
|
|
@@ -751,28 +802,28 @@ static void soc_resume_deferred(struct work_struct *work)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- for (i = 0; i < card->num_rtd; i++) {
|
|
|
+ list_for_each_entry(rtd, &card->rtd_list, list) {
|
|
|
|
|
|
- if (card->rtd[i].dai_link->ignore_suspend)
|
|
|
+ if (rtd->dai_link->ignore_suspend)
|
|
|
continue;
|
|
|
|
|
|
- snd_soc_dapm_stream_event(&card->rtd[i],
|
|
|
+ snd_soc_dapm_stream_event(rtd,
|
|
|
SNDRV_PCM_STREAM_PLAYBACK,
|
|
|
SND_SOC_DAPM_STREAM_RESUME);
|
|
|
|
|
|
- snd_soc_dapm_stream_event(&card->rtd[i],
|
|
|
+ snd_soc_dapm_stream_event(rtd,
|
|
|
SNDRV_PCM_STREAM_CAPTURE,
|
|
|
SND_SOC_DAPM_STREAM_RESUME);
|
|
|
}
|
|
|
|
|
|
/* unmute any active DACs */
|
|
|
- for (i = 0; i < card->num_rtd; i++) {
|
|
|
+ list_for_each_entry(rtd, &card->rtd_list, list) {
|
|
|
|
|
|
- if (card->rtd[i].dai_link->ignore_suspend)
|
|
|
+ if (rtd->dai_link->ignore_suspend)
|
|
|
continue;
|
|
|
|
|
|
- for (j = 0; j < card->rtd[i].num_codecs; j++) {
|
|
|
- struct snd_soc_dai *dai = card->rtd[i].codec_dais[j];
|
|
|
+ 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)
|
|
@@ -780,10 +831,10 @@ static void soc_resume_deferred(struct work_struct *work)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- for (i = 0; i < card->num_rtd; i++) {
|
|
|
- struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
|
|
|
+ list_for_each_entry(rtd, &card->rtd_list, list) {
|
|
|
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
|
|
|
|
|
- if (card->rtd[i].dai_link->ignore_suspend)
|
|
|
+ if (rtd->dai_link->ignore_suspend)
|
|
|
continue;
|
|
|
|
|
|
if (cpu_dai->driver->resume && !cpu_dai->driver->bus_control)
|
|
@@ -808,15 +859,14 @@ int snd_soc_resume(struct device *dev)
|
|
|
{
|
|
|
struct snd_soc_card *card = dev_get_drvdata(dev);
|
|
|
bool bus_control = false;
|
|
|
- int i;
|
|
|
+ struct snd_soc_pcm_runtime *rtd;
|
|
|
|
|
|
/* If the card is not initialized yet there is nothing to do */
|
|
|
if (!card->instantiated)
|
|
|
return 0;
|
|
|
|
|
|
/* activate pins from sleep state */
|
|
|
- for (i = 0; i < card->num_rtd; i++) {
|
|
|
- struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
|
|
|
+ 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;
|
|
|
int j;
|
|
@@ -837,8 +887,8 @@ int snd_soc_resume(struct device *dev)
|
|
|
* have that problem and may take a substantial amount of time to resume
|
|
|
* due to I/O costs and anti-pop so handle them out of line.
|
|
|
*/
|
|
|
- for (i = 0; i < card->num_rtd; i++) {
|
|
|
- struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
|
|
|
+ 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;
|
|
|
}
|
|
|
if (bus_control) {
|
|
@@ -910,18 +960,41 @@ static struct snd_soc_dai *snd_soc_find_dai(
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
-static int soc_bind_dai_link(struct snd_soc_card *card, int num)
|
|
|
+static bool soc_is_dai_link_bound(struct snd_soc_card *card,
|
|
|
+ struct snd_soc_dai_link *dai_link)
|
|
|
+{
|
|
|
+ struct snd_soc_pcm_runtime *rtd;
|
|
|
+
|
|
|
+ list_for_each_entry(rtd, &card->rtd_list, list) {
|
|
|
+ if (rtd->dai_link == dai_link)
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+static int soc_bind_dai_link(struct snd_soc_card *card,
|
|
|
+ struct snd_soc_dai_link *dai_link)
|
|
|
{
|
|
|
- struct snd_soc_dai_link *dai_link = &card->dai_link[num];
|
|
|
- struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
|
|
|
+ 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 **codec_dais = rtd->codec_dais;
|
|
|
+ struct snd_soc_dai **codec_dais;
|
|
|
struct snd_soc_platform *platform;
|
|
|
const char *platform_name;
|
|
|
int i;
|
|
|
|
|
|
- dev_dbg(card->dev, "ASoC: binding %s at idx %d\n", dai_link->name, num);
|
|
|
+ dev_dbg(card->dev, "ASoC: binding %s\n", dai_link->name);
|
|
|
+
|
|
|
+ rtd = soc_new_pcm_runtime(card, dai_link);
|
|
|
+ if (!rtd)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ 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;
|
|
|
+ }
|
|
|
|
|
|
cpu_dai_component.name = dai_link->cpu_name;
|
|
|
cpu_dai_component.of_node = dai_link->cpu_of_node;
|
|
@@ -930,18 +1003,19 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
|
|
|
if (!rtd->cpu_dai) {
|
|
|
dev_err(card->dev, "ASoC: CPU DAI %s not registered\n",
|
|
|
dai_link->cpu_dai_name);
|
|
|
- return -EPROBE_DEFER;
|
|
|
+ goto _err_defer;
|
|
|
}
|
|
|
|
|
|
rtd->num_codecs = dai_link->num_codecs;
|
|
|
|
|
|
/* Find CODEC from registered CODECs */
|
|
|
+ codec_dais = rtd->codec_dais;
|
|
|
for (i = 0; i < rtd->num_codecs; i++) {
|
|
|
codec_dais[i] = snd_soc_find_dai(&codecs[i]);
|
|
|
if (!codec_dais[i]) {
|
|
|
dev_err(card->dev, "ASoC: CODEC DAI %s not registered\n",
|
|
|
codecs[i].dai_name);
|
|
|
- return -EPROBE_DEFER;
|
|
|
+ goto _err_defer;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -973,9 +1047,12 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
|
|
|
return -EPROBE_DEFER;
|
|
|
}
|
|
|
|
|
|
- card->num_rtd++;
|
|
|
-
|
|
|
+ soc_add_pcm_runtime(card, rtd);
|
|
|
return 0;
|
|
|
+
|
|
|
+_err_defer:
|
|
|
+ soc_free_pcm_runtime(rtd);
|
|
|
+ return -EPROBE_DEFER;
|
|
|
}
|
|
|
|
|
|
static void soc_remove_component(struct snd_soc_component *component)
|
|
@@ -1014,9 +1091,9 @@ static void soc_remove_dai(struct snd_soc_dai *dai, int order)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order)
|
|
|
+static void soc_remove_link_dais(struct snd_soc_card *card,
|
|
|
+ struct snd_soc_pcm_runtime *rtd, int order)
|
|
|
{
|
|
|
- struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
|
|
|
int i;
|
|
|
|
|
|
/* unregister the rtd device */
|
|
@@ -1032,10 +1109,9 @@ static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order)
|
|
|
soc_remove_dai(rtd->cpu_dai, order);
|
|
|
}
|
|
|
|
|
|
-static void soc_remove_link_components(struct snd_soc_card *card, int num,
|
|
|
- int order)
|
|
|
+static void soc_remove_link_components(struct snd_soc_card *card,
|
|
|
+ struct snd_soc_pcm_runtime *rtd, int order)
|
|
|
{
|
|
|
- struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
|
|
|
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
|
|
struct snd_soc_platform *platform = rtd->platform;
|
|
|
struct snd_soc_component *component;
|
|
@@ -1061,23 +1137,200 @@ static void soc_remove_link_components(struct snd_soc_card *card, int num,
|
|
|
|
|
|
static void soc_remove_dai_links(struct snd_soc_card *card)
|
|
|
{
|
|
|
- int dai, order;
|
|
|
+ int order;
|
|
|
+ struct snd_soc_pcm_runtime *rtd;
|
|
|
+ struct snd_soc_dai_link *link, *_link;
|
|
|
|
|
|
for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
|
|
|
order++) {
|
|
|
- for (dai = 0; dai < card->num_rtd; dai++)
|
|
|
- soc_remove_link_dais(card, dai, order);
|
|
|
+ list_for_each_entry(rtd, &card->rtd_list, list)
|
|
|
+ soc_remove_link_dais(card, rtd, order);
|
|
|
}
|
|
|
|
|
|
for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
|
|
|
order++) {
|
|
|
- for (dai = 0; dai < card->num_rtd; dai++)
|
|
|
- soc_remove_link_components(card, dai, order);
|
|
|
+ list_for_each_entry(rtd, &card->rtd_list, list)
|
|
|
+ soc_remove_link_components(card, rtd, order);
|
|
|
}
|
|
|
|
|
|
- card->num_rtd = 0;
|
|
|
+ list_for_each_entry_safe(link, _link, &card->dai_link_list, list) {
|
|
|
+ if (link->dobj.type == SND_SOC_DOBJ_DAI_LINK)
|
|
|
+ dev_warn(card->dev, "Topology forgot to remove link %s?\n",
|
|
|
+ link->name);
|
|
|
+
|
|
|
+ list_del(&link->list);
|
|
|
+ card->num_dai_links--;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static int snd_soc_init_multicodec(struct snd_soc_card *card,
|
|
|
+ struct snd_soc_dai_link *dai_link)
|
|
|
+{
|
|
|
+ /* Legacy codec/codec_dai link is a single entry in multicodec */
|
|
|
+ if (dai_link->codec_name || dai_link->codec_of_node ||
|
|
|
+ dai_link->codec_dai_name) {
|
|
|
+ dai_link->num_codecs = 1;
|
|
|
+
|
|
|
+ dai_link->codecs = devm_kzalloc(card->dev,
|
|
|
+ sizeof(struct snd_soc_dai_link_component),
|
|
|
+ GFP_KERNEL);
|
|
|
+ if (!dai_link->codecs)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ dai_link->codecs[0].name = dai_link->codec_name;
|
|
|
+ dai_link->codecs[0].of_node = dai_link->codec_of_node;
|
|
|
+ dai_link->codecs[0].dai_name = dai_link->codec_dai_name;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!dai_link->codecs) {
|
|
|
+ dev_err(card->dev, "ASoC: DAI link has no CODECs\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int soc_init_dai_link(struct snd_soc_card *card,
|
|
|
+ struct snd_soc_dai_link *link)
|
|
|
+{
|
|
|
+ int i, ret;
|
|
|
+
|
|
|
+ ret = snd_soc_init_multicodec(card, link);
|
|
|
+ if (ret) {
|
|
|
+ dev_err(card->dev, "ASoC: failed to init multicodec\n");
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 0; i < link->num_codecs; i++) {
|
|
|
+ /*
|
|
|
+ * Codec must be specified by 1 of name or OF node,
|
|
|
+ * not both or neither.
|
|
|
+ */
|
|
|
+ if (!!link->codecs[i].name ==
|
|
|
+ !!link->codecs[i].of_node) {
|
|
|
+ dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n",
|
|
|
+ link->name);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ /* Codec DAI name must be specified */
|
|
|
+ if (!link->codecs[i].dai_name) {
|
|
|
+ dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n",
|
|
|
+ link->name);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Platform may be specified by either name or OF node, but
|
|
|
+ * can be left unspecified, and a dummy platform will be used.
|
|
|
+ */
|
|
|
+ if (link->platform_name && link->platform_of_node) {
|
|
|
+ dev_err(card->dev,
|
|
|
+ "ASoC: Both platform name/of_node are set for %s\n",
|
|
|
+ link->name);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * CPU device may be specified by either name or OF node, but
|
|
|
+ * 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;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * snd_soc_add_dai_link - Add a DAI link dynamically
|
|
|
+ * @card: The ASoC card to which the DAI link is added
|
|
|
+ * @dai_link: The new DAI link to add
|
|
|
+ *
|
|
|
+ * This function adds a DAI link to the ASoC card's link list.
|
|
|
+ *
|
|
|
+ * Note: Topology can use this API to add DAI links when probing the
|
|
|
+ * topology component. And machine drivers can still define static
|
|
|
+ * DAI links in dai_link array.
|
|
|
+ */
|
|
|
+int snd_soc_add_dai_link(struct snd_soc_card *card,
|
|
|
+ struct snd_soc_dai_link *dai_link)
|
|
|
+{
|
|
|
+ if (dai_link->dobj.type
|
|
|
+ && dai_link->dobj.type != SND_SOC_DOBJ_DAI_LINK) {
|
|
|
+ dev_err(card->dev, "Invalid dai link type %d\n",
|
|
|
+ dai_link->dobj.type);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ lockdep_assert_held(&client_mutex);
|
|
|
+ /* Notify the machine driver for extra initialization
|
|
|
+ * on the link created by topology.
|
|
|
+ */
|
|
|
+ if (dai_link->dobj.type && card->add_dai_link)
|
|
|
+ card->add_dai_link(card, dai_link);
|
|
|
+
|
|
|
+ list_add_tail(&dai_link->list, &card->dai_link_list);
|
|
|
+ card->num_dai_links++;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(snd_soc_add_dai_link);
|
|
|
+
|
|
|
+/**
|
|
|
+ * snd_soc_remove_dai_link - Remove a DAI link from the list
|
|
|
+ * @card: The ASoC card that owns the link
|
|
|
+ * @dai_link: The DAI link to remove
|
|
|
+ *
|
|
|
+ * This function removes a DAI link from the ASoC card's link list.
|
|
|
+ *
|
|
|
+ * For DAI links previously added by topology, topology should
|
|
|
+ * remove them by using the dobj embedded in the link.
|
|
|
+ */
|
|
|
+void snd_soc_remove_dai_link(struct snd_soc_card *card,
|
|
|
+ struct snd_soc_dai_link *dai_link)
|
|
|
+{
|
|
|
+ struct snd_soc_dai_link *link, *_link;
|
|
|
+
|
|
|
+ if (dai_link->dobj.type
|
|
|
+ && dai_link->dobj.type != SND_SOC_DOBJ_DAI_LINK) {
|
|
|
+ dev_err(card->dev, "Invalid dai link type %d\n",
|
|
|
+ dai_link->dobj.type);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ lockdep_assert_held(&client_mutex);
|
|
|
+ /* Notify the machine driver for extra destruction
|
|
|
+ * on the link created by topology.
|
|
|
+ */
|
|
|
+ if (dai_link->dobj.type && card->remove_dai_link)
|
|
|
+ card->remove_dai_link(card, dai_link);
|
|
|
+
|
|
|
+ list_for_each_entry_safe(link, _link, &card->dai_link_list, list) {
|
|
|
+ if (link == dai_link) {
|
|
|
+ list_del(&link->list);
|
|
|
+ card->num_dai_links--;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(snd_soc_remove_dai_link);
|
|
|
+
|
|
|
static void soc_set_name_prefix(struct snd_soc_card *card,
|
|
|
struct snd_soc_component *component)
|
|
|
{
|
|
@@ -1220,10 +1473,10 @@ static int soc_post_component_init(struct snd_soc_pcm_runtime *rtd,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int soc_probe_link_components(struct snd_soc_card *card, int num,
|
|
|
+static int soc_probe_link_components(struct snd_soc_card *card,
|
|
|
+ struct snd_soc_pcm_runtime *rtd,
|
|
|
int order)
|
|
|
{
|
|
|
- struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
|
|
|
struct snd_soc_platform *platform = rtd->platform;
|
|
|
struct snd_soc_component *component;
|
|
|
int i, ret;
|
|
@@ -1319,15 +1572,15 @@ static int soc_link_dai_widgets(struct snd_soc_card *card,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
|
|
|
+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 = &card->dai_link[num];
|
|
|
- struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
|
|
|
+ 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",
|
|
|
- card->name, num, order);
|
|
|
+ card->name, rtd->num, order);
|
|
|
|
|
|
/* set default power off timeout */
|
|
|
rtd->pmdown_time = pmdown_time;
|
|
@@ -1372,7 +1625,7 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
|
|
|
|
|
|
if (cpu_dai->driver->compress_new) {
|
|
|
/*create compress_device"*/
|
|
|
- ret = cpu_dai->driver->compress_new(rtd, num);
|
|
|
+ ret = cpu_dai->driver->compress_new(rtd, rtd->num);
|
|
|
if (ret < 0) {
|
|
|
dev_err(card->dev, "ASoC: can't create compress %s\n",
|
|
|
dai_link->stream_name);
|
|
@@ -1382,7 +1635,7 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
|
|
|
|
|
|
if (!dai_link->params) {
|
|
|
/* create the pcm */
|
|
|
- ret = soc_new_pcm(rtd, num);
|
|
|
+ ret = soc_new_pcm(rtd, rtd->num);
|
|
|
if (ret < 0) {
|
|
|
dev_err(card->dev, "ASoC: can't create pcm %s :%d\n",
|
|
|
dai_link->stream_name, ret);
|
|
@@ -1552,6 +1805,8 @@ EXPORT_SYMBOL_GPL(snd_soc_runtime_set_dai_fmt);
|
|
|
static int snd_soc_instantiate_card(struct snd_soc_card *card)
|
|
|
{
|
|
|
struct snd_soc_codec *codec;
|
|
|
+ struct snd_soc_pcm_runtime *rtd;
|
|
|
+ struct snd_soc_dai_link *dai_link;
|
|
|
int ret, i, order;
|
|
|
|
|
|
mutex_lock(&client_mutex);
|
|
@@ -1559,7 +1814,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
|
|
|
|
|
|
/* bind DAIs */
|
|
|
for (i = 0; i < card->num_links; i++) {
|
|
|
- ret = soc_bind_dai_link(card, i);
|
|
|
+ ret = soc_bind_dai_link(card, &card->dai_link[i]);
|
|
|
if (ret != 0)
|
|
|
goto base_error;
|
|
|
}
|
|
@@ -1571,6 +1826,10 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
|
|
|
goto base_error;
|
|
|
}
|
|
|
|
|
|
+ /* add predefined DAI links to the list */
|
|
|
+ for (i = 0; i < card->num_links; i++)
|
|
|
+ snd_soc_add_dai_link(card, card->dai_link+i);
|
|
|
+
|
|
|
/* initialize the register cache for each available codec */
|
|
|
list_for_each_entry(codec, &codec_list, list) {
|
|
|
if (codec->cache_init)
|
|
@@ -1624,8 +1883,8 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
|
|
|
/* probe all components used by DAI links on this card */
|
|
|
for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
|
|
|
order++) {
|
|
|
- for (i = 0; i < card->num_links; i++) {
|
|
|
- ret = soc_probe_link_components(card, i, order);
|
|
|
+ list_for_each_entry(rtd, &card->rtd_list, list) {
|
|
|
+ ret = soc_probe_link_components(card, rtd, order);
|
|
|
if (ret < 0) {
|
|
|
dev_err(card->dev,
|
|
|
"ASoC: failed to instantiate card %d\n",
|
|
@@ -1635,11 +1894,26 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /* Find new DAI links added during probing components and bind them.
|
|
|
+ * Components with topology may bring new DAIs and DAI links.
|
|
|
+ */
|
|
|
+ list_for_each_entry(dai_link, &card->dai_link_list, list) {
|
|
|
+ if (soc_is_dai_link_bound(card, dai_link))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ ret = soc_init_dai_link(card, dai_link);
|
|
|
+ if (ret)
|
|
|
+ goto probe_dai_err;
|
|
|
+ ret = soc_bind_dai_link(card, dai_link);
|
|
|
+ if (ret)
|
|
|
+ goto probe_dai_err;
|
|
|
+ }
|
|
|
+
|
|
|
/* probe all DAI links on this card */
|
|
|
for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
|
|
|
order++) {
|
|
|
- for (i = 0; i < card->num_links; i++) {
|
|
|
- ret = soc_probe_link_dais(card, i, order);
|
|
|
+ list_for_each_entry(rtd, &card->rtd_list, list) {
|
|
|
+ ret = soc_probe_link_dais(card, rtd, order);
|
|
|
if (ret < 0) {
|
|
|
dev_err(card->dev,
|
|
|
"ASoC: failed to instantiate card %d\n",
|
|
@@ -1733,6 +2007,7 @@ card_probe_error:
|
|
|
snd_card_free(card->snd_card);
|
|
|
|
|
|
base_error:
|
|
|
+ soc_remove_pcm_runtimes(card);
|
|
|
mutex_unlock(&card->mutex);
|
|
|
mutex_unlock(&client_mutex);
|
|
|
|
|
@@ -1763,13 +2038,12 @@ static int soc_probe(struct platform_device *pdev)
|
|
|
|
|
|
static int soc_cleanup_card_resources(struct snd_soc_card *card)
|
|
|
{
|
|
|
+ struct snd_soc_pcm_runtime *rtd;
|
|
|
int i;
|
|
|
|
|
|
/* make sure any delayed work runs */
|
|
|
- for (i = 0; i < card->num_rtd; i++) {
|
|
|
- struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
|
|
|
+ list_for_each_entry(rtd, &card->rtd_list, list)
|
|
|
flush_delayed_work(&rtd->delayed_work);
|
|
|
- }
|
|
|
|
|
|
/* remove auxiliary devices */
|
|
|
for (i = 0; i < card->num_aux_devs; i++)
|
|
@@ -1777,6 +2051,7 @@ static int soc_cleanup_card_resources(struct snd_soc_card *card)
|
|
|
|
|
|
/* remove and free each DAI */
|
|
|
soc_remove_dai_links(card);
|
|
|
+ soc_remove_pcm_runtimes(card);
|
|
|
|
|
|
soc_cleanup_card_debugfs(card);
|
|
|
|
|
@@ -1803,29 +2078,26 @@ static int soc_remove(struct platform_device *pdev)
|
|
|
int snd_soc_poweroff(struct device *dev)
|
|
|
{
|
|
|
struct snd_soc_card *card = dev_get_drvdata(dev);
|
|
|
- int i;
|
|
|
+ struct snd_soc_pcm_runtime *rtd;
|
|
|
|
|
|
if (!card->instantiated)
|
|
|
return 0;
|
|
|
|
|
|
/* Flush out pmdown_time work - we actually do want to run it
|
|
|
* now, we're shutting down so no imminent restart. */
|
|
|
- for (i = 0; i < card->num_rtd; i++) {
|
|
|
- struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
|
|
|
+ list_for_each_entry(rtd, &card->rtd_list, list)
|
|
|
flush_delayed_work(&rtd->delayed_work);
|
|
|
- }
|
|
|
|
|
|
snd_soc_dapm_shutdown(card);
|
|
|
|
|
|
/* deactivate pins to sleep state */
|
|
|
- for (i = 0; i < card->num_rtd; i++) {
|
|
|
- struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
|
|
|
+ list_for_each_entry(rtd, &card->rtd_list, list) {
|
|
|
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
|
|
- int j;
|
|
|
+ int i;
|
|
|
|
|
|
pinctrl_pm_select_sleep_state(cpu_dai->dev);
|
|
|
- for (j = 0; j < rtd->num_codecs; j++) {
|
|
|
- struct snd_soc_dai *codec_dai = rtd->codec_dais[j];
|
|
|
+ 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);
|
|
|
}
|
|
|
}
|
|
@@ -2301,33 +2573,6 @@ int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute,
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute);
|
|
|
|
|
|
-static int snd_soc_init_multicodec(struct snd_soc_card *card,
|
|
|
- struct snd_soc_dai_link *dai_link)
|
|
|
-{
|
|
|
- /* Legacy codec/codec_dai link is a single entry in multicodec */
|
|
|
- if (dai_link->codec_name || dai_link->codec_of_node ||
|
|
|
- dai_link->codec_dai_name) {
|
|
|
- dai_link->num_codecs = 1;
|
|
|
-
|
|
|
- dai_link->codecs = devm_kzalloc(card->dev,
|
|
|
- sizeof(struct snd_soc_dai_link_component),
|
|
|
- GFP_KERNEL);
|
|
|
- if (!dai_link->codecs)
|
|
|
- return -ENOMEM;
|
|
|
-
|
|
|
- dai_link->codecs[0].name = dai_link->codec_name;
|
|
|
- dai_link->codecs[0].of_node = dai_link->codec_of_node;
|
|
|
- dai_link->codecs[0].dai_name = dai_link->codec_dai_name;
|
|
|
- }
|
|
|
-
|
|
|
- if (!dai_link->codecs) {
|
|
|
- dev_err(card->dev, "ASoC: DAI link has no CODECs\n");
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
/**
|
|
|
* snd_soc_register_card - Register a card with the ASoC core
|
|
|
*
|
|
@@ -2336,7 +2581,8 @@ static int snd_soc_init_multicodec(struct snd_soc_card *card,
|
|
|
*/
|
|
|
int snd_soc_register_card(struct snd_soc_card *card)
|
|
|
{
|
|
|
- int i, j, ret;
|
|
|
+ int i, ret;
|
|
|
+ struct snd_soc_pcm_runtime *rtd;
|
|
|
|
|
|
if (!card->name || !card->dev)
|
|
|
return -EINVAL;
|
|
@@ -2344,63 +2590,11 @@ int snd_soc_register_card(struct snd_soc_card *card)
|
|
|
for (i = 0; i < card->num_links; i++) {
|
|
|
struct snd_soc_dai_link *link = &card->dai_link[i];
|
|
|
|
|
|
- ret = snd_soc_init_multicodec(card, link);
|
|
|
+ ret = soc_init_dai_link(card, link);
|
|
|
if (ret) {
|
|
|
- dev_err(card->dev, "ASoC: failed to init multicodec\n");
|
|
|
- return ret;
|
|
|
- }
|
|
|
-
|
|
|
- for (j = 0; j < link->num_codecs; j++) {
|
|
|
- /*
|
|
|
- * Codec must be specified by 1 of name or OF node,
|
|
|
- * not both or neither.
|
|
|
- */
|
|
|
- if (!!link->codecs[j].name ==
|
|
|
- !!link->codecs[j].of_node) {
|
|
|
- dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n",
|
|
|
- link->name);
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
- /* Codec DAI name must be specified */
|
|
|
- if (!link->codecs[j].dai_name) {
|
|
|
- dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n",
|
|
|
- link->name);
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * Platform may be specified by either name or OF node, but
|
|
|
- * can be left unspecified, and a dummy platform will be used.
|
|
|
- */
|
|
|
- if (link->platform_name && link->platform_of_node) {
|
|
|
- dev_err(card->dev,
|
|
|
- "ASoC: Both platform name/of_node are set for %s\n",
|
|
|
- link->name);
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * CPU device may be specified by either name or OF node, but
|
|
|
- * 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",
|
|
|
+ dev_err(card->dev, "ASoC: failed to init link %s\n",
|
|
|
link->name);
|
|
|
- return -EINVAL;
|
|
|
+ return ret;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -2408,25 +2602,18 @@ int snd_soc_register_card(struct snd_soc_card *card)
|
|
|
|
|
|
snd_soc_initialize_card_lists(card);
|
|
|
|
|
|
- card->rtd = devm_kzalloc(card->dev,
|
|
|
+ INIT_LIST_HEAD(&card->dai_link_list);
|
|
|
+ card->num_dai_links = 0;
|
|
|
+
|
|
|
+ INIT_LIST_HEAD(&card->rtd_list);
|
|
|
+ card->num_rtd = 0;
|
|
|
+
|
|
|
+ card->rtd_aux = devm_kzalloc(card->dev,
|
|
|
sizeof(struct snd_soc_pcm_runtime) *
|
|
|
- (card->num_links + card->num_aux_devs),
|
|
|
+ card->num_aux_devs,
|
|
|
GFP_KERNEL);
|
|
|
- if (card->rtd == NULL)
|
|
|
+ if (card->rtd_aux == NULL)
|
|
|
return -ENOMEM;
|
|
|
- card->num_rtd = 0;
|
|
|
- card->rtd_aux = &card->rtd[card->num_links];
|
|
|
-
|
|
|
- for (i = 0; i < card->num_links; i++) {
|
|
|
- card->rtd[i].card = card;
|
|
|
- card->rtd[i].dai_link = &card->dai_link[i];
|
|
|
- card->rtd[i].codec_dais = devm_kzalloc(card->dev,
|
|
|
- sizeof(struct snd_soc_dai *) *
|
|
|
- (card->rtd[i].dai_link->num_codecs),
|
|
|
- GFP_KERNEL);
|
|
|
- if (card->rtd[i].codec_dais == NULL)
|
|
|
- return -ENOMEM;
|
|
|
- }
|
|
|
|
|
|
for (i = 0; i < card->num_aux_devs; i++)
|
|
|
card->rtd_aux[i].card = card;
|
|
@@ -2442,8 +2629,7 @@ int snd_soc_register_card(struct snd_soc_card *card)
|
|
|
return ret;
|
|
|
|
|
|
/* deactivate pins to sleep state */
|
|
|
- for (i = 0; i < card->num_rtd; i++) {
|
|
|
- struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
|
|
|
+ list_for_each_entry(rtd, &card->rtd_list, list) {
|
|
|
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
|
|
int j;
|
|
|
|