Skip to content

Commit dc8384d

Browse files
committed
ASoC: SOF: Support for echoref (virtual DAI)
Merge series from Peter Ujfalusi <peter.ujfalusi@linux.intel.com>: The series adds support for echo reference functionality by allowing the capturing of playback audio right before it leaves the DSP. For this to work correctly we need a virtual DAI that is also connected to the echo reference capture device and in absence of playback a signal generator generates silence to allow the capture to run. When the real playback starts, application will start to receive the playback audio to be usable for echo reference.
2 parents 6f0fce2 + 6c52fda commit dc8384d

9 files changed

Lines changed: 370 additions & 99 deletions

File tree

include/uapi/sound/sof/tokens.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@
5656
#define SOF_TKN_SCHED_LP_MODE 207
5757
#define SOF_TKN_SCHED_MEM_USAGE 208
5858
#define SOF_TKN_SCHED_USE_CHAIN_DMA 209
59+
#define SOF_TKN_SCHED_KCPS 210
60+
#define SOF_TKN_SCHED_DIRECTION 211
61+
#define SOF_TKN_SCHED_DIRECTION_VALID 212
5962

6063
/* volume */
6164
#define SOF_TKN_VOLUME_RAMP_STEP_TYPE 250

sound/soc/intel/boards/sof_sdw.c

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,6 +1186,34 @@ static int create_bt_dailinks(struct snd_soc_card *card,
11861186
return 0;
11871187
}
11881188

1189+
static int create_echoref_dailink(struct snd_soc_card *card,
1190+
struct snd_soc_dai_link **dai_links, int *be_id)
1191+
{
1192+
struct device *dev = card->dev;
1193+
int ret;
1194+
char *name = devm_kasprintf(dev, GFP_KERNEL, "Loopback_Virtual");
1195+
1196+
if (!name)
1197+
return -ENOMEM;
1198+
1199+
/*
1200+
* use dummy DAI names as this won't be connected to an actual DAI but just to establish a
1201+
* fe <-> be connection for loopback capture for echo reference
1202+
*/
1203+
ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, name,
1204+
0, 1, "Loopback Virtual Pin", "dummy",
1205+
snd_soc_dummy_dlc.name, snd_soc_dummy_dlc.dai_name,
1206+
1, NULL, NULL);
1207+
if (ret)
1208+
return ret;
1209+
1210+
(*dai_links)++;
1211+
1212+
dev_dbg(dev, "Added echo reference DAI link\n");
1213+
1214+
return 0;
1215+
}
1216+
11891217
static int sof_card_dai_links_create(struct snd_soc_card *card)
11901218
{
11911219
struct device *dev = card->dev;
@@ -1294,8 +1322,12 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)
12941322
goto err_end;
12951323
}
12961324

1297-
/* allocate BE dailinks */
1298-
num_links = sdw_be_num + ssp_num + dmic_num + hdmi_num + bt_num;
1325+
/*
1326+
* allocate BE dailinks, add an extra DAI link for echo reference capture.
1327+
* This should be the last DAI link and it is expected both for monolithic
1328+
* and functional SOF topologies to support echo reference.
1329+
*/
1330+
num_links = sdw_be_num + ssp_num + dmic_num + hdmi_num + bt_num + 1;
12991331
dai_links = devm_kcalloc(dev, num_links, sizeof(*dai_links), GFP_KERNEL);
13001332
if (!dai_links) {
13011333
ret = -ENOMEM;
@@ -1344,6 +1376,13 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)
13441376
goto err_end;
13451377
}
13461378

1379+
/* dummy echo ref link. keep this as the last DAI link. The DAI link ID does not matter */
1380+
ret = create_echoref_dailink(card, &dai_links, &be_id);
1381+
if (ret) {
1382+
dev_err(dev, "failed to create echo ref dai link: %d\n", ret);
1383+
goto err_end;
1384+
}
1385+
13471386
WARN_ON(codec_conf != card->codec_conf + card->num_configs);
13481387
WARN_ON(dai_links != card->dai_link + card->num_links);
13491388

