diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c
index 06bcae631612..9a9d82220fd0 100644
--- a/sound/soc/sof/core.c
+++ b/sound/soc/sof/core.c
@@ -208,6 +208,11 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
 	/* set up platform component driver */
 	snd_sof_new_platform_drv(sdev);
 
+	if (sdev->dspless_mode_selected) {
+		sof_set_fw_state(sdev, SOF_DSPLESS_MODE);
+		goto skip_dsp_init;
+	}
+
 	/* register any debug/trace capabilities */
 	ret = snd_sof_dbg_init(sdev);
 	if (ret < 0) {
@@ -266,6 +271,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
 		dev_dbg(sdev->dev, "SOF firmware trace disabled\n");
 	}
 
+skip_dsp_init:
 	/* hereafter all FW boot flows are for PM reasons */
 	sdev->first_boot = false;
 
@@ -387,12 +393,18 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
 		return ret;
 
 	/* check all mandatory ops */
-	if (!sof_ops(sdev) || !sof_ops(sdev)->probe || !sof_ops(sdev)->run ||
-	    !sof_ops(sdev)->block_read || !sof_ops(sdev)->block_write ||
-	    !sof_ops(sdev)->send_msg || !sof_ops(sdev)->load_firmware ||
-	    !sof_ops(sdev)->ipc_msg_data) {
+	if (!sof_ops(sdev) || !sof_ops(sdev)->probe) {
+		sof_ops_free(sdev);
+		dev_err(dev, "missing mandatory ops\n");
+		return -EINVAL;
+	}
+
+	if (!sdev->dspless_mode_selected &&
+	    (!sof_ops(sdev)->run || !sof_ops(sdev)->block_read ||
+	     !sof_ops(sdev)->block_write || !sof_ops(sdev)->send_msg ||
+	     !sof_ops(sdev)->load_firmware || !sof_ops(sdev)->ipc_msg_data)) {
 		sof_ops_free(sdev);
-		dev_err(dev, "error: missing mandatory ops\n");
+		dev_err(dev, "missing mandatory DSP ops\n");
 		return -EINVAL;
 	}
 
diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c
index ade0507328af..b42b5982cbbc 100644
--- a/sound/soc/sof/debug.c
+++ b/sound/soc/sof/debug.c
@@ -370,6 +370,7 @@ static const struct soc_fw_state_info {
 	const char *name;
 } fw_state_dbg[] = {
 	{SOF_FW_BOOT_NOT_STARTED, "SOF_FW_BOOT_NOT_STARTED"},
+	{SOF_DSPLESS_MODE, "SOF_DSPLESS_MODE"},
 	{SOF_FW_BOOT_PREPARE, "SOF_FW_BOOT_PREPARE"},
 	{SOF_FW_BOOT_IN_PROGRESS, "SOF_FW_BOOT_IN_PROGRESS"},
 	{SOF_FW_BOOT_FAILED, "SOF_FW_BOOT_FAILED"},
diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c
index d9b4633bba7a..127b68caf9e1 100644
--- a/sound/soc/sof/pcm.c
+++ b/sound/soc/sof/pcm.c
@@ -330,6 +330,11 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
 			spcm->stream[substream->stream].suspend_ignored = true;
 			return 0;
 		}
+
+		/* On suspend the DMA must be stopped in DSPless mode */
+		if (sdev->dspless_mode_selected)
+			reset_hw_params = true;
+
 		fallthrough;
 	case SNDRV_PCM_TRIGGER_STOP:
 		ipc_first = true;
@@ -705,7 +710,6 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev)
 
 	pd->pcm_construct = sof_pcm_new;
 	pd->ignore_machine = drv_name;
-	pd->be_hw_params_fixup = sof_pcm_dai_link_fixup;
 	pd->be_pcm_base = SOF_BE_PCM_BASE;
 	pd->use_dai_pcm_id = true;
 	pd->topology_name_prefix = "sof";
@@ -714,4 +718,11 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev)
 	pd->module_get_upon_open = 1;
 
 	pd->legacy_dai_naming = 1;
