162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// This file is provided under a dual BSD/GPLv2 license.  When using or
462306a36Sopenharmony_ci// redistributing this file, you may do so under either license.
562306a36Sopenharmony_ci//
662306a36Sopenharmony_ci// Copyright(c) 2022 Intel Corporation. All rights reserved.
762306a36Sopenharmony_ci//
862306a36Sopenharmony_ci//
962306a36Sopenharmony_ci#include <linux/bitfield.h>
1062306a36Sopenharmony_ci#include <uapi/sound/sof/tokens.h>
1162306a36Sopenharmony_ci#include <sound/pcm_params.h>
1262306a36Sopenharmony_ci#include <sound/sof/ext_manifest4.h>
1362306a36Sopenharmony_ci#include <sound/intel-nhlt.h>
1462306a36Sopenharmony_ci#include "sof-priv.h"
1562306a36Sopenharmony_ci#include "sof-audio.h"
1662306a36Sopenharmony_ci#include "ipc4-priv.h"
1762306a36Sopenharmony_ci#include "ipc4-topology.h"
1862306a36Sopenharmony_ci#include "ops.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/*
2162306a36Sopenharmony_ci * The ignore_cpc flag can be used to ignore the CPC value for all modules by
2262306a36Sopenharmony_ci * using 0 instead.
2362306a36Sopenharmony_ci * The CPC is sent to the firmware along with the SOF_IPC4_MOD_INIT_INSTANCE
2462306a36Sopenharmony_ci * message and it is used for clock scaling.
2562306a36Sopenharmony_ci * 0 as CPC value will instruct the firmware to use maximum frequency, thus
2662306a36Sopenharmony_ci * deactivating the clock scaling.
2762306a36Sopenharmony_ci */
2862306a36Sopenharmony_cistatic bool ignore_cpc;
2962306a36Sopenharmony_cimodule_param_named(ipc4_ignore_cpc, ignore_cpc, bool, 0444);
3062306a36Sopenharmony_ciMODULE_PARM_DESC(ipc4_ignore_cpc,
3162306a36Sopenharmony_ci		 "Ignore CPC values. This option will disable clock scaling in firmware.");
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define SOF_IPC4_GAIN_PARAM_ID  0
3462306a36Sopenharmony_ci#define SOF_IPC4_TPLG_ABI_SIZE 6
3562306a36Sopenharmony_ci#define SOF_IPC4_CHAIN_DMA_BUF_SIZE_MS 2
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic DEFINE_IDA(alh_group_ida);
3862306a36Sopenharmony_cistatic DEFINE_IDA(pipeline_ida);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic const struct sof_topology_token ipc4_sched_tokens[] = {
4162306a36Sopenharmony_ci	{SOF_TKN_SCHED_LP_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
4262306a36Sopenharmony_ci		offsetof(struct sof_ipc4_pipeline, lp_mode)},
4362306a36Sopenharmony_ci	{SOF_TKN_SCHED_USE_CHAIN_DMA, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16,
4462306a36Sopenharmony_ci		offsetof(struct sof_ipc4_pipeline, use_chain_dma)},
4562306a36Sopenharmony_ci	{SOF_TKN_SCHED_CORE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
4662306a36Sopenharmony_ci		offsetof(struct sof_ipc4_pipeline, core_id)},
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic const struct sof_topology_token pipeline_tokens[] = {
5062306a36Sopenharmony_ci	{SOF_TKN_SCHED_DYNAMIC_PIPELINE, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16,
5162306a36Sopenharmony_ci		offsetof(struct snd_sof_widget, dynamic_pipeline_widget)},
5262306a36Sopenharmony_ci};
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic const struct sof_topology_token ipc4_comp_tokens[] = {
5562306a36Sopenharmony_ci	{SOF_TKN_COMP_IS_PAGES, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
5662306a36Sopenharmony_ci		offsetof(struct sof_ipc4_base_module_cfg, is_pages)},
5762306a36Sopenharmony_ci};
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic const struct sof_topology_token ipc4_in_audio_format_tokens[] = {
6062306a36Sopenharmony_ci	{SOF_TKN_CAVS_AUDIO_FORMAT_IN_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
6162306a36Sopenharmony_ci		offsetof(struct sof_ipc4_pin_format, audio_fmt.sampling_frequency)},
6262306a36Sopenharmony_ci	{SOF_TKN_CAVS_AUDIO_FORMAT_IN_BIT_DEPTH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
6362306a36Sopenharmony_ci		offsetof(struct sof_ipc4_pin_format, audio_fmt.bit_depth)},
6462306a36Sopenharmony_ci	{SOF_TKN_CAVS_AUDIO_FORMAT_IN_CH_MAP, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
6562306a36Sopenharmony_ci		offsetof(struct sof_ipc4_pin_format, audio_fmt.ch_map)},
6662306a36Sopenharmony_ci	{SOF_TKN_CAVS_AUDIO_FORMAT_IN_CH_CFG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
6762306a36Sopenharmony_ci		offsetof(struct sof_ipc4_pin_format, audio_fmt.ch_cfg)},
6862306a36Sopenharmony_ci	{SOF_TKN_CAVS_AUDIO_FORMAT_IN_INTERLEAVING_STYLE, SND_SOC_TPLG_TUPLE_TYPE_WORD,
6962306a36Sopenharmony_ci		get_token_u32, offsetof(struct sof_ipc4_pin_format,
7062306a36Sopenharmony_ci		audio_fmt.interleaving_style)},
7162306a36Sopenharmony_ci	{SOF_TKN_CAVS_AUDIO_FORMAT_IN_FMT_CFG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
7262306a36Sopenharmony_ci		offsetof(struct sof_ipc4_pin_format, audio_fmt.fmt_cfg)},
7362306a36Sopenharmony_ci	{SOF_TKN_CAVS_AUDIO_FORMAT_INPUT_PIN_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
7462306a36Sopenharmony_ci		offsetof(struct sof_ipc4_pin_format, pin_index)},
7562306a36Sopenharmony_ci	{SOF_TKN_CAVS_AUDIO_FORMAT_IBS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
7662306a36Sopenharmony_ci		offsetof(struct sof_ipc4_pin_format, buffer_size)},
7762306a36Sopenharmony_ci};
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic const struct sof_topology_token ipc4_out_audio_format_tokens[] = {
8062306a36Sopenharmony_ci	{SOF_TKN_CAVS_AUDIO_FORMAT_OUT_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
8162306a36Sopenharmony_ci		offsetof(struct sof_ipc4_pin_format, audio_fmt.sampling_frequency)},
8262306a36Sopenharmony_ci	{SOF_TKN_CAVS_AUDIO_FORMAT_OUT_BIT_DEPTH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
8362306a36Sopenharmony_ci		offsetof(struct sof_ipc4_pin_format, audio_fmt.bit_depth)},
8462306a36Sopenharmony_ci	{SOF_TKN_CAVS_AUDIO_FORMAT_OUT_CH_MAP, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
8562306a36Sopenharmony_ci		offsetof(struct sof_ipc4_pin_format, audio_fmt.ch_map)},
8662306a36Sopenharmony_ci	{SOF_TKN_CAVS_AUDIO_FORMAT_OUT_CH_CFG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
8762306a36Sopenharmony_ci		offsetof(struct sof_ipc4_pin_format, audio_fmt.ch_cfg)},
8862306a36Sopenharmony_ci	{SOF_TKN_CAVS_AUDIO_FORMAT_OUT_INTERLEAVING_STYLE, SND_SOC_TPLG_TUPLE_TYPE_WORD,
8962306a36Sopenharmony_ci		get_token_u32, offsetof(struct sof_ipc4_pin_format,
9062306a36Sopenharmony_ci		audio_fmt.interleaving_style)},
9162306a36Sopenharmony_ci	{SOF_TKN_CAVS_AUDIO_FORMAT_OUT_FMT_CFG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
9262306a36Sopenharmony_ci		offsetof(struct sof_ipc4_pin_format, audio_fmt.fmt_cfg)},
9362306a36Sopenharmony_ci	{SOF_TKN_CAVS_AUDIO_FORMAT_OUTPUT_PIN_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
9462306a36Sopenharmony_ci		offsetof(struct sof_ipc4_pin_format, pin_index)},
9562306a36Sopenharmony_ci	{SOF_TKN_CAVS_AUDIO_FORMAT_OBS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
9662306a36Sopenharmony_ci		offsetof(struct sof_ipc4_pin_format, buffer_size)},
9762306a36Sopenharmony_ci};
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic const struct sof_topology_token ipc4_copier_deep_buffer_tokens[] = {
10062306a36Sopenharmony_ci	{SOF_TKN_INTEL_COPIER_DEEP_BUFFER_DMA_MS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 0},
10162306a36Sopenharmony_ci};
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic const struct sof_topology_token ipc4_copier_tokens[] = {
10462306a36Sopenharmony_ci	{SOF_TKN_INTEL_COPIER_NODE_TYPE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 0},
10562306a36Sopenharmony_ci};
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic const struct sof_topology_token ipc4_audio_fmt_num_tokens[] = {
10862306a36Sopenharmony_ci	{SOF_TKN_COMP_NUM_INPUT_AUDIO_FORMATS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
10962306a36Sopenharmony_ci		offsetof(struct sof_ipc4_available_audio_format, num_input_formats)},
11062306a36Sopenharmony_ci	{SOF_TKN_COMP_NUM_OUTPUT_AUDIO_FORMATS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
11162306a36Sopenharmony_ci		offsetof(struct sof_ipc4_available_audio_format, num_output_formats)},
11262306a36Sopenharmony_ci};
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic const struct sof_topology_token dai_tokens[] = {
11562306a36Sopenharmony_ci	{SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type,
11662306a36Sopenharmony_ci		offsetof(struct sof_ipc4_copier, dai_type)},
11762306a36Sopenharmony_ci	{SOF_TKN_DAI_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
11862306a36Sopenharmony_ci		offsetof(struct sof_ipc4_copier, dai_index)},
11962306a36Sopenharmony_ci};
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci/* Component extended tokens */
12262306a36Sopenharmony_cistatic const struct sof_topology_token comp_ext_tokens[] = {
12362306a36Sopenharmony_ci	{SOF_TKN_COMP_UUID, SND_SOC_TPLG_TUPLE_TYPE_UUID, get_token_uuid,
12462306a36Sopenharmony_ci		offsetof(struct snd_sof_widget, uuid)},
12562306a36Sopenharmony_ci	{SOF_TKN_COMP_CORE_ID, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
12662306a36Sopenharmony_ci		offsetof(struct snd_sof_widget, core)},
12762306a36Sopenharmony_ci};
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic const struct sof_topology_token gain_tokens[] = {
13062306a36Sopenharmony_ci	{SOF_TKN_GAIN_RAMP_TYPE, SND_SOC_TPLG_TUPLE_TYPE_WORD,
13162306a36Sopenharmony_ci		get_token_u32, offsetof(struct sof_ipc4_gain_params, curve_type)},
13262306a36Sopenharmony_ci	{SOF_TKN_GAIN_RAMP_DURATION,
13362306a36Sopenharmony_ci		SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
13462306a36Sopenharmony_ci		offsetof(struct sof_ipc4_gain_params, curve_duration_l)},
13562306a36Sopenharmony_ci	{SOF_TKN_GAIN_VAL, SND_SOC_TPLG_TUPLE_TYPE_WORD,
13662306a36Sopenharmony_ci		get_token_u32, offsetof(struct sof_ipc4_gain_params, init_val)},
13762306a36Sopenharmony_ci};
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci/* SRC */
14062306a36Sopenharmony_cistatic const struct sof_topology_token src_tokens[] = {
14162306a36Sopenharmony_ci	{SOF_TKN_SRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
14262306a36Sopenharmony_ci		offsetof(struct sof_ipc4_src_data, sink_rate)},
14362306a36Sopenharmony_ci};
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic const struct sof_token_info ipc4_token_list[SOF_TOKEN_COUNT] = {
14662306a36Sopenharmony_ci	[SOF_DAI_TOKENS] = {"DAI tokens", dai_tokens, ARRAY_SIZE(dai_tokens)},
14762306a36Sopenharmony_ci	[SOF_PIPELINE_TOKENS] = {"Pipeline tokens", pipeline_tokens, ARRAY_SIZE(pipeline_tokens)},
14862306a36Sopenharmony_ci	[SOF_SCHED_TOKENS] = {"Scheduler tokens", ipc4_sched_tokens,
14962306a36Sopenharmony_ci		ARRAY_SIZE(ipc4_sched_tokens)},
15062306a36Sopenharmony_ci	[SOF_COMP_EXT_TOKENS] = {"Comp extended tokens", comp_ext_tokens,
15162306a36Sopenharmony_ci		ARRAY_SIZE(comp_ext_tokens)},
15262306a36Sopenharmony_ci	[SOF_COMP_TOKENS] = {"IPC4 Component tokens",
15362306a36Sopenharmony_ci		ipc4_comp_tokens, ARRAY_SIZE(ipc4_comp_tokens)},
15462306a36Sopenharmony_ci	[SOF_IN_AUDIO_FORMAT_TOKENS] = {"IPC4 Input Audio format tokens",
15562306a36Sopenharmony_ci		ipc4_in_audio_format_tokens, ARRAY_SIZE(ipc4_in_audio_format_tokens)},
15662306a36Sopenharmony_ci	[SOF_OUT_AUDIO_FORMAT_TOKENS] = {"IPC4 Output Audio format tokens",
15762306a36Sopenharmony_ci		ipc4_out_audio_format_tokens, ARRAY_SIZE(ipc4_out_audio_format_tokens)},
15862306a36Sopenharmony_ci	[SOF_COPIER_DEEP_BUFFER_TOKENS] = {"IPC4 Copier deep buffer tokens",
15962306a36Sopenharmony_ci		ipc4_copier_deep_buffer_tokens, ARRAY_SIZE(ipc4_copier_deep_buffer_tokens)},
16062306a36Sopenharmony_ci	[SOF_COPIER_TOKENS] = {"IPC4 Copier tokens", ipc4_copier_tokens,
16162306a36Sopenharmony_ci		ARRAY_SIZE(ipc4_copier_tokens)},
16262306a36Sopenharmony_ci	[SOF_AUDIO_FMT_NUM_TOKENS] = {"IPC4 Audio format number tokens",
16362306a36Sopenharmony_ci		ipc4_audio_fmt_num_tokens, ARRAY_SIZE(ipc4_audio_fmt_num_tokens)},
16462306a36Sopenharmony_ci	[SOF_GAIN_TOKENS] = {"Gain tokens", gain_tokens, ARRAY_SIZE(gain_tokens)},
16562306a36Sopenharmony_ci	[SOF_SRC_TOKENS] = {"SRC tokens", src_tokens, ARRAY_SIZE(src_tokens)},
16662306a36Sopenharmony_ci};
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic void sof_ipc4_dbg_audio_format(struct device *dev, struct sof_ipc4_pin_format *pin_fmt,
16962306a36Sopenharmony_ci				      int num_formats)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	int i;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	for (i = 0; i < num_formats; i++) {
17462306a36Sopenharmony_ci		struct sof_ipc4_audio_format *fmt = &pin_fmt[i].audio_fmt;
17562306a36Sopenharmony_ci		dev_dbg(dev,
17662306a36Sopenharmony_ci			"Pin index #%d: %uHz, %ubit (ch_map %#x ch_cfg %u interleaving_style %u fmt_cfg %#x) buffer size %d\n",
17762306a36Sopenharmony_ci			pin_fmt[i].pin_index, fmt->sampling_frequency, fmt->bit_depth, fmt->ch_map,
17862306a36Sopenharmony_ci			fmt->ch_cfg, fmt->interleaving_style, fmt->fmt_cfg,
17962306a36Sopenharmony_ci			pin_fmt[i].buffer_size);
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic const struct sof_ipc4_audio_format *
18462306a36Sopenharmony_cisof_ipc4_get_input_pin_audio_fmt(struct snd_sof_widget *swidget, int pin_index)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	struct sof_ipc4_base_module_cfg_ext *base_cfg_ext;
18762306a36Sopenharmony_ci	struct sof_ipc4_process *process;
18862306a36Sopenharmony_ci	int i;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	if (swidget->id != snd_soc_dapm_effect) {
19162306a36Sopenharmony_ci		struct sof_ipc4_base_module_cfg *base = swidget->private;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci		/* For non-process modules, base module config format is used for all input pins */
19462306a36Sopenharmony_ci		return &base->audio_fmt;
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	process = swidget->private;
19862306a36Sopenharmony_ci	base_cfg_ext = process->base_config_ext;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	/*
20162306a36Sopenharmony_ci	 * If there are multiple input formats available for a pin, the first available format
20262306a36Sopenharmony_ci	 * is chosen.
20362306a36Sopenharmony_ci	 */
20462306a36Sopenharmony_ci	for (i = 0; i < base_cfg_ext->num_input_pin_fmts; i++) {
20562306a36Sopenharmony_ci		struct sof_ipc4_pin_format *pin_format = &base_cfg_ext->pin_formats[i];
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci		if (pin_format->pin_index == pin_index)
20862306a36Sopenharmony_ci			return &pin_format->audio_fmt;
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	return NULL;
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci/**
21562306a36Sopenharmony_ci * sof_ipc4_get_audio_fmt - get available audio formats from swidget->tuples
21662306a36Sopenharmony_ci * @scomp: pointer to pointer to SOC component
21762306a36Sopenharmony_ci * @swidget: pointer to struct snd_sof_widget containing tuples
21862306a36Sopenharmony_ci * @available_fmt: pointer to struct sof_ipc4_available_audio_format being filling in
21962306a36Sopenharmony_ci * @module_base_cfg: Pointer to the base_config in the module init IPC payload
22062306a36Sopenharmony_ci *
22162306a36Sopenharmony_ci * Return: 0 if successful
22262306a36Sopenharmony_ci */
22362306a36Sopenharmony_cistatic int sof_ipc4_get_audio_fmt(struct snd_soc_component *scomp,
22462306a36Sopenharmony_ci				  struct snd_sof_widget *swidget,
22562306a36Sopenharmony_ci				  struct sof_ipc4_available_audio_format *available_fmt,
22662306a36Sopenharmony_ci				  struct sof_ipc4_base_module_cfg *module_base_cfg)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	struct sof_ipc4_pin_format *in_format = NULL;
22962306a36Sopenharmony_ci	struct sof_ipc4_pin_format *out_format;
23062306a36Sopenharmony_ci	int ret;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	ret = sof_update_ipc_object(scomp, available_fmt,
23362306a36Sopenharmony_ci				    SOF_AUDIO_FMT_NUM_TOKENS, swidget->tuples,
23462306a36Sopenharmony_ci				    swidget->num_tuples, sizeof(*available_fmt), 1);
23562306a36Sopenharmony_ci	if (ret) {
23662306a36Sopenharmony_ci		dev_err(scomp->dev, "Failed to parse audio format token count\n");
23762306a36Sopenharmony_ci		return ret;
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	if (!available_fmt->num_input_formats && !available_fmt->num_output_formats) {
24162306a36Sopenharmony_ci		dev_err(scomp->dev, "No input/output pin formats set in topology\n");
24262306a36Sopenharmony_ci		return -EINVAL;
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	dev_dbg(scomp->dev,
24662306a36Sopenharmony_ci		"Number of input audio formats: %d. Number of output audio formats: %d\n",
24762306a36Sopenharmony_ci		available_fmt->num_input_formats, available_fmt->num_output_formats);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	/* set is_pages in the module's base_config */
25062306a36Sopenharmony_ci	ret = sof_update_ipc_object(scomp, module_base_cfg, SOF_COMP_TOKENS, swidget->tuples,
25162306a36Sopenharmony_ci				    swidget->num_tuples, sizeof(*module_base_cfg), 1);
25262306a36Sopenharmony_ci	if (ret) {
25362306a36Sopenharmony_ci		dev_err(scomp->dev, "parse comp tokens for %s failed, error: %d\n",
25462306a36Sopenharmony_ci			swidget->widget->name, ret);
25562306a36Sopenharmony_ci		return ret;
25662306a36Sopenharmony_ci	}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	dev_dbg(scomp->dev, "widget %s: is_pages: %d\n", swidget->widget->name,
25962306a36Sopenharmony_ci		module_base_cfg->is_pages);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	if (available_fmt->num_input_formats) {
26262306a36Sopenharmony_ci		in_format = kcalloc(available_fmt->num_input_formats,
26362306a36Sopenharmony_ci				    sizeof(*in_format), GFP_KERNEL);
26462306a36Sopenharmony_ci		if (!in_format)
26562306a36Sopenharmony_ci			return -ENOMEM;
26662306a36Sopenharmony_ci		available_fmt->input_pin_fmts = in_format;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci		ret = sof_update_ipc_object(scomp, in_format,
26962306a36Sopenharmony_ci					    SOF_IN_AUDIO_FORMAT_TOKENS, swidget->tuples,
27062306a36Sopenharmony_ci					    swidget->num_tuples, sizeof(*in_format),
27162306a36Sopenharmony_ci					    available_fmt->num_input_formats);
27262306a36Sopenharmony_ci		if (ret) {
27362306a36Sopenharmony_ci			dev_err(scomp->dev, "parse input audio fmt tokens failed %d\n", ret);
27462306a36Sopenharmony_ci			goto err_in;
27562306a36Sopenharmony_ci		}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci		dev_dbg(scomp->dev, "Input audio formats for %s\n", swidget->widget->name);
27862306a36Sopenharmony_ci		sof_ipc4_dbg_audio_format(scomp->dev, in_format,
27962306a36Sopenharmony_ci					  available_fmt->num_input_formats);
28062306a36Sopenharmony_ci	}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	if (available_fmt->num_output_formats) {
28362306a36Sopenharmony_ci		out_format = kcalloc(available_fmt->num_output_formats, sizeof(*out_format),
28462306a36Sopenharmony_ci				     GFP_KERNEL);
28562306a36Sopenharmony_ci		if (!out_format) {
28662306a36Sopenharmony_ci			ret = -ENOMEM;
28762306a36Sopenharmony_ci			goto err_in;
28862306a36Sopenharmony_ci		}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci		ret = sof_update_ipc_object(scomp, out_format,
29162306a36Sopenharmony_ci					    SOF_OUT_AUDIO_FORMAT_TOKENS, swidget->tuples,
29262306a36Sopenharmony_ci					    swidget->num_tuples, sizeof(*out_format),
29362306a36Sopenharmony_ci					    available_fmt->num_output_formats);
29462306a36Sopenharmony_ci		if (ret) {
29562306a36Sopenharmony_ci			dev_err(scomp->dev, "parse output audio fmt tokens failed\n");
29662306a36Sopenharmony_ci			goto err_out;
29762306a36Sopenharmony_ci		}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci		available_fmt->output_pin_fmts = out_format;
30062306a36Sopenharmony_ci		dev_dbg(scomp->dev, "Output audio formats for %s\n", swidget->widget->name);
30162306a36Sopenharmony_ci		sof_ipc4_dbg_audio_format(scomp->dev, out_format,
30262306a36Sopenharmony_ci					  available_fmt->num_output_formats);
30362306a36Sopenharmony_ci	}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	return 0;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_cierr_out:
30862306a36Sopenharmony_ci	kfree(out_format);
30962306a36Sopenharmony_cierr_in:
31062306a36Sopenharmony_ci	kfree(in_format);
31162306a36Sopenharmony_ci	available_fmt->input_pin_fmts = NULL;
31262306a36Sopenharmony_ci	return ret;
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci/* release the memory allocated in sof_ipc4_get_audio_fmt */
31662306a36Sopenharmony_cistatic void sof_ipc4_free_audio_fmt(struct sof_ipc4_available_audio_format *available_fmt)
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	kfree(available_fmt->output_pin_fmts);
32062306a36Sopenharmony_ci	available_fmt->output_pin_fmts = NULL;
32162306a36Sopenharmony_ci	kfree(available_fmt->input_pin_fmts);
32262306a36Sopenharmony_ci	available_fmt->input_pin_fmts = NULL;
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic void sof_ipc4_widget_free_comp_pipeline(struct snd_sof_widget *swidget)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	kfree(swidget->private);
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic int sof_ipc4_widget_set_module_info(struct snd_sof_widget *swidget)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	struct snd_soc_component *scomp = swidget->scomp;
33362306a36Sopenharmony_ci	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	swidget->module_info = sof_ipc4_find_module_by_uuid(sdev, &swidget->uuid);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	if (swidget->module_info)
33862306a36Sopenharmony_ci		return 0;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	dev_err(sdev->dev, "failed to find module info for widget %s with UUID %pUL\n",
34162306a36Sopenharmony_ci		swidget->widget->name, &swidget->uuid);
34262306a36Sopenharmony_ci	return -EINVAL;
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistatic int sof_ipc4_widget_setup_msg(struct snd_sof_widget *swidget, struct sof_ipc4_msg *msg)
34662306a36Sopenharmony_ci{
34762306a36Sopenharmony_ci	struct sof_ipc4_fw_module *fw_module;
34862306a36Sopenharmony_ci	uint32_t type;
34962306a36Sopenharmony_ci	int ret;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	ret = sof_ipc4_widget_set_module_info(swidget);
35262306a36Sopenharmony_ci	if (ret)
35362306a36Sopenharmony_ci		return ret;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	fw_module = swidget->module_info;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	msg->primary = fw_module->man4_module_entry.id;
35862306a36Sopenharmony_ci	msg->primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_INIT_INSTANCE);
35962306a36Sopenharmony_ci	msg->primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
36062306a36Sopenharmony_ci	msg->primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	msg->extension = SOF_IPC4_MOD_EXT_CORE_ID(swidget->core);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	type = (fw_module->man4_module_entry.type & SOF_IPC4_MODULE_DP) ? 1 : 0;
36562306a36Sopenharmony_ci	msg->extension |= SOF_IPC4_MOD_EXT_DOMAIN(type);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	return 0;
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic void sof_ipc4_widget_update_kcontrol_module_id(struct snd_sof_widget *swidget)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	struct snd_soc_component *scomp = swidget->scomp;
37362306a36Sopenharmony_ci	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
37462306a36Sopenharmony_ci	struct sof_ipc4_fw_module *fw_module = swidget->module_info;
37562306a36Sopenharmony_ci	struct snd_sof_control *scontrol;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	/* update module ID for all kcontrols for this widget */
37862306a36Sopenharmony_ci	list_for_each_entry(scontrol, &sdev->kcontrol_list, list) {
37962306a36Sopenharmony_ci		if (scontrol->comp_id == swidget->comp_id) {
38062306a36Sopenharmony_ci			struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
38162306a36Sopenharmony_ci			struct sof_ipc4_msg *msg = &cdata->msg;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci			msg->primary |= fw_module->man4_module_entry.id;
38462306a36Sopenharmony_ci		}
38562306a36Sopenharmony_ci	}
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_cistatic int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	struct sof_ipc4_available_audio_format *available_fmt;
39162306a36Sopenharmony_ci	struct snd_soc_component *scomp = swidget->scomp;
39262306a36Sopenharmony_ci	struct sof_ipc4_copier *ipc4_copier;
39362306a36Sopenharmony_ci	int node_type = 0;
39462306a36Sopenharmony_ci	int ret;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	ipc4_copier = kzalloc(sizeof(*ipc4_copier), GFP_KERNEL);
39762306a36Sopenharmony_ci	if (!ipc4_copier)
39862306a36Sopenharmony_ci		return -ENOMEM;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	swidget->private = ipc4_copier;
40162306a36Sopenharmony_ci	available_fmt = &ipc4_copier->available_fmt;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	dev_dbg(scomp->dev, "Updating IPC structure for %s\n", swidget->widget->name);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	ret = sof_ipc4_get_audio_fmt(scomp, swidget, available_fmt,
40662306a36Sopenharmony_ci				     &ipc4_copier->data.base_config);
40762306a36Sopenharmony_ci	if (ret)
40862306a36Sopenharmony_ci		goto free_copier;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	/*
41162306a36Sopenharmony_ci	 * This callback is used by host copier and module-to-module copier,
41262306a36Sopenharmony_ci	 * and only host copier needs to set gtw_cfg.
41362306a36Sopenharmony_ci	 */
41462306a36Sopenharmony_ci	if (!WIDGET_IS_AIF(swidget->id))
41562306a36Sopenharmony_ci		goto skip_gtw_cfg;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	ret = sof_update_ipc_object(scomp, &node_type,
41862306a36Sopenharmony_ci				    SOF_COPIER_TOKENS, swidget->tuples,
41962306a36Sopenharmony_ci				    swidget->num_tuples, sizeof(node_type), 1);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	if (ret) {
42262306a36Sopenharmony_ci		dev_err(scomp->dev, "parse host copier node type token failed %d\n",
42362306a36Sopenharmony_ci			ret);
42462306a36Sopenharmony_ci		goto free_available_fmt;
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci	dev_dbg(scomp->dev, "host copier '%s' node_type %u\n", swidget->widget->name, node_type);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ciskip_gtw_cfg:
42962306a36Sopenharmony_ci	ipc4_copier->gtw_attr = kzalloc(sizeof(*ipc4_copier->gtw_attr), GFP_KERNEL);
43062306a36Sopenharmony_ci	if (!ipc4_copier->gtw_attr) {
43162306a36Sopenharmony_ci		ret = -ENOMEM;
43262306a36Sopenharmony_ci		goto free_available_fmt;
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	ipc4_copier->copier_config = (uint32_t *)ipc4_copier->gtw_attr;
43662306a36Sopenharmony_ci	ipc4_copier->data.gtw_cfg.config_length =
43762306a36Sopenharmony_ci		sizeof(struct sof_ipc4_gtw_attributes) >> 2;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	switch (swidget->id) {
44062306a36Sopenharmony_ci	case snd_soc_dapm_aif_in:
44162306a36Sopenharmony_ci	case snd_soc_dapm_aif_out:
44262306a36Sopenharmony_ci		ipc4_copier->data.gtw_cfg.node_id = SOF_IPC4_NODE_TYPE(node_type);
44362306a36Sopenharmony_ci		break;
44462306a36Sopenharmony_ci	case snd_soc_dapm_buffer:
44562306a36Sopenharmony_ci		ipc4_copier->data.gtw_cfg.node_id = SOF_IPC4_INVALID_NODE_ID;
44662306a36Sopenharmony_ci		ipc4_copier->ipc_config_size = 0;
44762306a36Sopenharmony_ci		break;
44862306a36Sopenharmony_ci	default:
44962306a36Sopenharmony_ci		dev_err(scomp->dev, "invalid widget type %d\n", swidget->id);
45062306a36Sopenharmony_ci		ret = -EINVAL;
45162306a36Sopenharmony_ci		goto free_gtw_attr;
45262306a36Sopenharmony_ci	}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	/* set up module info and message header */
45562306a36Sopenharmony_ci	ret = sof_ipc4_widget_setup_msg(swidget, &ipc4_copier->msg);
45662306a36Sopenharmony_ci	if (ret)
45762306a36Sopenharmony_ci		goto free_gtw_attr;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	return 0;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_cifree_gtw_attr:
46262306a36Sopenharmony_ci	kfree(ipc4_copier->gtw_attr);
46362306a36Sopenharmony_cifree_available_fmt:
46462306a36Sopenharmony_ci	sof_ipc4_free_audio_fmt(available_fmt);
46562306a36Sopenharmony_cifree_copier:
46662306a36Sopenharmony_ci	kfree(ipc4_copier);
46762306a36Sopenharmony_ci	swidget->private = NULL;
46862306a36Sopenharmony_ci	return ret;
46962306a36Sopenharmony_ci}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_cistatic void sof_ipc4_widget_free_comp_pcm(struct snd_sof_widget *swidget)
47262306a36Sopenharmony_ci{
47362306a36Sopenharmony_ci	struct sof_ipc4_copier *ipc4_copier = swidget->private;
47462306a36Sopenharmony_ci	struct sof_ipc4_available_audio_format *available_fmt;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	if (!ipc4_copier)
47762306a36Sopenharmony_ci		return;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	available_fmt = &ipc4_copier->available_fmt;
48062306a36Sopenharmony_ci	kfree(available_fmt->output_pin_fmts);
48162306a36Sopenharmony_ci	kfree(ipc4_copier->gtw_attr);
48262306a36Sopenharmony_ci	kfree(ipc4_copier);
48362306a36Sopenharmony_ci	swidget->private = NULL;
48462306a36Sopenharmony_ci}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_cistatic int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget)
48762306a36Sopenharmony_ci{
48862306a36Sopenharmony_ci	struct sof_ipc4_available_audio_format *available_fmt;
48962306a36Sopenharmony_ci	struct snd_soc_component *scomp = swidget->scomp;
49062306a36Sopenharmony_ci	struct snd_sof_dai *dai = swidget->private;
49162306a36Sopenharmony_ci	struct sof_ipc4_copier *ipc4_copier;
49262306a36Sopenharmony_ci	struct snd_sof_widget *pipe_widget;
49362306a36Sopenharmony_ci	struct sof_ipc4_pipeline *pipeline;
49462306a36Sopenharmony_ci	int node_type = 0;
49562306a36Sopenharmony_ci	int ret;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	ipc4_copier = kzalloc(sizeof(*ipc4_copier), GFP_KERNEL);
49862306a36Sopenharmony_ci	if (!ipc4_copier)
49962306a36Sopenharmony_ci		return -ENOMEM;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	available_fmt = &ipc4_copier->available_fmt;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	dev_dbg(scomp->dev, "Updating IPC structure for %s\n", swidget->widget->name);
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	ret = sof_ipc4_get_audio_fmt(scomp, swidget, available_fmt,
50662306a36Sopenharmony_ci				     &ipc4_copier->data.base_config);
50762306a36Sopenharmony_ci	if (ret)
50862306a36Sopenharmony_ci		goto free_copier;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	ret = sof_update_ipc_object(scomp, &node_type,
51162306a36Sopenharmony_ci				    SOF_COPIER_TOKENS, swidget->tuples,
51262306a36Sopenharmony_ci				    swidget->num_tuples, sizeof(node_type), 1);
51362306a36Sopenharmony_ci	if (ret) {
51462306a36Sopenharmony_ci		dev_err(scomp->dev, "parse dai node type failed %d\n", ret);
51562306a36Sopenharmony_ci		goto free_available_fmt;
51662306a36Sopenharmony_ci	}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	ret = sof_update_ipc_object(scomp, ipc4_copier,
51962306a36Sopenharmony_ci				    SOF_DAI_TOKENS, swidget->tuples,
52062306a36Sopenharmony_ci				    swidget->num_tuples, sizeof(u32), 1);
52162306a36Sopenharmony_ci	if (ret) {
52262306a36Sopenharmony_ci		dev_err(scomp->dev, "parse dai copier node token failed %d\n", ret);
52362306a36Sopenharmony_ci		goto free_available_fmt;
52462306a36Sopenharmony_ci	}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	dev_dbg(scomp->dev, "dai %s node_type %u dai_type %u dai_index %d\n", swidget->widget->name,
52762306a36Sopenharmony_ci		node_type, ipc4_copier->dai_type, ipc4_copier->dai_index);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	ipc4_copier->data.gtw_cfg.node_id = SOF_IPC4_NODE_TYPE(node_type);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	pipe_widget = swidget->spipe->pipe_widget;
53262306a36Sopenharmony_ci	pipeline = pipe_widget->private;
53362306a36Sopenharmony_ci	if (pipeline->use_chain_dma && ipc4_copier->dai_type != SOF_DAI_INTEL_HDA) {
53462306a36Sopenharmony_ci		dev_err(scomp->dev,
53562306a36Sopenharmony_ci			"Bad DAI type '%d', Chained DMA is only supported by HDA DAIs (%d).\n",
53662306a36Sopenharmony_ci			ipc4_copier->dai_type, SOF_DAI_INTEL_HDA);
53762306a36Sopenharmony_ci		ret = -ENODEV;
53862306a36Sopenharmony_ci		goto free_available_fmt;
53962306a36Sopenharmony_ci	}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	switch (ipc4_copier->dai_type) {
54262306a36Sopenharmony_ci	case SOF_DAI_INTEL_ALH:
54362306a36Sopenharmony_ci	{
54462306a36Sopenharmony_ci		struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
54562306a36Sopenharmony_ci		struct sof_ipc4_alh_configuration_blob *blob;
54662306a36Sopenharmony_ci		struct snd_soc_dapm_path *p;
54762306a36Sopenharmony_ci		struct snd_sof_widget *w;
54862306a36Sopenharmony_ci		int src_num = 0;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci		snd_soc_dapm_widget_for_each_source_path(swidget->widget, p)
55162306a36Sopenharmony_ci			src_num++;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci		if (swidget->id == snd_soc_dapm_dai_in && src_num == 0) {
55462306a36Sopenharmony_ci			/*
55562306a36Sopenharmony_ci			 * The blob will not be used if the ALH copier is playback direction
55662306a36Sopenharmony_ci			 * and doesn't connect to any source.
55762306a36Sopenharmony_ci			 * It is fine to call kfree(ipc4_copier->copier_config) since
55862306a36Sopenharmony_ci			 * ipc4_copier->copier_config is null.
55962306a36Sopenharmony_ci			 */
56062306a36Sopenharmony_ci			ret = 0;
56162306a36Sopenharmony_ci			break;
56262306a36Sopenharmony_ci		}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci		blob = kzalloc(sizeof(*blob), GFP_KERNEL);
56562306a36Sopenharmony_ci		if (!blob) {
56662306a36Sopenharmony_ci			ret = -ENOMEM;
56762306a36Sopenharmony_ci			goto free_available_fmt;
56862306a36Sopenharmony_ci		}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci		list_for_each_entry(w, &sdev->widget_list, list) {
57162306a36Sopenharmony_ci			if (w->widget->sname &&
57262306a36Sopenharmony_ci			    strcmp(w->widget->sname, swidget->widget->sname))
57362306a36Sopenharmony_ci				continue;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci			blob->alh_cfg.device_count++;
57662306a36Sopenharmony_ci		}
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci		ipc4_copier->copier_config = (uint32_t *)blob;
57962306a36Sopenharmony_ci		ipc4_copier->data.gtw_cfg.config_length = sizeof(*blob) >> 2;
58062306a36Sopenharmony_ci		break;
58162306a36Sopenharmony_ci	}
58262306a36Sopenharmony_ci	case SOF_DAI_INTEL_SSP:
58362306a36Sopenharmony_ci		/* set SSP DAI index as the node_id */
58462306a36Sopenharmony_ci		ipc4_copier->data.gtw_cfg.node_id |=
58562306a36Sopenharmony_ci			SOF_IPC4_NODE_INDEX_INTEL_SSP(ipc4_copier->dai_index);
58662306a36Sopenharmony_ci		break;
58762306a36Sopenharmony_ci	case SOF_DAI_INTEL_DMIC:
58862306a36Sopenharmony_ci		/* set DMIC DAI index as the node_id */
58962306a36Sopenharmony_ci		ipc4_copier->data.gtw_cfg.node_id |=
59062306a36Sopenharmony_ci			SOF_IPC4_NODE_INDEX_INTEL_DMIC(ipc4_copier->dai_index);
59162306a36Sopenharmony_ci		break;
59262306a36Sopenharmony_ci	default:
59362306a36Sopenharmony_ci		ipc4_copier->gtw_attr = kzalloc(sizeof(*ipc4_copier->gtw_attr), GFP_KERNEL);
59462306a36Sopenharmony_ci		if (!ipc4_copier->gtw_attr) {
59562306a36Sopenharmony_ci			ret = -ENOMEM;
59662306a36Sopenharmony_ci			goto free_available_fmt;
59762306a36Sopenharmony_ci		}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci		ipc4_copier->copier_config = (uint32_t *)ipc4_copier->gtw_attr;
60062306a36Sopenharmony_ci		ipc4_copier->data.gtw_cfg.config_length =
60162306a36Sopenharmony_ci			sizeof(struct sof_ipc4_gtw_attributes) >> 2;
60262306a36Sopenharmony_ci		break;
60362306a36Sopenharmony_ci	}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	dai->scomp = scomp;
60662306a36Sopenharmony_ci	dai->private = ipc4_copier;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	/* set up module info and message header */
60962306a36Sopenharmony_ci	ret = sof_ipc4_widget_setup_msg(swidget, &ipc4_copier->msg);
61062306a36Sopenharmony_ci	if (ret)
61162306a36Sopenharmony_ci		goto free_copier_config;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	return 0;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_cifree_copier_config:
61662306a36Sopenharmony_ci	kfree(ipc4_copier->copier_config);
61762306a36Sopenharmony_cifree_available_fmt:
61862306a36Sopenharmony_ci	sof_ipc4_free_audio_fmt(available_fmt);
61962306a36Sopenharmony_cifree_copier:
62062306a36Sopenharmony_ci	kfree(ipc4_copier);
62162306a36Sopenharmony_ci	dai->private = NULL;
62262306a36Sopenharmony_ci	dai->scomp = NULL;
62362306a36Sopenharmony_ci	return ret;
62462306a36Sopenharmony_ci}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_cistatic void sof_ipc4_widget_free_comp_dai(struct snd_sof_widget *swidget)
62762306a36Sopenharmony_ci{
62862306a36Sopenharmony_ci	struct sof_ipc4_available_audio_format *available_fmt;
62962306a36Sopenharmony_ci	struct snd_sof_dai *dai = swidget->private;
63062306a36Sopenharmony_ci	struct sof_ipc4_copier *ipc4_copier;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	if (!dai)
63362306a36Sopenharmony_ci		return;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	if (!dai->private) {
63662306a36Sopenharmony_ci		kfree(dai);
63762306a36Sopenharmony_ci		swidget->private = NULL;
63862306a36Sopenharmony_ci		return;
63962306a36Sopenharmony_ci	}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	ipc4_copier = dai->private;
64262306a36Sopenharmony_ci	available_fmt = &ipc4_copier->available_fmt;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	kfree(available_fmt->output_pin_fmts);
64562306a36Sopenharmony_ci	if (ipc4_copier->dai_type != SOF_DAI_INTEL_SSP &&
64662306a36Sopenharmony_ci	    ipc4_copier->dai_type != SOF_DAI_INTEL_DMIC)
64762306a36Sopenharmony_ci		kfree(ipc4_copier->copier_config);
64862306a36Sopenharmony_ci	kfree(dai->private);
64962306a36Sopenharmony_ci	kfree(dai);
65062306a36Sopenharmony_ci	swidget->private = NULL;
65162306a36Sopenharmony_ci}
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_cistatic int sof_ipc4_widget_setup_comp_pipeline(struct snd_sof_widget *swidget)
65462306a36Sopenharmony_ci{
65562306a36Sopenharmony_ci	struct snd_soc_component *scomp = swidget->scomp;
65662306a36Sopenharmony_ci	struct sof_ipc4_pipeline *pipeline;
65762306a36Sopenharmony_ci	struct snd_sof_pipeline *spipe = swidget->spipe;
65862306a36Sopenharmony_ci	int ret;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	pipeline = kzalloc(sizeof(*pipeline), GFP_KERNEL);
66162306a36Sopenharmony_ci	if (!pipeline)
66262306a36Sopenharmony_ci		return -ENOMEM;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	ret = sof_update_ipc_object(scomp, pipeline, SOF_SCHED_TOKENS, swidget->tuples,
66562306a36Sopenharmony_ci				    swidget->num_tuples, sizeof(*pipeline), 1);
66662306a36Sopenharmony_ci	if (ret) {
66762306a36Sopenharmony_ci		dev_err(scomp->dev, "parsing scheduler tokens failed\n");
66862306a36Sopenharmony_ci		goto err;
66962306a36Sopenharmony_ci	}
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	swidget->core = pipeline->core_id;
67262306a36Sopenharmony_ci	spipe->core_mask |= BIT(pipeline->core_id);
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	if (pipeline->use_chain_dma) {
67562306a36Sopenharmony_ci		dev_dbg(scomp->dev, "Set up chain DMA for %s\n", swidget->widget->name);
67662306a36Sopenharmony_ci		swidget->private = pipeline;
67762306a36Sopenharmony_ci		return 0;
67862306a36Sopenharmony_ci	}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	/* parse one set of pipeline tokens */
68162306a36Sopenharmony_ci	ret = sof_update_ipc_object(scomp, swidget, SOF_PIPELINE_TOKENS, swidget->tuples,
68262306a36Sopenharmony_ci				    swidget->num_tuples, sizeof(*swidget), 1);
68362306a36Sopenharmony_ci	if (ret) {
68462306a36Sopenharmony_ci		dev_err(scomp->dev, "parsing pipeline tokens failed\n");
68562306a36Sopenharmony_ci		goto err;
68662306a36Sopenharmony_ci	}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	/* TODO: Get priority from topology */
68962306a36Sopenharmony_ci	pipeline->priority = 0;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	dev_dbg(scomp->dev, "pipeline '%s': id %d, pri %d, core_id %u, lp mode %d\n",
69262306a36Sopenharmony_ci		swidget->widget->name, swidget->pipeline_id,
69362306a36Sopenharmony_ci		pipeline->priority, pipeline->core_id, pipeline->lp_mode);
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	swidget->private = pipeline;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	pipeline->msg.primary = SOF_IPC4_GLB_PIPE_PRIORITY(pipeline->priority);
69862306a36Sopenharmony_ci	pipeline->msg.primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_CREATE_PIPELINE);
69962306a36Sopenharmony_ci	pipeline->msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
70062306a36Sopenharmony_ci	pipeline->msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG);
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	pipeline->msg.extension = pipeline->lp_mode;
70362306a36Sopenharmony_ci	pipeline->msg.extension |= SOF_IPC4_GLB_PIPE_EXT_CORE_ID(pipeline->core_id);
70462306a36Sopenharmony_ci	pipeline->state = SOF_IPC4_PIPE_UNINITIALIZED;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	return 0;
70762306a36Sopenharmony_cierr:
70862306a36Sopenharmony_ci	kfree(pipeline);
70962306a36Sopenharmony_ci	return ret;
71062306a36Sopenharmony_ci}
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_cistatic int sof_ipc4_widget_setup_comp_pga(struct snd_sof_widget *swidget)
71362306a36Sopenharmony_ci{
71462306a36Sopenharmony_ci	struct snd_soc_component *scomp = swidget->scomp;
71562306a36Sopenharmony_ci	struct sof_ipc4_gain *gain;
71662306a36Sopenharmony_ci	int ret;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	gain = kzalloc(sizeof(*gain), GFP_KERNEL);
71962306a36Sopenharmony_ci	if (!gain)
72062306a36Sopenharmony_ci		return -ENOMEM;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	swidget->private = gain;
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	gain->data.params.channels = SOF_IPC4_GAIN_ALL_CHANNELS_MASK;
72562306a36Sopenharmony_ci	gain->data.params.init_val = SOF_IPC4_VOL_ZERO_DB;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	ret = sof_ipc4_get_audio_fmt(scomp, swidget, &gain->available_fmt, &gain->data.base_config);
72862306a36Sopenharmony_ci	if (ret)
72962306a36Sopenharmony_ci		goto err;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	ret = sof_update_ipc_object(scomp, &gain->data.params, SOF_GAIN_TOKENS,
73262306a36Sopenharmony_ci				    swidget->tuples, swidget->num_tuples, sizeof(gain->data), 1);
73362306a36Sopenharmony_ci	if (ret) {
73462306a36Sopenharmony_ci		dev_err(scomp->dev, "Parsing gain tokens failed\n");
73562306a36Sopenharmony_ci		goto err;
73662306a36Sopenharmony_ci	}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	dev_dbg(scomp->dev,
73962306a36Sopenharmony_ci		"pga widget %s: ramp type: %d, ramp duration %d, initial gain value: %#x\n",
74062306a36Sopenharmony_ci		swidget->widget->name, gain->data.params.curve_type,
74162306a36Sopenharmony_ci		gain->data.params.curve_duration_l, gain->data.params.init_val);
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	ret = sof_ipc4_widget_setup_msg(swidget, &gain->msg);
74462306a36Sopenharmony_ci	if (ret)
74562306a36Sopenharmony_ci		goto err;
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	sof_ipc4_widget_update_kcontrol_module_id(swidget);
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	return 0;
75062306a36Sopenharmony_cierr:
75162306a36Sopenharmony_ci	sof_ipc4_free_audio_fmt(&gain->available_fmt);
75262306a36Sopenharmony_ci	kfree(gain);
75362306a36Sopenharmony_ci	swidget->private = NULL;
75462306a36Sopenharmony_ci	return ret;
75562306a36Sopenharmony_ci}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_cistatic void sof_ipc4_widget_free_comp_pga(struct snd_sof_widget *swidget)
75862306a36Sopenharmony_ci{
75962306a36Sopenharmony_ci	struct sof_ipc4_gain *gain = swidget->private;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	if (!gain)
76262306a36Sopenharmony_ci		return;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	sof_ipc4_free_audio_fmt(&gain->available_fmt);
76562306a36Sopenharmony_ci	kfree(swidget->private);
76662306a36Sopenharmony_ci	swidget->private = NULL;
76762306a36Sopenharmony_ci}
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_cistatic int sof_ipc4_widget_setup_comp_mixer(struct snd_sof_widget *swidget)
77062306a36Sopenharmony_ci{
77162306a36Sopenharmony_ci	struct snd_soc_component *scomp = swidget->scomp;
77262306a36Sopenharmony_ci	struct sof_ipc4_mixer *mixer;
77362306a36Sopenharmony_ci	int ret;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	dev_dbg(scomp->dev, "Updating IPC structure for %s\n", swidget->widget->name);
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	mixer = kzalloc(sizeof(*mixer), GFP_KERNEL);
77862306a36Sopenharmony_ci	if (!mixer)
77962306a36Sopenharmony_ci		return -ENOMEM;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	swidget->private = mixer;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	ret = sof_ipc4_get_audio_fmt(scomp, swidget, &mixer->available_fmt,
78462306a36Sopenharmony_ci				     &mixer->base_config);
78562306a36Sopenharmony_ci	if (ret)
78662306a36Sopenharmony_ci		goto err;
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	ret = sof_ipc4_widget_setup_msg(swidget, &mixer->msg);
78962306a36Sopenharmony_ci	if (ret)
79062306a36Sopenharmony_ci		goto err;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	return 0;
79362306a36Sopenharmony_cierr:
79462306a36Sopenharmony_ci	sof_ipc4_free_audio_fmt(&mixer->available_fmt);
79562306a36Sopenharmony_ci	kfree(mixer);
79662306a36Sopenharmony_ci	swidget->private = NULL;
79762306a36Sopenharmony_ci	return ret;
79862306a36Sopenharmony_ci}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_cistatic int sof_ipc4_widget_setup_comp_src(struct snd_sof_widget *swidget)
80162306a36Sopenharmony_ci{
80262306a36Sopenharmony_ci	struct snd_soc_component *scomp = swidget->scomp;
80362306a36Sopenharmony_ci	struct snd_sof_pipeline *spipe = swidget->spipe;
80462306a36Sopenharmony_ci	struct sof_ipc4_src *src;
80562306a36Sopenharmony_ci	int ret;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	dev_dbg(scomp->dev, "Updating IPC structure for %s\n", swidget->widget->name);
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	src = kzalloc(sizeof(*src), GFP_KERNEL);
81062306a36Sopenharmony_ci	if (!src)
81162306a36Sopenharmony_ci		return -ENOMEM;
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	swidget->private = src;
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	ret = sof_ipc4_get_audio_fmt(scomp, swidget, &src->available_fmt,
81662306a36Sopenharmony_ci				     &src->data.base_config);
81762306a36Sopenharmony_ci	if (ret)
81862306a36Sopenharmony_ci		goto err;
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	ret = sof_update_ipc_object(scomp, &src->data, SOF_SRC_TOKENS, swidget->tuples,
82162306a36Sopenharmony_ci				    swidget->num_tuples, sizeof(*src), 1);
82262306a36Sopenharmony_ci	if (ret) {
82362306a36Sopenharmony_ci		dev_err(scomp->dev, "Parsing SRC tokens failed\n");
82462306a36Sopenharmony_ci		goto err;
82562306a36Sopenharmony_ci	}
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	spipe->core_mask |= BIT(swidget->core);
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	dev_dbg(scomp->dev, "SRC sink rate %d\n", src->data.sink_rate);
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	ret = sof_ipc4_widget_setup_msg(swidget, &src->msg);
83262306a36Sopenharmony_ci	if (ret)
83362306a36Sopenharmony_ci		goto err;
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	return 0;
83662306a36Sopenharmony_cierr:
83762306a36Sopenharmony_ci	sof_ipc4_free_audio_fmt(&src->available_fmt);
83862306a36Sopenharmony_ci	kfree(src);
83962306a36Sopenharmony_ci	swidget->private = NULL;
84062306a36Sopenharmony_ci	return ret;
84162306a36Sopenharmony_ci}
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_cistatic void sof_ipc4_widget_free_comp_src(struct snd_sof_widget *swidget)
84462306a36Sopenharmony_ci{
84562306a36Sopenharmony_ci	struct sof_ipc4_src *src = swidget->private;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	if (!src)
84862306a36Sopenharmony_ci		return;
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	sof_ipc4_free_audio_fmt(&src->available_fmt);
85162306a36Sopenharmony_ci	kfree(swidget->private);
85262306a36Sopenharmony_ci	swidget->private = NULL;
85362306a36Sopenharmony_ci}
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_cistatic void sof_ipc4_widget_free_comp_mixer(struct snd_sof_widget *swidget)
85662306a36Sopenharmony_ci{
85762306a36Sopenharmony_ci	struct sof_ipc4_mixer *mixer = swidget->private;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	if (!mixer)
86062306a36Sopenharmony_ci		return;
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	sof_ipc4_free_audio_fmt(&mixer->available_fmt);
86362306a36Sopenharmony_ci	kfree(swidget->private);
86462306a36Sopenharmony_ci	swidget->private = NULL;
86562306a36Sopenharmony_ci}
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci/*
86862306a36Sopenharmony_ci * Add the process modules support. The process modules are defined as snd_soc_dapm_effect modules.
86962306a36Sopenharmony_ci */
87062306a36Sopenharmony_cistatic int sof_ipc4_widget_setup_comp_process(struct snd_sof_widget *swidget)
87162306a36Sopenharmony_ci{
87262306a36Sopenharmony_ci	struct snd_soc_component *scomp = swidget->scomp;
87362306a36Sopenharmony_ci	struct sof_ipc4_fw_module *fw_module;
87462306a36Sopenharmony_ci	struct snd_sof_pipeline *spipe = swidget->spipe;
87562306a36Sopenharmony_ci	struct sof_ipc4_process *process;
87662306a36Sopenharmony_ci	void *cfg;
87762306a36Sopenharmony_ci	int ret;
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	process = kzalloc(sizeof(*process), GFP_KERNEL);
88062306a36Sopenharmony_ci	if (!process)
88162306a36Sopenharmony_ci		return -ENOMEM;
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	swidget->private = process;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	ret = sof_ipc4_get_audio_fmt(scomp, swidget, &process->available_fmt,
88662306a36Sopenharmony_ci				     &process->base_config);
88762306a36Sopenharmony_ci	if (ret)
88862306a36Sopenharmony_ci		goto err;
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	ret = sof_ipc4_widget_setup_msg(swidget, &process->msg);
89162306a36Sopenharmony_ci	if (ret)
89262306a36Sopenharmony_ci		goto err;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	/* parse process init module payload config type from module info */
89562306a36Sopenharmony_ci	fw_module = swidget->module_info;
89662306a36Sopenharmony_ci	process->init_config = FIELD_GET(SOF_IPC4_MODULE_INIT_CONFIG_MASK,
89762306a36Sopenharmony_ci					 fw_module->man4_module_entry.type);
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	process->ipc_config_size = sizeof(struct sof_ipc4_base_module_cfg);
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	/* allocate memory for base config extension if needed */
90262306a36Sopenharmony_ci	if (process->init_config == SOF_IPC4_MODULE_INIT_CONFIG_TYPE_BASE_CFG_WITH_EXT) {
90362306a36Sopenharmony_ci		struct sof_ipc4_base_module_cfg_ext *base_cfg_ext;
90462306a36Sopenharmony_ci		u32 ext_size = struct_size(base_cfg_ext, pin_formats,
90562306a36Sopenharmony_ci					   size_add(swidget->num_input_pins,
90662306a36Sopenharmony_ci						    swidget->num_output_pins));
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci		base_cfg_ext = kzalloc(ext_size, GFP_KERNEL);
90962306a36Sopenharmony_ci		if (!base_cfg_ext) {
91062306a36Sopenharmony_ci			ret = -ENOMEM;
91162306a36Sopenharmony_ci			goto free_available_fmt;
91262306a36Sopenharmony_ci		}
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci		base_cfg_ext->num_input_pin_fmts = swidget->num_input_pins;
91562306a36Sopenharmony_ci		base_cfg_ext->num_output_pin_fmts = swidget->num_output_pins;
91662306a36Sopenharmony_ci		process->base_config_ext = base_cfg_ext;
91762306a36Sopenharmony_ci		process->base_config_ext_size = ext_size;
91862306a36Sopenharmony_ci		process->ipc_config_size += ext_size;
91962306a36Sopenharmony_ci	}
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	cfg = kzalloc(process->ipc_config_size, GFP_KERNEL);
92262306a36Sopenharmony_ci	if (!cfg) {
92362306a36Sopenharmony_ci		ret = -ENOMEM;
92462306a36Sopenharmony_ci		goto free_base_cfg_ext;
92562306a36Sopenharmony_ci	}
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	process->ipc_config_data = cfg;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	sof_ipc4_widget_update_kcontrol_module_id(swidget);
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	/* set pipeline core mask to keep track of the core the module is scheduled to run on */
93262306a36Sopenharmony_ci	spipe->core_mask |= BIT(swidget->core);
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	return 0;
93562306a36Sopenharmony_cifree_base_cfg_ext:
93662306a36Sopenharmony_ci	kfree(process->base_config_ext);
93762306a36Sopenharmony_ci	process->base_config_ext = NULL;
93862306a36Sopenharmony_cifree_available_fmt:
93962306a36Sopenharmony_ci	sof_ipc4_free_audio_fmt(&process->available_fmt);
94062306a36Sopenharmony_cierr:
94162306a36Sopenharmony_ci	kfree(process);
94262306a36Sopenharmony_ci	swidget->private = NULL;
94362306a36Sopenharmony_ci	return ret;
94462306a36Sopenharmony_ci}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_cistatic void sof_ipc4_widget_free_comp_process(struct snd_sof_widget *swidget)
94762306a36Sopenharmony_ci{
94862306a36Sopenharmony_ci	struct sof_ipc4_process *process = swidget->private;
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	if (!process)
95162306a36Sopenharmony_ci		return;
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	kfree(process->ipc_config_data);
95462306a36Sopenharmony_ci	kfree(process->base_config_ext);
95562306a36Sopenharmony_ci	sof_ipc4_free_audio_fmt(&process->available_fmt);
95662306a36Sopenharmony_ci	kfree(swidget->private);
95762306a36Sopenharmony_ci	swidget->private = NULL;
95862306a36Sopenharmony_ci}
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_cistatic void
96162306a36Sopenharmony_cisof_ipc4_update_resource_usage(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,
96262306a36Sopenharmony_ci			       struct sof_ipc4_base_module_cfg *base_config)
96362306a36Sopenharmony_ci{
96462306a36Sopenharmony_ci	struct sof_ipc4_fw_module *fw_module = swidget->module_info;
96562306a36Sopenharmony_ci	struct snd_sof_widget *pipe_widget;
96662306a36Sopenharmony_ci	struct sof_ipc4_pipeline *pipeline;
96762306a36Sopenharmony_ci	int task_mem, queue_mem;
96862306a36Sopenharmony_ci	int ibs, bss, total;
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	ibs = base_config->ibs;
97162306a36Sopenharmony_ci	bss = base_config->is_pages;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	task_mem = SOF_IPC4_PIPELINE_OBJECT_SIZE;
97462306a36Sopenharmony_ci	task_mem += SOF_IPC4_MODULE_INSTANCE_LIST_ITEM_SIZE + bss;
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	if (fw_module->man4_module_entry.type & SOF_IPC4_MODULE_LL) {
97762306a36Sopenharmony_ci		task_mem += SOF_IPC4_FW_ROUNDUP(SOF_IPC4_LL_TASK_OBJECT_SIZE);
97862306a36Sopenharmony_ci		task_mem += SOF_IPC4_FW_MAX_QUEUE_COUNT * SOF_IPC4_MODULE_INSTANCE_LIST_ITEM_SIZE;
97962306a36Sopenharmony_ci		task_mem += SOF_IPC4_LL_TASK_LIST_ITEM_SIZE;
98062306a36Sopenharmony_ci	} else {
98162306a36Sopenharmony_ci		task_mem += SOF_IPC4_FW_ROUNDUP(SOF_IPC4_DP_TASK_OBJECT_SIZE);
98262306a36Sopenharmony_ci		task_mem += SOF_IPC4_DP_TASK_LIST_SIZE;
98362306a36Sopenharmony_ci	}
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	ibs = SOF_IPC4_FW_ROUNDUP(ibs);
98662306a36Sopenharmony_ci	queue_mem = SOF_IPC4_FW_MAX_QUEUE_COUNT * (SOF_IPC4_DATA_QUEUE_OBJECT_SIZE +  ibs);
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	total = SOF_IPC4_FW_PAGE(task_mem + queue_mem);
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	pipe_widget = swidget->spipe->pipe_widget;
99162306a36Sopenharmony_ci	pipeline = pipe_widget->private;
99262306a36Sopenharmony_ci	pipeline->mem_usage += total;
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	/* Update base_config->cpc from the module manifest */
99562306a36Sopenharmony_ci	sof_ipc4_update_cpc_from_manifest(sdev, fw_module, base_config);
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	if (ignore_cpc) {
99862306a36Sopenharmony_ci		dev_dbg(sdev->dev, "%s: ibs / obs: %u / %u, forcing cpc to 0 from %u\n",
99962306a36Sopenharmony_ci			swidget->widget->name, base_config->ibs, base_config->obs,
100062306a36Sopenharmony_ci			base_config->cpc);
100162306a36Sopenharmony_ci		base_config->cpc = 0;
100262306a36Sopenharmony_ci	} else {
100362306a36Sopenharmony_ci		dev_dbg(sdev->dev, "%s: ibs / obs / cpc: %u / %u / %u\n",
100462306a36Sopenharmony_ci			swidget->widget->name, base_config->ibs, base_config->obs,
100562306a36Sopenharmony_ci			base_config->cpc);
100662306a36Sopenharmony_ci	}
100762306a36Sopenharmony_ci}
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_cistatic int sof_ipc4_widget_assign_instance_id(struct snd_sof_dev *sdev,
101062306a36Sopenharmony_ci					      struct snd_sof_widget *swidget)
101162306a36Sopenharmony_ci{
101262306a36Sopenharmony_ci	struct sof_ipc4_fw_module *fw_module = swidget->module_info;
101362306a36Sopenharmony_ci	int max_instances = fw_module->man4_module_entry.instance_max_count;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	swidget->instance_id = ida_alloc_max(&fw_module->m_ida, max_instances, GFP_KERNEL);
101662306a36Sopenharmony_ci	if (swidget->instance_id < 0) {
101762306a36Sopenharmony_ci		dev_err(sdev->dev, "failed to assign instance id for widget %s",
101862306a36Sopenharmony_ci			swidget->widget->name);
101962306a36Sopenharmony_ci		return swidget->instance_id;
102062306a36Sopenharmony_ci	}
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	return 0;
102362306a36Sopenharmony_ci}
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci/* update hw_params based on the audio stream format */
102662306a36Sopenharmony_cistatic int sof_ipc4_update_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_hw_params *params,
102762306a36Sopenharmony_ci				     struct sof_ipc4_audio_format *fmt)
102862306a36Sopenharmony_ci{
102962306a36Sopenharmony_ci	snd_pcm_format_t snd_fmt;
103062306a36Sopenharmony_ci	struct snd_interval *i;
103162306a36Sopenharmony_ci	struct snd_mask *m;
103262306a36Sopenharmony_ci	int valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);
103362306a36Sopenharmony_ci	unsigned int channels, rate;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	switch (valid_bits) {
103662306a36Sopenharmony_ci	case 16:
103762306a36Sopenharmony_ci		snd_fmt = SNDRV_PCM_FORMAT_S16_LE;
103862306a36Sopenharmony_ci		break;
103962306a36Sopenharmony_ci	case 24:
104062306a36Sopenharmony_ci		snd_fmt = SNDRV_PCM_FORMAT_S24_LE;
104162306a36Sopenharmony_ci		break;
104262306a36Sopenharmony_ci	case 32:
104362306a36Sopenharmony_ci		snd_fmt = SNDRV_PCM_FORMAT_S32_LE;
104462306a36Sopenharmony_ci		break;
104562306a36Sopenharmony_ci	default:
104662306a36Sopenharmony_ci		dev_err(sdev->dev, "invalid PCM valid_bits %d\n", valid_bits);
104762306a36Sopenharmony_ci		return -EINVAL;
104862306a36Sopenharmony_ci	}
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
105162306a36Sopenharmony_ci	snd_mask_none(m);
105262306a36Sopenharmony_ci	snd_mask_set_format(m, snd_fmt);
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	rate = fmt->sampling_frequency;
105562306a36Sopenharmony_ci	i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
105662306a36Sopenharmony_ci	i->min = rate;
105762306a36Sopenharmony_ci	i->max = rate;
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg);
106062306a36Sopenharmony_ci	i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
106162306a36Sopenharmony_ci	i->min = channels;
106262306a36Sopenharmony_ci	i->max = channels;
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	return 0;
106562306a36Sopenharmony_ci}
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_cistatic bool sof_ipc4_is_single_format(struct snd_sof_dev *sdev,
106862306a36Sopenharmony_ci				      struct sof_ipc4_pin_format *pin_fmts, u32 pin_fmts_size)
106962306a36Sopenharmony_ci{
107062306a36Sopenharmony_ci	struct sof_ipc4_audio_format *fmt;
107162306a36Sopenharmony_ci	u32 rate, channels, valid_bits;
107262306a36Sopenharmony_ci	int i;
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	fmt = &pin_fmts[0].audio_fmt;
107562306a36Sopenharmony_ci	rate = fmt->sampling_frequency;
107662306a36Sopenharmony_ci	channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg);
107762306a36Sopenharmony_ci	valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	/* check if all output formats in topology are the same */
108062306a36Sopenharmony_ci	for (i = 1; i < pin_fmts_size; i++) {
108162306a36Sopenharmony_ci		u32 _rate, _channels, _valid_bits;
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci		fmt = &pin_fmts[i].audio_fmt;
108462306a36Sopenharmony_ci		_rate = fmt->sampling_frequency;
108562306a36Sopenharmony_ci		_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg);
108662306a36Sopenharmony_ci		_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci		if (_rate != rate || _channels != channels || _valid_bits != valid_bits)
108962306a36Sopenharmony_ci			return false;
109062306a36Sopenharmony_ci	}
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	return true;
109362306a36Sopenharmony_ci}
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_cistatic int sof_ipc4_init_output_audio_fmt(struct snd_sof_dev *sdev,
109662306a36Sopenharmony_ci					  struct sof_ipc4_base_module_cfg *base_config,
109762306a36Sopenharmony_ci					  struct sof_ipc4_available_audio_format *available_fmt,
109862306a36Sopenharmony_ci					  u32 out_ref_rate, u32 out_ref_channels,
109962306a36Sopenharmony_ci					  u32 out_ref_valid_bits)
110062306a36Sopenharmony_ci{
110162306a36Sopenharmony_ci	struct sof_ipc4_audio_format *out_fmt;
110262306a36Sopenharmony_ci	bool single_format;
110362306a36Sopenharmony_ci	int i;
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	if (!available_fmt->num_output_formats)
110662306a36Sopenharmony_ci		return -EINVAL;
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	single_format = sof_ipc4_is_single_format(sdev, available_fmt->output_pin_fmts,
110962306a36Sopenharmony_ci						  available_fmt->num_output_formats);
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	/* pick the first format if there's only one available or if all formats are the same */
111262306a36Sopenharmony_ci	if (single_format) {
111362306a36Sopenharmony_ci		base_config->obs = available_fmt->output_pin_fmts[0].buffer_size;
111462306a36Sopenharmony_ci		return 0;
111562306a36Sopenharmony_ci	}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	/*
111862306a36Sopenharmony_ci	 * if there are multiple output formats, then choose the output format that matches
111962306a36Sopenharmony_ci	 * the reference params
112062306a36Sopenharmony_ci	 */
112162306a36Sopenharmony_ci	for (i = 0; i < available_fmt->num_output_formats; i++) {
112262306a36Sopenharmony_ci		u32 _out_rate, _out_channels, _out_valid_bits;
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci		out_fmt = &available_fmt->output_pin_fmts[i].audio_fmt;
112562306a36Sopenharmony_ci		_out_rate = out_fmt->sampling_frequency;
112662306a36Sopenharmony_ci		_out_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(out_fmt->fmt_cfg);
112762306a36Sopenharmony_ci		_out_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(out_fmt->fmt_cfg);
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci		if (_out_rate == out_ref_rate && _out_channels == out_ref_channels &&
113062306a36Sopenharmony_ci		    _out_valid_bits == out_ref_valid_bits) {
113162306a36Sopenharmony_ci			base_config->obs = available_fmt->output_pin_fmts[i].buffer_size;
113262306a36Sopenharmony_ci			return i;
113362306a36Sopenharmony_ci		}
113462306a36Sopenharmony_ci	}
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	return -EINVAL;
113762306a36Sopenharmony_ci}
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_cistatic int sof_ipc4_get_valid_bits(struct snd_sof_dev *sdev, struct snd_pcm_hw_params *params)
114062306a36Sopenharmony_ci{
114162306a36Sopenharmony_ci	switch (params_format(params)) {
114262306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_S16_LE:
114362306a36Sopenharmony_ci		return 16;
114462306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_S24_LE:
114562306a36Sopenharmony_ci		return 24;
114662306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_S32_LE:
114762306a36Sopenharmony_ci		return 32;
114862306a36Sopenharmony_ci	default:
114962306a36Sopenharmony_ci		dev_err(sdev->dev, "invalid pcm frame format %d\n", params_format(params));
115062306a36Sopenharmony_ci		return -EINVAL;
115162306a36Sopenharmony_ci	}
115262306a36Sopenharmony_ci}
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_cistatic int sof_ipc4_init_input_audio_fmt(struct snd_sof_dev *sdev,
115562306a36Sopenharmony_ci					 struct snd_sof_widget *swidget,
115662306a36Sopenharmony_ci					 struct sof_ipc4_base_module_cfg *base_config,
115762306a36Sopenharmony_ci					 struct snd_pcm_hw_params *params,
115862306a36Sopenharmony_ci					 struct sof_ipc4_available_audio_format *available_fmt)
115962306a36Sopenharmony_ci{
116062306a36Sopenharmony_ci	struct sof_ipc4_pin_format *pin_fmts = available_fmt->input_pin_fmts;
116162306a36Sopenharmony_ci	u32 pin_fmts_size = available_fmt->num_input_formats;
116262306a36Sopenharmony_ci	u32 valid_bits;
116362306a36Sopenharmony_ci	u32 channels;
116462306a36Sopenharmony_ci	u32 rate;
116562306a36Sopenharmony_ci	bool single_format;
116662306a36Sopenharmony_ci	int sample_valid_bits;
116762306a36Sopenharmony_ci	int i = 0;
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	if (!available_fmt->num_input_formats) {
117062306a36Sopenharmony_ci		dev_err(sdev->dev, "no input formats for %s\n", swidget->widget->name);
117162306a36Sopenharmony_ci		return -EINVAL;
117262306a36Sopenharmony_ci	}
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	single_format = sof_ipc4_is_single_format(sdev, available_fmt->input_pin_fmts,
117562306a36Sopenharmony_ci						  available_fmt->num_input_formats);
117662306a36Sopenharmony_ci	if (single_format)
117762306a36Sopenharmony_ci		goto in_fmt;
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	sample_valid_bits = sof_ipc4_get_valid_bits(sdev, params);
118062306a36Sopenharmony_ci	if (sample_valid_bits < 0)
118162306a36Sopenharmony_ci		return sample_valid_bits;
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	/*
118462306a36Sopenharmony_ci	 * Search supported input audio formats with pin index 0 to match rate, channels and
118562306a36Sopenharmony_ci	 * sample_valid_bits from reference params
118662306a36Sopenharmony_ci	 */
118762306a36Sopenharmony_ci	for (i = 0; i < pin_fmts_size; i++) {
118862306a36Sopenharmony_ci		struct sof_ipc4_audio_format *fmt = &pin_fmts[i].audio_fmt;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci		if (pin_fmts[i].pin_index)
119162306a36Sopenharmony_ci			continue;
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci		rate = fmt->sampling_frequency;
119462306a36Sopenharmony_ci		channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg);
119562306a36Sopenharmony_ci		valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);
119662306a36Sopenharmony_ci		if (params_rate(params) == rate && params_channels(params) == channels &&
119762306a36Sopenharmony_ci		    sample_valid_bits == valid_bits) {
119862306a36Sopenharmony_ci			dev_dbg(sdev->dev, "matched audio format index for %uHz, %ubit, %u channels: %d\n",
119962306a36Sopenharmony_ci				rate, valid_bits, channels, i);
120062306a36Sopenharmony_ci			break;
120162306a36Sopenharmony_ci		}
120262306a36Sopenharmony_ci	}
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	if (i == pin_fmts_size) {
120562306a36Sopenharmony_ci		dev_err(sdev->dev, "%s: Unsupported audio format: %uHz, %ubit, %u channels\n",
120662306a36Sopenharmony_ci			__func__, params_rate(params), sample_valid_bits, params_channels(params));
120762306a36Sopenharmony_ci		return -EINVAL;
120862306a36Sopenharmony_ci	}
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ciin_fmt:
121162306a36Sopenharmony_ci	/* copy input format */
121262306a36Sopenharmony_ci	if (available_fmt->num_input_formats && i < available_fmt->num_input_formats) {
121362306a36Sopenharmony_ci		memcpy(&base_config->audio_fmt, &available_fmt->input_pin_fmts[i].audio_fmt,
121462306a36Sopenharmony_ci		       sizeof(struct sof_ipc4_audio_format));
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci		/* set base_cfg ibs/obs */
121762306a36Sopenharmony_ci		base_config->ibs = available_fmt->input_pin_fmts[i].buffer_size;
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci		dev_dbg(sdev->dev, "Init input audio formats for %s\n", swidget->widget->name);
122062306a36Sopenharmony_ci		sof_ipc4_dbg_audio_format(sdev->dev, &available_fmt->input_pin_fmts[i], 1);
122162306a36Sopenharmony_ci	}
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	return i;
122462306a36Sopenharmony_ci}
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_cistatic void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget)
122762306a36Sopenharmony_ci{
122862306a36Sopenharmony_ci	struct sof_ipc4_copier *ipc4_copier = NULL;
122962306a36Sopenharmony_ci	struct snd_sof_widget *pipe_widget;
123062306a36Sopenharmony_ci	struct sof_ipc4_pipeline *pipeline;
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	/* reset pipeline memory usage */
123362306a36Sopenharmony_ci	pipe_widget = swidget->spipe->pipe_widget;
123462306a36Sopenharmony_ci	pipeline = pipe_widget->private;
123562306a36Sopenharmony_ci	pipeline->mem_usage = 0;
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	if (WIDGET_IS_AIF(swidget->id) || swidget->id == snd_soc_dapm_buffer) {
123862306a36Sopenharmony_ci		if (pipeline->use_chain_dma) {
123962306a36Sopenharmony_ci			pipeline->msg.primary = 0;
124062306a36Sopenharmony_ci			pipeline->msg.extension = 0;
124162306a36Sopenharmony_ci		}
124262306a36Sopenharmony_ci		ipc4_copier = swidget->private;
124362306a36Sopenharmony_ci	} else if (WIDGET_IS_DAI(swidget->id)) {
124462306a36Sopenharmony_ci		struct snd_sof_dai *dai = swidget->private;
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci		ipc4_copier = dai->private;
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci		if (pipeline->use_chain_dma) {
124962306a36Sopenharmony_ci			pipeline->msg.primary = 0;
125062306a36Sopenharmony_ci			pipeline->msg.extension = 0;
125162306a36Sopenharmony_ci		}
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci		if (ipc4_copier->dai_type == SOF_DAI_INTEL_ALH) {
125462306a36Sopenharmony_ci			struct sof_ipc4_copier_data *copier_data = &ipc4_copier->data;
125562306a36Sopenharmony_ci			struct sof_ipc4_alh_configuration_blob *blob;
125662306a36Sopenharmony_ci			unsigned int group_id;
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci			blob = (struct sof_ipc4_alh_configuration_blob *)ipc4_copier->copier_config;
125962306a36Sopenharmony_ci			if (blob->alh_cfg.device_count > 1) {
126062306a36Sopenharmony_ci				group_id = SOF_IPC4_NODE_INDEX(ipc4_copier->data.gtw_cfg.node_id) -
126162306a36Sopenharmony_ci					   ALH_MULTI_GTW_BASE;
126262306a36Sopenharmony_ci				ida_free(&alh_group_ida, group_id);
126362306a36Sopenharmony_ci			}
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci			/* clear the node ID */
126662306a36Sopenharmony_ci			copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
126762306a36Sopenharmony_ci		}
126862306a36Sopenharmony_ci	}
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	if (ipc4_copier) {
127162306a36Sopenharmony_ci		kfree(ipc4_copier->ipc_config_data);
127262306a36Sopenharmony_ci		ipc4_copier->ipc_config_data = NULL;
127362306a36Sopenharmony_ci		ipc4_copier->ipc_config_size = 0;
127462306a36Sopenharmony_ci	}
127562306a36Sopenharmony_ci}
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_SND_INTEL_NHLT)
127862306a36Sopenharmony_cistatic int snd_sof_get_hw_config_params(struct snd_sof_dev *sdev, struct snd_sof_dai *dai,
127962306a36Sopenharmony_ci					int *sample_rate, int *channel_count, int *bit_depth)
128062306a36Sopenharmony_ci{
128162306a36Sopenharmony_ci	struct snd_soc_tplg_hw_config *hw_config;
128262306a36Sopenharmony_ci	struct snd_sof_dai_link *slink;
128362306a36Sopenharmony_ci	bool dai_link_found = false;
128462306a36Sopenharmony_ci	bool hw_cfg_found = false;
128562306a36Sopenharmony_ci	int i;
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	/* get current hw_config from link */
128862306a36Sopenharmony_ci	list_for_each_entry(slink, &sdev->dai_link_list, list) {
128962306a36Sopenharmony_ci		if (!strcmp(slink->link->name, dai->name)) {
129062306a36Sopenharmony_ci			dai_link_found = true;
129162306a36Sopenharmony_ci			break;
129262306a36Sopenharmony_ci		}
129362306a36Sopenharmony_ci	}
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	if (!dai_link_found) {
129662306a36Sopenharmony_ci		dev_err(sdev->dev, "%s: no DAI link found for DAI %s\n", __func__, dai->name);
129762306a36Sopenharmony_ci		return -EINVAL;
129862306a36Sopenharmony_ci	}
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci	for (i = 0; i < slink->num_hw_configs; i++) {
130162306a36Sopenharmony_ci		hw_config = &slink->hw_configs[i];
130262306a36Sopenharmony_ci		if (dai->current_config == le32_to_cpu(hw_config->id)) {
130362306a36Sopenharmony_ci			hw_cfg_found = true;
130462306a36Sopenharmony_ci			break;
130562306a36Sopenharmony_ci		}
130662306a36Sopenharmony_ci	}
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci	if (!hw_cfg_found) {
130962306a36Sopenharmony_ci		dev_err(sdev->dev, "%s: no matching hw_config found for DAI %s\n", __func__,
131062306a36Sopenharmony_ci			dai->name);
131162306a36Sopenharmony_ci		return -EINVAL;
131262306a36Sopenharmony_ci	}
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	*bit_depth = le32_to_cpu(hw_config->tdm_slot_width);
131562306a36Sopenharmony_ci	*channel_count = le32_to_cpu(hw_config->tdm_slots);
131662306a36Sopenharmony_ci	*sample_rate = le32_to_cpu(hw_config->fsync_rate);
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	dev_dbg(sdev->dev, "sample rate: %d sample width: %d channels: %d\n",
131962306a36Sopenharmony_ci		*sample_rate, *bit_depth, *channel_count);
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	return 0;
132262306a36Sopenharmony_ci}
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_cistatic int snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai,
132562306a36Sopenharmony_ci					  struct snd_pcm_hw_params *params, u32 dai_index,
132662306a36Sopenharmony_ci					  u32 linktype, u8 dir, u32 **dst, u32 *len)
132762306a36Sopenharmony_ci{
132862306a36Sopenharmony_ci	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
132962306a36Sopenharmony_ci	struct nhlt_specific_cfg *cfg;
133062306a36Sopenharmony_ci	int sample_rate, channel_count;
133162306a36Sopenharmony_ci	int bit_depth, ret;
133262306a36Sopenharmony_ci	u32 nhlt_type;
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci	/* convert to NHLT type */
133562306a36Sopenharmony_ci	switch (linktype) {
133662306a36Sopenharmony_ci	case SOF_DAI_INTEL_DMIC:
133762306a36Sopenharmony_ci		nhlt_type = NHLT_LINK_DMIC;
133862306a36Sopenharmony_ci		bit_depth = params_width(params);
133962306a36Sopenharmony_ci		channel_count = params_channels(params);
134062306a36Sopenharmony_ci		sample_rate = params_rate(params);
134162306a36Sopenharmony_ci		break;
134262306a36Sopenharmony_ci	case SOF_DAI_INTEL_SSP:
134362306a36Sopenharmony_ci		nhlt_type = NHLT_LINK_SSP;
134462306a36Sopenharmony_ci		ret = snd_sof_get_hw_config_params(sdev, dai, &sample_rate, &channel_count,
134562306a36Sopenharmony_ci						   &bit_depth);
134662306a36Sopenharmony_ci		if (ret < 0)
134762306a36Sopenharmony_ci			return ret;
134862306a36Sopenharmony_ci		break;
134962306a36Sopenharmony_ci	default:
135062306a36Sopenharmony_ci		return 0;
135162306a36Sopenharmony_ci	}
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	dev_dbg(sdev->dev, "dai index %d nhlt type %d direction %d\n",
135462306a36Sopenharmony_ci		dai_index, nhlt_type, dir);
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	/* find NHLT blob with matching params */
135762306a36Sopenharmony_ci	cfg = intel_nhlt_get_endpoint_blob(sdev->dev, ipc4_data->nhlt, dai_index, nhlt_type,
135862306a36Sopenharmony_ci					   bit_depth, bit_depth, channel_count, sample_rate,
135962306a36Sopenharmony_ci					   dir, 0);
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci	if (!cfg) {
136262306a36Sopenharmony_ci		dev_err(sdev->dev,
136362306a36Sopenharmony_ci			"no matching blob for sample rate: %d sample width: %d channels: %d\n",
136462306a36Sopenharmony_ci			sample_rate, bit_depth, channel_count);
136562306a36Sopenharmony_ci		return -EINVAL;
136662306a36Sopenharmony_ci	}
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci	/* config length should be in dwords */
136962306a36Sopenharmony_ci	*len = cfg->size >> 2;
137062306a36Sopenharmony_ci	*dst = (u32 *)cfg->caps;
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci	return 0;
137362306a36Sopenharmony_ci}
137462306a36Sopenharmony_ci#else
137562306a36Sopenharmony_cistatic int snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai,
137662306a36Sopenharmony_ci					  struct snd_pcm_hw_params *params, u32 dai_index,
137762306a36Sopenharmony_ci					  u32 linktype, u8 dir, u32 **dst, u32 *len)
137862306a36Sopenharmony_ci{
137962306a36Sopenharmony_ci	return 0;
138062306a36Sopenharmony_ci}
138162306a36Sopenharmony_ci#endif
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_cistatic bool sof_ipc4_copier_is_single_format(struct snd_sof_dev *sdev,
138462306a36Sopenharmony_ci					     struct sof_ipc4_pin_format *pin_fmts,
138562306a36Sopenharmony_ci					     u32 pin_fmts_size)
138662306a36Sopenharmony_ci{
138762306a36Sopenharmony_ci	struct sof_ipc4_audio_format *fmt;
138862306a36Sopenharmony_ci	u32 valid_bits;
138962306a36Sopenharmony_ci	int i;
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci	fmt = &pin_fmts[0].audio_fmt;
139262306a36Sopenharmony_ci	valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	/* check if all output formats in topology are the same */
139562306a36Sopenharmony_ci	for (i = 1; i < pin_fmts_size; i++) {
139662306a36Sopenharmony_ci		u32 _valid_bits;
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci		fmt = &pin_fmts[i].audio_fmt;
139962306a36Sopenharmony_ci		_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci		if (_valid_bits != valid_bits)
140262306a36Sopenharmony_ci			return false;
140362306a36Sopenharmony_ci	}
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci	return true;
140662306a36Sopenharmony_ci}
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_cistatic int
140962306a36Sopenharmony_cisof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
141062306a36Sopenharmony_ci			       struct snd_pcm_hw_params *fe_params,
141162306a36Sopenharmony_ci			       struct snd_sof_platform_stream_params *platform_params,
141262306a36Sopenharmony_ci			       struct snd_pcm_hw_params *pipeline_params, int dir)
141362306a36Sopenharmony_ci{
141462306a36Sopenharmony_ci	struct sof_ipc4_available_audio_format *available_fmt;
141562306a36Sopenharmony_ci	struct snd_soc_component *scomp = swidget->scomp;
141662306a36Sopenharmony_ci	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
141762306a36Sopenharmony_ci	struct sof_ipc4_copier_data *copier_data;
141862306a36Sopenharmony_ci	struct snd_pcm_hw_params *ref_params;
141962306a36Sopenharmony_ci	struct sof_ipc4_copier *ipc4_copier;
142062306a36Sopenharmony_ci	struct snd_sof_dai *dai;
142162306a36Sopenharmony_ci	u32 gtw_cfg_config_length;
142262306a36Sopenharmony_ci	u32 dma_config_tlv_size = 0;
142362306a36Sopenharmony_ci	void **ipc_config_data;
142462306a36Sopenharmony_ci	int *ipc_config_size;
142562306a36Sopenharmony_ci	u32 **data;
142662306a36Sopenharmony_ci	int ipc_size, ret, out_ref_valid_bits;
142762306a36Sopenharmony_ci	u32 out_ref_rate, out_ref_channels;
142862306a36Sopenharmony_ci	u32 deep_buffer_dma_ms = 0;
142962306a36Sopenharmony_ci	int output_fmt_index;
143062306a36Sopenharmony_ci	bool single_output_format;
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	dev_dbg(sdev->dev, "copier %s, type %d", swidget->widget->name, swidget->id);
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	switch (swidget->id) {
143562306a36Sopenharmony_ci	case snd_soc_dapm_aif_in:
143662306a36Sopenharmony_ci	case snd_soc_dapm_aif_out:
143762306a36Sopenharmony_ci	{
143862306a36Sopenharmony_ci		struct sof_ipc4_gtw_attributes *gtw_attr;
143962306a36Sopenharmony_ci		struct snd_sof_widget *pipe_widget;
144062306a36Sopenharmony_ci		struct sof_ipc4_pipeline *pipeline;
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci		/* parse the deep buffer dma size */
144362306a36Sopenharmony_ci		ret = sof_update_ipc_object(scomp, &deep_buffer_dma_ms,
144462306a36Sopenharmony_ci					    SOF_COPIER_DEEP_BUFFER_TOKENS, swidget->tuples,
144562306a36Sopenharmony_ci					    swidget->num_tuples, sizeof(u32), 1);
144662306a36Sopenharmony_ci		if (ret) {
144762306a36Sopenharmony_ci			dev_err(scomp->dev, "Failed to parse deep buffer dma size for %s\n",
144862306a36Sopenharmony_ci				swidget->widget->name);
144962306a36Sopenharmony_ci			return ret;
145062306a36Sopenharmony_ci		}
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci		ipc4_copier = (struct sof_ipc4_copier *)swidget->private;
145362306a36Sopenharmony_ci		gtw_attr = ipc4_copier->gtw_attr;
145462306a36Sopenharmony_ci		copier_data = &ipc4_copier->data;
145562306a36Sopenharmony_ci		available_fmt = &ipc4_copier->available_fmt;
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci		pipe_widget = swidget->spipe->pipe_widget;
145862306a36Sopenharmony_ci		pipeline = pipe_widget->private;
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci		if (pipeline->use_chain_dma) {
146162306a36Sopenharmony_ci			u32 host_dma_id;
146262306a36Sopenharmony_ci			u32 fifo_size;
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci			host_dma_id = platform_params->stream_tag - 1;
146562306a36Sopenharmony_ci			pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_HOST_ID(host_dma_id);
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci			/* Set SCS bit for S16_LE format only */
146862306a36Sopenharmony_ci			if (params_format(fe_params) == SNDRV_PCM_FORMAT_S16_LE)
146962306a36Sopenharmony_ci				pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_SCS_MASK;
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci			/*
147262306a36Sopenharmony_ci			 * Despite its name the bitfield 'fifo_size' is used to define DMA buffer
147362306a36Sopenharmony_ci			 * size. The expression calculates 2ms buffer size.
147462306a36Sopenharmony_ci			 */
147562306a36Sopenharmony_ci			fifo_size = DIV_ROUND_UP((SOF_IPC4_CHAIN_DMA_BUF_SIZE_MS *
147662306a36Sopenharmony_ci						  params_rate(fe_params) *
147762306a36Sopenharmony_ci						  params_channels(fe_params) *
147862306a36Sopenharmony_ci						  params_physical_width(fe_params)), 8000);
147962306a36Sopenharmony_ci			pipeline->msg.extension |= SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE(fifo_size);
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci			/*
148262306a36Sopenharmony_ci			 * Chain DMA does not support stream timestamping, set node_id to invalid
148362306a36Sopenharmony_ci			 * to skip the code in sof_ipc4_get_stream_start_offset().
148462306a36Sopenharmony_ci			 */
148562306a36Sopenharmony_ci			copier_data->gtw_cfg.node_id = SOF_IPC4_INVALID_NODE_ID;
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci			return 0;
148862306a36Sopenharmony_ci		}
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci		/*
149162306a36Sopenharmony_ci		 * Use the input_pin_fmts to match pcm params for playback and the output_pin_fmts
149262306a36Sopenharmony_ci		 * for capture.
149362306a36Sopenharmony_ci		 */
149462306a36Sopenharmony_ci		if (dir == SNDRV_PCM_STREAM_PLAYBACK)
149562306a36Sopenharmony_ci			ref_params = fe_params;
149662306a36Sopenharmony_ci		else
149762306a36Sopenharmony_ci			ref_params = pipeline_params;
149862306a36Sopenharmony_ci
149962306a36Sopenharmony_ci		copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
150062306a36Sopenharmony_ci		copier_data->gtw_cfg.node_id |=
150162306a36Sopenharmony_ci			SOF_IPC4_NODE_INDEX(platform_params->stream_tag - 1);
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci		/* set gateway attributes */
150462306a36Sopenharmony_ci		gtw_attr->lp_buffer_alloc = pipeline->lp_mode;
150562306a36Sopenharmony_ci		break;
150662306a36Sopenharmony_ci	}
150762306a36Sopenharmony_ci	case snd_soc_dapm_dai_in:
150862306a36Sopenharmony_ci	case snd_soc_dapm_dai_out:
150962306a36Sopenharmony_ci	{
151062306a36Sopenharmony_ci		struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
151162306a36Sopenharmony_ci		struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci		if (pipeline->use_chain_dma)
151462306a36Sopenharmony_ci			return 0;
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci		dai = swidget->private;
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci		ipc4_copier = (struct sof_ipc4_copier *)dai->private;
151962306a36Sopenharmony_ci		copier_data = &ipc4_copier->data;
152062306a36Sopenharmony_ci		available_fmt = &ipc4_copier->available_fmt;
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci		/*
152362306a36Sopenharmony_ci		 * When there is format conversion within a pipeline, the number of supported
152462306a36Sopenharmony_ci		 * output formats is typically limited to just 1 for the DAI copiers. But when there
152562306a36Sopenharmony_ci		 * is no format conversion, the DAI copiers input format must match that of the
152662306a36Sopenharmony_ci		 * FE hw_params for capture and the pipeline params for playback.
152762306a36Sopenharmony_ci		 */
152862306a36Sopenharmony_ci		if (dir == SNDRV_PCM_STREAM_PLAYBACK)
152962306a36Sopenharmony_ci			ref_params = pipeline_params;
153062306a36Sopenharmony_ci		else
153162306a36Sopenharmony_ci			ref_params = fe_params;
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci		ret = snd_sof_get_nhlt_endpoint_data(sdev, dai, fe_params, ipc4_copier->dai_index,
153462306a36Sopenharmony_ci						     ipc4_copier->dai_type, dir,
153562306a36Sopenharmony_ci						     &ipc4_copier->copier_config,
153662306a36Sopenharmony_ci						     &copier_data->gtw_cfg.config_length);
153762306a36Sopenharmony_ci		if (ret < 0)
153862306a36Sopenharmony_ci			return ret;
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci		break;
154162306a36Sopenharmony_ci	}
154262306a36Sopenharmony_ci	case snd_soc_dapm_buffer:
154362306a36Sopenharmony_ci	{
154462306a36Sopenharmony_ci		ipc4_copier = (struct sof_ipc4_copier *)swidget->private;
154562306a36Sopenharmony_ci		copier_data = &ipc4_copier->data;
154662306a36Sopenharmony_ci		available_fmt = &ipc4_copier->available_fmt;
154762306a36Sopenharmony_ci		ref_params = pipeline_params;
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci		break;
155062306a36Sopenharmony_ci	}
155162306a36Sopenharmony_ci	default:
155262306a36Sopenharmony_ci		dev_err(sdev->dev, "unsupported type %d for copier %s",
155362306a36Sopenharmony_ci			swidget->id, swidget->widget->name);
155462306a36Sopenharmony_ci		return -EINVAL;
155562306a36Sopenharmony_ci	}
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_ci	/* set input and output audio formats */
155862306a36Sopenharmony_ci	ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &copier_data->base_config, ref_params,
155962306a36Sopenharmony_ci					    available_fmt);
156062306a36Sopenharmony_ci	if (ret < 0)
156162306a36Sopenharmony_ci		return ret;
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci	/* set the reference params for output format selection */
156462306a36Sopenharmony_ci	single_output_format = sof_ipc4_copier_is_single_format(sdev,
156562306a36Sopenharmony_ci								available_fmt->output_pin_fmts,
156662306a36Sopenharmony_ci								available_fmt->num_output_formats);
156762306a36Sopenharmony_ci	switch (swidget->id) {
156862306a36Sopenharmony_ci	case snd_soc_dapm_aif_in:
156962306a36Sopenharmony_ci	case snd_soc_dapm_dai_out:
157062306a36Sopenharmony_ci	case snd_soc_dapm_buffer:
157162306a36Sopenharmony_ci	{
157262306a36Sopenharmony_ci		struct sof_ipc4_audio_format *in_fmt;
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_ci		in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt;
157562306a36Sopenharmony_ci		out_ref_rate = in_fmt->sampling_frequency;
157662306a36Sopenharmony_ci		out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci		if (!single_output_format)
157962306a36Sopenharmony_ci			out_ref_valid_bits =
158062306a36Sopenharmony_ci				SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);
158162306a36Sopenharmony_ci		break;
158262306a36Sopenharmony_ci	}
158362306a36Sopenharmony_ci	case snd_soc_dapm_aif_out:
158462306a36Sopenharmony_ci	case snd_soc_dapm_dai_in:
158562306a36Sopenharmony_ci		out_ref_rate = params_rate(fe_params);
158662306a36Sopenharmony_ci		out_ref_channels = params_channels(fe_params);
158762306a36Sopenharmony_ci		if (!single_output_format) {
158862306a36Sopenharmony_ci			out_ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params);
158962306a36Sopenharmony_ci			if (out_ref_valid_bits < 0)
159062306a36Sopenharmony_ci				return out_ref_valid_bits;
159162306a36Sopenharmony_ci		}
159262306a36Sopenharmony_ci		break;
159362306a36Sopenharmony_ci	default:
159462306a36Sopenharmony_ci		/*
159562306a36Sopenharmony_ci		 * Unsupported type should be caught by the former switch default
159662306a36Sopenharmony_ci		 * case, this should never happen in reality.
159762306a36Sopenharmony_ci		 */
159862306a36Sopenharmony_ci		return -EINVAL;
159962306a36Sopenharmony_ci	}
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci	/*
160262306a36Sopenharmony_ci	 * if the output format is the same across all available output formats, choose
160362306a36Sopenharmony_ci	 * that as the reference.
160462306a36Sopenharmony_ci	 */
160562306a36Sopenharmony_ci	if (single_output_format) {
160662306a36Sopenharmony_ci		struct sof_ipc4_audio_format *out_fmt;
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_ci		out_fmt = &available_fmt->output_pin_fmts[0].audio_fmt;
160962306a36Sopenharmony_ci		out_ref_valid_bits =
161062306a36Sopenharmony_ci			SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(out_fmt->fmt_cfg);
161162306a36Sopenharmony_ci	}
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci	dev_dbg(sdev->dev, "copier %s: reference output rate %d, channels %d valid_bits %d\n",
161462306a36Sopenharmony_ci		swidget->widget->name, out_ref_rate, out_ref_channels, out_ref_valid_bits);
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ci	output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, &copier_data->base_config,
161762306a36Sopenharmony_ci							  available_fmt, out_ref_rate,
161862306a36Sopenharmony_ci							  out_ref_channels, out_ref_valid_bits);
161962306a36Sopenharmony_ci	if (output_fmt_index < 0) {
162062306a36Sopenharmony_ci		dev_err(sdev->dev, "Failed to initialize output format for %s",
162162306a36Sopenharmony_ci			swidget->widget->name);
162262306a36Sopenharmony_ci		return output_fmt_index;
162362306a36Sopenharmony_ci	}
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	/*
162662306a36Sopenharmony_ci	 * Set the output format. Current topology defines pin 0 input and output formats in pairs.
162762306a36Sopenharmony_ci	 * This assumes that the pin 0 formats are defined before all other pins.
162862306a36Sopenharmony_ci	 * So pick the output audio format with the same index as the chosen
162962306a36Sopenharmony_ci	 * input format. This logic will need to be updated when the format definitions
163062306a36Sopenharmony_ci	 * in topology change.
163162306a36Sopenharmony_ci	 */
163262306a36Sopenharmony_ci	memcpy(&copier_data->out_format,
163362306a36Sopenharmony_ci	       &available_fmt->output_pin_fmts[output_fmt_index].audio_fmt,
163462306a36Sopenharmony_ci	       sizeof(struct sof_ipc4_audio_format));
163562306a36Sopenharmony_ci	dev_dbg(sdev->dev, "Output audio format for %s\n", swidget->widget->name);
163662306a36Sopenharmony_ci	sof_ipc4_dbg_audio_format(sdev->dev, &available_fmt->output_pin_fmts[output_fmt_index], 1);
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci	switch (swidget->id) {
163962306a36Sopenharmony_ci	case snd_soc_dapm_dai_in:
164062306a36Sopenharmony_ci	case snd_soc_dapm_dai_out:
164162306a36Sopenharmony_ci	{
164262306a36Sopenharmony_ci		/*
164362306a36Sopenharmony_ci		 * Only SOF_DAI_INTEL_ALH needs copier_data to set blob.
164462306a36Sopenharmony_ci		 * That's why only ALH dai's blob is set after sof_ipc4_init_input_audio_fmt
164562306a36Sopenharmony_ci		 */
164662306a36Sopenharmony_ci		if (ipc4_copier->dai_type == SOF_DAI_INTEL_ALH) {
164762306a36Sopenharmony_ci			struct sof_ipc4_alh_configuration_blob *blob;
164862306a36Sopenharmony_ci			struct sof_ipc4_copier_data *alh_data;
164962306a36Sopenharmony_ci			struct sof_ipc4_copier *alh_copier;
165062306a36Sopenharmony_ci			struct snd_sof_widget *w;
165162306a36Sopenharmony_ci			u32 ch_count = 0;
165262306a36Sopenharmony_ci			u32 ch_mask = 0;
165362306a36Sopenharmony_ci			u32 ch_map;
165462306a36Sopenharmony_ci			u32 step;
165562306a36Sopenharmony_ci			u32 mask;
165662306a36Sopenharmony_ci			int i;
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_ci			blob = (struct sof_ipc4_alh_configuration_blob *)ipc4_copier->copier_config;
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_ci			blob->gw_attr.lp_buffer_alloc = 0;
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_ci			/* Get channel_mask from ch_map */
166362306a36Sopenharmony_ci			ch_map = copier_data->base_config.audio_fmt.ch_map;
166462306a36Sopenharmony_ci			for (i = 0; ch_map; i++) {
166562306a36Sopenharmony_ci				if ((ch_map & 0xf) != 0xf) {
166662306a36Sopenharmony_ci					ch_mask |= BIT(i);
166762306a36Sopenharmony_ci					ch_count++;
166862306a36Sopenharmony_ci				}
166962306a36Sopenharmony_ci				ch_map >>= 4;
167062306a36Sopenharmony_ci			}
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci			step = ch_count / blob->alh_cfg.device_count;
167362306a36Sopenharmony_ci			mask =  GENMASK(step - 1, 0);
167462306a36Sopenharmony_ci			/*
167562306a36Sopenharmony_ci			 * Set each gtw_cfg.node_id to blob->alh_cfg.mapping[]
167662306a36Sopenharmony_ci			 * for all widgets with the same stream name
167762306a36Sopenharmony_ci			 */
167862306a36Sopenharmony_ci			i = 0;
167962306a36Sopenharmony_ci			list_for_each_entry(w, &sdev->widget_list, list) {
168062306a36Sopenharmony_ci				if (w->widget->sname &&
168162306a36Sopenharmony_ci				    strcmp(w->widget->sname, swidget->widget->sname))
168262306a36Sopenharmony_ci					continue;
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_ci				dai = w->private;
168562306a36Sopenharmony_ci				alh_copier = (struct sof_ipc4_copier *)dai->private;
168662306a36Sopenharmony_ci				alh_data = &alh_copier->data;
168762306a36Sopenharmony_ci				blob->alh_cfg.mapping[i].device = alh_data->gtw_cfg.node_id;
168862306a36Sopenharmony_ci				/*
168962306a36Sopenharmony_ci				 * Set the same channel mask for playback as the audio data is
169062306a36Sopenharmony_ci				 * duplicated for all speakers. For capture, split the channels
169162306a36Sopenharmony_ci				 * among the aggregated DAIs. For example, with 4 channels on 2
169262306a36Sopenharmony_ci				 * aggregated DAIs, the channel_mask should be 0x3 and 0xc for the
169362306a36Sopenharmony_ci				 * two DAI's.
169462306a36Sopenharmony_ci				 * The channel masks used depend on the cpu_dais used in the
169562306a36Sopenharmony_ci				 * dailink at the machine driver level, which actually comes from
169662306a36Sopenharmony_ci				 * the tables in soc_acpi files depending on the _ADR and devID
169762306a36Sopenharmony_ci				 * registers for each codec.
169862306a36Sopenharmony_ci				 */
169962306a36Sopenharmony_ci				if (w->id == snd_soc_dapm_dai_in)
170062306a36Sopenharmony_ci					blob->alh_cfg.mapping[i].channel_mask = ch_mask;
170162306a36Sopenharmony_ci				else
170262306a36Sopenharmony_ci					blob->alh_cfg.mapping[i].channel_mask = mask << (step * i);
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci				i++;
170562306a36Sopenharmony_ci			}
170662306a36Sopenharmony_ci			if (blob->alh_cfg.device_count > 1) {
170762306a36Sopenharmony_ci				int group_id;
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci				group_id = ida_alloc_max(&alh_group_ida, ALH_MULTI_GTW_COUNT - 1,
171062306a36Sopenharmony_ci							 GFP_KERNEL);
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci				if (group_id < 0)
171362306a36Sopenharmony_ci					return group_id;
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_ci				/* add multi-gateway base */
171662306a36Sopenharmony_ci				group_id += ALH_MULTI_GTW_BASE;
171762306a36Sopenharmony_ci				copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
171862306a36Sopenharmony_ci				copier_data->gtw_cfg.node_id |= SOF_IPC4_NODE_INDEX(group_id);
171962306a36Sopenharmony_ci			}
172062306a36Sopenharmony_ci		}
172162306a36Sopenharmony_ci	}
172262306a36Sopenharmony_ci	}
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci	/* modify the input params for the next widget */
172562306a36Sopenharmony_ci	ret = sof_ipc4_update_hw_params(sdev, pipeline_params, &copier_data->out_format);
172662306a36Sopenharmony_ci	if (ret)
172762306a36Sopenharmony_ci		return ret;
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci	/*
173062306a36Sopenharmony_ci	 * Set the gateway dma_buffer_size to 2ms buffer size to meet the FW expectation. In the
173162306a36Sopenharmony_ci	 * deep buffer case, set the dma_buffer_size depending on the deep_buffer_dma_ms set
173262306a36Sopenharmony_ci	 * in topology.
173362306a36Sopenharmony_ci	 */
173462306a36Sopenharmony_ci	switch (swidget->id) {
173562306a36Sopenharmony_ci	case snd_soc_dapm_dai_in:
173662306a36Sopenharmony_ci		copier_data->gtw_cfg.dma_buffer_size =
173762306a36Sopenharmony_ci			SOF_IPC4_MIN_DMA_BUFFER_SIZE * copier_data->base_config.ibs;
173862306a36Sopenharmony_ci		break;
173962306a36Sopenharmony_ci	case snd_soc_dapm_aif_in:
174062306a36Sopenharmony_ci			copier_data->gtw_cfg.dma_buffer_size =
174162306a36Sopenharmony_ci				max((u32)SOF_IPC4_MIN_DMA_BUFFER_SIZE, deep_buffer_dma_ms) *
174262306a36Sopenharmony_ci					copier_data->base_config.ibs;
174362306a36Sopenharmony_ci		break;
174462306a36Sopenharmony_ci	case snd_soc_dapm_dai_out:
174562306a36Sopenharmony_ci	case snd_soc_dapm_aif_out:
174662306a36Sopenharmony_ci		copier_data->gtw_cfg.dma_buffer_size =
174762306a36Sopenharmony_ci			SOF_IPC4_MIN_DMA_BUFFER_SIZE * copier_data->base_config.obs;
174862306a36Sopenharmony_ci		break;
174962306a36Sopenharmony_ci	default:
175062306a36Sopenharmony_ci		break;
175162306a36Sopenharmony_ci	}
175262306a36Sopenharmony_ci
175362306a36Sopenharmony_ci	data = &ipc4_copier->copier_config;
175462306a36Sopenharmony_ci	ipc_config_size = &ipc4_copier->ipc_config_size;
175562306a36Sopenharmony_ci	ipc_config_data = &ipc4_copier->ipc_config_data;
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_ci	/* config_length is DWORD based */
175862306a36Sopenharmony_ci	gtw_cfg_config_length = copier_data->gtw_cfg.config_length * 4;
175962306a36Sopenharmony_ci	ipc_size = sizeof(*copier_data) + gtw_cfg_config_length;
176062306a36Sopenharmony_ci
176162306a36Sopenharmony_ci	if (ipc4_copier->dma_config_tlv.type == SOF_IPC4_GTW_DMA_CONFIG_ID &&
176262306a36Sopenharmony_ci	    ipc4_copier->dma_config_tlv.length) {
176362306a36Sopenharmony_ci		dma_config_tlv_size = sizeof(ipc4_copier->dma_config_tlv) +
176462306a36Sopenharmony_ci			ipc4_copier->dma_config_tlv.dma_config.dma_priv_config_size;
176562306a36Sopenharmony_ci
176662306a36Sopenharmony_ci		/* paranoia check on TLV size/length */
176762306a36Sopenharmony_ci		if (dma_config_tlv_size != ipc4_copier->dma_config_tlv.length +
176862306a36Sopenharmony_ci		    sizeof(uint32_t) * 2) {
176962306a36Sopenharmony_ci			dev_err(sdev->dev, "Invalid configuration, TLV size %d length %d\n",
177062306a36Sopenharmony_ci				dma_config_tlv_size, ipc4_copier->dma_config_tlv.length);
177162306a36Sopenharmony_ci			return -EINVAL;
177262306a36Sopenharmony_ci		}
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_ci		ipc_size += dma_config_tlv_size;
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_ci		/* we also need to increase the size at the gtw level */
177762306a36Sopenharmony_ci		copier_data->gtw_cfg.config_length += dma_config_tlv_size / 4;
177862306a36Sopenharmony_ci	}
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_ci	dev_dbg(sdev->dev, "copier %s, IPC size is %d", swidget->widget->name, ipc_size);
178162306a36Sopenharmony_ci
178262306a36Sopenharmony_ci	*ipc_config_data = kzalloc(ipc_size, GFP_KERNEL);
178362306a36Sopenharmony_ci	if (!*ipc_config_data)
178462306a36Sopenharmony_ci		return -ENOMEM;
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci	*ipc_config_size = ipc_size;
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_ci	/* update pipeline memory usage */
178962306a36Sopenharmony_ci	sof_ipc4_update_resource_usage(sdev, swidget, &copier_data->base_config);
179062306a36Sopenharmony_ci
179162306a36Sopenharmony_ci	/* copy IPC data */
179262306a36Sopenharmony_ci	memcpy(*ipc_config_data, (void *)copier_data, sizeof(*copier_data));
179362306a36Sopenharmony_ci	if (gtw_cfg_config_length)
179462306a36Sopenharmony_ci		memcpy(*ipc_config_data + sizeof(*copier_data),
179562306a36Sopenharmony_ci		       *data, gtw_cfg_config_length);
179662306a36Sopenharmony_ci
179762306a36Sopenharmony_ci	/* add DMA Config TLV, if configured */
179862306a36Sopenharmony_ci	if (dma_config_tlv_size)
179962306a36Sopenharmony_ci		memcpy(*ipc_config_data + sizeof(*copier_data) +
180062306a36Sopenharmony_ci		       gtw_cfg_config_length,
180162306a36Sopenharmony_ci		       &ipc4_copier->dma_config_tlv, dma_config_tlv_size);
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_ci	/*
180462306a36Sopenharmony_ci	 * Restore gateway config length now that IPC payload is prepared. This avoids
180562306a36Sopenharmony_ci	 * counting the DMA CONFIG TLV multiple times
180662306a36Sopenharmony_ci	 */
180762306a36Sopenharmony_ci	copier_data->gtw_cfg.config_length = gtw_cfg_config_length / 4;
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci	return 0;
181062306a36Sopenharmony_ci}
181162306a36Sopenharmony_ci
181262306a36Sopenharmony_cistatic int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget,
181362306a36Sopenharmony_ci					struct snd_pcm_hw_params *fe_params,
181462306a36Sopenharmony_ci					struct snd_sof_platform_stream_params *platform_params,
181562306a36Sopenharmony_ci					struct snd_pcm_hw_params *pipeline_params, int dir)
181662306a36Sopenharmony_ci{
181762306a36Sopenharmony_ci	struct snd_soc_component *scomp = swidget->scomp;
181862306a36Sopenharmony_ci	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
181962306a36Sopenharmony_ci	struct sof_ipc4_gain *gain = swidget->private;
182062306a36Sopenharmony_ci	struct sof_ipc4_available_audio_format *available_fmt = &gain->available_fmt;
182162306a36Sopenharmony_ci	struct sof_ipc4_audio_format *in_fmt;
182262306a36Sopenharmony_ci	u32 out_ref_rate, out_ref_channels, out_ref_valid_bits;
182362306a36Sopenharmony_ci	int ret;
182462306a36Sopenharmony_ci
182562306a36Sopenharmony_ci	ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &gain->data.base_config,
182662306a36Sopenharmony_ci					    pipeline_params, available_fmt);
182762306a36Sopenharmony_ci	if (ret < 0)
182862306a36Sopenharmony_ci		return ret;
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_ci	in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt;
183162306a36Sopenharmony_ci	out_ref_rate = in_fmt->sampling_frequency;
183262306a36Sopenharmony_ci	out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
183362306a36Sopenharmony_ci	out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);
183462306a36Sopenharmony_ci
183562306a36Sopenharmony_ci	ret = sof_ipc4_init_output_audio_fmt(sdev, &gain->data.base_config, available_fmt,
183662306a36Sopenharmony_ci					     out_ref_rate, out_ref_channels, out_ref_valid_bits);
183762306a36Sopenharmony_ci	if (ret < 0) {
183862306a36Sopenharmony_ci		dev_err(sdev->dev, "Failed to initialize output format for %s",
183962306a36Sopenharmony_ci			swidget->widget->name);
184062306a36Sopenharmony_ci		return ret;
184162306a36Sopenharmony_ci	}
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_ci	/* update pipeline memory usage */
184462306a36Sopenharmony_ci	sof_ipc4_update_resource_usage(sdev, swidget, &gain->data.base_config);
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_ci	return 0;
184762306a36Sopenharmony_ci}
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_cistatic int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget,
185062306a36Sopenharmony_ci					 struct snd_pcm_hw_params *fe_params,
185162306a36Sopenharmony_ci					 struct snd_sof_platform_stream_params *platform_params,
185262306a36Sopenharmony_ci					 struct snd_pcm_hw_params *pipeline_params, int dir)
185362306a36Sopenharmony_ci{
185462306a36Sopenharmony_ci	struct snd_soc_component *scomp = swidget->scomp;
185562306a36Sopenharmony_ci	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
185662306a36Sopenharmony_ci	struct sof_ipc4_mixer *mixer = swidget->private;
185762306a36Sopenharmony_ci	struct sof_ipc4_available_audio_format *available_fmt = &mixer->available_fmt;
185862306a36Sopenharmony_ci	struct sof_ipc4_audio_format *in_fmt;
185962306a36Sopenharmony_ci	u32 out_ref_rate, out_ref_channels, out_ref_valid_bits;
186062306a36Sopenharmony_ci	int ret;
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci	ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &mixer->base_config,
186362306a36Sopenharmony_ci					    pipeline_params, available_fmt);
186462306a36Sopenharmony_ci	if (ret < 0)
186562306a36Sopenharmony_ci		return ret;
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ci	in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt;
186862306a36Sopenharmony_ci	out_ref_rate = in_fmt->sampling_frequency;
186962306a36Sopenharmony_ci	out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
187062306a36Sopenharmony_ci	out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci	ret = sof_ipc4_init_output_audio_fmt(sdev, &mixer->base_config, available_fmt,
187362306a36Sopenharmony_ci					     out_ref_rate, out_ref_channels, out_ref_valid_bits);
187462306a36Sopenharmony_ci	if (ret < 0) {
187562306a36Sopenharmony_ci		dev_err(sdev->dev, "Failed to initialize output format for %s",
187662306a36Sopenharmony_ci			swidget->widget->name);
187762306a36Sopenharmony_ci		return ret;
187862306a36Sopenharmony_ci	}
187962306a36Sopenharmony_ci
188062306a36Sopenharmony_ci	/* update pipeline memory usage */
188162306a36Sopenharmony_ci	sof_ipc4_update_resource_usage(sdev, swidget, &mixer->base_config);
188262306a36Sopenharmony_ci
188362306a36Sopenharmony_ci	return 0;
188462306a36Sopenharmony_ci}
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_cistatic int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget,
188762306a36Sopenharmony_ci				       struct snd_pcm_hw_params *fe_params,
188862306a36Sopenharmony_ci				       struct snd_sof_platform_stream_params *platform_params,
188962306a36Sopenharmony_ci				       struct snd_pcm_hw_params *pipeline_params, int dir)
189062306a36Sopenharmony_ci{
189162306a36Sopenharmony_ci	struct snd_soc_component *scomp = swidget->scomp;
189262306a36Sopenharmony_ci	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
189362306a36Sopenharmony_ci	struct sof_ipc4_src *src = swidget->private;
189462306a36Sopenharmony_ci	struct sof_ipc4_available_audio_format *available_fmt = &src->available_fmt;
189562306a36Sopenharmony_ci	struct sof_ipc4_audio_format *out_audio_fmt;
189662306a36Sopenharmony_ci	struct sof_ipc4_audio_format *in_audio_fmt;
189762306a36Sopenharmony_ci	u32 out_ref_rate, out_ref_channels, out_ref_valid_bits;
189862306a36Sopenharmony_ci	int output_format_index, input_format_index;
189962306a36Sopenharmony_ci
190062306a36Sopenharmony_ci	input_format_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, &src->data.base_config,
190162306a36Sopenharmony_ci							   pipeline_params, available_fmt);
190262306a36Sopenharmony_ci	if (input_format_index < 0)
190362306a36Sopenharmony_ci		return input_format_index;
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_ci	/*
190662306a36Sopenharmony_ci	 * For playback, the SRC sink rate will be configured based on the requested output
190762306a36Sopenharmony_ci	 * format, which is restricted to only deal with DAI's with a single format for now.
190862306a36Sopenharmony_ci	 */
190962306a36Sopenharmony_ci	if (dir == SNDRV_PCM_STREAM_PLAYBACK && available_fmt->num_output_formats > 1) {
191062306a36Sopenharmony_ci		dev_err(sdev->dev, "Invalid number of output formats: %d for SRC %s\n",
191162306a36Sopenharmony_ci			available_fmt->num_output_formats, swidget->widget->name);
191262306a36Sopenharmony_ci		return -EINVAL;
191362306a36Sopenharmony_ci	}
191462306a36Sopenharmony_ci
191562306a36Sopenharmony_ci	/*
191662306a36Sopenharmony_ci	 * SRC does not perform format conversion, so the output channels and valid bit depth must
191762306a36Sopenharmony_ci	 * be the same as that of the input.
191862306a36Sopenharmony_ci	 */
191962306a36Sopenharmony_ci	in_audio_fmt = &available_fmt->input_pin_fmts[input_format_index].audio_fmt;
192062306a36Sopenharmony_ci	out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_audio_fmt->fmt_cfg);
192162306a36Sopenharmony_ci	out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_audio_fmt->fmt_cfg);
192262306a36Sopenharmony_ci
192362306a36Sopenharmony_ci	/*
192462306a36Sopenharmony_ci	 * For capture, the SRC module should convert the rate to match the rate requested by the
192562306a36Sopenharmony_ci	 * PCM hw_params. Set the reference params based on the fe_params unconditionally as it
192662306a36Sopenharmony_ci	 * will be ignored for playback anyway.
192762306a36Sopenharmony_ci	 */
192862306a36Sopenharmony_ci	out_ref_rate = params_rate(fe_params);
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_ci	output_format_index = sof_ipc4_init_output_audio_fmt(sdev, &src->data.base_config,
193162306a36Sopenharmony_ci							     available_fmt, out_ref_rate,
193262306a36Sopenharmony_ci							     out_ref_channels, out_ref_valid_bits);
193362306a36Sopenharmony_ci	if (output_format_index < 0) {
193462306a36Sopenharmony_ci		dev_err(sdev->dev, "Failed to initialize output format for %s",
193562306a36Sopenharmony_ci			swidget->widget->name);
193662306a36Sopenharmony_ci		return output_format_index;
193762306a36Sopenharmony_ci	}
193862306a36Sopenharmony_ci
193962306a36Sopenharmony_ci	/* update pipeline memory usage */
194062306a36Sopenharmony_ci	sof_ipc4_update_resource_usage(sdev, swidget, &src->data.base_config);
194162306a36Sopenharmony_ci
194262306a36Sopenharmony_ci	out_audio_fmt = &available_fmt->output_pin_fmts[output_format_index].audio_fmt;
194362306a36Sopenharmony_ci	src->data.sink_rate = out_audio_fmt->sampling_frequency;
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_ci	/* update pipeline_params for sink widgets */
194662306a36Sopenharmony_ci	return sof_ipc4_update_hw_params(sdev, pipeline_params, out_audio_fmt);
194762306a36Sopenharmony_ci}
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_cistatic int
195062306a36Sopenharmony_cisof_ipc4_process_set_pin_formats(struct snd_sof_widget *swidget, int pin_type)
195162306a36Sopenharmony_ci{
195262306a36Sopenharmony_ci	struct sof_ipc4_process *process = swidget->private;
195362306a36Sopenharmony_ci	struct sof_ipc4_base_module_cfg_ext *base_cfg_ext = process->base_config_ext;
195462306a36Sopenharmony_ci	struct sof_ipc4_available_audio_format *available_fmt = &process->available_fmt;
195562306a36Sopenharmony_ci	struct sof_ipc4_pin_format *pin_format, *format_list_to_search;
195662306a36Sopenharmony_ci	struct snd_soc_component *scomp = swidget->scomp;
195762306a36Sopenharmony_ci	int num_pins, format_list_count;
195862306a36Sopenharmony_ci	int pin_format_offset = 0;
195962306a36Sopenharmony_ci	int i, j;
196062306a36Sopenharmony_ci
196162306a36Sopenharmony_ci	/* set number of pins, offset of pin format and format list to search based on pin type */
196262306a36Sopenharmony_ci	if (pin_type == SOF_PIN_TYPE_INPUT) {
196362306a36Sopenharmony_ci		num_pins = swidget->num_input_pins;
196462306a36Sopenharmony_ci		format_list_to_search = available_fmt->input_pin_fmts;
196562306a36Sopenharmony_ci		format_list_count = available_fmt->num_input_formats;
196662306a36Sopenharmony_ci	} else {
196762306a36Sopenharmony_ci		num_pins = swidget->num_output_pins;
196862306a36Sopenharmony_ci		pin_format_offset = swidget->num_input_pins;
196962306a36Sopenharmony_ci		format_list_to_search = available_fmt->output_pin_fmts;
197062306a36Sopenharmony_ci		format_list_count = available_fmt->num_output_formats;
197162306a36Sopenharmony_ci	}
197262306a36Sopenharmony_ci
197362306a36Sopenharmony_ci	for (i = pin_format_offset; i < num_pins + pin_format_offset; i++) {
197462306a36Sopenharmony_ci		pin_format = &base_cfg_ext->pin_formats[i];
197562306a36Sopenharmony_ci
197662306a36Sopenharmony_ci		/* Pin 0 audio formats are derived from the base config input/output format */
197762306a36Sopenharmony_ci		if (i == pin_format_offset) {
197862306a36Sopenharmony_ci			if (pin_type == SOF_PIN_TYPE_INPUT) {
197962306a36Sopenharmony_ci				pin_format->buffer_size = process->base_config.ibs;
198062306a36Sopenharmony_ci				pin_format->audio_fmt = process->base_config.audio_fmt;
198162306a36Sopenharmony_ci			} else {
198262306a36Sopenharmony_ci				pin_format->buffer_size = process->base_config.obs;
198362306a36Sopenharmony_ci				pin_format->audio_fmt = process->output_format;
198462306a36Sopenharmony_ci			}
198562306a36Sopenharmony_ci			continue;
198662306a36Sopenharmony_ci		}
198762306a36Sopenharmony_ci
198862306a36Sopenharmony_ci		/*
198962306a36Sopenharmony_ci		 * For all other pins, find the pin formats from those set in topology. If there
199062306a36Sopenharmony_ci		 * is more than one format specified for a pin, this will pick the first available
199162306a36Sopenharmony_ci		 * one.
199262306a36Sopenharmony_ci		 */
199362306a36Sopenharmony_ci		for (j = 0; j < format_list_count; j++) {
199462306a36Sopenharmony_ci			struct sof_ipc4_pin_format *pin_format_item = &format_list_to_search[j];
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci			if (pin_format_item->pin_index == i - pin_format_offset) {
199762306a36Sopenharmony_ci				*pin_format = *pin_format_item;
199862306a36Sopenharmony_ci				break;
199962306a36Sopenharmony_ci			}
200062306a36Sopenharmony_ci		}
200162306a36Sopenharmony_ci
200262306a36Sopenharmony_ci		if (j == format_list_count) {
200362306a36Sopenharmony_ci			dev_err(scomp->dev, "%s pin %d format not found for %s\n",
200462306a36Sopenharmony_ci				(pin_type == SOF_PIN_TYPE_INPUT) ? "input" : "output",
200562306a36Sopenharmony_ci				i - pin_format_offset, swidget->widget->name);
200662306a36Sopenharmony_ci			return -EINVAL;
200762306a36Sopenharmony_ci		}
200862306a36Sopenharmony_ci	}
200962306a36Sopenharmony_ci
201062306a36Sopenharmony_ci	return 0;
201162306a36Sopenharmony_ci}
201262306a36Sopenharmony_ci
201362306a36Sopenharmony_cistatic int sof_ipc4_process_add_base_cfg_extn(struct snd_sof_widget *swidget)
201462306a36Sopenharmony_ci{
201562306a36Sopenharmony_ci	int ret, i;
201662306a36Sopenharmony_ci
201762306a36Sopenharmony_ci	/* copy input and output pin formats */
201862306a36Sopenharmony_ci	for (i = 0; i <= SOF_PIN_TYPE_OUTPUT; i++) {
201962306a36Sopenharmony_ci		ret = sof_ipc4_process_set_pin_formats(swidget, i);
202062306a36Sopenharmony_ci		if (ret < 0)
202162306a36Sopenharmony_ci			return ret;
202262306a36Sopenharmony_ci	}
202362306a36Sopenharmony_ci
202462306a36Sopenharmony_ci	return 0;
202562306a36Sopenharmony_ci}
202662306a36Sopenharmony_ci
202762306a36Sopenharmony_cistatic int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget,
202862306a36Sopenharmony_ci					   struct snd_pcm_hw_params *fe_params,
202962306a36Sopenharmony_ci					   struct snd_sof_platform_stream_params *platform_params,
203062306a36Sopenharmony_ci					   struct snd_pcm_hw_params *pipeline_params, int dir)
203162306a36Sopenharmony_ci{
203262306a36Sopenharmony_ci	struct snd_soc_component *scomp = swidget->scomp;
203362306a36Sopenharmony_ci	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
203462306a36Sopenharmony_ci	struct sof_ipc4_process *process = swidget->private;
203562306a36Sopenharmony_ci	struct sof_ipc4_available_audio_format *available_fmt = &process->available_fmt;
203662306a36Sopenharmony_ci	struct sof_ipc4_audio_format *in_fmt;
203762306a36Sopenharmony_ci	u32 out_ref_rate, out_ref_channels, out_ref_valid_bits;
203862306a36Sopenharmony_ci	void *cfg = process->ipc_config_data;
203962306a36Sopenharmony_ci	int output_fmt_index;
204062306a36Sopenharmony_ci	int ret;
204162306a36Sopenharmony_ci
204262306a36Sopenharmony_ci	ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &process->base_config,
204362306a36Sopenharmony_ci					    pipeline_params, available_fmt);
204462306a36Sopenharmony_ci	if (ret < 0)
204562306a36Sopenharmony_ci		return ret;
204662306a36Sopenharmony_ci
204762306a36Sopenharmony_ci	in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt;
204862306a36Sopenharmony_ci	out_ref_rate = in_fmt->sampling_frequency;
204962306a36Sopenharmony_ci	out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);
205062306a36Sopenharmony_ci	out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);
205162306a36Sopenharmony_ci
205262306a36Sopenharmony_ci	output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, &process->base_config,
205362306a36Sopenharmony_ci							  available_fmt, out_ref_rate,
205462306a36Sopenharmony_ci							  out_ref_channels, out_ref_valid_bits);
205562306a36Sopenharmony_ci	if (output_fmt_index < 0 && available_fmt->num_output_formats) {
205662306a36Sopenharmony_ci		dev_err(sdev->dev, "Failed to initialize output format for %s",
205762306a36Sopenharmony_ci			swidget->widget->name);
205862306a36Sopenharmony_ci		return output_fmt_index;
205962306a36Sopenharmony_ci	}
206062306a36Sopenharmony_ci
206162306a36Sopenharmony_ci	/* copy Pin 0 output format */
206262306a36Sopenharmony_ci	if (available_fmt->num_output_formats &&
206362306a36Sopenharmony_ci	    output_fmt_index < available_fmt->num_output_formats &&
206462306a36Sopenharmony_ci	    !available_fmt->output_pin_fmts[output_fmt_index].pin_index) {
206562306a36Sopenharmony_ci		memcpy(&process->output_format,
206662306a36Sopenharmony_ci		       &available_fmt->output_pin_fmts[output_fmt_index].audio_fmt,
206762306a36Sopenharmony_ci		       sizeof(struct sof_ipc4_audio_format));
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci		/* modify the pipeline params with the pin 0 output format */
207062306a36Sopenharmony_ci		ret = sof_ipc4_update_hw_params(sdev, pipeline_params, &process->output_format);
207162306a36Sopenharmony_ci		if (ret)
207262306a36Sopenharmony_ci			return ret;
207362306a36Sopenharmony_ci	}
207462306a36Sopenharmony_ci
207562306a36Sopenharmony_ci	/* update pipeline memory usage */
207662306a36Sopenharmony_ci	sof_ipc4_update_resource_usage(sdev, swidget, &process->base_config);
207762306a36Sopenharmony_ci
207862306a36Sopenharmony_ci	/* ipc_config_data is composed of the base_config followed by an optional extension */
207962306a36Sopenharmony_ci	memcpy(cfg, &process->base_config, sizeof(struct sof_ipc4_base_module_cfg));
208062306a36Sopenharmony_ci	cfg += sizeof(struct sof_ipc4_base_module_cfg);
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_ci	if (process->init_config == SOF_IPC4_MODULE_INIT_CONFIG_TYPE_BASE_CFG_WITH_EXT) {
208362306a36Sopenharmony_ci		struct sof_ipc4_base_module_cfg_ext *base_cfg_ext = process->base_config_ext;
208462306a36Sopenharmony_ci
208562306a36Sopenharmony_ci		ret = sof_ipc4_process_add_base_cfg_extn(swidget);
208662306a36Sopenharmony_ci		if (ret < 0)
208762306a36Sopenharmony_ci			return ret;
208862306a36Sopenharmony_ci
208962306a36Sopenharmony_ci		memcpy(cfg, base_cfg_ext, process->base_config_ext_size);
209062306a36Sopenharmony_ci	}
209162306a36Sopenharmony_ci
209262306a36Sopenharmony_ci	return 0;
209362306a36Sopenharmony_ci}
209462306a36Sopenharmony_ci
209562306a36Sopenharmony_cistatic int sof_ipc4_control_load_volume(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)
209662306a36Sopenharmony_ci{
209762306a36Sopenharmony_ci	struct sof_ipc4_control_data *control_data;
209862306a36Sopenharmony_ci	struct sof_ipc4_msg *msg;
209962306a36Sopenharmony_ci	int i;
210062306a36Sopenharmony_ci
210162306a36Sopenharmony_ci	scontrol->size = struct_size(control_data, chanv, scontrol->num_channels);
210262306a36Sopenharmony_ci
210362306a36Sopenharmony_ci	/* scontrol->ipc_control_data will be freed in sof_control_unload */
210462306a36Sopenharmony_ci	scontrol->ipc_control_data = kzalloc(scontrol->size, GFP_KERNEL);
210562306a36Sopenharmony_ci	if (!scontrol->ipc_control_data)
210662306a36Sopenharmony_ci		return -ENOMEM;
210762306a36Sopenharmony_ci
210862306a36Sopenharmony_ci	control_data = scontrol->ipc_control_data;
210962306a36Sopenharmony_ci	control_data->index = scontrol->index;
211062306a36Sopenharmony_ci
211162306a36Sopenharmony_ci	msg = &control_data->msg;
211262306a36Sopenharmony_ci	msg->primary = SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_SET);
211362306a36Sopenharmony_ci	msg->primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
211462306a36Sopenharmony_ci	msg->primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
211562306a36Sopenharmony_ci
211662306a36Sopenharmony_ci	msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_GAIN_PARAM_ID);
211762306a36Sopenharmony_ci
211862306a36Sopenharmony_ci	/* set default volume values to 0dB in control */
211962306a36Sopenharmony_ci	for (i = 0; i < scontrol->num_channels; i++) {
212062306a36Sopenharmony_ci		control_data->chanv[i].channel = i;
212162306a36Sopenharmony_ci		control_data->chanv[i].value = SOF_IPC4_VOL_ZERO_DB;
212262306a36Sopenharmony_ci	}
212362306a36Sopenharmony_ci
212462306a36Sopenharmony_ci	return 0;
212562306a36Sopenharmony_ci}
212662306a36Sopenharmony_ci
212762306a36Sopenharmony_cistatic int sof_ipc4_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)
212862306a36Sopenharmony_ci{
212962306a36Sopenharmony_ci	struct sof_ipc4_control_data *control_data;
213062306a36Sopenharmony_ci	struct sof_ipc4_msg *msg;
213162306a36Sopenharmony_ci	int ret;
213262306a36Sopenharmony_ci
213362306a36Sopenharmony_ci	if (scontrol->max_size < (sizeof(*control_data) + sizeof(struct sof_abi_hdr))) {
213462306a36Sopenharmony_ci		dev_err(sdev->dev, "insufficient size for a bytes control %s: %zu.\n",
213562306a36Sopenharmony_ci			scontrol->name, scontrol->max_size);
213662306a36Sopenharmony_ci		return -EINVAL;
213762306a36Sopenharmony_ci	}
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ci	if (scontrol->priv_size > scontrol->max_size - sizeof(*control_data)) {
214062306a36Sopenharmony_ci		dev_err(sdev->dev, "scontrol %s bytes data size %zu exceeds max %zu.\n",
214162306a36Sopenharmony_ci			scontrol->name, scontrol->priv_size,
214262306a36Sopenharmony_ci			scontrol->max_size - sizeof(*control_data));
214362306a36Sopenharmony_ci		return -EINVAL;
214462306a36Sopenharmony_ci	}
214562306a36Sopenharmony_ci
214662306a36Sopenharmony_ci	scontrol->size = sizeof(struct sof_ipc4_control_data) + scontrol->priv_size;
214762306a36Sopenharmony_ci
214862306a36Sopenharmony_ci	scontrol->ipc_control_data = kzalloc(scontrol->max_size, GFP_KERNEL);
214962306a36Sopenharmony_ci	if (!scontrol->ipc_control_data)
215062306a36Sopenharmony_ci		return -ENOMEM;
215162306a36Sopenharmony_ci
215262306a36Sopenharmony_ci	control_data = scontrol->ipc_control_data;
215362306a36Sopenharmony_ci	control_data->index = scontrol->index;
215462306a36Sopenharmony_ci	if (scontrol->priv_size > 0) {
215562306a36Sopenharmony_ci		memcpy(control_data->data, scontrol->priv, scontrol->priv_size);
215662306a36Sopenharmony_ci		kfree(scontrol->priv);
215762306a36Sopenharmony_ci		scontrol->priv = NULL;
215862306a36Sopenharmony_ci
215962306a36Sopenharmony_ci		if (control_data->data->magic != SOF_IPC4_ABI_MAGIC) {
216062306a36Sopenharmony_ci			dev_err(sdev->dev, "Wrong ABI magic (%#x) for control: %s\n",
216162306a36Sopenharmony_ci				control_data->data->magic, scontrol->name);
216262306a36Sopenharmony_ci			ret = -EINVAL;
216362306a36Sopenharmony_ci			goto err;
216462306a36Sopenharmony_ci		}
216562306a36Sopenharmony_ci
216662306a36Sopenharmony_ci		/* TODO: check the ABI version */
216762306a36Sopenharmony_ci
216862306a36Sopenharmony_ci		if (control_data->data->size + sizeof(struct sof_abi_hdr) !=
216962306a36Sopenharmony_ci		    scontrol->priv_size) {
217062306a36Sopenharmony_ci			dev_err(sdev->dev, "Control %s conflict in bytes %zu vs. priv size %zu.\n",
217162306a36Sopenharmony_ci				scontrol->name,
217262306a36Sopenharmony_ci				control_data->data->size + sizeof(struct sof_abi_hdr),
217362306a36Sopenharmony_ci				scontrol->priv_size);
217462306a36Sopenharmony_ci			ret = -EINVAL;
217562306a36Sopenharmony_ci			goto err;
217662306a36Sopenharmony_ci		}
217762306a36Sopenharmony_ci	}
217862306a36Sopenharmony_ci
217962306a36Sopenharmony_ci	msg = &control_data->msg;
218062306a36Sopenharmony_ci	msg->primary = SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_SET);
218162306a36Sopenharmony_ci	msg->primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
218262306a36Sopenharmony_ci	msg->primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
218362306a36Sopenharmony_ci
218462306a36Sopenharmony_ci	return 0;
218562306a36Sopenharmony_ci
218662306a36Sopenharmony_cierr:
218762306a36Sopenharmony_ci	kfree(scontrol->ipc_control_data);
218862306a36Sopenharmony_ci	scontrol->ipc_control_data = NULL;
218962306a36Sopenharmony_ci	return ret;
219062306a36Sopenharmony_ci}
219162306a36Sopenharmony_ci
219262306a36Sopenharmony_cistatic int sof_ipc4_control_setup(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)
219362306a36Sopenharmony_ci{
219462306a36Sopenharmony_ci	switch (scontrol->info_type) {
219562306a36Sopenharmony_ci	case SND_SOC_TPLG_CTL_VOLSW:
219662306a36Sopenharmony_ci	case SND_SOC_TPLG_CTL_VOLSW_SX:
219762306a36Sopenharmony_ci	case SND_SOC_TPLG_CTL_VOLSW_XR_SX:
219862306a36Sopenharmony_ci		return sof_ipc4_control_load_volume(sdev, scontrol);
219962306a36Sopenharmony_ci	case SND_SOC_TPLG_CTL_BYTES:
220062306a36Sopenharmony_ci		return sof_ipc4_control_load_bytes(sdev, scontrol);
220162306a36Sopenharmony_ci	default:
220262306a36Sopenharmony_ci		break;
220362306a36Sopenharmony_ci	}
220462306a36Sopenharmony_ci
220562306a36Sopenharmony_ci	return 0;
220662306a36Sopenharmony_ci}
220762306a36Sopenharmony_ci
220862306a36Sopenharmony_cistatic int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
220962306a36Sopenharmony_ci{
221062306a36Sopenharmony_ci	struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
221162306a36Sopenharmony_ci	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
221262306a36Sopenharmony_ci	struct sof_ipc4_pipeline *pipeline;
221362306a36Sopenharmony_ci	struct sof_ipc4_msg *msg;
221462306a36Sopenharmony_ci	void *ipc_data = NULL;
221562306a36Sopenharmony_ci	u32 ipc_size = 0;
221662306a36Sopenharmony_ci	int ret;
221762306a36Sopenharmony_ci
221862306a36Sopenharmony_ci	switch (swidget->id) {
221962306a36Sopenharmony_ci	case snd_soc_dapm_scheduler:
222062306a36Sopenharmony_ci		pipeline = swidget->private;
222162306a36Sopenharmony_ci
222262306a36Sopenharmony_ci		if (pipeline->use_chain_dma) {
222362306a36Sopenharmony_ci			dev_warn(sdev->dev, "use_chain_dma set for scheduler %s",
222462306a36Sopenharmony_ci				 swidget->widget->name);
222562306a36Sopenharmony_ci			return 0;
222662306a36Sopenharmony_ci		}
222762306a36Sopenharmony_ci
222862306a36Sopenharmony_ci		dev_dbg(sdev->dev, "pipeline: %d memory pages: %d\n", swidget->pipeline_id,
222962306a36Sopenharmony_ci			pipeline->mem_usage);
223062306a36Sopenharmony_ci
223162306a36Sopenharmony_ci		msg = &pipeline->msg;
223262306a36Sopenharmony_ci		msg->primary |= pipeline->mem_usage;
223362306a36Sopenharmony_ci
223462306a36Sopenharmony_ci		swidget->instance_id = ida_alloc_max(&pipeline_ida, ipc4_data->max_num_pipelines,
223562306a36Sopenharmony_ci						     GFP_KERNEL);
223662306a36Sopenharmony_ci		if (swidget->instance_id < 0) {
223762306a36Sopenharmony_ci			dev_err(sdev->dev, "failed to assign pipeline id for %s: %d\n",
223862306a36Sopenharmony_ci				swidget->widget->name, swidget->instance_id);
223962306a36Sopenharmony_ci			return swidget->instance_id;
224062306a36Sopenharmony_ci		}
224162306a36Sopenharmony_ci		msg->primary &= ~SOF_IPC4_GLB_PIPE_INSTANCE_MASK;
224262306a36Sopenharmony_ci		msg->primary |= SOF_IPC4_GLB_PIPE_INSTANCE_ID(swidget->instance_id);
224362306a36Sopenharmony_ci		break;
224462306a36Sopenharmony_ci	case snd_soc_dapm_aif_in:
224562306a36Sopenharmony_ci	case snd_soc_dapm_aif_out:
224662306a36Sopenharmony_ci	case snd_soc_dapm_buffer:
224762306a36Sopenharmony_ci	{
224862306a36Sopenharmony_ci		struct sof_ipc4_copier *ipc4_copier = swidget->private;
224962306a36Sopenharmony_ci
225062306a36Sopenharmony_ci		pipeline = pipe_widget->private;
225162306a36Sopenharmony_ci		if (pipeline->use_chain_dma)
225262306a36Sopenharmony_ci			return 0;
225362306a36Sopenharmony_ci
225462306a36Sopenharmony_ci		ipc_size = ipc4_copier->ipc_config_size;
225562306a36Sopenharmony_ci		ipc_data = ipc4_copier->ipc_config_data;
225662306a36Sopenharmony_ci
225762306a36Sopenharmony_ci		msg = &ipc4_copier->msg;
225862306a36Sopenharmony_ci		break;
225962306a36Sopenharmony_ci	}
226062306a36Sopenharmony_ci	case snd_soc_dapm_dai_in:
226162306a36Sopenharmony_ci	case snd_soc_dapm_dai_out:
226262306a36Sopenharmony_ci	{
226362306a36Sopenharmony_ci		struct snd_sof_dai *dai = swidget->private;
226462306a36Sopenharmony_ci		struct sof_ipc4_copier *ipc4_copier = dai->private;
226562306a36Sopenharmony_ci
226662306a36Sopenharmony_ci		pipeline = pipe_widget->private;
226762306a36Sopenharmony_ci		if (pipeline->use_chain_dma)
226862306a36Sopenharmony_ci			return 0;
226962306a36Sopenharmony_ci
227062306a36Sopenharmony_ci		ipc_size = ipc4_copier->ipc_config_size;
227162306a36Sopenharmony_ci		ipc_data = ipc4_copier->ipc_config_data;
227262306a36Sopenharmony_ci
227362306a36Sopenharmony_ci		msg = &ipc4_copier->msg;
227462306a36Sopenharmony_ci		break;
227562306a36Sopenharmony_ci	}
227662306a36Sopenharmony_ci	case snd_soc_dapm_pga:
227762306a36Sopenharmony_ci	{
227862306a36Sopenharmony_ci		struct sof_ipc4_gain *gain = swidget->private;
227962306a36Sopenharmony_ci
228062306a36Sopenharmony_ci		ipc_size = sizeof(gain->data);
228162306a36Sopenharmony_ci		ipc_data = &gain->data;
228262306a36Sopenharmony_ci
228362306a36Sopenharmony_ci		msg = &gain->msg;
228462306a36Sopenharmony_ci		break;
228562306a36Sopenharmony_ci	}
228662306a36Sopenharmony_ci	case snd_soc_dapm_mixer:
228762306a36Sopenharmony_ci	{
228862306a36Sopenharmony_ci		struct sof_ipc4_mixer *mixer = swidget->private;
228962306a36Sopenharmony_ci
229062306a36Sopenharmony_ci		ipc_size = sizeof(mixer->base_config);
229162306a36Sopenharmony_ci		ipc_data = &mixer->base_config;
229262306a36Sopenharmony_ci
229362306a36Sopenharmony_ci		msg = &mixer->msg;
229462306a36Sopenharmony_ci		break;
229562306a36Sopenharmony_ci	}
229662306a36Sopenharmony_ci	case snd_soc_dapm_src:
229762306a36Sopenharmony_ci	{
229862306a36Sopenharmony_ci		struct sof_ipc4_src *src = swidget->private;
229962306a36Sopenharmony_ci
230062306a36Sopenharmony_ci		ipc_size = sizeof(src->data);
230162306a36Sopenharmony_ci		ipc_data = &src->data;
230262306a36Sopenharmony_ci
230362306a36Sopenharmony_ci		msg = &src->msg;
230462306a36Sopenharmony_ci		break;
230562306a36Sopenharmony_ci	}
230662306a36Sopenharmony_ci	case snd_soc_dapm_effect:
230762306a36Sopenharmony_ci	{
230862306a36Sopenharmony_ci		struct sof_ipc4_process *process = swidget->private;
230962306a36Sopenharmony_ci
231062306a36Sopenharmony_ci		if (!process->ipc_config_size) {
231162306a36Sopenharmony_ci			dev_err(sdev->dev, "module %s has no config data!\n",
231262306a36Sopenharmony_ci				swidget->widget->name);
231362306a36Sopenharmony_ci			return -EINVAL;
231462306a36Sopenharmony_ci		}
231562306a36Sopenharmony_ci
231662306a36Sopenharmony_ci		ipc_size = process->ipc_config_size;
231762306a36Sopenharmony_ci		ipc_data = process->ipc_config_data;
231862306a36Sopenharmony_ci
231962306a36Sopenharmony_ci		msg = &process->msg;
232062306a36Sopenharmony_ci		break;
232162306a36Sopenharmony_ci	}
232262306a36Sopenharmony_ci	default:
232362306a36Sopenharmony_ci		dev_err(sdev->dev, "widget type %d not supported", swidget->id);
232462306a36Sopenharmony_ci		return -EINVAL;
232562306a36Sopenharmony_ci	}
232662306a36Sopenharmony_ci
232762306a36Sopenharmony_ci	if (swidget->id != snd_soc_dapm_scheduler) {
232862306a36Sopenharmony_ci		ret = sof_ipc4_widget_assign_instance_id(sdev, swidget);
232962306a36Sopenharmony_ci		if (ret < 0) {
233062306a36Sopenharmony_ci			dev_err(sdev->dev, "failed to assign instance id for %s\n",
233162306a36Sopenharmony_ci				swidget->widget->name);
233262306a36Sopenharmony_ci			return ret;
233362306a36Sopenharmony_ci		}
233462306a36Sopenharmony_ci
233562306a36Sopenharmony_ci		msg->primary &= ~SOF_IPC4_MOD_INSTANCE_MASK;
233662306a36Sopenharmony_ci		msg->primary |= SOF_IPC4_MOD_INSTANCE(swidget->instance_id);
233762306a36Sopenharmony_ci
233862306a36Sopenharmony_ci		msg->extension &= ~SOF_IPC4_MOD_EXT_PARAM_SIZE_MASK;
233962306a36Sopenharmony_ci		msg->extension |= ipc_size >> 2;
234062306a36Sopenharmony_ci
234162306a36Sopenharmony_ci		msg->extension &= ~SOF_IPC4_MOD_EXT_PPL_ID_MASK;
234262306a36Sopenharmony_ci		msg->extension |= SOF_IPC4_MOD_EXT_PPL_ID(pipe_widget->instance_id);
234362306a36Sopenharmony_ci	}
234462306a36Sopenharmony_ci	dev_dbg(sdev->dev, "Create widget %s instance %d - pipe %d - core %d\n",
234562306a36Sopenharmony_ci		swidget->widget->name, swidget->instance_id, swidget->pipeline_id, swidget->core);
234662306a36Sopenharmony_ci
234762306a36Sopenharmony_ci	msg->data_size = ipc_size;
234862306a36Sopenharmony_ci	msg->data_ptr = ipc_data;
234962306a36Sopenharmony_ci
235062306a36Sopenharmony_ci	ret = sof_ipc_tx_message_no_reply(sdev->ipc, msg, ipc_size);
235162306a36Sopenharmony_ci	if (ret < 0) {
235262306a36Sopenharmony_ci		dev_err(sdev->dev, "failed to create module %s\n", swidget->widget->name);
235362306a36Sopenharmony_ci
235462306a36Sopenharmony_ci		if (swidget->id != snd_soc_dapm_scheduler) {
235562306a36Sopenharmony_ci			struct sof_ipc4_fw_module *fw_module = swidget->module_info;
235662306a36Sopenharmony_ci
235762306a36Sopenharmony_ci			ida_free(&fw_module->m_ida, swidget->instance_id);
235862306a36Sopenharmony_ci		} else {
235962306a36Sopenharmony_ci			ida_free(&pipeline_ida, swidget->instance_id);
236062306a36Sopenharmony_ci		}
236162306a36Sopenharmony_ci	}
236262306a36Sopenharmony_ci
236362306a36Sopenharmony_ci	return ret;
236462306a36Sopenharmony_ci}
236562306a36Sopenharmony_ci
236662306a36Sopenharmony_cistatic int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
236762306a36Sopenharmony_ci{
236862306a36Sopenharmony_ci	struct sof_ipc4_fw_module *fw_module = swidget->module_info;
236962306a36Sopenharmony_ci	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
237062306a36Sopenharmony_ci	int ret = 0;
237162306a36Sopenharmony_ci
237262306a36Sopenharmony_ci	mutex_lock(&ipc4_data->pipeline_state_mutex);
237362306a36Sopenharmony_ci
237462306a36Sopenharmony_ci	/* freeing a pipeline frees all the widgets associated with it */
237562306a36Sopenharmony_ci	if (swidget->id == snd_soc_dapm_scheduler) {
237662306a36Sopenharmony_ci		struct sof_ipc4_pipeline *pipeline = swidget->private;
237762306a36Sopenharmony_ci		struct sof_ipc4_msg msg = {{ 0 }};
237862306a36Sopenharmony_ci		u32 header;
237962306a36Sopenharmony_ci
238062306a36Sopenharmony_ci		if (pipeline->use_chain_dma) {
238162306a36Sopenharmony_ci			dev_warn(sdev->dev, "use_chain_dma set for scheduler %s",
238262306a36Sopenharmony_ci				 swidget->widget->name);
238362306a36Sopenharmony_ci			mutex_unlock(&ipc4_data->pipeline_state_mutex);
238462306a36Sopenharmony_ci			return 0;
238562306a36Sopenharmony_ci		}
238662306a36Sopenharmony_ci
238762306a36Sopenharmony_ci		header = SOF_IPC4_GLB_PIPE_INSTANCE_ID(swidget->instance_id);
238862306a36Sopenharmony_ci		header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_DELETE_PIPELINE);
238962306a36Sopenharmony_ci		header |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
239062306a36Sopenharmony_ci		header |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG);
239162306a36Sopenharmony_ci
239262306a36Sopenharmony_ci		msg.primary = header;
239362306a36Sopenharmony_ci
239462306a36Sopenharmony_ci		ret = sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0);
239562306a36Sopenharmony_ci		if (ret < 0)
239662306a36Sopenharmony_ci			dev_err(sdev->dev, "failed to free pipeline widget %s\n",
239762306a36Sopenharmony_ci				swidget->widget->name);
239862306a36Sopenharmony_ci
239962306a36Sopenharmony_ci		pipeline->mem_usage = 0;
240062306a36Sopenharmony_ci		pipeline->state = SOF_IPC4_PIPE_UNINITIALIZED;
240162306a36Sopenharmony_ci		ida_free(&pipeline_ida, swidget->instance_id);
240262306a36Sopenharmony_ci		swidget->instance_id = -EINVAL;
240362306a36Sopenharmony_ci	} else {
240462306a36Sopenharmony_ci		struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
240562306a36Sopenharmony_ci		struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
240662306a36Sopenharmony_ci
240762306a36Sopenharmony_ci		if (!pipeline->use_chain_dma)
240862306a36Sopenharmony_ci			ida_free(&fw_module->m_ida, swidget->instance_id);
240962306a36Sopenharmony_ci	}
241062306a36Sopenharmony_ci
241162306a36Sopenharmony_ci	mutex_unlock(&ipc4_data->pipeline_state_mutex);
241262306a36Sopenharmony_ci
241362306a36Sopenharmony_ci	return ret;
241462306a36Sopenharmony_ci}
241562306a36Sopenharmony_ci
241662306a36Sopenharmony_cistatic int sof_ipc4_get_queue_id(struct snd_sof_widget *src_widget,
241762306a36Sopenharmony_ci				 struct snd_sof_widget *sink_widget, bool pin_type)
241862306a36Sopenharmony_ci{
241962306a36Sopenharmony_ci	struct snd_sof_widget *current_swidget;
242062306a36Sopenharmony_ci	struct snd_soc_component *scomp;
242162306a36Sopenharmony_ci	struct ida *queue_ida;
242262306a36Sopenharmony_ci	const char *buddy_name;
242362306a36Sopenharmony_ci	char **pin_binding;
242462306a36Sopenharmony_ci	u32 num_pins;
242562306a36Sopenharmony_ci	int i;
242662306a36Sopenharmony_ci
242762306a36Sopenharmony_ci	if (pin_type == SOF_PIN_TYPE_OUTPUT) {
242862306a36Sopenharmony_ci		current_swidget = src_widget;
242962306a36Sopenharmony_ci		pin_binding = src_widget->output_pin_binding;
243062306a36Sopenharmony_ci		queue_ida = &src_widget->output_queue_ida;
243162306a36Sopenharmony_ci		num_pins = src_widget->num_output_pins;
243262306a36Sopenharmony_ci		buddy_name = sink_widget->widget->name;
243362306a36Sopenharmony_ci	} else {
243462306a36Sopenharmony_ci		current_swidget = sink_widget;
243562306a36Sopenharmony_ci		pin_binding = sink_widget->input_pin_binding;
243662306a36Sopenharmony_ci		queue_ida = &sink_widget->input_queue_ida;
243762306a36Sopenharmony_ci		num_pins = sink_widget->num_input_pins;
243862306a36Sopenharmony_ci		buddy_name = src_widget->widget->name;
243962306a36Sopenharmony_ci	}
244062306a36Sopenharmony_ci
244162306a36Sopenharmony_ci	scomp = current_swidget->scomp;
244262306a36Sopenharmony_ci
244362306a36Sopenharmony_ci	if (num_pins < 1) {
244462306a36Sopenharmony_ci		dev_err(scomp->dev, "invalid %s num_pins: %d for queue allocation for %s\n",
244562306a36Sopenharmony_ci			(pin_type == SOF_PIN_TYPE_OUTPUT ? "output" : "input"),
244662306a36Sopenharmony_ci			num_pins, current_swidget->widget->name);
244762306a36Sopenharmony_ci		return -EINVAL;
244862306a36Sopenharmony_ci	}
244962306a36Sopenharmony_ci
245062306a36Sopenharmony_ci	/* If there is only one input/output pin, queue id must be 0 */
245162306a36Sopenharmony_ci	if (num_pins == 1)
245262306a36Sopenharmony_ci		return 0;
245362306a36Sopenharmony_ci
245462306a36Sopenharmony_ci	/* Allocate queue ID from pin binding array if it is defined in topology. */
245562306a36Sopenharmony_ci	if (pin_binding) {
245662306a36Sopenharmony_ci		for (i = 0; i < num_pins; i++) {
245762306a36Sopenharmony_ci			if (!strcmp(pin_binding[i], buddy_name))
245862306a36Sopenharmony_ci				return i;
245962306a36Sopenharmony_ci		}
246062306a36Sopenharmony_ci		/*
246162306a36Sopenharmony_ci		 * Fail if no queue ID found from pin binding array, so that we don't
246262306a36Sopenharmony_ci		 * mixed use pin binding array and ida for queue ID allocation.
246362306a36Sopenharmony_ci		 */
246462306a36Sopenharmony_ci		dev_err(scomp->dev, "no %s queue id found from pin binding array for %s\n",
246562306a36Sopenharmony_ci			(pin_type == SOF_PIN_TYPE_OUTPUT ? "output" : "input"),
246662306a36Sopenharmony_ci			current_swidget->widget->name);
246762306a36Sopenharmony_ci		return -EINVAL;
246862306a36Sopenharmony_ci	}
246962306a36Sopenharmony_ci
247062306a36Sopenharmony_ci	/* If no pin binding array specified in topology, use ida to allocate one */
247162306a36Sopenharmony_ci	return ida_alloc_max(queue_ida, num_pins, GFP_KERNEL);
247262306a36Sopenharmony_ci}
247362306a36Sopenharmony_ci
247462306a36Sopenharmony_cistatic void sof_ipc4_put_queue_id(struct snd_sof_widget *swidget, int queue_id,
247562306a36Sopenharmony_ci				  bool pin_type)
247662306a36Sopenharmony_ci{
247762306a36Sopenharmony_ci	struct ida *queue_ida;
247862306a36Sopenharmony_ci	char **pin_binding;
247962306a36Sopenharmony_ci	int num_pins;
248062306a36Sopenharmony_ci
248162306a36Sopenharmony_ci	if (pin_type == SOF_PIN_TYPE_OUTPUT) {
248262306a36Sopenharmony_ci		pin_binding = swidget->output_pin_binding;
248362306a36Sopenharmony_ci		queue_ida = &swidget->output_queue_ida;
248462306a36Sopenharmony_ci		num_pins = swidget->num_output_pins;
248562306a36Sopenharmony_ci	} else {
248662306a36Sopenharmony_ci		pin_binding = swidget->input_pin_binding;
248762306a36Sopenharmony_ci		queue_ida = &swidget->input_queue_ida;
248862306a36Sopenharmony_ci		num_pins = swidget->num_input_pins;
248962306a36Sopenharmony_ci	}
249062306a36Sopenharmony_ci
249162306a36Sopenharmony_ci	/* Nothing to free if queue ID is not allocated with ida. */
249262306a36Sopenharmony_ci	if (num_pins == 1 || pin_binding)
249362306a36Sopenharmony_ci		return;
249462306a36Sopenharmony_ci
249562306a36Sopenharmony_ci	ida_free(queue_ida, queue_id);
249662306a36Sopenharmony_ci}
249762306a36Sopenharmony_ci
249862306a36Sopenharmony_cistatic int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev,
249962306a36Sopenharmony_ci					   struct snd_sof_widget *src_widget,
250062306a36Sopenharmony_ci					   struct snd_sof_widget *sink_widget,
250162306a36Sopenharmony_ci					   int sink_id)
250262306a36Sopenharmony_ci{
250362306a36Sopenharmony_ci	struct sof_ipc4_copier_config_set_sink_format format;
250462306a36Sopenharmony_ci	const struct sof_ipc_ops *iops = sdev->ipc->ops;
250562306a36Sopenharmony_ci	struct sof_ipc4_base_module_cfg *src_config;
250662306a36Sopenharmony_ci	const struct sof_ipc4_audio_format *pin_fmt;
250762306a36Sopenharmony_ci	struct sof_ipc4_fw_module *fw_module;
250862306a36Sopenharmony_ci	struct sof_ipc4_msg msg = {{ 0 }};
250962306a36Sopenharmony_ci
251062306a36Sopenharmony_ci	dev_dbg(sdev->dev, "%s set copier sink %d format\n",
251162306a36Sopenharmony_ci		src_widget->widget->name, sink_id);
251262306a36Sopenharmony_ci
251362306a36Sopenharmony_ci	if (WIDGET_IS_DAI(src_widget->id)) {
251462306a36Sopenharmony_ci		struct snd_sof_dai *dai = src_widget->private;
251562306a36Sopenharmony_ci
251662306a36Sopenharmony_ci		src_config = dai->private;
251762306a36Sopenharmony_ci	} else {
251862306a36Sopenharmony_ci		src_config = src_widget->private;
251962306a36Sopenharmony_ci	}
252062306a36Sopenharmony_ci
252162306a36Sopenharmony_ci	fw_module = src_widget->module_info;
252262306a36Sopenharmony_ci
252362306a36Sopenharmony_ci	format.sink_id = sink_id;
252462306a36Sopenharmony_ci	memcpy(&format.source_fmt, &src_config->audio_fmt, sizeof(format.source_fmt));
252562306a36Sopenharmony_ci
252662306a36Sopenharmony_ci	pin_fmt = sof_ipc4_get_input_pin_audio_fmt(sink_widget, sink_id);
252762306a36Sopenharmony_ci	if (!pin_fmt) {
252862306a36Sopenharmony_ci		dev_err(sdev->dev, "Unable to get pin %d format for %s",
252962306a36Sopenharmony_ci			sink_id, sink_widget->widget->name);
253062306a36Sopenharmony_ci		return -EINVAL;
253162306a36Sopenharmony_ci	}
253262306a36Sopenharmony_ci
253362306a36Sopenharmony_ci	memcpy(&format.sink_fmt, pin_fmt, sizeof(format.sink_fmt));
253462306a36Sopenharmony_ci
253562306a36Sopenharmony_ci	msg.data_size = sizeof(format);
253662306a36Sopenharmony_ci	msg.data_ptr = &format;
253762306a36Sopenharmony_ci
253862306a36Sopenharmony_ci	msg.primary = fw_module->man4_module_entry.id;
253962306a36Sopenharmony_ci	msg.primary |= SOF_IPC4_MOD_INSTANCE(src_widget->instance_id);
254062306a36Sopenharmony_ci	msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
254162306a36Sopenharmony_ci	msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
254262306a36Sopenharmony_ci
254362306a36Sopenharmony_ci	msg.extension =
254462306a36Sopenharmony_ci		SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_COPIER_MODULE_CFG_PARAM_SET_SINK_FORMAT);
254562306a36Sopenharmony_ci
254662306a36Sopenharmony_ci	return iops->set_get_data(sdev, &msg, msg.data_size, true);
254762306a36Sopenharmony_ci}
254862306a36Sopenharmony_ci
254962306a36Sopenharmony_cistatic int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *sroute)
255062306a36Sopenharmony_ci{
255162306a36Sopenharmony_ci	struct snd_sof_widget *src_widget = sroute->src_widget;
255262306a36Sopenharmony_ci	struct snd_sof_widget *sink_widget = sroute->sink_widget;
255362306a36Sopenharmony_ci	struct snd_sof_widget *src_pipe_widget = src_widget->spipe->pipe_widget;
255462306a36Sopenharmony_ci	struct snd_sof_widget *sink_pipe_widget = sink_widget->spipe->pipe_widget;
255562306a36Sopenharmony_ci	struct sof_ipc4_fw_module *src_fw_module = src_widget->module_info;
255662306a36Sopenharmony_ci	struct sof_ipc4_fw_module *sink_fw_module = sink_widget->module_info;
255762306a36Sopenharmony_ci	struct sof_ipc4_pipeline *src_pipeline = src_pipe_widget->private;
255862306a36Sopenharmony_ci	struct sof_ipc4_pipeline *sink_pipeline = sink_pipe_widget->private;
255962306a36Sopenharmony_ci	struct sof_ipc4_msg msg = {{ 0 }};
256062306a36Sopenharmony_ci	u32 header, extension;
256162306a36Sopenharmony_ci	int ret;
256262306a36Sopenharmony_ci
256362306a36Sopenharmony_ci	/* no route set up if chain DMA is used */
256462306a36Sopenharmony_ci	if (src_pipeline->use_chain_dma || sink_pipeline->use_chain_dma) {
256562306a36Sopenharmony_ci		if (!src_pipeline->use_chain_dma || !sink_pipeline->use_chain_dma) {
256662306a36Sopenharmony_ci			dev_err(sdev->dev,
256762306a36Sopenharmony_ci				"use_chain_dma must be set for both src %s and sink %s pipelines\n",
256862306a36Sopenharmony_ci				src_widget->widget->name, sink_widget->widget->name);
256962306a36Sopenharmony_ci			return -EINVAL;
257062306a36Sopenharmony_ci		}
257162306a36Sopenharmony_ci		return 0;
257262306a36Sopenharmony_ci	}
257362306a36Sopenharmony_ci
257462306a36Sopenharmony_ci	if (!src_fw_module || !sink_fw_module) {
257562306a36Sopenharmony_ci		dev_err(sdev->dev,
257662306a36Sopenharmony_ci			"cannot bind %s -> %s, no firmware module for: %s%s\n",
257762306a36Sopenharmony_ci			src_widget->widget->name, sink_widget->widget->name,
257862306a36Sopenharmony_ci			src_fw_module ? "" : " source",
257962306a36Sopenharmony_ci			sink_fw_module ? "" : " sink");
258062306a36Sopenharmony_ci
258162306a36Sopenharmony_ci		return -ENODEV;
258262306a36Sopenharmony_ci	}
258362306a36Sopenharmony_ci
258462306a36Sopenharmony_ci	sroute->src_queue_id = sof_ipc4_get_queue_id(src_widget, sink_widget,
258562306a36Sopenharmony_ci						     SOF_PIN_TYPE_OUTPUT);
258662306a36Sopenharmony_ci	if (sroute->src_queue_id < 0) {
258762306a36Sopenharmony_ci		dev_err(sdev->dev, "failed to get queue ID for source widget: %s\n",
258862306a36Sopenharmony_ci			src_widget->widget->name);
258962306a36Sopenharmony_ci		return sroute->src_queue_id;
259062306a36Sopenharmony_ci	}
259162306a36Sopenharmony_ci
259262306a36Sopenharmony_ci	sroute->dst_queue_id = sof_ipc4_get_queue_id(src_widget, sink_widget,
259362306a36Sopenharmony_ci						     SOF_PIN_TYPE_INPUT);
259462306a36Sopenharmony_ci	if (sroute->dst_queue_id < 0) {
259562306a36Sopenharmony_ci		dev_err(sdev->dev, "failed to get queue ID for sink widget: %s\n",
259662306a36Sopenharmony_ci			sink_widget->widget->name);
259762306a36Sopenharmony_ci		sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id,
259862306a36Sopenharmony_ci				      SOF_PIN_TYPE_OUTPUT);
259962306a36Sopenharmony_ci		return sroute->dst_queue_id;
260062306a36Sopenharmony_ci	}
260162306a36Sopenharmony_ci
260262306a36Sopenharmony_ci	/* Pin 0 format is already set during copier module init */
260362306a36Sopenharmony_ci	if (sroute->src_queue_id > 0 && WIDGET_IS_COPIER(src_widget->id)) {
260462306a36Sopenharmony_ci		ret = sof_ipc4_set_copier_sink_format(sdev, src_widget, sink_widget,
260562306a36Sopenharmony_ci						      sroute->src_queue_id);
260662306a36Sopenharmony_ci		if (ret < 0) {
260762306a36Sopenharmony_ci			dev_err(sdev->dev, "failed to set sink format for %s source queue ID %d\n",
260862306a36Sopenharmony_ci				src_widget->widget->name, sroute->src_queue_id);
260962306a36Sopenharmony_ci			goto out;
261062306a36Sopenharmony_ci		}
261162306a36Sopenharmony_ci	}
261262306a36Sopenharmony_ci
261362306a36Sopenharmony_ci	dev_dbg(sdev->dev, "bind %s:%d -> %s:%d\n",
261462306a36Sopenharmony_ci		src_widget->widget->name, sroute->src_queue_id,
261562306a36Sopenharmony_ci		sink_widget->widget->name, sroute->dst_queue_id);
261662306a36Sopenharmony_ci
261762306a36Sopenharmony_ci	header = src_fw_module->man4_module_entry.id;
261862306a36Sopenharmony_ci	header |= SOF_IPC4_MOD_INSTANCE(src_widget->instance_id);
261962306a36Sopenharmony_ci	header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_BIND);
262062306a36Sopenharmony_ci	header |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
262162306a36Sopenharmony_ci	header |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
262262306a36Sopenharmony_ci
262362306a36Sopenharmony_ci	extension = sink_fw_module->man4_module_entry.id;
262462306a36Sopenharmony_ci	extension |= SOF_IPC4_MOD_EXT_DST_MOD_INSTANCE(sink_widget->instance_id);
262562306a36Sopenharmony_ci	extension |= SOF_IPC4_MOD_EXT_DST_MOD_QUEUE_ID(sroute->dst_queue_id);
262662306a36Sopenharmony_ci	extension |= SOF_IPC4_MOD_EXT_SRC_MOD_QUEUE_ID(sroute->src_queue_id);
262762306a36Sopenharmony_ci
262862306a36Sopenharmony_ci	msg.primary = header;
262962306a36Sopenharmony_ci	msg.extension = extension;
263062306a36Sopenharmony_ci
263162306a36Sopenharmony_ci	ret = sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0);
263262306a36Sopenharmony_ci	if (ret < 0) {
263362306a36Sopenharmony_ci		dev_err(sdev->dev, "failed to bind modules %s:%d -> %s:%d\n",
263462306a36Sopenharmony_ci			src_widget->widget->name, sroute->src_queue_id,
263562306a36Sopenharmony_ci			sink_widget->widget->name, sroute->dst_queue_id);
263662306a36Sopenharmony_ci		goto out;
263762306a36Sopenharmony_ci	}
263862306a36Sopenharmony_ci
263962306a36Sopenharmony_ci	return ret;
264062306a36Sopenharmony_ci
264162306a36Sopenharmony_ciout:
264262306a36Sopenharmony_ci	sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_OUTPUT);
264362306a36Sopenharmony_ci	sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id, SOF_PIN_TYPE_INPUT);
264462306a36Sopenharmony_ci	return ret;
264562306a36Sopenharmony_ci}
264662306a36Sopenharmony_ci
264762306a36Sopenharmony_cistatic int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *sroute)
264862306a36Sopenharmony_ci{
264962306a36Sopenharmony_ci	struct snd_sof_widget *src_widget = sroute->src_widget;
265062306a36Sopenharmony_ci	struct snd_sof_widget *sink_widget = sroute->sink_widget;
265162306a36Sopenharmony_ci	struct sof_ipc4_fw_module *src_fw_module = src_widget->module_info;
265262306a36Sopenharmony_ci	struct sof_ipc4_fw_module *sink_fw_module = sink_widget->module_info;
265362306a36Sopenharmony_ci	struct sof_ipc4_msg msg = {{ 0 }};
265462306a36Sopenharmony_ci	struct snd_sof_widget *src_pipe_widget = src_widget->spipe->pipe_widget;
265562306a36Sopenharmony_ci	struct snd_sof_widget *sink_pipe_widget = sink_widget->spipe->pipe_widget;
265662306a36Sopenharmony_ci	struct sof_ipc4_pipeline *src_pipeline = src_pipe_widget->private;
265762306a36Sopenharmony_ci	struct sof_ipc4_pipeline *sink_pipeline = sink_pipe_widget->private;
265862306a36Sopenharmony_ci	u32 header, extension;
265962306a36Sopenharmony_ci	int ret = 0;
266062306a36Sopenharmony_ci
266162306a36Sopenharmony_ci	/* no route is set up if chain DMA is used */
266262306a36Sopenharmony_ci	if (src_pipeline->use_chain_dma || sink_pipeline->use_chain_dma)
266362306a36Sopenharmony_ci		return 0;
266462306a36Sopenharmony_ci
266562306a36Sopenharmony_ci	dev_dbg(sdev->dev, "unbind modules %s:%d -> %s:%d\n",
266662306a36Sopenharmony_ci		src_widget->widget->name, sroute->src_queue_id,
266762306a36Sopenharmony_ci		sink_widget->widget->name, sroute->dst_queue_id);
266862306a36Sopenharmony_ci
266962306a36Sopenharmony_ci	/*
267062306a36Sopenharmony_ci	 * routes belonging to the same pipeline will be disconnected by the FW when the pipeline
267162306a36Sopenharmony_ci	 * is freed. So avoid sending this IPC which will be ignored by the FW anyway.
267262306a36Sopenharmony_ci	 */
267362306a36Sopenharmony_ci	if (src_widget->spipe->pipe_widget == sink_widget->spipe->pipe_widget)
267462306a36Sopenharmony_ci		goto out;
267562306a36Sopenharmony_ci
267662306a36Sopenharmony_ci	header = src_fw_module->man4_module_entry.id;
267762306a36Sopenharmony_ci	header |= SOF_IPC4_MOD_INSTANCE(src_widget->instance_id);
267862306a36Sopenharmony_ci	header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_UNBIND);
267962306a36Sopenharmony_ci	header |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
268062306a36Sopenharmony_ci	header |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
268162306a36Sopenharmony_ci
268262306a36Sopenharmony_ci	extension = sink_fw_module->man4_module_entry.id;
268362306a36Sopenharmony_ci	extension |= SOF_IPC4_MOD_EXT_DST_MOD_INSTANCE(sink_widget->instance_id);
268462306a36Sopenharmony_ci	extension |= SOF_IPC4_MOD_EXT_DST_MOD_QUEUE_ID(sroute->dst_queue_id);
268562306a36Sopenharmony_ci	extension |= SOF_IPC4_MOD_EXT_SRC_MOD_QUEUE_ID(sroute->src_queue_id);
268662306a36Sopenharmony_ci
268762306a36Sopenharmony_ci	msg.primary = header;
268862306a36Sopenharmony_ci	msg.extension = extension;
268962306a36Sopenharmony_ci
269062306a36Sopenharmony_ci	ret = sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0);
269162306a36Sopenharmony_ci	if (ret < 0)
269262306a36Sopenharmony_ci		dev_err(sdev->dev, "failed to unbind modules %s:%d -> %s:%d\n",
269362306a36Sopenharmony_ci			src_widget->widget->name, sroute->src_queue_id,
269462306a36Sopenharmony_ci			sink_widget->widget->name, sroute->dst_queue_id);
269562306a36Sopenharmony_ciout:
269662306a36Sopenharmony_ci	sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id, SOF_PIN_TYPE_INPUT);
269762306a36Sopenharmony_ci	sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_OUTPUT);
269862306a36Sopenharmony_ci
269962306a36Sopenharmony_ci	return ret;
270062306a36Sopenharmony_ci}
270162306a36Sopenharmony_ci
270262306a36Sopenharmony_cistatic int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,
270362306a36Sopenharmony_ci			       unsigned int flags, struct snd_sof_dai_config_data *data)
270462306a36Sopenharmony_ci{
270562306a36Sopenharmony_ci	struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
270662306a36Sopenharmony_ci	struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
270762306a36Sopenharmony_ci	struct snd_sof_dai *dai = swidget->private;
270862306a36Sopenharmony_ci	struct sof_ipc4_gtw_attributes *gtw_attr;
270962306a36Sopenharmony_ci	struct sof_ipc4_copier_data *copier_data;
271062306a36Sopenharmony_ci	struct sof_ipc4_copier *ipc4_copier;
271162306a36Sopenharmony_ci
271262306a36Sopenharmony_ci	if (!dai || !dai->private) {
271362306a36Sopenharmony_ci		dev_err(sdev->dev, "Invalid DAI or DAI private data for %s\n",
271462306a36Sopenharmony_ci			swidget->widget->name);
271562306a36Sopenharmony_ci		return -EINVAL;
271662306a36Sopenharmony_ci	}
271762306a36Sopenharmony_ci
271862306a36Sopenharmony_ci	ipc4_copier = (struct sof_ipc4_copier *)dai->private;
271962306a36Sopenharmony_ci	copier_data = &ipc4_copier->data;
272062306a36Sopenharmony_ci
272162306a36Sopenharmony_ci	if (!data)
272262306a36Sopenharmony_ci		return 0;
272362306a36Sopenharmony_ci
272462306a36Sopenharmony_ci	switch (ipc4_copier->dai_type) {
272562306a36Sopenharmony_ci	case SOF_DAI_INTEL_HDA:
272662306a36Sopenharmony_ci		if (pipeline->use_chain_dma) {
272762306a36Sopenharmony_ci			pipeline->msg.primary &= ~SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK;
272862306a36Sopenharmony_ci			pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(data->dai_data);
272962306a36Sopenharmony_ci			break;
273062306a36Sopenharmony_ci		}
273162306a36Sopenharmony_ci		gtw_attr = ipc4_copier->gtw_attr;
273262306a36Sopenharmony_ci		gtw_attr->lp_buffer_alloc = pipeline->lp_mode;
273362306a36Sopenharmony_ci		fallthrough;
273462306a36Sopenharmony_ci	case SOF_DAI_INTEL_ALH:
273562306a36Sopenharmony_ci		/*
273662306a36Sopenharmony_ci		 * Do not clear the node ID when this op is invoked with
273762306a36Sopenharmony_ci		 * SOF_DAI_CONFIG_FLAGS_HW_FREE. It is needed to free the group_ida during
273862306a36Sopenharmony_ci		 * unprepare.
273962306a36Sopenharmony_ci		 */
274062306a36Sopenharmony_ci		if (flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS) {
274162306a36Sopenharmony_ci			copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
274262306a36Sopenharmony_ci			copier_data->gtw_cfg.node_id |= SOF_IPC4_NODE_INDEX(data->dai_data);
274362306a36Sopenharmony_ci		}
274462306a36Sopenharmony_ci		break;
274562306a36Sopenharmony_ci	case SOF_DAI_INTEL_DMIC:
274662306a36Sopenharmony_ci	case SOF_DAI_INTEL_SSP:
274762306a36Sopenharmony_ci		/* nothing to do for SSP/DMIC */
274862306a36Sopenharmony_ci		break;
274962306a36Sopenharmony_ci	default:
275062306a36Sopenharmony_ci		dev_err(sdev->dev, "%s: unsupported dai type %d\n", __func__,
275162306a36Sopenharmony_ci			ipc4_copier->dai_type);
275262306a36Sopenharmony_ci		return -EINVAL;
275362306a36Sopenharmony_ci	}
275462306a36Sopenharmony_ci
275562306a36Sopenharmony_ci	return 0;
275662306a36Sopenharmony_ci}
275762306a36Sopenharmony_ci
275862306a36Sopenharmony_cistatic int sof_ipc4_parse_manifest(struct snd_soc_component *scomp, int index,
275962306a36Sopenharmony_ci				   struct snd_soc_tplg_manifest *man)
276062306a36Sopenharmony_ci{
276162306a36Sopenharmony_ci	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
276262306a36Sopenharmony_ci	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
276362306a36Sopenharmony_ci	struct sof_manifest_tlv *manifest_tlv;
276462306a36Sopenharmony_ci	struct sof_manifest *manifest;
276562306a36Sopenharmony_ci	u32 size = le32_to_cpu(man->priv.size);
276662306a36Sopenharmony_ci	u8 *man_ptr = man->priv.data;
276762306a36Sopenharmony_ci	u32 len_check;
276862306a36Sopenharmony_ci	int i;
276962306a36Sopenharmony_ci
277062306a36Sopenharmony_ci	if (!size || size < SOF_IPC4_TPLG_ABI_SIZE) {
277162306a36Sopenharmony_ci		dev_err(scomp->dev, "%s: Invalid topology ABI size: %u\n",
277262306a36Sopenharmony_ci			__func__, size);
277362306a36Sopenharmony_ci		return -EINVAL;
277462306a36Sopenharmony_ci	}
277562306a36Sopenharmony_ci
277662306a36Sopenharmony_ci	manifest = (struct sof_manifest *)man_ptr;
277762306a36Sopenharmony_ci
277862306a36Sopenharmony_ci	dev_info(scomp->dev,
277962306a36Sopenharmony_ci		 "Topology: ABI %d:%d:%d Kernel ABI %u:%u:%u\n",
278062306a36Sopenharmony_ci		  le16_to_cpu(manifest->abi_major), le16_to_cpu(manifest->abi_minor),
278162306a36Sopenharmony_ci		  le16_to_cpu(manifest->abi_patch),
278262306a36Sopenharmony_ci		  SOF_ABI_MAJOR, SOF_ABI_MINOR, SOF_ABI_PATCH);
278362306a36Sopenharmony_ci
278462306a36Sopenharmony_ci	/* TODO: Add ABI compatibility check */
278562306a36Sopenharmony_ci
278662306a36Sopenharmony_ci	/* no more data after the ABI version */
278762306a36Sopenharmony_ci	if (size <= SOF_IPC4_TPLG_ABI_SIZE)
278862306a36Sopenharmony_ci		return 0;
278962306a36Sopenharmony_ci
279062306a36Sopenharmony_ci	manifest_tlv = manifest->items;
279162306a36Sopenharmony_ci	len_check = sizeof(struct sof_manifest);
279262306a36Sopenharmony_ci	for (i = 0; i < le16_to_cpu(manifest->count); i++) {
279362306a36Sopenharmony_ci		len_check += sizeof(struct sof_manifest_tlv) + le32_to_cpu(manifest_tlv->size);
279462306a36Sopenharmony_ci		if (len_check > size)
279562306a36Sopenharmony_ci			return -EINVAL;
279662306a36Sopenharmony_ci
279762306a36Sopenharmony_ci		switch (le32_to_cpu(manifest_tlv->type)) {
279862306a36Sopenharmony_ci		case SOF_MANIFEST_DATA_TYPE_NHLT:
279962306a36Sopenharmony_ci			/* no NHLT in BIOS, so use the one from topology manifest */
280062306a36Sopenharmony_ci			if (ipc4_data->nhlt)
280162306a36Sopenharmony_ci				break;
280262306a36Sopenharmony_ci			ipc4_data->nhlt = devm_kmemdup(sdev->dev, manifest_tlv->data,
280362306a36Sopenharmony_ci						       le32_to_cpu(manifest_tlv->size), GFP_KERNEL);
280462306a36Sopenharmony_ci			if (!ipc4_data->nhlt)
280562306a36Sopenharmony_ci				return -ENOMEM;
280662306a36Sopenharmony_ci			break;
280762306a36Sopenharmony_ci		default:
280862306a36Sopenharmony_ci			dev_warn(scomp->dev, "Skipping unknown manifest data type %d\n",
280962306a36Sopenharmony_ci				 manifest_tlv->type);
281062306a36Sopenharmony_ci			break;
281162306a36Sopenharmony_ci		}
281262306a36Sopenharmony_ci		man_ptr += sizeof(struct sof_manifest_tlv) + le32_to_cpu(manifest_tlv->size);
281362306a36Sopenharmony_ci		manifest_tlv = (struct sof_manifest_tlv *)man_ptr;
281462306a36Sopenharmony_ci	}
281562306a36Sopenharmony_ci
281662306a36Sopenharmony_ci	return 0;
281762306a36Sopenharmony_ci}
281862306a36Sopenharmony_ci
281962306a36Sopenharmony_cistatic int sof_ipc4_dai_get_clk(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int clk_type)
282062306a36Sopenharmony_ci{
282162306a36Sopenharmony_ci	struct sof_ipc4_copier *ipc4_copier = dai->private;
282262306a36Sopenharmony_ci	struct snd_soc_tplg_hw_config *hw_config;
282362306a36Sopenharmony_ci	struct snd_sof_dai_link *slink;
282462306a36Sopenharmony_ci	bool dai_link_found = false;
282562306a36Sopenharmony_ci	bool hw_cfg_found = false;
282662306a36Sopenharmony_ci	int i;
282762306a36Sopenharmony_ci
282862306a36Sopenharmony_ci	if (!ipc4_copier)
282962306a36Sopenharmony_ci		return 0;
283062306a36Sopenharmony_ci
283162306a36Sopenharmony_ci	list_for_each_entry(slink, &sdev->dai_link_list, list) {
283262306a36Sopenharmony_ci		if (!strcmp(slink->link->name, dai->name)) {
283362306a36Sopenharmony_ci			dai_link_found = true;
283462306a36Sopenharmony_ci			break;
283562306a36Sopenharmony_ci		}
283662306a36Sopenharmony_ci	}
283762306a36Sopenharmony_ci
283862306a36Sopenharmony_ci	if (!dai_link_found) {
283962306a36Sopenharmony_ci		dev_err(sdev->dev, "no DAI link found for DAI %s\n", dai->name);
284062306a36Sopenharmony_ci		return -EINVAL;
284162306a36Sopenharmony_ci	}
284262306a36Sopenharmony_ci
284362306a36Sopenharmony_ci	for (i = 0; i < slink->num_hw_configs; i++) {
284462306a36Sopenharmony_ci		hw_config = &slink->hw_configs[i];
284562306a36Sopenharmony_ci		if (dai->current_config == le32_to_cpu(hw_config->id)) {
284662306a36Sopenharmony_ci			hw_cfg_found = true;
284762306a36Sopenharmony_ci			break;
284862306a36Sopenharmony_ci		}
284962306a36Sopenharmony_ci	}
285062306a36Sopenharmony_ci
285162306a36Sopenharmony_ci	if (!hw_cfg_found) {
285262306a36Sopenharmony_ci		dev_err(sdev->dev, "no matching hw_config found for DAI %s\n", dai->name);
285362306a36Sopenharmony_ci		return -EINVAL;
285462306a36Sopenharmony_ci	}
285562306a36Sopenharmony_ci
285662306a36Sopenharmony_ci	switch (ipc4_copier->dai_type) {
285762306a36Sopenharmony_ci	case SOF_DAI_INTEL_SSP:
285862306a36Sopenharmony_ci		switch (clk_type) {
285962306a36Sopenharmony_ci		case SOF_DAI_CLK_INTEL_SSP_MCLK:
286062306a36Sopenharmony_ci			return le32_to_cpu(hw_config->mclk_rate);
286162306a36Sopenharmony_ci		case SOF_DAI_CLK_INTEL_SSP_BCLK:
286262306a36Sopenharmony_ci			return le32_to_cpu(hw_config->bclk_rate);
286362306a36Sopenharmony_ci		default:
286462306a36Sopenharmony_ci			dev_err(sdev->dev, "Invalid clk type for SSP %d\n", clk_type);
286562306a36Sopenharmony_ci			break;
286662306a36Sopenharmony_ci		}
286762306a36Sopenharmony_ci		break;
286862306a36Sopenharmony_ci	default:
286962306a36Sopenharmony_ci		dev_err(sdev->dev, "DAI type %d not supported yet!\n", ipc4_copier->dai_type);
287062306a36Sopenharmony_ci		break;
287162306a36Sopenharmony_ci	}
287262306a36Sopenharmony_ci
287362306a36Sopenharmony_ci	return -EINVAL;
287462306a36Sopenharmony_ci}
287562306a36Sopenharmony_ci
287662306a36Sopenharmony_cistatic int sof_ipc4_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verify)
287762306a36Sopenharmony_ci{
287862306a36Sopenharmony_ci	struct snd_sof_pcm *spcm;
287962306a36Sopenharmony_ci	int dir, ret;
288062306a36Sopenharmony_ci
288162306a36Sopenharmony_ci	/*
288262306a36Sopenharmony_ci	 * This function is called during system suspend, we need to make sure
288362306a36Sopenharmony_ci	 * that all streams have been freed up.
288462306a36Sopenharmony_ci	 * Freeing might have been skipped when xrun happened just at the start
288562306a36Sopenharmony_ci	 * of the suspend and it sent a SNDRV_PCM_TRIGGER_STOP to the active
288662306a36Sopenharmony_ci	 * stream. This will call sof_pcm_stream_free() with
288762306a36Sopenharmony_ci	 * free_widget_list = false which will leave the kernel and firmware out
288862306a36Sopenharmony_ci	 * of sync during suspend/resume.
288962306a36Sopenharmony_ci	 *
289062306a36Sopenharmony_ci	 * This will also make sure that paused streams handled correctly.
289162306a36Sopenharmony_ci	 */
289262306a36Sopenharmony_ci	list_for_each_entry(spcm, &sdev->pcm_list, list) {
289362306a36Sopenharmony_ci		for_each_pcm_streams(dir) {
289462306a36Sopenharmony_ci			struct snd_pcm_substream *substream = spcm->stream[dir].substream;
289562306a36Sopenharmony_ci
289662306a36Sopenharmony_ci			if (!substream || !substream->runtime || spcm->stream[dir].suspend_ignored)
289762306a36Sopenharmony_ci				continue;
289862306a36Sopenharmony_ci
289962306a36Sopenharmony_ci			if (spcm->stream[dir].list) {
290062306a36Sopenharmony_ci				ret = sof_pcm_stream_free(sdev, substream, spcm, dir, true);
290162306a36Sopenharmony_ci				if (ret < 0)
290262306a36Sopenharmony_ci					return ret;
290362306a36Sopenharmony_ci			}
290462306a36Sopenharmony_ci		}
290562306a36Sopenharmony_ci	}
290662306a36Sopenharmony_ci	return 0;
290762306a36Sopenharmony_ci}
290862306a36Sopenharmony_ci
290962306a36Sopenharmony_cistatic int sof_ipc4_link_setup(struct snd_sof_dev *sdev, struct snd_soc_dai_link *link)
291062306a36Sopenharmony_ci{
291162306a36Sopenharmony_ci	if (link->no_pcm)
291262306a36Sopenharmony_ci		return 0;
291362306a36Sopenharmony_ci
291462306a36Sopenharmony_ci	/*
291562306a36Sopenharmony_ci	 * set default trigger order for all links. Exceptions to
291662306a36Sopenharmony_ci	 * the rule will be handled in sof_pcm_dai_link_fixup()
291762306a36Sopenharmony_ci	 * For playback, the sequence is the following: start BE,
291862306a36Sopenharmony_ci	 * start FE, stop FE, stop BE; for Capture the sequence is
291962306a36Sopenharmony_ci	 * inverted start FE, start BE, stop BE, stop FE
292062306a36Sopenharmony_ci	 */
292162306a36Sopenharmony_ci	link->trigger[SNDRV_PCM_STREAM_PLAYBACK] = SND_SOC_DPCM_TRIGGER_POST;
292262306a36Sopenharmony_ci	link->trigger[SNDRV_PCM_STREAM_CAPTURE] = SND_SOC_DPCM_TRIGGER_PRE;
292362306a36Sopenharmony_ci
292462306a36Sopenharmony_ci	return 0;
292562306a36Sopenharmony_ci}
292662306a36Sopenharmony_ci
292762306a36Sopenharmony_cistatic enum sof_tokens common_copier_token_list[] = {
292862306a36Sopenharmony_ci	SOF_COMP_TOKENS,
292962306a36Sopenharmony_ci	SOF_AUDIO_FMT_NUM_TOKENS,
293062306a36Sopenharmony_ci	SOF_IN_AUDIO_FORMAT_TOKENS,
293162306a36Sopenharmony_ci	SOF_OUT_AUDIO_FORMAT_TOKENS,
293262306a36Sopenharmony_ci	SOF_COPIER_DEEP_BUFFER_TOKENS,
293362306a36Sopenharmony_ci	SOF_COPIER_TOKENS,
293462306a36Sopenharmony_ci	SOF_COMP_EXT_TOKENS,
293562306a36Sopenharmony_ci};
293662306a36Sopenharmony_ci
293762306a36Sopenharmony_cistatic enum sof_tokens pipeline_token_list[] = {
293862306a36Sopenharmony_ci	SOF_SCHED_TOKENS,
293962306a36Sopenharmony_ci	SOF_PIPELINE_TOKENS,
294062306a36Sopenharmony_ci};
294162306a36Sopenharmony_ci
294262306a36Sopenharmony_cistatic enum sof_tokens dai_token_list[] = {
294362306a36Sopenharmony_ci	SOF_COMP_TOKENS,
294462306a36Sopenharmony_ci	SOF_AUDIO_FMT_NUM_TOKENS,
294562306a36Sopenharmony_ci	SOF_IN_AUDIO_FORMAT_TOKENS,
294662306a36Sopenharmony_ci	SOF_OUT_AUDIO_FORMAT_TOKENS,
294762306a36Sopenharmony_ci	SOF_COPIER_TOKENS,
294862306a36Sopenharmony_ci	SOF_DAI_TOKENS,
294962306a36Sopenharmony_ci	SOF_COMP_EXT_TOKENS,
295062306a36Sopenharmony_ci};
295162306a36Sopenharmony_ci
295262306a36Sopenharmony_cistatic enum sof_tokens pga_token_list[] = {
295362306a36Sopenharmony_ci	SOF_COMP_TOKENS,
295462306a36Sopenharmony_ci	SOF_GAIN_TOKENS,
295562306a36Sopenharmony_ci	SOF_AUDIO_FMT_NUM_TOKENS,
295662306a36Sopenharmony_ci	SOF_IN_AUDIO_FORMAT_TOKENS,
295762306a36Sopenharmony_ci	SOF_OUT_AUDIO_FORMAT_TOKENS,
295862306a36Sopenharmony_ci	SOF_COMP_EXT_TOKENS,
295962306a36Sopenharmony_ci};
296062306a36Sopenharmony_ci
296162306a36Sopenharmony_cistatic enum sof_tokens mixer_token_list[] = {
296262306a36Sopenharmony_ci	SOF_COMP_TOKENS,
296362306a36Sopenharmony_ci	SOF_AUDIO_FMT_NUM_TOKENS,
296462306a36Sopenharmony_ci	SOF_IN_AUDIO_FORMAT_TOKENS,
296562306a36Sopenharmony_ci	SOF_OUT_AUDIO_FORMAT_TOKENS,
296662306a36Sopenharmony_ci	SOF_COMP_EXT_TOKENS,
296762306a36Sopenharmony_ci};
296862306a36Sopenharmony_ci
296962306a36Sopenharmony_cistatic enum sof_tokens src_token_list[] = {
297062306a36Sopenharmony_ci	SOF_COMP_TOKENS,
297162306a36Sopenharmony_ci	SOF_SRC_TOKENS,
297262306a36Sopenharmony_ci	SOF_AUDIO_FMT_NUM_TOKENS,
297362306a36Sopenharmony_ci	SOF_IN_AUDIO_FORMAT_TOKENS,
297462306a36Sopenharmony_ci	SOF_OUT_AUDIO_FORMAT_TOKENS,
297562306a36Sopenharmony_ci	SOF_COMP_EXT_TOKENS,
297662306a36Sopenharmony_ci};
297762306a36Sopenharmony_ci
297862306a36Sopenharmony_cistatic enum sof_tokens process_token_list[] = {
297962306a36Sopenharmony_ci	SOF_COMP_TOKENS,
298062306a36Sopenharmony_ci	SOF_AUDIO_FMT_NUM_TOKENS,
298162306a36Sopenharmony_ci	SOF_IN_AUDIO_FORMAT_TOKENS,
298262306a36Sopenharmony_ci	SOF_OUT_AUDIO_FORMAT_TOKENS,
298362306a36Sopenharmony_ci	SOF_COMP_EXT_TOKENS,
298462306a36Sopenharmony_ci};
298562306a36Sopenharmony_ci
298662306a36Sopenharmony_cistatic const struct sof_ipc_tplg_widget_ops tplg_ipc4_widget_ops[SND_SOC_DAPM_TYPE_COUNT] = {
298762306a36Sopenharmony_ci	[snd_soc_dapm_aif_in] =  {sof_ipc4_widget_setup_pcm, sof_ipc4_widget_free_comp_pcm,
298862306a36Sopenharmony_ci				  common_copier_token_list, ARRAY_SIZE(common_copier_token_list),
298962306a36Sopenharmony_ci				  NULL, sof_ipc4_prepare_copier_module,
299062306a36Sopenharmony_ci				  sof_ipc4_unprepare_copier_module},
299162306a36Sopenharmony_ci	[snd_soc_dapm_aif_out] = {sof_ipc4_widget_setup_pcm, sof_ipc4_widget_free_comp_pcm,
299262306a36Sopenharmony_ci				  common_copier_token_list, ARRAY_SIZE(common_copier_token_list),
299362306a36Sopenharmony_ci				  NULL, sof_ipc4_prepare_copier_module,
299462306a36Sopenharmony_ci				  sof_ipc4_unprepare_copier_module},
299562306a36Sopenharmony_ci	[snd_soc_dapm_dai_in] = {sof_ipc4_widget_setup_comp_dai, sof_ipc4_widget_free_comp_dai,
299662306a36Sopenharmony_ci				 dai_token_list, ARRAY_SIZE(dai_token_list), NULL,
299762306a36Sopenharmony_ci				 sof_ipc4_prepare_copier_module,
299862306a36Sopenharmony_ci				 sof_ipc4_unprepare_copier_module},
299962306a36Sopenharmony_ci	[snd_soc_dapm_dai_out] = {sof_ipc4_widget_setup_comp_dai, sof_ipc4_widget_free_comp_dai,
300062306a36Sopenharmony_ci				  dai_token_list, ARRAY_SIZE(dai_token_list), NULL,
300162306a36Sopenharmony_ci				  sof_ipc4_prepare_copier_module,
300262306a36Sopenharmony_ci				  sof_ipc4_unprepare_copier_module},
300362306a36Sopenharmony_ci	[snd_soc_dapm_buffer] = {sof_ipc4_widget_setup_pcm, sof_ipc4_widget_free_comp_pcm,
300462306a36Sopenharmony_ci				 common_copier_token_list, ARRAY_SIZE(common_copier_token_list),
300562306a36Sopenharmony_ci				 NULL, sof_ipc4_prepare_copier_module,
300662306a36Sopenharmony_ci				 sof_ipc4_unprepare_copier_module},
300762306a36Sopenharmony_ci	[snd_soc_dapm_scheduler] = {sof_ipc4_widget_setup_comp_pipeline,
300862306a36Sopenharmony_ci				    sof_ipc4_widget_free_comp_pipeline,
300962306a36Sopenharmony_ci				    pipeline_token_list, ARRAY_SIZE(pipeline_token_list), NULL,
301062306a36Sopenharmony_ci				    NULL, NULL},
301162306a36Sopenharmony_ci	[snd_soc_dapm_pga] = {sof_ipc4_widget_setup_comp_pga, sof_ipc4_widget_free_comp_pga,
301262306a36Sopenharmony_ci			      pga_token_list, ARRAY_SIZE(pga_token_list), NULL,
301362306a36Sopenharmony_ci			      sof_ipc4_prepare_gain_module,
301462306a36Sopenharmony_ci			      NULL},
301562306a36Sopenharmony_ci	[snd_soc_dapm_mixer] = {sof_ipc4_widget_setup_comp_mixer, sof_ipc4_widget_free_comp_mixer,
301662306a36Sopenharmony_ci				mixer_token_list, ARRAY_SIZE(mixer_token_list),
301762306a36Sopenharmony_ci				NULL, sof_ipc4_prepare_mixer_module,
301862306a36Sopenharmony_ci				NULL},
301962306a36Sopenharmony_ci	[snd_soc_dapm_src] = {sof_ipc4_widget_setup_comp_src, sof_ipc4_widget_free_comp_src,
302062306a36Sopenharmony_ci				src_token_list, ARRAY_SIZE(src_token_list),
302162306a36Sopenharmony_ci				NULL, sof_ipc4_prepare_src_module,
302262306a36Sopenharmony_ci				NULL},
302362306a36Sopenharmony_ci	[snd_soc_dapm_effect] = {sof_ipc4_widget_setup_comp_process,
302462306a36Sopenharmony_ci				sof_ipc4_widget_free_comp_process,
302562306a36Sopenharmony_ci				process_token_list, ARRAY_SIZE(process_token_list),
302662306a36Sopenharmony_ci				NULL, sof_ipc4_prepare_process_module,
302762306a36Sopenharmony_ci				NULL},
302862306a36Sopenharmony_ci};
302962306a36Sopenharmony_ci
303062306a36Sopenharmony_ciconst struct sof_ipc_tplg_ops ipc4_tplg_ops = {
303162306a36Sopenharmony_ci	.widget = tplg_ipc4_widget_ops,
303262306a36Sopenharmony_ci	.token_list = ipc4_token_list,
303362306a36Sopenharmony_ci	.control_setup = sof_ipc4_control_setup,
303462306a36Sopenharmony_ci	.control = &tplg_ipc4_control_ops,
303562306a36Sopenharmony_ci	.widget_setup = sof_ipc4_widget_setup,
303662306a36Sopenharmony_ci	.widget_free = sof_ipc4_widget_free,
303762306a36Sopenharmony_ci	.route_setup = sof_ipc4_route_setup,
303862306a36Sopenharmony_ci	.route_free = sof_ipc4_route_free,
303962306a36Sopenharmony_ci	.dai_config = sof_ipc4_dai_config,
304062306a36Sopenharmony_ci	.parse_manifest = sof_ipc4_parse_manifest,
304162306a36Sopenharmony_ci	.dai_get_clk = sof_ipc4_dai_get_clk,
304262306a36Sopenharmony_ci	.tear_down_all_pipelines = sof_ipc4_tear_down_all_pipelines,
304362306a36Sopenharmony_ci	.link_setup = sof_ipc4_link_setup,
304462306a36Sopenharmony_ci};
3045