summaryrefslogtreecommitdiff
path: root/sound/soc/sof/intel/hda-dai.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/sof/intel/hda-dai.c')
-rw-r--r--sound/soc/sof/intel/hda-dai.c1051
1 files changed, 625 insertions, 426 deletions
diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c
index cd12589355ef..883d0d3bae9e 100644
--- a/sound/soc/sof/intel/hda-dai.c
+++ b/sound/soc/sof/intel/hda-dai.c
@@ -3,565 +3,772 @@
// 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) 2018 Intel Corporation. All rights reserved.
+// Copyright(c) 2018 Intel Corporation
//
// Authors: Keyon Jie <yang.jie@linux.intel.com>
//
#include <sound/pcm_params.h>
#include <sound/hdaudio_ext.h>
+#include <sound/hda-mlink.h>
+#include <sound/hda_register.h>
+#include <sound/intel-nhlt.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"
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
-
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
-#include "../sof-probes.h"
-#endif
-
-struct hda_pipe_params {
- u32 ch;
- u32 s_freq;
- u32 s_fmt;
- u8 linktype;
- snd_pcm_format_t format;
- int link_index;
- int stream;
- unsigned int link_bps;
-};
-
/*
- * 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.
+ * The default method is to fetch NHLT from BIOS. With this parameter set
+ * it is possible to override that with NHLT in the SOF topology manifest.
*/
-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;
- }
+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");
- return false;
-}
-
-static struct hdac_ext_stream *
- hda_link_stream_assign(struct hdac_bus *bus,
- struct snd_pcm_substream *substream)
+int hda_dai_config(struct snd_soc_dapm_widget *w, unsigned int flags,
+ struct snd_sof_dai_config_data *data)
{
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct sof_intel_hda_stream *hda_stream;
- struct hdac_ext_stream *res = NULL;
- struct hdac_stream *stream = NULL;
+ struct snd_sof_widget *swidget = w->dobj.private;
+ const struct sof_ipc_tplg_ops *tplg_ops;
+ struct snd_sof_dev *sdev;
+ int ret;
- int stream_dir = substream->stream;
+ if (!swidget)
+ return 0;
- if (!bus->ppcap) {
- dev_err(bus->dev, "stream type not supported\n");
- return NULL;
- }
+ sdev = widget_to_sdev(w);
+ tplg_ops = sof_ipc_get_ops(sdev, tplg);
- spin_lock_irq(&bus->reg_lock);
- list_for_each_entry(stream, &bus->stream_list, list) {
- struct hdac_ext_stream *hstream =
- stream_to_hdac_ext_stream(stream);
- if (stream->direction != substream->stream)
- continue;
-
- hda_stream = hstream_to_sof_hda_stream(hstream);
-
- /* check if link is available */
- if (!hstream->link_locked) {
- if (stream->opened) {
- /*
- * check if the stream tag matches the stream
- * tag of one of the connected FEs
- */
- if (hda_check_fes(rtd, stream_dir,
- stream->stream_tag)) {
- res = hstream;
- break;
- }
- } else {
- res = hstream;
-
- /*
- * This must be a hostless stream.
- * So reserve the host DMA channel.
- */
- hda_stream->host_reserved = 1;
- break;
- }
+ 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;
}
}
- if (res) {
- /*
- * Decouple host and link DMA. The decoupled flag
- * is updated in snd_hdac_ext_stream_decouple().
- */
- if (!res->decoupled)
- snd_hdac_ext_stream_decouple_locked(bus, res, true);
+ return 0;
+}
+EXPORT_SYMBOL_NS(hda_dai_config, "SND_SOC_SOF_INTEL_HDA_COMMON");
- res->link_locked = 1;
- res->link_substream = substream;
- }
- spin_unlock_irq(&bus->reg_lock);
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
- return res;
+static struct snd_sof_dev *dai_to_sdev(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);
+
+ return widget_to_sdev(w);
}
-static int hda_link_dma_params(struct hdac_ext_stream *stream,
- struct hda_pipe_params *params)
+static const struct hda_dai_widget_dma_ops *
+hda_dai_get_ops(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai)
{
- struct hdac_stream *hstream = &stream->hstream;
- unsigned char stream_tag = hstream->stream_tag;
- struct hdac_bus *bus = hstream->bus;
- struct hdac_ext_link *link;
- unsigned int format_val;
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
+ struct snd_sof_widget *swidget = w->dobj.private;
+ struct snd_sof_dev *sdev;
+ struct snd_sof_dai *sdai;
- snd_hdac_ext_stream_decouple(bus, stream, true);
- snd_hdac_ext_link_stream_reset(stream);
+ sdev = widget_to_sdev(w);
- format_val = snd_hdac_calc_stream_format(params->s_freq, params->ch,
- params->format,
- params->link_bps, 0);
+ if (!swidget) {
+ dev_err(sdev->dev, "%s: swidget is NULL\n", __func__);
+ return NULL;
+ }
- dev_dbg(bus->dev, "format_val=%d, rate=%d, ch=%d, format=%d\n",
- format_val, params->s_freq, params->ch, params->format);
+ if (sdev->dspless_mode_selected)
+ return hda_select_dai_widget_ops(sdev, swidget);
- snd_hdac_ext_link_stream_setup(stream, format_val);
+ sdai = swidget->private;
- if (stream->hstream.direction == SNDRV_PCM_STREAM_PLAYBACK) {
- list_for_each_entry(link, &bus->hlink_list, list) {
- if (link->index == params->link_index)
- snd_hdac_ext_link_set_stream_id(link,
- stream_tag);
- }
- }
+ /* 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;
- stream->link_prepared = 1;
+ /* check if mandatory ops are set */
+ if (!ops || !ops->get_hext_stream)
+ return NULL;
- return 0;
+ sdai->platform_private = ops;
+ }
+
+ return sdai->platform_private;
}
-/* Update config for the DAI widget */
-static struct sof_ipc_dai_config *hda_dai_update_config(struct snd_soc_dapm_widget *w,
- int channel)
+static int
+hda_link_dma_cleanup(struct snd_pcm_substream *substream,
+ struct hdac_ext_stream *hext_stream,
+ struct snd_soc_dai *cpu_dai, bool release)
{
- struct snd_sof_widget *swidget = w->dobj.private;
- struct sof_ipc_dai_config *config;
- struct snd_sof_dai *sof_dai;
+ const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai);
+ struct sof_intel_hda_stream *hda_stream;
+ struct hdac_ext_link *hlink;
+ struct snd_sof_dev *sdev;
+ int stream_tag;
- if (!swidget)
- return NULL;
+ if (!ops) {
+ dev_err(cpu_dai->dev, "DAI widget ops not set\n");
+ return -EINVAL;
+ }
- sof_dai = swidget->private;
+ sdev = dai_to_sdev(substream, cpu_dai);
- if (!sof_dai || !sof_dai->dai_config) {
- dev_err(swidget->scomp->dev, "error: No config for DAI %s\n", w->name);
- return NULL;
+ hlink = ops->get_hlink(sdev, substream);
+ if (!hlink)
+ return -EINVAL;
+
+ 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);
}
- config = &sof_dai->dai_config[sof_dai->current_config];
+ if (!release) {
+ /*
+ * Force stream reconfiguration without releasing the channel on
+ * subsequent stream restart (without free), including LinkDMA
+ * reset.
+ * The stream is released via hda_dai_hw_free()
+ */
+ hext_stream->link_prepared = 0;
+ return 0;
+ }
+
+ if (ops->release_hext_stream)
+ ops->release_hext_stream(sdev, cpu_dai, substream);
- /* update config with stream tag */
- config->hda.link_dma_ch = channel;
+ hext_stream->link_prepared = 0;
+
+ /* free the host DMA channel reserved by hostless streams */
+ hda_stream = hstream_to_sof_hda_stream(hext_stream);
+ hda_stream->host_reserved = 0;
- return config;
+ return 0;
}
-static int hda_link_dai_widget_update(struct sof_intel_hda_stream *hda_stream,
- struct snd_soc_dapm_widget *w,
- int channel, bool widget_setup)
+static int hda_link_dma_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai)
{
- struct snd_sof_dev *sdev = hda_stream->sdev;
- struct sof_ipc_dai_config *config;
+ const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai);
+ struct hdac_ext_stream *hext_stream;
+ struct hdac_stream *hstream;
+ struct hdac_ext_link *hlink;
+ struct snd_sof_dev *sdev;
+ int stream_tag;
- config = hda_dai_update_config(w, channel);
- if (!config) {
- dev_err(sdev->dev, "error: no config for DAI %s\n", w->name);
- return -ENOENT;
+ if (!ops) {
+ dev_err(cpu_dai->dev, "DAI widget ops not set\n");
+ return -EINVAL;
}
- /* 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);
-
- return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE);
-}
+ sdev = dai_to_sdev(substream, cpu_dai);
-static int hda_link_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- struct hdac_stream *hstream = substream->runtime->private_data;
- struct hdac_bus *bus = hstream->bus;
- struct hdac_ext_stream *link_dev;
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
- struct sof_intel_hda_stream *hda_stream;
- struct hda_pipe_params p_params = {0};
- struct snd_soc_dapm_widget *w;
- struct hdac_ext_link *link;
- int stream_tag;
- int ret;
+ hlink = ops->get_hlink(sdev, substream);
+ if (!hlink)
+ return -EINVAL;
- /* get stored dma data if resuming from system suspend */
- link_dev = snd_soc_dai_get_dma_data(dai, substream);
- if (!link_dev) {
- link_dev = hda_link_stream_assign(bus, substream);
- if (!link_dev)
- return -EBUSY;
+ hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
- snd_soc_dai_set_dma_data(dai, substream, (void *)link_dev);
+ if (!hext_stream) {
+ if (ops->assign_hext_stream)
+ hext_stream = ops->assign_hext_stream(sdev, cpu_dai, substream);
}
- stream_tag = hdac_stream(link_dev)->stream_tag;
+ if (!hext_stream)
+ return -EBUSY;
- hda_stream = hstream_to_sof_hda_stream(link_dev);
+ hstream = &hext_stream->hstream;
+ stream_tag = hstream->stream_tag;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- w = dai->playback_widget;
- else
- w = dai->capture_widget;
+ if (hext_stream->hstream.direction == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_hdac_ext_bus_link_set_stream_id(hlink, stream_tag);
- /* set up the DAI widget and send the DAI_CONFIG with the new tag */
- ret = hda_link_dai_widget_update(hda_stream, w, stream_tag - 1, true);
- if (ret < 0)
- return ret;
+ /* set the hdac_stream in the codec dai */
+ if (ops->codec_dai_set_stream)
+ ops->codec_dai_set_stream(sdev, substream, hstream);
- link = snd_hdac_ext_bus_get_link(bus, codec_dai->component->name);
- if (!link)
- return -EINVAL;
+ if (ops->reset_hext_stream)
+ ops->reset_hext_stream(sdev, hext_stream);
- /* set the hdac_stream in the codec dai */
- snd_soc_dai_set_stream(codec_dai, hdac_stream(link_dev), substream->stream);
+ if (ops->calc_stream_format && ops->setup_hext_stream) {
+ unsigned int format_val = ops->calc_stream_format(sdev, substream, params);
- p_params.s_fmt = snd_pcm_format_width(params_format(params));
- p_params.ch = params_channels(params);
- p_params.s_freq = params_rate(params);
- p_params.stream = substream->stream;
- p_params.link_index = link->index;
- p_params.format = params_format(params);
+ ops->setup_hext_stream(sdev, hext_stream, format_val);
+ }
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- p_params.link_bps = codec_dai->driver->playback.sig_bits;
- else
- p_params.link_bps = codec_dai->driver->capture.sig_bits;
+ hext_stream->link_prepared = 1;
- return hda_link_dma_params(link_dev, &p_params);
+ return 0;
}
-static int hda_link_pcm_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+static int __maybe_unused hda_dai_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
{
- struct hdac_ext_stream *link_dev =
- snd_soc_dai_get_dma_data(dai, substream);
- 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, cpu_dai);
+ struct hdac_ext_stream *hext_stream;
+ struct snd_sof_dev *sdev = dai_to_sdev(substream, cpu_dai);
- if (link_dev->link_prepared)
- return 0;
+ if (!ops) {
+ dev_err(cpu_dai->dev, "DAI widget ops not set\n");
+ return -EINVAL;
+ }
- dev_dbg(sdev->dev, "hda: prepare stream dir %d\n", substream->stream);
+ hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
+ if (!hext_stream)
+ return 0;
- return hda_link_hw_params(substream, &rtd->dpcm[stream].hw_params,
- dai);
+ return hda_link_dma_cleanup(substream, hext_stream, cpu_dai, true);
}
-static int hda_link_dai_config_pause_push_ipc(struct snd_soc_dapm_widget *w)
+static int __maybe_unused hda_dai_hw_params_data(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai,
+ struct snd_sof_dai_config_data *data,
+ unsigned int flags)
{
- 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);
- struct sof_ipc_dai_config *config;
- struct snd_sof_dai *sof_dai;
- struct sof_ipc_reply reply;
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, 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_dev *sdev = widget_to_sdev(w);
int ret;
- sof_dai = swidget->private;
-
- if (!sof_dai || !sof_dai->dai_config) {
- dev_err(sdev->dev, "No config for DAI %s\n", w->name);
+ if (!ops) {
+ dev_err(sdev->dev, "DAI widget ops not set\n");
return -EINVAL;
}
- config = &sof_dai->dai_config[sof_dai->current_config];
-
- /* set PAUSE command flag */
- config->flags = FIELD_PREP(SOF_DAI_CONFIG_FLAGS_CMD_MASK, SOF_DAI_CONFIG_FLAGS_PAUSE);
+ hext_stream = ops->get_hext_stream(sdev, dai, substream);
+ if (hext_stream && hext_stream->link_prepared)
+ return 0;
- ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size,
- &reply, sizeof(reply));
+ ret = hda_link_dma_hw_params(substream, params, dai);
if (ret < 0)
- dev_err(sdev->dev, "DAI config for %s failed during pause push\n", w->name);
+ return ret;
+
+ hext_stream = ops->get_hext_stream(sdev, dai, substream);
+
+ 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_link_pcm_trigger(struct snd_pcm_substream *substream,
- int cmd, struct snd_soc_dai *dai)
+static int __maybe_unused hda_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
- struct hdac_ext_stream *link_dev =
- snd_soc_dai_get_dma_data(dai, substream);
- struct sof_intel_hda_stream *hda_stream;
- struct snd_soc_pcm_runtime *rtd;
- struct snd_soc_dapm_widget *w;
- struct hdac_ext_link *link;
- struct hdac_stream *hstream;
- struct hdac_bus *bus;
- int stream_tag;
- int ret;
+ struct snd_sof_dai_config_data data = { 0 };
+ unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
- hstream = substream->runtime->private_data;
- bus = hstream->bus;
- rtd = asoc_substream_to_rtd(substream);
+ return hda_dai_hw_params_data(substream, params, dai, &data, flags);
+}
- link = snd_hdac_ext_bus_get_link(bus, asoc_rtd_to_codec(rtd, 0)->component->name);
- if (!link)
- return -EINVAL;
+/*
+ * 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 __maybe_unused hda_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai);
+ struct hdac_ext_stream *hext_stream;
+ struct snd_sof_dev *sdev;
+ int ret;
- hda_stream = hstream_to_sof_hda_stream(link_dev);
+ if (!ops) {
+ dev_err(dai->dev, "DAI widget ops not set\n");
+ return -EINVAL;
+ }
- dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd);
+ dev_dbg(dai->dev, "cmd=%d dai %s direction %d\n", cmd,
+ dai->name, substream->stream);
- w = snd_soc_dai_get_widget(dai, substream->stream);
+ sdev = dai_to_sdev(substream, dai);
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- snd_hdac_ext_link_stream_start(link_dev);
- break;
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_STOP:
- snd_hdac_ext_link_stream_clear(link_dev);
+ hext_stream = ops->get_hext_stream(sdev, dai, substream);
+ if (!hext_stream)
+ return -EINVAL;
- /*
- * free DAI widget during stop/suspend to keep widget use_count's balanced.
- */
- ret = hda_link_dai_widget_update(hda_stream, w, DMA_CHAN_INVALID, false);
+ if (ops->pre_trigger) {
+ ret = ops->pre_trigger(sdev, dai, substream, cmd);
if (ret < 0)
return ret;
+ }
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- stream_tag = hdac_stream(link_dev)->stream_tag;
- snd_hdac_ext_link_clear_stream_id(link, stream_tag);
- }
-
- link_dev->link_prepared = 0;
- break;
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- snd_hdac_ext_link_stream_clear(link_dev);
+ if (ops->trigger) {
+ ret = ops->trigger(sdev, dai, substream, cmd);
+ if (ret < 0)
+ return ret;
+ }
- ret = hda_link_dai_config_pause_push_ipc(w);
+ if (ops->post_trigger) {
+ ret = ops->post_trigger(sdev, dai, substream, cmd);
if (ret < 0)
return ret;
+ }
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ ret = hda_link_dma_cleanup(substream, hext_stream, dai,
+ cmd != SNDRV_PCM_TRIGGER_STOP);
+ if (ret < 0) {
+ dev_err(sdev->dev, "%s: failed to clean up link DMA\n", __func__);
+ return ret;
+ }
break;
default:
- return -EINVAL;
+ break;
}
+
return 0;
}
-static int hda_link_hw_free(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
+
+static int hda_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
- unsigned int stream_tag;
- struct sof_intel_hda_stream *hda_stream;
- struct hdac_bus *bus;
- struct hdac_ext_link *link;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ int stream = substream->stream;
+
+ return hda_dai_hw_params(substream, &rtd->dpcm[stream].hw_params, dai);
+}
+
+static const struct snd_soc_dai_ops hda_dai_ops = {
+ .hw_params = hda_dai_hw_params,
+ .hw_free = hda_dai_hw_free,
+ .trigger = hda_dai_trigger,
+ .prepare = hda_dai_prepare,
+};
+
+#endif
+
+static struct sof_ipc4_copier *widget_to_copier(struct snd_soc_dapm_widget *w)
+{
+ struct snd_sof_widget *swidget = w->dobj.private;
+ struct snd_sof_dai *sdai = swidget->private;
+ struct sof_ipc4_copier *ipc4_copier = (struct sof_ipc4_copier *)sdai->private;
+
+ return ipc4_copier;
+}
+
+static int non_hda_dai_hw_params_data(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai,
+ struct snd_sof_dai_config_data *data,
+ unsigned int flags)
+{
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct sof_ipc4_dma_config_tlv *dma_config_tlv;
+ const struct hda_dai_widget_dma_ops *ops;
+ struct sof_ipc4_dma_config *dma_config;
+ struct sof_ipc4_copier *ipc4_copier;
+ struct hdac_ext_stream *hext_stream;
struct hdac_stream *hstream;
- struct snd_soc_pcm_runtime *rtd;
- struct hdac_ext_stream *link_dev;
- struct snd_soc_dapm_widget *w;
+ struct snd_sof_dev *sdev;
+ struct snd_soc_dai *dai;
+ int cpu_dai_id;
+ int stream_id;
int ret;
- hstream = substream->runtime->private_data;
- bus = hstream->bus;
- rtd = asoc_substream_to_rtd(substream);
- link_dev = snd_soc_dai_get_dma_data(dai, substream);
-
- if (!link_dev) {
- dev_dbg(dai->dev,
- "%s: link_dev is not assigned\n", __func__);
+ ops = hda_dai_get_ops(substream, cpu_dai);
+ if (!ops) {
+ dev_err(cpu_dai->dev, "DAI widget ops not set\n");
return -EINVAL;
}
- hda_stream = hstream_to_sof_hda_stream(link_dev);
+ sdev = widget_to_sdev(w);
+ hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- w = dai->playback_widget;
- else
- w = dai->capture_widget;
+ /* nothing more to do if the link is already prepared */
+ if (hext_stream && hext_stream->link_prepared)
+ return 0;
- /* free the link DMA channel in the FW and the DAI widget */
- ret = hda_link_dai_widget_update(hda_stream, w, DMA_CHAN_INVALID, false);
- if (ret < 0)
+ /* use HDaudio stream handling */
+ ret = hda_dai_hw_params_data(substream, params, cpu_dai, data, flags);
+ if (ret < 0) {
+ dev_err(cpu_dai->dev, "%s: hda_dai_hw_params_data failed: %d\n", __func__, ret);
return ret;
+ }
- link = snd_hdac_ext_bus_get_link(bus, asoc_rtd_to_codec(rtd, 0)->component->name);
- if (!link)
- return -EINVAL;
+ if (sdev->dspless_mode_selected)
+ return 0;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- stream_tag = hdac_stream(link_dev)->stream_tag;
- snd_hdac_ext_link_clear_stream_id(link, stream_tag);
+ /* get stream_id */
+ hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
+
+ if (!hext_stream) {
+ dev_err(cpu_dai->dev, "%s: no hext_stream found\n", __func__);
+ return -ENODEV;
}
- snd_soc_dai_set_dma_data(dai, substream, NULL);
- snd_hdac_ext_stream_release(link_dev, HDAC_EXT_STREAM_TYPE_LINK);
- link_dev->link_prepared = 0;
+ hstream = &hext_stream->hstream;
+ stream_id = hstream->stream_tag;
- /* free the host DMA channel reserved by hostless streams */
- hda_stream->host_reserved = 0;
+ if (!stream_id) {
+ dev_err(cpu_dai->dev, "%s: no stream_id allocated\n", __func__);
+ return -ENODEV;
+ }
+
+ /* configure TLV */
+ ipc4_copier = widget_to_copier(w);
+
+ for_each_rtd_cpu_dais(rtd, cpu_dai_id, dai) {
+ if (dai == cpu_dai)
+ break;
+ }
+
+ dma_config_tlv = &ipc4_copier->dma_config_tlv[cpu_dai_id];
+ dma_config_tlv->type = SOF_IPC4_GTW_DMA_CONFIG_ID;
+ /* dma_config_priv_size is zero */
+ dma_config_tlv->length = sizeof(dma_config_tlv->dma_config);
+
+ dma_config = &dma_config_tlv->dma_config;
+
+ dma_config->dma_method = SOF_IPC4_DMA_METHOD_HDA;
+ dma_config->pre_allocated_by_host = 1;
+ dma_config->dma_channel_id = stream_id - 1;
+ dma_config->stream_id = stream_id;
+ /*
+ * Currently we use a DMA for each device in ALH blob. The device will
+ * be copied in sof_ipc4_prepare_copier_module.
+ */
+ dma_config->dma_stream_channel_map.device_count = 1;
+ dma_config->dma_priv_config_size = 0;
return 0;
}
-static const struct snd_soc_dai_ops hda_link_dai_ops = {
- .hw_params = hda_link_hw_params,
- .hw_free = hda_link_hw_free,
- .trigger = hda_link_pcm_trigger,
- .prepare = hda_link_pcm_prepare,
-};
+static int non_hda_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct snd_sof_dai_config_data data = { 0 };
+ unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
-#endif
+ return non_hda_dai_hw_params_data(substream, params, cpu_dai, &data, flags);
+}
+
+static int non_hda_dai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ int stream = substream->stream;
-/* only one flag used so far to harden hw_params/hw_free/trigger/prepare */
-struct ssp_dai_dma_data {
- bool setup;
+ return non_hda_dai_hw_params(substream, &rtd->dpcm[stream].hw_params, cpu_dai);
+}
+
+static const struct snd_soc_dai_ops ssp_dai_ops = {
+ .hw_params = non_hda_dai_hw_params,
+ .hw_free = hda_dai_hw_free,
+ .trigger = hda_dai_trigger,
+ .prepare = non_hda_dai_prepare,
};
-static int ssp_dai_setup_or_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai,
- bool setup)
-{
- struct snd_soc_component *component;
- struct snd_sof_widget *swidget;
- struct snd_soc_dapm_widget *w;
- struct sof_ipc_fw_version *v;
+static const struct snd_soc_dai_ops dmic_dai_ops = {
+ .hw_params = non_hda_dai_hw_params,
+ .hw_free = hda_dai_hw_free,
+ .trigger = hda_dai_trigger,
+ .prepare = non_hda_dai_prepare,
+};
+
+int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai,
+ int link_id,
+ int intel_alh_id)
+{
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct sof_ipc4_dma_config_tlv *dma_config_tlv;
+ struct snd_sof_dai_config_data data = { 0 };
+ unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
+ const struct hda_dai_widget_dma_ops *ops;
+ struct sof_ipc4_dma_config *dma_config;
+ struct sof_ipc4_copier *ipc4_copier;
+ struct hdac_ext_stream *hext_stream;
+ struct snd_soc_dai *dai;
struct snd_sof_dev *sdev;
+ bool cpu_dai_found = false;
+ int cpu_dai_id;
+ int ch_mask;
+ int ret;
+ int i;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- w = dai->playback_widget;
- else
- w = dai->capture_widget;
+ if (!w) {
+ dev_err(cpu_dai->dev, "%s widget not found, check amp link num in the topology\n",
+ cpu_dai->name);
+ return -EINVAL;
+ }
- swidget = w->dobj.private;
- component = swidget->scomp;
- sdev = snd_soc_component_get_drvdata(component);
- v = &sdev->fw_ready.version;
+ ops = hda_dai_get_ops(substream, cpu_dai);
+ if (!ops) {
+ dev_err(cpu_dai->dev, "DAI widget ops not set\n");
+ return -EINVAL;
+ }
- /* DAI_CONFIG IPC during hw_params is not supported in older firmware */
- if (v->abi_version < SOF_ABI_VER(3, 18, 0))
+ sdev = widget_to_sdev(w);
+ hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
+
+ /* nothing more to do if the link is already prepared */
+ if (hext_stream && hext_stream->link_prepared)
return 0;
- if (setup)
- return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE);
+ /*
+ * reset the PCMSyCM registers to handle a prepare callback when the PCM is restarted
+ * due to xruns or after a call to snd_pcm_drain/drop()
+ */
+ ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id, cpu_dai->id,
+ 0, 0, substream->stream);
+ if (ret < 0) {
+ dev_err(cpu_dai->dev, "%s: hdac_bus_eml_sdw_map_stream_ch failed %d\n",
+ __func__, ret);
+ return ret;
+ }
- return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE);
-}
+ data.dai_index = (link_id << 8) | cpu_dai->id;
+ data.dai_node_id = intel_alh_id;
+ ret = non_hda_dai_hw_params_data(substream, params, cpu_dai, &data, flags);
+ if (ret < 0) {
+ dev_err(cpu_dai->dev, "%s: non_hda_dai_hw_params failed %d\n", __func__, ret);
+ return ret;
+ }
-static int ssp_dai_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct ssp_dai_dma_data *dma_data;
+ hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
+ if (!hext_stream)
+ return -ENODEV;
+
+ /*
+ * in the case of SoundWire we need to program the PCMSyCM registers. In case
+ * of aggregated devices, we need to define the channel mask for each sublink
+ * by reconstructing the split done in soc-pcm.c
+ */
+ for_each_rtd_cpu_dais(rtd, cpu_dai_id, dai) {
+ if (dai == cpu_dai) {
+ cpu_dai_found = true;
+ break;
+ }
+ }
- dma_data = kzalloc(sizeof(*dma_data), GFP_KERNEL);
- if (!dma_data)
- return -ENOMEM;
+ if (!cpu_dai_found)
+ return -ENODEV;
- snd_soc_dai_set_dma_data(dai, substream, dma_data);
+ ch_mask = GENMASK(params_channels(params) - 1, 0);
+ ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id, cpu_dai->id,
+ ch_mask,
+ hdac_stream(hext_stream)->stream_tag,
+ substream->stream);
+ if (ret < 0) {
+ dev_err(cpu_dai->dev, "%s: hdac_bus_eml_sdw_map_stream_ch failed %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ if (sdev->dspless_mode_selected)
+ return 0;
+
+ ipc4_copier = widget_to_copier(w);
+ dma_config_tlv = &ipc4_copier->dma_config_tlv[cpu_dai_id];
+ dma_config = &dma_config_tlv->dma_config;
+ dma_config->dma_stream_channel_map.mapping[0].device = data.dai_index;
+ dma_config->dma_stream_channel_map.mapping[0].channel_mask = ch_mask;
+
+ /*
+ * copy the dma_config_tlv to all ipc4_copier in the same link. Because only one copier
+ * will be handled in sof_ipc4_prepare_copier_module.
+ */
+ for_each_rtd_cpu_dais(rtd, i, dai) {
+ w = snd_soc_dai_get_widget(dai, substream->stream);
+ if (!w) {
+ dev_err(cpu_dai->dev,
+ "%s widget not found, check amp link num in the topology\n",
+ dai->name);
+ return -EINVAL;
+ }
+ ipc4_copier = widget_to_copier(w);
+ memcpy(&ipc4_copier->dma_config_tlv[cpu_dai_id], dma_config_tlv,
+ sizeof(*dma_config_tlv));
+ }
return 0;
}
+EXPORT_SYMBOL_NS(sdw_hda_dai_hw_params, "SND_SOC_SOF_INTEL_HDA_COMMON");
-static int ssp_dai_setup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai,
- bool setup)
+int sdw_hda_dai_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai,
+ int link_id)
{
- struct ssp_dai_dma_data *dma_data;
- int ret = 0;
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
+ struct snd_sof_dev *sdev;
+ int ret;
- 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;
+ ret = hda_dai_hw_free(substream, cpu_dai);
+ if (ret < 0) {
+ dev_err(cpu_dai->dev, "%s: non_hda_dai_hw_free failed %d\n", __func__, ret);
+ return ret;
}
- if (dma_data->setup != setup) {
- ret = ssp_dai_setup_or_free(substream, dai, setup);
- if (!ret)
- dma_data->setup = setup;
+ sdev = widget_to_sdev(w);
+
+ /* in the case of SoundWire we need to reset the PCMSyCM registers */
+ ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id, cpu_dai->id,
+ 0, 0, substream->stream);
+ if (ret < 0) {
+ dev_err(cpu_dai->dev, "%s: hdac_bus_eml_sdw_map_stream_ch failed %d\n",
+ __func__, ret);
+ return ret;
}
- return ret;
+
+ return 0;
}
+EXPORT_SYMBOL_NS(sdw_hda_dai_hw_free, "SND_SOC_SOF_INTEL_HDA_COMMON");
-static int ssp_dai_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
+int sdw_hda_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *cpu_dai)
{
- /* params are ignored for now */
- return ssp_dai_setup(substream, dai, true);
+ return hda_dai_trigger(substream, cmd, cpu_dai);
}
+EXPORT_SYMBOL_NS(sdw_hda_dai_trigger, "SND_SOC_SOF_INTEL_HDA_COMMON");
-static int ssp_dai_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+static int hda_dai_suspend(struct hdac_bus *bus)
{
- /*
- * the SSP will only be reconfigured during resume operations and
- * not in case of xruns
- */
- return ssp_dai_setup(substream, dai, true);
+ struct snd_soc_pcm_runtime *rtd;
+ struct hdac_ext_stream *hext_stream;
+ struct hdac_stream *s;
+ int ret;
+
+ /* set internal flag for BE */
+ list_for_each_entry(s, &bus->stream_list, list) {
+
+ hext_stream = stream_to_hdac_ext_stream(s);
+
+ /*
+ * clear stream. This should already be taken care for running
+ * streams when the SUSPEND trigger is called. But paused
+ * streams do not get suspended, so this needs to be done
+ * explicitly during suspend.
+ */
+ if (hext_stream->link_substream) {
+ const struct hda_dai_widget_dma_ops *ops;
+ struct snd_sof_widget *swidget;
+ struct snd_soc_dapm_widget *w;
+ struct snd_soc_dai *cpu_dai;
+ struct snd_sof_dev *sdev;
+ struct snd_sof_dai *sdai;
+
+ rtd = snd_soc_substream_to_rtd(hext_stream->link_substream);
+ cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ w = snd_soc_dai_get_widget(cpu_dai, hdac_stream(hext_stream)->direction);
+ swidget = w->dobj.private;
+ sdev = widget_to_sdev(w);
+ sdai = swidget->private;
+ ops = sdai->platform_private;
+
+ if (rtd->dpcm[hext_stream->link_substream->stream].state !=
+ SND_SOC_DPCM_STATE_PAUSED)
+ continue;
+
+ /* 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;
+ }
+
+ ret = hda_link_dma_cleanup(hext_stream->link_substream,
+ hext_stream, cpu_dai, true);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return 0;
}
-static int ssp_dai_trigger(struct snd_pcm_substream *substream,
- int cmd, struct snd_soc_dai *dai)
+static void ssp_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops)
{
- if (cmd != SNDRV_PCM_TRIGGER_SUSPEND)
- return 0;
+ const struct sof_intel_dsp_desc *chip;
+ int i;
- return ssp_dai_setup(substream, dai, false);
+ chip = get_chip_info(sdev->pdata);
+
+ if (chip->hw_ip_version >= SOF_INTEL_ACE_2_0) {
+ for (i = 0; i < ops->num_drv; i++) {
+ if (strstr(ops->drv[i].name, "SSP"))
+ ops->drv[i].ops = &ssp_dai_ops;
+ }
+ }
}
-static int ssp_dai_hw_free(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+static void dmic_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops)
{
- return ssp_dai_setup(substream, dai, false);
+ const struct sof_intel_dsp_desc *chip;
+ int i;
+
+ chip = get_chip_info(sdev->pdata);
+
+ if (chip->hw_ip_version >= SOF_INTEL_ACE_2_0) {
+ for (i = 0; i < ops->num_drv; i++) {
+ if (strstr(ops->drv[i].name, "DMIC"))
+ ops->drv[i].ops = &dmic_dai_ops;
+ }
+ }
}
-static void ssp_dai_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+#else
+
+static inline void ssp_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) {}
+static inline void dmic_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) {}
+
+#endif /* CONFIG_SND_SOC_SOF_HDA_LINK */
+
+void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops)
{
- struct ssp_dai_dma_data *dma_data;
+ int i;
- 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;
+ 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 = &hda_dai_ops;
+#endif
+ }
+
+ ssp_set_dai_drv_ops(sdev, ops);
+ dmic_set_dai_drv_ops(sdev, ops);
+
+ if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4 && !hda_use_tplg_nhlt) {
+ struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+
+ ipc4_data->nhlt = intel_nhlt_init(sdev->dev);
}
- snd_soc_dai_set_dma_data(dai, substream, NULL);
- kfree(dma_data);
}
+EXPORT_SYMBOL_NS(hda_set_dai_drv_ops, "SND_SOC_SOF_INTEL_HDA_COMMON");
-static const struct snd_soc_dai_ops ssp_dai_ops = {
- .startup = ssp_dai_startup,
- .hw_params = ssp_dai_hw_params,
- .prepare = ssp_dai_prepare,
- .trigger = ssp_dai_trigger,
- .hw_free = ssp_dai_hw_free,
- .shutdown = ssp_dai_shutdown,
-};
+void hda_ops_free(struct snd_sof_dev *sdev)
+{
+ if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
+ struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+
+ if (!hda_use_tplg_nhlt)
+ intel_nhlt_free(ipc4_data->nhlt);
+
+ kfree(sdev->private);
+ sdev->private = NULL;
+ }
+}
+EXPORT_SYMBOL_NS(hda_ops_free, "SND_SOC_SOF_INTEL_HDA_COMMON");
/*
* common dai driver for skl+ platforms.
@@ -571,7 +778,6 @@ static const struct snd_soc_dai_ops ssp_dai_ops = {
struct snd_soc_dai_driver skl_dai[] = {
{
.name = "SSP0 Pin",
- .ops = &ssp_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 8,
@@ -583,7 +789,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
.name = "SSP1 Pin",
- .ops = &ssp_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 8,
@@ -595,7 +800,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
.name = "SSP2 Pin",
- .ops = &ssp_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 8,
@@ -607,7 +811,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
.name = "SSP3 Pin",
- .ops = &ssp_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 8,
@@ -619,7 +822,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
.name = "SSP4 Pin",
- .ops = &ssp_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 8,
@@ -631,7 +833,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
.name = "SSP5 Pin",
- .ops = &ssp_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 8,
@@ -655,10 +856,9 @@ struct snd_soc_dai_driver skl_dai[] = {
.channels_max = 4,
},
},
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
{
.name = "iDisp1 Pin",
- .ops = &hda_link_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 8,
@@ -666,7 +866,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
.name = "iDisp2 Pin",
- .ops = &hda_link_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 8,
@@ -674,7 +873,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
.name = "iDisp3 Pin",
- .ops = &hda_link_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 8,
@@ -682,7 +880,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
.name = "iDisp4 Pin",
- .ops = &hda_link_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 8,
@@ -690,7 +887,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
.name = "Analog CPU DAI",
- .ops = &hda_link_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 16,
@@ -702,7 +898,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
.name = "Digital CPU DAI",
- .ops = &hda_link_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 16,
@@ -714,7 +909,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
.name = "Alt Analog CPU DAI",
- .ops = &hda_link_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 16,
@@ -724,20 +918,25 @@ struct snd_soc_dai_driver skl_dai[] = {
.channels_max = 16,
},
},
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
-{
- .name = "Probe Extraction CPU DAI",
- .compress_new = snd_soc_new_compress,
- .cops = &sof_probe_compr_ops,
- .capture = {
- .stream_name = "Probe Extraction",
- .channels_min = 1,
- .channels_max = 8,
- .rates = SNDRV_PCM_RATE_48000,
- .rate_min = 48000,
- .rate_max = 48000,
- },
-},
-#endif
#endif
};
+EXPORT_SYMBOL_NS(skl_dai, "SND_SOC_SOF_INTEL_HDA_COMMON");
+
+int hda_dsp_dais_suspend(struct snd_sof_dev *sdev)
+{
+ /*
+ * In the corner case where a SUSPEND happens during a PAUSE, the ALSA core
+ * does not throw the TRIGGER_SUSPEND. This leaves the DAIs in an unbalanced state.
+ * Since the component suspend is called last, we can trap this corner case
+ * and force the DAIs to release their resources.
+ */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
+ int ret;
+
+ ret = hda_dai_suspend(sof_to_bus(sdev));
+ if (ret < 0)
+ return ret;
+#endif
+
+ return 0;
+}