Blob Blame History Raw
From a150345aa758492e05d2934f318ce7c2566b1cfe Mon Sep 17 00:00:00 2001
From: Bard Liao <yung-chuan.liao@linux.intel.com>
Date: Tue, 14 Jun 2022 17:26:30 +0800
Subject: [PATCH] ASoC: SOF: ipc4-topology: add SoundWire/ALH aggregation support
Git-commit: a150345aa758492e05d2934f318ce7c2566b1cfe
Patch-mainline: v6.0-rc1
References: jsc#PED-850

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: Bard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Link: https://lore.kernel.org/r/20220614092630.20144-1-yung-chuan.liao@linux.intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>
Acked-by: Takashi Iwai <tiwai@suse.de>

---
 sound/soc/sof/ipc4-topology.c | 68 ++++++++++++++++++++++++++++++++---
 sound/soc/sof/ipc4-topology.h | 11 ++++++
 2 files changed, 74 insertions(+), 5 deletions(-)

diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c
index cb0f0823b8eb..3c949298e007 100644
--- a/sound/soc/sof/ipc4-topology.c
+++ b/sound/soc/sof/ipc4-topology.c
@@ -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);
+			}
 		}
 	}
 	}
diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h
index 1a9c0627bae9..3bc2fe38c733 100644
--- a/sound/soc/sof/ipc4-topology.h
+++ b/sound/soc/sof/ipc4-topology.h
@@ -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
-- 
2.35.3