+
+	/*
+	 * The fixup is only needed when the DSP is in use as with the DSPless
+	 * mode we are directly using the audio interface
+	 */
+	if (!sdev->dspless_mode_selected)
+		pd->be_hw_params_fixup = sof_pcm_dai_link_fixup;
 }
diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c
index 8d3383085d12..c74ce8d414e7 100644
--- a/sound/soc/sof/pm.c
+++ b/sound/soc/sof/pm.c
@@ -103,6 +103,11 @@ static int sof_resume(struct device *dev, bool runtime_resume)
 		return ret;
 	}
 
+	if (sdev->dspless_mode_selected) {
+		sof_set_fw_state(sdev, SOF_DSPLESS_MODE);
+		return 0;
+	}
+
 	/*
 	 * Nothing further to be done for platforms that support the low power
 	 * D0 substate. Resume trace and return when resuming from
diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c
index 4f12e137ff63..7651644fcd62 100644
--- a/sound/soc/sof/sof-audio.c
+++ b/sound/soc/sof/sof-audio.c
@@ -688,7 +688,7 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
 		struct snd_sof_widget *pipe_widget;
 		struct snd_sof_pipeline *spipe;
 
-		if (!swidget)
+		if (!swidget || sdev->dspless_mode_selected)
 			continue;
 
 		spipe = swidget->spipe;
diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c
index 9017f0864cdd..d6b7caa0cf03 100644
--- a/sound/soc/sof/sof-client.c
+++ b/sound/soc/sof/sof-client.c
@@ -130,6 +130,9 @@ int sof_register_clients(struct snd_sof_dev *sdev)
 {
 	int ret;
 
+	if (sdev->dspless_mode_selected)
+		return 0;
+
 	/* Register platform independent client devices */
 	ret = sof_register_ipc_flood_test(sdev);
 	if (ret) {
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index bc8ca1e05b83..d3d536b0a8f5 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -1144,8 +1144,12 @@ static void sof_disconnect_dai_widget(struct snd_soc_component *scomp,
 static int spcm_bind(struct snd_soc_component *scomp, struct snd_sof_pcm *spcm,
 		     int dir)
 {
+	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
 	struct snd_sof_widget *host_widget;
 
+	if (sdev->dspless_mode_selected)
+		return 0;
+
 	host_widget = snd_sof_find_swidget_sname(scomp,
 						 spcm->pcm.caps[dir].name,
 						 dir);
@@ -2270,6 +2274,126 @@ static struct snd_soc_tplg_ops sof_tplg_ops = {
 	.bytes_ext_ops_count	= ARRAY_SIZE(sof_bytes_ext_ops),
 };
 
+static int snd_sof_dspless_kcontrol(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	return 0;
+}
+
+static const struct snd_soc_tplg_kcontrol_ops sof_dspless_io_ops[] = {
+	{SOF_TPLG_KCTL_VOL_ID, snd_sof_dspless_kcontrol, snd_sof_dspless_kcontrol},
+	{SOF_TPLG_KCTL_BYTES_ID, snd_sof_dspless_kcontrol, snd_sof_dspless_kcontrol},
+	{SOF_TPLG_KCTL_ENUM_ID, snd_sof_dspless_kcontrol, snd_sof_dspless_kcontrol},
+	{SOF_TPLG_KCTL_SWITCH_ID, snd_sof_dspless_kcontrol, snd_sof_dspless_kcontrol},
+};
+
+static int snd_sof_dspless_bytes_ext_get(struct snd_kcontrol *kcontrol,
+					 unsigned int __user *binary_data,
+					 unsigned int size)
+{
+	return 0;
+}
+
+static int snd_sof_dspless_bytes_ext_put(struct snd_kcontrol *kcontrol,
+					 const unsigned int __user *binary_data,
+					 unsigned int size)
+{
+	return 0;
+}
+
+static const struct snd_soc_tplg_bytes_ext_ops sof_dspless_bytes_ext_ops[] = {
+	{SOF_TPLG_KCTL_BYTES_ID, snd_sof_dspless_bytes_ext_get, snd_sof_dspless_bytes_ext_put},
+	{SOF_TPLG_KCTL_BYTES_VOLATILE_RO, snd_sof_dspless_bytes_ext_get},
+};
+
+/* external widget init - used for any driver specific init */
+static int sof_dspless_widget_ready(struct snd_soc_component *scomp, int index,
+				    struct snd_soc_dapm_widget *w,
+				    struct snd_soc_tplg_dapm_widget *tw)
+{
+	if (WIDGET_IS_DAI(w->id)) {
+		struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+		struct snd_sof_widget *swidget;
+		struct snd_sof_dai dai;
+		int ret;
+
+		swidget = kzalloc(sizeof(*swidget), GFP_KERNEL);
+		if (!swidget)
+			return -ENOMEM;
+
+		memset(&dai, 0, sizeof(dai));
+
+		ret = sof_connect_dai_widget(scomp, w, tw, &dai);
+		if (ret) {
+			kfree(swidget);
+			return ret;
+		}
+
+		swidget->scomp = scomp;
+		swidget->widget = w;
+		mutex_init(&swidget->setup_mutex);
+		w->dobj.private = swidget;
+		list_add(&swidget->list, &sdev->widget_list);
+	}
+
+	return 0;
+}
+
+static int sof_dspless_widget_unload(struct snd_soc_component *scomp,
+				     struct snd_soc_dobj *dobj)
+{
+	struct snd_soc_dapm_widget *w = container_of(dobj, struct snd_soc_dapm_widget, dobj);
+
+	if (WIDGET_IS_DAI(w->id)) {
+		struct snd_sof_widget *swidget = dobj->private;
+
+		sof_disconnect_dai_widget(scomp, w);
+
+		if (!swidget)
+			return 0;
+
+		/* remove and free swidget object */
+		list_del(&swidget->list);
+		kfree(swidget);
+	}
+
+	return 0;
+}
+
+static int sof_dspless_link_load(struct snd_soc_component *scomp, int index,
+				 struct snd_soc_dai_link *link,
+				 struct snd_soc_tplg_link_config *cfg)
+{
+	link->platforms->name = dev_name(scomp->dev);
+
+	/* Set nonatomic property for FE dai links for FE-BE compatibility */
+	if (!link->no_pcm)
+		link->nonatomic = true;
+
+	return 0;
+}
+
+static struct snd_soc_tplg_ops sof_dspless_tplg_ops = {
+	/* external widget init - used for any driver specific init */
+	.widget_ready	= sof_dspless_widget_ready,
+	.widget_unload	= sof_dspless_widget_unload,
+
+	/* FE DAI - used for any driver specific init */
+	.dai_load	= sof_dai_load,
+	.dai_unload	= sof_dai_unload,
+
+	/* DAI link - used for any driver specific init */
+	.link_load	= sof_dspless_link_load,
+
+	/* vendor specific kcontrol handlers available for binding */
+	.io_ops		= sof_dspless_io_ops,
+	.io_ops_count	= ARRAY_SIZE(sof_dspless_io_ops),
+
+	/* vendor specific bytes ext handlers available for binding */
+	.bytes_ext_ops = sof_dspless_bytes_ext_ops,
+	.bytes_ext_ops_count = ARRAY_SIZE(sof_dspless_bytes_ext_ops),
+};
+
 int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file)
 {
 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
@@ -2287,7 +2411,11 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file)
 		return ret;
 	}
 
-	ret = snd_soc_tplg_component_load(scomp, &sof_tplg_ops, fw);
+	if (sdev->dspless_mode_selected)
+		ret = snd_soc_tplg_component_load(scomp, &sof_dspless_tplg_ops, fw);
+	else
+		ret = snd_soc_tplg_component_load(scomp, &sof_tplg_ops, fw);
+
 	if (ret < 0) {
 		dev_err(scomp->dev, "error: tplg component load failed %d\n",
 			ret);
