summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--sound/soc/sof/ipc3-topology.c745
-rw-r--r--sound/soc/sof/topology.c1157
2 files changed, 915 insertions, 987 deletions
diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c
index a9d55c1d1f84..e6aa78d492ea 100644
--- a/sound/soc/sof/ipc3-topology.c
+++ b/sound/soc/sof/ipc3-topology.c
@@ -8,7 +8,6 @@
 //
 
 #include <uapi/sound/sof/tokens.h>
-#include <sound/pcm_params.h>
 #include "sof-priv.h"
 #include "sof-audio.h"
 #include "ops.h"
@@ -66,6 +65,24 @@ static const struct sof_topology_token buffer_tokens[] = {
 		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,
@@ -139,6 +156,107 @@ static const struct sof_topology_token comp_tokens[] = {
 		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,
@@ -163,6 +281,16 @@ static const struct sof_token_info ipc3_token_list[SOF_TOKEN_COUNT] = {
 	[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)},
 };
 
 /**
@@ -797,6 +925,609 @@ static int sof_widget_update_ipc_comp_process(struct snd_sof_widget *swidget)
 	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;
@@ -868,6 +1599,13 @@ static enum sof_tokens pga_token_list[] = {
 	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,
@@ -880,6 +1618,11 @@ static const struct sof_ipc_tplg_widget_ops tplg_ipc3_widget_ops[SND_SOC_DAPM_TY
 				  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,
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index 187f7c46a42b..c9c72e94f696 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -554,204 +554,20 @@ int get_token_dai_type(void *elem, void *object, u32 offset)
 	return 0;
 }
 
-/* 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)},
-};
-
 /* 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)},
 };
 
 /**
@@ -1116,14 +932,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.
  */
@@ -1510,110 +1318,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;
-}
-
 /* bind PCM ID to host component ID */
 static int spcm_bind(struct snd_soc_component *scomp, struct snd_sof_pcm *spcm,
 		     int dir)
@@ -1734,6 +1438,21 @@ static int sof_widget_bind_event(struct snd_soc_component *scomp,
 	return -EINVAL;
 }
 
+static int sof_get_token_value(u32 token_id, struct snd_sof_tuple *tuples, int num_tuples)
+{
+	int i;
+
+	if (!tuples)
+		return -EINVAL;
+
+	for (i = 0; i < num_tuples; i++) {
+		if (tuples[i].token == token_id)
+			return tuples[i].value.v;
+	}
+
+	return -EINVAL;
+}
+
 /* external widget init - used for any driver specific init */
 static int sof_widget_ready(struct snd_soc_component *scomp, int index,
 			    struct snd_soc_dapm_widget *w,
@@ -1746,9 +1465,6 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
 	struct snd_sof_dai *dai;
 	enum sof_tokens *token_list;
 	int token_list_size;
-	struct sof_ipc_comp comp = {
-		.core = SOF_DSP_PRIMARY_CORE,
-	};
 	int ret = 0;
 
 	swidget = kzalloc(sizeof(*swidget), GFP_KERNEL);
@@ -1771,30 +1487,6 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
 	token_list = widget_ops[w->id].token_list;
 	token_list_size = widget_ops[w->id].token_list_size;
 
-	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;
-	}
-
 	/* handle any special case widgets */
 	switch (w->id) {
 	case snd_soc_dapm_dai_in:
@@ -1803,9 +1495,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) {
@@ -1853,7 +1546,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",
@@ -1927,14 +1630,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;
@@ -1970,9 +1667,6 @@ out:
 	if (widget_ops[swidget->id].ipc_free)
 		widget_ops[swidget->id].ipc_free(swidget);
 
-	/* free private value */
-	kfree(swidget->private);
-
 	kfree(swidget->tuples);
 
 	/* remove and free swidget object */
@@ -2118,589 +1812,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");
@@ -2737,104 +1866,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)
@@ -3121,6 +2305,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,