Unverified Commit 4c30004a authored by Ranjani Sridharan's avatar Ranjani Sridharan Committed by Mark Brown
Browse files
parent d0c0d5bf
Loading
Loading
Loading
Loading
+170 −3
Original line number Diff line number Diff line
@@ -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;