Commit a150345a authored by Bard Liao's avatar Bard Liao Committed by Mark Brown

ASoC: SOF: ipc4-topology: add SoundWire/ALH aggregation support

Some SoundWire hardware topologies rely on different amplifiers or
capture devices connected on different links. These devices need to be
'aggregated', remain synchronized and be handled as a single logical
device.

In the IPC3 solution, the aggregation for amplifiers was handled by a
firmware 'demux' component. In the IPC4 solution, the demux component is
not needed, the gateway component can handle multiple ALH/DMA transfers
at the same time. This change makes the topology slightly more complicated
in that only one ALH DAI will be connected in the topology with the
gateway. The other DAIs that are part of the 'aggregated' dailink are not
shown in the DAPM graph as connected to the gateway, but they will however
be activated thanks to a feature in soc-dapm.c where events are forwarded
to all DAIs in the dailink (see soc_dapm_stream_event).

The topology also sets the same stream name for all widgets, dais and
dailinks, so a search for the stream name helps identify cases where
SoundWire/ALH aggregation is needed.
Signed-off-by: default avatarBard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: default avatarPierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: default avatarRanjani Sridharan <ranjani.sridharan@linux.intel.com>
Link: https://lore.kernel.org/r/20220614092630.20144-1-yung-chuan.liao@linux.intel.comSigned-off-by: default avatarMark Brown <broonie@kernel.org>
parent 30ac4984
......@@ -19,6 +19,8 @@
#define SOF_IPC4_GAIN_PARAM_ID 0
#define SOF_IPC4_TPLG_ABI_SIZE 6
static DEFINE_IDA(alh_group_ida);
static const struct sof_topology_token ipc4_sched_tokens[] = {
{SOF_TKN_SCHED_LP_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
offsetof(struct sof_ipc4_pipeline, lp_mode)}
......@@ -478,7 +480,9 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget)
switch (ipc4_copier->dai_type) {
case SOF_DAI_INTEL_ALH:
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct sof_ipc4_alh_configuration_blob *blob;
struct snd_sof_widget *w;
blob = kzalloc(sizeof(*blob), GFP_KERNEL);
if (!blob) {
......@@ -486,6 +490,14 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget)
goto err;
}
list_for_each_entry(w, &sdev->widget_list, list) {
if (w->widget->sname &&
strcmp(w->widget->sname, swidget->widget->sname))
continue;
blob->alh_cfg.count++;
}
ipc4_copier->copier_config = (uint32_t *)blob;
ipc4_copier->data.gtw_cfg.config_length = sizeof(*blob) >> 2;
break;
......@@ -844,6 +856,17 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget)
struct snd_sof_dai *dai = swidget->private;
ipc4_copier = dai->private;
if (ipc4_copier->dai_type == SOF_DAI_INTEL_ALH) {
struct sof_ipc4_alh_configuration_blob *blob;
unsigned int group_id;
blob = (struct sof_ipc4_alh_configuration_blob *)ipc4_copier->copier_config;
if (blob->alh_cfg.count > 1) {
group_id = SOF_IPC4_NODE_INDEX(ipc4_copier->data.gtw_cfg.node_id) -
ALH_MULTI_GTW_BASE;
ida_free(&alh_group_ida, group_id);
}
}
}
if (ipc4_copier) {
......@@ -973,6 +996,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
struct sof_ipc4_copier_data *copier_data;
struct snd_pcm_hw_params *ref_params;
struct sof_ipc4_copier *ipc4_copier;
struct snd_sof_dai *dai;
struct snd_mask *fmt;
int out_sample_valid_bits;
size_t ref_audio_fmt_size;
......@@ -1022,7 +1046,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
case snd_soc_dapm_dai_in:
case snd_soc_dapm_dai_out:
{
struct snd_sof_dai *dai = swidget->private;
dai = swidget->private;
ipc4_copier = (struct sof_ipc4_copier *)dai->private;
copier_data = &ipc4_copier->data;
......@@ -1077,22 +1101,56 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
*/
if (ipc4_copier->dai_type == SOF_DAI_INTEL_ALH) {
struct sof_ipc4_alh_configuration_blob *blob;
struct sof_ipc4_copier_data *alh_data;
struct sof_ipc4_copier *alh_copier;
struct snd_sof_widget *w;
u32 ch_mask = 0;
u32 ch_map;
int i;
blob = (struct sof_ipc4_alh_configuration_blob *)ipc4_copier->copier_config;
/* TODO: add aggregation mode support */
blob->alh_cfg.count = 1;
blob->alh_cfg.mapping[0].alh_id = copier_data->gtw_cfg.node_id;
blob->gw_attr.lp_buffer_alloc = 0;
/* Get channel_mask from ch_map */
ch_map = copier_data->base_config.audio_fmt.ch_map;
for (i = 0; ch_map; i++) {
if ((ch_map & 0xf) != 0xf)
blob->alh_cfg.mapping[0].channel_mask |= BIT(i);
ch_mask |= BIT(i);
ch_map >>= 4;
}
/*
* Set each gtw_cfg.node_id to blob->alh_cfg.mapping[]
* for all widgets with the same stream name
*/
i = 0;
list_for_each_entry(w, &sdev->widget_list, list) {
if (w->widget->sname &&
strcmp(w->widget->sname, swidget->widget->sname))
continue;
dai = w->private;
alh_copier = (struct sof_ipc4_copier *)dai->private;
alh_data = &alh_copier->data;
blob->alh_cfg.mapping[i].alh_id = alh_data->gtw_cfg.node_id;
blob->alh_cfg.mapping[i].channel_mask = ch_mask;
i++;
}
if (blob->alh_cfg.count > 1) {
int group_id;
group_id = ida_alloc_max(&alh_group_ida, ALH_MULTI_GTW_COUNT,
GFP_KERNEL);
if (group_id < 0)
return group_id;
/* add multi-gateway base */
group_id += ALH_MULTI_GTW_BASE;
copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
copier_data->gtw_cfg.node_id |= SOF_IPC4_NODE_INDEX(group_id);
}
}
}
}
......
......@@ -42,6 +42,17 @@
#define ALH_MAX_NUMBER_OF_GTW 16
/*
* The base of multi-gateways. Multi-gateways addressing starts from
* ALH_MULTI_GTW_BASE and there are ALH_MULTI_GTW_COUNT multi-sources
* and ALH_MULTI_GTW_COUNT multi-sinks available.
* Addressing is continuous from ALH_MULTI_GTW_BASE to
* ALH_MULTI_GTW_BASE + ALH_MULTI_GTW_COUNT - 1.
*/
#define ALH_MULTI_GTW_BASE 0x50
/* A magic number from FW */
#define ALH_MULTI_GTW_COUNT 8
/**
* struct sof_ipc4_pipeline - pipeline config data
* @priority: Priority of this pipeline
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment