Commit 934bf822 authored by Rander Wang's avatar Rander Wang Committed by Mark Brown

ASoC: SOF: Intel: hda: fix link DMA config

For this bug, there are two capture pcm streams active, with one
stream and its related stream tag released before suspend. Later
when system suspend is done, the stream tag for the remaining
active stream is released by SOF driver. After system resume, hda
codec driver restores the stream tag for the active pcm stream,
but SOF goes to assign a new one, which now doesn't match with the
stream tag used by codec driver, and this causes DMA to fail
receiving data, leading to unrecoverable XRUN condition in FW.

For stream tag is stored in both hda codec and SOF driver, it
shouldn't be released only in SOF driver. This patch just keeps the
stream information in dma data and checks whether there is a stored
DMA data for stream resuming from S3 and restores it. And it also
removes DMA data when the stream is released.

Tested on Whiskey Lake platform.

GitHub issue: https://github.com/thesofproject/sof/issues/1594Signed-off-by: default avatarRander Wang <rander.wang@linux.intel.com>
Signed-off-by: default avatarPierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Link: https://lore.kernel.org/r/20190722141402.7194-19-pierre-louis.bossart@linux.intel.comSigned-off-by: default avatarMark Brown <broonie@kernel.org>
parent 7ccafa2b
...@@ -210,9 +210,13 @@ static int hda_link_hw_params(struct snd_pcm_substream *substream, ...@@ -210,9 +210,13 @@ static int hda_link_hw_params(struct snd_pcm_substream *substream,
int stream_tag; int stream_tag;
int ret; int ret;
link_dev = hda_link_stream_assign(bus, substream); /* get stored dma data if resuming from system suspend */
if (!link_dev) link_dev = snd_soc_dai_get_dma_data(dai, substream);
return -EBUSY; if (!link_dev) {
link_dev = hda_link_stream_assign(bus, substream);
if (!link_dev)
return -EBUSY;
}
stream_tag = hdac_stream(link_dev)->stream_tag; stream_tag = hdac_stream(link_dev)->stream_tag;
...@@ -316,7 +320,7 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, ...@@ -316,7 +320,7 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_STOP:
/* /*
* clear and release link DMA channel. It will be assigned when * clear link DMA channel. It will be assigned when
* hw_params is set up again after resume. * hw_params is set up again after resume.
*/ */
ret = hda_link_config_ipc(hda_stream, dai->name, ret = hda_link_config_ipc(hda_stream, dai->name,
...@@ -325,8 +329,6 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, ...@@ -325,8 +329,6 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
return ret; return ret;
stream_tag = hdac_stream(link_dev)->stream_tag; stream_tag = hdac_stream(link_dev)->stream_tag;
snd_hdac_ext_link_clear_stream_id(link, stream_tag); snd_hdac_ext_link_clear_stream_id(link, stream_tag);
snd_hdac_ext_stream_release(link_dev,
HDAC_EXT_STREAM_TYPE_LINK);
link_dev->link_prepared = 0; link_dev->link_prepared = 0;
/* fallthrough */ /* fallthrough */
...@@ -369,6 +371,7 @@ static int hda_link_hw_free(struct snd_pcm_substream *substream, ...@@ -369,6 +371,7 @@ static int hda_link_hw_free(struct snd_pcm_substream *substream,
stream_tag = hdac_stream(link_dev)->stream_tag; stream_tag = hdac_stream(link_dev)->stream_tag;
snd_hdac_ext_link_clear_stream_id(link, stream_tag); snd_hdac_ext_link_clear_stream_id(link, stream_tag);
snd_soc_dai_set_dma_data(dai, substream, NULL);
snd_hdac_ext_stream_release(link_dev, HDAC_EXT_STREAM_TYPE_LINK); snd_hdac_ext_stream_release(link_dev, HDAC_EXT_STREAM_TYPE_LINK);
link_dev->link_prepared = 0; link_dev->link_prepared = 0;
......
...@@ -439,10 +439,10 @@ int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev) ...@@ -439,10 +439,10 @@ int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev)
stream = stream_to_hdac_ext_stream(s); stream = stream_to_hdac_ext_stream(s);
/* /*
* clear and release stream. This should already be taken care * clear stream. This should already be taken care for running
* for running streams when the SUSPEND trigger is called. * streams when the SUSPEND trigger is called. But paused
* But paused streams do not get suspended, so this needs to be * streams do not get suspended, so this needs to be done
* done explicitly during suspend. * explicitly during suspend.
*/ */
if (stream->link_substream) { if (stream->link_substream) {
rtd = snd_pcm_substream_chip(stream->link_substream); rtd = snd_pcm_substream_chip(stream->link_substream);
...@@ -452,8 +452,6 @@ int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev) ...@@ -452,8 +452,6 @@ int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev)
return -EINVAL; return -EINVAL;
stream_tag = hdac_stream(stream)->stream_tag; stream_tag = hdac_stream(stream)->stream_tag;
snd_hdac_ext_link_clear_stream_id(link, stream_tag); snd_hdac_ext_link_clear_stream_id(link, stream_tag);
snd_hdac_ext_stream_release(stream,
HDAC_EXT_STREAM_TYPE_LINK);
stream->link_prepared = 0; stream->link_prepared = 0;
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment