diff mbox series

[15/18] ASoC: SOF: topology: Make DAI widget parsing IPC agnostic

Message ID 20220314200520.1233427-17-ranjani.sridharan@linux.intel.com
State Accepted
Commit 909dadf21aae8f7e604973218907ed39e10499e6
Headers show
Series Introduce IPC abstraction for SOF topology parsing | expand

Commit Message

Ranjani Sridharan March 14, 2022, 8:05 p.m. UTC
Define the list of tokens pertaining to the dai_in/out widgets, parse and
save them as part of the swidget tuples array. Once topology parsing is
complete, these tokens will be applied to create the IPC structure for the
DAI component based on the topology widget_setup op in ipc3_tplg_ops.

DAI link parsing is also made IPC agnostic by parsing the list of tokens
associated with all DAI types. The config will be applied to the
respective DAI widgets during topology complete.

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: Péter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
---
 sound/soc/sof/ipc3-topology.c |  745 ++++++++++++++++++++-
 sound/soc/sof/topology.c      | 1157 +++++----------------------------
 2 files changed, 915 insertions(+), 987 deletions(-)
diff mbox series

Patch

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 @@  static int sof_widget_unload(struct snd_soc_component *scomp,
 	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,