xref: /openbmc/linux/sound/soc/sof/ipc4-topology.c (revision 11f605633b33cdffd4ffe3b8e1e89590e8f521e7)
190e89155SRanjani Sridharan // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
290e89155SRanjani Sridharan //
390e89155SRanjani Sridharan // This file is provided under a dual BSD/GPLv2 license.  When using or
490e89155SRanjani Sridharan // redistributing this file, you may do so under either license.
590e89155SRanjani Sridharan //
690e89155SRanjani Sridharan // Copyright(c) 2022 Intel Corporation. All rights reserved.
790e89155SRanjani Sridharan //
890e89155SRanjani Sridharan //
990e89155SRanjani Sridharan #include <uapi/sound/sof/tokens.h>
1090e89155SRanjani Sridharan #include <sound/pcm_params.h>
1190e89155SRanjani Sridharan #include <sound/sof/ext_manifest4.h>
12aa84ffb7SRanjani Sridharan #include <sound/intel-nhlt.h>
1390e89155SRanjani Sridharan #include "sof-priv.h"
1490e89155SRanjani Sridharan #include "sof-audio.h"
1590e89155SRanjani Sridharan #include "ipc4-priv.h"
1690e89155SRanjani Sridharan #include "ipc4-topology.h"
1790e89155SRanjani Sridharan #include "ops.h"
1890e89155SRanjani Sridharan 
19d97964f8SRanjani Sridharan #define SOF_IPC4_GAIN_PARAM_ID  0
20323aa1f0SRanjani Sridharan #define SOF_IPC4_TPLG_ABI_SIZE 6
21d97964f8SRanjani Sridharan 
22a150345aSBard Liao static DEFINE_IDA(alh_group_ida);
23a2ba1f70SBard Liao static DEFINE_IDA(pipeline_ida);
24a150345aSBard Liao 
2590e89155SRanjani Sridharan static const struct sof_topology_token ipc4_sched_tokens[] = {
2690e89155SRanjani Sridharan 	{SOF_TKN_SCHED_LP_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
2790e89155SRanjani Sridharan 		offsetof(struct sof_ipc4_pipeline, lp_mode)}
2890e89155SRanjani Sridharan };
2990e89155SRanjani Sridharan 
3090e89155SRanjani Sridharan static const struct sof_topology_token pipeline_tokens[] = {
3190e89155SRanjani Sridharan 	{SOF_TKN_SCHED_DYNAMIC_PIPELINE, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16,
3290e89155SRanjani Sridharan 		offsetof(struct snd_sof_widget, dynamic_pipeline_widget)},
3390e89155SRanjani Sridharan };
3490e89155SRanjani Sridharan 
352cabd02bSRanjani Sridharan static const struct sof_topology_token ipc4_comp_tokens[] = {
362cabd02bSRanjani Sridharan 	{SOF_TKN_COMP_CPC, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
372cabd02bSRanjani Sridharan 		offsetof(struct sof_ipc4_base_module_cfg, cpc)},
382cabd02bSRanjani Sridharan 	{SOF_TKN_COMP_IS_PAGES, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
392cabd02bSRanjani Sridharan 		offsetof(struct sof_ipc4_base_module_cfg, is_pages)},
402cabd02bSRanjani Sridharan };
412cabd02bSRanjani Sridharan 
422cabd02bSRanjani Sridharan static const struct sof_topology_token ipc4_audio_format_buffer_size_tokens[] = {
432cabd02bSRanjani Sridharan 	{SOF_TKN_CAVS_AUDIO_FORMAT_IBS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
442cabd02bSRanjani Sridharan 		offsetof(struct sof_ipc4_base_module_cfg, ibs)},
452cabd02bSRanjani Sridharan 	{SOF_TKN_CAVS_AUDIO_FORMAT_OBS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
462cabd02bSRanjani Sridharan 		offsetof(struct sof_ipc4_base_module_cfg, obs)},
472cabd02bSRanjani Sridharan };
482cabd02bSRanjani Sridharan 
492cabd02bSRanjani Sridharan static const struct sof_topology_token ipc4_in_audio_format_tokens[] = {
502cabd02bSRanjani Sridharan 	{SOF_TKN_CAVS_AUDIO_FORMAT_IN_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
512cabd02bSRanjani Sridharan 		offsetof(struct sof_ipc4_audio_format, sampling_frequency)},
522cabd02bSRanjani Sridharan 	{SOF_TKN_CAVS_AUDIO_FORMAT_IN_BIT_DEPTH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
532cabd02bSRanjani Sridharan 		offsetof(struct sof_ipc4_audio_format, bit_depth)},
542cabd02bSRanjani Sridharan 	{SOF_TKN_CAVS_AUDIO_FORMAT_IN_CH_MAP, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
552cabd02bSRanjani Sridharan 		offsetof(struct sof_ipc4_audio_format, ch_map)},
562cabd02bSRanjani Sridharan 	{SOF_TKN_CAVS_AUDIO_FORMAT_IN_CH_CFG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
572cabd02bSRanjani Sridharan 		offsetof(struct sof_ipc4_audio_format, ch_cfg)},
582cabd02bSRanjani Sridharan 	{SOF_TKN_CAVS_AUDIO_FORMAT_IN_INTERLEAVING_STYLE, SND_SOC_TPLG_TUPLE_TYPE_WORD,
592cabd02bSRanjani Sridharan 		get_token_u32, offsetof(struct sof_ipc4_audio_format, interleaving_style)},
602cabd02bSRanjani Sridharan 	{SOF_TKN_CAVS_AUDIO_FORMAT_IN_FMT_CFG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
612cabd02bSRanjani Sridharan 		offsetof(struct sof_ipc4_audio_format, fmt_cfg)},
622cabd02bSRanjani Sridharan };
632cabd02bSRanjani Sridharan 
642cabd02bSRanjani Sridharan static const struct sof_topology_token ipc4_out_audio_format_tokens[] = {
652cabd02bSRanjani Sridharan 	{SOF_TKN_CAVS_AUDIO_FORMAT_OUT_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
662cabd02bSRanjani Sridharan 		offsetof(struct sof_ipc4_audio_format, sampling_frequency)},
672cabd02bSRanjani Sridharan 	{SOF_TKN_CAVS_AUDIO_FORMAT_OUT_BIT_DEPTH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
682cabd02bSRanjani Sridharan 		offsetof(struct sof_ipc4_audio_format, bit_depth)},
692cabd02bSRanjani Sridharan 	{SOF_TKN_CAVS_AUDIO_FORMAT_OUT_CH_MAP, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
702cabd02bSRanjani Sridharan 		offsetof(struct sof_ipc4_audio_format, ch_map)},
712cabd02bSRanjani Sridharan 	{SOF_TKN_CAVS_AUDIO_FORMAT_OUT_CH_CFG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
722cabd02bSRanjani Sridharan 		offsetof(struct sof_ipc4_audio_format, ch_cfg)},
732cabd02bSRanjani Sridharan 	{SOF_TKN_CAVS_AUDIO_FORMAT_OUT_INTERLEAVING_STYLE, SND_SOC_TPLG_TUPLE_TYPE_WORD,
742cabd02bSRanjani Sridharan 		get_token_u32, offsetof(struct sof_ipc4_audio_format, interleaving_style)},
752cabd02bSRanjani Sridharan 	{SOF_TKN_CAVS_AUDIO_FORMAT_OUT_FMT_CFG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
762cabd02bSRanjani Sridharan 		offsetof(struct sof_ipc4_audio_format, fmt_cfg)},
772cabd02bSRanjani Sridharan };
782cabd02bSRanjani Sridharan 
792cabd02bSRanjani Sridharan static const struct sof_topology_token ipc4_copier_gateway_cfg_tokens[] = {
802cabd02bSRanjani Sridharan 	{SOF_TKN_CAVS_AUDIO_FORMAT_DMA_BUFFER_SIZE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 0},
812cabd02bSRanjani Sridharan };
822cabd02bSRanjani Sridharan 
832cabd02bSRanjani Sridharan static const struct sof_topology_token ipc4_copier_tokens[] = {
842cabd02bSRanjani Sridharan 	{SOF_TKN_INTEL_COPIER_NODE_TYPE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 0},
852cabd02bSRanjani Sridharan };
862cabd02bSRanjani Sridharan 
872cabd02bSRanjani Sridharan static const struct sof_topology_token ipc4_audio_fmt_num_tokens[] = {
882cabd02bSRanjani Sridharan 	{SOF_TKN_COMP_NUM_AUDIO_FORMATS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
892cabd02bSRanjani Sridharan 		0},
902cabd02bSRanjani Sridharan };
912cabd02bSRanjani Sridharan 
92abfb536bSRanjani Sridharan static const struct sof_topology_token dai_tokens[] = {
93abfb536bSRanjani Sridharan 	{SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type,
94abfb536bSRanjani Sridharan 		offsetof(struct sof_ipc4_copier, dai_type)},
95abfb536bSRanjani Sridharan 	{SOF_TKN_DAI_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
96abfb536bSRanjani Sridharan 		offsetof(struct sof_ipc4_copier, dai_index)},
97abfb536bSRanjani Sridharan };
98abfb536bSRanjani Sridharan 
992cabd02bSRanjani Sridharan /* Component extended tokens */
1002cabd02bSRanjani Sridharan static const struct sof_topology_token comp_ext_tokens[] = {
1012cabd02bSRanjani Sridharan 	{SOF_TKN_COMP_UUID, SND_SOC_TPLG_TUPLE_TYPE_UUID, get_token_uuid,
1022cabd02bSRanjani Sridharan 		offsetof(struct snd_sof_widget, uuid)},
1032cabd02bSRanjani Sridharan };
1042cabd02bSRanjani Sridharan 
1054f838ab2SRanjani Sridharan static const struct sof_topology_token gain_tokens[] = {
1064f838ab2SRanjani Sridharan 	{SOF_TKN_GAIN_RAMP_TYPE, SND_SOC_TPLG_TUPLE_TYPE_WORD,
1074f838ab2SRanjani Sridharan 		get_token_u32, offsetof(struct sof_ipc4_gain_data, curve_type)},
1084f838ab2SRanjani Sridharan 	{SOF_TKN_GAIN_RAMP_DURATION,
1094f838ab2SRanjani Sridharan 		SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
1104f838ab2SRanjani Sridharan 		offsetof(struct sof_ipc4_gain_data, curve_duration)},
1114f838ab2SRanjani Sridharan 	{SOF_TKN_GAIN_VAL, SND_SOC_TPLG_TUPLE_TYPE_WORD,
1124f838ab2SRanjani Sridharan 		get_token_u32, offsetof(struct sof_ipc4_gain_data, init_val)},
1134f838ab2SRanjani Sridharan };
1144f838ab2SRanjani Sridharan 
115b85f4fc4SRander Wang /* SRC */
116b85f4fc4SRander Wang static const struct sof_topology_token src_tokens[] = {
117b85f4fc4SRander Wang 	{SOF_TKN_SRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
118b85f4fc4SRander Wang 		offsetof(struct sof_ipc4_src, sink_rate)},
119b85f4fc4SRander Wang };
120b85f4fc4SRander Wang 
12190e89155SRanjani Sridharan static const struct sof_token_info ipc4_token_list[SOF_TOKEN_COUNT] = {
122abfb536bSRanjani Sridharan 	[SOF_DAI_TOKENS] = {"DAI tokens", dai_tokens, ARRAY_SIZE(dai_tokens)},
12390e89155SRanjani Sridharan 	[SOF_PIPELINE_TOKENS] = {"Pipeline tokens", pipeline_tokens, ARRAY_SIZE(pipeline_tokens)},
12490e89155SRanjani Sridharan 	[SOF_SCHED_TOKENS] = {"Scheduler tokens", ipc4_sched_tokens,
12590e89155SRanjani Sridharan 		ARRAY_SIZE(ipc4_sched_tokens)},
1262cabd02bSRanjani Sridharan 	[SOF_COMP_EXT_TOKENS] = {"Comp extended tokens", comp_ext_tokens,
1272cabd02bSRanjani Sridharan 		ARRAY_SIZE(comp_ext_tokens)},
1282cabd02bSRanjani Sridharan 	[SOF_COMP_TOKENS] = {"IPC4 Component tokens",
1292cabd02bSRanjani Sridharan 		ipc4_comp_tokens, ARRAY_SIZE(ipc4_comp_tokens)},
1302cabd02bSRanjani Sridharan 	[SOF_IN_AUDIO_FORMAT_TOKENS] = {"IPC4 Input Audio format tokens",
1312cabd02bSRanjani Sridharan 		ipc4_in_audio_format_tokens, ARRAY_SIZE(ipc4_in_audio_format_tokens)},
1322cabd02bSRanjani Sridharan 	[SOF_OUT_AUDIO_FORMAT_TOKENS] = {"IPC4 Output Audio format tokens",
1332cabd02bSRanjani Sridharan 		ipc4_out_audio_format_tokens, ARRAY_SIZE(ipc4_out_audio_format_tokens)},
1342cabd02bSRanjani Sridharan 	[SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS] = {"IPC4 Audio format buffer size tokens",
1352cabd02bSRanjani Sridharan 		ipc4_audio_format_buffer_size_tokens,
1362cabd02bSRanjani Sridharan 		ARRAY_SIZE(ipc4_audio_format_buffer_size_tokens)},
1372cabd02bSRanjani Sridharan 	[SOF_COPIER_GATEWAY_CFG_TOKENS] = {"IPC4 Copier gateway config tokens",
1382cabd02bSRanjani Sridharan 		ipc4_copier_gateway_cfg_tokens, ARRAY_SIZE(ipc4_copier_gateway_cfg_tokens)},
1392cabd02bSRanjani Sridharan 	[SOF_COPIER_TOKENS] = {"IPC4 Copier tokens", ipc4_copier_tokens,
1402cabd02bSRanjani Sridharan 		ARRAY_SIZE(ipc4_copier_tokens)},
1412cabd02bSRanjani Sridharan 	[SOF_AUDIO_FMT_NUM_TOKENS] = {"IPC4 Audio format number tokens",
1422cabd02bSRanjani Sridharan 		ipc4_audio_fmt_num_tokens, ARRAY_SIZE(ipc4_audio_fmt_num_tokens)},
1434f838ab2SRanjani Sridharan 	[SOF_GAIN_TOKENS] = {"Gain tokens", gain_tokens, ARRAY_SIZE(gain_tokens)},
144b85f4fc4SRander Wang 	[SOF_SRC_TOKENS] = {"SRC tokens", src_tokens, ARRAY_SIZE(src_tokens)},
14590e89155SRanjani Sridharan };
14690e89155SRanjani Sridharan 
1472cabd02bSRanjani Sridharan static void sof_ipc4_dbg_audio_format(struct device *dev,
1482cabd02bSRanjani Sridharan 				      struct sof_ipc4_audio_format *format,
1492cabd02bSRanjani Sridharan 				      size_t object_size, int num_format)
1502cabd02bSRanjani Sridharan {
1512cabd02bSRanjani Sridharan 	struct sof_ipc4_audio_format *fmt;
1522cabd02bSRanjani Sridharan 	void *ptr = format;
1532cabd02bSRanjani Sridharan 	int i;
1542cabd02bSRanjani Sridharan 
1552cabd02bSRanjani Sridharan 	for (i = 0; i < num_format; i++, ptr = (u8 *)ptr + object_size) {
1562cabd02bSRanjani Sridharan 		fmt = ptr;
1572cabd02bSRanjani Sridharan 		dev_dbg(dev,
1582cabd02bSRanjani Sridharan 			" #%d: %uKHz, %ubit (ch_map %#x ch_cfg %u interleaving_style %u fmt_cfg %#x)\n",
1592cabd02bSRanjani Sridharan 			i, fmt->sampling_frequency, fmt->bit_depth, fmt->ch_map,
1602cabd02bSRanjani Sridharan 			fmt->ch_cfg, fmt->interleaving_style, fmt->fmt_cfg);
1612cabd02bSRanjani Sridharan 	}
1622cabd02bSRanjani Sridharan }
1632cabd02bSRanjani Sridharan 
1642cabd02bSRanjani Sridharan /**
1652cabd02bSRanjani Sridharan  * sof_ipc4_get_audio_fmt - get available audio formats from swidget->tuples
1662cabd02bSRanjani Sridharan  * @scomp: pointer to pointer to SOC component
1672cabd02bSRanjani Sridharan  * @swidget: pointer to struct snd_sof_widget containing tuples
1682cabd02bSRanjani Sridharan  * @available_fmt: pointer to struct sof_ipc4_available_audio_format being filling in
1692cabd02bSRanjani Sridharan  * @has_out_format: true if available_fmt contains output format
1702cabd02bSRanjani Sridharan  *
1712cabd02bSRanjani Sridharan  * Return: 0 if successful
1722cabd02bSRanjani Sridharan  */
1732cabd02bSRanjani Sridharan static int sof_ipc4_get_audio_fmt(struct snd_soc_component *scomp,
1742cabd02bSRanjani Sridharan 				  struct snd_sof_widget *swidget,
1752cabd02bSRanjani Sridharan 				  struct sof_ipc4_available_audio_format *available_fmt,
1762cabd02bSRanjani Sridharan 				  bool has_out_format)
1772cabd02bSRanjani Sridharan {
1782cabd02bSRanjani Sridharan 	struct sof_ipc4_base_module_cfg *base_config;
1792cabd02bSRanjani Sridharan 	struct sof_ipc4_audio_format *out_format;
1802cabd02bSRanjani Sridharan 	int audio_fmt_num = 0;
1812cabd02bSRanjani Sridharan 	int ret, i;
1822cabd02bSRanjani Sridharan 
1832cabd02bSRanjani Sridharan 	ret = sof_update_ipc_object(scomp, &audio_fmt_num,
1842cabd02bSRanjani Sridharan 				    SOF_AUDIO_FMT_NUM_TOKENS, swidget->tuples,
1852cabd02bSRanjani Sridharan 				    swidget->num_tuples, sizeof(audio_fmt_num), 1);
1862cabd02bSRanjani Sridharan 	if (ret || audio_fmt_num <= 0) {
1872cabd02bSRanjani Sridharan 		dev_err(scomp->dev, "Invalid number of audio formats: %d\n", audio_fmt_num);
1882cabd02bSRanjani Sridharan 		return -EINVAL;
1892cabd02bSRanjani Sridharan 	}
1902cabd02bSRanjani Sridharan 	available_fmt->audio_fmt_num = audio_fmt_num;
1912cabd02bSRanjani Sridharan 
1922cabd02bSRanjani Sridharan 	dev_dbg(scomp->dev, "Number of audio formats: %d\n", available_fmt->audio_fmt_num);
1932cabd02bSRanjani Sridharan 
1942cabd02bSRanjani Sridharan 	base_config = kcalloc(available_fmt->audio_fmt_num, sizeof(*base_config), GFP_KERNEL);
1952cabd02bSRanjani Sridharan 	if (!base_config)
1962cabd02bSRanjani Sridharan 		return -ENOMEM;
1972cabd02bSRanjani Sridharan 
1982cabd02bSRanjani Sridharan 	/* set cpc and is_pages for all base_cfg */
1992cabd02bSRanjani Sridharan 	for (i = 0; i < available_fmt->audio_fmt_num; i++) {
2002cabd02bSRanjani Sridharan 		ret = sof_update_ipc_object(scomp, &base_config[i],
2012cabd02bSRanjani Sridharan 					    SOF_COMP_TOKENS, swidget->tuples,
2022cabd02bSRanjani Sridharan 					    swidget->num_tuples, sizeof(*base_config), 1);
2032cabd02bSRanjani Sridharan 		if (ret) {
2042cabd02bSRanjani Sridharan 			dev_err(scomp->dev, "parse comp tokens failed %d\n", ret);
2052cabd02bSRanjani Sridharan 			goto err_in;
2062cabd02bSRanjani Sridharan 		}
2072cabd02bSRanjani Sridharan 	}
2082cabd02bSRanjani Sridharan 
2092cabd02bSRanjani Sridharan 	/* copy the ibs/obs for each base_cfg */
2102cabd02bSRanjani Sridharan 	ret = sof_update_ipc_object(scomp, base_config,
2112cabd02bSRanjani Sridharan 				    SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS, swidget->tuples,
2122cabd02bSRanjani Sridharan 				    swidget->num_tuples, sizeof(*base_config),
2132cabd02bSRanjani Sridharan 				    available_fmt->audio_fmt_num);
2142cabd02bSRanjani Sridharan 	if (ret) {
2152cabd02bSRanjani Sridharan 		dev_err(scomp->dev, "parse buffer size tokens failed %d\n", ret);
2162cabd02bSRanjani Sridharan 		goto err_in;
2172cabd02bSRanjani Sridharan 	}
2182cabd02bSRanjani Sridharan 
2192cabd02bSRanjani Sridharan 	for (i = 0; i < available_fmt->audio_fmt_num; i++)
2202cabd02bSRanjani Sridharan 		dev_dbg(scomp->dev, "%d: ibs: %d obs: %d cpc: %d is_pages: %d\n", i,
2212cabd02bSRanjani Sridharan 			base_config[i].ibs, base_config[i].obs,
2222cabd02bSRanjani Sridharan 			base_config[i].cpc, base_config[i].is_pages);
2232cabd02bSRanjani Sridharan 
2242cabd02bSRanjani Sridharan 	ret = sof_update_ipc_object(scomp, &base_config->audio_fmt,
2252cabd02bSRanjani Sridharan 				    SOF_IN_AUDIO_FORMAT_TOKENS, swidget->tuples,
2262cabd02bSRanjani Sridharan 				    swidget->num_tuples, sizeof(*base_config),
2272cabd02bSRanjani Sridharan 				    available_fmt->audio_fmt_num);
2282cabd02bSRanjani Sridharan 	if (ret) {
2292cabd02bSRanjani Sridharan 		dev_err(scomp->dev, "parse base_config audio_fmt tokens failed %d\n", ret);
2302cabd02bSRanjani Sridharan 		goto err_in;
2312cabd02bSRanjani Sridharan 	}
2322cabd02bSRanjani Sridharan 
2332cabd02bSRanjani Sridharan 	dev_dbg(scomp->dev, "Get input audio formats for %s\n", swidget->widget->name);
2342cabd02bSRanjani Sridharan 	sof_ipc4_dbg_audio_format(scomp->dev, &base_config->audio_fmt,
2352cabd02bSRanjani Sridharan 				  sizeof(*base_config),
2362cabd02bSRanjani Sridharan 				  available_fmt->audio_fmt_num);
2372cabd02bSRanjani Sridharan 
2382cabd02bSRanjani Sridharan 	available_fmt->base_config = base_config;
2392cabd02bSRanjani Sridharan 
2402cabd02bSRanjani Sridharan 	if (!has_out_format)
2412cabd02bSRanjani Sridharan 		return 0;
2422cabd02bSRanjani Sridharan 
2432cabd02bSRanjani Sridharan 	out_format = kcalloc(available_fmt->audio_fmt_num, sizeof(*out_format), GFP_KERNEL);
2442cabd02bSRanjani Sridharan 	if (!out_format) {
2452cabd02bSRanjani Sridharan 		ret = -ENOMEM;
2462cabd02bSRanjani Sridharan 		goto err_in;
2472cabd02bSRanjani Sridharan 	}
2482cabd02bSRanjani Sridharan 
2492cabd02bSRanjani Sridharan 	ret = sof_update_ipc_object(scomp, out_format,
2502cabd02bSRanjani Sridharan 				    SOF_OUT_AUDIO_FORMAT_TOKENS, swidget->tuples,
2512cabd02bSRanjani Sridharan 				    swidget->num_tuples, sizeof(*out_format),
2522cabd02bSRanjani Sridharan 				    available_fmt->audio_fmt_num);
2532cabd02bSRanjani Sridharan 
2542cabd02bSRanjani Sridharan 	if (ret) {
2552cabd02bSRanjani Sridharan 		dev_err(scomp->dev, "parse output audio_fmt tokens failed\n");
2562cabd02bSRanjani Sridharan 		goto err_out;
2572cabd02bSRanjani Sridharan 	}
2582cabd02bSRanjani Sridharan 
2592cabd02bSRanjani Sridharan 	available_fmt->out_audio_fmt = out_format;
2602cabd02bSRanjani Sridharan 	dev_dbg(scomp->dev, "Get output audio formats for %s\n", swidget->widget->name);
2612cabd02bSRanjani Sridharan 	sof_ipc4_dbg_audio_format(scomp->dev, out_format, sizeof(*out_format),
2622cabd02bSRanjani Sridharan 				  available_fmt->audio_fmt_num);
2632cabd02bSRanjani Sridharan 
2642cabd02bSRanjani Sridharan 	return 0;
2652cabd02bSRanjani Sridharan 
2662cabd02bSRanjani Sridharan err_out:
2672cabd02bSRanjani Sridharan 	kfree(out_format);
2682cabd02bSRanjani Sridharan err_in:
2692cabd02bSRanjani Sridharan 	kfree(base_config);
2702cabd02bSRanjani Sridharan 
2712cabd02bSRanjani Sridharan 	return ret;
2722cabd02bSRanjani Sridharan }
2732cabd02bSRanjani Sridharan 
274dc4fc0aeSLibin Yang /* release the memory allocated in sof_ipc4_get_audio_fmt */
275dc4fc0aeSLibin Yang static void sof_ipc4_free_audio_fmt(struct sof_ipc4_available_audio_format *available_fmt)
276dc4fc0aeSLibin Yang 
277dc4fc0aeSLibin Yang {
278dc4fc0aeSLibin Yang 	kfree(available_fmt->base_config);
279dc4fc0aeSLibin Yang 	available_fmt->base_config = NULL;
280dc4fc0aeSLibin Yang 	kfree(available_fmt->out_audio_fmt);
281dc4fc0aeSLibin Yang 	available_fmt->out_audio_fmt = NULL;
282dc4fc0aeSLibin Yang }
283dc4fc0aeSLibin Yang 
284a29b2d02SBard Liao static void sof_ipc4_widget_free_comp_pipeline(struct snd_sof_widget *swidget)
28590e89155SRanjani Sridharan {
28690e89155SRanjani Sridharan 	kfree(swidget->private);
28790e89155SRanjani Sridharan }
28890e89155SRanjani Sridharan 
2892cabd02bSRanjani Sridharan static int sof_ipc4_widget_set_module_info(struct snd_sof_widget *swidget)
2902cabd02bSRanjani Sridharan {
2912cabd02bSRanjani Sridharan 	struct snd_soc_component *scomp = swidget->scomp;
2922cabd02bSRanjani Sridharan 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
2932cabd02bSRanjani Sridharan 
294c73f8b47SPeter Ujfalusi 	swidget->module_info = sof_ipc4_find_module_by_uuid(sdev, &swidget->uuid);
2955a932cfcSPeter Ujfalusi 
296c73f8b47SPeter Ujfalusi 	if (swidget->module_info)
2972cabd02bSRanjani Sridharan 		return 0;
2982cabd02bSRanjani Sridharan 
2992cabd02bSRanjani Sridharan 	dev_err(sdev->dev, "failed to find module info for widget %s with UUID %pUL\n",
3002cabd02bSRanjani Sridharan 		swidget->widget->name, &swidget->uuid);
3012cabd02bSRanjani Sridharan 	return -EINVAL;
3022cabd02bSRanjani Sridharan }
3032cabd02bSRanjani Sridharan 
3042cabd02bSRanjani Sridharan static int sof_ipc4_widget_setup_msg(struct snd_sof_widget *swidget, struct sof_ipc4_msg *msg)
3052cabd02bSRanjani Sridharan {
3062cabd02bSRanjani Sridharan 	struct sof_ipc4_fw_module *fw_module;
307dc6137a5SRander Wang 	uint32_t type;
3082cabd02bSRanjani Sridharan 	int ret;
3092cabd02bSRanjani Sridharan 
3102cabd02bSRanjani Sridharan 	ret = sof_ipc4_widget_set_module_info(swidget);
3112cabd02bSRanjani Sridharan 	if (ret)
3122cabd02bSRanjani Sridharan 		return ret;
3132cabd02bSRanjani Sridharan 
3142cabd02bSRanjani Sridharan 	fw_module = swidget->module_info;
3152cabd02bSRanjani Sridharan 
3162cabd02bSRanjani Sridharan 	msg->primary = fw_module->man4_module_entry.id;
3172cabd02bSRanjani Sridharan 	msg->primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_INIT_INSTANCE);
3182cabd02bSRanjani Sridharan 	msg->primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
3192cabd02bSRanjani Sridharan 	msg->primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
3202cabd02bSRanjani Sridharan 
321a2ba1f70SBard Liao 	msg->extension = SOF_IPC4_MOD_EXT_CORE_ID(swidget->core);
3222cabd02bSRanjani Sridharan 
32380d53171SPierre-Louis Bossart 	type = (fw_module->man4_module_entry.type & SOF_IPC4_MODULE_DP) ? 1 : 0;
324dc6137a5SRander Wang 	msg->extension |= SOF_IPC4_MOD_EXT_DOMAIN(type);
325dc6137a5SRander Wang 
3262cabd02bSRanjani Sridharan 	return 0;
3272cabd02bSRanjani Sridharan }
3282cabd02bSRanjani Sridharan 
3292cabd02bSRanjani Sridharan static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget)
3302cabd02bSRanjani Sridharan {
3312cabd02bSRanjani Sridharan 	struct sof_ipc4_available_audio_format *available_fmt;
3322cabd02bSRanjani Sridharan 	struct snd_soc_component *scomp = swidget->scomp;
3332cabd02bSRanjani Sridharan 	struct sof_ipc4_copier *ipc4_copier;
3342cabd02bSRanjani Sridharan 	int node_type = 0;
3352cabd02bSRanjani Sridharan 	int ret, i;
3362cabd02bSRanjani Sridharan 
3372cabd02bSRanjani Sridharan 	ipc4_copier = kzalloc(sizeof(*ipc4_copier), GFP_KERNEL);
3382cabd02bSRanjani Sridharan 	if (!ipc4_copier)
3392cabd02bSRanjani Sridharan 		return -ENOMEM;
3402cabd02bSRanjani Sridharan 
3412cabd02bSRanjani Sridharan 	swidget->private = ipc4_copier;
3422cabd02bSRanjani Sridharan 	available_fmt = &ipc4_copier->available_fmt;
3432cabd02bSRanjani Sridharan 
3442cabd02bSRanjani Sridharan 	dev_dbg(scomp->dev, "Updating IPC structure for %s\n", swidget->widget->name);
3452cabd02bSRanjani Sridharan 
3462cabd02bSRanjani Sridharan 	ret = sof_ipc4_get_audio_fmt(scomp, swidget, available_fmt, true);
3472cabd02bSRanjani Sridharan 	if (ret)
3482cabd02bSRanjani Sridharan 		goto free_copier;
3492cabd02bSRanjani Sridharan 
3502cabd02bSRanjani Sridharan 	available_fmt->dma_buffer_size = kcalloc(available_fmt->audio_fmt_num, sizeof(u32),
3512cabd02bSRanjani Sridharan 						 GFP_KERNEL);
3522cabd02bSRanjani Sridharan 	if (!available_fmt->dma_buffer_size) {
3532cabd02bSRanjani Sridharan 		ret = -ENOMEM;
354dc4fc0aeSLibin Yang 		goto free_available_fmt;
3552cabd02bSRanjani Sridharan 	}
3562cabd02bSRanjani Sridharan 
3577d573425SBard Liao 	/*
3587d573425SBard Liao 	 * This callback is used by host copier and module-to-module copier,
3597d573425SBard Liao 	 * and only host copier needs to set gtw_cfg.
3607d573425SBard Liao 	 */
3617d573425SBard Liao 	if (!WIDGET_IS_AIF(swidget->id))
3627d573425SBard Liao 		goto skip_gtw_cfg;
3637d573425SBard Liao 
3642cabd02bSRanjani Sridharan 	ret = sof_update_ipc_object(scomp, available_fmt->dma_buffer_size,
3652cabd02bSRanjani Sridharan 				    SOF_COPIER_GATEWAY_CFG_TOKENS, swidget->tuples,
3662cabd02bSRanjani Sridharan 				    swidget->num_tuples, sizeof(u32),
3672cabd02bSRanjani Sridharan 				    available_fmt->audio_fmt_num);
3682cabd02bSRanjani Sridharan 	if (ret) {
3692cabd02bSRanjani Sridharan 		dev_err(scomp->dev, "Failed to parse dma buffer size in audio format for %s\n",
3702cabd02bSRanjani Sridharan 			swidget->widget->name);
3712cabd02bSRanjani Sridharan 		goto err;
3722cabd02bSRanjani Sridharan 	}
3732cabd02bSRanjani Sridharan 
3742cabd02bSRanjani Sridharan 	dev_dbg(scomp->dev, "dma buffer size:\n");
3752cabd02bSRanjani Sridharan 	for (i = 0; i < available_fmt->audio_fmt_num; i++)
3762cabd02bSRanjani Sridharan 		dev_dbg(scomp->dev, "%d: %u\n", i,
3772cabd02bSRanjani Sridharan 			available_fmt->dma_buffer_size[i]);
3782cabd02bSRanjani Sridharan 
3792cabd02bSRanjani Sridharan 	ret = sof_update_ipc_object(scomp, &node_type,
3802cabd02bSRanjani Sridharan 				    SOF_COPIER_TOKENS, swidget->tuples,
3812cabd02bSRanjani Sridharan 				    swidget->num_tuples, sizeof(node_type), 1);
3822cabd02bSRanjani Sridharan 
3832cabd02bSRanjani Sridharan 	if (ret) {
3842cabd02bSRanjani Sridharan 		dev_err(scomp->dev, "parse host copier node type token failed %d\n",
3852cabd02bSRanjani Sridharan 			ret);
3862cabd02bSRanjani Sridharan 		goto err;
3872cabd02bSRanjani Sridharan 	}
3882cabd02bSRanjani Sridharan 	dev_dbg(scomp->dev, "host copier '%s' node_type %u\n", swidget->widget->name, node_type);
3892cabd02bSRanjani Sridharan 
3907d573425SBard Liao skip_gtw_cfg:
3912cabd02bSRanjani Sridharan 	ipc4_copier->gtw_attr = kzalloc(sizeof(*ipc4_copier->gtw_attr), GFP_KERNEL);
3922cabd02bSRanjani Sridharan 	if (!ipc4_copier->gtw_attr) {
3932cabd02bSRanjani Sridharan 		ret = -ENOMEM;
3942cabd02bSRanjani Sridharan 		goto err;
3952cabd02bSRanjani Sridharan 	}
3962cabd02bSRanjani Sridharan 
3972cabd02bSRanjani Sridharan 	ipc4_copier->copier_config = (uint32_t *)ipc4_copier->gtw_attr;
3982cabd02bSRanjani Sridharan 	ipc4_copier->data.gtw_cfg.config_length =
3992cabd02bSRanjani Sridharan 		sizeof(struct sof_ipc4_gtw_attributes) >> 2;
4002cabd02bSRanjani Sridharan 
4017d573425SBard Liao 	switch (swidget->id) {
4027d573425SBard Liao 	case snd_soc_dapm_aif_in:
4037d573425SBard Liao 	case snd_soc_dapm_aif_out:
4047d573425SBard Liao 		ipc4_copier->data.gtw_cfg.node_id = SOF_IPC4_NODE_TYPE(node_type);
4057d573425SBard Liao 		break;
4067d573425SBard Liao 	case snd_soc_dapm_buffer:
4077d573425SBard Liao 		ipc4_copier->data.gtw_cfg.node_id = SOF_IPC4_INVALID_NODE_ID;
4087d573425SBard Liao 		ipc4_copier->ipc_config_size = 0;
4097d573425SBard Liao 		break;
4107d573425SBard Liao 	default:
4117d573425SBard Liao 		dev_err(scomp->dev, "invalid widget type %d\n", swidget->id);
4127d573425SBard Liao 		ret = -EINVAL;
4137d573425SBard Liao 		goto free_gtw_attr;
4147d573425SBard Liao 	}
4157d573425SBard Liao 
4162cabd02bSRanjani Sridharan 	/* set up module info and message header */
4172cabd02bSRanjani Sridharan 	ret = sof_ipc4_widget_setup_msg(swidget, &ipc4_copier->msg);
4182cabd02bSRanjani Sridharan 	if (ret)
4192cabd02bSRanjani Sridharan 		goto free_gtw_attr;
4202cabd02bSRanjani Sridharan 
4212cabd02bSRanjani Sridharan 	return 0;
4222cabd02bSRanjani Sridharan 
4232cabd02bSRanjani Sridharan free_gtw_attr:
4242cabd02bSRanjani Sridharan 	kfree(ipc4_copier->gtw_attr);
4252cabd02bSRanjani Sridharan err:
4262cabd02bSRanjani Sridharan 	kfree(available_fmt->dma_buffer_size);
427dc4fc0aeSLibin Yang free_available_fmt:
428dc4fc0aeSLibin Yang 	sof_ipc4_free_audio_fmt(available_fmt);
4292cabd02bSRanjani Sridharan free_copier:
4302cabd02bSRanjani Sridharan 	kfree(ipc4_copier);
431b737fd8cSLibin Yang 	swidget->private = NULL;
4322cabd02bSRanjani Sridharan 	return ret;
4332cabd02bSRanjani Sridharan }
4342cabd02bSRanjani Sridharan 
4352cabd02bSRanjani Sridharan static void sof_ipc4_widget_free_comp_pcm(struct snd_sof_widget *swidget)
4362cabd02bSRanjani Sridharan {
4372cabd02bSRanjani Sridharan 	struct sof_ipc4_copier *ipc4_copier = swidget->private;
4382cabd02bSRanjani Sridharan 	struct sof_ipc4_available_audio_format *available_fmt;
4392cabd02bSRanjani Sridharan 
4402cabd02bSRanjani Sridharan 	if (!ipc4_copier)
4412cabd02bSRanjani Sridharan 		return;
4422cabd02bSRanjani Sridharan 
4432cabd02bSRanjani Sridharan 	available_fmt = &ipc4_copier->available_fmt;
4442cabd02bSRanjani Sridharan 	kfree(available_fmt->dma_buffer_size);
4452cabd02bSRanjani Sridharan 	kfree(available_fmt->base_config);
4462cabd02bSRanjani Sridharan 	kfree(available_fmt->out_audio_fmt);
4472cabd02bSRanjani Sridharan 	kfree(ipc4_copier->gtw_attr);
4482cabd02bSRanjani Sridharan 	kfree(ipc4_copier);
4492cabd02bSRanjani Sridharan 	swidget->private = NULL;
4502cabd02bSRanjani Sridharan }
4512cabd02bSRanjani Sridharan 
452abfb536bSRanjani Sridharan static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget)
453abfb536bSRanjani Sridharan {
454abfb536bSRanjani Sridharan 	struct sof_ipc4_available_audio_format *available_fmt;
455abfb536bSRanjani Sridharan 	struct snd_soc_component *scomp = swidget->scomp;
456abfb536bSRanjani Sridharan 	struct snd_sof_dai *dai = swidget->private;
457abfb536bSRanjani Sridharan 	struct sof_ipc4_copier *ipc4_copier;
458abfb536bSRanjani Sridharan 	int node_type = 0;
459abfb536bSRanjani Sridharan 	int ret, i;
460abfb536bSRanjani Sridharan 
461abfb536bSRanjani Sridharan 	ipc4_copier = kzalloc(sizeof(*ipc4_copier), GFP_KERNEL);
462abfb536bSRanjani Sridharan 	if (!ipc4_copier)
463abfb536bSRanjani Sridharan 		return -ENOMEM;
464abfb536bSRanjani Sridharan 
465abfb536bSRanjani Sridharan 	available_fmt = &ipc4_copier->available_fmt;
466abfb536bSRanjani Sridharan 
467abfb536bSRanjani Sridharan 	dev_dbg(scomp->dev, "Updating IPC structure for %s\n", swidget->widget->name);
468abfb536bSRanjani Sridharan 
469abfb536bSRanjani Sridharan 	ret = sof_ipc4_get_audio_fmt(scomp, swidget, available_fmt, true);
470abfb536bSRanjani Sridharan 	if (ret)
471abfb536bSRanjani Sridharan 		goto free_copier;
472abfb536bSRanjani Sridharan 
473abfb536bSRanjani Sridharan 	available_fmt->dma_buffer_size = kcalloc(available_fmt->audio_fmt_num, sizeof(u32),
474abfb536bSRanjani Sridharan 						 GFP_KERNEL);
475abfb536bSRanjani Sridharan 	if (!available_fmt->dma_buffer_size) {
476abfb536bSRanjani Sridharan 		ret = -ENOMEM;
477dc4fc0aeSLibin Yang 		goto free_available_fmt;
478abfb536bSRanjani Sridharan 	}
479abfb536bSRanjani Sridharan 
480abfb536bSRanjani Sridharan 	ret = sof_update_ipc_object(scomp, available_fmt->dma_buffer_size,
481abfb536bSRanjani Sridharan 				    SOF_COPIER_GATEWAY_CFG_TOKENS, swidget->tuples,
482abfb536bSRanjani Sridharan 				    swidget->num_tuples, sizeof(u32),
483abfb536bSRanjani Sridharan 				    available_fmt->audio_fmt_num);
484abfb536bSRanjani Sridharan 	if (ret) {
485abfb536bSRanjani Sridharan 		dev_err(scomp->dev, "Failed to parse dma buffer size in audio format for %s\n",
486abfb536bSRanjani Sridharan 			swidget->widget->name);
487abfb536bSRanjani Sridharan 		goto err;
488abfb536bSRanjani Sridharan 	}
489abfb536bSRanjani Sridharan 
490abfb536bSRanjani Sridharan 	for (i = 0; i < available_fmt->audio_fmt_num; i++)
491abfb536bSRanjani Sridharan 		dev_dbg(scomp->dev, "%d: dma buffer size: %u\n", i,
492abfb536bSRanjani Sridharan 			available_fmt->dma_buffer_size[i]);
493abfb536bSRanjani Sridharan 
494abfb536bSRanjani Sridharan 	ret = sof_update_ipc_object(scomp, &node_type,
495abfb536bSRanjani Sridharan 				    SOF_COPIER_TOKENS, swidget->tuples,
496abfb536bSRanjani Sridharan 				    swidget->num_tuples, sizeof(node_type), 1);
497abfb536bSRanjani Sridharan 	if (ret) {
498abfb536bSRanjani Sridharan 		dev_err(scomp->dev, "parse dai node type failed %d\n", ret);
499abfb536bSRanjani Sridharan 		goto err;
500abfb536bSRanjani Sridharan 	}
501abfb536bSRanjani Sridharan 
502abfb536bSRanjani Sridharan 	ret = sof_update_ipc_object(scomp, ipc4_copier,
503abfb536bSRanjani Sridharan 				    SOF_DAI_TOKENS, swidget->tuples,
504abfb536bSRanjani Sridharan 				    swidget->num_tuples, sizeof(u32), 1);
505abfb536bSRanjani Sridharan 	if (ret) {
506abfb536bSRanjani Sridharan 		dev_err(scomp->dev, "parse dai copier node token failed %d\n", ret);
507abfb536bSRanjani Sridharan 		goto err;
508abfb536bSRanjani Sridharan 	}
509abfb536bSRanjani Sridharan 
510abfb536bSRanjani Sridharan 	dev_dbg(scomp->dev, "dai %s node_type %u dai_type %u dai_index %d\n", swidget->widget->name,
511abfb536bSRanjani Sridharan 		node_type, ipc4_copier->dai_type, ipc4_copier->dai_index);
512abfb536bSRanjani Sridharan 
513abfb536bSRanjani Sridharan 	ipc4_copier->data.gtw_cfg.node_id = SOF_IPC4_NODE_TYPE(node_type);
514aa84ffb7SRanjani Sridharan 
515aa84ffb7SRanjani Sridharan 	switch (ipc4_copier->dai_type) {
516a45a4d43SBard Liao 	case SOF_DAI_INTEL_ALH:
517a45a4d43SBard Liao 	{
518a150345aSBard Liao 		struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
519a45a4d43SBard Liao 		struct sof_ipc4_alh_configuration_blob *blob;
520a150345aSBard Liao 		struct snd_sof_widget *w;
521a45a4d43SBard Liao 
522a45a4d43SBard Liao 		blob = kzalloc(sizeof(*blob), GFP_KERNEL);
523a45a4d43SBard Liao 		if (!blob) {
524a45a4d43SBard Liao 			ret = -ENOMEM;
525a45a4d43SBard Liao 			goto err;
526a45a4d43SBard Liao 		}
527a45a4d43SBard Liao 
528a150345aSBard Liao 		list_for_each_entry(w, &sdev->widget_list, list) {
529a150345aSBard Liao 			if (w->widget->sname &&
530a150345aSBard Liao 			    strcmp(w->widget->sname, swidget->widget->sname))
531a150345aSBard Liao 				continue;
532a150345aSBard Liao 
533a150345aSBard Liao 			blob->alh_cfg.count++;
534a150345aSBard Liao 		}
535a150345aSBard Liao 
536a45a4d43SBard Liao 		ipc4_copier->copier_config = (uint32_t *)blob;
537a45a4d43SBard Liao 		ipc4_copier->data.gtw_cfg.config_length = sizeof(*blob) >> 2;
538a45a4d43SBard Liao 		break;
539a45a4d43SBard Liao 	}
540aa84ffb7SRanjani Sridharan 	case SOF_DAI_INTEL_SSP:
541aa84ffb7SRanjani Sridharan 		/* set SSP DAI index as the node_id */
542aa84ffb7SRanjani Sridharan 		ipc4_copier->data.gtw_cfg.node_id |=
543aa84ffb7SRanjani Sridharan 			SOF_IPC4_NODE_INDEX_INTEL_SSP(ipc4_copier->dai_index);
544aa84ffb7SRanjani Sridharan 		break;
545aa84ffb7SRanjani Sridharan 	case SOF_DAI_INTEL_DMIC:
546aa84ffb7SRanjani Sridharan 		/* set DMIC DAI index as the node_id */
547aa84ffb7SRanjani Sridharan 		ipc4_copier->data.gtw_cfg.node_id |=
548aa84ffb7SRanjani Sridharan 			SOF_IPC4_NODE_INDEX_INTEL_DMIC(ipc4_copier->dai_index);
549aa84ffb7SRanjani Sridharan 		break;
550aa84ffb7SRanjani Sridharan 	default:
551abfb536bSRanjani Sridharan 		ipc4_copier->gtw_attr = kzalloc(sizeof(*ipc4_copier->gtw_attr), GFP_KERNEL);
552abfb536bSRanjani Sridharan 		if (!ipc4_copier->gtw_attr) {
553abfb536bSRanjani Sridharan 			ret = -ENOMEM;
554abfb536bSRanjani Sridharan 			goto err;
555abfb536bSRanjani Sridharan 		}
556abfb536bSRanjani Sridharan 
557abfb536bSRanjani Sridharan 		ipc4_copier->copier_config = (uint32_t *)ipc4_copier->gtw_attr;
558aa84ffb7SRanjani Sridharan 		ipc4_copier->data.gtw_cfg.config_length =
559aa84ffb7SRanjani Sridharan 			sizeof(struct sof_ipc4_gtw_attributes) >> 2;
560aa84ffb7SRanjani Sridharan 		break;
561aa84ffb7SRanjani Sridharan 	}
562abfb536bSRanjani Sridharan 
563abfb536bSRanjani Sridharan 	dai->scomp = scomp;
564abfb536bSRanjani Sridharan 	dai->private = ipc4_copier;
565abfb536bSRanjani Sridharan 
566abfb536bSRanjani Sridharan 	/* set up module info and message header */
567abfb536bSRanjani Sridharan 	ret = sof_ipc4_widget_setup_msg(swidget, &ipc4_copier->msg);
568abfb536bSRanjani Sridharan 	if (ret)
569abfb536bSRanjani Sridharan 		goto free_copier_config;
570abfb536bSRanjani Sridharan 
571abfb536bSRanjani Sridharan 	return 0;
572abfb536bSRanjani Sridharan 
573abfb536bSRanjani Sridharan free_copier_config:
574abfb536bSRanjani Sridharan 	kfree(ipc4_copier->copier_config);
575abfb536bSRanjani Sridharan err:
576abfb536bSRanjani Sridharan 	kfree(available_fmt->dma_buffer_size);
577dc4fc0aeSLibin Yang free_available_fmt:
578dc4fc0aeSLibin Yang 	sof_ipc4_free_audio_fmt(available_fmt);
579abfb536bSRanjani Sridharan free_copier:
580abfb536bSRanjani Sridharan 	kfree(ipc4_copier);
581b737fd8cSLibin Yang 	dai->private = NULL;
582b737fd8cSLibin Yang 	dai->scomp = NULL;
583abfb536bSRanjani Sridharan 	return ret;
584abfb536bSRanjani Sridharan }
585abfb536bSRanjani Sridharan 
586abfb536bSRanjani Sridharan static void sof_ipc4_widget_free_comp_dai(struct snd_sof_widget *swidget)
587abfb536bSRanjani Sridharan {
588abfb536bSRanjani Sridharan 	struct sof_ipc4_available_audio_format *available_fmt;
589abfb536bSRanjani Sridharan 	struct snd_sof_dai *dai = swidget->private;
590abfb536bSRanjani Sridharan 	struct sof_ipc4_copier *ipc4_copier;
591abfb536bSRanjani Sridharan 
592abfb536bSRanjani Sridharan 	if (!dai)
593abfb536bSRanjani Sridharan 		return;
594abfb536bSRanjani Sridharan 
595b737fd8cSLibin Yang 	if (!dai->private) {
596b737fd8cSLibin Yang 		kfree(dai);
597b737fd8cSLibin Yang 		swidget->private = NULL;
598b737fd8cSLibin Yang 		return;
599b737fd8cSLibin Yang 	}
600b737fd8cSLibin Yang 
601abfb536bSRanjani Sridharan 	ipc4_copier = dai->private;
602abfb536bSRanjani Sridharan 	available_fmt = &ipc4_copier->available_fmt;
603abfb536bSRanjani Sridharan 
604abfb536bSRanjani Sridharan 	kfree(available_fmt->dma_buffer_size);
605abfb536bSRanjani Sridharan 	kfree(available_fmt->base_config);
606abfb536bSRanjani Sridharan 	kfree(available_fmt->out_audio_fmt);
607aa84ffb7SRanjani Sridharan 	if (ipc4_copier->dai_type != SOF_DAI_INTEL_SSP &&
608aa84ffb7SRanjani Sridharan 	    ipc4_copier->dai_type != SOF_DAI_INTEL_DMIC)
609abfb536bSRanjani Sridharan 		kfree(ipc4_copier->copier_config);
610abfb536bSRanjani Sridharan 	kfree(dai->private);
611abfb536bSRanjani Sridharan 	kfree(dai);
612abfb536bSRanjani Sridharan 	swidget->private = NULL;
613abfb536bSRanjani Sridharan }
614abfb536bSRanjani Sridharan 
61590e89155SRanjani Sridharan static int sof_ipc4_widget_setup_comp_pipeline(struct snd_sof_widget *swidget)
61690e89155SRanjani Sridharan {
61790e89155SRanjani Sridharan 	struct snd_soc_component *scomp = swidget->scomp;
61890e89155SRanjani Sridharan 	struct sof_ipc4_pipeline *pipeline;
61990e89155SRanjani Sridharan 	int ret;
62090e89155SRanjani Sridharan 
62190e89155SRanjani Sridharan 	pipeline = kzalloc(sizeof(*pipeline), GFP_KERNEL);
62290e89155SRanjani Sridharan 	if (!pipeline)
62390e89155SRanjani Sridharan 		return -ENOMEM;
62490e89155SRanjani Sridharan 
62590e89155SRanjani Sridharan 	ret = sof_update_ipc_object(scomp, pipeline, SOF_SCHED_TOKENS, swidget->tuples,
62690e89155SRanjani Sridharan 				    swidget->num_tuples, sizeof(*pipeline), 1);
62790e89155SRanjani Sridharan 	if (ret) {
62890e89155SRanjani Sridharan 		dev_err(scomp->dev, "parsing scheduler tokens failed\n");
62990e89155SRanjani Sridharan 		goto err;
63090e89155SRanjani Sridharan 	}
63190e89155SRanjani Sridharan 
63290e89155SRanjani Sridharan 	/* parse one set of pipeline tokens */
63390e89155SRanjani Sridharan 	ret = sof_update_ipc_object(scomp, swidget, SOF_PIPELINE_TOKENS, swidget->tuples,
63490e89155SRanjani Sridharan 				    swidget->num_tuples, sizeof(*swidget), 1);
63590e89155SRanjani Sridharan 	if (ret) {
63690e89155SRanjani Sridharan 		dev_err(scomp->dev, "parsing pipeline tokens failed\n");
63790e89155SRanjani Sridharan 		goto err;
63890e89155SRanjani Sridharan 	}
63990e89155SRanjani Sridharan 
64090e89155SRanjani Sridharan 	/* TODO: Get priority from topology */
64190e89155SRanjani Sridharan 	pipeline->priority = 0;
64290e89155SRanjani Sridharan 
64390e89155SRanjani Sridharan 	dev_dbg(scomp->dev, "pipeline '%s': id %d pri %d lp mode %d\n",
64490e89155SRanjani Sridharan 		swidget->widget->name, swidget->pipeline_id,
64590e89155SRanjani Sridharan 		pipeline->priority, pipeline->lp_mode);
64690e89155SRanjani Sridharan 
64790e89155SRanjani Sridharan 	swidget->private = pipeline;
64890e89155SRanjani Sridharan 
64990e89155SRanjani Sridharan 	pipeline->msg.primary = SOF_IPC4_GLB_PIPE_PRIORITY(pipeline->priority);
65090e89155SRanjani Sridharan 	pipeline->msg.primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_CREATE_PIPELINE);
65190e89155SRanjani Sridharan 	pipeline->msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
65290e89155SRanjani Sridharan 	pipeline->msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG);
65390e89155SRanjani Sridharan 
65490e89155SRanjani Sridharan 	pipeline->msg.extension = pipeline->lp_mode;
65590e89155SRanjani Sridharan 	pipeline->state = SOF_IPC4_PIPE_UNINITIALIZED;
65690e89155SRanjani Sridharan 
65790e89155SRanjani Sridharan 	return 0;
65890e89155SRanjani Sridharan err:
65990e89155SRanjani Sridharan 	kfree(pipeline);
66090e89155SRanjani Sridharan 	return ret;
66190e89155SRanjani Sridharan }
66290e89155SRanjani Sridharan 
6634f838ab2SRanjani Sridharan static int sof_ipc4_widget_setup_comp_pga(struct snd_sof_widget *swidget)
6644f838ab2SRanjani Sridharan {
6654f838ab2SRanjani Sridharan 	struct snd_soc_component *scomp = swidget->scomp;
6664f838ab2SRanjani Sridharan 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
6674f838ab2SRanjani Sridharan 	struct sof_ipc4_fw_module *fw_module;
6684f838ab2SRanjani Sridharan 	struct snd_sof_control *scontrol;
6694f838ab2SRanjani Sridharan 	struct sof_ipc4_gain *gain;
6704f838ab2SRanjani Sridharan 	int ret;
6714f838ab2SRanjani Sridharan 
6724f838ab2SRanjani Sridharan 	gain = kzalloc(sizeof(*gain), GFP_KERNEL);
6734f838ab2SRanjani Sridharan 	if (!gain)
6744f838ab2SRanjani Sridharan 		return -ENOMEM;
6754f838ab2SRanjani Sridharan 
6764f838ab2SRanjani Sridharan 	swidget->private = gain;
6774f838ab2SRanjani Sridharan 
6784f838ab2SRanjani Sridharan 	gain->data.channels = SOF_IPC4_GAIN_ALL_CHANNELS_MASK;
6794f838ab2SRanjani Sridharan 	gain->data.init_val = SOF_IPC4_VOL_ZERO_DB;
6804f838ab2SRanjani Sridharan 
6814f838ab2SRanjani Sridharan 	/* The out_audio_fmt in topology is ignored as it is not required to be sent to the FW */
6824f838ab2SRanjani Sridharan 	ret = sof_ipc4_get_audio_fmt(scomp, swidget, &gain->available_fmt, false);
6834f838ab2SRanjani Sridharan 	if (ret)
6844f838ab2SRanjani Sridharan 		goto err;
6854f838ab2SRanjani Sridharan 
6864f838ab2SRanjani Sridharan 	ret = sof_update_ipc_object(scomp, &gain->data, SOF_GAIN_TOKENS, swidget->tuples,
6874f838ab2SRanjani Sridharan 				    swidget->num_tuples, sizeof(gain->data), 1);
6884f838ab2SRanjani Sridharan 	if (ret) {
6894f838ab2SRanjani Sridharan 		dev_err(scomp->dev, "Parsing gain tokens failed\n");
6904f838ab2SRanjani Sridharan 		goto err;
6914f838ab2SRanjani Sridharan 	}
6924f838ab2SRanjani Sridharan 
6934f838ab2SRanjani Sridharan 	dev_dbg(scomp->dev,
6944f838ab2SRanjani Sridharan 		"pga widget %s: ramp type: %d, ramp duration %d, initial gain value: %#x, cpc %d\n",
6954f838ab2SRanjani Sridharan 		swidget->widget->name, gain->data.curve_type, gain->data.curve_duration,
6964f838ab2SRanjani Sridharan 		gain->data.init_val, gain->base_config.cpc);
6974f838ab2SRanjani Sridharan 
6984f838ab2SRanjani Sridharan 	ret = sof_ipc4_widget_setup_msg(swidget, &gain->msg);
6994f838ab2SRanjani Sridharan 	if (ret)
7004f838ab2SRanjani Sridharan 		goto err;
7014f838ab2SRanjani Sridharan 
7024f838ab2SRanjani Sridharan 	fw_module = swidget->module_info;
7034f838ab2SRanjani Sridharan 
7044f838ab2SRanjani Sridharan 	/* update module ID for all kcontrols for this widget */
7054f838ab2SRanjani Sridharan 	list_for_each_entry(scontrol, &sdev->kcontrol_list, list)
7064f838ab2SRanjani Sridharan 		if (scontrol->comp_id == swidget->comp_id) {
7074f838ab2SRanjani Sridharan 			struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
7084f838ab2SRanjani Sridharan 			struct sof_ipc4_msg *msg = &cdata->msg;
7094f838ab2SRanjani Sridharan 
7104f838ab2SRanjani Sridharan 			msg->primary |= fw_module->man4_module_entry.id;
7114f838ab2SRanjani Sridharan 		}
7124f838ab2SRanjani Sridharan 
7134f838ab2SRanjani Sridharan 	return 0;
7144f838ab2SRanjani Sridharan err:
715dc4fc0aeSLibin Yang 	sof_ipc4_free_audio_fmt(&gain->available_fmt);
7164f838ab2SRanjani Sridharan 	kfree(gain);
717b737fd8cSLibin Yang 	swidget->private = NULL;
7184f838ab2SRanjani Sridharan 	return ret;
7194f838ab2SRanjani Sridharan }
7204f838ab2SRanjani Sridharan 
721dc4fc0aeSLibin Yang static void sof_ipc4_widget_free_comp_pga(struct snd_sof_widget *swidget)
722dc4fc0aeSLibin Yang {
723dc4fc0aeSLibin Yang 	struct sof_ipc4_gain *gain = swidget->private;
724dc4fc0aeSLibin Yang 
725dc4fc0aeSLibin Yang 	if (!gain)
726dc4fc0aeSLibin Yang 		return;
727dc4fc0aeSLibin Yang 
728dc4fc0aeSLibin Yang 	sof_ipc4_free_audio_fmt(&gain->available_fmt);
729dc4fc0aeSLibin Yang 	kfree(swidget->private);
730dc4fc0aeSLibin Yang 	swidget->private = NULL;
731dc4fc0aeSLibin Yang }
732dc4fc0aeSLibin Yang 
7334d4ba014SRanjani Sridharan static int sof_ipc4_widget_setup_comp_mixer(struct snd_sof_widget *swidget)
7344d4ba014SRanjani Sridharan {
7354d4ba014SRanjani Sridharan 	struct snd_soc_component *scomp = swidget->scomp;
7364d4ba014SRanjani Sridharan 	struct sof_ipc4_mixer *mixer;
7374d4ba014SRanjani Sridharan 	int ret;
7384d4ba014SRanjani Sridharan 
7394d4ba014SRanjani Sridharan 	dev_dbg(scomp->dev, "Updating IPC structure for %s\n", swidget->widget->name);
7404d4ba014SRanjani Sridharan 
7414d4ba014SRanjani Sridharan 	mixer = kzalloc(sizeof(*mixer), GFP_KERNEL);
7424d4ba014SRanjani Sridharan 	if (!mixer)
7434d4ba014SRanjani Sridharan 		return -ENOMEM;
7444d4ba014SRanjani Sridharan 
7454d4ba014SRanjani Sridharan 	swidget->private = mixer;
7464d4ba014SRanjani Sridharan 
7474d4ba014SRanjani Sridharan 	/* The out_audio_fmt in topology is ignored as it is not required to be sent to the FW */
7484d4ba014SRanjani Sridharan 	ret = sof_ipc4_get_audio_fmt(scomp, swidget, &mixer->available_fmt, false);
7494d4ba014SRanjani Sridharan 	if (ret)
7504d4ba014SRanjani Sridharan 		goto err;
7514d4ba014SRanjani Sridharan 
7524d4ba014SRanjani Sridharan 	ret = sof_ipc4_widget_setup_msg(swidget, &mixer->msg);
7534d4ba014SRanjani Sridharan 	if (ret)
7544d4ba014SRanjani Sridharan 		goto err;
7554d4ba014SRanjani Sridharan 
7564d4ba014SRanjani Sridharan 	return 0;
7574d4ba014SRanjani Sridharan err:
758dc4fc0aeSLibin Yang 	sof_ipc4_free_audio_fmt(&mixer->available_fmt);
7594d4ba014SRanjani Sridharan 	kfree(mixer);
760b737fd8cSLibin Yang 	swidget->private = NULL;
7614d4ba014SRanjani Sridharan 	return ret;
7624d4ba014SRanjani Sridharan }
7634d4ba014SRanjani Sridharan 
764b85f4fc4SRander Wang static int sof_ipc4_widget_setup_comp_src(struct snd_sof_widget *swidget)
765b85f4fc4SRander Wang {
766b85f4fc4SRander Wang 	struct snd_soc_component *scomp = swidget->scomp;
767b85f4fc4SRander Wang 	struct sof_ipc4_src *src;
768b85f4fc4SRander Wang 	int ret;
769b85f4fc4SRander Wang 
770b85f4fc4SRander Wang 	dev_dbg(scomp->dev, "Updating IPC structure for %s\n", swidget->widget->name);
771b85f4fc4SRander Wang 
772b85f4fc4SRander Wang 	src = kzalloc(sizeof(*src), GFP_KERNEL);
773b85f4fc4SRander Wang 	if (!src)
774b85f4fc4SRander Wang 		return -ENOMEM;
775b85f4fc4SRander Wang 
776b85f4fc4SRander Wang 	swidget->private = src;
777b85f4fc4SRander Wang 
778b85f4fc4SRander Wang 	/* The out_audio_fmt in topology is ignored as it is not required by SRC */
779b85f4fc4SRander Wang 	ret = sof_ipc4_get_audio_fmt(scomp, swidget, &src->available_fmt, false);
780b85f4fc4SRander Wang 	if (ret)
781b85f4fc4SRander Wang 		goto err;
782b85f4fc4SRander Wang 
783b85f4fc4SRander Wang 	ret = sof_update_ipc_object(scomp, src, SOF_SRC_TOKENS, swidget->tuples,
784ecdb10dfSYang Yingliang 				    swidget->num_tuples, sizeof(*src), 1);
785b85f4fc4SRander Wang 	if (ret) {
786b85f4fc4SRander Wang 		dev_err(scomp->dev, "Parsing SRC tokens failed\n");
787b85f4fc4SRander Wang 		goto err;
788b85f4fc4SRander Wang 	}
789b85f4fc4SRander Wang 
790b85f4fc4SRander Wang 	dev_dbg(scomp->dev, "SRC sink rate %d\n", src->sink_rate);
791b85f4fc4SRander Wang 
792b85f4fc4SRander Wang 	ret = sof_ipc4_widget_setup_msg(swidget, &src->msg);
793b85f4fc4SRander Wang 	if (ret)
794b85f4fc4SRander Wang 		goto err;
795b85f4fc4SRander Wang 
796b85f4fc4SRander Wang 	return 0;
797b85f4fc4SRander Wang err:
798b85f4fc4SRander Wang 	sof_ipc4_free_audio_fmt(&src->available_fmt);
799b85f4fc4SRander Wang 	kfree(src);
800b85f4fc4SRander Wang 	swidget->private = NULL;
801b85f4fc4SRander Wang 	return ret;
802b85f4fc4SRander Wang }
803b85f4fc4SRander Wang 
804b85f4fc4SRander Wang static void sof_ipc4_widget_free_comp_src(struct snd_sof_widget *swidget)
805b85f4fc4SRander Wang {
806b85f4fc4SRander Wang 	struct sof_ipc4_src *src = swidget->private;
807b85f4fc4SRander Wang 
808b85f4fc4SRander Wang 	if (!src)
809b85f4fc4SRander Wang 		return;
810b85f4fc4SRander Wang 
811b85f4fc4SRander Wang 	sof_ipc4_free_audio_fmt(&src->available_fmt);
812b85f4fc4SRander Wang 	kfree(swidget->private);
813b85f4fc4SRander Wang 	swidget->private = NULL;
814b85f4fc4SRander Wang }
815b85f4fc4SRander Wang 
816dc4fc0aeSLibin Yang static void sof_ipc4_widget_free_comp_mixer(struct snd_sof_widget *swidget)
817dc4fc0aeSLibin Yang {
818dc4fc0aeSLibin Yang 	struct sof_ipc4_mixer *mixer = swidget->private;
819dc4fc0aeSLibin Yang 
820dc4fc0aeSLibin Yang 	if (!mixer)
821dc4fc0aeSLibin Yang 		return;
822dc4fc0aeSLibin Yang 
823dc4fc0aeSLibin Yang 	sof_ipc4_free_audio_fmt(&mixer->available_fmt);
824dc4fc0aeSLibin Yang 	kfree(swidget->private);
825dc4fc0aeSLibin Yang 	swidget->private = NULL;
826dc4fc0aeSLibin Yang }
827dc4fc0aeSLibin Yang 
828904c48c4SRanjani Sridharan static void
829904c48c4SRanjani Sridharan sof_ipc4_update_pipeline_mem_usage(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,
830904c48c4SRanjani Sridharan 				   struct sof_ipc4_base_module_cfg *base_config)
831904c48c4SRanjani Sridharan {
832904c48c4SRanjani Sridharan 	struct sof_ipc4_fw_module *fw_module = swidget->module_info;
833904c48c4SRanjani Sridharan 	struct snd_sof_widget *pipe_widget;
834904c48c4SRanjani Sridharan 	struct sof_ipc4_pipeline *pipeline;
835904c48c4SRanjani Sridharan 	int task_mem, queue_mem;
836904c48c4SRanjani Sridharan 	int ibs, bss, total;
837904c48c4SRanjani Sridharan 
838904c48c4SRanjani Sridharan 	ibs = base_config->ibs;
839904c48c4SRanjani Sridharan 	bss = base_config->is_pages;
840904c48c4SRanjani Sridharan 
841904c48c4SRanjani Sridharan 	task_mem = SOF_IPC4_PIPELINE_OBJECT_SIZE;
842904c48c4SRanjani Sridharan 	task_mem += SOF_IPC4_MODULE_INSTANCE_LIST_ITEM_SIZE + bss;
843904c48c4SRanjani Sridharan 
844904c48c4SRanjani Sridharan 	if (fw_module->man4_module_entry.type & SOF_IPC4_MODULE_LL) {
845904c48c4SRanjani Sridharan 		task_mem += SOF_IPC4_FW_ROUNDUP(SOF_IPC4_LL_TASK_OBJECT_SIZE);
846904c48c4SRanjani Sridharan 		task_mem += SOF_IPC4_FW_MAX_QUEUE_COUNT * SOF_IPC4_MODULE_INSTANCE_LIST_ITEM_SIZE;
847904c48c4SRanjani Sridharan 		task_mem += SOF_IPC4_LL_TASK_LIST_ITEM_SIZE;
848904c48c4SRanjani Sridharan 	} else {
849904c48c4SRanjani Sridharan 		task_mem += SOF_IPC4_FW_ROUNDUP(SOF_IPC4_DP_TASK_OBJECT_SIZE);
850904c48c4SRanjani Sridharan 		task_mem += SOF_IPC4_DP_TASK_LIST_SIZE;
851904c48c4SRanjani Sridharan 	}
852904c48c4SRanjani Sridharan 
853904c48c4SRanjani Sridharan 	ibs = SOF_IPC4_FW_ROUNDUP(ibs);
854904c48c4SRanjani Sridharan 	queue_mem = SOF_IPC4_FW_MAX_QUEUE_COUNT * (SOF_IPC4_DATA_QUEUE_OBJECT_SIZE +  ibs);
855904c48c4SRanjani Sridharan 
856904c48c4SRanjani Sridharan 	total = SOF_IPC4_FW_PAGE(task_mem + queue_mem);
857904c48c4SRanjani Sridharan 
8589c04363dSRanjani Sridharan 	pipe_widget = swidget->spipe->pipe_widget;
859904c48c4SRanjani Sridharan 	pipeline = pipe_widget->private;
860904c48c4SRanjani Sridharan 	pipeline->mem_usage += total;
861904c48c4SRanjani Sridharan }
862904c48c4SRanjani Sridharan 
863904c48c4SRanjani Sridharan static int sof_ipc4_widget_assign_instance_id(struct snd_sof_dev *sdev,
864904c48c4SRanjani Sridharan 					      struct snd_sof_widget *swidget)
865904c48c4SRanjani Sridharan {
866904c48c4SRanjani Sridharan 	struct sof_ipc4_fw_module *fw_module = swidget->module_info;
867904c48c4SRanjani Sridharan 	int max_instances = fw_module->man4_module_entry.instance_max_count;
868904c48c4SRanjani Sridharan 
869904c48c4SRanjani Sridharan 	swidget->instance_id = ida_alloc_max(&fw_module->m_ida, max_instances, GFP_KERNEL);
870904c48c4SRanjani Sridharan 	if (swidget->instance_id < 0) {
871904c48c4SRanjani Sridharan 		dev_err(sdev->dev, "failed to assign instance id for widget %s",
872904c48c4SRanjani Sridharan 			swidget->widget->name);
873904c48c4SRanjani Sridharan 		return swidget->instance_id;
874904c48c4SRanjani Sridharan 	}
875904c48c4SRanjani Sridharan 
876904c48c4SRanjani Sridharan 	return 0;
877904c48c4SRanjani Sridharan }
878904c48c4SRanjani Sridharan 
879904c48c4SRanjani Sridharan static int sof_ipc4_init_audio_fmt(struct snd_sof_dev *sdev,
880904c48c4SRanjani Sridharan 				   struct snd_sof_widget *swidget,
881904c48c4SRanjani Sridharan 				   struct sof_ipc4_base_module_cfg *base_config,
882904c48c4SRanjani Sridharan 				   struct sof_ipc4_audio_format *out_format,
883904c48c4SRanjani Sridharan 				   struct snd_pcm_hw_params *params,
884904c48c4SRanjani Sridharan 				   struct sof_ipc4_available_audio_format *available_fmt,
885904c48c4SRanjani Sridharan 				   size_t object_offset)
886904c48c4SRanjani Sridharan {
887904c48c4SRanjani Sridharan 	void *ptr = available_fmt->ref_audio_fmt;
888904c48c4SRanjani Sridharan 	u32 valid_bits;
889904c48c4SRanjani Sridharan 	u32 channels;
890904c48c4SRanjani Sridharan 	u32 rate;
891904c48c4SRanjani Sridharan 	int sample_valid_bits;
892904c48c4SRanjani Sridharan 	int i;
893904c48c4SRanjani Sridharan 
894904c48c4SRanjani Sridharan 	if (!ptr) {
895904c48c4SRanjani Sridharan 		dev_err(sdev->dev, "no reference formats for %s\n", swidget->widget->name);
896904c48c4SRanjani Sridharan 		return -EINVAL;
897904c48c4SRanjani Sridharan 	}
898904c48c4SRanjani Sridharan 
899904c48c4SRanjani Sridharan 	switch (params_format(params)) {
900904c48c4SRanjani Sridharan 	case SNDRV_PCM_FORMAT_S16_LE:
901904c48c4SRanjani Sridharan 		sample_valid_bits = 16;
902904c48c4SRanjani Sridharan 		break;
903904c48c4SRanjani Sridharan 	case SNDRV_PCM_FORMAT_S24_LE:
904904c48c4SRanjani Sridharan 		sample_valid_bits = 24;
905904c48c4SRanjani Sridharan 		break;
906904c48c4SRanjani Sridharan 	case SNDRV_PCM_FORMAT_S32_LE:
907904c48c4SRanjani Sridharan 		sample_valid_bits = 32;
908904c48c4SRanjani Sridharan 		break;
909904c48c4SRanjani Sridharan 	default:
910904c48c4SRanjani Sridharan 		dev_err(sdev->dev, "invalid pcm frame format %d\n", params_format(params));
911904c48c4SRanjani Sridharan 		return -EINVAL;
912904c48c4SRanjani Sridharan 	}
913904c48c4SRanjani Sridharan 
914904c48c4SRanjani Sridharan 	if (!available_fmt->audio_fmt_num) {
915904c48c4SRanjani Sridharan 		dev_err(sdev->dev, "no formats available for %s\n", swidget->widget->name);
916904c48c4SRanjani Sridharan 		return -EINVAL;
917904c48c4SRanjani Sridharan 	}
918904c48c4SRanjani Sridharan 
919904c48c4SRanjani Sridharan 	/*
920904c48c4SRanjani Sridharan 	 * Search supported audio formats to match rate, channels ,and
921904c48c4SRanjani Sridharan 	 * sample_valid_bytes from runtime params
922904c48c4SRanjani Sridharan 	 */
923904c48c4SRanjani Sridharan 	for (i = 0; i < available_fmt->audio_fmt_num; i++, ptr = (u8 *)ptr + object_offset) {
924904c48c4SRanjani Sridharan 		struct sof_ipc4_audio_format *fmt = ptr;
925904c48c4SRanjani Sridharan 
926904c48c4SRanjani Sridharan 		rate = fmt->sampling_frequency;
927904c48c4SRanjani Sridharan 		channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg);
928904c48c4SRanjani Sridharan 		valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);
929904c48c4SRanjani Sridharan 		if (params_rate(params) == rate && params_channels(params) == channels &&
930904c48c4SRanjani Sridharan 		    sample_valid_bits == valid_bits) {
9313809264bSPierre-Louis Bossart 			dev_dbg(sdev->dev, "matching audio format index for %uHz, %ubit, %u channels: %d\n",
9323809264bSPierre-Louis Bossart 				rate, valid_bits, channels, i);
933904c48c4SRanjani Sridharan 
934904c48c4SRanjani Sridharan 			/* copy ibs/obs and input format */
935904c48c4SRanjani Sridharan 			memcpy(base_config, &available_fmt->base_config[i],
936904c48c4SRanjani Sridharan 			       sizeof(struct sof_ipc4_base_module_cfg));
937904c48c4SRanjani Sridharan 
938904c48c4SRanjani Sridharan 			/* copy output format */
939904c48c4SRanjani Sridharan 			if (out_format)
940904c48c4SRanjani Sridharan 				memcpy(out_format, &available_fmt->out_audio_fmt[i],
941904c48c4SRanjani Sridharan 				       sizeof(struct sof_ipc4_audio_format));
942904c48c4SRanjani Sridharan 			break;
943904c48c4SRanjani Sridharan 		}
944904c48c4SRanjani Sridharan 	}
945904c48c4SRanjani Sridharan 
946904c48c4SRanjani Sridharan 	if (i == available_fmt->audio_fmt_num) {
947904c48c4SRanjani Sridharan 		dev_err(sdev->dev, "%s: Unsupported audio format: %uHz, %ubit, %u channels\n",
948904c48c4SRanjani Sridharan 			__func__, params_rate(params), sample_valid_bits, params_channels(params));
949904c48c4SRanjani Sridharan 		return -EINVAL;
950904c48c4SRanjani Sridharan 	}
951904c48c4SRanjani Sridharan 
952904c48c4SRanjani Sridharan 	dev_dbg(sdev->dev, "Init input audio formats for %s\n", swidget->widget->name);
953904c48c4SRanjani Sridharan 	sof_ipc4_dbg_audio_format(sdev->dev, &base_config->audio_fmt,
954904c48c4SRanjani Sridharan 				  sizeof(*base_config), 1);
955904c48c4SRanjani Sridharan 	if (out_format) {
956904c48c4SRanjani Sridharan 		dev_dbg(sdev->dev, "Init output audio formats for %s\n", swidget->widget->name);
957904c48c4SRanjani Sridharan 		sof_ipc4_dbg_audio_format(sdev->dev, out_format,
958904c48c4SRanjani Sridharan 					  sizeof(*out_format), 1);
959904c48c4SRanjani Sridharan 	}
960904c48c4SRanjani Sridharan 
961904c48c4SRanjani Sridharan 	/* Return the index of the matched format */
962904c48c4SRanjani Sridharan 	return i;
963904c48c4SRanjani Sridharan }
964904c48c4SRanjani Sridharan 
965904c48c4SRanjani Sridharan static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget)
966904c48c4SRanjani Sridharan {
967904c48c4SRanjani Sridharan 	struct sof_ipc4_copier *ipc4_copier = NULL;
968904c48c4SRanjani Sridharan 	struct snd_sof_widget *pipe_widget;
969904c48c4SRanjani Sridharan 	struct sof_ipc4_pipeline *pipeline;
970904c48c4SRanjani Sridharan 
971904c48c4SRanjani Sridharan 	/* reset pipeline memory usage */
9729c04363dSRanjani Sridharan 	pipe_widget = swidget->spipe->pipe_widget;
973904c48c4SRanjani Sridharan 	pipeline = pipe_widget->private;
974904c48c4SRanjani Sridharan 	pipeline->mem_usage = 0;
975904c48c4SRanjani Sridharan 
9767d573425SBard Liao 	if (WIDGET_IS_AIF(swidget->id) || swidget->id == snd_soc_dapm_buffer) {
977904c48c4SRanjani Sridharan 		ipc4_copier = swidget->private;
978acf52594SRanjani Sridharan 	} else if (WIDGET_IS_DAI(swidget->id)) {
979acf52594SRanjani Sridharan 		struct snd_sof_dai *dai = swidget->private;
980acf52594SRanjani Sridharan 
981acf52594SRanjani Sridharan 		ipc4_copier = dai->private;
982a150345aSBard Liao 		if (ipc4_copier->dai_type == SOF_DAI_INTEL_ALH) {
983a150345aSBard Liao 			struct sof_ipc4_alh_configuration_blob *blob;
984a150345aSBard Liao 			unsigned int group_id;
985a150345aSBard Liao 
986a150345aSBard Liao 			blob = (struct sof_ipc4_alh_configuration_blob *)ipc4_copier->copier_config;
987a150345aSBard Liao 			if (blob->alh_cfg.count > 1) {
988a150345aSBard Liao 				group_id = SOF_IPC4_NODE_INDEX(ipc4_copier->data.gtw_cfg.node_id) -
989a150345aSBard Liao 					   ALH_MULTI_GTW_BASE;
990a150345aSBard Liao 				ida_free(&alh_group_ida, group_id);
991a150345aSBard Liao 			}
992a150345aSBard Liao 		}
993acf52594SRanjani Sridharan 	}
994904c48c4SRanjani Sridharan 
995904c48c4SRanjani Sridharan 	if (ipc4_copier) {
996904c48c4SRanjani Sridharan 		kfree(ipc4_copier->ipc_config_data);
997904c48c4SRanjani Sridharan 		ipc4_copier->ipc_config_data = NULL;
998904c48c4SRanjani Sridharan 		ipc4_copier->ipc_config_size = 0;
999904c48c4SRanjani Sridharan 	}
1000904c48c4SRanjani Sridharan }
1001904c48c4SRanjani Sridharan 
1002aa84ffb7SRanjani Sridharan #if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_SND_INTEL_NHLT)
1003aa84ffb7SRanjani Sridharan static int snd_sof_get_hw_config_params(struct snd_sof_dev *sdev, struct snd_sof_dai *dai,
1004aa84ffb7SRanjani Sridharan 					int *sample_rate, int *channel_count, int *bit_depth)
1005aa84ffb7SRanjani Sridharan {
1006aa84ffb7SRanjani Sridharan 	struct snd_soc_tplg_hw_config *hw_config;
1007aa84ffb7SRanjani Sridharan 	struct snd_sof_dai_link *slink;
1008aa84ffb7SRanjani Sridharan 	bool dai_link_found = false;
1009aa84ffb7SRanjani Sridharan 	bool hw_cfg_found = false;
1010aa84ffb7SRanjani Sridharan 	int i;
1011aa84ffb7SRanjani Sridharan 
1012aa84ffb7SRanjani Sridharan 	/* get current hw_config from link */
1013aa84ffb7SRanjani Sridharan 	list_for_each_entry(slink, &sdev->dai_link_list, list) {
1014aa84ffb7SRanjani Sridharan 		if (!strcmp(slink->link->name, dai->name)) {
1015aa84ffb7SRanjani Sridharan 			dai_link_found = true;
1016aa84ffb7SRanjani Sridharan 			break;
1017aa84ffb7SRanjani Sridharan 		}
1018aa84ffb7SRanjani Sridharan 	}
1019aa84ffb7SRanjani Sridharan 
1020aa84ffb7SRanjani Sridharan 	if (!dai_link_found) {
1021aa84ffb7SRanjani Sridharan 		dev_err(sdev->dev, "%s: no DAI link found for DAI %s\n", __func__, dai->name);
1022aa84ffb7SRanjani Sridharan 		return -EINVAL;
1023aa84ffb7SRanjani Sridharan 	}
1024aa84ffb7SRanjani Sridharan 
1025aa84ffb7SRanjani Sridharan 	for (i = 0; i < slink->num_hw_configs; i++) {
1026aa84ffb7SRanjani Sridharan 		hw_config = &slink->hw_configs[i];
1027aa84ffb7SRanjani Sridharan 		if (dai->current_config == le32_to_cpu(hw_config->id)) {
1028aa84ffb7SRanjani Sridharan 			hw_cfg_found = true;
1029aa84ffb7SRanjani Sridharan 			break;
1030aa84ffb7SRanjani Sridharan 		}
1031aa84ffb7SRanjani Sridharan 	}
1032aa84ffb7SRanjani Sridharan 
1033aa84ffb7SRanjani Sridharan 	if (!hw_cfg_found) {
1034aa84ffb7SRanjani Sridharan 		dev_err(sdev->dev, "%s: no matching hw_config found for DAI %s\n", __func__,
1035aa84ffb7SRanjani Sridharan 			dai->name);
1036aa84ffb7SRanjani Sridharan 		return -EINVAL;
1037aa84ffb7SRanjani Sridharan 	}
1038aa84ffb7SRanjani Sridharan 
1039aa84ffb7SRanjani Sridharan 	*bit_depth = le32_to_cpu(hw_config->tdm_slot_width);
1040aa84ffb7SRanjani Sridharan 	*channel_count = le32_to_cpu(hw_config->tdm_slots);
1041aa84ffb7SRanjani Sridharan 	*sample_rate = le32_to_cpu(hw_config->fsync_rate);
1042aa84ffb7SRanjani Sridharan 
10433809264bSPierre-Louis Bossart 	dev_dbg(sdev->dev, "sample rate: %d sample width: %d channels: %d\n",
10443809264bSPierre-Louis Bossart 		*sample_rate, *bit_depth, *channel_count);
1045aa84ffb7SRanjani Sridharan 
1046aa84ffb7SRanjani Sridharan 	return 0;
1047aa84ffb7SRanjani Sridharan }
1048aa84ffb7SRanjani Sridharan 
1049aa84ffb7SRanjani Sridharan static int snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai,
1050aa84ffb7SRanjani Sridharan 					  struct snd_pcm_hw_params *params, u32 dai_index,
1051aa84ffb7SRanjani Sridharan 					  u32 linktype, u8 dir, u32 **dst, u32 *len)
1052aa84ffb7SRanjani Sridharan {
1053aa84ffb7SRanjani Sridharan 	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
1054aa84ffb7SRanjani Sridharan 	struct nhlt_specific_cfg *cfg;
1055aa84ffb7SRanjani Sridharan 	int sample_rate, channel_count;
1056aa84ffb7SRanjani Sridharan 	int bit_depth, ret;
1057aa84ffb7SRanjani Sridharan 	u32 nhlt_type;
1058aa84ffb7SRanjani Sridharan 
1059aa84ffb7SRanjani Sridharan 	/* convert to NHLT type */
1060aa84ffb7SRanjani Sridharan 	switch (linktype) {
1061aa84ffb7SRanjani Sridharan 	case SOF_DAI_INTEL_DMIC:
1062aa84ffb7SRanjani Sridharan 		nhlt_type = NHLT_LINK_DMIC;
1063aa84ffb7SRanjani Sridharan 		bit_depth = params_width(params);
1064aa84ffb7SRanjani Sridharan 		channel_count = params_channels(params);
1065aa84ffb7SRanjani Sridharan 		sample_rate = params_rate(params);
1066aa84ffb7SRanjani Sridharan 		break;
1067aa84ffb7SRanjani Sridharan 	case SOF_DAI_INTEL_SSP:
1068aa84ffb7SRanjani Sridharan 		nhlt_type = NHLT_LINK_SSP;
1069aa84ffb7SRanjani Sridharan 		ret = snd_sof_get_hw_config_params(sdev, dai, &sample_rate, &channel_count,
1070aa84ffb7SRanjani Sridharan 						   &bit_depth);
1071aa84ffb7SRanjani Sridharan 		if (ret < 0)
1072aa84ffb7SRanjani Sridharan 			return ret;
1073aa84ffb7SRanjani Sridharan 		break;
1074aa84ffb7SRanjani Sridharan 	default:
1075aa84ffb7SRanjani Sridharan 		return 0;
1076aa84ffb7SRanjani Sridharan 	}
1077aa84ffb7SRanjani Sridharan 
10783809264bSPierre-Louis Bossart 	dev_dbg(sdev->dev, "dai index %d nhlt type %d direction %d\n",
10793809264bSPierre-Louis Bossart 		dai_index, nhlt_type, dir);
1080aa84ffb7SRanjani Sridharan 
1081aa84ffb7SRanjani Sridharan 	/* find NHLT blob with matching params */
1082aa84ffb7SRanjani Sridharan 	cfg = intel_nhlt_get_endpoint_blob(sdev->dev, ipc4_data->nhlt, dai_index, nhlt_type,
1083aa84ffb7SRanjani Sridharan 					   bit_depth, bit_depth, channel_count, sample_rate,
1084aa84ffb7SRanjani Sridharan 					   dir, 0);
1085aa84ffb7SRanjani Sridharan 
1086aa84ffb7SRanjani Sridharan 	if (!cfg) {
1087aa84ffb7SRanjani Sridharan 		dev_err(sdev->dev,
1088aa84ffb7SRanjani Sridharan 			"no matching blob for sample rate: %d sample width: %d channels: %d\n",
1089aa84ffb7SRanjani Sridharan 			sample_rate, bit_depth, channel_count);
1090aa84ffb7SRanjani Sridharan 		return -EINVAL;
1091aa84ffb7SRanjani Sridharan 	}
1092aa84ffb7SRanjani Sridharan 
1093aa84ffb7SRanjani Sridharan 	/* config length should be in dwords */
1094aa84ffb7SRanjani Sridharan 	*len = cfg->size >> 2;
1095aa84ffb7SRanjani Sridharan 	*dst = (u32 *)cfg->caps;
1096aa84ffb7SRanjani Sridharan 
1097aa84ffb7SRanjani Sridharan 	return 0;
1098aa84ffb7SRanjani Sridharan }
1099aa84ffb7SRanjani Sridharan #else
1100aa84ffb7SRanjani Sridharan static int snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai,
1101aa84ffb7SRanjani Sridharan 					  struct snd_pcm_hw_params *params, u32 dai_index,
1102aa84ffb7SRanjani Sridharan 					  u32 linktype, u8 dir, u32 **dst, u32 *len)
1103aa84ffb7SRanjani Sridharan {
1104aa84ffb7SRanjani Sridharan 	return 0;
1105aa84ffb7SRanjani Sridharan }
1106aa84ffb7SRanjani Sridharan #endif
1107aa84ffb7SRanjani Sridharan 
1108904c48c4SRanjani Sridharan static int
1109904c48c4SRanjani Sridharan sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
1110904c48c4SRanjani Sridharan 			       struct snd_pcm_hw_params *fe_params,
1111904c48c4SRanjani Sridharan 			       struct snd_sof_platform_stream_params *platform_params,
1112904c48c4SRanjani Sridharan 			       struct snd_pcm_hw_params *pipeline_params, int dir)
1113904c48c4SRanjani Sridharan {
1114904c48c4SRanjani Sridharan 	struct sof_ipc4_available_audio_format *available_fmt;
1115904c48c4SRanjani Sridharan 	struct snd_soc_component *scomp = swidget->scomp;
1116904c48c4SRanjani Sridharan 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
1117904c48c4SRanjani Sridharan 	struct sof_ipc4_copier_data *copier_data;
1118904c48c4SRanjani Sridharan 	struct snd_pcm_hw_params *ref_params;
1119904c48c4SRanjani Sridharan 	struct sof_ipc4_copier *ipc4_copier;
1120a150345aSBard Liao 	struct snd_sof_dai *dai;
1121904c48c4SRanjani Sridharan 	struct snd_mask *fmt;
1122904c48c4SRanjani Sridharan 	int out_sample_valid_bits;
1123904c48c4SRanjani Sridharan 	size_t ref_audio_fmt_size;
1124904c48c4SRanjani Sridharan 	void **ipc_config_data;
1125904c48c4SRanjani Sridharan 	int *ipc_config_size;
1126904c48c4SRanjani Sridharan 	u32 **data;
1127904c48c4SRanjani Sridharan 	int ipc_size, ret;
1128904c48c4SRanjani Sridharan 
11293809264bSPierre-Louis Bossart 	dev_dbg(sdev->dev, "copier %s, type %d", swidget->widget->name, swidget->id);
1130904c48c4SRanjani Sridharan 
1131904c48c4SRanjani Sridharan 	switch (swidget->id) {
1132904c48c4SRanjani Sridharan 	case snd_soc_dapm_aif_in:
1133904c48c4SRanjani Sridharan 	case snd_soc_dapm_aif_out:
1134904c48c4SRanjani Sridharan 	{
1135904c48c4SRanjani Sridharan 		struct sof_ipc4_gtw_attributes *gtw_attr;
1136904c48c4SRanjani Sridharan 		struct snd_sof_widget *pipe_widget;
1137904c48c4SRanjani Sridharan 		struct sof_ipc4_pipeline *pipeline;
1138904c48c4SRanjani Sridharan 
11399c04363dSRanjani Sridharan 		pipe_widget = swidget->spipe->pipe_widget;
1140904c48c4SRanjani Sridharan 		pipeline = pipe_widget->private;
1141904c48c4SRanjani Sridharan 		ipc4_copier = (struct sof_ipc4_copier *)swidget->private;
1142904c48c4SRanjani Sridharan 		gtw_attr = ipc4_copier->gtw_attr;
1143904c48c4SRanjani Sridharan 		copier_data = &ipc4_copier->data;
1144904c48c4SRanjani Sridharan 		available_fmt = &ipc4_copier->available_fmt;
1145904c48c4SRanjani Sridharan 
1146904c48c4SRanjani Sridharan 		/*
1147904c48c4SRanjani Sridharan 		 * base_config->audio_fmt and out_audio_fmt represent the input and output audio
1148904c48c4SRanjani Sridharan 		 * formats. Use the input format as the reference to match pcm params for playback
1149904c48c4SRanjani Sridharan 		 * and the output format as reference for capture.
1150904c48c4SRanjani Sridharan 		 */
1151904c48c4SRanjani Sridharan 		if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
1152904c48c4SRanjani Sridharan 			available_fmt->ref_audio_fmt = &available_fmt->base_config->audio_fmt;
1153904c48c4SRanjani Sridharan 			ref_audio_fmt_size = sizeof(struct sof_ipc4_base_module_cfg);
1154904c48c4SRanjani Sridharan 		} else {
1155904c48c4SRanjani Sridharan 			available_fmt->ref_audio_fmt = available_fmt->out_audio_fmt;
1156904c48c4SRanjani Sridharan 			ref_audio_fmt_size = sizeof(struct sof_ipc4_audio_format);
1157904c48c4SRanjani Sridharan 		}
1158904c48c4SRanjani Sridharan 		copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
1159904c48c4SRanjani Sridharan 		copier_data->gtw_cfg.node_id |=
1160904c48c4SRanjani Sridharan 			SOF_IPC4_NODE_INDEX(platform_params->stream_tag - 1);
1161904c48c4SRanjani Sridharan 
1162904c48c4SRanjani Sridharan 		/* set gateway attributes */
1163904c48c4SRanjani Sridharan 		gtw_attr->lp_buffer_alloc = pipeline->lp_mode;
1164904c48c4SRanjani Sridharan 		ref_params = fe_params;
1165904c48c4SRanjani Sridharan 		break;
1166904c48c4SRanjani Sridharan 	}
1167acf52594SRanjani Sridharan 	case snd_soc_dapm_dai_in:
1168acf52594SRanjani Sridharan 	case snd_soc_dapm_dai_out:
1169acf52594SRanjani Sridharan 	{
1170a150345aSBard Liao 		dai = swidget->private;
1171acf52594SRanjani Sridharan 
1172acf52594SRanjani Sridharan 		ipc4_copier = (struct sof_ipc4_copier *)dai->private;
1173acf52594SRanjani Sridharan 		copier_data = &ipc4_copier->data;
1174acf52594SRanjani Sridharan 		available_fmt = &ipc4_copier->available_fmt;
1175acf52594SRanjani Sridharan 		if (dir == SNDRV_PCM_STREAM_CAPTURE) {
1176acf52594SRanjani Sridharan 			available_fmt->ref_audio_fmt = available_fmt->out_audio_fmt;
1177acf52594SRanjani Sridharan 			ref_audio_fmt_size = sizeof(struct sof_ipc4_audio_format);
1178acf52594SRanjani Sridharan 
1179acf52594SRanjani Sridharan 			/*
1180acf52594SRanjani Sridharan 			 * modify the input params for the dai copier as it only supports
1181acf52594SRanjani Sridharan 			 * 32-bit always
1182acf52594SRanjani Sridharan 			 */
1183acf52594SRanjani Sridharan 			fmt = hw_param_mask(pipeline_params, SNDRV_PCM_HW_PARAM_FORMAT);
1184acf52594SRanjani Sridharan 			snd_mask_none(fmt);
1185acf52594SRanjani Sridharan 			snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE);
1186acf52594SRanjani Sridharan 		} else {
1187acf52594SRanjani Sridharan 			available_fmt->ref_audio_fmt = &available_fmt->base_config->audio_fmt;
1188acf52594SRanjani Sridharan 			ref_audio_fmt_size = sizeof(struct sof_ipc4_base_module_cfg);
1189acf52594SRanjani Sridharan 		}
1190acf52594SRanjani Sridharan 
1191acf52594SRanjani Sridharan 		ref_params = pipeline_params;
1192acf52594SRanjani Sridharan 
1193aa84ffb7SRanjani Sridharan 		ret = snd_sof_get_nhlt_endpoint_data(sdev, dai, fe_params, ipc4_copier->dai_index,
1194aa84ffb7SRanjani Sridharan 						     ipc4_copier->dai_type, dir,
1195aa84ffb7SRanjani Sridharan 						     &ipc4_copier->copier_config,
1196aa84ffb7SRanjani Sridharan 						     &copier_data->gtw_cfg.config_length);
1197aa84ffb7SRanjani Sridharan 		if (ret < 0)
1198aa84ffb7SRanjani Sridharan 			return ret;
1199aa84ffb7SRanjani Sridharan 
1200acf52594SRanjani Sridharan 		break;
1201acf52594SRanjani Sridharan 	}
12027d573425SBard Liao 	case snd_soc_dapm_buffer:
12037d573425SBard Liao 	{
12047d573425SBard Liao 		ipc4_copier = (struct sof_ipc4_copier *)swidget->private;
12057d573425SBard Liao 		copier_data = &ipc4_copier->data;
12067d573425SBard Liao 		available_fmt = &ipc4_copier->available_fmt;
12077d573425SBard Liao 
12087d573425SBard Liao 		/*
12097d573425SBard Liao 		 * base_config->audio_fmt represent the input audio formats. Use
12107d573425SBard Liao 		 * the input format as the reference to match pcm params
12117d573425SBard Liao 		 */
12127d573425SBard Liao 		available_fmt->ref_audio_fmt = &available_fmt->base_config->audio_fmt;
12137d573425SBard Liao 		ref_audio_fmt_size = sizeof(struct sof_ipc4_base_module_cfg);
12147d573425SBard Liao 		ref_params = pipeline_params;
12157d573425SBard Liao 
12167d573425SBard Liao 		break;
12177d573425SBard Liao 	}
1218904c48c4SRanjani Sridharan 	default:
1219904c48c4SRanjani Sridharan 		dev_err(sdev->dev, "unsupported type %d for copier %s",
1220904c48c4SRanjani Sridharan 			swidget->id, swidget->widget->name);
1221904c48c4SRanjani Sridharan 		return -EINVAL;
1222904c48c4SRanjani Sridharan 	}
1223904c48c4SRanjani Sridharan 
1224904c48c4SRanjani Sridharan 	/* set input and output audio formats */
1225904c48c4SRanjani Sridharan 	ret = sof_ipc4_init_audio_fmt(sdev, swidget, &copier_data->base_config,
1226904c48c4SRanjani Sridharan 				      &copier_data->out_format, ref_params,
1227904c48c4SRanjani Sridharan 				      available_fmt, ref_audio_fmt_size);
1228904c48c4SRanjani Sridharan 	if (ret < 0)
1229904c48c4SRanjani Sridharan 		return ret;
1230904c48c4SRanjani Sridharan 
1231a45a4d43SBard Liao 	switch (swidget->id) {
1232a45a4d43SBard Liao 	case snd_soc_dapm_dai_in:
1233a45a4d43SBard Liao 	case snd_soc_dapm_dai_out:
1234a45a4d43SBard Liao 	{
1235a45a4d43SBard Liao 		/*
1236a45a4d43SBard Liao 		 * Only SOF_DAI_INTEL_ALH needs copier_data to set blob.
1237a45a4d43SBard Liao 		 * That's why only ALH dai's blob is set after sof_ipc4_init_audio_fmt
1238a45a4d43SBard Liao 		 */
1239a45a4d43SBard Liao 		if (ipc4_copier->dai_type == SOF_DAI_INTEL_ALH) {
1240a45a4d43SBard Liao 			struct sof_ipc4_alh_configuration_blob *blob;
1241a150345aSBard Liao 			struct sof_ipc4_copier_data *alh_data;
1242a150345aSBard Liao 			struct sof_ipc4_copier *alh_copier;
1243a150345aSBard Liao 			struct snd_sof_widget *w;
12440390a102SBard Liao 			u32 ch_count = 0;
1245a150345aSBard Liao 			u32 ch_mask = 0;
1246a45a4d43SBard Liao 			u32 ch_map;
12470390a102SBard Liao 			u32 step;
12480390a102SBard Liao 			u32 mask;
1249a45a4d43SBard Liao 			int i;
1250a45a4d43SBard Liao 
1251a45a4d43SBard Liao 			blob = (struct sof_ipc4_alh_configuration_blob *)ipc4_copier->copier_config;
1252a150345aSBard Liao 
1253a45a4d43SBard Liao 			blob->gw_attr.lp_buffer_alloc = 0;
1254a45a4d43SBard Liao 
1255a45a4d43SBard Liao 			/* Get channel_mask from ch_map */
1256a45a4d43SBard Liao 			ch_map = copier_data->base_config.audio_fmt.ch_map;
1257a45a4d43SBard Liao 			for (i = 0; ch_map; i++) {
12580390a102SBard Liao 				if ((ch_map & 0xf) != 0xf) {
1259a150345aSBard Liao 					ch_mask |= BIT(i);
12600390a102SBard Liao 					ch_count++;
12610390a102SBard Liao 				}
1262a45a4d43SBard Liao 				ch_map >>= 4;
1263a45a4d43SBard Liao 			}
1264a150345aSBard Liao 
12650390a102SBard Liao 			step = ch_count / blob->alh_cfg.count;
12660390a102SBard Liao 			mask =  GENMASK(step - 1, 0);
1267a150345aSBard Liao 			/*
1268a150345aSBard Liao 			 * Set each gtw_cfg.node_id to blob->alh_cfg.mapping[]
1269a150345aSBard Liao 			 * for all widgets with the same stream name
1270a150345aSBard Liao 			 */
1271a150345aSBard Liao 			i = 0;
1272a150345aSBard Liao 			list_for_each_entry(w, &sdev->widget_list, list) {
1273a150345aSBard Liao 				if (w->widget->sname &&
1274a150345aSBard Liao 				    strcmp(w->widget->sname, swidget->widget->sname))
1275a150345aSBard Liao 					continue;
1276a150345aSBard Liao 
1277a150345aSBard Liao 				dai = w->private;
1278a150345aSBard Liao 				alh_copier = (struct sof_ipc4_copier *)dai->private;
1279a150345aSBard Liao 				alh_data = &alh_copier->data;
1280a150345aSBard Liao 				blob->alh_cfg.mapping[i].alh_id = alh_data->gtw_cfg.node_id;
12810390a102SBard Liao 				/*
12820390a102SBard Liao 				 * Set the same channel mask for playback as the audio data is
12830390a102SBard Liao 				 * duplicated for all speakers. For capture, split the channels
12840390a102SBard Liao 				 * among the aggregated DAIs. For example, with 4 channels on 2
12850390a102SBard Liao 				 * aggregated DAIs, the channel_mask should be 0x3 and 0xc for the
12860390a102SBard Liao 				 * two DAI's.
12870390a102SBard Liao 				 * The channel masks used depend on the cpu_dais used in the
12880390a102SBard Liao 				 * dailink at the machine driver level, which actually comes from
12890390a102SBard Liao 				 * the tables in soc_acpi files depending on the _ADR and devID
12900390a102SBard Liao 				 * registers for each codec.
12910390a102SBard Liao 				 */
12920390a102SBard Liao 				if (w->id == snd_soc_dapm_dai_in)
1293a150345aSBard Liao 					blob->alh_cfg.mapping[i].channel_mask = ch_mask;
12940390a102SBard Liao 				else
12950390a102SBard Liao 					blob->alh_cfg.mapping[i].channel_mask = mask << (step * i);
12960390a102SBard Liao 
1297a150345aSBard Liao 				i++;
1298a150345aSBard Liao 			}
1299a150345aSBard Liao 			if (blob->alh_cfg.count > 1) {
1300a150345aSBard Liao 				int group_id;
1301a150345aSBard Liao 
13024ee6fc27SBard Liao 				group_id = ida_alloc_max(&alh_group_ida, ALH_MULTI_GTW_COUNT - 1,
1303a150345aSBard Liao 							 GFP_KERNEL);
1304a150345aSBard Liao 
1305a150345aSBard Liao 				if (group_id < 0)
1306a150345aSBard Liao 					return group_id;
1307a150345aSBard Liao 
1308a150345aSBard Liao 				/* add multi-gateway base */
1309a150345aSBard Liao 				group_id += ALH_MULTI_GTW_BASE;
1310a150345aSBard Liao 				copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
1311a150345aSBard Liao 				copier_data->gtw_cfg.node_id |= SOF_IPC4_NODE_INDEX(group_id);
1312a150345aSBard Liao 			}
1313a45a4d43SBard Liao 		}
1314a45a4d43SBard Liao 	}
1315a45a4d43SBard Liao 	}
1316a45a4d43SBard Liao 
1317904c48c4SRanjani Sridharan 	/* modify the input params for the next widget */
1318904c48c4SRanjani Sridharan 	fmt = hw_param_mask(pipeline_params, SNDRV_PCM_HW_PARAM_FORMAT);
1319904c48c4SRanjani Sridharan 	out_sample_valid_bits =
1320904c48c4SRanjani Sridharan 		SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(copier_data->out_format.fmt_cfg);
1321904c48c4SRanjani Sridharan 	snd_mask_none(fmt);
1322904c48c4SRanjani Sridharan 	switch (out_sample_valid_bits) {
1323904c48c4SRanjani Sridharan 	case 16:
1324904c48c4SRanjani Sridharan 		snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
1325904c48c4SRanjani Sridharan 		break;
1326904c48c4SRanjani Sridharan 	case 24:
1327904c48c4SRanjani Sridharan 		snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
1328904c48c4SRanjani Sridharan 		break;
1329904c48c4SRanjani Sridharan 	case 32:
1330904c48c4SRanjani Sridharan 		snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE);
1331904c48c4SRanjani Sridharan 		break;
1332904c48c4SRanjani Sridharan 	default:
1333904c48c4SRanjani Sridharan 		dev_err(sdev->dev, "invalid sample frame format %d\n",
1334904c48c4SRanjani Sridharan 			params_format(pipeline_params));
1335904c48c4SRanjani Sridharan 		return -EINVAL;
1336904c48c4SRanjani Sridharan 	}
1337904c48c4SRanjani Sridharan 
1338904c48c4SRanjani Sridharan 	/* set the gateway dma_buffer_size using the matched ID returned above */
1339904c48c4SRanjani Sridharan 	copier_data->gtw_cfg.dma_buffer_size = available_fmt->dma_buffer_size[ret];
1340904c48c4SRanjani Sridharan 
1341904c48c4SRanjani Sridharan 	data = &ipc4_copier->copier_config;
1342904c48c4SRanjani Sridharan 	ipc_config_size = &ipc4_copier->ipc_config_size;
1343904c48c4SRanjani Sridharan 	ipc_config_data = &ipc4_copier->ipc_config_data;
1344904c48c4SRanjani Sridharan 
1345904c48c4SRanjani Sridharan 	/* config_length is DWORD based */
1346904c48c4SRanjani Sridharan 	ipc_size = sizeof(*copier_data) + copier_data->gtw_cfg.config_length * 4;
1347904c48c4SRanjani Sridharan 
1348904c48c4SRanjani Sridharan 	dev_dbg(sdev->dev, "copier %s, IPC size is %d", swidget->widget->name, ipc_size);
1349904c48c4SRanjani Sridharan 
1350904c48c4SRanjani Sridharan 	*ipc_config_data = kzalloc(ipc_size, GFP_KERNEL);
1351904c48c4SRanjani Sridharan 	if (!*ipc_config_data)
1352904c48c4SRanjani Sridharan 		return -ENOMEM;
1353904c48c4SRanjani Sridharan 
1354904c48c4SRanjani Sridharan 	*ipc_config_size = ipc_size;
1355904c48c4SRanjani Sridharan 
1356904c48c4SRanjani Sridharan 	/* copy IPC data */
1357904c48c4SRanjani Sridharan 	memcpy(*ipc_config_data, (void *)copier_data, sizeof(*copier_data));
1358904c48c4SRanjani Sridharan 	if (copier_data->gtw_cfg.config_length)
1359904c48c4SRanjani Sridharan 		memcpy(*ipc_config_data + sizeof(*copier_data),
1360904c48c4SRanjani Sridharan 		       *data, copier_data->gtw_cfg.config_length * 4);
1361904c48c4SRanjani Sridharan 
1362904c48c4SRanjani Sridharan 	/* update pipeline memory usage */
1363904c48c4SRanjani Sridharan 	sof_ipc4_update_pipeline_mem_usage(sdev, swidget, &copier_data->base_config);
1364904c48c4SRanjani Sridharan 
1365711d0427SBard Liao 	return 0;
13664f838ab2SRanjani Sridharan }
13674f838ab2SRanjani Sridharan 
13684f838ab2SRanjani Sridharan static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget,
13694f838ab2SRanjani Sridharan 					struct snd_pcm_hw_params *fe_params,
13704f838ab2SRanjani Sridharan 					struct snd_sof_platform_stream_params *platform_params,
13714f838ab2SRanjani Sridharan 					struct snd_pcm_hw_params *pipeline_params, int dir)
13724f838ab2SRanjani Sridharan {
13734f838ab2SRanjani Sridharan 	struct snd_soc_component *scomp = swidget->scomp;
13744f838ab2SRanjani Sridharan 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
13754f838ab2SRanjani Sridharan 	struct sof_ipc4_gain *gain = swidget->private;
13764f838ab2SRanjani Sridharan 	int ret;
13774f838ab2SRanjani Sridharan 
13784f838ab2SRanjani Sridharan 	gain->available_fmt.ref_audio_fmt = &gain->available_fmt.base_config->audio_fmt;
13794f838ab2SRanjani Sridharan 
13804f838ab2SRanjani Sridharan 	/* output format is not required to be sent to the FW for gain */
13814f838ab2SRanjani Sridharan 	ret = sof_ipc4_init_audio_fmt(sdev, swidget, &gain->base_config,
13824f838ab2SRanjani Sridharan 				      NULL, pipeline_params, &gain->available_fmt,
13834f838ab2SRanjani Sridharan 				      sizeof(gain->base_config));
13844f838ab2SRanjani Sridharan 	if (ret < 0)
13854f838ab2SRanjani Sridharan 		return ret;
13864f838ab2SRanjani Sridharan 
13874f838ab2SRanjani Sridharan 	/* update pipeline memory usage */
13884f838ab2SRanjani Sridharan 	sof_ipc4_update_pipeline_mem_usage(sdev, swidget, &gain->base_config);
13894f838ab2SRanjani Sridharan 
1390711d0427SBard Liao 	return 0;
13914f838ab2SRanjani Sridharan }
13924f838ab2SRanjani Sridharan 
13934d4ba014SRanjani Sridharan static int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget,
13944d4ba014SRanjani Sridharan 					 struct snd_pcm_hw_params *fe_params,
13954d4ba014SRanjani Sridharan 					 struct snd_sof_platform_stream_params *platform_params,
13964d4ba014SRanjani Sridharan 					 struct snd_pcm_hw_params *pipeline_params, int dir)
13974d4ba014SRanjani Sridharan {
13984d4ba014SRanjani Sridharan 	struct snd_soc_component *scomp = swidget->scomp;
13994d4ba014SRanjani Sridharan 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
14004d4ba014SRanjani Sridharan 	struct sof_ipc4_mixer *mixer = swidget->private;
14014d4ba014SRanjani Sridharan 	int ret;
14024d4ba014SRanjani Sridharan 
14034d4ba014SRanjani Sridharan 	/* only 32bit is supported by mixer */
14044d4ba014SRanjani Sridharan 	mixer->available_fmt.ref_audio_fmt = &mixer->available_fmt.base_config->audio_fmt;
14054d4ba014SRanjani Sridharan 
14064d4ba014SRanjani Sridharan 	/* output format is not required to be sent to the FW for mixer */
14074d4ba014SRanjani Sridharan 	ret = sof_ipc4_init_audio_fmt(sdev, swidget, &mixer->base_config,
14084d4ba014SRanjani Sridharan 				      NULL, pipeline_params, &mixer->available_fmt,
14094d4ba014SRanjani Sridharan 				      sizeof(mixer->base_config));
14104d4ba014SRanjani Sridharan 	if (ret < 0)
14114d4ba014SRanjani Sridharan 		return ret;
14124d4ba014SRanjani Sridharan 
14134d4ba014SRanjani Sridharan 	/* update pipeline memory usage */
14144d4ba014SRanjani Sridharan 	sof_ipc4_update_pipeline_mem_usage(sdev, swidget, &mixer->base_config);
14154d4ba014SRanjani Sridharan 
1416711d0427SBard Liao 	return 0;
14174d4ba014SRanjani Sridharan }
14184d4ba014SRanjani Sridharan 
1419b85f4fc4SRander Wang static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget,
1420b85f4fc4SRander Wang 				       struct snd_pcm_hw_params *fe_params,
1421b85f4fc4SRander Wang 				       struct snd_sof_platform_stream_params *platform_params,
1422b85f4fc4SRander Wang 				       struct snd_pcm_hw_params *pipeline_params, int dir)
1423b85f4fc4SRander Wang {
1424b85f4fc4SRander Wang 	struct snd_soc_component *scomp = swidget->scomp;
1425b85f4fc4SRander Wang 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
1426b85f4fc4SRander Wang 	struct sof_ipc4_src *src = swidget->private;
1427b85f4fc4SRander Wang 	struct snd_interval *rate;
1428b85f4fc4SRander Wang 	int ret;
1429b85f4fc4SRander Wang 
1430b85f4fc4SRander Wang 	src->available_fmt.ref_audio_fmt = &src->available_fmt.base_config->audio_fmt;
1431b85f4fc4SRander Wang 
1432b85f4fc4SRander Wang 	/* output format is not required to be sent to the FW for SRC */
1433b85f4fc4SRander Wang 	ret = sof_ipc4_init_audio_fmt(sdev, swidget, &src->base_config,
1434b85f4fc4SRander Wang 				      NULL, pipeline_params, &src->available_fmt,
1435b85f4fc4SRander Wang 				      sizeof(src->base_config));
1436b85f4fc4SRander Wang 	if (ret < 0)
1437b85f4fc4SRander Wang 		return ret;
1438b85f4fc4SRander Wang 
1439b85f4fc4SRander Wang 	/* update pipeline memory usage */
1440b85f4fc4SRander Wang 	sof_ipc4_update_pipeline_mem_usage(sdev, swidget, &src->base_config);
1441b85f4fc4SRander Wang 
1442b85f4fc4SRander Wang 	/* update pipeline_params for sink widgets */
1443b85f4fc4SRander Wang 	rate = hw_param_interval(pipeline_params, SNDRV_PCM_HW_PARAM_RATE);
1444b85f4fc4SRander Wang 	rate->min = src->sink_rate;
1445b85f4fc4SRander Wang 	rate->max = rate->min;
1446b85f4fc4SRander Wang 
1447b85f4fc4SRander Wang 	return 0;
1448b85f4fc4SRander Wang }
1449b85f4fc4SRander Wang 
1450d97964f8SRanjani Sridharan static int sof_ipc4_control_load_volume(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)
1451d97964f8SRanjani Sridharan {
1452d97964f8SRanjani Sridharan 	struct sof_ipc4_control_data *control_data;
1453d97964f8SRanjani Sridharan 	struct sof_ipc4_msg *msg;
1454d97964f8SRanjani Sridharan 	int i;
1455d97964f8SRanjani Sridharan 
1456d97964f8SRanjani Sridharan 	scontrol->size = struct_size(control_data, chanv, scontrol->num_channels);
1457d97964f8SRanjani Sridharan 
1458d97964f8SRanjani Sridharan 	/* scontrol->ipc_control_data will be freed in sof_control_unload */
1459d97964f8SRanjani Sridharan 	scontrol->ipc_control_data = kzalloc(scontrol->size, GFP_KERNEL);
1460d97964f8SRanjani Sridharan 	if (!scontrol->ipc_control_data)
1461d97964f8SRanjani Sridharan 		return -ENOMEM;
1462d97964f8SRanjani Sridharan 
1463d97964f8SRanjani Sridharan 	control_data = scontrol->ipc_control_data;
1464d97964f8SRanjani Sridharan 	control_data->index = scontrol->index;
1465d97964f8SRanjani Sridharan 
1466d97964f8SRanjani Sridharan 	msg = &control_data->msg;
1467d97964f8SRanjani Sridharan 	msg->primary = SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_SET);
1468d97964f8SRanjani Sridharan 	msg->primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
1469d97964f8SRanjani Sridharan 	msg->primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
1470d97964f8SRanjani Sridharan 
1471d97964f8SRanjani Sridharan 	msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_GAIN_PARAM_ID);
1472d97964f8SRanjani Sridharan 
1473d97964f8SRanjani Sridharan 	/* set default volume values to 0dB in control */
1474d97964f8SRanjani Sridharan 	for (i = 0; i < scontrol->num_channels; i++) {
1475d97964f8SRanjani Sridharan 		control_data->chanv[i].channel = i;
1476d97964f8SRanjani Sridharan 		control_data->chanv[i].value = SOF_IPC4_VOL_ZERO_DB;
1477d97964f8SRanjani Sridharan 	}
1478d97964f8SRanjani Sridharan 
1479d97964f8SRanjani Sridharan 	return 0;
1480d97964f8SRanjani Sridharan }
1481d97964f8SRanjani Sridharan 
1482d97964f8SRanjani Sridharan static int sof_ipc4_control_setup(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)
1483d97964f8SRanjani Sridharan {
1484d97964f8SRanjani Sridharan 	switch (scontrol->info_type) {
1485d97964f8SRanjani Sridharan 	case SND_SOC_TPLG_CTL_VOLSW:
1486d97964f8SRanjani Sridharan 	case SND_SOC_TPLG_CTL_VOLSW_SX:
1487d97964f8SRanjani Sridharan 	case SND_SOC_TPLG_CTL_VOLSW_XR_SX:
1488d97964f8SRanjani Sridharan 		return sof_ipc4_control_load_volume(sdev, scontrol);
1489d97964f8SRanjani Sridharan 	default:
1490d97964f8SRanjani Sridharan 		break;
1491d97964f8SRanjani Sridharan 	}
1492d97964f8SRanjani Sridharan 
1493d97964f8SRanjani Sridharan 	return 0;
1494d97964f8SRanjani Sridharan }
1495d97964f8SRanjani Sridharan 
14966e9257a1SRanjani Sridharan static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
14976e9257a1SRanjani Sridharan {
14989c04363dSRanjani Sridharan 	struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
1499a2ba1f70SBard Liao 	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
15006e9257a1SRanjani Sridharan 	struct sof_ipc4_pipeline *pipeline;
15016e9257a1SRanjani Sridharan 	struct sof_ipc4_msg *msg;
15026e9257a1SRanjani Sridharan 	void *ipc_data = NULL;
15036e9257a1SRanjani Sridharan 	u32 ipc_size = 0;
15046e9257a1SRanjani Sridharan 	int ret;
15056e9257a1SRanjani Sridharan 
15066e9257a1SRanjani Sridharan 	switch (swidget->id) {
15076e9257a1SRanjani Sridharan 	case snd_soc_dapm_scheduler:
15086e9257a1SRanjani Sridharan 		pipeline = swidget->private;
15096e9257a1SRanjani Sridharan 
15106e9257a1SRanjani Sridharan 		dev_dbg(sdev->dev, "pipeline: %d memory pages: %d\n", swidget->pipeline_id,
15116e9257a1SRanjani Sridharan 			pipeline->mem_usage);
15126e9257a1SRanjani Sridharan 
15136e9257a1SRanjani Sridharan 		msg = &pipeline->msg;
15146e9257a1SRanjani Sridharan 		msg->primary |= pipeline->mem_usage;
1515a2ba1f70SBard Liao 
1516a2ba1f70SBard Liao 		swidget->instance_id = ida_alloc_max(&pipeline_ida, ipc4_data->max_num_pipelines,
1517a2ba1f70SBard Liao 						     GFP_KERNEL);
1518a2ba1f70SBard Liao 		if (swidget->instance_id < 0) {
1519a2ba1f70SBard Liao 			dev_err(sdev->dev, "failed to assign pipeline id for %s: %d\n",
1520a2ba1f70SBard Liao 				swidget->widget->name, swidget->instance_id);
1521a2ba1f70SBard Liao 			return swidget->instance_id;
1522a2ba1f70SBard Liao 		}
1523a2ba1f70SBard Liao 		msg->primary &= ~SOF_IPC4_GLB_PIPE_INSTANCE_MASK;
1524a2ba1f70SBard Liao 		msg->primary |= SOF_IPC4_GLB_PIPE_INSTANCE_ID(swidget->instance_id);
15256e9257a1SRanjani Sridharan 		break;
15266e9257a1SRanjani Sridharan 	case snd_soc_dapm_aif_in:
15276e9257a1SRanjani Sridharan 	case snd_soc_dapm_aif_out:
15287d573425SBard Liao 	case snd_soc_dapm_buffer:
15296e9257a1SRanjani Sridharan 	{
15306e9257a1SRanjani Sridharan 		struct sof_ipc4_copier *ipc4_copier = swidget->private;
15316e9257a1SRanjani Sridharan 
15326e9257a1SRanjani Sridharan 		ipc_size = ipc4_copier->ipc_config_size;
15336e9257a1SRanjani Sridharan 		ipc_data = ipc4_copier->ipc_config_data;
15346e9257a1SRanjani Sridharan 
15356e9257a1SRanjani Sridharan 		msg = &ipc4_copier->msg;
15366e9257a1SRanjani Sridharan 		break;
15376e9257a1SRanjani Sridharan 	}
15386e9257a1SRanjani Sridharan 	case snd_soc_dapm_dai_in:
15396e9257a1SRanjani Sridharan 	case snd_soc_dapm_dai_out:
15406e9257a1SRanjani Sridharan 	{
15416e9257a1SRanjani Sridharan 		struct snd_sof_dai *dai = swidget->private;
15426e9257a1SRanjani Sridharan 		struct sof_ipc4_copier *ipc4_copier = dai->private;
15436e9257a1SRanjani Sridharan 
15446e9257a1SRanjani Sridharan 		ipc_size = ipc4_copier->ipc_config_size;
15456e9257a1SRanjani Sridharan 		ipc_data = ipc4_copier->ipc_config_data;
15466e9257a1SRanjani Sridharan 
15476e9257a1SRanjani Sridharan 		msg = &ipc4_copier->msg;
15486e9257a1SRanjani Sridharan 		break;
15496e9257a1SRanjani Sridharan 	}
15506e9257a1SRanjani Sridharan 	case snd_soc_dapm_pga:
15516e9257a1SRanjani Sridharan 	{
15526e9257a1SRanjani Sridharan 		struct sof_ipc4_gain *gain = swidget->private;
15536e9257a1SRanjani Sridharan 
15546e9257a1SRanjani Sridharan 		ipc_size = sizeof(struct sof_ipc4_base_module_cfg) +
15556e9257a1SRanjani Sridharan 			   sizeof(struct sof_ipc4_gain_data);
15566e9257a1SRanjani Sridharan 		ipc_data = gain;
15576e9257a1SRanjani Sridharan 
15586e9257a1SRanjani Sridharan 		msg = &gain->msg;
15596e9257a1SRanjani Sridharan 		break;
15606e9257a1SRanjani Sridharan 	}
15616e9257a1SRanjani Sridharan 	case snd_soc_dapm_mixer:
15626e9257a1SRanjani Sridharan 	{
15636e9257a1SRanjani Sridharan 		struct sof_ipc4_mixer *mixer = swidget->private;
15646e9257a1SRanjani Sridharan 
15656e9257a1SRanjani Sridharan 		ipc_size = sizeof(mixer->base_config);
15666e9257a1SRanjani Sridharan 		ipc_data = &mixer->base_config;
15676e9257a1SRanjani Sridharan 
15686e9257a1SRanjani Sridharan 		msg = &mixer->msg;
15696e9257a1SRanjani Sridharan 		break;
15706e9257a1SRanjani Sridharan 	}
1571b85f4fc4SRander Wang 	case snd_soc_dapm_src:
1572b85f4fc4SRander Wang 	{
1573b85f4fc4SRander Wang 		struct sof_ipc4_src *src = swidget->private;
1574b85f4fc4SRander Wang 
1575b85f4fc4SRander Wang 		ipc_size = sizeof(struct sof_ipc4_base_module_cfg) + sizeof(src->sink_rate);
1576b85f4fc4SRander Wang 		ipc_data = src;
1577b85f4fc4SRander Wang 
1578b85f4fc4SRander Wang 		msg = &src->msg;
1579b85f4fc4SRander Wang 		break;
1580b85f4fc4SRander Wang 	}
15816e9257a1SRanjani Sridharan 	default:
15826e9257a1SRanjani Sridharan 		dev_err(sdev->dev, "widget type %d not supported", swidget->id);
15836e9257a1SRanjani Sridharan 		return -EINVAL;
15846e9257a1SRanjani Sridharan 	}
15856e9257a1SRanjani Sridharan 
15866e9257a1SRanjani Sridharan 	if (swidget->id != snd_soc_dapm_scheduler) {
1587711d0427SBard Liao 		ret = sof_ipc4_widget_assign_instance_id(sdev, swidget);
1588711d0427SBard Liao 		if (ret < 0) {
1589711d0427SBard Liao 			dev_err(sdev->dev, "failed to assign instance id for %s\n",
1590711d0427SBard Liao 				swidget->widget->name);
1591711d0427SBard Liao 			return ret;
1592711d0427SBard Liao 		}
15937738211bSPierre-Louis Bossart 
15946e9257a1SRanjani Sridharan 		msg->primary &= ~SOF_IPC4_MOD_INSTANCE_MASK;
15956e9257a1SRanjani Sridharan 		msg->primary |= SOF_IPC4_MOD_INSTANCE(swidget->instance_id);
15966e9257a1SRanjani Sridharan 
15976e9257a1SRanjani Sridharan 		msg->extension &= ~SOF_IPC4_MOD_EXT_PARAM_SIZE_MASK;
15986e9257a1SRanjani Sridharan 		msg->extension |= ipc_size >> 2;
1599a2ba1f70SBard Liao 
1600a2ba1f70SBard Liao 		msg->extension &= ~SOF_IPC4_MOD_EXT_PPL_ID_MASK;
1601a2ba1f70SBard Liao 		msg->extension |= SOF_IPC4_MOD_EXT_PPL_ID(pipe_widget->instance_id);
16026e9257a1SRanjani Sridharan 	}
1603711d0427SBard Liao 	dev_dbg(sdev->dev, "Create widget %s instance %d - pipe %d - core %d\n",
1604711d0427SBard Liao 		swidget->widget->name, swidget->instance_id, swidget->pipeline_id, swidget->core);
16056e9257a1SRanjani Sridharan 
16066e9257a1SRanjani Sridharan 	msg->data_size = ipc_size;
16076e9257a1SRanjani Sridharan 	msg->data_ptr = ipc_data;
16086e9257a1SRanjani Sridharan 
16096e9257a1SRanjani Sridharan 	ret = sof_ipc_tx_message(sdev->ipc, msg, ipc_size, NULL, 0);
161061eb0addSPeter Ujfalusi 	if (ret < 0) {
16116e9257a1SRanjani Sridharan 		dev_err(sdev->dev, "failed to create module %s\n", swidget->widget->name);
16126e9257a1SRanjani Sridharan 
161361eb0addSPeter Ujfalusi 		if (swidget->id != snd_soc_dapm_scheduler) {
161461eb0addSPeter Ujfalusi 			struct sof_ipc4_fw_module *fw_module = swidget->module_info;
161561eb0addSPeter Ujfalusi 
161661eb0addSPeter Ujfalusi 			ida_free(&fw_module->m_ida, swidget->instance_id);
1617a2ba1f70SBard Liao 		} else {
1618a2ba1f70SBard Liao 			ida_free(&pipeline_ida, swidget->instance_id);
161961eb0addSPeter Ujfalusi 		}
162061eb0addSPeter Ujfalusi 	}
162161eb0addSPeter Ujfalusi 
16226e9257a1SRanjani Sridharan 	return ret;
16236e9257a1SRanjani Sridharan }
16246e9257a1SRanjani Sridharan 
16256e9257a1SRanjani Sridharan static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
16266e9257a1SRanjani Sridharan {
1627711d0427SBard Liao 	struct sof_ipc4_fw_module *fw_module = swidget->module_info;
16286bc4d1b7SRanjani Sridharan 	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
16296e9257a1SRanjani Sridharan 	int ret = 0;
16306e9257a1SRanjani Sridharan 
16316bc4d1b7SRanjani Sridharan 	mutex_lock(&ipc4_data->pipeline_state_mutex);
16326bc4d1b7SRanjani Sridharan 
16336e9257a1SRanjani Sridharan 	/* freeing a pipeline frees all the widgets associated with it */
16346e9257a1SRanjani Sridharan 	if (swidget->id == snd_soc_dapm_scheduler) {
16356e9257a1SRanjani Sridharan 		struct sof_ipc4_pipeline *pipeline = swidget->private;
16366e9257a1SRanjani Sridharan 		struct sof_ipc4_msg msg = {{ 0 }};
16376e9257a1SRanjani Sridharan 		u32 header;
16386e9257a1SRanjani Sridharan 
1639a2ba1f70SBard Liao 		header = SOF_IPC4_GLB_PIPE_INSTANCE_ID(swidget->instance_id);
16406e9257a1SRanjani Sridharan 		header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_DELETE_PIPELINE);
16416e9257a1SRanjani Sridharan 		header |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
16426e9257a1SRanjani Sridharan 		header |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG);
16436e9257a1SRanjani Sridharan 
16446e9257a1SRanjani Sridharan 		msg.primary = header;
16456e9257a1SRanjani Sridharan 
16466e9257a1SRanjani Sridharan 		ret = sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0);
16476e9257a1SRanjani Sridharan 		if (ret < 0)
16486e9257a1SRanjani Sridharan 			dev_err(sdev->dev, "failed to free pipeline widget %s\n",
16496e9257a1SRanjani Sridharan 				swidget->widget->name);
16506e9257a1SRanjani Sridharan 
16516e9257a1SRanjani Sridharan 		pipeline->mem_usage = 0;
16526e9257a1SRanjani Sridharan 		pipeline->state = SOF_IPC4_PIPE_UNINITIALIZED;
1653a2ba1f70SBard Liao 		ida_free(&pipeline_ida, swidget->instance_id);
1654711d0427SBard Liao 	} else {
1655711d0427SBard Liao 		ida_free(&fw_module->m_ida, swidget->instance_id);
16566e9257a1SRanjani Sridharan 	}
16576e9257a1SRanjani Sridharan 
16586bc4d1b7SRanjani Sridharan 	mutex_unlock(&ipc4_data->pipeline_state_mutex);
16596bc4d1b7SRanjani Sridharan 
16606e9257a1SRanjani Sridharan 	return ret;
16616e9257a1SRanjani Sridharan }
16626e9257a1SRanjani Sridharan 
1663c84443dbSChao Song static int sof_ipc4_get_queue_id(struct snd_sof_widget *src_widget,
1664c84443dbSChao Song 				 struct snd_sof_widget *sink_widget, bool pin_type)
1665c84443dbSChao Song {
1666c84443dbSChao Song 	struct snd_sof_widget *current_swidget;
1667c84443dbSChao Song 	struct snd_soc_component *scomp;
1668c84443dbSChao Song 	struct ida *queue_ida;
1669c84443dbSChao Song 	const char *buddy_name;
1670c84443dbSChao Song 	char **pin_binding;
1671c84443dbSChao Song 	u32 num_pins;
1672c84443dbSChao Song 	int i;
1673c84443dbSChao Song 
1674c84443dbSChao Song 	if (pin_type == SOF_PIN_TYPE_SOURCE) {
1675c84443dbSChao Song 		current_swidget = src_widget;
1676c84443dbSChao Song 		pin_binding = src_widget->src_pin_binding;
1677c84443dbSChao Song 		queue_ida = &src_widget->src_queue_ida;
1678c84443dbSChao Song 		num_pins = src_widget->num_source_pins;
1679c84443dbSChao Song 		buddy_name = sink_widget->widget->name;
1680c84443dbSChao Song 	} else {
1681c84443dbSChao Song 		current_swidget = sink_widget;
1682c84443dbSChao Song 		pin_binding = sink_widget->sink_pin_binding;
1683c84443dbSChao Song 		queue_ida = &sink_widget->sink_queue_ida;
1684c84443dbSChao Song 		num_pins = sink_widget->num_sink_pins;
1685c84443dbSChao Song 		buddy_name = src_widget->widget->name;
1686c84443dbSChao Song 	}
1687c84443dbSChao Song 
1688c84443dbSChao Song 	scomp = current_swidget->scomp;
1689c84443dbSChao Song 
1690c84443dbSChao Song 	if (num_pins < 1) {
1691c84443dbSChao Song 		dev_err(scomp->dev, "invalid %s num_pins: %d for queue allocation for %s\n",
1692c84443dbSChao Song 			(pin_type == SOF_PIN_TYPE_SOURCE ? "source" : "sink"),
1693c84443dbSChao Song 			num_pins, current_swidget->widget->name);
1694c84443dbSChao Song 		return -EINVAL;
1695c84443dbSChao Song 	}
1696c84443dbSChao Song 
1697c84443dbSChao Song 	/* If there is only one sink/source pin, queue id must be 0 */
1698c84443dbSChao Song 	if (num_pins == 1)
1699c84443dbSChao Song 		return 0;
1700c84443dbSChao Song 
1701c84443dbSChao Song 	/* Allocate queue ID from pin binding array if it is defined in topology. */
1702c84443dbSChao Song 	if (pin_binding) {
1703c84443dbSChao Song 		for (i = 0; i < num_pins; i++) {
1704c84443dbSChao Song 			if (!strcmp(pin_binding[i], buddy_name))
1705c84443dbSChao Song 				return i;
1706c84443dbSChao Song 		}
1707c84443dbSChao Song 		/*
1708c84443dbSChao Song 		 * Fail if no queue ID found from pin binding array, so that we don't
1709c84443dbSChao Song 		 * mixed use pin binding array and ida for queue ID allocation.
1710c84443dbSChao Song 		 */
1711c84443dbSChao Song 		dev_err(scomp->dev, "no %s queue id found from pin binding array for %s\n",
1712c84443dbSChao Song 			(pin_type == SOF_PIN_TYPE_SOURCE ? "source" : "sink"),
1713c84443dbSChao Song 			current_swidget->widget->name);
1714c84443dbSChao Song 		return -EINVAL;
1715c84443dbSChao Song 	}
1716c84443dbSChao Song 
1717c84443dbSChao Song 	/* If no pin binding array specified in topology, use ida to allocate one */
1718c84443dbSChao Song 	return ida_alloc_max(queue_ida, num_pins, GFP_KERNEL);
1719c84443dbSChao Song }
1720c84443dbSChao Song 
1721c84443dbSChao Song static void sof_ipc4_put_queue_id(struct snd_sof_widget *swidget, int queue_id,
1722c84443dbSChao Song 				  bool pin_type)
1723c84443dbSChao Song {
1724c84443dbSChao Song 	struct ida *queue_ida;
1725c84443dbSChao Song 	char **pin_binding;
1726c84443dbSChao Song 	int num_pins;
1727c84443dbSChao Song 
1728c84443dbSChao Song 	if (pin_type == SOF_PIN_TYPE_SOURCE) {
1729c84443dbSChao Song 		pin_binding = swidget->src_pin_binding;
1730c84443dbSChao Song 		queue_ida = &swidget->src_queue_ida;
1731c84443dbSChao Song 		num_pins = swidget->num_source_pins;
1732c84443dbSChao Song 	} else {
1733c84443dbSChao Song 		pin_binding = swidget->sink_pin_binding;
1734c84443dbSChao Song 		queue_ida = &swidget->sink_queue_ida;
1735c84443dbSChao Song 		num_pins = swidget->num_sink_pins;
1736c84443dbSChao Song 	}
1737c84443dbSChao Song 
1738c84443dbSChao Song 	/* Nothing to free if queue ID is not allocated with ida. */
1739c84443dbSChao Song 	if (num_pins == 1 || pin_binding)
1740c84443dbSChao Song 		return;
1741c84443dbSChao Song 
1742c84443dbSChao Song 	ida_free(queue_ida, queue_id);
1743c84443dbSChao Song }
1744c84443dbSChao Song 
1745*11f60563SBard Liao static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev,
1746*11f60563SBard Liao 					   struct snd_sof_widget *src_widget,
1747*11f60563SBard Liao 					   struct snd_sof_widget *sink_widget,
1748*11f60563SBard Liao 					   int sink_id)
1749*11f60563SBard Liao {
1750*11f60563SBard Liao 	struct sof_ipc4_base_module_cfg *sink_config = sink_widget->private;
1751*11f60563SBard Liao 	struct sof_ipc4_base_module_cfg *src_config;
1752*11f60563SBard Liao 	struct sof_ipc4_copier_config_set_sink_format format;
1753*11f60563SBard Liao 	struct sof_ipc4_fw_module *fw_module;
1754*11f60563SBard Liao 	struct sof_ipc4_msg msg = {{ 0 }};
1755*11f60563SBard Liao 	u32 header, extension;
1756*11f60563SBard Liao 
1757*11f60563SBard Liao 	dev_dbg(sdev->dev, "%s set copier sink %d format\n",
1758*11f60563SBard Liao 		src_widget->widget->name, sink_id);
1759*11f60563SBard Liao 
1760*11f60563SBard Liao 	if (WIDGET_IS_DAI(src_widget->id)) {
1761*11f60563SBard Liao 		struct snd_sof_dai *dai = src_widget->private;
1762*11f60563SBard Liao 
1763*11f60563SBard Liao 		src_config = dai->private;
1764*11f60563SBard Liao 	} else {
1765*11f60563SBard Liao 		src_config = src_widget->private;
1766*11f60563SBard Liao 	}
1767*11f60563SBard Liao 
1768*11f60563SBard Liao 	fw_module = src_widget->module_info;
1769*11f60563SBard Liao 
1770*11f60563SBard Liao 	format.sink_id = sink_id;
1771*11f60563SBard Liao 	memcpy(&format.source_fmt, &src_config->audio_fmt, sizeof(format.source_fmt));
1772*11f60563SBard Liao 	memcpy(&format.sink_fmt, &sink_config->audio_fmt, sizeof(format.sink_fmt));
1773*11f60563SBard Liao 	msg.data_size = sizeof(format);
1774*11f60563SBard Liao 	msg.data_ptr = &format;
1775*11f60563SBard Liao 
1776*11f60563SBard Liao 	header = fw_module->man4_module_entry.id;
1777*11f60563SBard Liao 	header |= SOF_IPC4_MOD_INSTANCE(src_widget->instance_id);
1778*11f60563SBard Liao 	header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_SET);
1779*11f60563SBard Liao 	header |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
1780*11f60563SBard Liao 	header |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
1781*11f60563SBard Liao 
1782*11f60563SBard Liao 	extension = SOF_IPC4_MOD_EXT_MSG_SIZE(msg.data_size);
1783*11f60563SBard Liao 	extension |=
1784*11f60563SBard Liao 		SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_COPIER_MODULE_CFG_PARAM_SET_SINK_FORMAT);
1785*11f60563SBard Liao 	extension |= SOF_IPC4_MOD_EXT_MSG_LAST_BLOCK(1);
1786*11f60563SBard Liao 	extension |= SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK(1);
1787*11f60563SBard Liao 
1788*11f60563SBard Liao 	msg.primary = header;
1789*11f60563SBard Liao 	msg.extension = extension;
1790*11f60563SBard Liao 
1791*11f60563SBard Liao 	return sof_ipc_tx_message(sdev->ipc, &msg, msg.data_size, NULL, 0);
1792*11f60563SBard Liao }
1793*11f60563SBard Liao 
17943acd5270SRanjani Sridharan static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *sroute)
17953acd5270SRanjani Sridharan {
17963acd5270SRanjani Sridharan 	struct snd_sof_widget *src_widget = sroute->src_widget;
17973acd5270SRanjani Sridharan 	struct snd_sof_widget *sink_widget = sroute->sink_widget;
17983acd5270SRanjani Sridharan 	struct sof_ipc4_fw_module *src_fw_module = src_widget->module_info;
17993acd5270SRanjani Sridharan 	struct sof_ipc4_fw_module *sink_fw_module = sink_widget->module_info;
18003acd5270SRanjani Sridharan 	struct sof_ipc4_msg msg = {{ 0 }};
18013acd5270SRanjani Sridharan 	u32 header, extension;
18023acd5270SRanjani Sridharan 	int ret;
18033acd5270SRanjani Sridharan 
1804c84443dbSChao Song 	sroute->src_queue_id = sof_ipc4_get_queue_id(src_widget, sink_widget,
1805c84443dbSChao Song 						     SOF_PIN_TYPE_SOURCE);
1806c84443dbSChao Song 	if (sroute->src_queue_id < 0) {
1807c84443dbSChao Song 		dev_err(sdev->dev, "failed to get queue ID for source widget: %s\n",
1808c84443dbSChao Song 			src_widget->widget->name);
1809c84443dbSChao Song 		return sroute->src_queue_id;
1810c84443dbSChao Song 	}
1811c84443dbSChao Song 
1812c84443dbSChao Song 	sroute->dst_queue_id = sof_ipc4_get_queue_id(src_widget, sink_widget,
1813c84443dbSChao Song 						     SOF_PIN_TYPE_SINK);
1814c84443dbSChao Song 	if (sroute->dst_queue_id < 0) {
1815c84443dbSChao Song 		dev_err(sdev->dev, "failed to get queue ID for sink widget: %s\n",
1816c84443dbSChao Song 			sink_widget->widget->name);
1817c84443dbSChao Song 		sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id,
1818c84443dbSChao Song 				      SOF_PIN_TYPE_SOURCE);
1819c84443dbSChao Song 		return sroute->dst_queue_id;
1820c84443dbSChao Song 	}
1821c84443dbSChao Song 
1822*11f60563SBard Liao 	/* Pin 0 format is already set during copier module init */
1823*11f60563SBard Liao 	if (sroute->src_queue_id > 0 && WIDGET_IS_COPIER(src_widget->id)) {
1824*11f60563SBard Liao 		ret = sof_ipc4_set_copier_sink_format(sdev, src_widget, sink_widget,
1825*11f60563SBard Liao 						      sroute->src_queue_id);
1826*11f60563SBard Liao 		if (ret < 0) {
1827*11f60563SBard Liao 			dev_err(sdev->dev, "failed to set sink format for %s source queue ID %d\n",
1828*11f60563SBard Liao 				src_widget->widget->name, sroute->src_queue_id);
1829*11f60563SBard Liao 			goto out;
1830*11f60563SBard Liao 		}
1831*11f60563SBard Liao 	}
1832*11f60563SBard Liao 
1833c84443dbSChao Song 	dev_dbg(sdev->dev, "bind %s:%d -> %s:%d\n",
1834c84443dbSChao Song 		src_widget->widget->name, sroute->src_queue_id,
1835c84443dbSChao Song 		sink_widget->widget->name, sroute->dst_queue_id);
18363acd5270SRanjani Sridharan 
18373acd5270SRanjani Sridharan 	header = src_fw_module->man4_module_entry.id;
18383acd5270SRanjani Sridharan 	header |= SOF_IPC4_MOD_INSTANCE(src_widget->instance_id);
18393acd5270SRanjani Sridharan 	header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_BIND);
18403acd5270SRanjani Sridharan 	header |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
18413acd5270SRanjani Sridharan 	header |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
18423acd5270SRanjani Sridharan 
18433acd5270SRanjani Sridharan 	extension = sink_fw_module->man4_module_entry.id;
18443acd5270SRanjani Sridharan 	extension |= SOF_IPC4_MOD_EXT_DST_MOD_INSTANCE(sink_widget->instance_id);
1845c84443dbSChao Song 	extension |= SOF_IPC4_MOD_EXT_DST_MOD_QUEUE_ID(sroute->dst_queue_id);
1846c84443dbSChao Song 	extension |= SOF_IPC4_MOD_EXT_SRC_MOD_QUEUE_ID(sroute->src_queue_id);
18473acd5270SRanjani Sridharan 
18483acd5270SRanjani Sridharan 	msg.primary = header;
18493acd5270SRanjani Sridharan 	msg.extension = extension;
18503acd5270SRanjani Sridharan 
18513acd5270SRanjani Sridharan 	ret = sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0);
1852c84443dbSChao Song 	if (ret < 0) {
1853b796ff3bSRanjani Sridharan 		dev_err(sdev->dev, "failed to bind modules %s:%d -> %s:%d\n",
1854b796ff3bSRanjani Sridharan 			src_widget->widget->name, sroute->src_queue_id,
1855b796ff3bSRanjani Sridharan 			sink_widget->widget->name, sroute->dst_queue_id);
1856*11f60563SBard Liao 		goto out;
1857c84443dbSChao Song 	}
1858c84443dbSChao Song 
18593acd5270SRanjani Sridharan 	return ret;
1860*11f60563SBard Liao 
1861*11f60563SBard Liao out:
1862*11f60563SBard Liao 	sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_SOURCE);
1863*11f60563SBard Liao 	sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id, SOF_PIN_TYPE_SINK);
1864*11f60563SBard Liao 	return ret;
18653acd5270SRanjani Sridharan }
18663acd5270SRanjani Sridharan 
18673acd5270SRanjani Sridharan static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *sroute)
18683acd5270SRanjani Sridharan {
18693acd5270SRanjani Sridharan 	struct snd_sof_widget *src_widget = sroute->src_widget;
18703acd5270SRanjani Sridharan 	struct snd_sof_widget *sink_widget = sroute->sink_widget;
18713acd5270SRanjani Sridharan 	struct sof_ipc4_fw_module *src_fw_module = src_widget->module_info;
18723acd5270SRanjani Sridharan 	struct sof_ipc4_fw_module *sink_fw_module = sink_widget->module_info;
18733acd5270SRanjani Sridharan 	struct sof_ipc4_msg msg = {{ 0 }};
18743acd5270SRanjani Sridharan 	u32 header, extension;
18759a62d87aSRanjani Sridharan 	int ret = 0;
18763acd5270SRanjani Sridharan 
1877c84443dbSChao Song 	dev_dbg(sdev->dev, "unbind modules %s:%d -> %s:%d\n",
1878c84443dbSChao Song 		src_widget->widget->name, sroute->src_queue_id,
1879c84443dbSChao Song 		sink_widget->widget->name, sroute->dst_queue_id);
18803acd5270SRanjani Sridharan 
18819a62d87aSRanjani Sridharan 	/*
18829a62d87aSRanjani Sridharan 	 * routes belonging to the same pipeline will be disconnected by the FW when the pipeline
18839a62d87aSRanjani Sridharan 	 * is freed. So avoid sending this IPC which will be ignored by the FW anyway.
18849a62d87aSRanjani Sridharan 	 */
18859c04363dSRanjani Sridharan 	if (src_widget->spipe->pipe_widget == sink_widget->spipe->pipe_widget)
18869a62d87aSRanjani Sridharan 		goto out;
18879a62d87aSRanjani Sridharan 
18883acd5270SRanjani Sridharan 	header = src_fw_module->man4_module_entry.id;
18893acd5270SRanjani Sridharan 	header |= SOF_IPC4_MOD_INSTANCE(src_widget->instance_id);
18903acd5270SRanjani Sridharan 	header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_UNBIND);
18913acd5270SRanjani Sridharan 	header |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
18923acd5270SRanjani Sridharan 	header |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
18933acd5270SRanjani Sridharan 
18943acd5270SRanjani Sridharan 	extension = sink_fw_module->man4_module_entry.id;
18953acd5270SRanjani Sridharan 	extension |= SOF_IPC4_MOD_EXT_DST_MOD_INSTANCE(sink_widget->instance_id);
1896c84443dbSChao Song 	extension |= SOF_IPC4_MOD_EXT_DST_MOD_QUEUE_ID(sroute->dst_queue_id);
1897c84443dbSChao Song 	extension |= SOF_IPC4_MOD_EXT_SRC_MOD_QUEUE_ID(sroute->src_queue_id);
18983acd5270SRanjani Sridharan 
18993acd5270SRanjani Sridharan 	msg.primary = header;
19003acd5270SRanjani Sridharan 	msg.extension = extension;
19013acd5270SRanjani Sridharan 
19023acd5270SRanjani Sridharan 	ret = sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0);
19033acd5270SRanjani Sridharan 	if (ret < 0)
1904b796ff3bSRanjani Sridharan 		dev_err(sdev->dev, "failed to unbind modules %s:%d -> %s:%d\n",
1905b796ff3bSRanjani Sridharan 			src_widget->widget->name, sroute->src_queue_id,
1906b796ff3bSRanjani Sridharan 			sink_widget->widget->name, sroute->dst_queue_id);
19079a62d87aSRanjani Sridharan out:
1908c84443dbSChao Song 	sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id, SOF_PIN_TYPE_SINK);
1909c84443dbSChao Song 	sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_SOURCE);
1910c84443dbSChao Song 
19113acd5270SRanjani Sridharan 	return ret;
19123acd5270SRanjani Sridharan }
19133acd5270SRanjani Sridharan 
1914acf48a1fSRanjani Sridharan static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,
1915acf48a1fSRanjani Sridharan 			       unsigned int flags, struct snd_sof_dai_config_data *data)
1916acf48a1fSRanjani Sridharan {
19179c04363dSRanjani Sridharan 	struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
1918acf48a1fSRanjani Sridharan 	struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
1919acf48a1fSRanjani Sridharan 	struct snd_sof_dai *dai = swidget->private;
1920acf48a1fSRanjani Sridharan 	struct sof_ipc4_gtw_attributes *gtw_attr;
1921acf48a1fSRanjani Sridharan 	struct sof_ipc4_copier_data *copier_data;
1922acf48a1fSRanjani Sridharan 	struct sof_ipc4_copier *ipc4_copier;
1923acf48a1fSRanjani Sridharan 
1924acf48a1fSRanjani Sridharan 	if (!dai || !dai->private) {
1925acf48a1fSRanjani Sridharan 		dev_err(sdev->dev, "Invalid DAI or DAI private data for %s\n",
1926acf48a1fSRanjani Sridharan 			swidget->widget->name);
1927acf48a1fSRanjani Sridharan 		return -EINVAL;
1928acf48a1fSRanjani Sridharan 	}
1929acf48a1fSRanjani Sridharan 
1930acf48a1fSRanjani Sridharan 	ipc4_copier = (struct sof_ipc4_copier *)dai->private;
1931acf48a1fSRanjani Sridharan 	copier_data = &ipc4_copier->data;
1932acf48a1fSRanjani Sridharan 
1933acf48a1fSRanjani Sridharan 	if (!data)
1934acf48a1fSRanjani Sridharan 		return 0;
1935acf48a1fSRanjani Sridharan 
1936acf48a1fSRanjani Sridharan 	switch (ipc4_copier->dai_type) {
1937acf48a1fSRanjani Sridharan 	case SOF_DAI_INTEL_HDA:
1938acf48a1fSRanjani Sridharan 		gtw_attr = ipc4_copier->gtw_attr;
1939acf48a1fSRanjani Sridharan 		gtw_attr->lp_buffer_alloc = pipeline->lp_mode;
194037a26eecSRanjani Sridharan 		pipeline->skip_during_fe_trigger = true;
1941acf48a1fSRanjani Sridharan 		fallthrough;
1942acf48a1fSRanjani Sridharan 	case SOF_DAI_INTEL_ALH:
1943acf48a1fSRanjani Sridharan 		copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
1944acf48a1fSRanjani Sridharan 		copier_data->gtw_cfg.node_id |= SOF_IPC4_NODE_INDEX(data->dai_data);
1945acf48a1fSRanjani Sridharan 		break;
1946acf48a1fSRanjani Sridharan 	case SOF_DAI_INTEL_DMIC:
1947acf48a1fSRanjani Sridharan 	case SOF_DAI_INTEL_SSP:
1948acf48a1fSRanjani Sridharan 		/* nothing to do for SSP/DMIC */
1949acf48a1fSRanjani Sridharan 		break;
1950acf48a1fSRanjani Sridharan 	default:
1951acf48a1fSRanjani Sridharan 		dev_err(sdev->dev, "%s: unsupported dai type %d\n", __func__,
1952acf48a1fSRanjani Sridharan 			ipc4_copier->dai_type);
1953acf48a1fSRanjani Sridharan 		return -EINVAL;
1954acf48a1fSRanjani Sridharan 	}
1955acf48a1fSRanjani Sridharan 
1956acf48a1fSRanjani Sridharan 	return 0;
1957acf48a1fSRanjani Sridharan }
1958acf48a1fSRanjani Sridharan 
1959323aa1f0SRanjani Sridharan static int sof_ipc4_parse_manifest(struct snd_soc_component *scomp, int index,
1960323aa1f0SRanjani Sridharan 				   struct snd_soc_tplg_manifest *man)
1961323aa1f0SRanjani Sridharan {
1962323aa1f0SRanjani Sridharan 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
1963323aa1f0SRanjani Sridharan 	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
1964323aa1f0SRanjani Sridharan 	struct sof_manifest_tlv *manifest_tlv;
1965323aa1f0SRanjani Sridharan 	struct sof_manifest *manifest;
1966323aa1f0SRanjani Sridharan 	u32 size = le32_to_cpu(man->priv.size);
1967323aa1f0SRanjani Sridharan 	u8 *man_ptr = man->priv.data;
1968323aa1f0SRanjani Sridharan 	u32 len_check;
1969323aa1f0SRanjani Sridharan 	int i;
1970323aa1f0SRanjani Sridharan 
1971323aa1f0SRanjani Sridharan 	if (!size || size < SOF_IPC4_TPLG_ABI_SIZE) {
1972323aa1f0SRanjani Sridharan 		dev_err(scomp->dev, "%s: Invalid topology ABI size: %u\n",
1973323aa1f0SRanjani Sridharan 			__func__, size);
1974323aa1f0SRanjani Sridharan 		return -EINVAL;
1975323aa1f0SRanjani Sridharan 	}
1976323aa1f0SRanjani Sridharan 
1977323aa1f0SRanjani Sridharan 	manifest = (struct sof_manifest *)man_ptr;
1978323aa1f0SRanjani Sridharan 
1979323aa1f0SRanjani Sridharan 	dev_info(scomp->dev,
1980323aa1f0SRanjani Sridharan 		 "Topology: ABI %d:%d:%d Kernel ABI %u:%u:%u\n",
1981323aa1f0SRanjani Sridharan 		  le16_to_cpu(manifest->abi_major), le16_to_cpu(manifest->abi_minor),
1982323aa1f0SRanjani Sridharan 		  le16_to_cpu(manifest->abi_patch),
1983323aa1f0SRanjani Sridharan 		  SOF_ABI_MAJOR, SOF_ABI_MINOR, SOF_ABI_PATCH);
1984323aa1f0SRanjani Sridharan 
1985323aa1f0SRanjani Sridharan 	/* TODO: Add ABI compatibility check */
1986323aa1f0SRanjani Sridharan 
1987323aa1f0SRanjani Sridharan 	/* no more data after the ABI version */
1988323aa1f0SRanjani Sridharan 	if (size <= SOF_IPC4_TPLG_ABI_SIZE)
1989323aa1f0SRanjani Sridharan 		return 0;
1990323aa1f0SRanjani Sridharan 
1991323aa1f0SRanjani Sridharan 	manifest_tlv = manifest->items;
1992323aa1f0SRanjani Sridharan 	len_check = sizeof(struct sof_manifest);
1993323aa1f0SRanjani Sridharan 	for (i = 0; i < le16_to_cpu(manifest->count); i++) {
1994323aa1f0SRanjani Sridharan 		len_check += sizeof(struct sof_manifest_tlv) + le32_to_cpu(manifest_tlv->size);
1995323aa1f0SRanjani Sridharan 		if (len_check > size)
1996323aa1f0SRanjani Sridharan 			return -EINVAL;
1997323aa1f0SRanjani Sridharan 
1998323aa1f0SRanjani Sridharan 		switch (le32_to_cpu(manifest_tlv->type)) {
1999323aa1f0SRanjani Sridharan 		case SOF_MANIFEST_DATA_TYPE_NHLT:
2000323aa1f0SRanjani Sridharan 			/* no NHLT in BIOS, so use the one from topology manifest */
2001323aa1f0SRanjani Sridharan 			if (ipc4_data->nhlt)
2002323aa1f0SRanjani Sridharan 				break;
2003323aa1f0SRanjani Sridharan 			ipc4_data->nhlt = devm_kmemdup(sdev->dev, manifest_tlv->data,
2004323aa1f0SRanjani Sridharan 						       le32_to_cpu(manifest_tlv->size), GFP_KERNEL);
2005323aa1f0SRanjani Sridharan 			if (!ipc4_data->nhlt)
2006323aa1f0SRanjani Sridharan 				return -ENOMEM;
2007323aa1f0SRanjani Sridharan 			break;
2008323aa1f0SRanjani Sridharan 		default:
2009323aa1f0SRanjani Sridharan 			dev_warn(scomp->dev, "Skipping unknown manifest data type %d\n",
2010323aa1f0SRanjani Sridharan 				 manifest_tlv->type);
2011323aa1f0SRanjani Sridharan 			break;
2012323aa1f0SRanjani Sridharan 		}
2013323aa1f0SRanjani Sridharan 		man_ptr += sizeof(struct sof_manifest_tlv) + le32_to_cpu(manifest_tlv->size);
2014323aa1f0SRanjani Sridharan 		manifest_tlv = (struct sof_manifest_tlv *)man_ptr;
2015323aa1f0SRanjani Sridharan 	}
2016323aa1f0SRanjani Sridharan 
2017323aa1f0SRanjani Sridharan 	return 0;
2018323aa1f0SRanjani Sridharan }
2019323aa1f0SRanjani Sridharan 
20209e2b5d33SRanjani Sridharan static int sof_ipc4_dai_get_clk(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int clk_type)
20219e2b5d33SRanjani Sridharan {
20229e2b5d33SRanjani Sridharan 	struct sof_ipc4_copier *ipc4_copier = dai->private;
20239e2b5d33SRanjani Sridharan 	struct snd_soc_tplg_hw_config *hw_config;
20249e2b5d33SRanjani Sridharan 	struct snd_sof_dai_link *slink;
20259e2b5d33SRanjani Sridharan 	bool dai_link_found = false;
20269e2b5d33SRanjani Sridharan 	bool hw_cfg_found = false;
20279e2b5d33SRanjani Sridharan 	int i;
20289e2b5d33SRanjani Sridharan 
20299e2b5d33SRanjani Sridharan 	if (!ipc4_copier)
20309e2b5d33SRanjani Sridharan 		return 0;
20319e2b5d33SRanjani Sridharan 
20329e2b5d33SRanjani Sridharan 	list_for_each_entry(slink, &sdev->dai_link_list, list) {
20339e2b5d33SRanjani Sridharan 		if (!strcmp(slink->link->name, dai->name)) {
20349e2b5d33SRanjani Sridharan 			dai_link_found = true;
20359e2b5d33SRanjani Sridharan 			break;
20369e2b5d33SRanjani Sridharan 		}
20379e2b5d33SRanjani Sridharan 	}
20389e2b5d33SRanjani Sridharan 
20399e2b5d33SRanjani Sridharan 	if (!dai_link_found) {
20409e2b5d33SRanjani Sridharan 		dev_err(sdev->dev, "no DAI link found for DAI %s\n", dai->name);
20419e2b5d33SRanjani Sridharan 		return -EINVAL;
20429e2b5d33SRanjani Sridharan 	}
20439e2b5d33SRanjani Sridharan 
20449e2b5d33SRanjani Sridharan 	for (i = 0; i < slink->num_hw_configs; i++) {
20459e2b5d33SRanjani Sridharan 		hw_config = &slink->hw_configs[i];
20469e2b5d33SRanjani Sridharan 		if (dai->current_config == le32_to_cpu(hw_config->id)) {
20479e2b5d33SRanjani Sridharan 			hw_cfg_found = true;
20489e2b5d33SRanjani Sridharan 			break;
20499e2b5d33SRanjani Sridharan 		}
20509e2b5d33SRanjani Sridharan 	}
20519e2b5d33SRanjani Sridharan 
20529e2b5d33SRanjani Sridharan 	if (!hw_cfg_found) {
20539e2b5d33SRanjani Sridharan 		dev_err(sdev->dev, "no matching hw_config found for DAI %s\n", dai->name);
20549e2b5d33SRanjani Sridharan 		return -EINVAL;
20559e2b5d33SRanjani Sridharan 	}
20569e2b5d33SRanjani Sridharan 
20579e2b5d33SRanjani Sridharan 	switch (ipc4_copier->dai_type) {
20589e2b5d33SRanjani Sridharan 	case SOF_DAI_INTEL_SSP:
20599e2b5d33SRanjani Sridharan 		switch (clk_type) {
20609e2b5d33SRanjani Sridharan 		case SOF_DAI_CLK_INTEL_SSP_MCLK:
20619e2b5d33SRanjani Sridharan 			return le32_to_cpu(hw_config->mclk_rate);
20629e2b5d33SRanjani Sridharan 		case SOF_DAI_CLK_INTEL_SSP_BCLK:
20639e2b5d33SRanjani Sridharan 			return le32_to_cpu(hw_config->bclk_rate);
20649e2b5d33SRanjani Sridharan 		default:
20659e2b5d33SRanjani Sridharan 			dev_err(sdev->dev, "Invalid clk type for SSP %d\n", clk_type);
20669e2b5d33SRanjani Sridharan 			break;
20679e2b5d33SRanjani Sridharan 		}
20689e2b5d33SRanjani Sridharan 		break;
20699e2b5d33SRanjani Sridharan 	default:
20709e2b5d33SRanjani Sridharan 		dev_err(sdev->dev, "DAI type %d not supported yet!\n", ipc4_copier->dai_type);
20719e2b5d33SRanjani Sridharan 		break;
20729e2b5d33SRanjani Sridharan 	}
20739e2b5d33SRanjani Sridharan 
20749e2b5d33SRanjani Sridharan 	return -EINVAL;
20759e2b5d33SRanjani Sridharan }
20769e2b5d33SRanjani Sridharan 
207718cd1f32SPeter Ujfalusi static int sof_ipc4_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verify)
207818cd1f32SPeter Ujfalusi {
207918cd1f32SPeter Ujfalusi 	struct snd_sof_pcm *spcm;
208018cd1f32SPeter Ujfalusi 	int dir, ret;
208118cd1f32SPeter Ujfalusi 
208218cd1f32SPeter Ujfalusi 	/*
208318cd1f32SPeter Ujfalusi 	 * This function is called during system suspend, we need to make sure
208418cd1f32SPeter Ujfalusi 	 * that all streams have been freed up.
208518cd1f32SPeter Ujfalusi 	 * Freeing might have been skipped when xrun happened just at the start
208618cd1f32SPeter Ujfalusi 	 * of the suspend and it sent a SNDRV_PCM_TRIGGER_STOP to the active
208718cd1f32SPeter Ujfalusi 	 * stream. This will call sof_pcm_stream_free() with
208818cd1f32SPeter Ujfalusi 	 * free_widget_list = false which will leave the kernel and firmware out
208918cd1f32SPeter Ujfalusi 	 * of sync during suspend/resume.
209018cd1f32SPeter Ujfalusi 	 *
209118cd1f32SPeter Ujfalusi 	 * This will also make sure that paused streams handled correctly.
209218cd1f32SPeter Ujfalusi 	 */
209318cd1f32SPeter Ujfalusi 	list_for_each_entry(spcm, &sdev->pcm_list, list) {
209418cd1f32SPeter Ujfalusi 		for_each_pcm_streams(dir) {
209518cd1f32SPeter Ujfalusi 			struct snd_pcm_substream *substream = spcm->stream[dir].substream;
209618cd1f32SPeter Ujfalusi 
209782b18242SRanjani Sridharan 			if (!substream || !substream->runtime || spcm->stream[dir].suspend_ignored)
209818cd1f32SPeter Ujfalusi 				continue;
209918cd1f32SPeter Ujfalusi 
210018cd1f32SPeter Ujfalusi 			if (spcm->stream[dir].list) {
210118cd1f32SPeter Ujfalusi 				ret = sof_pcm_stream_free(sdev, substream, spcm, dir, true);
210218cd1f32SPeter Ujfalusi 				if (ret < 0)
210318cd1f32SPeter Ujfalusi 					return ret;
210418cd1f32SPeter Ujfalusi 			}
210518cd1f32SPeter Ujfalusi 		}
210618cd1f32SPeter Ujfalusi 	}
210718cd1f32SPeter Ujfalusi 	return 0;
210818cd1f32SPeter Ujfalusi }
210918cd1f32SPeter Ujfalusi 
2110e380c907SRanjani Sridharan static int sof_ipc4_link_setup(struct snd_sof_dev *sdev, struct snd_soc_dai_link *link)
2111e380c907SRanjani Sridharan {
2112e380c907SRanjani Sridharan 	if (link->no_pcm)
2113e380c907SRanjani Sridharan 		return 0;
2114e380c907SRanjani Sridharan 
2115e380c907SRanjani Sridharan 	/*
2116e380c907SRanjani Sridharan 	 * set default trigger order for all links. Exceptions to
2117e380c907SRanjani Sridharan 	 * the rule will be handled in sof_pcm_dai_link_fixup()
2118e380c907SRanjani Sridharan 	 * For playback, the sequence is the following: start BE,
2119e380c907SRanjani Sridharan 	 * start FE, stop FE, stop BE; for Capture the sequence is
2120e380c907SRanjani Sridharan 	 * inverted start FE, start BE, stop BE, stop FE
2121e380c907SRanjani Sridharan 	 */
2122e380c907SRanjani Sridharan 	link->trigger[SNDRV_PCM_STREAM_PLAYBACK] = SND_SOC_DPCM_TRIGGER_POST;
2123e380c907SRanjani Sridharan 	link->trigger[SNDRV_PCM_STREAM_CAPTURE] = SND_SOC_DPCM_TRIGGER_PRE;
2124e380c907SRanjani Sridharan 
2125e380c907SRanjani Sridharan 	return 0;
2126e380c907SRanjani Sridharan }
2127e380c907SRanjani Sridharan 
21287d573425SBard Liao static enum sof_tokens common_copier_token_list[] = {
21292cabd02bSRanjani Sridharan 	SOF_COMP_TOKENS,
21302cabd02bSRanjani Sridharan 	SOF_AUDIO_FMT_NUM_TOKENS,
21312cabd02bSRanjani Sridharan 	SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS,
21322cabd02bSRanjani Sridharan 	SOF_IN_AUDIO_FORMAT_TOKENS,
21332cabd02bSRanjani Sridharan 	SOF_OUT_AUDIO_FORMAT_TOKENS,
21342cabd02bSRanjani Sridharan 	SOF_COPIER_GATEWAY_CFG_TOKENS,
21352cabd02bSRanjani Sridharan 	SOF_COPIER_TOKENS,
21362cabd02bSRanjani Sridharan 	SOF_COMP_EXT_TOKENS,
21372cabd02bSRanjani Sridharan };
21382cabd02bSRanjani Sridharan 
213990e89155SRanjani Sridharan static enum sof_tokens pipeline_token_list[] = {
214090e89155SRanjani Sridharan 	SOF_SCHED_TOKENS,
214190e89155SRanjani Sridharan 	SOF_PIPELINE_TOKENS,
214290e89155SRanjani Sridharan };
214390e89155SRanjani Sridharan 
2144abfb536bSRanjani Sridharan static enum sof_tokens dai_token_list[] = {
2145abfb536bSRanjani Sridharan 	SOF_COMP_TOKENS,
2146abfb536bSRanjani Sridharan 	SOF_AUDIO_FMT_NUM_TOKENS,
2147abfb536bSRanjani Sridharan 	SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS,
2148abfb536bSRanjani Sridharan 	SOF_IN_AUDIO_FORMAT_TOKENS,
2149abfb536bSRanjani Sridharan 	SOF_OUT_AUDIO_FORMAT_TOKENS,
2150abfb536bSRanjani Sridharan 	SOF_COPIER_GATEWAY_CFG_TOKENS,
2151abfb536bSRanjani Sridharan 	SOF_COPIER_TOKENS,
2152abfb536bSRanjani Sridharan 	SOF_DAI_TOKENS,
2153abfb536bSRanjani Sridharan 	SOF_COMP_EXT_TOKENS,
2154abfb536bSRanjani Sridharan };
2155abfb536bSRanjani Sridharan 
21564f838ab2SRanjani Sridharan static enum sof_tokens pga_token_list[] = {
21574f838ab2SRanjani Sridharan 	SOF_COMP_TOKENS,
21584f838ab2SRanjani Sridharan 	SOF_GAIN_TOKENS,
21594f838ab2SRanjani Sridharan 	SOF_AUDIO_FMT_NUM_TOKENS,
21604f838ab2SRanjani Sridharan 	SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS,
21614f838ab2SRanjani Sridharan 	SOF_IN_AUDIO_FORMAT_TOKENS,
21624f838ab2SRanjani Sridharan 	SOF_COMP_EXT_TOKENS,
21634f838ab2SRanjani Sridharan };
21644f838ab2SRanjani Sridharan 
21654d4ba014SRanjani Sridharan static enum sof_tokens mixer_token_list[] = {
21664d4ba014SRanjani Sridharan 	SOF_COMP_TOKENS,
21674d4ba014SRanjani Sridharan 	SOF_AUDIO_FMT_NUM_TOKENS,
21684d4ba014SRanjani Sridharan 	SOF_IN_AUDIO_FORMAT_TOKENS,
21694d4ba014SRanjani Sridharan 	SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS,
21704d4ba014SRanjani Sridharan 	SOF_COMP_EXT_TOKENS,
21714d4ba014SRanjani Sridharan };
21724d4ba014SRanjani Sridharan 
2173b85f4fc4SRander Wang static enum sof_tokens src_token_list[] = {
2174b85f4fc4SRander Wang 	SOF_COMP_TOKENS,
2175b85f4fc4SRander Wang 	SOF_SRC_TOKENS,
2176b85f4fc4SRander Wang 	SOF_AUDIO_FMT_NUM_TOKENS,
2177b85f4fc4SRander Wang 	SOF_IN_AUDIO_FORMAT_TOKENS,
2178b85f4fc4SRander Wang 	SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS,
2179b85f4fc4SRander Wang 	SOF_COMP_EXT_TOKENS,
2180b85f4fc4SRander Wang };
2181b85f4fc4SRander Wang 
218290e89155SRanjani Sridharan static const struct sof_ipc_tplg_widget_ops tplg_ipc4_widget_ops[SND_SOC_DAPM_TYPE_COUNT] = {
21832cabd02bSRanjani Sridharan 	[snd_soc_dapm_aif_in] =  {sof_ipc4_widget_setup_pcm, sof_ipc4_widget_free_comp_pcm,
21847d573425SBard Liao 				  common_copier_token_list, ARRAY_SIZE(common_copier_token_list),
21857d573425SBard Liao 				  NULL, sof_ipc4_prepare_copier_module,
2186904c48c4SRanjani Sridharan 				  sof_ipc4_unprepare_copier_module},
21872cabd02bSRanjani Sridharan 	[snd_soc_dapm_aif_out] = {sof_ipc4_widget_setup_pcm, sof_ipc4_widget_free_comp_pcm,
21887d573425SBard Liao 				  common_copier_token_list, ARRAY_SIZE(common_copier_token_list),
21897d573425SBard Liao 				  NULL, sof_ipc4_prepare_copier_module,
2190904c48c4SRanjani Sridharan 				  sof_ipc4_unprepare_copier_module},
2191abfb536bSRanjani Sridharan 	[snd_soc_dapm_dai_in] = {sof_ipc4_widget_setup_comp_dai, sof_ipc4_widget_free_comp_dai,
2192acf52594SRanjani Sridharan 				 dai_token_list, ARRAY_SIZE(dai_token_list), NULL,
2193acf52594SRanjani Sridharan 				 sof_ipc4_prepare_copier_module,
2194acf52594SRanjani Sridharan 				 sof_ipc4_unprepare_copier_module},
2195abfb536bSRanjani Sridharan 	[snd_soc_dapm_dai_out] = {sof_ipc4_widget_setup_comp_dai, sof_ipc4_widget_free_comp_dai,
2196acf52594SRanjani Sridharan 				  dai_token_list, ARRAY_SIZE(dai_token_list), NULL,
2197acf52594SRanjani Sridharan 				  sof_ipc4_prepare_copier_module,
2198acf52594SRanjani Sridharan 				  sof_ipc4_unprepare_copier_module},
21997d573425SBard Liao 	[snd_soc_dapm_buffer] = {sof_ipc4_widget_setup_pcm, sof_ipc4_widget_free_comp_pcm,
22007d573425SBard Liao 				 common_copier_token_list, ARRAY_SIZE(common_copier_token_list),
22017d573425SBard Liao 				 NULL, sof_ipc4_prepare_copier_module,
22027d573425SBard Liao 				 sof_ipc4_unprepare_copier_module},
2203a29b2d02SBard Liao 	[snd_soc_dapm_scheduler] = {sof_ipc4_widget_setup_comp_pipeline,
2204a29b2d02SBard Liao 				    sof_ipc4_widget_free_comp_pipeline,
220590e89155SRanjani Sridharan 				    pipeline_token_list, ARRAY_SIZE(pipeline_token_list), NULL,
220690e89155SRanjani Sridharan 				    NULL, NULL},
2207dc4fc0aeSLibin Yang 	[snd_soc_dapm_pga] = {sof_ipc4_widget_setup_comp_pga, sof_ipc4_widget_free_comp_pga,
22084f838ab2SRanjani Sridharan 			      pga_token_list, ARRAY_SIZE(pga_token_list), NULL,
22094f838ab2SRanjani Sridharan 			      sof_ipc4_prepare_gain_module,
2210711d0427SBard Liao 			      NULL},
2211dc4fc0aeSLibin Yang 	[snd_soc_dapm_mixer] = {sof_ipc4_widget_setup_comp_mixer, sof_ipc4_widget_free_comp_mixer,
22124d4ba014SRanjani Sridharan 				mixer_token_list, ARRAY_SIZE(mixer_token_list),
22134d4ba014SRanjani Sridharan 				NULL, sof_ipc4_prepare_mixer_module,
2214711d0427SBard Liao 				NULL},
2215b85f4fc4SRander Wang 	[snd_soc_dapm_src] = {sof_ipc4_widget_setup_comp_src, sof_ipc4_widget_free_comp_src,
2216b85f4fc4SRander Wang 				src_token_list, ARRAY_SIZE(src_token_list),
2217b85f4fc4SRander Wang 				NULL, sof_ipc4_prepare_src_module,
2218b85f4fc4SRander Wang 				NULL},
221990e89155SRanjani Sridharan };
222090e89155SRanjani Sridharan 
222190e89155SRanjani Sridharan const struct sof_ipc_tplg_ops ipc4_tplg_ops = {
222290e89155SRanjani Sridharan 	.widget = tplg_ipc4_widget_ops,
222390e89155SRanjani Sridharan 	.token_list = ipc4_token_list,
2224d97964f8SRanjani Sridharan 	.control_setup = sof_ipc4_control_setup,
2225955e84fcSRanjani Sridharan 	.control = &tplg_ipc4_control_ops,
22266e9257a1SRanjani Sridharan 	.widget_setup = sof_ipc4_widget_setup,
22276e9257a1SRanjani Sridharan 	.widget_free = sof_ipc4_widget_free,
22283acd5270SRanjani Sridharan 	.route_setup = sof_ipc4_route_setup,
22293acd5270SRanjani Sridharan 	.route_free = sof_ipc4_route_free,
2230acf48a1fSRanjani Sridharan 	.dai_config = sof_ipc4_dai_config,
2231323aa1f0SRanjani Sridharan 	.parse_manifest = sof_ipc4_parse_manifest,
22329e2b5d33SRanjani Sridharan 	.dai_get_clk = sof_ipc4_dai_get_clk,
223318cd1f32SPeter Ujfalusi 	.tear_down_all_pipelines = sof_ipc4_tear_down_all_pipelines,
2234e380c907SRanjani Sridharan 	.link_setup = sof_ipc4_link_setup,
223590e89155SRanjani Sridharan };
2236