diff options
Diffstat (limited to 'drivers/staging/dream/qdsp5/audmgr.c')
-rw-r--r-- | drivers/staging/dream/qdsp5/audmgr.c | 313 |
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 0000000..1ad8b82 --- /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); |