Commit afe9194d authored by Vinod Koul's avatar Vinod Koul Committed by Greg Kroah-Hartman
Browse files

intel_sst: Save audio state across D3 on Medfield



During suspend and runtime_suspend audio dsp will be in D3 state
and will loose its context.

This patch adds support in driver to save the dsp context
and restore this context during resume

Signed-off-by: default avatarVinod Koul <vinod.koul@intel.com>
Signed-off-by: default avatarRamesh Babu K V <ramesh.babu@intel.com>
Signed-off-by: default avatarAlan Cox <alan@linux.intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 31dea738
Loading
Loading
Loading
Loading
+63 −4
Original line number Diff line number Diff line
@@ -316,9 +316,25 @@ 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);
@@ -374,16 +390,17 @@ static void __devexit intel_sst_remove(struct pci_dev *pci)
	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);
@@ -398,6 +415,46 @@ static void __devexit intel_sst_remove(struct pci_dev *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 +474,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;
+6 −3
Original line number Diff line number Diff line
@@ -28,8 +28,8 @@
 *  Common private declarations for SST
 */

#define SST_DRIVER_VERSION "1.2.09"
#define SST_VERSION_NUM 0x1209
#define SST_DRIVER_VERSION "1.2.11"
#define SST_VERSION_NUM 0x1211

/* driver names */
#define SST_DRV_NAME "intel_sst_driver"
@@ -37,6 +37,7 @@
#define SST_MFLD_PCI_ID 0x082F
#define PCI_ID_LENGTH 4
#define SST_SUSPEND_DELAY 2000
#define FW_CONTEXT_MEM (64*1024)

enum sst_states {
	SST_FW_LOADED = 1,
@@ -94,7 +95,7 @@ enum sst_ram_type {
/* SST shim registers to structure mapping  */
union config_status_reg {
	struct {
		u32 rsvd0:1;
		u32 mfld_strb:1;
		u32 sst_reset:1;
		u32 hw_rsvd:3;
		u32 sst_clk:2;
@@ -417,6 +418,8 @@ struct intel_sst_drv {
	unsigned int		audio_start;
	dev_t			devt_d, devt_c;
	unsigned int		max_streams;
	unsigned int		*fw_cntx;
	unsigned int		fw_cntx_size;
};

extern struct intel_sst_drv *sst_drv_ctx;
+2 −0
Original line number Diff line number Diff line
@@ -508,6 +508,7 @@ int register_sst_card(struct intel_sst_card_ops *card)
			sst_drv_ctx->pmic_state = SND_MAD_INIT_DONE;
			sst_drv_ctx->rx_time_slot_status = 0; /*default AMIC*/
			card->pcm_control = sst_pmic_ops.pcm_control;
			sst_drv_ctx->scard_ops->card_status = SND_CARD_UN_INIT;
			return 0;
		} else {
			pr_err("strcmp fail %s\n", card->module_name);
@@ -519,6 +520,7 @@ int register_sst_card(struct intel_sst_card_ops *card)
		pr_err("Repeat for registration..denied\n");
		return -EBADRQC;
	}
	sst_drv_ctx->scard_ops->card_status = SND_CARD_UN_INIT;
	return 0;
}
EXPORT_SYMBOL_GPL(register_sst_card);
+10 −4
Original line number Diff line number Diff line
@@ -73,7 +73,8 @@ static int intel_sst_reset_dsp_medfield(void)
	union config_status_reg csr;

	pr_debug("Resetting the DSP in medfield\n");
	csr.full = 0x048303E2;
	csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
	csr.full |= 0x382;
	sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);

	return 0;
@@ -109,11 +110,16 @@ static int sst_start_medfield(void)
{
	union config_status_reg csr;

	csr.full = 0x04830062;
	csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
	csr.part.bypass = 0;
	sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);
	csr.full = 0x04830063;
	csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
	csr.part.mfld_strb = 1;
	sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);
	csr.full = 0x04830061;
	csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
	csr.part.run_stall = 0;
	csr.part.sst_reset = 0;
	pr_debug("Starting the DSP_medfld %x\n", csr.full);
	sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);
	pr_debug("Starting the DSP_medfld\n");

+6 −0
Original line number Diff line number Diff line
@@ -56,6 +56,8 @@
#define IPC_IA_GET_FW_VERSION 0x04
#define IPC_IA_GET_FW_BUILD_INF 0x05
#define IPC_IA_GET_FW_INFO 0x06
#define IPC_IA_GET_FW_CTXT 0x07
#define IPC_IA_SET_FW_CTXT 0x08

/* I2L Codec Config/control msgs */
#define IPC_IA_SET_CODEC_PARAMS 0x10
@@ -406,4 +408,8 @@ struct ipc_post {
	char *mailbox_data;
};

struct snd_sst_ctxt_params {
	u32 address; /* Physical Address in DDR where the context is stored */
	u32 size; /* size of the context */
};
#endif /* __INTEL_SST_FW_IPC_H__ */
Loading