Unverified Commit 83f48ad5 authored by Mark Brown's avatar Mark Brown
Browse files

ASoC: SOF: Intel: improve HDaudio DAI support

Merge series from Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>:

The SOF CI and daily tests exposed a number of issues with corner
cases on platforms using the HDaudio DAI, such as UpExtreme boards or
usual HDaudio+DMIC laptops.

This patchset provides improvements for pause_push/pause_release,
suspend-resume, mixing use cases and combinations of all three.

The initial patches provide a cleanup, the last patches improve the
state machine and DMA handling.
parents 0386dd91 c4eb48f7
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -43,6 +43,9 @@ int sof_apl_ops_init(struct snd_sof_dev *sdev)
	/* ipc */
	sof_apl_ops.send_msg	= hda_dsp_ipc_send_msg;

	/* set DAI driver ops */
	hda_set_dai_drv_ops(sdev, &sof_apl_ops);

	/* debug */
	sof_apl_ops.debug_map	= apl_dsp_debugfs;
	sof_apl_ops.debug_map_count	= ARRAY_SIZE(apl_dsp_debugfs);
+3 −0
Original line number Diff line number Diff line
@@ -261,6 +261,9 @@ int sof_cnl_ops_init(struct snd_sof_dev *sdev)
	/* ipc */
	sof_cnl_ops.send_msg	= cnl_ipc_send_msg;

	/* set DAI driver ops */
	hda_set_dai_drv_ops(sdev, &sof_cnl_ops);

	/* debug */
	sof_cnl_ops.debug_map	= cnl_dsp_debugfs;
	sof_cnl_ops.debug_map_count	= ARRAY_SIZE(cnl_dsp_debugfs);
+278 −156
Original line number Diff line number Diff line
@@ -128,6 +128,40 @@ static struct hdac_ext_stream *
	return res;
}

static int hda_link_dma_cleanup(struct snd_pcm_substream *substream,
				struct hdac_stream *hstream,
				struct snd_soc_dai *cpu_dai,
				struct snd_soc_dai *codec_dai,
				bool trigger_suspend_stop)
{
	struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
	struct hdac_bus *bus = hstream->bus;
	struct sof_intel_hda_stream *hda_stream;
	struct hdac_ext_link *link;
	int stream_tag;

	link = snd_hdac_ext_bus_get_link(bus, codec_dai->component->name);
	if (!link)
		return -EINVAL;

	if (trigger_suspend_stop)
		snd_hdac_ext_link_stream_clear(hext_stream);

	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
		stream_tag = hdac_stream(hext_stream)->stream_tag;
		snd_hdac_ext_link_clear_stream_id(link, stream_tag);
	}
	snd_soc_dai_set_dma_data(cpu_dai, substream, NULL);
	snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK);
	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 0;
}

static int hda_link_dma_params(struct hdac_ext_stream *hext_stream,
			       struct hda_pipe_params *params)
{
@@ -162,61 +196,28 @@ static int hda_link_dma_params(struct hdac_ext_stream *hext_stream,
	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)
{
	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);

	return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE, &data);
}

static int hda_link_hw_params(struct snd_pcm_substream *substream,
			      struct snd_pcm_hw_params *params,
			      struct snd_soc_dai *dai)
static int hda_link_dma_hw_params(struct snd_pcm_substream *substream,
				  struct snd_pcm_hw_params *params)
{
	struct hdac_stream *hstream = substream->runtime->private_data;
	struct hdac_bus *bus = hstream->bus;
	struct hdac_ext_stream *hext_stream;
	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 sof_intel_hda_stream *hda_stream;
	struct hda_pipe_params p_params = {0};
	struct snd_soc_dapm_widget *w;
	struct hdac_bus *bus = hstream->bus;
	struct hdac_ext_link *link;
	int stream_tag;
	int ret;

	/* get stored dma data if resuming from system suspend */
	hext_stream = snd_soc_dai_get_dma_data(dai, substream);
	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;

