summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sound/soc/codecs/wcd_cpe_core.c37
-rw-r--r--sound/soc/codecs/wcd_cpe_services.c101
2 files changed, 102 insertions, 36 deletions
diff --git a/sound/soc/codecs/wcd_cpe_core.c b/sound/soc/codecs/wcd_cpe_core.c
index 0c355b74d96a..ac1b613248fd 100644
--- a/sound/soc/codecs/wcd_cpe_core.c
+++ b/sound/soc/codecs/wcd_cpe_core.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -823,6 +823,15 @@ static int wcd_cpe_enable(struct wcd_cpe_core *core,
int ret = 0;
if (enable) {
+ /* Reset CPE first */
+ ret = cpe_svc_reset(core->cpe_handle);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(core->dev,
+ "%s: CPE Reset failed, error = %d\n",
+ __func__, ret);
+ goto done;
+ }
+
ret = wcd_cpe_setup_irqs(core);
if (ret) {
dev_err(core->dev,
@@ -892,11 +901,10 @@ static int wcd_cpe_enable(struct wcd_cpe_core *core,
goto done;
}
- /* Reset CPE first */
- ret = cpe_svc_reset(core->cpe_handle);
+ ret = cpe_svc_shutdown(core->cpe_handle);
if (IS_ERR_VALUE(ret)) {
dev_err(core->dev,
- "%s: Failed to reset CPE with error %d\n",
+ "%s: CPE shutdown failed, error %d\n",
__func__, ret);
goto done;
}
@@ -1378,6 +1386,15 @@ static void wcd_cpe_svc_event_cb(const struct cpe_svc_notification *param)
complete(&core->online_compl);
break;
case CPE_SVC_OFFLINE:
+ /*
+ * offline can happen during normal shutdown,
+ * but we are interested in offline only during
+ * SSR.
+ */
+ if (core->ssr_type != WCD_CPE_SSR_EVENT &&
+ core->ssr_type != WCD_CPE_BUS_DOWN_EVENT)
+ break;
+
active_sessions = wcd_cpe_lsm_session_active();
wcd_cpe_change_online_state(core, 0);
complete(&core->offline_compl);
@@ -3677,18 +3694,18 @@ static int wcd_cpe_dealloc_lsm_session(void *core_handle,
lsm_sessions[session->id] = NULL;
kfree(session);
- ret = wcd_cpe_vote(core, false);
- if (ret)
- dev_dbg(core->dev,
- "%s: Failed to un-vote cpe, err = %d\n",
- __func__, ret);
-
if (!wcd_cpe_lsm_session_active()) {
cmi_deregister(core->cmi_afe_handle);
core->cmi_afe_handle = NULL;
wcd_cpe_deinitialize_afe_port_data();
}
+ ret = wcd_cpe_vote(core, false);
+ if (ret)
+ dev_dbg(core->dev,
+ "%s: Failed to un-vote cpe, err = %d\n",
+ __func__, ret);
+
return ret;
}
diff --git a/sound/soc/codecs/wcd_cpe_services.c b/sound/soc/codecs/wcd_cpe_services.c
index f100bf19ad92..d4dbf02d15d1 100644
--- a/sound/soc/codecs/wcd_cpe_services.c
+++ b/sound/soc/codecs/wcd_cpe_services.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -162,6 +162,8 @@ struct cpe_info {
struct list_head main_queue;
struct completion cmd_complete;
void *thread_handler;
+ bool stop_thread;
+ struct mutex msg_lock;
enum cpe_state state;
enum cpe_substate substate;
struct list_head client_list;
@@ -284,6 +286,8 @@ struct cpe_priv {
static struct cpe_priv cpe_d;
+static enum cpe_svc_result __cpe_svc_shutdown(void *cpe_handle);
+
static enum cpe_svc_result cpe_is_command_valid(
const struct cpe_info *t_info,
enum cpe_command command);
@@ -343,6 +347,8 @@ static bool cpe_register_read_autoinc_supported(void)
return true;
}
+
+/* Called under msgq locked context */
static void cpe_cmd_received(struct cpe_info *t_info)
{
struct cpe_command_node *node = NULL;
@@ -388,10 +394,22 @@ static int cpe_worker_thread(void *context)
while (!kthread_should_stop()) {
wait_for_completion(&t_info->cmd_complete);
+
+ CPE_SVC_GRAB_LOCK(&t_info->msg_lock, "msg_lock");
cpe_cmd_received(t_info);
reinit_completion(&t_info->cmd_complete);
+ if (t_info->stop_thread)
+ goto unlock_and_exit;
+ CPE_SVC_REL_LOCK(&t_info->msg_lock, "msg_lock");
};
+ pr_debug("%s: thread exited\n", __func__);
+ return 0;
+
+unlock_and_exit:
+ pr_debug("%s: thread stopped\n", __func__);
+ CPE_SVC_REL_LOCK(&t_info->msg_lock, "msg_lock");
+
return 0;
}
@@ -399,14 +417,30 @@ static void cpe_create_worker_thread(struct cpe_info *t_info)
{
INIT_LIST_HEAD(&t_info->main_queue);
init_completion(&t_info->cmd_complete);
+ t_info->stop_thread = false;
t_info->thread_handler = kthread_run(cpe_worker_thread,
(void *)t_info, "cpe-worker-thread");
+ pr_debug("%s: Created new worker thread\n",
+ __func__);
}
static void cpe_cleanup_worker_thread(struct cpe_info *t_info)
{
- if (t_info->thread_handler != NULL)
- kthread_stop(t_info->thread_handler);
+ if (!t_info->thread_handler) {
+ pr_err("%s: thread not created\n", __func__);
+ return;
+ }
+
+ /*
+ * Wake up the command handler in case
+ * it is waiting for an command to be processed.
+ */
+ CPE_SVC_GRAB_LOCK(&t_info->msg_lock, "msg_lock");
+ t_info->stop_thread = true;
+ complete(&t_info->cmd_complete);
+ CPE_SVC_REL_LOCK(&t_info->msg_lock, "msg_lock");
+
+ kthread_stop(t_info->thread_handler);
t_info->thread_handler = NULL;
}
@@ -436,14 +470,17 @@ cpe_send_cmd_to_thread(struct cpe_info *t_info,
cmd->command = command;
cmd->data = data;
+
+ CPE_SVC_GRAB_LOCK(&t_info->msg_lock, "msg_lock");
if (high_prio)
list_add(&(cmd->list),
&(t_info->main_queue));
else
list_add_tail(&(cmd->list),
&(t_info->main_queue));
-
complete(&t_info->cmd_complete);
+ CPE_SVC_REL_LOCK(&t_info->msg_lock, "msg_lock");
+
return rc;
}
@@ -825,9 +862,7 @@ static void cpe_process_irq_int(u32 irq,
case CPE_IRQ_WDOG_BITE:
case CPE_IRQ_RCO_WDOG_INT:
err_irq = true;
- CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
- cpe_svc_shutdown(t_info);
- CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+ __cpe_svc_shutdown(t_info);
break;
case CPE_IRQ_FLL_LOCK_LOST:
@@ -1222,17 +1257,6 @@ static enum cpe_process_result cpe_process_kill_thread(
{
struct cpe_svc_notification payload;
- if (t_info->pending) {
- struct cpe_send_msg *m =
- (struct cpe_send_msg *)t_info->pending;
- cpe_notify_cmi_client(t_info, m->payload,
- CPE_SVC_SHUTTING_DOWN);
- kfree(t_info->pending);
- t_info->pending = NULL;
- }
-
- cpe_command_cleanup(command_node);
- kfree(command_node);
cpe_d.cpe_msg_buffer = 0;
payload.result = CPE_SVC_SHUTTING_DOWN;
payload.event = CPE_SVC_OFFLINE;
@@ -1245,7 +1269,6 @@ static enum cpe_process_result cpe_process_kill_thread(
cpe_change_state(t_info, CPE_STATE_OFFLINE,
CPE_SS_IDLE);
cpe_broadcast_notification(t_info, &payload);
- cpe_cleanup_worker_thread(t_info);
return CPE_PROC_KILLED;
}
@@ -1562,6 +1585,7 @@ void *cpe_svc_initialize(
memset(t_info->tgt->outbox, 0, cap->outbox_size);
memset(t_info->tgt->inbox, 0, cap->inbox_size);
+ mutex_init(&t_info->msg_lock);
cpe_d.cpe_irq_control_callback = irq_control_callback;
t_info->cpe_process_command = cpe_mt_process_cmd;
t_info->cpe_cmd_validate = cpe_mt_validate_cmd;
@@ -1607,6 +1631,7 @@ enum cpe_svc_result cpe_svc_deinitialize(void *cpe_handle)
t_info->tgt->tgt_deinit(t_info->tgt);
cpe_change_state(t_info, CPE_STATE_UNINITIALIZED,
CPE_SS_IDLE);
+ mutex_destroy(&t_info->msg_lock);
kfree(t_info->tgt);
kfree(t_info);
mutex_destroy(&cpe_d.cpe_api_mutex);
@@ -1755,13 +1780,13 @@ enum cpe_svc_result cpe_svc_route_notification(void *cpe_handle,
return rc;
}
-enum cpe_svc_result cpe_svc_shutdown(void *cpe_handle)
+static enum cpe_svc_result __cpe_svc_shutdown(void *cpe_handle)
{
enum cpe_svc_result rc = CPE_SVC_SUCCESS;
struct cpe_info *t_info = (struct cpe_info *)cpe_handle;
struct cpe_command_node *n = NULL;
+ struct cpe_command_node kill_cmd;
- CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
if (!t_info)
t_info = cpe_d.cpe_default_handle;
@@ -1770,7 +1795,6 @@ enum cpe_svc_result cpe_svc_shutdown(void *cpe_handle)
if (rc != CPE_SVC_SUCCESS) {
pr_err("%s: cmd validation fail, cmd = %d\n",
__func__, CPE_CMD_SHUTDOWN);
- CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
return rc;
}
@@ -1782,7 +1806,11 @@ enum cpe_svc_result cpe_svc_shutdown(void *cpe_handle)
cpe_notify_cmi_client(t_info, (u8 *)n->data,
CPE_SVC_SHUTTING_DOWN);
}
-
+ /*
+ * Since command cannot be processed,
+ * delete it from the list and perform cleanup
+ */
+ list_del(&n->list);
cpe_command_cleanup(n);
kfree(n);
}
@@ -1792,10 +1820,31 @@ enum cpe_svc_result cpe_svc_shutdown(void *cpe_handle)
t_info->state = CPE_STATE_OFFLINE;
t_info->substate = CPE_SS_IDLE;
- rc = cpe_send_cmd_to_thread(t_info, CPE_CMD_KILL_THREAD,
- NULL, true);
- CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+ memset(&kill_cmd, 0, sizeof(kill_cmd));
+ kill_cmd.command = CPE_CMD_KILL_THREAD;
+ if (t_info->pending) {
+ struct cpe_send_msg *m =
+ (struct cpe_send_msg *)t_info->pending;
+ cpe_notify_cmi_client(t_info, m->payload,
+ CPE_SVC_SHUTTING_DOWN);
+ kfree(t_info->pending);
+ t_info->pending = NULL;
+ }
+
+ cpe_cleanup_worker_thread(t_info);
+ t_info->cpe_process_command(&kill_cmd);
+
+ return rc;
+}
+
+enum cpe_svc_result cpe_svc_shutdown(void *cpe_handle)
+{
+ enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+
+ CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+ rc = __cpe_svc_shutdown(cpe_handle);
+ CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
return rc;
}