aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/dream/qdsp5/audmgr.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/dream/qdsp5/audmgr.c')
-rw-r--r--drivers/staging/dream/qdsp5/audmgr.c313
1 files changed, 313 insertions, 0 deletions
diff --git a/drivers/staging/dream/qdsp5/audmgr.c b/drivers/staging/dream/qdsp5/audmgr.c
new file mode 100644
index 000000000000..1ad8b82c2570
--- /dev/null
+++ b/drivers/staging/dream/qdsp5/audmgr.c
@@ -0,0 +1,313 @@
+/* arch/arm/mach-msm/qdsp5/audmgr.c
+ *
+ * interface to "audmgr" service on the baseband cpu
+ *
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+
+#include <asm/atomic.h>
+#include <mach/msm_rpcrouter.h>
+
+#include "audmgr.h"
+
+#define STATE_CLOSED 0
+#define STATE_DISABLED 1
+#define STATE_ENABLING 2
+#define STATE_ENABLED 3
+#define STATE_DISABLING 4
+#define STATE_ERROR 5
+
+static void rpc_ack(struct msm_rpc_endpoint *ept, uint32_t xid)
+{
+ uint32_t rep[6];
+
+ rep[0] = cpu_to_be32(xid);
+ rep[1] = cpu_to_be32(1);
+ rep[2] = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED);
+ rep[3] = cpu_to_be32(RPC_ACCEPTSTAT_SUCCESS);
+ rep[4] = 0;
+ rep[5] = 0;
+
+ msm_rpc_write(ept, rep, sizeof(rep));
+}
+
+static void process_audmgr_callback(struct audmgr *am,
+ struct rpc_audmgr_cb_func_ptr *args,
+ int len)
+{
+ if (len < (sizeof(uint32_t) * 3))
+ return;
+ if (be32_to_cpu(args->set_to_one) != 1)
+ return;
+
+ switch (be32_to_cpu(args->status)) {
+ case RPC_AUDMGR_STATUS_READY:
+ if (len < sizeof(uint32_t) * 4)
+ break;
+ am->handle = be32_to_cpu(args->u.handle);
+ pr_info("audmgr: rpc READY handle=0x%08x\n", am->handle);
+ break;
+ case RPC_AUDMGR_STATUS_CODEC_CONFIG: {
+ uint32_t volume;
+ if (len < sizeof(uint32_t) * 4)
+ break;
+ volume = be32_to_cpu(args->u.volume);
+ pr_info("audmgr: rpc CODEC_CONFIG volume=0x%08x\n", volume);
+ am->state = STATE_ENABLED;
+ wake_up(&am->wait);
+ break;
+ }
+ case RPC_AUDMGR_STATUS_PENDING:
+ pr_err("audmgr: PENDING?\n");
+ break;
+ case RPC_AUDMGR_STATUS_SUSPEND:
+ pr_err("audmgr: SUSPEND?\n");
+ break;
+ case RPC_AUDMGR_STATUS_FAILURE:
+ pr_err("audmgr: FAILURE\n");
+ break;
+ case RPC_AUDMGR_STATUS_VOLUME_CHANGE:
+ pr_err("audmgr: VOLUME_CHANGE?\n");
+ break;
+ case RPC_AUDMGR_STATUS_DISABLED:
+ pr_err("audmgr: DISABLED\n");
+ am->state = STATE_DISABLED;
+ wake_up(&am->wait);
+ break;
+ case RPC_AUDMGR_STATUS_ERROR:
+ pr_err("audmgr: ERROR?\n");
+ am->state = STATE_ERROR;
+ wake_up(&am->wait);
+ break;
+ default:
+ break;
+ }
+}
+
+static void process_rpc_request(uint32_t proc, uint32_t xid,
+ void *data, int len, void *private)
+{
+ struct audmgr *am = private;
+ uint32_t *x = data;
+
+ if (0) {
+ int n = len / 4;
+ pr_info("rpc_call proc %d:", proc);
+ while (n--)
+ printk(" %08x", be32_to_cpu(*x++));
+ printk("\n");
+ }
+
+ if (proc == AUDMGR_CB_FUNC_PTR)
+ process_audmgr_callback(am, data, len);
+ else
+ pr_err("audmgr: unknown rpc proc %d\n", proc);
+ rpc_ack(am->ept, xid);
+}
+
+#define RPC_TYPE_REQUEST 0
+#define RPC_TYPE_REPLY 1
+
+#define RPC_VERSION 2
+
+#define RPC_COMMON_HDR_SZ (sizeof(uint32_t) * 2)
+#define RPC_REQUEST_HDR_SZ (sizeof(struct rpc_request_hdr))
+#define RPC_REPLY_HDR_SZ (sizeof(uint32_t) * 3)
+#define RPC_REPLY_SZ (sizeof(uint32_t) * 6)
+
+static int audmgr_rpc_thread(void *data)
+{
+ struct audmgr *am = data;
+ struct rpc_request_hdr *hdr = NULL;
+ uint32_t type;
+ int len;
+
+ pr_info("audmgr_rpc_thread() start\n");
+
+ while (!kthread_should_stop()) {
+ if (hdr) {
+ kfree(hdr);
+ hdr = NULL;
+ }
+ len = msm_rpc_read(am->ept, (void **) &hdr, -1, -1);
+ if (len < 0) {
+ pr_err("audmgr: rpc read failed (%d)\n", len);
+ break;
+ }
+ if (len < RPC_COMMON_HDR_SZ)
+ continue;
+
+ type = be32_to_cpu(hdr->type);
+ if (type == RPC_TYPE_REPLY) {
+ struct rpc_reply_hdr *rep = (void *) hdr;
+ uint32_t status;
+ if (len < RPC_REPLY_HDR_SZ)
+ continue;
+ status = be32_to_cpu(rep->reply_stat);
+ if (status == RPCMSG_REPLYSTAT_ACCEPTED) {
+ status = be32_to_cpu(rep->data.acc_hdr.accept_stat);
+ pr_info("audmgr: rpc_reply status %d\n", status);
+ } else {
+ pr_info("audmgr: rpc_reply denied!\n");
+ }
+ /* process reply */
+ continue;
+ }
+
+ if (len < RPC_REQUEST_HDR_SZ)
+ continue;
+
+ process_rpc_request(be32_to_cpu(hdr->procedure),
+ be32_to_cpu(hdr->xid),
+ (void *) (hdr + 1),
+ len - sizeof(*hdr),
+ data);
+ }
+ pr_info("audmgr_rpc_thread() exit\n");
+ if (hdr) {
+ kfree(hdr);
+ hdr = NULL;
+ }
+ am->task = NULL;
+ wake_up(&am->wait);
+ return 0;
+}
+
+struct audmgr_enable_msg {
+ struct rpc_request_hdr hdr;
+ struct rpc_audmgr_enable_client_args args;
+};
+
+struct audmgr_disable_msg {
+ struct rpc_request_hdr hdr;
+ uint32_t handle;
+};
+
+int audmgr_open(struct audmgr *am)
+{
+ int rc;
+
+ if (am->state != STATE_CLOSED)
+ return 0;
+
+ am->ept = msm_rpc_connect(AUDMGR_PROG,
+ AUDMGR_VERS,
+ MSM_RPC_UNINTERRUPTIBLE);
+
+ init_waitqueue_head(&am->wait);
+
+ if (IS_ERR(am->ept)) {
+ rc = PTR_ERR(am->ept);
+ am->ept = NULL;
+ pr_err("audmgr: failed to connect to audmgr svc\n");
+ return rc;
+ }
+
+ am->task = kthread_run(audmgr_rpc_thread, am, "audmgr_rpc");
+ if (IS_ERR(am->task)) {
+ rc = PTR_ERR(am->task);
+ am->task = NULL;
+ msm_rpc_close(am->ept);
+ am->ept = NULL;
+ return rc;
+ }
+
+ am->state = STATE_DISABLED;
+ return 0;
+}
+EXPORT_SYMBOL(audmgr_open);
+
+int audmgr_close(struct audmgr *am)
+{
+ return -EBUSY;
+}
+EXPORT_SYMBOL(audmgr_close);
+
+int audmgr_enable(struct audmgr *am, struct audmgr_config *cfg)
+{
+ struct audmgr_enable_msg msg;
+ int rc;
+
+ if (am->state == STATE_ENABLED)
+ return 0;
+
+ if (am->state == STATE_DISABLING)
+ pr_err("audmgr: state is DISABLING in enable?\n");
+ am->state = STATE_ENABLING;
+
+ msg.args.set_to_one = cpu_to_be32(1);
+ msg.args.tx_sample_rate = cpu_to_be32(cfg->tx_rate);
+ msg.args.rx_sample_rate = cpu_to_be32(cfg->rx_rate);
+ msg.args.def_method = cpu_to_be32(cfg->def_method);
+ msg.args.codec_type = cpu_to_be32(cfg->codec);
+ msg.args.snd_method = cpu_to_be32(cfg->snd_method);
+ msg.args.cb_func = cpu_to_be32(0x11111111);
+ msg.args.client_data = cpu_to_be32(0x11223344);
+
+ msm_rpc_setup_req(&msg.hdr, AUDMGR_PROG, msm_rpc_get_vers(am->ept),
+ AUDMGR_ENABLE_CLIENT);
+
+ rc = msm_rpc_write(am->ept, &msg, sizeof(msg));
+ if (rc < 0)
+ return rc;
+
+ rc = wait_event_timeout(am->wait, am->state != STATE_ENABLING, 15 * HZ);
+ if (rc == 0) {
+ pr_err("audmgr_enable: ARM9 did not reply to RPC am->state = %d\n", am->state);
+ BUG();
+ }
+ if (am->state == STATE_ENABLED)
+ return 0;
+
+ pr_err("audmgr: unexpected state %d while enabling?!\n", am->state);
+ return -ENODEV;
+}
+EXPORT_SYMBOL(audmgr_enable);
+
+int audmgr_disable(struct audmgr *am)
+{
+ struct audmgr_disable_msg msg;
+ int rc;
+
+ if (am->state == STATE_DISABLED)
+ return 0;
+
+ msm_rpc_setup_req(&msg.hdr, AUDMGR_PROG, msm_rpc_get_vers(am->ept),
+ AUDMGR_DISABLE_CLIENT);
+ msg.handle = cpu_to_be32(am->handle);
+
+ am->state = STATE_DISABLING;
+
+ rc = msm_rpc_write(am->ept, &msg, sizeof(msg));
+ if (rc < 0)
+ return rc;
+
+ rc = wait_event_timeout(am->wait, am->state != STATE_DISABLING, 15 * HZ);
+ if (rc == 0) {
+ pr_err("audmgr_disable: ARM9 did not reply to RPC am->state = %d\n", am->state);
+ BUG();
+ }
+
+ if (am->state == STATE_DISABLED)
+ return 0;
+
+ pr_err("audmgr: unexpected state %d while disabling?!\n", am->state);
+ return -ENODEV;
+}
+EXPORT_SYMBOL(audmgr_disable);