diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h index 806059052bfcca..93aafa87d4edc7 100644 --- a/include/sound/soc-dpcm.h +++ b/include/sound/soc-dpcm.h @@ -158,5 +158,9 @@ static inline void dpcm_path_put(struct snd_soc_dapm_widget_list **list) kfree(*list); } - +/* create/free virtual FE dai links */ +int soc_dpcm_vfe_new(struct snd_soc_card *, int index, const char *link_name, + const char *cpu_dai_name, const char *platform_name, + int stream_dir); +int soc_dpcm_vfe_free(struct snd_soc_card *card, const char *link_name); #endif diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index a0160a409e4094..2e924717cf0146 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1047,6 +1047,7 @@ static int soc_bind_dai_link(struct snd_soc_card *card, struct snd_soc_dai **codec_dais; struct snd_soc_platform *platform; struct device_node *platform_of_node; + struct snd_pcm_runtime *runtime; const char *platform_name; int i; @@ -1134,6 +1135,31 @@ static int soc_bind_dai_link(struct snd_soc_card *card, } soc_add_pcm_runtime(card, rtd); + + /* if this is a virtual FE link, create runtime and set it as active */ + if (rtd->dai_link->dynamic && rtd->dai_link->no_pcm) { + runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); + if (!runtime) + return -ENOMEM; + + /* set playback active status */ + if (rtd->dai_link->dpcm_playback) { + rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].runtime = runtime; + rtd->cpu_dai->playback_active = 1; + rtd->codec_dai->playback_active = 1; + } + + /* set capture active statue */ + if (rtd->dai_link->dpcm_capture) { + rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].runtime = runtime; + rtd->cpu_dai->capture_active = 1; + rtd->codec_dai->capture_active = 1; + } + + /* increment the active count for cpu dai */ + rtd->cpu_dai->active++; + } + return 0; _err_defer: diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 6d54128e44b46b..a0c1527e1b2917 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -855,6 +855,7 @@ static int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w, kcname_in_long_name = true; } else { switch (w->id) { + case snd_soc_dapm_siggen: case snd_soc_dapm_switch: case snd_soc_dapm_mixer: case snd_soc_dapm_pga: @@ -1246,7 +1247,8 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, custom_stop_condition); /* Drop starting point */ - list_del(widgets.next); + if (!list_is_singular(&widgets)) + list_del(widgets.next); ret = dapm_widget_list_create(list, &widgets); if (ret) @@ -3076,6 +3078,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) case snd_soc_dapm_demux: dapm_new_mux(w); break; + case snd_soc_dapm_siggen: case snd_soc_dapm_pga: case snd_soc_dapm_out_drv: dapm_new_pga(w); diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 4ce489165a6d9b..73faa469d8ae36 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1582,7 +1582,6 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, /* Create any new FE <--> BE connections */ for (i = 0; i < list->num_widgets; i++) { - switch (list->widgets[i]->id) { case snd_soc_dapm_dai_in: if (stream != SNDRV_PCM_STREAM_PLAYBACK) @@ -2759,6 +2758,8 @@ int soc_dpcm_runtime_update(struct snd_soc_card *card) mutex_unlock(&card->mutex); return 0; } +EXPORT_SYMBOL_GPL(soc_dpcm_runtime_update); + int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute) { struct snd_soc_dpcm *dpcm; @@ -2789,6 +2790,109 @@ int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute) return 0; } +/* + * create a virtual FE DAI link which has no pcm device registered. + * Virtual FE DAI links are used in hostless pipelines + * to enable the codecs when the pipeline is triggered + */ +int soc_dpcm_vfe_new(struct snd_soc_card *card, int index, + const char *link_name, const char *cpu_dai_name, + const char *platform_name, int stream_dir) +{ + struct snd_soc_dai_link *link; + + link = kzalloc(sizeof(*link), GFP_KERNEL); + if (!link) + return -ENOMEM; + + dev_dbg(card->dev, "ASoC: adding new virtual FE DAI link %s\n", + link_name); + + /* define FE DAI link */ + link->name = link_name; + link->cpu_dai_name = cpu_dai_name; + link->platform_name = platform_name; + link->codec_name = "snd-soc-dummy"; + link->codec_dai_name = "snd-soc-dummy-dai"; + + /* + * Enabling both dynamic and no_pcm flags indicates the link is virtual + * i.e. it is a FE dai link but without a registered pcm device + */ + link->dynamic = 1; + link->no_pcm = 1; + + /* enable playback */ + if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK) + link->dpcm_playback = 1; + + /* enable capture */ + if (stream_dir == SNDRV_PCM_STREAM_CAPTURE) + link->dpcm_capture = 1; + + link->dobj.index = index; + link->dobj.type = SND_SOC_DOBJ_DAI_LINK; + + /* add link to card dai link list */ + snd_soc_add_dai_link(card, link); + + return 0; +} +EXPORT_SYMBOL_GPL(soc_dpcm_vfe_new); + +/* free virtual FE DAI link */ +int soc_dpcm_vfe_free(struct snd_soc_card *card, const char *link_name) +{ + struct snd_soc_rtdcom_list *rtdcom1, *rtdcom2; + struct snd_soc_pcm_runtime *rtd, *temp; + struct snd_pcm_str *pstr; + int stream_dir; + + list_for_each_entry_safe(rtd, temp, &card->rtd_list, list) { + + /* delete requested FE link */ + if (!strcmp(rtd->dai_link->name, link_name)) { + + if (rtd->dai_link->dpcm_playback) + stream_dir = SNDRV_PCM_STREAM_PLAYBACK; + else + stream_dir = SNDRV_PCM_STREAM_CAPTURE; + + /* disconnect FE from BE */ + dpcm_be_disconnect(rtd, stream_dir); + + /* free pcm runtime */ + kfree(rtd->dpcm[stream_dir].runtime); + + pstr = &rtd->pcm->streams[stream_dir]; + + /* free pcm substream amd pcm */ + kfree(pstr->substream); + + /* free pcm */ + kfree(rtd->pcm); + + /* free codec dais and component list */ + kfree(rtd->codec_dais); + + for_each_rtdcom_safe(rtd, rtdcom1, rtdcom2) + kfree(rtdcom1); + + /* remove dai_link from card */ + snd_soc_remove_dai_link(card, rtd->dai_link); + + /* free link */ + kfree(rtd->dai_link); + + /* free runtime */ + kfree(rtd); + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(soc_dpcm_vfe_free); + static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream) { struct snd_soc_pcm_runtime *fe = fe_substream->private_data; @@ -2996,6 +3100,55 @@ static int soc_rtdcom_mmap(struct snd_pcm_substream *substream, return -EINVAL; } +/* + * for virtual FE dai links, there is no need + * to register PCM device. So only allocate memory for + * the dummy pcm device and substreams to be used for + * codec startup + */ +static int setup_virtual_fe(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm *pcm) +{ + struct snd_pcm_substream *substream; + struct snd_pcm_str *pstr; + int stream_dir; + + /* create pcm */ + pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + /* set up playback/capture substream */ + if (rtd->dai_link->dpcm_playback) + stream_dir = SNDRV_PCM_STREAM_PLAYBACK; + else + stream_dir = SNDRV_PCM_STREAM_CAPTURE; + + pstr = &pcm->streams[stream_dir]; + + /* allocate memory for substream */ + substream = kzalloc(sizeof(*substream), GFP_KERNEL); + if (!substream) + return -ENOMEM; + + /* define substream */ + substream->pcm = pcm; + substream->pstr = pstr; + substream->number = 0; + substream->stream = stream_dir; + sprintf(substream->name, "subdevice #%i", 0); + substream->buffer_bytes_max = UINT_MAX; + + /* set pcm substream */ + pstr->substream = substream; + + pcm->nonatomic = rtd->dai_link->nonatomic; + rtd->pcm = pcm; + pcm->private_data = rtd; + + return 0; +} + /* create a new pcm */ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) { @@ -3043,6 +3196,13 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num, playback, capture, &pcm); } else { + + /* set up virtual FE dai link */ + if (rtd->dai_link->dynamic && rtd->dai_link->no_pcm) { + setup_virtual_fe(rtd, pcm); + goto out; + } + if (rtd->dai_link->dynamic) snprintf(new_name, sizeof(new_name), "%s (*)", rtd->dai_link->stream_name);