[alsa-devel] [PATCH 0/3] ASoC: Intel: Baytrail: Fix stream restore after aborted suspend
It was found that resuming active stream fails on Baytrail ADSP in case of aborted suspend or when testing only partial suspend. Reason for this is that in those cases ADSP doesn't reach reset even though resume path is expecting so and fails to restart stream.
This revealed that suspend and resume phases do not go together: Operating state is not the same after returning certain resume phase than it was prior entering into corresponding suspend phase. Also we unload firmware in suspend_late before putting ADSP into reset in suspend_noirq when ADSP is actually already powered down.
Fix these by first rearranging and simplifying suspend/resume: Put ADSP into reset in suspend_late and bring it back in resume_late. Now only these will take care of ADSP reset and reboot and SNDRV_PCM_TRIGGER_SUSPEND/SNDRV_PCM_TRIGGER_RESUME callbacks will take care of stream stop/restart during suspend and resume phases.
Then modify stream resume so that full stream reallocation and restart is done only if suspend_late was reached. Otherwise resume it by using the IPC that resumes paused stream.
Jarkko Nikula (3): ASoC: Intel: Merge Baytrail ADSP suspend_noirq into suspend_late ASoC: Intel: Wait Baytrail ADSP boot at resume_early stage ASoC: Intel: Restore Baytrail ADSP streams only when ADSP was in reset
sound/soc/intel/sst-baytrail-ipc.c | 10 +-------- sound/soc/intel/sst-baytrail-ipc.h | 1 - sound/soc/intel/sst-baytrail-pcm.c | 43 +++++++++++++------------------------- 3 files changed, 16 insertions(+), 38 deletions(-)
Merge DSP reset and cleanup sequence in sst_byt_pcm_dev_suspend_noirq() into sst_byt_pcm_dev_suspend_late(). First their order was wrong by first unloading firmware modules in suspend_late and then taking DSP into reset in suspend_noirq. Second ACPI has put device into OFF state already during suspend_late so trying to reset the DSP is a no-op at suspend_noirq stage.
Fix these by moving DSP reset and cleanup into sst_byt_pcm_dev_suspend_late() before firmware unloading.
Signed-off-by: Jarkko Nikula jarkko.nikula@linux.intel.com Tested-by: Borun Fu borun.fu@intel.com --- sound/soc/intel/sst-baytrail-ipc.c | 10 +--------- sound/soc/intel/sst-baytrail-ipc.h | 1 - sound/soc/intel/sst-baytrail-pcm.c | 18 ------------------ 3 files changed, 1 insertion(+), 28 deletions(-)
diff --git a/sound/soc/intel/sst-baytrail-ipc.c b/sound/soc/intel/sst-baytrail-ipc.c index 67673a2c0f41..b4ad98c43e5c 100644 --- a/sound/soc/intel/sst-baytrail-ipc.c +++ b/sound/soc/intel/sst-baytrail-ipc.c @@ -817,7 +817,7 @@ static struct sst_dsp_device byt_dev = { .ops = &sst_byt_ops, };
-int sst_byt_dsp_suspend_noirq(struct device *dev, struct sst_pdata *pdata) +int sst_byt_dsp_suspend_late(struct device *dev, struct sst_pdata *pdata) { struct sst_byt *byt = pdata->dsp;
@@ -826,14 +826,6 @@ int sst_byt_dsp_suspend_noirq(struct device *dev, struct sst_pdata *pdata) sst_byt_drop_all(byt); dev_dbg(byt->dev, "dsp in reset\n");
- return 0; -} -EXPORT_SYMBOL_GPL(sst_byt_dsp_suspend_noirq); - -int sst_byt_dsp_suspend_late(struct device *dev, struct sst_pdata *pdata) -{ - struct sst_byt *byt = pdata->dsp; - dev_dbg(byt->dev, "free all blocks and unload fw\n"); sst_fw_unload(byt->fw);
diff --git a/sound/soc/intel/sst-baytrail-ipc.h b/sound/soc/intel/sst-baytrail-ipc.h index 06a4d202689b..8faff6dcf25d 100644 --- a/sound/soc/intel/sst-baytrail-ipc.h +++ b/sound/soc/intel/sst-baytrail-ipc.h @@ -66,7 +66,6 @@ int sst_byt_get_dsp_position(struct sst_byt *byt, int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata); void sst_byt_dsp_free(struct device *dev, struct sst_pdata *pdata); struct sst_dsp *sst_byt_get_dsp(struct sst_byt *byt); -int sst_byt_dsp_suspend_noirq(struct device *dev, struct sst_pdata *pdata); int sst_byt_dsp_suspend_late(struct device *dev, struct sst_pdata *pdata); int sst_byt_dsp_boot(struct device *dev, struct sst_pdata *pdata); int sst_byt_dsp_wait_for_ready(struct device *dev, struct sst_pdata *pdata); diff --git a/sound/soc/intel/sst-baytrail-pcm.c b/sound/soc/intel/sst-baytrail-pcm.c index 599401c0c655..ba7ed9720732 100644 --- a/sound/soc/intel/sst-baytrail-pcm.c +++ b/sound/soc/intel/sst-baytrail-pcm.c @@ -404,23 +404,6 @@ static const struct snd_soc_component_driver byt_dai_component = { };
#ifdef CONFIG_PM -static int sst_byt_pcm_dev_suspend_noirq(struct device *dev) -{ - struct sst_pdata *sst_pdata = dev_get_platdata(dev); - int ret; - - dev_dbg(dev, "suspending noirq\n"); - - /* at this point all streams will be stopped and context saved */ - ret = sst_byt_dsp_suspend_noirq(dev, sst_pdata); - if (ret < 0) { - dev_err(dev, "failed to suspend %d\n", ret); - return ret; - } - - return ret; -} - static int sst_byt_pcm_dev_suspend_late(struct device *dev) { struct sst_pdata *sst_pdata = dev_get_platdata(dev); @@ -458,7 +441,6 @@ static int sst_byt_pcm_dev_resume(struct device *dev) }
static const struct dev_pm_ops sst_byt_pm_ops = { - .suspend_noirq = sst_byt_pcm_dev_suspend_noirq, .suspend_late = sst_byt_pcm_dev_suspend_late, .resume_early = sst_byt_pcm_dev_resume_early, .resume = sst_byt_pcm_dev_resume,
Remove sst_byt_pcm_dev_resume() and move waiting of firmware boot into sst_byt_pcm_dev_resume_early(). Now suspend_late and resume_early phases are in sync with each other so that we know that ADSP was put into reset and was unpowered after suspend_late and is ready to resume IO after resume_early during resume stage in sst_byt_pcm_trigger().
Signed-off-by: Jarkko Nikula jarkko.nikula@linux.intel.com Tested-by: Borun Fu borun.fu@intel.com --- sound/soc/intel/sst-baytrail-pcm.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-)
diff --git a/sound/soc/intel/sst-baytrail-pcm.c b/sound/soc/intel/sst-baytrail-pcm.c index ba7ed9720732..eb7b31e13565 100644 --- a/sound/soc/intel/sst-baytrail-pcm.c +++ b/sound/soc/intel/sst-baytrail-pcm.c @@ -423,18 +423,14 @@ static int sst_byt_pcm_dev_suspend_late(struct device *dev) static int sst_byt_pcm_dev_resume_early(struct device *dev) { struct sst_pdata *sst_pdata = dev_get_platdata(dev); + int ret;
dev_dbg(dev, "resume early\n");
/* load fw and boot DSP */ - return sst_byt_dsp_boot(dev, sst_pdata); -} - -static int sst_byt_pcm_dev_resume(struct device *dev) -{ - struct sst_pdata *sst_pdata = dev_get_platdata(dev); - - dev_dbg(dev, "resume\n"); + ret = sst_byt_dsp_boot(dev, sst_pdata); + if (ret) + return ret;
/* wait for FW to finish booting */ return sst_byt_dsp_wait_for_ready(dev, sst_pdata); @@ -443,7 +439,6 @@ static int sst_byt_pcm_dev_resume(struct device *dev) static const struct dev_pm_ops sst_byt_pm_ops = { .suspend_late = sst_byt_pcm_dev_suspend_late, .resume_early = sst_byt_pcm_dev_resume_early, - .resume = sst_byt_pcm_dev_resume, };
#define SST_BYT_PM_OPS (&sst_byt_pm_ops)
There is no need to restore and restart PCM streams in case ADSP didn't reach reset and power off state during system suspend/resume cycle. In that case stream is still active but paused and firmware doesn't allow allocating a new stream before paused stream is freed.
ADSP remains active in case suspend sequence didn't go to suspend_late stage. This can happen when either suspend sequence is aborted by a wakeup or by letting only devices suspend by "echo devices >/sys/power/pm_test".
Currently stream restoring fails in these suspend cases. Fix this by adding a flag that indicates is complete stream reinitialization needed or is it enough to resume paused stream. Flag is set when we know that ADSP reached suspend_late.
Initial fix to this issue came from Fang Yang. I modified it a little and forward ported it to top of two other suspend/resume patches from me.
Signed-off-by: Jarkko Nikula jarkko.nikula@linux.intel.com Tested-by: Borun Fu borun.fu@intel.com Cc: yang fang yang.a.fang@intel.com --- sound/soc/intel/sst-baytrail-pcm.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/sound/soc/intel/sst-baytrail-pcm.c b/sound/soc/intel/sst-baytrail-pcm.c index eb7b31e13565..eab1c7d85187 100644 --- a/sound/soc/intel/sst-baytrail-pcm.c +++ b/sound/soc/intel/sst-baytrail-pcm.c @@ -59,6 +59,9 @@ struct sst_byt_priv_data {
/* DAI data */ struct sst_byt_pcm_data pcm[BYT_PCM_COUNT]; + + /* flag indicating is stream context restore needed after suspend */ + bool restore_stream; };
/* this may get called several times by oss emulation */ @@ -184,7 +187,10 @@ static int sst_byt_pcm_trigger(struct snd_pcm_substream *substream, int cmd) sst_byt_stream_start(byt, pcm_data->stream, 0); break; case SNDRV_PCM_TRIGGER_RESUME: - schedule_work(&pcm_data->work); + if (pdata->restore_stream == true) + schedule_work(&pcm_data->work); + else + sst_byt_stream_resume(byt, pcm_data->stream); break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: sst_byt_stream_resume(byt, pcm_data->stream); @@ -193,6 +199,7 @@ static int sst_byt_pcm_trigger(struct snd_pcm_substream *substream, int cmd) sst_byt_stream_stop(byt, pcm_data->stream); break; case SNDRV_PCM_TRIGGER_SUSPEND: + pdata->restore_stream = false; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: sst_byt_stream_pause(byt, pcm_data->stream); break; @@ -407,6 +414,7 @@ static const struct snd_soc_component_driver byt_dai_component = { static int sst_byt_pcm_dev_suspend_late(struct device *dev) { struct sst_pdata *sst_pdata = dev_get_platdata(dev); + struct sst_byt_priv_data *priv_data = dev_get_drvdata(dev); int ret;
dev_dbg(dev, "suspending late\n"); @@ -417,6 +425,8 @@ static int sst_byt_pcm_dev_suspend_late(struct device *dev) return ret; }
+ priv_data->restore_stream = true; + return ret; }
On Mon, Aug 11, 2014 at 02:15:35PM +0300, Jarkko Nikula wrote:
It was found that resuming active stream fails on Baytrail ADSP in case of aborted suspend or when testing only partial suspend. Reason for this is that in those cases ADSP doesn't reach reset even though resume path is expecting so and fails to restart stream.
Applied all, thanks. It'd have been helpful if the commit messages for the second and theard patches had made it clearer that these were bug fixes.
participants (2)
-
Jarkko Nikula
-
Mark Brown