sound/soc/sof/intel/hda-dai.c

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,22 @@ static const struct hda_dai_widget_dma_ops *
7070
hda_dai_get_ops(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai)
7171
{
7272
struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
73-
struct snd_sof_widget *swidget = w->dobj.private;
73+
struct snd_sof_widget *swidget;
7474
struct snd_sof_dev *sdev;
7575
struct snd_sof_dai *sdai;
7676

77-
sdev = widget_to_sdev(w);
77+
/*
78+
* this is unlikely if the topology and the machine driver DAI links match.
79+
* But if there's a missing DAI link in topology, this will prevent a NULL pointer
80+
* dereference later on.
81+
*/
82+
if (!w) {
83+
dev_err(cpu_dai->dev, "%s: widget is NULL\n", __func__);
84+
return NULL;
85+
}
7886

87+
sdev = widget_to_sdev(w);
88+
swidget = w->dobj.private;
7989
if (!swidget) {
8090
dev_err(sdev->dev, "%s: swidget is NULL\n", __func__);
8191
return NULL;
@@ -856,6 +866,14 @@ struct snd_soc_dai_driver skl_dai[] = {
856866
.channels_max = 4,
857867
},
858868
},
869+
{
870+
/* Virtual CPU DAI for Echo reference */
871+
.name = "Loopback Virtual Pin",
872+
.capture = {
873+
.channels_min = 1,
874+
.channels_max = 2,
875+
},
876+
},
859877
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
860878
{
861879
.name = "iDisp1 Pin",

sound/soc/sof/intel/hda.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -418,10 +418,10 @@
418418
(HDA_DSP_BDL_SIZE / sizeof(struct sof_intel_dsp_bdl))
419419

420420
/* Number of DAIs */
421-
#define SOF_SKL_NUM_DAIS_NOCODEC 8
421+
#define SOF_SKL_NUM_DAIS_NOCODEC 9
422422

423423
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
424-
#define SOF_SKL_NUM_DAIS 15
424+
#define SOF_SKL_NUM_DAIS 16
425425
#else
426426
#define SOF_SKL_NUM_DAIS SOF_SKL_NUM_DAIS_NOCODEC
427427
#endif

sound/soc/sof/ipc4-topology.c

Lines changed: 69 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ static const struct sof_topology_token ipc4_sched_tokens[] = {
7676
offsetof(struct sof_ipc4_pipeline, core_id)},
7777
{SOF_TKN_SCHED_PRIORITY, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
7878
offsetof(struct sof_ipc4_pipeline, priority)},
79+
{SOF_TKN_SCHED_DIRECTION, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
80+
offsetof(struct sof_ipc4_pipeline, direction)},
81+
{SOF_TKN_SCHED_DIRECTION, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16,
82+
offsetof(struct sof_ipc4_pipeline, direction_valid)},
7983
};
8084

8185
static const struct sof_topology_token pipeline_tokens[] = {
@@ -939,6 +943,10 @@ static int sof_ipc4_widget_setup_comp_pipeline(struct snd_sof_widget *swidget)
939943

940944
swidget->core = pipeline->core_id;
941945
spipe->core_mask |= BIT(pipeline->core_id);
946+
if (pipeline->direction_valid) {
947+
spipe->direction = pipeline->direction;
948+
spipe->direction_valid = true;
949+
}
942950

943951
if (pipeline->use_chain_dma) {
944952
dev_dbg(scomp->dev, "Set up chain DMA for %s\n", swidget->widget->name);
@@ -954,9 +962,9 @@ static int sof_ipc4_widget_setup_comp_pipeline(struct snd_sof_widget *swidget)
954962
goto err;
955963
}
956964

957-
dev_dbg(scomp->dev, "pipeline '%s': id %d, pri %d, core_id %u, lp mode %d\n",
965+
dev_dbg(scomp->dev, "pipeline '%s': id %d, pri %d, core_id %u, lp mode %d direction %d\n",
958966
swidget->widget->name, swidget->pipeline_id,
959-
pipeline->priority, pipeline->core_id, pipeline->lp_mode);
967+
pipeline->priority, pipeline->core_id, pipeline->lp_mode, pipeline->direction);
960968

961969
swidget->private = pipeline;
962970

@@ -2004,6 +2012,25 @@ sof_ipc4_prepare_dai_copier(struct snd_sof_dev *sdev, struct snd_sof_dai *dai,
20042012
return ret;
20052013
}
20062014

2015+
static void sof_ipc4_host_config(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,
2016+
struct snd_sof_platform_stream_params *platform_params)
2017+
{
2018+
struct sof_ipc4_copier *ipc4_copier = (struct sof_ipc4_copier *)swidget->private;
2019+
struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
2020+
struct sof_ipc4_copier_data *copier_data = &ipc4_copier->data;
2021+
struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
2022+
u32 host_dma_id = platform_params->stream_tag - 1;
2023+
2024+
if (pipeline->use_chain_dma) {
2025+
pipeline->msg.primary &= ~SOF_IPC4_GLB_CHAIN_DMA_HOST_ID_MASK;
2026+
pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_HOST_ID(host_dma_id);
2027+
return;
2028+
}
2029+
2030+
copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
2031+
copier_data->gtw_cfg.node_id |= SOF_IPC4_NODE_INDEX(host_dma_id);
2032+
}
2033+
20072034
static int
20082035
sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
20092036
struct snd_pcm_hw_params *fe_params,
@@ -2726,12 +2753,14 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget,
27262753
int input_fmt_index = 0;
27272754
int ret;
27282755

2729-
input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget,
2730-
&process->base_config,
2731-
pipeline_params,
2732-
available_fmt);
2733-
if (input_fmt_index < 0)
2734-
return input_fmt_index;
2756+
if (available_fmt->num_input_formats) {
2757+
input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget,
2758+
&process->base_config,
2759+
pipeline_params,
2760+
available_fmt);
2761+
if (input_fmt_index < 0)
2762+
return input_fmt_index;
2763+
}
27352764

