diff options
Diffstat (limited to 'sound/soc/intel/sst-baytrail-ipc.c')
-rw-r--r-- | sound/soc/intel/sst-baytrail-ipc.c | 140 |
1 files changed, 117 insertions, 23 deletions
diff --git a/sound/soc/intel/sst-baytrail-ipc.c b/sound/soc/intel/sst-baytrail-ipc.c index d0eaeee21be4..d207b22ea330 100644 --- a/sound/soc/intel/sst-baytrail-ipc.c +++ b/sound/soc/intel/sst-baytrail-ipc.c @@ -22,7 +22,6 @@ #include <linux/export.h> #include <linux/slab.h> #include <linux/delay.h> -#include <linux/list.h> #include <linux/platform_device.h> #include <linux/kthread.h> #include <linux/firmware.h> @@ -173,6 +172,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; @@ -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) { @@ -542,16 +560,20 @@ struct sst_byt_stream *sst_byt_stream_new(struct sst_byt *byt, int id, void *data) { struct sst_byt_stream *stream; + struct sst_dsp *sst = byt->dsp; + unsigned long flags; stream = kzalloc(sizeof(*stream), GFP_KERNEL); if (stream == NULL) return NULL; + spin_lock_irqsave(&sst->spinlock, flags); list_add(&stream->node, &byt->stream_list); stream->notify_position = notify_position; stream->pdata = data; stream->byt = byt; stream->str_id = id; + spin_unlock_irqrestore(&sst->spinlock, flags); return stream; } @@ -630,6 +652,8 @@ int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream) { u64 header; int ret = 0; + struct sst_dsp *sst = byt->dsp; + unsigned long flags; if (!stream->commited) goto out; @@ -644,8 +668,10 @@ int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream) stream->commited = false; out: + spin_lock_irqsave(&sst->spinlock, flags); list_del(&stream->node); kfree(stream); + spin_unlock_irqrestore(&sst->spinlock, flags); return ret; } @@ -653,36 +679,33 @@ 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) +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; + 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 = start_offset; + 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); @@ -774,6 +797,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; @@ -801,7 +891,7 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata) /* start the IPC message thread */ init_kthread_worker(&byt->kworker); byt->tx_thread = kthread_run(kthread_worker_fn, - &byt->kworker, + &byt->kworker, "%s", dev_name(byt->dev)); if (IS_ERR(byt->tx_thread)) { err = PTR_ERR(byt->tx_thread); @@ -816,7 +906,7 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata) byt->dsp = sst_dsp_new(dev, &byt_dev, pdata); if (byt->dsp == NULL) { err = -ENODEV; - goto err_free_msg; + goto dsp_err; } /* keep the DSP in reset state for base FW loading */ @@ -840,6 +930,7 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata) } pdata->dsp = byt; + byt->fw = byt_sst_fw; return 0; @@ -848,6 +939,8 @@ boot_err: sst_fw_free(byt_sst_fw); fw_err: sst_dsp_free(byt->dsp); +dsp_err: + kthread_stop(byt->tx_thread); err_free_msg: kfree(byt->msg); @@ -862,6 +955,7 @@ void sst_byt_dsp_free(struct device *dev, struct sst_pdata *pdata) sst_dsp_reset(byt->dsp); sst_fw_free_all(byt->dsp); sst_dsp_free(byt->dsp); + kthread_stop(byt->tx_thread); kfree(byt->msg); } EXPORT_SYMBOL_GPL(sst_byt_dsp_free); |