Merge remote-tracking branches 'asoc/topic/atmel', 'asoc/topic/davinci', 'asoc/topic...
[cascardo/linux.git] / sound / soc / intel / sst / sst.c
index 11c5786..1a7eeec 100644 (file)
@@ -423,23 +423,135 @@ static int intel_sst_runtime_suspend(struct device *dev)
        return ret;
 }
 
-static int intel_sst_runtime_resume(struct device *dev)
+static int intel_sst_suspend(struct device *dev)
 {
-       int ret = 0;
        struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+       struct sst_fw_save *fw_save;
+       int i, ret = 0;
 
-       if (ctx->sst_state == SST_RESET) {
-               ret = sst_load_fw(ctx);
-               if (ret) {
-                       dev_err(dev, "FW download fail %d\n", ret);
-                       sst_set_fw_state_locked(ctx, SST_RESET);
+       /* check first if we are already in SW reset */
+       if (ctx->sst_state == SST_RESET)
+               return 0;
+
+       /*
+        * check if any stream is active and running
+        * they should already by suspend by soc_suspend
+        */
+       for (i = 1; i <= ctx->info.max_streams; i++) {
+               struct stream_info *stream = &ctx->streams[i];
+
+               if (stream->status == STREAM_RUNNING) {
+                       dev_err(dev, "stream %d is running, cant susupend, abort\n", i);
+                       return -EBUSY;
                }
        }
+       synchronize_irq(ctx->irq_num);
+       flush_workqueue(ctx->post_msg_wq);
+
+       /* Move the SST state to Reset */
+       sst_set_fw_state_locked(ctx, SST_RESET);
+
+       /* tell DSP we are suspending */
+       if (ctx->ops->save_dsp_context(ctx))
+               return -EBUSY;
+
+       /* save the memories */
+       fw_save = kzalloc(sizeof(*fw_save), GFP_KERNEL);
+       if (!fw_save)
+               return -ENOMEM;
+       fw_save->iram = kzalloc(ctx->iram_end - ctx->iram_base, GFP_KERNEL);
+       if (!fw_save->iram) {
+               ret = -ENOMEM;
+               goto iram;
+       }
+       fw_save->dram = kzalloc(ctx->dram_end - ctx->dram_base, GFP_KERNEL);
+       if (!fw_save->dram) {
+               ret = -ENOMEM;
+               goto dram;
+       }
+       fw_save->sram = kzalloc(SST_MAILBOX_SIZE, GFP_KERNEL);
+       if (!fw_save->sram) {
+               ret = -ENOMEM;
+               goto sram;
+       }
+
+       fw_save->ddr = kzalloc(ctx->ddr_end - ctx->ddr_base, GFP_KERNEL);
+       if (!fw_save->ddr) {
+               ret = -ENOMEM;
+               goto ddr;
+       }
+
+       memcpy32_fromio(fw_save->iram, ctx->iram, ctx->iram_end - ctx->iram_base);
+       memcpy32_fromio(fw_save->dram, ctx->dram, ctx->dram_end - ctx->dram_base);
+       memcpy32_fromio(fw_save->sram, ctx->mailbox, SST_MAILBOX_SIZE);
+       memcpy32_fromio(fw_save->ddr, ctx->ddr, ctx->ddr_end - ctx->ddr_base);
+
+       ctx->fw_save = fw_save;
+       ctx->ops->reset(ctx);
+       return 0;
+ddr:
+       kfree(fw_save->sram);
+sram:
+       kfree(fw_save->dram);
+dram:
+       kfree(fw_save->iram);
+iram:
+       kfree(fw_save);
+       return ret;
+}
+
+static int intel_sst_resume(struct device *dev)
+{
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+       struct sst_fw_save *fw_save = ctx->fw_save;
+       int ret = 0;
+       struct sst_block *block;
+
+       if (!fw_save)
+               return 0;
+
+       sst_set_fw_state_locked(ctx, SST_FW_LOADING);
+
+       /* we have to restore the memory saved */
+       ctx->ops->reset(ctx);
+
+       ctx->fw_save = NULL;
+
+       memcpy32_toio(ctx->iram, fw_save->iram, ctx->iram_end - ctx->iram_base);
+       memcpy32_toio(ctx->dram, fw_save->dram, ctx->dram_end - ctx->dram_base);
+       memcpy32_toio(ctx->mailbox, fw_save->sram, SST_MAILBOX_SIZE);
+       memcpy32_toio(ctx->ddr, fw_save->ddr, ctx->ddr_end - ctx->ddr_base);
+
+       kfree(fw_save->sram);
+       kfree(fw_save->dram);
+       kfree(fw_save->iram);
+       kfree(fw_save->ddr);
+       kfree(fw_save);
+
+       block = sst_create_block(ctx, 0, FW_DWNL_ID);
+       if (block == NULL)
+               return -ENOMEM;
+
+
+       /* start and wait for ack */
+       ctx->ops->start(ctx);
+       ret = sst_wait_timeout(ctx, block);
+       if (ret) {
+               dev_err(ctx->dev, "fw download failed %d\n", ret);
+               /* FW download failed due to timeout */
+               ret = -EBUSY;
+
+       } else {
+               sst_set_fw_state_locked(ctx, SST_FW_RUNNING);
+       }
+
+       sst_free_block(ctx, block);
        return ret;
 }
 
 const struct dev_pm_ops intel_sst_pm = {
+       .suspend = intel_sst_suspend,
+       .resume = intel_sst_resume,
        .runtime_suspend = intel_sst_runtime_suspend,
-       .runtime_resume = intel_sst_runtime_resume,
 };
 EXPORT_SYMBOL_GPL(intel_sst_pm);