xref: /openbmc/linux/sound/soc/sof/ipc4-topology.c (revision de6aa72b265b72bca2b1897d5000c8f0147d3157)
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,
110e45cd86cSRander Wang 		offsetof(struct sof_ipc4_gain_data, curve_duration_l)},
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,
1589e269e3aSSeppo Ingalsuo 			" #%d: %uHz, %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",
695e45cd86cSRander Wang 		swidget->widget->name, gain->data.curve_type, gain->data.curve_duration_l,
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) {
983b66bfc3aSRanjani Sridharan 			struct sof_ipc4_copier_data *copier_data = &ipc4_copier->data;
984a150345aSBard Liao 			struct sof_ipc4_alh_configuration_blob *blob;
985a150345aSBard Liao 			unsigned int group_id;
986a150345aSBard Liao 
987a150345aSBard Liao 			blob = (struct sof_ipc4_alh_configuration_blob *)ipc4_copier->copier_config;
988a150345aSBard Liao 			if (blob->alh_cfg.count > 1) {
989a150345aSBard Liao 				group_id = SOF_IPC4_NODE_INDEX(ipc4_copier->data.gtw_cfg.node_id) -
990a150345aSBard Liao 					   ALH_MULTI_GTW_BASE;
991a150345aSBard Liao 				ida_free(&alh_group_ida, group_id);
992a150345aSBard Liao 			}
993b66bfc3aSRanjani Sridharan 
994b66bfc3aSRanjani Sridharan 			/* clear the node ID */
995b66bfc3aSRanjani Sridharan 			copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
996a150345aSBard Liao 		}
997acf52594SRanjani Sridharan 	}
998904c48c4SRanjani Sridharan 
999904c48c4SRanjani Sridharan 	if (ipc4_copier) {
1000904c48c4SRanjani Sridharan 		kfree(ipc4_copier->ipc_config_data);
1001904c48c4SRanjani Sridharan 		ipc4_copier->ipc_config_data = NULL;
1002904c48c4SRanjani Sridharan 		ipc4_copier->ipc_config_size = 0;
1003904c48c4SRanjani Sridharan 	}
1004904c48c4SRanjani Sridharan }
1005904c48c4SRanjani Sridharan 
1006aa84ffb7SRanjani Sridharan #if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_SND_INTEL_NHLT)
1007aa84ffb7SRanjani Sridharan static int snd_sof_get_hw_config_params(struct snd_sof_dev *sdev, struct snd_sof_dai *dai,
1008aa84ffb7SRanjani Sridharan 					int *sample_rate, int *channel_count, int *bit_depth)
1009aa84ffb7SRanjani Sridharan {
1010aa84ffb7SRanjani Sridharan 	struct snd_soc_tplg_hw_config *hw_config;
1011aa84ffb7SRanjani Sridharan 	struct snd_sof_dai_link *slink;
1012aa84ffb7SRanjani Sridharan 	bool dai_link_found = false;
1013aa84ffb7SRanjani Sridharan 	bool hw_cfg_found = false;
1014aa84ffb7SRanjani Sridharan 	int i;
1015aa84ffb7SRanjani Sridharan 
1016aa84ffb7SRanjani Sridharan 	/* get current hw_config from link */
1017aa84ffb7SRanjani Sridharan 	list_for_each_entry(slink, &sdev->dai_link_list, list) {
1018aa84ffb7SRanjani Sridharan 		if (!strcmp(slink->link->name, dai->name)) {
1019aa84ffb7SRanjani Sridharan 			dai_link_found = true;
1020aa84ffb7SRanjani Sridharan 			break;
1021aa84ffb7SRanjani Sridharan 		}
1022aa84ffb7SRanjani Sridharan 	}
1023aa84ffb7SRanjani Sridharan 
1024aa84ffb7SRanjani Sridharan 	if (!dai_link_found) {
1025aa84ffb7SRanjani Sridharan 		dev_err(sdev->dev, "%s: no DAI link found for DAI %s\n", __func__, dai->name);
1026aa84ffb7SRanjani Sridharan 		return -EINVAL;
1027aa84ffb7SRanjani Sridharan 	}
1028aa84ffb7SRanjani Sridharan 
1029aa84ffb7SRanjani Sridharan 	for (i = 0; i < slink->num_hw_configs; i++) {
1030aa84ffb7SRanjani Sridharan 		hw_config = &slink->hw_configs[i];
1031aa84ffb7SRanjani Sridharan 		if (dai->current_config == le32_to_cpu(hw_config->id)) {
1032aa84ffb7SRanjani Sridharan 			hw_cfg_found = true;
1033aa84ffb7SRanjani Sridharan 			break;
1034aa84ffb7SRanjani Sridharan 		}
1035aa84ffb7SRanjani Sridharan 	}
1036aa84ffb7SRanjani Sridharan 
1037aa84ffb7SRanjani Sridharan 	if (!hw_cfg_found) {
1038aa84ffb7SRanjani Sridharan 		dev_err(sdev->dev, "%s: no matching hw_config found for DAI %s\n", __func__,
1039aa84ffb7SRanjani Sridharan 			dai->name);
1040aa84ffb7SRanjani Sridharan 		return -EINVAL;
1041aa84ffb7SRanjani Sridharan 	}
1042aa84ffb7SRanjani Sridharan 
1043aa84ffb7SRanjani Sridharan 	*bit_depth = le32_to_cpu(hw_config->tdm_slot_width);
1044aa84ffb7SRanjani Sridharan 	*channel_count = le32_to_cpu(hw_config->tdm_slots);
1045aa84ffb7SRanjani Sridharan 	*sample_rate = le32_to_cpu(hw_config->fsync_rate);
1046aa84ffb7SRanjani Sridharan 
10473809264bSPierre-Louis Bossart 	dev_dbg(sdev->dev, "sample rate: %d sample width: %d channels: %d\n",
10483809264bSPierre-Louis Bossart 		*sample_rate, *bit_depth, *channel_count);
1049aa84ffb7SRanjani Sridharan 
1050aa84ffb7SRanjani Sridharan 	return 0;
1051aa84ffb7SRanjani Sridharan }
1052aa84ffb7SRanjani Sridharan 
1053aa84ffb7SRanjani Sridharan static int snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai,
1054aa84ffb7SRanjani Sridharan 					  struct snd_pcm_hw_params *params, u32 dai_index,
1055aa84ffb7SRanjani Sridharan 					  u32 linktype, u8 dir, u32 **dst, u32 *len)
1056aa84ffb7SRanjani Sridharan {
1057aa84ffb7SRanjani Sridharan 	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
1058aa84ffb7SRanjani Sridharan 	struct nhlt_specific_cfg *cfg;
1059aa84ffb7SRanjani Sridharan 	int sample_rate, channel_count;
1060aa84ffb7SRanjani Sridharan 	int bit_depth, ret;
1061aa84ffb7SRanjani Sridharan 	u32 nhlt_type;
1062aa84ffb7SRanjani Sridharan 
1063aa84ffb7SRanjani Sridharan 	/* convert to NHLT type */
1064aa84ffb7SRanjani Sridharan 	switch (linktype) {
1065aa84ffb7SRanjani Sridharan 	case SOF_DAI_INTEL_DMIC:
1066aa84ffb7SRanjani Sridharan 		nhlt_type = NHLT_LINK_DMIC;
1067aa84ffb7SRanjani Sridharan 		bit_depth = params_width(params);
1068aa84ffb7SRanjani Sridharan 		channel_count = params_channels(params);
1069aa84ffb7SRanjani Sridharan 		sample_rate = params_rate(params);
1070aa84ffb7SRanjani Sridharan 		break;
1071aa84ffb7SRanjani Sridharan 	case SOF_DAI_INTEL_SSP:
1072aa84ffb7SRanjani Sridharan 		nhlt_type = NHLT_LINK_SSP;
1073aa84ffb7SRanjani Sridharan 		ret = snd_sof_get_hw_config_params(sdev, dai, &sample_rate, &channel_count,
1074aa84ffb7SRanjani Sridharan 						   &bit_depth);
1075aa84ffb7SRanjani Sridharan 		if (ret < 0)
1076aa84ffb7SRanjani Sridharan 			return ret;
1077aa84ffb7SRanjani Sridharan 		break;
1078aa84ffb7SRanjani Sridharan 	default:
1079aa84ffb7SRanjani Sridharan 		return 0;
1080aa84ffb7SRanjani Sridharan 	}
1081aa84ffb7SRanjani Sridharan 
10823809264bSPierre-Louis Bossart 	dev_dbg(sdev->dev, "dai index %d nhlt type %d direction %d\n",
10833809264bSPierre-Louis Bossart 		dai_index, nhlt_type, dir);
1084aa84ffb7SRanjani Sridharan 
1085aa84ffb7SRanjani Sridharan 	/* find NHLT blob with matching params */
1086aa84ffb7SRanjani Sridharan 	cfg = intel_nhlt_get_endpoint_blob(sdev->dev, ipc4_data->nhlt, dai_index, nhlt_type,
1087aa84ffb7SRanjani Sridharan 					   bit_depth, bit_depth, channel_count, sample_rate,
1088aa84ffb7SRanjani Sridharan 					   dir, 0);
1089aa84ffb7SRanjani Sridharan 
1090aa84ffb7SRanjani Sridharan 	if (!cfg) {
1091aa84ffb7SRanjani Sridharan 		dev_err(sdev->dev,
1092aa84ffb7SRanjani Sridharan 			"no matching blob for sample rate: %d sample width: %d channels: %d\n",
1093aa84ffb7SRanjani Sridharan 			sample_rate, bit_depth, channel_count);
1094aa84ffb7SRanjani Sridharan 		return -EINVAL;
1095aa84ffb7SRanjani Sridharan 	}
1096aa84ffb7SRanjani Sridharan 
1097aa84ffb7SRanjani Sridharan 	/* config length should be in dwords */
1098aa84ffb7SRanjani Sridharan 	*len = cfg->size >> 2;
1099aa84ffb7SRanjani Sridharan 	*dst = (u32 *)cfg->caps;
1100aa84ffb7SRanjani Sridharan 
1101aa84ffb7SRanjani Sridharan 	return 0;
1102aa84ffb7SRanjani Sridharan }
1103aa84ffb7SRanjani Sridharan #else
1104aa84ffb7SRanjani Sridharan static int snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai,
1105aa84ffb7SRanjani Sridharan 					  struct snd_pcm_hw_params *params, u32 dai_index,
1106aa84ffb7SRanjani Sridharan 					  u32 linktype, u8 dir, u32 **dst, u32 *len)
1107aa84ffb7SRanjani Sridharan {
1108aa84ffb7SRanjani Sridharan 	return 0;
1109aa84ffb7SRanjani Sridharan }
1110aa84ffb7SRanjani Sridharan #endif
1111aa84ffb7SRanjani Sridharan 
1112904c48c4SRanjani Sridharan static int
1113904c48c4SRanjani Sridharan sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
1114904c48c4SRanjani Sridharan 			       struct snd_pcm_hw_params *fe_params,
1115904c48c4SRanjani Sridharan 			       struct snd_sof_platform_stream_params *platform_params,
1116904c48c4SRanjani Sridharan 			       struct snd_pcm_hw_params *pipeline_params, int dir)
1117904c48c4SRanjani Sridharan {
1118904c48c4SRanjani Sridharan 	struct sof_ipc4_available_audio_format *available_fmt;
1119904c48c4SRanjani Sridharan 	struct snd_soc_component *scomp = swidget->scomp;
1120904c48c4SRanjani Sridharan 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
1121904c48c4SRanjani Sridharan 	struct sof_ipc4_copier_data *copier_data;
1122904c48c4SRanjani Sridharan 	struct snd_pcm_hw_params *ref_params;
1123904c48c4SRanjani Sridharan 	struct sof_ipc4_copier *ipc4_copier;
1124a150345aSBard Liao 	struct snd_sof_dai *dai;
1125904c48c4SRanjani Sridharan 	struct snd_mask *fmt;
1126904c48c4SRanjani Sridharan 	int out_sample_valid_bits;
1127904c48c4SRanjani Sridharan 	size_t ref_audio_fmt_size;
1128904c48c4SRanjani Sridharan 	void **ipc_config_data;
1129904c48c4SRanjani Sridharan 	int *ipc_config_size;
1130904c48c4SRanjani Sridharan 	u32 **data;
1131904c48c4SRanjani Sridharan 	int ipc_size, ret;
1132904c48c4SRanjani Sridharan 
11333809264bSPierre-Louis Bossart 	dev_dbg(sdev->dev, "copier %s, type %d", swidget->widget->name, swidget->id);
1134904c48c4SRanjani Sridharan 
1135904c48c4SRanjani Sridharan 	switch (swidget->id) {
1136904c48c4SRanjani Sridharan 	case snd_soc_dapm_aif_in:
1137904c48c4SRanjani Sridharan 	case snd_soc_dapm_aif_out:
1138904c48c4SRanjani Sridharan 	{
1139904c48c4SRanjani Sridharan 		struct sof_ipc4_gtw_attributes *gtw_attr;
1140904c48c4SRanjani Sridharan 		struct snd_sof_widget *pipe_widget;
1141904c48c4SRanjani Sridharan 		struct sof_ipc4_pipeline *pipeline;
1142904c48c4SRanjani Sridharan 
11439c04363dSRanjani Sridharan 		pipe_widget = swidget->spipe->pipe_widget;
1144904c48c4SRanjani Sridharan 		pipeline = pipe_widget->private;
1145904c48c4SRanjani Sridharan 		ipc4_copier = (struct sof_ipc4_copier *)swidget->private;
1146904c48c4SRanjani Sridharan 		gtw_attr = ipc4_copier->gtw_attr;
1147904c48c4SRanjani Sridharan 		copier_data = &ipc4_copier->data;
1148904c48c4SRanjani Sridharan 		available_fmt = &ipc4_copier->available_fmt;
1149904c48c4SRanjani Sridharan 
1150904c48c4SRanjani Sridharan 		/*
1151904c48c4SRanjani Sridharan 		 * base_config->audio_fmt and out_audio_fmt represent the input and output audio
1152904c48c4SRanjani Sridharan 		 * formats. Use the input format as the reference to match pcm params for playback
1153904c48c4SRanjani Sridharan 		 * and the output format as reference for capture.
1154904c48c4SRanjani Sridharan 		 */
1155904c48c4SRanjani Sridharan 		if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
1156904c48c4SRanjani Sridharan 			available_fmt->ref_audio_fmt = &available_fmt->base_config->audio_fmt;
1157904c48c4SRanjani Sridharan 			ref_audio_fmt_size = sizeof(struct sof_ipc4_base_module_cfg);
1158904c48c4SRanjani Sridharan 		} else {
1159904c48c4SRanjani Sridharan 			available_fmt->ref_audio_fmt = available_fmt->out_audio_fmt;
1160904c48c4SRanjani Sridharan 			ref_audio_fmt_size = sizeof(struct sof_ipc4_audio_format);
1161904c48c4SRanjani Sridharan 		}
1162904c48c4SRanjani Sridharan 		copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
1163904c48c4SRanjani Sridharan 		copier_data->gtw_cfg.node_id |=
1164904c48c4SRanjani Sridharan 			SOF_IPC4_NODE_INDEX(platform_params->stream_tag - 1);
1165904c48c4SRanjani Sridharan 
1166904c48c4SRanjani Sridharan 		/* set gateway attributes */
1167904c48c4SRanjani Sridharan 		gtw_attr->lp_buffer_alloc = pipeline->lp_mode;
1168904c48c4SRanjani Sridharan 		ref_params = fe_params;
1169904c48c4SRanjani Sridharan 		break;
1170904c48c4SRanjani Sridharan 	}
1171acf52594SRanjani Sridharan 	case snd_soc_dapm_dai_in:
1172acf52594SRanjani Sridharan 	case snd_soc_dapm_dai_out:
1173acf52594SRanjani Sridharan 	{
1174a150345aSBard Liao 		dai = swidget->private;
1175acf52594SRanjani Sridharan 
1176acf52594SRanjani Sridharan 		ipc4_copier = (struct sof_ipc4_copier *)dai->private;
1177acf52594SRanjani Sridharan 		copier_data = &ipc4_copier->data;
1178acf52594SRanjani Sridharan 		available_fmt = &ipc4_copier->available_fmt;
1179acf52594SRanjani Sridharan 		if (dir == SNDRV_PCM_STREAM_CAPTURE) {
1180acf52594SRanjani Sridharan 			available_fmt->ref_audio_fmt = available_fmt->out_audio_fmt;
1181acf52594SRanjani Sridharan 			ref_audio_fmt_size = sizeof(struct sof_ipc4_audio_format);
1182acf52594SRanjani Sridharan 
1183acf52594SRanjani Sridharan 			/*
1184acf52594SRanjani Sridharan 			 * modify the input params for the dai copier as it only supports
1185acf52594SRanjani Sridharan 			 * 32-bit always
1186acf52594SRanjani Sridharan 			 */
1187acf52594SRanjani Sridharan 			fmt = hw_param_mask(pipeline_params, SNDRV_PCM_HW_PARAM_FORMAT);
1188acf52594SRanjani Sridharan 			snd_mask_none(fmt);
1189acf52594SRanjani Sridharan 			snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE);
1190acf52594SRanjani Sridharan 		} else {
1191acf52594SRanjani Sridharan 			available_fmt->ref_audio_fmt = &available_fmt->base_config->audio_fmt;
1192acf52594SRanjani Sridharan 			ref_audio_fmt_size = sizeof(struct sof_ipc4_base_module_cfg);
1193acf52594SRanjani Sridharan 		}
1194acf52594SRanjani Sridharan 
1195acf52594SRanjani Sridharan 		ref_params = pipeline_params;
1196acf52594SRanjani Sridharan 
1197aa84ffb7SRanjani Sridharan 		ret = snd_sof_get_nhlt_endpoint_data(sdev, dai, fe_params, ipc4_copier->dai_index,
1198aa84ffb7SRanjani Sridharan 						     ipc4_copier->dai_type, dir,
1199aa84ffb7SRanjani Sridharan 						     &ipc4_copier->copier_config,
1200aa84ffb7SRanjani Sridharan 						     &copier_data->gtw_cfg.config_length);
1201aa84ffb7SRanjani Sridharan 		if (ret < 0)
1202aa84ffb7SRanjani Sridharan 			return ret;
1203aa84ffb7SRanjani Sridharan 
1204acf52594SRanjani Sridharan 		break;
1205acf52594SRanjani Sridharan 	}
12067d573425SBard Liao 	case snd_soc_dapm_buffer:
12077d573425SBard Liao 	{
12087d573425SBard Liao 		ipc4_copier = (struct sof_ipc4_copier *)swidget->private;
12097d573425SBard Liao 		copier_data = &ipc4_copier->data;
12107d573425SBard Liao 		available_fmt = &ipc4_copier->available_fmt;
12117d573425SBard Liao 
12127d573425SBard Liao 		/*
12137d573425SBard Liao 		 * base_config->audio_fmt represent the input audio formats. Use
12147d573425SBard Liao 		 * the input format as the reference to match pcm params
12157d573425SBard Liao 		 */
12167d573425SBard Liao 		available_fmt->ref_audio_fmt = &available_fmt->base_config->audio_fmt;
12177d573425SBard Liao 		ref_audio_fmt_size = sizeof(struct sof_ipc4_base_module_cfg);
12187d573425SBard Liao 		ref_params = pipeline_params;
12197d573425SBard Liao 
12207d573425SBard Liao 		break;
12217d573425SBard Liao 	}
1222904c48c4SRanjani Sridharan 	default:
1223904c48c4SRanjani Sridharan 		dev_err(sdev->dev, "unsupported type %d for copier %s",
1224904c48c4SRanjani Sridharan 			swidget->id, swidget->widget->name);
1225904c48c4SRanjani Sridharan 		return -EINVAL;
1226904c48c4SRanjani Sridharan 	}
1227904c48c4SRanjani Sridharan 
1228904c48c4SRanjani Sridharan 	/* set input and output audio formats */
1229904c48c4SRanjani Sridharan 	ret = sof_ipc4_init_audio_fmt(sdev, swidget, &copier_data->base_config,
1230904c48c4SRanjani Sridharan 				      &copier_data->out_format, ref_params,
1231904c48c4SRanjani Sridharan 				      available_fmt, ref_audio_fmt_size);
1232904c48c4SRanjani Sridharan 	if (ret < 0)
1233904c48c4SRanjani Sridharan 		return ret;
1234904c48c4SRanjani Sridharan 
1235a45a4d43SBard Liao 	switch (swidget->id) {
1236a45a4d43SBard Liao 	case snd_soc_dapm_dai_in:
1237a45a4d43SBard Liao 	case snd_soc_dapm_dai_out:
1238a45a4d43SBard Liao 	{
1239a45a4d43SBard Liao 		/*
1240a45a4d43SBard Liao 		 * Only SOF_DAI_INTEL_ALH needs copier_data to set blob.
1241a45a4d43SBard Liao 		 * That's why only ALH dai's blob is set after sof_ipc4_init_audio_fmt
1242a45a4d43SBard Liao 		 */
1243a45a4d43SBard Liao 		if (ipc4_copier->dai_type == SOF_DAI_INTEL_ALH) {
1244a45a4d43SBard Liao 			struct sof_ipc4_alh_configuration_blob *blob;
1245a150345aSBard Liao 			struct sof_ipc4_copier_data *alh_data;
1246a150345aSBard Liao 			struct sof_ipc4_copier *alh_copier;
1247a150345aSBard Liao 			struct snd_sof_widget *w;
12480390a102SBard Liao 			u32 ch_count = 0;
1249a150345aSBard Liao 			u32 ch_mask = 0;
1250a45a4d43SBard Liao 			u32 ch_map;
12510390a102SBard Liao 			u32 step;
12520390a102SBard Liao 			u32 mask;
1253a45a4d43SBard Liao 			int i;
1254a45a4d43SBard Liao 
1255a45a4d43SBard Liao 			blob = (struct sof_ipc4_alh_configuration_blob *)ipc4_copier->copier_config;
1256a150345aSBard Liao 
1257a45a4d43SBard Liao 			blob->gw_attr.lp_buffer_alloc = 0;
1258a45a4d43SBard Liao 
1259a45a4d43SBard Liao 			/* Get channel_mask from ch_map */
1260a45a4d43SBard Liao 			ch_map = copier_data->base_config.audio_fmt.ch_map;
1261a45a4d43SBard Liao 			for (i = 0; ch_map; i++) {
12620390a102SBard Liao 				if ((ch_map & 0xf) != 0xf) {
1263a150345aSBard Liao 					ch_mask |= BIT(i);
12640390a102SBard Liao 					ch_count++;
12650390a102SBard Liao 				}
1266a45a4d43SBard Liao 				ch_map >>= 4;
1267a45a4d43SBard Liao 			}
1268a150345aSBard Liao 
12690390a102SBard Liao 			step = ch_count / blob->alh_cfg.count;
12700390a102SBard Liao 			mask =  GENMASK(step - 1, 0);
1271a150345aSBard Liao 			/*
1272a150345aSBard Liao 			 * Set each gtw_cfg.node_id to blob->alh_cfg.mapping[]
1273a150345aSBard Liao 			 * for all widgets with the same stream name
1274a150345aSBard Liao 			 */
1275a150345aSBard Liao 			i = 0;
1276a150345aSBard Liao 			list_for_each_entry(w, &sdev->widget_list, list) {
1277a150345aSBard Liao 				if (w->widget->sname &&
1278a150345aSBard Liao 				    strcmp(w->widget->sname, swidget->widget->sname))
1279a150345aSBard Liao 					continue;
1280a150345aSBard Liao 
1281a150345aSBard Liao 				dai = w->private;
1282a150345aSBard Liao 				alh_copier = (struct sof_ipc4_copier *)dai->private;
1283a150345aSBard Liao 				alh_data = &alh_copier->data;
1284a150345aSBard Liao 				blob->alh_cfg.mapping[i].alh_id = alh_data->gtw_cfg.node_id;
12850390a102SBard Liao 				/*
12860390a102SBard Liao 				 * Set the same channel mask for playback as the audio data is
12870390a102SBard Liao 				 * duplicated for all speakers. For capture, split the channels
12880390a102SBard Liao 				 * among the aggregated DAIs. For example, with 4 channels on 2
12890390a102SBard Liao 				 * aggregated DAIs, the channel_mask should be 0x3 and 0xc for the
12900390a102SBard Liao 				 * two DAI's.
12910390a102SBard Liao 				 * The channel masks used depend on the cpu_dais used in the
12920390a102SBard Liao 				 * dailink at the machine driver level, which actually comes from
12930390a102SBard Liao 				 * the tables in soc_acpi files depending on the _ADR and devID
12940390a102SBard Liao 				 * registers for each codec.
12950390a102SBard Liao 				 */
12960390a102SBard Liao 				if (w->id == snd_soc_dapm_dai_in)
1297a150345aSBard Liao 					blob->alh_cfg.mapping[i].channel_mask = ch_mask;
12980390a102SBard Liao 				else
12990390a102SBard Liao 					blob->alh_cfg.mapping[i].channel_mask = mask << (step * i);
13000390a102SBard Liao 
1301a150345aSBard Liao 				i++;
1302a150345aSBard Liao 			}
1303a150345aSBard Liao 			if (blob->alh_cfg.count > 1) {
1304a150345aSBard Liao 				int group_id;
1305a150345aSBard Liao 
13064ee6fc27SBard Liao 				group_id = ida_alloc_max(&alh_group_ida, ALH_MULTI_GTW_COUNT - 1,
1307a150345aSBard Liao 							 GFP_KERNEL);
1308a150345aSBard Liao 
1309a150345aSBard Liao 				if (group_id < 0)
1310a150345aSBard Liao 					return group_id;
1311a150345aSBard Liao 
1312a150345aSBard Liao 				/* add multi-gateway base */
1313a150345aSBard Liao 				group_id += ALH_MULTI_GTW_BASE;
1314a150345aSBard Liao 				copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
1315a150345aSBard Liao 				copier_data->gtw_cfg.node_id |= SOF_IPC4_NODE_INDEX(group_id);
1316a150345aSBard Liao 			}
1317a45a4d43SBard Liao 		}
1318a45a4d43SBard Liao 	}
1319a45a4d43SBard Liao 	}
1320a45a4d43SBard Liao 
1321904c48c4SRanjani Sridharan 	/* modify the input params for the next widget */
1322904c48c4SRanjani Sridharan 	fmt = hw_param_mask(pipeline_params, SNDRV_PCM_HW_PARAM_FORMAT);
1323904c48c4SRanjani Sridharan 	out_sample_valid_bits =
1324904c48c4SRanjani Sridharan 		SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(copier_data->out_format.fmt_cfg);
1325904c48c4SRanjani Sridharan 	snd_mask_none(fmt);
1326904c48c4SRanjani Sridharan 	switch (out_sample_valid_bits) {
1327904c48c4SRanjani Sridharan 	case 16:
1328904c48c4SRanjani Sridharan 		snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
1329904c48c4SRanjani Sridharan 		break;
1330904c48c4SRanjani Sridharan 	case 24:
1331904c48c4SRanjani Sridharan 		snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
1332904c48c4SRanjani Sridharan 		break;
1333904c48c4SRanjani Sridharan 	case 32:
1334904c48c4SRanjani Sridharan 		snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE);
1335904c48c4SRanjani Sridharan 		break;
1336904c48c4SRanjani Sridharan 	default:
1337904c48c4SRanjani Sridharan 		dev_err(sdev->dev, "invalid sample frame format %d\n",
1338904c48c4SRanjani Sridharan 			params_format(pipeline_params));
1339904c48c4SRanjani Sridharan 		return -EINVAL;
1340904c48c4SRanjani Sridharan 	}
1341904c48c4SRanjani Sridharan 
1342904c48c4SRanjani Sridharan 	/* set the gateway dma_buffer_size using the matched ID returned above */
1343904c48c4SRanjani Sridharan 	copier_data->gtw_cfg.dma_buffer_size = available_fmt->dma_buffer_size[ret];
1344904c48c4SRanjani Sridharan 
1345904c48c4SRanjani Sridharan 	data = &ipc4_copier->copier_config;
1346904c48c4SRanjani Sridharan 	ipc_config_size = &ipc4_copier->ipc_config_size;
1347904c48c4SRanjani Sridharan 	ipc_config_data = &ipc4_copier->ipc_config_data;
1348904c48c4SRanjani Sridharan 
1349904c48c4SRanjani Sridharan 	/* config_length is DWORD based */
1350904c48c4SRanjani Sridharan 	ipc_size = sizeof(*copier_data) + copier_data->gtw_cfg.config_length * 4;
1351904c48c4SRanjani Sridharan 
1352904c48c4SRanjani Sridharan 	dev_dbg(sdev->dev, "copier %s, IPC size is %d", swidget->widget->name, ipc_size);
1353904c48c4SRanjani Sridharan 
1354904c48c4SRanjani Sridharan 	*ipc_config_data = kzalloc(ipc_size, GFP_KERNEL);
1355904c48c4SRanjani Sridharan 	if (!*ipc_config_data)
1356904c48c4SRanjani Sridharan 		return -ENOMEM;
1357904c48c4SRanjani Sridharan 
1358904c48c4SRanjani Sridharan 	*ipc_config_size = ipc_size;
1359904c48c4SRanjani Sridharan 
1360904c48c4SRanjani Sridharan 	/* copy IPC data */
1361904c48c4SRanjani Sridharan 	memcpy(*ipc_config_data, (void *)copier_data, sizeof(*copier_data));
1362904c48c4SRanjani Sridharan 	if (copier_data->gtw_cfg.config_length)
1363904c48c4SRanjani Sridharan 		memcpy(*ipc_config_data + sizeof(*copier_data),
1364904c48c4SRanjani Sridharan 		       *data, copier_data->gtw_cfg.config_length * 4);
1365904c48c4SRanjani Sridharan 
1366904c48c4SRanjani Sridharan 	/* update pipeline memory usage */
1367904c48c4SRanjani Sridharan 	sof_ipc4_update_pipeline_mem_usage(sdev, swidget, &copier_data->base_config);
1368904c48c4SRanjani Sridharan 
1369711d0427SBard Liao 	return 0;
13704f838ab2SRanjani Sridharan }
13714f838ab2SRanjani Sridharan 
13724f838ab2SRanjani Sridharan static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget,
13734f838ab2SRanjani Sridharan 					struct snd_pcm_hw_params *fe_params,
13744f838ab2SRanjani Sridharan 					struct snd_sof_platform_stream_params *platform_params,
13754f838ab2SRanjani Sridharan 					struct snd_pcm_hw_params *pipeline_params, int dir)
13764f838ab2SRanjani Sridharan {
13774f838ab2SRanjani Sridharan 	struct snd_soc_component *scomp = swidget->scomp;
13784f838ab2SRanjani Sridharan 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
13794f838ab2SRanjani Sridharan 	struct sof_ipc4_gain *gain = swidget->private;
13804f838ab2SRanjani Sridharan 	int ret;
13814f838ab2SRanjani Sridharan 
13824f838ab2SRanjani Sridharan 	gain->available_fmt.ref_audio_fmt = &gain->available_fmt.base_config->audio_fmt;
13834f838ab2SRanjani Sridharan 
13844f838ab2SRanjani Sridharan 	/* output format is not required to be sent to the FW for gain */
13854f838ab2SRanjani Sridharan 	ret = sof_ipc4_init_audio_fmt(sdev, swidget, &gain->base_config,
13864f838ab2SRanjani Sridharan 				      NULL, pipeline_params, &gain->available_fmt,
13874f838ab2SRanjani Sridharan 				      sizeof(gain->base_config));
13884f838ab2SRanjani Sridharan 	if (ret < 0)
13894f838ab2SRanjani Sridharan 		return ret;
13904f838ab2SRanjani Sridharan 
13914f838ab2SRanjani Sridharan 	/* update pipeline memory usage */
13924f838ab2SRanjani Sridharan 	sof_ipc4_update_pipeline_mem_usage(sdev, swidget, &gain->base_config);
13934f838ab2SRanjani Sridharan 
1394711d0427SBard Liao 	return 0;
13954f838ab2SRanjani Sridharan }
13964f838ab2SRanjani Sridharan 
13974d4ba014SRanjani Sridharan static int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget,
13984d4ba014SRanjani Sridharan 					 struct snd_pcm_hw_params *fe_params,
13994d4ba014SRanjani Sridharan 					 struct snd_sof_platform_stream_params *platform_params,
14004d4ba014SRanjani Sridharan 					 struct snd_pcm_hw_params *pipeline_params, int dir)
14014d4ba014SRanjani Sridharan {
14024d4ba014SRanjani Sridharan 	struct snd_soc_component *scomp = swidget->scomp;
14034d4ba014SRanjani Sridharan 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
14044d4ba014SRanjani Sridharan 	struct sof_ipc4_mixer *mixer = swidget->private;
14054d4ba014SRanjani Sridharan 	int ret;
14064d4ba014SRanjani Sridharan 
14074d4ba014SRanjani Sridharan 	/* only 32bit is supported by mixer */
14084d4ba014SRanjani Sridharan 	mixer->available_fmt.ref_audio_fmt = &mixer->available_fmt.base_config->audio_fmt;
14094d4ba014SRanjani Sridharan 
14104d4ba014SRanjani Sridharan 	/* output format is not required to be sent to the FW for mixer */
14114d4ba014SRanjani Sridharan 	ret = sof_ipc4_init_audio_fmt(sdev, swidget, &mixer->base_config,
14124d4ba014SRanjani Sridharan 				      NULL, pipeline_params, &mixer->available_fmt,
14134d4ba014SRanjani Sridharan 				      sizeof(mixer->base_config));
14144d4ba014SRanjani Sridharan 	if (ret < 0)
14154d4ba014SRanjani Sridharan 		return ret;
14164d4ba014SRanjani Sridharan 
14174d4ba014SRanjani Sridharan 	/* update pipeline memory usage */
14184d4ba014SRanjani Sridharan 	sof_ipc4_update_pipeline_mem_usage(sdev, swidget, &mixer->base_config);
14194d4ba014SRanjani Sridharan 
1420711d0427SBard Liao 	return 0;
14214d4ba014SRanjani Sridharan }
14224d4ba014SRanjani Sridharan 
1423b85f4fc4SRander Wang static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget,
1424b85f4fc4SRander Wang 				       struct snd_pcm_hw_params *fe_params,
1425b85f4fc4SRander Wang 				       struct snd_sof_platform_stream_params *platform_params,
1426b85f4fc4SRander Wang 				       struct snd_pcm_hw_params *pipeline_params, int dir)
1427b85f4fc4SRander Wang {
1428b85f4fc4SRander Wang 	struct snd_soc_component *scomp = swidget->scomp;
1429b85f4fc4SRander Wang 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
1430b85f4fc4SRander Wang 	struct sof_ipc4_src *src = swidget->private;
1431b85f4fc4SRander Wang 	struct snd_interval *rate;
1432b85f4fc4SRander Wang 	int ret;
1433b85f4fc4SRander Wang 
1434b85f4fc4SRander Wang 	src->available_fmt.ref_audio_fmt = &src->available_fmt.base_config->audio_fmt;
1435b85f4fc4SRander Wang 
1436b85f4fc4SRander Wang 	/* output format is not required to be sent to the FW for SRC */
1437b85f4fc4SRander Wang 	ret = sof_ipc4_init_audio_fmt(sdev, swidget, &src->base_config,
1438b85f4fc4SRander Wang 				      NULL, pipeline_params, &src->available_fmt,
1439b85f4fc4SRander Wang 				      sizeof(src->base_config));
1440b85f4fc4SRander Wang 	if (ret < 0)
1441b85f4fc4SRander Wang 		return ret;
1442b85f4fc4SRander Wang 
1443b85f4fc4SRander Wang 	/* update pipeline memory usage */
1444b85f4fc4SRander Wang 	sof_ipc4_update_pipeline_mem_usage(sdev, swidget, &src->base_config);
1445b85f4fc4SRander Wang 
1446b85f4fc4SRander Wang 	/* update pipeline_params for sink widgets */
1447b85f4fc4SRander Wang 	rate = hw_param_interval(pipeline_params, SNDRV_PCM_HW_PARAM_RATE);
1448b85f4fc4SRander Wang 	rate->min = src->sink_rate;
1449b85f4fc4SRander Wang 	rate->max = rate->min;
1450b85f4fc4SRander Wang 
1451b85f4fc4SRander Wang 	return 0;
1452b85f4fc4SRander Wang }
1453b85f4fc4SRander Wang 
1454d97964f8SRanjani Sridharan static int sof_ipc4_control_load_volume(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)
1455d97964f8SRanjani Sridharan {
1456d97964f8SRanjani Sridharan 	struct sof_ipc4_control_data *control_data;
1457d97964f8SRanjani Sridharan 	struct sof_ipc4_msg *msg;
1458d97964f8SRanjani Sridharan 	int i;
1459d97964f8SRanjani Sridharan 
1460d97964f8SRanjani Sridharan 	scontrol->size = struct_size(control_data, chanv, scontrol->num_channels);
1461d97964f8SRanjani Sridharan 
1462d97964f8SRanjani Sridharan 	/* scontrol->ipc_control_data will be freed in sof_control_unload */
1463d97964f8SRanjani Sridharan 	scontrol->ipc_control_data = kzalloc(scontrol->size, GFP_KERNEL);
1464d97964f8SRanjani Sridharan 	if (!scontrol->ipc_control_data)
1465d97964f8SRanjani Sridharan 		return -ENOMEM;
1466d97964f8SRanjani Sridharan 
1467d97964f8SRanjani Sridharan 	control_data = scontrol->ipc_control_data;
1468d97964f8SRanjani Sridharan 	control_data->index = scontrol->index;
1469d97964f8SRanjani Sridharan 
1470d97964f8SRanjani Sridharan 	msg = &control_data->msg;
1471d97964f8SRanjani Sridharan 	msg->primary = SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_SET);
1472d97964f8SRanjani Sridharan 	msg->primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
1473d97964f8SRanjani Sridharan 	msg->primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
1474d97964f8SRanjani Sridharan 
1475d97964f8SRanjani Sridharan 	msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_GAIN_PARAM_ID);
1476d97964f8SRanjani Sridharan 
1477d97964f8SRanjani Sridharan 	/* set default volume values to 0dB in control */
1478d97964f8SRanjani Sridharan 	for (i = 0; i < scontrol->num_channels; i++) {
1479d97964f8SRanjani Sridharan 		control_data->chanv[i].channel = i;
1480d97964f8SRanjani Sridharan 		control_data->chanv[i].value = SOF_IPC4_VOL_ZERO_DB;
1481d97964f8SRanjani Sridharan 	}
1482d97964f8SRanjani Sridharan 
1483d97964f8SRanjani Sridharan 	return 0;
1484d97964f8SRanjani Sridharan }
1485d97964f8SRanjani Sridharan 
1486d97964f8SRanjani Sridharan static int sof_ipc4_control_setup(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)
1487d97964f8SRanjani Sridharan {
1488d97964f8SRanjani Sridharan 	switch (scontrol->info_type) {
1489d97964f8SRanjani Sridharan 	case SND_SOC_TPLG_CTL_VOLSW:
1490d97964f8SRanjani Sridharan 	case SND_SOC_TPLG_CTL_VOLSW_SX:
1491d97964f8SRanjani Sridharan 	case SND_SOC_TPLG_CTL_VOLSW_XR_SX:
1492d97964f8SRanjani Sridharan 		return sof_ipc4_control_load_volume(sdev, scontrol);
1493d97964f8SRanjani Sridharan 	default:
1494d97964f8SRanjani Sridharan 		break;
1495d97964f8SRanjani Sridharan 	}
1496d97964f8SRanjani Sridharan 
1497d97964f8SRanjani Sridharan 	return 0;
1498d97964f8SRanjani Sridharan }
1499d97964f8SRanjani Sridharan 
15006e9257a1SRanjani Sridharan static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
15016e9257a1SRanjani Sridharan {
15029c04363dSRanjani Sridharan 	struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
1503a2ba1f70SBard Liao 	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
15046e9257a1SRanjani Sridharan 	struct sof_ipc4_pipeline *pipeline;
15056e9257a1SRanjani Sridharan 	struct sof_ipc4_msg *msg;
15066e9257a1SRanjani Sridharan 	void *ipc_data = NULL;
15076e9257a1SRanjani Sridharan 	u32 ipc_size = 0;
15086e9257a1SRanjani Sridharan 	int ret;
15096e9257a1SRanjani Sridharan 
15106e9257a1SRanjani Sridharan 	switch (swidget->id) {
15116e9257a1SRanjani Sridharan 	case snd_soc_dapm_scheduler:
15126e9257a1SRanjani Sridharan 		pipeline = swidget->private;
15136e9257a1SRanjani Sridharan 
15146e9257a1SRanjani Sridharan 		dev_dbg(sdev->dev, "pipeline: %d memory pages: %d\n", swidget->pipeline_id,
15156e9257a1SRanjani Sridharan 			pipeline->mem_usage);
15166e9257a1SRanjani Sridharan 
15176e9257a1SRanjani Sridharan 		msg = &pipeline->msg;
15186e9257a1SRanjani Sridharan 		msg->primary |= pipeline->mem_usage;
1519a2ba1f70SBard Liao 
1520a2ba1f70SBard Liao 		swidget->instance_id = ida_alloc_max(&pipeline_ida, ipc4_data->max_num_pipelines,
1521a2ba1f70SBard Liao 						     GFP_KERNEL);
1522a2ba1f70SBard Liao 		if (swidget->instance_id < 0) {
1523a2ba1f70SBard Liao 			dev_err(sdev->dev, "failed to assign pipeline id for %s: %d\n",
1524a2ba1f70SBard Liao 				swidget->widget->name, swidget->instance_id);
1525a2ba1f70SBard Liao 			return swidget->instance_id;
1526a2ba1f70SBard Liao 		}
1527a2ba1f70SBard Liao 		msg->primary &= ~SOF_IPC4_GLB_PIPE_INSTANCE_MASK;
1528a2ba1f70SBard Liao 		msg->primary |= SOF_IPC4_GLB_PIPE_INSTANCE_ID(swidget->instance_id);
15296e9257a1SRanjani Sridharan 		break;
15306e9257a1SRanjani Sridharan 	case snd_soc_dapm_aif_in:
15316e9257a1SRanjani Sridharan 	case snd_soc_dapm_aif_out:
15327d573425SBard Liao 	case snd_soc_dapm_buffer:
15336e9257a1SRanjani Sridharan 	{
15346e9257a1SRanjani Sridharan 		struct sof_ipc4_copier *ipc4_copier = swidget->private;
15356e9257a1SRanjani Sridharan 
15366e9257a1SRanjani Sridharan 		ipc_size = ipc4_copier->ipc_config_size;
15376e9257a1SRanjani Sridharan 		ipc_data = ipc4_copier->ipc_config_data;
15386e9257a1SRanjani Sridharan 
15396e9257a1SRanjani Sridharan 		msg = &ipc4_copier->msg;
15406e9257a1SRanjani Sridharan 		break;
15416e9257a1SRanjani Sridharan 	}
15426e9257a1SRanjani Sridharan 	case snd_soc_dapm_dai_in:
15436e9257a1SRanjani Sridharan 	case snd_soc_dapm_dai_out:
15446e9257a1SRanjani Sridharan 	{
15456e9257a1SRanjani Sridharan 		struct snd_sof_dai *dai = swidget->private;
15466e9257a1SRanjani Sridharan 		struct sof_ipc4_copier *ipc4_copier = dai->private;
15476e9257a1SRanjani Sridharan 
15486e9257a1SRanjani Sridharan 		ipc_size = ipc4_copier->ipc_config_size;
15496e9257a1SRanjani Sridharan 		ipc_data = ipc4_copier->ipc_config_data;
15506e9257a1SRanjani Sridharan 
15516e9257a1SRanjani Sridharan 		msg = &ipc4_copier->msg;
15526e9257a1SRanjani Sridharan 		break;
15536e9257a1SRanjani Sridharan 	}
15546e9257a1SRanjani Sridharan 	case snd_soc_dapm_pga:
15556e9257a1SRanjani Sridharan 	{
15566e9257a1SRanjani Sridharan 		struct sof_ipc4_gain *gain = swidget->private;
15576e9257a1SRanjani Sridharan 
15586e9257a1SRanjani Sridharan 		ipc_size = sizeof(struct sof_ipc4_base_module_cfg) +
15596e9257a1SRanjani Sridharan 			   sizeof(struct sof_ipc4_gain_data);
15606e9257a1SRanjani Sridharan 		ipc_data = gain;
15616e9257a1SRanjani Sridharan 
15626e9257a1SRanjani Sridharan 		msg = &gain->msg;
15636e9257a1SRanjani Sridharan 		break;
15646e9257a1SRanjani Sridharan 	}
15656e9257a1SRanjani Sridharan 	case snd_soc_dapm_mixer:
15666e9257a1SRanjani Sridharan 	{
15676e9257a1SRanjani Sridharan 		struct sof_ipc4_mixer *mixer = swidget->private;
15686e9257a1SRanjani Sridharan 
15696e9257a1SRanjani Sridharan 		ipc_size = sizeof(mixer->base_config);
15706e9257a1SRanjani Sridharan 		ipc_data = &mixer->base_config;
15716e9257a1SRanjani Sridharan 
15726e9257a1SRanjani Sridharan 		msg = &mixer->msg;
15736e9257a1SRanjani Sridharan 		break;
15746e9257a1SRanjani Sridharan 	}
1575b85f4fc4SRander Wang 	case snd_soc_dapm_src:
1576b85f4fc4SRander Wang 	{
1577b85f4fc4SRander Wang 		struct sof_ipc4_src *src = swidget->private;
1578b85f4fc4SRander Wang 
1579b85f4fc4SRander Wang 		ipc_size = sizeof(struct sof_ipc4_base_module_cfg) + sizeof(src->sink_rate);
1580b85f4fc4SRander Wang 		ipc_data = src;
1581b85f4fc4SRander Wang 
1582b85f4fc4SRander Wang 		msg = &src->msg;
1583b85f4fc4SRander Wang 		break;
1584b85f4fc4SRander Wang 	}
15856e9257a1SRanjani Sridharan 	default:
15866e9257a1SRanjani Sridharan 		dev_err(sdev->dev, "widget type %d not supported", swidget->id);
15876e9257a1SRanjani Sridharan 		return -EINVAL;
15886e9257a1SRanjani Sridharan 	}
15896e9257a1SRanjani Sridharan 
15906e9257a1SRanjani Sridharan 	if (swidget->id != snd_soc_dapm_scheduler) {
1591711d0427SBard Liao 		ret = sof_ipc4_widget_assign_instance_id(sdev, swidget);
1592711d0427SBard Liao 		if (ret < 0) {
1593711d0427SBard Liao 			dev_err(sdev->dev, "failed to assign instance id for %s\n",
1594711d0427SBard Liao 				swidget->widget->name);
1595711d0427SBard Liao 			return ret;
1596711d0427SBard Liao 		}
15977738211bSPierre-Louis Bossart 
15986e9257a1SRanjani Sridharan 		msg->primary &= ~SOF_IPC4_MOD_INSTANCE_MASK;
15996e9257a1SRanjani Sridharan 		msg->primary |= SOF_IPC4_MOD_INSTANCE(swidget->instance_id);
16006e9257a1SRanjani Sridharan 
16016e9257a1SRanjani Sridharan 		msg->extension &= ~SOF_IPC4_MOD_EXT_PARAM_SIZE_MASK;
16026e9257a1SRanjani Sridharan 		msg->extension |= ipc_size >> 2;
1603a2ba1f70SBard Liao 
1604a2ba1f70SBard Liao 		msg->extension &= ~SOF_IPC4_MOD_EXT_PPL_ID_MASK;
1605a2ba1f70SBard Liao 		msg->extension |= SOF_IPC4_MOD_EXT_PPL_ID(pipe_widget->instance_id);
16066e9257a1SRanjani Sridharan 	}
1607711d0427SBard Liao 	dev_dbg(sdev->dev, "Create widget %s instance %d - pipe %d - core %d\n",
1608711d0427SBard Liao 		swidget->widget->name, swidget->instance_id, swidget->pipeline_id, swidget->core);
16096e9257a1SRanjani Sridharan 
16106e9257a1SRanjani Sridharan 	msg->data_size = ipc_size;
16116e9257a1SRanjani Sridharan 	msg->data_ptr = ipc_data;
16126e9257a1SRanjani Sridharan 
16136e9257a1SRanjani Sridharan 	ret = sof_ipc_tx_message(sdev->ipc, msg, ipc_size, NULL, 0);
161461eb0addSPeter Ujfalusi 	if (ret < 0) {
16156e9257a1SRanjani Sridharan 		dev_err(sdev->dev, "failed to create module %s\n", swidget->widget->name);
16166e9257a1SRanjani Sridharan 
161761eb0addSPeter Ujfalusi 		if (swidget->id != snd_soc_dapm_scheduler) {
161861eb0addSPeter Ujfalusi 			struct sof_ipc4_fw_module *fw_module = swidget->module_info;
161961eb0addSPeter Ujfalusi 
162061eb0addSPeter Ujfalusi 			ida_free(&fw_module->m_ida, swidget->instance_id);
1621a2ba1f70SBard Liao 		} else {
1622a2ba1f70SBard Liao 			ida_free(&pipeline_ida, swidget->instance_id);
162361eb0addSPeter Ujfalusi 		}
162461eb0addSPeter Ujfalusi 	}
162561eb0addSPeter Ujfalusi 
16266e9257a1SRanjani Sridharan 	return ret;
16276e9257a1SRanjani Sridharan }
16286e9257a1SRanjani Sridharan 
16296e9257a1SRanjani Sridharan static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
16306e9257a1SRanjani Sridharan {
1631711d0427SBard Liao 	struct sof_ipc4_fw_module *fw_module = swidget->module_info;
16326bc4d1b7SRanjani Sridharan 	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
16336e9257a1SRanjani Sridharan 	int ret = 0;
16346e9257a1SRanjani Sridharan 
16356bc4d1b7SRanjani Sridharan 	mutex_lock(&ipc4_data->pipeline_state_mutex);
16366bc4d1b7SRanjani Sridharan 
16376e9257a1SRanjani Sridharan 	/* freeing a pipeline frees all the widgets associated with it */
16386e9257a1SRanjani Sridharan 	if (swidget->id == snd_soc_dapm_scheduler) {
16396e9257a1SRanjani Sridharan 		struct sof_ipc4_pipeline *pipeline = swidget->private;
16406e9257a1SRanjani Sridharan 		struct sof_ipc4_msg msg = {{ 0 }};
16416e9257a1SRanjani Sridharan 		u32 header;
16426e9257a1SRanjani Sridharan 
1643a2ba1f70SBard Liao 		header = SOF_IPC4_GLB_PIPE_INSTANCE_ID(swidget->instance_id);
16446e9257a1SRanjani Sridharan 		header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_DELETE_PIPELINE);
16456e9257a1SRanjani Sridharan 		header |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
16466e9257a1SRanjani Sridharan 		header |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG);
16476e9257a1SRanjani Sridharan 
16486e9257a1SRanjani Sridharan 		msg.primary = header;
16496e9257a1SRanjani Sridharan 
16506e9257a1SRanjani Sridharan 		ret = sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0);
16516e9257a1SRanjani Sridharan 		if (ret < 0)
16526e9257a1SRanjani Sridharan 			dev_err(sdev->dev, "failed to free pipeline widget %s\n",
16536e9257a1SRanjani Sridharan 				swidget->widget->name);
16546e9257a1SRanjani Sridharan 
16556e9257a1SRanjani Sridharan 		pipeline->mem_usage = 0;
16566e9257a1SRanjani Sridharan 		pipeline->state = SOF_IPC4_PIPE_UNINITIALIZED;
1657a2ba1f70SBard Liao 		ida_free(&pipeline_ida, swidget->instance_id);
1658711d0427SBard Liao 	} else {
1659711d0427SBard Liao 		ida_free(&fw_module->m_ida, swidget->instance_id);
16606e9257a1SRanjani Sridharan 	}
16616e9257a1SRanjani Sridharan 
16626bc4d1b7SRanjani Sridharan 	mutex_unlock(&ipc4_data->pipeline_state_mutex);
16636bc4d1b7SRanjani Sridharan 
16646e9257a1SRanjani Sridharan 	return ret;
16656e9257a1SRanjani Sridharan }
16666e9257a1SRanjani Sridharan 
1667c84443dbSChao Song static int sof_ipc4_get_queue_id(struct snd_sof_widget *src_widget,
1668c84443dbSChao Song 				 struct snd_sof_widget *sink_widget, bool pin_type)
1669c84443dbSChao Song {
1670c84443dbSChao Song 	struct snd_sof_widget *current_swidget;
1671c84443dbSChao Song 	struct snd_soc_component *scomp;
1672c84443dbSChao Song 	struct ida *queue_ida;
1673c84443dbSChao Song 	const char *buddy_name;
1674c84443dbSChao Song 	char **pin_binding;
1675c84443dbSChao Song 	u32 num_pins;
1676c84443dbSChao Song 	int i;
1677c84443dbSChao Song 
1678c84443dbSChao Song 	if (pin_type == SOF_PIN_TYPE_SOURCE) {
1679c84443dbSChao Song 		current_swidget = src_widget;
1680c84443dbSChao Song 		pin_binding = src_widget->src_pin_binding;
1681c84443dbSChao Song 		queue_ida = &src_widget->src_queue_ida;
1682c84443dbSChao Song 		num_pins = src_widget->num_source_pins;
1683c84443dbSChao Song 		buddy_name = sink_widget->widget->name;
1684c84443dbSChao Song 	} else {
1685c84443dbSChao Song 		current_swidget = sink_widget;
1686c84443dbSChao Song 		pin_binding = sink_widget->sink_pin_binding;
1687c84443dbSChao Song 		queue_ida = &sink_widget->sink_queue_ida;
1688c84443dbSChao Song 		num_pins = sink_widget->num_sink_pins;
1689c84443dbSChao Song 		buddy_name = src_widget->widget->name;
1690c84443dbSChao Song 	}
1691c84443dbSChao Song 
1692c84443dbSChao Song 	scomp = current_swidget->scomp;
1693c84443dbSChao Song 
1694c84443dbSChao Song 	if (num_pins < 1) {
1695c84443dbSChao Song 		dev_err(scomp->dev, "invalid %s num_pins: %d for queue allocation for %s\n",
1696c84443dbSChao Song 			(pin_type == SOF_PIN_TYPE_SOURCE ? "source" : "sink"),
1697c84443dbSChao Song 			num_pins, current_swidget->widget->name);
1698c84443dbSChao Song 		return -EINVAL;
1699c84443dbSChao Song 	}
1700c84443dbSChao Song 
1701c84443dbSChao Song 	/* If there is only one sink/source pin, queue id must be 0 */
1702c84443dbSChao Song 	if (num_pins == 1)
1703c84443dbSChao Song 		return 0;
1704c84443dbSChao Song 
1705c84443dbSChao Song 	/* Allocate queue ID from pin binding array if it is defined in topology. */
1706c84443dbSChao Song 	if (pin_binding) {
1707c84443dbSChao Song 		for (i = 0; i < num_pins; i++) {
1708c84443dbSChao Song 			if (!strcmp(pin_binding[i], buddy_name))
1709c84443dbSChao Song 				return i;
1710c84443dbSChao Song 		}
1711c84443dbSChao Song 		/*
1712c84443dbSChao Song 		 * Fail if no queue ID found from pin binding array, so that we don't
1713c84443dbSChao Song 		 * mixed use pin binding array and ida for queue ID allocation.
1714c84443dbSChao Song 		 */
1715c84443dbSChao Song 		dev_err(scomp->dev, "no %s queue id found from pin binding array for %s\n",
1716c84443dbSChao Song 			(pin_type == SOF_PIN_TYPE_SOURCE ? "source" : "sink"),
1717c84443dbSChao Song 			current_swidget->widget->name);
1718c84443dbSChao Song 		return -EINVAL;
1719c84443dbSChao Song 	}
1720c84443dbSChao Song 
1721c84443dbSChao Song 	/* If no pin binding array specified in topology, use ida to allocate one */
1722c84443dbSChao Song 	return ida_alloc_max(queue_ida, num_pins, GFP_KERNEL);
1723c84443dbSChao Song }
1724c84443dbSChao Song 
1725c84443dbSChao Song static void sof_ipc4_put_queue_id(struct snd_sof_widget *swidget, int queue_id,
1726c84443dbSChao Song 				  bool pin_type)
1727c84443dbSChao Song {
1728c84443dbSChao Song 	struct ida *queue_ida;
1729c84443dbSChao Song 	char **pin_binding;
1730c84443dbSChao Song 	int num_pins;
1731c84443dbSChao Song 
1732c84443dbSChao Song 	if (pin_type == SOF_PIN_TYPE_SOURCE) {
1733c84443dbSChao Song 		pin_binding = swidget->src_pin_binding;
1734c84443dbSChao Song 		queue_ida = &swidget->src_queue_ida;
1735c84443dbSChao Song 		num_pins = swidget->num_source_pins;
1736c84443dbSChao Song 	} else {
1737c84443dbSChao Song 		pin_binding = swidget->sink_pin_binding;
1738c84443dbSChao Song 		queue_ida = &swidget->sink_queue_ida;
1739c84443dbSChao Song 		num_pins = swidget->num_sink_pins;
1740c84443dbSChao Song 	}
1741c84443dbSChao Song 
1742c84443dbSChao Song 	/* Nothing to free if queue ID is not allocated with ida. */
1743c84443dbSChao Song 	if (num_pins == 1 || pin_binding)
1744c84443dbSChao Song 		return;
1745c84443dbSChao Song 
1746c84443dbSChao Song 	ida_free(queue_ida, queue_id);
1747c84443dbSChao Song }
1748c84443dbSChao Song 
174911f60563SBard Liao static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev,
175011f60563SBard Liao 					   struct snd_sof_widget *src_widget,
175111f60563SBard Liao 					   struct snd_sof_widget *sink_widget,
175211f60563SBard Liao 					   int sink_id)
175311f60563SBard Liao {
175411f60563SBard Liao 	struct sof_ipc4_base_module_cfg *sink_config = sink_widget->private;
175511f60563SBard Liao 	struct sof_ipc4_base_module_cfg *src_config;
175611f60563SBard Liao 	struct sof_ipc4_copier_config_set_sink_format format;
175711f60563SBard Liao 	struct sof_ipc4_fw_module *fw_module;
175811f60563SBard Liao 	struct sof_ipc4_msg msg = {{ 0 }};
175911f60563SBard Liao 	u32 header, extension;
176011f60563SBard Liao 
176111f60563SBard Liao 	dev_dbg(sdev->dev, "%s set copier sink %d format\n",
176211f60563SBard Liao 		src_widget->widget->name, sink_id);
176311f60563SBard Liao 
176411f60563SBard Liao 	if (WIDGET_IS_DAI(src_widget->id)) {
176511f60563SBard Liao 		struct snd_sof_dai *dai = src_widget->private;
176611f60563SBard Liao 
176711f60563SBard Liao 		src_config = dai->private;
176811f60563SBard Liao 	} else {
176911f60563SBard Liao 		src_config = src_widget->private;
177011f60563SBard Liao 	}
177111f60563SBard Liao 
177211f60563SBard Liao 	fw_module = src_widget->module_info;
177311f60563SBard Liao 
177411f60563SBard Liao 	format.sink_id = sink_id;
177511f60563SBard Liao 	memcpy(&format.source_fmt, &src_config->audio_fmt, sizeof(format.source_fmt));
177611f60563SBard Liao 	memcpy(&format.sink_fmt, &sink_config->audio_fmt, sizeof(format.sink_fmt));
177711f60563SBard Liao 	msg.data_size = sizeof(format);
177811f60563SBard Liao 	msg.data_ptr = &format;
177911f60563SBard Liao 
178011f60563SBard Liao 	header = fw_module->man4_module_entry.id;
178111f60563SBard Liao 	header |= SOF_IPC4_MOD_INSTANCE(src_widget->instance_id);
178211f60563SBard Liao 	header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_SET);
178311f60563SBard Liao 	header |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
178411f60563SBard Liao 	header |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
178511f60563SBard Liao 
178611f60563SBard Liao 	extension = SOF_IPC4_MOD_EXT_MSG_SIZE(msg.data_size);
178711f60563SBard Liao 	extension |=
178811f60563SBard Liao 		SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_COPIER_MODULE_CFG_PARAM_SET_SINK_FORMAT);
178911f60563SBard Liao 	extension |= SOF_IPC4_MOD_EXT_MSG_LAST_BLOCK(1);
179011f60563SBard Liao 	extension |= SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK(1);
179111f60563SBard Liao 
179211f60563SBard Liao 	msg.primary = header;
179311f60563SBard Liao 	msg.extension = extension;
179411f60563SBard Liao 
179511f60563SBard Liao 	return sof_ipc_tx_message(sdev->ipc, &msg, msg.data_size, NULL, 0);
179611f60563SBard Liao }
179711f60563SBard Liao 
17983acd5270SRanjani Sridharan static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *sroute)
17993acd5270SRanjani Sridharan {
18003acd5270SRanjani Sridharan 	struct snd_sof_widget *src_widget = sroute->src_widget;
18013acd5270SRanjani Sridharan 	struct snd_sof_widget *sink_widget = sroute->sink_widget;
18023acd5270SRanjani Sridharan 	struct sof_ipc4_fw_module *src_fw_module = src_widget->module_info;
18033acd5270SRanjani Sridharan 	struct sof_ipc4_fw_module *sink_fw_module = sink_widget->module_info;
18043acd5270SRanjani Sridharan 	struct sof_ipc4_msg msg = {{ 0 }};
18053acd5270SRanjani Sridharan 	u32 header, extension;
18063acd5270SRanjani Sridharan 	int ret;
18073acd5270SRanjani Sridharan 
1808e3720f92SGuennadi Liakhovetski 	if (!src_fw_module || !sink_fw_module) {
1809*de6aa72bSPeter Ujfalusi 		dev_err(sdev->dev,
1810*de6aa72bSPeter Ujfalusi 			"cannot bind %s -> %s, no firmware module for: %s%s\n",
1811*de6aa72bSPeter Ujfalusi 			src_widget->widget->name, sink_widget->widget->name,
1812*de6aa72bSPeter Ujfalusi 			src_fw_module ? "" : " source",
1813*de6aa72bSPeter Ujfalusi 			sink_fw_module ? "" : " sink");
1814*de6aa72bSPeter Ujfalusi 
1815e3720f92SGuennadi Liakhovetski 		return -ENODEV;
1816e3720f92SGuennadi Liakhovetski 	}
1817e3720f92SGuennadi Liakhovetski 
1818c84443dbSChao Song 	sroute->src_queue_id = sof_ipc4_get_queue_id(src_widget, sink_widget,
1819c84443dbSChao Song 						     SOF_PIN_TYPE_SOURCE);
1820c84443dbSChao Song 	if (sroute->src_queue_id < 0) {
1821c84443dbSChao Song 		dev_err(sdev->dev, "failed to get queue ID for source widget: %s\n",
1822c84443dbSChao Song 			src_widget->widget->name);
1823c84443dbSChao Song 		return sroute->src_queue_id;
1824c84443dbSChao Song 	}
1825c84443dbSChao Song 
1826c84443dbSChao Song 	sroute->dst_queue_id = sof_ipc4_get_queue_id(src_widget, sink_widget,
1827c84443dbSChao Song 						     SOF_PIN_TYPE_SINK);
1828c84443dbSChao Song 	if (sroute->dst_queue_id < 0) {
1829c84443dbSChao Song 		dev_err(sdev->dev, "failed to get queue ID for sink widget: %s\n",
1830c84443dbSChao Song 			sink_widget->widget->name);
1831c84443dbSChao Song 		sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id,
1832c84443dbSChao Song 				      SOF_PIN_TYPE_SOURCE);
1833c84443dbSChao Song 		return sroute->dst_queue_id;
1834c84443dbSChao Song 	}
1835c84443dbSChao Song 
183611f60563SBard Liao 	/* Pin 0 format is already set during copier module init */
183711f60563SBard Liao 	if (sroute->src_queue_id > 0 && WIDGET_IS_COPIER(src_widget->id)) {
183811f60563SBard Liao 		ret = sof_ipc4_set_copier_sink_format(sdev, src_widget, sink_widget,
183911f60563SBard Liao 						      sroute->src_queue_id);
184011f60563SBard Liao 		if (ret < 0) {
184111f60563SBard Liao 			dev_err(sdev->dev, "failed to set sink format for %s source queue ID %d\n",
184211f60563SBard Liao 				src_widget->widget->name, sroute->src_queue_id);
184311f60563SBard Liao 			goto out;
184411f60563SBard Liao 		}
184511f60563SBard Liao 	}
184611f60563SBard Liao 
1847c84443dbSChao Song 	dev_dbg(sdev->dev, "bind %s:%d -> %s:%d\n",
1848c84443dbSChao Song 		src_widget->widget->name, sroute->src_queue_id,
1849c84443dbSChao Song 		sink_widget->widget->name, sroute->dst_queue_id);
18503acd5270SRanjani Sridharan 
18513acd5270SRanjani Sridharan 	header = src_fw_module->man4_module_entry.id;
18523acd5270SRanjani Sridharan 	header |= SOF_IPC4_MOD_INSTANCE(src_widget->instance_id);
18533acd5270SRanjani Sridharan 	header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_BIND);
18543acd5270SRanjani Sridharan 	header |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
18553acd5270SRanjani Sridharan 	header |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
18563acd5270SRanjani Sridharan 
18573acd5270SRanjani Sridharan 	extension = sink_fw_module->man4_module_entry.id;
18583acd5270SRanjani Sridharan 	extension |= SOF_IPC4_MOD_EXT_DST_MOD_INSTANCE(sink_widget->instance_id);
1859c84443dbSChao Song 	extension |= SOF_IPC4_MOD_EXT_DST_MOD_QUEUE_ID(sroute->dst_queue_id);
1860c84443dbSChao Song 	extension |= SOF_IPC4_MOD_EXT_SRC_MOD_QUEUE_ID(sroute->src_queue_id);
18613acd5270SRanjani Sridharan 
18623acd5270SRanjani Sridharan 	msg.primary = header;
18633acd5270SRanjani Sridharan 	msg.extension = extension;
18643acd5270SRanjani Sridharan 
18653acd5270SRanjani Sridharan 	ret = sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0);
1866c84443dbSChao Song 	if (ret < 0) {
1867b796ff3bSRanjani Sridharan 		dev_err(sdev->dev, "failed to bind modules %s:%d -> %s:%d\n",
1868b796ff3bSRanjani Sridharan 			src_widget->widget->name, sroute->src_queue_id,
1869b796ff3bSRanjani Sridharan 			sink_widget->widget->name, sroute->dst_queue_id);
187011f60563SBard Liao 		goto out;
1871c84443dbSChao Song 	}
1872c84443dbSChao Song 
18733acd5270SRanjani Sridharan 	return ret;
187411f60563SBard Liao 
187511f60563SBard Liao out:
187611f60563SBard Liao 	sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_SOURCE);
187711f60563SBard Liao 	sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id, SOF_PIN_TYPE_SINK);
187811f60563SBard Liao 	return ret;
18793acd5270SRanjani Sridharan }
18803acd5270SRanjani Sridharan 
18813acd5270SRanjani Sridharan static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *sroute)
18823acd5270SRanjani Sridharan {
18833acd5270SRanjani Sridharan 	struct snd_sof_widget *src_widget = sroute->src_widget;
18843acd5270SRanjani Sridharan 	struct snd_sof_widget *sink_widget = sroute->sink_widget;
18853acd5270SRanjani Sridharan 	struct sof_ipc4_fw_module *src_fw_module = src_widget->module_info;
18863acd5270SRanjani Sridharan 	struct sof_ipc4_fw_module *sink_fw_module = sink_widget->module_info;
18873acd5270SRanjani Sridharan 	struct sof_ipc4_msg msg = {{ 0 }};
18883acd5270SRanjani Sridharan 	u32 header, extension;
18899a62d87aSRanjani Sridharan 	int ret = 0;
18903acd5270SRanjani Sridharan 
1891c84443dbSChao Song 	dev_dbg(sdev->dev, "unbind modules %s:%d -> %s:%d\n",
1892c84443dbSChao Song 		src_widget->widget->name, sroute->src_queue_id,
1893c84443dbSChao Song 		sink_widget->widget->name, sroute->dst_queue_id);
18943acd5270SRanjani Sridharan 
18959a62d87aSRanjani Sridharan 	/*
18969a62d87aSRanjani Sridharan 	 * routes belonging to the same pipeline will be disconnected by the FW when the pipeline
18979a62d87aSRanjani Sridharan 	 * is freed. So avoid sending this IPC which will be ignored by the FW anyway.
18989a62d87aSRanjani Sridharan 	 */
18999c04363dSRanjani Sridharan 	if (src_widget->spipe->pipe_widget == sink_widget->spipe->pipe_widget)
19009a62d87aSRanjani Sridharan 		goto out;
19019a62d87aSRanjani Sridharan 
19023acd5270SRanjani Sridharan 	header = src_fw_module->man4_module_entry.id;
19033acd5270SRanjani Sridharan 	header |= SOF_IPC4_MOD_INSTANCE(src_widget->instance_id);
19043acd5270SRanjani Sridharan 	header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_UNBIND);
19053acd5270SRanjani Sridharan 	header |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
19063acd5270SRanjani Sridharan 	header |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
19073acd5270SRanjani Sridharan 
19083acd5270SRanjani Sridharan 	extension = sink_fw_module->man4_module_entry.id;
19093acd5270SRanjani Sridharan 	extension |= SOF_IPC4_MOD_EXT_DST_MOD_INSTANCE(sink_widget->instance_id);
1910c84443dbSChao Song 	extension |= SOF_IPC4_MOD_EXT_DST_MOD_QUEUE_ID(sroute->dst_queue_id);
1911c84443dbSChao Song 	extension |= SOF_IPC4_MOD_EXT_SRC_MOD_QUEUE_ID(sroute->src_queue_id);
19123acd5270SRanjani Sridharan 
19133acd5270SRanjani Sridharan 	msg.primary = header;
19143acd5270SRanjani Sridharan 	msg.extension = extension;
19153acd5270SRanjani Sridharan 
19163acd5270SRanjani Sridharan 	ret = sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0);
19173acd5270SRanjani Sridharan 	if (ret < 0)
1918b796ff3bSRanjani Sridharan 		dev_err(sdev->dev, "failed to unbind modules %s:%d -> %s:%d\n",
1919b796ff3bSRanjani Sridharan 			src_widget->widget->name, sroute->src_queue_id,
1920b796ff3bSRanjani Sridharan 			sink_widget->widget->name, sroute->dst_queue_id);
19219a62d87aSRanjani Sridharan out:
1922c84443dbSChao Song 	sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id, SOF_PIN_TYPE_SINK);
1923c84443dbSChao Song 	sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_SOURCE);
1924c84443dbSChao Song 
19253acd5270SRanjani Sridharan 	return ret;
19263acd5270SRanjani Sridharan }
19273acd5270SRanjani Sridharan 
1928acf48a1fSRanjani Sridharan static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,
1929acf48a1fSRanjani Sridharan 			       unsigned int flags, struct snd_sof_dai_config_data *data)
1930acf48a1fSRanjani Sridharan {
19319c04363dSRanjani Sridharan 	struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
1932acf48a1fSRanjani Sridharan 	struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
1933acf48a1fSRanjani Sridharan 	struct snd_sof_dai *dai = swidget->private;
1934acf48a1fSRanjani Sridharan 	struct sof_ipc4_gtw_attributes *gtw_attr;
1935acf48a1fSRanjani Sridharan 	struct sof_ipc4_copier_data *copier_data;
1936acf48a1fSRanjani Sridharan 	struct sof_ipc4_copier *ipc4_copier;
1937acf48a1fSRanjani Sridharan 
1938acf48a1fSRanjani Sridharan 	if (!dai || !dai->private) {
1939acf48a1fSRanjani Sridharan 		dev_err(sdev->dev, "Invalid DAI or DAI private data for %s\n",
1940acf48a1fSRanjani Sridharan 			swidget->widget->name);
1941acf48a1fSRanjani Sridharan 		return -EINVAL;
1942acf48a1fSRanjani Sridharan 	}
1943acf48a1fSRanjani Sridharan 
1944acf48a1fSRanjani Sridharan 	ipc4_copier = (struct sof_ipc4_copier *)dai->private;
1945acf48a1fSRanjani Sridharan 	copier_data = &ipc4_copier->data;
1946acf48a1fSRanjani Sridharan 
1947acf48a1fSRanjani Sridharan 	if (!data)
1948acf48a1fSRanjani Sridharan 		return 0;
1949acf48a1fSRanjani Sridharan 
1950acf48a1fSRanjani Sridharan 	switch (ipc4_copier->dai_type) {
1951acf48a1fSRanjani Sridharan 	case SOF_DAI_INTEL_HDA:
1952acf48a1fSRanjani Sridharan 		gtw_attr = ipc4_copier->gtw_attr;
1953acf48a1fSRanjani Sridharan 		gtw_attr->lp_buffer_alloc = pipeline->lp_mode;
195437a26eecSRanjani Sridharan 		pipeline->skip_during_fe_trigger = true;
1955acf48a1fSRanjani Sridharan 		fallthrough;
1956acf48a1fSRanjani Sridharan 	case SOF_DAI_INTEL_ALH:
1957b66bfc3aSRanjani Sridharan 		/*
1958b66bfc3aSRanjani Sridharan 		 * Do not clear the node ID when this op is invoked with
1959b66bfc3aSRanjani Sridharan 		 * SOF_DAI_CONFIG_FLAGS_HW_FREE. It is needed to free the group_ida during
1960b66bfc3aSRanjani Sridharan 		 * unprepare.
1961b66bfc3aSRanjani Sridharan 		 */
1962b66bfc3aSRanjani Sridharan 		if (flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS) {
1963acf48a1fSRanjani Sridharan 			copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
1964acf48a1fSRanjani Sridharan 			copier_data->gtw_cfg.node_id |= SOF_IPC4_NODE_INDEX(data->dai_data);
1965b66bfc3aSRanjani Sridharan 		}
1966acf48a1fSRanjani Sridharan 		break;
1967acf48a1fSRanjani Sridharan 	case SOF_DAI_INTEL_DMIC:
1968acf48a1fSRanjani Sridharan 	case SOF_DAI_INTEL_SSP:
1969acf48a1fSRanjani Sridharan 		/* nothing to do for SSP/DMIC */
1970acf48a1fSRanjani Sridharan 		break;
1971acf48a1fSRanjani Sridharan 	default:
1972acf48a1fSRanjani Sridharan 		dev_err(sdev->dev, "%s: unsupported dai type %d\n", __func__,
1973acf48a1fSRanjani Sridharan 			ipc4_copier->dai_type);
1974acf48a1fSRanjani Sridharan 		return -EINVAL;
1975acf48a1fSRanjani Sridharan 	}
1976acf48a1fSRanjani Sridharan 
1977acf48a1fSRanjani Sridharan 	return 0;
1978acf48a1fSRanjani Sridharan }
1979acf48a1fSRanjani Sridharan 
1980323aa1f0SRanjani Sridharan static int sof_ipc4_parse_manifest(struct snd_soc_component *scomp, int index,
1981323aa1f0SRanjani Sridharan 				   struct snd_soc_tplg_manifest *man)
1982323aa1f0SRanjani Sridharan {
1983323aa1f0SRanjani Sridharan 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
1984323aa1f0SRanjani Sridharan 	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
1985323aa1f0SRanjani Sridharan 	struct sof_manifest_tlv *manifest_tlv;
1986323aa1f0SRanjani Sridharan 	struct sof_manifest *manifest;
1987323aa1f0SRanjani Sridharan 	u32 size = le32_to_cpu(man->priv.size);
1988323aa1f0SRanjani Sridharan 	u8 *man_ptr = man->priv.data;
1989323aa1f0SRanjani Sridharan 	u32 len_check;
1990323aa1f0SRanjani Sridharan 	int i;
1991323aa1f0SRanjani Sridharan 
1992323aa1f0SRanjani Sridharan 	if (!size || size < SOF_IPC4_TPLG_ABI_SIZE) {
1993323aa1f0SRanjani Sridharan 		dev_err(scomp->dev, "%s: Invalid topology ABI size: %u\n",
1994323aa1f0SRanjani Sridharan 			__func__, size);
1995323aa1f0SRanjani Sridharan 		return -EINVAL;
1996323aa1f0SRanjani Sridharan 	}
1997323aa1f0SRanjani Sridharan 
1998323aa1f0SRanjani Sridharan 	manifest = (struct sof_manifest *)man_ptr;
1999323aa1f0SRanjani Sridharan 
2000323aa1f0SRanjani Sridharan 	dev_info(scomp->dev,
2001323aa1f0SRanjani Sridharan 		 "Topology: ABI %d:%d:%d Kernel ABI %u:%u:%u\n",
2002323aa1f0SRanjani Sridharan 		  le16_to_cpu(manifest->abi_major), le16_to_cpu(manifest->abi_minor),
2003323aa1f0SRanjani Sridharan 		  le16_to_cpu(manifest->abi_patch),
2004323aa1f0SRanjani Sridharan 		  SOF_ABI_MAJOR, SOF_ABI_MINOR, SOF_ABI_PATCH);
2005323aa1f0SRanjani Sridharan 
2006323aa1f0SRanjani Sridharan 	/* TODO: Add ABI compatibility check */
2007323aa1f0SRanjani Sridharan 
2008323aa1f0SRanjani Sridharan 	/* no more data after the ABI version */
2009323aa1f0SRanjani Sridharan 	if (size <= SOF_IPC4_TPLG_ABI_SIZE)
2010323aa1f0SRanjani Sridharan 		return 0;
2011323aa1f0SRanjani Sridharan 
2012323aa1f0SRanjani Sridharan 	manifest_tlv = manifest->items;
2013323aa1f0SRanjani Sridharan 	len_check = sizeof(struct sof_manifest);
2014323aa1f0SRanjani Sridharan 	for (i = 0; i < le16_to_cpu(manifest->count); i++) {
2015323aa1f0SRanjani Sridharan 		len_check += sizeof(struct sof_manifest_tlv) + le32_to_cpu(manifest_tlv->size);
2016323aa1f0SRanjani Sridharan 		if (len_check > size)
2017323aa1f0SRanjani Sridharan 			return -EINVAL;
2018323aa1f0SRanjani Sridharan 
2019323aa1f0SRanjani Sridharan 		switch (le32_to_cpu(manifest_tlv->type)) {
2020323aa1f0SRanjani Sridharan 		case SOF_MANIFEST_DATA_TYPE_NHLT:
2021323aa1f0SRanjani Sridharan 			/* no NHLT in BIOS, so use the one from topology manifest */
2022323aa1f0SRanjani Sridharan 			if (ipc4_data->nhlt)
2023323aa1f0SRanjani Sridharan 				break;
2024323aa1f0SRanjani Sridharan 			ipc4_data->nhlt = devm_kmemdup(sdev->dev, manifest_tlv->data,
2025323aa1f0SRanjani Sridharan 						       le32_to_cpu(manifest_tlv->size), GFP_KERNEL);
2026323aa1f0SRanjani Sridharan 			if (!ipc4_data->nhlt)
2027323aa1f0SRanjani Sridharan 				return -ENOMEM;
2028323aa1f0SRanjani Sridharan 			break;
2029323aa1f0SRanjani Sridharan 		default:
2030323aa1f0SRanjani Sridharan 			dev_warn(scomp->dev, "Skipping unknown manifest data type %d\n",
2031323aa1f0SRanjani Sridharan 				 manifest_tlv->type);
2032323aa1f0SRanjani Sridharan 			break;
2033323aa1f0SRanjani Sridharan 		}
2034323aa1f0SRanjani Sridharan 		man_ptr += sizeof(struct sof_manifest_tlv) + le32_to_cpu(manifest_tlv->size);
2035323aa1f0SRanjani Sridharan 		manifest_tlv = (struct sof_manifest_tlv *)man_ptr;
2036323aa1f0SRanjani Sridharan 	}
2037323aa1f0SRanjani Sridharan 
2038323aa1f0SRanjani Sridharan 	return 0;
2039323aa1f0SRanjani Sridharan }
2040323aa1f0SRanjani Sridharan 
20419e2b5d33SRanjani Sridharan static int sof_ipc4_dai_get_clk(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int clk_type)
20429e2b5d33SRanjani Sridharan {
20439e2b5d33SRanjani Sridharan 	struct sof_ipc4_copier *ipc4_copier = dai->private;
20449e2b5d33SRanjani Sridharan 	struct snd_soc_tplg_hw_config *hw_config;
20459e2b5d33SRanjani Sridharan 	struct snd_sof_dai_link *slink;
20469e2b5d33SRanjani Sridharan 	bool dai_link_found = false;
20479e2b5d33SRanjani Sridharan 	bool hw_cfg_found = false;
20489e2b5d33SRanjani Sridharan 	int i;
20499e2b5d33SRanjani Sridharan 
20509e2b5d33SRanjani Sridharan 	if (!ipc4_copier)
20519e2b5d33SRanjani Sridharan 		return 0;
20529e2b5d33SRanjani Sridharan 
20539e2b5d33SRanjani Sridharan 	list_for_each_entry(slink, &sdev->dai_link_list, list) {
20549e2b5d33SRanjani Sridharan 		if (!strcmp(slink->link->name, dai->name)) {
20559e2b5d33SRanjani Sridharan 			dai_link_found = true;
20569e2b5d33SRanjani Sridharan 			break;
20579e2b5d33SRanjani Sridharan 		}
20589e2b5d33SRanjani Sridharan 	}
20599e2b5d33SRanjani Sridharan 
20609e2b5d33SRanjani Sridharan 	if (!dai_link_found) {
20619e2b5d33SRanjani Sridharan 		dev_err(sdev->dev, "no DAI link found for DAI %s\n", dai->name);
20629e2b5d33SRanjani Sridharan 		return -EINVAL;
20639e2b5d33SRanjani Sridharan 	}
20649e2b5d33SRanjani Sridharan 
20659e2b5d33SRanjani Sridharan 	for (i = 0; i < slink->num_hw_configs; i++) {
20669e2b5d33SRanjani Sridharan 		hw_config = &slink->hw_configs[i];
20679e2b5d33SRanjani Sridharan 		if (dai->current_config == le32_to_cpu(hw_config->id)) {
20689e2b5d33SRanjani Sridharan 			hw_cfg_found = true;
20699e2b5d33SRanjani Sridharan 			break;
20709e2b5d33SRanjani Sridharan 		}
20719e2b5d33SRanjani Sridharan 	}
20729e2b5d33SRanjani Sridharan 
20739e2b5d33SRanjani Sridharan 	if (!hw_cfg_found) {
20749e2b5d33SRanjani Sridharan 		dev_err(sdev->dev, "no matching hw_config found for DAI %s\n", dai->name);
20759e2b5d33SRanjani Sridharan 		return -EINVAL;
20769e2b5d33SRanjani Sridharan 	}
20779e2b5d33SRanjani Sridharan 
20789e2b5d33SRanjani Sridharan 	switch (ipc4_copier->dai_type) {
20799e2b5d33SRanjani Sridharan 	case SOF_DAI_INTEL_SSP:
20809e2b5d33SRanjani Sridharan 		switch (clk_type) {
20819e2b5d33SRanjani Sridharan 		case SOF_DAI_CLK_INTEL_SSP_MCLK:
20829e2b5d33SRanjani Sridharan 			return le32_to_cpu(hw_config->mclk_rate);
20839e2b5d33SRanjani Sridharan 		case SOF_DAI_CLK_INTEL_SSP_BCLK:
20849e2b5d33SRanjani Sridharan 			return le32_to_cpu(hw_config->bclk_rate);
20859e2b5d33SRanjani Sridharan 		default:
20869e2b5d33SRanjani Sridharan 			dev_err(sdev->dev, "Invalid clk type for SSP %d\n", clk_type);
20879e2b5d33SRanjani Sridharan 			break;
20889e2b5d33SRanjani Sridharan 		}
20899e2b5d33SRanjani Sridharan 		break;
20909e2b5d33SRanjani Sridharan 	default:
20919e2b5d33SRanjani Sridharan 		dev_err(sdev->dev, "DAI type %d not supported yet!\n", ipc4_copier->dai_type);
20929e2b5d33SRanjani Sridharan 		break;
20939e2b5d33SRanjani Sridharan 	}
20949e2b5d33SRanjani Sridharan 
20959e2b5d33SRanjani Sridharan 	return -EINVAL;
20969e2b5d33SRanjani Sridharan }
20979e2b5d33SRanjani Sridharan 
209818cd1f32SPeter Ujfalusi static int sof_ipc4_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verify)
209918cd1f32SPeter Ujfalusi {
210018cd1f32SPeter Ujfalusi 	struct snd_sof_pcm *spcm;
210118cd1f32SPeter Ujfalusi 	int dir, ret;
210218cd1f32SPeter Ujfalusi 
210318cd1f32SPeter Ujfalusi 	/*
210418cd1f32SPeter Ujfalusi 	 * This function is called during system suspend, we need to make sure
210518cd1f32SPeter Ujfalusi 	 * that all streams have been freed up.
210618cd1f32SPeter Ujfalusi 	 * Freeing might have been skipped when xrun happened just at the start
210718cd1f32SPeter Ujfalusi 	 * of the suspend and it sent a SNDRV_PCM_TRIGGER_STOP to the active
210818cd1f32SPeter Ujfalusi 	 * stream. This will call sof_pcm_stream_free() with
210918cd1f32SPeter Ujfalusi 	 * free_widget_list = false which will leave the kernel and firmware out
211018cd1f32SPeter Ujfalusi 	 * of sync during suspend/resume.
211118cd1f32SPeter Ujfalusi 	 *
211218cd1f32SPeter Ujfalusi 	 * This will also make sure that paused streams handled correctly.
211318cd1f32SPeter Ujfalusi 	 */
211418cd1f32SPeter Ujfalusi 	list_for_each_entry(spcm, &sdev->pcm_list, list) {
211518cd1f32SPeter Ujfalusi 		for_each_pcm_streams(dir) {
211618cd1f32SPeter Ujfalusi 			struct snd_pcm_substream *substream = spcm->stream[dir].substream;
211718cd1f32SPeter Ujfalusi 
211882b18242SRanjani Sridharan 			if (!substream || !substream->runtime || spcm->stream[dir].suspend_ignored)
211918cd1f32SPeter Ujfalusi 				continue;
212018cd1f32SPeter Ujfalusi 
212118cd1f32SPeter Ujfalusi 			if (spcm->stream[dir].list) {
212218cd1f32SPeter Ujfalusi 				ret = sof_pcm_stream_free(sdev, substream, spcm, dir, true);
212318cd1f32SPeter Ujfalusi 				if (ret < 0)
212418cd1f32SPeter Ujfalusi 					return ret;
212518cd1f32SPeter Ujfalusi 			}
212618cd1f32SPeter Ujfalusi 		}
212718cd1f32SPeter Ujfalusi 	}
212818cd1f32SPeter Ujfalusi 	return 0;
212918cd1f32SPeter Ujfalusi }
213018cd1f32SPeter Ujfalusi 
2131e380c907SRanjani Sridharan static int sof_ipc4_link_setup(struct snd_sof_dev *sdev, struct snd_soc_dai_link *link)
2132e380c907SRanjani Sridharan {
2133e380c907SRanjani Sridharan 	if (link->no_pcm)
2134e380c907SRanjani Sridharan 		return 0;
2135e380c907SRanjani Sridharan 
2136e380c907SRanjani Sridharan 	/*
2137e380c907SRanjani Sridharan 	 * set default trigger order for all links. Exceptions to
2138e380c907SRanjani Sridharan 	 * the rule will be handled in sof_pcm_dai_link_fixup()
2139e380c907SRanjani Sridharan 	 * For playback, the sequence is the following: start BE,
2140e380c907SRanjani Sridharan 	 * start FE, stop FE, stop BE; for Capture the sequence is
2141e380c907SRanjani Sridharan 	 * inverted start FE, start BE, stop BE, stop FE
2142e380c907SRanjani Sridharan 	 */
2143e380c907SRanjani Sridharan 	link->trigger[SNDRV_PCM_STREAM_PLAYBACK] = SND_SOC_DPCM_TRIGGER_POST;
2144e380c907SRanjani Sridharan 	link->trigger[SNDRV_PCM_STREAM_CAPTURE] = SND_SOC_DPCM_TRIGGER_PRE;
2145e380c907SRanjani Sridharan 
2146e380c907SRanjani Sridharan 	return 0;
2147e380c907SRanjani Sridharan }
2148e380c907SRanjani Sridharan 
21497d573425SBard Liao static enum sof_tokens common_copier_token_list[] = {
21502cabd02bSRanjani Sridharan 	SOF_COMP_TOKENS,
21512cabd02bSRanjani Sridharan 	SOF_AUDIO_FMT_NUM_TOKENS,
21522cabd02bSRanjani Sridharan 	SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS,
21532cabd02bSRanjani Sridharan 	SOF_IN_AUDIO_FORMAT_TOKENS,
21542cabd02bSRanjani Sridharan 	SOF_OUT_AUDIO_FORMAT_TOKENS,
21552cabd02bSRanjani Sridharan 	SOF_COPIER_GATEWAY_CFG_TOKENS,
21562cabd02bSRanjani Sridharan 	SOF_COPIER_TOKENS,
21572cabd02bSRanjani Sridharan 	SOF_COMP_EXT_TOKENS,
21582cabd02bSRanjani Sridharan };
21592cabd02bSRanjani Sridharan 
216090e89155SRanjani Sridharan static enum sof_tokens pipeline_token_list[] = {
216190e89155SRanjani Sridharan 	SOF_SCHED_TOKENS,
216290e89155SRanjani Sridharan 	SOF_PIPELINE_TOKENS,
216390e89155SRanjani Sridharan };
216490e89155SRanjani Sridharan 
2165abfb536bSRanjani Sridharan static enum sof_tokens dai_token_list[] = {
2166abfb536bSRanjani Sridharan 	SOF_COMP_TOKENS,
2167abfb536bSRanjani Sridharan 	SOF_AUDIO_FMT_NUM_TOKENS,
2168abfb536bSRanjani Sridharan 	SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS,
2169abfb536bSRanjani Sridharan 	SOF_IN_AUDIO_FORMAT_TOKENS,
2170abfb536bSRanjani Sridharan 	SOF_OUT_AUDIO_FORMAT_TOKENS,
2171abfb536bSRanjani Sridharan 	SOF_COPIER_GATEWAY_CFG_TOKENS,
2172abfb536bSRanjani Sridharan 	SOF_COPIER_TOKENS,
2173abfb536bSRanjani Sridharan 	SOF_DAI_TOKENS,
2174abfb536bSRanjani Sridharan 	SOF_COMP_EXT_TOKENS,
2175abfb536bSRanjani Sridharan };
2176abfb536bSRanjani Sridharan 
21774f838ab2SRanjani Sridharan static enum sof_tokens pga_token_list[] = {
21784f838ab2SRanjani Sridharan 	SOF_COMP_TOKENS,
21794f838ab2SRanjani Sridharan 	SOF_GAIN_TOKENS,
21804f838ab2SRanjani Sridharan 	SOF_AUDIO_FMT_NUM_TOKENS,
21814f838ab2SRanjani Sridharan 	SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS,
21824f838ab2SRanjani Sridharan 	SOF_IN_AUDIO_FORMAT_TOKENS,
21834f838ab2SRanjani Sridharan 	SOF_COMP_EXT_TOKENS,
21844f838ab2SRanjani Sridharan };
21854f838ab2SRanjani Sridharan 
21864d4ba014SRanjani Sridharan static enum sof_tokens mixer_token_list[] = {
21874d4ba014SRanjani Sridharan 	SOF_COMP_TOKENS,
21884d4ba014SRanjani Sridharan 	SOF_AUDIO_FMT_NUM_TOKENS,
21894d4ba014SRanjani Sridharan 	SOF_IN_AUDIO_FORMAT_TOKENS,
21904d4ba014SRanjani Sridharan 	SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS,
21914d4ba014SRanjani Sridharan 	SOF_COMP_EXT_TOKENS,
21924d4ba014SRanjani Sridharan };
21934d4ba014SRanjani Sridharan 
2194b85f4fc4SRander Wang static enum sof_tokens src_token_list[] = {
2195b85f4fc4SRander Wang 	SOF_COMP_TOKENS,
2196b85f4fc4SRander Wang 	SOF_SRC_TOKENS,
2197b85f4fc4SRander Wang 	SOF_AUDIO_FMT_NUM_TOKENS,
2198b85f4fc4SRander Wang 	SOF_IN_AUDIO_FORMAT_TOKENS,
2199b85f4fc4SRander Wang 	SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS,
2200b85f4fc4SRander Wang 	SOF_COMP_EXT_TOKENS,
2201b85f4fc4SRander Wang };
2202b85f4fc4SRander Wang 
220390e89155SRanjani Sridharan static const struct sof_ipc_tplg_widget_ops tplg_ipc4_widget_ops[SND_SOC_DAPM_TYPE_COUNT] = {
22042cabd02bSRanjani Sridharan 	[snd_soc_dapm_aif_in] =  {sof_ipc4_widget_setup_pcm, sof_ipc4_widget_free_comp_pcm,
22057d573425SBard Liao 				  common_copier_token_list, ARRAY_SIZE(common_copier_token_list),
22067d573425SBard Liao 				  NULL, sof_ipc4_prepare_copier_module,
2207904c48c4SRanjani Sridharan 				  sof_ipc4_unprepare_copier_module},
22082cabd02bSRanjani Sridharan 	[snd_soc_dapm_aif_out] = {sof_ipc4_widget_setup_pcm, sof_ipc4_widget_free_comp_pcm,
22097d573425SBard Liao 				  common_copier_token_list, ARRAY_SIZE(common_copier_token_list),
22107d573425SBard Liao 				  NULL, sof_ipc4_prepare_copier_module,
2211904c48c4SRanjani Sridharan 				  sof_ipc4_unprepare_copier_module},
2212abfb536bSRanjani Sridharan 	[snd_soc_dapm_dai_in] = {sof_ipc4_widget_setup_comp_dai, sof_ipc4_widget_free_comp_dai,
2213acf52594SRanjani Sridharan 				 dai_token_list, ARRAY_SIZE(dai_token_list), NULL,
2214acf52594SRanjani Sridharan 				 sof_ipc4_prepare_copier_module,
2215acf52594SRanjani Sridharan 				 sof_ipc4_unprepare_copier_module},
2216abfb536bSRanjani Sridharan 	[snd_soc_dapm_dai_out] = {sof_ipc4_widget_setup_comp_dai, sof_ipc4_widget_free_comp_dai,
2217acf52594SRanjani Sridharan 				  dai_token_list, ARRAY_SIZE(dai_token_list), NULL,
2218acf52594SRanjani Sridharan 				  sof_ipc4_prepare_copier_module,
2219acf52594SRanjani Sridharan 				  sof_ipc4_unprepare_copier_module},
22207d573425SBard Liao 	[snd_soc_dapm_buffer] = {sof_ipc4_widget_setup_pcm, sof_ipc4_widget_free_comp_pcm,
22217d573425SBard Liao 				 common_copier_token_list, ARRAY_SIZE(common_copier_token_list),
22227d573425SBard Liao 				 NULL, sof_ipc4_prepare_copier_module,
22237d573425SBard Liao 				 sof_ipc4_unprepare_copier_module},
2224a29b2d02SBard Liao 	[snd_soc_dapm_scheduler] = {sof_ipc4_widget_setup_comp_pipeline,
2225a29b2d02SBard Liao 				    sof_ipc4_widget_free_comp_pipeline,
222690e89155SRanjani Sridharan 				    pipeline_token_list, ARRAY_SIZE(pipeline_token_list), NULL,
222790e89155SRanjani Sridharan 				    NULL, NULL},
2228dc4fc0aeSLibin Yang 	[snd_soc_dapm_pga] = {sof_ipc4_widget_setup_comp_pga, sof_ipc4_widget_free_comp_pga,
22294f838ab2SRanjani Sridharan 			      pga_token_list, ARRAY_SIZE(pga_token_list), NULL,
22304f838ab2SRanjani Sridharan 			      sof_ipc4_prepare_gain_module,
2231711d0427SBard Liao 			      NULL},
2232dc4fc0aeSLibin Yang 	[snd_soc_dapm_mixer] = {sof_ipc4_widget_setup_comp_mixer, sof_ipc4_widget_free_comp_mixer,
22334d4ba014SRanjani Sridharan 				mixer_token_list, ARRAY_SIZE(mixer_token_list),
22344d4ba014SRanjani Sridharan 				NULL, sof_ipc4_prepare_mixer_module,
2235711d0427SBard Liao 				NULL},
2236b85f4fc4SRander Wang 	[snd_soc_dapm_src] = {sof_ipc4_widget_setup_comp_src, sof_ipc4_widget_free_comp_src,
2237b85f4fc4SRander Wang 				src_token_list, ARRAY_SIZE(src_token_list),
2238b85f4fc4SRander Wang 				NULL, sof_ipc4_prepare_src_module,
2239b85f4fc4SRander Wang 				NULL},
224090e89155SRanjani Sridharan };
224190e89155SRanjani Sridharan 
224290e89155SRanjani Sridharan const struct sof_ipc_tplg_ops ipc4_tplg_ops = {
224390e89155SRanjani Sridharan 	.widget = tplg_ipc4_widget_ops,
224490e89155SRanjani Sridharan 	.token_list = ipc4_token_list,
2245d97964f8SRanjani Sridharan 	.control_setup = sof_ipc4_control_setup,
2246955e84fcSRanjani Sridharan 	.control = &tplg_ipc4_control_ops,
22476e9257a1SRanjani Sridharan 	.widget_setup = sof_ipc4_widget_setup,
22486e9257a1SRanjani Sridharan 	.widget_free = sof_ipc4_widget_free,
22493acd5270SRanjani Sridharan 	.route_setup = sof_ipc4_route_setup,
22503acd5270SRanjani Sridharan 	.route_free = sof_ipc4_route_free,
2251acf48a1fSRanjani Sridharan 	.dai_config = sof_ipc4_dai_config,
2252323aa1f0SRanjani Sridharan 	.parse_manifest = sof_ipc4_parse_manifest,
22539e2b5d33SRanjani Sridharan 	.dai_get_clk = sof_ipc4_dai_get_clk,
225418cd1f32SPeter Ujfalusi 	.tear_down_all_pipelines = sof_ipc4_tear_down_all_pipelines,
2255e380c907SRanjani Sridharan 	.link_setup = sof_ipc4_link_setup,
225690e89155SRanjani Sridharan };
2257