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

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
...@@ -316,9 +316,25 @@ static int __devinit intel_sst_probe(struct pci_dev *pci, ...@@ -316,9 +316,25 @@ static int __devinit intel_sst_probe(struct pci_dev *pci,
if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) { if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) {
ret = misc_register(&lpe_dev); ret = misc_register(&lpe_dev);
if (ret) { if (ret) {
pr_err("couldn't register misc driver\n"); pr_err("couldn't register LPE device\n");
goto do_free_misc; 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; sst_drv_ctx->lpe_stalled = 0;
pm_runtime_set_active(&pci->dev); pm_runtime_set_active(&pci->dev);
...@@ -374,16 +390,17 @@ static void __devexit intel_sst_remove(struct pci_dev *pci) ...@@ -374,16 +390,17 @@ static void __devexit intel_sst_remove(struct pci_dev *pci)
sst_drv_ctx->sst_state = SST_UN_INIT; sst_drv_ctx->sst_state = SST_UN_INIT;
mutex_unlock(&sst_drv_ctx->sst_lock); mutex_unlock(&sst_drv_ctx->sst_lock);
misc_deregister(&lpe_ctrl); 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); free_irq(pci->irq, sst_drv_ctx);
iounmap(sst_drv_ctx->dram); iounmap(sst_drv_ctx->dram);
iounmap(sst_drv_ctx->iram); iounmap(sst_drv_ctx->iram);
iounmap(sst_drv_ctx->mailbox); iounmap(sst_drv_ctx->mailbox);
iounmap(sst_drv_ctx->shim); iounmap(sst_drv_ctx->shim);
sst_drv_ctx->pmic_state = SND_MAD_UN_INIT; 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); kfree(sst_drv_ctx->mmap_mem);
} else
kfree(sst_drv_ctx->fw_cntx);
flush_scheduled_work(); flush_scheduled_work();
destroy_workqueue(sst_drv_ctx->process_reply_wq); destroy_workqueue(sst_drv_ctx->process_reply_wq);
destroy_workqueue(sst_drv_ctx->process_msg_wq); destroy_workqueue(sst_drv_ctx->process_msg_wq);
...@@ -398,6 +415,46 @@ static void __devexit intel_sst_remove(struct pci_dev *pci) ...@@ -398,6 +415,46 @@ static void __devexit intel_sst_remove(struct pci_dev *pci)
pci_set_drvdata(pci, NULL); 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 */ /* Power Management */
/* /*
* intel_sst_suspend - PCI suspend function * intel_sst_suspend - PCI suspend function
...@@ -417,6 +474,8 @@ int intel_sst_suspend(struct pci_dev *pci, pm_message_t state) ...@@ -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"); pr_err("active streams,not able to suspend\n");
return -EBUSY; return -EBUSY;
} }
/*save fw context*/
sst_save_dsp_context();
/*Assert RESET on LPE Processor*/ /*Assert RESET on LPE Processor*/
csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
csr.full = csr.full | 0x2; csr.full = csr.full | 0x2;
......
...@@ -28,8 +28,8 @@ ...@@ -28,8 +28,8 @@
* Common private declarations for SST * Common private declarations for SST
*/ */
#define SST_DRIVER_VERSION "1.2.09" #define SST_DRIVER_VERSION "1.2.11"
#define SST_VERSION_NUM 0x1209 #define SST_VERSION_NUM 0x1211
/* driver names */ /* driver names */
#define SST_DRV_NAME "intel_sst_driver" #define SST_DRV_NAME "intel_sst_driver"
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#define SST_MFLD_PCI_ID 0x082F #define SST_MFLD_PCI_ID 0x082F
#define PCI_ID_LENGTH 4 #define PCI_ID_LENGTH 4
#define SST_SUSPEND_DELAY 2000 #define SST_SUSPEND_DELAY 2000
#define FW_CONTEXT_MEM (64*1024)
enum sst_states { enum sst_states {
SST_FW_LOADED = 1, SST_FW_LOADED = 1,
...@@ -94,7 +95,7 @@ enum sst_ram_type { ...@@ -94,7 +95,7 @@ enum sst_ram_type {
/* SST shim registers to structure mapping */ /* SST shim registers to structure mapping */
union config_status_reg { union config_status_reg {
struct { struct {
u32 rsvd0:1; u32 mfld_strb:1;
u32 sst_reset:1; u32 sst_reset:1;
u32 hw_rsvd:3; u32 hw_rsvd:3;
u32 sst_clk:2; u32 sst_clk:2;
...@@ -417,6 +418,8 @@ struct intel_sst_drv { ...@@ -417,6 +418,8 @@ struct intel_sst_drv {
unsigned int audio_start; unsigned int audio_start;
dev_t devt_d, devt_c; dev_t devt_d, devt_c;
unsigned int max_streams; unsigned int max_streams;
unsigned int *fw_cntx;
unsigned int fw_cntx_size;
}; };
extern struct intel_sst_drv *sst_drv_ctx; extern struct intel_sst_drv *sst_drv_ctx;
......
...@@ -508,6 +508,7 @@ int register_sst_card(struct intel_sst_card_ops *card) ...@@ -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->pmic_state = SND_MAD_INIT_DONE;
sst_drv_ctx->rx_time_slot_status = 0; /*default AMIC*/ sst_drv_ctx->rx_time_slot_status = 0; /*default AMIC*/
card->pcm_control = sst_pmic_ops.pcm_control; card->pcm_control = sst_pmic_ops.pcm_control;
sst_drv_ctx->scard_ops->card_status = SND_CARD_UN_INIT;
return 0; return 0;
} else { } else {
pr_err("strcmp fail %s\n", card->module_name); pr_err("strcmp fail %s\n", card->module_name);
...@@ -519,6 +520,7 @@ int register_sst_card(struct intel_sst_card_ops *card) ...@@ -519,6 +520,7 @@ int register_sst_card(struct intel_sst_card_ops *card)
pr_err("Repeat for registration..denied\n"); pr_err("Repeat for registration..denied\n");
return -EBADRQC; return -EBADRQC;
} }
sst_drv_ctx->scard_ops->card_status = SND_CARD_UN_INIT;
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(register_sst_card); EXPORT_SYMBOL_GPL(register_sst_card);
......
...@@ -73,7 +73,8 @@ static int intel_sst_reset_dsp_medfield(void) ...@@ -73,7 +73,8 @@ static int intel_sst_reset_dsp_medfield(void)
union config_status_reg csr; union config_status_reg csr;
pr_debug("Resetting the DSP in medfield\n"); 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); sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);
return 0; return 0;
...@@ -109,11 +110,16 @@ static int sst_start_medfield(void) ...@@ -109,11 +110,16 @@ static int sst_start_medfield(void)
{ {
union config_status_reg csr; 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); 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); 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); sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);
pr_debug("Starting the DSP_medfld\n"); pr_debug("Starting the DSP_medfld\n");
......
...@@ -56,6 +56,8 @@ ...@@ -56,6 +56,8 @@
#define IPC_IA_GET_FW_VERSION 0x04 #define IPC_IA_GET_FW_VERSION 0x04
#define IPC_IA_GET_FW_BUILD_INF 0x05 #define IPC_IA_GET_FW_BUILD_INF 0x05
#define IPC_IA_GET_FW_INFO 0x06 #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 */ /* I2L Codec Config/control msgs */
#define IPC_IA_SET_CODEC_PARAMS 0x10 #define IPC_IA_SET_CODEC_PARAMS 0x10
...@@ -406,4 +408,8 @@ struct ipc_post { ...@@ -406,4 +408,8 @@ struct ipc_post {
char *mailbox_data; 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__ */ #endif /* __INTEL_SST_FW_IPC_H__ */
...@@ -154,6 +154,37 @@ void sst_clear_interrupt(void) ...@@ -154,6 +154,37 @@ void sst_clear_interrupt(void)
sst_shim_write(sst_drv_ctx->shim, SST_IMRX, imr.full); sst_shim_write(sst_drv_ctx->shim, SST_IMRX, imr.full);
} }
void sst_restore_fw_context(void)
{
struct snd_sst_ctxt_params fw_context;
struct ipc_post *msg = NULL;
pr_debug("restore_fw_context\n");
/*check cpu type*/
if (sst_drv_ctx->pci_id != SST_MFLD_PCI_ID)
return;
/*not supported for rest*/
if (!sst_drv_ctx->fw_cntx_size)
return;
/*nothing to restore*/
pr_debug("restoring context......\n");
/*send msg to fw*/
if (sst_create_large_msg(&msg))
return;
sst_fill_header(&msg->header, IPC_IA_SET_FW_CTXT, 1, 0);
msg->header.part.data = sizeof(fw_context) + sizeof(u32);
fw_context.address = virt_to_phys((void *)sst_drv_ctx->fw_cntx);
fw_context.size = sst_drv_ctx->fw_cntx_size;
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);
return;
}
/* /*
* process_fw_init - process the FW init msg * process_fw_init - process the FW init msg
* *
...@@ -184,13 +215,13 @@ int process_fw_init(struct sst_ipc_msg_wq *msg) ...@@ -184,13 +215,13 @@ int process_fw_init(struct sst_ipc_msg_wq *msg)
sst_drv_ctx->sst_state = SST_FW_RUNNING; sst_drv_ctx->sst_state = SST_FW_RUNNING;
sst_drv_ctx->lpe_stalled = 0; sst_drv_ctx->lpe_stalled = 0;
mutex_unlock(&sst_drv_ctx->sst_lock); mutex_unlock(&sst_drv_ctx->sst_lock);
pr_debug("FW Version %x.%x\n", pr_debug("FW Version %02x.%02x.%02x\n", init->fw_version.major,
init->fw_version.major, init->fw_version.minor); init->fw_version.minor, init->fw_version.build);
pr_debug("Build No %x Type %x\n", pr_debug("Build Type %x\n", init->fw_version.type);
init->fw_version.build, init->fw_version.type);
pr_debug(" Build date %s Time %s\n", pr_debug(" Build date %s Time %s\n",
init->build_info.date, init->build_info.time); init->build_info.date, init->build_info.time);
sst_wake_up_alloc_block(sst_drv_ctx, FW_DWNL_ID, retval, NULL); sst_wake_up_alloc_block(sst_drv_ctx, FW_DWNL_ID, retval, NULL);
sst_restore_fw_context();
return retval; return retval;
} }
/** /**
...@@ -615,12 +646,18 @@ void sst_process_reply(struct work_struct *work) ...@@ -615,12 +646,18 @@ void sst_process_reply(struct work_struct *work)
break; break;
case IPC_IA_FREE_STREAM: case IPC_IA_FREE_STREAM:
str_info = &sst_drv_ctx->streams[str_id];
if (!msg->header.part.data) { if (!msg->header.part.data) {
pr_debug("Stream %d freed\n", str_id); pr_debug("Stream %d freed\n", str_id);
} else { } else {
pr_err("Free for %d ret error %x\n", pr_err("Free for %d ret error %x\n",
str_id, msg->header.part.data); str_id, msg->header.part.data);
} }
if (str_info->ctrl_blk.on == true) {
str_info->ctrl_blk.on = false;
str_info->ctrl_blk.condition = true;
wake_up(&sst_drv_ctx->wait_queue);
}
break; break;
case IPC_IA_ALLOC_STREAM: { case IPC_IA_ALLOC_STREAM: {
/* map to stream, call play */ /* map to stream, call play */
...@@ -699,6 +736,17 @@ void sst_process_reply(struct work_struct *work) ...@@ -699,6 +736,17 @@ void sst_process_reply(struct work_struct *work)
case IPC_IA_START_STREAM: case IPC_IA_START_STREAM:
pr_debug("reply for START STREAM %x\n", msg->header.full); pr_debug("reply for START STREAM %x\n", msg->header.full);
break; break;
case IPC_IA_GET_FW_CTXT:
pr_debug("reply for get fw ctxt %x\n", msg->header.full);
if (msg->header.part.data)
sst_drv_ctx->fw_cntx_size = 0;
else
sst_drv_ctx->fw_cntx_size = *sst_drv_ctx->fw_cntx;
pr_debug("fw copied data %x\n", sst_drv_ctx->fw_cntx_size);
sst_wake_up_alloc_block(
sst_drv_ctx, str_id, msg->header.part.data, NULL);
break;
default: default:
/* Illegal case */ /* Illegal case */
pr_err("process reply:default = %x\n", msg->header.full); pr_err("process reply:default = %x\n", msg->header.full);
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/firmware.h> #include <linux/firmware.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/delay.h>
#include "intel_sst_ioctl.h" #include "intel_sst_ioctl.h"
#include "intel_sst.h" #include "intel_sst.h"
#include "intel_sst_fw_ipc.h" #include "intel_sst_fw_ipc.h"
...@@ -519,10 +520,6 @@ int sst_drain_stream(int str_id) ...@@ -519,10 +520,6 @@ int sst_drain_stream(int str_id)
str_info->data_blk.on = true; str_info->data_blk.on = true;
retval = sst_wait_interruptible(sst_drv_ctx, &str_info->data_blk); retval = sst_wait_interruptible(sst_drv_ctx, &str_info->data_blk);
str_info->need_draining = false; str_info->need_draining = false;
if (retval == -SST_ERR_INVALID_STREAM_ID) {
retval = -EINVAL;
sst_clean_stream(str_info);
}
return retval; return retval;
} }
...@@ -563,6 +560,12 @@ int sst_free_stream(int str_id) ...@@ -563,6 +560,12 @@ int sst_free_stream(int str_id)
str_info->data_blk.ret_code = 0; str_info->data_blk.ret_code = 0;
wake_up(&sst_drv_ctx->wait_queue); wake_up(&sst_drv_ctx->wait_queue);
} }
str_info->data_blk.on = true;
str_info->data_blk.condition = false;
retval = sst_wait_interruptible_timeout(sst_drv_ctx,
&str_info->ctrl_blk, SST_BLOCK_TIMEOUT);
pr_debug("wait for free returned %d\n", retval);
msleep(100);
mutex_lock(&sst_drv_ctx->stream_lock); mutex_lock(&sst_drv_ctx->stream_lock);
sst_clean_stream(str_info); sst_clean_stream(str_info);
mutex_unlock(&sst_drv_ctx->stream_lock); mutex_unlock(&sst_drv_ctx->stream_lock);
......
...@@ -363,7 +363,6 @@ int sst_parse_target(struct snd_sst_slot_info *slot) ...@@ -363,7 +363,6 @@ int sst_parse_target(struct snd_sst_slot_info *slot)
pr_err("SST_Activate_target_fail\n"); pr_err("SST_Activate_target_fail\n");
else else
pr_err("SST_Activate_target_pass\n"); pr_err("SST_Activate_target_pass\n");
return retval;
} else if (slot->action == SND_SST_PORT_PREPARE && } else if (slot->action == SND_SST_PORT_PREPARE &&
slot->device_type == SND_SST_DEVICE_PCM) { slot->device_type == SND_SST_DEVICE_PCM) {
retval = sst_prepare_target(slot); retval = sst_prepare_target(slot);
...@@ -371,12 +370,11 @@ int sst_parse_target(struct snd_sst_slot_info *slot) ...@@ -371,12 +370,11 @@ int sst_parse_target(struct snd_sst_slot_info *slot)
pr_err("SST_prepare_target_fail\n"); pr_err("SST_prepare_target_fail\n");
else else
pr_err("SST_prepare_target_pass\n"); pr_err("SST_prepare_target_pass\n");
return retval;
} else { } else {
pr_err("slot_action : %d, device_type: %d\n", pr_err("slot_action : %d, device_type: %d\n",
slot->action, slot->device_type); slot->action, slot->device_type);
return retval;
} }
return retval;
} }
int sst_send_target(struct snd_sst_target_device *target) int sst_send_target(struct snd_sst_target_device *target)
...@@ -886,8 +884,7 @@ static int sst_prepare_input_buffers_rar(struct stream_info *str_info, ...@@ -886,8 +884,7 @@ static int sst_prepare_input_buffers_rar(struct stream_info *str_info,
int *input_index, int *in_copied, int *input_index, int *in_copied,
int *input_index_valid_size, int *new_entry_flag) int *input_index_valid_size, int *new_entry_flag)
{ {
int retval = 0; int retval = 0, i;
int i;
if (str_info->ops == STREAM_OPS_PLAYBACK_DRM) { if (str_info->ops == STREAM_OPS_PLAYBACK_DRM) {
struct RAR_buffer rar_buffers; struct RAR_buffer rar_buffers;
...@@ -924,7 +921,6 @@ static int sst_prepare_input_buffers_rar(struct stream_info *str_info, ...@@ -924,7 +921,6 @@ static int sst_prepare_input_buffers_rar(struct stream_info *str_info,
return retval; return retval;
} }
#endif #endif
/*This function is used to prepare the kernel input buffers with contents /*This function is used to prepare the kernel input buffers with contents
before sending for decode*/ before sending for decode*/
static int sst_prepare_input_buffers(struct stream_info *str_info, static int sst_prepare_input_buffers(struct stream_info *str_info,
......
...@@ -802,8 +802,6 @@ static int __devinit snd_intelmad_sst_register( ...@@ -802,8 +802,6 @@ static int __devinit snd_intelmad_sst_register(
pr_err("sst card registration failed\n"); pr_err("sst card registration failed\n");
return ret_val; return ret_val;
} }
sst_drv_ctx->scard_ops->card_status = SND_CARD_UN_INIT;
sst_card_vendor_id = intelmaddata->sstdrv_ops->vendor_id; sst_card_vendor_id = intelmaddata->sstdrv_ops->vendor_id;
intelmaddata->pmic_status = PMIC_UNINIT; intelmaddata->pmic_status = PMIC_UNINIT;
return ret_val; return ret_val;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment