Add BE DAI drv ops for IPC4 for DMIC, SSP and HDA type DAI's.
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Bard Liao yung-chuan.liao@linux.intel.com --- sound/soc/sof/intel/hda-dai.c | 173 +++++++++++++++++++++++++++++++++- 1 file changed, 170 insertions(+), 3 deletions(-)
diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 9823230d2ef4..5423667002e5 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -10,6 +10,10 @@
#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" @@ -369,8 +373,7 @@ static int hda_dai_config_pause_push_ipc(struct snd_soc_dapm_widget *w) return ret; }
-static int ipc3_hda_dai_prepare(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) +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); @@ -438,6 +441,91 @@ static int ipc3_hda_dai_trigger(struct snd_pcm_substream *substream, return 0; }
+/* + * 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) +{ + 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_soc_pcm_runtime *rtd; + struct snd_sof_widget *swidget; + struct snd_soc_dapm_widget *w; + struct snd_soc_dai *codec_dai; + struct hdac_stream *hstream; + struct snd_soc_dai *cpu_dai; + int ret; + + dev_dbg(dai->dev, "%s: cmd=%d dai %s direction %d\n", __func__, cmd, + dai->name, substream->stream); + + hstream = substream->runtime->private_data; + 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; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + snd_hdac_ext_link_stream_start(hext_stream); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + { + struct snd_sof_widget *pipe_widget = swidget->pipe_widget; + struct sof_ipc4_pipeline *pipeline = pipe_widget->private; + + ret = sof_ipc4_set_pipeline_state(sdev, swidget->pipeline_id, + SOF_IPC4_PIPE_PAUSED); + if (ret < 0) + return ret; + + pipeline->state = SOF_IPC4_PIPE_PAUSED; + + snd_hdac_ext_link_stream_clear(hext_stream); + + ret = sof_ipc4_set_pipeline_state(sdev, swidget->pipeline_id, + SOF_IPC4_PIPE_RESET); + if (ret < 0) + return ret; + + pipeline->state = SOF_IPC4_PIPE_RESET; + + ret = hda_link_dma_cleanup(substream, hstream, cpu_dai, codec_dai, false); + 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: + { + struct snd_sof_widget *pipe_widget = swidget->pipe_widget; + struct sof_ipc4_pipeline *pipeline = pipe_widget->private; + + ret = sof_ipc4_set_pipeline_state(sdev, swidget->pipeline_id, + SOF_IPC4_PIPE_PAUSED); + if (ret < 0) + return ret; + + pipeline->state = SOF_IPC4_PIPE_PAUSED; + + snd_hdac_ext_link_stream_clear(hext_stream); + break; + } + default: + dev_err(sdev->dev, "%s: unknown trigger command %d\n", __func__, cmd); + return -EINVAL; + } + + return 0; +} + static int hda_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -454,7 +542,7 @@ static const struct snd_soc_dai_ops ipc3_hda_dai_ops = { .hw_params = hda_dai_hw_params, .hw_free = hda_dai_hw_free, .trigger = ipc3_hda_dai_trigger, - .prepare = ipc3_hda_dai_prepare, + .prepare = hda_dai_prepare, };
static int hda_dai_suspend(struct hdac_bus *bus) @@ -497,6 +585,14 @@ static int hda_dai_suspend(struct hdac_bus *bus)
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 */ @@ -608,6 +704,59 @@ static const struct snd_soc_dai_ops ipc3_ssp_dai_ops = { .shutdown = ssp_dai_shutdown, };
+static int ipc4_be_dai_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct snd_sof_widget *pipe_widget; + struct sof_ipc4_pipeline *pipeline; + struct snd_sof_widget *swidget; + struct snd_soc_dapm_widget *w; + struct snd_sof_dev *sdev; + int ret; + + w = snd_soc_dai_get_widget(dai, substream->stream); + swidget = w->dobj.private; + pipe_widget = swidget->pipe_widget; + pipeline = pipe_widget->private; + sdev = snd_soc_component_get_drvdata(swidget->scomp); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + ret = sof_ipc4_set_pipeline_state(sdev, swidget->pipeline_id, + SOF_IPC4_PIPE_PAUSED); + if (ret < 0) + return ret; + pipeline->state = SOF_IPC4_PIPE_PAUSED; + + ret = sof_ipc4_set_pipeline_state(sdev, swidget->pipeline_id, + SOF_IPC4_PIPE_RESET); + if (ret < 0) + return ret; + pipeline->state = SOF_IPC4_PIPE_RESET; + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + ret = sof_ipc4_set_pipeline_state(sdev, swidget->pipeline_id, + SOF_IPC4_PIPE_PAUSED); + if (ret < 0) + return ret; + pipeline->state = SOF_IPC4_PIPE_PAUSED; + break; + default: + break; + } + + return 0; +} + +static const struct snd_soc_dai_ops ipc4_dmic_dai_ops = { + .trigger = ipc4_be_dai_trigger, +}; + +static const struct snd_soc_dai_ops ipc4_ssp_dai_ops = { + .trigger = ipc4_be_dai_trigger, +}; + void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) { int i; @@ -624,6 +773,24 @@ void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) 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: + for (i = 0; i < ops->num_drv; i++) { + if (strstr(ops->drv[i].name, "DMIC")) { + ops->drv[i].ops = &ipc4_dmic_dai_ops; + continue; + } + if (strstr(ops->drv[i].name, "SSP")) { + ops->drv[i].ops = &ipc4_ssp_dai_ops; + continue; + } +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) + 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; #endif } break;