		snd_soc_dai_set_dma_data(dai, substream, (void *)hext_stream);
		snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)hext_stream);
	}

	stream_tag = hdac_stream(hext_stream)->stream_tag;

	hda_stream = hstream_to_sof_hda_stream(hext_stream);

	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
		w = dai->playback_widget;
	else
		w = dai->capture_widget;

	/* 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;

	link = snd_hdac_ext_bus_get_link(bus, codec_dai->component->name);
	if (!link)
		return -EINVAL;
@@ -239,26 +240,118 @@ static int hda_link_hw_params(struct snd_pcm_substream *substream,
	return hda_link_dma_params(hext_stream, &p_params);
}

static int hda_link_pcm_prepare(struct snd_pcm_substream *substream,
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;

	return hda_link_dma_hw_params(substream, &rtd->dpcm[stream].hw_params);
}

static int hda_link_dma_trigger(struct snd_pcm_substream *substream, int cmd)
{
	struct hdac_stream *hstream = substream->runtime->private_data;
	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;

	dev_dbg(cpu_dai->dev, "%s: cmd=%d\n", __func__, cmd);
	if (!hext_stream)
		return 0;

	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:
		ret = hda_link_dma_cleanup(substream, hstream, cpu_dai, codec_dai, true);
		if (ret < 0)
			return ret;

		break;
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
		snd_hdac_ext_link_stream_clear(hext_stream);

		break;
	default:
		return -EINVAL;
	}
	return 0;
}

static int hda_link_dma_hw_free(struct snd_pcm_substream *substream)
{
	struct hdac_stream *hstream = substream->runtime->private_data;
	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, hstream, 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);

	return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE, &data);
}

static int hda_dai_hw_params_update(struct snd_pcm_substream *substream,
				    struct snd_pcm_hw_params *params,
				    struct snd_soc_dai *dai)
{
	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)
		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);
}

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);
	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
	int stream = substream->stream;
	int ret;

	if (hext_stream->link_prepared)
	if (hext_stream && hext_stream->link_prepared)
		return 0;

	dev_dbg(sdev->dev, "hda: prepare stream dir %d\n", substream->stream);
	ret = hda_link_dma_hw_params(substream, params);
	if (ret < 0)
		return ret;

	return hda_link_hw_params(substream, &rtd->dpcm[stream].hw_params,
				  dai);
	return hda_dai_hw_params_update(substream, params, dai);
}

static int hda_link_dai_config_pause_push_ipc(struct snd_soc_dapm_widget *w)

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;
@@ -276,132 +369,134 @@ static int hda_link_dai_config_pause_push_ipc(struct snd_soc_dapm_widget *w)
	return ret;
}

static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
				int cmd, struct snd_soc_dai *dai)
static int ipc3_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 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;
	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;
	int ret;

	hstream = substream->runtime->private_data;
	bus = hstream->bus;
	rtd = asoc_substream_to_rtd(substream);
	if (hext_stream && hext_stream->link_prepared)
		return 0;

	link = snd_hdac_ext_bus_get_link(bus, asoc_rtd_to_codec(rtd, 0)->component->name);
	if (!link)
		return -EINVAL;
	dev_dbg(sdev->dev, "%s: prepare stream dir %d\n", __func__, substream->stream);

	hda_stream = hstream_to_sof_hda_stream(hext_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;

	dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd);
	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;

	ret = hda_link_dma_trigger(substream, cmd);
	if (ret < 0)
		return ret;

	w = snd_soc_dai_get_widget(dai, substream->stream);

	dev_dbg(dai->dev, "%s: cmd=%d\n", __func__, cmd);
	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:
		snd_hdac_ext_link_stream_clear(hext_stream);

		/*
		 * 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);
		ret = hda_dai_hw_free_ipc(substream->stream, dai);
		if (ret < 0)
			return ret;

		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
			stream_tag = hdac_stream(hext_stream)->stream_tag;
			snd_hdac_ext_link_clear_stream_id(link, stream_tag);
		}

		hext_stream->link_prepared = 0;
		break;
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
		snd_hdac_ext_link_stream_clear(hext_stream);

		ret = hda_link_dai_config_pause_push_ipc(w);
		ret = hda_dai_config_pause_push_ipc(w);
		if (ret < 0)
			return ret;
		break;

	default:
		return -EINVAL;
		break;
	}
	return 0;
}

static int hda_link_hw_free(struct snd_pcm_substream *substream,
static int hda_dai_hw_free(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 hdac_stream *hstream;
	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 = {
	.hw_params = hda_dai_hw_params,
	.hw_free = hda_dai_hw_free,
	.trigger = ipc3_hda_dai_trigger,
	.prepare = ipc3_hda_dai_prepare,
};

static int hda_dai_suspend(struct hdac_bus *bus)
{
	struct snd_soc_pcm_runtime *rtd;
	struct hdac_ext_stream *hext_stream;
	struct snd_soc_dapm_widget *w;
	struct hdac_stream *s;
	int ret;

	hstream = substream->runtime->private_data;
	bus = hstream->bus;
	rtd = asoc_substream_to_rtd(substream);
	hext_stream = snd_soc_dai_get_dma_data(dai, substream);
	/* set internal flag for BE */
	list_for_each_entry(s, &bus->stream_list, list) {

	if (!hext_stream) {
		dev_dbg(dai->dev,
			"%s: hext_stream is not assigned\n", __func__);
		return -EINVAL;
	}
		hext_stream = stream_to_hdac_ext_stream(s);

	hda_stream = hstream_to_sof_hda_stream(hext_stream);
		/*
		 * 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) {
			struct snd_soc_dai *cpu_dai;
			struct snd_soc_dai *codec_dai;

	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
		w = dai->playback_widget;
	else
		w = dai->capture_widget;
			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);

	/* 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);
			ret = hda_link_dma_cleanup(hext_stream->link_substream, s,
						   cpu_dai, codec_dai, false);
			if (ret < 0)
				return ret;

	link = snd_hdac_ext_bus_get_link(bus, asoc_rtd_to_codec(rtd, 0)->component->name);
	if (!link)
		return -EINVAL;

	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
		stream_tag = hdac_stream(hext_stream)->stream_tag;
		snd_hdac_ext_link_clear_stream_id(link, stream_tag);
			/* 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;
		}
	}

	snd_soc_dai_set_dma_data(dai, substream, NULL);
	snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK);
	hext_stream->link_prepared = 0;

	/* free the host DMA channel reserved by hostless streams */
	hda_stream->host_reserved = 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,
};

