diff options
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/sof/Makefile | 3 | ||||
-rw-r--r-- | sound/soc/sof/control.c | 26 | ||||
-rw-r--r-- | sound/soc/sof/core.c | 1 | ||||
-rw-r--r-- | sound/soc/sof/ipc.c | 14 | ||||
-rw-r--r-- | sound/soc/sof/ipc3-topology.c | 2019 | ||||
-rw-r--r-- | sound/soc/sof/sof-audio.c | 66 | ||||
-rw-r--r-- | sound/soc/sof/sof-audio.h | 135 | ||||
-rw-r--r-- | sound/soc/sof/sof-priv.h | 16 | ||||
-rw-r--r-- | sound/soc/sof/topology.c | 2648 |
9 files changed, 2735 insertions, 2193 deletions
diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index a0459f06c68a..e13dab59764c 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -1,7 +1,8 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\ - control.o trace.o iomem-utils.o sof-audio.o stream-ipc.o + control.o trace.o iomem-utils.o sof-audio.o stream-ipc.o\ + ipc3-topology.o ifneq ($(CONFIG_SND_SOC_SOF_CLIENT),) snd-sof-objs += sof-client.o endif diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index ef61936dad59..21ee0545945d 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -67,7 +67,7 @@ static inline u32 ipc_to_mixer(u32 value, u32 *volume_map, int size) static void snd_sof_refresh_control(struct snd_sof_control *scontrol) { - struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; struct snd_soc_component *scomp = scontrol->scomp; int ret; @@ -97,7 +97,7 @@ int snd_sof_volume_get(struct snd_kcontrol *kcontrol, struct soc_mixer_control *sm = (struct soc_mixer_control *)kcontrol->private_value; struct snd_sof_control *scontrol = sm->dobj.private; - struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; unsigned int i, channels = scontrol->num_channels; snd_sof_refresh_control(scontrol); @@ -118,7 +118,7 @@ int snd_sof_volume_put(struct snd_kcontrol *kcontrol, (struct soc_mixer_control *)kcontrol->private_value; struct snd_sof_control *scontrol = sm->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; unsigned int i, channels = scontrol->num_channels; bool change = false; u32 value; @@ -166,7 +166,7 @@ int snd_sof_switch_get(struct snd_kcontrol *kcontrol, struct soc_mixer_control *sm = (struct soc_mixer_control *)kcontrol->private_value; struct snd_sof_control *scontrol = sm->dobj.private; - struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; unsigned int i, channels = scontrol->num_channels; snd_sof_refresh_control(scontrol); @@ -185,7 +185,7 @@ int snd_sof_switch_put(struct snd_kcontrol *kcontrol, (struct soc_mixer_control *)kcontrol->private_value; struct snd_sof_control *scontrol = sm->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; unsigned int i, channels = scontrol->num_channels; bool change = false; u32 value; @@ -214,7 +214,7 @@ int snd_sof_enum_get(struct snd_kcontrol *kcontrol, struct soc_enum *se = (struct soc_enum *)kcontrol->private_value; struct snd_sof_control *scontrol = se->dobj.private; - struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; unsigned int i, channels = scontrol->num_channels; snd_sof_refresh_control(scontrol); @@ -233,7 +233,7 @@ int snd_sof_enum_put(struct snd_kcontrol *kcontrol, (struct soc_enum *)kcontrol->private_value; struct snd_sof_control *scontrol = se->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; unsigned int i, channels = scontrol->num_channels; bool change = false; u32 value; @@ -260,7 +260,7 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; struct sof_abi_hdr *data = cdata->data; size_t size; @@ -296,7 +296,7 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; struct sof_abi_hdr *data = cdata->data; size_t size; @@ -335,7 +335,7 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; struct snd_ctl_tlv header; const struct snd_ctl_tlv __user *tlvd = (const struct snd_ctl_tlv __user *)binary_data; @@ -409,7 +409,7 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _ struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; struct snd_ctl_tlv header; struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data; size_t data_size; @@ -482,7 +482,7 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; struct snd_ctl_tlv header; struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data; @@ -534,7 +534,7 @@ static void snd_sof_update_control(struct snd_sof_control *scontrol, struct sof_ipc_ctrl_data *local_cdata; int i; - local_cdata = scontrol->control_data; + local_cdata = scontrol->ipc_control_data; if (cdata->cmd == SOF_CTRL_CMD_BINARY) { if (cdata->num_elems != local_cdata->data->size) { diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 9217644e2eab..e91631618bff 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -370,6 +370,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) INIT_LIST_HEAD(&sdev->kcontrol_list); INIT_LIST_HEAD(&sdev->widget_list); INIT_LIST_HEAD(&sdev->dai_list); + INIT_LIST_HEAD(&sdev->dai_link_list); INIT_LIST_HEAD(&sdev->route_list); INIT_LIST_HEAD(&sdev->ipc_client_list); INIT_LIST_HEAD(&sdev->ipc_rx_handler_list); diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 34084e0008f1..19a294cbbb8d 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -812,7 +812,7 @@ static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev, int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, bool set) { struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_ipc_fw_ready *ready = &sdev->fw_ready; struct sof_ipc_fw_version *v = &ready->version; @@ -1023,6 +1023,18 @@ struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev) init_waitqueue_head(&msg->waitq); + /* + * Use IPC3 ops as it is the only available version now. With the addition of new IPC + * versions, this will need to be modified to use the selected version at runtime. + */ + ipc->ops = &ipc3_ops; + + /* check for mandatory ops */ + if (!ipc->ops->tplg || !ipc->ops->tplg->widget) { + dev_err(sdev->dev, "Invalid topology IPC ops\n"); + return NULL; + } + return ipc; } EXPORT_SYMBOL(snd_sof_ipc_init); diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c new file mode 100644 index 000000000000..fe1d5a56080a --- /dev/null +++ b/sound/soc/sof/ipc3-topology.c @@ -0,0 +1,2019 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2021 Intel Corporation. All rights reserved. +// +// + +#include <uapi/sound/sof/tokens.h> +#include <sound/pcm_params.h> +#include "sof-priv.h" +#include "sof-audio.h" +#include "ops.h" + +/* Full volume for default values */ +#define VOL_ZERO_DB BIT(VOLUME_FWL) + +struct sof_widget_data { + int ctrl_type; + int ipc_cmd; + struct sof_abi_hdr *pdata; + struct snd_sof_control *control; +}; + +struct sof_process_types { + const char *name; + enum sof_ipc_process_type type; + enum sof_comp_type comp_type; +}; + +static const struct sof_process_types sof_process[] = { + {"EQFIR", SOF_PROCESS_EQFIR, SOF_COMP_EQ_FIR}, + {"EQIIR", SOF_PROCESS_EQIIR, SOF_COMP_EQ_IIR}, + {"KEYWORD_DETECT", SOF_PROCESS_KEYWORD_DETECT, SOF_COMP_KEYWORD_DETECT}, + {"KPB", SOF_PROCESS_KPB, SOF_COMP_KPB}, + {"CHAN_SELECTOR", SOF_PROCESS_CHAN_SELECTOR, SOF_COMP_SELECTOR}, + {"MUX", SOF_PROCESS_MUX, SOF_COMP_MUX}, + {"DEMUX", SOF_PROCESS_DEMUX, SOF_COMP_DEMUX}, + {"DCBLOCK", SOF_PROCESS_DCBLOCK, SOF_COMP_DCBLOCK}, + {"SMART_AMP", SOF_PROCESS_SMART_AMP, SOF_COMP_SMART_AMP}, +}; + +static enum sof_ipc_process_type find_process(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sof_process); i++) { + if (strcmp(name, sof_process[i].name) == 0) + return sof_process[i].type; + } + + return SOF_PROCESS_NONE; +} + +static int get_token_process_type(void *elem, void *object, u32 offset) +{ + u32 *val = (u32 *)((u8 *)object + offset); + + *val = find_process((const char *)elem); + return 0; +} + +/* Buffers */ +static const struct sof_topology_token buffer_tokens[] = { + {SOF_TKN_BUF_SIZE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_buffer, size)}, + {SOF_TKN_BUF_CAPS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_buffer, caps)}, +}; + +/* DAI */ +static const struct sof_topology_token dai_tokens[] = { + {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, + offsetof(struct sof_ipc_comp_dai, type)}, + {SOF_TKN_DAI_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_dai, dai_index)}, + {SOF_TKN_DAI_DIRECTION, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_dai, direction)}, +}; + +/* BE DAI link */ +static const struct sof_topology_token dai_link_tokens[] = { + {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, + offsetof(struct sof_ipc_dai_config, type)}, + {SOF_TKN_DAI_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_config, dai_index)}, +}; + +/* scheduling */ +static const struct sof_topology_token sched_tokens[] = { + {SOF_TKN_SCHED_PERIOD, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_pipe_new, period)}, + {SOF_TKN_SCHED_PRIORITY, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_pipe_new, priority)}, + {SOF_TKN_SCHED_MIPS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_pipe_new, period_mips)}, + {SOF_TKN_SCHED_CORE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_pipe_new, core)}, + {SOF_TKN_SCHED_FRAMES, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_pipe_new, frames_per_sched)}, + {SOF_TKN_SCHED_TIME_DOMAIN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_pipe_new, time_domain)}, +}; + +static const struct sof_topology_token pipeline_tokens[] = { + {SOF_TKN_SCHED_DYNAMIC_PIPELINE, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, + offsetof(struct snd_sof_widget, dynamic_pipeline_widget)}, + +}; + +/* volume */ +static const struct sof_topology_token volume_tokens[] = { + {SOF_TKN_VOLUME_RAMP_STEP_TYPE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_volume, ramp)}, + {SOF_TKN_VOLUME_RAMP_STEP_MS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_volume, initial_ramp)}, +}; + +/* SRC */ +static const struct sof_topology_token src_tokens[] = { + {SOF_TKN_SRC_RATE_IN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_src, source_rate)}, + {SOF_TKN_SRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_src, sink_rate)}, +}; + +/* ASRC */ +static const struct sof_topology_token asrc_tokens[] = { + {SOF_TKN_ASRC_RATE_IN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_asrc, source_rate)}, + {SOF_TKN_ASRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_asrc, sink_rate)}, + {SOF_TKN_ASRC_ASYNCHRONOUS_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_asrc, asynchronous_mode)}, + {SOF_TKN_ASRC_OPERATION_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_asrc, operation_mode)}, +}; + +/* EFFECT */ +static const struct sof_topology_token process_tokens[] = { + {SOF_TKN_PROCESS_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_process_type, + offsetof(struct sof_ipc_comp_process, type)}, +}; + +/* PCM */ +static const struct sof_topology_token pcm_tokens[] = { + {SOF_TKN_PCM_DMAC_CONFIG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_host, dmac_config)}, +}; + +/* Generic components */ +static const struct sof_topology_token comp_tokens[] = { + {SOF_TKN_COMP_PERIOD_SINK_COUNT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_config, periods_sink)}, + {SOF_TKN_COMP_PERIOD_SOURCE_COUNT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp_config, periods_source)}, + {SOF_TKN_COMP_FORMAT, + SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_format, + offsetof(struct sof_ipc_comp_config, frame_fmt)}, +}; + +/* SSP */ +static const struct sof_topology_token ssp_tokens[] = { + {SOF_TKN_INTEL_SSP_CLKS_CONTROL, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_ssp_params, clks_control)}, + {SOF_TKN_INTEL_SSP_MCLK_ID, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_ssp_params, mclk_id)}, + {SOF_TKN_INTEL_SSP_SAMPLE_BITS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_ssp_params, sample_valid_bits)}, + {SOF_TKN_INTEL_SSP_FRAME_PULSE_WIDTH, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_ssp_params, frame_pulse_width)}, + {SOF_TKN_INTEL_SSP_QUIRKS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_ssp_params, quirks)}, + {SOF_TKN_INTEL_SSP_TDM_PADDING_PER_SLOT, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, + offsetof(struct sof_ipc_dai_ssp_params, tdm_per_slot_padding_flag)}, + {SOF_TKN_INTEL_SSP_BCLK_DELAY, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_ssp_params, bclk_delay)}, +}; + +/* ALH */ +static const struct sof_topology_token alh_tokens[] = { + {SOF_TKN_INTEL_ALH_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_alh_params, rate)}, + {SOF_TKN_INTEL_ALH_CH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_alh_params, channels)}, +}; + +/* DMIC */ +static const struct sof_topology_token dmic_tokens[] = { + {SOF_TKN_INTEL_DMIC_DRIVER_VERSION, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_dmic_params, driver_ipc_version)}, + {SOF_TKN_INTEL_DMIC_CLK_MIN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_dmic_params, pdmclk_min)}, + {SOF_TKN_INTEL_DMIC_CLK_MAX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_dmic_params, pdmclk_max)}, + {SOF_TKN_INTEL_DMIC_SAMPLE_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_dmic_params, fifo_fs)}, + {SOF_TKN_INTEL_DMIC_DUTY_MIN, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_params, duty_min)}, + {SOF_TKN_INTEL_DMIC_DUTY_MAX, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_params, duty_max)}, + {SOF_TKN_INTEL_DMIC_NUM_PDM_ACTIVE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_dmic_params, num_pdm_active)}, + {SOF_TKN_INTEL_DMIC_FIFO_WORD_LENGTH, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_params, fifo_bits)}, + {SOF_TKN_INTEL_DMIC_UNMUTE_RAMP_TIME_MS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_dmic_params, unmute_ramp_time)}, +}; + +/* ESAI */ +static const struct sof_topology_token esai_tokens[] = { + {SOF_TKN_IMX_ESAI_MCLK_ID, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_esai_params, mclk_id)}, +}; + +/* SAI */ +static const struct sof_topology_token sai_tokens[] = { + {SOF_TKN_IMX_SAI_MCLK_ID, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_sai_params, mclk_id)}, +}; + +/* + * DMIC PDM Tokens + * SOF_TKN_INTEL_DMIC_PDM_CTRL_ID should be the first token + * as it increments the index while parsing the array of pdm tokens + * and determines the correct offset + */ +static const struct sof_topology_token dmic_pdm_tokens[] = { + {SOF_TKN_INTEL_DMIC_PDM_CTRL_ID, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, id)}, + {SOF_TKN_INTEL_DMIC_PDM_MIC_A_Enable, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, enable_mic_a)}, + {SOF_TKN_INTEL_DMIC_PDM_MIC_B_Enable, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, enable_mic_b)}, + {SOF_TKN_INTEL_DMIC_PDM_POLARITY_A, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, polarity_mic_a)}, + {SOF_TKN_INTEL_DMIC_PDM_POLARITY_B, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, polarity_mic_b)}, + {SOF_TKN_INTEL_DMIC_PDM_CLK_EDGE, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, clk_edge)}, + {SOF_TKN_INTEL_DMIC_PDM_SKEW, SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, + offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, skew)}, +}; + +/* HDA */ +static const struct sof_topology_token hda_tokens[] = { + {SOF_TKN_INTEL_HDA_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_hda_params, rate)}, + {SOF_TKN_INTEL_HDA_CH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_hda_params, channels)}, +}; + +/* AFE */ +static const struct sof_topology_token afe_tokens[] = { + {SOF_TKN_MEDIATEK_AFE_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_mtk_afe_params, rate)}, + {SOF_TKN_MEDIATEK_AFE_CH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_mtk_afe_params, channels)}, + {SOF_TKN_MEDIATEK_AFE_FORMAT, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_format, + offsetof(struct sof_ipc_dai_mtk_afe_params, format)}, +}; + +/* Core tokens */ +static const struct sof_topology_token core_tokens[] = { + {SOF_TKN_COMP_CORE_ID, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp, core)}, +}; + +/* Component extended tokens */ +static const struct sof_topology_token comp_ext_tokens[] = { + {SOF_TKN_COMP_UUID, SND_SOC_TPLG_TUPLE_TYPE_UUID, get_token_uuid, + offsetof(struct snd_sof_widget, uuid)}, +}; + +static const struct sof_token_info ipc3_token_list[SOF_TOKEN_COUNT] = { + [SOF_PCM_TOKENS] = {"PCM tokens", pcm_tokens, ARRAY_SIZE(pcm_tokens)}, + [SOF_PIPELINE_TOKENS] = {"Pipeline tokens", pipeline_tokens, ARRAY_SIZE(pipeline_tokens)}, + [SOF_SCHED_TOKENS] = {"Scheduler tokens", sched_tokens, ARRAY_SIZE(sched_tokens)}, + [SOF_COMP_TOKENS] = {"Comp tokens", comp_tokens, ARRAY_SIZE(comp_tokens)}, + [SOF_CORE_TOKENS] = {"Core tokens", core_tokens, ARRAY_SIZE(core_tokens)}, + [SOF_COMP_EXT_TOKENS] = {"AFE tokens", comp_ext_tokens, ARRAY_SIZE(comp_ext_tokens)}, + [SOF_BUFFER_TOKENS] = {"Buffer tokens", buffer_tokens, ARRAY_SIZE(buffer_tokens)}, + [SOF_VOLUME_TOKENS] = {"Volume tokens", volume_tokens, ARRAY_SIZE(volume_tokens)}, + [SOF_SRC_TOKENS] = {"SRC tokens", src_tokens, ARRAY_SIZE(src_tokens)}, + [SOF_ASRC_TOKENS] = {"ASRC tokens", asrc_tokens, ARRAY_SIZE(asrc_tokens)}, + [SOF_PROCESS_TOKENS] = {"Process tokens", process_tokens, ARRAY_SIZE(process_tokens)}, + [SOF_DAI_TOKENS] = {"DAI tokens", dai_tokens, ARRAY_SIZE(dai_tokens)}, + [SOF_DAI_LINK_TOKENS] = {"DAI link tokens", dai_link_tokens, ARRAY_SIZE(dai_link_tokens)}, + [SOF_HDA_TOKENS] = {"HDA tokens", hda_tokens, ARRAY_SIZE(hda_tokens)}, + [SOF_SSP_TOKENS] = {"SSP tokens", ssp_tokens, ARRAY_SIZE(ssp_tokens)}, + [SOF_ALH_TOKENS] = {"ALH tokens", alh_tokens, ARRAY_SIZE(alh_tokens)}, + [SOF_DMIC_TOKENS] = {"DMIC tokens", dmic_tokens, ARRAY_SIZE(dmic_tokens)}, + [SOF_DMIC_PDM_TOKENS] = {"DMIC PDM tokens", dmic_pdm_tokens, ARRAY_SIZE(dmic_pdm_tokens)}, + [SOF_ESAI_TOKENS] = {"ESAI tokens", esai_tokens, ARRAY_SIZE(esai_tokens)}, + [SOF_SAI_TOKENS] = {"SAI tokens", sai_tokens, ARRAY_SIZE(sai_tokens)}, + [SOF_AFE_TOKENS] = {"AFE tokens", afe_tokens, ARRAY_SIZE(afe_tokens)}, +}; + +/** + * sof_comp_alloc - allocate and initialize buffer for a new component + * @swidget: pointer to struct snd_sof_widget containing extended data + * @ipc_size: IPC payload size that will be updated depending on valid + * extended data. + * @index: ID of the pipeline the component belongs to + * + * Return: The pointer to the new allocated component, NULL if failed. + */ +static void *sof_comp_alloc(struct snd_sof_widget *swidget, size_t *ipc_size, + int index) +{ + struct sof_ipc_comp *comp; + size_t total_size = *ipc_size; + size_t ext_size = sizeof(swidget->uuid); + + /* only non-zero UUID is valid */ + if (!guid_is_null(&swidget->uuid)) + total_size += ext_size; + + comp = kzalloc(total_size, GFP_KERNEL); + if (!comp) + return NULL; + + /* configure comp new IPC message */ + comp->hdr.size = total_size; + comp->hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; + comp->id = swidget->comp_id; + comp->pipeline_id = index; + comp->core = swidget->core; + + /* handle the extended data if needed */ + if (total_size > *ipc_size) { + /* append extended data to the end of the component */ + memcpy((u8 *)comp + *ipc_size, &swidget->uuid, ext_size); + comp->ext_data_length = ext_size; + } + + /* update ipc_size and return */ + *ipc_size = total_size; + return comp; +} + +static void sof_dbg_comp_config(struct snd_soc_component *scomp, struct sof_ipc_comp_config *config) +{ + dev_dbg(scomp->dev, " config: periods snk %d src %d fmt %d\n", + config->periods_sink, config->periods_source, + config->frame_fmt); +} + +static int sof_ipc3_widget_setup_comp_host(struct snd_sof_widget *swidget) +{ + struct snd_soc_component *scomp = swidget->scomp; + struct sof_ipc_comp_host *host; + size_t ipc_size = sizeof(*host); + int ret; + + host = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id); + if (!host) + return -ENOMEM; + swidget->private = host; + + /* configure host comp IPC message */ + host->comp.type = SOF_COMP_HOST; + host->config.hdr.size = sizeof(host->config); + + if (swidget->id == snd_soc_dapm_aif_out) + host->direction = SOF_IPC_STREAM_CAPTURE; + else + host->direction = SOF_IPC_STREAM_PLAYBACK; + + /* parse one set of pcm_tokens */ + ret = sof_update_ipc_object(scomp, host, SOF_PCM_TOKENS, swidget->tuples, + swidget->num_tuples, sizeof(*host), 1); + if (ret < 0) + goto err; + + /* parse one set of comp_tokens */ + ret = sof_update_ipc_object(scomp, &host->config, SOF_COMP_TOKENS, swidget->tuples, + swidget->num_tuples, sizeof(host->config), 1); + if (ret < 0) + goto err; + + dev_dbg(scomp->dev, "loaded host %s\n", swidget->widget->name); + sof_dbg_comp_config(scomp, &host->config); + + return 0; +err: + kfree(swidget->private); + swidget->private = NULL; + + return ret; +} + +static void sof_ipc3_widget_free_comp(struct snd_sof_widget *swidget) +{ + kfree(swidget->private); +} + +static int sof_ipc3_widget_setup_comp_tone(struct snd_sof_widget *swidget) +{ + struct snd_soc_component *scomp = swidget->scomp; + struct sof_ipc_comp_tone *tone; + size_t ipc_size = sizeof(*tone); + int ret; + + tone = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id); + if (!tone) + return -ENOMEM; + + swidget->private = tone; + + /* configure siggen IPC message */ + tone->comp.type = SOF_COMP_TONE; + tone->config.hdr.size = sizeof(tone->config); + + /* parse one set of comp tokens */ + ret = sof_update_ipc_object(scomp, &tone->config, SOF_COMP_TOKENS, swidget->tuples, + swidget->num_tuples, sizeof(tone->config), 1); + if (ret < 0) { + kfree(swidget->private); + swidget->private = NULL; + return ret; + } + + dev_dbg(scomp->dev, "tone %s: frequency %d amplitude %d\n", + swidget->widget->name, tone->frequency, tone->amplitude); + sof_dbg_comp_config(scomp, &tone->config); + + return 0; +} + +static int sof_ipc3_widget_setup_comp_mixer(struct snd_sof_widget *swidget) +{ + struct snd_soc_component *scomp = swidget->scomp; + struct sof_ipc_comp_mixer *mixer; + size_t ipc_size = sizeof(*mixer); + int ret; + + mixer = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id); + if (!mixer) + return -ENOMEM; + + swidget->private = mixer; + + /* configure mixer IPC message */ + mixer->comp.type = SOF_COMP_MIXER; + mixer->config.hdr.size = sizeof(mixer->config); + + /* parse one set of comp tokens */ + ret = sof_update_ipc_object(scomp, &mixer->config, SOF_COMP_TOKENS, + swidget->tuples, swidget->num_tuples, + sizeof(mixer->config), 1); + if (ret < 0) { + kfree(swidget->private); + swidget->private = NULL; + + return ret; + } + + dev_dbg(scomp->dev, "loaded mixer %s\n", swidget->widget->name); + sof_dbg_comp_config(scomp, &mixer->config); + + return 0; +} + +static int sof_ipc3_widget_setup_comp_pipeline(struct snd_sof_widget *swidget) +{ + struct snd_soc_component *scomp = swidget->scomp; + struct sof_ipc_pipe_new *pipeline; + struct snd_sof_widget *comp_swidget; + int ret; + + pipeline = kzalloc(sizeof(*pipeline), GFP_KERNEL); + if (!pipeline) + return -ENOMEM; + + /* configure pipeline IPC message */ + pipeline->hdr.size = sizeof(*pipeline); + pipeline->hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_PIPE_NEW; + pipeline->pipeline_id = swidget->pipeline_id; + pipeline->comp_id = swidget->comp_id; + + swidget->private = pipeline; + + /* component at start of pipeline is our stream id */ + comp_swidget = snd_sof_find_swidget(scomp, swidget->widget->sname); + if (!comp_swidget) { + dev_err(scomp->dev, "scheduler %s refers to non existent widget %s\n", + swidget->widget->name, swidget->widget->sname); + ret = -EINVAL; + goto err; + } + + pipeline->sched_id = comp_swidget->comp_id; + + /* parse one set of scheduler tokens */ + ret = sof_update_ipc_object(scomp, pipeline, SOF_SCHED_TOKENS, swidget->tuples, + swidget->num_tuples, sizeof(*pipeline), 1); + if (ret < 0) + goto err; + + /* parse one set of pipeline tokens */ + ret = sof_update_ipc_object(scomp, swidget, SOF_PIPELINE_TOKENS, swidget->tuples, + swidget->num_tuples, sizeof(*swidget), 1); + if (ret < 0) + goto err; + + if (sof_debug_check_flag(SOF_DBG_DISABLE_MULTICORE)) + pipeline->core = SOF_DSP_PRIMARY_CORE; + + if (sof_debug_check_flag(SOF_DBG_DYNAMIC_PIPELINES_OVERRIDE)) + swidget->dynamic_pipeline_widget = + sof_debug_check_flag(SOF_DBG_DYNAMIC_PIPELINES_ENABLE); + + dev_dbg(scomp->dev, "pipeline %s: period %d pri %d mips %d core %d frames %d dynamic %d\n", + swidget->widget->name, pipeline->period, pipeline->priority, + pipeline->period_mips, pipeline->core, pipeline->frames_per_sched, + swidget->dynamic_pipeline_widget); + + swidget->core = pipeline->core; + + return 0; + +err: + kfree(swidget->private); + swidget->private = NULL; + + return ret; +} + +static int sof_ipc3_widget_setup_comp_buffer(struct snd_sof_widget *swidget) +{ + struct snd_soc_component *scomp = swidget->scomp; + struct sof_ipc_buffer *buffer; + int ret; + + buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + swidget->private = buffer; + + /* configure dai IPC message */ + buffer->comp.hdr.size = sizeof(*buffer); + buffer->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_BUFFER_NEW; + buffer->comp.id = swidget->comp_id; + buffer->comp.type = SOF_COMP_BUFFER; + buffer->comp.pipeline_id = swidget->pipeline_id; + buffer->comp.core = swidget->core; + + /* parse one set of buffer tokens */ + ret = sof_update_ipc_object(scomp, buffer, SOF_BUFFER_TOKENS, swidget->tuples, + swidget->num_tuples, sizeof(*buffer), 1); + if (ret < 0) { + kfree(swidget->private); + swidget->private = NULL; + return ret; + } + + dev_dbg(scomp->dev, "buffer %s: size %d caps 0x%x\n", + swidget->widget->name, buffer->size, buffer->caps); + + return 0; +} + +static int sof_ipc3_widget_setup_comp_src(struct snd_sof_widget *swidget) +{ + struct snd_soc_component *scomp = swidget->scomp; + struct sof_ipc_comp_src *src; + size_t ipc_size = sizeof(*src); + int ret; + + src = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id); + if (!src) + return -ENOMEM; + + swidget->private = src; + + /* configure src IPC message */ + src->comp.type = SOF_COMP_SRC; + src->config.hdr.size = sizeof(src->config); + + /* parse one set of src tokens */ + ret = sof_update_ipc_object(scomp, src, SOF_SRC_TOKENS, swidget->tuples, + swidget->num_tuples, sizeof(*src), 1); + if (ret < 0) + goto err; + + /* parse one set of comp tokens */ + ret = sof_update_ipc_object(scomp, &src->config, SOF_COMP_TOKENS, + swidget->tuples, swidget->num_tuples, sizeof(src->config), 1); + if (ret < 0) + goto err; + + dev_dbg(scomp->dev, "src %s: source rate %d sink rate %d\n", + swidget->widget->name, src->source_rate, src->sink_rate); + sof_dbg_comp_config(scomp, &src->config); + + return 0; +err: + kfree(swidget->private); + swidget->private = NULL; + + return ret; +} + +static int sof_ipc3_widget_setup_comp_asrc(struct snd_sof_widget *swidget) +{ + struct snd_soc_component *scomp = swidget->scomp; + struct sof_ipc_comp_asrc *asrc; + size_t ipc_size = sizeof(*asrc); + int ret; + + asrc = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id); + if (!asrc) + return -ENOMEM; + + swidget->private = asrc; + + /* configure ASRC IPC message */ + asrc->comp.type = SOF_COMP_ASRC; + asrc->config.hdr.size = sizeof(asrc->config); + + /* parse one set of asrc tokens */ + ret = sof_update_ipc_object(scomp, asrc, SOF_ASRC_TOKENS, swidget->tuples, + swidget->num_tuples, sizeof(*asrc), 1); + if (ret < 0) + goto err; + + /* parse one set of comp tokens */ + ret = sof_update_ipc_object(scomp, &asrc->config, SOF_COMP_TOKENS, + swidget->tuples, swidget->num_tuples, sizeof(asrc->config), 1); + if (ret < 0) + goto err; + + dev_dbg(scomp->dev, "asrc %s: source rate %d sink rate %d asynch %d operation %d\n", + swidget->widget->name, asrc->source_rate, asrc->sink_rate, + asrc->asynchronous_mode, asrc->operation_mode); + + sof_dbg_comp_config(scomp, &asrc->config); + + return 0; +err: + kfree(swidget->private); + swidget->private = NULL; + + return ret; +} + +/* + * Mux topology + */ +static int sof_ipc3_widget_setup_comp_mux(struct snd_sof_widget *swidget) +{ + struct snd_soc_component *scomp = swidget->scomp; + struct sof_ipc_comp_mux *mux; + size_t ipc_size = sizeof(*mux); + int ret; + + mux = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id); + if (!mux) + return -ENOMEM; + + swidget->private = mux; + + /* configure mux IPC message */ + mux->comp.type = SOF_COMP_MUX; + mux->config.hdr.size = sizeof(mux->config); + + /* parse one set of comp tokens */ + ret = sof_update_ipc_object(scomp, &mux->config, SOF_COMP_TOKENS, + swidget->tuples, swidget->num_tuples, sizeof(mux->config), 1); + if (ret < 0) { + kfree(swidget->private); + swidget->private = NULL; + return ret; + } + + dev_dbg(scomp->dev, "loaded mux %s\n", swidget->widget->name); + sof_dbg_comp_config(scomp, &mux->config); + + return 0; +} + +/* + * PGA Topology + */ + +static int sof_ipc3_widget_setup_comp_pga(struct snd_sof_widget *swidget) +{ + struct snd_soc_component *scomp = swidget->scomp; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct sof_ipc_comp_volume *volume; + struct snd_sof_control *scontrol; + size_t ipc_size = sizeof(*volume); + int min_step, max_step; + int ret; + + volume = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id); + if (!volume) + return -ENOMEM; + + swidget->private = volume; + + /* configure volume IPC message */ + volume->comp.type = SOF_COMP_VOLUME; + volume->config.hdr.size = sizeof(volume->config); + + /* parse one set of volume tokens */ + ret = sof_update_ipc_object(scomp, volume, SOF_VOLUME_TOKENS, swidget->tuples, + swidget->num_tuples, sizeof(*volume), 1); + if (ret < 0) + goto err; + + /* parse one set of comp tokens */ + ret = sof_update_ipc_object(scomp, &volume->config, SOF_COMP_TOKENS, + swidget->tuples, swidget->num_tuples, + sizeof(volume->config), 1); + if (ret < 0) + goto err; + + dev_dbg(scomp->dev, "loaded PGA %s\n", swidget->widget->name); + sof_dbg_comp_config(scomp, &volume->config); + + list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { + if (scontrol->comp_id == swidget->comp_id && + scontrol->volume_table) { + min_step = scontrol->min_volume_step; + max_step = scontrol->max_volume_step; + volume->min_value = scontrol->volume_table[min_step]; + volume->max_value = scontrol->volume_table[max_step]; + volume->channels = scontrol->num_channels; + break; + } + } + + return 0; +err: + kfree(swidget->private); + swidget->private = NULL; + + return ret; +} + +static int sof_get_control_data(struct snd_soc_component *scomp, + struct snd_soc_dapm_widget *widget, + struct sof_widget_data *wdata, size_t *size) +{ + const struct snd_kcontrol_new *kc; + struct sof_ipc_ctrl_data *cdata; + struct soc_mixer_control *sm; + struct soc_bytes_ext *sbe; + struct soc_enum *se; + int i; + + *size = 0; + + for (i = 0; i < widget->num_kcontrols; i++) { + kc = &widget->kcontrol_news[i]; + + switch (widget->dobj.widget.kcontrol_type[i]) { + case SND_SOC_TPLG_TYPE_MIXER: + sm = (struct soc_mixer_control *)kc->private_value; + wdata[i].control = sm->dobj.private; + break; + case SND_SOC_TPLG_TYPE_BYTES: + sbe = (struct soc_bytes_ext *)kc->private_value; + wdata[i].control = sbe->dobj.private; + break; + case SND_SOC_TPLG_TYPE_ENUM: + se = (struct soc_enum *)kc->private_value; + wdata[i].control = se->dobj.private; + break; + default: + dev_err(scomp->dev, "Unknown kcontrol type %u in widget %s\n", + widget->dobj.widget.kcontrol_type[i], widget->name); + return -EINVAL; + } + + if (!wdata[i].control) { + dev_err(scomp->dev, "No scontrol for widget %s\n", widget->name); + return -EINVAL; + } + + cdata = wdata[i].control->ipc_control_data; + wdata[i].pdata = cdata->data; + if (!wdata[i].pdata) + return -EINVAL; + + /* make sure data is valid - data can be updated at runtime */ + if (widget->dobj.widget.kcontrol_type[i] == SND_SOC_TPLG_TYPE_BYTES && + wdata[i].pdata->magic != SOF_ABI_MAGIC) + return -EINVAL; + + *size += wdata[i].pdata->size; + + /* get data type */ + switch (cdata->cmd) { + case SOF_CTRL_CMD_VOLUME: + case SOF_CTRL_CMD_ENUM: + case SOF_CTRL_CMD_SWITCH: + wdata[i].ipc_cmd = SOF_IPC_COMP_SET_VALUE; + wdata[i].ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_SET; + break; + case SOF_CTRL_CMD_BINARY: + wdata[i].ipc_cmd = SOF_IPC_COMP_SET_DATA; + wdata[i].ctrl_type = SOF_CTRL_TYPE_DATA_SET; + break; + default: + break; + } + } + + return 0; +} + +static int sof_process_load(struct snd_soc_component *scomp, + struct snd_sof_widget *swidget, int type) +{ + struct snd_soc_dapm_widget *widget = swidget->widget; + struct sof_ipc_comp_process *process; + struct sof_widget_data *wdata = NULL; + size_t ipc_data_size = 0; + size_t ipc_size; + int offset = 0; + int ret; + int i; + + /* allocate struct for widget control data sizes and types */ + if (widget->num_kcontrols) { + wdata = kcalloc(widget->num_kcontrols, sizeof(*wdata), GFP_KERNEL); + if (!wdata) + return -ENOMEM; + + /* get possible component controls and get size of all pdata */ + ret = sof_get_control_data(scomp, widget, wdata, &ipc_data_size); + if (ret < 0) + goto out; + } + + ipc_size = sizeof(struct sof_ipc_comp_process) + ipc_data_size; + + /* we are exceeding max ipc size, config needs to be sent separately */ + if (ipc_size > SOF_IPC_MSG_MAX_SIZE) { + ipc_size -= ipc_data_size; + ipc_data_size = 0; + } + + process = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id); + if (!process) { + ret = -ENOMEM; + goto out; + } + + swidget->private = process; + + /* configure iir IPC message */ + process->comp.type = type; + process->config.hdr.size = sizeof(process->config); + + /* parse one set of comp tokens */ + ret = sof_update_ipc_object(scomp, &process->config, SOF_COMP_TOKENS, + swidget->tuples, swidget->num_tuples, + sizeof(process->config), 1); + if (ret < 0) + goto err; + + dev_dbg(scomp->dev, "loaded process %s\n", swidget->widget->name); + sof_dbg_comp_config(scomp, &process->config); + + /* + * found private data in control, so copy it. + * get possible component controls - get size of all pdata, + * then memcpy with headers + */ + if (ipc_data_size) { + for (i = 0; i < widget->num_kcontrols; i++) { + memcpy(&process->data[offset], + wdata[i].pdata->data, + wdata[i].pdata->size); + offset += wdata[i].pdata->size; + } + } + + process->size = ipc_data_size; + + kfree(wdata); + + return 0; +err: + kfree(swidget->private); + swidget->private = NULL; +out: + kfree(wdata); + return ret; +} + +static enum sof_comp_type find_process_comp_type(enum sof_ipc_process_type type) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sof_process); i++) { + if (sof_process[i].type == type) + return sof_process[i].comp_type; + } + + return SOF_COMP_NONE; +} + +/* + * Processing Component Topology - can be "effect", "codec", or general + * "processing". + */ + +static int sof_widget_update_ipc_comp_process(struct snd_sof_widget *swidget) +{ + struct snd_soc_component *scomp = swidget->scomp; + struct sof_ipc_comp_process config; + int ret; + + memset(&config, 0, sizeof(config)); + config.comp.core = swidget->core; + + /* parse one set of process tokens */ + ret = sof_update_ipc_object(scomp, &config, SOF_PROCESS_TOKENS, swidget->tuples, + swidget->num_tuples, sizeof(config), 1); + if (ret < 0) + return ret; + + /* now load process specific data and send IPC */ + return sof_process_load(scomp, swidget, find_process_comp_type(config.type)); +} + +static int sof_link_hda_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, + struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) +{ + struct sof_dai_private_data *private = dai->private; + u32 size = sizeof(*config); + int ret; + + /* init IPC */ + memset(&config->hda, 0, sizeof(config->hda)); + config->hdr.size = size; + + /* parse one set of HDA tokens */ + ret = sof_update_ipc_object(scomp, &config->hda, SOF_HDA_TOKENS, slink->tuples, + slink->num_tuples, size, 1); + if (ret < 0) + return ret; + + dev_dbg(scomp->dev, "HDA config rate %d channels %d\n", + config->hda.rate, config->hda.channels); + + config->hda.link_dma_ch = DMA_CHAN_INVALID; + + dai->number_configs = 1; + dai->current_config = 0; + private->dai_config = kmemdup(config, size, GFP_KERNEL); + if (!private->dai_config) + return -ENOMEM; + + return 0; +} + +static void sof_dai_set_format(struct snd_soc_tplg_hw_config *hw_config, + struct sof_ipc_dai_config *config) +{ + /* clock directions wrt codec */ + config->format &= ~SOF_DAI_FMT_CLOCK_PROVIDER_MASK; + if (hw_config->bclk_provider == SND_SOC_TPLG_BCLK_CP) { + /* codec is bclk provider */ + if (hw_config->fsync_provider == SND_SOC_TPLG_FSYNC_CP) + config->format |= SOF_DAI_FMT_CBP_CFP; + else + config->format |= SOF_DAI_FMT_CBP_CFC; + } else { + /* codec is bclk consumer */ + if (hw_config->fsync_provider == SND_SOC_TPLG_FSYNC_CP) + config->format |= SOF_DAI_FMT_CBC_CFP; + else + config->format |= SOF_DAI_FMT_CBC_CFC; + } + + /* inverted clocks ? */ + config->format &= ~SOF_DAI_FMT_INV_MASK; + if (hw_config->invert_bclk) { + if (hw_config->invert_fsync) + config->format |= SOF_DAI_FMT_IB_IF; + else + config->format |= SOF_DAI_FMT_IB_NF; + } else { + if (hw_config->invert_fsync) + config->format |= SOF_DAI_FMT_NB_IF; + else + config->format |= SOF_DAI_FMT_NB_NF; + } +} + +static int sof_link_sai_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, + struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) +{ + struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs; + struct sof_dai_private_data *private = dai->private; + u32 size = sizeof(*config); + int ret; + + /* handle master/slave and inverted clocks */ + sof_dai_set_format(hw_config, config); + + /* init IPC */ + memset(&config->sai, 0, sizeof(config->sai)); + config->hdr.size = size; + + /* parse one set of SAI tokens */ + ret = sof_update_ipc_object(scomp, &config->sai, SOF_SAI_TOKENS, slink->tuples, + slink->num_tuples, size, 1); + if (ret < 0) + return ret; + + config->sai.mclk_rate = le32_to_cpu(hw_config->mclk_rate); + config->sai.bclk_rate = le32_to_cpu(hw_config->bclk_rate); + config->sai.fsync_rate = le32_to_cpu(hw_config->fsync_rate); + config->sai.mclk_direction = hw_config->mclk_direction; + + config->sai.tdm_slots = le32_to_cpu(hw_config->tdm_slots); + config->sai.tdm_slot_width = le32_to_cpu(hw_config->tdm_slot_width); + config->sai.rx_slots = le32_to_cpu(hw_config->rx_slots); + config->sai.tx_slots = le32_to_cpu(hw_config->tx_slots); + + dev_info(scomp->dev, + "tplg: config SAI%d fmt 0x%x mclk %d width %d slots %d mclk id %d\n", + config->dai_index, config->format, + config->sai.mclk_rate, config->sai.tdm_slot_width, + config->sai.tdm_slots, config->sai.mclk_id); + + if (config->sai.tdm_slots < 1 || config->sai.tdm_slots > 8) { + dev_err(scomp->dev, "Invalid channel count for SAI%d\n", config->dai_index); + return -EINVAL; + } + + dai->number_configs = 1; + dai->current_config = 0; + private->dai_config = kmemdup(config, size, GFP_KERNEL); + if (!private->dai_config) + return -ENOMEM; + + return 0; +} + +static int sof_link_esai_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, + struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) +{ + struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs; + struct sof_dai_private_data *private = dai->private; + u32 size = sizeof(*config); + int ret; + + /* handle master/slave and inverted clocks */ + sof_dai_set_format(hw_config, config); + + /* init IPC */ + memset(&config->esai, 0, sizeof(config->esai)); + config->hdr.size = size; + + /* parse one set of ESAI tokens */ + ret = sof_update_ipc_object(scomp, &config->esai, SOF_ESAI_TOKENS, slink->tuples, + slink->num_tuples, size, 1); + if (ret < 0) + return ret; + + config->esai.mclk_rate = le32_to_cpu(hw_config->mclk_rate); + config->esai.bclk_rate = le32_to_cpu(hw_config->bclk_rate); + config->esai.fsync_rate = le32_to_cpu(hw_config->fsync_rate); + config->esai.mclk_direction = hw_config->mclk_direction; + config->esai.tdm_slots = le32_to_cpu(hw_config->tdm_slots); + config->esai.tdm_slot_width = le32_to_cpu(hw_config->tdm_slot_width); + config->esai.rx_slots = le32_to_cpu(hw_config->rx_slots); + config->esai.tx_slots = le32_to_cpu(hw_config->tx_slots); + + dev_info(scomp->dev, + "tplg: config ESAI%d fmt 0x%x mclk %d width %d slots %d mclk id %d\n", + config->dai_index, config->format, + config->esai.mclk_rate, config->esai.tdm_slot_width, + config->esai.tdm_slots, config->esai.mclk_id); + + if (config->esai.tdm_slots < 1 || config->esai.tdm_slots > 8) { + dev_err(scomp->dev, "Invalid channel count for ESAI%d\n", config->dai_index); + return -EINVAL; + } + + dai->number_configs = 1; + dai->current_config = 0; + private->dai_config = kmemdup(config, size, GFP_KERNEL); + if (!private->dai_config) + return -ENOMEM; + + return 0; +} + +static int sof_link_acp_dmic_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, + struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) +{ + struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs; + struct sof_dai_private_data *private = dai->private; + u32 size = sizeof(*config); + + /* handle master/slave and inverted clocks */ + sof_dai_set_format(hw_config, config); + + /* init IPC */ + memset(&config->acpdmic, 0, sizeof(config->acpdmic)); + config->hdr.size = size; + + config->acpdmic.fsync_rate = le32_to_cpu(hw_config->fsync_rate); + config->acpdmic.tdm_slots = le32_to_cpu(hw_config->tdm_slots); + + dev_info(scomp->dev, "ACP_DMIC config ACP%d channel %d rate %d\n", + config->dai_index, config->acpdmic.tdm_slots, + config->acpdmic.fsync_rate); + + dai->number_configs = 1; + dai->current_config = 0; + private->dai_config = kmemdup(config, size, GFP_KERNEL); + if (!private->dai_config) + return -ENOMEM; + + return 0; +} + +static int sof_link_acp_bt_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, + struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) +{ + struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs; + struct sof_dai_private_data *private = dai->private; + u32 size = sizeof(*config); + + /* handle master/slave and inverted clocks */ + sof_dai_set_format(hw_config, config); + + /* init IPC */ + memset(&config->acpbt, 0, sizeof(config->acpbt)); + config->hdr.size = size; + + config->acpbt.fsync_rate = le32_to_cpu(hw_config->fsync_rate); + config->acpbt.tdm_slots = le32_to_cpu(hw_config->tdm_slots); + + dev_info(scomp->dev, "ACP_BT config ACP%d channel %d rate %d\n", + config->dai_index, config->acpbt.tdm_slots, + config->acpbt.fsync_rate); + + dai->number_configs = 1; + dai->current_config = 0; + private->dai_config = kmemdup(config, size, GFP_KERNEL); + if (!private->dai_config) + return -ENOMEM; + + return 0; +} + +static int sof_link_acp_sp_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, + struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) +{ + struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs; + struct sof_dai_private_data *private = dai->private; + u32 size = sizeof(*config); + + /* handle master/slave and inverted clocks */ + sof_dai_set_format(hw_config, config); + + /* init IPC */ + memset(&config->acpsp, 0, sizeof(config->acpsp)); + config->hdr.size = size; + + config->acpsp.fsync_rate = le32_to_cpu(hw_config->fsync_rate); + config->acpsp.tdm_slots = le32_to_cpu(hw_config->tdm_slots); + + dev_info(scomp->dev, "ACP_SP config ACP%d channel %d rate %d\n", + config->dai_index, config->acpsp.tdm_slots, + config->acpsp.fsync_rate); + + dai->number_configs = 1; + dai->current_config = 0; + private->dai_config = kmemdup(config, size, GFP_KERNEL); + if (!private->dai_config) + return -ENOMEM; + + return 0; +} + +static int sof_link_afe_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, + struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) +{ + struct sof_dai_private_data *private = dai->private; + u32 size = sizeof(*config); + int ret; + + config->hdr.size = size; + + /* parse the required set of AFE tokens based on num_hw_cfgs */ + ret = sof_update_ipc_object(scomp, &config->afe, SOF_AFE_TOKENS, slink->tuples, + slink->num_tuples, size, slink->num_hw_configs); + if (ret < 0) + return ret; + + dev_dbg(scomp->dev, "AFE config rate %d channels %d format:%d\n", + config->afe.rate, config->afe.channels, config->afe.format); + + config->afe.stream_id = DMA_CHAN_INVALID; + + dai->number_configs = 1; + dai->current_config = 0; + private->dai_config = kmemdup(config, size, GFP_KERNEL); + if (!private->dai_config) + return -ENOMEM; + + return 0; +} + +static int sof_link_ssp_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, + struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) +{ + struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs; + struct sof_dai_private_data *private = dai->private; + u32 size = sizeof(*config); + int current_config = 0; + int i, ret; + + /* + * Parse common data, we should have 1 common data per hw_config. + */ + ret = sof_update_ipc_object(scomp, &config->ssp, SOF_SSP_TOKENS, slink->tuples, + slink->num_tuples, size, slink->num_hw_configs); + if (ret < 0) + return ret; + + /* process all possible hw configs */ + for (i = 0; i < slink->num_hw_configs; i++) { + if (le32_to_cpu(hw_config[i].id) == slink->default_hw_cfg_id) + current_config = i; + + /* handle master/slave and inverted clocks */ + sof_dai_set_format(&hw_config[i], &config[i]); + + config[i].hdr.size = size; + + /* copy differentiating hw configs to ipc structs */ + config[i].ssp.mclk_rate = le32_to_cpu(hw_config[i].mclk_rate); + config[i].ssp.bclk_rate = le32_to_cpu(hw_config[i].bclk_rate); + config[i].ssp.fsync_rate = le32_to_cpu(hw_config[i].fsync_rate); + config[i].ssp.tdm_slots = le32_to_cpu(hw_config[i].tdm_slots); + config[i].ssp.tdm_slot_width = le32_to_cpu(hw_config[i].tdm_slot_width); + config[i].ssp.mclk_direction = hw_config[i].mclk_direction; + config[i].ssp.rx_slots = le32_to_cpu(hw_config[i].rx_slots); + config[i].ssp.tx_slots = le32_to_cpu(hw_config[i].tx_slots); + + 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", + config[i].dai_index, config[i].format, + config[i].ssp.mclk_rate, config[i].ssp.bclk_rate, + config[i].ssp.fsync_rate, config[i].ssp.sample_valid_bits, + config[i].ssp.tdm_slot_width, config[i].ssp.tdm_slots, + config[i].ssp.mclk_id, config[i].ssp.quirks, config[i].ssp.clks_control); + + /* validate SSP fsync rate and channel count */ + if (config[i].ssp.fsync_rate < 8000 || config[i].ssp.fsync_rate > 192000) { + dev_err(scomp->dev, "Invalid fsync rate for SSP%d\n", config[i].dai_index); + return -EINVAL; + } + + if (config[i].ssp.tdm_slots < 1 || config[i].ssp.tdm_slots > 8) { + dev_err(scomp->dev, "Invalid channel count for SSP%d\n", + config[i].dai_index); + return -EINVAL; + } + } + + dai->number_configs = slink->num_hw_configs; + dai->current_config = current_config; + private->dai_config = kmemdup(config, size * slink->num_hw_configs, GFP_KERNEL); + if (!private->dai_config) + return -ENOMEM; + + return 0; +} + +static int sof_link_dmic_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, + struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct sof_dai_private_data *private = dai->private; + struct sof_ipc_fw_ready *ready = &sdev->fw_ready; + struct sof_ipc_fw_version *v = &ready->version; + size_t size = sizeof(*config); + int i, ret; + + /* Ensure the entire DMIC config struct is zeros */ + memset(&config->dmic, 0, sizeof(config->dmic)); + + /* parse the required set of DMIC tokens based on num_hw_cfgs */ + ret = sof_update_ipc_object(scomp, &config->dmic, SOF_DMIC_TOKENS, slink->tuples, + slink->num_tuples, size, slink->num_hw_configs); + if (ret < 0) + return ret; + + /* parse the required set of DMIC PDM tokens based on number of active PDM's */ + ret = sof_update_ipc_object(scomp, &config->dmic.pdm[0], SOF_DMIC_PDM_TOKENS, + slink->tuples, slink->num_tuples, + sizeof(struct sof_ipc_dai_dmic_pdm_ctrl), + config->dmic.num_pdm_active); + if (ret < 0) + return ret; + + /* set IPC header size */ + config->hdr.size = size; + + /* debug messages */ + dev_dbg(scomp->dev, "tplg: config DMIC%d driver version %d\n", + config->dai_index, config->dmic.driver_ipc_version); + dev_dbg(scomp->dev, "pdmclk_min %d pdm_clkmax %d duty_min %d\n", + config->dmic.pdmclk_min, config->dmic.pdmclk_max, + config->dmic.duty_min); + dev_dbg(scomp->dev, "duty_max %d fifo_fs %d num_pdms active %d\n", + config->dmic.duty_max, config->dmic.fifo_fs, + config->dmic.num_pdm_active); + dev_dbg(scomp->dev, "fifo word length %d\n", config->dmic.fifo_bits); + + for (i = 0; i < config->dmic.num_pdm_active; i++) { + dev_dbg(scomp->dev, "pdm %d mic a %d mic b %d\n", + config->dmic.pdm[i].id, + config->dmic.pdm[i].enable_mic_a, + config->dmic.pdm[i].enable_mic_b); + dev_dbg(scomp->dev, "pdm %d polarity a %d polarity b %d\n", + config->dmic.pdm[i].id, + config->dmic.pdm[i].polarity_mic_a, + config->dmic.pdm[i].polarity_mic_b); + dev_dbg(scomp->dev, "pdm %d clk_edge %d skew %d\n", + config->dmic.pdm[i].id, + config->dmic.pdm[i].clk_edge, + config->dmic.pdm[i].skew); + } + + /* + * this takes care of backwards compatible handling of fifo_bits_b. + * It is deprecated since firmware ABI version 3.0.1. + */ + if (SOF_ABI_VER(v->major, v->minor, v->micro) < SOF_ABI_VER(3, 0, 1)) + config->dmic.fifo_bits_b = config->dmic.fifo_bits; + + dai->number_configs = 1; + dai->current_config = 0; + private->dai_config = kmemdup(config, size, GFP_KERNEL); + if (!private->dai_config) + return -ENOMEM; + + return 0; +} + +static int sof_link_alh_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, + struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) +{ + struct sof_dai_private_data *private = dai->private; + u32 size = sizeof(*config); + int ret; + + /* parse the required set of ALH tokens based on num_hw_cfgs */ + ret = sof_update_ipc_object(scomp, &config->alh, SOF_ALH_TOKENS, slink->tuples, + slink->num_tuples, size, slink->num_hw_configs); + if (ret < 0) + return ret; + + /* init IPC */ + config->hdr.size = size; + + /* set config for all DAI's with name matching the link name */ + dai->number_configs = 1; + dai->current_config = 0; + private->dai_config = kmemdup(config, size, GFP_KERNEL); + if (!private->dai_config) + return -ENOMEM; + + return 0; +} + +static int sof_ipc3_widget_setup_comp_dai(struct snd_sof_widget *swidget) +{ + struct snd_soc_component *scomp = swidget->scomp; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dai *dai = swidget->private; + struct sof_dai_private_data *private; + struct sof_ipc_comp_dai *comp_dai; + size_t ipc_size = sizeof(*comp_dai); + struct sof_ipc_dai_config *config; + struct snd_sof_dai_link *slink; + int ret; + + private = kzalloc(sizeof(*private), GFP_KERNEL); + if (!private) + return -ENOMEM; + + dai->private = private; + + private->comp_dai = sof_comp_alloc(swidget, &ipc_size, swidget->pipeline_id); + if (!private->comp_dai) { + ret = -ENOMEM; + goto free; + } + + /* configure dai IPC message */ + comp_dai = private->comp_dai; + comp_dai->comp.type = SOF_COMP_DAI; + comp_dai->config.hdr.size = sizeof(comp_dai->config); + + /* parse one set of DAI tokens */ + ret = sof_update_ipc_object(scomp, comp_dai, SOF_DAI_TOKENS, swidget->tuples, + swidget->num_tuples, sizeof(*comp_dai), 1); + if (ret < 0) + goto free; + + /* update comp_tokens */ + ret = sof_update_ipc_object(scomp, &comp_dai->config, SOF_COMP_TOKENS, + swidget->tuples, swidget->num_tuples, + sizeof(comp_dai->config), 1); + if (ret < 0) + goto free; + + dev_dbg(scomp->dev, "%s dai %s: type %d index %d\n", + __func__, swidget->widget->name, comp_dai->type, comp_dai->dai_index); + sof_dbg_comp_config(scomp, &comp_dai->config); + + /* now update DAI config */ + list_for_each_entry(slink, &sdev->dai_link_list, list) { + struct sof_ipc_dai_config common_config; + int i; + + if (strcmp(slink->link->name, dai->name)) + continue; + + /* Reserve memory for all hw configs, eventually freed by widget */ + config = kcalloc(slink->num_hw_configs, sizeof(*config), GFP_KERNEL); + if (!config) { + ret = -ENOMEM; + goto free_comp; + } + + /* parse one set of DAI link tokens */ + ret = sof_update_ipc_object(scomp, &common_config, SOF_DAI_LINK_TOKENS, + slink->tuples, slink->num_tuples, + sizeof(common_config), 1); + if (ret < 0) + goto free_config; + + for (i = 0; i < slink->num_hw_configs; i++) { + config[i].hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG; + config[i].format = le32_to_cpu(slink->hw_configs[i].fmt); + config[i].type = common_config.type; + config[i].dai_index = comp_dai->dai_index; + } + + switch (common_config.type) { + case SOF_DAI_INTEL_SSP: + ret = sof_link_ssp_load(scomp, slink, config, dai); + break; + case SOF_DAI_INTEL_DMIC: + ret = sof_link_dmic_load(scomp, slink, config, dai); + break; + case SOF_DAI_INTEL_HDA: + ret = sof_link_hda_load(scomp, slink, config, dai); + break; + case SOF_DAI_INTEL_ALH: + ret = sof_link_alh_load(scomp, slink, config, dai); + break; + case SOF_DAI_IMX_SAI: + ret = sof_link_sai_load(scomp, slink, config, dai); + break; + case SOF_DAI_IMX_ESAI: + ret = sof_link_esai_load(scomp, slink, config, dai); + break; + case SOF_DAI_AMD_BT: + ret = sof_link_acp_bt_load(scomp, slink, config, dai); + break; + case SOF_DAI_AMD_SP: + ret = sof_link_acp_sp_load(scomp, slink, config, dai); + break; + case SOF_DAI_AMD_DMIC: + ret = sof_link_acp_dmic_load(scomp, slink, config, dai); + break; + case SOF_DAI_MEDIATEK_AFE: + ret = sof_link_afe_load(scomp, slink, config, dai); + break; + default: + break; + } + if (ret < 0) { + dev_err(scomp->dev, "failed to load config for dai %s\n", dai->name); + goto free_config; + } + + kfree(config); + } + + return 0; +free_config: + kfree(config); +free_comp: + kfree(comp_dai); +free: + kfree(private); + dai->private = NULL; + return ret; +} + +static void sof_ipc3_widget_free_comp_dai(struct snd_sof_widget *swidget) +{ + switch (swidget->id) { + case snd_soc_dapm_dai_in: + case snd_soc_dapm_dai_out: + { + struct snd_sof_dai *dai = swidget->private; + struct sof_dai_private_data *dai_data; + + if (!dai) + return; + + dai_data = dai->private; + if (dai_data) { + kfree(dai_data->comp_dai); + kfree(dai_data->dai_config); + kfree(dai_data); + } + kfree(dai); + break; + } + default: + break; + } +} + +static int sof_ipc3_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *sroute) +{ + struct sof_ipc_pipe_comp_connect connect; + struct sof_ipc_reply reply; + int ret; + + connect.hdr.size = sizeof(connect); + connect.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_CONNECT; + connect.source_id = sroute->src_widget->comp_id; + connect.sink_id = sroute->sink_widget->comp_id; + + dev_dbg(sdev->dev, "setting up route %s -> %s\n", + sroute->src_widget->widget->name, + sroute->sink_widget->widget->name); + + /* send ipc */ + ret = sof_ipc_tx_message(sdev->ipc, connect.hdr.cmd, &connect, sizeof(connect), + &reply, sizeof(reply)); + if (ret < 0) + dev_err(sdev->dev, "%s: route %s -> %s failed\n", __func__, + sroute->src_widget->widget->name, sroute->sink_widget->widget->name); + + return ret; +} + +static int sof_ipc3_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol) +{ + struct sof_ipc_ctrl_data *cdata; + int ret; + + scontrol->ipc_control_data = kzalloc(scontrol->max_size, GFP_KERNEL); + if (!scontrol->ipc_control_data) + return -ENOMEM; + + if (scontrol->max_size < sizeof(*cdata) || + scontrol->max_size < sizeof(struct sof_abi_hdr)) { + ret = -EINVAL; + goto err; + } + + /* init the get/put bytes data */ + if (scontrol->priv_size > scontrol->max_size - sizeof(*cdata)) { + dev_err(sdev->dev, "err: bytes data size %zu exceeds max %zu.\n", + scontrol->priv_size, scontrol->max_size - sizeof(*cdata)); + ret = -EINVAL; + goto err; + } + + scontrol->size = sizeof(struct sof_ipc_ctrl_data) + scontrol->priv_size; + + cdata = scontrol->ipc_control_data; + cdata->cmd = SOF_CTRL_CMD_BINARY; + cdata->index = scontrol->index; + + if (scontrol->priv_size > 0) { + memcpy(cdata->data, scontrol->priv, scontrol->priv_size); + kfree(scontrol->priv); + + if (cdata->data->magic != SOF_ABI_MAGIC) { + dev_err(sdev->dev, "Wrong ABI magic 0x%08x.\n", cdata->data->magic); + ret = -EINVAL; + goto err; + } + + if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) { + dev_err(sdev->dev, "Incompatible ABI version 0x%08x.\n", + cdata->data->abi); + ret = -EINVAL; + goto err; + } + + if (cdata->data->size + sizeof(struct sof_abi_hdr) != scontrol->priv_size) { + dev_err(sdev->dev, "Conflict in bytes vs. priv size.\n"); + ret = -EINVAL; + goto err; + } + } + + return 0; +err: + kfree(scontrol->ipc_control_data); + return ret; +} + +static int sof_ipc3_control_load_volume(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol) +{ + struct sof_ipc_ctrl_data *cdata; + int i; + + /* init the volume get/put data */ + scontrol->size = struct_size(cdata, chanv, scontrol->num_channels); + + scontrol->ipc_control_data = kzalloc(scontrol->size, GFP_KERNEL); + if (!scontrol->ipc_control_data) + return -ENOMEM; + + cdata = scontrol->ipc_control_data; + cdata->index = scontrol->index; + + /* set cmd for mixer control */ + if (scontrol->max == 1) { + cdata->cmd = SOF_CTRL_CMD_SWITCH; + return 0; + } + + cdata->cmd = SOF_CTRL_CMD_VOLUME; + + /* set default volume values to 0dB in control */ + for (i = 0; i < scontrol->num_channels; i++) { + cdata->chanv[i].channel = i; + cdata->chanv[i].value = VOL_ZERO_DB; + } + + return 0; +} + +static int sof_ipc3_control_load_enum(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol) +{ + struct sof_ipc_ctrl_data *cdata; + + /* init the enum get/put data */ + scontrol->size = struct_size(cdata, chanv, scontrol->num_channels); + + scontrol->ipc_control_data = kzalloc(scontrol->size, GFP_KERNEL); + if (!scontrol->ipc_control_data) + return -ENOMEM; + + cdata = scontrol->ipc_control_data; + cdata->index = scontrol->index; + cdata->cmd = SOF_CTRL_CMD_ENUM; + + return 0; +} + +static int sof_ipc3_control_setup(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol) +{ + switch (scontrol->info_type) { + case SND_SOC_TPLG_CTL_VOLSW: + case SND_SOC_TPLG_CTL_VOLSW_SX: + case SND_SOC_TPLG_CTL_VOLSW_XR_SX: + return sof_ipc3_control_load_volume(sdev, scontrol); + case SND_SOC_TPLG_CTL_BYTES: + return sof_ipc3_control_load_bytes(sdev, scontrol); + case SND_SOC_TPLG_CTL_ENUM: + case SND_SOC_TPLG_CTL_ENUM_VALUE: + return sof_ipc3_control_load_enum(sdev, scontrol); + default: + break; + } + + return 0; +} + +static int sof_ipc3_control_free(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol) +{ + struct sof_ipc_free fcomp; + + fcomp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_FREE; + fcomp.hdr.size = sizeof(fcomp); + fcomp.id = scontrol->comp_id; + + /* send IPC to the DSP */ + return sof_ipc_tx_message(sdev->ipc, fcomp.hdr.cmd, &fcomp, sizeof(fcomp), NULL, 0); +} + +/* send pcm params ipc */ +static int sof_ipc3_keyword_detect_pcm_params(struct snd_sof_widget *swidget, int dir) +{ + struct snd_soc_component *scomp = swidget->scomp; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct sof_ipc_pcm_params_reply ipc_params_reply; + struct snd_pcm_hw_params *params; + struct sof_ipc_pcm_params pcm; + struct snd_sof_pcm *spcm; + int ret; + + /* get runtime PCM params using widget's stream name */ + spcm = snd_sof_find_spcm_name(scomp, swidget->widget->sname); + if (!spcm) { + dev_err(scomp->dev, "Cannot find PCM for %s\n", swidget->widget->name); + return -EINVAL; + } + + params = &spcm->params[dir]; + + /* set IPC PCM params */ + memset(&pcm, 0, sizeof(pcm)); + pcm.hdr.size = sizeof(pcm); + pcm.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_PARAMS; + pcm.comp_id = swidget->comp_id; + pcm.params.hdr.size = sizeof(pcm.params); + pcm.params.direction = dir; + pcm.params.sample_valid_bytes = params_width(params) >> 3; + pcm.params.buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED; + pcm.params.rate = params_rate(params); + pcm.params.channels = params_channels(params); + pcm.params.host_period_bytes = params_period_bytes(params); + + /* set format */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16: + pcm.params.frame_fmt = SOF_IPC_FRAME_S16_LE; + break; + case SNDRV_PCM_FORMAT_S24: + pcm.params.frame_fmt = SOF_IPC_FRAME_S24_4LE; + break; + case SNDRV_PCM_FORMAT_S32: + pcm.params.frame_fmt = SOF_IPC_FRAME_S32_LE; + break; + default: + return -EINVAL; + } + + /* send IPC to the DSP */ + ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm), + &ipc_params_reply, sizeof(ipc_params_reply)); + if (ret < 0) + dev_err(scomp->dev, "%s: PCM params failed for %s\n", __func__, + swidget->widget->name); + + return ret; +} + + /* send stream trigger ipc */ +static int sof_ipc3_keyword_detect_trigger(struct snd_sof_widget *swidget, int cmd) +{ + struct snd_soc_component *scomp = swidget->scomp; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct sof_ipc_stream stream; + struct sof_ipc_reply reply; + int ret; + + /* set IPC stream params */ + stream.hdr.size = sizeof(stream); + stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | cmd; + stream.comp_id = swidget->comp_id; + + /* send IPC to the DSP */ + ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, + sizeof(stream), &reply, sizeof(reply)); + if (ret < 0) + dev_err(scomp->dev, "%s: Failed to trigger %s\n", __func__, swidget->widget->name); + + return ret; +} + +static int sof_ipc3_keyword_dapm_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_sof_widget *swidget = w->dobj.private; + struct snd_soc_component *scomp; + int stream = SNDRV_PCM_STREAM_CAPTURE; + struct snd_sof_pcm *spcm; + int ret = 0; + + if (!swidget) + return 0; + + scomp = swidget->scomp; + + dev_dbg(scomp->dev, "received event %d for widget %s\n", + event, w->name); + + /* get runtime PCM params using widget's stream name */ + spcm = snd_sof_find_spcm_name(scomp, swidget->widget->sname); + if (!spcm) { + dev_err(scomp->dev, "%s: Cannot find PCM for %s\n", __func__, + swidget->widget->name); + return -EINVAL; + } + + /* process events */ + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (spcm->stream[stream].suspend_ignored) { + dev_dbg(scomp->dev, "PRE_PMU event ignored, KWD pipeline is already RUNNING\n"); + return 0; + } + + /* set pcm params */ + ret = sof_ipc3_keyword_detect_pcm_params(swidget, stream); + if (ret < 0) { + dev_err(scomp->dev, "%s: Failed to set pcm params for widget %s\n", + __func__, swidget->widget->name); + break; + } + + /* start trigger */ + ret = sof_ipc3_keyword_detect_trigger(swidget, SOF_IPC_STREAM_TRIG_START); + if (ret < 0) + dev_err(scomp->dev, "%s: Failed to trigger widget %s\n", __func__, + swidget->widget->name); + break; + case SND_SOC_DAPM_POST_PMD: + if (spcm->stream[stream].suspend_ignored) { + dev_dbg(scomp->dev, + "POST_PMD event ignored, KWD pipeline will remain RUNNING\n"); + return 0; + } + + /* stop trigger */ + ret = sof_ipc3_keyword_detect_trigger(swidget, SOF_IPC_STREAM_TRIG_STOP); + if (ret < 0) + dev_err(scomp->dev, "%s: Failed to trigger widget %s\n", __func__, + swidget->widget->name); + + /* pcm free */ + ret = sof_ipc3_keyword_detect_trigger(swidget, SOF_IPC_STREAM_PCM_FREE); + if (ret < 0) + dev_err(scomp->dev, "%s: Failed to free PCM for widget %s\n", __func__, + swidget->widget->name); + break; + default: + break; + } + + return ret; +} + +/* event handlers for keyword detect component */ +static const struct snd_soc_tplg_widget_events sof_kwd_events[] = { + {SOF_KEYWORD_DETECT_DAPM_EVENT, sof_ipc3_keyword_dapm_event}, +}; + +static int sof_ipc3_widget_bind_event(struct snd_soc_component *scomp, + struct snd_sof_widget *swidget, u16 event_type) +{ + struct sof_ipc_comp *ipc_comp; + + /* validate widget event type */ + switch (event_type) { + case SOF_KEYWORD_DETECT_DAPM_EVENT: + /* only KEYWORD_DETECT comps should handle this */ + if (swidget->id != snd_soc_dapm_effect) + break; + + ipc_comp = swidget->private; + if (ipc_comp && ipc_comp->type != SOF_COMP_KEYWORD_DETECT) + break; + + /* bind event to keyword detect comp */ + return snd_soc_tplg_widget_bind_event(swidget->widget, sof_kwd_events, + ARRAY_SIZE(sof_kwd_events), event_type); + default: + break; + } + + dev_err(scomp->dev, "Invalid event type %d for widget %s\n", event_type, + swidget->widget->name); + + return -EINVAL; +} + +static int sof_ipc3_complete_pipeline(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) +{ + struct sof_ipc_pipe_ready ready; + struct sof_ipc_reply reply; + int ret; + + dev_dbg(sdev->dev, "tplg: complete pipeline %s id %d\n", + swidget->widget->name, swidget->comp_id); + + memset(&ready, 0, sizeof(ready)); + ready.hdr.size = sizeof(ready); + ready.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_PIPE_COMPLETE; + ready.comp_id = swidget->comp_id; + + ret = sof_ipc_tx_message(sdev->ipc, ready.hdr.cmd, &ready, sizeof(ready), &reply, + sizeof(reply)); + if (ret < 0) + return ret; + + return 1; +} + +/* token list for each topology object */ +static enum sof_tokens host_token_list[] = { + SOF_CORE_TOKENS, + SOF_COMP_EXT_TOKENS, + SOF_PCM_TOKENS, + SOF_COMP_TOKENS, +}; + +static enum sof_tokens comp_generic_token_list[] = { + SOF_CORE_TOKENS, + SOF_COMP_EXT_TOKENS, + SOF_COMP_TOKENS, +}; + +static enum sof_tokens buffer_token_list[] = { + SOF_BUFFER_TOKENS, +}; + +static enum sof_tokens pipeline_token_list[] = { + SOF_CORE_TOKENS, + SOF_COMP_EXT_TOKENS, + SOF_PIPELINE_TOKENS, + SOF_SCHED_TOKENS, +}; + +static enum sof_tokens asrc_token_list[] = { + SOF_CORE_TOKENS, + SOF_COMP_EXT_TOKENS, + SOF_ASRC_TOKENS, + SOF_COMP_TOKENS, +}; + +static enum sof_tokens src_token_list[] = { + SOF_CORE_TOKENS, + SOF_COMP_EXT_TOKENS, + SOF_SRC_TOKENS, + SOF_COMP_TOKENS +}; + +static enum sof_tokens pga_token_list[] = { + SOF_CORE_TOKENS, + SOF_COMP_EXT_TOKENS, + SOF_VOLUME_TOKENS, + SOF_COMP_TOKENS, +}; + +static enum sof_tokens dai_token_list[] = { + SOF_CORE_TOKENS, + SOF_COMP_EXT_TOKENS, + SOF_DAI_TOKENS, + SOF_COMP_TOKENS, +}; + +static enum sof_tokens process_token_list[] = { + SOF_CORE_TOKENS, + SOF_COMP_EXT_TOKENS, + SOF_PROCESS_TOKENS, + SOF_COMP_TOKENS, +}; + +static const struct sof_ipc_tplg_widget_ops tplg_ipc3_widget_ops[SND_SOC_DAPM_TYPE_COUNT] = { + [snd_soc_dapm_aif_in] = {sof_ipc3_widget_setup_comp_host, sof_ipc3_widget_free_comp, + host_token_list, ARRAY_SIZE(host_token_list), NULL}, + [snd_soc_dapm_aif_out] = {sof_ipc3_widget_setup_comp_host, sof_ipc3_widget_free_comp, + host_token_list, ARRAY_SIZE(host_token_list), NULL}, + + [snd_soc_dapm_dai_in] = {sof_ipc3_widget_setup_comp_dai, sof_ipc3_widget_free_comp_dai, + dai_token_list, ARRAY_SIZE(dai_token_list), NULL}, + [snd_soc_dapm_dai_out] = {sof_ipc3_widget_setup_comp_dai, sof_ipc3_widget_free_comp_dai, + dai_token_list, ARRAY_SIZE(dai_token_list), NULL}, + [snd_soc_dapm_buffer] = {sof_ipc3_widget_setup_comp_buffer, sof_ipc3_widget_free_comp, + buffer_token_list, ARRAY_SIZE(buffer_token_list), NULL}, + [snd_soc_dapm_mixer] = {sof_ipc3_widget_setup_comp_mixer, sof_ipc3_widget_free_comp, + comp_generic_token_list, ARRAY_SIZE(comp_generic_token_list), + NULL}, + [snd_soc_dapm_src] = {sof_ipc3_widget_setup_comp_src, sof_ipc3_widget_free_comp, + src_token_list, ARRAY_SIZE(src_token_list), NULL}, + [snd_soc_dapm_asrc] = {sof_ipc3_widget_setup_comp_asrc, sof_ipc3_widget_free_comp, + asrc_token_list, ARRAY_SIZE(asrc_token_list), NULL}, + [snd_soc_dapm_siggen] = {sof_ipc3_widget_setup_comp_tone, sof_ipc3_widget_free_comp, + comp_generic_token_list, ARRAY_SIZE(comp_generic_token_list), + NULL}, + [snd_soc_dapm_scheduler] = {sof_ipc3_widget_setup_comp_pipeline, sof_ipc3_widget_free_comp, + pipeline_token_list, ARRAY_SIZE(pipeline_token_list), NULL}, + [snd_soc_dapm_pga] = {sof_ipc3_widget_setup_comp_pga, sof_ipc3_widget_free_comp, + pga_token_list, ARRAY_SIZE(pga_token_list), NULL}, + [snd_soc_dapm_mux] = {sof_ipc3_widget_setup_comp_mux, sof_ipc3_widget_free_comp, + comp_generic_token_list, ARRAY_SIZE(comp_generic_token_list), NULL}, + [snd_soc_dapm_demux] = {sof_ipc3_widget_setup_comp_mux, sof_ipc3_widget_free_comp, + comp_generic_token_list, ARRAY_SIZE(comp_generic_token_list), + NULL}, + [snd_soc_dapm_effect] = {sof_widget_update_ipc_comp_process, sof_ipc3_widget_free_comp, + process_token_list, ARRAY_SIZE(process_token_list), + sof_ipc3_widget_bind_event}, +}; + +static const struct sof_ipc_tplg_ops ipc3_tplg_ops = { + .widget = tplg_ipc3_widget_ops, + .route_setup = sof_ipc3_route_setup, + .control_setup = sof_ipc3_control_setup, + .control_free = sof_ipc3_control_free, + .pipeline_complete = sof_ipc3_complete_pipeline, + .token_list = ipc3_token_list, +}; + +const struct sof_ipc_ops ipc3_ops = { + .tplg = &ipc3_tplg_ops, +}; diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 15c36a51f89f..683c290bb69a 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -263,45 +263,15 @@ use_count_dec: } EXPORT_SYMBOL(sof_widget_setup); -static int sof_route_setup_ipc(struct snd_sof_dev *sdev, struct snd_sof_route *sroute) -{ - struct sof_ipc_pipe_comp_connect connect; - struct sof_ipc_reply reply; - int ret; - - /* nothing to do if route is already set up */ - if (sroute->setup) - return 0; - - connect.hdr.size = sizeof(connect); - connect.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_CONNECT; - connect.source_id = sroute->src_widget->comp_id; - connect.sink_id = sroute->sink_widget->comp_id; - - dev_dbg(sdev->dev, "setting up route %s -> %s\n", - sroute->src_widget->widget->name, - sroute->sink_widget->widget->name); - - /* send ipc */ - ret = sof_ipc_tx_message(sdev->ipc, connect.hdr.cmd, &connect, sizeof(connect), - &reply, sizeof(reply)); - if (ret < 0) { - dev_err(sdev->dev, "%s: route setup failed %d\n", __func__, ret); - return ret; - } - - sroute->setup = true; - - return 0; -} - static int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink) { + const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; struct snd_sof_widget *src_widget = wsource->dobj.private; struct snd_sof_widget *sink_widget = wsink->dobj.private; struct snd_sof_route *sroute; bool route_found = false; + int ret; /* ignore routes involving virtual widgets in topology */ switch (src_widget->id) { @@ -335,7 +305,16 @@ static int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget return -EINVAL; } - return sof_route_setup_ipc(sdev, sroute); + /* nothing to do if route is already set up */ + if (sroute->setup) + return 0; + + ret = ipc_tplg_ops->route_setup(sdev, sroute); + if (ret < 0) + return ret; + + sroute->setup = true; + return 0; } static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, @@ -383,6 +362,7 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir) { + const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; struct snd_soc_dapm_widget *widget; int i, ret, num_widgets; @@ -453,10 +433,12 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, in if (pipe_widget->complete) continue; - pipe_widget->complete = snd_sof_complete_pipeline(sdev, pipe_widget); - if (pipe_widget->complete < 0) { - ret = pipe_widget->complete; - goto widget_free; + if (ipc_tplg_ops->pipeline_complete) { + pipe_widget->complete = ipc_tplg_ops->pipeline_complete(sdev, pipe_widget); + if (pipe_widget->complete < 0) { + ret = pipe_widget->complete; + goto widget_free; + } } } @@ -604,6 +586,7 @@ int sof_set_hw_params_upon_resume(struct device *dev) int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify) { + const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; struct sof_ipc_fw_version *v = &sdev->fw_ready.version; struct snd_sof_widget *swidget; struct snd_sof_route *sroute; @@ -656,7 +639,7 @@ int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify) sroute->sink_widget->dynamic_pipeline_widget)) continue; - ret = sof_route_setup_ipc(sdev, sroute); + ret = ipc_tplg_ops->route_setup(sdev, sroute); if (ret < 0) { dev_err(sdev->dev, "%s: restore pipeline connections failed\n", __func__); return ret; @@ -677,8 +660,11 @@ int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify) return ret; } - swidget->complete = - snd_sof_complete_pipeline(sdev, swidget); + if (ipc_tplg_ops->pipeline_complete) { + swidget->complete = ipc_tplg_ops->pipeline_complete(sdev, swidget); + if (swidget->complete < 0) + return swidget->complete; + } break; default: break; diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 450ee9977c55..622d43707b27 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -30,6 +30,55 @@ #define WIDGET_IS_DAI(id) ((id) == snd_soc_dapm_dai_in || (id) == snd_soc_dapm_dai_out) +/* + * Volume fractional word length define to 16 sets + * the volume linear gain value to use Qx.16 format + */ +#define VOLUME_FWL 16 + +struct snd_sof_widget; +struct snd_sof_route; +struct snd_sof_control; + +/** + * struct sof_ipc_tplg_widget_ops - IPC-specific ops for topology widgets + * @ipc_setup: Function pointer for setting up widget IPC params + * @ipc_free: Function pointer for freeing widget IPC params + * @token_list: List of token ID's that should be parsed for the widget + * @token_list_size: number of elements in token_list + * @bind_event: Function pointer for binding events to the widget + */ +struct sof_ipc_tplg_widget_ops { + int (*ipc_setup)(struct snd_sof_widget *swidget); + void (*ipc_free)(struct snd_sof_widget *swidget); + enum sof_tokens *token_list; + int token_list_size; + int (*bind_event)(struct snd_soc_component *scomp, struct snd_sof_widget *swidget, + u16 event_type); +}; + +/** + * struct sof_ipc_tplg_ops - IPC-specific topology ops + * @widget: Array of pointers to IPC-specific ops for widgets. This should always be of size + * SND_SOF_DAPM_TYPE_COUNT i.e one per widget type. Unsupported widget types will be + * initialized to 0. + * @route_setup: Function pointer for setting up pipeline connections + * @token_list: List of all tokens supported by the IPC version. The size of the token_list + * array should be SOF_TOKEN_COUNT. The unused elements in the array will be + * initialized to 0. + * @control_setup: Function pointer for setting up kcontrol IPC-specific data + * @control_free: Function pointer for freeing kcontrol IPC-specific data + * @pipeline_complete: Function pointer for pipeline complete IPC + */ +struct sof_ipc_tplg_ops { + const struct sof_ipc_tplg_widget_ops *widget; + int (*route_setup)(struct snd_sof_dev *sdev, struct snd_sof_route *sroute); + const struct sof_token_info *token_list; + int (*control_setup)(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol); + int (*control_free)(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol); + int (*pipeline_complete)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget); +}; + /** struct snd_sof_tuple - Tuple info * @token: Token ID * @value: union of a string or a u32 values @@ -42,6 +91,57 @@ struct snd_sof_tuple { } value; }; +/* + * List of SOF token ID's. The order of ID's does not matter as token arrays are looked up based on + * the ID. + */ +enum sof_tokens { + SOF_PCM_TOKENS, + SOF_PIPELINE_TOKENS, + SOF_SCHED_TOKENS, + SOF_ASRC_TOKENS, + SOF_SRC_TOKENS, + SOF_COMP_TOKENS, + SOF_BUFFER_TOKENS, + SOF_VOLUME_TOKENS, + SOF_PROCESS_TOKENS, + SOF_DAI_TOKENS, + SOF_DAI_LINK_TOKENS, + SOF_HDA_TOKENS, + SOF_SSP_TOKENS, + SOF_ALH_TOKENS, + SOF_DMIC_TOKENS, + SOF_DMIC_PDM_TOKENS, + SOF_ESAI_TOKENS, + SOF_SAI_TOKENS, + SOF_AFE_TOKENS, + SOF_CORE_TOKENS, + SOF_COMP_EXT_TOKENS, + + /* this should be the last */ + SOF_TOKEN_COUNT, +}; + +/** + * struct sof_topology_token - SOF topology token definition + * @token: Token number + * @type: Token type + * @get_token: Function pointer to parse the token value and save it in a object + * @offset: Offset within an object to save the token value into + */ +struct sof_topology_token { + u32 token; + u32 type; + int (*get_token)(void *elem, void *object, u32 offset); + u32 offset; +}; + +struct sof_token_info { + const char *name; + const struct sof_topology_token *tokens; + int count; +}; + /* PCM stream, mapped to FW component */ struct snd_sof_pcm_stream { u32 comp_id; @@ -78,13 +178,20 @@ struct snd_sof_led_control { /* ALSA SOF Kcontrol device */ struct snd_sof_control { struct snd_soc_component *scomp; + const char *name; int comp_id; int min_volume_step; /* min volume step for volume_table */ int max_volume_step; /* max volume step for volume_table */ int num_channels; unsigned int access; u32 readback_offset; /* offset to mmapped data if used */ - struct sof_ipc_ctrl_data *control_data; + int info_type; + int index; /* pipeline ID */ + void *priv; /* private data copied from topology */ + size_t priv_size; /* size of private data */ + size_t max_size; + void *ipc_control_data; + int max; /* applicable to volume controls */ u32 size; /* cdata size */ u32 *volume_table; /* volume table computed from tlv data*/ @@ -96,7 +203,26 @@ struct snd_sof_control { bool comp_data_dirty; }; -struct snd_sof_widget; +/** struct snd_sof_dai_link - DAI link info + * @tuples: array of parsed tuples + * @num_tuples: number of tuples in the tuples array + * @link: Pointer to snd_soc_dai_link + * @hw_configs: Pointer to hw configs in topology + * @num_hw_configs: Number of hw configs in topology + * @default_hw_cfg_id: Default hw config ID + * @type: DAI type + * @list: item in snd_sof_dev dai_link list + */ +struct snd_sof_dai_link { + struct snd_sof_tuple *tuples; + int num_tuples; + struct snd_soc_dai_link *link; + struct snd_soc_tplg_hw_config *hw_configs; + int num_hw_configs; + int default_hw_cfg_id; + int type; + struct list_head list; +}; /* ASoC SOF DAPM widget */ struct snd_sof_widget { @@ -194,8 +320,6 @@ void snd_sof_control_notify(struct snd_sof_dev *sdev, * be freed by snd_soc_unregister_component, */ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file); -int snd_sof_complete_pipeline(struct snd_sof_dev *sdev, - struct snd_sof_widget *swidget); /* * Stream IPC @@ -278,4 +402,7 @@ int get_token_u16(void *elem, void *object, u32 offset); int get_token_comp_format(void *elem, void *object, u32 offset); int get_token_dai_type(void *elem, void *object, u32 offset); int get_token_uuid(void *elem, void *object, u32 offset); +int sof_update_ipc_object(struct snd_soc_component *scomp, void *object, enum sof_tokens token_id, + struct snd_sof_tuple *tuples, int num_tuples, + size_t object_size, int token_instance_num); #endif diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index ef5a2adae5c7..0b89c3e6ef21 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -360,6 +360,18 @@ struct snd_sof_ipc_msg { bool ipc_complete; }; +struct sof_ipc_tplg_ops; + +/** + * struct sof_ipc_ops - IPC-specific ops + * @tplg: Pointer to IPC-specific topology ops + */ +struct sof_ipc_ops { + const struct sof_ipc_tplg_ops *tplg; +}; + +extern const struct sof_ipc_ops ipc3_ops; + /* SOF generic IPC data */ struct snd_sof_ipc { struct snd_sof_dev *sdev; @@ -370,6 +382,9 @@ struct snd_sof_ipc { bool disable_ipc_tx; struct snd_sof_ipc_msg msg; + + /* IPC ops based on version */ + const struct sof_ipc_ops *ops; }; /* @@ -441,6 +456,7 @@ struct snd_sof_dev { struct list_head kcontrol_list; struct list_head widget_list; struct list_head dai_list; + struct list_head dai_link_list; struct list_head route_list; struct snd_soc_component *component; u32 enabled_cores_mask; /* keep track of enabled cores */ diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index afd9eda67631..367fbe2d5b31 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -14,7 +14,6 @@ #include <linux/firmware.h> #include <linux/workqueue.h> #include <sound/tlv.h> -#include <sound/pcm_params.h> #include <uapi/sound/sof/tokens.h> #include "sof-priv.h" #include "sof-audio.h" @@ -28,15 +27,9 @@ #define VOL_TWENTIETH_ROOT_OF_TEN 73533 /* 40th root of 10 in Q1.16 fixed-point notation*/ #define VOL_FORTIETH_ROOT_OF_TEN 69419 -/* - * Volume fractional word length define to 16 sets - * the volume linear gain value to use Qx.16 format - */ -#define VOLUME_FWL 16 + /* 0.5 dB step value in topology TLV */ #define VOL_HALF_DB_STEP 50 -/* Full volume for default values */ -#define VOL_ZERO_DB BIT(VOLUME_FWL) /* TLV data items */ #define TLV_ITEMS 3 @@ -47,178 +40,100 @@ /* size of tplg abi in byte */ #define SOF_TPLG_ABI_SIZE 3 -struct sof_widget_data { - int ctrl_type; - int ipc_cmd; - struct sof_abi_hdr *pdata; - struct snd_sof_control *control; -}; - -/* send pcm params ipc */ -static int ipc_pcm_params(struct snd_sof_widget *swidget, int dir) +/** + * sof_update_ipc_object - Parse multiple sets of tokens within the token array associated with the + * token ID. + * @scomp: pointer to SOC component + * @object: target IPC struct to save the parsed values + * @token_id: token ID for the token array to be searched + * @tuples: pointer to the tuples array + * @num_tuples: number of tuples in the tuples array + * @object_size: size of the object + * @token_instance_num: number of times the same @token_id needs to be parsed i.e. the function + * looks for @token_instance_num of each token in the token array associated + * with the @token_id + */ +int sof_update_ipc_object(struct snd_soc_component *scomp, void *object, enum sof_tokens token_id, + struct snd_sof_tuple *tuples, int num_tuples, + size_t object_size, int token_instance_num) { - struct sof_ipc_pcm_params_reply ipc_params_reply; - struct snd_soc_component *scomp = swidget->scomp; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct sof_ipc_pcm_params pcm; - struct snd_pcm_hw_params *params; - struct snd_sof_pcm *spcm; - int ret; - - memset(&pcm, 0, sizeof(pcm)); + const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; + const struct sof_token_info *token_list = ipc_tplg_ops->token_list; + const struct sof_topology_token *tokens; + int i, j; - /* get runtime PCM params using widget's stream name */ - spcm = snd_sof_find_spcm_name(scomp, swidget->widget->sname); - if (!spcm) { - dev_err(scomp->dev, "error: cannot find PCM for %s\n", - swidget->widget->name); + if (token_list[token_id].count < 0) { + dev_err(scomp->dev, "Invalid token count for token ID: %d\n", token_id); return -EINVAL; } - params = &spcm->params[dir]; - - /* set IPC PCM params */ - pcm.hdr.size = sizeof(pcm); - pcm.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_PARAMS; - pcm.comp_id = swidget->comp_id; - pcm.params.hdr.size = sizeof(pcm.params); - pcm.params.direction = dir; - pcm.params.sample_valid_bytes = params_width(params) >> 3; - pcm.params.buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED; - pcm.params.rate = params_rate(params); - pcm.params.channels = params_channels(params); - pcm.params.host_period_bytes = params_period_bytes(params); - - /* set format */ - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16: - pcm.params.frame_fmt = SOF_IPC_FRAME_S16_LE; - break; - case SNDRV_PCM_FORMAT_S24: - pcm.params.frame_fmt = SOF_IPC_FRAME_S24_4LE; - break; - case SNDRV_PCM_FORMAT_S32: - pcm.params.frame_fmt = SOF_IPC_FRAME_S32_LE; - break; - default: + /* No tokens to match */ + if (!token_list[token_id].count) + return 0; + + tokens = token_list[token_id].tokens; + if (!tokens) { + dev_err(scomp->dev, "Invalid tokens for token id: %d\n", token_id); return -EINVAL; } - /* send IPC to the DSP */ - ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm), - &ipc_params_reply, sizeof(ipc_params_reply)); - if (ret < 0) - dev_err(scomp->dev, "error: pcm params failed for %s\n", - swidget->widget->name); - - return ret; -} - - /* send stream trigger ipc */ -static int ipc_trigger(struct snd_sof_widget *swidget, int cmd) -{ - struct snd_soc_component *scomp = swidget->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct sof_ipc_stream stream; - struct sof_ipc_reply reply; - int ret; + for (i = 0; i < token_list[token_id].count; i++) { + int offset = 0; + int num_tokens_matched = 0; - /* set IPC stream params */ - stream.hdr.size = sizeof(stream); - stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | cmd; - stream.comp_id = swidget->comp_id; + for (j = 0; j < num_tuples; j++) { + if (tokens[i].token == tuples[j].token) { + switch (tokens[i].type) { + case SND_SOC_TPLG_TUPLE_TYPE_WORD: + { + u32 *val = (u32 *)((u8 *)object + tokens[i].offset + + offset); - /* send IPC to the DSP */ - ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, - sizeof(stream), &reply, sizeof(reply)); - if (ret < 0) - dev_err(scomp->dev, "error: failed to trigger %s\n", - swidget->widget->name); - - return ret; -} - -static int sof_keyword_dapm_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *k, int event) -{ - struct snd_sof_widget *swidget = w->dobj.private; - struct snd_soc_component *scomp; - int stream = SNDRV_PCM_STREAM_CAPTURE; - struct snd_sof_pcm *spcm; - int ret = 0; - - if (!swidget) - return 0; - - scomp = swidget->scomp; - - dev_dbg(scomp->dev, "received event %d for widget %s\n", - event, w->name); + *val = tuples[j].value.v; + break; + } + case SND_SOC_TPLG_TUPLE_TYPE_SHORT: + case SND_SOC_TPLG_TUPLE_TYPE_BOOL: + { + u16 *val = (u16 *)((u8 *)object + tokens[i].offset + + offset); - /* get runtime PCM params using widget's stream name */ - spcm = snd_sof_find_spcm_name(scomp, swidget->widget->sname); - if (!spcm) { - dev_err(scomp->dev, "error: cannot find PCM for %s\n", - swidget->widget->name); - return -EINVAL; - } + *val = (u16)tuples[j].value.v; + break; + } + case SND_SOC_TPLG_TUPLE_TYPE_STRING: + { + if (!tokens[i].get_token) { + dev_err(scomp->dev, + "get_token not defined for token %d in %s\n", + tokens[i].token, token_list[token_id].name); + return -EINVAL; + } + + tokens[i].get_token((void *)tuples[j].value.s, object, + tokens[i].offset + offset); + break; + } + default: + break; + } - /* process events */ - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - if (spcm->stream[stream].suspend_ignored) { - dev_dbg(scomp->dev, "PRE_PMU event ignored, KWD pipeline is already RUNNING\n"); - return 0; - } + num_tokens_matched++; - /* set pcm params */ - ret = ipc_pcm_params(swidget, stream); - if (ret < 0) { - dev_err(scomp->dev, - "error: failed to set pcm params for widget %s\n", - swidget->widget->name); - break; - } + /* found all required sets of current token. Move to the next one */ + if (!(num_tokens_matched % token_instance_num)) + break; - /* start trigger */ - ret = ipc_trigger(swidget, SOF_IPC_STREAM_TRIG_START); - if (ret < 0) - dev_err(scomp->dev, - "error: failed to trigger widget %s\n", - swidget->widget->name); - break; - case SND_SOC_DAPM_POST_PMD: - if (spcm->stream[stream].suspend_ignored) { - dev_dbg(scomp->dev, "POST_PMD even ignored, KWD pipeline will remain RUNNING\n"); - return 0; + /* move to the next object */ + offset += object_size; + } } - - /* stop trigger */ - ret = ipc_trigger(swidget, SOF_IPC_STREAM_TRIG_STOP); - if (ret < 0) - dev_err(scomp->dev, - "error: failed to trigger widget %s\n", - swidget->widget->name); - - /* pcm free */ - ret = ipc_trigger(swidget, SOF_IPC_STREAM_PCM_FREE); - if (ret < 0) - dev_err(scomp->dev, - "error: failed to trigger widget %s\n", - swidget->widget->name); - break; - default: - break; } - return ret; + return 0; } -/* event handlers for keyword detect component */ -static const struct snd_soc_tplg_widget_events sof_kwd_events[] = { - {SOF_KEYWORD_DETECT_DAPM_EVENT, sof_keyword_dapm_event}, -}; - static inline int get_tlv_data(const int *p, int tlv[TLV_ITEMS]) { /* we only support dB scale TLV type at the moment */ @@ -423,60 +338,6 @@ static enum sof_ipc_frame find_format(const char *name) return SOF_IPC_FRAME_S32_LE; } -struct sof_process_types { - const char *name; - enum sof_ipc_process_type type; - enum sof_comp_type comp_type; -}; - -static const struct sof_process_types sof_process[] = { - {"EQFIR", SOF_PROCESS_EQFIR, SOF_COMP_EQ_FIR}, - {"EQIIR", SOF_PROCESS_EQIIR, SOF_COMP_EQ_IIR}, - {"KEYWORD_DETECT", SOF_PROCESS_KEYWORD_DETECT, SOF_COMP_KEYWORD_DETECT}, - {"KPB", SOF_PROCESS_KPB, SOF_COMP_KPB}, - {"CHAN_SELECTOR", SOF_PROCESS_CHAN_SELECTOR, SOF_COMP_SELECTOR}, - {"MUX", SOF_PROCESS_MUX, SOF_COMP_MUX}, - {"DEMUX", SOF_PROCESS_DEMUX, SOF_COMP_DEMUX}, - {"DCBLOCK", SOF_PROCESS_DCBLOCK, SOF_COMP_DCBLOCK}, - {"SMART_AMP", SOF_PROCESS_SMART_AMP, SOF_COMP_SMART_AMP}, -}; - -static enum sof_ipc_process_type find_process(const char *name) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(sof_process); i++) { - if (strcmp(name, sof_process[i].name) == 0) - return sof_process[i].type; - } - - return SOF_PROCESS_NONE; -} - -static enum sof_comp_type find_process_comp_type(enum sof_ipc_process_type type) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(sof_process); i++) { - if (sof_process[i].type == type) - return sof_process[i].comp_type; - } - - return SOF_COMP_NONE; -} - -/* - * Topology Token Parsing. - * New tokens should be added to headers and parsing tables below. - */ - -struct sof_topology_token { - u32 token; - u32 type; - int (*get_token)(void *elem, void *object, u32 offset); - u32 offset; -}; - int get_token_u32(void *elem, void *object, u32 offset) { struct snd_soc_tplg_vendor_value_elem *velem = elem; @@ -521,290 +382,20 @@ int get_token_dai_type(void *elem, void *object, u32 offset) return 0; } -static int get_token_process_type(void *elem, void *object, u32 offset) -{ - u32 *val = (u32 *)((u8 *)object + offset); - - *val = find_process((const char *)elem); - return 0; -} - -/* Buffers */ -static const struct sof_topology_token buffer_tokens[] = { - {SOF_TKN_BUF_SIZE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_buffer, size)}, - {SOF_TKN_BUF_CAPS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_buffer, caps)}, -}; - -/* DAI */ -static const struct sof_topology_token dai_tokens[] = { - {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, - offsetof(struct sof_ipc_comp_dai, type)}, - {SOF_TKN_DAI_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_dai, dai_index)}, - {SOF_TKN_DAI_DIRECTION, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_dai, direction)}, -}; - -/* BE DAI link */ -static const struct sof_topology_token dai_link_tokens[] = { - {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, - offsetof(struct sof_ipc_dai_config, type)}, - {SOF_TKN_DAI_INDEX, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_config, dai_index)}, -}; - -/* scheduling */ -static const struct sof_topology_token sched_tokens[] = { - {SOF_TKN_SCHED_PERIOD, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_pipe_new, period)}, - {SOF_TKN_SCHED_PRIORITY, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_pipe_new, priority)}, - {SOF_TKN_SCHED_MIPS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_pipe_new, period_mips)}, - {SOF_TKN_SCHED_CORE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_pipe_new, core)}, - {SOF_TKN_SCHED_FRAMES, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_pipe_new, frames_per_sched)}, - {SOF_TKN_SCHED_TIME_DOMAIN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_pipe_new, time_domain)}, -}; - -static const struct sof_topology_token pipeline_tokens[] = { - {SOF_TKN_SCHED_DYNAMIC_PIPELINE, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, - offsetof(struct snd_sof_widget, dynamic_pipeline_widget)}, - -}; - -/* volume */ -static const struct sof_topology_token volume_tokens[] = { - {SOF_TKN_VOLUME_RAMP_STEP_TYPE, SND_SOC_TPLG_TUPLE_TYPE_WORD, - get_token_u32, offsetof(struct sof_ipc_comp_volume, ramp)}, - {SOF_TKN_VOLUME_RAMP_STEP_MS, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_volume, initial_ramp)}, -}; - -/* SRC */ -static const struct sof_topology_token src_tokens[] = { - {SOF_TKN_SRC_RATE_IN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_src, source_rate)}, - {SOF_TKN_SRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_src, sink_rate)}, -}; - -/* ASRC */ -static const struct sof_topology_token asrc_tokens[] = { - {SOF_TKN_ASRC_RATE_IN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_asrc, source_rate)}, - {SOF_TKN_ASRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_asrc, sink_rate)}, - {SOF_TKN_ASRC_ASYNCHRONOUS_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, - get_token_u32, - offsetof(struct sof_ipc_comp_asrc, asynchronous_mode)}, - {SOF_TKN_ASRC_OPERATION_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, - get_token_u32, - offsetof(struct sof_ipc_comp_asrc, operation_mode)}, -}; - -/* Tone */ -static const struct sof_topology_token tone_tokens[] = { -}; - -/* EFFECT */ -static const struct sof_topology_token process_tokens[] = { - {SOF_TKN_PROCESS_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, - get_token_process_type, - offsetof(struct sof_ipc_comp_process, type)}, -}; - -/* PCM */ -static const struct sof_topology_token pcm_tokens[] = { - {SOF_TKN_PCM_DMAC_CONFIG, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_host, dmac_config)}, -}; - /* PCM */ static const struct sof_topology_token stream_tokens[] = { - {SOF_TKN_STREAM_PLAYBACK_COMPATIBLE_D0I3, - SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, + {SOF_TKN_STREAM_PLAYBACK_COMPATIBLE_D0I3, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, offsetof(struct snd_sof_pcm, stream[0].d0i3_compatible)}, - {SOF_TKN_STREAM_CAPTURE_COMPATIBLE_D0I3, - SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, + {SOF_TKN_STREAM_CAPTURE_COMPATIBLE_D0I3, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, offsetof(struct snd_sof_pcm, stream[1].d0i3_compatible)}, }; -/* Generic components */ -static const struct sof_topology_token comp_tokens[] = { - {SOF_TKN_COMP_PERIOD_SINK_COUNT, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_config, periods_sink)}, - {SOF_TKN_COMP_PERIOD_SOURCE_COUNT, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp_config, periods_source)}, - {SOF_TKN_COMP_FORMAT, - SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_format, - offsetof(struct sof_ipc_comp_config, frame_fmt)}, -}; - -/* SSP */ -static const struct sof_topology_token ssp_tokens[] = { - {SOF_TKN_INTEL_SSP_CLKS_CONTROL, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_ssp_params, clks_control)}, - {SOF_TKN_INTEL_SSP_MCLK_ID, - SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_ssp_params, mclk_id)}, - {SOF_TKN_INTEL_SSP_SAMPLE_BITS, SND_SOC_TPLG_TUPLE_TYPE_WORD, - get_token_u32, - offsetof(struct sof_ipc_dai_ssp_params, sample_valid_bits)}, - {SOF_TKN_INTEL_SSP_FRAME_PULSE_WIDTH, SND_SOC_TPLG_TUPLE_TYPE_SHORT, - get_token_u16, - offsetof(struct sof_ipc_dai_ssp_params, frame_pulse_width)}, - {SOF_TKN_INTEL_SSP_QUIRKS, SND_SOC_TPLG_TUPLE_TYPE_WORD, - get_token_u32, - offsetof(struct sof_ipc_dai_ssp_params, quirks)}, - {SOF_TKN_INTEL_SSP_TDM_PADDING_PER_SLOT, SND_SOC_TPLG_TUPLE_TYPE_BOOL, - get_token_u16, - offsetof(struct sof_ipc_dai_ssp_params, - tdm_per_slot_padding_flag)}, - {SOF_TKN_INTEL_SSP_BCLK_DELAY, SND_SOC_TPLG_TUPLE_TYPE_WORD, - get_token_u32, - offsetof(struct sof_ipc_dai_ssp_params, bclk_delay)}, - -}; - -/* ALH */ -static const struct sof_topology_token alh_tokens[] = { - {SOF_TKN_INTEL_ALH_RATE, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_alh_params, rate)}, - {SOF_TKN_INTEL_ALH_CH, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_alh_params, channels)}, -}; - -/* DMIC */ -static const struct sof_topology_token dmic_tokens[] = { - {SOF_TKN_INTEL_DMIC_DRIVER_VERSION, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_dmic_params, driver_ipc_version)}, - {SOF_TKN_INTEL_DMIC_CLK_MIN, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_dmic_params, pdmclk_min)}, - {SOF_TKN_INTEL_DMIC_CLK_MAX, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_dmic_params, pdmclk_max)}, - {SOF_TKN_INTEL_DMIC_SAMPLE_RATE, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_dmic_params, fifo_fs)}, - {SOF_TKN_INTEL_DMIC_DUTY_MIN, - SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_params, duty_min)}, - {SOF_TKN_INTEL_DMIC_DUTY_MAX, - SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_params, duty_max)}, - {SOF_TKN_INTEL_DMIC_NUM_PDM_ACTIVE, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_dmic_params, - num_pdm_active)}, - {SOF_TKN_INTEL_DMIC_FIFO_WORD_LENGTH, - SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_params, fifo_bits)}, - {SOF_TKN_INTEL_DMIC_UNMUTE_RAMP_TIME_MS, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_dmic_params, unmute_ramp_time)}, - -}; - -/* ESAI */ -static const struct sof_topology_token esai_tokens[] = { - {SOF_TKN_IMX_ESAI_MCLK_ID, - SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_esai_params, mclk_id)}, -}; - -/* SAI */ -static const struct sof_topology_token sai_tokens[] = { - {SOF_TKN_IMX_SAI_MCLK_ID, - SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_sai_params, mclk_id)}, -}; - -/* Core tokens */ -static const struct sof_topology_token core_tokens[] = { - {SOF_TKN_COMP_CORE_ID, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_comp, core)}, -}; - -/* Component extended tokens */ -static const struct sof_topology_token comp_ext_tokens[] = { - {SOF_TKN_COMP_UUID, - SND_SOC_TPLG_TUPLE_TYPE_UUID, get_token_uuid, - offsetof(struct snd_sof_widget, uuid)}, -}; - -/* - * DMIC PDM Tokens - * SOF_TKN_INTEL_DMIC_PDM_CTRL_ID should be the first token - * as it increments the index while parsing the array of pdm tokens - * and determines the correct offset - */ -static const struct sof_topology_token dmic_pdm_tokens[] = { - {SOF_TKN_INTEL_DMIC_PDM_CTRL_ID, - SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, id),}, - {SOF_TKN_INTEL_DMIC_PDM_MIC_A_Enable, - SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, enable_mic_a)}, - {SOF_TKN_INTEL_DMIC_PDM_MIC_B_Enable, - SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, enable_mic_b)}, - {SOF_TKN_INTEL_DMIC_PDM_POLARITY_A, - SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, polarity_mic_a)}, - {SOF_TKN_INTEL_DMIC_PDM_POLARITY_B, - SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, polarity_mic_b)}, - {SOF_TKN_INTEL_DMIC_PDM_CLK_EDGE, - SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, clk_edge)}, - {SOF_TKN_INTEL_DMIC_PDM_SKEW, - SND_SOC_TPLG_TUPLE_TYPE_SHORT, get_token_u16, - offsetof(struct sof_ipc_dai_dmic_pdm_ctrl, skew)}, -}; - -/* HDA */ -static const struct sof_topology_token hda_tokens[] = { - {SOF_TKN_INTEL_HDA_RATE, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_hda_params, rate)}, - {SOF_TKN_INTEL_HDA_CH, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_hda_params, channels)}, -}; - /* Leds */ static const struct sof_topology_token led_tokens[] = { {SOF_TKN_MUTE_LED_USE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct snd_sof_led_control, use_led)}, - {SOF_TKN_MUTE_LED_DIRECTION, SND_SOC_TPLG_TUPLE_TYPE_WORD, - get_token_u32, offsetof(struct snd_sof_led_control, direction)}, -}; - -/* AFE */ -static const struct sof_topology_token afe_tokens[] = { - {SOF_TKN_MEDIATEK_AFE_RATE, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_mtk_afe_params, rate)}, - {SOF_TKN_MEDIATEK_AFE_CH, - SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc_dai_mtk_afe_params, channels)}, - {SOF_TKN_MEDIATEK_AFE_FORMAT, - SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_comp_format, - offsetof(struct sof_ipc_dai_mtk_afe_params, format)}, + offsetof(struct snd_sof_led_control, use_led)}, + {SOF_TKN_MUTE_LED_DIRECTION, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct snd_sof_led_control, direction)}, }; /** @@ -853,6 +444,123 @@ static int sof_parse_uuid_tokens(struct snd_soc_component *scomp, } /** + * sof_copy_tuples - Parse tokens and copy them to the @tuples array + * @sdev: pointer to struct snd_sof_dev + * @array: source pointer to consecutive vendor arrays in topology + * @array_size: size of @array + * @token_id: Token ID associated with a token array + * @token_instance_num: number of times the same @token_id needs to be parsed i.e. the function + * looks for @token_instance_num of each token in the token array associated + * with the @token_id + * @tuples: tuples array to copy the matched tuples to + * @tuples_size: size of @tuples + * @num_copied_tuples: pointer to the number of copied tuples in the tuples array + * + */ +static int sof_copy_tuples(struct snd_sof_dev *sdev, struct snd_soc_tplg_vendor_array *array, + int array_size, u32 token_id, int token_instance_num, + struct snd_sof_tuple *tuples, int tuples_size, int *num_copied_tuples) +{ + const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; + const struct sof_token_info *token_list = ipc_tplg_ops->token_list; + const struct sof_topology_token *tokens; + int found = 0; + int num_tokens, asize; + int i, j; + + /* nothing to do if token_list is NULL */ + if (!token_list) + return 0; + + if (!tuples || !num_copied_tuples) { + dev_err(sdev->dev, "Invalid tuples array\n"); + return -EINVAL; + } + + tokens = token_list[token_id].tokens; + num_tokens = token_list[token_id].count; + + if (!tokens) { + dev_err(sdev->dev, "No token array defined for token ID: %d\n", token_id); + return -EINVAL; + } + + /* check if there's space in the tuples array for new tokens */ + if (*num_copied_tuples >= tuples_size) { + dev_err(sdev->dev, "No space in tuples array for new tokens from %s", + token_list[token_id].name); + return -EINVAL; + } + + while (array_size > 0 && found < num_tokens * token_instance_num) { + asize = le32_to_cpu(array->size); + + /* validate asize */ + if (asize < 0) { + dev_err(sdev->dev, "Invalid array size 0x%x\n", asize); + return -EINVAL; + } + + /* make sure there is enough data before parsing */ + array_size -= asize; + if (array_size < 0) { + dev_err(sdev->dev, "Invalid array size 0x%x\n", asize); + return -EINVAL; + } + + /* parse element by element */ + for (i = 0; i < le32_to_cpu(array->num_elems); i++) { + /* search for token */ + for (j = 0; j < num_tokens; j++) { + /* match token type */ + if (!(tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_WORD || + tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_SHORT || + tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_BYTE || + tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_BOOL || + tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_STRING)) + continue; + + if (tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_STRING) { + struct snd_soc_tplg_vendor_string_elem *elem; + + elem = &array->string[i]; + + /* match token id */ + if (tokens[j].token != le32_to_cpu(elem->token)) + continue; + + tuples[*num_copied_tuples].token = tokens[j].token; + tuples[*num_copied_tuples].value.s = elem->string; + } else { + struct snd_soc_tplg_vendor_value_elem *elem; + + elem = &array->value[i]; + + /* match token id */ + if (tokens[j].token != le32_to_cpu(elem->token)) + continue; + + tuples[*num_copied_tuples].token = tokens[j].token; + tuples[*num_copied_tuples].value.v = + le32_to_cpu(elem->value); + } + found++; + (*num_copied_tuples)++; + + /* stop if there's no space for any more new tuples */ + if (*num_copied_tuples == tuples_size) + return 0; + } + } + + /* next array */ + array = (struct snd_soc_tplg_vendor_array *)((u8 *)array + asize); + } + + return 0; +} + +/** * sof_parse_string_tokens - Parse multiple sets of tokens * @scomp: pointer to soc component * @object: target ipc struct for parsed values @@ -1052,14 +760,6 @@ static int sof_parse_tokens(struct snd_soc_component *scomp, void *object, array_size, 1, 0); } -static void sof_dbg_comp_config(struct snd_soc_component *scomp, - struct sof_ipc_comp_config *config) -{ - dev_dbg(scomp->dev, " config: periods snk %d src %d fmt %d\n", - config->periods_sink, config->periods_source, - config->frame_fmt); -} - /* * Standard Kcontrols. */ @@ -1072,16 +772,12 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_mixer_control *mc = container_of(hdr, struct snd_soc_tplg_mixer_control, hdr); - struct sof_ipc_ctrl_data *cdata; int tlv[TLV_ITEMS]; - unsigned int i; int ret; /* validate topology data */ - if (le32_to_cpu(mc->num_channels) > SND_SOC_TPLG_MAX_CHAN) { - ret = -EINVAL; - goto out; - } + if (le32_to_cpu(mc->num_channels) > SND_SOC_TPLG_MAX_CHAN) + return -EINVAL; /* * If control has more than 2 channels we need to override the info. This is because even if @@ -1092,48 +788,26 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, if (le32_to_cpu(mc->num_channels) > 2) kc->info = snd_sof_volume_info; - /* init the volume get/put data */ - scontrol->size = struct_size(scontrol->control_data, chanv, - le32_to_cpu(mc->num_channels)); - scontrol->control_data = kzalloc(scontrol->size, GFP_KERNEL); - if (!scontrol->control_data) { - ret = -ENOMEM; - goto out; - } - scontrol->comp_id = sdev->next_comp_id; scontrol->min_volume_step = le32_to_cpu(mc->min); scontrol->max_volume_step = le32_to_cpu(mc->max); scontrol->num_channels = le32_to_cpu(mc->num_channels); - scontrol->control_data->index = kc->index; - /* set cmd for mixer control */ - if (le32_to_cpu(mc->max) == 1) { - scontrol->control_data->cmd = SOF_CTRL_CMD_SWITCH; + scontrol->max = le32_to_cpu(mc->max); + if (le32_to_cpu(mc->max) == 1) goto skip; - } - - scontrol->control_data->cmd = SOF_CTRL_CMD_VOLUME; /* extract tlv data */ if (!kc->tlv.p || get_tlv_data(kc->tlv.p, tlv) < 0) { dev_err(scomp->dev, "error: invalid TLV data\n"); - ret = -EINVAL; - goto out_free; + return -EINVAL; } /* set up volume table */ ret = set_up_volume_table(scontrol, tlv, le32_to_cpu(mc->max) + 1); if (ret < 0) { dev_err(scomp->dev, "error: setting up volume table\n"); - goto out_free; - } - - /* set default volume values to 0dB in control */ - cdata = scontrol->control_data; - for (i = 0; i < scontrol->num_channels; i++) { - cdata->chanv[i].channel = i; - cdata->chanv[i].value = VOL_ZERO_DB; + return ret; } skip: @@ -1144,7 +818,7 @@ skip: if (ret != 0) { dev_err(scomp->dev, "error: parse led tokens failed %d\n", le32_to_cpu(mc->priv.size)); - goto out_free_table; + goto err; } dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d\n", @@ -1152,12 +826,10 @@ skip: return 0; -out_free_table: +err: if (le32_to_cpu(mc->max) > 1) kfree(scontrol->volume_table); -out_free: - kfree(scontrol->control_data); -out: + return ret; } @@ -1174,17 +846,8 @@ static int sof_control_load_enum(struct snd_soc_component *scomp, if (le32_to_cpu(ec->num_channels) > SND_SOC_TPLG_MAX_CHAN) return -EINVAL; - /* init the enum get/put data */ - scontrol->size = struct_size(scontrol->control_data, chanv, - le32_to_cpu(ec->num_channels)); - scontrol->control_data = kzalloc(scontrol->size, GFP_KERNEL); - if (!scontrol->control_data) - return -ENOMEM; - scontrol->comp_id = sdev->next_comp_id; scontrol->num_channels = le32_to_cpu(ec->num_channels); - scontrol->control_data->index = kc->index; - scontrol->control_data->cmd = SOF_CTRL_CMD_ENUM; dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d comp_id %d\n", scontrol->comp_id, scontrol->num_channels, scontrol->comp_id); @@ -1198,77 +861,27 @@ static int sof_control_load_bytes(struct snd_soc_component *scomp, struct snd_soc_tplg_ctl_hdr *hdr) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct sof_ipc_ctrl_data *cdata; struct snd_soc_tplg_bytes_control *control = container_of(hdr, struct snd_soc_tplg_bytes_control, hdr); struct soc_bytes_ext *sbe = (struct soc_bytes_ext *)kc->private_value; - size_t max_size = sbe->max; size_t priv_size = le32_to_cpu(control->priv.size); - int ret; - - if (max_size < sizeof(struct sof_ipc_ctrl_data) || - max_size < sizeof(struct sof_abi_hdr)) { - ret = -EINVAL; - goto out; - } - - /* init the get/put bytes data */ - if (priv_size > max_size - sizeof(struct sof_ipc_ctrl_data)) { - dev_err(scomp->dev, "err: bytes data size %zu exceeds max %zu.\n", - priv_size, max_size - sizeof(struct sof_ipc_ctrl_data)); - ret = -EINVAL; - goto out; - } - - scontrol->size = sizeof(struct sof_ipc_ctrl_data) + priv_size; - - scontrol->control_data = kzalloc(max_size, GFP_KERNEL); - cdata = scontrol->control_data; - if (!scontrol->control_data) { - ret = -ENOMEM; - goto out; - } + scontrol->max_size = sbe->max; scontrol->comp_id = sdev->next_comp_id; - scontrol->control_data->cmd = SOF_CTRL_CMD_BINARY; - scontrol->control_data->index = kc->index; - dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d\n", - scontrol->comp_id, scontrol->num_channels); + dev_dbg(scomp->dev, "tplg: load kcontrol index %d\n", scontrol->comp_id); - if (le32_to_cpu(control->priv.size) > 0) { - memcpy(cdata->data, control->priv.data, - le32_to_cpu(control->priv.size)); + /* copy the private data */ + if (priv_size > 0) { + scontrol->priv = kzalloc(priv_size, GFP_KERNEL); + if (!scontrol->priv) + return -ENOMEM; - if (cdata->data->magic != SOF_ABI_MAGIC) { - dev_err(scomp->dev, "error: Wrong ABI magic 0x%08x.\n", - cdata->data->magic); - ret = -EINVAL; - goto out_free; - } - if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, - cdata->data->abi)) { - dev_err(scomp->dev, - "error: Incompatible ABI version 0x%08x.\n", - cdata->data->abi); - ret = -EINVAL; - goto out_free; - } - if (cdata->data->size + sizeof(struct sof_abi_hdr) != - le32_to_cpu(control->priv.size)) { - dev_err(scomp->dev, - "error: Conflict in bytes vs. priv size.\n"); - ret = -EINVAL; - goto out_free; - } + memcpy(scontrol->priv, control->priv.data, priv_size); + scontrol->priv_size = priv_size; } return 0; - -out_free: - kfree(scontrol->control_data); -out: - return ret; } /* external kcontrol init - used for any driver specific init */ @@ -1291,8 +904,14 @@ static int sof_control_load(struct snd_soc_component *scomp, int index, if (!scontrol) return -ENOMEM; + scontrol->name = kstrdup(hdr->name, GFP_KERNEL); + if (!scontrol->name) + return -ENOMEM; + scontrol->scomp = scomp; scontrol->access = kc->access; + scontrol->info_type = le32_to_cpu(hdr->ops.info); + scontrol->index = kc->index; switch (le32_to_cpu(hdr->ops.info)) { case SND_SOC_TPLG_CTL_VOLSW: @@ -1343,22 +962,26 @@ static int sof_control_unload(struct snd_soc_component *scomp, struct snd_soc_dobj *dobj) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct sof_ipc_free fcomp; + const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; struct snd_sof_control *scontrol = dobj->private; + int ret = 0; - dev_dbg(scomp->dev, "tplg: unload control name : %s\n", scomp->name); + dev_dbg(scomp->dev, "tplg: unload control name : %s\n", scontrol->name); - fcomp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_FREE; - fcomp.hdr.size = sizeof(fcomp); - fcomp.id = scontrol->comp_id; + if (ipc_tplg_ops->control_free) { + ret = ipc_tplg_ops->control_free(sdev, scontrol); + if (ret < 0) + dev_err(scomp->dev, "failed to free control: %s\n", scontrol->name); + } - kfree(scontrol->control_data); + /* free all data before returning in case of error too */ + kfree(scontrol->ipc_control_data); + kfree(scontrol->priv); + kfree(scontrol->name); list_del(&scontrol->list); kfree(scontrol); - /* send IPC to the DSP */ - return sof_ipc_tx_message(sdev->ipc, - fcomp.hdr.cmd, &fcomp, sizeof(fcomp), - NULL, 0); + + return ret; } /* @@ -1446,152 +1069,6 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp, return 0; } -/** - * sof_comp_alloc - allocate and initialize buffer for a new component - * @swidget: pointer to struct snd_sof_widget containing extended data - * @ipc_size: IPC payload size that will be updated depending on valid - * extended data. - * @index: ID of the pipeline the component belongs to - * - * Return: The pointer to the new allocated component, NULL if failed. - */ -static struct sof_ipc_comp *sof_comp_alloc(struct snd_sof_widget *swidget, size_t *ipc_size, - int index) -{ - struct sof_ipc_comp *comp; - size_t total_size = *ipc_size; - size_t ext_size = sizeof(swidget->uuid); - - /* only non-zero UUID is valid */ - if (!guid_is_null(&swidget->uuid)) - total_size += ext_size; - - comp = kzalloc(total_size, GFP_KERNEL); - if (!comp) - return NULL; - - /* configure comp new IPC message */ - comp->hdr.size = total_size; - comp->hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; - comp->id = swidget->comp_id; - comp->pipeline_id = index; - comp->core = swidget->core; - - /* handle the extended data if needed */ - if (total_size > *ipc_size) { - /* append extended data to the end of the component */ - memcpy((u8 *)comp + *ipc_size, &swidget->uuid, ext_size); - comp->ext_data_length = ext_size; - } - - /* update ipc_size and return */ - *ipc_size = total_size; - return comp; -} - -static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw, - struct snd_sof_dai *dai) -{ - struct snd_soc_tplg_private *private = &tw->priv; - struct sof_dai_private_data *dai_data; - struct sof_ipc_comp_dai *comp_dai; - size_t ipc_size = sizeof(*comp_dai); - int ret; - - dai_data = kzalloc(sizeof(*dai_data), GFP_KERNEL); - if (!dai_data) - return -ENOMEM; - - comp_dai = (struct sof_ipc_comp_dai *) - sof_comp_alloc(swidget, &ipc_size, index); - if (!comp_dai) { - ret = -ENOMEM; - goto free; - } - - /* configure dai IPC message */ - comp_dai->comp.type = SOF_COMP_DAI; - comp_dai->config.hdr.size = sizeof(comp_dai->config); - - ret = sof_parse_tokens(scomp, comp_dai, dai_tokens, - ARRAY_SIZE(dai_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse dai tokens failed %d\n", - le32_to_cpu(private->size)); - goto free; - } - - ret = sof_parse_tokens(scomp, &comp_dai->config, comp_tokens, - ARRAY_SIZE(comp_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse dai.cfg tokens failed %d\n", - private->size); - goto free; - } - - dev_dbg(scomp->dev, "dai %s: type %d index %d\n", - swidget->widget->name, comp_dai->type, comp_dai->dai_index); - sof_dbg_comp_config(scomp, &comp_dai->config); - - if (dai) { - dai->scomp = scomp; - dai_data->comp_dai = comp_dai; - dai->private = dai_data; - } - - return 0; - -free: - kfree(dai_data); - return ret; -} - -/* - * Buffer topology - */ - -static int sof_widget_load_buffer(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw) -{ - struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_buffer *buffer; - int ret; - - buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); - if (!buffer) - return -ENOMEM; - - /* configure dai IPC message */ - buffer->comp.hdr.size = sizeof(*buffer); - buffer->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_BUFFER_NEW; - buffer->comp.id = swidget->comp_id; - buffer->comp.type = SOF_COMP_BUFFER; - buffer->comp.pipeline_id = index; - buffer->comp.core = swidget->core; - - ret = sof_parse_tokens(scomp, buffer, buffer_tokens, - ARRAY_SIZE(buffer_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse buffer tokens failed %d\n", - private->size); - kfree(buffer); - return ret; - } - - dev_dbg(scomp->dev, "buffer %s: size %d caps 0x%x\n", - swidget->widget->name, buffer->size, buffer->caps); - - swidget->private = buffer; - - return 0; -} - /* bind PCM ID to host component ID */ static int spcm_bind(struct snd_soc_component *scomp, struct snd_sof_pcm *spcm, int dir) @@ -1611,671 +1088,87 @@ static int spcm_bind(struct snd_soc_component *scomp, struct snd_sof_pcm *spcm, return 0; } -/* - * PCM Topology - */ - -static int sof_widget_load_pcm(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, - enum sof_ipc_stream_direction dir, - struct snd_soc_tplg_dapm_widget *tw) -{ - struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_comp_host *host; - size_t ipc_size = sizeof(*host); - int ret; - - host = (struct sof_ipc_comp_host *) - sof_comp_alloc(swidget, &ipc_size, index); - if (!host) - return -ENOMEM; - - /* configure host comp IPC message */ - host->comp.type = SOF_COMP_HOST; - host->direction = dir; - host->config.hdr.size = sizeof(host->config); - - ret = sof_parse_tokens(scomp, host, pcm_tokens, - ARRAY_SIZE(pcm_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse host tokens failed %d\n", - private->size); - goto err; - } - - ret = sof_parse_tokens(scomp, &host->config, comp_tokens, - ARRAY_SIZE(comp_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse host.cfg tokens failed %d\n", - le32_to_cpu(private->size)); - goto err; - } - - dev_dbg(scomp->dev, "loaded host %s\n", swidget->widget->name); - sof_dbg_comp_config(scomp, &host->config); - - swidget->private = host; - - return 0; -err: - kfree(host); - return ret; -} - -/* - * Pipeline Topology - */ -static int sof_widget_load_pipeline(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw) -{ - struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_pipe_new *pipeline; - struct snd_sof_widget *comp_swidget; - int ret; - - pipeline = kzalloc(sizeof(*pipeline), GFP_KERNEL); - if (!pipeline) - return -ENOMEM; - - /* configure dai IPC message */ - pipeline->hdr.size = sizeof(*pipeline); - pipeline->hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_PIPE_NEW; - pipeline->pipeline_id = index; - pipeline->comp_id = swidget->comp_id; - - /* component at start of pipeline is our stream id */ - comp_swidget = snd_sof_find_swidget(scomp, tw->sname); - if (!comp_swidget) { - dev_err(scomp->dev, "error: widget %s refers to non existent widget %s\n", - tw->name, tw->sname); - ret = -EINVAL; - goto err; - } - - pipeline->sched_id = comp_swidget->comp_id; - - dev_dbg(scomp->dev, "tplg: pipeline id %d comp %d scheduling comp id %d\n", - pipeline->pipeline_id, pipeline->comp_id, pipeline->sched_id); - - ret = sof_parse_tokens(scomp, pipeline, sched_tokens, - ARRAY_SIZE(sched_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse pipeline tokens failed %d\n", - private->size); - goto err; - } - - ret = sof_parse_tokens(scomp, swidget, pipeline_tokens, - ARRAY_SIZE(pipeline_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse dynamic pipeline token failed %d\n", - private->size); - goto err; - } - - if (sof_debug_check_flag(SOF_DBG_DISABLE_MULTICORE)) - pipeline->core = SOF_DSP_PRIMARY_CORE; - - if (sof_debug_check_flag(SOF_DBG_DYNAMIC_PIPELINES_OVERRIDE)) - swidget->dynamic_pipeline_widget = - sof_debug_check_flag(SOF_DBG_DYNAMIC_PIPELINES_ENABLE); - - dev_dbg(scomp->dev, "pipeline %s: period %d pri %d mips %d core %d frames %d dynamic %d\n", - swidget->widget->name, pipeline->period, pipeline->priority, - pipeline->period_mips, pipeline->core, pipeline->frames_per_sched, - swidget->dynamic_pipeline_widget); - - swidget->core = pipeline->core; - swidget->private = pipeline; - - return 0; -err: - kfree(pipeline); - return ret; -} - -/* - * Mixer topology - */ - -static int sof_widget_load_mixer(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw) -{ - struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_comp_mixer *mixer; - size_t ipc_size = sizeof(*mixer); - int ret; - - mixer = (struct sof_ipc_comp_mixer *) - sof_comp_alloc(swidget, &ipc_size, index); - if (!mixer) - return -ENOMEM; - - /* configure mixer IPC message */ - mixer->comp.type = SOF_COMP_MIXER; - mixer->config.hdr.size = sizeof(mixer->config); - - ret = sof_parse_tokens(scomp, &mixer->config, comp_tokens, - ARRAY_SIZE(comp_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse mixer.cfg tokens failed %d\n", - private->size); - kfree(mixer); - return ret; - } - - sof_dbg_comp_config(scomp, &mixer->config); - - swidget->private = mixer; - - return 0; -} - -/* - * Mux topology - */ -static int sof_widget_load_mux(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw) -{ - struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_comp_mux *mux; - size_t ipc_size = sizeof(*mux); - int ret; - - mux = (struct sof_ipc_comp_mux *) - sof_comp_alloc(swidget, &ipc_size, index); - if (!mux) - return -ENOMEM; - - /* configure mux IPC message */ - mux->comp.type = SOF_COMP_MUX; - mux->config.hdr.size = sizeof(mux->config); - - ret = sof_parse_tokens(scomp, &mux->config, comp_tokens, - ARRAY_SIZE(comp_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse mux.cfg tokens failed %d\n", - private->size); - kfree(mux); - return ret; - } - - sof_dbg_comp_config(scomp, &mux->config); - - swidget->private = mux; - - return 0; -} - -/* - * PGA Topology - */ - -static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw) +static int sof_widget_parse_tokens(struct snd_soc_component *scomp, struct snd_sof_widget *swidget, + struct snd_soc_tplg_dapm_widget *tw, + enum sof_tokens *object_token_list, int count) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; + const struct sof_token_info *token_list = ipc_tplg_ops->token_list; struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_comp_volume *volume; - struct snd_sof_control *scontrol; - size_t ipc_size = sizeof(*volume); - int min_step; - int max_step; - int ret; - - volume = (struct sof_ipc_comp_volume *) - sof_comp_alloc(swidget, &ipc_size, index); - if (!volume) - return -ENOMEM; - - if (!le32_to_cpu(tw->num_kcontrols)) { - dev_err(scomp->dev, "error: invalid kcontrol count %d for volume\n", - tw->num_kcontrols); - ret = -EINVAL; - goto err; - } - - /* configure volume IPC message */ - volume->comp.type = SOF_COMP_VOLUME; - volume->config.hdr.size = sizeof(volume->config); - - ret = sof_parse_tokens(scomp, volume, volume_tokens, - ARRAY_SIZE(volume_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse volume tokens failed %d\n", - private->size); - goto err; - } - ret = sof_parse_tokens(scomp, &volume->config, comp_tokens, - ARRAY_SIZE(comp_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse volume.cfg tokens failed %d\n", - le32_to_cpu(private->size)); - goto err; - } - - sof_dbg_comp_config(scomp, &volume->config); - - swidget->private = volume; - - list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { - if (scontrol->comp_id == swidget->comp_id && - scontrol->volume_table) { - min_step = scontrol->min_volume_step; - max_step = scontrol->max_volume_step; - volume->min_value = scontrol->volume_table[min_step]; - volume->max_value = scontrol->volume_table[max_step]; - volume->channels = scontrol->num_channels; - break; - } - } - - return 0; -err: - kfree(volume); - return ret; -} - -/* - * SRC Topology - */ - -static int sof_widget_load_src(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw) -{ - struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_comp_src *src; - size_t ipc_size = sizeof(*src); - int ret; - - src = (struct sof_ipc_comp_src *) - sof_comp_alloc(swidget, &ipc_size, index); - if (!src) - return -ENOMEM; - - /* configure src IPC message */ - src->comp.type = SOF_COMP_SRC; - src->config.hdr.size = sizeof(src->config); - - ret = sof_parse_tokens(scomp, src, src_tokens, - ARRAY_SIZE(src_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse src tokens failed %d\n", - private->size); - goto err; - } - - ret = sof_parse_tokens(scomp, &src->config, comp_tokens, - ARRAY_SIZE(comp_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse src.cfg tokens failed %d\n", - le32_to_cpu(private->size)); - goto err; - } - - dev_dbg(scomp->dev, "src %s: source rate %d sink rate %d\n", - swidget->widget->name, src->source_rate, src->sink_rate); - sof_dbg_comp_config(scomp, &src->config); - - swidget->private = src; - - return 0; -err: - kfree(src); - return ret; -} - -/* - * ASRC Topology - */ - -static int sof_widget_load_asrc(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw) -{ - struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_comp_asrc *asrc; - size_t ipc_size = sizeof(*asrc); - int ret; + int num_tuples = 0; + size_t size; + int ret, i; - asrc = (struct sof_ipc_comp_asrc *) - sof_comp_alloc(swidget, &ipc_size, index); - if (!asrc) - return -ENOMEM; - - /* configure ASRC IPC message */ - asrc->comp.type = SOF_COMP_ASRC; - asrc->config.hdr.size = sizeof(asrc->config); - - ret = sof_parse_tokens(scomp, asrc, asrc_tokens, - ARRAY_SIZE(asrc_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse asrc tokens failed %d\n", - private->size); - goto err; - } - - ret = sof_parse_tokens(scomp, &asrc->config, comp_tokens, - ARRAY_SIZE(comp_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse asrc.cfg tokens failed %d\n", - le32_to_cpu(private->size)); - goto err; + if (count > 0 && !object_token_list) { + dev_err(scomp->dev, "No token list for widget %s\n", swidget->widget->name); + return -EINVAL; } - dev_dbg(scomp->dev, "asrc %s: source rate %d sink rate %d " - "asynch %d operation %d\n", - swidget->widget->name, asrc->source_rate, asrc->sink_rate, - asrc->asynchronous_mode, asrc->operation_mode); - sof_dbg_comp_config(scomp, &asrc->config); - - swidget->private = asrc; - - return 0; -err: - kfree(asrc); - return ret; -} - -/* - * Signal Generator Topology - */ - -static int sof_widget_load_siggen(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw) -{ - struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_comp_tone *tone; - size_t ipc_size = sizeof(*tone); - int ret; + /* calculate max size of tuples array */ + for (i = 0; i < count; i++) + num_tuples += token_list[object_token_list[i]].count; - tone = (struct sof_ipc_comp_tone *) - sof_comp_alloc(swidget, &ipc_size, index); - if (!tone) + /* allocate memory for tuples array */ + size = sizeof(struct snd_sof_tuple) * num_tuples; + swidget->tuples = kzalloc(size, GFP_KERNEL); + if (!swidget->tuples) return -ENOMEM; - /* configure siggen IPC message */ - tone->comp.type = SOF_COMP_TONE; - tone->config.hdr.size = sizeof(tone->config); - - ret = sof_parse_tokens(scomp, tone, tone_tokens, - ARRAY_SIZE(tone_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse tone tokens failed %d\n", - le32_to_cpu(private->size)); - goto err; - } - - ret = sof_parse_tokens(scomp, &tone->config, comp_tokens, - ARRAY_SIZE(comp_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse tone.cfg tokens failed %d\n", - le32_to_cpu(private->size)); - goto err; - } - - dev_dbg(scomp->dev, "tone %s: frequency %d amplitude %d\n", - swidget->widget->name, tone->frequency, tone->amplitude); - sof_dbg_comp_config(scomp, &tone->config); - - swidget->private = tone; - - return 0; -err: - kfree(tone); - return ret; -} - -static int sof_get_control_data(struct snd_soc_component *scomp, - struct snd_soc_dapm_widget *widget, - struct sof_widget_data *wdata, - size_t *size) -{ - const struct snd_kcontrol_new *kc; - struct soc_mixer_control *sm; - struct soc_bytes_ext *sbe; - struct soc_enum *se; - int i; - - *size = 0; - - for (i = 0; i < widget->num_kcontrols; i++) { - kc = &widget->kcontrol_news[i]; - - switch (widget->dobj.widget.kcontrol_type[i]) { - case SND_SOC_TPLG_TYPE_MIXER: - sm = (struct soc_mixer_control *)kc->private_value; - wdata[i].control = sm->dobj.private; - break; - case SND_SOC_TPLG_TYPE_BYTES: - sbe = (struct soc_bytes_ext *)kc->private_value; - wdata[i].control = sbe->dobj.private; - break; - case SND_SOC_TPLG_TYPE_ENUM: - se = (struct soc_enum *)kc->private_value; - wdata[i].control = se->dobj.private; - break; - default: - dev_err(scomp->dev, "error: unknown kcontrol type %u in widget %s\n", - widget->dobj.widget.kcontrol_type[i], - widget->name); - return -EINVAL; - } - - if (!wdata[i].control) { - dev_err(scomp->dev, "error: no scontrol for widget %s\n", - widget->name); - return -EINVAL; + /* parse token list for widget */ + for (i = 0; i < count; i++) { + if (object_token_list[i] >= SOF_TOKEN_COUNT) { + dev_err(scomp->dev, "Invalid token id %d for widget %s\n", + object_token_list[i], swidget->widget->name); + ret = -EINVAL; + goto err; } - wdata[i].pdata = wdata[i].control->control_data->data; - if (!wdata[i].pdata) - return -EINVAL; - - /* make sure data is valid - data can be updated at runtime */ - if (widget->dobj.widget.kcontrol_type[i] == SND_SOC_TPLG_TYPE_BYTES && - wdata[i].pdata->magic != SOF_ABI_MAGIC) - return -EINVAL; - - *size += wdata[i].pdata->size; + /* parse and save UUID in swidget */ + if (object_token_list[i] == SOF_COMP_EXT_TOKENS) { + ret = sof_parse_tokens(scomp, swidget, + token_list[object_token_list[i]].tokens, + token_list[object_token_list[i]].count, + private->array, le32_to_cpu(private->size)); + if (ret < 0) { + dev_err(scomp->dev, "Failed parsing %s for widget %s\n", + token_list[object_token_list[i]].name, + swidget->widget->name); + goto err; + } - /* get data type */ - switch (wdata[i].control->control_data->cmd) { - case SOF_CTRL_CMD_VOLUME: - case SOF_CTRL_CMD_ENUM: - case SOF_CTRL_CMD_SWITCH: - wdata[i].ipc_cmd = SOF_IPC_COMP_SET_VALUE; - wdata[i].ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_SET; - break; - case SOF_CTRL_CMD_BINARY: - wdata[i].ipc_cmd = SOF_IPC_COMP_SET_DATA; - wdata[i].ctrl_type = SOF_CTRL_TYPE_DATA_SET; - break; - default: - break; + continue; } - } - - return 0; -} - -static int sof_process_load(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw, - int type) -{ - struct snd_soc_dapm_widget *widget = swidget->widget; - struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_comp_process *process; - struct sof_widget_data *wdata = NULL; - size_t ipc_data_size = 0; - size_t ipc_size; - int offset = 0; - int ret; - int i; - - /* allocate struct for widget control data sizes and types */ - if (widget->num_kcontrols) { - wdata = kcalloc(widget->num_kcontrols, - sizeof(*wdata), - GFP_KERNEL); - - if (!wdata) - return -ENOMEM; - - /* get possible component controls and get size of all pdata */ - ret = sof_get_control_data(scomp, widget, wdata, - &ipc_data_size); - - if (ret < 0) - goto out; - } - - ipc_size = sizeof(struct sof_ipc_comp_process) + ipc_data_size; - - /* we are exceeding max ipc size, config needs to be sent separately */ - if (ipc_size > SOF_IPC_MSG_MAX_SIZE) { - ipc_size -= ipc_data_size; - ipc_data_size = 0; - } - - process = (struct sof_ipc_comp_process *) - sof_comp_alloc(swidget, &ipc_size, index); - if (!process) { - ret = -ENOMEM; - goto out; - } - - /* configure iir IPC message */ - process->comp.type = type; - process->config.hdr.size = sizeof(process->config); - - ret = sof_parse_tokens(scomp, &process->config, comp_tokens, - ARRAY_SIZE(comp_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse process.cfg tokens failed %d\n", - le32_to_cpu(private->size)); - goto err; - } - sof_dbg_comp_config(scomp, &process->config); - - /* - * found private data in control, so copy it. - * get possible component controls - get size of all pdata, - * then memcpy with headers - */ - if (ipc_data_size) { - for (i = 0; i < widget->num_kcontrols; i++) { - memcpy(&process->data[offset], - wdata[i].pdata->data, - wdata[i].pdata->size); - offset += wdata[i].pdata->size; + /* copy one set of tuples per token ID into swidget->tuples */ + ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size), + object_token_list[i], 1, swidget->tuples, + num_tuples, &swidget->num_tuples); + if (ret < 0) { + dev_err(scomp->dev, "Failed parsing %s for widget %s err: %d\n", + token_list[object_token_list[i]].name, swidget->widget->name, ret); + goto err; } } - process->size = ipc_data_size; - swidget->private = process; + return 0; err: - if (ret < 0) - kfree(process); -out: - kfree(wdata); + kfree(swidget->tuples); return ret; } -/* - * Processing Component Topology - can be "effect", "codec", or general - * "processing". - */ - -static int sof_widget_load_process(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw) +static int sof_get_token_value(u32 token_id, struct snd_sof_tuple *tuples, int num_tuples) { - struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_comp_process config; - int ret; + int i; - /* check we have some tokens - we need at least process type */ - if (le32_to_cpu(private->size) == 0) { - dev_err(scomp->dev, "error: process tokens not found\n"); + if (!tuples) return -EINVAL; - } - - memset(&config, 0, sizeof(config)); - config.comp.core = swidget->core; - - /* get the process token */ - ret = sof_parse_tokens(scomp, &config, process_tokens, - ARRAY_SIZE(process_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse process tokens failed %d\n", - le32_to_cpu(private->size)); - return ret; - } - - /* now load process specific data and send IPC */ - ret = sof_process_load(scomp, index, swidget, tw, find_process_comp_type(config.type)); - if (ret < 0) { - dev_err(scomp->dev, "error: process loading failed\n"); - return ret; - } - - return 0; -} - -static int sof_widget_bind_event(struct snd_soc_component *scomp, - struct snd_sof_widget *swidget, - u16 event_type) -{ - struct sof_ipc_comp *ipc_comp; - /* validate widget event type */ - switch (event_type) { - case SOF_KEYWORD_DETECT_DAPM_EVENT: - /* only KEYWORD_DETECT comps should handle this */ - if (swidget->id != snd_soc_dapm_effect) - break; - - ipc_comp = swidget->private; - if (ipc_comp && ipc_comp->type != SOF_COMP_KEYWORD_DETECT) - break; - - /* bind event to keyword detect comp */ - return snd_soc_tplg_widget_bind_event(swidget->widget, - sof_kwd_events, - ARRAY_SIZE(sof_kwd_events), - event_type); - default: - break; + for (i = 0; i < num_tuples; i++) { + if (tuples[i].token == token_id) + return tuples[i].value.v; } - dev_err(scomp->dev, - "error: invalid event type %d for widget %s\n", - event_type, swidget->widget->name); return -EINVAL; } @@ -2285,11 +1178,12 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, struct snd_soc_tplg_dapm_widget *tw) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; + const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget; struct snd_sof_widget *swidget; struct snd_sof_dai *dai; - struct sof_ipc_comp comp = { - .core = SOF_DSP_PRIMARY_CORE, - }; + enum sof_tokens *token_list; + int token_list_size; int ret = 0; swidget = kzalloc(sizeof(*swidget), GFP_KERNEL); @@ -2309,29 +1203,8 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, strnlen(tw->sname, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) > 0 ? tw->sname : "none"); - ret = sof_parse_tokens(scomp, &comp, core_tokens, - ARRAY_SIZE(core_tokens), tw->priv.array, - le32_to_cpu(tw->priv.size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parsing core tokens failed %d\n", - ret); - kfree(swidget); - return ret; - } - - if (sof_debug_check_flag(SOF_DBG_DISABLE_MULTICORE)) - comp.core = SOF_DSP_PRIMARY_CORE; - - swidget->core = comp.core; - - ret = sof_parse_tokens(scomp, swidget, comp_ext_tokens, ARRAY_SIZE(comp_ext_tokens), - tw->priv.array, le32_to_cpu(tw->priv.size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parsing comp_ext_tokens failed %d\n", - ret); - kfree(swidget); - return ret; - } + token_list = widget_ops[w->id].token_list; + token_list_size = widget_ops[w->id].token_list_size; /* handle any special case widgets */ switch (w->id) { @@ -2341,9 +1214,10 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, if (!dai) { kfree(swidget); return -ENOMEM; + } - ret = sof_widget_load_dai(scomp, index, swidget, tw, dai); + ret = sof_widget_parse_tokens(scomp, swidget, tw, token_list, token_list_size); if (!ret) ret = sof_connect_dai_widget(scomp, w, tw, dai); if (ret < 0) { @@ -2353,41 +1227,35 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, list_add(&dai->list, &sdev->dai_list); swidget->private = dai; break; - case snd_soc_dapm_mixer: - ret = sof_widget_load_mixer(scomp, index, swidget, tw); + case snd_soc_dapm_effect: + /* check we have some tokens - we need at least process type */ + if (le32_to_cpu(tw->priv.size) == 0) { + dev_err(scomp->dev, "error: process tokens not found\n"); + ret = -EINVAL; + break; + } + ret = sof_widget_parse_tokens(scomp, swidget, tw, token_list, token_list_size); break; case snd_soc_dapm_pga: - ret = sof_widget_load_pga(scomp, index, swidget, tw); - break; + if (!le32_to_cpu(tw->num_kcontrols)) { + dev_err(scomp->dev, "invalid kcontrol count %d for volume\n", + tw->num_kcontrols); + ret = -EINVAL; + break; + } + + fallthrough; + case snd_soc_dapm_mixer: case snd_soc_dapm_buffer: - ret = sof_widget_load_buffer(scomp, index, swidget, tw); - break; case snd_soc_dapm_scheduler: - ret = sof_widget_load_pipeline(scomp, index, swidget, tw); - break; case snd_soc_dapm_aif_out: - ret = sof_widget_load_pcm(scomp, index, swidget, - SOF_IPC_STREAM_CAPTURE, tw); - break; case snd_soc_dapm_aif_in: - ret = sof_widget_load_pcm(scomp, index, swidget, - SOF_IPC_STREAM_PLAYBACK, tw); - break; case snd_soc_dapm_src: - ret = sof_widget_load_src(scomp, index, swidget, tw); - break; case snd_soc_dapm_asrc: - ret = sof_widget_load_asrc(scomp, index, swidget, tw); - break; case snd_soc_dapm_siggen: - ret = sof_widget_load_siggen(scomp, index, swidget, tw); - break; - case snd_soc_dapm_effect: - ret = sof_widget_load_process(scomp, index, swidget, tw); - break; case snd_soc_dapm_mux: case snd_soc_dapm_demux: - ret = sof_widget_load_mux(scomp, index, swidget, tw); + ret = sof_widget_parse_tokens(scomp, swidget, tw, token_list, token_list_size); break; case snd_soc_dapm_switch: case snd_soc_dapm_dai_link: @@ -2397,7 +1265,17 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, break; } - /* check IPC reply */ + if (sof_debug_check_flag(SOF_DBG_DISABLE_MULTICORE)) { + swidget->core = SOF_DSP_PRIMARY_CORE; + } else { + int core = sof_get_token_value(SOF_TKN_COMP_CORE_ID, swidget->tuples, + swidget->num_tuples); + + if (core >= 0) + swidget->core = core; + } + + /* check token parsing reply */ if (ret < 0) { dev_err(scomp->dev, "error: failed to add widget id %d type %d name : %s stream %s\n", @@ -2410,13 +1288,17 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, /* bind widget to external event */ if (tw->event_type) { - ret = sof_widget_bind_event(scomp, swidget, - le16_to_cpu(tw->event_type)); - if (ret) { - dev_err(scomp->dev, "error: widget event binding failed\n"); - kfree(swidget->private); - kfree(swidget); - return ret; + if (widget_ops[w->id].bind_event) { + ret = widget_ops[w->id].bind_event(scomp, swidget, + le16_to_cpu(tw->event_type)); + if (ret) { + dev_err(scomp->dev, "widget event binding failed for %s\n", + swidget->widget->name); + kfree(swidget->private); + kfree(swidget->tuples); + kfree(swidget); + return ret; + } } } @@ -2445,6 +1327,9 @@ static int sof_route_unload(struct snd_soc_component *scomp, static int sof_widget_unload(struct snd_soc_component *scomp, struct snd_soc_dobj *dobj) { + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; + const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget; const struct snd_kcontrol_new *kc; struct snd_soc_dapm_widget *widget; struct snd_sof_control *scontrol; @@ -2467,14 +1352,8 @@ static int sof_widget_unload(struct snd_soc_component *scomp, case snd_soc_dapm_dai_out: dai = swidget->private; - if (dai) { - struct sof_dai_private_data *dai_data = dai->private; - - kfree(dai_data->comp_dai); - kfree(dai_data->dai_config); - kfree(dai_data); + if (dai) list_del(&dai->list); - } break; default: break; @@ -2500,14 +1379,17 @@ static int sof_widget_unload(struct snd_soc_component *scomp, dev_warn(scomp->dev, "unsupported kcontrol_type\n"); goto out; } - kfree(scontrol->control_data); + kfree(scontrol->ipc_control_data); list_del(&scontrol->list); kfree(scontrol); } out: - /* free private value */ - kfree(swidget->private); + /* free IPC related data */ + if (widget_ops[swidget->id].ipc_free) + widget_ops[swidget->id].ipc_free(swidget); + + kfree(swidget->tuples); /* remove and free swidget object */ list_del(&swidget->list); @@ -2652,589 +1534,24 @@ static int sof_dai_unload(struct snd_soc_component *scomp, return 0; } -static void sof_dai_set_format(struct snd_soc_tplg_hw_config *hw_config, - struct sof_ipc_dai_config *config) -{ - /* clock directions wrt codec */ - if (hw_config->bclk_provider == SND_SOC_TPLG_BCLK_CP) { - /* codec is bclk provider */ - if (hw_config->fsync_provider == SND_SOC_TPLG_FSYNC_CP) - config->format |= SOF_DAI_FMT_CBP_CFP; - else - config->format |= SOF_DAI_FMT_CBP_CFC; - } else { - /* codec is bclk consumer */ - if (hw_config->fsync_provider == SND_SOC_TPLG_FSYNC_CP) - config->format |= SOF_DAI_FMT_CBC_CFP; - else - config->format |= SOF_DAI_FMT_CBC_CFC; - } - - /* inverted clocks ? */ - if (hw_config->invert_bclk) { - if (hw_config->invert_fsync) - config->format |= SOF_DAI_FMT_IB_IF; - else - config->format |= SOF_DAI_FMT_IB_NF; - } else { - if (hw_config->invert_fsync) - config->format |= SOF_DAI_FMT_NB_IF; - else - config->format |= SOF_DAI_FMT_NB_NF; - } -} - -/* - * Send IPC and set the same config for all DAIs with name matching the link - * name. Note that the function can only be used for the case that all DAIs - * have a common DAI config for now. - */ -static int sof_set_dai_config_multi(struct snd_sof_dev *sdev, u32 size, - struct snd_soc_dai_link *link, - struct sof_ipc_dai_config *config, - int num_conf, int curr_conf) -{ - struct sof_dai_private_data *dai_data; - struct snd_sof_dai *dai; - int found = 0; - int i; - - list_for_each_entry(dai, &sdev->dai_list, list) { - dai_data = dai->private; - if (!dai->name) - continue; - - if (strcmp(link->name, dai->name) == 0) { - /* - * the same dai config will be applied to all DAIs in - * the same dai link. We have to ensure that the ipc - * dai config's dai_index match to the component's - * dai_index. - */ - for (i = 0; i < num_conf; i++) - config[i].dai_index = dai_data->comp_dai->dai_index; - - dev_dbg(sdev->dev, "set DAI config for %s index %d\n", - dai->name, config[curr_conf].dai_index); - - dai->number_configs = num_conf; - dai->current_config = curr_conf; - dai_data->dai_config = kmemdup(config, size * num_conf, GFP_KERNEL); - if (!dai_data->dai_config) - return -ENOMEM; - - found = 1; - } - } - - /* - * machine driver may define a dai link with playback and capture - * dai enabled, but the dai link in topology would support both, one - * or none of them. Here print a warning message to notify user - */ - if (!found) { - dev_warn(sdev->dev, "warning: failed to find dai for dai link %s", - link->name); - } - - return 0; -} - -static int sof_set_dai_config(struct snd_sof_dev *sdev, u32 size, - struct snd_soc_dai_link *link, - struct sof_ipc_dai_config *config) -{ - return sof_set_dai_config_multi(sdev, size, link, config, 1, 0); -} - -static int sof_link_ssp_load(struct snd_soc_component *scomp, int index, - struct snd_soc_dai_link *link, - struct snd_soc_tplg_link_config *cfg, - struct snd_soc_tplg_hw_config *hw_config, - struct sof_ipc_dai_config *config, int curr_conf) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct snd_soc_tplg_private *private = &cfg->priv; - int num_conf = le32_to_cpu(cfg->num_hw_configs); - u32 size = sizeof(*config); - int ret; - int i; - - /* - * Parse common data, we should have 1 common data per hw_config. - */ - ret = sof_parse_token_sets(scomp, &config->ssp, ssp_tokens, - ARRAY_SIZE(ssp_tokens), private->array, - le32_to_cpu(private->size), - num_conf, size); - - if (ret != 0) { - dev_err(scomp->dev, "error: parse ssp tokens failed %d\n", - le32_to_cpu(private->size)); - return ret; - } - - /* process all possible hw configs */ - for (i = 0; i < num_conf; i++) { - - /* handle master/slave and inverted clocks */ - sof_dai_set_format(&hw_config[i], &config[i]); - - config[i].hdr.size = size; - - /* copy differentiating hw configs to ipc structs */ - config[i].ssp.mclk_rate = le32_to_cpu(hw_config[i].mclk_rate); - config[i].ssp.bclk_rate = le32_to_cpu(hw_config[i].bclk_rate); - config[i].ssp.fsync_rate = le32_to_cpu(hw_config[i].fsync_rate); - config[i].ssp.tdm_slots = le32_to_cpu(hw_config[i].tdm_slots); - config[i].ssp.tdm_slot_width = le32_to_cpu(hw_config[i].tdm_slot_width); - config[i].ssp.mclk_direction = hw_config[i].mclk_direction; - config[i].ssp.rx_slots = le32_to_cpu(hw_config[i].rx_slots); - config[i].ssp.tx_slots = le32_to_cpu(hw_config[i].tx_slots); - - 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", - config[i].dai_index, config[i].format, - config[i].ssp.mclk_rate, config[i].ssp.bclk_rate, - config[i].ssp.fsync_rate, config[i].ssp.sample_valid_bits, - config[i].ssp.tdm_slot_width, config[i].ssp.tdm_slots, - config[i].ssp.mclk_id, config[i].ssp.quirks, config[i].ssp.clks_control); - - /* validate SSP fsync rate and channel count */ - if (config[i].ssp.fsync_rate < 8000 || config[i].ssp.fsync_rate > 192000) { - dev_err(scomp->dev, "error: invalid fsync rate for SSP%d\n", - config[i].dai_index); - return -EINVAL; - } - - if (config[i].ssp.tdm_slots < 1 || config[i].ssp.tdm_slots > 8) { - dev_err(scomp->dev, "error: invalid channel count for SSP%d\n", - config[i].dai_index); - return -EINVAL; - } - } - - /* set config for all DAI's with name matching the link name */ - ret = sof_set_dai_config_multi(sdev, size, link, config, num_conf, curr_conf); - if (ret < 0) - dev_err(scomp->dev, "error: failed to save DAI config for SSP%d\n", - config->dai_index); - - return ret; -} - -static int sof_link_sai_load(struct snd_soc_component *scomp, int index, - struct snd_soc_dai_link *link, - struct snd_soc_tplg_link_config *cfg, - struct snd_soc_tplg_hw_config *hw_config, - struct sof_ipc_dai_config *config) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct snd_soc_tplg_private *private = &cfg->priv; - u32 size = sizeof(*config); - int ret; - - /* handle master/slave and inverted clocks */ - sof_dai_set_format(hw_config, config); - - /* init IPC */ - memset(&config->sai, 0, sizeof(struct sof_ipc_dai_sai_params)); - config->hdr.size = size; - - ret = sof_parse_tokens(scomp, &config->sai, sai_tokens, - ARRAY_SIZE(sai_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse sai tokens failed %d\n", - le32_to_cpu(private->size)); - return ret; - } - - config->sai.mclk_rate = le32_to_cpu(hw_config->mclk_rate); - config->sai.bclk_rate = le32_to_cpu(hw_config->bclk_rate); - config->sai.fsync_rate = le32_to_cpu(hw_config->fsync_rate); - config->sai.mclk_direction = hw_config->mclk_direction; - - config->sai.tdm_slots = le32_to_cpu(hw_config->tdm_slots); - config->sai.tdm_slot_width = le32_to_cpu(hw_config->tdm_slot_width); - config->sai.rx_slots = le32_to_cpu(hw_config->rx_slots); - config->sai.tx_slots = le32_to_cpu(hw_config->tx_slots); - - dev_info(scomp->dev, - "tplg: config SAI%d fmt 0x%x mclk %d width %d slots %d mclk id %d\n", - config->dai_index, config->format, - config->sai.mclk_rate, config->sai.tdm_slot_width, - config->sai.tdm_slots, config->sai.mclk_id); - - if (config->sai.tdm_slots < 1 || config->sai.tdm_slots > 8) { - dev_err(scomp->dev, "error: invalid channel count for SAI%d\n", - config->dai_index); - return -EINVAL; - } - - /* set config for all DAI's with name matching the link name */ - ret = sof_set_dai_config(sdev, size, link, config); - if (ret < 0) - dev_err(scomp->dev, "error: failed to save DAI config for SAI%d\n", - config->dai_index); - - return ret; -} - -static int sof_link_esai_load(struct snd_soc_component *scomp, int index, - struct snd_soc_dai_link *link, - struct snd_soc_tplg_link_config *cfg, - struct snd_soc_tplg_hw_config *hw_config, - struct sof_ipc_dai_config *config) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct snd_soc_tplg_private *private = &cfg->priv; - u32 size = sizeof(*config); - int ret; - - /* handle master/slave and inverted clocks */ - sof_dai_set_format(hw_config, config); - - /* init IPC */ - memset(&config->esai, 0, sizeof(struct sof_ipc_dai_esai_params)); - config->hdr.size = size; - - ret = sof_parse_tokens(scomp, &config->esai, esai_tokens, - ARRAY_SIZE(esai_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse esai tokens failed %d\n", - le32_to_cpu(private->size)); - return ret; - } - - config->esai.mclk_rate = le32_to_cpu(hw_config->mclk_rate); - config->esai.bclk_rate = le32_to_cpu(hw_config->bclk_rate); - config->esai.fsync_rate = le32_to_cpu(hw_config->fsync_rate); - config->esai.mclk_direction = hw_config->mclk_direction; - config->esai.tdm_slots = le32_to_cpu(hw_config->tdm_slots); - config->esai.tdm_slot_width = le32_to_cpu(hw_config->tdm_slot_width); - config->esai.rx_slots = le32_to_cpu(hw_config->rx_slots); - config->esai.tx_slots = le32_to_cpu(hw_config->tx_slots); - - dev_info(scomp->dev, - "tplg: config ESAI%d fmt 0x%x mclk %d width %d slots %d mclk id %d\n", - config->dai_index, config->format, - config->esai.mclk_rate, config->esai.tdm_slot_width, - config->esai.tdm_slots, config->esai.mclk_id); - - if (config->esai.tdm_slots < 1 || config->esai.tdm_slots > 8) { - dev_err(scomp->dev, "error: invalid channel count for ESAI%d\n", - config->dai_index); - return -EINVAL; - } - - /* set config for all DAI's with name matching the link name */ - ret = sof_set_dai_config(sdev, size, link, config); - if (ret < 0) - dev_err(scomp->dev, "error: failed to save DAI config for ESAI%d\n", - config->dai_index); - - return ret; -} - -static int sof_link_acp_dmic_load(struct snd_soc_component *scomp, int index, - struct snd_soc_dai_link *link, - struct snd_soc_tplg_link_config *cfg, - struct snd_soc_tplg_hw_config *hw_config, - struct sof_ipc_dai_config *config) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - u32 size = sizeof(*config); - int ret; - - /* handle master/slave and inverted clocks */ - sof_dai_set_format(hw_config, config); - - /* init IPC */ - memset(&config->acpdmic, 0, sizeof(struct sof_ipc_dai_acp_params)); - config->hdr.size = size; - - config->acpdmic.fsync_rate = le32_to_cpu(hw_config->fsync_rate); - config->acpdmic.tdm_slots = le32_to_cpu(hw_config->tdm_slots); - - dev_info(scomp->dev, "ACP_DMIC config ACP%d channel %d rate %d\n", - config->dai_index, config->acpdmic.tdm_slots, - config->acpdmic.fsync_rate); - - /* set config for all DAI's with name matching the link name */ - ret = sof_set_dai_config(sdev, size, link, config); - if (ret < 0) - dev_err(scomp->dev, "ACP_DMIC failed to save DAI config for ACP%d\n", - config->dai_index); - return ret; -} - -static int sof_link_acp_bt_load(struct snd_soc_component *scomp, int index, - struct snd_soc_dai_link *link, - struct snd_soc_tplg_link_config *cfg, - struct snd_soc_tplg_hw_config *hw_config, - struct sof_ipc_dai_config *config) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - u32 size = sizeof(*config); - int ret; - - /* handle master/slave and inverted clocks */ - sof_dai_set_format(hw_config, config); - - /* init IPC */ - memset(&config->acpbt, 0, sizeof(struct sof_ipc_dai_acp_params)); - config->hdr.size = size; - - config->acpbt.fsync_rate = le32_to_cpu(hw_config->fsync_rate); - config->acpbt.tdm_slots = le32_to_cpu(hw_config->tdm_slots); - - dev_info(scomp->dev, "ACP_BT config ACP%d channel %d rate %d\n", - config->dai_index, config->acpbt.tdm_slots, - config->acpbt.fsync_rate); - - /* set config for all DAI's with name matching the link name */ - ret = sof_set_dai_config(sdev, size, link, config); - if (ret < 0) - dev_err(scomp->dev, "ACP_BT failed to save DAI config for ACP%d\n", - config->dai_index); - return ret; -} - -static int sof_link_acp_sp_load(struct snd_soc_component *scomp, int index, - struct snd_soc_dai_link *link, - struct snd_soc_tplg_link_config *cfg, - struct snd_soc_tplg_hw_config *hw_config, - struct sof_ipc_dai_config *config) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - u32 size = sizeof(*config); - int ret; - - /* handle master/slave and inverted clocks */ - sof_dai_set_format(hw_config, config); - - /* init IPC */ - memset(&config->acpsp, 0, sizeof(struct sof_ipc_dai_acp_params)); - config->hdr.size = size; - - config->acpsp.fsync_rate = le32_to_cpu(hw_config->fsync_rate); - config->acpsp.tdm_slots = le32_to_cpu(hw_config->tdm_slots); - - dev_info(scomp->dev, "ACP_SP config ACP%d channel %d rate %d\n", - config->dai_index, config->acpsp.tdm_slots, - config->acpsp.fsync_rate); - - /* set config for all DAI's with name matching the link name */ - ret = sof_set_dai_config(sdev, size, link, config); - if (ret < 0) - dev_err(scomp->dev, "ACP_SP failed to save DAI config for ACP%d\n", - config->dai_index); - return ret; -} - -static int sof_link_afe_load(struct snd_soc_component *scomp, int index, - struct snd_soc_dai_link *link, - struct snd_soc_tplg_link_config *cfg, - struct snd_soc_tplg_hw_config *hw_config, - struct sof_ipc_dai_config *config) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct snd_soc_tplg_private *private = &cfg->priv; - u32 size = sizeof(*config); - int ret; - - config->hdr.size = size; - - /* get any bespoke DAI tokens */ - ret = sof_parse_tokens(scomp, &config->afe, afe_tokens, - ARRAY_SIZE(afe_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "parse afe tokens failed %d\n", - le32_to_cpu(private->size)); - return ret; - } - - dev_dbg(scomp->dev, "AFE config rate %d channels %d format:%d\n", - config->afe.rate, config->afe.channels, config->afe.format); - - config->afe.stream_id = DMA_CHAN_INVALID; - - ret = sof_set_dai_config(sdev, size, link, config); - if (ret < 0) - dev_err(scomp->dev, "failed to process afe dai link %s", link->name); - - return ret; -} - -static int sof_link_dmic_load(struct snd_soc_component *scomp, int index, - struct snd_soc_dai_link *link, - struct snd_soc_tplg_link_config *cfg, - struct snd_soc_tplg_hw_config *hw_config, - struct sof_ipc_dai_config *config) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct snd_soc_tplg_private *private = &cfg->priv; - struct sof_ipc_fw_ready *ready = &sdev->fw_ready; - struct sof_ipc_fw_version *v = &ready->version; - size_t size = sizeof(*config); - int ret, j; - - /* Ensure the entire DMIC config struct is zeros */ - memset(&config->dmic, 0, sizeof(struct sof_ipc_dai_dmic_params)); - - /* get DMIC tokens */ - ret = sof_parse_tokens(scomp, &config->dmic, dmic_tokens, - ARRAY_SIZE(dmic_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse dmic tokens failed %d\n", - le32_to_cpu(private->size)); - return ret; - } - - /* get DMIC PDM tokens */ - ret = sof_parse_token_sets(scomp, &config->dmic.pdm[0], dmic_pdm_tokens, - ARRAY_SIZE(dmic_pdm_tokens), private->array, - le32_to_cpu(private->size), - config->dmic.num_pdm_active, - sizeof(struct sof_ipc_dai_dmic_pdm_ctrl)); - - if (ret != 0) { - dev_err(scomp->dev, "error: parse dmic pdm tokens failed %d\n", - le32_to_cpu(private->size)); - return ret; - } - - /* set IPC header size */ - config->hdr.size = size; - - /* debug messages */ - dev_dbg(scomp->dev, "tplg: config DMIC%d driver version %d\n", - config->dai_index, config->dmic.driver_ipc_version); - dev_dbg(scomp->dev, "pdmclk_min %d pdm_clkmax %d duty_min %hd\n", - config->dmic.pdmclk_min, config->dmic.pdmclk_max, - config->dmic.duty_min); - dev_dbg(scomp->dev, "duty_max %hd fifo_fs %d num_pdms active %d\n", - config->dmic.duty_max, config->dmic.fifo_fs, - config->dmic.num_pdm_active); - dev_dbg(scomp->dev, "fifo word length %hd\n", config->dmic.fifo_bits); - - for (j = 0; j < config->dmic.num_pdm_active; j++) { - dev_dbg(scomp->dev, "pdm %hd mic a %hd mic b %hd\n", - config->dmic.pdm[j].id, - config->dmic.pdm[j].enable_mic_a, - config->dmic.pdm[j].enable_mic_b); - dev_dbg(scomp->dev, "pdm %hd polarity a %hd polarity b %hd\n", - config->dmic.pdm[j].id, - config->dmic.pdm[j].polarity_mic_a, - config->dmic.pdm[j].polarity_mic_b); - dev_dbg(scomp->dev, "pdm %hd clk_edge %hd skew %hd\n", - config->dmic.pdm[j].id, - config->dmic.pdm[j].clk_edge, - config->dmic.pdm[j].skew); - } - - /* - * this takes care of backwards compatible handling of fifo_bits_b. - * It is deprecated since firmware ABI version 3.0.1. - */ - if (SOF_ABI_VER(v->major, v->minor, v->micro) < SOF_ABI_VER(3, 0, 1)) - config->dmic.fifo_bits_b = config->dmic.fifo_bits; - - /* set config for all DAI's with name matching the link name */ - ret = sof_set_dai_config(sdev, size, link, config); - if (ret < 0) - dev_err(scomp->dev, "error: failed to save DAI config for DMIC%d\n", - config->dai_index); - - return ret; -} - -static int sof_link_hda_load(struct snd_soc_component *scomp, int index, - struct snd_soc_dai_link *link, - struct snd_soc_tplg_link_config *cfg, - struct snd_soc_tplg_hw_config *hw_config, - struct sof_ipc_dai_config *config) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct snd_soc_tplg_private *private = &cfg->priv; - u32 size = sizeof(*config); - int ret; - - /* init IPC */ - memset(&config->hda, 0, sizeof(struct sof_ipc_dai_hda_params)); - config->hdr.size = size; - - /* get any bespoke DAI tokens */ - ret = sof_parse_tokens(scomp, &config->hda, hda_tokens, - ARRAY_SIZE(hda_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse hda tokens failed %d\n", - le32_to_cpu(private->size)); - return ret; - } - - dev_dbg(scomp->dev, "HDA config rate %d channels %d\n", - config->hda.rate, config->hda.channels); - - config->hda.link_dma_ch = DMA_CHAN_INVALID; - - ret = sof_set_dai_config(sdev, size, link, config); - if (ret < 0) - dev_err(scomp->dev, "error: failed to process hda dai link %s", - link->name); - - return ret; -} - -static int sof_link_alh_load(struct snd_soc_component *scomp, int index, - struct snd_soc_dai_link *link, - struct snd_soc_tplg_link_config *cfg, - struct snd_soc_tplg_hw_config *hw_config, - struct sof_ipc_dai_config *config) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct snd_soc_tplg_private *private = &cfg->priv; - u32 size = sizeof(*config); - int ret; - - ret = sof_parse_tokens(scomp, &config->alh, alh_tokens, - ARRAY_SIZE(alh_tokens), private->array, - le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse alh tokens failed %d\n", - le32_to_cpu(private->size)); - return ret; - } - - /* init IPC */ - config->hdr.size = size; - - /* set config for all DAI's with name matching the link name */ - ret = sof_set_dai_config(sdev, size, link, config); - if (ret < 0) - dev_err(scomp->dev, "error: failed to save DAI config for ALH %d\n", - config->dai_index); - - return ret; -} +static const struct sof_topology_token common_dai_link_tokens[] = { + {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, + offsetof(struct snd_sof_dai_link, type)}, +}; /* DAI link - used for any driver specific init */ -static int sof_link_load(struct snd_soc_component *scomp, int index, - struct snd_soc_dai_link *link, +static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_soc_dai_link *link, struct snd_soc_tplg_link_config *cfg) { + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; + const struct sof_token_info *token_list = ipc_tplg_ops->token_list; struct snd_soc_tplg_private *private = &cfg->priv; - struct snd_soc_tplg_hw_config *hw_config; - struct sof_ipc_dai_config common_config; - struct sof_ipc_dai_config *config; - int curr_conf; - int num_conf; - int ret; - int i; + struct snd_sof_dai_link *slink; + size_t size; + u32 token_id = 0; + int num_tuples = 0; + int ret, num_sets; if (!link->platforms) { dev_err(scomp->dev, "error: no platforms\n"); @@ -3271,104 +1588,159 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, return -EINVAL; } - memset(&common_config, 0, sizeof(common_config)); + slink = kzalloc(sizeof(*slink), GFP_KERNEL); + if (!slink) + return -ENOMEM; - /* get any common DAI tokens */ - ret = sof_parse_tokens(scomp, &common_config, dai_link_tokens, ARRAY_SIZE(dai_link_tokens), - private->array, le32_to_cpu(private->size)); - if (ret != 0) { - dev_err(scomp->dev, "error: parse link tokens failed %d\n", - le32_to_cpu(private->size)); - return ret; + slink->num_hw_configs = le32_to_cpu(cfg->num_hw_configs); + slink->hw_configs = kmemdup(cfg->hw_config, + sizeof(*slink->hw_configs) * slink->num_hw_configs, + GFP_KERNEL); + if (!slink->hw_configs) { + kfree(slink); + return -ENOMEM; } - /* - * DAI links are expected to have at least 1 hw_config. - * But some older topologies might have no hw_config for HDA dai links. - */ - hw_config = cfg->hw_config; - num_conf = le32_to_cpu(cfg->num_hw_configs); - if (!num_conf) { - if (common_config.type != SOF_DAI_INTEL_HDA) { - dev_err(scomp->dev, "error: unexpected DAI config count %d!\n", - le32_to_cpu(cfg->num_hw_configs)); - return -EINVAL; - } - num_conf = 1; - curr_conf = 0; - } else { - dev_dbg(scomp->dev, "tplg: %d hw_configs found, default id: %d!\n", - cfg->num_hw_configs, le32_to_cpu(cfg->default_hw_config_id)); + slink->default_hw_cfg_id = le32_to_cpu(cfg->default_hw_config_id); + slink->link = link; - for (curr_conf = 0; curr_conf < num_conf; curr_conf++) { - if (hw_config[curr_conf].id == cfg->default_hw_config_id) - break; - } + dev_dbg(scomp->dev, "tplg: %d hw_configs found, default id: %d for dai link %s!\n", + slink->num_hw_configs, slink->default_hw_cfg_id, link->name); - if (curr_conf == num_conf) { - dev_err(scomp->dev, "error: default hw_config id: %d not found!\n", - le32_to_cpu(cfg->default_hw_config_id)); - return -EINVAL; - } + ret = sof_parse_tokens(scomp, slink, common_dai_link_tokens, + ARRAY_SIZE(common_dai_link_tokens), + private->array, le32_to_cpu(private->size)); + if (ret < 0) { + dev_err(scomp->dev, "Failed tp parse common DAI link tokens\n"); + kfree(slink->hw_configs); + kfree(slink); + return ret; } - /* Reserve memory for all hw configs, eventually freed by widget */ - config = kcalloc(num_conf, sizeof(*config), GFP_KERNEL); - if (!config) - return -ENOMEM; - - /* Copy common data to all config ipc structs */ - for (i = 0; i < num_conf; i++) { - config[i].hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG; - config[i].format = le32_to_cpu(hw_config[i].fmt); - config[i].type = common_config.type; - config[i].dai_index = common_config.dai_index; - } + if (!token_list) + goto out; - /* now load DAI specific data and send IPC - type comes from token */ - switch (common_config.type) { + /* calculate size of tuples array */ + num_tuples += token_list[SOF_DAI_LINK_TOKENS].count; + num_sets = slink->num_hw_configs; + switch (slink->type) { case SOF_DAI_INTEL_SSP: - ret = sof_link_ssp_load(scomp, index, link, cfg, hw_config, config, curr_conf); + token_id = SOF_SSP_TOKENS; + num_tuples += token_list[SOF_SSP_TOKENS].count * slink->num_hw_configs; break; case SOF_DAI_INTEL_DMIC: - ret = sof_link_dmic_load(scomp, index, link, cfg, hw_config + curr_conf, config); + token_id = SOF_DMIC_TOKENS; + num_tuples += token_list[SOF_DMIC_TOKENS].count; + + /* Allocate memory for max PDM controllers */ + num_tuples += token_list[SOF_DMIC_PDM_TOKENS].count * SOF_DAI_INTEL_DMIC_NUM_CTRL; break; case SOF_DAI_INTEL_HDA: - ret = sof_link_hda_load(scomp, index, link, cfg, hw_config + curr_conf, config); + token_id = SOF_HDA_TOKENS; + num_tuples += token_list[SOF_HDA_TOKENS].count; break; case SOF_DAI_INTEL_ALH: - ret = sof_link_alh_load(scomp, index, link, cfg, hw_config + curr_conf, config); + token_id = SOF_ALH_TOKENS; + num_tuples += token_list[SOF_ALH_TOKENS].count; break; case SOF_DAI_IMX_SAI: - ret = sof_link_sai_load(scomp, index, link, cfg, hw_config + curr_conf, config); + token_id = SOF_SAI_TOKENS; + num_tuples += token_list[SOF_SAI_TOKENS].count; break; case SOF_DAI_IMX_ESAI: - ret = sof_link_esai_load(scomp, index, link, cfg, hw_config + curr_conf, config); - break; - case SOF_DAI_AMD_BT: - ret = sof_link_acp_bt_load(scomp, index, link, cfg, hw_config + curr_conf, config); - break; - case SOF_DAI_AMD_SP: - ret = sof_link_acp_sp_load(scomp, index, link, cfg, hw_config + curr_conf, config); - break; - case SOF_DAI_AMD_DMIC: - ret = sof_link_acp_dmic_load(scomp, index, link, cfg, hw_config + curr_conf, - config); + token_id = SOF_ESAI_TOKENS; + num_tuples += token_list[SOF_ESAI_TOKENS].count; break; case SOF_DAI_MEDIATEK_AFE: - ret = sof_link_afe_load(scomp, index, link, cfg, hw_config + curr_conf, config); + token_id = SOF_AFE_TOKENS; + num_tuples += token_list[SOF_AFE_TOKENS].count; break; default: - dev_err(scomp->dev, "error: invalid DAI type %d\n", common_config.type); - ret = -EINVAL; break; } - kfree(config); + /* allocate memory for tuples array */ + size = sizeof(struct snd_sof_tuple) * num_tuples; + slink->tuples = kzalloc(size, GFP_KERNEL); + if (!slink->tuples) { + kfree(slink->hw_configs); + kfree(slink); + return -ENOMEM; + } + + /* parse one set of DAI link tokens */ + ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size), + SOF_DAI_LINK_TOKENS, 1, slink->tuples, + num_tuples, &slink->num_tuples); + if (ret < 0) { + dev_err(scomp->dev, "failed to parse %s for dai link %s\n", + token_list[SOF_DAI_LINK_TOKENS].name, link->name); + goto err; + } + + /* nothing more to do if there are no DAI type-specific tokens defined */ + if (!token_id || !token_list[token_id].tokens) + goto out; + + /* parse "num_sets" sets of DAI-specific tokens */ + ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size), + token_id, num_sets, slink->tuples, num_tuples, &slink->num_tuples); + if (ret < 0) { + dev_err(scomp->dev, "failed to parse %s for dai link %s\n", + token_list[token_id].name, link->name); + goto err; + } + + /* for DMIC, also parse all sets of DMIC PDM tokens based on active PDM count */ + if (token_id == SOF_DMIC_TOKENS) { + num_sets = sof_get_token_value(SOF_TKN_INTEL_DMIC_NUM_PDM_ACTIVE, + slink->tuples, slink->num_tuples); + + if (num_sets < 0) { + dev_err(sdev->dev, "Invalid active PDM count for %s\n", link->name); + ret = num_sets; + goto err; + } + + ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size), + SOF_DMIC_PDM_TOKENS, num_sets, slink->tuples, + num_tuples, &slink->num_tuples); + if (ret < 0) { + dev_err(scomp->dev, "failed to parse %s for dai link %s\n", + token_list[SOF_DMIC_PDM_TOKENS].name, link->name); + goto err; + } + } +out: + link->dobj.private = slink; + list_add(&slink->list, &sdev->dai_link_list); + + return 0; + +err: + kfree(slink->tuples); + kfree(slink->hw_configs); + kfree(slink); return ret; } +static int sof_link_unload(struct snd_soc_component *scomp, struct snd_soc_dobj *dobj) +{ + struct snd_sof_dai_link *slink = dobj->private; + + if (!slink) + return 0; + + kfree(slink->tuples); + list_del(&slink->list); + kfree(slink->hw_configs); + kfree(slink); + dobj->private = NULL; + + return 0; +} + /* DAI link - used for any driver specific init */ static int sof_route_load(struct snd_soc_component *scomp, int index, struct snd_soc_dapm_route *route) @@ -3453,29 +1825,6 @@ err: return ret; } -int snd_sof_complete_pipeline(struct snd_sof_dev *sdev, - struct snd_sof_widget *swidget) -{ - struct sof_ipc_pipe_ready ready; - struct sof_ipc_reply reply; - int ret; - - dev_dbg(sdev->dev, "tplg: complete pipeline %s id %d\n", - swidget->widget->name, swidget->comp_id); - - memset(&ready, 0, sizeof(ready)); - ready.hdr.size = sizeof(ready); - ready.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_PIPE_COMPLETE; - ready.comp_id = swidget->comp_id; - - ret = sof_ipc_tx_message(sdev->ipc, - ready.hdr.cmd, &ready, sizeof(ready), &reply, - sizeof(reply)); - if (ret < 0) - return ret; - return 1; -} - /** * sof_set_pipe_widget - Set pipe_widget for a component * @sdev: pointer to struct snd_sof_dev @@ -3515,8 +1864,38 @@ static int sof_complete(struct snd_soc_component *scomp) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_sof_widget *swidget, *comp_swidget; + const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; + const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget; + struct snd_sof_control *scontrol; int ret; + /* first update all control IPC structures based on the IPC version */ + if (ipc_tplg_ops->control_setup) + list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { + ret = ipc_tplg_ops->control_setup(sdev, scontrol); + if (ret < 0) { + dev_err(sdev->dev, "failed updating IPC struct for control %s\n", + scontrol->name); + return ret; + } + } + + /* + * then update all widget IPC structures. If any of the ipc_setup callbacks fail, the + * topology will be removed and all widgets will be unloaded resulting in freeing all + * associated memories. + */ + list_for_each_entry(swidget, &sdev->widget_list, list) { + if (widget_ops[swidget->id].ipc_setup) { + ret = widget_ops[swidget->id].ipc_setup(swidget); + if (ret < 0) { + dev_err(sdev->dev, "failed updating IPC struct for %s\n", + swidget->widget->name); + return ret; + } + } + } + /* set the pipe_widget and apply the dynamic_pipeline_widget_flag */ list_for_each_entry(swidget, &sdev->widget_list, list) { switch (swidget->id) { @@ -3637,6 +2016,7 @@ static struct snd_soc_tplg_ops sof_tplg_ops = { /* DAI link - used for any driver specific init */ .link_load = sof_link_load, + .link_unload = sof_link_unload, /* completion - called at completion of firmware loading */ .complete = sof_complete, |