brintos

brintos / linux-shallow public Read only

0
0
Text · 82.4 KiB · be61e37 Raw
2703 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) 2021 Intel Corporation7//8//9 10#include <uapi/sound/sof/tokens.h>11#include <sound/pcm_params.h>12#include "sof-priv.h"13#include "sof-audio.h"14#include "ipc3-priv.h"15#include "ops.h"16 17/* Full volume for default values */18#define VOL_ZERO_DB	BIT(VOLUME_FWL)19 20/* size of tplg ABI in bytes */21#define SOF_IPC3_TPLG_ABI_SIZE 322 23struct sof_widget_data {24	int ctrl_type;25	int ipc_cmd;26	void *pdata;27	size_t pdata_size;28	struct snd_sof_control *control;29};30 31struct sof_process_types {32	const char *name;33	enum sof_ipc_process_type type;34	enum sof_comp_type comp_type;35};36 37static const struct sof_process_types sof_process[] = {38	{"EQFIR", SOF_PROCESS_EQFIR, SOF_COMP_EQ_FIR},39	{"EQIIR", SOF_PROCESS_EQIIR, SOF_COMP_EQ_IIR},40	{"KEYWORD_DETECT", SOF_PROCESS_KEYWORD_DETECT, SOF_COMP_KEYWORD_DETECT},41	{"KPB", SOF_PROCESS_KPB, SOF_COMP_KPB},42	{"CHAN_SELECTOR", SOF_PROCESS_CHAN_SELECTOR, SOF_COMP_SELECTOR},43	{"MUX", SOF_PROCESS_MUX, SOF_COMP_MUX},44	{"DEMUX", SOF_PROCESS_DEMUX, SOF_COMP_DEMUX},45	{"DCBLOCK", SOF_PROCESS_DCBLOCK, SOF_COMP_DCBLOCK},46	{"SMART_AMP", SOF_PROCESS_SMART_AMP, SOF_COMP_SMART_AMP},47};48 49static enum sof_ipc_process_type find_process(const char *name)50{51	int i;52 53	for (i = 0; i < ARRAY_SIZE(sof_process); i++) {54		if (strcmp(name, sof_process[i].name) == 0)55			return sof_process[i].type;56	}57 58	return SOF_PROCESS_NONE;59}60 61static int get_token_process_type(void *elem, void *object, u32 offset)62{63	u32 *val = (u32 *)((u8 *)object + offset);64 65	*val = find_process((const char *)elem);66	return 0;67}68 69/* Buffers */70static const struct sof_topology_token buffer_tokens[] = {71	{SOF_TKN_BUF_SIZE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,72		offsetof(struct sof_ipc_buffer, size)},73	{SOF_TKN_BUF_CAPS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,74		offsetof(struct sof_ipc_buffer, caps)},75	{SOF_TKN_BUF_FLAGS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,76		offsetof(struct sof_ipc_buffer, flags)},77};78 79/* DAI */80static const struct sof_topology_token dai_tokens[] = {81	{SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type,82		offsetof(struct sof_ipc_comp_dai, type)},83	{SOF_TKN_DAI_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,84		offsetof(struct sof_ipc_comp_dai, dai_index)},85	{SOF_TKN_DAI_DIRECTION, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,86		offsetof(struct sof_ipc_comp_dai, direction)},87};88 89/* BE DAI link */90static const struct sof_topology_token dai_link_tokens[] = {91	{SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type,92		offsetof(struct sof_ipc_dai_config, type)},93	{SOF_TKN_DAI_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,94		offsetof(struct sof_ipc_dai_config, dai_index)},95};96 97/* scheduling */98static const struct sof_topology_token sched_tokens[] = {99	{SOF_TKN_SCHED_PERIOD, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,100		offsetof(struct sof_ipc_pipe_new, period)},101	{SOF_TKN_SCHED_PRIORITY, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,102		offsetof(struct sof_ipc_pipe_new, priority)},103	{SOF_TKN_SCHED_MIPS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,104		offsetof(struct sof_ipc_pipe_new, period_mips)},105	{SOF_TKN_SCHED_CORE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,106		offsetof(struct sof_ipc_pipe_new, core)},107	{SOF_TKN_SCHED_FRAMES, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,108		offsetof(struct sof_ipc_pipe_new, frames_per_sched)},109	{SOF_TKN_SCHED_TIME_DOMAIN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,110		offsetof(struct sof_ipc_pipe_new, time_domain)},111};112 113static const struct sof_topology_token pipeline_tokens[] = {114	{SOF_TKN_SCHED_DYNAMIC_PIPELINE, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16,115		offsetof(struct snd_sof_widget, dynamic_pipeline_widget)},116 117};118 119/* volume */120static const struct sof_topology_token volume_tokens[] = {121	{SOF_TKN_VOLUME_RAMP_STEP_TYPE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,122		offsetof(struct sof_ipc_comp_volume, ramp)},123	{SOF_TKN_VOLUME_RAMP_STEP_MS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,124		offsetof(struct sof_ipc_comp_volume, initial_ramp)},125};126 127/* SRC */128static const struct sof_topology_token src_tokens[] = {129	{SOF_TKN_SRC_RATE_IN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,130		offsetof(struct sof_ipc_comp_src, source_rate)},131	{SOF_TKN_SRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,132		offsetof(struct sof_ipc_comp_src, sink_rate)},133};134 135/* ASRC */136static const struct sof_topology_token asrc_tokens[] = {137	{SOF_TKN_ASRC_RATE_IN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,138		offsetof(struct sof_ipc_comp_asrc, source_rate)},139	{SOF_TKN_ASRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,140		offsetof(struct sof_ipc_comp_asrc, sink_rate)},141	{SOF_TKN_ASRC_ASYNCHRONOUS_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,142		offsetof(struct sof_ipc_comp_asrc, asynchronous_mode)},143	{SOF_TKN_ASRC_OPERATION_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,144		offsetof(struct sof_ipc_comp_asrc, operation_mode)},145};146 147/* EFFECT */148static const struct sof_topology_token process_tokens[] = {149	{SOF_TKN_PROCESS_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_process_type,150		offsetof(struct sof_ipc_comp_process, type)},151};152 153/* PCM */154static const struct sof_topology_token pcm_tokens[] = {155	{SOF_TKN_PCM_DMAC_CONFIG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,156		offsetof(struct sof_ipc_comp_host, dmac_config)},157};158 159/* Generic components */160static const struct sof_topology_token comp_tokens[] = {161	{SOF_TKN_COMP_PERIOD_SINK_COUNT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,162		offsetof(struct sof_ipc_comp_config, periods_sink)},163	{SOF_TKN_COMP_PERIOD_SOURCE_COUNT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,164		offsetof(struct sof_ipc_comp_config, periods_source)},165	{SOF_TKN_COMP_FORMAT,166		SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_format,167		offsetof(struct sof_ipc_comp_config, frame_fmt)},168};169 170/* SSP */171static const struct sof_topology_token ssp_tokens[] = {172	{SOF_TKN_INTEL_SSP_CLKS_CONTROL, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,173		offsetof(struct sof_ipc_dai_ssp_params, clks_control)},174	{SOF_TKN_INTEL_SSP_MCLK_ID, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,175		offsetof(struct sof_ipc_dai_ssp_params, mclk_id)},176	{SOF_TKN_INTEL_SSP_SAMPLE_BITS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,177		offsetof(struct sof_ipc_dai_ssp_params, sample_valid_bits)},178	{SOF_TKN_INTEL_SSP_FRAME_PULSE_WIDTH, SND_SOC_TPLG_TUPLE_TYPE_SHORT,	get_token_u16,179		offsetof(struct sof_ipc_dai_ssp_params, frame_pulse_width)},180	{SOF_TKN_INTEL_SSP_QUIRKS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,181		offsetof(struct sof_ipc_dai_ssp_params, quirks)},182	{SOF_TKN_INTEL_SSP_TDM_PADDING_PER_SLOT, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16,183		offsetof(struct sof_ipc_dai_ssp_params, tdm_per_slot_padding_flag)},184	{SOF_TKN_INTEL_SSP_BCLK_DELAY, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,185		offsetof(struct sof_ipc_dai_ssp_params, bclk_delay)},186};187 188/* ALH */189static const struct sof_topology_token alh_tokens[] = {190	{SOF_TKN_INTEL_ALH_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,191		offsetof(struct sof_ipc_dai_alh_params, rate)},192	{SOF_TKN_INTEL_ALH_CH,	SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,193		offsetof(struct sof_ipc_dai_alh_params, channels)},194};195 196/* DMIC */197static const struct sof_topology_token dmic_tokens[] = {198	{SOF_TKN_INTEL_DMIC_DRIVER_VERSION, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,199		offsetof(struct sof_ipc_dai_dmic_params, driver_ipc_version)},200	{SOF_TKN_INTEL_DMIC_CLK_MIN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,201		offsetof(struct sof_ipc_dai_dmic_params, pdmclk_min)},202	{SOF_TKN_INTEL_DMIC_CLK_MAX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,203		offsetof(struct sof_ipc_dai_dmic_params, pdmclk_max)},204	{SOF_TKN_INTEL_DMIC_SAMPLE_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,205		offsetof(struct sof_ipc_dai_dmic_params, fifo_fs)},206	{SOF_TKN_INTEL_DMIC_DUTY_MIN, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,207		offsetof(struct sof_ipc_dai_dmic_params, duty_min)},208	{SOF_TKN_INTEL_DMIC_DUTY_MAX, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,209		offsetof(struct sof_ipc_dai_dmic_params, duty_max)},210	{SOF_TKN_INTEL_DMIC_NUM_PDM_ACTIVE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,211		offsetof(struct sof_ipc_dai_dmic_params, num_pdm_active)},212	{SOF_TKN_INTEL_DMIC_FIFO_WORD_LENGTH, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,213		offsetof(struct sof_ipc_dai_dmic_params, fifo_bits)},214	{SOF_TKN_INTEL_DMIC_UNMUTE_RAMP_TIME_MS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,215		offsetof(struct sof_ipc_dai_dmic_params, unmute_ramp_time)},216};217 218/* ESAI */219static const struct sof_topology_token esai_tokens[] = {220	{SOF_TKN_IMX_ESAI_MCLK_ID, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,221		offsetof(struct sof_ipc_dai_esai_params, mclk_id)},222};223 224/* SAI */225static const struct sof_topology_token sai_tokens[] = {226	{SOF_TKN_IMX_SAI_MCLK_ID, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,227		offsetof(struct sof_ipc_dai_sai_params, mclk_id)},228};229 230/*231 * DMIC PDM Tokens232 * SOF_TKN_INTEL_DMIC_PDM_CTRL_ID should be the first token233 * as it increments the index while parsing the array of pdm tokens234 * and determines the correct offset235 */236static const struct sof_topology_token dmic_pdm_tokens[] = {237	{SOF_TKN_INTEL_DMIC_PDM_CTRL_ID, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,238		offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, id)},239	{SOF_TKN_INTEL_DMIC_PDM_MIC_A_Enable, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,240		offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, enable_mic_a)},241	{SOF_TKN_INTEL_DMIC_PDM_MIC_B_Enable, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,242		offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, enable_mic_b)},243	{SOF_TKN_INTEL_DMIC_PDM_POLARITY_A, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,244		offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, polarity_mic_a)},245	{SOF_TKN_INTEL_DMIC_PDM_POLARITY_B, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,246		offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, polarity_mic_b)},247	{SOF_TKN_INTEL_DMIC_PDM_CLK_EDGE, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,248		offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, clk_edge)},249	{SOF_TKN_INTEL_DMIC_PDM_SKEW, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16,250		offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, skew)},251};252 253/* HDA */254static const struct sof_topology_token hda_tokens[] = {255	{SOF_TKN_INTEL_HDA_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,256		offsetof(struct sof_ipc_dai_hda_params, rate)},257	{SOF_TKN_INTEL_HDA_CH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,258		offsetof(struct sof_ipc_dai_hda_params, channels)},259};260 261/* AFE */262static const struct sof_topology_token afe_tokens[] = {263	{SOF_TKN_MEDIATEK_AFE_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,264		offsetof(struct sof_ipc_dai_mtk_afe_params, rate)},265	{SOF_TKN_MEDIATEK_AFE_CH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,266		offsetof(struct sof_ipc_dai_mtk_afe_params, channels)},267	{SOF_TKN_MEDIATEK_AFE_FORMAT, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_format,268		offsetof(struct sof_ipc_dai_mtk_afe_params, format)},269};270 271/* ACPDMIC */272static const struct sof_topology_token acpdmic_tokens[] = {273	{SOF_TKN_AMD_ACPDMIC_RATE,274		SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,275		offsetof(struct sof_ipc_dai_acpdmic_params, pdm_rate)},276	{SOF_TKN_AMD_ACPDMIC_CH,277		SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,278		offsetof(struct sof_ipc_dai_acpdmic_params, pdm_ch)},279};280 281/* ACPI2S */282static const struct sof_topology_token acpi2s_tokens[] = {283	{SOF_TKN_AMD_ACPI2S_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,284		offsetof(struct sof_ipc_dai_acp_params, fsync_rate)},285	{SOF_TKN_AMD_ACPI2S_CH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,286		offsetof(struct sof_ipc_dai_acp_params, tdm_slots)},287	{SOF_TKN_AMD_ACPI2S_TDM_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,288		offsetof(struct sof_ipc_dai_acp_params, tdm_mode)},289};290 291/* MICFIL PDM */292static const struct sof_topology_token micfil_pdm_tokens[] = {293	{SOF_TKN_IMX_MICFIL_RATE,294		SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,295		offsetof(struct sof_ipc_dai_micfil_params, pdm_rate)},296	{SOF_TKN_IMX_MICFIL_CH,297		SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,298		offsetof(struct sof_ipc_dai_micfil_params, pdm_ch)},299};300 301/* ACP_SDW */302static const struct sof_topology_token acp_sdw_tokens[] = {303	{SOF_TKN_AMD_ACP_SDW_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,304		offsetof(struct sof_ipc_dai_acp_sdw_params, rate)},305	{SOF_TKN_AMD_ACP_SDW_CH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,306		offsetof(struct sof_ipc_dai_acp_sdw_params, channels)},307};308 309/* Core tokens */310static const struct sof_topology_token core_tokens[] = {311	{SOF_TKN_COMP_CORE_ID, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,312		offsetof(struct sof_ipc_comp, core)},313};314 315/* Component extended tokens */316static const struct sof_topology_token comp_ext_tokens[] = {317	{SOF_TKN_COMP_UUID, SND_SOC_TPLG_TUPLE_TYPE_UUID, get_token_uuid,318		offsetof(struct snd_sof_widget, uuid)},319};320 321static const struct sof_token_info ipc3_token_list[SOF_TOKEN_COUNT] = {322	[SOF_PCM_TOKENS] = {"PCM tokens", pcm_tokens, ARRAY_SIZE(pcm_tokens)},323	[SOF_PIPELINE_TOKENS] = {"Pipeline tokens", pipeline_tokens, ARRAY_SIZE(pipeline_tokens)},324	[SOF_SCHED_TOKENS] = {"Scheduler tokens", sched_tokens, ARRAY_SIZE(sched_tokens)},325	[SOF_COMP_TOKENS] = {"Comp tokens", comp_tokens, ARRAY_SIZE(comp_tokens)},326	[SOF_CORE_TOKENS] = {"Core tokens", core_tokens, ARRAY_SIZE(core_tokens)},327	[SOF_COMP_EXT_TOKENS] = {"AFE tokens", comp_ext_tokens, ARRAY_SIZE(comp_ext_tokens)},328	[SOF_BUFFER_TOKENS] = {"Buffer tokens", buffer_tokens, ARRAY_SIZE(buffer_tokens)},329	[SOF_VOLUME_TOKENS] = {"Volume tokens", volume_tokens, ARRAY_SIZE(volume_tokens)},330	[SOF_SRC_TOKENS] = {"SRC tokens", src_tokens, ARRAY_SIZE(src_tokens)},331	[SOF_ASRC_TOKENS] = {"ASRC tokens", asrc_tokens, ARRAY_SIZE(asrc_tokens)},332	[SOF_PROCESS_TOKENS] = {"Process tokens", process_tokens, ARRAY_SIZE(process_tokens)},333	[SOF_DAI_TOKENS] = {"DAI tokens", dai_tokens, ARRAY_SIZE(dai_tokens)},334	[SOF_DAI_LINK_TOKENS] = {"DAI link tokens", dai_link_tokens, ARRAY_SIZE(dai_link_tokens)},335	[SOF_HDA_TOKENS] = {"HDA tokens", hda_tokens, ARRAY_SIZE(hda_tokens)},336	[SOF_SSP_TOKENS] = {"SSP tokens", ssp_tokens, ARRAY_SIZE(ssp_tokens)},337	[SOF_ALH_TOKENS] = {"ALH tokens", alh_tokens, ARRAY_SIZE(alh_tokens)},338	[SOF_DMIC_TOKENS] = {"DMIC tokens", dmic_tokens, ARRAY_SIZE(dmic_tokens)},339	[SOF_DMIC_PDM_TOKENS] = {"DMIC PDM tokens", dmic_pdm_tokens, ARRAY_SIZE(dmic_pdm_tokens)},340	[SOF_ESAI_TOKENS] = {"ESAI tokens", esai_tokens, ARRAY_SIZE(esai_tokens)},341	[SOF_SAI_TOKENS] = {"SAI tokens", sai_tokens, ARRAY_SIZE(sai_tokens)},342	[SOF_AFE_TOKENS] = {"AFE tokens", afe_tokens, ARRAY_SIZE(afe_tokens)},343	[SOF_ACPDMIC_TOKENS] = {"ACPDMIC tokens", acpdmic_tokens, ARRAY_SIZE(acpdmic_tokens)},344	[SOF_ACPI2S_TOKENS]   = {"ACPI2S tokens", acpi2s_tokens, ARRAY_SIZE(acpi2s_tokens)},345	[SOF_MICFIL_TOKENS] = {"MICFIL PDM tokens",346		micfil_pdm_tokens, ARRAY_SIZE(micfil_pdm_tokens)},347	[SOF_ACP_SDW_TOKENS]   = {"ACP_SDW tokens", acp_sdw_tokens, ARRAY_SIZE(acp_sdw_tokens)},348};349 350/**351 * sof_comp_alloc - allocate and initialize buffer for a new component352 * @swidget: pointer to struct snd_sof_widget containing extended data353 * @ipc_size: IPC payload size that will be updated depending on valid354 *  extended data.355 * @index: ID of the pipeline the component belongs to356 *357 * Return: The pointer to the new allocated component, NULL if failed.358 */359static void *sof_comp_alloc(struct snd_sof_widget *swidget, size_t *ipc_size,360			    int index)361{362	struct sof_ipc_comp *comp;363	size_t total_size = *ipc_size;364	size_t ext_size = sizeof(swidget->uuid);365 366	/* only non-zero UUID is valid */367	if (!guid_is_null(&swidget->uuid))368		total_size += ext_size;369 370	comp = kzalloc(total_size, GFP_KERNEL);371	if (!comp)372		return NULL;373 374	/* configure comp new IPC message */375	comp->hdr.size = total_size;376	comp->hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW;377	comp->id = swidget->comp_id;378	comp->pipeline_id = index;379	comp->core = swidget->core;380 381	/* handle the extended data if needed */382	if (total_size > *ipc_size) {383		/* append extended data to the end of the component */384		memcpy((u8 *)comp + *ipc_size, &swidget->uuid, ext_size);385		comp->ext_data_length = ext_size;386	}387 388	/* update ipc_size and return */389	*ipc_size = total_size;390	return comp;391}392 393static void sof_dbg_comp_config(struct snd_soc_component *scomp, struct sof_ipc_comp_config *config)394{395	dev_dbg(scomp->dev, " config: periods snk %d src %d fmt %d\n",396		config->periods_sink, config->periods_source,397		config->frame_fmt);398}399 400static int sof_ipc3_widget_setup_comp_host(struct snd_sof_widget *swidget)401{402	struct snd_soc_component *scomp = swidget->scomp;403	struct sof_ipc_comp_host *host;404	size_t ipc_size = sizeof(*host);405	int ret;406 407	host = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id);408	if (!host)409		return -ENOMEM;410	swidget->private = host;411 412	/* configure host comp IPC message */413	host->comp.type = SOF_COMP_HOST;414	host->config.hdr.size = sizeof(host->config);415 416	if (swidget->id == snd_soc_dapm_aif_out)417		host->direction = SOF_IPC_STREAM_CAPTURE;418	else419		host->direction = SOF_IPC_STREAM_PLAYBACK;420 421	/* parse one set of pcm_tokens */422	ret = sof_update_ipc_object(scomp, host, SOF_PCM_TOKENS, swidget->tuples,423				    swidget->num_tuples, sizeof(*host), 1);424	if (ret < 0)425		goto err;426 427	/* parse one set of comp_tokens */428	ret = sof_update_ipc_object(scomp, &host->config, SOF_COMP_TOKENS, swidget->tuples,429				    swidget->num_tuples, sizeof(host->config), 1);430	if (ret < 0)431		goto err;432 433	dev_dbg(scomp->dev, "loaded host %s\n", swidget->widget->name);434	sof_dbg_comp_config(scomp, &host->config);435 436	return 0;437err:438	kfree(swidget->private);439	swidget->private = NULL;440 441	return ret;442}443 444static void sof_ipc3_widget_free_comp(struct snd_sof_widget *swidget)445{446	kfree(swidget->private);447}448 449static int sof_ipc3_widget_setup_comp_tone(struct snd_sof_widget *swidget)450{451	struct snd_soc_component *scomp = swidget->scomp;452	struct sof_ipc_comp_tone *tone;453	size_t ipc_size = sizeof(*tone);454	int ret;455 456	tone = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id);457	if (!tone)458		return -ENOMEM;459 460	swidget->private = tone;461 462	/* configure siggen IPC message */463	tone->comp.type = SOF_COMP_TONE;464	tone->config.hdr.size = sizeof(tone->config);465 466	/* parse one set of comp tokens */467	ret = sof_update_ipc_object(scomp, &tone->config, SOF_COMP_TOKENS, swidget->tuples,468				    swidget->num_tuples, sizeof(tone->config), 1);469	if (ret < 0) {470		kfree(swidget->private);471		swidget->private = NULL;472		return ret;473	}474 475	dev_dbg(scomp->dev, "tone %s: frequency %d amplitude %d\n",476		swidget->widget->name, tone->frequency, tone->amplitude);477	sof_dbg_comp_config(scomp, &tone->config);478 479	return 0;480}481 482static int sof_ipc3_widget_setup_comp_mixer(struct snd_sof_widget *swidget)483{484	struct snd_soc_component *scomp = swidget->scomp;485	struct sof_ipc_comp_mixer *mixer;486	size_t ipc_size = sizeof(*mixer);487	int ret;488 489	mixer = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id);490	if (!mixer)491		return -ENOMEM;492 493	swidget->private = mixer;494 495	/* configure mixer IPC message */496	mixer->comp.type = SOF_COMP_MIXER;497	mixer->config.hdr.size = sizeof(mixer->config);498 499	/* parse one set of comp tokens */500	ret = sof_update_ipc_object(scomp, &mixer->config, SOF_COMP_TOKENS,501				    swidget->tuples, swidget->num_tuples,502				    sizeof(mixer->config), 1);503	if (ret < 0) {504		kfree(swidget->private);505		swidget->private = NULL;506 507		return ret;508	}509 510	dev_dbg(scomp->dev, "loaded mixer %s\n", swidget->widget->name);511	sof_dbg_comp_config(scomp, &mixer->config);512 513	return 0;514}515 516static int sof_ipc3_widget_setup_comp_pipeline(struct snd_sof_widget *swidget)517{518	struct snd_soc_component *scomp = swidget->scomp;519	struct snd_sof_pipeline *spipe = swidget->spipe;520	struct sof_ipc_pipe_new *pipeline;521	struct snd_sof_widget *comp_swidget;522	int ret;523 524	pipeline = kzalloc(sizeof(*pipeline), GFP_KERNEL);525	if (!pipeline)526		return -ENOMEM;527 528	/* configure pipeline IPC message */529	pipeline->hdr.size = sizeof(*pipeline);530	pipeline->hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_PIPE_NEW;531	pipeline->pipeline_id = swidget->pipeline_id;532	pipeline->comp_id = swidget->comp_id;533 534	swidget->private = pipeline;535 536	/* component at start of pipeline is our stream id */537	comp_swidget = snd_sof_find_swidget(scomp, swidget->widget->sname);538	if (!comp_swidget) {539		dev_err(scomp->dev, "scheduler %s refers to non existent widget %s\n",540			swidget->widget->name, swidget->widget->sname);541		ret = -EINVAL;542		goto err;543	}544 545	pipeline->sched_id = comp_swidget->comp_id;546 547	/* parse one set of scheduler tokens */548	ret = sof_update_ipc_object(scomp, pipeline, SOF_SCHED_TOKENS, swidget->tuples,549				    swidget->num_tuples, sizeof(*pipeline), 1);550	if (ret < 0)551		goto err;552 553	/* parse one set of pipeline tokens */554	ret = sof_update_ipc_object(scomp, swidget, SOF_PIPELINE_TOKENS, swidget->tuples,555				    swidget->num_tuples, sizeof(*swidget), 1);556	if (ret < 0)557		goto err;558 559	if (sof_debug_check_flag(SOF_DBG_DISABLE_MULTICORE))560		pipeline->core = SOF_DSP_PRIMARY_CORE;561 562	if (sof_debug_check_flag(SOF_DBG_DYNAMIC_PIPELINES_OVERRIDE))563		swidget->dynamic_pipeline_widget =564			sof_debug_check_flag(SOF_DBG_DYNAMIC_PIPELINES_ENABLE);565 566	dev_dbg(scomp->dev, "pipeline %s: period %d pri %d mips %d core %d frames %d dynamic %d\n",567		swidget->widget->name, pipeline->period, pipeline->priority,568		pipeline->period_mips, pipeline->core, pipeline->frames_per_sched,569		swidget->dynamic_pipeline_widget);570 571	swidget->core = pipeline->core;572	spipe->core_mask |= BIT(pipeline->core);573 574	return 0;575 576err:577	kfree(swidget->private);578	swidget->private = NULL;579 580	return ret;581}582 583static int sof_ipc3_widget_setup_comp_buffer(struct snd_sof_widget *swidget)584{585	struct snd_soc_component *scomp = swidget->scomp;586	struct sof_ipc_buffer *buffer;587	int ret;588 589	buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);590	if (!buffer)591		return -ENOMEM;592 593	swidget->private = buffer;594 595	/* configure dai IPC message */596	buffer->comp.hdr.size = sizeof(*buffer);597	buffer->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_BUFFER_NEW;598	buffer->comp.id = swidget->comp_id;599	buffer->comp.type = SOF_COMP_BUFFER;600	buffer->comp.pipeline_id = swidget->pipeline_id;601	buffer->comp.core = swidget->core;602 603	/* parse one set of buffer tokens */604	ret = sof_update_ipc_object(scomp, buffer, SOF_BUFFER_TOKENS, swidget->tuples,605				    swidget->num_tuples, sizeof(*buffer), 1);606	if (ret < 0) {607		kfree(swidget->private);608		swidget->private = NULL;609		return ret;610	}611 612	dev_dbg(scomp->dev, "buffer %s: size %d caps 0x%x\n",613		swidget->widget->name, buffer->size, buffer->caps);614 615	return 0;616}617 618static int sof_ipc3_widget_setup_comp_src(struct snd_sof_widget *swidget)619{620	struct snd_soc_component *scomp = swidget->scomp;621	struct sof_ipc_comp_src *src;622	size_t ipc_size = sizeof(*src);623	int ret;624 625	src = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id);626	if (!src)627		return -ENOMEM;628 629	swidget->private = src;630 631	/* configure src IPC message */632	src->comp.type = SOF_COMP_SRC;633	src->config.hdr.size = sizeof(src->config);634 635	/* parse one set of src tokens */636	ret = sof_update_ipc_object(scomp, src, SOF_SRC_TOKENS, swidget->tuples,637				    swidget->num_tuples, sizeof(*src), 1);638	if (ret < 0)639		goto err;640 641	/* parse one set of comp tokens */642	ret = sof_update_ipc_object(scomp, &src->config, SOF_COMP_TOKENS,643				    swidget->tuples, swidget->num_tuples, sizeof(src->config), 1);644	if (ret < 0)645		goto err;646 647	dev_dbg(scomp->dev, "src %s: source rate %d sink rate %d\n",648		swidget->widget->name, src->source_rate, src->sink_rate);649	sof_dbg_comp_config(scomp, &src->config);650 651	return 0;652err:653	kfree(swidget->private);654	swidget->private = NULL;655 656	return ret;657}658 659static int sof_ipc3_widget_setup_comp_asrc(struct snd_sof_widget *swidget)660{661	struct snd_soc_component *scomp = swidget->scomp;662	struct sof_ipc_comp_asrc *asrc;663	size_t ipc_size = sizeof(*asrc);664	int ret;665 666	asrc = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id);667	if (!asrc)668		return -ENOMEM;669 670	swidget->private = asrc;671 672	/* configure ASRC IPC message */673	asrc->comp.type = SOF_COMP_ASRC;674	asrc->config.hdr.size = sizeof(asrc->config);675 676	/* parse one set of asrc tokens */677	ret = sof_update_ipc_object(scomp, asrc, SOF_ASRC_TOKENS, swidget->tuples,678				    swidget->num_tuples, sizeof(*asrc), 1);679	if (ret < 0)680		goto err;681 682	/* parse one set of comp tokens */683	ret = sof_update_ipc_object(scomp, &asrc->config, SOF_COMP_TOKENS,684				    swidget->tuples, swidget->num_tuples, sizeof(asrc->config), 1);685	if (ret < 0)686		goto err;687 688	dev_dbg(scomp->dev, "asrc %s: source rate %d sink rate %d asynch %d operation %d\n",689		swidget->widget->name, asrc->source_rate, asrc->sink_rate,690		asrc->asynchronous_mode, asrc->operation_mode);691 692	sof_dbg_comp_config(scomp, &asrc->config);693 694	return 0;695err:696	kfree(swidget->private);697	swidget->private = NULL;698 699	return ret;700}701 702/*703 * Mux topology704 */705static int sof_ipc3_widget_setup_comp_mux(struct snd_sof_widget *swidget)706{707	struct snd_soc_component *scomp = swidget->scomp;708	struct sof_ipc_comp_mux *mux;709	size_t ipc_size = sizeof(*mux);710	int ret;711 712	mux = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id);713	if (!mux)714		return -ENOMEM;715 716	swidget->private = mux;717 718	/* configure mux IPC message */719	mux->comp.type = SOF_COMP_MUX;720	mux->config.hdr.size = sizeof(mux->config);721 722	/* parse one set of comp tokens */723	ret = sof_update_ipc_object(scomp, &mux->config, SOF_COMP_TOKENS,724				    swidget->tuples, swidget->num_tuples, sizeof(mux->config), 1);725	if (ret < 0) {726		kfree(swidget->private);727		swidget->private = NULL;728		return ret;729	}730 731	dev_dbg(scomp->dev, "loaded mux %s\n", swidget->widget->name);732	sof_dbg_comp_config(scomp, &mux->config);733 734	return 0;735}736 737/*738 * PGA Topology739 */740 741static int sof_ipc3_widget_setup_comp_pga(struct snd_sof_widget *swidget)742{743	struct snd_soc_component *scomp = swidget->scomp;744	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);745	struct sof_ipc_comp_volume *volume;746	struct snd_sof_control *scontrol;747	size_t ipc_size = sizeof(*volume);748	int min_step, max_step;749	int ret;750 751	volume = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id);752	if (!volume)753		return -ENOMEM;754 755	swidget->private = volume;756 757	/* configure volume IPC message */758	volume->comp.type = SOF_COMP_VOLUME;759	volume->config.hdr.size = sizeof(volume->config);760 761	/* parse one set of volume tokens */762	ret = sof_update_ipc_object(scomp, volume, SOF_VOLUME_TOKENS, swidget->tuples,763				    swidget->num_tuples, sizeof(*volume), 1);764	if (ret < 0)765		goto err;766 767	/* parse one set of comp tokens */768	ret = sof_update_ipc_object(scomp, &volume->config, SOF_COMP_TOKENS,769				    swidget->tuples, swidget->num_tuples,770				    sizeof(volume->config), 1);771	if (ret < 0)772		goto err;773 774	dev_dbg(scomp->dev, "loaded PGA %s\n", swidget->widget->name);775	sof_dbg_comp_config(scomp, &volume->config);776 777	list_for_each_entry(scontrol, &sdev->kcontrol_list, list) {778		if (scontrol->comp_id == swidget->comp_id &&779		    scontrol->volume_table) {780			min_step = scontrol->min_volume_step;781			max_step = scontrol->max_volume_step;782			volume->min_value = scontrol->volume_table[min_step];783			volume->max_value = scontrol->volume_table[max_step];784			volume->channels = scontrol->num_channels;785			break;786		}787	}788 789	return 0;790err:791	kfree(swidget->private);792	swidget->private = NULL;793 794	return ret;795}796 797static int sof_get_control_data(struct snd_soc_component *scomp,798				struct snd_soc_dapm_widget *widget,799				struct sof_widget_data *wdata, size_t *size)800{801	const struct snd_kcontrol_new *kc;802	struct sof_ipc_ctrl_data *cdata;803	struct soc_mixer_control *sm;804	struct soc_bytes_ext *sbe;805	struct soc_enum *se;806	int i;807 808	*size = 0;809 810	for (i = 0; i < widget->num_kcontrols; i++) {811		kc = &widget->kcontrol_news[i];812 813		switch (widget->dobj.widget.kcontrol_type[i]) {814		case SND_SOC_TPLG_TYPE_MIXER:815			sm = (struct soc_mixer_control *)kc->private_value;816			wdata[i].control = sm->dobj.private;817			break;818		case SND_SOC_TPLG_TYPE_BYTES:819			sbe = (struct soc_bytes_ext *)kc->private_value;820			wdata[i].control = sbe->dobj.private;821			break;822		case SND_SOC_TPLG_TYPE_ENUM:823			se = (struct soc_enum *)kc->private_value;824			wdata[i].control = se->dobj.private;825			break;826		default:827			dev_err(scomp->dev, "Unknown kcontrol type %u in widget %s\n",828				widget->dobj.widget.kcontrol_type[i], widget->name);829			return -EINVAL;830		}831 832		if (!wdata[i].control) {833			dev_err(scomp->dev, "No scontrol for widget %s\n", widget->name);834			return -EINVAL;835		}836 837		cdata = wdata[i].control->ipc_control_data;838 839		if (widget->dobj.widget.kcontrol_type[i] == SND_SOC_TPLG_TYPE_BYTES) {840			/* make sure data is valid - data can be updated at runtime */841			if (cdata->data->magic != SOF_ABI_MAGIC)842				return -EINVAL;843 844			wdata[i].pdata = cdata->data->data;845			wdata[i].pdata_size = cdata->data->size;846		} else {847			/* points to the control data union */848			wdata[i].pdata = cdata->chanv;849			/*850			 * wdata[i].control->size is calculated with struct_size851			 * and includes the size of struct sof_ipc_ctrl_data852			 */853			wdata[i].pdata_size = wdata[i].control->size -854					      sizeof(struct sof_ipc_ctrl_data);855		}856 857		*size += wdata[i].pdata_size;858 859		/* get data type */860		switch (cdata->cmd) {861		case SOF_CTRL_CMD_VOLUME:862		case SOF_CTRL_CMD_ENUM:863		case SOF_CTRL_CMD_SWITCH:864			wdata[i].ipc_cmd = SOF_IPC_COMP_SET_VALUE;865			wdata[i].ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_SET;866			break;867		case SOF_CTRL_CMD_BINARY:868			wdata[i].ipc_cmd = SOF_IPC_COMP_SET_DATA;869			wdata[i].ctrl_type = SOF_CTRL_TYPE_DATA_SET;870			break;871		default:872			break;873		}874	}875 876	return 0;877}878 879static int sof_process_load(struct snd_soc_component *scomp,880			    struct snd_sof_widget *swidget, int type)881{882	struct snd_soc_dapm_widget *widget = swidget->widget;883	struct sof_ipc_comp_process *process;884	struct sof_widget_data *wdata = NULL;885	size_t ipc_data_size = 0;886	size_t ipc_size;887	int offset = 0;888	int ret;889	int i;890 891	/* allocate struct for widget control data sizes and types */892	if (widget->num_kcontrols) {893		wdata = kcalloc(widget->num_kcontrols, sizeof(*wdata), GFP_KERNEL);894		if (!wdata)895			return -ENOMEM;896 897		/* get possible component controls and get size of all pdata */898		ret = sof_get_control_data(scomp, widget, wdata, &ipc_data_size);899		if (ret < 0)900			goto out;901	}902 903	ipc_size = sizeof(struct sof_ipc_comp_process) + ipc_data_size;904 905	/* we are exceeding max ipc size, config needs to be sent separately */906	if (ipc_size > SOF_IPC_MSG_MAX_SIZE) {907		ipc_size -= ipc_data_size;908		ipc_data_size = 0;909	}910 911	process = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id);912	if (!process) {913		ret = -ENOMEM;914		goto out;915	}916 917	swidget->private = process;918 919	/* configure iir IPC message */920	process->comp.type = type;921	process->config.hdr.size = sizeof(process->config);922 923	/* parse one set of comp tokens */924	ret = sof_update_ipc_object(scomp, &process->config, SOF_COMP_TOKENS,925				    swidget->tuples, swidget->num_tuples,926				    sizeof(process->config), 1);927	if (ret < 0)928		goto err;929 930	dev_dbg(scomp->dev, "loaded process %s\n", swidget->widget->name);931	sof_dbg_comp_config(scomp, &process->config);932 933	/*934	 * found private data in control, so copy it.935	 * get possible component controls - get size of all pdata,936	 * then memcpy with headers937	 */938	if (ipc_data_size) {939		for (i = 0; i < widget->num_kcontrols; i++) {940			if (!wdata[i].pdata_size)941				continue;942 943			memcpy(&process->data[offset], wdata[i].pdata,944			       wdata[i].pdata_size);945			offset += wdata[i].pdata_size;946		}947	}948 949	process->size = ipc_data_size;950 951	kfree(wdata);952 953	return 0;954err:955	kfree(swidget->private);956	swidget->private = NULL;957out:958	kfree(wdata);959	return ret;960}961 962static enum sof_comp_type find_process_comp_type(enum sof_ipc_process_type type)963{964	int i;965 966	for (i = 0; i < ARRAY_SIZE(sof_process); i++) {967		if (sof_process[i].type == type)968			return sof_process[i].comp_type;969	}970 971	return SOF_COMP_NONE;972}973 974/*975 * Processing Component Topology - can be "effect", "codec", or general976 * "processing".977 */978 979static int sof_widget_update_ipc_comp_process(struct snd_sof_widget *swidget)980{981	struct snd_soc_component *scomp = swidget->scomp;982	struct sof_ipc_comp_process config;983	int ret;984 985	memset(&config, 0, sizeof(config));986	config.comp.core = swidget->core;987 988	/* parse one set of process tokens */989	ret = sof_update_ipc_object(scomp, &config, SOF_PROCESS_TOKENS, swidget->tuples,990				    swidget->num_tuples, sizeof(config), 1);991	if (ret < 0)992		return ret;993 994	/* now load process specific data and send IPC */995	return sof_process_load(scomp, swidget, find_process_comp_type(config.type));996}997 998static int sof_link_hda_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink,999			     struct sof_ipc_dai_config *config, struct snd_sof_dai *dai)1000{1001	struct sof_dai_private_data *private = dai->private;1002	u32 size = sizeof(*config);1003	int ret;1004 1005	/* init IPC */1006	memset(&config->hda, 0, sizeof(config->hda));1007	config->hdr.size = size;1008 1009	/* parse one set of HDA tokens */1010	ret = sof_update_ipc_object(scomp, &config->hda, SOF_HDA_TOKENS, slink->tuples,1011				    slink->num_tuples, size, 1);1012	if (ret < 0)1013		return ret;1014 1015	dev_dbg(scomp->dev, "HDA config rate %d channels %d\n",1016		config->hda.rate, config->hda.channels);1017 1018	config->hda.link_dma_ch = DMA_CHAN_INVALID;1019 1020	dai->number_configs = 1;1021	dai->current_config = 0;1022	private->dai_config = kmemdup(config, size, GFP_KERNEL);1023	if (!private->dai_config)1024		return -ENOMEM;1025 1026	return 0;1027}1028 1029static void sof_dai_set_format(struct snd_soc_tplg_hw_config *hw_config,1030			       struct sof_ipc_dai_config *config)1031{1032	/* clock directions wrt codec */1033	config->format &= ~SOF_DAI_FMT_CLOCK_PROVIDER_MASK;1034	if (hw_config->bclk_provider == SND_SOC_TPLG_BCLK_CP) {1035		/* codec is bclk provider */1036		if (hw_config->fsync_provider == SND_SOC_TPLG_FSYNC_CP)1037			config->format |= SOF_DAI_FMT_CBP_CFP;1038		else1039			config->format |= SOF_DAI_FMT_CBP_CFC;1040	} else {1041		/* codec is bclk consumer */1042		if (hw_config->fsync_provider == SND_SOC_TPLG_FSYNC_CP)1043			config->format |= SOF_DAI_FMT_CBC_CFP;1044		else1045			config->format |= SOF_DAI_FMT_CBC_CFC;1046	}1047 1048	/* inverted clocks ? */1049	config->format &= ~SOF_DAI_FMT_INV_MASK;1050	if (hw_config->invert_bclk) {1051		if (hw_config->invert_fsync)1052			config->format |= SOF_DAI_FMT_IB_IF;1053		else1054			config->format |= SOF_DAI_FMT_IB_NF;1055	} else {1056		if (hw_config->invert_fsync)1057			config->format |= SOF_DAI_FMT_NB_IF;1058		else1059			config->format |= SOF_DAI_FMT_NB_NF;1060	}1061}1062 1063static int sof_link_sai_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink,1064			     struct sof_ipc_dai_config *config, struct snd_sof_dai *dai)1065{1066	struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs;1067	struct sof_dai_private_data *private = dai->private;1068	u32 size = sizeof(*config);1069	int ret;1070 1071	/* handle master/slave and inverted clocks */1072	sof_dai_set_format(hw_config, config);1073 1074	/* init IPC */1075	memset(&config->sai, 0, sizeof(config->sai));1076	config->hdr.size = size;1077 1078	/* parse one set of SAI tokens */1079	ret = sof_update_ipc_object(scomp, &config->sai, SOF_SAI_TOKENS, slink->tuples,1080				    slink->num_tuples, size, 1);1081	if (ret < 0)1082		return ret;1083 1084	config->sai.mclk_rate = le32_to_cpu(hw_config->mclk_rate);1085	config->sai.bclk_rate = le32_to_cpu(hw_config->bclk_rate);1086	config->sai.fsync_rate = le32_to_cpu(hw_config->fsync_rate);1087	config->sai.mclk_direction = hw_config->mclk_direction;1088 1089	config->sai.tdm_slots = le32_to_cpu(hw_config->tdm_slots);1090	config->sai.tdm_slot_width = le32_to_cpu(hw_config->tdm_slot_width);1091	config->sai.rx_slots = le32_to_cpu(hw_config->rx_slots);1092	config->sai.tx_slots = le32_to_cpu(hw_config->tx_slots);1093 1094	dev_info(scomp->dev,1095		 "tplg: config SAI%d fmt 0x%x mclk %d width %d slots %d mclk id %d\n",1096		config->dai_index, config->format,1097		config->sai.mclk_rate, config->sai.tdm_slot_width,1098		config->sai.tdm_slots, config->sai.mclk_id);1099 1100	if (config->sai.tdm_slots < 1 || config->sai.tdm_slots > 8) {1101		dev_err(scomp->dev, "Invalid channel count for SAI%d\n", config->dai_index);1102		return -EINVAL;1103	}1104 1105	dai->number_configs = 1;1106	dai->current_config = 0;1107	private->dai_config = kmemdup(config, size, GFP_KERNEL);1108	if (!private->dai_config)1109		return -ENOMEM;1110 1111	return 0;1112}1113 1114static int sof_link_esai_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink,1115			      struct sof_ipc_dai_config *config, struct snd_sof_dai *dai)1116{1117	struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs;1118	struct sof_dai_private_data *private = dai->private;1119	u32 size = sizeof(*config);1120	int ret;1121 1122	/* handle master/slave and inverted clocks */1123	sof_dai_set_format(hw_config, config);1124 1125	/* init IPC */1126	memset(&config->esai, 0, sizeof(config->esai));1127	config->hdr.size = size;1128 1129	/* parse one set of ESAI tokens */1130	ret = sof_update_ipc_object(scomp, &config->esai, SOF_ESAI_TOKENS, slink->tuples,1131				    slink->num_tuples, size, 1);1132	if (ret < 0)1133		return ret;1134 1135	config->esai.mclk_rate = le32_to_cpu(hw_config->mclk_rate);1136	config->esai.bclk_rate = le32_to_cpu(hw_config->bclk_rate);1137	config->esai.fsync_rate = le32_to_cpu(hw_config->fsync_rate);1138	config->esai.mclk_direction = hw_config->mclk_direction;1139	config->esai.tdm_slots = le32_to_cpu(hw_config->tdm_slots);1140	config->esai.tdm_slot_width = le32_to_cpu(hw_config->tdm_slot_width);1141	config->esai.rx_slots = le32_to_cpu(hw_config->rx_slots);1142	config->esai.tx_slots = le32_to_cpu(hw_config->tx_slots);1143 1144	dev_info(scomp->dev,1145		 "tplg: config ESAI%d fmt 0x%x mclk %d width %d slots %d mclk id %d\n",1146		config->dai_index, config->format,1147		config->esai.mclk_rate, config->esai.tdm_slot_width,1148		config->esai.tdm_slots, config->esai.mclk_id);1149 1150	if (config->esai.tdm_slots < 1 || config->esai.tdm_slots > 8) {1151		dev_err(scomp->dev, "Invalid channel count for ESAI%d\n", config->dai_index);1152		return -EINVAL;1153	}1154 1155	dai->number_configs = 1;1156	dai->current_config = 0;1157	private->dai_config = kmemdup(config, size, GFP_KERNEL);1158	if (!private->dai_config)1159		return -ENOMEM;1160 1161	return 0;1162}1163 1164static int sof_link_micfil_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink,1165				struct sof_ipc_dai_config *config, struct snd_sof_dai *dai)1166{1167	struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs;1168	struct sof_dai_private_data *private = dai->private;1169	u32 size = sizeof(*config);1170	int ret;1171 1172       /* handle master/slave and inverted clocks */1173	sof_dai_set_format(hw_config, config);1174 1175	config->hdr.size = size;1176 1177	/* parse the required set of MICFIL PDM tokens based on num_hw_cfgs */1178	ret = sof_update_ipc_object(scomp, &config->micfil, SOF_MICFIL_TOKENS, slink->tuples,1179				    slink->num_tuples, size, slink->num_hw_configs);1180	if (ret < 0)1181		return ret;1182 1183	dev_info(scomp->dev, "MICFIL PDM config dai_index %d channel %d rate %d\n",1184		 config->dai_index, config->micfil.pdm_ch, config->micfil.pdm_rate);1185 1186	dai->number_configs = 1;1187	dai->current_config = 0;1188	private->dai_config = kmemdup(config, size, GFP_KERNEL);1189	if (!private->dai_config)1190		return -ENOMEM;1191 1192	return 0;1193}1194 1195static int sof_link_acp_dmic_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink,1196				  struct sof_ipc_dai_config *config, struct snd_sof_dai *dai)1197{1198	struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs;1199	struct sof_dai_private_data *private = dai->private;1200	u32 size = sizeof(*config);1201	int ret;1202 1203       /* handle master/slave and inverted clocks */1204	sof_dai_set_format(hw_config, config);1205 1206	config->hdr.size = size;1207 1208	/* parse the required set of ACPDMIC tokens based on num_hw_cfgs */1209	ret = sof_update_ipc_object(scomp, &config->acpdmic, SOF_ACPDMIC_TOKENS, slink->tuples,1210				    slink->num_tuples, size, slink->num_hw_configs);1211	if (ret < 0)1212		return ret;1213 1214	dev_info(scomp->dev, "ACP_DMIC config ACP%d channel %d rate %d\n",1215		 config->dai_index, config->acpdmic.pdm_ch,1216		 config->acpdmic.pdm_rate);1217 1218	dai->number_configs = 1;1219	dai->current_config = 0;1220	private->dai_config = kmemdup(config, size, GFP_KERNEL);1221	if (!private->dai_config)1222		return -ENOMEM;1223 1224	return 0;1225}1226 1227static int sof_link_acp_bt_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink,1228				struct sof_ipc_dai_config *config, struct snd_sof_dai *dai)1229{1230	struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs;1231	struct sof_dai_private_data *private = dai->private;1232	u32 size = sizeof(*config);1233	int ret;1234 1235	/* handle master/slave and inverted clocks */1236	sof_dai_set_format(hw_config, config);1237 1238	/* init IPC */1239	memset(&config->acpbt, 0, sizeof(config->acpbt));1240	config->hdr.size = size;1241 1242	ret = sof_update_ipc_object(scomp, &config->acpbt, SOF_ACPI2S_TOKENS, slink->tuples,1243				    slink->num_tuples, size, slink->num_hw_configs);1244	if (ret < 0)1245		return ret;1246 1247	dev_info(scomp->dev, "ACP_BT config ACP%d channel %d rate %d tdm_mode %d\n",1248		 config->dai_index, config->acpbt.tdm_slots,1249		 config->acpbt.fsync_rate, config->acpbt.tdm_mode);1250 1251	dai->number_configs = 1;1252	dai->current_config = 0;1253	private->dai_config = kmemdup(config, size, GFP_KERNEL);1254	if (!private->dai_config)1255		return -ENOMEM;1256 1257	return 0;1258}1259 1260static int sof_link_acp_sp_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink,1261				struct sof_ipc_dai_config *config, struct snd_sof_dai *dai)1262{1263	struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs;1264	struct sof_dai_private_data *private = dai->private;1265	u32 size = sizeof(*config);1266	int ret;1267 1268	/* handle master/slave and inverted clocks */1269	sof_dai_set_format(hw_config, config);1270 1271	/* init IPC */1272	memset(&config->acpsp, 0, sizeof(config->acpsp));1273	config->hdr.size = size;1274 1275	ret = sof_update_ipc_object(scomp, &config->acpsp, SOF_ACPI2S_TOKENS, slink->tuples,1276				    slink->num_tuples, size, slink->num_hw_configs);1277	if (ret < 0)1278		return ret;1279 1280 1281	dev_info(scomp->dev, "ACP_SP config ACP%d channel %d rate %d tdm_mode %d\n",1282		 config->dai_index, config->acpsp.tdm_slots,1283		 config->acpsp.fsync_rate, config->acpsp.tdm_mode);1284 1285	dai->number_configs = 1;1286	dai->current_config = 0;1287	private->dai_config = kmemdup(config, size, GFP_KERNEL);1288	if (!private->dai_config)1289		return -ENOMEM;1290 1291	return 0;1292}1293 1294static int sof_link_acp_hs_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink,1295				struct sof_ipc_dai_config *config, struct snd_sof_dai *dai)1296{1297	struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs;1298	struct sof_dai_private_data *private = dai->private;1299	u32 size = sizeof(*config);1300	int ret;1301 1302	/* Configures the DAI hardware format and inverted clocks */1303	sof_dai_set_format(hw_config, config);1304 1305	/* init IPC */1306	memset(&config->acphs, 0, sizeof(config->acphs));1307	config->hdr.size = size;1308 1309	ret = sof_update_ipc_object(scomp, &config->acphs, SOF_ACPI2S_TOKENS, slink->tuples,1310				    slink->num_tuples, size, slink->num_hw_configs);1311	if (ret < 0)1312		return ret;1313 1314	dev_info(scomp->dev, "ACP_HS config ACP%d channel %d rate %d tdm_mode %d\n",1315		 config->dai_index, config->acphs.tdm_slots,1316		 config->acphs.fsync_rate, config->acphs.tdm_mode);1317 1318	dai->number_configs = 1;1319	dai->current_config = 0;1320	private->dai_config = kmemdup(config, size, GFP_KERNEL);1321	if (!private->dai_config)1322		return -ENOMEM;1323 1324	return 0;1325}1326 1327static int sof_link_acp_sdw_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink,1328				 struct sof_ipc_dai_config *config, struct snd_sof_dai *dai)1329{1330	struct sof_dai_private_data *private = dai->private;1331	u32 size = sizeof(*config);1332	int ret;1333 1334	/* parse the required set of ACP_SDW tokens based on num_hw_cfgs */1335	ret = sof_update_ipc_object(scomp, &config->acp_sdw, SOF_ACP_SDW_TOKENS, slink->tuples,1336				    slink->num_tuples, size, slink->num_hw_configs);1337	if (ret < 0)1338		return ret;1339 1340	/* init IPC */1341	config->hdr.size = size;1342	dev_dbg(scomp->dev, "ACP SDW config rate %d channels %d\n",1343		config->acp_sdw.rate, config->acp_sdw.channels);1344 1345	/* set config for all DAI's with name matching the link name */1346	dai->number_configs = 1;1347	dai->current_config = 0;1348	private->dai_config = kmemdup(config, size, GFP_KERNEL);1349	if (!private->dai_config)1350		return -ENOMEM;1351 1352	return 0;1353}1354 1355static int sof_link_afe_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink,1356			     struct sof_ipc_dai_config *config, struct snd_sof_dai *dai)1357{1358	struct sof_dai_private_data *private = dai->private;1359	u32 size = sizeof(*config);1360	int ret;1361 1362	config->hdr.size = size;1363 1364	/* parse the required set of AFE tokens based on num_hw_cfgs */1365	ret = sof_update_ipc_object(scomp, &config->afe, SOF_AFE_TOKENS, slink->tuples,1366				    slink->num_tuples, size, slink->num_hw_configs);1367	if (ret < 0)1368		return ret;1369 1370	dev_dbg(scomp->dev, "AFE config rate %d channels %d format:%d\n",1371		config->afe.rate, config->afe.channels, config->afe.format);1372 1373	config->afe.stream_id = DMA_CHAN_INVALID;1374 1375	dai->number_configs = 1;1376	dai->current_config = 0;1377	private->dai_config = kmemdup(config, size, GFP_KERNEL);1378	if (!private->dai_config)1379		return -ENOMEM;1380 1381	return 0;1382}1383 1384static int sof_link_ssp_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink,1385			     struct sof_ipc_dai_config *config, struct snd_sof_dai *dai)1386{1387	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);1388	struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs;1389	struct sof_dai_private_data *private = dai->private;1390	u32 size = sizeof(*config);1391	int current_config = 0;1392	int i, ret;1393 1394	/*1395	 * Parse common data, we should have 1 common data per hw_config.1396	 */1397	ret = sof_update_ipc_object(scomp, &config->ssp, SOF_SSP_TOKENS, slink->tuples,1398				    slink->num_tuples, size, slink->num_hw_configs);1399	if (ret < 0)1400		return ret;1401 1402	/* process all possible hw configs */1403	for (i = 0; i < slink->num_hw_configs; i++) {1404		if (le32_to_cpu(hw_config[i].id) == slink->default_hw_cfg_id)1405			current_config = i;1406 1407		/* handle master/slave and inverted clocks */1408		sof_dai_set_format(&hw_config[i], &config[i]);1409 1410		config[i].hdr.size = size;1411 1412		if (sdev->mclk_id_override) {1413			dev_dbg(scomp->dev, "tplg: overriding topology mclk_id %d by quirk %d\n",1414				config[i].ssp.mclk_id, sdev->mclk_id_quirk);1415			config[i].ssp.mclk_id = sdev->mclk_id_quirk;1416		}1417 1418		/* copy differentiating hw configs to ipc structs */1419		config[i].ssp.mclk_rate = le32_to_cpu(hw_config[i].mclk_rate);1420		config[i].ssp.bclk_rate = le32_to_cpu(hw_config[i].bclk_rate);1421		config[i].ssp.fsync_rate = le32_to_cpu(hw_config[i].fsync_rate);1422		config[i].ssp.tdm_slots = le32_to_cpu(hw_config[i].tdm_slots);1423		config[i].ssp.tdm_slot_width = le32_to_cpu(hw_config[i].tdm_slot_width);1424		config[i].ssp.mclk_direction = hw_config[i].mclk_direction;1425		config[i].ssp.rx_slots = le32_to_cpu(hw_config[i].rx_slots);1426		config[i].ssp.tx_slots = le32_to_cpu(hw_config[i].tx_slots);1427 1428		dev_dbg(scomp->dev, "tplg: config SSP%d fmt %#x mclk %d bclk %d fclk %d width (%d)%d slots %d mclk id %d quirks %d clks_control %#x\n",1429			config[i].dai_index, config[i].format,1430			config[i].ssp.mclk_rate, config[i].ssp.bclk_rate,1431			config[i].ssp.fsync_rate, config[i].ssp.sample_valid_bits,1432			config[i].ssp.tdm_slot_width, config[i].ssp.tdm_slots,1433			config[i].ssp.mclk_id, config[i].ssp.quirks, config[i].ssp.clks_control);1434 1435		/* validate SSP fsync rate and channel count */1436		if (config[i].ssp.fsync_rate < 8000 || config[i].ssp.fsync_rate > 192000) {1437			dev_err(scomp->dev, "Invalid fsync rate for SSP%d\n", config[i].dai_index);1438			return -EINVAL;1439		}1440 1441		if (config[i].ssp.tdm_slots < 1 || config[i].ssp.tdm_slots > 8) {1442			dev_err(scomp->dev, "Invalid channel count for SSP%d\n",1443				config[i].dai_index);1444			return -EINVAL;1445		}1446	}1447 1448	dai->number_configs = slink->num_hw_configs;1449	dai->current_config = current_config;1450	private->dai_config = kmemdup(config, size * slink->num_hw_configs, GFP_KERNEL);1451	if (!private->dai_config)1452		return -ENOMEM;1453 1454	return 0;1455}1456 1457static int sof_link_dmic_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink,1458			      struct sof_ipc_dai_config *config, struct snd_sof_dai *dai)1459{1460	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);1461	struct sof_dai_private_data *private = dai->private;1462	struct sof_ipc_fw_ready *ready = &sdev->fw_ready;1463	struct sof_ipc_fw_version *v = &ready->version;1464	size_t size = sizeof(*config);1465	int i, ret;1466 1467	/* Ensure the entire DMIC config struct is zeros */1468	memset(&config->dmic, 0, sizeof(config->dmic));1469 1470	/* parse the required set of DMIC tokens based on num_hw_cfgs */1471	ret = sof_update_ipc_object(scomp, &config->dmic, SOF_DMIC_TOKENS, slink->tuples,1472				    slink->num_tuples, size, slink->num_hw_configs);1473	if (ret < 0)1474		return ret;1475 1476	/* parse the required set of DMIC PDM tokens based on number of active PDM's */1477	ret = sof_update_ipc_object(scomp, &config->dmic.pdm[0], SOF_DMIC_PDM_TOKENS,1478				    slink->tuples, slink->num_tuples,1479				    sizeof(struct sof_ipc_dai_dmic_pdm_ctrl),1480				    config->dmic.num_pdm_active);1481	if (ret < 0)1482		return ret;1483 1484	/* set IPC header size */1485	config->hdr.size = size;1486 1487	/* debug messages */1488	dev_dbg(scomp->dev, "tplg: config DMIC%d driver version %d\n",1489		config->dai_index, config->dmic.driver_ipc_version);1490	dev_dbg(scomp->dev, "pdmclk_min %d pdm_clkmax %d duty_min %d\n",1491		config->dmic.pdmclk_min, config->dmic.pdmclk_max,1492		config->dmic.duty_min);1493	dev_dbg(scomp->dev, "duty_max %d fifo_fs %d num_pdms active %d\n",1494		config->dmic.duty_max, config->dmic.fifo_fs,1495		config->dmic.num_pdm_active);1496	dev_dbg(scomp->dev, "fifo word length %d\n", config->dmic.fifo_bits);1497 1498	for (i = 0; i < config->dmic.num_pdm_active; i++) {1499		dev_dbg(scomp->dev, "pdm %d mic a %d mic b %d\n",1500			config->dmic.pdm[i].id,1501			config->dmic.pdm[i].enable_mic_a,1502			config->dmic.pdm[i].enable_mic_b);1503		dev_dbg(scomp->dev, "pdm %d polarity a %d polarity b %d\n",1504			config->dmic.pdm[i].id,1505			config->dmic.pdm[i].polarity_mic_a,1506			config->dmic.pdm[i].polarity_mic_b);1507		dev_dbg(scomp->dev, "pdm %d clk_edge %d skew %d\n",1508			config->dmic.pdm[i].id,1509			config->dmic.pdm[i].clk_edge,1510			config->dmic.pdm[i].skew);1511	}1512 1513	/*1514	 * this takes care of backwards compatible handling of fifo_bits_b.1515	 * It is deprecated since firmware ABI version 3.0.1.1516	 */1517	if (SOF_ABI_VER(v->major, v->minor, v->micro) < SOF_ABI_VER(3, 0, 1))1518		config->dmic.fifo_bits_b = config->dmic.fifo_bits;1519 1520	dai->number_configs = 1;1521	dai->current_config = 0;1522	private->dai_config = kmemdup(config, size, GFP_KERNEL);1523	if (!private->dai_config)1524		return -ENOMEM;1525 1526	return 0;1527}1528 1529static int sof_link_alh_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink,1530			     struct sof_ipc_dai_config *config, struct snd_sof_dai *dai)1531{1532	struct sof_dai_private_data *private = dai->private;1533	u32 size = sizeof(*config);1534	int ret;1535 1536	/* parse the required set of ALH tokens based on num_hw_cfgs */1537	ret = sof_update_ipc_object(scomp, &config->alh, SOF_ALH_TOKENS, slink->tuples,1538				    slink->num_tuples, size, slink->num_hw_configs);1539	if (ret < 0)1540		return ret;1541 1542	/* init IPC */1543	config->hdr.size = size;1544 1545	/* set config for all DAI's with name matching the link name */1546	dai->number_configs = 1;1547	dai->current_config = 0;1548	private->dai_config = kmemdup(config, size, GFP_KERNEL);1549	if (!private->dai_config)1550		return -ENOMEM;1551 1552	return 0;1553}1554 1555static int sof_ipc3_widget_setup_comp_dai(struct snd_sof_widget *swidget)1556{1557	struct snd_soc_component *scomp = swidget->scomp;1558	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);1559	struct snd_sof_dai *dai = swidget->private;1560	struct sof_dai_private_data *private;1561	struct sof_ipc_comp_dai *comp_dai;1562	size_t ipc_size = sizeof(*comp_dai);1563	struct sof_ipc_dai_config *config;1564	struct snd_sof_dai_link *slink;1565	int ret;1566 1567	private = kzalloc(sizeof(*private), GFP_KERNEL);1568	if (!private)1569		return -ENOMEM;1570 1571	dai->private = private;1572 1573	private->comp_dai = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id);1574	if (!private->comp_dai) {1575		ret = -ENOMEM;1576		goto free;1577	}1578 1579	/* configure dai IPC message */1580	comp_dai = private->comp_dai;1581	comp_dai->comp.type = SOF_COMP_DAI;1582	comp_dai->config.hdr.size = sizeof(comp_dai->config);1583 1584	/* parse one set of DAI tokens */1585	ret = sof_update_ipc_object(scomp, comp_dai, SOF_DAI_TOKENS, swidget->tuples,1586				    swidget->num_tuples, sizeof(*comp_dai), 1);1587	if (ret < 0)1588		goto free;1589 1590	/* update comp_tokens */1591	ret = sof_update_ipc_object(scomp, &comp_dai->config, SOF_COMP_TOKENS,1592				    swidget->tuples, swidget->num_tuples,1593				    sizeof(comp_dai->config), 1);1594	if (ret < 0)1595		goto free;1596 1597	dev_dbg(scomp->dev, "dai %s: type %d index %d\n",1598		swidget->widget->name, comp_dai->type, comp_dai->dai_index);1599	sof_dbg_comp_config(scomp, &comp_dai->config);1600 1601	/* now update DAI config */1602	list_for_each_entry(slink, &sdev->dai_link_list, list) {1603		struct sof_ipc_dai_config common_config;1604		int i;1605 1606		if (strcmp(slink->link->name, dai->name))1607			continue;1608 1609		/* Reserve memory for all hw configs, eventually freed by widget */1610		config = kcalloc(slink->num_hw_configs, sizeof(*config), GFP_KERNEL);1611		if (!config) {1612			ret = -ENOMEM;1613			goto free_comp;1614		}1615 1616		/* parse one set of DAI link tokens */1617		ret = sof_update_ipc_object(scomp, &common_config, SOF_DAI_LINK_TOKENS,1618					    slink->tuples, slink->num_tuples,1619					    sizeof(common_config), 1);1620		if (ret < 0)1621			goto free_config;1622 1623		for (i = 0; i < slink->num_hw_configs; i++) {1624			config[i].hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG;1625			config[i].format = le32_to_cpu(slink->hw_configs[i].fmt);1626			config[i].type = common_config.type;1627			config[i].dai_index = comp_dai->dai_index;1628		}1629 1630		switch (common_config.type) {1631		case SOF_DAI_INTEL_SSP:1632			ret = sof_link_ssp_load(scomp, slink, config, dai);1633			break;1634		case SOF_DAI_INTEL_DMIC:1635			ret = sof_link_dmic_load(scomp, slink, config, dai);1636			break;1637		case SOF_DAI_INTEL_HDA:1638			ret = sof_link_hda_load(scomp, slink, config, dai);1639			break;1640		case SOF_DAI_INTEL_ALH:1641			ret = sof_link_alh_load(scomp, slink, config, dai);1642			break;1643		case SOF_DAI_IMX_SAI:1644			ret = sof_link_sai_load(scomp, slink, config, dai);1645			break;1646		case SOF_DAI_IMX_ESAI:1647			ret = sof_link_esai_load(scomp, slink, config, dai);1648			break;1649		case SOF_DAI_IMX_MICFIL:1650			ret = sof_link_micfil_load(scomp, slink, config, dai);1651			break;1652		case SOF_DAI_AMD_BT:1653			ret = sof_link_acp_bt_load(scomp, slink, config, dai);1654			break;1655		case SOF_DAI_AMD_SP:1656		case SOF_DAI_AMD_SP_VIRTUAL:1657			ret = sof_link_acp_sp_load(scomp, slink, config, dai);1658			break;1659		case SOF_DAI_AMD_HS:1660		case SOF_DAI_AMD_HS_VIRTUAL:1661			ret = sof_link_acp_hs_load(scomp, slink, config, dai);1662			break;1663		case SOF_DAI_AMD_DMIC:1664			ret = sof_link_acp_dmic_load(scomp, slink, config, dai);1665			break;1666		case SOF_DAI_MEDIATEK_AFE:1667			ret = sof_link_afe_load(scomp, slink, config, dai);1668			break;1669		case SOF_DAI_AMD_SDW:1670			ret = sof_link_acp_sdw_load(scomp, slink, config, dai);1671			break;1672		default:1673			break;1674		}1675		if (ret < 0) {1676			dev_err(scomp->dev, "failed to load config for dai %s\n", dai->name);1677			goto free_config;1678		}1679 1680		kfree(config);1681	}1682 1683	return 0;1684free_config:1685	kfree(config);1686free_comp:1687	kfree(comp_dai);1688free:1689	kfree(private);1690	dai->private = NULL;1691	return ret;1692}1693 1694static void sof_ipc3_widget_free_comp_dai(struct snd_sof_widget *swidget)1695{1696	switch (swidget->id) {1697	case snd_soc_dapm_dai_in:1698	case snd_soc_dapm_dai_out:1699	{1700		struct snd_sof_dai *dai = swidget->private;1701		struct sof_dai_private_data *dai_data;1702 1703		if (!dai)1704			return;1705 1706		dai_data = dai->private;1707		if (dai_data) {1708			kfree(dai_data->comp_dai);1709			kfree(dai_data->dai_config);1710			kfree(dai_data);1711		}1712		kfree(dai);1713		break;1714	}1715	default:1716		break;1717	}1718}1719 1720static int sof_ipc3_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *sroute)1721{1722	struct sof_ipc_pipe_comp_connect connect;1723	int ret;1724 1725	connect.hdr.size = sizeof(connect);1726	connect.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_CONNECT;1727	connect.source_id = sroute->src_widget->comp_id;1728	connect.sink_id = sroute->sink_widget->comp_id;1729 1730	dev_dbg(sdev->dev, "setting up route %s -> %s\n",1731		sroute->src_widget->widget->name,1732		sroute->sink_widget->widget->name);1733 1734	/* send ipc */1735	ret = sof_ipc_tx_message_no_reply(sdev->ipc, &connect, sizeof(connect));1736	if (ret < 0)1737		dev_err(sdev->dev, "%s: route %s -> %s failed\n", __func__,1738			sroute->src_widget->widget->name, sroute->sink_widget->widget->name);1739 1740	return ret;1741}1742 1743static int sof_ipc3_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)1744{1745	struct sof_ipc_ctrl_data *cdata;1746	size_t priv_size_check;1747	int ret;1748 1749	if (scontrol->max_size < (sizeof(*cdata) + sizeof(struct sof_abi_hdr))) {1750		dev_err(sdev->dev, "%s: insufficient size for a bytes control: %zu.\n",1751			__func__, scontrol->max_size);1752		return -EINVAL;1753	}1754 1755	if (scontrol->priv_size > scontrol->max_size - sizeof(*cdata)) {1756		dev_err(sdev->dev,1757			"%s: bytes data size %zu exceeds max %zu.\n", __func__,1758			scontrol->priv_size, scontrol->max_size - sizeof(*cdata));1759		return -EINVAL;1760	}1761 1762	scontrol->ipc_control_data = kzalloc(scontrol->max_size, GFP_KERNEL);1763	if (!scontrol->ipc_control_data)1764		return -ENOMEM;1765 1766	scontrol->size = sizeof(struct sof_ipc_ctrl_data) + scontrol->priv_size;1767 1768	cdata = scontrol->ipc_control_data;1769	cdata->cmd = SOF_CTRL_CMD_BINARY;1770	cdata->index = scontrol->index;1771 1772	if (scontrol->priv_size > 0) {1773		memcpy(cdata->data, scontrol->priv, scontrol->priv_size);1774		kfree(scontrol->priv);1775		scontrol->priv = NULL;1776 1777		if (cdata->data->magic != SOF_ABI_MAGIC) {1778			dev_err(sdev->dev, "Wrong ABI magic 0x%08x.\n", cdata->data->magic);1779			ret = -EINVAL;1780			goto err;1781		}1782 1783		if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) {1784			dev_err(sdev->dev, "Incompatible ABI version 0x%08x.\n",1785				cdata->data->abi);1786			ret = -EINVAL;1787			goto err;1788		}1789 1790		priv_size_check = cdata->data->size + sizeof(struct sof_abi_hdr);1791		if (priv_size_check != scontrol->priv_size) {1792			dev_err(sdev->dev, "Conflict in bytes (%zu) vs. priv size (%zu).\n",1793				priv_size_check, scontrol->priv_size);1794			ret = -EINVAL;1795			goto err;1796		}1797	}1798 1799	return 0;1800err:1801	kfree(scontrol->ipc_control_data);1802	scontrol->ipc_control_data = NULL;1803	return ret;1804}1805 1806static int sof_ipc3_control_load_volume(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)1807{1808	struct sof_ipc_ctrl_data *cdata;1809	int i;1810 1811	/* init the volume get/put data */1812	scontrol->size = struct_size(cdata, chanv, scontrol->num_channels);1813 1814	scontrol->ipc_control_data = kzalloc(scontrol->size, GFP_KERNEL);1815	if (!scontrol->ipc_control_data)1816		return -ENOMEM;1817 1818	cdata = scontrol->ipc_control_data;1819	cdata->index = scontrol->index;1820 1821	/* set cmd for mixer control */1822	if (scontrol->max == 1) {1823		cdata->cmd = SOF_CTRL_CMD_SWITCH;1824		return 0;1825	}1826 1827	cdata->cmd = SOF_CTRL_CMD_VOLUME;1828 1829	/* set default volume values to 0dB in control */1830	for (i = 0; i < scontrol->num_channels; i++) {1831		cdata->chanv[i].channel = i;1832		cdata->chanv[i].value = VOL_ZERO_DB;1833	}1834 1835	return 0;1836}1837 1838static int sof_ipc3_control_load_enum(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)1839{1840	struct sof_ipc_ctrl_data *cdata;1841 1842	/* init the enum get/put data */1843	scontrol->size = struct_size(cdata, chanv, scontrol->num_channels);1844 1845	scontrol->ipc_control_data = kzalloc(scontrol->size, GFP_KERNEL);1846	if (!scontrol->ipc_control_data)1847		return -ENOMEM;1848 1849	cdata = scontrol->ipc_control_data;1850	cdata->index = scontrol->index;1851	cdata->cmd = SOF_CTRL_CMD_ENUM;1852 1853	return 0;1854}1855 1856static int sof_ipc3_control_setup(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)1857{1858	switch (scontrol->info_type) {1859	case SND_SOC_TPLG_CTL_VOLSW:1860	case SND_SOC_TPLG_CTL_VOLSW_SX:1861	case SND_SOC_TPLG_CTL_VOLSW_XR_SX:1862		return sof_ipc3_control_load_volume(sdev, scontrol);1863	case SND_SOC_TPLG_CTL_BYTES:1864		return sof_ipc3_control_load_bytes(sdev, scontrol);1865	case SND_SOC_TPLG_CTL_ENUM:1866	case SND_SOC_TPLG_CTL_ENUM_VALUE:1867		return sof_ipc3_control_load_enum(sdev, scontrol);1868	default:1869		break;1870	}1871 1872	return 0;1873}1874 1875static int sof_ipc3_control_free(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)1876{1877	struct sof_ipc_free fcomp;1878 1879	fcomp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_FREE;1880	fcomp.hdr.size = sizeof(fcomp);1881	fcomp.id = scontrol->comp_id;1882 1883	/* send IPC to the DSP */1884	return sof_ipc_tx_message_no_reply(sdev->ipc, &fcomp, sizeof(fcomp));1885}1886 1887/* send pcm params ipc */1888static int sof_ipc3_keyword_detect_pcm_params(struct snd_sof_widget *swidget, int dir)1889{1890	struct snd_soc_component *scomp = swidget->scomp;1891	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);1892	struct snd_pcm_hw_params *params;1893	struct sof_ipc_pcm_params pcm;1894	struct snd_sof_pcm *spcm;1895	int ret;1896 1897	/* get runtime PCM params using widget's stream name */1898	spcm = snd_sof_find_spcm_name(scomp, swidget->widget->sname);1899	if (!spcm) {1900		dev_err(scomp->dev, "Cannot find PCM for %s\n", swidget->widget->name);1901		return -EINVAL;1902	}1903 1904	params = &spcm->params[dir];1905 1906	/* set IPC PCM params */1907	memset(&pcm, 0, sizeof(pcm));1908	pcm.hdr.size = sizeof(pcm);1909	pcm.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_PARAMS;1910	pcm.comp_id = swidget->comp_id;1911	pcm.params.hdr.size = sizeof(pcm.params);1912	pcm.params.direction = dir;1913	pcm.params.sample_valid_bytes = params_width(params) >> 3;1914	pcm.params.buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED;1915	pcm.params.rate = params_rate(params);1916	pcm.params.channels = params_channels(params);1917	pcm.params.host_period_bytes = params_period_bytes(params);1918 1919	/* set format */1920	switch (params_format(params)) {1921	case SNDRV_PCM_FORMAT_S16:1922		pcm.params.frame_fmt = SOF_IPC_FRAME_S16_LE;1923		break;1924	case SNDRV_PCM_FORMAT_S24:1925		pcm.params.frame_fmt = SOF_IPC_FRAME_S24_4LE;1926		break;1927	case SNDRV_PCM_FORMAT_S32:1928		pcm.params.frame_fmt = SOF_IPC_FRAME_S32_LE;1929		break;1930	default:1931		return -EINVAL;1932	}1933 1934	/* send IPC to the DSP */1935	ret = sof_ipc_tx_message_no_reply(sdev->ipc, &pcm, sizeof(pcm));1936	if (ret < 0)1937		dev_err(scomp->dev, "%s: PCM params failed for %s\n", __func__,1938			swidget->widget->name);1939 1940	return ret;1941}1942 1943 /* send stream trigger ipc */1944static int sof_ipc3_keyword_detect_trigger(struct snd_sof_widget *swidget, int cmd)1945{1946	struct snd_soc_component *scomp = swidget->scomp;1947	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);1948	struct sof_ipc_stream stream;1949	int ret;1950 1951	/* set IPC stream params */1952	stream.hdr.size = sizeof(stream);1953	stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | cmd;1954	stream.comp_id = swidget->comp_id;1955 1956	/* send IPC to the DSP */1957	ret = sof_ipc_tx_message_no_reply(sdev->ipc, &stream, sizeof(stream));1958	if (ret < 0)1959		dev_err(scomp->dev, "%s: Failed to trigger %s\n", __func__, swidget->widget->name);1960 1961	return ret;1962}1963 1964static int sof_ipc3_keyword_dapm_event(struct snd_soc_dapm_widget *w,1965				       struct snd_kcontrol *k, int event)1966{1967	struct snd_sof_widget *swidget = w->dobj.private;1968	struct snd_soc_component *scomp;1969	int stream = SNDRV_PCM_STREAM_CAPTURE;1970	struct snd_sof_pcm *spcm;1971	int ret = 0;1972 1973	if (!swidget)1974		return 0;1975 1976	scomp = swidget->scomp;1977 1978	dev_dbg(scomp->dev, "received event %d for widget %s\n",1979		event, w->name);1980 1981	/* get runtime PCM params using widget's stream name */1982	spcm = snd_sof_find_spcm_name(scomp, swidget->widget->sname);1983	if (!spcm) {1984		dev_err(scomp->dev, "%s: Cannot find PCM for %s\n", __func__,1985			swidget->widget->name);1986		return -EINVAL;1987	}1988 1989	/* process events */1990	switch (event) {1991	case SND_SOC_DAPM_PRE_PMU:1992		if (spcm->stream[stream].suspend_ignored) {1993			dev_dbg(scomp->dev, "PRE_PMU event ignored, KWD pipeline is already RUNNING\n");1994			return 0;1995		}1996 1997		/* set pcm params */1998		ret = sof_ipc3_keyword_detect_pcm_params(swidget, stream);1999		if (ret < 0) {2000			dev_err(scomp->dev, "%s: Failed to set pcm params for widget %s\n",2001				__func__, swidget->widget->name);2002			break;2003		}2004 2005		/* start trigger */2006		ret = sof_ipc3_keyword_detect_trigger(swidget, SOF_IPC_STREAM_TRIG_START);2007		if (ret < 0)2008			dev_err(scomp->dev, "%s: Failed to trigger widget %s\n", __func__,2009				swidget->widget->name);2010		break;2011	case SND_SOC_DAPM_POST_PMD:2012		if (spcm->stream[stream].suspend_ignored) {2013			dev_dbg(scomp->dev,2014				"POST_PMD event ignored, KWD pipeline will remain RUNNING\n");2015			return 0;2016		}2017 2018		/* stop trigger */2019		ret = sof_ipc3_keyword_detect_trigger(swidget, SOF_IPC_STREAM_TRIG_STOP);2020		if (ret < 0)2021			dev_err(scomp->dev, "%s: Failed to trigger widget %s\n", __func__,2022				swidget->widget->name);2023 2024		/* pcm free */2025		ret = sof_ipc3_keyword_detect_trigger(swidget, SOF_IPC_STREAM_PCM_FREE);2026		if (ret < 0)2027			dev_err(scomp->dev, "%s: Failed to free PCM for widget %s\n", __func__,2028				swidget->widget->name);2029		break;2030	default:2031		break;2032	}2033 2034	return ret;2035}2036 2037/* event handlers for keyword detect component */2038static const struct snd_soc_tplg_widget_events sof_kwd_events[] = {2039	{SOF_KEYWORD_DETECT_DAPM_EVENT, sof_ipc3_keyword_dapm_event},2040};2041 2042static int sof_ipc3_widget_bind_event(struct snd_soc_component *scomp,2043				      struct snd_sof_widget *swidget, u16 event_type)2044{2045	struct sof_ipc_comp *ipc_comp;2046 2047	/* validate widget event type */2048	switch (event_type) {2049	case SOF_KEYWORD_DETECT_DAPM_EVENT:2050		/* only KEYWORD_DETECT comps should handle this */2051		if (swidget->id != snd_soc_dapm_effect)2052			break;2053 2054		ipc_comp = swidget->private;2055		if (ipc_comp && ipc_comp->type != SOF_COMP_KEYWORD_DETECT)2056			break;2057 2058		/* bind event to keyword detect comp */2059		return snd_soc_tplg_widget_bind_event(swidget->widget, sof_kwd_events,2060						      ARRAY_SIZE(sof_kwd_events), event_type);2061	default:2062		break;2063	}2064 2065	dev_err(scomp->dev, "Invalid event type %d for widget %s\n", event_type,2066		swidget->widget->name);2067 2068	return -EINVAL;2069}2070 2071static int sof_ipc3_complete_pipeline(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)2072{2073	struct sof_ipc_pipe_ready ready;2074	int ret;2075 2076	dev_dbg(sdev->dev, "tplg: complete pipeline %s id %d\n",2077		swidget->widget->name, swidget->comp_id);2078 2079	memset(&ready, 0, sizeof(ready));2080	ready.hdr.size = sizeof(ready);2081	ready.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_PIPE_COMPLETE;2082	ready.comp_id = swidget->comp_id;2083 2084	ret = sof_ipc_tx_message_no_reply(sdev->ipc, &ready, sizeof(ready));2085	if (ret < 0)2086		return ret;2087 2088	return 1;2089}2090 2091static int sof_ipc3_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)2092{2093	struct sof_ipc_free ipc_free = {2094		.hdr = {2095			.size = sizeof(ipc_free),2096			.cmd = SOF_IPC_GLB_TPLG_MSG,2097		},2098		.id = swidget->comp_id,2099	};2100	int ret;2101 2102	if (!swidget->private)2103		return 0;2104 2105	switch (swidget->id) {2106	case snd_soc_dapm_scheduler:2107	{2108		ipc_free.hdr.cmd |= SOF_IPC_TPLG_PIPE_FREE;2109		break;2110	}2111	case snd_soc_dapm_buffer:2112		ipc_free.hdr.cmd |= SOF_IPC_TPLG_BUFFER_FREE;2113		break;2114	default:2115		ipc_free.hdr.cmd |= SOF_IPC_TPLG_COMP_FREE;2116		break;2117	}2118 2119	ret = sof_ipc_tx_message_no_reply(sdev->ipc, &ipc_free, sizeof(ipc_free));2120	if (ret < 0)2121		dev_err(sdev->dev, "failed to free widget %s\n", swidget->widget->name);2122 2123	return ret;2124}2125 2126static int sof_ipc3_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,2127			       unsigned int flags, struct snd_sof_dai_config_data *data)2128{2129	struct sof_ipc_fw_version *v = &sdev->fw_ready.version;2130	struct snd_sof_dai *dai = swidget->private;2131	struct sof_dai_private_data *private;2132	struct sof_ipc_dai_config *config;2133	int ret = 0;2134 2135	if (!dai || !dai->private) {2136		dev_err(sdev->dev, "No private data for DAI %s\n", swidget->widget->name);2137		return -EINVAL;2138	}2139 2140	private = dai->private;2141	if (!private->dai_config) {2142		dev_err(sdev->dev, "No config for DAI %s\n", dai->name);2143		return -EINVAL;2144	}2145 2146	config = &private->dai_config[dai->current_config];2147	if (!config) {2148		dev_err(sdev->dev, "Invalid current config for DAI %s\n", dai->name);2149		return -EINVAL;2150	}2151 2152	switch (config->type) {2153	case SOF_DAI_INTEL_SSP:2154		/*2155		 * DAI_CONFIG IPC during hw_params/hw_free for SSP DAI's is not supported in older2156		 * firmware2157		 */2158		if (v->abi_version < SOF_ABI_VER(3, 18, 0) &&2159		    ((flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS) ||2160		     (flags & SOF_DAI_CONFIG_FLAGS_HW_FREE)))2161			return 0;2162		break;2163	case SOF_DAI_INTEL_HDA:2164		if (data)2165			config->hda.link_dma_ch = data->dai_data;2166		break;2167	case SOF_DAI_INTEL_ALH:2168		if (data) {2169			/* save the dai_index during hw_params and reuse it for hw_free */2170			if (flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS)2171				config->dai_index = data->dai_index;2172			config->alh.stream_id = data->dai_data;2173		}2174		break;2175	default:2176		break;2177	}2178 2179	/*2180	 * The dai_config op is invoked several times and the flags argument varies as below:2181	 * BE DAI hw_params: When the op is invoked during the BE DAI hw_params, flags contains2182	 * SOF_DAI_CONFIG_FLAGS_HW_PARAMS along with quirks2183	 * FE DAI hw_params: When invoked during FE DAI hw_params after the DAI widget has2184	 * just been set up in the DSP, flags is set to SOF_DAI_CONFIG_FLAGS_HW_PARAMS with no2185	 * quirks2186	 * BE DAI trigger: When invoked during the BE DAI trigger, flags is set to2187	 * SOF_DAI_CONFIG_FLAGS_PAUSE and contains no quirks2188	 * BE DAI hw_free: When invoked during the BE DAI hw_free, flags is set to2189	 * SOF_DAI_CONFIG_FLAGS_HW_FREE and contains no quirks2190	 * FE DAI hw_free: When invoked during the FE DAI hw_free, flags is set to2191	 * SOF_DAI_CONFIG_FLAGS_HW_FREE and contains no quirks2192	 *2193	 * The DAI_CONFIG IPC is sent to the DSP, only after the widget is set up during the FE2194	 * DAI hw_params. But since the BE DAI hw_params precedes the FE DAI hw_params, the quirks2195	 * need to be preserved when assigning the flags before sending the IPC.2196	 * For the case of PAUSE/HW_FREE, since there are no quirks, flags can be used as is.2197	 */2198 2199	if (flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS) {2200		/* Clear stale command */2201		config->flags &= ~SOF_DAI_CONFIG_FLAGS_CMD_MASK;2202		config->flags |= flags;2203	} else {2204		config->flags = flags;2205	}2206 2207	/* only send the IPC if the widget is set up in the DSP */2208	if (swidget->use_count > 0) {2209		ret = sof_ipc_tx_message_no_reply(sdev->ipc, config, config->hdr.size);2210		if (ret < 0)2211			dev_err(sdev->dev, "Failed to set dai config for %s\n", dai->name);2212 2213		/* clear the flags once the IPC has been sent even if it fails */2214		config->flags = SOF_DAI_CONFIG_FLAGS_NONE;2215	}2216 2217	return ret;2218}2219 2220static int sof_ipc3_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)2221{2222	int ret;2223 2224	if (!swidget->private)2225		return 0;2226 2227	switch (swidget->id) {2228	case snd_soc_dapm_dai_in:2229	case snd_soc_dapm_dai_out:2230	{2231		struct snd_sof_dai *dai = swidget->private;2232		struct sof_dai_private_data *dai_data = dai->private;2233		struct sof_ipc_comp *comp = &dai_data->comp_dai->comp;2234 2235		ret = sof_ipc_tx_message_no_reply(sdev->ipc, dai_data->comp_dai, comp->hdr.size);2236		break;2237	}2238	case snd_soc_dapm_scheduler:2239	{2240		struct sof_ipc_pipe_new *pipeline;2241 2242		pipeline = swidget->private;2243		ret = sof_ipc_tx_message_no_reply(sdev->ipc, pipeline, sizeof(*pipeline));2244		break;2245	}2246	default:2247	{2248		struct sof_ipc_cmd_hdr *hdr;2249 2250		hdr = swidget->private;2251		ret = sof_ipc_tx_message_no_reply(sdev->ipc, swidget->private, hdr->size);2252		break;2253	}2254	}2255	if (ret < 0)2256		dev_err(sdev->dev, "Failed to setup widget %s\n", swidget->widget->name);2257 2258	return ret;2259}2260 2261static int sof_ipc3_set_up_all_pipelines(struct snd_sof_dev *sdev, bool verify)2262{2263	struct sof_ipc_fw_version *v = &sdev->fw_ready.version;2264	struct snd_sof_widget *swidget;2265	struct snd_sof_route *sroute;2266	int ret;2267 2268	/* restore pipeline components */2269	list_for_each_entry(swidget, &sdev->widget_list, list) {2270		/* only set up the widgets belonging to static pipelines */2271		if (!verify && swidget->dynamic_pipeline_widget)2272			continue;2273 2274		/*2275		 * For older firmware, skip scheduler widgets in this loop,2276		 * sof_widget_setup() will be called in the 'complete pipeline' loop2277		 */2278		if (v->abi_version < SOF_ABI_VER(3, 19, 0) &&2279		    swidget->id == snd_soc_dapm_scheduler)2280			continue;2281 2282		/* update DAI config. The IPC will be sent in sof_widget_setup() */2283		if (WIDGET_IS_DAI(swidget->id)) {2284			struct snd_sof_dai *dai = swidget->private;2285			struct sof_dai_private_data *private;2286			struct sof_ipc_dai_config *config;2287 2288			if (!dai || !dai->private)2289				continue;2290			private = dai->private;2291			if (!private->dai_config)2292				continue;2293 2294			config = private->dai_config;2295			/*2296			 * The link DMA channel would be invalidated for running2297			 * streams but not for streams that were in the PAUSED2298			 * state during suspend. So invalidate it here before setting2299			 * the dai config in the DSP.2300			 */2301			if (config->type == SOF_DAI_INTEL_HDA)2302				config->hda.link_dma_ch = DMA_CHAN_INVALID;2303		}2304 2305		ret = sof_widget_setup(sdev, swidget);2306		if (ret < 0)2307			return ret;2308	}2309 2310	/* restore pipeline connections */2311	list_for_each_entry(sroute, &sdev->route_list, list) {2312		/* only set up routes belonging to static pipelines */2313		if (!verify && (sroute->src_widget->dynamic_pipeline_widget ||2314				sroute->sink_widget->dynamic_pipeline_widget))2315			continue;2316 2317		/*2318		 * For virtual routes, both sink and source are not buffer. IPC3 only supports2319		 * connections between a buffer and a component. Ignore the rest.2320		 */2321		if (sroute->src_widget->id != snd_soc_dapm_buffer &&2322		    sroute->sink_widget->id != snd_soc_dapm_buffer)2323			continue;2324 2325		ret = sof_route_setup(sdev, sroute->src_widget->widget,2326				      sroute->sink_widget->widget);2327		if (ret < 0) {2328			dev_err(sdev->dev, "%s: route set up failed\n", __func__);2329			return ret;2330		}2331	}2332 2333	/* complete pipeline */2334	list_for_each_entry(swidget, &sdev->widget_list, list) {2335		switch (swidget->id) {2336		case snd_soc_dapm_scheduler:2337			/* only complete static pipelines */2338			if (!verify && swidget->dynamic_pipeline_widget)2339				continue;2340 2341			if (v->abi_version < SOF_ABI_VER(3, 19, 0)) {2342				ret = sof_widget_setup(sdev, swidget);2343				if (ret < 0)2344					return ret;2345			}2346 2347			swidget->spipe->complete = sof_ipc3_complete_pipeline(sdev, swidget);2348			if (swidget->spipe->complete < 0)2349				return swidget->spipe->complete;2350			break;2351		default:2352			break;2353		}2354	}2355 2356	return 0;2357}2358 2359/*2360 * Free the PCM, its associated widgets and set the prepared flag to false for all PCMs that2361 * did not get suspended(ex: paused streams) so the widgets can be set up again during resume.2362 */2363static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev)2364{2365	struct snd_sof_widget *swidget;2366	struct snd_sof_pcm *spcm;2367	int dir, ret;2368 2369	/*2370	 * free all PCMs and their associated DAPM widgets if their connected DAPM widget2371	 * list is not NULL. This should only be true for paused streams at this point.2372	 * This is equivalent to the handling of FE DAI suspend trigger for running streams.2373	 */2374	list_for_each_entry(spcm, &sdev->pcm_list, list) {2375		for_each_pcm_streams(dir) {2376			struct snd_pcm_substream *substream = spcm->stream[dir].substream;2377 2378			if (!substream || !substream->runtime || spcm->stream[dir].suspend_ignored)2379				continue;2380 2381			if (spcm->stream[dir].list) {2382				ret = sof_pcm_stream_free(sdev, substream, spcm, dir, true);2383				if (ret < 0)2384					return ret;2385			}2386		}2387	}2388 2389	/*2390	 * free any left over DAI widgets. This is equivalent to the handling of suspend trigger2391	 * for the BE DAI for running streams.2392	 */2393	list_for_each_entry(swidget, &sdev->widget_list, list)2394		if (WIDGET_IS_DAI(swidget->id) && swidget->use_count == 1) {2395			ret = sof_widget_free(sdev, swidget);2396			if (ret < 0)2397				return ret;2398		}2399 2400	return 0;2401}2402 2403static int sof_ipc3_free_widgets_in_list(struct snd_sof_dev *sdev, bool include_scheduler,2404					 bool *dyn_widgets, bool verify)2405{2406	struct sof_ipc_fw_version *v = &sdev->fw_ready.version;2407	struct snd_sof_widget *swidget;2408	int ret;2409 2410	list_for_each_entry(swidget, &sdev->widget_list, list) {2411		if (swidget->dynamic_pipeline_widget) {2412			*dyn_widgets = true;2413			continue;2414		}2415 2416		/* Do not free widgets for static pipelines with FW older than SOF2.2 */2417		if (!verify && !swidget->dynamic_pipeline_widget &&2418		    SOF_FW_VER(v->major, v->minor, v->micro) < SOF_FW_VER(2, 2, 0)) {2419			mutex_lock(&swidget->setup_mutex);2420			swidget->use_count = 0;2421			mutex_unlock(&swidget->setup_mutex);2422			if (swidget->spipe)2423				swidget->spipe->complete = 0;2424			continue;2425		}2426 2427		if (include_scheduler && swidget->id != snd_soc_dapm_scheduler)2428			continue;2429 2430		if (!include_scheduler && swidget->id == snd_soc_dapm_scheduler)2431			continue;2432 2433		ret = sof_widget_free(sdev, swidget);2434		if (ret < 0)2435			return ret;2436	}2437 2438	return 0;2439}2440 2441/*2442 * For older firmware, this function doesn't free widgets for static pipelines during suspend.2443 * It only resets use_count for all widgets.2444 */2445static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verify)2446{2447	struct sof_ipc_fw_version *v = &sdev->fw_ready.version;2448	struct snd_sof_widget *swidget;2449	struct snd_sof_route *sroute;2450	bool dyn_widgets = false;2451	int ret;2452 2453	/*2454	 * This function is called during suspend and for one-time topology verification during2455	 * first boot. In both cases, there is no need to protect swidget->use_count and2456	 * sroute->setup because during suspend all running streams are suspended and during2457	 * topology loading the sound card unavailable to open PCMs. Do not free the scheduler2458	 * widgets yet so that the secondary cores do not get powered down before all the widgets2459	 * associated with the scheduler are freed.2460	 */2461	ret = sof_ipc3_free_widgets_in_list(sdev, false, &dyn_widgets, verify);2462	if (ret < 0)2463		return ret;2464 2465	/* free all the scheduler widgets now */2466	ret = sof_ipc3_free_widgets_in_list(sdev, true, &dyn_widgets, verify);2467	if (ret < 0)2468		return ret;2469 2470	/*2471	 * Tear down all pipelines associated with PCMs that did not get suspended2472	 * and unset the prepare flag so that they can be set up again during resume.2473	 * Skip this step for older firmware unless topology has any2474	 * dynamic pipeline (in which case the step is mandatory).2475	 */2476	if (!verify && (dyn_widgets || SOF_FW_VER(v->major, v->minor, v->micro) >=2477	    SOF_FW_VER(2, 2, 0))) {2478		ret = sof_tear_down_left_over_pipelines(sdev);2479		if (ret < 0) {2480			dev_err(sdev->dev, "failed to tear down paused pipelines\n");2481			return ret;2482		}2483	}2484 2485	list_for_each_entry(sroute, &sdev->route_list, list)2486		sroute->setup = false;2487 2488	/*2489	 * before suspending, make sure the refcounts are all zeroed out. There's no way2490	 * to recover at this point but this will help root cause bad sequences leading to2491	 * more issues on resume2492	 */2493	list_for_each_entry(swidget, &sdev->widget_list, list) {2494		if (swidget->use_count != 0) {2495			dev_err(sdev->dev, "%s: widget %s is still in use: count %d\n",2496				__func__, swidget->widget->name, swidget->use_count);2497		}2498	}2499 2500	return 0;2501}2502 2503static int sof_ipc3_dai_get_param(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int param_type)2504{2505	struct sof_dai_private_data *private = dai->private;2506 2507	if (!private || !private->dai_config)2508		return 0;2509 2510	switch (private->dai_config->type) {2511	case SOF_DAI_INTEL_SSP:2512		switch (param_type) {2513		case SOF_DAI_PARAM_INTEL_SSP_MCLK:2514			return private->dai_config->ssp.mclk_rate;2515		case SOF_DAI_PARAM_INTEL_SSP_BCLK:2516			return private->dai_config->ssp.bclk_rate;2517		case SOF_DAI_PARAM_INTEL_SSP_TDM_SLOTS:2518			return private->dai_config->ssp.tdm_slots;2519		default:2520			dev_err(sdev->dev, "invalid SSP param %d\n", param_type);2521			break;2522		}2523		break;2524	default:2525		/* not yet implemented for platforms other than the above */2526		dev_err(sdev->dev, "DAI type %d not supported yet!\n", private->dai_config->type);2527		break;2528	}2529 2530	return -EINVAL;2531}2532 2533static int sof_ipc3_parse_manifest(struct snd_soc_component *scomp, int index,2534				   struct snd_soc_tplg_manifest *man)2535{2536	u32 size = le32_to_cpu(man->priv.size);2537	u32 abi_version;2538 2539	/* backward compatible with tplg without ABI info */2540	if (!size) {2541		dev_dbg(scomp->dev, "No topology ABI info\n");2542		return 0;2543	}2544 2545	if (size != SOF_IPC3_TPLG_ABI_SIZE) {2546		dev_err(scomp->dev, "%s: Invalid topology ABI size: %u\n",2547			__func__, size);2548		return -EINVAL;2549	}2550 2551	dev_info(scomp->dev,2552		 "Topology: ABI %d:%d:%d Kernel ABI %d:%d:%d\n",2553		 man->priv.data[0], man->priv.data[1], man->priv.data[2],2554		 SOF_ABI_MAJOR, SOF_ABI_MINOR, SOF_ABI_PATCH);2555 2556	abi_version = SOF_ABI_VER(man->priv.data[0], man->priv.data[1], man->priv.data[2]);2557 2558	if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, abi_version)) {2559		dev_err(scomp->dev, "%s: Incompatible topology ABI version\n", __func__);2560		return -EINVAL;2561	}2562 2563	if (IS_ENABLED(CONFIG_SND_SOC_SOF_STRICT_ABI_CHECKS) &&2564	    SOF_ABI_VERSION_MINOR(abi_version) > SOF_ABI_MINOR) {2565		dev_err(scomp->dev, "%s: Topology ABI is more recent than kernel\n", __func__);2566		return -EINVAL;2567	}2568 2569	return 0;2570}2571 2572static int sof_ipc3_link_setup(struct snd_sof_dev *sdev, struct snd_soc_dai_link *link)2573{2574	if (link->no_pcm)2575		return 0;2576 2577	/*2578	 * set default trigger order for all links. Exceptions to2579	 * the rule will be handled in sof_pcm_dai_link_fixup()2580	 * For playback, the sequence is the following: start FE,2581	 * start BE, stop BE, stop FE; for Capture the sequence is2582	 * inverted start BE, start FE, stop FE, stop BE2583	 */2584	link->trigger[SNDRV_PCM_STREAM_PLAYBACK] = SND_SOC_DPCM_TRIGGER_PRE;2585	link->trigger[SNDRV_PCM_STREAM_CAPTURE] = SND_SOC_DPCM_TRIGGER_POST;2586 2587	return 0;2588}2589 2590/* token list for each topology object */2591static enum sof_tokens host_token_list[] = {2592	SOF_CORE_TOKENS,2593	SOF_COMP_EXT_TOKENS,2594	SOF_PCM_TOKENS,2595	SOF_COMP_TOKENS,2596};2597 2598static enum sof_tokens comp_generic_token_list[] = {2599	SOF_CORE_TOKENS,2600	SOF_COMP_EXT_TOKENS,2601	SOF_COMP_TOKENS,2602};2603 2604static enum sof_tokens buffer_token_list[] = {2605	SOF_BUFFER_TOKENS,2606};2607 2608static enum sof_tokens pipeline_token_list[] = {2609	SOF_CORE_TOKENS,2610	SOF_COMP_EXT_TOKENS,2611	SOF_PIPELINE_TOKENS,2612	SOF_SCHED_TOKENS,2613};2614 2615static enum sof_tokens asrc_token_list[] = {2616	SOF_CORE_TOKENS,2617	SOF_COMP_EXT_TOKENS,2618	SOF_ASRC_TOKENS,2619	SOF_COMP_TOKENS,2620};2621 2622static enum sof_tokens src_token_list[] = {2623	SOF_CORE_TOKENS,2624	SOF_COMP_EXT_TOKENS,2625	SOF_SRC_TOKENS,2626	SOF_COMP_TOKENS2627};2628 2629static enum sof_tokens pga_token_list[] = {2630	SOF_CORE_TOKENS,2631	SOF_COMP_EXT_TOKENS,2632	SOF_VOLUME_TOKENS,2633	SOF_COMP_TOKENS,2634};2635 2636static enum sof_tokens dai_token_list[] = {2637	SOF_CORE_TOKENS,2638	SOF_COMP_EXT_TOKENS,2639	SOF_DAI_TOKENS,2640	SOF_COMP_TOKENS,2641};2642 2643static enum sof_tokens process_token_list[] = {2644	SOF_CORE_TOKENS,2645	SOF_COMP_EXT_TOKENS,2646	SOF_PROCESS_TOKENS,2647	SOF_COMP_TOKENS,2648};2649 2650static const struct sof_ipc_tplg_widget_ops tplg_ipc3_widget_ops[SND_SOC_DAPM_TYPE_COUNT] = {2651	[snd_soc_dapm_aif_in] =  {sof_ipc3_widget_setup_comp_host, sof_ipc3_widget_free_comp,2652				  host_token_list, ARRAY_SIZE(host_token_list), NULL},2653	[snd_soc_dapm_aif_out] = {sof_ipc3_widget_setup_comp_host, sof_ipc3_widget_free_comp,2654				  host_token_list, ARRAY_SIZE(host_token_list), NULL},2655 2656	[snd_soc_dapm_dai_in] = {sof_ipc3_widget_setup_comp_dai, sof_ipc3_widget_free_comp_dai,2657				 dai_token_list, ARRAY_SIZE(dai_token_list), NULL},2658	[snd_soc_dapm_dai_out] = {sof_ipc3_widget_setup_comp_dai, sof_ipc3_widget_free_comp_dai,2659				  dai_token_list, ARRAY_SIZE(dai_token_list), NULL},2660	[snd_soc_dapm_buffer] = {sof_ipc3_widget_setup_comp_buffer, sof_ipc3_widget_free_comp,2661				 buffer_token_list, ARRAY_SIZE(buffer_token_list), NULL},2662	[snd_soc_dapm_mixer] = {sof_ipc3_widget_setup_comp_mixer, sof_ipc3_widget_free_comp,2663				comp_generic_token_list, ARRAY_SIZE(comp_generic_token_list),2664				NULL},2665	[snd_soc_dapm_src] = {sof_ipc3_widget_setup_comp_src, sof_ipc3_widget_free_comp,2666			      src_token_list, ARRAY_SIZE(src_token_list), NULL},2667	[snd_soc_dapm_asrc] = {sof_ipc3_widget_setup_comp_asrc, sof_ipc3_widget_free_comp,2668			       asrc_token_list, ARRAY_SIZE(asrc_token_list), NULL},2669	[snd_soc_dapm_siggen] = {sof_ipc3_widget_setup_comp_tone, sof_ipc3_widget_free_comp,2670				 comp_generic_token_list, ARRAY_SIZE(comp_generic_token_list),2671				 NULL},2672	[snd_soc_dapm_scheduler] = {sof_ipc3_widget_setup_comp_pipeline, sof_ipc3_widget_free_comp,2673				    pipeline_token_list, ARRAY_SIZE(pipeline_token_list), NULL},2674	[snd_soc_dapm_pga] = {sof_ipc3_widget_setup_comp_pga, sof_ipc3_widget_free_comp,2675			      pga_token_list, ARRAY_SIZE(pga_token_list), NULL},2676	[snd_soc_dapm_mux] = {sof_ipc3_widget_setup_comp_mux, sof_ipc3_widget_free_comp,2677			      comp_generic_token_list, ARRAY_SIZE(comp_generic_token_list), NULL},2678	[snd_soc_dapm_demux] = {sof_ipc3_widget_setup_comp_mux, sof_ipc3_widget_free_comp,2679				 comp_generic_token_list, ARRAY_SIZE(comp_generic_token_list),2680				 NULL},2681	[snd_soc_dapm_effect] = {sof_widget_update_ipc_comp_process, sof_ipc3_widget_free_comp,2682				 process_token_list, ARRAY_SIZE(process_token_list),2683				 sof_ipc3_widget_bind_event},2684};2685 2686const struct sof_ipc_tplg_ops ipc3_tplg_ops = {2687	.widget = tplg_ipc3_widget_ops,2688	.control = &tplg_ipc3_control_ops,2689	.route_setup = sof_ipc3_route_setup,2690	.control_setup = sof_ipc3_control_setup,2691	.control_free = sof_ipc3_control_free,2692	.pipeline_complete = sof_ipc3_complete_pipeline,2693	.token_list = ipc3_token_list,2694	.widget_free = sof_ipc3_widget_free,2695	.widget_setup = sof_ipc3_widget_setup,2696	.dai_config = sof_ipc3_dai_config,2697	.dai_get_param = sof_ipc3_dai_get_param,2698	.set_up_all_pipelines = sof_ipc3_set_up_all_pipelines,2699	.tear_down_all_pipelines = sof_ipc3_tear_down_all_pipelines,2700	.parse_manifest = sof_ipc3_parse_manifest,2701	.link_setup = sof_ipc3_link_setup,2702};2703