190e89155SRanjani Sridharan // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 290e89155SRanjani Sridharan // 390e89155SRanjani Sridharan // This file is provided under a dual BSD/GPLv2 license. When using or 490e89155SRanjani Sridharan // redistributing this file, you may do so under either license. 590e89155SRanjani Sridharan // 690e89155SRanjani Sridharan // Copyright(c) 2022 Intel Corporation. All rights reserved. 790e89155SRanjani Sridharan // 890e89155SRanjani Sridharan // 990e89155SRanjani Sridharan #include <uapi/sound/sof/tokens.h> 1090e89155SRanjani Sridharan #include <sound/pcm_params.h> 1190e89155SRanjani Sridharan #include <sound/sof/ext_manifest4.h> 12aa84ffb7SRanjani Sridharan #include <sound/intel-nhlt.h> 1390e89155SRanjani Sridharan #include "sof-priv.h" 1490e89155SRanjani Sridharan #include "sof-audio.h" 1590e89155SRanjani Sridharan #include "ipc4-priv.h" 1690e89155SRanjani Sridharan #include "ipc4-topology.h" 1790e89155SRanjani Sridharan #include "ops.h" 1890e89155SRanjani Sridharan 19d97964f8SRanjani Sridharan #define SOF_IPC4_GAIN_PARAM_ID 0 20323aa1f0SRanjani Sridharan #define SOF_IPC4_TPLG_ABI_SIZE 6 21d97964f8SRanjani Sridharan 22a150345aSBard Liao static DEFINE_IDA(alh_group_ida); 23a2ba1f70SBard Liao static DEFINE_IDA(pipeline_ida); 24a150345aSBard Liao 2590e89155SRanjani Sridharan static const struct sof_topology_token ipc4_sched_tokens[] = { 2690e89155SRanjani Sridharan {SOF_TKN_SCHED_LP_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 2705ade472SPeter Ujfalusi offsetof(struct sof_ipc4_pipeline, lp_mode)}, 2805ade472SPeter Ujfalusi {SOF_TKN_SCHED_CORE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 2905ade472SPeter Ujfalusi offsetof(struct sof_ipc4_pipeline, core_id)}, 3090e89155SRanjani Sridharan }; 3190e89155SRanjani Sridharan 3290e89155SRanjani Sridharan static const struct sof_topology_token pipeline_tokens[] = { 3390e89155SRanjani Sridharan {SOF_TKN_SCHED_DYNAMIC_PIPELINE, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, 3490e89155SRanjani Sridharan offsetof(struct snd_sof_widget, dynamic_pipeline_widget)}, 3590e89155SRanjani Sridharan }; 3690e89155SRanjani Sridharan 372cabd02bSRanjani Sridharan static const struct sof_topology_token ipc4_comp_tokens[] = { 382cabd02bSRanjani Sridharan {SOF_TKN_COMP_CPC, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 392cabd02bSRanjani Sridharan offsetof(struct sof_ipc4_base_module_cfg, cpc)}, 402cabd02bSRanjani Sridharan {SOF_TKN_COMP_IS_PAGES, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 412cabd02bSRanjani Sridharan offsetof(struct sof_ipc4_base_module_cfg, is_pages)}, 422cabd02bSRanjani Sridharan }; 432cabd02bSRanjani Sridharan 442cabd02bSRanjani Sridharan static const struct sof_topology_token ipc4_in_audio_format_tokens[] = { 452cabd02bSRanjani Sridharan {SOF_TKN_CAVS_AUDIO_FORMAT_IN_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 467ab6b1e8SRanjani Sridharan offsetof(struct sof_ipc4_pin_format, audio_fmt.sampling_frequency)}, 472cabd02bSRanjani Sridharan {SOF_TKN_CAVS_AUDIO_FORMAT_IN_BIT_DEPTH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 487ab6b1e8SRanjani Sridharan offsetof(struct sof_ipc4_pin_format, audio_fmt.bit_depth)}, 492cabd02bSRanjani Sridharan {SOF_TKN_CAVS_AUDIO_FORMAT_IN_CH_MAP, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 507ab6b1e8SRanjani Sridharan offsetof(struct sof_ipc4_pin_format, audio_fmt.ch_map)}, 512cabd02bSRanjani Sridharan {SOF_TKN_CAVS_AUDIO_FORMAT_IN_CH_CFG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 527ab6b1e8SRanjani Sridharan offsetof(struct sof_ipc4_pin_format, audio_fmt.ch_cfg)}, 532cabd02bSRanjani Sridharan {SOF_TKN_CAVS_AUDIO_FORMAT_IN_INTERLEAVING_STYLE, SND_SOC_TPLG_TUPLE_TYPE_WORD, 547ab6b1e8SRanjani Sridharan get_token_u32, offsetof(struct sof_ipc4_pin_format, 557ab6b1e8SRanjani Sridharan audio_fmt.interleaving_style)}, 562cabd02bSRanjani Sridharan {SOF_TKN_CAVS_AUDIO_FORMAT_IN_FMT_CFG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 577ab6b1e8SRanjani Sridharan offsetof(struct sof_ipc4_pin_format, audio_fmt.fmt_cfg)}, 587ab6b1e8SRanjani Sridharan {SOF_TKN_CAVS_AUDIO_FORMAT_PIN_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 597ab6b1e8SRanjani Sridharan offsetof(struct sof_ipc4_pin_format, pin_index)}, 607ab6b1e8SRanjani Sridharan {SOF_TKN_CAVS_AUDIO_FORMAT_IBS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 617ab6b1e8SRanjani Sridharan offsetof(struct sof_ipc4_pin_format, buffer_size)}, 622cabd02bSRanjani Sridharan }; 632cabd02bSRanjani Sridharan 642cabd02bSRanjani Sridharan static const struct sof_topology_token ipc4_out_audio_format_tokens[] = { 652cabd02bSRanjani Sridharan {SOF_TKN_CAVS_AUDIO_FORMAT_OUT_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 667ab6b1e8SRanjani Sridharan offsetof(struct sof_ipc4_pin_format, audio_fmt.sampling_frequency)}, 672cabd02bSRanjani Sridharan {SOF_TKN_CAVS_AUDIO_FORMAT_OUT_BIT_DEPTH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 687ab6b1e8SRanjani Sridharan offsetof(struct sof_ipc4_pin_format, audio_fmt.bit_depth)}, 692cabd02bSRanjani Sridharan {SOF_TKN_CAVS_AUDIO_FORMAT_OUT_CH_MAP, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 707ab6b1e8SRanjani Sridharan offsetof(struct sof_ipc4_pin_format, audio_fmt.ch_map)}, 712cabd02bSRanjani Sridharan {SOF_TKN_CAVS_AUDIO_FORMAT_OUT_CH_CFG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 727ab6b1e8SRanjani Sridharan offsetof(struct sof_ipc4_pin_format, audio_fmt.ch_cfg)}, 732cabd02bSRanjani Sridharan {SOF_TKN_CAVS_AUDIO_FORMAT_OUT_INTERLEAVING_STYLE, SND_SOC_TPLG_TUPLE_TYPE_WORD, 747ab6b1e8SRanjani Sridharan get_token_u32, offsetof(struct sof_ipc4_pin_format, 757ab6b1e8SRanjani Sridharan audio_fmt.interleaving_style)}, 762cabd02bSRanjani Sridharan {SOF_TKN_CAVS_AUDIO_FORMAT_OUT_FMT_CFG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 777ab6b1e8SRanjani Sridharan offsetof(struct sof_ipc4_pin_format, audio_fmt.fmt_cfg)}, 787ab6b1e8SRanjani Sridharan {SOF_TKN_CAVS_AUDIO_FORMAT_PIN_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 797ab6b1e8SRanjani Sridharan offsetof(struct sof_ipc4_pin_format, pin_index)}, 807ab6b1e8SRanjani Sridharan {SOF_TKN_CAVS_AUDIO_FORMAT_OBS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 817ab6b1e8SRanjani Sridharan offsetof(struct sof_ipc4_pin_format, buffer_size)}, 822cabd02bSRanjani Sridharan }; 832cabd02bSRanjani Sridharan 84594c1bb9SRanjani Sridharan static const struct sof_topology_token ipc4_copier_deep_buffer_tokens[] = { 85594c1bb9SRanjani Sridharan {SOF_TKN_INTEL_COPIER_DEEP_BUFFER_DMA_MS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 0}, 862cabd02bSRanjani Sridharan }; 872cabd02bSRanjani Sridharan 882cabd02bSRanjani Sridharan static const struct sof_topology_token ipc4_copier_tokens[] = { 892cabd02bSRanjani Sridharan {SOF_TKN_INTEL_COPIER_NODE_TYPE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 0}, 902cabd02bSRanjani Sridharan }; 912cabd02bSRanjani Sridharan 922cabd02bSRanjani Sridharan static const struct sof_topology_token ipc4_audio_fmt_num_tokens[] = { 932cabd02bSRanjani Sridharan {SOF_TKN_COMP_NUM_AUDIO_FORMATS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 942cabd02bSRanjani Sridharan 0}, 952cabd02bSRanjani Sridharan }; 962cabd02bSRanjani Sridharan 97abfb536bSRanjani Sridharan static const struct sof_topology_token dai_tokens[] = { 98abfb536bSRanjani Sridharan {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, 99abfb536bSRanjani Sridharan offsetof(struct sof_ipc4_copier, dai_type)}, 100abfb536bSRanjani Sridharan {SOF_TKN_DAI_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 101abfb536bSRanjani Sridharan offsetof(struct sof_ipc4_copier, dai_index)}, 102abfb536bSRanjani Sridharan }; 103abfb536bSRanjani Sridharan 1042cabd02bSRanjani Sridharan /* Component extended tokens */ 1052cabd02bSRanjani Sridharan static const struct sof_topology_token comp_ext_tokens[] = { 1062cabd02bSRanjani Sridharan {SOF_TKN_COMP_UUID, SND_SOC_TPLG_TUPLE_TYPE_UUID, get_token_uuid, 1072cabd02bSRanjani Sridharan offsetof(struct snd_sof_widget, uuid)}, 108755ddc3aSRander Wang {SOF_TKN_COMP_CORE_ID, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 109755ddc3aSRander Wang offsetof(struct snd_sof_widget, core)}, 1102cabd02bSRanjani Sridharan }; 1112cabd02bSRanjani Sridharan 1124f838ab2SRanjani Sridharan static const struct sof_topology_token gain_tokens[] = { 1134f838ab2SRanjani Sridharan {SOF_TKN_GAIN_RAMP_TYPE, SND_SOC_TPLG_TUPLE_TYPE_WORD, 1144f838ab2SRanjani Sridharan get_token_u32, offsetof(struct sof_ipc4_gain_data, curve_type)}, 1154f838ab2SRanjani Sridharan {SOF_TKN_GAIN_RAMP_DURATION, 1164f838ab2SRanjani Sridharan SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 117e45cd86cSRander Wang offsetof(struct sof_ipc4_gain_data, curve_duration_l)}, 1184f838ab2SRanjani Sridharan {SOF_TKN_GAIN_VAL, SND_SOC_TPLG_TUPLE_TYPE_WORD, 1194f838ab2SRanjani Sridharan get_token_u32, offsetof(struct sof_ipc4_gain_data, init_val)}, 1204f838ab2SRanjani Sridharan }; 1214f838ab2SRanjani Sridharan 122b85f4fc4SRander Wang /* SRC */ 123b85f4fc4SRander Wang static const struct sof_topology_token src_tokens[] = { 124b85f4fc4SRander Wang {SOF_TKN_SRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 125b85f4fc4SRander Wang offsetof(struct sof_ipc4_src, sink_rate)}, 126b85f4fc4SRander Wang }; 127b85f4fc4SRander Wang 12890e89155SRanjani Sridharan static const struct sof_token_info ipc4_token_list[SOF_TOKEN_COUNT] = { 129abfb536bSRanjani Sridharan [SOF_DAI_TOKENS] = {"DAI tokens", dai_tokens, ARRAY_SIZE(dai_tokens)}, 13090e89155SRanjani Sridharan [SOF_PIPELINE_TOKENS] = {"Pipeline tokens", pipeline_tokens, ARRAY_SIZE(pipeline_tokens)}, 13190e89155SRanjani Sridharan [SOF_SCHED_TOKENS] = {"Scheduler tokens", ipc4_sched_tokens, 13290e89155SRanjani Sridharan ARRAY_SIZE(ipc4_sched_tokens)}, 1332cabd02bSRanjani Sridharan [SOF_COMP_EXT_TOKENS] = {"Comp extended tokens", comp_ext_tokens, 1342cabd02bSRanjani Sridharan ARRAY_SIZE(comp_ext_tokens)}, 1352cabd02bSRanjani Sridharan [SOF_COMP_TOKENS] = {"IPC4 Component tokens", 1362cabd02bSRanjani Sridharan ipc4_comp_tokens, ARRAY_SIZE(ipc4_comp_tokens)}, 1372cabd02bSRanjani Sridharan [SOF_IN_AUDIO_FORMAT_TOKENS] = {"IPC4 Input Audio format tokens", 1382cabd02bSRanjani Sridharan ipc4_in_audio_format_tokens, ARRAY_SIZE(ipc4_in_audio_format_tokens)}, 1392cabd02bSRanjani Sridharan [SOF_OUT_AUDIO_FORMAT_TOKENS] = {"IPC4 Output Audio format tokens", 1402cabd02bSRanjani Sridharan ipc4_out_audio_format_tokens, ARRAY_SIZE(ipc4_out_audio_format_tokens)}, 141594c1bb9SRanjani Sridharan [SOF_COPIER_DEEP_BUFFER_TOKENS] = {"IPC4 Copier deep buffer tokens", 142594c1bb9SRanjani Sridharan ipc4_copier_deep_buffer_tokens, ARRAY_SIZE(ipc4_copier_deep_buffer_tokens)}, 1432cabd02bSRanjani Sridharan [SOF_COPIER_TOKENS] = {"IPC4 Copier tokens", ipc4_copier_tokens, 1442cabd02bSRanjani Sridharan ARRAY_SIZE(ipc4_copier_tokens)}, 1452cabd02bSRanjani Sridharan [SOF_AUDIO_FMT_NUM_TOKENS] = {"IPC4 Audio format number tokens", 1462cabd02bSRanjani Sridharan ipc4_audio_fmt_num_tokens, ARRAY_SIZE(ipc4_audio_fmt_num_tokens)}, 1474f838ab2SRanjani Sridharan [SOF_GAIN_TOKENS] = {"Gain tokens", gain_tokens, ARRAY_SIZE(gain_tokens)}, 148b85f4fc4SRander Wang [SOF_SRC_TOKENS] = {"SRC tokens", src_tokens, ARRAY_SIZE(src_tokens)}, 14990e89155SRanjani Sridharan }; 15090e89155SRanjani Sridharan 1517ab6b1e8SRanjani Sridharan static void sof_ipc4_dbg_audio_format(struct device *dev, struct sof_ipc4_pin_format *pin_fmt, 1527ab6b1e8SRanjani Sridharan int num_formats) 1532cabd02bSRanjani Sridharan { 1542cabd02bSRanjani Sridharan int i; 1552cabd02bSRanjani Sridharan 1567ab6b1e8SRanjani Sridharan for (i = 0; i < num_formats; i++) { 1577ab6b1e8SRanjani Sridharan struct sof_ipc4_audio_format *fmt = &pin_fmt[i].audio_fmt; 1582cabd02bSRanjani Sridharan dev_dbg(dev, 1597ab6b1e8SRanjani Sridharan "Pin index #%d: %uHz, %ubit (ch_map %#x ch_cfg %u interleaving_style %u fmt_cfg %#x) buffer size %d\n", 1607ab6b1e8SRanjani Sridharan pin_fmt[i].pin_index, fmt->sampling_frequency, fmt->bit_depth, fmt->ch_map, 1617ab6b1e8SRanjani Sridharan fmt->ch_cfg, fmt->interleaving_style, fmt->fmt_cfg, 1627ab6b1e8SRanjani Sridharan pin_fmt[i].buffer_size); 1632cabd02bSRanjani Sridharan } 1642cabd02bSRanjani Sridharan } 1652cabd02bSRanjani Sridharan 1662cabd02bSRanjani Sridharan /** 1672cabd02bSRanjani Sridharan * sof_ipc4_get_audio_fmt - get available audio formats from swidget->tuples 1682cabd02bSRanjani Sridharan * @scomp: pointer to pointer to SOC component 1692cabd02bSRanjani Sridharan * @swidget: pointer to struct snd_sof_widget containing tuples 1702cabd02bSRanjani Sridharan * @available_fmt: pointer to struct sof_ipc4_available_audio_format being filling in 171171d5cceSRanjani Sridharan * @module_base_cfg: Pointer to the base_config in the module init IPC payload 1722cabd02bSRanjani Sridharan * 1732cabd02bSRanjani Sridharan * Return: 0 if successful 1742cabd02bSRanjani Sridharan */ 1752cabd02bSRanjani Sridharan static int sof_ipc4_get_audio_fmt(struct snd_soc_component *scomp, 1762cabd02bSRanjani Sridharan struct snd_sof_widget *swidget, 1772cabd02bSRanjani Sridharan struct sof_ipc4_available_audio_format *available_fmt, 1788abc9ab9SRanjani Sridharan struct sof_ipc4_base_module_cfg *module_base_cfg) 1792cabd02bSRanjani Sridharan { 1807ab6b1e8SRanjani Sridharan struct sof_ipc4_pin_format *out_format, *in_format; 1812cabd02bSRanjani Sridharan int audio_fmt_num = 0; 1827ab6b1e8SRanjani Sridharan int ret; 1832cabd02bSRanjani Sridharan 1842cabd02bSRanjani Sridharan ret = sof_update_ipc_object(scomp, &audio_fmt_num, 1852cabd02bSRanjani Sridharan SOF_AUDIO_FMT_NUM_TOKENS, swidget->tuples, 1862cabd02bSRanjani Sridharan swidget->num_tuples, sizeof(audio_fmt_num), 1); 1872cabd02bSRanjani Sridharan if (ret || audio_fmt_num <= 0) { 1882cabd02bSRanjani Sridharan dev_err(scomp->dev, "Invalid number of audio formats: %d\n", audio_fmt_num); 1892cabd02bSRanjani Sridharan return -EINVAL; 1902cabd02bSRanjani Sridharan } 1912cabd02bSRanjani Sridharan available_fmt->audio_fmt_num = audio_fmt_num; 1922cabd02bSRanjani Sridharan 1932cabd02bSRanjani Sridharan dev_dbg(scomp->dev, "Number of audio formats: %d\n", available_fmt->audio_fmt_num); 1942cabd02bSRanjani Sridharan 195171d5cceSRanjani Sridharan /* set cpc and is_pages in the module's base_config */ 196171d5cceSRanjani Sridharan ret = sof_update_ipc_object(scomp, module_base_cfg, SOF_COMP_TOKENS, swidget->tuples, 197171d5cceSRanjani Sridharan swidget->num_tuples, sizeof(*module_base_cfg), 1); 1982cabd02bSRanjani Sridharan if (ret) { 199171d5cceSRanjani Sridharan dev_err(scomp->dev, "parse comp tokens for %s failed, error: %d\n", 200171d5cceSRanjani Sridharan swidget->widget->name, ret); 2017ab6b1e8SRanjani Sridharan return ret; 2022cabd02bSRanjani Sridharan } 203171d5cceSRanjani Sridharan 204171d5cceSRanjani Sridharan dev_dbg(scomp->dev, "widget %s cpc: %d is_pages: %d\n", 205171d5cceSRanjani Sridharan swidget->widget->name, module_base_cfg->cpc, module_base_cfg->is_pages); 2062cabd02bSRanjani Sridharan 2077ab6b1e8SRanjani Sridharan in_format = kcalloc(available_fmt->audio_fmt_num, sizeof(*in_format), GFP_KERNEL); 2087ab6b1e8SRanjani Sridharan if (!in_format) 2097ab6b1e8SRanjani Sridharan return -ENOMEM; 2102cabd02bSRanjani Sridharan 2117ab6b1e8SRanjani Sridharan ret = sof_update_ipc_object(scomp, in_format, 2122cabd02bSRanjani Sridharan SOF_IN_AUDIO_FORMAT_TOKENS, swidget->tuples, 2137ab6b1e8SRanjani Sridharan swidget->num_tuples, sizeof(*in_format), 2142cabd02bSRanjani Sridharan available_fmt->audio_fmt_num); 2152cabd02bSRanjani Sridharan if (ret) { 2162cabd02bSRanjani Sridharan dev_err(scomp->dev, "parse base_config audio_fmt tokens failed %d\n", ret); 2172cabd02bSRanjani Sridharan goto err_in; 2182cabd02bSRanjani Sridharan } 2192cabd02bSRanjani Sridharan 2202cabd02bSRanjani Sridharan dev_dbg(scomp->dev, "Get input audio formats for %s\n", swidget->widget->name); 2217ab6b1e8SRanjani Sridharan sof_ipc4_dbg_audio_format(scomp->dev, available_fmt->input_pin_fmts, 2222cabd02bSRanjani Sridharan available_fmt->audio_fmt_num); 2232cabd02bSRanjani Sridharan 2242cabd02bSRanjani Sridharan out_format = kcalloc(available_fmt->audio_fmt_num, sizeof(*out_format), GFP_KERNEL); 2252cabd02bSRanjani Sridharan if (!out_format) { 2262cabd02bSRanjani Sridharan ret = -ENOMEM; 2272cabd02bSRanjani Sridharan goto err_in; 2282cabd02bSRanjani Sridharan } 2292cabd02bSRanjani Sridharan 2302cabd02bSRanjani Sridharan ret = sof_update_ipc_object(scomp, out_format, 2312cabd02bSRanjani Sridharan SOF_OUT_AUDIO_FORMAT_TOKENS, swidget->tuples, 2322cabd02bSRanjani Sridharan swidget->num_tuples, sizeof(*out_format), 2332cabd02bSRanjani Sridharan available_fmt->audio_fmt_num); 2342cabd02bSRanjani Sridharan 2352cabd02bSRanjani Sridharan if (ret) { 2362cabd02bSRanjani Sridharan dev_err(scomp->dev, "parse output audio_fmt tokens failed\n"); 2372cabd02bSRanjani Sridharan goto err_out; 2382cabd02bSRanjani Sridharan } 2392cabd02bSRanjani Sridharan 2402cabd02bSRanjani Sridharan dev_dbg(scomp->dev, "Get output audio formats for %s\n", swidget->widget->name); 2417ab6b1e8SRanjani Sridharan sof_ipc4_dbg_audio_format(scomp->dev, out_format, available_fmt->audio_fmt_num); 2422cabd02bSRanjani Sridharan 2437ab6b1e8SRanjani Sridharan available_fmt->output_pin_fmts = out_format; 2447ab6b1e8SRanjani Sridharan available_fmt->input_pin_fmts = in_format; 245e63a73f9SRanjani Sridharan 2462cabd02bSRanjani Sridharan return 0; 2472cabd02bSRanjani Sridharan 2482cabd02bSRanjani Sridharan err_out: 2492cabd02bSRanjani Sridharan kfree(out_format); 2502cabd02bSRanjani Sridharan err_in: 251e63a73f9SRanjani Sridharan kfree(in_format); 2522cabd02bSRanjani Sridharan return ret; 2532cabd02bSRanjani Sridharan } 2542cabd02bSRanjani Sridharan 255dc4fc0aeSLibin Yang /* release the memory allocated in sof_ipc4_get_audio_fmt */ 256dc4fc0aeSLibin Yang static void sof_ipc4_free_audio_fmt(struct sof_ipc4_available_audio_format *available_fmt) 257dc4fc0aeSLibin Yang 258dc4fc0aeSLibin Yang { 2597ab6b1e8SRanjani Sridharan kfree(available_fmt->output_pin_fmts); 2607ab6b1e8SRanjani Sridharan available_fmt->output_pin_fmts = NULL; 2617ab6b1e8SRanjani Sridharan kfree(available_fmt->input_pin_fmts); 2627ab6b1e8SRanjani Sridharan available_fmt->input_pin_fmts = NULL; 263dc4fc0aeSLibin Yang } 264dc4fc0aeSLibin Yang 265a29b2d02SBard Liao static void sof_ipc4_widget_free_comp_pipeline(struct snd_sof_widget *swidget) 26690e89155SRanjani Sridharan { 26790e89155SRanjani Sridharan kfree(swidget->private); 26890e89155SRanjani Sridharan } 26990e89155SRanjani Sridharan 2702cabd02bSRanjani Sridharan static int sof_ipc4_widget_set_module_info(struct snd_sof_widget *swidget) 2712cabd02bSRanjani Sridharan { 2722cabd02bSRanjani Sridharan struct snd_soc_component *scomp = swidget->scomp; 2732cabd02bSRanjani Sridharan struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 2742cabd02bSRanjani Sridharan 275c73f8b47SPeter Ujfalusi swidget->module_info = sof_ipc4_find_module_by_uuid(sdev, &swidget->uuid); 2765a932cfcSPeter Ujfalusi 277c73f8b47SPeter Ujfalusi if (swidget->module_info) 2782cabd02bSRanjani Sridharan return 0; 2792cabd02bSRanjani Sridharan 2802cabd02bSRanjani Sridharan dev_err(sdev->dev, "failed to find module info for widget %s with UUID %pUL\n", 2812cabd02bSRanjani Sridharan swidget->widget->name, &swidget->uuid); 2822cabd02bSRanjani Sridharan return -EINVAL; 2832cabd02bSRanjani Sridharan } 2842cabd02bSRanjani Sridharan 2852cabd02bSRanjani Sridharan static int sof_ipc4_widget_setup_msg(struct snd_sof_widget *swidget, struct sof_ipc4_msg *msg) 2862cabd02bSRanjani Sridharan { 2872cabd02bSRanjani Sridharan struct sof_ipc4_fw_module *fw_module; 288dc6137a5SRander Wang uint32_t type; 2892cabd02bSRanjani Sridharan int ret; 2902cabd02bSRanjani Sridharan 2912cabd02bSRanjani Sridharan ret = sof_ipc4_widget_set_module_info(swidget); 2922cabd02bSRanjani Sridharan if (ret) 2932cabd02bSRanjani Sridharan return ret; 2942cabd02bSRanjani Sridharan 2952cabd02bSRanjani Sridharan fw_module = swidget->module_info; 2962cabd02bSRanjani Sridharan 2972cabd02bSRanjani Sridharan msg->primary = fw_module->man4_module_entry.id; 2982cabd02bSRanjani Sridharan msg->primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_INIT_INSTANCE); 2992cabd02bSRanjani Sridharan msg->primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); 3002cabd02bSRanjani Sridharan msg->primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG); 3012cabd02bSRanjani Sridharan 302a2ba1f70SBard Liao msg->extension = SOF_IPC4_MOD_EXT_CORE_ID(swidget->core); 3032cabd02bSRanjani Sridharan 30480d53171SPierre-Louis Bossart type = (fw_module->man4_module_entry.type & SOF_IPC4_MODULE_DP) ? 1 : 0; 305dc6137a5SRander Wang msg->extension |= SOF_IPC4_MOD_EXT_DOMAIN(type); 306dc6137a5SRander Wang 3072cabd02bSRanjani Sridharan return 0; 3082cabd02bSRanjani Sridharan } 3092cabd02bSRanjani Sridharan 3102cabd02bSRanjani Sridharan static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget) 3112cabd02bSRanjani Sridharan { 3122cabd02bSRanjani Sridharan struct sof_ipc4_available_audio_format *available_fmt; 3132cabd02bSRanjani Sridharan struct snd_soc_component *scomp = swidget->scomp; 3142cabd02bSRanjani Sridharan struct sof_ipc4_copier *ipc4_copier; 3152cabd02bSRanjani Sridharan int node_type = 0; 316594c1bb9SRanjani Sridharan int ret; 3172cabd02bSRanjani Sridharan 3182cabd02bSRanjani Sridharan ipc4_copier = kzalloc(sizeof(*ipc4_copier), GFP_KERNEL); 3192cabd02bSRanjani Sridharan if (!ipc4_copier) 3202cabd02bSRanjani Sridharan return -ENOMEM; 3212cabd02bSRanjani Sridharan 3222cabd02bSRanjani Sridharan swidget->private = ipc4_copier; 3232cabd02bSRanjani Sridharan available_fmt = &ipc4_copier->available_fmt; 3242cabd02bSRanjani Sridharan 3252cabd02bSRanjani Sridharan dev_dbg(scomp->dev, "Updating IPC structure for %s\n", swidget->widget->name); 3262cabd02bSRanjani Sridharan 327171d5cceSRanjani Sridharan ret = sof_ipc4_get_audio_fmt(scomp, swidget, available_fmt, 3288abc9ab9SRanjani Sridharan &ipc4_copier->data.base_config); 3292cabd02bSRanjani Sridharan if (ret) 3302cabd02bSRanjani Sridharan goto free_copier; 3312cabd02bSRanjani Sridharan 3327d573425SBard Liao /* 3337d573425SBard Liao * This callback is used by host copier and module-to-module copier, 3347d573425SBard Liao * and only host copier needs to set gtw_cfg. 3357d573425SBard Liao */ 3367d573425SBard Liao if (!WIDGET_IS_AIF(swidget->id)) 3377d573425SBard Liao goto skip_gtw_cfg; 3387d573425SBard Liao 3392cabd02bSRanjani Sridharan ret = sof_update_ipc_object(scomp, &node_type, 3402cabd02bSRanjani Sridharan SOF_COPIER_TOKENS, swidget->tuples, 3412cabd02bSRanjani Sridharan swidget->num_tuples, sizeof(node_type), 1); 3422cabd02bSRanjani Sridharan 3432cabd02bSRanjani Sridharan if (ret) { 3442cabd02bSRanjani Sridharan dev_err(scomp->dev, "parse host copier node type token failed %d\n", 3452cabd02bSRanjani Sridharan ret); 346594c1bb9SRanjani Sridharan goto free_available_fmt; 3472cabd02bSRanjani Sridharan } 3482cabd02bSRanjani Sridharan dev_dbg(scomp->dev, "host copier '%s' node_type %u\n", swidget->widget->name, node_type); 3492cabd02bSRanjani Sridharan 3507d573425SBard Liao skip_gtw_cfg: 3512cabd02bSRanjani Sridharan ipc4_copier->gtw_attr = kzalloc(sizeof(*ipc4_copier->gtw_attr), GFP_KERNEL); 3522cabd02bSRanjani Sridharan if (!ipc4_copier->gtw_attr) { 3532cabd02bSRanjani Sridharan ret = -ENOMEM; 354594c1bb9SRanjani Sridharan goto free_available_fmt; 3552cabd02bSRanjani Sridharan } 3562cabd02bSRanjani Sridharan 3572cabd02bSRanjani Sridharan ipc4_copier->copier_config = (uint32_t *)ipc4_copier->gtw_attr; 3582cabd02bSRanjani Sridharan ipc4_copier->data.gtw_cfg.config_length = 3592cabd02bSRanjani Sridharan sizeof(struct sof_ipc4_gtw_attributes) >> 2; 3602cabd02bSRanjani Sridharan 3617d573425SBard Liao switch (swidget->id) { 3627d573425SBard Liao case snd_soc_dapm_aif_in: 3637d573425SBard Liao case snd_soc_dapm_aif_out: 3647d573425SBard Liao ipc4_copier->data.gtw_cfg.node_id = SOF_IPC4_NODE_TYPE(node_type); 3657d573425SBard Liao break; 3667d573425SBard Liao case snd_soc_dapm_buffer: 3677d573425SBard Liao ipc4_copier->data.gtw_cfg.node_id = SOF_IPC4_INVALID_NODE_ID; 3687d573425SBard Liao ipc4_copier->ipc_config_size = 0; 3697d573425SBard Liao break; 3707d573425SBard Liao default: 3717d573425SBard Liao dev_err(scomp->dev, "invalid widget type %d\n", swidget->id); 3727d573425SBard Liao ret = -EINVAL; 3737d573425SBard Liao goto free_gtw_attr; 3747d573425SBard Liao } 3757d573425SBard Liao 3762cabd02bSRanjani Sridharan /* set up module info and message header */ 3772cabd02bSRanjani Sridharan ret = sof_ipc4_widget_setup_msg(swidget, &ipc4_copier->msg); 3782cabd02bSRanjani Sridharan if (ret) 3792cabd02bSRanjani Sridharan goto free_gtw_attr; 3802cabd02bSRanjani Sridharan 3812cabd02bSRanjani Sridharan return 0; 3822cabd02bSRanjani Sridharan 3832cabd02bSRanjani Sridharan free_gtw_attr: 3842cabd02bSRanjani Sridharan kfree(ipc4_copier->gtw_attr); 385dc4fc0aeSLibin Yang free_available_fmt: 386dc4fc0aeSLibin Yang sof_ipc4_free_audio_fmt(available_fmt); 3872cabd02bSRanjani Sridharan free_copier: 3882cabd02bSRanjani Sridharan kfree(ipc4_copier); 389b737fd8cSLibin Yang swidget->private = NULL; 3902cabd02bSRanjani Sridharan return ret; 3912cabd02bSRanjani Sridharan } 3922cabd02bSRanjani Sridharan 3932cabd02bSRanjani Sridharan static void sof_ipc4_widget_free_comp_pcm(struct snd_sof_widget *swidget) 3942cabd02bSRanjani Sridharan { 3952cabd02bSRanjani Sridharan struct sof_ipc4_copier *ipc4_copier = swidget->private; 3962cabd02bSRanjani Sridharan struct sof_ipc4_available_audio_format *available_fmt; 3972cabd02bSRanjani Sridharan 3982cabd02bSRanjani Sridharan if (!ipc4_copier) 3992cabd02bSRanjani Sridharan return; 4002cabd02bSRanjani Sridharan 4012cabd02bSRanjani Sridharan available_fmt = &ipc4_copier->available_fmt; 4027ab6b1e8SRanjani Sridharan kfree(available_fmt->output_pin_fmts); 4032cabd02bSRanjani Sridharan kfree(ipc4_copier->gtw_attr); 4042cabd02bSRanjani Sridharan kfree(ipc4_copier); 4052cabd02bSRanjani Sridharan swidget->private = NULL; 4062cabd02bSRanjani Sridharan } 4072cabd02bSRanjani Sridharan 408abfb536bSRanjani Sridharan static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) 409abfb536bSRanjani Sridharan { 410abfb536bSRanjani Sridharan struct sof_ipc4_available_audio_format *available_fmt; 411abfb536bSRanjani Sridharan struct snd_soc_component *scomp = swidget->scomp; 412abfb536bSRanjani Sridharan struct snd_sof_dai *dai = swidget->private; 413abfb536bSRanjani Sridharan struct sof_ipc4_copier *ipc4_copier; 414abfb536bSRanjani Sridharan int node_type = 0; 415594c1bb9SRanjani Sridharan int ret; 416abfb536bSRanjani Sridharan 417abfb536bSRanjani Sridharan ipc4_copier = kzalloc(sizeof(*ipc4_copier), GFP_KERNEL); 418abfb536bSRanjani Sridharan if (!ipc4_copier) 419abfb536bSRanjani Sridharan return -ENOMEM; 420abfb536bSRanjani Sridharan 421abfb536bSRanjani Sridharan available_fmt = &ipc4_copier->available_fmt; 422abfb536bSRanjani Sridharan 423abfb536bSRanjani Sridharan dev_dbg(scomp->dev, "Updating IPC structure for %s\n", swidget->widget->name); 424abfb536bSRanjani Sridharan 425171d5cceSRanjani Sridharan ret = sof_ipc4_get_audio_fmt(scomp, swidget, available_fmt, 4268abc9ab9SRanjani Sridharan &ipc4_copier->data.base_config); 427abfb536bSRanjani Sridharan if (ret) 428abfb536bSRanjani Sridharan goto free_copier; 429abfb536bSRanjani Sridharan 430abfb536bSRanjani Sridharan ret = sof_update_ipc_object(scomp, &node_type, 431abfb536bSRanjani Sridharan SOF_COPIER_TOKENS, swidget->tuples, 432abfb536bSRanjani Sridharan swidget->num_tuples, sizeof(node_type), 1); 433abfb536bSRanjani Sridharan if (ret) { 434abfb536bSRanjani Sridharan dev_err(scomp->dev, "parse dai node type failed %d\n", ret); 435594c1bb9SRanjani Sridharan goto free_available_fmt; 436abfb536bSRanjani Sridharan } 437abfb536bSRanjani Sridharan 438abfb536bSRanjani Sridharan ret = sof_update_ipc_object(scomp, ipc4_copier, 439abfb536bSRanjani Sridharan SOF_DAI_TOKENS, swidget->tuples, 440abfb536bSRanjani Sridharan swidget->num_tuples, sizeof(u32), 1); 441abfb536bSRanjani Sridharan if (ret) { 442abfb536bSRanjani Sridharan dev_err(scomp->dev, "parse dai copier node token failed %d\n", ret); 443594c1bb9SRanjani Sridharan goto free_available_fmt; 444abfb536bSRanjani Sridharan } 445abfb536bSRanjani Sridharan 446abfb536bSRanjani Sridharan dev_dbg(scomp->dev, "dai %s node_type %u dai_type %u dai_index %d\n", swidget->widget->name, 447abfb536bSRanjani Sridharan node_type, ipc4_copier->dai_type, ipc4_copier->dai_index); 448abfb536bSRanjani Sridharan 449abfb536bSRanjani Sridharan ipc4_copier->data.gtw_cfg.node_id = SOF_IPC4_NODE_TYPE(node_type); 450aa84ffb7SRanjani Sridharan 451aa84ffb7SRanjani Sridharan switch (ipc4_copier->dai_type) { 452a45a4d43SBard Liao case SOF_DAI_INTEL_ALH: 453a45a4d43SBard Liao { 454a150345aSBard Liao struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 455a45a4d43SBard Liao struct sof_ipc4_alh_configuration_blob *blob; 4563c50211fSBard Liao struct snd_soc_dapm_path *p; 457a150345aSBard Liao struct snd_sof_widget *w; 4583c50211fSBard Liao int src_num = 0; 4593c50211fSBard Liao 4603c50211fSBard Liao snd_soc_dapm_widget_for_each_source_path(swidget->widget, p) 4613c50211fSBard Liao src_num++; 4623c50211fSBard Liao 4633c50211fSBard Liao if (swidget->id == snd_soc_dapm_dai_in && src_num == 0) { 4643c50211fSBard Liao /* 4653c50211fSBard Liao * The blob will not be used if the ALH copier is playback direction 4663c50211fSBard Liao * and doesn't connect to any source. 4673c50211fSBard Liao * It is fine to call kfree(ipc4_copier->copier_config) since 4683c50211fSBard Liao * ipc4_copier->copier_config is null. 4693c50211fSBard Liao */ 4703c50211fSBard Liao ret = 0; 4713c50211fSBard Liao break; 4723c50211fSBard Liao } 473a45a4d43SBard Liao 474a45a4d43SBard Liao blob = kzalloc(sizeof(*blob), GFP_KERNEL); 475a45a4d43SBard Liao if (!blob) { 476a45a4d43SBard Liao ret = -ENOMEM; 477594c1bb9SRanjani Sridharan goto free_available_fmt; 478a45a4d43SBard Liao } 479a45a4d43SBard Liao 480a150345aSBard Liao list_for_each_entry(w, &sdev->widget_list, list) { 481a150345aSBard Liao if (w->widget->sname && 482a150345aSBard Liao strcmp(w->widget->sname, swidget->widget->sname)) 483a150345aSBard Liao continue; 484a150345aSBard Liao 485a150345aSBard Liao blob->alh_cfg.count++; 486a150345aSBard Liao } 487a150345aSBard Liao 488a45a4d43SBard Liao ipc4_copier->copier_config = (uint32_t *)blob; 489a45a4d43SBard Liao ipc4_copier->data.gtw_cfg.config_length = sizeof(*blob) >> 2; 490a45a4d43SBard Liao break; 491a45a4d43SBard Liao } 492aa84ffb7SRanjani Sridharan case SOF_DAI_INTEL_SSP: 493aa84ffb7SRanjani Sridharan /* set SSP DAI index as the node_id */ 494aa84ffb7SRanjani Sridharan ipc4_copier->data.gtw_cfg.node_id |= 495aa84ffb7SRanjani Sridharan SOF_IPC4_NODE_INDEX_INTEL_SSP(ipc4_copier->dai_index); 496aa84ffb7SRanjani Sridharan break; 497aa84ffb7SRanjani Sridharan case SOF_DAI_INTEL_DMIC: 498aa84ffb7SRanjani Sridharan /* set DMIC DAI index as the node_id */ 499aa84ffb7SRanjani Sridharan ipc4_copier->data.gtw_cfg.node_id |= 500aa84ffb7SRanjani Sridharan SOF_IPC4_NODE_INDEX_INTEL_DMIC(ipc4_copier->dai_index); 501aa84ffb7SRanjani Sridharan break; 502aa84ffb7SRanjani Sridharan default: 503abfb536bSRanjani Sridharan ipc4_copier->gtw_attr = kzalloc(sizeof(*ipc4_copier->gtw_attr), GFP_KERNEL); 504abfb536bSRanjani Sridharan if (!ipc4_copier->gtw_attr) { 505abfb536bSRanjani Sridharan ret = -ENOMEM; 506594c1bb9SRanjani Sridharan goto free_available_fmt; 507abfb536bSRanjani Sridharan } 508abfb536bSRanjani Sridharan 509abfb536bSRanjani Sridharan ipc4_copier->copier_config = (uint32_t *)ipc4_copier->gtw_attr; 510aa84ffb7SRanjani Sridharan ipc4_copier->data.gtw_cfg.config_length = 511aa84ffb7SRanjani Sridharan sizeof(struct sof_ipc4_gtw_attributes) >> 2; 512aa84ffb7SRanjani Sridharan break; 513aa84ffb7SRanjani Sridharan } 514abfb536bSRanjani Sridharan 515abfb536bSRanjani Sridharan dai->scomp = scomp; 516abfb536bSRanjani Sridharan dai->private = ipc4_copier; 517abfb536bSRanjani Sridharan 518abfb536bSRanjani Sridharan /* set up module info and message header */ 519abfb536bSRanjani Sridharan ret = sof_ipc4_widget_setup_msg(swidget, &ipc4_copier->msg); 520abfb536bSRanjani Sridharan if (ret) 521abfb536bSRanjani Sridharan goto free_copier_config; 522abfb536bSRanjani Sridharan 523abfb536bSRanjani Sridharan return 0; 524abfb536bSRanjani Sridharan 525abfb536bSRanjani Sridharan free_copier_config: 526abfb536bSRanjani Sridharan kfree(ipc4_copier->copier_config); 527dc4fc0aeSLibin Yang free_available_fmt: 528dc4fc0aeSLibin Yang sof_ipc4_free_audio_fmt(available_fmt); 529abfb536bSRanjani Sridharan free_copier: 530abfb536bSRanjani Sridharan kfree(ipc4_copier); 531b737fd8cSLibin Yang dai->private = NULL; 532b737fd8cSLibin Yang dai->scomp = NULL; 533abfb536bSRanjani Sridharan return ret; 534abfb536bSRanjani Sridharan } 535abfb536bSRanjani Sridharan 536abfb536bSRanjani Sridharan static void sof_ipc4_widget_free_comp_dai(struct snd_sof_widget *swidget) 537abfb536bSRanjani Sridharan { 538abfb536bSRanjani Sridharan struct sof_ipc4_available_audio_format *available_fmt; 539abfb536bSRanjani Sridharan struct snd_sof_dai *dai = swidget->private; 540abfb536bSRanjani Sridharan struct sof_ipc4_copier *ipc4_copier; 541abfb536bSRanjani Sridharan 542abfb536bSRanjani Sridharan if (!dai) 543abfb536bSRanjani Sridharan return; 544abfb536bSRanjani Sridharan 545b737fd8cSLibin Yang if (!dai->private) { 546b737fd8cSLibin Yang kfree(dai); 547b737fd8cSLibin Yang swidget->private = NULL; 548b737fd8cSLibin Yang return; 549b737fd8cSLibin Yang } 550b737fd8cSLibin Yang 551abfb536bSRanjani Sridharan ipc4_copier = dai->private; 552abfb536bSRanjani Sridharan available_fmt = &ipc4_copier->available_fmt; 553abfb536bSRanjani Sridharan 5547ab6b1e8SRanjani Sridharan kfree(available_fmt->output_pin_fmts); 555aa84ffb7SRanjani Sridharan if (ipc4_copier->dai_type != SOF_DAI_INTEL_SSP && 556aa84ffb7SRanjani Sridharan ipc4_copier->dai_type != SOF_DAI_INTEL_DMIC) 557abfb536bSRanjani Sridharan kfree(ipc4_copier->copier_config); 558abfb536bSRanjani Sridharan kfree(dai->private); 559abfb536bSRanjani Sridharan kfree(dai); 560abfb536bSRanjani Sridharan swidget->private = NULL; 561abfb536bSRanjani Sridharan } 562abfb536bSRanjani Sridharan 56390e89155SRanjani Sridharan static int sof_ipc4_widget_setup_comp_pipeline(struct snd_sof_widget *swidget) 56490e89155SRanjani Sridharan { 56590e89155SRanjani Sridharan struct snd_soc_component *scomp = swidget->scomp; 56690e89155SRanjani Sridharan struct sof_ipc4_pipeline *pipeline; 56790e89155SRanjani Sridharan int ret; 56890e89155SRanjani Sridharan 56990e89155SRanjani Sridharan pipeline = kzalloc(sizeof(*pipeline), GFP_KERNEL); 57090e89155SRanjani Sridharan if (!pipeline) 57190e89155SRanjani Sridharan return -ENOMEM; 57290e89155SRanjani Sridharan 57390e89155SRanjani Sridharan ret = sof_update_ipc_object(scomp, pipeline, SOF_SCHED_TOKENS, swidget->tuples, 57490e89155SRanjani Sridharan swidget->num_tuples, sizeof(*pipeline), 1); 57590e89155SRanjani Sridharan if (ret) { 57690e89155SRanjani Sridharan dev_err(scomp->dev, "parsing scheduler tokens failed\n"); 57790e89155SRanjani Sridharan goto err; 57890e89155SRanjani Sridharan } 57990e89155SRanjani Sridharan 58005ade472SPeter Ujfalusi swidget->core = pipeline->core_id; 58105ade472SPeter Ujfalusi 58290e89155SRanjani Sridharan /* parse one set of pipeline tokens */ 58390e89155SRanjani Sridharan ret = sof_update_ipc_object(scomp, swidget, SOF_PIPELINE_TOKENS, swidget->tuples, 58490e89155SRanjani Sridharan swidget->num_tuples, sizeof(*swidget), 1); 58590e89155SRanjani Sridharan if (ret) { 58690e89155SRanjani Sridharan dev_err(scomp->dev, "parsing pipeline tokens failed\n"); 58790e89155SRanjani Sridharan goto err; 58890e89155SRanjani Sridharan } 58990e89155SRanjani Sridharan 59090e89155SRanjani Sridharan /* TODO: Get priority from topology */ 59190e89155SRanjani Sridharan pipeline->priority = 0; 59290e89155SRanjani Sridharan 59305ade472SPeter Ujfalusi dev_dbg(scomp->dev, "pipeline '%s': id %d, pri %d, core_id %u, lp mode %d\n", 59490e89155SRanjani Sridharan swidget->widget->name, swidget->pipeline_id, 59505ade472SPeter Ujfalusi pipeline->priority, pipeline->core_id, pipeline->lp_mode); 59690e89155SRanjani Sridharan 59790e89155SRanjani Sridharan swidget->private = pipeline; 59890e89155SRanjani Sridharan 59990e89155SRanjani Sridharan pipeline->msg.primary = SOF_IPC4_GLB_PIPE_PRIORITY(pipeline->priority); 60090e89155SRanjani Sridharan pipeline->msg.primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_CREATE_PIPELINE); 60190e89155SRanjani Sridharan pipeline->msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); 60290e89155SRanjani Sridharan pipeline->msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG); 60390e89155SRanjani Sridharan 60490e89155SRanjani Sridharan pipeline->msg.extension = pipeline->lp_mode; 60505ade472SPeter Ujfalusi pipeline->msg.extension |= SOF_IPC4_GLB_PIPE_EXT_CORE_ID(pipeline->core_id); 60690e89155SRanjani Sridharan pipeline->state = SOF_IPC4_PIPE_UNINITIALIZED; 60790e89155SRanjani Sridharan 60890e89155SRanjani Sridharan return 0; 60990e89155SRanjani Sridharan err: 61090e89155SRanjani Sridharan kfree(pipeline); 61190e89155SRanjani Sridharan return ret; 61290e89155SRanjani Sridharan } 61390e89155SRanjani Sridharan 6144f838ab2SRanjani Sridharan static int sof_ipc4_widget_setup_comp_pga(struct snd_sof_widget *swidget) 6154f838ab2SRanjani Sridharan { 6164f838ab2SRanjani Sridharan struct snd_soc_component *scomp = swidget->scomp; 6174f838ab2SRanjani Sridharan struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 6184f838ab2SRanjani Sridharan struct sof_ipc4_fw_module *fw_module; 6194f838ab2SRanjani Sridharan struct snd_sof_control *scontrol; 6204f838ab2SRanjani Sridharan struct sof_ipc4_gain *gain; 6214f838ab2SRanjani Sridharan int ret; 6224f838ab2SRanjani Sridharan 6234f838ab2SRanjani Sridharan gain = kzalloc(sizeof(*gain), GFP_KERNEL); 6244f838ab2SRanjani Sridharan if (!gain) 6254f838ab2SRanjani Sridharan return -ENOMEM; 6264f838ab2SRanjani Sridharan 6274f838ab2SRanjani Sridharan swidget->private = gain; 6284f838ab2SRanjani Sridharan 6294f838ab2SRanjani Sridharan gain->data.channels = SOF_IPC4_GAIN_ALL_CHANNELS_MASK; 6304f838ab2SRanjani Sridharan gain->data.init_val = SOF_IPC4_VOL_ZERO_DB; 6314f838ab2SRanjani Sridharan 6328abc9ab9SRanjani Sridharan ret = sof_ipc4_get_audio_fmt(scomp, swidget, &gain->available_fmt, &gain->base_config); 6334f838ab2SRanjani Sridharan if (ret) 6344f838ab2SRanjani Sridharan goto err; 6354f838ab2SRanjani Sridharan 6364f838ab2SRanjani Sridharan ret = sof_update_ipc_object(scomp, &gain->data, SOF_GAIN_TOKENS, swidget->tuples, 6374f838ab2SRanjani Sridharan swidget->num_tuples, sizeof(gain->data), 1); 6384f838ab2SRanjani Sridharan if (ret) { 6394f838ab2SRanjani Sridharan dev_err(scomp->dev, "Parsing gain tokens failed\n"); 6404f838ab2SRanjani Sridharan goto err; 6414f838ab2SRanjani Sridharan } 6424f838ab2SRanjani Sridharan 6434f838ab2SRanjani Sridharan dev_dbg(scomp->dev, 6444f838ab2SRanjani Sridharan "pga widget %s: ramp type: %d, ramp duration %d, initial gain value: %#x, cpc %d\n", 645e45cd86cSRander Wang swidget->widget->name, gain->data.curve_type, gain->data.curve_duration_l, 6464f838ab2SRanjani Sridharan gain->data.init_val, gain->base_config.cpc); 6474f838ab2SRanjani Sridharan 6484f838ab2SRanjani Sridharan ret = sof_ipc4_widget_setup_msg(swidget, &gain->msg); 6494f838ab2SRanjani Sridharan if (ret) 6504f838ab2SRanjani Sridharan goto err; 6514f838ab2SRanjani Sridharan 6524f838ab2SRanjani Sridharan fw_module = swidget->module_info; 6534f838ab2SRanjani Sridharan 6544f838ab2SRanjani Sridharan /* update module ID for all kcontrols for this widget */ 6554f838ab2SRanjani Sridharan list_for_each_entry(scontrol, &sdev->kcontrol_list, list) 6564f838ab2SRanjani Sridharan if (scontrol->comp_id == swidget->comp_id) { 6574f838ab2SRanjani Sridharan struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; 6584f838ab2SRanjani Sridharan struct sof_ipc4_msg *msg = &cdata->msg; 6594f838ab2SRanjani Sridharan 6604f838ab2SRanjani Sridharan msg->primary |= fw_module->man4_module_entry.id; 6614f838ab2SRanjani Sridharan } 6624f838ab2SRanjani Sridharan 6634f838ab2SRanjani Sridharan return 0; 6644f838ab2SRanjani Sridharan err: 665dc4fc0aeSLibin Yang sof_ipc4_free_audio_fmt(&gain->available_fmt); 6664f838ab2SRanjani Sridharan kfree(gain); 667b737fd8cSLibin Yang swidget->private = NULL; 6684f838ab2SRanjani Sridharan return ret; 6694f838ab2SRanjani Sridharan } 6704f838ab2SRanjani Sridharan 671dc4fc0aeSLibin Yang static void sof_ipc4_widget_free_comp_pga(struct snd_sof_widget *swidget) 672dc4fc0aeSLibin Yang { 673dc4fc0aeSLibin Yang struct sof_ipc4_gain *gain = swidget->private; 674dc4fc0aeSLibin Yang 675dc4fc0aeSLibin Yang if (!gain) 676dc4fc0aeSLibin Yang return; 677dc4fc0aeSLibin Yang 678dc4fc0aeSLibin Yang sof_ipc4_free_audio_fmt(&gain->available_fmt); 679dc4fc0aeSLibin Yang kfree(swidget->private); 680dc4fc0aeSLibin Yang swidget->private = NULL; 681dc4fc0aeSLibin Yang } 682dc4fc0aeSLibin Yang 6834d4ba014SRanjani Sridharan static int sof_ipc4_widget_setup_comp_mixer(struct snd_sof_widget *swidget) 6844d4ba014SRanjani Sridharan { 6854d4ba014SRanjani Sridharan struct snd_soc_component *scomp = swidget->scomp; 6864d4ba014SRanjani Sridharan struct sof_ipc4_mixer *mixer; 6874d4ba014SRanjani Sridharan int ret; 6884d4ba014SRanjani Sridharan 6894d4ba014SRanjani Sridharan dev_dbg(scomp->dev, "Updating IPC structure for %s\n", swidget->widget->name); 6904d4ba014SRanjani Sridharan 6914d4ba014SRanjani Sridharan mixer = kzalloc(sizeof(*mixer), GFP_KERNEL); 6924d4ba014SRanjani Sridharan if (!mixer) 6934d4ba014SRanjani Sridharan return -ENOMEM; 6944d4ba014SRanjani Sridharan 6954d4ba014SRanjani Sridharan swidget->private = mixer; 6964d4ba014SRanjani Sridharan 697171d5cceSRanjani Sridharan ret = sof_ipc4_get_audio_fmt(scomp, swidget, &mixer->available_fmt, 6988abc9ab9SRanjani Sridharan &mixer->base_config); 6994d4ba014SRanjani Sridharan if (ret) 7004d4ba014SRanjani Sridharan goto err; 7014d4ba014SRanjani Sridharan 7024d4ba014SRanjani Sridharan ret = sof_ipc4_widget_setup_msg(swidget, &mixer->msg); 7034d4ba014SRanjani Sridharan if (ret) 7044d4ba014SRanjani Sridharan goto err; 7054d4ba014SRanjani Sridharan 7064d4ba014SRanjani Sridharan return 0; 7074d4ba014SRanjani Sridharan err: 708dc4fc0aeSLibin Yang sof_ipc4_free_audio_fmt(&mixer->available_fmt); 7094d4ba014SRanjani Sridharan kfree(mixer); 710b737fd8cSLibin Yang swidget->private = NULL; 7114d4ba014SRanjani Sridharan return ret; 7124d4ba014SRanjani Sridharan } 7134d4ba014SRanjani Sridharan 714b85f4fc4SRander Wang static int sof_ipc4_widget_setup_comp_src(struct snd_sof_widget *swidget) 715b85f4fc4SRander Wang { 716b85f4fc4SRander Wang struct snd_soc_component *scomp = swidget->scomp; 717b85f4fc4SRander Wang struct sof_ipc4_src *src; 718b85f4fc4SRander Wang int ret; 719b85f4fc4SRander Wang 720b85f4fc4SRander Wang dev_dbg(scomp->dev, "Updating IPC structure for %s\n", swidget->widget->name); 721b85f4fc4SRander Wang 722b85f4fc4SRander Wang src = kzalloc(sizeof(*src), GFP_KERNEL); 723b85f4fc4SRander Wang if (!src) 724b85f4fc4SRander Wang return -ENOMEM; 725b85f4fc4SRander Wang 726b85f4fc4SRander Wang swidget->private = src; 727b85f4fc4SRander Wang 7288abc9ab9SRanjani Sridharan ret = sof_ipc4_get_audio_fmt(scomp, swidget, &src->available_fmt, &src->base_config); 729b85f4fc4SRander Wang if (ret) 730b85f4fc4SRander Wang goto err; 731b85f4fc4SRander Wang 732b85f4fc4SRander Wang ret = sof_update_ipc_object(scomp, src, SOF_SRC_TOKENS, swidget->tuples, 733ecdb10dfSYang Yingliang swidget->num_tuples, sizeof(*src), 1); 734b85f4fc4SRander Wang if (ret) { 735b85f4fc4SRander Wang dev_err(scomp->dev, "Parsing SRC tokens failed\n"); 736b85f4fc4SRander Wang goto err; 737b85f4fc4SRander Wang } 738b85f4fc4SRander Wang 739b85f4fc4SRander Wang dev_dbg(scomp->dev, "SRC sink rate %d\n", src->sink_rate); 740b85f4fc4SRander Wang 741b85f4fc4SRander Wang ret = sof_ipc4_widget_setup_msg(swidget, &src->msg); 742b85f4fc4SRander Wang if (ret) 743b85f4fc4SRander Wang goto err; 744b85f4fc4SRander Wang 745b85f4fc4SRander Wang return 0; 746b85f4fc4SRander Wang err: 747b85f4fc4SRander Wang sof_ipc4_free_audio_fmt(&src->available_fmt); 748b85f4fc4SRander Wang kfree(src); 749b85f4fc4SRander Wang swidget->private = NULL; 750b85f4fc4SRander Wang return ret; 751b85f4fc4SRander Wang } 752b85f4fc4SRander Wang 753b85f4fc4SRander Wang static void sof_ipc4_widget_free_comp_src(struct snd_sof_widget *swidget) 754b85f4fc4SRander Wang { 755b85f4fc4SRander Wang struct sof_ipc4_src *src = swidget->private; 756b85f4fc4SRander Wang 757b85f4fc4SRander Wang if (!src) 758b85f4fc4SRander Wang return; 759b85f4fc4SRander Wang 760b85f4fc4SRander Wang sof_ipc4_free_audio_fmt(&src->available_fmt); 761b85f4fc4SRander Wang kfree(swidget->private); 762b85f4fc4SRander Wang swidget->private = NULL; 763b85f4fc4SRander Wang } 764b85f4fc4SRander Wang 765dc4fc0aeSLibin Yang static void sof_ipc4_widget_free_comp_mixer(struct snd_sof_widget *swidget) 766dc4fc0aeSLibin Yang { 767dc4fc0aeSLibin Yang struct sof_ipc4_mixer *mixer = swidget->private; 768dc4fc0aeSLibin Yang 769dc4fc0aeSLibin Yang if (!mixer) 770dc4fc0aeSLibin Yang return; 771dc4fc0aeSLibin Yang 772dc4fc0aeSLibin Yang sof_ipc4_free_audio_fmt(&mixer->available_fmt); 773dc4fc0aeSLibin Yang kfree(swidget->private); 774dc4fc0aeSLibin Yang swidget->private = NULL; 775dc4fc0aeSLibin Yang } 776dc4fc0aeSLibin Yang 777904c48c4SRanjani Sridharan static void 778904c48c4SRanjani Sridharan sof_ipc4_update_pipeline_mem_usage(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, 779904c48c4SRanjani Sridharan struct sof_ipc4_base_module_cfg *base_config) 780904c48c4SRanjani Sridharan { 781904c48c4SRanjani Sridharan struct sof_ipc4_fw_module *fw_module = swidget->module_info; 782904c48c4SRanjani Sridharan struct snd_sof_widget *pipe_widget; 783904c48c4SRanjani Sridharan struct sof_ipc4_pipeline *pipeline; 784904c48c4SRanjani Sridharan int task_mem, queue_mem; 785904c48c4SRanjani Sridharan int ibs, bss, total; 786904c48c4SRanjani Sridharan 787904c48c4SRanjani Sridharan ibs = base_config->ibs; 788904c48c4SRanjani Sridharan bss = base_config->is_pages; 789904c48c4SRanjani Sridharan 790904c48c4SRanjani Sridharan task_mem = SOF_IPC4_PIPELINE_OBJECT_SIZE; 791904c48c4SRanjani Sridharan task_mem += SOF_IPC4_MODULE_INSTANCE_LIST_ITEM_SIZE + bss; 792904c48c4SRanjani Sridharan 793904c48c4SRanjani Sridharan if (fw_module->man4_module_entry.type & SOF_IPC4_MODULE_LL) { 794904c48c4SRanjani Sridharan task_mem += SOF_IPC4_FW_ROUNDUP(SOF_IPC4_LL_TASK_OBJECT_SIZE); 795904c48c4SRanjani Sridharan task_mem += SOF_IPC4_FW_MAX_QUEUE_COUNT * SOF_IPC4_MODULE_INSTANCE_LIST_ITEM_SIZE; 796904c48c4SRanjani Sridharan task_mem += SOF_IPC4_LL_TASK_LIST_ITEM_SIZE; 797904c48c4SRanjani Sridharan } else { 798904c48c4SRanjani Sridharan task_mem += SOF_IPC4_FW_ROUNDUP(SOF_IPC4_DP_TASK_OBJECT_SIZE); 799904c48c4SRanjani Sridharan task_mem += SOF_IPC4_DP_TASK_LIST_SIZE; 800904c48c4SRanjani Sridharan } 801904c48c4SRanjani Sridharan 802904c48c4SRanjani Sridharan ibs = SOF_IPC4_FW_ROUNDUP(ibs); 803904c48c4SRanjani Sridharan queue_mem = SOF_IPC4_FW_MAX_QUEUE_COUNT * (SOF_IPC4_DATA_QUEUE_OBJECT_SIZE + ibs); 804904c48c4SRanjani Sridharan 805904c48c4SRanjani Sridharan total = SOF_IPC4_FW_PAGE(task_mem + queue_mem); 806904c48c4SRanjani Sridharan 8079c04363dSRanjani Sridharan pipe_widget = swidget->spipe->pipe_widget; 808904c48c4SRanjani Sridharan pipeline = pipe_widget->private; 809904c48c4SRanjani Sridharan pipeline->mem_usage += total; 810904c48c4SRanjani Sridharan } 811904c48c4SRanjani Sridharan 812904c48c4SRanjani Sridharan static int sof_ipc4_widget_assign_instance_id(struct snd_sof_dev *sdev, 813904c48c4SRanjani Sridharan struct snd_sof_widget *swidget) 814904c48c4SRanjani Sridharan { 815904c48c4SRanjani Sridharan struct sof_ipc4_fw_module *fw_module = swidget->module_info; 816904c48c4SRanjani Sridharan int max_instances = fw_module->man4_module_entry.instance_max_count; 817904c48c4SRanjani Sridharan 818904c48c4SRanjani Sridharan swidget->instance_id = ida_alloc_max(&fw_module->m_ida, max_instances, GFP_KERNEL); 819904c48c4SRanjani Sridharan if (swidget->instance_id < 0) { 820904c48c4SRanjani Sridharan dev_err(sdev->dev, "failed to assign instance id for widget %s", 821904c48c4SRanjani Sridharan swidget->widget->name); 822904c48c4SRanjani Sridharan return swidget->instance_id; 823904c48c4SRanjani Sridharan } 824904c48c4SRanjani Sridharan 825904c48c4SRanjani Sridharan return 0; 826904c48c4SRanjani Sridharan } 827904c48c4SRanjani Sridharan 828904c48c4SRanjani Sridharan static int sof_ipc4_init_audio_fmt(struct snd_sof_dev *sdev, 829904c48c4SRanjani Sridharan struct snd_sof_widget *swidget, 830904c48c4SRanjani Sridharan struct sof_ipc4_base_module_cfg *base_config, 831904c48c4SRanjani Sridharan struct snd_pcm_hw_params *params, 832e63a73f9SRanjani Sridharan struct sof_ipc4_available_audio_format *available_fmt) 833904c48c4SRanjani Sridharan { 8347ab6b1e8SRanjani Sridharan struct sof_ipc4_pin_format *pin_fmt = available_fmt->ref_audio_fmt; 835904c48c4SRanjani Sridharan u32 valid_bits; 836904c48c4SRanjani Sridharan u32 channels; 837904c48c4SRanjani Sridharan u32 rate; 838904c48c4SRanjani Sridharan int sample_valid_bits; 839904c48c4SRanjani Sridharan int i; 840904c48c4SRanjani Sridharan 8417ab6b1e8SRanjani Sridharan if (!pin_fmt) { 842904c48c4SRanjani Sridharan dev_err(sdev->dev, "no reference formats for %s\n", swidget->widget->name); 843904c48c4SRanjani Sridharan return -EINVAL; 844904c48c4SRanjani Sridharan } 845904c48c4SRanjani Sridharan 846904c48c4SRanjani Sridharan switch (params_format(params)) { 847904c48c4SRanjani Sridharan case SNDRV_PCM_FORMAT_S16_LE: 848904c48c4SRanjani Sridharan sample_valid_bits = 16; 849904c48c4SRanjani Sridharan break; 850904c48c4SRanjani Sridharan case SNDRV_PCM_FORMAT_S24_LE: 851904c48c4SRanjani Sridharan sample_valid_bits = 24; 852904c48c4SRanjani Sridharan break; 853904c48c4SRanjani Sridharan case SNDRV_PCM_FORMAT_S32_LE: 854904c48c4SRanjani Sridharan sample_valid_bits = 32; 855904c48c4SRanjani Sridharan break; 856904c48c4SRanjani Sridharan default: 857904c48c4SRanjani Sridharan dev_err(sdev->dev, "invalid pcm frame format %d\n", params_format(params)); 858904c48c4SRanjani Sridharan return -EINVAL; 859904c48c4SRanjani Sridharan } 860904c48c4SRanjani Sridharan 861904c48c4SRanjani Sridharan if (!available_fmt->audio_fmt_num) { 862904c48c4SRanjani Sridharan dev_err(sdev->dev, "no formats available for %s\n", swidget->widget->name); 863904c48c4SRanjani Sridharan return -EINVAL; 864904c48c4SRanjani Sridharan } 865904c48c4SRanjani Sridharan 866904c48c4SRanjani Sridharan /* 867904c48c4SRanjani Sridharan * Search supported audio formats to match rate, channels ,and 868904c48c4SRanjani Sridharan * sample_valid_bytes from runtime params 869904c48c4SRanjani Sridharan */ 8707ab6b1e8SRanjani Sridharan for (i = 0; i < available_fmt->audio_fmt_num; i++) { 8717ab6b1e8SRanjani Sridharan struct sof_ipc4_audio_format *fmt = &pin_fmt[i].audio_fmt; 8727ab6b1e8SRanjani Sridharan 873904c48c4SRanjani Sridharan rate = fmt->sampling_frequency; 874904c48c4SRanjani Sridharan channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg); 875904c48c4SRanjani Sridharan valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg); 876904c48c4SRanjani Sridharan if (params_rate(params) == rate && params_channels(params) == channels && 877904c48c4SRanjani Sridharan sample_valid_bits == valid_bits) { 878e63a73f9SRanjani Sridharan dev_dbg(sdev->dev, "matched audio format index for %uHz, %ubit, %u channels: %d\n", 8793809264bSPierre-Louis Bossart rate, valid_bits, channels, i); 880904c48c4SRanjani Sridharan break; 881904c48c4SRanjani Sridharan } 882904c48c4SRanjani Sridharan } 883904c48c4SRanjani Sridharan 884904c48c4SRanjani Sridharan if (i == available_fmt->audio_fmt_num) { 885904c48c4SRanjani Sridharan dev_err(sdev->dev, "%s: Unsupported audio format: %uHz, %ubit, %u channels\n", 886904c48c4SRanjani Sridharan __func__, params_rate(params), sample_valid_bits, params_channels(params)); 887904c48c4SRanjani Sridharan return -EINVAL; 888904c48c4SRanjani Sridharan } 889904c48c4SRanjani Sridharan 890e63a73f9SRanjani Sridharan /* copy input format */ 8917ab6b1e8SRanjani Sridharan memcpy(&base_config->audio_fmt, &available_fmt->input_pin_fmts[i].audio_fmt, 892*9c560549SRanjani Sridharan sizeof(struct sof_ipc4_audio_format)); 893e63a73f9SRanjani Sridharan 894e63a73f9SRanjani Sridharan /* set base_cfg ibs/obs */ 8957ab6b1e8SRanjani Sridharan base_config->ibs = available_fmt->input_pin_fmts[i].buffer_size; 8967ab6b1e8SRanjani Sridharan base_config->obs = available_fmt->output_pin_fmts[i].buffer_size; 897e63a73f9SRanjani Sridharan 898904c48c4SRanjani Sridharan dev_dbg(sdev->dev, "Init input audio formats for %s\n", swidget->widget->name); 8997ab6b1e8SRanjani Sridharan sof_ipc4_dbg_audio_format(sdev->dev, &available_fmt->input_pin_fmts[i], 1); 9007ab6b1e8SRanjani Sridharan 901904c48c4SRanjani Sridharan /* Return the index of the matched format */ 902904c48c4SRanjani Sridharan return i; 903904c48c4SRanjani Sridharan } 904904c48c4SRanjani Sridharan 905904c48c4SRanjani Sridharan static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget) 906904c48c4SRanjani Sridharan { 907904c48c4SRanjani Sridharan struct sof_ipc4_copier *ipc4_copier = NULL; 908904c48c4SRanjani Sridharan struct snd_sof_widget *pipe_widget; 909904c48c4SRanjani Sridharan struct sof_ipc4_pipeline *pipeline; 910904c48c4SRanjani Sridharan 911904c48c4SRanjani Sridharan /* reset pipeline memory usage */ 9129c04363dSRanjani Sridharan pipe_widget = swidget->spipe->pipe_widget; 913904c48c4SRanjani Sridharan pipeline = pipe_widget->private; 914904c48c4SRanjani Sridharan pipeline->mem_usage = 0; 915904c48c4SRanjani Sridharan 9167d573425SBard Liao if (WIDGET_IS_AIF(swidget->id) || swidget->id == snd_soc_dapm_buffer) { 917904c48c4SRanjani Sridharan ipc4_copier = swidget->private; 918acf52594SRanjani Sridharan } else if (WIDGET_IS_DAI(swidget->id)) { 919acf52594SRanjani Sridharan struct snd_sof_dai *dai = swidget->private; 920acf52594SRanjani Sridharan 921acf52594SRanjani Sridharan ipc4_copier = dai->private; 922a150345aSBard Liao if (ipc4_copier->dai_type == SOF_DAI_INTEL_ALH) { 923b66bfc3aSRanjani Sridharan struct sof_ipc4_copier_data *copier_data = &ipc4_copier->data; 924a150345aSBard Liao struct sof_ipc4_alh_configuration_blob *blob; 925a150345aSBard Liao unsigned int group_id; 926a150345aSBard Liao 927a150345aSBard Liao blob = (struct sof_ipc4_alh_configuration_blob *)ipc4_copier->copier_config; 928a150345aSBard Liao if (blob->alh_cfg.count > 1) { 929a150345aSBard Liao group_id = SOF_IPC4_NODE_INDEX(ipc4_copier->data.gtw_cfg.node_id) - 930a150345aSBard Liao ALH_MULTI_GTW_BASE; 931a150345aSBard Liao ida_free(&alh_group_ida, group_id); 932a150345aSBard Liao } 933b66bfc3aSRanjani Sridharan 934b66bfc3aSRanjani Sridharan /* clear the node ID */ 935b66bfc3aSRanjani Sridharan copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK; 936a150345aSBard Liao } 937acf52594SRanjani Sridharan } 938904c48c4SRanjani Sridharan 939904c48c4SRanjani Sridharan if (ipc4_copier) { 940904c48c4SRanjani Sridharan kfree(ipc4_copier->ipc_config_data); 941904c48c4SRanjani Sridharan ipc4_copier->ipc_config_data = NULL; 942904c48c4SRanjani Sridharan ipc4_copier->ipc_config_size = 0; 943904c48c4SRanjani Sridharan } 944904c48c4SRanjani Sridharan } 945904c48c4SRanjani Sridharan 946aa84ffb7SRanjani Sridharan #if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_SND_INTEL_NHLT) 947aa84ffb7SRanjani Sridharan static int snd_sof_get_hw_config_params(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, 948aa84ffb7SRanjani Sridharan int *sample_rate, int *channel_count, int *bit_depth) 949aa84ffb7SRanjani Sridharan { 950aa84ffb7SRanjani Sridharan struct snd_soc_tplg_hw_config *hw_config; 951aa84ffb7SRanjani Sridharan struct snd_sof_dai_link *slink; 952aa84ffb7SRanjani Sridharan bool dai_link_found = false; 953aa84ffb7SRanjani Sridharan bool hw_cfg_found = false; 954aa84ffb7SRanjani Sridharan int i; 955aa84ffb7SRanjani Sridharan 956aa84ffb7SRanjani Sridharan /* get current hw_config from link */ 957aa84ffb7SRanjani Sridharan list_for_each_entry(slink, &sdev->dai_link_list, list) { 958aa84ffb7SRanjani Sridharan if (!strcmp(slink->link->name, dai->name)) { 959aa84ffb7SRanjani Sridharan dai_link_found = true; 960aa84ffb7SRanjani Sridharan break; 961aa84ffb7SRanjani Sridharan } 962aa84ffb7SRanjani Sridharan } 963aa84ffb7SRanjani Sridharan 964aa84ffb7SRanjani Sridharan if (!dai_link_found) { 965aa84ffb7SRanjani Sridharan dev_err(sdev->dev, "%s: no DAI link found for DAI %s\n", __func__, dai->name); 966aa84ffb7SRanjani Sridharan return -EINVAL; 967aa84ffb7SRanjani Sridharan } 968aa84ffb7SRanjani Sridharan 969aa84ffb7SRanjani Sridharan for (i = 0; i < slink->num_hw_configs; i++) { 970aa84ffb7SRanjani Sridharan hw_config = &slink->hw_configs[i]; 971aa84ffb7SRanjani Sridharan if (dai->current_config == le32_to_cpu(hw_config->id)) { 972aa84ffb7SRanjani Sridharan hw_cfg_found = true; 973aa84ffb7SRanjani Sridharan break; 974aa84ffb7SRanjani Sridharan } 975aa84ffb7SRanjani Sridharan } 976aa84ffb7SRanjani Sridharan 977aa84ffb7SRanjani Sridharan if (!hw_cfg_found) { 978aa84ffb7SRanjani Sridharan dev_err(sdev->dev, "%s: no matching hw_config found for DAI %s\n", __func__, 979aa84ffb7SRanjani Sridharan dai->name); 980aa84ffb7SRanjani Sridharan return -EINVAL; 981aa84ffb7SRanjani Sridharan } 982aa84ffb7SRanjani Sridharan 983aa84ffb7SRanjani Sridharan *bit_depth = le32_to_cpu(hw_config->tdm_slot_width); 984aa84ffb7SRanjani Sridharan *channel_count = le32_to_cpu(hw_config->tdm_slots); 985aa84ffb7SRanjani Sridharan *sample_rate = le32_to_cpu(hw_config->fsync_rate); 986aa84ffb7SRanjani Sridharan 9873809264bSPierre-Louis Bossart dev_dbg(sdev->dev, "sample rate: %d sample width: %d channels: %d\n", 9883809264bSPierre-Louis Bossart *sample_rate, *bit_depth, *channel_count); 989aa84ffb7SRanjani Sridharan 990aa84ffb7SRanjani Sridharan return 0; 991aa84ffb7SRanjani Sridharan } 992aa84ffb7SRanjani Sridharan 993aa84ffb7SRanjani Sridharan static int snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, 994aa84ffb7SRanjani Sridharan struct snd_pcm_hw_params *params, u32 dai_index, 995aa84ffb7SRanjani Sridharan u32 linktype, u8 dir, u32 **dst, u32 *len) 996aa84ffb7SRanjani Sridharan { 997aa84ffb7SRanjani Sridharan struct sof_ipc4_fw_data *ipc4_data = sdev->private; 998aa84ffb7SRanjani Sridharan struct nhlt_specific_cfg *cfg; 999aa84ffb7SRanjani Sridharan int sample_rate, channel_count; 1000aa84ffb7SRanjani Sridharan int bit_depth, ret; 1001aa84ffb7SRanjani Sridharan u32 nhlt_type; 1002aa84ffb7SRanjani Sridharan 1003aa84ffb7SRanjani Sridharan /* convert to NHLT type */ 1004aa84ffb7SRanjani Sridharan switch (linktype) { 1005aa84ffb7SRanjani Sridharan case SOF_DAI_INTEL_DMIC: 1006aa84ffb7SRanjani Sridharan nhlt_type = NHLT_LINK_DMIC; 1007aa84ffb7SRanjani Sridharan bit_depth = params_width(params); 1008aa84ffb7SRanjani Sridharan channel_count = params_channels(params); 1009aa84ffb7SRanjani Sridharan sample_rate = params_rate(params); 1010aa84ffb7SRanjani Sridharan break; 1011aa84ffb7SRanjani Sridharan case SOF_DAI_INTEL_SSP: 1012aa84ffb7SRanjani Sridharan nhlt_type = NHLT_LINK_SSP; 1013aa84ffb7SRanjani Sridharan ret = snd_sof_get_hw_config_params(sdev, dai, &sample_rate, &channel_count, 1014aa84ffb7SRanjani Sridharan &bit_depth); 1015aa84ffb7SRanjani Sridharan if (ret < 0) 1016aa84ffb7SRanjani Sridharan return ret; 1017aa84ffb7SRanjani Sridharan break; 1018aa84ffb7SRanjani Sridharan default: 1019aa84ffb7SRanjani Sridharan return 0; 1020aa84ffb7SRanjani Sridharan } 1021aa84ffb7SRanjani Sridharan 10223809264bSPierre-Louis Bossart dev_dbg(sdev->dev, "dai index %d nhlt type %d direction %d\n", 10233809264bSPierre-Louis Bossart dai_index, nhlt_type, dir); 1024aa84ffb7SRanjani Sridharan 1025aa84ffb7SRanjani Sridharan /* find NHLT blob with matching params */ 1026aa84ffb7SRanjani Sridharan cfg = intel_nhlt_get_endpoint_blob(sdev->dev, ipc4_data->nhlt, dai_index, nhlt_type, 1027aa84ffb7SRanjani Sridharan bit_depth, bit_depth, channel_count, sample_rate, 1028aa84ffb7SRanjani Sridharan dir, 0); 1029aa84ffb7SRanjani Sridharan 1030aa84ffb7SRanjani Sridharan if (!cfg) { 1031aa84ffb7SRanjani Sridharan dev_err(sdev->dev, 1032aa84ffb7SRanjani Sridharan "no matching blob for sample rate: %d sample width: %d channels: %d\n", 1033aa84ffb7SRanjani Sridharan sample_rate, bit_depth, channel_count); 1034aa84ffb7SRanjani Sridharan return -EINVAL; 1035aa84ffb7SRanjani Sridharan } 1036aa84ffb7SRanjani Sridharan 1037aa84ffb7SRanjani Sridharan /* config length should be in dwords */ 1038aa84ffb7SRanjani Sridharan *len = cfg->size >> 2; 1039aa84ffb7SRanjani Sridharan *dst = (u32 *)cfg->caps; 1040aa84ffb7SRanjani Sridharan 1041aa84ffb7SRanjani Sridharan return 0; 1042aa84ffb7SRanjani Sridharan } 1043aa84ffb7SRanjani Sridharan #else 1044aa84ffb7SRanjani Sridharan static int snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, 1045aa84ffb7SRanjani Sridharan struct snd_pcm_hw_params *params, u32 dai_index, 1046aa84ffb7SRanjani Sridharan u32 linktype, u8 dir, u32 **dst, u32 *len) 1047aa84ffb7SRanjani Sridharan { 1048aa84ffb7SRanjani Sridharan return 0; 1049aa84ffb7SRanjani Sridharan } 1050aa84ffb7SRanjani Sridharan #endif 1051aa84ffb7SRanjani Sridharan 1052904c48c4SRanjani Sridharan static int 1053904c48c4SRanjani Sridharan sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, 1054904c48c4SRanjani Sridharan struct snd_pcm_hw_params *fe_params, 1055904c48c4SRanjani Sridharan struct snd_sof_platform_stream_params *platform_params, 1056904c48c4SRanjani Sridharan struct snd_pcm_hw_params *pipeline_params, int dir) 1057904c48c4SRanjani Sridharan { 1058904c48c4SRanjani Sridharan struct sof_ipc4_available_audio_format *available_fmt; 1059904c48c4SRanjani Sridharan struct snd_soc_component *scomp = swidget->scomp; 1060904c48c4SRanjani Sridharan struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 1061904c48c4SRanjani Sridharan struct sof_ipc4_copier_data *copier_data; 1062904c48c4SRanjani Sridharan struct snd_pcm_hw_params *ref_params; 1063904c48c4SRanjani Sridharan struct sof_ipc4_copier *ipc4_copier; 1064a150345aSBard Liao struct snd_sof_dai *dai; 1065904c48c4SRanjani Sridharan struct snd_mask *fmt; 1066904c48c4SRanjani Sridharan int out_sample_valid_bits; 1067904c48c4SRanjani Sridharan void **ipc_config_data; 1068904c48c4SRanjani Sridharan int *ipc_config_size; 1069904c48c4SRanjani Sridharan u32 **data; 1070904c48c4SRanjani Sridharan int ipc_size, ret; 1071594c1bb9SRanjani Sridharan u32 deep_buffer_dma_ms = 0; 1072904c48c4SRanjani Sridharan 10733809264bSPierre-Louis Bossart dev_dbg(sdev->dev, "copier %s, type %d", swidget->widget->name, swidget->id); 1074904c48c4SRanjani Sridharan 1075904c48c4SRanjani Sridharan switch (swidget->id) { 1076904c48c4SRanjani Sridharan case snd_soc_dapm_aif_in: 1077904c48c4SRanjani Sridharan case snd_soc_dapm_aif_out: 1078904c48c4SRanjani Sridharan { 1079904c48c4SRanjani Sridharan struct sof_ipc4_gtw_attributes *gtw_attr; 1080904c48c4SRanjani Sridharan struct snd_sof_widget *pipe_widget; 1081904c48c4SRanjani Sridharan struct sof_ipc4_pipeline *pipeline; 1082904c48c4SRanjani Sridharan 1083594c1bb9SRanjani Sridharan /* parse the deep buffer dma size */ 1084594c1bb9SRanjani Sridharan ret = sof_update_ipc_object(scomp, &deep_buffer_dma_ms, 1085594c1bb9SRanjani Sridharan SOF_COPIER_DEEP_BUFFER_TOKENS, swidget->tuples, 1086594c1bb9SRanjani Sridharan swidget->num_tuples, sizeof(u32), 1); 1087594c1bb9SRanjani Sridharan if (ret) { 1088594c1bb9SRanjani Sridharan dev_err(scomp->dev, "Failed to parse deep buffer dma size for %s\n", 1089594c1bb9SRanjani Sridharan swidget->widget->name); 1090594c1bb9SRanjani Sridharan return ret; 1091594c1bb9SRanjani Sridharan } 1092594c1bb9SRanjani Sridharan 10939c04363dSRanjani Sridharan pipe_widget = swidget->spipe->pipe_widget; 1094904c48c4SRanjani Sridharan pipeline = pipe_widget->private; 1095904c48c4SRanjani Sridharan ipc4_copier = (struct sof_ipc4_copier *)swidget->private; 1096904c48c4SRanjani Sridharan gtw_attr = ipc4_copier->gtw_attr; 1097904c48c4SRanjani Sridharan copier_data = &ipc4_copier->data; 1098904c48c4SRanjani Sridharan available_fmt = &ipc4_copier->available_fmt; 1099904c48c4SRanjani Sridharan 1100904c48c4SRanjani Sridharan /* 11017ab6b1e8SRanjani Sridharan * Use the input_pin_fmts to match pcm params for playback and the output_pin_fmts 1102e63a73f9SRanjani Sridharan * for capture. 1103904c48c4SRanjani Sridharan */ 1104e63a73f9SRanjani Sridharan if (dir == SNDRV_PCM_STREAM_PLAYBACK) 11057ab6b1e8SRanjani Sridharan available_fmt->ref_audio_fmt = available_fmt->input_pin_fmts; 1106e63a73f9SRanjani Sridharan else 11077ab6b1e8SRanjani Sridharan available_fmt->ref_audio_fmt = available_fmt->output_pin_fmts; 1108e63a73f9SRanjani Sridharan 1109904c48c4SRanjani Sridharan copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK; 1110904c48c4SRanjani Sridharan copier_data->gtw_cfg.node_id |= 1111904c48c4SRanjani Sridharan SOF_IPC4_NODE_INDEX(platform_params->stream_tag - 1); 1112904c48c4SRanjani Sridharan 1113904c48c4SRanjani Sridharan /* set gateway attributes */ 1114904c48c4SRanjani Sridharan gtw_attr->lp_buffer_alloc = pipeline->lp_mode; 1115904c48c4SRanjani Sridharan ref_params = fe_params; 1116904c48c4SRanjani Sridharan break; 1117904c48c4SRanjani Sridharan } 1118acf52594SRanjani Sridharan case snd_soc_dapm_dai_in: 1119acf52594SRanjani Sridharan case snd_soc_dapm_dai_out: 1120acf52594SRanjani Sridharan { 1121a150345aSBard Liao dai = swidget->private; 1122acf52594SRanjani Sridharan 1123acf52594SRanjani Sridharan ipc4_copier = (struct sof_ipc4_copier *)dai->private; 1124acf52594SRanjani Sridharan copier_data = &ipc4_copier->data; 1125acf52594SRanjani Sridharan available_fmt = &ipc4_copier->available_fmt; 1126acf52594SRanjani Sridharan if (dir == SNDRV_PCM_STREAM_CAPTURE) { 11277ab6b1e8SRanjani Sridharan available_fmt->ref_audio_fmt = available_fmt->output_pin_fmts; 1128acf52594SRanjani Sridharan 1129acf52594SRanjani Sridharan /* 1130acf52594SRanjani Sridharan * modify the input params for the dai copier as it only supports 1131acf52594SRanjani Sridharan * 32-bit always 1132acf52594SRanjani Sridharan */ 1133acf52594SRanjani Sridharan fmt = hw_param_mask(pipeline_params, SNDRV_PCM_HW_PARAM_FORMAT); 1134acf52594SRanjani Sridharan snd_mask_none(fmt); 1135acf52594SRanjani Sridharan snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE); 1136acf52594SRanjani Sridharan } else { 11377ab6b1e8SRanjani Sridharan available_fmt->ref_audio_fmt = available_fmt->input_pin_fmts; 1138acf52594SRanjani Sridharan } 1139acf52594SRanjani Sridharan 1140acf52594SRanjani Sridharan ref_params = pipeline_params; 1141acf52594SRanjani Sridharan 1142aa84ffb7SRanjani Sridharan ret = snd_sof_get_nhlt_endpoint_data(sdev, dai, fe_params, ipc4_copier->dai_index, 1143aa84ffb7SRanjani Sridharan ipc4_copier->dai_type, dir, 1144aa84ffb7SRanjani Sridharan &ipc4_copier->copier_config, 1145aa84ffb7SRanjani Sridharan &copier_data->gtw_cfg.config_length); 1146aa84ffb7SRanjani Sridharan if (ret < 0) 1147aa84ffb7SRanjani Sridharan return ret; 1148aa84ffb7SRanjani Sridharan 1149acf52594SRanjani Sridharan break; 1150acf52594SRanjani Sridharan } 11517d573425SBard Liao case snd_soc_dapm_buffer: 11527d573425SBard Liao { 11537d573425SBard Liao ipc4_copier = (struct sof_ipc4_copier *)swidget->private; 11547d573425SBard Liao copier_data = &ipc4_copier->data; 11557d573425SBard Liao available_fmt = &ipc4_copier->available_fmt; 11567d573425SBard Liao 1157e63a73f9SRanjani Sridharan /* Use the input formats as the reference to match pcm params */ 11587ab6b1e8SRanjani Sridharan available_fmt->ref_audio_fmt = available_fmt->input_pin_fmts; 11597d573425SBard Liao ref_params = pipeline_params; 11607d573425SBard Liao 11617d573425SBard Liao break; 11627d573425SBard Liao } 1163904c48c4SRanjani Sridharan default: 1164904c48c4SRanjani Sridharan dev_err(sdev->dev, "unsupported type %d for copier %s", 1165904c48c4SRanjani Sridharan swidget->id, swidget->widget->name); 1166904c48c4SRanjani Sridharan return -EINVAL; 1167904c48c4SRanjani Sridharan } 1168904c48c4SRanjani Sridharan 1169904c48c4SRanjani Sridharan /* set input and output audio formats */ 1170*9c560549SRanjani Sridharan ret = sof_ipc4_init_audio_fmt(sdev, swidget, &copier_data->base_config, ref_params, 1171e63a73f9SRanjani Sridharan available_fmt); 1172904c48c4SRanjani Sridharan if (ret < 0) 1173904c48c4SRanjani Sridharan return ret; 1174904c48c4SRanjani Sridharan 1175*9c560549SRanjani Sridharan /* 1176*9c560549SRanjani Sridharan * Set the output format. Current topology defines pin 0 input and output formats in pairs. 1177*9c560549SRanjani Sridharan * This assumes that the pin 0 formats are defined before all other pins. 1178*9c560549SRanjani Sridharan * So pick the output audio format with the same index as the chosen 1179*9c560549SRanjani Sridharan * input format. This logic will need to be updated when the format definitions 1180*9c560549SRanjani Sridharan * in topology change. 1181*9c560549SRanjani Sridharan */ 1182*9c560549SRanjani Sridharan memcpy(&copier_data->out_format, &available_fmt->output_pin_fmts[ret].audio_fmt, 1183*9c560549SRanjani Sridharan sizeof(struct sof_ipc4_audio_format)); 1184*9c560549SRanjani Sridharan dev_dbg(sdev->dev, "Output audio format for %s\n", swidget->widget->name); 1185*9c560549SRanjani Sridharan sof_ipc4_dbg_audio_format(sdev->dev, &available_fmt->output_pin_fmts[ret], 1); 1186*9c560549SRanjani Sridharan 1187a45a4d43SBard Liao switch (swidget->id) { 1188a45a4d43SBard Liao case snd_soc_dapm_dai_in: 1189a45a4d43SBard Liao case snd_soc_dapm_dai_out: 1190a45a4d43SBard Liao { 1191a45a4d43SBard Liao /* 1192a45a4d43SBard Liao * Only SOF_DAI_INTEL_ALH needs copier_data to set blob. 1193a45a4d43SBard Liao * That's why only ALH dai's blob is set after sof_ipc4_init_audio_fmt 1194a45a4d43SBard Liao */ 1195a45a4d43SBard Liao if (ipc4_copier->dai_type == SOF_DAI_INTEL_ALH) { 1196a45a4d43SBard Liao struct sof_ipc4_alh_configuration_blob *blob; 1197a150345aSBard Liao struct sof_ipc4_copier_data *alh_data; 1198a150345aSBard Liao struct sof_ipc4_copier *alh_copier; 1199a150345aSBard Liao struct snd_sof_widget *w; 12000390a102SBard Liao u32 ch_count = 0; 1201a150345aSBard Liao u32 ch_mask = 0; 1202a45a4d43SBard Liao u32 ch_map; 12030390a102SBard Liao u32 step; 12040390a102SBard Liao u32 mask; 1205a45a4d43SBard Liao int i; 1206a45a4d43SBard Liao 1207a45a4d43SBard Liao blob = (struct sof_ipc4_alh_configuration_blob *)ipc4_copier->copier_config; 1208a150345aSBard Liao 1209a45a4d43SBard Liao blob->gw_attr.lp_buffer_alloc = 0; 1210a45a4d43SBard Liao 1211a45a4d43SBard Liao /* Get channel_mask from ch_map */ 1212a45a4d43SBard Liao ch_map = copier_data->base_config.audio_fmt.ch_map; 1213a45a4d43SBard Liao for (i = 0; ch_map; i++) { 12140390a102SBard Liao if ((ch_map & 0xf) != 0xf) { 1215a150345aSBard Liao ch_mask |= BIT(i); 12160390a102SBard Liao ch_count++; 12170390a102SBard Liao } 1218a45a4d43SBard Liao ch_map >>= 4; 1219a45a4d43SBard Liao } 1220a150345aSBard Liao 12210390a102SBard Liao step = ch_count / blob->alh_cfg.count; 12220390a102SBard Liao mask = GENMASK(step - 1, 0); 1223a150345aSBard Liao /* 1224a150345aSBard Liao * Set each gtw_cfg.node_id to blob->alh_cfg.mapping[] 1225a150345aSBard Liao * for all widgets with the same stream name 1226a150345aSBard Liao */ 1227a150345aSBard Liao i = 0; 1228a150345aSBard Liao list_for_each_entry(w, &sdev->widget_list, list) { 1229a150345aSBard Liao if (w->widget->sname && 1230a150345aSBard Liao strcmp(w->widget->sname, swidget->widget->sname)) 1231a150345aSBard Liao continue; 1232a150345aSBard Liao 1233a150345aSBard Liao dai = w->private; 1234a150345aSBard Liao alh_copier = (struct sof_ipc4_copier *)dai->private; 1235a150345aSBard Liao alh_data = &alh_copier->data; 1236a150345aSBard Liao blob->alh_cfg.mapping[i].alh_id = alh_data->gtw_cfg.node_id; 12370390a102SBard Liao /* 12380390a102SBard Liao * Set the same channel mask for playback as the audio data is 12390390a102SBard Liao * duplicated for all speakers. For capture, split the channels 12400390a102SBard Liao * among the aggregated DAIs. For example, with 4 channels on 2 12410390a102SBard Liao * aggregated DAIs, the channel_mask should be 0x3 and 0xc for the 12420390a102SBard Liao * two DAI's. 12430390a102SBard Liao * The channel masks used depend on the cpu_dais used in the 12440390a102SBard Liao * dailink at the machine driver level, which actually comes from 12450390a102SBard Liao * the tables in soc_acpi files depending on the _ADR and devID 12460390a102SBard Liao * registers for each codec. 12470390a102SBard Liao */ 12480390a102SBard Liao if (w->id == snd_soc_dapm_dai_in) 1249a150345aSBard Liao blob->alh_cfg.mapping[i].channel_mask = ch_mask; 12500390a102SBard Liao else 12510390a102SBard Liao blob->alh_cfg.mapping[i].channel_mask = mask << (step * i); 12520390a102SBard Liao 1253a150345aSBard Liao i++; 1254a150345aSBard Liao } 1255a150345aSBard Liao if (blob->alh_cfg.count > 1) { 1256a150345aSBard Liao int group_id; 1257a150345aSBard Liao 12584ee6fc27SBard Liao group_id = ida_alloc_max(&alh_group_ida, ALH_MULTI_GTW_COUNT - 1, 1259a150345aSBard Liao GFP_KERNEL); 1260a150345aSBard Liao 1261a150345aSBard Liao if (group_id < 0) 1262a150345aSBard Liao return group_id; 1263a150345aSBard Liao 1264a150345aSBard Liao /* add multi-gateway base */ 1265a150345aSBard Liao group_id += ALH_MULTI_GTW_BASE; 1266a150345aSBard Liao copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK; 1267a150345aSBard Liao copier_data->gtw_cfg.node_id |= SOF_IPC4_NODE_INDEX(group_id); 1268a150345aSBard Liao } 1269a45a4d43SBard Liao } 1270a45a4d43SBard Liao } 1271a45a4d43SBard Liao } 1272a45a4d43SBard Liao 1273904c48c4SRanjani Sridharan /* modify the input params for the next widget */ 1274904c48c4SRanjani Sridharan fmt = hw_param_mask(pipeline_params, SNDRV_PCM_HW_PARAM_FORMAT); 1275904c48c4SRanjani Sridharan out_sample_valid_bits = 1276904c48c4SRanjani Sridharan SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(copier_data->out_format.fmt_cfg); 1277904c48c4SRanjani Sridharan snd_mask_none(fmt); 1278904c48c4SRanjani Sridharan switch (out_sample_valid_bits) { 1279904c48c4SRanjani Sridharan case 16: 1280904c48c4SRanjani Sridharan snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE); 1281904c48c4SRanjani Sridharan break; 1282904c48c4SRanjani Sridharan case 24: 1283904c48c4SRanjani Sridharan snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); 1284904c48c4SRanjani Sridharan break; 1285904c48c4SRanjani Sridharan case 32: 1286904c48c4SRanjani Sridharan snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE); 1287904c48c4SRanjani Sridharan break; 1288904c48c4SRanjani Sridharan default: 1289904c48c4SRanjani Sridharan dev_err(sdev->dev, "invalid sample frame format %d\n", 1290904c48c4SRanjani Sridharan params_format(pipeline_params)); 1291904c48c4SRanjani Sridharan return -EINVAL; 1292904c48c4SRanjani Sridharan } 1293904c48c4SRanjani Sridharan 1294594c1bb9SRanjani Sridharan /* 1295594c1bb9SRanjani Sridharan * Set the gateway dma_buffer_size to 2ms buffer size to meet the FW expectation. In the 1296594c1bb9SRanjani Sridharan * deep buffer case, set the dma_buffer_size depending on the deep_buffer_dma_ms set 1297594c1bb9SRanjani Sridharan * in topology. 1298594c1bb9SRanjani Sridharan */ 1299594c1bb9SRanjani Sridharan switch (swidget->id) { 1300594c1bb9SRanjani Sridharan case snd_soc_dapm_dai_in: 1301594c1bb9SRanjani Sridharan copier_data->gtw_cfg.dma_buffer_size = 1302594c1bb9SRanjani Sridharan SOF_IPC4_MIN_DMA_BUFFER_SIZE * copier_data->base_config.ibs; 1303594c1bb9SRanjani Sridharan break; 1304594c1bb9SRanjani Sridharan case snd_soc_dapm_aif_in: 1305594c1bb9SRanjani Sridharan copier_data->gtw_cfg.dma_buffer_size = 1306594c1bb9SRanjani Sridharan max((u32)SOF_IPC4_MIN_DMA_BUFFER_SIZE, deep_buffer_dma_ms) * 1307594c1bb9SRanjani Sridharan copier_data->base_config.ibs; 1308594c1bb9SRanjani Sridharan break; 1309594c1bb9SRanjani Sridharan case snd_soc_dapm_dai_out: 1310594c1bb9SRanjani Sridharan case snd_soc_dapm_aif_out: 1311594c1bb9SRanjani Sridharan copier_data->gtw_cfg.dma_buffer_size = 1312594c1bb9SRanjani Sridharan SOF_IPC4_MIN_DMA_BUFFER_SIZE * copier_data->base_config.obs; 1313594c1bb9SRanjani Sridharan break; 1314594c1bb9SRanjani Sridharan default: 1315594c1bb9SRanjani Sridharan break; 1316594c1bb9SRanjani Sridharan } 1317904c48c4SRanjani Sridharan 1318904c48c4SRanjani Sridharan data = &ipc4_copier->copier_config; 1319904c48c4SRanjani Sridharan ipc_config_size = &ipc4_copier->ipc_config_size; 1320904c48c4SRanjani Sridharan ipc_config_data = &ipc4_copier->ipc_config_data; 1321904c48c4SRanjani Sridharan 1322904c48c4SRanjani Sridharan /* config_length is DWORD based */ 1323904c48c4SRanjani Sridharan ipc_size = sizeof(*copier_data) + copier_data->gtw_cfg.config_length * 4; 1324904c48c4SRanjani Sridharan 1325904c48c4SRanjani Sridharan dev_dbg(sdev->dev, "copier %s, IPC size is %d", swidget->widget->name, ipc_size); 1326904c48c4SRanjani Sridharan 1327904c48c4SRanjani Sridharan *ipc_config_data = kzalloc(ipc_size, GFP_KERNEL); 1328904c48c4SRanjani Sridharan if (!*ipc_config_data) 1329904c48c4SRanjani Sridharan return -ENOMEM; 1330904c48c4SRanjani Sridharan 1331904c48c4SRanjani Sridharan *ipc_config_size = ipc_size; 1332904c48c4SRanjani Sridharan 1333904c48c4SRanjani Sridharan /* copy IPC data */ 1334904c48c4SRanjani Sridharan memcpy(*ipc_config_data, (void *)copier_data, sizeof(*copier_data)); 1335904c48c4SRanjani Sridharan if (copier_data->gtw_cfg.config_length) 1336904c48c4SRanjani Sridharan memcpy(*ipc_config_data + sizeof(*copier_data), 1337904c48c4SRanjani Sridharan *data, copier_data->gtw_cfg.config_length * 4); 1338904c48c4SRanjani Sridharan 1339904c48c4SRanjani Sridharan /* update pipeline memory usage */ 1340904c48c4SRanjani Sridharan sof_ipc4_update_pipeline_mem_usage(sdev, swidget, &copier_data->base_config); 1341904c48c4SRanjani Sridharan 1342711d0427SBard Liao return 0; 13434f838ab2SRanjani Sridharan } 13444f838ab2SRanjani Sridharan 13454f838ab2SRanjani Sridharan static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget, 13464f838ab2SRanjani Sridharan struct snd_pcm_hw_params *fe_params, 13474f838ab2SRanjani Sridharan struct snd_sof_platform_stream_params *platform_params, 13484f838ab2SRanjani Sridharan struct snd_pcm_hw_params *pipeline_params, int dir) 13494f838ab2SRanjani Sridharan { 13504f838ab2SRanjani Sridharan struct snd_soc_component *scomp = swidget->scomp; 13514f838ab2SRanjani Sridharan struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 13524f838ab2SRanjani Sridharan struct sof_ipc4_gain *gain = swidget->private; 1353e63a73f9SRanjani Sridharan struct sof_ipc4_available_audio_format *available_fmt = &gain->available_fmt; 13544f838ab2SRanjani Sridharan int ret; 13554f838ab2SRanjani Sridharan 13567ab6b1e8SRanjani Sridharan available_fmt->ref_audio_fmt = available_fmt->input_pin_fmts; 13574f838ab2SRanjani Sridharan 13584f838ab2SRanjani Sridharan /* output format is not required to be sent to the FW for gain */ 13594f838ab2SRanjani Sridharan ret = sof_ipc4_init_audio_fmt(sdev, swidget, &gain->base_config, 1360*9c560549SRanjani Sridharan pipeline_params, available_fmt); 13614f838ab2SRanjani Sridharan if (ret < 0) 13624f838ab2SRanjani Sridharan return ret; 13634f838ab2SRanjani Sridharan 13644f838ab2SRanjani Sridharan /* update pipeline memory usage */ 13654f838ab2SRanjani Sridharan sof_ipc4_update_pipeline_mem_usage(sdev, swidget, &gain->base_config); 13664f838ab2SRanjani Sridharan 1367711d0427SBard Liao return 0; 13684f838ab2SRanjani Sridharan } 13694f838ab2SRanjani Sridharan 13704d4ba014SRanjani Sridharan static int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget, 13714d4ba014SRanjani Sridharan struct snd_pcm_hw_params *fe_params, 13724d4ba014SRanjani Sridharan struct snd_sof_platform_stream_params *platform_params, 13734d4ba014SRanjani Sridharan struct snd_pcm_hw_params *pipeline_params, int dir) 13744d4ba014SRanjani Sridharan { 13754d4ba014SRanjani Sridharan struct snd_soc_component *scomp = swidget->scomp; 13764d4ba014SRanjani Sridharan struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 13774d4ba014SRanjani Sridharan struct sof_ipc4_mixer *mixer = swidget->private; 1378e63a73f9SRanjani Sridharan struct sof_ipc4_available_audio_format *available_fmt = &mixer->available_fmt; 13794d4ba014SRanjani Sridharan int ret; 13804d4ba014SRanjani Sridharan 13814d4ba014SRanjani Sridharan /* only 32bit is supported by mixer */ 13827ab6b1e8SRanjani Sridharan available_fmt->ref_audio_fmt = available_fmt->input_pin_fmts; 13834d4ba014SRanjani Sridharan 13844d4ba014SRanjani Sridharan /* output format is not required to be sent to the FW for mixer */ 13854d4ba014SRanjani Sridharan ret = sof_ipc4_init_audio_fmt(sdev, swidget, &mixer->base_config, 1386*9c560549SRanjani Sridharan pipeline_params, available_fmt); 13874d4ba014SRanjani Sridharan if (ret < 0) 13884d4ba014SRanjani Sridharan return ret; 13894d4ba014SRanjani Sridharan 13904d4ba014SRanjani Sridharan /* update pipeline memory usage */ 13914d4ba014SRanjani Sridharan sof_ipc4_update_pipeline_mem_usage(sdev, swidget, &mixer->base_config); 13924d4ba014SRanjani Sridharan 1393711d0427SBard Liao return 0; 13944d4ba014SRanjani Sridharan } 13954d4ba014SRanjani Sridharan 1396b85f4fc4SRander Wang static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget, 1397b85f4fc4SRander Wang struct snd_pcm_hw_params *fe_params, 1398b85f4fc4SRander Wang struct snd_sof_platform_stream_params *platform_params, 1399b85f4fc4SRander Wang struct snd_pcm_hw_params *pipeline_params, int dir) 1400b85f4fc4SRander Wang { 1401b85f4fc4SRander Wang struct snd_soc_component *scomp = swidget->scomp; 1402b85f4fc4SRander Wang struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 1403b85f4fc4SRander Wang struct sof_ipc4_src *src = swidget->private; 1404e63a73f9SRanjani Sridharan struct sof_ipc4_available_audio_format *available_fmt = &src->available_fmt; 1405b85f4fc4SRander Wang struct snd_interval *rate; 1406b85f4fc4SRander Wang int ret; 1407b85f4fc4SRander Wang 14087ab6b1e8SRanjani Sridharan available_fmt->ref_audio_fmt = available_fmt->input_pin_fmts; 1409b85f4fc4SRander Wang 1410b85f4fc4SRander Wang /* output format is not required to be sent to the FW for SRC */ 1411b85f4fc4SRander Wang ret = sof_ipc4_init_audio_fmt(sdev, swidget, &src->base_config, 1412*9c560549SRanjani Sridharan pipeline_params, available_fmt); 1413b85f4fc4SRander Wang if (ret < 0) 1414b85f4fc4SRander Wang return ret; 1415b85f4fc4SRander Wang 1416b85f4fc4SRander Wang /* update pipeline memory usage */ 1417b85f4fc4SRander Wang sof_ipc4_update_pipeline_mem_usage(sdev, swidget, &src->base_config); 1418b85f4fc4SRander Wang 1419b85f4fc4SRander Wang /* update pipeline_params for sink widgets */ 1420b85f4fc4SRander Wang rate = hw_param_interval(pipeline_params, SNDRV_PCM_HW_PARAM_RATE); 1421b85f4fc4SRander Wang rate->min = src->sink_rate; 1422b85f4fc4SRander Wang rate->max = rate->min; 1423b85f4fc4SRander Wang 1424b85f4fc4SRander Wang return 0; 1425b85f4fc4SRander Wang } 1426b85f4fc4SRander Wang 1427d97964f8SRanjani Sridharan static int sof_ipc4_control_load_volume(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol) 1428d97964f8SRanjani Sridharan { 1429d97964f8SRanjani Sridharan struct sof_ipc4_control_data *control_data; 1430d97964f8SRanjani Sridharan struct sof_ipc4_msg *msg; 1431d97964f8SRanjani Sridharan int i; 1432d97964f8SRanjani Sridharan 1433d97964f8SRanjani Sridharan scontrol->size = struct_size(control_data, chanv, scontrol->num_channels); 1434d97964f8SRanjani Sridharan 1435d97964f8SRanjani Sridharan /* scontrol->ipc_control_data will be freed in sof_control_unload */ 1436d97964f8SRanjani Sridharan scontrol->ipc_control_data = kzalloc(scontrol->size, GFP_KERNEL); 1437d97964f8SRanjani Sridharan if (!scontrol->ipc_control_data) 1438d97964f8SRanjani Sridharan return -ENOMEM; 1439d97964f8SRanjani Sridharan 1440d97964f8SRanjani Sridharan control_data = scontrol->ipc_control_data; 1441d97964f8SRanjani Sridharan control_data->index = scontrol->index; 1442d97964f8SRanjani Sridharan 1443d97964f8SRanjani Sridharan msg = &control_data->msg; 1444d97964f8SRanjani Sridharan msg->primary = SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_SET); 1445d97964f8SRanjani Sridharan msg->primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); 1446d97964f8SRanjani Sridharan msg->primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG); 1447d97964f8SRanjani Sridharan 1448d97964f8SRanjani Sridharan msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_GAIN_PARAM_ID); 1449d97964f8SRanjani Sridharan 1450d97964f8SRanjani Sridharan /* set default volume values to 0dB in control */ 1451d97964f8SRanjani Sridharan for (i = 0; i < scontrol->num_channels; i++) { 1452d97964f8SRanjani Sridharan control_data->chanv[i].channel = i; 1453d97964f8SRanjani Sridharan control_data->chanv[i].value = SOF_IPC4_VOL_ZERO_DB; 1454d97964f8SRanjani Sridharan } 1455d97964f8SRanjani Sridharan 1456d97964f8SRanjani Sridharan return 0; 1457d97964f8SRanjani Sridharan } 1458d97964f8SRanjani Sridharan 1459d97964f8SRanjani Sridharan static int sof_ipc4_control_setup(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol) 1460d97964f8SRanjani Sridharan { 1461d97964f8SRanjani Sridharan switch (scontrol->info_type) { 1462d97964f8SRanjani Sridharan case SND_SOC_TPLG_CTL_VOLSW: 1463d97964f8SRanjani Sridharan case SND_SOC_TPLG_CTL_VOLSW_SX: 1464d97964f8SRanjani Sridharan case SND_SOC_TPLG_CTL_VOLSW_XR_SX: 1465d97964f8SRanjani Sridharan return sof_ipc4_control_load_volume(sdev, scontrol); 1466d97964f8SRanjani Sridharan default: 1467d97964f8SRanjani Sridharan break; 1468d97964f8SRanjani Sridharan } 1469d97964f8SRanjani Sridharan 1470d97964f8SRanjani Sridharan return 0; 1471d97964f8SRanjani Sridharan } 1472d97964f8SRanjani Sridharan 14736e9257a1SRanjani Sridharan static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) 14746e9257a1SRanjani Sridharan { 14759c04363dSRanjani Sridharan struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; 1476a2ba1f70SBard Liao struct sof_ipc4_fw_data *ipc4_data = sdev->private; 14776e9257a1SRanjani Sridharan struct sof_ipc4_pipeline *pipeline; 14786e9257a1SRanjani Sridharan struct sof_ipc4_msg *msg; 14796e9257a1SRanjani Sridharan void *ipc_data = NULL; 14806e9257a1SRanjani Sridharan u32 ipc_size = 0; 14816e9257a1SRanjani Sridharan int ret; 14826e9257a1SRanjani Sridharan 14836e9257a1SRanjani Sridharan switch (swidget->id) { 14846e9257a1SRanjani Sridharan case snd_soc_dapm_scheduler: 14856e9257a1SRanjani Sridharan pipeline = swidget->private; 14866e9257a1SRanjani Sridharan 14876e9257a1SRanjani Sridharan dev_dbg(sdev->dev, "pipeline: %d memory pages: %d\n", swidget->pipeline_id, 14886e9257a1SRanjani Sridharan pipeline->mem_usage); 14896e9257a1SRanjani Sridharan 14906e9257a1SRanjani Sridharan msg = &pipeline->msg; 14916e9257a1SRanjani Sridharan msg->primary |= pipeline->mem_usage; 1492a2ba1f70SBard Liao 1493a2ba1f70SBard Liao swidget->instance_id = ida_alloc_max(&pipeline_ida, ipc4_data->max_num_pipelines, 1494a2ba1f70SBard Liao GFP_KERNEL); 1495a2ba1f70SBard Liao if (swidget->instance_id < 0) { 1496a2ba1f70SBard Liao dev_err(sdev->dev, "failed to assign pipeline id for %s: %d\n", 1497a2ba1f70SBard Liao swidget->widget->name, swidget->instance_id); 1498a2ba1f70SBard Liao return swidget->instance_id; 1499a2ba1f70SBard Liao } 1500a2ba1f70SBard Liao msg->primary &= ~SOF_IPC4_GLB_PIPE_INSTANCE_MASK; 1501a2ba1f70SBard Liao msg->primary |= SOF_IPC4_GLB_PIPE_INSTANCE_ID(swidget->instance_id); 15026e9257a1SRanjani Sridharan break; 15036e9257a1SRanjani Sridharan case snd_soc_dapm_aif_in: 15046e9257a1SRanjani Sridharan case snd_soc_dapm_aif_out: 15057d573425SBard Liao case snd_soc_dapm_buffer: 15066e9257a1SRanjani Sridharan { 15076e9257a1SRanjani Sridharan struct sof_ipc4_copier *ipc4_copier = swidget->private; 15086e9257a1SRanjani Sridharan 15096e9257a1SRanjani Sridharan ipc_size = ipc4_copier->ipc_config_size; 15106e9257a1SRanjani Sridharan ipc_data = ipc4_copier->ipc_config_data; 15116e9257a1SRanjani Sridharan 15126e9257a1SRanjani Sridharan msg = &ipc4_copier->msg; 15136e9257a1SRanjani Sridharan break; 15146e9257a1SRanjani Sridharan } 15156e9257a1SRanjani Sridharan case snd_soc_dapm_dai_in: 15166e9257a1SRanjani Sridharan case snd_soc_dapm_dai_out: 15176e9257a1SRanjani Sridharan { 15186e9257a1SRanjani Sridharan struct snd_sof_dai *dai = swidget->private; 15196e9257a1SRanjani Sridharan struct sof_ipc4_copier *ipc4_copier = dai->private; 15206e9257a1SRanjani Sridharan 15216e9257a1SRanjani Sridharan ipc_size = ipc4_copier->ipc_config_size; 15226e9257a1SRanjani Sridharan ipc_data = ipc4_copier->ipc_config_data; 15236e9257a1SRanjani Sridharan 15246e9257a1SRanjani Sridharan msg = &ipc4_copier->msg; 15256e9257a1SRanjani Sridharan break; 15266e9257a1SRanjani Sridharan } 15276e9257a1SRanjani Sridharan case snd_soc_dapm_pga: 15286e9257a1SRanjani Sridharan { 15296e9257a1SRanjani Sridharan struct sof_ipc4_gain *gain = swidget->private; 15306e9257a1SRanjani Sridharan 15316e9257a1SRanjani Sridharan ipc_size = sizeof(struct sof_ipc4_base_module_cfg) + 15326e9257a1SRanjani Sridharan sizeof(struct sof_ipc4_gain_data); 15336e9257a1SRanjani Sridharan ipc_data = gain; 15346e9257a1SRanjani Sridharan 15356e9257a1SRanjani Sridharan msg = &gain->msg; 15366e9257a1SRanjani Sridharan break; 15376e9257a1SRanjani Sridharan } 15386e9257a1SRanjani Sridharan case snd_soc_dapm_mixer: 15396e9257a1SRanjani Sridharan { 15406e9257a1SRanjani Sridharan struct sof_ipc4_mixer *mixer = swidget->private; 15416e9257a1SRanjani Sridharan 15426e9257a1SRanjani Sridharan ipc_size = sizeof(mixer->base_config); 15436e9257a1SRanjani Sridharan ipc_data = &mixer->base_config; 15446e9257a1SRanjani Sridharan 15456e9257a1SRanjani Sridharan msg = &mixer->msg; 15466e9257a1SRanjani Sridharan break; 15476e9257a1SRanjani Sridharan } 1548b85f4fc4SRander Wang case snd_soc_dapm_src: 1549b85f4fc4SRander Wang { 1550b85f4fc4SRander Wang struct sof_ipc4_src *src = swidget->private; 1551b85f4fc4SRander Wang 1552b85f4fc4SRander Wang ipc_size = sizeof(struct sof_ipc4_base_module_cfg) + sizeof(src->sink_rate); 1553b85f4fc4SRander Wang ipc_data = src; 1554b85f4fc4SRander Wang 1555b85f4fc4SRander Wang msg = &src->msg; 1556b85f4fc4SRander Wang break; 1557b85f4fc4SRander Wang } 15586e9257a1SRanjani Sridharan default: 15596e9257a1SRanjani Sridharan dev_err(sdev->dev, "widget type %d not supported", swidget->id); 15606e9257a1SRanjani Sridharan return -EINVAL; 15616e9257a1SRanjani Sridharan } 15626e9257a1SRanjani Sridharan 15636e9257a1SRanjani Sridharan if (swidget->id != snd_soc_dapm_scheduler) { 1564711d0427SBard Liao ret = sof_ipc4_widget_assign_instance_id(sdev, swidget); 1565711d0427SBard Liao if (ret < 0) { 1566711d0427SBard Liao dev_err(sdev->dev, "failed to assign instance id for %s\n", 1567711d0427SBard Liao swidget->widget->name); 1568711d0427SBard Liao return ret; 1569711d0427SBard Liao } 15707738211bSPierre-Louis Bossart 15716e9257a1SRanjani Sridharan msg->primary &= ~SOF_IPC4_MOD_INSTANCE_MASK; 15726e9257a1SRanjani Sridharan msg->primary |= SOF_IPC4_MOD_INSTANCE(swidget->instance_id); 15736e9257a1SRanjani Sridharan 15746e9257a1SRanjani Sridharan msg->extension &= ~SOF_IPC4_MOD_EXT_PARAM_SIZE_MASK; 15756e9257a1SRanjani Sridharan msg->extension |= ipc_size >> 2; 1576a2ba1f70SBard Liao 1577a2ba1f70SBard Liao msg->extension &= ~SOF_IPC4_MOD_EXT_PPL_ID_MASK; 1578a2ba1f70SBard Liao msg->extension |= SOF_IPC4_MOD_EXT_PPL_ID(pipe_widget->instance_id); 15796e9257a1SRanjani Sridharan } 1580711d0427SBard Liao dev_dbg(sdev->dev, "Create widget %s instance %d - pipe %d - core %d\n", 1581711d0427SBard Liao swidget->widget->name, swidget->instance_id, swidget->pipeline_id, swidget->core); 15826e9257a1SRanjani Sridharan 15836e9257a1SRanjani Sridharan msg->data_size = ipc_size; 15846e9257a1SRanjani Sridharan msg->data_ptr = ipc_data; 15856e9257a1SRanjani Sridharan 15866e9257a1SRanjani Sridharan ret = sof_ipc_tx_message(sdev->ipc, msg, ipc_size, NULL, 0); 158761eb0addSPeter Ujfalusi if (ret < 0) { 15886e9257a1SRanjani Sridharan dev_err(sdev->dev, "failed to create module %s\n", swidget->widget->name); 15896e9257a1SRanjani Sridharan 159061eb0addSPeter Ujfalusi if (swidget->id != snd_soc_dapm_scheduler) { 159161eb0addSPeter Ujfalusi struct sof_ipc4_fw_module *fw_module = swidget->module_info; 159261eb0addSPeter Ujfalusi 159361eb0addSPeter Ujfalusi ida_free(&fw_module->m_ida, swidget->instance_id); 1594a2ba1f70SBard Liao } else { 1595a2ba1f70SBard Liao ida_free(&pipeline_ida, swidget->instance_id); 159661eb0addSPeter Ujfalusi } 159761eb0addSPeter Ujfalusi } 159861eb0addSPeter Ujfalusi 15996e9257a1SRanjani Sridharan return ret; 16006e9257a1SRanjani Sridharan } 16016e9257a1SRanjani Sridharan 16026e9257a1SRanjani Sridharan static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) 16036e9257a1SRanjani Sridharan { 1604711d0427SBard Liao struct sof_ipc4_fw_module *fw_module = swidget->module_info; 16056bc4d1b7SRanjani Sridharan struct sof_ipc4_fw_data *ipc4_data = sdev->private; 16066e9257a1SRanjani Sridharan int ret = 0; 16076e9257a1SRanjani Sridharan 16086bc4d1b7SRanjani Sridharan mutex_lock(&ipc4_data->pipeline_state_mutex); 16096bc4d1b7SRanjani Sridharan 16106e9257a1SRanjani Sridharan /* freeing a pipeline frees all the widgets associated with it */ 16116e9257a1SRanjani Sridharan if (swidget->id == snd_soc_dapm_scheduler) { 16126e9257a1SRanjani Sridharan struct sof_ipc4_pipeline *pipeline = swidget->private; 16136e9257a1SRanjani Sridharan struct sof_ipc4_msg msg = {{ 0 }}; 16146e9257a1SRanjani Sridharan u32 header; 16156e9257a1SRanjani Sridharan 1616a2ba1f70SBard Liao header = SOF_IPC4_GLB_PIPE_INSTANCE_ID(swidget->instance_id); 16176e9257a1SRanjani Sridharan header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_DELETE_PIPELINE); 16186e9257a1SRanjani Sridharan header |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); 16196e9257a1SRanjani Sridharan header |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG); 16206e9257a1SRanjani Sridharan 16216e9257a1SRanjani Sridharan msg.primary = header; 16226e9257a1SRanjani Sridharan 16236e9257a1SRanjani Sridharan ret = sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0); 16246e9257a1SRanjani Sridharan if (ret < 0) 16256e9257a1SRanjani Sridharan dev_err(sdev->dev, "failed to free pipeline widget %s\n", 16266e9257a1SRanjani Sridharan swidget->widget->name); 16276e9257a1SRanjani Sridharan 16286e9257a1SRanjani Sridharan pipeline->mem_usage = 0; 16296e9257a1SRanjani Sridharan pipeline->state = SOF_IPC4_PIPE_UNINITIALIZED; 1630a2ba1f70SBard Liao ida_free(&pipeline_ida, swidget->instance_id); 1631711d0427SBard Liao } else { 1632711d0427SBard Liao ida_free(&fw_module->m_ida, swidget->instance_id); 16336e9257a1SRanjani Sridharan } 16346e9257a1SRanjani Sridharan 16356bc4d1b7SRanjani Sridharan mutex_unlock(&ipc4_data->pipeline_state_mutex); 16366bc4d1b7SRanjani Sridharan 16376e9257a1SRanjani Sridharan return ret; 16386e9257a1SRanjani Sridharan } 16396e9257a1SRanjani Sridharan 1640c84443dbSChao Song static int sof_ipc4_get_queue_id(struct snd_sof_widget *src_widget, 1641c84443dbSChao Song struct snd_sof_widget *sink_widget, bool pin_type) 1642c84443dbSChao Song { 1643c84443dbSChao Song struct snd_sof_widget *current_swidget; 1644c84443dbSChao Song struct snd_soc_component *scomp; 1645c84443dbSChao Song struct ida *queue_ida; 1646c84443dbSChao Song const char *buddy_name; 1647c84443dbSChao Song char **pin_binding; 1648c84443dbSChao Song u32 num_pins; 1649c84443dbSChao Song int i; 1650c84443dbSChao Song 1651bb79f2a6SRanjani Sridharan if (pin_type == SOF_PIN_TYPE_OUTPUT) { 1652c84443dbSChao Song current_swidget = src_widget; 1653bb79f2a6SRanjani Sridharan pin_binding = src_widget->output_pin_binding; 1654bb79f2a6SRanjani Sridharan queue_ida = &src_widget->output_queue_ida; 1655bb79f2a6SRanjani Sridharan num_pins = src_widget->num_output_pins; 1656c84443dbSChao Song buddy_name = sink_widget->widget->name; 1657c84443dbSChao Song } else { 1658c84443dbSChao Song current_swidget = sink_widget; 1659bb79f2a6SRanjani Sridharan pin_binding = sink_widget->input_pin_binding; 1660bb79f2a6SRanjani Sridharan queue_ida = &sink_widget->input_queue_ida; 1661bb79f2a6SRanjani Sridharan num_pins = sink_widget->num_input_pins; 1662c84443dbSChao Song buddy_name = src_widget->widget->name; 1663c84443dbSChao Song } 1664c84443dbSChao Song 1665c84443dbSChao Song scomp = current_swidget->scomp; 1666c84443dbSChao Song 1667c84443dbSChao Song if (num_pins < 1) { 1668c84443dbSChao Song dev_err(scomp->dev, "invalid %s num_pins: %d for queue allocation for %s\n", 1669bb79f2a6SRanjani Sridharan (pin_type == SOF_PIN_TYPE_OUTPUT ? "output" : "input"), 1670c84443dbSChao Song num_pins, current_swidget->widget->name); 1671c84443dbSChao Song return -EINVAL; 1672c84443dbSChao Song } 1673c84443dbSChao Song 1674bb79f2a6SRanjani Sridharan /* If there is only one input/output pin, queue id must be 0 */ 1675c84443dbSChao Song if (num_pins == 1) 1676c84443dbSChao Song return 0; 1677c84443dbSChao Song 1678c84443dbSChao Song /* Allocate queue ID from pin binding array if it is defined in topology. */ 1679c84443dbSChao Song if (pin_binding) { 1680c84443dbSChao Song for (i = 0; i < num_pins; i++) { 1681c84443dbSChao Song if (!strcmp(pin_binding[i], buddy_name)) 1682c84443dbSChao Song return i; 1683c84443dbSChao Song } 1684c84443dbSChao Song /* 1685c84443dbSChao Song * Fail if no queue ID found from pin binding array, so that we don't 1686c84443dbSChao Song * mixed use pin binding array and ida for queue ID allocation. 1687c84443dbSChao Song */ 1688c84443dbSChao Song dev_err(scomp->dev, "no %s queue id found from pin binding array for %s\n", 1689bb79f2a6SRanjani Sridharan (pin_type == SOF_PIN_TYPE_OUTPUT ? "output" : "input"), 1690c84443dbSChao Song current_swidget->widget->name); 1691c84443dbSChao Song return -EINVAL; 1692c84443dbSChao Song } 1693c84443dbSChao Song 1694c84443dbSChao Song /* If no pin binding array specified in topology, use ida to allocate one */ 1695c84443dbSChao Song return ida_alloc_max(queue_ida, num_pins, GFP_KERNEL); 1696c84443dbSChao Song } 1697c84443dbSChao Song 1698c84443dbSChao Song static void sof_ipc4_put_queue_id(struct snd_sof_widget *swidget, int queue_id, 1699c84443dbSChao Song bool pin_type) 1700c84443dbSChao Song { 1701c84443dbSChao Song struct ida *queue_ida; 1702c84443dbSChao Song char **pin_binding; 1703c84443dbSChao Song int num_pins; 1704c84443dbSChao Song 1705bb79f2a6SRanjani Sridharan if (pin_type == SOF_PIN_TYPE_OUTPUT) { 1706bb79f2a6SRanjani Sridharan pin_binding = swidget->output_pin_binding; 1707bb79f2a6SRanjani Sridharan queue_ida = &swidget->output_queue_ida; 1708bb79f2a6SRanjani Sridharan num_pins = swidget->num_output_pins; 1709c84443dbSChao Song } else { 1710bb79f2a6SRanjani Sridharan pin_binding = swidget->input_pin_binding; 1711bb79f2a6SRanjani Sridharan queue_ida = &swidget->input_queue_ida; 1712bb79f2a6SRanjani Sridharan num_pins = swidget->num_input_pins; 1713c84443dbSChao Song } 1714c84443dbSChao Song 1715c84443dbSChao Song /* Nothing to free if queue ID is not allocated with ida. */ 1716c84443dbSChao Song if (num_pins == 1 || pin_binding) 1717c84443dbSChao Song return; 1718c84443dbSChao Song 1719c84443dbSChao Song ida_free(queue_ida, queue_id); 1720c84443dbSChao Song } 1721c84443dbSChao Song 172211f60563SBard Liao static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev, 172311f60563SBard Liao struct snd_sof_widget *src_widget, 172411f60563SBard Liao struct snd_sof_widget *sink_widget, 172511f60563SBard Liao int sink_id) 172611f60563SBard Liao { 172711f60563SBard Liao struct sof_ipc4_base_module_cfg *sink_config = sink_widget->private; 172811f60563SBard Liao struct sof_ipc4_base_module_cfg *src_config; 172911f60563SBard Liao struct sof_ipc4_copier_config_set_sink_format format; 173011f60563SBard Liao struct sof_ipc4_fw_module *fw_module; 173111f60563SBard Liao struct sof_ipc4_msg msg = {{ 0 }}; 173211f60563SBard Liao u32 header, extension; 173311f60563SBard Liao 173411f60563SBard Liao dev_dbg(sdev->dev, "%s set copier sink %d format\n", 173511f60563SBard Liao src_widget->widget->name, sink_id); 173611f60563SBard Liao 173711f60563SBard Liao if (WIDGET_IS_DAI(src_widget->id)) { 173811f60563SBard Liao struct snd_sof_dai *dai = src_widget->private; 173911f60563SBard Liao 174011f60563SBard Liao src_config = dai->private; 174111f60563SBard Liao } else { 174211f60563SBard Liao src_config = src_widget->private; 174311f60563SBard Liao } 174411f60563SBard Liao 174511f60563SBard Liao fw_module = src_widget->module_info; 174611f60563SBard Liao 174711f60563SBard Liao format.sink_id = sink_id; 174811f60563SBard Liao memcpy(&format.source_fmt, &src_config->audio_fmt, sizeof(format.source_fmt)); 174911f60563SBard Liao memcpy(&format.sink_fmt, &sink_config->audio_fmt, sizeof(format.sink_fmt)); 175011f60563SBard Liao msg.data_size = sizeof(format); 175111f60563SBard Liao msg.data_ptr = &format; 175211f60563SBard Liao 175311f60563SBard Liao header = fw_module->man4_module_entry.id; 175411f60563SBard Liao header |= SOF_IPC4_MOD_INSTANCE(src_widget->instance_id); 175511f60563SBard Liao header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_SET); 175611f60563SBard Liao header |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); 175711f60563SBard Liao header |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG); 175811f60563SBard Liao 175911f60563SBard Liao extension = SOF_IPC4_MOD_EXT_MSG_SIZE(msg.data_size); 176011f60563SBard Liao extension |= 176111f60563SBard Liao SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_COPIER_MODULE_CFG_PARAM_SET_SINK_FORMAT); 176211f60563SBard Liao extension |= SOF_IPC4_MOD_EXT_MSG_LAST_BLOCK(1); 176311f60563SBard Liao extension |= SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK(1); 176411f60563SBard Liao 176511f60563SBard Liao msg.primary = header; 176611f60563SBard Liao msg.extension = extension; 176711f60563SBard Liao 176811f60563SBard Liao return sof_ipc_tx_message(sdev->ipc, &msg, msg.data_size, NULL, 0); 176911f60563SBard Liao } 177011f60563SBard Liao 17713acd5270SRanjani Sridharan static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *sroute) 17723acd5270SRanjani Sridharan { 17733acd5270SRanjani Sridharan struct snd_sof_widget *src_widget = sroute->src_widget; 17743acd5270SRanjani Sridharan struct snd_sof_widget *sink_widget = sroute->sink_widget; 17753acd5270SRanjani Sridharan struct sof_ipc4_fw_module *src_fw_module = src_widget->module_info; 17763acd5270SRanjani Sridharan struct sof_ipc4_fw_module *sink_fw_module = sink_widget->module_info; 17773acd5270SRanjani Sridharan struct sof_ipc4_msg msg = {{ 0 }}; 17783acd5270SRanjani Sridharan u32 header, extension; 17793acd5270SRanjani Sridharan int ret; 17803acd5270SRanjani Sridharan 1781c84443dbSChao Song sroute->src_queue_id = sof_ipc4_get_queue_id(src_widget, sink_widget, 1782bb79f2a6SRanjani Sridharan SOF_PIN_TYPE_OUTPUT); 1783c84443dbSChao Song if (sroute->src_queue_id < 0) { 1784c84443dbSChao Song dev_err(sdev->dev, "failed to get queue ID for source widget: %s\n", 1785c84443dbSChao Song src_widget->widget->name); 1786c84443dbSChao Song return sroute->src_queue_id; 1787c84443dbSChao Song } 1788c84443dbSChao Song 1789c84443dbSChao Song sroute->dst_queue_id = sof_ipc4_get_queue_id(src_widget, sink_widget, 1790bb79f2a6SRanjani Sridharan SOF_PIN_TYPE_INPUT); 1791c84443dbSChao Song if (sroute->dst_queue_id < 0) { 1792c84443dbSChao Song dev_err(sdev->dev, "failed to get queue ID for sink widget: %s\n", 1793c84443dbSChao Song sink_widget->widget->name); 1794c84443dbSChao Song sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, 1795bb79f2a6SRanjani Sridharan SOF_PIN_TYPE_OUTPUT); 1796c84443dbSChao Song return sroute->dst_queue_id; 1797c84443dbSChao Song } 1798c84443dbSChao Song 179911f60563SBard Liao /* Pin 0 format is already set during copier module init */ 180011f60563SBard Liao if (sroute->src_queue_id > 0 && WIDGET_IS_COPIER(src_widget->id)) { 180111f60563SBard Liao ret = sof_ipc4_set_copier_sink_format(sdev, src_widget, sink_widget, 180211f60563SBard Liao sroute->src_queue_id); 180311f60563SBard Liao if (ret < 0) { 180411f60563SBard Liao dev_err(sdev->dev, "failed to set sink format for %s source queue ID %d\n", 180511f60563SBard Liao src_widget->widget->name, sroute->src_queue_id); 180611f60563SBard Liao goto out; 180711f60563SBard Liao } 180811f60563SBard Liao } 180911f60563SBard Liao 1810c84443dbSChao Song dev_dbg(sdev->dev, "bind %s:%d -> %s:%d\n", 1811c84443dbSChao Song src_widget->widget->name, sroute->src_queue_id, 1812c84443dbSChao Song sink_widget->widget->name, sroute->dst_queue_id); 18133acd5270SRanjani Sridharan 18143acd5270SRanjani Sridharan header = src_fw_module->man4_module_entry.id; 18153acd5270SRanjani Sridharan header |= SOF_IPC4_MOD_INSTANCE(src_widget->instance_id); 18163acd5270SRanjani Sridharan header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_BIND); 18173acd5270SRanjani Sridharan header |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); 18183acd5270SRanjani Sridharan header |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG); 18193acd5270SRanjani Sridharan 18203acd5270SRanjani Sridharan extension = sink_fw_module->man4_module_entry.id; 18213acd5270SRanjani Sridharan extension |= SOF_IPC4_MOD_EXT_DST_MOD_INSTANCE(sink_widget->instance_id); 1822c84443dbSChao Song extension |= SOF_IPC4_MOD_EXT_DST_MOD_QUEUE_ID(sroute->dst_queue_id); 1823c84443dbSChao Song extension |= SOF_IPC4_MOD_EXT_SRC_MOD_QUEUE_ID(sroute->src_queue_id); 18243acd5270SRanjani Sridharan 18253acd5270SRanjani Sridharan msg.primary = header; 18263acd5270SRanjani Sridharan msg.extension = extension; 18273acd5270SRanjani Sridharan 18283acd5270SRanjani Sridharan ret = sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0); 1829c84443dbSChao Song if (ret < 0) { 1830b796ff3bSRanjani Sridharan dev_err(sdev->dev, "failed to bind modules %s:%d -> %s:%d\n", 1831b796ff3bSRanjani Sridharan src_widget->widget->name, sroute->src_queue_id, 1832b796ff3bSRanjani Sridharan sink_widget->widget->name, sroute->dst_queue_id); 183311f60563SBard Liao goto out; 1834c84443dbSChao Song } 1835c84443dbSChao Song 18363acd5270SRanjani Sridharan return ret; 183711f60563SBard Liao 183811f60563SBard Liao out: 1839bb79f2a6SRanjani Sridharan sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_OUTPUT); 1840bb79f2a6SRanjani Sridharan sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id, SOF_PIN_TYPE_INPUT); 184111f60563SBard Liao return ret; 18423acd5270SRanjani Sridharan } 18433acd5270SRanjani Sridharan 18443acd5270SRanjani Sridharan static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *sroute) 18453acd5270SRanjani Sridharan { 18463acd5270SRanjani Sridharan struct snd_sof_widget *src_widget = sroute->src_widget; 18473acd5270SRanjani Sridharan struct snd_sof_widget *sink_widget = sroute->sink_widget; 18483acd5270SRanjani Sridharan struct sof_ipc4_fw_module *src_fw_module = src_widget->module_info; 18493acd5270SRanjani Sridharan struct sof_ipc4_fw_module *sink_fw_module = sink_widget->module_info; 18503acd5270SRanjani Sridharan struct sof_ipc4_msg msg = {{ 0 }}; 18513acd5270SRanjani Sridharan u32 header, extension; 18529a62d87aSRanjani Sridharan int ret = 0; 18533acd5270SRanjani Sridharan 1854c84443dbSChao Song dev_dbg(sdev->dev, "unbind modules %s:%d -> %s:%d\n", 1855c84443dbSChao Song src_widget->widget->name, sroute->src_queue_id, 1856c84443dbSChao Song sink_widget->widget->name, sroute->dst_queue_id); 18573acd5270SRanjani Sridharan 18589a62d87aSRanjani Sridharan /* 18599a62d87aSRanjani Sridharan * routes belonging to the same pipeline will be disconnected by the FW when the pipeline 18609a62d87aSRanjani Sridharan * is freed. So avoid sending this IPC which will be ignored by the FW anyway. 18619a62d87aSRanjani Sridharan */ 18629c04363dSRanjani Sridharan if (src_widget->spipe->pipe_widget == sink_widget->spipe->pipe_widget) 18639a62d87aSRanjani Sridharan goto out; 18649a62d87aSRanjani Sridharan 18653acd5270SRanjani Sridharan header = src_fw_module->man4_module_entry.id; 18663acd5270SRanjani Sridharan header |= SOF_IPC4_MOD_INSTANCE(src_widget->instance_id); 18673acd5270SRanjani Sridharan header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_UNBIND); 18683acd5270SRanjani Sridharan header |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); 18693acd5270SRanjani Sridharan header |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG); 18703acd5270SRanjani Sridharan 18713acd5270SRanjani Sridharan extension = sink_fw_module->man4_module_entry.id; 18723acd5270SRanjani Sridharan extension |= SOF_IPC4_MOD_EXT_DST_MOD_INSTANCE(sink_widget->instance_id); 1873c84443dbSChao Song extension |= SOF_IPC4_MOD_EXT_DST_MOD_QUEUE_ID(sroute->dst_queue_id); 1874c84443dbSChao Song extension |= SOF_IPC4_MOD_EXT_SRC_MOD_QUEUE_ID(sroute->src_queue_id); 18753acd5270SRanjani Sridharan 18763acd5270SRanjani Sridharan msg.primary = header; 18773acd5270SRanjani Sridharan msg.extension = extension; 18783acd5270SRanjani Sridharan 18793acd5270SRanjani Sridharan ret = sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0); 18803acd5270SRanjani Sridharan if (ret < 0) 1881b796ff3bSRanjani Sridharan dev_err(sdev->dev, "failed to unbind modules %s:%d -> %s:%d\n", 1882b796ff3bSRanjani Sridharan src_widget->widget->name, sroute->src_queue_id, 1883b796ff3bSRanjani Sridharan sink_widget->widget->name, sroute->dst_queue_id); 18849a62d87aSRanjani Sridharan out: 1885bb79f2a6SRanjani Sridharan sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id, SOF_PIN_TYPE_INPUT); 1886bb79f2a6SRanjani Sridharan sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_OUTPUT); 1887c84443dbSChao Song 18883acd5270SRanjani Sridharan return ret; 18893acd5270SRanjani Sridharan } 18903acd5270SRanjani Sridharan 1891acf48a1fSRanjani Sridharan static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, 1892acf48a1fSRanjani Sridharan unsigned int flags, struct snd_sof_dai_config_data *data) 1893acf48a1fSRanjani Sridharan { 18949c04363dSRanjani Sridharan struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; 1895acf48a1fSRanjani Sridharan struct sof_ipc4_pipeline *pipeline = pipe_widget->private; 1896acf48a1fSRanjani Sridharan struct snd_sof_dai *dai = swidget->private; 1897acf48a1fSRanjani Sridharan struct sof_ipc4_gtw_attributes *gtw_attr; 1898acf48a1fSRanjani Sridharan struct sof_ipc4_copier_data *copier_data; 1899acf48a1fSRanjani Sridharan struct sof_ipc4_copier *ipc4_copier; 1900acf48a1fSRanjani Sridharan 1901acf48a1fSRanjani Sridharan if (!dai || !dai->private) { 1902acf48a1fSRanjani Sridharan dev_err(sdev->dev, "Invalid DAI or DAI private data for %s\n", 1903acf48a1fSRanjani Sridharan swidget->widget->name); 1904acf48a1fSRanjani Sridharan return -EINVAL; 1905acf48a1fSRanjani Sridharan } 1906acf48a1fSRanjani Sridharan 1907acf48a1fSRanjani Sridharan ipc4_copier = (struct sof_ipc4_copier *)dai->private; 1908acf48a1fSRanjani Sridharan copier_data = &ipc4_copier->data; 1909acf48a1fSRanjani Sridharan 1910acf48a1fSRanjani Sridharan if (!data) 1911acf48a1fSRanjani Sridharan return 0; 1912acf48a1fSRanjani Sridharan 1913acf48a1fSRanjani Sridharan switch (ipc4_copier->dai_type) { 1914acf48a1fSRanjani Sridharan case SOF_DAI_INTEL_HDA: 1915acf48a1fSRanjani Sridharan gtw_attr = ipc4_copier->gtw_attr; 1916acf48a1fSRanjani Sridharan gtw_attr->lp_buffer_alloc = pipeline->lp_mode; 191737a26eecSRanjani Sridharan pipeline->skip_during_fe_trigger = true; 1918acf48a1fSRanjani Sridharan fallthrough; 1919acf48a1fSRanjani Sridharan case SOF_DAI_INTEL_ALH: 1920b66bfc3aSRanjani Sridharan /* 1921b66bfc3aSRanjani Sridharan * Do not clear the node ID when this op is invoked with 1922b66bfc3aSRanjani Sridharan * SOF_DAI_CONFIG_FLAGS_HW_FREE. It is needed to free the group_ida during 1923b66bfc3aSRanjani Sridharan * unprepare. 1924b66bfc3aSRanjani Sridharan */ 1925b66bfc3aSRanjani Sridharan if (flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS) { 1926acf48a1fSRanjani Sridharan copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK; 1927acf48a1fSRanjani Sridharan copier_data->gtw_cfg.node_id |= SOF_IPC4_NODE_INDEX(data->dai_data); 1928b66bfc3aSRanjani Sridharan } 1929acf48a1fSRanjani Sridharan break; 1930acf48a1fSRanjani Sridharan case SOF_DAI_INTEL_DMIC: 1931acf48a1fSRanjani Sridharan case SOF_DAI_INTEL_SSP: 1932acf48a1fSRanjani Sridharan /* nothing to do for SSP/DMIC */ 1933acf48a1fSRanjani Sridharan break; 1934acf48a1fSRanjani Sridharan default: 1935acf48a1fSRanjani Sridharan dev_err(sdev->dev, "%s: unsupported dai type %d\n", __func__, 1936acf48a1fSRanjani Sridharan ipc4_copier->dai_type); 1937acf48a1fSRanjani Sridharan return -EINVAL; 1938acf48a1fSRanjani Sridharan } 1939acf48a1fSRanjani Sridharan 1940acf48a1fSRanjani Sridharan return 0; 1941acf48a1fSRanjani Sridharan } 1942acf48a1fSRanjani Sridharan 1943323aa1f0SRanjani Sridharan static int sof_ipc4_parse_manifest(struct snd_soc_component *scomp, int index, 1944323aa1f0SRanjani Sridharan struct snd_soc_tplg_manifest *man) 1945323aa1f0SRanjani Sridharan { 1946323aa1f0SRanjani Sridharan struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 1947323aa1f0SRanjani Sridharan struct sof_ipc4_fw_data *ipc4_data = sdev->private; 1948323aa1f0SRanjani Sridharan struct sof_manifest_tlv *manifest_tlv; 1949323aa1f0SRanjani Sridharan struct sof_manifest *manifest; 1950323aa1f0SRanjani Sridharan u32 size = le32_to_cpu(man->priv.size); 1951323aa1f0SRanjani Sridharan u8 *man_ptr = man->priv.data; 1952323aa1f0SRanjani Sridharan u32 len_check; 1953323aa1f0SRanjani Sridharan int i; 1954323aa1f0SRanjani Sridharan 1955323aa1f0SRanjani Sridharan if (!size || size < SOF_IPC4_TPLG_ABI_SIZE) { 1956323aa1f0SRanjani Sridharan dev_err(scomp->dev, "%s: Invalid topology ABI size: %u\n", 1957323aa1f0SRanjani Sridharan __func__, size); 1958323aa1f0SRanjani Sridharan return -EINVAL; 1959323aa1f0SRanjani Sridharan } 1960323aa1f0SRanjani Sridharan 1961323aa1f0SRanjani Sridharan manifest = (struct sof_manifest *)man_ptr; 1962323aa1f0SRanjani Sridharan 1963323aa1f0SRanjani Sridharan dev_info(scomp->dev, 1964323aa1f0SRanjani Sridharan "Topology: ABI %d:%d:%d Kernel ABI %u:%u:%u\n", 1965323aa1f0SRanjani Sridharan le16_to_cpu(manifest->abi_major), le16_to_cpu(manifest->abi_minor), 1966323aa1f0SRanjani Sridharan le16_to_cpu(manifest->abi_patch), 1967323aa1f0SRanjani Sridharan SOF_ABI_MAJOR, SOF_ABI_MINOR, SOF_ABI_PATCH); 1968323aa1f0SRanjani Sridharan 1969323aa1f0SRanjani Sridharan /* TODO: Add ABI compatibility check */ 1970323aa1f0SRanjani Sridharan 1971323aa1f0SRanjani Sridharan /* no more data after the ABI version */ 1972323aa1f0SRanjani Sridharan if (size <= SOF_IPC4_TPLG_ABI_SIZE) 1973323aa1f0SRanjani Sridharan return 0; 1974323aa1f0SRanjani Sridharan 1975323aa1f0SRanjani Sridharan manifest_tlv = manifest->items; 1976323aa1f0SRanjani Sridharan len_check = sizeof(struct sof_manifest); 1977323aa1f0SRanjani Sridharan for (i = 0; i < le16_to_cpu(manifest->count); i++) { 1978323aa1f0SRanjani Sridharan len_check += sizeof(struct sof_manifest_tlv) + le32_to_cpu(manifest_tlv->size); 1979323aa1f0SRanjani Sridharan if (len_check > size) 1980323aa1f0SRanjani Sridharan return -EINVAL; 1981323aa1f0SRanjani Sridharan 1982323aa1f0SRanjani Sridharan switch (le32_to_cpu(manifest_tlv->type)) { 1983323aa1f0SRanjani Sridharan case SOF_MANIFEST_DATA_TYPE_NHLT: 1984323aa1f0SRanjani Sridharan /* no NHLT in BIOS, so use the one from topology manifest */ 1985323aa1f0SRanjani Sridharan if (ipc4_data->nhlt) 1986323aa1f0SRanjani Sridharan break; 1987323aa1f0SRanjani Sridharan ipc4_data->nhlt = devm_kmemdup(sdev->dev, manifest_tlv->data, 1988323aa1f0SRanjani Sridharan le32_to_cpu(manifest_tlv->size), GFP_KERNEL); 1989323aa1f0SRanjani Sridharan if (!ipc4_data->nhlt) 1990323aa1f0SRanjani Sridharan return -ENOMEM; 1991323aa1f0SRanjani Sridharan break; 1992323aa1f0SRanjani Sridharan default: 1993323aa1f0SRanjani Sridharan dev_warn(scomp->dev, "Skipping unknown manifest data type %d\n", 1994323aa1f0SRanjani Sridharan manifest_tlv->type); 1995323aa1f0SRanjani Sridharan break; 1996323aa1f0SRanjani Sridharan } 1997323aa1f0SRanjani Sridharan man_ptr += sizeof(struct sof_manifest_tlv) + le32_to_cpu(manifest_tlv->size); 1998323aa1f0SRanjani Sridharan manifest_tlv = (struct sof_manifest_tlv *)man_ptr; 1999323aa1f0SRanjani Sridharan } 2000323aa1f0SRanjani Sridharan 2001323aa1f0SRanjani Sridharan return 0; 2002323aa1f0SRanjani Sridharan } 2003323aa1f0SRanjani Sridharan 20049e2b5d33SRanjani Sridharan static int sof_ipc4_dai_get_clk(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int clk_type) 20059e2b5d33SRanjani Sridharan { 20069e2b5d33SRanjani Sridharan struct sof_ipc4_copier *ipc4_copier = dai->private; 20079e2b5d33SRanjani Sridharan struct snd_soc_tplg_hw_config *hw_config; 20089e2b5d33SRanjani Sridharan struct snd_sof_dai_link *slink; 20099e2b5d33SRanjani Sridharan bool dai_link_found = false; 20109e2b5d33SRanjani Sridharan bool hw_cfg_found = false; 20119e2b5d33SRanjani Sridharan int i; 20129e2b5d33SRanjani Sridharan 20139e2b5d33SRanjani Sridharan if (!ipc4_copier) 20149e2b5d33SRanjani Sridharan return 0; 20159e2b5d33SRanjani Sridharan 20169e2b5d33SRanjani Sridharan list_for_each_entry(slink, &sdev->dai_link_list, list) { 20179e2b5d33SRanjani Sridharan if (!strcmp(slink->link->name, dai->name)) { 20189e2b5d33SRanjani Sridharan dai_link_found = true; 20199e2b5d33SRanjani Sridharan break; 20209e2b5d33SRanjani Sridharan } 20219e2b5d33SRanjani Sridharan } 20229e2b5d33SRanjani Sridharan 20239e2b5d33SRanjani Sridharan if (!dai_link_found) { 20249e2b5d33SRanjani Sridharan dev_err(sdev->dev, "no DAI link found for DAI %s\n", dai->name); 20259e2b5d33SRanjani Sridharan return -EINVAL; 20269e2b5d33SRanjani Sridharan } 20279e2b5d33SRanjani Sridharan 20289e2b5d33SRanjani Sridharan for (i = 0; i < slink->num_hw_configs; i++) { 20299e2b5d33SRanjani Sridharan hw_config = &slink->hw_configs[i]; 20309e2b5d33SRanjani Sridharan if (dai->current_config == le32_to_cpu(hw_config->id)) { 20319e2b5d33SRanjani Sridharan hw_cfg_found = true; 20329e2b5d33SRanjani Sridharan break; 20339e2b5d33SRanjani Sridharan } 20349e2b5d33SRanjani Sridharan } 20359e2b5d33SRanjani Sridharan 20369e2b5d33SRanjani Sridharan if (!hw_cfg_found) { 20379e2b5d33SRanjani Sridharan dev_err(sdev->dev, "no matching hw_config found for DAI %s\n", dai->name); 20389e2b5d33SRanjani Sridharan return -EINVAL; 20399e2b5d33SRanjani Sridharan } 20409e2b5d33SRanjani Sridharan 20419e2b5d33SRanjani Sridharan switch (ipc4_copier->dai_type) { 20429e2b5d33SRanjani Sridharan case SOF_DAI_INTEL_SSP: 20439e2b5d33SRanjani Sridharan switch (clk_type) { 20449e2b5d33SRanjani Sridharan case SOF_DAI_CLK_INTEL_SSP_MCLK: 20459e2b5d33SRanjani Sridharan return le32_to_cpu(hw_config->mclk_rate); 20469e2b5d33SRanjani Sridharan case SOF_DAI_CLK_INTEL_SSP_BCLK: 20479e2b5d33SRanjani Sridharan return le32_to_cpu(hw_config->bclk_rate); 20489e2b5d33SRanjani Sridharan default: 20499e2b5d33SRanjani Sridharan dev_err(sdev->dev, "Invalid clk type for SSP %d\n", clk_type); 20509e2b5d33SRanjani Sridharan break; 20519e2b5d33SRanjani Sridharan } 20529e2b5d33SRanjani Sridharan break; 20539e2b5d33SRanjani Sridharan default: 20549e2b5d33SRanjani Sridharan dev_err(sdev->dev, "DAI type %d not supported yet!\n", ipc4_copier->dai_type); 20559e2b5d33SRanjani Sridharan break; 20569e2b5d33SRanjani Sridharan } 20579e2b5d33SRanjani Sridharan 20589e2b5d33SRanjani Sridharan return -EINVAL; 20599e2b5d33SRanjani Sridharan } 20609e2b5d33SRanjani Sridharan 206118cd1f32SPeter Ujfalusi static int sof_ipc4_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verify) 206218cd1f32SPeter Ujfalusi { 206318cd1f32SPeter Ujfalusi struct snd_sof_pcm *spcm; 206418cd1f32SPeter Ujfalusi int dir, ret; 206518cd1f32SPeter Ujfalusi 206618cd1f32SPeter Ujfalusi /* 206718cd1f32SPeter Ujfalusi * This function is called during system suspend, we need to make sure 206818cd1f32SPeter Ujfalusi * that all streams have been freed up. 206918cd1f32SPeter Ujfalusi * Freeing might have been skipped when xrun happened just at the start 207018cd1f32SPeter Ujfalusi * of the suspend and it sent a SNDRV_PCM_TRIGGER_STOP to the active 207118cd1f32SPeter Ujfalusi * stream. This will call sof_pcm_stream_free() with 207218cd1f32SPeter Ujfalusi * free_widget_list = false which will leave the kernel and firmware out 207318cd1f32SPeter Ujfalusi * of sync during suspend/resume. 207418cd1f32SPeter Ujfalusi * 207518cd1f32SPeter Ujfalusi * This will also make sure that paused streams handled correctly. 207618cd1f32SPeter Ujfalusi */ 207718cd1f32SPeter Ujfalusi list_for_each_entry(spcm, &sdev->pcm_list, list) { 207818cd1f32SPeter Ujfalusi for_each_pcm_streams(dir) { 207918cd1f32SPeter Ujfalusi struct snd_pcm_substream *substream = spcm->stream[dir].substream; 208018cd1f32SPeter Ujfalusi 208182b18242SRanjani Sridharan if (!substream || !substream->runtime || spcm->stream[dir].suspend_ignored) 208218cd1f32SPeter Ujfalusi continue; 208318cd1f32SPeter Ujfalusi 208418cd1f32SPeter Ujfalusi if (spcm->stream[dir].list) { 208518cd1f32SPeter Ujfalusi ret = sof_pcm_stream_free(sdev, substream, spcm, dir, true); 208618cd1f32SPeter Ujfalusi if (ret < 0) 208718cd1f32SPeter Ujfalusi return ret; 208818cd1f32SPeter Ujfalusi } 208918cd1f32SPeter Ujfalusi } 209018cd1f32SPeter Ujfalusi } 209118cd1f32SPeter Ujfalusi return 0; 209218cd1f32SPeter Ujfalusi } 209318cd1f32SPeter Ujfalusi 2094e380c907SRanjani Sridharan static int sof_ipc4_link_setup(struct snd_sof_dev *sdev, struct snd_soc_dai_link *link) 2095e380c907SRanjani Sridharan { 2096e380c907SRanjani Sridharan if (link->no_pcm) 2097e380c907SRanjani Sridharan return 0; 2098e380c907SRanjani Sridharan 2099e380c907SRanjani Sridharan /* 2100e380c907SRanjani Sridharan * set default trigger order for all links. Exceptions to 2101e380c907SRanjani Sridharan * the rule will be handled in sof_pcm_dai_link_fixup() 2102e380c907SRanjani Sridharan * For playback, the sequence is the following: start BE, 2103e380c907SRanjani Sridharan * start FE, stop FE, stop BE; for Capture the sequence is 2104e380c907SRanjani Sridharan * inverted start FE, start BE, stop BE, stop FE 2105e380c907SRanjani Sridharan */ 2106e380c907SRanjani Sridharan link->trigger[SNDRV_PCM_STREAM_PLAYBACK] = SND_SOC_DPCM_TRIGGER_POST; 2107e380c907SRanjani Sridharan link->trigger[SNDRV_PCM_STREAM_CAPTURE] = SND_SOC_DPCM_TRIGGER_PRE; 2108e380c907SRanjani Sridharan 2109e380c907SRanjani Sridharan return 0; 2110e380c907SRanjani Sridharan } 2111e380c907SRanjani Sridharan 21127d573425SBard Liao static enum sof_tokens common_copier_token_list[] = { 21132cabd02bSRanjani Sridharan SOF_COMP_TOKENS, 21142cabd02bSRanjani Sridharan SOF_AUDIO_FMT_NUM_TOKENS, 21152cabd02bSRanjani Sridharan SOF_IN_AUDIO_FORMAT_TOKENS, 21162cabd02bSRanjani Sridharan SOF_OUT_AUDIO_FORMAT_TOKENS, 2117594c1bb9SRanjani Sridharan SOF_COPIER_DEEP_BUFFER_TOKENS, 21182cabd02bSRanjani Sridharan SOF_COPIER_TOKENS, 21192cabd02bSRanjani Sridharan SOF_COMP_EXT_TOKENS, 21202cabd02bSRanjani Sridharan }; 21212cabd02bSRanjani Sridharan 212290e89155SRanjani Sridharan static enum sof_tokens pipeline_token_list[] = { 212390e89155SRanjani Sridharan SOF_SCHED_TOKENS, 212490e89155SRanjani Sridharan SOF_PIPELINE_TOKENS, 212590e89155SRanjani Sridharan }; 212690e89155SRanjani Sridharan 2127abfb536bSRanjani Sridharan static enum sof_tokens dai_token_list[] = { 2128abfb536bSRanjani Sridharan SOF_COMP_TOKENS, 2129abfb536bSRanjani Sridharan SOF_AUDIO_FMT_NUM_TOKENS, 2130abfb536bSRanjani Sridharan SOF_IN_AUDIO_FORMAT_TOKENS, 2131abfb536bSRanjani Sridharan SOF_OUT_AUDIO_FORMAT_TOKENS, 2132abfb536bSRanjani Sridharan SOF_COPIER_TOKENS, 2133abfb536bSRanjani Sridharan SOF_DAI_TOKENS, 2134abfb536bSRanjani Sridharan SOF_COMP_EXT_TOKENS, 2135abfb536bSRanjani Sridharan }; 2136abfb536bSRanjani Sridharan 21374f838ab2SRanjani Sridharan static enum sof_tokens pga_token_list[] = { 21384f838ab2SRanjani Sridharan SOF_COMP_TOKENS, 21394f838ab2SRanjani Sridharan SOF_GAIN_TOKENS, 21404f838ab2SRanjani Sridharan SOF_AUDIO_FMT_NUM_TOKENS, 21414f838ab2SRanjani Sridharan SOF_IN_AUDIO_FORMAT_TOKENS, 21427ab6b1e8SRanjani Sridharan SOF_OUT_AUDIO_FORMAT_TOKENS, 21434f838ab2SRanjani Sridharan SOF_COMP_EXT_TOKENS, 21444f838ab2SRanjani Sridharan }; 21454f838ab2SRanjani Sridharan 21464d4ba014SRanjani Sridharan static enum sof_tokens mixer_token_list[] = { 21474d4ba014SRanjani Sridharan SOF_COMP_TOKENS, 21484d4ba014SRanjani Sridharan SOF_AUDIO_FMT_NUM_TOKENS, 21494d4ba014SRanjani Sridharan SOF_IN_AUDIO_FORMAT_TOKENS, 21507ab6b1e8SRanjani Sridharan SOF_OUT_AUDIO_FORMAT_TOKENS, 21514d4ba014SRanjani Sridharan SOF_COMP_EXT_TOKENS, 21524d4ba014SRanjani Sridharan }; 21534d4ba014SRanjani Sridharan 2154b85f4fc4SRander Wang static enum sof_tokens src_token_list[] = { 2155b85f4fc4SRander Wang SOF_COMP_TOKENS, 2156b85f4fc4SRander Wang SOF_SRC_TOKENS, 2157b85f4fc4SRander Wang SOF_AUDIO_FMT_NUM_TOKENS, 2158b85f4fc4SRander Wang SOF_IN_AUDIO_FORMAT_TOKENS, 21597ab6b1e8SRanjani Sridharan SOF_OUT_AUDIO_FORMAT_TOKENS, 2160b85f4fc4SRander Wang SOF_COMP_EXT_TOKENS, 2161b85f4fc4SRander Wang }; 2162b85f4fc4SRander Wang 216390e89155SRanjani Sridharan static const struct sof_ipc_tplg_widget_ops tplg_ipc4_widget_ops[SND_SOC_DAPM_TYPE_COUNT] = { 21642cabd02bSRanjani Sridharan [snd_soc_dapm_aif_in] = {sof_ipc4_widget_setup_pcm, sof_ipc4_widget_free_comp_pcm, 21657d573425SBard Liao common_copier_token_list, ARRAY_SIZE(common_copier_token_list), 21667d573425SBard Liao NULL, sof_ipc4_prepare_copier_module, 2167904c48c4SRanjani Sridharan sof_ipc4_unprepare_copier_module}, 21682cabd02bSRanjani Sridharan [snd_soc_dapm_aif_out] = {sof_ipc4_widget_setup_pcm, sof_ipc4_widget_free_comp_pcm, 21697d573425SBard Liao common_copier_token_list, ARRAY_SIZE(common_copier_token_list), 21707d573425SBard Liao NULL, sof_ipc4_prepare_copier_module, 2171904c48c4SRanjani Sridharan sof_ipc4_unprepare_copier_module}, 2172abfb536bSRanjani Sridharan [snd_soc_dapm_dai_in] = {sof_ipc4_widget_setup_comp_dai, sof_ipc4_widget_free_comp_dai, 2173acf52594SRanjani Sridharan dai_token_list, ARRAY_SIZE(dai_token_list), NULL, 2174acf52594SRanjani Sridharan sof_ipc4_prepare_copier_module, 2175acf52594SRanjani Sridharan sof_ipc4_unprepare_copier_module}, 2176abfb536bSRanjani Sridharan [snd_soc_dapm_dai_out] = {sof_ipc4_widget_setup_comp_dai, sof_ipc4_widget_free_comp_dai, 2177acf52594SRanjani Sridharan dai_token_list, ARRAY_SIZE(dai_token_list), NULL, 2178acf52594SRanjani Sridharan sof_ipc4_prepare_copier_module, 2179acf52594SRanjani Sridharan sof_ipc4_unprepare_copier_module}, 21807d573425SBard Liao [snd_soc_dapm_buffer] = {sof_ipc4_widget_setup_pcm, sof_ipc4_widget_free_comp_pcm, 21817d573425SBard Liao common_copier_token_list, ARRAY_SIZE(common_copier_token_list), 21827d573425SBard Liao NULL, sof_ipc4_prepare_copier_module, 21837d573425SBard Liao sof_ipc4_unprepare_copier_module}, 2184a29b2d02SBard Liao [snd_soc_dapm_scheduler] = {sof_ipc4_widget_setup_comp_pipeline, 2185a29b2d02SBard Liao sof_ipc4_widget_free_comp_pipeline, 218690e89155SRanjani Sridharan pipeline_token_list, ARRAY_SIZE(pipeline_token_list), NULL, 218790e89155SRanjani Sridharan NULL, NULL}, 2188dc4fc0aeSLibin Yang [snd_soc_dapm_pga] = {sof_ipc4_widget_setup_comp_pga, sof_ipc4_widget_free_comp_pga, 21894f838ab2SRanjani Sridharan pga_token_list, ARRAY_SIZE(pga_token_list), NULL, 21904f838ab2SRanjani Sridharan sof_ipc4_prepare_gain_module, 2191711d0427SBard Liao NULL}, 2192dc4fc0aeSLibin Yang [snd_soc_dapm_mixer] = {sof_ipc4_widget_setup_comp_mixer, sof_ipc4_widget_free_comp_mixer, 21934d4ba014SRanjani Sridharan mixer_token_list, ARRAY_SIZE(mixer_token_list), 21944d4ba014SRanjani Sridharan NULL, sof_ipc4_prepare_mixer_module, 2195711d0427SBard Liao NULL}, 2196b85f4fc4SRander Wang [snd_soc_dapm_src] = {sof_ipc4_widget_setup_comp_src, sof_ipc4_widget_free_comp_src, 2197b85f4fc4SRander Wang src_token_list, ARRAY_SIZE(src_token_list), 2198b85f4fc4SRander Wang NULL, sof_ipc4_prepare_src_module, 2199b85f4fc4SRander Wang NULL}, 220090e89155SRanjani Sridharan }; 220190e89155SRanjani Sridharan 220290e89155SRanjani Sridharan const struct sof_ipc_tplg_ops ipc4_tplg_ops = { 220390e89155SRanjani Sridharan .widget = tplg_ipc4_widget_ops, 220490e89155SRanjani Sridharan .token_list = ipc4_token_list, 2205d97964f8SRanjani Sridharan .control_setup = sof_ipc4_control_setup, 2206955e84fcSRanjani Sridharan .control = &tplg_ipc4_control_ops, 22076e9257a1SRanjani Sridharan .widget_setup = sof_ipc4_widget_setup, 22086e9257a1SRanjani Sridharan .widget_free = sof_ipc4_widget_free, 22093acd5270SRanjani Sridharan .route_setup = sof_ipc4_route_setup, 22103acd5270SRanjani Sridharan .route_free = sof_ipc4_route_free, 2211acf48a1fSRanjani Sridharan .dai_config = sof_ipc4_dai_config, 2212323aa1f0SRanjani Sridharan .parse_manifest = sof_ipc4_parse_manifest, 22139e2b5d33SRanjani Sridharan .dai_get_clk = sof_ipc4_dai_get_clk, 221418cd1f32SPeter Ujfalusi .tear_down_all_pipelines = sof_ipc4_tear_down_all_pipelines, 2215e380c907SRanjani Sridharan .link_setup = sof_ipc4_link_setup, 221690e89155SRanjani Sridharan }; 2217