Skip to content

Commit 48eb4ba

Browse files
author
Codrin Ciubotariu
committed
ASoC: soc: pcm: add support for DPCM BE to BE connection
Some FEs can connect BE capture audio interfaces to BE playback audio interfaces. This is accomplished by adding the dpcm_loopback flag in the snd_soc_dai_link structure, allowing the machine driver to mention that the FE (dynamic set) has loopback capability (capture->playback). The FE will have its callbacks called twice, once for capture stream and once for playback stream. The HW parameters for FE will not be set, since the HW parameters are on the BEs. The transfers are executed with minimal to none CPU intervention. Signed-off-by: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
1 parent e738c1c commit 48eb4ba

3 files changed

Lines changed: 361 additions & 31 deletions

File tree

include/sound/soc.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,7 @@ struct snd_soc_dai_link {
694694

695695
/* This DAI link can route to other DAI links at runtime (Frontend)*/
696696
unsigned int dynamic:1;
697+
unsigned int dpcm_loopback:1;
697698

698699
/* DPCM capture and Playback support */
699700
unsigned int dpcm_capture:1;

sound/soc/soc-dapm.c

Lines changed: 169 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4014,6 +4014,108 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
40144014
return ret;
40154015
}
40164016

4017+
static int
4018+
snd_soc_dai_link_loopback_event_pre_pmu(struct snd_soc_dapm_widget *w,
4019+
struct snd_pcm_substream *substream)
4020+
{
4021+
struct snd_soc_dapm_path *path;
4022+
struct snd_soc_dai *source, *sink;
4023+
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
4024+
int ret = 0;
4025+
4026+
rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].state = SND_SOC_DPCM_STATE_START;
4027+
rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].state = SND_SOC_DPCM_STATE_START;
4028+
4029+
substream->stream = SNDRV_PCM_STREAM_CAPTURE;
4030+
snd_soc_dapm_widget_for_each_source_path(w, path) {
4031+
source = path->source->priv;
4032+
snd_soc_dai_activate(source, substream->stream);
4033+
}
4034+
4035+
substream->stream = SNDRV_PCM_STREAM_PLAYBACK;
4036+
snd_soc_dapm_widget_for_each_sink_path(w, path) {
4037+
sink = path->sink->priv;
4038+
snd_soc_dai_activate(sink, substream->stream);
4039+
}
4040+
4041+
return ret;
4042+
}
4043+
4044+
static int snd_soc_dai_link_loopback_event(struct snd_soc_dapm_widget *w,
4045+
struct snd_kcontrol *kcontrol, int event)
4046+
{
4047+
struct snd_soc_dapm_path *path;
4048+
struct snd_soc_dai *source, *sink;
4049+
struct snd_pcm_substream *substream = w->priv;
4050+
int ret = 0, saved_stream = substream->stream;
4051+
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
4052+
4053+
if (WARN_ON(list_empty(&w->edges[SND_SOC_DAPM_DIR_OUT]) ||
4054+
list_empty(&w->edges[SND_SOC_DAPM_DIR_IN])))
4055+
return -EINVAL;
4056+
4057+
switch (event) {
4058+
case SND_SOC_DAPM_PRE_PMU:
4059+
ret = snd_soc_dai_link_loopback_event_pre_pmu(w, substream);
4060+
if (ret < 0)
4061+
goto out;
4062+
4063+
break;
4064+
4065+
case SND_SOC_DAPM_POST_PMU:
4066+
snd_soc_dapm_widget_for_each_sink_path(w, path) {
4067+
sink = path->sink->priv;
4068+
4069+
ret = snd_soc_dai_digital_mute(sink, 0,
4070+
SNDRV_PCM_STREAM_PLAYBACK);
4071+
if (ret != 0 && ret != -EOPNOTSUPP)
4072+
dev_warn(sink->dev,
4073+
"ASoC: Failed to unmute: %d\n", ret);
4074+
ret = 0;
4075+
}
4076+
break;
4077+
4078+
case SND_SOC_DAPM_PRE_PMD:
4079+
snd_soc_dapm_widget_for_each_sink_path(w, path) {
4080+
sink = path->sink->priv;
4081+
4082+
ret = snd_soc_dai_digital_mute(sink, 1,
4083+
SNDRV_PCM_STREAM_PLAYBACK);
4084+
if (ret != 0 && ret != -EOPNOTSUPP)
4085+
dev_warn(sink->dev,
4086+
"ASoC: Failed to mute: %d\n", ret);
4087+
ret = 0;
4088+
}
4089+
4090+
substream->stream = SNDRV_PCM_STREAM_CAPTURE;
4091+
snd_soc_dapm_widget_for_each_source_path(w, path) {
4092+
source = path->source->priv;
4093+
snd_soc_dai_deactivate(source, substream->stream);
4094+
}
4095+
4096+
substream->stream = SNDRV_PCM_STREAM_PLAYBACK;
4097+
snd_soc_dapm_widget_for_each_sink_path(w, path) {
4098+
sink = path->sink->priv;
4099+
snd_soc_dai_deactivate(sink, substream->stream);
4100+
}
4101+
break;
4102+
4103+
case SND_SOC_DAPM_POST_PMD:
4104+
rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].state = SND_SOC_DPCM_STATE_CLOSE;
4105+
rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].state = SND_SOC_DPCM_STATE_CLOSE;
4106+
break;
4107+
4108+
default:
4109+
WARN(1, "Unknown event %d\n", event);
4110+
ret = -EINVAL;
4111+
}
4112+
4113+
out:
4114+
/* Restore the substream direction */
4115+
substream->stream = saved_stream;
4116+
return ret;
4117+
}
4118+
40174119
static int snd_soc_dapm_dai_link_get(struct snd_kcontrol *kcontrol,
40184120
struct snd_ctl_elem_value *ucontrol)
40194121
{
@@ -4132,6 +4234,47 @@ snd_soc_dapm_alloc_kcontrol(struct snd_soc_card *card,
41324234
return NULL;
41334235
}
41344236

4237+
static struct snd_soc_dapm_widget *
4238+
snd_soc_dapm_new_loopback_dai(struct snd_soc_card *card, struct snd_pcm_substream *substream,
4239+
char *id)
4240+
{
4241+
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
4242+
struct snd_soc_dapm_widget template;
4243+
struct snd_soc_dapm_widget *w;
4244+
char *link_name;
4245+
int ret;
4246+
4247+
link_name = devm_kasprintf(card->dev, GFP_KERNEL, "%s-%s",
4248+
rtd->dai_link->name, id);
4249+
if (!link_name)
4250+
return ERR_PTR(-ENOMEM);
4251+
4252+
memset(&template, 0, sizeof(template));
4253+
template.reg = SND_SOC_NOPM;
4254+
template.id = snd_soc_dapm_dai_link;
4255+
template.name = link_name;
4256+
template.event = snd_soc_dai_link_loopback_event;
4257+
template.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
4258+
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD;
4259+
template.kcontrol_news = NULL;
4260+
4261+
w = snd_soc_dapm_new_control_unlocked(&card->dapm, &template);
4262+
if (IS_ERR(w)) {
4263+
ret = PTR_ERR(w);
4264+
dev_err(rtd->dev, "ASoC: Failed to create %s widget: %d\n",
4265+
link_name, ret);
4266+
goto outfree_link_name;
4267+
}
4268+
4269+
w->priv = substream;
4270+
4271+
return w;
4272+
4273+
outfree_link_name:
4274+
devm_kfree(card->dev, link_name);
4275+
return ERR_PTR(ret);
4276+
}
4277+
41354278
static struct snd_soc_dapm_widget *
41364279
snd_soc_dapm_new_dai(struct snd_soc_card *card,
41374280
struct snd_pcm_substream *substream,
@@ -4337,14 +4480,30 @@ static void dapm_connect_dai_pair(struct snd_soc_card *card,
43374480
struct snd_pcm_substream *substream;
43384481
struct snd_pcm_str *streams = rtd->pcm->streams;
43394482

4340-
if (dai_link->params) {
4483+
if (dai_link->params || (rtd->dai_link->dynamic && rtd->dai_link->dpcm_loopback)) {
43414484
playback_cpu = cpu_dai->capture_widget;
43424485
capture_cpu = cpu_dai->playback_widget;
43434486
} else {
43444487
playback_cpu = cpu_dai->playback_widget;
43454488
capture_cpu = cpu_dai->capture_widget;
43464489
}
43474490

4491+
if (rtd->dai_link->dynamic && rtd->dai_link->dpcm_loopback) {
4492+
if (!rtd->playback_widget && !rtd->capture_widget) {
4493+
streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd;
4494+
streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd;
4495+
substream = streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
4496+
dai = snd_soc_dapm_new_loopback_dai(card, substream, "hostless");
4497+
if (IS_ERR(dai))
4498+
return;
4499+
rtd->playback_widget = dai;
4500+
rtd->capture_widget = dai;
4501+
}
4502+
dapm_connect_dai_routes(&card->dapm, cpu_dai, playback_cpu,
4503+
dai,
4504+
cpu_dai, capture_cpu);
4505+
return;
4506+
}
43484507
/* connect BE DAI playback if widgets are valid */
43494508
codec = codec_dai->playback_widget;
43504509

@@ -4431,13 +4590,19 @@ void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card)
44314590
* dynamic FE links have no fixed DAI mapping.
44324591
* CODEC<->CODEC links have no direct connection.
44334592
*/
4434-
if (rtd->dai_link->dynamic)
4593+
if (rtd->dai_link->dynamic && !rtd->dai_link->dpcm_loopback)
44354594
continue;
44364595

44374596
if (rtd->num_cpus == 1) {
4438-
for_each_rtd_codec_dais(rtd, i, codec_dai)
4439-
dapm_connect_dai_pair(card, rtd, codec_dai,
4597+
/* connect FE to FE */
4598+
if (rtd->dai_link->dynamic && rtd->dai_link->dpcm_loopback) {
4599+
dapm_connect_dai_pair(card, rtd, asoc_rtd_to_cpu(rtd, 0),
44404600
asoc_rtd_to_cpu(rtd, 0));
4601+
} else {
4602+
for_each_rtd_codec_dais(rtd, i, codec_dai)
4603+
dapm_connect_dai_pair(card, rtd, codec_dai,
4604+
asoc_rtd_to_cpu(rtd, 0));
4605+
}
44414606
} else if (rtd->num_codecs == rtd->num_cpus) {
44424607
for_each_rtd_codec_dais(rtd, i, codec_dai)
44434608
dapm_connect_dai_pair(card, rtd, codec_dai,

0 commit comments

Comments
 (0)