@@ -5,7 +5,7 @@ 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 hda-mlink.o \
skl.o hda-loader-skl.o \
apl.o cnl.o tgl.o icl.o mtl.o hda-common-ops.o
new file mode 100644
@@ -0,0 +1,183 @@
+// 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 const struct hda_dai_widget_dma_ops hda_dma_ops = {
+ .get_hext_stream = hda_get_hext_stream,
+ .assign_hext_stream = hda_assign_hext_stream,
+ .release_hext_stream = hda_release_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 = 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_dma_ops;
+ break;
+ }
+ case SOF_INTEL_IPC4:
+ {
+ struct sof_ipc4_copier *ipc4_copier = sdai->private;
+
+ if (ipc4_copier->dai_type == SOF_DAI_INTEL_HDA)
+ return &hda_dma_ops;
+ break;
+ }
+ default:
+ break;
+ }
+#endif
+ return NULL;
+}
@@ -29,101 +29,30 @@ MODULE_PARM_DESC(sof_use_tplg_nhlt, "SOF topology nhlt override");
#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)
+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_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;
+ 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 = swidget->private;
- if (!bus->ppcap) {
- dev_err(bus->dev, "stream type not supported\n");
- return NULL;
- }
+ /* 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);
- 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 (!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,
@@ -131,6 +60,8 @@ static int hda_link_dma_cleanup(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai,
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;
@@ -145,8 +76,10 @@ static int hda_link_dma_cleanup(struct snd_pcm_substream *substream,
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 */
@@ -159,6 +92,7 @@ static int hda_link_dma_cleanup(struct snd_pcm_substream *substream,
static int hda_link_dma_hw_params(struct snd_pcm_substream *substream,
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 *codec_dai = asoc_rtd_to_codec(rtd, 0);
struct hdac_ext_stream *hext_stream;
@@ -168,7 +102,7 @@ static int hda_link_dma_hw_params(struct snd_pcm_substream *substream,
struct hdac_bus *bus;
unsigned int format_val;
unsigned int link_bps;
- unsigned char stream_tag;
+ int stream_tag;
sdev = snd_soc_component_get_drvdata(cpu_dai->component);
bus = sof_to_bus(sdev);
@@ -177,15 +111,16 @@ 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;
@@ -226,11 +161,15 @@ static int hda_link_dma_prepare(struct snd_pcm_substream *substream, struct snd_
static int hda_link_dma_trigger(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai,
int cmd)
{
+ 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_sof_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component);
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);
+ struct hdac_ext_stream *hext_stream;
int ret;
+ hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
+
if (!hext_stream)
return 0;
@@ -259,11 +198,18 @@ static int hda_link_dma_trigger(struct snd_pcm_substream *substream, struct snd_
static int hda_link_dma_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;
- hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
+ if (!ops) {
+ dev_err(sdev->dev, "DAI widget ops not set\n");
+ return -EINVAL;
+ }
+
+ hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
if (!hext_stream)
return 0;
@@ -288,18 +234,18 @@ static int hda_dai_hw_params_update(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
+ 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 snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
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);
+ hext_stream = ops->get_hext_stream(sdev, dai, substream);
if (!hext_stream)
return -EINVAL;
stream_tag = hdac_stream(hext_stream)->stream_tag;
- w = snd_soc_dai_get_widget(dai, substream->stream);
-
/* set up the DAI widget and send the DAI_CONFIG with the new tag */
return hda_dai_widget_update(w, stream_tag - 1, true);
}
@@ -308,10 +254,17 @@ 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_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;
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;
@@ -343,13 +296,14 @@ static int hda_dai_config_pause_push_ipc(struct snd_soc_dapm_widget *w)
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_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 snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct hdac_ext_stream *hext_stream;
int stream = substream->stream;
int ret;
+ hext_stream = ops->get_hext_stream(sdev, dai, substream);
if (hext_stream && hext_stream->link_prepared)
return 0;
@@ -418,27 +372,27 @@ static int ipc3_hda_dai_trigger(struct snd_pcm_substream *substream,
static int ipc4_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_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_sof_widget *pipe_widget;
- struct sof_ipc4_pipeline *pipeline;
+ const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai);
+ struct snd_sof_widget *swidget = w->dobj.private;
+ struct hdac_ext_stream *hext_stream;
+ struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
+ struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
struct snd_soc_pcm_runtime *rtd;
- struct snd_sof_widget *swidget;
- struct snd_soc_dapm_widget *w;
struct snd_soc_dai *codec_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);
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:
@@ -959,4 +959,8 @@ struct hda_dai_widget_dma_ops {
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);
+
#endif