diff options
Diffstat (limited to 'drivers/staging/intel_sst/intel_sst.c')
-rw-r--r-- | drivers/staging/intel_sst/intel_sst.c | 129 |
1 files changed, 110 insertions, 19 deletions
diff --git a/drivers/staging/intel_sst/intel_sst.c b/drivers/staging/intel_sst/intel_sst.c index 81c24d19eb9e..c0c144a2cda1 100644 --- a/drivers/staging/intel_sst/intel_sst.c +++ b/drivers/staging/intel_sst/intel_sst.c @@ -107,6 +107,9 @@ static irqreturn_t intel_sst_interrupt(int irq, void *context) unsigned int size = 0, str_id; struct stream_info *stream ; + /* Do not handle interrupt in suspended state */ + if (drv->sst_state == SST_SUSPENDED) + return IRQ_NONE; /* Interrupt arrived, check src */ isr.full = sst_shim_read(drv->shim, SST_ISRX); @@ -316,14 +319,30 @@ static int __devinit intel_sst_probe(struct pci_dev *pci, if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) { ret = misc_register(&lpe_dev); if (ret) { - pr_err("couldn't register misc driver\n"); + pr_err("couldn't register LPE device\n"); goto do_free_misc; } + } else if (sst_drv_ctx->pci_id == SST_MFLD_PCI_ID) { + u32 csr; + + /*allocate mem for fw context save during suspend*/ + sst_drv_ctx->fw_cntx = kzalloc(FW_CONTEXT_MEM, GFP_KERNEL); + if (!sst_drv_ctx->fw_cntx) { + ret = -ENOMEM; + goto do_free_misc; + } + /*setting zero as that is valid mem to restore*/ + sst_drv_ctx->fw_cntx_size = 0; + + /*set lpe start clock and ram size*/ + csr = sst_shim_read(sst_drv_ctx->shim, SST_CSR); + csr |= 0x30060; /*remove the clock ratio after fw fix*/ + sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr); } sst_drv_ctx->lpe_stalled = 0; - pm_runtime_set_active(&pci->dev); - pm_runtime_enable(&pci->dev); + pci_set_drvdata(pci, sst_drv_ctx); pm_runtime_allow(&pci->dev); + pm_runtime_put_noidle(&pci->dev); pr_debug("...successfully done!!!\n"); return ret; @@ -355,7 +374,8 @@ free_mad_wq: destroy_workqueue(sst_drv_ctx->mad_wq); do_free_drv_ctx: kfree(sst_drv_ctx); - pr_err("Probe failed with 0x%x\n", ret); + sst_drv_ctx = NULL; + pr_err("Probe failed with %d\n", ret); return ret; } @@ -369,35 +389,76 @@ do_free_drv_ctx: */ static void __devexit intel_sst_remove(struct pci_dev *pci) { + pm_runtime_get_noresume(&pci->dev); + pm_runtime_forbid(&pci->dev); pci_dev_put(sst_drv_ctx->pci); mutex_lock(&sst_drv_ctx->sst_lock); sst_drv_ctx->sst_state = SST_UN_INIT; mutex_unlock(&sst_drv_ctx->sst_lock); misc_deregister(&lpe_ctrl); - if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) - misc_deregister(&lpe_dev); free_irq(pci->irq, sst_drv_ctx); iounmap(sst_drv_ctx->dram); iounmap(sst_drv_ctx->iram); iounmap(sst_drv_ctx->mailbox); iounmap(sst_drv_ctx->shim); sst_drv_ctx->pmic_state = SND_MAD_UN_INIT; - if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) + if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) { + misc_deregister(&lpe_dev); kfree(sst_drv_ctx->mmap_mem); + } else + kfree(sst_drv_ctx->fw_cntx); flush_scheduled_work(); destroy_workqueue(sst_drv_ctx->process_reply_wq); destroy_workqueue(sst_drv_ctx->process_msg_wq); destroy_workqueue(sst_drv_ctx->post_msg_wq); destroy_workqueue(sst_drv_ctx->mad_wq); - kfree(sst_drv_ctx); - pci_release_region(pci, 1); - pci_release_region(pci, 2); - pci_release_region(pci, 3); - pci_release_region(pci, 4); - pci_release_region(pci, 5); + kfree(pci_get_drvdata(pci)); + sst_drv_ctx = NULL; + pci_release_regions(pci); + pci_disable_device(pci); pci_set_drvdata(pci, NULL); } +void sst_save_dsp_context(void) +{ + struct snd_sst_ctxt_params fw_context; + unsigned int pvt_id, i; + struct ipc_post *msg = NULL; + + /*check cpu type*/ + if (sst_drv_ctx->pci_id != SST_MFLD_PCI_ID) + return; + /*not supported for rest*/ + if (sst_drv_ctx->sst_state != SST_FW_RUNNING) { + pr_debug("fw not running no context save ...\n"); + return; + } + + /*send msg to fw*/ + if (sst_create_large_msg(&msg)) + return; + pvt_id = sst_assign_pvt_id(sst_drv_ctx); + i = sst_get_block_stream(sst_drv_ctx); + sst_drv_ctx->alloc_block[i].sst_id = pvt_id; + sst_fill_header(&msg->header, IPC_IA_GET_FW_CTXT, 1, pvt_id); + msg->header.part.data = sizeof(fw_context) + sizeof(u32); + fw_context.address = virt_to_phys((void *)sst_drv_ctx->fw_cntx); + fw_context.size = FW_CONTEXT_MEM; + memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); + memcpy(msg->mailbox_data + sizeof(u32), + &fw_context, sizeof(fw_context)); + spin_lock(&sst_drv_ctx->list_spin_lock); + list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); + spin_unlock(&sst_drv_ctx->list_spin_lock); + sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); + /*wait for reply*/ + if (sst_wait_timeout(sst_drv_ctx, &sst_drv_ctx->alloc_block[i])) + pr_debug("err fw context save timeout ...\n"); + sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT; + pr_debug("fw context saved ...\n"); + return; +} + /* Power Management */ /* * intel_sst_suspend - PCI suspend function @@ -417,6 +478,8 @@ int intel_sst_suspend(struct pci_dev *pci, pm_message_t state) pr_err("active streams,not able to suspend\n"); return -EBUSY; } + /*save fw context*/ + sst_save_dsp_context(); /*Assert RESET on LPE Processor*/ csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); csr.full = csr.full | 0x2; @@ -461,18 +524,45 @@ int intel_sst_resume(struct pci_dev *pci) return 0; } +/* The runtime_suspend/resume is pretty much similar to the legacy suspend/resume with the noted exception below: + * The PCI core takes care of taking the system through D3hot and restoring it back to D0 and so there is + * no need to duplicate that here. + */ static int intel_sst_runtime_suspend(struct device *dev) { - struct pci_dev *pci_dev = to_pci_dev(dev); - pr_debug("runtime_suspend called\n"); - return intel_sst_suspend(pci_dev, PMSG_SUSPEND); + union config_status_reg csr; + + pr_debug("intel_sst_runtime_suspend called\n"); + if (sst_drv_ctx->stream_cnt) { + pr_err("active streams,not able to suspend\n"); + return -EBUSY; + } + /*save fw context*/ + sst_save_dsp_context(); + /*Assert RESET on LPE Processor*/ + csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); + csr.full = csr.full | 0x2; + /* Move the SST state to Suspended */ + mutex_lock(&sst_drv_ctx->sst_lock); + sst_drv_ctx->sst_state = SST_SUSPENDED; + sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); + mutex_unlock(&sst_drv_ctx->sst_lock); + return 0; } static int intel_sst_runtime_resume(struct device *dev) { - struct pci_dev *pci_dev = to_pci_dev(dev); - pr_debug("runtime_resume called\n"); - return intel_sst_resume(pci_dev); + + pr_debug("intel_sst_runtime_resume called\n"); + if (sst_drv_ctx->sst_state != SST_SUSPENDED) { + pr_err("SST is not in suspended state\n"); + return 0; + } + + mutex_lock(&sst_drv_ctx->sst_lock); + sst_drv_ctx->sst_state = SST_UN_INIT; + mutex_unlock(&sst_drv_ctx->sst_lock); + return 0; } static int intel_sst_runtime_idle(struct device *dev) @@ -545,6 +635,7 @@ static void __exit intel_sst_exit(void) pci_unregister_driver(&driver); pr_debug("driver unloaded\n"); + sst_drv_ctx = NULL; return; } |