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