27362765
/* Configure output audio format only if the module supports output */
27372766
if (available_fmt->num_output_formats) {
@@ -2740,12 +2769,28 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget,
27402769
u32 out_ref_rate, out_ref_channels;
27412770
int out_ref_valid_bits, out_ref_type;
27422771

2743-
in_fmt = &available_fmt->input_pin_fmts[input_fmt_index].audio_fmt;
2772+
if (available_fmt->num_input_formats) {
2773+
in_fmt = &available_fmt->input_pin_fmts[input_fmt_index].audio_fmt;
27442774

2745-
out_ref_rate = in_fmt->sampling_frequency;
2746-
out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
2747-
out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);
2748-
out_ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg);
2775+
out_ref_rate = in_fmt->sampling_frequency;
2776+
out_ref_channels =
2777+
SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
2778+
out_ref_valid_bits =
2779+
SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);
2780+
out_ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg);
2781+
} else {
2782+
/* for modules without input formats, use FE params as reference */
2783+
out_ref_rate = params_rate(fe_params);
2784+
out_ref_channels = params_channels(fe_params);
2785+
ret = sof_ipc4_get_sample_type(sdev, fe_params);
2786+
if (ret < 0)
2787+
return ret;
2788+
out_ref_type = (u32)ret;
2789+
2790+
out_ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params);
2791+
if (out_ref_valid_bits < 0)
2792+
return out_ref_valid_bits;
2793+
}
27492794

27502795
output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget,
27512796
&process->base_config,
@@ -2773,6 +2818,16 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget,
27732818
if (ret)
27742819
return ret;
27752820
}
2821+
2822+
/* set base cfg to match the first output format if there are no input formats */
2823+
if (!available_fmt->num_input_formats) {
2824+
struct sof_ipc4_audio_format *out_fmt;
2825+
2826+
out_fmt = &available_fmt->output_pin_fmts[0].audio_fmt;
2827+
2828+
/* copy output format */
2829+
memcpy(&process->base_config.audio_fmt, out_fmt, sizeof(*out_fmt));
2830+
}
27762831
}
27772832

27782833
sof_ipc4_dbg_module_audio_format(sdev->dev, swidget, available_fmt,
@@ -3929,4 +3984,5 @@ const struct sof_ipc_tplg_ops ipc4_tplg_ops = {
39293984
.dai_get_param = sof_ipc4_dai_get_param,
39303985
.tear_down_all_pipelines = sof_ipc4_tear_down_all_pipelines,
39313986
.link_setup = sof_ipc4_link_setup,
3987+
.host_config = sof_ipc4_host_config,
39323988
};

sound/soc/sof/ipc4-topology.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@ struct sof_ipc4_copier_config_set_sink_format {
150150
* @use_chain_dma: flag to indicate if the firmware shall use chained DMA
151151
* @msg: message structure for pipeline
152152
* @skip_during_fe_trigger: skip triggering this pipeline during the FE DAI trigger
153+
* @direction_valid: flag indicating if valid direction is set in topology
154+
* @direction: pipeline direction set in topology if direction_valid is true
153155
*/
154156
struct sof_ipc4_pipeline {
155157
uint32_t priority;
@@ -160,6 +162,8 @@ struct sof_ipc4_pipeline {
160162
bool use_chain_dma;
161163
struct sof_ipc4_msg msg;
162164
bool skip_during_fe_trigger;
165+
bool direction_valid;
166+
u32 direction;
163167
};
164168

165169
/**

0 commit comments

Comments
 (0)