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