Initial Contribution

msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142

Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/arch/arm/mach-msm/qdsp5/Makefile b/arch/arm/mach-msm/qdsp5/Makefile
new file mode 100644
index 0000000..d8e7bf3
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/Makefile
@@ -0,0 +1,17 @@
+obj-y += adsp.o adsp_driver.o adsp_info.o adsp_rm.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
+ifdef CONFIG_MSM7X27A_AUDIO
+obj-y += audio_pcm_in.o
+else
+obj-y += audio_in.o
+endif
+obj-$(CONFIG_MSM7X27A_AUDIO) += audio_evrc_in.o audio_qcelp_in.o
+obj-y += audio_out.o audio_mp3.o audmgr.o audpp.o audrec.o audpreproc.o
+obj-y += audio_evrc.o audio_qcelp.o audio_amrnb.o audio_aac.o audio_amrnb_in.o
+obj-y += audio_wma.o audio_voicememo.o audio_pcm.o audio_amrwb.o audio_wmapro.o
+obj-y += snd.o snd_adie.o
+obj-$(CONFIG_ARCH_MSM7X27A) += audio_fm.o
diff --git a/arch/arm/mach-msm/qdsp5/adsp.c b/arch/arm/mach-msm/qdsp5/adsp.c
new file mode 100644
index 0000000..d392cce
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/adsp.c
@@ -0,0 +1,1397 @@
+/* arch/arm/mach-msm/qdsp5/adsp.c
+ *
+ * Register/Interrupt access for userspace aDSP library.
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved.
+ * 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>
+#include <linux/slab.h>
+#include <mach/debug_mm.h>
+#include <linux/debugfs.h>
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *dentry_adsp;
+static struct dentry *dentry_wdata;
+static struct dentry *dentry_rdata;
+static int wdump, rdump;
+#endif /* CONFIG_DEBUG_FS */
+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 <mach/clk.h>
+#include <mach/msm_adsp.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 uint32_t rpc_adsp_rtos_atom_prog;
+static uint32_t rpc_adsp_rtos_atom_vers;
+static uint32_t rpc_adsp_rtos_atom_vers_comp;
+static uint32_t rpc_adsp_rtos_mtoa_prog;
+static uint32_t rpc_adsp_rtos_mtoa_vers;
+static uint32_t rpc_adsp_rtos_mtoa_vers_comp;
+static DEFINE_MUTEX(adsp_open_lock);
+
+/* protect interactions with the ADSP command/message queue */
+static spinlock_t adsp_cmd_lock;
+static spinlock_t adsp_write_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`.
+ */
+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;
+}
+
+static int32_t adsp_validate_queue(uint32_t mod_id, unsigned q_idx,
+							uint32_t size)
+{
+	int32_t i;
+	struct adsp_rtos_mp_mtoa_init_info_type	*sptr;
+
+	sptr = adsp_info.init_info_ptr;
+	for (i = 0; i < sptr->mod_to_q_entries; i++)
+		if (mod_id == sptr->mod_to_q_tbl[i].module)
+			if (q_idx == sptr->mod_to_q_tbl[i].q_type) {
+				if (size <= sptr->mod_to_q_tbl[i].q_max_len)
+					return 0;
+				MM_ERR("q_idx: %d is not a valid queue \
+					for module %x\n", q_idx, mod_id);
+				return -EINVAL;
+			}
+	MM_ERR("cmd_buf size is more than allowed size\n");
+	return -EINVAL;
+}
+
+uint32_t adsp_get_module(struct adsp_info *info, uint32_t task)
+{
+	return info->task_to_module[current_image][task];
+}
+
+uint32_t adsp_get_queue_offset(struct adsp_info *info, uint32_t queue_id)
+{
+	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;
+
+	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_call_reply(adsp_module->rpc_client,
+					RPC_ADSP_RTOS_APP_TO_MODEM_PROC,
+					&rpc_req, sizeof(rpc_req),
+					&rpc_rsp, sizeof(rpc_rsp),
+					5 * HZ);
+
+	if (rc < 0) {
+		MM_ERR("error receiving RPC reply: %d (%d)\n",
+				rc, -ERESTARTSYS);
+		return rc;
+	}
+
+	if (be32_to_cpu(rpc_rsp.reply_stat) != RPCMSG_REPLYSTAT_ACCEPTED) {
+		MM_ERR("RPC call was denied!\n");
+		return -EPERM;
+	}
+
+	if (be32_to_cpu(rpc_rsp.data.acc_hdr.accept_stat) !=
+	    RPC_ACCEPTSTAT_SUCCESS) {
+		MM_ERR("RPC call was not successful (%d)\n",
+				be32_to_cpu(rpc_rsp.data.acc_hdr.accept_stat));
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+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;
+}
+
+static struct msm_adsp_module *find_adsp_module_by_id(
+	struct adsp_info *info, uint32_t id)
+{
+	int mod_idx;
+
+	if (id > info->max_module_id) {
+		return NULL;
+	} else {
+		mod_idx = get_module_index(id);
+		if (mod_idx < 0)
+			return NULL;
+		return info->id_to_module[mod_idx];
+	}
+}
+
+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))
+		adsp_module->rpc_client = msm_rpc_connect_compatible(
+		rpc_adsp_rtos_atom_prog,
+		rpc_adsp_rtos_atom_vers_comp,
+		MSM_RPC_UNINTERRUPTIBLE);
+
+	if (IS_ERR(adsp_module->rpc_client)) {
+		int rc = PTR_ERR(adsp_module->rpc_client);
+		adsp_module->rpc_client = 0;
+		MM_ERR("could not open rpc client: %d\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+/*
+ * 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;
+	struct rpc_reply_hdr rpc_rsp;
+
+	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)) {
+		adsp_info.init_info_rpc_client = msm_rpc_connect_compatible(
+		rpc_adsp_rtos_atom_prog,
+		rpc_adsp_rtos_atom_vers_comp,
+		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;
+			MM_ERR("could not open rpc client: %d\n", rc);
+			return;
+		}
+	}
+
+	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_call_reply(adsp_info.init_info_rpc_client,
+					RPC_ADSP_RTOS_APP_TO_MODEM_PROC,
+					&rpc_req, sizeof(rpc_req),
+					&rpc_rsp, sizeof(rpc_rsp),
+					5 * HZ);
+
+	if (rc < 0)
+		MM_ERR("could not send RPC request: %d\n", rc);
+}
+
+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;
+	static uint32_t init_info_cmd_sent;
+
+	if (!init_info_cmd_sent) {
+		init_waitqueue_head(&adsp_info.init_info_wait);
+		msm_get_init_info();
+		rc = wait_event_timeout(adsp_info.init_info_wait,
+			adsp_info.init_info_state == ADSP_STATE_INIT_INFO,
+			5 * HZ);
+		if (!rc) {
+			MM_ERR("INIT_INFO failed\n");
+			return -ETIMEDOUT;
+		}
+		init_info_cmd_sent++;
+	}
+
+	module = find_adsp_module_by_name(&adsp_info, name);
+	if (!module)
+		return -ENODEV;
+
+	mutex_lock(&module->lock);
+	MM_INFO("opening module %s\n", module->name);
+
+	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;
+		MM_ERR("REGISTER_APP failed\n");
+		goto done;
+	}
+
+	MM_DBG("module %s has been registered\n", module->name);
+
+done:
+	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->ops) {
+		MM_INFO("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) {
+			MM_INFO("disabling module %s\n", module->name);
+			msm_adsp_disable_locked(module);
+		}
+
+		msm_rpc_close(module->rpc_client);
+		module->rpc_client = 0;
+	} else {
+		MM_INFO("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)
+		MM_ERR("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;
+
+	if (!module || !cmd_buf) {
+		MM_ERR("Called with NULL parameters\n");
+		return -EINVAL;
+	}
+	info = module->info;
+	spin_lock_irqsave(&adsp_write_lock, flags);
+
+	if (module->state != ADSP_STATE_ENABLED) {
+		spin_unlock_irqrestore(&adsp_write_lock, flags);
+		MM_ERR("module %s not enabled before write\n", module->name);
+		return -ENODEV;
+	}
+	if (adsp_validate_module(module->id)) {
+		spin_unlock_irqrestore(&adsp_write_lock, flags);
+		MM_ERR("module id validation failed %s  %d\n",
+				module->name, module->id);
+		return -ENXIO;
+	}
+	if (dsp_queue_addr >= QDSP_MAX_NUM_QUEUES) {
+		spin_unlock_irqrestore(&adsp_write_lock, flags);
+		MM_ERR("Invalid Queue Index: %d\n", dsp_queue_addr);
+		return -ENXIO;
+	}
+	if (adsp_validate_queue(module->id, dsp_queue_addr, cmd_size)) {
+		spin_unlock_irqrestore(&adsp_write_lock, flags);
+		return -EINVAL;
+	}
+	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 > 50) {
+			MM_ERR("timeout waiting for DSP write ready\n");
+			ret_status = -EIO;
+			goto fail;
+		}
+		MM_DBG("waiting for DSP write ready\n");
+		udelay(2);
+		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 > 2500) {
+			MM_ERR("timeout waiting for adsp ack\n");
+			ret_status = -EIO;
+			goto fail;
+		}
+		udelay(2);
+		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;
+	} else {
+		/* No error */
+		/* 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++;
+	} /* Ctrl word status bits were 00, no error in the ctrl word */
+
+fail:
+	spin_unlock_irqrestore(&adsp_write_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;
+#ifdef CONFIG_DEBUG_FS
+	uint16_t *ptr;
+	int ii;
+
+	if (wdump > 0) {
+		ptr = cmd_buf;
+		pr_info("A->D:%x\n", module->id);
+		pr_info("adsp: %x %d\n", dsp_queue_addr, cmd_size);
+		for (ii = 0; ii < cmd_size/2; ii++)
+			pr_info("%x ", ptr[ii]);
+		pr_info("\n");
+	}
+#endif /* CONFIG_DEBUG_FS */
+	do {
+		rc = __msm_adsp_write(module, dsp_queue_addr, cmd_buf,
+								cmd_size);
+		if (rc == -EAGAIN)
+			udelay(10);
+	} while (rc == -EAGAIN && retries++ < 300);
+	if (retries > 50)
+		MM_ERR("adsp: %s command took %d attempts: rc %d\n",
+			module->name, retries, rc);
+	return rc;
+}
+
+static void *event_addr;
+static void read_event(void *buf, size_t len)
+{
+	uint32_t dptr[3];
+	struct rpc_adsp_rtos_modem_to_app_args_t *sptr;
+	struct adsp_rtos_mp_mtoa_type	*pkt_ptr;
+
+	sptr = 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);
+
+	if (len > EVENT_LEN)
+		len = EVENT_LEN;
+
+	memcpy(buf, dptr, len);
+}
+
+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;
+	struct adsp_rtos_mp_mtoa_type	*pkt_ptr;
+	struct queue_to_offset_type	*qptr;
+	struct queue_to_offset_type	*qtbl;
+	struct mod_to_queue_offsets	*mqptr;
+	struct mod_to_queue_offsets	*mqtbl;
+	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;
+
+	event = be32_to_cpu(args->mtoa_pkt.mp_mtoa_header.event);
+	proc_id = be32_to_cpu(args->mtoa_pkt.mp_mtoa_header.proc_id);
+
+	if (event == RPC_ADSP_RTOS_INIT_INFO) {
+		MM_INFO("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);
+		if (iptr->image_count > IMG_MAX)
+			iptr->image_count = IMG_MAX;
+		iptr->num_queue_offsets = be32_to_cpu(sptr->num_queue_offsets);
+		num_entries = iptr->num_queue_offsets;
+		if (num_entries > ENTRIES_MAX) {
+			num_entries = ENTRIES_MAX;
+			iptr->num_queue_offsets = ENTRIES_MAX;
+		}
+		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);
+		if (num_entries > ENTRIES_MAX)
+			num_entries = ENTRIES_MAX;
+		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);
+#if CONFIG_ADSP_RPC_VER > 0x30001
+		if (iptr->module_table_size > MODULES_MAX)
+			iptr->module_table_size = MODULES_MAX;
+#else
+		if (iptr->module_table_size > ENTRIES_MAX)
+			iptr->module_table_size = ENTRIES_MAX;
+#endif
+		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]);
+
+		mqptr = &sptr->mod_to_q_tbl[0];
+		mqtbl = &iptr->mod_to_q_tbl[0];
+		iptr->mod_to_q_entries = be32_to_cpu(sptr->mod_to_q_entries);
+		if (iptr->mod_to_q_entries > ENTRIES_MAX)
+			iptr->mod_to_q_entries = ENTRIES_MAX;
+		for (e_idx = 0; e_idx < iptr->mod_to_q_entries; e_idx++) {
+			mqtbl[e_idx].module = be32_to_cpu(mqptr->module);
+			mqtbl[e_idx].q_type = be32_to_cpu(mqptr->q_type);
+			mqtbl[e_idx].q_max_len = be32_to_cpu(mqptr->q_max_len);
+			mqptr++;
+		}
+
+		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;
+	}
+
+	pkt_ptr = &args->mtoa_pkt.adsp_rtos_mp_mtoa_data.mp_mtoa_packet;
+	module_id = be32_to_cpu(pkt_ptr->module);
+	image     = be32_to_cpu(pkt_ptr->image);
+
+	MM_DBG("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) {
+		MM_ERR("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:
+		MM_INFO("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:
+		MM_INFO("module %s: DISABLED\n", module->name);
+		module->state = ADSP_STATE_DISABLED;
+		wake_up(&module->state_wait);
+		break;
+	case RPC_ADSP_RTOS_SERVICE_RESET:
+		MM_INFO("module %s: SERVICE_RESET\n", module->name);
+		module->state = ADSP_STATE_DISABLED;
+		wake_up(&module->state_wait);
+		break;
+	case RPC_ADSP_RTOS_CMD_SUCCESS:
+		MM_INFO("module %s: CMD_SUCCESS\n", module->name);
+		break;
+	case RPC_ADSP_RTOS_CMD_FAIL:
+		MM_INFO("module %s: CMD_FAIL\n", module->name);
+		break;
+	case RPC_ADSP_RTOS_DISABLE_FAIL:
+		MM_INFO("module %s: DISABLE_FAIL\n", module->name);
+		break;
+	default:
+		MM_ERR("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);
+#ifdef CONFIG_MSM_ADSP_REPORT_EVENTS
+	event_addr = (uint32_t *)req;
+	module->ops->event(module->driver_data,
+				EVENT_MSG_ID,
+				EVENT_LEN,
+				read_event);
+#endif
+	mutex_unlock(&module->lock);
+}
+
+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;
+#if CONFIG_ADSP_RPC_VER > 0x30001
+	case RPC_ADSP_RTOS_MTOA_INIT_INFO_PROC:
+	case RPC_ADSP_RTOS_MTOA_EVENT_INFO_PROC:
+#else
+	case RPC_ADSP_RTOS_MODEM_TO_APP_PROC:
+#endif
+		handle_adsp_rtos_mtoa_app(req);
+		break;
+	default:
+		MM_ERR("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, exit = 0;
+
+	do {
+		rc = msm_rpc_read(rpc_cb_server_client, &buffer, -1, -1);
+		if (rc < 0) {
+			MM_ERR("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 (!msm_rpc_is_compatible_version(rpc_adsp_rtos_mtoa_vers,
+							req->vers))
+			goto bad_rpc;
+
+		handle_adsp_rtos_mtoa(req);
+		kfree(buffer);
+		continue;
+
+bad_rpc:
+		MM_ERR("bogus rpc from modem\n");
+		kfree(buffer);
+	} while (!exit);
+	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;
+#ifdef CONFIG_DEBUG_FS
+	uint16_t *ptr;
+	int ii;
+#endif /* CONFIG_DEBUG_FS */
+	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) {
+		MM_ERR("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) {
+		MM_ERR("no module for task id %d\n", rtos_task_id);
+		return 0;
+	}
+
+	module->num_events++;
+
+	if (!module->ops) {
+		MM_ERR("module %s is not open\n", module->name);
+		return 0;
+	}
+#ifdef CONFIG_DEBUG_FS
+	if (rdump > 0) {
+		ptr = read_event_addr;
+		pr_info("D->A\n");
+		pr_info("m_id = %x id = %x\n", module->id, msg_id);
+		for (ii = 0; ii < msg_length/2; ii++)
+			pr_info("%x ", ptr[ii]);
+		pr_info("\n");
+	}
+#endif /* CONFIG_DEBUG_FS */
+
+	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 < 50; 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(2);
+	}
+	MM_ERR("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) {
+		MM_ERR("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 < 15; 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 == 15)
+		MM_ERR("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_min_rate(module->clk, clk_rate);
+
+	return -EINVAL;
+}
+
+int msm_adsp_enable(struct msm_adsp_module *module)
+{
+	int rc = 0;
+
+	MM_INFO("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 {
+			MM_ERR("module '%s' enable timed out\n", module->name);
+			rc = -ETIMEDOUT;
+		}
+		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);
+		break;
+	case ADSP_STATE_ENABLING:
+		MM_DBG("module '%s' enable in progress\n", module->name);
+		break;
+	case ADSP_STATE_ENABLED:
+		MM_DBG("module '%s' already enabled\n", module->name);
+		break;
+	case ADSP_STATE_DISABLING:
+		MM_ERR("module '%s' disable in progress\n", module->name);
+		rc = -EBUSY;
+		break;
+	}
+	mutex_unlock(&module->lock);
+	return rc;
+}
+EXPORT_SYMBOL(msm_adsp_enable);
+
+int msm_adsp_disable_event_rsp(struct msm_adsp_module *module)
+{
+	int rc = 0;
+
+	mutex_lock(&module->lock);
+
+	rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_DISABLE_EVENT_RSP,
+							module->id, module);
+	mutex_unlock(&module->lock);
+
+	return rc;
+}
+EXPORT_SYMBOL(msm_adsp_disable_event_rsp);
+
+static int msm_adsp_disable_locked(struct msm_adsp_module *module)
+{
+	int rc = 0;
+
+	switch (module->state) {
+	case ADSP_STATE_DISABLED:
+		MM_DBG("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;
+		if (--module->open_count == 0 && module->clk)
+			clk_disable(module->clk);
+		mutex_lock(&adsp_open_lock);
+		if (--adsp_open_count == 0) {
+			disable_irq(INT_ADSP);
+			allow_suspend();
+			MM_DBG("disable interrupt\n");
+		}
+		mutex_unlock(&adsp_open_lock);
+	}
+	return rc;
+}
+
+int msm_adsp_disable(struct msm_adsp_module *module)
+{
+	int rc;
+	MM_INFO("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;
+
+	if (pdev->id != (rpc_adsp_rtos_atom_vers & RPC_VERSION_MAJOR_MASK))
+		return -EINVAL;
+
+	wake_lock_init(&adsp_wake_lock, WAKE_LOCK_SUSPEND, "adsp");
+	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;
+
+	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;
+
+	adsp_modules = kzalloc(
+		(sizeof(struct msm_adsp_module) + sizeof(void *)) *
+		count, GFP_KERNEL);
+	if (!adsp_modules)
+		return -ENOMEM;
+
+	adsp_info.id_to_module = (void *) (adsp_modules + count);
+
+	spin_lock_init(&adsp_cmd_lock);
+	spin_lock_init(&adsp_write_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);
+		MM_ERR("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) {
+		MM_ERR("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_min_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;
+		adsp_info.id_to_module[i] = mod;
+		platform_device_register(&mod->pdev);
+	}
+
+	msm_adsp_publish_cdevs(adsp_modules, count);
+	rmtask_init();
+
+	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);
+	kfree(adsp_info.init_info_ptr);
+	return rc;
+}
+#ifdef CONFIG_DEBUG_FS
+static int get_parameters(char *buf, long int *param1, int num_of_par)
+{
+	char *token;
+	int base, cnt;
+
+	token = strsep(&buf, " ");
+
+	for (cnt = 0; cnt < num_of_par; cnt++) {
+		if (token != NULL) {
+			if ((token[1] == 'x') || (token[1] == 'X'))
+				base = 16;
+			else
+				base = 10;
+
+			if (strict_strtoul(token, base, &param1[cnt]) != 0)
+				return -EINVAL;
+
+			token = strsep(&buf, " ");
+			}
+		else
+			return -EINVAL;
+	}
+	return 0;
+}
+
+
+static ssize_t adsp_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	pr_debug("adsp debugfs opened\n");
+	return 0;
+}
+static ssize_t adsp_debug_write(struct file *file, const char __user *buf,
+				size_t cnt, loff_t *ppos)
+{
+	char *access_str = file->private_data;
+	char lbuf[32];
+	int rc;
+	long int param[5];
+
+	if (cnt > sizeof(lbuf) - 1)
+		return -EINVAL;
+	rc = copy_from_user(lbuf, buf, cnt);
+	if (rc) {
+		pr_info("Unable to copy data from user space\n");
+		return -EFAULT;
+	}
+	lbuf[cnt] = '\0';
+
+	if (!strcmp(access_str, "write_log")) {
+		if (get_parameters(lbuf, param, 1) == 0) {
+			switch (param[0]) {
+			case 1:
+				if (wdump <= 0)
+					wdump = 1;
+				pr_debug("write cmd to DSP(A->D) dump \
+					 started:%d\n", wdump);
+				break;
+			case 0:
+				if (wdump > 0)
+					wdump = 0;
+				pr_debug("Stop write cmd to \
+					 DSP(A->D):%d\n", wdump);
+				break;
+			default:
+				rc = -EINVAL;
+				break;
+			}
+		} else
+			rc = -EINVAL;
+	} else if (!strcmp(access_str, "read_log")) {
+		if (get_parameters(lbuf, param, 1) == 0) {
+			switch (param[0]) {
+			case 1:
+				if (rdump <= 0)
+					rdump = 1;
+				pr_debug("write cmd from DSP(D->A) dump \
+					started:%d\n", wdump);
+				break;
+			case 0:
+				if (rdump > 0)
+					rdump = 0;
+				pr_debug("Stop write cmd from \
+					DSP(D->A):%d\n", wdump);
+				break;
+			default:
+				rc = -EINVAL;
+				break;
+			}
+		} else
+			rc = -EINVAL;
+	} else {
+		rc = -EINVAL;
+	}
+	if (rc == 0)
+		rc = cnt;
+	else {
+		pr_err("%s: rc = %d\n", __func__, rc);
+		pr_info("\nWrong command: Use =>\n");
+		pr_info("-------------------------\n");
+		pr_info("To Start A->D:: echo \"1\">/sys/kernel/debug/ \
+			adsp_cmd/write_log\n");
+		pr_info("To Start D->A:: echo \"1\">/sys/kernel/debug/ \
+			adsp_cmd/read_log\n");
+		pr_info("To Stop  A->D:: echo \"0\">/sys/kernel/debug/ \
+			adsp_cmd/write_log\n");
+		pr_info("To Stop  D->A:: echo \"0\">/sys/kernel/debug/ \
+			adsp_cmd/read_log\n");
+		pr_info("------------------------\n");
+	}
+
+	return rc;
+}
+#endif
+
+static struct platform_driver msm_adsp_driver = {
+	.probe = msm_adsp_probe,
+	.driver = {
+		.owner = THIS_MODULE,
+	},
+};
+
+static char msm_adsp_driver_name[] = "rs00000000";
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations adsp_debug_fops = {
+	.write = adsp_debug_write,
+	.open = adsp_debug_open,
+};
+#endif
+
+static int __init adsp_init(void)
+{
+	int rc;
+
+#ifdef CONFIG_DEBUG_FS
+	dentry_adsp    = debugfs_create_dir("adsp_cmd", 0);
+	if (!IS_ERR(dentry_adsp)) {
+		dentry_wdata   = debugfs_create_file("write_log", \
+		 S_IFREG | S_IRUGO, dentry_adsp,
+		 (void *) "write_log" , &adsp_debug_fops);
+		dentry_rdata   = debugfs_create_file("read_log", \
+		 S_IFREG | S_IRUGO, dentry_adsp,
+		 (void *) "read_log", &adsp_debug_fops);
+	}
+	rdump = 0;
+	wdump = 0;
+#endif /* CONFIG_DEBUG_FS */
+
+	rpc_adsp_rtos_atom_prog = 0x3000000a;
+	rpc_adsp_rtos_atom_vers = 0x10001;
+	rpc_adsp_rtos_atom_vers_comp = 0x00010001;
+	rpc_adsp_rtos_mtoa_prog = 0x3000000b;
+#if CONFIG_ADSP_RPC_VER > 0x30001
+	rpc_adsp_rtos_mtoa_vers = 0x30002;
+	rpc_adsp_rtos_mtoa_vers_comp = 0x00030002;
+#else
+	rpc_adsp_rtos_mtoa_vers = 0x30001;
+	rpc_adsp_rtos_mtoa_vers_comp = 0x00030001;
+#endif
+
+	snprintf(msm_adsp_driver_name, sizeof(msm_adsp_driver_name),
+		"rs%08x",
+		rpc_adsp_rtos_atom_prog);
+	msm_adsp_driver.driver.name = msm_adsp_driver_name;
+	rc = platform_driver_register(&msm_adsp_driver);
+	MM_INFO("%s -- %d\n", msm_adsp_driver_name, rc);
+	return rc;
+}
+
+device_initcall(adsp_init);
diff --git a/arch/arm/mach-msm/qdsp5/adsp.h b/arch/arm/mach-msm/qdsp5/adsp.h
new file mode 100644
index 0000000..a15925a
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/adsp.h
@@ -0,0 +1,352 @@
+/* arch/arm/mach-msm/qdsp5/adsp.h
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
+ * 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,
+			   struct file **filp, unsigned long *offset);
+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;
+
+	/* 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;
+};
+
+#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
+#define RPC_ADSP_RTOS_MTOA_EVENT_INFO_PROC 3
+#define RPC_ADSP_RTOS_MTOA_INIT_INFO_PROC 4
+
+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,
+	RPC_ADSP_RTOS_CMD_REMOTE_INIT_INFO_EVENT,
+	RPC_ADSP_RTOS_CMD_GET_INIT_INFO,
+};
+
+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,
+	RPC_ADSP_RTOS_INIT_INFO,
+	RPC_ADSP_RTOS_DISABLE_FAIL,
+};
+
+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 */
+};
+
+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  */
+#if CONFIG_ADSP_RPC_VER > 0x30001
+#define IMG_MAX         2
+#define ENTRIES_MAX     36
+#define MODULES_MAX     64
+#else
+#define IMG_MAX         6
+#define ENTRIES_MAX     48
+#endif
+#define QUEUES_MAX      64
+
+struct queue_to_offset_type {
+	uint32_t	queue;
+	uint32_t	offset;
+};
+
+struct mod_to_queue_offsets {
+	uint32_t        module;
+	uint32_t        q_type;
+	uint32_t        q_max_len;
+};
+
+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;
+#if CONFIG_ADSP_RPC_VER > 0x30001
+	uint32_t	module_entries[MODULES_MAX];
+#else
+	uint32_t	module_entries[ENTRIES_MAX];
+#endif
+	uint32_t	mod_to_q_entries;
+	struct mod_to_queue_offsets	mod_to_q_tbl[ENTRIES_MAX];
+	/*
+	 * queue_offsets[] is to store only queue_offsets
+	 */
+	uint32_t	queue_offsets[IMG_MAX][QUEUES_MAX];
+};
+
+struct adsp_rtos_mp_mtoa_s_type {
+	struct adsp_rtos_mp_mtoa_header_type mp_mtoa_header;
+#if CONFIG_ADSP_RPC_VER == 0x30001
+	uint32_t desc_field;
+#endif
+	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;
+};
+
+#define ADSP_STATE_DISABLED   0
+#define ADSP_STATE_ENABLING   1
+#define ADSP_STATE_ENABLED    2
+#define ADSP_STATE_DISABLING  3
+#define ADSP_STATE_INIT_INFO  4
+
+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);
+extern void rmtask_init(void);
+
+/* Value to indicate that a queue is not defined for a particular image */
+#define QDSP_RTOS_NO_QUEUE  0xfffffffe
+
+/*
+ * 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
diff --git a/arch/arm/mach-msm/qdsp5/adsp_6210.c b/arch/arm/mach-msm/qdsp5/adsp_6210.c
new file mode 100644
index 0000000..322ba68
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/adsp_6210.c
@@ -0,0 +1,283 @@
+/* arch/arm/mach-msm/qdsp5/adsp_6210.h
+ *
+ * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * 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/arch/arm/mach-msm/qdsp5/adsp_6220.c b/arch/arm/mach-msm/qdsp5/adsp_6220.c
new file mode 100644
index 0000000..f947cd7
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/adsp_6220.c
@@ -0,0 +1,284 @@
+/* arch/arm/mach-msm/qdsp5/adsp_6220.h
+ *
+ * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * 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/arch/arm/mach-msm/qdsp5/adsp_6225.c b/arch/arm/mach-msm/qdsp5/adsp_6225.c
new file mode 100644
index 0000000..6f8d3f4
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/adsp_6225.c
@@ -0,0 +1,328 @@
+/* arch/arm/mach-msm/qdsp5/adsp_6225.h
+ *
+ * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * 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/arch/arm/mach-msm/qdsp5/adsp_driver.c b/arch/arm/mach-msm/qdsp5/adsp_driver.c
new file mode 100644
index 0000000..1cb1c9b
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/adsp_driver.c
@@ -0,0 +1,667 @@
+/* arch/arm/mach-msm/qdsp5/adsp_driver.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ * 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 <linux/slab.h>
+
+#include "adsp.h"
+
+#include <linux/msm_adsp.h>
+#include <linux/android_pmem.h>
+#include <mach/debug_mm.h>
+
+struct adsp_pmem_info {
+	int fd;
+	void *vaddr;
+};
+
+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)) {
+			MM_ERR("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) {
+		MM_ERR("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)
+				MM_ERR("%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 file **filp, unsigned long *offset)
+{
+	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) {
+		MM_ERR("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);
+	if (filp)
+		*filp = region->file;
+	if (offset)
+		*offset = 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) {
+		MM_ERR("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
+		MM_INFO("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)) {
+		MM_ERR("module %s: verify failed.\n", adev->module->name);
+		rc = -EINVAL;
+		goto end;
+	}
+	/* complete the writes to the buffer */
+	wmb();
+	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) {
+		MM_ERR("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;
+	}
+	/* order the reads to the buffer */
+	rmb();
+	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 int adsp_pmem_del(struct msm_adsp_module *module)
+{
+	struct hlist_node *node, *tmp;
+	struct adsp_pmem_region *region;
+
+	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));
+
+	return 0;
+}
+
+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 msm_adsp_disable_event_rsp(adev->module);
+
+	case ADSP_IOCTL_DISABLE_ACK:
+		MM_ERR("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: {
+		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);
+	}
+
+	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;
+
+	case ADSP_IOCTL_UNREGISTER_PMEM:
+		return adsp_pmem_del(adev->module);
+
+	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;
+	int rc = 0;
+
+	MM_INFO("release '%s'\n", adev->name);
+
+	/* clear module before putting it to avoid race with open() */
+	adev->module = NULL;
+
+	rc = adsp_pmem_del(module);
+
+	msm_adsp_put(module);
+	return rc;
+}
+
+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) {
+		MM_ERR("event too large (%d bytes)\n", len);
+		return;
+	}
+
+	event = kmalloc(sizeof(*event), GFP_ATOMIC);
+	if (!event) {
+		MM_ERR("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;
+
+	MM_INFO("open '%s'\n", adev->name);
+
+	rc = msm_adsp_get(adev->name, &adev->module, &adsp_ops, adev);
+	if (rc)
+		return rc;
+
+	MM_INFO("opened 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/arch/arm/mach-msm/qdsp5/adsp_info.c b/arch/arm/mach-msm/qdsp5/adsp_info.c
new file mode 100644
index 0000000..62e385d
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/adsp_info.c
@@ -0,0 +1,144 @@
+/* arch/arm/mach-msm/adsp_info.c
+ *
+ * Copyright (c) 2008-2009, 2011 Code Aurora Forum. All rights reserved.
+ *
+ * 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_VIDEO_AAC_VOC_TURBO     0x01089f77
+#define QDSP_MODULE_VIDEO_AMR_TURBO         0x01089f78
+#define QDSP_MODULE_WM_TURBO_MODE           0x01089f79
+#define QDSP_MODULE_VDEC_LP_MODE_TURBO      0x01089f7a
+#define QDSP_MODULE_AUDREC0TASK             0x0109696f
+#define QDSP_MODULE_AUDREC1TASK             0x01096970
+#define QDSP_MODULE_RMTASK                  0x01090f8e
+#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(AUDPLAY1TASK, NULL, 0, NULL, NULL),
+	QDSP_MODULE(AUDPLAY2TASK, NULL, 0, NULL, NULL),
+	QDSP_MODULE(AUDPLAY3TASK, NULL, 0, NULL, NULL),
+	QDSP_MODULE(AUDPPTASK, NULL, 0, NULL, NULL),
+	QDSP_MODULE(AUDPREPROCTASK, NULL, 0, NULL, NULL),
+	QDSP_MODULE(RMTASK, NULL, 0, NULL, NULL),
+#if !defined(CONFIG_ARCH_MSM7X30)
+	QDSP_MODULE(AUDRECTASK, 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),
+	QDSP_MODULE(VIDEO_AAC_VOC_TURBO, NULL, 0, NULL, NULL),
+	QDSP_MODULE(VIDEO_AMR_TURBO, NULL, 0, NULL, NULL),
+	QDSP_MODULE(WM_TURBO_MODE, NULL, 0, NULL, NULL),
+	QDSP_MODULE(VDEC_LP_MODE_TURBO, NULL, 0, NULL, NULL),
+#if defined(CONFIG_MSM7X27A_AUDIO)
+	QDSP_MODULE(AUDREC1TASK, NULL, 0, NULL, NULL),
+#endif
+#else
+	QDSP_MODULE(AFETASK , NULL, 0, NULL, NULL),
+	QDSP_MODULE(AUDREC0TASK, NULL, 0, NULL, NULL),
+	QDSP_MODULE(AUDREC1TASK, NULL, 0, NULL, NULL),
+#endif
+};
+
+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/arch/arm/mach-msm/qdsp5/adsp_jpeg_patch_event.c b/arch/arm/mach-msm/qdsp5/adsp_jpeg_patch_event.c
new file mode 100644
index 0000000..8fb2e06
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/adsp_jpeg_patch_event.c
@@ -0,0 +1,39 @@
+/* arch/arm/mach-msm/qdsp5/adsp_jpeg_patch_event.c
+ *
+ * Verification code for aDSP JPEG events.
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * 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);
+	}
+	if (event->msg_id == JPEG_MSG_DEC_OP_PRODUCED) {
+		jpeg_msg_dec_op_produced *op = (jpeg_msg_dec_op_produced *)
+			event->data.msg16;
+		return adsp_pmem_paddr_fixup(module,
+				(void **)&op->luma_op_buf_addr) ||
+			adsp_pmem_paddr_fixup(module,
+				(void **)&op->chroma_op_buf_addr);
+	}
+
+	return 0;
+}
diff --git a/arch/arm/mach-msm/qdsp5/adsp_jpeg_verify_cmd.c b/arch/arm/mach-msm/qdsp5/adsp_jpeg_verify_cmd.c
new file mode 100644
index 0000000..87d5dc3
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/adsp_jpeg_verify_cmd.c
@@ -0,0 +1,201 @@
+/* arch/arm/mach-msm/qdsp5/adsp_jpeg_verify_cmd.c
+ *
+ * Verification code for aDSP JPEG packets from userspace.
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * 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"
+#include <mach/debug_mm.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)) {
+		MM_ERR("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)) {
+		MM_ERR("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) {
+			MM_ERR("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)) {
+			MM_ERR("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_dec_op_consumed)) {
+			MM_ERR("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;
+
+	case JPEG_CMD_DEC_IP:
+	{
+		jpeg_cmd_dec_ip *cmd =
+			(jpeg_cmd_dec_ip *)cmd_data;
+
+		if (cmd_size != sizeof(jpeg_cmd_dec_ip)) {
+			MM_ERR("module %s: JPEG_CMD_DEC_IP invalid \
+				size %d\n", module->name, cmd_size);
+			return -1;
+		}
+		if (adsp_pmem_fixup(module, (void **)&cmd->ip_buf_addr,
+			cmd->ip_buf_size))
+			return -1;
+	}
+	break;
+
+	default:
+		if (cmd_id > 7) {
+			MM_ERR("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/arch/arm/mach-msm/qdsp5/adsp_lpm_verify_cmd.c b/arch/arm/mach-msm/qdsp5/adsp_lpm_verify_cmd.c
new file mode 100644
index 0000000..06b70de
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/adsp_lpm_verify_cmd.c
@@ -0,0 +1,66 @@
+/* arch/arm/mach-msm/qdsp5/adsp_lpm_verify_cmd.c
+ *
+ * Verificion code for aDSP LPM packets from userspace.
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * 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"
+#include <mach/debug_mm.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) {
+		MM_ERR("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)) {
+			MM_ERR("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) {
+		MM_ERR("module %s: invalid cmd_id %d\n", module->name, cmd_id);
+		return -1;
+	}
+	return 0;
+}
+
diff --git a/arch/arm/mach-msm/qdsp5/adsp_rm.c b/arch/arm/mach-msm/qdsp5/adsp_rm.c
new file mode 100644
index 0000000..678dd81
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/adsp_rm.c
@@ -0,0 +1,192 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#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 <linux/msm_adsp.h>
+
+#include <mach/qdsp5/qdsp5rmtcmdi.h>
+#include <mach/qdsp5/qdsp5rmtmsg.h>
+#include <mach/debug_mm.h>
+#include "adsp.h"
+
+#define MAX_CLIENTS		5
+#define MAX_AUDIO_CLIENTS	5
+#define MAX_RM_CLIENTS		MAX_AUDIO_CLIENTS
+
+static char *rm_errs[] = {
+			"",
+			"PCM Blocks not Sufficient",
+			"TASK is already occupied",
+			"Concurrency not supported",
+			"MIPS not sufficient"
+			};
+static struct client {
+	wait_queue_head_t		wait;
+	unsigned int			wait_state;
+	struct aud_codec_config_ack	cfg_msg;
+} rmclient[MAX_RM_CLIENTS];
+
+static struct rm {
+	struct msm_adsp_module		*mod;
+	int				cnt;
+	int				state;
+
+	struct aud_codec_config_ack	cfg_msg;
+	struct mutex			lock;
+} rmtask;
+
+static void rm_dsp_event(void *data, unsigned id, size_t len,
+			void (*getevent) (void *ptr, size_t len));
+static struct msm_adsp_ops rm_ops = {
+	.event = rm_dsp_event,
+};
+
+int32_t get_adsp_resource(unsigned short client_id,
+				void *cmd_buf, size_t cmd_size)
+{
+	int rc = 0;
+	int client_idx;
+
+	client_idx = ((client_id >> 8) * MAX_CLIENTS) + (client_id & 0xFF);
+	if (client_idx >= MAX_RM_CLIENTS)
+		return -EINVAL;
+
+	mutex_lock(&rmtask.lock);
+	if (rmtask.state != ADSP_STATE_ENABLED) {
+		rc = msm_adsp_get("RMTASK", &rmtask.mod, &rm_ops, NULL);
+		if (rc) {
+			MM_ERR("Failed to get module RMTASK\n");
+			mutex_unlock(&rmtask.lock);
+			return rc;
+		}
+		rc = msm_adsp_enable(rmtask.mod);
+		if (rc) {
+			MM_ERR("RMTASK enable Failed\n");
+			msm_adsp_put(rmtask.mod);
+			mutex_unlock(&rmtask.lock);
+			return rc;
+		}
+		rmtask.state = ADSP_STATE_ENABLED;
+	}
+	rmclient[client_idx].wait_state = -1;
+	mutex_unlock(&rmtask.lock);
+	msm_adsp_write(rmtask.mod, QDSP_apuRmtQueue, cmd_buf, cmd_size);
+	rc = wait_event_interruptible_timeout(rmclient[client_idx].wait,
+			rmclient[client_idx].wait_state != -1, 5 * HZ);
+	mutex_lock(&rmtask.lock);
+	if (unlikely(rc < 0)) {
+		if (rc == -ERESTARTSYS)
+			MM_ERR("wait_event_interruptible "
+					"returned -ERESTARTSYS\n");
+		else
+			MM_ERR("wait_event_interruptible "
+					"returned error\n");
+		if (!rmtask.cnt)
+			goto disable_rm;
+		goto unlock;
+	} else if (rc == 0) {
+		MM_ERR("RMTASK Msg not received\n");
+		rc = -ETIMEDOUT;
+		if (!rmtask.cnt)
+			goto disable_rm;
+		goto unlock;
+	}
+	if (!(rmclient[client_idx].cfg_msg.enable)) {
+		MM_ERR("Reason for failure: %s\n",
+			rm_errs[rmclient[client_idx].cfg_msg.reason]);
+		rc = -EBUSY;
+		if (!rmtask.cnt)
+			goto disable_rm;
+		goto unlock;
+	}
+	rmtask.cnt++;
+	mutex_unlock(&rmtask.lock);
+	return 0;
+
+disable_rm:
+	msm_adsp_disable(rmtask.mod);
+	msm_adsp_put(rmtask.mod);
+	rmtask.state = ADSP_STATE_DISABLED;
+unlock:
+	mutex_unlock(&rmtask.lock);
+	return rc;
+}
+EXPORT_SYMBOL(get_adsp_resource);
+
+int32_t put_adsp_resource(unsigned short client_id, void *cmd_buf,
+							size_t cmd_size)
+{
+	mutex_lock(&rmtask.lock);
+	if (rmtask.state != ADSP_STATE_ENABLED) {
+		mutex_unlock(&rmtask.lock);
+		return 0;
+	}
+
+	msm_adsp_write(rmtask.mod, QDSP_apuRmtQueue, cmd_buf, cmd_size);
+	rmtask.cnt--;
+	if (!rmtask.cnt) {
+		msm_adsp_disable(rmtask.mod);
+		msm_adsp_put(rmtask.mod);
+		rmtask.state = ADSP_STATE_DISABLED;
+	}
+	mutex_unlock(&rmtask.lock);
+	return 0;
+}
+EXPORT_SYMBOL(put_adsp_resource);
+
+static void rm_dsp_event(void *data, unsigned id, size_t len,
+				void (*getevent) (void *ptr, size_t len))
+{
+	unsigned short client_id;
+	int client_idx;
+
+	MM_DBG("Msg ID = %d\n", id);
+
+	switch (id) {
+	case RMT_CODEC_CONFIG_ACK: {
+		getevent(&rmtask.cfg_msg, sizeof(rmtask.cfg_msg));
+		client_id = ((rmtask.cfg_msg.client_id << 8) |
+						rmtask.cfg_msg.task_id);
+		client_idx = ((client_id >> 8) * MAX_CLIENTS) +
+						(client_id & 0xFF);
+		memcpy(&rmclient[client_idx].cfg_msg, &rmtask.cfg_msg,
+							sizeof(rmtask.cfg_msg));
+		rmclient[client_idx].wait_state = 1;
+		wake_up(&rmclient[client_idx].wait);
+		break;
+	}
+	case RMT_DSP_OUT_OF_MIPS: {
+		struct rmt_dsp_out_of_mips msg;
+		getevent(&msg, sizeof(msg));
+		MM_ERR("RMT_DSP_OUT_OF_MIPS: Not enough resorces in ADSP \
+				to handle all sessions :%hx\n", msg.dec_info);
+		break;
+	}
+	default:
+		MM_DBG("Unknown Msg Id\n");
+		break;
+	}
+}
+
+void rmtask_init(void)
+{
+	int i;
+
+	for (i = 0; i < MAX_RM_CLIENTS; i++)
+		init_waitqueue_head(&rmclient[i].wait);
+	mutex_init(&rmtask.lock);
+}
diff --git a/arch/arm/mach-msm/qdsp5/adsp_vfe_patch_event.c b/arch/arm/mach-msm/qdsp5/adsp_vfe_patch_event.c
new file mode 100644
index 0000000..68ae380
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/adsp_vfe_patch_event.c
@@ -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 Google, Inc.
+ * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * 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/arch/arm/mach-msm/qdsp5/adsp_vfe_verify_cmd.c b/arch/arm/mach-msm/qdsp5/adsp_vfe_verify_cmd.c
new file mode 100644
index 0000000..dcd3d96
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/adsp_vfe_verify_cmd.c
@@ -0,0 +1,244 @@
+/* arch/arm/mach-msm/qdsp5/adsp_vfe_verify_cmd.c
+ *
+ * Verification code for aDSP VFE packets from userspace.
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * 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"
+#include <mach/debug_mm.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) {
+			MM_ERR("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) {
+		MM_ERR("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)) {
+			MM_ERR("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)) { 
+			MM_ERR("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) {
+			MM_ERR("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:
+		MM_ERR("module %s: unknown queue id %d\n",
+			 module->name, queue_id);
+		return -1;
+	}
+}
diff --git a/arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c b/arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c
new file mode 100644
index 0000000..f884a29
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c
@@ -0,0 +1,233 @@
+/* arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c
+ *
+ * Verificion code for aDSP VDEC packets from userspace.
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
+ *
+ * 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>
+#include <linux/android_pmem.h>
+
+#include <mach/qdsp5/qdsp5vdeccmdi.h>
+#include "adsp.h"
+#include <mach/debug_mm.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,
+				struct file **filp, unsigned long *offset)
+{
+	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);
+	MM_DBG("virt %x %x\n", (unsigned int)phys_addr,
+			(unsigned int)phys_size);
+	if (phys_addr) {
+		if (adsp_pmem_fixup_kvaddr(module, &phys_addr,
+			 &kvaddr, phys_size, filp, offset)) {
+			MM_ERR("ah%x al%x sh%x sl%x addr %x size %x\n",
+					*high, *low, size_high,
+					size_low, (unsigned int)phys_addr,
+					(unsigned int)phys_size);
+			return -EINVAL;
+		}
+	}
+	ptr_to_high_low_short(phys_addr, high, low);
+	MM_DBG("phys %x %x\n", (unsigned int)phys_addr,
+			(unsigned int)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;
+	unsigned short *frame_header_pkt;
+	int i, num_addr, skip, start_pos = 0, xdim_pos = 1, ydim_pos = 2;
+	unsigned short *frame_buffer_high, *frame_buffer_low;
+	unsigned long frame_buffer_size;
+	unsigned short frame_buffer_size_high, frame_buffer_size_low;
+	struct file *filp = NULL;
+	unsigned long offset = 0;
+	struct pmem_addr pmem_addr;
+	unsigned long Codec_Id = 0;
+
+	MM_DBG("cmd_size %d cmd_id %d cmd_data %x\n", cmd_size, cmd_id,
+					(unsigned int)cmd_data);
+	if (cmd_id != VIDDEC_CMD_SUBFRAME_PKT) {
+		MM_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,
+				&filp, &offset))
+		return -1;
+	Codec_Id = pkt->codec_selection_word;
+
+	/* deref those ptrs and check if they are a frame header packet */
+	frame_header_pkt = (unsigned short *)subframe_pkt_addr;
+	switch (frame_header_pkt[0]) {
+	case 0xB201: /* h.264 vld in dsp */
+	   if (Codec_Id == 0x8) {
+		num_addr = 16;
+		skip = 0;
+		start_pos = 5;
+	   } else {
+	       num_addr = 33;
+	       skip = 0;
+	       start_pos = 6;
+	   }
+		break;
+	case 0x8201: /* h.264 vld in arm */
+		num_addr = 16;
+		skip = 0;
+		start_pos = 6;
+		break;
+	case 0x4D01: /* mpeg-4 and h.263 vld in arm */
+		num_addr = 3;
+		skip = 0;
+		start_pos = 5;
+		break;
+	case 0x9201: /*For Real Decoder*/
+		num_addr = 2;
+		skip = 0;
+		start_pos = 5;
+		break;
+	case 0xBD01: /* mpeg-4 and h.263 vld in dsp */
+		num_addr = 3;
+		skip = 0;
+		start_pos = 6;
+		if (((frame_header_pkt[5] & 0x000c) >> 2) == 0x2) /* B-frame */
+			start_pos = 8;
+		break;
+	case 0x0001: /* wmv */
+		num_addr = 2;
+		skip = 0;
+		start_pos = 5;
+		break;
+	case 0xC201: /*WMV main profile*/
+		 num_addr = 3;
+		 skip = 0;
+		 start_pos = 6;
+		 break;
+	case 0xDD01: /* VP6 */
+		num_addr = 3;
+		skip = 0;
+		start_pos = 10;
+		break;
+	case 0xFD01: /* VP8 */
+		num_addr = 3;
+		skip = 0;
+		start_pos = 24;
+		break;
+	default:
+		return 0;
+	}
+
+	frame_buffer_high = &frame_header_pkt[start_pos];
+	frame_buffer_low = &frame_header_pkt[start_pos + 1];
+	frame_buffer_size = (frame_header_pkt[xdim_pos] *
+			     frame_header_pkt[ydim_pos] * 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 (frame_buffer_high && frame_buffer_low) {
+			if (pmem_fixup_high_low(frame_buffer_high,
+						frame_buffer_low,
+						frame_buffer_size_high,
+						frame_buffer_size_low,
+						module,
+						NULL, NULL, NULL, NULL))
+				return -EINVAL;
+	   }
+		frame_buffer_high += 2;
+		frame_buffer_low += 2;
+	}
+	/* Patch the output buffer. */
+	frame_buffer_high += 2*skip;
+	frame_buffer_low += 2*skip;
+	if (frame_buffer_high && frame_buffer_low) {
+		if (pmem_fixup_high_low(frame_buffer_high,
+					frame_buffer_low,
+					frame_buffer_size_high,
+					frame_buffer_size_low,
+					module,
+					NULL, NULL, NULL, NULL))
+			return -EINVAL;
+	}
+	if (filp) {
+		pmem_addr.vaddr = subframe_pkt_addr;
+		pmem_addr.length = ((subframe_pkt_size + 31) & (~31)) + 32;
+		pmem_addr.offset = offset;
+		if (pmem_cache_maint (filp, PMEM_CLEAN_CACHES, &pmem_addr)) {
+			MM_ERR("Cache operation failed for phys addr high %x"
+				" addr low %x\n", pkt->subframe_packet_high,
+				pkt->subframe_packet_low);
+			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:
+		return verify_vdec_pkt_cmd(module, cmd_data, cmd_size);
+	default:
+		MM_INFO("unknown video queue %u\n", queue_id);
+		return 0;
+	}
+}
+
diff --git a/arch/arm/mach-msm/qdsp5/adsp_videoenc_verify_cmd.c b/arch/arm/mach-msm/qdsp5/adsp_videoenc_verify_cmd.c
new file mode 100644
index 0000000..936b7af
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/adsp_videoenc_verify_cmd.c
@@ -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 Google, Inc.
+ * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * 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>
+
+#include <mach/qdsp5/qdsp5venccmdi.h>
+#include "adsp.h"
+#include <mach/debug_mm.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);
+	MM_DBG("virt %x %x\n", (unsigned int)phys_addr,
+			(unsigned int)phys_size);
+	if (adsp_pmem_fixup_kvaddr(module, &phys_addr, &kvaddr, phys_size,
+				NULL, NULL)) {
+		MM_ERR("ah%x al%x sh%x sl%x addr %x size %x\n",
+			*high, *low, size_high,
+			size_low, (unsigned int)phys_addr,
+			(unsigned int) phys_size);
+		return -1;
+	}
+	ptr_to_high_low_short(phys_addr, high, low);
+	MM_DBG("phys %x %x\n", (unsigned int)phys_addr,
+			(unsigned int)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;
+
+	MM_DBG("cmd_size %d cmd_id %d cmd_data %x\n",
+		cmd_size, cmd_id, (unsigned int)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_VENC_CLOCK:
+		if (cmd_size < sizeof(struct videnc_cmd_venc_clock))
+			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:
+		MM_INFO("adsp_video:unknown encoder video cmd %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:
+		return verify_venc_cmd(module, cmd_data, cmd_size);
+	default:
+		MM_INFO("unknown video queue %u\n", queue_id);
+		return 0;
+	}
+}
+
diff --git a/arch/arm/mach-msm/qdsp5/audio_aac.c b/arch/arm/mach-msm/qdsp5/audio_aac.c
new file mode 100644
index 0000000..556a5f1
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audio_aac.c
@@ -0,0 +1,1899 @@
+/* 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, 2011 Code Aurora Forum. All rights reserved.
+ *
+ * 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/list.h>
+#include <linux/earlysuspend.h>
+#include <linux/android_pmem.h>
+#include <linux/slab.h>
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include "audmgr.h"
+
+#include <mach/msm_adsp.h>
+#include <linux/msm_audio_aac.h>
+#include <mach/qdsp5/qdsp5audppcmdi.h>
+#include <mach/qdsp5/qdsp5audppmsg.h>
+#include <mach/qdsp5/qdsp5audplaycmdi.h>
+#include <mach/qdsp5/qdsp5audplaymsg.h>
+#include <mach/qdsp5/qdsp5rmtcmdi.h>
+#include <mach/debug_mm.h>
+
+#define BUFSZ 32768
+#define DMASZ (BUFSZ * 2)
+#define BUFSZ_MIN 4096
+#define DMASZ_MIN (BUFSZ_MIN * 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
+
+#define AUDAAC_METAFIELD_MASK 0xFFFF0000
+#define AUDAAC_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */
+#define AUDAAC_EOS_FLG_MASK 0x01
+#define AUDAAC_EOS_NONE 0x0 /* No EOS detected */
+#define AUDAAC_EOS_SET 0x1 /* EOS set in meta field */
+
+#define AUDAAC_EVENT_NUM 10 /* Default number of pre-allocated event packets */
+
+struct buffer {
+	void *data;
+	unsigned size;
+	unsigned used;		/* Input usage actual DSP produced PCM size  */
+	unsigned addr;
+	unsigned short mfield_sz; /*only useful for data has meta field */
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+struct audaac_suspend_ctl {
+	struct early_suspend node;
+	struct audio *audio;
+};
+#endif
+
+struct audaac_event{
+	struct list_head list;
+	int event_type;
+	union msm_audio_event_payload payload;
+};
+
+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 */
+	int32_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;
+	int32_t phys; /* physical address of write buffer */
+
+	int mfield; /* meta field embedded in data */
+	int rflush; /* Read  flush */
+	int eos_in_progress;
+	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 rmt_resource_released;
+	int teos; /* valid only if tunnel mode & no data left for decoder */
+	enum msm_aud_decoder_state dec_state;	/* Represents decoder state */
+	int reserved; /* A byte is being reserved */
+	char rsv_byte; /* Handle odd length user data */
+
+	const char *module_name;
+	unsigned queue_id;
+	uint16_t dec_id;
+	uint32_t read_ptr_offset;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	struct audaac_suspend_ctl suspend_ctl;
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *dentry;
+#endif
+
+	wait_queue_head_t wait;
+	struct list_head free_event_queue;
+	struct list_head event_queue;
+	wait_queue_head_t event_wait;
+	spinlock_t event_queue_lock;
+	struct mutex get_event_lock;
+	int event_abort;
+
+	struct msm_audio_bitstream_info stream_info;
+
+	int eq_enable;
+	int eq_needs_commit;
+	audpp_cmd_cfg_object_params_eqalizer eq;
+	audpp_cmd_cfg_object_params_volume vol_pan;
+};
+
+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);
+static void audaac_post_event(struct audio *audio, int type,
+		union msm_audio_event_payload payload);
+
+static int rmt_put_resource(struct audio *audio)
+{
+	struct aud_codec_config_cmd cmd;
+	unsigned short client_idx;
+
+	cmd.cmd_id = RM_CMD_AUD_CODEC_CFG;
+	cmd.client_id = RM_AUD_CLIENT_ID;
+	cmd.task_id = audio->dec_id;
+	cmd.enable = RMT_DISABLE;
+	cmd.dec_type = AUDDEC_DEC_AAC;
+	client_idx = ((cmd.client_id << 8) | cmd.task_id);
+
+	return put_adsp_resource(client_idx, &cmd, sizeof(cmd));
+}
+
+static int rmt_get_resource(struct audio *audio)
+{
+	struct aud_codec_config_cmd cmd;
+	unsigned short client_idx;
+
+	cmd.cmd_id = RM_CMD_AUD_CODEC_CFG;
+	cmd.client_id = RM_AUD_CLIENT_ID;
+	cmd.task_id = audio->dec_id;
+	cmd.enable = RMT_ENABLE;
+	cmd.dec_type = AUDDEC_DEC_AAC;
+	client_idx = ((cmd.client_id << 8) | cmd.task_id);
+
+	return get_adsp_resource(client_idx, &cmd, sizeof(cmd));
+}
+
+/* must be called with audio->lock held */
+static int audio_enable(struct audio *audio)
+{
+	struct audmgr_config cfg;
+	int rc;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+
+	if (audio->enabled)
+		return 0;
+
+	if (audio->rmt_resource_released == 1) {
+		audio->rmt_resource_released = 0;
+		rc = rmt_get_resource(audio);
+		if (rc) {
+			MM_ERR("ADSP resources are not available for AAC \
+				session 0x%08x on decoder: %d\n Ignoring \
+				error and going ahead with the playback\n",
+				(int)audio, audio->dec_id);
+		}
+	}
+
+	audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+	audio->out_tail = 0;
+	audio->out_needed = 0;
+
+	if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) {
+		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)) {
+		MM_ERR("msm_adsp_enable(audplay) failed\n");
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			audmgr_disable(&audio->audmgr);
+		return -ENODEV;
+	}
+
+	if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) {
+		MM_ERR("audpp_enable() failed\n");
+		msm_adsp_disable(audio->audplay);
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			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)
+{
+	int rc = 0;
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	if (audio->enabled) {
+		audio->enabled = 0;
+		audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+		auddec_dsp_config(audio, 0);
+		rc = wait_event_interruptible_timeout(audio->wait,
+				audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+				msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+		if (rc == 0)
+			rc = -ETIMEDOUT;
+		else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE)
+			rc = -EFAULT;
+		else
+			rc = 0;
+		wake_up(&audio->write_wait);
+		wake_up(&audio->read_wait);
+		msm_adsp_disable(audio->audplay);
+		audpp_disable(audio->dec_id, audio);
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			audmgr_disable(&audio->audmgr);
+		audio->out_needed = 0;
+		rmt_put_resource(audio);
+		audio->rmt_resource_released = 1;
+	}
+	return rc;
+}
+
+/* ------------------- 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]) {
+			MM_DBG("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 {
+			MM_ERR("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 {
+		MM_DBG("read cannot keep up\n");
+		audio->buf_refresh = 1;
+	}
+	wake_up(&audio->read_wait);
+	spin_unlock_irqrestore(&audio->dsp_lock, flags);
+
+}
+static void audaac_update_stream_info(struct audio *audio, uint32_t *payload)
+{
+	unsigned long flags;
+	union msm_audio_event_payload e_payload;
+
+	/* get stream info from DSP msg */
+	spin_lock_irqsave(&audio->dsp_lock, flags);
+
+	audio->stream_info.codec_type = AUDIO_CODEC_TYPE_AAC;
+	audio->stream_info.chan_info = (0x0000FFFF & payload[1]);
+	audio->stream_info.sample_rate = (0x0000FFFF & payload[2]);
+	audio->stream_info.bit_stream_info = (0x0000FFFF & payload[3]);
+	audio->stream_info.bit_rate = payload[4];
+
+	spin_unlock_irqrestore(&audio->dsp_lock, flags);
+	MM_DBG("chan_info=%d, sample_rate=%d, bit_stream_info=%d\n",
+			audio->stream_info.chan_info,
+			audio->stream_info.sample_rate,
+			audio->stream_info.bit_stream_info);
+
+	/* send event to ARM to notify steam info coming */
+	e_payload.stream_info = audio->stream_info;
+	audaac_post_event(audio, AUDIO_EVENT_STREAM_INFO, e_payload);
+}
+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));
+
+	MM_DBG("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;
+
+	case AUDPLAY_UP_STREAM_INFO:
+		audaac_update_stream_info(audio, msg);
+		break;
+
+	case ADSP_MESSAGE_ID:
+		MM_DBG("Received ADSP event: module enable(audplaytask)\n");
+		break;
+
+	default:
+		MM_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: {
+				uint16_t reason = msg[2];
+				MM_DBG("decoder status: sleep reason = \
+						0x%04x\n", reason);
+				if ((reason == AUDPP_MSG_REASON_MEM)
+					|| (reason ==
+					AUDPP_MSG_REASON_NODECODER)) {
+					audio->dec_state =
+						MSM_AUD_DECODER_STATE_FAILURE;
+					wake_up(&audio->wait);
+				} else if (reason == AUDPP_MSG_REASON_NONE) {
+					/* decoder is in disable state */
+					audio->dec_state =
+						MSM_AUD_DECODER_STATE_CLOSE;
+					wake_up(&audio->wait);
+				}
+				break;
+			}
+			case AUDPP_DEC_STATUS_INIT:
+				MM_DBG("decoder status: init \n");
+				if (audio->pcm_feedback)
+					audpp_cmd_cfg_routing_mode(audio);
+				else
+					audpp_cmd_cfg_adec_params(audio);
+				break;
+
+			case AUDPP_DEC_STATUS_CFG:
+				MM_DBG("decoder status: cfg \n");
+				break;
+			case AUDPP_DEC_STATUS_PLAY:
+				MM_DBG("decoder status: play \n");
+				if (audio->pcm_feedback) {
+					audplay_config_hostpcm(audio);
+					audplay_buffer_refresh(audio);
+				}
+				audio->dec_state =
+					MSM_AUD_DECODER_STATE_SUCCESS;
+				wake_up(&audio->wait);
+				break;
+			default:
+				MM_ERR("unknown decoder status \n");
+			}
+			break;
+		}
+	case AUDPP_MSG_CFG_MSG:
+		if (msg[0] == AUDPP_MSG_ENA_ENA) {
+			MM_DBG("CFG_MSG ENABLE\n");
+			auddec_dsp_config(audio, 1);
+			audio->out_needed = 0;
+			audio->running = 1;
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan);
+			audpp_dsp_set_eq(audio->dec_id, audio->eq_enable,
+								&audio->eq);
+			audpp_avsync(audio->dec_id, 22050);
+		} else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+			MM_DBG("CFG_MSG DISABLE\n");
+			audpp_avsync(audio->dec_id, 0);
+			audio->running = 0;
+		} else {
+			MM_DBG("CFG_MSG %d?\n",	msg[0]);
+		}
+		break;
+	case AUDPP_MSG_ROUTING_ACK:
+		MM_DBG("ROUTING_ACK mode=%d\n",	msg[1]);
+		audpp_cmd_cfg_adec_params(audio);
+		break;
+
+	case AUDPP_MSG_FLUSH_ACK:
+		MM_DBG("FLUSH_ACK\n");
+		audio->wflush = 0;
+		audio->rflush = 0;
+		wake_up(&audio->write_wait);
+		if (audio->pcm_feedback)
+			audplay_buffer_refresh(audio);
+		break;
+
+	case AUDPP_MSG_PCMDMAMISSED:
+		MM_DBG("PCMDMAMISSED\n");
+		audio->teos = 1;
+		wake_up(&audio->write_wait);
+		break;
+
+	default:
+		MM_ERR("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, audio->queue_id,\
+			cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+	u16 cfg_dec_cmd[AUDPP_CMD_CFG_DEC_TYPE_LEN / sizeof(unsigned short)];
+
+	memset(cfg_dec_cmd, 0, sizeof(cfg_dec_cmd));
+	cfg_dec_cmd[0] = AUDPP_CMD_CFG_DEC_TYPE;
+
+	if (enable)
+		cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC |
+			AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_AAC;
+	else
+		cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC |
+		AUDPP_CMD_DIS_DEC_V;
+
+	return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_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;
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	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)
+{
+	struct audplay_cmd_bitstream_data_avail_nt2 cmd;
+
+	cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2;
+	if (audio->mfield)
+		cmd.decoder_id = AUDAAC_METAFIELD_MASK |
+			(audio->out[idx].mfield_sz >> 1);
+	else
+	    cmd.decoder_id = audio->dec_id;
+	cmd.buf_ptr = audio->out[idx].addr;
+	cmd.buf_size = len / 2;
+	cmd.partition_number = 0;
+	/* complete all the writes to the input buffer */
+	wmb();
+	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;
+	/* AAC frame size */
+	refresh_cmd.buf0_length = audio->in[audio->fill_next].size -
+		(audio->in[audio->fill_next].size % 1024)
+		+ (audio->mfield ? 24 : 0);
+	refresh_cmd.buf_read_count = 0;
+	MM_DBG("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;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	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) {
+			MM_DBG("frame %d free\n", audio->out_tail);
+			frame->used = 0;
+			audio->out_tail ^= 1;
+			wake_up(&audio->write_wait);
+		} else if ((audio->out[0].used == 0) &&
+			 (audio->out[1].used == 0) &&
+			 (audio->eos_in_progress)) {
+			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);
+			MM_DBG("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_BSAC &&
+		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;
+	}
+
+#ifndef CONFIG_AUDIO_AAC_PLUS
+	if (AUDIO_AAC_SBR_ON_FLAG_OFF != config->sbr_on_flag)
+		goto done;
+#else
+	if (config->sbr_on_flag != AUDIO_AAC_SBR_ON_FLAG_OFF &&
+		config->sbr_on_flag != AUDIO_AAC_SBR_ON_FLAG_ON)
+		goto done;
+#endif
+
+#ifndef CONFIG_AUDIO_ENHANCED_AAC_PLUS
+	if (AUDIO_AAC_SBR_PS_ON_FLAG_OFF != config->sbr_ps_on_flag)
+		goto done;
+#else
+	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;
+#endif
+
+	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 int audaac_events_pending(struct audio *audio)
+{
+	unsigned long flags;
+	int empty;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+	empty = !list_empty(&audio->event_queue);
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+	return empty || audio->event_abort;
+}
+
+static void audaac_reset_event_queue(struct audio *audio)
+{
+	unsigned long flags;
+	struct audaac_event *drv_evt;
+	struct list_head *ptr, *next;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+	list_for_each_safe(ptr, next, &audio->event_queue) {
+		drv_evt = list_first_entry(&audio->event_queue,
+				struct audaac_event, list);
+		list_del(&drv_evt->list);
+		kfree(drv_evt);
+	}
+	list_for_each_safe(ptr, next, &audio->free_event_queue) {
+		drv_evt = list_first_entry(&audio->free_event_queue,
+				struct audaac_event, list);
+		list_del(&drv_evt->list);
+		kfree(drv_evt);
+	}
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+	return;
+}
+
+static long audaac_process_event_req(struct audio *audio, void __user *arg)
+{
+	long rc;
+	struct msm_audio_event usr_evt;
+	struct audaac_event *drv_evt = NULL;
+	int timeout;
+	unsigned long flags;
+
+	if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event)))
+		return -EFAULT;
+
+	timeout = (int) usr_evt.timeout_ms;
+
+	if (timeout > 0) {
+		rc = wait_event_interruptible_timeout(
+			audio->event_wait, audaac_events_pending(audio),
+			msecs_to_jiffies(timeout));
+		if (rc == 0)
+			return -ETIMEDOUT;
+	} else {
+		rc = wait_event_interruptible(
+			audio->event_wait, audaac_events_pending(audio));
+	}
+
+	if (rc < 0)
+		return rc;
+
+	if (audio->event_abort) {
+		audio->event_abort = 0;
+		return -ENODEV;
+	}
+
+	rc = 0;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+	if (!list_empty(&audio->event_queue)) {
+		drv_evt = list_first_entry(&audio->event_queue,
+				struct audaac_event, list);
+		list_del(&drv_evt->list);
+	}
+	if (drv_evt) {
+		usr_evt.event_type = drv_evt->event_type;
+		usr_evt.event_payload = drv_evt->payload;
+		list_add_tail(&drv_evt->list, &audio->free_event_queue);
+	} else
+		rc = -1;
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+	if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt)))
+		rc = -EFAULT;
+
+	return rc;
+}
+
+static int audio_enable_eq(struct audio *audio, int enable)
+{
+	if (audio->eq_enable == enable && !audio->eq_needs_commit)
+		return 0;
+
+	audio->eq_enable = enable;
+
+	if (audio->running) {
+		audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq);
+		audio->eq_needs_commit = 0;
+	}
+	return 0;
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct audio *audio = file->private_data;
+	int rc = -EINVAL;
+	unsigned long flags = 0;
+	uint16_t enable_mask;
+	int enable;
+	int prev_state;
+
+	MM_DBG("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;
+	}
+
+	switch (cmd) {
+	case AUDIO_ENABLE_AUDPP:
+		if (copy_from_user(&enable_mask, (void *) arg,
+						sizeof(enable_mask))) {
+			rc = -EFAULT;
+			break;
+		}
+
+		spin_lock_irqsave(&audio->dsp_lock, flags);
+		enable = (enable_mask & EQ_ENABLE) ? 1 : 0;
+		audio_enable_eq(audio, enable);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		rc = 0;
+		break;
+	case AUDIO_SET_VOLUME:
+		spin_lock_irqsave(&audio->dsp_lock, flags);
+		audio->vol_pan.volume = arg;
+		if (audio->running)
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		rc = 0;
+		break;
+
+	case AUDIO_SET_PAN:
+		spin_lock_irqsave(&audio->dsp_lock, flags);
+		audio->vol_pan.pan = arg;
+		if (audio->running)
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		rc = 0;
+		break;
+
+	case AUDIO_SET_EQ:
+		prev_state = audio->eq_enable;
+		audio->eq_enable = 0;
+		if (copy_from_user(&audio->eq.num_bands, (void *) arg,
+				sizeof(audio->eq) -
+				(AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) {
+			rc = -EFAULT;
+			break;
+		}
+		audio->eq_enable = prev_state;
+		audio->eq_needs_commit = 1;
+		rc = 0;
+		break;
+	}
+
+	if (-EINVAL != rc)
+		return rc;
+
+	if (cmd == AUDIO_GET_EVENT) {
+		MM_DBG("AUDIO_GET_EVENT\n");
+		if (mutex_trylock(&audio->get_event_lock)) {
+			rc = audaac_process_event_req(audio,
+					(void __user *) arg);
+			mutex_unlock(&audio->get_event_lock);
+		} else
+			rc = -EBUSY;
+		return rc;
+	}
+
+	if (cmd == AUDIO_ABORT_GET_EVENT) {
+		audio->event_abort = 1;
+		wake_up(&audio->event_wait);
+		return 0;
+	}
+
+	mutex_lock(&audio->lock);
+	switch (cmd) {
+	case AUDIO_START:
+		MM_DBG("AUDIO_START\n");
+		rc = audio_enable(audio);
+		if (!rc) {
+			rc = wait_event_interruptible_timeout(audio->wait,
+				audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+				msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+			MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc);
+
+			if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS)
+				rc = -ENODEV;
+			else
+				rc = 0;
+		}
+		break;
+	case AUDIO_STOP:
+		MM_DBG("AUDIO_STOP\n");
+		rc = audio_disable(audio);
+		audio->stopped = 1;
+		audio_ioport_reset(audio);
+		audio->stopped = 0;
+		break;
+	case AUDIO_FLUSH:
+		MM_DBG("AUDIO_FLUSH running=%d\n", audio->running);
+		audio->rflush = 1;
+		audio->wflush = 1;
+		audio_ioport_reset(audio);
+		if (audio->running) {
+			audpp_flush(audio->dec_id);
+			rc = wait_event_interruptible(audio->write_wait,
+				!audio->wflush);
+			if (rc < 0) {
+				MM_ERR("AUDIO_FLUSH interrupted\n");
+				rc = -EINTR;
+			}
+		} 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;
+			audio->mfield = config.meta_field;
+			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.meta_field = 0;
+			config.unused[0] = 0;
+			config.unused[1] = 0;
+			config.unused[2] = 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 = audio->pcm_feedback;
+			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.pcm_feedback != audio->pcm_feedback) {
+				MM_ERR("Not sufficient permission to"
+					 "change the playback mode\n");
+				rc = -EACCES;
+				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->buf_refresh = 0;
+					audio->read_next = 0;
+					audio->fill_next = 0;
+			}
+			rc = 0;
+			break;
+		}
+	case AUDIO_PAUSE:
+		MM_DBG("AUDIO_PAUSE %ld\n", arg);
+		rc = audpp_pause(audio->dec_id, (int) arg);
+		break;
+	case AUDIO_GET_STREAM_INFO:{
+		if (audio->stream_info.sample_rate == 0) {
+			/* haven't received DSP stream event,
+			the stream info is not updated */
+			rc = -EPERM;
+			break;
+		}
+		if (copy_to_user((void *)arg, &audio->stream_info,
+			sizeof(struct msm_audio_bitstream_info)))
+			rc = -EFAULT;
+		else
+			rc = 0;
+		break;
+	}
+	default:
+		rc = -EINVAL;
+	}
+	mutex_unlock(&audio->lock);
+	return rc;
+}
+/* Only useful in tunnel-mode */
+static int audaac_fsync(struct file *file, int datasync)
+{
+	struct audio *audio = file->private_data;
+	struct buffer *frame;
+	int rc = 0;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+
+	if (!audio->running || audio->pcm_feedback) {
+		rc = -EINVAL;
+		goto done_nolock;
+	}
+
+	mutex_lock(&audio->write_lock);
+
+	rc = wait_event_interruptible(audio->write_wait,
+		(!audio->out[0].used &&
+		!audio->out[1].used &&
+		audio->out_needed) || audio->wflush);
+
+	if (rc < 0)
+		goto done;
+	else if (audio->wflush) {
+		rc = -EBUSY;
+		goto done;
+	}
+
+	if (audio->reserved) {
+		MM_DBG("send reserved byte\n");
+		frame = audio->out + audio->out_tail;
+		((char *) frame->data)[0] = audio->rsv_byte;
+		((char *) frame->data)[1] = 0;
+		frame->used = 2;
+		audplay_send_data(audio, 0);
+
+		rc = wait_event_interruptible(audio->write_wait,
+			(!audio->out[0].used &&
+			!audio->out[1].used &&
+			audio->out_needed) || audio->wflush);
+
+		if (rc < 0)
+			goto done;
+		else if (audio->wflush) {
+			rc = -EBUSY;
+			goto done;
+		}
+	}
+
+	/* pcm dmamiss message is sent continously
+	 * when decoder is starved so no race
+	 * condition concern
+	 */
+	audio->teos = 0;
+
+	rc = wait_event_interruptible(audio->write_wait,
+		audio->teos || audio->wflush);
+
+	if (audio->wflush)
+		rc = -EBUSY;
+
+done:
+	mutex_unlock(&audio->write_lock);
+done_nolock:
+	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);
+	MM_DBG("to 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 */
+			MM_DBG("no partial frame done reading\n");
+			break;
+		} else {
+			MM_DBG("read from in[%d]\n", audio->read_next);
+			/* order reads to the output buffer */
+			rmb();
+			if (copy_to_user
+			    (buf, audio->in[audio->read_next].data,
+			     audio->in[audio->read_next].used)) {
+				MM_ERR("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;
+			break;
+				/*
+				* Force to exit while loop
+				* to prevent output thread
+				* sleep too long if data is not
+				* ready at this moment.
+				*/
+		}
+	}
+
+	/* 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;
+		MM_DBG("kick start pcm feedback again\n");
+		audplay_buffer_refresh(audio);
+	}
+
+	mutex_unlock(&audio->read_lock);
+
+	if (buf > start)
+		rc = buf - start;
+
+	MM_DBG("read %d bytes\n", rc);
+	return rc;
+}
+
+static int audaac_process_eos(struct audio *audio,
+	const char __user *buf_start, unsigned short mfield_size)
+{
+	struct buffer *frame;
+	char *buf_ptr;
+	int rc = 0;
+	unsigned long flags = 0;
+
+	MM_DBG("signal input EOS reserved=%d\n", audio->reserved);
+	if (audio->reserved) {
+		MM_DBG("Pass reserve byte\n");
+		frame = audio->out + audio->out_head;
+		buf_ptr = frame->data;
+		rc = wait_event_interruptible(audio->write_wait,
+					(frame->used == 0)
+					|| (audio->stopped)
+					|| (audio->wflush));
+	if (rc < 0)
+		goto done;
+	if (audio->stopped || audio->wflush) {
+		rc = -EBUSY;
+		goto done;
+	}
+	buf_ptr[0] = audio->rsv_byte;
+	buf_ptr[1] = 0;
+	audio->out_head ^= 1;
+	frame->mfield_sz = 0;
+	audio->reserved = 0;
+	frame->used = 2;
+	audplay_send_data(audio, 0);
+	}
+	MM_DBG("Now signal input EOS after reserved bytes %d %d %d\n",
+		audio->out[0].used, audio->out[1].used, audio->out_needed);
+	frame = audio->out + audio->out_head;
+
+	spin_lock_irqsave(&audio->dsp_lock, flags);
+	audio->eos_in_progress = 1;
+	spin_unlock_irqrestore(&audio->dsp_lock, flags);
+
+	rc = wait_event_interruptible(audio->write_wait,
+		(audio->out_needed &&
+		audio->out[0].used == 0 &&
+		audio->out[1].used == 0)
+		|| (audio->stopped)
+		|| (audio->wflush));
+
+	spin_lock_irqsave(&audio->dsp_lock, flags);
+	audio->eos_in_progress = 0;
+	spin_unlock_irqrestore(&audio->dsp_lock, flags);
+
+	if (rc < 0)
+		goto done;
+	if (audio->stopped || audio->wflush) {
+		rc = -EBUSY;
+		goto done;
+	}
+
+	if (copy_from_user(frame->data, buf_start, mfield_size)) {
+		rc = -EFAULT;
+		goto done;
+	}
+
+	frame->mfield_sz = mfield_size;
+	audio->out_head ^= 1;
+	frame->used = mfield_size;
+	audplay_send_data(audio, 0);
+done:
+	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, eos_condition = AUDAAC_EOS_NONE;
+	unsigned dsize;
+
+	unsigned short mfield_size = 0;
+	MM_DBG("cnt=%d\n", count);
+	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->mfield) {
+			if (buf == start) {
+				/* Processing beginning of user buffer */
+				if (__get_user(mfield_size,
+					(unsigned short __user *) buf)) {
+					rc = -EFAULT;
+					break;
+				} else 	if (mfield_size > count) {
+					rc = -EINVAL;
+					break;
+				}
+				MM_DBG("mf offset_val %x\n", mfield_size);
+				if (copy_from_user(cpy_ptr, buf, mfield_size)) {
+					rc = -EFAULT;
+					break;
+				}
+				/* Check if EOS flag is set and buffer has
+				* contains just meta field
+				*/
+				if (cpy_ptr[AUDAAC_EOS_FLG_OFFSET] &
+						AUDAAC_EOS_FLG_MASK) {
+					MM_DBG("eos set\n");
+					eos_condition = AUDAAC_EOS_SET;
+					if (mfield_size == count) {
+						buf += mfield_size;
+						break;
+					} else
+					cpy_ptr[AUDAAC_EOS_FLG_OFFSET] &=
+							~AUDAAC_EOS_FLG_MASK;
+				}
+				/* Check EOS to see if */
+				cpy_ptr += mfield_size;
+				count -= mfield_size;
+				dsize += mfield_size;
+				buf += mfield_size;
+			} else {
+				mfield_size = 0;
+				MM_DBG("continuous buffer\n");
+			}
+			frame->mfield_sz = mfield_size;
+		}
+
+		if (audio->reserved) {
+			MM_DBG("append reserved byte %x\n",
+				audio->rsv_byte);
+			*cpy_ptr = audio->rsv_byte;
+			xfer = (count > ((frame->size - mfield_size) - 1)) ?
+				(frame->size - mfield_size) - 1 : count;
+			cpy_ptr++;
+			dsize += 1;
+			audio->reserved = 0;
+		} else
+			xfer = (count > (frame->size - mfield_size)) ?
+				(frame->size - mfield_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];
+			MM_DBG("odd length buf reserve last byte %x\n",
+					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);
+		}
+	}
+	MM_DBG("eos_condition %x buf[0x%x] start[0x%x]\n", eos_condition,
+			(int) buf, (int) start);
+	if (eos_condition == AUDAAC_EOS_SET)
+		rc = audaac_process_eos(audio, start, mfield_size);
+	mutex_unlock(&audio->write_lock);
+	if (!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;
+
+	MM_INFO("audio instance 0x%08x freeing\n", (int)audio);
+	mutex_lock(&audio->lock);
+	audio_disable(audio);
+	if (audio->rmt_resource_released == 0)
+		rmt_put_resource(audio);
+	audio_flush(audio);
+	audio_flush_pcm_buf(audio);
+	msm_adsp_put(audio->audplay);
+	audpp_adec_free(audio->dec_id);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	unregister_early_suspend(&audio->suspend_ctl.node);
+#endif
+	audio->event_abort = 1;
+	wake_up(&audio->event_wait);
+	audaac_reset_event_queue(audio);
+	iounmap(audio->data);
+	pmem_kfree(audio->phys);
+	iounmap(audio->read_data);
+	pmem_kfree(audio->read_phys);
+	mutex_unlock(&audio->lock);
+#ifdef CONFIG_DEBUG_FS
+	if (audio->dentry)
+		debugfs_remove(audio->dentry);
+#endif
+	kfree(audio);
+	return 0;
+}
+
+static void audaac_post_event(struct audio *audio, int type,
+		union msm_audio_event_payload payload)
+{
+	struct audaac_event *e_node = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+
+	if (!list_empty(&audio->free_event_queue)) {
+		e_node = list_first_entry(&audio->free_event_queue,
+				struct audaac_event, list);
+		list_del(&e_node->list);
+	} else {
+		e_node = kmalloc(sizeof(struct audaac_event), GFP_ATOMIC);
+		if (!e_node) {
+			MM_ERR("No mem to post event %d\n", type);
+			spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+			return;
+		}
+	}
+
+	e_node->event_type = type;
+	e_node->payload = payload;
+
+	list_add_tail(&e_node->list, &audio->event_queue);
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+	wake_up(&audio->event_wait);
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void audaac_suspend(struct early_suspend *h)
+{
+	struct audaac_suspend_ctl *ctl =
+		container_of(h, struct audaac_suspend_ctl, node);
+	union msm_audio_event_payload payload;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	audaac_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload);
+}
+
+static void audaac_resume(struct early_suspend *h)
+{
+	struct audaac_suspend_ctl *ctl =
+		container_of(h, struct audaac_suspend_ctl, node);
+	union msm_audio_event_payload payload;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	audaac_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload);
+}
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t audaac_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t audaac_debug_read(struct file *file, char __user *buf,
+					size_t count, loff_t *ppos)
+{
+	const int debug_bufmax = 1024;
+	static char buffer[1024];
+	int n = 0, i;
+	struct audio *audio = file->private_data;
+
+	mutex_lock(&audio->lock);
+	n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"enabled %d\n", audio->enabled);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"stopped %d\n", audio->stopped);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"pcm_feedback %d\n", audio->pcm_feedback);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"out_buf_sz %d\n", audio->out[0].size);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"pcm_buf_count %d \n", audio->pcm_buf_count);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"pcm_buf_sz %d \n", audio->in[0].size);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"volume %x \n", audio->vol_pan.volume);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"sample rate %d \n", audio->out_sample_rate);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"channel mode %d \n", audio->out_channel_mode);
+	mutex_unlock(&audio->lock);
+	/* Following variables are only useful for debugging when
+	 * when playback halts unexpectedly. Thus, no mutual exclusion
+	 * enforced
+	 */
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"wflush %d\n", audio->wflush);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"rflush %d\n", audio->rflush);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"running %d \n", audio->running);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"dec state %d \n", audio->dec_state);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"out_needed %d \n", audio->out_needed);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"out_head %d \n", audio->out_head);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"out_tail %d \n", audio->out_tail);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"out[0].used %d \n", audio->out[0].used);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"out[1].used %d \n", audio->out[1].used);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"buffer_refresh %d \n", audio->buf_refresh);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"read_next %d \n", audio->read_next);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"fill_next %d \n", audio->fill_next);
+	for (i = 0; i < audio->pcm_buf_count; i++)
+		n += scnprintf(buffer + n, debug_bufmax - n,
+				"in[%d].used %d \n", i, audio->in[i].used);
+	buffer[n] = 0;
+	return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+
+static const struct file_operations audaac_debug_fops = {
+	.read = audaac_debug_read,
+	.open = audaac_debug_open,
+};
+#endif
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+	struct audio *audio = NULL;
+	int rc, dec_attrb, decid, index, offset = 0;
+	unsigned pmem_sz = DMASZ;
+	struct audaac_event *e_node = NULL;
+#ifdef CONFIG_DEBUG_FS
+	/* 4 bytes represents decoder number, 1 byte for terminate string */
+	char name[sizeof "msm_aac_" + 5];
+#endif
+
+	/* Allocate audio instance, set to zero */
+	audio = kzalloc(sizeof(struct audio), GFP_KERNEL);
+	if (!audio) {
+		MM_ERR("no memory to allocate audio instance \n");
+		rc = -ENOMEM;
+		goto done;
+	}
+	MM_INFO("audio instance 0x%08x created\n", (int)audio);
+
+	/* Allocate the decoder */
+	dec_attrb = AUDDEC_DEC_AAC;
+	if ((file->f_mode & FMODE_WRITE) &&
+			(file->f_mode & FMODE_READ)) {
+		dec_attrb |= MSM_AUD_MODE_NONTUNNEL;
+		audio->pcm_feedback = NON_TUNNEL_MODE_PLAYBACK;
+	} else if ((file->f_mode & FMODE_WRITE) &&
+			!(file->f_mode & FMODE_READ)) {
+		dec_attrb |= MSM_AUD_MODE_TUNNEL;
+		audio->pcm_feedback = TUNNEL_MODE_PLAYBACK;
+	} else {
+		kfree(audio);
+		rc = -EACCES;
+		goto done;
+	}
+	decid = audpp_adec_alloc(dec_attrb, &audio->module_name,
+			&audio->queue_id);
+
+	if (decid < 0) {
+		MM_ERR("No free decoder available, freeing instance 0x%08x\n",
+				(int)audio);
+		rc = -ENODEV;
+		kfree(audio);
+		goto done;
+	}
+	audio->dec_id = decid & MSM_AUD_DECODER_MASK;
+
+	while (pmem_sz >= DMASZ_MIN) {
+		MM_DBG("pmemsz = %d \n", pmem_sz);
+		audio->phys = pmem_kalloc(pmem_sz, PMEM_MEMTYPE_EBI1|
+						PMEM_ALIGNMENT_4K);
+		if (!IS_ERR((void *)audio->phys)) {
+			audio->data = ioremap(audio->phys, pmem_sz);
+			if (!audio->data) {
+				MM_ERR("could not allocate write buffers, \
+						freeing instance 0x%08x\n",
+						(int)audio);
+				rc = -ENOMEM;
+				pmem_kfree(audio->phys);
+				audpp_adec_free(audio->dec_id);
+				kfree(audio);
+				goto done;
+			}
+			MM_DBG("write buf: phy addr 0x%08x kernel addr \
+				0x%08x\n", audio->phys, (int)audio->data);
+			break;
+		} else if (pmem_sz == DMASZ_MIN) {
+			MM_ERR("could not allocate write buffers, freeing \
+					instance 0x%08x\n", (int)audio);
+			rc = -ENOMEM;
+			audpp_adec_free(audio->dec_id);
+			kfree(audio);
+			goto done;
+		} else
+			pmem_sz >>= 1;
+	}
+	audio->out_dma_sz = pmem_sz;
+
+	audio->read_phys = pmem_kalloc(PCM_BUFSZ_MIN * PCM_BUF_MAX_COUNT,
+				PMEM_MEMTYPE_EBI1|PMEM_ALIGNMENT_4K);
+	if (IS_ERR((void *)audio->read_phys)) {
+		MM_ERR("could not allocate read buffers, freeing instance \
+				0x%08x\n", (int)audio);
+		rc = -ENOMEM;
+		iounmap(audio->data);
+		pmem_kfree(audio->phys);
+		audpp_adec_free(audio->dec_id);
+		kfree(audio);
+		goto done;
+	}
+	audio->read_data = ioremap(audio->read_phys,
+				PCM_BUFSZ_MIN * PCM_BUF_MAX_COUNT);
+	if (!audio->read_data) {
+		MM_ERR("could not allocate read buffers, freeing instance \
+				0x%08x\n", (int)audio);
+		rc = -ENOMEM;
+		iounmap(audio->data);
+		pmem_kfree(audio->phys);
+		pmem_kfree(audio->read_phys);
+		audpp_adec_free(audio->dec_id);
+		kfree(audio);
+		goto done;
+	}
+	MM_DBG("read buf: phy addr 0x%08x kernel addr 0x%08x\n",
+				audio->read_phys, (int)audio->read_data);
+
+	if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) {
+		rc = audmgr_open(&audio->audmgr);
+		if (rc) {
+			MM_ERR("audmgr open failed, freeing instance \
+					0x%08x\n", (int)audio);
+			goto err;
+		}
+	}
+
+	rc = msm_adsp_get(audio->module_name, &audio->audplay,
+			  &audplay_adsp_ops_aac, audio);
+	if (rc) {
+		MM_ERR("failed to get %s module, freeing instance 0x%08x\n",
+				audio->module_name, (int)audio);
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			audmgr_close(&audio->audmgr);
+		goto err;
+	}
+
+	rc = rmt_get_resource(audio);
+	if (rc) {
+		MM_ERR("ADSP resources are not available for AAC session \
+			 0x%08x on decoder: %d\n", (int)audio, audio->dec_id);
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			audmgr_close(&audio->audmgr);
+		msm_adsp_put(audio->audplay);
+		goto err;
+	}
+
+	mutex_init(&audio->lock);
+	mutex_init(&audio->write_lock);
+	mutex_init(&audio->read_lock);
+	mutex_init(&audio->get_event_lock);
+	spin_lock_init(&audio->dsp_lock);
+	spin_lock_init(&audio->event_queue_lock);
+	INIT_LIST_HEAD(&audio->free_event_queue);
+	INIT_LIST_HEAD(&audio->event_queue);
+	init_waitqueue_head(&audio->write_wait);
+	init_waitqueue_head(&audio->read_wait);
+	init_waitqueue_head(&audio->wait);
+	init_waitqueue_head(&audio->event_wait);
+
+	audio->out[0].data = audio->data + 0;
+	audio->out[0].addr = audio->phys + 0;
+	audio->out[0].size = audio->out_dma_sz >> 1;
+
+	audio->out[1].data = audio->data + audio->out[0].size;
+	audio->out[1].addr = audio->phys + audio->out[0].size;
+	audio->out[1].size = audio->out[0].size;
+
+	audio->pcm_buf_count = PCM_BUF_MAX_COUNT;
+	for (index = 0; index < PCM_BUF_MAX_COUNT; index++) {
+		audio->in[index].data = audio->read_data + offset;
+		audio->in[index].addr = audio->read_phys + offset;
+		audio->in[index].size = PCM_BUFSZ_MIN;
+		audio->in[index].used = 0;
+		offset += PCM_BUFSZ_MIN;
+	}
+
+	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;
+#ifdef CONFIG_AUDIO_AAC_PLUS
+	audio->aac_config.sbr_on_flag = AUDIO_AAC_SBR_ON_FLAG_ON;
+#else
+	audio->aac_config.sbr_on_flag = AUDIO_AAC_SBR_ON_FLAG_OFF;
+#endif
+#ifdef CONFIG_AUDIO_ENHANCED_AAC_PLUS
+	audio->aac_config.sbr_ps_on_flag = AUDIO_AAC_SBR_PS_ON_FLAG_ON;
+#else
+	audio->aac_config.sbr_ps_on_flag = AUDIO_AAC_SBR_PS_ON_FLAG_OFF;
+#endif
+	audio->aac_config.dual_mono_mode = AUDIO_AAC_DUAL_MONO_PL_SR;
+	audio->aac_config.channel_configuration = 2;
+	audio->vol_pan.volume = 0x2000;
+
+	audio_flush(audio);
+
+	file->private_data = audio;
+	audio->opened = 1;
+#ifdef CONFIG_DEBUG_FS
+	snprintf(name, sizeof name, "msm_aac_%04x", audio->dec_id);
+	audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+				NULL, (void *) audio,
+				&audaac_debug_fops);
+
+	if (IS_ERR(audio->dentry))
+		MM_DBG("debugfs_create_file failed\n");
+#endif
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
+	audio->suspend_ctl.node.resume = audaac_resume;
+	audio->suspend_ctl.node.suspend = audaac_suspend;
+	audio->suspend_ctl.audio = audio;
+	register_early_suspend(&audio->suspend_ctl.node);
+#endif
+	for (index = 0; index < AUDAAC_EVENT_NUM; index++) {
+		e_node = kmalloc(sizeof(struct audaac_event), GFP_KERNEL);
+		if (e_node)
+			list_add_tail(&e_node->list, &audio->free_event_queue);
+		else {
+			MM_ERR("event pkt alloc failed\n");
+			break;
+		}
+	}
+	memset(&audio->stream_info, 0, sizeof(struct msm_audio_bitstream_info));
+done:
+	return rc;
+err:
+	iounmap(audio->data);
+	pmem_kfree(audio->phys);
+	iounmap(audio->read_data);
+	pmem_kfree(audio->read_phys);
+	audpp_adec_free(audio->dec_id);
+	kfree(audio);
+	return rc;
+}
+
+static const 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,
+	.fsync = audaac_fsync
+};
+
+struct miscdevice audio_aac_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "msm_aac",
+	.fops = &audio_aac_fops,
+};
+
+static int __init audio_init(void)
+{
+	return misc_register(&audio_aac_misc);
+}
+
+static void __exit audio_exit(void)
+{
+	misc_deregister(&audio_aac_misc);
+}
+
+module_init(audio_init);
+module_exit(audio_exit);
+
+MODULE_DESCRIPTION("MSM AAC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5/audio_amrnb.c b/arch/arm/mach-msm/qdsp5/audio_amrnb.c
new file mode 100644
index 0000000..f430b0c
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audio_amrnb.c
@@ -0,0 +1,1609 @@
+/* linux/arch/arm/mach-msm/qdsp5/audio_amrnb.c
+ *
+ * amrnb audio decoder device
+ *
+ * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * 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/debugfs.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/earlysuspend.h>
+#include <linux/android_pmem.h>
+#include <linux/slab.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 <mach/qdsp5/qdsp5rmtcmdi.h>
+#include <mach/debug_mm.h>
+
+#define BUFSZ 1024 /* Hold minimum 700ms voice data and 14 bytes of meta in*/
+#define DMASZ (BUFSZ * 2)
+
+#define AUDPLAY_INVALID_READ_PTR_OFFSET	0xFFFF
+#define AUDDEC_DEC_AMRNB 10
+
+#define PCM_BUFSZ_MIN 1624 /* 100ms worth of data and 24 bytes of meta out*/
+#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
+
+#define AUDAMRNB_METAFIELD_MASK 0xFFFF0000
+#define AUDAMRNB_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */
+#define AUDAMRNB_EOS_FLG_MASK 0x01
+#define AUDAMRNB_EOS_NONE 0x0 /* No EOS detected */
+#define AUDAMRNB_EOS_SET 0x1 /* EOS set in meta field */
+
+#define AUDAMRNB_EVENT_NUM 10 /* Default number of pre-allocated event pkts */
+
+struct buffer {
+	void *data;
+	unsigned size;
+	unsigned used;		/* Input usage actual DSP produced PCM size  */
+	unsigned addr;
+	unsigned short mfield_sz; /*only useful for data has meta field */
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+struct audamrnb_suspend_ctl {
+	struct early_suspend node;
+	struct audio *audio;
+};
+#endif
+
+struct audamrnb_event{
+	struct list_head list;
+	int event_type;
+	union msm_audio_event_payload payload;
+};
+
+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 */
+	int32_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;
+	int32_t phys; /* physical address of write buffer */
+
+	int mfield; /* meta field embedded in data */
+	int rflush; /* Read  flush */
+	int wflush; /* Write flush */
+	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;
+	int teos; /* valid only if tunnel mode & no data left for decoder */
+	enum msm_aud_decoder_state dec_state;	/* Represents decoder state */
+	int rmt_resource_released;
+
+	const char *module_name;
+	unsigned queue_id;
+	uint16_t dec_id;
+	uint32_t read_ptr_offset;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	struct audamrnb_suspend_ctl suspend_ctl;
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *dentry;
+#endif
+
+	wait_queue_head_t wait;
+	struct list_head free_event_queue;
+	struct list_head event_queue;
+	wait_queue_head_t event_wait;
+	spinlock_t event_queue_lock;
+	struct mutex get_event_lock;
+	int event_abort;
+
+	int eq_enable;
+	int eq_needs_commit;
+	audpp_cmd_cfg_object_params_eqalizer eq;
+	audpp_cmd_cfg_object_params_volume vol_pan;
+};
+
+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);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void audamrnb_post_event(struct audio *audio, int type,
+		union msm_audio_event_payload payload);
+#endif
+
+static int rmt_put_resource(struct audio *audio)
+{
+	struct aud_codec_config_cmd cmd;
+	unsigned short client_idx;
+
+	cmd.cmd_id = RM_CMD_AUD_CODEC_CFG;
+	cmd.client_id = RM_AUD_CLIENT_ID;
+	cmd.task_id = audio->dec_id;
+	cmd.enable = RMT_DISABLE;
+	cmd.dec_type = AUDDEC_DEC_AMRNB;
+	client_idx = ((cmd.client_id << 8) | cmd.task_id);
+
+	return put_adsp_resource(client_idx, &cmd, sizeof(cmd));
+}
+
+static int rmt_get_resource(struct audio *audio)
+{
+	struct aud_codec_config_cmd cmd;
+	unsigned short client_idx;
+
+	cmd.cmd_id = RM_CMD_AUD_CODEC_CFG;
+	cmd.client_id = RM_AUD_CLIENT_ID;
+	cmd.task_id = audio->dec_id;
+	cmd.enable = RMT_ENABLE;
+	cmd.dec_type = AUDDEC_DEC_AMRNB;
+	client_idx = ((cmd.client_id << 8) | cmd.task_id);
+
+	return get_adsp_resource(client_idx, &cmd, sizeof(cmd));
+}
+
+/* must be called with audio->lock held */
+static int audamrnb_enable(struct audio *audio)
+{
+	struct audmgr_config cfg;
+	int rc;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	if (audio->enabled)
+		return 0;
+
+	if (audio->rmt_resource_released == 1) {
+		audio->rmt_resource_released = 0;
+		rc = rmt_get_resource(audio);
+		if (rc) {
+			MM_ERR("ADSP resources are not available for AMRNB \
+				session 0x%08x on decoder: %d\n Ignoring \
+				error and going ahead with the playback\n",
+				(int)audio, audio->dec_id);
+		}
+	}
+
+	audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+	audio->out_tail = 0;
+	audio->out_needed = 0;
+
+	if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) {
+		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)) {
+		MM_ERR("msm_adsp_enable(audplay) failed\n");
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			audmgr_disable(&audio->audmgr);
+		return -ENODEV;
+	}
+
+	if (audpp_enable(audio->dec_id, audamrnb_dsp_event, audio)) {
+		MM_ERR("audpp_enable() failed\n");
+		msm_adsp_disable(audio->audplay);
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			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)
+{
+	int rc = 0;
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	if (audio->enabled) {
+		audio->enabled = 0;
+		audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+		auddec_dsp_config(audio, 0);
+		rc = wait_event_interruptible_timeout(audio->wait,
+				audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+				msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+		if (rc == 0)
+			rc = -ETIMEDOUT;
+		else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE)
+			rc = -EFAULT;
+		else
+			rc = 0;
+		wake_up(&audio->write_wait);
+		wake_up(&audio->read_wait);
+		msm_adsp_disable(audio->audplay);
+		audpp_disable(audio->dec_id, audio);
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			audmgr_disable(&audio->audmgr);
+		audio->out_needed = 0;
+		rmt_put_resource(audio);
+		audio->rmt_resource_released = 1;
+	}
+	return rc;
+}
+
+/* ------------------- dsp --------------------- */
+static void audamrnb_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]) {
+			MM_DBG("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 {
+			MM_ERR("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 {
+		MM_DBG("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));
+
+	MM_DBG("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;
+
+	case ADSP_MESSAGE_ID:
+		MM_DBG("Received ADSP event: module enable(audplaytask)\n");
+		break;
+
+	default:
+		MM_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: {
+				uint16_t reason = msg[2];
+				MM_DBG("decoder status:sleep reason = \
+						0x%04x\n", reason);
+				if ((reason == AUDPP_MSG_REASON_MEM)
+					|| (reason ==
+					AUDPP_MSG_REASON_NODECODER)) {
+					audio->dec_state =
+						MSM_AUD_DECODER_STATE_FAILURE;
+					wake_up(&audio->wait);
+				} else if (reason == AUDPP_MSG_REASON_NONE) {
+					/* decoder is in disable state */
+					audio->dec_state =
+						MSM_AUD_DECODER_STATE_CLOSE;
+					wake_up(&audio->wait);
+				}
+				break;
+			}
+			case AUDPP_DEC_STATUS_INIT:
+				MM_DBG("decoder status: init \n");
+				if (audio->pcm_feedback)
+					audpp_cmd_cfg_routing_mode(audio);
+				else
+					audpp_cmd_cfg_adec_params(audio);
+				break;
+
+			case AUDPP_DEC_STATUS_CFG:
+				MM_DBG("decoder status: cfg \n");
+				break;
+			case AUDPP_DEC_STATUS_PLAY:
+				MM_DBG("decoder status: play \n");
+				if (audio->pcm_feedback) {
+					audamrnb_config_hostpcm(audio);
+					audamrnb_buffer_refresh(audio);
+				}
+				audio->dec_state =
+					MSM_AUD_DECODER_STATE_SUCCESS;
+				wake_up(&audio->wait);
+				break;
+			default:
+				MM_ERR("unknown decoder status \n");
+				break;
+			}
+			break;
+		}
+	case AUDPP_MSG_CFG_MSG:
+		if (msg[0] == AUDPP_MSG_ENA_ENA) {
+			MM_DBG("CFG_MSG ENABLE\n");
+			auddec_dsp_config(audio, 1);
+			audio->out_needed = 0;
+			audio->running = 1;
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan);
+			audpp_dsp_set_eq(audio->dec_id,	audio->eq_enable,
+								&audio->eq);
+			audpp_avsync(audio->dec_id, 22050);
+		} else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+			MM_DBG("CFG_MSG DISABLE\n");
+			audpp_avsync(audio->dec_id, 0);
+			audio->running = 0;
+		} else {
+			MM_DBG("CFG_MSG %d?\n", msg[0]);
+		}
+		break;
+	case AUDPP_MSG_ROUTING_ACK:
+		MM_DBG("ROUTING_ACK mode=%d\n", msg[1]);
+		audpp_cmd_cfg_adec_params(audio);
+		break;
+	case AUDPP_MSG_FLUSH_ACK:
+		MM_DBG("FLUSH_ACK\n");
+		audio->wflush = 0;
+		audio->rflush = 0;
+		wake_up(&audio->write_wait);
+		if (audio->pcm_feedback)
+			audamrnb_buffer_refresh(audio);
+		break;
+	case AUDPP_MSG_PCMDMAMISSED:
+		MM_DBG("PCMDMAMISSED\n");
+		audio->teos = 1;
+		wake_up(&audio->write_wait);
+		break;
+	default:
+		MM_ERR("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, audio->queue_id, \
+			cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+	u16 cfg_dec_cmd[AUDPP_CMD_CFG_DEC_TYPE_LEN / sizeof(unsigned short)];
+
+	memset(cfg_dec_cmd, 0, sizeof(cfg_dec_cmd));
+	cfg_dec_cmd[0] = AUDPP_CMD_CFG_DEC_TYPE;
+	if (enable)
+		cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC |
+			AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_AMRNB;
+	else
+		cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC |
+			AUDPP_CMD_DIS_DEC_V;
+
+	return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_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;
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	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)
+{
+	struct audplay_cmd_bitstream_data_avail_nt2 cmd;
+
+	cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2;
+	if (audio->mfield)
+		cmd.decoder_id = AUDAMRNB_METAFIELD_MASK |
+			(audio->out[idx].mfield_sz >> 1);
+	else
+		cmd.decoder_id = audio->dec_id;
+	cmd.buf_ptr = audio->out[idx].addr;
+	cmd.buf_size = len / 2;
+	cmd.partition_number = 0;
+	/* complete writes to the input buffer */
+	wmb();
+	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) +
+	  (audio->mfield ? 24 : 0);
+	refresh_cmd.buf_read_count = 0;
+	MM_DBG("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;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	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 && !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) {
+			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);
+			MM_DBG("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->out_needed = 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->buf_refresh = 0;
+	audio->read_next = 0;
+	audio->fill_next = 0;
+}
+
+static void audamrnb_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);
+	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);
+}
+
+static int audamrnb_events_pending(struct audio *audio)
+{
+	unsigned long flags;
+	int empty;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+	empty = !list_empty(&audio->event_queue);
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+	return empty || audio->event_abort;
+}
+
+static void audamrnb_reset_event_queue(struct audio *audio)
+{
+	unsigned long flags;
+	struct audamrnb_event *drv_evt;
+	struct list_head *ptr, *next;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+	list_for_each_safe(ptr, next, &audio->event_queue) {
+		drv_evt = list_first_entry(&audio->event_queue,
+				struct audamrnb_event, list);
+		list_del(&drv_evt->list);
+		kfree(drv_evt);
+	}
+	list_for_each_safe(ptr, next, &audio->free_event_queue) {
+		drv_evt = list_first_entry(&audio->free_event_queue,
+				struct audamrnb_event, list);
+		list_del(&drv_evt->list);
+		kfree(drv_evt);
+	}
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+	return;
+}
+
+static long audamrnb_process_event_req(struct audio *audio, void __user *arg)
+{
+	long rc;
+	struct msm_audio_event usr_evt;
+	struct audamrnb_event *drv_evt = NULL;
+	int timeout;
+	unsigned long flags;
+
+	if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event)))
+		return -EFAULT;
+
+	timeout = (int) usr_evt.timeout_ms;
+
+	if (timeout > 0) {
+		rc = wait_event_interruptible_timeout(
+			audio->event_wait, audamrnb_events_pending(audio),
+			msecs_to_jiffies(timeout));
+		if (rc == 0)
+			return -ETIMEDOUT;
+	} else {
+		rc = wait_event_interruptible(
+			audio->event_wait, audamrnb_events_pending(audio));
+	}
+
+	if (rc < 0)
+		return rc;
+
+	if (audio->event_abort) {
+		audio->event_abort = 0;
+		return -ENODEV;
+	}
+
+	rc = 0;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+	if (!list_empty(&audio->event_queue)) {
+		drv_evt = list_first_entry(&audio->event_queue,
+				struct audamrnb_event, list);
+		list_del(&drv_evt->list);
+	}
+
+	if (drv_evt) {
+		usr_evt.event_type = drv_evt->event_type;
+		usr_evt.event_payload = drv_evt->payload;
+		list_add_tail(&drv_evt->list, &audio->free_event_queue);
+	} else
+		rc = -1;
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+	if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt)))
+		rc = -EFAULT;
+
+	return rc;
+}
+
+static int audio_enable_eq(struct audio *audio, int enable)
+{
+	if (audio->eq_enable == enable && !audio->eq_needs_commit)
+		return 0;
+
+	audio->eq_enable = enable;
+
+	if (audio->running) {
+		audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq);
+		audio->eq_needs_commit = 0;
+	}
+	return 0;
+}
+
+static long audamrnb_ioctl(struct file *file, unsigned int cmd,
+		unsigned long arg)
+{
+	struct audio *audio = file->private_data;
+	int rc = -EINVAL;
+	unsigned long flags = 0;
+	uint16_t enable_mask;
+	int enable;
+	int prev_state;
+
+	MM_DBG("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;
+	}
+
+	switch (cmd) {
+	case AUDIO_ENABLE_AUDPP:
+		if (copy_from_user(&enable_mask, (void *) arg,
+						sizeof(enable_mask))) {
+			rc = -EFAULT;
+			break;
+		}
+
+		spin_lock_irqsave(&audio->dsp_lock, flags);
+		enable = (enable_mask & EQ_ENABLE) ? 1 : 0;
+		audio_enable_eq(audio, enable);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		rc = 0;
+		break;
+	case AUDIO_SET_VOLUME:
+		spin_lock_irqsave(&audio->dsp_lock, flags);
+		audio->vol_pan.volume = arg;
+		if (audio->running)
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		rc = 0;
+		break;
+
+	case AUDIO_SET_PAN:
+		spin_lock_irqsave(&audio->dsp_lock, flags);
+		audio->vol_pan.pan = arg;
+		if (audio->running)
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		rc = 0;
+		break;
+
+	case AUDIO_SET_EQ:
+		prev_state = audio->eq_enable;
+		audio->eq_enable = 0;
+		if (copy_from_user(&audio->eq.num_bands, (void *) arg,
+				sizeof(audio->eq) -
+				(AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) {
+			rc = -EFAULT;
+			break;
+		}
+		audio->eq_enable = prev_state;
+		audio->eq_needs_commit = 1;
+		rc = 0;
+		break;
+	}
+
+	if (-EINVAL != rc)
+		return rc;
+
+	if (cmd == AUDIO_GET_EVENT) {
+		MM_DBG("AUDIO_GET_EVENT\n");
+		if (mutex_trylock(&audio->get_event_lock)) {
+			rc = audamrnb_process_event_req(audio,
+					(void __user *) arg);
+			mutex_unlock(&audio->get_event_lock);
+		} else
+			rc = -EBUSY;
+		return rc;
+	}
+
+	if (cmd == AUDIO_ABORT_GET_EVENT) {
+		audio->event_abort = 1;
+		wake_up(&audio->event_wait);
+		return 0;
+	}
+
+	mutex_lock(&audio->lock);
+	switch (cmd) {
+	case AUDIO_START:
+		MM_DBG("AUDIO_START\n");
+		rc = audamrnb_enable(audio);
+		if (!rc) {
+			rc = wait_event_interruptible_timeout(audio->wait,
+				audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+				msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+			MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc);
+
+			if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS)
+				rc = -ENODEV;
+			else
+				rc = 0;
+		}
+		break;
+	case AUDIO_STOP:
+		MM_DBG("AUDIO_STOP\n");
+		rc = audamrnb_disable(audio);
+		audio->stopped = 1;
+		audamrnb_ioport_reset(audio);
+		audio->stopped = 0;
+		break;
+	case AUDIO_FLUSH:
+		MM_DBG("AUDIO_FLUSH\n");
+		audio->rflush = 1;
+		audio->wflush = 1;
+		audamrnb_ioport_reset(audio);
+		if (audio->running) {
+			audpp_flush(audio->dec_id);
+			rc = wait_event_interruptible(audio->write_wait,
+				!audio->wflush);
+			if (rc < 0) {
+				MM_ERR("AUDIO_FLUSH interrupted\n");
+				rc = -EINTR;
+			}
+		} 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;
+			}
+			audio->mfield = config.meta_field;
+			rc = 0;
+			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.meta_field = 0;
+			config.unused[0] = 0;
+			config.unused[1] = 0;
+			config.unused[2] = 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 = audio->pcm_feedback;
+			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.pcm_feedback != audio->pcm_feedback) {
+			MM_ERR("Not sufficient permission to"
+				 "change the playback mode\n");
+			rc = -EACCES;
+			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)) {
+			MM_DBG("allocate PCM buf %d\n",
+					config.buffer_count *
+					config.buffer_size);
+			audio->read_phys = pmem_kalloc(
+						config.buffer_size *
+						config.buffer_count,
+						PMEM_MEMTYPE_EBI1|
+						PMEM_ALIGNMENT_4K);
+			if (IS_ERR((void *)audio->read_phys)) {
+					rc = -ENOMEM;
+					break;
+			}
+			audio->read_data = ioremap(audio->read_phys,
+							config.buffer_size *
+							config.buffer_count);
+			if (!audio->read_data) {
+				MM_ERR("no mem for read buf\n");
+				rc = -ENOMEM;
+				pmem_kfree(audio->read_phys);
+			} else {
+				uint8_t index;
+				uint32_t offset = 0;
+				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;
+				}
+				MM_DBG("read buf: phy addr 0x%08x kernel \
+					addr 0x%08x\n", audio->read_phys,
+					(int)audio->read_data);
+				rc = 0;
+			}
+		} else {
+			rc = 0;
+		}
+		break;
+	}
+	default:
+		rc = -EINVAL;
+	}
+	mutex_unlock(&audio->lock);
+	return rc;
+}
+
+/* Only useful in tunnel-mode */
+static int audamrnb_fsync(struct file *file, int datasync)
+{
+	struct audio *audio = file->private_data;
+	int rc = 0;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+
+	if (!audio->running || audio->pcm_feedback) {
+		rc = -EINVAL;
+		goto done_nolock;
+	}
+
+	mutex_lock(&audio->write_lock);
+
+	rc = wait_event_interruptible(audio->write_wait,
+		(!audio->out[0].used &&
+		!audio->out[1].used &&
+		audio->out_needed) || audio->wflush);
+
+	if (rc < 0)
+		goto done;
+	else if (audio->wflush) {
+		rc = -EBUSY;
+		goto done;
+	}
+
+	/* pcm dmamiss message is sent continously
+	 * when decoder is starved so no race
+	 * condition concern
+	 */
+	audio->teos = 0;
+
+	rc = wait_event_interruptible(audio->write_wait,
+		audio->teos || audio->wflush);
+
+	if (audio->wflush)
+		rc = -EBUSY;
+
+done:
+	mutex_unlock(&audio->write_lock);
+done_nolock:
+	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);
+	MM_DBG("%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
+			 */
+			MM_DBG("read stop - partial frame\n");
+			break;
+		} else {
+			MM_DBG("read from in[%d]\n", audio->read_next);
+			/* order reads from the output buffer */
+			rmb();
+			if (copy_to_user
+			    (buf, audio->in[audio->read_next].data,
+			     audio->in[audio->read_next].used)) {
+				MM_ERR("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;
+			break;
+		}
+	}
+
+	/* 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;
+		MM_DBG("kick start pcm feedback again\n");
+		audamrnb_buffer_refresh(audio);
+	}
+
+	mutex_unlock(&audio->read_lock);
+
+	if (buf > start)
+		rc = buf - start;
+
+	MM_DBG("read %d bytes\n", rc);
+	return rc;
+}
+
+static int audamrnb_process_eos(struct audio *audio,
+		const char __user *buf_start, unsigned short mfield_size)
+{
+	int rc = 0;
+	struct buffer *frame;
+
+	frame = audio->out + audio->out_head;
+
+	rc = wait_event_interruptible(audio->write_wait,
+		(audio->out_needed &&
+		audio->out[0].used == 0 &&
+		audio->out[1].used == 0)
+		|| (audio->stopped)
+		|| (audio->wflush));
+
+	if (rc < 0)
+		goto done;
+	if (audio->stopped || audio->wflush) {
+		rc = -EBUSY;
+		goto done;
+	}
+
+	if (copy_from_user(frame->data, buf_start, mfield_size)) {
+		rc = -EFAULT;
+		goto done;
+	}
+
+	frame->mfield_sz = mfield_size;
+	audio->out_head ^= 1;
+	frame->used = mfield_size;
+	audamrnb_send_data(audio, 0);
+
+done:
+	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;
+	char *cpy_ptr;
+	int rc = 0, eos_condition = AUDAMRNB_EOS_NONE;
+	unsigned short mfield_size = 0;
+
+	MM_DBG("cnt=%d\n", count);
+
+	if (count & 1)
+		return -EINVAL;
+
+	mutex_lock(&audio->write_lock);
+	while (count > 0) {
+		frame = audio->out + audio->out_head;
+		cpy_ptr = frame->data;
+		rc = wait_event_interruptible(audio->write_wait,
+					      (frame->used == 0)
+						|| (audio->stopped)
+						|| (audio->wflush));
+
+		MM_DBG("buffer available\n");
+		if (rc < 0)
+			break;
+		if (audio->stopped || audio->wflush) {
+			rc = -EBUSY;
+			break;
+		}
+
+		if (audio->mfield) {
+			if (buf == start) {
+				/* Processing beginning of user buffer */
+				if (__get_user(mfield_size,
+					(unsigned short __user *) buf)) {
+					rc = -EFAULT;
+					break;
+				} else 	if (mfield_size > count) {
+					rc = -EINVAL;
+					break;
+				}
+				MM_DBG("mf offset_val %x\n", mfield_size);
+				if (copy_from_user(cpy_ptr, buf, mfield_size)) {
+					rc = -EFAULT;
+					break;
+				}
+				/* Check if EOS flag is set and buffer
+				 * contains just meta field
+				 */
+				if (cpy_ptr[AUDAMRNB_EOS_FLG_OFFSET] &
+						AUDAMRNB_EOS_FLG_MASK) {
+					MM_DBG("eos set\n");
+					eos_condition = AUDAMRNB_EOS_SET;
+					if (mfield_size == count) {
+						buf += mfield_size;
+						break;
+					} else
+					cpy_ptr[AUDAMRNB_EOS_FLG_OFFSET] &=
+							~AUDAMRNB_EOS_FLG_MASK;
+				}
+				cpy_ptr += mfield_size;
+				count -= mfield_size;
+				buf += mfield_size;
+			} else {
+				mfield_size = 0;
+				MM_DBG("continuous buffer\n");
+			}
+			frame->mfield_sz = mfield_size;
+		}
+
+		xfer = (count > (frame->size - mfield_size)) ?
+			(frame->size - mfield_size) : count;
+		if (copy_from_user(cpy_ptr, buf, xfer)) {
+			rc = -EFAULT;
+			break;
+		}
+
+		frame->used = (xfer + mfield_size);
+		audio->out_head ^= 1;
+		count -= xfer;
+		buf += xfer;
+
+		audamrnb_send_data(audio, 0);
+
+	}
+	if (eos_condition == AUDAMRNB_EOS_SET)
+		rc = audamrnb_process_eos(audio, start, mfield_size);
+	mutex_unlock(&audio->write_lock);
+	if (!rc) {
+		if (buf > start)
+			return buf - start;
+	}
+	return rc;
+}
+
+static int audamrnb_release(struct inode *inode, struct file *file)
+{
+	struct audio *audio = file->private_data;
+
+	MM_INFO("audio instance 0x%08x freeing\n", (int)audio);
+	mutex_lock(&audio->lock);
+	audamrnb_disable(audio);
+	if (audio->rmt_resource_released == 0)
+		rmt_put_resource(audio);
+	audamrnb_flush(audio);
+	audamrnb_flush_pcm_buf(audio);
+	msm_adsp_put(audio->audplay);
+	audpp_adec_free(audio->dec_id);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	unregister_early_suspend(&audio->suspend_ctl.node);
+#endif
+	audio->event_abort = 1;
+	wake_up(&audio->event_wait);
+	audamrnb_reset_event_queue(audio);
+	iounmap(audio->data);
+	pmem_kfree(audio->phys);
+	if (audio->read_data) {
+		iounmap(audio->read_data);
+		pmem_kfree(audio->read_phys);
+	}
+	mutex_unlock(&audio->lock);
+#ifdef CONFIG_DEBUG_FS
+	if (audio->dentry)
+		debugfs_remove(audio->dentry);
+#endif
+	kfree(audio);
+	return 0;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void audamrnb_post_event(struct audio *audio, int type,
+		union msm_audio_event_payload payload)
+{
+	struct audamrnb_event *e_node = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+
+	if (!list_empty(&audio->free_event_queue)) {
+		e_node = list_first_entry(&audio->free_event_queue,
+				struct audamrnb_event, list);
+		list_del(&e_node->list);
+	} else {
+		e_node = kmalloc(sizeof(struct audamrnb_event), GFP_ATOMIC);
+		if (!e_node) {
+			MM_ERR("No mem to post event %d\n", type);
+			spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+			return;
+		}
+	}
+
+	e_node->event_type = type;
+	e_node->payload = payload;
+
+	list_add_tail(&e_node->list, &audio->event_queue);
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+	wake_up(&audio->event_wait);
+}
+
+static void audamrnb_suspend(struct early_suspend *h)
+{
+	struct audamrnb_suspend_ctl *ctl =
+		container_of(h, struct audamrnb_suspend_ctl, node);
+	union msm_audio_event_payload payload;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	audamrnb_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload);
+}
+
+static void audamrnb_resume(struct early_suspend *h)
+{
+	struct audamrnb_suspend_ctl *ctl =
+		container_of(h, struct audamrnb_suspend_ctl, node);
+	union msm_audio_event_payload payload;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	audamrnb_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload);
+}
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t audamrnb_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t audamrnb_debug_read(struct file *file, char __user *buf,
+					size_t count, loff_t *ppos)
+{
+	const int debug_bufmax = 1024;
+	static char buffer[1024];
+	int n = 0, i;
+	struct audio *audio = file->private_data;
+
+	mutex_lock(&audio->lock);
+	n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"enabled %d\n", audio->enabled);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"stopped %d\n", audio->stopped);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"pcm_feedback %d\n", audio->pcm_feedback);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"out_buf_sz %d\n", audio->out[0].size);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"pcm_buf_count %d \n", audio->pcm_buf_count);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"pcm_buf_sz %d \n", audio->in[0].size);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"volume %x \n", audio->vol_pan.volume);
+	mutex_unlock(&audio->lock);
+	/* Following variables are only useful for debugging when
+	 * when playback halts unexpectedly. Thus, no mutual exclusion
+	 * enforced
+	 */
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"wflush %d\n", audio->wflush);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"rflush %d\n", audio->rflush);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"running %d \n", audio->running);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"dec state %d \n", audio->dec_state);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"out_needed %d \n", audio->out_needed);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"out_head %d \n", audio->out_head);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"out_tail %d \n", audio->out_tail);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"out[0].used %d \n", audio->out[0].used);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"out[1].used %d \n", audio->out[1].used);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"buffer_refresh %d \n", audio->buf_refresh);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"read_next %d \n", audio->read_next);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"fill_next %d \n", audio->fill_next);
+	for (i = 0; i < audio->pcm_buf_count; i++)
+		n += scnprintf(buffer + n, debug_bufmax - n,
+				"in[%d].used %d \n", i, audio->in[i].used);
+	buffer[n] = 0;
+	return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+
+static const struct file_operations audamrnb_debug_fops = {
+	.read = audamrnb_debug_read,
+	.open = audamrnb_debug_open,
+};
+#endif
+
+static int audamrnb_open(struct inode *inode, struct file *file)
+{
+	struct audio *audio = NULL;
+	int rc, dec_attrb, decid, i;
+	struct audamrnb_event *e_node = NULL;
+#ifdef CONFIG_DEBUG_FS
+	/* 4 bytes represents decoder number, 1 byte for terminate string */
+	char name[sizeof "msm_amrnb_" + 5];
+#endif
+
+	/* Allocate Mem for audio instance */
+	audio = kzalloc(sizeof(struct audio), GFP_KERNEL);
+	if (!audio) {
+		MM_ERR("no memory to allocate audio instance \n");
+		rc = -ENOMEM;
+		goto done;
+	}
+	MM_INFO("audio instance 0x%08x created\n", (int)audio);
+
+	/* Allocate the decoder */
+	dec_attrb = AUDDEC_DEC_AMRNB;
+	if ((file->f_mode & FMODE_WRITE) &&
+			(file->f_mode & FMODE_READ)) {
+		dec_attrb |= MSM_AUD_MODE_NONTUNNEL;
+		audio->pcm_feedback = NON_TUNNEL_MODE_PLAYBACK;
+	} else if ((file->f_mode & FMODE_WRITE) &&
+			!(file->f_mode & FMODE_READ)) {
+		dec_attrb |= MSM_AUD_MODE_TUNNEL;
+		audio->pcm_feedback = TUNNEL_MODE_PLAYBACK;
+	} else {
+		kfree(audio);
+		rc = -EACCES;
+		goto done;
+	}
+
+	decid = audpp_adec_alloc(dec_attrb, &audio->module_name,
+			&audio->queue_id);
+
+	if (decid < 0) {
+		MM_ERR("No free decoder available, freeing instance 0x%08x\n",
+				(int)audio);
+		rc = -ENODEV;
+		kfree(audio);
+		goto done;
+	}
+
+	audio->dec_id = decid & MSM_AUD_DECODER_MASK;
+
+	audio->phys = pmem_kalloc(DMASZ, PMEM_MEMTYPE_EBI1|PMEM_ALIGNMENT_4K);
+	if (IS_ERR((void *)audio->phys)) {
+		MM_ERR("could not allocate write buffers, freeing instance \
+				0x%08x\n", (int)audio);
+		rc = -ENOMEM;
+		audpp_adec_free(audio->dec_id);
+		kfree(audio);
+		goto done;
+	} else {
+		audio->data = ioremap(audio->phys, DMASZ);
+		if (!audio->data) {
+			MM_ERR("could not allocate write buffers, freeing \
+					instance 0x%08x freeing\n", (int)audio);
+			rc = -ENOMEM;
+			pmem_kfree(audio->phys);
+			audpp_adec_free(audio->dec_id);
+			kfree(audio);
+			goto done;
+		}
+		MM_DBG("write buf: phy addr 0x%08x kernel addr \
+				0x%08x\n", audio->phys, (int)audio->data);
+	}
+
+	if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) {
+		rc = audmgr_open(&audio->audmgr);
+		if (rc) {
+			MM_ERR("audmgr open failed, freeing instance \
+					0x%08x\n", (int)audio);
+			goto err;
+		}
+	}
+
+	rc = msm_adsp_get(audio->module_name, &audio->audplay,
+		&audplay_adsp_ops_amrnb, audio);
+	if (rc) {
+		MM_ERR("failed to get %s module, freeing instance 0x%08x\n",
+				audio->module_name, (int)audio);
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			audmgr_close(&audio->audmgr);
+		goto err;
+	}
+
+	rc = rmt_get_resource(audio);
+	if (rc) {
+		MM_ERR("ADSP resources are not available for AMRNB session \
+			 0x%08x on decoder: %d\n", (int)audio, audio->dec_id);
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			audmgr_close(&audio->audmgr);
+		msm_adsp_put(audio->audplay);
+		goto err;
+	}
+
+	mutex_init(&audio->lock);
+	mutex_init(&audio->write_lock);
+	mutex_init(&audio->read_lock);
+	mutex_init(&audio->get_event_lock);
+	spin_lock_init(&audio->dsp_lock);
+	spin_lock_init(&audio->event_queue_lock);
+	INIT_LIST_HEAD(&audio->free_event_queue);
+	INIT_LIST_HEAD(&audio->event_queue);
+	init_waitqueue_head(&audio->write_wait);
+	init_waitqueue_head(&audio->read_wait);
+	init_waitqueue_head(&audio->wait);
+	init_waitqueue_head(&audio->event_wait);
+
+	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->vol_pan.volume = 0x2000;
+
+	audamrnb_flush(audio);
+
+	file->private_data = audio;
+	audio->opened = 1;
+#ifdef CONFIG_DEBUG_FS
+	snprintf(name, sizeof name, "msm_amrnb_%04x", audio->dec_id);
+	audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+			NULL, (void *) audio, &audamrnb_debug_fops);
+
+	if (IS_ERR(audio->dentry))
+		MM_DBG("debugfs_create_file failed\n");
+#endif
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
+	audio->suspend_ctl.node.resume = audamrnb_resume;
+	audio->suspend_ctl.node.suspend = audamrnb_suspend;
+	audio->suspend_ctl.audio = audio;
+	register_early_suspend(&audio->suspend_ctl.node);
+#endif
+	for (i = 0; i < AUDAMRNB_EVENT_NUM; i++) {
+		e_node = kmalloc(sizeof(struct audamrnb_event), GFP_KERNEL);
+		if (e_node)
+			list_add_tail(&e_node->list, &audio->free_event_queue);
+		else {
+			MM_ERR("event pkt alloc failed\n");
+			break;
+		}
+	}
+done:
+	return rc;
+err:
+	iounmap(audio->data);
+	pmem_kfree(audio->phys);
+	audpp_adec_free(audio->dec_id);
+	kfree(audio);
+	return rc;
+}
+
+static const 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,
+	.fsync = audamrnb_fsync,
+};
+
+struct miscdevice audio_amrnb_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "msm_amrnb",
+	.fops = &audio_amrnb_fops,
+};
+
+static int __init audamrnb_init(void)
+{
+	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");
diff --git a/arch/arm/mach-msm/qdsp5/audio_amrnb_in.c b/arch/arm/mach-msm/qdsp5/audio_amrnb_in.c
new file mode 100644
index 0000000..8d99a3d
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audio_amrnb_in.c
@@ -0,0 +1,669 @@
+/* arch/arm/mach-msm/qdsp5/audio_amrnb_in.c
+ *
+ * amrnb encoder device
+ *
+ * Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This code is based in part on arch/arm/mach-msm/qdsp5/audio_in.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/debugfs.h>
+#include <linux/delay.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+#include <mach/msm_rpcrouter.h>
+#include <linux/msm_audio_amrnb.h>
+
+#include "audmgr.h"
+
+#include <mach/qdsp5/qdsp5audpreproccmdi.h>
+#include <mach/qdsp5/qdsp5audpreprocmsg.h>
+#include <mach/qdsp5/qdsp5audreccmdi.h>
+#include <mach/qdsp5/qdsp5audrecmsg.h>
+#include <mach/debug_mm.h>
+
+/* FRAME_NUM must be a power of two */
+#define FRAME_NUM		(8)
+#define FRAME_SIZE		(22 * 2)
+#define DMASZ 			(FRAME_SIZE * FRAME_NUM)
+
+struct buffer {
+	void *data;
+	uint32_t size;
+	uint32_t read;
+	uint32_t addr;
+};
+
+struct audio_amrnb_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;
+
+	uint16_t audrec_obj_idx;
+
+	/* configuration to use on enable */
+	uint32_t buffer_size;
+	uint32_t enc_type; /* 0 for WAV ,1 for AAC,10 for AMRNB */
+	struct msm_audio_amrnb_enc_config amrnb_enc_cfg;
+
+	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() */
+
+	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 */
+
+};
+
+static int audio_amrnb_in_dsp_enable(struct audio_amrnb_in *audio, int enable);
+static int audio_amrnb_in_encmem_config(struct audio_amrnb_in *audio);
+static int audio_amrnb_in_encparam_config(struct audio_amrnb_in *audio);
+static int audio_amrnb_in_dsp_read_buffer(struct audio_amrnb_in *audio,
+					uint32_t read_cnt);
+static void audio_amrnb_in_flush(struct audio_amrnb_in *audio);
+static void audio_amrnb_in_dsp_event(void *data, unsigned id, uint16_t *msg);
+
+static struct audio_amrnb_in the_audio_amrnb_in;
+
+/* must be called with audio->lock held */
+static int audio_amrnb_in_enable(struct audio_amrnb_in *audio)
+{
+	struct audmgr_config cfg;
+	int rc;
+
+	if (audio->enabled)
+		return 0;
+
+	cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_8000;
+	cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE;
+	cfg.def_method = RPC_AUD_DEF_METHOD_RECORD;
+	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 (audrectask_enable(audio->enc_type, audio_amrnb_in_dsp_event,
+		audio)) {
+		audmgr_disable(&audio->audmgr);
+		MM_ERR("audrec_enable failed\n");
+		return -ENODEV;
+	}
+
+	audio->enabled = 1;
+	audio_amrnb_in_dsp_enable(audio, 1);
+
+	return 0;
+}
+
+/* must be called with audio->lock held */
+static int audio_amrnb_in_disable(struct audio_amrnb_in *audio)
+{
+	if (audio->enabled) {
+		audio->enabled = 0;
+		audio_amrnb_in_dsp_enable(audio, 0);
+		wake_up(&audio->wait);
+		audrectask_disable(audio->enc_type, audio);
+		audmgr_disable(&audio->audmgr);
+	}
+	return 0;
+}
+
+/* ------------------- dsp --------------------- */
+struct audio_amrnb_in_frame {
+	uint16_t frame_count_lsw;
+	uint16_t frame_count_msw;
+	uint16_t frame_length;
+	uint16_t erased_pcm;
+	unsigned char raw_bitstream[];
+} __attribute__((packed));
+
+static void audio_amrnb_in_get_dsp_frames(struct audio_amrnb_in *audio)
+{
+	struct audio_amrnb_in_frame *frame;
+	uint32_t index;
+	unsigned long flags;
+	index = audio->in_head;
+
+	frame = (void *) (((char *)audio->in[index].data) -
+		sizeof(*frame));
+	spin_lock_irqsave(&audio->dsp_lock, flags);
+	audio->in[index].size = FRAME_SIZE - (sizeof(*frame)); /* Send
+			Complete Transcoded Data, not actual frame part  */
+
+	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_amrnb_in_dsp_read_buffer(audio, audio->dsp_cnt++);
+	spin_unlock_irqrestore(&audio->dsp_lock, flags);
+
+	wake_up(&audio->wait);
+}
+
+static void audio_amrnb_in_dsp_event(void *data, unsigned id, uint16_t *msg)
+{
+	struct audio_amrnb_in *audio = data;
+
+	switch (id) {
+	case AUDREC_MSG_CMD_CFG_DONE_MSG:
+		MM_DBG("CFG_DONE_MSG\n");
+		if (msg[0] & AUDREC_MSG_CFG_DONE_ENC_ENA) {
+			audio->audrec_obj_idx = msg[1];
+			audio_amrnb_in_encmem_config(audio);
+		} else {
+			audio->running = 0;
+		}
+		break;
+	case AUDREC_MSG_CMD_AREC_MEM_CFG_DONE_MSG: {
+		MM_DBG("AREC_MEM_CFG_DONE_MSG\n");
+		if (msg[0] == audio->audrec_obj_idx)
+			audio_amrnb_in_encparam_config(audio);
+		else
+			MM_ERR("AREC_MEM_CFG_DONE_MSG ERR\n");
+		break;
+	}
+	case AUDREC_MSG_CMD_AREC_PARAM_CFG_DONE_MSG: {
+		MM_DBG("AREC_PARAM_CFG_DONE_MSG\n");
+		if (msg[0] == audio->audrec_obj_idx)
+			audio->running = 1;
+		else
+			MM_ERR("AREC_PARAM_CFG_DONE_MSG ERR\n");
+		break;
+	}
+	case AUDREC_MSG_PACKET_READY_MSG: {
+		MM_DBG("AUDREC_MSG_PACKET_READY_MSG\n");
+		if (msg[0] == audio->audrec_obj_idx)
+			audio_amrnb_in_get_dsp_frames(audio);
+		else
+			MM_ERR("PACKET_READY_MSG ERR\n");
+		break;
+	}
+	case AUDREC_MSG_FATAL_ERR_MSG: {
+		MM_ERR("FATAL_ERR_MSG %x\n", msg[0]);
+		break;
+	}
+	default:
+		MM_ERR("unknown event %d\n", id);
+	}
+}
+
+static int audio_amrnb_in_dsp_enable(struct audio_amrnb_in *audio, int enable)
+{
+	struct audrec_cmd_enc_cfg cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDREC_CMD_ENC_CFG;
+	cmd.audrec_enc_type = (audio->enc_type) |
+	(enable ? AUDREC_CMD_ENC_ENA : AUDREC_CMD_ENC_DIS);
+	/* Don't care on enable, required on disable */
+	cmd.audrec_obj_idx = audio->audrec_obj_idx;
+
+	return audrectask_send_cmdqueue(&cmd, sizeof(cmd));
+}
+
+static int audio_amrnb_in_encmem_config(struct audio_amrnb_in *audio)
+{
+	struct audrec_cmd_arecmem_cfg cmd;
+	uint16_t *data = (void *) audio->data;
+	unsigned cnt;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	cmd.cmd_id = AUDREC_CMD_ARECMEM_CFG;
+	cmd.audrec_obj_idx = audio->audrec_obj_idx;
+	/* Rate at which packet complete message comes */
+	cmd.audrec_up_pkt_intm_cnt = 1;
+	cmd.audrec_extpkt_buffer_msw = audio->phys >> 16;
+	cmd.audrec_extpkt_buffer_lsw = audio->phys;
+	/* Max Buffer no available for frames */
+	cmd.audrec_extpkt_buffer_num = FRAME_NUM;
+
+	/* prepare buffer pointers:
+	 * 4 halfword header + Frame Raw Packet (20ms data)
+	 */
+	for (cnt = 0; cnt < FRAME_NUM; cnt++) {
+		audio->in[cnt].data = data + 4; /* Pointer to Raw Packet part*/
+		MM_DBG(" audio->in[%d].data = %x \n", cnt,
+				(unsigned int)audio->in[cnt].data);
+		data += 22; /* Point to next Frame buffer */
+	}
+
+	return audrectask_send_cmdqueue(&cmd, sizeof(cmd));
+}
+
+static int audio_amrnb_in_encparam_config(struct audio_amrnb_in *audio)
+{
+	struct audrec_cmd_arecparam_amrnb_cfg cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	cmd.common.cmd_id = AUDREC_CMD_ARECPARAM_CFG;
+	cmd.common.audrec_obj_idx = audio->audrec_obj_idx;
+	cmd.samp_rate_idx = 0xb; /* 8k Sampling rate */
+	cmd.voicememoencweight1 = audio->amrnb_enc_cfg.voicememoencweight1;
+	cmd.voicememoencweight2 = audio->amrnb_enc_cfg.voicememoencweight2;
+	cmd.voicememoencweight3 = audio->amrnb_enc_cfg.voicememoencweight3;
+	cmd.voicememoencweight4 = audio->amrnb_enc_cfg.voicememoencweight4;
+	cmd.update_mode = 0x8000 | 0x0000;
+	cmd.dtx_mode = audio->amrnb_enc_cfg.dtx_mode_enable;
+	cmd.test_mode = audio->amrnb_enc_cfg.test_mode_enable;
+	cmd.used_mode = audio->amrnb_enc_cfg.enc_mode;
+
+	MM_DBG("cmd.common.cmd_id = 0x%4x\n", cmd.common.cmd_id);
+	MM_DBG("cmd.common.audrec_obj_idx = 0x%4x\n",
+			cmd.common.audrec_obj_idx);
+	MM_DBG("cmd.samp_rate_idx = 0x%4x\n", cmd.samp_rate_idx);
+	MM_DBG("cmd.voicememoencweight1 = 0x%4x\n",
+			cmd.voicememoencweight1);
+	MM_DBG("cmd.voicememoencweight2 = 0x%4x\n",
+			cmd.voicememoencweight2);
+	MM_DBG("cmd.voicememoencweight3 = 0x%4x\n",
+			cmd.voicememoencweight3);
+	MM_DBG("cmd.voicememoencweight4 = 0x%4x\n",
+			cmd.voicememoencweight4);
+	MM_DBG("cmd.update_mode = 0x%4x\n", cmd.update_mode);
+	MM_DBG("cmd.dtx_mode = 0x%4x\n", cmd.dtx_mode);
+	MM_DBG("cmd.test_mode = 0x%4x\n", cmd.test_mode);
+	MM_DBG("cmd.used_mode = 0x%4x\n", cmd.used_mode);
+
+	return audrectask_send_cmdqueue(&cmd, sizeof(cmd));
+}
+
+static int audio_amrnb_in_dsp_read_buffer(struct audio_amrnb_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;
+	cmd.type = audio->audrec_obj_idx;
+	cmd.curr_rec_count_msw = read_cnt >> 16;
+	cmd.curr_rec_count_lsw = read_cnt;
+
+	return audrectask_send_bitstreamqueue(&cmd, sizeof(cmd));
+}
+
+static void audio_amrnb_in_flush(struct audio_amrnb_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;
+	}
+}
+
+
+/* ------------------- device --------------------- */
+static long audio_amrnb_in_ioctl(struct file *file,
+				unsigned int cmd, unsigned long arg)
+{
+	struct audio_amrnb_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_amrnb_in_enable(audio);
+		break;
+	case AUDIO_STOP:
+		rc = audio_amrnb_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_amrnb_in_flush(audio);
+			mutex_unlock(&audio->read_lock);
+		}
+	case AUDIO_SET_CONFIG: {
+		rc = -EINVAL; /* Buffer size better to come from upper */
+		break;
+	}
+	case AUDIO_GET_CONFIG: {
+		struct msm_audio_config cfg;
+		cfg.buffer_size = audio->buffer_size;
+		cfg.buffer_count = FRAME_NUM;
+		cfg.sample_rate = 8000;
+		cfg.channel_count = 1;
+		cfg.type = 10;
+		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;
+	}
+	case AUDIO_GET_AMRNB_ENC_CONFIG: {
+		if (copy_to_user((void *)arg, &audio->amrnb_enc_cfg,
+			sizeof(audio->amrnb_enc_cfg)))
+			rc = -EFAULT;
+		else
+			rc = 0;
+		break;
+	}
+	case AUDIO_SET_AMRNB_ENC_CONFIG: {
+		struct msm_audio_amrnb_enc_config cfg;
+		if (copy_from_user
+			(&cfg, (void *)arg, sizeof(cfg))) {
+			rc = -EFAULT;
+		} else
+			rc = 0;
+		audio->amrnb_enc_cfg.voicememoencweight1 =
+					cfg.voicememoencweight1;
+		audio->amrnb_enc_cfg.voicememoencweight2 =
+					cfg.voicememoencweight2;
+		audio->amrnb_enc_cfg.voicememoencweight3 =
+					cfg.voicememoencweight3;
+		audio->amrnb_enc_cfg.voicememoencweight4 =
+					cfg.voicememoencweight4;
+		audio->amrnb_enc_cfg.dtx_mode_enable = cfg.dtx_mode_enable;
+		audio->amrnb_enc_cfg.test_mode_enable = cfg.test_mode_enable;
+		audio->amrnb_enc_cfg.enc_mode = cfg.enc_mode;
+		/* Run time change of Param */
+		break;
+	}
+	default:
+		rc = -EINVAL;
+	}
+	mutex_unlock(&audio->lock);
+	return rc;
+}
+
+static ssize_t audio_amrnb_in_read(struct file *file,
+				char __user *buf,
+				size_t count, loff_t *pos)
+{
+	struct audio_amrnb_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) {
+			dma_coherent_post_ops();
+			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;
+		} else {
+			MM_ERR("short read\n");
+			break;
+		}
+	}
+	mutex_unlock(&audio->read_lock);
+
+	if (buf > start)
+		return buf - start;
+
+	return rc;
+}
+
+static ssize_t audio_amrnb_in_write(struct file *file,
+				const char __user *buf,
+				size_t count, loff_t *pos)
+{
+	return -EINVAL;
+}
+
+static int audio_amrnb_in_release(struct inode *inode, struct file *file)
+{
+	struct audio_amrnb_in *audio = file->private_data;
+
+	mutex_lock(&audio->lock);
+	audio_amrnb_in_disable(audio);
+	audio_amrnb_in_flush(audio);
+	audio->opened = 0;
+	if (audio->data) {
+		dma_free_coherent(NULL, DMASZ, audio->data, audio->phys);
+		audio->data = NULL;
+	}
+	mutex_unlock(&audio->lock);
+	return 0;
+}
+
+
+static int audio_amrnb_in_open(struct inode *inode, struct file *file)
+{
+	struct audio_amrnb_in *audio = &the_audio_amrnb_in;
+	int rc;
+
+	mutex_lock(&audio->lock);
+	if (audio->opened) {
+		rc = -EBUSY;
+		goto done;
+	}
+
+	if (!audio->data) {
+		audio->data = dma_alloc_coherent(NULL, DMASZ,
+				&audio->phys, GFP_KERNEL);
+		if (!audio->data) {
+			rc = -ENOMEM;
+			goto done;
+		}
+	}
+
+	rc = audmgr_open(&audio->audmgr);
+	if (rc)
+		goto err;
+	audio->buffer_size = FRAME_SIZE - 8;
+	audio->enc_type = 10;
+	audio->amrnb_enc_cfg.voicememoencweight1 = 0x0000;
+	audio->amrnb_enc_cfg.voicememoencweight2 = 0x0000;
+	audio->amrnb_enc_cfg.voicememoencweight3 = 0x4000;
+	audio->amrnb_enc_cfg.voicememoencweight4 = 0x0000;
+	audio->amrnb_enc_cfg.dtx_mode_enable = 0;
+	audio->amrnb_enc_cfg.test_mode_enable = 0;
+	audio->amrnb_enc_cfg.enc_mode = 7;
+	audio->dsp_cnt = 0;
+	audio->stopped = 0;
+
+	audio_amrnb_in_flush(audio);
+
+	file->private_data = audio;
+	audio->opened = 1;
+	rc = 0;
+	goto done;
+
+err:
+	dma_free_coherent(NULL, DMASZ, audio->data, audio->phys);
+done:
+	mutex_unlock(&audio->lock);
+	return rc;
+}
+
+static const struct file_operations audio_fops = {
+	.owner		= THIS_MODULE,
+	.open		= audio_amrnb_in_open,
+	.release	= audio_amrnb_in_release,
+	.read		= audio_amrnb_in_read,
+	.write		= audio_amrnb_in_write,
+	.unlocked_ioctl	= audio_amrnb_in_ioctl,
+};
+
+struct miscdevice audio_amrnb_in_misc = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "msm_amrnb_in",
+	.fops	= &audio_fops,
+};
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t audamrnb_in_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t audamrnb_in_debug_read(struct file *file, char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	const int debug_bufmax = 1024;
+	static char buffer[1024];
+	int n = 0, i;
+	struct audio_amrnb_in *audio = file->private_data;
+
+	mutex_lock(&audio->lock);
+	n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"enabled %d\n", audio->enabled);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"stopped %d\n", audio->stopped);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"audrec_obj_idx %d\n", audio->audrec_obj_idx);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"dsp_cnt %d \n", audio->dsp_cnt);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"in_count %d \n", audio->in_count);
+	for (i = 0; i < FRAME_NUM; i++)
+		n += scnprintf(buffer + n, debug_bufmax - n,
+			"audio->in[%d].size %d \n", i, audio->in[i].size);
+	mutex_unlock(&audio->lock);
+	/* Following variables are only useful for debugging when
+	 * when record halts unexpectedly. Thus, no mutual exclusion
+	 * enforced
+	 */
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"running %d \n", audio->running);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"buffer_size %d \n", audio->buffer_size);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"in_head %d \n", audio->in_head);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"in_tail %d \n", audio->in_tail);
+	buffer[n] = 0;
+	return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+
+static const struct file_operations audamrnb_in_debug_fops = {
+	.read = audamrnb_in_debug_read,
+	.open = audamrnb_in_debug_open,
+};
+#endif
+
+static int __init audio_amrnb_in_init(void)
+{
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *dentry;
+#endif
+
+	mutex_init(&the_audio_amrnb_in.lock);
+	mutex_init(&the_audio_amrnb_in.read_lock);
+	spin_lock_init(&the_audio_amrnb_in.dsp_lock);
+	init_waitqueue_head(&the_audio_amrnb_in.wait);
+
+#ifdef CONFIG_DEBUG_FS
+	dentry = debugfs_create_file("msm_amrnb_in", S_IFREG | S_IRUGO, NULL,
+		(void *) &the_audio_amrnb_in, &audamrnb_in_debug_fops);
+
+	if (IS_ERR(dentry))
+		MM_ERR("debugfs_create_file failed\n");
+#endif
+	return misc_register(&audio_amrnb_in_misc);
+}
+
+static void __exit audio_amrnb_in_exit(void)
+{
+	misc_deregister(&audio_amrnb_in_misc);
+}
+
+module_init(audio_amrnb_in_init);
+module_exit(audio_amrnb_in_exit);
+
+MODULE_DESCRIPTION("MSM AMRNB Encoder driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5/audio_amrwb.c b/arch/arm/mach-msm/qdsp5/audio_amrwb.c
new file mode 100644
index 0000000..dcd3c05
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audio_amrwb.c
@@ -0,0 +1,1680 @@
+/* linux/arch/arm/mach-msm/qdsp5/audio_amrwb.c
+ *
+ * amrwb audio decoder device
+ *
+ * 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
+ * Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * 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/debugfs.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/earlysuspend.h>
+#include <linux/android_pmem.h>
+#include <linux/slab.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 <mach/qdsp5/qdsp5rmtcmdi.h>
+#include <mach/debug_mm.h>
+
+#define BUFSZ 4110 /* Hold minimum 700ms voice data and 14 bytes of meta in*/
+#define DMASZ (BUFSZ * 2)
+
+#define AUDPLAY_INVALID_READ_PTR_OFFSET	0xFFFF
+#define AUDDEC_DEC_AMRWB 11
+
+#define PCM_BUFSZ_MIN 8216 /* 100ms worth of data and 24 bytes of meta out*/
+#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
+
+#define AUDAMRWB_METAFIELD_MASK 0xFFFF0000
+#define AUDAMRWB_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */
+#define AUDAMRWB_EOS_FLG_MASK 0x01
+#define AUDAMRWB_EOS_NONE 0x0 /* No EOS detected */
+#define AUDAMRWB_EOS_SET 0x1 /* EOS set in meta field */
+
+#define AUDAMRWB_EVENT_NUM 10 /* Default number of pre-allocated event pkts */
+
+struct buffer {
+	void *data;
+	unsigned size;
+	unsigned used;		/* Input usage actual DSP produced PCM size  */
+	unsigned addr;
+	unsigned short mfield_sz; /*only useful for data has meta field */
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+struct audamrwb_suspend_ctl {
+	struct early_suspend node;
+	struct audio *audio;
+};
+#endif
+
+struct audamrwb_event{
+	struct list_head list;
+	int event_type;
+	union msm_audio_event_payload payload;
+};
+
+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 */
+	int32_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;
+
+	/* configuration to use on next enable */
+	uint32_t out_sample_rate;
+	uint32_t out_channel_mode;
+
+	/* data allocated for various buffers */
+	char *data;
+	int32_t phys; /* physical address of write buffer */
+
+	int mfield; /* meta field embedded in data */
+	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 rmt_resource_released;
+	int teos; /* valid only if tunnel mode & no data left for decoder */
+	enum msm_aud_decoder_state dec_state;	/* Represents decoder state */
+	int reserved; /* A byte is being reserved */
+	char rsv_byte; /* Handle odd length user data */
+
+	const char *module_name;
+	unsigned queue_id;
+	uint16_t dec_id;
+	uint32_t read_ptr_offset;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	struct audamrwb_suspend_ctl suspend_ctl;
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *dentry;
+#endif
+
+	wait_queue_head_t wait;
+	struct list_head free_event_queue;
+	struct list_head event_queue;
+	wait_queue_head_t event_wait;
+	spinlock_t event_queue_lock;
+	struct mutex get_event_lock;
+	int event_abort;
+
+	int eq_enable;
+	int eq_needs_commit;
+	audpp_cmd_cfg_object_params_eqalizer eq;
+	audpp_cmd_cfg_object_params_volume vol_pan;
+};
+
+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 audamrwb_send_data(struct audio *audio, unsigned needed);
+static void audamrwb_config_hostpcm(struct audio *audio);
+static void audamrwb_buffer_refresh(struct audio *audio);
+static void audamrwb_dsp_event(void *private, unsigned id, uint16_t *msg);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void audamrwb_post_event(struct audio *audio, int type,
+		union msm_audio_event_payload payload);
+#endif
+
+static int rmt_put_resource(struct audio *audio)
+{
+	struct aud_codec_config_cmd cmd;
+	unsigned short client_idx;
+
+	cmd.cmd_id = RM_CMD_AUD_CODEC_CFG;
+	cmd.client_id = RM_AUD_CLIENT_ID;
+	cmd.task_id = audio->dec_id;
+	cmd.enable = RMT_DISABLE;
+	cmd.dec_type = AUDDEC_DEC_AMRWB;
+	client_idx = ((cmd.client_id << 8) | cmd.task_id);
+
+	return put_adsp_resource(client_idx, &cmd, sizeof(cmd));
+}
+
+static int rmt_get_resource(struct audio *audio)
+{
+	struct aud_codec_config_cmd cmd;
+	unsigned short client_idx;
+
+	cmd.cmd_id = RM_CMD_AUD_CODEC_CFG;
+	cmd.client_id = RM_AUD_CLIENT_ID;
+	cmd.task_id = audio->dec_id;
+	cmd.enable = RMT_ENABLE;
+	cmd.dec_type = AUDDEC_DEC_AMRWB;
+	client_idx = ((cmd.client_id << 8) | cmd.task_id);
+
+	return get_adsp_resource(client_idx, &cmd, sizeof(cmd));
+}
+
+/* must be called with audio->lock held */
+static int audamrwb_enable(struct audio *audio)
+{
+	struct audmgr_config cfg;
+	int rc;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+
+	if (audio->enabled)
+		return 0;
+
+	if (audio->rmt_resource_released == 1) {
+		audio->rmt_resource_released = 0;
+		rc = rmt_get_resource(audio);
+		if (rc) {
+			MM_ERR("ADSP resources are not available for AMRWB \
+				session 0x%08x on decoder: %d\n Ignoring \
+				error and going ahead with the playback\n",
+				(int)audio, audio->dec_id);
+		}
+	}
+
+	audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+	audio->out_tail = 0;
+	audio->out_needed = 0;
+
+	if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) {
+		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_WB;
+		cfg.snd_method = RPC_SND_METHOD_MIDI;
+
+		rc = audmgr_enable(&audio->audmgr, &cfg);
+		if (rc < 0)
+			return rc;
+	}
+
+	if (msm_adsp_enable(audio->audplay)) {
+		MM_ERR("msm_adsp_enable(audplay) failed\n");
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			audmgr_disable(&audio->audmgr);
+		return -ENODEV;
+	}
+
+	if (audpp_enable(audio->dec_id, audamrwb_dsp_event, audio)) {
+		MM_ERR("audpp_enable() failed\n");
+		msm_adsp_disable(audio->audplay);
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			audmgr_disable(&audio->audmgr);
+		return -ENODEV;
+	}
+	audio->enabled = 1;
+	return 0;
+}
+
+/* must be called with audio->lock held */
+static int audamrwb_disable(struct audio *audio)
+{
+	int rc = 0;
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	if (audio->enabled) {
+		audio->enabled = 0;
+		audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+		auddec_dsp_config(audio, 0);
+		rc = wait_event_interruptible_timeout(audio->wait,
+				audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+				msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+		if (rc == 0)
+			rc = -ETIMEDOUT;
+		else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE)
+			rc = -EFAULT;
+		else
+			rc = 0;
+		wake_up(&audio->write_wait);
+		wake_up(&audio->read_wait);
+		msm_adsp_disable(audio->audplay);
+		audpp_disable(audio->dec_id, audio);
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			audmgr_disable(&audio->audmgr);
+		audio->out_needed = 0;
+		rmt_put_resource(audio);
+		audio->rmt_resource_released = 1;
+	}
+	return rc;
+}
+
+/* ------------------- dsp --------------------- */
+static void audamrwb_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]) {
+			MM_DBG("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 {
+			MM_ERR("expected=%x ret=%x\n",
+					audio->in[audio->fill_next].addr,
+					payload[1 + index * 2]);
+			break;
+		}
+	}
+	if (audio->in[audio->fill_next].used == 0) {
+		audamrwb_buffer_refresh(audio);
+	} else {
+		MM_DBG("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));
+
+	MM_DBG("msg_id=%x\n", id);
+
+	switch (id) {
+	case AUDPLAY_MSG_DEC_NEEDS_DATA:
+		audamrwb_send_data(audio, 1);
+		break;
+
+	case AUDPLAY_MSG_BUFFER_UPDATE:
+		audamrwb_update_pcm_buf_entry(audio, msg);
+		break;
+
+	default:
+		MM_ERR("unexpected message from decoder\n");
+	}
+}
+
+static void audamrwb_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: {
+				uint16_t reason = msg[2];
+				MM_DBG("decoder status:sleep reason=0x%04x\n",
+					reason);
+				if ((reason == AUDPP_MSG_REASON_MEM)
+					|| (reason ==
+					AUDPP_MSG_REASON_NODECODER)) {
+					audio->dec_state =
+						MSM_AUD_DECODER_STATE_FAILURE;
+					wake_up(&audio->wait);
+				} else if (reason == AUDPP_MSG_REASON_NONE) {
+					/* decoder is in disable state */
+					audio->dec_state =
+						MSM_AUD_DECODER_STATE_CLOSE;
+					wake_up(&audio->wait);
+				}
+				break;
+			}
+			case AUDPP_DEC_STATUS_INIT:
+				MM_DBG("decoder status: init\n");
+				if (audio->pcm_feedback)
+					audpp_cmd_cfg_routing_mode(audio);
+				else
+					audpp_cmd_cfg_adec_params(audio);
+				break;
+
+			case AUDPP_DEC_STATUS_CFG:
+				MM_DBG("decoder status: cfg\n");
+				break;
+			case AUDPP_DEC_STATUS_PLAY:
+				MM_DBG("decoder status: play\n");
+				if (audio->pcm_feedback) {
+					audamrwb_config_hostpcm(audio);
+					audamrwb_buffer_refresh(audio);
+				}
+				audio->dec_state =
+					MSM_AUD_DECODER_STATE_SUCCESS;
+				wake_up(&audio->wait);
+				break;
+			default:
+				MM_DBG("unknown decoder status\n");
+				break;
+			}
+			break;
+		}
+	case AUDPP_MSG_CFG_MSG:
+		if (msg[0] == AUDPP_MSG_ENA_ENA) {
+			MM_DBG("CFG_MSG ENABLE\n");
+			auddec_dsp_config(audio, 1);
+			audio->out_needed = 0;
+			audio->running = 1;
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan);
+			audpp_dsp_set_eq(audio->dec_id,	audio->eq_enable,
+								&audio->eq);
+			audpp_avsync(audio->dec_id, 22050);
+		} else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+			MM_DBG("CFG_MSG DISABLE\n");
+			audpp_avsync(audio->dec_id, 0);
+			audio->running = 0;
+		} else {
+			MM_DBG("CFG_MSG %d?\n", msg[0]);
+		}
+		break;
+	case AUDPP_MSG_ROUTING_ACK:
+		MM_DBG("ROUTING_ACK mode=%d\n", msg[1]);
+		audpp_cmd_cfg_adec_params(audio);
+		break;
+	case AUDPP_MSG_FLUSH_ACK:
+		MM_DBG("FLUSH_ACK\n");
+		audio->wflush = 0;
+		audio->rflush = 0;
+		wake_up(&audio->write_wait);
+		if (audio->pcm_feedback)
+			audamrwb_buffer_refresh(audio);
+		break;
+	case AUDPP_MSG_PCMDMAMISSED:
+		MM_DBG("PCMDMAMISSED\n");
+		audio->teos = 1;
+		wake_up(&audio->write_wait);
+		break;
+	default:
+		MM_DBG("UNKNOWN (%d)\n", id);
+	}
+
+}
+
+struct msm_adsp_ops audplay_adsp_ops_amrwb = {
+	.event = audplay_dsp_event,
+};
+
+#define audplay_send_queue0(audio, cmd, len) \
+	msm_adsp_write(audio->audplay, audio->queue_id, \
+			cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+	u16 cfg_dec_cmd[AUDPP_CMD_CFG_DEC_TYPE_LEN / sizeof(unsigned short)];
+
+	memset(cfg_dec_cmd, 0, sizeof(cfg_dec_cmd));
+	cfg_dec_cmd[0] = AUDPP_CMD_CFG_DEC_TYPE;
+	if (enable)
+		cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC |
+			AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_AMRWB;
+	else
+		cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC |
+			AUDPP_CMD_DIS_DEC_V;
+
+	return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd));
+}
+
+static void audpp_cmd_cfg_adec_params(struct audio *audio)
+{
+	struct audpp_cmd_cfg_adec_params_amrwb cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
+	cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_AMRWB_LEN;
+	cmd.common.dec_id = audio->dec_id;
+	cmd.common.input_sampling_frequency = audio->out_sample_rate;
+	cmd.stereo_cfg = audio->out_channel_mode;
+	audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_routing_mode(struct audio *audio)
+{
+	struct audpp_cmd_routing_mode cmd;
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	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)
+{
+	struct audplay_cmd_bitstream_data_avail_nt2 cmd;
+
+	cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2;
+	if (audio->mfield)
+		cmd.decoder_id = AUDAMRWB_METAFIELD_MASK |
+			(audio->out[idx].mfield_sz >> 1);
+	else
+		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 audamrwb_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;
+	MM_DBG("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 audamrwb_config_hostpcm(struct audio *audio)
+{
+	struct audplay_cmd_hpcm_buf_cfg cfg_cmd;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	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 audamrwb_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) {
+			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);
+			MM_DBG("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 audamrwb_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 audamrwb_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 void audamrwb_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);
+	audamrwb_flush(audio);
+	mutex_unlock(&audio->write_lock);
+	wake_up(&audio->read_wait);
+	mutex_lock(&audio->read_lock);
+	audamrwb_flush_pcm_buf(audio);
+	mutex_unlock(&audio->read_lock);
+}
+
+static int audamrwb_events_pending(struct audio *audio)
+{
+	unsigned long flags;
+	int empty;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+	empty = !list_empty(&audio->event_queue);
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+	return empty || audio->event_abort;
+}
+
+static void audamrwb_reset_event_queue(struct audio *audio)
+{
+	unsigned long flags;
+	struct audamrwb_event *drv_evt;
+	struct list_head *ptr, *next;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+	list_for_each_safe(ptr, next, &audio->event_queue) {
+		drv_evt = list_first_entry(&audio->event_queue,
+				struct audamrwb_event, list);
+		list_del(&drv_evt->list);
+		kfree(drv_evt);
+	}
+	list_for_each_safe(ptr, next, &audio->free_event_queue) {
+		drv_evt = list_first_entry(&audio->free_event_queue,
+				struct audamrwb_event, list);
+		list_del(&drv_evt->list);
+		kfree(drv_evt);
+	}
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+	return;
+}
+
+static long audamrwb_process_event_req(struct audio *audio, void __user *arg)
+{
+	long rc;
+	struct msm_audio_event usr_evt;
+	struct audamrwb_event *drv_evt = NULL;
+	int timeout;
+	unsigned long flags;
+
+	if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event)))
+		return -EFAULT;
+
+	timeout = (int) usr_evt.timeout_ms;
+
+	if (timeout > 0) {
+		rc = wait_event_interruptible_timeout(
+			audio->event_wait, audamrwb_events_pending(audio),
+			msecs_to_jiffies(timeout));
+		if (rc == 0)
+			return -ETIMEDOUT;
+	} else {
+		rc = wait_event_interruptible(
+			audio->event_wait, audamrwb_events_pending(audio));
+	}
+
+	if (rc < 0)
+		return rc;
+
+	if (audio->event_abort) {
+		audio->event_abort = 0;
+		return -ENODEV;
+	}
+
+	rc = 0;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+	if (!list_empty(&audio->event_queue)) {
+		drv_evt = list_first_entry(&audio->event_queue,
+				struct audamrwb_event, list);
+		list_del(&drv_evt->list);
+	}
+
+	if (drv_evt) {
+		usr_evt.event_type = drv_evt->event_type;
+		usr_evt.event_payload = drv_evt->payload;
+		list_add_tail(&drv_evt->list, &audio->free_event_queue);
+	} else
+		rc = -1;
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+	if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt)))
+		rc = -EFAULT;
+
+	return rc;
+}
+
+static int audio_enable_eq(struct audio *audio, int enable)
+{
+	if (audio->eq_enable == enable && !audio->eq_needs_commit)
+		return 0;
+
+	audio->eq_enable = enable;
+
+	if (audio->running) {
+		audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq);
+		audio->eq_needs_commit = 0;
+	}
+	return 0;
+}
+
+static long audamrwb_ioctl(struct file *file, unsigned int cmd,
+		unsigned long arg)
+{
+	struct audio *audio = file->private_data;
+	int rc = -EINVAL;
+	unsigned long flags = 0;
+	uint16_t enable_mask;
+	int enable;
+	int prev_state;
+
+	MM_DBG("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;
+	}
+
+	switch (cmd) {
+	case AUDIO_ENABLE_AUDPP:
+		if (copy_from_user(&enable_mask, (void *) arg,
+						sizeof(enable_mask))) {
+			rc = -EFAULT;
+			break;
+		}
+
+		spin_lock_irqsave(&audio->dsp_lock, flags);
+		enable = (enable_mask & EQ_ENABLE) ? 1 : 0;
+		audio_enable_eq(audio, enable);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		rc = 0;
+		break;
+	case AUDIO_SET_VOLUME:
+		spin_lock_irqsave(&audio->dsp_lock, flags);
+		audio->vol_pan.volume = arg;
+		if (audio->running)
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		rc = 0;
+		break;
+
+	case AUDIO_SET_PAN:
+		spin_lock_irqsave(&audio->dsp_lock, flags);
+		audio->vol_pan.pan = arg;
+		if (audio->running)
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		rc = 0;
+		break;
+
+	case AUDIO_SET_EQ:
+		prev_state = audio->eq_enable;
+		audio->eq_enable = 0;
+		if (copy_from_user(&audio->eq.num_bands, (void *) arg,
+				sizeof(audio->eq) -
+				(AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) {
+			rc = -EFAULT;
+			break;
+		}
+		audio->eq_enable = prev_state;
+		audio->eq_needs_commit = 1;
+		rc = 0;
+		break;
+	}
+
+	if (-EINVAL != rc)
+		return rc;
+
+	if (cmd == AUDIO_GET_EVENT) {
+		MM_DBG("AUDIO_GET_EVENT\n");
+		if (mutex_trylock(&audio->get_event_lock)) {
+			rc = audamrwb_process_event_req(audio,
+					(void __user *) arg);
+			mutex_unlock(&audio->get_event_lock);
+		} else
+			rc = -EBUSY;
+		return rc;
+	}
+
+	if (cmd == AUDIO_ABORT_GET_EVENT) {
+		audio->event_abort = 1;
+		wake_up(&audio->event_wait);
+		return 0;
+	}
+
+	mutex_lock(&audio->lock);
+	switch (cmd) {
+	case AUDIO_START:
+		MM_DBG("AUDIO_START\n");
+		rc = audamrwb_enable(audio);
+		if (!rc) {
+			rc = wait_event_interruptible_timeout(audio->wait,
+				audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+				msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+			MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc);
+
+			if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS)
+				rc = -ENODEV;
+			else
+				rc = 0;
+		}
+		break;
+	case AUDIO_STOP:
+		MM_DBG("AUDIO_STOP\n");
+		rc = audamrwb_disable(audio);
+		audio->stopped = 1;
+		audamrwb_ioport_reset(audio);
+		audio->stopped = 0;
+		break;
+	case AUDIO_FLUSH:
+		MM_DBG(" AUDIO_FLUSH\n");
+		audio->rflush = 1;
+		audio->wflush = 1;
+		audamrwb_ioport_reset(audio);
+		if (audio->running) {
+			audpp_flush(audio->dec_id);
+			rc = wait_event_interruptible(audio->write_wait,
+				!audio->wflush);
+			if (rc < 0) {
+				MM_ERR("AUDIO_FLUSH interrupted\n");
+				rc = -EINTR;
+			}
+		} 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;
+			audio->out_channel_mode = config.channel_count;
+			audio->out_sample_rate = config.sample_rate;
+			audio->mfield = config.meta_field;
+			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.meta_field = 0;
+			config.unused[0] = 0;
+			config.unused[1] = 0;
+			config.unused[2] = 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)) {
+			MM_DBG("allocate PCM buf %d\n", config.buffer_count *
+					config.buffer_size);
+			audio->read_phys = pmem_kalloc(
+						config.buffer_size *
+						config.buffer_count,
+						PMEM_MEMTYPE_EBI1|
+						PMEM_ALIGNMENT_4K);
+			if (IS_ERR((void *)audio->read_phys)) {
+					rc = -ENOMEM;
+					break;
+			}
+			audio->read_data = ioremap(audio->read_phys,
+							config.buffer_size *
+							config.buffer_count);
+			if (!audio->read_data) {
+				MM_ERR("no mem for read buf\n");
+				rc = -ENOMEM;
+				pmem_kfree(audio->read_phys);
+			} 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;
+				}
+				MM_DBG("read buf: phy addr 0x%08x \
+						kernel addr 0x%08x\n",
+						audio->read_phys,
+						(int)audio->read_data);
+				rc = 0;
+			}
+		} else {
+			rc = 0;
+		}
+		break;
+	}
+	default:
+		rc = -EINVAL;
+	}
+	mutex_unlock(&audio->lock);
+	return rc;
+}
+
+/* Only useful in tunnel-mode */
+static int audamrwb_fsync(struct file *file, int datasync)
+{
+	struct audio *audio = file->private_data;
+	struct buffer *frame;
+	int rc = 0;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+
+	if (!audio->running || audio->pcm_feedback) {
+		rc = -EINVAL;
+		goto done_nolock;
+	}
+
+	mutex_lock(&audio->write_lock);
+
+	rc = wait_event_interruptible(audio->write_wait,
+		(!audio->out[0].used &&
+		!audio->out[1].used &&
+		audio->out_needed) || audio->wflush);
+
+	if (rc < 0)
+		goto done;
+	else if (audio->wflush) {
+		rc = -EBUSY;
+		goto done;
+	}
+
+	if (audio->reserved) {
+		MM_DBG("send reserved byte\n");
+		frame = audio->out + audio->out_tail;
+		((char *) frame->data)[0] = audio->rsv_byte;
+		((char *) frame->data)[1] = 0;
+		frame->used = 2;
+		audamrwb_send_data(audio, 0);
+
+		rc = wait_event_interruptible(audio->write_wait,
+			(!audio->out[0].used &&
+			!audio->out[1].used &&
+			audio->out_needed) || audio->wflush);
+
+		if (rc < 0)
+			goto done;
+		else if (audio->wflush) {
+			rc = -EBUSY;
+			goto done;
+		}
+	}
+
+	/* pcm dmamiss message is sent continously
+	 * when decoder is starved so no race
+	 * condition concern
+	 */
+	audio->teos = 0;
+
+	rc = wait_event_interruptible(audio->write_wait,
+		audio->teos || audio->wflush);
+
+	if (audio->wflush)
+		rc = -EBUSY;
+
+done:
+	mutex_unlock(&audio->write_lock);
+done_nolock:
+	return rc;
+}
+
+static ssize_t audamrwb_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);
+	MM_DBG("count %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
+			 */
+			MM_DBG("read stop - partial frame\n");
+			break;
+		} else {
+			MM_DBG("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)) {
+				MM_ERR("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;
+			break;
+		}
+	}
+
+	/* 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;
+		MM_ERR("kick start pcm feedback again\n");
+		audamrwb_buffer_refresh(audio);
+	}
+
+	mutex_unlock(&audio->read_lock);
+
+	if (buf > start)
+		rc = buf - start;
+
+	MM_DBG("read %d bytes\n", rc);
+	return rc;
+}
+
+static int audamrwb_process_eos(struct audio *audio,
+		const char __user *buf_start, unsigned short mfield_size)
+{
+	struct buffer *frame;
+	char *buf_ptr;
+	int rc = 0;
+
+	MM_DBG("signal input EOS reserved=%d\n", audio->reserved);
+	if (audio->reserved) {
+		MM_DBG("Pass reserve byte\n");
+		frame = audio->out + audio->out_head;
+		buf_ptr = frame->data;
+		rc = wait_event_interruptible(audio->write_wait,
+					(frame->used == 0)
+					|| (audio->stopped)
+					|| (audio->wflush));
+	if (rc < 0)
+		goto done;
+	if (audio->stopped || audio->wflush) {
+		rc = -EBUSY;
+		goto done;
+	}
+	buf_ptr[0] = audio->rsv_byte;
+	buf_ptr[1] = 0;
+	audio->out_head ^= 1;
+	frame->mfield_sz = 0;
+	audio->reserved = 0;
+	frame->used = 2;
+	audamrwb_send_data(audio, 0);
+	}
+
+	MM_DBG("Now signal input EOS after reserved bytes %d %d %d\n",
+		audio->out[0].used, audio->out[1].used, audio->out_needed);
+
+	frame = audio->out + audio->out_head;
+
+	rc = wait_event_interruptible(audio->write_wait,
+		(audio->out_needed &&
+		audio->out[0].used == 0 &&
+		audio->out[1].used == 0)
+		|| (audio->stopped)
+		|| (audio->wflush));
+
+	if (rc < 0)
+		goto done;
+	if (audio->stopped || audio->wflush) {
+		rc = -EBUSY;
+		goto done;
+	}
+
+	if (copy_from_user(frame->data, buf_start, mfield_size)) {
+		rc = -EFAULT;
+		goto done;
+	}
+
+	frame->mfield_sz = mfield_size;
+	audio->out_head ^= 1;
+	frame->used = mfield_size;
+	audamrwb_send_data(audio, 0);
+
+done:
+	return rc;
+}
+
+static ssize_t audamrwb_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, eos_condition = AUDAMRWB_EOS_NONE;
+	unsigned short mfield_size = 0;
+	unsigned dsize;
+
+	MM_DBG("cnt=%d\n", count);
+
+	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));
+
+		MM_DBG("buffer available\n");
+		if (rc < 0)
+			break;
+		if (audio->stopped || audio->wflush) {
+			rc = -EBUSY;
+			break;
+		}
+
+		if (audio->mfield) {
+			if (buf == start) {
+				/* Processing beginning of user buffer */
+				if (__get_user(mfield_size,
+					(unsigned short __user *) buf)) {
+					rc = -EFAULT;
+					break;
+				} else 	if (mfield_size > count) {
+					rc = -EINVAL;
+					break;
+				}
+				MM_DBG("mf offset_val %x\n", mfield_size);
+				if (copy_from_user(cpy_ptr, buf, mfield_size)) {
+					rc = -EFAULT;
+					break;
+				}
+				/* Check if EOS flag is set and buffer
+				 * contains just meta field
+				 */
+				if (cpy_ptr[AUDAMRWB_EOS_FLG_OFFSET] &
+						AUDAMRWB_EOS_FLG_MASK) {
+					MM_DBG("eos set\n");
+					eos_condition = AUDAMRWB_EOS_SET;
+					if (mfield_size == count) {
+						buf += mfield_size;
+						break;
+					} else
+					cpy_ptr[AUDAMRWB_EOS_FLG_OFFSET] &=
+							~AUDAMRWB_EOS_FLG_MASK;
+				}
+				cpy_ptr += mfield_size;
+				count -= mfield_size;
+				dsize += mfield_size;
+				buf += mfield_size;
+			} else {
+				mfield_size = 0;
+				MM_DBG("continuous buffer\n");
+			}
+			frame->mfield_sz = mfield_size;
+		}
+
+		if (audio->reserved) {
+			MM_DBG("append reserved byte %x\n", audio->rsv_byte);
+			*cpy_ptr = audio->rsv_byte;
+			xfer = (count > ((frame->size - mfield_size) - 1)) ?
+				((frame->size - mfield_size) - 1) : count;
+			cpy_ptr++;
+			dsize += 1;
+			audio->reserved = 0;
+		} else
+			xfer = (count > (frame->size - mfield_size)) ?
+				(frame->size - mfield_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];
+			MM_DBG("odd length buf reserve last byte %x\n",
+					audio->rsv_byte);
+			audio->reserved = 1;
+			dsize--;
+		}
+		count -= xfer;
+		buf += xfer;
+
+		if (dsize > 0) {
+			audio->out_head ^= 1;
+			frame->used = dsize;
+			audamrwb_send_data(audio, 0);
+		}
+	}
+	MM_DBG("eos_condition %x buf[0x%x] start[0x%x]\n", eos_condition,
+			(int) buf, (int) start);
+	if (eos_condition == AUDAMRWB_EOS_SET)
+		rc = audamrwb_process_eos(audio, start, mfield_size);
+	mutex_unlock(&audio->write_lock);
+	if (!rc) {
+		if (buf > start)
+			return buf - start;
+	}
+	return rc;
+}
+
+static int audamrwb_release(struct inode *inode, struct file *file)
+{
+	struct audio *audio = file->private_data;
+
+	MM_INFO("audio instance 0x%08x freeing\n", (int)audio);
+	mutex_lock(&audio->lock);
+	audamrwb_disable(audio);
+	if (audio->rmt_resource_released == 0)
+		rmt_put_resource(audio);
+	audamrwb_flush(audio);
+	audamrwb_flush_pcm_buf(audio);
+	msm_adsp_put(audio->audplay);
+	audpp_adec_free(audio->dec_id);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	unregister_early_suspend(&audio->suspend_ctl.node);
+#endif
+	audio->event_abort = 1;
+	wake_up(&audio->event_wait);
+	audamrwb_reset_event_queue(audio);
+	iounmap(audio->data);
+	pmem_kfree(audio->phys);
+	if (audio->read_data) {
+		iounmap(audio->read_data);
+		pmem_kfree(audio->read_phys);
+	}
+	mutex_unlock(&audio->lock);
+#ifdef CONFIG_DEBUG_FS
+	if (audio->dentry)
+		debugfs_remove(audio->dentry);
+#endif
+	kfree(audio);
+	return 0;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void audamrwb_post_event(struct audio *audio, int type,
+		union msm_audio_event_payload payload)
+{
+	struct audamrwb_event *e_node = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+
+	if (!list_empty(&audio->free_event_queue)) {
+		e_node = list_first_entry(&audio->free_event_queue,
+				struct audamrwb_event, list);
+		list_del(&e_node->list);
+	} else {
+		e_node = kmalloc(sizeof(struct audamrwb_event), GFP_ATOMIC);
+		if (!e_node) {
+			MM_ERR("No mem to post event %d\n", type);
+			spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+			return;
+		}
+	}
+
+	e_node->event_type = type;
+	e_node->payload = payload;
+
+	list_add_tail(&e_node->list, &audio->event_queue);
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+	wake_up(&audio->event_wait);
+}
+
+static void audamrwb_suspend(struct early_suspend *h)
+{
+	struct audamrwb_suspend_ctl *ctl =
+		container_of(h, struct audamrwb_suspend_ctl, node);
+	union msm_audio_event_payload payload;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	audamrwb_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload);
+}
+
+static void audamrwb_resume(struct early_suspend *h)
+{
+	struct audamrwb_suspend_ctl *ctl =
+		container_of(h, struct audamrwb_suspend_ctl, node);
+	union msm_audio_event_payload payload;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	audamrwb_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload);
+}
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t audamrwb_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t audamrwb_debug_read(struct file *file, char __user *buf,
+					size_t count, loff_t *ppos)
+{
+	const int debug_bufmax = 1024;
+	static char buffer[1024];
+	int n = 0, i;
+	struct audio *audio = file->private_data;
+
+	mutex_lock(&audio->lock);
+	n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"enabled %d\n", audio->enabled);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"stopped %d\n", audio->stopped);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"pcm_feedback %d\n", audio->pcm_feedback);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"out_buf_sz %d\n", audio->out[0].size);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"pcm_buf_count %d \n", audio->pcm_buf_count);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"pcm_buf_sz %d \n", audio->in[0].size);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"volume %x \n", audio->vol_pan.volume);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"sample rate %d \n", audio->out_sample_rate);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"channel mode %d \n", audio->out_channel_mode);
+	mutex_unlock(&audio->lock);
+	/* Following variables are only useful for debugging when
+	 * when playback halts unexpectedly. Thus, no mutual exclusion
+	 * enforced
+	 */
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"wflush %d\n", audio->wflush);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"rflush %d\n", audio->rflush);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"running %d \n", audio->running);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"dec state %d \n", audio->dec_state);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"out_needed %d \n", audio->out_needed);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"out_head %d \n", audio->out_head);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"out_tail %d \n", audio->out_tail);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"out[0].used %d \n", audio->out[0].used);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"out[1].used %d \n", audio->out[1].used);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"buffer_refresh %d \n", audio->buf_refresh);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"read_next %d \n", audio->read_next);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"fill_next %d \n", audio->fill_next);
+	for (i = 0; i < audio->pcm_buf_count; i++)
+		n += scnprintf(buffer + n, debug_bufmax - n,
+				"in[%d].used %d \n", i, audio->in[i].used);
+	buffer[n] = 0;
+	return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+
+static const struct file_operations audamrwb_debug_fops = {
+	.read = audamrwb_debug_read,
+	.open = audamrwb_debug_open,
+};
+#endif
+
+static int audamrwb_open(struct inode *inode, struct file *file)
+{
+	struct audio *audio = NULL;
+	int rc, dec_attrb, decid, i;
+	struct audamrwb_event *e_node = NULL;
+#ifdef CONFIG_DEBUG_FS
+	/* 4 bytes represents decoder number, 1 byte for terminate string */
+	char name[sizeof "msm_amrwb_" + 5];
+#endif
+
+	/* Allocate Mem for audio instance */
+	audio = kzalloc(sizeof(struct audio), GFP_KERNEL);
+	if (!audio) {
+		MM_ERR("No memory to allocate audio instance\n");
+		rc = -ENOMEM;
+		goto done;
+	}
+	MM_INFO("audio instance 0x%08x created\n", (int)audio);
+
+	/* Allocate the decoder */
+	dec_attrb = AUDDEC_DEC_AMRWB;
+	if (file->f_mode & FMODE_READ)
+		dec_attrb |= MSM_AUD_MODE_NONTUNNEL;
+	else
+		dec_attrb |= MSM_AUD_MODE_TUNNEL;
+
+	decid = audpp_adec_alloc(dec_attrb, &audio->module_name,
+			&audio->queue_id);
+
+	if (decid < 0) {
+		MM_ERR("No free decoder available, freeing instance 0x%08x\n",
+				(int)audio);
+		rc = -ENODEV;
+		kfree(audio);
+		goto done;
+	}
+
+	audio->dec_id = decid & MSM_AUD_DECODER_MASK;
+
+	audio->phys = pmem_kalloc(DMASZ, PMEM_MEMTYPE_EBI1|PMEM_ALIGNMENT_4K);
+	if (IS_ERR((void *)audio->phys)) {
+		MM_ERR("could not allocate write buffers, freeing instance \
+				0x%08x\n", (int)audio);
+		rc = -ENOMEM;
+		audpp_adec_free(audio->dec_id);
+		kfree(audio);
+		goto done;
+	} else {
+		audio->data = ioremap(audio->phys, DMASZ);
+		if (!audio->data) {
+			MM_ERR("could not allocate write buffers, freeing \
+					instance 0x%08x\n", (int)audio);
+			rc = -ENOMEM;
+			pmem_kfree(audio->phys);
+			audpp_adec_free(audio->dec_id);
+			kfree(audio);
+			goto done;
+		}
+		MM_DBG("write buf: phy addr 0x%08x kernel addr 0x%08x\n",
+				audio->phys, (int)audio->data);
+	}
+
+	if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) {
+		rc = audmgr_open(&audio->audmgr);
+		if (rc) {
+			MM_ERR("audmgr open failed, freeing instance \
+					0x%08x\n", (int)audio);
+			goto err;
+		}
+	}
+
+	rc = msm_adsp_get(audio->module_name, &audio->audplay,
+		&audplay_adsp_ops_amrwb, audio);
+	if (rc) {
+		MM_ERR("failed to get %s module, freeing instance 0x%08x\n",
+				audio->module_name, (int)audio);
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			audmgr_close(&audio->audmgr);
+		goto err;
+	}
+
+	rc = rmt_get_resource(audio);
+	if (rc) {
+		MM_ERR("ADSP resources are not available for AMRWB session \
+			 0x%08x on decoder: %d\n", (int)audio, audio->dec_id);
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			audmgr_close(&audio->audmgr);
+		msm_adsp_put(audio->audplay);
+		goto err;
+	}
+
+	mutex_init(&audio->lock);
+	mutex_init(&audio->write_lock);
+	mutex_init(&audio->read_lock);
+	mutex_init(&audio->get_event_lock);
+	spin_lock_init(&audio->dsp_lock);
+	spin_lock_init(&audio->event_queue_lock);
+	INIT_LIST_HEAD(&audio->free_event_queue);
+	INIT_LIST_HEAD(&audio->event_queue);
+	init_waitqueue_head(&audio->write_wait);
+	init_waitqueue_head(&audio->read_wait);
+	init_waitqueue_head(&audio->wait);
+	init_waitqueue_head(&audio->event_wait);
+
+	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->vol_pan.volume = 0x2000;
+	audio->vol_pan.pan = 0x0;
+	audio->eq_enable = 0;
+	audio->out_sample_rate = 44100;
+	audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V;
+
+	audamrwb_flush(audio);
+
+	file->private_data = audio;
+	audio->opened = 1;
+	audio->event_abort = 0;
+#ifdef CONFIG_DEBUG_FS
+	snprintf(name, sizeof name, "msm_amrwb_%04x", audio->dec_id);
+	audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+			NULL, (void *) audio, &audamrwb_debug_fops);
+
+	if (IS_ERR(audio->dentry))
+		MM_DBG("debugfs_create_file failed\n");
+#endif
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
+	audio->suspend_ctl.node.resume = audamrwb_resume;
+	audio->suspend_ctl.node.suspend = audamrwb_suspend;
+	audio->suspend_ctl.audio = audio;
+	register_early_suspend(&audio->suspend_ctl.node);
+#endif
+	for (i = 0; i < AUDAMRWB_EVENT_NUM; i++) {
+		e_node = kmalloc(sizeof(struct audamrwb_event), GFP_KERNEL);
+		if (e_node)
+			list_add_tail(&e_node->list, &audio->free_event_queue);
+		else {
+			MM_ERR("event pkt alloc failed\n");
+			break;
+		}
+	}
+done:
+	return rc;
+err:
+	iounmap(audio->data);
+	pmem_kfree(audio->phys);
+	audpp_adec_free(audio->dec_id);
+	kfree(audio);
+	return rc;
+}
+
+static const struct file_operations audio_amrwb_fops = {
+	.owner = THIS_MODULE,
+	.open = audamrwb_open,
+	.release = audamrwb_release,
+	.read = audamrwb_read,
+	.write = audamrwb_write,
+	.unlocked_ioctl = audamrwb_ioctl,
+	.fsync = audamrwb_fsync,
+};
+
+struct miscdevice audio_amrwb_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "msm_amrwb",
+	.fops = &audio_amrwb_fops,
+};
+
+static int __init audamrwb_init(void)
+{
+	return misc_register(&audio_amrwb_misc);
+}
+
+static void __exit audamrwb_exit(void)
+{
+	misc_deregister(&audio_amrwb_misc);
+}
+
+module_init(audamrwb_init);
+module_exit(audamrwb_exit);
+
+MODULE_DESCRIPTION("MSM AMR-WB driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5/audio_evrc.c b/arch/arm/mach-msm/qdsp5/audio_evrc.c
new file mode 100644
index 0000000..eaa6528
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audio_evrc.c
@@ -0,0 +1,1603 @@
+/* arch/arm/mach-msm/audio_evrc.c
+ *
+ * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * 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/debugfs.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/earlysuspend.h>
+#include <linux/android_pmem.h>
+#include <linux/slab.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 <mach/qdsp5/qdsp5rmtcmdi.h>
+#include <mach/debug_mm.h>
+
+/* Hold 30 packets of 24 bytes each and 14 bytes of meta in */
+#define BUFSZ 			734
+#define DMASZ 			(BUFSZ * 2)
+
+#define AUDDEC_DEC_EVRC 	12
+
+#define PCM_BUFSZ_MIN 		1624	/* 100ms worth of data and
+					   and 24 bytes of meta out */
+#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
+
+#define AUDEVRC_METAFIELD_MASK 0xFFFF0000
+#define AUDEVRC_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */
+#define AUDEVRC_EOS_FLG_MASK 0x01
+#define AUDEVRC_EOS_NONE 0x0 /* No EOS detected */
+#define AUDEVRC_EOS_SET 0x1 /* EOS set in meta field */
+
+#define AUDEVRC_EVENT_NUM 10 /* Default number of pre-allocated event packets */
+
+struct buffer {
+	void *data;
+	unsigned size;
+	unsigned used;		/* Input usage actual DSP produced PCM size  */
+	unsigned addr;
+	unsigned short mfield_sz; /*only useful for data has meta field */
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+struct audevrc_suspend_ctl {
+	struct early_suspend node;
+	struct audio *audio;
+};
+#endif
+
+struct audevrc_event{
+	struct list_head list;
+	int event_type;
+	union msm_audio_event_payload payload;
+};
+
+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 */
+	int32_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;
+	int32_t phys;  /* physical address of write buffer */
+
+	int mfield; /* meta field embedded in data */
+	int rflush; /* Read  flush */
+	int wflush; /* Write flush */
+	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;
+	int teos; /* valid only if tunnel mode & no data left for decoder */
+	enum msm_aud_decoder_state dec_state;	/* Represents decoder state */
+	int rmt_resource_released;
+
+	const char *module_name;
+	unsigned queue_id;
+	uint16_t dec_id;
+	uint32_t read_ptr_offset;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	struct audevrc_suspend_ctl suspend_ctl;
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *dentry;
+#endif
+
+	wait_queue_head_t wait;
+	struct list_head free_event_queue;
+	struct list_head event_queue;
+	wait_queue_head_t event_wait;
+	spinlock_t event_queue_lock;
+	struct mutex get_event_lock;
+	int event_abort;
+
+	int eq_enable;
+	int eq_needs_commit;
+	audpp_cmd_cfg_object_params_eqalizer eq;
+	audpp_cmd_cfg_object_params_volume vol_pan;
+};
+
+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);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void audevrc_post_event(struct audio *audio, int type,
+		union msm_audio_event_payload payload);
+#endif
+
+static int rmt_put_resource(struct audio *audio)
+{
+	struct aud_codec_config_cmd cmd;
+	unsigned short client_idx;
+
+	cmd.cmd_id = RM_CMD_AUD_CODEC_CFG;
+	cmd.client_id = RM_AUD_CLIENT_ID;
+	cmd.task_id = audio->dec_id;
+	cmd.enable = RMT_DISABLE;
+	cmd.dec_type = AUDDEC_DEC_EVRC;
+	client_idx = ((cmd.client_id << 8) | cmd.task_id);
+
+	return put_adsp_resource(client_idx, &cmd, sizeof(cmd));
+}
+
+static int rmt_get_resource(struct audio *audio)
+{
+	struct aud_codec_config_cmd cmd;
+	unsigned short client_idx;
+
+	cmd.cmd_id = RM_CMD_AUD_CODEC_CFG;
+	cmd.client_id = RM_AUD_CLIENT_ID;
+	cmd.task_id = audio->dec_id;
+	cmd.enable = RMT_ENABLE;
+	cmd.dec_type = AUDDEC_DEC_EVRC;
+	client_idx = ((cmd.client_id << 8) | cmd.task_id);
+
+	return get_adsp_resource(client_idx, &cmd, sizeof(cmd));
+}
+
+/* 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;
+
+	if (audio->rmt_resource_released == 1) {
+		audio->rmt_resource_released = 0;
+		rc = rmt_get_resource(audio);
+		if (rc) {
+			MM_ERR("ADSP resources are not available for EVRC \
+				session 0x%08x on decoder: %d\n Ignoring \
+				error and going ahead with the playback\n",
+				(int)audio, audio->dec_id);
+		}
+	}
+
+	audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+	audio->out_tail = 0;
+	audio->out_needed = 0;
+
+	if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) {
+		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)) {
+		MM_ERR("msm_adsp_enable(audplay) failed\n");
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			audmgr_disable(&audio->audmgr);
+		return -ENODEV;
+	}
+
+	if (audpp_enable(audio->dec_id, audevrc_dsp_event, audio)) {
+		MM_ERR("audpp_enable() failed\n");
+		msm_adsp_disable(audio->audplay);
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			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)
+{
+	int rc = 0;
+	if (audio->enabled) {
+		audio->enabled = 0;
+		audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+		auddec_dsp_config(audio, 0);
+		rc = wait_event_interruptible_timeout(audio->wait,
+				audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+				msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+		if (rc == 0)
+			rc = -ETIMEDOUT;
+		else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE)
+			rc = -EFAULT;
+		else
+			rc = 0;
+		wake_up(&audio->write_wait);
+		wake_up(&audio->read_wait);
+		msm_adsp_disable(audio->audplay);
+		audpp_disable(audio->dec_id, audio);
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			audmgr_disable(&audio->audmgr);
+		audio->out_needed = 0;
+		rmt_put_resource(audio);
+		audio->rmt_resource_released = 1;
+	}
+	return rc;
+}
+
+/* ------------------- dsp --------------------- */
+
+static void audevrc_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]) {
+			MM_DBG("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 {
+			MM_ERR("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 {
+		MM_DBG("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));
+
+	MM_DBG("msg_id=%x\n", id);
+	switch (id) {
+	case AUDPLAY_MSG_DEC_NEEDS_DATA:
+		audevrc_send_data(audio, 1);
+		break;
+	case AUDPLAY_MSG_BUFFER_UPDATE:
+		MM_DBG("\n"); /* Macro prints the file name and function */
+		audevrc_update_pcm_buf_entry(audio, msg);
+		break;
+	case ADSP_MESSAGE_ID:
+		MM_DBG("Received ADSP event: module enable(audplaytask)\n");
+		break;
+	default:
+		MM_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: {
+				uint16_t reason = msg[2];
+				MM_DBG("decoder status:sleep reason = \
+						0x%04x\n", reason);
+				if ((reason == AUDPP_MSG_REASON_MEM)
+					|| (reason ==
+					AUDPP_MSG_REASON_NODECODER)) {
+					audio->dec_state =
+						MSM_AUD_DECODER_STATE_FAILURE;
+					wake_up(&audio->wait);
+				} else if (reason == AUDPP_MSG_REASON_NONE) {
+					/* decoder is in disable state */
+					audio->dec_state =
+						MSM_AUD_DECODER_STATE_CLOSE;
+					wake_up(&audio->wait);
+				}
+				break;
+			}
+			case AUDPP_DEC_STATUS_INIT:
+				MM_DBG("decoder status: init \n");
+				if (audio->pcm_feedback)
+					audpp_cmd_cfg_routing_mode(audio);
+				else
+					audpp_cmd_cfg_adec_params(audio);
+				break;
+
+			case AUDPP_DEC_STATUS_CFG:
+				MM_DBG("decoder status: cfg \n");
+				break;
+			case AUDPP_DEC_STATUS_PLAY:
+				MM_DBG("decoder status: play \n");
+				if (audio->pcm_feedback) {
+					audevrc_config_hostpcm(audio);
+					audevrc_buffer_refresh(audio);
+				}
+				audio->dec_state =
+					MSM_AUD_DECODER_STATE_SUCCESS;
+				wake_up(&audio->wait);
+				break;
+			default:
+				MM_ERR("unknown decoder status \n");
+			}
+			break;
+		}
+	case AUDPP_MSG_CFG_MSG:
+		if (msg[0] == AUDPP_MSG_ENA_ENA) {
+			MM_DBG("CFG_MSG ENABLE\n");
+			auddec_dsp_config(audio, 1);
+			audio->out_needed = 0;
+			audio->running = 1;
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan);
+			audpp_dsp_set_eq(audio->dec_id,	audio->eq_enable,
+								&audio->eq);
+			audpp_avsync(audio->dec_id, 22050);
+		} else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+			MM_DBG("CFG_MSG DISABLE\n");
+			audpp_avsync(audio->dec_id, 0);
+			audio->running = 0;
+		} else {
+			MM_DBG("CFG_MSG %d?\n", msg[0]);
+		}
+		break;
+	case AUDPP_MSG_ROUTING_ACK:
+		MM_DBG("ROUTING_ACK\n");
+		audpp_cmd_cfg_adec_params(audio);
+		break;
+	case AUDPP_MSG_FLUSH_ACK:
+		MM_DBG("FLUSH_ACK\n");
+		audio->wflush = 0;
+		audio->rflush = 0;
+		wake_up(&audio->write_wait);
+		if (audio->pcm_feedback)
+			audevrc_buffer_refresh(audio);
+		break;
+	case AUDPP_MSG_PCMDMAMISSED:
+		MM_DBG("PCMDMAMISSED\n");
+		audio->teos = 1;
+		wake_up(&audio->write_wait);
+		break;
+	default:
+		MM_ERR("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, audio->queue_id, \
+			cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+	u16 cfg_dec_cmd[AUDPP_CMD_CFG_DEC_TYPE_LEN / sizeof(unsigned short)];
+
+	memset(cfg_dec_cmd, 0, sizeof(cfg_dec_cmd));
+
+	cfg_dec_cmd[0] = AUDPP_CMD_CFG_DEC_TYPE;
+	if (enable)
+		cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC |
+			AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_EVRC;
+	else
+		cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC |
+			AUDPP_CMD_DIS_DEC_V;
+
+	return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_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;
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	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)
+{
+	struct audplay_cmd_bitstream_data_avail_nt2 cmd;
+
+	cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2;
+	if (audio->mfield)
+		cmd.decoder_id = AUDEVRC_METAFIELD_MASK |
+			(audio->out[idx].mfield_sz >> 1);
+	else
+		cmd.decoder_id = audio->dec_id;
+	cmd.buf_ptr = audio->out[idx].addr;
+	cmd.buf_size = len / 2;
+	cmd.partition_number = 0;
+	/* complete writes to the input buffer */
+	wmb();
+	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;
+	MM_DBG("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;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	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 && !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) {
+			MM_DBG("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);
+			MM_DBG("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->out_needed = 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->buf_refresh = 0;
+	audio->read_next = 0;
+	audio->fill_next = 0;
+}
+
+static void audevrc_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);
+	audevrc_flush(audio);
+	mutex_unlock(&audio->write_lock);
+	wake_up(&audio->read_wait);
+	mutex_lock(&audio->read_lock);
+	audevrc_flush_pcm_buf(audio);
+	mutex_unlock(&audio->read_lock);
+}
+
+static int audevrc_events_pending(struct audio *audio)
+{
+	unsigned long flags;
+	int empty;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+	empty = !list_empty(&audio->event_queue);
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+	return empty || audio->event_abort;
+}
+
+static void audevrc_reset_event_queue(struct audio *audio)
+{
+	unsigned long flags;
+	struct audevrc_event *drv_evt;
+	struct list_head *ptr, *next;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+	list_for_each_safe(ptr, next, &audio->event_queue) {
+		drv_evt = list_first_entry(&audio->event_queue,
+				struct audevrc_event, list);
+		list_del(&drv_evt->list);
+		kfree(drv_evt);
+	}
+	list_for_each_safe(ptr, next, &audio->free_event_queue) {
+		drv_evt = list_first_entry(&audio->free_event_queue,
+			struct audevrc_event, list);
+		list_del(&drv_evt->list);
+		kfree(drv_evt);
+	}
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+	return;
+}
+
+
+static long audevrc_process_event_req(struct audio *audio, void __user *arg)
+{
+	long rc;
+	struct msm_audio_event usr_evt;
+	struct audevrc_event *drv_evt = NULL;
+	int timeout;
+	unsigned long flags;
+
+	if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event)))
+		return -EFAULT;
+
+	timeout = (int) usr_evt.timeout_ms;
+
+	if (timeout > 0) {
+		rc = wait_event_interruptible_timeout(
+			audio->event_wait, audevrc_events_pending(audio),
+			msecs_to_jiffies(timeout));
+		if (rc == 0)
+			return -ETIMEDOUT;
+	} else {
+		rc = wait_event_interruptible(
+			audio->event_wait, audevrc_events_pending(audio));
+	}
+
+	if (rc < 0)
+		return rc;
+
+	if (audio->event_abort) {
+		audio->event_abort = 0;
+		return -ENODEV;
+	}
+
+	rc = 0;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+	if (!list_empty(&audio->event_queue)) {
+		drv_evt = list_first_entry(&audio->event_queue,
+				struct audevrc_event, list);
+		list_del(&drv_evt->list);
+	}
+	if (drv_evt) {
+		usr_evt.event_type = drv_evt->event_type;
+		usr_evt.event_payload = drv_evt->payload;
+		list_add_tail(&drv_evt->list, &audio->free_event_queue);
+	} else
+		rc = -1;
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+	if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt)))
+		rc = -EFAULT;
+
+	return rc;
+}
+
+static int audio_enable_eq(struct audio *audio, int enable)
+{
+	if (audio->eq_enable == enable && !audio->eq_needs_commit)
+		return 0;
+
+	audio->eq_enable = enable;
+
+	if (audio->running) {
+		audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq);
+		audio->eq_needs_commit = 0;
+	}
+	return 0;
+}
+
+static long audevrc_ioctl(struct file *file, unsigned int cmd,
+			  unsigned long arg)
+{
+	struct audio *audio = file->private_data;
+	int rc = -EINVAL;
+	unsigned long flags = 0;
+	uint16_t enable_mask;
+	int enable;
+	int prev_state;
+
+	MM_DBG("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;
+	}
+
+	switch (cmd) {
+	case AUDIO_ENABLE_AUDPP:
+		if (copy_from_user(&enable_mask, (void *) arg,
+						sizeof(enable_mask))) {
+			rc = -EFAULT;
+			break;
+		}
+
+		spin_lock_irqsave(&audio->dsp_lock, flags);
+		enable = (enable_mask & EQ_ENABLE) ? 1 : 0;
+		audio_enable_eq(audio, enable);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		rc = 0;
+		break;
+	case AUDIO_SET_VOLUME:
+		spin_lock_irqsave(&audio->dsp_lock, flags);
+		audio->vol_pan.volume = arg;
+		if (audio->running)
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		rc = 0;
+		break;
+
+	case AUDIO_SET_PAN:
+		spin_lock_irqsave(&audio->dsp_lock, flags);
+		audio->vol_pan.pan = arg;
+		if (audio->running)
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		rc = 0;
+		break;
+
+	case AUDIO_SET_EQ:
+		prev_state = audio->eq_enable;
+		audio->eq_enable = 0;
+		if (copy_from_user(&audio->eq.num_bands, (void *) arg,
+				sizeof(audio->eq) -
+				(AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) {
+			rc = -EFAULT;
+			break;
+		}
+		audio->eq_enable = prev_state;
+		audio->eq_needs_commit = 1;
+		rc = 0;
+		break;
+	}
+
+	if (-EINVAL != rc)
+		return rc;
+
+	if (cmd == AUDIO_GET_EVENT) {
+		MM_DBG("AUDIO_GET_EVENT\n");
+		if (mutex_trylock(&audio->get_event_lock)) {
+			rc = audevrc_process_event_req(audio,
+					(void __user *) arg);
+			mutex_unlock(&audio->get_event_lock);
+		} else
+			rc = -EBUSY;
+		return rc;
+	}
+
+	if (cmd == AUDIO_ABORT_GET_EVENT) {
+		audio->event_abort = 1;
+		wake_up(&audio->event_wait);
+		return 0;
+	}
+
+	mutex_lock(&audio->lock);
+	switch (cmd) {
+	case AUDIO_START:
+		MM_DBG("AUDIO_START\n");
+		rc = audevrc_enable(audio);
+		if (!rc) {
+			rc = wait_event_interruptible_timeout(audio->wait,
+				audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+				msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+			MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc);
+
+			if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS)
+				rc = -ENODEV;
+			else
+				rc = 0;
+		}
+		break;
+	case AUDIO_STOP:
+		MM_DBG("AUDIO_STOP\n");
+		rc = audevrc_disable(audio);
+		audio->stopped = 1;
+		audevrc_ioport_reset(audio);
+		audio->stopped = 0;
+		break;
+	case AUDIO_FLUSH:
+		MM_DBG("AUDIO_FLUSH\n");
+		audio->rflush = 1;
+		audio->wflush = 1;
+		audevrc_ioport_reset(audio);
+		if (audio->running) {
+			audpp_flush(audio->dec_id);
+			rc = wait_event_interruptible(audio->write_wait,
+				!audio->wflush);
+			if (rc < 0) {
+				MM_ERR("AUDIO_FLUSH interrupted\n");
+				rc = -EINTR;
+			}
+		} 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;
+			}
+			audio->mfield = config.meta_field;
+			rc = 0;
+			MM_DBG("AUDIO_SET_CONFIG applicable only \
+				for meta field configuration\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.meta_field = 0;
+			config.unused[0] = 0;
+			config.unused[1] = 0;
+			config.unused[2] = 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 = audio->pcm_feedback;
+			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.pcm_feedback != audio->pcm_feedback) {
+				MM_ERR("Not sufficient permission to"
+					 "change the playback mode\n");
+				rc = -EACCES;
+				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)) {
+				MM_DBG("allocate PCM buf %d\n",
+					config.buffer_count *
+					config.buffer_size);
+				audio->read_phys = pmem_kalloc(
+							config.buffer_size *
+							config.buffer_count,
+							PMEM_MEMTYPE_EBI1|
+							PMEM_ALIGNMENT_4K);
+				if (IS_ERR((void *)audio->read_phys)) {
+					rc = -ENOMEM;
+					break;
+				}
+				audio->read_data = ioremap(audio->read_phys,
+							config.buffer_size *
+							config.buffer_count);
+				if (!audio->read_data) {
+					MM_ERR("no mem for read buf\n");
+					rc = -ENOMEM;
+					pmem_kfree(audio->read_phys);
+				} else {
+					uint8_t index;
+					uint32_t offset = 0;
+					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;
+					}
+					MM_DBG("read buf: phy addr \
+						0x%08x kernel addr 0x%08x\n",
+						audio->read_phys,
+						(int)audio->read_data);
+					rc = 0;
+				}
+			} else {
+				rc = 0;
+			}
+			break;
+		}
+	case AUDIO_PAUSE:
+		MM_DBG("AUDIO_PAUSE %ld\n", arg);
+		rc = audpp_pause(audio->dec_id, (int) arg);
+		break;
+	default:
+		rc = -EINVAL;
+	}
+	mutex_unlock(&audio->lock);
+	return rc;
+}
+
+/* Only useful in tunnel-mode */
+static int audevrc_fsync(struct file *file, int datasync)
+{
+	struct audio *audio = file->private_data;
+	int rc = 0;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	if (!audio->running || audio->pcm_feedback) {
+		rc = -EINVAL;
+		goto done_nolock;
+	}
+
+	mutex_lock(&audio->write_lock);
+
+	rc = wait_event_interruptible(audio->write_wait,
+		(!audio->out[0].used &&
+		!audio->out[1].used &&
+		audio->out_needed) || audio->wflush);
+
+	if (rc < 0)
+		goto done;
+	else if (audio->wflush) {
+		rc = -EBUSY;
+		goto done;
+	}
+
+	/* pcm dmamiss message is sent continously
+	 * when decoder is starved so no race
+	 * condition concern
+	 */
+	audio->teos = 0;
+
+	rc = wait_event_interruptible(audio->write_wait,
+		audio->teos || audio->wflush);
+
+	if (audio->wflush)
+		rc = -EBUSY;
+
+done:
+	mutex_unlock(&audio->write_lock);
+done_nolock:
+	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);
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	while (count > 0) {
+		rc = wait_event_interruptible(audio->read_wait,
+				(audio->in[audio->read_next].used > 0) ||
+				(audio->stopped) || (audio->rflush));
+
+		MM_DBG("wait terminated \n");
+		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
+			 */
+			MM_DBG("read stop - partial frame\n");
+			break;
+		} else {
+			MM_DBG("read from in[%d]\n", audio->read_next);
+			/* order reads from the output buffer */
+			rmb();
+			if (copy_to_user
+			    (buf, audio->in[audio->read_next].data,
+			     audio->in[audio->read_next].used)) {
+				MM_ERR("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;
+			break;
+				/* Force to exit while loop
+				 * to prevent output thread
+				 * sleep too long if data is
+				 * not ready at this moment
+				 */
+
+		}
+	}
+	/* 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;
+		MM_DBG("kick start pcm feedback again\n");
+		audevrc_buffer_refresh(audio);
+	}
+	mutex_unlock(&audio->read_lock);
+	if (buf > start)
+		rc = buf - start;
+	MM_DBG("read %d bytes\n", rc);
+	return rc;
+}
+
+static int audevrc_process_eos(struct audio *audio,
+		const char __user *buf_start, unsigned short mfield_size)
+{
+	int rc = 0;
+	struct buffer *frame;
+
+	frame = audio->out + audio->out_head;
+
+	rc = wait_event_interruptible(audio->write_wait,
+		(audio->out_needed &&
+		audio->out[0].used == 0 &&
+		audio->out[1].used == 0)
+		|| (audio->stopped)
+		|| (audio->wflush));
+
+	if (rc < 0)
+		goto done;
+	if (audio->stopped || audio->wflush) {
+		rc = -EBUSY;
+		goto done;
+	}
+
+	if (copy_from_user(frame->data, buf_start, mfield_size)) {
+		rc = -EFAULT;
+		goto done;
+	}
+
+	frame->mfield_sz = mfield_size;
+	audio->out_head ^= 1;
+	frame->used = mfield_size;
+	audevrc_send_data(audio, 0);
+
+done:
+	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;
+	char *cpy_ptr;
+	unsigned short mfield_size = 0;
+	int rc = 0, eos_condition = AUDEVRC_EOS_NONE;
+
+	MM_DBG("cnt=%d\n", count);
+
+	if (count & 1)
+		return -EINVAL;
+
+	mutex_lock(&audio->write_lock);
+	while (count > 0) {
+		frame = audio->out + audio->out_head;
+		cpy_ptr = frame->data;
+		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->mfield) {
+			if (buf == start) {
+				/* Processing beginning of user buffer */
+				if (__get_user(mfield_size,
+					(unsigned short __user *) buf)) {
+					rc = -EFAULT;
+					break;
+				} else if (mfield_size > count) {
+					rc = -EINVAL;
+					break;
+				}
+				MM_DBG("mf offset_val %x\n", mfield_size);
+				if (copy_from_user(cpy_ptr, buf,
+							mfield_size)) {
+					rc = -EFAULT;
+					break;
+				}
+				/* Check if EOS flag is set and buffer has
+				 * contains just meta field
+				 */
+				if (cpy_ptr[AUDEVRC_EOS_FLG_OFFSET] &
+						AUDEVRC_EOS_FLG_MASK) {
+					MM_DBG("eos set\n");
+					eos_condition = AUDEVRC_EOS_SET;
+					if (mfield_size == count) {
+						buf += mfield_size;
+						break;
+					} else
+					cpy_ptr[AUDEVRC_EOS_FLG_OFFSET] &=
+						~AUDEVRC_EOS_FLG_MASK;
+				}
+				 /* Check EOS to see if */
+				cpy_ptr += mfield_size;
+				count -= mfield_size;
+				buf += mfield_size;
+			 } else {
+				 mfield_size = 0;
+				 MM_DBG("continuous buffer\n");
+			 }
+			 frame->mfield_sz = mfield_size;
+		}
+
+		xfer = (count > (frame->size - mfield_size)) ?
+			(frame->size - mfield_size) : count;
+		if (copy_from_user(cpy_ptr, buf, xfer)) {
+			rc = -EFAULT;
+			break;
+		}
+
+		frame->used = xfer + mfield_size;
+		audio->out_head ^= 1;
+		count -= xfer;
+		buf += xfer;
+		audevrc_send_data(audio, 0);
+	}
+	if (eos_condition == AUDEVRC_EOS_SET)
+		rc = audevrc_process_eos(audio, start, mfield_size);
+	mutex_unlock(&audio->write_lock);
+	if (!rc) {
+		if (buf > start)
+			return buf - start;
+	}
+	return rc;
+}
+
+static int audevrc_release(struct inode *inode, struct file *file)
+{
+	struct audio *audio = file->private_data;
+
+	MM_INFO("audio instance 0x%08x freeing\n", (int)audio);
+	mutex_lock(&audio->lock);
+	audevrc_disable(audio);
+	if (audio->rmt_resource_released == 0)
+		rmt_put_resource(audio);
+	audevrc_flush(audio);
+	audevrc_flush_pcm_buf(audio);
+	msm_adsp_put(audio->audplay);
+	audpp_adec_free(audio->dec_id);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	unregister_early_suspend(&audio->suspend_ctl.node);
+#endif
+	audio->event_abort = 1;
+	wake_up(&audio->event_wait);
+	audevrc_reset_event_queue(audio);
+	iounmap(audio->data);
+	pmem_kfree(audio->phys);
+	if (audio->read_data) {
+		iounmap(audio->read_data);
+		pmem_kfree(audio->read_phys);
+	}
+	mutex_unlock(&audio->lock);
+#ifdef CONFIG_DEBUG_FS
+	if (audio->dentry)
+		debugfs_remove(audio->dentry);
+#endif
+	kfree(audio);
+	return 0;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void audevrc_post_event(struct audio *audio, int type,
+		union msm_audio_event_payload payload)
+{
+	struct audevrc_event *e_node = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+
+	if (!list_empty(&audio->free_event_queue)) {
+		e_node = list_first_entry(&audio->free_event_queue,
+				struct audevrc_event, list);
+		list_del(&e_node->list);
+	} else {
+		e_node = kmalloc(sizeof(struct audevrc_event), GFP_ATOMIC);
+		if (!e_node) {
+			MM_ERR("No mem to post event %d\n", type);
+			spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+			return;
+		}
+	}
+
+	e_node->event_type = type;
+	e_node->payload = payload;
+
+	list_add_tail(&e_node->list, &audio->event_queue);
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+	wake_up(&audio->event_wait);
+}
+
+static void audevrc_suspend(struct early_suspend *h)
+{
+	struct audevrc_suspend_ctl *ctl =
+		container_of(h, struct audevrc_suspend_ctl, node);
+	union msm_audio_event_payload payload;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	audevrc_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload);
+}
+
+static void audevrc_resume(struct early_suspend *h)
+{
+	struct audevrc_suspend_ctl *ctl =
+		container_of(h, struct audevrc_suspend_ctl, node);
+	union msm_audio_event_payload payload;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	audevrc_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload);
+}
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t audevrc_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t audevrc_debug_read(struct file *file, char __user *buf,
+					size_t count, loff_t *ppos)
+{
+	const int debug_bufmax = 1024;
+	static char buffer[1024];
+	int n = 0, i;
+	struct audio *audio = file->private_data;
+
+	mutex_lock(&audio->lock);
+	n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"enabled %d\n", audio->enabled);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"stopped %d\n", audio->stopped);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"pcm_feedback %d\n", audio->pcm_feedback);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"out_buf_sz %d\n", audio->out[0].size);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"pcm_buf_count %d \n", audio->pcm_buf_count);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"pcm_buf_sz %d \n", audio->in[0].size);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"volume %x \n", audio->vol_pan.volume);
+	mutex_unlock(&audio->lock);
+	/* Following variables are only useful for debugging when
+	 * when playback halts unexpectedly. Thus, no mutual exclusion
+	 * enforced
+	 */
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"wflush %d\n", audio->wflush);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"rflush %d\n", audio->rflush);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"running %d \n", audio->running);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"dec state %d \n", audio->dec_state);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"out_needed %d \n", audio->out_needed);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"out_head %d \n", audio->out_head);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"out_tail %d \n", audio->out_tail);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"out[0].used %d \n", audio->out[0].used);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"out[1].used %d \n", audio->out[1].used);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"buffer_refresh %d \n", audio->buf_refresh);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"read_next %d \n", audio->read_next);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"fill_next %d \n", audio->fill_next);
+	for (i = 0; i < audio->pcm_buf_count; i++)
+		n += scnprintf(buffer + n, debug_bufmax - n,
+				"in[%d].size %d \n", i, audio->in[i].used);
+	buffer[n] = 0;
+	return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+
+static const struct file_operations audevrc_debug_fops = {
+	.read = audevrc_debug_read,
+	.open = audevrc_debug_open,
+};
+#endif
+
+static int audevrc_open(struct inode *inode, struct file *file)
+{
+	struct audio *audio = NULL;
+	int rc, dec_attrb, decid, i;
+	struct audevrc_event *e_node = NULL;
+#ifdef CONFIG_DEBUG_FS
+	/* 4 bytes represents decoder number, 1 byte for terminate string */
+	char name[sizeof "msm_evrc_" + 5];
+#endif
+
+	/* Allocate audio instance, set to zero */
+	audio = kzalloc(sizeof(struct audio), GFP_KERNEL);
+	if (!audio) {
+		MM_ERR("no memory to allocate audio instance\n");
+		rc = -ENOMEM;
+		goto done;
+	}
+	MM_INFO("audio instance 0x%08x created\n", (int)audio);
+
+	/* Allocate the decoder */
+	dec_attrb = AUDDEC_DEC_EVRC;
+	if ((file->f_mode & FMODE_WRITE) &&
+			(file->f_mode & FMODE_READ)) {
+		dec_attrb |= MSM_AUD_MODE_NONTUNNEL;
+		audio->pcm_feedback = NON_TUNNEL_MODE_PLAYBACK;
+	} else if ((file->f_mode & FMODE_WRITE) &&
+			!(file->f_mode & FMODE_READ)) {
+		dec_attrb |= MSM_AUD_MODE_TUNNEL;
+		audio->pcm_feedback = TUNNEL_MODE_PLAYBACK;
+	} else {
+		kfree(audio);
+		rc = -EACCES;
+		goto done;
+	}
+	decid = audpp_adec_alloc(dec_attrb, &audio->module_name,
+			&audio->queue_id);
+
+	if (decid < 0) {
+		MM_ERR("No free decoder available, freeing instance 0x%08x\n",
+				(int)audio);
+		rc = -ENODEV;
+		kfree(audio);
+		goto done;
+	}
+
+	audio->dec_id = decid & MSM_AUD_DECODER_MASK;
+
+	audio->phys = pmem_kalloc(DMASZ, PMEM_MEMTYPE_EBI1|PMEM_ALIGNMENT_4K);
+	if (IS_ERR((void *)audio->phys)) {
+		MM_ERR("could not allocate write buffers, freeing instance \
+				0x%08x\n", (int)audio);
+		rc = -ENOMEM;
+		audpp_adec_free(audio->dec_id);
+		kfree(audio);
+		goto done;
+	} else {
+		audio->data = ioremap(audio->phys, DMASZ);
+		if (!audio->data) {
+			MM_ERR("could not allocate write buffers, freeing \
+					instance 0x%08x\n", (int)audio);
+			rc = -ENOMEM;
+			pmem_kfree(audio->phys);
+			audpp_adec_free(audio->dec_id);
+			kfree(audio);
+			goto done;
+		}
+		MM_DBG("write buf: phy addr 0x%08x kernel addr 0x%08x\n",
+				audio->phys, (int)audio->data);
+	}
+
+	if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) {
+		rc = audmgr_open(&audio->audmgr);
+		if (rc) {
+			MM_ERR("audmgr open failed, freeing instance \
+					0x%08x\n", (int)audio);
+			goto err;
+		}
+	}
+
+	rc = msm_adsp_get(audio->module_name, &audio->audplay,
+			&audplay_adsp_ops_evrc, audio);
+
+	if (rc) {
+		MM_ERR("failed to get %s module, freeing instance 0x%08x\n",
+				audio->module_name, (int)audio);
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			audmgr_close(&audio->audmgr);
+		goto err;
+	}
+
+	rc = rmt_get_resource(audio);
+	if (rc) {
+		MM_ERR("ADSP resources are not available for EVRC session \
+			 0x%08x on decoder: %d\n", (int)audio, audio->dec_id);
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			audmgr_close(&audio->audmgr);
+		msm_adsp_put(audio->audplay);
+		goto err;
+	}
+
+	/* Initialize all locks of audio instance */
+	mutex_init(&audio->lock);
+	mutex_init(&audio->write_lock);
+	mutex_init(&audio->read_lock);
+	mutex_init(&audio->get_event_lock);
+	spin_lock_init(&audio->dsp_lock);
+	init_waitqueue_head(&audio->write_wait);
+	init_waitqueue_head(&audio->read_wait);
+	INIT_LIST_HEAD(&audio->free_event_queue);
+	INIT_LIST_HEAD(&audio->event_queue);
+	init_waitqueue_head(&audio->wait);
+	init_waitqueue_head(&audio->event_wait);
+	spin_lock_init(&audio->event_queue_lock);
+
+	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->vol_pan.volume = 0x3FFF;
+
+	audevrc_flush(audio);
+
+	file->private_data = audio;
+	audio->opened = 1;
+#ifdef CONFIG_DEBUG_FS
+	snprintf(name, sizeof name, "msm_evrc_%04x", audio->dec_id);
+	audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+			NULL, (void *) audio, &audevrc_debug_fops);
+
+	if (IS_ERR(audio->dentry))
+		MM_DBG("debugfs_create_file failed\n");
+#endif
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
+	audio->suspend_ctl.node.resume = audevrc_resume;
+	audio->suspend_ctl.node.suspend = audevrc_suspend;
+	audio->suspend_ctl.audio = audio;
+	register_early_suspend(&audio->suspend_ctl.node);
+#endif
+	for (i = 0; i < AUDEVRC_EVENT_NUM; i++) {
+		e_node = kmalloc(sizeof(struct audevrc_event), GFP_KERNEL);
+		if (e_node)
+			list_add_tail(&e_node->list, &audio->free_event_queue);
+		else {
+			MM_ERR("event pkt alloc failed\n");
+			break;
+		}
+	}
+done:
+	return rc;
+err:
+	iounmap(audio->data);
+	pmem_kfree(audio->phys);
+	audpp_adec_free(audio->dec_id);
+	kfree(audio);
+	return rc;
+}
+
+static const 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,
+	.fsync = audevrc_fsync,
+};
+
+struct miscdevice audio_evrc_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "msm_evrc",
+	.fops = &audio_evrc_fops,
+};
+
+static int __init audevrc_init(void)
+{
+	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");
diff --git a/arch/arm/mach-msm/qdsp5/audio_evrc_in.c b/arch/arm/mach-msm/qdsp5/audio_evrc_in.c
new file mode 100644
index 0000000..a640b28
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audio_evrc_in.c
@@ -0,0 +1,1375 @@
+/* arch/arm/mach-msm/qdsp5/audio_evrc_in.c
+ *
+ * evrc audio input device
+ *
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This code is based in part on arch/arm/mach-msm/qdsp5v2/audio_evrc_in.c,
+ * 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_qcp.h>
+
+#include <mach/msm_memtypes.h>
+#include <linux/memory_alloc.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/qdsp5audpreproc.h>
+#include <mach/qdsp5/qdsp5audpreproccmdi.h>
+#include <mach/qdsp5/qdsp5audpreprocmsg.h>
+#include <mach/qdsp5/qdsp5audreccmdi.h>
+#include <mach/qdsp5/qdsp5audrecmsg.h>
+#include <mach/debug_mm.h>
+
+#define FRAME_HEADER_SIZE	8 /* 8 bytes frame header */
+#define NT_FRAME_HEADER_SIZE	24 /* 24 bytes frame header */
+/* FRAME_NUM must be a power of two */
+#define FRAME_NUM	8
+#define EVRC_FRAME_SIZE	36 /* 36 bytes data */
+/*Tunnel mode : 36 bytes data + 8 byte header*/
+#define FRAME_SIZE	(EVRC_FRAME_SIZE + FRAME_HEADER_SIZE)
+ /* 36 bytes data  + 24 meta field*/
+#define NT_FRAME_SIZE	(EVRC_FRAME_SIZE + NT_FRAME_HEADER_SIZE)
+#define DMASZ		(FRAME_SIZE * FRAME_NUM)
+#define NT_DMASZ	(NT_FRAME_SIZE * FRAME_NUM)
+#define OUT_FRAME_NUM	2
+#define OUT_BUFFER_SIZE (4 * 1024 + NT_FRAME_HEADER_SIZE)
+#define BUFFER_SIZE	(OUT_BUFFER_SIZE * OUT_FRAME_NUM)
+
+#define AUDPREPROC_EVRC_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer*/
+#define AUDPREPROC_EVRC_EOS_FLG_MASK 0x01
+#define AUDPREPROC_EVRC_EOS_NONE 0x0 /* No EOS detected */
+#define AUDPREPROC_EVRC_EOS_SET 0x1 /* EOS set in meta field */
+
+struct buffer {
+	void *data;
+	uint32_t size;
+	uint32_t read;
+	uint32_t addr;
+	uint32_t used;
+	uint32_t mfield_sz;
+};
+
+struct audio_evrc_in {
+	struct buffer in[FRAME_NUM];
+
+	spinlock_t dsp_lock;
+
+	atomic_t in_bytes;
+	atomic_t in_samples;
+
+	struct mutex lock;
+	struct mutex read_lock;
+	wait_queue_head_t wait;
+	wait_queue_head_t wait_enable;
+	/*write section*/
+	struct buffer out[OUT_FRAME_NUM];
+
+	uint8_t out_head;
+	uint8_t out_tail;
+	uint8_t out_needed;	/* number of buffers the dsp is waiting for */
+	uint32_t out_count;
+
+	struct mutex write_lock;
+	wait_queue_head_t write_wait;
+	int32_t out_phys; /* physical address of write buffer */
+	char *out_data;
+	int mfield; /* meta field embedded in data */
+	int wflush; /*write flush */
+	int rflush; /*read flush*/
+	int out_frame_cnt;
+
+	struct msm_adsp_module *audrec;
+	struct msm_adsp_module *audpre;
+
+
+	/* configuration to use on next enable */
+	uint32_t samp_rate;
+	uint32_t channel_mode;
+	uint32_t buffer_size; /* Frame size (36 bytes) */
+	uint32_t enc_type; /* 11 for EVRC */
+	uint32_t mode; /* T or NT Mode*/
+
+	struct msm_audio_evrc_enc_config cfg;
+
+	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() */
+
+	uint32_t eos_ack;
+	uint32_t flush_ack;
+
+	const char *module_name;
+	unsigned queue_ids;
+	uint16_t enc_id; /* Session Id */
+
+	unsigned short samp_rate_index;
+	uint32_t audrec_obj_idx ;
+
+	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 */
+};
+
+struct audio_frame {
+	uint16_t frame_count_lsw;
+	uint16_t frame_count_msw;
+	uint16_t frame_length;
+	uint16_t erased_pcm;
+	unsigned char raw_bitstream[];
+} __packed;
+
+struct audio_frame_nt {
+	uint16_t metadata_len;
+	uint16_t frame_count_lsw;
+	uint16_t frame_count_msw;
+	uint16_t frame_length;
+	uint16_t erased_pcm;
+	uint16_t reserved;
+	uint16_t time_stamp_dword_lsw;
+	uint16_t time_stamp_dword_msw;
+	uint16_t time_stamp_lsw;
+	uint16_t time_stamp_msw;
+	uint16_t nflag_lsw;
+	uint16_t nflag_msw;
+	unsigned char raw_bitstream[]; /* samples */
+} __packed;
+
+struct evrc_encoded_meta_out {
+	uint16_t metadata_len;
+	uint16_t time_stamp_dword_lsw;
+	uint16_t time_stamp_dword_msw;
+	uint16_t time_stamp_lsw;
+	uint16_t time_stamp_msw;
+	uint16_t nflag_lsw;
+	uint16_t nflag_msw;
+};
+
+/* Audrec Queue command sent macro's */
+#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, ((audio->queue_ids & 0xFFFF0000) >> 16),\
+			cmd, len)
+#define audio_send_queue_rec(audio, cmd, len) \
+	msm_adsp_write(audio->audrec, (audio->queue_ids & 0x0000FFFF),\
+			cmd, len)
+
+static int audevrc_in_dsp_enable(struct audio_evrc_in *audio, int enable);
+static int audevrc_in_encparam_config(struct audio_evrc_in *audio);
+static int audevrc_in_encmem_config(struct audio_evrc_in *audio);
+static int audevrc_in_dsp_read_buffer(struct audio_evrc_in *audio,
+				uint32_t read_cnt);
+static void audevrc_in_flush(struct audio_evrc_in *audio);
+
+static void audevrc_in_get_dsp_frames(struct audio_evrc_in *audio);
+static int audpcm_config(struct audio_evrc_in *audio);
+static void audevrc_out_flush(struct audio_evrc_in *audio);
+static int audevrc_in_routing_mode_config(struct audio_evrc_in *audio);
+static void audrec_pcm_send_data(struct audio_evrc_in *audio, unsigned needed);
+static void audevrc_nt_in_get_dsp_frames(struct audio_evrc_in *audio);
+static void audevrc_in_flush(struct audio_evrc_in *audio);
+
+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 audevrc_in_enable(struct audio_evrc_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;
+	cfg.codec = RPC_AUD_DEF_CODEC_EVRC;
+	cfg.snd_method = RPC_SND_METHOD_MIDI;
+
+	if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+		rc = audmgr_enable(&audio->audmgr, &cfg);
+		if (rc < 0)
+			return rc;
+
+		if (msm_adsp_enable(audio->audpre)) {
+			audmgr_disable(&audio->audmgr);
+			MM_ERR("msm_adsp_enable(audpre) failed\n");
+			return -ENODEV;
+		}
+	}
+	if (msm_adsp_enable(audio->audrec)) {
+		if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+			audmgr_disable(&audio->audmgr);
+			msm_adsp_disable(audio->audpre);
+		}
+		MM_ERR("msm_adsp_enable(audrec) failed\n");
+		return -ENODEV;
+	}
+
+	audio->enabled = 1;
+	audevrc_in_dsp_enable(audio, 1);
+
+	return 0;
+}
+
+/* must be called with audio->lock held */
+static int audevrc_in_disable(struct audio_evrc_in *audio)
+{
+	if (audio->enabled) {
+		audio->enabled = 0;
+
+		audevrc_in_dsp_enable(audio, 0);
+
+		wake_up(&audio->wait);
+		wait_event_interruptible_timeout(audio->wait_enable,
+				audio->running == 0, 1*HZ);
+		msm_adsp_disable(audio->audrec);
+		if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+			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:
+		MM_DBG("type %d, status_flag %d\n", msg[0], msg[1]);
+		break;
+	case AUDPREPROC_MSG_ERROR_MSG_ID:
+		MM_ERR("err_index %d\n", msg[0]);
+		break;
+	case ADSP_MESSAGE_ID:
+		MM_DBG("Received ADSP event: module enable(audpreproctask)\n");
+		break;
+	default:
+		MM_ERR("unknown event %d\n", id);
+	}
+}
+
+static void audevrc_in_get_dsp_frames(struct audio_evrc_in *audio)
+{
+	struct audio_frame *frame;
+	uint32_t index;
+	unsigned long flags;
+
+	index = audio->in_head;
+
+	frame = (void *) (((char *)audio->in[index].data) -
+			sizeof(*frame));
+	spin_lock_irqsave(&audio->dsp_lock, flags);
+	audio->in[index].size = frame->frame_length;
+
+	/* statistics of read */
+	atomic_add(audio->in[index].size, &audio->in_bytes);
+	atomic_add(1, &audio->in_samples);
+
+	audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1);
+
+	/* If overflow, move the tail index foward. */
+	if (audio->in_head == audio->in_tail) {
+		MM_ERR("Error! not able to keep up the read\n");
+		audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1);
+		MM_ERR("in_count = %d\n", audio->in_count);
+	} else
+		audio->in_count++;
+
+	audevrc_in_dsp_read_buffer(audio, audio->dsp_cnt++);
+	spin_unlock_irqrestore(&audio->dsp_lock, flags);
+
+	wake_up(&audio->wait);
+}
+
+static void audevrc_nt_in_get_dsp_frames(struct audio_evrc_in *audio)
+{
+	struct audio_frame_nt *nt_frame;
+	uint32_t index;
+	unsigned long flags;
+
+	index = audio->in_head;
+	nt_frame = (void *) (((char *)audio->in[index].data) - \
+				sizeof(struct audio_frame_nt));
+	spin_lock_irqsave(&audio->dsp_lock, flags);
+	audio->in[index].size = nt_frame->frame_length;
+	/* statistics of read */
+	atomic_add(audio->in[index].size, &audio->in_bytes);
+	atomic_add(1, &audio->in_samples);
+
+	audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1);
+
+	/* If overflow, move the tail index foward. */
+	if (audio->in_head == audio->in_tail)
+		MM_DBG("Error! not able to keep up the read\n");
+	else
+		audio->in_count++;
+
+	spin_unlock_irqrestore(&audio->dsp_lock, flags);
+	wake_up(&audio->wait);
+}
+
+static int audrec_pcm_buffer_ptr_refresh(struct audio_evrc_in *audio,
+				       unsigned idx, unsigned len)
+{
+	struct audrec_cmd_pcm_buffer_ptr_refresh_arm_enc cmd;
+
+	if (len ==  NT_FRAME_HEADER_SIZE)
+		len = len / 2;
+	else
+		len = (len + NT_FRAME_HEADER_SIZE) / 2;
+	MM_DBG("len = %d\n", len);
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDREC_CMD_PCM_BUFFER_PTR_REFRESH_ARM_TO_ENC;
+	cmd.num_buffers = 1;
+	if (cmd.num_buffers == 1) {
+		cmd.buf_address_length[0] = (audio->out[idx].addr &
+							0xffff0000) >> 16;
+		cmd.buf_address_length[1] = (audio->out[idx].addr &
+							0x0000ffff);
+		cmd.buf_address_length[2] = (len & 0xffff0000) >> 16;
+		cmd.buf_address_length[3] = (len & 0x0000ffff);
+	}
+	audio->out_frame_cnt++;
+	return audio_send_queue_rec(audio, &cmd, sizeof(cmd));
+}
+
+static int audpcm_config(struct audio_evrc_in *audio)
+{
+	struct audrec_cmd_pcm_cfg_arm_to_enc cmd;
+	MM_DBG("\n");
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDREC_CMD_PCM_CFG_ARM_TO_ENC;
+	cmd.config_update_flag = AUDREC_PCM_CONFIG_UPDATE_FLAG_ENABLE;
+	cmd.enable_flag = AUDREC_ENABLE_FLAG_VALUE;
+	cmd.sampling_freq = convert_samp_index(audio->samp_rate);
+	if (!audio->channel_mode)
+		cmd.channels = 1;
+	else
+		cmd.channels = 2;
+	cmd.frequency_of_intimation = 1;
+	cmd.max_number_of_buffers = OUT_FRAME_NUM;
+	return audio_send_queue_rec(audio, &cmd, sizeof(cmd));
+}
+
+
+static int audevrc_in_routing_mode_config(struct audio_evrc_in *audio)
+{
+	struct audrec_cmd_routing_mode cmd;
+
+	MM_DBG("\n");
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDREC_CMD_ROUTING_MODE;
+	if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL)
+		cmd.routing_mode = 1;
+	return audio_send_queue_rec(audio, &cmd, sizeof(cmd));
+}
+
+static void audrec_dsp_event(void *data, unsigned id, size_t len,
+			    void (*getevent)(void *ptr, size_t len))
+{
+	struct audio_evrc_in *audio = NULL;
+
+	if (data)
+		audio = data;
+	else {
+		MM_ERR("invalid data for event %x\n", id);
+		return;
+	}
+
+	switch (id) {
+	case AUDREC_MSG_CMD_CFG_DONE_MSG: {
+		struct audrec_msg_cmd_cfg_done_msg cmd_cfg_done_msg;
+		getevent(&cmd_cfg_done_msg, AUDREC_MSG_CMD_CFG_DONE_MSG_LEN);
+		if (cmd_cfg_done_msg.audrec_enc_type & \
+				AUDREC_MSG_CFG_DONE_ENC_ENA) {
+			audio->audrec_obj_idx = cmd_cfg_done_msg.audrec_obj_idx;
+			MM_DBG("CFG ENABLED\n");
+			if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) {
+				MM_DBG("routing command\n");
+				audevrc_in_routing_mode_config(audio);
+			} else {
+				audevrc_in_encmem_config(audio);
+			}
+		} else {
+			MM_DBG("CFG SLEEP\n");
+			audio->running = 0;
+			wake_up(&audio->wait_enable);
+		}
+		break;
+	}
+	case AUDREC_MSG_CMD_ROUTING_MODE_DONE_MSG: {
+		struct audrec_msg_cmd_routing_mode_done_msg \
+			routing_msg;
+		getevent(&routing_msg, AUDREC_MSG_CMD_ROUTING_MODE_DONE_MSG);
+		MM_DBG("AUDREC_MSG_CMD_ROUTING_MODE_DONE_MSG");
+		if (routing_msg.configuration == 0) {
+			MM_ERR("routing configuration failed\n");
+			audio->running = 0;
+			wake_up(&audio->wait_enable);
+		} else
+			audevrc_in_encmem_config(audio);
+		break;
+	}
+	case AUDREC_MSG_CMD_AREC_MEM_CFG_DONE_MSG: {
+		MM_DBG("AREC_MEM_CFG_DONE_MSG\n");
+		if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)
+			audevrc_in_encparam_config(audio);
+		else
+			audpcm_config(audio);
+		break;
+	}
+	case AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG: {
+		MM_DBG("AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG");
+		audevrc_in_encparam_config(audio);
+	    break;
+	}
+	case AUDREC_MSG_CMD_AREC_PARAM_CFG_DONE_MSG: {
+		MM_DBG("AUDREC_MSG_CMD_AREC_PARAM_CFG_DONE_MSG\n");
+		audio->running = 1;
+		wake_up(&audio->wait_enable);
+		if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL)
+			audrec_pcm_send_data(audio, 1);
+		break;
+	}
+	case AUDREC_CMD_PCM_BUFFER_PTR_UPDATE_ARM_TO_ENC_MSG: {
+		MM_DBG("ptr_update recieved from DSP\n");
+		audrec_pcm_send_data(audio, 1);
+		break;
+	}
+	case AUDREC_MSG_NO_EXT_PKT_AVAILABLE_MSG: {
+		struct audrec_msg_no_ext_pkt_avail_msg err_msg;
+		getevent(&err_msg, AUDREC_MSG_NO_EXT_PKT_AVAILABLE_MSG_LEN);
+		MM_DBG("NO_EXT_PKT_AVAILABLE_MSG %x\n",\
+			err_msg.audrec_err_id);
+		break;
+	}
+	case AUDREC_MSG_PACKET_READY_MSG: {
+		struct audrec_msg_packet_ready_msg pkt_ready_msg;
+
+		getevent(&pkt_ready_msg, AUDREC_MSG_PACKET_READY_MSG_LEN);
+		MM_DBG("UP_PACKET_READY_MSG: write cnt msw  %d \
+		write cnt lsw %d read cnt msw %d  read cnt lsw %d \n",\
+		pkt_ready_msg.pkt_counter_msw, \
+		pkt_ready_msg.pkt_counter_lsw, \
+		pkt_ready_msg.pkt_read_cnt_msw, \
+		pkt_ready_msg.pkt_read_cnt_lsw);
+
+		audevrc_in_get_dsp_frames(audio);
+		break;
+	}
+	case AUDREC_UP_NT_PACKET_READY_MSG: {
+		struct audrec_up_nt_packet_ready_msg pkt_ready_msg;
+
+		getevent(&pkt_ready_msg, AUDREC_UP_NT_PACKET_READY_MSG_LEN);
+		MM_DBG("UP_NT_PACKET_READY_MSG: write cnt lsw  %d \
+		write cnt msw %d read cnt lsw %d  read cnt msw %d \n",\
+		pkt_ready_msg.audrec_packetwrite_cnt_lsw, \
+		pkt_ready_msg.audrec_packetwrite_cnt_msw, \
+		pkt_ready_msg.audrec_upprev_readcount_lsw, \
+		pkt_ready_msg.audrec_upprev_readcount_msw);
+
+		audevrc_nt_in_get_dsp_frames(audio);
+		break;
+	}
+	case AUDREC_CMD_FLUSH_DONE_MSG: {
+		audio->wflush = 0;
+		audio->rflush = 0;
+		audio->flush_ack = 1;
+		wake_up(&audio->write_wait);
+		MM_DBG("flush ack recieved\n");
+		break;
+	}
+	case ADSP_MESSAGE_ID:
+		MM_DBG("Received ADSP event: module \
+				enable/disable(audrectask)\n");
+		break;
+	default:
+		MM_ERR("unknown event %d\n", id);
+	}
+}
+
+static struct msm_adsp_ops audpre_evrc_adsp_ops = {
+	.event = audpre_dsp_event,
+};
+
+static struct msm_adsp_ops audrec_evrc_adsp_ops = {
+	.event = audrec_dsp_event,
+};
+
+static int audevrc_in_dsp_enable(struct audio_evrc_in *audio, int enable)
+{
+	struct audrec_cmd_enc_cfg cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDREC_CMD_ENC_CFG;
+	cmd.audrec_enc_type = (audio->enc_type & 0xFF) |
+			(enable ? AUDREC_CMD_ENC_ENA : AUDREC_CMD_ENC_DIS);
+	/* Don't care */
+	cmd.audrec_obj_idx = audio->audrec_obj_idx;
+
+	return audio_send_queue_rec(audio, &cmd, sizeof(cmd));
+}
+
+static int audevrc_in_encmem_config(struct audio_evrc_in *audio)
+{
+	struct audrec_cmd_arecmem_cfg cmd;
+	uint16_t *data = (void *) audio->data;
+	int n;
+	int header_len = 0;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	cmd.cmd_id = AUDREC_CMD_ARECMEM_CFG;
+	cmd.audrec_obj_idx = audio->audrec_obj_idx;
+	/* Rate at which packet complete message comes */
+	cmd.audrec_up_pkt_intm_cnt = 1;
+	cmd.audrec_extpkt_buffer_msw = audio->phys >> 16;
+	cmd.audrec_extpkt_buffer_lsw = audio->phys;
+	/* Max Buffer no available for frames */
+	cmd.audrec_extpkt_buffer_num = FRAME_NUM;
+
+	/* prepare buffer pointers:
+	 * T:36 bytes evrc packet + 4 halfword header
+	 * NT:36 bytes evrc packet + 12 halfword header
+	 */
+	if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)
+		header_len = FRAME_HEADER_SIZE/2;
+	else
+		header_len = NT_FRAME_HEADER_SIZE/2;
+
+	for (n = 0; n < FRAME_NUM; n++) {
+		audio->in[n].data = data + header_len;
+		data += (EVRC_FRAME_SIZE/2) + header_len;
+		MM_DBG("0x%8x\n", (int)(audio->in[n].data - header_len*2));
+	}
+
+	return audio_send_queue_rec(audio, &cmd, sizeof(cmd));
+}
+
+static int audevrc_in_encparam_config(struct audio_evrc_in *audio)
+{
+	struct audrec_cmd_arecparam_evrc_cfg cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.common.cmd_id = AUDREC_CMD_ARECPARAM_CFG;
+	cmd.common.audrec_obj_idx = audio->audrec_obj_idx;
+	cmd.enc_min_rate = audio->cfg.min_bit_rate;
+	cmd.enc_max_rate = audio->cfg.max_bit_rate;
+	cmd.rate_modulation_cmd = 0;  /* Default set to 0 */
+
+	return audio_send_queue_rec(audio, &cmd, sizeof(cmd));
+}
+
+static int audevrc_flush_command(struct audio_evrc_in *audio)
+{
+	struct audrec_cmd_flush cmd;
+	MM_DBG("\n");
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDREC_CMD_FLUSH;
+	return audio_send_queue_rec(audio, &cmd, sizeof(cmd));
+}
+
+static int audevrc_in_dsp_read_buffer(struct audio_evrc_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;
+	cmd.type = audio->audrec_obj_idx;
+	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 audevrc_ioport_reset(struct audio_evrc_in *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);
+	audevrc_in_flush(audio);
+	mutex_unlock(&audio->write_lock);
+	wake_up(&audio->wait);
+	mutex_lock(&audio->read_lock);
+	audevrc_out_flush(audio);
+	mutex_unlock(&audio->read_lock);
+}
+
+static void audevrc_in_flush(struct audio_evrc_in *audio)
+{
+	int i;
+
+	audio->dsp_cnt = 0;
+	audio->in_head = 0;
+	audio->in_tail = 0;
+	audio->in_count = 0;
+	audio->eos_ack = 0;
+	for (i = FRAME_NUM-1; i >= 0; i--) {
+		audio->in[i].size = 0;
+		audio->in[i].read = 0;
+	}
+	MM_DBG("in_bytes %d\n", atomic_read(&audio->in_bytes));
+	MM_DBG("in_samples %d\n", atomic_read(&audio->in_samples));
+	atomic_set(&audio->in_bytes, 0);
+	atomic_set(&audio->in_samples, 0);
+}
+
+static void audevrc_out_flush(struct audio_evrc_in *audio)
+{
+	int i;
+
+	audio->out_head = 0;
+	audio->out_tail = 0;
+	audio->out_count = 0;
+	for (i = OUT_FRAME_NUM-1; i >= 0; i--) {
+		audio->out[i].size = 0;
+		audio->out[i].read = 0;
+		audio->out[i].used = 0;
+	}
+}
+
+/* ------------------- device --------------------- */
+static long audevrc_in_ioctl(struct file *file,
+				unsigned int cmd, unsigned long arg)
+{
+	struct audio_evrc_in *audio = file->private_data;
+	int rc = 0;
+
+	MM_DBG("\n");
+	if (cmd == AUDIO_GET_STATS) {
+		struct msm_audio_stats stats;
+		stats.byte_count = atomic_read(&audio->in_bytes);
+		stats.sample_count = atomic_read(&audio->in_samples);
+		if (copy_to_user((void *) arg, &stats, sizeof(stats)))
+			return -EFAULT;
+		return rc;
+	}
+
+	mutex_lock(&audio->lock);
+	switch (cmd) {
+	case AUDIO_START: {
+		rc = audevrc_in_enable(audio);
+		if (!rc) {
+			rc =
+			wait_event_interruptible_timeout(audio->wait_enable,
+				audio->running != 0, 1*HZ);
+			MM_DBG("state %d rc = %d\n", audio->running, rc);
+
+			if (audio->running == 0)
+				rc = -ENODEV;
+			else
+				rc = 0;
+		}
+		audio->stopped = 0;
+		break;
+	}
+	case AUDIO_STOP: {
+		rc = audevrc_in_disable(audio);
+		audio->stopped = 1;
+		break;
+	}
+	case AUDIO_FLUSH: {
+		MM_DBG("AUDIO_FLUSH\n");
+		audio->rflush = 1;
+		audio->wflush = 1;
+		audevrc_ioport_reset(audio);
+		if (audio->running) {
+			audevrc_flush_command(audio);
+			rc = wait_event_interruptible(audio->write_wait,
+				!audio->wflush);
+			if (rc < 0) {
+				MM_ERR("AUDIO_FLUSH interrupted\n");
+				rc = -EINTR;
+			}
+		} else {
+			audio->rflush = 0;
+			audio->wflush = 0;
+		}
+		break;
+	}
+	case AUDIO_GET_CONFIG: {
+		struct msm_audio_config cfg;
+		memset(&cfg, 0, sizeof(cfg));
+		cfg.buffer_size = OUT_BUFFER_SIZE;
+		cfg.buffer_count = OUT_FRAME_NUM;
+		cfg.sample_rate = convert_samp_index(audio->samp_rate);
+		cfg.channel_count = 1;
+		cfg.type = 0;
+		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;
+	}
+	case AUDIO_GET_STREAM_CONFIG: {
+		struct msm_audio_stream_config cfg;
+		memset(&cfg, 0, sizeof(cfg));
+		cfg.buffer_size = audio->buffer_size;
+		cfg.buffer_count = FRAME_NUM;
+		if (copy_to_user((void *)arg, &cfg, sizeof(cfg)))
+			rc = -EFAULT;
+		else
+			rc = 0;
+		break;
+	}
+	case AUDIO_SET_STREAM_CONFIG: {
+		struct msm_audio_stream_config cfg;
+		if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) {
+			rc = -EFAULT;
+			break;
+		}
+		/* Allow only single frame */
+		if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+			if (cfg.buffer_size != (FRAME_SIZE - 8))
+				rc = -EINVAL;
+				break;
+		} else {
+			if (cfg.buffer_size != (EVRC_FRAME_SIZE + 14))
+				rc = -EINVAL;
+				break;
+		}
+		audio->buffer_size = cfg.buffer_size;
+		break;
+	}
+	case AUDIO_GET_EVRC_ENC_CONFIG: {
+		if (copy_to_user((void *) arg, &audio->cfg, sizeof(audio->cfg)))
+			rc = -EFAULT;
+		break;
+	}
+	case AUDIO_SET_EVRC_ENC_CONFIG: {
+		struct msm_audio_evrc_enc_config cfg;
+		if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) {
+			rc = -EFAULT;
+			break;
+		}
+		MM_DBG("0X%8x, 0x%8x, 0x%8x\n", cfg.min_bit_rate,
+				cfg.max_bit_rate, cfg.cdma_rate);
+		if (cfg.min_bit_rate > CDMA_RATE_FULL || \
+				 cfg.min_bit_rate < CDMA_RATE_EIGHTH) {
+			MM_ERR("invalid min bitrate\n");
+			rc = -EFAULT;
+			break;
+		}
+		if (cfg.max_bit_rate > CDMA_RATE_FULL || \
+				cfg.max_bit_rate < CDMA_RATE_EIGHTH) {
+			MM_ERR("invalid max bitrate\n");
+			rc = -EFAULT;
+			break;
+		}
+		/* Recording Does not support Erase and Blank */
+		if (cfg.cdma_rate > CDMA_RATE_FULL ||
+			cfg.cdma_rate < CDMA_RATE_EIGHTH) {
+			MM_ERR("invalid qcelp cdma rate\n");
+			rc = -EFAULT;
+			break;
+		}
+		memcpy(&audio->cfg, &cfg, sizeof(cfg));
+		break;
+	}
+	default:
+		rc = -EINVAL;
+	}
+	mutex_unlock(&audio->lock);
+	return rc;
+}
+
+static ssize_t audevrc_in_read(struct file *file,
+				char __user *buf,
+				size_t count, loff_t *pos)
+{
+	struct audio_evrc_in *audio = file->private_data;
+	unsigned long flags;
+	const char __user *start = buf;
+	void *data;
+	uint32_t index;
+	uint32_t size;
+	int rc = 0;
+	struct evrc_encoded_meta_out meta_field;
+	struct audio_frame_nt *nt_frame;
+	MM_DBG("count = %d\n", count);
+	mutex_lock(&audio->read_lock);
+	while (count > 0) {
+		rc = wait_event_interruptible(
+			audio->wait, (audio->in_count > 0) || audio->stopped ||
+			audio->rflush);
+		if (rc < 0)
+			break;
+
+		if (audio->rflush) {
+			rc = -EBUSY;
+			break;
+		}
+		if (audio->stopped && !audio->in_count) {
+			MM_DBG("Driver in stop state, No more buffer to read");
+			rc = 0;/* End of File */
+			break;
+		}
+
+		index = audio->in_tail;
+		data = (uint8_t *) audio->in[index].data;
+		size = audio->in[index].size;
+
+		if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) {
+			nt_frame = (struct audio_frame_nt *)(data -
+					sizeof(struct audio_frame_nt));
+			memcpy((char *)&meta_field.time_stamp_dword_lsw,
+				(char *)&nt_frame->time_stamp_dword_lsw,
+				(sizeof(struct evrc_encoded_meta_out) - \
+				sizeof(uint16_t)));
+			meta_field.metadata_len =
+					sizeof(struct evrc_encoded_meta_out);
+			if (copy_to_user((char *)start, (char *)&meta_field,
+					sizeof(struct evrc_encoded_meta_out))) {
+				rc = -EFAULT;
+				break;
+			}
+			if (nt_frame->nflag_lsw & 0x0001) {
+				MM_ERR("recieved EOS in read call\n");
+				audio->eos_ack = 1;
+			}
+			buf += sizeof(struct evrc_encoded_meta_out);
+			count -= sizeof(struct evrc_encoded_meta_out);
+		}
+		if (count >= size) {
+			/* order the reads on the buffer */
+			dma_coherent_post_ops();
+			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->mode == MSM_AUD_ENC_MODE_NONTUNNEL)) {
+				if (!audio->eos_ack) {
+					MM_DBG("sending read ptr command \
+							%d %d\n",
+							audio->dsp_cnt,
+							audio->in_tail);
+					audevrc_in_dsp_read_buffer(audio,
+							audio->dsp_cnt++);
+				}
+			}
+		} else {
+			MM_ERR("short read\n");
+			break;
+		}
+		break;
+	}
+	mutex_unlock(&audio->read_lock);
+
+	if (buf > start)
+		return buf - start;
+
+	return rc;
+}
+
+static void audrec_pcm_send_data(struct audio_evrc_in *audio, unsigned needed)
+{
+	struct buffer *frame;
+	unsigned long flags;
+	MM_DBG("\n");
+	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) {
+			MM_DBG("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);
+			audrec_pcm_buffer_ptr_refresh(audio,
+						 audio->out_tail,
+						    frame->used);
+			frame->used = 0xffffffff;
+			audio->out_needed = 0;
+		}
+	}
+ done:
+	spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+static int audevrc_in_fsync(struct file *file,	int datasync)
+
+{
+	struct audio_evrc_in *audio = file->private_data;
+	int rc = 0;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	if (!audio->running || (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)) {
+		rc = -EINVAL;
+		goto done_nolock;
+	}
+
+	mutex_lock(&audio->write_lock);
+
+	rc = wait_event_interruptible(audio->write_wait,
+			audio->wflush);
+	MM_DBG("waked on by some event audio->wflush = %d\n", audio->wflush);
+
+	if (rc < 0)
+		goto done;
+	else if (audio->wflush) {
+		rc = -EBUSY;
+		goto done;
+	}
+done:
+	mutex_unlock(&audio->write_lock);
+done_nolock:
+	return rc;
+
+}
+
+int audrec_evrc_process_eos(struct audio_evrc_in *audio,
+		const char __user *buf_start, unsigned short mfield_size)
+{
+	struct buffer *frame;
+	int rc = 0;
+
+	frame = audio->out + audio->out_head;
+
+	rc = wait_event_interruptible(audio->write_wait,
+		(audio->out_needed &&
+		audio->out[0].used == 0 &&
+		audio->out[1].used == 0)
+		|| (audio->stopped)
+		|| (audio->wflush));
+
+	if (rc < 0)
+		goto done;
+	if (audio->stopped || audio->wflush) {
+		rc = -EBUSY;
+		goto done;
+	}
+	if (copy_from_user(frame->data, buf_start, mfield_size)) {
+		rc = -EFAULT;
+		goto done;
+	}
+
+	frame->mfield_sz = mfield_size;
+	audio->out_head ^= 1;
+	frame->used = mfield_size;
+	MM_DBG("copying meta_out frame->used = %d\n", frame->used);
+	audrec_pcm_send_data(audio, 0);
+done:
+	return rc;
+}
+
+static ssize_t audevrc_in_write(struct file *file,
+				const char __user *buf,
+				size_t count, loff_t *pos)
+{
+	struct audio_evrc_in *audio = file->private_data;
+	const char __user *start = buf;
+	struct buffer *frame;
+	char *cpy_ptr;
+	int rc = 0, eos_condition = AUDPREPROC_EVRC_EOS_NONE;
+	unsigned short mfield_size = 0;
+	int write_count = 0;
+	MM_DBG("cnt=%d\n", count);
+
+	if (count & 1)
+		return -EINVAL;
+
+	if (audio->mode != MSM_AUD_ENC_MODE_NONTUNNEL)
+		return -EINVAL;
+
+	mutex_lock(&audio->write_lock);
+	frame = audio->out + audio->out_head;
+	/* if supplied count is more than driver buffer size
+	 * then only copy driver buffer size
+	 */
+	if (count > frame->size)
+		count = frame->size;
+
+	write_count = count;
+	cpy_ptr = frame->data;
+	rc = wait_event_interruptible(audio->write_wait,
+				      (frame->used == 0)
+					|| (audio->stopped)
+					|| (audio->wflush));
+	if (rc < 0)
+		goto error;
+
+	if (audio->stopped || audio->wflush) {
+		rc = -EBUSY;
+		goto error;
+	}
+	if (audio->mfield) {
+		if (buf == start) {
+			/* Processing beginning of user buffer */
+			if (__get_user(mfield_size,
+				(unsigned short __user *) buf)) {
+				rc = -EFAULT;
+				goto error;
+			} else if (mfield_size > count) {
+				rc = -EINVAL;
+				goto error;
+			}
+			MM_DBG("mf offset_val %x\n", mfield_size);
+			if (copy_from_user(cpy_ptr, buf, mfield_size)) {
+				rc = -EFAULT;
+				goto error;
+			}
+			/* Check if EOS flag is set and buffer has
+			 * contains just meta field
+			 */
+			if (cpy_ptr[AUDPREPROC_EVRC_EOS_FLG_OFFSET] &
+					AUDPREPROC_EVRC_EOS_FLG_MASK) {
+				eos_condition = AUDPREPROC_EVRC_EOS_SET;
+				MM_DBG("EOS SET\n");
+				if (mfield_size == count) {
+					buf += mfield_size;
+					eos_condition = 0;
+					goto exit;
+				} else
+				cpy_ptr[AUDPREPROC_EVRC_EOS_FLG_OFFSET] &=
+					~AUDPREPROC_EVRC_EOS_FLG_MASK;
+			}
+			cpy_ptr += mfield_size;
+			count -= mfield_size;
+			buf += mfield_size;
+		} else {
+			mfield_size = 0;
+			MM_DBG("continuous buffer\n");
+		}
+		frame->mfield_sz = mfield_size;
+	}
+	MM_DBG("copying the stream count = %d\n", count);
+	if (copy_from_user(cpy_ptr, buf, count)) {
+		rc = -EFAULT;
+		goto error;
+	}
+exit:
+	frame->used = count;
+	audio->out_head ^= 1;
+	if (!audio->flush_ack)
+		audrec_pcm_send_data(audio, 0);
+	else {
+		audrec_pcm_send_data(audio, 1);
+		audio->flush_ack = 0;
+	}
+	if (eos_condition == AUDPREPROC_EVRC_EOS_SET)
+		rc = audrec_evrc_process_eos(audio, start, mfield_size);
+	mutex_unlock(&audio->write_lock);
+	return write_count;
+error:
+	mutex_unlock(&audio->write_lock);
+	return rc;
+}
+
+static int audevrc_in_release(struct inode *inode, struct file *file)
+{
+	struct audio_evrc_in *audio = file->private_data;
+
+	mutex_lock(&audio->lock);
+	audevrc_in_disable(audio);
+	audevrc_in_flush(audio);
+	msm_adsp_put(audio->audrec);
+
+	if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)
+		msm_adsp_put(audio->audpre);
+
+	audpreproc_aenc_free(audio->enc_id);
+	audio->audrec = NULL;
+	audio->audpre = NULL;
+	audio->opened = 0;
+	if ((audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) && \
+	   (audio->out_data)) {
+		free_contiguous_memory(audio->out_data);
+		audio->out_data = NULL;
+	}
+	if (audio->data) {
+		free_contiguous_memory(audio->data);
+		audio->data = NULL;
+	}
+	mutex_unlock(&audio->lock);
+	return 0;
+}
+
+static struct audio_evrc_in the_audio_evrc_in;
+
+static int audevrc_in_open(struct inode *inode, struct file *file)
+{
+	struct audio_evrc_in *audio = &the_audio_evrc_in;
+	int rc;
+	int encid;
+	int dma_size = 0;
+
+	mutex_lock(&audio->lock);
+	if (audio->opened) {
+		rc = -EBUSY;
+		goto done;
+	}
+	if ((file->f_mode & FMODE_WRITE) &&
+		(file->f_mode & FMODE_READ)) {
+		audio->mode = MSM_AUD_ENC_MODE_NONTUNNEL;
+		dma_size = NT_DMASZ;
+		MM_DBG("Opened for non tunnel mode encoding\n");
+	} else if (!(file->f_mode & FMODE_WRITE) &&
+				(file->f_mode & FMODE_READ)) {
+		audio->mode = MSM_AUD_ENC_MODE_TUNNEL;
+		dma_size = DMASZ;
+		MM_DBG("Opened for tunnel mode encoding\n");
+	} else {
+		MM_ERR("Invalid mode\n");
+		rc = -EACCES;
+		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_8000,
+	audio->samp_rate_index = AUDREC_CMD_SAMP_RATE_INDX_8000;
+	audio->channel_mode = AUDREC_CMD_STEREO_MODE_MONO;
+	if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL)
+		audio->buffer_size = (EVRC_FRAME_SIZE + 14);
+	else
+		audio->buffer_size = EVRC_FRAME_SIZE;
+	audio->enc_type = AUDREC_CMD_TYPE_0_INDEX_EVRC | audio->mode;
+
+	audio->cfg.cdma_rate = CDMA_RATE_FULL;
+	audio->cfg.min_bit_rate = CDMA_RATE_FULL;
+	audio->cfg.max_bit_rate = CDMA_RATE_FULL;
+
+	if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+		rc = audmgr_open(&audio->audmgr);
+		if (rc)
+			goto done;
+	}
+
+	encid = audpreproc_aenc_alloc(audio->enc_type, &audio->module_name,
+			&audio->queue_ids);
+	if (encid < 0) {
+		MM_ERR("No free encoder available\n");
+		rc = -ENODEV;
+		goto done;
+	}
+	audio->enc_id = encid;
+
+	rc = msm_adsp_get(audio->module_name, &audio->audrec,
+			   &audrec_evrc_adsp_ops, audio);
+	if (rc) {
+		audpreproc_aenc_free(audio->enc_id);
+		goto done;
+	}
+
+	if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+		rc = msm_adsp_get("AUDPREPROCTASK", &audio->audpre,
+				&audpre_evrc_adsp_ops, audio);
+		if (rc) {
+			msm_adsp_put(audio->audrec);
+			audpreproc_aenc_free(audio->enc_id);
+			goto done;
+		}
+	}
+
+	audio->dsp_cnt = 0;
+	audio->stopped = 0;
+	audio->wflush = 0;
+	audio->rflush = 0;
+	audio->flush_ack = 0;
+
+	audevrc_in_flush(audio);
+	audevrc_out_flush(audio);
+
+	audio->data = allocate_contiguous_memory(dma_size, MEMTYPE_EBI1,
+				SZ_4K, 0);
+	if (!audio->data) {
+		MM_ERR("could not allocate read buffers\n");
+		rc = -ENOMEM;
+		goto evt_error;
+	} else {
+		audio->phys = memory_pool_node_paddr(audio->data);
+		if (!audio->phys) {
+			MM_ERR("could not get physical address\n");
+			rc = -ENOMEM;
+			free_contiguous_memory(audio->data);
+			goto evt_error;
+		}
+		MM_DBG("read buf: phy addr 0x%08x kernel addr 0x%08x\n",
+				audio->phys, (int)audio->data);
+	}
+	audio->out_data = NULL;
+	if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) {
+		audio->out_data = allocate_contiguous_memory(BUFFER_SIZE,
+				MEMTYPE_EBI1,
+				SZ_4K, 0);
+		if (!audio->out_data) {
+			MM_ERR("could not allocate read buffers\n");
+			rc = -ENOMEM;
+			free_contiguous_memory(audio->data);
+			goto evt_error;
+		} else {
+			audio->out_phys = memory_pool_node_paddr(
+						audio->out_data);
+			if (!audio->out_phys) {
+				MM_ERR("could not get physical address\n");
+				rc = -ENOMEM;
+				free_contiguous_memory(audio->data);
+				free_contiguous_memory(audio->out_data);
+				goto evt_error;
+			}
+			MM_DBG("write buf:phy addr 0x%08x kernel addr 0x%08x\n",
+					audio->out_phys, (int)audio->out_data);
+		}
+
+		/* Initialize buffer */
+		audio->out[0].data = audio->out_data + 0;
+		audio->out[0].addr = audio->out_phys + 0;
+		audio->out[0].size = OUT_BUFFER_SIZE;
+
+		audio->out[1].data = audio->out_data + OUT_BUFFER_SIZE;
+		audio->out[1].addr = audio->out_phys + OUT_BUFFER_SIZE;
+		audio->out[1].size = OUT_BUFFER_SIZE;
+
+		MM_DBG("audio->out[0].data = %d  audio->out[1].data = %d",
+				(unsigned int)audio->out[0].data,
+				(unsigned int)audio->out[1].data);
+		audio->mfield = NT_FRAME_HEADER_SIZE;
+		audio->out_frame_cnt++;
+	}
+	file->private_data = audio;
+	audio->opened = 1;
+
+done:
+	mutex_unlock(&audio->lock);
+	return rc;
+evt_error:
+	msm_adsp_put(audio->audrec);
+	if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)
+		msm_adsp_put(audio->audpre);
+
+	audpreproc_aenc_free(audio->enc_id);
+	mutex_unlock(&audio->lock);
+	return rc;
+}
+
+static const struct file_operations audio_evrc_in_fops = {
+	.owner		= THIS_MODULE,
+	.open		= audevrc_in_open,
+	.release	= audevrc_in_release,
+	.read		= audevrc_in_read,
+	.write		= audevrc_in_write,
+	.fsync		= audevrc_in_fsync,
+	.unlocked_ioctl	= audevrc_in_ioctl,
+};
+
+static struct miscdevice audevrc_in_misc = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "msm_evrc_in",
+	.fops	= &audio_evrc_in_fops,
+};
+
+static int __init audevrc_in_init(void)
+{
+	mutex_init(&the_audio_evrc_in.lock);
+	mutex_init(&the_audio_evrc_in.read_lock);
+	spin_lock_init(&the_audio_evrc_in.dsp_lock);
+	init_waitqueue_head(&the_audio_evrc_in.wait);
+	init_waitqueue_head(&the_audio_evrc_in.wait_enable);
+	mutex_init(&the_audio_evrc_in.write_lock);
+	init_waitqueue_head(&the_audio_evrc_in.write_wait);
+	return misc_register(&audevrc_in_misc);
+}
+device_initcall(audevrc_in_init);
diff --git a/arch/arm/mach-msm/qdsp5/audio_fm.c b/arch/arm/mach-msm/qdsp5/audio_fm.c
new file mode 100644
index 0000000..2ab7cad
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audio_fm.c
@@ -0,0 +1,169 @@
+/* arch/arm/mach-msm/qdsp5/audio_fm.c
+ *
+ * pcm audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * 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/msm_audio.h>
+#include <mach/debug_mm.h>
+
+#include "audmgr.h"
+
+struct audio {
+	struct mutex lock;
+	int opened;
+	int enabled;
+	int running;
+	struct audmgr audmgr;
+	uint16_t volume;
+};
+
+static struct audio fm_audio;
+
+/* must be called with audio->lock held */
+static int audio_enable(struct audio *audio)
+{
+	struct audmgr_config cfg;
+	int rc = 0;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+
+	if (audio->enabled)
+		return 0;
+
+	cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000;
+	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_VOICE;
+
+	rc = audmgr_enable(&audio->audmgr, &cfg);
+	if (rc < 0)
+		return rc;
+
+	audio->enabled = 1;
+	return rc;
+}
+
+/* must be called with audio->lock held */
+static int audio_disable(struct audio *audio)
+{
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	if (audio->enabled) {
+		audio->enabled = 0;
+		audmgr_disable(&audio->audmgr);
+	}
+	return 0;
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct audio *audio = file->private_data;
+	int rc = -EINVAL;
+
+	MM_DBG("cmd %d", cmd);
+
+	mutex_lock(&audio->lock);
+	switch (cmd) {
+	case AUDIO_START:
+		MM_DBG("AUDIO_START\n");
+		rc = audio_enable(audio);
+		break;
+	case AUDIO_STOP:
+		MM_DBG("AUDIO_STOP\n");
+		rc = audio_disable(audio);
+		audio->running = 0;
+		audio->enabled = 0;
+		break;
+
+	default:
+		rc = -EINVAL;
+	}
+	mutex_unlock(&audio->lock);
+	return rc;
+}
+
+static int audio_release(struct inode *inode, struct file *file)
+{
+	struct audio *audio = file->private_data;
+
+	MM_DBG("audio instance 0x%08x freeing\n", (int)audio);
+	mutex_lock(&audio->lock);
+	audio_disable(audio);
+	audio->running = 0;
+	audio->enabled = 0;
+	audio->opened = 0;
+	mutex_unlock(&audio->lock);
+	return 0;
+}
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+	struct audio *audio = &fm_audio;
+	int rc = 0;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	mutex_lock(&audio->lock);
+
+	if (audio->opened) {
+		MM_ERR("busy\n");
+		rc = -EBUSY;
+		goto done;
+	}
+
+	rc = audmgr_open(&audio->audmgr);
+
+	if (rc) {
+		MM_ERR("%s: failed to register listnet\n", __func__);
+		goto done;
+	}
+
+	file->private_data = audio;
+	audio->opened = 1;
+
+done:
+	mutex_unlock(&audio->lock);
+	return rc;
+}
+
+static const struct file_operations audio_fm_fops = {
+	.owner		= THIS_MODULE,
+	.open		= audio_open,
+	.release	= audio_release,
+	.unlocked_ioctl	= audio_ioctl,
+};
+
+struct miscdevice audio_fm_misc = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "msm_fm",
+	.fops	= &audio_fm_fops,
+};
+
+static int __init audio_init(void)
+{
+	struct audio *audio = &fm_audio;
+
+	mutex_init(&audio->lock);
+	return misc_register(&audio_fm_misc);
+}
+
+device_initcall(audio_init);
+
+MODULE_DESCRIPTION("MSM FM driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5/audio_in.c b/arch/arm/mach-msm/qdsp5/audio_in.c
new file mode 100644
index 0000000..6fc5d6b
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audio_in.c
@@ -0,0 +1,996 @@
+/* arch/arm/mach-msm/qdsp5/audio_in.c
+ *
+ * pcm audio input device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * 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_aac.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>
+#include <mach/debug_mm.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)
+
+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 bit_rate; /* bit rate for AAC */
+	uint32_t record_quality; /* record quality (bits/sample/channel)
+				    for AAC*/
+	uint32_t buffer_cfg_ioctl; /* to allow any one of buffer set ioctl */
+	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 tx_agc_enable;
+	audpreproc_cmd_cfg_agc_params tx_agc_cfg;
+	int ns_enable;
+	audpreproc_cmd_cfg_ns_params ns_cfg;
+	/* For different sample rate, the coeff might be different. *
+	 * All the coeff should be passed from user space	    */
+	int iir_enable;
+	audpreproc_cmd_cfg_iir_tuning_filter_params iir_cfg;
+};
+
+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_tx_agc(struct audio_in *audio);
+static int audio_dsp_set_ns(struct audio_in *audio);
+static int audio_dsp_set_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)) {
+		MM_ERR("msm_adsp_enable(audpre) failed\n");
+		return -ENODEV;
+	}
+	if (msm_adsp_enable(audio->audrec)) {
+		MM_ERR("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:
+		MM_INFO("type %d, status_flag %d\n", msg[0], msg[1]);
+		break;
+	case AUDPREPROC_MSG_ERROR_MSG_ID:
+		MM_INFO("err_index %d\n", msg[0]);
+		break;
+	case ADSP_MESSAGE_ID:
+		MM_DBG("Received ADSP event: module enable(audpreproctask)\n");
+		break;
+	default:
+		MM_ERR("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) {
+				MM_INFO("CFG ENABLED\n");
+				audio_in_encoder_config(audio);
+			} else {
+				MM_INFO("CFG SLEEP\n");
+				audio->running = 0;
+				audio->tx_agc_enable = 0;
+				audio->ns_enable = 0;
+				audio->iir_enable = 0;
+			}
+		} else {
+			MM_INFO("CMD_CFG_DONE %x\n", msg[0]);
+		}
+		break;
+	case AUDREC_MSG_CMD_AREC_PARAM_CFG_DONE_MSG: {
+		MM_INFO("PARAM CFG DONE\n");
+		audio->running = 1;
+		audio_dsp_set_tx_agc(audio);
+		audio_dsp_set_ns(audio);
+		audio_dsp_set_iir(audio);
+		break;
+	}
+	case AUDREC_MSG_FATAL_ERR_MSG:
+		MM_ERR("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;
+	case ADSP_MESSAGE_ID:
+		MM_DBG("Received ADSP event: module \
+				enable/disable(audrectask)\n");
+		break;
+	default:
+		MM_ERR("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)
+
+/* Convert Bit Rate to Record Quality field of DSP */
+static unsigned int bitrate_to_record_quality(unsigned int sample_rate,
+    unsigned int channel, unsigned int bit_rate) {
+	unsigned int temp;
+
+	temp = sample_rate * channel;
+	MM_DBG(" sample rate *  channel = %d \n", temp);
+	/* To represent in Q12 fixed format */
+	temp = (bit_rate * 4096) / temp;
+	MM_DBG(" Record Quality = 0x%8x \n", temp);
+	return temp;
+}
+
+static int audio_dsp_set_tx_agc(struct audio_in *audio)
+{
+	audpreproc_cmd_cfg_agc_params cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	audio->tx_agc_cfg.cmd_id = AUDPREPROC_CMD_CFG_AGC_PARAMS;
+	if (audio->tx_agc_enable) {
+		/* cmd.tx_agc_param_mask = 0xFE00 from sample code */
+		audio->tx_agc_cfg.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);
+		audio->tx_agc_cfg.tx_agc_enable_flag =
+			AUDPREPROC_CMD_TX_AGC_ENA_FLAG_ENA;
+		/* cmd.param_mask = 0xFFF0 from sample code */
+		audio->tx_agc_cfg.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);
+	} else {
+		audio->tx_agc_cfg.tx_agc_param_mask =
+			(1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_TX_AGC_ENA_FLAG);
+		audio->tx_agc_cfg.tx_agc_enable_flag =
+			AUDPREPROC_CMD_TX_AGC_ENA_FLAG_DIS;
+	}
+	cmd = audio->tx_agc_cfg;
+
+	return audio_send_queue_pre(audio, &cmd, sizeof(cmd));
+}
+
+static int audio_enable_tx_agc(struct audio_in *audio, int enable)
+{
+	if (audio->tx_agc_enable != enable) {
+		audio->tx_agc_enable = enable;
+		if (audio->running)
+			audio_dsp_set_tx_agc(audio);
+	}
+	return 0;
+}
+
+static int audio_dsp_set_ns(struct audio_in *audio)
+{
+	audpreproc_cmd_cfg_ns_params cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	audio->ns_cfg.cmd_id = AUDPREPROC_CMD_CFG_NS_PARAMS;
+
+	if (audio->ns_enable) {
+		/* cmd.ec_mode_new is fixed as 0x0064 when enable
+		 * from sample code */
+		audio->ns_cfg.ec_mode_new =
+			AUDPREPROC_CMD_EC_MODE_NEW_NS_ENA |
+			AUDPREPROC_CMD_EC_MODE_NEW_HB_ENA |
+			AUDPREPROC_CMD_EC_MODE_NEW_VA_ENA;
+	} else {
+		audio->ns_cfg.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;
+	}
+	cmd = audio->ns_cfg;
+
+	return audio_send_queue_pre(audio, &cmd, sizeof(cmd));
+}
+
+static int 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);
+	}
+	return 0;
+}
+
+static int audio_dsp_set_iir(struct audio_in *audio)
+{
+	audpreproc_cmd_cfg_iir_tuning_filter_params cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	audio->iir_cfg.cmd_id = AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS;
+
+	if (audio->iir_enable)
+		/* cmd.active_flag is 0xFFFF from sample code but 0x0001 here */
+		audio->iir_cfg.active_flag = AUDPREPROC_CMD_IIR_ACTIVE_FLAG_ENA;
+	else
+		audio->iir_cfg.active_flag = AUDPREPROC_CMD_IIR_ACTIVE_FLAG_DIS;
+
+	cmd = audio->iir_cfg;
+
+	return audio_send_queue_pre(audio, &cmd, sizeof(cmd));
+}
+
+static int audio_enable_iir(struct audio_in *audio, int enable)
+{
+	if (audio->iir_enable != enable) {
+		audio->iir_enable = enable;
+		if (audio->running)
+			audio_dsp_set_iir(audio);
+	}
+	return 0;
+}
+
+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 */
+
+	/* cmd.rec_quality is based on user set bit rate / sample rate /
+	 * channel
+	 */
+	cmd.rec_quality = audio->record_quality;
+
+	/* 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_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;
+		/* The below code is to make mutual exclusive between
+		 * AUDIO_SET_CONFIG and AUDIO_SET_STREAM_CONFIG.
+		 * Allow any one IOCTL.
+		 */
+		if (audio->buffer_cfg_ioctl == AUDIO_SET_STREAM_CONFIG) {
+			rc = -EINVAL;
+			break;
+		}
+		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;
+		audio->buffer_cfg_ioctl = AUDIO_SET_CONFIG;
+		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;
+	}
+	case AUDIO_GET_STREAM_CONFIG: {
+		struct msm_audio_stream_config cfg;
+		cfg.buffer_size = audio->buffer_size;
+		cfg.buffer_count = FRAME_NUM;
+		if (copy_to_user((void *)arg, &cfg, sizeof(cfg)))
+			rc = -EFAULT;
+		else
+			rc = 0;
+		break;
+	}
+	case AUDIO_SET_STREAM_CONFIG: {
+		struct msm_audio_stream_config cfg;
+		/* The below code is to make mutual exclusive between
+		 * AUDIO_SET_CONFIG and AUDIO_SET_STREAM_CONFIG.
+		 * Allow any one IOCTL.
+		 */
+		if (audio->buffer_cfg_ioctl == AUDIO_SET_CONFIG) {
+			rc = -EINVAL;
+			break;
+		}
+		if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
+			rc = -EFAULT;
+			break;
+		} else
+			rc = 0;
+		audio->buffer_size = cfg.buffer_size;
+		/* The IOCTL is only of AAC, set the encoder as AAC */
+		audio->type = 1;
+		audio->buffer_cfg_ioctl = AUDIO_SET_STREAM_CONFIG;
+		break;
+	}
+	case AUDIO_GET_AAC_ENC_CONFIG: {
+		struct msm_audio_aac_enc_config cfg;
+		if (audio->channel_mode == AUDREC_CMD_STEREO_MODE_MONO)
+			cfg.channels = 1;
+		else
+			cfg.channels = 2;
+		cfg.sample_rate = convert_samp_index(audio->samp_rate);
+		cfg.bit_rate = audio->bit_rate;
+		cfg.stream_format = AUDIO_AAC_FORMAT_RAW;
+		if (copy_to_user((void *)arg, &cfg, sizeof(cfg)))
+			rc = -EFAULT;
+		else
+			rc = 0;
+		break;
+	}
+	case AUDIO_SET_AAC_ENC_CONFIG: {
+		struct msm_audio_aac_enc_config cfg;
+		unsigned int record_quality;
+		if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
+			rc = -EFAULT;
+			break;
+		}
+		if (cfg.stream_format != AUDIO_AAC_FORMAT_RAW) {
+			MM_ERR("unsupported AAC format\n");
+			rc = -EINVAL;
+			break;
+		}
+		record_quality = bitrate_to_record_quality(cfg.sample_rate,
+					cfg.channels, cfg.bit_rate);
+		/* Range of Record Quality Supported by DSP, Q12 format */
+		if ((record_quality < 0x800) || (record_quality > 0x4000)) {
+			MM_ERR("Unsupported bit rate \n");
+			rc = -EINVAL;
+			break;
+		}
+		if (cfg.channels == 1) {
+			cfg.channels = AUDREC_CMD_STEREO_MODE_MONO;
+		} else if (cfg.channels == 2) {
+			cfg.channels = AUDREC_CMD_STEREO_MODE_STEREO;
+		} 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.channels;
+		audio->bit_rate = cfg.bit_rate;
+		audio->record_quality = record_quality;
+		MM_DBG(" Record Quality = 0x%8x \n", audio->record_quality);
+		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 && !audio->in_count) {
+			rc = 0;/* End of File */
+			break;
+		}
+
+		index = audio->in_tail;
+		data = (uint8_t *) audio->in[index].data;
+		size = audio->in[index].size;
+		if (count >= size) {
+			/* order the reads on the buffer */
+			dma_coherent_post_ops();
+			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;
+		} else {
+			MM_ERR("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;
+
+	/* For AAC, bit rate hard coded, default settings is
+	 * sample rate (11025) x channel count (1) x recording quality (1.75)
+	 * = 19293 bps  */
+	audio->bit_rate = 19293;
+	audio->record_quality = 0x1c00;
+
+	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->buffer_cfg_ioctl = 0; /* No valid ioctl set */
+
+	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;
+
+	mutex_lock(&audio->lock);
+	switch (cmd) {
+	case AUDIO_ENABLE_AUDPRE:
+		if (copy_from_user(&enable_mask, (void *) arg,
+						sizeof(enable_mask))) {
+			rc = -EFAULT;
+			break;
+		}
+
+		enable = (enable_mask & AGC_ENABLE) ? 1 : 0;
+		audio_enable_tx_agc(audio, enable);
+		enable = (enable_mask & NS_ENABLE) ? 1 : 0;
+		audio_enable_ns(audio, enable);
+		enable = (enable_mask & TX_IIR_ENABLE) ? 1 : 0;
+		audio_enable_iir(audio, enable);
+		break;
+
+	case AUDIO_SET_AGC:
+		if (copy_from_user(&audio->tx_agc_cfg, (void *) arg,
+						sizeof(audio->tx_agc_cfg)))
+			rc = -EFAULT;
+		break;
+
+	case AUDIO_SET_NS:
+		if (copy_from_user(&audio->ns_cfg, (void *) arg,
+						sizeof(audio->ns_cfg)))
+			rc = -EFAULT;
+		break;
+
+	case AUDIO_SET_TX_IIR:
+		if (copy_from_user(&audio->iir_cfg, (void *) arg,
+						sizeof(audio->iir_cfg)))
+			rc = -EFAULT;
+		break;
+
+	default:
+		rc = -EINVAL;
+	}
+
+	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,
+};
+
+struct miscdevice audio_in_misc = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "msm_pcm_in",
+	.fops	= &audio_fops,
+};
+
+static const struct file_operations audpre_fops = {
+	.owner		= THIS_MODULE,
+	.open		= audpre_open,
+	.unlocked_ioctl	= audpre_ioctl,
+};
+
+struct miscdevice audpre_misc = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "msm_preproc_ctl",
+	.fops	= &audpre_fops,
+};
+
+static int __init audio_in_init(void)
+{
+	the_audio_in.data = dma_alloc_coherent(NULL, DMASZ,
+					       &the_audio_in.phys, GFP_KERNEL);
+	if (!the_audio_in.data) {
+		MM_ERR("Unable to allocate DMA buffer\n");
+		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);
+	return misc_register(&audio_in_misc) || misc_register(&audpre_misc);
+}
+
+device_initcall(audio_in_init);
diff --git a/arch/arm/mach-msm/qdsp5/audio_mp3.c b/arch/arm/mach-msm/qdsp5/audio_mp3.c
new file mode 100644
index 0000000..a4f2d18
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audio_mp3.c
@@ -0,0 +1,2337 @@
+/* arch/arm/mach-msm/qdsp5/audio_mp3.c
+ *
+ * mp3 audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * 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/earlysuspend.h>
+#include <linux/list.h>
+#include <linux/android_pmem.h>
+#include <linux/slab.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 <mach/qdsp5/qdsp5rmtcmdi.h>
+#include <mach/debug_mm.h>
+
+#define ADRV_STATUS_AIO_INTF 0x00000001
+#define ADRV_STATUS_OBUF_GIVEN 0x00000002
+#define ADRV_STATUS_IBUF_GIVEN 0x00000004
+#define ADRV_STATUS_FSYNC 0x00000008
+
+/* 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
+
+#define AUDMP3_METAFIELD_MASK 0xFFFF0000
+#define AUDMP3_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */
+#define AUDMP3_EOS_FLG_MASK 0x01
+#define AUDMP3_EOS_NONE 0x0 /* No EOS detected */
+#define AUDMP3_EOS_SET 0x1 /* EOS set in meta field */
+
+#define AUDMP3_EVENT_NUM 10 /* Default number of pre-allocated event packets */
+
+#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;							\
+})
+
+struct audio;
+
+struct buffer {
+	void *data;
+	unsigned size;
+	unsigned used;		/* Input usage actual DSP produced PCM size  */
+	unsigned addr;
+	unsigned short mfield_sz; /*only useful for data has meta field */
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+struct audmp3_suspend_ctl {
+  struct early_suspend node;
+  struct audio *audio;
+};
+#endif
+
+struct audmp3_event {
+	struct list_head list;
+	int event_type;
+	union msm_audio_event_payload payload;
+};
+
+struct audmp3_pmem_region {
+	struct list_head list;
+	struct file *file;
+	int fd;
+	void *vaddr;
+	unsigned long paddr;
+	unsigned long kvaddr;
+	unsigned long len;
+	unsigned ref_cnt;
+};
+
+struct audmp3_buffer_node {
+	struct list_head list;
+	struct msm_audio_aio_buf buf;
+	unsigned long paddr;
+};
+
+struct audmp3_drv_operations {
+	void (*pcm_buf_update)(struct audio *, uint32_t *);
+	void (*buffer_refresh)(struct audio *);
+	void (*send_data)(struct audio *, unsigned);
+	void (*out_flush)(struct audio *);
+	void (*in_flush)(struct audio *);
+	int (*fsync)(struct audio *);
+};
+
+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;
+	struct list_head out_queue; /* queue to retain output buffers */
+	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 */
+	int32_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 */
+	struct list_head in_queue; /* queue to retain input buffers */
+	/* ---- 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;
+	int32_t phys; /* physical address of write buffer */
+
+	uint32_t drv_status;
+	int mfield; /* meta field embedded in data */
+	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 rmt_resource_released;
+	int teos; /* valid only if tunnel mode & no data left for decoder */
+	enum msm_aud_decoder_state dec_state;	/* Represents decoder state */
+	int reserved; /* A byte is being reserved */
+	char rsv_byte; /* Handle odd length user data */
+
+	const char *module_name;
+	unsigned queue_id;
+	uint16_t dec_id;
+	uint32_t read_ptr_offset;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	struct audmp3_suspend_ctl suspend_ctl;
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *dentry;
+#endif
+
+	wait_queue_head_t wait;
+	struct list_head free_event_queue;
+	struct list_head event_queue;
+	wait_queue_head_t event_wait;
+	spinlock_t event_queue_lock;
+	struct mutex get_event_lock;
+	int event_abort;
+
+	struct list_head pmem_region_queue; /* protected by lock */
+	struct audmp3_drv_operations drv_ops;
+
+	int eq_enable;
+	int eq_needs_commit;
+	audpp_cmd_cfg_object_params_eqalizer eq;
+	audpp_cmd_cfg_object_params_volume vol_pan;
+};
+
+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);
+static void audmp3_post_event(struct audio *audio, int type,
+	union msm_audio_event_payload payload);
+static unsigned long audmp3_pmem_fixup(struct audio *audio, void *addr,
+				unsigned long len, int ref_up);
+
+static int rmt_put_resource(struct audio *audio)
+{
+	struct aud_codec_config_cmd cmd;
+	unsigned short client_idx;
+
+	cmd.cmd_id = RM_CMD_AUD_CODEC_CFG;
+	cmd.client_id = RM_AUD_CLIENT_ID;
+	cmd.task_id = audio->dec_id;
+	cmd.enable = RMT_DISABLE;
+	cmd.dec_type = AUDDEC_DEC_MP3;
+	client_idx = ((cmd.client_id << 8) | cmd.task_id);
+
+	return put_adsp_resource(client_idx, &cmd, sizeof(cmd));
+}
+
+static int rmt_get_resource(struct audio *audio)
+{
+	struct aud_codec_config_cmd cmd;
+	unsigned short client_idx;
+
+	cmd.cmd_id = RM_CMD_AUD_CODEC_CFG;
+	cmd.client_id = RM_AUD_CLIENT_ID;
+	cmd.task_id = audio->dec_id;
+	cmd.enable = RMT_ENABLE;
+	cmd.dec_type = AUDDEC_DEC_MP3;
+	client_idx = ((cmd.client_id << 8) | cmd.task_id);
+
+	return get_adsp_resource(client_idx, &cmd, sizeof(cmd));
+}
+
+/* must be called with audio->lock held */
+static int audio_enable(struct audio *audio)
+{
+	struct audmgr_config cfg;
+	int rc;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+
+	if (audio->enabled)
+		return 0;
+
+	if (audio->rmt_resource_released == 1) {
+		audio->rmt_resource_released = 0;
+		rc = rmt_get_resource(audio);
+		if (rc) {
+			MM_ERR("ADSP resources are not available for MP3 \
+				session 0x%08x on decoder: %d\n Ignoring \
+				error and going ahead with the playback\n",
+				(int)audio, audio->dec_id);
+		}
+	}
+
+	audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+	audio->out_tail = 0;
+	audio->out_needed = 0;
+
+	if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) {
+		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)) {
+		MM_ERR("msm_adsp_enable(audplay) failed\n");
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			audmgr_disable(&audio->audmgr);
+		return -ENODEV;
+	}
+
+	if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) {
+		MM_ERR("audpp_enable() failed\n");
+		msm_adsp_disable(audio->audplay);
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			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)
+{
+	int rc = 0;
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	if (audio->enabled) {
+		audio->enabled = 0;
+		audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+		auddec_dsp_config(audio, 0);
+		rc = wait_event_interruptible_timeout(audio->wait,
+				audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+				msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+		if (rc == 0)
+			rc = -ETIMEDOUT;
+		else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE)
+			rc = -EFAULT;
+		else
+			rc = 0;
+		wake_up(&audio->write_wait);
+		wake_up(&audio->read_wait);
+		msm_adsp_disable(audio->audplay);
+		audpp_disable(audio->dec_id, audio);
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			audmgr_disable(&audio->audmgr);
+		audio->out_needed = 0;
+		rmt_put_resource(audio);
+		audio->rmt_resource_released = 1;
+	}
+	return rc;
+}
+
+/* ------------------- dsp --------------------- */
+static void audmp3_async_pcm_buf_update(struct audio *audio, uint32_t *payload)
+{
+	unsigned long flags;
+	union msm_audio_event_payload event_payload;
+	struct audmp3_buffer_node *filled_buf;
+	uint8_t index;
+
+	if (audio->rflush)
+		return;
+
+	spin_lock_irqsave(&audio->dsp_lock, flags);
+	for (index = 0; index < payload[1]; index++) {
+		BUG_ON(list_empty(&audio->in_queue));
+		filled_buf = list_first_entry(&audio->in_queue,
+					struct audmp3_buffer_node, list);
+		if (filled_buf->paddr == payload[2 + index * 2]) {
+			list_del(&filled_buf->list);
+			event_payload.aio_buf = filled_buf->buf;
+			event_payload.aio_buf.data_len =
+				payload[3 + index * 2];
+			MM_DBG("pcm buf %p data_len %d\n", filled_buf,
+					event_payload.aio_buf.data_len);
+			audmp3_post_event(audio, AUDIO_EVENT_READ_DONE,
+						event_payload);
+			kfree(filled_buf);
+		} else {
+			MM_ERR("expected=%lx ret=%x\n", filled_buf->paddr,
+					payload[2 + index * 2]);
+			break;
+		}
+	}
+
+	audio->drv_status &= ~ADRV_STATUS_IBUF_GIVEN;
+	audio->drv_ops.buffer_refresh(audio);
+	spin_unlock_irqrestore(&audio->dsp_lock, flags);
+
+}
+
+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]) {
+			MM_DBG("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 {
+			MM_ERR("expected=%x ret=%x\n",
+					audio->in[audio->fill_next].addr,
+					payload[2 + index * 2]);
+			break;
+		}
+	}
+	if (audio->in[audio->fill_next].used == 0) {
+		audio->drv_ops.buffer_refresh(audio);
+	} else {
+		MM_DBG("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));
+
+	MM_DBG("msg_id=%x\n", id);
+
+	switch (id) {
+	case AUDPLAY_MSG_DEC_NEEDS_DATA:
+		audio->drv_ops.send_data(audio, 1);
+		break;
+
+	case AUDPLAY_MSG_BUFFER_UPDATE:
+		audio->drv_ops.pcm_buf_update(audio, msg);
+		break;
+
+	case ADSP_MESSAGE_ID:
+		MM_DBG("Received ADSP event: module enable(audplaytask)\n");
+		break;
+
+	default:
+		MM_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: {
+				uint16_t reason = msg[2];
+				MM_DBG("decoder status: sleep reason=0x%04x\n",
+						reason);
+				if ((reason == AUDPP_MSG_REASON_MEM)
+					|| (reason ==
+					AUDPP_MSG_REASON_NODECODER)) {
+					audio->dec_state =
+						MSM_AUD_DECODER_STATE_FAILURE;
+					wake_up(&audio->wait);
+				} else if (reason == AUDPP_MSG_REASON_NONE) {
+					/* decoder is in disable state */
+					audio->dec_state =
+						MSM_AUD_DECODER_STATE_CLOSE;
+					wake_up(&audio->wait);
+				}
+				break;
+			}
+			case AUDPP_DEC_STATUS_INIT:
+				MM_DBG("decoder status: init \n");
+				if (audio->pcm_feedback)
+					audpp_cmd_cfg_routing_mode(audio);
+				else
+					audpp_cmd_cfg_adec_params(audio);
+				break;
+
+			case AUDPP_DEC_STATUS_CFG:
+				MM_DBG("decoder status: cfg \n");
+				break;
+			case AUDPP_DEC_STATUS_PLAY:
+				MM_DBG("decoder status: play \n");
+				if (audio->pcm_feedback) {
+					audplay_config_hostpcm(audio);
+					audio->drv_ops.buffer_refresh(audio);
+				}
+				audio->dec_state =
+					MSM_AUD_DECODER_STATE_SUCCESS;
+				wake_up(&audio->wait);
+				break;
+			default:
+				MM_ERR("unknown decoder status \n");
+				break;
+			}
+			break;
+		}
+	case AUDPP_MSG_CFG_MSG:
+		if (msg[0] == AUDPP_MSG_ENA_ENA) {
+			MM_DBG("CFG_MSG ENABLE\n");
+			auddec_dsp_config(audio, 1);
+			audio->out_needed = 0;
+			audio->running = 1;
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan);
+			audpp_dsp_set_eq(audio->dec_id, audio->eq_enable,
+								&audio->eq);
+			audpp_avsync(audio->dec_id, 22050);
+		} else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+			MM_DBG("CFG_MSG DISABLE\n");
+			audpp_avsync(audio->dec_id, 0);
+			audio->running = 0;
+		} else {
+			MM_DBG("CFG_MSG %d?\n", msg[0]);
+		}
+		break;
+	case AUDPP_MSG_ROUTING_ACK:
+		MM_DBG("ROUTING_ACK mode=%d\n",	msg[1]);
+		audpp_cmd_cfg_adec_params(audio);
+		break;
+
+	case AUDPP_MSG_FLUSH_ACK:
+		MM_DBG("FLUSH_ACK\n");
+		audio->wflush = 0;
+		audio->rflush = 0;
+		wake_up(&audio->write_wait);
+		if (audio->pcm_feedback)
+			audio->drv_ops.buffer_refresh(audio);
+		break;
+
+	case AUDPP_MSG_PCMDMAMISSED:
+		MM_DBG("PCMDMAMISSED\n");
+		audio->teos = 1;
+		wake_up(&audio->write_wait);
+		break;
+
+	default:
+		MM_ERR("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, audio->queue_id, \
+			cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+	u16 cfg_dec_cmd[AUDPP_CMD_CFG_DEC_TYPE_LEN / sizeof(unsigned short)];
+
+	memset(cfg_dec_cmd, 0, sizeof(cfg_dec_cmd));
+
+	cfg_dec_cmd[0] = AUDPP_CMD_CFG_DEC_TYPE;
+	if (enable)
+		cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC |
+			AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_MP3;
+	else
+		cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC |
+			AUDPP_CMD_DIS_DEC_V;
+
+	return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_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;
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	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)
+{
+	struct audplay_cmd_bitstream_data_avail_nt2 cmd;
+
+	cmd.cmd_id		= AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2;
+	if (audio->mfield)
+		cmd.decoder_id = AUDMP3_METAFIELD_MASK |
+			(audio->out[idx].mfield_sz >> 1);
+	else
+		cmd.decoder_id		= audio->dec_id;
+	cmd.buf_ptr		= audio->out[idx].addr;
+	cmd.buf_size		= len/2;
+	cmd.partition_number	= 0;
+	/* complete all the writes to the input buffer */
+	wmb();
+	return audplay_send_queue0(audio, &cmd, sizeof(cmd));
+}
+
+/* Caller holds irq_lock */
+static void audmp3_async_buffer_refresh(struct audio *audio)
+{
+	struct audplay_cmd_buffer_refresh refresh_cmd;
+	struct audmp3_buffer_node *next_buf;
+
+	if (!audio->running ||
+	    audio->drv_status & ADRV_STATUS_IBUF_GIVEN)
+		return;
+
+	if (!list_empty(&audio->in_queue)) {
+		next_buf = list_first_entry(&audio->in_queue,
+		    struct audmp3_buffer_node, list);
+		if (!next_buf)
+			return;
+		MM_DBG("next buf %p phy %lx len %d\n", next_buf,
+				next_buf->paddr, next_buf->buf.buf_len);
+		refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH;
+		refresh_cmd.num_buffers = 1;
+		refresh_cmd.buf0_address = next_buf->paddr;
+		refresh_cmd.buf0_length = next_buf->buf.buf_len -
+			(next_buf->buf.buf_len % 576) +
+			(audio->mfield ? 24 : 0); /* Mp3 frame size */
+		refresh_cmd.buf_read_count = 0;
+		audio->drv_status |= ADRV_STATUS_IBUF_GIVEN;
+		(void) audplay_send_queue0(audio, &refresh_cmd,
+			sizeof(refresh_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) +
+		(audio->mfield ? 24 : 0); /* Mp3 frame size */
+	refresh_cmd.buf_read_count = 0;
+	MM_DBG("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;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	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 audmp3_async_send_data(struct audio *audio, unsigned needed)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&audio->dsp_lock, flags);
+	if (!audio->running)
+		goto done;
+
+	if (needed && !audio->wflush) {
+		audio->out_needed = 1;
+		if (audio->drv_status & ADRV_STATUS_OBUF_GIVEN) {
+			/* pop one node out of queue */
+			union msm_audio_event_payload payload;
+			struct audmp3_buffer_node *used_buf;
+
+			MM_DBG("consumed\n");
+			BUG_ON(list_empty(&audio->out_queue));
+			used_buf = list_first_entry(&audio->out_queue,
+				struct audmp3_buffer_node, list);
+			list_del(&used_buf->list);
+			payload.aio_buf = used_buf->buf;
+			audmp3_post_event(audio, AUDIO_EVENT_WRITE_DONE,
+				payload);
+			kfree(used_buf);
+			audio->drv_status &= ~ADRV_STATUS_OBUF_GIVEN;
+		}
+
+	}
+
+	if (audio->out_needed) {
+		struct audmp3_buffer_node *next_buf;
+		struct audplay_cmd_bitstream_data_avail_nt2 cmd;
+		if (!list_empty(&audio->out_queue)) {
+			next_buf = list_first_entry(&audio->out_queue,
+					struct audmp3_buffer_node, list);
+			MM_DBG("next_buf %p\n", next_buf);
+			if (next_buf) {
+				MM_DBG("next buf phy %lx len %d\n",
+						next_buf->paddr,
+						next_buf->buf.data_len);
+
+				cmd.cmd_id =
+					AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2;
+				if (audio->mfield)
+					cmd.decoder_id = AUDMP3_METAFIELD_MASK |
+						(next_buf->buf.mfield_sz >> 1);
+				else
+					cmd.decoder_id = audio->dec_id;
+				cmd.buf_ptr	= (unsigned) next_buf->paddr;
+				cmd.buf_size = next_buf->buf.data_len >> 1;
+				cmd.partition_number	= 0;
+				/* complete the writes to the input buffer */
+				wmb();
+				audplay_send_queue0(audio, &cmd, sizeof(cmd));
+				audio->out_needed = 0;
+				audio->drv_status |= ADRV_STATUS_OBUF_GIVEN;
+			}
+		}
+	}
+
+done:
+	spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+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) {
+			MM_DBG("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);
+		  MM_DBG("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 audmp3_async_flush(struct audio *audio)
+{
+	struct audmp3_buffer_node *buf_node;
+	struct list_head *ptr, *next;
+	union msm_audio_event_payload payload;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	list_for_each_safe(ptr, next, &audio->out_queue) {
+		buf_node = list_entry(ptr, struct audmp3_buffer_node, list);
+		list_del(&buf_node->list);
+		payload.aio_buf = buf_node->buf;
+		audmp3_post_event(audio, AUDIO_EVENT_WRITE_DONE,
+				payload);
+		kfree(buf_node);
+	}
+	audio->drv_status &= ~ADRV_STATUS_OBUF_GIVEN;
+	audio->out_needed = 0;
+	atomic_set(&audio->out_bytes, 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->reserved = 0;
+	audio->out_needed = 0;
+	atomic_set(&audio->out_bytes, 0);
+}
+
+static void audmp3_async_flush_pcm_buf(struct audio *audio)
+{
+	struct audmp3_buffer_node *buf_node;
+	struct list_head *ptr, *next;
+	union msm_audio_event_payload payload;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	list_for_each_safe(ptr, next, &audio->in_queue) {
+		buf_node = list_entry(ptr, struct audmp3_buffer_node, list);
+		list_del(&buf_node->list);
+		payload.aio_buf = buf_node->buf;
+		payload.aio_buf.data_len = 0;
+		audmp3_post_event(audio, AUDIO_EVENT_READ_DONE,
+				payload);
+		kfree(buf_node);
+	}
+	audio->drv_status &= ~ADRV_STATUS_IBUF_GIVEN;
+
+}
+
+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 void audio_ioport_reset(struct audio *audio)
+{
+	if (audio->drv_status & ADRV_STATUS_AIO_INTF) {
+		/* If fsync is in progress, make sure
+		 * return value of fsync indicates
+		 * abort due to flush
+		 */
+		if (audio->drv_status & ADRV_STATUS_FSYNC) {
+			MM_DBG("fsync in progress\n");
+			wake_up(&audio->write_wait);
+			mutex_lock(&audio->write_lock);
+			audio->drv_ops.out_flush(audio);
+			mutex_unlock(&audio->write_lock);
+		} else
+			audio->drv_ops.out_flush(audio);
+		audio->drv_ops.in_flush(audio);
+	} else {
+		/* 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->drv_ops.out_flush(audio);
+		mutex_unlock(&audio->write_lock);
+		wake_up(&audio->read_wait);
+		mutex_lock(&audio->read_lock);
+		audio->drv_ops.in_flush(audio);
+		mutex_unlock(&audio->read_lock);
+	}
+}
+
+static int audmp3_events_pending(struct audio *audio)
+{
+	unsigned long flags;
+	int empty;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+	empty = !list_empty(&audio->event_queue);
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+	return empty || audio->event_abort;
+}
+
+static void audmp3_reset_event_queue(struct audio *audio)
+{
+	unsigned long flags;
+	struct audmp3_event *drv_evt;
+	struct list_head *ptr, *next;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+	list_for_each_safe(ptr, next, &audio->event_queue) {
+		drv_evt = list_first_entry(&audio->event_queue,
+			struct audmp3_event, list);
+		list_del(&drv_evt->list);
+		kfree(drv_evt);
+	}
+	list_for_each_safe(ptr, next, &audio->free_event_queue) {
+		drv_evt = list_first_entry(&audio->free_event_queue,
+			struct audmp3_event, list);
+		list_del(&drv_evt->list);
+		kfree(drv_evt);
+	}
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+	return;
+}
+
+static long audmp3_process_event_req(struct audio *audio, void __user *arg)
+{
+	long rc;
+	struct msm_audio_event usr_evt;
+	struct audmp3_event *drv_evt = NULL;
+	int timeout;
+	unsigned long flags;
+
+	if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event)))
+		return -EFAULT;
+
+	timeout = (int) usr_evt.timeout_ms;
+
+	if (timeout > 0) {
+		rc = wait_event_interruptible_timeout(
+			audio->event_wait, audmp3_events_pending(audio),
+			msecs_to_jiffies(timeout));
+		if (rc == 0)
+			return -ETIMEDOUT;
+	} else {
+		rc = wait_event_interruptible(
+			audio->event_wait, audmp3_events_pending(audio));
+	}
+
+	if (rc < 0)
+		return rc;
+
+	if (audio->event_abort) {
+		audio->event_abort = 0;
+		return -ENODEV;
+	}
+
+	rc = 0;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+	if (!list_empty(&audio->event_queue)) {
+		drv_evt = list_first_entry(&audio->event_queue,
+			struct audmp3_event, list);
+		list_del(&drv_evt->list);
+	}
+	if (drv_evt) {
+		usr_evt.event_type = drv_evt->event_type;
+		usr_evt.event_payload = drv_evt->payload;
+		list_add_tail(&drv_evt->list, &audio->free_event_queue);
+	} else
+		rc = -1;
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+	if (drv_evt->event_type == AUDIO_EVENT_WRITE_DONE ||
+	    drv_evt->event_type == AUDIO_EVENT_READ_DONE) {
+		mutex_lock(&audio->lock);
+		audmp3_pmem_fixup(audio, drv_evt->payload.aio_buf.buf_addr,
+				  drv_evt->payload.aio_buf.buf_len, 0);
+		mutex_unlock(&audio->lock);
+	}
+
+	/* order reads from the output buffer */
+	if (drv_evt->event_type == AUDIO_EVENT_READ_DONE)
+		rmb();
+
+	if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt)))
+		rc = -EFAULT;
+
+	return rc;
+}
+
+static int audmp3_pmem_check(struct audio *audio,
+		void *vaddr, unsigned long len)
+{
+	struct audmp3_pmem_region *region_elt;
+	struct audmp3_pmem_region t = { .vaddr = vaddr, .len = len };
+
+	list_for_each_entry(region_elt, &audio->pmem_region_queue, list) {
+		if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) ||
+		    OVERLAPS(region_elt, &t)) {
+			MM_ERR("region (vaddr %p len %ld)"
+				" clashes with registered region"
+				" (vaddr %p paddr %p len %ld)\n",
+				vaddr, len,
+				region_elt->vaddr,
+				(void *)region_elt->paddr,
+				region_elt->len);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int audmp3_pmem_add(struct audio *audio,
+	struct msm_audio_pmem_info *info)
+{
+	unsigned long paddr, kvaddr, len;
+	struct file *file;
+	struct audmp3_pmem_region *region;
+	int rc = -EINVAL;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	region = kmalloc(sizeof(*region), GFP_KERNEL);
+
+	if (!region) {
+		rc = -ENOMEM;
+		goto end;
+	}
+
+	if (get_pmem_file(info->fd, &paddr, &kvaddr, &len, &file)) {
+		kfree(region);
+		goto end;
+	}
+
+	rc = audmp3_pmem_check(audio, info->vaddr, len);
+	if (rc < 0) {
+		put_pmem_file(file);
+		kfree(region);
+		goto end;
+	}
+
+	region->vaddr = info->vaddr;
+	region->fd = info->fd;
+	region->paddr = paddr;
+	region->kvaddr = kvaddr;
+	region->len = len;
+	region->file = file;
+	region->ref_cnt = 0;
+	MM_DBG("add region paddr %lx vaddr %p, len %lu\n", region->paddr,
+			region->vaddr, region->len);
+	list_add_tail(&region->list, &audio->pmem_region_queue);
+end:
+	return rc;
+}
+
+static int audmp3_pmem_remove(struct audio *audio,
+	struct msm_audio_pmem_info *info)
+{
+	struct audmp3_pmem_region *region;
+	struct list_head *ptr, *next;
+	int rc = -EINVAL;
+
+	MM_DBG("info fd %d vaddr %p\n", info->fd, info->vaddr);
+
+	list_for_each_safe(ptr, next, &audio->pmem_region_queue) {
+		region = list_entry(ptr, struct audmp3_pmem_region, list);
+
+		if ((region->fd == info->fd) &&
+		    (region->vaddr == info->vaddr)) {
+			if (region->ref_cnt) {
+				MM_DBG("region %p in use ref_cnt %d\n",
+						region, region->ref_cnt);
+				break;
+			}
+			MM_DBG("remove region fd %d vaddr %p \n",
+					info->fd, info->vaddr);
+			list_del(&region->list);
+			put_pmem_file(region->file);
+			kfree(region);
+			rc = 0;
+			break;
+		}
+	}
+
+	return rc;
+}
+
+static int audmp3_pmem_lookup_vaddr(struct audio *audio, void *addr,
+		     unsigned long len, struct audmp3_pmem_region **region)
+{
+	struct audmp3_pmem_region *region_elt;
+
+	int match_count = 0;
+
+	*region = NULL;
+
+	/* returns physical address or zero */
+	list_for_each_entry(region_elt, &audio->pmem_region_queue,
+		list) {
+		if (addr >= region_elt->vaddr &&
+		    addr < region_elt->vaddr + region_elt->len &&
+		    addr + 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) {
+		MM_ERR("multiple hits for vaddr %p, len %ld\n", addr, len);
+		list_for_each_entry(region_elt,
+		  &audio->pmem_region_queue, list) {
+			if (addr >= region_elt->vaddr &&
+			    addr < region_elt->vaddr + region_elt->len &&
+			    addr + len <= region_elt->vaddr + region_elt->len)
+				MM_ERR("\t%p, %ld --> %p\n", region_elt->vaddr,
+						region_elt->len,
+						(void *)region_elt->paddr);
+		}
+	}
+
+	return *region ? 0 : -1;
+}
+
+unsigned long audmp3_pmem_fixup(struct audio *audio, void *addr,
+		    unsigned long len, int ref_up)
+{
+	struct audmp3_pmem_region *region;
+	unsigned long paddr;
+	int ret;
+
+	ret = audmp3_pmem_lookup_vaddr(audio, addr, len, &region);
+	if (ret) {
+		MM_ERR("lookup (%p, %ld) failed\n", addr, len);
+		return 0;
+	}
+	if (ref_up)
+		region->ref_cnt++;
+	else
+		region->ref_cnt--;
+	MM_DBG("found region %p ref_cnt %d\n", region, region->ref_cnt);
+	paddr = region->paddr + (addr - region->vaddr);
+	return paddr;
+}
+
+/* audio -> lock must be held at this point */
+static int audmp3_aio_buf_add(struct audio *audio, unsigned dir,
+	void __user *arg)
+{
+	unsigned long flags;
+	struct audmp3_buffer_node *buf_node;
+
+	buf_node = kmalloc(sizeof(*buf_node), GFP_KERNEL);
+
+	if (!buf_node)
+		return -ENOMEM;
+
+	if (copy_from_user(&buf_node->buf, arg, sizeof(buf_node->buf))) {
+		kfree(buf_node);
+		return -EFAULT;
+	}
+
+	MM_DBG("node %p dir %x buf_addr %p buf_len %d data_len \
+			%d\n", buf_node, dir,
+			buf_node->buf.buf_addr, buf_node->buf.buf_len,
+			buf_node->buf.data_len);
+
+	buf_node->paddr = audmp3_pmem_fixup(
+		audio, buf_node->buf.buf_addr,
+		buf_node->buf.buf_len, 1);
+
+	if (dir) {
+		/* write */
+		if (!buf_node->paddr ||
+		    (buf_node->paddr & 0x1) ||
+		    (buf_node->buf.data_len & 0x1) ||
+		    (!audio->pcm_feedback &&
+		    !buf_node->buf.data_len)) {
+			kfree(buf_node);
+			return -EINVAL;
+		}
+		spin_lock_irqsave(&audio->dsp_lock, flags);
+		list_add_tail(&buf_node->list, &audio->out_queue);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		audio->drv_ops.send_data(audio, 0);
+	} else {
+		/* read */
+		if (!buf_node->paddr ||
+		    (buf_node->paddr & 0x1) ||
+		    (buf_node->buf.buf_len < PCM_BUFSZ_MIN)) {
+			kfree(buf_node);
+			return -EINVAL;
+		}
+		spin_lock_irqsave(&audio->dsp_lock, flags);
+		list_add_tail(&buf_node->list, &audio->in_queue);
+		audio->drv_ops.buffer_refresh(audio);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+	}
+
+	MM_DBG("Add buf_node %p paddr %lx\n", buf_node, buf_node->paddr);
+
+	return 0;
+}
+
+static int audio_enable_eq(struct audio *audio, int enable)
+{
+	if (audio->eq_enable == enable && !audio->eq_needs_commit)
+		return 0;
+
+	audio->eq_enable = enable;
+
+	if (audio->running) {
+		audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq);
+		audio->eq_needs_commit = 0;
+	}
+	return 0;
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct audio *audio = file->private_data;
+	int rc = -EINVAL;
+	unsigned long flags = 0;
+	uint16_t enable_mask;
+	int enable;
+	int prev_state;
+
+	MM_DBG("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;
+	}
+
+	switch (cmd) {
+	case AUDIO_ENABLE_AUDPP:
+		if (copy_from_user(&enable_mask, (void *) arg,
+						sizeof(enable_mask))) {
+			rc = -EFAULT;
+			break;
+		}
+
+		spin_lock_irqsave(&audio->dsp_lock, flags);
+		enable = (enable_mask & EQ_ENABLE) ? 1 : 0;
+		audio_enable_eq(audio, enable);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		rc = 0;
+		break;
+	case AUDIO_SET_VOLUME:
+		spin_lock_irqsave(&audio->dsp_lock, flags);
+		audio->vol_pan.volume = arg;
+		if (audio->running)
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		rc = 0;
+		break;
+
+	case AUDIO_SET_PAN:
+		spin_lock_irqsave(&audio->dsp_lock, flags);
+		audio->vol_pan.pan = arg;
+		if (audio->running)
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		rc = 0;
+		break;
+
+	case AUDIO_SET_EQ:
+		prev_state = audio->eq_enable;
+		audio->eq_enable = 0;
+		if (copy_from_user(&audio->eq.num_bands, (void *) arg,
+				sizeof(audio->eq) -
+				(AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) {
+			rc = -EFAULT;
+			break;
+		}
+		audio->eq_enable = prev_state;
+		audio->eq_needs_commit = 1;
+		rc = 0;
+		break;
+	}
+
+	if (-EINVAL != rc)
+		return rc;
+
+	if (cmd == AUDIO_GET_EVENT) {
+		MM_DBG(" AUDIO_GET_EVENT\n");
+		if (mutex_trylock(&audio->get_event_lock)) {
+			rc = audmp3_process_event_req(audio,
+				(void __user *) arg);
+			mutex_unlock(&audio->get_event_lock);
+		} else
+			rc = -EBUSY;
+		return rc;
+	}
+
+	if (cmd == AUDIO_ABORT_GET_EVENT) {
+		audio->event_abort = 1;
+		wake_up(&audio->event_wait);
+		return 0;
+	}
+
+	mutex_lock(&audio->lock);
+	switch (cmd) {
+	case AUDIO_START:
+		MM_DBG("AUDIO_START\n");
+		rc = audio_enable(audio);
+		if (!rc) {
+			rc = wait_event_interruptible_timeout(audio->wait,
+				audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+				msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+			MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc);
+
+			if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS)
+				rc = -ENODEV;
+			else
+				rc = 0;
+		}
+		break;
+	case AUDIO_STOP:
+		MM_DBG("AUDIO_STOP\n");
+		rc = audio_disable(audio);
+		audio->stopped = 1;
+		audio_ioport_reset(audio);
+		audio->stopped = 0;
+		break;
+	case AUDIO_FLUSH:
+		MM_DBG("AUDIO_FLUSH\n");
+		audio->rflush = 1;
+		audio->wflush = 1;
+		audio_ioport_reset(audio);
+		if (audio->running) {
+			audpp_flush(audio->dec_id);
+			rc = wait_event_interruptible(audio->write_wait,
+				!audio->wflush);
+			if (rc < 0) {
+				MM_ERR("AUDIO_FLUSH interrupted\n");
+				rc = -EINTR;
+			}
+		} 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->mfield = config.meta_field;
+		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.meta_field = 0;
+		config.unused[0] = 0;
+		config.unused[1] = 0;
+		config.unused[2] = 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 = audio->pcm_feedback;
+		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.pcm_feedback != audio->pcm_feedback) {
+				MM_ERR("Not sufficient permission to"
+					 "change the playback mode\n");
+				rc = -EACCES;
+				break;
+			}
+			if (audio->drv_status & ADRV_STATUS_AIO_INTF) {
+				rc = 0;
+				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)) {
+				MM_DBG("allocate PCM buffer %d\n",
+					config.buffer_count *
+					config.buffer_size);
+				audio->read_phys = pmem_kalloc(
+							config.buffer_size *
+							config.buffer_count,
+							PMEM_MEMTYPE_EBI1|
+							PMEM_ALIGNMENT_4K);
+				if (IS_ERR((void *)audio->read_phys)) {
+					rc = -ENOMEM;
+					break;
+				}
+				audio->read_data = ioremap(audio->read_phys,
+							config.buffer_size *
+							config.buffer_count);
+				if (!audio->read_data) {
+					MM_ERR("malloc read buf failed\n");
+					rc = -ENOMEM;
+					pmem_kfree(audio->read_phys);
+				} else {
+					uint8_t index;
+					uint32_t offset = 0;
+					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;
+					MM_DBG("read buf: phy addr \
+						0x%08x kernel addr 0x%08x\n",
+						audio->read_phys,
+						(int)audio->read_data);
+				}
+			} else {
+				rc = 0;
+			}
+			break;
+		}
+	case AUDIO_PAUSE:
+		MM_DBG("AUDIO_PAUSE %ld\n", arg);
+		rc = audpp_pause(audio->dec_id, (int) arg);
+		break;
+
+	case AUDIO_REGISTER_PMEM: {
+			struct msm_audio_pmem_info info;
+			MM_DBG("AUDIO_REGISTER_PMEM\n");
+			if (copy_from_user(&info, (void *) arg, sizeof(info)))
+				rc = -EFAULT;
+			else
+				rc = audmp3_pmem_add(audio, &info);
+			break;
+		}
+
+	case AUDIO_DEREGISTER_PMEM: {
+			struct msm_audio_pmem_info info;
+			MM_DBG("AUDIO_DEREGISTER_PMEM\n");
+			if (copy_from_user(&info, (void *) arg, sizeof(info)))
+				rc = -EFAULT;
+			else
+				rc = audmp3_pmem_remove(audio, &info);
+			break;
+		}
+	case AUDIO_ASYNC_WRITE:
+		if (audio->drv_status & ADRV_STATUS_FSYNC)
+			rc = -EBUSY;
+		else
+			rc = audmp3_aio_buf_add(audio, 1, (void __user *) arg);
+		break;
+
+	case AUDIO_ASYNC_READ:
+		if (audio->pcm_feedback)
+			rc = audmp3_aio_buf_add(audio, 0, (void __user *) arg);
+		else
+			rc = -EPERM;
+		break;
+
+	default:
+		rc = -EINVAL;
+	}
+	mutex_unlock(&audio->lock);
+	return rc;
+}
+
+/* Only useful in tunnel-mode */
+int audmp3_async_fsync(struct audio *audio)
+{
+	int rc = 0;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+
+	/* Blocking client sends more data */
+	mutex_lock(&audio->lock);
+	audio->drv_status |= ADRV_STATUS_FSYNC;
+	mutex_unlock(&audio->lock);
+
+	mutex_lock(&audio->write_lock);
+	/* pcm dmamiss message is sent continously
+	 * when decoder is starved so no race
+	 * condition concern
+	 */
+	audio->teos = 0;
+
+	rc = wait_event_interruptible(audio->write_wait,
+		(audio->teos && audio->out_needed &&
+		list_empty(&audio->out_queue))
+		|| audio->wflush || audio->stopped);
+
+	if (audio->stopped || audio->wflush)
+		rc = -EBUSY;
+
+	mutex_unlock(&audio->write_lock);
+	mutex_lock(&audio->lock);
+	audio->drv_status &= ~ADRV_STATUS_FSYNC;
+	mutex_unlock(&audio->lock);
+
+	return rc;
+}
+
+int audmp3_sync_fsync(struct audio *audio)
+{
+	struct buffer *frame;
+	int rc = 0;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+
+	mutex_lock(&audio->write_lock);
+
+	rc = wait_event_interruptible(audio->write_wait,
+		(!audio->out[0].used &&
+		!audio->out[1].used &&
+		audio->out_needed) || audio->wflush);
+
+	if (rc < 0)
+		goto done;
+	else if (audio->wflush) {
+		rc = -EBUSY;
+		goto done;
+	}
+
+	if (audio->reserved) {
+		MM_DBG("send reserved byte\n");
+		frame = audio->out + audio->out_tail;
+		((char *) frame->data)[0] = audio->rsv_byte;
+		((char *) frame->data)[1] = 0;
+		frame->used = 2;
+		audio->drv_ops.send_data(audio, 0);
+
+		rc = wait_event_interruptible(audio->write_wait,
+			(!audio->out[0].used &&
+			!audio->out[1].used &&
+			audio->out_needed) || audio->wflush);
+
+		if (rc < 0)
+			goto done;
+		else if (audio->wflush) {
+			rc = -EBUSY;
+			goto done;
+		}
+	}
+
+	/* pcm dmamiss message is sent continously
+	 * when decoder is starved so no race
+	 * condition concern
+	 */
+	audio->teos = 0;
+
+	rc = wait_event_interruptible(audio->write_wait,
+		audio->teos || audio->wflush);
+
+	if (audio->wflush)
+		rc = -EBUSY;
+
+done:
+	mutex_unlock(&audio->write_lock);
+	return rc;
+}
+
+int audmp3_fsync(struct file *file, int datasync)
+{
+	struct audio *audio = file->private_data;
+
+	if (!audio->running || audio->pcm_feedback)
+		return -EINVAL;
+
+	return audio->drv_ops.fsync(audio);
+}
+
+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->drv_status & ADRV_STATUS_AIO_INTF)
+		return -EPERM;
+	else if (!audio->pcm_feedback)
+		return 0; /* PCM feedback disabled. Nothing to read */
+
+	mutex_lock(&audio->read_lock);
+	MM_DBG("%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
+			 */
+			MM_DBG("no partial frame done reading\n");
+			break;
+		} else {
+			MM_DBG("read from in[%d]\n", audio->read_next);
+			/* order reads from the output buffer */
+			rmb();
+			if (copy_to_user
+			    (buf, audio->in[audio->read_next].data,
+			     audio->in[audio->read_next].used)) {
+				MM_ERR("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;
+			break;	/* Force to exit while loop
+				 * to prevent output thread
+				 * sleep too long if data is
+				 * not ready at this moment.
+				 */
+		}
+	}
+
+	/* 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;
+		MM_DBG("kick start pcm feedback again\n");
+		audio->drv_ops.buffer_refresh(audio);
+	}
+
+	mutex_unlock(&audio->read_lock);
+
+	if (buf > start)
+		rc = buf - start;
+
+	MM_DBG("read %d bytes\n", rc);
+	return rc;
+}
+
+static int audmp3_process_eos(struct audio *audio,
+		const char __user *buf_start, unsigned short mfield_size)
+{
+	int rc = 0;
+	struct buffer *frame;
+	char *buf_ptr;
+
+	if (audio->reserved) {
+		MM_DBG("flush reserve byte\n");
+		frame = audio->out + audio->out_head;
+		buf_ptr = frame->data;
+		rc = wait_event_interruptible(audio->write_wait,
+			(frame->used == 0)
+			|| (audio->stopped)
+			|| (audio->wflush));
+		if (rc < 0)
+			goto done;
+		if (audio->stopped || audio->wflush) {
+			rc = -EBUSY;
+			goto done;
+		}
+
+		buf_ptr[0] = audio->rsv_byte;
+		buf_ptr[1] = 0;
+		audio->out_head ^= 1;
+		frame->mfield_sz = 0;
+		frame->used = 2;
+		audio->reserved = 0;
+		audio->drv_ops.send_data(audio, 0);
+	}
+
+	frame = audio->out + audio->out_head;
+
+	rc = wait_event_interruptible(audio->write_wait,
+		(audio->out_needed &&
+		audio->out[0].used == 0 &&
+		audio->out[1].used == 0)
+		|| (audio->stopped)
+		|| (audio->wflush));
+
+	if (rc < 0)
+		goto done;
+	if (audio->stopped || audio->wflush) {
+		rc = -EBUSY;
+		goto done;
+	}
+
+	if (copy_from_user(frame->data, buf_start, mfield_size)) {
+		rc = -EFAULT;
+		goto done;
+	}
+
+	frame->mfield_sz = mfield_size;
+	audio->out_head ^= 1;
+	frame->used = mfield_size;
+	audio->drv_ops.send_data(audio, 0);
+done:
+	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, eos_condition = AUDMP3_EOS_NONE;
+	unsigned dsize;
+	unsigned short mfield_size = 0;
+
+	if (audio->drv_status & ADRV_STATUS_AIO_INTF)
+		return -EPERM;
+
+	MM_DBG("cnt=%d\n", count);
+
+	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->mfield) {
+			if (buf == start) {
+				/* Processing beginning of user buffer */
+				if (__get_user(mfield_size,
+					(unsigned short __user *) buf)) {
+					rc = -EFAULT;
+					break;
+				} else  if (mfield_size > count) {
+					rc = -EINVAL;
+					break;
+				}
+				MM_DBG("mf offset_val %x\n", mfield_size);
+				if (copy_from_user(cpy_ptr, buf, mfield_size)) {
+					rc = -EFAULT;
+					break;
+				}
+				/* Check if EOS flag is set and buffer has
+				 * contains just meta field
+				 */
+				if (cpy_ptr[AUDMP3_EOS_FLG_OFFSET] &
+						 AUDMP3_EOS_FLG_MASK) {
+					MM_DBG("EOS SET\n");
+					eos_condition = AUDMP3_EOS_SET;
+					if (mfield_size == count) {
+						buf += mfield_size;
+						break;
+					} else
+						cpy_ptr[AUDMP3_EOS_FLG_OFFSET]
+							&= ~AUDMP3_EOS_FLG_MASK;
+				}
+				cpy_ptr += mfield_size;
+				count -= mfield_size;
+				dsize += mfield_size;
+				buf += mfield_size;
+			} else {
+				mfield_size = 0;
+				MM_DBG("continuous buffer\n");
+			}
+			frame->mfield_sz = mfield_size;
+		}
+
+		if (audio->reserved) {
+			MM_DBG("append reserved byte %x\n", audio->rsv_byte);
+			*cpy_ptr = audio->rsv_byte;
+			xfer = (count > ((frame->size - mfield_size) - 1)) ?
+				(frame->size - mfield_size) - 1 : count;
+			cpy_ptr++;
+			dsize += 1;
+			audio->reserved = 0;
+		} else
+			xfer = (count > (frame->size - mfield_size)) ?
+				(frame->size - mfield_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];
+			MM_DBG("odd length buf reserve last byte %x\n",
+					audio->rsv_byte);
+			audio->reserved = 1;
+			dsize--;
+		}
+		count -= xfer;
+		buf += xfer;
+
+		if (dsize > 0) {
+			audio->out_head ^= 1;
+			frame->used = dsize;
+			audio->drv_ops.send_data(audio, 0);
+		}
+	}
+	if (eos_condition == AUDMP3_EOS_SET)
+		rc = audmp3_process_eos(audio, start, mfield_size);
+	mutex_unlock(&audio->write_lock);
+	if (!rc) {
+		if (buf > start)
+			return buf - start;
+	}
+	return rc;
+}
+
+static void audmp3_reset_pmem_region(struct audio *audio)
+{
+	struct audmp3_pmem_region *region;
+	struct list_head *ptr, *next;
+
+	list_for_each_safe(ptr, next, &audio->pmem_region_queue) {
+		region = list_entry(ptr, struct audmp3_pmem_region, list);
+		list_del(&region->list);
+		put_pmem_file(region->file);
+		kfree(region);
+	}
+
+	return;
+}
+
+static int audio_release(struct inode *inode, struct file *file)
+{
+	struct audio *audio = file->private_data;
+
+	MM_INFO("audio instance 0x%08x freeing\n", (int)audio);
+	mutex_lock(&audio->lock);
+	audio_disable(audio);
+	if (audio->rmt_resource_released == 0)
+		rmt_put_resource(audio);
+	audio->drv_ops.out_flush(audio);
+	audio->drv_ops.in_flush(audio);
+	audmp3_reset_pmem_region(audio);
+
+	msm_adsp_put(audio->audplay);
+	audpp_adec_free(audio->dec_id);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	unregister_early_suspend(&audio->suspend_ctl.node);
+#endif
+	audio->opened = 0;
+	audio->event_abort = 1;
+	wake_up(&audio->event_wait);
+	audmp3_reset_event_queue(audio);
+	MM_DBG("pmem area = 0x%8x\n", (unsigned int)audio->data);
+	if (audio->data) {
+		iounmap(audio->data);
+		pmem_kfree(audio->phys);
+	}
+	if (audio->read_data) {
+		iounmap(audio->read_data);
+		pmem_kfree(audio->read_phys);
+	}
+	mutex_unlock(&audio->lock);
+#ifdef CONFIG_DEBUG_FS
+	if (audio->dentry)
+		debugfs_remove(audio->dentry);
+#endif
+	kfree(audio);
+	return 0;
+}
+
+static void audmp3_post_event(struct audio *audio, int type,
+	union msm_audio_event_payload payload)
+{
+	struct audmp3_event *e_node = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+
+	if (!list_empty(&audio->free_event_queue)) {
+		e_node = list_first_entry(&audio->free_event_queue,
+			struct audmp3_event, list);
+		list_del(&e_node->list);
+	} else {
+		e_node = kmalloc(sizeof(struct audmp3_event), GFP_ATOMIC);
+		if (!e_node) {
+			MM_ERR("No mem to post event %d\n", type);
+			spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+			return;
+		}
+	}
+
+	e_node->event_type = type;
+	e_node->payload = payload;
+
+	list_add_tail(&e_node->list, &audio->event_queue);
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+	wake_up(&audio->event_wait);
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void audmp3_suspend(struct early_suspend *h)
+{
+	struct audmp3_suspend_ctl *ctl =
+		container_of(h, struct audmp3_suspend_ctl, node);
+	union msm_audio_event_payload payload;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	audmp3_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload);
+}
+
+static void audmp3_resume(struct early_suspend *h)
+{
+	struct audmp3_suspend_ctl *ctl =
+		container_of(h, struct audmp3_suspend_ctl, node);
+	union msm_audio_event_payload payload;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	audmp3_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload);
+}
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t audmp3_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t audmp3_debug_read(struct file *file, char __user *buf,
+			  size_t count, loff_t *ppos)
+{
+	const int debug_bufmax = 4096;
+	static char buffer[4096];
+	int n = 0, i;
+	struct audio *audio = file->private_data;
+
+	mutex_lock(&audio->lock);
+	n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "enabled %d\n", audio->enabled);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "stopped %d\n", audio->stopped);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "pcm_feedback %d\n", audio->pcm_feedback);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "out_buf_sz %d\n", audio->out[0].size);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "pcm_buf_count %d \n", audio->pcm_buf_count);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "pcm_buf_sz %d \n", audio->in[0].size);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "volume %x \n", audio->vol_pan.volume);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "sample rate %d \n", audio->out_sample_rate);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+		"channel mode %d \n", audio->out_channel_mode);
+	mutex_unlock(&audio->lock);
+	/* Following variables are only useful for debugging when
+	 * when playback halts unexpectedly. Thus, no mutual exclusion
+	 * enforced
+	 */
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "wflush %d\n", audio->wflush);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "rflush %d\n", audio->rflush);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "running %d \n", audio->running);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				"dec state %d \n", audio->dec_state);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "out_needed %d \n", audio->out_needed);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "out_head %d \n", audio->out_head);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "out_tail %d \n", audio->out_tail);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "out[0].used %d \n", audio->out[0].used);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "out[1].used %d \n", audio->out[1].used);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "buffer_refresh %d \n", audio->buf_refresh);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "read_next %d \n", audio->read_next);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "fill_next %d \n", audio->fill_next);
+	for (i = 0; i < audio->pcm_buf_count; i++)
+		n += scnprintf(buffer + n, debug_bufmax - n,
+			"in[%d].size %d \n", i, audio->in[i].used);
+	buffer[n] = 0;
+	return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+
+static const struct file_operations audmp3_debug_fops = {
+	.read = audmp3_debug_read,
+	.open = audmp3_debug_open,
+};
+#endif
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+
+	struct audio *audio = NULL;
+	int rc, i, dec_attrb, decid;
+	struct audmp3_event *e_node = NULL;
+	unsigned pmem_sz = DMASZ_MAX;
+#ifdef CONFIG_DEBUG_FS
+	/* 4 bytes represents decoder number, 1 byte for terminate string */
+	char name[sizeof "msm_mp3_" + 5];
+#endif
+
+	/* Allocate audio instance, set to zero */
+	audio = kzalloc(sizeof(struct audio), GFP_KERNEL);
+	if (!audio) {
+		MM_ERR("no memory to allocate audio instance \n");
+		rc = -ENOMEM;
+		goto done;
+	}
+	MM_INFO("audio instance 0x%08x created\n", (int)audio);
+
+	/* Allocate the decoder */
+	dec_attrb = AUDDEC_DEC_MP3;
+	if ((file->f_mode & FMODE_WRITE) &&
+			(file->f_mode & FMODE_READ)) {
+		dec_attrb |= MSM_AUD_MODE_NONTUNNEL;
+		audio->pcm_feedback = NON_TUNNEL_MODE_PLAYBACK;
+	} else if ((file->f_mode & FMODE_WRITE) &&
+			!(file->f_mode & FMODE_READ)) {
+		dec_attrb |= MSM_AUD_MODE_TUNNEL;
+		audio->pcm_feedback = TUNNEL_MODE_PLAYBACK;
+	} else {
+		kfree(audio);
+		rc = -EACCES;
+		goto done;
+	}
+
+	decid = audpp_adec_alloc(dec_attrb, &audio->module_name,
+			&audio->queue_id);
+	if (decid < 0) {
+		MM_ERR("No free decoder available, freeing instance 0x%08x\n",
+				(int)audio);
+		rc = -ENODEV;
+		kfree(audio);
+		goto done;
+	}
+	audio->dec_id = decid & MSM_AUD_DECODER_MASK;
+
+	/* Non AIO interface */
+	if (!(file->f_flags & O_NONBLOCK)) {
+		while (pmem_sz >= DMASZ_MIN) {
+			MM_DBG("pmemsz = %d \n", pmem_sz);
+			audio->phys = pmem_kalloc(pmem_sz, PMEM_MEMTYPE_EBI1|
+						PMEM_ALIGNMENT_4K);
+			if (!IS_ERR((void *)audio->phys)) {
+				audio->data = ioremap(audio->phys, pmem_sz);
+				if (!audio->data) {
+					MM_ERR("could not allocate write \
+						buffers, freeing instance \
+						0x%08x\n", (int)audio);
+					rc = -ENOMEM;
+					pmem_kfree(audio->phys);
+					audpp_adec_free(audio->dec_id);
+					kfree(audio);
+					goto done;
+				}
+				MM_DBG("write buf: phy addr 0x%08x kernel addr\
+					0x%08x\n", audio->phys,\
+					(int)audio->data);
+				break;
+			} else if (pmem_sz == DMASZ_MIN) {
+				MM_ERR("could not allocate write buffers, \
+						freeing instance 0x%08x\n",
+						(int)audio);
+				rc = -ENOMEM;
+				audpp_adec_free(audio->dec_id);
+				kfree(audio);
+				goto done;
+			} else
+				pmem_sz >>= 1;
+		}
+		audio->out_dma_sz = pmem_sz;
+	}
+
+	if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) {
+		rc = audmgr_open(&audio->audmgr);
+		if (rc) {
+			MM_ERR("audmgr open failed, freeing instance \
+					0x%08x\n", (int)audio);
+			goto err;
+		}
+	}
+
+	rc = msm_adsp_get(audio->module_name, &audio->audplay,
+		&audplay_adsp_ops, audio);
+
+	if (rc) {
+		MM_ERR("failed to get %s module, freeing instance 0x%08x\n",
+				audio->module_name, (int)audio);
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			audmgr_close(&audio->audmgr);
+		goto err;
+	}
+
+	rc = rmt_get_resource(audio);
+	if (rc) {
+		MM_ERR("ADSP resources are not available for MP3 session \
+			 0x%08x on decoder: %d\n", (int)audio, audio->dec_id);
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			audmgr_close(&audio->audmgr);
+		msm_adsp_put(audio->audplay);
+		goto err;
+	}
+
+	if (file->f_flags & O_NONBLOCK) {
+		MM_DBG("set to aio interface\n");
+		audio->drv_status |= ADRV_STATUS_AIO_INTF;
+		audio->drv_ops.pcm_buf_update = audmp3_async_pcm_buf_update;
+		audio->drv_ops.buffer_refresh = audmp3_async_buffer_refresh;
+		audio->drv_ops.send_data = audmp3_async_send_data;
+		audio->drv_ops.out_flush = audmp3_async_flush;
+		audio->drv_ops.in_flush = audmp3_async_flush_pcm_buf;
+		audio->drv_ops.fsync = audmp3_async_fsync;
+	} else {
+		MM_DBG("set to std io interface\n");
+		audio->drv_ops.pcm_buf_update = audio_update_pcm_buf_entry;
+		audio->drv_ops.buffer_refresh = audplay_buffer_refresh;
+		audio->drv_ops.send_data = audplay_send_data;
+		audio->drv_ops.out_flush = audio_flush;
+		audio->drv_ops.in_flush = audio_flush_pcm_buf;
+		audio->drv_ops.fsync = audmp3_sync_fsync;
+		audio->out[0].data = audio->data + 0;
+		audio->out[0].addr = audio->phys + 0;
+		audio->out[0].size = (audio->out_dma_sz >> 1);
+
+		audio->out[1].data = audio->data + audio->out[0].size;
+		audio->out[1].addr = audio->phys + audio->out[0].size;
+		audio->out[1].size = audio->out[0].size;
+	}
+
+	/* Initialize all locks of audio instance */
+	mutex_init(&audio->lock);
+	mutex_init(&audio->write_lock);
+	mutex_init(&audio->read_lock);
+	mutex_init(&audio->get_event_lock);
+	spin_lock_init(&audio->dsp_lock);
+	init_waitqueue_head(&audio->write_wait);
+	init_waitqueue_head(&audio->read_wait);
+	INIT_LIST_HEAD(&audio->out_queue);
+	INIT_LIST_HEAD(&audio->in_queue);
+	INIT_LIST_HEAD(&audio->pmem_region_queue);
+	INIT_LIST_HEAD(&audio->free_event_queue);
+	INIT_LIST_HEAD(&audio->event_queue);
+	init_waitqueue_head(&audio->wait);
+	init_waitqueue_head(&audio->event_wait);
+	spin_lock_init(&audio->event_queue_lock);
+
+	audio->out_sample_rate = 44100;
+	audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V;
+	audio->vol_pan.volume = 0x2000;
+
+	audio->drv_ops.out_flush(audio);
+
+	file->private_data = audio;
+	audio->opened = 1;
+#ifdef CONFIG_DEBUG_FS
+	snprintf(name, sizeof name, "msm_mp3_%04x", audio->dec_id);
+	audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+			NULL, (void *) audio, &audmp3_debug_fops);
+
+	if (IS_ERR(audio->dentry))
+		MM_DBG("debugfs_create_file failed\n");
+#endif
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
+	audio->suspend_ctl.node.resume = audmp3_resume;
+	audio->suspend_ctl.node.suspend = audmp3_suspend;
+	audio->suspend_ctl.audio = audio;
+	register_early_suspend(&audio->suspend_ctl.node);
+#endif
+	for (i = 0; i < AUDMP3_EVENT_NUM; i++) {
+		e_node = kmalloc(sizeof(struct audmp3_event), GFP_KERNEL);
+		if (e_node)
+			list_add_tail(&e_node->list, &audio->free_event_queue);
+		else {
+			MM_ERR("event pkt alloc failed\n");
+			break;
+		}
+	}
+done:
+	return rc;
+err:
+	if (audio->data) {
+		iounmap(audio->data);
+		pmem_kfree(audio->phys);
+	}
+	audpp_adec_free(audio->dec_id);
+	kfree(audio);
+	return rc;
+}
+
+static const 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,
+	.fsync = audmp3_fsync,
+};
+
+struct miscdevice audio_mp3_misc = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "msm_mp3",
+	.fops	= &audio_mp3_fops,
+};
+
+static int __init audio_init(void)
+{
+	return misc_register(&audio_mp3_misc);
+}
+
+static void __exit audio_exit(void)
+{
+	misc_deregister(&audio_mp3_misc);
+}
+
+module_init(audio_init);
+module_exit(audio_exit);
+
+MODULE_DESCRIPTION("MSM MP3 driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5/audio_out.c b/arch/arm/mach-msm/qdsp5/audio_out.c
new file mode 100644
index 0000000..7c56037
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audio_out.c
@@ -0,0 +1,1020 @@
+/* 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 <mach/debug_mm.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 COMMON_OBJ_ID 6
+
+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 teos; /* valid only if tunnel mode & no data left for decoder */
+	int opened;
+	int enabled;
+	int running;
+	int stopped; /* set when stopped, cleared on flush */
+
+	struct wake_lock wakelock;
+	struct wake_lock idlelock;
+
+	audpp_cmd_cfg_object_params_volume vol_pan;
+};
+
+struct audio_copp {
+	int mbadrc_enable;
+	int mbadrc_needs_commit;
+	char *mbadrc_data;
+	dma_addr_t mbadrc_phys;
+
+	audpp_cmd_cfg_object_params_mbadrc mbadrc;
+
+	int eq_enable;
+	int eq_needs_commit;
+	audpp_cmd_cfg_object_params_eqalizer eq;
+
+	int rx_iir_enable;
+	int rx_iir_needs_commit;
+	audpp_cmd_cfg_object_params_pcm iir;
+
+	audpp_cmd_cfg_object_params_volume vol_pan;
+
+	int qconcert_plus_enable;
+	int qconcert_plus_needs_commit;
+	audpp_cmd_cfg_object_params_qconcert qconcert_plus;
+
+	int status;
+	int opened;
+	struct mutex lock;
+
+	struct audpp_event_callback ecb;
+} the_audio_copp;
+
+static void audio_prevent_sleep(struct audio *audio)
+{
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	wake_lock(&audio->wakelock);
+	wake_lock(&audio->idlelock);
+}
+
+static void audio_allow_sleep(struct audio *audio)
+{
+	wake_unlock(&audio->wakelock);
+	wake_unlock(&audio->idlelock);
+	MM_DBG("\n"); /* Macro prints the file name and function */
+}
+
+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 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;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+
+	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)) {
+		MM_ERR("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)
+{
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	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;
+}
+
+void audio_commit_pending_pp_params(void *priv, unsigned id, uint16_t *msg)
+{
+	struct audio_copp *audio_copp = priv;
+
+	if (AUDPP_MSG_CFG_MSG == id && msg[0] == AUDPP_MSG_ENA_DIS)
+		return;
+
+	if (!audio_copp->status)
+		return;
+
+	audpp_dsp_set_mbadrc(COMMON_OBJ_ID, audio_copp->mbadrc_enable,
+						&audio_copp->mbadrc);
+
+	audpp_dsp_set_eq(COMMON_OBJ_ID, audio_copp->eq_enable,
+						&audio_copp->eq);
+
+	audpp_dsp_set_rx_iir(COMMON_OBJ_ID, audio_copp->rx_iir_enable,
+							&audio_copp->iir);
+	audpp_dsp_set_vol_pan(COMMON_OBJ_ID, &audio_copp->vol_pan);
+
+	audpp_dsp_set_qconcert_plus(COMMON_OBJ_ID,
+				audio_copp->qconcert_plus_enable,
+				&audio_copp->qconcert_plus);
+}
+EXPORT_SYMBOL(audio_commit_pending_pp_params);
+
+/* ------------------- 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;
+
+		/* MM_INFO("HOST_PCM id %d idx %d\n", id, idx); */
+		if (id != AUDPP_MSG_HOSTPCM_ID_ARM_RX) {
+			MM_ERR("bogus id\n");
+			break;
+		}
+		if (idx > 1) {
+			MM_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:
+		MM_INFO("PCMDMAMISSED %d\n", msg[0]);
+		audio->teos = 1;
+		wake_up(&audio->wait);
+		break;
+	case AUDPP_MSG_CFG_MSG:
+		if (msg[0] == AUDPP_MSG_ENA_ENA) {
+			LOG(EV_ENABLE, 1);
+			MM_DBG("CFG_MSG ENABLE\n");
+			audio->out_needed = 0;
+			audio->running = 1;
+			audpp_dsp_set_vol_pan(5, &audio->vol_pan);
+			audio_dsp_out_enable(audio, 1);
+		} else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+			LOG(EV_ENABLE, 0);
+			MM_DBG("CFG_MSG DISABLE\n");
+			audio->running = 0;
+		} else {
+			MM_ERR("CFG_MSG %d?\n", msg[0]);
+		}
+		break;
+	default:
+		MM_ERR("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;
+		if (audio->out[0].used)
+			cmd.write_buf1_len	= audio->out[0].used;
+		else
+			cmd.write_buf1_len	= audio->out[0].size;
+		cmd.write_buf2LSW	= audio->out[1].addr;
+		cmd.write_buf2MSW	= audio->out[1].addr >> 16;
+		if (audio->out[1].used)
+			cmd.write_buf2_len	= audio->out[1].used;
+		else
+			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);
+	dma_coherent_pre_ops();
+	return audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+/* ------------------- device --------------------- */
+
+static int audio_enable_mbadrc(struct audio_copp *audio_copp, int enable)
+{
+	if (audio_copp->mbadrc_enable == enable &&
+				!audio_copp->mbadrc_needs_commit)
+		return 0;
+
+	audio_copp->mbadrc_enable = enable;
+	if (is_audpp_enable()) {
+		audpp_dsp_set_mbadrc(COMMON_OBJ_ID, enable,
+						&audio_copp->mbadrc);
+		audio_copp->mbadrc_needs_commit = 0;
+	}
+
+	return 0;
+}
+
+static int audio_enable_eq(struct audio_copp *audio_copp, int enable)
+{
+	if (audio_copp->eq_enable == enable &&
+				!audio_copp->eq_needs_commit)
+		return 0;
+
+	audio_copp->eq_enable = enable;
+
+	if (is_audpp_enable()) {
+		audpp_dsp_set_eq(COMMON_OBJ_ID, enable, &audio_copp->eq);
+		audio_copp->eq_needs_commit = 0;
+	}
+	return 0;
+}
+
+static int audio_enable_rx_iir(struct audio_copp *audio_copp, int enable)
+{
+	if (audio_copp->rx_iir_enable == enable &&
+				!audio_copp->rx_iir_needs_commit)
+		return 0;
+
+	audio_copp->rx_iir_enable = enable;
+
+	if (is_audpp_enable()) {
+		audpp_dsp_set_rx_iir(COMMON_OBJ_ID, enable, &audio_copp->iir);
+		audio_copp->rx_iir_needs_commit = 0;
+	}
+	return 0;
+}
+
+static int audio_enable_vol_pan(struct audio_copp *audio_copp)
+{
+	if (is_audpp_enable())
+		audpp_dsp_set_vol_pan(COMMON_OBJ_ID, &audio_copp->vol_pan);
+	return 0;
+}
+
+static int audio_enable_qconcert_plus(struct audio_copp *audio_copp, int enable)
+{
+	if (audio_copp->qconcert_plus_enable == enable &&
+				!audio_copp->qconcert_plus_needs_commit)
+		return 0;
+
+	audio_copp->qconcert_plus_enable = enable;
+
+	if (is_audpp_enable()) {
+		audpp_dsp_set_qconcert_plus(COMMON_OBJ_ID, enable,
+					&audio_copp->qconcert_plus);
+		audio_copp->qconcert_plus_needs_commit = 0;
+	}
+	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 = -EINVAL;
+	unsigned long flags = 0;
+
+	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;
+	}
+
+	switch (cmd) {
+	case AUDIO_SET_VOLUME:
+		spin_lock_irqsave(&audio->dsp_lock, flags);
+		audio->vol_pan.volume = arg;
+		if (audio->running)
+			audpp_dsp_set_vol_pan(5, &audio->vol_pan);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		return 0;
+
+	case AUDIO_SET_PAN:
+		spin_lock_irqsave(&audio->dsp_lock, flags);
+		audio->vol_pan.pan = arg;
+		if (audio->running)
+			audpp_dsp_set_vol_pan(5, &audio->vol_pan);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		return 0;
+	}
+
+	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;
+		if (copy_to_user((void*) arg, &config, sizeof(config))) {
+			rc = -EFAULT;
+		} else {
+			rc = 0;
+		}
+		break;
+	}
+	default:
+		rc = -EINVAL;
+	}
+	mutex_unlock(&audio->lock);
+	return rc;
+}
+
+/* Only useful in tunnel-mode */
+static int audio_fsync(struct file *file,	int datasync)
+{
+	struct audio *audio = file->private_data;
+	int rc = 0;
+
+	if (!audio->running)
+		return -EINVAL;
+
+	mutex_lock(&audio->write_lock);
+
+	rc = wait_event_interruptible(audio->wait,
+		(!audio->out[0].used &&
+		!audio->out[1].used));
+
+	if (rc < 0)
+		goto done;
+
+	/* pcm dmamiss message is sent continously when
+	 * decoder is starved so no race condition concern
+	 */
+
+	audio->teos = 0;
+
+	rc = wait_event_interruptible(audio->wait,
+		audio->teos);
+
+done:
+	mutex_unlock(&audio->write_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);
+		if ((sched_setscheduler(current, SCHED_RR, &s)) < 0)
+			MM_ERR("sched_setscheduler failed\n");
+	}
+
+	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 };
+		if ((sched_setscheduler(current, old_policy, &v)) < 0)
+			MM_ERR("sched_setscheduler failed\n");
+		if (likely(!cap_nice)) {
+			struct cred *new = prepare_creds();
+			cap_lower(new->cap_effective, CAP_SYS_NICE);
+			commit_creds(new);
+		}
+	}
+
+	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) {
+		MM_ERR("busy\n");
+		rc = -EBUSY;
+		goto done;
+	}
+
+	if (!audio->data) {
+		audio->data = dma_alloc_coherent(NULL, DMASZ, 
+						 &audio->phys, GFP_KERNEL);
+		if (!audio->data) {
+			MM_ERR("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->vol_pan.volume = 0x2000;
+	audio->vol_pan.pan = 0x0;
+
+	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_copp *audio_copp = file->private_data;
+	int rc = 0, enable;
+	uint16_t enable_mask;
+	int prev_state;
+
+	mutex_lock(&audio_copp->lock);
+	switch (cmd) {
+	case AUDIO_ENABLE_AUDPP:
+		if (copy_from_user(&enable_mask, (void *) arg,
+						sizeof(enable_mask))) {
+			rc = -EFAULT;
+			break;
+		}
+
+		enable = ((enable_mask & ADRC_ENABLE) ||
+				(enable_mask & MBADRC_ENABLE)) ? 1 : 0;
+		audio_enable_mbadrc(audio_copp, enable);
+		enable = (enable_mask & EQ_ENABLE) ? 1 : 0;
+		audio_enable_eq(audio_copp, enable);
+		enable = (enable_mask & IIR_ENABLE) ? 1 : 0;
+		audio_enable_rx_iir(audio_copp, enable);
+		enable = (enable_mask & QCONCERT_PLUS_ENABLE) ? 1 : 0;
+		audio_enable_qconcert_plus(audio_copp, enable);
+		break;
+
+	case AUDIO_SET_MBADRC: {
+		uint32_t mbadrc_coeff_buf;
+		prev_state = audio_copp->mbadrc_enable;
+		audio_copp->mbadrc_enable = 0;
+		if (copy_from_user(&audio_copp->mbadrc.num_bands, (void *) arg,
+				sizeof(audio_copp->mbadrc) -
+				(AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2)))
+			rc = -EFAULT;
+		else if (audio_copp->mbadrc.ext_buf_size) {
+			mbadrc_coeff_buf = (uint32_t) ((char *) arg +
+					sizeof(audio_copp->mbadrc) -
+				(AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2));
+			if ((copy_from_user(audio_copp->mbadrc_data,
+					(void *) mbadrc_coeff_buf,
+					AUDPP_MBADRC_EXTERNAL_BUF_SIZE * 2))) {
+				rc = -EFAULT;
+				break;
+			}
+			audio_copp->mbadrc.ext_buf_lsw =
+					audio_copp->mbadrc_phys & 0xFFFF;
+			audio_copp->mbadrc.ext_buf_msw =
+				((audio_copp->mbadrc_phys & 0xFFFF0000) >> 16);
+		}
+		audio_copp->mbadrc_enable = prev_state;
+		if (!rc)
+			audio_copp->mbadrc_needs_commit = 1;
+		break;
+	}
+
+	case AUDIO_SET_ADRC: {
+			struct audpp_cmd_cfg_object_params_adrc adrc;
+			prev_state = audio_copp->mbadrc_enable;
+			audio_copp->mbadrc_enable = 0;
+			if (copy_from_user(&adrc.compression_th, (void *) arg,
+							sizeof(adrc) - 2)) {
+				rc = -EFAULT;
+				audio_copp->mbadrc_enable = prev_state;
+				break;
+			}
+			audio_copp->mbadrc.num_bands = 1;
+			audio_copp->mbadrc.down_samp_level = 8;
+			audio_copp->mbadrc.adrc_delay = adrc.adrc_delay;
+			audio_copp->mbadrc.ext_buf_size = 0;
+			audio_copp->mbadrc.ext_partition = 0;
+			audio_copp->mbadrc.adrc_band[0].subband_enable = 1;
+			audio_copp->mbadrc.adrc_band[0].adrc_sub_mute = 0;
+			audio_copp->mbadrc.adrc_band[0].rms_time =
+								adrc.rms_time;
+			audio_copp->mbadrc.adrc_band[0].compression_th =
+							adrc.compression_th;
+			audio_copp->mbadrc.adrc_band[0].compression_slope =
+							adrc.compression_slope;
+			audio_copp->mbadrc.adrc_band[0].attack_const_lsw =
+							adrc.attack_const_lsw;
+			audio_copp->mbadrc.adrc_band[0].attack_const_msw =
+							adrc.attack_const_msw;
+			audio_copp->mbadrc.adrc_band[0].release_const_lsw =
+							adrc.release_const_lsw;
+			audio_copp->mbadrc.adrc_band[0].release_const_msw =
+							adrc.release_const_msw;
+			audio_copp->mbadrc.adrc_band[0].makeup_gain = 0x2000;
+			audio_copp->mbadrc_enable = prev_state;
+			audio_copp->mbadrc_needs_commit = 1;
+			break;
+		}
+
+	case AUDIO_SET_EQ:
+		prev_state = audio_copp->eq_enable;
+		audio_copp->eq_enable = 0;
+		if (copy_from_user(&audio_copp->eq.num_bands, (void *) arg,
+				sizeof(audio_copp->eq) -
+				(AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2)))
+			rc = -EFAULT;
+		audio_copp->eq_enable = prev_state;
+		audio_copp->eq_needs_commit = 1;
+		break;
+
+	case AUDIO_SET_RX_IIR:
+		prev_state = audio_copp->rx_iir_enable;
+		audio_copp->rx_iir_enable = 0;
+		if (copy_from_user(&audio_copp->iir.num_bands, (void *) arg,
+				sizeof(audio_copp->iir) -
+				(AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2)))
+			rc = -EFAULT;
+		audio_copp->rx_iir_enable = prev_state;
+		audio_copp->rx_iir_needs_commit = 1;
+		break;
+
+	case AUDIO_SET_VOLUME:
+		audio_copp->vol_pan.volume = arg;
+		audio_enable_vol_pan(audio_copp);
+		break;
+
+	case AUDIO_SET_PAN:
+		audio_copp->vol_pan.pan = arg;
+		audio_enable_vol_pan(audio_copp);
+		break;
+
+	case AUDIO_SET_QCONCERT_PLUS:
+		prev_state = audio_copp->qconcert_plus_enable;
+		audio_copp->qconcert_plus_enable = 0;
+		if (copy_from_user(&audio_copp->qconcert_plus.op_mode,
+				(void *) arg,
+				sizeof(audio_copp->qconcert_plus) -
+				(AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2)))
+			rc = -EFAULT;
+		audio_copp->qconcert_plus_enable = prev_state;
+		audio_copp->qconcert_plus_needs_commit = 1;
+		break;
+
+	default:
+		rc = -EINVAL;
+	}
+
+	mutex_unlock(&audio_copp->lock);
+	return rc;
+}
+
+static int audpp_open(struct inode *inode, struct file *file)
+{
+	struct audio_copp *audio_copp = &the_audio_copp;
+	int rc;
+
+	mutex_lock(&audio_copp->lock);
+	if (audio_copp->opened) {
+		mutex_unlock(&audio_copp->lock);
+		return -EBUSY;
+	}
+
+	audio_copp->opened = 1;
+
+	if (!audio_copp->status) {
+		audio_copp->ecb.fn = audio_commit_pending_pp_params;
+		audio_copp->ecb.private = audio_copp;
+		rc = audpp_register_event_callback(&audio_copp->ecb);
+		if (rc) {
+			audio_copp->opened = 0;
+			mutex_unlock(&audio_copp->lock);
+			return rc;
+		}
+		audio_copp->mbadrc_data = dma_alloc_coherent(NULL,
+				AUDPP_MBADRC_EXTERNAL_BUF_SIZE * 2,
+				 &audio_copp->mbadrc_phys, GFP_KERNEL);
+		if (!audio_copp->mbadrc_data) {
+			MM_ERR("could not allocate DMA buffers\n");
+			audio_copp->opened = 0;
+			audpp_unregister_event_callback(&audio_copp->ecb);
+			mutex_unlock(&audio_copp->lock);
+			return -ENOMEM;
+		}
+		audio_copp->vol_pan.volume = 0x2000;
+		audio_copp->vol_pan.pan = 0x0;
+		audio_copp->status = 1;
+	}
+
+	file->private_data = audio_copp;
+	mutex_unlock(&audio_copp->lock);
+
+	return 0;
+}
+
+static int audpp_release(struct inode *inode, struct file *file)
+{
+	struct audio_copp *audio_copp = &the_audio_copp;
+
+	audio_copp->opened = 0;
+
+	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,
+	.fsync		= audio_fsync,
+};
+
+static struct file_operations audpp_fops = {
+	.owner		= THIS_MODULE,
+	.open		= audpp_open,
+	.release	= audpp_release,
+	.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);
+	mutex_init(&the_audio_copp.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/arch/arm/mach-msm/qdsp5/audio_pcm.c b/arch/arm/mach-msm/qdsp5/audio_pcm.c
new file mode 100644
index 0000000..7a26b50
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audio_pcm.c
@@ -0,0 +1,1692 @@
+/* audio_pcm.c - pcm audio decoder driver
+ *
+ * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Based on the mp3 decoder 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/debugfs.h>
+#include <linux/delay.h>
+#include <linux/earlysuspend.h>
+#include <linux/list.h>
+#include <linux/android_pmem.h>
+#include <linux/slab.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 <mach/qdsp5/qdsp5rmtcmdi.h>
+#include <mach/debug_mm.h>
+
+/* for queue ids - should be relative to module number*/
+#include "adsp.h"
+
+#define ADRV_STATUS_AIO_INTF 0x00000001
+#define ADRV_STATUS_OBUF_GIVEN 0x00000002
+#define ADRV_STATUS_IBUF_GIVEN 0x00000004
+#define ADRV_STATUS_FSYNC 0x00000008
+
+/* 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 AUDDEC_DEC_PCM 0
+
+/* 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
+
+#define AUDPCM_EVENT_NUM 10 /* Default number of pre-allocated event packets */
+
+#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;							\
+})
+
+struct audio;
+
+struct buffer {
+	void *data;
+	unsigned size;
+	unsigned used;		/* Input usage actual DSP produced PCM size  */
+	unsigned addr;
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+struct audpcm_suspend_ctl {
+  struct early_suspend node;
+  struct audio *audio;
+};
+#endif
+
+struct audpcm_event {
+	struct list_head list;
+	int event_type;
+	union msm_audio_event_payload payload;
+};
+
+struct audpcm_pmem_region {
+	struct list_head list;
+	struct file *file;
+	int fd;
+	void *vaddr_ref;
+	void *vaddr;
+	unsigned long paddr;
+	unsigned long kvaddr;
+	unsigned long len;
+	unsigned ref_cnt;
+};
+
+struct audpcm_buffer_node {
+	struct list_head list;
+	struct msm_audio_aio_buf buf;
+	unsigned long paddr;
+};
+
+struct audpcm_drv_operations {
+	void (*send_data)(struct audio *, unsigned);
+	void (*out_flush)(struct audio *);
+	int (*fsync)(struct audio *);
+};
+
+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;
+	struct list_head out_queue; /* queue to retain output buffers */
+	atomic_t out_bytes;
+
+	struct mutex lock;
+	struct mutex write_lock;
+	wait_queue_head_t write_wait;
+
+	struct msm_adsp_module *audplay;
+
+	/* configuration to use on next enable */
+	uint32_t out_sample_rate;
+	uint32_t out_channel_mode;
+	uint32_t out_bits; /* bits per sample */
+
+	struct audmgr audmgr;
+
+	/* data allocated for various buffers */
+	char *data;
+	int32_t phys;
+
+	uint32_t drv_status;
+	int wflush; /* Write flush */
+	int opened;
+	int enabled;
+	int running;
+	int stopped; /* set when stopped, cleared on flush */
+	int teos; /* valid only if tunnel mode & no data left for decoder */
+	int rmt_resource_released;
+	enum msm_aud_decoder_state dec_state; /* Represents decoder state */
+	int reserved; /* A byte is being reserved */
+	char rsv_byte; /* Handle odd length user data */
+
+	const char *module_name;
+	unsigned queue_id;
+
+	unsigned volume;
+
+	uint16_t dec_id;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	struct audpcm_suspend_ctl suspend_ctl;
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *dentry;
+#endif
+	wait_queue_head_t wait;
+	struct list_head free_event_queue;
+	struct list_head event_queue;
+	wait_queue_head_t event_wait;
+	spinlock_t event_queue_lock;
+	struct mutex get_event_lock;
+	int event_abort;
+
+	struct list_head pmem_region_queue;
+	struct audpcm_drv_operations drv_ops;
+};
+
+static int auddec_dsp_config(struct audio *audio, int enable);
+static void audpp_cmd_cfg_adec_params(struct audio *audio);
+static void audplay_send_data(struct audio *audio, unsigned needed);
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg);
+static void audpcm_post_event(struct audio *audio, int type,
+	union msm_audio_event_payload payload);
+static unsigned long audpcm_pmem_fixup(struct audio *audio, void *addr,
+	unsigned long len, int ref_up);
+
+static int rmt_put_resource(struct audio *audio)
+{
+	struct aud_codec_config_cmd cmd;
+	unsigned short client_idx;
+
+	cmd.cmd_id = RM_CMD_AUD_CODEC_CFG;
+	cmd.client_id = RM_AUD_CLIENT_ID;
+	cmd.task_id = audio->dec_id;
+	cmd.enable = RMT_DISABLE;
+	cmd.dec_type = AUDDEC_DEC_PCM;
+	client_idx = ((cmd.client_id << 8) | cmd.task_id);
+
+	return put_adsp_resource(client_idx, &cmd, sizeof(cmd));
+}
+
+static int rmt_get_resource(struct audio *audio)
+{
+	struct aud_codec_config_cmd cmd;
+	unsigned short client_idx;
+
+	cmd.cmd_id = RM_CMD_AUD_CODEC_CFG;
+	cmd.client_id = RM_AUD_CLIENT_ID;
+	cmd.task_id = audio->dec_id;
+	cmd.enable = RMT_ENABLE;
+	cmd.dec_type = AUDDEC_DEC_PCM;
+	client_idx = ((cmd.client_id << 8) | cmd.task_id);
+
+	return get_adsp_resource(client_idx, &cmd, sizeof(cmd));
+}
+
+/* must be called with audio->lock held */
+static int audio_enable(struct audio *audio)
+{
+	struct audmgr_config cfg;
+	int rc;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	if (audio->enabled)
+		return 0;
+
+	if (audio->rmt_resource_released == 1) {
+		audio->rmt_resource_released = 0;
+		rc = rmt_get_resource(audio);
+		if (rc) {
+			MM_ERR("ADSP resources are not available for PCM \
+				session 0x%08x on decoder: %d\n Ignoring \
+				error and going ahead with the playback\n",
+				(int)audio, audio->dec_id);
+		}
+	}
+
+	audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+	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_PCM;
+	cfg.snd_method = RPC_SND_METHOD_MIDI;
+
+	rc = audmgr_enable(&audio->audmgr, &cfg);
+	if (rc < 0)
+		return rc;
+
+	if (msm_adsp_enable(audio->audplay)) {
+		MM_ERR("msm_adsp_enable(audplay) failed\n");
+		audmgr_disable(&audio->audmgr);
+		return -ENODEV;
+	}
+
+	if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) {
+		MM_ERR("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)
+{
+	int rc = 0;
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	if (audio->enabled) {
+		audio->enabled = 0;
+		audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+		auddec_dsp_config(audio, 0);
+		rc = wait_event_interruptible_timeout(audio->wait,
+				audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+				msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+		if (rc == 0)
+			rc = -ETIMEDOUT;
+		else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE)
+			rc = -EFAULT;
+		else
+			rc = 0;
+		wake_up(&audio->write_wait);
+		msm_adsp_disable(audio->audplay);
+		audpp_disable(audio->dec_id, audio);
+		audmgr_disable(&audio->audmgr);
+		audio->out_needed = 0;
+		rmt_put_resource(audio);
+		audio->rmt_resource_released = 1;
+	}
+	return rc;
+}
+
+/* ------------------- dsp --------------------- */
+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));
+
+	MM_DBG("msg_id=%x\n", id);
+
+	switch (id) {
+	case AUDPLAY_MSG_DEC_NEEDS_DATA:
+		audio->drv_ops.send_data(audio, 1);
+		break;
+	case ADSP_MESSAGE_ID:
+		MM_DBG("Received ADSP event: module enable(audplaytask)\n");
+		break;
+	default:
+		MM_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: {
+				uint16_t reason = msg[2];
+				MM_DBG("decoder status: sleep reason = \
+						0x%04x\n", reason);
+				if ((reason == AUDPP_MSG_REASON_MEM)
+						|| (reason ==
+						AUDPP_MSG_REASON_NODECODER)) {
+					audio->dec_state =
+						MSM_AUD_DECODER_STATE_FAILURE;
+					wake_up(&audio->wait);
+				} else if (reason == AUDPP_MSG_REASON_NONE) {
+					/* decoder is in disable state */
+					audio->dec_state =
+						MSM_AUD_DECODER_STATE_CLOSE;
+					wake_up(&audio->wait);
+				}
+				break;
+			}
+			case AUDPP_DEC_STATUS_INIT:
+				MM_DBG("decoder status: init\n");
+				audpp_cmd_cfg_adec_params(audio);
+				break;
+
+			case AUDPP_DEC_STATUS_CFG:
+				MM_DBG("decoder status: cfg \n");
+				break;
+			case AUDPP_DEC_STATUS_PLAY:
+				MM_DBG("decoder status: play \n");
+				audio->dec_state =
+					MSM_AUD_DECODER_STATE_SUCCESS;
+				wake_up(&audio->wait);
+				break;
+			default:
+				MM_ERR("unknown decoder status \n");
+				break;
+			}
+			break;
+		}
+	case AUDPP_MSG_CFG_MSG:
+		if (msg[0] == AUDPP_MSG_ENA_ENA) {
+			MM_DBG("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);
+		} else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+			MM_DBG("CFG_MSG DISABLE\n");
+			audio->running = 0;
+		} else {
+			MM_ERR("CFG_MSG %d?\n",	msg[0]);
+		}
+		break;
+	case AUDPP_MSG_FLUSH_ACK:
+		MM_DBG("FLUSH_ACK\n");
+		audio->wflush = 0;
+		wake_up(&audio->write_wait);
+		break;
+
+	case AUDPP_MSG_PCMDMAMISSED:
+		MM_DBG("PCMDMAMISSED\n");
+		audio->teos = 1;
+		wake_up(&audio->write_wait);
+		break;
+
+	default:
+		MM_ERR("UNKNOWN (%d)\n", id);
+	}
+
+}
+
+
+struct msm_adsp_ops audpcmdec_adsp_ops = {
+	.event = audplay_dsp_event,
+};
+
+
+#define audplay_send_queue0(audio, cmd, len) \
+	msm_adsp_write(audio->audplay, audio->queue_id, \
+			cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+	u16 cfg_dec_cmd[AUDPP_CMD_CFG_DEC_TYPE_LEN / sizeof(unsigned short)];
+
+	memset(cfg_dec_cmd, 0, sizeof(cfg_dec_cmd));
+
+	cfg_dec_cmd[0] = AUDPP_CMD_CFG_DEC_TYPE;
+	if (enable)
+		cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC |
+				AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_PCM;
+	else
+		cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC |
+				AUDPP_CMD_DIS_DEC_V;
+
+	return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd));
+}
+
+static void audpp_cmd_cfg_adec_params(struct audio *audio)
+{
+	audpp_cmd_cfg_adec_params_wav cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
+	cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_WAV_LEN;
+	cmd.common.dec_id = audio->dec_id;
+	cmd.common.input_sampling_frequency = audio->out_sample_rate;
+	cmd.stereo_cfg = audio->out_channel_mode;
+	cmd.pcm_width = audio->out_bits;
+	cmd.sign = 0;
+	audpp_send_queue2(&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;
+	/* complete writes to the input buffer */
+	wmb();
+	return audplay_send_queue0(audio, &cmd, sizeof(cmd));
+}
+
+static void audpcm_async_send_data(struct audio *audio, unsigned needed)
+{
+	unsigned long flags;
+
+	if (!audio->running)
+		return;
+
+	spin_lock_irqsave(&audio->dsp_lock, flags);
+
+	if (needed && !audio->wflush) {
+		audio->out_needed = 1;
+		if (audio->drv_status & ADRV_STATUS_OBUF_GIVEN) {
+			/* pop one node out of queue */
+			union msm_audio_event_payload payload;
+			struct audpcm_buffer_node *used_buf;
+
+			MM_DBG("consumed\n");
+
+			BUG_ON(list_empty(&audio->out_queue));
+			used_buf = list_first_entry(&audio->out_queue,
+				struct audpcm_buffer_node, list);
+			list_del(&used_buf->list);
+			payload.aio_buf = used_buf->buf;
+			audpcm_post_event(audio, AUDIO_EVENT_WRITE_DONE,
+				payload);
+			kfree(used_buf);
+			audio->drv_status &= ~ADRV_STATUS_OBUF_GIVEN;
+		}
+	}
+	if (audio->out_needed) {
+		struct audpcm_buffer_node *next_buf;
+		audplay_cmd_bitstream_data_avail cmd;
+		if (!list_empty(&audio->out_queue)) {
+			next_buf = list_first_entry(&audio->out_queue,
+					struct audpcm_buffer_node, list);
+			MM_DBG("next_buf %p\n", next_buf);
+			if (next_buf) {
+				MM_DBG("next buf phy %lx len %d\n",
+				next_buf->paddr, next_buf->buf.data_len);
+
+				cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL;
+				if (next_buf->buf.data_len)
+					cmd.decoder_id = audio->dec_id;
+				else {
+					cmd.decoder_id = -1;
+					MM_DBG("input EOS signaled\n");
+				}
+				cmd.buf_ptr	= (unsigned) next_buf->paddr;
+				cmd.buf_size = next_buf->buf.data_len >> 1;
+				cmd.partition_number	= 0;
+				/* complete writes to the input buffer */
+				wmb();
+				audplay_send_queue0(audio, &cmd, sizeof(cmd));
+				audio->out_needed = 0;
+				audio->drv_status |= ADRV_STATUS_OBUF_GIVEN;
+			}
+		}
+	}
+	spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+static void audplay_send_data(struct audio *audio, unsigned needed)
+{
+	struct buffer *frame;
+	unsigned long flags;
+
+	if (!audio->running)
+		return;
+
+	spin_lock_irqsave(&audio->dsp_lock, flags);
+
+	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) {
+			MM_DBG("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);
+			MM_DBG("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;
+		}
+	}
+	spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+/* ------------------- device --------------------- */
+static void audpcm_async_flush(struct audio *audio)
+{
+	struct audpcm_buffer_node *buf_node;
+	struct list_head *ptr, *next;
+	union msm_audio_event_payload payload;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	list_for_each_safe(ptr, next, &audio->out_queue) {
+		buf_node = list_entry(ptr, struct audpcm_buffer_node, list);
+		list_del(&buf_node->list);
+		payload.aio_buf = buf_node->buf;
+		audpcm_post_event(audio, AUDIO_EVENT_WRITE_DONE,
+				payload);
+		kfree(buf_node);
+	}
+	audio->drv_status &= ~ADRV_STATUS_OBUF_GIVEN;
+	audio->out_needed = 0;
+	atomic_set(&audio->out_bytes, 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->reserved = 0;
+	audio->out_needed = 0;
+	atomic_set(&audio->out_bytes, 0);
+}
+
+static void audio_ioport_reset(struct audio *audio)
+{
+	if (audio->drv_status & ADRV_STATUS_AIO_INTF) {
+		/* If fsync is in progress, make sure
+		 * return value of fsync indicates
+		 * abort due to flush
+		 */
+		if (audio->drv_status & ADRV_STATUS_FSYNC) {
+			MM_DBG("fsync in progress\n");
+			wake_up(&audio->write_wait);
+			mutex_lock(&audio->write_lock);
+			audio->drv_ops.out_flush(audio);
+			mutex_unlock(&audio->write_lock);
+		} else
+			audio->drv_ops.out_flush(audio);
+	} else {
+		/* 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->drv_ops.out_flush(audio);
+		mutex_unlock(&audio->write_lock);
+	}
+}
+
+static int audpcm_events_pending(struct audio *audio)
+{
+	unsigned long flags;
+	int empty;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+	empty = !list_empty(&audio->event_queue);
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+	return empty || audio->event_abort;
+}
+
+static void audpcm_reset_event_queue(struct audio *audio)
+{
+	unsigned long flags;
+	struct audpcm_event *drv_evt;
+	struct list_head *ptr, *next;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+	list_for_each_safe(ptr, next, &audio->event_queue) {
+		drv_evt = list_first_entry(&audio->event_queue,
+			struct audpcm_event, list);
+		list_del(&drv_evt->list);
+		kfree(drv_evt);
+	}
+	list_for_each_safe(ptr, next, &audio->free_event_queue) {
+		drv_evt = list_first_entry(&audio->free_event_queue,
+			struct audpcm_event, list);
+		list_del(&drv_evt->list);
+		kfree(drv_evt);
+	}
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+	return;
+}
+
+static long audpcm_process_event_req(struct audio *audio, void __user *arg)
+{
+	long rc;
+	struct msm_audio_event usr_evt;
+	struct audpcm_event *drv_evt = NULL;
+	int timeout;
+	unsigned long flags;
+
+	if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event)))
+		return -EFAULT;
+
+	timeout = (int) usr_evt.timeout_ms;
+
+	if (timeout > 0) {
+		rc = wait_event_interruptible_timeout(
+			audio->event_wait, audpcm_events_pending(audio),
+			msecs_to_jiffies(timeout));
+		if (rc == 0)
+			return -ETIMEDOUT;
+	} else {
+		rc = wait_event_interruptible(
+			audio->event_wait, audpcm_events_pending(audio));
+	}
+
+	if (rc < 0)
+		return rc;
+
+	if (audio->event_abort) {
+		audio->event_abort = 0;
+		return -ENODEV;
+	}
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+	if (!list_empty(&audio->event_queue)) {
+		drv_evt = list_first_entry(&audio->event_queue,
+			struct audpcm_event, list);
+		list_del(&drv_evt->list);
+	}
+	if (drv_evt) {
+		usr_evt.event_type = drv_evt->event_type;
+		usr_evt.event_payload = drv_evt->payload;
+		list_add_tail(&drv_evt->list, &audio->free_event_queue);
+	} else
+		rc = -1;
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+	if (drv_evt && drv_evt->event_type == AUDIO_EVENT_WRITE_DONE) {
+		mutex_lock(&audio->lock);
+		audpcm_pmem_fixup(audio, drv_evt->payload.aio_buf.buf_addr,
+				  drv_evt->payload.aio_buf.buf_len, 0);
+		mutex_unlock(&audio->lock);
+	}
+	if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt)))
+		rc = -EFAULT;
+
+	return rc;
+}
+
+static int audpcm_pmem_check(struct audio *audio,
+		void *vaddr, unsigned long len)
+{
+	struct audpcm_pmem_region *region_elt;
+	struct audpcm_pmem_region t = { .vaddr = vaddr, .len = len };
+
+	list_for_each_entry(region_elt, &audio->pmem_region_queue, list) {
+		if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) ||
+		    OVERLAPS(region_elt, &t)) {
+			MM_ERR("region (vaddr %p len %ld)"
+				" clashes with registered region"
+				" (vaddr %p paddr %p len %ld)\n",
+				vaddr, len,
+				region_elt->vaddr,
+				(void *)region_elt->paddr,
+				region_elt->len);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int audpcm_pmem_add(struct audio *audio,
+	struct msm_audio_pmem_info *info)
+{
+	unsigned long paddr, kvaddr, len;
+	struct file *file;
+	struct audpcm_pmem_region *region;
+	struct vm_area_struct *vma;
+	int rc = -EINVAL;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	region = kmalloc(sizeof(*region), GFP_KERNEL);
+	if (!region)
+		return -ENOMEM;
+
+	if (get_pmem_file(info->fd, &paddr, &kvaddr, &len, &file)) {
+		kfree(region);
+		return -EINVAL;
+	}
+
+	vma = find_vma_intersection(current->active_mm,
+		(unsigned long) info->vaddr, (unsigned long) info->vaddr+1);
+
+	if (vma && ((vma->vm_end - vma->vm_start) == len)) {
+		rc = audpcm_pmem_check(audio, (void *) vma->vm_start, len);
+		if (rc < 0) {
+			put_pmem_file(file);
+			kfree(region);
+			return rc;
+		}
+		region->vaddr = (void *) vma->vm_start;
+		region->vaddr_ref = info->vaddr;
+		MM_DBG("Valid VMA region vma->vm_start = 0x%8x \
+			vma->vm_end = 0x%8x\n", (int) vma->vm_start,
+			(int) vma->vm_end);
+	} else {
+		MM_ERR("No valid VMA region found\n");
+		put_pmem_file(file);
+		kfree(region);
+		return rc;
+	}
+	region->fd = info->fd;
+	region->paddr = paddr;
+	region->kvaddr = kvaddr;
+	region->len = len;
+	region->file = file;
+	region->ref_cnt = 0;
+	MM_DBG("add region paddr %lx vaddr %p, len %lu\n", region->paddr,
+			region->vaddr, region->len);
+	list_add_tail(&region->list, &audio->pmem_region_queue);
+	return rc;
+}
+
+static int audpcm_pmem_remove(struct audio *audio,
+	struct msm_audio_pmem_info *info)
+{
+	struct audpcm_pmem_region *region;
+	struct list_head *ptr, *next;
+	int rc = -EINVAL;
+
+	MM_DBG("info fd %d vaddr %p\n",	info->fd, info->vaddr);
+
+	list_for_each_safe(ptr, next, &audio->pmem_region_queue) {
+		region = list_entry(ptr, struct audpcm_pmem_region, list);
+
+		if ((region->fd == info->fd) &&
+		    (region->vaddr_ref == info->vaddr)) {
+			if (region->ref_cnt) {
+				MM_DBG("region %p in use ref_cnt %d\n",
+						region, region->ref_cnt);
+				break;
+			}
+			MM_DBG("remove region fd %d vaddr %p \n", info->fd,
+					info->vaddr);
+			list_del(&region->list);
+			put_pmem_file(region->file);
+			kfree(region);
+			rc = 0;
+			break;
+		}
+	}
+
+	return rc;
+}
+
+static int audpcm_pmem_lookup_vaddr(struct audio *audio, void *addr,
+		     unsigned long len, struct audpcm_pmem_region **region)
+{
+	struct audpcm_pmem_region *region_elt;
+
+	int match_count = 0;
+
+	*region = NULL;
+
+	/* returns physical address or zero */
+	list_for_each_entry(region_elt, &audio->pmem_region_queue,
+		list) {
+		if (addr >= region_elt->vaddr &&
+		    addr < region_elt->vaddr + region_elt->len &&
+		    addr + 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) {
+		MM_ERR("multiple hits for vaddr %p, len %ld\n", addr, len);
+		list_for_each_entry(region_elt,
+		  &audio->pmem_region_queue, list) {
+			if (addr >= region_elt->vaddr &&
+			    addr < region_elt->vaddr + region_elt->len &&
+			    addr + len <= region_elt->vaddr + region_elt->len)
+				MM_ERR("\t%p, %ld --> %p\n", region_elt->vaddr,
+						region_elt->len,
+						(void *)region_elt->paddr);
+		}
+	}
+
+	return *region ? 0 : -1;
+}
+
+static unsigned long audpcm_pmem_fixup(struct audio *audio, void *addr,
+		    unsigned long len, int ref_up)
+{
+	struct audpcm_pmem_region *region;
+	unsigned long paddr;
+	int ret;
+
+	ret = audpcm_pmem_lookup_vaddr(audio, addr, len, &region);
+	if (ret) {
+		MM_ERR("lookup (%p, %ld) failed\n", addr, len);
+		return 0;
+	}
+	if (ref_up)
+		region->ref_cnt++;
+	else
+		region->ref_cnt--;
+	MM_DBG("found region %p ref_cnt %d\n", region, region->ref_cnt);
+	paddr = region->paddr + (addr - region->vaddr);
+	return paddr;
+}
+
+/* audio -> lock must be held at this point */
+static int audpcm_aio_buf_add(struct audio *audio, unsigned dir,
+	void __user *arg)
+{
+	unsigned long flags;
+	struct audpcm_buffer_node *buf_node;
+
+	buf_node = kmalloc(sizeof(*buf_node), GFP_KERNEL);
+
+	if (!buf_node)
+		return -ENOMEM;
+
+	if (copy_from_user(&buf_node->buf, arg, sizeof(buf_node->buf))) {
+		kfree(buf_node);
+		return -EFAULT;
+	}
+
+	MM_DBG("node %p dir %x buf_addr %p buf_len %d data_len %d\n",
+			buf_node, dir, buf_node->buf.buf_addr,
+			buf_node->buf.buf_len, buf_node->buf.data_len);
+
+	buf_node->paddr = audpcm_pmem_fixup(
+		audio, buf_node->buf.buf_addr,
+		buf_node->buf.buf_len, 1);
+	if (dir) {
+		/* write */
+		if (!buf_node->paddr ||
+		    (buf_node->paddr & 0x1) ||
+		    (buf_node->buf.data_len & 0x1) ||
+		    (!buf_node->buf.data_len)) {
+			kfree(buf_node);
+			return -EINVAL;
+		}
+		spin_lock_irqsave(&audio->dsp_lock, flags);
+		list_add_tail(&buf_node->list, &audio->out_queue);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		audio->drv_ops.send_data(audio, 0);
+	}
+
+	MM_DBG("Add buf_node %p paddr %lx\n", buf_node, buf_node->paddr);
+
+	return 0;
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct audio *audio = file->private_data;
+	int rc = 0;
+
+	MM_DBG("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;
+	}
+	if (cmd == AUDIO_GET_EVENT) {
+		MM_DBG("AUDIO_GET_EVENT\n");
+		if (mutex_trylock(&audio->get_event_lock)) {
+			rc = audpcm_process_event_req(audio,
+				(void __user *) arg);
+			mutex_unlock(&audio->get_event_lock);
+		} else
+			rc = -EBUSY;
+		return rc;
+	}
+
+	if (cmd == AUDIO_ABORT_GET_EVENT) {
+		audio->event_abort = 1;
+		wake_up(&audio->event_wait);
+		return 0;
+	}
+
+	mutex_lock(&audio->lock);
+	switch (cmd) {
+	case AUDIO_START:
+		MM_DBG("AUDIO_START\n");
+		rc = audio_enable(audio);
+		if (!rc) {
+			rc = wait_event_interruptible_timeout(audio->wait,
+				audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+				msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+			MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc);
+
+			if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS)
+				rc = -ENODEV;
+			else
+				rc = 0;
+		}
+		break;
+	case AUDIO_STOP:
+		MM_DBG("AUDIO_STOP\n");
+		rc = audio_disable(audio);
+		audio->stopped = 1;
+		audio_ioport_reset(audio);
+		audio->stopped = 0;
+		break;
+	case AUDIO_FLUSH:
+		MM_DBG("AUDIO_FLUSH\n");
+		audio->wflush = 1;
+		audio_ioport_reset(audio);
+		if (audio->running) {
+			audpp_flush(audio->dec_id);
+			rc = wait_event_interruptible(audio->write_wait,
+				!audio->wflush);
+			if (rc < 0) {
+				MM_ERR("AUDIO_FLUSH interrupted\n");
+				rc = -EINTR;
+			}
+		} else {
+			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;
+		}
+		if (config.bits == 8)
+			config.bits = AUDPP_CMD_WAV_PCM_WIDTH_8;
+		else if (config.bits == 16)
+			config.bits = AUDPP_CMD_WAV_PCM_WIDTH_16;
+		else if (config.bits == 24)
+			config.bits = AUDPP_CMD_WAV_PCM_WIDTH_24;
+		else {
+			rc = -EINVAL;
+			break;
+		}
+		audio->out_sample_rate = config.sample_rate;
+		audio->out_channel_mode = config.channel_count;
+		audio->out_bits = config.bits;
+		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;
+		if (audio->out_bits == AUDPP_CMD_WAV_PCM_WIDTH_8)
+			config.bits = 8;
+		else if (audio->out_bits == AUDPP_CMD_WAV_PCM_WIDTH_24)
+			config.bits = 24;
+		else
+			config.bits = 16;
+		config.unused[0] = 0;
+		config.unused[1] = 0;
+
+		if (copy_to_user((void *) arg, &config, sizeof(config)))
+			rc = -EFAULT;
+		else
+			rc = 0;
+		break;
+	}
+
+	case AUDIO_PAUSE:
+		MM_DBG("AUDIO_PAUSE %ld\n", arg);
+		rc = audpp_pause(audio->dec_id, (int) arg);
+		break;
+
+	case AUDIO_REGISTER_PMEM: {
+			struct msm_audio_pmem_info info;
+			MM_DBG("AUDIO_REGISTER_PMEM\n");
+			if (copy_from_user(&info, (void *) arg, sizeof(info)))
+				rc = -EFAULT;
+			else
+				rc = audpcm_pmem_add(audio, &info);
+			break;
+		}
+
+	case AUDIO_DEREGISTER_PMEM: {
+			struct msm_audio_pmem_info info;
+			MM_DBG("AUDIO_DEREGISTER_PMEM\n");
+			if (copy_from_user(&info, (void *) arg, sizeof(info)))
+				rc = -EFAULT;
+			else
+				rc = audpcm_pmem_remove(audio, &info);
+			break;
+		}
+
+	case AUDIO_ASYNC_WRITE:
+		if (audio->drv_status & ADRV_STATUS_FSYNC)
+			rc = -EBUSY;
+		else
+			rc = audpcm_aio_buf_add(audio, 1, (void __user *) arg);
+		break;
+
+	case AUDIO_ASYNC_READ:
+		MM_ERR("AUDIO_ASYNC_READ not supported\n");
+		rc = -EPERM;
+		break;
+
+	default:
+		rc = -EINVAL;
+	}
+	mutex_unlock(&audio->lock);
+	return rc;
+}
+
+/* Only useful in tunnel-mode */
+int audpcm_async_fsync(struct audio *audio)
+{
+	int rc = 0;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+
+	/* Blocking client sends more data */
+	mutex_lock(&audio->lock);
+	audio->drv_status |= ADRV_STATUS_FSYNC;
+	mutex_unlock(&audio->lock);
+
+	mutex_lock(&audio->write_lock);
+	/* pcm dmamiss message is sent continously
+	 * when decoder is starved so no race
+	 * condition concern
+	 */
+	audio->teos = 0;
+
+	rc = wait_event_interruptible(audio->write_wait,
+		(audio->teos && audio->out_needed &&
+		list_empty(&audio->out_queue))
+		|| audio->wflush || audio->stopped);
+
+	if (audio->stopped || audio->wflush)
+		rc = -EBUSY;
+
+	mutex_unlock(&audio->write_lock);
+	mutex_lock(&audio->lock);
+	audio->drv_status &= ~ADRV_STATUS_FSYNC;
+	mutex_unlock(&audio->lock);
+
+	return rc;
+}
+
+int audpcm_sync_fsync(struct audio *audio)
+{
+	struct buffer *frame;
+	int rc = 0;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+
+	mutex_lock(&audio->write_lock);
+
+	rc = wait_event_interruptible(audio->write_wait,
+		(!audio->out[0].used &&
+		!audio->out[1].used &&
+		audio->out_needed) || audio->wflush);
+
+	if (rc < 0)
+		goto done;
+	else if (audio->wflush) {
+		rc = -EBUSY;
+		goto done;
+	}
+
+	if (audio->reserved) {
+		MM_DBG("send reserved byte\n");
+		frame = audio->out + audio->out_tail;
+		((char *) frame->data)[0] = audio->rsv_byte;
+		((char *) frame->data)[1] = 0;
+		frame->used = 2;
+		audio->drv_ops.send_data(audio, 0);
+
+		rc = wait_event_interruptible(audio->write_wait,
+			(!audio->out[0].used &&
+			!audio->out[1].used &&
+			audio->out_needed) || audio->wflush);
+
+		if (rc < 0)
+			goto done;
+		else if (audio->wflush) {
+			rc = -EBUSY;
+			goto done;
+		}
+	}
+
+	/* pcm dmamiss message is sent continously
+	 * when decoder is starved so no race
+	 * condition concern
+	 */
+	audio->teos = 0;
+
+	rc = wait_event_interruptible(audio->write_wait,
+		audio->teos || audio->wflush);
+
+	if (audio->wflush)
+		rc = -EBUSY;
+
+done:
+	mutex_unlock(&audio->write_lock);
+	return rc;
+}
+
+int audpcm_fsync(struct file *file, int datasync)
+{
+	struct audio *audio = file->private_data;
+
+	if (!audio->running)
+		return -EINVAL;
+
+	return audio->drv_ops.fsync(audio);
+}
+
+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;
+
+	if (audio->drv_status & ADRV_STATUS_AIO_INTF)
+		return -EPERM;
+
+	MM_DBG("cnt=%d\n", count);
+
+	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) {
+			MM_DBG("append reserved byte %x\n", 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];
+			MM_DBG("odd length buf reserve last byte %x\n",
+					audio->rsv_byte);
+			audio->reserved = 1;
+			dsize--;
+		}
+		count -= xfer;
+		buf += xfer;
+
+		if (dsize > 0) {
+			audio->out_head ^= 1;
+			frame->used = dsize;
+			audio->drv_ops.send_data(audio, 0);
+		}
+	}
+	mutex_unlock(&audio->write_lock);
+	if (buf > start)
+		return buf - start;
+
+	return rc;
+}
+
+static void audpcm_reset_pmem_region(struct audio *audio)
+{
+	struct audpcm_pmem_region *region;
+	struct list_head *ptr, *next;
+
+	list_for_each_safe(ptr, next, &audio->pmem_region_queue) {
+		region = list_entry(ptr, struct audpcm_pmem_region, list);
+		list_del(&region->list);
+		put_pmem_file(region->file);
+		kfree(region);
+	}
+
+	return;
+}
+
+static int audio_release(struct inode *inode, struct file *file)
+{
+	struct audio *audio = file->private_data;
+
+	MM_DBG("audio instance 0x%08x freeing\n", (int)audio);
+	mutex_lock(&audio->lock);
+	audio_disable(audio);
+	if (audio->rmt_resource_released == 0)
+		rmt_put_resource(audio);
+	audio->drv_ops.out_flush(audio);
+	audpcm_reset_pmem_region(audio);
+
+	msm_adsp_put(audio->audplay);
+	audpp_adec_free(audio->dec_id);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	unregister_early_suspend(&audio->suspend_ctl.node);
+#endif
+	audio->opened = 0;
+	audio->event_abort = 1;
+	wake_up(&audio->event_wait);
+	audpcm_reset_event_queue(audio);
+	MM_DBG("pmem area = 0x%8x\n", (unsigned int)audio->data);
+	if (audio->data) {
+		iounmap(audio->data);
+		pmem_kfree(audio->phys);
+	}
+	mutex_unlock(&audio->lock);
+#ifdef CONFIG_DEBUG_FS
+	if (audio->dentry)
+		debugfs_remove(audio->dentry);
+#endif
+	kfree(audio);
+	return 0;
+}
+
+static void audpcm_post_event(struct audio *audio, int type,
+	union msm_audio_event_payload payload)
+{
+	struct audpcm_event *e_node = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+
+	if (!list_empty(&audio->free_event_queue)) {
+		e_node = list_first_entry(&audio->free_event_queue,
+			struct audpcm_event, list);
+		list_del(&e_node->list);
+	} else {
+		e_node = kmalloc(sizeof(struct audpcm_event), GFP_ATOMIC);
+		if (!e_node) {
+			MM_ERR("No mem to post event %d\n", type);
+			spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+			return;
+		}
+	}
+
+	e_node->event_type = type;
+	e_node->payload = payload;
+
+	list_add_tail(&e_node->list, &audio->event_queue);
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+	wake_up(&audio->event_wait);
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void audpcm_suspend(struct early_suspend *h)
+{
+	struct audpcm_suspend_ctl *ctl =
+		container_of(h, struct audpcm_suspend_ctl, node);
+	union msm_audio_event_payload payload;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	audpcm_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload);
+}
+
+static void audpcm_resume(struct early_suspend *h)
+{
+	struct audpcm_suspend_ctl *ctl =
+		container_of(h, struct audpcm_suspend_ctl, node);
+	union msm_audio_event_payload payload;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	audpcm_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload);
+}
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t audpcm_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t audpcm_debug_read(struct file *file, char __user *buf,
+			  size_t count, loff_t *ppos)
+{
+	const int debug_bufmax = 4096;
+	static char buffer[4096];
+	int n = 0;
+	struct audio *audio = file->private_data;
+
+	mutex_lock(&audio->lock);
+	n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "enabled %d\n", audio->enabled);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "stopped %d\n", audio->stopped);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "out_buf_sz %d\n", audio->out[0].size);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "volume %x \n", audio->volume);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "sample rate %d \n", audio->out_sample_rate);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+		"channel mode %d \n", audio->out_channel_mode);
+	mutex_unlock(&audio->lock);
+	/* Following variables are only useful for debugging when
+	 * when playback halts unexpectedly. Thus, no mutual exclusion
+	 * enforced
+	 */
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "wflush %d\n", audio->wflush);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "running %d \n", audio->running);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				"dec state %d \n", audio->dec_state);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "out_needed %d \n", audio->out_needed);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "out_head %d \n", audio->out_head);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "out_tail %d \n", audio->out_tail);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "out[0].used %d \n", audio->out[0].used);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "out[1].used %d \n", audio->out[1].used);
+	buffer[n] = 0;
+	return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+
+static const struct file_operations audpcm_debug_fops = {
+	.read = audpcm_debug_read,
+	.open = audpcm_debug_open,
+};
+#endif
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+	struct audio *audio = NULL;
+	int rc, i, dec_attrb, decid;
+	struct audpcm_event *e_node = NULL;
+	unsigned pmem_sz = DMASZ_MAX;
+
+#ifdef CONFIG_DEBUG_FS
+	/* 4 bytes represents decoder number, 1 byte for terminate string */
+	char name[sizeof "msm_pcm_dec_" + 5];
+#endif
+
+	/* Allocate audio instance, set to zero */
+	audio = kzalloc(sizeof(struct audio), GFP_KERNEL);
+	if (!audio) {
+		MM_ERR("no memory to allocate audio instance \n");
+		rc = -ENOMEM;
+		goto done;
+	}
+	MM_DBG("audio instance 0x%08x created\n", (int)audio);
+
+	/* Allocate the decoder */
+	dec_attrb = AUDDEC_DEC_PCM;
+	if (file->f_mode & FMODE_READ) {
+		MM_ERR("Non-Tunneled mode not supported\n");
+		rc = -EPERM;
+		kfree(audio);
+		goto done;
+	} else
+		dec_attrb |= MSM_AUD_MODE_TUNNEL;
+
+	decid = audpp_adec_alloc(dec_attrb, &audio->module_name,
+			&audio->queue_id);
+	if (decid < 0) {
+		MM_ERR("No free decoder available\n");
+		rc = -ENODEV;
+		MM_DBG("audio instance 0x%08x freeing\n", (int)audio);
+		kfree(audio);
+		goto done;
+	}
+	audio->dec_id = decid & MSM_AUD_DECODER_MASK;
+
+	/* Non AIO interface */
+	if (!(file->f_flags & O_NONBLOCK)) {
+		while (pmem_sz >= DMASZ_MIN) {
+			MM_DBG("pmemsz = %d \n", pmem_sz);
+			audio->phys = pmem_kalloc(pmem_sz, PMEM_MEMTYPE_EBI1|
+					PMEM_ALIGNMENT_4K);
+			if (!IS_ERR((void *)audio->phys)) {
+				audio->data = ioremap(audio->phys, pmem_sz);
+				if (!audio->data) {
+					MM_ERR("could not allocate write\
+							buffers\n");
+					rc = -ENOMEM;
+					pmem_kfree(audio->phys);
+					audpp_adec_free(audio->dec_id);
+					MM_DBG("audio instance 0x%08x\
+						freeing\n", (int)audio);
+					kfree(audio);
+					goto done;
+				}
+				MM_DBG("write buf: phy addr 0x%08x kernel addr\
+					0x%08x\n", audio->phys,\
+					(int)audio->data);
+				break;
+			} else if (pmem_sz == DMASZ_MIN) {
+				MM_ERR("could not allocate write buffers\n");
+				rc = -ENOMEM;
+				audpp_adec_free(audio->dec_id);
+				MM_DBG("audio instance 0x%08x freeing\n",\
+					(int)audio);
+				kfree(audio);
+				goto done;
+			} else
+				pmem_sz >>= 1;
+		}
+		audio->out_dma_sz = pmem_sz;
+	}
+
+	rc = audmgr_open(&audio->audmgr);
+	if (rc)
+		goto err;
+
+	rc = msm_adsp_get(audio->module_name, &audio->audplay,
+			&audpcmdec_adsp_ops, audio);
+	if (rc) {
+		MM_ERR("failed to get %s module\n", audio->module_name);
+		audmgr_close(&audio->audmgr);
+		goto err;
+	}
+
+	rc = rmt_get_resource(audio);
+	if (rc) {
+		MM_ERR("ADSP resources are not available for PCM session \
+			 0x%08x on decoder: %d\n", (int)audio, audio->dec_id);
+		audmgr_close(&audio->audmgr);
+		msm_adsp_put(audio->audplay);
+		goto err;
+	}
+
+	if (file->f_flags & O_NONBLOCK) {
+		MM_DBG("set to aio interface\n");
+		audio->drv_status |= ADRV_STATUS_AIO_INTF;
+		audio->drv_ops.send_data = audpcm_async_send_data;
+		audio->drv_ops.out_flush = audpcm_async_flush;
+		audio->drv_ops.fsync = audpcm_async_fsync;
+	} else {
+		MM_DBG("set to std io interface\n");
+		audio->drv_ops.send_data = audplay_send_data;
+		audio->drv_ops.out_flush = audio_flush;
+		audio->drv_ops.fsync = audpcm_sync_fsync;
+		audio->out[0].data = audio->data + 0;
+		audio->out[0].addr = audio->phys + 0;
+		audio->out[0].size = (audio->out_dma_sz >> 1);
+
+		audio->out[1].data = audio->data + audio->out[0].size;
+		audio->out[1].addr = audio->phys + audio->out[0].size;
+		audio->out[1].size = audio->out[0].size;
+	}
+
+	/* Initialize all locks of audio instance */
+	mutex_init(&audio->lock);
+	mutex_init(&audio->write_lock);
+	mutex_init(&audio->get_event_lock);
+	spin_lock_init(&audio->dsp_lock);
+	init_waitqueue_head(&audio->write_wait);
+	INIT_LIST_HEAD(&audio->out_queue);
+	INIT_LIST_HEAD(&audio->pmem_region_queue);
+	INIT_LIST_HEAD(&audio->free_event_queue);
+	INIT_LIST_HEAD(&audio->event_queue);
+	init_waitqueue_head(&audio->wait);
+	init_waitqueue_head(&audio->event_wait);
+	spin_lock_init(&audio->event_queue_lock);
+
+	audio->out_sample_rate = 44100;
+	audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V;
+	audio->out_bits = AUDPP_CMD_WAV_PCM_WIDTH_16;
+	audio->volume = 0x2000;
+	audio->drv_ops.out_flush(audio);
+
+	file->private_data = audio;
+	audio->opened = 1;
+
+#ifdef CONFIG_DEBUG_FS
+	snprintf(name, sizeof name, "msm_pcm_dec_%04x", audio->dec_id);
+	audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+		NULL, (void *) audio, &audpcm_debug_fops);
+
+	if (IS_ERR(audio->dentry))
+		MM_DBG("debugfs_create_file failed\n");
+#endif
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
+	audio->suspend_ctl.node.resume = audpcm_resume;
+	audio->suspend_ctl.node.suspend = audpcm_suspend;
+	audio->suspend_ctl.audio = audio;
+	register_early_suspend(&audio->suspend_ctl.node);
+#endif
+	for (i = 0; i < AUDPCM_EVENT_NUM; i++) {
+		e_node = kmalloc(sizeof(struct audpcm_event), GFP_KERNEL);
+		if (e_node)
+			list_add_tail(&e_node->list, &audio->free_event_queue);
+		else {
+			MM_ERR("event pkt alloc failed\n");
+			break;
+		}
+	}
+done:
+	return rc;
+err:
+	if (audio->data) {
+		iounmap(audio->data);
+		pmem_kfree(audio->phys);
+	}
+	audpp_adec_free(audio->dec_id);
+	MM_DBG("audio instance 0x%08x freeing\n", (int)audio);
+	kfree(audio);
+	return rc;
+}
+
+static const struct file_operations audio_pcm_fops = {
+	.owner		= THIS_MODULE,
+	.open		= audio_open,
+	.release	= audio_release,
+	.write		= audio_write,
+	.unlocked_ioctl	= audio_ioctl,
+	.fsync = audpcm_fsync,
+};
+
+struct miscdevice audio_pcm_misc = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "msm_pcm_dec",
+	.fops	= &audio_pcm_fops,
+};
+
+static int __init audio_init(void)
+{
+	return misc_register(&audio_pcm_misc);
+}
+
+device_initcall(audio_init);
diff --git a/arch/arm/mach-msm/qdsp5/audio_pcm_in.c b/arch/arm/mach-msm/qdsp5/audio_pcm_in.c
new file mode 100644
index 0000000..0b35f1a
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audio_pcm_in.c
@@ -0,0 +1,950 @@
+/* arch/arm/mach-msm/qdsp5/audio_pcm_in.c
+ *
+ * pcm audio input device
+ *
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This code is based in part on arch/arm/mach-msm/qdsp5v2/audio_pcm_in.c,
+ * 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 <mach/msm_memtypes.h>
+#include <linux/memory_alloc.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/qdsp5audpreproc.h>
+#include <mach/qdsp5/qdsp5audpreproccmdi.h>
+#include <mach/qdsp5/qdsp5audpreprocmsg.h>
+#include <mach/qdsp5/qdsp5audreccmdi.h>
+#include <mach/qdsp5/qdsp5audrecmsg.h>
+#include <mach/debug_mm.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)
+
+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;
+	const char *module_name;
+	unsigned queue_ids;
+	uint16_t enc_id; /* Session Id */
+
+	/* 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 enc_type; /* 0 for PCM */
+	uint32_t mode; /* Tunnel for PCM */
+	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;
+	uint32_t audrec_obj_idx ;
+
+	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 tx_agc_enable;
+	audpreproc_cmd_cfg_agc_params tx_agc_cfg;
+	int ns_enable;
+	audpreproc_cmd_cfg_ns_params ns_cfg;
+	/* For different sample rate, the coeff might be different. *
+	 * All the coeff should be passed from user space	    */
+	int iir_enable;
+	audpreproc_cmd_cfg_iir_tuning_filter_params iir_cfg;
+};
+
+static int audpcm_in_dsp_enable(struct audio_in *audio, int enable);
+static int audpcm_in_encmem_config(struct audio_in *audio);
+static int audpcm_in_encparam_config(struct audio_in *audio);
+static int audpcm_in_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt);
+static void audpcm_in_flush(struct audio_in *audio);
+static int audio_dsp_set_tx_agc(struct audio_in *audio);
+static int audio_dsp_set_ns(struct audio_in *audio);
+static int audio_dsp_set_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 audpcm_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;
+	cfg.codec = RPC_AUD_DEF_CODEC_PCM;
+	cfg.snd_method = RPC_SND_METHOD_MIDI;
+
+	rc = audmgr_enable(&audio->audmgr, &cfg);
+	if (rc < 0)
+		return rc;
+
+	if (msm_adsp_enable(audio->audpre)) {
+		MM_ERR("msm_adsp_enable(audpre) failed\n");
+		audmgr_disable(&audio->audmgr);
+		return -ENODEV;
+	}
+	if (msm_adsp_enable(audio->audrec)) {
+		audmgr_disable(&audio->audmgr);
+		msm_adsp_disable(audio->audpre);
+		MM_ERR("msm_adsp_enable(audrec) failed\n");
+		return -ENODEV;
+	}
+
+	audio->enabled = 1;
+	audpcm_in_dsp_enable(audio, 1);
+
+	return 0;
+}
+
+/* must be called with audio->lock held */
+static int audpcm_in_disable(struct audio_in *audio)
+{
+	if (audio->enabled) {
+		audio->enabled = 0;
+
+		audpcm_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:
+		MM_INFO("type %d, status_flag %d\n", msg[0], msg[1]);
+		break;
+	case AUDPREPROC_MSG_ERROR_MSG_ID:
+		MM_ERR("err_index %d\n", msg[0]);
+		break;
+	case ADSP_MESSAGE_ID:
+		MM_DBG("Received ADSP event: module enable(audpreproctask)\n");
+		break;
+	default:
+		MM_ERR("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[];
+} __packed;
+
+static void audpcm_in_get_dsp_frames(struct audio_in *audio)
+{
+	struct audio_frame *frame;
+	uint32_t index;
+	unsigned long flags;
+
+	index = audio->in_head;
+
+	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);
+		MM_ERR("Error! not able to keep up the read\n");
+	} else
+		audio->in_count++;
+
+	audpcm_in_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 = NULL;
+	uint16_t msg[3];
+
+	if (data)
+		audio = data;
+	else {
+		MM_ERR("invalid data for event %x\n", id);
+		return;
+	}
+
+	getevent(msg, sizeof(msg));
+
+	switch (id) {
+	case AUDREC_MSG_CMD_CFG_DONE_MSG: {
+		if (msg[0] & AUDREC_MSG_CFG_DONE_ENC_ENA) {
+			audio->audrec_obj_idx = msg[1];
+			MM_INFO("CFG ENABLED\n");
+			audpcm_in_encmem_config(audio);
+		} else {
+			MM_INFO("CFG SLEEP\n");
+			audio->running = 0;
+			audio->tx_agc_enable = 0;
+			audio->ns_enable = 0;
+			audio->iir_enable = 0;
+		}
+		break;
+	}
+	case AUDREC_MSG_CMD_AREC_MEM_CFG_DONE_MSG: {
+		MM_DBG("AREC_MEM_CFG_DONE_MSG\n");
+		audpcm_in_encparam_config(audio);
+		break;
+	}
+	case AUDREC_MSG_CMD_AREC_PARAM_CFG_DONE_MSG: {
+		MM_INFO("PARAM CFG DONE\n");
+		audio->running = 1;
+		audio_dsp_set_tx_agc(audio);
+		audio_dsp_set_ns(audio);
+		audio_dsp_set_iir(audio);
+		break;
+	}
+	case AUDREC_MSG_NO_EXT_PKT_AVAILABLE_MSG: {
+		MM_DBG("ERROR %x\n", msg[0]);
+		break;
+	}
+	case AUDREC_MSG_PACKET_READY_MSG: {
+		struct audrec_msg_packet_ready_msg pkt_ready_msg;
+
+		getevent(&pkt_ready_msg, AUDREC_MSG_PACKET_READY_MSG_LEN);
+		MM_DBG("UP_PACKET_READY_MSG: write cnt msw  %d \
+		write cnt lsw %d read cnt msw %d  read cnt lsw %d \n",\
+		pkt_ready_msg.pkt_counter_msw, \
+		pkt_ready_msg.pkt_counter_lsw, \
+		pkt_ready_msg.pkt_read_cnt_msw, \
+		pkt_ready_msg.pkt_read_cnt_lsw);
+
+		audpcm_in_get_dsp_frames(audio);
+		break;
+	}
+	case ADSP_MESSAGE_ID: {
+		MM_DBG("Received ADSP event: module \
+				enable/disable(audrectask)\n");
+		break;
+	}
+	default:
+		MM_ERR("unknown event %d\n", id);
+	}
+}
+
+static struct msm_adsp_ops audpre_adsp_ops = {
+	.event = audpre_dsp_event,
+};
+
+static 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, ((audio->queue_ids & 0xFFFF0000) >> 16),\
+			cmd, len)
+#define audio_send_queue_rec(audio, cmd, len) \
+	msm_adsp_write(audio->audrec, (audio->queue_ids & 0x0000FFFF),\
+			cmd, len)
+
+static int audio_dsp_set_tx_agc(struct audio_in *audio)
+{
+	audpreproc_cmd_cfg_agc_params cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	audio->tx_agc_cfg.cmd_id = AUDPREPROC_CMD_CFG_AGC_PARAMS;
+	if (audio->tx_agc_enable) {
+		/* cmd.tx_agc_param_mask = 0xFE00 from sample code */
+		audio->tx_agc_cfg.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);
+		audio->tx_agc_cfg.tx_agc_enable_flag =
+			AUDPREPROC_CMD_TX_AGC_ENA_FLAG_ENA;
+		/* cmd.param_mask = 0xFFF0 from sample code */
+		audio->tx_agc_cfg.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);
+	} else {
+		audio->tx_agc_cfg.tx_agc_param_mask =
+			(1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_TX_AGC_ENA_FLAG);
+		audio->tx_agc_cfg.tx_agc_enable_flag =
+			AUDPREPROC_CMD_TX_AGC_ENA_FLAG_DIS;
+	}
+	cmd = audio->tx_agc_cfg;
+
+	return audio_send_queue_pre(audio, &cmd, sizeof(cmd));
+}
+
+static int audio_enable_tx_agc(struct audio_in *audio, int enable)
+{
+	if (audio->tx_agc_enable != enable) {
+		audio->tx_agc_enable = enable;
+		if (audio->running)
+			audio_dsp_set_tx_agc(audio);
+	}
+	return 0;
+}
+
+static int audio_dsp_set_ns(struct audio_in *audio)
+{
+	audpreproc_cmd_cfg_ns_params cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	audio->ns_cfg.cmd_id = AUDPREPROC_CMD_CFG_NS_PARAMS;
+
+	if (audio->ns_enable) {
+		/* cmd.ec_mode_new is fixed as 0x0064 when enable
+		 * from sample code */
+		audio->ns_cfg.ec_mode_new =
+			AUDPREPROC_CMD_EC_MODE_NEW_NS_ENA |
+			AUDPREPROC_CMD_EC_MODE_NEW_HB_ENA |
+			AUDPREPROC_CMD_EC_MODE_NEW_VA_ENA;
+	} else {
+		audio->ns_cfg.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;
+	}
+	cmd = audio->ns_cfg;
+
+	return audio_send_queue_pre(audio, &cmd, sizeof(cmd));
+}
+
+static int 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);
+	}
+	return 0;
+}
+
+static int audio_dsp_set_iir(struct audio_in *audio)
+{
+	audpreproc_cmd_cfg_iir_tuning_filter_params cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	audio->iir_cfg.cmd_id = AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS;
+
+	if (audio->iir_enable)
+		/* cmd.active_flag is 0xFFFF from sample code but 0x0001 here */
+		audio->iir_cfg.active_flag = AUDPREPROC_CMD_IIR_ACTIVE_FLAG_ENA;
+	else
+		audio->iir_cfg.active_flag = AUDPREPROC_CMD_IIR_ACTIVE_FLAG_DIS;
+
+	cmd = audio->iir_cfg;
+
+	return audio_send_queue_pre(audio, &cmd, sizeof(cmd));
+}
+
+static int audio_enable_iir(struct audio_in *audio, int enable)
+{
+	if (audio->iir_enable != enable) {
+		audio->iir_enable = enable;
+		if (audio->running)
+			audio_dsp_set_iir(audio);
+	}
+	return 0;
+}
+
+static int audpcm_in_dsp_enable(struct audio_in *audio, int enable)
+{
+	struct audrec_cmd_enc_cfg cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDREC_CMD_ENC_CFG;
+
+	cmd.audrec_enc_type = (audio->enc_type & 0xFF) |
+	(enable ? AUDREC_CMD_ENC_ENA : AUDREC_CMD_ENC_DIS);
+	/* Don't care */
+	cmd.audrec_obj_idx = audio->audrec_obj_idx;
+
+	return audio_send_queue_rec(audio, &cmd, sizeof(cmd));
+}
+
+static int audpcm_in_encmem_config(struct audio_in *audio)
+{
+	struct audrec_cmd_arecmem_cfg cmd;
+	uint16_t cnt = 0;
+	uint16_t *data = (void *) audio->data;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	cmd.cmd_id = AUDREC_CMD_ARECMEM_CFG;
+	cmd.audrec_obj_idx = audio->audrec_obj_idx;
+	/* Rate at which packet complete message comes */
+	cmd.audrec_up_pkt_intm_cnt = 1;
+	cmd.audrec_extpkt_buffer_msw = audio->phys >> 16;
+	cmd.audrec_extpkt_buffer_lsw = audio->phys;
+	/* Max Buffer no available for frames */
+	cmd.audrec_extpkt_buffer_num = FRAME_NUM;
+
+	/* prepare buffer pointers:
+	 * Mono: 1024 samples + 4 halfword header
+	 * Stereo: 2048 samples + 4 halfword header
+	 */
+	for (cnt = 0; cnt < FRAME_NUM; cnt++) {
+		audio->in[cnt].data = data + 4;
+			data += (4 + (audio->channel_mode ? 2048 : 1024));
+	}
+
+	return audio_send_queue_rec(audio, &cmd, sizeof(cmd));
+}
+
+static int audpcm_in_encparam_config(struct audio_in *audio)
+{
+	struct audrec_cmd_arecparam_wav_cfg cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.common.cmd_id = AUDREC_CMD_ARECPARAM_CFG;
+	cmd.common.audrec_obj_idx = audio->audrec_obj_idx;
+	cmd.samp_rate_idx = audio->samp_rate_index;
+	cmd.stereo_mode = audio->channel_mode; /* 0 for mono, 1 for stereo */
+
+	return audio_send_queue_rec(audio, &cmd, sizeof(cmd));
+}
+
+static int audpcm_in_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;
+	cmd.type = audio->audrec_obj_idx;
+	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 audpcm_in_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 = FRAME_NUM-1; i <= 0; i--) {
+		audio->in[i].size = 0;
+		audio->in[i].read = 0;
+	}
+}
+
+static long audpcm_in_ioctl(struct file *file,
+				unsigned int cmd, unsigned long arg)
+{
+	struct audio_in *audio = file->private_data;
+	int rc = 0;
+
+	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 = audpcm_in_enable(audio);
+		audio->stopped = 0;
+		break;
+	}
+	case AUDIO_STOP:
+		rc = audpcm_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);
+			audpcm_in_flush(audio);
+			mutex_unlock(&audio->read_lock);
+		}
+		break;
+	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;
+		}
+
+		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;
+		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;
+		cfg.type = 0;
+		cfg.unused[0] = 0;
+		cfg.unused[1] = 0;
+		cfg.unused[2] = 0;
+		if (copy_to_user((void *) arg, &cfg, sizeof(cfg)))
+			rc = -EFAULT;
+		break;
+	}
+	default:
+		rc = -EINVAL;
+	}
+	mutex_unlock(&audio->lock);
+	return rc;
+}
+
+static ssize_t audpcm_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 && !audio->in_count) {
+			rc = 0;/* End of File */
+			break;
+		}
+
+		index = audio->in_tail;
+		data = (uint8_t *) audio->in[index].data;
+		size = audio->in[index].size;
+		if (count >= size) {
+			/* order the reads on the buffer */
+			dma_coherent_post_ops();
+			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;
+		} else {
+			MM_ERR("short read\n");
+			break;
+		}
+	}
+	mutex_unlock(&audio->read_lock);
+
+	if (buf > start)
+		return buf - start;
+
+	return rc;
+}
+
+static ssize_t audpcm_in_write(struct file *file,
+				const char __user *buf,
+				size_t count, loff_t *pos)
+{
+	return -EINVAL;
+}
+
+static int audpcm_in_release(struct inode *inode, struct file *file)
+{
+	struct audio_in *audio = file->private_data;
+
+	mutex_lock(&audio->lock);
+	audpcm_in_disable(audio);
+	audpcm_in_flush(audio);
+	audpreproc_aenc_free(audio->enc_id);
+	msm_adsp_put(audio->audrec);
+	msm_adsp_put(audio->audpre);
+	audio->audrec = NULL;
+	audio->audpre = NULL;
+	audio->opened = 0;
+	if (audio->data) {
+		free_contiguous_memory((void *)audio->data);
+		audio->data = NULL;
+	}
+	mutex_unlock(&audio->lock);
+	return 0;
+}
+
+static struct audio_in the_audio_in;
+
+static int audpcm_in_open(struct inode *inode, struct file *file)
+{
+	struct audio_in *audio = &the_audio_in;
+	int rc;
+
+	int encid;
+	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->mode = MSM_AUD_ENC_MODE_TUNNEL;
+	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->enc_type = AUDREC_CMD_TYPE_0_INDEX_WAV | audio->mode;
+
+	rc = audmgr_open(&audio->audmgr);
+	if (rc)
+		goto done;
+	encid = audpreproc_aenc_alloc(audio->enc_type, &audio->module_name,
+			&audio->queue_ids);
+	if (encid < 0) {
+		MM_ERR("No free encoder available\n");
+		rc = -ENODEV;
+		goto done;
+	}
+	audio->enc_id = encid;
+
+	rc = msm_adsp_get(audio->module_name, &audio->audrec,
+			   &audrec_adsp_ops, audio);
+	if (rc) {
+		audpreproc_aenc_free(audio->enc_id);
+		goto done;
+	}
+
+	rc = msm_adsp_get("AUDPREPROCTASK", &audio->audpre,
+				&audpre_adsp_ops, audio);
+	if (rc) {
+		msm_adsp_put(audio->audrec);
+		audpreproc_aenc_free(audio->enc_id);
+		goto done;
+	}
+
+	audio->dsp_cnt = 0;
+	audio->stopped = 0;
+
+	audpcm_in_flush(audio);
+
+	audio->data = allocate_contiguous_memory(DMASZ, MEMTYPE_EBI1,
+				SZ_4K, 0);
+	if (!audio->data) {
+		MM_ERR("could not allocate read buffers\n");
+		rc = -ENOMEM;
+		goto evt_error;
+	} else {
+		audio->phys = memory_pool_node_paddr(audio->data);
+		if (!audio->phys) {
+			MM_ERR("could not get physical address\n");
+			rc = -ENOMEM;
+			free_contiguous_memory(audio->data);
+			goto evt_error;
+		}
+		MM_DBG("read buf: phy addr 0x%08x kernel addr 0x%08x\n",
+				audio->phys, (int)audio->data);
+	}
+
+	file->private_data = audio;
+	audio->opened = 1;
+	rc = 0;
+done:
+	mutex_unlock(&audio->lock);
+	return rc;
+evt_error:
+	msm_adsp_put(audio->audrec);
+	msm_adsp_put(audio->audpre);
+	audpreproc_aenc_free(audio->enc_id);
+	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;
+
+	mutex_lock(&audio->lock);
+	switch (cmd) {
+	case AUDIO_ENABLE_AUDPRE:
+		if (copy_from_user(&enable_mask, (void *) arg,
+						sizeof(enable_mask))) {
+			rc = -EFAULT;
+			break;
+		}
+
+		enable = (enable_mask & AGC_ENABLE) ? 1 : 0;
+		audio_enable_tx_agc(audio, enable);
+		enable = (enable_mask & NS_ENABLE) ? 1 : 0;
+		audio_enable_ns(audio, enable);
+		enable = (enable_mask & TX_IIR_ENABLE) ? 1 : 0;
+		audio_enable_iir(audio, enable);
+		break;
+
+	case AUDIO_SET_AGC:
+		if (copy_from_user(&audio->tx_agc_cfg, (void *) arg,
+						sizeof(audio->tx_agc_cfg)))
+			rc = -EFAULT;
+		break;
+
+	case AUDIO_SET_NS:
+		if (copy_from_user(&audio->ns_cfg, (void *) arg,
+						sizeof(audio->ns_cfg)))
+			rc = -EFAULT;
+		break;
+
+	case AUDIO_SET_TX_IIR:
+		if (copy_from_user(&audio->iir_cfg, (void *) arg,
+						sizeof(audio->iir_cfg)))
+			rc = -EFAULT;
+		break;
+
+	default:
+		rc = -EINVAL;
+	}
+
+	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 const struct file_operations audio_fops = {
+	.owner		= THIS_MODULE,
+	.open		= audpcm_in_open,
+	.release	= audpcm_in_release,
+	.read		= audpcm_in_read,
+	.write		= audpcm_in_write,
+	.unlocked_ioctl	= audpcm_in_ioctl,
+};
+
+static struct miscdevice audpcm_in_misc = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "msm_pcm_in",
+	.fops	= &audio_fops,
+};
+
+static const struct file_operations audpre_fops = {
+	.owner		= THIS_MODULE,
+	.open		= audpre_open,
+	.unlocked_ioctl	= audpre_ioctl,
+};
+
+static struct miscdevice audpre_misc = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "msm_preproc_ctl",
+	.fops	= &audpre_fops,
+};
+
+static int __init audpcm_in_init(void)
+{
+
+	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);
+	return misc_register(&audpcm_in_misc) || misc_register(&audpre_misc);
+}
+device_initcall(audpcm_in_init);
diff --git a/arch/arm/mach-msm/qdsp5/audio_qcelp.c b/arch/arm/mach-msm/qdsp5/audio_qcelp.c
new file mode 100644
index 0000000..5b0a9cd
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audio_qcelp.c
@@ -0,0 +1,1602 @@
+/* arch/arm/mach-msm/qdsp5/audio_qcelp.c
+ *
+ * qcelp 13k audio decoder device
+ *
+ * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * 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 <linux/debugfs.h>
+#include <linux/earlysuspend.h>
+#include <linux/list.h>
+#include <linux/android_pmem.h>
+#include <linux/slab.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 <mach/qdsp5/qdsp5rmtcmdi.h>
+#include <mach/debug_mm.h>
+
+#include "audmgr.h"
+
+#define BUFSZ 1094 /* QCELP 13K Hold 600ms packet data = 36 * 30 and
+		      14 bytes of meta in */
+#define BUF_COUNT 2
+#define DMASZ (BUFSZ * BUF_COUNT)
+
+#define PCM_BUFSZ_MIN 1624 /* 100ms worth of data and
+			      24 bytes of meta out  */
+#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
+
+#define AUDQCELP_METAFIELD_MASK 0xFFFF0000
+#define AUDQCELP_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */
+#define AUDQCELP_EOS_FLG_MASK 0x01
+#define AUDQCELP_EOS_NONE 0x0 /* No EOS detected */
+#define AUDQCELP_EOS_SET 0x1 /* EOS set in meta field */
+
+#define AUDQCELP_EVENT_NUM 10 /* Default number of pre-allocated event pkts */
+
+struct buffer {
+	void *data;
+	unsigned size;
+	unsigned used;		/* Input usage actual DSP produced PCM size  */
+	unsigned addr;
+	unsigned short mfield_sz; /*only useful for data has meta field */
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+struct audqcelp_suspend_ctl {
+	struct early_suspend node;
+	struct audio *audio;
+};
+#endif
+
+struct audqcelp_event{
+	struct list_head list;
+	int event_type;
+	union msm_audio_event_payload payload;
+};
+
+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 */
+	int32_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;
+	int32_t phys; /* physical address of write buffer */
+
+	int mfield; /* meta field embedded in data */
+	int rflush; /* Read  flush */
+	int wflush; /* Write flush */
+	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;
+	int teos; /* valid only if tunnel mode & no data left for decoder */
+	enum msm_aud_decoder_state dec_state;	/* Represents decoder state */
+	int rmt_resource_released;
+
+	const char *module_name;
+	unsigned queue_id;
+	uint16_t dec_id;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	struct audqcelp_suspend_ctl suspend_ctl;
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *dentry;
+#endif
+
+	wait_queue_head_t wait;
+	struct list_head free_event_queue;
+	struct list_head event_queue;
+	wait_queue_head_t event_wait;
+	spinlock_t event_queue_lock;
+	struct mutex get_event_lock;
+	int event_abort;
+
+	int eq_enable;
+	int eq_needs_commit;
+	audpp_cmd_cfg_object_params_eqalizer eq;
+	audpp_cmd_cfg_object_params_volume vol_pan;
+};
+
+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);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void audqcelp_post_event(struct audio *audio, int type,
+		union msm_audio_event_payload payload);
+#endif
+
+static int rmt_put_resource(struct audio *audio)
+{
+	struct aud_codec_config_cmd cmd;
+	unsigned short client_idx;
+
+	cmd.cmd_id = RM_CMD_AUD_CODEC_CFG;
+	cmd.client_id = RM_AUD_CLIENT_ID;
+	cmd.task_id = audio->dec_id;
+	cmd.enable = RMT_DISABLE;
+	cmd.dec_type = AUDDEC_DEC_QCELP;
+	client_idx = ((cmd.client_id << 8) | cmd.task_id);
+
+	return put_adsp_resource(client_idx, &cmd, sizeof(cmd));
+}
+
+static int rmt_get_resource(struct audio *audio)
+{
+	struct aud_codec_config_cmd cmd;
+	unsigned short client_idx;
+
+	cmd.cmd_id = RM_CMD_AUD_CODEC_CFG;
+	cmd.client_id = RM_AUD_CLIENT_ID;
+	cmd.task_id = audio->dec_id;
+	cmd.enable = RMT_ENABLE;
+	cmd.dec_type = AUDDEC_DEC_QCELP;
+	client_idx = ((cmd.client_id << 8) | cmd.task_id);
+
+	return get_adsp_resource(client_idx, &cmd, sizeof(cmd));
+}
+
+/* must be called with audio->lock held */
+static int audqcelp_enable(struct audio *audio)
+{
+	struct audmgr_config cfg;
+	int rc;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	if (audio->enabled)
+		return 0;
+
+	if (audio->rmt_resource_released == 1) {
+		audio->rmt_resource_released = 0;
+		rc = rmt_get_resource(audio);
+		if (rc) {
+			MM_ERR("ADSP resources are not available for QCELP \
+				session 0x%08x on decoder: %d\n Ignoring \
+				error and going ahead with the playback\n",
+				(int)audio, audio->dec_id);
+		}
+	}
+
+	audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+	audio->out_tail = 0;
+	audio->out_needed = 0;
+
+	if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) {
+		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)) {
+		MM_ERR("msm_adsp_enable(audplay) failed\n");
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			audmgr_disable(&audio->audmgr);
+		return -ENODEV;
+	}
+
+	if (audpp_enable(audio->dec_id, audqcelp_dsp_event, audio)) {
+		MM_ERR("audpp_enable() failed\n");
+		msm_adsp_disable(audio->audplay);
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			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)
+{
+	int rc = 0;
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	if (audio->enabled) {
+		audio->enabled = 0;
+		audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+		auddec_dsp_config(audio, 0);
+		rc = wait_event_interruptible_timeout(audio->wait,
+				audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+				msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+		if (rc == 0)
+			rc = -ETIMEDOUT;
+		else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE)
+			rc = -EFAULT;
+		else
+			rc = 0;
+		wake_up(&audio->write_wait);
+		wake_up(&audio->read_wait);
+		msm_adsp_disable(audio->audplay);
+		audpp_disable(audio->dec_id, audio);
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			audmgr_disable(&audio->audmgr);
+		audio->out_needed = 0;
+		rmt_put_resource(audio);
+		audio->rmt_resource_released = 1;
+	}
+	return rc;
+}
+
+/* ------------------- dsp --------------------- */
+static void audqcelp_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]) {
+			MM_DBG("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 {
+			MM_ERR("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 {
+		MM_DBG("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));
+
+	MM_DBG("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;
+
+	case ADSP_MESSAGE_ID:
+		MM_DBG("Received ADSP event: module enable(audplaytask)\n");
+		break;
+
+	default:
+		MM_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: {
+				uint16_t reason = msg[2];
+				MM_DBG("decoder status:sleep reason = \
+						0x%04x\n", reason);
+				if ((reason == AUDPP_MSG_REASON_MEM)
+					|| (reason ==
+					AUDPP_MSG_REASON_NODECODER)) {
+					audio->dec_state =
+					MSM_AUD_DECODER_STATE_FAILURE;
+					wake_up(&audio->wait);
+				} else if (reason == AUDPP_MSG_REASON_NONE) {
+					/* decoder is in disable state */
+					audio->dec_state =
+						MSM_AUD_DECODER_STATE_CLOSE;
+					wake_up(&audio->wait);
+				}
+				break;
+			}
+			case AUDPP_DEC_STATUS_INIT:
+				MM_DBG("decoder status: init \n");
+				if (audio->pcm_feedback)
+					audpp_cmd_cfg_routing_mode(audio);
+				else
+					audpp_cmd_cfg_adec_params(audio);
+				break;
+
+			case AUDPP_DEC_STATUS_CFG:
+				MM_DBG("decoder status: cfg \n");
+				break;
+			case AUDPP_DEC_STATUS_PLAY:
+				MM_DBG("decoder status: play \n");
+				if (audio->pcm_feedback) {
+					audqcelp_config_hostpcm(audio);
+					audqcelp_buffer_refresh(audio);
+				}
+				audio->dec_state =
+					MSM_AUD_DECODER_STATE_SUCCESS;
+				wake_up(&audio->wait);
+				break;
+			default:
+				MM_ERR("unknown decoder status\n");
+			}
+			break;
+		}
+	case AUDPP_MSG_CFG_MSG:
+		if (msg[0] == AUDPP_MSG_ENA_ENA) {
+			MM_DBG("CFG_MSG ENABLE\n");
+			auddec_dsp_config(audio, 1);
+			audio->out_needed = 0;
+			audio->running = 1;
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan);
+			audpp_dsp_set_eq(audio->dec_id,	audio->eq_enable,
+								&audio->eq);
+			audpp_avsync(audio->dec_id, 22050);
+		} else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+			MM_DBG("CFG_MSG DISABLE\n");
+			audpp_avsync(audio->dec_id, 0);
+			audio->running = 0;
+		} else {
+			MM_DBG("CFG_MSG %d?\n", msg[0]);
+		}
+		break;
+	case AUDPP_MSG_ROUTING_ACK:
+		MM_DBG("ROUTING_ACK mode=%d\n", msg[1]);
+		audpp_cmd_cfg_adec_params(audio);
+		break;
+	case AUDPP_MSG_FLUSH_ACK:
+		MM_DBG("FLUSH_ACK\n");
+		audio->wflush = 0;
+		audio->rflush = 0;
+		wake_up(&audio->write_wait);
+		if (audio->pcm_feedback)
+			audqcelp_buffer_refresh(audio);
+		break;
+	case AUDPP_MSG_PCMDMAMISSED:
+		MM_DBG("PCMDMAMISSED\n");
+		audio->teos = 1;
+		wake_up(&audio->write_wait);
+		break;
+	default:
+		MM_ERR("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, audio->queue_id, \
+		cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+	u16 cfg_dec_cmd[AUDPP_CMD_CFG_DEC_TYPE_LEN / sizeof(unsigned short)];
+
+	memset(cfg_dec_cmd, 0, sizeof(cfg_dec_cmd));
+
+	cfg_dec_cmd[0] = AUDPP_CMD_CFG_DEC_TYPE;
+	if (enable)
+		cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC |
+			AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_QCELP;
+	else
+		cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC |
+			AUDPP_CMD_DIS_DEC_V;
+
+	return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_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;
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	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)
+{
+	struct audplay_cmd_bitstream_data_avail_nt2 cmd;
+
+	cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2;
+	if (audio->mfield)
+		cmd.decoder_id = AUDQCELP_METAFIELD_MASK |
+			(audio->out[idx].mfield_sz >> 1);
+	else
+		cmd.decoder_id = audio->dec_id;
+	cmd.buf_ptr = audio->out[idx].addr;
+	cmd.buf_size = len / 2;
+	cmd.partition_number = 0;
+	/* complete writes to the input buffer */
+	wmb();
+	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;
+	MM_DBG("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;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	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 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 && !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) {
+			MM_DBG("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);
+			MM_DBG("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->out_needed = 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->buf_refresh = 0;
+	audio->read_next = 0;
+	audio->fill_next = 0;
+}
+
+static void audqcelp_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);
+	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);
+}
+
+static int audqcelp_events_pending(struct audio *audio)
+{
+	unsigned long flags;
+	int empty;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+	empty = !list_empty(&audio->event_queue);
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+	return empty || audio->event_abort;
+}
+
+static void audqcelp_reset_event_queue(struct audio *audio)
+{
+	unsigned long flags;
+	struct audqcelp_event *drv_evt;
+	struct list_head *ptr, *next;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+	list_for_each_safe(ptr, next, &audio->event_queue) {
+		drv_evt = list_first_entry(&audio->event_queue,
+				struct audqcelp_event, list);
+		list_del(&drv_evt->list);
+		kfree(drv_evt);
+	}
+	list_for_each_safe(ptr, next, &audio->free_event_queue) {
+		drv_evt = list_first_entry(&audio->free_event_queue,
+				struct audqcelp_event, list);
+		list_del(&drv_evt->list);
+		kfree(drv_evt);
+	}
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+	return;
+}
+
+
+static long audqcelp_process_event_req(struct audio *audio, void __user *arg)
+{
+	long rc;
+	struct msm_audio_event usr_evt;
+	struct audqcelp_event *drv_evt = NULL;
+	int timeout;
+	unsigned long flags;
+
+	if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event)))
+		return -EFAULT;
+
+	timeout = (int) usr_evt.timeout_ms;
+
+	if (timeout > 0) {
+		rc = wait_event_interruptible_timeout(
+			audio->event_wait, audqcelp_events_pending(audio),
+			msecs_to_jiffies(timeout));
+		if (rc == 0)
+			return -ETIMEDOUT;
+	} else {
+		rc = wait_event_interruptible(
+			audio->event_wait, audqcelp_events_pending(audio));
+	}
+
+	if (rc < 0)
+		return rc;
+
+	if (audio->event_abort) {
+		audio->event_abort = 0;
+		return -ENODEV;
+	}
+
+	rc = 0;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+	if (!list_empty(&audio->event_queue)) {
+		drv_evt = list_first_entry(&audio->event_queue,
+				struct audqcelp_event, list);
+		list_del(&drv_evt->list);
+	}
+
+	if (drv_evt) {
+		usr_evt.event_type = drv_evt->event_type;
+		usr_evt.event_payload = drv_evt->payload;
+		list_add_tail(&drv_evt->list, &audio->free_event_queue);
+	} else
+		rc = -1;
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+	if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt)))
+		rc = -EFAULT;
+
+	return rc;
+}
+
+static int audio_enable_eq(struct audio *audio, int enable)
+{
+	if (audio->eq_enable == enable && !audio->eq_needs_commit)
+		return 0;
+
+	audio->eq_enable = enable;
+
+	if (audio->running) {
+		audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq);
+		audio->eq_needs_commit = 0;
+	}
+	return 0;
+}
+
+static long audqcelp_ioctl(struct file *file, unsigned int cmd,
+		unsigned long arg)
+{
+	struct audio *audio = file->private_data;
+	int rc = -EINVAL;
+	unsigned long flags = 0;
+	uint16_t enable_mask;
+	int enable;
+	int prev_state;
+
+	MM_DBG("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;
+	}
+
+	switch (cmd) {
+	case AUDIO_ENABLE_AUDPP:
+		if (copy_from_user(&enable_mask, (void *) arg,
+						sizeof(enable_mask))) {
+			rc = -EFAULT;
+			break;
+		}
+
+		spin_lock_irqsave(&audio->dsp_lock, flags);
+		enable = (enable_mask & EQ_ENABLE) ? 1 : 0;
+		audio_enable_eq(audio, enable);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		rc = 0;
+		break;
+	case AUDIO_SET_VOLUME:
+		spin_lock_irqsave(&audio->dsp_lock, flags);
+		audio->vol_pan.volume = arg;
+		if (audio->running)
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		rc = 0;
+		break;
+
+	case AUDIO_SET_PAN:
+		spin_lock_irqsave(&audio->dsp_lock, flags);
+		audio->vol_pan.pan = arg;
+		if (audio->running)
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		rc = 0;
+		break;
+
+	case AUDIO_SET_EQ:
+		prev_state = audio->eq_enable;
+		audio->eq_enable = 0;
+		if (copy_from_user(&audio->eq.num_bands, (void *) arg,
+				sizeof(audio->eq) -
+				(AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) {
+			rc = -EFAULT;
+			break;
+		}
+		audio->eq_enable = prev_state;
+		audio->eq_needs_commit = 1;
+		rc = 0;
+		break;
+	}
+
+	if (-EINVAL != rc)
+		return rc;
+
+	if (cmd == AUDIO_GET_EVENT) {
+		MM_DBG("AUDIO_GET_EVENT\n");
+		if (mutex_trylock(&audio->get_event_lock)) {
+			rc = audqcelp_process_event_req(audio,
+					(void __user *) arg);
+			mutex_unlock(&audio->get_event_lock);
+		} else
+			rc = -EBUSY;
+		return rc;
+	}
+
+	if (cmd == AUDIO_ABORT_GET_EVENT) {
+		audio->event_abort = 1;
+		wake_up(&audio->event_wait);
+		return 0;
+	}
+
+	mutex_lock(&audio->lock);
+	switch (cmd) {
+	case AUDIO_START:
+		MM_DBG("AUDIO_START\n");
+		rc = audqcelp_enable(audio);
+		if (!rc) {
+			rc = wait_event_interruptible_timeout(audio->wait,
+				audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+				msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+			MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc);
+
+			if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS)
+				rc = -ENODEV;
+			else
+				rc = 0;
+		}
+		break;
+	case AUDIO_STOP:
+		MM_DBG("AUDIO_STOP\n");
+		rc = audqcelp_disable(audio);
+		audio->stopped = 1;
+		audqcelp_ioport_reset(audio);
+		audio->stopped = 0;
+		break;
+	case AUDIO_FLUSH:
+		MM_DBG("AUDIO_FLUSH\n");
+		audio->rflush = 1;
+		audio->wflush = 1;
+		audqcelp_ioport_reset(audio);
+		if (audio->running) {
+			audpp_flush(audio->dec_id);
+			rc = wait_event_interruptible(audio->write_wait,
+				!audio->wflush);
+			if (rc < 0) {
+				MM_ERR("AUDIO_FLUSH interrupted\n");
+				rc = -EINTR;
+			}
+		} 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;
+			}
+			audio->mfield = config.meta_field;
+			MM_DBG("AUDIO_SET_CONFIG applicable \
+				for metafield configuration\n");
+			rc = 0;
+			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.meta_field = 0;
+			config.unused[0] = 0;
+			config.unused[1] = 0;
+			config.unused[2] = 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 = audio->pcm_feedback;
+			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.pcm_feedback != audio->pcm_feedback) {
+				MM_ERR("Not sufficient permission to"
+					 "change the playback mode\n");
+				rc = -EACCES;
+				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)) {
+				MM_DBG("allocate PCM buf %d\n",
+				config.buffer_count * config.buffer_size);
+				audio->read_phys = pmem_kalloc(
+							config.buffer_size *
+							config.buffer_count,
+							PMEM_MEMTYPE_EBI1|
+							PMEM_ALIGNMENT_4K);
+				if (IS_ERR((void *)audio->read_phys)) {
+					rc = -ENOMEM;
+					break;
+				}
+				audio->read_data = ioremap(audio->read_phys,
+							config.buffer_size *
+							config.buffer_count);
+				if (!audio->read_data) {
+					MM_ERR("no mem for read buf\n");
+					rc = -ENOMEM;
+					pmem_kfree(audio->read_phys);
+				} else {
+					uint8_t index;
+					uint32_t offset = 0;
+
+					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;
+					}
+					MM_DBG("read buf: phy addr 0x%08x \
+						kernel addr 0x%08x\n",
+						audio->read_phys,
+						(int)audio->read_data);
+					rc = 0;
+				}
+			} else {
+				rc = 0;
+			}
+			break;
+		}
+	case AUDIO_PAUSE:
+		MM_DBG("AUDIO_PAUSE %ld\n", arg);
+		rc = audpp_pause(audio->dec_id, (int) arg);
+		break;
+	default:
+		rc = -EINVAL;
+	}
+	mutex_unlock(&audio->lock);
+	return rc;
+}
+
+/* Only useful in tunnel-mode */
+static int audqcelp_fsync(struct file *file, int datasync)
+{
+	struct audio *audio = file->private_data;
+	int rc = 0;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	if (!audio->running || audio->pcm_feedback) {
+		rc = -EINVAL;
+		goto done_nolock;
+	}
+
+	mutex_lock(&audio->write_lock);
+
+	rc = wait_event_interruptible(audio->write_wait,
+		(!audio->out[0].used &&
+		!audio->out[1].used &&
+		audio->out_needed) || audio->wflush);
+
+	if (rc < 0)
+		goto done;
+	else if (audio->wflush) {
+		rc = -EBUSY;
+		goto done;
+	}
+
+	/* pcm dmamiss message is sent continously
+	 * when decoder is starved so no race
+	 * condition concern
+	 */
+	audio->teos = 0;
+
+	rc = wait_event_interruptible(audio->write_wait,
+		audio->teos || audio->wflush);
+
+	if (audio->wflush)
+		rc = -EBUSY;
+
+done:
+	mutex_unlock(&audio->write_lock);
+done_nolock:
+	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);
+	MM_DBG("%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 */
+			MM_DBG("read stop - partial frame\n");
+			break;
+		} else {
+			MM_DBG("read from in[%d]\n", audio->read_next);
+			/* order reads from the output buffer */
+			rmb();
+			if (copy_to_user(buf,
+				audio->in[audio->read_next].data,
+				audio->in[audio->read_next].used)) {
+				MM_ERR("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;
+			break;
+				/* Force to exit while loop
+				 * to prevent output thread
+				 * sleep too long if data is
+				 * not ready at this moment.
+				 */
+		}
+	}
+
+	/* 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;
+		MM_DBG("kick start pcm feedback again\n");
+		audqcelp_buffer_refresh(audio);
+	}
+
+	mutex_unlock(&audio->read_lock);
+
+	if (buf > start)
+		rc = buf - start;
+
+	MM_DBG("read %d bytes\n", rc);
+	return rc;
+}
+
+static int audqcelp_process_eos(struct audio *audio,
+		const char __user *buf_start, unsigned short mfield_size)
+{
+	struct buffer *frame;
+	int rc = 0;
+
+	frame = audio->out + audio->out_head;
+
+	rc = wait_event_interruptible(audio->write_wait,
+		(audio->out_needed &&
+		audio->out[0].used == 0 &&
+		audio->out[1].used == 0)
+		|| (audio->stopped)
+		|| (audio->wflush));
+
+	if (rc < 0)
+		goto done;
+	if (audio->stopped || audio->wflush) {
+		rc = -EBUSY;
+		goto done;
+	}
+
+	if (copy_from_user(frame->data, buf_start, mfield_size)) {
+		rc = -EFAULT;
+		goto done;
+	}
+
+	frame->mfield_sz = mfield_size;
+	audio->out_head ^= 1;
+	frame->used = mfield_size;
+	audqcelp_send_data(audio, 0);
+
+done:
+	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;
+	char *cpy_ptr;
+	int rc = 0, eos_condition = AUDQCELP_EOS_NONE;
+	unsigned short mfield_size = 0;
+
+	MM_DBG("cnt=%d\n", count);
+
+	if (count & 1)
+		return -EINVAL;
+
+	mutex_lock(&audio->write_lock);
+	while (count > 0) {
+		frame = audio->out + audio->out_head;
+		cpy_ptr = frame->data;
+		rc = wait_event_interruptible(audio->write_wait,
+					      (frame->used == 0)
+						|| (audio->stopped)
+						|| (audio->wflush));
+		MM_DBG("buffer available\n");
+		if (rc < 0)
+			break;
+		if (audio->stopped || audio->wflush) {
+			rc = -EBUSY;
+			break;
+		}
+
+		if (audio->mfield) {
+			if (buf == start) {
+				/* Processing beginning of user buffer */
+				if (__get_user(mfield_size,
+					(unsigned short __user *) buf)) {
+					rc = -EFAULT;
+					break;
+				} else 	if (mfield_size > count) {
+					rc = -EINVAL;
+					break;
+				}
+				MM_DBG("mf offset_val %x\n", mfield_size);
+				if (copy_from_user(cpy_ptr, buf, mfield_size)) {
+					rc = -EFAULT;
+					break;
+				}
+				/* Check if EOS flag is set and buffer has
+				 * contains just meta field
+				 */
+				if (cpy_ptr[AUDQCELP_EOS_FLG_OFFSET] &
+						AUDQCELP_EOS_FLG_MASK) {
+					MM_DBG("EOS SET\n");
+					eos_condition = AUDQCELP_EOS_SET;
+					if (mfield_size == count) {
+						buf += mfield_size;
+						break;
+					} else
+					cpy_ptr[AUDQCELP_EOS_FLG_OFFSET] &=
+						~AUDQCELP_EOS_FLG_MASK;
+				}
+				cpy_ptr += mfield_size;
+				count -= mfield_size;
+				buf += mfield_size;
+			} else {
+				mfield_size = 0;
+				MM_DBG("continuous buffer\n");
+			}
+			frame->mfield_sz = mfield_size;
+		}
+
+		xfer = (count > (frame->size - mfield_size)) ?
+			(frame->size - mfield_size) : count;
+		if (copy_from_user(cpy_ptr, buf, xfer)) {
+			rc = -EFAULT;
+			break;
+		}
+
+		frame->used = xfer + mfield_size;
+		audio->out_head ^= 1;
+		count -= xfer;
+		buf += xfer;
+		audqcelp_send_data(audio, 0);
+	}
+	if (eos_condition == AUDQCELP_EOS_SET)
+		rc = audqcelp_process_eos(audio, start, mfield_size);
+	mutex_unlock(&audio->write_lock);
+	if (!rc) {
+		if (buf > start)
+			return buf - start;
+	}
+	return rc;
+}
+
+static int audqcelp_release(struct inode *inode, struct file *file)
+{
+	struct audio *audio = file->private_data;
+
+	MM_INFO("audio instance 0x%08x freeing\n", (int) audio);
+	mutex_lock(&audio->lock);
+	audqcelp_disable(audio);
+	if (audio->rmt_resource_released == 0)
+		rmt_put_resource(audio);
+	audqcelp_flush(audio);
+	audqcelp_flush_pcm_buf(audio);
+	msm_adsp_put(audio->audplay);
+	audpp_adec_free(audio->dec_id);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	unregister_early_suspend(&audio->suspend_ctl.node);
+#endif
+	audio->opened = 0;
+	audio->event_abort = 1;
+	wake_up(&audio->event_wait);
+	audqcelp_reset_event_queue(audio);
+	iounmap(audio->data);
+	pmem_kfree(audio->phys);
+	if (audio->read_data) {
+		iounmap(audio->read_data);
+		pmem_kfree(audio->read_phys);
+	}
+	mutex_unlock(&audio->lock);
+#ifdef CONFIG_DEBUG_FS
+	if (audio->dentry)
+		debugfs_remove(audio->dentry);
+#endif
+	kfree(audio);
+	return 0;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void audqcelp_post_event(struct audio *audio, int type,
+		union msm_audio_event_payload payload)
+{
+	struct audqcelp_event *e_node = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+
+	if (!list_empty(&audio->free_event_queue)) {
+		e_node = list_first_entry(&audio->free_event_queue,
+				struct audqcelp_event, list);
+		list_del(&e_node->list);
+	} else {
+		e_node = kmalloc(sizeof(struct audqcelp_event), GFP_ATOMIC);
+		if (!e_node) {
+			MM_ERR("No mem to post event %d\n", type);
+			spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+			return;
+		}
+	}
+
+	e_node->event_type = type;
+	e_node->payload = payload;
+
+	list_add_tail(&e_node->list, &audio->event_queue);
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+	wake_up(&audio->event_wait);
+}
+
+static void audqcelp_suspend(struct early_suspend *h)
+{
+	struct audqcelp_suspend_ctl *ctl =
+		container_of(h, struct audqcelp_suspend_ctl, node);
+	union msm_audio_event_payload payload;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	audqcelp_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload);
+}
+
+static void audqcelp_resume(struct early_suspend *h)
+{
+	struct audqcelp_suspend_ctl *ctl =
+		container_of(h, struct audqcelp_suspend_ctl, node);
+	union msm_audio_event_payload payload;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	audqcelp_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload);
+}
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t audqcelp_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t audqcelp_debug_read(struct file *file, char __user *buf,
+					size_t count, loff_t *ppos)
+{
+	const int debug_bufmax = 1024;
+	static char buffer[1024];
+	int n = 0, i;
+	struct audio *audio = file->private_data;
+
+	mutex_lock(&audio->lock);
+	n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"enabled %d\n", audio->enabled);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"stopped %d\n", audio->stopped);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"pcm_feedback %d\n", audio->pcm_feedback);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"out_buf_sz %d\n", audio->out[0].size);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"pcm_buf_count %d \n", audio->pcm_buf_count);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"pcm_buf_sz %d \n", audio->in[0].size);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"volume %x \n", audio->vol_pan.volume);
+	mutex_unlock(&audio->lock);
+	/* Following variables are only useful for debugging when
+	 * when playback halts unexpectedly. Thus, no mutual exclusion
+	 * enforced
+	 */
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"wflush %d\n", audio->wflush);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"rflush %d\n", audio->rflush);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"running %d \n", audio->running);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"dec state %d \n", audio->dec_state);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"out_needed %d \n", audio->out_needed);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"out_head %d \n", audio->out_head);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"out_tail %d \n", audio->out_tail);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"out[0].used %d \n", audio->out[0].used);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"out[1].used %d \n", audio->out[1].used);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"buffer_refresh %d \n", audio->buf_refresh);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"read_next %d \n", audio->read_next);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"fill_next %d \n", audio->fill_next);
+	for (i = 0; i < audio->pcm_buf_count; i++)
+		n += scnprintf(buffer + n, debug_bufmax - n,
+				"in[%d].size %d \n", i, audio->in[i].used);
+	buffer[n] = 0;
+	return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+
+static const struct file_operations audqcelp_debug_fops = {
+	.read = audqcelp_debug_read,
+	.open = audqcelp_debug_open,
+};
+#endif
+
+static int audqcelp_open(struct inode *inode, struct file *file)
+{
+	struct audio *audio = NULL;
+	int rc, dec_attrb, decid, i;
+	struct audqcelp_event *e_node = NULL;
+#ifdef CONFIG_DEBUG_FS
+	/* 4 bytes represents decoder number, 1 byte for terminate string */
+	char name[sizeof "msm_qcelp_" + 5];
+#endif
+
+	/* Create audio instance, set to zero */
+	audio = kzalloc(sizeof(struct audio), GFP_KERNEL);
+	if (!audio) {
+		MM_ERR("no memory to allocate audio instance\n");
+		rc = -ENOMEM;
+		goto done;
+	}
+	MM_INFO("audio instance 0x%08x created\n", (int)audio);
+
+	/* Allocate the decoder */
+	dec_attrb = AUDDEC_DEC_QCELP;
+	if ((file->f_mode & FMODE_WRITE) &&
+			(file->f_mode & FMODE_READ)) {
+		dec_attrb |= MSM_AUD_MODE_NONTUNNEL;
+		audio->pcm_feedback = NON_TUNNEL_MODE_PLAYBACK;
+	} else if ((file->f_mode & FMODE_WRITE) &&
+			!(file->f_mode & FMODE_READ)) {
+		dec_attrb |= MSM_AUD_MODE_TUNNEL;
+		audio->pcm_feedback = TUNNEL_MODE_PLAYBACK;
+	} else {
+		kfree(audio);
+		rc = -EACCES;
+		goto done;
+	}
+	decid = audpp_adec_alloc(dec_attrb, &audio->module_name,
+			&audio->queue_id);
+	if (decid < 0) {
+		MM_ERR("No free decoder available, freeing instance 0x%08x\n",
+				(int)audio);
+		rc = -ENODEV;
+		kfree(audio);
+		goto done;
+	}
+	audio->dec_id = decid & MSM_AUD_DECODER_MASK;
+
+	audio->phys = pmem_kalloc(DMASZ, PMEM_MEMTYPE_EBI1|PMEM_ALIGNMENT_4K);
+	if (IS_ERR((void *)audio->phys)) {
+		MM_ERR("could not allocate write buffers, freeing instance \
+				0x%08x\n", (int)audio);
+		rc = -ENOMEM;
+		audpp_adec_free(audio->dec_id);
+		kfree(audio);
+		goto done;
+	} else {
+		audio->data = ioremap(audio->phys, DMASZ);
+		if (!audio->data) {
+			MM_ERR("could not allocate write buffers, freeing \
+					instance 0x%08x\n", (int)audio);
+			rc = -ENOMEM;
+			pmem_kfree(audio->phys);
+			audpp_adec_free(audio->dec_id);
+			kfree(audio);
+			goto done;
+		}
+		MM_DBG("write buf: phy addr 0x%08x kernel addr 0x%08x\n",
+				audio->phys, (int)audio->data);
+	}
+
+	if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) {
+		rc = audmgr_open(&audio->audmgr);
+		if (rc) {
+			MM_ERR("audmgr open failed, freeing instance \
+					0x%08x\n", (int)audio);
+			goto err;
+		}
+	}
+
+	rc = msm_adsp_get(audio->module_name, &audio->audplay,
+		&audplay_adsp_ops_qcelp, audio);
+	if (rc) {
+		MM_ERR("failed to get %s module, freeing instance 0x%08x\n",
+				audio->module_name, (int)audio);
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			audmgr_close(&audio->audmgr);
+		goto err;
+	}
+
+	rc = rmt_get_resource(audio);
+	if (rc) {
+		MM_ERR("ADSP resources are not available for QCELP session \
+			 0x%08x on decoder: %d\n", (int)audio, audio->dec_id);
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			audmgr_close(&audio->audmgr);
+		msm_adsp_put(audio->audplay);
+		goto err;
+	}
+
+	/* Initialize all locks of audio instance */
+	mutex_init(&audio->lock);
+	mutex_init(&audio->write_lock);
+	mutex_init(&audio->read_lock);
+	mutex_init(&audio->get_event_lock);
+	spin_lock_init(&audio->dsp_lock);
+	init_waitqueue_head(&audio->write_wait);
+	init_waitqueue_head(&audio->read_wait);
+	INIT_LIST_HEAD(&audio->free_event_queue);
+	INIT_LIST_HEAD(&audio->event_queue);
+	init_waitqueue_head(&audio->wait);
+	init_waitqueue_head(&audio->event_wait);
+	spin_lock_init(&audio->event_queue_lock);
+
+	/* Initialize buffer */
+	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->vol_pan.volume = 0x2000;
+
+	audqcelp_flush(audio);
+
+	file->private_data = audio;
+	audio->opened = 1;
+#ifdef CONFIG_DEBUG_FS
+	snprintf(name, sizeof name, "msm_qcelp_%04x", audio->dec_id);
+	audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+			NULL, (void *) audio, &audqcelp_debug_fops);
+
+	if (IS_ERR(audio->dentry))
+		MM_DBG("debugfs_create_file failed\n");
+#endif
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
+	audio->suspend_ctl.node.resume = audqcelp_resume;
+	audio->suspend_ctl.node.suspend = audqcelp_suspend;
+	audio->suspend_ctl.audio = audio;
+	register_early_suspend(&audio->suspend_ctl.node);
+#endif
+	for (i = 0; i < AUDQCELP_EVENT_NUM; i++) {
+		e_node = kmalloc(sizeof(struct audqcelp_event), GFP_KERNEL);
+		if (e_node)
+			list_add_tail(&e_node->list, &audio->free_event_queue);
+		else {
+			MM_ERR("event pkt alloc failed\n");
+			break;
+		}
+	}
+done:
+	return rc;
+err:
+	iounmap(audio->data);
+	pmem_kfree(audio->phys);
+	audpp_adec_free(audio->dec_id);
+	kfree(audio);
+	return rc;
+}
+
+static const 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,
+	.fsync = audqcelp_fsync,
+};
+
+struct miscdevice audio_qcelp_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "msm_qcelp",
+	.fops = &audio_qcelp_fops,
+};
+
+static int __init audqcelp_init(void)
+{
+	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");
diff --git a/arch/arm/mach-msm/qdsp5/audio_qcelp_in.c b/arch/arm/mach-msm/qdsp5/audio_qcelp_in.c
new file mode 100644
index 0000000..31edc83
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audio_qcelp_in.c
@@ -0,0 +1,1380 @@
+/* arch/arm/mach-msm/qdsp5/audio_qcelp_in.c
+ *
+ * qcelp audio input device
+ *
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This code is based in part on arch/arm/mach-msm/qdsp5v2/audio_qcelp_in.c,
+ * 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_qcp.h>
+
+#include <mach/msm_memtypes.h>
+#include <linux/memory_alloc.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/qdsp5audpreproc.h>
+#include <mach/qdsp5/qdsp5audpreproccmdi.h>
+#include <mach/qdsp5/qdsp5audpreprocmsg.h>
+#include <mach/qdsp5/qdsp5audreccmdi.h>
+#include <mach/qdsp5/qdsp5audrecmsg.h>
+#include <mach/debug_mm.h>
+
+#define FRAME_HEADER_SIZE	8 /* 8 bytes frame header */
+#define NT_FRAME_HEADER_SIZE	24 /* 24 bytes frame header */
+/* FRAME_NUM must be a power of two */
+#define FRAME_NUM	8
+#define QCELP_FRAME_SIZE	36 /* 36 bytes data */
+/*Tunnel mode : 36 bytes data + 8 byte header*/
+#define FRAME_SIZE	(QCELP_FRAME_SIZE + FRAME_HEADER_SIZE)
+ /* 36 bytes data  + 24 meta field*/
+#define NT_FRAME_SIZE	(QCELP_FRAME_SIZE + NT_FRAME_HEADER_SIZE)
+#define DMASZ		(FRAME_SIZE * FRAME_NUM)
+#define NT_DMASZ	(NT_FRAME_SIZE * FRAME_NUM)
+#define OUT_FRAME_NUM	2
+#define OUT_BUFFER_SIZE (4 * 1024 + NT_FRAME_HEADER_SIZE)
+#define BUFFER_SIZE	(OUT_BUFFER_SIZE * OUT_FRAME_NUM)
+
+/* Offset from beginning of buffer*/
+#define AUDPREPROC_QCELP_EOS_FLG_OFFSET 0x0A
+#define AUDPREPROC_QCELP_EOS_FLG_MASK 0x01
+#define AUDPREPROC_QCELP_EOS_NONE 0x0 /* No EOS detected */
+#define AUDPREPROC_QCELP_EOS_SET 0x1 /* EOS set in meta field */
+
+struct buffer {
+	void *data;
+	uint32_t size;
+	uint32_t read;
+	uint32_t addr;
+	uint32_t used;
+	uint32_t mfield_sz;
+};
+
+struct audio_qcelp_in {
+	struct buffer in[FRAME_NUM];
+
+	spinlock_t dsp_lock;
+
+	atomic_t in_bytes;
+	atomic_t in_samples;
+
+	struct mutex lock;
+	struct mutex read_lock;
+	wait_queue_head_t wait;
+	wait_queue_head_t wait_enable;
+	/*write section*/
+	struct buffer out[OUT_FRAME_NUM];
+
+	uint8_t out_head;
+	uint8_t out_tail;
+	uint8_t out_needed;	/* number of buffers the dsp is waiting for */
+	uint32_t out_count;
+
+	struct mutex write_lock;
+	wait_queue_head_t write_wait;
+	int32_t out_phys; /* physical address of write buffer */
+	char *out_data;
+	int mfield; /* meta field embedded in data */
+	int wflush; /*write flush */
+	int rflush; /*read flush*/
+	int out_frame_cnt;
+
+	struct msm_adsp_module *audrec;
+	struct msm_adsp_module *audpre;
+
+
+	/* configuration to use on next enable */
+	uint32_t samp_rate;
+	uint32_t channel_mode;
+	uint32_t buffer_size; /* Frame size (36 bytes) */
+	uint32_t enc_type; /* 11 for QCELP */
+	uint32_t mode; /* T or NT Mode*/
+
+	struct msm_audio_qcelp_enc_config cfg;
+
+	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() */
+
+	uint32_t eos_ack;
+	uint32_t flush_ack;
+
+	const char *module_name;
+	unsigned queue_ids;
+	uint16_t enc_id; /* Session Id */
+
+	unsigned short samp_rate_index;
+	uint32_t audrec_obj_idx ;
+
+	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 */
+};
+
+struct audio_frame {
+	uint16_t frame_count_lsw;
+	uint16_t frame_count_msw;
+	uint16_t frame_length;
+	uint16_t erased_pcm;
+	unsigned char raw_bitstream[];
+} __packed;
+
+struct audio_frame_nt {
+	uint16_t metadata_len;
+	uint16_t frame_count_lsw;
+	uint16_t frame_count_msw;
+	uint16_t frame_length;
+	uint16_t erased_pcm;
+	uint16_t reserved;
+	uint16_t time_stamp_dword_lsw;
+	uint16_t time_stamp_dword_msw;
+	uint16_t time_stamp_lsw;
+	uint16_t time_stamp_msw;
+	uint16_t nflag_lsw;
+	uint16_t nflag_msw;
+	unsigned char raw_bitstream[]; /* samples */
+} __packed;
+
+struct qcelp_encoded_meta_out {
+	uint16_t metadata_len;
+	uint16_t time_stamp_dword_lsw;
+	uint16_t time_stamp_dword_msw;
+	uint16_t time_stamp_lsw;
+	uint16_t time_stamp_msw;
+	uint16_t nflag_lsw;
+	uint16_t nflag_msw;
+};
+
+/* Audrec Queue command sent macro's */
+#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, ((audio->queue_ids & 0xFFFF0000) >> 16),\
+			cmd, len)
+#define audio_send_queue_rec(audio, cmd, len) \
+	msm_adsp_write(audio->audrec, (audio->queue_ids & 0x0000FFFF),\
+			cmd, len)
+
+static int audqcelp_in_dsp_enable(struct audio_qcelp_in *audio, int enable);
+static int audqcelp_in_encparam_config(struct audio_qcelp_in *audio);
+static int audqcelp_in_encmem_config(struct audio_qcelp_in *audio);
+static int audqcelp_in_dsp_read_buffer(struct audio_qcelp_in *audio,
+				uint32_t read_cnt);
+static void audqcelp_in_flush(struct audio_qcelp_in *audio);
+
+static void audqcelp_in_get_dsp_frames(struct audio_qcelp_in *audio);
+static int audpcm_config(struct audio_qcelp_in *audio);
+static void audqcelp_out_flush(struct audio_qcelp_in *audio);
+static int audqcelp_in_routing_mode_config(struct audio_qcelp_in *audio);
+static void audrec_pcm_send_data(struct audio_qcelp_in *audio, unsigned needed);
+static void audqcelp_nt_in_get_dsp_frames(struct audio_qcelp_in *audio);
+static void audqcelp_in_flush(struct audio_qcelp_in *audio);
+
+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 audqcelp_in_enable(struct audio_qcelp_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;
+	cfg.codec = RPC_AUD_DEF_CODEC_13K;
+	cfg.snd_method = RPC_SND_METHOD_MIDI;
+
+	if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+		rc = audmgr_enable(&audio->audmgr, &cfg);
+		if (rc < 0)
+			return rc;
+
+		if (msm_adsp_enable(audio->audpre)) {
+			audmgr_disable(&audio->audmgr);
+			MM_ERR("msm_adsp_enable(audpre) failed\n");
+			return -ENODEV;
+		}
+	}
+	if (msm_adsp_enable(audio->audrec)) {
+		if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+			audmgr_disable(&audio->audmgr);
+			msm_adsp_disable(audio->audpre);
+		}
+		MM_ERR("msm_adsp_enable(audrec) failed\n");
+		return -ENODEV;
+	}
+
+	audio->enabled = 1;
+	audqcelp_in_dsp_enable(audio, 1);
+
+	return 0;
+}
+
+/* must be called with audio->lock held */
+static int audqcelp_in_disable(struct audio_qcelp_in *audio)
+{
+	if (audio->enabled) {
+		audio->enabled = 0;
+
+		audqcelp_in_dsp_enable(audio, 0);
+
+		wake_up(&audio->wait);
+		wait_event_interruptible_timeout(audio->wait_enable,
+				audio->running == 0, 1*HZ);
+		msm_adsp_disable(audio->audrec);
+		if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+			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:
+		MM_DBG("type %d, status_flag %d\n", msg[0], msg[1]);
+		break;
+	case AUDPREPROC_MSG_ERROR_MSG_ID:
+		MM_ERR("err_index %d\n", msg[0]);
+		break;
+	case ADSP_MESSAGE_ID:
+		MM_DBG("Received ADSP event: module enable(audpreproctask)\n");
+		break;
+	default:
+		MM_ERR("unknown event %d\n", id);
+	}
+}
+
+static void audqcelp_in_get_dsp_frames(struct audio_qcelp_in *audio)
+{
+	struct audio_frame *frame;
+	uint32_t index;
+	unsigned long flags;
+
+	index = audio->in_head;
+
+	frame = (void *) (((char *)audio->in[index].data) -
+			sizeof(*frame));
+	spin_lock_irqsave(&audio->dsp_lock, flags);
+	audio->in[index].size = frame->frame_length;
+
+	/* statistics of read */
+	atomic_add(audio->in[index].size, &audio->in_bytes);
+	atomic_add(1, &audio->in_samples);
+
+	audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1);
+
+	/* If overflow, move the tail index foward. */
+	if (audio->in_head == audio->in_tail) {
+		MM_ERR("Error! not able to keep up the read\n");
+		audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1);
+		MM_ERR("in_count = %d\n", audio->in_count);
+	} else
+		audio->in_count++;
+
+	audqcelp_in_dsp_read_buffer(audio, audio->dsp_cnt++);
+	spin_unlock_irqrestore(&audio->dsp_lock, flags);
+
+	wake_up(&audio->wait);
+}
+
+static void audqcelp_nt_in_get_dsp_frames(struct audio_qcelp_in *audio)
+{
+	struct audio_frame_nt *nt_frame;
+	uint32_t index;
+	unsigned long flags;
+
+	index = audio->in_head;
+	nt_frame = (void *) (((char *)audio->in[index].data) - \
+				sizeof(struct audio_frame_nt));
+	spin_lock_irqsave(&audio->dsp_lock, flags);
+	audio->in[index].size = nt_frame->frame_length;
+	/* statistics of read */
+	atomic_add(audio->in[index].size, &audio->in_bytes);
+	atomic_add(1, &audio->in_samples);
+
+	audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1);
+
+	/* If overflow, move the tail index foward. */
+	if (audio->in_head == audio->in_tail)
+		MM_DBG("Error! not able to keep up the read\n");
+	else
+		audio->in_count++;
+
+	spin_unlock_irqrestore(&audio->dsp_lock, flags);
+	wake_up(&audio->wait);
+}
+
+static int audrec_pcm_buffer_ptr_refresh(struct audio_qcelp_in *audio,
+				       unsigned idx, unsigned len)
+{
+	struct audrec_cmd_pcm_buffer_ptr_refresh_arm_enc cmd;
+
+	if (len ==  NT_FRAME_HEADER_SIZE)
+		len = len / 2;
+	else
+		len = (len + NT_FRAME_HEADER_SIZE) / 2;
+	MM_DBG("len = %d\n", len);
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDREC_CMD_PCM_BUFFER_PTR_REFRESH_ARM_TO_ENC;
+	cmd.num_buffers = 1;
+	if (cmd.num_buffers == 1) {
+		cmd.buf_address_length[0] = (audio->out[idx].addr &
+							0xffff0000) >> 16;
+		cmd.buf_address_length[1] = (audio->out[idx].addr &
+							0x0000ffff);
+		cmd.buf_address_length[2] = (len & 0xffff0000) >> 16;
+		cmd.buf_address_length[3] = (len & 0x0000ffff);
+	}
+	audio->out_frame_cnt++;
+	return audio_send_queue_rec(audio, &cmd, sizeof(cmd));
+}
+
+static int audpcm_config(struct audio_qcelp_in *audio)
+{
+	struct audrec_cmd_pcm_cfg_arm_to_enc cmd;
+	MM_DBG("\n");
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDREC_CMD_PCM_CFG_ARM_TO_ENC;
+	cmd.config_update_flag = AUDREC_PCM_CONFIG_UPDATE_FLAG_ENABLE;
+	cmd.enable_flag = AUDREC_ENABLE_FLAG_VALUE;
+	cmd.sampling_freq = convert_samp_index(audio->samp_rate);
+	if (!audio->channel_mode)
+		cmd.channels = 1;
+	else
+		cmd.channels = 2;
+	cmd.frequency_of_intimation = 1;
+	cmd.max_number_of_buffers = OUT_FRAME_NUM;
+	return audio_send_queue_rec(audio, &cmd, sizeof(cmd));
+}
+
+
+static int audqcelp_in_routing_mode_config(struct audio_qcelp_in *audio)
+{
+	struct audrec_cmd_routing_mode cmd;
+
+	MM_DBG("\n");
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDREC_CMD_ROUTING_MODE;
+	if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL)
+		cmd.routing_mode = 1;
+	return audio_send_queue_rec(audio, &cmd, sizeof(cmd));
+}
+
+static void audrec_dsp_event(void *data, unsigned id, size_t len,
+			    void (*getevent)(void *ptr, size_t len))
+{
+	struct audio_qcelp_in *audio = NULL;
+
+	if (data)
+		audio = data;
+	else {
+		MM_ERR("invalid data for event %x\n", id);
+		return;
+	}
+
+	switch (id) {
+	case AUDREC_MSG_CMD_CFG_DONE_MSG: {
+		struct audrec_msg_cmd_cfg_done_msg cmd_cfg_done_msg;
+		getevent(&cmd_cfg_done_msg, AUDREC_MSG_CMD_CFG_DONE_MSG_LEN);
+		if (cmd_cfg_done_msg.audrec_enc_type & \
+				AUDREC_MSG_CFG_DONE_ENC_ENA) {
+			audio->audrec_obj_idx = cmd_cfg_done_msg.audrec_obj_idx;
+			MM_DBG("CFG ENABLED\n");
+			if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) {
+				MM_DBG("routing command\n");
+				audqcelp_in_routing_mode_config(audio);
+			} else {
+				audqcelp_in_encmem_config(audio);
+			}
+		} else {
+			MM_DBG("CFG SLEEP\n");
+			audio->running = 0;
+			wake_up(&audio->wait_enable);
+		}
+		break;
+	}
+	case AUDREC_MSG_CMD_ROUTING_MODE_DONE_MSG: {
+		struct audrec_msg_cmd_routing_mode_done_msg \
+			routing_msg;
+		getevent(&routing_msg, AUDREC_MSG_CMD_ROUTING_MODE_DONE_MSG);
+		MM_DBG("AUDREC_MSG_CMD_ROUTING_MODE_DONE_MSG");
+		if (routing_msg.configuration == 0) {
+			MM_ERR("routing configuration failed\n");
+			audio->running = 0;
+			wake_up(&audio->wait_enable);
+		} else
+			audqcelp_in_encmem_config(audio);
+		break;
+	}
+	case AUDREC_MSG_CMD_AREC_MEM_CFG_DONE_MSG: {
+		MM_DBG("AREC_MEM_CFG_DONE_MSG\n");
+		if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)
+			audqcelp_in_encparam_config(audio);
+		else
+			audpcm_config(audio);
+		break;
+	}
+	case AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG: {
+		MM_DBG("AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG");
+		audqcelp_in_encparam_config(audio);
+	    break;
+	}
+	case AUDREC_MSG_CMD_AREC_PARAM_CFG_DONE_MSG: {
+		MM_DBG("AUDREC_MSG_CMD_AREC_PARAM_CFG_DONE_MSG\n");
+		audio->running = 1;
+		wake_up(&audio->wait_enable);
+		if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL)
+			audrec_pcm_send_data(audio, 1);
+		break;
+	}
+	case AUDREC_CMD_PCM_BUFFER_PTR_UPDATE_ARM_TO_ENC_MSG: {
+		MM_DBG("ptr_update recieved from DSP\n");
+		audrec_pcm_send_data(audio, 1);
+		break;
+	}
+	case AUDREC_MSG_NO_EXT_PKT_AVAILABLE_MSG: {
+		struct audrec_msg_no_ext_pkt_avail_msg err_msg;
+		getevent(&err_msg, AUDREC_MSG_NO_EXT_PKT_AVAILABLE_MSG_LEN);
+		MM_DBG("NO_EXT_PKT_AVAILABLE_MSG %x\n",\
+			err_msg.audrec_err_id);
+		break;
+	}
+	case AUDREC_MSG_PACKET_READY_MSG: {
+		struct audrec_msg_packet_ready_msg pkt_ready_msg;
+
+		getevent(&pkt_ready_msg, AUDREC_MSG_PACKET_READY_MSG_LEN);
+		MM_DBG("UP_PACKET_READY_MSG: write cnt msw  %d \
+		write cnt lsw %d read cnt msw %d  read cnt lsw %d \n",\
+		pkt_ready_msg.pkt_counter_msw, \
+		pkt_ready_msg.pkt_counter_lsw, \
+		pkt_ready_msg.pkt_read_cnt_msw, \
+		pkt_ready_msg.pkt_read_cnt_lsw);
+
+		audqcelp_in_get_dsp_frames(audio);
+		break;
+	}
+	case AUDREC_UP_NT_PACKET_READY_MSG: {
+		struct audrec_up_nt_packet_ready_msg pkt_ready_msg;
+
+		getevent(&pkt_ready_msg, AUDREC_UP_NT_PACKET_READY_MSG_LEN);
+		MM_DBG("UP_NT_PACKET_READY_MSG: write cnt lsw  %d \
+		write cnt msw %d read cnt lsw %d  read cnt msw %d \n",\
+		pkt_ready_msg.audrec_packetwrite_cnt_lsw, \
+		pkt_ready_msg.audrec_packetwrite_cnt_msw, \
+		pkt_ready_msg.audrec_upprev_readcount_lsw, \
+		pkt_ready_msg.audrec_upprev_readcount_msw);
+
+		audqcelp_nt_in_get_dsp_frames(audio);
+		break;
+	}
+	case AUDREC_CMD_FLUSH_DONE_MSG: {
+		audio->wflush = 0;
+		audio->rflush = 0;
+		audio->flush_ack = 1;
+		wake_up(&audio->write_wait);
+		MM_DBG("flush ack recieved\n");
+		break;
+	}
+	case ADSP_MESSAGE_ID:
+		MM_DBG("Received ADSP event: module \
+				enable/disable(audrectask)\n");
+		break;
+	default:
+		MM_ERR("unknown event %d\n", id);
+	}
+}
+
+static struct msm_adsp_ops audpre_qcelp_adsp_ops = {
+	.event = audpre_dsp_event,
+};
+
+static struct msm_adsp_ops audrec_qcelp_adsp_ops = {
+	.event = audrec_dsp_event,
+};
+
+static int audqcelp_in_dsp_enable(struct audio_qcelp_in *audio, int enable)
+{
+	struct audrec_cmd_enc_cfg cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDREC_CMD_ENC_CFG;
+	cmd.audrec_enc_type = (audio->enc_type & 0xFF) |
+			(enable ? AUDREC_CMD_ENC_ENA : AUDREC_CMD_ENC_DIS);
+	/* Don't care */
+	cmd.audrec_obj_idx = audio->audrec_obj_idx;
+
+	return audio_send_queue_rec(audio, &cmd, sizeof(cmd));
+}
+
+static int audqcelp_in_encmem_config(struct audio_qcelp_in *audio)
+{
+	struct audrec_cmd_arecmem_cfg cmd;
+	uint16_t *data = (void *) audio->data;
+	int n;
+	int header_len = 0;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	cmd.cmd_id = AUDREC_CMD_ARECMEM_CFG;
+	cmd.audrec_obj_idx = audio->audrec_obj_idx;
+	/* Rate at which packet complete message comes */
+	cmd.audrec_up_pkt_intm_cnt = 1;
+	cmd.audrec_extpkt_buffer_msw = audio->phys >> 16;
+	cmd.audrec_extpkt_buffer_lsw = audio->phys;
+	/* Max Buffer no available for frames */
+	cmd.audrec_extpkt_buffer_num = FRAME_NUM;
+
+	/* prepare buffer pointers:
+	 * T:36 bytes qcelp packet + 4 halfword header
+	 * NT:36 bytes qcelp packet + 12 halfword header
+	 */
+	if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)
+		header_len = FRAME_HEADER_SIZE/2;
+	else
+		header_len = NT_FRAME_HEADER_SIZE/2;
+
+	for (n = 0; n < FRAME_NUM; n++) {
+		audio->in[n].data = data + header_len;
+		data += (QCELP_FRAME_SIZE/2) + header_len;
+		MM_DBG("0x%8x\n", (int)(audio->in[n].data - header_len*2));
+	}
+
+	return audio_send_queue_rec(audio, &cmd, sizeof(cmd));
+}
+
+static int audqcelp_in_encparam_config(struct audio_qcelp_in *audio)
+{
+	struct audrec_cmd_arecparam_qcelp_cfg cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.common.cmd_id = AUDREC_CMD_ARECPARAM_CFG;
+	cmd.common.audrec_obj_idx = audio->audrec_obj_idx;
+	cmd.enc_min_rate = audio->cfg.min_bit_rate;
+	cmd.enc_max_rate = audio->cfg.max_bit_rate;
+	cmd.rate_modulation_cmd = 0;  /* Default set to 0 */
+	cmd.reduced_rate_level = 0;  /* Default set to 0 */
+
+	return audio_send_queue_rec(audio, &cmd, sizeof(cmd));
+}
+
+static int audqcelp_flush_command(struct audio_qcelp_in *audio)
+{
+	struct audrec_cmd_flush cmd;
+	MM_DBG("\n");
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDREC_CMD_FLUSH;
+	return audio_send_queue_rec(audio, &cmd, sizeof(cmd));
+}
+
+static int audqcelp_in_dsp_read_buffer(struct audio_qcelp_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;
+	cmd.type = audio->audrec_obj_idx;
+	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 audqcelp_ioport_reset(struct audio_qcelp_in *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);
+	audqcelp_in_flush(audio);
+	mutex_unlock(&audio->write_lock);
+	wake_up(&audio->wait);
+	mutex_lock(&audio->read_lock);
+	audqcelp_out_flush(audio);
+	mutex_unlock(&audio->read_lock);
+}
+
+static void audqcelp_in_flush(struct audio_qcelp_in *audio)
+{
+	int i;
+
+	audio->dsp_cnt = 0;
+	audio->in_head = 0;
+	audio->in_tail = 0;
+	audio->in_count = 0;
+	audio->eos_ack = 0;
+	for (i = FRAME_NUM-1; i >= 0; i--) {
+		audio->in[i].size = 0;
+		audio->in[i].read = 0;
+	}
+	MM_DBG("in_bytes %d\n", atomic_read(&audio->in_bytes));
+	MM_DBG("in_samples %d\n", atomic_read(&audio->in_samples));
+	atomic_set(&audio->in_bytes, 0);
+	atomic_set(&audio->in_samples, 0);
+}
+
+static void audqcelp_out_flush(struct audio_qcelp_in *audio)
+{
+	int i;
+
+	audio->out_head = 0;
+	audio->out_tail = 0;
+	audio->out_count = 0;
+	for (i = OUT_FRAME_NUM-1; i >= 0; i--) {
+		audio->out[i].size = 0;
+		audio->out[i].read = 0;
+		audio->out[i].used = 0;
+	}
+}
+
+/* ------------------- device --------------------- */
+static long audqcelp_in_ioctl(struct file *file,
+				unsigned int cmd, unsigned long arg)
+{
+	struct audio_qcelp_in *audio = file->private_data;
+	int rc = 0;
+
+	MM_DBG("\n");
+	if (cmd == AUDIO_GET_STATS) {
+		struct msm_audio_stats stats;
+		stats.byte_count = atomic_read(&audio->in_bytes);
+		stats.sample_count = atomic_read(&audio->in_samples);
+		if (copy_to_user((void *) arg, &stats, sizeof(stats)))
+			return -EFAULT;
+		return rc;
+	}
+
+	mutex_lock(&audio->lock);
+	switch (cmd) {
+	case AUDIO_START: {
+		rc = audqcelp_in_enable(audio);
+		if (!rc) {
+			rc =
+			wait_event_interruptible_timeout(audio->wait_enable,
+				audio->running != 0, 1*HZ);
+			MM_DBG("state %d rc = %d\n", audio->running, rc);
+
+			if (audio->running == 0)
+				rc = -ENODEV;
+			else
+				rc = 0;
+		}
+		audio->stopped = 0;
+		break;
+	}
+	case AUDIO_STOP: {
+		rc = audqcelp_in_disable(audio);
+		audio->stopped = 1;
+		break;
+	}
+	case AUDIO_FLUSH: {
+		MM_DBG("AUDIO_FLUSH\n");
+		audio->rflush = 1;
+		audio->wflush = 1;
+		audqcelp_ioport_reset(audio);
+		if (audio->running) {
+			audqcelp_flush_command(audio);
+			rc = wait_event_interruptible(audio->write_wait,
+				!audio->wflush);
+			if (rc < 0) {
+				MM_ERR("AUDIO_FLUSH interrupted\n");
+				rc = -EINTR;
+			}
+		} else {
+			audio->rflush = 0;
+			audio->wflush = 0;
+		}
+		break;
+	}
+	case AUDIO_GET_CONFIG: {
+		struct msm_audio_config cfg;
+		memset(&cfg, 0, sizeof(cfg));
+		cfg.buffer_size = OUT_BUFFER_SIZE;
+		cfg.buffer_count = OUT_FRAME_NUM;
+		cfg.sample_rate = convert_samp_index(audio->samp_rate);
+		cfg.channel_count = 1;
+		cfg.type = 0;
+		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;
+	}
+	case AUDIO_GET_STREAM_CONFIG: {
+		struct msm_audio_stream_config cfg;
+		memset(&cfg, 0, sizeof(cfg));
+		cfg.buffer_size = audio->buffer_size;
+		cfg.buffer_count = FRAME_NUM;
+		if (copy_to_user((void *)arg, &cfg, sizeof(cfg)))
+			rc = -EFAULT;
+		else
+			rc = 0;
+		break;
+	}
+	case AUDIO_SET_STREAM_CONFIG: {
+		struct msm_audio_stream_config cfg;
+		if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) {
+			rc = -EFAULT;
+			break;
+		}
+		/* Allow only single frame */
+		if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+			if (cfg.buffer_size != (FRAME_SIZE - 8))
+				rc = -EINVAL;
+				break;
+		} else {
+			if (cfg.buffer_size != (QCELP_FRAME_SIZE + 14))
+				rc = -EINVAL;
+				break;
+		}
+		audio->buffer_size = cfg.buffer_size;
+		break;
+	}
+	case AUDIO_GET_QCELP_ENC_CONFIG: {
+		if (copy_to_user((void *) arg, &audio->cfg, sizeof(audio->cfg)))
+			rc = -EFAULT;
+		break;
+	}
+	case AUDIO_SET_QCELP_ENC_CONFIG: {
+		struct msm_audio_qcelp_enc_config cfg;
+		if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) {
+			rc = -EFAULT;
+			break;
+		}
+		MM_DBG("0X%8x, 0x%8x, 0x%8x\n", cfg.min_bit_rate,
+				cfg.max_bit_rate, cfg.cdma_rate);
+		if (cfg.min_bit_rate > CDMA_RATE_FULL || \
+				 cfg.min_bit_rate < CDMA_RATE_EIGHTH) {
+			MM_ERR("invalid min bitrate\n");
+			rc = -EFAULT;
+			break;
+		}
+		if (cfg.max_bit_rate > CDMA_RATE_FULL || \
+				cfg.max_bit_rate < CDMA_RATE_EIGHTH) {
+			MM_ERR("invalid max bitrate\n");
+			rc = -EFAULT;
+			break;
+		}
+		/* Recording Does not support Erase and Blank */
+		if (cfg.cdma_rate > CDMA_RATE_FULL ||
+			cfg.cdma_rate < CDMA_RATE_EIGHTH) {
+			MM_ERR("invalid qcelp cdma rate\n");
+			rc = -EFAULT;
+			break;
+		}
+		memcpy(&audio->cfg, &cfg, sizeof(cfg));
+		break;
+	}
+	default:
+		rc = -EINVAL;
+	}
+	mutex_unlock(&audio->lock);
+	return rc;
+}
+
+static ssize_t audqcelp_in_read(struct file *file,
+				char __user *buf,
+				size_t count, loff_t *pos)
+{
+	struct audio_qcelp_in *audio = file->private_data;
+	unsigned long flags;
+	const char __user *start = buf;
+	void *data;
+	uint32_t index;
+	uint32_t size;
+	int rc = 0;
+	struct qcelp_encoded_meta_out meta_field;
+	struct audio_frame_nt *nt_frame;
+	MM_DBG("count = %d\n", count);
+	mutex_lock(&audio->read_lock);
+	while (count > 0) {
+		rc = wait_event_interruptible(
+			audio->wait, (audio->in_count > 0) || audio->stopped ||
+			audio->rflush);
+		if (rc < 0)
+			break;
+
+		if (audio->rflush) {
+			rc = -EBUSY;
+			break;
+		}
+		if (audio->stopped && !audio->in_count) {
+			MM_DBG("Driver in stop state, No more buffer to read");
+			rc = 0;/* End of File */
+			break;
+		}
+
+		index = audio->in_tail;
+		data = (uint8_t *) audio->in[index].data;
+		size = audio->in[index].size;
+
+		if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) {
+			nt_frame = (struct audio_frame_nt *)(data -
+					sizeof(struct audio_frame_nt));
+			memcpy((char *)&meta_field.time_stamp_dword_lsw,
+				(char *)&nt_frame->time_stamp_dword_lsw,
+				(sizeof(struct qcelp_encoded_meta_out) - \
+				sizeof(uint16_t)));
+			meta_field.metadata_len =
+					sizeof(struct qcelp_encoded_meta_out);
+			if (copy_to_user((char *)start, (char *)&meta_field,
+				sizeof(struct qcelp_encoded_meta_out))) {
+				rc = -EFAULT;
+				break;
+			}
+			if (nt_frame->nflag_lsw & 0x0001) {
+				MM_ERR("recieved EOS in read call\n");
+				audio->eos_ack = 1;
+			}
+			buf += sizeof(struct qcelp_encoded_meta_out);
+			count -= sizeof(struct qcelp_encoded_meta_out);
+		}
+		if (count >= size) {
+			/* order the reads on the buffer */
+			dma_coherent_post_ops();
+			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->mode == MSM_AUD_ENC_MODE_NONTUNNEL)) {
+				if (!audio->eos_ack) {
+					MM_DBG("sending read ptr command \
+							%d %d\n",
+							audio->dsp_cnt,
+							audio->in_tail);
+					audqcelp_in_dsp_read_buffer(audio,
+							audio->dsp_cnt++);
+				}
+			}
+		} else {
+			MM_ERR("short read\n");
+			break;
+		}
+		break;
+	}
+	mutex_unlock(&audio->read_lock);
+
+	if (buf > start)
+		return buf - start;
+
+	return rc;
+}
+
+static void audrec_pcm_send_data(struct audio_qcelp_in *audio, unsigned needed)
+{
+	struct buffer *frame;
+	unsigned long flags;
+	MM_DBG("\n");
+	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) {
+			MM_DBG("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);
+			audrec_pcm_buffer_ptr_refresh(audio,
+						 audio->out_tail,
+						    frame->used);
+			frame->used = 0xffffffff;
+			audio->out_needed = 0;
+		}
+	}
+ done:
+	spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+static int audqcelp_in_fsync(struct file *file,	int datasync)
+
+{
+	struct audio_qcelp_in *audio = file->private_data;
+	int rc = 0;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	if (!audio->running || (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)) {
+		rc = -EINVAL;
+		goto done_nolock;
+	}
+
+	mutex_lock(&audio->write_lock);
+
+	rc = wait_event_interruptible(audio->write_wait,
+			audio->wflush);
+	MM_DBG("waked on by some event audio->wflush = %d\n", audio->wflush);
+
+	if (rc < 0)
+		goto done;
+	else if (audio->wflush) {
+		rc = -EBUSY;
+		goto done;
+	}
+done:
+	mutex_unlock(&audio->write_lock);
+done_nolock:
+	return rc;
+
+}
+
+int audrec_qcelp_process_eos(struct audio_qcelp_in *audio,
+		const char __user *buf_start, unsigned short mfield_size)
+{
+	struct buffer *frame;
+	int rc = 0;
+
+	frame = audio->out + audio->out_head;
+
+	rc = wait_event_interruptible(audio->write_wait,
+		(audio->out_needed &&
+		audio->out[0].used == 0 &&
+		audio->out[1].used == 0)
+		|| (audio->stopped)
+		|| (audio->wflush));
+
+	if (rc < 0)
+		goto done;
+	if (audio->stopped || audio->wflush) {
+		rc = -EBUSY;
+		goto done;
+	}
+	if (copy_from_user(frame->data, buf_start, mfield_size)) {
+		rc = -EFAULT;
+		goto done;
+	}
+
+	frame->mfield_sz = mfield_size;
+	audio->out_head ^= 1;
+	frame->used = mfield_size;
+	MM_DBG("copying meta_out frame->used = %d\n", frame->used);
+	audrec_pcm_send_data(audio, 0);
+done:
+	return rc;
+}
+
+static ssize_t audqcelp_in_write(struct file *file,
+				const char __user *buf,
+				size_t count, loff_t *pos)
+{
+	struct audio_qcelp_in *audio = file->private_data;
+	const char __user *start = buf;
+	struct buffer *frame;
+	char *cpy_ptr;
+	int rc = 0, eos_condition = AUDPREPROC_QCELP_EOS_NONE;
+	unsigned short mfield_size = 0;
+	int write_count = 0;
+	MM_DBG("cnt=%d\n", count);
+
+	if (count & 1)
+		return -EINVAL;
+
+	if (audio->mode != MSM_AUD_ENC_MODE_NONTUNNEL)
+		return -EINVAL;
+
+	mutex_lock(&audio->write_lock);
+	frame = audio->out + audio->out_head;
+	/* if supplied count is more than driver buffer size
+	 * then only copy driver buffer size
+	 */
+	if (count > frame->size)
+		count = frame->size;
+
+	write_count = count;
+	cpy_ptr = frame->data;
+	rc = wait_event_interruptible(audio->write_wait,
+				      (frame->used == 0)
+					|| (audio->stopped)
+					|| (audio->wflush));
+	if (rc < 0)
+		goto error;
+
+	if (audio->stopped || audio->wflush) {
+		rc = -EBUSY;
+		goto error;
+	}
+	if (audio->mfield) {
+		if (buf == start) {
+			/* Processing beginning of user buffer */
+			if (__get_user(mfield_size,
+				(unsigned short __user *) buf)) {
+				rc = -EFAULT;
+				goto error;
+			} else if (mfield_size > count) {
+				rc = -EINVAL;
+				goto error;
+			}
+			MM_DBG("mf offset_val %x\n", mfield_size);
+			if (copy_from_user(cpy_ptr, buf, mfield_size)) {
+				rc = -EFAULT;
+				goto error;
+			}
+			/* Check if EOS flag is set and buffer has
+			 * contains just meta field
+			 */
+			if (cpy_ptr[AUDPREPROC_QCELP_EOS_FLG_OFFSET] &
+					AUDPREPROC_QCELP_EOS_FLG_MASK) {
+				eos_condition = AUDPREPROC_QCELP_EOS_SET;
+				MM_DBG("EOS SET\n");
+				if (mfield_size == count) {
+					buf += mfield_size;
+					eos_condition = 0;
+					goto exit;
+				} else
+				cpy_ptr[AUDPREPROC_QCELP_EOS_FLG_OFFSET] &=
+					~AUDPREPROC_QCELP_EOS_FLG_MASK;
+			}
+			cpy_ptr += mfield_size;
+			count -= mfield_size;
+			buf += mfield_size;
+		} else {
+			mfield_size = 0;
+			MM_DBG("continuous buffer\n");
+		}
+		frame->mfield_sz = mfield_size;
+	}
+	MM_DBG("copying the stream count = %d\n", count);
+	if (copy_from_user(cpy_ptr, buf, count)) {
+		rc = -EFAULT;
+		goto error;
+	}
+exit:
+	frame->used = count;
+	audio->out_head ^= 1;
+	if (!audio->flush_ack)
+		audrec_pcm_send_data(audio, 0);
+	else {
+		audrec_pcm_send_data(audio, 1);
+		audio->flush_ack = 0;
+	}
+	if (eos_condition == AUDPREPROC_QCELP_EOS_SET)
+		rc = audrec_qcelp_process_eos(audio, start, mfield_size);
+	mutex_unlock(&audio->write_lock);
+	return write_count;
+error:
+	mutex_unlock(&audio->write_lock);
+	return rc;
+}
+
+static int audqcelp_in_release(struct inode *inode, struct file *file)
+{
+	struct audio_qcelp_in *audio = file->private_data;
+
+	mutex_lock(&audio->lock);
+	audqcelp_in_disable(audio);
+	audqcelp_in_flush(audio);
+	msm_adsp_put(audio->audrec);
+
+	if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)
+		msm_adsp_put(audio->audpre);
+
+	audpreproc_aenc_free(audio->enc_id);
+	audio->audrec = NULL;
+	audio->audpre = NULL;
+	audio->opened = 0;
+
+	if ((audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) && \
+	   (audio->out_data)) {
+		free_contiguous_memory(audio->out_data);
+		audio->out_data = NULL;
+	}
+
+	if (audio->data) {
+		free_contiguous_memory(audio->data);
+		audio->data = NULL;
+	}
+	mutex_unlock(&audio->lock);
+	return 0;
+}
+
+static struct audio_qcelp_in the_audio_qcelp_in;
+
+static int audqcelp_in_open(struct inode *inode, struct file *file)
+{
+	struct audio_qcelp_in *audio = &the_audio_qcelp_in;
+	int rc;
+	int encid;
+	int dma_size = 0;
+
+	mutex_lock(&audio->lock);
+	if (audio->opened) {
+		rc = -EBUSY;
+		goto done;
+	}
+	if ((file->f_mode & FMODE_WRITE) &&
+		(file->f_mode & FMODE_READ)) {
+		audio->mode = MSM_AUD_ENC_MODE_NONTUNNEL;
+		dma_size = NT_DMASZ;
+		MM_DBG("Opened for non tunnel mode encoding\n");
+	} else if (!(file->f_mode & FMODE_WRITE) &&
+				(file->f_mode & FMODE_READ)) {
+		audio->mode = MSM_AUD_ENC_MODE_TUNNEL;
+		dma_size = DMASZ;
+		MM_DBG("Opened for tunnel mode encoding\n");
+	} else {
+		MM_ERR("Invalid mode\n");
+		rc = -EACCES;
+		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_8000,
+	audio->samp_rate_index = AUDREC_CMD_SAMP_RATE_INDX_8000;
+	audio->channel_mode = AUDREC_CMD_STEREO_MODE_MONO;
+	if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL)
+		audio->buffer_size = (QCELP_FRAME_SIZE + 14);
+	else
+		audio->buffer_size = QCELP_FRAME_SIZE;
+	audio->enc_type = AUDREC_CMD_TYPE_0_INDEX_QCELP | audio->mode;
+
+	audio->cfg.cdma_rate = CDMA_RATE_FULL;
+	audio->cfg.min_bit_rate = CDMA_RATE_FULL;
+	audio->cfg.max_bit_rate = CDMA_RATE_FULL;
+
+	if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+		rc = audmgr_open(&audio->audmgr);
+		if (rc)
+			goto done;
+	}
+
+	encid = audpreproc_aenc_alloc(audio->enc_type, &audio->module_name,
+			&audio->queue_ids);
+	if (encid < 0) {
+		MM_ERR("No free encoder available\n");
+		rc = -ENODEV;
+		goto done;
+	}
+	audio->enc_id = encid;
+
+	rc = msm_adsp_get(audio->module_name, &audio->audrec,
+			   &audrec_qcelp_adsp_ops, audio);
+	if (rc) {
+		audpreproc_aenc_free(audio->enc_id);
+		goto done;
+	}
+
+	if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+		rc = msm_adsp_get("AUDPREPROCTASK", &audio->audpre,
+				&audpre_qcelp_adsp_ops, audio);
+		if (rc) {
+			msm_adsp_put(audio->audrec);
+			audpreproc_aenc_free(audio->enc_id);
+			goto done;
+		}
+	}
+
+	audio->dsp_cnt = 0;
+	audio->stopped = 0;
+	audio->wflush = 0;
+	audio->rflush = 0;
+	audio->flush_ack = 0;
+
+	audqcelp_in_flush(audio);
+	audqcelp_out_flush(audio);
+
+	audio->data = allocate_contiguous_memory(dma_size, MEMTYPE_EBI1,
+				SZ_4K, 0);
+	if (!audio->data) {
+		MM_ERR("could not allocate read buffers\n");
+		rc = -ENOMEM;
+		goto evt_error;
+	} else {
+		audio->phys = memory_pool_node_paddr(audio->data);
+		if (!audio->phys) {
+			MM_ERR("could not get physical address\n");
+			rc = -ENOMEM;
+			free_contiguous_memory(audio->data);
+			goto evt_error;
+		}
+		MM_DBG("read buf: phy addr 0x%08x kernel addr 0x%08x\n",
+				audio->phys, (int)audio->data);
+	}
+
+	audio->out_data = NULL;
+	if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) {
+		audio->out_data = allocate_contiguous_memory(BUFFER_SIZE,
+				MEMTYPE_EBI1,
+				SZ_4K, 0);
+		if (!audio->out_data) {
+			MM_ERR("could not allocate read buffers\n");
+			rc = -ENOMEM;
+			free_contiguous_memory(audio->data);
+			goto evt_error;
+		} else {
+			audio->out_phys = memory_pool_node_paddr(
+						audio->out_data);
+			if (!audio->out_phys) {
+				MM_ERR("could not get physical address\n");
+				rc = -ENOMEM;
+				free_contiguous_memory(audio->data);
+				free_contiguous_memory(audio->out_data);
+				goto evt_error;
+			}
+			MM_DBG("write buf:phy addr 0x%08x kernel addr 0x%08x\n",
+					audio->out_phys, (int)audio->out_data);
+		}
+
+		/* Initialize buffer */
+		audio->out[0].data = audio->out_data + 0;
+		audio->out[0].addr = audio->out_phys + 0;
+		audio->out[0].size = OUT_BUFFER_SIZE;
+
+		audio->out[1].data = audio->out_data + OUT_BUFFER_SIZE;
+		audio->out[1].addr = audio->out_phys + OUT_BUFFER_SIZE;
+		audio->out[1].size = OUT_BUFFER_SIZE;
+
+		MM_DBG("audio->out[0].data = %d  audio->out[1].data = %d",
+				(unsigned int)audio->out[0].data,
+				(unsigned int)audio->out[1].data);
+		audio->mfield = NT_FRAME_HEADER_SIZE;
+		audio->out_frame_cnt++;
+	}
+	file->private_data = audio;
+	audio->opened = 1;
+
+done:
+	mutex_unlock(&audio->lock);
+	return rc;
+evt_error:
+	msm_adsp_put(audio->audrec);
+	if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)
+		msm_adsp_put(audio->audpre);
+
+	audpreproc_aenc_free(audio->enc_id);
+	mutex_unlock(&audio->lock);
+	return rc;
+}
+
+static const struct file_operations audio_qcelp_in_fops = {
+	.owner		= THIS_MODULE,
+	.open		= audqcelp_in_open,
+	.release	= audqcelp_in_release,
+	.read		= audqcelp_in_read,
+	.write		= audqcelp_in_write,
+	.fsync		= audqcelp_in_fsync,
+	.unlocked_ioctl	= audqcelp_in_ioctl,
+};
+
+static struct miscdevice audqcelp_in_misc = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "msm_qcelp_in",
+	.fops	= &audio_qcelp_in_fops,
+};
+
+static int __init audqcelp_in_init(void)
+{
+	mutex_init(&the_audio_qcelp_in.lock);
+	mutex_init(&the_audio_qcelp_in.read_lock);
+	spin_lock_init(&the_audio_qcelp_in.dsp_lock);
+	init_waitqueue_head(&the_audio_qcelp_in.wait);
+	init_waitqueue_head(&the_audio_qcelp_in.wait_enable);
+	mutex_init(&the_audio_qcelp_in.write_lock);
+	init_waitqueue_head(&the_audio_qcelp_in.write_wait);
+	return misc_register(&audqcelp_in_misc);
+}
+device_initcall(audqcelp_in_init);
diff --git a/arch/arm/mach-msm/qdsp5/audio_voicememo.c b/arch/arm/mach-msm/qdsp5/audio_voicememo.c
new file mode 100644
index 0000000..b7e8e1c
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audio_voicememo.c
@@ -0,0 +1,960 @@
+/* arch/arm/mach-msm/qdsp5/audio_voicememo.c
+ *
+ * Voice Memo device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This code is based in part on arch/arm/mach-msm/qdsp5/audio_mp3.c
+ *
+ * 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 <linux/msm_audio_voicememo.h>
+#include <linux/slab.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_rpcrouter.h>
+#include <mach/debug_mm.h>
+
+#include "audmgr.h"
+
+#define SND_PROG_VERS "rs30000002:0x00020001"
+#define SND_PROG 0x30000002
+#define SND_VERS_COMP 0x00020001
+#define SND_VERS2_COMP 0x00030001
+
+#define SND_VOC_REC_START_PROC                  19
+#define SND_VOC_REC_STOP_PROC                   20
+#define SND_VOC_REC_PAUSE_PROC			21
+#define SND_VOC_REC_RESUME_PROC                 22
+#define SND_VOC_REC_PUT_BUF_PROC                23
+
+#define SND_VOC_REC_AV_SYNC_CB_PTR_PROC 	9
+#define SND_VOC_REC_CB_FUNC_TYPE_PROC 		10
+
+#define REC_CLIENT_DATA		0x11223344
+#define DATA_CB_FUNC_ID		0x12345678
+#define AV_SYNC_CB_FUNC_ID	0x87654321
+#define CLIENT_DATA		0xaabbccdd
+
+#define RPC_TYPE_REQUEST 0
+#define RPC_TYPE_REPLY 1
+
+#define RPC_STATUS_FAILURE 0
+#define RPC_STATUS_SUCCESS 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)
+
+#define MAX_FRAME_SIZE 36 /* QCELP - 36, AMRNB - 32, EVRC - 24 */
+#define MAX_REC_BUF_COUNT 5 /* Maximum supported voc rec buffers */
+#define MAX_REC_BUF_SIZE (MAX_FRAME_SIZE * 10)
+#define MAX_VOICEMEMO_BUF_SIZE  \
+	((MAX_REC_BUF_SIZE)*MAX_REC_BUF_COUNT) /* 5 buffers for 200ms frame */
+#define MSM_AUD_BUFFER_UPDATE_WAIT_MS 2000
+
+enum rpc_voc_rec_status_type {
+	RPC_VOC_REC_STAT_SUCCESS = 1,
+	RPC_VOC_REC_STAT_DONE = 2,
+	RPC_VOC_REC_STAT_AUTO_STOP = 4,
+	RPC_VOC_REC_STAT_PAUSED = 8,
+	RPC_VOC_REC_STAT_RESUMED = 16,
+	RPC_VOC_REC_STAT_ERROR = 32,
+	RPC_VOC_REC_STAT_BUFFER_ERROR = 64,
+	RPC_VOC_REC_STAT_INVALID_PARAM = 128,
+	RPC_VOC_REC_STAT_INT_TIME = 256,
+	RPC_VOC_REC_STAT_DATA = 512,
+	RPC_VOC_REC_STAT_NOT_READY = 1024,
+	RPC_VOC_REC_STAT_INFORM_EVRC = 2048,
+	RPC_VOC_REC_STAT_INFORM_13K = 4096,
+	RPC_VOC_REC_STAT_INFORM_AMR = 8192,
+	RPC_VOC_REC_STAT_INFORM_MAX = 65535
+};
+
+struct rpc_snd_voc_rec_start_args {
+	uint32_t param_status; /* 1 = valid, 0 = not valid */
+	uint32_t rec_type;
+	uint32_t rec_interval_ms;
+	uint32_t auto_stop_ms;
+	uint32_t capability;
+	uint32_t max_rate;
+	uint32_t min_rate;
+	uint32_t frame_format;
+	uint32_t dtx_enable;
+	uint32_t data_req_ms;
+	uint32_t rec_client_data;
+
+	uint32_t cb_func_id;
+	uint32_t sync_cb_func_id;
+	uint32_t client_data;
+};
+
+struct rpc_snd_voc_rec_put_buf_args {
+	uint32_t buf;
+	uint32_t num_bytes;
+};
+
+struct snd_voc_rec_start_msg {
+	struct rpc_request_hdr hdr;
+	struct rpc_snd_voc_rec_start_args args;
+};
+
+struct snd_voc_rec_put_buf_msg {
+	struct rpc_request_hdr hdr;
+	struct rpc_snd_voc_rec_put_buf_args args;
+};
+
+struct snd_voc_rec_av_sync_cb_func_data {
+	uint32_t sync_cb_func_id;
+	uint32_t status;  /* Pointer status (1 = valid, 0  = invalid) */
+	uint32_t num_samples;
+	uint32_t time_stamp[2];
+	uint32_t lost_samples;
+	uint32_t frame_index;
+	uint32_t client_data;
+};
+
+struct snd_voc_rec_cb_func_fw_data {
+	uint32_t fw_ptr_status; /* FW Pointer status (1=valid,0=invalid) */
+	uint32_t rec_buffer_size;
+	uint32_t data[MAX_REC_BUF_SIZE/4];
+	uint32_t rec_buffer_size_copy;
+	uint32_t rec_num_frames; /* Number of voice frames */
+	uint32_t rec_length; /* Valid data in record buffer =
+			      * data_req_ms amount of data */
+	uint32_t client_data; /* A11 rec buffer pointer */
+	uint32_t rw_ptr_status; /* RW Pointer status (1=valid,0=invalid) */
+};
+
+struct snd_voc_rec_cb_func_rw_data {
+	uint32_t fw_ptr_status; /* FW Pointer status (1=valid,0=invalid) */
+	uint32_t rw_ptr_status; /* RW Pointer status (1=valid,0=invalid) */
+	uint32_t rec_buffer_size;
+	uint32_t data[MAX_REC_BUF_SIZE/4];
+	uint32_t rec_buffer_size_copy;
+	uint32_t rec_num_frames; /* Number of voice frames */
+	uint32_t rec_length; /* Valid data in record buffer =
+			      * data_req_ms amount of data */
+	uint32_t client_data; /* A11 rec buffer pointer */
+};
+
+struct snd_voc_rec_data_cb_func_data {
+	uint32_t cb_func_id;
+	uint32_t status; /* Pointer status (1 = valid, 0  = invalid) */
+	uint32_t rec_status;
+
+	union {
+		struct snd_voc_rec_cb_func_fw_data fw_data;
+		struct snd_voc_rec_cb_func_rw_data rw_data;
+	} pkt;
+};
+
+struct buffer {
+	void *data;
+	unsigned size;
+	unsigned used; /* Usage actual recorded data */
+	unsigned addr;
+	unsigned numframes;
+};
+
+struct audio_voicememo {
+	uint32_t byte_count; /* Pass statistics to user space for
+			      * time stamping */
+	uint32_t frame_count;
+
+	int opened;
+	int enabled;
+	int running;
+	int stopped;
+	int pause_resume;
+
+	uint32_t rpc_prog;
+	uint32_t rpc_ver;
+	uint32_t rpc_xid;
+	uint32_t rpc_status;
+
+	struct mutex lock;
+	struct mutex read_lock;
+	struct mutex dsp_lock;
+	wait_queue_head_t read_wait;
+	wait_queue_head_t wait;
+
+	struct buffer in[MAX_REC_BUF_COUNT];
+	char *rec_buf_ptr;
+	dma_addr_t phys;
+	uint32_t rec_buf_size;
+	uint8_t read_next;	/* index to input buffers to be read next */
+	uint8_t fill_next;	/* index to buffer that should be filled as
+				 * data comes from A9 */
+
+	struct audmgr audmgr;
+
+	struct msm_audio_voicememo_config voicememo_cfg;
+
+	struct msm_rpc_endpoint *sndept;
+	struct task_struct *task;
+};
+
+static struct audio_voicememo the_audio_voicememo;
+
+static int audvoicememo_validate_usr_config(
+		struct msm_audio_voicememo_config *config)
+{
+	int rc = -1; /* error */
+
+	if (config->rec_type != RPC_VOC_REC_FORWARD &&
+		config->rec_type != RPC_VOC_REC_REVERSE &&
+		config->rec_type != RPC_VOC_REC_BOTH)
+		goto done;
+
+	/* QCELP, EVRC, AMR-NB only */
+	if (config->capability != RPC_VOC_CAP_IS733 &&
+		config->capability != RPC_VOC_CAP_IS127 &&
+		config->capability != RPC_VOC_CAP_AMR)
+		goto done;
+
+	/* QCP, AMR format supported */
+	if ((config->frame_format != RPC_VOC_PB_NATIVE_QCP) &&
+		(config->frame_format != RPC_VOC_PB_AMR))
+		goto done;
+
+	if ((config->frame_format == RPC_VOC_PB_AMR) &&
+		(config->capability != RPC_VOC_CAP_AMR))
+		goto done;
+
+	/* To make sure, max kernel buf size matches
+	 * with max data request time */
+	if (config->data_req_ms > ((MAX_REC_BUF_SIZE/MAX_FRAME_SIZE)*20))
+		goto done;
+
+	rc = 0;
+done:
+	return rc;
+}
+
+static void audvoicememo_flush_buf(struct audio_voicememo *audio)
+{
+	uint8_t index;
+
+	for (index = 0; index < MAX_REC_BUF_COUNT; index++)
+		audio->in[index].used = 0;
+
+	audio->read_next = 0;
+	audio->fill_next = 0;
+}
+
+static void audvoicememo_ioport_reset(struct audio_voicememo *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->read_wait);
+	mutex_lock(&audio->read_lock);
+	audvoicememo_flush_buf(audio);
+	mutex_unlock(&audio->read_lock);
+}
+
+/* must be called with audio->lock held */
+static int audvoicememo_enable(struct audio_voicememo *audio)
+{
+	struct audmgr_config cfg;
+	struct snd_voc_rec_put_buf_msg bmsg;
+	struct snd_voc_rec_start_msg msg;
+	uint8_t index;
+	uint32_t offset = 0;
+	int rc;
+
+	if (audio->enabled)
+		return 0;
+
+	/* Codec / method configure to audmgr client */
+	cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_8000;
+	cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE;
+	cfg.def_method = RPC_AUD_DEF_METHOD_RECORD;
+
+	if (audio->voicememo_cfg.capability == RPC_VOC_CAP_IS733)
+		cfg.codec = RPC_AUD_DEF_CODEC_VOC_13K;
+	else if (audio->voicememo_cfg.capability == RPC_VOC_CAP_IS127)
+		cfg.codec = RPC_AUD_DEF_CODEC_VOC_EVRC;
+	else
+		cfg.codec = RPC_AUD_DEF_CODEC_VOC_AMR; /* RPC_VOC_CAP_AMR */
+
+	cfg.snd_method = RPC_SND_METHOD_VOICE;
+	rc = audmgr_enable(&audio->audmgr, &cfg);
+
+	if (rc < 0)
+		return rc;
+
+	/* Configure VOC Rec buffer */
+	for (index = 0; index < MAX_REC_BUF_COUNT; index++) {
+		audio->in[index].data = audio->rec_buf_ptr + offset;
+		audio->in[index].addr = audio->phys + offset;
+		audio->in[index].size = audio->rec_buf_size;
+		audio->in[index].used = 0;
+		audio->in[index].numframes = 0;
+		offset += audio->rec_buf_size;
+		bmsg.args.buf = (uint32_t) audio->in[index].data;
+		bmsg.args.num_bytes = cpu_to_be32(audio->in[index].size);
+		MM_DBG("rec_buf_ptr=0x%8x, rec_buf_size = 0x%8x\n",
+				bmsg.args.buf, bmsg.args.num_bytes);
+
+		msm_rpc_setup_req(&bmsg.hdr, audio->rpc_prog, audio->rpc_ver,
+				SND_VOC_REC_PUT_BUF_PROC);
+		audio->rpc_xid = bmsg.hdr.xid;
+		audio->rpc_status = RPC_STATUS_FAILURE;
+		msm_rpc_write(audio->sndept, &bmsg, sizeof(bmsg));
+		rc = wait_event_timeout(audio->wait,
+			audio->rpc_status != RPC_STATUS_FAILURE, 1 * HZ);
+		if (rc == 0)
+			goto err;
+	}
+
+
+	/* Start Recording */
+	msg.args.param_status = cpu_to_be32(0x00000001);
+	msg.args.rec_type = cpu_to_be32(audio->voicememo_cfg.rec_type);
+	msg.args.rec_interval_ms =
+		cpu_to_be32(audio->voicememo_cfg.rec_interval_ms);
+	msg.args.auto_stop_ms = cpu_to_be32(audio->voicememo_cfg.auto_stop_ms);
+	msg.args.capability = cpu_to_be32(audio->voicememo_cfg.capability);
+	msg.args.max_rate = cpu_to_be32(audio->voicememo_cfg.max_rate);
+	msg.args.min_rate = cpu_to_be32(audio->voicememo_cfg.min_rate);
+	msg.args.frame_format = cpu_to_be32(audio->voicememo_cfg.frame_format);
+	msg.args.dtx_enable = cpu_to_be32(audio->voicememo_cfg.dtx_enable);
+	msg.args.data_req_ms = cpu_to_be32(audio->voicememo_cfg.data_req_ms);
+	msg.args.rec_client_data = cpu_to_be32(REC_CLIENT_DATA);
+	msg.args.cb_func_id = cpu_to_be32(DATA_CB_FUNC_ID);
+	msg.args.sync_cb_func_id = cpu_to_be32(AV_SYNC_CB_FUNC_ID);
+	msg.args.client_data = cpu_to_be32(CLIENT_DATA);
+
+	msm_rpc_setup_req(&msg.hdr, audio->rpc_prog, audio->rpc_ver,
+			SND_VOC_REC_START_PROC);
+
+	audio->rpc_xid = msg.hdr.xid;
+	audio->rpc_status = RPC_STATUS_FAILURE;
+	msm_rpc_write(audio->sndept, &msg, sizeof(msg));
+	rc = wait_event_timeout(audio->wait,
+		audio->rpc_status != RPC_STATUS_FAILURE, 1 * HZ);
+	if (rc == 0)
+		goto err;
+
+	audio->rpc_xid = 0;
+	audio->enabled = 1;
+	return 0;
+
+err:
+	audio->rpc_xid = 0;
+	audmgr_disable(&audio->audmgr);
+	MM_ERR("Fail\n");
+	return -1;
+}
+
+/* must be called with audio->lock held */
+static int audvoicememo_disable(struct audio_voicememo *audio)
+{
+	struct rpc_request_hdr rhdr;
+	int rc = 0;
+	if (audio->enabled) {
+		msm_rpc_setup_req(&rhdr, audio->rpc_prog, audio->rpc_ver,
+				SND_VOC_REC_STOP_PROC);
+		rc = msm_rpc_write(audio->sndept, &rhdr, sizeof(rhdr));
+		wait_event_timeout(audio->wait, audio->stopped == 0,
+				1 * HZ);
+		wake_up(&audio->read_wait);
+		audmgr_disable(&audio->audmgr);
+		audio->enabled = 0;
+	}
+	return 0;
+}
+
+/* RPC Reply Generator */
+static void rpc_reply(struct msm_rpc_endpoint *ept, uint32_t xid)
+{
+	int rc = 0;
+	uint8_t reply_buf[sizeof(struct rpc_reply_hdr)];
+	struct rpc_reply_hdr *reply = (struct rpc_reply_hdr *)reply_buf;
+
+	MM_DBG("inside\n");
+	reply->xid = cpu_to_be32(xid);
+	reply->type = cpu_to_be32(RPC_TYPE_REPLY); /* reply */
+	reply->reply_stat = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED);
+
+	reply->data.acc_hdr.accept_stat = cpu_to_be32(RPC_ACCEPTSTAT_SUCCESS);
+	reply->data.acc_hdr.verf_flavor = 0;
+	reply->data.acc_hdr.verf_length = 0;
+
+	rc = msm_rpc_write(ept, reply_buf, sizeof(reply_buf));
+	if (rc < 0)
+		MM_ERR("could not write RPC response: %d\n", rc);
+}
+
+static void process_rpc_request(uint32_t proc, uint32_t xid,
+		void *data, int len, void *private)
+{
+	struct audio_voicememo *audio = private;
+
+	MM_DBG("inside\n");
+	/* Sending Ack before processing the request
+	 * to make sure A9 get response immediate
+	 * However, if there is validation of request planned
+	 * may be move this reply Ack at the end */
+	rpc_reply(audio->sndept, xid);
+	switch (proc) {
+	case SND_VOC_REC_AV_SYNC_CB_PTR_PROC: {
+		MM_DBG("AV Sync CB:func_id=0x%8x,status=0x%x\n",
+			be32_to_cpu(( \
+			(struct snd_voc_rec_av_sync_cb_func_data *)\
+			data)->sync_cb_func_id),\
+			be32_to_cpu(( \
+			(struct snd_voc_rec_av_sync_cb_func_data *)\
+			data)->status));
+		break;
+		}
+	case SND_VOC_REC_CB_FUNC_TYPE_PROC: {
+		struct snd_voc_rec_data_cb_func_data *datacb_data
+			= (void *)(data);
+		struct snd_voc_rec_put_buf_msg bmsg;
+		uint32_t rec_status = be32_to_cpu(datacb_data->rec_status);
+
+		MM_DBG("Data CB:func_id=0x%8x,status=0x%x,\
+			rec_status=0x%x\n",
+			be32_to_cpu(datacb_data->cb_func_id),\
+			be32_to_cpu(datacb_data->status),\
+			be32_to_cpu(datacb_data->rec_status));
+
+		/* Data recorded */
+		if ((rec_status == RPC_VOC_REC_STAT_DATA) ||
+		(rec_status == RPC_VOC_REC_STAT_DONE)) {
+			if (datacb_data->pkt.fw_data.fw_ptr_status &&
+			be32_to_cpu(datacb_data->pkt.fw_data.rec_length)) {
+
+				MM_DBG("Copy FW link:rec_buf_size \
+				= 0x%08x, rec_length=0x%08x\n",
+				be32_to_cpu( \
+				datacb_data->pkt.fw_data. \
+				rec_buffer_size_copy),\
+				be32_to_cpu(datacb_data->pkt.fw_data. \
+				rec_length));
+
+				mutex_lock(&audio->dsp_lock);
+				memcpy(audio->in[audio->fill_next].data, \
+					&(datacb_data->pkt.fw_data.data[0]), \
+				be32_to_cpu(
+				datacb_data->pkt.fw_data.rec_length));
+				audio->in[audio->fill_next].used =
+				be32_to_cpu(
+					datacb_data->pkt.fw_data.rec_length);
+				audio->in[audio->fill_next].numframes =
+				be32_to_cpu(
+				datacb_data->pkt.fw_data.rec_num_frames);
+				mutex_unlock(&audio->dsp_lock);
+			} else if (datacb_data->pkt.rw_data.rw_ptr_status &&
+			be32_to_cpu(datacb_data->pkt.rw_data.rec_length)) {
+				MM_DBG("Copy RW link:rec_buf_size \
+				=0x%08x, rec_length=0x%08x\n",
+				be32_to_cpu( \
+				datacb_data->pkt.rw_data. \
+				rec_buffer_size_copy),\
+				be32_to_cpu(datacb_data->pkt.rw_data. \
+				rec_length));
+
+				mutex_lock(&audio->dsp_lock);
+				memcpy(audio->in[audio->fill_next].data, \
+				&(datacb_data->pkt.rw_data.data[0]), \
+				be32_to_cpu(
+					datacb_data->pkt.rw_data.rec_length));
+				audio->in[audio->fill_next].used =
+				be32_to_cpu(
+					datacb_data->pkt.rw_data.rec_length);
+				audio->in[audio->fill_next].numframes =
+				be32_to_cpu(
+				datacb_data->pkt.rw_data.rec_num_frames);
+				mutex_unlock(&audio->dsp_lock);
+			}
+			if (rec_status != RPC_VOC_REC_STAT_DONE) {
+				/* Not end of record */
+				bmsg.args.buf = \
+				(uint32_t) audio->in[audio->fill_next].data;
+				bmsg.args.num_bytes = \
+				be32_to_cpu(audio->in[audio->fill_next].size);
+
+				if (++audio->fill_next ==  MAX_REC_BUF_COUNT)
+					audio->fill_next = 0;
+
+				msm_rpc_setup_req(&bmsg.hdr, audio->rpc_prog,
+				audio->rpc_ver, SND_VOC_REC_PUT_BUF_PROC);
+
+				msm_rpc_write(audio->sndept, &bmsg,
+				sizeof(bmsg));
+
+				wake_up(&audio->read_wait);
+			} else {
+				/* Indication record stopped gracefully */
+				MM_DBG("End Of Voice Record\n");
+				wake_up(&audio->wait);
+			}
+		} else if (rec_status == RPC_VOC_REC_STAT_PAUSED) {
+			MM_DBG(" Voice Record PAUSED\n");
+			audio->pause_resume = 1;
+		} else if (rec_status == RPC_VOC_REC_STAT_RESUMED) {
+			MM_DBG(" Voice Record RESUMED\n");
+			audio->pause_resume = 0;
+		} else if ((rec_status == RPC_VOC_REC_STAT_ERROR) ||
+		(rec_status == RPC_VOC_REC_STAT_INVALID_PARAM) ||
+		(rec_status == RPC_VOC_REC_STAT_BUFFER_ERROR))
+			MM_ERR("error recording =0x%8x\n",
+				rec_status);
+		else if (rec_status == RPC_VOC_REC_STAT_INT_TIME)
+			MM_DBG("Frames recorded matches interval \
+					callback time\n");
+		else if (rec_status == RPC_VOC_REC_STAT_AUTO_STOP) {
+			MM_DBG(" Voice Record AUTO STOP\n");
+			wake_up(&audio->read_wait);
+			audmgr_disable(&audio->audmgr);
+			audio->stopped = 1;
+			audvoicememo_ioport_reset(audio);
+			audio->stopped = 0;
+			audio->enabled = 0;
+		}
+			break;
+		}
+	default:
+		MM_ERR("UNKNOWN PROC , proc = 0x%8x \n", proc);
+	}
+}
+
+static int voicememo_rpc_thread(void *data)
+{
+	struct audio_voicememo *audio = data;
+	struct rpc_request_hdr *hdr = NULL;
+	uint32_t type;
+	int len;
+
+	MM_DBG("start\n");
+
+	while (!kthread_should_stop()) {
+		kfree(hdr);
+		hdr = NULL;
+
+		len = msm_rpc_read(audio->sndept, (void **) &hdr, -1, -1);
+		MM_DBG("rpc_read len = 0x%x\n", len);
+		if (len < 0) {
+			MM_ERR("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);
+
+				/* Confirm major RPC success during open*/
+				if ((audio->enabled == 0) &&
+					(status == RPC_ACCEPTSTAT_SUCCESS) &&
+					(audio->rpc_xid == rep->xid)) {
+						audio->rpc_status = \
+							RPC_STATUS_SUCCESS;
+						wake_up(&audio->wait);
+				}
+				MM_DBG("rpc_reply status 0x%8x\n", status);
+			} else {
+				MM_ERR("rpc_reply denied!\n");
+			}
+			/* process reply */
+			continue;
+		} else if (type == RPC_TYPE_REQUEST) {
+			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),
+						audio);
+		} else
+			MM_ERR("Unexpected type (%d)\n", type);
+	}
+	MM_DBG("stop\n");
+	kfree(hdr);
+	hdr = NULL;
+
+	return 0;
+}
+
+/* ------------------- device --------------------- */
+static long audio_voicememo_ioctl(struct file *file,
+				unsigned int cmd, unsigned long arg)
+{
+	struct audio_voicememo *audio = file->private_data;
+	int rc = 0;
+
+	if (cmd == AUDIO_GET_STATS) {
+		struct msm_audio_stats stats;
+		mutex_lock(&audio->dsp_lock);
+		stats.byte_count = audio->byte_count;
+		stats.sample_count = audio->frame_count;
+		mutex_unlock(&audio->dsp_lock);
+		if (copy_to_user((void *) arg, &stats, sizeof(stats)))
+			return -EFAULT;
+		return 0;
+	}
+
+	mutex_lock(&audio->lock);
+	switch (cmd) {
+	case AUDIO_START: {
+			MM_DBG("AUDIO_START\n");
+			audio->byte_count = 0;
+			audio->frame_count = 0;
+			if (audio->voicememo_cfg.rec_type != RPC_VOC_REC_NONE)
+				rc = audvoicememo_enable(audio);
+			else
+				rc = -EINVAL;
+			MM_DBG("AUDIO_START rc %d\n", rc);
+			break;
+		}
+	case AUDIO_STOP: {
+			MM_DBG("AUDIO_STOP\n");
+			rc = audvoicememo_disable(audio);
+			audio->stopped = 1;
+			audvoicememo_ioport_reset(audio);
+			audio->stopped = 0;
+			MM_DBG("AUDIO_STOP rc %d\n", rc);
+			break;
+		}
+	case AUDIO_GET_CONFIG: {
+			struct msm_audio_config cfg;
+			MM_DBG("AUDIO_GET_CONFIG\n");
+			cfg.buffer_size = audio->rec_buf_size;
+			cfg.buffer_count = MAX_REC_BUF_COUNT;
+			cfg.sample_rate = 8000; /* Voice Encoder works on 8k,
+						 * Mono */
+			cfg.channel_count = 1;
+			cfg.type = 0;
+			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;
+			MM_DBG("AUDIO_GET_CONFIG rc %d\n", rc);
+			break;
+		}
+	case AUDIO_GET_VOICEMEMO_CONFIG: {
+			MM_DBG("AUDIO_GET_VOICEMEMO_CONFIG\n");
+			if (copy_to_user((void *)arg, &audio->voicememo_cfg,
+				sizeof(audio->voicememo_cfg)))
+				rc = -EFAULT;
+			else
+				rc = 0;
+			MM_DBG("AUDIO_GET_VOICEMEMO_CONFIG rc %d\n", rc);
+			break;
+		}
+	case AUDIO_SET_VOICEMEMO_CONFIG: {
+			struct msm_audio_voicememo_config usr_config;
+			MM_DBG("AUDIO_SET_VOICEMEMO_CONFIG\n");
+			if (copy_from_user
+				(&usr_config, (void *)arg,
+				sizeof(usr_config))) {
+				rc = -EFAULT;
+				break;
+			}
+			if (audvoicememo_validate_usr_config(&usr_config)
+					== 0) {
+				audio->voicememo_cfg = usr_config;
+				rc = 0;
+			} else
+				rc = -EINVAL;
+			MM_DBG("AUDIO_SET_VOICEMEMO_CONFIG rc %d\n", rc);
+			break;
+		}
+	case AUDIO_PAUSE: {
+			struct rpc_request_hdr rhdr;
+			MM_DBG("AUDIO_PAUSE\n");
+			if (arg == 1)
+				msm_rpc_setup_req(&rhdr, audio->rpc_prog,
+				audio->rpc_ver, SND_VOC_REC_PAUSE_PROC);
+			else
+				msm_rpc_setup_req(&rhdr, audio->rpc_prog,
+				audio->rpc_ver, SND_VOC_REC_RESUME_PROC);
+
+			rc = msm_rpc_write(audio->sndept, &rhdr, sizeof(rhdr));
+			MM_DBG("AUDIO_PAUSE exit %d\n",	rc);
+			break;
+		}
+	default:
+		MM_ERR("IOCTL %d not supported\n", cmd);
+		rc = -EINVAL;
+	}
+	mutex_unlock(&audio->lock);
+	return rc;
+}
+
+static ssize_t audio_voicememo_read(struct file *file,
+				char __user *buf,
+				size_t count, loff_t *pos)
+{
+	struct audio_voicememo *audio = file->private_data;
+	const char __user *start = buf;
+	int rc = 0;
+
+	mutex_lock(&audio->read_lock);
+
+	MM_DBG("buff read =0x%8x \n", count);
+
+	while (count > 0) {
+		rc = wait_event_interruptible_timeout(audio->read_wait,
+			(audio->in[audio->read_next].used > 0) ||
+			(audio->stopped),
+			msecs_to_jiffies(MSM_AUD_BUFFER_UPDATE_WAIT_MS));
+
+		if (rc == 0) {
+			rc = -ETIMEDOUT;
+			break;
+		} else 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 split frames, read count must be greater or
+			 * equal to size of existing frames to copy
+			 */
+			MM_DBG("read not in frame boundary\n");
+			break;
+		} else {
+			mutex_lock(&audio->dsp_lock);
+			dma_coherent_post_ops();
+			if (copy_to_user
+				(buf, audio->in[audio->read_next].data,
+				audio->in[audio->read_next].used)) {
+				MM_ERR("invalid addr %x \n", (unsigned int)buf);
+				rc = -EFAULT;
+				mutex_unlock(&audio->dsp_lock);
+				break;
+			}
+			count -= audio->in[audio->read_next].used;
+			audio->byte_count += audio->in[audio->read_next].used;
+			audio->frame_count +=
+			audio->in[audio->read_next].numframes;
+			buf += audio->in[audio->read_next].used;
+			audio->in[audio->read_next].used = 0;
+			mutex_unlock(&audio->dsp_lock);
+			if ((++audio->read_next) == MAX_REC_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
+					 */
+		}
+	}
+	mutex_unlock(&audio->read_lock);
+	if (buf > start)
+		rc = buf - start;
+	MM_DBG("exit return =0x%8x\n", rc);
+	return rc;
+}
+
+static ssize_t audio_voicememo_write(struct file *file,
+				const char __user *buf,
+				size_t count, loff_t *pos)
+{
+	return -EINVAL;
+}
+
+static int audio_voicememo_release(struct inode *inode, struct file *file)
+{
+	struct audio_voicememo *audio = file->private_data;
+
+	mutex_lock(&audio->lock);
+	audvoicememo_disable(audio);
+	audvoicememo_flush_buf(audio);
+	audio->opened = 0;
+	mutex_unlock(&audio->lock);
+	return 0;
+}
+
+static int audio_voicememo_open(struct inode *inode, struct file *file)
+{
+	struct audio_voicememo *audio = &the_audio_voicememo;
+	int rc;
+
+	mutex_lock(&audio->lock);
+	if (audio->opened) {
+		rc = -EBUSY;
+		goto done;
+	}
+
+	rc = audmgr_open(&audio->audmgr);
+
+	if (rc)
+		goto done;
+
+	/*Set default param to None*/
+	memset(&audio->voicememo_cfg, 0, sizeof(audio->voicememo_cfg));
+
+	file->private_data = audio;
+	audio->opened = 1;
+	rc = 0;
+done:
+	mutex_unlock(&audio->lock);
+	return rc;
+}
+
+static const struct file_operations audio_fops = {
+	.owner		= THIS_MODULE,
+	.open		= audio_voicememo_open,
+	.release	= audio_voicememo_release,
+	.read		= audio_voicememo_read,
+	.write		= audio_voicememo_write,
+	.unlocked_ioctl	= audio_voicememo_ioctl,
+};
+
+struct miscdevice audio_voicememo_misc = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "msm_voicememo",
+	.fops	= &audio_fops,
+};
+
+static int audio_voicememo_probe(struct platform_device *pdev)
+{
+	int rc;
+
+	if ((pdev->id != (SND_VERS_COMP & RPC_VERSION_MAJOR_MASK)) &&
+	    (pdev->id != (SND_VERS2_COMP & RPC_VERSION_MAJOR_MASK)))
+		return -EINVAL;
+
+	mutex_init(&the_audio_voicememo.lock);
+	mutex_init(&the_audio_voicememo.read_lock);
+	mutex_init(&the_audio_voicememo.dsp_lock);
+	init_waitqueue_head(&the_audio_voicememo.read_wait);
+	init_waitqueue_head(&the_audio_voicememo.wait);
+
+	the_audio_voicememo.rec_buf_ptr = dma_alloc_coherent(NULL,
+					MAX_VOICEMEMO_BUF_SIZE,
+					&the_audio_voicememo.phys, GFP_KERNEL);
+	if (the_audio_voicememo.rec_buf_ptr == NULL) {
+		MM_ERR("error allocating memory\n");
+		rc = -ENOMEM;
+		return rc;
+	}
+	the_audio_voicememo.rec_buf_size = MAX_REC_BUF_SIZE;
+	MM_DBG("rec_buf_ptr = 0x%8x, phys = 0x%8x \n",
+		(uint32_t) the_audio_voicememo.rec_buf_ptr, \
+		the_audio_voicememo.phys);
+
+	the_audio_voicememo.sndept = msm_rpc_connect_compatible(SND_PROG,
+					SND_VERS_COMP, MSM_RPC_UNINTERRUPTIBLE);
+	if (IS_ERR(the_audio_voicememo.sndept)) {
+		MM_DBG("connect failed with VERS \
+				= %x, trying again with another API\n",
+				SND_VERS_COMP);
+		the_audio_voicememo.sndept = msm_rpc_connect_compatible(
+					SND_PROG, SND_VERS2_COMP,
+					MSM_RPC_UNINTERRUPTIBLE);
+		if (IS_ERR(the_audio_voicememo.sndept)) {
+			rc = PTR_ERR(the_audio_voicememo.sndept);
+			the_audio_voicememo.sndept = NULL;
+			MM_ERR("Failed to connect to snd svc\n");
+			goto err;
+		}
+		the_audio_voicememo.rpc_ver = SND_VERS2_COMP;
+	} else
+		the_audio_voicememo.rpc_ver = SND_VERS_COMP;
+
+	the_audio_voicememo.task = kthread_run(voicememo_rpc_thread,
+					&the_audio_voicememo, "voicememo_rpc");
+	if (IS_ERR(the_audio_voicememo.task)) {
+		rc = PTR_ERR(the_audio_voicememo.task);
+		the_audio_voicememo.task = NULL;
+		msm_rpc_close(the_audio_voicememo.sndept);
+		the_audio_voicememo.sndept = NULL;
+		MM_ERR("Failed to create voicememo_rpc task\n");
+		goto err;
+	}
+	the_audio_voicememo.rpc_prog = SND_PROG;
+
+	return misc_register(&audio_voicememo_misc);
+err:
+	dma_free_coherent(NULL, MAX_VOICEMEMO_BUF_SIZE,
+		the_audio_voicememo.rec_buf_ptr,
+		the_audio_voicememo.phys);
+	the_audio_voicememo.rec_buf_ptr = NULL;
+	return rc;
+}
+
+static void __exit audio_voicememo_exit(void)
+{
+	/* Close the RPC connection to make thread to comeout */
+	msm_rpc_close(the_audio_voicememo.sndept);
+	the_audio_voicememo.sndept = NULL;
+	kthread_stop(the_audio_voicememo.task);
+	the_audio_voicememo.task = NULL;
+	if (the_audio_voicememo.rec_buf_ptr)
+		dma_free_coherent(NULL, MAX_VOICEMEMO_BUF_SIZE,
+			the_audio_voicememo.rec_buf_ptr,
+			the_audio_voicememo.phys);
+	the_audio_voicememo.rec_buf_ptr = NULL;
+	misc_deregister(&audio_voicememo_misc);
+}
+
+static char audio_voicememo_rpc_name[] = "rs00000000";
+
+static struct platform_driver audio_voicememo_driver = {
+	.probe = audio_voicememo_probe,
+	.driver = {
+		.owner = THIS_MODULE,
+	},
+ };
+
+static int __init audio_voicememo_init(void)
+{
+	snprintf(audio_voicememo_rpc_name, sizeof(audio_voicememo_rpc_name),
+			"rs%08x", SND_PROG);
+	audio_voicememo_driver.driver.name = audio_voicememo_rpc_name;
+	return platform_driver_register(&audio_voicememo_driver);
+}
+
+module_init(audio_voicememo_init);
+module_exit(audio_voicememo_exit);
+
+MODULE_DESCRIPTION("MSM Voice Memo driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("QUALCOMM");
diff --git a/arch/arm/mach-msm/qdsp5/audio_wma.c b/arch/arm/mach-msm/qdsp5/audio_wma.c
new file mode 100644
index 0000000..11f051f
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audio_wma.c
@@ -0,0 +1,1760 @@
+/* audio_wma.c - wma audio decoder driver
+ *
+ * Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * 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/debugfs.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/earlysuspend.h>
+#include <linux/android_pmem.h>
+#include <linux/slab.h>
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+
+#include <linux/msm_audio.h>
+
+#include "audmgr.h"
+
+#include <linux/msm_audio_wma.h>
+#include <mach/qdsp5/qdsp5audppcmdi.h>
+#include <mach/qdsp5/qdsp5audppmsg.h>
+#include <mach/qdsp5/qdsp5audplaycmdi.h>
+#include <mach/qdsp5/qdsp5audplaymsg.h>
+#include <mach/qdsp5/qdsp5rmtcmdi.h>
+#include <mach/debug_mm.h>
+
+/* Size must be power of 2 */
+#define BUFSZ_MAX 	2062	/* Includes meta in size */
+#define BUFSZ_MIN 	1038	/* Includes meta in size */
+#define DMASZ_MAX 	(BUFSZ_MAX * 2)
+#define DMASZ_MIN 	(BUFSZ_MIN * 2)
+
+#define AUDPLAY_INVALID_READ_PTR_OFFSET	0xFFFF
+#define AUDDEC_DEC_WMA 4
+
+#define PCM_BUFSZ_MIN 	8216 	/* Hold one stereo WMA frame and meta out*/
+#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
+
+#define AUDWMA_METAFIELD_MASK 0xFFFF0000
+#define AUDWMA_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */
+#define AUDWMA_EOS_FLG_MASK 0x01
+#define AUDWMA_EOS_NONE 0x0 /* No EOS detected */
+#define AUDWMA_EOS_SET 0x1 /* EOS set in meta field */
+
+#define AUDWMA_EVENT_NUM 10 /* Default number of pre-allocated event packets */
+
+struct buffer {
+	void *data;
+	unsigned size;
+	unsigned used;		/* Input usage actual DSP produced PCM size  */
+	unsigned addr;
+	unsigned short mfield_sz; /*only useful for data has meta field */
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+struct audwma_suspend_ctl {
+	struct early_suspend node;
+	struct audio *audio;
+};
+#endif
+
+struct audwma_event{
+	struct list_head list;
+	int event_type;
+	union msm_audio_event_payload payload;
+};
+
+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 */
+	int32_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_wma_config wma_config;
+	struct audmgr audmgr;
+
+	/* data allocated for various buffers */
+	char *data;
+	int32_t phys; /* physical address of write buffer */
+
+	int mfield; /* meta field embedded in data */
+	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 rmt_resource_released;
+	int teos; /* valid only if tunnel mode & no data left for decoder */
+	enum msm_aud_decoder_state dec_state;	/* Represents decoder state */
+	int reserved; /* A byte is being reserved */
+	char rsv_byte; /* Handle odd length user data */
+
+	const char *module_name;
+	unsigned queue_id;
+	uint16_t dec_id;
+	uint32_t read_ptr_offset;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	struct audwma_suspend_ctl suspend_ctl;
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *dentry;
+#endif
+
+	wait_queue_head_t wait;
+	struct list_head free_event_queue;
+	struct list_head event_queue;
+	wait_queue_head_t event_wait;
+	spinlock_t event_queue_lock;
+	struct mutex get_event_lock;
+	int event_abort;
+
+	int eq_enable;
+	int eq_needs_commit;
+	audpp_cmd_cfg_object_params_eqalizer eq;
+	audpp_cmd_cfg_object_params_volume vol_pan;
+};
+
+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);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void audwma_post_event(struct audio *audio, int type,
+		union msm_audio_event_payload payload);
+#endif
+
+static int rmt_put_resource(struct audio *audio)
+{
+	struct aud_codec_config_cmd cmd;
+	unsigned short client_idx;
+
+	cmd.cmd_id = RM_CMD_AUD_CODEC_CFG;
+	cmd.client_id = RM_AUD_CLIENT_ID;
+	cmd.task_id = audio->dec_id;
+	cmd.enable = RMT_DISABLE;
+	cmd.dec_type = AUDDEC_DEC_WMA;
+	client_idx = ((cmd.client_id << 8) | cmd.task_id);
+
+	return put_adsp_resource(client_idx, &cmd, sizeof(cmd));
+}
+
+static int rmt_get_resource(struct audio *audio)
+{
+	struct aud_codec_config_cmd cmd;
+	unsigned short client_idx;
+
+	cmd.cmd_id = RM_CMD_AUD_CODEC_CFG;
+	cmd.client_id = RM_AUD_CLIENT_ID;
+	cmd.task_id = audio->dec_id;
+	cmd.enable = RMT_ENABLE;
+	cmd.dec_type = AUDDEC_DEC_WMA;
+	client_idx = ((cmd.client_id << 8) | cmd.task_id);
+
+	return get_adsp_resource(client_idx, &cmd, sizeof(cmd));
+}
+
+/* must be called with audio->lock held */
+static int audio_enable(struct audio *audio)
+{
+	struct audmgr_config cfg;
+	int rc;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	if (audio->enabled)
+		return 0;
+
+	if (audio->rmt_resource_released == 1) {
+		audio->rmt_resource_released = 0;
+		rc = rmt_get_resource(audio);
+		if (rc) {
+			MM_ERR("ADSP resources are not available for WMA \
+				session 0x%08x on decoder: %d\n Ignoring \
+				error and going ahead with the playback\n",
+				(int)audio, audio->dec_id);
+		}
+	}
+
+	audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+	audio->out_tail = 0;
+	audio->out_needed = 0;
+
+	if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) {
+		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_WMA;
+		cfg.snd_method = RPC_SND_METHOD_MIDI;
+
+		rc = audmgr_enable(&audio->audmgr, &cfg);
+		if (rc < 0)
+			return rc;
+	}
+
+	if (msm_adsp_enable(audio->audplay)) {
+		MM_ERR("msm_adsp_enable(audplay) failed\n");
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			audmgr_disable(&audio->audmgr);
+		return -ENODEV;
+	}
+
+	if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) {
+		MM_ERR("audpp_enable() failed\n");
+		msm_adsp_disable(audio->audplay);
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			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)
+{
+	int rc = 0;
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	if (audio->enabled) {
+		audio->enabled = 0;
+		audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+		auddec_dsp_config(audio, 0);
+		rc = wait_event_interruptible_timeout(audio->wait,
+				audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+				msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+		if (rc == 0)
+			rc = -ETIMEDOUT;
+		else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE)
+			rc = -EFAULT;
+		else
+			rc = 0;
+		wake_up(&audio->write_wait);
+		wake_up(&audio->read_wait);
+		msm_adsp_disable(audio->audplay);
+		audpp_disable(audio->dec_id, audio);
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			audmgr_disable(&audio->audmgr);
+		audio->out_needed = 0;
+		rmt_put_resource(audio);
+		audio->rmt_resource_released = 1;
+	}
+	return rc;
+}
+
+/* ------------------- 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]) {
+			MM_DBG("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 {
+			MM_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 {
+		MM_DBG("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));
+
+	MM_DBG("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;
+
+	case ADSP_MESSAGE_ID:
+		MM_DBG("Received ADSP event: module enable(audplaytask)\n");
+		break;
+
+	default:
+		MM_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: {
+				uint16_t reason = msg[2];
+				MM_DBG("decoder status:sleep reason = \
+						0x%04x\n", reason);
+				if ((reason == AUDPP_MSG_REASON_MEM)
+					|| (reason ==
+					AUDPP_MSG_REASON_NODECODER)) {
+					audio->dec_state =
+						MSM_AUD_DECODER_STATE_FAILURE;
+					wake_up(&audio->wait);
+				} else if (reason == AUDPP_MSG_REASON_NONE) {
+					/* decoder is in disable state */
+					audio->dec_state =
+						MSM_AUD_DECODER_STATE_CLOSE;
+					wake_up(&audio->wait);
+				}
+				break;
+			}
+			case AUDPP_DEC_STATUS_INIT:
+				MM_DBG("decoder status: init\n");
+				if (audio->pcm_feedback)
+					audpp_cmd_cfg_routing_mode(audio);
+				else
+					audpp_cmd_cfg_adec_params(audio);
+				break;
+
+			case AUDPP_DEC_STATUS_CFG:
+				MM_DBG("decoder status: cfg\n");
+				break;
+			case AUDPP_DEC_STATUS_PLAY:
+				MM_DBG("decoder status: play\n");
+				if (audio->pcm_feedback) {
+					audplay_config_hostpcm(audio);
+					audplay_buffer_refresh(audio);
+				}
+				audio->dec_state =
+					MSM_AUD_DECODER_STATE_SUCCESS;
+				wake_up(&audio->wait);
+				break;
+			default:
+				MM_ERR("unknown decoder status\n");
+			}
+			break;
+		}
+	case AUDPP_MSG_CFG_MSG:
+		if (msg[0] == AUDPP_MSG_ENA_ENA) {
+			MM_DBG("CFG_MSG ENABLE\n");
+			auddec_dsp_config(audio, 1);
+			audio->out_needed = 0;
+			audio->running = 1;
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan);
+			audpp_dsp_set_eq(audio->dec_id,	audio->eq_enable,
+								&audio->eq);
+			audpp_avsync(audio->dec_id, 22050);
+		} else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+			MM_DBG("CFG_MSG DISABLE\n");
+			audpp_avsync(audio->dec_id, 0);
+			audio->running = 0;
+		} else {
+			MM_DBG("CFG_MSG %d?\n", msg[0]);
+		}
+		break;
+	case AUDPP_MSG_ROUTING_ACK:
+		MM_DBG("ROUTING_ACK mode=%d\n", msg[1]);
+		audpp_cmd_cfg_adec_params(audio);
+		break;
+
+	case AUDPP_MSG_FLUSH_ACK:
+		MM_DBG("FLUSH_ACK\n");
+		audio->wflush = 0;
+		audio->rflush = 0;
+		wake_up(&audio->write_wait);
+		if (audio->pcm_feedback)
+			audplay_buffer_refresh(audio);
+	case AUDPP_MSG_PCMDMAMISSED:
+		MM_DBG("PCMDMAMISSED\n");
+		audio->teos = 1;
+		wake_up(&audio->write_wait);
+		break;
+
+	default:
+		MM_ERR("UNKNOWN (%d)\n", id);
+	}
+
+}
+
+static struct msm_adsp_ops audplay_adsp_ops_wma = {
+	.event = audplay_dsp_event,
+};
+
+#define audplay_send_queue0(audio, cmd, len) \
+	msm_adsp_write(audio->audplay, audio->queue_id, \
+			cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+	u16 cfg_dec_cmd[AUDPP_CMD_CFG_DEC_TYPE_LEN / sizeof(unsigned short)];
+
+	memset(cfg_dec_cmd, 0, sizeof(cfg_dec_cmd));
+	cfg_dec_cmd[0] = AUDPP_CMD_CFG_DEC_TYPE;
+	if (enable)
+		cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC |
+			AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_WMA;
+	else
+		cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC |
+			AUDPP_CMD_DIS_DEC_V;
+
+	return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd));
+}
+
+static void audpp_cmd_cfg_adec_params(struct audio *audio)
+{
+	struct audpp_cmd_cfg_adec_params_wma cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
+	cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_WMA_LEN;
+	cmd.common.dec_id = audio->dec_id;
+	cmd.common.input_sampling_frequency = audio->out_sample_rate;
+
+	/*
+	 * Test done for sample with the following configuration
+	 * armdatareqthr 	= 1262
+	 * channelsdecoded 	= 1(MONO)/2(STEREO)
+	 * wmabytespersec 	= Tested with 6003 Bytes per sec
+	 * wmasamplingfreq	= 44100
+	 * wmaencoderopts	= 31
+	 */
+
+	cmd.armdatareqthr = audio->wma_config.armdatareqthr;
+	cmd.channelsdecoded = audio->wma_config.channelsdecoded;
+	cmd.wmabytespersec = audio->wma_config.wmabytespersec;
+	cmd.wmasamplingfreq = audio->wma_config.wmasamplingfreq;
+	cmd.wmaencoderopts = audio->wma_config.wmaencoderopts;
+
+	audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_routing_mode(struct audio *audio)
+{
+	struct audpp_cmd_routing_mode cmd;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	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 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;
+	refresh_cmd.buf_read_count = 0;
+
+	MM_DBG("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;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	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 int audplay_dsp_send_data_avail(struct audio *audio,
+					unsigned idx, unsigned len)
+{
+	struct audplay_cmd_bitstream_data_avail_nt2 cmd;
+
+	cmd.cmd_id		= AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2;
+	if (audio->mfield)
+		cmd.decoder_id = AUDWMA_METAFIELD_MASK |
+			(audio->out[idx].mfield_sz >> 1);
+	else
+		cmd.decoder_id		= audio->dec_id;
+	cmd.buf_ptr		= audio->out[idx].addr;
+	cmd.buf_size		= len/2;
+	cmd.partition_number	= 0;
+	/* complete writes to the input buffer */
+	wmb();
+	return audplay_send_queue0(audio, &cmd, sizeof(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) {
+			MM_DBG("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
+		 */
+
+		MM_DBG("\n"); /* Macro prints the file name and function */
+		frame = audio->out + audio->out_tail;
+		if (frame->used) {
+			BUG_ON(frame->used == 0xffffffff);
+			MM_DBG("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->buf_refresh = 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 int audwma_events_pending(struct audio *audio)
+{
+	unsigned long flags;
+	int empty;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+	empty = !list_empty(&audio->event_queue);
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+	return empty || audio->event_abort;
+}
+
+static void audwma_reset_event_queue(struct audio *audio)
+{
+	unsigned long flags;
+	struct audwma_event *drv_evt;
+	struct list_head *ptr, *next;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+	list_for_each_safe(ptr, next, &audio->event_queue) {
+		drv_evt = list_first_entry(&audio->event_queue,
+				struct audwma_event, list);
+		list_del(&drv_evt->list);
+		kfree(drv_evt);
+	}
+	list_for_each_safe(ptr, next, &audio->free_event_queue) {
+		drv_evt = list_first_entry(&audio->free_event_queue,
+				struct audwma_event, list);
+		list_del(&drv_evt->list);
+		kfree(drv_evt);
+	}
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+	return;
+}
+
+static long audwma_process_event_req(struct audio *audio, void __user *arg)
+{
+	long rc;
+	struct msm_audio_event usr_evt;
+	struct audwma_event *drv_evt = NULL;
+	int timeout;
+	unsigned long flags;
+
+	if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event)))
+		return -EFAULT;
+
+	timeout = (int) usr_evt.timeout_ms;
+
+	if (timeout > 0) {
+		rc = wait_event_interruptible_timeout(
+				audio->event_wait, audwma_events_pending(audio),
+				msecs_to_jiffies(timeout));
+		if (rc == 0)
+			return -ETIMEDOUT;
+	} else {
+		rc = wait_event_interruptible(
+			audio->event_wait, audwma_events_pending(audio));
+	}
+
+	if (rc < 0)
+		return rc;
+
+	if (audio->event_abort) {
+		audio->event_abort = 0;
+		return -ENODEV;
+	}
+
+	rc = 0;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+	if (!list_empty(&audio->event_queue)) {
+		drv_evt = list_first_entry(&audio->event_queue,
+				struct audwma_event, list);
+		list_del(&drv_evt->list);
+	}
+
+	if (drv_evt) {
+		usr_evt.event_type = drv_evt->event_type;
+		usr_evt.event_payload = drv_evt->payload;
+		list_add_tail(&drv_evt->list, &audio->free_event_queue);
+	} else
+		rc = -1;
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+	if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt)))
+		rc = -EFAULT;
+
+	return rc;
+}
+
+static int audio_enable_eq(struct audio *audio, int enable)
+{
+	if (audio->eq_enable == enable && !audio->eq_needs_commit)
+		return 0;
+
+	audio->eq_enable = enable;
+
+	if (audio->running) {
+		audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq);
+		audio->eq_needs_commit = 0;
+	}
+	return 0;
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct audio *audio = file->private_data;
+	int rc = -EINVAL;
+	unsigned long flags = 0;
+	uint16_t enable_mask;
+	int enable;
+	int prev_state;
+
+	MM_DBG("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;
+	}
+
+	switch (cmd) {
+	case AUDIO_ENABLE_AUDPP:
+		if (copy_from_user(&enable_mask, (void *) arg,
+						sizeof(enable_mask))) {
+			rc = -EFAULT;
+			break;
+		}
+
+		spin_lock_irqsave(&audio->dsp_lock, flags);
+		enable = (enable_mask & EQ_ENABLE) ? 1 : 0;
+		audio_enable_eq(audio, enable);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		rc = 0;
+		break;
+	case AUDIO_SET_VOLUME:
+		spin_lock_irqsave(&audio->dsp_lock, flags);
+		audio->vol_pan.volume = arg;
+		if (audio->running)
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		rc = 0;
+		break;
+
+	case AUDIO_SET_PAN:
+		spin_lock_irqsave(&audio->dsp_lock, flags);
+		audio->vol_pan.pan = arg;
+		if (audio->running)
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		rc = 0;
+		break;
+
+	case AUDIO_SET_EQ:
+		prev_state = audio->eq_enable;
+		audio->eq_enable = 0;
+		if (copy_from_user(&audio->eq.num_bands, (void *) arg,
+				sizeof(audio->eq) -
+				(AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) {
+			rc = -EFAULT;
+			break;
+		}
+		audio->eq_enable = prev_state;
+		audio->eq_needs_commit = 1;
+		rc = 0;
+		break;
+	}
+
+	if (-EINVAL != rc)
+		return rc;
+
+	if (cmd == AUDIO_GET_EVENT) {
+		MM_DBG("AUDIO_GET_EVENT\n");
+		if (mutex_trylock(&audio->get_event_lock)) {
+			rc = audwma_process_event_req(audio,
+					(void __user *) arg);
+			mutex_unlock(&audio->get_event_lock);
+		} else
+			rc = -EBUSY;
+		return rc;
+	}
+
+	if (cmd == AUDIO_ABORT_GET_EVENT) {
+		audio->event_abort = 1;
+		wake_up(&audio->event_wait);
+		return 0;
+	}
+
+	mutex_lock(&audio->lock);
+	switch (cmd) {
+	case AUDIO_START:
+		MM_DBG("AUDIO_START\n");
+		rc = audio_enable(audio);
+		if (!rc) {
+			rc = wait_event_interruptible_timeout(audio->wait,
+				audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+				msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+			MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc);
+
+			if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS)
+				rc = -ENODEV;
+			else
+				rc = 0;
+		}
+		break;
+	case AUDIO_STOP:
+		MM_DBG("AUDIO_STOP\n");
+		rc = audio_disable(audio);
+		audio->stopped = 1;
+		audio_ioport_reset(audio);
+		audio->stopped = 0;
+		break;
+	case AUDIO_FLUSH:
+		MM_DBG("AUDIO_FLUSH\n");
+		audio->rflush = 1;
+		audio->wflush = 1;
+		audio_ioport_reset(audio);
+		if (audio->running) {
+			audpp_flush(audio->dec_id);
+			rc = wait_event_interruptible(audio->write_wait,
+				!audio->wflush);
+			if (rc < 0) {
+				MM_ERR("AUDIO_FLUSH interrupted\n");
+				rc = -EINTR;
+			}
+		} 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->mfield = config.meta_field;
+		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.meta_field = 0;
+		config.unused[0] = 0;
+		config.unused[1] = 0;
+		config.unused[2] = 0;
+		if (copy_to_user((void *) arg, &config, sizeof(config)))
+			rc = -EFAULT;
+		else
+			rc = 0;
+
+		break;
+	}
+	case AUDIO_GET_WMA_CONFIG:{
+			if (copy_to_user((void *)arg, &audio->wma_config,
+				sizeof(audio->wma_config)))
+				rc = -EFAULT;
+			else
+				rc = 0;
+			break;
+		}
+	case AUDIO_SET_WMA_CONFIG:{
+		struct msm_audio_wma_config usr_config;
+
+		if (copy_from_user
+			(&usr_config, (void *)arg,
+			sizeof(usr_config))) {
+			rc = -EFAULT;
+			break;
+		}
+
+		audio->wma_config = usr_config;
+		rc = 0;
+		break;
+	}
+	case AUDIO_GET_PCM_CONFIG:{
+			struct msm_audio_pcm_config config;
+			config.pcm_feedback = audio->pcm_feedback;
+			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.pcm_feedback != audio->pcm_feedback) {
+				MM_ERR("Not sufficient permission to"
+					 "change the playback mode\n");
+				rc = -EACCES;
+				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)) {
+				MM_DBG("allocate PCM buffer %d\n",
+						config.buffer_count *
+						config.buffer_size);
+				audio->read_phys = pmem_kalloc(
+							config.buffer_size *
+							config.buffer_count,
+							PMEM_MEMTYPE_EBI1|
+							PMEM_ALIGNMENT_4K);
+				if (IS_ERR((void *)audio->read_phys)) {
+					rc = -ENOMEM;
+					break;
+				}
+				audio->read_data = ioremap(audio->read_phys,
+							config.buffer_size *
+							config.buffer_count);
+				if (!audio->read_data) {
+					MM_ERR("read buf alloc fail\n");
+					rc = -ENOMEM;
+					pmem_kfree(audio->read_phys);
+				} else {
+					uint8_t index;
+					uint32_t offset = 0;
+					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;
+					}
+					MM_DBG("read buf: phy addr \
+						0x%08x kernel addr 0x%08x\n",
+						audio->read_phys,
+						(int)audio->read_data);
+					rc = 0;
+				}
+			} else {
+				rc = 0;
+			}
+			break;
+		}
+	case AUDIO_PAUSE:
+		MM_DBG("AUDIO_PAUSE %ld\n", arg);
+		rc = audpp_pause(audio->dec_id, (int) arg);
+		break;
+	default:
+		rc = -EINVAL;
+	}
+	mutex_unlock(&audio->lock);
+	return rc;
+}
+
+/* Only useful in tunnel-mode */
+static int audio_fsync(struct file *file, int datasync)
+{
+	struct audio *audio = file->private_data;
+	struct buffer *frame;
+	int rc = 0;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+
+	if (!audio->running || audio->pcm_feedback) {
+		rc = -EINVAL;
+		goto done_nolock;
+	}
+
+	mutex_lock(&audio->write_lock);
+
+	rc = wait_event_interruptible(audio->write_wait,
+		(!audio->out[0].used &&
+		!audio->out[1].used &&
+		audio->out_needed) || audio->wflush);
+
+	if (rc < 0)
+		goto done;
+	else if (audio->wflush) {
+		rc = -EBUSY;
+		goto done;
+	}
+
+	if (audio->reserved) {
+		MM_DBG("send reserved byte\n");
+		frame = audio->out + audio->out_tail;
+		((char *) frame->data)[0] = audio->rsv_byte;
+		((char *) frame->data)[1] = 0;
+		frame->used = 2;
+		audplay_send_data(audio, 0);
+
+		rc = wait_event_interruptible(audio->write_wait,
+			(!audio->out[0].used &&
+			!audio->out[1].used &&
+			audio->out_needed) || audio->wflush);
+
+		if (rc < 0)
+			goto done;
+		else if (audio->wflush) {
+			rc = -EBUSY;
+			goto done;
+		}
+	}
+
+	/* pcm dmamiss message is sent continously
+	 * when decoder is starved so no race
+	 * condition concern
+	 */
+	audio->teos = 0;
+
+	rc = wait_event_interruptible(audio->write_wait,
+		audio->teos || audio->wflush);
+
+	if (audio->wflush)
+		rc = -EBUSY;
+
+done:
+	mutex_unlock(&audio->write_lock);
+done_nolock:
+	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);
+	MM_DBG("%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 */
+			MM_DBG("audio_read: no partial frame done reading\n");
+			break;
+		} else {
+			MM_DBG("audio_read: read from in[%d]\n",
+					audio->read_next);
+			/* order reads from the output buffer */
+			rmb();
+			if (copy_to_user
+			    (buf, audio->in[audio->read_next].data,
+			     audio->in[audio->read_next].used)) {
+				MM_ERR("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;
+			break;	/* Force to exit while loop
+				 * to prevent output thread
+				 * sleep too long if data is
+				 * not ready at this moment.
+				 */
+		}
+	}
+
+	/* 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;
+		MM_DBG("kick start pcm feedback again\n");
+		audplay_buffer_refresh(audio);
+	}
+
+	mutex_unlock(&audio->read_lock);
+
+	if (buf > start)
+		rc = buf - start;
+
+	MM_DBG("read %d bytes\n", rc);
+	return rc;
+}
+
+static int audwma_process_eos(struct audio *audio,
+		const char __user *buf_start, unsigned short mfield_size)
+{
+	int rc = 0;
+	struct buffer *frame;
+	char *buf_ptr;
+
+	if (audio->reserved) {
+		MM_DBG("flush reserve byte\n");
+		frame = audio->out + audio->out_head;
+		buf_ptr = frame->data;
+		rc = wait_event_interruptible(audio->write_wait,
+				(frame->used == 0)
+				|| (audio->stopped)
+				|| (audio->wflush));
+		if (rc < 0)
+			goto done;
+		if (audio->stopped || audio->wflush) {
+			rc = -EBUSY;
+			goto done;
+		}
+
+		buf_ptr[0] = audio->rsv_byte;
+		buf_ptr[1] = 0;
+		audio->out_head ^= 1;
+		frame->mfield_sz = 0;
+		frame->used = 2;
+		audio->reserved = 0;
+		audplay_send_data(audio, 0);
+	}
+
+	frame = audio->out + audio->out_head;
+
+	rc = wait_event_interruptible(audio->write_wait,
+		(audio->out_needed &&
+		audio->out[0].used == 0 &&
+		audio->out[1].used == 0)
+		|| (audio->stopped)
+		|| (audio->wflush));
+
+	if (rc < 0)
+		goto done;
+	if (audio->stopped || audio->wflush) {
+		rc = -EBUSY;
+		goto done;
+	}
+
+	if (copy_from_user(frame->data, buf_start, mfield_size)) {
+		rc = -EFAULT;
+		goto done;
+	}
+
+	frame->mfield_sz = mfield_size;
+	audio->out_head ^= 1;
+	frame->used = mfield_size;
+	audplay_send_data(audio, 0);
+done:
+	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, eos_condition = AUDWMA_EOS_NONE;
+	unsigned dsize;
+	unsigned short mfield_size = 0;
+
+	MM_DBG("cnt=%d\n", count);
+
+	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->mfield) {
+			if (buf == start) {
+				/* Processing beginning of user buffer */
+				if (__get_user(mfield_size,
+					(unsigned short __user *) buf)) {
+					rc = -EFAULT;
+					break;
+				} else  if (mfield_size > count) {
+					rc = -EINVAL;
+					break;
+				}
+				MM_DBG("audio_write: mf offset_val %x\n",
+						mfield_size);
+				if (copy_from_user(cpy_ptr, buf, mfield_size)) {
+					rc = -EFAULT;
+					break;
+				}
+				/* Check if EOS flag is set and buffer has
+				 * contains just meta field
+				 */
+				if (cpy_ptr[AUDWMA_EOS_FLG_OFFSET] &
+						 AUDWMA_EOS_FLG_MASK) {
+					MM_DBG("audio_write: EOS SET\n");
+					eos_condition = AUDWMA_EOS_SET;
+					if (mfield_size == count) {
+						buf += mfield_size;
+						break;
+					} else
+						cpy_ptr[AUDWMA_EOS_FLG_OFFSET]
+							&= ~AUDWMA_EOS_FLG_MASK;
+				}
+				cpy_ptr += mfield_size;
+				count -= mfield_size;
+				dsize += mfield_size;
+				buf += mfield_size;
+			} else {
+				mfield_size = 0;
+				MM_DBG("audio_write: continuous buffer\n");
+			}
+			frame->mfield_sz = mfield_size;
+		}
+
+		if (audio->reserved) {
+			MM_DBG("append reserved byte %x\n", audio->rsv_byte);
+			*cpy_ptr = audio->rsv_byte;
+			xfer = (count > ((frame->size - mfield_size) - 1)) ?
+				(frame->size - mfield_size) - 1 : count;
+			cpy_ptr++;
+			dsize += 1;
+			audio->reserved = 0;
+		} else
+			xfer = (count > (frame->size - mfield_size)) ?
+				(frame->size - mfield_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];
+			MM_DBG("odd length buf reserve last byte %x\n",
+					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);
+		}
+	}
+	if (eos_condition == AUDWMA_EOS_SET)
+		rc = audwma_process_eos(audio, start, mfield_size);
+	mutex_unlock(&audio->write_lock);
+	if (!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;
+
+	MM_INFO("audio instance 0x%08x freeing\n", (int)audio);
+	mutex_lock(&audio->lock);
+	audio_disable(audio);
+	if (audio->rmt_resource_released == 0)
+		rmt_put_resource(audio);
+	audio_flush(audio);
+	audio_flush_pcm_buf(audio);
+	msm_adsp_put(audio->audplay);
+	audpp_adec_free(audio->dec_id);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	unregister_early_suspend(&audio->suspend_ctl.node);
+#endif
+	audio->event_abort = 1;
+	wake_up(&audio->event_wait);
+	audwma_reset_event_queue(audio);
+	iounmap(audio->data);
+	pmem_kfree(audio->phys);
+	if (audio->read_data) {
+		iounmap(audio->read_data);
+		pmem_kfree(audio->read_phys);
+	}
+	mutex_unlock(&audio->lock);
+#ifdef CONFIG_DEBUG_FS
+	if (audio->dentry)
+		debugfs_remove(audio->dentry);
+#endif
+	kfree(audio);
+	return 0;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void audwma_post_event(struct audio *audio, int type,
+		union msm_audio_event_payload payload)
+{
+	struct audwma_event *e_node = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+
+	if (!list_empty(&audio->free_event_queue)) {
+		e_node = list_first_entry(&audio->free_event_queue,
+				struct audwma_event, list);
+		list_del(&e_node->list);
+	} else {
+		e_node = kmalloc(sizeof(struct audwma_event), GFP_ATOMIC);
+		if (!e_node) {
+			MM_ERR("No mem to post event %d\n", type);
+			spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+			return;
+		}
+	}
+
+	e_node->event_type = type;
+	e_node->payload = payload;
+
+	list_add_tail(&e_node->list, &audio->event_queue);
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+	wake_up(&audio->event_wait);
+}
+
+static void audwma_suspend(struct early_suspend *h)
+{
+	struct audwma_suspend_ctl *ctl =
+		container_of(h, struct audwma_suspend_ctl, node);
+	union msm_audio_event_payload payload;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	audwma_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload);
+}
+
+static void audwma_resume(struct early_suspend *h)
+{
+	struct audwma_suspend_ctl *ctl =
+		container_of(h, struct audwma_suspend_ctl, node);
+	union msm_audio_event_payload payload;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	audwma_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload);
+}
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t audwma_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t audwma_debug_read(struct file *file, char __user *buf,
+			  size_t count, loff_t *ppos)
+{
+	const int debug_bufmax = 4096;
+	static char buffer[4096];
+	int n = 0, i;
+	struct audio *audio = file->private_data;
+
+	mutex_lock(&audio->lock);
+	n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "enabled %d\n", audio->enabled);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "stopped %d\n", audio->stopped);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "pcm_feedback %d\n", audio->pcm_feedback);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "out_buf_sz %d\n", audio->out[0].size);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "pcm_buf_count %d \n", audio->pcm_buf_count);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "pcm_buf_sz %d \n", audio->in[0].size);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "volume %x \n", audio->vol_pan.volume);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "sample rate %d \n", audio->out_sample_rate);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+		"channel mode %d \n", audio->out_channel_mode);
+	mutex_unlock(&audio->lock);
+	/* Following variables are only useful for debugging when
+	 * when playback halts unexpectedly. Thus, no mutual exclusion
+	 * enforced
+	 */
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "wflush %d\n", audio->wflush);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "rflush %d\n", audio->rflush);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "running %d \n", audio->running);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"dec state %d \n", audio->dec_state);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "out_needed %d \n", audio->out_needed);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "out_head %d \n", audio->out_head);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "out_tail %d \n", audio->out_tail);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "out[0].used %d \n", audio->out[0].used);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "out[1].used %d \n", audio->out[1].used);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "buffer_refresh %d \n", audio->buf_refresh);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "read_next %d \n", audio->read_next);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "fill_next %d \n", audio->fill_next);
+	for (i = 0; i < audio->pcm_buf_count; i++)
+		n += scnprintf(buffer + n, debug_bufmax - n,
+			"in[%d].size %d \n", i, audio->in[i].used);
+	buffer[n] = 0;
+	return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+
+static const struct file_operations audwma_debug_fops = {
+	.read = audwma_debug_read,
+	.open = audwma_debug_open,
+};
+#endif
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+	struct audio *audio = NULL;
+	int rc, dec_attrb, decid, i;
+	unsigned pmem_sz = DMASZ_MAX;
+	struct audwma_event *e_node = NULL;
+#ifdef CONFIG_DEBUG_FS
+	/* 4 bytes represents decoder number, 1 byte for terminate string */
+	char name[sizeof "msm_wma_" + 5];
+#endif
+
+	/* Allocate Mem for audio instance */
+	audio = kzalloc(sizeof(struct audio), GFP_KERNEL);
+	if (!audio) {
+		MM_ERR("no memory to allocate audio instance \n");
+		rc = -ENOMEM;
+		goto done;
+	}
+	MM_INFO("audio instance 0x%08x created\n", (int)audio);
+
+	/* Allocate the decoder */
+	dec_attrb = AUDDEC_DEC_WMA;
+	if ((file->f_mode & FMODE_WRITE) &&
+			(file->f_mode & FMODE_READ)) {
+		dec_attrb |= MSM_AUD_MODE_NONTUNNEL;
+		audio->pcm_feedback = NON_TUNNEL_MODE_PLAYBACK;
+	} else if ((file->f_mode & FMODE_WRITE) &&
+			!(file->f_mode & FMODE_READ)) {
+		dec_attrb |= MSM_AUD_MODE_TUNNEL;
+		audio->pcm_feedback = TUNNEL_MODE_PLAYBACK;
+	} else {
+		kfree(audio);
+		rc = -EACCES;
+		goto done;
+	}
+
+	decid = audpp_adec_alloc(dec_attrb, &audio->module_name,
+			&audio->queue_id);
+
+	if (decid < 0) {
+		MM_ERR("No free decoder available, freeing instance 0x%08x\n",
+				(int)audio);
+		rc = -ENODEV;
+		kfree(audio);
+		goto done;
+	}
+	audio->dec_id = decid & MSM_AUD_DECODER_MASK;
+
+	while (pmem_sz >= DMASZ_MIN) {
+		MM_DBG("pmemsz = %d\n", pmem_sz);
+		audio->phys = pmem_kalloc(pmem_sz, PMEM_MEMTYPE_EBI1|
+					PMEM_ALIGNMENT_4K);
+		if (!IS_ERR((void *)audio->phys)) {
+			audio->data = ioremap(audio->phys, pmem_sz);
+			if (!audio->data) {
+				MM_ERR("could not allocate write buffers, \
+						freeing instance 0x%08x\n",
+						(int)audio);
+				rc = -ENOMEM;
+				pmem_kfree(audio->phys);
+				audpp_adec_free(audio->dec_id);
+				kfree(audio);
+				goto done;
+			}
+			MM_DBG("write buf: phy addr 0x%08x kernel addr \
+				0x%08x\n", audio->phys, (int)audio->data);
+			break;
+		} else if (pmem_sz == DMASZ_MIN) {
+			MM_ERR("could not allocate write buffers, freeing \
+					instance 0x%08x\n", (int)audio);
+			rc = -ENOMEM;
+			audpp_adec_free(audio->dec_id);
+			kfree(audio);
+			goto done;
+		} else
+		pmem_sz >>= 1;
+	}
+	audio->out_dma_sz = pmem_sz;
+
+	if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK) {
+		rc = audmgr_open(&audio->audmgr);
+		if (rc) {
+			MM_ERR("audmgr open failed, freeing instance \
+					0x%08x\n", (int)audio);
+			goto err;
+		}
+	}
+
+	rc = msm_adsp_get(audio->module_name, &audio->audplay,
+			&audplay_adsp_ops_wma, audio);
+	if (rc) {
+		MM_ERR("failed to get %s module, freeing instance 0x%08x\n",
+				audio->module_name, (int)audio);
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			audmgr_close(&audio->audmgr);
+		goto err;
+	}
+
+	rc = rmt_get_resource(audio);
+	if (rc) {
+		MM_ERR("ADSP resources are not available for WMA session \
+			 0x%08x on decoder: %d\n", (int)audio, audio->dec_id);
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			audmgr_close(&audio->audmgr);
+		msm_adsp_put(audio->audplay);
+		goto err;
+	}
+
+	mutex_init(&audio->lock);
+	mutex_init(&audio->write_lock);
+	mutex_init(&audio->read_lock);
+	mutex_init(&audio->get_event_lock);
+	spin_lock_init(&audio->dsp_lock);
+	init_waitqueue_head(&audio->write_wait);
+	init_waitqueue_head(&audio->read_wait);
+	INIT_LIST_HEAD(&audio->free_event_queue);
+	INIT_LIST_HEAD(&audio->event_queue);
+	init_waitqueue_head(&audio->wait);
+	init_waitqueue_head(&audio->event_wait);
+	spin_lock_init(&audio->event_queue_lock);
+
+	audio->out[0].data = audio->data + 0;
+	audio->out[0].addr = audio->phys + 0;
+	audio->out[0].size = audio->out_dma_sz >> 1;
+
+	audio->out[1].data = audio->data + audio->out[0].size;
+	audio->out[1].addr = audio->phys + audio->out[0].size;
+	audio->out[1].size = audio->out[0].size;
+
+	audio->wma_config.armdatareqthr =  1262;
+	audio->wma_config.channelsdecoded = 2;
+	audio->wma_config.wmabytespersec = 6003;
+	audio->wma_config.wmasamplingfreq = 44100;
+	audio->wma_config.wmaencoderopts = 31;
+
+	audio->out_sample_rate = 44100;
+	audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V;
+
+	audio->vol_pan.volume = 0x2000;
+
+	audio_flush(audio);
+
+	file->private_data = audio;
+	audio->opened = 1;
+#ifdef CONFIG_DEBUG_FS
+	snprintf(name, sizeof name, "msm_wma_%04x", audio->dec_id);
+	audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+				NULL, (void *) audio,
+				&audwma_debug_fops);
+
+	if (IS_ERR(audio->dentry))
+		MM_DBG("debugfs_create_file failed\n");
+#endif
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
+	audio->suspend_ctl.node.resume = audwma_resume;
+	audio->suspend_ctl.node.suspend = audwma_suspend;
+	audio->suspend_ctl.audio = audio;
+	register_early_suspend(&audio->suspend_ctl.node);
+#endif
+	for (i = 0; i < AUDWMA_EVENT_NUM; i++) {
+		e_node = kmalloc(sizeof(struct audwma_event), GFP_KERNEL);
+		if (e_node)
+			list_add_tail(&e_node->list, &audio->free_event_queue);
+		else {
+			MM_ERR("event pkt alloc failed\n");
+			break;
+		}
+	}
+done:
+	return rc;
+err:
+	iounmap(audio->data);
+	pmem_kfree(audio->phys);
+	audpp_adec_free(audio->dec_id);
+	kfree(audio);
+	return rc;
+}
+
+static const struct file_operations audio_wma_fops = {
+	.owner		= THIS_MODULE,
+	.open		= audio_open,
+	.release	= audio_release,
+	.read 		= audio_read,
+	.write		= audio_write,
+	.unlocked_ioctl	= audio_ioctl,
+	.fsync 		= audio_fsync,
+};
+
+struct miscdevice audio_wma_misc = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "msm_wma",
+	.fops	= &audio_wma_fops,
+};
+
+static int __init audio_init(void)
+{
+	return misc_register(&audio_wma_misc);
+}
+
+device_initcall(audio_init);
diff --git a/arch/arm/mach-msm/qdsp5/audio_wmapro.c b/arch/arm/mach-msm/qdsp5/audio_wmapro.c
new file mode 100644
index 0000000..b5ddf5f
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audio_wmapro.c
@@ -0,0 +1,1748 @@
+/* audio_wmapro.c - wmapro audio decoder driver
+ *
+ * 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
+ * Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved.
+ *
+ * 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/debugfs.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/earlysuspend.h>
+#include <linux/android_pmem.h>
+#include <linux/slab.h>
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+
+#include <linux/msm_audio.h>
+
+#include "audmgr.h"
+
+#include <linux/msm_audio_wmapro.h>
+#include <mach/qdsp5/qdsp5audppcmdi.h>
+#include <mach/qdsp5/qdsp5audppmsg.h>
+#include <mach/qdsp5/qdsp5audplaycmdi.h>
+#include <mach/qdsp5/qdsp5audplaymsg.h>
+#include <mach/qdsp5/qdsp5rmtcmdi.h>
+#include <mach/debug_mm.h>
+
+/* Size must be power of 2 */
+#define BUFSZ_MAX	8206	/* Includes meta in size */
+#define BUFSZ_MIN 	2062	/* Includes meta in size */
+#define DMASZ_MAX 	(BUFSZ_MAX * 2)
+#define DMASZ_MIN 	(BUFSZ_MIN * 2)
+
+#define AUDPLAY_INVALID_READ_PTR_OFFSET	0xFFFF
+#define AUDDEC_DEC_WMAPRO 13
+
+#define PCM_BUFSZ_MIN 	8216 	/* Hold one stereo WMAPRO frame and meta out*/
+#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
+
+#define AUDWMAPRO_METAFIELD_MASK 0xFFFF0000
+#define AUDWMAPRO_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */
+#define AUDWMAPRO_EOS_FLG_MASK 0x01
+#define AUDWMAPRO_EOS_NONE 0x0 /* No EOS detected */
+#define AUDWMAPRO_EOS_SET 0x1 /* EOS set in meta field */
+
+#define AUDWMAPRO_EVENT_NUM 10 /* Default no. of pre-allocated event packets */
+
+struct buffer {
+	void *data;
+	unsigned size;
+	unsigned used;		/* Input usage actual DSP produced PCM size  */
+	unsigned addr;
+	unsigned short mfield_sz; /*only useful for data has meta field */
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+struct audwmapro_suspend_ctl {
+	struct early_suspend node;
+	struct audio *audio;
+};
+#endif
+
+struct audwmapro_event{
+	struct list_head list;
+	int event_type;
+	union msm_audio_event_payload payload;
+};
+
+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 */
+	int32_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_wmapro_config wmapro_config;
+	struct audmgr audmgr;
+
+	/* data allocated for various buffers */
+	char *data;
+	int32_t phys; /* physical address of write buffer */
+
+	int mfield; /* meta field embedded in data */
+	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 rmt_resource_released;
+	int teos; /* valid only if tunnel mode & no data left for decoder */
+	enum msm_aud_decoder_state dec_state;	/* Represents decoder state */
+	int reserved; /* A byte is being reserved */
+	char rsv_byte; /* Handle odd length user data */
+
+	const char *module_name;
+	unsigned queue_id;
+	uint16_t dec_id;
+	uint32_t read_ptr_offset;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	struct audwmapro_suspend_ctl suspend_ctl;
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *dentry;
+#endif
+
+	wait_queue_head_t wait;
+	struct list_head free_event_queue;
+	struct list_head event_queue;
+	wait_queue_head_t event_wait;
+	spinlock_t event_queue_lock;
+	struct mutex get_event_lock;
+	int event_abort;
+
+	int eq_enable;
+	int eq_needs_commit;
+	audpp_cmd_cfg_object_params_eqalizer eq;
+	audpp_cmd_cfg_object_params_volume vol_pan;
+};
+
+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);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void audwmapro_post_event(struct audio *audio, int type,
+		union msm_audio_event_payload payload);
+#endif
+
+static int rmt_put_resource(struct audio *audio)
+{
+	struct aud_codec_config_cmd cmd;
+	unsigned short client_idx;
+
+	cmd.cmd_id = RM_CMD_AUD_CODEC_CFG;
+	cmd.client_id = RM_AUD_CLIENT_ID;
+	cmd.task_id = audio->dec_id;
+	cmd.enable = RMT_DISABLE;
+	cmd.dec_type = AUDDEC_DEC_WMAPRO;
+	client_idx = ((cmd.client_id << 8) | cmd.task_id);
+
+	return put_adsp_resource(client_idx, &cmd, sizeof(cmd));
+}
+
+static int rmt_get_resource(struct audio *audio)
+{
+	struct aud_codec_config_cmd cmd;
+	unsigned short client_idx;
+
+	cmd.cmd_id = RM_CMD_AUD_CODEC_CFG;
+	cmd.client_id = RM_AUD_CLIENT_ID;
+	cmd.task_id = audio->dec_id;
+	cmd.enable = RMT_ENABLE;
+	cmd.dec_type = AUDDEC_DEC_WMAPRO;
+	client_idx = ((cmd.client_id << 8) | cmd.task_id);
+
+	return get_adsp_resource(client_idx, &cmd, sizeof(cmd));
+}
+
+/* must be called with audio->lock held */
+static int audio_enable(struct audio *audio)
+{
+	struct audmgr_config cfg;
+	int rc;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	if (audio->enabled)
+		return 0;
+
+	if (audio->rmt_resource_released == 1) {
+		audio->rmt_resource_released = 0;
+		rc = rmt_get_resource(audio);
+		if (rc) {
+			MM_ERR("ADSP resources are not available for WMAPRO \
+				session 0x%08x on decoder: %d\n Ignoring \
+				error and going ahead with the playback\n",
+				(int)audio, audio->dec_id);
+		}
+	}
+
+	audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+	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_WMA;
+	cfg.snd_method = RPC_SND_METHOD_MIDI;
+
+	rc = audmgr_enable(&audio->audmgr, &cfg);
+	if (rc < 0)
+		return rc;
+
+	if (msm_adsp_enable(audio->audplay)) {
+		MM_ERR("msm_adsp_enable(audplay) failed\n");
+		audmgr_disable(&audio->audmgr);
+		return -ENODEV;
+	}
+
+	if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) {
+		MM_ERR("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)
+{
+	int rc = 0;
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	if (audio->enabled) {
+		audio->enabled = 0;
+		audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+		auddec_dsp_config(audio, 0);
+		rc = wait_event_interruptible_timeout(audio->wait,
+				audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+				msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+		if (rc == 0)
+			rc = -ETIMEDOUT;
+		else if (audio->dec_state != MSM_AUD_DECODER_STATE_CLOSE)
+			rc = -EFAULT;
+		else
+			rc = 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;
+		rmt_put_resource(audio);
+		audio->rmt_resource_released = 1;
+	}
+	return rc;
+}
+
+/* ------------------- 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]) {
+			MM_DBG("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 {
+			MM_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 {
+		MM_DBG("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));
+
+	MM_DBG("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;
+
+	case ADSP_MESSAGE_ID:
+		MM_DBG("Received ADSP event: module enable(audplaytask)\n");
+		break;
+
+	default:
+		MM_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: {
+				uint16_t reason = msg[2];
+				MM_DBG("decoder status:sleep reason = \
+						0x%04x\n", reason);
+				if ((reason == AUDPP_MSG_REASON_MEM)
+					|| (reason ==
+					AUDPP_MSG_REASON_NODECODER)) {
+					audio->dec_state =
+						MSM_AUD_DECODER_STATE_FAILURE;
+					wake_up(&audio->wait);
+				} else if (reason == AUDPP_MSG_REASON_NONE) {
+					/* decoder is in disable state */
+					audio->dec_state =
+						MSM_AUD_DECODER_STATE_CLOSE;
+					wake_up(&audio->wait);
+				}
+				break;
+			}
+			case AUDPP_DEC_STATUS_INIT:
+				MM_DBG("decoder status: init\n");
+				if (audio->pcm_feedback)
+					audpp_cmd_cfg_routing_mode(audio);
+				else
+					audpp_cmd_cfg_adec_params(audio);
+				break;
+
+			case AUDPP_DEC_STATUS_CFG:
+				MM_DBG("decoder status: cfg\n");
+				break;
+			case AUDPP_DEC_STATUS_PLAY:
+				MM_DBG("decoder status: play\n");
+				if (audio->pcm_feedback) {
+					audplay_config_hostpcm(audio);
+					audplay_buffer_refresh(audio);
+				}
+				audio->dec_state =
+					MSM_AUD_DECODER_STATE_SUCCESS;
+				wake_up(&audio->wait);
+				break;
+			default:
+				MM_ERR("unknown decoder status\n");
+			}
+			break;
+		}
+	case AUDPP_MSG_CFG_MSG:
+		if (msg[0] == AUDPP_MSG_ENA_ENA) {
+			MM_DBG("CFG_MSG ENABLE\n");
+			auddec_dsp_config(audio, 1);
+			audio->out_needed = 0;
+			audio->running = 1;
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan);
+			audpp_dsp_set_eq(audio->dec_id,	audio->eq_enable,
+								&audio->eq);
+			audpp_avsync(audio->dec_id, 22050);
+		} else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+			MM_DBG("CFG_MSG DISABLE\n");
+			audpp_avsync(audio->dec_id, 0);
+			audio->running = 0;
+		} else {
+			MM_DBG("CFG_MSG %d?\n", msg[0]);
+		}
+		break;
+	case AUDPP_MSG_ROUTING_ACK:
+		MM_DBG("ROUTING_ACK mode=%d\n", msg[1]);
+		audpp_cmd_cfg_adec_params(audio);
+		break;
+
+	case AUDPP_MSG_FLUSH_ACK:
+		MM_DBG("FLUSH_ACK\n");
+		audio->wflush = 0;
+		audio->rflush = 0;
+		wake_up(&audio->write_wait);
+		if (audio->pcm_feedback)
+			audplay_buffer_refresh(audio);
+	case AUDPP_MSG_PCMDMAMISSED:
+		MM_DBG("PCMDMAMISSED\n");
+		audio->teos = 1;
+		wake_up(&audio->write_wait);
+		break;
+
+	default:
+		MM_ERR("UNKNOWN (%d)\n", id);
+	}
+
+}
+
+static struct msm_adsp_ops audplay_adsp_ops_wmapro = {
+	.event = audplay_dsp_event,
+};
+
+#define audplay_send_queue0(audio, cmd, len) \
+	msm_adsp_write(audio->audplay, audio->queue_id, \
+			cmd, len)
+
+static int auddec_dsp_config(struct audio *audio, int enable)
+{
+	u16 cfg_dec_cmd[AUDPP_CMD_CFG_DEC_TYPE_LEN / sizeof(unsigned short)];
+
+	memset(cfg_dec_cmd, 0, sizeof(cfg_dec_cmd));
+	cfg_dec_cmd[0] = AUDPP_CMD_CFG_DEC_TYPE;
+	if (enable)
+		cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC |
+			AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_WMAPRO;
+	else
+		cfg_dec_cmd[1 + audio->dec_id] = AUDPP_CMD_UPDATDE_CFG_DEC |
+			AUDPP_CMD_DIS_DEC_V;
+
+	return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd));
+}
+
+static void audpp_cmd_cfg_adec_params(struct audio *audio)
+{
+	struct audpp_cmd_cfg_adec_params_wmapro cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
+	cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_WMAPRO_LEN;
+	cmd.common.dec_id = audio->dec_id;
+	cmd.common.input_sampling_frequency = audio->out_sample_rate;
+
+	cmd.armdatareqthr = audio->wmapro_config.armdatareqthr;
+	cmd.numchannels = audio->wmapro_config.numchannels;
+	cmd.validbitspersample = audio->wmapro_config.validbitspersample;
+	cmd.formattag = audio->wmapro_config.formattag;
+	cmd.samplingrate = audio->wmapro_config.samplingrate;
+	cmd.avgbytespersecond = audio->wmapro_config.avgbytespersecond;
+	cmd.asfpacketlength = audio->wmapro_config.asfpacketlength;
+	cmd.channelmask = audio->wmapro_config.channelmask;
+	cmd.encodeopt = audio->wmapro_config.encodeopt;
+	cmd.advancedencodeopt = audio->wmapro_config.advancedencodeopt;
+	cmd.advancedencodeopt2 = audio->wmapro_config.advancedencodeopt2;
+
+	audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+static void audpp_cmd_cfg_routing_mode(struct audio *audio)
+{
+	struct audpp_cmd_routing_mode cmd;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	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 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;
+	refresh_cmd.buf_read_count = 0;
+
+	MM_DBG("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;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	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 int audplay_dsp_send_data_avail(struct audio *audio,
+					unsigned idx, unsigned len)
+{
+	struct audplay_cmd_bitstream_data_avail_nt2 cmd;
+
+	cmd.cmd_id		= AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2;
+	if (audio->mfield)
+		cmd.decoder_id = AUDWMAPRO_METAFIELD_MASK |
+			(audio->out[idx].mfield_sz >> 1);
+	else
+		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_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) {
+			MM_DBG("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
+		 */
+
+		MM_DBG("\n"); /* Macro prints the file name and function */
+		frame = audio->out + audio->out_tail;
+		if (frame->used) {
+			BUG_ON(frame->used == 0xffffffff);
+			MM_DBG("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->buf_refresh = 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 int audwmapro_events_pending(struct audio *audio)
+{
+	unsigned long flags;
+	int empty;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+	empty = !list_empty(&audio->event_queue);
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+	return empty || audio->event_abort;
+}
+
+static void audwmapro_reset_event_queue(struct audio *audio)
+{
+	unsigned long flags;
+	struct audwmapro_event *drv_evt;
+	struct list_head *ptr, *next;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+	list_for_each_safe(ptr, next, &audio->event_queue) {
+		drv_evt = list_first_entry(&audio->event_queue,
+				struct audwmapro_event, list);
+		list_del(&drv_evt->list);
+		kfree(drv_evt);
+	}
+	list_for_each_safe(ptr, next, &audio->free_event_queue) {
+		drv_evt = list_first_entry(&audio->free_event_queue,
+				struct audwmapro_event, list);
+		list_del(&drv_evt->list);
+		kfree(drv_evt);
+	}
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+	return;
+}
+
+static long audwmapro_process_event_req(struct audio *audio, void __user *arg)
+{
+	long rc;
+	struct msm_audio_event usr_evt;
+	struct audwmapro_event *drv_evt = NULL;
+	int timeout;
+	unsigned long flags;
+
+	if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event)))
+		return -EFAULT;
+
+	timeout = (int) usr_evt.timeout_ms;
+
+	if (timeout > 0) {
+		rc = wait_event_interruptible_timeout(audio->event_wait,
+				audwmapro_events_pending(audio),
+				msecs_to_jiffies(timeout));
+		if (rc == 0)
+			return -ETIMEDOUT;
+	} else {
+		rc = wait_event_interruptible(
+			audio->event_wait, audwmapro_events_pending(audio));
+	}
+
+	if (rc < 0)
+		return rc;
+
+	if (audio->event_abort) {
+		audio->event_abort = 0;
+		return -ENODEV;
+	}
+
+	rc = 0;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+	if (!list_empty(&audio->event_queue)) {
+		drv_evt = list_first_entry(&audio->event_queue,
+				struct audwmapro_event, list);
+		list_del(&drv_evt->list);
+	}
+
+	if (drv_evt) {
+		usr_evt.event_type = drv_evt->event_type;
+		usr_evt.event_payload = drv_evt->payload;
+		list_add_tail(&drv_evt->list, &audio->free_event_queue);
+	} else
+		rc = -1;
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+	if (!rc && copy_to_user(arg, &usr_evt, sizeof(usr_evt)))
+		rc = -EFAULT;
+
+	return rc;
+}
+
+static int audio_enable_eq(struct audio *audio, int enable)
+{
+	if (audio->eq_enable == enable && !audio->eq_needs_commit)
+		return 0;
+
+	audio->eq_enable = enable;
+
+	if (audio->running) {
+		audpp_dsp_set_eq(audio->dec_id, enable, &audio->eq);
+		audio->eq_needs_commit = 0;
+	}
+	return 0;
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct audio *audio = file->private_data;
+	int rc = -EINVAL;
+	unsigned long flags = 0;
+	uint16_t enable_mask;
+	int enable;
+	int prev_state;
+
+	MM_DBG("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;
+	}
+
+	switch (cmd) {
+	case AUDIO_ENABLE_AUDPP:
+		if (copy_from_user(&enable_mask, (void *) arg,
+						sizeof(enable_mask))) {
+			rc = -EFAULT;
+			break;
+		}
+
+		spin_lock_irqsave(&audio->dsp_lock, flags);
+		enable = (enable_mask & EQ_ENABLE) ? 1 : 0;
+		audio_enable_eq(audio, enable);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		rc = 0;
+		break;
+	case AUDIO_SET_VOLUME:
+		spin_lock_irqsave(&audio->dsp_lock, flags);
+		audio->vol_pan.volume = arg;
+		if (audio->running)
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		rc = 0;
+		break;
+
+	case AUDIO_SET_PAN:
+		spin_lock_irqsave(&audio->dsp_lock, flags);
+		audio->vol_pan.pan = arg;
+		if (audio->running)
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		rc = 0;
+		break;
+
+	case AUDIO_SET_EQ:
+		prev_state = audio->eq_enable;
+		audio->eq_enable = 0;
+		if (copy_from_user(&audio->eq.num_bands, (void *) arg,
+				sizeof(audio->eq) -
+				(AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) {
+			rc = -EFAULT;
+			break;
+		}
+		audio->eq_enable = prev_state;
+		audio->eq_needs_commit = 1;
+		rc = 0;
+		break;
+	}
+
+	if (-EINVAL != rc)
+		return rc;
+
+	if (cmd == AUDIO_GET_EVENT) {
+		MM_DBG("AUDIO_GET_EVENT\n");
+		if (mutex_trylock(&audio->get_event_lock)) {
+			rc = audwmapro_process_event_req(audio,
+					(void __user *) arg);
+			mutex_unlock(&audio->get_event_lock);
+		} else
+			rc = -EBUSY;
+		return rc;
+	}
+
+	if (cmd == AUDIO_ABORT_GET_EVENT) {
+		audio->event_abort = 1;
+		wake_up(&audio->event_wait);
+		return 0;
+	}
+
+	mutex_lock(&audio->lock);
+	switch (cmd) {
+	case AUDIO_START:
+		MM_DBG("AUDIO_START\n");
+		rc = audio_enable(audio);
+		if (!rc) {
+			rc = wait_event_interruptible_timeout(audio->wait,
+				audio->dec_state != MSM_AUD_DECODER_STATE_NONE,
+				msecs_to_jiffies(MSM_AUD_DECODER_WAIT_MS));
+			MM_INFO("dec_state %d rc = %d\n", audio->dec_state, rc);
+
+			if (audio->dec_state != MSM_AUD_DECODER_STATE_SUCCESS)
+				rc = -ENODEV;
+			else
+				rc = 0;
+		}
+		break;
+	case AUDIO_STOP:
+		MM_DBG("AUDIO_STOP\n");
+		rc = audio_disable(audio);
+		audio->stopped = 1;
+		audio_ioport_reset(audio);
+		audio->stopped = 0;
+		break;
+	case AUDIO_FLUSH:
+		MM_DBG("AUDIO_FLUSH\n");
+		audio->rflush = 1;
+		audio->wflush = 1;
+		audio_ioport_reset(audio);
+		if (audio->running) {
+			audpp_flush(audio->dec_id);
+			rc = wait_event_interruptible(audio->write_wait,
+				!audio->wflush);
+			if (rc < 0) {
+				MM_ERR("AUDIO_FLUSH interrupted\n");
+				rc = -EINTR;
+			}
+		} 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->mfield = config.meta_field;
+		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.meta_field = 0;
+		config.unused[0] = 0;
+		config.unused[1] = 0;
+		config.unused[2] = 0;
+		if (copy_to_user((void *) arg, &config, sizeof(config)))
+			rc = -EFAULT;
+		else
+			rc = 0;
+
+		break;
+	}
+	case AUDIO_GET_WMAPRO_CONFIG:{
+			if (copy_to_user((void *)arg, &audio->wmapro_config,
+				sizeof(audio->wmapro_config)))
+				rc = -EFAULT;
+			else
+				rc = 0;
+			break;
+		}
+	case AUDIO_SET_WMAPRO_CONFIG:{
+		struct msm_audio_wmapro_config usr_config;
+
+		if (copy_from_user
+			(&usr_config, (void *)arg,
+			sizeof(usr_config))) {
+			rc = -EFAULT;
+			break;
+		}
+
+		audio->wmapro_config = usr_config;
+
+		/* Need to swap the first and last words of advancedencodeopt2
+		 * as DSP cannot read 32-bit variable at a time. Need to be
+		 * split into two 16-bit and swap them as required by DSP */
+
+		audio->wmapro_config.advancedencodeopt2 =
+			((audio->wmapro_config.advancedencodeopt2 & 0xFFFF0000)
+			 >> 16) | ((audio->wmapro_config.advancedencodeopt2
+			 << 16) & 0xFFFF0000);
+		rc = 0;
+		break;
+	}
+	case AUDIO_GET_PCM_CONFIG:{
+			struct msm_audio_pcm_config config;
+			config.pcm_feedback = audio->pcm_feedback;
+			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.pcm_feedback != audio->pcm_feedback) {
+				MM_ERR("Not sufficient permission to"
+						"change the playback mode\n");
+				rc = -EACCES;
+				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)) {
+				MM_DBG("allocate PCM buffer %d\n",
+						config.buffer_count *
+						config.buffer_size);
+				audio->read_phys = pmem_kalloc(
+							config.buffer_size *
+							config.buffer_count,
+							PMEM_MEMTYPE_EBI1|
+							PMEM_ALIGNMENT_4K);
+				if (IS_ERR((void *)audio->read_phys)) {
+					rc = -ENOMEM;
+					break;
+				}
+				audio->read_data = ioremap(audio->read_phys,
+							config.buffer_size *
+							config.buffer_count);
+				if (!audio->read_data) {
+					MM_ERR("read buf alloc fail\n");
+					rc = -ENOMEM;
+					pmem_kfree(audio->read_phys);
+				} 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;
+					}
+					MM_DBG("read buf: phy addr \
+						0x%08x kernel addr 0x%08x\n",
+						audio->read_phys,
+						(int)audio->read_data);
+					rc = 0;
+				}
+			} else {
+				rc = 0;
+			}
+			break;
+		}
+	case AUDIO_PAUSE:
+		MM_DBG("AUDIO_PAUSE %ld\n", arg);
+		rc = audpp_pause(audio->dec_id, (int) arg);
+		break;
+	default:
+		rc = -EINVAL;
+	}
+	mutex_unlock(&audio->lock);
+	return rc;
+}
+
+/* Only useful in tunnel-mode */
+static int audio_fsync(struct file *file, int datasync)
+{
+	struct audio *audio = file->private_data;
+	struct buffer *frame;
+	int rc = 0;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+
+	if (!audio->running || audio->pcm_feedback) {
+		rc = -EINVAL;
+		goto done_nolock;
+	}
+
+	mutex_lock(&audio->write_lock);
+
+	rc = wait_event_interruptible(audio->write_wait,
+		(!audio->out[0].used &&
+		!audio->out[1].used &&
+		audio->out_needed) || audio->wflush);
+
+	if (rc < 0)
+		goto done;
+	else if (audio->wflush) {
+		rc = -EBUSY;
+		goto done;
+	}
+
+	if (audio->reserved) {
+		MM_DBG("send reserved byte\n");
+		frame = audio->out + audio->out_tail;
+		((char *) frame->data)[0] = audio->rsv_byte;
+		((char *) frame->data)[1] = 0;
+		frame->used = 2;
+		audplay_send_data(audio, 0);
+
+		rc = wait_event_interruptible(audio->write_wait,
+			(!audio->out[0].used &&
+			!audio->out[1].used &&
+			audio->out_needed) || audio->wflush);
+
+		if (rc < 0)
+			goto done;
+		else if (audio->wflush) {
+			rc = -EBUSY;
+			goto done;
+		}
+	}
+
+	/* pcm dmamiss message is sent continously
+	 * when decoder is starved so no race
+	 * condition concern
+	 */
+	audio->teos = 0;
+
+	rc = wait_event_interruptible(audio->write_wait,
+		audio->teos || audio->wflush);
+
+	if (audio->wflush)
+		rc = -EBUSY;
+
+done:
+	mutex_unlock(&audio->write_lock);
+done_nolock:
+	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);
+	MM_DBG("%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 */
+			MM_DBG("audio_read: no partial frame done reading\n");
+			break;
+		} else {
+			MM_DBG("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)) {
+				MM_ERR("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;
+			break;	/* Force to exit while loop
+				 * to prevent output thread
+				 * sleep too long if data is
+				 * not ready at this moment.
+				 */
+		}
+	}
+
+	/* 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;
+		MM_DBG("kick start pcm feedback again\n");
+		audplay_buffer_refresh(audio);
+	}
+
+	mutex_unlock(&audio->read_lock);
+
+	if (buf > start)
+		rc = buf - start;
+
+	MM_DBG("read %d bytes\n", rc);
+	return rc;
+}
+
+static int audwmapro_process_eos(struct audio *audio,
+		const char __user *buf_start, unsigned short mfield_size)
+{
+	int rc = 0;
+	struct buffer *frame;
+	char *buf_ptr;
+
+	if (audio->reserved) {
+		MM_DBG("flush reserve byte\n");
+		frame = audio->out + audio->out_head;
+		buf_ptr = frame->data;
+		rc = wait_event_interruptible(audio->write_wait,
+				(frame->used == 0)
+				|| (audio->stopped)
+				|| (audio->wflush));
+		if (rc < 0)
+			goto done;
+		if (audio->stopped || audio->wflush) {
+			rc = -EBUSY;
+			goto done;
+		}
+
+		buf_ptr[0] = audio->rsv_byte;
+		buf_ptr[1] = 0;
+		audio->out_head ^= 1;
+		frame->mfield_sz = 0;
+		frame->used = 2;
+		audio->reserved = 0;
+		audplay_send_data(audio, 0);
+	}
+
+	frame = audio->out + audio->out_head;
+
+	rc = wait_event_interruptible(audio->write_wait,
+		(audio->out_needed &&
+		audio->out[0].used == 0 &&
+		audio->out[1].used == 0)
+		|| (audio->stopped)
+		|| (audio->wflush));
+
+	if (rc < 0)
+		goto done;
+	if (audio->stopped || audio->wflush) {
+		rc = -EBUSY;
+		goto done;
+	}
+
+	if (copy_from_user(frame->data, buf_start, mfield_size)) {
+		rc = -EFAULT;
+		goto done;
+	}
+
+	frame->mfield_sz = mfield_size;
+	audio->out_head ^= 1;
+	frame->used = mfield_size;
+	audplay_send_data(audio, 0);
+done:
+	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, eos_condition = AUDWMAPRO_EOS_NONE;
+	unsigned dsize;
+	unsigned short mfield_size = 0;
+
+	MM_DBG("cnt=%d\n", count);
+
+	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->mfield) {
+			if (buf == start) {
+				/* Processing beginning of user buffer */
+				if (__get_user(mfield_size,
+					(unsigned short __user *) buf)) {
+					rc = -EFAULT;
+					break;
+				} else  if (mfield_size > count) {
+					rc = -EINVAL;
+					break;
+				}
+				MM_DBG("audio_write: mf offset_val %x\n",
+						mfield_size);
+				if (copy_from_user(cpy_ptr, buf, mfield_size)) {
+					rc = -EFAULT;
+					break;
+				}
+				/* Check if EOS flag is set and buffer has
+				 * contains just meta field
+				 */
+				if (cpy_ptr[AUDWMAPRO_EOS_FLG_OFFSET] &
+						 AUDWMAPRO_EOS_FLG_MASK) {
+					MM_DBG("audio_write: EOS SET\n");
+					eos_condition = AUDWMAPRO_EOS_SET;
+					if (mfield_size == count) {
+						buf += mfield_size;
+						break;
+					} else
+					cpy_ptr[AUDWMAPRO_EOS_FLG_OFFSET]
+						&= ~AUDWMAPRO_EOS_FLG_MASK;
+				}
+				cpy_ptr += mfield_size;
+				count -= mfield_size;
+				dsize += mfield_size;
+				buf += mfield_size;
+			} else {
+				mfield_size = 0;
+				MM_DBG("audio_write: continuous buffer\n");
+			}
+			frame->mfield_sz = mfield_size;
+		}
+
+		if (audio->reserved) {
+			MM_DBG("append reserved byte %x\n", audio->rsv_byte);
+			*cpy_ptr = audio->rsv_byte;
+			xfer = (count > ((frame->size - mfield_size) - 1)) ?
+				(frame->size - mfield_size) - 1 : count;
+			cpy_ptr++;
+			dsize += 1;
+			audio->reserved = 0;
+		} else
+			xfer = (count > (frame->size - mfield_size)) ?
+				(frame->size - mfield_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];
+			MM_DBG("odd length buf reserve last byte %x\n",
+					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);
+		}
+	}
+	if (eos_condition == AUDWMAPRO_EOS_SET)
+		rc = audwmapro_process_eos(audio, start, mfield_size);
+	mutex_unlock(&audio->write_lock);
+	if (!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;
+
+	MM_INFO("audio instance 0x%08x freeing\n", (int)audio);
+	mutex_lock(&audio->lock);
+	audio_disable(audio);
+	if (audio->rmt_resource_released == 0)
+		rmt_put_resource(audio);
+	audio_flush(audio);
+	audio_flush_pcm_buf(audio);
+	msm_adsp_put(audio->audplay);
+	audpp_adec_free(audio->dec_id);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	unregister_early_suspend(&audio->suspend_ctl.node);
+#endif
+	audio->event_abort = 1;
+	wake_up(&audio->event_wait);
+	audwmapro_reset_event_queue(audio);
+	iounmap(audio->data);
+	pmem_kfree(audio->phys);
+	if (audio->read_data) {
+		iounmap(audio->read_data);
+		pmem_kfree(audio->read_phys);
+	}
+	mutex_unlock(&audio->lock);
+#ifdef CONFIG_DEBUG_FS
+	if (audio->dentry)
+		debugfs_remove(audio->dentry);
+#endif
+	kfree(audio);
+	return 0;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void audwmapro_post_event(struct audio *audio, int type,
+		union msm_audio_event_payload payload)
+{
+	struct audwmapro_event *e_node = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+
+	if (!list_empty(&audio->free_event_queue)) {
+		e_node = list_first_entry(&audio->free_event_queue,
+				struct audwmapro_event, list);
+		list_del(&e_node->list);
+	} else {
+		e_node = kmalloc(sizeof(struct audwmapro_event), GFP_ATOMIC);
+		if (!e_node) {
+			MM_ERR("No mem to post event %d\n", type);
+			spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+			return;
+		}
+	}
+
+	e_node->event_type = type;
+	e_node->payload = payload;
+
+	list_add_tail(&e_node->list, &audio->event_queue);
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+	wake_up(&audio->event_wait);
+}
+
+static void audwmapro_suspend(struct early_suspend *h)
+{
+	struct audwmapro_suspend_ctl *ctl =
+		container_of(h, struct audwmapro_suspend_ctl, node);
+	union msm_audio_event_payload payload;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	audwmapro_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload);
+}
+
+static void audwmapro_resume(struct early_suspend *h)
+{
+	struct audwmapro_suspend_ctl *ctl =
+		container_of(h, struct audwmapro_suspend_ctl, node);
+	union msm_audio_event_payload payload;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	audwmapro_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload);
+}
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t audwmapro_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t audwmapro_debug_read(struct file *file, char __user *buf,
+			  size_t count, loff_t *ppos)
+{
+	const int debug_bufmax = 4096;
+	static char buffer[4096];
+	int n = 0, i;
+	struct audio *audio = file->private_data;
+
+	mutex_lock(&audio->lock);
+	n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "enabled %d\n", audio->enabled);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "stopped %d\n", audio->stopped);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "pcm_feedback %d\n", audio->pcm_feedback);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "out_buf_sz %d\n", audio->out[0].size);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "pcm_buf_count %d \n", audio->pcm_buf_count);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "pcm_buf_sz %d \n", audio->in[0].size);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "volume %x \n", audio->vol_pan.volume);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "sample rate %d \n", audio->out_sample_rate);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+		"channel mode %d \n", audio->out_channel_mode);
+	mutex_unlock(&audio->lock);
+	/* Following variables are only useful for debugging when
+	 * when playback halts unexpectedly. Thus, no mutual exclusion
+	 * enforced
+	 */
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "wflush %d\n", audio->wflush);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "rflush %d\n", audio->rflush);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "running %d \n", audio->running);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"dec state %d \n", audio->dec_state);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "out_needed %d \n", audio->out_needed);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "out_head %d \n", audio->out_head);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "out_tail %d \n", audio->out_tail);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "out[0].used %d \n", audio->out[0].used);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "out[1].used %d \n", audio->out[1].used);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "buffer_refresh %d \n", audio->buf_refresh);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "read_next %d \n", audio->read_next);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+				   "fill_next %d \n", audio->fill_next);
+	for (i = 0; i < audio->pcm_buf_count; i++)
+		n += scnprintf(buffer + n, debug_bufmax - n,
+			"in[%d].size %d \n", i, audio->in[i].used);
+	buffer[n] = 0;
+	return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+
+static const struct file_operations audwmapro_debug_fops = {
+	.read = audwmapro_debug_read,
+	.open = audwmapro_debug_open,
+};
+#endif
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+	struct audio *audio = NULL;
+	int rc, dec_attrb, decid, i;
+	unsigned pmem_sz = DMASZ_MAX;
+	struct audwmapro_event *e_node = NULL;
+#ifdef CONFIG_DEBUG_FS
+	/* 4 bytes represents decoder number, 1 byte for terminate string */
+	char name[sizeof "msm_wmapro_" + 5];
+#endif
+
+	/* Allocate Mem for audio instance */
+	audio = kzalloc(sizeof(struct audio), GFP_KERNEL);
+	if (!audio) {
+		MM_ERR("no memory to allocate audio instance \n");
+		rc = -ENOMEM;
+		goto done;
+	}
+	MM_INFO("audio instance 0x%08x created\n", (int)audio);
+
+	/* Allocate the decoder */
+	dec_attrb = AUDDEC_DEC_WMAPRO;
+	if ((file->f_mode & FMODE_WRITE) &&
+			(file->f_mode & FMODE_READ)) {
+		dec_attrb |= MSM_AUD_MODE_NONTUNNEL;
+		audio->pcm_feedback = NON_TUNNEL_MODE_PLAYBACK;
+	} else if ((file->f_mode & FMODE_WRITE) &&
+			!(file->f_mode & FMODE_READ)) {
+		dec_attrb |= MSM_AUD_MODE_TUNNEL;
+		audio->pcm_feedback = TUNNEL_MODE_PLAYBACK;
+	} else {
+		kfree(audio);
+		rc = -EACCES;
+		goto done;
+	}
+
+	decid = audpp_adec_alloc(dec_attrb, &audio->module_name,
+			&audio->queue_id);
+
+	if (decid < 0) {
+		MM_ERR("No free decoder available, freeing instance 0x%08x\n",
+				(int)audio);
+		rc = -ENODEV;
+		kfree(audio);
+		goto done;
+	}
+	audio->dec_id = decid & MSM_AUD_DECODER_MASK;
+
+	while (pmem_sz >= DMASZ_MIN) {
+		MM_DBG("pmemsz = %d\n", pmem_sz);
+		audio->phys = pmem_kalloc(pmem_sz, PMEM_MEMTYPE_EBI1|
+					PMEM_ALIGNMENT_4K);
+		if (!IS_ERR((void *)audio->phys)) {
+			audio->data = ioremap(audio->phys, pmem_sz);
+			if (!audio->data) {
+				MM_ERR("could not allocate write buffers, \
+						freeing instance 0x%08x\n",
+						(int)audio);
+				rc = -ENOMEM;
+				pmem_kfree(audio->phys);
+				audpp_adec_free(audio->dec_id);
+				kfree(audio);
+				goto done;
+			}
+			MM_DBG("write buf: phy addr 0x%08x kernel addr \
+				0x%08x\n", audio->phys, (int)audio->data);
+			break;
+		} else if (pmem_sz == DMASZ_MIN) {
+			MM_ERR("could not allocate write buffers, freeing \
+					instance 0x%08x\n", (int)audio);
+			rc = -ENOMEM;
+			audpp_adec_free(audio->dec_id);
+			kfree(audio);
+			goto done;
+		} else
+		pmem_sz >>= 1;
+	}
+	audio->out_dma_sz = pmem_sz;
+
+	rc = audmgr_open(&audio->audmgr);
+	if (rc) {
+		MM_ERR("audmgr open failed, freeing instance 0x%08x\n",
+				(int)audio);
+		goto err;
+	}
+
+	rc = msm_adsp_get(audio->module_name, &audio->audplay,
+			&audplay_adsp_ops_wmapro, audio);
+	if (rc) {
+		MM_ERR("failed to get %s module, freeing instance 0x%08x\n",
+				audio->module_name, (int)audio);
+		audmgr_close(&audio->audmgr);
+		goto err;
+	}
+
+	rc = rmt_get_resource(audio);
+	if (rc) {
+		MM_ERR("ADSP resources are not available for WMAPRO session \
+			 0x%08x on decoder: %d\n", (int)audio, audio->dec_id);
+		if (audio->pcm_feedback == TUNNEL_MODE_PLAYBACK)
+			audmgr_close(&audio->audmgr);
+		msm_adsp_put(audio->audplay);
+		goto err;
+	}
+
+	mutex_init(&audio->lock);
+	mutex_init(&audio->write_lock);
+	mutex_init(&audio->read_lock);
+	mutex_init(&audio->get_event_lock);
+	spin_lock_init(&audio->dsp_lock);
+	init_waitqueue_head(&audio->write_wait);
+	init_waitqueue_head(&audio->read_wait);
+	INIT_LIST_HEAD(&audio->free_event_queue);
+	INIT_LIST_HEAD(&audio->event_queue);
+	init_waitqueue_head(&audio->wait);
+	init_waitqueue_head(&audio->event_wait);
+	spin_lock_init(&audio->event_queue_lock);
+
+	audio->out[0].data = audio->data + 0;
+	audio->out[0].addr = audio->phys + 0;
+	audio->out[0].size = audio->out_dma_sz >> 1;
+
+	audio->out[1].data = audio->data + audio->out[0].size;
+	audio->out[1].addr = audio->phys + audio->out[0].size;
+	audio->out[1].size = audio->out[0].size;
+
+	audio->out_sample_rate = 44100;
+	audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V;
+
+	audio->vol_pan.volume = 0x2000;
+
+	audio_flush(audio);
+
+	file->private_data = audio;
+	audio->opened = 1;
+#ifdef CONFIG_DEBUG_FS
+	snprintf(name, sizeof name, "msm_wmapro_%04x", audio->dec_id);
+	audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+				NULL, (void *) audio,
+				&audwmapro_debug_fops);
+
+	if (IS_ERR(audio->dentry))
+		MM_DBG("debugfs_create_file failed\n");
+#endif
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	audio->suspend_ctl.node.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
+	audio->suspend_ctl.node.resume = audwmapro_resume;
+	audio->suspend_ctl.node.suspend = audwmapro_suspend;
+	audio->suspend_ctl.audio = audio;
+	register_early_suspend(&audio->suspend_ctl.node);
+#endif
+	for (i = 0; i < AUDWMAPRO_EVENT_NUM; i++) {
+		e_node = kmalloc(sizeof(struct audwmapro_event), GFP_KERNEL);
+		if (e_node)
+			list_add_tail(&e_node->list, &audio->free_event_queue);
+		else {
+			MM_ERR("event pkt alloc failed\n");
+			break;
+		}
+	}
+done:
+	return rc;
+err:
+	iounmap(audio->data);
+	pmem_kfree(audio->phys);
+	audpp_adec_free(audio->dec_id);
+	kfree(audio);
+	return rc;
+}
+
+static const struct file_operations audio_wmapro_fops = {
+	.owner		= THIS_MODULE,
+	.open		= audio_open,
+	.release	= audio_release,
+	.read 		= audio_read,
+	.write		= audio_write,
+	.unlocked_ioctl	= audio_ioctl,
+	.fsync 		= audio_fsync,
+};
+
+struct miscdevice audio_wmapro_misc = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "msm_wmapro",
+	.fops	= &audio_wmapro_fops,
+};
+
+static int __init audio_init(void)
+{
+	return misc_register(&audio_wmapro_misc);
+}
+
+device_initcall(audio_init);
diff --git a/arch/arm/mach-msm/qdsp5/audmgr.c b/arch/arm/mach-msm/qdsp5/audmgr.c
new file mode 100644
index 0000000..231a28c
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audmgr.c
@@ -0,0 +1,365 @@
+/* arch/arm/mach-msm/qdsp5/audmgr.c
+ *
+ * interface to "audmgr" service on the baseband cpu
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * 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 <linux/slab.h>
+
+#include <asm/atomic.h>
+#include <mach/msm_rpcrouter.h>
+
+#include "audmgr.h"
+#include <mach/debug_mm.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
+
+/* store information used across complete audmgr sessions */
+struct audmgr_global {
+	struct mutex *lock;
+	struct msm_rpc_endpoint *ept;
+	struct task_struct *task;
+	uint32_t rpc_version;
+};
+static DEFINE_MUTEX(audmgr_lock);
+
+static struct audmgr_global the_audmgr_state = {
+	.lock = &audmgr_lock,
+};
+
+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_global *amg,
+				   struct rpc_audmgr_cb_func_ptr *args,
+				   int len)
+{
+	struct audmgr *am;
+
+	/* Allow only if complete arguments recevied */
+	if (len < (sizeof(struct rpc_audmgr_cb_func_ptr)))
+		return;
+
+	/* Allow only if valid argument */
+	if (be32_to_cpu(args->set_to_one) != 1)
+		return;
+
+	am = (struct audmgr *) be32_to_cpu(args->client_data);
+
+	if (!am)
+		return;
+
+	switch (be32_to_cpu(args->status)) {
+	case RPC_AUDMGR_STATUS_READY:
+		am->handle = be32_to_cpu(args->u.handle);
+		MM_INFO("rpc READY handle=0x%08x\n", am->handle);
+		break;
+	case RPC_AUDMGR_STATUS_CODEC_CONFIG: {
+		uint32_t volume;
+		volume = be32_to_cpu(args->u.volume);
+		MM_INFO("rpc CODEC_CONFIG volume=0x%08x\n", volume);
+		am->state = STATE_ENABLED;
+		wake_up(&am->wait);
+		break;
+	}
+	case RPC_AUDMGR_STATUS_PENDING:
+		MM_ERR("PENDING?\n");
+		break;
+	case RPC_AUDMGR_STATUS_SUSPEND:
+		MM_ERR("SUSPEND?\n");
+		break;
+	case RPC_AUDMGR_STATUS_FAILURE:
+		MM_ERR("FAILURE\n");
+		break;
+	case RPC_AUDMGR_STATUS_VOLUME_CHANGE:
+		MM_ERR("VOLUME_CHANGE?\n");
+		break;
+	case RPC_AUDMGR_STATUS_DISABLED:
+		MM_ERR("DISABLED\n");
+		am->state = STATE_DISABLED;
+		wake_up(&am->wait);
+		break;
+	case RPC_AUDMGR_STATUS_ERROR:
+		MM_ERR("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_global *amg = private;
+
+	if (proc == AUDMGR_CB_FUNC_PTR)
+		process_audmgr_callback(amg, data, len);
+	else
+		MM_ERR("unknown rpc proc %d\n", proc);
+	rpc_ack(amg->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_global *amg = data;
+	struct rpc_request_hdr *hdr = NULL;
+	uint32_t type;
+	int len;
+
+	MM_INFO("start\n");
+
+	while (!kthread_should_stop()) {
+		if (hdr) {
+			kfree(hdr);
+			hdr = NULL;
+		}
+		len = msm_rpc_read(amg->ept, (void **) &hdr, -1, -1);
+		if (len < 0) {
+			MM_ERR("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);
+				MM_INFO("rpc_reply status %d\n", status);
+			} else {
+				MM_INFO("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);
+	}
+	MM_INFO("exit\n");
+	if (hdr) {
+		kfree(hdr);
+		hdr = NULL;
+	}
+	amg->task = NULL;
+	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)
+{
+	struct audmgr_global *amg = &the_audmgr_state;
+	int rc;
+
+	if (am->state != STATE_CLOSED)
+		return 0;
+
+	mutex_lock(amg->lock);
+
+	/* connect to audmgr end point and polling thread only once */
+	if (amg->ept == NULL) {
+		amg->ept = msm_rpc_connect_compatible(AUDMGR_PROG,
+				AUDMGR_VERS_COMP_VER3,
+				MSM_RPC_UNINTERRUPTIBLE);
+		if (IS_ERR(amg->ept)) {
+			MM_ERR("connect failed with current VERS \
+				= %x, trying again with another API\n",
+				AUDMGR_VERS_COMP_VER3);
+			amg->ept = msm_rpc_connect_compatible(AUDMGR_PROG,
+					AUDMGR_VERS_COMP_VER2,
+					MSM_RPC_UNINTERRUPTIBLE);
+			if (IS_ERR(amg->ept)) {
+				MM_ERR("connect failed with current VERS \
+					= %x, trying again with another API\n",
+					AUDMGR_VERS_COMP_VER2);
+				amg->ept = msm_rpc_connect_compatible(
+						AUDMGR_PROG,
+						AUDMGR_VERS_COMP,
+						MSM_RPC_UNINTERRUPTIBLE);
+				if (IS_ERR(amg->ept)) {
+					MM_ERR("connect failed with current \
+					VERS=%x, trying again with another \
+					API\n", AUDMGR_VERS_COMP);
+					amg->ept = msm_rpc_connect(AUDMGR_PROG,
+						AUDMGR_VERS,
+						MSM_RPC_UNINTERRUPTIBLE);
+					amg->rpc_version = AUDMGR_VERS;
+				} else
+					amg->rpc_version = AUDMGR_VERS_COMP;
+			} else
+				amg->rpc_version = AUDMGR_VERS_COMP_VER2;
+		} else
+			amg->rpc_version = AUDMGR_VERS_COMP_VER3;
+
+		if (IS_ERR(amg->ept)) {
+			rc = PTR_ERR(amg->ept);
+			amg->ept = NULL;
+			MM_ERR("failed to connect to audmgr svc\n");
+			goto done;
+		}
+
+		amg->task = kthread_run(audmgr_rpc_thread, amg, "audmgr_rpc");
+		if (IS_ERR(amg->task)) {
+			rc = PTR_ERR(amg->task);
+			amg->task = NULL;
+			msm_rpc_close(amg->ept);
+			amg->ept = NULL;
+			goto done;
+		}
+	}
+
+	/* Initialize session parameters */
+	init_waitqueue_head(&am->wait);
+	am->state = STATE_DISABLED;
+	rc = 0;
+done:
+	mutex_unlock(amg->lock);
+	return rc;
+}
+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_global *amg = &the_audmgr_state;
+	struct audmgr_enable_msg msg;
+	int rc;
+
+	if (am->state == STATE_ENABLED)
+		return 0;
+
+	if (am->state == STATE_DISABLING)
+		MM_ERR("state is DISABLING in enable?\n");
+	am->state = STATE_ENABLING;
+
+	MM_INFO("session 0x%08x\n", (int) am);
+	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((int)am);
+
+	msm_rpc_setup_req(&msg.hdr, AUDMGR_PROG, amg->rpc_version,
+			  AUDMGR_ENABLE_CLIENT);
+
+	rc = msm_rpc_write(amg->ept, &msg, sizeof(msg));
+	if (rc < 0)
+		return rc;
+
+	rc = wait_event_timeout(am->wait, am->state != STATE_ENABLING, 15 * HZ);
+	if (rc == 0) {
+		MM_ERR("ARM9 did not reply to RPC am->state = %d\n", am->state);
+	}
+	if (am->state == STATE_ENABLED)
+		return 0;
+
+	MM_ERR("unexpected state %d while enabling?!\n", am->state);
+	return -ENODEV;
+}
+EXPORT_SYMBOL(audmgr_enable);
+
+int audmgr_disable(struct audmgr *am)
+{
+	struct audmgr_global *amg = &the_audmgr_state;
+	struct audmgr_disable_msg msg;
+	int rc;
+
+	if (am->state == STATE_DISABLED)
+		return 0;
+
+	MM_INFO("session 0x%08x\n", (int) am);
+	msg.handle = cpu_to_be32(am->handle);
+	msm_rpc_setup_req(&msg.hdr, AUDMGR_PROG, amg->rpc_version,
+			  AUDMGR_DISABLE_CLIENT);
+
+	am->state = STATE_DISABLING;
+
+	rc = msm_rpc_write(amg->ept, &msg, sizeof(msg));
+	if (rc < 0)
+		return rc;
+
+	rc = wait_event_timeout(am->wait, am->state != STATE_DISABLING, 15 * HZ);
+	if (rc == 0) {
+		MM_ERR("ARM9 did not reply to RPC am->state = %d\n", am->state);
+	}
+
+	if (am->state == STATE_DISABLED)
+		return 0;
+
+	MM_ERR("unexpected state %d while disabling?!\n", am->state);
+	return -ENODEV;
+}
+EXPORT_SYMBOL(audmgr_disable);
diff --git a/arch/arm/mach-msm/qdsp5/audmgr.h b/arch/arm/mach-msm/qdsp5/audmgr.h
new file mode 100644
index 0000000..225beef
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audmgr.h
@@ -0,0 +1,266 @@
+/* arch/arm/mach-msm/qdsp5/audmgr.h
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ *
+ * 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 _AUDIO_RPC_H_
+#define _AUDIO_RPC_H_
+
+#include <mach/qdsp5/qdsp5audppcmdi.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_VERS "rs30000013:0x7feccbff"
+#define AUDMGR_PROG 0x30000013
+#define AUDMGR_VERS 0x7feccbff
+#define AUDMGR_VERS_COMP 0x00010001
+#define AUDMGR_VERS_COMP_VER2 0x00020001
+#define AUDMGR_VERS_COMP_VER3 0x00030001
+
+struct rpc_audmgr_cb_func_ptr {
+	uint32_t cb_id; /* cb_func */
+	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 = volume
+	   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;
+	uint32_t client_data;
+};
+
+#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_VERS "rs31000013:0xf8e3e2d9"
+#define AUDMGR_CB_PROG 0x31000013
+#define AUDMGR_CB_VERS 0xf8e3e2d9
+
+struct audmgr {
+	wait_queue_head_t wait;
+	uint32_t handle;
+	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);
+typedef void (*audrec_event_func)(void *private, unsigned id, uint16_t *msg);
+
+/* worst case delay of 1sec for response */
+#define MSM_AUD_DECODER_WAIT_MS 1000
+#define MSM_AUD_MODE_TUNNEL  0x00000100
+#define MSM_AUD_MODE_NONTUNNEL  0x00000200
+#define MSM_AUD_DECODER_MASK  0x0000FFFF
+#define MSM_AUD_OP_MASK  0xFFFF0000
+
+/*Playback mode*/
+#define NON_TUNNEL_MODE_PLAYBACK 1
+#define TUNNEL_MODE_PLAYBACK 0
+
+enum msm_aud_decoder_state {
+	MSM_AUD_DECODER_STATE_NONE = 0,
+	MSM_AUD_DECODER_STATE_FAILURE = 1,
+	MSM_AUD_DECODER_STATE_SUCCESS = 2,
+	MSM_AUD_DECODER_STATE_CLOSE = 3,
+};
+
+int audpp_adec_alloc(unsigned dec_attrb, const char **module_name,
+			unsigned *queueid);
+void audpp_adec_free(int decid);
+
+struct audpp_event_callback {
+	audpp_event_func fn;
+	void *private;
+};
+
+int audpp_register_event_callback(struct audpp_event_callback *eh);
+int audpp_unregister_event_callback(struct audpp_event_callback *eh);
+int is_audpp_enable(void);
+
+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);
+int audpp_dsp_set_mbadrc(unsigned id, unsigned enable,
+			audpp_cmd_cfg_object_params_mbadrc *mbadrc);
+int audpp_dsp_set_eq(unsigned id, unsigned enable,
+			audpp_cmd_cfg_object_params_eqalizer *eq);
+int audpp_dsp_set_rx_iir(unsigned id, unsigned enable,
+				audpp_cmd_cfg_object_params_pcm *iir);
+int audpp_dsp_set_vol_pan(unsigned id,
+				audpp_cmd_cfg_object_params_volume *vol_pan);
+int audpp_dsp_set_qconcert_plus(unsigned id, unsigned enable,
+			audpp_cmd_cfg_object_params_qconcert *qconcert_plus);
+int audrectask_enable(unsigned enc_type, audrec_event_func func, void *private);
+void audrectask_disable(unsigned enc_type, void *private);
+
+int audrectask_send_cmdqueue(void *cmd, unsigned len);
+int audrectask_send_bitstreamqueue(void *cmd, unsigned len);
+
+#endif
diff --git a/arch/arm/mach-msm/qdsp5/audmgr_new.h b/arch/arm/mach-msm/qdsp5/audmgr_new.h
new file mode 100644
index 0000000..3604405
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audmgr_new.h
@@ -0,0 +1,213 @@
+/* arch/arm/mach-msm/qdsp5/audmgr.h
+ *
+ * Copyright 2008 (c) Code Aurora Forum. All rights reserved.
+ * 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/arch/arm/mach-msm/qdsp5/audpp.c b/arch/arm/mach-msm/qdsp5/audpp.c
new file mode 100644
index 0000000..3e834d8
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audpp.c
@@ -0,0 +1,873 @@
+
+/* arch/arm/mach-msm/qdsp5/audpp.c
+ *
+ * common code to deal with the AUDPP dsp task (audio postproc)
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved.
+ *
+ * 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 <linux/sched.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/board.h>
+#include <mach/msm_adsp.h>
+
+#include "audmgr.h"
+
+#include <mach/qdsp5/qdsp5audppcmdi.h>
+#include <mach/qdsp5/qdsp5audppmsg.h>
+#include <mach/debug_mm.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);
+static DEFINE_MUTEX(audpp_dec_lock);
+
+#define CH_COUNT 5
+#define AUDPP_CLNT_MAX_COUNT 6
+#define AUDPP_AVSYNC_INFO_SIZE 7
+
+#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_VOLUME_PAN		0
+#define AUDPP_CMD_IIR_TUNING_FILTER	1
+#define AUDPP_CMD_EQUALIZER		2
+#define AUDPP_CMD_ADRC			3
+#define AUDPP_CMD_SPECTROGRAM		4
+#define AUDPP_CMD_QCONCERT		5
+#define AUDPP_CMD_SIDECHAIN_TUNING_FILTER	6
+#define AUDPP_CMD_SAMPLING_FREQUENCY	7
+#define AUDPP_CMD_QAFX			8
+#define AUDPP_CMD_QRUMBLE		9
+#define AUDPP_CMD_MBADRC		10
+
+#define MAX_EVENT_CALLBACK_CLIENTS 	1
+
+#define AUDPP_CONCURRENCY_DEFAULT 6	/* All non tunnel mode */
+#define AUDPP_MAX_DECODER_CNT 5
+#define AUDPP_CODEC_MASK 0x000000FF
+#define AUDPP_MODE_MASK 0x00000F00
+#define AUDPP_OP_MASK 0xF0000000
+
+struct audpp_decoder_info {
+	unsigned int codec;
+	pid_t pid;
+};
+
+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;
+
+	/* Related to decoder allocation */
+	struct mutex *lock_dec;
+	struct msm_adspdec_database *dec_database;
+	struct audpp_decoder_info dec_info_table[AUDPP_MAX_DECODER_CNT];
+	unsigned dec_inuse;
+	unsigned long concurrency;
+
+	/* 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_event_callback *cb_tbl[MAX_EVENT_CALLBACK_CLIENTS];
+
+	wait_queue_head_t event_wait;
+};
+
+struct audpp_state the_audpp_state = {
+	.lock = &audpp_lock,
+	.lock_dec = &audpp_dec_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));
+}
+
+int is_audpp_enable(void)
+{
+	struct audpp_state *audpp = &the_audpp_state;
+
+	return audpp->enabled;
+}
+EXPORT_SYMBOL(is_audpp_enable);
+
+int audpp_register_event_callback(struct audpp_event_callback *ecb)
+{
+	struct audpp_state *audpp = &the_audpp_state;
+	int i;
+
+	for (i = 0; i < MAX_EVENT_CALLBACK_CLIENTS; ++i) {
+		if (NULL == audpp->cb_tbl[i]) {
+			audpp->cb_tbl[i] = ecb;
+			return 0;
+		}
+	}
+	return -1;
+}
+EXPORT_SYMBOL(audpp_register_event_callback);
+
+int audpp_unregister_event_callback(struct audpp_event_callback *ecb)
+{
+	struct audpp_state *audpp = &the_audpp_state;
+	int i;
+
+	for (i = 0; i < MAX_EVENT_CALLBACK_CLIENTS; ++i) {
+		if (ecb == audpp->cb_tbl[i]) {
+			audpp->cb_tbl[i] = NULL;
+			return 0;
+		}
+	}
+	return -1;
+}
+EXPORT_SYMBOL(audpp_unregister_event_callback);
+
+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);
+	}
+
+	for (n = 0; n < MAX_EVENT_CALLBACK_CLIENTS; ++n)
+		if (audpp->cb_tbl[n] && audpp->cb_tbl[n]->fn)
+			audpp->cb_tbl[n]->fn(audpp->cb_tbl[n]->private, 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_handle_pcmdmamiss(struct audpp_state *audpp,
+				    uint16_t bit_mask)
+{
+	uint8_t b_index;
+
+	for (b_index = 0; b_index < AUDPP_CLNT_MAX_COUNT; b_index++) {
+		if (bit_mask & (0x1 << b_index))
+			if (audpp->func[b_index])
+				audpp->func[b_index] (audpp->private[b_index],
+						      AUDPP_MSG_PCMDMAMISSED,
+						      &bit_mask);
+	}
+}
+
+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];
+			MM_DBG("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:
+		audpp_handle_pcmdmamiss(audpp, msg[0]);
+		break;
+	case AUDPP_MSG_CFG_MSG:
+		if (msg[0] == AUDPP_MSG_ENA_ENA) {
+			MM_INFO("ENABLE\n");
+			audpp->enabled = 1;
+			audpp_broadcast(audpp, id, msg);
+		} else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+			MM_INFO("DISABLE\n");
+			audpp->enabled = 0;
+			wake_up(&audpp->event_wait);
+			audpp_broadcast(audpp, id, msg);
+		} else {
+			MM_ERR("invalid config msg %d\n", msg[0]);
+		}
+		break;
+	case AUDPP_MSG_ROUTING_ACK:
+		audpp_notify_clnt(audpp, msg[0], id, msg);
+		break;
+	case AUDPP_MSG_FLUSH_ACK:
+		audpp_notify_clnt(audpp, msg[0], id, msg);
+		break;
+	case ADSP_MESSAGE_ID:
+		MM_DBG("Received ADSP event: module enable/disable(audpptask)");
+		break;
+	default:
+		MM_ERR("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) {
+		MM_DBG("enable\n");
+		res = msm_adsp_get("AUDPPTASK", &audpp->mod, &adsp_ops, audpp);
+		if (res < 0) {
+			MM_ERR("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;
+	int rc;
+
+	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) {
+		MM_DBG("disable\n");
+		LOG(EV_DISABLE, 2);
+		audpp_dsp_config(0);
+		rc = wait_event_interruptible(audpp->event_wait,
+				(audpp->enabled == 0));
+		if (audpp->enabled == 0)
+			MM_INFO("Received CFG_MSG_DISABLE from ADSP\n");
+		else
+			MM_ERR("Didn't receive CFG_MSG DISABLE \
+					message from ADSP\n");
+		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);
+
+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);
+
+/* Implementation of COPP features */
+int audpp_dsp_set_mbadrc(unsigned id, unsigned enable,
+			 audpp_cmd_cfg_object_params_mbadrc *mbadrc)
+{
+	audpp_cmd_cfg_object_params_mbadrc cmd;
+
+	if (id != 6)
+		return -EINVAL;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.common.comman_cfg = AUDPP_CMD_CFG_OBJ_UPDATE;
+	cmd.common.command_type = AUDPP_CMD_MBADRC;
+
+	if (enable) {
+		memcpy(&cmd.num_bands, &mbadrc->num_bands,
+		       sizeof(*mbadrc) -
+		       (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2));
+		cmd.enable = AUDPP_CMD_ADRC_FLAG_ENA;
+	} else
+		cmd.enable = AUDPP_CMD_ADRC_FLAG_DIS;
+
+	/*order the writes to mbadrc */
+	dma_coherent_pre_ops();
+	return audpp_send_queue3(&cmd, sizeof(cmd));
+}
+EXPORT_SYMBOL(audpp_dsp_set_mbadrc);
+
+int audpp_dsp_set_qconcert_plus(unsigned id, unsigned enable,
+				audpp_cmd_cfg_object_params_qconcert *
+				qconcert_plus)
+{
+	audpp_cmd_cfg_object_params_qconcert cmd;
+	if (id != 6)
+		return -EINVAL;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.common.comman_cfg = AUDPP_CMD_CFG_OBJ_UPDATE;
+	cmd.common.command_type = AUDPP_CMD_QCONCERT;
+
+	if (enable) {
+		memcpy(&cmd.op_mode, &qconcert_plus->op_mode,
+		       sizeof(audpp_cmd_cfg_object_params_qconcert) -
+		       (AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2));
+		cmd.enable_flag = AUDPP_CMD_ADRC_FLAG_ENA;
+	} else
+		cmd.enable_flag = AUDPP_CMD_ADRC_FLAG_DIS;
+
+	return audpp_send_queue3(&cmd, sizeof(cmd));
+}
+
+int audpp_dsp_set_rx_iir(unsigned id, unsigned enable,
+			 audpp_cmd_cfg_object_params_pcm *iir)
+{
+	audpp_cmd_cfg_object_params_pcm cmd;
+
+	if (id != 6)
+		return -EINVAL;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.common.comman_cfg = AUDPP_CMD_CFG_OBJ_UPDATE;
+	cmd.common.command_type = AUDPP_CMD_IIR_TUNING_FILTER;
+
+	if (enable) {
+		cmd.active_flag = AUDPP_CMD_IIR_FLAG_ENA;
+		cmd.num_bands = iir->num_bands;
+		memcpy(&cmd.params_filter, &iir->params_filter,
+		       sizeof(iir->params_filter));
+	} else
+		cmd.active_flag = AUDPP_CMD_IIR_FLAG_DIS;
+
+	return audpp_send_queue3(&cmd, sizeof(cmd));
+}
+EXPORT_SYMBOL(audpp_dsp_set_rx_iir);
+
+/* Implementation Of COPP + POPP */
+int audpp_dsp_set_eq(unsigned id, unsigned enable,
+		     audpp_cmd_cfg_object_params_eqalizer *eq)
+{
+	audpp_cmd_cfg_object_params_eqalizer cmd;
+	unsigned short *id_ptr = (unsigned short *)&cmd;
+
+	if (id > 6 || id == 5)
+		return -EINVAL;
+
+	memset(&cmd, 0, sizeof(cmd));
+	id_ptr[1 + id] = AUDPP_CMD_CFG_OBJ_UPDATE;
+	cmd.common.command_type = AUDPP_CMD_EQUALIZER;
+
+	if (enable) {
+		cmd.eq_flag = AUDPP_CMD_EQ_FLAG_ENA;
+		cmd.num_bands = eq->num_bands;
+		memcpy(&cmd.eq_coeff, &eq->eq_coeff, sizeof(eq->eq_coeff));
+	} else
+		cmd.eq_flag = AUDPP_CMD_EQ_FLAG_DIS;
+
+	return audpp_send_queue3(&cmd, sizeof(cmd));
+}
+EXPORT_SYMBOL(audpp_dsp_set_eq);
+
+int audpp_dsp_set_vol_pan(unsigned id,
+			  audpp_cmd_cfg_object_params_volume *vol_pan)
+{
+	audpp_cmd_cfg_object_params_volume cmd;
+	unsigned short *id_ptr = (unsigned short *)&cmd;
+
+	if (id > 6)
+		return -EINVAL;
+
+	memset(&cmd, 0, sizeof(cmd));
+	id_ptr[1 + id] = AUDPP_CMD_CFG_OBJ_UPDATE;
+	cmd.common.command_type = AUDPP_CMD_VOLUME_PAN;
+
+	cmd.volume = vol_pan->volume;
+	cmd.pan = vol_pan->pan;
+
+	return audpp_send_queue3(&cmd, sizeof(cmd));
+}
+EXPORT_SYMBOL(audpp_dsp_set_vol_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);
+
+/* dec_attrb = 7:0, 0 - No Decoder, else supported decoder *
+ * like mp3, aac, wma etc ... *
+ *           =  15:8, bit[8] = 1 - Tunnel, bit[9] = 1 - NonTunnel *
+ *           =  31:16, reserved */
+int audpp_adec_alloc(unsigned dec_attrb, const char **module_name,
+		     unsigned *queueid)
+{
+	struct audpp_state *audpp = &the_audpp_state;
+	int decid = -1, idx, lidx, mode, codec;
+	int codecs_supported, min_codecs_supported;
+	unsigned int *concurrency_entry;
+	mutex_lock(audpp->lock_dec);
+	/* Represents in bit mask */
+	mode = ((dec_attrb & AUDPP_MODE_MASK) << 16);
+	codec = (1 << (dec_attrb & AUDPP_CODEC_MASK));
+	/* Point to Last entry of the row */
+	concurrency_entry = ((audpp->dec_database->dec_concurrency_table +
+			      ((audpp->concurrency + 1) *
+			       (audpp->dec_database->num_dec))) - 1);
+
+	lidx = audpp->dec_database->num_dec;
+	min_codecs_supported = sizeof(unsigned int) * 8;
+
+	MM_DBG("mode = 0x%08x codec = 0x%08x\n", mode, codec);
+
+	for (idx = lidx; idx > 0; idx--, concurrency_entry--) {
+		if (!(audpp->dec_inuse & (1 << (idx - 1)))) {
+			if ((mode & *concurrency_entry) &&
+			    (codec & *concurrency_entry)) {
+				/* Check supports minimum number codecs */
+				codecs_supported =
+				    audpp->dec_database->dec_info_list[idx -
+								       1].
+				    nr_codec_support;
+				if (codecs_supported < min_codecs_supported) {
+					lidx = idx - 1;
+					min_codecs_supported = codecs_supported;
+				}
+			}
+		}
+	}
+
+	if (lidx < audpp->dec_database->num_dec) {
+		audpp->dec_inuse |= (1 << lidx);
+		*module_name =
+		    audpp->dec_database->dec_info_list[lidx].module_name;
+		*queueid =
+		    audpp->dec_database->dec_info_list[lidx].module_queueid;
+		decid = audpp->dec_database->dec_info_list[lidx].module_decid;
+		audpp->dec_info_table[lidx].codec =
+		    (dec_attrb & AUDPP_CODEC_MASK);
+		audpp->dec_info_table[lidx].pid = current->pid;
+		/* point to row to get supported operation */
+		concurrency_entry =
+		    ((audpp->dec_database->dec_concurrency_table +
+		      ((audpp->concurrency) * (audpp->dec_database->num_dec))) +
+		     lidx);
+		decid |= ((*concurrency_entry & AUDPP_OP_MASK) >> 12);
+		MM_INFO("decid =0x%08x module_name=%s, queueid=%d \n",
+			decid, *module_name, *queueid);
+	}
+	mutex_unlock(audpp->lock_dec);
+	return decid;
+
+}
+EXPORT_SYMBOL(audpp_adec_alloc);
+
+void audpp_adec_free(int decid)
+{
+	struct audpp_state *audpp = &the_audpp_state;
+	int idx;
+	mutex_lock(audpp->lock_dec);
+	for (idx = audpp->dec_database->num_dec; idx > 0; idx--) {
+		if (audpp->dec_database->dec_info_list[idx - 1].module_decid ==
+		    decid) {
+			audpp->dec_inuse &= ~(1 << (idx - 1));
+			audpp->dec_info_table[idx - 1].codec = -1;
+			audpp->dec_info_table[idx - 1].pid = 0;
+			MM_INFO("free decid =%d \n", decid);
+			break;
+		}
+	}
+	mutex_unlock(audpp->lock_dec);
+	return;
+
+}
+EXPORT_SYMBOL(audpp_adec_free);
+
+static ssize_t concurrency_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct audpp_state *audpp = &the_audpp_state;
+	int rc;
+	mutex_lock(audpp->lock_dec);
+	rc = sprintf(buf, "%ld\n", audpp->concurrency);
+	mutex_unlock(audpp->lock_dec);
+	return rc;
+}
+
+static ssize_t concurrency_store(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf, size_t count)
+{
+	struct audpp_state *audpp = &the_audpp_state;
+	unsigned long concurrency;
+	int rc = -1;
+	mutex_lock(audpp->lock_dec);
+	if (audpp->dec_inuse) {
+		MM_ERR("Can not change profile, while playback in progress\n");
+		goto done;
+	}
+	rc = strict_strtoul(buf, 10, &concurrency);
+	if (!rc &&
+		(concurrency < audpp->dec_database->num_concurrency_support)) {
+		audpp->concurrency = concurrency;
+		MM_DBG("Concurrency case %ld\n", audpp->concurrency);
+		rc = count;
+	} else {
+		MM_ERR("Not a valid Concurrency case\n");
+		rc = -EINVAL;
+	}
+done:
+	mutex_unlock(audpp->lock_dec);
+	return rc;
+}
+
+static ssize_t decoder_info_show(struct device *dev,
+				 struct device_attribute *attr, char *buf);
+static struct device_attribute dev_attr_decoder[AUDPP_MAX_DECODER_CNT] = {
+	__ATTR(decoder0, S_IRUGO, decoder_info_show, NULL),
+	__ATTR(decoder1, S_IRUGO, decoder_info_show, NULL),
+	__ATTR(decoder2, S_IRUGO, decoder_info_show, NULL),
+	__ATTR(decoder3, S_IRUGO, decoder_info_show, NULL),
+	__ATTR(decoder4, S_IRUGO, decoder_info_show, NULL),
+};
+
+static ssize_t decoder_info_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	int cpy_sz = 0;
+	struct audpp_state *audpp = &the_audpp_state;
+	const ptrdiff_t off = attr - dev_attr_decoder;	/* decoder number */
+	mutex_lock(audpp->lock_dec);
+	cpy_sz += scnprintf(buf + cpy_sz, PAGE_SIZE - cpy_sz, "%d:",
+			    audpp->dec_info_table[off].codec);
+	cpy_sz += scnprintf(buf + cpy_sz, PAGE_SIZE - cpy_sz, "%d\n",
+			    audpp->dec_info_table[off].pid);
+	mutex_unlock(audpp->lock_dec);
+	return cpy_sz;
+}
+
+static DEVICE_ATTR(concurrency, S_IWUSR | S_IRUGO, concurrency_show,
+	    concurrency_store);
+static int audpp_probe(struct platform_device *pdev)
+{
+	int rc, idx;
+	struct audpp_state *audpp = &the_audpp_state;
+	audpp->concurrency = AUDPP_CONCURRENCY_DEFAULT;
+	audpp->dec_database =
+	    (struct msm_adspdec_database *)pdev->dev.platform_data;
+
+	MM_INFO("Number of decoder supported %d\n",
+			audpp->dec_database->num_dec);
+	MM_INFO("Number of concurrency supported %d\n",
+			audpp->dec_database->num_concurrency_support);
+
+	init_waitqueue_head(&audpp->event_wait);
+
+	for (idx = 0; idx < audpp->dec_database->num_dec; idx++) {
+		audpp->dec_info_table[idx].codec = -1;
+		audpp->dec_info_table[idx].pid = 0;
+		MM_INFO("module_name:%s\n",
+			audpp->dec_database->dec_info_list[idx].module_name);
+		MM_INFO("queueid:%d\n",
+			audpp->dec_database->dec_info_list[idx].module_queueid);
+		MM_INFO("decid:%d\n",
+			audpp->dec_database->dec_info_list[idx].module_decid);
+		MM_INFO("nr_codec_support:%d\n",
+			audpp->dec_database->dec_info_list[idx].
+			nr_codec_support);
+	}
+
+	for (idx = 0; idx < audpp->dec_database->num_dec; idx++) {
+		rc = device_create_file(&pdev->dev, &dev_attr_decoder[idx]);
+		if (rc)
+			goto err;
+	}
+	rc = device_create_file(&pdev->dev, &dev_attr_concurrency);
+	if (rc)
+		goto err;
+	else
+		goto done;
+err:
+	while (idx--)
+		device_remove_file(&pdev->dev, &dev_attr_decoder[idx]);
+done:
+	return rc;
+}
+
+static struct platform_driver audpp_plat_driver = {
+	.probe = audpp_probe,
+	.driver = {
+		   .name = "msm_adspdec",
+		   .owner = THIS_MODULE,
+		   },
+};
+
+static int __init audpp_init(void)
+{
+	return platform_driver_register(&audpp_plat_driver);
+}
+
+device_initcall(audpp_init);
diff --git a/arch/arm/mach-msm/qdsp5/audpreproc.c b/arch/arm/mach-msm/qdsp5/audpreproc.c
new file mode 100644
index 0000000..230429f
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audpreproc.c
@@ -0,0 +1,169 @@
+/*
+ * Common code to deal with the AUDPREPROC dsp task (audio preprocessing)
+ *
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * Based on the audpp layer in arch/arm/mach-msm/qdsp5/audpp.c
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <mach/msm_adsp.h>
+#include <mach/debug_mm.h>
+#include <mach/qdsp5/qdsp5audpreproc.h>
+#include <mach/qdsp5/qdsp5audreccmdi.h>
+
+static DEFINE_MUTEX(audpreproc_lock);
+
+struct msm_adspenc_info {
+	const char *module_name;
+	unsigned module_queueids;
+	int module_encid; /* streamid */
+	int enc_formats; /* supported formats */
+	int nr_codec_support; /* number of codec suported */
+};
+
+#define ENC_MODULE_INFO(name, queueids, encid, formats, nr_codec) \
+	{.module_name = name, .module_queueids = queueids, \
+	 .module_encid = encid, .enc_formats = formats, \
+	 .nr_codec_support = nr_codec }
+
+#ifdef CONFIG_MSM7X27A_AUDIO
+#define ENC0_FORMAT ((1<<AUDREC_CMD_TYPE_1_INDEX_SBC)| \
+		(1<<AUDREC_CMD_TYPE_0_INDEX_AAC)| \
+		(1<<AUDREC_CMD_TYPE_0_INDEX_AMRNB)| \
+		(1<<AUDREC_CMD_TYPE_0_INDEX_EVRC)| \
+		(1<<AUDREC_CMD_TYPE_0_INDEX_QCELP))
+
+#define ENC1_FORMAT (1<<AUDREC_CMD_TYPE_0_INDEX_WAV)
+#else
+#define ENC0_FORMAT ((1<<AUDREC_CMD_TYPE_0_INDEX_WAV)| \
+		(1<<AUDREC_CMD_TYPE_1_INDEX_SBC)| \
+		(1<<AUDREC_CMD_TYPE_0_INDEX_AAC)| \
+		(1<<AUDREC_CMD_TYPE_0_INDEX_AMRNB)| \
+		(1<<AUDREC_CMD_TYPE_0_INDEX_EVRC)| \
+		(1<<AUDREC_CMD_TYPE_0_INDEX_QCELP))
+#endif
+
+struct msm_adspenc_database {
+	unsigned num_enc;
+	struct msm_adspenc_info *enc_info_list;
+};
+
+#ifdef CONFIG_MSM7X27A_AUDIO
+static struct msm_adspenc_info enc_info_list[] = {
+	ENC_MODULE_INFO("AUDRECTASK", \
+			((QDSP_uPAudRecBitStreamQueue << 16)| \
+			  QDSP_uPAudRecCmdQueue), 0, \
+			(ENC0_FORMAT | (1 << MSM_ADSP_ENC_MODE_TUNNEL) | \
+			(1 << MSM_ADSP_ENC_MODE_NON_TUNNEL)), 5),
+
+	ENC_MODULE_INFO("AUDREC1TASK", \
+			((QDSP_uPAudRec1BitStreamQueue << 16)| \
+			  QDSP_uPAudRec1CmdQueue), 1, \
+			(ENC1_FORMAT | (1 << MSM_ADSP_ENC_MODE_TUNNEL)), 1),
+};
+#else
+static struct msm_adspenc_info enc_info_list[] = {
+	ENC_MODULE_INFO("AUDRECTASK",
+			((QDSP_uPAudRecBitStreamQueue << 16)| \
+			  QDSP_uPAudRecCmdQueue), 0, \
+			(ENC0_FORMAT | (1 << MSM_ADSP_ENC_MODE_TUNNEL)), 6),
+};
+#endif
+
+static struct msm_adspenc_database msm_enc_database = {
+	.num_enc = ARRAY_SIZE(enc_info_list),
+	.enc_info_list = enc_info_list,
+};
+
+struct audpreproc_state {
+	struct msm_adsp_module *mod;
+	struct mutex *lock;
+	unsigned open_count;
+	unsigned enc_inuse;
+};
+
+static struct audpreproc_state the_audpreproc_state = {
+	.lock = &audpreproc_lock,
+};
+
+/* enc_type = supported encode format *
+ * like pcm, aac, sbc, evrc, qcelp, amrnb etc ... *
+ */
+int audpreproc_aenc_alloc(unsigned enc_type, const char **module_name,
+		     unsigned *queue_ids)
+{
+	struct audpreproc_state *audpreproc = &the_audpreproc_state;
+	int encid = -1, idx, lidx, mode, codec;
+	int codecs_supported, min_codecs_supported;
+
+	mutex_lock(audpreproc->lock);
+	/* Represents in bit mask */
+	mode = ((enc_type & AUDPREPROC_MODE_MASK) << 16);
+	codec = (1 << (enc_type & AUDPREPROC_CODEC_MASK));
+
+	lidx = msm_enc_database.num_enc;
+	min_codecs_supported = sizeof(unsigned int) * 8;
+	MM_DBG("mode = 0x%08x codec = 0x%08x\n", mode, codec);
+
+	for (idx = lidx-1; idx >= 0; idx--) {
+		/* encoder free and supports the format */
+		if (!(audpreproc->enc_inuse & (1 << (idx))) &&
+		((mode & msm_enc_database.enc_info_list[idx].enc_formats)
+		== mode) && ((codec &
+		msm_enc_database.enc_info_list[idx].enc_formats)
+		== codec)){
+			/* Check supports minimum number codecs */
+			codecs_supported =
+			msm_enc_database.enc_info_list[idx].nr_codec_support;
+			if (codecs_supported < min_codecs_supported) {
+				lidx = idx;
+				min_codecs_supported = codecs_supported;
+			}
+		}
+	}
+
+	if (lidx < msm_enc_database.num_enc) {
+		audpreproc->enc_inuse |= (1 << lidx);
+		*module_name =
+		    msm_enc_database.enc_info_list[lidx].module_name;
+		*queue_ids =
+		    msm_enc_database.enc_info_list[lidx].module_queueids;
+		encid = msm_enc_database.enc_info_list[lidx].module_encid;
+	}
+
+	mutex_unlock(audpreproc->lock);
+	return encid;
+}
+EXPORT_SYMBOL(audpreproc_aenc_alloc);
+
+void audpreproc_aenc_free(int enc_id)
+{
+	struct audpreproc_state *audpreproc = &the_audpreproc_state;
+	int idx;
+
+	mutex_lock(audpreproc->lock);
+	for (idx = 0; idx < msm_enc_database.num_enc; idx++) {
+		if (msm_enc_database.enc_info_list[idx].module_encid ==
+		    enc_id) {
+			audpreproc->enc_inuse &= ~(1 << idx);
+			break;
+		}
+	}
+	mutex_unlock(audpreproc->lock);
+	return;
+
+}
+EXPORT_SYMBOL(audpreproc_aenc_free);
diff --git a/arch/arm/mach-msm/qdsp5/audrec.c b/arch/arm/mach-msm/qdsp5/audrec.c
new file mode 100644
index 0000000..d5cb168
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/audrec.c
@@ -0,0 +1,272 @@
+/* arch/arm/mach-msm/qdsp5/audrec.c
+ *
+ * common code to deal with the AUDREC dsp task (audio recording)
+ *
+ * Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * Based on the audpp layer in arch/arm/mach-msm/qdsp5/audpp.c
+ *
+ * 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/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 <mach/qdsp5/qdsp5audreccmdi.h>
+#include <mach/qdsp5/qdsp5audrecmsg.h>
+
+#include "audmgr.h"
+#include <mach/debug_mm.h>
+
+static DEFINE_MUTEX(audrec_lock);
+
+#define MAX_ENC_COUNT 8 /* Max encoder supported */
+
+#define ENC_SESSION_FREE 0
+#define ENC_SESSION_ACTIVE 1
+
+struct enc_session {
+	unsigned enc_type;  /* Param to identify type of encoder */
+	unsigned audrec_obj_idx;  /* Param to identify REC_OBJ or Session ID */
+	audrec_event_func event_func; /* Event Call back
+					routine for the encoder */
+	void *private;	/* private data element passed as
+				part of Event Call back  routine */
+	unsigned state; /* Current state of the encoder session ,
+				free, active*/
+};
+
+struct audrec_state {
+	struct msm_adsp_module *audrec_mod;
+	struct enc_session enc_session[MAX_ENC_COUNT];
+	struct mutex *lock;
+	unsigned enc_count;
+};
+
+struct audrec_state the_audrec_state = {
+	.lock = &audrec_lock,
+};
+
+int audrectask_send_cmdqueue(void *cmd, unsigned len)
+{
+	return msm_adsp_write(the_audrec_state.audrec_mod,
+				QDSP_uPAudRecCmdQueue, cmd, len);
+}
+EXPORT_SYMBOL(audrectask_send_cmdqueue);
+
+int audrectask_send_bitstreamqueue(void *cmd, unsigned len)
+{
+	return msm_adsp_write(the_audrec_state.audrec_mod,
+				QDSP_uPAudRecBitStreamQueue, cmd, len);
+}
+EXPORT_SYMBOL(audrectask_send_bitstreamqueue);
+
+static void audrectask_dsp_event(void *data, unsigned id, size_t len,
+			    void (*getevent)(void *ptr, size_t len))
+{
+	struct audrec_state *audrec = data;
+	int cnt;
+	uint16_t msg[5]; /* Max size of message */
+	getevent(msg, len);
+
+	switch (id) {
+	case AUDREC_MSG_CMD_CFG_DONE_MSG: {
+		MM_DBG("CMD CFG DONE %x\n", msg[1]);
+		if (msg[0] & AUDREC_MSG_CFG_DONE_ENC_ENA) {
+			for (cnt = 0; cnt < MAX_ENC_COUNT ; cnt++) {
+				if (audrec->enc_session[cnt].enc_type ==
+					(msg[0] & AUDREC_CMD_ENC_TYPE_MASK)) {
+					audrec->enc_session[cnt].audrec_obj_idx
+					 = msg[1];
+					audrec->enc_session[cnt].event_func(
+					audrec->enc_session[cnt].private, id,
+					msg);
+					break;
+				}
+			}
+		} else {
+			for (cnt = 0; cnt < MAX_ENC_COUNT ; cnt++) {
+				if (audrec->enc_session[cnt].enc_type ==
+					(msg[0] & AUDREC_CMD_ENC_TYPE_MASK)) {
+					audrec->enc_session[cnt].event_func(
+					audrec->enc_session[cnt].private, id,
+					msg);
+					audrec->enc_session[cnt].audrec_obj_idx
+					= 0xFFFFFFFF;
+					audrec->enc_session[cnt].state
+					= ENC_SESSION_FREE;
+					audrec->enc_session[cnt].enc_type
+					= 0xFFFFFFFF;
+					audrec->enc_session[cnt].event_func
+					= NULL;
+					audrec->enc_session[cnt].private
+					= NULL;
+					break;
+				}
+			}
+		}
+		break;
+	}
+	case AUDREC_MSG_CMD_AREC_MEM_CFG_DONE_MSG: {
+		MM_DBG("CMD AREC MEM CFG DONE %x\n", msg[0]);
+		for (cnt = 0; cnt < MAX_ENC_COUNT ; cnt++) {
+			if (audrec->enc_session[cnt].audrec_obj_idx ==
+				msg[0]) {
+				audrec->enc_session[cnt].event_func(
+				audrec->enc_session[cnt].private, id, msg);
+				break;
+			}
+		}
+		break;
+	}
+	case AUDREC_MSG_CMD_AREC_PARAM_CFG_DONE_MSG: {
+		MM_DBG("CMD AREC PARAM CFG DONE %x\n", msg[0]);
+		for (cnt = 0; cnt < MAX_ENC_COUNT ; cnt++) {
+			if (audrec->enc_session[cnt].audrec_obj_idx ==
+				msg[0]) {
+				audrec->enc_session[cnt].event_func(
+				audrec->enc_session[cnt].private, id, msg);
+				break;
+			}
+		}
+		break;
+	}
+	case AUDREC_MSG_PACKET_READY_MSG: {
+		MM_DBG("PCK READY %x\n", msg[0]);
+		for (cnt = 0; cnt < MAX_ENC_COUNT ; cnt++) {
+			if (audrec->enc_session[cnt].audrec_obj_idx ==
+				msg[0]) {
+				audrec->enc_session[cnt].event_func(
+				audrec->enc_session[cnt].private, id, msg);
+				break;
+			}
+		}
+		break;
+	}
+	case AUDREC_MSG_FATAL_ERR_MSG: {
+		MM_ERR("ERROR %x\n", msg[0]);
+		if (msg[1] & AUDREC_MSG_FATAL_ERR_TYPE_0) {
+			for (cnt = 0; cnt < MAX_ENC_COUNT ; cnt++) {
+				if (audrec->enc_session[cnt].audrec_obj_idx ==
+					msg[0]) {
+					audrec->enc_session[cnt].event_func(
+					audrec->enc_session[cnt].private, id,
+					msg);
+				break;
+				}
+			}
+		} else if (msg[1] & AUDREC_MSG_FATAL_ERR_TYPE_1) {
+			cnt = audrec->enc_count-1;
+			if (audrec->enc_session[cnt].event_func)
+				audrec->enc_session[cnt].event_func(
+				audrec->enc_session[cnt].private, id,
+				msg);
+		}
+		break;
+	}
+	case ADSP_MESSAGE_ID:
+		MM_DBG("Received ADSP event: module \
+				enable/disable(audrectask)\n");
+		break;
+	default:
+		MM_ERR("unknown event %d\n", id);
+	}
+}
+
+static struct msm_adsp_ops adsp_ops = {
+	.event = audrectask_dsp_event,
+};
+
+int audrectask_enable(unsigned enc_type, audrec_event_func func, void *private)
+{
+	struct audrec_state *audrec = &the_audrec_state;
+	int cnt, rc = 0;
+
+	mutex_lock(audrec->lock);
+
+	if (audrec->enc_count++ == 0) {
+		MM_DBG("enable\n");
+		for (cnt = 0; cnt < MAX_ENC_COUNT ; cnt++) {
+			if (audrec->enc_session[cnt].state ==
+				ENC_SESSION_FREE) {
+				audrec->enc_session[cnt].state =
+				ENC_SESSION_ACTIVE;
+				audrec->enc_session[cnt].enc_type = enc_type;
+				audrec->enc_session[cnt].event_func = func;
+				audrec->enc_session[cnt].private = private;
+				break;
+			}
+		}
+		rc = msm_adsp_get("AUDRECTASK", &audrec->audrec_mod, &adsp_ops,
+					audrec);
+		if (rc < 0) {
+			MM_ERR("cannot open AUDRECTASK\n");
+			audrec->enc_count = 0;
+			audrec->enc_session[cnt].state = ENC_SESSION_FREE;
+			audrec->enc_session[cnt].enc_type = 0xFFFFFFFF;
+			audrec->enc_session[cnt].event_func = NULL;
+			audrec->enc_session[cnt].private = NULL;
+			goto out;
+		}
+		msm_adsp_enable(audrec->audrec_mod);
+	} else {
+		for (cnt = 0; cnt < MAX_ENC_COUNT ; cnt++) {
+			if (audrec->enc_session[cnt].state ==
+				ENC_SESSION_FREE) {
+				audrec->enc_session[cnt].state =
+				ENC_SESSION_ACTIVE;
+				audrec->enc_session[cnt].enc_type = enc_type;
+				audrec->enc_session[cnt].event_func = func;
+				audrec->enc_session[cnt].private = private;
+				break;
+			}
+		}
+	}
+	if (cnt == MAX_ENC_COUNT)
+		rc = -EBUSY;
+	else
+		rc = 0;
+
+out:
+	mutex_unlock(audrec->lock);
+	return rc;
+}
+EXPORT_SYMBOL(audrectask_enable);
+
+void audrectask_disable(unsigned enc_type, void *private)
+{
+	struct audrec_state *audrec = &the_audrec_state;
+
+	mutex_lock(audrec->lock);
+
+	if (--audrec->enc_count == 0) {
+		MM_DBG("\n"); /* Macro prints the file name and function */
+		msm_adsp_disable(audrec->audrec_mod);
+		msm_adsp_put(audrec->audrec_mod);
+		audrec->audrec_mod = NULL;
+	}
+
+	mutex_unlock(audrec->lock);
+}
+EXPORT_SYMBOL(audrectask_disable);
+
diff --git a/arch/arm/mach-msm/qdsp5/evlog.h b/arch/arm/mach-msm/qdsp5/evlog.h
new file mode 100644
index 0000000..1f0f16b
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/evlog.h
@@ -0,0 +1,125 @@
+/* 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 {
+	struct timespec 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;
+	struct timespec now, t;
+	
+	max = log->max;
+	getnstimeofday(&now);
+	local_irq_save(flags);
+	n = (log->next - 1) & (max - 1);
+	entry = log->entry;
+	while (n != log->next) {
+		t = timespec_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,
+					  "%lu.%03lu %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;
+	getnstimeofday(&entry->when);
+	entry->id = id;
+	entry->arg = arg;
+	log->next = (log->next + 1) & (log->max - 1);
+done:
+	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/arch/arm/mach-msm/qdsp5/snd.c b/arch/arm/mach-msm/qdsp5/snd.c
new file mode 100644
index 0000000..f1db012
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/snd.c
@@ -0,0 +1,675 @@
+/* arch/arm/mach-msm/qdsp5/snd.c
+ *
+ * interface to "snd" service on the baseband cpu
+ *
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * 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 <linux/seq_file.h>
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/board.h>
+#include <mach/msm_rpcrouter.h>
+#include <mach/debug_mm.h>
+
+struct snd_ctxt {
+	struct mutex lock;
+	int opened;
+	struct msm_rpc_endpoint *ept;
+	struct msm_snd_endpoints *snd_epts;
+};
+
+struct snd_sys_ctxt {
+	struct mutex lock;
+	struct msm_rpc_endpoint *ept;
+};
+
+static struct snd_sys_ctxt the_snd_sys;
+
+static struct snd_ctxt the_snd;
+
+#define RPC_SND_PROG	0x30000002
+#define RPC_SND_CB_PROG	0x31000002
+
+#define RPC_SND_VERS                    0x00020001
+#define RPC_SND_VERS2                    0x00030001
+
+#define SND_SET_DEVICE_PROC 2
+#define SND_SET_VOLUME_PROC 3
+#define SND_AVC_CTL_PROC 29
+#define SND_AGC_CTL_PROC 30
+
+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 rpc_snd_avc_ctl_args {
+	uint32_t avc_ctl;
+	uint32_t cb_func;
+	uint32_t client_data;
+};
+
+struct rpc_snd_agc_ctl_args {
+	uint32_t agc_ctl;
+	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_avc_ctl_msg {
+	struct rpc_request_hdr hdr;
+	struct rpc_snd_avc_ctl_args args;
+};
+
+struct snd_agc_ctl_msg {
+	struct rpc_request_hdr hdr;
+	struct rpc_snd_agc_ctl_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))) {
+		MM_ERR("snd_ioctl get endpoint: invalid read pointer\n");
+		return -EFAULT;
+	}
+
+	index = ept.id;
+	if (index < 0 || index >= snd->snd_epts->num) {
+		MM_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))) {
+		MM_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 snd_avc_ctl_msg avc_msg;
+	struct snd_agc_ctl_msg agc_msg;
+
+	struct msm_snd_device_config dev;
+	struct msm_snd_volume_config vol;
+	struct snd_ctxt *snd = file->private_data;
+	int rc = 0;
+
+	uint32_t avc, agc;
+
+	mutex_lock(&snd->lock);
+	switch (cmd) {
+	case SND_SET_DEVICE:
+		if (copy_from_user(&dev, (void __user *) arg, sizeof(dev))) {
+			MM_ERR("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) {
+			MM_ERR("set device: invalid mute status\n");
+			rc = -EINVAL;
+			break;
+		}
+		dmsg.args.cb_func = -1;
+		dmsg.args.client_data = 0;
+
+		MM_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))) {
+			MM_ERR("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) {
+			MM_ERR("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;
+
+		MM_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_AVC_CTL:
+		if (get_user(avc, (uint32_t __user *) arg)) {
+			rc = -EFAULT;
+			break;
+		} else if ((avc != 1) && (avc != 0)) {
+			rc = -EINVAL;
+			break;
+		}
+
+		avc_msg.args.avc_ctl = cpu_to_be32(avc);
+		avc_msg.args.cb_func = -1;
+		avc_msg.args.client_data = 0;
+
+		MM_INFO("snd_avc_ctl %d\n", avc);
+
+		rc = msm_rpc_call(snd->ept,
+			SND_AVC_CTL_PROC,
+			&avc_msg, sizeof(avc_msg), 5 * HZ);
+		break;
+
+	case SND_AGC_CTL:
+		if (get_user(agc, (uint32_t __user *) arg)) {
+			rc = -EFAULT;
+			break;
+		} else if ((agc != 1) && (agc != 0)) {
+			rc = -EINVAL;
+			break;
+		}
+		agc_msg.args.agc_ctl = cpu_to_be32(agc);
+		agc_msg.args.cb_func = -1;
+		agc_msg.args.client_data = 0;
+
+		MM_INFO("snd_agc_ctl %d\n", agc);
+
+		rc = msm_rpc_call(snd->ept,
+			SND_AGC_CTL_PROC,
+			&agc_msg, sizeof(agc_msg), 5 * HZ);
+		break;
+
+	case SND_GET_NUM_ENDPOINTS:
+		if (copy_to_user((void __user *)arg,
+				&snd->snd_epts->num, sizeof(unsigned))) {
+			MM_ERR("get endpoint: invalid pointer\n");
+			rc = -EFAULT;
+		}
+		break;
+
+	case SND_GET_ENDPOINT:
+		rc = get_endpoint(snd, arg);
+		break;
+
+	default:
+		MM_ERR("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;
+	int rc;
+
+	mutex_lock(&snd->lock);
+	rc = msm_rpc_close(snd->ept);
+	if (rc < 0)
+		MM_ERR("msm_rpc_close failed\n");
+	snd->ept = NULL;
+	snd->opened = 0;
+	mutex_unlock(&snd->lock);
+	return 0;
+}
+static int snd_sys_release(void)
+{
+	struct snd_sys_ctxt *snd_sys = &the_snd_sys;
+	int rc = 0;
+
+	mutex_lock(&snd_sys->lock);
+	rc = msm_rpc_close(snd_sys->ept);
+	if (rc < 0)
+		MM_ERR("msm_rpc_close failed\n");
+	snd_sys->ept = NULL;
+	mutex_unlock(&snd_sys->lock);
+	return rc;
+}
+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_compatible(RPC_SND_PROG,
+					RPC_SND_VERS, 0);
+			if (IS_ERR(snd->ept)) {
+				MM_DBG("connect failed with current VERS \
+					= %x, trying again with another API\n",
+					RPC_SND_VERS2);
+				snd->ept =
+					msm_rpc_connect_compatible(RPC_SND_PROG,
+							RPC_SND_VERS2, 0);
+			}
+			if (IS_ERR(snd->ept)) {
+				rc = PTR_ERR(snd->ept);
+				snd->ept = NULL;
+				MM_ERR("failed to connect snd svc\n");
+				goto err;
+			}
+		}
+		file->private_data = snd;
+		snd->opened = 1;
+	} else {
+		MM_ERR("snd already opened\n");
+		rc = -EBUSY;
+	}
+
+err:
+	mutex_unlock(&snd->lock);
+	return rc;
+}
+static int snd_sys_open(void)
+{
+	struct snd_sys_ctxt *snd_sys = &the_snd_sys;
+	int rc = 0;
+
+	mutex_lock(&snd_sys->lock);
+	if (snd_sys->ept == NULL) {
+		snd_sys->ept = msm_rpc_connect_compatible(RPC_SND_PROG,
+			RPC_SND_VERS, 0);
+		if (IS_ERR(snd_sys->ept)) {
+			MM_DBG("connect failed with current VERS \
+				= %x, trying again with another API\n",
+				RPC_SND_VERS2);
+			snd_sys->ept = msm_rpc_connect_compatible(RPC_SND_PROG,
+					RPC_SND_VERS2, 0);
+		}
+		if (IS_ERR(snd_sys->ept)) {
+			rc = PTR_ERR(snd_sys->ept);
+			snd_sys->ept = NULL;
+			MM_ERR("failed to connect snd svc\n");
+			goto err;
+		}
+	} else
+		MM_DBG("snd already opened\n");
+
+err:
+	mutex_unlock(&snd_sys->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 long snd_agc_enable(unsigned long arg)
+{
+	struct snd_sys_ctxt *snd_sys = &the_snd_sys;
+	struct snd_agc_ctl_msg agc_msg;
+	int rc = 0;
+
+	if ((arg != 1) && (arg != 0))
+		return -EINVAL;
+
+	agc_msg.args.agc_ctl = cpu_to_be32(arg);
+	agc_msg.args.cb_func = -1;
+	agc_msg.args.client_data = 0;
+
+	MM_DBG("snd_agc_ctl %ld,%d\n", arg, agc_msg.args.agc_ctl);
+
+	rc = msm_rpc_call(snd_sys->ept,
+		SND_AGC_CTL_PROC,
+		&agc_msg, sizeof(agc_msg), 5 * HZ);
+	return rc;
+}
+
+static long snd_avc_enable(unsigned long arg)
+{
+	struct snd_sys_ctxt *snd_sys = &the_snd_sys;
+	struct snd_avc_ctl_msg avc_msg;
+	int rc = 0;
+
+	if ((arg != 1) && (arg != 0))
+		return -EINVAL;
+
+	avc_msg.args.avc_ctl = cpu_to_be32(arg);
+
+	avc_msg.args.cb_func = -1;
+	avc_msg.args.client_data = 0;
+
+	MM_DBG("snd_avc_ctl %ld,%d\n", arg, avc_msg.args.avc_ctl);
+
+	rc = msm_rpc_call(snd_sys->ept,
+		SND_AVC_CTL_PROC,
+		&avc_msg, sizeof(avc_msg), 5 * HZ);
+	return rc;
+}
+
+static ssize_t snd_agc_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	ssize_t status;
+	struct snd_sys_ctxt *snd_sys = &the_snd_sys;
+	int rc = 0;
+
+	rc = snd_sys_open();
+	if (rc)
+		return rc;
+
+	mutex_lock(&snd_sys->lock);
+
+	if (sysfs_streq(buf, "enable"))
+		status = snd_agc_enable(1);
+	else if (sysfs_streq(buf, "disable"))
+		status = snd_agc_enable(0);
+	else
+		status = -EINVAL;
+
+	mutex_unlock(&snd_sys->lock);
+	rc = snd_sys_release();
+	if (rc)
+		return rc;
+
+	return status ? : size;
+}
+
+static ssize_t snd_avc_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	ssize_t status;
+	struct snd_sys_ctxt *snd_sys = &the_snd_sys;
+	int rc = 0;
+
+	rc = snd_sys_open();
+	if (rc)
+		return rc;
+
+	mutex_lock(&snd_sys->lock);
+
+	if (sysfs_streq(buf, "enable"))
+		status = snd_avc_enable(1);
+	else if (sysfs_streq(buf, "disable"))
+		status = snd_avc_enable(0);
+	else
+		status = -EINVAL;
+
+	mutex_unlock(&snd_sys->lock);
+	rc = snd_sys_release();
+	if (rc)
+		return rc;
+
+	return status ? : size;
+}
+
+static long snd_vol_enable(const char *arg)
+{
+	struct snd_sys_ctxt *snd_sys = &the_snd_sys;
+	struct snd_set_volume_msg vmsg;
+	struct msm_snd_volume_config vol;
+	int rc = 0;
+
+	rc = sscanf(arg, "%d %d %d", &vol.device, &vol.method, &vol.volume);
+	if (rc != 3) {
+		MM_ERR("Invalid arguments. Usage: <device> <method> \
+				<volume>\n");
+		rc = -EINVAL;
+		return rc;
+	}
+
+	vmsg.args.device = cpu_to_be32(vol.device);
+	vmsg.args.method = cpu_to_be32(vol.method);
+	if (vol.method != SND_METHOD_VOICE) {
+		MM_ERR("snd_ioctl set volume: invalid method\n");
+		rc = -EINVAL;
+		return rc;
+	}
+
+	vmsg.args.volume = cpu_to_be32(vol.volume);
+	vmsg.args.cb_func = -1;
+	vmsg.args.client_data = 0;
+
+	MM_DBG("snd_set_volume %d %d %d\n", vol.device, vol.method,
+			vol.volume);
+
+	rc = msm_rpc_call(snd_sys->ept,
+		SND_SET_VOLUME_PROC,
+		&vmsg, sizeof(vmsg), 5 * HZ);
+	return rc;
+}
+
+static long snd_dev_enable(const char *arg)
+{
+	struct snd_sys_ctxt *snd_sys = &the_snd_sys;
+	struct snd_set_device_msg dmsg;
+	struct msm_snd_device_config dev;
+	int rc = 0;
+
+	rc = sscanf(arg, "%d %d %d", &dev.device, &dev.ear_mute, &dev.mic_mute);
+	if (rc != 3) {
+		MM_ERR("Invalid arguments. Usage: <device> <ear_mute> \
+				<mic_mute>\n");
+		rc = -EINVAL;
+		return rc;
+	}
+	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) {
+		MM_ERR("snd_ioctl set device: invalid mute status\n");
+		rc = -EINVAL;
+		return rc;
+	}
+	dmsg.args.cb_func = -1;
+	dmsg.args.client_data = 0;
+
+	MM_INFO("snd_set_device %d %d %d\n", dev.device, dev.ear_mute,
+			dev.mic_mute);
+
+	rc = msm_rpc_call(snd_sys->ept,
+		SND_SET_DEVICE_PROC,
+		&dmsg, sizeof(dmsg), 5 * HZ);
+	return rc;
+}
+
+static ssize_t snd_dev_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	ssize_t status;
+	struct snd_sys_ctxt *snd_sys = &the_snd_sys;
+	int rc = 0;
+
+	rc = snd_sys_open();
+	if (rc)
+		return rc;
+
+	mutex_lock(&snd_sys->lock);
+	status = snd_dev_enable(buf);
+	mutex_unlock(&snd_sys->lock);
+
+	rc = snd_sys_release();
+	if (rc)
+		return rc;
+
+	return status ? : size;
+}
+
+static ssize_t snd_vol_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	ssize_t status;
+	struct snd_sys_ctxt *snd_sys = &the_snd_sys;
+	int rc = 0;
+
+	rc = snd_sys_open();
+	if (rc)
+		return rc;
+
+	mutex_lock(&snd_sys->lock);
+	status = snd_vol_enable(buf);
+	mutex_unlock(&snd_sys->lock);
+
+	rc = snd_sys_release();
+	if (rc)
+		return rc;
+
+	return status ? : size;
+}
+
+static DEVICE_ATTR(agc, S_IWUSR | S_IRUGO,
+		NULL, snd_agc_store);
+
+static DEVICE_ATTR(avc, S_IWUSR | S_IRUGO,
+		NULL, snd_avc_store);
+
+static DEVICE_ATTR(device, S_IWUSR | S_IRUGO,
+		NULL, snd_dev_store);
+
+static DEVICE_ATTR(volume, S_IWUSR | S_IRUGO,
+		NULL, snd_vol_store);
+
+static int snd_probe(struct platform_device *pdev)
+{
+	struct snd_ctxt *snd = &the_snd;
+	struct snd_sys_ctxt *snd_sys = &the_snd_sys;
+	int rc = 0;
+
+	mutex_init(&snd->lock);
+	mutex_init(&snd_sys->lock);
+	snd_sys->ept = NULL;
+	snd->snd_epts = (struct msm_snd_endpoints *)pdev->dev.platform_data;
+	rc = misc_register(&snd_misc);
+	if (rc)
+		return rc;
+
+	rc = device_create_file(snd_misc.this_device, &dev_attr_agc);
+	if (rc) {
+		misc_deregister(&snd_misc);
+		return rc;
+	}
+
+	rc = device_create_file(snd_misc.this_device, &dev_attr_avc);
+	if (rc) {
+		device_remove_file(snd_misc.this_device,
+						&dev_attr_agc);
+		misc_deregister(&snd_misc);
+		return rc;
+	}
+
+	rc = device_create_file(snd_misc.this_device, &dev_attr_device);
+	if (rc) {
+		device_remove_file(snd_misc.this_device,
+						&dev_attr_agc);
+		device_remove_file(snd_misc.this_device,
+						&dev_attr_avc);
+		misc_deregister(&snd_misc);
+		return rc;
+	}
+
+	rc = device_create_file(snd_misc.this_device, &dev_attr_volume);
+	if (rc) {
+		device_remove_file(snd_misc.this_device,
+						&dev_attr_agc);
+		device_remove_file(snd_misc.this_device,
+						&dev_attr_avc);
+		device_remove_file(snd_misc.this_device,
+						&dev_attr_device);
+		misc_deregister(&snd_misc);
+	}
+
+	return rc;
+}
+
+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);
diff --git a/arch/arm/mach-msm/qdsp5/snd_adie.c b/arch/arm/mach-msm/qdsp5/snd_adie.c
new file mode 100644
index 0000000..ba7efc3
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5/snd_adie.c
@@ -0,0 +1,480 @@
+/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+#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 <mach/msm_rpcrouter.h>
+#include <linux/debugfs.h>
+#include <mach/qdsp5/snd_adie.h>
+#include <mach/debug_mm.h>
+
+static struct adie_svc_client adie_client[ADIE_SVC_MAX_CLIENTS];
+static DEFINE_MUTEX(adie_client_lock);
+
+static int adie_svc_process_cb(struct msm_rpc_client *client,
+				 void *buffer, int in_size)
+{
+	int rc, id;
+	uint32_t accept_status;
+	struct rpc_request_hdr *req;
+	struct adie_svc_client_register_cb_cb_args arg, *buf_ptr;
+
+	req = (struct rpc_request_hdr *)buffer;
+	for (id = 0; id < ADIE_SVC_MAX_CLIENTS; id++) {
+		if (adie_client[id].rpc_client == client)
+			break;
+	}
+	if (id == ADIE_SVC_MAX_CLIENTS) {
+		MM_ERR("RPC reply with invalid rpc client\n");
+		accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR;
+		goto err;
+	}
+
+	buf_ptr = (struct adie_svc_client_register_cb_cb_args *)(req + 1);
+	arg.cb_id		= be32_to_cpu(buf_ptr->cb_id);
+	arg.size		= be32_to_cpu(buf_ptr->size);
+	arg.client_id		= be32_to_cpu(buf_ptr->client_id);
+	arg.adie_block		= be32_to_cpu(buf_ptr->adie_block);
+	arg.status		= be32_to_cpu(buf_ptr->status);
+	arg.client_operation	= be32_to_cpu(buf_ptr->client_operation);
+
+	if (arg.cb_id != adie_client[id].cb_id) {
+		MM_ERR("RPC reply with invalid invalid cb_id\n");
+		accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR;
+		goto err;
+	}
+
+	mutex_lock(&adie_client[id].lock);
+	switch (arg.client_operation) {
+	case ADIE_SVC_REGISTER_CLIENT:
+		MM_DBG("ADIE_SVC_REGISTER_CLIENT callback\n");
+		adie_client[id].client_id = arg.client_id;
+		break;
+	case ADIE_SVC_DEREGISTER_CLIENT:
+		MM_DBG("ADIE_SVC_DEREGISTER_CLIENT callback\n");
+		break;
+	case ADIE_SVC_CONFIG_ADIE_BLOCK:
+		MM_DBG("ADIE_SVC_CONFIG_ADIE_BLOCK callback\n");
+		if (adie_client[id].client_id != arg.client_id) {
+			mutex_unlock(&adie_client[id].lock);
+			accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR;
+			goto err;
+		}
+		break;
+	default:
+		accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR;
+		goto err;
+	}
+
+	adie_client[id].status = arg.status;
+	adie_client[id].adie_svc_cb_done = 1;
+	mutex_unlock(&adie_client[id].lock);
+	wake_up(&adie_client[id].wq);
+	accept_status = RPC_ACCEPTSTAT_SUCCESS;
+
+err:
+	msm_rpc_start_accepted_reply(client, be32_to_cpu(req->xid),
+				     accept_status);
+	rc = msm_rpc_send_accepted_reply(client, 0);
+	if (rc)
+		MM_ERR("%s: send accepted reply failed: %d\n", __func__, rc);
+
+	return rc;
+}
+
+static int adie_svc_rpc_cb_func(struct msm_rpc_client *client,
+			    void *buffer, int in_size)
+{
+	int rc = 0;
+	struct rpc_request_hdr *req;
+
+	req = (struct rpc_request_hdr *)buffer;
+
+	MM_DBG("procedure received to rpc cb %d\n",
+			be32_to_cpu(req->procedure));
+	switch (be32_to_cpu(req->procedure)) {
+	case ADIE_SVC_CLIENT_STATUS_FUNC_PTR_TYPE_PROC:
+		rc = adie_svc_process_cb(client, buffer, in_size);
+		break;
+	default:
+		MM_ERR("%s: procedure not supported %d\n", __func__,
+		       be32_to_cpu(req->procedure));
+		msm_rpc_start_accepted_reply(client, be32_to_cpu(req->xid),
+					     RPC_ACCEPTSTAT_PROC_UNAVAIL);
+		rc = msm_rpc_send_accepted_reply(client, 0);
+		if (rc)
+			MM_ERR("%s: sending reply failed: %d\n", __func__, rc);
+		break;
+	}
+	return rc;
+}
+
+static int adie_svc_client_register_arg(struct msm_rpc_client *client,
+		void *buf, void *data)
+{
+	struct adie_svc_client_register_cb_args *arg;
+
+	arg = (struct adie_svc_client_register_cb_args *)data;
+
+	*((int *)buf) = cpu_to_be32((int)arg->cb_id);
+	return sizeof(int);
+}
+
+static int adie_svc_client_deregister_arg(struct msm_rpc_client *client,
+		void *buf, void *data)
+{
+	struct adie_svc_client_deregister_cb_args *arg;
+
+	arg = (struct adie_svc_client_deregister_cb_args *)data;
+
+	*((int *)buf) = cpu_to_be32(arg->client_id);
+	return sizeof(int);
+}
+
+static int adie_svc_config_adie_block_arg(struct msm_rpc_client *client,
+		void *buf, void *data)
+{
+	struct adie_svc_config_adie_block_cb_args *arg;
+	int size = 0;
+
+	arg = (struct adie_svc_config_adie_block_cb_args *)data;
+
+	*((int *)buf) = cpu_to_be32(arg->client_id);
+	size += sizeof(int);
+	buf += sizeof(int);
+
+	*((int *)buf) = cpu_to_be32(arg->adie_block);
+	size += sizeof(int);
+	buf += sizeof(int);
+
+	*((int *)buf) = cpu_to_be32(arg->config);
+	size += sizeof(int);
+
+	return size;
+}
+
+/* Returns : client id on success
+ *           and -1 on failure
+ */
+int adie_svc_get(void)
+{
+	int id, rc = 0;
+	struct adie_svc_client_register_cb_args arg;
+
+	mutex_lock(&adie_client_lock);
+	for (id = 0; id < ADIE_SVC_MAX_CLIENTS; id++) {
+		if (adie_client[id].client_id == -1 &&
+				adie_client[id].rpc_client == NULL)
+			break;
+	}
+	if (id == ADIE_SVC_MAX_CLIENTS) {
+		mutex_unlock(&adie_client_lock);
+		return -1;
+	}
+
+	mutex_lock(&adie_client[id].lock);
+	adie_client[id].rpc_client = msm_rpc_register_client("adie_client",
+							ADIE_SVC_PROG,
+							ADIE_SVC_VERS, 1,
+							adie_svc_rpc_cb_func);
+	if (IS_ERR(adie_client[id].rpc_client)) {
+		MM_ERR("Failed to register RPC client\n");
+		adie_client[id].rpc_client = NULL;
+		mutex_unlock(&adie_client[id].lock);
+		mutex_unlock(&adie_client_lock);
+		return -1;
+	}
+	mutex_unlock(&adie_client_lock);
+
+	adie_client[id].adie_svc_cb_done = 0;
+	arg.cb_id = id;
+	adie_client[id].cb_id = arg.cb_id;
+	mutex_unlock(&adie_client[id].lock);
+	rc = msm_rpc_client_req(adie_client[id].rpc_client,
+				SND_ADIE_SVC_CLIENT_REGISTER_PROC,
+				adie_svc_client_register_arg, &arg,
+					NULL, NULL, -1);
+	if (!rc) {
+		rc = wait_event_interruptible(adie_client[id].wq,
+				adie_client[id].adie_svc_cb_done);
+		mutex_lock(&adie_client[id].lock);
+		if (unlikely(rc < 0)) {
+			if (rc == -ERESTARTSYS)
+				MM_ERR("wait_event_interruptible "
+						"returned -ERESTARTSYS\n");
+			else
+				MM_ERR("wait_event_interruptible "
+						"returned error\n");
+			rc = -1;
+			goto err;
+		}
+		MM_DBG("Status %d received from CB function, id %d rc %d\n",
+		       adie_client[id].status, adie_client[id].client_id, rc);
+		rc = id;
+		if (adie_client[id].status == ADIE_SVC_STATUS_FAILURE) {
+			MM_ERR("Received failed status for register request\n");
+			rc = -1;
+		} else
+			goto done;
+	} else {
+		MM_ERR("Failed to send register client request\n");
+		rc = -1;
+		mutex_lock(&adie_client[id].lock);
+	}
+err:
+	msm_rpc_unregister_client(adie_client[id].rpc_client);
+	adie_client[id].rpc_client = NULL;
+	adie_client[id].client_id = -1;
+	adie_client[id].cb_id = MSM_RPC_CLIENT_NULL_CB_ID;
+	adie_client[id].adie_svc_cb_done = 0;
+done:
+	mutex_unlock(&adie_client[id].lock);
+	return rc;
+}
+EXPORT_SYMBOL(adie_svc_get);
+
+/* Returns: 0 on succes and
+ *         -1 on failure
+ */
+int adie_svc_put(int id)
+{
+	int rc = 0;
+	struct adie_svc_client_deregister_cb_args arg;
+
+	if (id < 0 || id >= ADIE_SVC_MAX_CLIENTS)
+		return -1;
+
+	mutex_lock(&adie_client[id].lock);
+	if (adie_client[id].client_id == -1 ||
+			adie_client[id].rpc_client == NULL) {
+		mutex_unlock(&adie_client[id].lock);
+		return -1;
+	}
+	arg.client_id = adie_client[id].client_id;
+	adie_client[id].adie_svc_cb_done = 0;
+	mutex_unlock(&adie_client[id].lock);
+	rc = msm_rpc_client_req(adie_client[id].rpc_client,
+					SND_ADIE_SVC_CLIENT_DEREGISTER_PROC,
+					adie_svc_client_deregister_arg, &arg,
+					NULL, NULL, -1);
+	if (!rc) {
+		rc = wait_event_interruptible(adie_client[id].wq,
+				adie_client[id].adie_svc_cb_done);
+		if (unlikely(rc < 0)) {
+			if (rc == -ERESTARTSYS)
+				MM_ERR("wait_event_interruptible "
+						"returned -ERESTARTSYS\n");
+			else
+				MM_ERR("wait_event_interruptible "
+						"returned error\n");
+			rc = -1;
+			goto err;
+		}
+		MM_DBG("Status received from CB function\n");
+		mutex_lock(&adie_client[id].lock);
+		if (adie_client[id].status == ADIE_SVC_STATUS_FAILURE) {
+			rc = -1;
+		} else {
+			msm_rpc_unregister_client(adie_client[id].rpc_client);
+			adie_client[id].rpc_client = NULL;
+			adie_client[id].client_id = -1;
+			adie_client[id].cb_id = MSM_RPC_CLIENT_NULL_CB_ID;
+			adie_client[id].adie_svc_cb_done = 0;
+		}
+		mutex_unlock(&adie_client[id].lock);
+	} else {
+		MM_ERR("Failed to send deregister client request\n");
+		rc = -1;
+	}
+err:
+	return rc;
+}
+EXPORT_SYMBOL(adie_svc_put);
+
+/* Returns: 0 on success
+ *          2 already in use
+ *         -1 on failure
+ */
+int adie_svc_config_adie_block(int id,
+		enum adie_block_enum_type adie_block_type, bool enable)
+{
+	int rc = 0;
+	struct adie_svc_config_adie_block_cb_args arg;
+
+	if (id < 0 || id >= ADIE_SVC_MAX_CLIENTS)
+		return -1;
+
+	mutex_lock(&adie_client[id].lock);
+	if (adie_client[id].client_id == -1 ||
+			adie_client[id].rpc_client == NULL) {
+		mutex_unlock(&adie_client[id].lock);
+		return -1;
+	}
+	arg.client_id 	= adie_client[id].client_id;
+	arg.adie_block	= adie_block_type;
+	arg.config	= (enum adie_config_enum_type)enable;
+	adie_client[id].adie_svc_cb_done = 0;
+	mutex_unlock(&adie_client[id].lock);
+	rc = msm_rpc_client_req(adie_client[id].rpc_client,
+					SND_ADIE_SVC_CONFIG_ADIE_BLOCK_PROC,
+					adie_svc_config_adie_block_arg, &arg,
+					NULL, NULL, -1);
+	if (!rc) {
+		rc = wait_event_interruptible(adie_client[id].wq,
+				adie_client[id].adie_svc_cb_done);
+		if (unlikely(rc < 0)) {
+			if (rc == -ERESTARTSYS)
+				MM_ERR("wait_event_interruptible "
+						"returned -ERESTARTSYS\n");
+			else
+				MM_ERR("wait_event_interruptible "
+						"returned error\n");
+			rc = -1;
+			goto err;
+		}
+		MM_DBG("Status received from CB function\n");
+		mutex_lock(&adie_client[id].lock);
+		if (adie_client[id].status == ADIE_SVC_STATUS_FAILURE)
+			rc = -1;
+		else
+			rc = adie_client[id].status;
+		mutex_unlock(&adie_client[id].lock);
+	} else {
+		MM_ERR("Failed to send adie block config request\n");
+		rc = -1;
+	}
+err:
+	return rc;
+}
+EXPORT_SYMBOL(adie_svc_config_adie_block);
+
+#ifdef CONFIG_DEBUG_FS
+
+struct dentry *dentry;
+
+static ssize_t snd_adie_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t snd_adie_debug_write(struct file *file, const char __user *buf,
+	       size_t count, loff_t *ppos)
+{
+	int rc = 0, op = 0;
+	int id = 0, adie_block = 0, config = 1;
+
+	sscanf(buf, "%d %d %d %d", &op, &id, &adie_block, &config);
+	MM_INFO("\nUser input: op %d id %d block %d config %d\n", op, id,
+			adie_block, config);
+	switch (op) {
+	case ADIE_SVC_REGISTER_CLIENT:
+		MM_INFO("ADIE_SVC_REGISTER_CLIENT\n");
+		rc = adie_svc_get();
+		if (rc >= 0)
+			MM_INFO("Client registered: %d\n", rc);
+		else
+			MM_ERR("Failed registering client\n");
+		break;
+	case ADIE_SVC_DEREGISTER_CLIENT:
+		MM_INFO("ADIE_SVC_DEREGISTER_CLIENT: %d\n", id);
+		rc = adie_svc_put(id);
+		if (!rc)
+			MM_INFO("Client %d deregistered\n", id);
+		else
+			MM_ERR("Failed unregistering the client: %d\n",	id);
+		break;
+	case ADIE_SVC_CONFIG_ADIE_BLOCK:
+		MM_INFO("ADIE_SVC_CONFIG_ADIE_BLOCK: id %d adie_block %d \
+				config %d\n", id, adie_block, config);
+		rc =  adie_svc_config_adie_block(id,
+			(enum adie_block_enum_type)adie_block, (bool)config);
+		if (!rc)
+			MM_INFO("ADIE block %d %s", adie_block,
+					config ? "enabled\n" : "disabled\n");
+		else if (rc == 2)
+			MM_INFO("ADIE block %d already in use\n", adie_block);
+		else
+			MM_ERR("ERROR configuring the ADIE block\n");
+		break;
+	default:
+		MM_INFO("Invalid operation\n");
+	}
+	return count;
+}
+
+static ssize_t snd_adie_debug_read(struct file *file, char __user *buf,
+			  size_t count, loff_t *ppos)
+{
+	static char buffer[1024];
+	const int debug_bufmax = sizeof(buffer);
+	int id, n = 0;
+
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"LIST OF CLIENTS\n");
+	for (id = 0; id < ADIE_SVC_MAX_CLIENTS ; id++) {
+		if (adie_client[id].client_id != -1 &&
+				adie_client[id].rpc_client != NULL) {
+			n += scnprintf(buffer + n, debug_bufmax - n,
+				"id %d rpc client 0x%08x\n", id,
+				(uint32_t)adie_client[id].rpc_client);
+		}
+	}
+	buffer[n] = 0;
+	return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+
+static const struct file_operations snd_adie_debug_fops = {
+	.read = snd_adie_debug_read,
+	.open = snd_adie_debug_open,
+	.write = snd_adie_debug_write,
+};
+#endif
+
+static void __exit snd_adie_exit(void)
+{
+#ifdef CONFIG_DEBUG_FS
+	if (dentry)
+		debugfs_remove(dentry);
+#endif
+}
+
+static int __init snd_adie_init(void)
+{
+	int id;
+#ifdef CONFIG_DEBUG_FS
+	char name[sizeof "msm_snd_adie"];
+
+	snprintf(name, sizeof name, "msm_snd_adie");
+	dentry = debugfs_create_file(name, S_IFREG | S_IRUGO | S_IWUGO,
+			NULL, NULL, &snd_adie_debug_fops);
+	if (IS_ERR(dentry))
+		MM_DBG("debugfs_create_file failed\n");
+#endif
+	for (id = 0; id < ADIE_SVC_MAX_CLIENTS; id++) {
+		adie_client[id].client_id = -1;
+		adie_client[id].cb_id = MSM_RPC_CLIENT_NULL_CB_ID;
+		adie_client[id].status = 0;
+		adie_client[id].adie_svc_cb_done = 0;
+		mutex_init(&adie_client[id].lock);
+		init_waitqueue_head(&adie_client[id].wq);
+		adie_client[id].rpc_client = NULL;
+	}
+	return 0;
+}
+
+module_init(snd_adie_init);
+module_exit(snd_adie_exit);