Merge "ASoC: wcd934x-dsp-cntl: add support for subsystem restart"
This commit is contained in:
commit
a0e79f8e03
3 changed files with 170 additions and 22 deletions
|
@ -639,12 +639,10 @@ static int wcd_spi_init(struct spi_device *spi)
|
||||||
WCD_SPI_SLAVE_TRNS_LEN,
|
WCD_SPI_SLAVE_TRNS_LEN,
|
||||||
0xFFFF0000,
|
0xFFFF0000,
|
||||||
(WCD_SPI_RW_MULTI_MAX_LEN / 4) << 16);
|
(WCD_SPI_RW_MULTI_MAX_LEN / 4) << 16);
|
||||||
done:
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
err_wr_en:
|
err_wr_en:
|
||||||
wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_DISABLE,
|
wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_DISABLE,
|
||||||
WCD_SPI_CLK_FLAG_IMMEDIATE);
|
WCD_SPI_CLK_FLAG_IMMEDIATE);
|
||||||
|
done:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -845,6 +843,7 @@ static int wdsp_spi_event_handler(struct device *dev, void *priv_data,
|
||||||
__func__, event);
|
__func__, event);
|
||||||
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
|
case WDSP_EVENT_PRE_DLOAD_CODE:
|
||||||
case WDSP_EVENT_PRE_DLOAD_DATA:
|
case WDSP_EVENT_PRE_DLOAD_DATA:
|
||||||
ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_ENABLE,
|
ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_ENABLE,
|
||||||
WCD_SPI_CLK_FLAG_IMMEDIATE);
|
WCD_SPI_CLK_FLAG_IMMEDIATE);
|
||||||
|
|
|
@ -28,21 +28,22 @@
|
||||||
#define WCD_MEM_ENABLE_MAX_RETRIES 20
|
#define WCD_MEM_ENABLE_MAX_RETRIES 20
|
||||||
#define WCD_DSP_BOOT_TIMEOUT_MS 3000
|
#define WCD_DSP_BOOT_TIMEOUT_MS 3000
|
||||||
#define WCD_SYSFS_ENTRY_MAX_LEN 8
|
#define WCD_SYSFS_ENTRY_MAX_LEN 8
|
||||||
|
#define WCD_PROCFS_ENTRY_MAX_LEN 16
|
||||||
#define WCD_934X_RAMDUMP_START_ADDR 0x20100000
|
#define WCD_934X_RAMDUMP_START_ADDR 0x20100000
|
||||||
#define WCD_934X_RAMDUMP_SIZE ((1024 * 1024) - 128)
|
#define WCD_934X_RAMDUMP_SIZE ((1024 * 1024) - 128)
|
||||||
|
|
||||||
#define WCD_CNTL_MUTEX_LOCK(codec, lock) \
|
#define WCD_CNTL_MUTEX_LOCK(codec, lock) \
|
||||||
{ \
|
{ \
|
||||||
dev_dbg(codec->dev, "mutex_lock(%s)\n", \
|
dev_dbg(codec->dev, "%s: mutex_lock(%s)\n", \
|
||||||
__func__); \
|
__func__, __stringify_1(lock)); \
|
||||||
mutex_lock(&lock); \
|
mutex_lock(&lock); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define WCD_CNTL_MUTEX_UNLOCK(codec, lock) \
|
#define WCD_CNTL_MUTEX_UNLOCK(codec, lock) \
|
||||||
{ \
|
{ \
|
||||||
dev_dbg(codec->dev, "mutex_unlock(%s)\n",\
|
dev_dbg(codec->dev, "%s: mutex_unlock(%s)\n", \
|
||||||
__func__); \
|
__func__, __stringify_1(lock)); \
|
||||||
mutex_unlock(&lock); \
|
mutex_unlock(&lock); \
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wcd_cntl_attribute {
|
struct wcd_cntl_attribute {
|
||||||
|
@ -149,6 +150,97 @@ static struct kobj_type wcd_cntl_ktype = {
|
||||||
.sysfs_ops = &wcd_cntl_sysfs_ops,
|
.sysfs_ops = &wcd_cntl_sysfs_ops,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void wcd_cntl_change_online_state(struct wcd_dsp_cntl *cntl,
|
||||||
|
u8 online)
|
||||||
|
{
|
||||||
|
struct wdsp_ssr_entry *ssr_entry = &cntl->ssr_entry;
|
||||||
|
unsigned long ret;
|
||||||
|
|
||||||
|
WCD_CNTL_MUTEX_LOCK(cntl->codec, cntl->ssr_mutex);
|
||||||
|
ssr_entry->offline = !online;
|
||||||
|
/* Make sure the write is complete */
|
||||||
|
wmb();
|
||||||
|
ret = xchg(&ssr_entry->offline_change, 1);
|
||||||
|
wake_up_interruptible(&ssr_entry->offline_poll_wait);
|
||||||
|
dev_dbg(cntl->codec->dev,
|
||||||
|
"%s: requested %u, offline %u offline_change %u, ret = %ldn",
|
||||||
|
__func__, online, ssr_entry->offline,
|
||||||
|
ssr_entry->offline_change, ret);
|
||||||
|
WCD_CNTL_MUTEX_UNLOCK(cntl->codec, cntl->ssr_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t wdsp_ssr_entry_read(struct snd_info_entry *entry,
|
||||||
|
void *file_priv_data, struct file *file,
|
||||||
|
char __user *buf, size_t count, loff_t pos)
|
||||||
|
{
|
||||||
|
int len = 0;
|
||||||
|
char buffer[WCD_PROCFS_ENTRY_MAX_LEN];
|
||||||
|
struct wcd_dsp_cntl *cntl;
|
||||||
|
struct wdsp_ssr_entry *ssr_entry;
|
||||||
|
ssize_t ret;
|
||||||
|
u8 offline;
|
||||||
|
|
||||||
|
cntl = (struct wcd_dsp_cntl *) entry->private_data;
|
||||||
|
if (!cntl) {
|
||||||
|
pr_err("%s: Invalid private data for SSR procfs entry\n",
|
||||||
|
__func__);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssr_entry = &cntl->ssr_entry;
|
||||||
|
|
||||||
|
WCD_CNTL_MUTEX_LOCK(cntl->codec, cntl->ssr_mutex);
|
||||||
|
offline = ssr_entry->offline;
|
||||||
|
/* Make sure the read is complete */
|
||||||
|
rmb();
|
||||||
|
dev_dbg(cntl->codec->dev, "%s: offline = %s\n", __func__,
|
||||||
|
offline ? "true" : "false");
|
||||||
|
len = snprintf(buffer, sizeof(buffer), "%s\n",
|
||||||
|
offline ? "OFFLINE" : "ONLINE");
|
||||||
|
ret = simple_read_from_buffer(buf, count, &pos, buffer, len);
|
||||||
|
WCD_CNTL_MUTEX_UNLOCK(cntl->codec, cntl->ssr_mutex);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int wdsp_ssr_entry_poll(struct snd_info_entry *entry,
|
||||||
|
void *private_data, struct file *file,
|
||||||
|
poll_table *wait)
|
||||||
|
{
|
||||||
|
struct wcd_dsp_cntl *cntl;
|
||||||
|
struct wdsp_ssr_entry *ssr_entry;
|
||||||
|
unsigned int ret = 0;
|
||||||
|
|
||||||
|
if (!entry || !entry->private_data) {
|
||||||
|
pr_err("%s: %s is NULL\n", __func__,
|
||||||
|
(!entry) ? "entry" : "private_data");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cntl = (struct wcd_dsp_cntl *) entry->private_data;
|
||||||
|
ssr_entry = &cntl->ssr_entry;
|
||||||
|
|
||||||
|
dev_dbg(cntl->codec->dev, "%s: Poll wait, offline = %u\n",
|
||||||
|
__func__, ssr_entry->offline);
|
||||||
|
poll_wait(file, &ssr_entry->offline_poll_wait, wait);
|
||||||
|
dev_dbg(cntl->codec->dev, "%s: Woken up Poll wait, offline = %u\n",
|
||||||
|
__func__, ssr_entry->offline);
|
||||||
|
|
||||||
|
WCD_CNTL_MUTEX_LOCK(cntl->codec, cntl->ssr_mutex);
|
||||||
|
if (xchg(&ssr_entry->offline_change, 0))
|
||||||
|
ret = POLLIN | POLLPRI | POLLRDNORM;
|
||||||
|
dev_dbg(cntl->codec->dev, "%s: ret (%d) from poll_wait\n",
|
||||||
|
__func__, ret);
|
||||||
|
WCD_CNTL_MUTEX_UNLOCK(cntl->codec, cntl->ssr_mutex);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct snd_info_entry_ops wdsp_ssr_entry_ops = {
|
||||||
|
.read = wdsp_ssr_entry_read,
|
||||||
|
.poll = wdsp_ssr_entry_poll,
|
||||||
|
};
|
||||||
|
|
||||||
static int wcd_cntl_cpe_fll_calibrate(struct wcd_dsp_cntl *cntl)
|
static int wcd_cntl_cpe_fll_calibrate(struct wcd_dsp_cntl *cntl)
|
||||||
{
|
{
|
||||||
struct snd_soc_codec *codec = cntl->codec;
|
struct snd_soc_codec *codec = cntl->codec;
|
||||||
|
@ -596,6 +688,7 @@ static irqreturn_t wcd_cntl_err_irq(int irq, void *data)
|
||||||
dev_err(cntl->codec->dev,
|
dev_err(cntl->codec->dev,
|
||||||
"%s: Failed to handle fatal irq 0x%x\n",
|
"%s: Failed to handle fatal irq 0x%x\n",
|
||||||
__func__, status & cntl->irqs.fatal_irqs);
|
__func__, status & cntl->irqs.fatal_irqs);
|
||||||
|
wcd_cntl_change_online_state(cntl, 0);
|
||||||
} else {
|
} else {
|
||||||
dev_err(cntl->codec->dev, "%s: Invalid intr_handler\n",
|
dev_err(cntl->codec->dev, "%s: Invalid intr_handler\n",
|
||||||
__func__);
|
__func__);
|
||||||
|
@ -612,10 +705,15 @@ static int wcd_control_handler(struct device *dev, void *priv_data,
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
|
case WDSP_EVENT_POST_INIT:
|
||||||
case WDSP_EVENT_POST_DLOAD_CODE:
|
case WDSP_EVENT_POST_DLOAD_CODE:
|
||||||
case WDSP_EVENT_DLOAD_FAILED:
|
case WDSP_EVENT_DLOAD_FAILED:
|
||||||
case WDSP_EVENT_POST_SHUTDOWN:
|
case WDSP_EVENT_POST_SHUTDOWN:
|
||||||
|
|
||||||
|
if (event == WDSP_EVENT_POST_DLOAD_CODE)
|
||||||
|
/* Mark DSP online since code download is complete */
|
||||||
|
wcd_cntl_change_online_state(cntl, 1);
|
||||||
|
|
||||||
/* Disable CPAR */
|
/* Disable CPAR */
|
||||||
wcd_cntl_cpar_ctrl(cntl, false);
|
wcd_cntl_cpar_ctrl(cntl, false);
|
||||||
/* Disable all the clocks */
|
/* Disable all the clocks */
|
||||||
|
@ -626,12 +724,8 @@ static int wcd_control_handler(struct device *dev, void *priv_data,
|
||||||
__func__, ret);
|
__func__, ret);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WDSP_EVENT_PRE_DLOAD_CODE:
|
|
||||||
|
|
||||||
wcd_cntl_enable_memory(cntl);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case WDSP_EVENT_PRE_DLOAD_DATA:
|
case WDSP_EVENT_PRE_DLOAD_DATA:
|
||||||
|
case WDSP_EVENT_PRE_DLOAD_CODE:
|
||||||
|
|
||||||
/* Enable all the clocks */
|
/* Enable all the clocks */
|
||||||
ret = wcd_cntl_clocks_enable(cntl);
|
ret = wcd_cntl_clocks_enable(cntl);
|
||||||
|
@ -644,6 +738,9 @@ static int wcd_control_handler(struct device *dev, void *priv_data,
|
||||||
|
|
||||||
/* Enable CPAR */
|
/* Enable CPAR */
|
||||||
wcd_cntl_cpar_ctrl(cntl, true);
|
wcd_cntl_cpar_ctrl(cntl, true);
|
||||||
|
|
||||||
|
if (event == WDSP_EVENT_PRE_DLOAD_CODE)
|
||||||
|
wcd_cntl_enable_memory(cntl);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WDSP_EVENT_DO_BOOT:
|
case WDSP_EVENT_DO_BOOT:
|
||||||
|
@ -850,6 +947,10 @@ static int wcd_ctrl_component_bind(struct device *dev,
|
||||||
void *data)
|
void *data)
|
||||||
{
|
{
|
||||||
struct wcd_dsp_cntl *cntl;
|
struct wcd_dsp_cntl *cntl;
|
||||||
|
struct snd_soc_codec *codec;
|
||||||
|
struct snd_card *card;
|
||||||
|
struct snd_info_entry *entry;
|
||||||
|
char proc_name[WCD_PROCFS_ENTRY_MAX_LEN];
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (!dev || !master || !data) {
|
if (!dev || !master || !data) {
|
||||||
|
@ -867,13 +968,47 @@ static int wcd_ctrl_component_bind(struct device *dev,
|
||||||
cntl->m_dev = master;
|
cntl->m_dev = master;
|
||||||
cntl->m_ops = data;
|
cntl->m_ops = data;
|
||||||
|
|
||||||
if (cntl->m_ops->register_cmpnt_ops)
|
if (!cntl->m_ops->register_cmpnt_ops) {
|
||||||
ret = cntl->m_ops->register_cmpnt_ops(master, dev, cntl,
|
dev_err(dev, "%s: invalid master callback register_cmpnt_ops\n",
|
||||||
&control_ops);
|
__func__);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
if (ret)
|
ret = cntl->m_ops->register_cmpnt_ops(master, dev, cntl, &control_ops);
|
||||||
|
if (ret) {
|
||||||
dev_err(dev, "%s: register_cmpnt_ops failed, err = %d\n",
|
dev_err(dev, "%s: register_cmpnt_ops failed, err = %d\n",
|
||||||
__func__, ret);
|
__func__, ret);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
codec = cntl->codec;
|
||||||
|
card = codec->component.card->snd_card;
|
||||||
|
snprintf(proc_name, WCD_PROCFS_ENTRY_MAX_LEN, "%s%d%s", "cpe",
|
||||||
|
cntl->dsp_instance, "_state");
|
||||||
|
entry = snd_info_create_card_entry(card, proc_name, card->proc_root);
|
||||||
|
if (!entry) {
|
||||||
|
/* Do not treat this as Fatal error */
|
||||||
|
dev_err(dev, "%s: Failed to create procfs entry %s\n",
|
||||||
|
__func__, proc_name);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
cntl->ssr_entry.entry = entry;
|
||||||
|
cntl->ssr_entry.offline = 1;
|
||||||
|
entry->size = WCD_PROCFS_ENTRY_MAX_LEN;
|
||||||
|
entry->content = SNDRV_INFO_CONTENT_DATA;
|
||||||
|
entry->c.ops = &wdsp_ssr_entry_ops;
|
||||||
|
entry->private_data = cntl;
|
||||||
|
ret = snd_info_register(entry);
|
||||||
|
if (IS_ERR_VALUE(ret)) {
|
||||||
|
dev_err(dev, "%s: Failed to register entry %s, err = %d\n",
|
||||||
|
__func__, proc_name, ret);
|
||||||
|
snd_info_free_entry(entry);
|
||||||
|
/* Let bind still happen even if creating the entry failed */
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
done:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -952,6 +1087,8 @@ void wcd_dsp_cntl_init(struct snd_soc_codec *codec,
|
||||||
memcpy(&control->irqs, ¶ms->irqs, sizeof(control->irqs));
|
memcpy(&control->irqs, ¶ms->irqs, sizeof(control->irqs));
|
||||||
init_completion(&control->boot_complete);
|
init_completion(&control->boot_complete);
|
||||||
mutex_init(&control->clk_mutex);
|
mutex_init(&control->clk_mutex);
|
||||||
|
mutex_init(&control->ssr_mutex);
|
||||||
|
init_waitqueue_head(&control->ssr_entry.offline_poll_wait);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The default state of WDSP is in SVS mode.
|
* The default state of WDSP is in SVS mode.
|
||||||
|
@ -1004,6 +1141,7 @@ void wcd_dsp_cntl_deinit(struct wcd_dsp_cntl **cntl)
|
||||||
component_del(codec->dev, &wcd_ctrl_component_ops);
|
component_del(codec->dev, &wcd_ctrl_component_ops);
|
||||||
|
|
||||||
mutex_destroy(&control->clk_mutex);
|
mutex_destroy(&control->clk_mutex);
|
||||||
|
mutex_destroy(&control->ssr_mutex);
|
||||||
kfree(*cntl);
|
kfree(*cntl);
|
||||||
*cntl = NULL;
|
*cntl = NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,13 @@ struct wcd_dsp_params {
|
||||||
u32 dsp_instance;
|
u32 dsp_instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct wdsp_ssr_entry {
|
||||||
|
u8 offline;
|
||||||
|
u8 offline_change;
|
||||||
|
wait_queue_head_t offline_poll_wait;
|
||||||
|
struct snd_info_entry *entry;
|
||||||
|
};
|
||||||
|
|
||||||
struct wcd_dsp_cntl {
|
struct wcd_dsp_cntl {
|
||||||
/* Handle to codec */
|
/* Handle to codec */
|
||||||
struct snd_soc_codec *codec;
|
struct snd_soc_codec *codec;
|
||||||
|
@ -89,6 +96,10 @@ struct wcd_dsp_cntl {
|
||||||
|
|
||||||
/* Keep track of WDSP boot status */
|
/* Keep track of WDSP boot status */
|
||||||
bool is_wdsp_booted;
|
bool is_wdsp_booted;
|
||||||
|
|
||||||
|
/* SSR related */
|
||||||
|
struct wdsp_ssr_entry ssr_entry;
|
||||||
|
struct mutex ssr_mutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
void wcd_dsp_cntl_init(struct snd_soc_codec *codec,
|
void wcd_dsp_cntl_init(struct snd_soc_codec *codec,
|
||||||
|
|
Loading…
Add table
Reference in a new issue