diff options
Diffstat (limited to 'sound/soc/intel/sst/sst.c')
-rw-r--r-- | sound/soc/intel/sst/sst.c | 128 |
1 files changed, 120 insertions, 8 deletions
diff --git a/sound/soc/intel/sst/sst.c b/sound/soc/intel/sst/sst.c index 11c578651c1c..1a7eeec444b1 100644 --- a/sound/soc/intel/sst/sst.c +++ b/sound/soc/intel/sst/sst.c | |||
@@ -423,23 +423,135 @@ static int intel_sst_runtime_suspend(struct device *dev) | |||
423 | return ret; | 423 | return ret; |
424 | } | 424 | } |
425 | 425 | ||
426 | static int intel_sst_runtime_resume(struct device *dev) | 426 | static int intel_sst_suspend(struct device *dev) |
427 | { | 427 | { |
428 | int ret = 0; | ||
429 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | 428 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); |
429 | struct sst_fw_save *fw_save; | ||
430 | int i, ret = 0; | ||
430 | 431 | ||
431 | if (ctx->sst_state == SST_RESET) { | 432 | /* check first if we are already in SW reset */ |
432 | ret = sst_load_fw(ctx); | 433 | if (ctx->sst_state == SST_RESET) |
433 | if (ret) { | 434 | return 0; |
434 | dev_err(dev, "FW download fail %d\n", ret); | 435 | |
435 | sst_set_fw_state_locked(ctx, SST_RESET); | 436 | /* |
437 | * check if any stream is active and running | ||
438 | * they should already by suspend by soc_suspend | ||
439 | */ | ||
440 | for (i = 1; i <= ctx->info.max_streams; i++) { | ||
441 | struct stream_info *stream = &ctx->streams[i]; | ||
442 | |||
443 | if (stream->status == STREAM_RUNNING) { | ||
444 | dev_err(dev, "stream %d is running, cant susupend, abort\n", i); | ||
445 | return -EBUSY; | ||
436 | } | 446 | } |
437 | } | 447 | } |
448 | synchronize_irq(ctx->irq_num); | ||
449 | flush_workqueue(ctx->post_msg_wq); | ||
450 | |||
451 | /* Move the SST state to Reset */ | ||
452 | sst_set_fw_state_locked(ctx, SST_RESET); | ||
453 | |||
454 | /* tell DSP we are suspending */ | ||
455 | if (ctx->ops->save_dsp_context(ctx)) | ||
456 | return -EBUSY; | ||
457 | |||
458 | /* save the memories */ | ||
459 | fw_save = kzalloc(sizeof(*fw_save), GFP_KERNEL); | ||
460 | if (!fw_save) | ||
461 | return -ENOMEM; | ||
462 | fw_save->iram = kzalloc(ctx->iram_end - ctx->iram_base, GFP_KERNEL); | ||
463 | if (!fw_save->iram) { | ||
464 | ret = -ENOMEM; | ||
465 | goto iram; | ||
466 | } | ||
467 | fw_save->dram = kzalloc(ctx->dram_end - ctx->dram_base, GFP_KERNEL); | ||
468 | if (!fw_save->dram) { | ||
469 | ret = -ENOMEM; | ||
470 | goto dram; | ||
471 | } | ||
472 | fw_save->sram = kzalloc(SST_MAILBOX_SIZE, GFP_KERNEL); | ||
473 | if (!fw_save->sram) { | ||
474 | ret = -ENOMEM; | ||
475 | goto sram; | ||
476 | } | ||
477 | |||
478 | fw_save->ddr = kzalloc(ctx->ddr_end - ctx->ddr_base, GFP_KERNEL); | ||
479 | if (!fw_save->ddr) { | ||
480 | ret = -ENOMEM; | ||
481 | goto ddr; | ||
482 | } | ||
483 | |||
484 | memcpy32_fromio(fw_save->iram, ctx->iram, ctx->iram_end - ctx->iram_base); | ||
485 | memcpy32_fromio(fw_save->dram, ctx->dram, ctx->dram_end - ctx->dram_base); | ||
486 | memcpy32_fromio(fw_save->sram, ctx->mailbox, SST_MAILBOX_SIZE); | ||
487 | memcpy32_fromio(fw_save->ddr, ctx->ddr, ctx->ddr_end - ctx->ddr_base); | ||
488 | |||
489 | ctx->fw_save = fw_save; | ||
490 | ctx->ops->reset(ctx); | ||
491 | return 0; | ||
492 | ddr: | ||
493 | kfree(fw_save->sram); | ||
494 | sram: | ||
495 | kfree(fw_save->dram); | ||
496 | dram: | ||
497 | kfree(fw_save->iram); | ||
498 | iram: | ||
499 | kfree(fw_save); | ||
500 | return ret; | ||
501 | } | ||
502 | |||
503 | static int intel_sst_resume(struct device *dev) | ||
504 | { | ||
505 | struct intel_sst_drv *ctx = dev_get_drvdata(dev); | ||
506 | struct sst_fw_save *fw_save = ctx->fw_save; | ||
507 | int ret = 0; | ||
508 | struct sst_block *block; | ||
509 | |||
510 | if (!fw_save) | ||
511 | return 0; | ||
512 | |||
513 | sst_set_fw_state_locked(ctx, SST_FW_LOADING); | ||
514 | |||
515 | /* we have to restore the memory saved */ | ||
516 | ctx->ops->reset(ctx); | ||
517 | |||
518 | ctx->fw_save = NULL; | ||
519 | |||
520 | memcpy32_toio(ctx->iram, fw_save->iram, ctx->iram_end - ctx->iram_base); | ||
521 | memcpy32_toio(ctx->dram, fw_save->dram, ctx->dram_end - ctx->dram_base); | ||
522 | memcpy32_toio(ctx->mailbox, fw_save->sram, SST_MAILBOX_SIZE); | ||
523 | memcpy32_toio(ctx->ddr, fw_save->ddr, ctx->ddr_end - ctx->ddr_base); | ||
524 | |||
525 | kfree(fw_save->sram); | ||
526 | kfree(fw_save->dram); | ||
527 | kfree(fw_save->iram); | ||
528 | kfree(fw_save->ddr); | ||
529 | kfree(fw_save); | ||
530 | |||
531 | block = sst_create_block(ctx, 0, FW_DWNL_ID); | ||
532 | if (block == NULL) | ||
533 | return -ENOMEM; | ||
534 | |||
535 | |||
536 | /* start and wait for ack */ | ||
537 | ctx->ops->start(ctx); | ||
538 | ret = sst_wait_timeout(ctx, block); | ||
539 | if (ret) { | ||
540 | dev_err(ctx->dev, "fw download failed %d\n", ret); | ||
541 | /* FW download failed due to timeout */ | ||
542 | ret = -EBUSY; | ||
543 | |||
544 | } else { | ||
545 | sst_set_fw_state_locked(ctx, SST_FW_RUNNING); | ||
546 | } | ||
547 | |||
548 | sst_free_block(ctx, block); | ||
438 | return ret; | 549 | return ret; |
439 | } | 550 | } |
440 | 551 | ||
441 | const struct dev_pm_ops intel_sst_pm = { | 552 | const struct dev_pm_ops intel_sst_pm = { |
553 | .suspend = intel_sst_suspend, | ||
554 | .resume = intel_sst_resume, | ||
442 | .runtime_suspend = intel_sst_runtime_suspend, | 555 | .runtime_suspend = intel_sst_runtime_suspend, |
443 | .runtime_resume = intel_sst_runtime_resume, | ||
444 | }; | 556 | }; |
445 | EXPORT_SYMBOL_GPL(intel_sst_pm); | 557 | EXPORT_SYMBOL_GPL(intel_sst_pm); |