[alsa-devel] [PATCH 0/7] ASoC: Intel: Baytrail SST suspend/resume support
This set adds suspend/resume support to Baytrail SST. This is implemented by putting the DSP into reset and by releasing driver resources before suspend. In resume path the DSP is rebooted in normal boot state. Active streams are restored by restarting them from last known buffer position before suspend.
Patches 1-5 are preparing for the suspend/resume and patch 6 implements the core logic that takes care of stopping the DSP before suspend and restarting it again after resume. Patch 7 removes ignore_suspend flag from byt-rt5640 machine driver since it doesn't have use at the moment and adds suspend/resume callbacks that are hooked to ASoC core. This takes care that active stream will be paused and resumed over suspend/resume cycle on byt-rt5640 machine.
Jarkko Nikula (5): ASoC: Intel: Sample Baytrail DSP DMA pointer only after each period ASoC: Intel: Simplify Baytrail stream control IPC construction ASoC: Intel: Pass stream start position to sst_byt_stream_start() ASoC: Intel: Move Baytrail extended fw address saving to sst_byt_boot() ASoC: Intel: Allow byt-5640 machine driver and SST core go to suspend
Liam Girdwood (2): ASoC: Intel: Allow Rx/Tx message list can be cleared prior to suspend ASoC: Intel: Add Baytrail suspend/resume support
sound/soc/intel/byt-rt5640.c | 14 +++- sound/soc/intel/sst-baytrail-dsp.c | 14 ++-- sound/soc/intel/sst-baytrail-ipc.c | 124 ++++++++++++++++++++++++++++------ sound/soc/intel/sst-baytrail-ipc.h | 7 +- sound/soc/intel/sst-baytrail-pcm.c | 135 ++++++++++++++++++++++++++++++++----- 5 files changed, 249 insertions(+), 45 deletions(-)
This is for preparing suspend/resume support but can give also more safeguard against concurrent timestamp structure access between DSP firmware and host.
Now DSP DMA pointer is sampled in each pcm pointer callback in sst_byt_pcm_pointer() but that is unneeded since DSP updates the timestamp period basis and can potentially be racy if sst_byt_pcm_pointer() is called when DSP is updating the timestamp.
By taking DSP DMA pointer only after period elapsed IPC messages in byt_notify_pointer() and returning stored hw pointer in sst_byt_pcm_pointer() there is less risk for concurrent access.
The same stored hw pointer can be also used in suspend/resume code for restarting the stream at the same position.
Signed-off-by: Jarkko Nikula jarkko.nikula@linux.intel.com --- sound/soc/intel/sst-baytrail-pcm.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/sound/soc/intel/sst-baytrail-pcm.c b/sound/soc/intel/sst-baytrail-pcm.c index 6d101f3813b4..b8a89e9f753d 100644 --- a/sound/soc/intel/sst-baytrail-pcm.c +++ b/sound/soc/intel/sst-baytrail-pcm.c @@ -45,6 +45,9 @@ struct sst_byt_pcm_data { struct sst_byt_stream *stream; struct snd_pcm_substream *substream; struct mutex mutex; + + /* latest DSP DMA hw pointer */ + u32 hw_ptr; };
/* private data for the driver */ @@ -168,13 +171,19 @@ static u32 byt_notify_pointer(struct sst_byt_stream *stream, void *data) struct snd_pcm_substream *substream = pcm_data->substream; struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; - u32 pos; + struct sst_byt_priv_data *pdata = + snd_soc_platform_get_drvdata(rtd->platform); + struct sst_byt *byt = pdata->byt; + u32 pos, hw_pos;
+ hw_pos = sst_byt_get_dsp_position(byt, pcm_data->stream, + snd_pcm_lib_buffer_bytes(substream)); + pcm_data->hw_ptr = hw_pos; pos = frames_to_bytes(runtime, (runtime->control->appl_ptr % runtime->buffer_size));
- dev_dbg(rtd->dev, "PCM: App pointer %d bytes\n", pos); + dev_dbg(rtd->dev, "PCM: App/DMA pointer %u/%u bytes\n", pos, hw_pos);
snd_pcm_period_elapsed(substream); return pos; @@ -184,20 +193,11 @@ static snd_pcm_uframes_t sst_byt_pcm_pointer(struct snd_pcm_substream *substream { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; - struct sst_byt_priv_data *pdata = - snd_soc_platform_get_drvdata(rtd->platform); struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd); - struct sst_byt *byt = pdata->byt; - snd_pcm_uframes_t offset; - int pos;
- pos = sst_byt_get_dsp_position(byt, pcm_data->stream, - snd_pcm_lib_buffer_bytes(substream)); - offset = bytes_to_frames(runtime, pos); + dev_dbg(rtd->dev, "PCM: DMA pointer %u bytes\n", pcm_data->hw_ptr);
- dev_dbg(rtd->dev, "PCM: DMA pointer %zu bytes\n", - frames_to_bytes(runtime, (u32)offset)); - return offset; + return bytes_to_frames(runtime, pcm_data->hw_ptr); }
static int sst_byt_pcm_open(struct snd_pcm_substream *substream)
Baytrail ADSP stream IPC simplifies a little by moving IPC_IA_START_STREAM construction and sending directly into sst_byt_stream_start() from sst_byt_stream_operations(). This is because IPC_IA_START_STREAM is only stream IPC with extra message data so this move saves a few code lines.
Main motivation for this is to prepare for passing stream start position to sst_byt_stream_start() which will be needed in resume code.
Signed-off-by: Jarkko Nikula jarkko.nikula@linux.intel.com --- sound/soc/intel/sst-baytrail-ipc.c | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-)
diff --git a/sound/soc/intel/sst-baytrail-ipc.c b/sound/soc/intel/sst-baytrail-ipc.c index 0d31dbbf4806..69fdf5c70ea4 100644 --- a/sound/soc/intel/sst-baytrail-ipc.c +++ b/sound/soc/intel/sst-baytrail-ipc.c @@ -661,36 +661,32 @@ out: static int sst_byt_stream_operations(struct sst_byt *byt, int type, int stream_id, int wait) { - struct sst_byt_start_stream_params start_stream; u64 header; - void *tx_msg = NULL; - size_t size = 0; - - if (type != IPC_IA_START_STREAM) { - header = sst_byt_header(type, 0, false, stream_id); - } else { - start_stream.byte_offset = 0; - header = sst_byt_header(IPC_IA_START_STREAM, - sizeof(start_stream) + sizeof(u32), - true, stream_id); - tx_msg = &start_stream; - size = sizeof(start_stream); - }
+ header = sst_byt_header(type, 0, false, stream_id); if (wait) - return sst_byt_ipc_tx_msg_wait(byt, header, - tx_msg, size, NULL, 0); + return sst_byt_ipc_tx_msg_wait(byt, header, NULL, 0, NULL, 0); else - return sst_byt_ipc_tx_msg_nowait(byt, header, tx_msg, size); + return sst_byt_ipc_tx_msg_nowait(byt, header, NULL, 0); }
/* stream ALSA trigger operations */ int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream) { + struct sst_byt_start_stream_params start_stream; + void *tx_msg; + size_t size; + u64 header; int ret;
- ret = sst_byt_stream_operations(byt, IPC_IA_START_STREAM, - stream->str_id, 0); + start_stream.byte_offset = 0; + header = sst_byt_header(IPC_IA_START_STREAM, + sizeof(start_stream) + sizeof(u32), + true, stream->str_id); + tx_msg = &start_stream; + size = sizeof(start_stream); + + ret = sst_byt_ipc_tx_msg_nowait(byt, header, tx_msg, size); if (ret < 0) dev_err(byt->dev, "ipc: error failed to start stream %d\n", stream->str_id);
Stream start position will be needed in resume code. Prepare for it by adding start offset argument to sst_byt_stream_start().
Signed-off-by: Jarkko Nikula jarkko.nikula@linux.intel.com --- sound/soc/intel/sst-baytrail-ipc.c | 5 +++-- sound/soc/intel/sst-baytrail-ipc.h | 3 ++- sound/soc/intel/sst-baytrail-pcm.c | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/sound/soc/intel/sst-baytrail-ipc.c b/sound/soc/intel/sst-baytrail-ipc.c index 69fdf5c70ea4..8941ff2f4b78 100644 --- a/sound/soc/intel/sst-baytrail-ipc.c +++ b/sound/soc/intel/sst-baytrail-ipc.c @@ -671,7 +671,8 @@ static int sst_byt_stream_operations(struct sst_byt *byt, int type, }
/* stream ALSA trigger operations */ -int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream) +int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream, + u32 start_offset) { struct sst_byt_start_stream_params start_stream; void *tx_msg; @@ -679,7 +680,7 @@ int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream) u64 header; int ret;
- start_stream.byte_offset = 0; + start_stream.byte_offset = start_offset; header = sst_byt_header(IPC_IA_START_STREAM, sizeof(start_stream) + sizeof(u32), true, stream->str_id); diff --git a/sound/soc/intel/sst-baytrail-ipc.h b/sound/soc/intel/sst-baytrail-ipc.h index f172b6440fa9..b643d9892f60 100644 --- a/sound/soc/intel/sst-baytrail-ipc.h +++ b/sound/soc/intel/sst-baytrail-ipc.h @@ -53,7 +53,8 @@ int sst_byt_stream_commit(struct sst_byt *byt, struct sst_byt_stream *stream); int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream);
/* stream ALSA trigger operations */ -int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream); +int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream, + u32 start_offset); int sst_byt_stream_stop(struct sst_byt *byt, struct sst_byt_stream *stream); int sst_byt_stream_pause(struct sst_byt *byt, struct sst_byt_stream *stream); int sst_byt_stream_resume(struct sst_byt *byt, struct sst_byt_stream *stream); diff --git a/sound/soc/intel/sst-baytrail-pcm.c b/sound/soc/intel/sst-baytrail-pcm.c index b8a89e9f753d..e2c2540ffff4 100644 --- a/sound/soc/intel/sst-baytrail-pcm.c +++ b/sound/soc/intel/sst-baytrail-pcm.c @@ -145,7 +145,7 @@ static int sst_byt_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
switch (cmd) { case SNDRV_PCM_TRIGGER_START: - sst_byt_stream_start(byt, pcm_data->stream); + sst_byt_stream_start(byt, pcm_data->stream, 0); break; case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
We have to save the physical address of extended firmware block in the beginning of mailbox every time when we boot the DSP firmware since that mailbox address is re-used after DSP firmware is running. Otherwise DSP firmware will get bogus extended firmware block address during next DSP boot.
Currently this is not problem but becomes when DSP runtime rebooting is implemented. Prepare for that by moving extended firmware address saving from sst_byt_init() to sst_byt_boot().
Signed-off-by: Jarkko Nikula jarkko.nikula@linux.intel.com --- sound/soc/intel/sst-baytrail-dsp.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/sound/soc/intel/sst-baytrail-dsp.c b/sound/soc/intel/sst-baytrail-dsp.c index a50bf7fc0e3a..4a5d489e520b 100644 --- a/sound/soc/intel/sst-baytrail-dsp.c +++ b/sound/soc/intel/sst-baytrail-dsp.c @@ -214,6 +214,13 @@ static void sst_byt_boot(struct sst_dsp *sst) { int tries = 10;
+ /* + * save the physical address of extended firmware block in the first + * 4 bytes of the mailbox + */ + memcpy_toio(sst->addr.lpe + SST_BYT_MAILBOX_OFFSET, + &sst->pdata->fw_base, sizeof(u32)); + /* release stall and wait to unstall */ sst_dsp_shim_update_bits64(sst, SST_CSR, SST_BYT_CSR_STALL, 0x0); while (tries--) { @@ -317,13 +324,6 @@ static int sst_byt_init(struct sst_dsp *sst, struct sst_pdata *pdata) return ret; }
- /* - * save the physical address of extended firmware block in the first - * 4 bytes of the mailbox - */ - memcpy_toio(sst->addr.lpe + SST_BYT_MAILBOX_OFFSET, - &pdata->fw_base, sizeof(u32)); - ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32)); if (ret) return ret;
From: Liam Girdwood liam.r.girdwood@linux.intel.com
Suspend/resume requires reloading FW to boot state so we need to also make sure that the driver matches the FW state at boot.
Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com Signed-off-by: Jarkko Nikula jarkko.nikula@linux.intel.com --- sound/soc/intel/sst-baytrail-ipc.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/sound/soc/intel/sst-baytrail-ipc.c b/sound/soc/intel/sst-baytrail-ipc.c index 8941ff2f4b78..2487b6e2dfec 100644 --- a/sound/soc/intel/sst-baytrail-ipc.c +++ b/sound/soc/intel/sst-baytrail-ipc.c @@ -299,6 +299,24 @@ static inline void sst_byt_tx_msg_reply_complete(struct sst_byt *byt, wake_up(&msg->waitq); }
+static void sst_byt_drop_all(struct sst_byt *byt) +{ + struct ipc_message *msg, *tmp; + unsigned long flags; + + /* drop all TX and Rx messages before we stall + reset DSP */ + spin_lock_irqsave(&byt->dsp->spinlock, flags); + list_for_each_entry_safe(msg, tmp, &byt->tx_list, list) { + list_move(&msg->list, &byt->empty_list); + } + + list_for_each_entry_safe(msg, tmp, &byt->rx_list, list) { + list_move(&msg->list, &byt->empty_list); + } + + spin_unlock_irqrestore(&byt->dsp->spinlock, flags); +} + static int sst_byt_tx_wait_done(struct sst_byt *byt, struct ipc_message *msg, void *rx_data) {
From: Liam Girdwood liam.r.girdwood@linux.intel.com
Add suspend and resume support to Baytrail SST DSP. This is implemented by unloading firmware modules and putting DSP into reset prior suspend and restarting DSP again in normal boot state after resume.
Context restore for running streams is implemented by scheduling a work from sst_byt_pcm_trigger() that will allocate a stream with existing parameters and start it from last known buffer position before suspend.
[Jarkko: Squashed together 5 WIP patches from Liam and 1 from me]
Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com Signed-off-by: Jarkko Nikula jarkko.nikula@linux.intel.com --- sound/soc/intel/sst-baytrail-ipc.c | 69 ++++++++++++++++++++++++ sound/soc/intel/sst-baytrail-ipc.h | 4 ++ sound/soc/intel/sst-baytrail-pcm.c | 107 ++++++++++++++++++++++++++++++++++++- 3 files changed, 179 insertions(+), 1 deletion(-)
diff --git a/sound/soc/intel/sst-baytrail-ipc.c b/sound/soc/intel/sst-baytrail-ipc.c index 2487b6e2dfec..7c1ec003d55d 100644 --- a/sound/soc/intel/sst-baytrail-ipc.c +++ b/sound/soc/intel/sst-baytrail-ipc.c @@ -173,6 +173,7 @@ struct sst_byt { /* boot */ wait_queue_head_t boot_wait; bool boot_complete; + struct sst_fw *fw;
/* IPC messaging */ struct list_head tx_list; @@ -797,6 +798,73 @@ static struct sst_dsp_device byt_dev = { .ops = &sst_byt_ops, };
+int sst_byt_dsp_suspend_noirq(struct device *dev, struct sst_pdata *pdata) +{ + struct sst_byt *byt = pdata->dsp; + + dev_dbg(byt->dev, "dsp reset\n"); + sst_dsp_reset(byt->dsp); + 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); + + return 0; +} +EXPORT_SYMBOL_GPL(sst_byt_dsp_suspend_late); + +int sst_byt_dsp_boot(struct device *dev, struct sst_pdata *pdata) +{ + struct sst_byt *byt = pdata->dsp; + int ret; + + dev_dbg(byt->dev, "reload dsp fw\n"); + + sst_dsp_reset(byt->dsp); + + ret = sst_fw_reload(byt->fw); + if (ret < 0) { + dev_err(dev, "error: failed to reload firmware\n"); + return ret; + } + + /* wait for DSP boot completion */ + byt->boot_complete = false; + sst_dsp_boot(byt->dsp); + dev_dbg(byt->dev, "dsp booting...\n"); + + return 0; +} +EXPORT_SYMBOL_GPL(sst_byt_dsp_boot); + +int sst_byt_dsp_wait_for_ready(struct device *dev, struct sst_pdata *pdata) +{ + struct sst_byt *byt = pdata->dsp; + int err; + + dev_dbg(byt->dev, "wait for dsp reboot\n"); + + err = wait_event_timeout(byt->boot_wait, byt->boot_complete, + msecs_to_jiffies(IPC_BOOT_MSECS)); + if (err == 0) { + dev_err(byt->dev, "ipc: error DSP boot timeout\n"); + return -EIO; + } + + dev_dbg(byt->dev, "dsp rebooted\n"); + return 0; +} +EXPORT_SYMBOL_GPL(sst_byt_dsp_wait_for_ready); + int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata) { struct sst_byt *byt; @@ -863,6 +931,7 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata) }
pdata->dsp = byt; + byt->fw = byt_sst_fw;
return 0;
diff --git a/sound/soc/intel/sst-baytrail-ipc.h b/sound/soc/intel/sst-baytrail-ipc.h index b643d9892f60..06a4d202689b 100644 --- a/sound/soc/intel/sst-baytrail-ipc.h +++ b/sound/soc/intel/sst-baytrail-ipc.h @@ -66,5 +66,9 @@ 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);
#endif diff --git a/sound/soc/intel/sst-baytrail-pcm.c b/sound/soc/intel/sst-baytrail-pcm.c index e2c2540ffff4..00a2118d20f5 100644 --- a/sound/soc/intel/sst-baytrail-pcm.c +++ b/sound/soc/intel/sst-baytrail-pcm.c @@ -48,6 +48,8 @@ struct sst_byt_pcm_data {
/* latest DSP DMA hw pointer */ u32 hw_ptr; + + struct work_struct work; };
/* private data for the driver */ @@ -133,6 +135,38 @@ static int sst_byt_pcm_hw_free(struct snd_pcm_substream *substream) return 0; }
+static int sst_byt_pcm_restore_stream_context(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sst_byt_priv_data *pdata = + snd_soc_platform_get_drvdata(rtd->platform); + struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd); + struct sst_byt *byt = pdata->byt; + int ret; + + /* commit stream using existing stream params */ + ret = sst_byt_stream_commit(byt, pcm_data->stream); + if (ret < 0) { + dev_err(rtd->dev, "PCM: failed stream commit %d\n", ret); + return ret; + } + + sst_byt_stream_start(byt, pcm_data->stream, pcm_data->hw_ptr); + + dev_dbg(rtd->dev, "stream context restored at offset %d\n", + pcm_data->hw_ptr); + + return 0; +} + +static void sst_byt_pcm_work(struct work_struct *work) +{ + struct sst_byt_pcm_data *pcm_data = + container_of(work, struct sst_byt_pcm_data, work); + + sst_byt_pcm_restore_stream_context(pcm_data->substream); +} + static int sst_byt_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -148,6 +182,8 @@ 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); + break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: sst_byt_stream_resume(byt, pcm_data->stream); break; @@ -344,8 +380,10 @@ static int sst_byt_pcm_probe(struct snd_soc_platform *platform) priv_data->byt = plat_data->dsp; snd_soc_platform_set_drvdata(platform, priv_data);
- for (i = 0; i < ARRAY_SIZE(byt_dais); i++) + for (i = 0; i < ARRAY_SIZE(byt_dais); i++) { mutex_init(&priv_data->pcm[i].mutex); + INIT_WORK(&priv_data->pcm[i].work, sst_byt_pcm_work); + }
return 0; } @@ -367,6 +405,72 @@ static const struct snd_soc_component_driver byt_dai_component = { .name = "byt-dai", };
+#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); + int ret; + + dev_dbg(dev, "suspending late\n"); + + ret = sst_byt_dsp_suspend_late(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_resume_early(struct device *dev) +{ + struct sst_pdata *sst_pdata = dev_get_platdata(dev); + + 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"); + + /* wait for FW to finish booting */ + return sst_byt_dsp_wait_for_ready(dev, sst_pdata); +} + +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, +}; + +#define SST_BYT_PM_OPS (&sst_byt_pm_ops) +#else +#define SST_BYT_PM_OPS NULL +#endif + static int sst_byt_pcm_dev_probe(struct platform_device *pdev) { struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev); @@ -409,6 +513,7 @@ static struct platform_driver sst_byt_pcm_driver = { .driver = { .name = "baytrail-pcm-audio", .owner = THIS_MODULE, + .pm = SST_BYT_PM_OPS, },
.probe = sst_byt_pcm_dev_probe,
Since there is no support for compressed audio in Baytrail ADSP firmware there is no need to leave it on during suspend since ALSA PCM buffers are too small for leaving ADSP on for playing or recording.
Implement PM callbacks to Baytrail byt-rt5640.c machine driver that call snd_soc_suspend and snd_soc_resume functions and unset the ignore_suspend fields in DAI links.
This makes soc-core and ALSA core gracefully suspend and resume active stream and call sst_byt_pcm_trigger() during suspend-resume cycle.
Signed-off-by: Jarkko Nikula jarkko.nikula@linux.intel.com --- sound/soc/intel/byt-rt5640.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/sound/soc/intel/byt-rt5640.c b/sound/soc/intel/byt-rt5640.c index eff97c8e5218..9061616f0f45 100644 --- a/sound/soc/intel/byt-rt5640.c +++ b/sound/soc/intel/byt-rt5640.c @@ -124,7 +124,6 @@ static struct snd_soc_dai_link byt_rt5640_dais[] = { .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, .init = byt_rt5640_init, - .ignore_suspend = 1, .ops = &byt_rt5640_ops, }, { @@ -137,7 +136,6 @@ static struct snd_soc_dai_link byt_rt5640_dais[] = { .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, .init = NULL, - .ignore_suspend = 1, .ops = &byt_rt5640_ops, }, }; @@ -152,6 +150,17 @@ static struct snd_soc_card byt_rt5640_card = { .num_dapm_routes = ARRAY_SIZE(byt_rt5640_audio_map), };
+#ifdef CONFIG_PM_SLEEP +static const struct dev_pm_ops byt_rt5640_pm_ops = { + .suspend = snd_soc_suspend, + .resume = snd_soc_resume, +}; + +#define BYT_RT5640_PM_OPS (&byt_rt5640_pm_ops) +#else +#define BYT_RT5640_PM_OPS NULL +#endif + static int byt_rt5640_probe(struct platform_device *pdev) { struct snd_soc_card *card = &byt_rt5640_card; @@ -177,6 +186,7 @@ static struct platform_driver byt_rt5640_audio = { .driver = { .name = "byt-rt5640", .owner = THIS_MODULE, + .pm = BYT_RT5640_PM_OPS, }, }; module_platform_driver(byt_rt5640_audio)
On Thu, May 08, 2014 at 04:07:20PM +0300, Jarkko Nikula wrote:
This set adds suspend/resume support to Baytrail SST. This is implemented by putting the DSP into reset and by releasing driver resources before suspend. In resume path the DSP is rebooted in normal boot state. Active streams are restored by restarting them from last known buffer position before suspend.
Applied all, thanks.
participants (2)
-
Jarkko Nikula
-
Mark Brown