#endif

/* only one flag used so far to harden hw_params/hw_free/trigger/prepare */
@@ -414,10 +509,7 @@ static int ssp_dai_setup_or_free(struct snd_pcm_substream *substream, struct snd
{
	struct snd_soc_dapm_widget *w;

	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
		w = dai->playback_widget;
	else
		w = dai->capture_widget;
	w = snd_soc_dai_get_widget(dai, substream->stream);

	if (setup)
		return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE, NULL);
@@ -478,7 +570,7 @@ static int ssp_dai_prepare(struct snd_pcm_substream *substream,
	return ssp_dai_setup(substream, dai, true);
}

static int ssp_dai_trigger(struct snd_pcm_substream *substream,
static int ipc3_ssp_dai_trigger(struct snd_pcm_substream *substream,
				int cmd, struct snd_soc_dai *dai)
{
	if (cmd != SNDRV_PCM_TRIGGER_SUSPEND)
@@ -507,15 +599,39 @@ static void ssp_dai_shutdown(struct snd_pcm_substream *substream,
	kfree(dma_data);
}

static const struct snd_soc_dai_ops ssp_dai_ops = {
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 = ssp_dai_trigger,
	.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)
			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;
	default:
		break;
	}
}

/*
 * common dai driver for skl+ platforms.
 * some products who use this DAI array only physically have a subset of
@@ -524,7 +640,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,
@@ -536,7 +651,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
	.name = "SSP1 Pin",
	.ops = &ssp_dai_ops,
	.playback = {
		.channels_min = 1,
		.channels_max = 8,
@@ -548,7 +662,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
	.name = "SSP2 Pin",
	.ops = &ssp_dai_ops,
	.playback = {
		.channels_min = 1,
		.channels_max = 8,
@@ -560,7 +673,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
	.name = "SSP3 Pin",
	.ops = &ssp_dai_ops,
	.playback = {
		.channels_min = 1,
		.channels_max = 8,
@@ -572,7 +684,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
	.name = "SSP4 Pin",
	.ops = &ssp_dai_ops,
	.playback = {
		.channels_min = 1,
		.channels_max = 8,
@@ -584,7 +695,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
	.name = "SSP5 Pin",
	.ops = &ssp_dai_ops,
	.playback = {
		.channels_min = 1,
		.channels_max = 8,
@@ -611,7 +721,6 @@ struct snd_soc_dai_driver skl_dai[] = {
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
{
	.name = "iDisp1 Pin",
	.ops = &hda_link_dai_ops,
	.playback = {
		.channels_min = 1,
		.channels_max = 8,
@@ -619,7 +728,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
	.name = "iDisp2 Pin",
	.ops = &hda_link_dai_ops,
	.playback = {
		.channels_min = 1,
		.channels_max = 8,
@@ -627,7 +735,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
	.name = "iDisp3 Pin",
	.ops = &hda_link_dai_ops,
	.playback = {
		.channels_min = 1,
		.channels_max = 8,
@@ -635,7 +742,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
	.name = "iDisp4 Pin",
	.ops = &hda_link_dai_ops,
	.playback = {
		.channels_min = 1,
		.channels_max = 8,
@@ -643,7 +749,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
	.name = "Analog CPU DAI",
	.ops = &hda_link_dai_ops,
	.playback = {
		.channels_min = 1,
		.channels_max = 16,
@@ -655,7 +760,6 @@ struct snd_soc_dai_driver skl_dai[] = {
},
{
	.name = "Digital CPU DAI",
	.ops = &hda_link_dai_ops,
	.playback = {
		.channels_min = 1,
		.channels_max = 16,
@@ -667,7 +771,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,
@@ -679,3 +782,22 @@ struct snd_soc_dai_driver skl_dai[] = {
},
#endif
};

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)
	int ret;

	ret = hda_dai_suspend(sof_to_bus(sdev));
	if (ret < 0)
		return ret;
#endif

	return 0;
}
+6 −36
Original line number Diff line number Diff line
@@ -894,44 +894,14 @@ int hda_dsp_shutdown(struct snd_sof_dev *sdev)

int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev)
{
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
	struct hdac_bus *bus = sof_to_bus(sdev);
	struct snd_soc_pcm_runtime *rtd;
	struct hdac_ext_stream *hext_stream;
	struct hdac_ext_link *link;
	struct hdac_stream *s;
	const char *name;
	int stream_tag;

	/* 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) {
			rtd = asoc_substream_to_rtd(hext_stream->link_substream);
			name = asoc_rtd_to_codec(rtd, 0)->component->name;
			link = snd_hdac_ext_bus_get_link(bus, name);
			if (!link)
				return -EINVAL;

			hext_stream->link_prepared = 0;
	int ret;

			if (hdac_stream(hext_stream)->direction ==
				SNDRV_PCM_STREAM_CAPTURE)
				continue;
	/* make sure all DAI resources are freed */
	ret = hda_dsp_dais_suspend(sdev);
	if (ret < 0)
		dev_warn(sdev->dev, "%s: failure in hda_dsp_dais_suspend\n", __func__);

			stream_tag = hdac_stream(hext_stream)->stream_tag;
			snd_hdac_ext_link_clear_stream_id(link, stream_tag);
		}
	}
#endif
	return 0;
	return ret;
}

void hda_dsp_d0i3_work(struct work_struct *work)
+3 −0
Original line number Diff line number Diff line
@@ -697,6 +697,7 @@ static inline bool hda_common_check_sdw_irq(struct snd_sof_dev *sdev)

/* common dai driver */
extern struct snd_soc_dai_driver skl_dai[];
int hda_dsp_dais_suspend(struct snd_sof_dev *sdev);

/*
 * Platform Specific HW abstraction Ops.
@@ -761,4 +762,6 @@ int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_f

extern int sof_hda_position_quirk;

void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops);

#endif
Loading