summaryrefslogtreecommitdiff
path: root/sound/soc/sof/intel
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/sof/intel')
-rw-r--r--sound/soc/sof/intel/Kconfig7
-rw-r--r--sound/soc/sof/intel/Makefile5
-rw-r--r--sound/soc/sof/intel/apl.c4
-rw-r--r--sound/soc/sof/intel/cnl.c4
-rw-r--r--sound/soc/sof/intel/hda-common-ops.c1
-rw-r--r--sound/soc/sof/intel/hda-ctrl.c9
-rw-r--r--sound/soc/sof/intel/hda-dai-ops.c390
-rw-r--r--sound/soc/sof/intel/hda-dai.c698
-rw-r--r--sound/soc/sof/intel/hda-dsp.c101
-rw-r--r--sound/soc/sof/intel/hda-ipc.c3
-rw-r--r--sound/soc/sof/intel/hda-loader.c9
-rw-r--r--sound/soc/sof/intel/hda-mlink.c822
-rw-r--r--sound/soc/sof/intel/hda-pcm.c24
-rw-r--r--sound/soc/sof/intel/hda-stream.c93
-rw-r--r--sound/soc/sof/intel/hda.c233
-rw-r--r--sound/soc/sof/intel/hda.h69
-rw-r--r--sound/soc/sof/intel/icl.c4
-rw-r--r--sound/soc/sof/intel/mtl.c26
-rw-r--r--sound/soc/sof/intel/pci-apl.c2
-rw-r--r--sound/soc/sof/intel/pci-cnl.c3
-rw-r--r--sound/soc/sof/intel/pci-icl.c2
-rw-r--r--sound/soc/sof/intel/pci-mtl.c1
-rw-r--r--sound/soc/sof/intel/pci-skl.c2
-rw-r--r--sound/soc/sof/intel/pci-tgl.c8
-rw-r--r--sound/soc/sof/intel/tgl.c4
25 files changed, 1753 insertions, 771 deletions
diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig
index 715ba8a7f2f8..f4eeacf1f281 100644
--- a/sound/soc/sof/intel/Kconfig
+++ b/sound/soc/sof/intel/Kconfig
@@ -269,6 +269,13 @@ config SND_SOC_SOF_HDA_COMMON
select SND_INTEL_DSP_CONFIG
select SND_SOC_SOF_HDA_LINK_BASELINE
select SND_SOC_SOF_HDA_PROBES
+ select SND_SOC_SOF_HDA_MLINK if SND_SOC_SOF_HDA_LINK
+ help
+ This option is not user-selectable but automagically handled by
+ 'select' statements at a higher level.
+
+config SND_SOC_SOF_HDA_MLINK
+ tristate
help
This option is not user-selectable but automagically handled by
'select' statements at a higher level.
diff --git a/sound/soc/sof/intel/Makefile b/sound/soc/sof/intel/Makefile
index 8201cbdd654c..fdb463c12e91 100644
--- a/sound/soc/sof/intel/Makefile
+++ b/sound/soc/sof/intel/Makefile
@@ -5,10 +5,12 @@ snd-sof-acpi-intel-bdw-objs := bdw.o
snd-sof-intel-hda-common-objs := hda.o hda-loader.o hda-stream.o hda-trace.o \
hda-dsp.o hda-ipc.o hda-ctrl.o hda-pcm.o \
- hda-dai.o hda-bus.o hda-mlink.o \
+ hda-dai.o hda-dai-ops.o hda-bus.o \
skl.o hda-loader-skl.o \
apl.o cnl.o tgl.o icl.o mtl.o hda-common-ops.o
+snd-sof-intel-hda-mlink-objs := hda-mlink.o
+
snd-sof-intel-hda-common-$(CONFIG_SND_SOC_SOF_HDA_PROBES) += hda-probes.o
snd-sof-intel-hda-objs := hda-codec.o
@@ -19,6 +21,7 @@ obj-$(CONFIG_SND_SOC_SOF_INTEL_ATOM_HIFI_EP) += snd-sof-intel-atom.o
obj-$(CONFIG_SND_SOC_SOF_BAYTRAIL) += snd-sof-acpi-intel-byt.o
obj-$(CONFIG_SND_SOC_SOF_BROADWELL) += snd-sof-acpi-intel-bdw.o
obj-$(CONFIG_SND_SOC_SOF_HDA_COMMON) += snd-sof-intel-hda-common.o
+obj-$(CONFIG_SND_SOC_SOF_HDA_MLINK) += snd-sof-intel-hda-mlink.o
obj-$(CONFIG_SND_SOC_SOF_HDA) += snd-sof-intel-hda.o
snd-sof-pci-intel-tng-objs := pci-tng.o
diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c
index 0e7a7e4ad976..e1f25a8f0c32 100644
--- a/sound/soc/sof/intel/apl.c
+++ b/sound/soc/sof/intel/apl.c
@@ -48,6 +48,8 @@ int sof_apl_ops_init(struct snd_sof_dev *sdev)
/* debug */
sof_apl_ops.ipc_dump = hda_ipc_dump;
+
+ sof_apl_ops.set_power_state = hda_dsp_set_power_state_ipc3;
}
if (sdev->pdata->ipc_type == SOF_INTEL_IPC4) {
@@ -73,6 +75,8 @@ int sof_apl_ops_init(struct snd_sof_dev *sdev)
/* debug */
sof_apl_ops.ipc_dump = hda_ipc4_dump;
+
+ sof_apl_ops.set_power_state = hda_dsp_set_power_state_ipc4;
}
/* set DAI driver ops */
diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c
index a08a77fa946b..a95222e53ecf 100644
--- a/sound/soc/sof/intel/cnl.c
+++ b/sound/soc/sof/intel/cnl.c
@@ -395,6 +395,8 @@ int sof_cnl_ops_init(struct snd_sof_dev *sdev)
/* debug */
sof_cnl_ops.ipc_dump = cnl_ipc_dump;
+
+ sof_cnl_ops.set_power_state = hda_dsp_set_power_state_ipc3;
}
if (sdev->pdata->ipc_type == SOF_INTEL_IPC4) {
@@ -420,6 +422,8 @@ int sof_cnl_ops_init(struct snd_sof_dev *sdev)
/* debug */
sof_cnl_ops.ipc_dump = cnl_ipc4_dump;
+
+ sof_cnl_ops.set_power_state = hda_dsp_set_power_state_ipc4;
}
/* set DAI driver ops */
diff --git a/sound/soc/sof/intel/hda-common-ops.c b/sound/soc/sof/intel/hda-common-ops.c
index 397303b3ac9d..8e1cd0babd32 100644
--- a/sound/soc/sof/intel/hda-common-ops.c
+++ b/sound/soc/sof/intel/hda-common-ops.c
@@ -89,7 +89,6 @@ struct snd_sof_dsp_ops sof_hda_common_ops = {
.runtime_resume = hda_dsp_runtime_resume,
.runtime_idle = hda_dsp_runtime_idle,
.set_hw_params_upon_resume = hda_dsp_set_hw_params_upon_resume,
- .set_power_state = hda_dsp_set_power_state,
/* ALSA HW info flags */
.hw_info = SNDRV_PCM_INFO_MMAP |
diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c
index f3bdeba28412..84bf01bd360a 100644
--- a/sound/soc/sof/intel/hda-ctrl.c
+++ b/sound/soc/sof/intel/hda-ctrl.c
@@ -19,6 +19,7 @@
#include <sound/hdaudio_ext.h>
#include <sound/hda_register.h>
#include <sound/hda_component.h>
+#include <sound/hda-mlink.h>
#include "../ops.h"
#include "hda.h"
@@ -158,16 +159,18 @@ void hda_dsp_ctrl_misc_clock_gating(struct snd_sof_dev *sdev, bool enable)
*/
int hda_dsp_ctrl_clock_power_gating(struct snd_sof_dev *sdev, bool enable)
{
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
u32 val;
/* enable/disable audio dsp clock gating */
val = enable ? PCI_CGCTL_ADSPDCGE : 0;
snd_sof_pci_update_bits(sdev, PCI_CGCTL, PCI_CGCTL_ADSPDCGE, val);
- /* enable/disable DMI Link L1 support */
+ /* disable the DMI link when requested. But enable only if it wasn't disabled previously */
val = enable ? HDA_VS_INTEL_EM2_L1SEN : 0;
- snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_EM2,
- HDA_VS_INTEL_EM2_L1SEN, val);
+ if (!enable || !hda->l1_disabled)
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_EM2,
+ HDA_VS_INTEL_EM2_L1SEN, val);
/* enable/disable audio dsp power gating */
val = enable ? 0 : PCI_PGCTL_ADSPPGD;
diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c
new file mode 100644
index 000000000000..4b39cecacd68
--- /dev/null
+++ b/sound/soc/sof/intel/hda-dai-ops.c
@@ -0,0 +1,390 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2022 Intel Corporation. All rights reserved.
+
+#include <sound/pcm_params.h>
+#include <sound/hdaudio_ext.h>
+#include <sound/sof/ipc4/header.h>
+#include <uapi/sound/sof/header.h>
+#include "../ipc4-priv.h"
+#include "../ipc4-topology.h"
+#include "../sof-priv.h"
+#include "../sof-audio.h"
+#include "hda.h"
+
+/* These ops are only applicable for the HDA DAI's in their current form */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
+/*
+ * This function checks if the host dma channel corresponding
+ * to the link DMA stream_tag argument is assigned to one
+ * of the FEs connected to the BE DAI.
+ */
+static bool hda_check_fes(struct snd_soc_pcm_runtime *rtd,
+ int dir, int stream_tag)
+{
+ struct snd_pcm_substream *fe_substream;
+ struct hdac_stream *fe_hstream;
+ struct snd_soc_dpcm *dpcm;
+
+ for_each_dpcm_fe(rtd, dir, dpcm) {
+ fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, dir);
+ fe_hstream = fe_substream->runtime->private_data;
+ if (fe_hstream->stream_tag == stream_tag)
+ return true;
+ }
+
+ return false;
+}
+
+static struct hdac_ext_stream *
+hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct sof_intel_hda_stream *hda_stream;
+ const struct sof_intel_dsp_desc *chip;
+ struct snd_sof_dev *sdev;
+ struct hdac_ext_stream *res = NULL;
+ struct hdac_stream *hstream = NULL;
+
+ int stream_dir = substream->stream;
+
+ if (!bus->ppcap) {
+ dev_err(bus->dev, "stream type not supported\n");
+ return NULL;
+ }
+
+ spin_lock_irq(&bus->reg_lock);
+ list_for_each_entry(hstream, &bus->stream_list, list) {
+ struct hdac_ext_stream *hext_stream =
+ stream_to_hdac_ext_stream(hstream);
+ if (hstream->direction != substream->stream)
+ continue;
+
+ hda_stream = hstream_to_sof_hda_stream(hext_stream);
+ sdev = hda_stream->sdev;
+ chip = get_chip_info(sdev->pdata);
+
+ /* check if link is available */
+ if (!hext_stream->link_locked) {
+ /*
+ * choose the first available link for platforms that do not have the
+ * PROCEN_FMT_QUIRK set.
+ */
+ if (!(chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK)) {
+ res = hext_stream;
+ break;
+ }
+
+ if (hstream->opened) {
+ /*
+ * check if the stream tag matches the stream
+ * tag of one of the connected FEs
+ */
+ if (hda_check_fes(rtd, stream_dir,
+ hstream->stream_tag)) {
+ res = hext_stream;
+ break;
+ }
+ } else {
+ res = hext_stream;
+
+ /*
+ * This must be a hostless stream.
+ * So reserve the host DMA channel.
+ */
+ hda_stream->host_reserved = 1;
+ break;
+ }
+ }
+ }
+
+ if (res) {
+ /* Make sure that host and link DMA is decoupled. */
+ snd_hdac_ext_stream_decouple_locked(bus, res, true);
+
+ res->link_locked = 1;
+ res->link_substream = substream;
+ }
+ spin_unlock_irq(&bus->reg_lock);
+
+ return res;
+}
+
+static struct hdac_ext_stream *hda_get_hext_stream(struct snd_sof_dev *sdev,
+ struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream)
+{
+ return snd_soc_dai_get_dma_data(cpu_dai, substream);
+}
+
+static struct hdac_ext_stream *hda_assign_hext_stream(struct snd_sof_dev *sdev,
+ struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream)
+{
+ struct hdac_ext_stream *hext_stream;
+
+ hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream);
+ if (!hext_stream)
+ return NULL;
+
+ snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)hext_stream);
+
+ return hext_stream;
+}
+
+static void hda_release_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream)
+{
+ struct hdac_ext_stream *hext_stream = hda_get_hext_stream(sdev, cpu_dai, substream);
+
+ snd_soc_dai_set_dma_data(cpu_dai, substream, NULL);
+ snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK);
+}
+
+static void hda_setup_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream,
+ unsigned int format_val)
+{
+ snd_hdac_ext_stream_setup(hext_stream, format_val);
+}
+
+static void hda_reset_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream)
+{
+ snd_hdac_ext_stream_reset(hext_stream);
+}
+
+static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_sof_widget *pipe_widget;
+ struct sof_ipc4_pipeline *pipeline;
+ struct snd_sof_widget *swidget;
+ struct snd_soc_dapm_widget *w;
+ int ret;
+
+ w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
+ swidget = w->dobj.private;
+ pipe_widget = swidget->spipe->pipe_widget;
+ pipeline = pipe_widget->private;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
+ SOF_IPC4_PIPE_PAUSED);
+ if (ret < 0)
+ return ret;
+
+ pipeline->state = SOF_IPC4_PIPE_PAUSED;
+ break;
+ default:
+ dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int hda_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ snd_hdac_ext_stream_start(hext_stream);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ snd_hdac_ext_stream_clear(hext_stream);
+ break;
+ default:
+ dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_sof_widget *pipe_widget;
+ struct sof_ipc4_pipeline *pipeline;
+ struct snd_sof_widget *swidget;
+ struct snd_soc_dapm_widget *w;
+ int ret;
+
+ w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
+ swidget = w->dobj.private;
+ pipe_widget = swidget->spipe->pipe_widget;
+ pipeline = pipe_widget->private;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (pipeline->state != SOF_IPC4_PIPE_PAUSED) {
+ ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
+ SOF_IPC4_PIPE_PAUSED);
+ if (ret < 0)
+ return ret;
+ pipeline->state = SOF_IPC4_PIPE_PAUSED;
+ }
+
+ ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
+ SOF_IPC4_PIPE_RUNNING);
+ if (ret < 0)
+ return ret;
+ pipeline->state = SOF_IPC4_PIPE_RUNNING;
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ {
+ ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
+ SOF_IPC4_PIPE_RESET);
+ if (ret < 0)
+ return ret;
+
+ pipeline->state = SOF_IPC4_PIPE_RESET;
+ break;
+ }
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ break;
+ default:
+ dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct hda_dai_widget_dma_ops hda_ipc4_dma_ops = {
+ .get_hext_stream = hda_get_hext_stream,
+ .assign_hext_stream = hda_assign_hext_stream,
+ .release_hext_stream = hda_release_hext_stream,
+ .setup_hext_stream = hda_setup_hext_stream,
+ .reset_hext_stream = hda_reset_hext_stream,
+ .pre_trigger = hda_ipc4_pre_trigger,
+ .trigger = hda_trigger,
+ .post_trigger = hda_ipc4_post_trigger
+};
+
+static const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = {
+ .get_hext_stream = hda_get_hext_stream,
+ .assign_hext_stream = hda_assign_hext_stream,
+ .release_hext_stream = hda_release_hext_stream,
+ .setup_hext_stream = hda_setup_hext_stream,
+ .reset_hext_stream = hda_reset_hext_stream,
+ .trigger = hda_trigger,
+};
+
+static int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ {
+ struct snd_sof_dai_config_data data = { 0 };
+
+ data.dai_data = DMA_CHAN_INVALID;
+ return hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_HW_FREE, &data);
+ }
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ return hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_PAUSE, NULL);
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct hda_dai_widget_dma_ops hda_ipc3_dma_ops = {
+ .get_hext_stream = hda_get_hext_stream,
+ .assign_hext_stream = hda_assign_hext_stream,
+ .release_hext_stream = hda_release_hext_stream,
+ .setup_hext_stream = hda_setup_hext_stream,
+ .reset_hext_stream = hda_reset_hext_stream,
+ .trigger = hda_trigger,
+ .post_trigger = hda_ipc3_post_trigger,
+};
+
+static struct hdac_ext_stream *
+hda_dspless_get_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream)
+{
+ struct hdac_stream *hstream = substream->runtime->private_data;
+
+ return stream_to_hdac_ext_stream(hstream);
+}
+
+static void hda_dspless_setup_hext_stream(struct snd_sof_dev *sdev,
+ struct hdac_ext_stream *hext_stream,
+ unsigned int format_val)
+{
+ /*
+ * Save the format_val which was adjusted by the maxbps of the codec.
+ * This information is not available on the FE side since there we are
+ * using dummy_codec.
+ */
+ hext_stream->hstream.format_val = format_val;
+}
+
+static const struct hda_dai_widget_dma_ops hda_dspless_dma_ops = {
+ .get_hext_stream = hda_dspless_get_hext_stream,
+ .setup_hext_stream = hda_dspless_setup_hext_stream,
+};
+
+#endif
+
+const struct hda_dai_widget_dma_ops *
+hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
+{
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
+ struct snd_sof_dai *sdai;
+
+ if (sdev->dspless_mode_selected)
+ return &hda_dspless_dma_ops;
+
+ sdai = swidget->private;
+
+ switch (sdev->pdata->ipc_type) {
+ case SOF_IPC:
+ {
+ struct sof_dai_private_data *private = sdai->private;
+
+ if (private->dai_config->type == SOF_DAI_INTEL_HDA)
+ return &hda_ipc3_dma_ops;
+ break;
+ }
+ case SOF_INTEL_IPC4:
+ {
+ struct sof_ipc4_copier *ipc4_copier = sdai->private;
+
+ if (ipc4_copier->dai_type == SOF_DAI_INTEL_HDA) {
+ struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
+ struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
+
+ if (pipeline->use_chain_dma)
+ return &hda_ipc4_chain_dma_ops;
+
+ return &hda_ipc4_dma_ops;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+#endif
+ return NULL;
+}
diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c
index 8d9c38d562d3..44a5d94c5050 100644
--- a/sound/soc/sof/intel/hda-dai.c
+++ b/sound/soc/sof/intel/hda-dai.c
@@ -27,119 +27,77 @@ static bool hda_use_tplg_nhlt;
module_param_named(sof_use_tplg_nhlt, hda_use_tplg_nhlt, bool, 0444);
MODULE_PARM_DESC(sof_use_tplg_nhlt, "SOF topology nhlt override");
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
+int hda_dai_config(struct snd_soc_dapm_widget *w, unsigned int flags,
+ struct snd_sof_dai_config_data *data)
+{
+ struct snd_sof_widget *swidget = w->dobj.private;
+ const struct sof_ipc_tplg_ops *tplg_ops;
+ struct snd_soc_component *component;
+ struct snd_sof_dev *sdev;
+ int ret;
-struct hda_pipe_params {
- u32 ch;
- u32 s_freq;
- snd_pcm_format_t format;
- int link_index;
- unsigned int link_bps;
-};
+ if (!swidget)
+ return 0;
-/*
- * This function checks if the host dma channel corresponding
- * to the link DMA stream_tag argument is assigned to one
- * of the FEs connected to the BE DAI.
- */
-static bool hda_check_fes(struct snd_soc_pcm_runtime *rtd,
- int dir, int stream_tag)
-{
- struct snd_pcm_substream *fe_substream;
- struct hdac_stream *fe_hstream;
- struct snd_soc_dpcm *dpcm;
-
- for_each_dpcm_fe(rtd, dir, dpcm) {
- fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, dir);
- fe_hstream = fe_substream->runtime->private_data;
- if (fe_hstream->stream_tag == stream_tag)
- return true;
+ component = swidget->scomp;
+ sdev = snd_soc_component_get_drvdata(component);
+ tplg_ops = sof_ipc_get_ops(sdev, tplg);
+
+ if (tplg_ops && tplg_ops->dai_config) {
+ ret = tplg_ops->dai_config(sdev, swidget, flags, data);
+ if (ret < 0) {
+ dev_err(sdev->dev, "DAI config with flags %x failed for widget %s\n",
+ flags, w->name);
+ return ret;
+ }
}
- return false;
+ return 0;
}
-static struct hdac_ext_stream *
-hda_link_stream_assign(struct hdac_bus *bus,
- struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct sof_intel_hda_stream *hda_stream;
- const struct sof_intel_dsp_desc *chip;
- struct snd_sof_dev *sdev;
- struct hdac_ext_stream *res = NULL;
- struct hdac_stream *hstream = NULL;
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
- int stream_dir = substream->stream;
+static const struct hda_dai_widget_dma_ops *
+hda_dai_get_ops(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai)
+{
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component);
+ struct snd_sof_widget *swidget = w->dobj.private;
+ struct snd_sof_dai *sdai;
- if (!bus->ppcap) {
- dev_err(bus->dev, "stream type not supported\n");
- return NULL;
- }
+ /*
+ * The swidget parameter of hda_select_dai_widget_ops() is ignored in
+ * case of DSPless mode
+ */
+ if (sdev->dspless_mode_selected)
+ return hda_select_dai_widget_ops(sdev, NULL);
- spin_lock_irq(&bus->reg_lock);
- list_for_each_entry(hstream, &bus->stream_list, list) {
- struct hdac_ext_stream *hext_stream =
- stream_to_hdac_ext_stream(hstream);
- if (hstream->direction != substream->stream)
- continue;
-
- hda_stream = hstream_to_sof_hda_stream(hext_stream);
- sdev = hda_stream->sdev;
- chip = get_chip_info(sdev->pdata);
-
- /* check if link is available */
- if (!hext_stream->link_locked) {
- /*
- * choose the first available link for platforms that do not have the
- * PROCEN_FMT_QUIRK set.
- */
- if (!(chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK)) {
- res = hext_stream;
- break;
- }
+ sdai = swidget->private;
- if (hstream->opened) {
- /*
- * check if the stream tag matches the stream
- * tag of one of the connected FEs
- */
- if (hda_check_fes(rtd, stream_dir,
- hstream->stream_tag)) {
- res = hext_stream;
- break;
- }
- } else {
- res = hext_stream;
-
- /*
- * This must be a hostless stream.
- * So reserve the host DMA channel.
- */
- hda_stream->host_reserved = 1;
- break;
- }
- }
- }
+ /* select and set the DAI widget ops if not set already */
+ if (!sdai->platform_private) {
+ const struct hda_dai_widget_dma_ops *ops =
+ hda_select_dai_widget_ops(sdev, swidget);
+ if (!ops)
+ return NULL;
- if (res) {
- /* Make sure that host and link DMA is decoupled. */
- snd_hdac_ext_stream_decouple_locked(bus, res, true);
+ /* check if mandatory ops are set */
+ if (!ops || !ops->get_hext_stream)
+ return NULL;
- res->link_locked = 1;
- res->link_substream = substream;
+ sdai->platform_private = ops;
}
- spin_unlock_irq(&bus->reg_lock);
- return res;
+ return sdai->platform_private;
}
static int hda_link_dma_cleanup(struct snd_pcm_substream *substream,
struct hdac_ext_stream *hext_stream,
struct snd_soc_dai *cpu_dai,
- struct snd_soc_dai *codec_dai,
- bool trigger_suspend_stop)
+ struct snd_soc_dai *codec_dai)
{
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component);
+ const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai);
struct hdac_stream *hstream = &hext_stream->hstream;
struct hdac_bus *bus = hstream->bus;
struct sof_intel_hda_stream *hda_stream;
@@ -150,15 +108,14 @@ static int hda_link_dma_cleanup(struct snd_pcm_substream *substream,
if (!hlink)
return -EINVAL;
- if (trigger_suspend_stop)
- snd_hdac_ext_stream_clear(hext_stream);
-
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
stream_tag = hdac_stream(hext_stream)->stream_tag;
snd_hdac_ext_bus_link_clear_stream_id(hlink, stream_tag);
}
- snd_soc_dai_set_dma_data(cpu_dai, substream, NULL);
- snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK);
+
+ if (ops->release_hext_stream)
+ ops->release_hext_stream(sdev, cpu_dai, substream);
+
hext_stream->link_prepared = 0;
/* free the host DMA channel reserved by hostless streams */
@@ -168,50 +125,20 @@ static int hda_link_dma_cleanup(struct snd_pcm_substream *substream,
return 0;
}
-static int hda_link_dma_params(struct hdac_ext_stream *hext_stream,
- struct hda_pipe_params *params)
-{
- struct hdac_stream *hstream = &hext_stream->hstream;
- unsigned char stream_tag = hstream->stream_tag;
- struct hdac_bus *bus = hstream->bus;
- struct hdac_ext_link *hlink;
- unsigned int format_val;
-
- snd_hdac_ext_stream_reset(hext_stream);
-
- format_val = snd_hdac_calc_stream_format(params->s_freq, params->ch,
- params->format,
- params->link_bps, 0);
-
- dev_dbg(bus->dev, "format_val=%d, rate=%d, ch=%d, format=%d\n",
- format_val, params->s_freq, params->ch, params->format);
-
- snd_hdac_ext_stream_setup(hext_stream, format_val);
-
- if (hext_stream->hstream.direction == SNDRV_PCM_STREAM_PLAYBACK) {
- list_for_each_entry(hlink, &bus->hlink_list, list) {
- if (hlink->index == params->link_index)
- snd_hdac_ext_bus_link_set_stream_id(hlink,
- stream_tag);
- }
- }
-
- hext_stream->link_prepared = 1;
-
- return 0;
-}
-
static int hda_link_dma_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai)
{
+ const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai);
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
- struct hda_pipe_params p_params = {0};
struct hdac_ext_stream *hext_stream;
+ struct hdac_stream *hstream;
struct hdac_ext_link *hlink;
struct snd_sof_dev *sdev;
struct hdac_bus *bus;
+ unsigned int format_val;
+ unsigned int link_bps;
+ int stream_tag;
sdev = snd_soc_component_get_drvdata(cpu_dai->component);
bus = sof_to_bus(sdev);
@@ -220,337 +147,196 @@ static int hda_link_dma_hw_params(struct snd_pcm_substream *substream,
if (!hlink)
return -EINVAL;
- hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
- if (!hext_stream) {
- hext_stream = hda_link_stream_assign(bus, substream);
- if (!hext_stream)
- return -EBUSY;
+ hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
- snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)hext_stream);
+ if (!hext_stream) {
+ if (ops->assign_hext_stream)
+ hext_stream = ops->assign_hext_stream(sdev, cpu_dai, substream);
}
+ if (!hext_stream)
+ return -EBUSY;
+
+ hstream = &hext_stream->hstream;
+ stream_tag = hstream->stream_tag;
+
+ if (hext_stream->hstream.direction == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_hdac_ext_bus_link_set_stream_id(hlink, stream_tag);
+
/* set the hdac_stream in the codec dai */
snd_soc_dai_set_stream(codec_dai, hdac_stream(hext_stream), substream->stream);
- p_params.ch = params_channels(params);
- p_params.s_freq = params_rate(params);
- p_params.link_index = hlink->index;
- p_params.format = params_format(params);
-
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- p_params.link_bps = codec_dai->driver->playback.sig_bits;
+ link_bps = codec_dai->driver->playback.sig_bits;
else
- p_params.link_bps = codec_dai->driver->capture.sig_bits;
+ link_bps = codec_dai->driver->capture.sig_bits;
- return hda_link_dma_params(hext_stream, &p_params);
-}
+ if (ops->reset_hext_stream)
+ ops->reset_hext_stream(sdev, hext_stream);
-static int hda_link_dma_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- int stream = substream->stream;
+ format_val = snd_hdac_calc_stream_format(params_rate(params), params_channels(params),
+ params_format(params), link_bps, 0);
- return hda_link_dma_hw_params(substream, &rtd->dpcm[stream].hw_params);
-}
+ dev_dbg(bus->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val,
+ params_rate(params), params_channels(params), params_format(params));
-static int hda_link_dma_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
- struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
- struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
- int ret;
+ if (ops->setup_hext_stream)
+ ops->setup_hext_stream(sdev, hext_stream, format_val);
- if (!hext_stream)
- return 0;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- snd_hdac_ext_stream_start(hext_stream);
- break;
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_STOP:
- ret = hda_link_dma_cleanup(substream, hext_stream, cpu_dai, codec_dai, true);
- if (ret < 0)
- return ret;
-
- break;
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- snd_hdac_ext_stream_clear(hext_stream);
+ hext_stream->link_prepared = 1;
- break;
- default:
- return -EINVAL;
- }
return 0;
}
-static int hda_link_dma_hw_free(struct snd_pcm_substream *substream)
+static int hda_link_dma_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
- struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
- struct hdac_ext_stream *hext_stream;
-
- hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
- if (!hext_stream)
- return 0;
-
- return hda_link_dma_cleanup(substream, hext_stream, cpu_dai, codec_dai, false);
-}
-
-static int hda_dai_widget_update(struct snd_soc_dapm_widget *w,
- int channel, bool widget_setup)
-{
- struct snd_sof_dai_config_data data;
-
- data.dai_data = channel;
-
- /* set up/free DAI widget and send DAI_CONFIG IPC */
- if (widget_setup)
- return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_2_STEP_STOP, &data);
+ int stream = substream->stream;
- return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE, &data);
+ return hda_link_dma_hw_params(substream, &rtd->dpcm[stream].hw_params, cpu_dai);
}
-static int hda_dai_hw_params_update(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
+static int hda_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai)
{
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component);
+ const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai);
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
struct hdac_ext_stream *hext_stream;
- struct snd_soc_dapm_widget *w;
- int stream_tag;
- hext_stream = snd_soc_dai_get_dma_data(dai, substream);
- if (!hext_stream)
+ if (!ops) {
+ dev_err(sdev->dev, "DAI widget ops not set\n");
return -EINVAL;
+ }
- stream_tag = hdac_stream(hext_stream)->stream_tag;
-
- w = snd_soc_dai_get_widget(dai, substream->stream);
+ hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
+ if (!hext_stream)
+ return 0;
- /* set up the DAI widget and send the DAI_CONFIG with the new tag */
- return hda_dai_widget_update(w, stream_tag - 1, true);
+ return hda_link_dma_cleanup(substream, hext_stream, cpu_dai, codec_dai);
}
static int hda_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct hdac_ext_stream *hext_stream =
- snd_soc_dai_get_dma_data(dai, substream);
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, substream->stream);
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
+ const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai);
+ struct hdac_ext_stream *hext_stream;
+ struct snd_sof_dai_config_data data = { 0 };
+ unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
int ret;
+ if (!ops) {
+ dev_err(sdev->dev, "DAI widget ops not set\n");
+ return -EINVAL;
+ }
+
+ hext_stream = ops->get_hext_stream(sdev, dai, substream);
if (hext_stream && hext_stream->link_prepared)
return 0;
- ret = hda_link_dma_hw_params(substream, params);
+ ret = hda_link_dma_hw_params(substream, params, dai);
if (ret < 0)
return ret;
- return hda_dai_hw_params_update(substream, params, dai);
-}
-
-
-static int hda_dai_config_pause_push_ipc(struct snd_soc_dapm_widget *w)
-{
- struct snd_sof_widget *swidget = w->dobj.private;
- struct snd_soc_component *component = swidget->scomp;
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
- int ret = 0;
+ hext_stream = ops->get_hext_stream(sdev, dai, substream);
- if (tplg_ops->dai_config) {
- ret = tplg_ops->dai_config(sdev, swidget, SOF_DAI_CONFIG_FLAGS_PAUSE, NULL);
- if (ret < 0)
- dev_err(sdev->dev, "%s: DAI config failed for widget %s\n", __func__,
- w->name);
- }
+ flags |= SOF_DAI_CONFIG_FLAGS_2_STEP_STOP << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
+ data.dai_data = hdac_stream(hext_stream)->stream_tag - 1;
- return ret;
+ return hda_dai_config(w, flags, &data);
}
static int hda_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
- struct hdac_ext_stream *hext_stream =
- snd_soc_dai_get_dma_data(dai, substream);
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, substream->stream);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- int stream = substream->stream;
+ const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai);
+ struct hdac_ext_stream *hext_stream;
+ struct snd_sof_dai_config_data data = { 0 };
+ unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
int ret;
+ hext_stream = ops->get_hext_stream(sdev, dai, substream);
if (hext_stream && hext_stream->link_prepared)
return 0;
dev_dbg(sdev->dev, "prepare stream dir %d\n", substream->stream);
- ret = hda_link_dma_prepare(substream);
- if (ret < 0)
- return ret;
-
- return hda_dai_hw_params_update(substream, &rtd->dpcm[stream].hw_params, dai);
-}
-
-static int hda_dai_hw_free_ipc(int stream, /* direction */
- struct snd_soc_dai *dai)
-{
- struct snd_soc_dapm_widget *w;
-
- w = snd_soc_dai_get_widget(dai, stream);
-
- /* free the link DMA channel in the FW and the DAI widget */
- return hda_dai_widget_update(w, DMA_CHAN_INVALID, false);
-}
-
-static int ipc3_hda_dai_trigger(struct snd_pcm_substream *substream,
- int cmd, struct snd_soc_dai *dai)
-{
- struct snd_soc_dapm_widget *w;
- int ret;
-
- dev_dbg(dai->dev, "cmd=%d dai %s direction %d\n", cmd,
- dai->name, substream->stream);
-
- ret = hda_link_dma_trigger(substream, cmd);
+ ret = hda_link_dma_prepare(substream, dai);
if (ret < 0)
return ret;
- w = snd_soc_dai_get_widget(dai, substream->stream);
+ hext_stream = ops->get_hext_stream(sdev, dai, substream);
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_STOP:
- /*
- * free DAI widget during stop/suspend to keep widget use_count's balanced.
- */
- ret = hda_dai_hw_free_ipc(substream->stream, dai);
- if (ret < 0)
- return ret;
-
- break;
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- ret = hda_dai_config_pause_push_ipc(w);
- if (ret < 0)
- return ret;
- break;
+ flags |= SOF_DAI_CONFIG_FLAGS_2_STEP_STOP << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
+ data.dai_data = hdac_stream(hext_stream)->stream_tag - 1;
- default:
- break;
- }
- return 0;
+ return hda_dai_config(w, flags, &data);
}
/*
* In contrast to IPC3, the dai trigger in IPC4 mixes pipeline state changes
* (over IPC channel) and DMA state change (direct host register changes).
*/
-static int ipc4_hda_dai_trigger(struct snd_pcm_substream *substream,
- int cmd, struct snd_soc_dai *dai)
+static int hda_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
{
- struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(dai, substream);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
- struct snd_sof_widget *pipe_widget;
- struct sof_ipc4_pipeline *pipeline;
+ const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai);
+ struct hdac_ext_stream *hext_stream;
struct snd_soc_pcm_runtime *rtd;
- struct snd_sof_widget *swidget;
- struct snd_soc_dapm_widget *w;
struct snd_soc_dai *codec_dai;
- struct snd_soc_dai *cpu_dai;
int ret;
dev_dbg(dai->dev, "cmd=%d dai %s direction %d\n", cmd,
dai->name, substream->stream);
+ hext_stream = ops->get_hext_stream(sdev, dai, substream);
+ if (!hext_stream)
+ return -EINVAL;
+
rtd = asoc_substream_to_rtd(substream);
- cpu_dai = asoc_rtd_to_cpu(rtd, 0);
codec_dai = asoc_rtd_to_codec(rtd, 0);
- w = snd_soc_dai_get_widget(dai, substream->stream);
- swidget = w->dobj.private;
- pipe_widget = swidget->spipe->pipe_widget;
- pipeline = pipe_widget->private;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- snd_hdac_ext_stream_start(hext_stream);
- if (pipeline->state != SOF_IPC4_PIPE_PAUSED) {
- ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
- SOF_IPC4_PIPE_PAUSED);
- if (ret < 0)
- return ret;
- pipeline->state = SOF_IPC4_PIPE_PAUSED;
- }
-
- ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
- SOF_IPC4_PIPE_RUNNING);
+ if (ops->pre_trigger) {
+ ret = ops->pre_trigger(sdev, dai, substream, cmd);
if (ret < 0)
return ret;
- pipeline->state = SOF_IPC4_PIPE_RUNNING;
- break;
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_STOP:
- {
- ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
- SOF_IPC4_PIPE_PAUSED);
+ }
+
+ if (ops->trigger) {
+ ret = ops->trigger(sdev, dai, substream, cmd);
if (ret < 0)
return ret;
+ }
- pipeline->state = SOF_IPC4_PIPE_PAUSED;
-
- snd_hdac_ext_stream_clear(hext_stream);
-
- ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
- SOF_IPC4_PIPE_RESET);
+ if (ops->post_trigger) {
+ ret = ops->post_trigger(sdev, dai, substream, cmd);
if (ret < 0)
return ret;
+ }
- pipeline->state = SOF_IPC4_PIPE_RESET;
-
- ret = hda_link_dma_cleanup(substream, hext_stream, cpu_dai, codec_dai, false);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ ret = hda_link_dma_cleanup(substream, hext_stream, dai, codec_dai);
if (ret < 0) {
dev_err(sdev->dev, "%s: failed to clean up link DMA\n", __func__);
return ret;
}
break;
- }
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- {
- ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
- SOF_IPC4_PIPE_PAUSED);
- if (ret < 0)
- return ret;
-
- pipeline->state = SOF_IPC4_PIPE_PAUSED;
-
- snd_hdac_ext_stream_clear(hext_stream);
- break;
- }
default:
- dev_err(sdev->dev, "%s: unknown trigger command %d\n", __func__, cmd);
- return -EINVAL;
+ break;
}
return 0;
}
-static int hda_dai_hw_free(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- int ret;
-
- ret = hda_link_dma_hw_free(substream);
- if (ret < 0)
- return ret;
-
- return hda_dai_hw_free_ipc(substream->stream, dai);
-}
-
-static const struct snd_soc_dai_ops ipc3_hda_dai_ops = {
+static const struct snd_soc_dai_ops hda_dai_ops = {
.hw_params = hda_dai_hw_params,
.hw_free = hda_dai_hw_free,
- .trigger = ipc3_hda_dai_trigger,
+ .trigger = hda_dai_trigger,
.prepare = hda_dai_prepare,
};
@@ -573,186 +359,62 @@ static int hda_dai_suspend(struct hdac_bus *bus)
* explicitly during suspend.
*/
if (hext_stream->link_substream) {
- struct snd_soc_dai *cpu_dai;
+ const struct hda_dai_widget_dma_ops *ops;
+ struct snd_sof_widget *swidget;
+ struct snd_soc_dapm_widget *w;
struct snd_soc_dai *codec_dai;
+ struct snd_soc_dai *cpu_dai;
+ struct snd_sof_dev *sdev;
+ struct snd_sof_dai *sdai;
rtd = asoc_substream_to_rtd(hext_stream->link_substream);
cpu_dai = asoc_rtd_to_cpu(rtd, 0);
codec_dai = asoc_rtd_to_codec(rtd, 0);
+ w = snd_soc_dai_get_widget(cpu_dai, hdac_stream(hext_stream)->direction);
+ swidget = w->dobj.private;
+ sdev = snd_soc_component_get_drvdata(swidget->scomp);
+ sdai = swidget->private;
+ ops = sdai->platform_private;
ret = hda_link_dma_cleanup(hext_stream->link_substream,
hext_stream,
- cpu_dai, codec_dai, false);
+ cpu_dai, codec_dai);
if (ret < 0)
return ret;
- /* for consistency with TRIGGER_SUSPEND we free DAI resources */
- ret = hda_dai_hw_free_ipc(hdac_stream(hext_stream)->direction, cpu_dai);
- if (ret < 0)
- return ret;
+ /* for consistency with TRIGGER_SUSPEND */
+ if (ops->post_trigger) {
+ ret = ops->post_trigger(sdev, cpu_dai,
+ hext_stream->link_substream,
+ SNDRV_PCM_TRIGGER_SUSPEND);
+ if (ret < 0)
+ return ret;
+ }
}
}
return 0;
}
-static const struct snd_soc_dai_ops ipc4_hda_dai_ops = {
- .hw_params = hda_dai_hw_params,
- .hw_free = hda_dai_hw_free,
- .trigger = ipc4_hda_dai_trigger,
- .prepare = hda_dai_prepare,
-};
-
#endif
-/* only one flag used so far to harden hw_params/hw_free/trigger/prepare */
-struct ssp_dai_dma_data {
- bool setup;
-};
-
-static int ssp_dai_setup_or_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai,
- bool setup)
-{
- struct snd_soc_dapm_widget *w;
-
- w = snd_soc_dai_get_widget(dai, substream->stream);
-
- if (setup)
- return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE, NULL);
-
- return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE, NULL);
-}
-
-static int ssp_dai_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct ssp_dai_dma_data *dma_data;
-
- dma_data = kzalloc(sizeof(*dma_data), GFP_KERNEL);
- if (!dma_data)
- return -ENOMEM;
-
- snd_soc_dai_set_dma_data(dai, substream, dma_data);
-
- return 0;
-}
-
-static int ssp_dai_setup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai,
- bool setup)
-{
- struct ssp_dai_dma_data *dma_data;
- int ret = 0;
-
- dma_data = snd_soc_dai_get_dma_data(dai, substream);
- if (!dma_data) {
- dev_err(dai->dev, "%s: failed to get dma_data\n", __func__);
- return -EIO;
- }
-
- if (dma_data->setup != setup) {
- ret = ssp_dai_setup_or_free(substream, dai, setup);
- if (!ret)
- dma_data->setup = setup;
- }
- return ret;
-}
-
-static int ssp_dai_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- /* params are ignored for now */
- return ssp_dai_setup(substream, dai, true);
-}
-
-static int ssp_dai_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- /*
- * the SSP will only be reconfigured during resume operations and
- * not in case of xruns
- */
- return ssp_dai_setup(substream, dai, true);
-}
-
-static int ipc3_ssp_dai_trigger(struct snd_pcm_substream *substream,
- int cmd, struct snd_soc_dai *dai)
-{
- if (cmd != SNDRV_PCM_TRIGGER_SUSPEND)
- return 0;
-
- return ssp_dai_setup(substream, dai, false);
-}
-
-static int ssp_dai_hw_free(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- return ssp_dai_setup(substream, dai, false);
-}
-
-static void ssp_dai_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct ssp_dai_dma_data *dma_data;
-
- dma_data = snd_soc_dai_get_dma_data(dai, substream);
- if (!dma_data) {
- dev_err(dai->dev, "%s: failed to get dma_data\n", __func__);
- return;
- }
- snd_soc_dai_set_dma_data(dai, substream, NULL);
- kfree(dma_data);
-}
-
-static const struct snd_soc_dai_ops ipc3_ssp_dai_ops = {
- .startup = ssp_dai_startup,
- .hw_params = ssp_dai_hw_params,
- .prepare = ssp_dai_prepare,
- .trigger = ipc3_ssp_dai_trigger,
- .hw_free = ssp_dai_hw_free,
- .shutdown = ssp_dai_shutdown,
-};
-
void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops)
{
int i;
- switch (sdev->pdata->ipc_type) {
- case SOF_IPC:
- for (i = 0; i < ops->num_drv; i++) {
- if (strstr(ops->drv[i].name, "SSP")) {
- ops->drv[i].ops = &ipc3_ssp_dai_ops;
- continue;
- }
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
- if (strstr(ops->drv[i].name, "iDisp") ||
- strstr(ops->drv[i].name, "Analog") ||
- strstr(ops->drv[i].name, "Digital"))
- ops->drv[i].ops = &ipc3_hda_dai_ops;
-#endif
- }
- break;
- case SOF_INTEL_IPC4:
- {
- struct sof_ipc4_fw_data *ipc4_data = sdev->private;
-
- for (i = 0; i < ops->num_drv; i++) {
+ for (i = 0; i < ops->num_drv; i++) {
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
- if (strstr(ops->drv[i].name, "iDisp") ||
- strstr(ops->drv[i].name, "Analog") ||
- strstr(ops->drv[i].name, "Digital"))
- ops->drv[i].ops = &ipc4_hda_dai_ops;
+ if (strstr(ops->drv[i].name, "iDisp") ||
+ strstr(ops->drv[i].name, "Analog") ||
+ strstr(ops->drv[i].name, "Digital"))
+ ops->drv[i].ops = &hda_dai_ops;
#endif
- }
+ }
- if (!hda_use_tplg_nhlt)
- ipc4_data->nhlt = intel_nhlt_init(sdev->dev);
+ if (sdev->pdata->ipc_type == SOF_INTEL_IPC4 && !hda_use_tplg_nhlt) {
+ struct sof_ipc4_fw_data *ipc4_data = sdev->private;
- break;
- }
- default:
- break;
+ ipc4_data->nhlt = intel_nhlt_init(sdev->dev);
}
}
diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c
index a6f2822401e0..44f39a520bb3 100644
--- a/sound/soc/sof/intel/hda-dsp.c
+++ b/sound/soc/sof/intel/hda-dsp.c
@@ -18,6 +18,7 @@
#include <linux/module.h>
#include <sound/hdaudio_ext.h>
#include <sound/hda_register.h>
+#include <sound/hda-mlink.h>
#include <trace/events/sof_intel.h>
#include "../sof-audio.h"
#include "../ops.h"
@@ -321,6 +322,9 @@ void hda_dsp_ipc_int_enable(struct snd_sof_dev *sdev)
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
const struct sof_intel_dsp_desc *chip = hda->desc;
+ if (sdev->dspless_mode_selected)
+ return;
+
/* enable IPC DONE and BUSY interrupts */
snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl,
HDA_DSP_REG_HIPCCTL_DONE | HDA_DSP_REG_HIPCCTL_BUSY,
@@ -336,6 +340,9 @@ void hda_dsp_ipc_int_disable(struct snd_sof_dev *sdev)
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
const struct sof_intel_dsp_desc *chip = hda->desc;
+ if (sdev->dspless_mode_selected)
+ return;
+
/* disable IPC interrupt */
snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC,
HDA_DSP_ADSPIC_IPC, 0);
@@ -567,31 +574,11 @@ static void hda_dsp_state_log(struct snd_sof_dev *sdev)
* is called again either because of a new IPC sent to the DSP or
* during system suspend/resume.
*/
-int hda_dsp_set_power_state(struct snd_sof_dev *sdev,
- const struct sof_dsp_power_state *target_state)
+static int hda_dsp_set_power_state(struct snd_sof_dev *sdev,
+ const struct sof_dsp_power_state *target_state)
{
int ret = 0;
- /*
- * When the DSP is already in D0I3 and the target state is D0I3,
- * it could be the case that the DSP is in D0I3 during S0
- * and the system is suspending to S0Ix. Therefore,
- * hda_dsp_set_D0_state() must be called to disable trace DMA
- * by sending the PM_GATE IPC to the FW.
- */
- if (target_state->substate == SOF_HDA_DSP_PM_D0I3 &&
- sdev->system_suspend_target == SOF_SUSPEND_S0IX)
- goto set_state;
-
- /*
- * For all other cases, return without doing anything if
- * the DSP is already in the target state.
- */
- if (target_state->state == sdev->dsp_power_state.state &&
- target_state->substate == sdev->dsp_power_state.substate)
- return 0;
-
-set_state:
switch (target_state->state) {
case SOF_DSP_PM_D0:
ret = hda_dsp_set_D0_state(sdev, target_state);
@@ -623,6 +610,42 @@ set_state:
return ret;
}
+int hda_dsp_set_power_state_ipc3(struct snd_sof_dev *sdev,
+ const struct sof_dsp_power_state *target_state)
+{
+ /*
+ * When the DSP is already in D0I3 and the target state is D0I3,
+ * it could be the case that the DSP is in D0I3 during S0
+ * and the system is suspending to S0Ix. Therefore,
+ * hda_dsp_set_D0_state() must be called to disable trace DMA
+ * by sending the PM_GATE IPC to the FW.
+ */
+ if (target_state->substate == SOF_HDA_DSP_PM_D0I3 &&
+ sdev->system_suspend_target == SOF_SUSPEND_S0IX)
+ return hda_dsp_set_power_state(sdev, target_state);
+
+ /*
+ * For all other cases, return without doing anything if
+ * the DSP is already in the target state.
+ */
+ if (target_state->state == sdev->dsp_power_state.state &&
+ target_state->substate == sdev->dsp_power_state.substate)
+ return 0;
+
+ return hda_dsp_set_power_state(sdev, target_state);
+}
+
+int hda_dsp_set_power_state_ipc4(struct snd_sof_dev *sdev,
+ const struct sof_dsp_power_state *target_state)
+{
+ /* Return without doing anything if the DSP is already in the target state */
+ if (target_state->state == sdev->dsp_power_state.state &&
+ target_state->substate == sdev->dsp_power_state.substate)
+ return 0;
+
+ return hda_dsp_set_power_state(sdev, target_state);
+}
+
/*
* Audio DSP states may transform as below:-
*
@@ -681,6 +704,9 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend)
/* power down all hda links */
hda_bus_ml_suspend(bus);
+ if (sdev->dspless_mode_selected)
+ goto skip_dsp;
+
ret = chip->power_down_dsp(sdev);
if (ret < 0) {
dev_err(sdev->dev, "failed to power down DSP during suspend\n");
@@ -694,6 +720,7 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend)
/* disable ppcap interrupt */
hda_dsp_ctrl_ppcap_enable(sdev, false);
hda_dsp_ctrl_ppcap_int_enable(sdev, false);
+skip_dsp:
/* disable hda bus irq and streams */
hda_dsp_ctrl_stop_chip(sdev);
@@ -744,9 +771,11 @@ static int hda_resume(struct snd_sof_dev *sdev, bool runtime_resume)
hda_codec_jack_check(sdev);
}
- /* enable ppcap interrupt */
- hda_dsp_ctrl_ppcap_enable(sdev, true);
- hda_dsp_ctrl_ppcap_int_enable(sdev, true);
+ if (!sdev->dspless_mode_selected) {
+ /* enable ppcap interrupt */
+ hda_dsp_ctrl_ppcap_enable(sdev, true);
+ hda_dsp_ctrl_ppcap_int_enable(sdev, true);
+ }
cleanup:
/* display codec can powered off after controller init */
@@ -788,7 +817,7 @@ int hda_dsp_resume(struct snd_sof_dev *sdev)
}
/* restore L1SEN bit */
- if (hda->l1_support_changed)
+ if (hda->l1_disabled)
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
HDA_VS_INTEL_EM2,
HDA_VS_INTEL_EM2_L1SEN, 0);
@@ -843,8 +872,10 @@ int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev)
};
int ret;
- /* cancel any attempt for DSP D0I3 */
- cancel_delayed_work_sync(&hda->d0i3_work);
+ if (!sdev->dspless_mode_selected) {
+ /* cancel any attempt for DSP D0I3 */
+ cancel_delayed_work_sync(&hda->d0i3_work);
+ }
/* stop hda controller and power dsp off */
ret = hda_suspend(sdev, true);
@@ -866,8 +897,10 @@ int hda_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state)
};
int ret;
- /* cancel any attempt for DSP D0I3 */
- cancel_delayed_work_sync(&hda->d0i3_work);
+ if (!sdev->dspless_mode_selected) {
+ /* cancel any attempt for DSP D0I3 */
+ cancel_delayed_work_sync(&hda->d0i3_work);
+ }
if (target_state == SOF_DSP_PM_D0) {
/* Set DSP power state */
@@ -880,11 +913,9 @@ int hda_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state)
}
/* enable L1SEN to make sure the system can enter S0Ix */
- hda->l1_support_changed =
- snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
- HDA_VS_INTEL_EM2,
- HDA_VS_INTEL_EM2_L1SEN,
- HDA_VS_INTEL_EM2_L1SEN);
+ if (hda->l1_disabled)
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_EM2,
+ HDA_VS_INTEL_EM2_L1SEN, HDA_VS_INTEL_EM2_L1SEN);
/* stop the CORB/RIRB DMA if it is On */
hda_codec_suspend_cmd_io(sdev);
diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c
index df541b22b2d2..a838dddb1d32 100644
--- a/sound/soc/sof/intel/hda-ipc.c
+++ b/sound/soc/sof/intel/hda-ipc.c
@@ -355,6 +355,9 @@ bool hda_dsp_check_ipc_irq(struct snd_sof_dev *sdev)
bool ret = false;
u32 irq_status;
+ if (sdev->dspless_mode_selected)
+ return false;
+
/* store status */
irq_status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIS);
trace_sof_intel_hda_irq_ipc_check(sdev, irq_status);
diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c
index d680562edb35..50ce6b190002 100644
--- a/sound/soc/sof/intel/hda-loader.c
+++ b/sound/soc/sof/intel/hda-loader.c
@@ -321,13 +321,13 @@ int hda_cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream
int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev)
{
struct hdac_ext_stream *iccmax_stream;
- struct hdac_bus *bus = sof_to_bus(sdev);
struct snd_dma_buffer dmab_bdl;
int ret, ret1;
u8 original_gb;
/* save the original LTRP guardband value */
- original_gb = snd_hdac_chip_readb(bus, VS_LTRP) & HDA_VS_INTEL_LTRP_GB_MASK;
+ original_gb = snd_sof_dsp_read8(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_LTRP) &
+ HDA_VS_INTEL_LTRP_GB_MASK;
/*
* Prepare capture stream for ICCMAX. We do not need to store
@@ -356,7 +356,8 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev)
}
/* restore the original guardband value after FW boot */
- snd_hdac_chip_updateb(bus, VS_LTRP, HDA_VS_INTEL_LTRP_GB_MASK, original_gb);
+ snd_sof_dsp_update8(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_LTRP,
+ HDA_VS_INTEL_LTRP_GB_MASK, original_gb);
return ret;
}
@@ -556,7 +557,7 @@ int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev,
goto cleanup;
}
- ret = sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0);
+ ret = sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0);
ret1 = cl_trigger(sdev, hext_stream, SNDRV_PCM_TRIGGER_STOP);
if (ret1 < 0) {
diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c
index 76ab9a2e7bb3..775582ab7494 100644
--- a/sound/soc/sof/intel/hda-mlink.c
+++ b/sound/soc/sof/intel/hda-mlink.c
@@ -12,49 +12,746 @@
#include <sound/hdaudio_ext.h>
#include <sound/hda_register.h>
+#include <sound/hda-mlink.h>
-#include <linux/acpi.h>
+#include <linux/bitfield.h>
#include <linux/module.h>
-#include <linux/soundwire/sdw.h>
-#include <linux/soundwire/sdw_intel.h>
-#include <sound/intel-dsp-config.h>
-#include <sound/intel-nhlt.h>
-#include <sound/sof.h>
-#include <sound/sof/xtensa.h>
-#include "../sof-audio.h"
-#include "../sof-pci-dev.h"
-#include "../ops.h"
-#include "hda.h"
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_MLINK)
-void hda_bus_ml_get_capabilities(struct hdac_bus *bus)
+/**
+ * struct hdac_ext2_link - HDAudio extended+alternate link
+ *
+ * @hext_link: hdac_ext_link
+ * @alt: flag set for alternate extended links
+ * @intc: boolean for interrupt capable
+ * @ofls: boolean for offload support
+ * @lss: boolean for link synchronization capabilities
+ * @slcount: sublink count
+ * @elid: extended link ID (AZX_REG_ML_LEPTR_ID_ defines)
+ * @elver: extended link version
+ * @leptr: extended link pointer
+ * @eml_lock: mutual exclusion to access shared registers e.g. CPA/SPA bits
+ * in LCTL register
+ * @base_ptr: pointer to shim/ip/shim_vs space
+ * @instance_offset: offset between each of @slcount instances managed by link
+ * @shim_offset: offset to SHIM register base
+ * @ip_offset: offset to IP register base
+ * @shim_vs_offset: offset to vendor-specific (VS) SHIM base
+ */
+struct hdac_ext2_link {
+ struct hdac_ext_link hext_link;
+
+ /* read directly from LCAP register */
+ bool alt;
+ bool intc;
+ bool ofls;
+ bool lss;
+ int slcount;
+ int elid;
+ int elver;
+ u32 leptr;
+
+ struct mutex eml_lock; /* prevent concurrent access to e.g. CPA/SPA */
+
+ /* internal values computed from LCAP contents */
+ void __iomem *base_ptr;
+ u32 instance_offset;
+ u32 shim_offset;
+ u32 ip_offset;
+ u32 shim_vs_offset;
+};
+
+#define hdac_ext_link_to_ext2(h) container_of(h, struct hdac_ext2_link, hext_link)
+
+#define AZX_REG_SDW_INSTANCE_OFFSET 0x8000
+#define AZX_REG_SDW_SHIM_OFFSET 0x0
+#define AZX_REG_SDW_IP_OFFSET 0x100
+#define AZX_REG_SDW_VS_SHIM_OFFSET 0x6000
+
+/* only one instance supported */
+#define AZX_REG_INTEL_DMIC_SHIM_OFFSET 0x0
+#define AZX_REG_INTEL_DMIC_IP_OFFSET 0x100
+#define AZX_REG_INTEL_DMIC_VS_SHIM_OFFSET 0x6000
+
+#define AZX_REG_INTEL_SSP_INSTANCE_OFFSET 0x1000
+#define AZX_REG_INTEL_SSP_SHIM_OFFSET 0x0
+#define AZX_REG_INTEL_SSP_IP_OFFSET 0x100
+#define AZX_REG_INTEL_SSP_VS_SHIM_OFFSET 0xC00
+
+/* only one instance supported */
+#define AZX_REG_INTEL_UAOL_SHIM_OFFSET 0x0
+#define AZX_REG_INTEL_UAOL_IP_OFFSET 0x100
+#define AZX_REG_INTEL_UAOL_VS_SHIM_OFFSET 0xC00
+
+/* HDAML section - this part follows sequences in the hardware specification,
+ * including naming conventions and the use of the hdaml_ prefix.
+ * The code is intentionally minimal with limited dependencies on frameworks or
+ * helpers. Locking and scanning lists is handled at a higher level
+ */
+
+static int hdaml_lnk_enum(struct device *dev, struct hdac_ext2_link *h2link,
+ void __iomem *ml_addr, int link_idx)
+{
+ struct hdac_ext_link *hlink = &h2link->hext_link;
+ u32 base_offset;
+
+ hlink->lcaps = readl(ml_addr + AZX_REG_ML_LCAP);
+
+ h2link->alt = FIELD_GET(AZX_ML_HDA_LCAP_ALT, hlink->lcaps);
+
+ /* handle alternate extensions */
+ if (!h2link->alt) {
+ h2link->slcount = 1;
+
+ /*
+ * LSDIID is initialized by hardware for HDaudio link,
+ * it needs to be setup by software for alternate links
+ */
+ hlink->lsdiid = readw(ml_addr + AZX_REG_ML_LSDIID);
+
+ dev_dbg(dev, "Link %d: HDAudio - lsdiid=%d\n",
+ link_idx, hlink->lsdiid);
+
+ return 0;
+ }
+
+ h2link->intc = FIELD_GET(AZX_ML_HDA_LCAP_INTC, hlink->lcaps);
+ h2link->ofls = FIELD_GET(AZX_ML_HDA_LCAP_OFLS, hlink->lcaps);
+ h2link->lss = FIELD_GET(AZX_ML_HDA_LCAP_LSS, hlink->lcaps);
+
+ /* read slcount (increment due to zero-based hardware representation */
+ h2link->slcount = FIELD_GET(AZX_ML_HDA_LCAP_SLCOUNT, hlink->lcaps) + 1;
+ dev_dbg(dev, "Link %d: HDAudio extended - sublink count %d\n",
+ link_idx, h2link->slcount);
+
+ /* find IP ID and offsets */
+ h2link->leptr = readl(hlink->ml_addr + AZX_REG_ML_LEPTR);
+
+ h2link->elid = FIELD_GET(AZX_REG_ML_LEPTR_ID, h2link->leptr);
+
+ base_offset = FIELD_GET(AZX_REG_ML_LEPTR_PTR, h2link->leptr);
+ h2link->base_ptr = hlink->ml_addr + base_offset;
+
+ switch (h2link->elid) {
+ case AZX_REG_ML_LEPTR_ID_SDW:
+ h2link->shim_offset = AZX_REG_SDW_SHIM_OFFSET;
+ h2link->ip_offset = AZX_REG_SDW_IP_OFFSET;
+ h2link->shim_vs_offset = AZX_REG_SDW_VS_SHIM_OFFSET;
+ dev_dbg(dev, "Link %d: HDAudio extended - SoundWire alternate link, leptr.ptr %#x\n",
+ link_idx, base_offset);
+ break;
+ case AZX_REG_ML_LEPTR_ID_INTEL_DMIC:
+ h2link->shim_offset = AZX_REG_INTEL_DMIC_SHIM_OFFSET;
+ h2link->ip_offset = AZX_REG_INTEL_DMIC_IP_OFFSET;
+ h2link->shim_vs_offset = AZX_REG_INTEL_DMIC_VS_SHIM_OFFSET;
+ dev_dbg(dev, "Link %d: HDAudio extended - INTEL DMIC alternate link, leptr.ptr %#x\n",
+ link_idx, base_offset);
+ break;
+ case AZX_REG_ML_LEPTR_ID_INTEL_SSP:
+ h2link->shim_offset = AZX_REG_INTEL_SSP_SHIM_OFFSET;
+ h2link->ip_offset = AZX_REG_INTEL_SSP_IP_OFFSET;
+ h2link->shim_vs_offset = AZX_REG_INTEL_SSP_VS_SHIM_OFFSET;
+ dev_dbg(dev, "Link %d: HDAudio extended - INTEL SSP alternate link, leptr.ptr %#x\n",
+ link_idx, base_offset);
+ break;
+ case AZX_REG_ML_LEPTR_ID_INTEL_UAOL:
+ h2link->shim_offset = AZX_REG_INTEL_UAOL_SHIM_OFFSET;
+ h2link->ip_offset = AZX_REG_INTEL_UAOL_IP_OFFSET;
+ h2link->shim_vs_offset = AZX_REG_INTEL_UAOL_VS_SHIM_OFFSET;
+ dev_dbg(dev, "Link %d: HDAudio extended - INTEL UAOL alternate link, leptr.ptr %#x\n",
+ link_idx, base_offset);
+ break;
+ default:
+ dev_err(dev, "Link %d: HDAudio extended - Unsupported alternate link, leptr.id=%#02x value\n",
+ link_idx, h2link->elid);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * Hardware recommendations are to wait ~10us before checking any hardware transition
+ * reported by bits changing status.
+ * This value does not need to be super-precise, a slack of 5us is perfectly acceptable.
+ * The worst-case is about 1ms before reporting an issue
+ */
+#define HDAML_POLL_DELAY_MIN_US 10
+#define HDAML_POLL_DELAY_SLACK_US 5
+#define HDAML_POLL_DELAY_RETRY 100
+
+static int check_sublink_power(u32 __iomem *lctl, int sublink, bool enabled)
{
- if (bus->mlcap)
- snd_hdac_ext_bus_get_ml_capabilities(bus);
+ int mask = BIT(sublink) << AZX_ML_LCTL_CPA_SHIFT;
+ int retry = HDAML_POLL_DELAY_RETRY;
+ u32 val;
+
+ usleep_range(HDAML_POLL_DELAY_MIN_US,
+ HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
+ do {
+ val = readl(lctl);
+ if (enabled) {
+ if (val & mask)
+ return 0;
+ } else {
+ if (!(val & mask))
+ return 0;
+ }
+ usleep_range(HDAML_POLL_DELAY_MIN_US,
+ HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
+
+ } while (--retry);
+
+ return -EIO;
}
-void hda_bus_ml_free(struct hdac_bus *bus)
+static int hdaml_link_init(u32 __iomem *lctl, int sublink)
+{
+ u32 val;
+ u32 mask = BIT(sublink) << AZX_ML_LCTL_SPA_SHIFT;
+
+ val = readl(lctl);
+ val |= mask;
+
+ writel(val, lctl);
+
+ return check_sublink_power(lctl, sublink, true);
+}
+
+static int hdaml_link_shutdown(u32 __iomem *lctl, int sublink)
+{
+ u32 val;
+ u32 mask;
+
+ val = readl(lctl);
+ mask = BIT(sublink) << AZX_ML_LCTL_SPA_SHIFT;
+ val &= ~mask;
+
+ writel(val, lctl);
+
+ return check_sublink_power(lctl, sublink, false);
+}
+
+static void hdaml_link_enable_interrupt(u32 __iomem *lctl, bool enable)
+{
+ u32 val;
+
+ val = readl(lctl);
+ if (enable)
+ val |= AZX_ML_LCTL_INTEN;
+ else
+ val &= ~AZX_ML_LCTL_INTEN;
+
+ writel(val, lctl);
+}
+
+static bool hdaml_link_check_interrupt(u32 __iomem *lctl)
+{
+ u32 val;
+
+ val = readl(lctl);
+
+ return val & AZX_ML_LCTL_INTSTS;
+}
+
+static int hdaml_wait_bit(void __iomem *base, int offset, u32 mask, u32 target)
+{
+ int timeout = HDAML_POLL_DELAY_RETRY;
+ u32 reg_read;
+
+ do {
+ reg_read = readl(base + offset);
+ if ((reg_read & mask) == target)
+ return 0;
+
+ timeout--;
+ usleep_range(HDAML_POLL_DELAY_MIN_US,
+ HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
+ } while (timeout != 0);
+
+ return -EAGAIN;
+}
+
+static void hdaml_link_set_syncprd(u32 __iomem *lsync, u32 syncprd)
+{
+ u32 val;
+
+ val = readl(lsync);
+ val &= ~AZX_REG_ML_LSYNC_SYNCPRD;
+ val |= (syncprd & AZX_REG_ML_LSYNC_SYNCPRD);
+
+ /*
+ * set SYNCPU but do not wait. The bit is cleared by hardware when
+ * the link becomes active.
+ */
+ val |= AZX_REG_ML_LSYNC_SYNCPU;
+
+ writel(val, lsync);
+}
+
+static int hdaml_link_wait_syncpu(u32 __iomem *lsync)
+{
+ return hdaml_wait_bit(lsync, 0, AZX_REG_ML_LSYNC_SYNCPU, 0);
+}
+
+static void hdaml_link_sync_arm(u32 __iomem *lsync, int sublink)
+{
+ u32 val;
+
+ val = readl(lsync);
+ val |= (AZX_REG_ML_LSYNC_CMDSYNC << sublink);
+
+ writel(val, lsync);
+}
+
+static void hdaml_link_sync_go(u32 __iomem *lsync)
{
+ u32 val;
+
+ val = readl(lsync);
+ val |= AZX_REG_ML_LSYNC_SYNCGO;
+
+ writel(val, lsync);
+}
+
+static bool hdaml_link_check_cmdsync(u32 __iomem *lsync, u32 cmdsync_mask)
+{
+ u32 val;
+
+ val = readl(lsync);
+
+ return !!(val & cmdsync_mask);
+}
+
+static void hdaml_link_set_lsdiid(u32 __iomem *lsdiid, int dev_num)
+{
+ u32 val;
+
+ val = readl(lsdiid);
+ val |= BIT(dev_num);
+
+ writel(val, lsdiid);
+}
+
+static void hdaml_lctl_offload_enable(u32 __iomem *lctl, bool enable)
+{
+ u32 val = readl(lctl);
+
+ if (enable)
+ val |= AZX_ML_LCTL_OFLEN;
+ else
+ val &= ~AZX_ML_LCTL_OFLEN;
+
+ writel(val, lctl);
+}
+
+/* END HDAML section */
+
+static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index)
+{
+ struct hdac_ext2_link *h2link;
struct hdac_ext_link *hlink;
+ int ret;
+
+ h2link = kzalloc(sizeof(*h2link), GFP_KERNEL);
+ if (!h2link)
+ return -ENOMEM;
+
+ /* basic initialization */
+ hlink = &h2link->hext_link;
+
+ hlink->index = index;
+ hlink->bus = bus;
+ hlink->ml_addr = bus->mlcap + AZX_ML_BASE + (AZX_ML_INTERVAL * index);
+
+ ret = hdaml_lnk_enum(bus->dev, h2link, hlink->ml_addr, index);
+ if (ret < 0) {
+ kfree(h2link);
+ return ret;
+ }
+
+ mutex_init(&h2link->eml_lock);
+
+ list_add_tail(&hlink->list, &bus->hlink_list);
+
+ /*
+ * HDaudio regular links are powered-on by default, the
+ * refcount needs to be initialized.
+ */
+ if (!h2link->alt)
+ hlink->ref_count = 1;
+
+ return 0;
+}
+
+int hda_bus_ml_init(struct hdac_bus *bus)
+{
+ u32 link_count;
+ int ret;
+ int i;
+
+ if (!bus->mlcap)
+ return 0;
+
+ link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1;
+
+ dev_dbg(bus->dev, "HDAudio Multi-Link count: %d\n", link_count);
+
+ for (i = 0; i < link_count; i++) {
+ ret = hda_ml_alloc_h2link(bus, i);
+ if (ret < 0) {
+ hda_bus_ml_free(bus);
+ return ret;
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL_NS(hda_bus_ml_init, SND_SOC_SOF_HDA_MLINK);
+
+void hda_bus_ml_free(struct hdac_bus *bus)
+{
+ struct hdac_ext_link *hlink, *_h;
+ struct hdac_ext2_link *h2link;
if (!bus->mlcap)
return;
- while (!list_empty(&bus->hlink_list)) {
- hlink = list_first_entry(&bus->hlink_list, struct hdac_ext_link, list);
+ list_for_each_entry_safe(hlink, _h, &bus->hlink_list, list) {
list_del(&hlink->list);
- kfree(hlink);
+ h2link = hdac_ext_link_to_ext2(hlink);
+
+ mutex_destroy(&h2link->eml_lock);
+ kfree(h2link);
+ }
+}
+EXPORT_SYMBOL_NS(hda_bus_ml_free, SND_SOC_SOF_HDA_MLINK);
+
+static struct hdac_ext2_link *
+find_ext2_link(struct hdac_bus *bus, bool alt, int elid)
+{
+ struct hdac_ext_link *hlink;
+
+ list_for_each_entry(hlink, &bus->hlink_list, list) {
+ struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
+
+ if (h2link->alt == alt && h2link->elid == elid)
+ return h2link;
}
+
+ return NULL;
+}
+
+int hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid)
+{
+ struct hdac_ext2_link *h2link;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return 0;
+
+ return h2link->slcount;
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_get_count, SND_SOC_SOF_HDA_MLINK);
+
+void hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool enable)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return;
+
+ if (!h2link->intc)
+ return;
+
+ hlink = &h2link->hext_link;
+
+ mutex_lock(&h2link->eml_lock);
+
+ hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
+
+ mutex_unlock(&h2link->eml_lock);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_enable_interrupt, SND_SOC_SOF_HDA_MLINK);
+
+bool hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return false;
+
+ if (!h2link->intc)
+ return false;
+
+ hlink = &h2link->hext_link;
+
+ return hdaml_link_check_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_check_interrupt, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_set_syncprd_unlocked(struct hdac_bus *bus, bool alt, int elid, u32 syncprd)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return 0;
+
+ if (!h2link->lss)
+ return 0;
+
+ hlink = &h2link->hext_link;
+
+ hdaml_link_set_syncprd(hlink->ml_addr + AZX_REG_ML_LSYNC, syncprd);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_set_syncprd_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_sdw_set_syncprd_unlocked(struct hdac_bus *bus, u32 syncprd)
+{
+ return hdac_bus_eml_set_syncprd_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, syncprd);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_syncprd_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus *bus, bool alt, int elid)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return 0;
+
+ if (!h2link->lss)
+ return 0;
+
+ hlink = &h2link->hext_link;
+
+ return hdaml_link_wait_syncpu(hlink->ml_addr + AZX_REG_ML_LSYNC);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_wait_syncpu_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus *bus)
+{
+ return hdac_bus_eml_wait_syncpu_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_wait_syncpu_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+void hdac_bus_eml_sync_arm_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return;
+
+ if (!h2link->lss)
+ return;
+
+ hlink = &h2link->hext_link;
+
+ hdaml_link_sync_arm(hlink->ml_addr + AZX_REG_ML_LSYNC, sublink);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sync_arm_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+void hdac_bus_eml_sdw_sync_arm_unlocked(struct hdac_bus *bus, int sublink)
+{
+ hdac_bus_eml_sync_arm_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_arm_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_sync_go_unlocked(struct hdac_bus *bus, bool alt, int elid)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return 0;
+
+ if (!h2link->lss)
+ return 0;
+
+ hlink = &h2link->hext_link;
+
+ hdaml_link_sync_go(hlink->ml_addr + AZX_REG_ML_LSYNC);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sync_go_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_sdw_sync_go_unlocked(struct hdac_bus *bus)
+{
+ return hdac_bus_eml_sync_go_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_go_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+bool hdac_bus_eml_check_cmdsync_unlocked(struct hdac_bus *bus, bool alt, int elid)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+ u32 cmdsync_mask;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return 0;
+
+ if (!h2link->lss)
+ return 0;
+
+ hlink = &h2link->hext_link;
+
+ cmdsync_mask = GENMASK(AZX_REG_ML_LSYNC_CMDSYNC_SHIFT + h2link->slcount - 1,
+ AZX_REG_ML_LSYNC_CMDSYNC_SHIFT);
+
+ return hdaml_link_check_cmdsync(hlink->ml_addr + AZX_REG_ML_LSYNC,
+ cmdsync_mask);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_check_cmdsync_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+bool hdac_bus_eml_sdw_check_cmdsync_unlocked(struct hdac_bus *bus)
+{
+ return hdac_bus_eml_check_cmdsync_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_check_cmdsync_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid, int sublink,
+ bool eml_lock)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+ int ret = 0;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return -ENODEV;
+
+ if (sublink >= h2link->slcount)
+ return -EINVAL;
+
+ hlink = &h2link->hext_link;
+
+ if (eml_lock)
+ mutex_lock(&h2link->eml_lock);
+
+ if (++hlink->ref_count > 1)
+ goto skip_init;
+
+ ret = hdaml_link_init(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);
+
+skip_init:
+ if (eml_lock)
+ mutex_unlock(&h2link->eml_lock);
+
+ return ret;
+}
+
+int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink)
+{
+ return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, true);
}
+EXPORT_SYMBOL_NS(hdac_bus_eml_power_up, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
+{
+ return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, false);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_power_up_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+static int hdac_bus_eml_power_down_base(struct hdac_bus *bus, bool alt, int elid, int sublink,
+ bool eml_lock)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+ int ret = 0;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return -ENODEV;
+
+ if (sublink >= h2link->slcount)
+ return -EINVAL;
+
+ hlink = &h2link->hext_link;
+
+ if (eml_lock)
+ mutex_lock(&h2link->eml_lock);
+
+ if (--hlink->ref_count > 0)
+ goto skip_shutdown;
+
+ ret = hdaml_link_shutdown(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);
+
+skip_shutdown:
+ if (eml_lock)
+ mutex_unlock(&h2link->eml_lock);
+
+ return ret;
+}
+
+int hdac_bus_eml_power_down(struct hdac_bus *bus, bool alt, int elid, int sublink)
+{
+ return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, true);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_power_down, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
+{
+ return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, false);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_power_down_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_sdw_power_up_unlocked(struct hdac_bus *bus, int sublink)
+{
+ return hdac_bus_eml_power_up_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_up_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink)
+{
+ return hdac_bus_eml_power_down_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_down_unlocked, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+
+ h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
+ if (!h2link)
+ return -ENODEV;
+
+ hlink = &h2link->hext_link;
+
+ mutex_lock(&h2link->eml_lock);
+
+ hdaml_link_set_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink), dev_num);
+
+ mutex_unlock(&h2link->eml_lock);
+
+ return 0;
+} EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_lsdiid, SND_SOC_SOF_HDA_MLINK);
void hda_bus_ml_put_all(struct hdac_bus *bus)
{
struct hdac_ext_link *hlink;
- list_for_each_entry(hlink, &bus->hlink_list, list)
- snd_hdac_ext_bus_link_put(bus, hlink);
+ list_for_each_entry(hlink, &bus->hlink_list, list) {
+ struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
+
+ if (!h2link->alt)
+ snd_hdac_ext_bus_link_put(bus, hlink);
+ }
}
+EXPORT_SYMBOL_NS(hda_bus_ml_put_all, SND_SOC_SOF_HDA_MLINK);
void hda_bus_ml_reset_losidv(struct hdac_bus *bus)
{
@@ -64,6 +761,7 @@ void hda_bus_ml_reset_losidv(struct hdac_bus *bus)
list_for_each_entry(hlink, &bus->hlink_list, list)
writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV);
}
+EXPORT_SYMBOL_NS(hda_bus_ml_reset_losidv, SND_SOC_SOF_HDA_MLINK);
int hda_bus_ml_resume(struct hdac_bus *bus)
{
@@ -72,7 +770,9 @@ int hda_bus_ml_resume(struct hdac_bus *bus)
/* power up links that were active before suspend */
list_for_each_entry(hlink, &bus->hlink_list, list) {
- if (hlink->ref_count) {
+ struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
+
+ if (!h2link->alt && hlink->ref_count) {
ret = snd_hdac_ext_bus_link_power_up(hlink);
if (ret < 0)
return ret;
@@ -80,10 +780,86 @@ int hda_bus_ml_resume(struct hdac_bus *bus)
}
return 0;
}
+EXPORT_SYMBOL_NS(hda_bus_ml_resume, SND_SOC_SOF_HDA_MLINK);
int hda_bus_ml_suspend(struct hdac_bus *bus)
{
- return snd_hdac_ext_bus_link_power_down_all(bus);
+ struct hdac_ext_link *hlink;
+ int ret;
+
+ list_for_each_entry(hlink, &bus->hlink_list, list) {
+ struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
+
+ if (!h2link->alt) {
+ ret = snd_hdac_ext_bus_link_power_down(hlink);
+ if (ret < 0)
+ return ret;
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL_NS(hda_bus_ml_suspend, SND_SOC_SOF_HDA_MLINK);
+
+struct mutex *hdac_bus_eml_get_mutex(struct hdac_bus *bus, bool alt, int elid)
+{
+ struct hdac_ext2_link *h2link;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return NULL;
+
+ return &h2link->eml_lock;
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_get_mutex, SND_SOC_SOF_HDA_MLINK);
+
+struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus)
+{
+ struct hdac_ext2_link *h2link;
+
+ h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_SSP);
+ if (!h2link)
+ return NULL;
+
+ return &h2link->hext_link;
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_ssp_get_hlink, SND_SOC_SOF_HDA_MLINK);
+
+struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus)
+{
+ struct hdac_ext2_link *h2link;
+
+ h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_DMIC);
+ if (!h2link)
+ return NULL;
+
+ return &h2link->hext_link;
+}
+EXPORT_SYMBOL_NS(hdac_bus_eml_dmic_get_hlink, SND_SOC_SOF_HDA_MLINK);
+
+int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable)
+{
+ struct hdac_ext2_link *h2link;
+ struct hdac_ext_link *hlink;
+
+ h2link = find_ext2_link(bus, alt, elid);
+ if (!h2link)
+ return -ENODEV;
+
+ if (!h2link->ofls)
+ return 0;
+
+ hlink = &h2link->hext_link;
+
+ mutex_lock(&h2link->eml_lock);
+
+ hdaml_lctl_offload_enable(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
+
+ mutex_unlock(&h2link->eml_lock);
+
+ return 0;
}
+EXPORT_SYMBOL_NS(hdac_bus_eml_enable_offload, SND_SOC_SOF_HDA_MLINK);
#endif
+
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c
index dc0b359ed9b6..981e7b699bdb 100644
--- a/sound/soc/sof/intel/hda-pcm.c
+++ b/sound/soc/sof/intel/hda-pcm.c
@@ -101,18 +101,23 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev,
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
struct snd_dma_buffer *dmab;
int ret;
- u32 size, rate, bits;
-
- size = params_buffer_bytes(params);
- rate = hda_dsp_get_mult_div(sdev, params_rate(params));
- bits = hda_dsp_get_bits(sdev, params_width(params));
hstream->substream = substream;
dmab = substream->runtime->dma_buffer_p;
- hstream->format_val = rate | bits | (params_channels(params) - 1);
- hstream->bufsize = size;
+ /*
+ * Use the codec required format val (which is link_bps adjusted) when
+ * the DSP is not in use
+ */
+ if (!sdev->dspless_mode_selected) {
+ u32 rate = hda_dsp_get_mult_div(sdev, params_rate(params));
+ u32 bits = hda_dsp_get_bits(sdev, params_width(params));
+
+ hstream->format_val = rate | bits | (params_channels(params) - 1);
+ }
+
+ hstream->bufsize = params_buffer_bytes(params);
hstream->period_bytes = params_period_bytes(params);
hstream->no_period_wakeup =
(params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) &&
@@ -249,6 +254,11 @@ int hda_dsp_pcm_open(struct snd_sof_dev *sdev,
snd_pcm_hw_constraint_integer(substream->runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
+ /* Only S16 and S32 supported by HDA hardware when used without DSP */
+ if (sdev->dspless_mode_selected)
+ snd_pcm_hw_constraint_mask64(substream->runtime, SNDRV_PCM_HW_PARAM_FORMAT,
+ SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S32);
+
/* binding pcm substream to hda stream */
substream->runtime->private_data = &dsp_stream->hstream;
return 0;
diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c
index 7f0fd05a96e6..8de422604ad5 100644
--- a/sound/soc/sof/intel/hda-stream.c
+++ b/sound/soc/sof/intel/hda-stream.c
@@ -182,6 +182,8 @@ int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev,
struct hdac_ext_stream *
hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags)
{
+ const struct sof_intel_dsp_desc *chip_info = get_chip_info(sdev->pdata);
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
struct hdac_bus *bus = sof_to_bus(sdev);
struct sof_intel_hda_stream *hda_stream;
struct hdac_ext_stream *hext_stream = NULL;
@@ -220,12 +222,15 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags)
/*
* Prevent DMI Link L1 entry for streams that don't support it.
* Workaround to address a known issue with host DMA that results
- * in xruns during pause/release in capture scenarios.
+ * in xruns during pause/release in capture scenarios. This is not needed for the ACE IP.
*/
- if (!(flags & SOF_HDA_STREAM_DMI_L1_COMPATIBLE))
+ if (chip_info->hw_ip_version < SOF_INTEL_ACE_1_0 &&
+ !(flags & SOF_HDA_STREAM_DMI_L1_COMPATIBLE)) {
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
HDA_VS_INTEL_EM2,
HDA_VS_INTEL_EM2_L1SEN, 0);
+ hda->l1_disabled = true;
+ }
return hext_stream;
}
@@ -233,6 +238,8 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction, u32 flags)
/* free a stream */
int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag)
{
+ const struct sof_intel_dsp_desc *chip_info = get_chip_info(sdev->pdata);
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
struct hdac_bus *bus = sof_to_bus(sdev);
struct sof_intel_hda_stream *hda_stream;
struct hdac_ext_stream *hext_stream;
@@ -264,9 +271,11 @@ int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag)
spin_unlock_irq(&bus->reg_lock);
/* Enable DMI L1 if permitted */
- if (dmi_l1_enable)
+ if (chip_info->hw_ip_version < SOF_INTEL_ACE_1_0 && dmi_l1_enable) {
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, HDA_VS_INTEL_EM2,
HDA_VS_INTEL_EM2_L1SEN, HDA_VS_INTEL_EM2_L1SEN);
+ hda->l1_disabled = false;
+ }
if (!found) {
dev_err(sdev->dev, "%s: stream_tag %d not opened!\n",
@@ -328,7 +337,13 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev,
/* cmd must be for audio stream */
switch (cmd) {
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (!sdev->dspless_mode_selected)
+ break;
+ fallthrough;
case SNDRV_PCM_TRIGGER_START:
+ if (hstream->running)
+ break;
+
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL,
1 << hstream->index,
1 << hstream->index);
@@ -351,8 +366,11 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev,
hstream->running = true;
break;
- case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (!sdev->dspless_mode_selected)
+ break;
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
sd_offset,
@@ -476,9 +494,8 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
{
const struct sof_intel_dsp_desc *chip = get_chip_info(sdev->pdata);
struct hdac_bus *bus = sof_to_bus(sdev);
- struct hdac_stream *hstream = &hext_stream->hstream;
- int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
- int ret;
+ struct hdac_stream *hstream;
+ int sd_offset, ret;
u32 dma_start = SOF_HDA_SD_CTL_DMA_START;
u32 mask;
u32 run;
@@ -493,10 +510,14 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
return -ENODEV;
}
- /* decouple host and link DMA */
- mask = 0x1 << hstream->index;
- snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
- mask, mask);
+ hstream = &hext_stream->hstream;
+ sd_offset = SOF_STREAM_SD_OFFSET(hstream);
+ mask = BIT(hstream->index);
+
+ /* decouple host and link DMA if the DSP is used */
+ if (!sdev->dspless_mode_selected)
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
+ mask, mask);
/* clear stream status */
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset,
@@ -597,11 +618,10 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
* enable decoupled mode
*/
- if (chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK) {
+ if (!sdev->dspless_mode_selected && (chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK))
/* couple host and link DMA, disable DSP features */
snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
mask, 0);
- }
/* program stream format */
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
@@ -609,11 +629,10 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
SOF_HDA_ADSP_REG_SD_FORMAT,
0xffff, hstream->format_val);
- if (chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK) {
+ if (!sdev->dspless_mode_selected && (chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK))
/* decouple host and link DMA, enable DSP features */
snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
mask, mask);
- }
/* program last valid index */
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR,
@@ -666,20 +685,23 @@ int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev,
struct hdac_ext_stream *hext_stream = container_of(hstream,
struct hdac_ext_stream,
hstream);
- struct hdac_bus *bus = sof_to_bus(sdev);
- u32 mask = 0x1 << hstream->index;
int ret;
ret = hda_dsp_stream_reset(sdev, hstream);
if (ret < 0)
return ret;
- spin_lock_irq(&bus->reg_lock);
- /* couple host and link DMA if link DMA channel is idle */
- if (!hext_stream->link_locked)
- snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR,
- SOF_HDA_REG_PP_PPCTL, mask, 0);
- spin_unlock_irq(&bus->reg_lock);
+ if (!sdev->dspless_mode_selected) {
+ struct hdac_bus *bus = sof_to_bus(sdev);
+ u32 mask = BIT(hstream->index);
+
+ spin_lock_irq(&bus->reg_lock);
+ /* couple host and link DMA if link DMA channel is idle */
+ if (!hext_stream->link_locked)
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR,
+ SOF_HDA_REG_PP_PPCTL, mask, 0);
+ spin_unlock_irq(&bus->reg_lock);
+ }
hda_dsp_stream_spib_config(sdev, hext_stream, HDA_DSP_SPIB_DISABLE, 0);
@@ -861,12 +883,14 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev)
hext_stream = &hda_stream->hext_stream;
- hext_stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] +
- SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i;
+ if (sdev->bar[HDA_DSP_PP_BAR]) {
+ hext_stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] +
+ SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i;
- hext_stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] +
- SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total +
- SOF_HDA_PPLC_INTERVAL * i;
+ hext_stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] +
+ SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total +
+ SOF_HDA_PPLC_INTERVAL * i;
+ }
hstream = &hext_stream->hstream;
@@ -917,13 +941,14 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev)
hext_stream = &hda_stream->hext_stream;
- /* we always have DSP support */
- hext_stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] +
- SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i;
+ if (sdev->bar[HDA_DSP_PP_BAR]) {
+ hext_stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] +
+ SOF_HDA_PPHC_BASE + SOF_HDA_PPHC_INTERVAL * i;
- hext_stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] +
- SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total +
- SOF_HDA_PPLC_INTERVAL * i;
+ hext_stream->pplc_addr = sdev->bar[HDA_DSP_PP_BAR] +
+ SOF_HDA_PPLC_BASE + SOF_HDA_PPLC_MULTI * num_total +
+ SOF_HDA_PPLC_INTERVAL * i;
+ }
hstream = &hext_stream->hstream;
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c
index 81c697e20108..3153e21f100a 100644
--- a/sound/soc/sof/intel/hda.c
+++ b/sound/soc/sof/intel/hda.c
@@ -26,6 +26,7 @@
#include <sound/intel-nhlt.h>
#include <sound/sof.h>
#include <sound/sof/xtensa.h>
+#include <sound/hda-mlink.h>
#include "../sof-audio.h"
#include "../sof-pci-dev.h"
#include "../ops.h"
@@ -44,68 +45,37 @@
#define EXCEPT_MAX_HDR_SIZE 0x400
#define HDA_EXT_ROM_STATUS_SIZE 8
-int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags,
- struct snd_sof_dai_config_data *data)
+static u32 hda_get_interface_mask(struct snd_sof_dev *sdev)
{
- struct snd_sof_widget *swidget = w->dobj.private;
- struct snd_soc_component *component = swidget->scomp;
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
- struct snd_sof_dai *sof_dai = swidget->private;
- int ret;
-
- if (!sof_dai) {
- dev_err(sdev->dev, "%s: No DAI for DAI widget %s\n", __func__, w->name);
- return -EINVAL;
- }
-
- if (tplg_ops->dai_config) {
- unsigned int flags;
-
- /* set HW_PARAMS flag along with quirks */
- flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS |
- quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
-
- ret = tplg_ops->dai_config(sdev, swidget, flags, data);
- if (ret < 0) {
- dev_err(sdev->dev, "%s: DAI config failed for widget %s\n", __func__,
- w->name);
- return ret;
- }
- }
-
- return 0;
-}
-
-int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags,
- struct snd_sof_dai_config_data *data)
-{
- struct snd_sof_widget *swidget = w->dobj.private;
- struct snd_soc_component *component = swidget->scomp;
- struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
- struct snd_sof_dai *sof_dai = swidget->private;
-
- if (!sof_dai) {
- dev_err(sdev->dev, "%s: No DAI for BE DAI widget %s\n", __func__, w->name);
- return -EINVAL;
- }
-
- if (tplg_ops->dai_config) {
- unsigned int flags;
- int ret;
-
- /* set HW_FREE flag along with any quirks */
- flags = SOF_DAI_CONFIG_FLAGS_HW_FREE |
- quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
+ const struct sof_intel_dsp_desc *chip;
+ u32 interface_mask[2] = { 0 };
- ret = tplg_ops->dai_config(sdev, swidget, flags, data);
- if (ret < 0)
- dev_err(sdev->dev, "%s: DAI config failed for widget '%s'\n", __func__,
- w->name);
+ chip = get_chip_info(sdev->pdata);
+ switch (chip->hw_ip_version) {
+ case SOF_INTEL_TANGIER:
+ case SOF_INTEL_BAYTRAIL:
+ case SOF_INTEL_BROADWELL:
+ interface_mask[0] = BIT(SOF_DAI_INTEL_SSP);
+ break;
+ case SOF_INTEL_CAVS_1_5:
+ case SOF_INTEL_CAVS_1_5_PLUS:
+ interface_mask[0] = BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) |
+ BIT(SOF_DAI_INTEL_HDA);
+ interface_mask[1] = BIT(SOF_DAI_INTEL_HDA);
+ break;
+ case SOF_INTEL_CAVS_1_8:
+ case SOF_INTEL_CAVS_2_0:
+ case SOF_INTEL_CAVS_2_5:
+ case SOF_INTEL_ACE_1_0:
+ interface_mask[0] = BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) |
+ BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH);
+ interface_mask[1] = BIT(SOF_DAI_INTEL_HDA);
+ break;
+ default:
+ break;
}
- return 0;
+ return interface_mask[sdev->dspless_mode_selected];
}
#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
@@ -124,35 +94,17 @@ static int sdw_params_stream(struct device *dev,
struct sdw_intel_stream_params_data *params_data)
{
struct snd_soc_dai *d = params_data->dai;
- struct snd_sof_dai_config_data data;
- struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(d, params_data->stream);
+ struct snd_sof_dai_config_data data = { 0 };
- w = snd_soc_dai_get_widget(d, params_data->stream);
data.dai_index = (params_data->link_id << 8) | d->id;
data.dai_data = params_data->alh_stream_id;
- return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE, &data);
-}
-
-static int sdw_free_stream(struct device *dev,
- struct sdw_intel_stream_free_data *free_data)
-{
- struct snd_soc_dai *d = free_data->dai;
- struct snd_sof_dai_config_data data;
- struct snd_soc_dapm_widget *w;
-
- w = snd_soc_dai_get_widget(d, free_data->stream);
- data.dai_index = (free_data->link_id << 8) | d->id;
-
- /* send invalid stream_id */
- data.dai_data = 0xFFFF;
-
- return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE, &data);
+ return hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_HW_PARAMS, &data);
}
struct sdw_intel_ops sdw_callback = {
.params_stream = sdw_params_stream,
- .free_stream = sdw_free_stream,
};
void hda_common_enable_sdw_irq(struct snd_sof_dev *sdev, bool enable)
@@ -171,8 +123,12 @@ void hda_common_enable_sdw_irq(struct snd_sof_dev *sdev, bool enable)
void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable)
{
+ u32 interface_mask = hda_get_interface_mask(sdev);
const struct sof_intel_dsp_desc *chip;
+ if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH)))
+ return;
+
chip = get_chip_info(sdev->pdata);
if (chip && chip->enable_sdw_irq)
chip->enable_sdw_irq(sdev, enable);
@@ -180,10 +136,14 @@ void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable)
static int hda_sdw_acpi_scan(struct snd_sof_dev *sdev)
{
+ u32 interface_mask = hda_get_interface_mask(sdev);
struct sof_intel_hda_dev *hdev;
acpi_handle handle;
int ret;
+ if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH)))
+ return -EINVAL;
+
handle = ACPI_HANDLE(sdev->dev);
/* save ACPI info for the probe step */
@@ -254,8 +214,8 @@ int hda_sdw_check_lcount_common(struct snd_sof_dev *sdev)
/* Check HW supported vs property value */
if (caps < ctx->count) {
dev_err(sdev->dev,
- "BIOS master count %d is larger than hardware capabilities %d\n",
- ctx->count, caps);
+ "%s: BIOS master count %d is larger than hardware capabilities %d\n",
+ __func__, ctx->count, caps);
return -EINVAL;
}
@@ -337,8 +297,12 @@ out:
static bool hda_dsp_check_sdw_irq(struct snd_sof_dev *sdev)
{
+ u32 interface_mask = hda_get_interface_mask(sdev);
const struct sof_intel_dsp_desc *chip;
+ if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH)))
+ return false;
+
chip = get_chip_info(sdev->pdata);
if (chip && chip->check_sdw_irq)
return chip->check_sdw_irq(sdev);
@@ -353,8 +317,12 @@ static irqreturn_t hda_dsp_sdw_thread(int irq, void *context)
static bool hda_sdw_check_wakeen_irq(struct snd_sof_dev *sdev)
{
+ u32 interface_mask = hda_get_interface_mask(sdev);
struct sof_intel_hda_dev *hdev;
+ if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH)))
+ return false;
+
hdev = sdev->pdata->hw_pdata;
if (hdev->sdw &&
snd_sof_dsp_read(sdev, HDA_DSP_BAR,
@@ -366,8 +334,12 @@ static bool hda_sdw_check_wakeen_irq(struct snd_sof_dev *sdev)
void hda_sdw_process_wakeen(struct snd_sof_dev *sdev)
{
+ u32 interface_mask = hda_get_interface_mask(sdev);
struct sof_intel_hda_dev *hdev;
+ if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH)))
+ return;
+
hdev = sdev->pdata->hw_pdata;
if (!hdev->sdw)
return;
@@ -927,6 +899,7 @@ static int dmic_detect_topology_fixup(struct snd_sof_dev *sdev,
static int hda_init_caps(struct snd_sof_dev *sdev)
{
+ u32 interface_mask = hda_get_interface_mask(sdev);
struct hdac_bus *bus = sof_to_bus(sdev);
struct snd_sof_pdata *pdata = sdev->pdata;
struct sof_intel_hda_dev *hdev = pdata->hw_pdata;
@@ -945,7 +918,11 @@ static int hda_init_caps(struct snd_sof_dev *sdev)
return ret;
}
- hda_bus_ml_get_capabilities(bus);
+ hda_bus_ml_init(bus);
+
+ /* Skip SoundWire if it is not supported */
+ if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH)))
+ goto skip_soundwire;
/* scan SoundWire capabilities exposed by DSDT */
ret = hda_sdw_acpi_scan(sdev);
@@ -1054,21 +1031,25 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
const struct sof_intel_dsp_desc *chip;
int ret = 0;
- /*
- * detect DSP by checking class/subclass/prog-id information
- * class=04 subclass 03 prog-if 00: no DSP, legacy driver is required
- * class=04 subclass 01 prog-if 00: DSP is present
- * (and may be required e.g. for DMIC or SSP support)
- * class=04 subclass 03 prog-if 80: either of DSP or legacy mode works
- */
- if (pci->class == 0x040300) {
- dev_err(sdev->dev, "error: the DSP is not enabled on this platform, aborting probe\n");
- return -ENODEV;
- } else if (pci->class != 0x040100 && pci->class != 0x040380) {
- dev_err(sdev->dev, "error: unknown PCI class/subclass/prog-if 0x%06x found, aborting probe\n", pci->class);
- return -ENODEV;
+ if (!sdev->dspless_mode_selected) {
+ /*
+ * detect DSP by checking class/subclass/prog-id information
+ * class=04 subclass 03 prog-if 00: no DSP, legacy driver is required
+ * class=04 subclass 01 prog-if 00: DSP is present
+ * (and may be required e.g. for DMIC or SSP support)
+ * class=04 subclass 03 prog-if 80: either of DSP or legacy mode works
+ */
+ if (pci->class == 0x040300) {
+ dev_err(sdev->dev, "the DSP is not enabled on this platform, aborting probe\n");
+ return -ENODEV;
+ } else if (pci->class != 0x040100 && pci->class != 0x040380) {
+ dev_err(sdev->dev, "unknown PCI class/subclass/prog-if 0x%06x found, aborting probe\n",
+ pci->class);
+ return -ENODEV;
+ }
+ dev_info(sdev->dev, "DSP detected with PCI class/subclass/prog-if 0x%06x\n",
+ pci->class);
}
- dev_info(sdev->dev, "DSP detected with PCI class/subclass/prog-if 0x%06x\n", pci->class);
chip = get_chip_info(sdev->pdata);
if (!chip) {
@@ -1104,12 +1085,18 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
hdev->no_ipc_position = sof_ops(sdev)->pcm_pointer ? 1 : 0;
#endif
+ if (sdev->dspless_mode_selected)
+ hdev->no_ipc_position = 1;
+
/* set up HDA base */
bus = sof_to_bus(sdev);
ret = hda_init(sdev);
if (ret < 0)
goto hdac_bus_unmap;
+ if (sdev->dspless_mode_selected)
+ goto skip_dsp_setup;
+
/* DSP base */
sdev->bar[HDA_DSP_BAR] = pci_ioremap_bar(pci, HDA_DSP_BAR);
if (!sdev->bar[HDA_DSP_BAR]) {
@@ -1120,6 +1107,7 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
sdev->mmio_bar = HDA_DSP_BAR;
sdev->mailbox_bar = HDA_DSP_BAR;
+skip_dsp_setup:
/* allow 64bit DMA address if supported by H/W */
if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(64))) {
@@ -1185,14 +1173,16 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
if (ret < 0)
goto free_ipc_irq;
- /* enable ppcap interrupt */
- hda_dsp_ctrl_ppcap_enable(sdev, true);
- hda_dsp_ctrl_ppcap_int_enable(sdev, true);
+ if (!sdev->dspless_mode_selected) {
+ /* enable ppcap interrupt */
+ hda_dsp_ctrl_ppcap_enable(sdev, true);
+ hda_dsp_ctrl_ppcap_int_enable(sdev, true);
- /* set default mailbox offset for FW ready message */
- sdev->dsp_box.offset = HDA_DSP_MBOX_UPLINK_OFFSET;
+ /* set default mailbox offset for FW ready message */
+ sdev->dsp_box.offset = HDA_DSP_MBOX_UPLINK_OFFSET;
- INIT_DELAYED_WORK(&hdev->d0i3_work, hda_dsp_d0i3_work);
+ INIT_DELAYED_WORK(&hdev->d0i3_work, hda_dsp_d0i3_work);
+ }
init_waitqueue_head(&hdev->waitq);
@@ -1208,7 +1198,8 @@ free_irq_vector:
free_streams:
hda_dsp_stream_free(sdev);
/* dsp_unmap: not currently used */
- iounmap(sdev->bar[HDA_DSP_BAR]);
+ if (!sdev->dspless_mode_selected)
+ iounmap(sdev->bar[HDA_DSP_BAR]);
hdac_bus_unmap:
platform_device_unregister(hdev->dmic_dev);
iounmap(bus->remap_addr);
@@ -1228,8 +1219,9 @@ int hda_dsp_remove(struct snd_sof_dev *sdev)
if (nhlt)
intel_nhlt_free(nhlt);
- /* cancel any attempt for DSP D0I3 */
- cancel_delayed_work_sync(&hda->d0i3_work);
+ if (!sdev->dspless_mode_selected)
+ /* cancel any attempt for DSP D0I3 */
+ cancel_delayed_work_sync(&hda->d0i3_work);
hda_codec_device_remove(sdev);
@@ -1238,14 +1230,19 @@ int hda_dsp_remove(struct snd_sof_dev *sdev)
if (!IS_ERR_OR_NULL(hda->dmic_dev))
platform_device_unregister(hda->dmic_dev);
- /* disable DSP IRQ */
- snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
- SOF_HDA_PPCTL_PIE, 0);
+ if (!sdev->dspless_mode_selected) {
+ /* disable DSP IRQ */
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
+ SOF_HDA_PPCTL_PIE, 0);
+ }
/* disable CIE and GIE interrupts */
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL,
SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN, 0);
+ if (sdev->dspless_mode_selected)
+ goto skip_disable_dsp;
+
/* no need to check for error as the DSP will be disabled anyway */
if (chip && chip->power_down_dsp)
chip->power_down_dsp(sdev);
@@ -1254,6 +1251,7 @@ int hda_dsp_remove(struct snd_sof_dev *sdev)
snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL,
SOF_HDA_PPCTL_GPROCEN, 0);
+skip_disable_dsp:
free_irq(sdev->ipc_irq, sdev);
if (sdev->msi_enabled)
pci_free_irq_vectors(pci);
@@ -1262,7 +1260,9 @@ int hda_dsp_remove(struct snd_sof_dev *sdev)
hda_bus_ml_free(sof_to_bus(sdev));
- iounmap(sdev->bar[HDA_DSP_BAR]);
+ if (!sdev->dspless_mode_selected)
+ iounmap(sdev->bar[HDA_DSP_BAR]);
+
iounmap(bus->remap_addr);
sof_hda_bus_exit(sdev);
@@ -1568,12 +1568,16 @@ void hda_set_mach_params(struct snd_soc_acpi_mach *mach,
struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev)
{
+ u32 interface_mask = hda_get_interface_mask(sdev);
struct snd_sof_pdata *sof_pdata = sdev->pdata;
const struct sof_dev_desc *desc = sof_pdata->desc;
- struct snd_soc_acpi_mach *mach;
+ struct snd_soc_acpi_mach *mach = NULL;
const char *tplg_filename;
- mach = snd_soc_acpi_find_machine(desc->machines);
+ /* Try I2S or DMIC if it is supported */
+ if (interface_mask & (BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC)))
+ mach = snd_soc_acpi_find_machine(desc->machines);
+
if (mach) {
bool add_extension = false;
bool tplg_fixup = false;
@@ -1680,10 +1684,8 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev)
}
}
- /*
- * If I2S fails, try SoundWire
- */
- if (!mach)
+ /* If I2S fails, try SoundWire if it is supported */
+ if (!mach && (interface_mask & BIT(SOF_DAI_INTEL_ALH)))
mach = hda_sdw_machine_select(sdev);
/*
@@ -1729,3 +1731,4 @@ MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA);
MODULE_IMPORT_NS(SND_INTEL_SOUNDWIRE_ACPI);
MODULE_IMPORT_NS(SOUNDWIRE_INTEL_INIT);
MODULE_IMPORT_NS(SOUNDWIRE_INTEL);
+MODULE_IMPORT_NS(SND_SOC_SOF_HDA_MLINK);
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h
index 45f9d4248f14..c4befacde23e 100644
--- a/sound/soc/sof/intel/hda.h
+++ b/sound/soc/sof/intel/hda.h
@@ -502,7 +502,7 @@ struct sof_intel_hda_dev {
u32 stream_max;
/* PM related */
- bool l1_support_changed;/* during suspend, is L1SEN changed or not */
+ bool l1_disabled;/* is DMI link L1 disabled? */
/* DMIC device */
struct platform_device *dmic_dev;
@@ -584,8 +584,10 @@ void hda_dsp_ipc_int_enable(struct snd_sof_dev *sdev);
void hda_dsp_ipc_int_disable(struct snd_sof_dev *sdev);
bool hda_dsp_core_is_enabled(struct snd_sof_dev *sdev, unsigned int core_mask);
-int hda_dsp_set_power_state(struct snd_sof_dev *sdev,
- const struct sof_dsp_power_state *target_state);
+int hda_dsp_set_power_state_ipc3(struct snd_sof_dev *sdev,
+ const struct sof_dsp_power_state *target_state);
+int hda_dsp_set_power_state_ipc4(struct snd_sof_dev *sdev,
+ const struct sof_dsp_power_state *target_state);
int hda_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state);
int hda_dsp_resume(struct snd_sof_dev *sdev);
@@ -763,26 +765,6 @@ static inline int hda_codec_i915_exit(struct snd_sof_dev *sdev) { return 0; }
#endif
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
-
-void hda_bus_ml_get_capabilities(struct hdac_bus *bus);
-void hda_bus_ml_free(struct hdac_bus *bus);
-void hda_bus_ml_put_all(struct hdac_bus *bus);
-void hda_bus_ml_reset_losidv(struct hdac_bus *bus);
-int hda_bus_ml_resume(struct hdac_bus *bus);
-int hda_bus_ml_suspend(struct hdac_bus *bus);
-
-#else
-
-static inline void hda_bus_ml_get_capabilities(struct hdac_bus *bus) { }
-static inline void hda_bus_ml_free(struct hdac_bus *bus) { }
-static inline void hda_bus_ml_put_all(struct hdac_bus *bus) { }
-static inline void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { }
-static inline int hda_bus_ml_resume(struct hdac_bus *bus) { return 0; }
-static inline int hda_bus_ml_suspend(struct hdac_bus *bus) { return 0; }
-
-#endif /* CONFIG_SND_SOC_SOF_HDA */
-
/*
* Trace Control.
*/
@@ -896,10 +878,6 @@ int hda_pci_intel_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
struct snd_sof_dai;
struct sof_ipc_dai_config;
-int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags,
- struct snd_sof_dai_config_data *data);
-int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags,
- struct snd_sof_dai_config_data *data);
#define SOF_HDA_POSITION_QUIRK_USE_SKYLAKE_LEGACY (0) /* previous implementation */
#define SOF_HDA_POSITION_QUIRK_USE_DPIB_REGISTERS (1) /* recommended if VC0 only */
@@ -928,4 +906,41 @@ extern struct sdw_intel_ops sdw_callback;
struct sof_ipc4_fw_library;
int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev,
struct sof_ipc4_fw_library *fw_lib, bool reload);
+
+/**
+ * struct hda_dai_widget_dma_ops - DAI DMA ops optional by default unless specified otherwise
+ * @get_hext_stream: Mandatory function pointer to get the saved pointer to struct hdac_ext_stream
+ * @assign_hext_stream: Function pointer to assign a hdac_ext_stream
+ * @release_hext_stream: Function pointer to release the hdac_ext_stream
+ * @setup_hext_stream: Function pointer for hdac_ext_stream setup
+ * @reset_hext_stream: Function pointer for hdac_ext_stream reset
+ * @pre_trigger: Function pointer for DAI DMA pre-trigger actions
+ * @trigger: Function pointer for DAI DMA trigger actions
+ * @post_trigger: Function pointer for DAI DMA post-trigger actions
+ */
+struct hda_dai_widget_dma_ops {
+ struct hdac_ext_stream *(*get_hext_stream)(struct snd_sof_dev *sdev,
+ struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream);
+ struct hdac_ext_stream *(*assign_hext_stream)(struct snd_sof_dev *sdev,
+ struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream);
+ void (*release_hext_stream)(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream);
+ void (*setup_hext_stream)(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream,
+ unsigned int format_val);
+ void (*reset_hext_stream)(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_sream);
+ int (*pre_trigger)(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream, int cmd);
+ int (*trigger)(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream, int cmd);
+ int (*post_trigger)(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream, int cmd);
+};
+
+const struct hda_dai_widget_dma_ops *
+hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget);
+int hda_dai_config(struct snd_soc_dapm_widget *w, unsigned int flags,
+ struct snd_sof_dai_config_data *data);
+
#endif
diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c
index 435941a1692f..0f249efc6a5a 100644
--- a/sound/soc/sof/intel/icl.c
+++ b/sound/soc/sof/intel/icl.c
@@ -116,6 +116,8 @@ int sof_icl_ops_init(struct snd_sof_dev *sdev)
/* debug */
sof_icl_ops.ipc_dump = cnl_ipc_dump;
+
+ sof_icl_ops.set_power_state = hda_dsp_set_power_state_ipc3;
}
if (sdev->pdata->ipc_type == SOF_INTEL_IPC4) {
@@ -141,6 +143,8 @@ int sof_icl_ops_init(struct snd_sof_dev *sdev)
/* debug */
sof_icl_ops.ipc_dump = cnl_ipc4_dump;
+
+ sof_icl_ops.set_power_state = hda_dsp_set_power_state_ipc4;
}
/* debug */
diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c
index 307faad2ecf4..46caf3ccde66 100644
--- a/sound/soc/sof/intel/mtl.c
+++ b/sound/soc/sof/intel/mtl.c
@@ -60,6 +60,9 @@ static bool mtl_dsp_check_ipc_irq(struct snd_sof_dev *sdev)
u32 irq_status;
u32 hfintipptr;
+ if (sdev->dspless_mode_selected)
+ return false;
+
/* read Interrupt IP Pointer */
hfintipptr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_HFINTIPPTR) & MTL_HFINTIPPTR_PTR_MASK;
irq_status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, hfintipptr + MTL_DSP_IRQSTS);
@@ -120,6 +123,9 @@ static void mtl_enable_ipc_interrupts(struct snd_sof_dev *sdev)
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
const struct sof_intel_dsp_desc *chip = hda->desc;
+ if (sdev->dspless_mode_selected)
+ return;
+
/* enable IPC DONE and BUSY interrupts */
snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl,
MTL_DSP_REG_HFIPCXCTL_BUSY | MTL_DSP_REG_HFIPCXCTL_DONE,
@@ -131,6 +137,9 @@ static void mtl_disable_ipc_interrupts(struct snd_sof_dev *sdev)
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
const struct sof_intel_dsp_desc *chip = hda->desc;
+ if (sdev->dspless_mode_selected)
+ return;
+
/* disable IPC DONE and BUSY interrupts */
snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, chip->ipc_ctl,
MTL_DSP_REG_HFIPCXCTL_BUSY | MTL_DSP_REG_HFIPCXCTL_DONE, 0);
@@ -143,6 +152,9 @@ static void mtl_enable_sdw_irq(struct snd_sof_dev *sdev, bool enable)
u32 val;
int ret;
+ if (sdev->dspless_mode_selected)
+ return;
+
/* Enable/Disable SoundWire interrupt */
mask = MTL_DSP_REG_HfSNDWIE_IE_MASK;
if (enable)
@@ -170,6 +182,9 @@ static int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable)
u32 val;
int ret;
+ if (sdev->dspless_mode_selected)
+ return 0;
+
/* read Interrupt IP Pointer */
hfintipptr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, MTL_HFINTIPPTR) & MTL_HFINTIPPTR_PTR_MASK;
@@ -217,6 +232,7 @@ static int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable)
/* pre fw run operations */
static int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev)
{
+ struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
u32 dsphfpwrsts;
u32 dsphfdsscs;
u32 cpa;
@@ -255,9 +271,11 @@ static int mtl_dsp_pre_fw_run(struct snd_sof_dev *sdev)
if (ret < 0)
dev_err(sdev->dev, "failed to power up gated DSP domain\n");
- /* make sure SoundWire is not power-gated */
- snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, MTL_HFPWRCTL,
- MTL_HfPWRCTL_WPIOXPG(1), MTL_HfPWRCTL_WPIOXPG(1));
+ /* if SoundWire is used, make sure it is not power-gated */
+ if (hdev->info.handle && hdev->info.link_mask > 0)
+ snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, MTL_HFPWRCTL,
+ MTL_HfPWRCTL_WPIOXPG(1), MTL_HfPWRCTL_WPIOXPG(1));
+
return ret;
}
@@ -650,6 +668,8 @@ int sof_mtl_ops_init(struct snd_sof_dev *sdev)
/* set DAI ops */
hda_set_dai_drv_ops(sdev, &sof_mtl_ops);
+ sof_mtl_ops.set_power_state = hda_dsp_set_power_state_ipc4;
+
return 0;
};
EXPORT_SYMBOL_NS(sof_mtl_ops_init, SND_SOC_SOF_INTEL_HDA_COMMON);
diff --git a/sound/soc/sof/intel/pci-apl.c b/sound/soc/sof/intel/pci-apl.c
index aff6cb573c27..69cad5a6bc72 100644
--- a/sound/soc/sof/intel/pci-apl.c
+++ b/sound/soc/sof/intel/pci-apl.c
@@ -29,6 +29,7 @@ static const struct sof_dev_desc bxt_desc = {
.chip_info = &apl_chip_info,
.ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_IPC,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC] = "intel/sof",
[SOF_INTEL_IPC4] = "intel/avs/apl",
@@ -60,6 +61,7 @@ static const struct sof_dev_desc glk_desc = {
.chip_info = &apl_chip_info,
.ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_IPC,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC] = "intel/sof",
[SOF_INTEL_IPC4] = "intel/avs/glk",
diff --git a/sound/soc/sof/intel/pci-cnl.c b/sound/soc/sof/intel/pci-cnl.c
index 4c0c1c369dcd..8895508a0be6 100644
--- a/sound/soc/sof/intel/pci-cnl.c
+++ b/sound/soc/sof/intel/pci-cnl.c
@@ -30,6 +30,7 @@ static const struct sof_dev_desc cnl_desc = {
.chip_info = &cnl_chip_info,
.ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_IPC,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC] = "intel/sof",
[SOF_INTEL_IPC4] = "intel/avs/cnl",
@@ -62,6 +63,7 @@ static const struct sof_dev_desc cfl_desc = {
.chip_info = &cnl_chip_info,
.ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_IPC,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC] = "intel/sof",
[SOF_INTEL_IPC4] = "intel/avs/cnl",
@@ -94,6 +96,7 @@ static const struct sof_dev_desc cml_desc = {
.chip_info = &cnl_chip_info,
.ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_IPC,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC] = "intel/sof",
[SOF_INTEL_IPC4] = "intel/avs/cnl",
diff --git a/sound/soc/sof/intel/pci-icl.c b/sound/soc/sof/intel/pci-icl.c
index 6785669113b3..5fb5a820693e 100644
--- a/sound/soc/sof/intel/pci-icl.c
+++ b/sound/soc/sof/intel/pci-icl.c
@@ -30,6 +30,7 @@ static const struct sof_dev_desc icl_desc = {
.chip_info = &icl_chip_info,
.ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_IPC,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC] = "intel/sof",
[SOF_INTEL_IPC4] = "intel/avs/icl",
@@ -61,6 +62,7 @@ static const struct sof_dev_desc jsl_desc = {
.chip_info = &jsl_chip_info,
.ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_IPC,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC] = "intel/sof",
[SOF_INTEL_IPC4] = "intel/avs/jsl",
diff --git a/sound/soc/sof/intel/pci-mtl.c b/sound/soc/sof/intel/pci-mtl.c
index b183dc0014b4..e276e1e37fed 100644
--- a/sound/soc/sof/intel/pci-mtl.c
+++ b/sound/soc/sof/intel/pci-mtl.c
@@ -31,6 +31,7 @@ static const struct sof_dev_desc mtl_desc = {
.chip_info = &mtl_chip_info,
.ipc_supported_mask = BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_INTEL_IPC4,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_INTEL_IPC4] = "intel/sof-ipc4/mtl",
},
diff --git a/sound/soc/sof/intel/pci-skl.c b/sound/soc/sof/intel/pci-skl.c
index 5b4bccf81965..5e69af6eed34 100644
--- a/sound/soc/sof/intel/pci-skl.c
+++ b/sound/soc/sof/intel/pci-skl.c
@@ -26,6 +26,7 @@ static struct sof_dev_desc skl_desc = {
.irqindex_host_ipc = -1,
.ipc_supported_mask = BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_INTEL_IPC4,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_INTEL_IPC4] = "intel/avs/skl",
},
@@ -50,6 +51,7 @@ static struct sof_dev_desc kbl_desc = {
.irqindex_host_ipc = -1,
.ipc_supported_mask = BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_INTEL_IPC4,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_INTEL_IPC4] = "intel/avs/kbl",
},
diff --git a/sound/soc/sof/intel/pci-tgl.c b/sound/soc/sof/intel/pci-tgl.c
index 22e769e0831d..ca37ff1bbd2a 100644
--- a/sound/soc/sof/intel/pci-tgl.c
+++ b/sound/soc/sof/intel/pci-tgl.c
@@ -30,6 +30,7 @@ static const struct sof_dev_desc tgl_desc = {
.chip_info = &tgl_chip_info,
.ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_IPC,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC] = "intel/sof",
[SOF_INTEL_IPC4] = "intel/avs/tgl",
@@ -62,6 +63,7 @@ static const struct sof_dev_desc tglh_desc = {
.chip_info = &tglh_chip_info,
.ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_IPC,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC] = "intel/sof",
[SOF_INTEL_IPC4] = "intel/avs/tgl-h",
@@ -93,6 +95,7 @@ static const struct sof_dev_desc ehl_desc = {
.chip_info = &ehl_chip_info,
.ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_IPC,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC] = "intel/sof",
[SOF_INTEL_IPC4] = "intel/avs/ehl",
@@ -125,6 +128,7 @@ static const struct sof_dev_desc adls_desc = {
.chip_info = &adls_chip_info,
.ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_IPC,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC] = "intel/sof",
[SOF_INTEL_IPC4] = "intel/avs/adl-s",
@@ -157,6 +161,7 @@ static const struct sof_dev_desc adl_desc = {
.chip_info = &tgl_chip_info,
.ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_IPC,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC] = "intel/sof",
[SOF_INTEL_IPC4] = "intel/avs/adl",
@@ -189,6 +194,7 @@ static const struct sof_dev_desc adl_n_desc = {
.chip_info = &tgl_chip_info,
.ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_IPC,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC] = "intel/sof",
[SOF_INTEL_IPC4] = "intel/avs/adl-n",
@@ -221,6 +227,7 @@ static const struct sof_dev_desc rpls_desc = {
.chip_info = &adls_chip_info,
.ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_IPC,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC] = "intel/sof",
[SOF_INTEL_IPC4] = "intel/avs/rpl-s",
@@ -253,6 +260,7 @@ static const struct sof_dev_desc rpl_desc = {
.chip_info = &tgl_chip_info,
.ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4),
.ipc_default = SOF_IPC,
+ .dspless_mode_supported = true, /* Only supported for HDaudio */
.default_fw_path = {
[SOF_IPC] = "intel/sof",
[SOF_INTEL_IPC4] = "intel/avs/rpl",
diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c
index 58ac3a46e6a7..2713b7dc7931 100644
--- a/sound/soc/sof/intel/tgl.c
+++ b/sound/soc/sof/intel/tgl.c
@@ -71,6 +71,8 @@ int sof_tgl_ops_init(struct snd_sof_dev *sdev)
/* debug */
sof_tgl_ops.ipc_dump = cnl_ipc_dump;
+
+ sof_tgl_ops.set_power_state = hda_dsp_set_power_state_ipc3;
}
if (sdev->pdata->ipc_type == SOF_INTEL_IPC4) {
@@ -96,6 +98,8 @@ int sof_tgl_ops_init(struct snd_sof_dev *sdev)
/* debug */
sof_tgl_ops.ipc_dump = cnl_ipc4_dump;
+
+ sof_tgl_ops.set_power_state = hda_dsp_set_power_state_ipc4;
}
/* set DAI driver ops */