Staging: HTC Dream: add qdsp support
authorIliyan Malchev <ibm@android.com>
Fri, 17 Jul 2009 11:10:30 +0000 (13:10 +0200)
committerGreg Kroah-Hartman <gregkh@suse.de>
Tue, 15 Sep 2009 19:01:43 +0000 (12:01 -0700)
QDSP code is neccessarry for communication with some hardware
components on HTC Dream, including camera hardware. It also drives DSP
coproccessor.

Signed-off-by: Pavel Machek <pavel@ucw.cz>
Cc: Brian Swetland <swetland@google.com>
Cc: Iliyan Malchev <ibm@android.com>
Cc: San Mehat <san@android.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
28 files changed:
drivers/staging/dream/qdsp5/Makefile [new file with mode: 0644]
drivers/staging/dream/qdsp5/adsp.c [new file with mode: 0644]
drivers/staging/dream/qdsp5/adsp.h [new file with mode: 0644]
drivers/staging/dream/qdsp5/adsp_6210.c [new file with mode: 0644]
drivers/staging/dream/qdsp5/adsp_6220.c [new file with mode: 0644]
drivers/staging/dream/qdsp5/adsp_6225.c [new file with mode: 0644]
drivers/staging/dream/qdsp5/adsp_driver.c [new file with mode: 0644]
drivers/staging/dream/qdsp5/adsp_info.c [new file with mode: 0644]
drivers/staging/dream/qdsp5/adsp_jpeg_patch_event.c [new file with mode: 0644]
drivers/staging/dream/qdsp5/adsp_jpeg_verify_cmd.c [new file with mode: 0644]
drivers/staging/dream/qdsp5/adsp_lpm_verify_cmd.c [new file with mode: 0644]
drivers/staging/dream/qdsp5/adsp_vfe_patch_event.c [new file with mode: 0644]
drivers/staging/dream/qdsp5/adsp_vfe_verify_cmd.c [new file with mode: 0644]
drivers/staging/dream/qdsp5/adsp_video_verify_cmd.c [new file with mode: 0644]
drivers/staging/dream/qdsp5/adsp_videoenc_verify_cmd.c [new file with mode: 0644]
drivers/staging/dream/qdsp5/audio_aac.c [new file with mode: 0644]
drivers/staging/dream/qdsp5/audio_amrnb.c [new file with mode: 0644]
drivers/staging/dream/qdsp5/audio_evrc.c [new file with mode: 0644]
drivers/staging/dream/qdsp5/audio_in.c [new file with mode: 0644]
drivers/staging/dream/qdsp5/audio_mp3.c [new file with mode: 0644]
drivers/staging/dream/qdsp5/audio_out.c [new file with mode: 0644]
drivers/staging/dream/qdsp5/audio_qcelp.c [new file with mode: 0644]
drivers/staging/dream/qdsp5/audmgr.c [new file with mode: 0644]
drivers/staging/dream/qdsp5/audmgr.h [new file with mode: 0644]
drivers/staging/dream/qdsp5/audmgr_new.h [new file with mode: 0644]
drivers/staging/dream/qdsp5/audpp.c [new file with mode: 0644]
drivers/staging/dream/qdsp5/evlog.h [new file with mode: 0644]
drivers/staging/dream/qdsp5/snd.c [new file with mode: 0644]

diff --git a/drivers/staging/dream/qdsp5/Makefile b/drivers/staging/dream/qdsp5/Makefile
new file mode 100644 (file)
index 0000000..991d4a7
--- /dev/null
@@ -0,0 +1,17 @@
+obj-y += adsp.o
+ifeq ($(CONFIG_MSM_AMSS_VERSION_6350),y)
+obj-y += adsp_info.o
+obj-y += audio_evrc.o audio_qcelp.o audio_amrnb.o audio_aac.o
+else
+obj-y += adsp_6225.o
+endif
+
+obj-y += adsp_driver.o
+obj-y += adsp_video_verify_cmd.o
+obj-y += adsp_videoenc_verify_cmd.o
+obj-y += adsp_jpeg_verify_cmd.o adsp_jpeg_patch_event.o
+obj-y += adsp_vfe_verify_cmd.o adsp_vfe_patch_event.o
+obj-y += adsp_lpm_verify_cmd.o
+obj-y += audio_out.o audio_in.o audio_mp3.o audmgr.o audpp.o
+obj-y += snd.o
+
diff --git a/drivers/staging/dream/qdsp5/adsp.c b/drivers/staging/dream/qdsp5/adsp.c
new file mode 100644 (file)
index 0000000..d096456
--- /dev/null
@@ -0,0 +1,1163 @@
+/* arch/arm/mach-msm/qdsp5/adsp.c
+ *
+ * Register/Interrupt access for userspace aDSP library.
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Iliyan Malchev <ibm@android.com>
+ *
+ * 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.
+ *
+ */
+
+/* TODO:
+ * - move shareable rpc code outside of adsp.c
+ * - general solution for virt->phys patchup
+ * - queue IDs should be relative to modules
+ * - disallow access to non-associated queues
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/wakelock.h>
+
+static struct wake_lock adsp_wake_lock;
+static inline void prevent_suspend(void)
+{
+       wake_lock(&adsp_wake_lock);
+}
+static inline void allow_suspend(void)
+{
+       wake_unlock(&adsp_wake_lock);
+}
+
+#include <linux/io.h>
+#include <mach/msm_iomap.h>
+#include "adsp.h"
+
+#define INT_ADSP INT_ADSP_A9_A11
+
+static struct adsp_info adsp_info;
+static struct msm_rpc_endpoint *rpc_cb_server_client;
+static struct msm_adsp_module *adsp_modules;
+static int adsp_open_count;
+static DEFINE_MUTEX(adsp_open_lock);
+
+/* protect interactions with the ADSP command/message queue */
+static spinlock_t adsp_cmd_lock;
+
+static uint32_t current_image = -1;
+
+void adsp_set_image(struct adsp_info *info, uint32_t image)
+{
+       current_image = image;
+}
+
+/*
+ * Checks whether the module_id is available in the
+ * module_entries table.If module_id is available returns `0`.
+ * If module_id is not available returns `-ENXIO`.
+ */
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+static int32_t adsp_validate_module(uint32_t module_id)
+{
+       uint32_t        *ptr;
+       uint32_t        module_index;
+       uint32_t        num_mod_entries;
+
+       ptr = adsp_info.init_info_ptr->module_entries;
+       num_mod_entries = adsp_info.init_info_ptr->module_table_size;
+
+       for (module_index = 0; module_index < num_mod_entries; module_index++)
+               if (module_id == ptr[module_index])
+                       return 0;
+
+       return -ENXIO;
+}
+#else
+static inline int32_t adsp_validate_module(uint32_t module_id) { return 0; }
+#endif
+
+uint32_t adsp_get_module(struct adsp_info *info, uint32_t task)
+{
+       BUG_ON(current_image == -1UL);
+       return info->task_to_module[current_image][task];
+}
+
+uint32_t adsp_get_queue_offset(struct adsp_info *info, uint32_t queue_id)
+{
+       BUG_ON(current_image == -1UL);
+       return info->queue_offset[current_image][queue_id];
+}
+
+static int rpc_adsp_rtos_app_to_modem(uint32_t cmd, uint32_t module,
+                                     struct msm_adsp_module *adsp_module)
+{
+       int rc;
+       struct rpc_adsp_rtos_app_to_modem_args_t rpc_req;
+       struct rpc_reply_hdr *rpc_rsp;
+
+       msm_rpc_setup_req(&rpc_req.hdr,
+                         RPC_ADSP_RTOS_ATOM_PROG,
+                         msm_rpc_get_vers(adsp_module->rpc_client),
+                         RPC_ADSP_RTOS_APP_TO_MODEM_PROC);
+
+       rpc_req.gotit = cpu_to_be32(1);
+       rpc_req.cmd = cpu_to_be32(cmd);
+       rpc_req.proc_id = cpu_to_be32(RPC_ADSP_RTOS_PROC_APPS);
+       rpc_req.module = cpu_to_be32(module);
+       rc = msm_rpc_write(adsp_module->rpc_client, &rpc_req, sizeof(rpc_req));
+       if (rc < 0) {
+               pr_err("adsp: could not send RPC request: %d\n", rc);
+               return rc;
+       }
+
+       rc = msm_rpc_read(adsp_module->rpc_client,
+                         (void **)&rpc_rsp, -1, (5*HZ));
+       if (rc < 0) {
+               pr_err("adsp: error receiving RPC reply: %d (%d)\n",
+                      rc, -ERESTARTSYS);
+               return rc;
+       }
+
+       if (be32_to_cpu(rpc_rsp->reply_stat) != RPCMSG_REPLYSTAT_ACCEPTED) {
+               pr_err("adsp: RPC call was denied!\n");
+               kfree(rpc_rsp);
+               return -EPERM;
+       }
+
+       if (be32_to_cpu(rpc_rsp->data.acc_hdr.accept_stat) !=
+           RPC_ACCEPTSTAT_SUCCESS) {
+               pr_err("adsp error: RPC call was not successful (%d)\n",
+                      be32_to_cpu(rpc_rsp->data.acc_hdr.accept_stat));
+               kfree(rpc_rsp);
+               return -EINVAL;
+       }
+
+       kfree(rpc_rsp);
+       return 0;
+}
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+static int get_module_index(uint32_t id)
+{
+       int mod_idx;
+       for (mod_idx = 0; mod_idx < adsp_info.module_count; mod_idx++)
+               if (adsp_info.module[mod_idx].id == id)
+                       return mod_idx;
+
+       return -ENXIO;
+}
+#endif
+
+static struct msm_adsp_module *find_adsp_module_by_id(
+       struct adsp_info *info, uint32_t id)
+{
+       if (id > info->max_module_id) {
+               return NULL;
+       } else {
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+               id = get_module_index(id);
+               if (id < 0)
+                       return NULL;
+#endif
+               return info->id_to_module[id];
+       }
+}
+
+static struct msm_adsp_module *find_adsp_module_by_name(
+       struct adsp_info *info, const char *name)
+{
+       unsigned n;
+       for (n = 0; n < info->module_count; n++)
+               if (!strcmp(name, adsp_modules[n].name))
+                       return adsp_modules + n;
+       return NULL;
+}
+
+static int adsp_rpc_init(struct msm_adsp_module *adsp_module)
+{
+       /* remove the original connect once compatible support is complete */
+       adsp_module->rpc_client = msm_rpc_connect(
+                       RPC_ADSP_RTOS_ATOM_PROG,
+                       RPC_ADSP_RTOS_ATOM_VERS,
+                       MSM_RPC_UNINTERRUPTIBLE);
+
+       if (IS_ERR(adsp_module->rpc_client)) {
+               int rc = PTR_ERR(adsp_module->rpc_client);
+               adsp_module->rpc_client = 0;
+               pr_err("adsp: could not open rpc client: %d\n", rc);
+               return rc;
+       }
+
+       return 0;
+}
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+/*
+ * Send RPC_ADSP_RTOS_CMD_GET_INIT_INFO cmd to ARM9 and get
+ * queue offsets and module entries (init info) as part of the event.
+ */
+static void  msm_get_init_info(void)
+{
+       int rc;
+       struct rpc_adsp_rtos_app_to_modem_args_t rpc_req;
+
+       adsp_info.init_info_rpc_client = msm_rpc_connect(
+                       RPC_ADSP_RTOS_ATOM_PROG,
+                       RPC_ADSP_RTOS_ATOM_VERS,
+                       MSM_RPC_UNINTERRUPTIBLE);
+       if (IS_ERR(adsp_info.init_info_rpc_client)) {
+               rc = PTR_ERR(adsp_info.init_info_rpc_client);
+               adsp_info.init_info_rpc_client = 0;
+               pr_err("adsp: could not open rpc client: %d\n", rc);
+               return;
+       }
+
+       msm_rpc_setup_req(&rpc_req.hdr,
+                       RPC_ADSP_RTOS_ATOM_PROG,
+                       msm_rpc_get_vers(adsp_info.init_info_rpc_client),
+                       RPC_ADSP_RTOS_APP_TO_MODEM_PROC);
+
+       rpc_req.gotit = cpu_to_be32(1);
+       rpc_req.cmd = cpu_to_be32(RPC_ADSP_RTOS_CMD_GET_INIT_INFO);
+       rpc_req.proc_id = cpu_to_be32(RPC_ADSP_RTOS_PROC_APPS);
+       rpc_req.module = 0;
+
+       rc = msm_rpc_write(adsp_info.init_info_rpc_client,
+                               &rpc_req, sizeof(rpc_req));
+       if (rc < 0)
+               pr_err("adsp: could not send RPC request: %d\n", rc);
+}
+#endif
+
+int msm_adsp_get(const char *name, struct msm_adsp_module **out,
+                struct msm_adsp_ops *ops, void *driver_data)
+{
+       struct msm_adsp_module *module;
+       int rc = 0;
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+       static uint32_t init_info_cmd_sent;
+       if (!init_info_cmd_sent) {
+               msm_get_init_info();
+               init_waitqueue_head(&adsp_info.init_info_wait);
+               rc = wait_event_timeout(adsp_info.init_info_wait,
+                       adsp_info.init_info_state == ADSP_STATE_INIT_INFO,
+                       5 * HZ);
+               if (!rc) {
+                       pr_info("adsp: INIT_INFO failed\n");
+                       return -ETIMEDOUT;
+               }
+               init_info_cmd_sent++;
+       }
+#endif
+
+       module = find_adsp_module_by_name(&adsp_info, name);
+       if (!module)
+               return -ENODEV;
+
+       mutex_lock(&module->lock);
+       pr_info("adsp: opening module %s\n", module->name);
+       if (module->open_count++ == 0 && module->clk)
+               clk_enable(module->clk);
+
+       mutex_lock(&adsp_open_lock);
+       if (adsp_open_count++ == 0) {
+               enable_irq(INT_ADSP);
+               prevent_suspend();
+       }
+       mutex_unlock(&adsp_open_lock);
+
+       if (module->ops) {
+               rc = -EBUSY;
+               goto done;
+       }
+
+       rc = adsp_rpc_init(module);
+       if (rc)
+               goto done;
+
+       module->ops = ops;
+       module->driver_data = driver_data;
+       *out = module;
+       rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_REGISTER_APP,
+                                       module->id, module);
+       if (rc) {
+               module->ops = NULL;
+               module->driver_data = NULL;
+               *out = NULL;
+               pr_err("adsp: REGISTER_APP failed\n");
+               goto done;
+       }
+
+       pr_info("adsp: module %s has been registered\n", module->name);
+
+done:
+       mutex_lock(&adsp_open_lock);
+       if (rc && --adsp_open_count == 0) {
+               disable_irq(INT_ADSP);
+               allow_suspend();
+       }
+       if (rc && --module->open_count == 0 && module->clk)
+               clk_disable(module->clk);
+       mutex_unlock(&adsp_open_lock);
+       mutex_unlock(&module->lock);
+       return rc;
+}
+EXPORT_SYMBOL(msm_adsp_get);
+
+static int msm_adsp_disable_locked(struct msm_adsp_module *module);
+
+void msm_adsp_put(struct msm_adsp_module *module)
+{
+       unsigned long flags;
+
+       mutex_lock(&module->lock);
+       if (--module->open_count == 0 && module->clk)
+               clk_disable(module->clk);
+       if (module->ops) {
+               pr_info("adsp: closing module %s\n", module->name);
+
+               /* lock to ensure a dsp event cannot be delivered
+                * during or after removal of the ops and driver_data
+                */
+               spin_lock_irqsave(&adsp_cmd_lock, flags);
+               module->ops = NULL;
+               module->driver_data = NULL;
+               spin_unlock_irqrestore(&adsp_cmd_lock, flags);
+
+               if (module->state != ADSP_STATE_DISABLED) {
+                       pr_info("adsp: disabling module %s\n", module->name);
+                       msm_adsp_disable_locked(module);
+               }
+
+               msm_rpc_close(module->rpc_client);
+               module->rpc_client = 0;
+               if (--adsp_open_count == 0) {
+                       disable_irq(INT_ADSP);
+                       allow_suspend();
+                       pr_info("adsp: disable interrupt\n");
+               }
+       } else {
+               pr_info("adsp: module %s is already closed\n", module->name);
+       }
+       mutex_unlock(&module->lock);
+}
+EXPORT_SYMBOL(msm_adsp_put);
+
+/* this should be common code with rpc_servers.c */
+static int rpc_send_accepted_void_reply(struct msm_rpc_endpoint *client,
+                                       uint32_t xid, uint32_t accept_status)
+{
+       int rc = 0;
+       uint8_t reply_buf[sizeof(struct rpc_reply_hdr)];
+       struct rpc_reply_hdr *reply = (struct rpc_reply_hdr *)reply_buf;
+
+       reply->xid = cpu_to_be32(xid);
+       reply->type = cpu_to_be32(1); /* reply */
+       reply->reply_stat = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED);
+
+       reply->data.acc_hdr.accept_stat = cpu_to_be32(accept_status);
+       reply->data.acc_hdr.verf_flavor = 0;
+       reply->data.acc_hdr.verf_length = 0;
+
+       rc = msm_rpc_write(rpc_cb_server_client, reply_buf, sizeof(reply_buf));
+       if (rc < 0)
+               pr_err("adsp: could not write RPC response: %d\n", rc);
+       return rc;
+}
+
+int __msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr,
+                  void *cmd_buf, size_t cmd_size)
+{
+       uint32_t ctrl_word;
+       uint32_t dsp_q_addr;
+       uint32_t dsp_addr;
+       uint32_t cmd_id = 0;
+       int cnt = 0;
+       int ret_status = 0;
+       unsigned long flags;
+       struct adsp_info *info = module->info;
+
+       spin_lock_irqsave(&adsp_cmd_lock, flags);
+
+       if (module->state != ADSP_STATE_ENABLED) {
+               spin_unlock_irqrestore(&adsp_cmd_lock, flags);
+               pr_err("adsp: module %s not enabled before write\n",
+                      module->name);
+               return -ENODEV;
+       }
+       if (adsp_validate_module(module->id)) {
+               spin_unlock_irqrestore(&adsp_cmd_lock, flags);
+               pr_info("adsp: module id validation failed %s  %d\n",
+                       module->name, module->id);
+               return -ENXIO;
+       }
+       dsp_q_addr = adsp_get_queue_offset(info, dsp_queue_addr);
+       dsp_q_addr &= ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M;
+
+       /* Poll until the ADSP is ready to accept a command.
+        * Wait for 100us, return error if it's not responding.
+        * If this returns an error, we need to disable ALL modules and
+        * then retry.
+        */
+       while (((ctrl_word = readl(info->write_ctrl)) &
+               ADSP_RTOS_WRITE_CTRL_WORD_READY_M) !=
+               ADSP_RTOS_WRITE_CTRL_WORD_READY_V) {
+               if (cnt > 100) {
+                       pr_err("adsp: timeout waiting for DSP write ready\n");
+                       ret_status = -EIO;
+                       goto fail;
+               }
+               pr_warning("adsp: waiting for DSP write ready\n");
+               udelay(1);
+               cnt++;
+       }
+
+       /* Set the mutex bits */
+       ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M);
+       ctrl_word |=  ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V;
+
+       /* Clear the command bits */
+       ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_CMD_M);
+
+       /* Set the queue address bits */
+       ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M);
+       ctrl_word |= dsp_q_addr;
+
+       writel(ctrl_word, info->write_ctrl);
+
+       /* Generate an interrupt to the DSP.  This notifies the DSP that
+        * we are about to send a command on this particular queue.  The
+        * DSP will in response change its state.
+        */
+       writel(1, info->send_irq);
+
+       /* Poll until the adsp responds to the interrupt; this does not
+        * generate an interrupt from the adsp.  This should happen within
+        * 5ms.
+        */
+       cnt = 0;
+       while ((readl(info->write_ctrl) &
+               ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M) ==
+               ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V) {
+               if (cnt > 5000) {
+                       pr_err("adsp: timeout waiting for adsp ack\n");
+                       ret_status = -EIO;
+                       goto fail;
+               }
+               udelay(1);
+               cnt++;
+       }
+
+       /* Read the ctrl word */
+       ctrl_word = readl(info->write_ctrl);
+
+       if ((ctrl_word & ADSP_RTOS_WRITE_CTRL_WORD_STATUS_M) !=
+                        ADSP_RTOS_WRITE_CTRL_WORD_NO_ERR_V) {
+               ret_status = -EAGAIN;
+               goto fail;
+       }
+
+       /* Ctrl word status bits were 00, no error in the ctrl word */
+
+       /* Get the DSP buffer address */
+       dsp_addr = (ctrl_word & ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M) +
+                  (uint32_t)MSM_AD5_BASE;
+
+       if (dsp_addr < (uint32_t)(MSM_AD5_BASE + QDSP_RAMC_OFFSET)) {
+               uint16_t *buf_ptr = (uint16_t *) cmd_buf;
+               uint16_t *dsp_addr16 = (uint16_t *)dsp_addr;
+               cmd_size /= sizeof(uint16_t);
+
+               /* Save the command ID */
+               cmd_id = (uint32_t) buf_ptr[0];
+
+               /* Copy the command to DSP memory */
+               cmd_size++;
+               while (--cmd_size)
+                       *dsp_addr16++ = *buf_ptr++;
+       } else {
+               uint32_t *buf_ptr = (uint32_t *) cmd_buf;
+               uint32_t *dsp_addr32 = (uint32_t *)dsp_addr;
+               cmd_size /= sizeof(uint32_t);
+
+               /* Save the command ID */
+               cmd_id = buf_ptr[0];
+
+               cmd_size++;
+               while (--cmd_size)
+                       *dsp_addr32++ = *buf_ptr++;
+       }
+
+       /* Set the mutex bits */
+       ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M);
+       ctrl_word |=  ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V;
+
+       /* Set the command bits to write done */
+       ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_CMD_M);
+       ctrl_word |= ADSP_RTOS_WRITE_CTRL_WORD_CMD_WRITE_DONE_V;
+
+       /* Set the queue address bits */
+       ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M);
+       ctrl_word |= dsp_q_addr;
+
+       writel(ctrl_word, info->write_ctrl);
+
+       /* Generate an interrupt to the DSP.  It does not respond with
+        * an interrupt, and we do not need to wait for it to
+        * acknowledge, because it will hold the mutex lock until it's
+        * ready to receive more commands again.
+        */
+       writel(1, info->send_irq);
+
+       module->num_commands++;
+
+fail:
+       spin_unlock_irqrestore(&adsp_cmd_lock, flags);
+       return ret_status;
+}
+EXPORT_SYMBOL(msm_adsp_write);
+
+int msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr,
+                  void *cmd_buf, size_t cmd_size)
+{
+       int rc, retries = 0;
+       do {
+               rc = __msm_adsp_write(module, dsp_queue_addr, cmd_buf, cmd_size);
+               if (rc == -EAGAIN)
+                       udelay(10);
+       } while(rc == -EAGAIN && retries++ < 100);
+       if (retries > 50)
+               pr_warning("adsp: %s command took %d attempts: rc %d\n",
+                               module->name, retries, rc);
+       return rc;
+}
+
+#ifdef CONFIG_MSM_ADSP_REPORT_EVENTS
+static void *modem_event_addr;
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+static void read_modem_event(void *buf, size_t len)
+{
+       uint32_t *dptr = buf;
+       struct rpc_adsp_rtos_modem_to_app_args_t *sptr;
+       struct adsp_rtos_mp_mtoa_type *pkt_ptr;
+
+       sptr = modem_event_addr;
+       pkt_ptr = &sptr->mtoa_pkt.adsp_rtos_mp_mtoa_data.mp_mtoa_packet;
+
+       dptr[0] = be32_to_cpu(sptr->mtoa_pkt.mp_mtoa_header.event);
+       dptr[1] = be32_to_cpu(pkt_ptr->module);
+       dptr[2] = be32_to_cpu(pkt_ptr->image);
+}
+#else
+static void read_modem_event(void *buf, size_t len)
+{
+       uint32_t *dptr = buf;
+       struct rpc_adsp_rtos_modem_to_app_args_t *sptr =
+               modem_event_addr;
+       dptr[0] = be32_to_cpu(sptr->event);
+       dptr[1] = be32_to_cpu(sptr->module);
+       dptr[2] = be32_to_cpu(sptr->image);
+}
+#endif /* CONFIG_MSM_AMSS_VERSION >= 6350 */
+#endif /* CONFIG_MSM_ADSP_REPORT_EVENTS */
+
+static void handle_adsp_rtos_mtoa_app(struct rpc_request_hdr *req)
+{
+       struct rpc_adsp_rtos_modem_to_app_args_t *args =
+               (struct rpc_adsp_rtos_modem_to_app_args_t *)req;
+       uint32_t event;
+       uint32_t proc_id;
+       uint32_t module_id;
+       uint32_t image;
+       struct msm_adsp_module *module;
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+       struct adsp_rtos_mp_mtoa_type *pkt_ptr =
+               &args->mtoa_pkt.adsp_rtos_mp_mtoa_data.mp_mtoa_packet;
+
+       event = be32_to_cpu(args->mtoa_pkt.mp_mtoa_header.event);
+       proc_id = be32_to_cpu(args->mtoa_pkt.mp_mtoa_header.proc_id);
+       module_id = be32_to_cpu(pkt_ptr->module);
+       image = be32_to_cpu(pkt_ptr->image);
+
+       if (be32_to_cpu(args->mtoa_pkt.desc_field) == RPC_ADSP_RTOS_INIT_INFO) {
+               struct queue_to_offset_type *qptr;
+               struct queue_to_offset_type *qtbl;
+               uint32_t *mptr;
+               uint32_t *mtbl;
+               uint32_t q_idx;
+               uint32_t num_entries;
+               uint32_t entries_per_image;
+               struct adsp_rtos_mp_mtoa_init_info_type *iptr;
+               struct adsp_rtos_mp_mtoa_init_info_type *sptr;
+               int32_t i_no, e_idx;
+
+               pr_info("adsp:INIT_INFO Event\n");
+               sptr = &args->mtoa_pkt.adsp_rtos_mp_mtoa_data.
+                               mp_mtoa_init_packet;
+
+               iptr = adsp_info.init_info_ptr;
+               iptr->image_count = be32_to_cpu(sptr->image_count);
+               iptr->num_queue_offsets = be32_to_cpu(sptr->num_queue_offsets);
+               num_entries = iptr->num_queue_offsets;
+               qptr = &sptr->queue_offsets_tbl[0][0];
+               for (i_no = 0; i_no < iptr->image_count; i_no++) {
+                       qtbl = &iptr->queue_offsets_tbl[i_no][0];
+                       for (e_idx = 0; e_idx < num_entries; e_idx++) {
+                               qtbl[e_idx].offset = be32_to_cpu(qptr->offset);
+                               qtbl[e_idx].queue = be32_to_cpu(qptr->queue);
+                               q_idx = be32_to_cpu(qptr->queue);
+                               iptr->queue_offsets[i_no][q_idx] =
+                                               qtbl[e_idx].offset;
+                               qptr++;
+                       }
+               }
+
+               num_entries = be32_to_cpu(sptr->num_task_module_entries);
+               iptr->num_task_module_entries = num_entries;
+               entries_per_image = num_entries / iptr->image_count;
+               mptr = &sptr->task_to_module_tbl[0][0];
+               for (i_no = 0; i_no < iptr->image_count; i_no++) {
+                       mtbl = &iptr->task_to_module_tbl[i_no][0];
+                       for (e_idx = 0; e_idx < entries_per_image; e_idx++) {
+                               mtbl[e_idx] = be32_to_cpu(*mptr);
+                               mptr++;
+                       }
+               }
+
+               iptr->module_table_size = be32_to_cpu(sptr->module_table_size);
+               mptr = &sptr->module_entries[0];
+               for (i_no = 0; i_no < iptr->module_table_size; i_no++)
+                       iptr->module_entries[i_no] = be32_to_cpu(mptr[i_no]);
+               adsp_info.init_info_state = ADSP_STATE_INIT_INFO;
+               rpc_send_accepted_void_reply(rpc_cb_server_client, req->xid,
+                                               RPC_ACCEPTSTAT_SUCCESS);
+               wake_up(&adsp_info.init_info_wait);
+
+               return;
+       }
+#else
+       event = be32_to_cpu(args->event);
+       proc_id = be32_to_cpu(args->proc_id);
+       module_id = be32_to_cpu(args->module);
+       image = be32_to_cpu(args->image);
+#endif
+
+       pr_info("adsp: rpc event=%d, proc_id=%d, module=%d, image=%d\n",
+               event, proc_id, module_id, image);
+
+       module = find_adsp_module_by_id(&adsp_info, module_id);
+       if (!module) {
+               pr_err("adsp: module %d is not supported!\n", module_id);
+               rpc_send_accepted_void_reply(rpc_cb_server_client, req->xid,
+                               RPC_ACCEPTSTAT_GARBAGE_ARGS);
+               return;
+       }
+
+       mutex_lock(&module->lock);
+       switch (event) {
+       case RPC_ADSP_RTOS_MOD_READY:
+               pr_info("adsp: module %s: READY\n", module->name);
+               module->state = ADSP_STATE_ENABLED;
+               wake_up(&module->state_wait);
+               adsp_set_image(module->info, image);
+               break;
+       case RPC_ADSP_RTOS_MOD_DISABLE:
+               pr_info("adsp: module %s: DISABLED\n", module->name);
+               module->state = ADSP_STATE_DISABLED;
+               wake_up(&module->state_wait);
+               break;
+       case RPC_ADSP_RTOS_SERVICE_RESET:
+               pr_info("adsp: module %s: SERVICE_RESET\n", module->name);
+               module->state = ADSP_STATE_DISABLED;
+               wake_up(&module->state_wait);
+               break;
+       case RPC_ADSP_RTOS_CMD_SUCCESS:
+               pr_info("adsp: module %s: CMD_SUCCESS\n", module->name);
+               break;
+       case RPC_ADSP_RTOS_CMD_FAIL:
+               pr_info("adsp: module %s: CMD_FAIL\n", module->name);
+               break;
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+       case RPC_ADSP_RTOS_DISABLE_FAIL:
+               pr_info("adsp: module %s: DISABLE_FAIL\n", module->name);
+               break;
+#endif
+       default:
+               pr_info("adsp: unknown event %d\n", event);
+               rpc_send_accepted_void_reply(rpc_cb_server_client, req->xid,
+                                            RPC_ACCEPTSTAT_GARBAGE_ARGS);
+               mutex_unlock(&module->lock);
+               return;
+       }
+       rpc_send_accepted_void_reply(rpc_cb_server_client, req->xid,
+                                    RPC_ACCEPTSTAT_SUCCESS);
+       mutex_unlock(&module->lock);
+#ifdef CONFIG_MSM_ADSP_REPORT_EVENTS
+       modem_event_addr = (uint32_t *)req;
+       module->ops->event(module->driver_data, EVENT_MSG_ID,
+                               EVENT_LEN, read_modem_event);
+#endif
+}
+
+static int handle_adsp_rtos_mtoa(struct rpc_request_hdr *req)
+{
+       switch (req->procedure) {
+       case RPC_ADSP_RTOS_MTOA_NULL_PROC:
+               rpc_send_accepted_void_reply(rpc_cb_server_client,
+                                            req->xid,
+                                            RPC_ACCEPTSTAT_SUCCESS);
+               break;
+       case RPC_ADSP_RTOS_MODEM_TO_APP_PROC:
+               handle_adsp_rtos_mtoa_app(req);
+               break;
+       default:
+               pr_err("adsp: unknowned proc %d\n", req->procedure);
+               rpc_send_accepted_void_reply(
+                       rpc_cb_server_client, req->xid,
+                       RPC_ACCEPTSTAT_PROC_UNAVAIL);
+               break;
+       }
+       return 0;
+}
+
+/* this should be common code with rpc_servers.c */
+static int adsp_rpc_thread(void *data)
+{
+       void *buffer;
+       struct rpc_request_hdr *req;
+       int rc;
+
+       do {
+               rc = msm_rpc_read(rpc_cb_server_client, &buffer, -1, -1);
+               if (rc < 0) {
+                       pr_err("adsp: could not read rpc: %d\n", rc);
+                       break;
+               }
+               req = (struct rpc_request_hdr *)buffer;
+
+               req->type = be32_to_cpu(req->type);
+               req->xid = be32_to_cpu(req->xid);
+               req->rpc_vers = be32_to_cpu(req->rpc_vers);
+               req->prog = be32_to_cpu(req->prog);
+               req->vers = be32_to_cpu(req->vers);
+               req->procedure = be32_to_cpu(req->procedure);
+
+               if (req->type != 0)
+                       goto bad_rpc;
+               if (req->rpc_vers != 2)
+                       goto bad_rpc;
+               if (req->prog != RPC_ADSP_RTOS_MTOA_PROG)
+                       goto bad_rpc;
+               if (req->vers != RPC_ADSP_RTOS_MTOA_VERS)
+                       goto bad_rpc;
+
+               handle_adsp_rtos_mtoa(req);
+               kfree(buffer);
+               continue;
+
+bad_rpc:
+               pr_err("adsp: bogus rpc from modem\n");
+               kfree(buffer);
+       } while (1);
+
+       do_exit(0);
+}
+
+static size_t read_event_size;
+static void *read_event_addr;
+
+static void read_event_16(void *buf, size_t len)
+{
+       uint16_t *dst = buf;
+       uint16_t *src = read_event_addr;
+       len /= 2;
+       if (len > read_event_size)
+               len = read_event_size;
+       while (len--)
+               *dst++ = *src++;
+}
+
+static void read_event_32(void *buf, size_t len)
+{
+       uint32_t *dst = buf;
+       uint32_t *src = read_event_addr;
+       len /= 2;
+       if (len > read_event_size)
+               len = read_event_size;
+       while (len--)
+               *dst++ = *src++;
+}
+
+static int adsp_rtos_read_ctrl_word_cmd_tast_to_h_v(
+       struct adsp_info *info, void *dsp_addr)
+{
+       struct msm_adsp_module *module;
+       unsigned rtos_task_id;
+       unsigned msg_id;
+       unsigned msg_length;
+       void (*func)(void *, size_t);
+
+       if (dsp_addr >= (void *)(MSM_AD5_BASE + QDSP_RAMC_OFFSET)) {
+               uint32_t *dsp_addr32 = dsp_addr;
+               uint32_t tmp = *dsp_addr32++;
+               rtos_task_id = (tmp & ADSP_RTOS_READ_CTRL_WORD_TASK_ID_M) >> 8;
+               msg_id = (tmp & ADSP_RTOS_READ_CTRL_WORD_MSG_ID_M);
+               read_event_size = tmp >> 16;
+               read_event_addr = dsp_addr32;
+               msg_length = read_event_size * sizeof(uint32_t);
+               func = read_event_32;
+       } else {
+               uint16_t *dsp_addr16 = dsp_addr;
+               uint16_t tmp = *dsp_addr16++;
+               rtos_task_id = (tmp & ADSP_RTOS_READ_CTRL_WORD_TASK_ID_M) >> 8;
+               msg_id = tmp & ADSP_RTOS_READ_CTRL_WORD_MSG_ID_M;
+               read_event_size = *dsp_addr16++;
+               read_event_addr = dsp_addr16;
+               msg_length = read_event_size * sizeof(uint16_t);
+               func = read_event_16;
+       }
+
+       if (rtos_task_id > info->max_task_id) {
+               pr_err("adsp: bogus task id %d\n", rtos_task_id);
+               return 0;
+       }
+       module = find_adsp_module_by_id(info,
+                                       adsp_get_module(info, rtos_task_id));
+
+       if (!module) {
+               pr_err("adsp: no module for task id %d\n", rtos_task_id);
+               return 0;
+       }
+
+       module->num_events++;
+
+       if (!module->ops) {
+               pr_err("adsp: module %s is not open\n", module->name);
+               return 0;
+       }
+
+       module->ops->event(module->driver_data, msg_id, msg_length, func);
+       return 0;
+}
+
+static int adsp_get_event(struct adsp_info *info)
+{
+       uint32_t ctrl_word;
+       uint32_t ready;
+       void *dsp_addr;
+       uint32_t cmd_type;
+       int cnt;
+       unsigned long flags;
+       int rc = 0;
+
+       spin_lock_irqsave(&adsp_cmd_lock, flags);
+
+       /* Whenever the DSP has a message, it updates this control word
+        * and generates an interrupt.  When we receive the interrupt, we
+        * read this register to find out what ADSP task the command is
+        * comming from.
+        *
+        * The ADSP should *always* be ready on the first call, but the
+        * irq handler calls us in a loop (to handle back-to-back command
+        * processing), so we give the DSP some time to return to the
+        * ready state.  The DSP will not issue another IRQ for events
+        * pending between the first IRQ and the event queue being drained,
+        * unfortunately.
+        */
+
+       for (cnt = 0; cnt < 10; cnt++) {
+               ctrl_word = readl(info->read_ctrl);
+
+               if ((ctrl_word & ADSP_RTOS_READ_CTRL_WORD_FLAG_M) ==
+                   ADSP_RTOS_READ_CTRL_WORD_FLAG_UP_CONT_V)
+                       goto ready;
+
+               udelay(10);
+       }
+       pr_warning("adsp: not ready after 100uS\n");
+       rc = -EBUSY;
+       goto done;
+
+ready:
+       /* Here we check to see if there are pending messages. If there are
+        * none, we siply return -EAGAIN to indicate that there are no more
+        * messages pending.
+        */
+       ready = ctrl_word & ADSP_RTOS_READ_CTRL_WORD_READY_M;
+       if ((ready != ADSP_RTOS_READ_CTRL_WORD_READY_V) &&
+           (ready != ADSP_RTOS_READ_CTRL_WORD_CONT_V)) {
+               rc = -EAGAIN;
+               goto done;
+       }
+
+       /* DSP says that there are messages waiting for the host to read */
+
+       /* Get the Command Type */
+       cmd_type = ctrl_word & ADSP_RTOS_READ_CTRL_WORD_CMD_TYPE_M;
+
+       /* Get the DSP buffer address */
+       dsp_addr = (void *)((ctrl_word &
+                            ADSP_RTOS_READ_CTRL_WORD_DSP_ADDR_M) +
+                           (uint32_t)MSM_AD5_BASE);
+
+       /* We can only handle Task-to-Host messages */
+       if (cmd_type != ADSP_RTOS_READ_CTRL_WORD_CMD_TASK_TO_H_V) {
+               pr_err("adsp: unknown dsp cmd_type %d\n", cmd_type);
+               rc = -EIO;
+               goto done;
+       }
+
+       adsp_rtos_read_ctrl_word_cmd_tast_to_h_v(info, dsp_addr);
+
+       ctrl_word = readl(info->read_ctrl);
+       ctrl_word &= ~ADSP_RTOS_READ_CTRL_WORD_READY_M;
+
+       /* Write ctrl word to the DSP */
+       writel(ctrl_word, info->read_ctrl);
+
+       /* Generate an interrupt to the DSP */
+       writel(1, info->send_irq);
+
+done:
+       spin_unlock_irqrestore(&adsp_cmd_lock, flags);
+       return rc;
+}
+
+static irqreturn_t adsp_irq_handler(int irq, void *data)
+{
+       struct adsp_info *info = &adsp_info;
+       int cnt = 0;
+       for (cnt = 0; cnt < 10; cnt++)
+               if (adsp_get_event(info) < 0)
+                       break;
+       if (cnt > info->event_backlog_max)
+               info->event_backlog_max = cnt;
+       info->events_received += cnt;
+       if (cnt == 10)
+               pr_err("adsp: too many (%d) events for single irq!\n", cnt);
+       return IRQ_HANDLED;
+}
+
+int adsp_set_clkrate(struct msm_adsp_module *module, unsigned long clk_rate)
+{
+       if (module->clk && clk_rate)
+               return clk_set_rate(module->clk, clk_rate);
+
+       return -EINVAL;
+}
+
+int msm_adsp_enable(struct msm_adsp_module *module)
+{
+       int rc = 0;
+
+       pr_info("msm_adsp_enable() '%s'state[%d] id[%d]\n",
+               module->name, module->state, module->id);
+
+       mutex_lock(&module->lock);
+       switch (module->state) {
+       case ADSP_STATE_DISABLED:
+               rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_ENABLE,
+                                               module->id, module);
+               if (rc)
+                       break;
+               module->state = ADSP_STATE_ENABLING;
+               mutex_unlock(&module->lock);
+               rc = wait_event_timeout(module->state_wait,
+                                       module->state != ADSP_STATE_ENABLING,
+                                       1 * HZ);
+               mutex_lock(&module->lock);
+               if (module->state == ADSP_STATE_ENABLED) {
+                       rc = 0;
+               } else {
+                       pr_err("adsp: module '%s' enable timed out\n",
+                              module->name);
+                       rc = -ETIMEDOUT;
+               }
+               break;
+       case ADSP_STATE_ENABLING:
+               pr_warning("adsp: module '%s' enable in progress\n",
+                          module->name);
+               break;
+       case ADSP_STATE_ENABLED:
+               pr_warning("adsp: module '%s' already enabled\n",
+                          module->name);
+               break;
+       case ADSP_STATE_DISABLING:
+               pr_err("adsp: module '%s' disable in progress\n",
+                      module->name);
+               rc = -EBUSY;
+               break;
+       }
+       mutex_unlock(&module->lock);
+       return rc;
+}
+EXPORT_SYMBOL(msm_adsp_enable);
+
+static int msm_adsp_disable_locked(struct msm_adsp_module *module)
+{
+       int rc = 0;
+
+       switch (module->state) {
+       case ADSP_STATE_DISABLED:
+               pr_warning("adsp: module '%s' already disabled\n",
+                          module->name);
+               break;
+       case ADSP_STATE_ENABLING:
+       case ADSP_STATE_ENABLED:
+               rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_DISABLE,
+                                               module->id, module);
+               module->state = ADSP_STATE_DISABLED;
+       }
+       return rc;
+}
+
+int msm_adsp_disable(struct msm_adsp_module *module)
+{
+       int rc;
+       pr_info("msm_adsp_disable() '%s'\n", module->name);
+       mutex_lock(&module->lock);
+       rc = msm_adsp_disable_locked(module);
+       mutex_unlock(&module->lock);
+       return rc;
+}
+EXPORT_SYMBOL(msm_adsp_disable);
+
+static int msm_adsp_probe(struct platform_device *pdev)
+{
+       unsigned count;
+       int rc, i;
+       int max_module_id;
+
+       pr_info("adsp: probe\n");
+
+       wake_lock_init(&adsp_wake_lock, WAKE_LOCK_SUSPEND, "adsp");
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+       adsp_info.init_info_ptr = kzalloc(
+               (sizeof(struct adsp_rtos_mp_mtoa_init_info_type)), GFP_KERNEL);
+       if (!adsp_info.init_info_ptr)
+               return -ENOMEM;
+#endif
+
+       rc = adsp_init_info(&adsp_info);
+       if (rc)
+               return rc;
+       adsp_info.send_irq += (uint32_t) MSM_AD5_BASE;
+       adsp_info.read_ctrl += (uint32_t) MSM_AD5_BASE;
+       adsp_info.write_ctrl += (uint32_t) MSM_AD5_BASE;
+       count = adsp_info.module_count;
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+       max_module_id = count;
+#else
+       max_module_id = adsp_info.max_module_id + 1;
+#endif
+
+       adsp_modules = kzalloc(
+               sizeof(struct msm_adsp_module) * count +
+               sizeof(void *) * max_module_id, GFP_KERNEL);
+       if (!adsp_modules)
+               return -ENOMEM;
+
+       adsp_info.id_to_module = (void *) (adsp_modules + count);
+
+       spin_lock_init(&adsp_cmd_lock);
+
+       rc = request_irq(INT_ADSP, adsp_irq_handler, IRQF_TRIGGER_RISING,
+                        "adsp", 0);
+       if (rc < 0)
+               goto fail_request_irq;
+       disable_irq(INT_ADSP);
+
+       rpc_cb_server_client = msm_rpc_open();
+       if (IS_ERR(rpc_cb_server_client)) {
+               rpc_cb_server_client = NULL;
+               rc = PTR_ERR(rpc_cb_server_client);
+               pr_err("adsp: could not create rpc server (%d)\n", rc);
+               goto fail_rpc_open;
+       }
+
+       rc = msm_rpc_register_server(rpc_cb_server_client,
+                                    RPC_ADSP_RTOS_MTOA_PROG,
+                                    RPC_ADSP_RTOS_MTOA_VERS);
+       if (rc) {
+               pr_err("adsp: could not register callback server (%d)\n", rc);
+               goto fail_rpc_register;
+       }
+
+       /* start the kernel thread to process the callbacks */
+       kthread_run(adsp_rpc_thread, NULL, "kadspd");
+
+       for (i = 0; i < count; i++) {
+               struct msm_adsp_module *mod = adsp_modules + i;
+               mutex_init(&mod->lock);
+               init_waitqueue_head(&mod->state_wait);
+               mod->info = &adsp_info;
+               mod->name = adsp_info.module[i].name;
+               mod->id = adsp_info.module[i].id;
+               if (adsp_info.module[i].clk_name)
+                       mod->clk = clk_get(NULL, adsp_info.module[i].clk_name);
+               else
+                       mod->clk = NULL;
+               if (mod->clk && adsp_info.module[i].clk_rate)
+                       clk_set_rate(mod->clk, adsp_info.module[i].clk_rate);
+               mod->verify_cmd = adsp_info.module[i].verify_cmd;
+               mod->patch_event = adsp_info.module[i].patch_event;
+               INIT_HLIST_HEAD(&mod->pmem_regions);
+               mod->pdev.name = adsp_info.module[i].pdev_name;
+               mod->pdev.id = -1;
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+               adsp_info.id_to_module[i] = mod;
+#else
+               adsp_info.id_to_module[mod->id] = mod;
+#endif
+               platform_device_register(&mod->pdev);
+       }
+
+       msm_adsp_publish_cdevs(adsp_modules, count);
+
+       return 0;
+
+fail_rpc_register:
+       msm_rpc_close(rpc_cb_server_client);
+       rpc_cb_server_client = NULL;
+fail_rpc_open:
+       enable_irq(INT_ADSP);
+       free_irq(INT_ADSP, 0);
+fail_request_irq:
+       kfree(adsp_modules);
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+       kfree(adsp_info.init_info_ptr);
+#endif
+       return rc;
+}
+
+static struct platform_driver msm_adsp_driver = {
+       .probe = msm_adsp_probe,
+       .driver = {
+               .name = MSM_ADSP_DRIVER_NAME,
+               .owner = THIS_MODULE,
+       },
+};
+
+static int __init adsp_init(void)
+{
+       return platform_driver_register(&msm_adsp_driver);
+}
+
+device_initcall(adsp_init);
diff --git a/drivers/staging/dream/qdsp5/adsp.h b/drivers/staging/dream/qdsp5/adsp.h
new file mode 100644 (file)
index 0000000..0e5c9ab
--- /dev/null
@@ -0,0 +1,369 @@
+/* arch/arm/mach-msm/qdsp5/adsp.h
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Iliyan Malchev <ibm@android.com>
+ *
+ * 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.
+ *
+ */
+
+#ifndef _ARCH_ARM_MACH_MSM_ADSP_H
+#define _ARCH_ARM_MACH_MSM_ADSP_H
+
+#include <linux/types.h>
+#include <linux/msm_adsp.h>
+#include <mach/msm_rpcrouter.h>
+#include <mach/msm_adsp.h>
+
+int adsp_pmem_fixup(struct msm_adsp_module *module, void **addr,
+                   unsigned long len);
+int adsp_pmem_fixup_kvaddr(struct msm_adsp_module *module, void **addr,
+                          unsigned long *kvaddr, unsigned long len);
+int adsp_pmem_paddr_fixup(struct msm_adsp_module *module, void **addr);
+
+int adsp_vfe_verify_cmd(struct msm_adsp_module *module,
+                       unsigned int queue_id, void *cmd_data,
+                       size_t cmd_size);
+int adsp_jpeg_verify_cmd(struct msm_adsp_module *module,
+                        unsigned int queue_id, void *cmd_data,
+                        size_t cmd_size);
+int adsp_lpm_verify_cmd(struct msm_adsp_module *module,
+                       unsigned int queue_id, void *cmd_data,
+                       size_t cmd_size);
+int adsp_video_verify_cmd(struct msm_adsp_module *module,
+                         unsigned int queue_id, void *cmd_data,
+                         size_t cmd_size);
+int adsp_videoenc_verify_cmd(struct msm_adsp_module *module,
+                         unsigned int queue_id, void *cmd_data,
+                         size_t cmd_size);
+
+
+struct adsp_event;
+
+int adsp_vfe_patch_event(struct msm_adsp_module *module,
+                       struct adsp_event *event);
+
+int adsp_jpeg_patch_event(struct msm_adsp_module *module,
+                       struct adsp_event *event);
+
+
+struct adsp_module_info {
+       const char *name;
+       const char *pdev_name;
+       uint32_t id;
+       const char *clk_name;
+       unsigned long clk_rate;
+       int (*verify_cmd) (struct msm_adsp_module*, unsigned int, void *,
+                          size_t);
+       int (*patch_event) (struct msm_adsp_module*, struct adsp_event *);
+};
+
+#define ADSP_EVENT_MAX_SIZE 496
+#define EVENT_LEN      12
+#define EVENT_MSG_ID   ((uint16_t)~0)
+
+struct adsp_event {
+       struct list_head list;
+       uint32_t size; /* always in bytes */
+       uint16_t msg_id;
+       uint16_t type; /* 0 for msgs (from aDSP), -1 for events (from ARM9) */
+       int is16; /* always 0 (msg is 32-bit) when the event type is 1(ARM9) */
+       union {
+               uint16_t msg16[ADSP_EVENT_MAX_SIZE / 2];
+               uint32_t msg32[ADSP_EVENT_MAX_SIZE / 4];
+       } data;
+};
+
+struct adsp_info {
+       uint32_t send_irq;
+       uint32_t read_ctrl;
+       uint32_t write_ctrl;
+
+       uint32_t max_msg16_size;
+       uint32_t max_msg32_size;
+
+       uint32_t max_task_id;
+       uint32_t max_module_id;
+       uint32_t max_queue_id;
+       uint32_t max_image_id;
+
+       /* for each image id, a map of queue id to offset */
+       uint32_t **queue_offset;
+
+       /* for each image id, a map of task id to module id */
+       uint32_t **task_to_module;
+
+       /* for each module id, map of module id to module */
+       struct msm_adsp_module **id_to_module;
+
+       uint32_t module_count;
+       struct adsp_module_info *module;
+
+       /* stats */
+       uint32_t events_received;
+       uint32_t event_backlog_max;
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+       /* rpc_client for init_info */
+       struct msm_rpc_endpoint *init_info_rpc_client;
+       struct adsp_rtos_mp_mtoa_init_info_type *init_info_ptr;
+       wait_queue_head_t init_info_wait;
+       unsigned init_info_state;
+#endif
+};
+
+#define RPC_ADSP_RTOS_ATOM_PROG 0x3000000a
+#define RPC_ADSP_RTOS_MTOA_PROG 0x3000000b
+#define RPC_ADSP_RTOS_ATOM_NULL_PROC 0
+#define RPC_ADSP_RTOS_MTOA_NULL_PROC 0
+#define RPC_ADSP_RTOS_APP_TO_MODEM_PROC 2
+#define RPC_ADSP_RTOS_MODEM_TO_APP_PROC 2
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+#define RPC_ADSP_RTOS_ATOM_VERS MSM_RPC_VERS(1,0)
+#define RPC_ADSP_RTOS_MTOA_VERS MSM_RPC_VERS(2,1) /* must be actual vers */
+#define MSM_ADSP_DRIVER_NAME "rs3000000a:00010000"
+#elif (CONFIG_MSM_AMSS_VERSION == 6220) || (CONFIG_MSM_AMSS_VERSION == 6225)
+#define RPC_ADSP_RTOS_ATOM_VERS MSM_RPC_VERS(0x71d1094b, 0)
+#define RPC_ADSP_RTOS_MTOA_VERS MSM_RPC_VERS(0xee3a9966, 0)
+#define MSM_ADSP_DRIVER_NAME "rs3000000a:71d1094b"
+#elif CONFIG_MSM_AMSS_VERSION == 6210
+#define RPC_ADSP_RTOS_ATOM_VERS MSM_RPC_VERS(0x20f17fd3, 0)
+#define RPC_ADSP_RTOS_MTOA_VERS MSM_RPC_VERS(0x75babbd6, 0)
+#define MSM_ADSP_DRIVER_NAME "rs3000000a:20f17fd3"
+#else
+#error "Unknown AMSS version"
+#endif
+
+enum rpc_adsp_rtos_proc_type {
+       RPC_ADSP_RTOS_PROC_NONE = 0,
+       RPC_ADSP_RTOS_PROC_MODEM = 1,
+       RPC_ADSP_RTOS_PROC_APPS = 2,
+};
+
+enum {
+       RPC_ADSP_RTOS_CMD_REGISTER_APP,
+       RPC_ADSP_RTOS_CMD_ENABLE,
+       RPC_ADSP_RTOS_CMD_DISABLE,
+       RPC_ADSP_RTOS_CMD_KERNEL_COMMAND,
+       RPC_ADSP_RTOS_CMD_16_COMMAND,
+       RPC_ADSP_RTOS_CMD_32_COMMAND,
+       RPC_ADSP_RTOS_CMD_DISABLE_EVENT_RSP,
+       RPC_ADSP_RTOS_CMD_REMOTE_EVENT,
+       RPC_ADSP_RTOS_CMD_SET_STATE,
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+       RPC_ADSP_RTOS_CMD_REMOTE_INIT_INFO_EVENT,
+       RPC_ADSP_RTOS_CMD_GET_INIT_INFO,
+#endif
+};
+
+enum rpc_adsp_rtos_mod_status_type {
+       RPC_ADSP_RTOS_MOD_READY,
+       RPC_ADSP_RTOS_MOD_DISABLE,
+       RPC_ADSP_RTOS_SERVICE_RESET,
+       RPC_ADSP_RTOS_CMD_FAIL,
+       RPC_ADSP_RTOS_CMD_SUCCESS,
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+       RPC_ADSP_RTOS_INIT_INFO,
+       RPC_ADSP_RTOS_DISABLE_FAIL,
+#endif
+};
+
+struct rpc_adsp_rtos_app_to_modem_args_t {
+       struct rpc_request_hdr hdr;
+       uint32_t gotit; /* if 1, the next elements are present */
+       uint32_t cmd; /* e.g., RPC_ADSP_RTOS_CMD_REGISTER_APP */
+       uint32_t proc_id; /* e.g., RPC_ADSP_RTOS_PROC_APPS */
+       uint32_t module; /* e.g., QDSP_MODULE_AUDPPTASK */
+};
+
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+enum qdsp_image_type {
+       QDSP_IMAGE_COMBO,
+       QDSP_IMAGE_GAUDIO,
+       QDSP_IMAGE_QTV_LP,
+       QDSP_IMAGE_MAX,
+       /* DO NOT USE: Force this enum to be a 32bit type to improve speed */
+       QDSP_IMAGE_32BIT_DUMMY = 0x10000
+};
+
+struct adsp_rtos_mp_mtoa_header_type {
+       enum rpc_adsp_rtos_mod_status_type  event;
+       enum rpc_adsp_rtos_proc_type        proc_id;
+};
+
+/* ADSP RTOS MP Communications - Modem to APP's  Event Info*/
+struct adsp_rtos_mp_mtoa_type {
+       uint32_t        module;
+       uint32_t        image;
+       uint32_t        apps_okts;
+};
+
+/* ADSP RTOS MP Communications - Modem to APP's Init Info  */
+#define IMG_MAX         8
+#define ENTRIES_MAX     64
+
+struct queue_to_offset_type {
+       uint32_t        queue;
+       uint32_t        offset;
+};
+
+struct adsp_rtos_mp_mtoa_init_info_type {
+       uint32_t        image_count;
+       uint32_t        num_queue_offsets;
+       struct queue_to_offset_type     queue_offsets_tbl[IMG_MAX][ENTRIES_MAX];
+       uint32_t        num_task_module_entries;
+       uint32_t        task_to_module_tbl[IMG_MAX][ENTRIES_MAX];
+
+       uint32_t        module_table_size;
+       uint32_t        module_entries[ENTRIES_MAX];
+       /*
+        * queue_offsets[] is to store only queue_offsets
+        */
+       uint32_t        queue_offsets[IMG_MAX][ENTRIES_MAX];
+};
+
+struct adsp_rtos_mp_mtoa_s_type {
+       struct adsp_rtos_mp_mtoa_header_type mp_mtoa_header;
+
+       uint32_t desc_field;
+       union {
+               struct adsp_rtos_mp_mtoa_init_info_type mp_mtoa_init_packet;
+               struct adsp_rtos_mp_mtoa_type mp_mtoa_packet;
+       } adsp_rtos_mp_mtoa_data;
+};
+
+struct rpc_adsp_rtos_modem_to_app_args_t {
+       struct rpc_request_hdr hdr;
+       uint32_t gotit; /* if 1, the next elements are present */
+       struct adsp_rtos_mp_mtoa_s_type mtoa_pkt;
+};
+#else
+struct rpc_adsp_rtos_modem_to_app_args_t {
+       struct rpc_request_hdr hdr;
+       uint32_t gotit; /* if 1, the next elements are present */
+       uint32_t event; /* e.g., RPC_ADSP_RTOS_CMD_REGISTER_APP */
+       uint32_t proc_id; /* e.g., RPC_ADSP_RTOS_PROC_APPS */
+       uint32_t module; /* e.g., QDSP_MODULE_AUDPPTASK */
+       uint32_t image; /* RPC_QDSP_IMAGE_GAUDIO */
+};
+#endif /* CONFIG_MSM_AMSS_VERSION >= 6350 */
+
+#define ADSP_STATE_DISABLED   0
+#define ADSP_STATE_ENABLING   1
+#define ADSP_STATE_ENABLED    2
+#define ADSP_STATE_DISABLING  3
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+#define ADSP_STATE_INIT_INFO  4
+#endif
+
+struct msm_adsp_module {
+       struct mutex lock;
+       const char *name;
+       unsigned id;
+       struct adsp_info *info;
+
+       struct msm_rpc_endpoint *rpc_client;
+       struct msm_adsp_ops *ops;
+       void *driver_data;
+
+       /* statistics */
+       unsigned num_commands;
+       unsigned num_events;
+
+       wait_queue_head_t state_wait;
+       unsigned state;
+
+       struct platform_device pdev;
+       struct clk *clk;
+       int open_count;
+
+       struct mutex pmem_regions_lock;
+       struct hlist_head pmem_regions;
+       int (*verify_cmd) (struct msm_adsp_module*, unsigned int, void *,
+                          size_t);
+       int (*patch_event) (struct msm_adsp_module*, struct adsp_event *);
+};
+
+extern void msm_adsp_publish_cdevs(struct msm_adsp_module *, unsigned);
+extern int adsp_init_info(struct adsp_info *info);
+
+/* Value to indicate that a queue is not defined for a particular image */
+#if CONFIG_MSM_AMSS_VERSION >= 6350
+#define QDSP_RTOS_NO_QUEUE  0xfffffffe
+#else
+#define QDSP_RTOS_NO_QUEUE  0xffffffff
+#endif
+
+/*
+ * Constants used to communicate with the ADSP RTOS
+ */
+#define ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M            0x80000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V     0x80000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_AVAIL_V      0x00000000U
+
+#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_M              0x70000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_WRITE_REQ_V    0x00000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_WRITE_DONE_V   0x10000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_NO_CMD_V       0x70000000U
+
+#define ADSP_RTOS_WRITE_CTRL_WORD_STATUS_M           0x0E000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_NO_ERR_V           0x00000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_NO_FREE_BUF_V      0x02000000U
+
+#define ADSP_RTOS_WRITE_CTRL_WORD_KERNEL_FLG_M       0x01000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_HTOD_MSG_WRITE_V   0x00000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_HTOD_CMD_V         0x01000000U
+
+#define ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M         0x00FFFFFFU
+#define ADSP_RTOS_WRITE_CTRL_WORD_HTOD_CMD_ID_M      0x00FFFFFFU
+
+/* Combination of MUTEX and CMD bits to check if the DSP is busy */
+#define ADSP_RTOS_WRITE_CTRL_WORD_READY_M            0xF0000000U
+#define ADSP_RTOS_WRITE_CTRL_WORD_READY_V            0x70000000U
+
+/* RTOS to Host processor command mask values */
+#define ADSP_RTOS_READ_CTRL_WORD_FLAG_M              0x80000000U
+#define ADSP_RTOS_READ_CTRL_WORD_FLAG_UP_WAIT_V      0x00000000U
+#define ADSP_RTOS_READ_CTRL_WORD_FLAG_UP_CONT_V      0x80000000U
+
+#define ADSP_RTOS_READ_CTRL_WORD_CMD_M               0x60000000U
+#define ADSP_RTOS_READ_CTRL_WORD_READ_DONE_V         0x00000000U
+#define ADSP_RTOS_READ_CTRL_WORD_READ_REQ_V          0x20000000U
+#define ADSP_RTOS_READ_CTRL_WORD_NO_CMD_V            0x60000000U
+
+/* Combination of FLAG and COMMAND bits to check if MSG ready */
+#define ADSP_RTOS_READ_CTRL_WORD_READY_M             0xE0000000U
+#define ADSP_RTOS_READ_CTRL_WORD_READY_V             0xA0000000U
+#define ADSP_RTOS_READ_CTRL_WORD_CONT_V              0xC0000000U
+#define ADSP_RTOS_READ_CTRL_WORD_DONE_V              0xE0000000U
+
+#define ADSP_RTOS_READ_CTRL_WORD_STATUS_M            0x18000000U
+#define ADSP_RTOS_READ_CTRL_WORD_NO_ERR_V            0x00000000U
+
+#define ADSP_RTOS_READ_CTRL_WORD_IN_PROG_M           0x04000000U
+#define ADSP_RTOS_READ_CTRL_WORD_NO_READ_IN_PROG_V   0x00000000U
+#define ADSP_RTOS_READ_CTRL_WORD_READ_IN_PROG_V      0x04000000U
+
+#define ADSP_RTOS_READ_CTRL_WORD_CMD_TYPE_M          0x03000000U
+#define ADSP_RTOS_READ_CTRL_WORD_CMD_TASK_TO_H_V     0x00000000U
+#define ADSP_RTOS_READ_CTRL_WORD_CMD_KRNL_TO_H_V     0x01000000U
+#define ADSP_RTOS_READ_CTRL_WORD_CMD_H_TO_KRNL_CFM_V 0x02000000U
+
+#define ADSP_RTOS_READ_CTRL_WORD_DSP_ADDR_M          0x00FFFFFFU
+
+#define ADSP_RTOS_READ_CTRL_WORD_MSG_ID_M            0x000000FFU
+#define ADSP_RTOS_READ_CTRL_WORD_TASK_ID_M           0x0000FF00U
+
+/* Base address of DSP and DSP hardware registers */
+#define QDSP_RAMC_OFFSET  0x400000
+
+#endif /* _ARCH_ARM_MACH_MSM_ADSP_H */
diff --git a/drivers/staging/dream/qdsp5/adsp_6210.c b/drivers/staging/dream/qdsp5/adsp_6210.c
new file mode 100644 (file)
index 0000000..3cf4e99
--- /dev/null
@@ -0,0 +1,283 @@
+/* arch/arm/mach-msm/qdsp5/adsp_6210.h
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated.
+ *
+ * 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 "adsp.h"
+
+/* Firmware modules */
+typedef enum {
+       QDSP_MODULE_KERNEL,
+       QDSP_MODULE_AFETASK,
+       QDSP_MODULE_AUDPLAY0TASK,
+       QDSP_MODULE_AUDPLAY1TASK,
+       QDSP_MODULE_AUDPPTASK,
+       QDSP_MODULE_VIDEOTASK,
+       QDSP_MODULE_VIDEO_AAC_VOC,
+       QDSP_MODULE_PCM_DEC,
+       QDSP_MODULE_AUDIO_DEC_MP3,
+       QDSP_MODULE_AUDIO_DEC_AAC,
+       QDSP_MODULE_AUDIO_DEC_WMA,
+       QDSP_MODULE_HOSTPCM,
+       QDSP_MODULE_DTMF,
+       QDSP_MODULE_AUDRECTASK,
+       QDSP_MODULE_AUDPREPROCTASK,
+       QDSP_MODULE_SBC_ENC,
+       QDSP_MODULE_VOC,
+       QDSP_MODULE_VOC_PCM,
+       QDSP_MODULE_VOCENCTASK,
+       QDSP_MODULE_VOCDECTASK,
+       QDSP_MODULE_VOICEPROCTASK,
+       QDSP_MODULE_VIDEOENCTASK,
+       QDSP_MODULE_VFETASK,
+       QDSP_MODULE_WAV_ENC,
+       QDSP_MODULE_AACLC_ENC,
+       QDSP_MODULE_VIDEO_AMR,
+       QDSP_MODULE_VOC_AMR,
+       QDSP_MODULE_VOC_EVRC,
+       QDSP_MODULE_VOC_13K,
+       QDSP_MODULE_VOC_FGV,
+       QDSP_MODULE_DIAGTASK,
+       QDSP_MODULE_JPEGTASK,
+       QDSP_MODULE_LPMTASK,
+       QDSP_MODULE_QCAMTASK,
+       QDSP_MODULE_MODMATHTASK,
+       QDSP_MODULE_AUDPLAY2TASK,
+       QDSP_MODULE_AUDPLAY3TASK,
+       QDSP_MODULE_AUDPLAY4TASK,
+       QDSP_MODULE_GRAPHICSTASK,
+       QDSP_MODULE_MIDI,
+       QDSP_MODULE_GAUDIO,
+       QDSP_MODULE_VDEC_LP_MODE,
+       QDSP_MODULE_MAX,
+} qdsp_module_type;
+
+#define QDSP_RTOS_MAX_TASK_ID  19U
+
+/* Table of modules indexed by task ID for the GAUDIO image */
+static qdsp_module_type qdsp_gaudio_task_to_module_table[] = {
+       QDSP_MODULE_KERNEL,
+       QDSP_MODULE_AFETASK,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_AUDPPTASK,
+       QDSP_MODULE_AUDPLAY0TASK,
+       QDSP_MODULE_AUDPLAY1TASK,
+       QDSP_MODULE_AUDPLAY2TASK,
+       QDSP_MODULE_AUDPLAY3TASK,
+       QDSP_MODULE_AUDPLAY4TASK,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_AUDRECTASK,
+       QDSP_MODULE_AUDPREPROCTASK,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_GRAPHICSTASK,
+       QDSP_MODULE_MAX
+};
+
+/* Queue offset table indexed by queue ID for the GAUDIO image */
+static uint32_t qdsp_gaudio_queue_offset_table[] = {
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_lpmCommandQueue              */
+       0x3be,               /* QDSP_mpuAfeQueue                  */
+       0x3ee,               /* QDSP_mpuGraphicsCmdQueue          */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_mpuModmathCmdQueue           */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_mpuVDecCmdQueue              */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_mpuVDecPktQueue              */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_mpuVEncCmdQueue              */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_rxMpuDecCmdQueue             */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_rxMpuDecPktQueue             */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_txMpuEncQueue                */
+       0x3c2,               /* QDSP_uPAudPPCmd1Queue             */
+       0x3c6,               /* QDSP_uPAudPPCmd2Queue             */
+       0x3ca,               /* QDSP_uPAudPPCmd3Queue             */
+       0x3da,               /* QDSP_uPAudPlay0BitStreamCtrlQueue */
+       0x3de,               /* QDSP_uPAudPlay1BitStreamCtrlQueue */
+       0x3e2,               /* QDSP_uPAudPlay2BitStreamCtrlQueue */
+       0x3e6,               /* QDSP_uPAudPlay3BitStreamCtrlQueue */
+       0x3ea,               /* QDSP_uPAudPlay4BitStreamCtrlQueue */
+       0x3ce,               /* QDSP_uPAudPreProcCmdQueue         */
+       0x3d6,               /* QDSP_uPAudRecBitStreamQueue       */
+       0x3d2,               /* QDSP_uPAudRecCmdQueue             */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPJpegActionCmdQueue         */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPJpegCfgCmdQueue            */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPVocProcQueue               */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_vfeCommandQueue              */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_vfeCommandScaleQueue         */
+       QDSP_RTOS_NO_QUEUE   /* QDSP_vfeCommandTableQueue         */
+};
+
+/* Table of modules indexed by task ID for the COMBO image */
+static qdsp_module_type qdsp_combo_task_to_module_table[] = {
+       QDSP_MODULE_KERNEL,
+       QDSP_MODULE_AFETASK,
+       QDSP_MODULE_VOCDECTASK,
+       QDSP_MODULE_VOCENCTASK,
+       QDSP_MODULE_VIDEOTASK,
+       QDSP_MODULE_VIDEOENCTASK,
+       QDSP_MODULE_VOICEPROCTASK,
+       QDSP_MODULE_VFETASK,
+       QDSP_MODULE_JPEGTASK,
+       QDSP_MODULE_AUDPPTASK,
+       QDSP_MODULE_AUDPLAY0TASK,
+       QDSP_MODULE_AUDPLAY1TASK,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_LPMTASK,
+       QDSP_MODULE_AUDRECTASK,
+       QDSP_MODULE_AUDPREPROCTASK,
+       QDSP_MODULE_MODMATHTASK,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX
+};
+
+/* Queue offset table indexed by queue ID for the COMBO image */
+static uint32_t qdsp_combo_queue_offset_table[] = {
+       0x585,               /* QDSP_lpmCommandQueue              */
+       0x52d,               /* QDSP_mpuAfeQueue                  */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_mpuGraphicsCmdQueue          */
+       0x541,               /* QDSP_mpuModmathCmdQueue           */
+       0x555,               /* QDSP_mpuVDecCmdQueue              */
+       0x559,               /* QDSP_mpuVDecPktQueue              */
+       0x551,               /* QDSP_mpuVEncCmdQueue              */
+       0x535,               /* QDSP_rxMpuDecCmdQueue             */
+       0x539,               /* QDSP_rxMpuDecPktQueue             */
+       0x53d,               /* QDSP_txMpuEncQueue                */
+       0x55d,               /* QDSP_uPAudPPCmd1Queue             */
+       0x561,               /* QDSP_uPAudPPCmd2Queue             */
+       0x565,               /* QDSP_uPAudPPCmd3Queue             */
+       0x575,               /* QDSP_uPAudPlay0BitStreamCtrlQueue */
+       0x579,               /* QDSP_uPAudPlay1BitStreamCtrlQueue */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPAudPlay2BitStreamCtrlQueue */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPAudPlay3BitStreamCtrlQueue */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPAudPlay4BitStreamCtrlQueue */
+       0x569,               /* QDSP_uPAudPreProcCmdQueue         */
+       0x571,               /* QDSP_uPAudRecBitStreamQueue       */
+       0x56d,               /* QDSP_uPAudRecCmdQueue             */
+       0x581,               /* QDSP_uPJpegActionCmdQueue         */
+       0x57d,               /* QDSP_uPJpegCfgCmdQueue            */
+       0x531,               /* QDSP_uPVocProcQueue               */
+       0x545,               /* QDSP_vfeCommandQueue              */
+       0x54d,               /* QDSP_vfeCommandScaleQueue         */
+       0x549                /* QDSP_vfeCommandTableQueue         */
+};
+
+/* Table of modules indexed by task ID for the QTV_LP image */
+static qdsp_module_type qdsp_qtv_lp_task_to_module_table[] = {
+       QDSP_MODULE_KERNEL,
+       QDSP_MODULE_AFETASK,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_VIDEOTASK,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_AUDPPTASK,
+       QDSP_MODULE_AUDPLAY0TASK,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_AUDRECTASK,
+       QDSP_MODULE_AUDPREPROCTASK,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX
+};
+
+/* Queue offset table indexed by queue ID for the QTV_LP image */
+static uint32_t qdsp_qtv_lp_queue_offset_table[] = {
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_lpmCommandQueue              */
+       0x40c,               /* QDSP_mpuAfeQueue                  */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_mpuGraphicsCmdQueue          */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_mpuModmathCmdQueue           */
+       0x410,               /* QDSP_mpuVDecCmdQueue              */
+       0x414,               /* QDSP_mpuVDecPktQueue              */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_mpuVEncCmdQueue              */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_rxMpuDecCmdQueue             */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_rxMpuDecPktQueue             */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_txMpuEncQueue                */
+       0x41c,               /* QDSP_uPAudPPCmd1Queue             */
+       0x420,               /* QDSP_uPAudPPCmd2Queue             */
+       0x424,               /* QDSP_uPAudPPCmd3Queue             */
+       0x430,               /* QDSP_uPAudPlay0BitStreamCtrlQueue */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPAudPlay1BitStreamCtrlQueue */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPAudPlay2BitStreamCtrlQueue */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPAudPlay3BitStreamCtrlQueue */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPAudPlay4BitStreamCtrlQueue */
+       0x418,               /* QDSP_uPAudPreProcCmdQueue         */
+       0x42c,               /* QDSP_uPAudRecBitStreamQueue       */
+       0x428,               /* QDSP_uPAudRecCmdQueue             */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPJpegActionCmdQueue         */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPJpegCfgCmdQueue            */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPVocProcQueue               */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_vfeCommandQueue              */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_vfeCommandScaleQueue         */
+       QDSP_RTOS_NO_QUEUE   /* QDSP_vfeCommandTableQueue         */
+};
+
+/* Tables to convert tasks to modules */
+static uint32_t *qdsp_task_to_module[] = {
+       qdsp_combo_task_to_module_table,
+       qdsp_gaudio_task_to_module_table,
+       qdsp_qtv_lp_task_to_module_table,
+};
+
+/* Tables to retrieve queue offsets */
+static uint32_t *qdsp_queue_offset_table[] = {
+       qdsp_combo_queue_offset_table,
+       qdsp_gaudio_queue_offset_table,
+       qdsp_qtv_lp_queue_offset_table,
+};
+
+#define QDSP_MODULE(n) \
+       { .name = #n, .pdev_name = "adsp_" #n, .id = QDSP_MODULE_##n }
+
+static struct adsp_module_info module_info[] = {
+       QDSP_MODULE(AUDPPTASK),
+       QDSP_MODULE(AUDRECTASK),
+       QDSP_MODULE(AUDPREPROCTASK),
+       QDSP_MODULE(VFETASK),
+       QDSP_MODULE(QCAMTASK),
+       QDSP_MODULE(LPMTASK),
+       QDSP_MODULE(JPEGTASK),
+       QDSP_MODULE(VIDEOTASK),
+       QDSP_MODULE(VDEC_LP_MODE),
+};
+
+int adsp_init_info(struct adsp_info *info)
+{
+       info->send_irq =   0x00c00200;
+       info->read_ctrl =  0x00400038;
+       info->write_ctrl = 0x00400034;
+
+       info->max_msg16_size = 193;
+       info->max_msg32_size = 8;
+
+       info->max_task_id = 16;
+       info->max_module_id = QDSP_MODULE_MAX - 1;
+       info->max_queue_id = QDSP_QUEUE_MAX;
+       info->max_image_id = 2;
+       info->queue_offset = qdsp_queue_offset_table;
+       info->task_to_module = qdsp_task_to_module;
+
+       info->module_count = ARRAY_SIZE(module_info);
+       info->module = module_info;
+       return 0;
+}
diff --git a/drivers/staging/dream/qdsp5/adsp_6220.c b/drivers/staging/dream/qdsp5/adsp_6220.c
new file mode 100644 (file)
index 0000000..02225cd
--- /dev/null
@@ -0,0 +1,284 @@
+/* arch/arm/mach-msm/qdsp5/adsp_6220.h
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated.
+ *
+ * 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 "adsp.h"
+
+/* Firmware modules */
+typedef enum {
+       QDSP_MODULE_KERNEL,
+       QDSP_MODULE_AFETASK,
+       QDSP_MODULE_AUDPLAY0TASK,
+       QDSP_MODULE_AUDPLAY1TASK,
+       QDSP_MODULE_AUDPPTASK,
+       QDSP_MODULE_VIDEOTASK,
+       QDSP_MODULE_VIDEO_AAC_VOC,
+       QDSP_MODULE_PCM_DEC,
+       QDSP_MODULE_AUDIO_DEC_MP3,
+       QDSP_MODULE_AUDIO_DEC_AAC,
+       QDSP_MODULE_AUDIO_DEC_WMA,
+       QDSP_MODULE_HOSTPCM,
+       QDSP_MODULE_DTMF,
+       QDSP_MODULE_AUDRECTASK,
+       QDSP_MODULE_AUDPREPROCTASK,
+       QDSP_MODULE_SBC_ENC,
+       QDSP_MODULE_VOC,
+       QDSP_MODULE_VOC_PCM,
+       QDSP_MODULE_VOCENCTASK,
+       QDSP_MODULE_VOCDECTASK,
+       QDSP_MODULE_VOICEPROCTASK,
+       QDSP_MODULE_VIDEOENCTASK,
+       QDSP_MODULE_VFETASK,
+       QDSP_MODULE_WAV_ENC,
+       QDSP_MODULE_AACLC_ENC,
+       QDSP_MODULE_VIDEO_AMR,
+       QDSP_MODULE_VOC_AMR,
+       QDSP_MODULE_VOC_EVRC,
+       QDSP_MODULE_VOC_13K,
+       QDSP_MODULE_VOC_FGV,
+       QDSP_MODULE_DIAGTASK,
+       QDSP_MODULE_JPEGTASK,
+       QDSP_MODULE_LPMTASK,
+       QDSP_MODULE_QCAMTASK,
+       QDSP_MODULE_MODMATHTASK,
+       QDSP_MODULE_AUDPLAY2TASK,
+       QDSP_MODULE_AUDPLAY3TASK,
+       QDSP_MODULE_AUDPLAY4TASK,
+       QDSP_MODULE_GRAPHICSTASK,
+       QDSP_MODULE_MIDI,
+       QDSP_MODULE_GAUDIO,
+       QDSP_MODULE_VDEC_LP_MODE,
+       QDSP_MODULE_MAX,
+} qdsp_module_type;
+
+#define QDSP_RTOS_MAX_TASK_ID  19U
+
+/* Table of modules indexed by task ID for the GAUDIO image */
+static qdsp_module_type qdsp_gaudio_task_to_module_table[] = {
+       QDSP_MODULE_KERNEL,
+       QDSP_MODULE_AFETASK,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_AUDPPTASK,
+       QDSP_MODULE_AUDPLAY0TASK,
+       QDSP_MODULE_AUDPLAY1TASK,
+       QDSP_MODULE_AUDPLAY2TASK,
+       QDSP_MODULE_AUDPLAY3TASK,
+       QDSP_MODULE_AUDPLAY4TASK,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_AUDRECTASK,
+       QDSP_MODULE_AUDPREPROCTASK,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_GRAPHICSTASK,
+       QDSP_MODULE_MAX
+};
+
+/* Queue offset table indexed by queue ID for the GAUDIO image */
+static uint32_t qdsp_gaudio_queue_offset_table[] = {
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_lpmCommandQueue              */
+       0x3f0,               /* QDSP_mpuAfeQueue                  */
+       0x420,               /* QDSP_mpuGraphicsCmdQueue          */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_mpuModmathCmdQueue           */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_mpuVDecCmdQueue              */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_mpuVDecPktQueue              */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_mpuVEncCmdQueue              */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_rxMpuDecCmdQueue             */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_rxMpuDecPktQueue             */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_txMpuEncQueue                */
+       0x3f4,               /* QDSP_uPAudPPCmd1Queue             */
+       0x3f8,               /* QDSP_uPAudPPCmd2Queue             */
+       0x3fc,               /* QDSP_uPAudPPCmd3Queue             */
+       0x40c,               /* QDSP_uPAudPlay0BitStreamCtrlQueue */
+       0x410,               /* QDSP_uPAudPlay1BitStreamCtrlQueue */
+       0x414,               /* QDSP_uPAudPlay2BitStreamCtrlQueue */
+       0x418,               /* QDSP_uPAudPlay3BitStreamCtrlQueue */
+       0x41c,               /* QDSP_uPAudPlay4BitStreamCtrlQueue */
+       0x400,               /* QDSP_uPAudPreProcCmdQueue         */
+       0x408,               /* QDSP_uPAudRecBitStreamQueue       */
+       0x404,               /* QDSP_uPAudRecCmdQueue             */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPJpegActionCmdQueue         */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPJpegCfgCmdQueue            */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPVocProcQueue               */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_vfeCommandQueue              */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_vfeCommandScaleQueue         */
+       QDSP_RTOS_NO_QUEUE   /* QDSP_vfeCommandTableQueue         */
+};
+
+/* Table of modules indexed by task ID for the COMBO image */
+static qdsp_module_type qdsp_combo_task_to_module_table[] = {
+       QDSP_MODULE_KERNEL,
+       QDSP_MODULE_AFETASK,
+       QDSP_MODULE_VOCDECTASK,
+       QDSP_MODULE_VOCENCTASK,
+       QDSP_MODULE_VIDEOTASK,
+       QDSP_MODULE_VIDEOENCTASK,
+       QDSP_MODULE_VOICEPROCTASK,
+       QDSP_MODULE_VFETASK,
+       QDSP_MODULE_JPEGTASK,
+       QDSP_MODULE_AUDPPTASK,
+       QDSP_MODULE_AUDPLAY0TASK,
+       QDSP_MODULE_AUDPLAY1TASK,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_LPMTASK,
+       QDSP_MODULE_AUDRECTASK,
+       QDSP_MODULE_AUDPREPROCTASK,
+       QDSP_MODULE_MODMATHTASK,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX
+};
+
+/* Queue offset table indexed by queue ID for the COMBO image */
+static uint32_t qdsp_combo_queue_offset_table[] = {
+       0x6f2,               /* QDSP_lpmCommandQueue              */
+       0x69e,               /* QDSP_mpuAfeQueue                  */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_mpuGraphicsCmdQueue          */
+       0x6b2,               /* QDSP_mpuModmathCmdQueue           */
+       0x6c6,               /* QDSP_mpuVDecCmdQueue              */
+       0x6ca,               /* QDSP_mpuVDecPktQueue              */
+       0x6c2,               /* QDSP_mpuVEncCmdQueue              */
+       0x6a6,               /* QDSP_rxMpuDecCmdQueue             */
+       0x6aa,               /* QDSP_rxMpuDecPktQueue             */
+       0x6ae,               /* QDSP_txMpuEncQueue                */
+       0x6ce,               /* QDSP_uPAudPPCmd1Queue             */
+       0x6d2,               /* QDSP_uPAudPPCmd2Queue             */
+       0x6d6,               /* QDSP_uPAudPPCmd3Queue             */
+       0x6e6,               /* QDSP_uPAudPlay0BitStreamCtrlQueue */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPAudPlay1BitStreamCtrlQueue */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPAudPlay2BitStreamCtrlQueue */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPAudPlay3BitStreamCtrlQueue */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPAudPlay4BitStreamCtrlQueue */
+       0x6da,               /* QDSP_uPAudPreProcCmdQueue         */
+       0x6e2,               /* QDSP_uPAudRecBitStreamQueue       */
+       0x6de,               /* QDSP_uPAudRecCmdQueue             */
+       0x6ee,               /* QDSP_uPJpegActionCmdQueue         */
+       0x6ea,               /* QDSP_uPJpegCfgCmdQueue            */
+       0x6a2,               /* QDSP_uPVocProcQueue               */
+       0x6b6,               /* QDSP_vfeCommandQueue              */
+       0x6be,               /* QDSP_vfeCommandScaleQueue         */
+       0x6ba                /* QDSP_vfeCommandTableQueue         */
+};
+
+/* Table of modules indexed by task ID for the QTV_LP image */
+static qdsp_module_type qdsp_qtv_lp_task_to_module_table[] = {
+       QDSP_MODULE_KERNEL,
+       QDSP_MODULE_AFETASK,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_VIDEOTASK,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_AUDPPTASK,
+       QDSP_MODULE_AUDPLAY0TASK,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_AUDRECTASK,
+       QDSP_MODULE_AUDPREPROCTASK,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX
+};
+
+/* Queue offset table indexed by queue ID for the QTV_LP image */
+static uint32_t qdsp_qtv_lp_queue_offset_table[] = {
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_lpmCommandQueue              */
+       0x430,               /* QDSP_mpuAfeQueue                  */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_mpuGraphicsCmdQueue          */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_mpuModmathCmdQueue           */
+       0x434,               /* QDSP_mpuVDecCmdQueue              */
+       0x438,               /* QDSP_mpuVDecPktQueue              */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_mpuVEncCmdQueue              */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_rxMpuDecCmdQueue             */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_rxMpuDecPktQueue             */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_txMpuEncQueue                */
+       0x440,               /* QDSP_uPAudPPCmd1Queue             */
+       0x444,               /* QDSP_uPAudPPCmd2Queue             */
+       0x448,               /* QDSP_uPAudPPCmd3Queue             */
+       0x454,               /* QDSP_uPAudPlay0BitStreamCtrlQueue */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPAudPlay1BitStreamCtrlQueue */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPAudPlay2BitStreamCtrlQueue */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPAudPlay3BitStreamCtrlQueue */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPAudPlay4BitStreamCtrlQueue */
+       0x43c,               /* QDSP_uPAudPreProcCmdQueue         */
+       0x450,               /* QDSP_uPAudRecBitStreamQueue       */
+       0x44c,               /* QDSP_uPAudRecCmdQueue             */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPJpegActionCmdQueue         */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPJpegCfgCmdQueue            */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPVocProcQueue               */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_vfeCommandQueue              */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_vfeCommandScaleQueue         */
+       QDSP_RTOS_NO_QUEUE   /* QDSP_vfeCommandTableQueue         */
+};
+
+/* Tables to convert tasks to modules */
+static qdsp_module_type *qdsp_task_to_module[] = {
+       qdsp_combo_task_to_module_table,
+       qdsp_gaudio_task_to_module_table,
+       qdsp_qtv_lp_task_to_module_table,
+};
+
+/* Tables to retrieve queue offsets */
+static uint32_t *qdsp_queue_offset_table[] = {
+       qdsp_combo_queue_offset_table,
+       qdsp_gaudio_queue_offset_table,
+       qdsp_qtv_lp_queue_offset_table,
+};
+
+#define QDSP_MODULE(n) \
+       { .name = #n, .pdev_name = "adsp_" #n, .id = QDSP_MODULE_##n }
+
+static struct adsp_module_info module_info[] = {
+       QDSP_MODULE(AUDPLAY0TASK),
+       QDSP_MODULE(AUDPPTASK),
+       QDSP_MODULE(AUDPREPROCTASK),
+       QDSP_MODULE(AUDRECTASK),
+       QDSP_MODULE(VFETASK),
+       QDSP_MODULE(QCAMTASK),
+       QDSP_MODULE(LPMTASK),
+       QDSP_MODULE(JPEGTASK),
+       QDSP_MODULE(VIDEOTASK),
+       QDSP_MODULE(VDEC_LP_MODE),
+};
+
+int adsp_init_info(struct adsp_info *info)
+{
+       info->send_irq =   0x00c00200;
+       info->read_ctrl =  0x00400038;
+       info->write_ctrl = 0x00400034;
+
+       info->max_msg16_size = 193;
+       info->max_msg32_size = 8;
+
+       info->max_task_id = 16;
+       info->max_module_id = QDSP_MODULE_MAX - 1;
+       info->max_queue_id = QDSP_QUEUE_MAX;
+       info->max_image_id = 2;
+       info->queue_offset = qdsp_queue_offset_table;
+       info->task_to_module = qdsp_task_to_module;
+
+       info->module_count = ARRAY_SIZE(module_info);
+       info->module = module_info;
+       return 0;
+}
diff --git a/drivers/staging/dream/qdsp5/adsp_6225.c b/drivers/staging/dream/qdsp5/adsp_6225.c
new file mode 100644 (file)
index 0000000..5078afb
--- /dev/null
@@ -0,0 +1,328 @@
+/* arch/arm/mach-msm/qdsp5/adsp_6225.h
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated.
+ *
+ * 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 "adsp.h"
+
+/* Firmware modules */
+typedef enum {
+       QDSP_MODULE_KERNEL,
+       QDSP_MODULE_AFETASK,
+       QDSP_MODULE_AUDPLAY0TASK,
+       QDSP_MODULE_AUDPLAY1TASK,
+       QDSP_MODULE_AUDPPTASK,
+       QDSP_MODULE_VIDEOTASK,
+       QDSP_MODULE_VIDEO_AAC_VOC,
+       QDSP_MODULE_PCM_DEC,
+       QDSP_MODULE_AUDIO_DEC_MP3,
+       QDSP_MODULE_AUDIO_DEC_AAC,
+       QDSP_MODULE_AUDIO_DEC_WMA,
+       QDSP_MODULE_HOSTPCM,
+       QDSP_MODULE_DTMF,
+       QDSP_MODULE_AUDRECTASK,
+       QDSP_MODULE_AUDPREPROCTASK,
+       QDSP_MODULE_SBC_ENC,
+       QDSP_MODULE_VOC_UMTS,
+       QDSP_MODULE_VOC_CDMA,
+       QDSP_MODULE_VOC_PCM,
+       QDSP_MODULE_VOCENCTASK,
+       QDSP_MODULE_VOCDECTASK,
+       QDSP_MODULE_VOICEPROCTASK,
+       QDSP_MODULE_VIDEOENCTASK,
+       QDSP_MODULE_VFETASK,
+       QDSP_MODULE_WAV_ENC,
+       QDSP_MODULE_AACLC_ENC,
+       QDSP_MODULE_VIDEO_AMR,
+       QDSP_MODULE_VOC_AMR,
+       QDSP_MODULE_VOC_EVRC,
+       QDSP_MODULE_VOC_13K,
+       QDSP_MODULE_VOC_FGV,
+       QDSP_MODULE_DIAGTASK,
+       QDSP_MODULE_JPEGTASK,
+       QDSP_MODULE_LPMTASK,
+       QDSP_MODULE_QCAMTASK,
+       QDSP_MODULE_MODMATHTASK,
+       QDSP_MODULE_AUDPLAY2TASK,
+       QDSP_MODULE_AUDPLAY3TASK,
+       QDSP_MODULE_AUDPLAY4TASK,
+       QDSP_MODULE_GRAPHICSTASK,
+       QDSP_MODULE_MIDI,
+       QDSP_MODULE_GAUDIO,
+       QDSP_MODULE_VDEC_LP_MODE,
+       QDSP_MODULE_MAX,
+} qdsp_module_type;
+
+#define QDSP_RTOS_MAX_TASK_ID  30U
+
+/* Table of modules indexed by task ID for the GAUDIO image */
+static qdsp_module_type qdsp_gaudio_task_to_module_table[] = {
+       QDSP_MODULE_KERNEL,
+       QDSP_MODULE_AFETASK,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_AUDPPTASK,
+       QDSP_MODULE_AUDPLAY0TASK,
+       QDSP_MODULE_AUDPLAY1TASK,
+       QDSP_MODULE_AUDPLAY2TASK,
+       QDSP_MODULE_AUDPLAY3TASK,
+       QDSP_MODULE_AUDPLAY4TASK,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_AUDRECTASK,
+       QDSP_MODULE_AUDPREPROCTASK,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_GRAPHICSTASK,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+};
+
+/* Queue offset table indexed by queue ID for the GAUDIO image */
+static uint32_t qdsp_gaudio_queue_offset_table[] = {
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_lpmCommandQueue              */
+       0x3f0,               /* QDSP_mpuAfeQueue                  */
+       0x420,               /* QDSP_mpuGraphicsCmdQueue          */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_mpuModmathCmdQueue           */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_mpuVDecCmdQueue              */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_mpuVDecPktQueue              */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_mpuVEncCmdQueue              */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_rxMpuDecCmdQueue             */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_rxMpuDecPktQueue             */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_txMpuEncQueue                */
+       0x3f4,               /* QDSP_uPAudPPCmd1Queue             */
+       0x3f8,               /* QDSP_uPAudPPCmd2Queue             */
+       0x3fc,               /* QDSP_uPAudPPCmd3Queue             */
+       0x40c,               /* QDSP_uPAudPlay0BitStreamCtrlQueue */
+       0x410,               /* QDSP_uPAudPlay1BitStreamCtrlQueue */
+       0x414,               /* QDSP_uPAudPlay2BitStreamCtrlQueue */
+       0x418,               /* QDSP_uPAudPlay3BitStreamCtrlQueue */
+       0x41c,               /* QDSP_uPAudPlay4BitStreamCtrlQueue */
+       0x400,               /* QDSP_uPAudPreProcCmdQueue         */
+       0x408,               /* QDSP_uPAudRecBitStreamQueue       */
+       0x404,               /* QDSP_uPAudRecCmdQueue             */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPJpegActionCmdQueue         */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPJpegCfgCmdQueue            */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPVocProcQueue               */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_vfeCommandQueue              */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_vfeCommandScaleQueue         */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_vfeCommandTableQueue         */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPDiagQueue                  */
+};
+
+/* Table of modules indexed by task ID for the COMBO image */
+static qdsp_module_type qdsp_combo_task_to_module_table[] = {
+       QDSP_MODULE_KERNEL,
+       QDSP_MODULE_AFETASK,
+       QDSP_MODULE_VOCDECTASK,
+       QDSP_MODULE_VOCENCTASK,
+       QDSP_MODULE_VIDEOTASK,
+       QDSP_MODULE_VIDEOENCTASK,
+       QDSP_MODULE_VOICEPROCTASK,
+       QDSP_MODULE_VFETASK,
+       QDSP_MODULE_JPEGTASK,
+       QDSP_MODULE_AUDPPTASK,
+       QDSP_MODULE_AUDPLAY0TASK,
+       QDSP_MODULE_AUDPLAY1TASK,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_LPMTASK,
+       QDSP_MODULE_AUDRECTASK,
+       QDSP_MODULE_AUDPREPROCTASK,
+       QDSP_MODULE_MODMATHTASK,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_DIAGTASK,
+       QDSP_MODULE_MAX,
+};
+
+/* Queue offset table indexed by queue ID for the COMBO image */
+static uint32_t qdsp_combo_queue_offset_table[] = {
+       0x714,               /* QDSP_lpmCommandQueue              */
+       0x6bc,               /* QDSP_mpuAfeQueue                  */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_mpuGraphicsCmdQueue          */
+       0x6d0,               /* QDSP_mpuModmathCmdQueue           */
+       0x6e8,               /* QDSP_mpuVDecCmdQueue              */
+       0x6ec,               /* QDSP_mpuVDecPktQueue              */
+       0x6e4,               /* QDSP_mpuVEncCmdQueue              */
+       0x6c4,               /* QDSP_rxMpuDecCmdQueue             */
+       0x6c8,               /* QDSP_rxMpuDecPktQueue             */
+       0x6cc,               /* QDSP_txMpuEncQueue                */
+       0x6f0,               /* QDSP_uPAudPPCmd1Queue             */
+       0x6f4,               /* QDSP_uPAudPPCmd2Queue             */
+       0x6f8,               /* QDSP_uPAudPPCmd3Queue             */
+       0x708,               /* QDSP_uPAudPlay0BitStreamCtrlQueue */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPAudPlay1BitStreamCtrlQueue */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPAudPlay2BitStreamCtrlQueue */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPAudPlay3BitStreamCtrlQueue */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPAudPlay4BitStreamCtrlQueue */
+       0x6fc,               /* QDSP_uPAudPreProcCmdQueue         */
+       0x704,               /* QDSP_uPAudRecBitStreamQueue       */
+       0x700,               /* QDSP_uPAudRecCmdQueue             */
+       0x710,               /* QDSP_uPJpegActionCmdQueue         */
+       0x70c,               /* QDSP_uPJpegCfgCmdQueue            */
+       0x6c0,               /* QDSP_uPVocProcQueue               */
+       0x6d8,               /* QDSP_vfeCommandQueue              */
+       0x6e0,               /* QDSP_vfeCommandScaleQueue         */
+       0x6dc,               /* QDSP_vfeCommandTableQueue         */
+       0x6d4,               /* QDSP_uPDiagQueue                  */
+};
+
+/* Table of modules indexed by task ID for the QTV_LP image */
+static qdsp_module_type qdsp_qtv_lp_task_to_module_table[] = {
+       QDSP_MODULE_KERNEL,
+       QDSP_MODULE_AFETASK,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_VIDEOTASK,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_AUDPPTASK,
+       QDSP_MODULE_AUDPLAY0TASK,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_AUDRECTASK,
+       QDSP_MODULE_AUDPREPROCTASK,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+       QDSP_MODULE_MAX,
+};
+
+/* Queue offset table indexed by queue ID for the QTV_LP image */
+static uint32_t qdsp_qtv_lp_queue_offset_table[] = {
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_lpmCommandQueue              */
+       0x3fe,               /* QDSP_mpuAfeQueue                  */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_mpuGraphicsCmdQueue          */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_mpuModmathCmdQueue           */
+       0x402,               /* QDSP_mpuVDecCmdQueue              */
+       0x406,               /* QDSP_mpuVDecPktQueue              */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_mpuVEncCmdQueue              */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_rxMpuDecCmdQueue             */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_rxMpuDecPktQueue             */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_txMpuEncQueue                */
+       0x40e,               /* QDSP_uPAudPPCmd1Queue             */
+       0x412,               /* QDSP_uPAudPPCmd2Queue             */
+       0x416,               /* QDSP_uPAudPPCmd3Queue             */
+       0x422,               /* QDSP_uPAudPlay0BitStreamCtrlQueue */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPAudPlay1BitStreamCtrlQueue */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPAudPlay2BitStreamCtrlQueue */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPAudPlay3BitStreamCtrlQueue */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPAudPlay4BitStreamCtrlQueue */
+       0x40a,               /* QDSP_uPAudPreProcCmdQueue         */
+       0x41e,               /* QDSP_uPAudRecBitStreamQueue       */
+       0x41a,               /* QDSP_uPAudRecCmdQueue             */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPJpegActionCmdQueue         */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPJpegCfgCmdQueue            */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPVocProcQueue               */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_vfeCommandQueue              */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_vfeCommandScaleQueue         */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_vfeCommandTableQueue         */
+       QDSP_RTOS_NO_QUEUE,  /* QDSP_uPDiagQueue                  */
+};
+
+/* Tables to convert tasks to modules */
+static qdsp_module_type *qdsp_task_to_module[] = {
+       qdsp_combo_task_to_module_table,
+       qdsp_gaudio_task_to_module_table,
+       qdsp_qtv_lp_task_to_module_table,
+};
+
+/* Tables to retrieve queue offsets */
+static uint32_t *qdsp_queue_offset_table[] = {
+       qdsp_combo_queue_offset_table,
+       qdsp_gaudio_queue_offset_table,
+       qdsp_qtv_lp_queue_offset_table,
+};
+
+#define QDSP_MODULE(n, clkname, clkrate, verify_cmd_func, patch_event_func) \
+       { .name = #n, .pdev_name = "adsp_" #n, .id = QDSP_MODULE_##n, \
+         .clk_name = clkname, .clk_rate = clkrate, \
+         .verify_cmd = verify_cmd_func, .patch_event = patch_event_func }
+
+static struct adsp_module_info module_info[] = {
+       QDSP_MODULE(AUDPLAY0TASK, NULL, 0, NULL, NULL),
+       QDSP_MODULE(AUDPPTASK, NULL, 0, NULL, NULL),
+       QDSP_MODULE(AUDRECTASK, NULL, 0, NULL, NULL),
+       QDSP_MODULE(AUDPREPROCTASK, NULL, 0, NULL, NULL),
+       QDSP_MODULE(VFETASK, "vfe_clk", 0, adsp_vfe_verify_cmd,
+               adsp_vfe_patch_event),
+       QDSP_MODULE(QCAMTASK, NULL, 0, NULL, NULL),
+       QDSP_MODULE(LPMTASK, NULL, 0, adsp_lpm_verify_cmd, NULL),
+       QDSP_MODULE(JPEGTASK, "vdc_clk", 0, adsp_jpeg_verify_cmd,
+               adsp_jpeg_patch_event),
+       QDSP_MODULE(VIDEOTASK, "vdc_clk", 96000000,
+               adsp_video_verify_cmd, NULL),
+       QDSP_MODULE(VDEC_LP_MODE, NULL, 0, NULL, NULL),
+       QDSP_MODULE(VIDEOENCTASK, "vdc_clk", 96000000,
+               adsp_videoenc_verify_cmd, NULL),
+};
+
+int adsp_init_info(struct adsp_info *info)
+{
+       info->send_irq =   0x00c00200;
+       info->read_ctrl =  0x00400038;
+       info->write_ctrl = 0x00400034;
+
+       info->max_msg16_size = 193;
+       info->max_msg32_size = 8;
+
+       info->max_task_id = 16;
+       info->max_module_id = QDSP_MODULE_MAX - 1;
+       info->max_queue_id = QDSP_QUEUE_MAX;
+       info->max_image_id = 2;
+       info->queue_offset = qdsp_queue_offset_table;
+       info->task_to_module = qdsp_task_to_module;
+
+       info->module_count = ARRAY_SIZE(module_info);
+       info->module = module_info;
+       return 0;
+}
diff --git a/drivers/staging/dream/qdsp5/adsp_driver.c b/drivers/staging/dream/qdsp5/adsp_driver.c
new file mode 100644 (file)
index 0000000..e55a0db
--- /dev/null
@@ -0,0 +1,641 @@
+/* arch/arm/mach-msm/qdsp5/adsp_driver.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Iliyan Malchev <ibm@android.com>
+ *
+ * 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/cdev.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+
+#include "adsp.h"
+
+#include <linux/msm_adsp.h>
+#include <linux/android_pmem.h>
+
+struct adsp_pmem_region {
+       struct hlist_node list;
+       void *vaddr;
+       unsigned long paddr;
+       unsigned long kvaddr;
+       unsigned long len;
+       struct file *file;
+};
+
+struct adsp_device {
+       struct msm_adsp_module *module;
+
+       spinlock_t event_queue_lock;
+       wait_queue_head_t event_wait;
+       struct list_head event_queue;
+       int abort;
+
+       const char *name;
+       struct device *device;
+       struct cdev cdev;
+};
+
+static struct adsp_device *inode_to_device(struct inode *inode);
+
+#define __CONTAINS(r, v, l) ({                                 \
+       typeof(r) __r = r;                                      \
+       typeof(v) __v = v;                                      \
+       typeof(v) __e = __v + l;                                \
+       int res = __v >= __r->vaddr &&                          \
+               __e <= __r->vaddr + __r->len;                   \
+       res;                                                    \
+})
+
+#define CONTAINS(r1, r2) ({                                    \
+       typeof(r2) __r2 = r2;                                   \
+       __CONTAINS(r1, __r2->vaddr, __r2->len);                 \
+})
+
+#define IN_RANGE(r, v) ({                                      \
+       typeof(r) __r = r;                                      \
+       typeof(v) __vv = v;                                     \
+       int res = ((__vv >= __r->vaddr) &&                      \
+               (__vv < (__r->vaddr + __r->len)));              \
+       res;                                                    \
+})
+
+#define OVERLAPS(r1, r2) ({                                    \
+       typeof(r1) __r1 = r1;                                   \
+       typeof(r2) __r2 = r2;                                   \
+       typeof(__r2->vaddr) __v = __r2->vaddr;                  \
+       typeof(__v) __e = __v + __r2->len - 1;                  \
+       int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \
+       res;                                                    \
+})
+
+static int adsp_pmem_check(struct msm_adsp_module *module,
+               void *vaddr, unsigned long len)
+{
+       struct adsp_pmem_region *region_elt;
+       struct hlist_node *node;
+       struct adsp_pmem_region t = { .vaddr = vaddr, .len = len };
+
+       hlist_for_each_entry(region_elt, node, &module->pmem_regions, list) {
+               if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) ||
+                   OVERLAPS(region_elt, &t)) {
+                       printk(KERN_ERR "adsp: module %s:"
+                               " region (vaddr %p len %ld)"
+                               " clashes with registered region"
+                               " (vaddr %p paddr %p len %ld)\n",
+                               module->name,
+                               vaddr, len,
+                               region_elt->vaddr,
+                               (void *)region_elt->paddr,
+                               region_elt->len);
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static int adsp_pmem_add(struct msm_adsp_module *module,
+                        struct adsp_pmem_info *info)
+{
+       unsigned long paddr, kvaddr, len;
+       struct file *file;
+       struct adsp_pmem_region *region;
+       int rc = -EINVAL;
+
+       mutex_lock(&module->pmem_regions_lock);
+       region = kmalloc(sizeof(*region), GFP_KERNEL);
+       if (!region) {
+               rc = -ENOMEM;
+               goto end;
+       }
+       INIT_HLIST_NODE(&region->list);
+       if (get_pmem_file(info->fd, &paddr, &kvaddr, &len, &file)) {
+               kfree(region);
+               goto end;
+       }
+
+       rc = adsp_pmem_check(module, info->vaddr, len);
+       if (rc < 0) {
+               put_pmem_file(file);
+               kfree(region);
+               goto end;
+       }
+
+       region->vaddr = info->vaddr;
+       region->paddr = paddr;
+       region->kvaddr = kvaddr;
+       region->len = len;
+       region->file = file;
+
+       hlist_add_head(&region->list, &module->pmem_regions);
+end:
+       mutex_unlock(&module->pmem_regions_lock);
+       return rc;
+}
+
+static int adsp_pmem_lookup_vaddr(struct msm_adsp_module *module, void **addr,
+                    unsigned long len, struct adsp_pmem_region **region)
+{
+       struct hlist_node *node;
+       void *vaddr = *addr;
+       struct adsp_pmem_region *region_elt;
+
+       int match_count = 0;
+
+       *region = NULL;
+
+       /* returns physical address or zero */
+       hlist_for_each_entry(region_elt, node, &module->pmem_regions, list) {
+               if (vaddr >= region_elt->vaddr &&
+                   vaddr < region_elt->vaddr + region_elt->len &&
+                   vaddr + len <= region_elt->vaddr + region_elt->len) {
+                       /* offset since we could pass vaddr inside a registerd
+                        * pmem buffer
+                        */
+
+                       match_count++;
+                       if (!*region)
+                               *region = region_elt;
+               }
+       }
+
+       if (match_count > 1) {
+               printk(KERN_ERR "adsp: module %s: "
+                       "multiple hits for vaddr %p, len %ld\n",
+                       module->name, vaddr, len);
+               hlist_for_each_entry(region_elt, node,
+                               &module->pmem_regions, list) {
+                       if (vaddr >= region_elt->vaddr &&
+                           vaddr < region_elt->vaddr + region_elt->len &&
+                           vaddr + len <= region_elt->vaddr + region_elt->len)
+                               printk(KERN_ERR "\t%p, %ld --> %p\n",
+                                       region_elt->vaddr,
+                                       region_elt->len,
+                                       (void *)region_elt->paddr);
+               }
+       }
+
+       return *region ? 0 : -1;
+}
+
+int adsp_pmem_fixup_kvaddr(struct msm_adsp_module *module, void **addr,
+                          unsigned long *kvaddr, unsigned long len)
+{
+       struct adsp_pmem_region *region;
+       void *vaddr = *addr;
+       unsigned long *paddr = (unsigned long *)addr;
+       int ret;
+
+       ret = adsp_pmem_lookup_vaddr(module, addr, len, &region);
+       if (ret) {
+               printk(KERN_ERR "adsp: not patching %s (paddr & kvaddr),"
+                       " lookup (%p, %ld) failed\n",
+                       module->name, vaddr, len);
+               return ret;
+       }
+       *paddr = region->paddr + (vaddr - region->vaddr);
+       *kvaddr = region->kvaddr + (vaddr - region->vaddr);
+       return 0;
+}
+
+int adsp_pmem_fixup(struct msm_adsp_module *module, void **addr,
+                   unsigned long len)
+{
+       struct adsp_pmem_region *region;
+       void *vaddr = *addr;
+       unsigned long *paddr = (unsigned long *)addr;
+       int ret;
+
+       ret = adsp_pmem_lookup_vaddr(module, addr, len, &region);
+       if (ret) {
+               printk(KERN_ERR "adsp: not patching %s, lookup (%p, %ld) failed\n",
+                       module->name, vaddr, len);
+               return ret;
+       }
+
+       *paddr = region->paddr + (vaddr - region->vaddr);
+       return 0;
+}
+
+static int adsp_verify_cmd(struct msm_adsp_module *module,
+                          unsigned int queue_id, void *cmd_data,
+                          size_t cmd_size)
+{
+       /* call the per module verifier */
+       if (module->verify_cmd)
+               return module->verify_cmd(module, queue_id, cmd_data,
+                                            cmd_size);
+       else
+               printk(KERN_INFO "adsp: no packet verifying function "
+                                "for task %s\n", module->name);
+       return 0;
+}
+
+static long adsp_write_cmd(struct adsp_device *adev, void __user *arg)
+{
+       struct adsp_command_t cmd;
+       unsigned char buf[256];
+       void *cmd_data;
+       long rc;
+
+       if (copy_from_user(&cmd, (void __user *)arg, sizeof(cmd)))
+               return -EFAULT;
+
+       if (cmd.len > 256) {
+               cmd_data = kmalloc(cmd.len, GFP_USER);
+               if (!cmd_data)
+                       return -ENOMEM;
+       } else {
+               cmd_data = buf;
+       }
+
+       if (copy_from_user(cmd_data, (void __user *)(cmd.data), cmd.len)) {
+               rc = -EFAULT;
+               goto end;
+       }
+
+       mutex_lock(&adev->module->pmem_regions_lock);
+       if (adsp_verify_cmd(adev->module, cmd.queue, cmd_data, cmd.len)) {
+               printk(KERN_ERR "module %s: verify failed.\n",
+                       adev->module->name);
+               rc = -EINVAL;
+               goto end;
+       }
+       rc = msm_adsp_write(adev->module, cmd.queue, cmd_data, cmd.len);
+end:
+       mutex_unlock(&adev->module->pmem_regions_lock);
+
+       if (cmd.len > 256)
+               kfree(cmd_data);
+
+       return rc;
+}
+
+static int adsp_events_pending(struct adsp_device *adev)
+{
+       unsigned long flags;
+       int yes;
+       spin_lock_irqsave(&adev->event_queue_lock, flags);
+       yes = !list_empty(&adev->event_queue);
+       spin_unlock_irqrestore(&adev->event_queue_lock, flags);
+       return yes || adev->abort;
+}
+
+static int adsp_pmem_lookup_paddr(struct msm_adsp_module *module, void **addr,
+                    struct adsp_pmem_region **region)
+{
+       struct hlist_node *node;
+       unsigned long paddr = (unsigned long)(*addr);
+       struct adsp_pmem_region *region_elt;
+
+       hlist_for_each_entry(region_elt, node, &module->pmem_regions, list) {
+               if (paddr >= region_elt->paddr &&
+                   paddr < region_elt->paddr + region_elt->len) {
+                       *region = region_elt;
+                       return 0;
+               }
+       }
+       return -1;
+}
+
+int adsp_pmem_paddr_fixup(struct msm_adsp_module *module, void **addr)
+{
+       struct adsp_pmem_region *region;
+       unsigned long paddr = (unsigned long)(*addr);
+       unsigned long *vaddr = (unsigned long *)addr;
+       int ret;
+
+       ret = adsp_pmem_lookup_paddr(module, addr, &region);
+       if (ret) {
+               printk(KERN_ERR "adsp: not patching %s, paddr %p lookup failed\n",
+                       module->name, vaddr);
+               return ret;
+       }
+
+       *vaddr = (unsigned long)region->vaddr + (paddr - region->paddr);
+       return 0;
+}
+
+static int adsp_patch_event(struct msm_adsp_module *module,
+                               struct adsp_event *event)
+{
+       /* call the per-module msg verifier */
+       if (module->patch_event)
+               return module->patch_event(module, event);
+       return 0;
+}
+
+static long adsp_get_event(struct adsp_device *adev, void __user *arg)
+{
+       unsigned long flags;
+       struct adsp_event *data = NULL;
+       struct adsp_event_t evt;
+       int timeout;
+       long rc = 0;
+
+       if (copy_from_user(&evt, arg, sizeof(struct adsp_event_t)))
+               return -EFAULT;
+
+       timeout = (int)evt.timeout_ms;
+
+       if (timeout > 0) {
+               rc = wait_event_interruptible_timeout(
+                       adev->event_wait, adsp_events_pending(adev),
+                       msecs_to_jiffies(timeout));
+               if (rc == 0)
+                       return -ETIMEDOUT;
+       } else {
+               rc = wait_event_interruptible(
+                       adev->event_wait, adsp_events_pending(adev));
+       }
+       if (rc < 0)
+               return rc;
+
+       if (adev->abort)
+               return -ENODEV;
+
+       spin_lock_irqsave(&adev->event_queue_lock, flags);
+       if (!list_empty(&adev->event_queue)) {
+               data = list_first_entry(&adev->event_queue,
+                                       struct adsp_event, list);
+               list_del(&data->list);
+       }
+       spin_unlock_irqrestore(&adev->event_queue_lock, flags);
+
+       if (!data)
+               return -EAGAIN;
+
+       /* DSP messages are type 0; they may contain physical addresses */
+       if (data->type == 0)
+               adsp_patch_event(adev->module, data);
+
+       /* map adsp_event --> adsp_event_t */
+       if (evt.len < data->size) {
+               rc = -ETOOSMALL;
+               goto end;
+       }
+       if (data->msg_id != EVENT_MSG_ID) {
+               if (copy_to_user((void *)(evt.data), data->data.msg16,
+                                       data->size)) {
+                       rc = -EFAULT;
+                       goto end;
+       }
+       } else {
+               if (copy_to_user((void *)(evt.data), data->data.msg32,
+                                       data->size)) {
+                       rc = -EFAULT;
+                       goto end;
+               }
+       }
+
+       evt.type = data->type; /* 0 --> from aDSP, 1 --> from ARM9 */
+       evt.msg_id = data->msg_id;
+       evt.flags = data->is16;
+       evt.len = data->size;
+       if (copy_to_user(arg, &evt, sizeof(evt)))
+               rc = -EFAULT;
+end:
+       kfree(data);
+       return rc;
+}
+
+static long adsp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+       struct adsp_device *adev = filp->private_data;
+
+       switch (cmd) {
+       case ADSP_IOCTL_ENABLE:
+               return msm_adsp_enable(adev->module);
+
+       case ADSP_IOCTL_DISABLE:
+               return msm_adsp_disable(adev->module);
+
+       case ADSP_IOCTL_DISABLE_EVENT_RSP:
+               return 0;
+
+       case ADSP_IOCTL_DISABLE_ACK:
+               pr_err("adsp: ADSP_IOCTL_DISABLE_ACK is not implemented.\n");
+               break;
+
+       case ADSP_IOCTL_WRITE_COMMAND:
+               return adsp_write_cmd(adev, (void __user *) arg);
+
+       case ADSP_IOCTL_GET_EVENT:
+               return adsp_get_event(adev, (void __user *) arg);
+
+       case ADSP_IOCTL_SET_CLKRATE: {
+#if CONFIG_MSM_AMSS_VERSION==6350
+               unsigned long clk_rate;
+               if (copy_from_user(&clk_rate, (void *) arg, sizeof(clk_rate)))
+                       return -EFAULT;
+               return adsp_set_clkrate(adev->module, clk_rate);
+#endif
+       }
+
+       case ADSP_IOCTL_REGISTER_PMEM: {
+               struct adsp_pmem_info info;
+               if (copy_from_user(&info, (void *) arg, sizeof(info)))
+                       return -EFAULT;
+               return adsp_pmem_add(adev->module, &info);
+       }
+
+       case ADSP_IOCTL_ABORT_EVENT_READ:
+               adev->abort = 1;
+               wake_up(&adev->event_wait);
+               break;
+
+       default:
+               break;
+       }
+       return -EINVAL;
+}
+
+static int adsp_release(struct inode *inode, struct file *filp)
+{
+       struct adsp_device *adev = filp->private_data;
+       struct msm_adsp_module *module = adev->module;
+       struct hlist_node *node, *tmp;
+       struct adsp_pmem_region *region;
+
+       pr_info("adsp_release() '%s'\n", adev->name);
+
+       /* clear module before putting it to avoid race with open() */
+       adev->module = NULL;
+
+       mutex_lock(&module->pmem_regions_lock);
+       hlist_for_each_safe(node, tmp, &module->pmem_regions) {
+               region = hlist_entry(node, struct adsp_pmem_region, list);
+               hlist_del(node);
+               put_pmem_file(region->file);
+               kfree(region);
+       }
+       mutex_unlock(&module->pmem_regions_lock);
+       BUG_ON(!hlist_empty(&module->pmem_regions));
+
+       msm_adsp_put(module);
+       return 0;
+}
+
+static void adsp_event(void *driver_data, unsigned id, size_t len,
+                      void (*getevent)(void *ptr, size_t len))
+{
+       struct adsp_device *adev = driver_data;
+       struct adsp_event *event;
+       unsigned long flags;
+
+       if (len > ADSP_EVENT_MAX_SIZE) {
+               pr_err("adsp_event: event too large (%d bytes)\n", len);
+               return;
+       }
+
+       event = kmalloc(sizeof(*event), GFP_ATOMIC);
+       if (!event) {
+               pr_err("adsp_event: cannot allocate buffer\n");
+               return;
+       }
+
+       if (id != EVENT_MSG_ID) {
+               event->type = 0;
+               event->is16 = 0;
+               event->msg_id = id;
+               event->size = len;
+
+               getevent(event->data.msg16, len);
+       } else {
+               event->type = 1;
+               event->is16 = 1;
+               event->msg_id = id;
+               event->size = len;
+               getevent(event->data.msg32, len);
+       }
+
+       spin_lock_irqsave(&adev->event_queue_lock, flags);
+       list_add_tail(&event->list, &adev->event_queue);
+       spin_unlock_irqrestore(&adev->event_queue_lock, flags);
+       wake_up(&adev->event_wait);
+}
+
+static struct msm_adsp_ops adsp_ops = {
+       .event = adsp_event,
+};
+
+static int adsp_open(struct inode *inode, struct file *filp)
+{
+       struct adsp_device *adev;
+       int rc;
+
+       rc = nonseekable_open(inode, filp);
+       if (rc < 0)
+               return rc;
+
+       adev = inode_to_device(inode);
+       if (!adev)
+               return -ENODEV;
+
+       pr_info("adsp_open() name = '%s'\n", adev->name);
+
+       rc = msm_adsp_get(adev->name, &adev->module, &adsp_ops, adev);
+       if (rc)
+               return rc;
+
+       pr_info("adsp_open() module '%s' adev %p\n", adev->name, adev);
+       filp->private_data = adev;
+       adev->abort = 0;
+       INIT_HLIST_HEAD(&adev->module->pmem_regions);
+       mutex_init(&adev->module->pmem_regions_lock);
+
+       return 0;
+}
+
+static unsigned adsp_device_count;
+static struct adsp_device *adsp_devices;
+
+static struct adsp_device *inode_to_device(struct inode *inode)
+{
+       unsigned n = MINOR(inode->i_rdev);
+       if (n < adsp_device_count) {
+               if (adsp_devices[n].device)
+                       return adsp_devices + n;
+       }
+       return NULL;
+}
+
+static dev_t adsp_devno;
+static struct class *adsp_class;
+
+static struct file_operations adsp_fops = {
+       .owner = THIS_MODULE,
+       .open = adsp_open,
+       .unlocked_ioctl = adsp_ioctl,
+       .release = adsp_release,
+};
+
+static void adsp_create(struct adsp_device *adev, const char *name,
+                       struct device *parent, dev_t devt)
+{
+       struct device *dev;
+       int rc;
+
+       dev = device_create(adsp_class, parent, devt, "%s", name);
+       if (IS_ERR(dev))
+               return;
+
+       init_waitqueue_head(&adev->event_wait);
+       INIT_LIST_HEAD(&adev->event_queue);
+       spin_lock_init(&adev->event_queue_lock);
+
+       cdev_init(&adev->cdev, &adsp_fops);
+       adev->cdev.owner = THIS_MODULE;
+
+       rc = cdev_add(&adev->cdev, devt, 1);
+       if (rc < 0) {
+               device_destroy(adsp_class, devt);
+       } else {
+               adev->device = dev;
+               adev->name = name;
+       }
+}
+
+void msm_adsp_publish_cdevs(struct msm_adsp_module *modules, unsigned n)
+{
+       int rc;
+
+       adsp_devices = kzalloc(sizeof(struct adsp_device) * n, GFP_KERNEL);
+       if (!adsp_devices)
+               return;
+
+       adsp_class = class_create(THIS_MODULE, "adsp");
+       if (IS_ERR(adsp_class))
+               goto fail_create_class;
+
+       rc = alloc_chrdev_region(&adsp_devno, 0, n, "adsp");
+       if (rc < 0)
+               goto fail_alloc_region;
+
+       adsp_device_count = n;
+       for (n = 0; n < adsp_device_count; n++) {
+               adsp_create(adsp_devices + n,
+                           modules[n].name, &modules[n].pdev.dev,
+                           MKDEV(MAJOR(adsp_devno), n));
+       }
+
+       return;
+
+fail_alloc_region:
+       class_unregister(adsp_class);
+fail_create_class:
+       kfree(adsp_devices);
+}
diff --git a/drivers/staging/dream/qdsp5/adsp_info.c b/drivers/staging/dream/qdsp5/adsp_info.c
new file mode 100644 (file)
index 0000000..b9c77d2
--- /dev/null
@@ -0,0 +1,121 @@
+/* arch/arm/mach-msm/adsp_info.c
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated.
+ * Copyright (c) 2008 QUALCOMM USA, 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 "adsp.h"
+
+/* Firmware modules */
+#define QDSP_MODULE_KERNEL                  0x0106dd4e
+#define QDSP_MODULE_AFETASK                 0x0106dd6f
+#define QDSP_MODULE_AUDPLAY0TASK            0x0106dd70
+#define QDSP_MODULE_AUDPLAY1TASK            0x0106dd71
+#define QDSP_MODULE_AUDPPTASK               0x0106dd72
+#define QDSP_MODULE_VIDEOTASK               0x0106dd73
+#define QDSP_MODULE_VIDEO_AAC_VOC           0x0106dd74
+#define QDSP_MODULE_PCM_DEC                 0x0106dd75
+#define QDSP_MODULE_AUDIO_DEC_MP3           0x0106dd76
+#define QDSP_MODULE_AUDIO_DEC_AAC           0x0106dd77
+#define QDSP_MODULE_AUDIO_DEC_WMA           0x0106dd78
+#define QDSP_MODULE_HOSTPCM                 0x0106dd79
+#define QDSP_MODULE_DTMF                    0x0106dd7a
+#define QDSP_MODULE_AUDRECTASK              0x0106dd7b
+#define QDSP_MODULE_AUDPREPROCTASK          0x0106dd7c
+#define QDSP_MODULE_SBC_ENC                 0x0106dd7d
+#define QDSP_MODULE_VOC_UMTS                0x0106dd9a
+#define QDSP_MODULE_VOC_CDMA                0x0106dd98
+#define QDSP_MODULE_VOC_PCM                 0x0106dd7f
+#define QDSP_MODULE_VOCENCTASK              0x0106dd80
+#define QDSP_MODULE_VOCDECTASK              0x0106dd81
+#define QDSP_MODULE_VOICEPROCTASK           0x0106dd82
+#define QDSP_MODULE_VIDEOENCTASK            0x0106dd83
+#define QDSP_MODULE_VFETASK                 0x0106dd84
+#define QDSP_MODULE_WAV_ENC                 0x0106dd85
+#define QDSP_MODULE_AACLC_ENC               0x0106dd86
+#define QDSP_MODULE_VIDEO_AMR               0x0106dd87
+#define QDSP_MODULE_VOC_AMR                 0x0106dd88
+#define QDSP_MODULE_VOC_EVRC                0x0106dd89
+#define QDSP_MODULE_VOC_13K                 0x0106dd8a
+#define QDSP_MODULE_VOC_FGV                 0x0106dd8b
+#define QDSP_MODULE_DIAGTASK                0x0106dd8c
+#define QDSP_MODULE_JPEGTASK                0x0106dd8d
+#define QDSP_MODULE_LPMTASK                 0x0106dd8e
+#define QDSP_MODULE_QCAMTASK                0x0106dd8f
+#define QDSP_MODULE_MODMATHTASK             0x0106dd90
+#define QDSP_MODULE_AUDPLAY2TASK            0x0106dd91
+#define QDSP_MODULE_AUDPLAY3TASK            0x0106dd92
+#define QDSP_MODULE_AUDPLAY4TASK            0x0106dd93
+#define QDSP_MODULE_GRAPHICSTASK            0x0106dd94
+#define QDSP_MODULE_MIDI                    0x0106dd95
+#define QDSP_MODULE_GAUDIO                  0x0106dd96
+#define QDSP_MODULE_VDEC_LP_MODE            0x0106dd97
+#define QDSP_MODULE_MAX                     0x7fffffff
+
+   /* DO NOT USE: Force this enum to be a 32bit type to improve speed */
+#define QDSP_MODULE_32BIT_DUMMY 0x10000
+
+static uint32_t *qdsp_task_to_module[IMG_MAX];
+static uint32_t        *qdsp_queue_offset_table[IMG_MAX];
+
+#define QDSP_MODULE(n, clkname, clkrate, verify_cmd_func, patch_event_func) \
+       { .name = #n, .pdev_name = "adsp_" #n, .id = QDSP_MODULE_##n, \
+         .clk_name = clkname, .clk_rate = clkrate, \
+         .verify_cmd = verify_cmd_func, .patch_event = patch_event_func }
+
+static struct adsp_module_info module_info[] = {
+       QDSP_MODULE(AUDPLAY0TASK, NULL, 0, NULL, NULL),
+       QDSP_MODULE(AUDPPTASK, NULL, 0, NULL, NULL),
+       QDSP_MODULE(AUDRECTASK, NULL, 0, NULL, NULL),
+       QDSP_MODULE(AUDPREPROCTASK, NULL, 0, NULL, NULL),
+       QDSP_MODULE(VFETASK, "vfe_clk", 0, adsp_vfe_verify_cmd,
+               adsp_vfe_patch_event),
+       QDSP_MODULE(QCAMTASK, NULL, 0, NULL, NULL),
+       QDSP_MODULE(LPMTASK, NULL, 0, adsp_lpm_verify_cmd, NULL),
+       QDSP_MODULE(JPEGTASK, "vdc_clk", 96000000, adsp_jpeg_verify_cmd,
+               adsp_jpeg_patch_event),
+       QDSP_MODULE(VIDEOTASK, "vdc_clk", 96000000,
+               adsp_video_verify_cmd, NULL),
+       QDSP_MODULE(VDEC_LP_MODE, NULL, 0, NULL, NULL),
+       QDSP_MODULE(VIDEOENCTASK, "vdc_clk", 96000000,
+               adsp_videoenc_verify_cmd, NULL),
+};
+
+int adsp_init_info(struct adsp_info *info)
+{
+       uint32_t img_num;
+
+       info->send_irq =   0x00c00200;
+       info->read_ctrl =  0x00400038;
+       info->write_ctrl = 0x00400034;
+
+       info->max_msg16_size = 193;
+       info->max_msg32_size = 8;
+       for (img_num = 0; img_num < IMG_MAX; img_num++)
+               qdsp_queue_offset_table[img_num] =
+               &info->init_info_ptr->queue_offsets[img_num][0];
+
+       for (img_num = 0; img_num < IMG_MAX; img_num++)
+               qdsp_task_to_module[img_num] =
+               &info->init_info_ptr->task_to_module_tbl[img_num][0];
+       info->max_task_id = 30;
+       info->max_module_id = QDSP_MODULE_MAX - 1;
+       info->max_queue_id = QDSP_MAX_NUM_QUEUES;
+       info->max_image_id = 2;
+       info->queue_offset = qdsp_queue_offset_table;
+       info->task_to_module = qdsp_task_to_module;
+
+       info->module_count = ARRAY_SIZE(module_info);
+       info->module = module_info;
+       return 0;
+}
diff --git a/drivers/staging/dream/qdsp5/adsp_jpeg_patch_event.c b/drivers/staging/dream/qdsp5/adsp_jpeg_patch_event.c
new file mode 100644 (file)
index 0000000..4f493ed
--- /dev/null
@@ -0,0 +1,31 @@
+/* arch/arm/mach-msm/qdsp5/adsp_jpeg_patch_event.c
+ *
+ * Verification code for aDSP JPEG events.
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated
+ * 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 <mach/qdsp5/qdsp5jpegmsg.h>
+#include "adsp.h"
+
+int adsp_jpeg_patch_event(struct msm_adsp_module *module,
+                       struct adsp_event *event)
+{
+       if (event->msg_id == JPEG_MSG_ENC_OP_PRODUCED) {
+               jpeg_msg_enc_op_produced *op = (jpeg_msg_enc_op_produced *)event->data.msg16;
+               return adsp_pmem_paddr_fixup(module, (void **)&op->op_buf_addr);
+       }
+
+       return 0;
+}
diff --git a/drivers/staging/dream/qdsp5/adsp_jpeg_verify_cmd.c b/drivers/staging/dream/qdsp5/adsp_jpeg_verify_cmd.c
new file mode 100644 (file)
index 0000000..b33eba2
--- /dev/null
@@ -0,0 +1,182 @@
+/* arch/arm/mach-msm/qdsp5/adsp_jpeg_verify_cmd.c
+ *
+ * Verification code for aDSP JPEG packets from userspace.
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated
+ * 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 <mach/qdsp5/qdsp5jpegcmdi.h>
+#include "adsp.h"
+
+static uint32_t dec_fmt;
+
+static inline void get_sizes(jpeg_cmd_enc_cfg *cmd, uint32_t *luma_size,
+                            uint32_t *chroma_size)
+{
+       uint32_t fmt, luma_width, luma_height;
+
+       fmt = cmd->process_cfg & JPEG_CMD_ENC_PROCESS_CFG_IP_DATA_FORMAT_M;
+       luma_width = (cmd->ip_size_cfg & JPEG_CMD_IP_SIZE_CFG_LUMA_WIDTH_M)
+                     >> 16;
+       luma_height = cmd->frag_cfg & JPEG_CMD_FRAG_SIZE_LUMA_HEIGHT_M;
+       *luma_size = luma_width * luma_height;
+       if (fmt == JPEG_CMD_ENC_PROCESS_CFG_IP_DATA_FORMAT_H2V2)
+               *chroma_size = *luma_size/2;
+       else
+               *chroma_size = *luma_size;
+}
+
+static inline int verify_jpeg_cmd_enc_cfg(struct msm_adsp_module *module,
+                                         void *cmd_data, size_t cmd_size)
+{
+       jpeg_cmd_enc_cfg *cmd = (jpeg_cmd_enc_cfg *)cmd_data;
+       uint32_t luma_size, chroma_size;
+       int i, num_frags;
+
+       if (cmd_size != sizeof(jpeg_cmd_enc_cfg)) {
+               printk(KERN_ERR "adsp: module %s: JPEG ENC CFG invalid cmd_size %d\n",
+                       module->name, cmd_size);
+               return -1;
+       }
+
+       get_sizes(cmd, &luma_size, &chroma_size);
+       num_frags = (cmd->process_cfg >> 10) & 0xf;
+       num_frags = ((num_frags == 1) ? num_frags : num_frags * 2);
+       for (i = 0; i < num_frags; i += 2) {
+               if (adsp_pmem_fixup(module, (void **)(&cmd->frag_cfg_part[i]), luma_size) ||
+                   adsp_pmem_fixup(module, (void **)(&cmd->frag_cfg_part[i+1]), chroma_size))
+                       return -1;
+       }
+
+       if (adsp_pmem_fixup(module, (void **)&cmd->op_buf_0_cfg_part1,
+                           cmd->op_buf_0_cfg_part2) ||
+           adsp_pmem_fixup(module, (void **)&cmd->op_buf_1_cfg_part1,
+                           cmd->op_buf_1_cfg_part2))
+               return -1;
+       return 0;
+}
+
+static inline int verify_jpeg_cmd_dec_cfg(struct msm_adsp_module *module,
+                                         void *cmd_data, size_t cmd_size)
+{
+       jpeg_cmd_dec_cfg *cmd = (jpeg_cmd_dec_cfg *)cmd_data;
+       uint32_t div;
+
+       if (cmd_size != sizeof(jpeg_cmd_dec_cfg)) {
+               printk(KERN_ERR "adsp: module %s: JPEG DEC CFG invalid cmd_size %d\n",
+                       module->name, cmd_size);
+               return -1;
+       }
+
+       if (adsp_pmem_fixup(module, (void **)&cmd->ip_stream_buf_cfg_part1,
+                           cmd->ip_stream_buf_cfg_part2) ||
+           adsp_pmem_fixup(module, (void **)&cmd->op_stream_buf_0_cfg_part1,
+                           cmd->op_stream_buf_0_cfg_part2) ||
+           adsp_pmem_fixup(module, (void **)&cmd->op_stream_buf_1_cfg_part1,
+                           cmd->op_stream_buf_1_cfg_part2))
+               return -1;
+       dec_fmt = cmd->op_data_format &
+               JPEG_CMD_DEC_OP_DATA_FORMAT_M;
+       div = (dec_fmt == JPEG_CMD_DEC_OP_DATA_FORMAT_H2V2) ? 2 : 1;
+       if (adsp_pmem_fixup(module, (void **)&cmd->op_stream_buf_0_cfg_part3,
+                           cmd->op_stream_buf_0_cfg_part2 / div) ||
+           adsp_pmem_fixup(module, (void **)&cmd->op_stream_buf_1_cfg_part3,
+                           cmd->op_stream_buf_1_cfg_part2 / div))
+               return -1;
+       return 0;
+}
+
+static int verify_jpeg_cfg_cmd(struct msm_adsp_module *module,
+                              void *cmd_data, size_t cmd_size)
+{
+       uint32_t cmd_id = ((uint32_t *)cmd_data)[0];
+       switch(cmd_id) {
+       case JPEG_CMD_ENC_CFG:
+               return verify_jpeg_cmd_enc_cfg(module, cmd_data, cmd_size);
+       case JPEG_CMD_DEC_CFG:
+               return verify_jpeg_cmd_dec_cfg(module, cmd_data, cmd_size);
+       default:
+               if (cmd_id > 1) {
+                       printk(KERN_ERR "adsp: module %s: invalid JPEG CFG cmd_id %d\n", module->name, cmd_id);
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+static int verify_jpeg_action_cmd(struct msm_adsp_module *module,
+                                 void *cmd_data, size_t cmd_size)
+{
+       uint32_t cmd_id = ((uint32_t *)cmd_data)[0];
+       switch (cmd_id) {
+       case JPEG_CMD_ENC_OP_CONSUMED:
+       {
+               jpeg_cmd_enc_op_consumed *cmd =
+                       (jpeg_cmd_enc_op_consumed *)cmd_data;
+
+               if (cmd_size != sizeof(jpeg_cmd_enc_op_consumed)) {
+                       printk(KERN_ERR "adsp: module %s: JPEG_CMD_ENC_OP_CONSUMED invalid size %d\n",
+                               module->name, cmd_size);
+                       return -1;
+               }
+
+               if (adsp_pmem_fixup(module, (void **)&cmd->op_buf_addr,
+                                   cmd->op_buf_size))
+                       return -1;
+       }
+       break;
+       case JPEG_CMD_DEC_OP_CONSUMED:
+       {
+               uint32_t div;
+               jpeg_cmd_dec_op_consumed *cmd =
+                       (jpeg_cmd_dec_op_consumed *)cmd_data;
+
+               if (cmd_size != sizeof(jpeg_cmd_enc_op_consumed)) {
+                       printk(KERN_ERR "adsp: module %s: JPEG_CMD_DEC_OP_CONSUMED invalid size %d\n",
+                               module->name, cmd_size);
+                       return -1;
+               }
+
+               div = (dec_fmt == JPEG_CMD_DEC_OP_DATA_FORMAT_H2V2) ?  2 : 1;
+               if (adsp_pmem_fixup(module, (void **)&cmd->luma_op_buf_addr,
+                                   cmd->luma_op_buf_size) ||
+                   adsp_pmem_fixup(module, (void **)&cmd->chroma_op_buf_addr,
+                                   cmd->luma_op_buf_size / div))
+                       return -1;
+       }
+       break;
+       default:
+               if (cmd_id > 7) {
+                       printk(KERN_ERR "adsp: module %s: invalid cmd_id %d\n",
+                               module->name, cmd_id);
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+int adsp_jpeg_verify_cmd(struct msm_adsp_module *module,
+                        unsigned int queue_id, void *cmd_data,
+                        size_t cmd_size)
+{
+       switch(queue_id) {
+       case QDSP_uPJpegCfgCmdQueue:
+               return verify_jpeg_cfg_cmd(module, cmd_data, cmd_size);
+       case QDSP_uPJpegActionCmdQueue:
+               return verify_jpeg_action_cmd(module, cmd_data, cmd_size);
+       default:
+               return -1;
+       }
+}
+
diff --git a/drivers/staging/dream/qdsp5/adsp_lpm_verify_cmd.c b/drivers/staging/dream/qdsp5/adsp_lpm_verify_cmd.c
new file mode 100644 (file)
index 0000000..1e23ef3
--- /dev/null
@@ -0,0 +1,65 @@
+/* arch/arm/mach-msm/qdsp5/adsp_lpm_verify_cmd.c
+ *
+ * Verificion code for aDSP LPM packets from userspace.
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated
+ * 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 <mach/qdsp5/qdsp5lpmcmdi.h>
+#include "adsp.h"
+
+int adsp_lpm_verify_cmd(struct msm_adsp_module *module,
+                        unsigned int queue_id, void *cmd_data,
+                        size_t cmd_size)
+{
+       uint32_t cmd_id, col_height, input_row_incr, output_row_incr,
+               input_size, output_size;
+       uint32_t size_mask = 0x0fff;
+       lpm_cmd_start *cmd;
+
+       if (queue_id != QDSP_lpmCommandQueue) {
+               printk(KERN_ERR "adsp: module %s: wrong queue id %d\n",
+                       module->name, queue_id);
+               return -1;
+       }
+
+       cmd = (lpm_cmd_start *)cmd_data;
+       cmd_id = cmd->cmd_id;
+
+       if (cmd_id == LPM_CMD_START) {
+               if (cmd_size != sizeof(lpm_cmd_start)) {
+                       printk(KERN_ERR "adsp: module %s: wrong size %d, expect %d\n",
+                               module->name, cmd_size, sizeof(lpm_cmd_start));
+                       return -1;
+               }
+               col_height = cmd->ip_data_cfg_part1 & size_mask;
+               input_row_incr = cmd->ip_data_cfg_part2 & size_mask;
+               output_row_incr = cmd->op_data_cfg_part1 & size_mask;
+               input_size = col_height * input_row_incr;
+               output_size = col_height * output_row_incr;
+               if ((cmd->ip_data_cfg_part4 && adsp_pmem_fixup(module,
+                                   (void **)(&cmd->ip_data_cfg_part4),
+                                   input_size)) ||
+                  (cmd->op_data_cfg_part3 && adsp_pmem_fixup(module,
+                                   (void **)(&cmd->op_data_cfg_part3),
+                                   output_size)))
+                       return -1;
+       } else if (cmd_id > 1) {
+               printk(KERN_ERR "adsp: module %s: invalid cmd_id %d\n",
+                       module->name, cmd_id);
+               return -1;
+       }
+       return 0;
+}
+
diff --git a/drivers/staging/dream/qdsp5/adsp_vfe_patch_event.c b/drivers/staging/dream/qdsp5/adsp_vfe_patch_event.c
new file mode 100644 (file)
index 0000000..a56392b
--- /dev/null
@@ -0,0 +1,54 @@
+/* arch/arm/mach-msm/qdsp5/adsp_vfe_patch_event.c
+ *
+ * Verification code for aDSP VFE packets from userspace.
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated
+ * 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 <mach/qdsp5/qdsp5vfemsg.h>
+#include "adsp.h"
+
+static int patch_op_event(struct msm_adsp_module *module,
+                               struct adsp_event *event)
+{
+       vfe_msg_op1 *op = (vfe_msg_op1 *)event->data.msg16;
+       if (adsp_pmem_paddr_fixup(module, (void **)&op->op1_buf_y_addr) ||
+           adsp_pmem_paddr_fixup(module, (void **)&op->op1_buf_cbcr_addr))
+               return -1;
+       return 0;
+}
+
+static int patch_af_wb_event(struct msm_adsp_module *module,
+                               struct adsp_event *event)
+{
+       vfe_msg_stats_wb_exp *af = (vfe_msg_stats_wb_exp *)event->data.msg16;
+       return adsp_pmem_paddr_fixup(module, (void **)&af->wb_exp_stats_op_buf);
+}
+
+int adsp_vfe_patch_event(struct msm_adsp_module *module,
+                       struct adsp_event *event)
+{
+       switch(event->msg_id) {
+       case VFE_MSG_OP1:
+       case VFE_MSG_OP2:
+               return patch_op_event(module, event);
+       case VFE_MSG_STATS_AF:
+       case VFE_MSG_STATS_WB_EXP:
+               return patch_af_wb_event(module, event);
+       default:
+               break;
+       }
+
+       return 0;
+}
diff --git a/drivers/staging/dream/qdsp5/adsp_vfe_verify_cmd.c b/drivers/staging/dream/qdsp5/adsp_vfe_verify_cmd.c
new file mode 100644 (file)
index 0000000..927d50a
--- /dev/null
@@ -0,0 +1,239 @@
+/* arch/arm/mach-msm/qdsp5/adsp_vfe_verify_cmd.c
+ *
+ * Verification code for aDSP VFE packets from userspace.
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated
+ * 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 <mach/qdsp5/qdsp5vfecmdi.h>
+#include "adsp.h"
+
+static uint32_t size1_y, size2_y, size1_cbcr, size2_cbcr;
+static uint32_t af_size = 4228;
+static uint32_t awb_size = 8196;
+
+static inline int verify_cmd_op_ack(struct msm_adsp_module *module,
+                                   void *cmd_data, size_t cmd_size)
+{
+       vfe_cmd_op1_ack *cmd = (vfe_cmd_op1_ack *)cmd_data;
+       void **addr_y = (void **)&cmd->op1_buf_y_addr;
+       void **addr_cbcr = (void **)(&cmd->op1_buf_cbcr_addr);
+
+       if (cmd_size != sizeof(vfe_cmd_op1_ack))
+               return -1;
+       if ((*addr_y && adsp_pmem_fixup(module, addr_y, size1_y)) ||
+           (*addr_cbcr && adsp_pmem_fixup(module, addr_cbcr, size1_cbcr)))
+               return -1;
+       return 0;
+}
+
+static inline int verify_cmd_stats_autofocus_cfg(struct msm_adsp_module *module,
+                                                void *cmd_data, size_t cmd_size)
+{
+       int i;
+       vfe_cmd_stats_autofocus_cfg *cmd =
+               (vfe_cmd_stats_autofocus_cfg *)cmd_data;
+
+       if (cmd_size != sizeof(vfe_cmd_stats_autofocus_cfg))
+               return -1;
+
+       for (i = 0; i < 3; i++) {
+               void **addr = (void **)(&cmd->af_stats_op_buf[i]);
+               if (*addr && adsp_pmem_fixup(module, addr, af_size))
+                       return -1;
+       }
+       return 0;
+}
+
+static inline int verify_cmd_stats_wb_exp_cfg(struct msm_adsp_module *module,
+                                             void *cmd_data, size_t cmd_size)
+{
+       vfe_cmd_stats_wb_exp_cfg *cmd =
+               (vfe_cmd_stats_wb_exp_cfg *)cmd_data;
+       int i;
+
+       if (cmd_size != sizeof(vfe_cmd_stats_wb_exp_cfg))
+               return -1;
+
+       for (i = 0; i < 3; i++) {
+               void **addr = (void **)(&cmd->wb_exp_stats_op_buf[i]);
+               if (*addr && adsp_pmem_fixup(module, addr, awb_size))
+                       return -1;
+       }
+       return 0;
+}
+
+static inline int verify_cmd_stats_af_ack(struct msm_adsp_module *module,
+                                         void *cmd_data, size_t cmd_size)
+{
+       vfe_cmd_stats_af_ack *cmd = (vfe_cmd_stats_af_ack *)cmd_data;
+       void **addr = (void **)&cmd->af_stats_op_buf;
+
+       if (cmd_size != sizeof(vfe_cmd_stats_af_ack))
+               return -1;
+
+       if (*addr && adsp_pmem_fixup(module, addr, af_size))
+               return -1;
+       return 0;
+}
+
+static inline int verify_cmd_stats_wb_exp_ack(struct msm_adsp_module *module,
+                                             void *cmd_data, size_t cmd_size)
+{
+       vfe_cmd_stats_wb_exp_ack *cmd =
+               (vfe_cmd_stats_wb_exp_ack *)cmd_data;
+       void **addr = (void **)&cmd->wb_exp_stats_op_buf;
+
+       if (cmd_size != sizeof(vfe_cmd_stats_wb_exp_ack))
+               return -1;
+
+       if (*addr && adsp_pmem_fixup(module, addr, awb_size))
+               return -1;
+       return 0;
+}
+
+static int verify_vfe_command(struct msm_adsp_module *module,
+                             void *cmd_data, size_t cmd_size)
+{
+       uint32_t cmd_id = ((uint32_t *)cmd_data)[0];
+       switch (cmd_id) {
+       case VFE_CMD_OP1_ACK:
+               return verify_cmd_op_ack(module, cmd_data, cmd_size);
+       case VFE_CMD_OP2_ACK:
+               return verify_cmd_op_ack(module, cmd_data, cmd_size);
+       case VFE_CMD_STATS_AUTOFOCUS_CFG:
+               return verify_cmd_stats_autofocus_cfg(module, cmd_data,
+                                                     cmd_size);
+       case VFE_CMD_STATS_WB_EXP_CFG:
+               return verify_cmd_stats_wb_exp_cfg(module, cmd_data, cmd_size);
+       case VFE_CMD_STATS_AF_ACK:
+               return verify_cmd_stats_af_ack(module, cmd_data, cmd_size);
+       case VFE_CMD_STATS_WB_EXP_ACK:
+               return verify_cmd_stats_wb_exp_ack(module, cmd_data, cmd_size);
+       default:
+               if (cmd_id > 29) {
+                       printk(KERN_ERR "adsp: module %s: invalid VFE command id %d\n", module->name, cmd_id);
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+static int verify_vfe_command_scale(struct msm_adsp_module *module,
+                                   void *cmd_data, size_t cmd_size)
+{
+       uint32_t cmd_id = ((uint32_t *)cmd_data)[0];
+       // FIXME: check the size
+       if (cmd_id > 1) {
+               printk(KERN_ERR "adsp: module %s: invalid VFE SCALE command id %d\n", module->name, cmd_id);
+               return -1;
+       }
+       return 0;
+}
+
+
+static uint32_t get_size(uint32_t hw)
+{
+       uint32_t height, width;
+       uint32_t height_mask = 0x3ffc;
+       uint32_t width_mask = 0x3ffc000;
+
+       height = (hw & height_mask) >> 2;
+       width = (hw & width_mask) >> 14 ;
+       return height * width;
+}
+
+static int verify_vfe_command_table(struct msm_adsp_module *module,
+                                   void *cmd_data, size_t cmd_size)
+{
+       uint32_t cmd_id = ((uint32_t *)cmd_data)[0];
+       int i;
+
+       switch (cmd_id) {
+       case VFE_CMD_AXI_IP_CFG:
+       {
+               vfe_cmd_axi_ip_cfg *cmd = (vfe_cmd_axi_ip_cfg *)cmd_data;
+               uint32_t size;
+               if (cmd_size != sizeof(vfe_cmd_axi_ip_cfg)) {
+                       printk(KERN_ERR "adsp: module %s: invalid VFE TABLE (VFE_CMD_AXI_IP_CFG) command size %d\n",
+                               module->name, cmd_size);
+                       return -1;
+               }
+               size = get_size(cmd->ip_cfg_part2);
+
+               for (i = 0; i < 8; i++) {
+                       void **addr = (void **)
+                               &cmd->ip_buf_addr[i];
+                       if (*addr && adsp_pmem_fixup(module, addr, size))
+                               return -1;
+               }
+       }
+       case VFE_CMD_AXI_OP_CFG:
+       {
+               vfe_cmd_axi_op_cfg *cmd = (vfe_cmd_axi_op_cfg *)cmd_data;
+               void **addr1_y, **addr2_y, **addr1_cbcr, **addr2_cbcr;
+
+               if (cmd_size != sizeof(vfe_cmd_axi_op_cfg)) {
+                       printk(KERN_ERR "adsp: module %s: invalid VFE TABLE (VFE_CMD_AXI_OP_CFG) command size %d\n",
+                               module->name, cmd_size);
+                       return -1;
+               }
+               size1_y = get_size(cmd->op1_y_cfg_part2);
+               size1_cbcr = get_size(cmd->op1_cbcr_cfg_part2);
+               size2_y = get_size(cmd->op2_y_cfg_part2);
+               size2_cbcr = get_size(cmd->op2_cbcr_cfg_part2);
+               for (i = 0; i < 8; i++) {
+                       addr1_y = (void **)(&cmd->op1_buf1_addr[2*i]);
+                       addr1_cbcr = (void **)(&cmd->op1_buf1_addr[2*i+1]);
+                       addr2_y = (void **)(&cmd->op2_buf1_addr[2*i]);
+                       addr2_cbcr = (void **)(&cmd->op2_buf1_addr[2*i+1]);
+/*
+                       printk("module %s: [%d] %p %p %p %p\n",
+                               module->name, i,
+                               *addr1_y, *addr1_cbcr, *addr2_y, *addr2_cbcr);
+*/
+                       if ((*addr1_y && adsp_pmem_fixup(module, addr1_y, size1_y)) ||
+                           (*addr1_cbcr && adsp_pmem_fixup(module, addr1_cbcr, size1_cbcr)) ||
+                           (*addr2_y && adsp_pmem_fixup(module, addr2_y, size2_y)) ||
+                           (*addr2_cbcr && adsp_pmem_fixup(module, addr2_cbcr, size2_cbcr)))
+                               return -1;
+               }
+       }
+       default:
+               if (cmd_id > 4) {
+                       printk(KERN_ERR "adsp: module %s: invalid VFE TABLE command id %d\n",
+                               module->name, cmd_id);
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+int adsp_vfe_verify_cmd(struct msm_adsp_module *module,
+                       unsigned int queue_id, void *cmd_data,
+                       size_t cmd_size)
+{
+       switch (queue_id) {
+       case QDSP_vfeCommandQueue:
+               return verify_vfe_command(module, cmd_data, cmd_size);
+       case QDSP_vfeCommandScaleQueue:
+               return verify_vfe_command_scale(module, cmd_data, cmd_size);
+       case QDSP_vfeCommandTableQueue:
+               return verify_vfe_command_table(module, cmd_data, cmd_size);
+       default:
+               printk(KERN_ERR "adsp: module %s: unknown queue id %d\n",
+                       module->name, queue_id);
+               return -1;
+       }
+}
diff --git a/drivers/staging/dream/qdsp5/adsp_video_verify_cmd.c b/drivers/staging/dream/qdsp5/adsp_video_verify_cmd.c
new file mode 100644 (file)
index 0000000..53aff77
--- /dev/null
@@ -0,0 +1,163 @@
+/* arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c
+ *
+ * Verificion code for aDSP VDEC packets from userspace.
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated
+ * 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/io.h>
+
+#define ADSP_DEBUG_MSGS 0
+#if ADSP_DEBUG_MSGS
+#define DLOG(fmt,args...) \
+       do { printk(KERN_INFO "[%s:%s:%d] "fmt, __FILE__, __func__, __LINE__, \
+            ##args); } \
+       while (0)
+#else
+#define DLOG(x...) do {} while (0)
+#endif
+
+
+#include <mach/qdsp5/qdsp5vdeccmdi.h>
+#include "adsp.h"
+
+static inline void *high_low_short_to_ptr(unsigned short high,
+                                         unsigned short low)
+{
+       return (void *)((((unsigned long)high) << 16) | ((unsigned long)low));
+}
+
+static inline void ptr_to_high_low_short(void *ptr, unsigned short *high,
+                                        unsigned short *low)
+{
+       *high = (unsigned short)((((unsigned long)ptr) >> 16) & 0xffff);
+       *low = (unsigned short)((unsigned long)ptr & 0xffff);
+}
+
+static int pmem_fixup_high_low(unsigned short *high,
+                               unsigned short *low,
+                               unsigned short size_high,
+                               unsigned short size_low,
+                               struct msm_adsp_module *module,
+                               unsigned long *addr, unsigned long *size)
+{
+       void *phys_addr;
+       unsigned long phys_size;
+       unsigned long kvaddr;
+
+       phys_addr = high_low_short_to_ptr(*high, *low);
+       phys_size = (unsigned long)high_low_short_to_ptr(size_high, size_low);
+       DLOG("virt %x %x\n", phys_addr, phys_size);
+       if (adsp_pmem_fixup_kvaddr(module, &phys_addr, &kvaddr, phys_size)) {
+               DLOG("ah%x al%x sh%x sl%x addr %x size %x\n",
+                       *high, *low, size_high, size_low, phys_addr, phys_size);
+               return -1;
+       }
+       ptr_to_high_low_short(phys_addr, high, low);
+       DLOG("phys %x %x\n", phys_addr, phys_size);
+       if (addr)
+               *addr = kvaddr;
+       if (size)
+               *size = phys_size;
+       return 0;
+}
+
+static int verify_vdec_pkt_cmd(struct msm_adsp_module *module,
+                              void *cmd_data, size_t cmd_size)
+{
+       unsigned short cmd_id = ((unsigned short *)cmd_data)[0];
+       viddec_cmd_subframe_pkt *pkt;
+       unsigned long subframe_pkt_addr;
+       unsigned long subframe_pkt_size;
+       viddec_cmd_frame_header_packet *frame_header_pkt;
+       int i, num_addr, skip;
+       unsigned short *frame_buffer_high, *frame_buffer_low;
+       unsigned long frame_buffer_size;
+       unsigned short frame_buffer_size_high, frame_buffer_size_low;
+
+       DLOG("cmd_size %d cmd_id %d cmd_data %x\n", cmd_size, cmd_id, cmd_data);
+       if (cmd_id != VIDDEC_CMD_SUBFRAME_PKT) {
+               printk(KERN_INFO "adsp_video: unknown video packet %u\n",
+                       cmd_id);
+               return 0;
+       }
+       if (cmd_size < sizeof(viddec_cmd_subframe_pkt))
+               return -1;
+
+       pkt = (viddec_cmd_subframe_pkt *)cmd_data;
+
+       if (pmem_fixup_high_low(&(pkt->subframe_packet_high),
+                               &(pkt->subframe_packet_low),
+                               pkt->subframe_packet_size_high,
+                               pkt->subframe_packet_size_low,
+                               module,
+                               &subframe_pkt_addr,
+                               &subframe_pkt_size))
+               return -1;
+
+       /* deref those ptrs and check if they are a frame header packet */
+       frame_header_pkt = (viddec_cmd_frame_header_packet *)subframe_pkt_addr;
+
+       switch (frame_header_pkt->packet_id) {
+       case 0xB201: /* h.264 */
+               num_addr = skip = 8;
+               break;
+       case 0x4D01: /* mpeg-4 and h.263 */
+               num_addr = 3;
+               skip = 0;
+               break;
+       default:
+               return 0;
+       }
+
+       frame_buffer_high = &frame_header_pkt->frame_buffer_0_high;
+       frame_buffer_low = &frame_header_pkt->frame_buffer_0_low;
+       frame_buffer_size = (frame_header_pkt->x_dimension *
+                            frame_header_pkt->y_dimension * 3) / 2;
+       ptr_to_high_low_short((void *)frame_buffer_size,
+                             &frame_buffer_size_high,
+                             &frame_buffer_size_low);
+       for (i = 0; i < num_addr; i++) {
+               if (pmem_fixup_high_low(frame_buffer_high, frame_buffer_low,
+                                       frame_buffer_size_high,
+                                       frame_buffer_size_low,
+                                       module,
+                                       NULL, NULL))
+                       return -1;
+               frame_buffer_high += 2;
+               frame_buffer_low += 2;
+       }
+       /* Patch the output buffer. */
+       frame_buffer_high += 2*skip;
+       frame_buffer_low += 2*skip;
+       if (pmem_fixup_high_low(frame_buffer_high, frame_buffer_low,
+                               frame_buffer_size_high,
+                               frame_buffer_size_low, module, NULL, NULL))
+               return -1;
+       return 0;
+}
+
+int adsp_video_verify_cmd(struct msm_adsp_module *module,
+                        unsigned int queue_id, void *cmd_data,
+                        size_t cmd_size)
+{
+       switch (queue_id) {
+       case QDSP_mpuVDecPktQueue:
+               DLOG("\n");
+               return verify_vdec_pkt_cmd(module, cmd_data, cmd_size);
+       default:
+               printk(KERN_INFO "unknown video queue %u\n", queue_id);
+               return 0;
+       }
+}
+
diff --git a/drivers/staging/dream/qdsp5/adsp_videoenc_verify_cmd.c b/drivers/staging/dream/qdsp5/adsp_videoenc_verify_cmd.c
new file mode 100644 (file)
index 0000000..ee37449
--- /dev/null
@@ -0,0 +1,235 @@
+/* arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c
+ *
+ * Verificion code for aDSP VENC packets from userspace.
+ *
+ * Copyright (c) 2008 QUALCOMM Incorporated
+ * 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/io.h>
+
+#define ADSP_DEBUG_MSGS 0
+#if ADSP_DEBUG_MSGS
+#define DLOG(fmt,args...) \
+       do { printk(KERN_INFO "[%s:%s:%d] "fmt, __FILE__, __func__, __LINE__, \
+            ##args); } \
+       while (0)
+#else
+#define DLOG(x...) do {} while (0)
+#endif
+
+#include <mach/qdsp5/qdsp5venccmdi.h>
+#include "adsp.h"
+
+
+static unsigned short x_dimension, y_dimension;
+
+static inline void *high_low_short_to_ptr(unsigned short high,
+                                         unsigned short low)
+{
+       return (void *)((((unsigned long)high) << 16) | ((unsigned long)low));
+}
+
+static inline void ptr_to_high_low_short(void *ptr, unsigned short *high,
+                                        unsigned short *low)
+{
+       *high = (unsigned short)((((unsigned long)ptr) >> 16) & 0xffff);
+       *low = (unsigned short)((unsigned long)ptr & 0xffff);
+}
+
+static int pmem_fixup_high_low(unsigned short *high,
+                               unsigned short *low,
+                               unsigned short size_high,
+                               unsigned short size_low,
+                               struct msm_adsp_module *module,
+                               unsigned long *addr, unsigned long *size)
+{
+       void *phys_addr;
+       unsigned long phys_size;
+       unsigned long kvaddr;
+
+       phys_addr = high_low_short_to_ptr(*high, *low);
+       phys_size = (unsigned long)high_low_short_to_ptr(size_high, size_low);
+       DLOG("virt %x %x\n", phys_addr, phys_size);
+       if (adsp_pmem_fixup_kvaddr(module, &phys_addr, &kvaddr, phys_size)) {
+               DLOG("ah%x al%x sh%x sl%x addr %x size %x\n",
+                       *high, *low, size_high, size_low, phys_addr, phys_size);
+               return -1;
+       }
+       ptr_to_high_low_short(phys_addr, high, low);
+       DLOG("phys %x %x\n", phys_addr, phys_size);
+       if (addr)
+               *addr = kvaddr;
+       if (size)
+               *size = phys_size;
+       return 0;
+}
+
+static int verify_venc_cmd(struct msm_adsp_module *module,
+                              void *cmd_data, size_t cmd_size)
+{
+       unsigned short cmd_id = ((unsigned short *)cmd_data)[0];
+       unsigned long frame_buf_size, luma_buf_size, chroma_buf_size;
+       unsigned short frame_buf_size_high, frame_buf_size_low;
+       unsigned short luma_buf_size_high, luma_buf_size_low;
+       unsigned short chroma_buf_size_high, chroma_buf_size_low;
+       videnc_cmd_cfg *config_cmd;
+       videnc_cmd_frame_start *frame_cmd;
+       videnc_cmd_dis *dis_cmd;
+
+       DLOG("cmd_size %d cmd_id %d cmd_data %x\n", cmd_size, cmd_id, cmd_data);
+       switch (cmd_id) {
+       case VIDENC_CMD_ACTIVE:
+               if (cmd_size < sizeof(videnc_cmd_active))
+                       return -1;
+               break;
+       case VIDENC_CMD_IDLE:
+               if (cmd_size < sizeof(videnc_cmd_idle))
+                       return -1;
+               x_dimension = y_dimension = 0;
+               break;
+       case VIDENC_CMD_STATUS_QUERY:
+               if (cmd_size < sizeof(videnc_cmd_status_query))
+                       return -1;
+               break;
+       case VIDENC_CMD_RC_CFG:
+               if (cmd_size < sizeof(videnc_cmd_rc_cfg))
+                       return -1;
+               break;
+       case VIDENC_CMD_INTRA_REFRESH:
+               if (cmd_size < sizeof(videnc_cmd_intra_refresh))
+                       return -1;
+               break;
+       case VIDENC_CMD_DIGITAL_ZOOM:
+               if (cmd_size < sizeof(videnc_cmd_digital_zoom))
+                       return -1;
+               break;
+       case VIDENC_CMD_DIS_CFG:
+               if (cmd_size < sizeof(videnc_cmd_dis_cfg))
+                       return -1;
+               break;
+       case VIDENC_CMD_CFG:
+               if (cmd_size < sizeof(videnc_cmd_cfg))
+                       return -1;
+               config_cmd = (videnc_cmd_cfg *)cmd_data;
+               x_dimension = ((config_cmd->venc_frame_dim) & 0xFF00)>>8;
+               x_dimension = x_dimension*16;
+               y_dimension = (config_cmd->venc_frame_dim) & 0xFF;
+               y_dimension = y_dimension * 16;
+               break;
+       case VIDENC_CMD_FRAME_START:
+               if (cmd_size < sizeof(videnc_cmd_frame_start))
+                       return -1;
+               frame_cmd = (videnc_cmd_frame_start *)cmd_data;
+               luma_buf_size = x_dimension * y_dimension;
+               chroma_buf_size = luma_buf_size>>1;
+               frame_buf_size = luma_buf_size + chroma_buf_size;
+               ptr_to_high_low_short((void *)luma_buf_size,
+                             &luma_buf_size_high,
+                             &luma_buf_size_low);
+               ptr_to_high_low_short((void *)chroma_buf_size,
+                             &chroma_buf_size_high,
+                             &chroma_buf_size_low);
+               ptr_to_high_low_short((void *)frame_buf_size,
+                             &frame_buf_size_high,
+                             &frame_buf_size_low);
+               /* Address of raw Y data. */
+               if (pmem_fixup_high_low(&frame_cmd->input_luma_addr_high,
+                                       &frame_cmd->input_luma_addr_low,
+                                       luma_buf_size_high,
+                                       luma_buf_size_low,
+                                       module,
+                                       NULL, NULL))
+                       return -1;
+               /* Address of raw CbCr data */
+               if (pmem_fixup_high_low(&frame_cmd->input_chroma_addr_high,
+                                       &frame_cmd->input_chroma_addr_low,
+                                       chroma_buf_size_high,
+                                       chroma_buf_size_low,
+                                       module,
+                                       NULL, NULL))
+                       return -1;
+               /* Reference VOP */
+               if (pmem_fixup_high_low(&frame_cmd->ref_vop_buf_ptr_high,
+                                       &frame_cmd->ref_vop_buf_ptr_low,
+                                       frame_buf_size_high,
+                                       frame_buf_size_low,
+                                       module,
+                                       NULL, NULL))
+                       return -1;
+               /* Encoded Packet Address */
+               if (pmem_fixup_high_low(&frame_cmd->enc_pkt_buf_ptr_high,
+                                       &frame_cmd->enc_pkt_buf_ptr_low,
+                                       frame_cmd->enc_pkt_buf_size_high,
+                                       frame_cmd->enc_pkt_buf_size_low,
+                                       module,
+                                       NULL, NULL))
+                       return -1;
+               /* Unfiltered VOP Buffer Address */
+               if (pmem_fixup_high_low(
+                               &frame_cmd->unfilt_recon_vop_buf_ptr_high,
+                               &frame_cmd->unfilt_recon_vop_buf_ptr_low,
+                               frame_buf_size_high,
+                               frame_buf_size_low,
+                               module,
+                               NULL, NULL))
+                       return -1;
+               /* Filtered VOP Buffer Address */
+               if (pmem_fixup_high_low(&frame_cmd->filt_recon_vop_buf_ptr_high,
+                                       &frame_cmd->filt_recon_vop_buf_ptr_low,
+                                       frame_buf_size_high,
+                                       frame_buf_size_low,
+                                       module,
+                                       NULL, NULL))
+                       return -1;
+               break;
+       case VIDENC_CMD_DIS:
+               if (cmd_size < sizeof(videnc_cmd_dis))
+                       return -1;
+               dis_cmd = (videnc_cmd_dis *)cmd_data;
+               luma_buf_size = x_dimension * y_dimension;
+               ptr_to_high_low_short((void *)luma_buf_size,
+                             &luma_buf_size_high,
+                             &luma_buf_size_low);
+               /* Prev VFE Luma Output Address */
+               if (pmem_fixup_high_low(&dis_cmd->vfe_out_prev_luma_addr_high,
+                                       &dis_cmd->vfe_out_prev_luma_addr_low,
+                                       luma_buf_size_high,
+                                       luma_buf_size_low,
+                                       module,
+                                       NULL, NULL))
+                       return -1;
+               break;
+       default:
+               printk(KERN_INFO "adsp_video:unknown encoder video command %u\n",
+                       cmd_id);
+               return 0;
+       }
+
+       return 0;
+}
+
+
+int adsp_videoenc_verify_cmd(struct msm_adsp_module *module,
+                        unsigned int queue_id, void *cmd_data,
+                        size_t cmd_size)
+{
+       switch (queue_id) {
+       case QDSP_mpuVEncCmdQueue:
+               DLOG("\n");
+               return verify_venc_cmd(module, cmd_data, cmd_size);
+       default:
+               printk(KERN_INFO "unknown video queue %u\n", queue_id);
+               return 0;
+       }
+}
+
diff --git a/drivers/staging/dream/qdsp5/audio_aac.c b/drivers/staging/dream/qdsp5/audio_aac.c
new file mode 100644 (file)
index 0000000..1c20f28
--- /dev/null
@@ -0,0 +1,1052 @@
+/* arch/arm/mach-msm/qdsp5/audio_aac.c
+ *
+ * aac audio decoder device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2008-2009 QUALCOMM USA, 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/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/delay.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include "audmgr.h"
+
+#include <mach/msm_adsp.h>
+#include <mach/msm_audio_aac.h>
+#include <mach/qdsp5/qdsp5audppcmdi.h>
+#include <mach/qdsp5/qdsp5audppmsg.h>
+#include <mach/qdsp5/qdsp5audplaycmdi.h>
+#include <mach/qdsp5/qdsp5audplaymsg.h>
+
+/* for queue ids - should be relative to module number*/
+#include "adsp.h"
+
+#ifdef DEBUG
+#define dprintk(format, arg...) \
+printk(KERN_DEBUG format, ## arg)
+#else
+#define dprintk(format, arg...) do {} while (0)
+#endif
+
+#define BUFSZ 32768
+#define DMASZ (BUFSZ * 2)
+
+#define AUDPLAY_INVALID_READ_PTR_OFFSET        0xFFFF
+#define AUDDEC_DEC_AAC 5
+
+#define PCM_BUFSZ_MIN 9600     /* Hold one stereo AAC frame */
+#define PCM_BUF_MAX_COUNT 5    /* DSP only accepts 5 buffers at most
+                                  but support 2 buffers currently */
+#define ROUTING_MODE_FTRT 1
+#define ROUTING_MODE_RT 2
+/* Decoder status received from AUDPPTASK */
+#define  AUDPP_DEC_STATUS_SLEEP        0
+#define         AUDPP_DEC_STATUS_INIT  1
+#define  AUDPP_DEC_STATUS_CFG   2
+#define  AUDPP_DEC_STATUS_PLAY  3
+
+struct buffer {
+       void *data;
+       unsigned size;
+       unsigned used;          /* Input usage actual DSP produced PCM size  */
+       unsigned addr;
+};
+
+struct audio {
+       struct buffer out[2];
+
+       spinlock_t dsp_lock;
+
+       uint8_t out_head;
+       uint8_t out_tail;
+       uint8_t out_needed;     /* number of buffers the dsp is waiting for */
+
+       atomic_t out_bytes;
+
+       struct mutex lock;
+       struct mutex write_lock;
+       wait_queue_head_t write_wait;
+
+       /* Host PCM section */
+       struct buffer in[PCM_BUF_MAX_COUNT];
+       struct mutex read_lock;
+       wait_queue_head_t read_wait;    /* Wait queue for read */
+       char *read_data;        /* pointer to reader buffer */
+       dma_addr_t read_phys;   /* physical address of reader buffer */
+       uint8_t read_next;      /* index to input buffers to be read next */
+       uint8_t fill_next;      /* index to buffer that DSP should be filling */
+       uint8_t pcm_buf_count;  /* number of pcm buffer allocated */
+       /* ---- End of Host PCM section */
+
+       struct msm_adsp_module *audplay;
+
+       /* configuration to use on next enable */
+       uint32_t out_sample_rate;
+       uint32_t out_channel_mode;
+       struct msm_audio_aac_config aac_config;
+       struct audmgr audmgr;
+
+       /* data allocated for various buffers */
+       char *data;
+       dma_addr_t phys;
+
+       int rflush; /* Read  flush */
+       int wflush; /* Write flush */
+       int opened;
+       int enabled;
+       int running;
+       int stopped;    /* set when stopped, cleared on flush */
+       int pcm_feedback;
+       int buf_refresh;
+
+       int reserved; /* A byte is being reserved */
+       char rsv_byte; /* Handle odd length user data */
+
+       unsigned volume;
+
+       uint16_t dec_id;
+       uint32_t read_ptr_offset;
+};
+
+static int auddec_dsp_config(struct audio *audio, int enable);
+static void audpp_cmd_cfg_adec_params(struct audio *audio);
+static void audpp_cmd_cfg_routing_mode(struct audio *audio);
+static void audplay_send_data(struct audio *audio, unsigned needed);
+static void audplay_config_hostpcm(struct audio *audio);
+static void audplay_buffer_refresh(struct audio *audio);
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg);
+
+/* must be called with audio->lock held */
+static int audio_enable(struct audio *audio)
+{
+       struct audmgr_config cfg;
+       int rc;
+
+       dprintk("audio_enable()\n");
+
+       if (audio->enabled)
+               return 0;
+
+       audio->out_tail = 0;
+       audio->out_needed = 0;
+
+       cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE;
+       cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000;
+       cfg.def_method = RPC_AUD_DEF_METHOD_PLAYBACK;
+       cfg.codec = RPC_AUD_DEF_CODEC_AAC;
+       cfg.snd_method = RPC_SND_METHOD_MIDI;
+
+       rc = audmgr_enable(&audio->audmgr, &cfg);
+       if (rc < 0)
+               return rc;
+
+       if (msm_adsp_enable(audio->audplay)) {
+               pr_err("audio: msm_adsp_enable(audplay) failed\n");
+               audmgr_disable(&audio->audmgr);
+               return -ENODEV;
+       }
+
+       if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) {
+               pr_err("audio: audpp_enable() failed\n");
+    msm_adsp_disable(audio->audplay);
+               audmgr_disable(&audio->audmgr);
+               return -ENODEV;
+       }
+       audio->enabled = 1;
+       return 0;
+}
+
+/* must be called with audio->lock held */
+static int audio_disable(struct audio *audio)
+{
+       dprintk("audio_disable()\n");
+       if (audio->enabled) {
+               audio->enabled = 0;
+               auddec_dsp_config(audio, 0);
+               wake_up(&audio->write_wait);
+               wake_up(&audio->read_wait);
+               msm_adsp_disable(audio->audplay);
+               audpp_disable(audio->dec_id, audio);
+               audmgr_disable(&audio->audmgr);
+               audio->out_needed = 0;
+       }
+       return 0;
+}
+
+/* ------------------- dsp --------------------- */
+static void audio_update_pcm_buf_entry(struct audio *audio, uint32_t *payload)
+{
+       uint8_t index;
+       unsigned long flags;
+
+       if (audio->rflush)
+               return;
+
+       spin_lock_irqsave(&audio->dsp_lock, flags);
+       for (index = 0; index < payload[1]; index++) {
+               if (audio->in[audio->fill_next].addr ==
+                   payload[2 + index * 2]) {
+                       dprintk("audio_update_pcm_buf_entry: in[%d] ready\n",
+                               audio->fill_next);
+                       audio->in[audio->fill_next].used =
+                               payload[3 + index * 2];
+                       if ((++audio->fill_next) == audio->pcm_buf_count)
+                               audio->fill_next = 0;
+
+               } else {
+                       pr_err
+                           ("audio_update_pcm_buf_entry: expected=%x ret=%x\n"
+                            , audio->in[audio->fill_next].addr,
+                            payload[1 + index * 2]);
+                       break;
+               }
+       }
+       if (audio->in[audio->fill_next].used == 0) {
+               audplay_buffer_refresh(audio);
+       } else {
+               dprintk("audio_update_pcm_buf_entry: read cannot keep up\n");
+               audio->buf_refresh = 1;
+       }
+       wake_up(&audio->read_wait);
+       spin_unlock_irqrestore(&audio->dsp_lock, flags);
+
+}
+
+static void audplay_dsp_event(void *data, unsigned id, size_t len,
+                             void (*getevent) (void *ptr, size_t len))
+{
+       struct audio *audio = data;
+       uint32_t msg[28];
+       getevent(msg, sizeof(msg));
+
+       dprintk("audplay_dsp_event: msg_id=%x\n", id);
+
+       switch (id) {
+       case AUDPLAY_MSG_DEC_NEEDS_DATA:
+               audplay_send_data(audio, 1);
+               break;
+
+       case AUDPLAY_MSG_BUFFER_UPDATE:
+               audio_update_pcm_buf_entry(audio, msg);
+               break;
+
+       default:
+               pr_err("unexpected message from decoder \n");
+       }
+}
+
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg)
+{
+       struct audio *audio = private;
+
+       switch (id) {
+       case AUDPP_MSG_STATUS_MSG:{
+                       unsigned status = msg[1];
+
+                       switch (status) {
+                       case AUDPP_DEC_STATUS_SLEEP:
+                               dprintk("decoder status: sleep \n");
+                               break;
+
+                       case AUDPP_DEC_STATUS_INIT:
+                               dprintk("decoder status: init \n");
+                               audpp_cmd_cfg_routing_mode(audio);
+                               break;
+
+                       case AUDPP_DEC_STATUS_CFG:
+                               dprintk("decoder status: cfg \n");
+                               break;
+                       case AUDPP_DEC_STATUS_PLAY:
+                               dprintk("decoder status: play \n");
+                               if (audio->pcm_feedback) {
+                                       audplay_config_hostpcm(audio);
+                                       audplay_buffer_refresh(audio);
+                               }
+                               break;
+                       default:
+                               pr_err("unknown decoder status \n");
+                       }
+                       break;
+               }
+       case AUDPP_MSG_CFG_MSG:
+               if (msg[0] == AUDPP_MSG_ENA_ENA) {
+                       dprintk("audio_dsp_event: CFG_MSG ENABLE\n");
+                       auddec_dsp_config(audio, 1);
+                       audio->out_needed = 0;
+                       audio->running = 1;
+                       audpp_set_volume_and_pan(audio->dec_id, audio->volume,
+                                                0);
+                       audpp_avsync(audio->dec_id, 22050);
+               } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+                       dprintk("audio_dsp_event: CFG_MSG DISABLE\n");
+                       audpp_avsync(audio->dec_id, 0);
+                       audio->running = 0;
+               } else {
+                       pr_err("audio_dsp_event: CFG_MSG %d?\n", msg[0]);
+               }
+               break;
+       case AUDPP_MSG_ROUTING_ACK:
+               dprintk("audio_dsp_event: ROUTING_ACK mode=%d\n", msg[1]);
+               audpp_cmd_cfg_adec_params(audio);
+               break;
+
+       case AUDPP_MSG_FLUSH_ACK:
+               dprintk("%s: FLUSH_ACK\n", __func__);
+               audio->wflush = 0;
+               audio->rflush = 0;
+               if (audio->pcm_feedback)
+                       audplay_buffer_refresh(audio);
+               break;
+
+       default:
+               pr_err("audio_dsp_event: UNKNOWN (%d)\n", id);
+       }
+
+}
+
+struct msm_adsp_ops audplay_adsp_ops_aac = {
+       .event = audplay_dsp_event,
+};
+
+#define audplay_send_queue0(audio, cmd, len) \
+       msm_adsp_write(audio->audplay, QDSP_uPAudPlay0BitStreamCtrlQueue, \
+                      cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+       audpp_cmd_cfg_dec_type cmd;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+       if (enable)
+               cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+                   AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_AAC;
+       else
+               cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | AUDPP_CMD_DIS_DEC_V;
+
+       return audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_adec_params(struct audio *audio)
+{
+       audpp_cmd_cfg_adec_params_aac cmd;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
+       cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_AAC_LEN;
+       cmd.common.dec_id = audio->dec_id;
+       cmd.common.input_sampling_frequency = audio->out_sample_rate;
+       cmd.format = audio->aac_config.format;
+       cmd.audio_object = audio->aac_config.audio_object;
+       cmd.ep_config = audio->aac_config.ep_config;
+       cmd.aac_section_data_resilience_flag =
+               audio->aac_config.aac_section_data_resilience_flag;
+       cmd.aac_scalefactor_data_resilience_flag =
+               audio->aac_config.aac_scalefactor_data_resilience_flag;
+       cmd.aac_spectral_data_resilience_flag =
+               audio->aac_config.aac_spectral_data_resilience_flag;
+       cmd.sbr_on_flag = audio->aac_config.sbr_on_flag;
+       cmd.sbr_ps_on_flag = audio->aac_config.sbr_ps_on_flag;
+       cmd.channel_configuration = audio->aac_config.channel_configuration;
+
+       audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_routing_mode(struct audio *audio)
+{
+       struct audpp_cmd_routing_mode cmd;
+       dprintk("audpp_cmd_cfg_routing_mode()\n");
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.cmd_id = AUDPP_CMD_ROUTING_MODE;
+       cmd.object_number = audio->dec_id;
+       if (audio->pcm_feedback)
+               cmd.routing_mode = ROUTING_MODE_FTRT;
+       else
+               cmd.routing_mode = ROUTING_MODE_RT;
+
+       audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static int audplay_dsp_send_data_avail(struct audio *audio,
+                                      unsigned idx, unsigned len)
+{
+       audplay_cmd_bitstream_data_avail cmd;
+
+       cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL;
+       cmd.decoder_id = audio->dec_id;
+       cmd.buf_ptr = audio->out[idx].addr;
+       cmd.buf_size = len / 2;
+       cmd.partition_number = 0;
+       return audplay_send_queue0(audio, &cmd, sizeof(cmd));
+}
+
+static void audplay_buffer_refresh(struct audio *audio)
+{
+       struct audplay_cmd_buffer_refresh refresh_cmd;
+
+       refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH;
+       refresh_cmd.num_buffers = 1;
+       refresh_cmd.buf0_address = audio->in[audio->fill_next].addr;
+       refresh_cmd.buf0_length = audio->in[audio->fill_next].size -
+               (audio->in[audio->fill_next].size % 1024); /* AAC frame size */
+       refresh_cmd.buf_read_count = 0;
+       dprintk("audplay_buffer_fresh: buf0_addr=%x buf0_len=%d\n",
+               refresh_cmd.buf0_address, refresh_cmd.buf0_length);
+       (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd));
+}
+
+static void audplay_config_hostpcm(struct audio *audio)
+{
+       struct audplay_cmd_hpcm_buf_cfg cfg_cmd;
+
+       dprintk("audplay_config_hostpcm()\n");
+       cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG;
+       cfg_cmd.max_buffers = audio->pcm_buf_count;
+       cfg_cmd.byte_swap = 0;
+       cfg_cmd.hostpcm_config = (0x8000) | (0x4000);
+       cfg_cmd.feedback_frequency = 1;
+       cfg_cmd.partition_number = 0;
+       (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd));
+
+}
+
+static void audplay_send_data(struct audio *audio, unsigned needed)
+{
+       struct buffer *frame;
+       unsigned long flags;
+
+       spin_lock_irqsave(&audio->dsp_lock, flags);
+       if (!audio->running)
+               goto done;
+
+       if (needed && !audio->wflush) {
+               /* We were called from the callback because the DSP
+                * requested more data.  Note that the DSP does want
+                * more data, and if a buffer was in-flight, mark it
+                * as available (since the DSP must now be done with
+                * it).
+                */
+               audio->out_needed = 1;
+               frame = audio->out + audio->out_tail;
+               if (frame->used == 0xffffffff) {
+                       dprintk("frame %d free\n", audio->out_tail);
+                       frame->used = 0;
+                       audio->out_tail ^= 1;
+                       wake_up(&audio->write_wait);
+               }
+       }
+
+       if (audio->out_needed) {
+               /* If the DSP currently wants data and we have a
+                * buffer available, we will send it and reset
+                * the needed flag.  We'll mark the buffer as in-flight
+                * so that it won't be recycled until the next buffer
+                * is requested
+                */
+
+               frame = audio->out + audio->out_tail;
+               if (frame->used) {
+                       BUG_ON(frame->used == 0xffffffff);
+/*                      printk("frame %d busy\n", audio->out_tail); */
+                       audplay_dsp_send_data_avail(audio, audio->out_tail,
+                                                   frame->used);
+                       frame->used = 0xffffffff;
+                       audio->out_needed = 0;
+               }
+       }
+ done:
+       spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+/* ------------------- device --------------------- */
+
+static void audio_flush(struct audio *audio)
+{
+       audio->out[0].used = 0;
+       audio->out[1].used = 0;
+       audio->out_head = 0;
+       audio->out_tail = 0;
+       audio->reserved = 0;
+       audio->out_needed = 0;
+       atomic_set(&audio->out_bytes, 0);
+}
+
+static void audio_flush_pcm_buf(struct audio *audio)
+{
+       uint8_t index;
+
+       for (index = 0; index < PCM_BUF_MAX_COUNT; index++)
+               audio->in[index].used = 0;
+       audio->buf_refresh = 0;
+       audio->read_next = 0;
+       audio->fill_next = 0;
+}
+
+static int audaac_validate_usr_config(struct msm_audio_aac_config *config)
+{
+       int ret_val = -1;
+
+       if (config->format != AUDIO_AAC_FORMAT_ADTS &&
+               config->format != AUDIO_AAC_FORMAT_RAW &&
+               config->format != AUDIO_AAC_FORMAT_PSUEDO_RAW &&
+               config->format != AUDIO_AAC_FORMAT_LOAS)
+               goto done;
+
+       if (config->audio_object != AUDIO_AAC_OBJECT_LC &&
+               config->audio_object != AUDIO_AAC_OBJECT_LTP &&
+               config->audio_object != AUDIO_AAC_OBJECT_ERLC)
+               goto done;
+
+       if (config->audio_object == AUDIO_AAC_OBJECT_ERLC) {
+               if (config->ep_config > 3)
+                       goto done;
+               if (config->aac_scalefactor_data_resilience_flag !=
+                       AUDIO_AAC_SCA_DATA_RES_OFF &&
+                       config->aac_scalefactor_data_resilience_flag !=
+                       AUDIO_AAC_SCA_DATA_RES_ON)
+                       goto done;
+               if (config->aac_section_data_resilience_flag !=
+                       AUDIO_AAC_SEC_DATA_RES_OFF &&
+                       config->aac_section_data_resilience_flag !=
+                       AUDIO_AAC_SEC_DATA_RES_ON)
+                       goto done;
+               if (config->aac_spectral_data_resilience_flag !=
+                       AUDIO_AAC_SPEC_DATA_RES_OFF &&
+                       config->aac_spectral_data_resilience_flag !=
+                       AUDIO_AAC_SPEC_DATA_RES_ON)
+                       goto done;
+       } else {
+               config->aac_section_data_resilience_flag =
+                       AUDIO_AAC_SEC_DATA_RES_OFF;
+               config->aac_scalefactor_data_resilience_flag =
+                       AUDIO_AAC_SCA_DATA_RES_OFF;
+               config->aac_spectral_data_resilience_flag =
+                       AUDIO_AAC_SPEC_DATA_RES_OFF;
+       }
+
+       if (config->sbr_on_flag != AUDIO_AAC_SBR_ON_FLAG_OFF &&
+               config->sbr_on_flag != AUDIO_AAC_SBR_ON_FLAG_ON)
+               goto done;
+
+       if (config->sbr_ps_on_flag != AUDIO_AAC_SBR_PS_ON_FLAG_OFF &&
+               config->sbr_ps_on_flag != AUDIO_AAC_SBR_PS_ON_FLAG_ON)
+               goto done;
+
+       if (config->dual_mono_mode > AUDIO_AAC_DUAL_MONO_PL_SR)
+               goto done;
+
+       if (config->channel_configuration > 2)
+               goto done;
+
+       ret_val = 0;
+ done:
+       return ret_val;
+}
+
+static void audio_ioport_reset(struct audio *audio)
+{
+       /* Make sure read/write thread are free from
+        * sleep and knowing that system is not able
+        * to process io request at the moment
+        */
+       wake_up(&audio->write_wait);
+       mutex_lock(&audio->write_lock);
+       audio_flush(audio);
+       mutex_unlock(&audio->write_lock);
+       wake_up(&audio->read_wait);
+       mutex_lock(&audio->read_lock);
+       audio_flush_pcm_buf(audio);
+       mutex_unlock(&audio->read_lock);
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+       struct audio *audio = file->private_data;
+       int rc = 0;
+
+       dprintk("audio_ioctl() cmd = %d\n", cmd);
+
+       if (cmd == AUDIO_GET_STATS) {
+               struct msm_audio_stats stats;
+               stats.byte_count = audpp_avsync_byte_count(audio->dec_id);
+               stats.sample_count = audpp_avsync_sample_count(audio->dec_id);
+               if (copy_to_user((void *)arg, &stats, sizeof(stats)))
+                       return -EFAULT;
+               return 0;
+       }
+       if (cmd == AUDIO_SET_VOLUME) {
+               unsigned long flags;
+               spin_lock_irqsave(&audio->dsp_lock, flags);
+               audio->volume = arg;
+               if (audio->running)
+                       audpp_set_volume_and_pan(audio->dec_id, arg, 0);
+               spin_unlock_irqrestore(&audio->dsp_lock, flags);
+               return 0;
+       }
+       mutex_lock(&audio->lock);
+       switch (cmd) {
+       case AUDIO_START:
+               rc = audio_enable(audio);
+               break;
+       case AUDIO_STOP:
+               rc = audio_disable(audio);
+               audio->stopped = 1;
+               audio_ioport_reset(audio);
+               audio->stopped = 0;
+               break;
+       case AUDIO_FLUSH:
+               dprintk("%s: AUDIO_FLUSH\n", __func__);
+               audio->rflush = 1;
+               audio->wflush = 1;
+               audio_ioport_reset(audio);
+               if (audio->running)
+                       audpp_flush(audio->dec_id);
+               else {
+                       audio->rflush = 0;
+                       audio->wflush = 0;
+               }
+               break;
+
+       case AUDIO_SET_CONFIG:{
+                       struct msm_audio_config config;
+
+                       if (copy_from_user
+                           (&config, (void *)arg, sizeof(config))) {
+                               rc = -EFAULT;
+                               break;
+                       }
+
+                       if (config.channel_count == 1) {
+                               config.channel_count =
+                                   AUDPP_CMD_PCM_INTF_MONO_V;
+                       } else if (config.channel_count == 2) {
+                               config.channel_count =
+                                   AUDPP_CMD_PCM_INTF_STEREO_V;
+                       } else {
+                               rc = -EINVAL;
+                               break;
+                       }
+
+                       audio->out_sample_rate = config.sample_rate;
+                       audio->out_channel_mode = config.channel_count;
+                       rc = 0;
+                       break;
+               }
+       case AUDIO_GET_CONFIG:{
+                       struct msm_audio_config config;
+                       config.buffer_size = BUFSZ;
+                       config.buffer_count = 2;
+                       config.sample_rate = audio->out_sample_rate;
+                       if (audio->out_channel_mode ==
+                           AUDPP_CMD_PCM_INTF_MONO_V) {
+                               config.channel_count = 1;
+                       } else {
+                               config.channel_count = 2;
+                       }
+                       config.unused[0] = 0;
+                       config.unused[1] = 0;
+                       config.unused[2] = 0;
+                       config.unused[3] = 0;
+                       if (copy_to_user((void *)arg, &config,
+                                        sizeof(config)))
+                               rc = -EFAULT;
+                       else
+                               rc = 0;
+
+                       break;
+               }
+       case AUDIO_GET_AAC_CONFIG:{
+                       if (copy_to_user((void *)arg, &audio->aac_config,
+                               sizeof(audio->aac_config)))
+                               rc = -EFAULT;
+                       else
+                               rc = 0;
+                       break;
+               }
+       case AUDIO_SET_AAC_CONFIG:{
+                       struct msm_audio_aac_config usr_config;
+
+                       if (copy_from_user
+                               (&usr_config, (void *)arg,
+                                       sizeof(usr_config))) {
+                               rc = -EFAULT;
+                               break;
+                       }
+
+                       if (audaac_validate_usr_config(&usr_config) == 0) {
+                               audio->aac_config = usr_config;
+                               rc = 0;
+                       } else
+                               rc = -EINVAL;
+
+                       break;
+               }
+       case AUDIO_GET_PCM_CONFIG:{
+                       struct msm_audio_pcm_config config;
+                       config.pcm_feedback = 0;
+                       config.buffer_count = PCM_BUF_MAX_COUNT;
+                       config.buffer_size = PCM_BUFSZ_MIN;
+                       if (copy_to_user((void *)arg, &config,
+                                        sizeof(config)))
+                               rc = -EFAULT;
+                       else
+                               rc = 0;
+                       break;
+               }
+       case AUDIO_SET_PCM_CONFIG:{
+                       struct msm_audio_pcm_config config;
+                       if (copy_from_user
+                           (&config, (void *)arg, sizeof(config))) {
+                               rc = -EFAULT;
+                               break;
+                       }
+                       if ((config.buffer_count > PCM_BUF_MAX_COUNT) ||
+                           (config.buffer_count == 1))
+                               config.buffer_count = PCM_BUF_MAX_COUNT;
+
+                       if (config.buffer_size < PCM_BUFSZ_MIN)
+                               config.buffer_size = PCM_BUFSZ_MIN;
+
+                       /* Check if pcm feedback is required */
+                       if ((config.pcm_feedback) && (!audio->read_data)) {
+                               dprintk("ioctl: allocate PCM buffer %d\n",
+                                       config.buffer_count *
+                                       config.buffer_size);
+                               audio->read_data =
+                                   dma_alloc_coherent(NULL,
+                                                      config.buffer_size *
+                                                      config.buffer_count,
+                                                      &audio->read_phys,
+                                                      GFP_KERNEL);
+                               if (!audio->read_data) {
+                                       pr_err("audio_aac: buf alloc fail\n");
+                                       rc = -1;
+                               } else {
+                                       uint8_t index;
+                                       uint32_t offset = 0;
+                                       audio->pcm_feedback = 1;
+                                       audio->buf_refresh = 0;
+                                       audio->pcm_buf_count =
+                                           config.buffer_count;
+                                       audio->read_next = 0;
+                                       audio->fill_next = 0;
+
+                                       for (index = 0;
+                                            index < config.buffer_count;
+                                            index++) {
+                                               audio->in[index].data =
+                                                   audio->read_data + offset;
+                                               audio->in[index].addr =
+                                                   audio->read_phys + offset;
+                                               audio->in[index].size =
+                                                   config.buffer_size;
+                                               audio->in[index].used = 0;
+                                               offset += config.buffer_size;
+                                       }
+                                       rc = 0;
+                               }
+                       } else {
+                               rc = 0;
+                       }
+                       break;
+               }
+       case AUDIO_PAUSE:
+               dprintk("%s: AUDIO_PAUSE %ld\n", __func__, arg);
+               rc = audpp_pause(audio->dec_id, (int) arg);
+               break;
+       default:
+               rc = -EINVAL;
+       }
+       mutex_unlock(&audio->lock);
+       return rc;
+}
+
+static ssize_t audio_read(struct file *file, char __user *buf, size_t count,
+                         loff_t *pos)
+{
+       struct audio *audio = file->private_data;
+       const char __user *start = buf;
+       int rc = 0;
+
+       if (!audio->pcm_feedback)
+               return 0; /* PCM feedback is not enabled. Nothing to read */
+
+       mutex_lock(&audio->read_lock);
+       dprintk("audio_read() %d \n", count);
+       while (count > 0) {
+               rc = wait_event_interruptible(audio->read_wait,
+                                             (audio->in[audio->read_next].
+                                               used > 0) || (audio->stopped)
+                                               || (audio->rflush));
+
+               if (rc < 0)
+                       break;
+
+               if (audio->stopped || audio->rflush) {
+                       rc = -EBUSY;
+                       break;
+               }
+
+               if (count < audio->in[audio->read_next].used) {
+                       /* Read must happen in frame boundary. Since driver
+                          does not know frame size, read count must be greater
+                          or equal to size of PCM samples */
+                       dprintk("audio_read: no partial frame done reading\n");
+                       break;
+               } else {
+                       dprintk("audio_read: read from in[%d]\n",
+                               audio->read_next);
+                       if (copy_to_user
+                           (buf, audio->in[audio->read_next].data,
+                            audio->in[audio->read_next].used)) {
+                               pr_err("audio_read: invalid addr %x \n",
+                                      (unsigned int)buf);
+                               rc = -EFAULT;
+                               break;
+                       }
+                       count -= audio->in[audio->read_next].used;
+                       buf += audio->in[audio->read_next].used;
+                       audio->in[audio->read_next].used = 0;
+                       if ((++audio->read_next) == audio->pcm_buf_count)
+                               audio->read_next = 0;
+                       if (audio->in[audio->read_next].used == 0)
+                               break; /* No data ready at this moment
+                                       * Exit while loop to prevent
+                                       * output thread sleep too long
+                                       */
+               }
+       }
+
+       /* don't feed output buffer to HW decoder during flushing
+        * buffer refresh command will be sent once flush completes
+        * send buf refresh command here can confuse HW decoder
+        */
+       if (audio->buf_refresh && !audio->rflush) {
+               audio->buf_refresh = 0;
+               dprintk("audio_read: kick start pcm feedback again\n");
+               audplay_buffer_refresh(audio);
+       }
+
+       mutex_unlock(&audio->read_lock);
+
+       if (buf > start)
+               rc = buf - start;
+
+       dprintk("audio_read: read %d bytes\n", rc);
+       return rc;
+}
+
+static ssize_t audio_write(struct file *file, const char __user *buf,
+                          size_t count, loff_t *pos)
+{
+       struct audio *audio = file->private_data;
+       const char __user *start = buf;
+       struct buffer *frame;
+       size_t xfer;
+       char *cpy_ptr;
+       int rc = 0;
+       unsigned dsize;
+
+       mutex_lock(&audio->write_lock);
+       while (count > 0) {
+               frame = audio->out + audio->out_head;
+               cpy_ptr = frame->data;
+               dsize = 0;
+               rc = wait_event_interruptible(audio->write_wait,
+                                             (frame->used == 0)
+                                               || (audio->stopped)
+                                               || (audio->wflush));
+               if (rc < 0)
+                       break;
+               if (audio->stopped || audio->wflush) {
+                       rc = -EBUSY;
+                       break;
+               }
+
+               if (audio->reserved) {
+                       dprintk("%s: append reserved byte %x\n",
+                               __func__, audio->rsv_byte);
+                       *cpy_ptr = audio->rsv_byte;
+                       xfer = (count > (frame->size - 1)) ?
+                               frame->size - 1 : count;
+                       cpy_ptr++;
+                       dsize = 1;
+                       audio->reserved = 0;
+               } else
+                       xfer = (count > frame->size) ? frame->size : count;
+
+               if (copy_from_user(cpy_ptr, buf, xfer)) {
+                       rc = -EFAULT;
+                       break;
+               }
+
+               dsize += xfer;
+               if (dsize & 1) {
+                       audio->rsv_byte = ((char *) frame->data)[dsize - 1];
+                       dprintk("%s: odd length buf reserve last byte %x\n",
+                               __func__, audio->rsv_byte);
+                       audio->reserved = 1;
+                       dsize--;
+               }
+               count -= xfer;
+               buf += xfer;
+
+               if (dsize > 0) {
+                       audio->out_head ^= 1;
+                       frame->used = dsize;
+                       audplay_send_data(audio, 0);
+               }
+       }
+       mutex_unlock(&audio->write_lock);
+       if (buf > start)
+               return buf - start;
+       return rc;
+}
+
+static int audio_release(struct inode *inode, struct file *file)
+{
+       struct audio *audio = file->private_data;
+
+       dprintk("audio_release()\n");
+
+       mutex_lock(&audio->lock);
+       audio_disable(audio);
+       audio_flush(audio);
+       audio_flush_pcm_buf(audio);
+       msm_adsp_put(audio->audplay);
+       audio->audplay = NULL;
+       audio->opened = 0;
+       audio->reserved = 0;
+       dma_free_coherent(NULL, DMASZ, audio->data, audio->phys);
+       audio->data = NULL;
+       if (audio->read_data != NULL) {
+               dma_free_coherent(NULL,
+                                 audio->in[0].size * audio->pcm_buf_count,
+                                 audio->read_data, audio->read_phys);
+               audio->read_data = NULL;
+       }
+       audio->pcm_feedback = 0;
+       mutex_unlock(&audio->lock);
+       return 0;
+}
+
+struct audio the_aac_audio;
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+       struct audio *audio = &the_aac_audio;
+       int rc;
+
+       mutex_lock(&audio->lock);
+
+       if (audio->opened) {
+               pr_err("audio: busy\n");
+               rc = -EBUSY;
+               goto done;
+       }
+
+       if (!audio->data) {
+               audio->data = dma_alloc_coherent(NULL, DMASZ,
+                                                &audio->phys, GFP_KERNEL);
+               if (!audio->data) {
+                       pr_err("audio: could not allocate DMA buffers\n");
+                       rc = -ENOMEM;
+                       goto done;
+               }
+       }
+
+       rc = audmgr_open(&audio->audmgr);
+       if (rc)
+               goto done;
+
+       rc = msm_adsp_get("AUDPLAY0TASK", &audio->audplay,
+                         &audplay_adsp_ops_aac, audio);
+       if (rc) {
+               pr_err("audio: failed to get audplay0 dsp module\n");
+               goto done;
+       }
+       audio->out_sample_rate = 44100;
+       audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V;
+       audio->aac_config.format = AUDIO_AAC_FORMAT_ADTS;
+       audio->aac_config.audio_object = AUDIO_AAC_OBJECT_LC;
+       audio->aac_config.ep_config = 0;
+       audio->aac_config.aac_section_data_resilience_flag =
+               AUDIO_AAC_SEC_DATA_RES_OFF;
+       audio->aac_config.aac_scalefactor_data_resilience_flag =
+               AUDIO_AAC_SCA_DATA_RES_OFF;
+       audio->aac_config.aac_spectral_data_resilience_flag =
+               AUDIO_AAC_SPEC_DATA_RES_OFF;
+       audio->aac_config.sbr_on_flag = AUDIO_AAC_SBR_ON_FLAG_ON;
+       audio->aac_config.sbr_ps_on_flag = AUDIO_AAC_SBR_PS_ON_FLAG_ON;
+       audio->aac_config.dual_mono_mode = AUDIO_AAC_DUAL_MONO_PL_SR;
+       audio->aac_config.channel_configuration = 2;
+       audio->dec_id = 0;
+
+       audio->out[0].data = audio->data + 0;
+       audio->out[0].addr = audio->phys + 0;
+       audio->out[0].size = BUFSZ;
+
+       audio->out[1].data = audio->data + BUFSZ;
+       audio->out[1].addr = audio->phys + BUFSZ;
+       audio->out[1].size = BUFSZ;
+
+       audio->volume = 0x2000; /* Q13 1.0 */
+
+       audio_flush(audio);
+
+       file->private_data = audio;
+       audio->opened = 1;
+       rc = 0;
+done:
+       mutex_unlock(&audio->lock);
+       return rc;
+}
+
+static struct file_operations audio_aac_fops = {
+       .owner = THIS_MODULE,
+       .open = audio_open,
+       .release = audio_release,
+       .read = audio_read,
+       .write = audio_write,
+       .unlocked_ioctl = audio_ioctl,
+};
+
+struct miscdevice audio_aac_misc = {
+       .minor = MISC_DYNAMIC_MINOR,
+       .name = "msm_aac",
+       .fops = &audio_aac_fops,
+};
+
+static int __init audio_init(void)
+{
+       mutex_init(&the_aac_audio.lock);
+       mutex_init(&the_aac_audio.write_lock);
+       mutex_init(&the_aac_audio.read_lock);
+       spin_lock_init(&the_aac_audio.dsp_lock);
+       init_waitqueue_head(&the_aac_audio.write_wait);
+       init_waitqueue_head(&the_aac_audio.read_wait);
+       the_aac_audio.read_data = NULL;
+       return misc_register(&audio_aac_misc);
+}
+
+device_initcall(audio_init);
diff --git a/drivers/staging/dream/qdsp5/audio_amrnb.c b/drivers/staging/dream/qdsp5/audio_amrnb.c
new file mode 100644 (file)
index 0000000..cd818a5
--- /dev/null
@@ -0,0 +1,873 @@
+/* linux/arch/arm/mach-msm/qdsp5/audio_amrnb.c
+ *
+ * amrnb audio decoder device
+ *
+ * Copyright (c) 2008 QUALCOMM USA, INC.
+ *
+ * Based on the mp3 native driver in arch/arm/mach-msm/qdsp5/audio_mp3.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ *
+ * All source code in this file is licensed under the following license except
+ * where indicated.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/delay.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+#include <linux/msm_audio.h>
+#include "audmgr.h"
+
+#include <mach/qdsp5/qdsp5audppcmdi.h>
+#include <mach/qdsp5/qdsp5audppmsg.h>
+#include <mach/qdsp5/qdsp5audplaycmdi.h>
+#include <mach/qdsp5/qdsp5audplaymsg.h>
+
+/* for queue ids - should be relative to module number*/
+#include "adsp.h"
+
+#define DEBUG
+#ifdef DEBUG
+#define dprintk(format, arg...) \
+printk(KERN_DEBUG format, ## arg)
+#else
+#define dprintk(format, arg...) do {} while (0)
+#endif
+
+#define BUFSZ 1024 /* Hold minimum 700ms voice data */
+#define DMASZ (BUFSZ * 2)
+
+#define AUDPLAY_INVALID_READ_PTR_OFFSET        0xFFFF
+#define AUDDEC_DEC_AMRNB 10
+
+#define PCM_BUFSZ_MIN 1600 /* 100ms worth of data */
+#define AMRNB_DECODED_FRSZ 320 /* AMR-NB 20ms 8KHz mono PCM size */
+#define PCM_BUF_MAX_COUNT 5    /* DSP only accepts 5 buffers at most
+                                  but support 2 buffers currently */
+#define ROUTING_MODE_FTRT 1
+#define ROUTING_MODE_RT 2
+/* Decoder status received from AUDPPTASK */
+#define  AUDPP_DEC_STATUS_SLEEP        0
+#define         AUDPP_DEC_STATUS_INIT  1
+#define  AUDPP_DEC_STATUS_CFG   2
+#define  AUDPP_DEC_STATUS_PLAY  3
+
+struct buffer {
+       void *data;
+       unsigned size;
+       unsigned used;          /* Input usage actual DSP produced PCM size  */
+       unsigned addr;
+};
+
+struct audio {
+       struct buffer out[2];
+
+       spinlock_t dsp_lock;
+
+       uint8_t out_head;
+       uint8_t out_tail;
+       uint8_t out_needed;     /* number of buffers the dsp is waiting for */
+
+       atomic_t out_bytes;
+
+       struct mutex lock;
+       struct mutex write_lock;
+       wait_queue_head_t write_wait;
+
+       /* Host PCM section */
+       struct buffer in[PCM_BUF_MAX_COUNT];
+       struct mutex read_lock;
+       wait_queue_head_t read_wait;    /* Wait queue for read */
+       char *read_data;        /* pointer to reader buffer */
+       dma_addr_t read_phys;   /* physical address of reader buffer */
+       uint8_t read_next;      /* index to input buffers to be read next */
+       uint8_t fill_next;      /* index to buffer that DSP should be filling */
+       uint8_t pcm_buf_count;  /* number of pcm buffer allocated */
+       /* ---- End of Host PCM section */
+
+       struct msm_adsp_module *audplay;
+
+       struct audmgr audmgr;
+
+       /* data allocated for various buffers */
+       char *data;
+       dma_addr_t phys;
+
+       uint8_t opened:1;
+       uint8_t enabled:1;
+       uint8_t running:1;
+       uint8_t stopped:1;      /* set when stopped, cleared on flush */
+       uint8_t pcm_feedback:1;
+       uint8_t buf_refresh:1;
+
+       unsigned volume;
+
+       uint16_t dec_id;
+       uint32_t read_ptr_offset;
+};
+
+struct audpp_cmd_cfg_adec_params_amrnb {
+   audpp_cmd_cfg_adec_params_common     common;
+   unsigned short                       stereo_cfg;
+} __attribute__((packed)) ;
+
+static int auddec_dsp_config(struct audio *audio, int enable);
+static void audpp_cmd_cfg_adec_params(struct audio *audio);
+static void audpp_cmd_cfg_routing_mode(struct audio *audio);
+static void audamrnb_send_data(struct audio *audio, unsigned needed);
+static void audamrnb_config_hostpcm(struct audio *audio);
+static void audamrnb_buffer_refresh(struct audio *audio);
+static void audamrnb_dsp_event(void *private, unsigned id, uint16_t *msg);
+
+/* must be called with audio->lock held */
+static int audamrnb_enable(struct audio *audio)
+{
+       struct audmgr_config cfg;
+       int rc;
+
+       dprintk("audamrnb_enable()\n");
+
+       if (audio->enabled)
+               return 0;
+
+       audio->out_tail = 0;
+       audio->out_needed = 0;
+
+       cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE;
+       cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000;
+       cfg.def_method = RPC_AUD_DEF_METHOD_PLAYBACK;
+       cfg.codec = RPC_AUD_DEF_CODEC_AMR_NB;
+       cfg.snd_method = RPC_SND_METHOD_MIDI;
+
+       rc = audmgr_enable(&audio->audmgr, &cfg);
+       if (rc < 0)
+               return rc;
+
+       if (msm_adsp_enable(audio->audplay)) {
+               pr_err("audio: msm_adsp_enable(audplay) failed\n");
+               audmgr_disable(&audio->audmgr);
+               return -ENODEV;
+       }
+
+       if (audpp_enable(audio->dec_id, audamrnb_dsp_event, audio)) {
+               pr_err("audio: audpp_enable() failed\n");
+               msm_adsp_disable(audio->audplay);
+               audmgr_disable(&audio->audmgr);
+               return -ENODEV;
+       }
+       audio->enabled = 1;
+       return 0;
+}
+
+/* must be called with audio->lock held */
+static int audamrnb_disable(struct audio *audio)
+{
+       dprintk("audamrnb_disable()\n");
+       if (audio->enabled) {
+               audio->enabled = 0;
+               auddec_dsp_config(audio, 0);
+               wake_up(&audio->write_wait);
+               wake_up(&audio->read_wait);
+               msm_adsp_disable(audio->audplay);
+               audpp_disable(audio->dec_id, audio);
+               audmgr_disable(&audio->audmgr);
+               audio->out_needed = 0;
+       }
+       return 0;
+}
+
+/* ------------------- dsp --------------------- */
+static void audamrnb_update_pcm_buf_entry(struct audio *audio,
+               uint32_t *payload)
+{
+       uint8_t index;
+       unsigned long flags;
+
+       spin_lock_irqsave(&audio->dsp_lock, flags);
+       for (index = 0; index < payload[1]; index++) {
+               if (audio->in[audio->fill_next].addr ==
+                   payload[2 + index * 2]) {
+                       dprintk("audamrnb_update_pcm_buf_entry: in[%d] ready\n",
+                               audio->fill_next);
+                       audio->in[audio->fill_next].used =
+                           payload[3 + index * 2];
+                       if ((++audio->fill_next) == audio->pcm_buf_count)
+                               audio->fill_next = 0;
+
+               } else {
+                       pr_err
+                         ("audamrnb_update_pcm_buf_entry: expected=%x ret=%x\n"
+                          , audio->in[audio->fill_next].addr,
+                          payload[1 + index * 2]);
+                       break;
+               }
+       }
+       if (audio->in[audio->fill_next].used == 0) {
+               audamrnb_buffer_refresh(audio);
+       } else {
+               dprintk("audamrnb_update_pcm_buf_entry: read cannot keep up\n");
+               audio->buf_refresh = 1;
+       }
+
+       spin_unlock_irqrestore(&audio->dsp_lock, flags);
+       wake_up(&audio->read_wait);
+}
+
+static void audplay_dsp_event(void *data, unsigned id, size_t len,
+                             void (*getevent) (void *ptr, size_t len))
+{
+       struct audio *audio = data;
+       uint32_t msg[28];
+       getevent(msg, sizeof(msg));
+
+       dprintk("audplay_dsp_event: msg_id=%x\n", id);
+
+       switch (id) {
+       case AUDPLAY_MSG_DEC_NEEDS_DATA:
+               audamrnb_send_data(audio, 1);
+               break;
+
+       case AUDPLAY_MSG_BUFFER_UPDATE:
+               audamrnb_update_pcm_buf_entry(audio, msg);
+               break;
+
+       default:
+               pr_err("unexpected message from decoder \n");
+       }
+}
+
+static void audamrnb_dsp_event(void *private, unsigned id, uint16_t *msg)
+{
+       struct audio *audio = private;
+
+       switch (id) {
+       case AUDPP_MSG_STATUS_MSG:{
+                       unsigned status = msg[1];
+
+                       switch (status) {
+                       case AUDPP_DEC_STATUS_SLEEP:
+                               dprintk("decoder status: sleep \n");
+                               break;
+
+                       case AUDPP_DEC_STATUS_INIT:
+                               dprintk("decoder status: init \n");
+                               audpp_cmd_cfg_routing_mode(audio);
+                               break;
+
+                       case AUDPP_DEC_STATUS_CFG:
+                               dprintk("decoder status: cfg \n");
+                               break;
+                       case AUDPP_DEC_STATUS_PLAY:
+                               dprintk("decoder status: play \n");
+                               if (audio->pcm_feedback) {
+                                       audamrnb_config_hostpcm(audio);
+                                       audamrnb_buffer_refresh(audio);
+                               }
+                               break;
+                       default:
+                               pr_err("unknown decoder status \n");
+                               break;
+                       }
+                       break;
+               }
+       case AUDPP_MSG_CFG_MSG:
+               if (msg[0] == AUDPP_MSG_ENA_ENA) {
+                       dprintk("audamrnb_dsp_event: CFG_MSG ENABLE\n");
+                       auddec_dsp_config(audio, 1);
+                       audio->out_needed = 0;
+                       audio->running = 1;
+                       audpp_set_volume_and_pan(audio->dec_id, audio->volume,
+                                                0);
+                       audpp_avsync(audio->dec_id, 22050);
+               } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+                       dprintk("audamrnb_dsp_event: CFG_MSG DISABLE\n");
+                       audpp_avsync(audio->dec_id, 0);
+                       audio->running = 0;
+               } else {
+                       pr_err("audamrnb_dsp_event: CFG_MSG %d?\n", msg[0]);
+               }
+               break;
+       case AUDPP_MSG_ROUTING_ACK:
+               dprintk("audamrnb_dsp_event: ROUTING_ACK mode=%d\n", msg[1]);
+               audpp_cmd_cfg_adec_params(audio);
+               break;
+
+       default:
+               pr_err("audamrnb_dsp_event: UNKNOWN (%d)\n", id);
+       }
+
+}
+
+struct msm_adsp_ops audplay_adsp_ops_amrnb = {
+       .event = audplay_dsp_event,
+};
+
+#define audplay_send_queue0(audio, cmd, len) \
+       msm_adsp_write(audio->audplay, QDSP_uPAudPlay0BitStreamCtrlQueue, \
+                      cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+       audpp_cmd_cfg_dec_type cmd;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+       if (enable)
+               cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+                   AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_AMRNB;
+       else
+               cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | AUDPP_CMD_DIS_DEC_V;
+
+       return audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_adec_params(struct audio *audio)
+{
+       struct audpp_cmd_cfg_adec_params_amrnb cmd;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
+       cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_V13K_LEN;
+       cmd.common.dec_id = audio->dec_id;
+       cmd.common.input_sampling_frequency = 8000;
+       cmd.stereo_cfg = AUDPP_CMD_PCM_INTF_MONO_V;
+
+       audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_routing_mode(struct audio *audio)
+{
+       struct audpp_cmd_routing_mode cmd;
+       dprintk("audpp_cmd_cfg_routing_mode()\n");
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.cmd_id = AUDPP_CMD_ROUTING_MODE;
+       cmd.object_number = audio->dec_id;
+       if (audio->pcm_feedback)
+               cmd.routing_mode = ROUTING_MODE_FTRT;
+       else
+               cmd.routing_mode = ROUTING_MODE_RT;
+
+       audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static int audplay_dsp_send_data_avail(struct audio *audio,
+                                      unsigned idx, unsigned len)
+{
+       audplay_cmd_bitstream_data_avail cmd;
+
+       cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL;
+       cmd.decoder_id = audio->dec_id;
+       cmd.buf_ptr = audio->out[idx].addr;
+       cmd.buf_size = len / 2;
+       cmd.partition_number = 0;
+       return audplay_send_queue0(audio, &cmd, sizeof(cmd));
+}
+
+static void audamrnb_buffer_refresh(struct audio *audio)
+{
+       struct audplay_cmd_buffer_refresh refresh_cmd;
+
+       refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH;
+       refresh_cmd.num_buffers = 1;
+       refresh_cmd.buf0_address = audio->in[audio->fill_next].addr;
+       refresh_cmd.buf0_length = audio->in[audio->fill_next].size -
+         (audio->in[audio->fill_next].size % AMRNB_DECODED_FRSZ);
+       refresh_cmd.buf_read_count = 0;
+       dprintk("audplay_buffer_fresh: buf0_addr=%x buf0_len=%d\n",
+               refresh_cmd.buf0_address, refresh_cmd.buf0_length);
+       (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd));
+}
+
+static void audamrnb_config_hostpcm(struct audio *audio)
+{
+       struct audplay_cmd_hpcm_buf_cfg cfg_cmd;
+
+       dprintk("audamrnb_config_hostpcm()\n");
+       cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG;
+       cfg_cmd.max_buffers = audio->pcm_buf_count;
+       cfg_cmd.byte_swap = 0;
+       cfg_cmd.hostpcm_config = (0x8000) | (0x4000);
+       cfg_cmd.feedback_frequency = 1;
+       cfg_cmd.partition_number = 0;
+       (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd));
+
+}
+
+static void audamrnb_send_data(struct audio *audio, unsigned needed)
+{
+       struct buffer *frame;
+       unsigned long flags;
+
+       spin_lock_irqsave(&audio->dsp_lock, flags);
+       if (!audio->running)
+               goto done;
+
+       if (needed) {
+               /* We were called from the callback because the DSP
+                * requested more data.  Note that the DSP does want
+                * more data, and if a buffer was in-flight, mark it
+                * as available (since the DSP must now be done with
+                * it).
+                */
+               audio->out_needed = 1;
+               frame = audio->out + audio->out_tail;
+               if (frame->used == 0xffffffff) {
+                       frame->used = 0;
+                       audio->out_tail ^= 1;
+                       wake_up(&audio->write_wait);
+               }
+       }
+
+       if (audio->out_needed) {
+               /* If the DSP currently wants data and we have a
+                * buffer available, we will send it and reset
+                * the needed flag.  We'll mark the buffer as in-flight
+                * so that it won't be recycled until the next buffer
+                * is requested
+                */
+
+               frame = audio->out + audio->out_tail;
+               if (frame->used) {
+                       BUG_ON(frame->used == 0xffffffff);
+/*                      printk("frame %d busy\n", audio->out_tail); */
+                       audplay_dsp_send_data_avail(audio, audio->out_tail,
+                                                   frame->used);
+                       frame->used = 0xffffffff;
+                       audio->out_needed = 0;
+               }
+       }
+ done:
+       spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+/* ------------------- device --------------------- */
+
+static void audamrnb_flush(struct audio *audio)
+{
+       audio->out[0].used = 0;
+       audio->out[1].used = 0;
+       audio->out_head = 0;
+       audio->out_tail = 0;
+       audio->stopped = 0;
+       atomic_set(&audio->out_bytes, 0);
+}
+
+static void audamrnb_flush_pcm_buf(struct audio *audio)
+{
+       uint8_t index;
+
+       for (index = 0; index < PCM_BUF_MAX_COUNT; index++)
+               audio->in[index].used = 0;
+
+       audio->read_next = 0;
+       audio->fill_next = 0;
+}
+
+static long audamrnb_ioctl(struct file *file, unsigned int cmd,
+               unsigned long arg)
+{
+       struct audio *audio = file->private_data;
+       int rc = 0;
+
+       dprintk("audamrnb_ioctl() cmd = %d\n", cmd);
+
+       if (cmd == AUDIO_GET_STATS) {
+               struct msm_audio_stats stats;
+               stats.byte_count = audpp_avsync_byte_count(audio->dec_id);
+               stats.sample_count = audpp_avsync_sample_count(audio->dec_id);
+               if (copy_to_user((void *)arg, &stats, sizeof(stats)))
+                       return -EFAULT;
+               return 0;
+       }
+       if (cmd == AUDIO_SET_VOLUME) {
+               unsigned long flags;
+               spin_lock_irqsave(&audio->dsp_lock, flags);
+               audio->volume = arg;
+               if (audio->running)
+                       audpp_set_volume_and_pan(audio->dec_id, arg, 0);
+               spin_unlock_irqrestore(&audio->dsp_lock, flags);
+    return 0;
+       }
+       mutex_lock(&audio->lock);
+       switch (cmd) {
+       case AUDIO_START:
+               rc = audamrnb_enable(audio);
+               break;
+       case AUDIO_STOP:
+               rc = audamrnb_disable(audio);
+               audio->stopped = 1;
+               break;
+       case AUDIO_FLUSH:
+               if (audio->stopped) {
+                       /* Make sure we're stopped and we wake any threads
+                        * that might be blocked holding the write_lock.
+                        * While audio->stopped write threads will always
+                        * exit immediately.
+                        */
+                       wake_up(&audio->write_wait);
+                       mutex_lock(&audio->write_lock);
+                       audamrnb_flush(audio);
+                       mutex_unlock(&audio->write_lock);
+                       wake_up(&audio->read_wait);
+                       mutex_lock(&audio->read_lock);
+                       audamrnb_flush_pcm_buf(audio);
+                       mutex_unlock(&audio->read_lock);
+                       break;
+               }
+
+  case AUDIO_SET_CONFIG:{
+      dprintk("AUDIO_SET_CONFIG not applicable \n");
+                       break;
+               }
+       case AUDIO_GET_CONFIG:{
+                       struct msm_audio_config config;
+                       config.buffer_size = BUFSZ;
+                       config.buffer_count = 2;
+                       config.sample_rate = 8000;
+                       config.channel_count = 1;
+                       config.unused[0] = 0;
+                       config.unused[1] = 0;
+                       config.unused[2] = 0;
+                       config.unused[3] = 0;
+                       if (copy_to_user((void *)arg, &config,
+                                        sizeof(config)))
+                               rc = -EFAULT;
+                       else
+                               rc = 0;
+
+                       break;
+               }
+       case AUDIO_GET_PCM_CONFIG:{
+                       struct msm_audio_pcm_config config;
+                       config.pcm_feedback = 0;
+                       config.buffer_count = PCM_BUF_MAX_COUNT;
+                       config.buffer_size = PCM_BUFSZ_MIN;
+                       if (copy_to_user((void *)arg, &config,
+                                        sizeof(config)))
+                               rc = -EFAULT;
+                       else
+                               rc = 0;
+                       break;
+               }
+       case AUDIO_SET_PCM_CONFIG:{
+               struct msm_audio_pcm_config config;
+               if (copy_from_user
+                   (&config, (void *)arg, sizeof(config))) {
+                       rc = -EFAULT;
+                       break;
+               }
+               if ((config.buffer_count > PCM_BUF_MAX_COUNT) ||
+                   (config.buffer_count == 1))
+                       config.buffer_count = PCM_BUF_MAX_COUNT;
+
+               if (config.buffer_size < PCM_BUFSZ_MIN)
+                       config.buffer_size = PCM_BUFSZ_MIN;
+
+                       /* Check if pcm feedback is required */
+               if ((config.pcm_feedback) && (!audio->read_data)) {
+                       dprintk("audamrnb_ioctl: allocate PCM buf %d\n",
+                                       config.buffer_count *
+                                       config.buffer_size);
+                       audio->read_data =
+                               dma_alloc_coherent(NULL,
+                                                      config.buffer_size *
+                                                      config.buffer_count,
+                                                      &audio->read_phys,
+                                                      GFP_KERNEL);
+                       if (!audio->read_data) {
+                               pr_err("audamrnb_ioctl: no mem for pcm buf\n");
+                               rc = -1;
+                       } else {
+                               uint8_t index;
+                               uint32_t offset = 0;
+                               audio->pcm_feedback = 1;
+                               audio->buf_refresh = 0;
+                               audio->pcm_buf_count =
+                                       config.buffer_count;
+                               audio->read_next = 0;
+                               audio->fill_next = 0;
+
+                               for (index = 0;
+                               index < config.buffer_count; index++) {
+                                       audio->in[index].data =
+                                               audio->read_data + offset;
+                                       audio->in[index].addr =
+                                           audio->read_phys + offset;
+                                       audio->in[index].size =
+                                           config.buffer_size;
+                                       audio->in[index].used = 0;
+                                       offset += config.buffer_size;
+                               }
+                               rc = 0;
+                       }
+               } else {
+                       rc = 0;
+               }
+               break;
+       }
+       default:
+               rc = -EINVAL;
+       }
+       mutex_unlock(&audio->lock);
+       return rc;
+}
+
+static ssize_t audamrnb_read(struct file *file, char __user *buf, size_t count,
+                         loff_t *pos)
+{
+       struct audio *audio = file->private_data;
+       const char __user *start = buf;
+       int rc = 0;
+
+       if (!audio->pcm_feedback)
+               return 0; /* PCM feedback is not enabled. Nothing to read */
+
+       mutex_lock(&audio->read_lock);
+       dprintk("audamrnb_read() %d \n", count);
+       while (count > 0) {
+               rc = wait_event_interruptible(audio->read_wait,
+                                             (audio->in[audio->read_next].
+                                              used > 0) || (audio->stopped));
+
+               if (rc < 0)
+                       break;
+
+               if (audio->stopped) {
+                       rc = -EBUSY;
+                       break;
+               }
+
+               if (count < audio->in[audio->read_next].used) {
+                       /* Read must happen in frame boundary. Since driver does
+                        * not know frame size, read count must be greater or
+                        * equal to size of PCM samples
+                        */
+                       dprintk("audamrnb_read:read stop - partial frame\n");
+                       break;
+               } else {
+                       dprintk("audamrnb_read: read from in[%d]\n",
+                               audio->read_next);
+                       if (copy_to_user
+                           (buf, audio->in[audio->read_next].data,
+                            audio->in[audio->read_next].used)) {
+                               pr_err("audamrnb_read: invalid addr %x \n",
+                                      (unsigned int)buf);
+                               rc = -EFAULT;
+                               break;
+                       }
+                       count -= audio->in[audio->read_next].used;
+                       buf += audio->in[audio->read_next].used;
+                       audio->in[audio->read_next].used = 0;
+                       if ((++audio->read_next) == audio->pcm_buf_count)
+                               audio->read_next = 0;
+               }
+       }
+
+       if (audio->buf_refresh) {
+               audio->buf_refresh = 0;
+               dprintk("audamrnb_read: kick start pcm feedback again\n");
+               audamrnb_buffer_refresh(audio);
+       }
+
+       mutex_unlock(&audio->read_lock);
+
+       if (buf > start)
+               rc = buf - start;
+
+       dprintk("audamrnb_read: read %d bytes\n", rc);
+       return rc;
+}
+
+static ssize_t audamrnb_write(struct file *file, const char __user *buf,
+                          size_t count, loff_t *pos)
+{
+       struct audio *audio = file->private_data;
+       const char __user *start = buf;
+       struct buffer *frame;
+       size_t xfer;
+       int rc = 0;
+
+       if (count & 1)
+               return -EINVAL;
+       dprintk("audamrnb_write() \n");
+       mutex_lock(&audio->write_lock);
+       while (count > 0) {
+               frame = audio->out + audio->out_head;
+               rc = wait_event_interruptible(audio->write_wait,
+                                             (frame->used == 0)
+                                             || (audio->stopped));
+               dprintk("audamrnb_write() buffer available\n");
+               if (rc < 0)
+                       break;
+               if (audio->stopped) {
+                       rc = -EBUSY;
+                       break;
+               }
+               xfer = (count > frame->size) ? frame->size : count;
+               if (copy_from_user(frame->data, buf, xfer)) {
+                       rc = -EFAULT;
+                       break;
+               }
+
+               frame->used = xfer;
+               audio->out_head ^= 1;
+               count -= xfer;
+               buf += xfer;
+
+               audamrnb_send_data(audio, 0);
+
+       }
+       mutex_unlock(&audio->write_lock);
+       if (buf > start)
+               return buf - start;
+       return rc;
+}
+
+static int audamrnb_release(struct inode *inode, struct file *file)
+{
+       struct audio *audio = file->private_data;
+
+       dprintk("audamrnb_release()\n");
+
+       mutex_lock(&audio->lock);
+       audamrnb_disable(audio);
+       audamrnb_flush(audio);
+       audamrnb_flush_pcm_buf(audio);
+       msm_adsp_put(audio->audplay);
+       audio->audplay = NULL;
+       audio->opened = 0;
+       dma_free_coherent(NULL, DMASZ, audio->data, audio->phys);
+       audio->data = NULL;
+       if (audio->read_data != NULL) {
+               dma_free_coherent(NULL,
+                                 audio->in[0].size * audio->pcm_buf_count,
+                                 audio->read_data, audio->read_phys);
+               audio->read_data = NULL;
+       }
+       audio->pcm_feedback = 0;
+       mutex_unlock(&audio->lock);
+       return 0;
+}
+
+static struct audio the_amrnb_audio;
+
+static int audamrnb_open(struct inode *inode, struct file *file)
+{
+       struct audio *audio = &the_amrnb_audio;
+       int rc;
+
+       mutex_lock(&audio->lock);
+
+       if (audio->opened) {
+               pr_err("audio: busy\n");
+               rc = -EBUSY;
+               goto done;
+       }
+
+       if (!audio->data) {
+               audio->data = dma_alloc_coherent(NULL, DMASZ,
+                                                &audio->phys, GFP_KERNEL);
+               if (!audio->data) {
+                       pr_err("audio: could not allocate DMA buffers\n");
+                       rc = -ENOMEM;
+                       goto done;
+               }
+       }
+
+       rc = audmgr_open(&audio->audmgr);
+       if (rc)
+               goto done;
+
+       rc = msm_adsp_get("AUDPLAY0TASK", &audio->audplay,
+               &audplay_adsp_ops_amrnb, audio);
+       if (rc) {
+               pr_err("audio: failed to get audplay0 dsp module\n");
+               audmgr_disable(&audio->audmgr);
+               dma_free_coherent(NULL, DMASZ, audio->data, audio->phys);
+               audio->data = NULL;
+               goto done;
+       }
+
+       audio->dec_id = 0;
+
+       audio->out[0].data = audio->data + 0;
+       audio->out[0].addr = audio->phys + 0;
+       audio->out[0].size = BUFSZ;
+
+       audio->out[1].data = audio->data + BUFSZ;
+       audio->out[1].addr = audio->phys + BUFSZ;
+       audio->out[1].size = BUFSZ;
+
+       audio->volume = 0x2000; /* Q13 1.0 */
+
+       audamrnb_flush(audio);
+
+       file->private_data = audio;
+       audio->opened = 1;
+       rc = 0;
+done:
+       mutex_unlock(&audio->lock);
+       return rc;
+}
+
+static struct file_operations audio_amrnb_fops = {
+       .owner = THIS_MODULE,
+       .open = audamrnb_open,
+       .release = audamrnb_release,
+       .read = audamrnb_read,
+       .write = audamrnb_write,
+       .unlocked_ioctl = audamrnb_ioctl,
+};
+
+struct miscdevice audio_amrnb_misc = {
+       .minor = MISC_DYNAMIC_MINOR,
+       .name = "msm_amrnb",
+       .fops = &audio_amrnb_fops,
+};
+
+static int __init audamrnb_init(void)
+{
+       mutex_init(&the_amrnb_audio.lock);
+       mutex_init(&the_amrnb_audio.write_lock);
+       mutex_init(&the_amrnb_audio.read_lock);
+       spin_lock_init(&the_amrnb_audio.dsp_lock);
+       init_waitqueue_head(&the_amrnb_audio.write_wait);
+       init_waitqueue_head(&the_amrnb_audio.read_wait);
+       the_amrnb_audio.read_data = NULL;
+       return misc_register(&audio_amrnb_misc);
+}
+
+static void __exit audamrnb_exit(void)
+{
+       misc_deregister(&audio_amrnb_misc);
+}
+
+module_init(audamrnb_init);
+module_exit(audamrnb_exit);
+
+MODULE_DESCRIPTION("MSM AMR-NB driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("QUALCOMM Inc");
diff --git a/drivers/staging/dream/qdsp5/audio_evrc.c b/drivers/staging/dream/qdsp5/audio_evrc.c
new file mode 100644 (file)
index 0000000..4b43e18
--- /dev/null
@@ -0,0 +1,845 @@
+/* arch/arm/mach-msm/audio_evrc.c
+ *
+ * Copyright (c) 2008 QUALCOMM USA, INC.
+ *
+ * This code also borrows from audio_aac.c, which is
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ *
+ * 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org.
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+#include <linux/msm_audio.h>
+#include "audmgr.h"
+
+#include <mach/qdsp5/qdsp5audppcmdi.h>
+#include <mach/qdsp5/qdsp5audppmsg.h>
+#include <mach/qdsp5/qdsp5audplaycmdi.h>
+#include <mach/qdsp5/qdsp5audplaymsg.h>
+
+#include "adsp.h"
+
+#ifdef DEBUG
+#define dprintk(format, arg...) \
+       printk(KERN_DEBUG format, ## arg)
+#else
+#define dprintk(format, arg...) do {} while (0)
+#endif
+
+/* Hold 30 packets of 24 bytes each*/
+#define BUFSZ                  720
+#define DMASZ                  (BUFSZ * 2)
+
+#define AUDDEC_DEC_EVRC        12
+
+#define PCM_BUFSZ_MIN          1600    /* 100ms worth of data */
+#define PCM_BUF_MAX_COUNT      5
+/* DSP only accepts 5 buffers at most
+ * but support 2 buffers currently
+ */
+#define EVRC_DECODED_FRSZ      320     /* EVRC 20ms 8KHz mono PCM size */
+
+#define ROUTING_MODE_FTRT      1
+#define ROUTING_MODE_RT        2
+/* Decoder status received from AUDPPTASK */
+#define  AUDPP_DEC_STATUS_SLEEP        0
+#define         AUDPP_DEC_STATUS_INIT  1
+#define  AUDPP_DEC_STATUS_CFG   2
+#define  AUDPP_DEC_STATUS_PLAY  3
+
+struct buffer {
+       void *data;
+       unsigned size;
+       unsigned used;          /* Input usage actual DSP produced PCM size  */
+       unsigned addr;
+};
+
+struct audio {
+       struct buffer out[2];
+
+       spinlock_t dsp_lock;
+
+       uint8_t out_head;
+       uint8_t out_tail;
+       uint8_t out_needed;     /* number of buffers the dsp is waiting for */
+
+       atomic_t out_bytes;
+
+       struct mutex lock;
+       struct mutex write_lock;
+       wait_queue_head_t write_wait;
+
+       /* Host PCM section */
+       struct buffer in[PCM_BUF_MAX_COUNT];
+       struct mutex read_lock;
+       wait_queue_head_t read_wait;    /* Wait queue for read */
+       char *read_data;        /* pointer to reader buffer */
+       dma_addr_t read_phys;   /* physical address of reader buffer */
+       uint8_t read_next;      /* index to input buffers to be read next */
+       uint8_t fill_next;      /* index to buffer that DSP should be filling */
+       uint8_t pcm_buf_count;  /* number of pcm buffer allocated */
+       /* ---- End of Host PCM section */
+
+       struct msm_adsp_module *audplay;
+       struct audmgr audmgr;
+
+       /* data allocated for various buffers */
+       char *data;
+       dma_addr_t phys;
+
+       uint8_t opened:1;
+       uint8_t enabled:1;
+       uint8_t running:1;
+       uint8_t stopped:1;      /* set when stopped, cleared on flush */
+       uint8_t pcm_feedback:1;
+       uint8_t buf_refresh:1;
+
+       unsigned volume;
+       uint16_t dec_id;
+       uint32_t read_ptr_offset;
+};
+static struct audio the_evrc_audio;
+
+static int auddec_dsp_config(struct audio *audio, int enable);
+static void audpp_cmd_cfg_adec_params(struct audio *audio);
+static void audpp_cmd_cfg_routing_mode(struct audio *audio);
+static void audevrc_send_data(struct audio *audio, unsigned needed);
+static void audevrc_dsp_event(void *private, unsigned id, uint16_t *msg);
+static void audevrc_config_hostpcm(struct audio *audio);
+static void audevrc_buffer_refresh(struct audio *audio);
+
+/* must be called with audio->lock held */
+static int audevrc_enable(struct audio *audio)
+{
+       struct audmgr_config cfg;
+       int rc;
+
+       if (audio->enabled)
+               return 0;
+
+       audio->out_tail = 0;
+       audio->out_needed = 0;
+
+       cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE;
+       cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000;
+       cfg.def_method = RPC_AUD_DEF_METHOD_PLAYBACK;
+       cfg.codec = RPC_AUD_DEF_CODEC_EVRC;
+       cfg.snd_method = RPC_SND_METHOD_MIDI;
+
+       rc = audmgr_enable(&audio->audmgr, &cfg);
+       if (rc < 0)
+               return rc;
+
+       if (msm_adsp_enable(audio->audplay)) {
+               pr_err("audio: msm_adsp_enable(audplay) failed\n");
+               audmgr_disable(&audio->audmgr);
+               return -ENODEV;
+       }
+
+       if (audpp_enable(audio->dec_id, audevrc_dsp_event, audio)) {
+               pr_err("audio: audpp_enable() failed\n");
+               msm_adsp_disable(audio->audplay);
+               audmgr_disable(&audio->audmgr);
+               return -ENODEV;
+       }
+       audio->enabled = 1;
+       return 0;
+}
+
+/* must be called with audio->lock held */
+static int audevrc_disable(struct audio *audio)
+{
+       if (audio->enabled) {
+               audio->enabled = 0;
+               auddec_dsp_config(audio, 0);
+               wake_up(&audio->write_wait);
+               wake_up(&audio->read_wait);
+               msm_adsp_disable(audio->audplay);
+               audpp_disable(audio->dec_id, audio);
+               audmgr_disable(&audio->audmgr);
+               audio->out_needed = 0;
+       }
+       return 0;
+}
+
+/* ------------------- dsp --------------------- */
+
+static void audevrc_update_pcm_buf_entry(struct audio *audio,
+                                        uint32_t *payload)
+{
+       uint8_t index;
+       unsigned long flags;
+
+       spin_lock_irqsave(&audio->dsp_lock, flags);
+       for (index = 0; index < payload[1]; index++) {
+               if (audio->in[audio->fill_next].addr
+                               == payload[2 + index * 2]) {
+                       dprintk("audevrc_update_pcm_buf_entry: in[%d] ready\n",
+                               audio->fill_next);
+                       audio->in[audio->fill_next].used =
+                               payload[3 + index * 2];
+                       if ((++audio->fill_next) == audio->pcm_buf_count)
+                               audio->fill_next = 0;
+
+               } else {
+                       pr_err
+                       ("audevrc_update_pcm_buf_entry: expected=%x ret=%x\n",
+                               audio->in[audio->fill_next].addr,
+                               payload[1 + index * 2]);
+                       break;
+               }
+       }
+       if (audio->in[audio->fill_next].used == 0) {
+               audevrc_buffer_refresh(audio);
+       } else {
+               dprintk("audevrc_update_pcm_buf_entry: read cannot keep up\n");
+               audio->buf_refresh = 1;
+       }
+
+       spin_unlock_irqrestore(&audio->dsp_lock, flags);
+       wake_up(&audio->read_wait);
+}
+
+static void audplay_dsp_event(void *data, unsigned id, size_t len,
+                             void (*getevent) (void *ptr, size_t len))
+{
+       struct audio *audio = data;
+       uint32_t msg[28];
+       getevent(msg, sizeof(msg));
+
+       dprintk("audplay_dsp_event: msg_id=%x\n", id);
+       switch (id) {
+       case AUDPLAY_MSG_DEC_NEEDS_DATA:
+               audevrc_send_data(audio, 1);
+               break;
+       case AUDPLAY_MSG_BUFFER_UPDATE:
+               dprintk("audevrc_update_pcm_buf_entry:======> \n");
+               audevrc_update_pcm_buf_entry(audio, msg);
+               break;
+       default:
+               pr_err("unexpected message from decoder \n");
+       }
+}
+
+static void audevrc_dsp_event(void *private, unsigned id, uint16_t *msg)
+{
+       struct audio *audio = private;
+
+       switch (id) {
+       case AUDPP_MSG_STATUS_MSG:{
+                       unsigned status = msg[1];
+
+                       switch (status) {
+                       case AUDPP_DEC_STATUS_SLEEP:
+                               dprintk("decoder status: sleep \n");
+                               break;
+
+                       case AUDPP_DEC_STATUS_INIT:
+                               dprintk("decoder status: init \n");
+                               audpp_cmd_cfg_routing_mode(audio);
+                               break;
+
+                       case AUDPP_DEC_STATUS_CFG:
+                               dprintk("decoder status: cfg \n");
+                               break;
+                       case AUDPP_DEC_STATUS_PLAY:
+                               dprintk("decoder status: play \n");
+                               if (audio->pcm_feedback) {
+                                       audevrc_config_hostpcm(audio);
+                                       audevrc_buffer_refresh(audio);
+                               }
+                               break;
+                       default:
+                               pr_err("unknown decoder status \n");
+                       }
+                       break;
+               }
+       case AUDPP_MSG_CFG_MSG:
+               if (msg[0] == AUDPP_MSG_ENA_ENA) {
+                       dprintk("audevrc_dsp_event: CFG_MSG ENABLE\n");
+                       auddec_dsp_config(audio, 1);
+                       audio->out_needed = 0;
+                       audio->running = 1;
+                       audpp_set_volume_and_pan(audio->dec_id, audio->volume,
+                                                0);
+                       audpp_avsync(audio->dec_id, 22050);
+               } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+                       dprintk("audevrc_dsp_event: CFG_MSG DISABLE\n");
+                       audpp_avsync(audio->dec_id, 0);
+                       audio->running = 0;
+               } else {
+                       pr_err("audevrc_dsp_event: CFG_MSG %d?\n", msg[0]);
+               }
+               break;
+       case AUDPP_MSG_ROUTING_ACK:
+               dprintk("audevrc_dsp_event: ROUTING_ACK\n");
+               audpp_cmd_cfg_adec_params(audio);
+               break;
+
+       default:
+               pr_err("audevrc_dsp_event: UNKNOWN (%d)\n", id);
+       }
+
+}
+
+struct msm_adsp_ops audplay_adsp_ops_evrc = {
+       .event = audplay_dsp_event,
+};
+
+#define audplay_send_queue0(audio, cmd, len) \
+       msm_adsp_write(audio->audplay, QDSP_uPAudPlay0BitStreamCtrlQueue, \
+                      cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+       audpp_cmd_cfg_dec_type cmd;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+       if (enable)
+               cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+                   AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_EVRC;
+       else
+               cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | AUDPP_CMD_DIS_DEC_V;
+
+       return audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_adec_params(struct audio *audio)
+{
+       struct audpp_cmd_cfg_adec_params_evrc cmd;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
+       cmd.common.length = sizeof(cmd);
+       cmd.common.dec_id = audio->dec_id;
+       cmd.common.input_sampling_frequency = 8000;
+       cmd.stereo_cfg = AUDPP_CMD_PCM_INTF_MONO_V;
+
+       audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_routing_mode(struct audio *audio)
+{
+       struct audpp_cmd_routing_mode cmd;
+       dprintk("audpp_cmd_cfg_routing_mode()\n");
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.cmd_id = AUDPP_CMD_ROUTING_MODE;
+       cmd.object_number = audio->dec_id;
+       if (audio->pcm_feedback)
+               cmd.routing_mode = ROUTING_MODE_FTRT;
+       else
+               cmd.routing_mode = ROUTING_MODE_RT;
+
+       audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static int audplay_dsp_send_data_avail(struct audio *audio,
+                                      unsigned idx, unsigned len)
+{
+       audplay_cmd_bitstream_data_avail cmd;
+
+       cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL;
+       cmd.decoder_id = audio->dec_id;
+       cmd.buf_ptr = audio->out[idx].addr;
+       cmd.buf_size = len / 2;
+       cmd.partition_number = 0;
+       return audplay_send_queue0(audio, &cmd, sizeof(cmd));
+}
+
+static void audevrc_buffer_refresh(struct audio *audio)
+{
+       struct audplay_cmd_buffer_refresh refresh_cmd;
+
+       refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH;
+       refresh_cmd.num_buffers = 1;
+       refresh_cmd.buf0_address = audio->in[audio->fill_next].addr;
+       refresh_cmd.buf0_length = audio->in[audio->fill_next].size;
+
+       refresh_cmd.buf_read_count = 0;
+       dprintk("audplay_buffer_fresh: buf0_addr=%x buf0_len=%d\n",
+               refresh_cmd.buf0_address, refresh_cmd.buf0_length);
+       audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd));
+}
+
+static void audevrc_config_hostpcm(struct audio *audio)
+{
+       struct audplay_cmd_hpcm_buf_cfg cfg_cmd;
+
+       dprintk("audevrc_config_hostpcm()\n");
+       cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG;
+       cfg_cmd.max_buffers = 1;
+       cfg_cmd.byte_swap = 0;
+       cfg_cmd.hostpcm_config = (0x8000) | (0x4000);
+       cfg_cmd.feedback_frequency = 1;
+       cfg_cmd.partition_number = 0;
+       audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd));
+
+}
+
+static void audevrc_send_data(struct audio *audio, unsigned needed)
+{
+       struct buffer *frame;
+       unsigned long flags;
+
+       spin_lock_irqsave(&audio->dsp_lock, flags);
+       if (!audio->running)
+               goto done;
+
+       if (needed) {
+               /* We were called from the callback because the DSP
+                * requested more data.  Note that the DSP does want
+                * more data, and if a buffer was in-flight, mark it
+                * as available (since the DSP must now be done with
+                * it).
+                */
+               audio->out_needed = 1;
+               frame = audio->out + audio->out_tail;
+               if (frame->used == 0xffffffff) {
+                       dprintk("frame %d free\n", audio->out_tail);
+                       frame->used = 0;
+                       audio->out_tail ^= 1;
+                       wake_up(&audio->write_wait);
+               }
+       }
+
+       if (audio->out_needed) {
+               /* If the DSP currently wants data and we have a
+                * buffer available, we will send it and reset
+                * the needed flag.  We'll mark the buffer as in-flight
+                * so that it won't be recycled until the next buffer
+                * is requested
+                */
+
+               frame = audio->out + audio->out_tail;
+               if (frame->used) {
+                       BUG_ON(frame->used == 0xffffffff);
+                       dprintk("frame %d busy\n", audio->out_tail);
+                       audplay_dsp_send_data_avail(audio, audio->out_tail,
+                                                   frame->used);
+                       frame->used = 0xffffffff;
+                       audio->out_needed = 0;
+               }
+       }
+done:
+       spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+/* ------------------- device --------------------- */
+
+static void audevrc_flush(struct audio *audio)
+{
+       audio->out[0].used = 0;
+       audio->out[1].used = 0;
+       audio->out_head = 0;
+       audio->out_tail = 0;
+       audio->stopped = 0;
+       atomic_set(&audio->out_bytes, 0);
+}
+
+static void audevrc_flush_pcm_buf(struct audio *audio)
+{
+       uint8_t index;
+
+       for (index = 0; index < PCM_BUF_MAX_COUNT; index++)
+               audio->in[index].used = 0;
+
+       audio->read_next = 0;
+       audio->fill_next = 0;
+}
+
+static long audevrc_ioctl(struct file *file, unsigned int cmd,
+                         unsigned long arg)
+{
+       struct audio *audio = file->private_data;
+       int rc = 0;
+
+       dprintk("audevrc_ioctl() cmd = %d\n", cmd);
+
+       if (cmd == AUDIO_GET_STATS) {
+               struct msm_audio_stats stats;
+               stats.byte_count = audpp_avsync_byte_count(audio->dec_id);
+               stats.sample_count = audpp_avsync_sample_count(audio->dec_id);
+               if (copy_to_user((void *)arg, &stats, sizeof(stats)))
+                       return -EFAULT;
+               return 0;
+       }
+       if (cmd == AUDIO_SET_VOLUME) {
+               unsigned long flags;
+               spin_lock_irqsave(&audio->dsp_lock, flags);
+               audio->volume = arg;
+               if (audio->running)
+                       audpp_set_volume_and_pan(audio->dec_id, arg, 0);
+               spin_unlock_irqrestore(&audio->dsp_lock, flags);
+               return 0;
+       }
+       mutex_lock(&audio->lock);
+       switch (cmd) {
+       case AUDIO_START:
+               rc = audevrc_enable(audio);
+               break;
+       case AUDIO_STOP:
+               rc = audevrc_disable(audio);
+               audio->stopped = 1;
+               break;
+       case AUDIO_SET_CONFIG:{
+                       dprintk("AUDIO_SET_CONFIG not applicable \n");
+                       break;
+               }
+       case AUDIO_GET_CONFIG:{
+                       struct msm_audio_config config;
+                       config.buffer_size = BUFSZ;
+                       config.buffer_count = 2;
+                       config.sample_rate = 8000;
+                       config.channel_count = 1;
+                       config.unused[0] = 0;
+                       config.unused[1] = 0;
+                       config.unused[2] = 0;
+                       config.unused[3] = 0;
+                       if (copy_to_user((void *)arg, &config, sizeof(config)))
+                               rc = -EFAULT;
+                       else
+                               rc = 0;
+                       break;
+               }
+       case AUDIO_GET_PCM_CONFIG:{
+                       struct msm_audio_pcm_config config;
+                       config.pcm_feedback = 0;
+                       config.buffer_count = PCM_BUF_MAX_COUNT;
+                       config.buffer_size = PCM_BUFSZ_MIN;
+                       if (copy_to_user((void *)arg, &config, sizeof(config)))
+                               rc = -EFAULT;
+                       else
+                               rc = 0;
+                       break;
+               }
+       case AUDIO_SET_PCM_CONFIG:{
+                       struct msm_audio_pcm_config config;
+                       if (copy_from_user
+                           (&config, (void *)arg, sizeof(config))) {
+                               rc = -EFAULT;
+                               break;
+                       }
+                       if ((config.buffer_count > PCM_BUF_MAX_COUNT) ||
+                           (config.buffer_count == 1))
+                               config.buffer_count = PCM_BUF_MAX_COUNT;
+
+                       if (config.buffer_size < PCM_BUFSZ_MIN)
+                               config.buffer_size = PCM_BUFSZ_MIN;
+
+                       /* Check if pcm feedback is required */
+                       if ((config.pcm_feedback) && (!audio->read_data)) {
+                               dprintk("audevrc_ioctl: allocate PCM buf %d\n",
+                                       config.buffer_count *
+                                       config.buffer_size);
+                               audio->read_data =
+                                   dma_alloc_coherent(NULL,
+                                                      config.buffer_size *
+                                                      config.buffer_count,
+                                                      &audio->read_phys,
+                                                      GFP_KERNEL);
+                               if (!audio->read_data) {
+                                       pr_err
+                                       ("audevrc_ioctl: no mem for pcm buf\n");
+                                       rc = -1;
+                               } else {
+                                       uint8_t index;
+                                       uint32_t offset = 0;
+                                       audio->pcm_feedback = 1;
+                                       audio->buf_refresh = 0;
+                                       audio->pcm_buf_count =
+                                           config.buffer_count;
+                                       audio->read_next = 0;
+                                       audio->fill_next = 0;
+
+                                       for (index = 0;
+                                            index < config.buffer_count;
+                                            index++) {
+                                               audio->in[index].data =
+                                                   audio->read_data + offset;
+                                               audio->in[index].addr =
+                                                   audio->read_phys + offset;
+                                               audio->in[index].size =
+                                                   config.buffer_size;
+                                               audio->in[index].used = 0;
+                                               offset += config.buffer_size;
+                                       }
+                                       rc = 0;
+                               }
+                       } else {
+                               rc = 0;
+                       }
+                       break;
+               }
+       case AUDIO_PAUSE:
+               dprintk("%s: AUDIO_PAUSE %ld\n", __func__, arg);
+               rc = audpp_pause(audio->dec_id, (int) arg);
+               break;
+       default:
+               rc = -EINVAL;
+       }
+       mutex_unlock(&audio->lock);
+       return rc;
+}
+
+static ssize_t audevrc_read(struct file *file, char __user *buf, size_t count,
+                           loff_t *pos)
+{
+       struct audio *audio = file->private_data;
+       const char __user *start = buf;
+       int rc = 0;
+       if (!audio->pcm_feedback) {
+               return 0;
+               /* PCM feedback is not enabled. Nothing to read */
+       }
+       mutex_lock(&audio->read_lock);
+       dprintk("audevrc_read() \n");
+       while (count > 0) {
+               rc = wait_event_interruptible(audio->read_wait,
+                                             (audio->in[audio->read_next].
+                                              used > 0) || (audio->stopped));
+               dprintk("audevrc_read() wait terminated \n");
+               if (rc < 0)
+                       break;
+               if (audio->stopped) {
+                       rc = -EBUSY;
+                       break;
+               }
+               if (count < audio->in[audio->read_next].used) {
+                       /* Read must happen in frame boundary. Since driver does
+                        * not know frame size, read count must be greater or
+                        * equal to size of PCM samples
+                        */
+                       dprintk("audevrc_read:read stop - partial frame\n");
+                       break;
+               } else {
+                       dprintk("audevrc_read: read from in[%d]\n",
+                               audio->read_next);
+                       if (copy_to_user
+                           (buf, audio->in[audio->read_next].data,
+                            audio->in[audio->read_next].used)) {
+                               pr_err("audevrc_read: invalid addr %x \n",
+                                      (unsigned int)buf);
+                               rc = -EFAULT;
+                               break;
+                       }
+                       count -= audio->in[audio->read_next].used;
+                       buf += audio->in[audio->read_next].used;
+                       audio->in[audio->read_next].used = 0;
+                       if ((++audio->read_next) == audio->pcm_buf_count)
+                               audio->read_next = 0;
+                       if (audio->in[audio->read_next].used == 0)
+                               break;  /* No data ready at this moment
+                                        * Exit while loop to prevent
+                                        * output thread sleep too long
+                                        */
+
+               }
+       }
+       if (audio->buf_refresh) {
+               audio->buf_refresh = 0;
+               dprintk("audevrc_read: kick start pcm feedback again\n");
+               audevrc_buffer_refresh(audio);
+       }
+       mutex_unlock(&audio->read_lock);
+       if (buf > start)
+               rc = buf - start;
+       dprintk("audevrc_read: read %d bytes\n", rc);
+       return rc;
+}
+
+static ssize_t audevrc_write(struct file *file, const char __user *buf,
+                            size_t count, loff_t *pos)
+{
+       struct audio *audio = file->private_data;
+       const char __user *start = buf;
+       struct buffer *frame;
+       size_t xfer;
+       int rc = 0;
+
+       if (count & 1)
+               return -EINVAL;
+       mutex_lock(&audio->write_lock);
+       dprintk("audevrc_write() \n");
+       while (count > 0) {
+               frame = audio->out + audio->out_head;
+               rc = wait_event_interruptible(audio->write_wait,
+                                             (frame->used == 0)
+                                             || (audio->stopped));
+               if (rc < 0)
+                       break;
+               if (audio->stopped) {
+                       rc = -EBUSY;
+                       break;
+               }
+               xfer = (count > frame->size) ? frame->size : count;
+               if (copy_from_user(frame->data, buf, xfer)) {
+                       rc = -EFAULT;
+                       break;
+               }
+
+               frame->used = xfer;
+               audio->out_head ^= 1;
+               count -= xfer;
+               buf += xfer;
+
+               audevrc_send_data(audio, 0);
+
+       }
+       mutex_unlock(&audio->write_lock);
+       if (buf > start)
+               return buf - start;
+       return rc;
+}
+
+static int audevrc_release(struct inode *inode, struct file *file)
+{
+       struct audio *audio = file->private_data;
+
+       dprintk("audevrc_release()\n");
+
+       mutex_lock(&audio->lock);
+       audevrc_disable(audio);
+       audevrc_flush(audio);
+       audevrc_flush_pcm_buf(audio);
+       msm_adsp_put(audio->audplay);
+       audio->audplay = NULL;
+       audio->opened = 0;
+       dma_free_coherent(NULL, DMASZ, audio->data, audio->phys);
+       audio->data = NULL;
+       if (audio->read_data != NULL) {
+               dma_free_coherent(NULL,
+                                 audio->in[0].size * audio->pcm_buf_count,
+                                 audio->read_data, audio->read_phys);
+               audio->read_data = NULL;
+       }
+       audio->pcm_feedback = 0;
+       mutex_unlock(&audio->lock);
+       return 0;
+}
+
+static struct audio the_evrc_audio;
+
+static int audevrc_open(struct inode *inode, struct file *file)
+{
+       struct audio *audio = &the_evrc_audio;
+       int rc;
+
+       if (audio->opened) {
+               pr_err("audio: busy\n");
+               return -EBUSY;
+       }
+
+       /* Acquire Lock */
+       mutex_lock(&audio->lock);
+
+       if (!audio->data) {
+               audio->data = dma_alloc_coherent(NULL, DMASZ,
+                                                &audio->phys, GFP_KERNEL);
+               if (!audio->data) {
+                       pr_err("audio: could not allocate DMA buffers\n");
+                       rc = -ENOMEM;
+                       goto dma_fail;
+               }
+       }
+
+       rc = audmgr_open(&audio->audmgr);
+       if (rc)
+               goto audmgr_fail;
+
+       rc = msm_adsp_get("AUDPLAY0TASK", &audio->audplay,
+                         &audplay_adsp_ops_evrc, audio);
+       if (rc) {
+               pr_err("audio: failed to get audplay0 dsp module\n");
+               goto adsp_fail;
+       }
+
+       audio->dec_id = 0;
+
+       audio->out[0].data = audio->data + 0;
+       audio->out[0].addr = audio->phys + 0;
+       audio->out[0].size = BUFSZ;
+
+       audio->out[1].data = audio->data + BUFSZ;
+       audio->out[1].addr = audio->phys + BUFSZ;
+       audio->out[1].size = BUFSZ;
+
+       audio->volume = 0x3FFF;
+
+       audevrc_flush(audio);
+
+       audio->opened = 1;
+       file->private_data = audio;
+
+       mutex_unlock(&audio->lock);
+       return rc;
+
+adsp_fail:
+       audmgr_close(&audio->audmgr);
+audmgr_fail:
+       dma_free_coherent(NULL, DMASZ, audio->data, audio->phys);
+dma_fail:
+       mutex_unlock(&audio->lock);
+       return rc;
+}
+
+static struct file_operations audio_evrc_fops = {
+       .owner = THIS_MODULE,
+       .open = audevrc_open,
+       .release = audevrc_release,
+       .read = audevrc_read,
+       .write = audevrc_write,
+       .unlocked_ioctl = audevrc_ioctl,
+};
+
+struct miscdevice audio_evrc_misc = {
+       .minor = MISC_DYNAMIC_MINOR,
+       .name = "msm_evrc",
+       .fops = &audio_evrc_fops,
+};
+
+static int __init audevrc_init(void)
+{
+       mutex_init(&the_evrc_audio.lock);
+       mutex_init(&the_evrc_audio.write_lock);
+       mutex_init(&the_evrc_audio.read_lock);
+       spin_lock_init(&the_evrc_audio.dsp_lock);
+       init_waitqueue_head(&the_evrc_audio.write_wait);
+       init_waitqueue_head(&the_evrc_audio.read_wait);
+       the_evrc_audio.read_data = NULL;
+       return misc_register(&audio_evrc_misc);
+}
+
+static void __exit audevrc_exit(void)
+{
+       misc_deregister(&audio_evrc_misc);
+}
+
+module_init(audevrc_init);
+module_exit(audevrc_exit);
+
+MODULE_DESCRIPTION("MSM EVRC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("QUALCOMM Inc");
diff --git a/drivers/staging/dream/qdsp5/audio_in.c b/drivers/staging/dream/qdsp5/audio_in.c
new file mode 100644 (file)
index 0000000..6df70d8
--- /dev/null
@@ -0,0 +1,967 @@
+/* arch/arm/mach-msm/qdsp5/audio_in.c
+ *
+ * pcm audio input device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ *
+ * 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/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/delay.h>
+
+#include <linux/msm_audio.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+#include <mach/msm_rpcrouter.h>
+
+#include "audmgr.h"
+
+#include <mach/qdsp5/qdsp5audpreproccmdi.h>
+#include <mach/qdsp5/qdsp5audpreprocmsg.h>
+#include <mach/qdsp5/qdsp5audreccmdi.h>
+#include <mach/qdsp5/qdsp5audrecmsg.h>
+
+/* for queue ids - should be relative to module number*/
+#include "adsp.h"
+
+/* FRAME_NUM must be a power of two */
+#define FRAME_NUM              (8)
+#define FRAME_SIZE             (2052 * 2)
+#define MONO_DATA_SIZE         (2048)
+#define STEREO_DATA_SIZE       (MONO_DATA_SIZE * 2)
+#define DMASZ                  (FRAME_SIZE * FRAME_NUM)
+
+#define AGC_PARAM_SIZE         (20)
+#define NS_PARAM_SIZE          (6)
+#define IIR_PARAM_SIZE         (48)
+#define DEBUG                  (0)
+
+#define AGC_ENABLE   0x0001
+#define NS_ENABLE    0x0002
+#define IIR_ENABLE   0x0004
+
+struct tx_agc_config {
+       uint16_t agc_params[AGC_PARAM_SIZE];
+};
+
+struct ns_config {
+       uint16_t ns_params[NS_PARAM_SIZE];
+};
+
+struct tx_iir_filter {
+       uint16_t num_bands;
+       uint16_t iir_params[IIR_PARAM_SIZE];
+};
+
+struct audpre_cmd_iir_config_type {
+       uint16_t cmd_id;
+       uint16_t active_flag;
+       uint16_t num_bands;
+       uint16_t iir_params[IIR_PARAM_SIZE];
+};
+
+struct buffer {
+       void *data;
+       uint32_t size;
+       uint32_t read;
+       uint32_t addr;
+};
+
+struct audio_in {
+       struct buffer in[FRAME_NUM];
+
+       spinlock_t dsp_lock;
+
+       atomic_t in_bytes;
+
+       struct mutex lock;
+       struct mutex read_lock;
+       wait_queue_head_t wait;
+
+       struct msm_adsp_module *audpre;
+       struct msm_adsp_module *audrec;
+
+       /* configuration to use on next enable */
+       uint32_t samp_rate;
+       uint32_t channel_mode;
+       uint32_t buffer_size; /* 2048 for mono, 4096 for stereo */
+       uint32_t type; /* 0 for PCM ,1 for AAC */
+       uint32_t dsp_cnt;
+       uint32_t in_head; /* next buffer dsp will write */
+       uint32_t in_tail; /* next buffer read() will read */
+       uint32_t in_count; /* number of buffers available to read() */
+
+       unsigned short samp_rate_index;
+
+       struct audmgr audmgr;
+
+       /* data allocated for various buffers */
+       char *data;
+       dma_addr_t phys;
+
+       int opened;
+       int enabled;
+       int running;
+       int stopped; /* set when stopped, cleared on flush */
+
+       /* audpre settings */
+       int agc_enable;
+       struct tx_agc_config agc;
+
+       int ns_enable;
+       struct ns_config ns;
+
+       int iir_enable;
+       struct tx_iir_filter iir;
+};
+
+static int audio_in_dsp_enable(struct audio_in *audio, int enable);
+static int audio_in_encoder_config(struct audio_in *audio);
+static int audio_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt);
+static void audio_flush(struct audio_in *audio);
+static int audio_dsp_set_agc(struct audio_in *audio);
+static int audio_dsp_set_ns(struct audio_in *audio);
+static int audio_dsp_set_tx_iir(struct audio_in *audio);
+
+static unsigned convert_dsp_samp_index(unsigned index)
+{
+       switch (index) {
+       case 48000:     return AUDREC_CMD_SAMP_RATE_INDX_48000;
+       case 44100:     return AUDREC_CMD_SAMP_RATE_INDX_44100;
+       case 32000:     return AUDREC_CMD_SAMP_RATE_INDX_32000;
+       case 24000:     return AUDREC_CMD_SAMP_RATE_INDX_24000;
+       case 22050:     return AUDREC_CMD_SAMP_RATE_INDX_22050;
+       case 16000:     return AUDREC_CMD_SAMP_RATE_INDX_16000;
+       case 12000:     return AUDREC_CMD_SAMP_RATE_INDX_12000;
+       case 11025:     return AUDREC_CMD_SAMP_RATE_INDX_11025;
+       case 8000:      return AUDREC_CMD_SAMP_RATE_INDX_8000;
+       default:        return AUDREC_CMD_SAMP_RATE_INDX_11025;
+       }
+}
+
+static unsigned convert_samp_rate(unsigned hz)
+{
+       switch (hz) {
+       case 48000: return RPC_AUD_DEF_SAMPLE_RATE_48000;
+       case 44100: return RPC_AUD_DEF_SAMPLE_RATE_44100;
+       case 32000: return RPC_AUD_DEF_SAMPLE_RATE_32000;
+       case 24000: return RPC_AUD_DEF_SAMPLE_RATE_24000;
+       case 22050: return RPC_AUD_DEF_SAMPLE_RATE_22050;
+       case 16000: return RPC_AUD_DEF_SAMPLE_RATE_16000;
+       case 12000: return RPC_AUD_DEF_SAMPLE_RATE_12000;
+       case 11025: return RPC_AUD_DEF_SAMPLE_RATE_11025;
+       case 8000:  return RPC_AUD_DEF_SAMPLE_RATE_8000;
+       default:    return RPC_AUD_DEF_SAMPLE_RATE_11025;
+       }
+}
+
+static unsigned convert_samp_index(unsigned index)
+{
+       switch (index) {
+       case RPC_AUD_DEF_SAMPLE_RATE_48000:     return 48000;
+       case RPC_AUD_DEF_SAMPLE_RATE_44100:     return 44100;
+       case RPC_AUD_DEF_SAMPLE_RATE_32000:     return 32000;
+       case RPC_AUD_DEF_SAMPLE_RATE_24000:     return 24000;
+       case RPC_AUD_DEF_SAMPLE_RATE_22050:     return 22050;
+       case RPC_AUD_DEF_SAMPLE_RATE_16000:     return 16000;
+       case RPC_AUD_DEF_SAMPLE_RATE_12000:     return 12000;
+       case RPC_AUD_DEF_SAMPLE_RATE_11025:     return 11025;
+       case RPC_AUD_DEF_SAMPLE_RATE_8000:      return 8000;
+       default:                                return 11025;
+       }
+}
+
+/* must be called with audio->lock held */
+static int audio_in_enable(struct audio_in *audio)
+{
+       struct audmgr_config cfg;
+       int rc;
+
+       if (audio->enabled)
+               return 0;
+
+       cfg.tx_rate = audio->samp_rate;
+       cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE;
+       cfg.def_method = RPC_AUD_DEF_METHOD_RECORD;
+       if (audio->type == AUDREC_CMD_TYPE_0_INDEX_WAV)
+               cfg.codec = RPC_AUD_DEF_CODEC_PCM;
+       else
+               cfg.codec = RPC_AUD_DEF_CODEC_AAC;
+       cfg.snd_method = RPC_SND_METHOD_MIDI;
+
+       rc = audmgr_enable(&audio->audmgr, &cfg);
+       if (rc < 0)
+               return rc;
+
+       if (msm_adsp_enable(audio->audpre)) {
+               pr_err("audrec: msm_adsp_enable(audpre) failed\n");
+               return -ENODEV;
+       }
+       if (msm_adsp_enable(audio->audrec)) {
+               pr_err("audrec: msm_adsp_enable(audrec) failed\n");
+               return -ENODEV;
+       }
+
+       audio->enabled = 1;
+       audio_in_dsp_enable(audio, 1);
+
+       return 0;
+}
+
+/* must be called with audio->lock held */
+static int audio_in_disable(struct audio_in *audio)
+{
+       if (audio->enabled) {
+               audio->enabled = 0;
+
+               audio_in_dsp_enable(audio, 0);
+
+               wake_up(&audio->wait);
+
+               msm_adsp_disable(audio->audrec);
+               msm_adsp_disable(audio->audpre);
+               audmgr_disable(&audio->audmgr);
+       }
+       return 0;
+}
+
+/* ------------------- dsp --------------------- */
+static void audpre_dsp_event(void *data, unsigned id, size_t len,
+                           void (*getevent)(void *ptr, size_t len))
+{
+       uint16_t msg[2];
+       getevent(msg, sizeof(msg));
+
+       switch (id) {
+       case AUDPREPROC_MSG_CMD_CFG_DONE_MSG:
+               pr_info("audpre: type %d, status_flag %d\n", msg[0], msg[1]);
+               break;
+       case AUDPREPROC_MSG_ERROR_MSG_ID:
+               pr_info("audpre: err_index %d\n", msg[0]);
+               break;
+       default:
+               pr_err("audpre: unknown event %d\n", id);
+       }
+}
+
+struct audio_frame {
+       uint16_t count_low;
+       uint16_t count_high;
+       uint16_t bytes;
+       uint16_t unknown;
+       unsigned char samples[];
+} __attribute__((packed));
+
+static void audio_in_get_dsp_frames(struct audio_in *audio)
+{
+       struct audio_frame *frame;
+       uint32_t index;
+       unsigned long flags;
+
+       index = audio->in_head;
+
+       /* XXX check for bogus frame size? */
+
+       frame = (void *) (((char *)audio->in[index].data) - sizeof(*frame));
+
+       spin_lock_irqsave(&audio->dsp_lock, flags);
+       audio->in[index].size = frame->bytes;
+
+       audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1);
+
+       /* If overflow, move the tail index foward. */
+       if (audio->in_head == audio->in_tail)
+               audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1);
+       else
+               audio->in_count++;
+
+       audio_dsp_read_buffer(audio, audio->dsp_cnt++);
+       spin_unlock_irqrestore(&audio->dsp_lock, flags);
+
+       wake_up(&audio->wait);
+}
+
+static void audrec_dsp_event(void *data, unsigned id, size_t len,
+                           void (*getevent)(void *ptr, size_t len))
+{
+       struct audio_in *audio = data;
+       uint16_t msg[3];
+       getevent(msg, sizeof(msg));
+
+       switch (id) {
+       case AUDREC_MSG_CMD_CFG_DONE_MSG:
+               if (msg[0] & AUDREC_MSG_CFG_DONE_TYPE_0_UPDATE) {
+                       if (msg[0] & AUDREC_MSG_CFG_DONE_TYPE_0_ENA) {
+                               pr_info("audpre: CFG ENABLED\n");
+                               audio_dsp_set_agc(audio);
+                               audio_dsp_set_ns(audio);
+                               audio_dsp_set_tx_iir(audio);
+                               audio_in_encoder_config(audio);
+                       } else {
+                               pr_info("audrec: CFG SLEEP\n");
+                               audio->running = 0;
+                       }
+               } else {
+                       pr_info("audrec: CMD_CFG_DONE %x\n", msg[0]);
+               }
+               break;
+       case AUDREC_MSG_CMD_AREC_PARAM_CFG_DONE_MSG: {
+               pr_info("audrec: PARAM CFG DONE\n");
+               audio->running = 1;
+               break;
+       }
+       case AUDREC_MSG_FATAL_ERR_MSG:
+               pr_err("audrec: ERROR %x\n", msg[0]);
+               break;
+       case AUDREC_MSG_PACKET_READY_MSG:
+/* REC_DBG("type %x, count %d", msg[0], (msg[1] | (msg[2] << 16))); */
+               audio_in_get_dsp_frames(audio);
+               break;
+       default:
+               pr_err("audrec: unknown event %d\n", id);
+       }
+}
+
+struct msm_adsp_ops audpre_adsp_ops = {
+       .event = audpre_dsp_event,
+};
+
+struct msm_adsp_ops audrec_adsp_ops = {
+       .event = audrec_dsp_event,
+};
+
+
+#define audio_send_queue_pre(audio, cmd, len) \
+       msm_adsp_write(audio->audpre, QDSP_uPAudPreProcCmdQueue, cmd, len)
+#define audio_send_queue_recbs(audio, cmd, len) \
+       msm_adsp_write(audio->audrec, QDSP_uPAudRecBitStreamQueue, cmd, len)
+#define audio_send_queue_rec(audio, cmd, len) \
+       msm_adsp_write(audio->audrec, \
+       QDSP_uPAudRecCmdQueue, cmd, len)
+
+static int audio_dsp_set_agc(struct audio_in *audio)
+{
+       audpreproc_cmd_cfg_agc_params cmd;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.cmd_id = AUDPREPROC_CMD_CFG_AGC_PARAMS;
+
+       if (audio->agc_enable) {
+               /* cmd.tx_agc_param_mask = 0xFE00 from sample code */
+               cmd.tx_agc_param_mask =
+               (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_SLOPE) |
+               (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_TH) |
+               (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_EXP_SLOPE) |
+               (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_EXP_TH) |
+               (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_AIG_FLAG) |
+               (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_STATIC_GAIN) |
+               (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_TX_AGC_ENA_FLAG);
+               cmd.tx_agc_enable_flag =
+                       AUDPREPROC_CMD_TX_AGC_ENA_FLAG_ENA;
+               memcpy(&cmd.static_gain, &audio->agc.agc_params[0],
+                       sizeof(uint16_t) * 6);
+               /* cmd.param_mask = 0xFFF0 from sample code */
+               cmd.param_mask =
+                       (1 << AUDPREPROC_CMD_PARAM_MASK_RMS_TAY) |
+                       (1 << AUDPREPROC_CMD_PARAM_MASK_RELEASEK) |
+                       (1 << AUDPREPROC_CMD_PARAM_MASK_DELAY) |
+                       (1 << AUDPREPROC_CMD_PARAM_MASK_ATTACKK) |
+                       (1 << AUDPREPROC_CMD_PARAM_MASK_LEAKRATE_SLOW) |
+                       (1 << AUDPREPROC_CMD_PARAM_MASK_LEAKRATE_FAST) |
+                       (1 << AUDPREPROC_CMD_PARAM_MASK_AIG_RELEASEK) |
+                       (1 << AUDPREPROC_CMD_PARAM_MASK_AIG_MIN) |
+                       (1 << AUDPREPROC_CMD_PARAM_MASK_AIG_MAX) |
+                       (1 << AUDPREPROC_CMD_PARAM_MASK_LEAK_UP) |
+                       (1 << AUDPREPROC_CMD_PARAM_MASK_LEAK_DOWN) |
+                       (1 << AUDPREPROC_CMD_PARAM_MASK_AIG_ATTACKK);
+               memcpy(&cmd.aig_attackk, &audio->agc.agc_params[6],
+                       sizeof(uint16_t) * 14);
+
+       } else {
+               cmd.tx_agc_param_mask =
+                       (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_TX_AGC_ENA_FLAG);
+               cmd.tx_agc_enable_flag =
+                       AUDPREPROC_CMD_TX_AGC_ENA_FLAG_DIS;
+       }
+#if DEBUG
+       pr_info("cmd_id = 0x%04x\n", cmd.cmd_id);
+       pr_info("tx_agc_param_mask = 0x%04x\n", cmd.tx_agc_param_mask);
+       pr_info("tx_agc_enable_flag = 0x%04x\n", cmd.tx_agc_enable_flag);
+       pr_info("static_gain = 0x%04x\n", cmd.static_gain);
+       pr_info("adaptive_gain_flag = 0x%04x\n", cmd.adaptive_gain_flag);
+       pr_info("expander_th = 0x%04x\n", cmd.expander_th);
+       pr_info("expander_slope = 0x%04x\n", cmd.expander_slope);
+       pr_info("compressor_th = 0x%04x\n", cmd.compressor_th);
+       pr_info("compressor_slope = 0x%04x\n", cmd.compressor_slope);
+       pr_info("param_mask = 0x%04x\n", cmd.param_mask);
+       pr_info("aig_attackk = 0x%04x\n", cmd.aig_attackk);
+       pr_info("aig_leak_down = 0x%04x\n", cmd.aig_leak_down);
+       pr_info("aig_leak_up = 0x%04x\n", cmd.aig_leak_up);
+       pr_info("aig_max = 0x%04x\n", cmd.aig_max);
+       pr_info("aig_min = 0x%04x\n", cmd.aig_min);
+       pr_info("aig_releasek = 0x%04x\n", cmd.aig_releasek);
+       pr_info("aig_leakrate_fast = 0x%04x\n", cmd.aig_leakrate_fast);
+       pr_info("aig_leakrate_slow = 0x%04x\n", cmd.aig_leakrate_slow);
+       pr_info("attackk_msw = 0x%04x\n", cmd.attackk_msw);
+       pr_info("attackk_lsw = 0x%04x\n", cmd.attackk_lsw);
+       pr_info("delay = 0x%04x\n", cmd.delay);
+       pr_info("releasek_msw = 0x%04x\n", cmd.releasek_msw);
+       pr_info("releasek_lsw = 0x%04x\n", cmd.releasek_lsw);
+       pr_info("rms_tav = 0x%04x\n", cmd.rms_tav);
+#endif
+       return audio_send_queue_pre(audio, &cmd, sizeof(cmd));
+}
+
+static int audio_dsp_set_ns(struct audio_in *audio)
+{
+       audpreproc_cmd_cfg_ns_params cmd;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.cmd_id = AUDPREPROC_CMD_CFG_NS_PARAMS;
+
+       if (audio->ns_enable) {
+               /* cmd.ec_mode_new is fixed as 0x0064 when enable from sample code */
+               cmd.ec_mode_new =
+                       AUDPREPROC_CMD_EC_MODE_NEW_NS_ENA |
+                       AUDPREPROC_CMD_EC_MODE_NEW_HB_ENA |
+                       AUDPREPROC_CMD_EC_MODE_NEW_VA_ENA;
+               memcpy(&cmd.dens_gamma_n, &audio->ns.ns_params,
+                       sizeof(audio->ns.ns_params));
+       } else {
+               cmd.ec_mode_new =
+                       AUDPREPROC_CMD_EC_MODE_NEW_NLMS_DIS |
+                       AUDPREPROC_CMD_EC_MODE_NEW_DES_DIS |
+                       AUDPREPROC_CMD_EC_MODE_NEW_NS_DIS |
+                       AUDPREPROC_CMD_EC_MODE_NEW_CNI_DIS |
+                       AUDPREPROC_CMD_EC_MODE_NEW_NLES_DIS |
+                       AUDPREPROC_CMD_EC_MODE_NEW_HB_DIS |
+                       AUDPREPROC_CMD_EC_MODE_NEW_VA_DIS |
+                       AUDPREPROC_CMD_EC_MODE_NEW_PCD_DIS |
+                       AUDPREPROC_CMD_EC_MODE_NEW_FEHI_DIS |
+                       AUDPREPROC_CMD_EC_MODE_NEW_NEHI_DIS |
+                       AUDPREPROC_CMD_EC_MODE_NEW_NLPP_DIS |
+                       AUDPREPROC_CMD_EC_MODE_NEW_FNE_DIS |
+                       AUDPREPROC_CMD_EC_MODE_NEW_PRENLMS_DIS;
+       }
+#if DEBUG
+       pr_info("cmd_id = 0x%04x\n", cmd.cmd_id);
+       pr_info("ec_mode_new = 0x%04x\n", cmd.ec_mode_new);
+       pr_info("dens_gamma_n = 0x%04x\n", cmd.dens_gamma_n);
+       pr_info("dens_nfe_block_size = 0x%04x\n", cmd.dens_nfe_block_size);
+       pr_info("dens_limit_ns = 0x%04x\n", cmd.dens_limit_ns);
+       pr_info("dens_limit_ns_d = 0x%04x\n", cmd.dens_limit_ns_d);
+       pr_info("wb_gamma_e = 0x%04x\n", cmd.wb_gamma_e);
+       pr_info("wb_gamma_n = 0x%04x\n", cmd.wb_gamma_n);
+#endif
+       return audio_send_queue_pre(audio, &cmd, sizeof(cmd));
+}
+
+static int audio_dsp_set_tx_iir(struct audio_in *audio)
+{
+       struct audpre_cmd_iir_config_type cmd;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.cmd_id = AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS;
+
+       if (audio->iir_enable) {
+               cmd.active_flag = AUDPREPROC_CMD_IIR_ACTIVE_FLAG_ENA;
+               cmd.num_bands = audio->iir.num_bands;
+               memcpy(&cmd.iir_params, &audio->iir.iir_params,
+                       sizeof(audio->iir.iir_params));
+       } else {
+               cmd.active_flag = AUDPREPROC_CMD_IIR_ACTIVE_FLAG_DIS;
+       }
+#if DEBUG
+       pr_info("cmd_id = 0x%04x\n", cmd.cmd_id);
+       pr_info("active_flag = 0x%04x\n", cmd.active_flag);
+#endif
+       return audio_send_queue_pre(audio, &cmd, sizeof(cmd));
+}
+
+static int audio_in_dsp_enable(struct audio_in *audio, int enable)
+{
+       audrec_cmd_cfg cmd;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.cmd_id = AUDREC_CMD_CFG;
+       cmd.type_0 = enable ? AUDREC_CMD_TYPE_0_ENA : AUDREC_CMD_TYPE_0_DIS;
+       cmd.type_0 |= (AUDREC_CMD_TYPE_0_UPDATE | audio->type);
+       cmd.type_1 = 0;
+
+       return audio_send_queue_rec(audio, &cmd, sizeof(cmd));
+}
+
+static int audio_in_encoder_config(struct audio_in *audio)
+{
+       audrec_cmd_arec0param_cfg cmd;
+       uint16_t *data = (void *) audio->data;
+       unsigned n;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.cmd_id = AUDREC_CMD_AREC0PARAM_CFG;
+       cmd.ptr_to_extpkt_buffer_msw = audio->phys >> 16;
+       cmd.ptr_to_extpkt_buffer_lsw = audio->phys;
+       cmd.buf_len = FRAME_NUM; /* Both WAV and AAC use 8 frames */
+       cmd.samp_rate_index = audio->samp_rate_index;
+       cmd.stereo_mode = audio->channel_mode; /* 0 for mono, 1 for stereo */
+
+       /* FIXME have no idea why cmd.rec_quality is fixed
+        * as 0x1C00 from sample code
+        */
+       cmd.rec_quality = 0x1C00;
+
+       /* prepare buffer pointers:
+        * Mono: 1024 samples + 4 halfword header
+        * Stereo: 2048 samples + 4 halfword header
+        * AAC
+        * Mono/Stere: 768 + 4 halfword header
+        */
+       for (n = 0; n < FRAME_NUM; n++) {
+               audio->in[n].data = data + 4;
+               if (audio->type == AUDREC_CMD_TYPE_0_INDEX_WAV)
+                       data += (4 + (audio->channel_mode ? 2048 : 1024));
+               else if (audio->type == AUDREC_CMD_TYPE_0_INDEX_AAC)
+                       data += (4 + 768);
+       }
+
+       return audio_send_queue_rec(audio, &cmd, sizeof(cmd));
+}
+
+static int audio_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt)
+{
+       audrec_cmd_packet_ext_ptr cmd;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.cmd_id = AUDREC_CMD_PACKET_EXT_PTR;
+       /* Both WAV and AAC use AUDREC_CMD_TYPE_0 */
+       cmd.type = AUDREC_CMD_TYPE_0;
+       cmd.curr_rec_count_msw = read_cnt >> 16;
+       cmd.curr_rec_count_lsw = read_cnt;
+
+       return audio_send_queue_recbs(audio, &cmd, sizeof(cmd));
+}
+
+/* ------------------- device --------------------- */
+
+static void audio_enable_agc(struct audio_in *audio, int enable)
+{
+       if (audio->agc_enable != enable) {
+               audio->agc_enable = enable;
+               if (audio->running)
+                       audio_dsp_set_agc(audio);
+       }
+}
+
+static void audio_enable_ns(struct audio_in *audio, int enable)
+{
+       if (audio->ns_enable != enable) {
+               audio->ns_enable = enable;
+               if (audio->running)
+                       audio_dsp_set_ns(audio);
+       }
+}
+
+static void audio_enable_tx_iir(struct audio_in *audio, int enable)
+{
+       if (audio->iir_enable != enable) {
+               audio->iir_enable = enable;
+               if (audio->running)
+                       audio_dsp_set_tx_iir(audio);
+       }
+}
+
+static void audio_flush(struct audio_in *audio)
+{
+       int i;
+
+       audio->dsp_cnt = 0;
+       audio->in_head = 0;
+       audio->in_tail = 0;
+       audio->in_count = 0;
+       for (i = 0; i < FRAME_NUM; i++) {
+               audio->in[i].size = 0;
+               audio->in[i].read = 0;
+       }
+}
+
+static long audio_in_ioctl(struct file *file,
+                               unsigned int cmd, unsigned long arg)
+{
+       struct audio_in *audio = file->private_data;
+       int rc;
+
+       if (cmd == AUDIO_GET_STATS) {
+               struct msm_audio_stats stats;
+               stats.byte_count = atomic_read(&audio->in_bytes);
+               if (copy_to_user((void *) arg, &stats, sizeof(stats)))
+                       return -EFAULT;
+               return 0;
+       }
+
+       mutex_lock(&audio->lock);
+       switch (cmd) {
+       case AUDIO_START:
+               rc = audio_in_enable(audio);
+               break;
+       case AUDIO_STOP:
+               rc = audio_in_disable(audio);
+               audio->stopped = 1;
+               break;
+       case AUDIO_FLUSH:
+               if (audio->stopped) {
+                       /* Make sure we're stopped and we wake any threads
+                        * that might be blocked holding the read_lock.
+                        * While audio->stopped read threads will always
+                        * exit immediately.
+                        */
+                       wake_up(&audio->wait);
+                       mutex_lock(&audio->read_lock);
+                       audio_flush(audio);
+                       mutex_unlock(&audio->read_lock);
+               }
+       case AUDIO_SET_CONFIG: {
+               struct msm_audio_config cfg;
+               if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) {
+                       rc = -EFAULT;
+                       break;
+               }
+               if (cfg.channel_count == 1) {
+                       cfg.channel_count = AUDREC_CMD_STEREO_MODE_MONO;
+               } else if (cfg.channel_count == 2) {
+                       cfg.channel_count = AUDREC_CMD_STEREO_MODE_STEREO;
+               } else {
+                       rc = -EINVAL;
+                       break;
+               }
+
+               if (cfg.type == 0) {
+                       cfg.type = AUDREC_CMD_TYPE_0_INDEX_WAV;
+               } else if (cfg.type == 1) {
+                       cfg.type = AUDREC_CMD_TYPE_0_INDEX_AAC;
+               } else {
+                       rc = -EINVAL;
+                       break;
+               }
+               audio->samp_rate = convert_samp_rate(cfg.sample_rate);
+               audio->samp_rate_index =
+                 convert_dsp_samp_index(cfg.sample_rate);
+               audio->channel_mode = cfg.channel_count;
+               audio->buffer_size =
+                               audio->channel_mode ? STEREO_DATA_SIZE
+                                                       : MONO_DATA_SIZE;
+               audio->type = cfg.type;
+               rc = 0;
+               break;
+       }
+       case AUDIO_GET_CONFIG: {
+               struct msm_audio_config cfg;
+               cfg.buffer_size = audio->buffer_size;
+               cfg.buffer_count = FRAME_NUM;
+               cfg.sample_rate = convert_samp_index(audio->samp_rate);
+               if (audio->channel_mode == AUDREC_CMD_STEREO_MODE_MONO)
+                       cfg.channel_count = 1;
+               else
+                       cfg.channel_count = 2;
+               if (audio->type == AUDREC_CMD_TYPE_0_INDEX_WAV)
+                       cfg.type = 0;
+               else
+                       cfg.type = 1;
+               cfg.unused[0] = 0;
+               cfg.unused[1] = 0;
+               cfg.unused[2] = 0;
+               if (copy_to_user((void *) arg, &cfg, sizeof(cfg)))
+                       rc = -EFAULT;
+               else
+                       rc = 0;
+               break;
+       }
+       default:
+               rc = -EINVAL;
+       }
+       mutex_unlock(&audio->lock);
+       return rc;
+}
+
+static ssize_t audio_in_read(struct file *file,
+                               char __user *buf,
+                               size_t count, loff_t *pos)
+{
+       struct audio_in *audio = file->private_data;
+       unsigned long flags;
+       const char __user *start = buf;
+       void *data;
+       uint32_t index;
+       uint32_t size;
+       int rc = 0;
+
+       mutex_lock(&audio->read_lock);
+       while (count > 0) {
+               rc = wait_event_interruptible(
+                       audio->wait, (audio->in_count > 0) || audio->stopped);
+               if (rc < 0)
+                       break;
+
+               if (audio->stopped) {
+                       rc = -EBUSY;
+                       break;
+               }
+
+               index = audio->in_tail;
+               data = (uint8_t *) audio->in[index].data;
+               size = audio->in[index].size;
+               if (count >= size) {
+                       if (copy_to_user(buf, data, size)) {
+                               rc = -EFAULT;
+                               break;
+                       }
+                       spin_lock_irqsave(&audio->dsp_lock, flags);
+                       if (index != audio->in_tail) {
+                       /* overrun -- data is invalid and we need to retry */
+                               spin_unlock_irqrestore(&audio->dsp_lock, flags);
+                               continue;
+                       }
+                       audio->in[index].size = 0;
+                       audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1);
+                       audio->in_count--;
+                       spin_unlock_irqrestore(&audio->dsp_lock, flags);
+                       count -= size;
+                       buf += size;
+                       if (audio->type == AUDREC_CMD_TYPE_0_INDEX_AAC)
+                               break;
+               } else {
+                       pr_err("audio_in: short read\n");
+                       break;
+               }
+               if (audio->type == AUDREC_CMD_TYPE_0_INDEX_AAC)
+                       break; /* AAC only read one frame */
+       }
+       mutex_unlock(&audio->read_lock);
+
+       if (buf > start)
+               return buf - start;
+
+       return rc;
+}
+
+static ssize_t audio_in_write(struct file *file,
+                               const char __user *buf,
+                               size_t count, loff_t *pos)
+{
+       return -EINVAL;
+}
+
+static int audio_in_release(struct inode *inode, struct file *file)
+{
+       struct audio_in *audio = file->private_data;
+
+       mutex_lock(&audio->lock);
+       audio_in_disable(audio);
+       audio_flush(audio);
+       msm_adsp_put(audio->audrec);
+       msm_adsp_put(audio->audpre);
+       audio->audrec = NULL;
+       audio->audpre = NULL;
+       audio->opened = 0;
+       mutex_unlock(&audio->lock);
+       return 0;
+}
+
+struct audio_in the_audio_in;
+
+static int audio_in_open(struct inode *inode, struct file *file)
+{
+       struct audio_in *audio = &the_audio_in;
+       int rc;
+
+       mutex_lock(&audio->lock);
+       if (audio->opened) {
+               rc = -EBUSY;
+               goto done;
+       }
+
+       /* Settings will be re-config at AUDIO_SET_CONFIG,
+        * but at least we need to have initial config
+        */
+       audio->samp_rate = RPC_AUD_DEF_SAMPLE_RATE_11025;
+       audio->samp_rate_index = AUDREC_CMD_SAMP_RATE_INDX_11025;
+       audio->channel_mode = AUDREC_CMD_STEREO_MODE_MONO;
+       audio->buffer_size = MONO_DATA_SIZE;
+       audio->type = AUDREC_CMD_TYPE_0_INDEX_WAV;
+
+       rc = audmgr_open(&audio->audmgr);
+       if (rc)
+               goto done;
+       rc = msm_adsp_get("AUDPREPROCTASK", &audio->audpre,
+                               &audpre_adsp_ops, audio);
+       if (rc)
+               goto done;
+       rc = msm_adsp_get("AUDRECTASK", &audio->audrec,
+                          &audrec_adsp_ops, audio);
+       if (rc)
+               goto done;
+
+       audio->dsp_cnt = 0;
+       audio->stopped = 0;
+
+       audio_flush(audio);
+
+       file->private_data = audio;
+       audio->opened = 1;
+       rc = 0;
+done:
+       mutex_unlock(&audio->lock);
+       return rc;
+}
+
+static long audpre_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+       struct audio_in *audio = file->private_data;
+       int rc = 0, enable;
+       uint16_t enable_mask;
+#if DEBUG
+       int i;
+#endif
+
+       mutex_lock(&audio->lock);
+       switch (cmd) {
+       case AUDIO_ENABLE_AUDPRE: {
+               if (copy_from_user(&enable_mask, (void *) arg,
+                               sizeof(enable_mask)))
+                       goto out_fault;
+
+               enable = (enable_mask & AGC_ENABLE) ? 1 : 0;
+               audio_enable_agc(audio, enable);
+               enable = (enable_mask & NS_ENABLE) ? 1 : 0;
+               audio_enable_ns(audio, enable);
+               enable = (enable_mask & IIR_ENABLE) ? 1 : 0;
+               audio_enable_tx_iir(audio, enable);
+               break;
+       }
+       case AUDIO_SET_AGC: {
+               if (copy_from_user(&audio->agc, (void *) arg,
+                               sizeof(audio->agc)))
+                       goto out_fault;
+#if DEBUG
+               pr_info("set agc\n");
+               for (i = 0; i < AGC_PARAM_SIZE; i++) \
+                       pr_info("agc_params[%d] = 0x%04x\n", i,
+                               audio->agc.agc_params[i]);
+#endif
+               break;
+       }
+       case AUDIO_SET_NS: {
+               if (copy_from_user(&audio->ns, (void *) arg,
+                               sizeof(audio->ns)))
+                       goto out_fault;
+#if DEBUG
+               pr_info("set ns\n");
+               for (i = 0; i < NS_PARAM_SIZE; i++) \
+                       pr_info("ns_params[%d] = 0x%04x\n",
+                               i, audio->ns.ns_params[i]);
+#endif
+               break;
+       }
+       case AUDIO_SET_TX_IIR: {
+               if (copy_from_user(&audio->iir, (void *) arg,
+                               sizeof(audio->iir)))
+                       goto out_fault;
+#if DEBUG
+               pr_info("set iir\n");
+               pr_info("iir.num_bands = 0x%04x\n", audio->iir.num_bands);
+               for (i = 0; i < IIR_PARAM_SIZE; i++) \
+                       pr_info("iir_params[%d] = 0x%04x\n",
+                               i, audio->iir.iir_params[i]);
+#endif
+               break;
+       }
+       default:
+               rc = -EINVAL;
+       }
+
+       goto out;
+
+out_fault:
+       rc = -EFAULT;
+out:
+       mutex_unlock(&audio->lock);
+       return rc;
+}
+
+static int audpre_open(struct inode *inode, struct file *file)
+{
+       struct audio_in *audio = &the_audio_in;
+       file->private_data = audio;
+       return 0;
+}
+
+static struct file_operations audio_fops = {
+       .owner          = THIS_MODULE,
+       .open           = audio_in_open,
+       .release        = audio_in_release,
+       .read           = audio_in_read,
+       .write          = audio_in_write,
+       .unlocked_ioctl = audio_in_ioctl,
+};
+
+static struct file_operations audpre_fops = {
+       .owner          = THIS_MODULE,
+       .open           = audpre_open,
+       .unlocked_ioctl = audpre_ioctl,
+};
+
+struct miscdevice audio_in_misc = {
+       .minor  = MISC_DYNAMIC_MINOR,
+       .name   = "msm_pcm_in",
+       .fops   = &audio_fops,
+};
+
+struct miscdevice audpre_misc = {
+       .minor  = MISC_DYNAMIC_MINOR,
+       .name   = "msm_audpre",
+       .fops   = &audpre_fops,
+};
+
+static int __init audio_in_init(void)
+{
+       int rc;
+       the_audio_in.data = dma_alloc_coherent(NULL, DMASZ,
+                                              &the_audio_in.phys, GFP_KERNEL);
+       if (!the_audio_in.data) {
+               printk(KERN_ERR "%s: Unable to allocate DMA buffer\n",
+                      __func__);
+               return -ENOMEM;
+       }
+
+       mutex_init(&the_audio_in.lock);
+       mutex_init(&the_audio_in.read_lock);
+       spin_lock_init(&the_audio_in.dsp_lock);
+       init_waitqueue_head(&the_audio_in.wait);
+       rc = misc_register(&audio_in_misc);
+       if (!rc) {
+               rc = misc_register(&audpre_misc);
+               if (rc < 0)
+                       misc_deregister(&audio_in_misc);
+       }
+       return rc;
+}
+
+device_initcall(audio_in_init);
diff --git a/drivers/staging/dream/qdsp5/audio_mp3.c b/drivers/staging/dream/qdsp5/audio_mp3.c
new file mode 100644 (file)
index 0000000..72b8d70
--- /dev/null
@@ -0,0 +1,971 @@
+/* arch/arm/mach-msm/qdsp5/audio_mp3.c
+ *
+ * mp3 audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ *
+ * 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/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/delay.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+
+#include <linux/msm_audio.h>
+
+#include "audmgr.h"
+
+#include <mach/qdsp5/qdsp5audppcmdi.h>
+#include <mach/qdsp5/qdsp5audppmsg.h>
+#include <mach/qdsp5/qdsp5audplaycmdi.h>
+#include <mach/qdsp5/qdsp5audplaymsg.h>
+
+/* for queue ids - should be relative to module number*/
+#include "adsp.h"
+
+#ifdef DEBUG
+#define dprintk(format, arg...) \
+printk(KERN_DEBUG format, ## arg)
+#else
+#define dprintk(format, arg...) do {} while (0)
+#endif
+
+/* Size must be power of 2 */
+#define BUFSZ_MAX 32768
+#define BUFSZ_MIN 4096
+#define DMASZ_MAX (BUFSZ_MAX * 2)
+#define DMASZ_MIN (BUFSZ_MIN * 2)
+
+#define AUDPLAY_INVALID_READ_PTR_OFFSET        0xFFFF
+#define AUDDEC_DEC_MP3 2
+
+#define PCM_BUFSZ_MIN 4800     /* Hold one stereo MP3 frame */
+#define PCM_BUF_MAX_COUNT 5    /* DSP only accepts 5 buffers at most
+                                  but support 2 buffers currently */
+#define ROUTING_MODE_FTRT 1
+#define ROUTING_MODE_RT 2
+/* Decoder status received from AUDPPTASK */
+#define  AUDPP_DEC_STATUS_SLEEP        0
+#define         AUDPP_DEC_STATUS_INIT  1
+#define  AUDPP_DEC_STATUS_CFG   2
+#define  AUDPP_DEC_STATUS_PLAY  3
+
+struct buffer {
+       void *data;
+       unsigned size;
+       unsigned used;          /* Input usage actual DSP produced PCM size  */
+       unsigned addr;
+};
+
+struct audio {
+       struct buffer out[2];
+
+       spinlock_t dsp_lock;
+
+       uint8_t out_head;
+       uint8_t out_tail;
+       uint8_t out_needed; /* number of buffers the dsp is waiting for */
+       unsigned out_dma_sz;
+
+       atomic_t out_bytes;
+
+       struct mutex lock;
+       struct mutex write_lock;
+       wait_queue_head_t write_wait;
+
+       /* Host PCM section */
+       struct buffer in[PCM_BUF_MAX_COUNT];
+       struct mutex read_lock;
+       wait_queue_head_t read_wait;    /* Wait queue for read */
+       char *read_data;        /* pointer to reader buffer */
+       dma_addr_t read_phys;   /* physical address of reader buffer */
+       uint8_t read_next;      /* index to input buffers to be read next */
+       uint8_t fill_next;      /* index to buffer that DSP should be filling */
+       uint8_t pcm_buf_count;  /* number of pcm buffer allocated */
+       /* ---- End of Host PCM section */
+
+       struct msm_adsp_module *audplay;
+
+       /* configuration to use on next enable */
+       uint32_t out_sample_rate;
+       uint32_t out_channel_mode;
+
+       struct audmgr audmgr;
+
+       /* data allocated for various buffers */
+       char *data;
+       dma_addr_t phys;
+
+       int rflush; /* Read  flush */
+       int wflush; /* Write flush */
+       int opened;
+       int enabled;
+       int running;
+       int stopped; /* set when stopped, cleared on flush */
+       int pcm_feedback;
+       int buf_refresh;
+
+       int reserved; /* A byte is being reserved */
+       char rsv_byte; /* Handle odd length user data */
+
+       unsigned volume;
+
+       uint16_t dec_id;
+       uint32_t read_ptr_offset;
+};
+
+static int auddec_dsp_config(struct audio *audio, int enable);
+static void audpp_cmd_cfg_adec_params(struct audio *audio);
+static void audpp_cmd_cfg_routing_mode(struct audio *audio);
+static void audplay_send_data(struct audio *audio, unsigned needed);
+static void audplay_config_hostpcm(struct audio *audio);
+static void audplay_buffer_refresh(struct audio *audio);
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg);
+
+/* must be called with audio->lock held */
+static int audio_enable(struct audio *audio)
+{
+       struct audmgr_config cfg;
+       int rc;
+
+       pr_info("audio_enable()\n");
+
+       if (audio->enabled)
+               return 0;
+
+       audio->out_tail = 0;
+       audio->out_needed = 0;
+
+       cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE;
+       cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000;
+       cfg.def_method = RPC_AUD_DEF_METHOD_PLAYBACK;
+       cfg.codec = RPC_AUD_DEF_CODEC_MP3;
+       cfg.snd_method = RPC_SND_METHOD_MIDI;
+
+       rc = audmgr_enable(&audio->audmgr, &cfg);
+       if (rc < 0)
+               return rc;
+
+       if (msm_adsp_enable(audio->audplay)) {
+               pr_err("audio: msm_adsp_enable(audplay) failed\n");
+               audmgr_disable(&audio->audmgr);
+               return -ENODEV;
+       }
+
+       if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) {
+               pr_err("audio: audpp_enable() failed\n");
+               msm_adsp_disable(audio->audplay);
+               audmgr_disable(&audio->audmgr);
+               return -ENODEV;
+       }
+
+       audio->enabled = 1;
+       return 0;
+}
+
+/* must be called with audio->lock held */
+static int audio_disable(struct audio *audio)
+{
+       pr_info("audio_disable()\n");
+       if (audio->enabled) {
+               audio->enabled = 0;
+               auddec_dsp_config(audio, 0);
+               wake_up(&audio->write_wait);
+               wake_up(&audio->read_wait);
+               msm_adsp_disable(audio->audplay);
+               audpp_disable(audio->dec_id, audio);
+               audmgr_disable(&audio->audmgr);
+               audio->out_needed = 0;
+       }
+       return 0;
+}
+
+/* ------------------- dsp --------------------- */
+static void audio_update_pcm_buf_entry(struct audio *audio, uint32_t *payload)
+{
+       uint8_t index;
+       unsigned long flags;
+
+       if (audio->rflush) {
+               audio->buf_refresh = 1;
+               return;
+       }
+       spin_lock_irqsave(&audio->dsp_lock, flags);
+       for (index = 0; index < payload[1]; index++) {
+               if (audio->in[audio->fill_next].addr ==
+                   payload[2 + index * 2]) {
+                       pr_info("audio_update_pcm_buf_entry: in[%d] ready\n",
+                               audio->fill_next);
+                       audio->in[audio->fill_next].used =
+                         payload[3 + index * 2];
+                       if ((++audio->fill_next) == audio->pcm_buf_count)
+                               audio->fill_next = 0;
+
+               } else {
+                       pr_err
+                           ("audio_update_pcm_buf_entry: expected=%x ret=%x\n"
+                            , audio->in[audio->fill_next].addr,
+                            payload[1 + index * 2]);
+                       break;
+               }
+       }
+       if (audio->in[audio->fill_next].used == 0) {
+               audplay_buffer_refresh(audio);
+       } else {
+               pr_info("audio_update_pcm_buf_entry: read cannot keep up\n");
+               audio->buf_refresh = 1;
+       }
+       wake_up(&audio->read_wait);
+       spin_unlock_irqrestore(&audio->dsp_lock, flags);
+
+}
+
+static void audplay_dsp_event(void *data, unsigned id, size_t len,
+                             void (*getevent) (void *ptr, size_t len))
+{
+       struct audio *audio = data;
+       uint32_t msg[28];
+       getevent(msg, sizeof(msg));
+
+       dprintk("audplay_dsp_event: msg_id=%x\n", id);
+
+       switch (id) {
+       case AUDPLAY_MSG_DEC_NEEDS_DATA:
+               audplay_send_data(audio, 1);
+               break;
+
+       case AUDPLAY_MSG_BUFFER_UPDATE:
+               audio_update_pcm_buf_entry(audio, msg);
+               break;
+
+       default:
+               pr_err("unexpected message from decoder \n");
+               break;
+       }
+}
+
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg)
+{
+       struct audio *audio = private;
+
+       switch (id) {
+       case AUDPP_MSG_STATUS_MSG:{
+                       unsigned status = msg[1];
+
+                       switch (status) {
+                       case AUDPP_DEC_STATUS_SLEEP:
+                               pr_info("decoder status: sleep \n");
+                               break;
+
+                       case AUDPP_DEC_STATUS_INIT:
+                               pr_info("decoder status: init \n");
+                               audpp_cmd_cfg_routing_mode(audio);
+                               break;
+
+                       case AUDPP_DEC_STATUS_CFG:
+                               pr_info("decoder status: cfg \n");
+                               break;
+                       case AUDPP_DEC_STATUS_PLAY:
+                               pr_info("decoder status: play \n");
+                               if (audio->pcm_feedback) {
+                                       audplay_config_hostpcm(audio);
+                                       audplay_buffer_refresh(audio);
+                               }
+                               break;
+                       default:
+                               pr_err("unknown decoder status \n");
+                               break;
+                       }
+      break;
+               }
+       case AUDPP_MSG_CFG_MSG:
+               if (msg[0] == AUDPP_MSG_ENA_ENA) {
+                       pr_info("audio_dsp_event: CFG_MSG ENABLE\n");
+                       auddec_dsp_config(audio, 1);
+                       audio->out_needed = 0;
+                       audio->running = 1;
+                       audpp_set_volume_and_pan(audio->dec_id, audio->volume,
+                                                0);
+                       audpp_avsync(audio->dec_id, 22050);
+               } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+                       pr_info("audio_dsp_event: CFG_MSG DISABLE\n");
+                       audpp_avsync(audio->dec_id, 0);
+                       audio->running = 0;
+               } else {
+                       pr_err("audio_dsp_event: CFG_MSG %d?\n", msg[0]);
+               }
+               break;
+       case AUDPP_MSG_ROUTING_ACK:
+               pr_info("audio_dsp_event: ROUTING_ACK mode=%d\n", msg[1]);
+               audpp_cmd_cfg_adec_params(audio);
+               break;
+
+       case AUDPP_MSG_FLUSH_ACK:
+               dprintk("%s: FLUSH_ACK\n", __func__);
+               audio->wflush = 0;
+               audio->rflush = 0;
+               if (audio->pcm_feedback)
+                       audplay_buffer_refresh(audio);
+               break;
+
+       default:
+               pr_err("audio_dsp_event: UNKNOWN (%d)\n", id);
+       }
+
+}
+
+
+struct msm_adsp_ops audplay_adsp_ops = {
+       .event = audplay_dsp_event,
+};
+
+
+#define audplay_send_queue0(audio, cmd, len) \
+       msm_adsp_write(audio->audplay, QDSP_uPAudPlay0BitStreamCtrlQueue, \
+                      cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+       audpp_cmd_cfg_dec_type cmd;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+       if (enable)
+               cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+                              AUDPP_CMD_ENA_DEC_V |
+                              AUDDEC_DEC_MP3;
+       else
+               cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+                              AUDPP_CMD_DIS_DEC_V;
+
+       return audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_adec_params(struct audio *audio)
+{
+       audpp_cmd_cfg_adec_params_mp3 cmd;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
+       cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_MP3_LEN;
+       cmd.common.dec_id = audio->dec_id;
+       cmd.common.input_sampling_frequency = audio->out_sample_rate;
+
+       audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_routing_mode(struct audio *audio)
+{
+       struct audpp_cmd_routing_mode cmd;
+       pr_info("audpp_cmd_cfg_routing_mode()\n");
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.cmd_id = AUDPP_CMD_ROUTING_MODE;
+       cmd.object_number = audio->dec_id;
+       if (audio->pcm_feedback)
+               cmd.routing_mode = ROUTING_MODE_FTRT;
+       else
+               cmd.routing_mode = ROUTING_MODE_RT;
+
+       audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static int audplay_dsp_send_data_avail(struct audio *audio,
+                                       unsigned idx, unsigned len)
+{
+       audplay_cmd_bitstream_data_avail cmd;
+
+       cmd.cmd_id              = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL;
+       cmd.decoder_id          = audio->dec_id;
+       cmd.buf_ptr             = audio->out[idx].addr;
+       cmd.buf_size            = len/2;
+       cmd.partition_number    = 0;
+       return audplay_send_queue0(audio, &cmd, sizeof(cmd));
+}
+
+static void audplay_buffer_refresh(struct audio *audio)
+{
+       struct audplay_cmd_buffer_refresh refresh_cmd;
+
+       refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH;
+       refresh_cmd.num_buffers = 1;
+       refresh_cmd.buf0_address = audio->in[audio->fill_next].addr;
+       refresh_cmd.buf0_length = audio->in[audio->fill_next].size -
+         (audio->in[audio->fill_next].size % 576);     /* Mp3 frame size */
+       refresh_cmd.buf_read_count = 0;
+       pr_info("audplay_buffer_fresh: buf0_addr=%x buf0_len=%d\n",
+               refresh_cmd.buf0_address, refresh_cmd.buf0_length);
+       (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd));
+}
+
+static void audplay_config_hostpcm(struct audio *audio)
+{
+       struct audplay_cmd_hpcm_buf_cfg cfg_cmd;
+
+       pr_info("audplay_config_hostpcm()\n");
+       cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG;
+       cfg_cmd.max_buffers = 1;
+       cfg_cmd.byte_swap = 0;
+       cfg_cmd.hostpcm_config = (0x8000) | (0x4000);
+       cfg_cmd.feedback_frequency = 1;
+       cfg_cmd.partition_number = 0;
+       (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd));
+
+}
+
+static void audplay_send_data(struct audio *audio, unsigned needed)
+{
+       struct buffer *frame;
+       unsigned long flags;
+
+       spin_lock_irqsave(&audio->dsp_lock, flags);
+       if (!audio->running)
+               goto done;
+
+       if (audio->wflush) {
+               audio->out_needed = 1;
+               goto done;
+       }
+
+       if (needed && !audio->wflush) {
+               /* We were called from the callback because the DSP
+                * requested more data.  Note that the DSP does want
+                * more data, and if a buffer was in-flight, mark it
+                * as available (since the DSP must now be done with
+                * it).
+                */
+               audio->out_needed = 1;
+               frame = audio->out + audio->out_tail;
+               if (frame->used == 0xffffffff) {
+                 dprintk("frame %d free\n", audio->out_tail);
+                 frame->used = 0;
+                 audio->out_tail ^= 1;
+                 wake_up(&audio->write_wait);
+               }
+       }
+
+       if (audio->out_needed) {
+               /* If the DSP currently wants data and we have a
+                * buffer available, we will send it and reset
+                * the needed flag.  We'll mark the buffer as in-flight
+                * so that it won't be recycled until the next buffer
+                * is requested
+                */
+
+               frame = audio->out + audio->out_tail;
+               if (frame->used) {
+                 BUG_ON(frame->used == 0xffffffff);
+                 dprintk("frame %d busy\n", audio->out_tail);
+                 audplay_dsp_send_data_avail(audio, audio->out_tail,
+                                             frame->used);
+                 frame->used = 0xffffffff;
+                 audio->out_needed = 0;
+               }
+       }
+done:
+       spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+/* ------------------- device --------------------- */
+
+static void audio_flush(struct audio *audio)
+{
+       audio->out[0].used = 0;
+       audio->out[1].used = 0;
+       audio->out_head = 0;
+       audio->out_tail = 0;
+       audio->reserved = 0;
+       atomic_set(&audio->out_bytes, 0);
+}
+
+static void audio_flush_pcm_buf(struct audio *audio)
+{
+       uint8_t index;
+
+       for (index = 0; index < PCM_BUF_MAX_COUNT; index++)
+               audio->in[index].used = 0;
+
+       audio->read_next = 0;
+       audio->fill_next = 0;
+}
+
+static void audio_ioport_reset(struct audio *audio)
+{
+       /* Make sure read/write thread are free from
+        * sleep and knowing that system is not able
+        * to process io request at the moment
+        */
+       wake_up(&audio->write_wait);
+       mutex_lock(&audio->write_lock);
+       audio_flush(audio);
+       mutex_unlock(&audio->write_lock);
+       wake_up(&audio->read_wait);
+       mutex_lock(&audio->read_lock);
+       audio_flush_pcm_buf(audio);
+       mutex_unlock(&audio->read_lock);
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+       struct audio *audio = file->private_data;
+       int rc = 0;
+
+       pr_info("audio_ioctl() cmd = %d\n", cmd);
+
+       if (cmd == AUDIO_GET_STATS) {
+               struct msm_audio_stats stats;
+               stats.byte_count = audpp_avsync_byte_count(audio->dec_id);
+               stats.sample_count = audpp_avsync_sample_count(audio->dec_id);
+               if (copy_to_user((void *) arg, &stats, sizeof(stats)))
+                       return -EFAULT;
+               return 0;
+       }
+       if (cmd == AUDIO_SET_VOLUME) {
+               unsigned long flags;
+               spin_lock_irqsave(&audio->dsp_lock, flags);
+               audio->volume = arg;
+               if (audio->running)
+                       audpp_set_volume_and_pan(audio->dec_id, arg, 0);
+               spin_unlock_irqrestore(&audio->dsp_lock, flags);
+               return 0;
+       }
+       mutex_lock(&audio->lock);
+       switch (cmd) {
+       case AUDIO_START:
+               rc = audio_enable(audio);
+               break;
+       case AUDIO_STOP:
+               rc = audio_disable(audio);
+               audio->stopped = 1;
+               audio_ioport_reset(audio);
+               audio->stopped = 0;
+               break;
+       case AUDIO_FLUSH:
+               dprintk("%s: AUDIO_FLUSH\n", __func__);
+               audio->rflush = 1;
+               audio->wflush = 1;
+               audio_ioport_reset(audio);
+               audio->rflush = 0;
+               audio->wflush = 0;
+
+               if (audio->buf_refresh) {
+                       audio->buf_refresh = 0;
+                       audplay_buffer_refresh(audio);
+               }
+               break;
+
+       case AUDIO_SET_CONFIG: {
+               struct msm_audio_config config;
+               if (copy_from_user(&config, (void *) arg, sizeof(config))) {
+                       rc = -EFAULT;
+                       break;
+               }
+               if (config.channel_count == 1) {
+                       config.channel_count = AUDPP_CMD_PCM_INTF_MONO_V;
+               } else if (config.channel_count == 2) {
+                       config.channel_count = AUDPP_CMD_PCM_INTF_STEREO_V;
+               } else {
+                       rc = -EINVAL;
+                       break;
+               }
+               audio->out_sample_rate = config.sample_rate;
+               audio->out_channel_mode = config.channel_count;
+               rc = 0;
+               break;
+       }
+       case AUDIO_GET_CONFIG: {
+               struct msm_audio_config config;
+               config.buffer_size = (audio->out_dma_sz >> 1);
+               config.buffer_count = 2;
+               config.sample_rate = audio->out_sample_rate;
+               if (audio->out_channel_mode == AUDPP_CMD_PCM_INTF_MONO_V) {
+                       config.channel_count = 1;
+               } else {
+                       config.channel_count = 2;
+               }
+               config.unused[0] = 0;
+               config.unused[1] = 0;
+               config.unused[2] = 0;
+               config.unused[3] = 0;
+               if (copy_to_user((void *) arg, &config, sizeof(config))) {
+                       rc = -EFAULT;
+               } else {
+                       rc = 0;
+               }
+               break;
+       }
+       case AUDIO_GET_PCM_CONFIG:{
+               struct msm_audio_pcm_config config;
+               config.pcm_feedback = 0;
+               config.buffer_count = PCM_BUF_MAX_COUNT;
+               config.buffer_size = PCM_BUFSZ_MIN;
+               if (copy_to_user((void *)arg, &config,
+                        sizeof(config)))
+                       rc = -EFAULT;
+               else
+                       rc = 0;
+               break;
+       }
+       case AUDIO_SET_PCM_CONFIG:{
+                       struct msm_audio_pcm_config config;
+                       if (copy_from_user
+                           (&config, (void *)arg, sizeof(config))) {
+                               rc = -EFAULT;
+                               break;
+                       }
+                       if ((config.buffer_count > PCM_BUF_MAX_COUNT) ||
+                           (config.buffer_count == 1))
+                               config.buffer_count = PCM_BUF_MAX_COUNT;
+
+                       if (config.buffer_size < PCM_BUFSZ_MIN)
+                               config.buffer_size = PCM_BUFSZ_MIN;
+
+                       /* Check if pcm feedback is required */
+                       if ((config.pcm_feedback) && (!audio->read_data)) {
+                               pr_info("ioctl: allocate PCM buffer %d\n",
+                                       config.buffer_count *
+                                       config.buffer_size);
+                               audio->read_data =
+                                   dma_alloc_coherent(NULL,
+                                                      config.buffer_size *
+                                                      config.buffer_count,
+                                                      &audio->read_phys,
+                                                      GFP_KERNEL);
+                               if (!audio->read_data) {
+                                       pr_err("audio_mp3: malloc pcm \
+                                       buf failed\n");
+                                       rc = -1;
+                               } else {
+                                       uint8_t index;
+                                       uint32_t offset = 0;
+                                       audio->pcm_feedback = 1;
+                                       audio->buf_refresh = 0;
+                                       audio->pcm_buf_count =
+                                           config.buffer_count;
+                                       audio->read_next = 0;
+                                       audio->fill_next = 0;
+
+                                       for (index = 0;
+                                            index < config.buffer_count;
+                                            index++) {
+                                               audio->in[index].data =
+                                                   audio->read_data + offset;
+                                               audio->in[index].addr =
+                                                   audio->read_phys + offset;
+                                               audio->in[index].size =
+                                                   config.buffer_size;
+                                               audio->in[index].used = 0;
+                                               offset += config.buffer_size;
+                                       }
+                                       rc = 0;
+                               }
+                       } else {
+                               rc = 0;
+                       }
+                       break;
+               }
+       case AUDIO_PAUSE:
+               dprintk("%s: AUDIO_PAUSE %ld\n", __func__, arg);
+               rc = audpp_pause(audio->dec_id, (int) arg);
+               break;
+       default:
+               rc = -EINVAL;
+       }
+       mutex_unlock(&audio->lock);
+       return rc;
+}
+
+static ssize_t audio_read(struct file *file, char __user *buf, size_t count,
+                         loff_t *pos)
+{
+       struct audio *audio = file->private_data;
+       const char __user *start = buf;
+       int rc = 0;
+
+       if (!audio->pcm_feedback)
+               return 0; /* PCM feedback disabled. Nothing to read */
+
+       mutex_lock(&audio->read_lock);
+       pr_info("audio_read() %d \n", count);
+       while (count > 0) {
+               rc = wait_event_interruptible(audio->read_wait,
+                                             (audio->in[audio->read_next].
+                                              used > 0) || (audio->stopped)
+                                                  || (audio->rflush));
+
+               if (rc < 0)
+                       break;
+
+               if (audio->stopped || audio->rflush) {
+                       rc = -EBUSY;
+                       break;
+               }
+
+               if (count < audio->in[audio->read_next].used) {
+                       /* Read must happen in frame boundary. Since
+                        * driver does not know frame size, read count
+                        * must be greater or equal
+                        * to size of PCM samples
+                        */
+                       pr_info("audio_read: no partial frame done reading\n");
+                       break;
+               } else {
+                       pr_info("audio_read: read from in[%d]\n",
+                               audio->read_next);
+                       if (copy_to_user
+                           (buf, audio->in[audio->read_next].data,
+                            audio->in[audio->read_next].used)) {
+                               pr_err("audio_read: invalid addr %x \n",
+                                      (unsigned int)buf);
+                               rc = -EFAULT;
+                               break;
+                       }
+                       count -= audio->in[audio->read_next].used;
+                       buf += audio->in[audio->read_next].used;
+                       audio->in[audio->read_next].used = 0;
+                       if ((++audio->read_next) == audio->pcm_buf_count)
+                               audio->read_next = 0;
+                       if (audio->in[audio->read_next].used == 0)
+                               break; /* No data ready at this moment
+                                       * Exit while loop to prevent
+                                       * output thread sleep too long
+                                       */
+               }
+       }
+
+       /* don't feed output buffer to HW decoder during flushing
+        * buffer refresh command will be sent once flush completes
+        * send buf refresh command here can confuse HW decoder
+        */
+       if (audio->buf_refresh && !audio->rflush) {
+               audio->buf_refresh = 0;
+               pr_info("audio_read: kick start pcm feedback again\n");
+               audplay_buffer_refresh(audio);
+       }
+
+       mutex_unlock(&audio->read_lock);
+
+       if (buf > start)
+               rc = buf - start;
+
+       pr_info("audio_read: read %d bytes\n", rc);
+       return rc;
+}
+
+static ssize_t audio_write(struct file *file, const char __user *buf,
+                          size_t count, loff_t *pos)
+{
+       struct audio *audio = file->private_data;
+       const char __user *start = buf;
+       struct buffer *frame;
+       size_t xfer;
+       char *cpy_ptr;
+       int rc = 0;
+       unsigned dsize;
+
+       mutex_lock(&audio->write_lock);
+       while (count > 0) {
+               frame = audio->out + audio->out_head;
+               cpy_ptr = frame->data;
+               dsize = 0;
+               rc = wait_event_interruptible(audio->write_wait,
+                                             (frame->used == 0)
+                                             || (audio->stopped)
+                                                 || (audio->wflush));
+               if (rc < 0)
+                       break;
+               if (audio->stopped || audio->wflush) {
+                       rc = -EBUSY;
+                       break;
+               }
+
+               if (audio->reserved) {
+                       dprintk("%s: append reserved byte %x\n",
+                               __func__, audio->rsv_byte);
+                       *cpy_ptr = audio->rsv_byte;
+                       xfer = (count > (frame->size - 1)) ?
+                               frame->size - 1 : count;
+                       cpy_ptr++;
+                       dsize = 1;
+                       audio->reserved = 0;
+               } else
+                       xfer = (count > frame->size) ? frame->size : count;
+
+               if (copy_from_user(cpy_ptr, buf, xfer)) {
+                       rc = -EFAULT;
+                       break;
+               }
+
+               dsize += xfer;
+               if (dsize & 1) {
+                       audio->rsv_byte = ((char *) frame->data)[dsize - 1];
+                       dprintk("%s: odd length buf reserve last byte %x\n",
+                               __func__, audio->rsv_byte);
+                       audio->reserved = 1;
+                       dsize--;
+               }
+               count -= xfer;
+               buf += xfer;
+
+               if (dsize > 0) {
+                       audio->out_head ^= 1;
+                       frame->used = dsize;
+                       audplay_send_data(audio, 0);
+               }
+       }
+       mutex_unlock(&audio->write_lock);
+       if (buf > start)
+               return buf - start;
+       return rc;
+}
+
+static int audio_release(struct inode *inode, struct file *file)
+{
+       struct audio *audio = file->private_data;
+
+       dprintk("audio_release()\n");
+
+       mutex_lock(&audio->lock);
+       audio_disable(audio);
+       audio_flush(audio);
+       audio_flush_pcm_buf(audio);
+       msm_adsp_put(audio->audplay);
+       audio->audplay = NULL;
+       audio->opened = 0;
+       audio->reserved = 0;
+       dma_free_coherent(NULL, audio->out_dma_sz, audio->data, audio->phys);
+       audio->data = NULL;
+       if (audio->read_data != NULL) {
+               dma_free_coherent(NULL,
+                                 audio->in[0].size * audio->pcm_buf_count,
+                                 audio->read_data, audio->read_phys);
+               audio->read_data = NULL;
+       }
+       audio->pcm_feedback = 0;
+       mutex_unlock(&audio->lock);
+       return 0;
+}
+
+struct audio the_mp3_audio;
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+       struct audio *audio = &the_mp3_audio;
+       int rc;
+       unsigned pmem_sz;
+
+       mutex_lock(&audio->lock);
+
+       if (audio->opened) {
+               pr_err("audio: busy\n");
+               rc = -EBUSY;
+               goto done;
+       }
+
+       pmem_sz = DMASZ_MAX;
+
+       while (pmem_sz >= DMASZ_MIN) {
+               audio->data = dma_alloc_coherent(NULL, pmem_sz,
+                                                &audio->phys, GFP_KERNEL);
+               if (audio->data)
+                       break;
+               else if (pmem_sz == DMASZ_MIN) {
+                       pr_err("audio: could not allocate DMA buffers\n");
+                       rc = -ENOMEM;
+                       goto done;
+               } else
+                       pmem_sz >>= 1;
+       }
+
+       dprintk("%s: allocated %d bytes DMA buffer\n", __func__, pmem_sz);
+
+       rc = audmgr_open(&audio->audmgr);
+       if (rc) {
+               dma_free_coherent(NULL, pmem_sz,
+               audio->data, audio->phys);
+               goto done;
+       }
+
+       rc = msm_adsp_get("AUDPLAY0TASK", &audio->audplay, &audplay_adsp_ops,
+                         audio);
+       if (rc) {
+               pr_err("audio: failed to get audplay0 dsp module\n");
+               dma_free_coherent(NULL, pmem_sz,
+               audio->data, audio->phys);
+               audmgr_close(&audio->audmgr);
+               goto done;
+       }
+
+       audio->out_dma_sz = pmem_sz;
+       pmem_sz >>= 1; /* Shift by 1 to get size of ping pong buffer */
+
+       audio->out_sample_rate = 44100;
+       audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V;
+       audio->dec_id = 0;
+
+       audio->out[0].data = audio->data + 0;
+       audio->out[0].addr = audio->phys + 0;
+       audio->out[0].size = pmem_sz;
+
+       audio->out[1].data = audio->data + pmem_sz;
+       audio->out[1].addr = audio->phys + pmem_sz;
+       audio->out[1].size = pmem_sz;
+
+       audio->volume = 0x2000; /* equal to Q13 number 1.0 Unit Gain */
+
+       audio_flush(audio);
+
+       file->private_data = audio;
+       audio->opened = 1;
+       rc = 0;
+done:
+       mutex_unlock(&audio->lock);
+       return rc;
+}
+
+static struct file_operations audio_mp3_fops = {
+       .owner          = THIS_MODULE,
+       .open           = audio_open,
+       .release        = audio_release,
+       .read           = audio_read,
+       .write          = audio_write,
+       .unlocked_ioctl = audio_ioctl,
+};
+
+struct miscdevice audio_mp3_misc = {
+       .minor  = MISC_DYNAMIC_MINOR,
+       .name   = "msm_mp3",
+       .fops   = &audio_mp3_fops,
+};
+
+static int __init audio_init(void)
+{
+       mutex_init(&the_mp3_audio.lock);
+       mutex_init(&the_mp3_audio.write_lock);
+       mutex_init(&the_mp3_audio.read_lock);
+       spin_lock_init(&the_mp3_audio.dsp_lock);
+       init_waitqueue_head(&the_mp3_audio.write_wait);
+       init_waitqueue_head(&the_mp3_audio.read_wait);
+       the_mp3_audio.read_data = NULL;
+       return misc_register(&audio_mp3_misc);
+}
+
+device_initcall(audio_init);
diff --git a/drivers/staging/dream/qdsp5/audio_out.c b/drivers/staging/dream/qdsp5/audio_out.c
new file mode 100644 (file)
index 0000000..5a76ecc
--- /dev/null
@@ -0,0 +1,851 @@
+/* arch/arm/mach-msm/qdsp5/audio_out.c
+ *
+ * pcm audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ *
+ * 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/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/wakelock.h>
+
+#include <linux/msm_audio.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+
+#include "audmgr.h"
+
+#include <mach/qdsp5/qdsp5audppcmdi.h>
+#include <mach/qdsp5/qdsp5audppmsg.h>
+
+#include <mach/htc_pwrsink.h>
+
+#include "evlog.h"
+
+#define LOG_AUDIO_EVENTS 1
+#define LOG_AUDIO_FAULTS 0
+
+enum {
+       EV_NULL,
+       EV_OPEN,
+       EV_WRITE,
+       EV_RETURN,
+       EV_IOCTL,
+       EV_WRITE_WAIT,
+       EV_WAIT_EVENT,
+       EV_FILL_BUFFER,
+       EV_SEND_BUFFER,
+       EV_DSP_EVENT,
+       EV_ENABLE,
+};
+
+#if (LOG_AUDIO_EVENTS != 1)
+static inline void LOG(unsigned id, unsigned arg) {}
+#else
+static const char *pcm_log_strings[] = {
+       "NULL",
+       "OPEN",
+       "WRITE",
+       "RETURN",
+       "IOCTL",
+       "WRITE_WAIT",
+       "WAIT_EVENT",
+       "FILL_BUFFER",
+       "SEND_BUFFER",
+       "DSP_EVENT",
+       "ENABLE",
+};
+
+DECLARE_LOG(pcm_log, 64, pcm_log_strings);
+
+static int __init _pcm_log_init(void)
+{
+       return ev_log_init(&pcm_log);
+}
+module_init(_pcm_log_init);
+
+#define LOG(id,arg) ev_log_write(&pcm_log, id, arg)
+#endif
+
+
+
+
+
+#define BUFSZ (960 * 5)
+#define DMASZ (BUFSZ * 2)
+
+#define AUDPP_CMD_CFG_OBJ_UPDATE 0x8000
+#define AUDPP_CMD_EQ_FLAG_DIS  0x0000
+#define AUDPP_CMD_EQ_FLAG_ENA  -1
+#define AUDPP_CMD_IIR_FLAG_DIS   0x0000
+#define AUDPP_CMD_IIR_FLAG_ENA   -1
+
+#define AUDPP_CMD_IIR_TUNING_FILTER  1
+#define AUDPP_CMD_EQUALIZER    2
+#define AUDPP_CMD_ADRC 3
+
+#define ADRC_ENABLE  0x0001
+#define EQ_ENABLE    0x0002
+#define IIR_ENABLE   0x0004
+
+struct adrc_filter {
+       uint16_t compression_th;
+       uint16_t compression_slope;
+       uint16_t rms_time;
+       uint16_t attack_const_lsw;
+       uint16_t attack_const_msw;
+       uint16_t release_const_lsw;
+       uint16_t release_const_msw;
+       uint16_t adrc_system_delay;
+};
+
+struct eqalizer {
+       uint16_t num_bands;
+       uint16_t eq_params[132];
+};
+
+struct rx_iir_filter {
+       uint16_t num_bands;
+       uint16_t iir_params[48];
+};
+
+typedef struct {
+       audpp_cmd_cfg_object_params_common common;
+       uint16_t eq_flag;
+       uint16_t num_bands;
+       uint16_t eq_params[132];
+} audpp_cmd_cfg_object_params_eq;
+
+typedef struct {
+       audpp_cmd_cfg_object_params_common common;
+       uint16_t active_flag;
+       uint16_t num_bands;
+       uint16_t iir_params[48];
+} audpp_cmd_cfg_object_params_rx_iir;
+
+struct buffer {
+       void *data;
+       unsigned size;
+       unsigned used;
+       unsigned addr;
+};
+
+struct audio {
+       struct buffer out[2];
+
+       spinlock_t dsp_lock;
+
+       uint8_t out_head;
+       uint8_t out_tail;
+       uint8_t out_needed; /* number of buffers the dsp is waiting for */
+
+       atomic_t out_bytes;
+
+       struct mutex lock;
+       struct mutex write_lock;
+       wait_queue_head_t wait;
+
+       /* configuration to use on next enable */
+       uint32_t out_sample_rate;
+       uint32_t out_channel_mode;
+       uint32_t out_weight;
+       uint32_t out_buffer_size;
+
+       struct audmgr audmgr;
+
+       /* data allocated for various buffers */
+       char *data;
+       dma_addr_t phys;
+
+       int opened;
+       int enabled;
+       int running;
+       int stopped; /* set when stopped, cleared on flush */
+       unsigned volume;
+
+       struct wake_lock wakelock;
+       struct wake_lock idlelock;
+
+       int adrc_enable;
+       struct adrc_filter adrc;
+
+       int eq_enable;
+       struct eqalizer eq;
+
+       int rx_iir_enable;
+       struct rx_iir_filter iir;
+};
+
+static void audio_prevent_sleep(struct audio *audio)
+{
+       printk(KERN_INFO "++++++++++++++++++++++++++++++\n");
+       wake_lock(&audio->wakelock);
+       wake_lock(&audio->idlelock);
+}
+
+static void audio_allow_sleep(struct audio *audio)
+{
+       wake_unlock(&audio->wakelock);
+       wake_unlock(&audio->idlelock);
+       printk(KERN_INFO "------------------------------\n");
+}
+
+static int audio_dsp_out_enable(struct audio *audio, int yes);
+static int audio_dsp_send_buffer(struct audio *audio, unsigned id, unsigned len);
+static int audio_dsp_set_adrc(struct audio *audio);
+static int audio_dsp_set_eq(struct audio *audio);
+static int audio_dsp_set_rx_iir(struct audio *audio);
+
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg);
+
+/* must be called with audio->lock held */
+static int audio_enable(struct audio *audio)
+{
+       struct audmgr_config cfg;
+       int rc;
+
+       pr_info("audio_enable()\n");
+
+       if (audio->enabled)
+               return 0;
+
+       /* refuse to start if we're not ready */
+       if (!audio->out[0].used || !audio->out[1].used)
+               return -EIO;
+
+       /* we start buffers 0 and 1, so buffer 0 will be the
+        * next one the dsp will want
+        */
+       audio->out_tail = 0;
+       audio->out_needed = 0;
+
+       cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE;
+       cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000;
+       cfg.def_method = RPC_AUD_DEF_METHOD_HOST_PCM;
+       cfg.codec = RPC_AUD_DEF_CODEC_PCM;
+       cfg.snd_method = RPC_SND_METHOD_MIDI;
+
+       audio_prevent_sleep(audio);
+       rc = audmgr_enable(&audio->audmgr, &cfg);
+       if (rc < 0) {
+               audio_allow_sleep(audio);
+               return rc;
+       }
+
+       if (audpp_enable(-1, audio_dsp_event, audio)) {
+               pr_err("audio: audpp_enable() failed\n");
+               audmgr_disable(&audio->audmgr);
+               audio_allow_sleep(audio);
+               return -ENODEV;
+       }
+
+       audio->enabled = 1;
+       htc_pwrsink_set(PWRSINK_AUDIO, 100);
+       return 0;
+}
+
+/* must be called with audio->lock held */
+static int audio_disable(struct audio *audio)
+{
+       pr_info("audio_disable()\n");
+       if (audio->enabled) {
+               audio->enabled = 0;
+               audio_dsp_out_enable(audio, 0);
+
+               audpp_disable(-1, audio);
+
+               wake_up(&audio->wait);
+               audmgr_disable(&audio->audmgr);
+               audio->out_needed = 0;
+               audio_allow_sleep(audio);
+       }
+       return 0;
+}
+
+/* ------------------- dsp --------------------- */
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg)
+{
+       struct audio *audio = private;
+       struct buffer *frame;
+       unsigned long flags;
+
+       LOG(EV_DSP_EVENT, id);
+       switch (id) {
+       case AUDPP_MSG_HOST_PCM_INTF_MSG: {
+               unsigned id = msg[2];
+               unsigned idx = msg[3] - 1;
+
+               /* pr_info("audio_dsp_event: HOST_PCM id %d idx %d\n", id, idx); */
+               if (id != AUDPP_MSG_HOSTPCM_ID_ARM_RX) {
+                       pr_err("bogus id\n");
+                       break;
+               }
+               if (idx > 1) {
+                       pr_err("bogus buffer idx\n");
+                       break;
+               }
+
+               spin_lock_irqsave(&audio->dsp_lock, flags);
+               if (audio->running) {
+                       atomic_add(audio->out[idx].used, &audio->out_bytes);
+                       audio->out[idx].used = 0;
+
+                       frame = audio->out + audio->out_tail;
+                       if (frame->used) {
+                               audio_dsp_send_buffer(
+                                       audio, audio->out_tail, frame->used);
+                               audio->out_tail ^= 1;
+                       } else {
+                               audio->out_needed++;
+                       }
+                       wake_up(&audio->wait);
+               }
+               spin_unlock_irqrestore(&audio->dsp_lock, flags);
+               break;
+       }
+       case AUDPP_MSG_PCMDMAMISSED:
+               pr_info("audio_dsp_event: PCMDMAMISSED %d\n", msg[0]);
+               break;
+       case AUDPP_MSG_CFG_MSG:
+               if (msg[0] == AUDPP_MSG_ENA_ENA) {
+                       LOG(EV_ENABLE, 1);
+                       pr_info("audio_dsp_event: CFG_MSG ENABLE\n");
+                       audio->out_needed = 0;
+                       audio->running = 1;
+                       audpp_set_volume_and_pan(5, audio->volume, 0);
+                       audio_dsp_set_adrc(audio);
+                       audio_dsp_set_eq(audio);
+                       audio_dsp_set_rx_iir(audio);
+                       audio_dsp_out_enable(audio, 1);
+               } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+                       LOG(EV_ENABLE, 0);
+                       pr_info("audio_dsp_event: CFG_MSG DISABLE\n");
+                       audio->running = 0;
+               } else {
+                       pr_err("audio_dsp_event: CFG_MSG %d?\n", msg[0]);
+               }
+               break;
+       default:
+               pr_err("audio_dsp_event: UNKNOWN (%d)\n", id);
+       }
+}
+
+static int audio_dsp_out_enable(struct audio *audio, int yes)
+{
+       audpp_cmd_pcm_intf cmd;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.cmd_id      = AUDPP_CMD_PCM_INTF_2;
+       cmd.object_num  = AUDPP_CMD_PCM_INTF_OBJECT_NUM;
+       cmd.config      = AUDPP_CMD_PCM_INTF_CONFIG_CMD_V;
+       cmd.intf_type   = AUDPP_CMD_PCM_INTF_RX_ENA_ARMTODSP_V;
+
+       if (yes) {
+               cmd.write_buf1LSW       = audio->out[0].addr;
+               cmd.write_buf1MSW       = audio->out[0].addr >> 16;
+               cmd.write_buf1_len      = audio->out[0].size;
+               cmd.write_buf2LSW       = audio->out[1].addr;
+               cmd.write_buf2MSW       = audio->out[1].addr >> 16;
+               cmd.write_buf2_len      = audio->out[1].size;
+               cmd.arm_to_rx_flag      = AUDPP_CMD_PCM_INTF_ENA_V;
+               cmd.weight_decoder_to_rx = audio->out_weight;
+               cmd.weight_arm_to_rx    = 1;
+               cmd.partition_number_arm_to_dsp = 0;
+               cmd.sample_rate         = audio->out_sample_rate;
+               cmd.channel_mode        = audio->out_channel_mode;
+       }
+
+       return audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static int audio_dsp_send_buffer(struct audio *audio, unsigned idx, unsigned len)
+{
+       audpp_cmd_pcm_intf_send_buffer cmd;
+
+       cmd.cmd_id              = AUDPP_CMD_PCM_INTF_2;
+       cmd.host_pcm_object     = AUDPP_CMD_PCM_INTF_OBJECT_NUM;
+       cmd.config              = AUDPP_CMD_PCM_INTF_BUFFER_CMD_V;
+       cmd.intf_type           = AUDPP_CMD_PCM_INTF_RX_ENA_ARMTODSP_V;
+       cmd.dsp_to_arm_buf_id   = 0;
+       cmd.arm_to_dsp_buf_id   = idx + 1;
+       cmd.arm_to_dsp_buf_len  = len;
+
+       LOG(EV_SEND_BUFFER, idx);
+       return audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static int audio_dsp_set_adrc(struct audio *audio)
+{
+       audpp_cmd_cfg_object_params_adrc cmd;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.common.comman_cfg = AUDPP_CMD_CFG_OBJ_UPDATE;
+       cmd.common.command_type = AUDPP_CMD_ADRC;
+
+       if (audio->adrc_enable) {
+               cmd.adrc_flag = AUDPP_CMD_ADRC_FLAG_ENA;
+               cmd.compression_th = audio->adrc.compression_th;
+               cmd.compression_slope = audio->adrc.compression_slope;
+               cmd.rms_time = audio->adrc.rms_time;
+               cmd.attack_const_lsw = audio->adrc.attack_const_lsw;
+               cmd.attack_const_msw = audio->adrc.attack_const_msw;
+               cmd.release_const_lsw = audio->adrc.release_const_lsw;
+               cmd.release_const_msw = audio->adrc.release_const_msw;
+               cmd.adrc_system_delay = audio->adrc.adrc_system_delay;
+       } else {
+               cmd.adrc_flag = AUDPP_CMD_ADRC_FLAG_DIS;
+       }
+       return audpp_send_queue3(&cmd, sizeof(cmd));
+}
+
+static int audio_dsp_set_eq(struct audio *audio)
+{
+       audpp_cmd_cfg_object_params_eq cmd;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.common.comman_cfg = AUDPP_CMD_CFG_OBJ_UPDATE;
+       cmd.common.command_type = AUDPP_CMD_EQUALIZER;
+
+       if (audio->eq_enable) {
+               cmd.eq_flag = AUDPP_CMD_EQ_FLAG_ENA;
+               cmd.num_bands = audio->eq.num_bands;
+               memcpy(&cmd.eq_params, audio->eq.eq_params,
+                      sizeof(audio->eq.eq_params));
+       } else {
+               cmd.eq_flag = AUDPP_CMD_EQ_FLAG_DIS;
+       }
+       return audpp_send_queue3(&cmd, sizeof(cmd));
+}
+
+static int audio_dsp_set_rx_iir(struct audio *audio)
+{
+       audpp_cmd_cfg_object_params_rx_iir cmd;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.common.comman_cfg = AUDPP_CMD_CFG_OBJ_UPDATE;
+       cmd.common.command_type = AUDPP_CMD_IIR_TUNING_FILTER;
+
+       if (audio->rx_iir_enable) {
+               cmd.active_flag = AUDPP_CMD_IIR_FLAG_ENA;
+               cmd.num_bands = audio->iir.num_bands;
+               memcpy(&cmd.iir_params, audio->iir.iir_params,
+                      sizeof(audio->iir.iir_params));
+       } else {
+               cmd.active_flag = AUDPP_CMD_IIR_FLAG_DIS;
+       }
+
+       return audpp_send_queue3(&cmd, sizeof(cmd));
+}
+
+/* ------------------- device --------------------- */
+
+static int audio_enable_adrc(struct audio *audio, int enable)
+{
+       if (audio->adrc_enable != enable) {
+               audio->adrc_enable = enable;
+               if (audio->running)
+                       audio_dsp_set_adrc(audio);
+       }
+       return 0;
+}
+
+static int audio_enable_eq(struct audio *audio, int enable)
+{
+       if (audio->eq_enable != enable) {
+               audio->eq_enable = enable;
+               if (audio->running)
+                       audio_dsp_set_eq(audio);
+       }
+       return 0;
+}
+
+static int audio_enable_rx_iir(struct audio *audio, int enable)
+{
+       if (audio->rx_iir_enable != enable) {
+               audio->rx_iir_enable = enable;
+               if (audio->running)
+                       audio_dsp_set_rx_iir(audio);
+       }
+       return 0;
+}
+
+static void audio_flush(struct audio *audio)
+{
+       audio->out[0].used = 0;
+       audio->out[1].used = 0;
+       audio->out_head = 0;
+       audio->out_tail = 0;
+       audio->stopped = 0;
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+       struct audio *audio = file->private_data;
+       int rc;
+
+       if (cmd == AUDIO_GET_STATS) {
+               struct msm_audio_stats stats;
+               stats.byte_count = atomic_read(&audio->out_bytes);
+               if (copy_to_user((void*) arg, &stats, sizeof(stats)))
+                       return -EFAULT;
+               return 0;
+       }
+       if (cmd == AUDIO_SET_VOLUME) {
+               unsigned long flags;
+               spin_lock_irqsave(&audio->dsp_lock, flags);
+               audio->volume = arg;
+               if (audio->running)
+                       audpp_set_volume_and_pan(6, arg, 0);
+               spin_unlock_irqrestore(&audio->dsp_lock, flags);
+       }
+
+       LOG(EV_IOCTL, cmd);
+       mutex_lock(&audio->lock);
+       switch (cmd) {
+       case AUDIO_START:
+               rc = audio_enable(audio);
+               break;
+       case AUDIO_STOP:
+               rc = audio_disable(audio);
+               audio->stopped = 1;
+               break;
+       case AUDIO_FLUSH:
+               if (audio->stopped) {
+                       /* Make sure we're stopped and we wake any threads
+                        * that might be blocked holding the write_lock.
+                        * While audio->stopped write threads will always
+                        * exit immediately.
+                        */
+                       wake_up(&audio->wait);
+                       mutex_lock(&audio->write_lock);
+                       audio_flush(audio);
+                       mutex_unlock(&audio->write_lock);
+               }
+       case AUDIO_SET_CONFIG: {
+               struct msm_audio_config config;
+               if (copy_from_user(&config, (void*) arg, sizeof(config))) {
+                       rc = -EFAULT;
+                       break;
+               }
+               if (config.channel_count == 1) {
+                       config.channel_count = AUDPP_CMD_PCM_INTF_MONO_V;
+               } else if (config.channel_count == 2) {
+                       config.channel_count= AUDPP_CMD_PCM_INTF_STEREO_V;
+               } else {
+                       rc = -EINVAL;
+                       break;
+               }
+               audio->out_sample_rate = config.sample_rate;
+               audio->out_channel_mode = config.channel_count;
+               rc = 0;
+               break;
+       }
+       case AUDIO_GET_CONFIG: {
+               struct msm_audio_config config;
+               config.buffer_size = BUFSZ;
+               config.buffer_count = 2;
+               config.sample_rate = audio->out_sample_rate;
+               if (audio->out_channel_mode == AUDPP_CMD_PCM_INTF_MONO_V) {
+                       config.channel_count = 1;
+               } else {
+                       config.channel_count = 2;
+               }
+               config.unused[0] = 0;
+               config.unused[1] = 0;
+               config.unused[2] = 0;
+               config.unused[3] = 0;
+               if (copy_to_user((void*) arg, &config, sizeof(config))) {
+                       rc = -EFAULT;
+               } else {
+                       rc = 0;
+               }
+               break;
+       }
+       default:
+               rc = -EINVAL;
+       }
+       mutex_unlock(&audio->lock);
+       return rc;
+}
+
+static ssize_t audio_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
+{
+       return -EINVAL;
+}
+
+static inline int rt_policy(int policy)
+{
+       if (unlikely(policy == SCHED_FIFO) || unlikely(policy == SCHED_RR))
+               return 1;
+       return 0;
+}
+
+static inline int task_has_rt_policy(struct task_struct *p)
+{
+       return rt_policy(p->policy);
+}
+
+static ssize_t audio_write(struct file *file, const char __user *buf,
+                          size_t count, loff_t *pos)
+{
+       struct sched_param s = { .sched_priority = 1 };
+       struct audio *audio = file->private_data;
+       unsigned long flags;
+       const char __user *start = buf;
+       struct buffer *frame;
+       size_t xfer;
+       int old_prio = current->rt_priority;
+       int old_policy = current->policy;
+       int cap_nice = cap_raised(current_cap(), CAP_SYS_NICE);
+       int rc = 0;
+
+       LOG(EV_WRITE, count | (audio->running << 28) | (audio->stopped << 24));
+
+       /* just for this write, set us real-time */
+       if (!task_has_rt_policy(current)) {
+               struct cred *new = prepare_creds();
+               cap_raise(new->cap_effective, CAP_SYS_NICE);
+               commit_creds(new);
+               sched_setscheduler(current, SCHED_RR, &s);
+       }
+
+       mutex_lock(&audio->write_lock);
+       while (count > 0) {
+               frame = audio->out + audio->out_head;
+
+               LOG(EV_WAIT_EVENT, 0);
+               rc = wait_event_interruptible(audio->wait,
+                                             (frame->used == 0) || (audio->stopped));
+               LOG(EV_WAIT_EVENT, 1);
+
+               if (rc < 0)
+                       break;
+               if (audio->stopped) {
+                       rc = -EBUSY;
+                       break;
+               }
+               xfer = count > frame->size ? frame->size : count;
+               if (copy_from_user(frame->data, buf, xfer)) {
+                       rc = -EFAULT;
+                       break;
+               }
+               frame->used = xfer;
+               audio->out_head ^= 1;
+               count -= xfer;
+               buf += xfer;
+
+               spin_lock_irqsave(&audio->dsp_lock, flags);
+               LOG(EV_FILL_BUFFER, audio->out_head ^ 1);
+               frame = audio->out + audio->out_tail;
+               if (frame->used && audio->out_needed) {
+                       audio_dsp_send_buffer(audio, audio->out_tail, frame->used);
+                       audio->out_tail ^= 1;
+                       audio->out_needed--;
+               }
+               spin_unlock_irqrestore(&audio->dsp_lock, flags);
+       }
+
+       mutex_unlock(&audio->write_lock);
+
+       /* restore scheduling policy and priority */
+       if (!rt_policy(old_policy)) {
+               struct sched_param v = { .sched_priority = old_prio };
+               sched_setscheduler(current, old_policy, &v);
+               if (likely(!cap_nice)) {
+                       struct cred *new = prepare_creds();
+                       cap_lower(new->cap_effective, CAP_SYS_NICE);
+                       commit_creds(new);
+                       sched_setscheduler(current, SCHED_RR, &s);
+               }
+       }
+
+       LOG(EV_RETURN,(buf > start) ? (buf - start) : rc);
+       if (buf > start)
+               return buf - start;
+       return rc;
+}
+
+static int audio_release(struct inode *inode, struct file *file)
+{
+       struct audio *audio = file->private_data;
+
+       LOG(EV_OPEN, 0);
+       mutex_lock(&audio->lock);
+       audio_disable(audio);
+       audio_flush(audio);
+       audio->opened = 0;
+       mutex_unlock(&audio->lock);
+       htc_pwrsink_set(PWRSINK_AUDIO, 0);
+       return 0;
+}
+
+struct audio the_audio;
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+       struct audio *audio = &the_audio;
+       int rc;
+
+       mutex_lock(&audio->lock);
+
+       if (audio->opened) {
+               pr_err("audio: busy\n");
+               rc = -EBUSY;
+               goto done;
+       }
+
+       if (!audio->data) {
+               audio->data = dma_alloc_coherent(NULL, DMASZ,
+                                                &audio->phys, GFP_KERNEL);
+               if (!audio->data) {
+                       pr_err("audio: could not allocate DMA buffers\n");
+                       rc = -ENOMEM;
+                       goto done;
+               }
+       }
+
+       rc = audmgr_open(&audio->audmgr);
+       if (rc)
+               goto done;
+
+       audio->out_buffer_size = BUFSZ;
+       audio->out_sample_rate = 44100;
+       audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V;
+       audio->out_weight = 100;
+
+       audio->out[0].data = audio->data + 0;
+       audio->out[0].addr = audio->phys + 0;
+       audio->out[0].size = BUFSZ;
+
+       audio->out[1].data = audio->data + BUFSZ;
+       audio->out[1].addr = audio->phys + BUFSZ;
+       audio->out[1].size = BUFSZ;
+
+       audio->volume = 0x2000;
+
+       audio_flush(audio);
+
+       file->private_data = audio;
+       audio->opened = 1;
+       rc = 0;
+       LOG(EV_OPEN, 1);
+done:
+       mutex_unlock(&audio->lock);
+       return rc;
+}
+
+static long audpp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+       struct audio *audio = file->private_data;
+       int rc = 0, enable;
+       uint16_t enable_mask;
+
+       mutex_lock(&audio->lock);
+       switch (cmd) {
+       case AUDIO_ENABLE_AUDPP:
+               if (copy_from_user(&enable_mask, (void *) arg, sizeof(enable_mask)))
+                       goto out_fault;
+
+               enable = (enable_mask & ADRC_ENABLE)? 1 : 0;
+               audio_enable_adrc(audio, enable);
+               enable = (enable_mask & EQ_ENABLE)? 1 : 0;
+               audio_enable_eq(audio, enable);
+               enable = (enable_mask & IIR_ENABLE)? 1 : 0;
+               audio_enable_rx_iir(audio, enable);
+               break;
+
+       case AUDIO_SET_ADRC:
+               if (copy_from_user(&audio->adrc, (void*) arg, sizeof(audio->adrc)))
+                       goto out_fault;
+               break;
+
+       case AUDIO_SET_EQ:
+               if (copy_from_user(&audio->eq, (void*) arg, sizeof(audio->eq)))
+                       goto out_fault;
+               break;
+
+       case AUDIO_SET_RX_IIR:
+               if (copy_from_user(&audio->iir, (void*) arg, sizeof(audio->iir)))
+                       goto out_fault;
+               break;
+
+       default:
+               rc = -EINVAL;
+       }
+
+       goto out;
+
+ out_fault:
+       rc = -EFAULT;
+ out:
+       mutex_unlock(&audio->lock);
+       return rc;
+}
+
+static int audpp_open(struct inode *inode, struct file *file)
+{
+       struct audio *audio = &the_audio;
+
+       file->private_data = audio;
+       return 0;
+}
+
+static struct file_operations audio_fops = {
+       .owner          = THIS_MODULE,
+       .open           = audio_open,
+       .release        = audio_release,
+       .read           = audio_read,
+       .write          = audio_write,
+       .unlocked_ioctl = audio_ioctl,
+};
+
+static struct file_operations audpp_fops = {
+       .owner          = THIS_MODULE,
+       .open           = audpp_open,
+       .unlocked_ioctl = audpp_ioctl,
+};
+
+struct miscdevice audio_misc = {
+       .minor  = MISC_DYNAMIC_MINOR,
+       .name   = "msm_pcm_out",
+       .fops   = &audio_fops,
+};
+
+struct miscdevice audpp_misc = {
+       .minor  = MISC_DYNAMIC_MINOR,
+       .name   = "msm_pcm_ctl",
+       .fops   = &audpp_fops,
+};
+
+static int __init audio_init(void)
+{
+       mutex_init(&the_audio.lock);
+       mutex_init(&the_audio.write_lock);
+       spin_lock_init(&the_audio.dsp_lock);
+       init_waitqueue_head(&the_audio.wait);
+       wake_lock_init(&the_audio.wakelock, WAKE_LOCK_SUSPEND, "audio_pcm");
+       wake_lock_init(&the_audio.idlelock, WAKE_LOCK_IDLE, "audio_pcm_idle");
+       return (misc_register(&audio_misc) || misc_register(&audpp_misc));
+}
+
+device_initcall(audio_init);
diff --git a/drivers/staging/dream/qdsp5/audio_qcelp.c b/drivers/staging/dream/qdsp5/audio_qcelp.c
new file mode 100644 (file)
index 0000000..f0f50e3
--- /dev/null
@@ -0,0 +1,856 @@
+/* arch/arm/mach-msm/qdsp5/audio_qcelp.c
+ *
+ * qcelp 13k audio decoder device
+ *
+ * Copyright (c) 2008 QUALCOMM USA, INC.
+ *
+ * This code is based in part on audio_mp3.c, which is
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ *
+ * 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+#include <linux/msm_audio.h>
+#include <mach/qdsp5/qdsp5audppcmdi.h>
+#include <mach/qdsp5/qdsp5audppmsg.h>
+#include <mach/qdsp5/qdsp5audplaycmdi.h>
+#include <mach/qdsp5/qdsp5audplaymsg.h>
+
+#include "audmgr.h"
+/* for queue ids - should be relative to module number*/
+#include "adsp.h"
+
+#ifdef DEBUG
+#define dprintk(format, arg...) \
+printk(KERN_DEBUG format, ## arg)
+#else
+#define dprintk(format, arg...) do {} while (0)
+#endif
+
+#define BUFSZ 1080 /* QCELP 13K Hold 600ms packet data = 36 * 30 */
+#define BUF_COUNT 2
+#define DMASZ (BUFSZ * BUF_COUNT)
+
+#define PCM_BUFSZ_MIN 1600 /* 100ms worth of data */
+#define PCM_BUF_MAX_COUNT 5
+
+#define AUDDEC_DEC_QCELP 9
+
+#define        ROUTING_MODE_FTRT       1
+#define        ROUTING_MODE_RT         2
+/* Decoder status received from AUDPPTASK */
+#define        AUDPP_DEC_STATUS_SLEEP  0
+#define        AUDPP_DEC_STATUS_INIT   1
+#define        AUDPP_DEC_STATUS_CFG    2
+#define        AUDPP_DEC_STATUS_PLAY   3
+
+struct buffer {
+       void *data;
+       unsigned size;
+       unsigned used;          /* Input usage actual DSP produced PCM size  */
+       unsigned addr;
+};
+
+struct audio {
+       struct buffer out[BUF_COUNT];
+
+       spinlock_t dsp_lock;
+
+       uint8_t out_head;
+       uint8_t out_tail;
+       uint8_t out_needed;     /* number of buffers the dsp is waiting for */
+
+       struct mutex lock;
+       struct mutex write_lock;
+       wait_queue_head_t write_wait;
+
+       /* Host PCM section - START */
+       struct buffer in[PCM_BUF_MAX_COUNT];
+       struct mutex read_lock;
+       wait_queue_head_t read_wait;    /* Wait queue for read */
+       char *read_data;        /* pointer to reader buffer */
+       dma_addr_t read_phys;   /* physical address of reader buffer */
+       uint8_t read_next;      /* index to input buffers to be read next */
+       uint8_t fill_next;      /* index to buffer that DSP should be filling */
+       uint8_t pcm_buf_count;  /* number of pcm buffer allocated */
+       /* Host PCM section - END */
+
+       struct msm_adsp_module *audplay;
+
+       struct audmgr audmgr;
+
+       /* data allocated for various buffers */
+       char *data;
+       dma_addr_t phys;
+
+       uint8_t opened:1;
+       uint8_t enabled:1;
+       uint8_t running:1;
+       uint8_t stopped:1;      /* set when stopped, cleared on flush */
+       uint8_t pcm_feedback:1; /* set when non-tunnel mode */
+       uint8_t buf_refresh:1;
+
+       unsigned volume;
+
+       uint16_t dec_id;
+};
+
+static struct audio the_qcelp_audio;
+
+static int auddec_dsp_config(struct audio *audio, int enable);
+static void audpp_cmd_cfg_adec_params(struct audio *audio);
+static void audpp_cmd_cfg_routing_mode(struct audio *audio);
+static void audqcelp_send_data(struct audio *audio, unsigned needed);
+static void audqcelp_config_hostpcm(struct audio *audio);
+static void audqcelp_buffer_refresh(struct audio *audio);
+static void audqcelp_dsp_event(void *private, unsigned id, uint16_t *msg);
+
+/* must be called with audio->lock held */
+static int audqcelp_enable(struct audio *audio)
+{
+       struct audmgr_config cfg;
+       int rc;
+
+       dprintk("audqcelp_enable()\n");
+
+       if (audio->enabled)
+               return 0;
+
+       audio->out_tail = 0;
+       audio->out_needed = 0;
+
+       cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE;
+       cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000;
+       cfg.def_method = RPC_AUD_DEF_METHOD_PLAYBACK;
+       cfg.codec = RPC_AUD_DEF_CODEC_13K;
+       cfg.snd_method = RPC_SND_METHOD_MIDI;
+
+       rc = audmgr_enable(&audio->audmgr, &cfg);
+       if (rc < 0)
+               return rc;
+
+       if (msm_adsp_enable(audio->audplay)) {
+               pr_err("audio: msm_adsp_enable(audplay) failed\n");
+               audmgr_disable(&audio->audmgr);
+               return -ENODEV;
+       }
+
+       if (audpp_enable(audio->dec_id, audqcelp_dsp_event, audio)) {
+               pr_err("audio: audpp_enable() failed\n");
+               msm_adsp_disable(audio->audplay);
+               audmgr_disable(&audio->audmgr);
+               return -ENODEV;
+       }
+       audio->enabled = 1;
+       return 0;
+}
+
+/* must be called with audio->lock held */
+static int audqcelp_disable(struct audio *audio)
+{
+       dprintk("audqcelp_disable()\n");
+       if (audio->enabled) {
+               audio->enabled = 0;
+               auddec_dsp_config(audio, 0);
+               wake_up(&audio->write_wait);
+               wake_up(&audio->read_wait);
+               msm_adsp_disable(audio->audplay);
+               audpp_disable(audio->dec_id, audio);
+               audmgr_disable(&audio->audmgr);
+               audio->out_needed = 0;
+       }
+       return 0;
+}
+
+/* ------------------- dsp --------------------- */
+static void audqcelp_update_pcm_buf_entry(struct audio *audio,
+       uint32_t *payload)
+{
+       uint8_t index;
+       unsigned long flags;
+
+       spin_lock_irqsave(&audio->dsp_lock, flags);
+       for (index = 0; index < payload[1]; index++) {
+               if (audio->in[audio->fill_next].addr ==
+                       payload[2 + index * 2]) {
+                       dprintk("audqcelp_update_pcm_buf_entry: in[%d] ready\n",
+                       audio->fill_next);
+                       audio->in[audio->fill_next].used =
+                       payload[3 + index * 2];
+                       if ((++audio->fill_next) == audio->pcm_buf_count)
+                               audio->fill_next = 0;
+               } else {
+                       pr_err(
+                       "audqcelp_update_pcm_buf_entry: expected=%x ret=%x\n",
+                       audio->in[audio->fill_next].addr,
+                       payload[1 + index * 2]);
+                       break;
+               }
+       }
+       if (audio->in[audio->fill_next].used == 0) {
+               audqcelp_buffer_refresh(audio);
+       } else {
+               dprintk("audqcelp_update_pcm_buf_entry: read cannot keep up\n");
+               audio->buf_refresh = 1;
+       }
+
+       spin_unlock_irqrestore(&audio->dsp_lock, flags);
+       wake_up(&audio->read_wait);
+}
+
+static void audplay_dsp_event(void *data, unsigned id, size_t len,
+                             void (*getevent) (void *ptr, size_t len))
+{
+       struct audio *audio = data;
+       uint32_t msg[28];
+       getevent(msg, sizeof(msg));
+
+       dprintk("audplay_dsp_event: msg_id=%x\n", id);
+
+       switch (id) {
+       case AUDPLAY_MSG_DEC_NEEDS_DATA:
+               audqcelp_send_data(audio, 1);
+               break;
+
+       case AUDPLAY_MSG_BUFFER_UPDATE:
+               audqcelp_update_pcm_buf_entry(audio, msg);
+               break;
+
+       default:
+               pr_err("unexpected message from decoder \n");
+       }
+}
+
+static void audqcelp_dsp_event(void *private, unsigned id, uint16_t *msg)
+{
+       struct audio *audio = private;
+
+       switch (id) {
+       case AUDPP_MSG_STATUS_MSG:{
+                       unsigned status = msg[1];
+
+                       switch (status) {
+                       case AUDPP_DEC_STATUS_SLEEP:
+                               dprintk("decoder status: sleep \n");
+                               break;
+
+                       case AUDPP_DEC_STATUS_INIT:
+                               dprintk("decoder status: init \n");
+                               audpp_cmd_cfg_routing_mode(audio);
+                               break;
+
+                       case AUDPP_DEC_STATUS_CFG:
+                               dprintk("decoder status: cfg \n");
+                               break;
+                       case AUDPP_DEC_STATUS_PLAY:
+                               dprintk("decoder status: play \n");
+                               if (audio->pcm_feedback) {
+                                       audqcelp_config_hostpcm(audio);
+                                       audqcelp_buffer_refresh(audio);
+                               }
+                               break;
+                       default:
+                               pr_err("unknown decoder status \n");
+                       }
+                       break;
+               }
+       case AUDPP_MSG_CFG_MSG:
+               if (msg[0] == AUDPP_MSG_ENA_ENA) {
+                       dprintk("audqcelp_dsp_event: CFG_MSG ENABLE\n");
+                       auddec_dsp_config(audio, 1);
+                       audio->out_needed = 0;
+                       audio->running = 1;
+                       audpp_set_volume_and_pan(audio->dec_id, audio->volume,
+                                                0);
+                       audpp_avsync(audio->dec_id, 22050);
+               } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+                       dprintk("audqcelp_dsp_event: CFG_MSG DISABLE\n");
+                       audpp_avsync(audio->dec_id, 0);
+                       audio->running = 0;
+               } else {
+                       pr_err("audqcelp_dsp_event: CFG_MSG %d?\n", msg[0]);
+               }
+               break;
+       case AUDPP_MSG_ROUTING_ACK:
+               dprintk("audqcelp_dsp_event: ROUTING_ACK mode=%d\n", msg[1]);
+               audpp_cmd_cfg_adec_params(audio);
+               break;
+       default:
+               pr_err("audqcelp_dsp_event: UNKNOWN (%d)\n", id);
+       }
+
+}
+
+struct msm_adsp_ops audplay_adsp_ops_qcelp = {
+       .event = audplay_dsp_event,
+};
+
+#define audplay_send_queue0(audio, cmd, len) \
+       msm_adsp_write(audio->audplay, QDSP_uPAudPlay0BitStreamCtrlQueue, \
+                      cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+       audpp_cmd_cfg_dec_type cmd;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+       if (enable)
+               cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+                   AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_QCELP;
+       else
+               cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | AUDPP_CMD_DIS_DEC_V;
+
+       return audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_adec_params(struct audio *audio)
+{
+       struct audpp_cmd_cfg_adec_params_v13k cmd;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
+       cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_V13K_LEN;
+       cmd.common.dec_id = audio->dec_id;
+       cmd.common.input_sampling_frequency = 8000;
+       cmd.stereo_cfg = AUDPP_CMD_PCM_INTF_MONO_V;
+
+       audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_routing_mode(struct audio *audio)
+{
+       struct audpp_cmd_routing_mode cmd;
+       dprintk("audpp_cmd_cfg_routing_mode()\n");
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.cmd_id = AUDPP_CMD_ROUTING_MODE;
+       cmd.object_number = audio->dec_id;
+       if (audio->pcm_feedback)
+               cmd.routing_mode = ROUTING_MODE_FTRT;
+       else
+               cmd.routing_mode = ROUTING_MODE_RT;
+       audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static int audplay_dsp_send_data_avail(struct audio *audio,
+                                      unsigned idx, unsigned len)
+{
+       audplay_cmd_bitstream_data_avail cmd;
+
+       cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL;
+       cmd.decoder_id = audio->dec_id;
+       cmd.buf_ptr = audio->out[idx].addr;
+       cmd.buf_size = len / 2;
+       cmd.partition_number = 0;
+       return audplay_send_queue0(audio, &cmd, sizeof(cmd));
+}
+
+static void audqcelp_buffer_refresh(struct audio *audio)
+{
+       struct audplay_cmd_buffer_refresh refresh_cmd;
+
+       refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH;
+       refresh_cmd.num_buffers = 1;
+       refresh_cmd.buf0_address = audio->in[audio->fill_next].addr;
+       refresh_cmd.buf0_length = audio->in[audio->fill_next].size;
+       refresh_cmd.buf_read_count = 0;
+       dprintk("audplay_buffer_fresh: buf0_addr=%x buf0_len=%d\n",
+               refresh_cmd.buf0_address, refresh_cmd.buf0_length);
+
+       (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd));
+}
+
+static void audqcelp_config_hostpcm(struct audio *audio)
+{
+       struct audplay_cmd_hpcm_buf_cfg cfg_cmd;
+
+       dprintk("audqcelp_config_hostpcm()\n");
+       cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG;
+       cfg_cmd.max_buffers = audio->pcm_buf_count;
+       cfg_cmd.byte_swap = 0;
+       cfg_cmd.hostpcm_config = (0x8000) | (0x4000);
+       cfg_cmd.feedback_frequency = 1;
+       cfg_cmd.partition_number = 0;
+
+       (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd));
+}
+
+static void audqcelp_send_data(struct audio *audio, unsigned needed)
+{
+       struct buffer *frame;
+       unsigned long flags;
+
+       spin_lock_irqsave(&audio->dsp_lock, flags);
+       if (!audio->running)
+               goto done;
+
+       if (needed) {
+               /* We were called from the callback because the DSP
+                * requested more data.  Note that the DSP does want
+                * more data, and if a buffer was in-flight, mark it
+                * as available (since the DSP must now be done with
+                * it).
+                */
+               audio->out_needed = 1;
+               frame = audio->out + audio->out_tail;
+               if (frame->used == 0xffffffff) {
+                       dprintk("frame %d free\n", audio->out_tail);
+                       frame->used = 0;
+                       audio->out_tail ^= 1;
+                       wake_up(&audio->write_wait);
+               }
+       }
+
+       if (audio->out_needed) {
+               /* If the DSP currently wants data and we have a
+                * buffer available, we will send it and reset
+                * the needed flag.  We'll mark the buffer as in-flight
+                * so that it won't be recycled until the next buffer
+                * is requested
+                */
+
+               frame = audio->out + audio->out_tail;
+               if (frame->used) {
+                       BUG_ON(frame->used == 0xffffffff);
+                       dprintk("frame %d busy\n", audio->out_tail);
+                       audplay_dsp_send_data_avail(audio, audio->out_tail,
+                                                   frame->used);
+                       frame->used = 0xffffffff;
+                       audio->out_needed = 0;
+               }
+       }
+ done:
+       spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+/* ------------------- device --------------------- */
+
+static void audqcelp_flush(struct audio *audio)
+{
+       audio->out[0].used = 0;
+       audio->out[1].used = 0;
+       audio->out_head = 0;
+       audio->out_tail = 0;
+       audio->stopped = 0;
+}
+
+static void audqcelp_flush_pcm_buf(struct audio *audio)
+{
+       uint8_t index;
+
+       for (index = 0; index < PCM_BUF_MAX_COUNT; index++)
+               audio->in[index].used = 0;
+
+       audio->read_next = 0;
+       audio->fill_next = 0;
+}
+
+static long audqcelp_ioctl(struct file *file, unsigned int cmd,
+               unsigned long arg)
+{
+       struct audio *audio = file->private_data;
+       int rc = 0;
+
+       dprintk("audqcelp_ioctl() cmd = %d\n", cmd);
+
+       if (cmd == AUDIO_GET_STATS) {
+               struct msm_audio_stats stats;
+               stats.byte_count = audpp_avsync_byte_count(audio->dec_id);
+               stats.sample_count = audpp_avsync_sample_count(audio->dec_id);
+               if (copy_to_user((void *)arg, &stats, sizeof(stats)))
+                       return -EFAULT;
+               return 0;
+       }
+       if (cmd == AUDIO_SET_VOLUME) {
+               unsigned long flags;
+               spin_lock_irqsave(&audio->dsp_lock, flags);
+               audio->volume = arg;
+               if (audio->running)
+                       audpp_set_volume_and_pan(audio->dec_id, arg, 0);
+               spin_unlock_irqrestore(&audio->dsp_lock, flags);
+               return 0;
+       }
+       mutex_lock(&audio->lock);
+       switch (cmd) {
+       case AUDIO_START:
+               rc = audqcelp_enable(audio);
+               break;
+       case AUDIO_STOP:
+               rc = audqcelp_disable(audio);
+               audio->stopped = 1;
+               break;
+       case AUDIO_FLUSH:
+               if (audio->stopped) {
+                       /* Make sure we're stopped and we wake any threads
+                        * that might be blocked holding the write_lock.
+                        * While audio->stopped write threads will always
+                        * exit immediately.
+                        */
+                       wake_up(&audio->write_wait);
+                       mutex_lock(&audio->write_lock);
+                       audqcelp_flush(audio);
+                       mutex_unlock(&audio->write_lock);
+                       wake_up(&audio->read_wait);
+                       mutex_lock(&audio->read_lock);
+                       audqcelp_flush_pcm_buf(audio);
+                       mutex_unlock(&audio->read_lock);
+                       break;
+               }
+               break;
+       case AUDIO_SET_CONFIG:
+               dprintk("AUDIO_SET_CONFIG not applicable \n");
+               break;
+       case AUDIO_GET_CONFIG:{
+                       struct msm_audio_config config;
+                       config.buffer_size = BUFSZ;
+                       config.buffer_count = BUF_COUNT;
+                       config.sample_rate = 8000;
+                       config.channel_count = 1;
+                       config.unused[0] = 0;
+                       config.unused[1] = 0;
+                       config.unused[2] = 0;
+                       config.unused[3] = 0;
+                       if (copy_to_user((void *)arg, &config,
+                                        sizeof(config)))
+                               rc = -EFAULT;
+                       else
+                               rc = 0;
+
+                       break;
+               }
+       case AUDIO_GET_PCM_CONFIG:{
+                       struct msm_audio_pcm_config config;
+
+                       config.pcm_feedback = 0;
+                       config.buffer_count = PCM_BUF_MAX_COUNT;
+                       config.buffer_size = PCM_BUFSZ_MIN;
+                       if (copy_to_user((void *)arg, &config,
+                               sizeof(config)))
+                               rc = -EFAULT;
+                       else
+                               rc = 0;
+                       break;
+               }
+       case AUDIO_SET_PCM_CONFIG:{
+                       struct msm_audio_pcm_config config;
+
+                       if (copy_from_user(&config, (void *)arg,
+                               sizeof(config))) {
+                               rc = -EFAULT;
+                               break;
+                       }
+                       if ((config.buffer_count > PCM_BUF_MAX_COUNT) ||
+                               (config.buffer_count == 1))
+                               config.buffer_count = PCM_BUF_MAX_COUNT;
+
+                       if (config.buffer_size < PCM_BUFSZ_MIN)
+                               config.buffer_size = PCM_BUFSZ_MIN;
+
+                       /* Check if pcm feedback is required */
+                       if ((config.pcm_feedback) && (!audio->read_data)) {
+                               dprintk(
+                               "audqcelp_ioctl: allocate PCM buf %d\n",
+                               config.buffer_count * config.buffer_size);
+                               audio->read_data = dma_alloc_coherent(NULL,
+                               config.buffer_size * config.buffer_count,
+                               &audio->read_phys, GFP_KERNEL);
+                               if (!audio->read_data) {
+                                       pr_err(
+                                       "audqcelp_ioctl: no mem for pcm buf\n"
+                                       );
+                                       rc = -ENOMEM;
+                               } else {
+                                       uint8_t index;
+                                       uint32_t offset = 0;
+
+                                       audio->pcm_feedback = 1;
+                                       audio->buf_refresh = 0;
+                                       audio->pcm_buf_count =
+                                               config.buffer_count;
+                                       audio->read_next = 0;
+                                       audio->fill_next = 0;
+
+                                       for (index = 0;
+                                       index < config.buffer_count; index++) {
+                                               audio->in[index].data =
+                                               audio->read_data + offset;
+                                               audio->in[index].addr =
+                                               audio->read_phys + offset;
+                                               audio->in[index].size =
+                                               config.buffer_size;
+                                               audio->in[index].used = 0;
+                                               offset += config.buffer_size;
+                                       }
+                                       rc = 0;
+                               }
+                       } else {
+                               rc = 0;
+                       }
+                       break;
+               }
+       case AUDIO_PAUSE:
+               dprintk("%s: AUDIO_PAUSE %ld\n", __func__, arg);
+               rc = audpp_pause(audio->dec_id, (int) arg);
+               break;
+       default:
+               rc = -EINVAL;
+       }
+       mutex_unlock(&audio->lock);
+       return rc;
+}
+
+static ssize_t audqcelp_read(struct file *file, char __user *buf, size_t count,
+                       loff_t *pos)
+{
+       struct audio *audio = file->private_data;
+       const char __user *start = buf;
+       int rc = 0;
+
+       if (!audio->pcm_feedback)
+               return 0; /* PCM feedback is not enabled. Nothing to read */
+
+       mutex_lock(&audio->read_lock);
+       dprintk("audqcelp_read() %d \n", count);
+       while (count > 0) {
+               rc = wait_event_interruptible(audio->read_wait,
+                               (audio->in[audio->read_next].used > 0) ||
+                               (audio->stopped));
+               if (rc < 0)
+                       break;
+
+               if (audio->stopped) {
+                       rc = -EBUSY;
+                       break;
+               }
+
+               if (count < audio->in[audio->read_next].used) {
+                       /* Read must happen in frame boundary. Since driver does
+                       not know frame size, read count must be greater or equal
+                       to size of PCM samples */
+                       dprintk("audqcelp_read:read stop - partial frame\n");
+                       break;
+               } else {
+                       dprintk("audqcelp_read: read from in[%d]\n",
+                               audio->read_next);
+                       if (copy_to_user(buf,
+                               audio->in[audio->read_next].data,
+                               audio->in[audio->read_next].used)) {
+                               pr_err("audqcelp_read: invalid addr %x \n",
+                                       (unsigned int)buf);
+                               rc = -EFAULT;
+                               break;
+                       }
+                       count -= audio->in[audio->read_next].used;
+                       buf += audio->in[audio->read_next].used;
+                       audio->in[audio->read_next].used = 0;
+                       if ((++audio->read_next) == audio->pcm_buf_count)
+                               audio->read_next = 0;
+               }
+       }
+
+       if (audio->buf_refresh) {
+               audio->buf_refresh = 0;
+               dprintk("audqcelp_read: kick start pcm feedback again\n");
+               audqcelp_buffer_refresh(audio);
+       }
+
+       mutex_unlock(&audio->read_lock);
+
+       if (buf > start)
+               rc = buf - start;
+
+       dprintk("audqcelp_read: read %d bytes\n", rc);
+       return rc;
+}
+
+static ssize_t audqcelp_write(struct file *file, const char __user *buf,
+                          size_t count, loff_t *pos)
+{
+       struct audio *audio = file->private_data;
+       const char __user *start = buf;
+       struct buffer *frame;
+       size_t xfer;
+       int rc = 0;
+
+       if (count & 1)
+               return -EINVAL;
+       dprintk("audqcelp_write() \n");
+       mutex_lock(&audio->write_lock);
+       while (count > 0) {
+               frame = audio->out + audio->out_head;
+               rc = wait_event_interruptible(audio->write_wait,
+                                             (frame->used == 0)
+                                             || (audio->stopped));
+               dprintk("audqcelp_write() buffer available\n");
+               if (rc < 0)
+                       break;
+               if (audio->stopped) {
+                       rc = -EBUSY;
+                       break;
+               }
+               xfer = (count > frame->size) ? frame->size : count;
+               if (copy_from_user(frame->data, buf, xfer)) {
+                       rc = -EFAULT;
+                       break;
+               }
+
+               frame->used = xfer;
+               audio->out_head ^= 1;
+               count -= xfer;
+               buf += xfer;
+
+               audqcelp_send_data(audio, 0);
+
+       }
+       mutex_unlock(&audio->write_lock);
+       if (buf > start)
+               return buf - start;
+       return rc;
+}
+
+static int audqcelp_release(struct inode *inode, struct file *file)
+{
+       struct audio *audio = file->private_data;
+
+       dprintk("audqcelp_release()\n");
+
+       mutex_lock(&audio->lock);
+       audqcelp_disable(audio);
+       audqcelp_flush(audio);
+       audqcelp_flush_pcm_buf(audio);
+       msm_adsp_put(audio->audplay);
+       audio->audplay = NULL;
+       audio->opened = 0;
+       if (audio->data)
+               dma_free_coherent(NULL, DMASZ, audio->data, audio->phys);
+       audio->data = NULL;
+       if (audio->read_data) {
+               dma_free_coherent(NULL,
+                                audio->in[0].size * audio->pcm_buf_count,
+                                audio->read_data, audio->read_phys);
+               audio->read_data = NULL;
+       }
+       audio->pcm_feedback = 0;
+       mutex_unlock(&audio->lock);
+       return 0;
+}
+
+static int audqcelp_open(struct inode *inode, struct file *file)
+{
+       struct audio *audio = &the_qcelp_audio;
+       int rc;
+
+       mutex_lock(&audio->lock);
+
+       if (audio->opened) {
+               pr_err("audio: busy\n");
+               rc = -EBUSY;
+               goto done;
+       }
+
+       audio->data = dma_alloc_coherent(NULL, DMASZ,
+                                        &audio->phys, GFP_KERNEL);
+       if (!audio->data) {
+               pr_err("audio: could not allocate DMA buffers\n");
+               rc = -ENOMEM;
+               goto done;
+       }
+
+       rc = audmgr_open(&audio->audmgr);
+       if (rc)
+               goto err;
+
+       rc = msm_adsp_get("AUDPLAY0TASK", &audio->audplay,
+               &audplay_adsp_ops_qcelp, audio);
+       if (rc) {
+               pr_err("audio: failed to get audplay0 dsp module\n");
+               audmgr_close(&audio->audmgr);
+               goto err;
+       }
+
+       audio->dec_id = 0;
+
+       audio->out[0].data = audio->data + 0;
+       audio->out[0].addr = audio->phys + 0;
+       audio->out[0].size = BUFSZ;
+
+       audio->out[1].data = audio->data + BUFSZ;
+       audio->out[1].addr = audio->phys + BUFSZ;
+       audio->out[1].size = BUFSZ;
+
+       audio->volume = 0x2000; /* Q13 1.0 */
+
+       audqcelp_flush(audio);
+
+       file->private_data = audio;
+       audio->opened = 1;
+       rc = 0;
+done:
+       mutex_unlock(&audio->lock);
+       return rc;
+err:
+       dma_free_coherent(NULL, DMASZ, audio->data, audio->phys);
+       mutex_unlock(&audio->lock);
+       return rc;
+}
+
+static struct file_operations audio_qcelp_fops = {
+       .owner = THIS_MODULE,
+       .open = audqcelp_open,
+       .release = audqcelp_release,
+       .read = audqcelp_read,
+       .write = audqcelp_write,
+       .unlocked_ioctl = audqcelp_ioctl,
+};
+
+struct miscdevice audio_qcelp_misc = {
+       .minor = MISC_DYNAMIC_MINOR,
+       .name = "msm_qcelp",
+       .fops = &audio_qcelp_fops,
+};
+
+static int __init audqcelp_init(void)
+{
+       mutex_init(&the_qcelp_audio.lock);
+       mutex_init(&the_qcelp_audio.write_lock);
+       mutex_init(&the_qcelp_audio.read_lock);
+       spin_lock_init(&the_qcelp_audio.dsp_lock);
+       init_waitqueue_head(&the_qcelp_audio.write_wait);
+       init_waitqueue_head(&the_qcelp_audio.read_wait);
+       the_qcelp_audio.read_data = NULL;
+       return misc_register(&audio_qcelp_misc);
+}
+
+static void __exit audqcelp_exit(void)
+{
+       misc_deregister(&audio_qcelp_misc);
+}
+
+module_init(audqcelp_init);
+module_exit(audqcelp_exit);
+
+MODULE_DESCRIPTION("MSM QCELP 13K driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("QUALCOMM");
diff --git a/drivers/staging/dream/qdsp5/audmgr.c b/drivers/staging/dream/qdsp5/audmgr.c
new file mode 100644 (file)
index 0000000..1ad8b82
--- /dev/null
@@ -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);
diff --git a/drivers/staging/dream/qdsp5/audmgr.h b/drivers/staging/dream/qdsp5/audmgr.h
new file mode 100644 (file)
index 0000000..c07c36b
--- /dev/null
@@ -0,0 +1,215 @@
+/* arch/arm/mach-msm/qdsp5/audmgr.h
+ *
+ * Copyright 2008 (c) QUALCOMM Incorporated.
+ * 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.
+ *
+ */
+
+#ifndef _ARCH_ARM_MACH_MSM_AUDMGR_H
+#define _ARCH_ARM_MACH_MSM_AUDMGR_H
+
+#if CONFIG_MSM_AMSS_VERSION==6350
+#include "audmgr_new.h"
+#else
+
+enum rpc_aud_def_sample_rate_type {
+       RPC_AUD_DEF_SAMPLE_RATE_NONE,
+       RPC_AUD_DEF_SAMPLE_RATE_8000,
+       RPC_AUD_DEF_SAMPLE_RATE_11025,
+       RPC_AUD_DEF_SAMPLE_RATE_12000,
+       RPC_AUD_DEF_SAMPLE_RATE_16000,
+       RPC_AUD_DEF_SAMPLE_RATE_22050,
+       RPC_AUD_DEF_SAMPLE_RATE_24000,
+       RPC_AUD_DEF_SAMPLE_RATE_32000,
+       RPC_AUD_DEF_SAMPLE_RATE_44100,
+       RPC_AUD_DEF_SAMPLE_RATE_48000,
+       RPC_AUD_DEF_SAMPLE_RATE_MAX,
+};
+
+enum rpc_aud_def_method_type {
+       RPC_AUD_DEF_METHOD_NONE,
+       RPC_AUD_DEF_METHOD_KEY_BEEP,
+       RPC_AUD_DEF_METHOD_PLAYBACK,
+       RPC_AUD_DEF_METHOD_VOICE,
+       RPC_AUD_DEF_METHOD_RECORD,
+       RPC_AUD_DEF_METHOD_HOST_PCM,
+       RPC_AUD_DEF_METHOD_MIDI_OUT,
+       RPC_AUD_DEF_METHOD_RECORD_SBC,
+       RPC_AUD_DEF_METHOD_DTMF_RINGER,
+       RPC_AUD_DEF_METHOD_MAX,
+};
+
+enum rpc_aud_def_codec_type {
+       RPC_AUD_DEF_CODEC_NONE,
+       RPC_AUD_DEF_CODEC_DTMF,
+       RPC_AUD_DEF_CODEC_MIDI,
+       RPC_AUD_DEF_CODEC_MP3,
+       RPC_AUD_DEF_CODEC_PCM,
+       RPC_AUD_DEF_CODEC_AAC,
+       RPC_AUD_DEF_CODEC_WMA,
+       RPC_AUD_DEF_CODEC_RA,
+       RPC_AUD_DEF_CODEC_ADPCM,
+       RPC_AUD_DEF_CODEC_GAUDIO,
+       RPC_AUD_DEF_CODEC_VOC_EVRC,
+       RPC_AUD_DEF_CODEC_VOC_13K,
+       RPC_AUD_DEF_CODEC_VOC_4GV_NB,
+       RPC_AUD_DEF_CODEC_VOC_AMR,
+       RPC_AUD_DEF_CODEC_VOC_EFR,
+       RPC_AUD_DEF_CODEC_VOC_FR,
+       RPC_AUD_DEF_CODEC_VOC_HR,
+       RPC_AUD_DEF_CODEC_VOC,
+       RPC_AUD_DEF_CODEC_SBC,
+       RPC_AUD_DEF_CODEC_VOC_PCM,
+       RPC_AUD_DEF_CODEC_AMR_WB,
+       RPC_AUD_DEF_CODEC_AMR_WB_PLUS,
+       RPC_AUD_DEF_CODEC_MAX,
+};
+
+enum rpc_snd_method_type {
+       RPC_SND_METHOD_VOICE = 0,
+       RPC_SND_METHOD_KEY_BEEP,
+       RPC_SND_METHOD_MESSAGE,
+       RPC_SND_METHOD_RING,
+       RPC_SND_METHOD_MIDI,
+       RPC_SND_METHOD_AUX,
+       RPC_SND_METHOD_MAX,
+};
+
+enum rpc_voc_codec_type {
+       RPC_VOC_CODEC_DEFAULT,
+       RPC_VOC_CODEC_ON_CHIP_0 = RPC_VOC_CODEC_DEFAULT,
+       RPC_VOC_CODEC_ON_CHIP_1,
+       RPC_VOC_CODEC_STEREO_HEADSET,
+       RPC_VOC_CODEC_ON_CHIP_AUX,
+       RPC_VOC_CODEC_BT_OFF_BOARD,
+       RPC_VOC_CODEC_BT_A2DP,
+       RPC_VOC_CODEC_OFF_BOARD,
+       RPC_VOC_CODEC_SDAC,
+       RPC_VOC_CODEC_RX_EXT_SDAC_TX_INTERNAL,
+       RPC_VOC_CODEC_IN_STEREO_SADC_OUT_MONO_HANDSET,
+       RPC_VOC_CODEC_IN_STEREO_SADC_OUT_STEREO_HEADSET,
+       RPC_VOC_CODEC_TX_INT_SADC_RX_EXT_AUXPCM,
+       RPC_VOC_CODEC_EXT_STEREO_SADC_OUT_MONO_HANDSET,
+       RPC_VOC_CODEC_EXT_STEREO_SADC_OUT_STEREO_HEADSET,
+       RPC_VOC_CODEC_TTY_ON_CHIP_1,
+       RPC_VOC_CODEC_TTY_OFF_BOARD,
+       RPC_VOC_CODEC_TTY_VCO,
+       RPC_VOC_CODEC_TTY_HCO,
+       RPC_VOC_CODEC_ON_CHIP_0_DUAL_MIC,
+       RPC_VOC_CODEC_MAX,
+       RPC_VOC_CODEC_NONE,
+};
+
+enum rpc_audmgr_status_type {
+       RPC_AUDMGR_STATUS_READY,
+       RPC_AUDMGR_STATUS_CODEC_CONFIG,
+       RPC_AUDMGR_STATUS_PENDING,
+       RPC_AUDMGR_STATUS_SUSPEND,
+       RPC_AUDMGR_STATUS_FAILURE,
+       RPC_AUDMGR_STATUS_VOLUME_CHANGE,
+       RPC_AUDMGR_STATUS_DISABLED,
+       RPC_AUDMGR_STATUS_ERROR,
+};
+
+struct rpc_audmgr_enable_client_args {
+       uint32_t set_to_one;
+       uint32_t tx_sample_rate;
+       uint32_t rx_sample_rate;
+       uint32_t def_method;
+       uint32_t codec_type;
+       uint32_t snd_method;
+
+       uint32_t cb_func;
+       uint32_t client_data;
+};
+
+#define AUDMGR_ENABLE_CLIENT                   2
+#define AUDMGR_DISABLE_CLIENT                  3
+#define AUDMGR_SUSPEND_EVENT_RSP               4
+#define AUDMGR_REGISTER_OPERATION_LISTENER     5
+#define AUDMGR_UNREGISTER_OPERATION_LISTENER   6
+#define AUDMGR_REGISTER_CODEC_LISTENER         7
+#define AUDMGR_GET_RX_SAMPLE_RATE              8
+#define AUDMGR_GET_TX_SAMPLE_RATE              9
+#define AUDMGR_SET_DEVICE_MODE                 10
+
+#if CONFIG_MSM_AMSS_VERSION < 6220
+#define AUDMGR_PROG_VERS "rs30000013:46255756"
+#define AUDMGR_PROG 0x30000013
+#define AUDMGR_VERS 0x46255756
+#else
+#define AUDMGR_PROG_VERS "rs30000013:e94e8f0c"
+#define AUDMGR_PROG 0x30000013
+#define AUDMGR_VERS 0xe94e8f0c
+#endif
+
+struct rpc_audmgr_cb_func_ptr {
+       uint32_t cb_id;
+       uint32_t set_to_one;
+       uint32_t status;
+       union {
+               uint32_t handle;
+               uint32_t volume;
+
+       } u;
+};
+
+#define AUDMGR_CB_FUNC_PTR                     1
+#define AUDMGR_OPR_LSTNR_CB_FUNC_PTR           2
+#define AUDMGR_CODEC_LSTR_FUNC_PTR             3
+
+#if CONFIG_MSM_AMSS_VERSION < 6220
+#define AUDMGR_CB_PROG 0x31000013
+#define AUDMGR_CB_VERS 0x5fa922a9
+#else
+#define AUDMGR_CB_PROG 0x31000013
+#define AUDMGR_CB_VERS 0x21570ba7
+#endif
+
+struct audmgr {
+       wait_queue_head_t wait;
+       uint32_t handle;
+       struct msm_rpc_endpoint *ept;
+       struct task_struct *task;
+       int state;
+};
+
+struct audmgr_config {
+       uint32_t tx_rate;
+       uint32_t rx_rate;
+       uint32_t def_method;
+       uint32_t codec;
+       uint32_t snd_method;
+};
+
+int audmgr_open(struct audmgr *am);
+int audmgr_close(struct audmgr *am);
+int audmgr_enable(struct audmgr *am, struct audmgr_config *cfg);
+int audmgr_disable(struct audmgr *am);
+
+typedef void (*audpp_event_func)(void *private, unsigned id, uint16_t *msg);
+
+int audpp_enable(int id, audpp_event_func func, void *private);
+void audpp_disable(int id, void *private);
+
+int audpp_send_queue1(void *cmd, unsigned len);
+int audpp_send_queue2(void *cmd, unsigned len);
+int audpp_send_queue3(void *cmd, unsigned len);
+
+int audpp_pause(unsigned id, int pause);
+int audpp_set_volume_and_pan(unsigned id, unsigned volume, int pan);
+void audpp_avsync(int id, unsigned rate);
+unsigned audpp_avsync_sample_count(int id);
+unsigned audpp_avsync_byte_count(int id);
+
+#endif
+#endif
diff --git a/drivers/staging/dream/qdsp5/audmgr_new.h b/drivers/staging/dream/qdsp5/audmgr_new.h
new file mode 100644 (file)
index 0000000..49670fe
--- /dev/null
@@ -0,0 +1,213 @@
+/* arch/arm/mach-msm/qdsp5/audmgr.h
+ *
+ * Copyright 2008 (c) QUALCOMM Incorporated.
+ * 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.
+ *
+ */
+
+#ifndef _ARCH_ARM_MACH_MSM_AUDMGR_NEW_H
+#define _ARCH_ARM_MACH_MSM_AUDMGR_NEW_H
+
+enum rpc_aud_def_sample_rate_type {
+       RPC_AUD_DEF_SAMPLE_RATE_NONE,
+       RPC_AUD_DEF_SAMPLE_RATE_8000,
+       RPC_AUD_DEF_SAMPLE_RATE_11025,
+       RPC_AUD_DEF_SAMPLE_RATE_12000,
+       RPC_AUD_DEF_SAMPLE_RATE_16000,
+       RPC_AUD_DEF_SAMPLE_RATE_22050,
+       RPC_AUD_DEF_SAMPLE_RATE_24000,
+       RPC_AUD_DEF_SAMPLE_RATE_32000,
+       RPC_AUD_DEF_SAMPLE_RATE_44100,
+       RPC_AUD_DEF_SAMPLE_RATE_48000,
+       RPC_AUD_DEF_SAMPLE_RATE_MAX,
+};
+
+enum rpc_aud_def_method_type {
+       RPC_AUD_DEF_METHOD_NONE,
+       RPC_AUD_DEF_METHOD_KEY_BEEP,
+       RPC_AUD_DEF_METHOD_PLAYBACK,
+       RPC_AUD_DEF_METHOD_VOICE,
+       RPC_AUD_DEF_METHOD_RECORD,
+       RPC_AUD_DEF_METHOD_HOST_PCM,
+       RPC_AUD_DEF_METHOD_MIDI_OUT,
+       RPC_AUD_DEF_METHOD_RECORD_SBC,
+       RPC_AUD_DEF_METHOD_DTMF_RINGER,
+       RPC_AUD_DEF_METHOD_MAX,
+};
+
+enum rpc_aud_def_codec_type {
+       RPC_AUD_DEF_CODEC_NONE,
+       RPC_AUD_DEF_CODEC_DTMF,
+       RPC_AUD_DEF_CODEC_MIDI,
+       RPC_AUD_DEF_CODEC_MP3,
+       RPC_AUD_DEF_CODEC_PCM,
+       RPC_AUD_DEF_CODEC_AAC,
+       RPC_AUD_DEF_CODEC_WMA,
+       RPC_AUD_DEF_CODEC_RA,
+       RPC_AUD_DEF_CODEC_ADPCM,
+       RPC_AUD_DEF_CODEC_GAUDIO,
+       RPC_AUD_DEF_CODEC_VOC_EVRC,
+       RPC_AUD_DEF_CODEC_VOC_13K,
+       RPC_AUD_DEF_CODEC_VOC_4GV_NB,
+       RPC_AUD_DEF_CODEC_VOC_AMR,
+       RPC_AUD_DEF_CODEC_VOC_EFR,
+       RPC_AUD_DEF_CODEC_VOC_FR,
+       RPC_AUD_DEF_CODEC_VOC_HR,
+       RPC_AUD_DEF_CODEC_VOC_CDMA,
+       RPC_AUD_DEF_CODEC_VOC_CDMA_WB,
+       RPC_AUD_DEF_CODEC_VOC_UMTS,
+       RPC_AUD_DEF_CODEC_VOC_UMTS_WB,
+       RPC_AUD_DEF_CODEC_SBC,
+       RPC_AUD_DEF_CODEC_VOC_PCM,
+       RPC_AUD_DEF_CODEC_AMR_WB,
+       RPC_AUD_DEF_CODEC_AMR_WB_PLUS,
+       RPC_AUD_DEF_CODEC_AAC_BSAC,
+       RPC_AUD_DEF_CODEC_MAX,
+       RPC_AUD_DEF_CODEC_AMR_NB,
+       RPC_AUD_DEF_CODEC_13K,
+       RPC_AUD_DEF_CODEC_EVRC,
+       RPC_AUD_DEF_CODEC_MAX_002,
+};
+
+enum rpc_snd_method_type {
+       RPC_SND_METHOD_VOICE = 0,
+       RPC_SND_METHOD_KEY_BEEP,
+       RPC_SND_METHOD_MESSAGE,
+       RPC_SND_METHOD_RING,
+       RPC_SND_METHOD_MIDI,
+       RPC_SND_METHOD_AUX,
+       RPC_SND_METHOD_MAX,
+};
+
+enum rpc_voc_codec_type {
+       RPC_VOC_CODEC_DEFAULT,
+       RPC_VOC_CODEC_ON_CHIP_0 = RPC_VOC_CODEC_DEFAULT,
+       RPC_VOC_CODEC_ON_CHIP_1,
+       RPC_VOC_CODEC_STEREO_HEADSET,
+       RPC_VOC_CODEC_ON_CHIP_AUX,
+       RPC_VOC_CODEC_BT_OFF_BOARD,
+       RPC_VOC_CODEC_BT_A2DP,
+       RPC_VOC_CODEC_OFF_BOARD,
+       RPC_VOC_CODEC_SDAC,
+       RPC_VOC_CODEC_RX_EXT_SDAC_TX_INTERNAL,
+       RPC_VOC_CODEC_IN_STEREO_SADC_OUT_MONO_HANDSET,
+       RPC_VOC_CODEC_IN_STEREO_SADC_OUT_STEREO_HEADSET,
+       RPC_VOC_CODEC_TX_INT_SADC_RX_EXT_AUXPCM,
+       RPC_VOC_CODEC_EXT_STEREO_SADC_OUT_MONO_HANDSET,
+       RPC_VOC_CODEC_EXT_STEREO_SADC_OUT_STEREO_HEADSET,
+       RPC_VOC_CODEC_TTY_ON_CHIP_1,
+       RPC_VOC_CODEC_TTY_OFF_BOARD,
+       RPC_VOC_CODEC_TTY_VCO,
+       RPC_VOC_CODEC_TTY_HCO,
+       RPC_VOC_CODEC_ON_CHIP_0_DUAL_MIC,
+       RPC_VOC_CODEC_MAX,
+       RPC_VOC_CODEC_NONE,
+};
+
+enum rpc_audmgr_status_type {
+       RPC_AUDMGR_STATUS_READY,
+       RPC_AUDMGR_STATUS_CODEC_CONFIG,
+       RPC_AUDMGR_STATUS_PENDING,
+       RPC_AUDMGR_STATUS_SUSPEND,
+       RPC_AUDMGR_STATUS_FAILURE,
+       RPC_AUDMGR_STATUS_VOLUME_CHANGE,
+       RPC_AUDMGR_STATUS_DISABLED,
+       RPC_AUDMGR_STATUS_ERROR,
+};
+
+struct rpc_audmgr_enable_client_args {
+       uint32_t set_to_one;
+       uint32_t tx_sample_rate;
+       uint32_t rx_sample_rate;
+       uint32_t def_method;
+       uint32_t codec_type;
+       uint32_t snd_method;
+
+       uint32_t cb_func;
+       uint32_t client_data;
+};
+
+#define AUDMGR_ENABLE_CLIENT                   2
+#define AUDMGR_DISABLE_CLIENT                  3
+#define AUDMGR_SUSPEND_EVENT_RSP               4
+#define AUDMGR_REGISTER_OPERATION_LISTENER     5
+#define AUDMGR_UNREGISTER_OPERATION_LISTENER   6
+#define AUDMGR_REGISTER_CODEC_LISTENER         7
+#define AUDMGR_GET_RX_SAMPLE_RATE              8
+#define AUDMGR_GET_TX_SAMPLE_RATE              9
+#define AUDMGR_SET_DEVICE_MODE                 10
+
+#define AUDMGR_PROG 0x30000013
+#define AUDMGR_VERS MSM_RPC_VERS(1,0)
+
+struct rpc_audmgr_cb_func_ptr {
+       uint32_t cb_id;
+       uint32_t status; /* Audmgr status */
+       uint32_t set_to_one;  /* Pointer status (1 = valid, 0  = invalid) */
+       uint32_t disc;
+       /* disc = AUDMGR_STATUS_READY => data=handle
+          disc = AUDMGR_STATUS_CODEC_CONFIG => data = handle
+          disc = AUDMGR_STATUS_DISABLED => data =status_disabled
+          disc = AUDMGR_STATUS_VOLUME_CHANGE => data = volume-change */
+       union {
+               uint32_t handle;
+               uint32_t volume;
+               uint32_t status_disabled;
+               uint32_t volume_change;
+       } u;
+};
+
+#define AUDMGR_CB_FUNC_PTR                     1
+#define AUDMGR_OPR_LSTNR_CB_FUNC_PTR           2
+#define AUDMGR_CODEC_LSTR_FUNC_PTR             3
+
+#define AUDMGR_CB_PROG 0x31000013
+#define AUDMGR_CB_VERS 0xf8e3e2d9
+
+struct audmgr {
+       wait_queue_head_t wait;
+       uint32_t handle;
+       struct msm_rpc_endpoint *ept;
+       struct task_struct *task;
+       int state;
+};
+
+struct audmgr_config {
+       uint32_t tx_rate;
+       uint32_t rx_rate;
+       uint32_t def_method;
+       uint32_t codec;
+       uint32_t snd_method;
+};
+
+int audmgr_open(struct audmgr *am);
+int audmgr_close(struct audmgr *am);
+int audmgr_enable(struct audmgr *am, struct audmgr_config *cfg);
+int audmgr_disable(struct audmgr *am);
+
+typedef void (*audpp_event_func)(void *private, unsigned id, uint16_t *msg);
+
+int audpp_enable(int id, audpp_event_func func, void *private);
+void audpp_disable(int id, void *private);
+
+int audpp_send_queue1(void *cmd, unsigned len);
+int audpp_send_queue2(void *cmd, unsigned len);
+int audpp_send_queue3(void *cmd, unsigned len);
+
+int audpp_set_volume_and_pan(unsigned id, unsigned volume, int pan);
+int audpp_pause(unsigned id, int pause);
+int audpp_flush(unsigned id);
+void audpp_avsync(int id, unsigned rate);
+unsigned audpp_avsync_sample_count(int id);
+unsigned audpp_avsync_byte_count(int id);
+
+#endif
diff --git a/drivers/staging/dream/qdsp5/audpp.c b/drivers/staging/dream/qdsp5/audpp.c
new file mode 100644 (file)
index 0000000..d06556e
--- /dev/null
@@ -0,0 +1,429 @@
+
+/* arch/arm/mach-msm/qdsp5/audpp.c
+ *
+ * common code to deal with the AUDPP dsp task (audio postproc)
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+
+#include "audmgr.h"
+
+#include <mach/qdsp5/qdsp5audppcmdi.h>
+#include <mach/qdsp5/qdsp5audppmsg.h>
+
+/* for queue ids - should be relative to module number*/
+#include "adsp.h"
+
+#include "evlog.h"
+
+
+enum {
+       EV_NULL,
+       EV_ENABLE,
+       EV_DISABLE,
+       EV_EVENT,
+       EV_DATA,
+};
+
+static const char *dsp_log_strings[] = {
+       "NULL",
+       "ENABLE",
+       "DISABLE",
+       "EVENT",
+       "DATA",
+};
+
+DECLARE_LOG(dsp_log, 64, dsp_log_strings);
+
+static int __init _dsp_log_init(void)
+{
+       return ev_log_init(&dsp_log);
+}
+module_init(_dsp_log_init);
+#define LOG(id,arg) ev_log_write(&dsp_log, id, arg)
+
+static DEFINE_MUTEX(audpp_lock);
+
+#define CH_COUNT 5
+#define AUDPP_CLNT_MAX_COUNT 6
+#define AUDPP_AVSYNC_INFO_SIZE 7
+
+struct audpp_state {
+       struct msm_adsp_module *mod;
+       audpp_event_func func[AUDPP_CLNT_MAX_COUNT];
+       void *private[AUDPP_CLNT_MAX_COUNT];
+       struct mutex *lock;
+       unsigned open_count;
+       unsigned enabled;
+
+       /* which channels are actually enabled */
+       unsigned avsync_mask;
+
+       /* flags, 48 bits sample/bytes counter per channel */
+       uint16_t avsync[CH_COUNT * AUDPP_CLNT_MAX_COUNT + 1];
+};
+
+struct audpp_state the_audpp_state = {
+       .lock = &audpp_lock,
+};
+
+int audpp_send_queue1(void *cmd, unsigned len)
+{
+       return msm_adsp_write(the_audpp_state.mod,
+                             QDSP_uPAudPPCmd1Queue, cmd, len);
+}
+EXPORT_SYMBOL(audpp_send_queue1);
+
+int audpp_send_queue2(void *cmd, unsigned len)
+{
+       return msm_adsp_write(the_audpp_state.mod,
+                             QDSP_uPAudPPCmd2Queue, cmd, len);
+}
+EXPORT_SYMBOL(audpp_send_queue2);
+
+int audpp_send_queue3(void *cmd, unsigned len)
+{
+       return msm_adsp_write(the_audpp_state.mod,
+                             QDSP_uPAudPPCmd3Queue, cmd, len);
+}
+EXPORT_SYMBOL(audpp_send_queue3);
+
+static int audpp_dsp_config(int enable)
+{
+       audpp_cmd_cfg cmd;
+
+       cmd.cmd_id = AUDPP_CMD_CFG;
+       cmd.cfg = enable ? AUDPP_CMD_CFG_ENABLE : AUDPP_CMD_CFG_SLEEP;
+
+       return audpp_send_queue1(&cmd, sizeof(cmd));
+}
+
+static void audpp_broadcast(struct audpp_state *audpp, unsigned id,
+                           uint16_t *msg)
+{
+       unsigned n;
+       for (n = 0; n < AUDPP_CLNT_MAX_COUNT; n++) {
+               if (audpp->func[n])
+                       audpp->func[n] (audpp->private[n], id, msg);
+       }
+}
+
+static void audpp_notify_clnt(struct audpp_state *audpp, unsigned clnt_id,
+                             unsigned id, uint16_t *msg)
+{
+       if (clnt_id < AUDPP_CLNT_MAX_COUNT && audpp->func[clnt_id])
+               audpp->func[clnt_id] (audpp->private[clnt_id], id, msg);
+}
+
+static void audpp_dsp_event(void *data, unsigned id, size_t len,
+                           void (*getevent)(void *ptr, size_t len))
+{
+       struct audpp_state *audpp = data;
+       uint16_t msg[8];
+
+       if (id == AUDPP_MSG_AVSYNC_MSG) {
+               getevent(audpp->avsync, sizeof(audpp->avsync));
+
+               /* mask off any channels we're not watching to avoid
+                * cases where we might get one last update after
+                * disabling avsync and end up in an odd state when
+                * we next read...
+                */
+               audpp->avsync[0] &= audpp->avsync_mask;
+               return;
+       }
+
+       getevent(msg, sizeof(msg));
+
+       LOG(EV_EVENT, (id << 16) | msg[0]);
+       LOG(EV_DATA, (msg[1] << 16) | msg[2]);
+
+       switch (id) {
+       case AUDPP_MSG_STATUS_MSG:{
+                       unsigned cid = msg[0];
+                       pr_info("audpp: status %d %d %d\n", cid, msg[1],
+                               msg[2]);
+                       if ((cid < 5) && audpp->func[cid])
+                               audpp->func[cid] (audpp->private[cid], id, msg);
+                       break;
+               }
+       case AUDPP_MSG_HOST_PCM_INTF_MSG:
+               if (audpp->func[5])
+                       audpp->func[5] (audpp->private[5], id, msg);
+               break;
+       case AUDPP_MSG_PCMDMAMISSED:
+               pr_err("audpp: DMA missed obj=%x\n", msg[0]);
+               break;
+       case AUDPP_MSG_CFG_MSG:
+               if (msg[0] == AUDPP_MSG_ENA_ENA) {
+                       pr_info("audpp: ENABLE\n");
+                       audpp->enabled = 1;
+                       audpp_broadcast(audpp, id, msg);
+               } else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+                       pr_info("audpp: DISABLE\n");
+                       audpp->enabled = 0;
+                       audpp_broadcast(audpp, id, msg);
+               } else {
+                       pr_err("audpp: invalid config msg %d\n", msg[0]);
+               }
+               break;
+       case AUDPP_MSG_ROUTING_ACK:
+               audpp_broadcast(audpp, id, msg);
+               break;
+       case AUDPP_MSG_FLUSH_ACK:
+               audpp_notify_clnt(audpp, msg[0], id, msg);
+               break;
+       default:
+         pr_info("audpp: unhandled msg id %x\n", id);
+       }
+}
+
+static struct msm_adsp_ops adsp_ops = {
+       .event = audpp_dsp_event,
+};
+
+static void audpp_fake_event(struct audpp_state *audpp, int id,
+                            unsigned event, unsigned arg)
+{
+       uint16_t msg[1];
+       msg[0] = arg;
+       audpp->func[id] (audpp->private[id], event, msg);
+}
+
+int audpp_enable(int id, audpp_event_func func, void *private)
+{
+       struct audpp_state *audpp = &the_audpp_state;
+       int res = 0;
+
+       if (id < -1 || id > 4)
+               return -EINVAL;
+
+       if (id == -1)
+               id = 5;
+
+       mutex_lock(audpp->lock);
+       if (audpp->func[id]) {
+               res = -EBUSY;
+               goto out;
+       }
+
+       audpp->func[id] = func;
+       audpp->private[id] = private;
+
+       LOG(EV_ENABLE, 1);
+       if (audpp->open_count++ == 0) {
+               pr_info("audpp: enable\n");
+               res = msm_adsp_get("AUDPPTASK", &audpp->mod, &adsp_ops, audpp);
+               if (res < 0) {
+                       pr_err("audpp: cannot open AUDPPTASK\n");
+                       audpp->open_count = 0;
+                       audpp->func[id] = NULL;
+                       audpp->private[id] = NULL;
+                       goto out;
+               }
+               LOG(EV_ENABLE, 2);
+               msm_adsp_enable(audpp->mod);
+               audpp_dsp_config(1);
+       } else {
+               unsigned long flags;
+               local_irq_save(flags);
+               if (audpp->enabled)
+                       audpp_fake_event(audpp, id,
+                                        AUDPP_MSG_CFG_MSG, AUDPP_MSG_ENA_ENA);
+               local_irq_restore(flags);
+       }
+
+       res = 0;
+out:
+       mutex_unlock(audpp->lock);
+       return res;
+}
+EXPORT_SYMBOL(audpp_enable);
+
+void audpp_disable(int id, void *private)
+{
+       struct audpp_state *audpp = &the_audpp_state;
+       unsigned long flags;
+
+       if (id < -1 || id > 4)
+               return;
+
+       if (id == -1)
+               id = 5;
+
+       mutex_lock(audpp->lock);
+       LOG(EV_DISABLE, 1);
+       if (!audpp->func[id])
+               goto out;
+       if (audpp->private[id] != private)
+               goto out;
+
+       local_irq_save(flags);
+       audpp_fake_event(audpp, id, AUDPP_MSG_CFG_MSG, AUDPP_MSG_ENA_DIS);
+       audpp->func[id] = NULL;
+       audpp->private[id] = NULL;
+       local_irq_restore(flags);
+
+       if (--audpp->open_count == 0) {
+               pr_info("audpp: disable\n");
+               LOG(EV_DISABLE, 2);
+               audpp_dsp_config(0);
+               msm_adsp_disable(audpp->mod);
+               msm_adsp_put(audpp->mod);
+               audpp->mod = NULL;
+       }
+out:
+       mutex_unlock(audpp->lock);
+}
+EXPORT_SYMBOL(audpp_disable);
+
+#define BAD_ID(id) ((id < 0) || (id >= CH_COUNT))
+
+void audpp_avsync(int id, unsigned rate)
+{
+       unsigned long flags;
+       audpp_cmd_avsync cmd;
+
+       if (BAD_ID(id))
+               return;
+
+       local_irq_save(flags);
+       if (rate)
+               the_audpp_state.avsync_mask |= (1 << id);
+       else
+               the_audpp_state.avsync_mask &= (~(1 << id));
+       the_audpp_state.avsync[0] &= the_audpp_state.avsync_mask;
+       local_irq_restore(flags);
+
+       cmd.cmd_id = AUDPP_CMD_AVSYNC;
+       cmd.object_number = id;
+       cmd.interrupt_interval_lsw = rate;
+       cmd.interrupt_interval_msw = rate >> 16;
+       audpp_send_queue1(&cmd, sizeof(cmd));
+}
+EXPORT_SYMBOL(audpp_avsync);
+
+unsigned audpp_avsync_sample_count(int id)
+{
+       uint16_t *avsync = the_audpp_state.avsync;
+       unsigned val;
+       unsigned long flags;
+       unsigned mask;
+
+       if (BAD_ID(id))
+               return 0;
+
+       mask = 1 << id;
+       id = id * AUDPP_AVSYNC_INFO_SIZE + 2;
+       local_irq_save(flags);
+       if (avsync[0] & mask)
+               val = (avsync[id] << 16) | avsync[id + 1];
+       else
+               val = 0;
+       local_irq_restore(flags);
+
+       return val;
+}
+EXPORT_SYMBOL(audpp_avsync_sample_count);
+
+unsigned audpp_avsync_byte_count(int id)
+{
+       uint16_t *avsync = the_audpp_state.avsync;
+       unsigned val;
+       unsigned long flags;
+       unsigned mask;
+
+       if (BAD_ID(id))
+               return 0;
+
+       mask = 1 << id;
+       id = id * AUDPP_AVSYNC_INFO_SIZE + 5;
+       local_irq_save(flags);
+       if (avsync[0] & mask)
+               val = (avsync[id] << 16) | avsync[id + 1];
+       else
+               val = 0;
+       local_irq_restore(flags);
+
+       return val;
+}
+EXPORT_SYMBOL(audpp_avsync_byte_count);
+
+#define AUDPP_CMD_CFG_OBJ_UPDATE 0x8000
+#define AUDPP_CMD_VOLUME_PAN 0
+
+int audpp_set_volume_and_pan(unsigned id, unsigned volume, int pan)
+{
+       /* cmd, obj_cfg[7], cmd_type, volume, pan */
+       uint16_t cmd[11];
+
+       if (id > 6)
+               return -EINVAL;
+
+       memset(cmd, 0, sizeof(cmd));
+       cmd[0] = AUDPP_CMD_CFG_OBJECT_PARAMS;
+       cmd[1 + id] = AUDPP_CMD_CFG_OBJ_UPDATE;
+       cmd[8] = AUDPP_CMD_VOLUME_PAN;
+       cmd[9] = volume;
+       cmd[10] = pan;
+
+       return audpp_send_queue3(cmd, sizeof(cmd));
+}
+EXPORT_SYMBOL(audpp_set_volume_and_pan);
+
+int audpp_pause(unsigned id, int pause)
+{
+       /* pause 1 = pause 0 = resume */
+       u16 pause_cmd[AUDPP_CMD_DEC_CTRL_LEN / sizeof(unsigned short)];
+
+       if (id >= CH_COUNT)
+               return -EINVAL;
+
+       memset(pause_cmd, 0, sizeof(pause_cmd));
+
+       pause_cmd[0] = AUDPP_CMD_DEC_CTRL;
+       if (pause == 1)
+               pause_cmd[1 + id] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_PAUSE_V;
+       else if (pause == 0)
+               pause_cmd[1 + id] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_RESUME_V;
+       else
+               return -EINVAL;
+
+       return audpp_send_queue1(pause_cmd, sizeof(pause_cmd));
+}
+EXPORT_SYMBOL(audpp_pause);
+
+int audpp_flush(unsigned id)
+{
+       u16 flush_cmd[AUDPP_CMD_DEC_CTRL_LEN / sizeof(unsigned short)];
+
+       if (id >= CH_COUNT)
+               return -EINVAL;
+
+       memset(flush_cmd, 0, sizeof(flush_cmd));
+
+       flush_cmd[0] = AUDPP_CMD_DEC_CTRL;
+       flush_cmd[1 + id] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_FLUSH_V;
+
+       return audpp_send_queue1(flush_cmd, sizeof(flush_cmd));
+}
+EXPORT_SYMBOL(audpp_flush);
diff --git a/drivers/staging/dream/qdsp5/evlog.h b/drivers/staging/dream/qdsp5/evlog.h
new file mode 100644 (file)
index 0000000..922ce67
--- /dev/null
@@ -0,0 +1,133 @@
+/* arch/arm/mach-msm/qdsp5/evlog.h
+ *
+ * simple event log debugging facility
+ *
+ * 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/fs.h>
+#include <linux/hrtimer.h>
+#include <linux/debugfs.h>
+
+#define EV_LOG_ENTRY_NAME(n) n##_entry
+
+#define DECLARE_LOG(_name, _size, _str) \
+static struct ev_entry EV_LOG_ENTRY_NAME(_name)[_size]; \
+static struct ev_log _name = { \
+       .name = #_name, \
+       .strings = _str, \
+       .num_strings = ARRAY_SIZE(_str), \
+       .entry = EV_LOG_ENTRY_NAME(_name), \
+       .max = ARRAY_SIZE(EV_LOG_ENTRY_NAME(_name)), \
+}
+
+struct ev_entry {
+       ktime_t when;
+       uint32_t id;
+       uint32_t arg;
+};
+
+struct ev_log {
+       struct ev_entry *entry;
+       unsigned max;
+       unsigned next;
+       unsigned fault;
+       const char **strings;
+       unsigned num_strings;
+       const char *name;
+};
+
+static char ev_buf[4096];
+
+static ssize_t ev_log_read(struct file *file, char __user *buf,
+                          size_t count, loff_t *ppos)
+{
+       struct ev_log *log = file->private_data;
+       struct ev_entry *entry;
+       unsigned long flags;
+       int size = 0;
+       unsigned n, id, max;
+       ktime_t now, t;
+
+       max = log->max;
+       now = ktime_get();
+       local_irq_save(flags);
+       n = (log->next - 1) & (max - 1);
+       entry = log->entry;
+       while (n != log->next) {
+               t = ktime_sub(now, entry[n].when);
+               id = entry[n].id;
+               if (id) {
+                       const char *str;
+                       if (id < log->num_strings)
+                               str = log->strings[id];
+                       else
+                               str = "UNKNOWN";
+                       size += scnprintf(ev_buf + size, 4096 - size,
+                                         "%8d.%03d %08x %s\n",
+                                         t.tv.sec, t.tv.nsec / 1000000,
+                                         entry[n].arg, str);
+               }
+               n = (n - 1) & (max - 1);
+       }
+       log->fault = 0;
+       local_irq_restore(flags);
+       return simple_read_from_buffer(buf, count, ppos, ev_buf, size);
+}
+
+static void ev_log_write(struct ev_log *log, unsigned id, unsigned arg)
+{
+       struct ev_entry *entry;
+       unsigned long flags;
+       local_irq_save(flags);
+
+       if (log->fault) {
+               if (log->fault == 1)
+                       goto done;
+               log->fault--;
+       }
+
+       entry = log->entry + log->next;
+       entry->when = ktime_get();
+       entry->id = id;
+       entry->arg = arg;
+       log->next = (log->next + 1) & (log->max - 1);
+done:
+       local_irq_restore(flags);
+}
+
+static void ev_log_freeze(struct ev_log *log, unsigned count)
+{
+       unsigned long flags;
+       local_irq_save(flags);
+       log->fault = count;
+       local_irq_restore(flags);
+}
+
+static int ev_log_open(struct inode *inode, struct file *file)
+{
+       file->private_data = inode->i_private;
+       return 0;
+}
+
+static const struct file_operations ev_log_ops = {
+       .read = ev_log_read,
+       .open = ev_log_open,
+};
+
+static int ev_log_init(struct ev_log *log)
+{
+       debugfs_create_file(log->name, 0444, 0, log, &ev_log_ops);
+       return 0;
+}
+
diff --git a/drivers/staging/dream/qdsp5/snd.c b/drivers/staging/dream/qdsp5/snd.c
new file mode 100644 (file)
index 0000000..037d7ff
--- /dev/null
@@ -0,0 +1,279 @@
+/* arch/arm/mach-msm/qdsp5/snd.c
+ *
+ * interface to "snd" service on the baseband cpu
+ *
+ * Copyright (C) 2008 HTC Corporation
+ *
+ * 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/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/delay.h>
+#include <linux/msm_audio.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/board.h>
+#include <mach/msm_rpcrouter.h>
+
+struct snd_ctxt {
+       struct mutex lock;
+       int opened;
+       struct msm_rpc_endpoint *ept;
+       struct msm_snd_endpoints *snd_epts;
+};
+
+static struct snd_ctxt the_snd;
+
+#define RPC_SND_PROG    0x30000002
+#define RPC_SND_CB_PROG 0x31000002
+#if CONFIG_MSM_AMSS_VERSION == 6210
+#define RPC_SND_VERS   0x94756085 /* 2490720389 */
+#elif (CONFIG_MSM_AMSS_VERSION == 6220) || \
+      (CONFIG_MSM_AMSS_VERSION == 6225)
+#define RPC_SND_VERS   0xaa2b1a44 /* 2854951492 */
+#elif CONFIG_MSM_AMSS_VERSION == 6350
+#define RPC_SND_VERS   MSM_RPC_VERS(1,0)
+#endif
+
+#define SND_SET_DEVICE_PROC 2
+#define SND_SET_VOLUME_PROC 3
+
+struct rpc_snd_set_device_args {
+       uint32_t device;
+       uint32_t ear_mute;
+       uint32_t mic_mute;
+
+       uint32_t cb_func;
+       uint32_t client_data;
+};
+
+struct rpc_snd_set_volume_args {
+       uint32_t device;
+       uint32_t method;
+       uint32_t volume;
+
+       uint32_t cb_func;
+       uint32_t client_data;
+};
+
+struct snd_set_device_msg {
+       struct rpc_request_hdr hdr;
+       struct rpc_snd_set_device_args args;
+};
+
+struct snd_set_volume_msg {
+       struct rpc_request_hdr hdr;
+       struct rpc_snd_set_volume_args args;
+};
+
+struct snd_endpoint *get_snd_endpoints(int *size);
+
+static inline int check_mute(int mute)
+{
+       return (mute == SND_MUTE_MUTED ||
+               mute == SND_MUTE_UNMUTED) ? 0 : -EINVAL;
+}
+
+static int get_endpoint(struct snd_ctxt *snd, unsigned long arg)
+{
+       int rc = 0, index;
+       struct msm_snd_endpoint ept;
+
+       if (copy_from_user(&ept, (void __user *)arg, sizeof(ept))) {
+               pr_err("snd_ioctl get endpoint: invalid read pointer.\n");
+               return -EFAULT;
+       }
+
+       index = ept.id;
+       if (index < 0 || index >= snd->snd_epts->num) {
+               pr_err("snd_ioctl get endpoint: invalid index!\n");
+               return -EINVAL;
+       }
+
+       ept.id = snd->snd_epts->endpoints[index].id;
+       strncpy(ept.name,
+               snd->snd_epts->endpoints[index].name,
+               sizeof(ept.name));
+
+       if (copy_to_user((void __user *)arg, &ept, sizeof(ept))) {
+               pr_err("snd_ioctl get endpoint: invalid write pointer.\n");
+               rc = -EFAULT;
+       }
+
+       return rc;
+}
+
+static long snd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+       struct snd_set_device_msg dmsg;
+       struct snd_set_volume_msg vmsg;
+       struct msm_snd_device_config dev;
+       struct msm_snd_volume_config vol;
+       struct snd_ctxt *snd = file->private_data;
+       int rc = 0;
+
+       mutex_lock(&snd->lock);
+       switch (cmd) {
+       case SND_SET_DEVICE:
+               if (copy_from_user(&dev, (void __user *) arg, sizeof(dev))) {
+                       pr_err("snd_ioctl set device: invalid pointer.\n");
+                       rc = -EFAULT;
+                       break;
+               }
+
+               dmsg.args.device = cpu_to_be32(dev.device);
+               dmsg.args.ear_mute = cpu_to_be32(dev.ear_mute);
+               dmsg.args.mic_mute = cpu_to_be32(dev.mic_mute);
+               if (check_mute(dev.ear_mute) < 0 ||
+                               check_mute(dev.mic_mute) < 0) {
+                       pr_err("snd_ioctl set device: invalid mute status.\n");
+                       rc = -EINVAL;
+                       break;
+               }
+               dmsg.args.cb_func = -1;
+               dmsg.args.client_data = 0;
+
+               pr_info("snd_set_device %d %d %d\n", dev.device,
+                                                dev.ear_mute, dev.mic_mute);
+
+               rc = msm_rpc_call(snd->ept,
+                       SND_SET_DEVICE_PROC,
+                       &dmsg, sizeof(dmsg), 5 * HZ);
+               break;
+
+       case SND_SET_VOLUME:
+               if (copy_from_user(&vol, (void __user *) arg, sizeof(vol))) {
+                       pr_err("snd_ioctl set volume: invalid pointer.\n");
+                       rc = -EFAULT;
+                       break;
+               }
+
+               vmsg.args.device = cpu_to_be32(vol.device);
+               vmsg.args.method = cpu_to_be32(vol.method);
+               if (vol.method != SND_METHOD_VOICE) {
+                       pr_err("snd_ioctl set volume: invalid method.\n");
+                       rc = -EINVAL;
+                       break;
+               }
+
+               vmsg.args.volume = cpu_to_be32(vol.volume);
+               vmsg.args.cb_func = -1;
+               vmsg.args.client_data = 0;
+
+               pr_info("snd_set_volume %d %d %d\n", vol.device,
+                                               vol.method, vol.volume);
+
+               rc = msm_rpc_call(snd->ept,
+                       SND_SET_VOLUME_PROC,
+                       &vmsg, sizeof(vmsg), 5 * HZ);
+               break;
+
+       case SND_GET_NUM_ENDPOINTS:
+               if (copy_to_user((void __user *)arg,
+                               &snd->snd_epts->num, sizeof(unsigned))) {
+                       pr_err("snd_ioctl get endpoint: invalid pointer.\n");
+                       rc = -EFAULT;
+               }
+               break;
+
+       case SND_GET_ENDPOINT:
+               rc = get_endpoint(snd, arg);
+               break;
+
+       default:
+               pr_err("snd_ioctl unknown command.\n");
+               rc = -EINVAL;
+               break;
+       }
+       mutex_unlock(&snd->lock);
+
+       return rc;
+}
+
+static int snd_release(struct inode *inode, struct file *file)
+{
+       struct snd_ctxt *snd = file->private_data;
+
+       mutex_lock(&snd->lock);
+       snd->opened = 0;
+       mutex_unlock(&snd->lock);
+       return 0;
+}
+
+static int snd_open(struct inode *inode, struct file *file)
+{
+       struct snd_ctxt *snd = &the_snd;
+       int rc = 0;
+
+       mutex_lock(&snd->lock);
+       if (snd->opened == 0) {
+               if (snd->ept == NULL) {
+                       snd->ept = msm_rpc_connect(RPC_SND_PROG, RPC_SND_VERS,
+                                       MSM_RPC_UNINTERRUPTIBLE);
+                       if (IS_ERR(snd->ept)) {
+                               rc = PTR_ERR(snd->ept);
+                               snd->ept = NULL;
+                               pr_err("snd: failed to connect snd svc\n");
+                               goto err;
+                       }
+               }
+               file->private_data = snd;
+               snd->opened = 1;
+       } else {
+               pr_err("snd already opened.\n");
+               rc = -EBUSY;
+       }
+
+err:
+       mutex_unlock(&snd->lock);
+       return rc;
+}
+
+static struct file_operations snd_fops = {
+       .owner          = THIS_MODULE,
+       .open           = snd_open,
+       .release        = snd_release,
+       .unlocked_ioctl = snd_ioctl,
+};
+
+struct miscdevice snd_misc = {
+       .minor  = MISC_DYNAMIC_MINOR,
+       .name   = "msm_snd",
+       .fops   = &snd_fops,
+};
+
+static int snd_probe(struct platform_device *pdev)
+{
+       struct snd_ctxt *snd = &the_snd;
+       mutex_init(&snd->lock);
+       snd->snd_epts = (struct msm_snd_endpoints *)pdev->dev.platform_data;
+       return misc_register(&snd_misc);
+}
+
+static struct platform_driver snd_plat_driver = {
+       .probe = snd_probe,
+       .driver = {
+               .name = "msm_snd",
+               .owner = THIS_MODULE,
+       },
+};
+
+static int __init snd_init(void)
+{
+       return platform_driver_register(&snd_plat_driver);
+}
+
+module_init(snd_init);