3444 lines · c
1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)2//3// This file is provided under a dual BSD/GPLv2 license. When using or4// redistributing this file, you may do so under either license.5//6// Copyright(c) 2022 Intel Corporation7//8//9#include <linux/bitfield.h>10#include <uapi/sound/sof/tokens.h>11#include <sound/pcm_params.h>12#include <sound/sof/ext_manifest4.h>13#include <sound/intel-nhlt.h>14#include "sof-priv.h"15#include "sof-audio.h"16#include "ipc4-priv.h"17#include "ipc4-topology.h"18#include "ops.h"19 20/*21 * The ignore_cpc flag can be used to ignore the CPC value for all modules by22 * using 0 instead.23 * The CPC is sent to the firmware along with the SOF_IPC4_MOD_INIT_INSTANCE24 * message and it is used for clock scaling.25 * 0 as CPC value will instruct the firmware to use maximum frequency, thus26 * deactivating the clock scaling.27 */28static bool ignore_cpc;29module_param_named(ipc4_ignore_cpc, ignore_cpc, bool, 0444);30MODULE_PARM_DESC(ipc4_ignore_cpc,31 "Ignore CPC values. This option will disable clock scaling in firmware.");32 33#define SOF_IPC4_GAIN_PARAM_ID 034#define SOF_IPC4_TPLG_ABI_SIZE 635#define SOF_IPC4_CHAIN_DMA_BUF_SIZE_MS 236 37static DEFINE_IDA(alh_group_ida);38static DEFINE_IDA(pipeline_ida);39 40static const struct sof_topology_token ipc4_sched_tokens[] = {41 {SOF_TKN_SCHED_LP_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,42 offsetof(struct sof_ipc4_pipeline, lp_mode)},43 {SOF_TKN_SCHED_USE_CHAIN_DMA, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16,44 offsetof(struct sof_ipc4_pipeline, use_chain_dma)},45 {SOF_TKN_SCHED_CORE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,46 offsetof(struct sof_ipc4_pipeline, core_id)},47 {SOF_TKN_SCHED_PRIORITY, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,48 offsetof(struct sof_ipc4_pipeline, priority)},49};50 51static const struct sof_topology_token pipeline_tokens[] = {52 {SOF_TKN_SCHED_DYNAMIC_PIPELINE, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16,53 offsetof(struct snd_sof_widget, dynamic_pipeline_widget)},54};55 56static const struct sof_topology_token ipc4_comp_tokens[] = {57 {SOF_TKN_COMP_IS_PAGES, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,58 offsetof(struct sof_ipc4_base_module_cfg, is_pages)},59};60 61static const struct sof_topology_token ipc4_in_audio_format_tokens[] = {62 {SOF_TKN_CAVS_AUDIO_FORMAT_IN_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,63 offsetof(struct sof_ipc4_pin_format, audio_fmt.sampling_frequency)},64 {SOF_TKN_CAVS_AUDIO_FORMAT_IN_BIT_DEPTH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,65 offsetof(struct sof_ipc4_pin_format, audio_fmt.bit_depth)},66 {SOF_TKN_CAVS_AUDIO_FORMAT_IN_CH_MAP, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,67 offsetof(struct sof_ipc4_pin_format, audio_fmt.ch_map)},68 {SOF_TKN_CAVS_AUDIO_FORMAT_IN_CH_CFG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,69 offsetof(struct sof_ipc4_pin_format, audio_fmt.ch_cfg)},70 {SOF_TKN_CAVS_AUDIO_FORMAT_IN_INTERLEAVING_STYLE, SND_SOC_TPLG_TUPLE_TYPE_WORD,71 get_token_u32, offsetof(struct sof_ipc4_pin_format,72 audio_fmt.interleaving_style)},73 {SOF_TKN_CAVS_AUDIO_FORMAT_IN_FMT_CFG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,74 offsetof(struct sof_ipc4_pin_format, audio_fmt.fmt_cfg)},75 {SOF_TKN_CAVS_AUDIO_FORMAT_INPUT_PIN_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,76 offsetof(struct sof_ipc4_pin_format, pin_index)},77 {SOF_TKN_CAVS_AUDIO_FORMAT_IBS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,78 offsetof(struct sof_ipc4_pin_format, buffer_size)},79};80 81static const struct sof_topology_token ipc4_out_audio_format_tokens[] = {82 {SOF_TKN_CAVS_AUDIO_FORMAT_OUT_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,83 offsetof(struct sof_ipc4_pin_format, audio_fmt.sampling_frequency)},84 {SOF_TKN_CAVS_AUDIO_FORMAT_OUT_BIT_DEPTH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,85 offsetof(struct sof_ipc4_pin_format, audio_fmt.bit_depth)},86 {SOF_TKN_CAVS_AUDIO_FORMAT_OUT_CH_MAP, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,87 offsetof(struct sof_ipc4_pin_format, audio_fmt.ch_map)},88 {SOF_TKN_CAVS_AUDIO_FORMAT_OUT_CH_CFG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,89 offsetof(struct sof_ipc4_pin_format, audio_fmt.ch_cfg)},90 {SOF_TKN_CAVS_AUDIO_FORMAT_OUT_INTERLEAVING_STYLE, SND_SOC_TPLG_TUPLE_TYPE_WORD,91 get_token_u32, offsetof(struct sof_ipc4_pin_format,92 audio_fmt.interleaving_style)},93 {SOF_TKN_CAVS_AUDIO_FORMAT_OUT_FMT_CFG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,94 offsetof(struct sof_ipc4_pin_format, audio_fmt.fmt_cfg)},95 {SOF_TKN_CAVS_AUDIO_FORMAT_OUTPUT_PIN_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,96 offsetof(struct sof_ipc4_pin_format, pin_index)},97 {SOF_TKN_CAVS_AUDIO_FORMAT_OBS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,98 offsetof(struct sof_ipc4_pin_format, buffer_size)},99};100 101static const struct sof_topology_token ipc4_copier_deep_buffer_tokens[] = {102 {SOF_TKN_INTEL_COPIER_DEEP_BUFFER_DMA_MS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 0},103};104 105static const struct sof_topology_token ipc4_copier_tokens[] = {106 {SOF_TKN_INTEL_COPIER_NODE_TYPE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 0},107};108 109static const struct sof_topology_token ipc4_audio_fmt_num_tokens[] = {110 {SOF_TKN_COMP_NUM_INPUT_AUDIO_FORMATS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,111 offsetof(struct sof_ipc4_available_audio_format, num_input_formats)},112 {SOF_TKN_COMP_NUM_OUTPUT_AUDIO_FORMATS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,113 offsetof(struct sof_ipc4_available_audio_format, num_output_formats)},114};115 116static const struct sof_topology_token dai_tokens[] = {117 {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type,118 offsetof(struct sof_ipc4_copier, dai_type)},119 {SOF_TKN_DAI_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,120 offsetof(struct sof_ipc4_copier, dai_index)},121};122 123/* Component extended tokens */124static const struct sof_topology_token comp_ext_tokens[] = {125 {SOF_TKN_COMP_UUID, SND_SOC_TPLG_TUPLE_TYPE_UUID, get_token_uuid,126 offsetof(struct snd_sof_widget, uuid)},127 {SOF_TKN_COMP_CORE_ID, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,128 offsetof(struct snd_sof_widget, core)},129};130 131static const struct sof_topology_token gain_tokens[] = {132 {SOF_TKN_GAIN_RAMP_TYPE, SND_SOC_TPLG_TUPLE_TYPE_WORD,133 get_token_u32, offsetof(struct sof_ipc4_gain_params, curve_type)},134 {SOF_TKN_GAIN_RAMP_DURATION,135 SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,136 offsetof(struct sof_ipc4_gain_params, curve_duration_l)},137 {SOF_TKN_GAIN_VAL, SND_SOC_TPLG_TUPLE_TYPE_WORD,138 get_token_u32, offsetof(struct sof_ipc4_gain_params, init_val)},139};140 141/* SRC */142static const struct sof_topology_token src_tokens[] = {143 {SOF_TKN_SRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,144 offsetof(struct sof_ipc4_src_data, sink_rate)},145};146 147static const struct sof_token_info ipc4_token_list[SOF_TOKEN_COUNT] = {148 [SOF_DAI_TOKENS] = {"DAI tokens", dai_tokens, ARRAY_SIZE(dai_tokens)},149 [SOF_PIPELINE_TOKENS] = {"Pipeline tokens", pipeline_tokens, ARRAY_SIZE(pipeline_tokens)},150 [SOF_SCHED_TOKENS] = {"Scheduler tokens", ipc4_sched_tokens,151 ARRAY_SIZE(ipc4_sched_tokens)},152 [SOF_COMP_EXT_TOKENS] = {"Comp extended tokens", comp_ext_tokens,153 ARRAY_SIZE(comp_ext_tokens)},154 [SOF_COMP_TOKENS] = {"IPC4 Component tokens",155 ipc4_comp_tokens, ARRAY_SIZE(ipc4_comp_tokens)},156 [SOF_IN_AUDIO_FORMAT_TOKENS] = {"IPC4 Input Audio format tokens",157 ipc4_in_audio_format_tokens, ARRAY_SIZE(ipc4_in_audio_format_tokens)},158 [SOF_OUT_AUDIO_FORMAT_TOKENS] = {"IPC4 Output Audio format tokens",159 ipc4_out_audio_format_tokens, ARRAY_SIZE(ipc4_out_audio_format_tokens)},160 [SOF_COPIER_DEEP_BUFFER_TOKENS] = {"IPC4 Copier deep buffer tokens",161 ipc4_copier_deep_buffer_tokens, ARRAY_SIZE(ipc4_copier_deep_buffer_tokens)},162 [SOF_COPIER_TOKENS] = {"IPC4 Copier tokens", ipc4_copier_tokens,163 ARRAY_SIZE(ipc4_copier_tokens)},164 [SOF_AUDIO_FMT_NUM_TOKENS] = {"IPC4 Audio format number tokens",165 ipc4_audio_fmt_num_tokens, ARRAY_SIZE(ipc4_audio_fmt_num_tokens)},166 [SOF_GAIN_TOKENS] = {"Gain tokens", gain_tokens, ARRAY_SIZE(gain_tokens)},167 [SOF_SRC_TOKENS] = {"SRC tokens", src_tokens, ARRAY_SIZE(src_tokens)},168};169 170struct snd_sof_widget *sof_ipc4_find_swidget_by_ids(struct snd_sof_dev *sdev,171 u32 module_id, int instance_id)172{173 struct snd_sof_widget *swidget;174 175 list_for_each_entry(swidget, &sdev->widget_list, list) {176 struct sof_ipc4_fw_module *fw_module = swidget->module_info;177 178 /* Only active module instances have valid instance_id */179 if (!swidget->use_count)180 continue;181 182 if (fw_module && fw_module->man4_module_entry.id == module_id &&183 swidget->instance_id == instance_id)184 return swidget;185 }186 187 return NULL;188}189 190static void sof_ipc4_dbg_audio_format(struct device *dev, struct sof_ipc4_pin_format *pin_fmt,191 int num_formats)192{193 int i;194 195 for (i = 0; i < num_formats; i++) {196 struct sof_ipc4_audio_format *fmt = &pin_fmt[i].audio_fmt;197 dev_dbg(dev,198 "Pin index #%d: %uHz, %ubit, %luch (ch_map %#x ch_cfg %u interleaving_style %u fmt_cfg %#x) buffer size %d\n",199 pin_fmt[i].pin_index, fmt->sampling_frequency, fmt->bit_depth,200 SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg),201 fmt->ch_map, fmt->ch_cfg, fmt->interleaving_style, fmt->fmt_cfg,202 pin_fmt[i].buffer_size);203 }204}205 206static const struct sof_ipc4_audio_format *207sof_ipc4_get_input_pin_audio_fmt(struct snd_sof_widget *swidget, int pin_index)208{209 struct sof_ipc4_base_module_cfg_ext *base_cfg_ext;210 struct sof_ipc4_process *process;211 int i;212 213 if (swidget->id != snd_soc_dapm_effect) {214 struct sof_ipc4_base_module_cfg *base = swidget->private;215 216 /* For non-process modules, base module config format is used for all input pins */217 return &base->audio_fmt;218 }219 220 process = swidget->private;221 222 /*223 * For process modules without base config extension, base module config224 * format is used for all input pins225 */226 if (process->init_config != SOF_IPC4_MODULE_INIT_CONFIG_TYPE_BASE_CFG_WITH_EXT)227 return &process->base_config.audio_fmt;228 229 base_cfg_ext = process->base_config_ext;230 231 /*232 * If there are multiple input formats available for a pin, the first available format233 * is chosen.234 */235 for (i = 0; i < base_cfg_ext->num_input_pin_fmts; i++) {236 struct sof_ipc4_pin_format *pin_format = &base_cfg_ext->pin_formats[i];237 238 if (pin_format->pin_index == pin_index)239 return &pin_format->audio_fmt;240 }241 242 return NULL;243}244 245/**246 * sof_ipc4_get_audio_fmt - get available audio formats from swidget->tuples247 * @scomp: pointer to pointer to SOC component248 * @swidget: pointer to struct snd_sof_widget containing tuples249 * @available_fmt: pointer to struct sof_ipc4_available_audio_format being filling in250 * @module_base_cfg: Pointer to the base_config in the module init IPC payload251 *252 * Return: 0 if successful253 */254static int sof_ipc4_get_audio_fmt(struct snd_soc_component *scomp,255 struct snd_sof_widget *swidget,256 struct sof_ipc4_available_audio_format *available_fmt,257 struct sof_ipc4_base_module_cfg *module_base_cfg)258{259 struct sof_ipc4_pin_format *in_format = NULL;260 struct sof_ipc4_pin_format *out_format;261 int ret;262 263 ret = sof_update_ipc_object(scomp, available_fmt,264 SOF_AUDIO_FMT_NUM_TOKENS, swidget->tuples,265 swidget->num_tuples, sizeof(*available_fmt), 1);266 if (ret) {267 dev_err(scomp->dev, "Failed to parse audio format token count\n");268 return ret;269 }270 271 if (!available_fmt->num_input_formats && !available_fmt->num_output_formats) {272 dev_err(scomp->dev, "No input/output pin formats set in topology\n");273 return -EINVAL;274 }275 276 dev_dbg(scomp->dev,277 "Number of input audio formats: %d. Number of output audio formats: %d\n",278 available_fmt->num_input_formats, available_fmt->num_output_formats);279 280 /* set is_pages in the module's base_config */281 ret = sof_update_ipc_object(scomp, module_base_cfg, SOF_COMP_TOKENS, swidget->tuples,282 swidget->num_tuples, sizeof(*module_base_cfg), 1);283 if (ret) {284 dev_err(scomp->dev, "parse comp tokens for %s failed, error: %d\n",285 swidget->widget->name, ret);286 return ret;287 }288 289 dev_dbg(scomp->dev, "widget %s: is_pages: %d\n", swidget->widget->name,290 module_base_cfg->is_pages);291 292 if (available_fmt->num_input_formats) {293 in_format = kcalloc(available_fmt->num_input_formats,294 sizeof(*in_format), GFP_KERNEL);295 if (!in_format)296 return -ENOMEM;297 available_fmt->input_pin_fmts = in_format;298 299 ret = sof_update_ipc_object(scomp, in_format,300 SOF_IN_AUDIO_FORMAT_TOKENS, swidget->tuples,301 swidget->num_tuples, sizeof(*in_format),302 available_fmt->num_input_formats);303 if (ret) {304 dev_err(scomp->dev, "parse input audio fmt tokens failed %d\n", ret);305 goto err_in;306 }307 308 dev_dbg(scomp->dev, "Input audio formats for %s\n", swidget->widget->name);309 sof_ipc4_dbg_audio_format(scomp->dev, in_format,310 available_fmt->num_input_formats);311 }312 313 if (available_fmt->num_output_formats) {314 out_format = kcalloc(available_fmt->num_output_formats, sizeof(*out_format),315 GFP_KERNEL);316 if (!out_format) {317 ret = -ENOMEM;318 goto err_in;319 }320 321 ret = sof_update_ipc_object(scomp, out_format,322 SOF_OUT_AUDIO_FORMAT_TOKENS, swidget->tuples,323 swidget->num_tuples, sizeof(*out_format),324 available_fmt->num_output_formats);325 if (ret) {326 dev_err(scomp->dev, "parse output audio fmt tokens failed\n");327 goto err_out;328 }329 330 available_fmt->output_pin_fmts = out_format;331 dev_dbg(scomp->dev, "Output audio formats for %s\n", swidget->widget->name);332 sof_ipc4_dbg_audio_format(scomp->dev, out_format,333 available_fmt->num_output_formats);334 }335 336 return 0;337 338err_out:339 kfree(out_format);340err_in:341 kfree(in_format);342 available_fmt->input_pin_fmts = NULL;343 return ret;344}345 346/* release the memory allocated in sof_ipc4_get_audio_fmt */347static void sof_ipc4_free_audio_fmt(struct sof_ipc4_available_audio_format *available_fmt)348 349{350 kfree(available_fmt->output_pin_fmts);351 available_fmt->output_pin_fmts = NULL;352 kfree(available_fmt->input_pin_fmts);353 available_fmt->input_pin_fmts = NULL;354}355 356static void sof_ipc4_widget_free_comp_pipeline(struct snd_sof_widget *swidget)357{358 kfree(swidget->private);359}360 361static int sof_ipc4_widget_set_module_info(struct snd_sof_widget *swidget)362{363 struct snd_soc_component *scomp = swidget->scomp;364 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);365 366 swidget->module_info = sof_ipc4_find_module_by_uuid(sdev, &swidget->uuid);367 368 if (swidget->module_info)369 return 0;370 371 dev_err(sdev->dev, "failed to find module info for widget %s with UUID %pUL\n",372 swidget->widget->name, &swidget->uuid);373 return -EINVAL;374}375 376static int sof_ipc4_widget_setup_msg(struct snd_sof_widget *swidget, struct sof_ipc4_msg *msg)377{378 struct sof_ipc4_fw_module *fw_module;379 uint32_t type;380 int ret;381 382 ret = sof_ipc4_widget_set_module_info(swidget);383 if (ret)384 return ret;385 386 fw_module = swidget->module_info;387 388 msg->primary = fw_module->man4_module_entry.id;389 msg->primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_INIT_INSTANCE);390 msg->primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);391 msg->primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);392 393 msg->extension = SOF_IPC4_MOD_EXT_CORE_ID(swidget->core);394 395 type = (fw_module->man4_module_entry.type & SOF_IPC4_MODULE_DP) ? 1 : 0;396 msg->extension |= SOF_IPC4_MOD_EXT_DOMAIN(type);397 398 return 0;399}400 401static void sof_ipc4_widget_update_kcontrol_module_id(struct snd_sof_widget *swidget)402{403 struct snd_soc_component *scomp = swidget->scomp;404 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);405 struct sof_ipc4_fw_module *fw_module = swidget->module_info;406 struct snd_sof_control *scontrol;407 408 /* update module ID for all kcontrols for this widget */409 list_for_each_entry(scontrol, &sdev->kcontrol_list, list) {410 if (scontrol->comp_id == swidget->comp_id) {411 struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;412 struct sof_ipc4_msg *msg = &cdata->msg;413 414 msg->primary |= fw_module->man4_module_entry.id;415 }416 }417}418 419static int420sof_ipc4_update_card_components_string(struct snd_sof_widget *swidget,421 struct snd_sof_pcm *spcm, int dir)422{423 struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;424 struct sof_ipc4_pipeline *pipeline = pipe_widget->private;425 struct snd_soc_component *scomp = spcm->scomp;426 struct snd_soc_card *card = scomp->card;427 const char *pt_marker = "iec61937-pcm";428 429 /*430 * Update the card's components list with iec61937-pcm and a list of PCM431 * ids where ChainDMA is enabled.432 * These PCMs can be used for bytestream passthrough.433 */434 if (!pipeline->use_chain_dma)435 return 0;436 437 if (card->components) {438 const char *tmp = card->components;439 440 if (strstr(card->components, pt_marker))441 card->components = devm_kasprintf(card->dev, GFP_KERNEL,442 "%s,%d",443 card->components,444 spcm->pcm.pcm_id);445 else446 card->components = devm_kasprintf(card->dev, GFP_KERNEL,447 "%s %s:%d",448 card->components,449 pt_marker,450 spcm->pcm.pcm_id);451 452 devm_kfree(card->dev, tmp);453 } else {454 card->components = devm_kasprintf(card->dev, GFP_KERNEL,455 "%s:%d", pt_marker,456 spcm->pcm.pcm_id);457 }458 459 if (!card->components)460 return -ENOMEM;461 462 return 0;463}464 465static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget)466{467 struct sof_ipc4_available_audio_format *available_fmt;468 struct snd_soc_component *scomp = swidget->scomp;469 struct sof_ipc4_copier *ipc4_copier;470 struct snd_sof_pcm *spcm;471 int node_type = 0;472 int ret, dir;473 474 ipc4_copier = kzalloc(sizeof(*ipc4_copier), GFP_KERNEL);475 if (!ipc4_copier)476 return -ENOMEM;477 478 swidget->private = ipc4_copier;479 available_fmt = &ipc4_copier->available_fmt;480 481 dev_dbg(scomp->dev, "Updating IPC structure for %s\n", swidget->widget->name);482 483 ret = sof_ipc4_get_audio_fmt(scomp, swidget, available_fmt,484 &ipc4_copier->data.base_config);485 if (ret)486 goto free_copier;487 488 /*489 * This callback is used by host copier and module-to-module copier,490 * and only host copier needs to set gtw_cfg.491 */492 if (!WIDGET_IS_AIF(swidget->id))493 goto skip_gtw_cfg;494 495 ret = sof_update_ipc_object(scomp, &node_type,496 SOF_COPIER_TOKENS, swidget->tuples,497 swidget->num_tuples, sizeof(node_type), 1);498 499 if (ret) {500 dev_err(scomp->dev, "parse host copier node type token failed %d\n",501 ret);502 goto free_available_fmt;503 }504 dev_dbg(scomp->dev, "host copier '%s' node_type %u\n", swidget->widget->name, node_type);505 506 spcm = snd_sof_find_spcm_comp(scomp, swidget->comp_id, &dir);507 if (!spcm)508 goto skip_gtw_cfg;509 510 ret = sof_ipc4_update_card_components_string(swidget, spcm, dir);511 if (ret)512 goto free_available_fmt;513 514 if (dir == SNDRV_PCM_STREAM_PLAYBACK) {515 struct snd_sof_pcm_stream *sps = &spcm->stream[dir];516 517 sof_update_ipc_object(scomp, &sps->dsp_max_burst_size_in_ms,518 SOF_COPIER_DEEP_BUFFER_TOKENS,519 swidget->tuples,520 swidget->num_tuples, sizeof(u32), 1);521 /* Set default DMA buffer size if it is not specified in topology */522 if (!sps->dsp_max_burst_size_in_ms)523 sps->dsp_max_burst_size_in_ms = SOF_IPC4_MIN_DMA_BUFFER_SIZE;524 } else {525 /* Capture data is copied from DSP to host in 1ms bursts */526 spcm->stream[dir].dsp_max_burst_size_in_ms = 1;527 }528 529skip_gtw_cfg:530 ipc4_copier->gtw_attr = kzalloc(sizeof(*ipc4_copier->gtw_attr), GFP_KERNEL);531 if (!ipc4_copier->gtw_attr) {532 ret = -ENOMEM;533 goto free_available_fmt;534 }535 536 ipc4_copier->copier_config = (uint32_t *)ipc4_copier->gtw_attr;537 ipc4_copier->data.gtw_cfg.config_length =538 sizeof(struct sof_ipc4_gtw_attributes) >> 2;539 540 switch (swidget->id) {541 case snd_soc_dapm_aif_in:542 case snd_soc_dapm_aif_out:543 ipc4_copier->data.gtw_cfg.node_id = SOF_IPC4_NODE_TYPE(node_type);544 break;545 case snd_soc_dapm_buffer:546 ipc4_copier->data.gtw_cfg.node_id = SOF_IPC4_INVALID_NODE_ID;547 ipc4_copier->ipc_config_size = 0;548 break;549 default:550 dev_err(scomp->dev, "invalid widget type %d\n", swidget->id);551 ret = -EINVAL;552 goto free_gtw_attr;553 }554 555 /* set up module info and message header */556 ret = sof_ipc4_widget_setup_msg(swidget, &ipc4_copier->msg);557 if (ret)558 goto free_gtw_attr;559 560 return 0;561 562free_gtw_attr:563 kfree(ipc4_copier->gtw_attr);564free_available_fmt:565 sof_ipc4_free_audio_fmt(available_fmt);566free_copier:567 kfree(ipc4_copier);568 swidget->private = NULL;569 return ret;570}571 572static void sof_ipc4_widget_free_comp_pcm(struct snd_sof_widget *swidget)573{574 struct sof_ipc4_copier *ipc4_copier = swidget->private;575 struct sof_ipc4_available_audio_format *available_fmt;576 577 if (!ipc4_copier)578 return;579 580 available_fmt = &ipc4_copier->available_fmt;581 kfree(available_fmt->output_pin_fmts);582 kfree(ipc4_copier->gtw_attr);583 kfree(ipc4_copier);584 swidget->private = NULL;585}586 587static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget)588{589 struct sof_ipc4_available_audio_format *available_fmt;590 struct snd_soc_component *scomp = swidget->scomp;591 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);592 struct snd_sof_dai *dai = swidget->private;593 struct sof_ipc4_copier *ipc4_copier;594 struct snd_sof_widget *pipe_widget;595 struct sof_ipc4_pipeline *pipeline;596 int node_type = 0;597 int ret;598 599 ipc4_copier = kzalloc(sizeof(*ipc4_copier), GFP_KERNEL);600 if (!ipc4_copier)601 return -ENOMEM;602 603 available_fmt = &ipc4_copier->available_fmt;604 605 dev_dbg(scomp->dev, "Updating IPC structure for %s\n", swidget->widget->name);606 607 ret = sof_ipc4_get_audio_fmt(scomp, swidget, available_fmt,608 &ipc4_copier->data.base_config);609 if (ret)610 goto free_copier;611 612 ret = sof_update_ipc_object(scomp, &node_type,613 SOF_COPIER_TOKENS, swidget->tuples,614 swidget->num_tuples, sizeof(node_type), 1);615 if (ret) {616 dev_err(scomp->dev, "parse dai node type failed %d\n", ret);617 goto free_available_fmt;618 }619 620 ret = sof_update_ipc_object(scomp, ipc4_copier,621 SOF_DAI_TOKENS, swidget->tuples,622 swidget->num_tuples, sizeof(u32), 1);623 if (ret) {624 dev_err(scomp->dev, "parse dai copier node token failed %d\n", ret);625 goto free_available_fmt;626 }627 628 dev_dbg(scomp->dev, "dai %s node_type %u dai_type %u dai_index %d\n", swidget->widget->name,629 node_type, ipc4_copier->dai_type, ipc4_copier->dai_index);630 631 dai->type = ipc4_copier->dai_type;632 ipc4_copier->data.gtw_cfg.node_id = SOF_IPC4_NODE_TYPE(node_type);633 634 pipe_widget = swidget->spipe->pipe_widget;635 pipeline = pipe_widget->private;636 637 if (pipeline->use_chain_dma &&638 !snd_sof_is_chain_dma_supported(sdev, ipc4_copier->dai_type)) {639 dev_err(scomp->dev, "Bad DAI type '%d', Chain DMA is not supported\n",640 ipc4_copier->dai_type);641 ret = -ENODEV;642 goto free_available_fmt;643 }644 645 switch (ipc4_copier->dai_type) {646 case SOF_DAI_INTEL_ALH:647 {648 struct sof_ipc4_alh_configuration_blob *blob;649 struct snd_soc_dapm_path *p;650 struct snd_sof_widget *w;651 int src_num = 0;652 653 snd_soc_dapm_widget_for_each_source_path(swidget->widget, p)654 src_num++;655 656 if (swidget->id == snd_soc_dapm_dai_in && src_num == 0) {657 /*658 * The blob will not be used if the ALH copier is playback direction659 * and doesn't connect to any source.660 * It is fine to call kfree(ipc4_copier->copier_config) since661 * ipc4_copier->copier_config is null.662 */663 ret = 0;664 break;665 }666 667 blob = kzalloc(sizeof(*blob), GFP_KERNEL);668 if (!blob) {669 ret = -ENOMEM;670 goto free_available_fmt;671 }672 673 list_for_each_entry(w, &sdev->widget_list, list) {674 if (w->widget->sname &&675 strcmp(w->widget->sname, swidget->widget->sname))676 continue;677 678 blob->alh_cfg.device_count++;679 }680 681 ipc4_copier->copier_config = (uint32_t *)blob;682 /* set data.gtw_cfg.config_length based on device_count */683 ipc4_copier->data.gtw_cfg.config_length = (sizeof(blob->gw_attr) +684 sizeof(blob->alh_cfg.device_count) +685 sizeof(*blob->alh_cfg.mapping) *686 blob->alh_cfg.device_count) >> 2;687 break;688 }689 case SOF_DAI_INTEL_SSP:690 /* set SSP DAI index as the node_id */691 ipc4_copier->data.gtw_cfg.node_id |=692 SOF_IPC4_NODE_INDEX_INTEL_SSP(ipc4_copier->dai_index);693 break;694 case SOF_DAI_INTEL_DMIC:695 /* set DMIC DAI index as the node_id */696 ipc4_copier->data.gtw_cfg.node_id |=697 SOF_IPC4_NODE_INDEX_INTEL_DMIC(ipc4_copier->dai_index);698 break;699 default:700 ipc4_copier->gtw_attr = kzalloc(sizeof(*ipc4_copier->gtw_attr), GFP_KERNEL);701 if (!ipc4_copier->gtw_attr) {702 ret = -ENOMEM;703 goto free_available_fmt;704 }705 706 ipc4_copier->copier_config = (uint32_t *)ipc4_copier->gtw_attr;707 ipc4_copier->data.gtw_cfg.config_length =708 sizeof(struct sof_ipc4_gtw_attributes) >> 2;709 break;710 }711 712 dai->scomp = scomp;713 dai->private = ipc4_copier;714 715 /* set up module info and message header */716 ret = sof_ipc4_widget_setup_msg(swidget, &ipc4_copier->msg);717 if (ret)718 goto free_copier_config;719 720 return 0;721 722free_copier_config:723 kfree(ipc4_copier->copier_config);724free_available_fmt:725 sof_ipc4_free_audio_fmt(available_fmt);726free_copier:727 kfree(ipc4_copier);728 dai->private = NULL;729 dai->scomp = NULL;730 return ret;731}732 733static void sof_ipc4_widget_free_comp_dai(struct snd_sof_widget *swidget)734{735 struct sof_ipc4_available_audio_format *available_fmt;736 struct snd_sof_dai *dai = swidget->private;737 struct sof_ipc4_copier *ipc4_copier;738 739 if (!dai)740 return;741 742 if (!dai->private) {743 kfree(dai);744 swidget->private = NULL;745 return;746 }747 748 ipc4_copier = dai->private;749 available_fmt = &ipc4_copier->available_fmt;750 751 kfree(available_fmt->output_pin_fmts);752 if (ipc4_copier->dai_type != SOF_DAI_INTEL_SSP &&753 ipc4_copier->dai_type != SOF_DAI_INTEL_DMIC)754 kfree(ipc4_copier->copier_config);755 kfree(dai->private);756 kfree(dai);757 swidget->private = NULL;758}759 760static int sof_ipc4_widget_setup_comp_pipeline(struct snd_sof_widget *swidget)761{762 struct snd_soc_component *scomp = swidget->scomp;763 struct sof_ipc4_pipeline *pipeline;764 struct snd_sof_pipeline *spipe = swidget->spipe;765 int ret;766 767 pipeline = kzalloc(sizeof(*pipeline), GFP_KERNEL);768 if (!pipeline)769 return -ENOMEM;770 771 ret = sof_update_ipc_object(scomp, pipeline, SOF_SCHED_TOKENS, swidget->tuples,772 swidget->num_tuples, sizeof(*pipeline), 1);773 if (ret) {774 dev_err(scomp->dev, "parsing scheduler tokens failed\n");775 goto err;776 }777 778 swidget->core = pipeline->core_id;779 spipe->core_mask |= BIT(pipeline->core_id);780 781 if (pipeline->use_chain_dma) {782 dev_dbg(scomp->dev, "Set up chain DMA for %s\n", swidget->widget->name);783 swidget->private = pipeline;784 return 0;785 }786 787 /* parse one set of pipeline tokens */788 ret = sof_update_ipc_object(scomp, swidget, SOF_PIPELINE_TOKENS, swidget->tuples,789 swidget->num_tuples, sizeof(*swidget), 1);790 if (ret) {791 dev_err(scomp->dev, "parsing pipeline tokens failed\n");792 goto err;793 }794 795 dev_dbg(scomp->dev, "pipeline '%s': id %d, pri %d, core_id %u, lp mode %d\n",796 swidget->widget->name, swidget->pipeline_id,797 pipeline->priority, pipeline->core_id, pipeline->lp_mode);798 799 swidget->private = pipeline;800 801 pipeline->msg.primary = SOF_IPC4_GLB_PIPE_PRIORITY(pipeline->priority);802 pipeline->msg.primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_CREATE_PIPELINE);803 pipeline->msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);804 pipeline->msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG);805 806 pipeline->msg.extension = pipeline->lp_mode;807 pipeline->msg.extension |= SOF_IPC4_GLB_PIPE_EXT_CORE_ID(pipeline->core_id);808 pipeline->state = SOF_IPC4_PIPE_UNINITIALIZED;809 810 return 0;811err:812 kfree(pipeline);813 return ret;814}815 816static int sof_ipc4_widget_setup_comp_pga(struct snd_sof_widget *swidget)817{818 struct snd_soc_component *scomp = swidget->scomp;819 struct sof_ipc4_gain *gain;820 int ret;821 822 gain = kzalloc(sizeof(*gain), GFP_KERNEL);823 if (!gain)824 return -ENOMEM;825 826 swidget->private = gain;827 828 gain->data.params.channels = SOF_IPC4_GAIN_ALL_CHANNELS_MASK;829 gain->data.params.init_val = SOF_IPC4_VOL_ZERO_DB;830 831 ret = sof_ipc4_get_audio_fmt(scomp, swidget, &gain->available_fmt, &gain->data.base_config);832 if (ret)833 goto err;834 835 ret = sof_update_ipc_object(scomp, &gain->data.params, SOF_GAIN_TOKENS,836 swidget->tuples, swidget->num_tuples, sizeof(gain->data), 1);837 if (ret) {838 dev_err(scomp->dev, "Parsing gain tokens failed\n");839 goto err;840 }841 842 dev_dbg(scomp->dev,843 "pga widget %s: ramp type: %d, ramp duration %d, initial gain value: %#x\n",844 swidget->widget->name, gain->data.params.curve_type,845 gain->data.params.curve_duration_l, gain->data.params.init_val);846 847 ret = sof_ipc4_widget_setup_msg(swidget, &gain->msg);848 if (ret)849 goto err;850 851 sof_ipc4_widget_update_kcontrol_module_id(swidget);852 853 return 0;854err:855 sof_ipc4_free_audio_fmt(&gain->available_fmt);856 kfree(gain);857 swidget->private = NULL;858 return ret;859}860 861static void sof_ipc4_widget_free_comp_pga(struct snd_sof_widget *swidget)862{863 struct sof_ipc4_gain *gain = swidget->private;864 865 if (!gain)866 return;867 868 sof_ipc4_free_audio_fmt(&gain->available_fmt);869 kfree(swidget->private);870 swidget->private = NULL;871}872 873static int sof_ipc4_widget_setup_comp_mixer(struct snd_sof_widget *swidget)874{875 struct snd_soc_component *scomp = swidget->scomp;876 struct sof_ipc4_mixer *mixer;877 int ret;878 879 dev_dbg(scomp->dev, "Updating IPC structure for %s\n", swidget->widget->name);880 881 mixer = kzalloc(sizeof(*mixer), GFP_KERNEL);882 if (!mixer)883 return -ENOMEM;884 885 swidget->private = mixer;886 887 ret = sof_ipc4_get_audio_fmt(scomp, swidget, &mixer->available_fmt,888 &mixer->base_config);889 if (ret)890 goto err;891 892 ret = sof_ipc4_widget_setup_msg(swidget, &mixer->msg);893 if (ret)894 goto err;895 896 return 0;897err:898 sof_ipc4_free_audio_fmt(&mixer->available_fmt);899 kfree(mixer);900 swidget->private = NULL;901 return ret;902}903 904static int sof_ipc4_widget_setup_comp_src(struct snd_sof_widget *swidget)905{906 struct snd_soc_component *scomp = swidget->scomp;907 struct snd_sof_pipeline *spipe = swidget->spipe;908 struct sof_ipc4_src *src;909 int ret;910 911 dev_dbg(scomp->dev, "Updating IPC structure for %s\n", swidget->widget->name);912 913 src = kzalloc(sizeof(*src), GFP_KERNEL);914 if (!src)915 return -ENOMEM;916 917 swidget->private = src;918 919 ret = sof_ipc4_get_audio_fmt(scomp, swidget, &src->available_fmt,920 &src->data.base_config);921 if (ret)922 goto err;923 924 ret = sof_update_ipc_object(scomp, &src->data, SOF_SRC_TOKENS, swidget->tuples,925 swidget->num_tuples, sizeof(*src), 1);926 if (ret) {927 dev_err(scomp->dev, "Parsing SRC tokens failed\n");928 goto err;929 }930 931 spipe->core_mask |= BIT(swidget->core);932 933 dev_dbg(scomp->dev, "SRC sink rate %d\n", src->data.sink_rate);934 935 ret = sof_ipc4_widget_setup_msg(swidget, &src->msg);936 if (ret)937 goto err;938 939 return 0;940err:941 sof_ipc4_free_audio_fmt(&src->available_fmt);942 kfree(src);943 swidget->private = NULL;944 return ret;945}946 947static void sof_ipc4_widget_free_comp_src(struct snd_sof_widget *swidget)948{949 struct sof_ipc4_src *src = swidget->private;950 951 if (!src)952 return;953 954 sof_ipc4_free_audio_fmt(&src->available_fmt);955 kfree(swidget->private);956 swidget->private = NULL;957}958 959static void sof_ipc4_widget_free_comp_mixer(struct snd_sof_widget *swidget)960{961 struct sof_ipc4_mixer *mixer = swidget->private;962 963 if (!mixer)964 return;965 966 sof_ipc4_free_audio_fmt(&mixer->available_fmt);967 kfree(swidget->private);968 swidget->private = NULL;969}970 971/*972 * Add the process modules support. The process modules are defined as snd_soc_dapm_effect modules.973 */974static int sof_ipc4_widget_setup_comp_process(struct snd_sof_widget *swidget)975{976 struct snd_soc_component *scomp = swidget->scomp;977 struct sof_ipc4_fw_module *fw_module;978 struct snd_sof_pipeline *spipe = swidget->spipe;979 struct sof_ipc4_process *process;980 void *cfg;981 int ret;982 983 process = kzalloc(sizeof(*process), GFP_KERNEL);984 if (!process)985 return -ENOMEM;986 987 swidget->private = process;988 989 ret = sof_ipc4_get_audio_fmt(scomp, swidget, &process->available_fmt,990 &process->base_config);991 if (ret)992 goto err;993 994 ret = sof_ipc4_widget_setup_msg(swidget, &process->msg);995 if (ret)996 goto err;997 998 /* parse process init module payload config type from module info */999 fw_module = swidget->module_info;1000 process->init_config = FIELD_GET(SOF_IPC4_MODULE_INIT_CONFIG_MASK,1001 fw_module->man4_module_entry.type);1002 1003 process->ipc_config_size = sizeof(struct sof_ipc4_base_module_cfg);1004 1005 /* allocate memory for base config extension if needed */1006 if (process->init_config == SOF_IPC4_MODULE_INIT_CONFIG_TYPE_BASE_CFG_WITH_EXT) {1007 struct sof_ipc4_base_module_cfg_ext *base_cfg_ext;1008 u32 ext_size = struct_size(base_cfg_ext, pin_formats,1009 size_add(swidget->num_input_pins,1010 swidget->num_output_pins));1011 1012 base_cfg_ext = kzalloc(ext_size, GFP_KERNEL);1013 if (!base_cfg_ext) {1014 ret = -ENOMEM;1015 goto free_available_fmt;1016 }1017 1018 base_cfg_ext->num_input_pin_fmts = swidget->num_input_pins;1019 base_cfg_ext->num_output_pin_fmts = swidget->num_output_pins;1020 process->base_config_ext = base_cfg_ext;1021 process->base_config_ext_size = ext_size;1022 process->ipc_config_size += ext_size;1023 }1024 1025 cfg = kzalloc(process->ipc_config_size, GFP_KERNEL);1026 if (!cfg) {1027 ret = -ENOMEM;1028 goto free_base_cfg_ext;1029 }1030 1031 process->ipc_config_data = cfg;1032 1033 sof_ipc4_widget_update_kcontrol_module_id(swidget);1034 1035 /* set pipeline core mask to keep track of the core the module is scheduled to run on */1036 spipe->core_mask |= BIT(swidget->core);1037 1038 return 0;1039free_base_cfg_ext:1040 kfree(process->base_config_ext);1041 process->base_config_ext = NULL;1042free_available_fmt:1043 sof_ipc4_free_audio_fmt(&process->available_fmt);1044err:1045 kfree(process);1046 swidget->private = NULL;1047 return ret;1048}1049 1050static void sof_ipc4_widget_free_comp_process(struct snd_sof_widget *swidget)1051{1052 struct sof_ipc4_process *process = swidget->private;1053 1054 if (!process)1055 return;1056 1057 kfree(process->ipc_config_data);1058 kfree(process->base_config_ext);1059 sof_ipc4_free_audio_fmt(&process->available_fmt);1060 kfree(swidget->private);1061 swidget->private = NULL;1062}1063 1064static void1065sof_ipc4_update_resource_usage(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,1066 struct sof_ipc4_base_module_cfg *base_config)1067{1068 struct sof_ipc4_fw_module *fw_module = swidget->module_info;1069 struct snd_sof_widget *pipe_widget;1070 struct sof_ipc4_pipeline *pipeline;1071 int task_mem, queue_mem;1072 int ibs, bss, total;1073 1074 ibs = base_config->ibs;1075 bss = base_config->is_pages;1076 1077 task_mem = SOF_IPC4_PIPELINE_OBJECT_SIZE;1078 task_mem += SOF_IPC4_MODULE_INSTANCE_LIST_ITEM_SIZE + bss;1079 1080 if (fw_module->man4_module_entry.type & SOF_IPC4_MODULE_LL) {1081 task_mem += SOF_IPC4_FW_ROUNDUP(SOF_IPC4_LL_TASK_OBJECT_SIZE);1082 task_mem += SOF_IPC4_FW_MAX_QUEUE_COUNT * SOF_IPC4_MODULE_INSTANCE_LIST_ITEM_SIZE;1083 task_mem += SOF_IPC4_LL_TASK_LIST_ITEM_SIZE;1084 } else {1085 task_mem += SOF_IPC4_FW_ROUNDUP(SOF_IPC4_DP_TASK_OBJECT_SIZE);1086 task_mem += SOF_IPC4_DP_TASK_LIST_SIZE;1087 }1088 1089 ibs = SOF_IPC4_FW_ROUNDUP(ibs);1090 queue_mem = SOF_IPC4_FW_MAX_QUEUE_COUNT * (SOF_IPC4_DATA_QUEUE_OBJECT_SIZE + ibs);1091 1092 total = SOF_IPC4_FW_PAGE(task_mem + queue_mem);1093 1094 pipe_widget = swidget->spipe->pipe_widget;1095 pipeline = pipe_widget->private;1096 pipeline->mem_usage += total;1097 1098 /* Update base_config->cpc from the module manifest */1099 sof_ipc4_update_cpc_from_manifest(sdev, fw_module, base_config);1100 1101 if (ignore_cpc) {1102 dev_dbg(sdev->dev, "%s: ibs / obs: %u / %u, forcing cpc to 0 from %u\n",1103 swidget->widget->name, base_config->ibs, base_config->obs,1104 base_config->cpc);1105 base_config->cpc = 0;1106 } else {1107 dev_dbg(sdev->dev, "%s: ibs / obs / cpc: %u / %u / %u\n",1108 swidget->widget->name, base_config->ibs, base_config->obs,1109 base_config->cpc);1110 }1111}1112 1113static int sof_ipc4_widget_assign_instance_id(struct snd_sof_dev *sdev,1114 struct snd_sof_widget *swidget)1115{1116 struct sof_ipc4_fw_module *fw_module = swidget->module_info;1117 int max_instances = fw_module->man4_module_entry.instance_max_count;1118 1119 swidget->instance_id = ida_alloc_max(&fw_module->m_ida, max_instances, GFP_KERNEL);1120 if (swidget->instance_id < 0) {1121 dev_err(sdev->dev, "failed to assign instance id for widget %s",1122 swidget->widget->name);1123 return swidget->instance_id;1124 }1125 1126 return 0;1127}1128 1129/* update hw_params based on the audio stream format */1130static int sof_ipc4_update_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_hw_params *params,1131 struct sof_ipc4_audio_format *fmt, u32 param_to_update)1132{1133 struct snd_interval *i;1134 1135 if (param_to_update & BIT(SNDRV_PCM_HW_PARAM_FORMAT)) {1136 int valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);1137 snd_pcm_format_t snd_fmt;1138 struct snd_mask *m;1139 1140 switch (valid_bits) {1141 case 16:1142 snd_fmt = SNDRV_PCM_FORMAT_S16_LE;1143 break;1144 case 24:1145 snd_fmt = SNDRV_PCM_FORMAT_S24_LE;1146 break;1147 case 32:1148 snd_fmt = SNDRV_PCM_FORMAT_S32_LE;1149 break;1150 default:1151 dev_err(sdev->dev, "invalid PCM valid_bits %d\n", valid_bits);1152 return -EINVAL;1153 }1154 1155 m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);1156 snd_mask_none(m);1157 snd_mask_set_format(m, snd_fmt);1158 }1159 1160 if (param_to_update & BIT(SNDRV_PCM_HW_PARAM_RATE)) {1161 unsigned int rate = fmt->sampling_frequency;1162 1163 i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);1164 i->min = rate;1165 i->max = rate;1166 }1167 1168 if (param_to_update & BIT(SNDRV_PCM_HW_PARAM_CHANNELS)) {1169 unsigned int channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg);1170 1171 i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);1172 i->min = channels;1173 i->max = channels;1174 }1175 1176 return 0;1177}1178 1179static bool sof_ipc4_is_single_format(struct snd_sof_dev *sdev,1180 struct sof_ipc4_pin_format *pin_fmts, u32 pin_fmts_size)1181{1182 struct sof_ipc4_audio_format *fmt;1183 u32 rate, channels, valid_bits;1184 int i;1185 1186 fmt = &pin_fmts[0].audio_fmt;1187 rate = fmt->sampling_frequency;1188 channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg);1189 valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);1190 1191 /* check if all output formats in topology are the same */1192 for (i = 1; i < pin_fmts_size; i++) {1193 u32 _rate, _channels, _valid_bits;1194 1195 fmt = &pin_fmts[i].audio_fmt;1196 _rate = fmt->sampling_frequency;1197 _channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg);1198 _valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);1199 1200 if (_rate != rate || _channels != channels || _valid_bits != valid_bits)1201 return false;1202 }1203 1204 return true;1205}1206 1207static int sof_ipc4_init_output_audio_fmt(struct snd_sof_dev *sdev,1208 struct sof_ipc4_base_module_cfg *base_config,1209 struct sof_ipc4_available_audio_format *available_fmt,1210 u32 out_ref_rate, u32 out_ref_channels,1211 u32 out_ref_valid_bits)1212{1213 struct sof_ipc4_audio_format *out_fmt;1214 bool single_format;1215 int i;1216 1217 if (!available_fmt->num_output_formats)1218 return -EINVAL;1219 1220 single_format = sof_ipc4_is_single_format(sdev, available_fmt->output_pin_fmts,1221 available_fmt->num_output_formats);1222 1223 /* pick the first format if there's only one available or if all formats are the same */1224 if (single_format) {1225 base_config->obs = available_fmt->output_pin_fmts[0].buffer_size;1226 return 0;1227 }1228 1229 /*1230 * if there are multiple output formats, then choose the output format that matches1231 * the reference params1232 */1233 for (i = 0; i < available_fmt->num_output_formats; i++) {1234 u32 _out_rate, _out_channels, _out_valid_bits;1235 1236 out_fmt = &available_fmt->output_pin_fmts[i].audio_fmt;1237 _out_rate = out_fmt->sampling_frequency;1238 _out_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(out_fmt->fmt_cfg);1239 _out_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(out_fmt->fmt_cfg);1240 1241 if (_out_rate == out_ref_rate && _out_channels == out_ref_channels &&1242 _out_valid_bits == out_ref_valid_bits) {1243 base_config->obs = available_fmt->output_pin_fmts[i].buffer_size;1244 return i;1245 }1246 }1247 1248 return -EINVAL;1249}1250 1251static int sof_ipc4_get_valid_bits(struct snd_sof_dev *sdev, struct snd_pcm_hw_params *params)1252{1253 switch (params_format(params)) {1254 case SNDRV_PCM_FORMAT_S16_LE:1255 return 16;1256 case SNDRV_PCM_FORMAT_S24_LE:1257 return 24;1258 case SNDRV_PCM_FORMAT_S32_LE:1259 return 32;1260 default:1261 dev_err(sdev->dev, "invalid pcm frame format %d\n", params_format(params));1262 return -EINVAL;1263 }1264}1265 1266static int sof_ipc4_init_input_audio_fmt(struct snd_sof_dev *sdev,1267 struct snd_sof_widget *swidget,1268 struct sof_ipc4_base_module_cfg *base_config,1269 struct snd_pcm_hw_params *params,1270 struct sof_ipc4_available_audio_format *available_fmt)1271{1272 struct sof_ipc4_pin_format *pin_fmts = available_fmt->input_pin_fmts;1273 u32 pin_fmts_size = available_fmt->num_input_formats;1274 u32 valid_bits;1275 u32 channels;1276 u32 rate;1277 bool single_format;1278 int sample_valid_bits;1279 int i = 0;1280 1281 if (!available_fmt->num_input_formats) {1282 dev_err(sdev->dev, "no input formats for %s\n", swidget->widget->name);1283 return -EINVAL;1284 }1285 1286 single_format = sof_ipc4_is_single_format(sdev, available_fmt->input_pin_fmts,1287 available_fmt->num_input_formats);1288 if (single_format)1289 goto in_fmt;1290 1291 sample_valid_bits = sof_ipc4_get_valid_bits(sdev, params);1292 if (sample_valid_bits < 0)1293 return sample_valid_bits;1294 1295 /*1296 * Search supported input audio formats with pin index 0 to match rate, channels and1297 * sample_valid_bits from reference params1298 */1299 for (i = 0; i < pin_fmts_size; i++) {1300 struct sof_ipc4_audio_format *fmt = &pin_fmts[i].audio_fmt;1301 1302 if (pin_fmts[i].pin_index)1303 continue;1304 1305 rate = fmt->sampling_frequency;1306 channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg);1307 valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);1308 if (params_rate(params) == rate && params_channels(params) == channels &&1309 sample_valid_bits == valid_bits) {1310 dev_dbg(sdev->dev, "matched audio format index for %uHz, %ubit, %u channels: %d\n",1311 rate, valid_bits, channels, i);1312 break;1313 }1314 }1315 1316 if (i == pin_fmts_size) {1317 dev_err(sdev->dev, "%s: Unsupported audio format: %uHz, %ubit, %u channels\n",1318 __func__, params_rate(params), sample_valid_bits, params_channels(params));1319 return -EINVAL;1320 }1321 1322in_fmt:1323 /* copy input format */1324 if (available_fmt->num_input_formats && i < available_fmt->num_input_formats) {1325 memcpy(&base_config->audio_fmt, &available_fmt->input_pin_fmts[i].audio_fmt,1326 sizeof(struct sof_ipc4_audio_format));1327 1328 /* set base_cfg ibs/obs */1329 base_config->ibs = available_fmt->input_pin_fmts[i].buffer_size;1330 1331 dev_dbg(sdev->dev, "Init input audio formats for %s\n", swidget->widget->name);1332 sof_ipc4_dbg_audio_format(sdev->dev, &available_fmt->input_pin_fmts[i], 1);1333 }1334 1335 return i;1336}1337 1338static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget)1339{1340 struct sof_ipc4_copier *ipc4_copier = NULL;1341 struct snd_sof_widget *pipe_widget;1342 struct sof_ipc4_pipeline *pipeline;1343 1344 /* reset pipeline memory usage */1345 pipe_widget = swidget->spipe->pipe_widget;1346 pipeline = pipe_widget->private;1347 pipeline->mem_usage = 0;1348 1349 if (WIDGET_IS_AIF(swidget->id) || swidget->id == snd_soc_dapm_buffer) {1350 if (pipeline->use_chain_dma) {1351 pipeline->msg.primary = 0;1352 pipeline->msg.extension = 0;1353 }1354 ipc4_copier = swidget->private;1355 } else if (WIDGET_IS_DAI(swidget->id)) {1356 struct snd_sof_dai *dai = swidget->private;1357 1358 ipc4_copier = dai->private;1359 1360 if (pipeline->use_chain_dma) {1361 /*1362 * Preserve the DMA Link ID and clear other bits since1363 * the DMA Link ID is only configured once during1364 * dai_config, other fields are expected to be 0 for1365 * re-configuration1366 */1367 pipeline->msg.primary &= SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK;1368 pipeline->msg.extension = 0;1369 }1370 1371 if (ipc4_copier->dai_type == SOF_DAI_INTEL_ALH) {1372 struct sof_ipc4_alh_configuration_blob *blob;1373 unsigned int group_id;1374 1375 blob = (struct sof_ipc4_alh_configuration_blob *)ipc4_copier->copier_config;1376 if (blob->alh_cfg.device_count > 1) {1377 group_id = SOF_IPC4_NODE_INDEX(ipc4_copier->data.gtw_cfg.node_id) -1378 ALH_MULTI_GTW_BASE;1379 ida_free(&alh_group_ida, group_id);1380 }1381 }1382 }1383 1384 if (ipc4_copier) {1385 kfree(ipc4_copier->ipc_config_data);1386 ipc4_copier->ipc_config_data = NULL;1387 ipc4_copier->ipc_config_size = 0;1388 }1389}1390 1391#if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_SND_INTEL_NHLT)1392static int snd_sof_get_hw_config_params(struct snd_sof_dev *sdev, struct snd_sof_dai *dai,1393 int *sample_rate, int *channel_count, int *bit_depth)1394{1395 struct snd_soc_tplg_hw_config *hw_config;1396 struct snd_sof_dai_link *slink;1397 bool dai_link_found = false;1398 bool hw_cfg_found = false;1399 int i;1400 1401 /* get current hw_config from link */1402 list_for_each_entry(slink, &sdev->dai_link_list, list) {1403 if (!strcmp(slink->link->name, dai->name)) {1404 dai_link_found = true;1405 break;1406 }1407 }1408 1409 if (!dai_link_found) {1410 dev_err(sdev->dev, "%s: no DAI link found for DAI %s\n", __func__, dai->name);1411 return -EINVAL;1412 }1413 1414 for (i = 0; i < slink->num_hw_configs; i++) {1415 hw_config = &slink->hw_configs[i];1416 if (dai->current_config == le32_to_cpu(hw_config->id)) {1417 hw_cfg_found = true;1418 break;1419 }1420 }1421 1422 if (!hw_cfg_found) {1423 dev_err(sdev->dev, "%s: no matching hw_config found for DAI %s\n", __func__,1424 dai->name);1425 return -EINVAL;1426 }1427 1428 *bit_depth = le32_to_cpu(hw_config->tdm_slot_width);1429 *channel_count = le32_to_cpu(hw_config->tdm_slots);1430 *sample_rate = le32_to_cpu(hw_config->fsync_rate);1431 1432 dev_dbg(sdev->dev, "sample rate: %d sample width: %d channels: %d\n",1433 *sample_rate, *bit_depth, *channel_count);1434 1435 return 0;1436}1437 1438static int1439snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai,1440 bool single_bitdepth,1441 struct snd_pcm_hw_params *params, u32 dai_index,1442 u32 linktype, u8 dir, u32 **dst, u32 *len)1443{1444 struct sof_ipc4_fw_data *ipc4_data = sdev->private;1445 struct nhlt_specific_cfg *cfg;1446 int sample_rate, channel_count;1447 bool format_change = false;1448 int bit_depth, ret;1449 u32 nhlt_type;1450 int dev_type = 0;1451 1452 /* convert to NHLT type */1453 switch (linktype) {1454 case SOF_DAI_INTEL_DMIC:1455 nhlt_type = NHLT_LINK_DMIC;1456 channel_count = params_channels(params);1457 sample_rate = params_rate(params);1458 bit_depth = params_width(params);1459 /*1460 * Look for 32-bit blob first instead of 16-bit if copier1461 * supports multiple formats1462 */1463 if (bit_depth == 16 && !single_bitdepth) {1464 dev_dbg(sdev->dev, "Looking for 32-bit blob first for DMIC\n");1465 format_change = true;1466 bit_depth = 32;1467 }1468 break;1469 case SOF_DAI_INTEL_SSP:1470 nhlt_type = NHLT_LINK_SSP;1471 ret = snd_sof_get_hw_config_params(sdev, dai, &sample_rate, &channel_count,1472 &bit_depth);1473 if (ret < 0)1474 return ret;1475 1476 /*1477 * We need to know the type of the external device attached to a SSP1478 * port to retrieve the blob from NHLT. However, device type is not1479 * specified in topology.1480 * Query the type for the port and then pass that information back1481 * to the blob lookup function.1482 */1483 dev_type = intel_nhlt_ssp_device_type(sdev->dev, ipc4_data->nhlt,1484 dai_index);1485 if (dev_type < 0)1486 return dev_type;1487 break;1488 default:1489 return 0;1490 }1491 1492 dev_dbg(sdev->dev, "dai index %d nhlt type %d direction %d dev type %d\n",1493 dai_index, nhlt_type, dir, dev_type);1494 1495 /* find NHLT blob with matching params */1496 cfg = intel_nhlt_get_endpoint_blob(sdev->dev, ipc4_data->nhlt, dai_index, nhlt_type,1497 bit_depth, bit_depth, channel_count, sample_rate,1498 dir, dev_type);1499 1500 if (!cfg) {1501 bool get_new_blob = false;1502 1503 if (format_change) {1504 /*1505 * The 32-bit blob was not found in NHLT table, try to1506 * look for one based on the params1507 */1508 bit_depth = params_width(params);1509 format_change = false;1510 get_new_blob = true;1511 } else if (linktype == SOF_DAI_INTEL_DMIC && !single_bitdepth) {1512 /*1513 * The requested 32-bit blob (no format change for the1514 * blob request) was not found in NHLT table, try to1515 * look for 16-bit blob if the copier supports multiple1516 * formats1517 */1518 bit_depth = 16;1519 format_change = true;1520 get_new_blob = true;1521 }1522 1523 if (get_new_blob) {1524 cfg = intel_nhlt_get_endpoint_blob(sdev->dev, ipc4_data->nhlt,1525 dai_index, nhlt_type,1526 bit_depth, bit_depth,1527 channel_count, sample_rate,1528 dir, dev_type);1529 if (cfg)1530 goto out;1531 }1532 1533 dev_err(sdev->dev,1534 "no matching blob for sample rate: %d sample width: %d channels: %d\n",1535 sample_rate, bit_depth, channel_count);1536 return -EINVAL;1537 }1538 1539out:1540 /* config length should be in dwords */1541 *len = cfg->size >> 2;1542 *dst = (u32 *)cfg->caps;1543 1544 if (format_change) {1545 /*1546 * Update the params to reflect that different blob was loaded1547 * instead of the requested bit depth (16 -> 32 or 32 -> 16).1548 * This information is going to be used by the caller to find1549 * matching copier format on the dai side.1550 */1551 struct snd_mask *m;1552 1553 m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);1554 snd_mask_none(m);1555 if (bit_depth == 16)1556 snd_mask_set_format(m, SNDRV_PCM_FORMAT_S16_LE);1557 else1558 snd_mask_set_format(m, SNDRV_PCM_FORMAT_S32_LE);1559 1560 }1561 1562 return 0;1563}1564#else1565static int1566snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai,1567 bool single_bitdepth,1568 struct snd_pcm_hw_params *params, u32 dai_index,1569 u32 linktype, u8 dir, u32 **dst, u32 *len)1570{1571 return 0;1572}1573#endif1574 1575bool sof_ipc4_copier_is_single_bitdepth(struct snd_sof_dev *sdev,1576 struct sof_ipc4_pin_format *pin_fmts,1577 u32 pin_fmts_size)1578{1579 struct sof_ipc4_audio_format *fmt;1580 u32 valid_bits;1581 int i;1582 1583 fmt = &pin_fmts[0].audio_fmt;1584 valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);1585 1586 /* check if all formats in topology are the same */1587 for (i = 1; i < pin_fmts_size; i++) {1588 u32 _valid_bits;1589 1590 fmt = &pin_fmts[i].audio_fmt;1591 _valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);1592 1593 if (_valid_bits != valid_bits)1594 return false;1595 }1596 1597 return true;1598}1599 1600static int1601sof_ipc4_adjust_params_to_dai_format(struct snd_sof_dev *sdev,1602 struct snd_pcm_hw_params *params,1603 struct sof_ipc4_pin_format *pin_fmts,1604 u32 pin_fmts_size)1605{1606 u32 params_mask = BIT(SNDRV_PCM_HW_PARAM_RATE) |1607 BIT(SNDRV_PCM_HW_PARAM_CHANNELS) |1608 BIT(SNDRV_PCM_HW_PARAM_FORMAT);1609 struct sof_ipc4_audio_format *fmt;1610 u32 rate, channels, valid_bits;1611 int i;1612 1613 fmt = &pin_fmts[0].audio_fmt;1614 rate = fmt->sampling_frequency;1615 channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg);1616 valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);1617 1618 /* check if parameters in topology defined formats are the same */1619 for (i = 1; i < pin_fmts_size; i++) {1620 u32 val;1621 1622 fmt = &pin_fmts[i].audio_fmt;1623 1624 if (params_mask & BIT(SNDRV_PCM_HW_PARAM_RATE)) {1625 val = fmt->sampling_frequency;1626 if (val != rate)1627 params_mask &= ~BIT(SNDRV_PCM_HW_PARAM_RATE);1628 }1629 if (params_mask & BIT(SNDRV_PCM_HW_PARAM_CHANNELS)) {1630 val = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg);1631 if (val != channels)1632 params_mask &= ~BIT(SNDRV_PCM_HW_PARAM_CHANNELS);1633 }1634 if (params_mask & BIT(SNDRV_PCM_HW_PARAM_FORMAT)) {1635 val = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg);1636 if (val != valid_bits)1637 params_mask &= ~BIT(SNDRV_PCM_HW_PARAM_FORMAT);1638 }1639 }1640 1641 if (params_mask)1642 return sof_ipc4_update_hw_params(sdev, params,1643 &pin_fmts[0].audio_fmt,1644 params_mask);1645 1646 return 0;1647}1648 1649static int1650sof_ipc4_prepare_dai_copier(struct snd_sof_dev *sdev, struct snd_sof_dai *dai,1651 struct snd_pcm_hw_params *params, int dir)1652{1653 struct sof_ipc4_available_audio_format *available_fmt;1654 struct snd_pcm_hw_params dai_params = *params;1655 struct sof_ipc4_copier_data *copier_data;1656 struct sof_ipc4_pin_format *pin_fmts;1657 struct sof_ipc4_copier *ipc4_copier;1658 bool single_bitdepth;1659 u32 num_pin_fmts;1660 int ret;1661 1662 ipc4_copier = dai->private;1663 copier_data = &ipc4_copier->data;1664 available_fmt = &ipc4_copier->available_fmt;1665 1666 /*1667 * Fixup the params based on the format parameters of the DAI. If any1668 * of the RATE, CHANNELS, bit depth is static among the formats then1669 * narrow the params to only allow that specific parameter value.1670 */1671 if (dir == SNDRV_PCM_STREAM_PLAYBACK) {1672 pin_fmts = available_fmt->output_pin_fmts;1673 num_pin_fmts = available_fmt->num_output_formats;1674 } else {1675 pin_fmts = available_fmt->input_pin_fmts;1676 num_pin_fmts = available_fmt->num_input_formats;1677 }1678 1679 ret = sof_ipc4_adjust_params_to_dai_format(sdev, &dai_params, pin_fmts,1680 num_pin_fmts);1681 if (ret)1682 return ret;1683 1684 single_bitdepth = sof_ipc4_copier_is_single_bitdepth(sdev, pin_fmts,1685 num_pin_fmts);1686 ret = snd_sof_get_nhlt_endpoint_data(sdev, dai, single_bitdepth,1687 &dai_params,1688 ipc4_copier->dai_index,1689 ipc4_copier->dai_type, dir,1690 &ipc4_copier->copier_config,1691 &copier_data->gtw_cfg.config_length);1692 /* Update the params to reflect the changes made in this function */1693 if (!ret)1694 *params = dai_params;1695 1696 return ret;1697}1698 1699static int1700sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,1701 struct snd_pcm_hw_params *fe_params,1702 struct snd_sof_platform_stream_params *platform_params,1703 struct snd_pcm_hw_params *pipeline_params, int dir)1704{1705 struct sof_ipc4_available_audio_format *available_fmt;1706 struct snd_soc_component *scomp = swidget->scomp;1707 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);1708 struct sof_ipc4_copier_data *copier_data;1709 struct snd_pcm_hw_params ref_params;1710 struct sof_ipc4_copier *ipc4_copier;1711 struct snd_sof_dai *dai;1712 u32 gtw_cfg_config_length;1713 u32 dma_config_tlv_size = 0;1714 void **ipc_config_data;1715 int *ipc_config_size;1716 u32 **data;1717 int ipc_size, ret, out_ref_valid_bits;1718 u32 out_ref_rate, out_ref_channels;1719 u32 deep_buffer_dma_ms = 0;1720 int output_fmt_index;1721 bool single_output_bitdepth;1722 int i;1723 1724 dev_dbg(sdev->dev, "copier %s, type %d", swidget->widget->name, swidget->id);1725 1726 switch (swidget->id) {1727 case snd_soc_dapm_aif_in:1728 case snd_soc_dapm_aif_out:1729 {1730 struct sof_ipc4_gtw_attributes *gtw_attr;1731 struct snd_sof_widget *pipe_widget;1732 struct sof_ipc4_pipeline *pipeline;1733 1734 /* parse the deep buffer dma size */1735 ret = sof_update_ipc_object(scomp, &deep_buffer_dma_ms,1736 SOF_COPIER_DEEP_BUFFER_TOKENS, swidget->tuples,1737 swidget->num_tuples, sizeof(u32), 1);1738 if (ret) {1739 dev_err(scomp->dev, "Failed to parse deep buffer dma size for %s\n",1740 swidget->widget->name);1741 return ret;1742 }1743 1744 ipc4_copier = (struct sof_ipc4_copier *)swidget->private;1745 gtw_attr = ipc4_copier->gtw_attr;1746 copier_data = &ipc4_copier->data;1747 available_fmt = &ipc4_copier->available_fmt;1748 1749 pipe_widget = swidget->spipe->pipe_widget;1750 pipeline = pipe_widget->private;1751 1752 if (pipeline->use_chain_dma) {1753 u32 host_dma_id;1754 u32 fifo_size;1755 1756 host_dma_id = platform_params->stream_tag - 1;1757 pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_HOST_ID(host_dma_id);1758 1759 /* Set SCS bit for S16_LE format only */1760 if (params_format(fe_params) == SNDRV_PCM_FORMAT_S16_LE)1761 pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_SCS_MASK;1762 1763 /*1764 * Despite its name the bitfield 'fifo_size' is used to define DMA buffer1765 * size. The expression calculates 2ms buffer size.1766 */1767 fifo_size = DIV_ROUND_UP((SOF_IPC4_CHAIN_DMA_BUF_SIZE_MS *1768 params_rate(fe_params) *1769 params_channels(fe_params) *1770 params_physical_width(fe_params)), 8000);1771 pipeline->msg.extension |= SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE(fifo_size);1772 1773 /*1774 * Chain DMA does not support stream timestamping, set node_id to invalid1775 * to skip the code in sof_ipc4_get_stream_start_offset().1776 */1777 copier_data->gtw_cfg.node_id = SOF_IPC4_INVALID_NODE_ID;1778 1779 return 0;1780 }1781 1782 /*1783 * Use the input_pin_fmts to match pcm params for playback and the output_pin_fmts1784 * for capture.1785 */1786 if (dir == SNDRV_PCM_STREAM_PLAYBACK)1787 ref_params = *fe_params;1788 else1789 ref_params = *pipeline_params;1790 1791 copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;1792 copier_data->gtw_cfg.node_id |=1793 SOF_IPC4_NODE_INDEX(platform_params->stream_tag - 1);1794 1795 /* set gateway attributes */1796 gtw_attr->lp_buffer_alloc = pipeline->lp_mode;1797 break;1798 }1799 case snd_soc_dapm_dai_in:1800 case snd_soc_dapm_dai_out:1801 {1802 struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;1803 struct sof_ipc4_pipeline *pipeline = pipe_widget->private;1804 1805 if (pipeline->use_chain_dma)1806 return 0;1807 1808 dai = swidget->private;1809 1810 ipc4_copier = (struct sof_ipc4_copier *)dai->private;1811 copier_data = &ipc4_copier->data;1812 available_fmt = &ipc4_copier->available_fmt;1813 1814 /*1815 * Use the fe_params as a base for the copier configuration.1816 * The ref_params might get updated to reflect what format is1817 * supported by the copier on the DAI side.1818 *1819 * In case of capture the ref_params returned will be used to1820 * find the input configuration of the copier.1821 */1822 ref_params = *fe_params;1823 ret = sof_ipc4_prepare_dai_copier(sdev, dai, &ref_params, dir);1824 if (ret < 0)1825 return ret;1826 1827 /*1828 * For playback the pipeline_params needs to be used to find the1829 * input configuration of the copier.1830 */1831 if (dir == SNDRV_PCM_STREAM_PLAYBACK)1832 ref_params = *pipeline_params;1833 1834 break;1835 }1836 case snd_soc_dapm_buffer:1837 {1838 ipc4_copier = (struct sof_ipc4_copier *)swidget->private;1839 copier_data = &ipc4_copier->data;1840 available_fmt = &ipc4_copier->available_fmt;1841 ref_params = *pipeline_params;1842 1843 break;1844 }1845 default:1846 dev_err(sdev->dev, "unsupported type %d for copier %s",1847 swidget->id, swidget->widget->name);1848 return -EINVAL;1849 }1850 1851 /* set input and output audio formats */1852 ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &copier_data->base_config,1853 &ref_params, available_fmt);1854 if (ret < 0)1855 return ret;1856 1857 /* set the reference params for output format selection */1858 single_output_bitdepth = sof_ipc4_copier_is_single_bitdepth(sdev,1859 available_fmt->output_pin_fmts,1860 available_fmt->num_output_formats);1861 switch (swidget->id) {1862 case snd_soc_dapm_aif_in:1863 case snd_soc_dapm_dai_out:1864 case snd_soc_dapm_buffer:1865 {1866 struct sof_ipc4_audio_format *in_fmt;1867 1868 in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt;1869 out_ref_rate = in_fmt->sampling_frequency;1870 out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);1871 1872 if (!single_output_bitdepth)1873 out_ref_valid_bits =1874 SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);1875 break;1876 }1877 case snd_soc_dapm_aif_out:1878 case snd_soc_dapm_dai_in:1879 out_ref_rate = params_rate(fe_params);1880 out_ref_channels = params_channels(fe_params);1881 if (!single_output_bitdepth) {1882 out_ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params);1883 if (out_ref_valid_bits < 0)1884 return out_ref_valid_bits;1885 }1886 break;1887 default:1888 /*1889 * Unsupported type should be caught by the former switch default1890 * case, this should never happen in reality.1891 */1892 return -EINVAL;1893 }1894 1895 /*1896 * if the output format is the same across all available output formats, choose1897 * that as the reference.1898 */1899 if (single_output_bitdepth) {1900 struct sof_ipc4_audio_format *out_fmt;1901 1902 out_fmt = &available_fmt->output_pin_fmts[0].audio_fmt;1903 out_ref_valid_bits =1904 SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(out_fmt->fmt_cfg);1905 }1906 1907 dev_dbg(sdev->dev, "copier %s: reference output rate %d, channels %d valid_bits %d\n",1908 swidget->widget->name, out_ref_rate, out_ref_channels, out_ref_valid_bits);1909 1910 output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, &copier_data->base_config,1911 available_fmt, out_ref_rate,1912 out_ref_channels, out_ref_valid_bits);1913 if (output_fmt_index < 0) {1914 dev_err(sdev->dev, "Failed to initialize output format for %s",1915 swidget->widget->name);1916 return output_fmt_index;1917 }1918 1919 /*1920 * Set the output format. Current topology defines pin 0 input and output formats in pairs.1921 * This assumes that the pin 0 formats are defined before all other pins.1922 * So pick the output audio format with the same index as the chosen1923 * input format. This logic will need to be updated when the format definitions1924 * in topology change.1925 */1926 memcpy(&copier_data->out_format,1927 &available_fmt->output_pin_fmts[output_fmt_index].audio_fmt,1928 sizeof(struct sof_ipc4_audio_format));1929 dev_dbg(sdev->dev, "Output audio format for %s\n", swidget->widget->name);1930 sof_ipc4_dbg_audio_format(sdev->dev, &available_fmt->output_pin_fmts[output_fmt_index], 1);1931 1932 switch (swidget->id) {1933 case snd_soc_dapm_dai_in:1934 case snd_soc_dapm_dai_out:1935 {1936 /*1937 * Only SOF_DAI_INTEL_ALH needs copier_data to set blob.1938 * That's why only ALH dai's blob is set after sof_ipc4_init_input_audio_fmt1939 */1940 if (ipc4_copier->dai_type == SOF_DAI_INTEL_ALH) {1941 struct sof_ipc4_alh_configuration_blob *blob;1942 struct sof_ipc4_dma_config *dma_config;1943 struct sof_ipc4_copier_data *alh_data;1944 struct sof_ipc4_copier *alh_copier;1945 struct snd_sof_widget *w;1946 u32 ch_count = 0;1947 u32 ch_mask = 0;1948 u32 ch_map;1949 u32 step;1950 u32 mask;1951 1952 blob = (struct sof_ipc4_alh_configuration_blob *)ipc4_copier->copier_config;1953 1954 blob->gw_attr.lp_buffer_alloc = 0;1955 1956 /* Get channel_mask from ch_map */1957 ch_map = copier_data->base_config.audio_fmt.ch_map;1958 for (i = 0; ch_map; i++) {1959 if ((ch_map & 0xf) != 0xf) {1960 ch_mask |= BIT(i);1961 ch_count++;1962 }1963 ch_map >>= 4;1964 }1965 1966 step = ch_count / blob->alh_cfg.device_count;1967 mask = GENMASK(step - 1, 0);1968 /*1969 * Set each gtw_cfg.node_id to blob->alh_cfg.mapping[]1970 * for all widgets with the same stream name1971 */1972 i = 0;1973 list_for_each_entry(w, &sdev->widget_list, list) {1974 u32 node_type;1975 1976 if (w->widget->sname &&1977 strcmp(w->widget->sname, swidget->widget->sname))1978 continue;1979 1980 dai = w->private;1981 alh_copier = (struct sof_ipc4_copier *)dai->private;1982 alh_data = &alh_copier->data;1983 node_type = SOF_IPC4_GET_NODE_TYPE(alh_data->gtw_cfg.node_id);1984 blob->alh_cfg.mapping[i].device = SOF_IPC4_NODE_TYPE(node_type);1985 blob->alh_cfg.mapping[i].device |=1986 SOF_IPC4_NODE_INDEX(alh_copier->dai_index);1987 1988 /*1989 * The mapping[i] device in ALH blob should be the same as the1990 * dma_config_tlv[i] mapping device if a dma_config_tlv is present.1991 * The device id will be used for DMA tlv mapping purposes.1992 */1993 if (ipc4_copier->dma_config_tlv[i].length) {1994 dma_config = &ipc4_copier->dma_config_tlv[i].dma_config;1995 blob->alh_cfg.mapping[i].device =1996 dma_config->dma_stream_channel_map.mapping[0].device;1997 }1998 1999 /*2000 * Set the same channel mask for playback as the audio data is2001 * duplicated for all speakers. For capture, split the channels2002 * among the aggregated DAIs. For example, with 4 channels on 22003 * aggregated DAIs, the channel_mask should be 0x3 and 0xc for the2004 * two DAI's.2005 * The channel masks used depend on the cpu_dais used in the2006 * dailink at the machine driver level, which actually comes from2007 * the tables in soc_acpi files depending on the _ADR and devID2008 * registers for each codec.2009 */2010 if (w->id == snd_soc_dapm_dai_in)2011 blob->alh_cfg.mapping[i].channel_mask = ch_mask;2012 else2013 blob->alh_cfg.mapping[i].channel_mask = mask << (step * i);2014 2015 i++;2016 }2017 if (blob->alh_cfg.device_count > 1) {2018 int group_id;2019 2020 group_id = ida_alloc_max(&alh_group_ida, ALH_MULTI_GTW_COUNT - 1,2021 GFP_KERNEL);2022 2023 if (group_id < 0)2024 return group_id;2025 2026 /* add multi-gateway base */2027 group_id += ALH_MULTI_GTW_BASE;2028 copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;2029 copier_data->gtw_cfg.node_id |= SOF_IPC4_NODE_INDEX(group_id);2030 }2031 }2032 }2033 }2034 2035 /* modify the input params for the next widget */2036 ret = sof_ipc4_update_hw_params(sdev, pipeline_params,2037 &copier_data->out_format,2038 BIT(SNDRV_PCM_HW_PARAM_FORMAT) |2039 BIT(SNDRV_PCM_HW_PARAM_CHANNELS) |2040 BIT(SNDRV_PCM_HW_PARAM_RATE));2041 if (ret)2042 return ret;2043 2044 /*2045 * Set the gateway dma_buffer_size to 2ms buffer size to meet the FW expectation. In the2046 * deep buffer case, set the dma_buffer_size depending on the deep_buffer_dma_ms set2047 * in topology.2048 */2049 switch (swidget->id) {2050 case snd_soc_dapm_dai_in:2051 copier_data->gtw_cfg.dma_buffer_size =2052 SOF_IPC4_MIN_DMA_BUFFER_SIZE * copier_data->base_config.ibs;2053 break;2054 case snd_soc_dapm_aif_in:2055 copier_data->gtw_cfg.dma_buffer_size =2056 max((u32)SOF_IPC4_MIN_DMA_BUFFER_SIZE, deep_buffer_dma_ms) *2057 copier_data->base_config.ibs;2058 dev_dbg(sdev->dev, "copier %s, dma buffer%s: %u ms (%u bytes)",2059 swidget->widget->name,2060 deep_buffer_dma_ms ? " (using Deep Buffer)" : "",2061 max((u32)SOF_IPC4_MIN_DMA_BUFFER_SIZE, deep_buffer_dma_ms),2062 copier_data->gtw_cfg.dma_buffer_size);2063 break;2064 case snd_soc_dapm_dai_out:2065 case snd_soc_dapm_aif_out:2066 copier_data->gtw_cfg.dma_buffer_size =2067 SOF_IPC4_MIN_DMA_BUFFER_SIZE * copier_data->base_config.obs;2068 break;2069 default:2070 break;2071 }2072 2073 data = &ipc4_copier->copier_config;2074 ipc_config_size = &ipc4_copier->ipc_config_size;2075 ipc_config_data = &ipc4_copier->ipc_config_data;2076 2077 /* config_length is DWORD based */2078 gtw_cfg_config_length = copier_data->gtw_cfg.config_length * 4;2079 ipc_size = sizeof(*copier_data) + gtw_cfg_config_length;2080 2081 dma_config_tlv_size = 0;2082 for (i = 0; i < SOF_IPC4_DMA_DEVICE_MAX_COUNT; i++) {2083 if (ipc4_copier->dma_config_tlv[i].type != SOF_IPC4_GTW_DMA_CONFIG_ID)2084 continue;2085 dma_config_tlv_size += ipc4_copier->dma_config_tlv[i].length;2086 dma_config_tlv_size +=2087 ipc4_copier->dma_config_tlv[i].dma_config.dma_priv_config_size;2088 dma_config_tlv_size += (sizeof(ipc4_copier->dma_config_tlv[i]) -2089 sizeof(ipc4_copier->dma_config_tlv[i].dma_config));2090 }2091 2092 if (dma_config_tlv_size) {2093 ipc_size += dma_config_tlv_size;2094 2095 /* we also need to increase the size at the gtw level */2096 copier_data->gtw_cfg.config_length += dma_config_tlv_size / 4;2097 }2098 2099 dev_dbg(sdev->dev, "copier %s, IPC size is %d", swidget->widget->name, ipc_size);2100 2101 *ipc_config_data = kzalloc(ipc_size, GFP_KERNEL);2102 if (!*ipc_config_data)2103 return -ENOMEM;2104 2105 *ipc_config_size = ipc_size;2106 2107 /* update pipeline memory usage */2108 sof_ipc4_update_resource_usage(sdev, swidget, &copier_data->base_config);2109 2110 /* copy IPC data */2111 memcpy(*ipc_config_data, (void *)copier_data, sizeof(*copier_data));2112 if (gtw_cfg_config_length)2113 memcpy(*ipc_config_data + sizeof(*copier_data),2114 *data, gtw_cfg_config_length);2115 2116 /* add DMA Config TLV, if configured */2117 if (dma_config_tlv_size)2118 memcpy(*ipc_config_data + sizeof(*copier_data) +2119 gtw_cfg_config_length,2120 &ipc4_copier->dma_config_tlv, dma_config_tlv_size);2121 2122 /*2123 * Restore gateway config length now that IPC payload is prepared. This avoids2124 * counting the DMA CONFIG TLV multiple times2125 */2126 copier_data->gtw_cfg.config_length = gtw_cfg_config_length / 4;2127 2128 return 0;2129}2130 2131static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget,2132 struct snd_pcm_hw_params *fe_params,2133 struct snd_sof_platform_stream_params *platform_params,2134 struct snd_pcm_hw_params *pipeline_params, int dir)2135{2136 struct snd_soc_component *scomp = swidget->scomp;2137 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);2138 struct sof_ipc4_gain *gain = swidget->private;2139 struct sof_ipc4_available_audio_format *available_fmt = &gain->available_fmt;2140 struct sof_ipc4_audio_format *in_fmt;2141 u32 out_ref_rate, out_ref_channels, out_ref_valid_bits;2142 int ret;2143 2144 ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &gain->data.base_config,2145 pipeline_params, available_fmt);2146 if (ret < 0)2147 return ret;2148 2149 in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt;2150 out_ref_rate = in_fmt->sampling_frequency;2151 out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);2152 out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);2153 2154 ret = sof_ipc4_init_output_audio_fmt(sdev, &gain->data.base_config, available_fmt,2155 out_ref_rate, out_ref_channels, out_ref_valid_bits);2156 if (ret < 0) {2157 dev_err(sdev->dev, "Failed to initialize output format for %s",2158 swidget->widget->name);2159 return ret;2160 }2161 2162 /* update pipeline memory usage */2163 sof_ipc4_update_resource_usage(sdev, swidget, &gain->data.base_config);2164 2165 return 0;2166}2167 2168static int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget,2169 struct snd_pcm_hw_params *fe_params,2170 struct snd_sof_platform_stream_params *platform_params,2171 struct snd_pcm_hw_params *pipeline_params, int dir)2172{2173 struct snd_soc_component *scomp = swidget->scomp;2174 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);2175 struct sof_ipc4_mixer *mixer = swidget->private;2176 struct sof_ipc4_available_audio_format *available_fmt = &mixer->available_fmt;2177 struct sof_ipc4_audio_format *in_fmt;2178 u32 out_ref_rate, out_ref_channels, out_ref_valid_bits;2179 int ret;2180 2181 ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &mixer->base_config,2182 pipeline_params, available_fmt);2183 if (ret < 0)2184 return ret;2185 2186 in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt;2187 out_ref_rate = in_fmt->sampling_frequency;2188 out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);2189 out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);2190 2191 ret = sof_ipc4_init_output_audio_fmt(sdev, &mixer->base_config, available_fmt,2192 out_ref_rate, out_ref_channels, out_ref_valid_bits);2193 if (ret < 0) {2194 dev_err(sdev->dev, "Failed to initialize output format for %s",2195 swidget->widget->name);2196 return ret;2197 }2198 2199 /* update pipeline memory usage */2200 sof_ipc4_update_resource_usage(sdev, swidget, &mixer->base_config);2201 2202 return 0;2203}2204 2205static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget,2206 struct snd_pcm_hw_params *fe_params,2207 struct snd_sof_platform_stream_params *platform_params,2208 struct snd_pcm_hw_params *pipeline_params, int dir)2209{2210 struct snd_soc_component *scomp = swidget->scomp;2211 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);2212 struct sof_ipc4_src *src = swidget->private;2213 struct sof_ipc4_available_audio_format *available_fmt = &src->available_fmt;2214 struct sof_ipc4_audio_format *out_audio_fmt;2215 struct sof_ipc4_audio_format *in_audio_fmt;2216 u32 out_ref_rate, out_ref_channels, out_ref_valid_bits;2217 int output_format_index, input_format_index;2218 2219 input_format_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, &src->data.base_config,2220 pipeline_params, available_fmt);2221 if (input_format_index < 0)2222 return input_format_index;2223 2224 /*2225 * For playback, the SRC sink rate will be configured based on the requested output2226 * format, which is restricted to only deal with DAI's with a single format for now.2227 */2228 if (dir == SNDRV_PCM_STREAM_PLAYBACK && available_fmt->num_output_formats > 1) {2229 dev_err(sdev->dev, "Invalid number of output formats: %d for SRC %s\n",2230 available_fmt->num_output_formats, swidget->widget->name);2231 return -EINVAL;2232 }2233 2234 /*2235 * SRC does not perform format conversion, so the output channels and valid bit depth must2236 * be the same as that of the input.2237 */2238 in_audio_fmt = &available_fmt->input_pin_fmts[input_format_index].audio_fmt;2239 out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_audio_fmt->fmt_cfg);2240 out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_audio_fmt->fmt_cfg);2241 2242 /*2243 * For capture, the SRC module should convert the rate to match the rate requested by the2244 * PCM hw_params. Set the reference params based on the fe_params unconditionally as it2245 * will be ignored for playback anyway.2246 */2247 out_ref_rate = params_rate(fe_params);2248 2249 output_format_index = sof_ipc4_init_output_audio_fmt(sdev, &src->data.base_config,2250 available_fmt, out_ref_rate,2251 out_ref_channels, out_ref_valid_bits);2252 if (output_format_index < 0) {2253 dev_err(sdev->dev, "Failed to initialize output format for %s",2254 swidget->widget->name);2255 return output_format_index;2256 }2257 2258 /* update pipeline memory usage */2259 sof_ipc4_update_resource_usage(sdev, swidget, &src->data.base_config);2260 2261 out_audio_fmt = &available_fmt->output_pin_fmts[output_format_index].audio_fmt;2262 src->data.sink_rate = out_audio_fmt->sampling_frequency;2263 2264 /* update pipeline_params for sink widgets */2265 return sof_ipc4_update_hw_params(sdev, pipeline_params, out_audio_fmt,2266 BIT(SNDRV_PCM_HW_PARAM_FORMAT) |2267 BIT(SNDRV_PCM_HW_PARAM_CHANNELS) |2268 BIT(SNDRV_PCM_HW_PARAM_RATE));2269}2270 2271static int2272sof_ipc4_process_set_pin_formats(struct snd_sof_widget *swidget, int pin_type)2273{2274 struct sof_ipc4_process *process = swidget->private;2275 struct sof_ipc4_base_module_cfg_ext *base_cfg_ext = process->base_config_ext;2276 struct sof_ipc4_available_audio_format *available_fmt = &process->available_fmt;2277 struct sof_ipc4_pin_format *pin_format, *format_list_to_search;2278 struct snd_soc_component *scomp = swidget->scomp;2279 int num_pins, format_list_count;2280 int pin_format_offset = 0;2281 int i, j;2282 2283 /* set number of pins, offset of pin format and format list to search based on pin type */2284 if (pin_type == SOF_PIN_TYPE_INPUT) {2285 num_pins = swidget->num_input_pins;2286 format_list_to_search = available_fmt->input_pin_fmts;2287 format_list_count = available_fmt->num_input_formats;2288 } else {2289 num_pins = swidget->num_output_pins;2290 pin_format_offset = swidget->num_input_pins;2291 format_list_to_search = available_fmt->output_pin_fmts;2292 format_list_count = available_fmt->num_output_formats;2293 }2294 2295 for (i = pin_format_offset; i < num_pins + pin_format_offset; i++) {2296 pin_format = &base_cfg_ext->pin_formats[i];2297 2298 /* Pin 0 audio formats are derived from the base config input/output format */2299 if (i == pin_format_offset) {2300 if (pin_type == SOF_PIN_TYPE_INPUT) {2301 pin_format->buffer_size = process->base_config.ibs;2302 pin_format->audio_fmt = process->base_config.audio_fmt;2303 } else {2304 pin_format->buffer_size = process->base_config.obs;2305 pin_format->audio_fmt = process->output_format;2306 }2307 continue;2308 }2309 2310 /*2311 * For all other pins, find the pin formats from those set in topology. If there2312 * is more than one format specified for a pin, this will pick the first available2313 * one.2314 */2315 for (j = 0; j < format_list_count; j++) {2316 struct sof_ipc4_pin_format *pin_format_item = &format_list_to_search[j];2317 2318 if (pin_format_item->pin_index == i - pin_format_offset) {2319 *pin_format = *pin_format_item;2320 break;2321 }2322 }2323 2324 if (j == format_list_count) {2325 dev_err(scomp->dev, "%s pin %d format not found for %s\n",2326 (pin_type == SOF_PIN_TYPE_INPUT) ? "input" : "output",2327 i - pin_format_offset, swidget->widget->name);2328 return -EINVAL;2329 }2330 }2331 2332 return 0;2333}2334 2335static int sof_ipc4_process_add_base_cfg_extn(struct snd_sof_widget *swidget)2336{2337 int ret, i;2338 2339 /* copy input and output pin formats */2340 for (i = 0; i <= SOF_PIN_TYPE_OUTPUT; i++) {2341 ret = sof_ipc4_process_set_pin_formats(swidget, i);2342 if (ret < 0)2343 return ret;2344 }2345 2346 return 0;2347}2348 2349static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget,2350 struct snd_pcm_hw_params *fe_params,2351 struct snd_sof_platform_stream_params *platform_params,2352 struct snd_pcm_hw_params *pipeline_params, int dir)2353{2354 struct snd_soc_component *scomp = swidget->scomp;2355 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);2356 struct sof_ipc4_process *process = swidget->private;2357 struct sof_ipc4_available_audio_format *available_fmt = &process->available_fmt;2358 struct sof_ipc4_audio_format *in_fmt;2359 u32 out_ref_rate, out_ref_channels, out_ref_valid_bits;2360 void *cfg = process->ipc_config_data;2361 int output_fmt_index;2362 int ret;2363 2364 ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &process->base_config,2365 pipeline_params, available_fmt);2366 if (ret < 0)2367 return ret;2368 2369 in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt;2370 out_ref_rate = in_fmt->sampling_frequency;2371 out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg);2372 out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg);2373 2374 output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, &process->base_config,2375 available_fmt, out_ref_rate,2376 out_ref_channels, out_ref_valid_bits);2377 if (output_fmt_index < 0 && available_fmt->num_output_formats) {2378 dev_err(sdev->dev, "Failed to initialize output format for %s",2379 swidget->widget->name);2380 return output_fmt_index;2381 }2382 2383 /* copy Pin 0 output format */2384 if (available_fmt->num_output_formats &&2385 output_fmt_index < available_fmt->num_output_formats &&2386 !available_fmt->output_pin_fmts[output_fmt_index].pin_index) {2387 memcpy(&process->output_format,2388 &available_fmt->output_pin_fmts[output_fmt_index].audio_fmt,2389 sizeof(struct sof_ipc4_audio_format));2390 2391 /* modify the pipeline params with the pin 0 output format */2392 ret = sof_ipc4_update_hw_params(sdev, pipeline_params,2393 &process->output_format,2394 BIT(SNDRV_PCM_HW_PARAM_FORMAT) |2395 BIT(SNDRV_PCM_HW_PARAM_CHANNELS) |2396 BIT(SNDRV_PCM_HW_PARAM_RATE));2397 if (ret)2398 return ret;2399 }2400 2401 /* update pipeline memory usage */2402 sof_ipc4_update_resource_usage(sdev, swidget, &process->base_config);2403 2404 /* ipc_config_data is composed of the base_config followed by an optional extension */2405 memcpy(cfg, &process->base_config, sizeof(struct sof_ipc4_base_module_cfg));2406 cfg += sizeof(struct sof_ipc4_base_module_cfg);2407 2408 if (process->init_config == SOF_IPC4_MODULE_INIT_CONFIG_TYPE_BASE_CFG_WITH_EXT) {2409 struct sof_ipc4_base_module_cfg_ext *base_cfg_ext = process->base_config_ext;2410 2411 ret = sof_ipc4_process_add_base_cfg_extn(swidget);2412 if (ret < 0)2413 return ret;2414 2415 memcpy(cfg, base_cfg_ext, process->base_config_ext_size);2416 }2417 2418 return 0;2419}2420 2421static int sof_ipc4_control_load_volume(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)2422{2423 struct sof_ipc4_control_data *control_data;2424 struct sof_ipc4_msg *msg;2425 int i;2426 2427 scontrol->size = struct_size(control_data, chanv, scontrol->num_channels);2428 2429 /* scontrol->ipc_control_data will be freed in sof_control_unload */2430 scontrol->ipc_control_data = kzalloc(scontrol->size, GFP_KERNEL);2431 if (!scontrol->ipc_control_data)2432 return -ENOMEM;2433 2434 control_data = scontrol->ipc_control_data;2435 control_data->index = scontrol->index;2436 2437 msg = &control_data->msg;2438 msg->primary = SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_SET);2439 msg->primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);2440 msg->primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);2441 2442 /* volume controls with range 0-1 (off/on) are switch controls */2443 if (scontrol->max == 1)2444 msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_SWITCH_CONTROL_PARAM_ID);2445 else2446 msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_GAIN_PARAM_ID);2447 2448 for (i = 0; i < scontrol->num_channels; i++) {2449 control_data->chanv[i].channel = i;2450 /*2451 * Default, initial values:2452 * - 0dB for volume controls2453 * - off (0) for switch controls - value already zero after2454 * memory allocation2455 */2456 if (scontrol->max > 1)2457 control_data->chanv[i].value = SOF_IPC4_VOL_ZERO_DB;2458 }2459 2460 return 0;2461}2462 2463static int sof_ipc4_control_load_enum(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)2464{2465 struct sof_ipc4_control_data *control_data;2466 struct sof_ipc4_msg *msg;2467 int i;2468 2469 scontrol->size = struct_size(control_data, chanv, scontrol->num_channels);2470 2471 /* scontrol->ipc_control_data will be freed in sof_control_unload */2472 scontrol->ipc_control_data = kzalloc(scontrol->size, GFP_KERNEL);2473 if (!scontrol->ipc_control_data)2474 return -ENOMEM;2475 2476 control_data = scontrol->ipc_control_data;2477 control_data->index = scontrol->index;2478 2479 msg = &control_data->msg;2480 msg->primary = SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_SET);2481 msg->primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);2482 msg->primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);2483 2484 msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_ENUM_CONTROL_PARAM_ID);2485 2486 /* Default, initial value for enums: first enum entry is selected (0) */2487 for (i = 0; i < scontrol->num_channels; i++)2488 control_data->chanv[i].channel = i;2489 2490 return 0;2491}2492 2493static int sof_ipc4_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)2494{2495 struct sof_ipc4_control_data *control_data;2496 struct sof_ipc4_msg *msg;2497 int ret;2498 2499 if (scontrol->max_size < (sizeof(*control_data) + sizeof(struct sof_abi_hdr))) {2500 dev_err(sdev->dev, "insufficient size for a bytes control %s: %zu.\n",2501 scontrol->name, scontrol->max_size);2502 return -EINVAL;2503 }2504 2505 if (scontrol->priv_size > scontrol->max_size - sizeof(*control_data)) {2506 dev_err(sdev->dev, "scontrol %s bytes data size %zu exceeds max %zu.\n",2507 scontrol->name, scontrol->priv_size,2508 scontrol->max_size - sizeof(*control_data));2509 return -EINVAL;2510 }2511 2512 scontrol->size = sizeof(struct sof_ipc4_control_data) + scontrol->priv_size;2513 2514 scontrol->ipc_control_data = kzalloc(scontrol->max_size, GFP_KERNEL);2515 if (!scontrol->ipc_control_data)2516 return -ENOMEM;2517 2518 control_data = scontrol->ipc_control_data;2519 control_data->index = scontrol->index;2520 if (scontrol->priv_size > 0) {2521 memcpy(control_data->data, scontrol->priv, scontrol->priv_size);2522 kfree(scontrol->priv);2523 scontrol->priv = NULL;2524 2525 if (control_data->data->magic != SOF_IPC4_ABI_MAGIC) {2526 dev_err(sdev->dev, "Wrong ABI magic (%#x) for control: %s\n",2527 control_data->data->magic, scontrol->name);2528 ret = -EINVAL;2529 goto err;2530 }2531 2532 /* TODO: check the ABI version */2533 2534 if (control_data->data->size + sizeof(struct sof_abi_hdr) !=2535 scontrol->priv_size) {2536 dev_err(sdev->dev, "Control %s conflict in bytes %zu vs. priv size %zu.\n",2537 scontrol->name,2538 control_data->data->size + sizeof(struct sof_abi_hdr),2539 scontrol->priv_size);2540 ret = -EINVAL;2541 goto err;2542 }2543 }2544 2545 msg = &control_data->msg;2546 msg->primary = SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_SET);2547 msg->primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);2548 msg->primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);2549 2550 return 0;2551 2552err:2553 kfree(scontrol->ipc_control_data);2554 scontrol->ipc_control_data = NULL;2555 return ret;2556}2557 2558static int sof_ipc4_control_setup(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)2559{2560 switch (scontrol->info_type) {2561 case SND_SOC_TPLG_CTL_VOLSW:2562 case SND_SOC_TPLG_CTL_VOLSW_SX:2563 case SND_SOC_TPLG_CTL_VOLSW_XR_SX:2564 return sof_ipc4_control_load_volume(sdev, scontrol);2565 case SND_SOC_TPLG_CTL_BYTES:2566 return sof_ipc4_control_load_bytes(sdev, scontrol);2567 case SND_SOC_TPLG_CTL_ENUM:2568 case SND_SOC_TPLG_CTL_ENUM_VALUE:2569 return sof_ipc4_control_load_enum(sdev, scontrol);2570 default:2571 break;2572 }2573 2574 return 0;2575}2576 2577static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)2578{2579 struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;2580 struct sof_ipc4_fw_data *ipc4_data = sdev->private;2581 struct sof_ipc4_pipeline *pipeline;2582 struct sof_ipc4_msg *msg;2583 void *ipc_data = NULL;2584 u32 ipc_size = 0;2585 int ret;2586 2587 switch (swidget->id) {2588 case snd_soc_dapm_scheduler:2589 pipeline = swidget->private;2590 2591 if (pipeline->use_chain_dma) {2592 dev_warn(sdev->dev, "use_chain_dma set for scheduler %s",2593 swidget->widget->name);2594 return 0;2595 }2596 2597 dev_dbg(sdev->dev, "pipeline: %d memory pages: %d\n", swidget->pipeline_id,2598 pipeline->mem_usage);2599 2600 msg = &pipeline->msg;2601 msg->primary |= pipeline->mem_usage;2602 2603 swidget->instance_id = ida_alloc_max(&pipeline_ida, ipc4_data->max_num_pipelines,2604 GFP_KERNEL);2605 if (swidget->instance_id < 0) {2606 dev_err(sdev->dev, "failed to assign pipeline id for %s: %d\n",2607 swidget->widget->name, swidget->instance_id);2608 return swidget->instance_id;2609 }2610 msg->primary &= ~SOF_IPC4_GLB_PIPE_INSTANCE_MASK;2611 msg->primary |= SOF_IPC4_GLB_PIPE_INSTANCE_ID(swidget->instance_id);2612 break;2613 case snd_soc_dapm_aif_in:2614 case snd_soc_dapm_aif_out:2615 case snd_soc_dapm_buffer:2616 {2617 struct sof_ipc4_copier *ipc4_copier = swidget->private;2618 2619 pipeline = pipe_widget->private;2620 if (pipeline->use_chain_dma)2621 return 0;2622 2623 ipc_size = ipc4_copier->ipc_config_size;2624 ipc_data = ipc4_copier->ipc_config_data;2625 2626 msg = &ipc4_copier->msg;2627 break;2628 }2629 case snd_soc_dapm_dai_in:2630 case snd_soc_dapm_dai_out:2631 {2632 struct snd_sof_dai *dai = swidget->private;2633 struct sof_ipc4_copier *ipc4_copier = dai->private;2634 2635 pipeline = pipe_widget->private;2636 if (pipeline->use_chain_dma)2637 return 0;2638 2639 ipc_size = ipc4_copier->ipc_config_size;2640 ipc_data = ipc4_copier->ipc_config_data;2641 2642 msg = &ipc4_copier->msg;2643 break;2644 }2645 case snd_soc_dapm_pga:2646 {2647 struct sof_ipc4_gain *gain = swidget->private;2648 2649 ipc_size = sizeof(gain->data);2650 ipc_data = &gain->data;2651 2652 msg = &gain->msg;2653 break;2654 }2655 case snd_soc_dapm_mixer:2656 {2657 struct sof_ipc4_mixer *mixer = swidget->private;2658 2659 ipc_size = sizeof(mixer->base_config);2660 ipc_data = &mixer->base_config;2661 2662 msg = &mixer->msg;2663 break;2664 }2665 case snd_soc_dapm_src:2666 {2667 struct sof_ipc4_src *src = swidget->private;2668 2669 ipc_size = sizeof(src->data);2670 ipc_data = &src->data;2671 2672 msg = &src->msg;2673 break;2674 }2675 case snd_soc_dapm_effect:2676 {2677 struct sof_ipc4_process *process = swidget->private;2678 2679 if (!process->ipc_config_size) {2680 dev_err(sdev->dev, "module %s has no config data!\n",2681 swidget->widget->name);2682 return -EINVAL;2683 }2684 2685 ipc_size = process->ipc_config_size;2686 ipc_data = process->ipc_config_data;2687 2688 msg = &process->msg;2689 break;2690 }2691 default:2692 dev_err(sdev->dev, "widget type %d not supported", swidget->id);2693 return -EINVAL;2694 }2695 2696 if (swidget->id != snd_soc_dapm_scheduler) {2697 int module_id = msg->primary & SOF_IPC4_MOD_ID_MASK;2698 2699 ret = sof_ipc4_widget_assign_instance_id(sdev, swidget);2700 if (ret < 0) {2701 dev_err(sdev->dev, "failed to assign instance id for %s\n",2702 swidget->widget->name);2703 return ret;2704 }2705 2706 msg->primary &= ~SOF_IPC4_MOD_INSTANCE_MASK;2707 msg->primary |= SOF_IPC4_MOD_INSTANCE(swidget->instance_id);2708 2709 msg->extension &= ~SOF_IPC4_MOD_EXT_PARAM_SIZE_MASK;2710 msg->extension |= ipc_size >> 2;2711 2712 msg->extension &= ~SOF_IPC4_MOD_EXT_PPL_ID_MASK;2713 msg->extension |= SOF_IPC4_MOD_EXT_PPL_ID(pipe_widget->instance_id);2714 2715 dev_dbg(sdev->dev, "Create widget %s (pipe %d) - ID %d, instance %d, core %d\n",2716 swidget->widget->name, swidget->pipeline_id, module_id,2717 swidget->instance_id, swidget->core);2718 } else {2719 dev_dbg(sdev->dev, "Create pipeline %s (pipe %d) - instance %d, core %d\n",2720 swidget->widget->name, swidget->pipeline_id,2721 swidget->instance_id, swidget->core);2722 }2723 2724 msg->data_size = ipc_size;2725 msg->data_ptr = ipc_data;2726 2727 ret = sof_ipc_tx_message_no_reply(sdev->ipc, msg, ipc_size);2728 if (ret < 0) {2729 dev_err(sdev->dev, "failed to create module %s\n", swidget->widget->name);2730 2731 if (swidget->id != snd_soc_dapm_scheduler) {2732 struct sof_ipc4_fw_module *fw_module = swidget->module_info;2733 2734 ida_free(&fw_module->m_ida, swidget->instance_id);2735 } else {2736 ida_free(&pipeline_ida, swidget->instance_id);2737 }2738 }2739 2740 return ret;2741}2742 2743static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)2744{2745 struct sof_ipc4_fw_module *fw_module = swidget->module_info;2746 struct sof_ipc4_fw_data *ipc4_data = sdev->private;2747 int ret = 0;2748 2749 mutex_lock(&ipc4_data->pipeline_state_mutex);2750 2751 /* freeing a pipeline frees all the widgets associated with it */2752 if (swidget->id == snd_soc_dapm_scheduler) {2753 struct sof_ipc4_pipeline *pipeline = swidget->private;2754 struct sof_ipc4_msg msg = {{ 0 }};2755 u32 header;2756 2757 if (pipeline->use_chain_dma) {2758 dev_warn(sdev->dev, "use_chain_dma set for scheduler %s",2759 swidget->widget->name);2760 mutex_unlock(&ipc4_data->pipeline_state_mutex);2761 return 0;2762 }2763 2764 header = SOF_IPC4_GLB_PIPE_INSTANCE_ID(swidget->instance_id);2765 header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_DELETE_PIPELINE);2766 header |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);2767 header |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG);2768 2769 msg.primary = header;2770 2771 ret = sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0);2772 if (ret < 0)2773 dev_err(sdev->dev, "failed to free pipeline widget %s\n",2774 swidget->widget->name);2775 2776 pipeline->mem_usage = 0;2777 pipeline->state = SOF_IPC4_PIPE_UNINITIALIZED;2778 ida_free(&pipeline_ida, swidget->instance_id);2779 swidget->instance_id = -EINVAL;2780 } else {2781 struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;2782 struct sof_ipc4_pipeline *pipeline = pipe_widget->private;2783 2784 if (!pipeline->use_chain_dma)2785 ida_free(&fw_module->m_ida, swidget->instance_id);2786 }2787 2788 mutex_unlock(&ipc4_data->pipeline_state_mutex);2789 2790 return ret;2791}2792 2793static int sof_ipc4_get_queue_id(struct snd_sof_widget *src_widget,2794 struct snd_sof_widget *sink_widget, bool pin_type)2795{2796 struct snd_sof_widget *current_swidget;2797 struct snd_soc_component *scomp;2798 struct ida *queue_ida;2799 const char *buddy_name;2800 char **pin_binding;2801 u32 num_pins;2802 int i;2803 2804 if (pin_type == SOF_PIN_TYPE_OUTPUT) {2805 current_swidget = src_widget;2806 pin_binding = src_widget->output_pin_binding;2807 queue_ida = &src_widget->output_queue_ida;2808 num_pins = src_widget->num_output_pins;2809 buddy_name = sink_widget->widget->name;2810 } else {2811 current_swidget = sink_widget;2812 pin_binding = sink_widget->input_pin_binding;2813 queue_ida = &sink_widget->input_queue_ida;2814 num_pins = sink_widget->num_input_pins;2815 buddy_name = src_widget->widget->name;2816 }2817 2818 scomp = current_swidget->scomp;2819 2820 if (num_pins < 1) {2821 dev_err(scomp->dev, "invalid %s num_pins: %d for queue allocation for %s\n",2822 (pin_type == SOF_PIN_TYPE_OUTPUT ? "output" : "input"),2823 num_pins, current_swidget->widget->name);2824 return -EINVAL;2825 }2826 2827 /* If there is only one input/output pin, queue id must be 0 */2828 if (num_pins == 1)2829 return 0;2830 2831 /* Allocate queue ID from pin binding array if it is defined in topology. */2832 if (pin_binding) {2833 for (i = 0; i < num_pins; i++) {2834 if (!strcmp(pin_binding[i], buddy_name))2835 return i;2836 }2837 /*2838 * Fail if no queue ID found from pin binding array, so that we don't2839 * mixed use pin binding array and ida for queue ID allocation.2840 */2841 dev_err(scomp->dev, "no %s queue id found from pin binding array for %s\n",2842 (pin_type == SOF_PIN_TYPE_OUTPUT ? "output" : "input"),2843 current_swidget->widget->name);2844 return -EINVAL;2845 }2846 2847 /* If no pin binding array specified in topology, use ida to allocate one */2848 return ida_alloc_max(queue_ida, num_pins, GFP_KERNEL);2849}2850 2851static void sof_ipc4_put_queue_id(struct snd_sof_widget *swidget, int queue_id,2852 bool pin_type)2853{2854 struct ida *queue_ida;2855 char **pin_binding;2856 int num_pins;2857 2858 if (pin_type == SOF_PIN_TYPE_OUTPUT) {2859 pin_binding = swidget->output_pin_binding;2860 queue_ida = &swidget->output_queue_ida;2861 num_pins = swidget->num_output_pins;2862 } else {2863 pin_binding = swidget->input_pin_binding;2864 queue_ida = &swidget->input_queue_ida;2865 num_pins = swidget->num_input_pins;2866 }2867 2868 /* Nothing to free if queue ID is not allocated with ida. */2869 if (num_pins == 1 || pin_binding)2870 return;2871 2872 ida_free(queue_ida, queue_id);2873}2874 2875static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev,2876 struct snd_sof_widget *src_widget,2877 struct snd_sof_widget *sink_widget,2878 struct snd_sof_route *sroute)2879{2880 struct sof_ipc4_copier_config_set_sink_format format;2881 const struct sof_ipc_ops *iops = sdev->ipc->ops;2882 struct sof_ipc4_base_module_cfg *src_config;2883 const struct sof_ipc4_audio_format *pin_fmt;2884 struct sof_ipc4_fw_module *fw_module;2885 struct sof_ipc4_msg msg = {{ 0 }};2886 2887 if (WIDGET_IS_DAI(src_widget->id)) {2888 struct snd_sof_dai *dai = src_widget->private;2889 2890 src_config = dai->private;2891 } else {2892 src_config = src_widget->private;2893 }2894 2895 fw_module = src_widget->module_info;2896 2897 format.sink_id = sroute->src_queue_id;2898 memcpy(&format.source_fmt, &src_config->audio_fmt, sizeof(format.source_fmt));2899 2900 pin_fmt = sof_ipc4_get_input_pin_audio_fmt(sink_widget, sroute->dst_queue_id);2901 if (!pin_fmt) {2902 dev_err(sdev->dev,2903 "Failed to get input audio format of %s:%d for output of %s:%d\n",2904 sink_widget->widget->name, sroute->dst_queue_id,2905 src_widget->widget->name, sroute->src_queue_id);2906 return -EINVAL;2907 }2908 2909 memcpy(&format.sink_fmt, pin_fmt, sizeof(format.sink_fmt));2910 2911 msg.data_size = sizeof(format);2912 msg.data_ptr = &format;2913 2914 msg.primary = fw_module->man4_module_entry.id;2915 msg.primary |= SOF_IPC4_MOD_INSTANCE(src_widget->instance_id);2916 msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);2917 msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);2918 2919 msg.extension =2920 SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_COPIER_MODULE_CFG_PARAM_SET_SINK_FORMAT);2921 2922 return iops->set_get_data(sdev, &msg, msg.data_size, true);2923}2924 2925static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *sroute)2926{2927 struct snd_sof_widget *src_widget = sroute->src_widget;2928 struct snd_sof_widget *sink_widget = sroute->sink_widget;2929 struct snd_sof_widget *src_pipe_widget = src_widget->spipe->pipe_widget;2930 struct snd_sof_widget *sink_pipe_widget = sink_widget->spipe->pipe_widget;2931 struct sof_ipc4_fw_module *src_fw_module = src_widget->module_info;2932 struct sof_ipc4_fw_module *sink_fw_module = sink_widget->module_info;2933 struct sof_ipc4_pipeline *src_pipeline = src_pipe_widget->private;2934 struct sof_ipc4_pipeline *sink_pipeline = sink_pipe_widget->private;2935 struct sof_ipc4_msg msg = {{ 0 }};2936 u32 header, extension;2937 int ret;2938 2939 /* no route set up if chain DMA is used */2940 if (src_pipeline->use_chain_dma || sink_pipeline->use_chain_dma) {2941 if (!src_pipeline->use_chain_dma || !sink_pipeline->use_chain_dma) {2942 dev_err(sdev->dev,2943 "use_chain_dma must be set for both src %s and sink %s pipelines\n",2944 src_widget->widget->name, sink_widget->widget->name);2945 return -EINVAL;2946 }2947 return 0;2948 }2949 2950 if (!src_fw_module || !sink_fw_module) {2951 dev_err(sdev->dev,2952 "cannot bind %s -> %s, no firmware module for: %s%s\n",2953 src_widget->widget->name, sink_widget->widget->name,2954 src_fw_module ? "" : " source",2955 sink_fw_module ? "" : " sink");2956 2957 return -ENODEV;2958 }2959 2960 sroute->src_queue_id = sof_ipc4_get_queue_id(src_widget, sink_widget,2961 SOF_PIN_TYPE_OUTPUT);2962 if (sroute->src_queue_id < 0) {2963 dev_err(sdev->dev,2964 "failed to get src_queue_id ID from source widget %s\n",2965 src_widget->widget->name);2966 return sroute->src_queue_id;2967 }2968 2969 sroute->dst_queue_id = sof_ipc4_get_queue_id(src_widget, sink_widget,2970 SOF_PIN_TYPE_INPUT);2971 if (sroute->dst_queue_id < 0) {2972 dev_err(sdev->dev,2973 "failed to get dst_queue_id ID from sink widget %s\n",2974 sink_widget->widget->name);2975 sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id,2976 SOF_PIN_TYPE_OUTPUT);2977 return sroute->dst_queue_id;2978 }2979 2980 /* Pin 0 format is already set during copier module init */2981 if (sroute->src_queue_id > 0 && WIDGET_IS_COPIER(src_widget->id)) {2982 ret = sof_ipc4_set_copier_sink_format(sdev, src_widget,2983 sink_widget, sroute);2984 if (ret < 0) {2985 dev_err(sdev->dev,2986 "failed to set sink format for source %s:%d\n",2987 src_widget->widget->name, sroute->src_queue_id);2988 goto out;2989 }2990 }2991 2992 dev_dbg(sdev->dev, "bind %s:%d -> %s:%d\n",2993 src_widget->widget->name, sroute->src_queue_id,2994 sink_widget->widget->name, sroute->dst_queue_id);2995 2996 header = src_fw_module->man4_module_entry.id;2997 header |= SOF_IPC4_MOD_INSTANCE(src_widget->instance_id);2998 header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_BIND);2999 header |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);3000 header |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);3001 3002 extension = sink_fw_module->man4_module_entry.id;3003 extension |= SOF_IPC4_MOD_EXT_DST_MOD_INSTANCE(sink_widget->instance_id);3004 extension |= SOF_IPC4_MOD_EXT_DST_MOD_QUEUE_ID(sroute->dst_queue_id);3005 extension |= SOF_IPC4_MOD_EXT_SRC_MOD_QUEUE_ID(sroute->src_queue_id);3006 3007 msg.primary = header;3008 msg.extension = extension;3009 3010 ret = sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0);3011 if (ret < 0) {3012 dev_err(sdev->dev, "failed to bind modules %s:%d -> %s:%d\n",3013 src_widget->widget->name, sroute->src_queue_id,3014 sink_widget->widget->name, sroute->dst_queue_id);3015 goto out;3016 }3017 3018 return ret;3019 3020out:3021 sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_OUTPUT);3022 sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id, SOF_PIN_TYPE_INPUT);3023 return ret;3024}3025 3026static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *sroute)3027{3028 struct snd_sof_widget *src_widget = sroute->src_widget;3029 struct snd_sof_widget *sink_widget = sroute->sink_widget;3030 struct sof_ipc4_fw_module *src_fw_module = src_widget->module_info;3031 struct sof_ipc4_fw_module *sink_fw_module = sink_widget->module_info;3032 struct sof_ipc4_msg msg = {{ 0 }};3033 struct snd_sof_widget *src_pipe_widget = src_widget->spipe->pipe_widget;3034 struct snd_sof_widget *sink_pipe_widget = sink_widget->spipe->pipe_widget;3035 struct sof_ipc4_pipeline *src_pipeline = src_pipe_widget->private;3036 struct sof_ipc4_pipeline *sink_pipeline = sink_pipe_widget->private;3037 u32 header, extension;3038 int ret = 0;3039 3040 /* no route is set up if chain DMA is used */3041 if (src_pipeline->use_chain_dma || sink_pipeline->use_chain_dma)3042 return 0;3043 3044 dev_dbg(sdev->dev, "unbind modules %s:%d -> %s:%d\n",3045 src_widget->widget->name, sroute->src_queue_id,3046 sink_widget->widget->name, sroute->dst_queue_id);3047 3048 /*3049 * routes belonging to the same pipeline will be disconnected by the FW when the pipeline3050 * is freed. So avoid sending this IPC which will be ignored by the FW anyway.3051 */3052 if (src_widget->spipe->pipe_widget == sink_widget->spipe->pipe_widget)3053 goto out;3054 3055 header = src_fw_module->man4_module_entry.id;3056 header |= SOF_IPC4_MOD_INSTANCE(src_widget->instance_id);3057 header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_UNBIND);3058 header |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);3059 header |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);3060 3061 extension = sink_fw_module->man4_module_entry.id;3062 extension |= SOF_IPC4_MOD_EXT_DST_MOD_INSTANCE(sink_widget->instance_id);3063 extension |= SOF_IPC4_MOD_EXT_DST_MOD_QUEUE_ID(sroute->dst_queue_id);3064 extension |= SOF_IPC4_MOD_EXT_SRC_MOD_QUEUE_ID(sroute->src_queue_id);3065 3066 msg.primary = header;3067 msg.extension = extension;3068 3069 ret = sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0);3070 if (ret < 0)3071 dev_err(sdev->dev, "failed to unbind modules %s:%d -> %s:%d\n",3072 src_widget->widget->name, sroute->src_queue_id,3073 sink_widget->widget->name, sroute->dst_queue_id);3074out:3075 sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id, SOF_PIN_TYPE_INPUT);3076 sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_OUTPUT);3077 3078 return ret;3079}3080 3081static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,3082 unsigned int flags, struct snd_sof_dai_config_data *data)3083{3084 struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;3085 struct sof_ipc4_pipeline *pipeline = pipe_widget->private;3086 struct snd_sof_dai *dai = swidget->private;3087 struct sof_ipc4_gtw_attributes *gtw_attr;3088 struct sof_ipc4_copier_data *copier_data;3089 struct sof_ipc4_copier *ipc4_copier;3090 3091 if (!dai || !dai->private) {3092 dev_err(sdev->dev, "Invalid DAI or DAI private data for %s\n",3093 swidget->widget->name);3094 return -EINVAL;3095 }3096 3097 ipc4_copier = (struct sof_ipc4_copier *)dai->private;3098 copier_data = &ipc4_copier->data;3099 3100 if (!data)3101 return 0;3102 3103 if (pipeline->use_chain_dma) {3104 /*3105 * Only configure the DMA Link ID for ChainDMA when this op is3106 * invoked with SOF_DAI_CONFIG_FLAGS_HW_PARAMS3107 */3108 if (flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS) {3109 pipeline->msg.primary &= ~SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK;3110 pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(data->dai_data);3111 }3112 return 0;3113 }3114 3115 switch (ipc4_copier->dai_type) {3116 case SOF_DAI_INTEL_HDA:3117 gtw_attr = ipc4_copier->gtw_attr;3118 gtw_attr->lp_buffer_alloc = pipeline->lp_mode;3119 if (flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS) {3120 copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;3121 copier_data->gtw_cfg.node_id |= SOF_IPC4_NODE_INDEX(data->dai_data);3122 }3123 break;3124 case SOF_DAI_INTEL_ALH:3125 /*3126 * Do not clear the node ID when this op is invoked with3127 * SOF_DAI_CONFIG_FLAGS_HW_FREE. It is needed to free the group_ida during3128 * unprepare. The node_id for multi-gateway DAI's will be overwritten with the3129 * group_id during copier's ipc_prepare op.3130 */3131 if (flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS) {3132 struct sof_ipc4_alh_configuration_blob *blob;3133 3134 blob = (struct sof_ipc4_alh_configuration_blob *)ipc4_copier->copier_config;3135 ipc4_copier->dai_index = data->dai_node_id;3136 3137 /*3138 * no need to set the node_id for aggregated DAI's. These will be assigned3139 * a group_id during widget ipc_prepare3140 */3141 if (blob->alh_cfg.device_count == 1) {3142 copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;3143 copier_data->gtw_cfg.node_id |=3144 SOF_IPC4_NODE_INDEX(data->dai_node_id);3145 }3146 }3147 3148 break;3149 case SOF_DAI_INTEL_DMIC:3150 case SOF_DAI_INTEL_SSP:3151 /* nothing to do for SSP/DMIC */3152 break;3153 default:3154 dev_err(sdev->dev, "%s: unsupported dai type %d\n", __func__,3155 ipc4_copier->dai_type);3156 return -EINVAL;3157 }3158 3159 return 0;3160}3161 3162static int sof_ipc4_parse_manifest(struct snd_soc_component *scomp, int index,3163 struct snd_soc_tplg_manifest *man)3164{3165 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);3166 struct sof_ipc4_fw_data *ipc4_data = sdev->private;3167 struct sof_manifest_tlv *manifest_tlv;3168 struct sof_manifest *manifest;3169 u32 size = le32_to_cpu(man->priv.size);3170 u8 *man_ptr = man->priv.data;3171 u32 len_check;3172 int i;3173 3174 if (!size || size < SOF_IPC4_TPLG_ABI_SIZE) {3175 dev_err(scomp->dev, "%s: Invalid topology ABI size: %u\n",3176 __func__, size);3177 return -EINVAL;3178 }3179 3180 manifest = (struct sof_manifest *)man_ptr;3181 3182 dev_info(scomp->dev,3183 "Topology: ABI %d:%d:%d Kernel ABI %u:%u:%u\n",3184 le16_to_cpu(manifest->abi_major), le16_to_cpu(manifest->abi_minor),3185 le16_to_cpu(manifest->abi_patch),3186 SOF_ABI_MAJOR, SOF_ABI_MINOR, SOF_ABI_PATCH);3187 3188 /* TODO: Add ABI compatibility check */3189 3190 /* no more data after the ABI version */3191 if (size <= SOF_IPC4_TPLG_ABI_SIZE)3192 return 0;3193 3194 manifest_tlv = manifest->items;3195 len_check = sizeof(struct sof_manifest);3196 for (i = 0; i < le16_to_cpu(manifest->count); i++) {3197 len_check += sizeof(struct sof_manifest_tlv) + le32_to_cpu(manifest_tlv->size);3198 if (len_check > size)3199 return -EINVAL;3200 3201 switch (le32_to_cpu(manifest_tlv->type)) {3202 case SOF_MANIFEST_DATA_TYPE_NHLT:3203 /* no NHLT in BIOS, so use the one from topology manifest */3204 if (ipc4_data->nhlt)3205 break;3206 ipc4_data->nhlt = devm_kmemdup(sdev->dev, manifest_tlv->data,3207 le32_to_cpu(manifest_tlv->size), GFP_KERNEL);3208 if (!ipc4_data->nhlt)3209 return -ENOMEM;3210 break;3211 default:3212 dev_warn(scomp->dev, "Skipping unknown manifest data type %d\n",3213 manifest_tlv->type);3214 break;3215 }3216 man_ptr += sizeof(struct sof_manifest_tlv) + le32_to_cpu(manifest_tlv->size);3217 manifest_tlv = (struct sof_manifest_tlv *)man_ptr;3218 }3219 3220 return 0;3221}3222 3223static int sof_ipc4_dai_get_param(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int param_type)3224{3225 struct sof_ipc4_copier *ipc4_copier = dai->private;3226 struct snd_soc_tplg_hw_config *hw_config;3227 struct snd_sof_dai_link *slink;3228 bool dai_link_found = false;3229 bool hw_cfg_found = false;3230 int i;3231 3232 if (!ipc4_copier)3233 return 0;3234 3235 list_for_each_entry(slink, &sdev->dai_link_list, list) {3236 if (!strcmp(slink->link->name, dai->name)) {3237 dai_link_found = true;3238 break;3239 }3240 }3241 3242 if (!dai_link_found) {3243 dev_err(sdev->dev, "no DAI link found for DAI %s\n", dai->name);3244 return -EINVAL;3245 }3246 3247 for (i = 0; i < slink->num_hw_configs; i++) {3248 hw_config = &slink->hw_configs[i];3249 if (dai->current_config == le32_to_cpu(hw_config->id)) {3250 hw_cfg_found = true;3251 break;3252 }3253 }3254 3255 if (!hw_cfg_found) {3256 dev_err(sdev->dev, "no matching hw_config found for DAI %s\n", dai->name);3257 return -EINVAL;3258 }3259 3260 switch (ipc4_copier->dai_type) {3261 case SOF_DAI_INTEL_SSP:3262 switch (param_type) {3263 case SOF_DAI_PARAM_INTEL_SSP_MCLK:3264 return le32_to_cpu(hw_config->mclk_rate);3265 case SOF_DAI_PARAM_INTEL_SSP_BCLK:3266 return le32_to_cpu(hw_config->bclk_rate);3267 case SOF_DAI_PARAM_INTEL_SSP_TDM_SLOTS:3268 return le32_to_cpu(hw_config->tdm_slots);3269 default:3270 dev_err(sdev->dev, "invalid SSP param %d\n", param_type);3271 break;3272 }3273 break;3274 default:3275 dev_err(sdev->dev, "DAI type %d not supported yet!\n", ipc4_copier->dai_type);3276 break;3277 }3278 3279 return -EINVAL;3280}3281 3282static int sof_ipc4_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verify)3283{3284 struct snd_sof_pcm *spcm;3285 int dir, ret;3286 3287 /*3288 * This function is called during system suspend, we need to make sure3289 * that all streams have been freed up.3290 * Freeing might have been skipped when xrun happened just at the start3291 * of the suspend and it sent a SNDRV_PCM_TRIGGER_STOP to the active3292 * stream. This will call sof_pcm_stream_free() with3293 * free_widget_list = false which will leave the kernel and firmware out3294 * of sync during suspend/resume.3295 *3296 * This will also make sure that paused streams handled correctly.3297 */3298 list_for_each_entry(spcm, &sdev->pcm_list, list) {3299 for_each_pcm_streams(dir) {3300 struct snd_pcm_substream *substream = spcm->stream[dir].substream;3301 3302 if (!substream || !substream->runtime || spcm->stream[dir].suspend_ignored)3303 continue;3304 3305 if (spcm->stream[dir].list) {3306 ret = sof_pcm_stream_free(sdev, substream, spcm, dir, true);3307 if (ret < 0)3308 return ret;3309 }3310 }3311 }3312 return 0;3313}3314 3315static int sof_ipc4_link_setup(struct snd_sof_dev *sdev, struct snd_soc_dai_link *link)3316{3317 if (link->no_pcm)3318 return 0;3319 3320 /*3321 * set default trigger order for all links. Exceptions to3322 * the rule will be handled in sof_pcm_dai_link_fixup()3323 * For playback, the sequence is the following: start BE,3324 * start FE, stop FE, stop BE; for Capture the sequence is3325 * inverted start FE, start BE, stop BE, stop FE3326 */3327 link->trigger[SNDRV_PCM_STREAM_PLAYBACK] = SND_SOC_DPCM_TRIGGER_POST;3328 link->trigger[SNDRV_PCM_STREAM_CAPTURE] = SND_SOC_DPCM_TRIGGER_PRE;3329 3330 return 0;3331}3332 3333/* Tokens needed for different copier variants (aif, dai and buffer) */3334static enum sof_tokens copier_token_list[] = {3335 SOF_COMP_TOKENS,3336 SOF_COPIER_TOKENS,3337 SOF_AUDIO_FMT_NUM_TOKENS,3338 SOF_IN_AUDIO_FORMAT_TOKENS,3339 SOF_OUT_AUDIO_FORMAT_TOKENS,3340 SOF_COMP_EXT_TOKENS,3341 3342 SOF_COPIER_DEEP_BUFFER_TOKENS, /* for AIF copier */3343 SOF_DAI_TOKENS, /* for DAI copier */3344};3345 3346static enum sof_tokens pipeline_token_list[] = {3347 SOF_SCHED_TOKENS,3348 SOF_PIPELINE_TOKENS,3349};3350 3351static enum sof_tokens pga_token_list[] = {3352 SOF_COMP_TOKENS,3353 SOF_GAIN_TOKENS,3354 SOF_AUDIO_FMT_NUM_TOKENS,3355 SOF_IN_AUDIO_FORMAT_TOKENS,3356 SOF_OUT_AUDIO_FORMAT_TOKENS,3357 SOF_COMP_EXT_TOKENS,3358};3359 3360static enum sof_tokens mixer_token_list[] = {3361 SOF_COMP_TOKENS,3362 SOF_AUDIO_FMT_NUM_TOKENS,3363 SOF_IN_AUDIO_FORMAT_TOKENS,3364 SOF_OUT_AUDIO_FORMAT_TOKENS,3365 SOF_COMP_EXT_TOKENS,3366};3367 3368static enum sof_tokens src_token_list[] = {3369 SOF_COMP_TOKENS,3370 SOF_SRC_TOKENS,3371 SOF_AUDIO_FMT_NUM_TOKENS,3372 SOF_IN_AUDIO_FORMAT_TOKENS,3373 SOF_OUT_AUDIO_FORMAT_TOKENS,3374 SOF_COMP_EXT_TOKENS,3375};3376 3377static enum sof_tokens process_token_list[] = {3378 SOF_COMP_TOKENS,3379 SOF_AUDIO_FMT_NUM_TOKENS,3380 SOF_IN_AUDIO_FORMAT_TOKENS,3381 SOF_OUT_AUDIO_FORMAT_TOKENS,3382 SOF_COMP_EXT_TOKENS,3383};3384 3385static const struct sof_ipc_tplg_widget_ops tplg_ipc4_widget_ops[SND_SOC_DAPM_TYPE_COUNT] = {3386 [snd_soc_dapm_aif_in] = {sof_ipc4_widget_setup_pcm, sof_ipc4_widget_free_comp_pcm,3387 copier_token_list, ARRAY_SIZE(copier_token_list),3388 NULL, sof_ipc4_prepare_copier_module,3389 sof_ipc4_unprepare_copier_module},3390 [snd_soc_dapm_aif_out] = {sof_ipc4_widget_setup_pcm, sof_ipc4_widget_free_comp_pcm,3391 copier_token_list, ARRAY_SIZE(copier_token_list),3392 NULL, sof_ipc4_prepare_copier_module,3393 sof_ipc4_unprepare_copier_module},3394 [snd_soc_dapm_dai_in] = {sof_ipc4_widget_setup_comp_dai, sof_ipc4_widget_free_comp_dai,3395 copier_token_list, ARRAY_SIZE(copier_token_list), NULL,3396 sof_ipc4_prepare_copier_module,3397 sof_ipc4_unprepare_copier_module},3398 [snd_soc_dapm_dai_out] = {sof_ipc4_widget_setup_comp_dai, sof_ipc4_widget_free_comp_dai,3399 copier_token_list, ARRAY_SIZE(copier_token_list), NULL,3400 sof_ipc4_prepare_copier_module,3401 sof_ipc4_unprepare_copier_module},3402 [snd_soc_dapm_buffer] = {sof_ipc4_widget_setup_pcm, sof_ipc4_widget_free_comp_pcm,3403 copier_token_list, ARRAY_SIZE(copier_token_list),3404 NULL, sof_ipc4_prepare_copier_module,3405 sof_ipc4_unprepare_copier_module},3406 [snd_soc_dapm_scheduler] = {sof_ipc4_widget_setup_comp_pipeline,3407 sof_ipc4_widget_free_comp_pipeline,3408 pipeline_token_list, ARRAY_SIZE(pipeline_token_list), NULL,3409 NULL, NULL},3410 [snd_soc_dapm_pga] = {sof_ipc4_widget_setup_comp_pga, sof_ipc4_widget_free_comp_pga,3411 pga_token_list, ARRAY_SIZE(pga_token_list), NULL,3412 sof_ipc4_prepare_gain_module,3413 NULL},3414 [snd_soc_dapm_mixer] = {sof_ipc4_widget_setup_comp_mixer, sof_ipc4_widget_free_comp_mixer,3415 mixer_token_list, ARRAY_SIZE(mixer_token_list),3416 NULL, sof_ipc4_prepare_mixer_module,3417 NULL},3418 [snd_soc_dapm_src] = {sof_ipc4_widget_setup_comp_src, sof_ipc4_widget_free_comp_src,3419 src_token_list, ARRAY_SIZE(src_token_list),3420 NULL, sof_ipc4_prepare_src_module,3421 NULL},3422 [snd_soc_dapm_effect] = {sof_ipc4_widget_setup_comp_process,3423 sof_ipc4_widget_free_comp_process,3424 process_token_list, ARRAY_SIZE(process_token_list),3425 NULL, sof_ipc4_prepare_process_module,3426 NULL},3427};3428 3429const struct sof_ipc_tplg_ops ipc4_tplg_ops = {3430 .widget = tplg_ipc4_widget_ops,3431 .token_list = ipc4_token_list,3432 .control_setup = sof_ipc4_control_setup,3433 .control = &tplg_ipc4_control_ops,3434 .widget_setup = sof_ipc4_widget_setup,3435 .widget_free = sof_ipc4_widget_free,3436 .route_setup = sof_ipc4_route_setup,3437 .route_free = sof_ipc4_route_free,3438 .dai_config = sof_ipc4_dai_config,3439 .parse_manifest = sof_ipc4_parse_manifest,3440 .dai_get_param = sof_ipc4_dai_get_param,3441 .tear_down_all_pipelines = sof_ipc4_tear_down_all_pipelines,3442 .link_setup = sof_ipc4_link_setup,3443};3444