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/qdsp5v2/Makefile b/arch/arm/mach-msm/qdsp5v2/Makefile
new file mode 100755
index 0000000..3ae3c1b
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/Makefile
@@ -0,0 +1,22 @@
+obj-y += afe.o audio_interct.o mi2s.o audio_dev_ctl.o voice.o
+
+ifeq ($(CONFIG_TIMPANI_CODEC), y)
+obj-y += snddev_icodec.o
+else ifeq ($(CONFIG_MARIMBA_CODEC), y)
+obj-y += snddev_icodec.o
+endif
+
+obj-$(CONFIG_MARIMBA_CODEC) += snddev_data_marimba.o
+obj-$(CONFIG_TIMPANI_CODEC) += snddev_data_timpani.o
+
+obj-y += audio_pcm.o audpp.o audio_mp3.o audio_wma.o audio_aac.o audio_amrnb.o
+obj-y += audio_amrwb.o audio_wmapro.o audio_adpcm.o audio_evrc.o audio_qcelp.o
+obj-y += aux_pcm.o snddev_ecodec.o audio_out.o
+obj-y += audio_lpa.o mp3_funcs.o pcm_funcs.o
+obj-y += audpreproc.o audio_pcm_in.o audio_aac_in.o audio_amrnb_in.o audio_a2dp_in.o
+obj-y += audio_evrc_in.o audio_qcelp_in.o
+obj-y += adsp.o adsp_driver.o adsp_info.o
+obj-y += audio_acdb.o snddev_virtual.o
+obj-y += audio_fm.o
+obj-y += lpa.o snddev_mi2s.o
+obj-y += audio_mvs.o
\ No newline at end of file
diff --git a/arch/arm/mach-msm/qdsp5v2/adsp.c b/arch/arm/mach-msm/qdsp5v2/adsp.c
new file mode 100644
index 0000000..6d4d074
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/adsp.c
@@ -0,0 +1,1229 @@
+/*
+ * Register/Interrupt access for userspace aDSP library.
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (c) 2008-2009,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/slab.h>
+#include <linux/io.h>
+#include <mach/msm_iomap.h>
+#include <mach/clk.h>
+#include <mach/msm_adsp.h>
+#include "adsp.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 */
+
+#define INT_ADSP INT_ADSP_A9_A11
+
+static struct adsp_info adsp_info;
+static struct msm_adsp_module *adsp_modules;
+static int adsp_open_count;
+
+static DEFINE_MUTEX(adsp_open_lock);
+
+/* protect interactions with the ADSP command/message queue */
+static spinlock_t adsp_cmd_lock;
+static 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)
+{
+	struct adsp_rtos_atom_cmd adspsvc_cmd;
+	int err;
+
+	adspsvc_cmd.cmd = cmd;
+	adspsvc_cmd.proc_id = RPC_ADSP_RTOS_PROC_APPS;
+	adspsvc_cmd.module = module;
+	adspsvc_cmd.cb_handle = adsp_info.cb_handle;
+
+	err = dalrpc_fcn_5(DALDEVICE_ADSP_CMD_IDX | 0x80000000,
+					adsp_info.handle,
+					&adspsvc_cmd, sizeof(adspsvc_cmd));
+	if (err < 0)
+		MM_ERR("ADSP command send Failed\n");
+
+	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;
+}
+
+/*
+ * 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)
+{
+	struct adsp_rtos_atom_cmd cmd;
+	int err;
+
+	cmd.cmd = RPC_ADSP_RTOS_CMD_GET_INIT_INFO;
+	cmd.proc_id = RPC_ADSP_RTOS_PROC_APPS;
+	cmd.module = 0;
+	cmd.cb_handle = adsp_info.cb_handle;
+
+	err = dalrpc_fcn_5(DALDEVICE_ADSP_CMD_IDX | 0x80000000,
+							adsp_info.handle,
+							&cmd, sizeof(cmd));
+	if (err < 0)
+		MM_ERR("INIT_INFO command send Failed\n");
+}
+
+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;
+
+	module = find_adsp_module_by_name(&adsp_info, name);
+	if (!module)
+		return -ENODEV;
+
+	mutex_lock(&module->lock);
+	MM_DBG("opening module %s\n", module->name);
+
+	if (module->ops) {
+		rc = -EBUSY;
+		mutex_unlock(&module->lock);
+		goto done;
+	}
+
+	module->ops = ops;
+	module->driver_data = driver_data;
+	*out = module;
+	mutex_unlock(&module->lock);
+	rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_REGISTER_APP,
+					module->id, module);
+	if (rc) {
+		mutex_lock(&module->lock);
+		module->ops = NULL;
+		module->driver_data = NULL;
+		*out = NULL;
+		MM_ERR("REGISTER_APP failed\n");
+		mutex_unlock(&module->lock);
+		goto done;
+	}
+
+	MM_INFO("module %s has been registered\n", module->name);
+
+done:
+	return rc;
+}
+EXPORT_SYMBOL(msm_adsp_get);
+
+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);
+			mutex_unlock(&module->lock);
+			msm_adsp_disable(module);
+			return;
+		}
+	} else {
+		MM_INFO("module %s is already closed\n", module->name);
+	}
+	mutex_unlock(&module->lock);
+}
+EXPORT_SYMBOL(msm_adsp_put);
+
+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(50);
+	} while (rc == -EAGAIN && retries++ < 300);
+	if (retries > 20)
+		MM_INFO("%s command took %d attempts: rc %d\n",
+			module->name, retries, rc);
+	return rc;
+}
+
+#ifdef CONFIG_MSM_ADSP_REPORT_EVENTS
+static void *event_addr;
+static void read_event(void *buf, size_t len)
+{
+	uint32_t dptr[3];
+	struct adsp_rtos_mp_mtoa_s_type *sptr;
+	struct adsp_rtos_mp_mtoa_type	*pkt_ptr;
+
+	sptr = event_addr;
+	pkt_ptr = &sptr->adsp_rtos_mp_mtoa_data.mp_mtoa_packet;
+
+	dptr[0] = sptr->mp_mtoa_header.event;
+	dptr[1] = pkt_ptr->module;
+	dptr[2] = pkt_ptr->image;
+
+	if (len > EVENT_LEN)
+		len = EVENT_LEN;
+
+	memcpy(buf, dptr, len);
+}
+#endif
+
+static void adsp_rtos_mtoa_cb(void *context, uint32_t param,
+					void *evt_buf, uint32_t len)
+{
+	struct adsp_rtos_mp_mtoa_s_type *args = NULL;
+	uint32_t event = 0;
+	uint32_t proc_id = 0;
+	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;
+	static uint32_t	init_info_completed;
+	static uint32_t init_info_len =
+				sizeof(struct adsp_rtos_mp_mtoa_header_type);
+	static uint32_t	next_init_info_byte;
+	static uint32_t expected_byte = 1;
+	uint32_t hdr_len = sizeof(struct adsp_rtos_mp_mtoa_header_type);
+
+	if (len) {
+		args = (struct adsp_rtos_mp_mtoa_s_type *) evt_buf;
+		event = args->mp_mtoa_header.event;
+		proc_id = args->mp_mtoa_header.proc_id;
+	}
+
+	if (!init_info_completed && event == RPC_ADSP_RTOS_INIT_INFO) {
+		memcpy(((char *)adsp_info.raw_event) + init_info_len,
+						(char *)evt_buf + hdr_len + 4,
+							len - ((hdr_len + 4)));
+		init_info_len += (len - (hdr_len + 4));
+		evt_buf += hdr_len;
+		next_init_info_byte = *(uint32_t *) evt_buf;
+		expected_byte += len;
+		if (next_init_info_byte &&
+				(expected_byte != next_init_info_byte)) {
+			MM_ERR("INIT_INFO - expecting next byte to be %d\n"
+				"\tbut ADSPSVC indicated next byte to be %d\n",
+				expected_byte, next_init_info_byte);
+			return;
+		}
+		if (!next_init_info_byte) {
+			args = adsp_info.raw_event;
+			args->mp_mtoa_header.event = event;
+			args->mp_mtoa_header.proc_id = proc_id;
+			init_info_completed = 1;
+		} else
+			return;
+	}
+
+	if (event == RPC_ADSP_RTOS_INIT_INFO) {
+		MM_INFO("INIT_INFO Event\n");
+		sptr = &args->adsp_rtos_mp_mtoa_data.mp_mtoa_init_packet;
+
+		iptr = adsp_info.init_info_ptr;
+		iptr->image_count = sptr->image_count;
+		if (iptr->image_count > IMG_MAX)
+			iptr->image_count = IMG_MAX;
+		iptr->num_queue_offsets = 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 = qptr->offset;
+				qtbl[e_idx].queue = qptr->queue;
+				q_idx = qptr->queue;
+				iptr->queue_offsets[i_no][q_idx] =
+							qtbl[e_idx].offset;
+				qptr++;
+			}
+		}
+
+		num_entries = 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] = *mptr;
+				mptr++;
+			}
+		}
+
+		iptr->module_table_size = sptr->module_table_size;
+		if (iptr->module_table_size > MODULES_MAX)
+			iptr->module_table_size = MODULES_MAX;
+		mptr = &sptr->module_entries[0];
+		for (i_no = 0; i_no < iptr->module_table_size; i_no++)
+			iptr->module_entries[i_no] = mptr[i_no];
+
+		mqptr = &sptr->mod_to_q_tbl[0];
+		mqtbl = &iptr->mod_to_q_tbl[0];
+		iptr->mod_to_q_entries = 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 = mqptr->module;
+			mqtbl[e_idx].q_type = mqptr->q_type;
+			mqtbl[e_idx].q_max_len = mqptr->q_max_len;
+			mqptr++;
+		}
+
+		adsp_info.init_info_state = ADSP_STATE_INIT_INFO;
+		kfree(adsp_info.raw_event);
+		wake_up(&adsp_info.init_info_wait);
+		return;
+	}
+	pkt_ptr = &args->adsp_rtos_mp_mtoa_data.mp_mtoa_packet;
+	module_id = pkt_ptr->module;
+	image     = pkt_ptr->image;
+
+	MM_INFO("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);
+		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);
+		mutex_unlock(&module->lock);
+		return;
+	}
+#ifdef CONFIG_MSM_ADSP_REPORT_EVENTS
+	event_addr = (uint32_t *)evt_buf;
+	if (module->ops)
+		module->ops->event(module->driver_data,
+					EVENT_MSG_ID,
+					EVENT_LEN,
+					read_event);
+#endif
+	mutex_unlock(&module->lock);
+}
+
+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:
+		module->state = ADSP_STATE_ENABLING;
+		mutex_unlock(&module->lock);
+		rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_ENABLE,
+						module->id, module);
+		if (rc) {
+			mutex_lock(&module->lock);
+			module->state = ADSP_STATE_DISABLED;
+			break;
+		}
+		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);
+		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);
+
+int msm_adsp_disable(struct msm_adsp_module *module)
+{
+	int rc = 0;
+
+	mutex_lock(&module->lock);
+	switch (module->state) {
+	case ADSP_STATE_DISABLED:
+		MM_DBG("module '%s' already disabled\n", module->name);
+		mutex_unlock(&module->lock);
+		break;
+	case ADSP_STATE_ENABLING:
+	case ADSP_STATE_ENABLED:
+		mutex_unlock(&module->lock);
+		rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_DISABLE,
+						module->id, module);
+		mutex_lock(&module->lock);
+		module->state = ADSP_STATE_DISABLED;
+		if (--module->open_count == 0 && module->clk)
+			clk_disable(module->clk);
+		mutex_unlock(&module->lock);
+		mutex_lock(&adsp_open_lock);
+		if (--adsp_open_count == 0) {
+			disable_irq(INT_ADSP);
+			MM_INFO("disable interrupt\n");
+		}
+		mutex_unlock(&adsp_open_lock);
+		break;
+	}
+	return rc;
+}
+EXPORT_SYMBOL(msm_adsp_disable);
+
+static int msm_adsp_probe(struct platform_device *pdev)
+{
+	unsigned count;
+	int rc, i;
+
+	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;
+
+	adsp_info.raw_event = kzalloc(
+		(sizeof(struct adsp_rtos_mp_mtoa_s_type)), GFP_KERNEL);
+	if (!adsp_info.raw_event) {
+		kfree(adsp_info.init_info_ptr);
+		return -ENOMEM;
+	}
+
+	rc = adsp_init_info(&adsp_info);
+	if (rc) {
+		kfree(adsp_info.init_info_ptr);
+		kfree(adsp_info.raw_event);
+		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) {
+		kfree(adsp_info.init_info_ptr);
+		kfree(adsp_info.raw_event);
+		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);
+
+	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);
+
+	rc = daldevice_attach(DALRPC_ADSPSVC_DEVICEID, DALRPC_ADSPSVC_PORT,
+					DALRPC_ADSPSVC_DEST, &adsp_info.handle);
+	if (rc) {
+		MM_ERR("adsp attach failed : %d\n", rc);
+		goto fail_dal_attach;
+	}
+
+	adsp_info.cb_handle = dalrpc_alloc_cb(adsp_info.handle,
+						adsp_rtos_mtoa_cb, NULL);
+	if (adsp_info.cb_handle == NULL) {
+		MM_ERR("Callback registration failed\n");
+		goto fail_allocate_cb;
+	}
+
+	/* Get INIT_INFO */
+	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,
+		10 * HZ);
+	if (!rc) {
+		MM_ERR("INIT_INFO failed\n");
+		rc = -ETIMEDOUT;
+	} else
+		return 0;
+
+fail_allocate_cb:
+	daldevice_detach(adsp_info.handle);
+	adsp_info.handle = NULL;
+fail_dal_attach:
+	enable_irq(INT_ADSP);
+	free_irq(INT_ADSP, 0);
+fail_request_irq:
+	kfree(adsp_modules);
+	kfree(adsp_info.init_info_ptr);
+	kfree(adsp_info.raw_event);
+	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 (!strncmp(access_str, "write_log", 9)) {
+		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 (!strncmp(access_str, "read_log", 8)) {
+		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,
+	},
+};
+
+struct platform_device msm_adsp_device = {
+	.name = "msm_adsp",
+	.id = -1,
+};
+
+static char msm_adsp_driver_name[] = "msm_adsp";
+
+#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);
+	}
+#endif /* CONFIG_DEBUG_FS */
+
+	msm_adsp_driver.driver.name = msm_adsp_driver_name;
+	rc = platform_device_register(&msm_adsp_device);
+	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/qdsp5v2/adsp.h b/arch/arm/mach-msm/qdsp5v2/adsp.h
new file mode 100644
index 0000000..18f4046
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/adsp.h
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (c) 2008-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.
+ *
+ */
+
+#ifndef _ARCH_ARM_MACH_MSM_ADSP_H
+#define _ARCH_ARM_MACH_MSM_ADSP_H
+
+#include <linux/types.h>
+#include <linux/msm_adsp.h>
+#include <linux/platform_device.h>
+#include <mach/msm_adsp.h>
+#include <mach/dal.h>
+
+int adsp_pmem_fixup(struct msm_adsp_module *module, void **addr,
+		    unsigned long len);
+int adsp_pmem_fixup_kvaddr(struct msm_adsp_module *module, void **addr,
+			   unsigned long *kvaddr, unsigned long len);
+int adsp_pmem_paddr_fixup(struct msm_adsp_module *module, void **addr);
+
+int adsp_vfe_verify_cmd(struct msm_adsp_module *module,
+			unsigned int queue_id, void *cmd_data,
+			size_t cmd_size);
+int adsp_jpeg_verify_cmd(struct msm_adsp_module *module,
+			 unsigned int queue_id, void *cmd_data,
+			 size_t cmd_size);
+int adsp_lpm_verify_cmd(struct msm_adsp_module *module,
+			unsigned int queue_id, void *cmd_data,
+			size_t cmd_size);
+int adsp_video_verify_cmd(struct msm_adsp_module *module,
+			  unsigned int queue_id, void *cmd_data,
+			  size_t cmd_size);
+int adsp_videoenc_verify_cmd(struct msm_adsp_module *module,
+			  unsigned int queue_id, void *cmd_data,
+			  size_t cmd_size);
+
+
+struct adsp_event;
+
+int adsp_vfe_patch_event(struct msm_adsp_module *module,
+			struct adsp_event *event);
+
+int adsp_jpeg_patch_event(struct msm_adsp_module *module,
+			struct adsp_event *event);
+
+
+struct adsp_module_info {
+	const char *name;
+	const char *pdev_name;
+	uint32_t id;
+	const char *clk_name;
+	unsigned long clk_rate;
+	int (*verify_cmd) (struct msm_adsp_module*, unsigned int, void *,
+			   size_t);
+	int (*patch_event) (struct msm_adsp_module*, struct adsp_event *);
+};
+
+#define ADSP_EVENT_MAX_SIZE 496
+#define EVENT_LEN       12
+#define EVENT_MSG_ID ((uint16_t)~0)
+
+struct adsp_event {
+	struct list_head list;
+	uint32_t size; /* always in bytes */
+	uint16_t msg_id;
+	uint16_t type; /* 0 for msgs (from aDSP), -1 for events (from ARM9) */
+	int is16; /* always 0 (msg is 32-bit) when the event type is 1(ARM9) */
+	union {
+		uint16_t msg16[ADSP_EVENT_MAX_SIZE / 2];
+		uint32_t msg32[ADSP_EVENT_MAX_SIZE / 4];
+	} data;
+};
+
+#define DALRPC_ADSPSVC_DEVICEID 0x0200009A
+#define DALRPC_ADSPSVC_DEST SMD_APPS_MODEM
+#define DALRPC_ADSPSVC_PORT "DAL00"
+
+enum {
+	DALDEVICE_ADSP_CMD_IDX = DALDEVICE_FIRST_DEVICE_API_IDX,
+};
+
+struct adsp_rtos_atom_cmd {
+	uint32_t cmd;
+	uint32_t proc_id;
+	uint32_t module;
+	void *cb_handle;
+};
+
+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,
+};
+
+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;
+	uint32_t		            version;
+	enum rpc_adsp_rtos_proc_type        proc_id;
+};
+
+/* ADSP RTOS MP Communications - Modem to APP's  Event Info*/
+struct adsp_rtos_mp_mtoa_type {
+	uint32_t	module;
+	uint32_t	image;
+	uint32_t	apps_okts;
+};
+
+/* ADSP RTOS MP Communications - Modem to APP's Init Info  */
+#define IMG_MAX         2
+#define ENTRIES_MAX     36
+#define MODULES_MAX     64
+#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;
+	uint32_t	module_entries[MODULES_MAX];
+	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;
+
+	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 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 adsp_rtos_mp_mtoa_init_info_type	*init_info_ptr;
+	struct adsp_rtos_mp_mtoa_s_type *raw_event;
+	wait_queue_head_t	init_info_wait;
+	unsigned 		init_info_state;
+
+	void *handle;
+	void *cb_handle;
+};
+
+#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_adsp_ops *ops;
+	void *driver_data;
+
+	/* statistics */
+	unsigned num_commands;
+	unsigned num_events;
+
+	wait_queue_head_t state_wait;
+	unsigned state;
+
+	struct platform_device pdev;
+	struct clk *clk;
+	int open_count;
+
+	struct mutex pmem_regions_lock;
+	struct hlist_head pmem_regions;
+	int (*verify_cmd) (struct msm_adsp_module*, unsigned int, void *,
+			   size_t);
+	int (*patch_event) (struct msm_adsp_module*, struct adsp_event *);
+};
+
+extern void msm_adsp_publish_cdevs(struct msm_adsp_module *, unsigned);
+extern int adsp_init_info(struct adsp_info *info);
+
+/* Value to indicate that a queue is not defined for a particular image */
+#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/qdsp5v2/adsp_driver.c b/arch/arm/mach-msm/qdsp5v2/adsp_driver.c
new file mode 100644
index 0000000..28f9dd6
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/adsp_driver.c
@@ -0,0 +1,655 @@
+/*
+ * 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/msm_adsp.h>
+#include <linux/android_pmem.h>
+#include "adsp.h"
+#include <mach/debug_mm.h>
+#include <linux/slab.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 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);
+	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;
+	}
+	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;
+	}
+	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 const 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/qdsp5v2/adsp_info.c b/arch/arm/mach-msm/qdsp5v2/adsp_info.c
new file mode 100644
index 0000000..4026367
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/adsp_info.c
@@ -0,0 +1,121 @@
+/*
+ * 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 "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_AUDREC2TASK             0x010a2f59
+#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(AFETASK , NULL, 0, NULL, NULL),
+	QDSP_MODULE(AUDREC0TASK, NULL, 0, NULL, NULL),
+	QDSP_MODULE(AUDREC1TASK, NULL, 0, NULL, NULL),
+	QDSP_MODULE(AUDREC2TASK, NULL, 0, NULL, NULL),
+};
+
+int adsp_init_info(struct adsp_info *info)
+{
+	uint32_t img_num;
+
+	info->send_irq =   0x00c00200;
+	info->read_ctrl =  0x00400038;
+	info->write_ctrl = 0x00400034;
+
+	info->max_msg16_size = 193;
+	info->max_msg32_size = 8;
+	for (img_num = 0; img_num < IMG_MAX; img_num++)
+		qdsp_queue_offset_table[img_num] =
+		&info->init_info_ptr->queue_offsets[img_num][0];
+
+	for (img_num = 0; img_num < IMG_MAX; img_num++)
+		qdsp_task_to_module[img_num] =
+		&info->init_info_ptr->task_to_module_tbl[img_num][0];
+	info->max_task_id = ENTRIES_MAX;
+	info->max_module_id = QDSP_MODULE_MAX - 1;
+	info->max_queue_id = QDSP_MAX_NUM_QUEUES;
+	info->max_image_id = 0;
+	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/qdsp5v2/afe.c b/arch/arm/mach-msm/qdsp5v2/afe.c
new file mode 100644
index 0000000..20c9898
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/afe.c
@@ -0,0 +1,534 @@
+/* Copyright (c) 2009-2011, 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/wait.h>
+#include <linux/sched.h>
+#include <linux/jiffies.h>
+#include <linux/debugfs.h>
+#include <asm/uaccess.h>
+#include <mach/qdsp5v2/qdsp5afecmdi.h>
+#include <mach/qdsp5v2/qdsp5afemsg.h>
+#include <mach/qdsp5v2/afe.h>
+#include <mach/msm_adsp.h>
+#include <mach/debug_mm.h>
+
+#define AFE_MAX_TIMEOUT 500 /* 500 ms */
+#define AFE_MAX_CLNT 6 /* 6 HW path defined so far */
+#define GETDEVICEID(x) ((x) - 1)
+
+struct msm_afe_state {
+	struct msm_adsp_module *mod;
+	struct msm_adsp_ops    adsp_ops;
+	struct mutex           lock;
+	u8                     in_use;
+	u8                     codec_config[AFE_MAX_CLNT];
+	wait_queue_head_t      wait;
+	u8			aux_conf_flag;
+};
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *debugfs_afelb;
+#endif
+
+
+static struct msm_afe_state the_afe_state;
+
+#define afe_send_queue(afe, cmd, len) \
+  msm_adsp_write(afe->mod, QDSP_apuAfeQueue, \
+	cmd, len)
+
+static void afe_dsp_event(void *data, unsigned id, size_t len,
+			    void (*getevent)(void *ptr, size_t len))
+{
+	struct msm_afe_state *afe = data;
+
+	MM_DBG("msg_id %d \n", id);
+
+	switch (id) {
+	case AFE_APU_MSG_CODEC_CONFIG_ACK: {
+		struct afe_msg_codec_config_ack afe_ack;
+		getevent(&afe_ack, AFE_APU_MSG_CODEC_CONFIG_ACK_LEN);
+		MM_DBG("%s: device_id: %d device activity: %d\n", __func__,
+		afe_ack.device_id, afe_ack.device_activity);
+		if (afe_ack.device_activity == AFE_MSG_CODEC_CONFIG_DISABLED)
+			afe->codec_config[GETDEVICEID(afe_ack.device_id)] = 0;
+		else
+			afe->codec_config[GETDEVICEID(afe_ack.device_id)] =
+			afe_ack.device_activity;
+
+		wake_up(&afe->wait);
+		break;
+	}
+	case AFE_APU_MSG_VOC_TIMING_SUCCESS:
+		MM_INFO("Received VOC_TIMING_SUCCESS message from AFETASK\n");
+		break;
+	case ADSP_MESSAGE_ID:
+		MM_DBG("Received ADSP event: module enable/disable(audpptask)");
+		break;
+	default:
+		MM_ERR("unexpected message from afe \n");
+	}
+
+	return;
+}
+
+static void afe_dsp_codec_config(struct msm_afe_state *afe,
+	u8 path_id, u8 enable, struct msm_afe_config *config)
+{
+	struct afe_cmd_codec_config cmd;
+
+	MM_DBG("%s() %p\n", __func__, config);
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AFE_CMD_CODEC_CONFIG_CMD;
+	cmd.device_id = path_id;
+	cmd.activity = enable;
+	if (config) {
+		MM_DBG("%s: sample_rate %x ch mode %x vol %x\n",
+			__func__, config->sample_rate,
+			config->channel_mode, config->volume);
+		cmd.sample_rate = config->sample_rate;
+		cmd.channel_mode = config->channel_mode;
+		cmd.volume = config->volume;
+	}
+	afe_send_queue(afe, &cmd, sizeof(cmd));
+}
+/* Function is called after afe module been enabled */
+void afe_loopback(int enable)
+{
+	struct afe_cmd_loopback cmd;
+	struct msm_afe_state *afe;
+
+	afe = &the_afe_state;
+	MM_DBG("enable %d\n", enable);
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AFE_CMD_LOOPBACK;
+	if (enable)
+		cmd.enable_flag = AFE_LOOPBACK_ENABLE_COMMAND;
+
+	afe_send_queue(afe, &cmd, sizeof(cmd));
+}
+EXPORT_SYMBOL(afe_loopback);
+
+void afe_ext_loopback(int enable, int rx_copp_id, int tx_copp_id)
+{
+	struct afe_cmd_ext_loopback cmd;
+	struct msm_afe_state *afe;
+
+	afe = &the_afe_state;
+	MM_DBG("enable %d\n", enable);
+	if ((rx_copp_id == 0) && (tx_copp_id == 0)) {
+		afe_loopback(enable);
+	} else {
+		memset(&cmd, 0, sizeof(cmd));
+		cmd.cmd_id = AFE_CMD_EXT_LOOPBACK;
+		cmd.source_id = tx_copp_id;
+		cmd.dst_id = rx_copp_id;
+		if (enable)
+			cmd.enable_flag = AFE_LOOPBACK_ENABLE_COMMAND;
+
+		afe_send_queue(afe, &cmd, sizeof(cmd));
+	}
+}
+EXPORT_SYMBOL(afe_ext_loopback);
+
+void afe_device_volume_ctrl(u16 device_id, u16 device_volume)
+{
+	struct afe_cmd_device_volume_ctrl cmd;
+	struct msm_afe_state *afe;
+
+	afe = &the_afe_state;
+	MM_DBG("device 0x%4x volume 0x%4x\n", device_id, device_volume);
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AFE_CMD_DEVICE_VOLUME_CTRL;
+	cmd.device_id = device_id;
+	cmd.device_volume = device_volume;
+	afe_send_queue(afe, &cmd, sizeof(cmd));
+}
+EXPORT_SYMBOL(afe_device_volume_ctrl);
+
+int afe_enable(u8 path_id, struct msm_afe_config *config)
+{
+	struct msm_afe_state *afe = &the_afe_state;
+	int rc;
+
+	MM_DBG("%s: path %d\n", __func__, path_id);
+	if ((GETDEVICEID(path_id) < 0) || (GETDEVICEID(path_id) > 5)) {
+		MM_ERR("Invalid path_id: %d\n", path_id);
+		return -EINVAL;
+	}
+	mutex_lock(&afe->lock);
+	if (!afe->in_use && !afe->aux_conf_flag) {
+		/* enable afe */
+		rc = msm_adsp_get("AFETASK", &afe->mod, &afe->adsp_ops, afe);
+		if (rc < 0) {
+			MM_ERR("%s: failed to get AFETASK module\n", __func__);
+			goto error_adsp_get;
+		}
+		rc = msm_adsp_enable(afe->mod);
+		if (rc < 0)
+			goto error_adsp_enable;
+	}
+	/* Issue codec config command */
+	afe_dsp_codec_config(afe, path_id, 1, config);
+	rc = wait_event_timeout(afe->wait,
+		afe->codec_config[GETDEVICEID(path_id)],
+		msecs_to_jiffies(AFE_MAX_TIMEOUT));
+	if (!rc) {
+		MM_ERR("AFE failed to respond within %d ms\n", AFE_MAX_TIMEOUT);
+		rc = -ENODEV;
+		if (!afe->in_use) {
+			if (!afe->aux_conf_flag ||
+			(afe->aux_conf_flag &&
+			(path_id == AFE_HW_PATH_AUXPCM_RX ||
+			path_id == AFE_HW_PATH_AUXPCM_TX))) {
+				/* clean up if there is no client */
+				msm_adsp_disable(afe->mod);
+				msm_adsp_put(afe->mod);
+				afe->aux_conf_flag = 0;
+				afe->mod = NULL;
+			}
+		}
+
+	} else {
+		rc = 0;
+		afe->in_use++;
+	}
+
+	mutex_unlock(&afe->lock);
+	return rc;
+
+error_adsp_enable:
+	msm_adsp_put(afe->mod);
+	afe->mod = NULL;
+error_adsp_get:
+	mutex_unlock(&afe->lock);
+	return rc;
+}
+EXPORT_SYMBOL(afe_enable);
+
+int afe_config_fm_codec(int fm_enable, uint16_t source)
+{
+	struct afe_cmd_fm_codec_config cmd;
+	struct msm_afe_state *afe = &the_afe_state;
+	int rc = 0;
+	int i = 0;
+	unsigned short *ptrmem = (unsigned short *)&cmd;
+
+	MM_INFO(" configure fm codec\n");
+	mutex_lock(&afe->lock);
+	if (!afe->in_use) {
+		/* enable afe */
+		rc = msm_adsp_get("AFETASK", &afe->mod, &afe->adsp_ops, afe);
+		if (rc < 0) {
+			MM_ERR("%s: failed to get AFETASK module\n", __func__);
+			goto error_adsp_get;
+		}
+		rc = msm_adsp_enable(afe->mod);
+		if (rc < 0)
+			goto error_adsp_enable;
+	}
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AFE_CMD_FM_RX_ROUTING_CMD;
+	cmd.enable = fm_enable;
+	cmd.device_id = source;
+
+	for (i = 0; i < sizeof(cmd)/2; i++, ++ptrmem)
+		MM_DBG("cmd[%d]=0x%04x\n", i, *ptrmem);
+	afe_send_queue(afe, &cmd, sizeof(cmd));
+
+	mutex_unlock(&afe->lock);
+	return rc;
+error_adsp_enable:
+	msm_adsp_put(afe->mod);
+	afe->mod = NULL;
+error_adsp_get:
+	mutex_unlock(&afe->lock);
+	return rc;
+}
+EXPORT_SYMBOL(afe_config_fm_codec);
+
+int afe_config_fm_volume(uint16_t volume)
+{
+	struct afe_cmd_fm_volume_config cmd;
+	struct msm_afe_state *afe = &the_afe_state;
+	int rc = 0;
+
+	MM_INFO(" configure fm volume\n");
+	mutex_lock(&afe->lock);
+	if (!afe->in_use) {
+		/* enable afe */
+		rc = msm_adsp_get("AFETASK", &afe->mod, &afe->adsp_ops, afe);
+		if (rc < 0) {
+			MM_ERR("%s: failed to get AFETASK module\n", __func__);
+			goto error_adsp_get;
+		}
+		rc = msm_adsp_enable(afe->mod);
+		if (rc < 0)
+			goto error_adsp_enable;
+	}
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AFE_CMD_FM_PLAYBACK_VOLUME_CMD;
+	cmd.volume = volume;
+
+	afe_send_queue(afe, &cmd, sizeof(cmd));
+
+	mutex_unlock(&afe->lock);
+	return rc;
+error_adsp_enable:
+	msm_adsp_put(afe->mod);
+	afe->mod = NULL;
+error_adsp_get:
+	mutex_unlock(&afe->lock);
+	return rc;
+}
+EXPORT_SYMBOL(afe_config_fm_volume);
+
+int afe_config_fm_calibration_gain(uint16_t device_id,
+			uint16_t calibration_gain)
+{
+	struct afe_cmd_fm_calibgain_config cmd;
+	struct msm_afe_state *afe = &the_afe_state;
+	int rc = 0;
+
+	MM_INFO("Configure for rx device = 0x%4x, gain = 0x%4x\n", device_id,
+			calibration_gain);
+	mutex_lock(&afe->lock);
+	if (!afe->in_use) {
+		/* enable afe */
+		rc = msm_adsp_get("AFETASK", &afe->mod, &afe->adsp_ops, afe);
+		if (rc < 0) {
+			MM_ERR("%s: failed to get AFETASK module\n", __func__);
+			goto error_adsp_get;
+		}
+		rc = msm_adsp_enable(afe->mod);
+		if (rc < 0)
+			goto error_adsp_enable;
+	}
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AFE_CMD_FM_CALIBRATION_GAIN_CMD;
+	cmd.device_id = device_id;
+	cmd.calibration_gain = calibration_gain;
+
+	afe_send_queue(afe, &cmd, sizeof(cmd));
+
+	mutex_unlock(&afe->lock);
+	return rc;
+error_adsp_enable:
+	msm_adsp_put(afe->mod);
+	afe->mod = NULL;
+error_adsp_get:
+	mutex_unlock(&afe->lock);
+	return rc;
+}
+EXPORT_SYMBOL(afe_config_fm_calibration_gain);
+
+int afe_config_aux_codec(int pcm_ctl_value, int aux_codec_intf_value,
+				int data_format_pad)
+{
+	struct afe_cmd_aux_codec_config cmd;
+	struct msm_afe_state *afe = &the_afe_state;
+	int rc = 0;
+
+	MM_DBG(" configure aux codec \n");
+	mutex_lock(&afe->lock);
+	if (!afe->in_use && !afe->aux_conf_flag) {
+		/* enable afe */
+		rc = msm_adsp_get("AFETASK", &afe->mod, &afe->adsp_ops, afe);
+		if (rc < 0) {
+			MM_ERR("%s: failed to get AFETASK module\n", __func__);
+			goto error_adsp_get;
+		}
+		rc = msm_adsp_enable(afe->mod);
+		if (rc < 0)
+			goto error_adsp_enable;
+	}
+	afe->aux_conf_flag = 1;
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AFE_CMD_AUX_CODEC_CONFIG_CMD;
+	cmd.dma_path_ctl = 0;
+	cmd.pcm_ctl = pcm_ctl_value;
+	cmd.eight_khz_int_mode = 0;
+	cmd.aux_codec_intf_ctl = aux_codec_intf_value;
+	cmd.data_format_padding_info = data_format_pad;
+
+	afe_send_queue(afe, &cmd, sizeof(cmd));
+
+	mutex_unlock(&afe->lock);
+	return rc;
+error_adsp_enable:
+	msm_adsp_put(afe->mod);
+	afe->mod = NULL;
+error_adsp_get:
+	mutex_unlock(&afe->lock);
+	return rc;
+}
+EXPORT_SYMBOL(afe_config_aux_codec);
+
+int afe_config_rmc_block(struct acdb_rmc_block *acdb_rmc)
+{
+	struct afe_cmd_cfg_rmc cmd;
+	struct msm_afe_state *afe = &the_afe_state;
+	int rc = 0;
+	int i = 0;
+	unsigned short *ptrmem = (unsigned short *)&cmd;
+
+	MM_DBG(" configure rmc block\n");
+	mutex_lock(&afe->lock);
+	if (!afe->in_use && !afe->mod) {
+		/* enable afe */
+		rc = msm_adsp_get("AFETASK", &afe->mod, &afe->adsp_ops, afe);
+		if (rc < 0) {
+			MM_DBG("%s: failed to get AFETASK module\n", __func__);
+			goto error_adsp_get;
+		}
+		rc = msm_adsp_enable(afe->mod);
+		if (rc < 0)
+			goto error_adsp_enable;
+	}
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AFE_CMD_CFG_RMC_PARAMS;
+
+	cmd.rmc_mode = acdb_rmc->rmc_enable;
+	cmd.rmc_ipw_length_ms =	acdb_rmc->rmc_ipw_length_ms;
+	cmd.rmc_peak_length_ms = acdb_rmc->rmc_peak_length_ms;
+	cmd.rmc_init_pulse_length_ms = acdb_rmc->rmc_init_pulse_length_ms;
+	cmd.rmc_total_int_length_ms = acdb_rmc->rmc_total_int_length_ms;
+	cmd.rmc_rampupdn_length_ms = acdb_rmc->rmc_rampupdn_length_ms;
+	cmd.rmc_delay_length_ms = acdb_rmc->rmc_delay_length_ms;
+	cmd.rmc_detect_start_threshdb = acdb_rmc->rmc_detect_start_threshdb;
+	cmd.rmc_init_pulse_threshdb = acdb_rmc->rmc_init_pulse_threshdb;
+
+	for (i = 0; i < sizeof(cmd)/2; i++, ++ptrmem)
+		MM_DBG("cmd[%d]=0x%04x\n", i, *ptrmem);
+	afe_send_queue(afe, &cmd, sizeof(cmd));
+
+	mutex_unlock(&afe->lock);
+	return rc;
+error_adsp_enable:
+	msm_adsp_put(afe->mod);
+	afe->mod = NULL;
+error_adsp_get:
+	mutex_unlock(&afe->lock);
+	return rc;
+}
+EXPORT_SYMBOL(afe_config_rmc_block);
+
+int afe_disable(u8 path_id)
+{
+	struct msm_afe_state *afe = &the_afe_state;
+	int rc;
+
+	mutex_lock(&afe->lock);
+
+	BUG_ON(!afe->in_use);
+	MM_DBG("%s() path_id:%d codec state:%d\n", __func__, path_id,
+	afe->codec_config[GETDEVICEID(path_id)]);
+	afe_dsp_codec_config(afe, path_id, 0, NULL);
+	rc = wait_event_timeout(afe->wait,
+		!afe->codec_config[GETDEVICEID(path_id)],
+		msecs_to_jiffies(AFE_MAX_TIMEOUT));
+	if (!rc) {
+		MM_ERR("AFE failed to respond within %d ms\n", AFE_MAX_TIMEOUT);
+		rc = -1;
+	} else
+		rc = 0;
+	afe->in_use--;
+	MM_DBG("%s() in_use:%d \n", __func__, afe->in_use);
+	if (!afe->in_use) {
+		msm_adsp_disable(afe->mod);
+		msm_adsp_put(afe->mod);
+		afe->aux_conf_flag = 0;
+		afe->mod = NULL;
+	}
+	mutex_unlock(&afe->lock);
+	return rc;
+}
+EXPORT_SYMBOL(afe_disable);
+
+
+#ifdef CONFIG_DEBUG_FS
+static int afe_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	MM_INFO("debug intf %s\n", (char *) file->private_data);
+	return 0;
+}
+
+static ssize_t afe_debug_write(struct file *filp,
+	const char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+	char *lb_str = filp->private_data;
+	char cmd;
+
+	if (get_user(cmd, ubuf))
+		return -EFAULT;
+
+	MM_INFO("%s %c\n", lb_str, cmd);
+
+	if (!strcmp(lb_str, "afe_loopback")) {
+		switch (cmd) {
+		case '1':
+			afe_loopback(1);
+			break;
+		case '0':
+			afe_loopback(0);
+			break;
+		}
+	}
+
+	return cnt;
+}
+
+static const struct file_operations afe_debug_fops = {
+	.open = afe_debug_open,
+	.write = afe_debug_write
+};
+#endif
+
+static int __init afe_init(void)
+{
+	struct msm_afe_state *afe = &the_afe_state;
+
+	MM_INFO("AFE driver init\n");
+
+	memset(afe, 0, sizeof(struct msm_afe_state));
+	afe->adsp_ops.event = afe_dsp_event;
+	mutex_init(&afe->lock);
+	init_waitqueue_head(&afe->wait);
+
+#ifdef CONFIG_DEBUG_FS
+	debugfs_afelb = debugfs_create_file("afe_loopback",
+	S_IFREG | S_IWUGO, NULL, (void *) "afe_loopback",
+	&afe_debug_fops);
+#endif
+
+	return 0;
+}
+
+static void __exit afe_exit(void)
+{
+	MM_INFO("AFE driver exit\n");
+#ifdef CONFIG_DEBUG_FS
+	if (debugfs_afelb)
+		debugfs_remove(debugfs_afelb);
+#endif
+	if (the_afe_state.mod)
+		msm_adsp_put(the_afe_state.mod);
+	return;
+}
+
+module_init(afe_init);
+module_exit(afe_exit);
+
+MODULE_DESCRIPTION("MSM AFE driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_a2dp_in.c b/arch/arm/mach-msm/qdsp5v2/audio_a2dp_in.c
new file mode 100644
index 0000000..bb3404a7
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_a2dp_in.c
@@ -0,0 +1,977 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * sbc/pcm audio input driver
+ * Based on the pcm input driver in arch/arm/mach-msm/qdsp5v2/audio_pcm_in.c
+ *
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * 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 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/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/msm_audio.h>
+#include <linux/msm_audio_sbc.h>
+#include <linux/android_pmem.h>
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+
+#include <mach/msm_adsp.h>
+#include <mach/qdsp5v2/qdsp5audreccmdi.h>
+#include <mach/qdsp5v2/qdsp5audrecmsg.h>
+#include <mach/qdsp5v2/audpreproc.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/debug_mm.h>
+
+/* FRAME_NUM must be a power of two */
+#define FRAME_NUM		(8)
+#define FRAME_SIZE		(2052 * 2)
+#define FRAME_SIZE_SBC		(768 * 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;
+	uint32_t frame_num;
+	uint32_t frame_len;
+};
+
+struct audio_a2dp_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;
+
+	struct msm_adsp_module *audrec;
+
+	struct audrec_session_info session_info; /*audrec session info*/
+
+	/* 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;
+	struct msm_audio_sbc_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 mode;
+
+	const char *module_name;
+	unsigned queue_ids;
+	uint16_t enc_id; /* Session Id */
+
+	uint16_t source; /* Encoding source bit mask */
+	uint32_t device_events; /* device events interested in */
+	uint32_t dev_cnt;
+	spinlock_t dev_lock;
+
+	/* 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 */
+	int abort; /* set when error, like sample rate mismatch */
+};
+
+static struct audio_a2dp_in the_audio_a2dp_in;
+
+struct wav_frame {
+	uint16_t frame_count_lsw;
+	uint16_t frame_count_msw;
+	uint16_t frame_length;
+	uint16_t erased_a2dp;
+	unsigned char raw_bitstream[]; /* samples */
+};
+
+struct sbc_frame {
+	uint16_t bit_rate_msw;
+	uint16_t bit_rate_lsw;
+	uint16_t frame_length;
+	uint16_t frame_num;
+	unsigned char raw_bitstream[]; /* samples */
+};
+
+struct audio_frame {
+	union {
+		struct wav_frame wav;
+		struct sbc_frame sbc;
+	} a2dp;
+} __attribute__((packed));
+
+/* Audrec Queue command sent macro's */
+#define audrec_send_bitstreamqueue(audio, cmd, len) \
+	msm_adsp_write(audio->audrec, ((audio->queue_ids & 0xFFFF0000) >> 16),\
+			cmd, len)
+
+#define audrec_send_audrecqueue(audio, cmd, len) \
+	msm_adsp_write(audio->audrec, (audio->queue_ids & 0x0000FFFF),\
+			cmd, len)
+
+/* DSP command send functions */
+static int auda2dp_in_enc_config(struct audio_a2dp_in *audio, int enable);
+static int auda2dp_in_param_config(struct audio_a2dp_in *audio);
+static int auda2dp_in_mem_config(struct audio_a2dp_in *audio);
+static int auda2dp_in_record_config(struct audio_a2dp_in *audio, int enable);
+static int auda2dp_dsp_read_buffer(struct audio_a2dp_in *audio,
+							uint32_t read_cnt);
+
+static void auda2dp_in_get_dsp_frames(struct audio_a2dp_in *audio);
+
+static void auda2dp_in_flush(struct audio_a2dp_in *audio);
+
+static void a2dp_in_listener(u32 evt_id, union auddev_evt_data *evt_payload,
+				void *private_data)
+{
+	struct audio_a2dp_in *audio = (struct audio_a2dp_in *) private_data;
+	unsigned long flags;
+
+	MM_DBG("evt_id = 0x%8x\n", evt_id);
+	switch (evt_id) {
+	case AUDDEV_EVT_DEV_RDY: {
+		MM_DBG("AUDDEV_EVT_DEV_RDY\n");
+		spin_lock_irqsave(&audio->dev_lock, flags);
+		audio->dev_cnt++;
+		audio->source |= (0x1 << evt_payload->routing_id);
+		spin_unlock_irqrestore(&audio->dev_lock, flags);
+
+		if ((audio->running == 1) && (audio->enabled == 1))
+			auda2dp_in_record_config(audio, 1);
+
+		break;
+	}
+	case AUDDEV_EVT_DEV_RLS: {
+		MM_DBG("AUDDEV_EVT_DEV_RLS\n");
+		spin_lock_irqsave(&audio->dev_lock, flags);
+		audio->dev_cnt--;
+		audio->source &= ~(0x1 << evt_payload->routing_id);
+		spin_unlock_irqrestore(&audio->dev_lock, flags);
+
+		if (!audio->running || !audio->enabled)
+			break;
+
+		/* Turn of as per source */
+		if (audio->source)
+			auda2dp_in_record_config(audio, 1);
+		else
+			/* Turn off all */
+			auda2dp_in_record_config(audio, 0);
+
+		break;
+	}
+	case AUDDEV_EVT_FREQ_CHG: {
+		MM_DBG("Encoder Driver got sample rate change event\n");
+		MM_DBG("sample rate %d\n", evt_payload->freq_info.sample_rate);
+		MM_DBG("dev_type %d\n", evt_payload->freq_info.dev_type);
+		MM_DBG("acdb_dev_id %d\n", evt_payload->freq_info.acdb_dev_id);
+		if (audio->running == 1) {
+			/* Stop Recording sample rate does not match
+			   with device sample rate */
+			if (evt_payload->freq_info.sample_rate !=
+				audio->samp_rate) {
+				auda2dp_in_record_config(audio, 0);
+				audio->abort = 1;
+				wake_up(&audio->wait);
+			}
+		}
+		break;
+	}
+	default:
+		MM_ERR("wrong event %d\n", evt_id);
+		break;
+	}
+}
+
+/* ------------------- dsp preproc event handler--------------------- */
+static void audpreproc_dsp_event(void *data, unsigned id,  void *msg)
+{
+	struct audio_a2dp_in *audio = data;
+
+	switch (id) {
+	case AUDPREPROC_ERROR_MSG: {
+		struct audpreproc_err_msg *err_msg = msg;
+
+		MM_ERR("ERROR_MSG: stream id %d err idx %d\n",
+		err_msg->stream_id, err_msg->aud_preproc_err_idx);
+		/* Error case */
+		wake_up(&audio->wait_enable);
+		break;
+	}
+	case AUDPREPROC_CMD_CFG_DONE_MSG: {
+		MM_DBG("CMD_CFG_DONE_MSG \n");
+		break;
+	}
+	case AUDPREPROC_CMD_ENC_CFG_DONE_MSG: {
+		struct audpreproc_cmd_enc_cfg_done_msg *enc_cfg_msg = msg;
+
+		MM_DBG("CMD_ENC_CFG_DONE_MSG: stream id %d enc type \
+			0x%8x\n", enc_cfg_msg->stream_id,
+			enc_cfg_msg->rec_enc_type);
+		/* Encoder enable success */
+		if (enc_cfg_msg->rec_enc_type & ENCODE_ENABLE)
+			auda2dp_in_param_config(audio);
+		else { /* Encoder disable success */
+			audio->running = 0;
+			auda2dp_in_record_config(audio, 0);
+		}
+		break;
+	}
+	case AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG: {
+		MM_DBG("CMD_ENC_PARAM_CFG_DONE_MSG \n");
+		auda2dp_in_mem_config(audio);
+		break;
+	}
+	case AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG: {
+		MM_DBG("AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG \n");
+		wake_up(&audio->wait_enable);
+		break;
+	}
+	default:
+		MM_ERR("Unknown Event id %d\n", id);
+	}
+}
+
+/* ------------------- dsp audrec event handler--------------------- */
+static void audrec_dsp_event(void *data, unsigned id, size_t len,
+			    void (*getevent)(void *ptr, size_t len))
+{
+	struct audio_a2dp_in *audio = data;
+
+	switch (id) {
+	case AUDREC_CMD_MEM_CFG_DONE_MSG: {
+		MM_DBG("CMD_MEM_CFG_DONE MSG DONE\n");
+		audio->running = 1;
+		if (audio->dev_cnt > 0)
+			auda2dp_in_record_config(audio, 1);
+		break;
+	}
+	case AUDREC_FATAL_ERR_MSG: {
+		struct audrec_fatal_err_msg fatal_err_msg;
+
+		getevent(&fatal_err_msg, AUDREC_FATAL_ERR_MSG_LEN);
+		MM_ERR("FATAL_ERR_MSG: err id %d\n",
+				fatal_err_msg.audrec_err_id);
+		/* Error stop the encoder */
+		audio->stopped = 1;
+		wake_up(&audio->wait);
+		break;
+	}
+	case AUDREC_UP_PACKET_READY_MSG: {
+		struct audrec_up_pkt_ready_msg pkt_ready_msg;
+
+		getevent(&pkt_ready_msg, AUDREC_UP_PACKET_READY_MSG_LEN);
+		MM_DBG("UP_PACKET_READY_MSG: write cnt lsw  %d \
+		write cnt msw %d read cnt lsw %d  read cnt msw %d \n",\
+		pkt_ready_msg.audrec_packet_write_cnt_lsw, \
+		pkt_ready_msg.audrec_packet_write_cnt_msw, \
+		pkt_ready_msg.audrec_up_prev_read_cnt_lsw, \
+		pkt_ready_msg.audrec_up_prev_read_cnt_msw);
+
+		auda2dp_in_get_dsp_frames(audio);
+		break;
+	}
+	case ADSP_MESSAGE_ID: {
+		MM_DBG("Received ADSP event: module audrectask\n");
+		break;
+	}
+	default:
+		MM_ERR("Unknown Event id %d\n", id);
+	}
+}
+
+static void auda2dp_in_get_dsp_frames(struct audio_a2dp_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);
+	if (audio->enc_type == ENC_TYPE_WAV)
+		audio->in[index].size = frame->a2dp.wav.frame_length;
+	else if (audio->enc_type == ENC_TYPE_SBC) {
+		audio->in[index].size = frame->a2dp.sbc.frame_length *
+						frame->a2dp.sbc.frame_num;
+		audio->in[index].frame_num = frame->a2dp.sbc.frame_num;
+		audio->in[index].frame_len = frame->a2dp.sbc.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)
+		audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1);
+	else
+		audio->in_count++;
+
+	auda2dp_dsp_read_buffer(audio, audio->dsp_cnt++);
+	spin_unlock_irqrestore(&audio->dsp_lock, flags);
+	wake_up(&audio->wait);
+}
+
+static struct msm_adsp_ops audrec_adsp_ops = {
+	.event = audrec_dsp_event,
+};
+
+static int auda2dp_in_enc_config(struct audio_a2dp_in *audio, int enable)
+{
+	struct audpreproc_audrec_cmd_enc_cfg cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ENC_CFG_2;
+	cmd.stream_id = audio->enc_id;
+
+	if (enable)
+		cmd.audrec_enc_type = audio->enc_type | ENCODE_ENABLE;
+	else
+		cmd.audrec_enc_type &= ~(ENCODE_ENABLE);
+
+	return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+static int auda2dp_in_param_config(struct audio_a2dp_in *audio)
+{
+	if (audio->enc_type == ENC_TYPE_WAV) {
+		struct audpreproc_audrec_cmd_parm_cfg_wav cmd;
+
+		memset(&cmd, 0, sizeof(cmd));
+		cmd.common.cmd_id = AUDPREPROC_AUDREC_CMD_PARAM_CFG;
+		cmd.common.stream_id = audio->enc_id;
+
+		cmd.aud_rec_samplerate_idx = audio->samp_rate;
+		cmd.aud_rec_stereo_mode = audio->channel_mode;
+		return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+	} else if (audio->enc_type == ENC_TYPE_SBC) {
+		struct audpreproc_audrec_cmd_parm_cfg_sbc cmd;
+
+		memset(&cmd, 0, sizeof(cmd));
+		cmd.common.cmd_id = AUDPREPROC_AUDREC_CMD_PARAM_CFG;
+		cmd.common.stream_id = audio->enc_id;
+		cmd.aud_rec_sbc_enc_param =
+			(audio->cfg.number_of_blocks <<
+			AUDREC_SBC_ENC_PARAM_NUM_SUB_BLOCKS_MASK) |
+			(audio->cfg.number_of_subbands <<
+			AUDREC_SBC_ENC_PARAM_NUM_SUB_BANDS_MASK) |
+			(audio->cfg.mode <<
+			AUDREC_SBC_ENC_PARAM_MODE_MASK) |
+			(audio->cfg.bit_allocation <<
+			AUDREC_SBC_ENC_PARAM_BIT_ALLOC_MASK);
+		cmd.aud_rec_sbc_bit_rate_msw =
+			(audio->cfg.bit_rate & 0xFFFF0000) >> 16;
+		cmd.aud_rec_sbc_bit_rate_lsw =
+			(audio->cfg.bit_rate & 0xFFFF);
+		return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+	}
+	return 0;
+}
+
+/* To Do: msm_snddev_route_enc(audio->enc_id); */
+static int auda2dp_in_record_config(struct audio_a2dp_in *audio, int enable)
+{
+	struct audpreproc_afe_cmd_audio_record_cfg cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG;
+	cmd.stream_id = audio->enc_id;
+	if (enable)
+		cmd.destination_activity = AUDIO_RECORDING_TURN_ON;
+	else
+		cmd.destination_activity = AUDIO_RECORDING_TURN_OFF;
+
+	cmd.source_mix_mask = audio->source;
+	if (audio->enc_id == 2) {
+		if ((cmd.source_mix_mask &
+				INTERNAL_CODEC_TX_SOURCE_MIX_MASK) ||
+			(cmd.source_mix_mask & AUX_CODEC_TX_SOURCE_MIX_MASK) ||
+			(cmd.source_mix_mask & VOICE_UL_SOURCE_MIX_MASK) ||
+			(cmd.source_mix_mask & VOICE_DL_SOURCE_MIX_MASK)) {
+			cmd.pipe_id = SOURCE_PIPE_1;
+		}
+		if (cmd.source_mix_mask &
+				AUDPP_A2DP_PIPE_SOURCE_MIX_MASK)
+			cmd.pipe_id |= SOURCE_PIPE_0;
+	}
+	return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+static int auda2dp_in_mem_config(struct audio_a2dp_in *audio)
+{
+	struct audrec_cmd_arecmem_cfg cmd;
+	uint16_t *data = (void *) audio->data;
+	int n;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDREC_CMD_MEM_CFG_CMD;
+	cmd.audrec_up_pkt_intm_count = 1;
+	cmd.audrec_ext_pkt_start_addr_msw = audio->phys >> 16;
+	cmd.audrec_ext_pkt_start_addr_lsw = audio->phys;
+	cmd.audrec_ext_pkt_buf_number = FRAME_NUM;
+
+	/* prepare buffer pointers:
+	 * Wav:
+	 * Mono: 1024 samples + 4 halfword header
+	 * Stereo: 2048 samples + 4 halfword header
+	 * SBC:
+	 * 768 + 4 halfword header
+	 */
+	if (audio->enc_type == ENC_TYPE_SBC) {
+		for (n = 0; n < FRAME_NUM; n++) {
+			audio->in[n].data = data + 4;
+			data += (4 + (FRAME_SIZE_SBC/2));
+			MM_DBG("0x%8x\n", (int)(audio->in[n].data - 8));
+		}
+	} else if (audio->enc_type == ENC_TYPE_WAV) {
+		for (n = 0; n < FRAME_NUM; n++) {
+			audio->in[n].data = data + 4;
+			data += (4 + (audio->channel_mode ? 2048 : 1024));
+			MM_DBG("0x%8x\n", (int)(audio->in[n].data - 8));
+		}
+	}
+
+	return audrec_send_audrecqueue(audio, &cmd, sizeof(cmd));
+}
+
+static int auda2dp_dsp_read_buffer(struct audio_a2dp_in *audio,
+							uint32_t read_cnt)
+{
+	struct up_audrec_packet_ext_ptr cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = UP_AUDREC_PACKET_EXT_PTR;
+	cmd.audrec_up_curr_read_count_msw = read_cnt >> 16;
+	cmd.audrec_up_curr_read_count_lsw = read_cnt;
+
+	return audrec_send_bitstreamqueue(audio, &cmd, sizeof(cmd));
+}
+
+/* must be called with audio->lock held */
+static int auda2dp_in_enable(struct audio_a2dp_in *audio)
+{
+	if (audio->enabled)
+		return 0;
+
+	if (audpreproc_enable(audio->enc_id, &audpreproc_dsp_event, audio)) {
+		MM_ERR("msm_adsp_enable(audpreproc) failed\n");
+		return -ENODEV;
+	}
+
+	if (msm_adsp_enable(audio->audrec)) {
+		MM_ERR("msm_adsp_enable(audrec) failed\n");
+		audpreproc_disable(audio->enc_id, audio);
+		return -ENODEV;
+	}
+	audio->enabled = 1;
+	auda2dp_in_enc_config(audio, 1);
+
+	return 0;
+}
+
+/* must be called with audio->lock held */
+static int auda2dp_in_disable(struct audio_a2dp_in *audio)
+{
+	if (audio->enabled) {
+		audio->enabled = 0;
+		auda2dp_in_enc_config(audio, 0);
+		wake_up(&audio->wait);
+		wait_event_interruptible_timeout(audio->wait_enable,
+				audio->running == 0, 1*HZ);
+		msm_adsp_disable(audio->audrec);
+		audpreproc_disable(audio->enc_id, audio);
+	}
+	return 0;
+}
+
+static void auda2dp_in_flush(struct audio_a2dp_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;
+	}
+	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);
+}
+
+/* ------------------- device --------------------- */
+static long auda2dp_in_ioctl(struct file *file,
+				unsigned int cmd, unsigned long arg)
+{
+	struct audio_a2dp_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);
+		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: {
+		uint32_t freq;
+		/* Poll at 48KHz always */
+		freq = 48000;
+		MM_DBG("AUDIO_START\n");
+		rc = msm_snddev_request_freq(&freq, audio->enc_id,
+					SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+		MM_DBG("sample rate configured %d sample rate requested %d\n",
+				freq, audio->samp_rate);
+		if (rc < 0) {
+			MM_DBG("sample rate can not be set, return code %d\n",\
+							rc);
+			msm_snddev_withdraw_freq(audio->enc_id,
+						SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+			MM_DBG("msm_snddev_withdraw_freq\n");
+			break;
+		}
+		/*update aurec session info in audpreproc layer*/
+		audio->session_info.session_id = audio->enc_id;
+		audio->session_info.sampling_freq = audio->samp_rate;
+		audpreproc_update_audrec_info(&audio->session_info);
+		rc = auda2dp_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;
+				msm_snddev_withdraw_freq(audio->enc_id,
+					SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+				MM_DBG("msm_snddev_withdraw_freq\n");
+			} else
+				rc = 0;
+		}
+		audio->stopped = 0;
+		break;
+	}
+	case AUDIO_STOP: {
+		/*reset the sampling frequency information at audpreproc layer*/
+		audio->session_info.sampling_freq = 0;
+		audpreproc_update_audrec_info(&audio->session_info);
+		rc = auda2dp_in_disable(audio);
+		rc = msm_snddev_withdraw_freq(audio->enc_id,
+					SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+		MM_DBG("msm_snddev_withdraw_freq\n");
+		audio->stopped = 1;
+		audio->abort = 0;
+		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);
+			auda2dp_in_flush(audio);
+			mutex_unlock(&audio->read_lock);
+		}
+		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->enc_type == ENC_TYPE_SBC) &&
+				(cfg.buffer_size != FRAME_SIZE_SBC))
+			rc = -EINVAL;
+		else
+			audio->buffer_size = cfg.buffer_size;
+		break;
+	}
+	case AUDIO_GET_STREAM_CONFIG: {
+		struct msm_audio_stream_config cfg;
+		memset(&cfg, 0, sizeof(cfg));
+		if (audio->enc_type == ENC_TYPE_SBC)
+			cfg.buffer_size = FRAME_SIZE_SBC;
+		else
+			cfg.buffer_size = MONO_DATA_SIZE;
+		cfg.buffer_count = FRAME_NUM;
+		if (copy_to_user((void *) arg, &cfg, sizeof(cfg)))
+			rc = -EFAULT;
+		break;
+	}
+	case AUDIO_SET_SBC_ENC_CONFIG: {
+		if (copy_from_user(&audio->cfg, (void *) arg,
+						sizeof(audio->cfg))) {
+			rc = -EFAULT;
+			break;
+		}
+		audio->samp_rate = audio->cfg.sample_rate;
+		audio->channel_mode = audio->cfg.channels;
+		audio->enc_type = ENC_TYPE_SBC;
+		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_MODE_MONO;
+			audio->buffer_size = MONO_DATA_SIZE;
+		} else if (cfg.channel_count == 2) {
+			cfg.channel_count = AUDREC_CMD_MODE_STEREO;
+			audio->buffer_size = STEREO_DATA_SIZE;
+		} else {
+			rc = -EINVAL;
+			break;
+		}
+		audio->samp_rate = cfg.sample_rate;
+		audio->channel_mode = cfg.channel_count;
+		audio->enc_type = ENC_TYPE_WAV;
+		break;
+	}
+	case AUDIO_GET_SBC_ENC_CONFIG: {
+		struct msm_audio_sbc_enc_config cfg;
+		memset(&cfg, 0, sizeof(cfg));
+		cfg.bit_allocation = audio->cfg.bit_allocation;
+		cfg.mode =  audio->cfg.mode;
+		cfg.number_of_subbands = audio->cfg.number_of_subbands;
+		cfg.number_of_blocks = audio->cfg.number_of_blocks;
+		cfg.sample_rate = audio->samp_rate;
+		cfg.channels = audio->channel_mode;
+		cfg.bit_rate = audio->cfg.bit_rate;
+		if (copy_to_user((void *) arg, &cfg, sizeof(cfg)))
+			rc = -EFAULT;
+		break;
+	}
+	case AUDIO_GET_CONFIG: {
+		struct msm_audio_config cfg;
+		memset(&cfg, 0, sizeof(cfg));
+		cfg.buffer_count = FRAME_NUM;
+		cfg.sample_rate = audio->samp_rate;
+		if (audio->channel_mode == AUDREC_CMD_MODE_MONO) {
+			cfg.channel_count = 1;
+			cfg.buffer_size = MONO_DATA_SIZE;
+		} else {
+			cfg.channel_count = 2;
+			cfg.buffer_size = STEREO_DATA_SIZE;
+		}
+		cfg.type = ENC_TYPE_WAV;
+		if (copy_to_user((void *) arg, &cfg, sizeof(cfg)))
+			rc = -EFAULT;
+		break;
+	}
+	case AUDIO_GET_SESSION_ID: {
+		if (copy_to_user((void *) arg, &audio->enc_id,
+			sizeof(unsigned short))) {
+			rc = -EFAULT;
+		}
+		break;
+	}
+	default:
+		rc = -EINVAL;
+	}
+	mutex_unlock(&audio->lock);
+	return rc;
+}
+
+static ssize_t auda2dp_in_read(struct file *file,
+				char __user *buf,
+				size_t count, loff_t *pos)
+{
+	struct audio_a2dp_in *audio = file->private_data;
+	unsigned long flags;
+	const char __user *start = buf;
+	void *data;
+	uint32_t index;
+	uint32_t size;
+	int rc = 0;
+	uint32_t f_len = 0, f_num = 0;
+	int i = 0;
+
+	mutex_lock(&audio->read_lock);
+	while (count > 0) {
+		rc = wait_event_interruptible(
+			audio->wait, (audio->in_count > 0) || audio->stopped ||
+			audio->abort);
+
+		if (rc < 0)
+			break;
+
+		if (audio->stopped && !audio->in_count) {
+			MM_DBG("Driver in stop state, No more buffer to read");
+			rc = 0;/* End of File */
+			break;
+		}
+
+		if (audio->abort) {
+			rc = -EPERM; /* Not permitted due to abort */
+			break;
+		}
+
+		index = audio->in_tail;
+		data = (uint8_t *) audio->in[index].data;
+		size = audio->in[index].size;
+		if (count >= size) {
+			if (audio->enc_type == ENC_TYPE_SBC &&
+				(audio->in[index].frame_len % 2)) {
+				f_len = audio->in[index].frame_len;
+				f_num = audio->in[index].frame_num;
+				for (i = 0; i < f_num; i++) {
+					if (copy_to_user(&buf[i * f_len],
+					(uint8_t *) (data + (i * (f_len + 1))),
+					f_len)) {
+						rc = -EFAULT;
+						break;
+					}
+				}
+			} else {
+				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 auda2dp_in_write(struct file *file,
+				const char __user *buf,
+				size_t count, loff_t *pos)
+{
+	return -EINVAL;
+}
+
+static int auda2dp_in_release(struct inode *inode, struct file *file)
+{
+	struct audio_a2dp_in *audio = file->private_data;
+
+	mutex_lock(&audio->lock);
+	/* with draw frequency for session
+	   incase not stopped the driver */
+	msm_snddev_withdraw_freq(audio->enc_id, SNDDEV_CAP_TX,
+					AUDDEV_CLNT_ENC);
+	auddev_unregister_evt_listner(AUDDEV_CLNT_ENC, audio->enc_id);
+	/*reset the sampling frequency information at audpreproc layer*/
+	audio->session_info.sampling_freq = 0;
+	audpreproc_update_audrec_info(&audio->session_info);
+	auda2dp_in_disable(audio);
+	auda2dp_in_flush(audio);
+	msm_adsp_put(audio->audrec);
+	audpreproc_aenc_free(audio->enc_id);
+	audio->audrec = NULL;
+	audio->opened = 0;
+	if (audio->data) {
+		iounmap(audio->data);
+		pmem_kfree(audio->phys);
+		audio->data = NULL;
+	}
+	mutex_unlock(&audio->lock);
+	return 0;
+}
+
+static int auda2dp_in_open(struct inode *inode, struct file *file)
+{
+	struct audio_a2dp_in *audio = &the_audio_a2dp_in;
+	int rc;
+	int encid;
+
+	mutex_lock(&audio->lock);
+	if (audio->opened) {
+		rc = -EBUSY;
+		goto done;
+	}
+
+	audio->phys = pmem_kalloc(DMASZ, PMEM_MEMTYPE_EBI1|
+					PMEM_ALIGNMENT_4K);
+	if (!IS_ERR((void *)audio->phys)) {
+		audio->data = ioremap(audio->phys, DMASZ);
+		if (!audio->data) {
+			MM_ERR("could not allocate DMA buffers\n");
+			rc = -ENOMEM;
+			pmem_kfree(audio->phys);
+			goto done;
+		}
+	} else {
+		MM_ERR("could not allocate DMA buffers\n");
+		rc = -ENOMEM;
+		goto done;
+	}
+	MM_DBG("Memory addr = 0x%8x  phy addr = 0x%8x\n",\
+		(int) audio->data, (int) audio->phys);
+
+	if ((file->f_mode & FMODE_WRITE) &&
+				(file->f_mode & FMODE_READ)) {
+		rc = -EACCES;
+		MM_ERR("Non tunnel encoding is not supported\n");
+		goto done;
+	} else if (!(file->f_mode & FMODE_WRITE) &&
+					(file->f_mode & FMODE_READ)) {
+		audio->mode = MSM_AUD_ENC_MODE_TUNNEL;
+		MM_DBG("Opened for Tunnel mode encoding\n");
+	} else {
+		rc = -EACCES;
+		goto done;
+	}
+	/* Settings will be re-config at AUDIO_SET_CONFIG/SBC_ENC_CONFIG,
+	 * but at least we need to have initial config
+	 */
+	audio->channel_mode = AUDREC_CMD_MODE_MONO;
+	audio->buffer_size = FRAME_SIZE_SBC;
+	audio->samp_rate = 48000;
+	audio->enc_type = ENC_TYPE_SBC | audio->mode;
+	audio->cfg.bit_allocation = AUDIO_SBC_BA_SNR;
+	audio->cfg.mode = AUDIO_SBC_MODE_JSTEREO;
+	audio->cfg.number_of_subbands = AUDIO_SBC_BANDS_8;
+	audio->cfg.number_of_blocks = AUDIO_SBC_BLOCKS_16;
+	audio->cfg.bit_rate = 320000; /* max 512kbps(mono), 320kbs(others) */
+
+	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;
+	}
+
+	audio->stopped = 0;
+	audio->source = 0;
+	audio->abort = 0;
+	auda2dp_in_flush(audio);
+	audio->device_events = AUDDEV_EVT_DEV_RDY | AUDDEV_EVT_DEV_RLS |
+				AUDDEV_EVT_FREQ_CHG;
+
+	rc = auddev_register_evt_listner(audio->device_events,
+					AUDDEV_CLNT_ENC, audio->enc_id,
+					a2dp_in_listener, (void *) audio);
+	if (rc) {
+		MM_ERR("failed to register device event listener\n");
+		goto evt_error;
+	}
+	file->private_data = audio;
+	audio->opened = 1;
+	rc = 0;
+done:
+	mutex_unlock(&audio->lock);
+	return rc;
+evt_error:
+	msm_adsp_put(audio->audrec);
+	audpreproc_aenc_free(audio->enc_id);
+	mutex_unlock(&audio->lock);
+	return rc;
+}
+
+static const struct file_operations audio_a2dp_in_fops = {
+	.owner		= THIS_MODULE,
+	.open		= auda2dp_in_open,
+	.release	= auda2dp_in_release,
+	.read		= auda2dp_in_read,
+	.write		= auda2dp_in_write,
+	.unlocked_ioctl	= auda2dp_in_ioctl,
+};
+
+struct miscdevice audio_a2dp_in_misc = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "msm_a2dp_in",
+	.fops	= &audio_a2dp_in_fops,
+};
+
+static int __init auda2dp_in_init(void)
+{
+	mutex_init(&the_audio_a2dp_in.lock);
+	mutex_init(&the_audio_a2dp_in.read_lock);
+	spin_lock_init(&the_audio_a2dp_in.dsp_lock);
+	spin_lock_init(&the_audio_a2dp_in.dev_lock);
+	init_waitqueue_head(&the_audio_a2dp_in.wait);
+	init_waitqueue_head(&the_audio_a2dp_in.wait_enable);
+	return misc_register(&audio_a2dp_in_misc);
+}
+
+device_initcall(auda2dp_in_init);
+
+MODULE_DESCRIPTION("MSM SBC encode driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_aac.c b/arch/arm/mach-msm/qdsp5v2/audio_aac.c
new file mode 100644
index 0000000..d75dc92
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_aac.c
@@ -0,0 +1,2025 @@
+/*
+ * aac audio decoder device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * 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/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 <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <linux/slab.h>
+#include <mach/msm_adsp.h>
+#include <linux/msm_audio_aac.h>
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/qdsp5audplaycmdi.h>
+#include <mach/qdsp5v2/qdsp5audplaymsg.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/qdsp5v2/audpp.h>
+#include <mach/qdsp5v2/audio_dev_ctl.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 */
+
+#define BITSTREAM_ERROR_THRESHOLD_VALUE 0x1 /* DEFAULT THRESHOLD VALUE */
+
+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;
+
+	/* AV sync Info */
+	int avsync_flag;              /* Flag to indicate feedback from DSP */
+	wait_queue_head_t avsync_wait;/* Wait queue for AV Sync Message     */
+	/* 48 bits sample/bytes counter per channel */
+	uint16_t avsync[AUDPP_AVSYNC_CH_COUNT * AUDPP_AVSYNC_NUM_WORDS + 1];
+
+	/* 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 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;
+	int16_t source;
+
+#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;
+	uint32_t device_events;
+
+	struct msm_audio_bitstream_info stream_info;
+	struct msm_audio_bitstream_error_info bitstream_error_info;
+	uint32_t bitstream_error_threshold_value;
+
+	int eq_enable;
+	int eq_needs_commit;
+	struct audpp_cmd_cfg_object_params_eqalizer eq;
+	struct 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_error_threshold_config(struct audio *audio);
+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);
+
+/* must be called with audio->lock held */
+static int audio_enable(struct audio *audio)
+{
+	MM_DBG("\n"); /* Macro prints the file name and function */
+
+	if (audio->enabled)
+		return 0;
+
+	audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+	audio->out_tail = 0;
+	audio->out_needed = 0;
+
+	if (msm_adsp_enable(audio->audplay)) {
+		MM_ERR("msm_adsp_enable(audplay) failed\n");
+		return -ENODEV;
+	}
+
+	if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) {
+		MM_ERR("audpp_enable() failed\n");
+		msm_adsp_disable(audio->audplay);
+		return -ENODEV;
+	}
+	audio->enabled = 1;
+	return 0;
+}
+
+static void aac_listner(u32 evt_id, union auddev_evt_data *evt_payload,
+			void *private_data)
+{
+	struct audio *audio = (struct audio *) private_data;
+	switch (evt_id) {
+	case AUDDEV_EVT_DEV_RDY:
+		MM_DBG(":AUDDEV_EVT_DEV_RDY\n");
+		audio->source |= (0x1 << evt_payload->routing_id);
+		if (audio->running == 1 && audio->enabled == 1)
+			audpp_route_stream(audio->dec_id, audio->source);
+		break;
+	case AUDDEV_EVT_DEV_RLS:
+		MM_DBG(":AUDDEV_EVT_DEV_RLS\n");
+		audio->source &= ~(0x1 << evt_payload->routing_id);
+		if (audio->running == 1 && audio->enabled == 1)
+			audpp_route_stream(audio->dec_id, audio->source);
+		break;
+	case AUDDEV_EVT_STREAM_VOL_CHG:
+		audio->vol_pan.volume = evt_payload->session_vol;
+		MM_DBG(":AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n",
+				audio->vol_pan.volume);
+		if (audio->running)
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+					POPP);
+		break;
+	default:
+		MM_ERR(":ERROR:wrong event\n");
+		break;
+	}
+}
+/* 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);
+		audio->out_needed = 0;
+	}
+	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_bitstream_error_info(struct audio *audio, uint32_t *payload)
+{
+	unsigned long flags;
+	union msm_audio_event_payload e_payload;
+
+	if (payload[0] != AUDDEC_DEC_AAC) {
+		MM_ERR("Unexpected bitstream error info from DSP:\
+				Invalid decoder\n");
+		return;
+	}
+
+	/* get stream info from DSP msg */
+	spin_lock_irqsave(&audio->dsp_lock, flags);
+
+	audio->bitstream_error_info.dec_id = payload[0];
+	audio->bitstream_error_info.err_msg_indicator = payload[1];
+	audio->bitstream_error_info.err_type = payload[2];
+
+	spin_unlock_irqrestore(&audio->dsp_lock, flags);
+	MM_ERR("bit_stream_error_type=%d error_count=%d\n",
+			audio->bitstream_error_info.err_type, (0x0000FFFF &
+			audio->bitstream_error_info.err_msg_indicator));
+
+	/* send event to ARM to notify error info coming */
+	e_payload.error_info = audio->bitstream_error_info;
+	audaac_post_event(audio, AUDIO_EVENT_BITSTREAM_ERROR_INFO, e_payload);
+}
+
+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:
+		if ((msg[1] & AUDPLAY_STREAM_INFO_MSG_MASK) ==
+				AUDPLAY_STREAM_INFO_MSG_MASK) {
+			audaac_bitstream_error_info(audio, msg);
+		} else {
+			audaac_update_stream_info(audio, msg);
+		}
+		break;
+
+	case AUDPLAY_UP_OUTPORT_FLUSH_ACK:
+		MM_DBG("OUTPORT_FLUSH_ACK\n");
+		audio->rflush = 0;
+		wake_up(&audio->read_wait);
+		if (audio->pcm_feedback)
+			audplay_buffer_refresh(audio);
+		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");
+				/* send  mixer command */
+				audpp_route_stream(audio->dec_id,
+						audio->source);
+				if (audio->pcm_feedback) {
+					audplay_error_threshold_config(audio);
+					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,
+					POPP);
+			audpp_dsp_set_eq(audio->dec_id, audio->eq_enable,
+					&audio->eq, POPP);
+		} else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+			MM_DBG("CFG_MSG DISABLE\n");
+			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;
+
+	case AUDPP_MSG_AVSYNC_MSG:
+		MM_DBG("AUDPP_MSG_AVSYNC_MSG\n");
+		memcpy(&audio->avsync[0], msg, sizeof(audio->avsync));
+		audio->avsync_flag = 1;
+		wake_up(&audio->avsync_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)
+{
+	struct audpp_cmd_cfg_dec_type cfg_dec_cmd;
+
+	memset(&cfg_dec_cmd, 0, sizeof(cfg_dec_cmd));
+
+	cfg_dec_cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+	if (enable)
+		cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+				AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_AAC;
+	else
+		cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+				AUDPP_CMD_DIS_DEC_V;
+	cfg_dec_cmd.dm_mode = 0x0;
+	cfg_dec_cmd.stream_id = audio->dec_id;
+
+	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_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;
+	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_outport_flush(struct audio *audio)
+{
+	struct audplay_cmd_outport_flush op_flush_cmd;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	op_flush_cmd.cmd_id = AUDPLAY_CMD_OUTPORT_FLUSH;
+	(void)audplay_send_queue0(audio, &op_flush_cmd, sizeof(op_flush_cmd));
+}
+
+static void audplay_error_threshold_config(struct audio *audio)
+{
+	union audplay_cmd_channel_info ch_cfg_cmd;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	ch_cfg_cmd.thr_update.cmd_id = AUDPLAY_CMD_CHANNEL_INFO;
+	ch_cfg_cmd.thr_update.threshold_update = AUDPLAY_ERROR_THRESHOLD_ENABLE;
+	ch_cfg_cmd.thr_update.threshold_value =
+		audio->bitstream_error_threshold_value;
+	(void)audplay_send_queue0(audio, &ch_cfg_cmd, sizeof(ch_cfg_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);
+		}
+	}
+
+	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);
+	audio->avsync_flag = 1;
+	wake_up(&audio->avsync_wait);
+
+}
+
+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, POPP);
+		audio->eq_needs_commit = 0;
+	}
+	return 0;
+}
+
+static int audio_get_avsync_data(struct audio *audio,
+						struct msm_audio_stats *stats)
+{
+	int rc = -EINVAL;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	if (audio->dec_id == audio->avsync[0] && audio->avsync_flag) {
+		/* av_sync sample count */
+		stats->sample_count = (audio->avsync[2] << 16) |
+							(audio->avsync[3]);
+
+		/* av_sync byte_count */
+		stats->byte_count = (audio->avsync[5] << 16) |
+							(audio->avsync[6]);
+
+		audio->avsync_flag = 0;
+		rc = 0;
+	}
+	local_irq_restore(flags);
+	return rc;
+
+}
+
+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;
+
+		audio->avsync_flag = 0;
+		memset(&stats, 0, sizeof(stats));
+		if (audpp_query_avsync(audio->dec_id) < 0)
+			return rc;
+
+		rc = wait_event_interruptible_timeout(audio->avsync_wait,
+				(audio->avsync_flag == 1),
+				msecs_to_jiffies(AUDPP_AVSYNC_EVENT_TIMEOUT));
+
+		if (rc < 0)
+			return rc;
+		else if ((rc > 0) || ((rc == 0) && (audio->avsync_flag == 1))) {
+			if (audio_get_avsync_data(audio, &stats) < 0)
+				return rc;
+
+			if (copy_to_user((void *)arg, &stats, sizeof(stats)))
+				return -EFAULT;
+			return 0;
+		} else
+			return -EAGAIN;
+	}
+
+	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,
+					POPP);
+		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,
+					POPP);
+		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_OUTPORT_FLUSH:
+		MM_DBG("AUDIO_OUTPORT_FLUSH\n");
+		audio->rflush = 1;
+		wake_up(&audio->read_wait);
+		mutex_lock(&audio->read_lock);
+		audio_flush_pcm_buf(audio);
+		mutex_unlock(&audio->read_lock);
+		audplay_outport_flush(audio);
+		rc = wait_event_interruptible(audio->read_wait,
+				!audio->rflush);
+		if (rc < 0) {
+			MM_ERR("AUDPLAY_OUTPORT_FLUSH interrupted\n");
+			rc = -EINTR;
+		}
+		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;
+	}
+	case AUDIO_GET_BITSTREAM_ERROR_INFO:{
+		if ((audio->bitstream_error_info.err_msg_indicator &
+				AUDPLAY_STREAM_INFO_MSG_MASK) ==
+				AUDPLAY_STREAM_INFO_MSG_MASK) {
+			/* haven't received bitstream error info event,
+			the bitstream error info is not updated */
+			rc = -EPERM;
+			break;
+		}
+		if (copy_to_user((void *)arg, &audio->bitstream_error_info,
+			sizeof(struct msm_audio_bitstream_error_info)))
+			rc = -EFAULT;
+		else
+			rc = 0;
+		break;
+	}
+	case AUDIO_GET_SESSION_ID:
+		if (copy_to_user((void *) arg, &audio->dec_id,
+				sizeof(unsigned short)))
+			rc =  -EFAULT;
+		else
+			rc = 0;
+		break;
+	case AUDIO_SET_ERR_THRESHOLD_VALUE:
+		if (copy_from_user(&audio->bitstream_error_threshold_value,
+					(void *)arg, sizeof(uint32_t)))
+			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_timeout(audio->read_wait,
+					      (audio->in[audio->read_next].
+						used > 0) || (audio->stopped)
+						|| (audio->rflush),
+			msecs_to_jiffies(MSM_AUD_BUFFER_UPDATE_WAIT_MS));
+
+		if (rc == 0) {
+			rc = -ETIMEDOUT;
+			break;
+		} else 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);
+			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;
+
+	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;
+
+	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 = 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);
+	auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id);
+	audio_disable(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);
+			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);
+
+	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);
+		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);
+	init_waitqueue_head(&audio->avsync_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->bitstream_error_threshold_value =
+		BITSTREAM_ERROR_THRESHOLD_VALUE;
+
+	audio_flush(audio);
+
+	file->private_data = audio;
+	audio->opened = 1;
+
+	audio->device_events = AUDDEV_EVT_DEV_RDY
+				|AUDDEV_EVT_DEV_RLS|
+				AUDDEV_EVT_STREAM_VOL_CHG;
+
+	rc = auddev_register_evt_listner(audio->device_events,
+					AUDDEV_CLNT_DEC,
+					audio->dec_id,
+					aac_listner,
+					(void *)audio);
+	if (rc) {
+		MM_ERR("%s: failed to register listner\n", __func__);
+		goto event_err;
+	}
+
+#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));
+	memset(&audio->bitstream_error_info, 0,
+			sizeof(struct msm_audio_bitstream_info));
+done:
+	return rc;
+event_err:
+	msm_adsp_put(audio->audplay);
+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/qdsp5v2/audio_aac_in.c b/arch/arm/mach-msm/qdsp5v2/audio_aac_in.c
new file mode 100644
index 0000000..610dbae
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_aac_in.c
@@ -0,0 +1,1465 @@
+/*
+ * aac audio input 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/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/msm_audio_aac.h>
+#include <linux/android_pmem.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+
+#include <mach/msm_adsp.h>
+#include <mach/qdsp5v2/qdsp5audreccmdi.h>
+#include <mach/qdsp5v2/qdsp5audrecmsg.h>
+#include <mach/qdsp5v2/audpreproc.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/debug_mm.h>
+
+/* FRAME_NUM must be a power of two */
+#define FRAME_NUM		(8)
+#define FRAME_SIZE		(772 * 2) /* 1536 bytes data */
+#define NT_FRAME_SIZE	(780 * 2) /* 1536 bytes data  + 24 meta field*/
+#define AAC_FRAME_SIZE	1536
+#define DMASZ 			(FRAME_SIZE * FRAME_NUM)
+#define OUT_FRAME_NUM	(2)
+#define META_OUT_SIZE	(24)
+#define META_IN_SIZE	(14)
+#define OUT_BUFFER_SIZE (32 * 1024 + META_OUT_SIZE)
+#define BUFFER_SIZE		(OUT_BUFFER_SIZE * OUT_FRAME_NUM)
+
+#define AUDPREPROC_AAC_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */
+#define AUDPREPROC_AAC_EOS_FLG_MASK 0x01
+#define AUDPREPROC_AAC_EOS_NONE 0x0 /* No EOS detected */
+#define AUDPREPROC_AAC_EOS_SET 0x1 /* EOS set in meta field */
+
+#define PCM_CONFIG_UPDATE_FLAG_ENABLE -1
+#define PCM_CONFIG_UPDATE_FLAG_DISABLE	0
+
+#define ENABLE_FLAG_VALUE	-1
+#define DISABLE_FLAG_VALUE	0
+
+struct buffer {
+	void *data;
+	uint32_t size;
+	uint32_t read;
+	uint32_t addr;
+	uint32_t used;
+	uint32_t mfield_sz;
+};
+
+struct audio_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;
+
+	/* configuration to use on next enable */
+	uint32_t buffer_size; /* Frame size (36 bytes) */
+	uint32_t samp_rate;
+	uint32_t channel_mode;
+	uint32_t bit_rate; /* bit rate for AAC */
+	uint32_t record_quality; /* record quality (bits/sample/channel) */
+	uint32_t enc_type;
+
+	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 mode;
+	uint32_t eos_ack;
+	uint32_t flush_ack;
+
+	const char *module_name;
+	unsigned queue_ids;
+	uint16_t enc_id;
+
+	struct audrec_session_info session_info; /*audrec session info*/
+	uint16_t source; /* Encoding source bit mask */
+	uint32_t device_events; /* device events interested in */
+	uint32_t dev_cnt;
+	spinlock_t dev_lock;
+
+	/* 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 */
+	int abort; /* set when error, like sample rate mismatch */
+};
+
+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[]; /* samples */
+} __attribute__((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 */
+} __attribute__((packed));
+
+struct aac_encoded_meta_in {
+	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 audrec_send_bitstreamqueue(audio, cmd, len) \
+	msm_adsp_write(audio->audrec, ((audio->queue_ids & 0xFFFF0000) >> 16),\
+			cmd, len)
+
+#define audrec_send_audrecqueue(audio, cmd, len) \
+	msm_adsp_write(audio->audrec, (audio->queue_ids & 0x0000FFFF),\
+			cmd, len)
+
+/* DSP command send functions */
+static int audaac_in_enc_config(struct audio_in *audio, int enable);
+static int audaac_in_param_config(struct audio_in *audio);
+static int audaac_in_mem_config(struct audio_in *audio);
+static int audaac_in_record_config(struct audio_in *audio, int enable);
+static int audaac_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt);
+
+static void audaac_in_get_dsp_frames(struct audio_in *audio);
+static int audpcm_config(struct audio_in *audio);
+static void audaac_out_flush(struct audio_in *audio);
+static int audpreproc_cmd_cfg_routing_mode(struct audio_in *audio);
+static void audpreproc_pcm_send_data(struct audio_in *audio, unsigned needed);
+static void audaac_nt_in_get_dsp_frames(struct audio_in *audio);
+
+static void audaac_in_flush(struct audio_in *audio);
+
+static void aac_in_listener(u32 evt_id, union auddev_evt_data *evt_payload,
+				void *private_data)
+{
+	struct audio_in *audio = (struct audio_in *) private_data;
+	unsigned long flags;
+
+	MM_DBG("evt_id = 0x%8x\n", evt_id);
+	switch (evt_id) {
+	case AUDDEV_EVT_DEV_RDY: {
+		MM_DBG("AUDDEV_EVT_DEV_RDY\n");
+		spin_lock_irqsave(&audio->dev_lock, flags);
+		audio->dev_cnt++;
+		audio->source |= (0x1 << evt_payload->routing_id);
+		spin_unlock_irqrestore(&audio->dev_lock, flags);
+
+		if ((audio->running == 1) && (audio->enabled == 1) &&
+			(audio->mode == MSM_AUD_ENC_MODE_TUNNEL))
+			audaac_in_record_config(audio, 1);
+
+		break;
+	}
+	case AUDDEV_EVT_DEV_RLS: {
+		MM_DBG("AUDDEV_EVT_DEV_RLS\n");
+		spin_lock_irqsave(&audio->dev_lock, flags);
+		audio->dev_cnt--;
+		audio->source &= ~(0x1 << evt_payload->routing_id);
+		spin_unlock_irqrestore(&audio->dev_lock, flags);
+
+		if ((!audio->running) || (!audio->enabled))
+			break;
+
+		if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+			/* Turn of as per source */
+			if (audio->source)
+				audaac_in_record_config(audio, 1);
+			else
+			/* Turn off all */
+				audaac_in_record_config(audio, 0);
+		}
+		break;
+	}
+	case AUDDEV_EVT_FREQ_CHG: {
+		MM_DBG("Encoder Driver got sample rate change event\n");
+		MM_DBG("sample rate %d\n", evt_payload->freq_info.sample_rate);
+		MM_DBG("dev_type %d\n", evt_payload->freq_info.dev_type);
+		MM_DBG("acdb_dev_id %d\n", evt_payload->freq_info.acdb_dev_id);
+		if ((audio->running == 1) && (audio->enabled == 1)) {
+			/* Stop Recording sample rate does not match
+			   with device sample rate */
+			if (evt_payload->freq_info.sample_rate !=
+				audio->samp_rate) {
+				if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)
+					audaac_in_record_config(audio, 0);
+				audio->abort = 1;
+				wake_up(&audio->wait);
+			}
+		}
+		break;
+	}
+	default:
+		MM_ERR("wrong event %d\n", evt_id);
+		break;
+	}
+}
+
+/* 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;
+}
+
+/* ------------------- dsp preproc event handler--------------------- */
+static void audpreproc_dsp_event(void *data, unsigned id,  void *msg)
+{
+	struct audio_in *audio = data;
+
+	switch (id) {
+	case AUDPREPROC_ERROR_MSG: {
+		struct audpreproc_err_msg *err_msg = msg;
+
+		MM_ERR("ERROR_MSG: stream id %d err idx %d\n",
+		err_msg->stream_id, err_msg->aud_preproc_err_idx);
+		/* Error case */
+		wake_up(&audio->wait_enable);
+		break;
+	}
+	case AUDPREPROC_CMD_CFG_DONE_MSG: {
+		MM_DBG("CMD_CFG_DONE_MSG \n");
+		break;
+	}
+	case AUDPREPROC_CMD_ENC_CFG_DONE_MSG: {
+		struct audpreproc_cmd_enc_cfg_done_msg *enc_cfg_msg = msg;
+
+		MM_DBG("CMD_ENC_CFG_DONE_MSG: stream id %d enc type \
+			0x%8x\n", enc_cfg_msg->stream_id,
+			enc_cfg_msg->rec_enc_type);
+		/* Encoder enable success */
+		if (enc_cfg_msg->rec_enc_type & ENCODE_ENABLE) {
+			if(audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) {
+				MM_DBG("routing command\n");
+				audpreproc_cmd_cfg_routing_mode(audio);
+			} else {
+				audaac_in_param_config(audio);
+			}
+		} else { /* Encoder disable success */
+			audio->running = 0;
+			if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)
+				audaac_in_record_config(audio, 0);
+			else
+				wake_up(&audio->wait_enable);
+		}
+		break;
+	}
+	case AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG: {
+		MM_DBG("CMD_ENC_PARAM_CFG_DONE_MSG\n");
+		if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)
+			audaac_in_mem_config(audio);
+		else
+			audpcm_config(audio);
+		break;
+	}
+	case AUDPREPROC_CMD_ROUTING_MODE_DONE_MSG: {
+		struct audpreproc_cmd_routing_mode_done\
+				*routing_cfg_done_msg = msg;
+		if (routing_cfg_done_msg->configuration == 0) {
+			MM_INFO("routing configuration failed\n");
+			audio->running = 0;
+		} else
+			audaac_in_param_config(audio);
+		break;
+	}
+	case AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG: {
+		MM_DBG("AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG\n");
+		wake_up(&audio->wait_enable);
+		break;
+	}
+	default:
+		MM_ERR("Unknown Event id %d\n", id);
+	}
+}
+
+/* ------------------- dsp audrec event handler--------------------- */
+static void audrec_dsp_event(void *data, unsigned id, size_t len,
+			    void (*getevent)(void *ptr, size_t len))
+{
+	struct audio_in *audio = data;
+
+	switch (id) {
+	case AUDREC_CMD_MEM_CFG_DONE_MSG: {
+		MM_DBG("CMD_MEM_CFG_DONE MSG DONE\n");
+		audio->running = 1;
+		if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+			if (audio->dev_cnt > 0)
+				audaac_in_record_config(audio, 1);
+		} else {
+			audpreproc_pcm_send_data(audio, 1);
+			wake_up(&audio->wait_enable);
+		}
+		break;
+	}
+	case AUDREC_FATAL_ERR_MSG: {
+		struct audrec_fatal_err_msg fatal_err_msg;
+
+		getevent(&fatal_err_msg, AUDREC_FATAL_ERR_MSG_LEN);
+		MM_ERR("FATAL_ERR_MSG: err id %d\n",
+				fatal_err_msg.audrec_err_id);
+		/* Error stop the encoder */
+		audio->stopped = 1;
+		wake_up(&audio->wait);
+		if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL)
+			wake_up(&audio->write_wait);
+		break;
+	}
+	case AUDREC_UP_PACKET_READY_MSG: {
+		struct audrec_up_pkt_ready_msg pkt_ready_msg;
+
+		getevent(&pkt_ready_msg, AUDREC_UP_PACKET_READY_MSG_LEN);
+		MM_DBG("UP_PACKET_READY_MSG: write cnt lsw  %d \
+		write cnt msw %d read cnt lsw %d  read cnt msw %d \n",\
+		pkt_ready_msg.audrec_packet_write_cnt_lsw, \
+		pkt_ready_msg.audrec_packet_write_cnt_msw, \
+		pkt_ready_msg.audrec_up_prev_read_cnt_lsw, \
+		pkt_ready_msg.audrec_up_prev_read_cnt_msw);
+
+		audaac_in_get_dsp_frames(audio);
+		break;
+	}
+	case AUDREC_CMD_PCM_BUFFER_PTR_UPDATE_ARM_TO_ENC_MSG: {
+		MM_DBG("ptr_update recieved from DSP\n");
+		audpreproc_pcm_send_data(audio, 1);
+		break;
+	}
+	case AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG: {
+		MM_ERR("AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG");
+		audaac_in_mem_config(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);
+
+		audaac_nt_in_get_dsp_frames(audio);
+		break;
+	}
+	case AUDREC_CMD_EOS_ACK_MSG: {
+		MM_DBG("eos ack recieved\n");
+		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 audrectask\n");
+		break;
+	}
+	default:
+		MM_ERR("Unknown Event id %d\n", id);
+	}
+}
+
+static void audaac_in_get_dsp_frames(struct audio_in *audio)
+{
+	struct audio_frame *frame;
+	uint32_t index;
+	unsigned long flags;
+
+	MM_DBG("head = %d\n", audio->in_head);
+	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);
+	} else
+		audio->in_count++;
+
+	audaac_dsp_read_buffer(audio, audio->dsp_cnt++);
+	spin_unlock_irqrestore(&audio->dsp_lock, flags);
+
+	wake_up(&audio->wait);
+}
+
+static void audaac_nt_in_get_dsp_frames(struct audio_in *audio)
+{
+	struct audio_frame_nt *nt_frame;
+	uint32_t index;
+	unsigned long flags;
+	MM_DBG("head = %d\n", audio->in_head);
+	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);
+}
+
+
+struct msm_adsp_ops audrec_aac_adsp_ops = {
+	.event = audrec_dsp_event,
+};
+
+static int audpreproc_pcm_buffer_ptr_refresh(struct audio_in *audio,
+				       unsigned idx, unsigned len)
+{
+	struct audrec_cmd_pcm_buffer_ptr_refresh_arm_enc cmd;
+
+	if (len ==  META_OUT_SIZE)
+		len = len / 2;
+	else
+		len = (len + META_OUT_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 audrec_send_audrecqueue(audio, (void *)&cmd,
+					(unsigned int)sizeof(cmd));
+}
+
+
+static int audpcm_config(struct audio_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 = PCM_CONFIG_UPDATE_FLAG_ENABLE;
+	cmd.enable_flag = ENABLE_FLAG_VALUE;
+	cmd.sampling_freq = 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 audrec_send_audrecqueue(audio, (void *)&cmd,
+					(unsigned int)sizeof(cmd));
+}
+
+
+static int audpreproc_cmd_cfg_routing_mode(struct audio_in *audio)
+{
+	struct audpreproc_audrec_cmd_routing_mode cmd;
+
+	MM_DBG("\n");
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ROUTING_MODE;
+	cmd.stream_id = audio->enc_id;
+	if (audio->mode == MSM_ADSP_ENC_MODE_NON_TUNNEL)
+		cmd.routing_mode = 1;
+	return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+
+
+static int audaac_in_enc_config(struct audio_in *audio, int enable)
+{
+	struct audpreproc_audrec_cmd_enc_cfg cmd;
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ENC_CFG_2;
+	cmd.stream_id = audio->enc_id;
+
+	if (enable)
+		cmd.audrec_enc_type = audio->enc_type | ENCODE_ENABLE;
+	else
+		cmd.audrec_enc_type &= ~(ENCODE_ENABLE);
+
+	return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+static int audaac_in_param_config(struct audio_in *audio)
+{
+	struct audpreproc_audrec_cmd_parm_cfg_aac cmd;
+
+	MM_DBG("\n");
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.common.cmd_id = AUDPREPROC_AUDREC_CMD_PARAM_CFG;
+	cmd.common.stream_id = audio->enc_id;
+
+	cmd.aud_rec_samplerate_idx = audio->samp_rate;
+	cmd.aud_rec_stereo_mode = audio->channel_mode;
+	cmd.recording_quality = audio->record_quality;
+
+	return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+/* To Do: msm_snddev_route_enc(audio->enc_id); */
+static int audaac_in_record_config(struct audio_in *audio, int enable)
+{
+	struct audpreproc_afe_cmd_audio_record_cfg cmd;
+	MM_DBG("\n");
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG;
+	cmd.stream_id = audio->enc_id;
+	if (enable)
+		cmd.destination_activity = AUDIO_RECORDING_TURN_ON;
+	else
+		cmd.destination_activity = AUDIO_RECORDING_TURN_OFF;
+
+	cmd.source_mix_mask = audio->source;
+	if (audio->enc_id == 2) {
+		if ((cmd.source_mix_mask & INTERNAL_CODEC_TX_SOURCE_MIX_MASK) ||
+			(cmd.source_mix_mask & AUX_CODEC_TX_SOURCE_MIX_MASK) ||
+			(cmd.source_mix_mask & VOICE_UL_SOURCE_MIX_MASK) ||
+			(cmd.source_mix_mask & VOICE_DL_SOURCE_MIX_MASK)) {
+			cmd.pipe_id = SOURCE_PIPE_1;
+		}
+		if (cmd.source_mix_mask &
+				AUDPP_A2DP_PIPE_SOURCE_MIX_MASK)
+			cmd.pipe_id |= SOURCE_PIPE_0;
+	}
+	return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+static int audaac_in_mem_config(struct audio_in *audio)
+{
+	struct audrec_cmd_arecmem_cfg cmd;
+	uint16_t *data = (void *) audio->data;
+	int n;
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDREC_CMD_MEM_CFG_CMD;
+	cmd.audrec_up_pkt_intm_count = 1;
+	cmd.audrec_ext_pkt_start_addr_msw = audio->phys >> 16;
+	cmd.audrec_ext_pkt_start_addr_lsw = audio->phys;
+	cmd.audrec_ext_pkt_buf_number = FRAME_NUM;
+	MM_DBG("audio->phys = %x\n", audio->phys);
+	/* prepare buffer pointers:
+	 * 1536 bytes aac packet + 4 halfword header
+	 */
+	for (n = 0; n < FRAME_NUM; n++) {
+		if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+			audio->in[n].data = data + 4;
+			data += (FRAME_SIZE/2);
+			MM_DBG("0x%8x\n", (int)(audio->in[n].data - 8));
+		} else  {
+			audio->in[n].data = data + 12;
+			data += ((AAC_FRAME_SIZE) / 2) + 12;
+			MM_DBG("0x%8x\n", (int)(audio->in[n].data - 24));
+		}
+	}
+	return audrec_send_audrecqueue(audio, &cmd, sizeof(cmd));
+}
+
+static int audaac_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt)
+{
+	struct up_audrec_packet_ext_ptr cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = UP_AUDREC_PACKET_EXT_PTR;
+	cmd.audrec_up_curr_read_count_msw = read_cnt >> 16;
+	cmd.audrec_up_curr_read_count_lsw = read_cnt;
+
+	return audrec_send_bitstreamqueue(audio, &cmd, sizeof(cmd));
+}
+static int audaac_flush_command(struct audio_in *audio)
+{
+	struct audrec_cmd_flush cmd;
+	MM_DBG("\n");
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDREC_CMD_FLUSH;
+	return audrec_send_audrecqueue(audio, &cmd, sizeof(cmd));
+}
+
+/* must be called with audio->lock held */
+static int audaac_in_enable(struct audio_in *audio)
+{
+	if (audio->enabled)
+		return 0;
+
+	if (audpreproc_enable(audio->enc_id, &audpreproc_dsp_event, audio)) {
+		MM_ERR("msm_adsp_enable(audpreproc) failed\n");
+		return -ENODEV;
+	}
+
+	if (msm_adsp_enable(audio->audrec)) {
+		MM_ERR("msm_adsp_enable(audrec) failed\n");
+		audpreproc_disable(audio->enc_id, audio);
+		return -ENODEV;
+	}
+	audio->enabled = 1;
+	audaac_in_enc_config(audio, 1);
+
+	return 0;
+}
+
+/* must be called with audio->lock held */
+static int audaac_in_disable(struct audio_in *audio)
+{
+	if (audio->enabled) {
+		audio->enabled = 0;
+		audaac_in_enc_config(audio, 0);
+		wake_up(&audio->wait);
+		wait_event_interruptible_timeout(audio->wait_enable,
+				audio->running == 0, 1*HZ);
+		msm_adsp_disable(audio->audrec);
+		audpreproc_disable(audio->enc_id, audio);
+	}
+	return 0;
+}
+
+static void audaac_ioport_reset(struct audio_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);
+	audaac_in_flush(audio);
+	mutex_unlock(&audio->write_lock);
+	wake_up(&audio->wait);
+	mutex_lock(&audio->read_lock);
+	audaac_out_flush(audio);
+	mutex_unlock(&audio->read_lock);
+}
+
+static void audaac_in_flush(struct audio_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 = 0; i < FRAME_NUM; 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 audaac_out_flush(struct audio_in *audio)
+{
+	int i;
+
+	audio->out_head = 0;
+	audio->out_tail = 0;
+	audio->out_count = 0;
+	for (i = 0; i < OUT_FRAME_NUM; i++) {
+		audio->out[i].size = 0;
+		audio->out[i].read = 0;
+		audio->out[i].used = 0;
+	}
+}
+
+/* ------------------- device --------------------- */
+static long audaac_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);
+		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: {
+		uint32_t freq;
+		/* Poll at 48KHz always */
+		freq = 48000;
+		MM_DBG("AUDIO_START\n");
+		rc = msm_snddev_request_freq(&freq, audio->enc_id,
+					SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+		MM_DBG("sample rate configured %d sample rate requested %d\n",
+				freq, audio->samp_rate);
+		if (rc < 0) {
+			MM_DBG(" Sample rate can not be set, return code %d\n",
+								 rc);
+			msm_snddev_withdraw_freq(audio->enc_id,
+					SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+			MM_DBG("msm_snddev_withdraw_freq\n");
+			break;
+		}
+		/*update aurec session info in audpreproc layer*/
+		audio->session_info.session_id = audio->enc_id;
+		audio->session_info.sampling_freq = audio->samp_rate;
+		audpreproc_update_audrec_info(&audio->session_info);
+		rc = audaac_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: {
+		audio->session_info.sampling_freq = 0;
+		audpreproc_update_audrec_info(&audio->session_info);
+		rc = audaac_in_disable(audio);
+		rc = msm_snddev_withdraw_freq(audio->enc_id,
+					SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+		MM_DBG("msm_snddev_withdraw_freq\n");
+		audio->stopped = 1;
+		audio->abort = 0;
+		break;
+	}
+	case AUDIO_FLUSH:
+		MM_DBG("AUDIO_FLUSH\n");
+		audio->rflush = 1;
+		audio->wflush = 1;
+		audaac_ioport_reset(audio);
+		if (audio->running) {
+			audaac_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_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;
+		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 != (NT_FRAME_SIZE - 24)) {
+				rc = -EINVAL;
+				break;
+			}
+		}
+		audio->buffer_size = cfg.buffer_size;
+		break;
+	}
+	case AUDIO_GET_CONFIG: {
+		struct msm_audio_pcm_config cfg;
+		memset(&cfg, 0, sizeof(cfg));
+		cfg.buffer_size = OUT_BUFFER_SIZE;
+		cfg.buffer_count = OUT_FRAME_NUM;
+		if (copy_to_user((void *)arg, &cfg, sizeof(cfg)))
+			rc = -EFAULT;
+		break;
+	}
+	case AUDIO_GET_AAC_ENC_CONFIG: {
+		struct msm_audio_aac_enc_config cfg;
+		if (audio->channel_mode == AUDREC_CMD_MODE_MONO)
+			cfg.channels = 1;
+		else
+			cfg.channels = 2;
+		cfg.sample_rate = 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;
+		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;
+		}
+		MM_DBG("channels = %d\n", cfg.channels);
+		if (cfg.channels == 1) {
+			cfg.channels = AUDREC_CMD_MODE_MONO;
+		} else if (cfg.channels == 2) {
+			cfg.channels = AUDREC_CMD_MODE_STEREO;
+		} else {
+			rc = -EINVAL;
+			break;
+		}
+		MM_DBG("channels = %d\n", cfg.channels);
+		audio->samp_rate = 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);
+		break;
+	}
+	case AUDIO_GET_SESSION_ID: {
+		if (copy_to_user((void *) arg, &audio->enc_id,
+			sizeof(unsigned short))) {
+			rc = -EFAULT;
+		}
+		break;
+	}
+	default:
+		rc = -EINVAL;
+	}
+	mutex_unlock(&audio->lock);
+	return rc;
+}
+
+static ssize_t audaac_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;
+	struct aac_encoded_meta_in 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->abort || 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;
+		}
+
+		if (audio->abort) {
+			rc = -EPERM; /* Not permitted due to abort */
+			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, 12);
+			meta_field.metadata_len =
+					sizeof(struct aac_encoded_meta_in);
+			if (copy_to_user((char *)start, (char *)&meta_field,
+					sizeof(struct aac_encoded_meta_in))) {
+				rc = -EFAULT;
+				break;
+			}
+			if (nt_frame->nflag_lsw & 0x0001) {
+				MM_ERR("recieved EOS in read call\n");
+				audio->eos_ack = 1;
+			}
+			buf += sizeof(struct aac_encoded_meta_in);
+			count -= sizeof(struct aac_encoded_meta_in);
+		}
+		if (count >= size) {
+			if (copy_to_user(buf, data, size)) {
+				rc = -EFAULT;
+				break;
+			}
+			spin_lock_irqsave(&audio->dsp_lock, flags);
+			if (index != audio->in_tail) {
+				/* overrun -- data is
+				 * invalid and we need to retry */
+				spin_unlock_irqrestore(&audio->dsp_lock, flags);
+				continue;
+			}
+			audio->in[index].size = 0;
+			audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1);
+			audio->in_count--;
+			spin_unlock_irqrestore(&audio->dsp_lock, flags);
+			count -= size;
+			buf += size;
+			if ((audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) &&
+						(!audio->eos_ack)) {
+				MM_DBG("sending read ptr command %d %d\n",
+							audio->dsp_cnt,
+							audio->in_tail);
+				audaac_dsp_read_buffer(audio,
+							audio->dsp_cnt++);
+				break;
+			}
+		} else {
+			MM_ERR("short read\n");
+			break;
+		}
+		break;
+	}
+	mutex_unlock(&audio->read_lock);
+	if (buf > start)
+		return buf - start;
+
+	return rc;
+}
+
+static void audpreproc_pcm_send_data(struct audio_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);
+			MM_DBG("frame %d busy\n", audio->out_tail);
+			audpreproc_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 audaac_in_fsync(struct file *file,	int datasync)
+
+{
+	struct audio_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 audpreproc_aac_process_eos(struct audio_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);
+	audpreproc_pcm_send_data(audio, 0);
+done:
+	return rc;
+}
+
+static ssize_t audaac_in_write(struct file *file,
+				const char __user *buf,
+				size_t count, loff_t *pos)
+{
+	struct audio_in *audio = file->private_data;
+	const char __user *start = buf;
+	struct buffer *frame;
+	char *cpy_ptr;
+	int rc = 0, eos_condition = AUDPREPROC_AAC_EOS_NONE;
+	unsigned short mfield_size = 0;
+	int write_count = count;
+	MM_DBG("cnt=%d\n", count);
+
+	if (count & 1)
+		return -EINVAL;
+
+	mutex_lock(&audio->write_lock);
+	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)
+		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_AAC_EOS_FLG_OFFSET] &
+					AUDPREPROC_AAC_EOS_FLG_MASK) {
+				MM_DBG("EOS SET\n");
+				eos_condition = AUDPREPROC_AAC_EOS_SET;
+				if (mfield_size == count) {
+					buf += mfield_size;
+					if (audio->mode ==
+						MSM_AUD_ENC_MODE_NONTUNNEL) {
+						eos_condition = 0;
+						goto exit;
+					}
+					goto error;
+				} else
+				cpy_ptr[AUDPREPROC_AAC_EOS_FLG_OFFSET] &=
+					~AUDPREPROC_AAC_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)
+		audpreproc_pcm_send_data(audio, 0);
+	else {
+		audpreproc_pcm_send_data(audio, 1);
+		audio->flush_ack = 0;
+	}
+	if (eos_condition == AUDPREPROC_AAC_EOS_SET)
+		rc = audpreproc_aac_process_eos(audio, start, mfield_size);
+	mutex_unlock(&audio->write_lock);
+	return write_count;
+error:
+	mutex_unlock(&audio->write_lock);
+	return rc;
+}
+
+static int audaac_in_release(struct inode *inode, struct file *file)
+{
+	struct audio_in *audio = file->private_data;
+
+	mutex_lock(&audio->lock);
+	/* with draw frequency for session
+	   incase not stopped the driver */
+	msm_snddev_withdraw_freq(audio->enc_id, SNDDEV_CAP_TX,
+					AUDDEV_CLNT_ENC);
+	auddev_unregister_evt_listner(AUDDEV_CLNT_ENC, audio->enc_id);
+	/*reset the sampling frequency information at audpreproc layer*/
+	audio->session_info.sampling_freq = 0;
+	audpreproc_update_audrec_info(&audio->session_info);
+	audaac_in_disable(audio);
+	audaac_in_flush(audio);
+	msm_adsp_put(audio->audrec);
+	audpreproc_aenc_free(audio->enc_id);
+	audio->audrec = NULL;
+	audio->opened = 0;
+	if (audio->data) {
+		iounmap(audio->data);
+		pmem_kfree(audio->phys);
+		audio->data = NULL;
+	}
+	if (audio->out_data) {
+		iounmap(audio->out_data);
+		pmem_kfree(audio->out_phys);
+		audio->out_data = NULL;
+	}
+	mutex_unlock(&audio->lock);
+	return 0;
+}
+
+struct audio_in the_audio_aac_in;
+
+static int audaac_in_open(struct inode *inode, struct file *file)
+{
+	struct audio_in *audio = &the_audio_aac_in;
+	int rc;
+	int encid;
+
+	mutex_lock(&audio->lock);
+	if (audio->opened) {
+		rc = -EBUSY;
+		goto done;
+	}
+	audio->phys = pmem_kalloc(DMASZ, PMEM_MEMTYPE_EBI1|
+					PMEM_ALIGNMENT_4K);
+	if (!IS_ERR((void *)audio->phys)) {
+		audio->data = ioremap(audio->phys, DMASZ);
+		if (!audio->data) {
+			MM_ERR("could not allocate DMA buffers\n");
+			rc = -ENOMEM;
+			pmem_kfree(audio->phys);
+			goto done;
+		}
+	} else {
+		MM_ERR("could not allocate DMA buffers\n");
+		rc = -ENOMEM;
+		goto done;
+	}
+	MM_DBG("Memory addr = 0x%8x  phy addr = 0x%8x\n",\
+		(int) audio->data, (int) audio->phys);
+	if ((file->f_mode & FMODE_WRITE) &&
+				(file->f_mode & FMODE_READ)) {
+		audio->mode = MSM_AUD_ENC_MODE_NONTUNNEL;
+	} else if (!(file->f_mode & FMODE_WRITE) &&
+					(file->f_mode & FMODE_READ)) {
+		audio->mode = MSM_AUD_ENC_MODE_TUNNEL;
+		MM_DBG("Opened for tunnel mode encoding\n");
+	} else {
+		rc = -EACCES;
+		goto done;
+	}
+
+	/* Settings will be re-config at AUDIO_SET_CONFIG,
+	 * but at least we need to have initial config
+	 */
+	 if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL)
+			audio->buffer_size = (NT_FRAME_SIZE - 24);
+	else
+			audio->buffer_size = (FRAME_SIZE - 8);
+	audio->enc_type = ENC_TYPE_AAC | audio->mode;
+	audio->samp_rate = 8000;
+	audio->channel_mode = AUDREC_CMD_MODE_MONO;
+	/* For AAC, bit rate hard coded, default settings is
+	 * sample rate (8000) x channel count (1) x recording quality (1.75)
+	 * = 14000 bps  */
+	audio->bit_rate = 14000;
+	audio->record_quality = 0x1c00;
+	MM_DBG("enc_type = %x\n", audio->enc_type);
+	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_aac_adsp_ops, audio);
+
+	if (rc) {
+		audpreproc_aenc_free(audio->enc_id);
+		goto done;
+	}
+
+	audio->stopped = 0;
+	audio->source = 0;
+	audio->abort = 0;
+	audio->wflush = 0;
+	audio->rflush = 0;
+	audio->flush_ack = 0;
+
+	audaac_in_flush(audio);
+	audaac_out_flush(audio);
+
+	audio->out_phys = pmem_kalloc(BUFFER_SIZE,
+				PMEM_MEMTYPE_EBI1 | PMEM_ALIGNMENT_4K);
+	if (IS_ERR((void *)audio->out_phys)) {
+		MM_ERR("could not allocate write buffers\n");
+		rc = -ENOMEM;
+		goto evt_error;
+	} else {
+		audio->out_data = ioremap(audio->out_phys, BUFFER_SIZE);
+		if (!audio->out_data) {
+			MM_ERR("could not allocate write buffers\n");
+			rc = -ENOMEM;
+			pmem_kfree(audio->out_phys);
+			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->device_events = AUDDEV_EVT_DEV_RDY | AUDDEV_EVT_DEV_RLS |
+				AUDDEV_EVT_FREQ_CHG;
+
+	rc = auddev_register_evt_listner(audio->device_events,
+					AUDDEV_CLNT_ENC, audio->enc_id,
+					aac_in_listener, (void *) audio);
+	if (rc) {
+		MM_ERR("failed to register device event listener\n");
+		iounmap(audio->out_data);
+		pmem_kfree(audio->out_phys);
+		goto evt_error;
+	}
+	audio->mfield = META_OUT_SIZE;
+	file->private_data = audio;
+	audio->opened = 1;
+	audio->out_frame_cnt++;
+	rc = 0;
+done:
+	mutex_unlock(&audio->lock);
+	return rc;
+evt_error:
+	msm_adsp_put(audio->audrec);
+	audpreproc_aenc_free(audio->enc_id);
+	mutex_unlock(&audio->lock);
+	return rc;
+}
+
+static const struct file_operations audio_in_fops = {
+	.owner		= THIS_MODULE,
+	.open		= audaac_in_open,
+	.release	= audaac_in_release,
+	.read		= audaac_in_read,
+	.write		= audaac_in_write,
+	.fsync		= audaac_in_fsync,
+	.unlocked_ioctl	= audaac_in_ioctl,
+};
+
+struct miscdevice audio_aac_in_misc = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "msm_aac_in",
+	.fops	= &audio_in_fops,
+};
+
+static int __init audaac_in_init(void)
+{
+	mutex_init(&the_audio_aac_in.lock);
+	mutex_init(&the_audio_aac_in.read_lock);
+	spin_lock_init(&the_audio_aac_in.dsp_lock);
+	spin_lock_init(&the_audio_aac_in.dev_lock);
+	init_waitqueue_head(&the_audio_aac_in.wait);
+	init_waitqueue_head(&the_audio_aac_in.wait_enable);
+	mutex_init(&the_audio_aac_in.write_lock);
+	init_waitqueue_head(&the_audio_aac_in.write_wait);
+
+	return misc_register(&audio_aac_in_misc);
+}
+
+device_initcall(audaac_in_init);
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_acdb.c b/arch/arm/mach-msm/qdsp5v2/audio_acdb.c
new file mode 100644
index 0000000..84406cd
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_acdb.c
@@ -0,0 +1,3407 @@
+/* Copyright (c) 2009-2011, 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/miscdevice.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+#include <linux/android_pmem.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/uaccess.h>
+#include <linux/msm_audio.h>
+#include <linux/slab.h>
+#include <mach/dal.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/qdsp5v2/audpp.h>
+#include <mach/qdsp5v2/audpreproc.h>
+#include <mach/qdsp5v2/qdsp5audppcmdi.h>
+#include <mach/qdsp5v2/qdsp5audpreproccmdi.h>
+#include <mach/qdsp5v2/qdsp5audpreprocmsg.h>
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/afe.h>
+#include <mach/qdsp5v2/audio_acdbi.h>
+#include <mach/qdsp5v2/acdb_commands.h>
+#include <mach/qdsp5v2/audio_acdb_def.h>
+#include <linux/mfd/marimba.h>
+#include <mach/debug_mm.h>
+#include <linux/debugfs.h>
+
+/* this is the ACDB device ID */
+#define DALDEVICEID_ACDB		0x02000069
+#define ACDB_PORT_NAME			"DAL00"
+#define ACDB_CPU			SMD_APPS_MODEM
+#define ACDB_BUF_SIZE			4096
+#define PBE_BUF_SIZE                    (33*1024)
+#define FLUENCE_BUF_SIZE	498
+
+#define ACDB_VALUES_NOT_FILLED		0
+#define ACDB_VALUES_FILLED		1
+#define MAX_RETRY			10
+
+/*below macro is used to align the session info received from
+Devctl driver with the state mentioned as not to alter the
+Existing code*/
+#define AUDREC_OFFSET	2
+/* rpc table index */
+enum {
+	ACDB_DalACDB_ioctl = DALDEVICE_FIRST_DEVICE_API_IDX
+};
+
+enum {
+	CAL_DATA_READY	= 0x1,
+	AUDPP_READY	= 0x2,
+	AUDREC0_READY	= 0x4,
+	AUDREC1_READY	= 0x8,
+	AUDREC2_READY	= 0x10,
+};
+
+
+struct acdb_data {
+	void *handle;
+
+	u32 phys_addr;
+	u8 *virt_addr;
+
+	struct task_struct *cb_thread_task;
+	struct auddev_evt_audcal_info *device_info;
+
+	u32 acdb_state;
+	struct audpp_event_callback audpp_cb;
+	struct audpreproc_event_callback audpreproc_cb;
+
+	struct audpp_cmd_cfg_object_params_pcm *pp_iir;
+	struct audpp_cmd_cfg_cal_gain *calib_gain_rx;
+	struct audpp_cmd_cfg_pbe *pbe_block;
+	struct audpp_cmd_cfg_object_params_mbadrc *pp_mbadrc;
+	struct audpreproc_cmd_cfg_agc_params *preproc_agc;
+	struct audpreproc_cmd_cfg_iir_tuning_filter_params *preproc_iir;
+	struct audpreproc_cmd_cfg_cal_gain *calib_gain_tx;
+	struct acdb_mbadrc_block mbadrc_block;
+	struct audpreproc_cmd_cfg_lvnv_param preproc_lvnv;
+
+	wait_queue_head_t wait;
+	struct mutex acdb_mutex;
+	u32 device_cb_compl;
+	u32 audpp_cb_compl;
+	u32 preproc_cb_compl;
+	u8 preproc_stream_id;
+	u8 audrec_applied;
+	u32 multiple_sessions;
+	u32 cur_tx_session;
+	struct acdb_result acdb_result;
+	u16 *pbe_extbuff;
+	u16 *pbe_enable_flag;
+	u32 fluence_extbuff;
+	u8 *fluence_extbuff_virt;
+
+	struct acdb_pbe_block *pbe_blk;
+
+	spinlock_t dsp_lock;
+	int dec_id;
+	struct audpp_cmd_cfg_object_params_eqalizer eq;
+	 /*status to enable or disable the fluence*/
+	int fleuce_feature_status[MAX_AUDREC_SESSIONS];
+	struct audrec_session_info session_info;
+	/*pmem info*/
+	int pmem_fd;
+	unsigned long paddr;
+	unsigned long kvaddr;
+	unsigned long pmem_len;
+	struct file *file;
+	/* pmem for get acdb blk */
+	unsigned long	get_blk_paddr;
+	u8		*get_blk_kvaddr;
+};
+
+static struct acdb_data		acdb_data;
+
+struct acdb_cache_node {
+	u32 node_status;
+	s32 stream_id;
+	u32 phys_addr_acdb_values;
+	u8 *virt_addr_acdb_values;
+	struct auddev_evt_audcal_info device_info;
+};
+
+/*for RX devices  acdb values are applied based on copp ID so
+the depth of tx cache is MAX number of COPP supported in the system*/
+struct acdb_cache_node acdb_cache_rx[MAX_COPP_NODE_SUPPORTED];
+
+/*for TX devices acdb values are applied based on AUDREC session and
+the depth of the tx cache is define by number of AUDREC sessions supported*/
+struct acdb_cache_node acdb_cache_tx[MAX_AUDREC_SESSIONS];
+
+/*Audrec session info includes Attributes Sampling frequency and enc_id */
+struct audrec_session_info session_info[MAX_AUDREC_SESSIONS];
+#ifdef CONFIG_DEBUG_FS
+
+#define RTC_MAX_TIMEOUT 500 /* 500 ms */
+#define PMEM_RTC_ACDB_QUERY_MEM 4096
+#define EXTRACT_HIGH_WORD(x) ((x & 0xFFFF0000)>>16)
+#define EXTRACT_LOW_WORD(x) (0x0000FFFF & x)
+#define	ACDB_RTC_TX 0xF1
+#define	ACDB_RTC_RX 0x1F
+
+
+static u32 acdb_audpp_entry[][4] = {
+
+  { ABID_AUDIO_RTC_VOLUME_PAN_RX,\
+    IID_AUDIO_RTC_VOLUME_PAN_PARAMETERS,\
+    AUDPP_CMD_VOLUME_PAN,\
+    ACDB_RTC_RX
+   },
+  { ABID_AUDIO_IIR_RX,\
+     IID_AUDIO_IIR_COEFF,\
+     AUDPP_CMD_IIR_TUNING_FILTER,
+     ACDB_RTC_RX
+   },
+  { ABID_AUDIO_RTC_EQUALIZER_PARAMETERS,\
+     IID_AUDIO_RTC_EQUALIZER_PARAMETERS,\
+     AUDPP_CMD_EQUALIZER,\
+     ACDB_RTC_RX
+   },
+  { ABID_AUDIO_RTC_SPA,\
+     IID_AUDIO_RTC_SPA_PARAMETERS,\
+     AUDPP_CMD_SPECTROGRAM,
+     ACDB_RTC_RX
+   },
+  { ABID_AUDIO_STF_RX,\
+     IID_AUDIO_IIR_COEFF,\
+     AUDPP_CMD_SIDECHAIN_TUNING_FILTER,\
+     ACDB_RTC_RX
+  },
+  {
+     ABID_AUDIO_MBADRC_RX,\
+     IID_AUDIO_RTC_MBADRC_PARAMETERS,\
+     AUDPP_CMD_MBADRC,\
+     ACDB_RTC_RX
+  },
+  {
+    ABID_AUDIO_AGC_TX,\
+    IID_AUDIO_AGC_PARAMETERS,\
+    AUDPREPROC_CMD_CFG_AGC_PARAMS,\
+    ACDB_RTC_TX
+  },
+  {
+    ABID_AUDIO_AGC_TX,\
+    IID_AUDIO_RTC_AGC_PARAMETERS,\
+    AUDPREPROC_CMD_CFG_AGC_PARAMS,\
+    ACDB_RTC_TX
+  },
+  {
+    ABID_AUDIO_NS_TX,\
+    IID_NS_PARAMETERS,\
+    AUDPREPROC_CMD_CFG_NS_PARAMS,\
+    ACDB_RTC_TX
+  },
+  {
+     ABID_AUDIO_IIR_TX,\
+     IID_AUDIO_RTC_TX_IIR_COEFF,\
+     AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS,\
+     ACDB_RTC_TX
+  },
+  {
+     ABID_AUDIO_IIR_TX,\
+     IID_AUDIO_IIR_COEFF,\
+     AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS,\
+     ACDB_RTC_TX
+  }
+ /*Any new entries should be added here*/
+};
+
+static struct dentry *get_set_abid_dentry;
+static struct dentry *get_set_abid_data_dentry;
+
+struct rtc_acdb_pmem {
+	u8 *viraddr;
+	int32_t phys;
+};
+
+struct rtc_acdb_data {
+	u32 acdb_id;
+	u32 cmd_id;
+	u32 set_abid;
+	u32 set_iid;
+	u32 abid;
+	u32 err;
+	bool valid_abid;
+	u32 tx_rx_ctl;
+	struct rtc_acdb_pmem rtc_read;
+	struct rtc_acdb_pmem rtc_write;
+	wait_queue_head_t  wait;
+};
+
+struct get_abid {
+	u32	cmd_id;
+	u32	acdb_id;
+	u32	set_abid;
+	u32	set_iid;
+};
+
+struct acdb_block_mbadrc_rtc {
+	u16 enable;
+	u16 num_bands;
+	u16 down_samp_level;
+	u16 adrc_delay;
+	u16 ext_buf_size;
+	u16 ext_partition;
+	u16 ext_buf_msw;
+	u16 ext_buf_lsw;
+	struct adrc_config adrc_band[AUDPP_MAX_MBADRC_BANDS];
+	signed int ExtBuff[196];
+} __attribute__((packed));
+
+enum {
+	ACDB_RTC_SUCCESS,
+	ACDB_RTC_ERR_INVALID_DEVICE,
+	ACDB_RTC_ERR_DEVICE_INACTIVE,
+	ACDB_RTC_ERR_INVALID_ABID,
+	ACDB_RTC_DSP_FAILURE,
+	ACDB_RTC_DSP_FEATURE_NOT_AVAILABLE,
+	ACDB_RTC_ERR_INVALID_LEN,
+	ACDB_RTC_ERR_UNKNOWN_FAILURE,
+	ACDB_RTC_PENDING_RESPONSE,
+	ACDB_RTC_INIT_FAILURE,
+};
+
+static  struct rtc_acdb_data rtc_acdb;
+
+static int rtc_getsetabid_dbg_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	MM_INFO("GET-SET ABID Open debug intf %s\n",
+			(char *) file->private_data);
+	return 0;
+}
+
+static bool get_feature_id(u32 set_abid, u32 iid, unsigned short *feature_id)
+{
+	bool ret_value = false;
+	int i = 0;
+
+	for (; i < (sizeof(acdb_audpp_entry) / sizeof(acdb_audpp_entry[0]));\
+		i++) {
+		if (acdb_audpp_entry[i][0] == set_abid &&
+			acdb_audpp_entry[i][1] == iid) {
+			*feature_id =  acdb_audpp_entry[i][2];
+			rtc_acdb.tx_rx_ctl = acdb_audpp_entry[i][3];
+			ret_value = true;
+			break;
+		}
+	}
+	return ret_value;
+}
+static ssize_t rtc_getsetabid_dbg_write(struct file *filp,
+					const char __user *ubuf,
+					size_t cnt, loff_t *ppos)
+{
+	struct  get_abid write_abid;
+	unsigned short feat_id = 0;
+	rtc_acdb.valid_abid = false;
+
+	if (copy_from_user(&write_abid, \
+		(void *)ubuf, sizeof(struct get_abid))) {
+		MM_ERR("ACDB DATA WRITE - INVALID READ LEN\n");
+		rtc_acdb.err = ACDB_RTC_ERR_INVALID_LEN;
+		return cnt;
+	}
+	MM_INFO("SET ABID : Cmd ID: %d Device:%d ABID:%d IID : %d cnt: %d\n",\
+		write_abid.cmd_id, write_abid.acdb_id,
+		write_abid.set_abid, write_abid.set_iid, cnt);
+	if (write_abid.acdb_id > ACDB_ID_MAX ||
+		write_abid.acdb_id < ACDB_ID_HANDSET_SPKR){
+		rtc_acdb.err = ACDB_RTC_ERR_INVALID_DEVICE;
+		return cnt;
+	}
+	if (!is_dev_opened(write_abid.acdb_id))	{
+		rtc_acdb.err = ACDB_RTC_ERR_DEVICE_INACTIVE;
+		return cnt;
+	}
+	rtc_acdb.err = ACDB_RTC_ERR_INVALID_ABID;
+	rtc_acdb.abid = write_abid.set_abid;
+	if (get_feature_id(write_abid.set_abid, \
+		write_abid.set_iid, &feat_id)) {
+		rtc_acdb.err = ACDB_RTC_SUCCESS;
+		rtc_acdb.cmd_id = write_abid.cmd_id;
+		rtc_acdb.acdb_id = write_abid.acdb_id;
+		rtc_acdb.set_abid = feat_id;
+		rtc_acdb.valid_abid = true;
+		rtc_acdb.set_iid = write_abid.set_iid;
+	}
+	return cnt;
+}
+static ssize_t	rtc_getsetabid_dbg_read(struct file *file, char __user *buf,
+					size_t count, loff_t *ppos)
+{
+	static char buffer[1024];
+	int n = 0;
+	u32 msg = rtc_acdb.err;
+	memcpy(buffer, &rtc_acdb.cmd_id, sizeof(struct get_abid));
+	memcpy(buffer+16, &msg, 4);
+	n = 20;
+	MM_INFO("SET ABID : Cmd ID: %x Device:%x ABID:%x IID : %x Err: %d\n",\
+		rtc_acdb.cmd_id, rtc_acdb.acdb_id, rtc_acdb.set_abid,\
+		rtc_acdb.set_iid, rtc_acdb.err);
+	return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+
+static int rtc_getsetabid_data_dbg_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	MM_INFO("GET-SET ABID DATA Open debug intf %s\n",
+		(char *) file->private_data);
+	return 0;
+}
+
+void acdb_rtc_set_err(u32 ErrCode)
+{
+	if (rtc_acdb.err == ACDB_RTC_PENDING_RESPONSE) {
+		if (ErrCode == 0xFFFF) {
+			rtc_acdb.err = ACDB_RTC_SUCCESS;
+			MM_INFO("RTC READ SUCCESS---\n");
+		} else if (ErrCode == 0) {
+			rtc_acdb.err = ACDB_RTC_DSP_FAILURE;
+			MM_INFO("RTC READ FAIL---\n");
+		} else if (ErrCode == 1) {
+			rtc_acdb.err = ACDB_RTC_DSP_FEATURE_NOT_AVAILABLE;
+			MM_INFO("RTC READ FEAT UNAVAILABLE---\n");
+		} else {
+			rtc_acdb.err = ACDB_RTC_DSP_FAILURE;
+			MM_ERR("RTC Err CODE---\n");
+		}
+	} else {
+		rtc_acdb.err = ACDB_RTC_DSP_FAILURE;
+		MM_ERR("RTC Err code Invalid State\n");
+	}
+	wake_up(&rtc_acdb.wait);
+}
+static ssize_t	rtc_getsetabid_data_dbg_read(struct file *file,
+					char __user *buf, size_t count,
+					loff_t *ppos)
+{
+	static char buffer[PMEM_RTC_ACDB_QUERY_MEM];
+	int rc, n = 0;
+	int counter = 0;
+	struct rtc_acdb_pmem *rtc_read = &rtc_acdb.rtc_read;
+	memset(&buffer, 0, PMEM_RTC_ACDB_QUERY_MEM);
+
+	if (rtc_acdb.valid_abid != true) {
+		MM_ERR("ACDB DATA READ ---INVALID ABID\n");
+		n = 0;
+		rtc_acdb.err = ACDB_RTC_ERR_INVALID_ABID;
+	} else {
+		if (PMEM_RTC_ACDB_QUERY_MEM < count) {
+			MM_ERR("ACDB DATA READ ---\
+				INVALID READ LEN %x\n", count);
+			n = 0;
+			rtc_acdb.err = ACDB_RTC_ERR_INVALID_LEN;
+		} else {
+			rtc_acdb.err = ACDB_RTC_PENDING_RESPONSE;
+			if (rtc_read->viraddr != NULL) {
+				memset(rtc_read->viraddr,
+					0, PMEM_RTC_ACDB_QUERY_MEM);
+			}
+			if (rtc_acdb.tx_rx_ctl == ACDB_RTC_RX) {
+				struct rtc_audpp_read_data rtc_read_cmd;
+				rtc_read_cmd.cmd_id =
+					AUDPP_CMD_PP_FEAT_QUERY_PARAMS;
+				rtc_read_cmd.obj_id =
+					AUDPP_CMD_COPP_STREAM;
+				rtc_read_cmd.route_id =
+					acdb_data.device_info->dev_id;
+				rtc_read_cmd.feature_id = rtc_acdb.set_abid;
+				rtc_read_cmd.extbufsizemsw =
+					EXTRACT_HIGH_WORD(\
+						PMEM_RTC_ACDB_QUERY_MEM);
+				rtc_read_cmd.extbufsizelsw =
+					EXTRACT_LOW_WORD(\
+						PMEM_RTC_ACDB_QUERY_MEM);
+				rtc_read_cmd.extpart = 0x0000;
+				rtc_read_cmd.extbufstartmsw =
+					EXTRACT_HIGH_WORD(rtc_read->phys);
+				rtc_read_cmd.extbufstartlsw =
+					EXTRACT_LOW_WORD(rtc_read->phys);
+				rc = audpp_send_queue2(&rtc_read_cmd,
+						sizeof(rtc_read_cmd));
+				MM_INFO("ACDB READ Command RC --->%x\
+					Route ID=%x\n", rc,\
+					acdb_data.device_info->dev_id);
+			} else if (rtc_acdb.tx_rx_ctl == ACDB_RTC_TX) {
+				struct rtc_audpreproc_read_data rtc_audpreproc;
+				rtc_audpreproc.cmd_id =
+					AUDPREPROC_CMD_FEAT_QUERY_PARAMS;
+				rtc_audpreproc.stream_id =
+					acdb_data.preproc_stream_id;
+				rtc_audpreproc.feature_id = rtc_acdb.set_abid;
+				rtc_audpreproc.extbufsizemsw =
+					EXTRACT_HIGH_WORD(\
+						PMEM_RTC_ACDB_QUERY_MEM);
+				rtc_audpreproc.extbufsizelsw =
+					EXTRACT_LOW_WORD(\
+						PMEM_RTC_ACDB_QUERY_MEM);
+				rtc_audpreproc.extpart = 0x0000;
+				rtc_audpreproc.extbufstartmsw =
+					EXTRACT_HIGH_WORD(rtc_read->phys);
+				rtc_audpreproc.extbufstartlsw =
+					EXTRACT_LOW_WORD(rtc_read->phys);
+				rc =  audpreproc_send_preproccmdqueue(
+						&rtc_audpreproc,\
+						sizeof(rtc_audpreproc));
+				MM_INFO("ACDB READ Command RC --->%x,\
+					stream_id %x\n", rc,\
+					acdb_data.preproc_stream_id);
+			}
+		rc = wait_event_timeout(rtc_acdb.wait,
+					(rtc_acdb.err !=
+					ACDB_RTC_PENDING_RESPONSE),
+					msecs_to_jiffies(RTC_MAX_TIMEOUT));
+		MM_INFO("ACDB READ ACK Count = %x Err = %x\n",
+			count, rtc_acdb.err);
+		{
+			if (rtc_acdb.err == ACDB_RTC_SUCCESS
+				&& rtc_read->viraddr != NULL) {
+				memcpy(buffer, rtc_read->viraddr, count);
+				n = count;
+				while (counter < count) {
+					MM_DBG("%x", \
+						rtc_read->viraddr[counter]);
+					counter++;
+					}
+				}
+		}
+	}
+	}
+	return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+
+static bool acdb_set_tx_rtc(const char *ubuf, size_t writecount)
+{
+	struct audpreproc_cmd_cfg_iir_tuning_filter_params *preproc_iir;
+	struct audpreproc_cmd_cfg_agc_params *preproc_agc;
+	struct audpreproc_cmd_cfg_ns_params *preproc_ns;
+	s32	result = 0;
+	bool retval = false;
+	unsigned short iircmdsize =
+		sizeof(struct audpreproc_cmd_cfg_iir_tuning_filter_params);
+	unsigned short iircmdid = AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS;
+
+	rtc_acdb.err = ACDB_RTC_ERR_UNKNOWN_FAILURE;
+
+	switch (rtc_acdb.set_abid) {
+
+	case AUDPREPROC_CMD_CFG_AGC_PARAMS:
+	case AUDPREPROC_CMD_CFG_AGC_PARAMS_2:
+	{
+		preproc_agc = kmalloc(sizeof(\
+					struct audpreproc_cmd_cfg_agc_params),\
+					GFP_KERNEL);
+		if ((sizeof(struct audpreproc_cmd_cfg_agc_params) -\
+			(2*sizeof(unsigned short)))
+			< writecount) {
+				MM_ERR("ACDB DATA WRITE --\
+					AGC TX writecount > DSP struct\n");
+		} else {
+			if (preproc_agc != NULL) {
+				char *base; unsigned short offset;
+				unsigned short *offset_addr;
+				base = (char *)preproc_agc;
+				offset = offsetof(struct \
+						audpreproc_cmd_cfg_agc_params,\
+						tx_agc_param_mask);
+				offset_addr = (unsigned short *)(base + offset);
+				if ((copy_from_user(offset_addr,\
+					(void *)ubuf, writecount)) == 0x00) {
+					preproc_agc->cmd_id =
+						AUDPREPROC_CMD_CFG_AGC_PARAMS;
+					preproc_agc->stream_id =
+						acdb_data.preproc_stream_id;
+					result = audpreproc_dsp_set_agc(
+						preproc_agc,
+						sizeof(struct \
+						audpreproc_cmd_cfg_agc_params));
+					if (result) {
+						MM_ERR("ACDB=> Failed to \
+							send AGC data to \
+							preproc)\n");
+					} else {
+						retval = true;
+					       }
+				} else {
+					MM_ERR("ACDB DATA WRITE ---\
+						GC Tx copy_from_user Fail\n");
+				}
+			} else {
+				MM_ERR("ACDB DATA WRITE --\
+					AGC TX kalloc Failed LEN\n");
+			}
+		}
+		if (preproc_agc != NULL)
+			kfree(preproc_agc);
+		break;
+	}
+	case AUDPREPROC_CMD_CFG_NS_PARAMS:
+	{
+
+		preproc_ns = kmalloc(sizeof(struct \
+					audpreproc_cmd_cfg_ns_params),\
+					GFP_KERNEL);
+		if ((sizeof(struct audpreproc_cmd_cfg_ns_params) -\
+				(2 * sizeof(unsigned short)))
+				< writecount) {
+				MM_ERR("ACDB DATA WRITE --\
+					NS TX writecount > DSP struct\n");
+		} else {
+			if (preproc_ns != NULL) {
+				char *base; unsigned short offset;
+				unsigned short *offset_addr;
+				base = (char *)preproc_ns;
+				offset = offsetof(struct \
+						audpreproc_cmd_cfg_ns_params,\
+						ec_mode_new);
+				offset_addr = (unsigned short *)(base + offset);
+				if ((copy_from_user(offset_addr,\
+					(void *)ubuf, writecount)) == 0x00) {
+					preproc_ns->cmd_id =
+						AUDPREPROC_CMD_CFG_NS_PARAMS;
+					preproc_ns->stream_id =
+						acdb_data.preproc_stream_id;
+					result = audpreproc_dsp_set_ns(
+						preproc_ns,
+						sizeof(struct \
+						audpreproc_cmd_cfg_ns_params));
+					if (result) {
+						MM_ERR("ACDB=> Failed to send \
+							NS data to preproc\n");
+					} else {
+						retval = true;
+					}
+				} else {
+					MM_ERR("ACDB DATA WRITE ---NS Tx \
+						copy_from_user Fail\n");
+					}
+			} else {
+				MM_ERR("ACDB DATA WRITE --NS TX\
+					kalloc Failed LEN\n");
+			}
+		}
+		if (preproc_ns != NULL)
+			kfree(preproc_ns);
+		break;
+	}
+	case AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS:
+	{
+
+		preproc_iir = kmalloc(sizeof(struct \
+				audpreproc_cmd_cfg_iir_tuning_filter_params),\
+				GFP_KERNEL);
+		if ((sizeof(struct \
+			audpreproc_cmd_cfg_iir_tuning_filter_params)-\
+			(2 * sizeof(unsigned short)))
+			< writecount) {
+			MM_ERR("ACDB DATA WRITE --IIR TX writecount\
+						> DSP struct\n");
+		} else {
+			if (preproc_iir != NULL) {
+				char *base; unsigned short offset;
+				unsigned short *offset_addr;
+				base = (char *)preproc_iir;
+				offset = offsetof(struct \
+				audpreproc_cmd_cfg_iir_tuning_filter_params,\
+				active_flag);
+				offset_addr = (unsigned short *)(base + \
+						offset);
+				if ((copy_from_user(offset_addr,\
+					(void *)ubuf, writecount)) == 0x00) {
+					preproc_iir->cmd_id = iircmdid;
+					preproc_iir->stream_id =
+						acdb_data.preproc_stream_id;
+					result = audpreproc_dsp_set_iir(\
+							preproc_iir,
+							iircmdsize);
+					if (result) {
+						MM_ERR("ACDB=> Failed to send\
+						IIR data to preproc\n");
+					} else {
+						retval = true;
+					}
+				} else {
+					MM_ERR("ACDB DATA WRITE ---IIR Tx \
+						copy_from_user Fail\n");
+				}
+			} else {
+				MM_ERR("ACDB DATA WRITE --IIR TX kalloc \
+					Failed LEN\n");
+		     }
+		}
+		if (preproc_iir != NULL)
+			kfree(preproc_iir);
+		break;
+	}
+	}
+	return retval;
+}
+
+static bool acdb_set_rx_rtc(const char *ubuf, size_t writecount)
+{
+
+	struct audpp_cmd_cfg_object_params_volpan *volpan_config;
+	struct audpp_cmd_cfg_object_params_mbadrc *mbadrc_config;
+	struct acdb_block_mbadrc_rtc *acdb_mbadrc_rtc;
+	struct audpp_cmd_cfg_object_params_sidechain *stf_config;
+	struct audpp_cmd_cfg_object_params_spectram *spa_config;
+	struct audpp_cmd_cfg_object_params_eqalizer *eq_config;
+	struct audpp_cmd_cfg_object_params_pcm *iir_config;
+	unsigned short temp_spa[34];
+	struct rtc_acdb_pmem *rtc_write = &rtc_acdb.rtc_write;
+	s32	result = 0;
+	bool retval = false;
+
+	switch (rtc_acdb.set_abid) {
+	case AUDPP_CMD_VOLUME_PAN:
+	{
+		volpan_config =  kmalloc(sizeof(struct \
+					 audpp_cmd_cfg_object_params_volpan),\
+					 GFP_KERNEL);
+		if ((sizeof(struct audpp_cmd_cfg_object_params_volpan) -\
+			sizeof(struct audpp_cmd_cfg_object_params_common))
+			< writecount) {
+			MM_ERR("ACDB DATA WRITE --\
+				VolPan writecount > DSP struct\n");
+		} else {
+			if (volpan_config != NULL) {
+				char *base; unsigned short offset;
+				unsigned short *offset_addr;
+				base = (char *)volpan_config;
+				offset = offsetof(struct \
+					audpp_cmd_cfg_object_params_volpan,\
+					volume);
+				offset_addr = (unsigned short *)(base+offset);
+				if ((copy_from_user(offset_addr,\
+					(void *)ubuf, writecount)) == 0x00) {
+					MM_ERR("ACDB RX WRITE DATA:\
+						AUDPP_CMD_VOLUME_PAN\n");
+					result = audpp_set_volume_and_pan(
+						acdb_data.device_info->dev_id,\
+						volpan_config->volume,
+						volpan_config->pan,
+						COPP);
+					if (result) {
+						MM_ERR("ACDB=> Failed to \
+							send VOLPAN data to"
+							" postproc\n");
+					} else {
+						retval = true;
+					}
+				} else {
+					MM_ERR("ACDB DATA WRITE ---\
+						copy_from_user Fail\n");
+				}
+			} else {
+				MM_ERR("ACDB DATA WRITE --\
+					Vol Pan kalloc Failed LEN\n");
+			}
+		}
+	if (volpan_config != NULL)
+		kfree(volpan_config);
+	break;
+	}
+
+	case AUDPP_CMD_IIR_TUNING_FILTER:
+	{
+		iir_config =  kmalloc(sizeof(struct \
+				audpp_cmd_cfg_object_params_pcm),\
+				GFP_KERNEL);
+		if ((sizeof(struct audpp_cmd_cfg_object_params_pcm) -\
+			sizeof(struct audpp_cmd_cfg_object_params_common))
+			< writecount) {
+			MM_ERR("ACDB DATA WRITE --\
+					IIR RX writecount > DSP struct\n");
+		} else {
+			if (iir_config != NULL) {
+				char *base; unsigned short offset;
+				unsigned short *offset_addr;
+				base = (char *)iir_config;
+				offset = offsetof(struct \
+					audpp_cmd_cfg_object_params_pcm,\
+					active_flag);
+				offset_addr = (unsigned short *)(base+offset);
+				if ((copy_from_user(offset_addr,\
+					(void *)ubuf, writecount)) == 0x00) {
+
+					iir_config->common.cmd_id =
+						AUDPP_CMD_CFG_OBJECT_PARAMS;
+					iir_config->common.stream =
+						AUDPP_CMD_COPP_STREAM;
+					iir_config->common.stream_id = 0;
+					iir_config->common.obj_cfg =
+						AUDPP_CMD_OBJ0_UPDATE;
+					iir_config->common.command_type = 0;
+					MM_ERR("ACDB RX WRITE DATA:\
+					AUDPP_CMD_IIR_TUNING_FILTER\n");
+					result = audpp_dsp_set_rx_iir(
+						acdb_data.device_info->dev_id,
+						iir_config->active_flag,\
+						iir_config, COPP);
+					if (result) {
+						MM_ERR("ACDB=> Failed to send\
+							IIR data to\
+							postproc\n");
+					} else {
+						retval = true;
+					}
+				} else {
+					MM_ERR("ACDB DATA WRITE ---\
+						IIR Rx copy_from_user Fail\n");
+				      }
+			 } else {
+				MM_ERR("ACDB DATA WRITE --\
+					acdb_iir_block kalloc Failed LEN\n");
+			}
+		}
+		if (iir_config != NULL)
+			kfree(iir_config);
+		break;
+	}
+	case AUDPP_CMD_EQUALIZER:
+	{
+		eq_config =  kmalloc(sizeof(struct \
+				audpp_cmd_cfg_object_params_eqalizer),\
+				GFP_KERNEL);
+	if ((sizeof(struct audpp_cmd_cfg_object_params_eqalizer) -\
+			sizeof(struct audpp_cmd_cfg_object_params_common))
+			< writecount) {
+			MM_ERR("ACDB DATA WRITE --\
+			EQ RX writecount > DSP struct\n");
+		} else {
+			if (eq_config != NULL) {
+				char *base; unsigned short offset;
+				unsigned short *offset_addr;
+				base = (char *)eq_config;
+				offset = offsetof(struct \
+					audpp_cmd_cfg_object_params_eqalizer,\
+					eq_flag);
+				offset_addr = (unsigned short *)(base+offset);
+				if ((copy_from_user(offset_addr,\
+					(void *)ubuf, writecount)) == 0x00) {
+					eq_config->common.cmd_id =
+						AUDPP_CMD_CFG_OBJECT_PARAMS;
+					eq_config->common.stream =
+						AUDPP_CMD_COPP_STREAM;
+					eq_config->common.stream_id = 0;
+					eq_config->common.obj_cfg =
+						AUDPP_CMD_OBJ0_UPDATE;
+					eq_config->common.command_type = 0;
+					MM_ERR("ACDB RX WRITE\
+					DATA:AUDPP_CMD_EQUALIZER\n");
+					result = audpp_dsp_set_eq(
+						acdb_data.device_info->dev_id,
+						eq_config->eq_flag,\
+						eq_config,
+						COPP);
+					if (result) {
+						MM_ERR("ACDB=> Failed to \
+						send EQ data to postproc\n");
+					} else {
+						retval = true;
+					}
+				} else {
+					MM_ERR("ACDB DATA WRITE ---\
+					EQ Rx copy_from_user Fail\n");
+				}
+			} else {
+				MM_ERR("ACDB DATA WRITE --\
+					EQ kalloc Failed LEN\n");
+			}
+		}
+		if (eq_config != NULL)
+			kfree(eq_config);
+		break;
+	}
+
+	case AUDPP_CMD_SPECTROGRAM:
+	{
+		spa_config =  kmalloc(sizeof(struct \
+				audpp_cmd_cfg_object_params_spectram),\
+				GFP_KERNEL);
+		if ((sizeof(struct audpp_cmd_cfg_object_params_spectram)-\
+				sizeof(struct \
+				audpp_cmd_cfg_object_params_common))
+				< (2 * sizeof(unsigned short))) {
+					MM_ERR("ACDB DATA WRITE --SPA \
+					RX writecount > DSP struct\n");
+		} else {
+			if (spa_config != NULL) {
+				if ((copy_from_user(&temp_spa[0],\
+					(void *)ubuf,
+					(34 * sizeof(unsigned short))))
+					== 0x00) {
+					spa_config->common.cmd_id =
+						AUDPP_CMD_CFG_OBJECT_PARAMS;
+					spa_config->common.stream =
+						AUDPP_CMD_COPP_STREAM;
+					spa_config->common.stream_id = 0;
+					spa_config->common.obj_cfg =
+						AUDPP_CMD_OBJ0_UPDATE;
+					spa_config->common.command_type = 0;
+					spa_config->sample_interval =
+						temp_spa[0];
+					spa_config->num_coeff = temp_spa[1];
+					MM_ERR("ACDB RX WRITE DATA:\
+						AUDPP_CMD_SPECTROGRAM\n");
+					result = audpp_dsp_set_spa(
+						acdb_data.device_info->dev_id,\
+						spa_config, COPP);
+					if (result) {
+						MM_ERR("ACDB=> Failed to \
+							send SPA data \
+							to postproc\n");
+					} else {
+						retval = true;
+					      }
+				} else {
+					MM_ERR("ACDB DATA WRITE \
+					---SPA Rx copy_from_user\
+					Fail\n");
+				}
+			} else {
+				MM_ERR("ACDB DATA WRITE --\
+				SPA kalloc Failed LEN\n");
+			       }
+			}
+		if (spa_config != NULL)
+			kfree(spa_config);
+	break;
+	}
+	case AUDPP_CMD_MBADRC:
+	{
+		acdb_mbadrc_rtc =  kmalloc(sizeof(struct \
+					acdb_block_mbadrc_rtc),\
+					GFP_KERNEL);
+		mbadrc_config =  kmalloc(sizeof(struct \
+					audpp_cmd_cfg_object_params_mbadrc),\
+					GFP_KERNEL);
+		if (mbadrc_config != NULL && acdb_mbadrc_rtc != NULL) {
+			if ((copy_from_user(acdb_mbadrc_rtc,\
+				(void *)ubuf,
+				sizeof(struct acdb_block_mbadrc_rtc)))
+				== 0x00) {
+				mbadrc_config->common.cmd_id =
+					AUDPP_CMD_CFG_OBJECT_PARAMS;
+				mbadrc_config->common.stream =
+					AUDPP_CMD_COPP_STREAM;
+				mbadrc_config->common.stream_id = 0;
+				mbadrc_config->common.obj_cfg =
+					AUDPP_CMD_OBJ0_UPDATE;
+				mbadrc_config->common.command_type = 0;
+				mbadrc_config->enable =
+						acdb_mbadrc_rtc->enable;
+				mbadrc_config->num_bands =
+						acdb_mbadrc_rtc->num_bands;
+				mbadrc_config->down_samp_level =
+				acdb_mbadrc_rtc->down_samp_level;
+				mbadrc_config->adrc_delay =
+					acdb_mbadrc_rtc->adrc_delay;
+				memcpy(mbadrc_config->adrc_band,\
+					acdb_mbadrc_rtc->adrc_band,\
+					AUDPP_MAX_MBADRC_BANDS *\
+					sizeof(struct adrc_config));
+				if (mbadrc_config->num_bands > 1) {
+					mbadrc_config->ext_buf_size =
+						(97 * 2) + (33 * 2 * \
+					(mbadrc_config->num_bands - 2));
+				}
+				mbadrc_config->ext_partition = 0;
+				mbadrc_config->ext_buf_lsw =
+					(u16) EXTRACT_LOW_WORD(\
+						rtc_write->phys);
+				mbadrc_config->ext_buf_msw =
+					(u16) EXTRACT_HIGH_WORD(\
+						rtc_write->phys);
+				memcpy(rtc_write->viraddr,
+					acdb_mbadrc_rtc->ExtBuff,
+					(196*sizeof(signed int)));
+				result = audpp_dsp_set_mbadrc(
+						acdb_data.device_info->dev_id,
+						mbadrc_config->enable,
+						mbadrc_config, COPP);
+				if (result) {
+					MM_ERR("ACDB=> Failed to \
+						Send MBADRC data \
+						to postproc\n");
+				} else {
+					retval = true;
+				}
+			} else {
+				MM_ERR("ACDB DATA WRITE ---\
+					MBADRC Rx copy_from_user Fail\n");
+			}
+		} else {
+			MM_ERR("ACDB DATA WRITE --MBADRC kalloc Failed LEN\n");
+		}
+		if (mbadrc_config != NULL)
+			kfree(mbadrc_config);
+		if (acdb_mbadrc_rtc != NULL)
+			kfree(acdb_mbadrc_rtc);
+	break;
+	}
+	case AUDPP_CMD_SIDECHAIN_TUNING_FILTER:
+	{
+		stf_config =  kmalloc(sizeof(struct \
+				audpp_cmd_cfg_object_params_sidechain),\
+				GFP_KERNEL);
+		if ((sizeof(struct audpp_cmd_cfg_object_params_sidechain) -\
+			sizeof(struct audpp_cmd_cfg_object_params_common))
+			< writecount) {
+				MM_ERR("ACDB DATA WRITE --\
+					STF RX writecount > DSP struct\n");
+		} else {
+			if (stf_config != NULL) {
+				char *base; unsigned short offset;
+				unsigned short *offset_addr;
+				base = (char *)stf_config;
+				offset = offsetof(struct \
+					audpp_cmd_cfg_object_params_sidechain,\
+					active_flag);
+				offset_addr = (unsigned short *)(base+offset);
+				if ((copy_from_user(offset_addr,\
+					(void *)ubuf, writecount)) == 0x00) {
+					stf_config->common.cmd_id =
+						AUDPP_CMD_CFG_OBJECT_PARAMS;
+					stf_config->common.stream =
+						AUDPP_CMD_COPP_STREAM;
+					stf_config->common.stream_id = 0;
+					stf_config->common.obj_cfg =
+						AUDPP_CMD_OBJ0_UPDATE;
+					stf_config->common.command_type = 0;
+					MM_ERR("ACDB RX WRITE DATA:\
+					AUDPP_CMD_SIDECHAIN_TUNING_FILTER\n");
+				result = audpp_dsp_set_stf(
+						acdb_data.device_info->dev_id,\
+						stf_config->active_flag,\
+						stf_config, COPP);
+					if (result) {
+						MM_ERR("ACDB=> Failed to send \
+						STF data to postproc\n");
+					} else {
+						retval = true;
+					}
+				} else {
+					MM_ERR("ACDB DATA WRITE ---\
+					STF Rx copy_from_user Fail\n");
+				}
+			} else {
+				MM_ERR("ACDB DATA WRITE \
+					STF kalloc Failed LEN\n");
+		}
+	}
+	if (stf_config != NULL)
+		kfree(stf_config);
+	break;
+	}
+	}
+	return retval;
+}
+static ssize_t rtc_getsetabid_data_dbg_write(struct file *filp,
+						const char __user *ubuf,
+						size_t cnt, loff_t *ppos)
+{
+	if (rtc_acdb.valid_abid != true) {
+		MM_INFO("ACDB DATA READ ---INVALID ABID\n");
+		rtc_acdb.err = ACDB_RTC_ERR_INVALID_ABID;
+	} else {
+		if (rtc_acdb.tx_rx_ctl == ACDB_RTC_RX) {
+			if (acdb_set_rx_rtc(ubuf, cnt)) {
+				rtc_acdb.err = ACDB_RTC_SUCCESS;
+			} else {
+			rtc_acdb.err = ACDB_RTC_ERR_UNKNOWN_FAILURE;
+			cnt = 0;
+		}
+	} else if (rtc_acdb.tx_rx_ctl == ACDB_RTC_TX) {
+		if (acdb_set_tx_rtc(ubuf, cnt)) {
+			rtc_acdb.err = ACDB_RTC_SUCCESS;
+		} else {
+			rtc_acdb.err = ACDB_RTC_ERR_UNKNOWN_FAILURE;
+			cnt = 0;
+		}
+	}
+  }
+	return cnt;
+}
+
+
+static const	struct file_operations rtc_acdb_data_debug_fops = {
+	.open = rtc_getsetabid_data_dbg_open,
+	.write = rtc_getsetabid_data_dbg_write,
+	.read = rtc_getsetabid_data_dbg_read
+};
+
+static const	struct file_operations rtc_acdb_debug_fops = {
+	.open = rtc_getsetabid_dbg_open,
+	.write = rtc_getsetabid_dbg_write,
+	.read = rtc_getsetabid_dbg_read
+};
+
+static void rtc_acdb_deinit(void)
+{
+	struct rtc_acdb_pmem *rtc_read = &rtc_acdb.rtc_read;
+	struct rtc_acdb_pmem *rtc_write = &rtc_acdb.rtc_write;
+	if (get_set_abid_dentry) {
+		MM_DBG("GetSet ABID remove debugfs\n");
+		debugfs_remove(get_set_abid_dentry);
+	}
+
+	if (get_set_abid_data_dentry) {
+		MM_DBG("GetSet ABID remove debugfs\n");
+		debugfs_remove(get_set_abid_data_dentry);
+	}
+	rtc_acdb.abid = 0;
+	rtc_acdb.acdb_id = 0;
+	rtc_acdb.cmd_id = 0;
+	rtc_acdb.err = 1;
+	rtc_acdb.set_abid = 0;
+	rtc_acdb.set_iid = 0;
+	rtc_acdb.tx_rx_ctl = 0;
+	rtc_acdb.valid_abid = false;
+
+	if (rtc_read->viraddr != NULL || ((void *)rtc_read->phys) != NULL) {
+		iounmap(rtc_read->viraddr);
+		pmem_kfree(rtc_read->phys);
+	}
+	if (rtc_write->viraddr != NULL || ((void *)rtc_write->phys) != NULL) {
+		iounmap(rtc_write->viraddr);
+		pmem_kfree(rtc_write->phys);
+	}
+}
+
+static bool rtc_acdb_init(void)
+{
+	struct rtc_acdb_pmem *rtc_read = &rtc_acdb.rtc_read;
+	struct rtc_acdb_pmem *rtc_write = &rtc_acdb.rtc_write;
+	s32 result = 0;
+	char name[sizeof "get_set_abid"+1];
+	char name1[sizeof "get_set_abid_data"+1];
+	rtc_acdb.abid = 0;
+	rtc_acdb.acdb_id = 0;
+	rtc_acdb.cmd_id = 0;
+	rtc_acdb.err = 1;
+	rtc_acdb.set_abid = 0;
+	rtc_acdb.set_iid = 0;
+	rtc_acdb.valid_abid = false;
+	rtc_acdb.tx_rx_ctl = 0;
+	snprintf(name, sizeof name, "get_set_abid");
+	get_set_abid_dentry = debugfs_create_file(name,
+					S_IFREG | S_IRUGO | S_IWUGO,
+					NULL, NULL, &rtc_acdb_debug_fops);
+	if (IS_ERR(get_set_abid_dentry)) {
+		MM_ERR("SET GET ABID debugfs_create_file failed\n");
+		return false;
+	}
+
+    snprintf(name1, sizeof name1, "get_set_abid_data");
+	get_set_abid_data_dentry = debugfs_create_file(name1,
+					S_IFREG | S_IRUGO | S_IWUGO,
+					NULL, NULL,
+					&rtc_acdb_data_debug_fops);
+	if (IS_ERR(get_set_abid_data_dentry)) {
+		MM_ERR("SET GET ABID DATA debugfs_create_file failed\n");
+		return false;
+	}
+
+	rtc_read->phys = pmem_kalloc(PMEM_RTC_ACDB_QUERY_MEM,
+					PMEM_MEMTYPE_EBI1|PMEM_ALIGNMENT_4K);
+
+	if (IS_ERR((void *)rtc_read->phys)) {
+		MM_ERR("ACDB Cannot allocate physical memory\n");
+		result = -ENOMEM;
+		goto error;
+	}
+	rtc_read->viraddr = ioremap(rtc_read->phys, PMEM_RTC_ACDB_QUERY_MEM);
+
+	if (rtc_read->viraddr == NULL) {
+		MM_ERR("ACDB Could not map physical address\n");
+		result = -ENOMEM;
+		goto error;
+	}
+	memset(rtc_read->viraddr, 0, PMEM_RTC_ACDB_QUERY_MEM);
+
+	rtc_write->phys = pmem_kalloc(PMEM_RTC_ACDB_QUERY_MEM,
+					PMEM_MEMTYPE_EBI1|PMEM_ALIGNMENT_4K);
+
+	if (IS_ERR((void *)rtc_write->phys)) {
+		MM_ERR("ACDB Cannot allocate physical memory\n");
+		result = -ENOMEM;
+		goto error;
+	}
+	rtc_write->viraddr = ioremap(rtc_write->phys, PMEM_RTC_ACDB_QUERY_MEM);
+
+	if (rtc_write->viraddr == NULL) {
+		MM_ERR("ACDB Could not map physical address\n");
+		result = -ENOMEM;
+		goto error;
+	}
+	memset(rtc_write->viraddr, 0, PMEM_RTC_ACDB_QUERY_MEM);
+	init_waitqueue_head(&rtc_acdb.wait);
+	return true;
+error:
+	MM_DBG("INIT RTC FAILED REMOVING RTC DEBUG FS\n");
+	if (get_set_abid_dentry) {
+		MM_DBG("GetSet ABID remove debugfs\n");
+		debugfs_remove(get_set_abid_dentry);
+	}
+
+	if (get_set_abid_data_dentry) {
+		MM_DBG("GetSet ABID remove debugfs\n");
+		debugfs_remove(get_set_abid_data_dentry);
+	}
+	if (rtc_read->viraddr != NULL || ((void *)rtc_read->phys) != NULL) {
+		iounmap(rtc_read->viraddr);
+		pmem_kfree(rtc_read->phys);
+	}
+	if (rtc_write->viraddr != NULL || ((void *)rtc_write->phys) != NULL) {
+		iounmap(rtc_write->viraddr);
+		pmem_kfree(rtc_write->phys);
+	}
+	return false;
+}
+#endif /*CONFIG_DEBUG_FS*/
+static s32 acdb_set_calibration_blk(unsigned long arg)
+{
+	struct acdb_cmd_device acdb_cmd;
+	s32 result = 0;
+
+	MM_DBG("acdb_set_calibration_blk\n");
+	if (copy_from_user(&acdb_cmd, (struct acdb_cmd_device *)arg,
+			sizeof(acdb_cmd))) {
+		MM_ERR("Failed copy command struct from user in"
+			"acdb_set_calibration_blk\n");
+		return -EFAULT;
+	}
+	acdb_cmd.phys_buf = (u32 *)acdb_data.paddr;
+
+	MM_DBG("acdb_cmd.phys_buf %x\n", (u32)acdb_cmd.phys_buf);
+
+	result = dalrpc_fcn_8(ACDB_DalACDB_ioctl, acdb_data.handle,
+			(const void *)&acdb_cmd, sizeof(acdb_cmd),
+			&acdb_data.acdb_result,
+			sizeof(acdb_data.acdb_result));
+
+	if (result < 0) {
+		MM_ERR("ACDB=> Device Set RPC failure"
+			" result = %d\n", result);
+		return -EINVAL;
+	} else {
+		MM_ERR("ACDB=> Device Set RPC success\n");
+		if (acdb_data.acdb_result.result == ACDB_RES_SUCCESS)
+			MM_DBG("ACDB_SET_DEVICE Success\n");
+		else if (acdb_data.acdb_result.result == ACDB_RES_FAILURE)
+			MM_ERR("ACDB_SET_DEVICE Failure\n");
+		else if (acdb_data.acdb_result.result == ACDB_RES_BADPARM)
+			MM_ERR("ACDB_SET_DEVICE BadParams\n");
+		else
+			MM_ERR("Unknown error\n");
+	}
+	return result;
+}
+
+static s32 acdb_get_calibration_blk(unsigned long arg)
+{
+	s32 result = 0;
+	struct acdb_cmd_device acdb_cmd;
+
+	MM_DBG("acdb_get_calibration_blk\n");
+
+	if (copy_from_user(&acdb_cmd, (struct acdb_cmd_device *)arg,
+			sizeof(acdb_cmd))) {
+		MM_ERR("Failed copy command struct from user in"
+			"acdb_get_calibration_blk\n");
+		return -EFAULT;
+	}
+	acdb_cmd.phys_buf = (u32 *)acdb_data.paddr;
+	MM_ERR("acdb_cmd.phys_buf %x\n", (u32)acdb_cmd.phys_buf);
+
+	result = dalrpc_fcn_8(ACDB_DalACDB_ioctl, acdb_data.handle,
+			(const void *)&acdb_cmd, sizeof(acdb_cmd),
+			&acdb_data.acdb_result,
+			sizeof(acdb_data.acdb_result));
+
+	if (result < 0) {
+		MM_ERR("ACDB=> Device Get RPC failure"
+			" result = %d\n", result);
+		return -EINVAL;
+	} else {
+		MM_ERR("ACDB=> Device Get RPC Success\n");
+		if (acdb_data.acdb_result.result == ACDB_RES_SUCCESS)
+			MM_DBG("ACDB_GET_DEVICE Success\n");
+		else if (acdb_data.acdb_result.result == ACDB_RES_FAILURE)
+			MM_ERR("ACDB_GET_DEVICE Failure\n");
+		else if (acdb_data.acdb_result.result == ACDB_RES_BADPARM)
+			MM_ERR("ACDB_GET_DEVICE BadParams\n");
+		else
+			MM_ERR("Unknown error\n");
+	}
+	return result;
+}
+
+static int audio_acdb_open(struct inode *inode, struct file *file)
+{
+	MM_DBG("%s\n", __func__);
+	return 0;
+}
+static int audio_acdb_release(struct inode *inode, struct file *file)
+{
+	MM_DBG("%s\n", __func__);
+	return 0;
+}
+
+static long audio_acdb_ioctl(struct file *file, unsigned int cmd,
+					unsigned long arg)
+{
+	int rc = 0;
+	unsigned long flags = 0;
+	struct msm_audio_pmem_info info;
+
+	MM_DBG("%s\n", __func__);
+
+	switch (cmd) {
+	case AUDIO_SET_EQ:
+		MM_DBG("IOCTL SET_EQ_CONFIG\n");
+		if (copy_from_user(&acdb_data.eq.num_bands, (void *) arg,
+				sizeof(acdb_data.eq) -
+				(AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN + 2))) {
+			rc = -EFAULT;
+			break;
+		}
+		spin_lock_irqsave(&acdb_data.dsp_lock, flags);
+		acdb_data.dec_id    = 0;
+		rc = audpp_dsp_set_eq(acdb_data.dec_id, 1,
+			&acdb_data.eq, COPP);
+		if (rc < 0)
+			MM_ERR("AUDPP returned err =%d\n", rc);
+		spin_unlock_irqrestore(&acdb_data.dsp_lock, flags);
+		break;
+	case AUDIO_REGISTER_PMEM:
+		MM_DBG("AUDIO_REGISTER_PMEM\n");
+		if (copy_from_user(&info, (void *) arg, sizeof(info))) {
+			MM_ERR("Cannot copy from user\n");
+			return -EFAULT;
+		}
+		rc = get_pmem_file(info.fd, &acdb_data.paddr,
+					&acdb_data.kvaddr,
+					&acdb_data.pmem_len,
+					&acdb_data.file);
+		if (rc == 0)
+			acdb_data.pmem_fd = info.fd;
+		break;
+	case AUDIO_DEREGISTER_PMEM:
+		if (acdb_data.pmem_fd)
+			put_pmem_file(acdb_data.file);
+		break;
+	case AUDIO_SET_ACDB_BLK:
+		MM_DBG("IOCTL AUDIO_SET_ACDB_BLK\n");
+		rc = acdb_set_calibration_blk(arg);
+		break;
+	case AUDIO_GET_ACDB_BLK:
+		MM_DBG("IOiCTL AUDIO_GET_ACDB_BLK\n");
+		rc = acdb_get_calibration_blk(arg);
+		break;
+	default:
+		MM_DBG("Unknown IOCTL%d\n", cmd);
+		rc = -EINVAL;
+	}
+	return rc;
+}
+
+static const struct file_operations acdb_fops = {
+	.owner = THIS_MODULE,
+	.open = audio_acdb_open,
+	.release = audio_acdb_release,
+	.llseek = no_llseek,
+	.unlocked_ioctl = audio_acdb_ioctl
+};
+
+struct miscdevice acdb_misc = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "msm_acdb",
+	.fops	= &acdb_fops,
+};
+
+static s32 acdb_get_calibration(void)
+{
+	struct acdb_cmd_get_device_table	acdb_cmd;
+	s32					result = 0;
+	u32 iterations = 0;
+
+	MM_DBG("acdb state = %d\n", acdb_data.acdb_state);
+
+	acdb_cmd.command_id = ACDB_GET_DEVICE_TABLE;
+	acdb_cmd.device_id = acdb_data.device_info->acdb_id;
+	acdb_cmd.network_id = 0x0108B153;
+	acdb_cmd.sample_rate_id = acdb_data.device_info->sample_rate;
+	acdb_cmd.total_bytes = ACDB_BUF_SIZE;
+	acdb_cmd.phys_buf = (u32 *)acdb_data.phys_addr;
+	MM_DBG("device_id = %d, sampling_freq = %d\n",
+				acdb_cmd.device_id, acdb_cmd.sample_rate_id);
+
+	do {
+		result = dalrpc_fcn_8(ACDB_DalACDB_ioctl, acdb_data.handle,
+				(const void *)&acdb_cmd, sizeof(acdb_cmd),
+				&acdb_data.acdb_result,
+				sizeof(acdb_data.acdb_result));
+
+		if (result < 0) {
+			MM_ERR("ACDB=> Device table RPC failure"
+				" result = %d\n", result);
+			goto error;
+		}
+		/*following check is introduced to handle boot up race
+		condition between AUDCAL SW peers running on apps
+		and modem (ACDB_RES_BADSTATE indicates modem AUDCAL SW is
+		not in initialized sate) we need to retry to get ACDB
+		values*/
+		if (acdb_data.acdb_result.result == ACDB_RES_BADSTATE) {
+			msleep(500);
+			iterations++;
+		} else if (acdb_data.acdb_result.result == ACDB_RES_SUCCESS) {
+			MM_DBG("Modem query for acdb values is successful"
+					" (iterations = %d)\n", iterations);
+			acdb_data.acdb_state |= CAL_DATA_READY;
+			return result;
+		} else {
+			MM_ERR("ACDB=> modem failed to fill acdb values,"
+					" reuslt = %d, (iterations = %d)\n",
+					acdb_data.acdb_result.result,
+					iterations);
+			goto error;
+		}
+	} while (iterations < MAX_RETRY);
+	MM_ERR("ACDB=> AUDCAL SW on modem is not in intiailized state (%d)\n",
+			acdb_data.acdb_result.result);
+error:
+	result = -EINVAL;
+	return result;
+}
+
+s32 acdb_get_calibration_data(struct acdb_get_block *get_block)
+{
+	s32 result = -EINVAL;
+	struct acdb_cmd_device acdb_cmd;
+	struct acdb_result acdb_result;
+
+	MM_DBG("acdb_get_calibration_data\n");
+
+	acdb_cmd.command_id = ACDB_GET_DEVICE;
+	acdb_cmd.network_id = 0x0108B153;
+	acdb_cmd.device_id = get_block->acdb_id;
+	acdb_cmd.sample_rate_id = get_block->sample_rate_id;
+	acdb_cmd.interface_id = get_block->interface_id;
+	acdb_cmd.algorithm_block_id = get_block->algorithm_block_id;
+	acdb_cmd.total_bytes = get_block->total_bytes;
+	acdb_cmd.phys_buf = (u32 *)acdb_data.get_blk_paddr;
+
+	result = dalrpc_fcn_8(ACDB_DalACDB_ioctl, acdb_data.handle,
+			(const void *)&acdb_cmd, sizeof(acdb_cmd),
+			&acdb_result,
+			sizeof(acdb_result));
+
+	if (result < 0) {
+		MM_ERR("ACDB=> Device Get RPC failure"
+			" result = %d\n", result);
+		goto err_state;
+	} else {
+		MM_DBG("ACDB=> Device Get RPC Success\n");
+		if (acdb_result.result == ACDB_RES_SUCCESS) {
+			MM_DBG("ACDB_GET_DEVICE Success\n");
+			result = 0;
+			memcpy(get_block->buf_ptr, acdb_data.get_blk_kvaddr,
+					get_block->total_bytes);
+		} else if (acdb_result.result == ACDB_RES_FAILURE)
+			MM_ERR("ACDB_GET_DEVICE Failure\n");
+		else if (acdb_result.result == ACDB_RES_BADPARM)
+			MM_ERR("ACDB_GET_DEVICE BadParams\n");
+		else
+			MM_ERR("Unknown error\n");
+	}
+err_state:
+	return result;
+}
+EXPORT_SYMBOL(acdb_get_calibration_data);
+
+static u8 check_device_info_already_present(
+		struct auddev_evt_audcal_info   audcal_info,
+			struct acdb_cache_node *acdb_cache_free_node)
+{
+	if ((audcal_info.dev_id ==
+				acdb_cache_free_node->device_info.dev_id) &&
+		(audcal_info.sample_rate ==
+				acdb_cache_free_node->device_info.\
+				sample_rate) &&
+			(audcal_info.acdb_id ==
+				acdb_cache_free_node->device_info.acdb_id)) {
+		MM_DBG("acdb values are already present\n");
+		/*if acdb state is not set for CAL_DATA_READY and node status
+		is filled, acdb state should be updated with CAL_DATA_READY
+		state*/
+		acdb_data.acdb_state |= CAL_DATA_READY;
+		/*checking for cache node status if it is not filled then the
+		acdb values are not cleaned from node so update node status
+		with acdb value filled*/
+		if ((acdb_cache_free_node->node_status != ACDB_VALUES_FILLED) &&
+			((acdb_data.device_info->dev_type & RX_DEVICE) == 1)) {
+			MM_DBG("device was released earlier\n");
+			acdb_cache_free_node->node_status = ACDB_VALUES_FILLED;
+			return 2; /*node is presnet but status as not filled*/
+		}
+		return 1; /*node is present but status as filled*/
+	}
+	MM_DBG("copying device info into node\n");
+	/*as device information is not present in cache copy
+	the current device information into the node*/
+	memcpy(&acdb_cache_free_node->device_info,
+				 &audcal_info, sizeof(audcal_info));
+	return 0; /*cant find the node*/
+}
+
+static struct acdb_iir_block *get_audpp_irr_block(void)
+{
+	struct header *prs_hdr;
+	u32 index = 0;
+
+	while (index < acdb_data.acdb_result.used_bytes) {
+		prs_hdr = (struct header *)(acdb_data.virt_addr + index);
+		if (prs_hdr->dbor_signature == DBOR_SIGNATURE) {
+			if (prs_hdr->abid == ABID_AUDIO_IIR_RX) {
+				if (prs_hdr->iid == IID_AUDIO_IIR_COEFF)
+					return (struct acdb_iir_block *)
+						(acdb_data.virt_addr + index
+						 + sizeof(struct header));
+			} else {
+				index += prs_hdr->data_len +
+						sizeof(struct header);
+			}
+		} else {
+			break;
+		}
+	}
+	return NULL;
+}
+
+
+static s32 acdb_fill_audpp_iir(void)
+{
+	struct acdb_iir_block *acdb_iir;
+	s32 i = 0;
+
+	acdb_iir = get_audpp_irr_block();
+	if (acdb_iir == NULL) {
+		MM_ERR("unable to find  audpp iir block returning\n");
+		return -1;
+	}
+	memset(acdb_data.pp_iir, 0, sizeof(*acdb_data.pp_iir));
+
+	acdb_data.pp_iir->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS;
+	acdb_data.pp_iir->common.stream = AUDPP_CMD_COPP_STREAM;
+	acdb_data.pp_iir->common.stream_id = 0;
+	acdb_data.pp_iir->common.obj_cfg = AUDPP_CMD_OBJ0_UPDATE;
+	acdb_data.pp_iir->common.command_type = 0;
+
+	acdb_data.pp_iir->active_flag = acdb_iir->enable_flag;
+	acdb_data.pp_iir->num_bands = acdb_iir->stage_count;
+	for (; i < acdb_iir->stage_count; i++) {
+		acdb_data.pp_iir->params_filter.filter_4_params.
+			numerator_filter[i].numerator_b0_filter_lsw =
+			acdb_iir->stages[i].b0_lo;
+		acdb_data.pp_iir->params_filter.filter_4_params.
+			numerator_filter[i].numerator_b0_filter_msw =
+			acdb_iir->stages[i].b0_hi;
+		acdb_data.pp_iir->params_filter.filter_4_params.
+			numerator_filter[i].numerator_b1_filter_lsw =
+			acdb_iir->stages[i].b1_lo;
+		acdb_data.pp_iir->params_filter.filter_4_params.
+			numerator_filter[i].numerator_b1_filter_msw =
+			acdb_iir->stages[i].b1_hi;
+		acdb_data.pp_iir->params_filter.filter_4_params.
+			numerator_filter[i].numerator_b2_filter_lsw =
+			acdb_iir->stages[i].b2_lo;
+		acdb_data.pp_iir->params_filter.filter_4_params.
+			numerator_filter[i].numerator_b2_filter_msw =
+			acdb_iir->stages[i].b2_hi;
+		acdb_data.pp_iir->params_filter.filter_4_params.
+			denominator_filter[i].denominator_a0_filter_lsw =
+			acdb_iir->stages_a[i].a1_lo;
+		acdb_data.pp_iir->params_filter.filter_4_params.
+			denominator_filter[i].denominator_a0_filter_msw =
+			acdb_iir->stages_a[i].a1_hi;
+		acdb_data.pp_iir->params_filter.filter_4_params.
+			denominator_filter[i].denominator_a1_filter_lsw =
+			acdb_iir->stages_a[i].a2_lo;
+		acdb_data.pp_iir->params_filter.filter_4_params.
+			denominator_filter[i].denominator_a1_filter_msw =
+			acdb_iir->stages_a[i].a2_hi;
+		acdb_data.pp_iir->params_filter.filter_4_params.
+			shift_factor_filter[i].shift_factor_0 =
+			acdb_iir->shift_factor[i];
+		acdb_data.pp_iir->params_filter.filter_4_params.pan_filter[i].
+			pan_filter_0 = acdb_iir->pan[i];
+	}
+	return 0;
+}
+
+static void extract_mbadrc(u32 *phy_addr, struct header *prs_hdr, u32 *index)
+{
+	if (prs_hdr->iid == IID_MBADRC_EXT_BUFF) {
+		MM_DBG("Got IID = IID_MBADRC_EXT_BUFF\n");
+		*phy_addr = acdb_data.phys_addr	+ *index +
+					sizeof(struct header);
+		memcpy(acdb_data.mbadrc_block.ext_buf,
+				(acdb_data.virt_addr + *index +
+					sizeof(struct header)), 196*2);
+		MM_DBG("phy_addr = %x\n", *phy_addr);
+		*index += prs_hdr->data_len + sizeof(struct header);
+	} else if (prs_hdr->iid == IID_MBADRC_BAND_CONFIG) {
+		MM_DBG("Got IID == IID_MBADRC_BAND_CONFIG\n");
+		memcpy(acdb_data.mbadrc_block.band_config, (acdb_data.virt_addr
+					+ *index + sizeof(struct header)),
+				sizeof(struct mbadrc_band_config_type) *
+					 acdb_data.mbadrc_block.parameters.\
+						mbadrc_num_bands);
+		*index += prs_hdr->data_len + sizeof(struct header);
+	} else if (prs_hdr->iid == IID_MBADRC_PARAMETERS) {
+		struct mbadrc_parameter *tmp;
+		tmp = (struct mbadrc_parameter *)(acdb_data.virt_addr + *index
+						+ sizeof(struct header));
+		MM_DBG("Got IID == IID_MBADRC_PARAMETERS\n");
+		acdb_data.mbadrc_block.parameters.mbadrc_enable =
+							tmp->mbadrc_enable;
+		acdb_data.mbadrc_block.parameters.mbadrc_num_bands =
+							tmp->mbadrc_num_bands;
+		acdb_data.mbadrc_block.parameters.mbadrc_down_sample_level =
+						tmp->mbadrc_down_sample_level;
+		acdb_data.mbadrc_block.parameters.mbadrc_delay =
+							tmp->mbadrc_delay;
+		*index += prs_hdr->data_len + sizeof(struct header);
+	}
+}
+
+static void get_audpp_mbadrc_block(u32 *phy_addr)
+{
+	struct header *prs_hdr;
+	u32 index = 0;
+
+	while (index < acdb_data.acdb_result.used_bytes) {
+		prs_hdr = (struct header *)(acdb_data.virt_addr + index);
+
+		if (prs_hdr->dbor_signature == DBOR_SIGNATURE) {
+			if (prs_hdr->abid == ABID_AUDIO_MBADRC_RX) {
+				if ((prs_hdr->iid == IID_MBADRC_EXT_BUFF)
+					|| (prs_hdr->iid ==
+						IID_MBADRC_BAND_CONFIG)
+					|| (prs_hdr->iid ==
+						IID_MBADRC_PARAMETERS)) {
+					extract_mbadrc(phy_addr, prs_hdr,
+								&index);
+				}
+			} else {
+				index += prs_hdr->data_len +
+						sizeof(struct header);
+			}
+		} else {
+			break;
+		}
+	}
+}
+
+static s32 acdb_fill_audpp_mbadrc(void)
+{
+	u32 mbadrc_phys_addr = -1;
+	get_audpp_mbadrc_block(&mbadrc_phys_addr);
+	if (IS_ERR_VALUE(mbadrc_phys_addr)) {
+		MM_ERR("failed to get mbadrc block\n");
+		return -1;
+	}
+
+	memset(acdb_data.pp_mbadrc, 0, sizeof(*acdb_data.pp_mbadrc));
+
+	acdb_data.pp_mbadrc->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS;
+	acdb_data.pp_mbadrc->common.stream = AUDPP_CMD_COPP_STREAM;
+	acdb_data.pp_mbadrc->common.stream_id = 0;
+	acdb_data.pp_mbadrc->common.obj_cfg = AUDPP_CMD_OBJ0_UPDATE;
+	acdb_data.pp_mbadrc->common.command_type = 0;
+
+	acdb_data.pp_mbadrc->enable = acdb_data.mbadrc_block.\
+					parameters.mbadrc_enable;
+	acdb_data.pp_mbadrc->num_bands =
+				acdb_data.mbadrc_block.\
+					parameters.mbadrc_num_bands;
+	acdb_data.pp_mbadrc->down_samp_level =
+				acdb_data.mbadrc_block.parameters.\
+					mbadrc_down_sample_level;
+	acdb_data.pp_mbadrc->adrc_delay =
+				acdb_data.mbadrc_block.parameters.\
+					mbadrc_delay;
+
+	if (acdb_data.mbadrc_block.parameters.mbadrc_num_bands > 1)
+		acdb_data.pp_mbadrc->ext_buf_size = (97 * 2) +
+			(33 * 2 * (acdb_data.mbadrc_block.parameters.\
+					mbadrc_num_bands - 2));
+
+	acdb_data.pp_mbadrc->ext_partition = 0;
+	acdb_data.pp_mbadrc->ext_buf_lsw = (u16)(mbadrc_phys_addr\
+						 & 0xFFFF);
+	acdb_data.pp_mbadrc->ext_buf_msw = (u16)((mbadrc_phys_addr\
+						 & 0xFFFF0000) >> 16);
+	memcpy(acdb_data.pp_mbadrc->adrc_band, acdb_data.mbadrc_block.\
+					band_config,
+		sizeof(struct mbadrc_band_config_type) *
+		acdb_data.mbadrc_block.parameters.mbadrc_num_bands);
+	return 0;
+}
+
+static struct acdb_calib_gain_rx *get_audpp_cal_gain(void)
+{
+	struct header *prs_hdr;
+	u32 index = 0;
+
+	while (index < acdb_data.acdb_result.used_bytes) {
+		prs_hdr = (struct header *)(acdb_data.virt_addr + index);
+		if (prs_hdr->dbor_signature == DBOR_SIGNATURE) {
+			if (prs_hdr->abid == ABID_AUDIO_CALIBRATION_GAIN_RX) {
+				if (prs_hdr->iid ==
+					IID_AUDIO_CALIBRATION_GAIN_RX) {
+					MM_DBG("Got audpp_calib_gain_rx"
+					" block\n");
+					return (struct acdb_calib_gain_rx *)
+						(acdb_data.virt_addr + index
+						+ sizeof(struct header));
+				}
+			} else {
+				index += prs_hdr->data_len +
+					sizeof(struct header);
+			}
+		} else {
+			break;
+		}
+	}
+	return NULL;
+}
+
+static s32 acdb_fill_audpp_cal_gain(void)
+{
+	struct acdb_calib_gain_rx *acdb_calib_gain_rx = NULL;
+
+	acdb_calib_gain_rx = get_audpp_cal_gain();
+	if (acdb_calib_gain_rx == NULL) {
+		MM_ERR("unable to find  audpp"
+			" calibration gain block returning\n");
+		return -1;
+	}
+	MM_DBG("Calibration value"
+		" for calib_gain_rx %d\n", acdb_calib_gain_rx->audppcalgain);
+	memset(acdb_data.calib_gain_rx, 0, sizeof(*acdb_data.calib_gain_rx));
+
+	acdb_data.calib_gain_rx->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS;
+	acdb_data.calib_gain_rx->common.stream = AUDPP_CMD_COPP_STREAM;
+	acdb_data.calib_gain_rx->common.stream_id = 0;
+	acdb_data.calib_gain_rx->common.obj_cfg = AUDPP_CMD_OBJ0_UPDATE;
+	acdb_data.calib_gain_rx->common.command_type = 0;
+
+	acdb_data.calib_gain_rx->audppcalgain =
+				acdb_calib_gain_rx->audppcalgain;
+	return 0;
+}
+
+static void extract_pbe_block(struct header *prs_hdr, u32 *index)
+{
+	if (prs_hdr->iid == IID_AUDIO_PBE_RX_ENABLE_FLAG) {
+		MM_DBG("Got IID = IID_AUDIO_PBE_RX_ENABLE\n");
+		acdb_data.pbe_enable_flag = (u16 *)(acdb_data.virt_addr +
+							*index +
+							sizeof(struct header));
+		*index += prs_hdr->data_len + sizeof(struct header);
+	} else if (prs_hdr->iid == IID_PBE_CONFIG_PARAMETERS) {
+		MM_DBG("Got IID == IID_PBE_CONFIG_PARAMETERS\n");
+		acdb_data.pbe_blk = (struct acdb_pbe_block *)
+					(acdb_data.virt_addr + *index
+					+ sizeof(struct header));
+		*index += prs_hdr->data_len + sizeof(struct header);
+	}
+}
+
+static s32 get_audpp_pbe_block(void)
+{
+	struct header *prs_hdr;
+	u32 index = 0;
+	s32 result = -1;
+
+	while (index < acdb_data.acdb_result.used_bytes) {
+		prs_hdr = (struct header *)(acdb_data.virt_addr + index);
+
+		if (prs_hdr->dbor_signature == DBOR_SIGNATURE) {
+			if (prs_hdr->abid == ABID_AUDIO_PBE_RX) {
+				if ((prs_hdr->iid == IID_PBE_CONFIG_PARAMETERS)
+					|| (prs_hdr->iid ==
+						IID_AUDIO_PBE_RX_ENABLE_FLAG)) {
+					extract_pbe_block(prs_hdr, &index);
+					result = 0;
+				}
+			} else {
+				index += prs_hdr->data_len +
+					sizeof(struct header);
+			}
+		} else {
+			break;
+		}
+	}
+	return result;
+}
+
+static s32 acdb_fill_audpp_pbe(void)
+{
+	s32 result = -1;
+
+	result = get_audpp_pbe_block();
+	if (IS_ERR_VALUE(result))
+		return result;
+	memset(acdb_data.pbe_block, 0, sizeof(*acdb_data.pbe_block));
+
+	acdb_data.pbe_block->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS;
+	acdb_data.pbe_block->common.stream = AUDPP_CMD_COPP_STREAM;
+	acdb_data.pbe_block->common.stream_id = 0;
+	acdb_data.pbe_block->common.obj_cfg = AUDPP_CMD_OBJ0_UPDATE;
+	acdb_data.pbe_block->common.command_type = 0;
+	acdb_data.pbe_block->pbe_enable = *acdb_data.pbe_enable_flag;
+
+	acdb_data.pbe_block->realbassmix = acdb_data.pbe_blk->realbassmix;
+	acdb_data.pbe_block->basscolorcontrol =
+					acdb_data.pbe_blk->basscolorcontrol;
+	acdb_data.pbe_block->mainchaindelay = acdb_data.pbe_blk->mainchaindelay;
+	acdb_data.pbe_block->xoverfltorder = acdb_data.pbe_blk->xoverfltorder;
+	acdb_data.pbe_block->bandpassfltorder =
+					acdb_data.pbe_blk->bandpassfltorder;
+	acdb_data.pbe_block->adrcdelay = acdb_data.pbe_blk->adrcdelay;
+	acdb_data.pbe_block->downsamplelevel =
+					acdb_data.pbe_blk->downsamplelevel;
+	acdb_data.pbe_block->comprmstav = acdb_data.pbe_blk->comprmstav;
+	acdb_data.pbe_block->expthreshold = acdb_data.pbe_blk->expthreshold;
+	acdb_data.pbe_block->expslope = acdb_data.pbe_blk->expslope;
+	acdb_data.pbe_block->compthreshold = acdb_data.pbe_blk->compthreshold;
+	acdb_data.pbe_block->compslope = acdb_data.pbe_blk->compslope;
+	acdb_data.pbe_block->cpmpattack_lsw = acdb_data.pbe_blk->cpmpattack_lsw;
+	acdb_data.pbe_block->compattack_msw = acdb_data.pbe_blk->compattack_msw;
+	acdb_data.pbe_block->comprelease_lsw =
+					acdb_data.pbe_blk->comprelease_lsw;
+	acdb_data.pbe_block->comprelease_msw =
+					acdb_data.pbe_blk->comprelease_msw;
+	acdb_data.pbe_block->compmakeupgain = acdb_data.pbe_blk->compmakeupgain;
+	acdb_data.pbe_block->baselimthreshold =
+					acdb_data.pbe_blk->baselimthreshold;
+	acdb_data.pbe_block->highlimthreshold =
+					acdb_data.pbe_blk->highlimthreshold;
+	acdb_data.pbe_block->basslimmakeupgain =
+					acdb_data.pbe_blk->basslimmakeupgain;
+	acdb_data.pbe_block->highlimmakeupgain =
+					acdb_data.pbe_blk->highlimmakeupgain;
+	acdb_data.pbe_block->limbassgrc = acdb_data.pbe_blk->limbassgrc;
+	acdb_data.pbe_block->limhighgrc = acdb_data.pbe_blk->limhighgrc;
+	acdb_data.pbe_block->limdelay = acdb_data.pbe_blk->limdelay;
+	memcpy(acdb_data.pbe_block->filter_coeffs,
+		acdb_data.pbe_blk->filter_coeffs, sizeof(u16)*90);
+	acdb_data.pbe_block->extpartition = 0;
+	acdb_data.pbe_block->extbuffsize_lsw = PBE_BUF_SIZE;
+	acdb_data.pbe_block->extbuffsize_msw = 0;
+	acdb_data.pbe_block->extbuffstart_lsw = ((u32)acdb_data.pbe_extbuff
+							& 0xFFFF);
+	acdb_data.pbe_block->extbuffstart_msw = (((u32)acdb_data.pbe_extbuff
+							& 0xFFFF0000) >> 16);
+	return 0;
+}
+
+
+static s32 acdb_calibrate_audpp(void)
+{
+	s32	result = 0;
+
+	result = acdb_fill_audpp_iir();
+	if (!IS_ERR_VALUE(result)) {
+		result = audpp_dsp_set_rx_iir(acdb_data.device_info->dev_id,
+				acdb_data.pp_iir->active_flag,
+					acdb_data.pp_iir, COPP);
+		if (result) {
+			MM_ERR("ACDB=> Failed to send IIR data to postproc\n");
+			result = -EINVAL;
+			goto done;
+		} else
+			MM_DBG("AUDPP is calibrated with IIR parameters"
+					" for COPP ID %d\n",
+						acdb_data.device_info->dev_id);
+	}
+	result = acdb_fill_audpp_mbadrc();
+	if (!IS_ERR_VALUE(result)) {
+		result = audpp_dsp_set_mbadrc(acdb_data.device_info->dev_id,
+					acdb_data.pp_mbadrc->enable,
+					acdb_data.pp_mbadrc, COPP);
+		if (result) {
+			MM_ERR("ACDB=> Failed to send MBADRC data to"
+					" postproc\n");
+			result = -EINVAL;
+			goto done;
+		} else
+			MM_DBG("AUDPP is calibrated with MBADRC parameters"
+					" for COPP ID %d\n",
+					acdb_data.device_info->dev_id);
+	}
+	result = acdb_fill_audpp_cal_gain();
+	if (!(IS_ERR_VALUE(result))) {
+		result = audpp_dsp_set_gain_rx(acdb_data.device_info->dev_id,
+					acdb_data.calib_gain_rx, COPP);
+		if (result) {
+			MM_ERR("ACDB=> Failed to send gain_rx"
+				" data to postproc\n");
+			result = -EINVAL;
+			goto done;
+		} else
+			MM_DBG("AUDPP is calibrated with calib_gain_rx\n");
+	}
+	result = acdb_fill_audpp_pbe();
+	if (!(IS_ERR_VALUE(result))) {
+		result = audpp_dsp_set_pbe(acdb_data.device_info->dev_id,
+					acdb_data.pbe_block->pbe_enable,
+					acdb_data.pbe_block, COPP);
+		if (result) {
+			MM_ERR("ACDB=> Failed to send pbe block"
+				"data to postproc\n");
+			result = -EINVAL;
+			goto done;
+		}
+		MM_DBG("AUDPP is calibarted with PBE\n");
+	}
+done:
+	return result;
+}
+
+static struct acdb_agc_block *get_audpreproc_agc_block(void)
+{
+	struct header *prs_hdr;
+	u32 index = 0;
+
+	while (index < acdb_data.acdb_result.used_bytes) {
+		prs_hdr = (struct header *)(acdb_data.virt_addr + index);
+		if (prs_hdr->dbor_signature == DBOR_SIGNATURE) {
+			if (prs_hdr->abid == ABID_AUDIO_AGC_TX) {
+				if (prs_hdr->iid == IID_AUDIO_AGC_PARAMETERS) {
+					MM_DBG("GOT ABID_AUDIO_AGC_TX\n");
+					return (struct acdb_agc_block *)
+						(acdb_data.virt_addr + index
+						 + sizeof(struct header));
+				}
+			} else {
+				index += prs_hdr->data_len +
+						sizeof(struct header);
+			}
+		} else {
+			break;
+		}
+	}
+	return NULL;
+}
+
+static s32 acdb_fill_audpreproc_agc(void)
+{
+	struct acdb_agc_block	*acdb_agc;
+
+	acdb_agc = get_audpreproc_agc_block();
+	if (!acdb_agc) {
+		MM_DBG("unable to find preproc agc parameters winding up\n");
+		return -1;
+	}
+	memset(acdb_data.preproc_agc, 0, sizeof(*acdb_data.preproc_agc));
+	acdb_data.preproc_agc->cmd_id = AUDPREPROC_CMD_CFG_AGC_PARAMS;
+	acdb_data.preproc_agc->stream_id = acdb_data.preproc_stream_id;
+	/* 0xFE00 to configure all parameters */
+	acdb_data.preproc_agc->tx_agc_param_mask = 0xFFFF;
+
+	if (acdb_agc->enable_status)
+		acdb_data.preproc_agc->tx_agc_enable_flag =
+			AUDPREPROC_CMD_TX_AGC_ENA_FLAG_ENA;
+	else
+		acdb_data.preproc_agc->tx_agc_enable_flag =
+			AUDPREPROC_CMD_TX_AGC_ENA_FLAG_DIS;
+
+	acdb_data.preproc_agc->comp_rlink_static_gain =
+		acdb_agc->comp_rlink_static_gain;
+	acdb_data.preproc_agc->comp_rlink_aig_flag =
+		acdb_agc->comp_rlink_aig_flag;
+	acdb_data.preproc_agc->expander_rlink_th =
+		acdb_agc->exp_rlink_threshold;
+	acdb_data.preproc_agc->expander_rlink_slope =
+		acdb_agc->exp_rlink_slope;
+	acdb_data.preproc_agc->compressor_rlink_th =
+		acdb_agc->comp_rlink_threshold;
+	acdb_data.preproc_agc->compressor_rlink_slope =
+		acdb_agc->comp_rlink_slope;
+
+	/* 0xFFF0 to configure all parameters */
+	acdb_data.preproc_agc->tx_adc_agc_param_mask = 0xFFFF;
+
+	acdb_data.preproc_agc->comp_rlink_aig_attackk =
+		acdb_agc->comp_rlink_aig_attack_k;
+	acdb_data.preproc_agc->comp_rlink_aig_leak_down =
+		acdb_agc->comp_rlink_aig_leak_down;
+	acdb_data.preproc_agc->comp_rlink_aig_leak_up =
+		acdb_agc->comp_rlink_aig_leak_up;
+	acdb_data.preproc_agc->comp_rlink_aig_max =
+		acdb_agc->comp_rlink_aig_max;
+	acdb_data.preproc_agc->comp_rlink_aig_min =
+		acdb_agc->comp_rlink_aig_min;
+	acdb_data.preproc_agc->comp_rlink_aig_releasek =
+		acdb_agc->comp_rlink_aig_release_k;
+	acdb_data.preproc_agc->comp_rlink_aig_leakrate_fast =
+		acdb_agc->comp_rlink_aig_sm_leak_rate_fast;
+	acdb_data.preproc_agc->comp_rlink_aig_leakrate_slow =
+		acdb_agc->comp_rlink_aig_sm_leak_rate_slow;
+	acdb_data.preproc_agc->comp_rlink_attackk_msw =
+		acdb_agc->comp_rlink_attack_k_msw;
+	acdb_data.preproc_agc->comp_rlink_attackk_lsw =
+		acdb_agc->comp_rlink_attack_k_lsw;
+	acdb_data.preproc_agc->comp_rlink_delay =
+		acdb_agc->comp_rlink_delay;
+	acdb_data.preproc_agc->comp_rlink_releasek_msw =
+		acdb_agc->comp_rlink_release_k_msw;
+	acdb_data.preproc_agc->comp_rlink_releasek_lsw =
+		acdb_agc->comp_rlink_release_k_lsw;
+	acdb_data.preproc_agc->comp_rlink_rms_tav =
+		acdb_agc->comp_rlink_rms_trav;
+	return 0;
+}
+
+static struct acdb_iir_block *get_audpreproc_irr_block(void)
+{
+
+	struct header *prs_hdr;
+	u32 index = 0;
+
+	while (index < acdb_data.acdb_result.used_bytes) {
+		prs_hdr = (struct header *)(acdb_data.virt_addr + index);
+
+		if (prs_hdr->dbor_signature == DBOR_SIGNATURE) {
+			if (prs_hdr->abid == ABID_AUDIO_IIR_TX) {
+				if (prs_hdr->iid == IID_AUDIO_IIR_COEFF)
+					return (struct acdb_iir_block *)
+						(acdb_data.virt_addr + index
+						 + sizeof(struct header));
+			} else {
+				index += prs_hdr->data_len +
+						sizeof(struct header);
+			}
+		} else {
+			break;
+		}
+	}
+	return NULL;
+}
+
+
+static s32 acdb_fill_audpreproc_iir(void)
+{
+	struct acdb_iir_block	*acdb_iir;
+
+
+	acdb_iir =  get_audpreproc_irr_block();
+	if (!acdb_iir) {
+		MM_DBG("unable to find preproc iir parameters winding up\n");
+		return -1;
+	}
+	memset(acdb_data.preproc_iir, 0, sizeof(*acdb_data.preproc_iir));
+
+	acdb_data.preproc_iir->cmd_id =
+		AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS;
+	acdb_data.preproc_iir->stream_id = acdb_data.preproc_stream_id;
+	acdb_data.preproc_iir->active_flag = acdb_iir->enable_flag;
+	acdb_data.preproc_iir->num_bands = acdb_iir->stage_count;
+
+	acdb_data.preproc_iir->numerator_coeff_b0_filter0_lsw =
+		acdb_iir->stages[0].b0_lo;
+	acdb_data.preproc_iir->numerator_coeff_b0_filter0_msw =
+		acdb_iir->stages[0].b0_hi;
+	acdb_data.preproc_iir->numerator_coeff_b1_filter0_lsw =
+		acdb_iir->stages[0].b1_lo;
+	acdb_data.preproc_iir->numerator_coeff_b1_filter0_msw =
+		acdb_iir->stages[0].b1_hi;
+	acdb_data.preproc_iir->numerator_coeff_b2_filter0_lsw =
+		acdb_iir->stages[0].b2_lo;
+	acdb_data.preproc_iir->numerator_coeff_b2_filter0_msw =
+		acdb_iir->stages[0].b2_hi;
+
+	acdb_data.preproc_iir->numerator_coeff_b0_filter1_lsw =
+		acdb_iir->stages[1].b0_lo;
+	acdb_data.preproc_iir->numerator_coeff_b0_filter1_msw =
+		acdb_iir->stages[1].b0_hi;
+	acdb_data.preproc_iir->numerator_coeff_b1_filter1_lsw =
+		acdb_iir->stages[1].b1_lo;
+	acdb_data.preproc_iir->numerator_coeff_b1_filter1_msw =
+		acdb_iir->stages[1].b1_hi;
+	acdb_data.preproc_iir->numerator_coeff_b2_filter1_lsw =
+		acdb_iir->stages[1].b2_lo;
+	acdb_data.preproc_iir->numerator_coeff_b2_filter1_msw =
+		acdb_iir->stages[1].b2_hi;
+
+	acdb_data.preproc_iir->numerator_coeff_b0_filter2_lsw =
+		acdb_iir->stages[2].b0_lo;
+	acdb_data.preproc_iir->numerator_coeff_b0_filter2_msw =
+		acdb_iir->stages[2].b0_hi;
+	acdb_data.preproc_iir->numerator_coeff_b1_filter2_lsw =
+		acdb_iir->stages[2].b1_lo;
+	acdb_data.preproc_iir->numerator_coeff_b1_filter2_msw =
+		acdb_iir->stages[2].b1_hi;
+	acdb_data.preproc_iir->numerator_coeff_b2_filter2_lsw =
+		acdb_iir->stages[2].b2_lo;
+	acdb_data.preproc_iir->numerator_coeff_b2_filter2_msw =
+		acdb_iir->stages[2].b2_hi;
+
+	acdb_data.preproc_iir->numerator_coeff_b0_filter3_lsw =
+		acdb_iir->stages[3].b0_lo;
+	acdb_data.preproc_iir->numerator_coeff_b0_filter3_msw =
+		acdb_iir->stages[3].b0_hi;
+	acdb_data.preproc_iir->numerator_coeff_b1_filter3_lsw =
+		acdb_iir->stages[3].b1_lo;
+	acdb_data.preproc_iir->numerator_coeff_b1_filter3_msw =
+		acdb_iir->stages[3].b1_hi;
+	acdb_data.preproc_iir->numerator_coeff_b2_filter3_lsw =
+		acdb_iir->stages[3].b2_lo;
+	acdb_data.preproc_iir->numerator_coeff_b2_filter3_msw =
+		acdb_iir->stages[3].b2_hi;
+
+	acdb_data.preproc_iir->denominator_coeff_a0_filter0_lsw =
+		acdb_iir->stages_a[0].a1_lo;
+	acdb_data.preproc_iir->denominator_coeff_a0_filter0_msw =
+		acdb_iir->stages_a[0].a1_hi;
+	acdb_data.preproc_iir->denominator_coeff_a1_filter0_lsw =
+		acdb_iir->stages_a[0].a2_lo;
+	acdb_data.preproc_iir->denominator_coeff_a1_filter0_msw =
+		acdb_iir->stages_a[0].a2_hi;
+
+	acdb_data.preproc_iir->denominator_coeff_a0_filter1_lsw =
+		acdb_iir->stages_a[1].a1_lo;
+	acdb_data.preproc_iir->denominator_coeff_a0_filter1_msw =
+		acdb_iir->stages_a[1].a1_hi;
+	acdb_data.preproc_iir->denominator_coeff_a1_filter1_lsw =
+		acdb_iir->stages_a[1].a2_lo;
+	acdb_data.preproc_iir->denominator_coeff_a1_filter1_msw =
+		acdb_iir->stages_a[1].a2_hi;
+
+	acdb_data.preproc_iir->denominator_coeff_a0_filter2_lsw =
+		acdb_iir->stages_a[2].a1_lo;
+	acdb_data.preproc_iir->denominator_coeff_a0_filter2_msw =
+		acdb_iir->stages_a[2].a1_hi;
+	acdb_data.preproc_iir->denominator_coeff_a1_filter2_lsw =
+		acdb_iir->stages_a[2].a2_lo;
+	acdb_data.preproc_iir->denominator_coeff_a1_filter2_msw =
+		acdb_iir->stages_a[2].a2_hi;
+
+	acdb_data.preproc_iir->denominator_coeff_a0_filter3_lsw =
+		acdb_iir->stages_a[3].a1_lo;
+	acdb_data.preproc_iir->denominator_coeff_a0_filter3_msw =
+		acdb_iir->stages_a[3].a1_hi;
+	acdb_data.preproc_iir->denominator_coeff_a1_filter3_lsw =
+		acdb_iir->stages_a[3].a2_lo;
+	acdb_data.preproc_iir->denominator_coeff_a1_filter3_msw =
+		acdb_iir->stages_a[3].a2_hi;
+
+	acdb_data.preproc_iir->shift_factor_filter0 =
+		acdb_iir->shift_factor[0];
+	acdb_data.preproc_iir->shift_factor_filter1 =
+		acdb_iir->shift_factor[1];
+	acdb_data.preproc_iir->shift_factor_filter2 =
+		acdb_iir->shift_factor[2];
+	acdb_data.preproc_iir->shift_factor_filter3 =
+		acdb_iir->shift_factor[3];
+
+	acdb_data.preproc_iir->pan_of_filter0 =
+		acdb_iir->pan[0];
+	acdb_data.preproc_iir->pan_of_filter1 =
+		acdb_iir->pan[1];
+	acdb_data.preproc_iir->pan_of_filter2 =
+		acdb_iir->pan[2];
+	acdb_data.preproc_iir->pan_of_filter3 =
+		acdb_iir->pan[3];
+	return 0;
+}
+
+static struct acdb_calib_gain_tx *get_audpreproc_cal_gain(void)
+{
+	struct header *prs_hdr;
+	u32 index = 0;
+
+	while (index < acdb_data.acdb_result.used_bytes) {
+		prs_hdr = (struct header *)(acdb_data.virt_addr + index);
+		if (prs_hdr->dbor_signature == DBOR_SIGNATURE) {
+			if (prs_hdr->abid == ABID_AUDIO_CALIBRATION_GAIN_TX) {
+				if (prs_hdr->iid ==
+					IID_AUDIO_CALIBRATION_GAIN_TX) {
+					MM_DBG("Got audpreproc_calib_gain_tx"
+					" block\n");
+					return (struct acdb_calib_gain_tx *)
+						(acdb_data.virt_addr + index
+						+ sizeof(struct header));
+				}
+			} else {
+				index += prs_hdr->data_len +
+					sizeof(struct header);
+			}
+		} else {
+			break;
+		}
+	}
+	return NULL;
+}
+
+static s32 acdb_fill_audpreproc_cal_gain(void)
+{
+	struct acdb_calib_gain_tx *acdb_calib_gain_tx = NULL;
+
+	acdb_calib_gain_tx = get_audpreproc_cal_gain();
+	if (acdb_calib_gain_tx == NULL) {
+		MM_ERR("unable to find  audpreproc"
+			" calibration block returning\n");
+		return -1;
+	}
+	MM_DBG("Calibration value"
+		" for calib_gain_tx %d\n", acdb_calib_gain_tx->audprecalgain);
+	memset(acdb_data.calib_gain_tx, 0, sizeof(*acdb_data.calib_gain_tx));
+
+	acdb_data.calib_gain_tx->cmd_id =
+					AUDPREPROC_CMD_CFG_CAL_GAIN_PARAMS;
+	acdb_data.calib_gain_tx->stream_id = acdb_data.preproc_stream_id;
+	acdb_data.calib_gain_tx->audprecalgain =
+					acdb_calib_gain_tx->audprecalgain;
+	return 0;
+}
+
+static struct acdb_rmc_block *get_rmc_blk(void)
+{
+	struct header *prs_hdr;
+	u32 index = 0;
+
+	while (index < acdb_data.acdb_result.used_bytes) {
+		prs_hdr = (struct header *)(acdb_data.virt_addr + index);
+		if (prs_hdr->dbor_signature == DBOR_SIGNATURE) {
+			if (prs_hdr->abid == ABID_AUDIO_RMC_TX) {
+				if (prs_hdr->iid ==
+					IID_AUDIO_RMC_PARAM) {
+					MM_DBG("Got afe_rmc block\n");
+					return (struct acdb_rmc_block *)
+						(acdb_data.virt_addr + index
+						+ sizeof(struct header));
+				}
+			} else {
+				index += prs_hdr->data_len +
+					sizeof(struct header);
+			}
+		} else {
+			break;
+		}
+	}
+	return NULL;
+}
+
+struct acdb_fluence_block *get_audpp_fluence_block(void)
+{
+	struct header *prs_hdr;
+	u32 index = 0;
+
+	while (index < acdb_data.acdb_result.used_bytes) {
+		prs_hdr = (struct header *)(acdb_data.virt_addr + index);
+
+		if (prs_hdr->dbor_signature == DBOR_SIGNATURE) {
+			if (prs_hdr->abid == ABID_AUDIO_FLUENCE_TX) {
+				if (prs_hdr->iid == IID_AUDIO_FLUENCE_TX) {
+					MM_DBG("got fluence block\n");
+					return (struct acdb_fluence_block *)
+						(acdb_data.virt_addr + index
+						+ sizeof(struct header));
+				}
+			} else {
+				index += prs_hdr->data_len +
+					sizeof(struct header);
+			}
+		} else {
+			break;
+		}
+	}
+	return NULL;
+}
+
+static s32 acdb_fill_audpreproc_fluence(void)
+{
+	struct acdb_fluence_block *fluence_block = NULL;
+	fluence_block = get_audpp_fluence_block();
+	if (!fluence_block) {
+		MM_ERR("error in finding fluence block\n");
+		return -EPERM;
+	}
+	memset(&acdb_data.preproc_lvnv, 0, sizeof(
+				struct audpreproc_cmd_cfg_lvnv_param));
+	memcpy(acdb_data.fluence_extbuff_virt,
+			&fluence_block->cs_tuningMode,
+			(sizeof(struct acdb_fluence_block) -
+					sizeof(fluence_block->csmode)));
+	acdb_data.preproc_lvnv.cmd_id = AUDPREPROC_CMD_CFG_LVNV_PARMS;
+	acdb_data.preproc_lvnv.stream_id = acdb_data.preproc_stream_id;
+	acdb_data.preproc_lvnv.cs_mode = fluence_block->csmode;
+	acdb_data.preproc_lvnv.lvnv_ext_buf_size = FLUENCE_BUF_SIZE;
+	acdb_data.preproc_lvnv.lvnv_ext_buf_start_lsw =\
+				((u32)(acdb_data.fluence_extbuff)\
+						& 0x0000FFFF);
+	acdb_data.preproc_lvnv.lvnv_ext_buf_start_msw =\
+				(((u32)acdb_data.fluence_extbuff\
+					& 0xFFFF0000) >> 16);
+	return 0;
+}
+
+s32 acdb_calibrate_audpreproc(void)
+{
+	s32	result = 0;
+	struct acdb_rmc_block *acdb_rmc = NULL;
+
+	result = acdb_fill_audpreproc_agc();
+	if (!IS_ERR_VALUE(result)) {
+		result = audpreproc_dsp_set_agc(acdb_data.preproc_agc, sizeof(
+					struct audpreproc_cmd_cfg_agc_params));
+		if (result) {
+			MM_ERR("ACDB=> Failed to send AGC data to preproc)\n");
+			result = -EINVAL;
+			goto done;
+		} else
+			MM_DBG("AUDPREC is calibrated with AGC parameters"
+				" for COPP ID %d and AUDREC session %d\n",
+					acdb_data.device_info->dev_id,
+					acdb_data.preproc_stream_id);
+	}
+	result = acdb_fill_audpreproc_iir();
+	if (!IS_ERR_VALUE(result)) {
+		result = audpreproc_dsp_set_iir(acdb_data.preproc_iir,
+				sizeof(struct\
+				audpreproc_cmd_cfg_iir_tuning_filter_params));
+		if (result) {
+			MM_ERR("ACDB=> Failed to send IIR data to preproc\n");
+			result = -EINVAL;
+			goto done;
+		} else
+			MM_DBG("audpreproc is calibrated with iir parameters"
+			" for COPP ID %d and AUREC session %d\n",
+					acdb_data.device_info->dev_id,
+					acdb_data.preproc_stream_id);
+	}
+	result = acdb_fill_audpreproc_cal_gain();
+	if (!(IS_ERR_VALUE(result))) {
+		result = audpreproc_dsp_set_gain_tx(acdb_data.calib_gain_tx,
+				sizeof(struct audpreproc_cmd_cfg_cal_gain));
+		if (result) {
+			MM_ERR("ACDB=> Failed to send calib_gain_tx"
+				" data to preproc\n");
+			result = -EINVAL;
+			goto done;
+		} else
+			MM_DBG("AUDPREPROC is calibrated"
+				" with calib_gain_tx\n");
+	}
+	acdb_rmc = get_rmc_blk();
+	if (acdb_rmc != NULL) {
+		result = afe_config_rmc_block(acdb_rmc);
+		if (result) {
+			MM_ERR("ACDB=> Failed to send rmc"
+				" data to afe\n");
+			result = -EINVAL;
+			goto done;
+		} else
+			MM_DBG("AFE is calibrated with rmc params\n");
+	} else
+		MM_DBG("RMC block was not found\n");
+	if (!acdb_data.fleuce_feature_status[acdb_data.preproc_stream_id]) {
+		result = acdb_fill_audpreproc_fluence();
+		if (!(IS_ERR_VALUE(result))) {
+			result = audpreproc_dsp_set_lvnv(
+					&acdb_data.preproc_lvnv,
+					sizeof(struct\
+					audpreproc_cmd_cfg_lvnv_param));
+			if (result) {
+				MM_ERR("ACDB=> Failed to send lvnv "
+						"data to preproc\n");
+				result = -EINVAL;
+				goto done;
+			} else
+				MM_DBG("AUDPREPROC is calibrated"
+						" with lvnv parameters\n");
+		} else
+			MM_ERR("fluence block is not found\n");
+	} else
+		MM_DBG("fluence block override\n");
+done:
+	return result;
+}
+
+static s32 acdb_send_calibration(void)
+{
+	s32 result = 0;
+
+	if ((acdb_data.device_info->dev_type & RX_DEVICE) == 1) {
+		result = acdb_calibrate_audpp();
+		if (result)
+			goto done;
+	} else if ((acdb_data.device_info->dev_type & TX_DEVICE) == 2) {
+		result = acdb_calibrate_audpreproc();
+		if (result)
+			goto done;
+		if (acdb_data.preproc_stream_id == 0)
+			acdb_data.audrec_applied |= AUDREC0_READY;
+		else if (acdb_data.preproc_stream_id == 1)
+			acdb_data.audrec_applied |= AUDREC1_READY;
+		else if (acdb_data.preproc_stream_id == 2)
+			acdb_data.audrec_applied |= AUDREC2_READY;
+		MM_DBG("acdb_data.audrec_applied = %x\n",
+					acdb_data.audrec_applied);
+	}
+done:
+	return result;
+}
+
+static u8 check_tx_acdb_values_cached(void)
+{
+	u8 stream_id  = acdb_data.preproc_stream_id;
+
+	if ((acdb_data.device_info->dev_id ==
+		acdb_cache_tx[stream_id].device_info.dev_id) &&
+		(acdb_data.device_info->sample_rate ==
+		acdb_cache_tx[stream_id].device_info.sample_rate) &&
+		(acdb_data.device_info->acdb_id ==
+		acdb_cache_tx[stream_id].device_info.acdb_id) &&
+		(acdb_cache_tx[stream_id].node_status ==
+						ACDB_VALUES_FILLED))
+		return 0;
+	else
+		return 1;
+}
+
+static void handle_tx_device_ready_callback(void)
+{
+	u8 i = 0;
+	u8 ret = 0;
+	u8 acdb_value_apply = 0;
+	u8 result = 0;
+	u8 stream_id = acdb_data.preproc_stream_id;
+
+	if (acdb_data.multiple_sessions) {
+		for (i = 0; i < MAX_AUDREC_SESSIONS; i++) {
+			/*check is to exclude copying acdb values in the
+			current node pointed by acdb_data structure*/
+			if (acdb_cache_tx[i].phys_addr_acdb_values !=
+							acdb_data.phys_addr) {
+				ret = check_device_info_already_present(\
+							*acdb_data.device_info,
+							&acdb_cache_tx[i]);
+				if (ret) {
+					memcpy((char *)acdb_cache_tx[i].\
+						virt_addr_acdb_values,
+						(char *)acdb_data.virt_addr,
+								ACDB_BUF_SIZE);
+					acdb_cache_tx[i].node_status =
+							ACDB_VALUES_FILLED;
+				}
+			}
+		}
+		acdb_data.multiple_sessions = 0;
+	}
+	/*check wheather AUDREC enabled before device call backs*/
+	if ((acdb_data.acdb_state & AUDREC0_READY) &&
+			!(acdb_data.audrec_applied & AUDREC0_READY)) {
+		MM_DBG("AUDREC0 already enabled apply acdb values\n");
+		acdb_value_apply |= AUDREC0_READY;
+	} else if ((acdb_data.acdb_state & AUDREC1_READY) &&
+			!(acdb_data.audrec_applied & AUDREC1_READY)) {
+		MM_DBG("AUDREC1 already enabled apply acdb values\n");
+		acdb_value_apply |= AUDREC1_READY;
+	} else if ((acdb_data.acdb_state & AUDREC2_READY) &&
+			!(acdb_data.audrec_applied & AUDREC2_READY)) {
+		MM_DBG("AUDREC2 already enabled apply acdb values\n");
+		acdb_value_apply |= AUDREC2_READY;
+	}
+	if (acdb_value_apply) {
+		if (session_info[stream_id].sampling_freq)
+			acdb_data.device_info->sample_rate =
+					session_info[stream_id].sampling_freq;
+		result = check_tx_acdb_values_cached();
+		if (result) {
+			result = acdb_get_calibration();
+			if (result < 0) {
+				MM_ERR("Not able to get calibration"
+						" data continue\n");
+				return;
+			}
+		}
+		acdb_cache_tx[stream_id].node_status = ACDB_VALUES_FILLED;
+		acdb_send_calibration();
+	}
+}
+
+static struct acdb_cache_node *get_acdb_values_from_cache_tx(u32 stream_id)
+{
+	MM_DBG("searching node with stream_id %d\n", stream_id);
+	if ((acdb_cache_tx[stream_id].stream_id == stream_id) &&
+			(acdb_cache_tx[stream_id].node_status ==
+					ACDB_VALUES_NOT_FILLED)) {
+			return &acdb_cache_tx[stream_id];
+	}
+	MM_DBG("Error! in finding node\n");
+	return NULL;
+}
+
+static void update_acdb_data_struct(struct acdb_cache_node *cur_node)
+{
+	if (cur_node) {
+		acdb_data.device_info = &cur_node->device_info;
+		acdb_data.virt_addr = cur_node->virt_addr_acdb_values;
+		acdb_data.phys_addr = cur_node->phys_addr_acdb_values;
+	} else
+		MM_ERR("error in curent node\n");
+}
+
+static void send_acdb_values_for_active_devices(void)
+{
+	u32 i = 0;
+	for (i = 0; i < MAX_COPP_NODE_SUPPORTED; i++) {
+		if (acdb_cache_rx[i].node_status ==
+					ACDB_VALUES_FILLED) {
+			update_acdb_data_struct(&acdb_cache_rx[i]);
+			if (acdb_data.acdb_state & CAL_DATA_READY)
+				acdb_send_calibration();
+		}
+	}
+}
+
+static s32 initialize_rpc(void)
+{
+	s32 result = 0;
+
+	result = daldevice_attach(DALDEVICEID_ACDB, ACDB_PORT_NAME,
+			ACDB_CPU, &acdb_data.handle);
+
+	if (result) {
+		MM_ERR("ACDB=> Device Attach failed\n");
+		result = -ENODEV;
+		goto done;
+	}
+done:
+	return result;
+}
+
+static u32 allocate_memory_acdb_cache_tx(void)
+{
+	u32 result = 0;
+	u32 i = 0;
+	u32 err = 0;
+	/*initialize local cache */
+	for (i = 0; i < MAX_AUDREC_SESSIONS; i++) {
+		acdb_cache_tx[i].phys_addr_acdb_values =
+					pmem_kalloc(ACDB_BUF_SIZE,
+						(PMEM_MEMTYPE_EBI1
+						| PMEM_ALIGNMENT_4K));
+
+		if (IS_ERR((void *)acdb_cache_tx[i].phys_addr_acdb_values)) {
+			MM_ERR("ACDB=> Cannot allocate physical memory\n");
+			result = -ENOMEM;
+			goto error;
+		}
+		acdb_cache_tx[i].virt_addr_acdb_values =
+					ioremap(
+					acdb_cache_tx[i].phys_addr_acdb_values,
+						ACDB_BUF_SIZE);
+		if (acdb_cache_tx[i].virt_addr_acdb_values == NULL) {
+			MM_ERR("ACDB=> Could not map physical address\n");
+			result = -ENOMEM;
+			pmem_kfree(acdb_cache_tx[i].phys_addr_acdb_values);
+			goto error;
+		}
+		memset(acdb_cache_tx[i].virt_addr_acdb_values, 0,
+						ACDB_BUF_SIZE);
+	}
+	return result;
+error:
+	for (err = 0; err < i; err++) {
+		iounmap(acdb_cache_tx[i].virt_addr_acdb_values);
+		pmem_kfree(acdb_cache_tx[i].phys_addr_acdb_values);
+
+	}
+	return result;
+}
+
+static u32 allocate_memory_acdb_cache_rx(void)
+{
+	u32 result = 0;
+	u32 i = 0;
+	u32 err = 0;
+
+	/*initialize local cache */
+	for (i = 0; i < MAX_COPP_NODE_SUPPORTED; i++) {
+		acdb_cache_rx[i].phys_addr_acdb_values =
+					pmem_kalloc(ACDB_BUF_SIZE,
+						(PMEM_MEMTYPE_EBI1
+						| PMEM_ALIGNMENT_4K));
+
+		if (IS_ERR((void *)acdb_cache_rx[i].phys_addr_acdb_values)) {
+			MM_ERR("ACDB=> Can not allocate physical memory\n");
+			result = -ENOMEM;
+			goto error;
+		}
+		acdb_cache_rx[i].virt_addr_acdb_values =
+					ioremap(
+					acdb_cache_rx[i].phys_addr_acdb_values,
+						ACDB_BUF_SIZE);
+		if (acdb_cache_rx[i].virt_addr_acdb_values == NULL) {
+			MM_ERR("ACDB=> Could not map physical address\n");
+			result = -ENOMEM;
+			pmem_kfree(acdb_cache_rx[i].phys_addr_acdb_values);
+			goto error;
+		}
+		memset(acdb_cache_rx[i].virt_addr_acdb_values, 0,
+						ACDB_BUF_SIZE);
+	}
+	return result;
+error:
+	for (err = 0; err < i; err++) {
+		iounmap(acdb_cache_rx[i].virt_addr_acdb_values);
+		pmem_kfree(acdb_cache_rx[i].phys_addr_acdb_values);
+
+	}
+	return result;
+}
+
+static u32 allocate_memory_acdb_get_blk(void)
+{
+	u32 result = 0;
+	acdb_data.get_blk_paddr = pmem_kalloc(ACDB_BUF_SIZE,
+						(PMEM_MEMTYPE_EBI1
+						| PMEM_ALIGNMENT_4K));
+	if (IS_ERR((void *)acdb_data.get_blk_paddr)) {
+		MM_ERR("ACDB=> Cannot allocate physical memory\n");
+		result = -ENOMEM;
+		goto error;
+	}
+	acdb_data.get_blk_kvaddr = ioremap(acdb_data.get_blk_paddr,
+					ACDB_BUF_SIZE);
+	if (acdb_data.get_blk_kvaddr == NULL) {
+		MM_ERR("ACDB=> Could not map physical address\n");
+		result = -ENOMEM;
+		pmem_kfree(acdb_data.get_blk_paddr);
+		goto error;
+	}
+	memset(acdb_data.get_blk_kvaddr, 0, ACDB_BUF_SIZE);
+error:
+	return result;
+}
+
+static void free_memory_acdb_cache_rx(void)
+{
+	u32 i = 0;
+
+	for (i = 0; i < MAX_COPP_NODE_SUPPORTED; i++) {
+		iounmap(acdb_cache_rx[i].virt_addr_acdb_values);
+		pmem_kfree(acdb_cache_rx[i].phys_addr_acdb_values);
+	}
+}
+
+static void free_memory_acdb_cache_tx(void)
+{
+	u32 i = 0;
+
+	for (i = 0; i < MAX_AUDREC_SESSIONS; i++) {
+		iounmap(acdb_cache_tx[i].virt_addr_acdb_values);
+		pmem_kfree(acdb_cache_tx[i].phys_addr_acdb_values);
+	}
+}
+
+static void free_memory_acdb_get_blk(void)
+{
+	iounmap(acdb_data.get_blk_kvaddr);
+	pmem_kfree(acdb_data.get_blk_paddr);
+}
+
+static s32 initialize_memory(void)
+{
+	s32 result = 0;
+
+	result = allocate_memory_acdb_get_blk();
+	if (result < 0) {
+		MM_ERR("memory allocation for get blk failed\n");
+		goto done;
+	}
+
+	result = allocate_memory_acdb_cache_rx();
+	if (result < 0) {
+		MM_ERR("memory allocation for rx cache is failed\n");
+		free_memory_acdb_get_blk();
+		goto done;
+	}
+	result = allocate_memory_acdb_cache_tx();
+	if (result < 0) {
+		MM_ERR("memory allocation for tx cache is failed\n");
+		free_memory_acdb_get_blk();
+		free_memory_acdb_cache_rx();
+		goto done;
+	}
+	acdb_data.pp_iir = kmalloc(sizeof(*acdb_data.pp_iir),
+		GFP_KERNEL);
+	if (acdb_data.pp_iir == NULL) {
+		MM_ERR("ACDB=> Could not allocate postproc iir memory\n");
+		free_memory_acdb_get_blk();
+		free_memory_acdb_cache_rx();
+		free_memory_acdb_cache_tx();
+		result = -ENOMEM;
+		goto done;
+	}
+
+	acdb_data.pp_mbadrc = kmalloc(sizeof(*acdb_data.pp_mbadrc), GFP_KERNEL);
+	if (acdb_data.pp_mbadrc == NULL) {
+		MM_ERR("ACDB=> Could not allocate postproc mbadrc memory\n");
+		free_memory_acdb_get_blk();
+		free_memory_acdb_cache_rx();
+		free_memory_acdb_cache_tx();
+		kfree(acdb_data.pp_iir);
+		result = -ENOMEM;
+		goto done;
+	}
+	acdb_data.calib_gain_rx = kmalloc(sizeof(*acdb_data.calib_gain_rx),
+							GFP_KERNEL);
+	if (acdb_data.calib_gain_rx == NULL) {
+		MM_ERR("ACDB=> Could not allocate"
+			" postproc calib_gain_rx memory\n");
+		free_memory_acdb_get_blk();
+		free_memory_acdb_cache_rx();
+		free_memory_acdb_cache_tx();
+		kfree(acdb_data.pp_iir);
+		kfree(acdb_data.pp_mbadrc);
+		result = -ENOMEM;
+		goto done;
+	}
+
+	acdb_data.preproc_agc = kmalloc(sizeof(*acdb_data.preproc_agc),
+							GFP_KERNEL);
+	if (acdb_data.preproc_agc == NULL) {
+		MM_ERR("ACDB=> Could not allocate preproc agc memory\n");
+		free_memory_acdb_get_blk();
+		free_memory_acdb_cache_rx();
+		free_memory_acdb_cache_tx();
+		kfree(acdb_data.pp_iir);
+		kfree(acdb_data.pp_mbadrc);
+		kfree(acdb_data.calib_gain_rx);
+		result = -ENOMEM;
+		goto done;
+	}
+
+	acdb_data.preproc_iir = kmalloc(sizeof(*acdb_data.preproc_iir),
+							GFP_KERNEL);
+	if (acdb_data.preproc_iir == NULL) {
+		MM_ERR("ACDB=> Could not allocate preproc iir memory\n");
+		free_memory_acdb_get_blk();
+		free_memory_acdb_cache_rx();
+		free_memory_acdb_cache_tx();
+		kfree(acdb_data.pp_iir);
+		kfree(acdb_data.pp_mbadrc);
+		kfree(acdb_data.calib_gain_rx);
+		kfree(acdb_data.preproc_agc);
+		result = -ENOMEM;
+		goto done;
+	}
+	acdb_data.calib_gain_tx = kmalloc(sizeof(*acdb_data.calib_gain_tx),
+							GFP_KERNEL);
+	if (acdb_data.calib_gain_tx == NULL) {
+		MM_ERR("ACDB=> Could not allocate"
+			" preproc calib_gain_tx memory\n");
+		free_memory_acdb_get_blk();
+		free_memory_acdb_cache_rx();
+		free_memory_acdb_cache_tx();
+		kfree(acdb_data.pp_iir);
+		kfree(acdb_data.pp_mbadrc);
+		kfree(acdb_data.calib_gain_rx);
+		kfree(acdb_data.preproc_agc);
+		kfree(acdb_data.preproc_iir);
+		result = -ENOMEM;
+		goto done;
+	}
+	acdb_data.pbe_block = kmalloc(sizeof(*acdb_data.pbe_block),
+						GFP_KERNEL);
+	if (acdb_data.pbe_block == NULL) {
+		MM_ERR("ACDB=> Could not allocate pbe_block memory\n");
+		free_memory_acdb_get_blk();
+		free_memory_acdb_cache_rx();
+		free_memory_acdb_cache_tx();
+		kfree(acdb_data.pp_iir);
+		kfree(acdb_data.pp_mbadrc);
+		kfree(acdb_data.calib_gain_rx);
+		kfree(acdb_data.preproc_agc);
+		kfree(acdb_data.preproc_iir);
+		kfree(acdb_data.calib_gain_tx);
+		result = -ENOMEM;
+		goto done;
+	}
+	acdb_data.pbe_extbuff = (u16 *)(pmem_kalloc(PBE_BUF_SIZE,
+					(PMEM_MEMTYPE_EBI1 |
+					PMEM_ALIGNMENT_4K)));
+	if (IS_ERR((void *)acdb_data.pbe_extbuff)) {
+		MM_ERR("ACDB=> Cannot allocate physical memory\n");
+		free_memory_acdb_get_blk();
+		free_memory_acdb_cache_rx();
+		free_memory_acdb_cache_tx();
+		kfree(acdb_data.pp_iir);
+		kfree(acdb_data.pp_mbadrc);
+		kfree(acdb_data.calib_gain_rx);
+		kfree(acdb_data.preproc_agc);
+		kfree(acdb_data.preproc_iir);
+		kfree(acdb_data.calib_gain_tx);
+		kfree(acdb_data.pbe_block);
+		result = -ENOMEM;
+		goto done;
+	}
+	acdb_data.fluence_extbuff = pmem_kalloc(FLUENCE_BUF_SIZE,
+							(PMEM_MEMTYPE_EBI1 |
+							PMEM_ALIGNMENT_4K));
+	if (IS_ERR((void *)acdb_data.fluence_extbuff)) {
+		MM_ERR("ACDB=> cannot allocate physical memory for "
+					"fluence block\n");
+		free_memory_acdb_get_blk();
+		free_memory_acdb_cache_rx();
+		free_memory_acdb_cache_tx();
+		kfree(acdb_data.pp_iir);
+		kfree(acdb_data.pp_mbadrc);
+		kfree(acdb_data.calib_gain_rx);
+		kfree(acdb_data.preproc_agc);
+		kfree(acdb_data.preproc_iir);
+		kfree(acdb_data.calib_gain_tx);
+		kfree(acdb_data.pbe_block);
+		pmem_kfree((int32_t)acdb_data.pbe_extbuff);
+		result = -ENOMEM;
+		goto done;
+	}
+	acdb_data.fluence_extbuff_virt =
+					ioremap(
+					acdb_data.fluence_extbuff,
+						FLUENCE_BUF_SIZE);
+	if (acdb_data.fluence_extbuff_virt == NULL) {
+		MM_ERR("ACDB=> Could not map physical address\n");
+		free_memory_acdb_get_blk();
+		free_memory_acdb_cache_rx();
+		free_memory_acdb_cache_tx();
+		kfree(acdb_data.pp_iir);
+		kfree(acdb_data.pp_mbadrc);
+		kfree(acdb_data.calib_gain_rx);
+		kfree(acdb_data.preproc_agc);
+		kfree(acdb_data.preproc_iir);
+		kfree(acdb_data.calib_gain_tx);
+		kfree(acdb_data.pbe_block);
+		pmem_kfree((int32_t)acdb_data.pbe_extbuff);
+		pmem_kfree((int32_t)acdb_data.fluence_extbuff);
+		result = -ENOMEM;
+		goto done;
+	}
+done:
+	return result;
+}
+
+static u32 free_acdb_cache_node(union auddev_evt_data *evt)
+{
+	u32 session_id;
+	if ((evt->audcal_info.dev_type & TX_DEVICE) == 2) {
+		/*Second argument to find_first_bit should be maximum number
+		of bits interested
+		*/
+		session_id = find_first_bit(
+				(unsigned long *)&(evt->audcal_info.sessions),
+				sizeof(evt->audcal_info.sessions) * 8);
+		MM_DBG("freeing node %d for tx device", session_id);
+		acdb_cache_tx[session_id].
+			node_status = ACDB_VALUES_NOT_FILLED;
+	} else {
+		if (--(acdb_cache_rx[evt->audcal_info.dev_id].stream_id) <= 0) {
+			MM_DBG("freeing rx cache node %d\n",
+						evt->audcal_info.dev_id);
+			acdb_cache_rx[evt->audcal_info.dev_id].
+				node_status = ACDB_VALUES_NOT_FILLED;
+			acdb_cache_rx[evt->audcal_info.dev_id].stream_id = 0;
+		}
+	}
+	return 0;
+}
+
+static u8 check_device_change(struct auddev_evt_audcal_info audcal_info)
+{
+	if (!acdb_data.device_info) {
+		MM_ERR("not pointing to previous valid device detail\n");
+		return 1; /*device info will not be pointing to*/
+			/* valid device when acdb driver comes up*/
+	}
+	if ((audcal_info.dev_id == acdb_data.device_info->dev_id) &&
+		(audcal_info.sample_rate ==
+				acdb_data.device_info->sample_rate) &&
+		(audcal_info.acdb_id == acdb_data.device_info->acdb_id)) {
+		return 0;
+	}
+	return 1;
+}
+
+static void device_cb(u32 evt_id, union auddev_evt_data *evt, void *private)
+{
+	struct auddev_evt_audcal_info	audcal_info;
+	struct acdb_cache_node *acdb_cache_free_node =  NULL;
+	u32 stream_id = 0;
+	u8 ret = 0;
+	u8 count = 0;
+	u8 i = 0;
+	u8 device_change = 0;
+
+	if (!((evt_id == AUDDEV_EVT_DEV_RDY) ||
+		(evt_id == AUDDEV_EVT_DEV_RLS))) {
+		goto done;
+	}
+	/*if session value is zero it indicates that device call back is for
+	voice call we will drop the request as acdb values for voice call is
+	not applied from acdb driver*/
+	if (!evt->audcal_info.sessions) {
+		MM_DBG("no active sessions and call back is for"
+				" voice call\n");
+		goto done;
+	}
+	if (evt_id == AUDDEV_EVT_DEV_RLS) {
+		MM_DBG("got release command for dev %d\n",
+					evt->audcal_info.dev_id);
+		acdb_data.acdb_state &= ~CAL_DATA_READY;
+		free_acdb_cache_node(evt);
+		/*reset the applied flag for the session routed to the device*/
+		acdb_data.audrec_applied &= ~(evt->audcal_info.sessions
+							<< AUDREC_OFFSET);
+		goto done;
+	}
+	if (((evt->audcal_info.dev_type & RX_DEVICE) == 1) &&
+			(evt->audcal_info.acdb_id == PSEUDO_ACDB_ID)) {
+		MM_INFO("device cb is for rx device with pseudo acdb id\n");
+		goto done;
+	}
+	audcal_info = evt->audcal_info;
+	MM_DBG("dev_id = %d\n", audcal_info.dev_id);
+	MM_DBG("sample_rate = %d\n", audcal_info.sample_rate);
+	MM_DBG("acdb_id = %d\n", audcal_info.acdb_id);
+	MM_DBG("sessions = %d\n", audcal_info.sessions);
+	MM_DBG("acdb_state = %x\n", acdb_data.acdb_state);
+	mutex_lock(&acdb_data.acdb_mutex);
+	device_change = check_device_change(audcal_info);
+	if (!device_change) {
+		if ((audcal_info.dev_type & TX_DEVICE) == 2) {
+			if (!(acdb_data.acdb_state & AUDREC0_READY))
+				acdb_data.audrec_applied &= ~AUDREC0_READY;
+			if (!(acdb_data.acdb_state & AUDREC1_READY))
+				acdb_data.audrec_applied &= ~AUDREC1_READY;
+			if (!(acdb_data.acdb_state & AUDREC2_READY))
+				acdb_data.audrec_applied &= ~AUDREC2_READY;
+				acdb_data.acdb_state &= ~CAL_DATA_READY;
+				goto update_cache;
+		}
+	} else
+		/* state is updated to querry the modem for values */
+		acdb_data.acdb_state &= ~CAL_DATA_READY;
+
+update_cache:
+	if ((audcal_info.dev_type & TX_DEVICE) == 2) {
+		/*loop is to take care of use case:- multiple Audrec
+		sessions are routed before enabling the device in this use
+		case we will get the sessions value as bits set for all the
+		sessions routed before device enable, so we should take care
+		of copying device info to all the sessions*/
+		for (i = 0; i < MAX_AUDREC_SESSIONS; i++) {
+			stream_id = ((audcal_info.sessions >> i) & 0x01);
+			if (stream_id) {
+				acdb_cache_free_node =	&acdb_cache_tx[i];
+				ret  = check_device_info_already_present(
+							audcal_info,
+							acdb_cache_free_node);
+				acdb_cache_free_node->stream_id = i;
+				acdb_data.cur_tx_session = i;
+				count++;
+			}
+		}
+		if (count > 1)
+			acdb_data.multiple_sessions = 1;
+	} else {
+		acdb_cache_free_node = &acdb_cache_rx[audcal_info.dev_id];
+		ret = check_device_info_already_present(audcal_info,
+						acdb_cache_free_node);
+		if (ret == 1) {
+			MM_DBG("got device ready call back for another "
+					"audplay task sessions on same COPP\n");
+			/*stream_id is used to keep track of number of active*/
+			/*sessions active on this device*/
+			acdb_cache_free_node->stream_id++;
+			mutex_unlock(&acdb_data.acdb_mutex);
+			goto done;
+		}
+		acdb_cache_free_node->stream_id++;
+	}
+	update_acdb_data_struct(acdb_cache_free_node);
+	acdb_data.device_cb_compl = 1;
+	mutex_unlock(&acdb_data.acdb_mutex);
+	wake_up(&acdb_data.wait);
+done:
+	return;
+}
+
+
+static s32 register_device_cb(void)
+{
+	s32 result = 0;
+
+	result = auddev_register_evt_listner((AUDDEV_EVT_DEV_RDY
+						| AUDDEV_EVT_DEV_RLS),
+		AUDDEV_CLNT_AUDIOCAL, 0, device_cb, (void *)&acdb_data);
+
+	if (result) {
+		MM_ERR("ACDB=> Could not register device callback\n");
+		result = -ENODEV;
+		goto done;
+	}
+done:
+	return result;
+}
+
+static void audpp_cb(void *private, u32 id, u16 *msg)
+{
+	MM_DBG("\n");
+	if (id != AUDPP_MSG_CFG_MSG)
+		goto done;
+
+	if (msg[0] == AUDPP_MSG_ENA_DIS) {
+		acdb_data.acdb_state &= ~AUDPP_READY;
+		MM_DBG("AUDPP_MSG_ENA_DIS\n");
+		goto done;
+	}
+
+	acdb_data.acdb_state |= AUDPP_READY;
+	acdb_data.audpp_cb_compl = 1;
+	wake_up(&acdb_data.wait);
+done:
+	return;
+}
+
+static s8 handle_audpreproc_cb(void)
+{
+	struct acdb_cache_node *acdb_cached_values;
+	s8 result = 0;
+	u8 stream_id = acdb_data.preproc_stream_id;
+	acdb_data.preproc_cb_compl = 0;
+	acdb_cached_values = get_acdb_values_from_cache_tx(stream_id);
+	if (acdb_cached_values == NULL) {
+		MM_DBG("ERROR: to get chached acdb values\n");
+		return -EPERM;
+	}
+	update_acdb_data_struct(acdb_cached_values);
+	if (acdb_data.device_info->dev_id == PSEUDO_ACDB_ID) {
+		MM_INFO("audpreproc is routed to pseudo device\n");
+		return result;
+	}
+	if (session_info[stream_id].sampling_freq)
+		acdb_data.device_info->sample_rate =
+					session_info[stream_id].sampling_freq;
+	if (!(acdb_data.acdb_state & CAL_DATA_READY)) {
+		result = check_tx_acdb_values_cached();
+		if (result) {
+			result = acdb_get_calibration();
+			if (result < 0) {
+				MM_ERR("failed to get calibration data\n");
+				return result;
+			}
+		}
+		acdb_cached_values->node_status = ACDB_VALUES_FILLED;
+	}
+	return result;
+}
+
+void fluence_feature_update(int enable, int stream_id)
+{
+	MM_INFO("Fluence feature over ride with = %d\n", enable);
+	acdb_data.fleuce_feature_status[stream_id] = enable;
+}
+EXPORT_SYMBOL(fluence_feature_update);
+
+static void audpreproc_cb(void *private, u32 id, void *msg)
+{
+	struct audpreproc_cmd_enc_cfg_done_msg *tmp;
+	u8 result = 0;
+	int stream_id = 0;
+	if (id != AUDPREPROC_CMD_ENC_CFG_DONE_MSG)
+		goto done;
+
+	tmp = (struct audpreproc_cmd_enc_cfg_done_msg *)msg;
+	acdb_data.preproc_stream_id = tmp->stream_id;
+	stream_id = acdb_data.preproc_stream_id;
+	get_audrec_session_info(stream_id, &session_info[stream_id]);
+	MM_DBG("rec_enc_type = %x\n", tmp->rec_enc_type);
+	if ((tmp->rec_enc_type & 0x8000) ==
+				AUD_PREPROC_CONFIG_DISABLED) {
+		if (acdb_data.preproc_stream_id == 0) {
+			acdb_data.acdb_state &= ~AUDREC0_READY;
+			acdb_data.audrec_applied &= ~AUDREC0_READY;
+		} else if (acdb_data.preproc_stream_id == 1) {
+			acdb_data.acdb_state &= ~AUDREC1_READY;
+			acdb_data.audrec_applied &= ~AUDREC1_READY;
+		} else if (acdb_data.preproc_stream_id == 2) {
+			acdb_data.acdb_state &= ~AUDREC2_READY;
+			acdb_data.audrec_applied &= ~AUDREC2_READY;
+		}
+		acdb_data.fleuce_feature_status[stream_id] = 0;
+		acdb_cache_tx[tmp->stream_id].node_status =\
+						ACDB_VALUES_NOT_FILLED;
+		acdb_data.acdb_state &= ~CAL_DATA_READY;
+		goto done;
+	}
+	/*Following check is added to make sure that device info
+	  is updated. audpre proc layer enabled without device
+	  callback at this scenario we should not access
+	  device information
+	 */
+	if (acdb_data.device_info &&
+		session_info[stream_id].sampling_freq) {
+		acdb_data.device_info->sample_rate =
+					session_info[stream_id].sampling_freq;
+		result = check_tx_acdb_values_cached();
+		if (!result) {
+			MM_INFO("acdb values for the stream is" \
+						" querried from modem");
+			acdb_data.acdb_state |= CAL_DATA_READY;
+		} else {
+			acdb_data.acdb_state &= ~CAL_DATA_READY;
+		}
+	}
+	if (acdb_data.preproc_stream_id == 0)
+		acdb_data.acdb_state |= AUDREC0_READY;
+	else if (acdb_data.preproc_stream_id == 1)
+		acdb_data.acdb_state |= AUDREC1_READY;
+	else if (acdb_data.preproc_stream_id == 2)
+		acdb_data.acdb_state |= AUDREC2_READY;
+	acdb_data.preproc_cb_compl = 1;
+	MM_DBG("acdb_data.acdb_state = %x\n", acdb_data.acdb_state);
+	wake_up(&acdb_data.wait);
+done:
+	return;
+}
+
+static s32 register_audpp_cb(void)
+{
+	s32 result = 0;
+
+	acdb_data.audpp_cb.fn = audpp_cb;
+	acdb_data.audpp_cb.private = NULL;
+	result = audpp_register_event_callback(&acdb_data.audpp_cb);
+	if (result) {
+		MM_ERR("ACDB=> Could not register audpp callback\n");
+		result = -ENODEV;
+		goto done;
+	}
+done:
+	return result;
+}
+
+static s32 register_audpreproc_cb(void)
+{
+	s32 result = 0;
+
+	acdb_data.audpreproc_cb.fn = audpreproc_cb;
+	acdb_data.audpreproc_cb.private = NULL;
+	result = audpreproc_register_event_callback(&acdb_data.audpreproc_cb);
+	if (result) {
+		MM_ERR("ACDB=> Could not register audpreproc callback\n");
+		result = -ENODEV;
+		goto done;
+	}
+
+done:
+	return result;
+}
+
+static s32 acdb_initialize_data(void)
+{
+	s32	result = 0;
+
+	mutex_init(&acdb_data.acdb_mutex);
+
+	result = initialize_rpc();
+	if (result)
+		goto err;
+
+	result = initialize_memory();
+	if (result)
+		goto err1;
+
+	result = register_device_cb();
+	if (result)
+		goto err2;
+
+	result = register_audpp_cb();
+	if (result)
+		goto err3;
+
+	result = register_audpreproc_cb();
+	if (result)
+		goto err4;
+
+	return result;
+
+err4:
+	result = audpreproc_unregister_event_callback(&acdb_data.audpreproc_cb);
+	if (result)
+		MM_ERR("ACDB=> Could not unregister audpreproc callback\n");
+err3:
+	result = audpp_unregister_event_callback(&acdb_data.audpp_cb);
+	if (result)
+		MM_ERR("ACDB=> Could not unregister audpp callback\n");
+err2:
+	result = auddev_unregister_evt_listner(AUDDEV_CLNT_AUDIOCAL, 0);
+	if (result)
+		MM_ERR("ACDB=> Could not unregister device callback\n");
+err1:
+	daldevice_detach(acdb_data.handle);
+	acdb_data.handle = NULL;
+err:
+	return result;
+}
+
+static s32 initialize_modem_acdb(void)
+{
+	struct acdb_cmd_init_adie acdb_cmd;
+	u8 codec_type = -1;
+	s32 result = 0;
+	u8 iterations = 0;
+
+	codec_type = adie_get_detected_codec_type();
+	if (codec_type == MARIMBA_ID)
+		acdb_cmd.adie_type = ACDB_CURRENT_ADIE_MODE_MARIMBA;
+	else if (codec_type == TIMPANI_ID)
+		acdb_cmd.adie_type = ACDB_CURRENT_ADIE_MODE_TIMPANI;
+	else
+		acdb_cmd.adie_type = ACDB_CURRENT_ADIE_MODE_UNKNOWN;
+	acdb_cmd.command_id = ACDB_CMD_INITIALIZE_FOR_ADIE;
+	do {
+		/*Initialize ACDB software on modem based on codec type*/
+		result = dalrpc_fcn_8(ACDB_DalACDB_ioctl, acdb_data.handle,
+				(const void *)&acdb_cmd, sizeof(acdb_cmd),
+				&acdb_data.acdb_result,
+				sizeof(acdb_data.acdb_result));
+		if (result < 0) {
+			MM_ERR("ACDB=> RPC failure result = %d\n", result);
+			goto error;
+		}
+		/*following check is introduced to handle boot up race
+		condition between AUDCAL SW peers running on apps
+		and modem (ACDB_RES_BADSTATE indicates modem AUDCAL SW is
+		not in initialized sate) we need to retry to get ACDB
+		initialized*/
+		if (acdb_data.acdb_result.result == ACDB_RES_BADSTATE) {
+			msleep(500);
+			iterations++;
+		} else if (acdb_data.acdb_result.result == ACDB_RES_SUCCESS) {
+			MM_DBG("Modem ACDB SW initialized ((iterations = %d)\n",
+							iterations);
+			return result;
+		} else {
+			MM_ERR("ACDB=> Modem ACDB SW failed to initialize"
+					" reuslt = %d, (iterations = %d)\n",
+					acdb_data.acdb_result.result,
+					iterations);
+			goto error;
+		}
+	} while (iterations < MAX_RETRY);
+	MM_ERR("ACDB=> AUDCAL SW on modem is not in intiailized state (%d)\n",
+			acdb_data.acdb_result.result);
+error:
+	result = -EINVAL;
+	return result;
+}
+
+static s32 acdb_calibrate_device(void *data)
+{
+	s32 result = 0;
+
+	/* initialize driver */
+	result = acdb_initialize_data();
+	if (result)
+		goto done;
+
+	result = initialize_modem_acdb();
+	if (result < 0)
+		MM_ERR("failed to initialize modem ACDB\n");
+
+	while (!kthread_should_stop()) {
+		MM_DBG("Waiting for call back events\n");
+		wait_event_interruptible(acdb_data.wait,
+					(acdb_data.device_cb_compl
+					| acdb_data.audpp_cb_compl
+					| acdb_data.preproc_cb_compl));
+		mutex_lock(&acdb_data.acdb_mutex);
+		if (acdb_data.device_cb_compl) {
+			acdb_data.device_cb_compl = 0;
+			if (!(acdb_data.acdb_state & CAL_DATA_READY)) {
+				if ((acdb_data.device_info->dev_type
+							& RX_DEVICE) == 1) {
+					/*we need to get calibration values
+					only for RX device as resampler
+					moved to start of the pre - proc chain
+					tx calibration value will be based on
+					sampling frequency what audrec is
+					configured, calibration values for tx
+					device are fetch in audpreproc
+					callback*/
+					result = acdb_get_calibration();
+					if (result < 0) {
+						mutex_unlock(
+							&acdb_data.acdb_mutex);
+						MM_ERR("Not able to get "
+							"calibration "
+							"data continue\n");
+						continue;
+					}
+				}
+			}
+			MM_DBG("acdb state = %d\n",
+					 acdb_data.acdb_state);
+			if ((acdb_data.device_info->dev_type & TX_DEVICE) == 2)
+				handle_tx_device_ready_callback();
+			else {
+				acdb_cache_rx[acdb_data.device_info->dev_id]\
+						.node_status =
+						ACDB_VALUES_FILLED;
+				if (acdb_data.acdb_state &
+						AUDPP_READY) {
+					MM_DBG("AUDPP already enabled "
+							"apply acdb values\n");
+					goto apply;
+				}
+			}
+		}
+
+		if (!(acdb_data.audpp_cb_compl ||
+				acdb_data.preproc_cb_compl)) {
+			MM_DBG("need to wait for either AUDPP / AUDPREPROC "
+					"Event\n");
+			mutex_unlock(&acdb_data.acdb_mutex);
+			continue;
+		} else {
+			MM_DBG("got audpp / preproc call back\n");
+			if (acdb_data.audpp_cb_compl) {
+				send_acdb_values_for_active_devices();
+				acdb_data.audpp_cb_compl = 0;
+				mutex_unlock(&acdb_data.acdb_mutex);
+				continue;
+			} else {
+				result = handle_audpreproc_cb();
+				if (result < 0) {
+					mutex_unlock(&acdb_data.acdb_mutex);
+					continue;
+				}
+			}
+		}
+apply:
+		if (acdb_data.acdb_state & CAL_DATA_READY)
+			result = acdb_send_calibration();
+
+		mutex_unlock(&acdb_data.acdb_mutex);
+	}
+done:
+	return 0;
+}
+
+static int __init acdb_init(void)
+{
+
+	s32 result = 0;
+
+	memset(&acdb_data, 0, sizeof(acdb_data));
+	spin_lock_init(&acdb_data.dsp_lock);
+	acdb_data.cb_thread_task = kthread_run(acdb_calibrate_device,
+		NULL, "acdb_cb_thread");
+
+	if (IS_ERR(acdb_data.cb_thread_task)) {
+		MM_ERR("ACDB=> Could not register cb thread\n");
+		result = -ENODEV;
+		goto err;
+	}
+#ifdef CONFIG_DEBUG_FS
+	/*This is RTC specific INIT used only with debugfs*/
+	if (!rtc_acdb_init())
+		MM_ERR("RTC ACDB=>INIT Failure\n");
+
+#endif
+	init_waitqueue_head(&acdb_data.wait);
+
+	return misc_register(&acdb_misc);
+err:
+	return result;
+}
+
+static void __exit acdb_exit(void)
+{
+	s32	result = 0;
+	u32 i = 0;
+
+	result = auddev_unregister_evt_listner(AUDDEV_CLNT_AUDIOCAL, 0);
+	if (result)
+		MM_ERR("ACDB=> Could not unregister device callback\n");
+
+	result = audpp_unregister_event_callback(&acdb_data.audpp_cb);
+	if (result)
+		MM_ERR("ACDB=> Could not unregister audpp callback\n");
+
+	result = audpreproc_unregister_event_callback(&acdb_data.\
+				audpreproc_cb);
+	if (result)
+		MM_ERR("ACDB=> Could not unregister audpreproc callback\n");
+
+	result = kthread_stop(acdb_data.cb_thread_task);
+	if (result)
+		MM_ERR("ACDB=> Could not stop kthread\n");
+
+	free_memory_acdb_get_blk();
+
+	for (i = 0; i < MAX_COPP_NODE_SUPPORTED; i++) {
+		if (i < MAX_AUDREC_SESSIONS) {
+			iounmap(acdb_cache_tx[i].virt_addr_acdb_values);
+			pmem_kfree(acdb_cache_tx[i].phys_addr_acdb_values);
+		}
+		iounmap(acdb_cache_rx[i].virt_addr_acdb_values);
+		pmem_kfree(acdb_cache_rx[i].phys_addr_acdb_values);
+	}
+	kfree(acdb_data.device_info);
+	kfree(acdb_data.pp_iir);
+	kfree(acdb_data.pp_mbadrc);
+	kfree(acdb_data.preproc_agc);
+	kfree(acdb_data.preproc_iir);
+	pmem_kfree((int32_t)acdb_data.pbe_extbuff);
+	iounmap(acdb_data.fluence_extbuff_virt);
+	pmem_kfree((int32_t)acdb_data.fluence_extbuff);
+	mutex_destroy(&acdb_data.acdb_mutex);
+	memset(&acdb_data, 0, sizeof(acdb_data));
+	#ifdef CONFIG_DEBUG_FS
+	rtc_acdb_deinit();
+	#endif
+}
+
+late_initcall(acdb_init);
+module_exit(acdb_exit);
+
+MODULE_DESCRIPTION("MSM 7x30 Audio ACDB driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_adpcm.c b/arch/arm/mach-msm/qdsp5v2/audio_adpcm.c
new file mode 100644
index 0000000..8d767c9
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_adpcm.c
@@ -0,0 +1,1741 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Based on the mp3 native driver in arch/arm/mach-msm/qdsp5v2/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 <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+#include <linux/slab.h>
+#include <linux/msm_audio.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/qdsp5audplaycmdi.h>
+#include <mach/qdsp5v2/qdsp5audplaymsg.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/qdsp5v2/audpp.h>
+#include <mach/debug_mm.h>
+
+/* Size must be power of 2 */
+#define BUFSZ_MAX 	32768	/* Includes meta in size */
+#define BUFSZ_MIN 	4096	/* 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_ADPCM 1
+
+#define PCM_BUFSZ_MIN 	8216 	/* Hold one stereo ADPCM 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 AUDADPCM_METAFIELD_MASK 0xFFFF0000
+#define AUDADPCM_EOS_FLG_OFFSET 0x0A /* Offset from beginning of buffer */
+#define AUDADPCM_EOS_FLG_MASK 0x01
+#define AUDADPCM_EOS_NONE 0x0 /* No EOS detected */
+#define AUDADPCM_EOS_SET 0x1 /* EOS set in meta field */
+
+#define AUDADPCM_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 audadpcm_suspend_ctl {
+	struct early_suspend node;
+	struct audio *audio;
+};
+#endif
+
+struct audadpcm_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;
+	uint32_t out_block_size;
+
+	/* 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 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;
+	int16_t source;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	struct audadpcm_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;
+	/* AV sync Info */
+	int avsync_flag;              /* Flag to indicate feedback from DSP */
+	wait_queue_head_t avsync_wait;/* Wait queue for AV Sync Message     */
+	/* flags, 48 bits sample/bytes counter per channel */
+	uint16_t avsync[AUDPP_AVSYNC_CH_COUNT * AUDPP_AVSYNC_NUM_WORDS + 1];
+	uint32_t device_events;
+
+	int eq_enable;
+	int eq_needs_commit;
+	struct audpp_cmd_cfg_object_params_eqalizer eq;
+	struct 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 audadpcm_post_event(struct audio *audio, int type,
+		union msm_audio_event_payload payload);
+
+/* must be called with audio->lock held */
+static int audio_enable(struct audio *audio)
+{
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	if (audio->enabled)
+		return 0;
+
+	audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+	audio->out_tail = 0;
+	audio->out_needed = 0;
+
+	if (msm_adsp_enable(audio->audplay)) {
+		MM_ERR("msm_adsp_enable(audplay) failed\n");
+		return -ENODEV;
+	}
+
+	if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) {
+		MM_ERR("audpp_enable() failed\n");
+		msm_adsp_disable(audio->audplay);
+		return -ENODEV;
+	}
+
+	audio->enabled = 1;
+	return 0;
+}
+
+static void adpcm_listner(u32 evt_id, union auddev_evt_data *evt_payload,
+			void *private_data)
+{
+	struct audio *audio = (struct audio *) private_data;
+	switch (evt_id) {
+	case AUDDEV_EVT_DEV_RDY:
+		MM_DBG(":AUDDEV_EVT_DEV_RDY\n");
+		audio->source |= (0x1 << evt_payload->routing_id);
+		if (audio->running == 1 && audio->enabled == 1)
+			audpp_route_stream(audio->dec_id, audio->source);
+		break;
+	case AUDDEV_EVT_DEV_RLS:
+		MM_DBG(":AUDDEV_EVT_DEV_RLS\n");
+		if (audio->dec_state == MSM_AUD_DECODER_STATE_SUCCESS &&
+							audio->enabled == 1)
+			audpp_route_stream(audio->dec_id,
+				msm_snddev_route_dec(audio->dec_id));
+		break;
+	case AUDDEV_EVT_STREAM_VOL_CHG:
+		audio->vol_pan.volume = evt_payload->session_vol;
+		MM_DBG(":AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n",
+				audio->vol_pan.volume);
+		if (audio->running)
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+					POPP);
+		break;
+	default:
+		MM_ERR(":ERROR:wrong event\n");
+		break;
+	}
+}
+/* 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);
+		audio->out_needed = 0;
+	}
+	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");
+				/* send  mixer command */
+				audpp_route_stream(audio->dec_id,
+						audio->source);
+				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,
+					POPP);
+			audpp_dsp_set_eq(audio->dec_id,	audio->eq_enable,
+					&audio->eq, POPP);
+		} else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+			MM_DBG("CFG_MSG DISABLE\n");
+			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;
+
+	case AUDPP_MSG_AVSYNC_MSG:
+		MM_DBG("AUDPP_MSG_AVSYNC_MSG\n");
+		memcpy(&audio->avsync[0], msg, sizeof(audio->avsync));
+		audio->avsync_flag = 1;
+		wake_up(&audio->avsync_wait);
+		break;
+
+	default:
+		MM_ERR("UNKNOWN (%d)\n", id);
+	}
+
+}
+
+static struct msm_adsp_ops audplay_adsp_ops_adpcm = {
+	.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)
+{
+	struct audpp_cmd_cfg_dec_type cfg_dec_cmd;
+
+	memset(&cfg_dec_cmd, 0, sizeof(cfg_dec_cmd));
+
+	cfg_dec_cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+	if (enable)
+		cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+				AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_ADPCM;
+	else
+		cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+				AUDPP_CMD_DIS_DEC_V;
+	cfg_dec_cmd.dm_mode = 0x0;
+	cfg_dec_cmd.stream_id = audio->dec_id;
+	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_adpcm cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS;
+	cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_ADPCM_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.block_size =  audio->out_block_size;
+
+	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 = AUDADPCM_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);
+	audio->avsync_flag = 1;
+	wake_up(&audio->avsync_wait);
+}
+
+static int audadpcm_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 audadpcm_reset_event_queue(struct audio *audio)
+{
+	unsigned long flags;
+	struct audadpcm_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 audadpcm_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 audadpcm_event, list);
+		list_del(&drv_evt->list);
+		kfree(drv_evt);
+	}
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+	return;
+}
+
+static long audadpcm_process_event_req(struct audio *audio, void __user *arg)
+{
+	long rc;
+	struct msm_audio_event usr_evt;
+	struct audadpcm_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, audadpcm_events_pending(audio),
+			msecs_to_jiffies(timeout));
+		if (rc == 0)
+			return -ETIMEDOUT;
+	} else {
+		rc = wait_event_interruptible(
+			audio->event_wait, audadpcm_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 audadpcm_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, POPP);
+		audio->eq_needs_commit = 0;
+	}
+	return 0;
+}
+
+static int audio_get_avsync_data(struct audio *audio,
+						struct msm_audio_stats *stats)
+{
+	int rc = -EINVAL;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	if (audio->dec_id == audio->avsync[0] && audio->avsync_flag) {
+		/* av_sync sample count */
+		stats->sample_count = (audio->avsync[2] << 16) |
+						(audio->avsync[3]);
+
+		/* av_sync byte_count */
+		stats->byte_count = (audio->avsync[5] << 16) |
+						(audio->avsync[6]);
+
+		audio->avsync_flag = 0;
+		rc = 0;
+	}
+	local_irq_restore(flags);
+	return rc;
+
+}
+
+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;
+
+		audio->avsync_flag = 0;
+		memset(&stats, 0, sizeof(stats));
+		if (audpp_query_avsync(audio->dec_id) < 0)
+			return rc;
+
+		rc = wait_event_interruptible_timeout(audio->avsync_wait,
+				(audio->avsync_flag == 1),
+				msecs_to_jiffies(AUDPP_AVSYNC_EVENT_TIMEOUT));
+
+		if (rc < 0)
+			return rc;
+		else if ((rc > 0) || ((rc == 0) && (audio->avsync_flag == 1))) {
+			if (audio_get_avsync_data(audio, &stats) < 0)
+				return rc;
+
+			if (copy_to_user((void *)arg, &stats, sizeof(stats)))
+				return -EFAULT;
+			return 0;
+		} else
+			return -EAGAIN;
+	}
+
+	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,
+					POPP);
+		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,
+					POPP);
+		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 = audadpcm_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;
+		audio->out_block_size = config.bits;
+		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 ((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;
+	case AUDIO_GET_SESSION_ID:
+		if (copy_to_user((void *) arg, &audio->dec_id,
+					sizeof(unsigned short)))
+			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;
+	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 audadpcm_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 = AUDADPCM_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[AUDADPCM_EOS_FLG_OFFSET] &
+						 AUDADPCM_EOS_FLG_MASK) {
+					MM_DBG("audio_write: EOS SET\n");
+					eos_condition = AUDADPCM_EOS_SET;
+					if (mfield_size == count) {
+						buf += mfield_size;
+						break;
+					} else
+					cpy_ptr[AUDADPCM_EOS_FLG_OFFSET]
+						&= ~AUDADPCM_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 == AUDADPCM_EOS_SET)
+		rc = audadpcm_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);
+	auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id);
+	audio_disable(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);
+	audadpcm_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;
+}
+
+static void audadpcm_post_event(struct audio *audio, int type,
+		union msm_audio_event_payload payload)
+{
+	struct audadpcm_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 audadpcm_event, list);
+		list_del(&e_node->list);
+	} else {
+		e_node = kmalloc(sizeof(struct audadpcm_event), GFP_ATOMIC);
+		if (!e_node) {
+			MM_ERR("No mem to post event %d\n", type);
+			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 audadpcm_suspend(struct early_suspend *h)
+{
+	struct audadpcm_suspend_ctl *ctl =
+		container_of(h, struct audadpcm_suspend_ctl, node);
+	union msm_audio_event_payload payload;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	audadpcm_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload);
+}
+
+static void audadpcm_resume(struct early_suspend *h)
+{
+	struct audadpcm_suspend_ctl *ctl =
+		container_of(h, struct audadpcm_suspend_ctl, node);
+	union msm_audio_event_payload payload;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	audadpcm_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload);
+}
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t audadpcm_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t audadpcm_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 audadpcm_debug_fops = {
+	.read = audadpcm_debug_read,
+	.open = audadpcm_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 audadpcm_event *e_node = NULL;
+#ifdef CONFIG_DEBUG_FS
+	/* 4 bytes represents decoder number, 1 byte for terminate string */
+	char name[sizeof "msm_adpcm_" + 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_ADPCM;
+	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 = msm_adsp_get(audio->module_name, &audio->audplay,
+			&audplay_adsp_ops_adpcm, audio);
+	if (rc) {
+		MM_ERR("failed to get %s module, freeing instance 0x%08x\n",
+				audio->module_name, (int)audio);
+		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);
+	init_waitqueue_head(&audio->avsync_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->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;
+
+	audio->device_events = AUDDEV_EVT_DEV_RDY
+				|AUDDEV_EVT_DEV_RLS|
+				AUDDEV_EVT_STREAM_VOL_CHG;
+
+	rc = auddev_register_evt_listner(audio->device_events,
+					AUDDEV_CLNT_DEC,
+					audio->dec_id,
+					adpcm_listner,
+					(void *)audio);
+	if (rc) {
+		MM_ERR("%s: failed to register listner\n", __func__);
+		goto event_err;
+	}
+
+#ifdef CONFIG_DEBUG_FS
+	snprintf(name, sizeof name, "msm_adpcm_%04x", audio->dec_id);
+	audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+				NULL, (void *) audio,
+				&audadpcm_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 = audadpcm_resume;
+	audio->suspend_ctl.node.suspend = audadpcm_suspend;
+	audio->suspend_ctl.audio = audio;
+	register_early_suspend(&audio->suspend_ctl.node);
+#endif
+	for (i = 0; i < AUDADPCM_EVENT_NUM; i++) {
+		e_node = kmalloc(sizeof(struct audadpcm_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;
+event_err:
+	msm_adsp_put(audio->audplay);
+err:
+	iounmap(audio->data);
+	pmem_kfree(audio->phys);
+	audpp_adec_free(audio->dec_id);
+	kfree(audio);
+	return rc;
+}
+
+static const struct file_operations audio_adpcm_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_adpcm_misc = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "msm_adpcm",
+	.fops	= &audio_adpcm_fops,
+};
+
+static int __init audio_init(void)
+{
+	return misc_register(&audio_adpcm_misc);
+}
+
+device_initcall(audio_init);
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_amrnb.c b/arch/arm/mach-msm/qdsp5v2/audio_amrnb.c
new file mode 100644
index 0000000..53a8850
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_amrnb.c
@@ -0,0 +1,1633 @@
+/*
+ * amrnb audio decoder device
+ *
+ * Copyright (c) 2008-2010, 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 <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+#include <linux/msm_audio.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <linux/slab.h>
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/qdsp5audplaycmdi.h>
+#include <mach/qdsp5v2/qdsp5audplaymsg.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/qdsp5v2/audpp.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;
+
+	/* 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 */
+
+	const char *module_name;
+	unsigned queue_id;
+	uint16_t dec_id;
+	uint32_t read_ptr_offset;
+	int16_t source;
+
+#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;
+	/* AV sync Info */
+	int avsync_flag;              /* Flag to indicate feedback from DSP */
+	wait_queue_head_t avsync_wait;/* Wait queue for AV Sync Message     */
+	/* flags, 48 bits sample/bytes counter per channel */
+	uint16_t avsync[AUDPP_AVSYNC_CH_COUNT * AUDPP_AVSYNC_NUM_WORDS + 1];
+
+	uint32_t device_events;
+
+	int eq_enable;
+	int eq_needs_commit;
+	struct audpp_cmd_cfg_object_params_eqalizer eq;
+	struct audpp_cmd_cfg_object_params_volume vol_pan;
+};
+
+struct audpp_cmd_cfg_adec_params_amrnb {
+   struct 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);
+static void audamrnb_post_event(struct audio *audio, int type,
+		union msm_audio_event_payload payload);
+
+/* must be called with audio->lock held */
+static int audamrnb_enable(struct audio *audio)
+{
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	if (audio->enabled)
+		return 0;
+
+	audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+	audio->out_tail = 0;
+	audio->out_needed = 0;
+
+	if (msm_adsp_enable(audio->audplay)) {
+		MM_ERR("msm_adsp_enable(audplay) failed\n");
+		return -ENODEV;
+	}
+
+	if (audpp_enable(audio->dec_id, audamrnb_dsp_event, audio)) {
+		MM_ERR("audpp_enable() failed\n");
+		msm_adsp_disable(audio->audplay);
+		return -ENODEV;
+	}
+	audio->enabled = 1;
+	return 0;
+}
+
+static void amrnb_listner(u32 evt_id, union auddev_evt_data *evt_payload,
+			void *private_data)
+{
+	struct audio *audio = (struct audio *) private_data;
+	switch (evt_id) {
+	case AUDDEV_EVT_DEV_RDY:
+		MM_DBG(":AUDDEV_EVT_DEV_RDY\n");
+		audio->source |= (0x1 << evt_payload->routing_id);
+		if (audio->running == 1 && audio->enabled == 1)
+			audpp_route_stream(audio->dec_id, audio->source);
+		break;
+	case AUDDEV_EVT_DEV_RLS:
+		MM_DBG(":AUDDEV_EVT_DEV_RLS\n");
+		audio->source &= ~(0x1 << evt_payload->routing_id);
+		if (audio->running == 1 && audio->enabled == 1)
+			audpp_route_stream(audio->dec_id, audio->source);
+		break;
+	case AUDDEV_EVT_STREAM_VOL_CHG:
+		audio->vol_pan.volume = evt_payload->session_vol;
+		MM_DBG(":AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n",
+				audio->vol_pan.volume);
+		if (audio->running)
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+					POPP);
+		break;
+	default:
+		MM_ERR(":ERROR:wrong event\n");
+		break;
+	}
+}
+/* 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);
+		audio->out_needed = 0;
+	}
+	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");
+				/* send  mixer command */
+				audpp_route_stream(audio->dec_id,
+						audio->source);
+				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,
+					POPP);
+			audpp_dsp_set_eq(audio->dec_id, audio->eq_enable,
+					&audio->eq, POPP);
+		} else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+			MM_DBG("CFG_MSG DISABLE\n");
+			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;
+
+	case AUDPP_MSG_AVSYNC_MSG:
+		MM_DBG("AUDPP_MSG_AVSYNC_MSG\n");
+		memcpy(&audio->avsync[0], msg, sizeof(audio->avsync));
+		audio->avsync_flag = 1;
+		wake_up(&audio->avsync_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)
+{
+	struct audpp_cmd_cfg_dec_type cfg_dec_cmd;
+
+	memset(&cfg_dec_cmd, 0, sizeof(cfg_dec_cmd));
+
+	cfg_dec_cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+	if (enable)
+		cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+				AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_AMRNB;
+	else
+		cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+				AUDPP_CMD_DIS_DEC_V;
+	cfg_dec_cmd.dm_mode = 0x0;
+	cfg_dec_cmd.stream_id = audio->dec_id;
+
+	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;
+	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);
+	audio->avsync_flag = 1;
+	wake_up(&audio->avsync_wait);
+}
+
+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, POPP);
+		audio->eq_needs_commit = 0;
+	}
+	return 0;
+}
+
+static int audio_get_avsync_data(struct audio *audio,
+						struct msm_audio_stats *stats)
+{
+	int rc = -EINVAL;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	if (audio->dec_id == audio->avsync[0] && audio->avsync_flag) {
+		/* av_sync sample count */
+		stats->sample_count = (audio->avsync[2] << 16) |
+						(audio->avsync[3]);
+
+		/* av_sync byte_count */
+		stats->byte_count = (audio->avsync[5] << 16) |
+						(audio->avsync[6]);
+
+		audio->avsync_flag = 0;
+		rc = 0;
+	}
+	local_irq_restore(flags);
+	return rc;
+
+}
+
+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;
+
+		audio->avsync_flag = 0;
+		memset(&stats, 0, sizeof(stats));
+		if (audpp_query_avsync(audio->dec_id) < 0)
+			return rc;
+
+		rc = wait_event_interruptible_timeout(audio->avsync_wait,
+				(audio->avsync_flag == 1),
+				msecs_to_jiffies(AUDPP_AVSYNC_EVENT_TIMEOUT));
+
+		if (rc < 0)
+			return rc;
+		else if ((rc > 0) || ((rc == 0) && (audio->avsync_flag == 1))) {
+			if (audio_get_avsync_data(audio, &stats) < 0)
+				return rc;
+
+			if (copy_to_user((void *)arg, &stats, sizeof(stats)))
+				return -EFAULT;
+			return 0;
+		} else
+			return -EAGAIN;
+	}
+
+	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,
+					POPP);
+		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,
+					POPP);
+		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;
+	}
+	case AUDIO_GET_SESSION_ID:
+		if (copy_to_user((void *) arg, &audio->dec_id,
+			sizeof(unsigned short)))
+			rc =  -EFAULT;
+		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);
+
+			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);
+	auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id);
+	audamrnb_disable(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;
+}
+
+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);
+			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 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\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);
+	}
+
+	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);
+		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);
+	init_waitqueue_head(&audio->avsync_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;
+
+	audio->device_events = AUDDEV_EVT_DEV_RDY
+				|AUDDEV_EVT_DEV_RLS|
+				AUDDEV_EVT_STREAM_VOL_CHG;
+
+	rc = auddev_register_evt_listner(audio->device_events,
+					AUDDEV_CLNT_DEC,
+					audio->dec_id,
+					amrnb_listner,
+					(void *)audio);
+	if (rc) {
+		MM_ERR("%s: failed to register listner\n", __func__);
+		goto event_err;
+	}
+
+#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;
+event_err:
+	msm_adsp_put(audio->audplay);
+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/qdsp5v2/audio_amrnb_in.c b/arch/arm/mach-msm/qdsp5v2/audio_amrnb_in.c
new file mode 100644
index 0000000..a685af5
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_amrnb_in.c
@@ -0,0 +1,887 @@
+/*
+ * amrnb audio input 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/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/msm_audio_amrnb.h>
+#include <linux/android_pmem.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+
+#include <mach/msm_adsp.h>
+#include <mach/qdsp5v2/qdsp5audreccmdi.h>
+#include <mach/qdsp5v2/qdsp5audrecmsg.h>
+#include <mach/qdsp5v2/audpreproc.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/debug_mm.h>
+
+/* FRAME_NUM must be a power of two */
+#define FRAME_NUM		(8)
+#define FRAME_SIZE		(22 * 2) /* 36 bytes data */
+#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;
+	atomic_t in_samples;
+
+	struct mutex lock;
+	struct mutex read_lock;
+	wait_queue_head_t wait;
+	wait_queue_head_t wait_enable;
+
+	struct msm_adsp_module *audrec;
+	struct audrec_session_info session_info; /*audrec session info*/
+
+	/* configuration to use on next enable */
+	uint32_t buffer_size; /* Frame size (36 bytes) */
+	uint32_t enc_type;
+
+	int dtx_mode;
+	uint32_t frame_format;
+	uint32_t used_mode;
+	uint32_t rec_mode;
+
+	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 mode;
+
+	const char *module_name;
+	unsigned queue_ids;
+	uint16_t enc_id;
+
+	uint16_t source; /* Encoding source bit mask */
+	uint32_t device_events;
+	uint32_t in_call;
+	uint32_t dev_cnt;
+	int voice_state;
+	spinlock_t dev_lock;
+
+	/* 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[]; /* samples */
+} __attribute__((packed));
+
+/* Audrec Queue command sent macro's */
+#define audrec_send_bitstreamqueue(audio, cmd, len) \
+	msm_adsp_write(audio->audrec, ((audio->queue_ids & 0xFFFF0000) >> 16),\
+			cmd, len)
+
+#define audrec_send_audrecqueue(audio, cmd, len) \
+	msm_adsp_write(audio->audrec, (audio->queue_ids & 0x0000FFFF),\
+			cmd, len)
+
+struct audio_in the_audio_amrnb_in;
+
+/* DSP command send functions */
+static int audamrnb_in_enc_config(struct audio_in *audio, int enable);
+static int audamrnb_in_param_config(struct audio_in *audio);
+static int audamrnb_in_mem_config(struct audio_in *audio);
+static int audamrnb_in_record_config(struct audio_in *audio, int enable);
+static int audamrnb_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt);
+
+static void audamrnb_in_get_dsp_frames(struct audio_in *audio);
+
+static void audamrnb_in_flush(struct audio_in *audio);
+
+static void amrnb_in_listener(u32 evt_id, union auddev_evt_data *evt_payload,
+				void *private_data)
+{
+	struct audio_in *audio = (struct audio_in *) private_data;
+	unsigned long flags;
+
+	MM_DBG("evt_id = 0x%8x\n", evt_id);
+	switch (evt_id) {
+	case AUDDEV_EVT_DEV_RDY: {
+		MM_DBG("AUDDEV_EVT_DEV_RDY\n");
+		spin_lock_irqsave(&audio->dev_lock, flags);
+		audio->dev_cnt++;
+		if (!audio->in_call)
+			audio->source |= (0x1 << evt_payload->routing_id);
+		spin_unlock_irqrestore(&audio->dev_lock, flags);
+
+		if ((audio->running == 1) && (audio->enabled == 1))
+			audamrnb_in_record_config(audio, 1);
+
+		break;
+	}
+	case AUDDEV_EVT_DEV_RLS: {
+		MM_DBG("AUDDEV_EVT_DEV_RLS\n");
+		spin_lock_irqsave(&audio->dev_lock, flags);
+		audio->dev_cnt--;
+		if (!audio->in_call)
+			audio->source &= ~(0x1 << evt_payload->routing_id);
+		spin_unlock_irqrestore(&audio->dev_lock, flags);
+
+		if ((!audio->running) || (!audio->enabled))
+			break;
+
+		/* Turn of as per source */
+		if (audio->source)
+			audamrnb_in_record_config(audio, 1);
+		else
+			/* Turn off all */
+			audamrnb_in_record_config(audio, 0);
+
+		break;
+	}
+	case AUDDEV_EVT_VOICE_STATE_CHG: {
+		MM_DBG("AUDDEV_EVT_VOICE_STATE_CHG, state = %d\n",
+				evt_payload->voice_state);
+		audio->voice_state = evt_payload->voice_state;
+		if (audio->in_call && audio->running) {
+			if (audio->voice_state == VOICE_STATE_INCALL)
+				audamrnb_in_record_config(audio, 1);
+			else if (audio->voice_state == VOICE_STATE_OFFCALL) {
+				audamrnb_in_record_config(audio, 0);
+				wake_up(&audio->wait);
+			}
+		}
+
+		break;
+	}
+	default:
+		MM_ERR("wrong event %d\n", evt_id);
+		break;
+	}
+}
+
+/* ------------------- dsp preproc event handler--------------------- */
+static void audpreproc_dsp_event(void *data, unsigned id,  void *msg)
+{
+	struct audio_in *audio = data;
+
+	switch (id) {
+	case AUDPREPROC_ERROR_MSG: {
+		struct audpreproc_err_msg *err_msg = msg;
+
+		MM_ERR("ERROR_MSG: stream id %d err idx %d\n",
+		err_msg->stream_id, err_msg->aud_preproc_err_idx);
+		/* Error case */
+		wake_up(&audio->wait_enable);
+		break;
+	}
+	case AUDPREPROC_CMD_CFG_DONE_MSG: {
+		MM_DBG("CMD_CFG_DONE_MSG \n");
+		break;
+	}
+	case AUDPREPROC_CMD_ENC_CFG_DONE_MSG: {
+		struct audpreproc_cmd_enc_cfg_done_msg *enc_cfg_msg = msg;
+
+		MM_DBG("CMD_ENC_CFG_DONE_MSG: stream id %d enc type \
+			0x%8x\n", enc_cfg_msg->stream_id,
+			enc_cfg_msg->rec_enc_type);
+		/* Encoder enable success */
+		if (enc_cfg_msg->rec_enc_type & ENCODE_ENABLE)
+			audamrnb_in_param_config(audio);
+		else { /* Encoder disable success */
+			audio->running = 0;
+			audamrnb_in_record_config(audio, 0);
+		}
+		break;
+	}
+	case AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG: {
+		MM_DBG("CMD_ENC_PARAM_CFG_DONE_MSG \n");
+		audamrnb_in_mem_config(audio);
+		break;
+	}
+	case AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG: {
+		MM_DBG("AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG \n");
+		wake_up(&audio->wait_enable);
+		break;
+	}
+	default:
+		MM_ERR("Unknown Event id %d\n", id);
+	}
+}
+
+/* ------------------- dsp audrec event handler--------------------- */
+static void audrec_dsp_event(void *data, unsigned id, size_t len,
+			    void (*getevent)(void *ptr, size_t len))
+{
+	struct audio_in *audio = data;
+
+	switch (id) {
+	case AUDREC_CMD_MEM_CFG_DONE_MSG: {
+		MM_DBG("CMD_MEM_CFG_DONE MSG DONE\n");
+		audio->running = 1;
+		if ((!audio->in_call && (audio->dev_cnt > 0)) ||
+			(audio->in_call &&
+				(audio->voice_state == VOICE_STATE_INCALL)))
+			audamrnb_in_record_config(audio, 1);
+		break;
+	}
+	case AUDREC_FATAL_ERR_MSG: {
+		struct audrec_fatal_err_msg fatal_err_msg;
+
+		getevent(&fatal_err_msg, AUDREC_FATAL_ERR_MSG_LEN);
+		MM_ERR("FATAL_ERR_MSG: err id %d\n",
+				fatal_err_msg.audrec_err_id);
+		/* Error stop the encoder */
+		audio->stopped = 1;
+		wake_up(&audio->wait);
+		break;
+	}
+	case AUDREC_UP_PACKET_READY_MSG: {
+		struct audrec_up_pkt_ready_msg pkt_ready_msg;
+
+		getevent(&pkt_ready_msg, AUDREC_UP_PACKET_READY_MSG_LEN);
+		MM_DBG("UP_PACKET_READY_MSG: write cnt lsw  %d \
+		write cnt msw %d read cnt lsw %d  read cnt msw %d \n",\
+		pkt_ready_msg.audrec_packet_write_cnt_lsw, \
+		pkt_ready_msg.audrec_packet_write_cnt_msw, \
+		pkt_ready_msg.audrec_up_prev_read_cnt_lsw, \
+		pkt_ready_msg.audrec_up_prev_read_cnt_msw);
+
+		audamrnb_in_get_dsp_frames(audio);
+		break;
+	}
+	case ADSP_MESSAGE_ID: {
+		MM_DBG("Received ADSP event:module audrectask\n");
+		break;
+	}
+	default:
+		MM_ERR("Unknown Event id %d\n", id);
+	}
+}
+
+static void audamrnb_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->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)
+		audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1);
+	else
+		audio->in_count++;
+
+	audamrnb_dsp_read_buffer(audio, audio->dsp_cnt++);
+	spin_unlock_irqrestore(&audio->dsp_lock, flags);
+
+	wake_up(&audio->wait);
+}
+struct msm_adsp_ops audrec_amrnb_adsp_ops = {
+	.event = audrec_dsp_event,
+};
+
+static int audamrnb_in_enc_config(struct audio_in *audio, int enable)
+{
+	struct audpreproc_audrec_cmd_enc_cfg cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ENC_CFG_2;
+	cmd.stream_id = audio->enc_id;
+
+	if (enable)
+		cmd.audrec_enc_type = audio->enc_type | ENCODE_ENABLE;
+	else
+		cmd.audrec_enc_type &= ~(ENCODE_ENABLE);
+
+	return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+static int audamrnb_in_param_config(struct audio_in *audio)
+{
+	struct audpreproc_audrec_cmd_parm_cfg_amrnb cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.common.cmd_id = AUDPREPROC_AUDREC_CMD_PARAM_CFG;
+	cmd.common.stream_id = audio->enc_id;
+
+	cmd.dtx_mode = audio->dtx_mode;
+	cmd.test_mode = -1; /* Default set to -1 */
+	cmd.used_mode = audio->used_mode;
+
+	return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+/* To Do: msm_snddev_route_enc(audio->enc_id); */
+static int audamrnb_in_record_config(struct audio_in *audio, int enable)
+{
+	struct audpreproc_afe_cmd_audio_record_cfg cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG;
+	cmd.stream_id = audio->enc_id;
+	if (enable)
+		cmd.destination_activity = AUDIO_RECORDING_TURN_ON;
+	else
+		cmd.destination_activity = AUDIO_RECORDING_TURN_OFF;
+
+	cmd.source_mix_mask = audio->source;
+	if (audio->enc_id == 2) {
+		if ((cmd.source_mix_mask &
+				INTERNAL_CODEC_TX_SOURCE_MIX_MASK) ||
+			(cmd.source_mix_mask & AUX_CODEC_TX_SOURCE_MIX_MASK) ||
+			(cmd.source_mix_mask & VOICE_UL_SOURCE_MIX_MASK) ||
+			(cmd.source_mix_mask & VOICE_DL_SOURCE_MIX_MASK)) {
+			cmd.pipe_id = SOURCE_PIPE_1;
+		}
+		if (cmd.source_mix_mask &
+				AUDPP_A2DP_PIPE_SOURCE_MIX_MASK)
+			cmd.pipe_id |= SOURCE_PIPE_0;
+	}
+
+	return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+static int audamrnb_in_mem_config(struct audio_in *audio)
+{
+	struct audrec_cmd_arecmem_cfg cmd;
+	uint16_t *data = (void *) audio->data;
+	int n;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDREC_CMD_MEM_CFG_CMD;
+	cmd.audrec_up_pkt_intm_count = 1;
+	cmd.audrec_ext_pkt_start_addr_msw = audio->phys >> 16;
+	cmd.audrec_ext_pkt_start_addr_lsw = audio->phys;
+	cmd.audrec_ext_pkt_buf_number = FRAME_NUM;
+
+	/* prepare buffer pointers:
+	 * 36 bytes amrnb packet + 4 halfword header
+	 */
+	for (n = 0; n < FRAME_NUM; n++) {
+		audio->in[n].data = data + 4;
+		data += (FRAME_SIZE/2); /* word increment */
+		MM_DBG("0x%8x\n", (int)(audio->in[n].data - 8));
+	}
+
+	return audrec_send_audrecqueue(audio, &cmd, sizeof(cmd));
+}
+
+static int audamrnb_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt)
+{
+	struct up_audrec_packet_ext_ptr cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = UP_AUDREC_PACKET_EXT_PTR;
+	cmd.audrec_up_curr_read_count_msw = read_cnt >> 16;
+	cmd.audrec_up_curr_read_count_lsw = read_cnt;
+
+	return audrec_send_bitstreamqueue(audio, &cmd, sizeof(cmd));
+}
+
+/* must be called with audio->lock held */
+static int audamrnb_in_enable(struct audio_in *audio)
+{
+	if (audio->enabled)
+		return 0;
+
+	if (audpreproc_enable(audio->enc_id, &audpreproc_dsp_event, audio)) {
+		MM_ERR("msm_adsp_enable(audpreproc) failed\n");
+		return -ENODEV;
+	}
+
+	if (msm_adsp_enable(audio->audrec)) {
+		MM_ERR("msm_adsp_enable(audrec) failed\n");
+		audpreproc_disable(audio->enc_id, audio);
+		return -ENODEV;
+	}
+	audio->enabled = 1;
+	audamrnb_in_enc_config(audio, 1);
+
+	return 0;
+}
+
+/* must be called with audio->lock held */
+static int audamrnb_in_disable(struct audio_in *audio)
+{
+	if (audio->enabled) {
+		audio->enabled = 0;
+		audamrnb_in_enc_config(audio, 0);
+		wake_up(&audio->wait);
+		wait_event_interruptible_timeout(audio->wait_enable,
+				audio->running == 0, 1*HZ);
+		msm_adsp_disable(audio->audrec);
+		audpreproc_disable(audio->enc_id, audio);
+	}
+	return 0;
+}
+
+static void audamrnb_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 = 0; i < FRAME_NUM; 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);
+}
+
+/* ------------------- device --------------------- */
+static long audamrnb_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);
+		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: {
+		uint32_t freq;
+		freq = 48000;
+		MM_DBG("AUDIO_START\n");
+		if (audio->in_call && (audio->voice_state !=
+				VOICE_STATE_INCALL)) {
+			rc = -EPERM;
+			break;
+		}
+		rc = msm_snddev_request_freq(&freq, audio->enc_id,
+					SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+		MM_DBG("sample rate configured %d\n", freq);
+		if (rc < 0) {
+			MM_DBG(" Sample rate can not be set, return code %d\n",
+								 rc);
+			msm_snddev_withdraw_freq(audio->enc_id,
+					SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+			MM_DBG("msm_snddev_withdraw_freq\n");
+			break;
+		}
+		/*update aurec session info in audpreproc layer*/
+		audio->session_info.session_id = audio->enc_id;
+		/*amrnb works only on 8KHz*/
+		audio->session_info.sampling_freq = 8000;
+		audpreproc_update_audrec_info(&audio->session_info);
+		rc = audamrnb_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: {
+		/*reset the sampling frequency information at audpreproc layer*/
+		audio->session_info.sampling_freq = 0;
+		audpreproc_update_audrec_info(&audio->session_info);
+		rc = audamrnb_in_disable(audio);
+		rc = msm_snddev_withdraw_freq(audio->enc_id,
+					SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+		MM_DBG("msm_snddev_withdraw_freq\n");
+		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);
+			audamrnb_in_flush(audio);
+			mutex_unlock(&audio->read_lock);
+		}
+		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 (cfg.buffer_size != (FRAME_SIZE - 8))
+			rc = -EINVAL;
+		else
+			audio->buffer_size = cfg.buffer_size;
+		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;
+		break;
+	}
+	case AUDIO_GET_AMRNB_ENC_CONFIG_V2: {
+		struct msm_audio_amrnb_enc_config_v2 cfg;
+		memset(&cfg, 0, sizeof(cfg));
+		cfg.dtx_enable = ((audio->dtx_mode == -1) ? 1 : 0);
+		cfg.band_mode = audio->used_mode;
+		cfg.frame_format = audio->frame_format;
+		if (copy_to_user((void *) arg, &cfg, sizeof(cfg)))
+			rc = -EFAULT;
+		break;
+	}
+	case AUDIO_SET_AMRNB_ENC_CONFIG_V2: {
+		struct msm_audio_amrnb_enc_config_v2 cfg;
+		if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) {
+			rc = -EFAULT;
+			break;
+		}
+		/* DSP does not support any other than default format */
+		if (audio->frame_format != cfg.frame_format) {
+			rc = -EINVAL;
+			break;
+		}
+		if (cfg.dtx_enable == 0)
+			audio->dtx_mode = 0;
+		else if (cfg.dtx_enable == 1)
+			audio->dtx_mode = -1;
+		else {
+			rc = -EINVAL;
+			break;
+		}
+		audio->used_mode = cfg.band_mode;
+		break;
+	}
+	case AUDIO_SET_INCALL: {
+		struct msm_voicerec_mode cfg;
+		unsigned long flags;
+		if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) {
+			rc = -EFAULT;
+			break;
+		}
+		if (cfg.rec_mode != VOC_REC_BOTH &&
+			cfg.rec_mode != VOC_REC_UPLINK &&
+			cfg.rec_mode != VOC_REC_DOWNLINK) {
+			MM_ERR("invalid rec_mode\n");
+			rc = -EINVAL;
+			break;
+		} else {
+			spin_lock_irqsave(&audio->dev_lock, flags);
+			if (cfg.rec_mode == VOC_REC_UPLINK)
+				audio->source = VOICE_UL_SOURCE_MIX_MASK;
+			else if (cfg.rec_mode == VOC_REC_DOWNLINK)
+				audio->source = VOICE_DL_SOURCE_MIX_MASK;
+			else
+				audio->source = VOICE_DL_SOURCE_MIX_MASK |
+						VOICE_UL_SOURCE_MIX_MASK ;
+			audio->in_call = 1;
+			spin_unlock_irqrestore(&audio->dev_lock, flags);
+		}
+		break;
+	}
+	case AUDIO_GET_SESSION_ID: {
+		if (copy_to_user((void *) arg, &audio->enc_id,
+			sizeof(unsigned short))) {
+			rc = -EFAULT;
+		}
+		break;
+	}
+	default:
+		rc = -EINVAL;
+	}
+	mutex_unlock(&audio->lock);
+	return rc;
+}
+
+static ssize_t audamrnb_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
+			|| (audio->in_call && audio->running &&
+				(audio->voice_state == VOICE_STATE_OFFCALL)));
+		if (rc < 0)
+			break;
+
+		if (!audio->in_count) {
+			if (audio->stopped)  {
+				rc = 0;/* End of File */
+				break;
+			} else if (audio->in_call && audio->running &&
+				(audio->voice_state == VOICE_STATE_OFFCALL)) {
+				MM_DBG("Not Permitted Voice Terminated\n");
+				rc = -EPERM; /* Voice Call stopped */
+				break;
+			}
+		}
+
+		index = audio->in_tail;
+		data = (uint8_t *) audio->in[index].data;
+		size = audio->in[index].size;
+		if (count >= size) {
+			if (copy_to_user(buf, data, size)) {
+				rc = -EFAULT;
+				break;
+			}
+			spin_lock_irqsave(&audio->dsp_lock, flags);
+			if (index != audio->in_tail) {
+				/* overrun -- data is
+				 * invalid and we need to retry */
+				spin_unlock_irqrestore(&audio->dsp_lock, flags);
+				continue;
+			}
+			audio->in[index].size = 0;
+			audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1);
+			audio->in_count--;
+			spin_unlock_irqrestore(&audio->dsp_lock, flags);
+			count -= size;
+			buf += size;
+		} else {
+			MM_ERR("short read\n");
+			break;
+		}
+	}
+	mutex_unlock(&audio->read_lock);
+
+	if (buf > start)
+		return buf - start;
+
+	return rc;
+}
+
+static ssize_t audamrnb_in_write(struct file *file,
+				const char __user *buf,
+				size_t count, loff_t *pos)
+{
+	return -EINVAL;
+}
+
+static int audamrnb_in_release(struct inode *inode, struct file *file)
+{
+	struct audio_in *audio = file->private_data;
+
+	MM_DBG("\n");
+	mutex_lock(&audio->lock);
+	audio->in_call = 0;
+	/* with draw frequency for session
+	   incase not stopped the driver */
+	msm_snddev_withdraw_freq(audio->enc_id, SNDDEV_CAP_TX,
+					AUDDEV_CLNT_ENC);
+	auddev_unregister_evt_listner(AUDDEV_CLNT_ENC, audio->enc_id);
+	/*reset the sampling frequency information at audpreproc layer*/
+	audio->session_info.sampling_freq = 0;
+	audpreproc_update_audrec_info(&audio->session_info);
+	audamrnb_in_disable(audio);
+	audamrnb_in_flush(audio);
+	msm_adsp_put(audio->audrec);
+	audpreproc_aenc_free(audio->enc_id);
+	audio->audrec = NULL;
+	audio->opened = 0;
+	if (audio->data) {
+		iounmap(audio->data);
+		pmem_kfree(audio->phys);
+		audio->data = NULL;
+	}
+	mutex_unlock(&audio->lock);
+	return 0;
+}
+
+static int audamrnb_in_open(struct inode *inode, struct file *file)
+{
+	struct audio_in *audio = &the_audio_amrnb_in;
+	int rc;
+	int encid;
+
+	mutex_lock(&audio->lock);
+	if (audio->opened) {
+		rc = -EBUSY;
+		goto done;
+	}
+	audio->phys = pmem_kalloc(DMASZ, PMEM_MEMTYPE_EBI1|
+					PMEM_ALIGNMENT_4K);
+	if (!IS_ERR((void *)audio->phys)) {
+		audio->data = ioremap(audio->phys, DMASZ);
+		if (!audio->data) {
+			MM_ERR("could not allocate DMA buffers\n");
+			rc = -ENOMEM;
+			pmem_kfree(audio->phys);
+			goto done;
+		}
+	} else {
+		MM_ERR("could not allocate DMA buffers\n");
+		rc = -ENOMEM;
+		goto done;
+	}
+	MM_DBG("Memory addr = 0x%8x  phy addr = 0x%8x\n",\
+		(int) audio->data, (int) audio->phys);
+	if ((file->f_mode & FMODE_WRITE) &&
+			(file->f_mode & FMODE_READ)) {
+		rc = -EACCES;
+		MM_ERR("Non tunnel encoding is not supported\n");
+		goto done;
+	} else if (!(file->f_mode & FMODE_WRITE) &&
+					(file->f_mode & FMODE_READ)) {
+		audio->mode = MSM_AUD_ENC_MODE_TUNNEL;
+		MM_DBG("Opened for tunnel mode encoding\n");
+	} else {
+		rc = -EACCES;
+		goto done;
+	}
+
+
+	/* Settings will be re-config at AUDIO_SET_CONFIG,
+	 * but at least we need to have initial config
+	 */
+	audio->buffer_size = (FRAME_SIZE - 8);
+	audio->enc_type = ENC_TYPE_AMRNB | audio->mode;
+	audio->dtx_mode = -1;
+	audio->frame_format = 0;
+	audio->used_mode = 7; /* Bit Rate 12.2 kbps MR122 */
+
+	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_amrnb_adsp_ops, audio);
+
+	if (rc) {
+		audpreproc_aenc_free(audio->enc_id);
+		goto done;
+	}
+
+	audio->stopped = 0;
+	audio->source = 0;
+
+	audamrnb_in_flush(audio);
+
+	audio->device_events = AUDDEV_EVT_DEV_RDY | AUDDEV_EVT_DEV_RLS |
+				AUDDEV_EVT_VOICE_STATE_CHG;
+
+	audio->voice_state = msm_get_voice_state();
+	rc = auddev_register_evt_listner(audio->device_events,
+					AUDDEV_CLNT_ENC, audio->enc_id,
+					amrnb_in_listener, (void *) audio);
+	if (rc) {
+		MM_ERR("failed to register device event listener\n");
+		goto evt_error;
+	}
+	file->private_data = audio;
+	audio->opened = 1;
+done:
+	mutex_unlock(&audio->lock);
+	return rc;
+evt_error:
+	msm_adsp_put(audio->audrec);
+	audpreproc_aenc_free(audio->enc_id);
+	mutex_unlock(&audio->lock);
+	return rc;
+}
+
+static const struct file_operations audio_in_fops = {
+	.owner		= THIS_MODULE,
+	.open		= audamrnb_in_open,
+	.release	= audamrnb_in_release,
+	.read		= audamrnb_in_read,
+	.write		= audamrnb_in_write,
+	.unlocked_ioctl	= audamrnb_in_ioctl,
+};
+
+struct miscdevice audio_amrnb_in_misc = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "msm_amrnb_in",
+	.fops	= &audio_in_fops,
+};
+
+static int __init audamrnb_in_init(void)
+{
+	mutex_init(&the_audio_amrnb_in.lock);
+	mutex_init(&the_audio_amrnb_in.read_lock);
+	spin_lock_init(&the_audio_amrnb_in.dsp_lock);
+	spin_lock_init(&the_audio_amrnb_in.dev_lock);
+	init_waitqueue_head(&the_audio_amrnb_in.wait);
+	init_waitqueue_head(&the_audio_amrnb_in.wait_enable);
+	return misc_register(&audio_amrnb_in_misc);
+}
+
+device_initcall(audamrnb_in_init);
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_amrwb.c b/arch/arm/mach-msm/qdsp5v2/audio_amrwb.c
new file mode 100644
index 0000000..4793e6e
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_amrwb.c
@@ -0,0 +1,1712 @@
+/* amrwb audio decoder device
+ *
+ * Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Based on the mp3 native driver in arch/arm/mach-msm/qdsp5v2/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 <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+#include <linux/msm_audio.h>
+#include <linux/slab.h>
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/qdsp5audplaycmdi.h>
+#include <mach/qdsp5v2/qdsp5audplaymsg.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/qdsp5v2/audpp.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;
+
+	/* 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 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;
+	int16_t source;
+
+#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;
+	/* AV sync Info */
+	int avsync_flag;              /* Flag to indicate feedback from DSP */
+	wait_queue_head_t avsync_wait;/* Wait queue for AV Sync Message     */
+	/* flags, 48 bits sample/bytes counter per channel */
+	uint16_t avsync[AUDPP_AVSYNC_CH_COUNT * AUDPP_AVSYNC_NUM_WORDS + 1];
+
+	uint32_t device_events;
+
+	int eq_enable;
+	int eq_needs_commit;
+	struct audpp_cmd_cfg_object_params_eqalizer eq;
+	struct 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);
+static void audamrwb_post_event(struct audio *audio, int type,
+		union msm_audio_event_payload payload);
+
+/* must be called with audio->lock held */
+static int audamrwb_enable(struct audio *audio)
+{
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+
+	if (audio->enabled)
+		return 0;
+
+	audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+	audio->out_tail = 0;
+	audio->out_needed = 0;
+
+	if (msm_adsp_enable(audio->audplay)) {
+		MM_ERR("msm_adsp_enable(audplay) failed\n");
+		return -ENODEV;
+	}
+
+	if (audpp_enable(audio->dec_id, audamrwb_dsp_event, audio)) {
+		MM_ERR("audpp_enable() failed\n");
+		msm_adsp_disable(audio->audplay);
+		return -ENODEV;
+	}
+	audio->enabled = 1;
+	return 0;
+}
+
+static void amrwb_listner(u32 evt_id, union auddev_evt_data *evt_payload,
+			void *private_data)
+{
+	struct audio *audio = (struct audio *) private_data;
+	switch (evt_id) {
+	case AUDDEV_EVT_DEV_RDY:
+		MM_DBG("AUDDEV_EVT_DEV_RDY\n");
+		audio->source |= (0x1 << evt_payload->routing_id);
+		if (audio->running == 1 && audio->enabled == 1)
+			audpp_route_stream(audio->dec_id, audio->source);
+		break;
+	case AUDDEV_EVT_DEV_RLS:
+		MM_DBG("AUDDEV_EVT_DEV_RLS\n");
+		audio->source &= ~(0x1 << evt_payload->routing_id);
+		if (audio->running == 1 && audio->enabled == 1)
+			audpp_route_stream(audio->dec_id, audio->source);
+		break;
+	case AUDDEV_EVT_STREAM_VOL_CHG:
+		audio->vol_pan.volume = evt_payload->session_vol;
+		MM_DBG("AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n",
+				audio->vol_pan.volume);
+		if (audio->running)
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+					POPP);
+		break;
+	default:
+		MM_ERR("ERROR:wrong event\n");
+		break;
+	}
+}
+
+/* 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);
+		audio->out_needed = 0;
+	}
+	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("audamrwb_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("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("audamrwb_update_pcm_buf_entry: \
+				read cannot keep up\n");
+		audio->buf_refresh = 1;
+	}
+	wake_up(&audio->read_wait);
+	spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+static void audplay_dsp_event(void *data, unsigned id, size_t len,
+			      void (*getevent) (void *ptr, size_t len))
+{
+	struct audio *audio = data;
+	uint32_t msg[28];
+	getevent(msg, sizeof(msg));
+
+	MM_DBG("audplay_dsp_event: 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;
+
+	case ADSP_MESSAGE_ID:
+		MM_DBG("Received ADSP event:module audplaytask\n");
+		break;
+
+	default:
+		MM_DBG("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");
+				/* send  mixer command */
+				audpp_route_stream(audio->dec_id,
+						audio->source);
+				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,
+					POPP);
+			audpp_dsp_set_eq(audio->dec_id,	audio->eq_enable,
+					&audio->eq, POPP);
+		} else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+			MM_DBG("CFG_MSG DISABLE\n");
+			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;
+
+	case AUDPP_MSG_AVSYNC_MSG:
+		MM_DBG("AUDPP_MSG_AVSYNC_MSG\n");
+		memcpy(&audio->avsync[0], msg, sizeof(audio->avsync));
+		audio->avsync_flag = 1;
+		wake_up(&audio->avsync_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)
+{
+	struct audpp_cmd_cfg_dec_type cfg_dec_cmd;
+
+	memset(&cfg_dec_cmd, 0, sizeof(cfg_dec_cmd));
+
+	cfg_dec_cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+	if (enable)
+		cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+				AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_AMRWB;
+	else
+		cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+				AUDPP_CMD_DIS_DEC_V;
+	cfg_dec_cmd.dm_mode = 0x0;
+	cfg_dec_cmd.stream_id = audio->dec_id;
+	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);
+	audio->avsync_flag = 1;
+	wake_up(&audio->avsync_wait);
+}
+
+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, POPP);
+		audio->eq_needs_commit = 0;
+	}
+	return 0;
+}
+
+static int audio_get_avsync_data(struct audio *audio,
+						struct msm_audio_stats *stats)
+{
+	int rc = -EINVAL;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	if (audio->dec_id == audio->avsync[0] && audio->avsync_flag) {
+		/* av_sync sample count */
+		stats->sample_count = (audio->avsync[2] << 16) |
+						(audio->avsync[3]);
+
+		/* av_sync byte_count */
+		stats->byte_count = (audio->avsync[5] << 16) |
+						(audio->avsync[6]);
+
+		audio->avsync_flag = 0;
+		rc = 0;
+	}
+	local_irq_restore(flags);
+	return rc;
+
+}
+
+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;
+
+		audio->avsync_flag = 0;
+		memset(&stats, 0, sizeof(stats));
+		if (audpp_query_avsync(audio->dec_id) < 0)
+			return rc;
+
+		rc = wait_event_interruptible_timeout(audio->avsync_wait,
+				(audio->avsync_flag == 1),
+				msecs_to_jiffies(AUDPP_AVSYNC_EVENT_TIMEOUT));
+
+		if (rc < 0)
+			return rc;
+		else if ((rc > 0) || ((rc == 0) && (audio->avsync_flag == 1))) {
+			if (audio_get_avsync_data(audio, &stats) < 0)
+				return rc;
+
+			if (copy_to_user((void *)arg, &stats, sizeof(stats)))
+				return -EFAULT;
+			return 0;
+		} else
+			return -EAGAIN;
+	}
+
+	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,
+					POPP);
+		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,
+					POPP);
+		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;
+	}
+	case AUDIO_GET_SESSION_ID:
+		if (copy_to_user((void *) arg, &audio->dec_id,
+					sizeof(unsigned short)))
+			rc = -EFAULT;
+		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_DBG("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);
+	auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id);
+	audamrwb_disable(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;
+}
+
+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);
+			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 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);
+	}
+
+	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);
+		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);
+	init_waitqueue_head(&audio->avsync_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;
+	audio->device_events = AUDDEV_EVT_DEV_RDY
+				|AUDDEV_EVT_DEV_RLS|
+				AUDDEV_EVT_STREAM_VOL_CHG;
+
+	rc = auddev_register_evt_listner(audio->device_events,
+					AUDDEV_CLNT_DEC,
+					audio->dec_id,
+					amrwb_listner,
+					(void *)audio);
+	if (rc) {
+		MM_ERR("failed to register listner\n");
+		goto event_err;
+	}
+
+#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;
+event_err:
+	msm_adsp_put(audio->audplay);
+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/qdsp5v2/audio_dev_ctl.c b/arch/arm/mach-msm/qdsp5v2/audio_dev_ctl.c
new file mode 100644
index 0000000..b6d6e5e
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_dev_ctl.c
@@ -0,0 +1,1328 @@
+/* Copyright (c) 2009-2011, 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/msm_audio.h>
+#include <asm/uaccess.h>
+#include <asm/atomic.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <mach/debug_mm.h>
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/audpp.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+
+#ifndef MAX
+#define  MAX(x, y) (((x) > (y)) ? (x) : (y))
+#endif
+
+
+static DEFINE_MUTEX(session_lock);
+
+struct audio_dev_ctrl_state {
+	struct msm_snddev_info *devs[AUDIO_DEV_CTL_MAX_DEV];
+	u32 num_dev;
+	atomic_t opened;
+	struct msm_snddev_info *voice_rx_dev;
+	struct msm_snddev_info *voice_tx_dev;
+	wait_queue_head_t      wait;
+};
+
+static struct audio_dev_ctrl_state audio_dev_ctrl;
+struct event_listner event;
+#define MAX_DEC_SESSIONS	7
+#define MAX_ENC_SESSIONS	3
+
+struct session_freq {
+	int freq;
+	int evt;
+};
+
+
+struct audio_routing_info {
+	unsigned short mixer_mask[MAX_DEC_SESSIONS];
+	unsigned short audrec_mixer_mask[MAX_ENC_SESSIONS];
+	struct session_freq dec_freq[MAX_DEC_SESSIONS];
+	struct session_freq enc_freq[MAX_ENC_SESSIONS];
+	int dual_mic_setting[MAX_ENC_SESSIONS];
+	int voice_tx_dev_id;
+	int voice_rx_dev_id;
+	int voice_tx_sample_rate;
+	int voice_rx_sample_rate;
+	signed int voice_tx_vol;
+	signed int voice_rx_vol;
+	int tx_mute;
+	int rx_mute;
+	int voice_state;
+};
+
+static struct audio_routing_info routing_info;
+
+#ifdef CONFIG_DEBUG_FS
+
+static struct dentry *dentry;
+static int rtc_getdevice_dbg_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	MM_INFO("debug intf %s\n", (char *) file->private_data);
+	return 0;
+}
+bool is_dev_opened(u32 adb_id)
+{
+
+	int dev_id = 0;
+	struct msm_snddev_info *dev_info = NULL;
+
+	for (dev_id = 0; dev_id < audio_dev_ctrl.num_dev; dev_id++) {
+		dev_info = audio_dev_ctrl_find_dev(dev_id);
+	      if (IS_ERR(dev_info)) {
+		MM_ERR("pass invalid dev_id %d\n", dev_id);
+			  return false;
+		}
+		if (dev_info->opened && (dev_info->acdb_id == adb_id))
+			return true;
+	}
+
+  return false;
+}
+static ssize_t rtc_getdevice_dbg_read(struct file *file, char __user *buf,
+			  size_t count, loff_t *ppos)
+{
+	static char buffer[1024];
+	static char swap_buf[1024];
+	const int debug_bufmax = sizeof(buffer);
+	int n = 0;
+	int swap_count = 0;
+	int rc = 0;
+    int dev_count = 0;
+	int dev_id = 0;
+	struct msm_snddev_info *dev_info = NULL;
+
+
+	if (audio_dev_ctrl.num_dev <= 0) {
+		MM_ERR("Invalid no Device present\n");
+		dev_count = 0;
+		n = scnprintf(buffer, debug_bufmax, "DEV_NO:0x%x\n", dev_count);
+	} else {
+	for (dev_id = 0; dev_id < audio_dev_ctrl.num_dev; dev_id++) {
+		dev_info = audio_dev_ctrl_find_dev(dev_id);
+		if (IS_ERR(dev_info)) {
+			MM_ERR("pass invalid dev_id %d\n", dev_id);
+			rc = PTR_ERR(dev_info);
+			return rc;
+		}
+		if (dev_info->opened) {
+			n += scnprintf(swap_buf + n, debug_bufmax - n,
+					"ACDB_ID:0x%x;CAPB:0x%x\n",
+					dev_info->acdb_id,
+					dev_info->capability);
+		      dev_count++;
+		      MM_DBG("RTC Get Device %x COPP %x Session Mask \
+			      %x Capb %x Dev Count %x\n",
+			     dev_id , dev_info->copp_id, dev_info->sessions,
+			     dev_info->capability, dev_count);
+
+		}
+	}
+
+	swap_count = scnprintf(buffer, debug_bufmax, \
+			"DEV_NO:0x%x\n", dev_count);
+
+	memcpy(buffer+swap_count, swap_buf, n*sizeof(char));
+	n = n+swap_count;
+
+	buffer[n] = 0;
+    }
+	return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+
+static const struct file_operations rtc_acdb_debug_fops = {
+	.open = rtc_getdevice_dbg_open,
+	.read = rtc_getdevice_dbg_read
+};
+#endif
+int msm_reset_all_device(void)
+{
+	int rc = 0;
+	int dev_id = 0;
+	struct msm_snddev_info *dev_info = NULL;
+
+	for (dev_id = 0; dev_id < audio_dev_ctrl.num_dev; dev_id++) {
+		dev_info = audio_dev_ctrl_find_dev(dev_id);
+		if (IS_ERR(dev_info)) {
+			MM_ERR("pass invalid dev_id %d\n", dev_id);
+			rc = PTR_ERR(dev_info);
+			return rc;
+		}
+		if (!dev_info->opened)
+			continue;
+		MM_DBG("Resetting device %d active on COPP %d"
+			"with  0x%08x as routing\n",
+				dev_id, dev_info->copp_id, dev_info->sessions);
+		broadcast_event(AUDDEV_EVT_REL_PENDING,
+					dev_id,
+					SESSION_IGNORE);
+		rc = dev_info->dev_ops.close(dev_info);
+		if (rc < 0) {
+			MM_ERR("Snd device %d failed close!\n", dev_id);
+			return rc;
+		} else {
+			dev_info->opened = 0;
+			broadcast_event(AUDDEV_EVT_DEV_RLS,
+				dev_id,
+				SESSION_IGNORE);
+		}
+		dev_info->sessions = 0;
+	}
+	return 0;
+}
+EXPORT_SYMBOL(msm_reset_all_device);
+
+int msm_set_dual_mic_config(int enc_session_id, int config)
+{
+	int i;
+	if (enc_session_id >= MAX_ENC_SESSIONS)
+		return -EINVAL;
+	/*config is set(1) dual mic recording is selected */
+	/*config is reset (0) dual mic recording is not selected*/
+	routing_info.dual_mic_setting[enc_session_id] = config;
+	for (i = 0; i < MAX_ENC_SESSIONS; i++)
+		MM_DBG("dual_mic_setting[%d] = %d\n",
+			i, routing_info.dual_mic_setting[i]);
+	return 0;
+}
+EXPORT_SYMBOL(msm_set_dual_mic_config);
+
+int msm_get_dual_mic_config(int enc_session_id)
+{
+	if (enc_session_id >= MAX_ENC_SESSIONS)
+		return -EINVAL;
+	return routing_info.dual_mic_setting[enc_session_id];
+}
+EXPORT_SYMBOL(msm_get_dual_mic_config);
+
+int msm_get_voice_state(void)
+{
+	MM_DBG("voice state %d\n", routing_info.voice_state);
+	return routing_info.voice_state;
+}
+EXPORT_SYMBOL(msm_get_voice_state);
+
+int msm_set_voice_mute(int dir, int mute)
+{
+	MM_DBG("dir %x mute %x\n", dir, mute);
+	if (!audio_dev_ctrl.voice_rx_dev
+		|| !audio_dev_ctrl.voice_tx_dev)
+		return -EPERM;
+	if (dir == DIR_TX) {
+		routing_info.tx_mute = mute;
+		broadcast_event(AUDDEV_EVT_DEVICE_VOL_MUTE_CHG,
+			routing_info.voice_tx_dev_id, SESSION_IGNORE);
+	} else
+		return -EPERM;
+	return 0;
+}
+EXPORT_SYMBOL(msm_set_voice_mute);
+
+int msm_set_voice_vol(int dir, s32 volume)
+{
+	if (!audio_dev_ctrl.voice_rx_dev
+		|| !audio_dev_ctrl.voice_tx_dev)
+		return -EPERM;
+	if (dir == DIR_TX) {
+		routing_info.voice_tx_vol = volume;
+		broadcast_event(AUDDEV_EVT_DEVICE_VOL_MUTE_CHG,
+					routing_info.voice_tx_dev_id,
+					SESSION_IGNORE);
+	} else if (dir == DIR_RX) {
+		routing_info.voice_rx_vol = volume;
+		broadcast_event(AUDDEV_EVT_DEVICE_VOL_MUTE_CHG,
+					routing_info.voice_rx_dev_id,
+					SESSION_IGNORE);
+	} else
+		return -EINVAL;
+	return 0;
+}
+EXPORT_SYMBOL(msm_set_voice_vol);
+
+void msm_snddev_register(struct msm_snddev_info *dev_info)
+{
+	mutex_lock(&session_lock);
+	if (audio_dev_ctrl.num_dev < AUDIO_DEV_CTL_MAX_DEV) {
+		audio_dev_ctrl.devs[audio_dev_ctrl.num_dev] = dev_info;
+		dev_info->dev_volume = 50; /* 50%  */
+		dev_info->sessions = 0x0;
+		dev_info->usage_count = 0;
+		dev_info->set_sample_rate = 0;
+		audio_dev_ctrl.num_dev++;
+	} else
+		MM_ERR("%s: device registry max out\n", __func__);
+	mutex_unlock(&session_lock);
+}
+EXPORT_SYMBOL(msm_snddev_register);
+
+int msm_snddev_devcount(void)
+{
+	return audio_dev_ctrl.num_dev;
+}
+EXPORT_SYMBOL(msm_snddev_devcount);
+
+int msm_snddev_query(int dev_id)
+{
+	if (dev_id <= audio_dev_ctrl.num_dev)
+			return 0;
+	return -ENODEV;
+}
+EXPORT_SYMBOL(msm_snddev_query);
+
+int msm_snddev_is_set(int popp_id, int copp_id)
+{
+	return routing_info.mixer_mask[popp_id] & (0x1 << copp_id);
+}
+EXPORT_SYMBOL(msm_snddev_is_set);
+
+unsigned short msm_snddev_route_enc(int enc_id)
+{
+	if (enc_id >= MAX_ENC_SESSIONS)
+		return -EINVAL;
+	return routing_info.audrec_mixer_mask[enc_id];
+}
+EXPORT_SYMBOL(msm_snddev_route_enc);
+
+unsigned short msm_snddev_route_dec(int popp_id)
+{
+	if (popp_id >= MAX_DEC_SESSIONS)
+		return -EINVAL;
+	return routing_info.mixer_mask[popp_id];
+}
+EXPORT_SYMBOL(msm_snddev_route_dec);
+
+int msm_snddev_set_dec(int popp_id, int copp_id, int set)
+{
+	if (set)
+		routing_info.mixer_mask[popp_id] |= (0x1 << copp_id);
+	else
+		routing_info.mixer_mask[popp_id] &= ~(0x1 << copp_id);
+
+	return 0;
+}
+EXPORT_SYMBOL(msm_snddev_set_dec);
+
+int msm_snddev_set_enc(int popp_id, int copp_id, int set)
+{
+	if (set)
+		routing_info.audrec_mixer_mask[popp_id] |= (0x1 << copp_id);
+	else
+		routing_info.audrec_mixer_mask[popp_id] &= ~(0x1 << copp_id);
+	return 0;
+}
+EXPORT_SYMBOL(msm_snddev_set_enc);
+
+int msm_device_is_voice(int dev_id)
+{
+	if ((dev_id == routing_info.voice_rx_dev_id)
+		|| (dev_id == routing_info.voice_tx_dev_id))
+		return 0;
+	else
+		return -EINVAL;
+}
+EXPORT_SYMBOL(msm_device_is_voice);
+
+int msm_set_voc_route(struct msm_snddev_info *dev_info,
+			int stream_type, int dev_id)
+{
+	int rc = 0;
+	u32 session_mask = 0;
+
+	mutex_lock(&session_lock);
+	switch (stream_type) {
+	case AUDIO_ROUTE_STREAM_VOICE_RX:
+		if (audio_dev_ctrl.voice_rx_dev)
+			audio_dev_ctrl.voice_rx_dev->sessions &= ~0xFF;
+
+		if (!(dev_info->capability & SNDDEV_CAP_RX) |
+		    !(dev_info->capability & SNDDEV_CAP_VOICE)) {
+			rc = -EINVAL;
+			break;
+		}
+		audio_dev_ctrl.voice_rx_dev = dev_info;
+		if (audio_dev_ctrl.voice_rx_dev) {
+			session_mask =
+				0x1 << (8 * ((int)AUDDEV_CLNT_VOC-1));
+			audio_dev_ctrl.voice_rx_dev->sessions |=
+				session_mask;
+		}
+		routing_info.voice_rx_dev_id = dev_id;
+		break;
+	case AUDIO_ROUTE_STREAM_VOICE_TX:
+		if (audio_dev_ctrl.voice_tx_dev)
+			audio_dev_ctrl.voice_tx_dev->sessions &= ~0xFF;
+
+		if (!(dev_info->capability & SNDDEV_CAP_TX) |
+		    !(dev_info->capability & SNDDEV_CAP_VOICE)) {
+			rc = -EINVAL;
+			break;
+		}
+
+		audio_dev_ctrl.voice_tx_dev = dev_info;
+		if (audio_dev_ctrl.voice_rx_dev) {
+			session_mask =
+				0x1 << (8 * ((int)AUDDEV_CLNT_VOC-1));
+			audio_dev_ctrl.voice_tx_dev->sessions |=
+				session_mask;
+		}
+		routing_info.voice_tx_dev_id = dev_id;
+		break;
+	default:
+		rc = -EINVAL;
+	}
+	mutex_unlock(&session_lock);
+	return rc;
+}
+EXPORT_SYMBOL(msm_set_voc_route);
+
+void msm_release_voc_thread(void)
+{
+	wake_up(&audio_dev_ctrl.wait);
+}
+EXPORT_SYMBOL(msm_release_voc_thread);
+
+int msm_snddev_get_enc_freq(session_id)
+{
+	return routing_info.enc_freq[session_id].freq;
+}
+EXPORT_SYMBOL(msm_snddev_get_enc_freq);
+
+int msm_get_voc_freq(int *tx_freq, int *rx_freq)
+{
+	*tx_freq = routing_info.voice_tx_sample_rate;
+	*rx_freq = routing_info.voice_rx_sample_rate;
+	return 0;
+}
+EXPORT_SYMBOL(msm_get_voc_freq);
+
+int msm_get_voc_route(u32 *rx_id, u32 *tx_id)
+{
+	int rc = 0;
+
+	if (!rx_id || !tx_id)
+		return -EINVAL;
+
+	mutex_lock(&session_lock);
+	if (!audio_dev_ctrl.voice_rx_dev || !audio_dev_ctrl.voice_tx_dev) {
+		rc = -ENODEV;
+		mutex_unlock(&session_lock);
+		return rc;
+	}
+
+	*rx_id = audio_dev_ctrl.voice_rx_dev->acdb_id;
+	*tx_id = audio_dev_ctrl.voice_tx_dev->acdb_id;
+
+	mutex_unlock(&session_lock);
+
+	return rc;
+}
+EXPORT_SYMBOL(msm_get_voc_route);
+
+struct msm_snddev_info *audio_dev_ctrl_find_dev(u32 dev_id)
+{
+	struct msm_snddev_info *info;
+
+	if ((audio_dev_ctrl.num_dev - 1) < dev_id) {
+		info = ERR_PTR(-ENODEV);
+		goto error;
+	}
+
+	info = audio_dev_ctrl.devs[dev_id];
+error:
+	return info;
+
+}
+EXPORT_SYMBOL(audio_dev_ctrl_find_dev);
+
+int snddev_voice_set_volume(int vol, int path)
+{
+	if (audio_dev_ctrl.voice_rx_dev
+		&& audio_dev_ctrl.voice_tx_dev) {
+		if (path)
+			audio_dev_ctrl.voice_tx_dev->dev_volume = vol;
+		else
+			audio_dev_ctrl.voice_rx_dev->dev_volume = vol;
+	} else
+		return -ENODEV;
+	return 0;
+}
+EXPORT_SYMBOL(snddev_voice_set_volume);
+
+static int audio_dev_ctrl_get_devices(struct audio_dev_ctrl_state *dev_ctrl,
+				      void __user *arg)
+{
+	int rc = 0;
+	u32 index;
+	struct msm_snd_device_list work_list;
+	struct msm_snd_device_info *work_tbl;
+
+	if (copy_from_user(&work_list, arg, sizeof(work_list))) {
+		rc = -EFAULT;
+		goto error;
+	}
+
+	if (work_list.num_dev > dev_ctrl->num_dev) {
+		rc = -EINVAL;
+		goto error;
+	}
+
+	work_tbl = kmalloc(work_list.num_dev *
+		sizeof(struct msm_snd_device_info), GFP_KERNEL);
+	if (!work_tbl) {
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	for (index = 0; index < dev_ctrl->num_dev; index++) {
+		work_tbl[index].dev_id = index;
+		work_tbl[index].dev_cap = dev_ctrl->devs[index]->capability;
+		strlcpy(work_tbl[index].dev_name, dev_ctrl->devs[index]->name,
+		64);
+	}
+
+	if (copy_to_user((void *) (work_list.list), work_tbl,
+		 work_list.num_dev * sizeof(struct msm_snd_device_info)))
+		rc = -EFAULT;
+	kfree(work_tbl);
+error:
+	return rc;
+}
+
+
+int auddev_register_evt_listner(u32 evt_id, u32 clnt_type, u32 clnt_id,
+		void (*listner)(u32 evt_id,
+			union auddev_evt_data *evt_payload,
+			void *private_data),
+		void *private_data)
+{
+	int rc;
+	struct msm_snd_evt_listner *callback = NULL;
+	struct msm_snd_evt_listner *new_cb;
+
+	new_cb = kzalloc(sizeof(struct msm_snd_evt_listner), GFP_KERNEL);
+	if (!new_cb) {
+		MM_ERR("No memory to add new listener node\n");
+		return -ENOMEM;
+	}
+
+	mutex_lock(&session_lock);
+	new_cb->cb_next = NULL;
+	new_cb->auddev_evt_listener = listner;
+	new_cb->evt_id = evt_id;
+	new_cb->clnt_type = clnt_type;
+	new_cb->clnt_id = clnt_id;
+	new_cb->private_data = private_data;
+	if (event.cb == NULL) {
+		event.cb = new_cb;
+		new_cb->cb_prev = NULL;
+	} else {
+		callback = event.cb;
+		for (; ;) {
+			if (callback->cb_next == NULL)
+				break;
+			else {
+				callback = callback->cb_next;
+				continue;
+			}
+		}
+		callback->cb_next = new_cb;
+		new_cb->cb_prev = callback;
+	}
+	event.num_listner++;
+	mutex_unlock(&session_lock);
+	rc = 0;
+	return rc;
+}
+EXPORT_SYMBOL(auddev_register_evt_listner);
+
+int auddev_unregister_evt_listner(u32 clnt_type, u32 clnt_id)
+{
+	struct msm_snd_evt_listner *callback = event.cb;
+	struct msm_snddev_info *info;
+	u32 session_mask = 0;
+	int i = 0;
+
+	mutex_lock(&session_lock);
+	while (callback != NULL) {
+		if ((callback->clnt_type == clnt_type)
+			&& (callback->clnt_id == clnt_id))
+			break;
+		 callback = callback->cb_next;
+	}
+	if (callback == NULL) {
+		mutex_unlock(&session_lock);
+		return -EINVAL;
+	}
+
+	if ((callback->cb_next == NULL) && (callback->cb_prev == NULL))
+		event.cb = NULL;
+	else if (callback->cb_next == NULL)
+		callback->cb_prev->cb_next = NULL;
+	else if (callback->cb_prev == NULL) {
+		callback->cb_next->cb_prev = NULL;
+		event.cb = callback->cb_next;
+	} else {
+		callback->cb_prev->cb_next = callback->cb_next;
+		callback->cb_next->cb_prev = callback->cb_prev;
+	}
+	kfree(callback);
+
+	session_mask = (0x1 << (clnt_id)) << (8 * ((int)clnt_type-1));
+	for (i = 0; i < audio_dev_ctrl.num_dev; i++) {
+		info = audio_dev_ctrl.devs[i];
+		info->sessions &= ~session_mask;
+	}
+	if (clnt_type == AUDDEV_CLNT_ENC)
+		msm_set_dual_mic_config(clnt_id, 0);
+	mutex_unlock(&session_lock);
+	return 0;
+}
+EXPORT_SYMBOL(auddev_unregister_evt_listner);
+
+int msm_snddev_withdraw_freq(u32 session_id, u32 capability, u32 clnt_type)
+{
+	int i = 0;
+	struct msm_snddev_info *info;
+	u32 session_mask = 0;
+
+	if ((clnt_type == AUDDEV_CLNT_VOC) && (session_id != 0))
+		return -EINVAL;
+	if ((clnt_type == AUDDEV_CLNT_DEC)
+			&& (session_id >= MAX_DEC_SESSIONS))
+		return -EINVAL;
+	if ((clnt_type == AUDDEV_CLNT_ENC)
+			&& (session_id >= MAX_ENC_SESSIONS))
+		return -EINVAL;
+
+	session_mask = (0x1 << (session_id)) << (8 * ((int)clnt_type-1));
+
+	for (i = 0; i < audio_dev_ctrl.num_dev; i++) {
+		info = audio_dev_ctrl.devs[i];
+		if ((info->sessions & session_mask)
+			&& (info->capability & capability)) {
+			if (!(info->sessions & ~(session_mask)))
+				info->set_sample_rate = 0;
+		}
+	}
+	if (clnt_type == AUDDEV_CLNT_DEC)
+		routing_info.dec_freq[session_id].freq
+					= 0;
+	else if (clnt_type == AUDDEV_CLNT_ENC)
+		routing_info.enc_freq[session_id].freq
+					= 0;
+	else if (capability == SNDDEV_CAP_TX)
+		routing_info.voice_tx_sample_rate = 0;
+	else
+		routing_info.voice_rx_sample_rate = 48000;
+	return 0;
+}
+
+int msm_snddev_request_freq(int *freq, u32 session_id,
+			u32 capability, u32 clnt_type)
+{
+	int i = 0;
+	int rc = 0;
+	struct msm_snddev_info *info;
+	u32 set_freq;
+	u32 session_mask = 0;
+	u32 clnt_type_mask = 0;
+
+	MM_DBG(": clnt_type 0x%08x\n", clnt_type);
+
+	if ((clnt_type == AUDDEV_CLNT_VOC) && (session_id != 0))
+		return -EINVAL;
+	if ((clnt_type == AUDDEV_CLNT_DEC)
+			&& (session_id >= MAX_DEC_SESSIONS))
+		return -EINVAL;
+	if ((clnt_type == AUDDEV_CLNT_ENC)
+			&& (session_id >= MAX_ENC_SESSIONS))
+		return -EINVAL;
+	session_mask = ((0x1 << session_id)) << (8 * (clnt_type-1));
+	clnt_type_mask = (0xFF << (8 * (clnt_type-1)));
+	if (!(*freq == 8000) && !(*freq == 11025) &&
+		!(*freq == 12000) && !(*freq == 16000) &&
+		!(*freq == 22050) && !(*freq == 24000) &&
+		!(*freq == 32000) && !(*freq == 44100) &&
+		!(*freq == 48000))
+		return -EINVAL;
+
+	for (i = 0; i < audio_dev_ctrl.num_dev; i++) {
+		info = audio_dev_ctrl.devs[i];
+		if ((info->sessions & session_mask)
+			&& (info->capability & capability)) {
+			rc = 0;
+			if ((info->sessions & ~clnt_type_mask)
+				&& ((*freq != 8000) && (*freq != 16000)
+					&& (*freq != 48000))) {
+				if (clnt_type == AUDDEV_CLNT_ENC) {
+					routing_info.enc_freq[session_id].freq
+							= 0;
+					return -EPERM;
+				} else if (clnt_type == AUDDEV_CLNT_DEC) {
+					routing_info.dec_freq[session_id].freq
+							= 0;
+					return -EPERM;
+				}
+			}
+			if (*freq == info->set_sample_rate) {
+				rc = info->set_sample_rate;
+				continue;
+			}
+			set_freq = MAX(*freq, info->set_sample_rate);
+
+
+			if (clnt_type == AUDDEV_CLNT_DEC)
+				routing_info.dec_freq[session_id].freq
+						= set_freq;
+			else if (clnt_type == AUDDEV_CLNT_ENC)
+				routing_info.enc_freq[session_id].freq
+						= set_freq;
+			else if (capability == SNDDEV_CAP_TX)
+				routing_info.voice_tx_sample_rate = set_freq;
+
+			rc = set_freq;
+			*freq = set_freq;
+			/* There is difference in device sample rate to
+			 * requested sample rate. So update device sample rate
+			 * and propagate sample rate change event to active
+			 * sessions of the device.
+			 */
+			if (info->set_sample_rate != set_freq) {
+				info->set_sample_rate = set_freq;
+				if (info->opened) {
+					/* Ignore propagating sample rate
+					 * change event to requested client
+					 * session
+					 */
+					if (clnt_type == AUDDEV_CLNT_DEC)
+						routing_info.\
+						dec_freq[session_id].evt = 1;
+					else if (clnt_type == AUDDEV_CLNT_ENC)
+						routing_info.\
+						enc_freq[session_id].evt = 1;
+					broadcast_event(AUDDEV_EVT_FREQ_CHG, i,
+								SESSION_IGNORE);
+					set_freq = info->dev_ops.set_freq(info,
+								set_freq);
+					broadcast_event(AUDDEV_EVT_DEV_RDY, i,
+								SESSION_IGNORE);
+				}
+			}
+		}
+		MM_DBG("info->set_sample_rate = %d\n", info->set_sample_rate);
+		MM_DBG("routing_info.enc_freq.freq = %d\n",
+					routing_info.enc_freq[session_id].freq);
+	}
+	return rc;
+}
+EXPORT_SYMBOL(msm_snddev_request_freq);
+
+int msm_snddev_enable_sidetone(u32 dev_id, u32 enable)
+{
+	int rc;
+	struct msm_snddev_info *dev_info;
+
+	MM_DBG("dev_id %d enable %d\n", dev_id, enable);
+
+	dev_info = audio_dev_ctrl_find_dev(dev_id);
+
+	if (IS_ERR(dev_info)) {
+		MM_ERR("bad dev_id %d\n", dev_id);
+		rc = -EINVAL;
+	} else if (!dev_info->dev_ops.enable_sidetone) {
+		MM_DBG("dev %d no sidetone support\n", dev_id);
+		rc = -EPERM;
+	} else
+		rc = dev_info->dev_ops.enable_sidetone(dev_info, enable);
+
+	return rc;
+}
+EXPORT_SYMBOL(msm_snddev_enable_sidetone);
+
+static long audio_dev_ctrl_ioctl(struct file *file,
+				 unsigned int cmd, unsigned long arg)
+{
+	int rc = 0;
+	struct audio_dev_ctrl_state *dev_ctrl = file->private_data;
+
+	mutex_lock(&session_lock);
+	switch (cmd) {
+	case AUDIO_GET_NUM_SND_DEVICE:
+		rc = put_user(dev_ctrl->num_dev, (uint32_t __user *) arg);
+		break;
+	case AUDIO_GET_SND_DEVICES:
+		rc = audio_dev_ctrl_get_devices(dev_ctrl, (void __user *) arg);
+		break;
+	case AUDIO_ENABLE_SND_DEVICE: {
+		struct msm_snddev_info *dev_info;
+		u32 dev_id;
+
+		if (get_user(dev_id, (u32 __user *) arg)) {
+			rc = -EFAULT;
+			break;
+		}
+		dev_info = audio_dev_ctrl_find_dev(dev_id);
+		if (IS_ERR(dev_info))
+			rc = PTR_ERR(dev_info);
+		else {
+			rc = dev_info->dev_ops.open(dev_info);
+			if (!rc)
+				dev_info->opened = 1;
+			wake_up(&audio_dev_ctrl.wait);
+		}
+		break;
+
+	}
+
+	case AUDIO_DISABLE_SND_DEVICE: {
+		struct msm_snddev_info *dev_info;
+		u32 dev_id;
+
+		if (get_user(dev_id, (u32 __user *) arg)) {
+			rc = -EFAULT;
+			break;
+		}
+		dev_info = audio_dev_ctrl_find_dev(dev_id);
+		if (IS_ERR(dev_info))
+			rc = PTR_ERR(dev_info);
+		else {
+			rc = dev_info->dev_ops.close(dev_info);
+			dev_info->opened = 0;
+		}
+		break;
+	}
+
+	case AUDIO_ROUTE_STREAM: {
+		struct msm_audio_route_config route_cfg;
+		struct msm_snddev_info *dev_info;
+
+		if (copy_from_user(&route_cfg, (void __user *) arg,
+			sizeof(struct msm_audio_route_config))) {
+			rc = -EFAULT;
+			break;
+		}
+		MM_DBG("%s: route cfg %d %d type\n", __func__,
+		route_cfg.dev_id, route_cfg.stream_type);
+		dev_info = audio_dev_ctrl_find_dev(route_cfg.dev_id);
+		if (IS_ERR(dev_info)) {
+			MM_ERR("%s: pass invalid dev_id\n", __func__);
+			rc = PTR_ERR(dev_info);
+			break;
+		}
+
+		switch (route_cfg.stream_type) {
+
+		case AUDIO_ROUTE_STREAM_VOICE_RX:
+			if (!(dev_info->capability & SNDDEV_CAP_RX) |
+			    !(dev_info->capability & SNDDEV_CAP_VOICE)) {
+				rc = -EINVAL;
+				break;
+			}
+			dev_ctrl->voice_rx_dev = dev_info;
+			break;
+		case AUDIO_ROUTE_STREAM_VOICE_TX:
+			if (!(dev_info->capability & SNDDEV_CAP_TX) |
+			    !(dev_info->capability & SNDDEV_CAP_VOICE)) {
+				rc = -EINVAL;
+				break;
+			}
+			dev_ctrl->voice_tx_dev = dev_info;
+			break;
+		}
+		break;
+	}
+
+	default:
+		rc = -EINVAL;
+	}
+	mutex_unlock(&session_lock);
+	return rc;
+}
+
+static int audio_dev_ctrl_open(struct inode *inode, struct file *file)
+{
+	MM_DBG("open audio_dev_ctrl\n");
+	atomic_inc(&audio_dev_ctrl.opened);
+	file->private_data = &audio_dev_ctrl;
+	return 0;
+}
+
+static int audio_dev_ctrl_release(struct inode *inode, struct file *file)
+{
+	MM_DBG("release audio_dev_ctrl\n");
+	atomic_dec(&audio_dev_ctrl.opened);
+	return 0;
+}
+
+static const struct file_operations audio_dev_ctrl_fops = {
+	.owner = THIS_MODULE,
+	.open = audio_dev_ctrl_open,
+	.release = audio_dev_ctrl_release,
+	.unlocked_ioctl = audio_dev_ctrl_ioctl,
+};
+
+
+struct miscdevice audio_dev_ctrl_misc = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "msm_audio_dev_ctrl",
+	.fops	= &audio_dev_ctrl_fops,
+};
+
+/* session id is 32 bit routing mask per device
+ * 0-7 for voice clients
+ * 8-15 for Decoder clients
+ * 16-23 for Encoder clients
+ * 24-31 Do not care
+ */
+void broadcast_event(u32 evt_id, u32 dev_id, u32 session_id)
+{
+	int clnt_id = 0, i;
+	union auddev_evt_data *evt_payload;
+	struct msm_snd_evt_listner *callback;
+	struct msm_snddev_info *dev_info = NULL;
+	u32 session_mask = 0;
+	static int pending_sent;
+
+	MM_DBG(": evt_id = %d\n", evt_id);
+
+	if ((evt_id != AUDDEV_EVT_START_VOICE)
+		&& (evt_id != AUDDEV_EVT_END_VOICE)
+		&& (evt_id != AUDDEV_EVT_STREAM_VOL_CHG)
+		&& (evt_id != AUDDEV_EVT_VOICE_STATE_CHG)) {
+		dev_info = audio_dev_ctrl_find_dev(dev_id);
+		if (IS_ERR(dev_info)) {
+			MM_ERR("pass invalid dev_id\n");
+			return;
+		}
+	}
+
+	if (event.cb != NULL)
+		callback = event.cb;
+	else
+		return;
+
+	evt_payload = kzalloc(sizeof(union auddev_evt_data),
+			GFP_KERNEL);
+	if (evt_payload == NULL) {
+		MM_ERR("Memory allocation for event payload failed\n");
+		return;
+	}
+
+	mutex_lock(&session_lock);
+
+	if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG)
+		routing_info.voice_state = dev_id;
+
+	for (; ;) {
+		if (!(evt_id & callback->evt_id)) {
+			if (callback->cb_next == NULL)
+				break;
+			else {
+				callback = callback->cb_next;
+				continue;
+			}
+		}
+		clnt_id = callback->clnt_id;
+		memset(evt_payload, 0, sizeof(union auddev_evt_data));
+
+		if ((evt_id == AUDDEV_EVT_START_VOICE)
+			|| (evt_id == AUDDEV_EVT_END_VOICE))
+			goto skip_check;
+		if (callback->clnt_type == AUDDEV_CLNT_AUDIOCAL)
+			goto aud_cal;
+
+		session_mask = (0x1 << (clnt_id))
+				<< (8 * ((int)callback->clnt_type-1));
+
+		if ((evt_id == AUDDEV_EVT_STREAM_VOL_CHG) || \
+			(evt_id == AUDDEV_EVT_VOICE_STATE_CHG)) {
+			MM_DBG("AUDDEV_EVT_STREAM_VOL_CHG or\
+				AUDDEV_EVT_VOICE_STATE_CHG\n");
+			goto volume_strm;
+		}
+
+		MM_DBG("dev_info->sessions = %08x\n", dev_info->sessions);
+
+		if ((!session_id && !(dev_info->sessions & session_mask)) ||
+			(session_id && ((dev_info->sessions & session_mask) !=
+						session_id))) {
+			if (callback->cb_next == NULL)
+				break;
+			else {
+				callback = callback->cb_next;
+				continue;
+			}
+		}
+		if (evt_id == AUDDEV_EVT_DEV_CHG_VOICE)
+			goto voc_events;
+
+volume_strm:
+		if (callback->clnt_type == AUDDEV_CLNT_DEC) {
+			MM_DBG("AUDDEV_CLNT_DEC\n");
+			if (evt_id == AUDDEV_EVT_STREAM_VOL_CHG) {
+				MM_DBG("clnt_id = %d, session_id = 0x%8x\n",
+					clnt_id, session_id);
+				if (session_mask != session_id)
+					goto sent_dec;
+				else
+					evt_payload->session_vol =
+						msm_vol_ctl.volume;
+			} else if (evt_id == AUDDEV_EVT_FREQ_CHG) {
+				if (routing_info.dec_freq[clnt_id].evt) {
+					routing_info.dec_freq[clnt_id].evt
+							= 0;
+					goto sent_dec;
+				} else if (routing_info.dec_freq[clnt_id].freq
+					== dev_info->set_sample_rate)
+					goto sent_dec;
+				else {
+					evt_payload->freq_info.sample_rate
+						= dev_info->set_sample_rate;
+					evt_payload->freq_info.dev_type
+						= dev_info->capability;
+					evt_payload->freq_info.acdb_dev_id
+						= dev_info->acdb_id;
+				}
+			/* Propogate device information to client */
+			} else if (evt_id == AUDDEV_EVT_DEVICE_INFO) {
+				evt_payload->devinfo.dev_id
+					= dev_info->copp_id;
+				evt_payload->devinfo.acdb_id
+					= dev_info->acdb_id;
+				evt_payload->devinfo.dev_type =
+					(dev_info->capability & SNDDEV_CAP_TX) ?
+					SNDDEV_CAP_TX : SNDDEV_CAP_RX;
+				evt_payload->devinfo.sample_rate
+					= dev_info->sample_rate;
+				if (session_id == SESSION_IGNORE)
+					evt_payload->devinfo.sessions
+					= dev_info->sessions;
+				else
+					evt_payload->devinfo.sessions
+					= session_id;
+				evt_payload->devinfo.sessions =
+					(evt_payload->devinfo.sessions >>
+						((AUDDEV_CLNT_DEC-1) * 8));
+			} else if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG)
+				evt_payload->voice_state =
+					routing_info.voice_state;
+			else
+				evt_payload->routing_id = dev_info->copp_id;
+			callback->auddev_evt_listener(
+					evt_id,
+					evt_payload,
+					callback->private_data);
+sent_dec:
+			if ((evt_id != AUDDEV_EVT_STREAM_VOL_CHG) &&
+				(evt_id != AUDDEV_EVT_VOICE_STATE_CHG))
+				routing_info.dec_freq[clnt_id].freq
+						= dev_info->set_sample_rate;
+
+			if (callback->cb_next == NULL)
+				break;
+			else {
+				callback = callback->cb_next;
+				continue;
+			}
+		}
+		if (callback->clnt_type == AUDDEV_CLNT_ENC) {
+
+			MM_DBG("AUDDEV_CLNT_ENC\n");
+			if (evt_id == AUDDEV_EVT_FREQ_CHG) {
+				if (routing_info.enc_freq[clnt_id].evt) {
+					routing_info.enc_freq[clnt_id].evt
+							= 0;
+					goto sent_enc;
+				 } else {
+					evt_payload->freq_info.sample_rate
+						= dev_info->set_sample_rate;
+					evt_payload->freq_info.dev_type
+						= dev_info->capability;
+					evt_payload->freq_info.acdb_dev_id
+						= dev_info->acdb_id;
+				}
+			/* Propogate device information to client */
+			} else if (evt_id == AUDDEV_EVT_DEVICE_INFO) {
+				evt_payload->devinfo.dev_id
+					= dev_info->copp_id;
+				evt_payload->devinfo.acdb_id
+					= dev_info->acdb_id;
+				evt_payload->devinfo.dev_type =
+					(dev_info->capability & SNDDEV_CAP_TX) ?
+					SNDDEV_CAP_TX : SNDDEV_CAP_RX;
+				evt_payload->devinfo.sample_rate
+					= dev_info->sample_rate;
+				if (session_id == SESSION_IGNORE)
+					evt_payload->devinfo.sessions
+					= dev_info->sessions;
+				else
+					evt_payload->devinfo.sessions
+					= session_id;
+				evt_payload->devinfo.sessions =
+					(evt_payload->devinfo.sessions >>
+						((AUDDEV_CLNT_ENC-1) * 8));
+			} else if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG)
+				evt_payload->voice_state =
+					routing_info.voice_state;
+			else
+				evt_payload->routing_id = dev_info->copp_id;
+			callback->auddev_evt_listener(
+					evt_id,
+					evt_payload,
+					callback->private_data);
+sent_enc:
+			if (callback->cb_next == NULL)
+					break;
+			else {
+				callback = callback->cb_next;
+				continue;
+			}
+		}
+aud_cal:
+		if (callback->clnt_type == AUDDEV_CLNT_AUDIOCAL) {
+			int temp_sessions;
+			MM_DBG("AUDDEV_CLNT_AUDIOCAL\n");
+			if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG)
+				evt_payload->voice_state =
+					routing_info.voice_state;
+			else if (!dev_info->sessions)
+				goto sent_aud_cal;
+			else {
+				evt_payload->audcal_info.dev_id =
+						dev_info->copp_id;
+				evt_payload->audcal_info.acdb_id =
+						dev_info->acdb_id;
+				evt_payload->audcal_info.dev_type =
+					(dev_info->capability & SNDDEV_CAP_TX) ?
+					SNDDEV_CAP_TX : SNDDEV_CAP_RX;
+				evt_payload->audcal_info.sample_rate =
+					dev_info->set_sample_rate ?
+					dev_info->set_sample_rate :
+					dev_info->sample_rate;
+			}
+			if (evt_payload->audcal_info.dev_type ==
+						SNDDEV_CAP_TX) {
+				if (session_id == SESSION_IGNORE)
+					temp_sessions = dev_info->sessions;
+				else
+					temp_sessions = session_id;
+				evt_payload->audcal_info.sessions =
+					(temp_sessions >>
+						((AUDDEV_CLNT_ENC-1) * 8));
+			} else {
+				if (session_id == SESSION_IGNORE)
+					temp_sessions = dev_info->sessions;
+				else
+					temp_sessions = session_id;
+				evt_payload->audcal_info.sessions =
+					(temp_sessions >>
+						((AUDDEV_CLNT_DEC-1) * 8));
+			}
+			callback->auddev_evt_listener(
+				evt_id,
+				evt_payload,
+				callback->private_data);
+
+sent_aud_cal:
+			if (callback->cb_next == NULL)
+				break;
+			else {
+				callback = callback->cb_next;
+				continue;
+			}
+		}
+skip_check:
+voc_events:
+		if (callback->clnt_type == AUDDEV_CLNT_VOC) {
+			MM_DBG("AUDDEV_CLNT_VOC\n");
+			if (evt_id == AUDDEV_EVT_DEV_RLS) {
+				if (!pending_sent)
+					goto sent_voc;
+				else
+					pending_sent = 0;
+			}
+			if (evt_id == AUDDEV_EVT_REL_PENDING)
+				pending_sent = 1;
+
+			if (evt_id == AUDDEV_EVT_DEVICE_VOL_MUTE_CHG) {
+				if (dev_info->capability & SNDDEV_CAP_TX) {
+					evt_payload->voc_vm_info.dev_type =
+						SNDDEV_CAP_TX;
+					evt_payload->voc_vm_info.acdb_dev_id =
+						dev_info->acdb_id;
+					evt_payload->
+					voc_vm_info.dev_vm_val.mute =
+						routing_info.tx_mute;
+				} else {
+					evt_payload->voc_vm_info.dev_type =
+						SNDDEV_CAP_RX;
+					evt_payload->voc_vm_info.acdb_dev_id =
+						dev_info->acdb_id;
+					evt_payload->
+					voc_vm_info.dev_vm_val.vol =
+						routing_info.voice_rx_vol;
+				}
+			} else if ((evt_id == AUDDEV_EVT_START_VOICE)
+					|| (evt_id == AUDDEV_EVT_END_VOICE))
+				memset(evt_payload, 0,
+					sizeof(union auddev_evt_data));
+			else if (evt_id == AUDDEV_EVT_FREQ_CHG) {
+				if (routing_info.voice_tx_sample_rate
+						!= dev_info->set_sample_rate) {
+					routing_info.voice_tx_sample_rate
+						= dev_info->set_sample_rate;
+					evt_payload->freq_info.sample_rate
+						= dev_info->set_sample_rate;
+					evt_payload->freq_info.dev_type
+						= dev_info->capability;
+					evt_payload->freq_info.acdb_dev_id
+						= dev_info->acdb_id;
+				} else
+					goto sent_voc;
+			} else if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG)
+				evt_payload->voice_state =
+						routing_info.voice_state;
+			else {
+				evt_payload->voc_devinfo.dev_type =
+					(dev_info->capability & SNDDEV_CAP_TX) ?
+					SNDDEV_CAP_TX : SNDDEV_CAP_RX;
+				evt_payload->voc_devinfo.acdb_dev_id =
+					dev_info->acdb_id;
+				evt_payload->voc_devinfo.dev_sample =
+					dev_info->set_sample_rate ?
+					dev_info->set_sample_rate :
+					dev_info->sample_rate;
+				evt_payload->voc_devinfo.dev_id = dev_id;
+				if (dev_info->capability & SNDDEV_CAP_RX) {
+					for (i = 0; i < VOC_RX_VOL_ARRAY_NUM;
+						i++) {
+						evt_payload->
+						voc_devinfo.max_rx_vol[i] =
+						dev_info->max_voc_rx_vol[i];
+						evt_payload
+						->voc_devinfo.min_rx_vol[i] =
+						dev_info->min_voc_rx_vol[i];
+					}
+				}
+			}
+			callback->auddev_evt_listener(
+				evt_id,
+				evt_payload,
+				callback->private_data);
+			if (evt_id == AUDDEV_EVT_DEV_RLS)
+				dev_info->sessions &= ~(0xFF);
+sent_voc:
+			if (callback->cb_next == NULL)
+				break;
+			else {
+				callback = callback->cb_next;
+				continue;
+			}
+		}
+	}
+	kfree(evt_payload);
+	mutex_unlock(&session_lock);
+}
+EXPORT_SYMBOL(broadcast_event);
+
+
+void mixer_post_event(u32 evt_id, u32 id)
+{
+
+	MM_DBG("evt_id = %d\n", evt_id);
+	switch (evt_id) {
+	case AUDDEV_EVT_DEV_CHG_VOICE: /* Called from Voice_route */
+		broadcast_event(AUDDEV_EVT_DEV_CHG_VOICE, id, SESSION_IGNORE);
+		break;
+	case AUDDEV_EVT_DEV_RDY:
+		broadcast_event(AUDDEV_EVT_DEV_RDY, id, SESSION_IGNORE);
+		break;
+	case AUDDEV_EVT_DEV_RLS:
+		broadcast_event(AUDDEV_EVT_DEV_RLS, id, SESSION_IGNORE);
+		break;
+	case AUDDEV_EVT_REL_PENDING:
+		broadcast_event(AUDDEV_EVT_REL_PENDING, id, SESSION_IGNORE);
+		break;
+	case AUDDEV_EVT_DEVICE_VOL_MUTE_CHG:
+		broadcast_event(AUDDEV_EVT_DEVICE_VOL_MUTE_CHG, id,
+							SESSION_IGNORE);
+		break;
+	case AUDDEV_EVT_STREAM_VOL_CHG:
+		broadcast_event(AUDDEV_EVT_STREAM_VOL_CHG, id,
+							SESSION_IGNORE);
+		break;
+	case AUDDEV_EVT_START_VOICE:
+		broadcast_event(AUDDEV_EVT_START_VOICE,
+				id, SESSION_IGNORE);
+		break;
+	case AUDDEV_EVT_END_VOICE:
+		broadcast_event(AUDDEV_EVT_END_VOICE,
+				id, SESSION_IGNORE);
+		break;
+	case AUDDEV_EVT_FREQ_CHG:
+		broadcast_event(AUDDEV_EVT_FREQ_CHG, id, SESSION_IGNORE);
+		break;
+	default:
+		break;
+	}
+}
+EXPORT_SYMBOL(mixer_post_event);
+
+static int __init audio_dev_ctrl_init(void)
+{
+#ifdef CONFIG_DEBUG_FS
+	char name[sizeof "rtc_get_device"+1];
+#endif
+
+	init_waitqueue_head(&audio_dev_ctrl.wait);
+
+	event.cb = NULL;
+
+	atomic_set(&audio_dev_ctrl.opened, 0);
+	audio_dev_ctrl.num_dev = 0;
+	audio_dev_ctrl.voice_tx_dev = NULL;
+	audio_dev_ctrl.voice_rx_dev = NULL;
+	routing_info.voice_state = VOICE_STATE_INVALID;
+#ifdef CONFIG_DEBUG_FS
+	snprintf(name, sizeof name, "rtc_get_device");
+	dentry = debugfs_create_file(name, S_IFREG | S_IRUGO | S_IWUGO,
+			NULL, NULL, &rtc_acdb_debug_fops);
+	if (IS_ERR(dentry))
+		MM_DBG("debugfs_create_file failed\n");
+#endif
+
+	return misc_register(&audio_dev_ctrl_misc);
+}
+
+static void __exit audio_dev_ctrl_exit(void)
+{
+#ifdef CONFIG_DEBUG_FS
+	if (dentry)
+		debugfs_remove(dentry);
+#endif
+
+}
+module_init(audio_dev_ctrl_init);
+module_exit(audio_dev_ctrl_exit);
+
+MODULE_DESCRIPTION("MSM 7K Audio Device Control driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_evrc.c b/arch/arm/mach-msm/qdsp5v2/audio_evrc.c
new file mode 100644
index 0000000..8e7db90
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_evrc.c
@@ -0,0 +1,1624 @@
+/*
+ * Copyright (c) 2008-2010, 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 <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+#include <linux/msm_audio.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <linux/slab.h>
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/qdsp5audplaycmdi.h>
+#include <mach/qdsp5v2/qdsp5audplaymsg.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/qdsp5v2/audpp.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;
+
+	/* 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 */
+
+	const char *module_name;
+	unsigned queue_id;
+	uint16_t dec_id;
+	uint32_t read_ptr_offset;
+	int16_t source;
+
+#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;
+	/* AV sync Info */
+	int avsync_flag;              /* Flag to indicate feedback from DSP */
+	wait_queue_head_t avsync_wait;/* Wait queue for AV Sync Message     */
+	/* flags, 48 bits sample/bytes counter per channel */
+	uint16_t avsync[AUDPP_AVSYNC_CH_COUNT * AUDPP_AVSYNC_NUM_WORDS + 1];
+
+	uint32_t device_events;
+
+	int eq_enable;
+	int eq_needs_commit;
+	struct audpp_cmd_cfg_object_params_eqalizer eq;
+	struct 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);
+static void audevrc_post_event(struct audio *audio, int type,
+		union msm_audio_event_payload payload);
+
+/* must be called with audio->lock held */
+static int audevrc_enable(struct audio *audio)
+{
+	if (audio->enabled)
+		return 0;
+
+	audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+	audio->out_tail = 0;
+	audio->out_needed = 0;
+
+	if (msm_adsp_enable(audio->audplay)) {
+		MM_ERR("msm_adsp_enable(audplay) failed\n");
+		return -ENODEV;
+	}
+
+	if (audpp_enable(audio->dec_id, audevrc_dsp_event, audio)) {
+		MM_ERR("audpp_enable() failed\n");
+		msm_adsp_disable(audio->audplay);
+		return -ENODEV;
+	}
+	audio->enabled = 1;
+	return 0;
+}
+
+static void evrc_listner(u32 evt_id, union auddev_evt_data *evt_payload,
+			void *private_data)
+{
+	struct audio *audio = (struct audio *) private_data;
+	switch (evt_id) {
+	case AUDDEV_EVT_DEV_RDY:
+		MM_DBG(":AUDDEV_EVT_DEV_RDY\n");
+		audio->source |= (0x1 << evt_payload->routing_id);
+		if (audio->running == 1 && audio->enabled == 1)
+			audpp_route_stream(audio->dec_id, audio->source);
+		break;
+	case AUDDEV_EVT_DEV_RLS:
+		MM_DBG(":AUDDEV_EVT_DEV_RLS\n");
+		audio->source &= ~(0x1 << evt_payload->routing_id);
+		if (audio->running == 1 && audio->enabled == 1)
+			audpp_route_stream(audio->dec_id, audio->source);
+		break;
+	case AUDDEV_EVT_STREAM_VOL_CHG:
+		audio->vol_pan.volume = evt_payload->session_vol;
+		MM_DBG(":AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n",
+				audio->vol_pan.volume);
+		if (audio->running)
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+					POPP);
+		break;
+	default:
+		MM_ERR(":ERROR:wrong event\n");
+		break;
+	}
+}
+/* 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);
+		audio->out_needed = 0;
+	}
+	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");
+				audpp_route_stream(audio->dec_id,
+						audio->source);
+				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,
+					POPP);
+			audpp_dsp_set_eq(audio->dec_id,	audio->eq_enable,
+					&audio->eq, POPP);
+		} else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+			MM_DBG("CFG_MSG DISABLE\n");
+			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;
+
+	case AUDPP_MSG_AVSYNC_MSG:
+		MM_DBG("AUDPP_MSG_AVSYNC_MSG\n");
+		memcpy(&audio->avsync[0], msg, sizeof(audio->avsync));
+		audio->avsync_flag = 1;
+		wake_up(&audio->avsync_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)
+{
+	struct audpp_cmd_cfg_dec_type cfg_dec_cmd;
+
+	memset(&cfg_dec_cmd, 0, sizeof(cfg_dec_cmd));
+
+	cfg_dec_cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+	if (enable)
+		cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+				AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_EVRC;
+	else
+		cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+				AUDPP_CMD_DIS_DEC_V;
+	cfg_dec_cmd.dm_mode = 0x0;
+	cfg_dec_cmd.stream_id = audio->dec_id;
+
+	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;
+	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);
+	audio->avsync_flag = 1;
+	wake_up(&audio->avsync_wait);
+}
+
+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, POPP);
+		audio->eq_needs_commit = 0;
+	}
+	return 0;
+}
+
+static int audio_get_avsync_data(struct audio *audio,
+						struct msm_audio_stats *stats)
+{
+	int rc = -EINVAL;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	if (audio->dec_id == audio->avsync[0] && audio->avsync_flag) {
+		/* av_sync sample count */
+		stats->sample_count = (audio->avsync[2] << 16) |
+						(audio->avsync[3]);
+
+		/* av_sync byte_count */
+		stats->byte_count = (audio->avsync[5] << 16) |
+						(audio->avsync[6]);
+
+		audio->avsync_flag = 0;
+		rc = 0;
+	}
+	local_irq_restore(flags);
+	return rc;
+
+}
+
+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;
+
+		audio->avsync_flag = 0;
+		memset(&stats, 0, sizeof(stats));
+		if (audpp_query_avsync(audio->dec_id) < 0)
+			return rc;
+
+		rc = wait_event_interruptible_timeout(audio->avsync_wait,
+				(audio->avsync_flag == 1),
+				msecs_to_jiffies(AUDPP_AVSYNC_EVENT_TIMEOUT));
+
+		if (rc < 0)
+			return rc;
+		else if ((rc > 0) || ((rc == 0) && (audio->avsync_flag == 1))) {
+			if (audio_get_avsync_data(audio, &stats) < 0)
+				return rc;
+
+			if (copy_to_user((void *)arg, &stats, sizeof(stats)))
+				return -EFAULT;
+			return 0;
+		} else
+			return -EAGAIN;
+	}
+
+	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,
+					POPP);
+		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,
+					POPP);
+		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;
+	case AUDIO_GET_SESSION_ID:
+		if (copy_to_user((void *) arg, &audio->dec_id,
+				sizeof(unsigned short)))
+			rc = -EFAULT;
+		else
+			rc = 0;
+		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);
+			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);
+	auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id);
+	audevrc_disable(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;
+}
+
+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);
+			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 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);
+	}
+
+	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);
+		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);
+	init_waitqueue_head(&audio->avsync_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 = 0x3FFF;
+
+	audevrc_flush(audio);
+
+	file->private_data = audio;
+	audio->opened = 1;
+
+	audio->device_events = AUDDEV_EVT_DEV_RDY
+				|AUDDEV_EVT_DEV_RLS|
+				AUDDEV_EVT_STREAM_VOL_CHG;
+
+	rc = auddev_register_evt_listner(audio->device_events,
+					AUDDEV_CLNT_DEC,
+					audio->dec_id,
+					evrc_listner,
+					(void *)audio);
+	if (rc) {
+		MM_ERR("%s: failed to register listner\n", __func__);
+		goto event_err;
+	}
+
+#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;
+event_err:
+	msm_adsp_put(audio->audplay);
+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/qdsp5v2/audio_evrc_in.c b/arch/arm/mach-msm/qdsp5v2/audio_evrc_in.c
new file mode 100644
index 0000000..763856e
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_evrc_in.c
@@ -0,0 +1,1490 @@
+/*
+ * evrc audio input 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/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/msm_audio_qcp.h>
+#include <linux/android_pmem.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+
+#include <mach/msm_adsp.h>
+#include <mach/qdsp5v2/qdsp5audreccmdi.h>
+#include <mach/qdsp5v2/qdsp5audrecmsg.h>
+#include <mach/qdsp5v2/audpreproc.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/debug_mm.h>
+
+#define META_OUT_SIZE	24
+/* FRAME_NUM must be a power of two */
+#define FRAME_NUM	8
+#define EVRC_FRAME_SIZE	36 /* 36 bytes data */
+#define FRAME_SIZE	(22 * 2) /* 36 bytes data */
+ /* 36 bytes data  + 24 meta field*/
+#define NT_FRAME_SIZE	(EVRC_FRAME_SIZE + META_OUT_SIZE)
+#define DMASZ		(NT_FRAME_SIZE * FRAME_NUM)
+#define OUT_FRAME_NUM	2
+#define OUT_BUFFER_SIZE (4 * 1024 + META_OUT_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_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 audrec_session_info session_info; /*audrec session info*/
+
+	/* configuration to use on next enable */
+	uint32_t buffer_size; /* Frame size (36 bytes) */
+	uint32_t samp_rate;
+	uint32_t channel_mode;
+	uint32_t enc_type;
+
+	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 mode;
+	uint32_t eos_ack;
+	uint32_t flush_ack;
+
+	const char *module_name;
+	unsigned queue_ids;
+	uint16_t enc_id;
+
+	uint16_t source; /* Encoding source bit mask */
+	uint32_t device_events;
+	uint32_t in_call;
+	uint32_t dev_cnt;
+	int voice_state;
+	spinlock_t dev_lock;
+
+	/* 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[]; /* samples */
+} __attribute__((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 */
+} __attribute__((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 audrec_send_bitstreamqueue(audio, cmd, len) \
+	msm_adsp_write(audio->audrec, ((audio->queue_ids & 0xFFFF0000) >> 16),\
+			cmd, len)
+
+#define audrec_send_audrecqueue(audio, cmd, len) \
+	msm_adsp_write(audio->audrec, (audio->queue_ids & 0x0000FFFF),\
+			cmd, len)
+
+/* DSP command send functions */
+static int audevrc_in_enc_config(struct audio_in *audio, int enable);
+static int audevrc_in_param_config(struct audio_in *audio);
+static int audevrc_in_mem_config(struct audio_in *audio);
+static int audevrc_in_record_config(struct audio_in *audio, int enable);
+static int audevrc_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt);
+
+static void audevrc_in_get_dsp_frames(struct audio_in *audio);
+static int audpcm_config(struct audio_in *audio);
+static void audevrc_out_flush(struct audio_in *audio);
+static int audpreproc_cmd_cfg_routing_mode(struct audio_in *audio);
+static void audpreproc_pcm_send_data(struct audio_in *audio, unsigned needed);
+static void audevrc_nt_in_get_dsp_frames(struct audio_in *audio);
+
+static void audevrc_in_flush(struct audio_in *audio);
+
+static void evrc_in_listener(u32 evt_id, union auddev_evt_data *evt_payload,
+				void *private_data)
+{
+	struct audio_in *audio = (struct audio_in *) private_data;
+	unsigned long flags;
+
+	MM_DBG("evt_id = 0x%8x\n", evt_id);
+	switch (evt_id) {
+	case AUDDEV_EVT_DEV_RDY: {
+		MM_DBG("AUDDEV_EVT_DEV_RDY\n");
+		spin_lock_irqsave(&audio->dev_lock, flags);
+		audio->dev_cnt++;
+		if (!audio->in_call)
+			audio->source |= (0x1 << evt_payload->routing_id);
+		spin_unlock_irqrestore(&audio->dev_lock, flags);
+
+		if ((audio->running == 1) && (audio->enabled == 1) &&
+			(audio->mode == MSM_AUD_ENC_MODE_TUNNEL))
+			audevrc_in_record_config(audio, 1);
+	}
+		break;
+	case AUDDEV_EVT_DEV_RLS: {
+		MM_DBG("AUDDEV_EVT_DEV_RLS\n");
+		spin_lock_irqsave(&audio->dev_lock, flags);
+		audio->dev_cnt--;
+		if (!audio->in_call)
+			audio->source &= ~(0x1 << evt_payload->routing_id);
+		spin_unlock_irqrestore(&audio->dev_lock, flags);
+
+		if ((!audio->running) || (!audio->enabled))
+			break;
+
+		if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+			/* Turn of as per source */
+			if (audio->source)
+				audevrc_in_record_config(audio, 1);
+			else
+				/* Turn off all */
+				audevrc_in_record_config(audio, 0);
+		}
+	}
+		break;
+	case AUDDEV_EVT_VOICE_STATE_CHG: {
+		MM_DBG("AUDDEV_EVT_VOICE_STATE_CHG, state = %d\n",
+				evt_payload->voice_state);
+		audio->voice_state = evt_payload->voice_state;
+		if (audio->in_call && audio->running &&
+		   (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)) {
+			if (audio->voice_state == VOICE_STATE_INCALL)
+				audevrc_in_record_config(audio, 1);
+			else if (audio->voice_state == VOICE_STATE_OFFCALL) {
+				audevrc_in_record_config(audio, 0);
+				wake_up(&audio->wait);
+			}
+		}
+
+		break;
+	}
+	default:
+		MM_ERR("wrong event %d\n", evt_id);
+		break;
+	}
+}
+
+/* ------------------- dsp preproc event handler--------------------- */
+static void audpreproc_dsp_event(void *data, unsigned id,  void *msg)
+{
+	struct audio_in *audio = data;
+
+	switch (id) {
+	case AUDPREPROC_ERROR_MSG: {
+		struct audpreproc_err_msg *err_msg = msg;
+
+		MM_ERR("ERROR_MSG: stream id %d err idx %d\n",
+		err_msg->stream_id, err_msg->aud_preproc_err_idx);
+		/* Error case */
+		wake_up(&audio->wait_enable);
+		break;
+	}
+	case AUDPREPROC_CMD_CFG_DONE_MSG: {
+		MM_DBG("CMD_CFG_DONE_MSG \n");
+		break;
+	}
+	case AUDPREPROC_CMD_ENC_CFG_DONE_MSG: {
+		struct audpreproc_cmd_enc_cfg_done_msg *enc_cfg_msg = msg;
+
+		MM_DBG("CMD_ENC_CFG_DONE_MSG: stream id %d enc type \
+			0x%8x\n", enc_cfg_msg->stream_id,
+			enc_cfg_msg->rec_enc_type);
+		/* Encoder enable success */
+		if (enc_cfg_msg->rec_enc_type & ENCODE_ENABLE) {
+			if(audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) {
+				MM_DBG("routing command\n");
+				audpreproc_cmd_cfg_routing_mode(audio);
+			} else {
+				audevrc_in_param_config(audio);
+			}
+		} else { /* Encoder disable success */
+			audio->running = 0;
+			if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)
+				audevrc_in_record_config(audio, 0);
+			else
+				wake_up(&audio->wait_enable);
+		}
+		break;
+	}
+	case AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG: {
+		MM_DBG("CMD_ENC_PARAM_CFG_DONE_MSG\n");
+		if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)
+			audevrc_in_mem_config(audio);
+		else
+			audpcm_config(audio);
+		break;
+	}
+	case AUDPREPROC_CMD_ROUTING_MODE_DONE_MSG: {
+		struct audpreproc_cmd_routing_mode_done\
+				*routing_cfg_done_msg = msg;
+		if (routing_cfg_done_msg->configuration == 0) {
+			MM_INFO("routing configuration failed\n");
+			audio->running = 0;
+		} else
+			audevrc_in_param_config(audio);
+		break;
+	}
+	case AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG: {
+		MM_DBG("AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG \n");
+		wake_up(&audio->wait_enable);
+		break;
+	}
+	default:
+		MM_ERR("Unknown Event id %d\n", id);
+	}
+}
+
+/* ------------------- dsp audrec event handler--------------------- */
+static void audrec_dsp_event(void *data, unsigned id, size_t len,
+			    void (*getevent)(void *ptr, size_t len))
+{
+	struct audio_in *audio = data;
+
+	switch (id) {
+	case AUDREC_CMD_MEM_CFG_DONE_MSG: {
+		MM_DBG("CMD_MEM_CFG_DONE MSG DONE\n");
+		audio->running = 1;
+		if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+			if ((!audio->in_call && (audio->dev_cnt > 0)) ||
+				(audio->in_call &&
+					(audio->voice_state \
+						== VOICE_STATE_INCALL)))
+				audevrc_in_record_config(audio, 1);
+		} else {
+			audpreproc_pcm_send_data(audio, 1);
+			wake_up(&audio->wait_enable);
+		}
+		break;
+	}
+	case AUDREC_FATAL_ERR_MSG: {
+		struct audrec_fatal_err_msg fatal_err_msg;
+
+		getevent(&fatal_err_msg, AUDREC_FATAL_ERR_MSG_LEN);
+		MM_ERR("FATAL_ERR_MSG: err id %d\n",
+				fatal_err_msg.audrec_err_id);
+		/* Error stop the encoder */
+		audio->stopped = 1;
+		wake_up(&audio->wait);
+		if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL)
+			wake_up(&audio->write_wait);
+		break;
+	}
+	case AUDREC_UP_PACKET_READY_MSG: {
+		struct audrec_up_pkt_ready_msg pkt_ready_msg;
+
+		getevent(&pkt_ready_msg, AUDREC_UP_PACKET_READY_MSG_LEN);
+		MM_DBG("UP_PACKET_READY_MSG: write cnt lsw  %d \
+		write cnt msw %d read cnt lsw %d  read cnt msw %d \n",\
+		pkt_ready_msg.audrec_packet_write_cnt_lsw, \
+		pkt_ready_msg.audrec_packet_write_cnt_msw, \
+		pkt_ready_msg.audrec_up_prev_read_cnt_lsw, \
+		pkt_ready_msg.audrec_up_prev_read_cnt_msw);
+
+		audevrc_in_get_dsp_frames(audio);
+		break;
+	}
+	case AUDREC_CMD_PCM_BUFFER_PTR_UPDATE_ARM_TO_ENC_MSG: {
+		MM_DBG("ptr_update recieved from DSP\n");
+		audpreproc_pcm_send_data(audio, 1);
+		break;
+	}
+	case AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG: {
+		MM_ERR("AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG");
+		audevrc_in_mem_config(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_EOS_ACK_MSG: {
+		MM_DBG("eos ack recieved\n");
+		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 audrectask\n");
+		break;
+	}
+	default:
+		MM_ERR("Unknown Event id %d\n", id);
+	}
+}
+
+static void audevrc_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->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_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_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);
+}
+
+
+struct msm_adsp_ops audrec_evrc_adsp_ops = {
+	.event = audrec_dsp_event,
+};
+
+static int audpreproc_pcm_buffer_ptr_refresh(struct audio_in *audio,
+				       unsigned idx, unsigned len)
+{
+	struct audrec_cmd_pcm_buffer_ptr_refresh_arm_enc cmd;
+
+	if (len ==  META_OUT_SIZE)
+		len = len / 2;
+	else
+		len = (len + META_OUT_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 audrec_send_audrecqueue(audio, (void *)&cmd,
+					(unsigned int)sizeof(cmd));
+}
+
+
+static int audpcm_config(struct audio_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 = 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 audrec_send_audrecqueue(audio, (void *)&cmd,
+					(unsigned int)sizeof(cmd));
+}
+
+
+static int audpreproc_cmd_cfg_routing_mode(struct audio_in *audio)
+{
+	struct audpreproc_audrec_cmd_routing_mode cmd;
+
+	MM_DBG("\n");
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ROUTING_MODE;
+	cmd.stream_id = audio->enc_id;
+	if (audio->mode == MSM_ADSP_ENC_MODE_NON_TUNNEL)
+		cmd.routing_mode = 1;
+	return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+
+
+static int audevrc_in_enc_config(struct audio_in *audio, int enable)
+{
+	struct audpreproc_audrec_cmd_enc_cfg cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ENC_CFG_2;
+	cmd.stream_id = audio->enc_id;
+
+	if (enable)
+		cmd.audrec_enc_type = audio->enc_type | ENCODE_ENABLE;
+	else
+		cmd.audrec_enc_type &= ~(ENCODE_ENABLE);
+
+	return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+static int audevrc_in_param_config(struct audio_in *audio)
+{
+	struct audpreproc_audrec_cmd_parm_cfg_evrc cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.common.cmd_id = AUDPREPROC_AUDREC_CMD_PARAM_CFG;
+	cmd.common.stream_id = audio->enc_id;
+
+	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 audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+/* To Do: msm_snddev_route_enc(audio->enc_id); */
+static int audevrc_in_record_config(struct audio_in *audio, int enable)
+{
+	struct audpreproc_afe_cmd_audio_record_cfg cmd;
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG;
+	cmd.stream_id = audio->enc_id;
+	if (enable)
+		cmd.destination_activity = AUDIO_RECORDING_TURN_ON;
+	else
+		cmd.destination_activity = AUDIO_RECORDING_TURN_OFF;
+
+	cmd.source_mix_mask = audio->source;
+	if (audio->enc_id == 2) {
+		if ((cmd.source_mix_mask &
+				INTERNAL_CODEC_TX_SOURCE_MIX_MASK) ||
+			(cmd.source_mix_mask & AUX_CODEC_TX_SOURCE_MIX_MASK) ||
+			(cmd.source_mix_mask & VOICE_UL_SOURCE_MIX_MASK) ||
+			(cmd.source_mix_mask & VOICE_DL_SOURCE_MIX_MASK)) {
+			cmd.pipe_id = SOURCE_PIPE_1;
+		}
+		if (cmd.source_mix_mask &
+				AUDPP_A2DP_PIPE_SOURCE_MIX_MASK)
+			cmd.pipe_id |= SOURCE_PIPE_0;
+	}
+	MM_DBG("stream_id %x destination_activity %x \
+	source_mix_mask %x pipe_id %x",\
+	cmd.stream_id, cmd.destination_activity,
+	cmd.source_mix_mask, cmd.pipe_id);
+	return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+static int audevrc_in_mem_config(struct audio_in *audio)
+{
+	struct audrec_cmd_arecmem_cfg cmd;
+	uint16_t *data = (void *) audio->data;
+	int n;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDREC_CMD_MEM_CFG_CMD;
+	cmd.audrec_up_pkt_intm_count = 1;
+	cmd.audrec_ext_pkt_start_addr_msw = audio->phys >> 16;
+	cmd.audrec_ext_pkt_start_addr_lsw = audio->phys;
+	cmd.audrec_ext_pkt_buf_number = FRAME_NUM;
+	MM_DBG("audio->phys = %x\n", audio->phys);
+	/* prepare buffer pointers:
+	 * T:36 bytes evrc packet + 4 halfword header
+	 * NT:36 bytes evrc packet + 12 halfword header
+	 */
+	for (n = 0; n < FRAME_NUM; n++) {
+		if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+			audio->in[n].data = data + 4;
+			data += (FRAME_SIZE/2);
+			MM_DBG("0x%8x\n", (int)(audio->in[n].data - 8));
+		} else  {
+			audio->in[n].data = data + 12;
+			data += ((EVRC_FRAME_SIZE) / 2) + 12;
+			MM_DBG("0x%8x\n", (int)(audio->in[n].data - 24));
+		}
+	}
+	return audrec_send_audrecqueue(audio, &cmd, sizeof(cmd));
+}
+
+static int audevrc_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt)
+{
+	struct up_audrec_packet_ext_ptr cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = UP_AUDREC_PACKET_EXT_PTR;
+	cmd.audrec_up_curr_read_count_msw = read_cnt >> 16;
+	cmd.audrec_up_curr_read_count_lsw = read_cnt;
+
+	return audrec_send_bitstreamqueue(audio, &cmd, sizeof(cmd));
+}
+static int audevrc_flush_command(struct audio_in *audio)
+{
+	struct audrec_cmd_flush cmd;
+	MM_DBG("\n");
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDREC_CMD_FLUSH;
+	return audrec_send_audrecqueue(audio, &cmd, sizeof(cmd));
+}
+
+/* must be called with audio->lock held */
+static int audevrc_in_enable(struct audio_in *audio)
+{
+	if (audio->enabled)
+		return 0;
+
+	if (audpreproc_enable(audio->enc_id, &audpreproc_dsp_event, audio)) {
+		MM_ERR("msm_adsp_enable(audpreproc) failed\n");
+		return -ENODEV;
+	}
+
+	if (msm_adsp_enable(audio->audrec)) {
+		MM_ERR("msm_adsp_enable(audrec) failed\n");
+		audpreproc_disable(audio->enc_id, audio);
+		return -ENODEV;
+	}
+	audio->enabled = 1;
+	audevrc_in_enc_config(audio, 1);
+
+	return 0;
+}
+
+/* must be called with audio->lock held */
+static int audevrc_in_disable(struct audio_in *audio)
+{
+	if (audio->enabled) {
+		audio->enabled = 0;
+		audevrc_in_enc_config(audio, 0);
+		wake_up(&audio->wait);
+		wait_event_interruptible_timeout(audio->wait_enable,
+				audio->running == 0, 1*HZ);
+		msm_adsp_disable(audio->audrec);
+		audpreproc_disable(audio->enc_id, audio);
+	}
+	return 0;
+}
+
+static void audevrc_ioport_reset(struct audio_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_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 = 0; i < FRAME_NUM; 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_in *audio)
+{
+	int i;
+
+	audio->out_head = 0;
+	audio->out_tail = 0;
+	audio->out_count = 0;
+	for (i = 0; i < OUT_FRAME_NUM; 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_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: {
+		uint32_t freq;
+		freq = 48000;
+		MM_DBG("AUDIO_START\n");
+		if (audio->in_call && (audio->voice_state !=
+				VOICE_STATE_INCALL)) {
+			rc = -EPERM;
+			break;
+		}
+		rc = msm_snddev_request_freq(&freq, audio->enc_id,
+					SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+		MM_DBG("sample rate configured %d\n", freq);
+		if (rc < 0) {
+			MM_DBG(" Sample rate can not be set, return code %d\n",
+								 rc);
+			msm_snddev_withdraw_freq(audio->enc_id,
+					SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+			MM_DBG("msm_snddev_withdraw_freq\n");
+			break;
+		}
+		/*update aurec session info in audpreproc layer*/
+		audio->session_info.session_id = audio->enc_id;
+		audio->session_info.sampling_freq = audio->samp_rate;
+		audpreproc_update_audrec_info(&audio->session_info);
+		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: {
+		/*reset the sampling frequency information at audpreproc layer*/
+		audio->session_info.sampling_freq = 0;
+		audpreproc_update_audrec_info(&audio->session_info);
+		rc = audevrc_in_disable(audio);
+		rc = msm_snddev_withdraw_freq(audio->enc_id,
+					SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+		MM_DBG("msm_snddev_withdraw_freq\n");
+		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_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_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;
+		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;
+	}
+	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 = audio->samp_rate;
+		cfg.channel_count = audio->channel_mode;
+		if (copy_to_user((void *)arg, &cfg, sizeof(cfg)))
+			rc = -EFAULT;
+		break;
+	}
+	case AUDIO_SET_INCALL: {
+		struct msm_voicerec_mode cfg;
+		unsigned long flags;
+		if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+			if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) {
+				rc = -EFAULT;
+				break;
+			}
+			if (cfg.rec_mode != VOC_REC_BOTH &&
+				cfg.rec_mode != VOC_REC_UPLINK &&
+				cfg.rec_mode != VOC_REC_DOWNLINK) {
+				MM_ERR("invalid rec_mode\n");
+				rc = -EINVAL;
+				break;
+			} else {
+				spin_lock_irqsave(&audio->dev_lock, flags);
+				if (cfg.rec_mode == VOC_REC_UPLINK)
+					audio->source = \
+						VOICE_UL_SOURCE_MIX_MASK;
+				else if (cfg.rec_mode == VOC_REC_DOWNLINK)
+					audio->source = \
+						VOICE_DL_SOURCE_MIX_MASK;
+				else
+					audio->source = \
+						VOICE_DL_SOURCE_MIX_MASK |
+						VOICE_UL_SOURCE_MIX_MASK ;
+				audio->in_call = 1;
+				spin_unlock_irqrestore(&audio->dev_lock, flags);
+			}
+		}
+		break;
+	}
+	case AUDIO_GET_SESSION_ID: {
+		if (copy_to_user((void *) arg, &audio->enc_id,
+			sizeof(unsigned short))) {
+			rc = -EFAULT;
+		}
+		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_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 ||
+			((audio->mode == MSM_AUD_ENC_MODE_TUNNEL) &&
+			 audio->in_call && audio->running &&
+			(audio->voice_state == VOICE_STATE_OFFCALL)));
+		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;
+			} else if ((audio->mode == MSM_AUD_ENC_MODE_TUNNEL) &&
+					audio->in_call && audio->running &&
+					(audio->voice_state \
+						== VOICE_STATE_OFFCALL)) {
+				MM_DBG("Not Permitted Voice Terminated\n");
+				rc = -EPERM; /* Voice Call stopped */
+				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) {
+			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_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 audpreproc_pcm_send_data(struct audio_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);
+			audpreproc_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_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 audpreproc_evrc_process_eos(struct audio_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);
+	audpreproc_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_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)
+		audpreproc_pcm_send_data(audio, 0);
+	else {
+		audpreproc_pcm_send_data(audio, 1);
+		audio->flush_ack = 0;
+	}
+	if (eos_condition == AUDPREPROC_EVRC_EOS_SET)
+		rc = audpreproc_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_in *audio = file->private_data;
+
+	mutex_lock(&audio->lock);
+	audio->in_call = 0;
+	/* with draw frequency for session
+	   incase not stopped the driver */
+	msm_snddev_withdraw_freq(audio->enc_id, SNDDEV_CAP_TX,
+					AUDDEV_CLNT_ENC);
+	auddev_unregister_evt_listner(AUDDEV_CLNT_ENC, audio->enc_id);
+	/*reset the sampling frequency information at audpreproc layer*/
+	audio->session_info.sampling_freq = 0;
+	audpreproc_update_audrec_info(&audio->session_info);
+	audevrc_in_disable(audio);
+	audevrc_in_flush(audio);
+	msm_adsp_put(audio->audrec);
+	audpreproc_aenc_free(audio->enc_id);
+	audio->audrec = NULL;
+	audio->opened = 0;
+	if (audio->data) {
+		iounmap(audio->data);
+		pmem_kfree(audio->phys);
+		audio->data = NULL;
+	}
+	if (audio->out_data) {
+		iounmap(audio->out_data);
+		pmem_kfree(audio->out_phys);
+		audio->out_data = NULL;
+	}
+	mutex_unlock(&audio->lock);
+	return 0;
+}
+
+struct audio_in the_audio_evrc_in;
+static int audevrc_in_open(struct inode *inode, struct file *file)
+{
+	struct audio_in *audio = &the_audio_evrc_in;
+	int rc;
+	int encid;
+
+	mutex_lock(&audio->lock);
+	if (audio->opened) {
+		rc = -EBUSY;
+		goto done;
+	}
+	audio->phys = pmem_kalloc(DMASZ, PMEM_MEMTYPE_EBI1|
+					PMEM_ALIGNMENT_4K);
+	if (!IS_ERR((void *)audio->phys)) {
+		audio->data = ioremap(audio->phys, DMASZ);
+		if (!audio->data) {
+			MM_ERR("could not allocate DMA buffers\n");
+			rc = -ENOMEM;
+			pmem_kfree(audio->phys);
+			goto done;
+		}
+	} else {
+		MM_ERR("could not allocate DMA buffers\n");
+		rc = -ENOMEM;
+		goto done;
+	}
+	MM_DBG("Memory addr = 0x%8x  phy addr = 0x%8x\n",\
+		(int) audio->data, (int) audio->phys);
+	if ((file->f_mode & FMODE_WRITE) &&
+		(file->f_mode & FMODE_READ)) {
+		audio->mode = MSM_AUD_ENC_MODE_NONTUNNEL;
+		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;
+		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
+	 */
+	if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL)
+			audio->buffer_size = (EVRC_FRAME_SIZE + 14);
+	else
+			audio->buffer_size = (FRAME_SIZE - 8);
+	audio->enc_type = ENC_TYPE_EVRC | audio->mode;
+	audio->samp_rate = 8000;
+	audio->channel_mode = AUDREC_CMD_MODE_MONO;
+	audio->cfg.cdma_rate = CDMA_RATE_FULL;
+	audio->cfg.min_bit_rate = CDMA_RATE_FULL;
+	audio->cfg.max_bit_rate = CDMA_RATE_FULL;
+
+	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;
+	}
+
+	audio->stopped = 0;
+	audio->source = 0;
+	audio->wflush = 0;
+	audio->rflush = 0;
+	audio->flush_ack = 0;
+
+	audevrc_in_flush(audio);
+	audevrc_out_flush(audio);
+
+	audio->out_phys = pmem_kalloc(BUFFER_SIZE,
+				PMEM_MEMTYPE_EBI1 | PMEM_ALIGNMENT_4K);
+	if (IS_ERR((void *)audio->out_phys)) {
+		MM_ERR("could not allocate write buffers\n");
+		rc = -ENOMEM;
+		goto evt_error;
+	} else {
+		audio->out_data = ioremap(audio->out_phys, BUFFER_SIZE);
+		if (!audio->out_data) {
+			MM_ERR("could map write buffers\n");
+			rc = -ENOMEM;
+			pmem_kfree(audio->out_phys);
+			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->device_events = AUDDEV_EVT_DEV_RDY | AUDDEV_EVT_DEV_RLS |
+				AUDDEV_EVT_VOICE_STATE_CHG;
+
+	audio->voice_state = msm_get_voice_state();
+	rc = auddev_register_evt_listner(audio->device_events,
+					AUDDEV_CLNT_ENC, audio->enc_id,
+					evrc_in_listener, (void *) audio);
+	if (rc) {
+		MM_ERR("failed to register device event listener\n");
+		iounmap(audio->out_data);
+		pmem_kfree(audio->out_phys);
+		goto evt_error;
+	}
+	audio->mfield = META_OUT_SIZE;
+	file->private_data = audio;
+	audio->opened = 1;
+	audio->out_frame_cnt++;
+done:
+	mutex_unlock(&audio->lock);
+	return rc;
+evt_error:
+	msm_adsp_put(audio->audrec);
+	audpreproc_aenc_free(audio->enc_id);
+	mutex_unlock(&audio->lock);
+	return rc;
+}
+
+static const struct file_operations audio_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,
+};
+
+struct miscdevice audio_evrc_in_misc = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "msm_evrc_in",
+	.fops	= &audio_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);
+	spin_lock_init(&the_audio_evrc_in.dev_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(&audio_evrc_in_misc);
+}
+
+device_initcall(audevrc_in_init);
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_fm.c b/arch/arm/mach-msm/qdsp5v2/audio_fm.c
new file mode 100644
index 0000000..af65c80
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_fm.c
@@ -0,0 +1,358 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Based on the mp3 native driver in arch/arm/mach-msm/qdsp5v2/audio_mp3.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ *
+ * All source code in this file is licensed under the following license except
+ * where indicated.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/android_pmem.h>
+#include <linux/msm_audio.h>
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+#include <mach/debug_mm.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/qdsp5v2/afe.h>
+#include <mach/qdsp5v2/acdb_commands.h>
+#include <mach/qdsp5v2/audio_acdbi.h>
+#include <mach/qdsp5v2/audio_acdb_def.h>
+
+#define SESSION_ID_FM 6
+#define FM_ENABLE	0xFFFF
+#define FM_DISABLE	0x0
+#define FM_COPP		0x2
+/* Macro specifies maximum FM routing
+	possible */
+#define FM_MAX_RX_ROUTE	0x2
+
+struct fm_rx_calib_gain {
+	uint16_t device_id;
+	struct auddev_evt_devinfo dev_details;
+	struct  acdb_calib_gain_rx  calib_rx;
+};
+
+struct audio {
+	struct mutex lock;
+
+	int opened;
+	int enabled;
+	int running;
+
+	uint16_t dec_id;
+	uint16_t source;
+	uint16_t fm_source;
+	uint16_t fm_mask;
+	uint32_t device_events;
+	uint16_t volume;
+	struct fm_rx_calib_gain fm_calibration_rx[FM_MAX_RX_ROUTE];
+};
+
+static struct audio fm_audio;
+
+/* must be called with audio->lock held */
+static int audio_enable(struct audio *audio)
+{
+	int rc = 0;
+	if (audio->enabled)
+		return 0;
+
+	MM_DBG("fm mask= %08x fm_source = %08x\n",
+			audio->fm_mask, audio->fm_source);
+	if (audio->fm_mask && audio->fm_source) {
+		rc = afe_config_fm_codec(FM_ENABLE, audio->fm_mask);
+		if (!rc)
+			audio->running = 1;
+		/* Routed to icodec rx path */
+		if ((audio->fm_mask & AFE_HW_PATH_CODEC_RX) ==
+				AFE_HW_PATH_CODEC_RX) {
+			afe_config_fm_calibration_gain(
+			audio->fm_calibration_rx[0].device_id,
+			audio->fm_calibration_rx[0].calib_rx.audppcalgain);
+		}
+		/* Routed to aux codec rx path */
+		if ((audio->fm_mask & AFE_HW_PATH_AUXPCM_RX) ==
+				AFE_HW_PATH_AUXPCM_RX){
+			afe_config_fm_calibration_gain(
+			audio->fm_calibration_rx[1].device_id,
+			audio->fm_calibration_rx[1].calib_rx.audppcalgain);
+		}
+	}
+
+	audio->enabled = 1;
+	return rc;
+}
+
+static void fm_listner(u32 evt_id, union auddev_evt_data *evt_payload,
+			void *private_data)
+{
+	struct audio *audio = (struct audio *) private_data;
+	struct auddev_evt_devinfo *devinfo =
+			(struct auddev_evt_devinfo *)evt_payload;
+	switch (evt_id) {
+	case AUDDEV_EVT_DEV_RDY:
+		MM_DBG(":AUDDEV_EVT_DEV_RDY\n");
+		if (evt_payload->routing_id == FM_COPP)
+			audio->fm_source = 1;
+		else
+			audio->source = (0x1 << evt_payload->routing_id);
+
+		if (audio->source & 0x1)
+			audio->fm_mask = 0x1;
+		else if (audio->source & 0x2)
+			audio->fm_mask = 0x3;
+		else
+			audio->fm_mask = 0x0;
+
+		if (!audio->enabled
+			|| !audio->fm_mask
+			|| !audio->fm_source)
+			break;
+		else {
+			afe_config_fm_codec(FM_ENABLE, audio->fm_mask);
+			audio->running = 1;
+		}
+		break;
+	case AUDDEV_EVT_DEV_RLS:
+		MM_DBG(":AUDDEV_EVT_DEV_RLS\n");
+		if (evt_payload->routing_id == FM_COPP)
+			audio->fm_source = 0;
+		else
+			audio->source &= ~(0x1 << evt_payload->routing_id);
+
+		if (audio->source & 0x1)
+			audio->fm_mask = 0x1;
+		else if (audio->source & 0x2)
+			audio->fm_mask = 0x3;
+		else
+			audio->fm_mask = 0x0;
+
+		if (audio->running
+			&& (!audio->fm_mask || !audio->fm_source)) {
+			afe_config_fm_codec(FM_DISABLE, audio->fm_mask);
+			audio->running = 0;
+		}
+		break;
+	case AUDDEV_EVT_STREAM_VOL_CHG:
+		MM_DBG(":AUDDEV_EVT_STREAM_VOL_CHG, stream vol \n");
+		audio->volume = evt_payload->session_vol;
+		afe_config_fm_volume(audio->volume);
+		break;
+	case AUDDEV_EVT_DEVICE_INFO:{
+		struct acdb_get_block get_block;
+		int rc = 0;
+		MM_DBG(":AUDDEV_EVT_DEVICE_INFO\n");
+		MM_DBG("sample_rate = %d\n", devinfo->sample_rate);
+		MM_DBG("acdb_id = %d\n", devinfo->acdb_id);
+		/* Applucable only for icodec rx and aux codec rx path
+			and fm stream routed to it */
+		if (((devinfo->dev_id == 0x00) || (devinfo->dev_id == 0x01)) &&
+			(devinfo->sessions && (1 << audio->dec_id))) {
+			/* Query ACDB driver for calib gain, only if difference
+				in device */
+			if ((audio->fm_calibration_rx[devinfo->dev_id].
+				dev_details.acdb_id != devinfo->acdb_id) ||
+				(audio->fm_calibration_rx[devinfo->dev_id].
+				dev_details.sample_rate !=
+					devinfo->sample_rate)) {
+				audio->fm_calibration_rx[devinfo->dev_id].
+					dev_details.dev_id = devinfo->dev_id;
+				audio->fm_calibration_rx[devinfo->dev_id].
+					dev_details.sample_rate =
+						devinfo->sample_rate;
+				audio->fm_calibration_rx[devinfo->dev_id].
+					dev_details.dev_type =
+						devinfo->dev_type;
+				audio->fm_calibration_rx[devinfo->dev_id].
+					dev_details.sessions =
+						devinfo->sessions;
+				/* Query ACDB driver for calibration gain */
+				get_block.acdb_id = devinfo->acdb_id;
+				get_block.sample_rate_id = devinfo->sample_rate;
+				get_block.interface_id =
+					IID_AUDIO_CALIBRATION_GAIN_RX;
+				get_block.algorithm_block_id =
+					ABID_AUDIO_CALIBRATION_GAIN_RX;
+				get_block.total_bytes =
+					sizeof(struct  acdb_calib_gain_rx);
+				get_block.buf_ptr = (u32 *)
+				&audio->fm_calibration_rx[devinfo->dev_id].
+				calib_rx;
+
+				rc = acdb_get_calibration_data(&get_block);
+				if (rc < 0) {
+					MM_ERR("Unable to get calibration"\
+						"gain\n");
+					/* Set to unity incase of error */
+					audio->\
+					fm_calibration_rx[devinfo->dev_id].
+					calib_rx.audppcalgain = 0x2000;
+				} else
+					MM_DBG("calibration gain = 0x%8x\n",
+						*(get_block.buf_ptr));
+			}
+			if (audio->running) {
+				afe_config_fm_calibration_gain(
+				audio->fm_calibration_rx[devinfo->dev_id].
+					device_id,
+				audio->fm_calibration_rx[devinfo->dev_id].
+					calib_rx.audppcalgain);
+				}
+			}
+		break;
+	}
+	default:
+		MM_DBG(":ERROR:wrong event\n");
+		break;
+	}
+}
+/* must be called with audio->lock held */
+static int audio_disable(struct audio *audio)
+{
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	return afe_config_fm_codec(FM_DISABLE, audio->source);
+}
+
+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\n", 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;
+	case AUDIO_GET_SESSION_ID:
+		if (copy_to_user((void *) arg, &audio->dec_id,
+					sizeof(unsigned short)))
+			rc = -EFAULT;
+		else
+			rc = 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);
+	auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id);
+	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;
+
+
+	if (audio->opened)
+		return -EPERM;
+
+	/* Allocate the decoder */
+	audio->dec_id = SESSION_ID_FM;
+
+	audio->running = 0;
+	audio->fm_source = 0;
+	audio->fm_mask = 0;
+
+	/* Initialize the calibration gain structure */
+	audio->fm_calibration_rx[0].device_id = AFE_HW_PATH_CODEC_RX;
+	audio->fm_calibration_rx[1].device_id = AFE_HW_PATH_AUXPCM_RX;
+	audio->fm_calibration_rx[0].calib_rx.audppcalgain = 0x2000;
+	audio->fm_calibration_rx[1].calib_rx.audppcalgain = 0x2000;
+	audio->fm_calibration_rx[0].dev_details.acdb_id = PSEUDO_ACDB_ID;
+	audio->fm_calibration_rx[1].dev_details.acdb_id = PSEUDO_ACDB_ID;
+
+	audio->device_events = AUDDEV_EVT_DEV_RDY
+				|AUDDEV_EVT_DEV_RLS|
+				AUDDEV_EVT_STREAM_VOL_CHG|
+				AUDDEV_EVT_DEVICE_INFO;
+
+	rc = auddev_register_evt_listner(audio->device_events,
+					AUDDEV_CLNT_DEC,
+					audio->dec_id,
+					fm_listner,
+					(void *)audio);
+
+	if (rc) {
+		MM_ERR("%s: failed to register listnet\n", __func__);
+		goto event_err;
+	}
+
+	audio->opened = 1;
+	file->private_data = audio;
+
+event_err:
+	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/qdsp5v2/audio_interct.c b/arch/arm/mach-msm/qdsp5v2/audio_interct.c
new file mode 100644
index 0000000..785ed8e
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_interct.c
@@ -0,0 +1,124 @@
+/* Copyright (c) 2009, 2011 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/platform_device.h>
+#include <asm/io.h>
+#include <mach/qdsp5v2/audio_interct.h>
+
+#define AUDIO_INTERCT_ADSPLPA_WBRX_SEL_BMSK 0x4
+#define AUDIO_INTERCT_ADSPLPA_WBRX_SEL_SHFT 0x2
+#define AUDIO_INTERCT_ADSPAV_RPCMI2SRX_SEL_BMSK 0x10
+#define AUDIO_INTERCT_ADSPAV_RPCMI2SRX_SEL_SHFT 0x4
+#define AUDIO_INTERCT_ADSPAV_TPCMI2STX_SEL_BMSK 0x40
+#define AUDIO_INTERCT_ADSPAV_TPCMI2STX_SEL_SHFT 0x6
+#define AUDIO_INTERCT_ADSPAV_AUX_REGSEL_BMSK 0x100
+#define AUDIO_INTERCT_ADSPAV_AUX_REGSEL_SHFT 0x8
+
+/* Should look to protect this register */
+void __iomem *aictl_reg;
+
+void audio_interct_codec(u32 source)
+{
+	u32 reg_val;
+
+	reg_val = readl(aictl_reg);
+	reg_val = (reg_val & ~AUDIO_INTERCT_ADSPLPA_WBRX_SEL_BMSK) |
+		(source << AUDIO_INTERCT_ADSPLPA_WBRX_SEL_SHFT);
+	writel(reg_val, aictl_reg);
+	mb();
+}
+EXPORT_SYMBOL(audio_interct_codec);
+
+void audio_interct_aux_regsel(u32 source)
+{
+	u32 reg_val;
+
+	reg_val = readl(aictl_reg);
+	reg_val = (reg_val & ~AUDIO_INTERCT_ADSPAV_AUX_REGSEL_BMSK) |
+		(source << AUDIO_INTERCT_ADSPAV_AUX_REGSEL_SHFT);
+	writel(reg_val, aictl_reg);
+	mb();
+}
+EXPORT_SYMBOL(audio_interct_aux_regsel);
+
+void audio_interct_tpcm_source(u32 source)
+{
+	u32 reg_val;
+
+	reg_val = readl(aictl_reg);
+	reg_val = (reg_val & ~AUDIO_INTERCT_ADSPAV_TPCMI2STX_SEL_BMSK) |
+		(source << AUDIO_INTERCT_ADSPAV_TPCMI2STX_SEL_SHFT);
+	writel(reg_val, aictl_reg);
+	mb();
+}
+EXPORT_SYMBOL(audio_interct_tpcm_source);
+
+void audio_interct_rpcm_source(u32 source)
+{
+	u32 reg_val;
+
+	reg_val = readl(aictl_reg);
+	reg_val = (reg_val & ~AUDIO_INTERCT_ADSPAV_RPCMI2SRX_SEL_BMSK) |
+		(source << AUDIO_INTERCT_ADSPAV_RPCMI2SRX_SEL_SHFT);
+	writel(reg_val, aictl_reg);
+	mb();
+}
+EXPORT_SYMBOL(audio_interct_rpcm_source);
+
+static int audio_interct_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct resource *aictl_mem;
+
+	aictl_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!aictl_mem) {
+		rc = -ENODEV;
+		goto error;
+	}
+	aictl_reg = ioremap(aictl_mem->start,
+			(aictl_mem->end - aictl_mem->start) + 1);
+error:
+	return rc;
+}
+
+
+static int audio_interct_remove(struct platform_device *pdev)
+{
+	iounmap(aictl_reg);
+	return 0;
+}
+
+static struct platform_driver audio_interct_driver = {
+	.probe = audio_interct_probe,
+	.remove = audio_interct_remove,
+	.driver = {
+		.name = "audio_interct",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init audio_interct_init(void)
+{
+	return platform_driver_register(&audio_interct_driver);
+}
+
+static void __exit audio_interct_exit(void)
+{
+	platform_driver_unregister(&audio_interct_driver);
+}
+
+module_init(audio_interct_init);
+module_exit(audio_interct_exit);
+
+MODULE_DESCRIPTION("MSM Audio Interconnect driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_lpa.c b/arch/arm/mach-msm/qdsp5v2/audio_lpa.c
new file mode 100644
index 0000000..da4ae73
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_lpa.c
@@ -0,0 +1,1700 @@
+/* low power 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/cdev.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 <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+#include <linux/slab.h>
+#include <linux/msm_audio.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/qdsp5audplaycmdi.h>
+#include <mach/qdsp5v2/qdsp5audplaymsg.h>
+#include <mach/qdsp5v2/audpp.h>
+#include <mach/qdsp5v2/codec_utils.h>
+#include <mach/qdsp5v2/mp3_funcs.h>
+#include <mach/qdsp5v2/pcm_funcs.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
+#define ADRV_STATUS_PAUSE 0x00000010
+
+#define DEVICE_SWITCH_STATE_NONE     0
+#define DEVICE_SWITCH_STATE_PENDING  1
+#define DEVICE_SWITCH_STATE_READY    2
+#define DEVICE_SWITCH_STATE_COMPLETE 3
+
+#define AUDDEC_DEC_PCM 0
+#define AUDDEC_DEC_MP3 2
+
+#define PCM_BUFSZ_MIN 4800	/* Hold one stereo MP3 frame */
+
+/* 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 AUDLPA_EVENT_NUM 10 /* Default number of pre-allocated event packets */
+
+#define MASK_32BITS     0xFFFFFFFF
+
+#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;							\
+})
+
+/* payload[7]; -1 indicates error, 0 indicates no error */
+#define CHECK_ERROR(v) (!v[7])
+
+/* calculates avsync_info from payload */
+#define CALCULATE_AVSYNC_FROM_PAYLOAD(v) ((uint64_t)((((uint64_t)v[10]) \
+					<< 32) | (v[11] & MASK_32BITS)))
+
+/* calculates avsync_info from avsync_info stored in audio */
+#define CALCULATE_AVSYNC(v)					   \
+			((uint64_t)((((uint64_t)v[4]) << 32) | 	   \
+			 (v[5] << 16) | (v[6])))
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+struct audlpa_suspend_ctl {
+	struct early_suspend node;
+	struct audio *audio;
+};
+#endif
+
+struct audlpa_event {
+	struct list_head list;
+	int event_type;
+	union msm_audio_event_payload payload;
+};
+
+struct audlpa_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 audlpa_buffer_node {
+	struct list_head list;
+	struct msm_audio_aio_buf buf;
+	unsigned long paddr;
+};
+
+struct audlpa_dec {
+	char *name;
+	int dec_attrb;
+	long (*ioctl)(struct file *, unsigned int, unsigned long);
+	void (*adec_params)(struct audio *);
+};
+
+struct audlpa_dec audlpa_decs[] = {
+	{"msm_mp3_lp", AUDDEC_DEC_MP3, &mp3_ioctl, &audpp_cmd_cfg_mp3_params},
+	{"msm_pcm_lp_dec", AUDDEC_DEC_PCM, &pcm_ioctl,
+		&audpp_cmd_cfg_pcm_params},
+};
+
+static int auddec_dsp_config(struct audio *audio, int enable);
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg);
+static void audlpa_post_event(struct audio *audio, int type,
+	union msm_audio_event_payload payload);
+static unsigned long audlpa_pmem_fixup(struct audio *audio, void *addr,
+				unsigned long len, int ref_up);
+static void audlpa_async_send_data(struct audio *audio, unsigned needed,
+				uint32_t *payload);
+
+static void lpa_listner(u32 evt_id, union auddev_evt_data *evt_payload,
+			void *private_data)
+{
+	struct audio *audio = (struct audio *) private_data;
+	switch (evt_id) {
+	case AUDDEV_EVT_DEV_RDY:
+		MM_DBG(":AUDDEV_EVT_DEV_RDY routing id = %d\n",
+		evt_payload->routing_id);
+		/* Do not select HLB path for icodec, if there is already COPP3
+		 * routing exists. DSP can not support concurrency of HLB path
+		 * and COPP3 routing as it involves different buffer Path */
+		if (((0x1 << evt_payload->routing_id) == AUDPP_MIXER_ICODEC) &&
+			!(audio->source & AUDPP_MIXER_3)) {
+			audio->source |= AUDPP_MIXER_HLB;
+			MM_DBG("mixer_mask modified for low-power audio\n");
+		} else
+			audio->source |= (0x1 << evt_payload->routing_id);
+
+		MM_DBG("running = %d, enabled = %d, source = 0x%x\n",
+			audio->running, audio->enabled, audio->source);
+		if (audio->running == 1 && audio->enabled == 1) {
+			audpp_route_stream(audio->dec_id, audio->source);
+			if (audio->source & AUDPP_MIXER_HLB)
+				audpp_dsp_set_vol_pan(
+					AUDPP_CMD_CFG_DEV_MIXER_ID_4,
+					&audio->vol_pan,
+					COPP);
+			else if (audio->source & AUDPP_MIXER_NONHLB)
+				audpp_dsp_set_vol_pan(
+					audio->dec_id, &audio->vol_pan, POPP);
+			if (audio->device_switch == DEVICE_SWITCH_STATE_READY) {
+				audio->wflush = 1;
+				audio->device_switch =
+					DEVICE_SWITCH_STATE_COMPLETE;
+				audpp_flush(audio->dec_id);
+				if (wait_event_interruptible(audio->write_wait,
+							 !audio->wflush) < 0)
+					MM_DBG("AUDIO_FLUSH interrupted\n");
+
+				if (audio->wflush == 0) {
+					if (audio->drv_status &
+						ADRV_STATUS_PAUSE) {
+						if (audpp_pause(audio->dec_id,
+							1))
+							MM_DBG("audpp_pause"
+								"failed\n");
+					}
+				}
+			}
+		}
+		break;
+	case AUDDEV_EVT_REL_PENDING:
+		MM_DBG(":AUDDEV_EVT_REL_PENDING\n");
+		/* If route to multiple devices like COPP3, not need to
+		 * handle device switch */
+		if ((audio->running == 1) && (audio->enabled == 1) &&
+			!(audio->source & AUDPP_MIXER_3)) {
+			if (audio->device_switch == DEVICE_SWITCH_STATE_NONE) {
+				if (!(audio->drv_status & ADRV_STATUS_PAUSE)) {
+					if (audpp_pause(audio->dec_id, 1))
+						MM_DBG("audpp pause failed\n");
+				}
+				audio->device_switch =
+					DEVICE_SWITCH_STATE_PENDING;
+				audio->avsync_flag = 0;
+				if (audpp_query_avsync(audio->dec_id) < 0)
+					MM_DBG("query avsync failed\n");
+
+				if (wait_event_interruptible_timeout
+					(audio->avsync_wait, audio->avsync_flag,
+				 msecs_to_jiffies(AVSYNC_EVENT_TIMEOUT)) < 0)
+					MM_DBG("AV sync timeout failed\n");
+				if (audio->avsync_flag == 1) {
+					if (audio->device_switch ==
+						DEVICE_SWITCH_STATE_PENDING)
+						audio->device_switch =
+						DEVICE_SWITCH_STATE_READY;
+				}
+			}
+		}
+		break;
+	case AUDDEV_EVT_DEV_RLS:
+		/* If there is already COPP3 routing exists. icodec route
+		 * was not having HLB path. */
+		MM_DBG(":AUDDEV_EVT_DEV_RLS routing id = %d\n",
+			evt_payload->routing_id);
+		if (((0x1 << evt_payload->routing_id) == AUDPP_MIXER_ICODEC) &&
+			!(audio->source & AUDPP_MIXER_3))
+			audio->source &= ~AUDPP_MIXER_HLB;
+		else
+			audio->source &= ~(0x1 << evt_payload->routing_id);
+		MM_DBG("running = %d, enabled = %d, source = 0x%x\n",
+			audio->running, audio->enabled, audio->source);
+
+		if (audio->running == 1 && audio->enabled == 1)
+			audpp_route_stream(audio->dec_id, audio->source);
+		break;
+	case AUDDEV_EVT_STREAM_VOL_CHG:
+		audio->vol_pan.volume = evt_payload->session_vol;
+		MM_DBG("\n:AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n"
+			"running = %d, enabled = %d, source = 0x%x",
+			audio->vol_pan.volume, audio->running,
+			audio->enabled, audio->source);
+		if (audio->running == 1 && audio->enabled == 1) {
+			if (audio->source & AUDPP_MIXER_HLB)
+				audpp_dsp_set_vol_pan(
+					AUDPP_CMD_CFG_DEV_MIXER_ID_4,
+					&audio->vol_pan, COPP);
+			else if (audio->source & AUDPP_MIXER_NONHLB)
+				audpp_dsp_set_vol_pan(
+					audio->dec_id, &audio->vol_pan, POPP);
+		}
+		break;
+	default:
+		MM_ERR(":ERROR:wrong event\n");
+		break;
+	}
+}
+
+/* must be called with audio->lock held */
+static int audio_enable(struct audio *audio)
+{
+	MM_DBG("\n"); /* Macro prints the file name and function */
+
+	if (audio->enabled)
+		return 0;
+
+	audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+	audio->out_needed = 0;
+
+	if (msm_adsp_enable(audio->audplay)) {
+		MM_ERR("msm_adsp_enable(audplay) failed\n");
+		return -ENODEV;
+	}
+
+	if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) {
+		MM_ERR("audpp_enable() failed\n");
+		msm_adsp_disable(audio->audplay);
+		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);
+		audio->out_needed = 0;
+	}
+	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:
+		audlpa_async_send_data(audio, 1, 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");
+				audio->codec_ops.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");
+				/* send  mixer command */
+				audpp_route_stream(audio->dec_id,
+						audio->source);
+				audio->dec_state =
+					MSM_AUD_DECODER_STATE_SUCCESS;
+				wake_up(&audio->wait);
+				break;
+			case AUDPP_DEC_STATUS_EOS:
+				MM_DBG("decoder status: EOS\n");
+				audio->teos = 1;
+				wake_up(&audio->write_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;
+			MM_DBG("source = 0x%x\n", audio->source);
+			if (audio->source & AUDPP_MIXER_HLB)
+				audpp_dsp_set_vol_pan(
+					AUDPP_CMD_CFG_DEV_MIXER_ID_4,
+					&audio->vol_pan,
+					COPP);
+			else if (audio->source & AUDPP_MIXER_NONHLB)
+				audpp_dsp_set_vol_pan(
+					audio->dec_id, &audio->vol_pan,
+					POPP);
+			audpp_dsp_set_eq(audio->dec_id, audio->eq_enable,
+					&audio->eq, POPP);
+		} else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+			MM_DBG("CFG_MSG DISABLE\n");
+			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]);
+		audio->codec_ops.adec_params(audio);
+		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");
+		wake_up(&audio->write_wait);
+		break;
+
+	case AUDPP_MSG_AVSYNC_MSG:
+		MM_DBG("AVSYNC_MSG\n");
+		memcpy(&audio->avsync[0], msg, sizeof(audio->avsync));
+		audio->avsync_flag = 1;
+		wake_up(&audio->avsync_wait);
+		break;
+
+	default:
+		MM_ERR("UNKNOWN (%d)\n", id);
+	}
+
+}
+
+struct msm_adsp_ops audplay_adsp_ops_lpa = {
+	.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)
+{
+	struct audpp_cmd_cfg_dec_type cfg_dec_cmd;
+
+	memset(&cfg_dec_cmd, 0, sizeof(cfg_dec_cmd));
+
+	cfg_dec_cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+	if (enable)
+		cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+			AUDPP_CMD_ENA_DEC_V |
+			audlpa_decs[audio->minor_no].dec_attrb;
+	else
+		cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+				AUDPP_CMD_DIS_DEC_V;
+	cfg_dec_cmd.dm_mode = 0x0;
+	cfg_dec_cmd.stream_id = audio->dec_id;
+	return audpp_send_queue1(&cfg_dec_cmd, sizeof(cfg_dec_cmd));
+}
+
+static void audlpa_async_send_buffer(struct audio *audio)
+{
+	int	found = 0;
+	uint64_t temp = 0;
+	struct audplay_cmd_bitstream_data_avail cmd;
+	struct audlpa_buffer_node *next_buf = NULL;
+
+	temp = audio->bytecount_head;
+	if (audio->device_switch == DEVICE_SWITCH_STATE_NONE) {
+		list_for_each_entry(next_buf, &audio->out_queue, list) {
+			if (temp == audio->bytecount_given) {
+				found = 1;
+				break;
+			} else
+				temp += next_buf->buf.data_len;
+		}
+		if (next_buf && found) {
+			cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL;
+			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;
+			audio->bytecount_given += next_buf->buf.data_len;
+			wmb();
+			audplay_send_queue0(audio, &cmd, sizeof(cmd));
+			audio->out_needed = 0;
+			audio->drv_status |= ADRV_STATUS_OBUF_GIVEN;
+		}
+	} else if (audio->device_switch == DEVICE_SWITCH_STATE_COMPLETE) {
+		audio->device_switch = DEVICE_SWITCH_STATE_NONE;
+		next_buf = list_first_entry(&audio->out_queue,
+					struct audlpa_buffer_node, list);
+		if (next_buf) {
+			cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL;
+			cmd.decoder_id = audio->dec_id;
+			temp = audio->bytecount_head +
+				next_buf->buf.data_len -
+				audio->bytecount_consumed;
+			if (audpp_restore_avsync(audio->dec_id,
+						&audio->avsync[0]))
+				MM_DBG("audpp_restore_avsync failed\n");
+
+			if ((signed)(temp >= 0) &&
+			((signed)(next_buf->buf.data_len - temp) >= 0)) {
+				cmd.buf_ptr	= (unsigned) (next_buf->paddr +
+						  (next_buf->buf.data_len -
+						   temp));
+				cmd.buf_size = temp >> 1;
+				cmd.partition_number	= 0;
+				audio->bytecount_given =
+					audio->bytecount_consumed + temp;
+				wmb();
+				audplay_send_queue0(audio, &cmd, sizeof(cmd));
+				audio->out_needed = 0;
+				audio->drv_status |= ADRV_STATUS_OBUF_GIVEN;
+			}
+		}
+	}
+}
+
+static void audlpa_async_send_data(struct audio *audio, unsigned needed,
+				uint32_t *payload)
+{
+	unsigned long flags;
+	uint64_t temp = 0;
+
+	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) {
+			union msm_audio_event_payload evt_payload;
+			struct audlpa_buffer_node *used_buf = NULL;
+
+			if (CHECK_ERROR(payload))
+				audio->bytecount_consumed =
+					CALCULATE_AVSYNC_FROM_PAYLOAD(payload);
+
+			if ((audio->device_switch ==
+				DEVICE_SWITCH_STATE_COMPLETE) &&
+				(audio->avsync_flag == 1)) {
+				audio->avsync_flag = 0;
+				audio->bytecount_consumed =
+					CALCULATE_AVSYNC(audio->avsync);
+			}
+			BUG_ON(list_empty(&audio->out_queue));
+			temp = audio->bytecount_head;
+			used_buf = list_first_entry(&audio->out_queue,
+					struct audlpa_buffer_node, list);
+			if ((audio->bytecount_head + used_buf->buf.data_len) <
+				audio->bytecount_consumed) {
+				audio->bytecount_head += used_buf->buf.data_len;
+				temp = audio->bytecount_head;
+				list_del(&used_buf->list);
+				evt_payload.aio_buf = used_buf->buf;
+				audlpa_post_event(audio, AUDIO_EVENT_WRITE_DONE,
+						  evt_payload);
+				kfree(used_buf);
+			}
+			audio->drv_status &= ~ADRV_STATUS_OBUF_GIVEN;
+		}
+	}
+	if (audio->out_needed) {
+		if (!list_empty(&audio->out_queue))
+			audlpa_async_send_buffer(audio);
+	}
+done:
+	spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+/* ------------------- device --------------------- */
+static void audlpa_async_flush(struct audio *audio)
+{
+	struct audlpa_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 audlpa_buffer_node, list);
+		list_del(&buf_node->list);
+		payload.aio_buf = buf_node->buf;
+		if ((buf_node->paddr != 0xFFFFFFFF) &&
+			(buf_node->buf.data_len != 0))
+			audlpa_post_event(audio, AUDIO_EVENT_WRITE_DONE,
+							  payload);
+		kfree(buf_node);
+	}
+	audio->drv_status &= ~ADRV_STATUS_OBUF_GIVEN;
+	audio->out_needed = 0;
+	audio->bytecount_consumed = 0;
+	audio->bytecount_head = 0;
+	audio->bytecount_given = 0;
+	audio->device_switch = DEVICE_SWITCH_STATE_NONE;
+	atomic_set(&audio->out_bytes, 0);
+}
+
+static void audio_ioport_reset(struct audio *audio)
+{
+	/* 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);
+		audlpa_async_flush(audio);
+		mutex_unlock(&audio->write_lock);
+		audio->avsync_flag = 1;
+		wake_up(&audio->avsync_wait);
+	} else
+		audlpa_async_flush(audio);
+}
+
+static int audlpa_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 audlpa_reset_event_queue(struct audio *audio)
+{
+	unsigned long flags;
+	struct audlpa_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 audlpa_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 audlpa_event, list);
+		list_del(&drv_evt->list);
+		kfree(drv_evt);
+	}
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+	return;
+}
+
+static long audlpa_process_event_req(struct audio *audio, void __user *arg)
+{
+	long rc;
+	struct msm_audio_event usr_evt;
+	struct audlpa_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, audlpa_events_pending(audio),
+			msecs_to_jiffies(timeout));
+		if (rc == 0)
+			return -ETIMEDOUT;
+	} else {
+		rc = wait_event_interruptible(
+			audio->event_wait, audlpa_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 audlpa_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);
+		audlpa_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 audlpa_pmem_check(struct audio *audio,
+		void *vaddr, unsigned long len)
+{
+	struct audlpa_pmem_region *region_elt;
+	struct audlpa_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 audlpa_pmem_add(struct audio *audio,
+	struct msm_audio_pmem_info *info)
+{
+	unsigned long paddr, kvaddr, len;
+	struct file *file;
+	struct audlpa_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 = audlpa_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 audlpa_pmem_remove(struct audio *audio,
+	struct msm_audio_pmem_info *info)
+{
+	struct audlpa_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 audlpa_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 audlpa_pmem_lookup_vaddr(struct audio *audio, void *addr,
+		     unsigned long len, struct audlpa_pmem_region **region)
+{
+	struct audlpa_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 audlpa_pmem_fixup(struct audio *audio, void *addr,
+		    unsigned long len, int ref_up)
+{
+	struct audlpa_pmem_region *region;
+	unsigned long paddr;
+	int ret;
+
+	ret = audlpa_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 audlpa_aio_buf_add(struct audio *audio, unsigned dir,
+	void __user *arg)
+{
+	unsigned long flags;
+	struct audlpa_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 = audlpa_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)) {
+			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);
+		audlpa_async_send_data(audio, 0, 0);
+	} else {
+		/* read */
+	}
+
+	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, POPP);
+		audio->eq_needs_commit = 0;
+	}
+	return 0;
+}
+
+static int audio_get_avsync_data(struct audio *audio,
+						struct msm_audio_stats *stats)
+{
+	int rc = -EINVAL;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	if (audio->dec_id == audio->avsync[0] && audio->avsync_flag) {
+		/* av_sync sample count */
+		stats->sample_count = (audio->avsync[2] << 16) |
+						(audio->avsync[3]);
+
+		/* av_sync byte_count */
+		stats->byte_count = (audio->avsync[5] << 16) |
+						(audio->avsync[6]);
+
+		audio->avsync_flag = 0;
+		rc = 0;
+	}
+	local_irq_restore(flags);
+	return rc;
+
+}
+
+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("audio_ioctl() cmd = %d\n", cmd);
+
+	if (cmd == AUDIO_GET_STATS) {
+		struct msm_audio_stats stats;
+
+		audio->avsync_flag = 0;
+		memset(&stats, 0, sizeof(stats));
+		if (audpp_query_avsync(audio->dec_id) < 0)
+			return rc;
+
+		rc = wait_event_interruptible_timeout(audio->avsync_wait,
+				(audio->avsync_flag == 1),
+				msecs_to_jiffies(AVSYNC_EVENT_TIMEOUT));
+
+		if (rc < 0)
+			return rc;
+		else if ((rc > 0) || ((rc == 0) && (audio->avsync_flag == 1))) {
+			if (audio_get_avsync_data(audio, &stats) < 0)
+				return rc;
+
+			if (copy_to_user((void *) arg, &stats, sizeof(stats)))
+				return -EFAULT;
+			return 0;
+		} else
+			return -EAGAIN;
+	}
+
+	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(AUDPP_CMD_CFG_DEV_MIXER_ID_4,
+						&audio->vol_pan,
+						COPP);
+		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(AUDPP_CMD_CFG_DEV_MIXER_ID_4,
+						&audio->vol_pan,
+						COPP);
+		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 = audlpa_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_DBG("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;
+		audio->drv_status &= ~ADRV_STATUS_PAUSE;
+		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;
+		MM_INFO("AUDIO_SET_CONFIG\n");
+		if (copy_from_user(&config, (void *) arg, sizeof(config))) {
+			rc = -EFAULT;
+			MM_INFO("ERROR: copy from user\n");
+			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;
+			MM_INFO("ERROR: config.channel_count == %d\n",
+					config.channel_count);
+			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;
+			MM_INFO("ERROR: config.bits == %d\n", config.bits);
+			break;
+		}
+		audio->out_sample_rate = config.sample_rate;
+		audio->out_channel_mode = config.channel_count;
+		audio->out_bits = config.bits;
+		MM_DBG("AUDIO_SET_CONFIG: config.bits = %d\n", config.bits);
+		rc = 0;
+		break;
+	}
+
+	case AUDIO_GET_CONFIG:{
+		struct msm_audio_config config;
+		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.meta_field = 0;
+		config.unused[0] = 0;
+		config.unused[1] = 0;
+		config.unused[2] = 0;
+		MM_DBG("AUDIO_GET_CONFIG: config.bits = %d\n", config.bits);
+		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);
+		if (arg == 1)
+			audio->drv_status |= ADRV_STATUS_PAUSE;
+		else if (arg == 0)
+			audio->drv_status &= ~ADRV_STATUS_PAUSE;
+		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 = audlpa_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 = audlpa_pmem_remove(audio, &info);
+			break;
+		}
+	case AUDIO_ASYNC_WRITE:
+		if (audio->drv_status & ADRV_STATUS_FSYNC)
+			rc = -EBUSY;
+		else
+			rc = audlpa_aio_buf_add(audio, 1, (void __user *) arg);
+		break;
+
+	case AUDIO_GET_SESSION_ID:
+		if (copy_to_user((void *) arg, &audio->dec_id,
+					sizeof(unsigned short)))
+			rc = -EFAULT;
+		else
+			rc = 0;
+		break;
+	default:
+		rc = audio->codec_ops.ioctl(file, cmd, arg);
+	}
+	mutex_unlock(&audio->lock);
+	return rc;
+}
+
+/* Only useful in tunnel-mode */
+int audlpa_async_fsync(struct audio *audio)
+{
+	int rc = 0, empty = 0;
+	struct audlpa_buffer_node *buf_node;
+
+	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);
+	audio->teos = 0;
+	empty = list_empty(&audio->out_queue);
+	buf_node = kmalloc(sizeof(*buf_node), GFP_KERNEL);
+	if (!buf_node)
+		goto done;
+
+	buf_node->paddr = 0xFFFFFFFF;
+	buf_node->buf.data_len = 0;
+	buf_node->buf.buf_addr = NULL;
+	buf_node->buf.buf_len = 0;
+	buf_node->buf.private_data = NULL;
+	list_add_tail(&buf_node->list, &audio->out_queue);
+	if ((empty != 0) && (audio->out_needed == 1))
+		audlpa_async_send_data(audio, 0, 0);
+
+	rc = wait_event_interruptible(audio->write_wait,
+				  audio->teos || audio->wflush ||
+				  audio->stopped);
+
+	if (rc < 0)
+		goto done;
+
+	if (audio->teos == 1) {
+		/* Releasing all the pending buffers to user */
+		audio->teos = 0;
+		audlpa_async_flush(audio);
+	}
+
+	if (audio->stopped || audio->wflush)
+		rc = -EBUSY;
+
+done:
+	mutex_unlock(&audio->write_lock);
+	mutex_lock(&audio->lock);
+	audio->drv_status &= ~ADRV_STATUS_FSYNC;
+	mutex_unlock(&audio->lock);
+
+	return rc;
+}
+
+int audlpa_fsync(struct file *file, int datasync)
+{
+	struct audio *audio = file->private_data;
+
+	if (!audio->running)
+		return -EINVAL;
+
+	return audlpa_async_fsync(audio);
+}
+
+static void audlpa_reset_pmem_region(struct audio *audio)
+{
+	struct audlpa_pmem_region *region;
+	struct list_head *ptr, *next;
+
+	list_for_each_safe(ptr, next, &audio->pmem_region_queue) {
+		region = list_entry(ptr, struct audlpa_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("\n"); /* Macro prints the file name and function */
+
+	MM_INFO("audio instance 0x%08x freeing\n", (int)audio);
+	mutex_lock(&audio->lock);
+	auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id);
+	audio_disable(audio);
+	audlpa_async_flush(audio);
+	audlpa_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);
+	audlpa_reset_event_queue(audio);
+	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 audlpa_post_event(struct audio *audio, int type,
+	union msm_audio_event_payload payload)
+{
+	struct audlpa_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 audlpa_event, list);
+		list_del(&e_node->list);
+	} else {
+		e_node = kmalloc(sizeof(struct audlpa_event), GFP_ATOMIC);
+		if (!e_node) {
+			MM_ERR("No mem to post event %d\n", type);
+			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 audlpa_suspend(struct early_suspend *h)
+{
+	struct audlpa_suspend_ctl *ctl =
+		container_of(h, struct audlpa_suspend_ctl, node);
+	union msm_audio_event_payload payload;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	audlpa_post_event(ctl->audio, AUDIO_EVENT_SUSPEND, payload);
+}
+
+static void audlpa_resume(struct early_suspend *h)
+{
+	struct audlpa_suspend_ctl *ctl =
+		container_of(h, struct audlpa_suspend_ctl, node);
+	union msm_audio_event_payload payload;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	audlpa_post_event(ctl->audio, AUDIO_EVENT_RESUME, payload);
+}
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t audlpa_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t audlpa_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,
+					"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,
+					"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);
+	buffer[n] = 0;
+	return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+
+static const struct file_operations audlpa_debug_fops = {
+	.read = audlpa_debug_read,
+	.open = audlpa_debug_open,
+};
+#endif
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+	struct audio *audio = NULL;
+	int rc, i, dec_attrb = 0, decid;
+	struct audlpa_event *e_node = NULL;
+#ifdef CONFIG_DEBUG_FS
+	/* 4 bytes represents decoder number, 1 byte for terminate string */
+	char name[sizeof "msm_lpa_" + 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);
+
+	if ((file->f_mode & FMODE_WRITE) && !(file->f_mode & FMODE_READ)) {
+		dec_attrb |= MSM_AUD_MODE_TUNNEL;
+	} else {
+		kfree(audio);
+		rc = -EACCES;
+		goto done;
+	}
+
+	/* Allocate the decoder based on inode minor number*/
+	audio->minor_no = iminor(inode);
+	dec_attrb |= audlpa_decs[audio->minor_no].dec_attrb;
+	audio->codec_ops.ioctl = audlpa_decs[audio->minor_no].ioctl;
+	audio->codec_ops.adec_params = audlpa_decs[audio->minor_no].adec_params;
+
+	dec_attrb |= MSM_AUD_MODE_LP;
+
+	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_INFO("audio instance 0x%08x freeing\n", (int)audio);
+		kfree(audio);
+		goto done;
+	}
+	audio->dec_id = decid & MSM_AUD_DECODER_MASK;
+
+	MM_DBG("set to aio interface\n");
+	audio->drv_status |= ADRV_STATUS_AIO_INTF;
+
+	rc = msm_adsp_get(audio->module_name, &audio->audplay,
+		&audplay_adsp_ops_lpa, audio);
+
+	if (rc) {
+		MM_ERR("failed to get %s module\n", audio->module_name);
+		goto err;
+	}
+
+	/* 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);
+	init_waitqueue_head(&audio->avsync_wait);
+
+	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->vol_pan.volume = 0x2000;
+
+	audlpa_async_flush(audio);
+
+	file->private_data = audio;
+	audio->opened = 1;
+
+	audio->device_events = AUDDEV_EVT_DEV_RDY
+				|AUDDEV_EVT_DEV_RLS | AUDDEV_EVT_REL_PENDING
+				|AUDDEV_EVT_STREAM_VOL_CHG;
+	audio->device_switch = DEVICE_SWITCH_STATE_NONE;
+	audio->drv_status &= ~ADRV_STATUS_PAUSE;
+	audio->bytecount_consumed = 0;
+	audio->bytecount_head = 0;
+	audio->bytecount_given = 0;
+
+	rc = auddev_register_evt_listner(audio->device_events,
+					AUDDEV_CLNT_DEC,
+					audio->dec_id,
+					lpa_listner,
+					(void *)audio);
+	if (rc) {
+		MM_ERR("%s: failed to register listnet\n", __func__);
+		goto event_err;
+	}
+
+#ifdef CONFIG_DEBUG_FS
+	snprintf(name, sizeof name, "msm_lpa_%04x", audio->dec_id);
+	audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+			NULL, (void *) audio, &audlpa_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 = audlpa_resume;
+	audio->suspend_ctl.node.suspend = audlpa_suspend;
+	audio->suspend_ctl.audio = audio;
+	register_early_suspend(&audio->suspend_ctl.node);
+#endif
+	for (i = 0; i < AUDLPA_EVENT_NUM; i++) {
+		e_node = kmalloc(sizeof(struct audlpa_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;
+event_err:
+	msm_adsp_put(audio->audplay);
+err:
+	iounmap(audio->data);
+	pmem_kfree(audio->phys);
+	audpp_adec_free(audio->dec_id);
+	MM_INFO("audio instance 0x%08x freeing\n", (int)audio);
+	kfree(audio);
+	return rc;
+}
+
+static const struct file_operations audio_lpa_fops = {
+	.owner		= THIS_MODULE,
+	.open		= audio_open,
+	.release	= audio_release,
+	.unlocked_ioctl	= audio_ioctl,
+	.fsync		= audlpa_fsync,
+};
+
+static dev_t audlpa_devno;
+static struct class *audlpa_class;
+struct audlpa_device {
+	const char *name;
+	struct device *device;
+	struct cdev cdev;
+};
+
+static struct audlpa_device *audlpa_devices;
+
+static void audlpa_create(struct audlpa_device *adev, const char *name,
+			struct device *parent, dev_t devt)
+{
+	struct device *dev;
+	int rc;
+
+	dev = device_create(audlpa_class, parent, devt, "%s", name);
+	if (IS_ERR(dev))
+		return;
+
+	cdev_init(&adev->cdev, &audio_lpa_fops);
+	adev->cdev.owner = THIS_MODULE;
+
+	rc = cdev_add(&adev->cdev, devt, 1);
+	if (rc < 0) {
+		device_destroy(audlpa_class, devt);
+	} else {
+		adev->device = dev;
+		adev->name = name;
+	}
+}
+
+static int __init audio_init(void)
+{
+	int rc;
+	int n = ARRAY_SIZE(audlpa_decs);
+
+	audlpa_devices = kzalloc(sizeof(struct audlpa_device) * n, GFP_KERNEL);
+	if (!audlpa_devices)
+		return -ENOMEM;
+
+	audlpa_class = class_create(THIS_MODULE, "audlpa");
+	if (IS_ERR(audlpa_class))
+		goto fail_create_class;
+
+	rc = alloc_chrdev_region(&audlpa_devno, 0, n, "msm_audio_lpa");
+	if (rc < 0)
+		goto fail_alloc_region;
+
+	for (n = 0; n < ARRAY_SIZE(audlpa_decs); n++) {
+		audlpa_create(audlpa_devices + n,
+				audlpa_decs[n].name, NULL,
+				MKDEV(MAJOR(audlpa_devno), n));
+	}
+
+	return 0;
+
+fail_alloc_region:
+	class_unregister(audlpa_class);
+	return rc;
+fail_create_class:
+	kfree(audlpa_devices);
+	return -ENOMEM;
+}
+
+static void __exit audio_exit(void)
+{
+	class_unregister(audlpa_class);
+	kfree(audlpa_devices);
+}
+
+module_init(audio_init);
+module_exit(audio_exit);
+
+MODULE_DESCRIPTION("MSM LPA driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_mp3.c b/arch/arm/mach-msm/qdsp5v2/audio_mp3.c
new file mode 100644
index 0000000..7c0cde8
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_mp3.c
@@ -0,0 +1,2505 @@
+/* mp3 audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * 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/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 <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+
+#include <linux/msm_audio.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/qdsp5audplaycmdi.h>
+#include <mach/qdsp5v2/qdsp5audplaymsg.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/qdsp5v2/audpp.h>
+#include <mach/debug_mm.h>
+#include <linux/slab.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 BITSTREAM_ERROR_THRESHOLD_VALUE 0x1 /* DEFAULT THRESHOLD VALUE */
+
+#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 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;
+
+	/* 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 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;
+	int16_t source;
+
+#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;
+	/* AV sync Info */
+	int avsync_flag;              /* Flag to indicate feedback from DSP */
+	wait_queue_head_t avsync_wait;/* Wait queue for AV Sync Message     */
+	/* flags, 48 bits sample/bytes counter per channel */
+	uint16_t avsync[AUDPP_AVSYNC_CH_COUNT * AUDPP_AVSYNC_NUM_WORDS + 1];
+
+	uint32_t device_events;
+
+	struct list_head pmem_region_queue; /* protected by lock */
+	struct audmp3_drv_operations drv_ops;
+
+	struct msm_audio_bitstream_info stream_info;
+	struct msm_audio_bitstream_error_info bitstream_error_info;
+	uint32_t bitstream_error_threshold_value;
+
+	int eq_enable;
+	int eq_needs_commit;
+	struct audpp_cmd_cfg_object_params_eqalizer eq;
+	struct 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_error_threshold_config(struct audio *audio);
+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 void mp3_listner(u32 evt_id, union auddev_evt_data *evt_payload,
+			void *private_data)
+{
+	struct audio *audio = (struct audio *) private_data;
+	switch (evt_id) {
+	case AUDDEV_EVT_DEV_RDY:
+		MM_DBG(":AUDDEV_EVT_DEV_RDY\n");
+		audio->source |= (0x1 << evt_payload->routing_id);
+		if (audio->running == 1 && audio->enabled == 1)
+			audpp_route_stream(audio->dec_id, audio->source);
+
+		break;
+	case AUDDEV_EVT_DEV_RLS:
+		MM_DBG(":AUDDEV_EVT_DEV_RLS\n");
+		audio->source &= ~(0x1 << evt_payload->routing_id);
+		if (audio->running == 1 && audio->enabled == 1)
+			audpp_route_stream(audio->dec_id, audio->source);
+		break;
+	case AUDDEV_EVT_STREAM_VOL_CHG:
+		audio->vol_pan.volume = evt_payload->session_vol;
+		MM_DBG(":AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n",
+				audio->vol_pan.volume);
+		if (audio->running)
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+					POPP);
+		break;
+	default:
+		MM_ERR(":ERROR:wrong event\n");
+		break;
+	}
+}
+/* must be called with audio->lock held */
+static int audio_enable(struct audio *audio)
+{
+	MM_DBG("\n"); /* Macro prints the file name and function */
+
+	if (audio->enabled)
+		return 0;
+
+	audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+	audio->out_tail = 0;
+	audio->out_needed = 0;
+
+	if (msm_adsp_enable(audio->audplay)) {
+		MM_ERR("msm_adsp_enable(audplay) failed\n");
+		return -ENODEV;
+	}
+
+	if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) {
+		MM_ERR("audpp_enable() failed\n");
+		msm_adsp_disable(audio->audplay);
+		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);
+		audio->out_needed = 0;
+	}
+	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 audmp3_bitstream_error_info(struct audio *audio, uint32_t *payload)
+{
+	unsigned long flags;
+	union msm_audio_event_payload e_payload;
+
+	if (payload[0] != AUDDEC_DEC_MP3) {
+		MM_ERR("Unexpected bitstream error info from DSP:\
+				Invalid decoder\n");
+		return;
+	}
+
+	/* get stream info from DSP msg */
+	spin_lock_irqsave(&audio->dsp_lock, flags);
+
+	audio->bitstream_error_info.dec_id = payload[0];
+	audio->bitstream_error_info.err_msg_indicator = payload[1];
+	audio->bitstream_error_info.err_type = payload[2];
+
+	spin_unlock_irqrestore(&audio->dsp_lock, flags);
+	MM_ERR("bit_stream_error_type=%d error_count=%d\n",
+			audio->bitstream_error_info.err_type, (0x0000FFFF &
+			audio->bitstream_error_info.err_msg_indicator));
+
+	/* send event to ARM to notify error info coming */
+	e_payload.error_info = audio->bitstream_error_info;
+	audmp3_post_event(audio, AUDIO_EVENT_BITSTREAM_ERROR_INFO, e_payload);
+}
+
+static void audmp3_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_MP3;
+	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;
+	audmp3_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:
+		audio->drv_ops.send_data(audio, 1);
+		break;
+
+	case AUDPLAY_MSG_BUFFER_UPDATE:
+		audio->drv_ops.pcm_buf_update(audio, msg);
+		break;
+
+	case AUDPLAY_UP_STREAM_INFO:
+		if ((msg[1] & AUDPLAY_STREAM_INFO_MSG_MASK) ==
+				AUDPLAY_STREAM_INFO_MSG_MASK) {
+			audmp3_bitstream_error_info(audio, msg);
+		} else {
+			audmp3_update_stream_info(audio, msg);
+		}
+		break;
+
+	case AUDPLAY_UP_OUTPORT_FLUSH_ACK:
+		MM_DBG("OUTPORT_FLUSH_ACK\n");
+		audio->rflush = 0;
+		wake_up(&audio->read_wait);
+		if (audio->pcm_feedback)
+			audio->drv_ops.buffer_refresh(audio);
+		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");
+				/* send  mixer command */
+				audpp_route_stream(audio->dec_id,
+						audio->source);
+				if (audio->pcm_feedback) {
+					audplay_error_threshold_config(audio);
+					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,
+					POPP);
+			audpp_dsp_set_eq(audio->dec_id, audio->eq_enable,
+					&audio->eq, POPP);
+		} else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+			MM_DBG("CFG_MSG DISABLE\n");
+			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;
+
+	case AUDPP_MSG_AVSYNC_MSG:
+		MM_DBG("AUDPP_MSG_AVSYNC_MSG\n");
+		memcpy(&audio->avsync[0], msg, sizeof(audio->avsync));
+		audio->avsync_flag = 1;
+		wake_up(&audio->avsync_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)
+{
+	struct audpp_cmd_cfg_dec_type cfg_dec_cmd;
+
+	memset(&cfg_dec_cmd, 0, sizeof(cfg_dec_cmd));
+
+	cfg_dec_cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+	if (enable)
+		cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+				AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_MP3;
+	else
+		cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+				AUDPP_CMD_DIS_DEC_V;
+	cfg_dec_cmd.dm_mode = 0x0;
+	cfg_dec_cmd.stream_id = audio->dec_id;
+	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_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;
+	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_error_threshold_config(struct audio *audio)
+{
+	union audplay_cmd_channel_info ch_cfg_cmd;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	ch_cfg_cmd.thr_update.cmd_id = AUDPLAY_CMD_CHANNEL_INFO;
+	ch_cfg_cmd.thr_update.threshold_update = AUDPLAY_ERROR_THRESHOLD_ENABLE;
+	ch_cfg_cmd.thr_update.threshold_value =
+		audio->bitstream_error_threshold_value;
+	(void)audplay_send_queue0(audio, &ch_cfg_cmd, sizeof(ch_cfg_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 audplay_outport_flush(struct audio *audio)
+{
+	struct audplay_cmd_outport_flush op_flush_cmd;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	op_flush_cmd.cmd_id = AUDPLAY_CMD_OUTPORT_FLUSH;
+	(void)audplay_send_queue0(audio, &op_flush_cmd, sizeof(op_flush_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;
+				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);
+	}
+	audio->avsync_flag = 1;
+	wake_up(&audio->avsync_wait);
+}
+
+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);
+	}
+	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, POPP);
+		audio->eq_needs_commit = 0;
+	}
+	return 0;
+}
+
+static int audio_get_avsync_data(struct audio *audio,
+						struct msm_audio_stats *stats)
+{
+	int rc = -EINVAL;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	if (audio->dec_id == audio->avsync[0] && audio->avsync_flag) {
+		/* av_sync sample count */
+		stats->sample_count = (audio->avsync[2] << 16) |
+						(audio->avsync[3]);
+
+		/* av_sync byte_count */
+		stats->byte_count = (audio->avsync[5] << 16) |
+						(audio->avsync[6]);
+
+		audio->avsync_flag = 0;
+		rc = 0;
+	}
+	local_irq_restore(flags);
+	return rc;
+
+}
+
+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;
+
+		audio->avsync_flag = 0;
+		memset(&stats, 0, sizeof(stats));
+		if (audpp_query_avsync(audio->dec_id) < 0)
+			return rc;
+
+		rc = wait_event_interruptible_timeout(audio->avsync_wait,
+				(audio->avsync_flag == 1),
+				msecs_to_jiffies(AUDPP_AVSYNC_EVENT_TIMEOUT));
+
+		if (rc < 0)
+			return rc;
+		else if ((rc > 0) || ((rc == 0) && (audio->avsync_flag == 1))) {
+			if (audio_get_avsync_data(audio, &stats) < 0)
+				return rc;
+
+			if (copy_to_user((void *)arg, &stats, sizeof(stats)))
+				return -EFAULT;
+			return 0;
+		} else
+			return -EAGAIN;
+	}
+
+	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,
+					POPP);
+		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,
+					POPP);
+		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_OUTPORT_FLUSH:
+		MM_DBG("AUDIO_OUTPORT_FLUSH\n");
+		audio->rflush = 1;
+		if (audio->drv_status & ADRV_STATUS_AIO_INTF) {
+			audio->drv_ops.in_flush(audio);
+		} else {
+			wake_up(&audio->read_wait);
+			mutex_lock(&audio->read_lock);
+			audio->drv_ops.in_flush(audio);
+			mutex_unlock(&audio->read_lock);
+		}
+		audplay_outport_flush(audio);
+		rc = wait_event_interruptible(audio->read_wait,
+				!audio->rflush);
+		if (rc < 0) {
+			MM_ERR("AUDPLAY_OUTPORT_FLUSH interrupted\n");
+			rc = -EINTR;
+		}
+		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_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;
+	}
+	case AUDIO_GET_BITSTREAM_ERROR_INFO:{
+		if ((audio->bitstream_error_info.err_msg_indicator &
+				AUDPLAY_STREAM_INFO_MSG_MASK) ==
+				AUDPLAY_STREAM_INFO_MSG_MASK) {
+			/* haven't received bitstream error info event,
+			the bitstream error info is not updated */
+			rc = -EPERM;
+			break;
+		}
+		if (copy_to_user((void *)arg, &audio->bitstream_error_info,
+			sizeof(struct msm_audio_bitstream_error_info)))
+			rc = -EFAULT;
+		else
+			rc = 0;
+		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;
+	case AUDIO_GET_SESSION_ID:
+		if (copy_to_user((void *) arg, &audio->dec_id,
+					sizeof(unsigned short)))
+			rc = -EFAULT;
+		else
+			rc = 0;
+		break;
+	case AUDIO_SET_ERR_THRESHOLD_VALUE:
+		if (copy_from_user(&audio->bitstream_error_threshold_value,
+					(void *)arg, sizeof(uint32_t)))
+			rc = -EFAULT;
+		else
+			rc = 0;
+		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_timeout(
+			audio->read_wait,
+			(audio->in[audio->read_next].
+			used > 0) || (audio->stopped)
+			|| (audio->rflush),
+			msecs_to_jiffies(MSM_AUD_BUFFER_UPDATE_WAIT_MS));
+
+		if (rc == 0) {
+			rc = -ETIMEDOUT;
+			break;
+		} else 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);
+
+			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);
+	auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id);
+	audio_disable(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);
+	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);
+			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;
+
+	/* AIO interface */
+	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");
+		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->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;
+	}
+
+	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);
+		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->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);
+	init_waitqueue_head(&audio->avsync_wait);
+
+	audio->out_sample_rate = 44100;
+	audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V;
+	audio->vol_pan.volume = 0x2000;
+	audio->bitstream_error_threshold_value =
+		BITSTREAM_ERROR_THRESHOLD_VALUE;
+
+	audio->drv_ops.out_flush(audio);
+
+	file->private_data = audio;
+	audio->opened = 1;
+
+	audio->device_events = AUDDEV_EVT_DEV_RDY
+				|AUDDEV_EVT_DEV_RLS |
+				AUDDEV_EVT_STREAM_VOL_CHG;
+
+	rc = auddev_register_evt_listner(audio->device_events,
+					AUDDEV_CLNT_DEC,
+					audio->dec_id,
+					mp3_listner,
+					(void *)audio);
+	if (rc) {
+		MM_ERR("%s: failed to register listner\n", __func__);
+		goto event_err;
+	}
+
+#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;
+		}
+	}
+	memset(&audio->stream_info, 0, sizeof(struct msm_audio_bitstream_info));
+	memset(&audio->bitstream_error_info, 0,
+			sizeof(struct msm_audio_bitstream_info));
+done:
+	return rc;
+event_err:
+	msm_adsp_put(audio->audplay);
+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/qdsp5v2/audio_mvs.c b/arch/arm/mach-msm/qdsp5v2/audio_mvs.c
new file mode 100644
index 0000000..dc41bf4
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_mvs.c
@@ -0,0 +1,1748 @@
+/* Copyright (c) 2010-2011, 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/kthread.h>
+#include <linux/list.h>
+#include <linux/uaccess.h>
+#include <linux/mutex.h>
+#include <linux/wakelock.h>
+#include <linux/msm_audio_mvs.h>
+#include <linux/slab.h>
+#include <mach/msm_rpcrouter.h>
+
+#define MVS_PROG 0x30000014
+#define MVS_VERS 0x00030001
+#define MVS_VERS_COMP_VER4 0x00040001
+#define MVS_VERS_COMP_VER5 0x00050001
+
+#define MVS_CLIENT_ID_VOIP 0x00000003
+
+#define MVS_ACQUIRE_PROC 4
+#define MVS_ENABLE_PROC 5
+#define MVS_RELEASE_PROC 6
+#define MVS_AMR_SET_AMR_MODE_PROC 7
+#define MVS_AMR_SET_AWB_MODE_PROC 8
+#define MVS_VOC_SET_FRAME_RATE_PROC 10
+#define MVS_GSM_SET_DTX_MODE_PROC 11
+#define MVS_G729A_SET_MODE_PROC 12
+#define MVS_G711_GET_MODE_PROC 14
+#define MVS_G711_SET_MODE_PROC 15
+#define MVS_G711A_GET_MODE_PROC 16
+#define MVS_G711A_SET_MODE_PROC 17
+#define MVS_G722_SET_MODE_PROC 20
+#define MVS_G722_GET_MODE_PROC 21
+#define MVS_SET_DTX_MODE_PROC 22
+
+#define MVS_EVENT_CB_TYPE_PROC 1
+#define MVS_PACKET_UL_FN_TYPE_PROC 2
+#define MVS_PACKET_DL_FN_TYPE_PROC 3
+
+#define MVS_CB_FUNC_ID 0xAAAABBBB
+#define MVS_UL_CB_FUNC_ID 0xBBBBCCCC
+#define MVS_DL_CB_FUNC_ID 0xCCCCDDDD
+
+#define MVS_FRAME_MODE_VOC_TX 1
+#define MVS_FRAME_MODE_VOC_RX 2
+#define MVS_FRAME_MODE_AMR_UL 3
+#define MVS_FRAME_MODE_AMR_DL 4
+#define MVS_FRAME_MODE_GSM_UL 5
+#define MVS_FRAME_MODE_GSM_DL 6
+#define MVS_FRAME_MODE_HR_UL 7
+#define MVS_FRAME_MODE_HR_DL 8
+#define MVS_FRAME_MODE_G711_UL 9
+#define MVS_FRAME_MODE_G711_DL 10
+#define MVS_FRAME_MODE_PCM_UL 13
+#define MVS_FRAME_MODE_PCM_DL 14
+#define MVS_FRAME_MODE_G729A_UL 17
+#define MVS_FRAME_MODE_G729A_DL 18
+#define MVS_FRAME_MODE_G711A_UL 19
+#define MVS_FRAME_MODE_G711A_DL 20
+#define MVS_FRAME_MODE_G722_UL 21
+#define MVS_FRAME_MODE_G722_DL 22
+
+
+
+#define MVS_PKT_CONTEXT_ISR 0x00000001
+
+#define RPC_TYPE_REQUEST 0
+#define RPC_TYPE_REPLY 1
+
+#define RPC_STATUS_FAILURE 0
+#define RPC_STATUS_SUCCESS 1
+#define RPC_STATUS_REJECT 1
+
+#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)
+
+enum audio_mvs_state_type {
+	AUDIO_MVS_CLOSED,
+	AUDIO_MVS_OPENED,
+	AUDIO_MVS_STARTED,
+	AUDIO_MVS_STOPPED
+};
+
+enum audio_mvs_event_type {
+	AUDIO_MVS_COMMAND,
+	AUDIO_MVS_MODE,
+	AUDIO_MVS_NOTIFY
+};
+
+enum audio_mvs_cmd_status_type {
+	AUDIO_MVS_CMD_FAILURE,
+	AUDIO_MVS_CMD_BUSY,
+	AUDIO_MVS_CMD_SUCCESS
+};
+
+enum audio_mvs_mode_status_type {
+	AUDIO_MVS_MODE_NOT_AVAIL,
+	AUDIO_MVS_MODE_INIT,
+	AUDIO_MVS_MODE_READY
+};
+
+enum audio_mvs_pkt_status_type {
+	AUDIO_MVS_PKT_NORMAL,
+	AUDIO_MVS_PKT_FAST,
+	AUDIO_MVS_PKT_SLOW
+};
+
+/* Parameters required for MVS acquire. */
+struct rpc_audio_mvs_acquire_args {
+	uint32_t client_id;
+	uint32_t cb_func_id;
+};
+
+struct audio_mvs_acquire_msg {
+	struct rpc_request_hdr rpc_hdr;
+	struct rpc_audio_mvs_acquire_args acquire_args;
+};
+
+/* Parameters required for MVS enable. */
+struct rpc_audio_mvs_enable_args {
+	uint32_t client_id;
+	uint32_t mode;
+	uint32_t ul_cb_func_id;
+	uint32_t dl_cb_func_id;
+	uint32_t context;
+};
+
+struct audio_mvs_enable_msg {
+	struct rpc_request_hdr rpc_hdr;
+	struct rpc_audio_mvs_enable_args enable_args;
+};
+
+/* Parameters required for MVS release. */
+struct audio_mvs_release_msg {
+	struct rpc_request_hdr rpc_hdr;
+	uint32_t client_id;
+};
+
+/* Parameters required for setting AMR mode. */
+struct audio_mvs_set_amr_mode_msg {
+	struct rpc_request_hdr rpc_hdr;
+	uint32_t amr_mode;
+};
+
+/* Parameters required for setting DTX. */
+struct audio_mvs_set_dtx_mode_msg {
+	struct rpc_request_hdr rpc_hdr;
+	uint32_t dtx_mode;
+};
+
+/* Parameters required for setting EVRC mode. */
+struct audio_mvs_set_voc_mode_msg {
+	struct rpc_request_hdr rpc_hdr;
+	uint32_t max_rate;
+	uint32_t min_rate;
+};
+
+/* Parameters for G711 mode */
+struct audio_mvs_set_g711_mode_msg {
+	struct rpc_request_hdr rpc_hdr;
+	uint32_t g711_mode;
+};
+
+/* Parameters for G729 mode */
+struct audio_mvs_set_g729_mode_msg {
+	struct rpc_request_hdr rpc_hdr;
+	uint32_t g729_mode;
+};
+
+/* Parameters for G722 mode */
+struct audio_mvs_set_g722_mode_msg {
+	struct rpc_request_hdr rpc_hdr;
+	uint32_t g722_mode;
+};
+
+
+/* Parameters for G711A mode */
+struct audio_mvs_set_g711A_mode_msg {
+	struct rpc_request_hdr rpc_hdr;
+	uint32_t g711A_mode;
+};
+
+/* Parameters for EFR FR and HR mode */
+struct audio_mvs_set_efr_mode_msg {
+	struct rpc_request_hdr rpc_hdr;
+	uint32_t efr_mode;
+};
+
+union audio_mvs_event_data {
+	struct mvs_ev_command_type {
+		uint32_t event;
+		uint32_t client_id;
+		uint32_t cmd_status;
+	} mvs_ev_command_type;
+
+	struct mvs_ev_mode_type {
+		uint32_t event;
+		uint32_t client_id;
+		uint32_t mode_status;
+		uint32_t mode;
+	} mvs_ev_mode_type;
+
+	struct mvs_ev_notify_type {
+		uint32_t event;
+		uint32_t client_id;
+		uint32_t buf_dir;
+		uint32_t max_frames;
+	} mvs_ev_notify_type;
+};
+
+struct audio_mvs_cb_func_args {
+	uint32_t cb_func_id;
+	uint32_t valid_ptr;
+	uint32_t event;
+	union audio_mvs_event_data event_data;
+};
+
+struct audio_mvs_frame_info_hdr {
+	uint32_t frame_mode;
+	uint32_t mvs_mode;
+	uint16_t buf_free_cnt;
+};
+
+struct audio_mvs_ul_reply {
+	struct rpc_reply_hdr reply_hdr;
+	uint32_t valid_pkt_status_ptr;
+	uint32_t pkt_status;
+};
+
+struct audio_mvs_dl_cb_func_args {
+	uint32_t cb_func_id;
+
+	uint32_t valid_ptr;
+	uint32_t frame_mode;
+	uint32_t frame_mode_ignore;
+
+	struct audio_mvs_frame_info_hdr frame_info_hdr;
+
+	uint32_t amr_frame;
+	uint32_t amr_mode;
+};
+/*general codec parameters includes AMR, G711A, PCM
+G729, VOC and HR vocoders
+*/
+struct gnr_cdc_param {
+	uint32_t param1;
+	uint32_t param2;
+	uint32_t valid_pkt_status_ptr;
+	uint32_t pkt_status;
+};
+/*G711 codec parameter*/
+struct g711_param {
+	uint32_t param1;
+	uint32_t valid_pkt_status_ptr;
+	uint32_t pkt_status;
+};
+
+union codec_param {
+	struct gnr_cdc_param gnr_arg;
+	struct g711_param g711_arg;
+};
+
+struct audio_mvs_dl_reply {
+	struct rpc_reply_hdr reply_hdr;
+
+	uint32_t voc_pkt[MVS_MAX_VOC_PKT_SIZE/4];
+
+	uint32_t valid_frame_info_ptr;
+	uint32_t frame_mode;
+	uint32_t frame_mode_again;
+
+	struct audio_mvs_frame_info_hdr frame_info_hdr;
+	union codec_param cdc_param;
+};
+
+struct audio_mvs_buf_node {
+	struct list_head list;
+	struct msm_audio_mvs_frame frame;
+};
+
+/* Each buffer is 20 ms, queue holds 200 ms of data. */
+#define MVS_MAX_Q_LEN 10
+
+struct audio_mvs_info_type {
+	enum audio_mvs_state_type state;
+	uint32_t frame_mode;
+	uint32_t mvs_mode;
+	uint32_t buf_free_cnt;
+	uint32_t rate_type;
+	uint32_t dtx_mode;
+
+	struct msm_rpc_endpoint *rpc_endpt;
+	uint32_t rpc_prog;
+	uint32_t rpc_ver;
+	uint32_t rpc_status;
+
+	uint8_t *mem_chunk;
+
+	struct list_head in_queue;
+	struct list_head free_in_queue;
+
+	struct list_head out_queue;
+	struct list_head free_out_queue;
+
+	struct task_struct *task;
+
+	wait_queue_head_t wait;
+	wait_queue_head_t mode_wait;
+	wait_queue_head_t out_wait;
+
+	struct mutex lock;
+	struct mutex in_lock;
+	struct mutex out_lock;
+
+	struct wake_lock suspend_lock;
+	struct wake_lock idle_lock;
+};
+
+static struct audio_mvs_info_type audio_mvs_info;
+
+static int audio_mvs_setup_mode(struct audio_mvs_info_type *audio)
+{
+	int rc = 0;
+
+	pr_debug("%s:\n", __func__);
+
+	switch (audio->mvs_mode) {
+	case MVS_MODE_AMR:
+	case MVS_MODE_AMR_WB: {
+		struct audio_mvs_set_amr_mode_msg set_amr_mode_msg;
+		struct audio_mvs_set_dtx_mode_msg set_dtx_mode_msg;
+
+		/* Set AMR mode. */
+		memset(&set_amr_mode_msg, 0, sizeof(set_amr_mode_msg));
+		set_amr_mode_msg.amr_mode = cpu_to_be32(audio->rate_type);
+
+		if (audio->mvs_mode == MVS_MODE_AMR) {
+			msm_rpc_setup_req(&set_amr_mode_msg.rpc_hdr,
+					  audio->rpc_prog,
+					  audio->rpc_ver,
+					  MVS_AMR_SET_AMR_MODE_PROC);
+		} else {
+			msm_rpc_setup_req(&set_amr_mode_msg.rpc_hdr,
+					  audio->rpc_prog,
+					  audio->rpc_ver,
+					  MVS_AMR_SET_AWB_MODE_PROC);
+		}
+
+		audio->rpc_status = RPC_STATUS_FAILURE;
+		rc = msm_rpc_write(audio->rpc_endpt,
+				   &set_amr_mode_msg,
+				   sizeof(set_amr_mode_msg));
+
+		if (rc >= 0) {
+			pr_debug("%s: RPC write for set amr mode done\n",
+					__func__);
+
+			/* Save the MVS configuration information. */
+			audio->frame_mode = MVS_FRAME_MODE_AMR_DL;
+
+			/* Disable DTX. */
+			memset(&set_dtx_mode_msg, 0, sizeof(set_dtx_mode_msg));
+			set_dtx_mode_msg.dtx_mode = cpu_to_be32(0);
+
+			msm_rpc_setup_req(&set_dtx_mode_msg.rpc_hdr,
+					  audio->rpc_prog,
+					  audio->rpc_ver,
+					  MVS_SET_DTX_MODE_PROC);
+
+			audio->rpc_status = RPC_STATUS_FAILURE;
+			rc = msm_rpc_write(audio->rpc_endpt,
+					   &set_dtx_mode_msg,
+					   sizeof(set_dtx_mode_msg));
+
+			if (rc >= 0) {
+				pr_debug("%s: RPC write for set dtx done\n",
+						 __func__);
+
+				rc = 0;
+			}
+		} else {
+			pr_err("%s: RPC write for set amr mode failed %d\n",
+			       __func__, rc);
+		}
+		break;
+	}
+	case MVS_MODE_PCM:
+	case MVS_MODE_LINEAR_PCM: {
+		/* PCM does not have any params to be set.
+		Save the MVS configuration information. */
+		audio->rate_type = MVS_AMR_MODE_UNDEF;
+		audio->frame_mode = MVS_FRAME_MODE_PCM_DL;
+		break;
+	}
+	case MVS_MODE_IS127:
+	case MVS_MODE_IS733:
+	case MVS_MODE_4GV_NB:
+	case MVS_MODE_4GV_WB: {
+		struct audio_mvs_set_voc_mode_msg set_voc_mode_msg;
+
+		/* Set EVRC mode. */
+		memset(&set_voc_mode_msg, 0, sizeof(set_voc_mode_msg));
+		set_voc_mode_msg.min_rate = cpu_to_be32(audio->rate_type);
+		set_voc_mode_msg.max_rate = cpu_to_be32(audio->rate_type);
+
+		msm_rpc_setup_req(&set_voc_mode_msg.rpc_hdr,
+				  audio->rpc_prog,
+				  audio->rpc_ver,
+				  MVS_VOC_SET_FRAME_RATE_PROC);
+
+		audio->rpc_status = RPC_STATUS_FAILURE;
+		rc = msm_rpc_write(audio->rpc_endpt,
+				   &set_voc_mode_msg,
+				   sizeof(set_voc_mode_msg));
+
+		if (rc >= 0) {
+			pr_debug("%s: RPC write for set voc mode done\n",
+					__func__);
+
+			/* Save the MVS configuration information. */
+			audio->frame_mode = MVS_FRAME_MODE_VOC_RX;
+
+			rc = 0;
+		} else {
+			pr_err("%s: RPC write for set voc mode failed %d\n",
+			       __func__, rc);
+		}
+		break;
+	}
+	case MVS_MODE_G711: {
+		struct audio_mvs_set_g711_mode_msg set_g711_mode_msg;
+
+		/* Set G711 mode. */
+		memset(&set_g711_mode_msg, 0, sizeof(set_g711_mode_msg));
+		set_g711_mode_msg.g711_mode = cpu_to_be32(audio->rate_type);
+
+		pr_debug("%s: mode of g711:%d\n",
+			       __func__, set_g711_mode_msg.g711_mode);
+
+		msm_rpc_setup_req(&set_g711_mode_msg.rpc_hdr,
+				 audio->rpc_prog,
+				 audio->rpc_ver,
+				 MVS_G711_SET_MODE_PROC);
+
+		audio->rpc_status = RPC_STATUS_FAILURE;
+		rc = msm_rpc_write(audio->rpc_endpt,
+				  &set_g711_mode_msg,
+				  sizeof(set_g711_mode_msg));
+
+		if (rc >= 0) {
+			pr_debug("%s: RPC write for set g711 mode done\n",
+					__func__);
+			/* Save the MVS configuration information. */
+			audio->frame_mode = MVS_FRAME_MODE_G711_DL;
+
+			rc = 0;
+		} else {
+		       pr_err("%s: RPC write for set g711 mode failed %d\n",
+			      __func__, rc);
+		}
+		break;
+	}
+	case MVS_MODE_G729A: {
+		struct audio_mvs_set_g729_mode_msg set_g729_mode_msg;
+
+		/* Set G729 mode. */
+		memset(&set_g729_mode_msg, 0, sizeof(set_g729_mode_msg));
+		set_g729_mode_msg.g729_mode = cpu_to_be32(audio->dtx_mode);
+
+		pr_debug("%s: mode of g729:%d\n",
+			       __func__, set_g729_mode_msg.g729_mode);
+
+		msm_rpc_setup_req(&set_g729_mode_msg.rpc_hdr,
+				 audio->rpc_prog,
+				 audio->rpc_ver,
+				 MVS_G729A_SET_MODE_PROC);
+
+		audio->rpc_status = RPC_STATUS_FAILURE;
+		rc = msm_rpc_write(audio->rpc_endpt,
+				  &set_g729_mode_msg,
+				  sizeof(set_g729_mode_msg));
+
+		if (rc >= 0) {
+			pr_debug("%s: RPC write for set g729 mode done\n",
+			       __func__);
+
+			/* Save the MVS configuration information. */
+			audio->frame_mode = MVS_FRAME_MODE_G729A_DL;
+
+			rc = 0;
+		} else {
+		       pr_err("%s: RPC write for set g729 mode failed %d\n",
+			      __func__, rc);
+		}
+		break;
+	}
+	case MVS_MODE_G722: {
+		struct audio_mvs_set_g722_mode_msg set_g722_mode_msg;
+
+		/* Set G722 mode. */
+		memset(&set_g722_mode_msg, 0, sizeof(set_g722_mode_msg));
+		set_g722_mode_msg.g722_mode = cpu_to_be32(audio->rate_type);
+
+		pr_debug("%s: mode of g722:%d\n",
+		      __func__, set_g722_mode_msg.g722_mode);
+
+		msm_rpc_setup_req(&set_g722_mode_msg.rpc_hdr,
+			audio->rpc_prog,
+			audio->rpc_ver,
+			MVS_G722_SET_MODE_PROC);
+
+		audio->rpc_status = RPC_STATUS_FAILURE;
+		rc = msm_rpc_write(audio->rpc_endpt,
+			 &set_g722_mode_msg,
+			 sizeof(set_g722_mode_msg));
+
+		if (rc >= 0) {
+			pr_debug("%s: RPC write for set g722 mode done\n",
+			__func__);
+
+			/* Save the MVS configuration information. */
+			audio->frame_mode = MVS_FRAME_MODE_G722_DL;
+
+			rc = 0;
+		}
+		break;
+	}
+	case MVS_MODE_G711A: {
+		struct audio_mvs_set_g711A_mode_msg set_g711A_mode_msg;
+		struct audio_mvs_set_dtx_mode_msg set_dtx_mode_msg;
+
+		/* Set G711A mode. */
+		memset(&set_g711A_mode_msg, 0, sizeof(set_g711A_mode_msg));
+		set_g711A_mode_msg.g711A_mode = cpu_to_be32(audio->rate_type);
+
+		pr_debug("%s: mode of g711A:%d\n",
+		       __func__, set_g711A_mode_msg.g711A_mode);
+
+		msm_rpc_setup_req(&set_g711A_mode_msg.rpc_hdr,
+			 audio->rpc_prog,
+			 audio->rpc_ver,
+			 MVS_G711A_SET_MODE_PROC);
+
+		audio->rpc_status = RPC_STATUS_FAILURE;
+		rc = msm_rpc_write(audio->rpc_endpt,
+			  &set_g711A_mode_msg,
+			  sizeof(set_g711A_mode_msg));
+
+		if (rc >= 0) {
+			pr_debug("%s: RPC write for set g711A mode done\n",
+				       __func__);
+
+			/* Save the MVS configuration information. */
+			audio->frame_mode = MVS_FRAME_MODE_G711A_DL;
+			/* Set DTX MODE. */
+			memset(&set_dtx_mode_msg, 0, sizeof(set_dtx_mode_msg));
+			set_dtx_mode_msg.dtx_mode =
+				cpu_to_be32((audio->dtx_mode));
+
+			msm_rpc_setup_req(&set_dtx_mode_msg.rpc_hdr,
+					  audio->rpc_prog,
+					  audio->rpc_ver,
+					  MVS_SET_DTX_MODE_PROC);
+
+			audio->rpc_status = RPC_STATUS_FAILURE;
+			rc = msm_rpc_write(audio->rpc_endpt,
+					   &set_dtx_mode_msg,
+					   sizeof(set_dtx_mode_msg));
+
+			if (rc >= 0) {
+				pr_debug("%s: RPC write for set dtx done\n",
+						 __func__);
+
+				rc = 0;
+			}
+			rc = 0;
+		} else {
+		pr_err("%s: RPC write for set g711A mode failed %d\n",
+		      __func__, rc);
+		}
+		break;
+	}
+	case MVS_MODE_EFR:
+	case MVS_MODE_FR:
+	case MVS_MODE_HR: {
+		struct audio_mvs_set_efr_mode_msg set_efr_mode_msg;
+
+		/* Set G729 mode. */
+		memset(&set_efr_mode_msg, 0, sizeof(set_efr_mode_msg));
+		set_efr_mode_msg.efr_mode = cpu_to_be32(audio->dtx_mode);
+
+		pr_debug("%s: mode of EFR, FR and HR:%d\n",
+			       __func__, set_efr_mode_msg.efr_mode);
+
+		msm_rpc_setup_req(&set_efr_mode_msg.rpc_hdr,
+				 audio->rpc_prog,
+				 audio->rpc_ver,
+				 MVS_GSM_SET_DTX_MODE_PROC);
+
+		audio->rpc_status = RPC_STATUS_FAILURE;
+		rc = msm_rpc_write(audio->rpc_endpt,
+				  &set_efr_mode_msg,
+				  sizeof(set_efr_mode_msg));
+
+		if (rc >= 0) {
+			pr_debug("%s: RPC write for set EFR, FR and HR mode done\n",
+			       __func__);
+
+			/* Save the MVS configuration information. */
+			if ((audio->mvs_mode == MVS_MODE_EFR) ||
+				(audio->mvs_mode == MVS_MODE_FR))
+				audio->frame_mode = MVS_FRAME_MODE_GSM_DL;
+			if (audio->mvs_mode == MVS_MODE_HR)
+				audio->frame_mode = MVS_FRAME_MODE_HR_DL;
+
+			rc = 0;
+		} else {
+		       pr_err("%s: RPC write for set EFR, FR and HR mode failed %d\n",
+			      __func__, rc);
+		}
+		break;
+	}
+	default:
+		rc = -EINVAL;
+		pr_err("Default case\n");
+	}
+	return rc;
+}
+
+static int audio_mvs_setup(struct audio_mvs_info_type *audio)
+{
+	int rc = 0;
+	struct audio_mvs_enable_msg enable_msg;
+
+	pr_debug("%s:\n", __func__);
+
+	/* Enable MVS. */
+	memset(&enable_msg, 0, sizeof(enable_msg));
+	enable_msg.enable_args.client_id = cpu_to_be32(MVS_CLIENT_ID_VOIP);
+	enable_msg.enable_args.mode = cpu_to_be32(audio->mvs_mode);
+	enable_msg.enable_args.ul_cb_func_id = cpu_to_be32(MVS_UL_CB_FUNC_ID);
+	enable_msg.enable_args.dl_cb_func_id = cpu_to_be32(MVS_DL_CB_FUNC_ID);
+	enable_msg.enable_args.context = cpu_to_be32(MVS_PKT_CONTEXT_ISR);
+
+	msm_rpc_setup_req(&enable_msg.rpc_hdr,
+			  audio->rpc_prog,
+			  audio->rpc_ver,
+			  MVS_ENABLE_PROC);
+
+	audio->rpc_status = RPC_STATUS_FAILURE;
+	rc = msm_rpc_write(audio->rpc_endpt, &enable_msg, sizeof(enable_msg));
+
+	if (rc >= 0) {
+		pr_debug("%s: RPC write for enable done\n", __func__);
+
+		rc = wait_event_timeout(audio->mode_wait,
+				(audio->rpc_status != RPC_STATUS_FAILURE),
+				10 * HZ);
+
+		if (rc > 0) {
+			pr_debug("%s: Wait event for enable succeeded\n",
+				 __func__);
+			rc = audio_mvs_setup_mode(audio);
+			if (rc < 0) {
+				pr_err("%s: Unknown MVS mode %d\n",
+				       __func__, audio->mvs_mode);
+			}
+			pr_err("rc value after mode setup: %d\n", rc);
+		} else {
+			pr_err("%s: Wait event for enable failed %d\n",
+			       __func__, rc);
+		}
+	} else {
+		pr_err("%s: RPC write for enable failed %d\n", __func__, rc);
+	}
+
+	return rc;
+}
+
+static int audio_mvs_start(struct audio_mvs_info_type *audio)
+{
+	int rc = 0;
+	struct audio_mvs_acquire_msg acquire_msg;
+
+	pr_info("%s:\n", __func__);
+
+	/* Prevent sleep. */
+	wake_lock(&audio->suspend_lock);
+	wake_lock(&audio->idle_lock);
+
+	/* Acquire MVS. */
+	memset(&acquire_msg, 0, sizeof(acquire_msg));
+	acquire_msg.acquire_args.client_id = cpu_to_be32(MVS_CLIENT_ID_VOIP);
+	acquire_msg.acquire_args.cb_func_id = cpu_to_be32(MVS_CB_FUNC_ID);
+
+	msm_rpc_setup_req(&acquire_msg.rpc_hdr,
+			  audio->rpc_prog,
+			  audio->rpc_ver,
+			  MVS_ACQUIRE_PROC);
+
+	audio->rpc_status = RPC_STATUS_FAILURE;
+	rc = msm_rpc_write(audio->rpc_endpt,
+			   &acquire_msg,
+			   sizeof(acquire_msg));
+
+	if (rc >= 0) {
+		pr_debug("%s: RPC write for acquire done\n", __func__);
+
+		rc = wait_event_timeout(audio->wait,
+			(audio->rpc_status != RPC_STATUS_FAILURE),
+			1 * HZ);
+
+		if (rc > 0) {
+
+			rc = audio_mvs_setup(audio);
+
+			if (rc == 0)
+				audio->state = AUDIO_MVS_STARTED;
+
+		} else {
+			pr_err("%s: Wait event for acquire failed %d\n",
+			       __func__, rc);
+
+			rc = -EBUSY;
+		}
+	} else {
+		pr_err("%s: RPC write for acquire failed %d\n", __func__, rc);
+
+		rc = -EBUSY;
+	}
+
+	return rc;
+}
+
+static int audio_mvs_stop(struct audio_mvs_info_type *audio)
+{
+	int rc = 0;
+	struct audio_mvs_release_msg release_msg;
+
+	pr_info("%s:\n", __func__);
+
+	/* Release MVS. */
+	memset(&release_msg, 0, sizeof(release_msg));
+	release_msg.client_id = cpu_to_be32(MVS_CLIENT_ID_VOIP);
+
+	msm_rpc_setup_req(&release_msg.rpc_hdr,
+			  audio->rpc_prog,
+			  audio->rpc_ver,
+			  MVS_RELEASE_PROC);
+
+	audio->rpc_status = RPC_STATUS_FAILURE;
+	rc = msm_rpc_write(audio->rpc_endpt, &release_msg, sizeof(release_msg));
+
+	if (rc >= 0) {
+		pr_debug("%s: RPC write for release done\n", __func__);
+
+		rc = wait_event_timeout(audio->mode_wait,
+				(audio->rpc_status != RPC_STATUS_FAILURE),
+				1 * HZ);
+
+		if (rc > 0) {
+			pr_debug("%s: Wait event for release succeeded\n",
+				 __func__);
+
+			audio->state = AUDIO_MVS_STOPPED;
+
+			/* Un-block read in case it is waiting for data. */
+			wake_up(&audio->out_wait);
+			rc = 0;
+		} else {
+			pr_err("%s: Wait event for release failed %d\n",
+			       __func__, rc);
+		}
+	} else {
+		pr_err("%s: RPC write for release failed %d\n", __func__, rc);
+	}
+
+	/* Allow sleep. */
+	wake_unlock(&audio->suspend_lock);
+	wake_unlock(&audio->idle_lock);
+
+	return rc;
+}
+
+static void audio_mvs_process_rpc_request(uint32_t procedure,
+					  uint32_t xid,
+					  void *data,
+					  uint32_t length,
+					  struct audio_mvs_info_type *audio)
+{
+	int rc = 0;
+
+	pr_debug("%s:\n", __func__);
+
+	switch (procedure) {
+	case MVS_EVENT_CB_TYPE_PROC: {
+		struct audio_mvs_cb_func_args *args = data;
+		struct rpc_reply_hdr reply_hdr;
+
+		pr_debug("%s: MVS CB CB_FUNC_ID 0x%x\n",
+			 __func__, be32_to_cpu(args->cb_func_id));
+
+		if (be32_to_cpu(args->valid_ptr)) {
+			uint32_t event_type = be32_to_cpu(args->event);
+
+			pr_debug("%s: MVS CB event type %d\n",
+				 __func__, be32_to_cpu(args->event));
+
+			if (event_type == AUDIO_MVS_COMMAND) {
+				uint32_t cmd_status = be32_to_cpu(
+			args->event_data.mvs_ev_command_type.cmd_status);
+
+				pr_debug("%s: MVS CB command status %d\n",
+					 __func__, cmd_status);
+
+				if (cmd_status == AUDIO_MVS_CMD_SUCCESS) {
+					audio->rpc_status = RPC_STATUS_SUCCESS;
+					wake_up(&audio->wait);
+				}
+
+			} else if (event_type == AUDIO_MVS_MODE) {
+				uint32_t mode_status = be32_to_cpu(
+				args->event_data.mvs_ev_mode_type.mode_status);
+
+				pr_debug("%s: MVS CB mode status %d\n",
+					 __func__, mode_status);
+
+				if (mode_status == AUDIO_MVS_MODE_READY) {
+					audio->rpc_status = RPC_STATUS_SUCCESS;
+					wake_up(&audio->mode_wait);
+				}
+			} else {
+				pr_err("%s: MVS CB unknown event type %d\n",
+				       __func__, event_type);
+			}
+		} else {
+			pr_err("%s: MVS CB event pointer not valid\n",
+			       __func__);
+		}
+
+		/* Send ack to modem. */
+		memset(&reply_hdr, 0, sizeof(reply_hdr));
+		reply_hdr.xid = cpu_to_be32(xid);
+		reply_hdr.type = cpu_to_be32(RPC_TYPE_REPLY);
+		reply_hdr.reply_stat = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED);
+
+		reply_hdr.data.acc_hdr.accept_stat = cpu_to_be32(
+			RPC_ACCEPTSTAT_SUCCESS);
+		reply_hdr.data.acc_hdr.verf_flavor = 0;
+		reply_hdr.data.acc_hdr.verf_length = 0;
+
+		rc = msm_rpc_write(audio->rpc_endpt,
+				   &reply_hdr,
+				   sizeof(reply_hdr));
+
+		if (rc < 0)
+			pr_err("%s: RPC write for response failed %d\n",
+			       __func__, rc);
+
+		break;
+	}
+
+	case MVS_PACKET_UL_FN_TYPE_PROC: {
+		uint32_t *args = data;
+		uint32_t pkt_len;
+		uint32_t frame_mode;
+		struct audio_mvs_ul_reply ul_reply;
+		struct audio_mvs_buf_node *buf_node = NULL;
+
+		pr_debug("%s: MVS UL CB_FUNC_ID 0x%x\n",
+			 __func__, be32_to_cpu(*args));
+		args++;
+
+		pkt_len = be32_to_cpu(*args);
+		pr_debug("%s: UL pkt_len %d\n", __func__, pkt_len);
+		args++;
+
+		/* Copy the vocoder packets. */
+		mutex_lock(&audio->out_lock);
+
+		if (!list_empty(&audio->free_out_queue)) {
+			buf_node = list_first_entry(&audio->free_out_queue,
+						    struct audio_mvs_buf_node,
+						    list);
+			list_del(&buf_node->list);
+
+			memcpy(&buf_node->frame.voc_pkt[0], args, pkt_len);
+			buf_node->frame.len = pkt_len;
+			pkt_len = ALIGN(pkt_len, 4);
+			args = args + pkt_len/4;
+
+			pr_debug("%s: UL valid_ptr 0x%x\n",
+				 __func__, be32_to_cpu(*args));
+			args++;
+
+			frame_mode = be32_to_cpu(*args);
+			pr_debug("%s: UL frame_mode %d\n",
+				 __func__, frame_mode);
+			args++;
+
+			pr_debug("%s: UL frame_mode %d\n",
+				 __func__, be32_to_cpu(*args));
+			args++;
+
+			pr_debug("%s: UL frame_mode %d\n",
+				 __func__, be32_to_cpu(*args));
+			args++;
+
+			pr_debug("%s: UL mvs_mode %d\n",
+				 __func__, be32_to_cpu(*args));
+			args++;
+
+			pr_debug("%s: UL buf_free_cnt %d\n",
+				 __func__, be32_to_cpu(*args));
+			args++;
+
+			if (frame_mode == MVS_FRAME_MODE_AMR_UL) {
+				/* Extract AMR frame type. */
+				buf_node->frame.frame_type = be32_to_cpu(*args);
+
+				pr_debug("%s: UL AMR frame_type %d\n",
+					 __func__, be32_to_cpu(*args));
+			} else if ((frame_mode == MVS_FRAME_MODE_PCM_UL) ||
+				   (frame_mode == MVS_FRAME_MODE_VOC_TX)) {
+				/* PCM and EVRC don't have frame_type */
+				buf_node->frame.frame_type = 0;
+			} else if (frame_mode == MVS_FRAME_MODE_G711_UL) {
+				/* Extract G711 frame type. */
+				buf_node->frame.frame_type = be32_to_cpu(*args);
+
+				pr_debug("%s: UL G711 frame_type %d\n",
+					__func__, be32_to_cpu(*args));
+			} else if (frame_mode == MVS_FRAME_MODE_G729A_UL) {
+				/* Extract G729 frame type. */
+				buf_node->frame.frame_type = be32_to_cpu(*args);
+
+				pr_debug("%s: UL G729 frame_type %d\n",
+					__func__, be32_to_cpu(*args));
+			} else if (frame_mode == MVS_FRAME_MODE_G722_UL) {
+				/* Extract G722 frame type. */
+				buf_node->frame.frame_type = be32_to_cpu(*args);
+
+				pr_debug("%s: UL G722 frame_type %d\n",
+				       __func__, be32_to_cpu(*args));
+			} else if (frame_mode == MVS_FRAME_MODE_G711A_UL) {
+				/* Extract G711A frame type. */
+				buf_node->frame.frame_type = be32_to_cpu(*args);
+
+				pr_debug("%s: UL G711A frame_type %d\n",
+				       __func__, be32_to_cpu(*args));
+			} else if ((frame_mode == MVS_FRAME_MODE_GSM_UL) ||
+				   (frame_mode == MVS_FRAME_MODE_HR_UL)) {
+				/* Extract EFR, FR and HR frame type. */
+				buf_node->frame.frame_type = be32_to_cpu(*args);
+
+				pr_debug("%s: UL EFR,FR,HR frame_type %d\n",
+					__func__, be32_to_cpu(*args));
+			} else {
+				pr_debug("%s: UL Unknown frame mode %d\n",
+				       __func__, frame_mode);
+			}
+
+			list_add_tail(&buf_node->list, &audio->out_queue);
+		} else {
+			pr_err("%s: UL data dropped, read is slow\n", __func__);
+		}
+
+		mutex_unlock(&audio->out_lock);
+
+		wake_up(&audio->out_wait);
+
+		/* Send UL message accept to modem. */
+		memset(&ul_reply, 0, sizeof(ul_reply));
+		ul_reply.reply_hdr.xid = cpu_to_be32(xid);
+		ul_reply.reply_hdr.type = cpu_to_be32(RPC_TYPE_REPLY);
+		ul_reply.reply_hdr.reply_stat = cpu_to_be32(
+			RPCMSG_REPLYSTAT_ACCEPTED);
+
+		ul_reply.reply_hdr.data.acc_hdr.accept_stat = cpu_to_be32(
+			RPC_ACCEPTSTAT_SUCCESS);
+		ul_reply.reply_hdr.data.acc_hdr.verf_flavor = 0;
+		ul_reply.reply_hdr.data.acc_hdr.verf_length = 0;
+
+		ul_reply.valid_pkt_status_ptr = cpu_to_be32(0x00000001);
+		ul_reply.pkt_status = cpu_to_be32(0x00000000);
+
+		rc = msm_rpc_write(audio->rpc_endpt,
+				   &ul_reply,
+				   sizeof(ul_reply));
+
+		if (rc < 0)
+			pr_err("%s: RPC write for UL response failed %d\n",
+			       __func__, rc);
+
+		break;
+	}
+
+	case MVS_PACKET_DL_FN_TYPE_PROC: {
+		struct audio_mvs_dl_cb_func_args *args = data;
+		struct audio_mvs_dl_reply dl_reply;
+		uint32_t frame_mode;
+		struct audio_mvs_buf_node *buf_node = NULL;
+
+		pr_debug("%s: MVS DL CB CB_FUNC_ID 0x%x\n",
+			 __func__, be32_to_cpu(args->cb_func_id));
+
+		frame_mode = be32_to_cpu(args->frame_mode);
+		pr_debug("%s: DL frame_mode %d\n", __func__, frame_mode);
+
+		/* Prepare and send the DL packets to modem. */
+		memset(&dl_reply, 0, sizeof(dl_reply));
+		dl_reply.reply_hdr.xid = cpu_to_be32(xid);
+		dl_reply.reply_hdr.type = cpu_to_be32(RPC_TYPE_REPLY);
+		dl_reply.reply_hdr.reply_stat = cpu_to_be32(
+			RPCMSG_REPLYSTAT_ACCEPTED);
+
+		dl_reply.reply_hdr.data.acc_hdr.accept_stat = cpu_to_be32(
+			RPC_ACCEPTSTAT_SUCCESS);
+		dl_reply.reply_hdr.data.acc_hdr.verf_flavor = 0;
+		dl_reply.reply_hdr.data.acc_hdr.verf_length = 0;
+
+		mutex_lock(&audio->in_lock);
+
+		if (!list_empty(&audio->in_queue)) {
+			buf_node = list_first_entry(&audio->in_queue,
+						    struct audio_mvs_buf_node,
+						    list);
+			list_del(&buf_node->list);
+
+			memcpy(&dl_reply.voc_pkt,
+			       &buf_node->frame.voc_pkt[0],
+			       buf_node->frame.len);
+
+			pr_debug("%s:frame mode %d\n", __func__, frame_mode);
+			if (frame_mode == MVS_FRAME_MODE_AMR_DL) {
+				dl_reply.cdc_param.gnr_arg.param1 = cpu_to_be32(
+					buf_node->frame.frame_type);
+				dl_reply.cdc_param.gnr_arg.param2 =
+						cpu_to_be32(audio->rate_type);
+				dl_reply.cdc_param.\
+						gnr_arg.valid_pkt_status_ptr =
+							cpu_to_be32(0x00000001);
+				dl_reply.cdc_param.gnr_arg.pkt_status =
+					cpu_to_be32(AUDIO_MVS_PKT_NORMAL);
+			} else if (frame_mode == MVS_FRAME_MODE_PCM_DL) {
+				dl_reply.cdc_param.gnr_arg.param1 = 0;
+				dl_reply.cdc_param.gnr_arg.param2 = 0;
+				dl_reply.cdc_param.\
+						gnr_arg.valid_pkt_status_ptr =
+							cpu_to_be32(0x00000001);
+				dl_reply.cdc_param.gnr_arg.pkt_status =
+					cpu_to_be32(AUDIO_MVS_PKT_NORMAL);
+			} else if (frame_mode == MVS_FRAME_MODE_VOC_RX) {
+				dl_reply.cdc_param.gnr_arg.param1 =
+						cpu_to_be32(audio->rate_type);
+				dl_reply.cdc_param.gnr_arg.param2 = 0;
+				dl_reply.cdc_param.\
+						gnr_arg.valid_pkt_status_ptr =
+							cpu_to_be32(0x00000001);
+				dl_reply.cdc_param.gnr_arg.pkt_status =
+					cpu_to_be32(AUDIO_MVS_PKT_NORMAL);
+			} else if (frame_mode == MVS_FRAME_MODE_G711_DL) {
+				dl_reply.cdc_param.g711_arg.param1 =
+				cpu_to_be32(buf_node->frame.frame_type);
+				dl_reply.cdc_param.\
+						g711_arg.valid_pkt_status_ptr =
+							cpu_to_be32(0x00000001);
+				dl_reply.cdc_param.g711_arg.pkt_status =
+					cpu_to_be32(AUDIO_MVS_PKT_NORMAL);
+			} else if (frame_mode == MVS_FRAME_MODE_G729A_DL) {
+				dl_reply.cdc_param.gnr_arg.param1 = cpu_to_be32(
+				       buf_node->frame.frame_type);
+				dl_reply.cdc_param.gnr_arg.param2 =
+						cpu_to_be32(audio->rate_type);
+				dl_reply.cdc_param.\
+						gnr_arg.valid_pkt_status_ptr =
+							cpu_to_be32(0x00000001);
+				dl_reply.cdc_param.gnr_arg.pkt_status =
+					cpu_to_be32(AUDIO_MVS_PKT_NORMAL);
+			} else if (frame_mode == MVS_FRAME_MODE_G722_DL) {
+				dl_reply.cdc_param.gnr_arg.param1 = cpu_to_be32(
+				      buf_node->frame.frame_type);
+				dl_reply.cdc_param.gnr_arg.param2 =
+						cpu_to_be32(audio->rate_type);
+				dl_reply.cdc_param.\
+						gnr_arg.valid_pkt_status_ptr =
+							cpu_to_be32(0x00000001);
+				dl_reply.cdc_param.gnr_arg.pkt_status =
+					cpu_to_be32(AUDIO_MVS_PKT_NORMAL);
+			} else if (frame_mode == MVS_FRAME_MODE_G711A_DL) {
+				dl_reply.cdc_param.gnr_arg.param1 = cpu_to_be32(
+				       buf_node->frame.frame_type);
+				dl_reply.cdc_param.gnr_arg.param2 =
+						cpu_to_be32(audio->rate_type);
+				dl_reply.cdc_param.\
+						gnr_arg.valid_pkt_status_ptr =
+							cpu_to_be32(0x00000001);
+				dl_reply.cdc_param.gnr_arg.pkt_status =
+					cpu_to_be32(AUDIO_MVS_PKT_NORMAL);
+			} else if ((frame_mode == MVS_FRAME_MODE_GSM_DL) ||
+				   (frame_mode == MVS_FRAME_MODE_HR_DL)) {
+				dl_reply.cdc_param.gnr_arg.param1 = cpu_to_be32(
+				       buf_node->frame.frame_type);
+				dl_reply.cdc_param.gnr_arg.param2 =
+						cpu_to_be32(audio->rate_type);
+				dl_reply.cdc_param.\
+						gnr_arg.valid_pkt_status_ptr =
+							cpu_to_be32(0x00000001);
+				dl_reply.cdc_param.gnr_arg.pkt_status =
+					cpu_to_be32(AUDIO_MVS_PKT_NORMAL);
+			} else {
+				pr_err("%s: DL Unknown frame mode %d\n",
+				       __func__, frame_mode);
+			}
+			list_add_tail(&buf_node->list, &audio->free_in_queue);
+		} else {
+			pr_debug("%s: No DL data available to send to MVS\n",
+				 __func__);
+			if (frame_mode == MVS_FRAME_MODE_G711_DL) {
+				dl_reply.cdc_param.\
+						g711_arg.valid_pkt_status_ptr =
+							cpu_to_be32(0x00000001);
+				dl_reply.cdc_param.g711_arg.pkt_status =
+						cpu_to_be32(AUDIO_MVS_PKT_SLOW);
+			} else {
+				dl_reply.cdc_param.\
+						gnr_arg.valid_pkt_status_ptr =
+							cpu_to_be32(0x00000001);
+				dl_reply.cdc_param.gnr_arg.pkt_status =
+						cpu_to_be32(AUDIO_MVS_PKT_SLOW);
+			}
+		}
+
+		mutex_unlock(&audio->in_lock);
+
+		dl_reply.valid_frame_info_ptr = cpu_to_be32(0x00000001);
+
+		dl_reply.frame_mode = cpu_to_be32(audio->frame_mode);
+		dl_reply.frame_mode_again = cpu_to_be32(audio->frame_mode);
+
+		dl_reply.frame_info_hdr.frame_mode =
+			cpu_to_be32(audio->frame_mode);
+		dl_reply.frame_info_hdr.mvs_mode = cpu_to_be32(audio->mvs_mode);
+		dl_reply.frame_info_hdr.buf_free_cnt = 0;
+
+		rc = msm_rpc_write(audio->rpc_endpt,
+				   &dl_reply,
+				   sizeof(dl_reply));
+
+		if (rc < 0)
+			pr_err("%s: RPC write for DL response failed %d\n",
+			       __func__, rc);
+
+		break;
+	}
+
+	default:
+		pr_err("%s: Unknown CB type %d\n", __func__, procedure);
+	}
+}
+
+static int audio_mvs_thread(void *data)
+{
+	struct audio_mvs_info_type *audio = data;
+	struct rpc_request_hdr *rpc_hdr = NULL;
+
+	pr_info("%s:\n", __func__);
+
+	while (!kthread_should_stop()) {
+
+		int rpc_hdr_len = msm_rpc_read(audio->rpc_endpt,
+					       (void **) &rpc_hdr,
+					       -1,
+					       -1);
+
+		if (rpc_hdr_len < 0) {
+			pr_err("%s: RPC read failed %d\n",
+			       __func__, rpc_hdr_len);
+
+			break;
+		} else if (rpc_hdr_len < RPC_COMMON_HDR_SZ) {
+			continue;
+		} else {
+			uint32_t rpc_type = be32_to_cpu(rpc_hdr->type);
+			if (rpc_type == RPC_TYPE_REPLY) {
+				struct rpc_reply_hdr *rpc_reply =
+					(void *) rpc_hdr;
+				uint32_t reply_status;
+
+				if (rpc_hdr_len < RPC_REPLY_HDR_SZ)
+					continue;
+
+				reply_status =
+					be32_to_cpu(rpc_reply->reply_stat);
+
+				if (reply_status != RPCMSG_REPLYSTAT_ACCEPTED) {
+					/* If the command is not accepted, there
+					 * will be no response callback. Wake
+					 * the caller and report error. */
+					audio->rpc_status = RPC_STATUS_REJECT;
+
+					wake_up(&audio->wait);
+
+					pr_err("%s: RPC reply status denied\n",
+					       __func__);
+				}
+			} else if (rpc_type == RPC_TYPE_REQUEST) {
+				if (rpc_hdr_len < RPC_REQUEST_HDR_SZ)
+					continue;
+
+				audio_mvs_process_rpc_request(
+					be32_to_cpu(rpc_hdr->procedure),
+					be32_to_cpu(rpc_hdr->xid),
+					(void *) (rpc_hdr + 1),
+					(rpc_hdr_len - sizeof(*rpc_hdr)),
+					audio);
+			} else {
+				pr_err("%s: Unexpected RPC type %d\n",
+				       __func__, rpc_type);
+			}
+		}
+
+		kfree(rpc_hdr);
+		rpc_hdr = NULL;
+	}
+
+	pr_info("%s: MVS thread stopped\n", __func__);
+
+	return 0;
+}
+
+static int audio_mvs_alloc_buf(struct audio_mvs_info_type *audio)
+{
+	int i = 0;
+	struct audio_mvs_buf_node *buf_node = NULL;
+	struct list_head *ptr = NULL;
+	struct list_head *next = NULL;
+
+	pr_debug("%s:\n", __func__);
+
+	/* Allocate input buffers. */
+	for (i = 0; i < MVS_MAX_Q_LEN; i++) {
+			buf_node = kmalloc(sizeof(struct audio_mvs_buf_node),
+					   GFP_KERNEL);
+
+			if (buf_node != NULL) {
+				list_add_tail(&buf_node->list,
+					      &audio->free_in_queue);
+			} else {
+				pr_err("%s: No memory for IO buffers\n",
+				       __func__);
+				goto err;
+			}
+			buf_node = NULL;
+	}
+
+	/* Allocate output buffers. */
+	for (i = 0; i < MVS_MAX_Q_LEN; i++) {
+			buf_node = kmalloc(sizeof(struct audio_mvs_buf_node),
+					   GFP_KERNEL);
+
+			if (buf_node != NULL) {
+				list_add_tail(&buf_node->list,
+					      &audio->free_out_queue);
+			} else {
+				pr_err("%s: No memory for IO buffers\n",
+				       __func__);
+				goto err;
+			}
+			buf_node = NULL;
+	}
+
+	return 0;
+
+err:
+	list_for_each_safe(ptr, next, &audio->free_in_queue) {
+		buf_node = list_entry(ptr, struct audio_mvs_buf_node, list);
+		list_del(&buf_node->list);
+		kfree(buf_node);
+		buf_node = NULL;
+	}
+
+	ptr = next = NULL;
+	list_for_each_safe(ptr, next, &audio->free_out_queue) {
+		buf_node = list_entry(ptr, struct audio_mvs_buf_node, list);
+		list_del(&buf_node->list);
+		kfree(buf_node);
+		buf_node = NULL;
+	}
+
+	return -ENOMEM;
+}
+
+static void audio_mvs_free_buf(struct audio_mvs_info_type *audio)
+{
+	struct list_head *ptr = NULL;
+	struct list_head *next = NULL;
+	struct audio_mvs_buf_node *buf_node = NULL;
+
+	pr_debug("%s:\n", __func__);
+
+	mutex_lock(&audio->in_lock);
+	/* Free input buffers. */
+	list_for_each_safe(ptr, next, &audio->in_queue) {
+		buf_node = list_entry(ptr, struct audio_mvs_buf_node, list);
+		list_del(&buf_node->list);
+		kfree(buf_node);
+		buf_node = NULL;
+	}
+
+	ptr = next = NULL;
+	/* Free free_input buffers. */
+	list_for_each_safe(ptr, next, &audio->free_in_queue) {
+		buf_node = list_entry(ptr, struct audio_mvs_buf_node, list);
+		list_del(&buf_node->list);
+		kfree(buf_node);
+		buf_node = NULL;
+	}
+	mutex_unlock(&audio->in_lock);
+
+	mutex_lock(&audio->out_lock);
+	ptr = next = NULL;
+	/* Free output buffers. */
+	list_for_each_safe(ptr, next, &audio->out_queue) {
+		buf_node = list_entry(ptr, struct audio_mvs_buf_node, list);
+		list_del(&buf_node->list);
+		kfree(buf_node);
+		buf_node = NULL;
+	}
+
+	/* Free free_ioutput buffers. */
+	ptr = next = NULL;
+	list_for_each_safe(ptr, next, &audio->free_out_queue) {
+		buf_node = list_entry(ptr, struct audio_mvs_buf_node, list);
+		list_del(&buf_node->list);
+		kfree(buf_node);
+		buf_node = NULL;
+	}
+	mutex_unlock(&audio->out_lock);
+}
+
+static int audio_mvs_open(struct inode *inode, struct file *file)
+{
+	int rc = 0;
+
+	pr_info("%s:\n", __func__);
+
+	mutex_lock(&audio_mvs_info.lock);
+
+	if (audio_mvs_info.state == AUDIO_MVS_CLOSED) {
+
+		if (audio_mvs_info.task != NULL ||
+			audio_mvs_info.rpc_endpt != NULL) {
+			rc = audio_mvs_alloc_buf(&audio_mvs_info);
+
+			if (rc == 0) {
+				audio_mvs_info.state = AUDIO_MVS_OPENED;
+				file->private_data = &audio_mvs_info;
+			}
+		}  else {
+			pr_err("%s: MVS thread and RPC end point do not exist\n",
+				   __func__);
+
+			rc = -ENODEV;
+		}
+	} else {
+		pr_err("%s: MVS driver exists, state %d\n",
+		       __func__, audio_mvs_info.state);
+
+		rc = -EBUSY;
+	}
+
+	mutex_unlock(&audio_mvs_info.lock);
+
+	return rc;
+}
+
+static int audio_mvs_release(struct inode *inode, struct file *file)
+{
+
+	struct audio_mvs_info_type *audio = file->private_data;
+
+	pr_info("%s:\n", __func__);
+
+	mutex_lock(&audio->lock);
+	if (audio->state == AUDIO_MVS_STARTED)
+		audio_mvs_stop(audio);
+	audio_mvs_free_buf(audio);
+	audio->state = AUDIO_MVS_CLOSED;
+	mutex_unlock(&audio->lock);
+
+	pr_debug("%s: Release done\n", __func__);
+	return 0;
+}
+
+static ssize_t audio_mvs_read(struct file *file,
+			      char __user *buf,
+			      size_t count,
+			      loff_t *pos)
+{
+	int rc = 0;
+	struct audio_mvs_buf_node *buf_node = NULL;
+	struct audio_mvs_info_type *audio = file->private_data;
+
+	pr_debug("%s:\n", __func__);
+
+	rc = wait_event_interruptible_timeout(audio->out_wait,
+			(!list_empty(&audio->out_queue) ||
+			 audio->state == AUDIO_MVS_STOPPED),
+			1 * HZ);
+
+	if (rc > 0) {
+		mutex_lock(&audio->out_lock);
+		if ((audio->state == AUDIO_MVS_STARTED) &&
+		    (!list_empty(&audio->out_queue))) {
+
+			if (count >= sizeof(struct msm_audio_mvs_frame)) {
+				buf_node = list_first_entry(&audio->out_queue,
+						struct audio_mvs_buf_node,
+						list);
+				list_del(&buf_node->list);
+
+				rc = copy_to_user(buf,
+					&buf_node->frame,
+					sizeof(struct msm_audio_mvs_frame));
+
+				if (rc == 0) {
+					rc = buf_node->frame.len +
+					    sizeof(buf_node->frame.frame_type) +
+					    sizeof(buf_node->frame.len);
+				} else {
+					pr_err("%s: Copy to user retuned %d",
+					       __func__, rc);
+
+					rc = -EFAULT;
+				}
+
+				list_add_tail(&buf_node->list,
+					      &audio->free_out_queue);
+			} else {
+				pr_err("%s: Read count %d < sizeof(frame) %d",
+				       __func__, count,
+				       sizeof(struct msm_audio_mvs_frame));
+
+				rc = -ENOMEM;
+			}
+		} else {
+			pr_err("%s: Read performed in state %d\n",
+			       __func__, audio->state);
+
+			rc = -EPERM;
+		}
+		mutex_unlock(&audio->out_lock);
+
+	} else if (rc == 0) {
+		pr_err("%s: No UL data available\n", __func__);
+
+		rc = -ETIMEDOUT;
+	} else {
+		pr_err("%s: Read was interrupted\n", __func__);
+
+		rc = -ERESTARTSYS;
+	}
+
+	return rc;
+}
+
+static ssize_t audio_mvs_write(struct file *file,
+			       const char __user *buf,
+			       size_t count,
+			       loff_t *pos)
+{
+	int rc = 0;
+	struct audio_mvs_buf_node *buf_node = NULL;
+	struct audio_mvs_info_type *audio = file->private_data;
+
+	pr_debug("%s:\n", __func__);
+
+	mutex_lock(&audio->in_lock);
+	if (audio->state == AUDIO_MVS_STARTED) {
+		if (count <= sizeof(struct msm_audio_mvs_frame)) {
+			if (!list_empty(&audio->free_in_queue)) {
+				buf_node =
+					list_first_entry(&audio->free_in_queue,
+						struct audio_mvs_buf_node,
+						list);
+				list_del(&buf_node->list);
+
+				rc = copy_from_user(&buf_node->frame,
+						    buf,
+						    count);
+
+				list_add_tail(&buf_node->list,
+					      &audio->in_queue);
+			} else {
+				pr_err("%s: No free DL buffs\n", __func__);
+			}
+		} else {
+			pr_err("%s: Write count %d < sizeof(frame) %d",
+			       __func__, count,
+			       sizeof(struct msm_audio_mvs_frame));
+
+			rc = -ENOMEM;
+		}
+	} else {
+		pr_err("%s: Write performed in invalid state %d\n",
+		       __func__, audio->state);
+
+		rc = -EPERM;
+	}
+	mutex_unlock(&audio->in_lock);
+
+	return rc;
+}
+
+static long audio_mvs_ioctl(struct file *file,
+			    unsigned int cmd,
+			    unsigned long arg)
+{
+	int rc = 0;
+
+	struct audio_mvs_info_type *audio = file->private_data;
+
+	pr_info("%s:\n", __func__);
+
+	switch (cmd) {
+	case AUDIO_GET_MVS_CONFIG: {
+		struct msm_audio_mvs_config config;
+
+		pr_debug("%s: IOCTL GET_MVS_CONFIG\n", __func__);
+
+		mutex_lock(&audio->lock);
+		config.mvs_mode = audio->mvs_mode;
+		config.rate_type = audio->rate_type;
+		mutex_unlock(&audio->lock);
+
+		rc = copy_to_user((void *)arg, &config, sizeof(config));
+		if (rc == 0)
+			rc = sizeof(config);
+		else
+			pr_err("%s: Config copy failed %d\n", __func__, rc);
+
+		break;
+	}
+
+	case AUDIO_SET_MVS_CONFIG: {
+		struct msm_audio_mvs_config config;
+
+		pr_debug("%s: IOCTL SET_MVS_CONFIG\n", __func__);
+
+		rc = copy_from_user(&config, (void *)arg, sizeof(config));
+		if (rc == 0) {
+			mutex_lock(&audio->lock);
+
+			if (audio->state == AUDIO_MVS_OPENED) {
+				audio->mvs_mode = config.mvs_mode;
+				audio->rate_type = config.rate_type;
+				audio->dtx_mode = config.dtx_mode;
+			} else {
+				pr_err("%s: Set confg called in state %d\n",
+				       __func__, audio->state);
+
+				rc = -EPERM;
+			}
+
+			mutex_unlock(&audio->lock);
+		} else {
+			pr_err("%s: Config copy failed %d\n", __func__, rc);
+		}
+
+		break;
+	}
+
+	case AUDIO_START: {
+		pr_debug("%s: IOCTL START\n", __func__);
+
+		mutex_lock(&audio->lock);
+
+		if (audio->state == AUDIO_MVS_OPENED ||
+		    audio->state == AUDIO_MVS_STOPPED) {
+			rc = audio_mvs_start(audio);
+
+			if (rc != 0)
+				audio_mvs_stop(audio);
+		} else {
+			pr_err("%s: Start called in invalid state %d\n",
+			       __func__, audio->state);
+
+			rc = -EPERM;
+		}
+
+		mutex_unlock(&audio->lock);
+
+		break;
+	}
+
+	case AUDIO_STOP: {
+		pr_debug("%s: IOCTL STOP\n", __func__);
+
+		mutex_lock(&audio->lock);
+
+		if (audio->state == AUDIO_MVS_STARTED) {
+			rc = audio_mvs_stop(audio);
+		} else {
+			pr_err("%s: Stop called in invalid state %d\n",
+			       __func__, audio->state);
+
+			rc = -EPERM;
+		}
+
+		mutex_unlock(&audio->lock);
+		break;
+	}
+
+	default: {
+		pr_err("%s: Unknown IOCTL %d\n", __func__, cmd);
+	}
+	}
+
+	return rc;
+}
+
+static const struct file_operations audio_mvs_fops = {
+	.owner = THIS_MODULE,
+	.open = audio_mvs_open,
+	.release = audio_mvs_release,
+	.read = audio_mvs_read,
+	.write = audio_mvs_write,
+	.unlocked_ioctl = audio_mvs_ioctl
+};
+
+struct miscdevice audio_mvs_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "msm_mvs",
+	.fops = &audio_mvs_fops
+};
+
+static int __init audio_mvs_init(void)
+{
+	int rc;
+
+	pr_info("%s:\n", __func__);
+
+	memset(&audio_mvs_info, 0, sizeof(audio_mvs_info));
+	mutex_init(&audio_mvs_info.lock);
+	mutex_init(&audio_mvs_info.in_lock);
+	mutex_init(&audio_mvs_info.out_lock);
+
+	init_waitqueue_head(&audio_mvs_info.wait);
+	init_waitqueue_head(&audio_mvs_info.mode_wait);
+	init_waitqueue_head(&audio_mvs_info.out_wait);
+
+	INIT_LIST_HEAD(&audio_mvs_info.in_queue);
+	INIT_LIST_HEAD(&audio_mvs_info.free_in_queue);
+	INIT_LIST_HEAD(&audio_mvs_info.out_queue);
+	INIT_LIST_HEAD(&audio_mvs_info.free_out_queue);
+
+	wake_lock_init(&audio_mvs_info.suspend_lock,
+		       WAKE_LOCK_SUSPEND,
+		       "audio_mvs_suspend");
+	wake_lock_init(&audio_mvs_info.idle_lock,
+		       WAKE_LOCK_IDLE,
+		       "audio_mvs_idle");
+
+	audio_mvs_info.rpc_endpt = msm_rpc_connect_compatible(MVS_PROG,
+					MVS_VERS_COMP_VER5,
+					MSM_RPC_UNINTERRUPTIBLE);
+
+	if (IS_ERR(audio_mvs_info.rpc_endpt)) {
+		pr_err("%s: MVS RPC connect failed ver 0x%x\n", __func__,
+				MVS_VERS_COMP_VER5);
+		audio_mvs_info.rpc_endpt = msm_rpc_connect_compatible(MVS_PROG,
+					MVS_VERS_COMP_VER4,
+					MSM_RPC_UNINTERRUPTIBLE);
+		if (IS_ERR(audio_mvs_info.rpc_endpt)) {
+			pr_err("%s: MVS RPC connect failed ver 0x%x\n",
+				__func__, MVS_VERS_COMP_VER4);
+			audio_mvs_info.rpc_endpt =
+				msm_rpc_connect_compatible(MVS_PROG,
+				MVS_VERS,
+				MSM_RPC_UNINTERRUPTIBLE);
+			if (IS_ERR(audio_mvs_info.rpc_endpt)) {
+				pr_err("%s: MVS RPC connect failed ver 0x%x\n",
+				   __func__, MVS_VERS);
+				rc = PTR_ERR(audio_mvs_info.rpc_endpt);
+				audio_mvs_info.rpc_endpt = NULL;
+				goto done;
+			} else {
+				pr_debug("%s: MVS RPC connect succeeded ver\
+					0x%x\n", __func__, MVS_VERS);
+				audio_mvs_info.rpc_prog = MVS_PROG;
+				audio_mvs_info.rpc_ver = MVS_VERS;
+			}
+		} else {
+			pr_debug("%s: MVS RPC connect succeeded ver 0x%x\n",
+				__func__, MVS_VERS_COMP_VER4);
+			audio_mvs_info.rpc_prog = MVS_PROG;
+			audio_mvs_info.rpc_ver = MVS_VERS_COMP_VER4;
+		}
+	} else {
+		pr_debug("%s: MVS RPC connect succeeded ver 0x%x\n", __func__,
+			MVS_VERS_COMP_VER5);
+		audio_mvs_info.rpc_prog = MVS_PROG;
+		audio_mvs_info.rpc_ver = MVS_VERS_COMP_VER5;
+	}
+	audio_mvs_info.task = kthread_run(audio_mvs_thread,
+					  &audio_mvs_info,
+					  "audio_mvs");
+	if (IS_ERR(audio_mvs_info.task)) {
+		pr_err("%s: MVS thread create failed\n",  __func__);
+		rc = PTR_ERR(audio_mvs_info.task);
+		audio_mvs_info.task = NULL;
+		msm_rpc_close(audio_mvs_info.rpc_endpt);
+		audio_mvs_info.rpc_endpt = NULL;
+		goto done;
+	}
+
+	rc = misc_register(&audio_mvs_misc);
+done:
+	return rc;
+}
+
+static void __exit audio_mvs_exit(void)
+{
+	pr_info("%s:\n", __func__);
+
+	misc_deregister(&audio_mvs_misc);
+}
+
+module_init(audio_mvs_init);
+module_exit(audio_mvs_exit);
+
+MODULE_DESCRIPTION("MSM MVS driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_out.c b/arch/arm/mach-msm/qdsp5v2/audio_out.c
new file mode 100644
index 0000000..7c1e5ca
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_out.c
@@ -0,0 +1,722 @@
+/*
+ * pcm 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/wakelock.h>
+
+#include <linux/msm_audio.h>
+#include <linux/android_pmem.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+#include <mach/qdsp5v2/qdsp5audppcmdi.h>
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/qdsp5v2/audpp.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+
+#include <mach/htc_pwrsink.h>
+#include <mach/debug_mm.h>
+
+#define BUFSZ (960 * 5)
+#define DMASZ (BUFSZ * 2)
+
+#define HOSTPCM_STREAM_ID 5
+
+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;
+	uint32_t device_events;
+	int16_t source;
+
+	/* 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 */
+	uint16_t dec_id;
+	int voice_state;
+
+	struct wake_lock wakelock;
+	struct wake_lock idlelock;
+
+	struct audpp_cmd_cfg_object_params_volume vol_pan;
+};
+
+static void audio_out_listener(u32 evt_id, union auddev_evt_data *evt_payload,
+				void *private_data)
+{
+	struct audio *audio = private_data;
+	switch (evt_id) {
+	case AUDDEV_EVT_DEV_RDY:
+		MM_DBG(":AUDDEV_EVT_DEV_RDY\n");
+		audio->source |= (0x1 << evt_payload->routing_id);
+		if (audio->running == 1 && audio->enabled == 1)
+			audpp_route_stream(audio->dec_id, audio->source);
+		break;
+	case AUDDEV_EVT_DEV_RLS:
+		MM_DBG(":AUDDEV_EVT_DEV_RLS\n");
+		audio->source &= ~(0x1 << evt_payload->routing_id);
+		if (audio->running == 1 && audio->enabled == 1)
+			audpp_route_stream(audio->dec_id, audio->source);
+		break;
+	case AUDDEV_EVT_STREAM_VOL_CHG:
+		audio->vol_pan.volume = evt_payload->session_vol;
+		MM_DBG(":AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n",
+				audio->vol_pan.volume);
+		if (audio->running)
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+					POPP);
+		break;
+	case AUDDEV_EVT_VOICE_STATE_CHG:
+		MM_DBG("AUDDEV_EVT_VOICE_STATE_CHG, state = %d\n",
+						evt_payload->voice_state);
+		audio->voice_state = evt_payload->voice_state;
+		/* Voice uplink Rx case */
+		if (audio->running &&
+			(audio->source & AUDPP_MIXER_UPLINK_RX) &&
+			(audio->voice_state == VOICE_STATE_OFFCALL)) {
+			MM_DBG("Voice is terminated, Wake up write: %x %x\n",
+					audio->voice_state, audio->source);
+			wake_up(&audio->wait);
+		}
+		break;
+	default:
+		MM_ERR("ERROR:wrong event\n");
+		break;
+       }
+}
+
+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)
+{
+	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;
+
+	audio_prevent_sleep(audio);
+
+	if (audpp_enable(-1, audio_dsp_event, audio)) {
+		MM_ERR("audpp_enable() failed\n");
+		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);
+		audio->out_needed = 0;
+		audio_allow_sleep(audio);
+	}
+	return 0;
+}
+
+/* ------------------- dsp --------------------- */
+static void audio_dsp_event(void *private, unsigned id, uint16_t *msg)
+{
+	struct audio *audio = private;
+	struct buffer *frame;
+	unsigned long flags;
+	static unsigned long pcmdmamsd_time;
+
+	switch (id) {
+	case AUDPP_MSG_HOST_PCM_INTF_MSG: {
+		unsigned id = msg[3];
+		unsigned idx = msg[4] - 1;
+
+		MM_DBG("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) {
+				/* Reset teos flag to avoid stale
+				 * PCMDMAMISS been considered
+				 */
+				audio->teos = 0;
+				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:
+		/* prints only if 1 second is elapsed since the last time
+		 * this message has been printed */
+		if (printk_timed_ratelimit(&pcmdmamsd_time, 1000))
+			printk(KERN_INFO "[%s:%s] PCMDMAMISSED %d\n",
+					__MM_FILE__, __func__, msg[0]);
+		audio->teos++;
+		MM_DBG("PCMDMAMISSED Count per Buffer %d\n", audio->teos);
+		wake_up(&audio->wait);
+		break;
+	case AUDPP_MSG_CFG_MSG:
+		if (msg[0] == AUDPP_MSG_ENA_ENA) {
+			MM_DBG("CFG_MSG ENABLE\n");
+			audio->out_needed = 0;
+			audio->running = 1;
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+					POPP);
+			audpp_route_stream(audio->dec_id, audio->source);
+			audio_dsp_out_enable(audio, 1);
+		} 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;
+	default:
+		MM_ERR("UNKNOWN (%d)\n", id);
+	}
+}
+
+static int audio_dsp_out_enable(struct audio *audio, int yes)
+{
+	struct audpp_cmd_pcm_intf cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id	= AUDPP_CMD_PCM_INTF;
+	cmd.stream	= AUDPP_CMD_POPP_STREAM;
+	cmd.stream_id	= audio->dec_id;
+	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)
+{
+	struct audpp_cmd_pcm_intf_send_buffer cmd;
+
+	cmd.cmd_id	= AUDPP_CMD_PCM_INTF;
+	cmd.stream	= AUDPP_CMD_POPP_STREAM;
+	cmd.stream_id	= audio->dec_id;
+	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;
+
+	return audpp_send_queue2(&cmd, sizeof(cmd));
+}
+
+/* ------------------- 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->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(audio->dec_id, &audio->vol_pan,
+					POPP);
+		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(audio->dec_id, &audio->vol_pan,
+					POPP);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		return 0;
+	}
+
+	mutex_lock(&audio->lock);
+	switch (cmd) {
+	case AUDIO_START:
+		if ((audio->voice_state != VOICE_STATE_INCALL)
+			&& (audio->source & AUDPP_MIXER_UPLINK_RX)) {
+			MM_ERR("Unable to Start : state %d source %d\n",
+					audio->voice_state, audio->source);
+			rc = -EPERM;
+			break;
+		}
+		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;
+	}
+	case AUDIO_GET_SESSION_ID: {
+		if (copy_to_user((void *) arg, &audio->dec_id,
+					sizeof(unsigned short)))
+			return -EFAULT;
+		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);
+
+	/* PCM DMAMISS message is sent only once in
+	 * hpcm interface. So, wait for buffer complete
+	 * and teos flag.
+	 */
+	rc = wait_event_interruptible(audio->wait,
+		(!audio->out[0].used &&
+		!audio->out[1].used));
+
+	if (rc < 0)
+		goto done;
+
+	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;
+
+
+	if ((audio->voice_state == VOICE_STATE_OFFCALL)
+		&& (audio->source & AUDPP_MIXER_UPLINK_RX) &&
+							audio->running) {
+		MM_ERR("Not Permitted Voice Terminated: state %d source %x \
+			running %d\n",
+			audio->voice_state, audio->source, audio->running);
+		return -EPERM;
+	}
+	/* 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;
+
+		rc = wait_event_interruptible(audio->wait,
+		      (frame->used == 0) || (audio->stopped) ||
+			((audio->voice_state == VOICE_STATE_OFFCALL) &&
+			(audio->source & AUDPP_MIXER_UPLINK_RX)));
+
+		if (rc < 0)
+			break;
+		if (audio->stopped) {
+			rc = -EBUSY;
+			break;
+		} else if ((audio->voice_state == VOICE_STATE_OFFCALL) &&
+			(audio->source & AUDPP_MIXER_UPLINK_RX)) {
+			MM_ERR("Not Permitted Voice Terminated: %d\n",
+							audio->voice_state);
+			rc = -EPERM;
+			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);
+		frame = audio->out + audio->out_tail;
+		if (frame->used && audio->out_needed) {
+			/* Reset teos flag to avoid stale
+			 * PCMDMAMISS been considered
+			 */
+			audio->teos = 0;
+			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);
+		}
+	}
+
+	if (buf > start)
+		return buf - start;
+	return rc;
+}
+
+static int audio_release(struct inode *inode, struct file *file)
+{
+	struct audio *audio = file->private_data;
+
+	mutex_lock(&audio->lock);
+	auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id);
+	audio_disable(audio);
+	audio_flush(audio);
+	audio->opened = 0;
+	mutex_unlock(&audio->lock);
+	htc_pwrsink_set(PWRSINK_AUDIO, 0);
+	return 0;
+}
+
+static 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;
+	}
+
+
+	audio->dec_id = HOSTPCM_STREAM_ID;
+
+	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->source = 0x0;
+
+	audio_flush(audio);
+	audio->voice_state = msm_get_voice_state();
+	MM_DBG("voice_state = %x\n", audio->voice_state);
+	audio->device_events = AUDDEV_EVT_DEV_RDY
+				|AUDDEV_EVT_DEV_RLS|
+				AUDDEV_EVT_STREAM_VOL_CHG|
+				AUDDEV_EVT_VOICE_STATE_CHG;
+
+	MM_DBG("register for event callback pdata %p\n", audio);
+	rc = auddev_register_evt_listner(audio->device_events,
+					AUDDEV_CLNT_DEC,
+					audio->dec_id,
+					audio_out_listener,
+					(void *)audio);
+	if (rc) {
+		MM_ERR("%s: failed to register listener\n", __func__);
+		goto done;
+	}
+
+	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_open,
+	.release	= audio_release,
+	.read		= audio_read,
+	.write		= audio_write,
+	.unlocked_ioctl	= audio_ioctl,
+	.fsync		= audio_fsync,
+};
+
+struct miscdevice audio_misc = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "msm_pcm_out",
+	.fops	= &audio_fops,
+};
+
+static int __init audio_init(void)
+{
+	the_audio.phys = pmem_kalloc(DMASZ, PMEM_MEMTYPE_EBI1|
+					PMEM_ALIGNMENT_4K);
+	if (!IS_ERR((void *)the_audio.phys)) {
+		the_audio.data = ioremap(the_audio.phys, DMASZ);
+		if (!the_audio.data) {
+			MM_ERR("could not map pmem buffers\n");
+			pmem_kfree(the_audio.phys);
+			return -ENOMEM;
+		}
+	} else {
+			MM_ERR("could not allocate pmem buffers\n");
+			return -ENOMEM;
+	}
+	MM_DBG("Memory addr = 0x%8x  phy addr = 0x%8x\n",\
+		(int) the_audio.data, (int) the_audio.phys);
+	mutex_init(&the_audio.lock);
+	mutex_init(&the_audio.write_lock);
+	spin_lock_init(&the_audio.dsp_lock);
+	init_waitqueue_head(&the_audio.wait);
+	wake_lock_init(&the_audio.wakelock, WAKE_LOCK_SUSPEND, "audio_pcm");
+	wake_lock_init(&the_audio.idlelock, WAKE_LOCK_IDLE, "audio_pcm_idle");
+	return misc_register(&audio_misc);
+}
+
+late_initcall(audio_init);
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_pcm.c b/arch/arm/mach-msm/qdsp5v2/audio_pcm.c
new file mode 100644
index 0000000..3115d52
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_pcm.c
@@ -0,0 +1,1697 @@
+/* arch/arm/mach-msm/qdsp5v2/audio_pcm.c
+ *
+ *
+ * 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 <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+
+#include <linux/msm_audio.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+
+#include <mach/qdsp5v2/qdsp5audppcmdi.h>
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/qdsp5audplaycmdi.h>
+#include <mach/qdsp5v2/qdsp5audplaymsg.h>
+#include <mach/qdsp5v2/audpp.h>
+#include <mach/debug_mm.h>
+#include <linux/slab.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 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;
+	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 */
+
+	/* 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 */
+	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;
+	uint32_t device_events;
+
+	unsigned volume;
+
+	uint16_t dec_id;
+	int16_t source;
+
+#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;
+	/* AV sync Info */
+	int avsync_flag;              /* Flag to indicate feedback from DSP */
+	wait_queue_head_t avsync_wait;/* Wait queue for AV Sync Message     */
+	/* flags, 48 bits sample/bytes counter per channel */
+	uint16_t avsync[AUDPP_AVSYNC_CH_COUNT * AUDPP_AVSYNC_NUM_WORDS + 1];
+
+	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 void pcm_listner(u32 evt_id, union auddev_evt_data *evt_payload,
+			void *private_data)
+{
+	struct audio *audio = (struct audio *) private_data;
+	switch (evt_id) {
+	case AUDDEV_EVT_DEV_RDY:
+		MM_DBG("AUDDEV_EVT_DEV_RDY\n");
+		audio->source |= (0x1 << evt_payload->routing_id);
+		if (audio->running == 1 && audio->enabled == 1)
+			audpp_route_stream(audio->dec_id, audio->source);
+		break;
+	case AUDDEV_EVT_DEV_RLS:
+		MM_DBG("AUDDEV_EVT_DEV_RLS\n");
+		audio->source &= ~(0x1 << evt_payload->routing_id);
+		if (audio->running == 1 && audio->enabled == 1)
+			audpp_route_stream(audio->dec_id, audio->source);
+		break;
+	case AUDDEV_EVT_STREAM_VOL_CHG:
+		audio->volume = evt_payload->session_vol;
+		MM_DBG("AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n",
+				audio->volume);
+		if (audio->running)
+			audpp_set_volume_and_pan(audio->dec_id, audio->volume,
+					0, POPP);
+		break;
+	default:
+		MM_ERR("ERROR:wrong event\n");
+		break;
+	}
+}
+/* must be called with audio->lock held */
+static int audio_enable(struct audio *audio)
+{
+	MM_DBG("\n"); /* Macro prints the file name and function */
+
+	if (audio->enabled)
+		return 0;
+
+	audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+	audio->out_tail = 0;
+	audio->out_needed = 0;
+
+	if (msm_adsp_enable(audio->audplay)) {
+		MM_ERR("msm_adsp_enable(audplay) failed\n");
+		return -ENODEV;
+	}
+
+	if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) {
+		MM_ERR("audpp_enable() failed\n");
+		msm_adsp_disable(audio->audplay);
+		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);
+		audio->out_needed = 0;
+	}
+	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 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");
+				audpp_route_stream(audio->dec_id,
+						audio->source);
+				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, POPP);
+		} else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+			MM_DBG("CFG_MSG DISABLE\n");
+			audio->running = 0;
+		} else {
+			MM_ERR("audio_dsp_event: 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;
+
+	case AUDPP_MSG_AVSYNC_MSG:
+		pr_info("%s: AVSYNC_MSG\n", __func__);
+		memcpy(&audio->avsync[0], msg, sizeof(audio->avsync));
+		audio->avsync_flag = 1;
+		wake_up(&audio->avsync_wait);
+		break;
+
+	default:
+		MM_DBG("audio_dsp_event: 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)
+{
+	struct audpp_cmd_cfg_dec_type cfg_dec_cmd;
+
+	memset(&cfg_dec_cmd, 0, sizeof(cfg_dec_cmd));
+
+	cfg_dec_cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+	if (enable)
+		cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+				AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_PCM;
+	else
+		cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+				AUDPP_CMD_DIS_DEC_V;
+	cfg_dec_cmd.dm_mode = 0x0;
+	cfg_dec_cmd.stream_id = audio->dec_id;
+	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_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 >> 1;
+	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)
+{
+	struct 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;
+		struct 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);
+	}
+	audio->avsync_flag = 1;
+	wake_up(&audio->avsync_wait);
+}
+
+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;
+	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;
+	}
+
+	rc = audpcm_pmem_check(audio, info->vaddr, len);
+	if (rc < 0) {
+		put_pmem_file(file);
+		kfree(region);
+		return rc;
+	}
+
+	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);
+	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 == 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 int audio_get_avsync_data(struct audio *audio,
+						struct msm_audio_stats *stats)
+{
+	int rc = -EINVAL;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	if (audio->dec_id == audio->avsync[0] && audio->avsync_flag) {
+		/* av_sync sample count */
+		stats->sample_count = (audio->avsync[2] << 16) |
+						(audio->avsync[3]);
+
+		/* av_sync byte_count */
+		stats->byte_count = (audio->avsync[5] << 16) |
+						(audio->avsync[6]);
+
+		audio->avsync_flag = 0;
+		rc = 0;
+	}
+	local_irq_restore(flags);
+	return rc;
+
+}
+
+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;
+
+		audio->avsync_flag = 0;
+		memset(&stats, 0, sizeof(stats));
+		if (audpp_query_avsync(audio->dec_id) < 0)
+			return rc;
+
+		rc = wait_event_interruptible_timeout(audio->avsync_wait,
+				(audio->avsync_flag == 1),
+				msecs_to_jiffies(AUDPP_AVSYNC_EVENT_TIMEOUT));
+
+		if (rc < 0)
+			return rc;
+		else if ((rc > 0) || ((rc == 0) && (audio->avsync_flag == 1))) {
+			if (audio_get_avsync_data(audio, &stats) < 0)
+				return rc;
+
+			if (copy_to_user((void *)arg, &stats, sizeof(stats)))
+				return -EFAULT;
+			return 0;
+		} else
+			return -EAGAIN;
+	}
+	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,
+					POPP);
+		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;
+
+	case AUDIO_GET_SESSION_ID:
+		if (copy_to_user((void *) arg, &audio->dec_id,
+					sizeof(unsigned short)))
+			return -EFAULT;
+		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_INFO("audio instance 0x%08x freeing\n", (int)audio);
+
+	mutex_lock(&audio->lock);
+	auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id);
+	audio_disable(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);
+	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);
+			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_INFO("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, freeing instance 0x%08x\n",
+				(int)audio);
+		rc = -ENODEV;
+		kfree(audio);
+		goto done;
+	}
+	audio->dec_id = decid & MSM_AUD_DECODER_MASK;
+
+	/* AIO interface */
+	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");
+		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->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;
+	}
+
+	rc = msm_adsp_get(audio->module_name, &audio->audplay,
+			&audpcmdec_adsp_ops, audio);
+	if (rc) {
+		MM_ERR("failed to get %s module, freeing instance 0x%08x\n",
+				audio->module_name, (int)audio);
+		goto err;
+	}
+
+	/* 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);
+	init_waitqueue_head(&audio->avsync_wait);
+
+	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 = 0x7FFF;
+	audio->drv_ops.out_flush(audio);
+
+	file->private_data = audio;
+	audio->opened = 1;
+
+	audio->device_events = AUDDEV_EVT_DEV_RDY
+				|AUDDEV_EVT_DEV_RLS|
+				AUDDEV_EVT_STREAM_VOL_CHG;
+
+	rc = auddev_register_evt_listner(audio->device_events,
+					AUDDEV_CLNT_DEC,
+					audio->dec_id,
+					pcm_listner,
+					(void *)audio);
+	if (rc) {
+		MM_ERR("failed to register listnet\n");
+		goto event_err;
+	}
+
+#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_ERR("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;
+event_err:
+	msm_adsp_put(audio->audplay);
+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_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/qdsp5v2/audio_pcm_in.c b/arch/arm/mach-msm/qdsp5v2/audio_pcm_in.c
new file mode 100644
index 0000000..71a07e5
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_pcm_in.c
@@ -0,0 +1,940 @@
+/*
+ * pcm audio input 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/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/msm_audio.h>
+#include <linux/android_pmem.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+
+#include <mach/msm_adsp.h>
+#include <mach/qdsp5v2/qdsp5audreccmdi.h>
+#include <mach/qdsp5v2/qdsp5audrecmsg.h>
+#include <mach/qdsp5v2/audpreproc.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/debug_mm.h>
+#include <mach/qdsp5v2/audio_acdbi.h>
+
+/* FRAME_NUM must be a power of two */
+#define FRAME_NUM		(8)
+#define FRAME_HEADER_SIZE       (8) /*4 half words*/
+/* size of a mono frame with 256 samples */
+#define MONO_DATA_SIZE_256	(512) /* in bytes*/
+/*size of a mono frame with 512 samples */
+#define MONO_DATA_SIZE_512	(1024) /* in bytes*/
+/*size of a mono frame with 1024 samples */
+#define MONO_DATA_SIZE_1024	(2048) /* in bytes */
+
+/*size of a stereo frame with 256 samples per channel */
+#define STEREO_DATA_SIZE_256	(1024) /* in bytes*/
+/*size of a stereo frame with 512 samples per channel */
+#define STEREO_DATA_SIZE_512	(2048) /* in bytes*/
+/*size of a stereo frame with 1024 samples per channel */
+#define STEREO_DATA_SIZE_1024	(4096) /* in bytes */
+
+#define MAX_FRAME_SIZE		((STEREO_DATA_SIZE_1024) + FRAME_HEADER_SIZE)
+#define DMASZ			(MAX_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;
+	atomic_t in_samples;
+
+	struct mutex lock;
+	struct mutex read_lock;
+	wait_queue_head_t wait;
+	wait_queue_head_t wait_enable;
+
+	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 enc_type;
+
+	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 mode;
+
+	const char *module_name;
+	unsigned queue_ids;
+	uint16_t enc_id; /* Session Id */
+
+	uint16_t source; /* Encoding source bit mask */
+	uint32_t device_events; /* device events interested in */
+	uint32_t in_call;
+	uint32_t dev_cnt;
+	int voice_state;
+	spinlock_t dev_lock;
+
+	struct audrec_session_info session_info; /*audrec session info*/
+	/* 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 */
+	int abort; /* set when error, like sample rate mismatch */
+	int dual_mic_config;
+};
+
+static struct audio_in the_audio_in;
+
+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[]; /* samples */
+} __attribute__((packed));
+
+/* Audrec Queue command sent macro's */
+#define audrec_send_bitstreamqueue(audio, cmd, len) \
+	msm_adsp_write(audio->audrec, ((audio->queue_ids & 0xFFFF0000) >> 16),\
+			cmd, len)
+
+#define audrec_send_audrecqueue(audio, cmd, len) \
+	msm_adsp_write(audio->audrec, (audio->queue_ids & 0x0000FFFF),\
+			cmd, len)
+
+/* DSP command send functions */
+static int audpcm_in_enc_config(struct audio_in *audio, int enable);
+static int audpcm_in_param_config(struct audio_in *audio);
+static int audpcm_in_mem_config(struct audio_in *audio);
+static int audpcm_in_record_config(struct audio_in *audio, int enable);
+static int audpcm_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt);
+
+static void audpcm_in_get_dsp_frames(struct audio_in *audio);
+
+static void audpcm_in_flush(struct audio_in *audio);
+
+static void pcm_in_listener(u32 evt_id, union auddev_evt_data *evt_payload,
+				void *private_data)
+{
+	struct audio_in *audio = (struct audio_in *) private_data;
+	unsigned long flags;
+
+	MM_DBG("evt_id = 0x%8x\n", evt_id);
+	switch (evt_id) {
+	case AUDDEV_EVT_DEV_RDY: {
+		MM_DBG("AUDDEV_EVT_DEV_RDY\n");
+		spin_lock_irqsave(&audio->dev_lock, flags);
+		audio->dev_cnt++;
+		if (!audio->in_call)
+			audio->source |= (0x1 << evt_payload->routing_id);
+		spin_unlock_irqrestore(&audio->dev_lock, flags);
+
+		if ((audio->running == 1) && (audio->enabled == 1))
+			audpcm_in_record_config(audio, 1);
+
+		break;
+	}
+	case AUDDEV_EVT_DEV_RLS: {
+		MM_DBG("AUDDEV_EVT_DEV_RLS\n");
+		spin_lock_irqsave(&audio->dev_lock, flags);
+		audio->dev_cnt--;
+		if (!audio->in_call)
+			audio->source &= ~(0x1 << evt_payload->routing_id);
+		spin_unlock_irqrestore(&audio->dev_lock, flags);
+
+		if (!audio->running || !audio->enabled)
+			break;
+
+		/* Turn of as per source */
+		if (audio->source)
+			audpcm_in_record_config(audio, 1);
+		else
+			/* Turn off all */
+			audpcm_in_record_config(audio, 0);
+
+		break;
+	}
+	case AUDDEV_EVT_VOICE_STATE_CHG: {
+		MM_DBG("AUDDEV_EVT_VOICE_STATE_CHG, state = %d\n",
+				evt_payload->voice_state);
+		audio->voice_state = evt_payload->voice_state;
+		if (audio->in_call && audio->running) {
+			if (audio->voice_state == VOICE_STATE_INCALL)
+				audpcm_in_record_config(audio, 1);
+			else if (audio->voice_state == VOICE_STATE_OFFCALL) {
+				audpcm_in_record_config(audio, 0);
+				wake_up(&audio->wait);
+			}
+		}
+		break;
+	}
+	case AUDDEV_EVT_FREQ_CHG: {
+		MM_DBG("Encoder Driver got sample rate change event\n");
+		MM_DBG("sample rate %d\n", evt_payload->freq_info.sample_rate);
+		MM_DBG("dev_type %d\n", evt_payload->freq_info.dev_type);
+		MM_DBG("acdb_dev_id %d\n", evt_payload->freq_info.acdb_dev_id);
+		if (audio->running == 1) {
+			/* Stop Recording sample rate does not match
+			   with device sample rate */
+			if (evt_payload->freq_info.sample_rate !=
+				audio->samp_rate) {
+				audpcm_in_record_config(audio, 0);
+				audio->abort = 1;
+				wake_up(&audio->wait);
+			}
+		}
+		break;
+	}
+	default:
+		MM_ERR("wrong event %d\n", evt_id);
+		break;
+	}
+}
+
+/* ------------------- dsp preproc event handler--------------------- */
+static void audpreproc_dsp_event(void *data, unsigned id,  void *msg)
+{
+	struct audio_in *audio = data;
+
+	switch (id) {
+	case AUDPREPROC_ERROR_MSG: {
+		struct audpreproc_err_msg *err_msg = msg;
+
+		MM_ERR("ERROR_MSG: stream id %d err idx %d\n",
+		err_msg->stream_id, err_msg->aud_preproc_err_idx);
+		/* Error case */
+		wake_up(&audio->wait_enable);
+		break;
+	}
+	case AUDPREPROC_CMD_CFG_DONE_MSG: {
+		MM_DBG("CMD_CFG_DONE_MSG \n");
+		break;
+	}
+	case AUDPREPROC_CMD_ENC_CFG_DONE_MSG: {
+		struct audpreproc_cmd_enc_cfg_done_msg *enc_cfg_msg = msg;
+
+		MM_DBG("CMD_ENC_CFG_DONE_MSG: stream id %d enc type \
+			0x%8x\n", enc_cfg_msg->stream_id,
+			enc_cfg_msg->rec_enc_type);
+		/* Encoder enable success */
+		if (enc_cfg_msg->rec_enc_type & ENCODE_ENABLE)
+			audpcm_in_param_config(audio);
+		else { /* Encoder disable success */
+			audio->running = 0;
+			audpcm_in_record_config(audio, 0);
+		}
+		break;
+	}
+	case AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG: {
+		MM_DBG("CMD_ENC_PARAM_CFG_DONE_MSG \n");
+		audpcm_in_mem_config(audio);
+		break;
+	}
+	case AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG: {
+		MM_DBG("AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG \n");
+		wake_up(&audio->wait_enable);
+		break;
+	}
+	default:
+		MM_ERR("Unknown Event id %d\n", id);
+	}
+}
+
+/* ------------------- dsp audrec event handler--------------------- */
+static void audrec_dsp_event(void *data, unsigned id, size_t len,
+			    void (*getevent)(void *ptr, size_t len))
+{
+	struct audio_in *audio = data;
+
+	switch (id) {
+	case AUDREC_CMD_MEM_CFG_DONE_MSG: {
+		MM_DBG("CMD_MEM_CFG_DONE MSG DONE\n");
+		audio->running = 1;
+		if ((!audio->in_call && (audio->dev_cnt > 0)) ||
+			(audio->in_call &&
+				(audio->voice_state == VOICE_STATE_INCALL)))
+			audpcm_in_record_config(audio, 1);
+		break;
+	}
+	case AUDREC_FATAL_ERR_MSG: {
+		struct audrec_fatal_err_msg fatal_err_msg;
+
+		getevent(&fatal_err_msg, AUDREC_FATAL_ERR_MSG_LEN);
+		MM_ERR("FATAL_ERR_MSG: err id %d\n",
+				fatal_err_msg.audrec_err_id);
+		/* Error stop the encoder */
+		audio->stopped = 1;
+		wake_up(&audio->wait);
+		break;
+	}
+	case AUDREC_UP_PACKET_READY_MSG: {
+		struct audrec_up_pkt_ready_msg pkt_ready_msg;
+
+		getevent(&pkt_ready_msg, AUDREC_UP_PACKET_READY_MSG_LEN);
+		MM_DBG("UP_PACKET_READY_MSG: write cnt lsw  %d \
+		write cnt msw %d read cnt lsw %d  read cnt msw %d \n",\
+		pkt_ready_msg.audrec_packet_write_cnt_lsw, \
+		pkt_ready_msg.audrec_packet_write_cnt_msw, \
+		pkt_ready_msg.audrec_up_prev_read_cnt_lsw, \
+		pkt_ready_msg.audrec_up_prev_read_cnt_msw);
+
+		audpcm_in_get_dsp_frames(audio);
+		break;
+	}
+	case ADSP_MESSAGE_ID: {
+		MM_DBG("Received ADSP event :module audrectask\n");
+		break;
+	}
+	default:
+		MM_ERR("Unknown Event id %d\n", id);
+	}
+}
+
+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->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)
+		audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1);
+	else
+		audio->in_count++;
+
+	audpcm_dsp_read_buffer(audio, audio->dsp_cnt++);
+	spin_unlock_irqrestore(&audio->dsp_lock, flags);
+
+	wake_up(&audio->wait);
+}
+
+struct msm_adsp_ops audrec_adsp_ops = {
+	.event = audrec_dsp_event,
+};
+
+static int audpcm_in_enc_config(struct audio_in *audio, int enable)
+{
+	struct audpreproc_audrec_cmd_enc_cfg cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ENC_CFG_2;
+	cmd.stream_id = audio->enc_id;
+
+	if (enable)
+		cmd.audrec_enc_type = audio->enc_type | ENCODE_ENABLE;
+	else
+		cmd.audrec_enc_type &= ~(ENCODE_ENABLE);
+
+	return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+static int audpcm_in_param_config(struct audio_in *audio)
+{
+	struct audpreproc_audrec_cmd_parm_cfg_wav cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.common.cmd_id = AUDPREPROC_AUDREC_CMD_PARAM_CFG;
+	cmd.common.stream_id = audio->enc_id;
+
+	cmd.aud_rec_samplerate_idx = audio->samp_rate;
+	if (audio->dual_mic_config)
+		cmd.aud_rec_stereo_mode = DUAL_MIC_STEREO_RECORDING;
+	else
+		cmd.aud_rec_stereo_mode = audio->channel_mode;
+
+	if (audio->channel_mode == AUDREC_CMD_MODE_MONO)
+		cmd.aud_rec_frame_size = audio->buffer_size/2;
+	else
+		cmd.aud_rec_frame_size = audio->buffer_size/4;
+	return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+/* To Do: msm_snddev_route_enc(audio->enc_id); */
+static int audpcm_in_record_config(struct audio_in *audio, int enable)
+{
+	struct audpreproc_afe_cmd_audio_record_cfg cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG;
+	cmd.stream_id = audio->enc_id;
+	if (enable)
+		cmd.destination_activity = AUDIO_RECORDING_TURN_ON;
+	else
+		cmd.destination_activity = AUDIO_RECORDING_TURN_OFF;
+
+	cmd.source_mix_mask = audio->source;
+	if (audio->enc_id == 2) {
+		if ((cmd.source_mix_mask &
+				INTERNAL_CODEC_TX_SOURCE_MIX_MASK) ||
+			(cmd.source_mix_mask & AUX_CODEC_TX_SOURCE_MIX_MASK) ||
+			(cmd.source_mix_mask & VOICE_UL_SOURCE_MIX_MASK) ||
+			(cmd.source_mix_mask & VOICE_DL_SOURCE_MIX_MASK)) {
+			cmd.pipe_id = SOURCE_PIPE_1;
+		}
+		if (cmd.source_mix_mask &
+				AUDPP_A2DP_PIPE_SOURCE_MIX_MASK)
+			cmd.pipe_id |= SOURCE_PIPE_0;
+	}
+
+	return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+static int audpcm_in_mem_config(struct audio_in *audio)
+{
+	struct audrec_cmd_arecmem_cfg cmd;
+	uint16_t *data = (void *) audio->data;
+	int n;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDREC_CMD_MEM_CFG_CMD;
+	cmd.audrec_up_pkt_intm_count = 1;
+	cmd.audrec_ext_pkt_start_addr_msw = audio->phys >> 16;
+	cmd.audrec_ext_pkt_start_addr_lsw = audio->phys;
+	cmd.audrec_ext_pkt_buf_number = FRAME_NUM;
+
+	/* prepare buffer pointers:
+	 * Mono: 1024 samples + 4 halfword header
+	 * Stereo: 2048 samples + 4 halfword header
+	 */
+	for (n = 0; n < FRAME_NUM; n++) {
+		/* word increment*/
+		audio->in[n].data = data + (FRAME_HEADER_SIZE/2);
+		data += ((FRAME_HEADER_SIZE/2) + (audio->buffer_size/2));
+		MM_DBG("0x%8x\n", (int)(audio->in[n].data - FRAME_HEADER_SIZE));
+	}
+
+	return audrec_send_audrecqueue(audio, &cmd, sizeof(cmd));
+}
+
+static int audpcm_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt)
+{
+	struct up_audrec_packet_ext_ptr cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = UP_AUDREC_PACKET_EXT_PTR;
+	cmd.audrec_up_curr_read_count_msw = read_cnt >> 16;
+	cmd.audrec_up_curr_read_count_lsw = read_cnt;
+
+	return audrec_send_bitstreamqueue(audio, &cmd, sizeof(cmd));
+}
+
+/* must be called with audio->lock held */
+static int audpcm_in_enable(struct audio_in *audio)
+{
+	if (audio->enabled)
+		return 0;
+
+	if (audpreproc_enable(audio->enc_id, &audpreproc_dsp_event, audio)) {
+		MM_ERR("msm_adsp_enable(audpreproc) failed\n");
+		return -ENODEV;
+	}
+
+	if (msm_adsp_enable(audio->audrec)) {
+		MM_ERR("msm_adsp_enable(audrec) failed\n");
+		audpreproc_disable(audio->enc_id, audio);
+		return -ENODEV;
+	}
+	audio->enabled = 1;
+	audpcm_in_enc_config(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_enc_config(audio, 0);
+		wake_up(&audio->wait);
+		wait_event_interruptible_timeout(audio->wait_enable,
+				audio->running == 0, 1*HZ);
+		msm_adsp_disable(audio->audrec);
+		audpreproc_disable(audio->enc_id, audio);
+	}
+	return 0;
+}
+
+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 = 0; i < FRAME_NUM; 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);
+}
+
+/* ------------------- device --------------------- */
+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);
+		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: {
+		uint32_t freq;
+		/* Poll at 48KHz always */
+		freq = 48000;
+		MM_DBG("AUDIO_START\n");
+		if (audio->in_call && (audio->voice_state !=
+				VOICE_STATE_INCALL)) {
+			rc = -EPERM;
+			break;
+		}
+		rc = msm_snddev_request_freq(&freq, audio->enc_id,
+					SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+		MM_DBG("sample rate configured %d sample rate requested %d\n",
+				freq, audio->samp_rate);
+		if (rc < 0) {
+			MM_DBG("sample rate can not be set, return code %d\n",\
+							rc);
+			msm_snddev_withdraw_freq(audio->enc_id,
+						SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+			MM_DBG("msm_snddev_withdraw_freq\n");
+			break;
+		}
+		audio->dual_mic_config = msm_get_dual_mic_config(audio->enc_id);
+		/*DSP supports fluence block and by default ACDB layer will
+		applies the fluence pre-processing feature, if dual MIC config
+		is enabled implies client want to record pure dual MIC sample
+		for this we need to over ride the fluence pre processing
+		feature at ACDB layer to not to apply if fluence preprocessing
+		feature supported*/
+		if (audio->dual_mic_config) {
+			MM_INFO("dual MIC config = %d, over ride the fluence "
+			"feature\n", audio->dual_mic_config);
+			fluence_feature_update(audio->dual_mic_config,
+							audio->enc_id);
+		}
+		/*update aurec session info in audpreproc layer*/
+		audio->session_info.session_id = audio->enc_id;
+		audio->session_info.sampling_freq = audio->samp_rate;
+		audpreproc_update_audrec_info(&audio->session_info);
+		rc = audpcm_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: {
+		/*reset the sampling frequency information at audpreproc layer*/
+		audio->session_info.sampling_freq = 0;
+		audpreproc_update_audrec_info(&audio->session_info);
+		rc = audpcm_in_disable(audio);
+		rc = msm_snddev_withdraw_freq(audio->enc_id,
+					SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+		MM_DBG("msm_snddev_withdraw_freq\n");
+		audio->stopped = 1;
+		audio->abort = 0;
+		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_MODE_MONO;
+			if ((cfg.buffer_size == MONO_DATA_SIZE_256) ||
+			    (cfg.buffer_size == MONO_DATA_SIZE_512) ||
+			    (cfg.buffer_size == MONO_DATA_SIZE_1024)) {
+				audio->buffer_size = cfg.buffer_size;
+			} else {
+				rc = -EINVAL;
+				break;
+			}
+		} else if (cfg.channel_count == 2) {
+			cfg.channel_count = AUDREC_CMD_MODE_STEREO;
+			if ((cfg.buffer_size == STEREO_DATA_SIZE_256) ||
+			    (cfg.buffer_size == STEREO_DATA_SIZE_512) ||
+			    (cfg.buffer_size == STEREO_DATA_SIZE_1024)) {
+				audio->buffer_size = cfg.buffer_size;
+			} else {
+				rc = -EINVAL;
+				break;
+			}
+		} else {
+			rc = -EINVAL;
+			break;
+		}
+		audio->samp_rate = cfg.sample_rate;
+		audio->channel_mode = cfg.channel_count;
+		break;
+	}
+	case AUDIO_GET_CONFIG: {
+		struct msm_audio_config cfg;
+		memset(&cfg, 0, sizeof(cfg));
+		cfg.buffer_size = audio->buffer_size;
+		cfg.buffer_count = FRAME_NUM;
+		cfg.sample_rate = audio->samp_rate;
+		if (audio->channel_mode == AUDREC_CMD_MODE_MONO)
+			cfg.channel_count = 1;
+		else
+			cfg.channel_count = 2;
+		if (copy_to_user((void *) arg, &cfg, sizeof(cfg)))
+			rc = -EFAULT;
+		break;
+	}
+	case AUDIO_SET_INCALL: {
+		struct msm_voicerec_mode cfg;
+		unsigned long flags;
+		if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) {
+			rc = -EFAULT;
+			break;
+		}
+		if (cfg.rec_mode != VOC_REC_BOTH &&
+			cfg.rec_mode != VOC_REC_UPLINK &&
+			cfg.rec_mode != VOC_REC_DOWNLINK) {
+			MM_ERR("invalid rec_mode\n");
+			rc = -EINVAL;
+			break;
+		} else {
+			spin_lock_irqsave(&audio->dev_lock, flags);
+			if (cfg.rec_mode == VOC_REC_UPLINK)
+				audio->source = VOICE_UL_SOURCE_MIX_MASK;
+			else if (cfg.rec_mode == VOC_REC_DOWNLINK)
+				audio->source = VOICE_DL_SOURCE_MIX_MASK;
+			else
+				audio->source = VOICE_DL_SOURCE_MIX_MASK |
+						VOICE_UL_SOURCE_MIX_MASK ;
+			audio->in_call = 1;
+			spin_unlock_irqrestore(&audio->dev_lock, flags);
+		}
+		break;
+	}
+	case AUDIO_GET_SESSION_ID: {
+		if (copy_to_user((void *) arg, &audio->enc_id,
+			sizeof(unsigned short))) {
+			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 ||
+			audio->abort || (audio->in_call && audio->running &&
+				(audio->voice_state == VOICE_STATE_OFFCALL)));
+		if (rc < 0)
+			break;
+
+		if (!audio->in_count) {
+			if (audio->stopped) {
+				MM_DBG("Driver in stop state, No more \
+						buffer to read");
+				rc = 0;/* End of File */
+				break;
+			} else if (audio->in_call && audio->running &&
+				(audio->voice_state == VOICE_STATE_OFFCALL)) {
+				MM_DBG("Not Permitted Voice Terminated\n");
+				rc = -EPERM; /* Voice Call stopped */
+				break;
+			}
+		}
+
+		if (audio->abort) {
+			rc = -EPERM; /* Not permitted due to abort */
+			break;
+		}
+
+		index = audio->in_tail;
+		data = (uint8_t *) audio->in[index].data;
+		size = audio->in[index].size;
+		if (count >= size) {
+			if (copy_to_user(buf, data, size)) {
+				rc = -EFAULT;
+				break;
+			}
+			spin_lock_irqsave(&audio->dsp_lock, flags);
+			if (index != audio->in_tail) {
+				/* overrun -- data is
+				 * invalid and we need to retry */
+				spin_unlock_irqrestore(&audio->dsp_lock, flags);
+				continue;
+			}
+			audio->in[index].size = 0;
+			audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1);
+			audio->in_count--;
+			spin_unlock_irqrestore(&audio->dsp_lock, flags);
+			count -= size;
+			buf += size;
+		} else {
+			MM_ERR("short read count %d\n", count);
+			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);
+	audio->in_call = 0;
+	/* with draw frequency for session
+	   incase not stopped the driver */
+	msm_snddev_withdraw_freq(audio->enc_id, SNDDEV_CAP_TX,
+					AUDDEV_CLNT_ENC);
+	auddev_unregister_evt_listner(AUDDEV_CLNT_ENC, audio->enc_id);
+	/*reset the sampling frequency information at audpreproc layer*/
+	audio->session_info.sampling_freq = 0;
+	audpreproc_update_audrec_info(&audio->session_info);
+	audpcm_in_disable(audio);
+	audpcm_in_flush(audio);
+	msm_adsp_put(audio->audrec);
+	audpreproc_aenc_free(audio->enc_id);
+	audio->audrec = NULL;
+	audio->opened = 0;
+	if (audio->data) {
+		iounmap(audio->data);
+		pmem_kfree(audio->phys);
+		audio->data = NULL;
+	}
+	mutex_unlock(&audio->lock);
+	return 0;
+}
+
+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;
+	}
+	audio->phys = pmem_kalloc(DMASZ, PMEM_MEMTYPE_EBI1|
+					PMEM_ALIGNMENT_4K);
+	if (!IS_ERR((void *)audio->phys)) {
+		audio->data = ioremap(audio->phys, DMASZ);
+		if (!audio->data) {
+			MM_ERR("could not allocate read buffers\n");
+			rc = -ENOMEM;
+			pmem_kfree(audio->phys);
+			goto done;
+		}
+	} else {
+		MM_ERR("could not allocate read buffers\n");
+		rc = -ENOMEM;
+		goto done;
+	}
+	MM_DBG("Memory addr = 0x%8x  phy addr = 0x%8x\n",\
+		(int) audio->data, (int) audio->phys);
+	if ((file->f_mode & FMODE_WRITE) &&
+			(file->f_mode & FMODE_READ)) {
+		rc = -EACCES;
+		MM_ERR("Non tunnel encoding is not supported\n");
+		goto done;
+	} else if (!(file->f_mode & FMODE_WRITE) &&
+					(file->f_mode & FMODE_READ)) {
+		audio->mode = MSM_AUD_ENC_MODE_TUNNEL;
+		MM_DBG("Opened for tunnel mode encoding\n");
+	} else {
+		rc = -EACCES;
+		goto done;
+	}
+	/* Settings will be re-config at AUDIO_SET_CONFIG,
+	 * but at least we need to have initial config
+	 */
+	audio->channel_mode = AUDREC_CMD_MODE_MONO;
+	audio->buffer_size = MONO_DATA_SIZE_1024;
+	audio->samp_rate = 8000;
+	audio->enc_type = ENC_TYPE_EXT_WAV | audio->mode;
+
+	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;
+	}
+
+	audio->stopped = 0;
+	audio->source = 0;
+	audio->abort = 0;
+	audpcm_in_flush(audio);
+	audio->device_events = AUDDEV_EVT_DEV_RDY | AUDDEV_EVT_DEV_RLS |
+				AUDDEV_EVT_FREQ_CHG |
+				AUDDEV_EVT_VOICE_STATE_CHG;
+
+	audio->voice_state = msm_get_voice_state();
+	rc = auddev_register_evt_listner(audio->device_events,
+					AUDDEV_CLNT_ENC, audio->enc_id,
+					pcm_in_listener, (void *) audio);
+	if (rc) {
+		MM_ERR("failed to register device event listener\n");
+		goto evt_error;
+	}
+	file->private_data = audio;
+	audio->opened = 1;
+	rc = 0;
+done:
+	mutex_unlock(&audio->lock);
+	return rc;
+evt_error:
+	msm_adsp_put(audio->audrec);
+	audpreproc_aenc_free(audio->enc_id);
+	mutex_unlock(&audio->lock);
+	return rc;
+}
+
+static const struct file_operations audio_in_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,
+};
+
+struct miscdevice audio_in_misc = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "msm_pcm_in",
+	.fops	= &audio_in_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);
+	spin_lock_init(&the_audio_in.dev_lock);
+	init_waitqueue_head(&the_audio_in.wait);
+	init_waitqueue_head(&the_audio_in.wait_enable);
+	return misc_register(&audio_in_misc);
+}
+
+device_initcall(audpcm_in_init);
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_qcelp.c b/arch/arm/mach-msm/qdsp5v2/audio_qcelp.c
new file mode 100644
index 0000000..b57d72f
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_qcelp.c
@@ -0,0 +1,1625 @@
+/*
+ * qcelp 13k audio decoder device
+ *
+ * Copyright (c) 2008-2010, 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 <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+#include <linux/slab.h>
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/qdsp5audplaycmdi.h>
+#include <mach/qdsp5v2/qdsp5audplaymsg.h>
+#include <mach/qdsp5v2/audpp.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <linux/msm_audio.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/debug_mm.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;
+
+	/* 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 */
+
+	const char *module_name;
+	unsigned queue_id;
+	uint16_t dec_id;
+	int16_t source;
+
+#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;
+	/* AV sync Info */
+	int avsync_flag;              /* Flag to indicate feedback from DSP */
+	wait_queue_head_t avsync_wait;/* Wait queue for AV Sync Message     */
+	/* flags, 48 bits sample/bytes counter per channel */
+	uint16_t avsync[AUDPP_AVSYNC_CH_COUNT * AUDPP_AVSYNC_NUM_WORDS + 1];
+
+	uint32_t device_events;
+
+	int eq_enable;
+	int eq_needs_commit;
+	struct audpp_cmd_cfg_object_params_eqalizer eq;
+	struct 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);
+static void audqcelp_post_event(struct audio *audio, int type,
+		union msm_audio_event_payload payload);
+
+/* must be called with audio->lock held */
+static int audqcelp_enable(struct audio *audio)
+{
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	if (audio->enabled)
+		return 0;
+
+	audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+	audio->out_tail = 0;
+	audio->out_needed = 0;
+
+	if (msm_adsp_enable(audio->audplay)) {
+		MM_ERR("msm_adsp_enable(audplay) failed\n");
+		return -ENODEV;
+	}
+
+	if (audpp_enable(audio->dec_id, audqcelp_dsp_event, audio)) {
+		MM_ERR("audpp_enable() failed\n");
+		msm_adsp_disable(audio->audplay);
+		return -ENODEV;
+	}
+	audio->enabled = 1;
+	return 0;
+}
+
+static void qcelp_listner(u32 evt_id, union auddev_evt_data *evt_payload,
+			void *private_data)
+{
+	struct audio *audio = (struct audio *) private_data;
+	switch (evt_id) {
+	case AUDDEV_EVT_DEV_RDY:
+		MM_DBG(":AUDDEV_EVT_DEV_RDY\n");
+		audio->source |= (0x1 << evt_payload->routing_id);
+		if (audio->running == 1 && audio->enabled == 1)
+			audpp_route_stream(audio->dec_id, audio->source);
+		break;
+	case AUDDEV_EVT_DEV_RLS:
+		MM_DBG(":AUDDEV_EVT_DEV_RLS\n");
+		audio->source &= ~(0x1 << evt_payload->routing_id);
+		if (audio->running == 1 && audio->enabled == 1)
+			audpp_route_stream(audio->dec_id, audio->source);
+		break;
+	case AUDDEV_EVT_STREAM_VOL_CHG:
+		audio->vol_pan.volume = evt_payload->session_vol;
+		MM_DBG(":AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n",
+				audio->vol_pan.volume);
+		if (audio->running)
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+					POPP);
+		break;
+	default:
+		MM_ERR(":ERROR:wrong event\n");
+		break;
+	}
+}
+/* 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);
+		audio->out_needed = 0;
+	}
+	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");
+				/* send  mixer command */
+				audpp_route_stream(audio->dec_id,
+						audio->source);
+				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,
+					POPP);
+			audpp_dsp_set_eq(audio->dec_id,	audio->eq_enable,
+					&audio->eq, POPP);
+		} else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+			MM_DBG("CFG_MSG DISABLE\n");
+			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;
+
+	case AUDPP_MSG_AVSYNC_MSG:
+		MM_DBG("AUDPP_MSG_AVSYNC_MSG\n");
+		memcpy(&audio->avsync[0], msg, sizeof(audio->avsync));
+		audio->avsync_flag = 1;
+		wake_up(&audio->avsync_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)
+{
+	struct audpp_cmd_cfg_dec_type cfg_dec_cmd;
+
+	memset(&cfg_dec_cmd, 0, sizeof(cfg_dec_cmd));
+
+	cfg_dec_cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+	if (enable)
+		cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+				AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_QCELP;
+	else
+		cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+				AUDPP_CMD_DIS_DEC_V;
+	cfg_dec_cmd.dm_mode = 0x0;
+	cfg_dec_cmd.stream_id = audio->dec_id;
+
+	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;
+	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);
+	audio->avsync_flag = 1;
+	wake_up(&audio->avsync_wait);
+}
+
+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, POPP);
+		audio->eq_needs_commit = 0;
+	}
+	return 0;
+}
+
+static int audio_get_avsync_data(struct audio *audio,
+						struct msm_audio_stats *stats)
+{
+	int rc = -EINVAL;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	if (audio->dec_id == audio->avsync[0] && audio->avsync_flag) {
+		/* av_sync sample count */
+		stats->sample_count = (audio->avsync[2] << 16) |
+						(audio->avsync[3]);
+
+		/* av_sync byte_count */
+		stats->byte_count = (audio->avsync[5] << 16) |
+						(audio->avsync[6]);
+
+		audio->avsync_flag = 0;
+		rc = 0;
+	}
+	local_irq_restore(flags);
+	return rc;
+
+}
+
+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;
+
+		audio->avsync_flag = 0;
+		memset(&stats, 0, sizeof(stats));
+		if (audpp_query_avsync(audio->dec_id) < 0)
+			return rc;
+
+		rc = wait_event_interruptible_timeout(audio->avsync_wait,
+				(audio->avsync_flag == 1),
+				msecs_to_jiffies(AUDPP_AVSYNC_EVENT_TIMEOUT));
+
+		if (rc < 0)
+			return rc;
+		else if ((rc > 0) || ((rc == 0) && (audio->avsync_flag == 1))) {
+			if (audio_get_avsync_data(audio, &stats) < 0)
+				return rc;
+
+			if (copy_to_user((void *)arg, &stats, sizeof(stats)))
+				return -EFAULT;
+			return 0;
+		} else
+			return -EAGAIN;
+	}
+
+	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,
+					POPP);
+		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,
+					POPP);
+		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;
+	case AUDIO_GET_SESSION_ID:
+		if (copy_to_user((void *) arg, &audio->dec_id,
+				sizeof(unsigned short)))
+			rc = -EFAULT;
+		else
+			rc = 0;
+		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);
+
+			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);
+	auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id);
+	audqcelp_disable(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;
+}
+
+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);
+			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 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);
+	}
+
+	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);
+		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);
+	init_waitqueue_head(&audio->avsync_wait);
+
+	/* 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;
+
+	audio->device_events = AUDDEV_EVT_DEV_RDY
+				|AUDDEV_EVT_DEV_RLS|
+				AUDDEV_EVT_STREAM_VOL_CHG;
+
+	rc = auddev_register_evt_listner(audio->device_events,
+					AUDDEV_CLNT_DEC,
+					audio->dec_id,
+					qcelp_listner,
+					(void *)audio);
+	if (rc) {
+		MM_ERR("%s: failed to register listnet\n", __func__);
+		goto event_err;
+	}
+
+#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;
+event_err:
+	msm_adsp_put(audio->audplay);
+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/qdsp5v2/audio_qcelp_in.c b/arch/arm/mach-msm/qdsp5v2/audio_qcelp_in.c
new file mode 100644
index 0000000..47bd589
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_qcelp_in.c
@@ -0,0 +1,1497 @@
+/*
+ * qcelp audio input 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/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/msm_audio_qcp.h>
+#include <linux/android_pmem.h>
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+
+#include <mach/msm_adsp.h>
+#include <mach/qdsp5v2/qdsp5audreccmdi.h>
+#include <mach/qdsp5v2/qdsp5audrecmsg.h>
+#include <mach/qdsp5v2/audpreproc.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/debug_mm.h>
+
+#define META_OUT_SIZE	24
+/* FRAME_NUM must be a power of two */
+#define FRAME_NUM	8
+#define QCELP_FRAME_SIZE	36 /* 36 bytes data */
+#define FRAME_SIZE	(22 * 2) /* 36 bytes data */
+ /* 36 bytes data  + 24 meta field*/
+#define NT_FRAME_SIZE	(QCELP_FRAME_SIZE + META_OUT_SIZE)
+#define DMASZ		(NT_FRAME_SIZE * FRAME_NUM)
+#define OUT_FRAME_NUM	(2)
+#define OUT_BUFFER_SIZE (4 * 1024 + META_OUT_SIZE)
+#define BUFFER_SIZE	(OUT_BUFFER_SIZE * OUT_FRAME_NUM)
+
+
+#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_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 audrec_session_info session_info; /*audrec session info*/
+
+	/* configuration to use on next enable */
+	uint32_t buffer_size; /* Frame size (36 bytes) */
+	uint32_t samp_rate;
+	uint32_t channel_mode;
+	uint32_t enc_type;
+
+	struct msm_audio_qcelp_enc_config cfg;
+	uint32_t rec_mode;
+
+	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 mode;
+	uint32_t eos_ack;
+	uint32_t flush_ack;
+
+	const char *module_name;
+	unsigned queue_ids;
+	uint16_t enc_id;
+
+	uint16_t source; /* Encoding source bit mask */
+	uint32_t device_events;
+	uint32_t in_call;
+	uint32_t dev_cnt;
+	int voice_state;
+	spinlock_t dev_lock;
+
+	/* 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[]; /* samples */
+} __attribute__((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 */
+} __attribute__((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 audrec_send_bitstreamqueue(audio, cmd, len) \
+	msm_adsp_write(audio->audrec, ((audio->queue_ids & 0xFFFF0000) >> 16),\
+			cmd, len)
+
+#define audrec_send_audrecqueue(audio, cmd, len) \
+	msm_adsp_write(audio->audrec, (audio->queue_ids & 0x0000FFFF),\
+			cmd, len)
+
+/* DSP command send functions */
+static int audqcelp_in_enc_config(struct audio_in *audio, int enable);
+static int audqcelp_in_param_config(struct audio_in *audio);
+static int audqcelp_in_mem_config(struct audio_in *audio);
+static int audqcelp_in_record_config(struct audio_in *audio, int enable);
+static int audqcelp_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt);
+
+static void audqcelp_in_get_dsp_frames(struct audio_in *audio);
+static int audpcm_config(struct audio_in *audio);
+static void audqcelp_out_flush(struct audio_in *audio);
+static int audpreproc_cmd_cfg_routing_mode(struct audio_in *audio);
+static void audpreproc_pcm_send_data(struct audio_in *audio, unsigned needed);
+static void audqcelp_nt_in_get_dsp_frames(struct audio_in *audio);
+
+static void audqcelp_in_flush(struct audio_in *audio);
+
+static void qcelp_in_listener(u32 evt_id, union auddev_evt_data *evt_payload,
+				void *private_data)
+{
+	struct audio_in *audio = (struct audio_in *) private_data;
+	unsigned long flags;
+
+	MM_DBG("evt_id = 0x%8x\n", evt_id);
+	switch (evt_id) {
+	case AUDDEV_EVT_DEV_RDY: {
+		MM_DBG("AUDDEV_EVT_DEV_RDY\n");
+		spin_lock_irqsave(&audio->dev_lock, flags);
+		audio->dev_cnt++;
+		if (!audio->in_call)
+			audio->source |= (0x1 << evt_payload->routing_id);
+		spin_unlock_irqrestore(&audio->dev_lock, flags);
+
+		if ((audio->running == 1) && (audio->enabled == 1) &&
+			(audio->mode == MSM_AUD_ENC_MODE_TUNNEL))
+			audqcelp_in_record_config(audio, 1);
+	}
+		break;
+	case AUDDEV_EVT_DEV_RLS: {
+		MM_DBG("AUDDEV_EVT_DEV_RLS\n");
+		spin_lock_irqsave(&audio->dev_lock, flags);
+		audio->dev_cnt--;
+		if (!audio->in_call)
+			audio->source &= ~(0x1 << evt_payload->routing_id);
+		spin_unlock_irqrestore(&audio->dev_lock, flags);
+
+		if ((!audio->running) || (!audio->enabled))
+			break;
+
+		if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+			/* Turn of as per source */
+			if (audio->source)
+				audqcelp_in_record_config(audio, 1);
+			else
+				/* Turn off all */
+				audqcelp_in_record_config(audio, 0);
+		}
+	}
+		break;
+	case AUDDEV_EVT_VOICE_STATE_CHG: {
+		MM_DBG("AUDDEV_EVT_VOICE_STATE_CHG, state = %d\n",
+				evt_payload->voice_state);
+		audio->voice_state = evt_payload->voice_state;
+		if (audio->in_call && audio->running &&
+		   (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)) {
+			if (audio->voice_state == VOICE_STATE_INCALL)
+				audqcelp_in_record_config(audio, 1);
+			else if (audio->voice_state == VOICE_STATE_OFFCALL) {
+				audqcelp_in_record_config(audio, 0);
+				wake_up(&audio->wait);
+			}
+		}
+
+		break;
+	}
+	default:
+		MM_ERR("wrong event %d\n", evt_id);
+		break;
+	}
+}
+
+/* ------------------- dsp preproc event handler--------------------- */
+static void audpreproc_dsp_event(void *data, unsigned id,  void *msg)
+{
+	struct audio_in *audio = data;
+
+	switch (id) {
+	case AUDPREPROC_ERROR_MSG: {
+		struct audpreproc_err_msg *err_msg = msg;
+
+		MM_ERR("ERROR_MSG: stream id %d err idx %d\n",
+		err_msg->stream_id, err_msg->aud_preproc_err_idx);
+		/* Error case */
+		wake_up(&audio->wait_enable);
+		break;
+	}
+	case AUDPREPROC_CMD_CFG_DONE_MSG: {
+		MM_DBG("CMD_CFG_DONE_MSG \n");
+		break;
+	}
+	case AUDPREPROC_CMD_ENC_CFG_DONE_MSG: {
+		struct audpreproc_cmd_enc_cfg_done_msg *enc_cfg_msg = msg;
+
+		MM_DBG("CMD_ENC_CFG_DONE_MSG: stream id %d enc type \
+			0x%8x\n", enc_cfg_msg->stream_id,
+			enc_cfg_msg->rec_enc_type);
+		/* Encoder enable success */
+		if (enc_cfg_msg->rec_enc_type & ENCODE_ENABLE) {
+			if(audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL) {
+				MM_DBG("routing command\n");
+				audpreproc_cmd_cfg_routing_mode(audio);
+			} else {
+				audqcelp_in_param_config(audio);
+			}
+		} else { /* Encoder disable success */
+			audio->running = 0;
+			if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)
+				audqcelp_in_record_config(audio, 0);
+			else
+				wake_up(&audio->wait_enable);
+		}
+		break;
+	}
+	case AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG: {
+		MM_DBG("CMD_ENC_PARAM_CFG_DONE_MSG\n");
+		if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL)
+			audqcelp_in_mem_config(audio);
+		else
+			audpcm_config(audio);
+		break;
+	}
+	case AUDPREPROC_CMD_ROUTING_MODE_DONE_MSG: {
+		struct audpreproc_cmd_routing_mode_done\
+				*routing_cfg_done_msg = msg;
+		if (routing_cfg_done_msg->configuration == 0) {
+			MM_INFO("routing configuration failed\n");
+			audio->running = 0;
+		} else
+			audqcelp_in_param_config(audio);
+		break;
+	}
+	case AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG: {
+		MM_DBG("AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG \n");
+		wake_up(&audio->wait_enable);
+		break;
+	}
+	default:
+		MM_ERR("Unknown Event id %d\n", id);
+	}
+}
+
+/* ------------------- dsp audrec event handler--------------------- */
+static void audrec_dsp_event(void *data, unsigned id, size_t len,
+			    void (*getevent)(void *ptr, size_t len))
+{
+	struct audio_in *audio = data;
+
+	switch (id) {
+	case AUDREC_CMD_MEM_CFG_DONE_MSG: {
+		MM_DBG("CMD_MEM_CFG_DONE MSG DONE\n");
+		audio->running = 1;
+		if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+			if ((!audio->in_call && (audio->dev_cnt > 0)) ||
+				(audio->in_call &&
+					(audio->voice_state \
+						== VOICE_STATE_INCALL)))
+				audqcelp_in_record_config(audio, 1);
+		} else {
+			audpreproc_pcm_send_data(audio, 1);
+			wake_up(&audio->wait_enable);
+		}
+		break;
+	}
+	case AUDREC_FATAL_ERR_MSG: {
+		struct audrec_fatal_err_msg fatal_err_msg;
+
+		getevent(&fatal_err_msg, AUDREC_FATAL_ERR_MSG_LEN);
+		MM_ERR("FATAL_ERR_MSG: err id %d\n",
+				fatal_err_msg.audrec_err_id);
+		/* Error stop the encoder */
+		audio->stopped = 1;
+		wake_up(&audio->wait);
+		if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL)
+			wake_up(&audio->write_wait);
+		break;
+	}
+	case AUDREC_UP_PACKET_READY_MSG: {
+		struct audrec_up_pkt_ready_msg pkt_ready_msg;
+
+		getevent(&pkt_ready_msg, AUDREC_UP_PACKET_READY_MSG_LEN);
+		MM_DBG("UP_PACKET_READY_MSG: write cnt lsw  %d \
+		write cnt msw %d read cnt lsw %d  read cnt msw %d \n",\
+		pkt_ready_msg.audrec_packet_write_cnt_lsw, \
+		pkt_ready_msg.audrec_packet_write_cnt_msw, \
+		pkt_ready_msg.audrec_up_prev_read_cnt_lsw, \
+		pkt_ready_msg.audrec_up_prev_read_cnt_msw);
+
+		audqcelp_in_get_dsp_frames(audio);
+		break;
+	}
+	case AUDREC_CMD_PCM_BUFFER_PTR_UPDATE_ARM_TO_ENC_MSG: {
+		MM_DBG("ptr_update recieved from DSP\n");
+		audpreproc_pcm_send_data(audio, 1);
+		break;
+	}
+	case AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG: {
+		MM_ERR("AUDREC_CMD_PCM_CFG_ARM_TO_ENC_DONE_MSG");
+		audqcelp_in_mem_config(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_EOS_ACK_MSG: {
+		MM_DBG("eos ack recieved\n");
+		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 audrectask\n");
+		break;
+	}
+	default:
+		MM_ERR("Unknown Event id %d\n", id);
+	}
+}
+
+static void audqcelp_in_get_dsp_frames(struct audio_in *audio)
+{
+	struct audio_frame *frame;
+	uint32_t index;
+	unsigned long flags;
+
+	MM_DBG("head = %d\n", audio->in_head);
+	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_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_in *audio)
+{
+	struct audio_frame_nt *nt_frame;
+	uint32_t index;
+	unsigned long flags;
+	MM_DBG("head = %d\n", audio->in_head);
+	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);
+}
+
+
+struct msm_adsp_ops audrec_qcelp_adsp_ops = {
+	.event = audrec_dsp_event,
+};
+
+static int audpreproc_pcm_buffer_ptr_refresh(struct audio_in *audio,
+				       unsigned idx, unsigned len)
+{
+	struct audrec_cmd_pcm_buffer_ptr_refresh_arm_enc cmd;
+
+	if (len ==  META_OUT_SIZE)
+		len = len / 2;
+	else
+		len = (len + META_OUT_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 audrec_send_audrecqueue(audio, (void *)&cmd,
+					(unsigned int)sizeof(cmd));
+}
+
+
+static int audpcm_config(struct audio_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 = 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 audrec_send_audrecqueue(audio, (void *)&cmd,
+					(unsigned int)sizeof(cmd));
+}
+
+
+static int audpreproc_cmd_cfg_routing_mode(struct audio_in *audio)
+{
+	struct audpreproc_audrec_cmd_routing_mode cmd;
+
+	MM_DBG("\n");
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ROUTING_MODE;
+	cmd.stream_id = audio->enc_id;
+	if (audio->mode == MSM_ADSP_ENC_MODE_NON_TUNNEL)
+		cmd.routing_mode = 1;
+	return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+
+
+static int audqcelp_in_enc_config(struct audio_in *audio, int enable)
+{
+	struct audpreproc_audrec_cmd_enc_cfg cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDPREPROC_AUDREC_CMD_ENC_CFG_2;
+	cmd.stream_id = audio->enc_id;
+
+	if (enable)
+		cmd.audrec_enc_type = audio->enc_type | ENCODE_ENABLE;
+	else
+		cmd.audrec_enc_type &= ~(ENCODE_ENABLE);
+
+	return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+static int audqcelp_in_param_config(struct audio_in *audio)
+{
+	struct audpreproc_audrec_cmd_parm_cfg_qcelp13k cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.common.cmd_id = AUDPREPROC_AUDREC_CMD_PARAM_CFG;
+	cmd.common.stream_id = audio->enc_id;
+
+	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 audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+/* To Do: msm_snddev_route_enc(audio->enc_id); */
+static int audqcelp_in_record_config(struct audio_in *audio, int enable)
+{
+	struct audpreproc_afe_cmd_audio_record_cfg cmd;
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG;
+	cmd.stream_id = audio->enc_id;
+	if (enable)
+		cmd.destination_activity = AUDIO_RECORDING_TURN_ON;
+	else
+		cmd.destination_activity = AUDIO_RECORDING_TURN_OFF;
+
+	cmd.source_mix_mask = audio->source;
+	if (audio->enc_id == 2) {
+		if ((cmd.source_mix_mask &
+				INTERNAL_CODEC_TX_SOURCE_MIX_MASK) ||
+			(cmd.source_mix_mask & AUX_CODEC_TX_SOURCE_MIX_MASK) ||
+			(cmd.source_mix_mask & VOICE_UL_SOURCE_MIX_MASK) ||
+			(cmd.source_mix_mask & VOICE_DL_SOURCE_MIX_MASK)) {
+			cmd.pipe_id = SOURCE_PIPE_1;
+		}
+		if (cmd.source_mix_mask &
+				AUDPP_A2DP_PIPE_SOURCE_MIX_MASK)
+			cmd.pipe_id |= SOURCE_PIPE_0;
+	}
+	MM_DBG("stream_id %x destination_activity %x \
+	source_mix_mask %x pipe_id %x",\
+	cmd.stream_id, cmd.destination_activity,
+	cmd.source_mix_mask, cmd.pipe_id);
+	return audpreproc_send_audreccmdqueue(&cmd, sizeof(cmd));
+}
+
+static int audqcelp_in_mem_config(struct audio_in *audio)
+{
+	struct audrec_cmd_arecmem_cfg cmd;
+	uint16_t *data = (void *) audio->data;
+	int n;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDREC_CMD_MEM_CFG_CMD;
+	cmd.audrec_up_pkt_intm_count = 1;
+	cmd.audrec_ext_pkt_start_addr_msw = audio->phys >> 16;
+	cmd.audrec_ext_pkt_start_addr_lsw = audio->phys;
+	cmd.audrec_ext_pkt_buf_number = FRAME_NUM;
+	MM_DBG("audio->phys = %x\n", audio->phys);
+	/* prepare buffer pointers:
+	 * T:36 bytes qcelp ppacket + 4 halfword header
+	 * NT:36 bytes qcelp packet + 12 halfword header
+	 */
+	for (n = 0; n < FRAME_NUM; n++) {
+		if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+			audio->in[n].data = data + 4;
+			data += (FRAME_SIZE/2);
+			MM_DBG("0x%8x\n", (int)(audio->in[n].data - 8));
+		} else  {
+			audio->in[n].data = data + 12;
+			data += ((QCELP_FRAME_SIZE) / 2) + 12;
+			MM_DBG("0x%8x\n", (int)(audio->in[n].data - 24));
+		}
+	}
+	return audrec_send_audrecqueue(audio, &cmd, sizeof(cmd));
+}
+
+static int audqcelp_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt)
+{
+	struct up_audrec_packet_ext_ptr cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = UP_AUDREC_PACKET_EXT_PTR;
+	cmd.audrec_up_curr_read_count_msw = read_cnt >> 16;
+	cmd.audrec_up_curr_read_count_lsw = read_cnt;
+
+	return audrec_send_bitstreamqueue(audio, &cmd, sizeof(cmd));
+}
+static int audqcelp_flush_command(struct audio_in *audio)
+{
+	struct audrec_cmd_flush cmd;
+	MM_DBG("\n");
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDREC_CMD_FLUSH;
+	return audrec_send_audrecqueue(audio, &cmd, sizeof(cmd));
+}
+
+/* must be called with audio->lock held */
+static int audqcelp_in_enable(struct audio_in *audio)
+{
+	if (audio->enabled)
+		return 0;
+
+	if (audpreproc_enable(audio->enc_id, &audpreproc_dsp_event, audio)) {
+		MM_ERR("msm_adsp_enable(audpreproc) failed\n");
+		return -ENODEV;
+	}
+
+	if (msm_adsp_enable(audio->audrec)) {
+		MM_ERR("msm_adsp_enable(audrec) failed\n");
+		audpreproc_disable(audio->enc_id, audio);
+		return -ENODEV;
+	}
+	audio->enabled = 1;
+	audqcelp_in_enc_config(audio, 1);
+
+	return 0;
+}
+
+/* must be called with audio->lock held */
+static int audqcelp_in_disable(struct audio_in *audio)
+{
+	if (audio->enabled) {
+		audio->enabled = 0;
+		audqcelp_in_enc_config(audio, 0);
+		wake_up(&audio->wait);
+		wait_event_interruptible_timeout(audio->wait_enable,
+				audio->running == 0, 1*HZ);
+		msm_adsp_disable(audio->audrec);
+		audpreproc_disable(audio->enc_id, audio);
+	}
+	return 0;
+}
+
+static void audqcelp_ioport_reset(struct audio_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_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 = 0; i < FRAME_NUM; 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_in *audio)
+{
+	int i;
+
+	audio->out_head = 0;
+	audio->out_tail = 0;
+	audio->out_count = 0;
+	for (i = 0; i < OUT_FRAME_NUM; 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_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: {
+		uint32_t freq;
+		freq = 48000;
+		MM_DBG("AUDIO_START\n");
+		if (audio->in_call && (audio->voice_state !=
+				VOICE_STATE_INCALL)) {
+			rc = -EPERM;
+			break;
+		}
+		rc = msm_snddev_request_freq(&freq, audio->enc_id,
+					SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+		MM_DBG("sample rate configured %d\n", freq);
+		if (rc < 0) {
+			MM_DBG(" Sample rate can not be set, return code %d\n",
+								 rc);
+			msm_snddev_withdraw_freq(audio->enc_id,
+					SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+			MM_DBG("msm_snddev_withdraw_freq\n");
+			break;
+		}
+		/*update aurec session info in audpreproc layer*/
+		audio->session_info.session_id = audio->enc_id;
+		audio->session_info.sampling_freq = audio->samp_rate;
+		audpreproc_update_audrec_info(&audio->session_info);
+		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: {
+		/*reset the sampling frequency information at audpreproc layer*/
+		audio->session_info.sampling_freq = 0;
+		audpreproc_update_audrec_info(&audio->session_info);
+		rc = audqcelp_in_disable(audio);
+		rc = msm_snddev_withdraw_freq(audio->enc_id,
+					SNDDEV_CAP_TX, AUDDEV_CLNT_ENC);
+		MM_DBG("msm_snddev_withdraw_freq\n");
+		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_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_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;
+		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;
+	}
+	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 = audio->samp_rate;
+		cfg.channel_count = audio->channel_mode;
+		if (copy_to_user((void *)arg, &cfg, sizeof(cfg)))
+			rc = -EFAULT;
+		break;
+	}
+	case AUDIO_SET_INCALL: {
+		struct msm_voicerec_mode cfg;
+		unsigned long flags;
+		if (audio->mode == MSM_AUD_ENC_MODE_TUNNEL) {
+			if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) {
+				rc = -EFAULT;
+				break;
+			}
+			if (cfg.rec_mode != VOC_REC_BOTH &&
+				cfg.rec_mode != VOC_REC_UPLINK &&
+				cfg.rec_mode != VOC_REC_DOWNLINK) {
+				MM_ERR("invalid rec_mode\n");
+				rc = -EINVAL;
+				break;
+			} else {
+				spin_lock_irqsave(&audio->dev_lock, flags);
+				if (cfg.rec_mode == VOC_REC_UPLINK)
+					audio->source = \
+						VOICE_UL_SOURCE_MIX_MASK;
+				else if (cfg.rec_mode == VOC_REC_DOWNLINK)
+					audio->source = \
+						VOICE_DL_SOURCE_MIX_MASK;
+				else
+					audio->source = \
+						VOICE_DL_SOURCE_MIX_MASK |
+						VOICE_UL_SOURCE_MIX_MASK ;
+				audio->in_call = 1;
+				spin_unlock_irqrestore(&audio->dev_lock, flags);
+			}
+		}
+		break;
+	}
+	case AUDIO_GET_SESSION_ID: {
+		if (copy_to_user((void *) arg, &audio->enc_id,
+			sizeof(unsigned short))) {
+			rc = -EFAULT;
+		}
+		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_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 ||
+			((audio->mode == MSM_AUD_ENC_MODE_TUNNEL) &&
+			 audio->in_call && audio->running &&
+			(audio->voice_state == VOICE_STATE_OFFCALL)));
+		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;
+			} else if ((audio->mode == MSM_AUD_ENC_MODE_TUNNEL) &&
+					audio->in_call && audio->running &&
+					(audio->voice_state \
+						== VOICE_STATE_OFFCALL)) {
+				MM_DBG("Not Permitted Voice Terminated\n");
+				rc = -EPERM; /* Voice Call stopped */
+				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) {
+			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_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 audpreproc_pcm_send_data(struct audio_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);
+			audpreproc_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_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 audpreproc_qcelp_process_eos(struct audio_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);
+	audpreproc_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_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)
+		audpreproc_pcm_send_data(audio, 0);
+	else {
+		audpreproc_pcm_send_data(audio, 1);
+		audio->flush_ack = 0;
+	}
+	if (eos_condition == AUDPREPROC_QCELP_EOS_SET)
+		rc = audpreproc_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_in *audio = file->private_data;
+
+	mutex_lock(&audio->lock);
+	audio->in_call = 0;
+	/* with draw frequency for session
+	   incase not stopped the driver */
+	msm_snddev_withdraw_freq(audio->enc_id, SNDDEV_CAP_TX,
+					AUDDEV_CLNT_ENC);
+	auddev_unregister_evt_listner(AUDDEV_CLNT_ENC, audio->enc_id);
+	/*reset the sampling frequency information at audpreproc layer*/
+	audio->session_info.sampling_freq = 0;
+	audpreproc_update_audrec_info(&audio->session_info);
+	audqcelp_in_disable(audio);
+	audqcelp_in_flush(audio);
+	msm_adsp_put(audio->audrec);
+	audpreproc_aenc_free(audio->enc_id);
+	audio->audrec = NULL;
+	audio->opened = 0;
+	if (audio->data) {
+		iounmap(audio->data);
+		pmem_kfree(audio->phys);
+		audio->data = NULL;
+	}
+	if (audio->out_data) {
+		iounmap(audio->out_data);
+		pmem_kfree(audio->out_phys);
+		audio->out_data = NULL;
+	}
+	mutex_unlock(&audio->lock);
+	return 0;
+}
+
+struct audio_in the_audio_qcelp_in;
+static int audqcelp_in_open(struct inode *inode, struct file *file)
+{
+	struct audio_in *audio = &the_audio_qcelp_in;
+	int rc;
+	int encid;
+
+	mutex_lock(&audio->lock);
+	if (audio->opened) {
+		rc = -EBUSY;
+		goto done;
+	}
+	audio->phys = pmem_kalloc(DMASZ, PMEM_MEMTYPE_EBI1|
+					PMEM_ALIGNMENT_4K);
+	if (!IS_ERR((void *)audio->phys)) {
+		audio->data = ioremap(audio->phys, DMASZ);
+		if (!audio->data) {
+			MM_ERR("could not allocate DMA buffers\n");
+			rc = -ENOMEM;
+			pmem_kfree(audio->phys);
+			goto done;
+		}
+	} else {
+		MM_ERR("could not allocate DMA buffers\n");
+		rc = -ENOMEM;
+		goto done;
+	}
+	MM_DBG("Memory addr = 0x%8x  phy addr = 0x%8x\n",\
+		(int) audio->data, (int) audio->phys);
+	if ((file->f_mode & FMODE_WRITE) &&
+		(file->f_mode & FMODE_READ)) {
+		audio->mode = MSM_AUD_ENC_MODE_NONTUNNEL;
+		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;
+		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
+	 */
+	if (audio->mode == MSM_AUD_ENC_MODE_NONTUNNEL)
+			audio->buffer_size = (QCELP_FRAME_SIZE + 14);
+	else
+			audio->buffer_size = (FRAME_SIZE - 8);
+	audio->enc_type = ENC_TYPE_V13K | audio->mode;
+	audio->samp_rate = 8000;
+	audio->channel_mode = AUDREC_CMD_MODE_MONO;
+	audio->cfg.cdma_rate = CDMA_RATE_FULL;
+	audio->cfg.min_bit_rate = CDMA_RATE_FULL;
+	audio->cfg.max_bit_rate = CDMA_RATE_FULL;
+	audio->source = INTERNAL_CODEC_TX_SOURCE_MIX_MASK;
+	audio->rec_mode = VOC_REC_UPLINK;
+
+	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;
+	}
+
+	audio->stopped = 0;
+	audio->source = 0;
+	audio->wflush = 0;
+	audio->rflush = 0;
+	audio->flush_ack = 0;
+
+	audqcelp_in_flush(audio);
+	audqcelp_out_flush(audio);
+
+	audio->out_phys = pmem_kalloc(BUFFER_SIZE,
+				PMEM_MEMTYPE_EBI1 | PMEM_ALIGNMENT_4K);
+	if (IS_ERR((void *)audio->out_phys)) {
+		MM_ERR("could not allocate write buffers\n");
+		rc = -ENOMEM;
+		goto evt_error;
+	} else {
+		audio->out_data = ioremap(audio->out_phys, BUFFER_SIZE);
+		if (!audio->out_data) {
+			MM_ERR("could not allocate write buffers\n");
+			rc = -ENOMEM;
+			pmem_kfree(audio->out_phys);
+			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->device_events = AUDDEV_EVT_DEV_RDY | AUDDEV_EVT_DEV_RLS |
+				AUDDEV_EVT_VOICE_STATE_CHG;
+
+	audio->voice_state = msm_get_voice_state();
+	rc = auddev_register_evt_listner(audio->device_events,
+					AUDDEV_CLNT_ENC, audio->enc_id,
+					qcelp_in_listener, (void *) audio);
+	if (rc) {
+		MM_ERR("failed to register device event listener\n");
+		iounmap(audio->out_data);
+		pmem_kfree(audio->out_phys);
+		goto evt_error;
+	}
+	audio->mfield = META_OUT_SIZE;
+	file->private_data = audio;
+	audio->opened = 1;
+	audio->out_frame_cnt++;
+done:
+	mutex_unlock(&audio->lock);
+	return rc;
+evt_error:
+	msm_adsp_put(audio->audrec);
+	audpreproc_aenc_free(audio->enc_id);
+	mutex_unlock(&audio->lock);
+	return rc;
+}
+
+static const struct file_operations audio_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,
+};
+
+struct miscdevice audio_qcelp_in_misc = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "msm_qcelp_in",
+	.fops	= &audio_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);
+	spin_lock_init(&the_audio_qcelp_in.dev_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(&audio_qcelp_in_misc);
+}
+
+device_initcall(audqcelp_in_init);
diff --git a/arch/arm/mach-msm/qdsp5v2/audio_wma.c b/arch/arm/mach-msm/qdsp5v2/audio_wma.c
new file mode 100644
index 0000000..2695b83
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_wma.c
@@ -0,0 +1,1785 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Based on the mp3 native driver in arch/arm/mach-msm/qdsp5v2/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 <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+#include <linux/slab.h>
+#include <linux/msm_audio.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+
+#include <linux/msm_audio_wma.h>
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/qdsp5audplaycmdi.h>
+#include <mach/qdsp5v2/qdsp5audplaymsg.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/qdsp5v2/audpp.h>
+#include <mach/debug_mm.h>
+
+/* Size must be power of 2 */
+#define BUFSZ_MAX 	4110	/* 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;
+
+	/* 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 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;
+	int16_t source;
+
+#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;
+	/* AV sync Info */
+	int avsync_flag;              /* Flag to indicate feedback from DSP */
+	wait_queue_head_t avsync_wait;/* Wait queue for AV Sync Message     */
+	/* flags, 48 bits sample/bytes counter per channel */
+	uint16_t avsync[AUDPP_AVSYNC_CH_COUNT * AUDPP_AVSYNC_NUM_WORDS + 1];
+
+	uint32_t device_events;
+
+	int eq_enable;
+	int eq_needs_commit;
+	struct audpp_cmd_cfg_object_params_eqalizer eq;
+	struct 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 audwma_post_event(struct audio *audio, int type,
+		union msm_audio_event_payload payload);
+
+/* must be called with audio->lock held */
+static int audio_enable(struct audio *audio)
+{
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	if (audio->enabled)
+		return 0;
+
+	audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+	audio->out_tail = 0;
+	audio->out_needed = 0;
+
+	if (msm_adsp_enable(audio->audplay)) {
+		MM_ERR("msm_adsp_enable(audplay) failed\n");
+		return -ENODEV;
+	}
+
+	if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) {
+		MM_ERR("audpp_enable() failed\n");
+		msm_adsp_disable(audio->audplay);
+		return -ENODEV;
+	}
+
+	audio->enabled = 1;
+	return 0;
+}
+
+static void wma_listner(u32 evt_id, union auddev_evt_data *evt_payload,
+			void *private_data)
+{
+	struct audio *audio = (struct audio *) private_data;
+	switch (evt_id) {
+	case AUDDEV_EVT_DEV_RDY:
+		MM_DBG(":AUDDEV_EVT_DEV_RDY\n");
+		audio->source |= (0x1 << evt_payload->routing_id);
+		if (audio->running == 1 && audio->enabled == 1)
+			audpp_route_stream(audio->dec_id, audio->source);
+		break;
+	case AUDDEV_EVT_DEV_RLS:
+		MM_DBG(":AUDDEV_EVT_DEV_RLS\n");
+		audio->source &= ~(0x1 << evt_payload->routing_id);
+		if (audio->running == 1 && audio->enabled == 1)
+			audpp_route_stream(audio->dec_id, audio->source);
+		break;
+	case AUDDEV_EVT_STREAM_VOL_CHG:
+		audio->vol_pan.volume = evt_payload->session_vol;
+		MM_DBG(":AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n",
+				audio->vol_pan.volume);
+		if (audio->running)
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+					POPP);
+		break;
+	default:
+		MM_ERR(":ERROR:wrong event\n");
+		break;
+	}
+}
+/* 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);
+		audio->out_needed = 0;
+	}
+	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");
+				/* send  mixer command */
+				audpp_route_stream(audio->dec_id,
+						audio->source);
+				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,
+					POPP);
+			audpp_dsp_set_eq(audio->dec_id, audio->eq_enable,
+					&audio->eq, POPP);
+		} else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+			MM_DBG("CFG_MSG DISABLE\n");
+			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;
+
+	case AUDPP_MSG_AVSYNC_MSG:
+		MM_DBG("AUDPP_MSG_AVSYNC_MSG\n");
+		memcpy(&audio->avsync[0], msg, sizeof(audio->avsync));
+		audio->avsync_flag = 1;
+		wake_up(&audio->avsync_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)
+{
+	struct audpp_cmd_cfg_dec_type cfg_dec_cmd;
+
+	memset(&cfg_dec_cmd, 0, sizeof(cfg_dec_cmd));
+
+	cfg_dec_cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+	if (enable)
+		cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+				AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_WMA;
+	else
+		cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+				AUDPP_CMD_DIS_DEC_V;
+	cfg_dec_cmd.dm_mode = 0x0;
+	cfg_dec_cmd.stream_id = audio->dec_id;
+	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->pcm_feedback)
+		cmd.decoder_id = 0;
+	else {
+		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;
+	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);
+	audio->avsync_flag = 1;
+	wake_up(&audio->avsync_wait);
+}
+
+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, POPP);
+		audio->eq_needs_commit = 0;
+	}
+	return 0;
+}
+
+static int audio_get_avsync_data(struct audio *audio,
+						struct msm_audio_stats *stats)
+{
+	int rc = -EINVAL;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	if (audio->dec_id == audio->avsync[0] && audio->avsync_flag) {
+		/* av_sync sample count */
+		stats->sample_count = (audio->avsync[2] << 16) |
+						(audio->avsync[3]);
+
+		/* av_sync byte_count */
+		stats->byte_count = (audio->avsync[5] << 16) |
+						(audio->avsync[6]);
+
+		audio->avsync_flag = 0;
+		rc = 0;
+	}
+	local_irq_restore(flags);
+	return rc;
+
+}
+
+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;
+
+		audio->avsync_flag = 0;
+		memset(&stats, 0, sizeof(stats));
+		if (audpp_query_avsync(audio->dec_id) < 0)
+			return rc;
+
+		rc = wait_event_interruptible_timeout(audio->avsync_wait,
+				(audio->avsync_flag == 1),
+				msecs_to_jiffies(AUDPP_AVSYNC_EVENT_TIMEOUT));
+
+		if (rc < 0)
+			return rc;
+		else if ((rc > 0) || ((rc == 0) && (audio->avsync_flag == 1))) {
+			if (audio_get_avsync_data(audio, &stats) < 0)
+				return rc;
+
+			if (copy_to_user((void *)arg, &stats, sizeof(stats)))
+				return -EFAULT;
+			return 0;
+		} else
+			return -EAGAIN;
+	}
+
+	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,
+					POPP);
+		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,
+					POPP);
+		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;
+	case AUDIO_GET_SESSION_ID:
+		if (copy_to_user((void *) arg, &audio->dec_id,
+					sizeof(unsigned short)))
+			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;
+	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 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);
+	auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id);
+	audio_disable(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;
+}
+
+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);
+			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 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;
+
+	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);
+		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);
+	init_waitqueue_head(&audio->avsync_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->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;
+
+	audio->device_events = AUDDEV_EVT_DEV_RDY
+				|AUDDEV_EVT_DEV_RLS|
+				AUDDEV_EVT_STREAM_VOL_CHG;
+
+	rc = auddev_register_evt_listner(audio->device_events,
+					AUDDEV_CLNT_DEC,
+					audio->dec_id,
+					wma_listner,
+					(void *)audio);
+	if (rc) {
+		MM_ERR("%s: failed to register listner\n", __func__);
+		goto event_err;
+	}
+
+#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;
+event_err:
+	msm_adsp_put(audio->audplay);
+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/qdsp5v2/audio_wmapro.c b/arch/arm/mach-msm/qdsp5v2/audio_wmapro.c
new file mode 100644
index 0000000..e9837be
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audio_wmapro.c
@@ -0,0 +1,1801 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * Based on the mp3 native driver in arch/arm/mach-msm/qdsp5v2/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 <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/msm_adsp.h>
+
+#include <linux/msm_audio.h>
+#include <linux/slab.h>
+#include <linux/msm_audio_wmapro.h>
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/qdsp5audplaycmdi.h>
+#include <mach/qdsp5v2/qdsp5audplaymsg.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/qdsp5v2/audpp.h>
+#include <mach/debug_mm.h>
+
+/* Size must be power of 2 */
+#define BUFSZ_MAX 	4110	/* 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;
+
+	/* 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 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;
+	int16_t source;
+
+#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;
+	/* AV sync Info */
+	int avsync_flag;              /* Flag to indicate feedback from DSP */
+	wait_queue_head_t avsync_wait;/* Wait queue for AV Sync Message     */
+	/* flags, 48 bits sample/bytes counter per channel */
+	uint16_t avsync[AUDPP_AVSYNC_CH_COUNT * AUDPP_AVSYNC_NUM_WORDS + 1];
+
+	uint32_t device_events;
+
+	int eq_enable;
+	int eq_needs_commit;
+	struct audpp_cmd_cfg_object_params_eqalizer eq;
+	struct 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 audwmapro_post_event(struct audio *audio, int type,
+		union msm_audio_event_payload payload);
+
+/* must be called with audio->lock held */
+static int audio_enable(struct audio *audio)
+{
+	MM_DBG("\n"); /* Macro prints the file name and function */
+	if (audio->enabled)
+		return 0;
+
+	audio->dec_state = MSM_AUD_DECODER_STATE_NONE;
+	audio->out_tail = 0;
+	audio->out_needed = 0;
+
+	if (msm_adsp_enable(audio->audplay)) {
+		MM_ERR("msm_adsp_enable(audplay) failed\n");
+		return -ENODEV;
+	}
+
+	if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) {
+		MM_ERR("audpp_enable() failed\n");
+		msm_adsp_disable(audio->audplay);
+		return -ENODEV;
+	}
+
+	audio->enabled = 1;
+	return 0;
+}
+
+static void wmapro_listner(u32 evt_id, union auddev_evt_data *evt_payload,
+			void *private_data)
+{
+	struct audio *audio = (struct audio *) private_data;
+	switch (evt_id) {
+	case AUDDEV_EVT_DEV_RDY:
+		MM_DBG(":AUDDEV_EVT_DEV_RDY\n");
+		audio->source |= (0x1 << evt_payload->routing_id);
+		if (audio->running == 1 && audio->enabled == 1)
+			audpp_route_stream(audio->dec_id, audio->source);
+		break;
+	case AUDDEV_EVT_DEV_RLS:
+		MM_DBG(":AUDDEV_EVT_DEV_RLS\n");
+		audio->source &= ~(0x1 << evt_payload->routing_id);
+		if (audio->running == 1 && audio->enabled == 1)
+			audpp_route_stream(audio->dec_id, audio->source);
+		break;
+	case AUDDEV_EVT_STREAM_VOL_CHG:
+		audio->vol_pan.volume = evt_payload->session_vol;
+		MM_DBG(":AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d\n",
+				audio->vol_pan.volume);
+		if (audio->running)
+			audpp_dsp_set_vol_pan(audio->dec_id, &audio->vol_pan,
+					POPP);
+		break;
+	default:
+		MM_ERR(":ERROR:wrong event\n");
+		break;
+	}
+}
+
+/* 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);
+		audio->out_needed = 0;
+	}
+	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");
+				audpp_route_stream(audio->dec_id,
+						audio->source);
+				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,
+					POPP);
+			audpp_dsp_set_eq(audio->dec_id, audio->eq_enable,
+					&audio->eq, POPP);
+		} else if (msg[0] == AUDPP_MSG_ENA_DIS) {
+			MM_DBG("CFG_MSG DISABLE\n");
+			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;
+
+	case AUDPP_MSG_AVSYNC_MSG:
+		MM_DBG("AUDPP_MSG_AVSYNC_MSG\n");
+		memcpy(&audio->avsync[0], msg, sizeof(audio->avsync));
+		audio->avsync_flag = 1;
+		wake_up(&audio->avsync_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)
+{
+	struct audpp_cmd_cfg_dec_type cfg_dec_cmd;
+
+	memset(&cfg_dec_cmd, 0, sizeof(cfg_dec_cmd));
+
+	cfg_dec_cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE;
+	if (enable)
+		cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+				AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_WMAPRO;
+	else
+		cfg_dec_cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC |
+				AUDPP_CMD_DIS_DEC_V;
+	cfg_dec_cmd.dm_mode = 0x0;
+	cfg_dec_cmd.stream_id = audio->dec_id;
+	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;
+
+	/*
+	 * Test done for sample with the following configuration
+	 * armdatareqthr 	= 1262
+	 * channelsdecoded 	= 1(MONO)/2(STEREO)
+	 * wmaprobytespersec 	= Tested with 6003 Bytes per sec
+	 * wmaprosamplingfreq	= 44100
+	 * wmaproencoderopts	= 31
+	 */
+
+	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);
+	audio->avsync_flag = 1;
+	wake_up(&audio->avsync_wait);
+}
+
+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, POPP);
+		audio->eq_needs_commit = 0;
+	}
+	return 0;
+}
+
+static int audio_get_avsync_data(struct audio *audio,
+						struct msm_audio_stats *stats)
+{
+	int rc = -EINVAL;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	if (audio->dec_id == audio->avsync[0] && audio->avsync_flag) {
+		/* av_sync sample count */
+		stats->sample_count = (audio->avsync[2] << 16) |
+						(audio->avsync[3]);
+
+		/* av_sync byte_count */
+		stats->byte_count = (audio->avsync[5] << 16) |
+						(audio->avsync[6]);
+
+		audio->avsync_flag = 0;
+		rc = 0;
+	}
+	local_irq_restore(flags);
+	return rc;
+
+}
+
+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;
+
+		audio->avsync_flag = 0;
+		memset(&stats, 0, sizeof(stats));
+		if (audpp_query_avsync(audio->dec_id) < 0)
+			return rc;
+
+		rc = wait_event_interruptible_timeout(audio->avsync_wait,
+				(audio->avsync_flag == 1),
+				msecs_to_jiffies(AUDPP_AVSYNC_EVENT_TIMEOUT));
+
+		if (rc < 0)
+			return rc;
+		else if ((rc > 0) || ((rc == 0) && (audio->avsync_flag == 1))) {
+			if (audio_get_avsync_data(audio, &stats) < 0)
+				return rc;
+
+			if (copy_to_user((void *)arg, &stats, sizeof(stats)))
+				return -EFAULT;
+			return 0;
+		} else
+			return -EAGAIN;
+	}
+
+	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,
+					POPP);
+		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,
+					POPP);
+		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;
+	case AUDIO_GET_SESSION_ID:
+		if (copy_to_user((void *) arg, &audio->dec_id,
+					sizeof(unsigned short)))
+			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;
+	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);
+	auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->dec_id);
+	audio_disable(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;
+}
+
+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);
+			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 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 = 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);
+		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);
+	init_waitqueue_head(&audio->avsync_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->wmapro_config.armdatareqthr =  1268;
+	audio->wmapro_config.numchannels = 2;
+	audio->wmapro_config.avgbytespersecond = 6003;
+	audio->wmapro_config.samplingrate = 44100;
+	audio->wmapro_config.encodeopt = 224;
+	audio->wmapro_config.validbitspersample = 16;
+	audio->wmapro_config.formattag = 354;
+	audio->wmapro_config.asfpacketlength = 2230;
+	audio->wmapro_config.channelmask = 3;
+	audio->wmapro_config.advancedencodeopt = 32834;
+	audio->wmapro_config.advancedencodeopt2 = 0;*/
+
+	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;
+	audio->device_events = AUDDEV_EVT_DEV_RDY
+				|AUDDEV_EVT_DEV_RLS|
+				AUDDEV_EVT_STREAM_VOL_CHG;
+
+	rc = auddev_register_evt_listner(audio->device_events,
+					AUDDEV_CLNT_DEC,
+					audio->dec_id,
+					wmapro_listner,
+					(void *)audio);
+	if (rc) {
+		MM_ERR("%s: failed to register listner\n", __func__);
+		goto event_err;
+	}
+
+#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;
+event_err:
+	msm_adsp_put(audio->audplay);
+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/qdsp5v2/audpp.c b/arch/arm/mach-msm/qdsp5v2/audpp.c
new file mode 100644
index 0000000..be274c7
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audpp.c
@@ -0,0 +1,1133 @@
+/* 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-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/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/wakelock.h>
+
+
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include <mach/board.h>
+#include <mach/msm_adsp.h>
+#include <mach/qdsp5v2/audio_acdbi.h>
+#include <mach/qdsp5v2/qdsp5audppcmdi.h>
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/audpp.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+
+#include "../qdsp5/evlog.h"
+#include <mach/debug_mm.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);
+static struct wake_lock audpp_wake_lock;
+
+#define CH_COUNT 5
+#define AUDPP_CLNT_MAX_COUNT 6
+
+#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_STF_FLAG_ENA -1
+#define AUDPP_CMD_STF_FLAG_DIS 0x0000
+
+#define MAX_EVENT_CALLBACK_CLIENTS 	1
+
+#define AUDPP_CONCURRENCY_DEFAULT 0	/* Set default to LPA 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;
+
+	struct audpp_event_callback *cb_tbl[MAX_EVENT_CALLBACK_CLIENTS];
+
+	/* Related to decoder instances */
+	uint8_t op_mode; /* Specifies Turbo/Non Turbo mode */
+	uint8_t decoder_count; /* No. of decoders active running */
+	uint8_t codec_max_instances; /* Max codecs allowed currently */
+	uint8_t codec_cnt[MSM_MAX_DEC_CNT]; /* Nr of each codec
+						 type enabled */
+
+	wait_queue_head_t event_wait;
+};
+
+struct audpp_state the_audpp_state = {
+	.lock = &audpp_lock,
+	.lock_dec = &audpp_dec_lock,
+};
+
+static inline void prevent_suspend(void)
+{
+	wake_lock(&audpp_wake_lock);
+}
+static inline void allow_suspend(void)
+{
+	wake_unlock(&audpp_wake_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)
+{
+	struct 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));
+}
+
+void audpp_route_stream(unsigned short dec_id, unsigned short mixer_mask)
+{
+	struct audpp_cmd_cfg_dev_mixer_params mixer_params_cmd;
+
+	memset(&mixer_params_cmd, 0, sizeof(mixer_params_cmd));
+
+	mixer_params_cmd.cmd_id = AUDPP_CMD_CFG_DEV_MIXER;
+	mixer_params_cmd.stream_id = dec_id;
+	mixer_params_cmd.mixer_cmd = mixer_mask;
+	audpp_send_queue1(&mixer_params_cmd, sizeof(mixer_params_cmd));
+
+}
+EXPORT_SYMBOL(audpp_route_stream);
+
+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];
+
+	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;
+	case AUDPP_MSG_AVSYNC_MSG:
+		audpp_notify_clnt(audpp, msg[0], id, msg);
+		break;
+#ifdef CONFIG_DEBUG_FS
+	case AUDPP_MSG_FEAT_QUERY_DM_DONE:
+		MM_INFO(" RTC ACK --> %x %x %x %x %x %x %x %x\n", msg[0],\
+			msg[1], msg[2], msg[3], msg[4], \
+			msg[5], msg[6], msg[7]);
+		acdb_rtc_set_err(msg[3]);
+		break;
+#endif
+	default:
+		MM_INFO("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("audpp: cannot open AUDPPTASK\n");
+			audpp->open_count = 0;
+			audpp->func[id] = NULL;
+			audpp->private[id] = NULL;
+			goto out;
+		}
+		LOG(EV_ENABLE, 2);
+		prevent_suspend();
+		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;
+		allow_suspend();
+	}
+out:
+	mutex_unlock(audpp->lock);
+}
+EXPORT_SYMBOL(audpp_disable);
+
+#define BAD_ID(id) ((id < 0) || (id >= CH_COUNT))
+
+int audpp_restore_avsync(int id, uint16_t *avsync)
+{
+	struct audpp_cmd_avsync cmd;
+
+	if (BAD_ID(id))
+		return -1;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDPP_CMD_AVSYNC;
+	cmd.stream_id = id;
+	cmd.interrupt_interval = 0; /* Setting it to Zero as there won't be
+					periodic update */
+	cmd.sample_counter_dlsw = avsync[3];
+	cmd.sample_counter_dmsw = avsync[2];
+	cmd.sample_counter_msw = avsync[1];
+	cmd.byte_counter_dlsw = avsync[6];
+	cmd.byte_counter_dmsw = avsync[5];
+	cmd.byte_counter_msw = avsync[4];
+
+	return audpp_send_queue1(&cmd, sizeof(cmd));
+}
+EXPORT_SYMBOL(audpp_restore_avsync);
+
+int audpp_query_avsync(int id)
+{
+	struct audpp_cmd_query_avsync cmd;
+
+	if (BAD_ID(id))
+		return -EINVAL;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cmd_id = AUDPP_CMD_QUERY_AVSYNC;
+	cmd.stream_id = id;
+	return audpp_send_queue1(&cmd, sizeof(cmd));
+
+}
+EXPORT_SYMBOL(audpp_query_avsync);
+
+int audpp_set_volume_and_pan(unsigned id, unsigned volume, int pan,
+			enum obj_type objtype)
+{
+	/* cmd, obj_cfg[7], cmd_type, volume, pan */
+	uint16_t cmd[7];
+
+	if (objtype) {
+		if (id > 5) {
+			MM_ERR("Wrong POPP decoder id: %d\n", id);
+			return -EINVAL;
+		}
+	} else {
+		if (id > 3) {
+			MM_ERR("Wrong COPP decoder id: %d\n", id);
+			return -EINVAL;
+		}
+	}
+
+	memset(cmd, 0, sizeof(cmd));
+	cmd[0] = AUDPP_CMD_CFG_OBJECT_PARAMS;
+	if (objtype)
+		cmd[1] = AUDPP_CMD_POPP_STREAM;
+	else
+		cmd[1] = AUDPP_CMD_COPP_STREAM;
+	cmd[2] = id;
+	cmd[3] = AUDPP_CMD_CFG_OBJ_UPDATE;
+	cmd[4] = AUDPP_CMD_VOLUME_PAN;
+	cmd[5] = volume;
+	cmd[6] = 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,
+	struct audpp_cmd_cfg_object_params_mbadrc *mbadrc,
+	enum obj_type objtype)
+{
+	if (objtype) {
+		if (id > 5) {
+			MM_ERR("Wrong POPP decoder id: %d\n", id);
+			return -EINVAL;
+		}
+	} else {
+		if (id > 3) {
+			MM_ERR("Wrong COPP decoder id: %d\n", id);
+			return -EINVAL;
+		}
+	}
+
+	mbadrc->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS;
+	if (objtype)
+		mbadrc->common.stream = AUDPP_CMD_POPP_STREAM;
+	else
+		mbadrc->common.stream = AUDPP_CMD_COPP_STREAM;
+
+	mbadrc->common.stream_id = id;
+	mbadrc->common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE;
+	mbadrc->common.command_type = AUDPP_CMD_MBADRC;
+
+	if (enable)
+		mbadrc->enable = AUDPP_CMD_ADRC_FLAG_ENA;
+	else
+		mbadrc->enable = AUDPP_CMD_ADRC_FLAG_DIS;
+
+	return audpp_send_queue3(mbadrc,
+			sizeof(struct audpp_cmd_cfg_object_params_mbadrc));
+}
+EXPORT_SYMBOL(audpp_dsp_set_mbadrc);
+
+int audpp_dsp_set_qconcert_plus(unsigned id, unsigned enable,
+	struct audpp_cmd_cfg_object_params_qconcert *qconcert_plus,
+	enum obj_type objtype)
+{
+	if (objtype) {
+		if (id > 5) {
+			MM_ERR("Wrong POPP decoder id: %d\n", id);
+			return -EINVAL;
+		}
+	} else {
+		if (id > 3) {
+			MM_ERR("Wrong COPP decoder id: %d\n", id);
+			return -EINVAL;
+		}
+	}
+
+	qconcert_plus->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS;
+	if (objtype)
+		qconcert_plus->common.stream = AUDPP_CMD_POPP_STREAM;
+	else
+		qconcert_plus->common.stream = AUDPP_CMD_COPP_STREAM;
+
+	qconcert_plus->common.stream_id = id;
+	qconcert_plus->common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE;
+	qconcert_plus->common.command_type = AUDPP_CMD_QCONCERT;
+
+	if (enable)
+		qconcert_plus->enable_flag = AUDPP_CMD_ADRC_FLAG_ENA;
+	else
+		qconcert_plus->enable_flag = AUDPP_CMD_ADRC_FLAG_DIS;
+
+	return audpp_send_queue3(qconcert_plus,
+		sizeof(struct audpp_cmd_cfg_object_params_qconcert));
+}
+EXPORT_SYMBOL(audpp_dsp_set_qconcert_plus);
+
+int audpp_dsp_set_rx_iir(unsigned id, unsigned enable,
+	struct audpp_cmd_cfg_object_params_pcm *iir,
+	enum obj_type objtype)
+{
+
+	if (objtype) {
+		if (id > 5) {
+			MM_ERR("Wrong POPP decoder id: %d\n", id);
+			return -EINVAL;
+		}
+	} else {
+		if (id > 3) {
+			MM_ERR("Wrong COPP decoder id: %d\n", id);
+			return -EINVAL;
+		}
+	}
+
+	iir->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS;
+	if (objtype)
+		iir->common.stream = AUDPP_CMD_POPP_STREAM;
+	else
+		iir->common.stream = AUDPP_CMD_COPP_STREAM;
+
+	iir->common.stream_id = id;
+	iir->common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE;
+	iir->common.command_type = AUDPP_CMD_IIR_TUNING_FILTER;
+
+	if (enable)
+		iir->active_flag = AUDPP_CMD_IIR_FLAG_ENA;
+	else
+		iir->active_flag = AUDPP_CMD_IIR_FLAG_DIS;
+
+	return audpp_send_queue3(iir,
+		sizeof(struct audpp_cmd_cfg_object_params_pcm));
+}
+EXPORT_SYMBOL(audpp_dsp_set_rx_iir);
+
+int audpp_dsp_set_gain_rx(unsigned id,
+			struct audpp_cmd_cfg_cal_gain *calib_gain_rx,
+			enum obj_type objtype)
+{
+	if (objtype) {
+		return -EINVAL;
+	} else {
+		if (id > 3) {
+			MM_ERR("Wrong COPP decoder id: %d\n", id);
+			return -EINVAL;
+		}
+	}
+	calib_gain_rx->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS;
+	calib_gain_rx->common.stream = AUDPP_CMD_COPP_STREAM;
+
+	calib_gain_rx->common.stream_id = id;
+	calib_gain_rx->common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE;
+	calib_gain_rx->common.command_type = AUDPP_CMD_CALIB_GAIN_RX;
+
+	return audpp_send_queue3(calib_gain_rx,
+			sizeof(struct audpp_cmd_cfg_cal_gain));
+}
+EXPORT_SYMBOL(audpp_dsp_set_gain_rx);
+
+int audpp_dsp_set_pbe(unsigned id, unsigned enable,
+			struct audpp_cmd_cfg_pbe *pbe_block,
+			enum obj_type objtype)
+{
+	if (objtype) {
+		if (id > 5) {
+			MM_ERR("Wrong POPP decoder id: %d\n", id);
+			return -EINVAL;
+		}
+	} else {
+		if (id > 3) {
+			MM_ERR("Wrong COPP decoder id: %d\n", id);
+			return -EINVAL;
+		}
+	}
+
+	pbe_block->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS;
+	if (objtype)
+		pbe_block->common.stream = AUDPP_CMD_POPP_STREAM;
+	else
+		pbe_block->common.stream = AUDPP_CMD_COPP_STREAM;
+
+	pbe_block->common.stream_id = id;
+	pbe_block->common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE;
+	pbe_block->common.command_type = AUDPP_CMD_PBE;
+
+	if (enable)
+		pbe_block->pbe_enable = AUDPP_CMD_PBE_FLAG_ENA;
+	else
+		pbe_block->pbe_enable = AUDPP_CMD_PBE_FLAG_DIS;
+
+	return audpp_send_queue3(pbe_block,
+			sizeof(struct audpp_cmd_cfg_pbe));
+}
+EXPORT_SYMBOL(audpp_dsp_set_pbe);
+
+int audpp_dsp_set_spa(unsigned id,
+     struct audpp_cmd_cfg_object_params_spectram *spa,
+			enum obj_type objtype){
+	struct audpp_cmd_cfg_object_params_spectram cmd;
+
+	if (objtype) {
+		if (id > 5) {
+			MM_ERR("Wrong POPP decoder id: %d\n", id);
+			return -EINVAL;
+		}
+	} else {
+		if (id > 3) {
+			MM_ERR("Wrong COPP decoder id: %d\n", id);
+			return -EINVAL;
+		}
+	}
+
+	memset(&cmd, 0, sizeof(cmd));
+	if (objtype)
+		cmd.common.stream = AUDPP_CMD_POPP_STREAM;
+	else
+		cmd.common.stream = AUDPP_CMD_COPP_STREAM;
+
+	cmd.common.stream_id = id;
+	cmd.common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE;
+	cmd.common.command_type = AUDPP_CMD_SPECTROGRAM;
+       cmd.sample_interval = spa->sample_interval;
+	cmd.num_coeff = spa->num_coeff;
+	return audpp_send_queue3(&cmd, sizeof(cmd));
+
+}
+EXPORT_SYMBOL(audpp_dsp_set_spa);
+
+int audpp_dsp_set_stf(unsigned id, unsigned enable,
+     struct audpp_cmd_cfg_object_params_sidechain *stf,
+			enum obj_type objtype){
+	if (objtype) {
+		if (id > 5) {
+			MM_ERR("Wrong POPP decoder id: %d\n", id);
+			return -EINVAL;
+		}
+	} else {
+		if (id > 3) {
+			MM_ERR("Wrong COPP decoder id: %d\n", id);
+			return -EINVAL;
+		}
+	}
+
+	stf->common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS;
+	if (objtype)
+		stf->common.stream = AUDPP_CMD_POPP_STREAM;
+	else
+		stf->common.stream = AUDPP_CMD_COPP_STREAM;
+
+	stf->common.stream_id = id;
+	stf->common.obj_cfg = AUDPP_CMD_CFG_OBJ_UPDATE;
+	stf->common.command_type = AUDPP_CMD_SIDECHAIN_TUNING_FILTER;
+
+	if (enable)
+		stf->active_flag = AUDPP_CMD_STF_FLAG_ENA;
+	else
+		stf->active_flag = AUDPP_CMD_STF_FLAG_DIS;
+	return audpp_send_queue3(stf,
+		sizeof(struct audpp_cmd_cfg_object_params_sidechain));
+}
+EXPORT_SYMBOL(audpp_dsp_set_stf);
+
+/* Implementation Of COPP + POPP */
+int audpp_dsp_set_eq(unsigned id, unsigned enable,
+		     struct audpp_cmd_cfg_object_params_eqalizer *eq,
+				enum obj_type objtype)
+{
+	struct audpp_cmd_cfg_object_params_eqalizer cmd;
+
+	if (objtype) {
+		if (id > 5) {
+			MM_ERR("Wrong POPP decoder id: %d\n", id);
+			return -EINVAL;
+		}
+	} else {
+		if (id > 3) {
+			MM_ERR("Wrong COPP decoder id: %d\n", id);
+			return -EINVAL;
+		}
+	}
+
+	memset(&cmd, 0, sizeof(cmd));
+	if (objtype)
+		cmd.common.stream = AUDPP_CMD_POPP_STREAM;
+	else
+		cmd.common.stream = AUDPP_CMD_COPP_STREAM;
+
+	cmd.common.stream_id = id;
+	cmd.common.obj_cfg = 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,
+			  struct audpp_cmd_cfg_object_params_volume *vol_pan,
+					enum obj_type objtype)
+{
+	struct audpp_cmd_cfg_object_params_volume cmd;
+
+	if (objtype) {
+		if (id > 5) {
+			MM_ERR("Wrong POPP decoder id: %d\n", id);
+			return -EINVAL;
+		}
+	} else {
+		if (id > AUDPP_MAX_COPP_DEVICES) {
+			MM_ERR("Wrong COPP decoder id: %d\n", id);
+			return -EINVAL;
+		}
+	}
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.common.cmd_id = AUDPP_CMD_CFG_OBJECT_PARAMS;
+	if (objtype)
+		cmd.common.stream = AUDPP_CMD_POPP_STREAM;
+	else
+		cmd.common.stream = AUDPP_CMD_COPP_STREAM;
+
+	cmd.common.stream_id = id;
+	cmd.common.obj_cfg = 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;
+	pause_cmd[1] = id;
+	if (pause == 1)
+		pause_cmd[2] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_PAUSE_V;
+	else if (pause == 0)
+		pause_cmd[2] = 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;
+	flush_cmd[2] = 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;
+	u8 max_instance, codec_type;
+
+	struct dec_instance_table *dec_instance_list;
+	dec_instance_list = (struct dec_instance_table *)
+				(audpp->dec_database->dec_instance_list);
+
+	mutex_lock(audpp->lock_dec);
+	/* Represents in bit mask */
+	mode = ((dec_attrb & AUDPP_MODE_MASK) << 16);
+	codec = (1 << (dec_attrb & AUDPP_CODEC_MASK));
+	codec_type = (dec_attrb & AUDPP_CODEC_MASK);
+
+	/* Find  whether same/different codec instances are running */
+	audpp->decoder_count++;
+	audpp->codec_cnt[codec_type]++;
+	max_instance = 0;
+
+	/*if different instance of codec*/
+	if (audpp->codec_cnt[codec_type] < audpp->decoder_count) {
+		max_instance = audpp->codec_max_instances;
+		/* Get the maximum no. of instances that can be supported */
+		for (idx = 0; idx < MSM_MAX_DEC_CNT; idx++) {
+			if (audpp->codec_cnt[idx]) {
+				if ((dec_instance_list +
+					audpp->op_mode * MSM_MAX_DEC_CNT +
+						idx)->
+						max_instances_diff_dec <
+						max_instance) {
+						max_instance =
+						(dec_instance_list +
+							 audpp->op_mode *
+								MSM_MAX_DEC_CNT
+								+ idx)->
+							max_instances_diff_dec;
+				}
+			}
+		}
+		/* if different codec type, should not cross maximum other
+		   supported */
+		if (audpp->decoder_count > (max_instance + 1)) {
+			MM_ERR("Can not support, already reached max\n");
+			audpp->decoder_count--;
+			audpp->codec_cnt[codec_type]--;
+			goto done;
+		}
+		audpp->codec_max_instances = max_instance;
+		MM_DBG("different codec running\n");
+	} else {
+		max_instance = (dec_instance_list + audpp->op_mode *
+						MSM_MAX_DEC_CNT +
+						 codec_type)->
+							max_instances_same_dec;
+		/* if same codec type, should not cross maximum supported */
+		if (audpp->decoder_count > max_instance) {
+			MM_ERR("Can not support, already reached max\n");
+			audpp->decoder_count--;
+			audpp->codec_cnt[codec_type]--;
+			goto done;
+		}
+		audpp->codec_max_instances = max_instance;
+		MM_DBG("same codec running\n");
+	}
+
+	/* 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) == mode) &&
+			    (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);
+	}
+done:
+	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->decoder_count--;
+			audpp->\
+			codec_cnt[audpp->dec_info_table[idx - 1].codec]--;
+			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);
+	}
+
+	wake_lock_init(&audpp_wake_lock, WAKE_LOCK_SUSPEND, "audpp");
+	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);
+	audpp->op_mode = 0; /* Consider as non turbo mode */
+	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/qdsp5v2/audpreproc.c b/arch/arm/mach-msm/qdsp5v2/audpreproc.c
new file mode 100644
index 0000000..09611a5
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/audpreproc.c
@@ -0,0 +1,523 @@
+/*
+ * Common code to deal with the AUDPREPROC dsp task (audio preprocessing)
+ *
+ * Copyright (c) 2009-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 <linux/wakelock.h>
+#include <mach/msm_adsp.h>
+#include <mach/qdsp5v2/audio_acdbi.h>
+#include <mach/qdsp5v2/audpreproc.h>
+#include <mach/debug_mm.h>
+#include <mach/qdsp5v2/qdsp5audpreprocmsg.h>
+
+static DEFINE_MUTEX(audpreproc_lock);
+static struct wake_lock audpre_wake_lock;
+static struct wake_lock audpre_idle_wake_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 }
+
+#define MAX_EVENT_CALLBACK_CLIENTS 1
+
+#define ENC0_FORMAT ((1<<MSM_ADSP_ENC_CODEC_WAV)| \
+	(1<<MSM_ADSP_ENC_CODEC_SBC) | (1<<MSM_ADSP_ENC_CODEC_EXT_WAV))
+
+#define ENC1_FORMAT ((1<<MSM_ADSP_ENC_CODEC_WAV)| \
+	(1<<MSM_ADSP_ENC_CODEC_AAC) | (1<<MSM_ADSP_ENC_CODEC_AMRNB) | \
+	(1<<MSM_ADSP_ENC_CODEC_EVRC) | (1<<MSM_ADSP_ENC_CODEC_QCELP) | \
+	(1<<MSM_ADSP_ENC_CODEC_EXT_WAV))
+
+#define ENC2_FORMAT ((1<<MSM_ADSP_ENC_CODEC_WAV) | \
+	(1<<MSM_ADSP_ENC_CODEC_EXT_WAV))
+
+struct msm_adspenc_database {
+	unsigned num_enc;
+	struct msm_adspenc_info *enc_info_list;
+};
+
+static struct msm_adspenc_info enc_info_list[] = {
+	ENC_MODULE_INFO("AUDREC0TASK", \
+			((QDSP_uPAudRec0BitStreamQueue << 16)| \
+			   QDSP_uPAudRec0CmdQueue), 0, \
+			 (ENC0_FORMAT  | (1 << MSM_ADSP_ENC_MODE_TUNNEL)), 3),
+
+	ENC_MODULE_INFO("AUDREC1TASK", \
+			 ((QDSP_uPAudRec1BitStreamQueue << 16)| \
+			   QDSP_uPAudRec1CmdQueue), 1, \
+			 (ENC1_FORMAT | (1 << MSM_ADSP_ENC_MODE_TUNNEL) | \
+			  (1 << MSM_ADSP_ENC_MODE_NON_TUNNEL)), 6),
+
+	ENC_MODULE_INFO("AUDREC2TASK", \
+			 ((QDSP_uPAudRec2BitStreamQueue << 16)| \
+			   QDSP_uPAudRec2CmdQueue), 2, \
+			 (ENC2_FORMAT  | (1 << MSM_ADSP_ENC_MODE_TUNNEL)), 2),
+
+};
+
+static struct msm_adspenc_database msm_enc_database = {
+	.num_enc = ARRAY_SIZE(enc_info_list),
+	.enc_info_list = enc_info_list,
+};
+
+
+static struct audrec_session_info
+		session_info[MAX_ENC_COUNT] = { {0, 0}, {0, 0}, {0, 0} };
+
+struct audpreproc_state {
+	struct msm_adsp_module *mod;
+	audpreproc_event_func func[MAX_ENC_COUNT];
+	void *private[MAX_ENC_COUNT];
+	struct mutex *lock;
+	unsigned open_count;
+	unsigned enc_inuse;
+	struct audpreproc_event_callback *cb_tbl[MAX_EVENT_CALLBACK_CLIENTS];
+};
+
+static struct audpreproc_state the_audpreproc_state = {
+	.lock = &audpreproc_lock,
+};
+
+static inline void prevent_suspend(void)
+{
+	wake_lock(&audpre_wake_lock);
+	wake_lock(&audpre_idle_wake_lock);
+}
+static inline void allow_suspend(void)
+{
+	wake_unlock(&audpre_wake_lock);
+	wake_unlock(&audpre_idle_wake_lock);
+}
+
+/* DSP preproc event handler */
+static void audpreproc_dsp_event(void *data, unsigned id, size_t len,
+			    void (*getevent)(void *ptr, size_t len))
+{
+	struct audpreproc_state *audpreproc = data;
+	int n = 0;
+
+	switch (id) {
+	case AUDPREPROC_CMD_CFG_DONE_MSG: {
+		struct audpreproc_cmd_cfg_done_msg cfg_done_msg;
+
+		getevent(&cfg_done_msg, AUDPREPROC_CMD_CFG_DONE_MSG_LEN);
+		MM_DBG("AUDPREPROC_CMD_CFG_DONE_MSG: stream id %d preproc \
+			type %x\n", cfg_done_msg.stream_id, \
+			cfg_done_msg.aud_preproc_type);
+		if ((cfg_done_msg.stream_id < MAX_ENC_COUNT) &&
+				audpreproc->func[cfg_done_msg.stream_id])
+			audpreproc->func[cfg_done_msg.stream_id](
+			audpreproc->private[cfg_done_msg.stream_id], id,
+			&cfg_done_msg);
+		break;
+	}
+	case AUDPREPROC_ERROR_MSG: {
+		struct audpreproc_err_msg err_msg;
+
+		getevent(&err_msg, AUDPREPROC_ERROR_MSG_LEN);
+		MM_DBG("AUDPREPROC_ERROR_MSG: stream id %d err idx %d\n",
+		err_msg.stream_id, err_msg.aud_preproc_err_idx);
+		if ((err_msg.stream_id < MAX_ENC_COUNT) &&
+				audpreproc->func[err_msg.stream_id])
+			audpreproc->func[err_msg.stream_id](
+			audpreproc->private[err_msg.stream_id], id,
+			&err_msg);
+		break;
+	}
+	case AUDPREPROC_CMD_ENC_CFG_DONE_MSG: {
+		struct audpreproc_cmd_enc_cfg_done_msg enc_cfg_msg;
+
+		getevent(&enc_cfg_msg, AUDPREPROC_CMD_ENC_CFG_DONE_MSG_LEN);
+		MM_DBG("AUDPREPROC_CMD_ENC_CFG_DONE_MSG: stream id %d enc type \
+			%d\n", enc_cfg_msg.stream_id, enc_cfg_msg.rec_enc_type);
+		if ((enc_cfg_msg.stream_id < MAX_ENC_COUNT) &&
+				audpreproc->func[enc_cfg_msg.stream_id])
+			audpreproc->func[enc_cfg_msg.stream_id](
+			audpreproc->private[enc_cfg_msg.stream_id], id,
+			&enc_cfg_msg);
+		for (n = 0; n < MAX_EVENT_CALLBACK_CLIENTS; ++n) {
+			if (audpreproc->cb_tbl[n] &&
+					audpreproc->cb_tbl[n]->fn) {
+				audpreproc->cb_tbl[n]->fn( \
+					audpreproc->cb_tbl[n]->private, \
+					id, (void *) &enc_cfg_msg);
+			}
+		}
+		break;
+	}
+	case AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG: {
+		struct audpreproc_cmd_enc_param_cfg_done_msg enc_param_msg;
+
+		getevent(&enc_param_msg,
+				AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG_LEN);
+		MM_DBG("AUDPREPROC_CMD_ENC_PARAM_CFG_DONE_MSG: stream id %d\n",
+				 enc_param_msg.stream_id);
+		if ((enc_param_msg.stream_id < MAX_ENC_COUNT) &&
+				audpreproc->func[enc_param_msg.stream_id])
+			audpreproc->func[enc_param_msg.stream_id](
+			audpreproc->private[enc_param_msg.stream_id], id,
+			&enc_param_msg);
+		break;
+	}
+	case AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG: {
+		struct audpreproc_afe_cmd_audio_record_cfg_done
+						record_cfg_done;
+		getevent(&record_cfg_done,
+			AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG_LEN);
+		MM_DBG("AUDPREPROC_AFE_CMD_AUDIO_RECORD_CFG_DONE_MSG: \
+			stream id %d\n", record_cfg_done.stream_id);
+		if ((record_cfg_done.stream_id < MAX_ENC_COUNT) &&
+				audpreproc->func[record_cfg_done.stream_id])
+			audpreproc->func[record_cfg_done.stream_id](
+			audpreproc->private[record_cfg_done.stream_id], id,
+			&record_cfg_done);
+		break;
+	}
+	case AUDPREPROC_CMD_ROUTING_MODE_DONE_MSG: {
+		struct audpreproc_cmd_routing_mode_done  routing_mode_done;
+
+		getevent(&routing_mode_done,
+			AUDPREPROC_CMD_ROUTING_MODE_DONE_MSG_LEN);
+		MM_DBG("AUDPREPROC_CMD_ROUTING_MODE_DONE_MSG: \
+			stream id %d\n", routing_mode_done.stream_id);
+		if ((routing_mode_done.stream_id < MAX_ENC_COUNT) &&
+				audpreproc->func[routing_mode_done.stream_id])
+			audpreproc->func[routing_mode_done.stream_id](
+			audpreproc->private[routing_mode_done.stream_id], id,
+			&routing_mode_done);
+		break;
+	}
+#ifdef CONFIG_DEBUG_FS
+	case AUDPREPROC_MSG_FEAT_QUERY_DM_DONE:
+	   {
+	    uint16_t msg[3];
+	    getevent(msg, sizeof(msg));
+	    MM_INFO("RTC ACK --> %x %x %x\n", msg[0], msg[1], msg[2]);
+	    acdb_rtc_set_err(msg[2]);
+	   }
+	break;
+#endif
+	case ADSP_MESSAGE_ID: {
+		MM_DBG("Received ADSP event:module audpreproctask\n");
+		break;
+	}
+	default:
+		MM_ERR("Unknown Event %d\n", id);
+	}
+	return;
+}
+
+static struct msm_adsp_ops adsp_ops = {
+	.event = audpreproc_dsp_event,
+};
+
+/* EXPORTED API's */
+int audpreproc_enable(int enc_id, audpreproc_event_func func, void *private)
+{
+	struct audpreproc_state *audpreproc = &the_audpreproc_state;
+	int res = 0;
+
+	if (enc_id < 0 || enc_id > (MAX_ENC_COUNT - 1))
+		return -EINVAL;
+
+	mutex_lock(audpreproc->lock);
+	if (audpreproc->func[enc_id]) {
+		res = -EBUSY;
+		goto out;
+	}
+
+	audpreproc->func[enc_id] = func;
+	audpreproc->private[enc_id] = private;
+
+	/* First client to enable preproc task */
+	if (audpreproc->open_count++ == 0) {
+		MM_DBG("Get AUDPREPROCTASK\n");
+		res = msm_adsp_get("AUDPREPROCTASK", &audpreproc->mod,
+				&adsp_ops, audpreproc);
+		if (res < 0) {
+			MM_ERR("Can not get AUDPREPROCTASK\n");
+			audpreproc->open_count = 0;
+			audpreproc->func[enc_id] = NULL;
+			audpreproc->private[enc_id] = NULL;
+			goto out;
+		}
+		prevent_suspend();
+		if (msm_adsp_enable(audpreproc->mod)) {
+			MM_ERR("Can not enable AUDPREPROCTASK\n");
+			audpreproc->open_count = 0;
+			audpreproc->func[enc_id] = NULL;
+			audpreproc->private[enc_id] = NULL;
+			msm_adsp_put(audpreproc->mod);
+			audpreproc->mod = NULL;
+			res = -ENODEV;
+			allow_suspend();
+			goto out;
+		}
+	}
+	res = 0;
+out:
+	mutex_unlock(audpreproc->lock);
+	return res;
+}
+EXPORT_SYMBOL(audpreproc_enable);
+
+int audpreproc_update_audrec_info(
+			struct audrec_session_info *audrec_session_info)
+{
+	if (!audrec_session_info) {
+		MM_ERR("error in audrec session info address\n");
+		return -EINVAL;
+	}
+	if (audrec_session_info->session_id < MAX_ENC_COUNT) {
+		memcpy(&session_info[audrec_session_info->session_id],
+				audrec_session_info,
+				sizeof(struct audrec_session_info));
+		return 0;
+	}
+	return -EINVAL;
+}
+EXPORT_SYMBOL(audpreproc_update_audrec_info);
+
+void audpreproc_disable(int enc_id, void *private)
+{
+	struct audpreproc_state *audpreproc = &the_audpreproc_state;
+
+	if (enc_id < 0 || enc_id > (MAX_ENC_COUNT - 1))
+		return;
+
+	mutex_lock(audpreproc->lock);
+	if (!audpreproc->func[enc_id])
+		goto out;
+	if (audpreproc->private[enc_id] != private)
+		goto out;
+
+	audpreproc->func[enc_id] = NULL;
+	audpreproc->private[enc_id] = NULL;
+
+	/* Last client then disable preproc task */
+	if (--audpreproc->open_count == 0) {
+		msm_adsp_disable(audpreproc->mod);
+		MM_DBG("Put AUDPREPROCTASK\n");
+		msm_adsp_put(audpreproc->mod);
+		audpreproc->mod = NULL;
+		allow_suspend();
+	}
+out:
+	mutex_unlock(audpreproc->lock);
+	return;
+}
+EXPORT_SYMBOL(audpreproc_disable);
+
+
+int audpreproc_register_event_callback(struct audpreproc_event_callback *ecb)
+{
+	struct audpreproc_state *audpreproc = &the_audpreproc_state;
+	int i;
+
+	for (i = 0; i < MAX_EVENT_CALLBACK_CLIENTS; ++i) {
+		if (NULL == audpreproc->cb_tbl[i]) {
+			audpreproc->cb_tbl[i] = ecb;
+			return 0;
+		}
+	}
+	return -1;
+}
+EXPORT_SYMBOL(audpreproc_register_event_callback);
+
+int audpreproc_unregister_event_callback(struct audpreproc_event_callback *ecb)
+{
+	struct audpreproc_state *audpreproc = &the_audpreproc_state;
+	int i;
+
+	for (i = 0; i < MAX_EVENT_CALLBACK_CLIENTS; ++i) {
+		if (ecb == audpreproc->cb_tbl[i]) {
+			audpreproc->cb_tbl[i] = NULL;
+			return 0;
+		}
+	}
+	return -1;
+}
+EXPORT_SYMBOL(audpreproc_unregister_event_callback);
+
+
+/* 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;
+	static int wakelock_init;
+
+	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;
+	}
+
+	if (!wakelock_init) {
+		wake_lock_init(&audpre_wake_lock, WAKE_LOCK_SUSPEND, "audpre");
+		wake_lock_init(&audpre_idle_wake_lock, WAKE_LOCK_IDLE,
+				"audpre_idle");
+		wakelock_init = 1;
+	}
+
+	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);
+
+int audpreproc_send_preproccmdqueue(void *cmd, unsigned len)
+{
+	return msm_adsp_write(the_audpreproc_state.mod,
+				QDSP_uPAudPreProcCmdQueue, cmd, len);
+}
+EXPORT_SYMBOL(audpreproc_send_preproccmdqueue);
+
+int audpreproc_send_audreccmdqueue(void *cmd, unsigned len)
+{
+	return msm_adsp_write(the_audpreproc_state.mod,
+			      QDSP_uPAudPreProcAudRecCmdQueue, cmd, len);
+}
+EXPORT_SYMBOL(audpreproc_send_audreccmdqueue);
+
+int audpreproc_send_audrec2cmdqueue(void *cmd, unsigned len)
+{
+	return msm_adsp_write(the_audpreproc_state.mod,
+			      QDSP_uPAudRec2CmdQueue, cmd, len);
+}
+EXPORT_SYMBOL(audpreproc_send_audrec2cmdqueue);
+
+int audpreproc_dsp_set_agc(struct audpreproc_cmd_cfg_agc_params *agc,
+				unsigned len)
+{
+	return msm_adsp_write(the_audpreproc_state.mod,
+				QDSP_uPAudPreProcCmdQueue, agc, len);
+}
+EXPORT_SYMBOL(audpreproc_dsp_set_agc);
+
+int audpreproc_dsp_set_agc2(struct audpreproc_cmd_cfg_agc_params_2 *agc2,
+				unsigned len)
+{
+	return msm_adsp_write(the_audpreproc_state.mod,
+				QDSP_uPAudPreProcCmdQueue, agc2, len);
+}
+EXPORT_SYMBOL(audpreproc_dsp_set_agc2);
+
+int audpreproc_dsp_set_ns(struct audpreproc_cmd_cfg_ns_params *ns,
+				unsigned len)
+{
+	return msm_adsp_write(the_audpreproc_state.mod,
+				QDSP_uPAudPreProcCmdQueue, ns, len);
+}
+EXPORT_SYMBOL(audpreproc_dsp_set_ns);
+
+int audpreproc_dsp_set_iir(
+struct audpreproc_cmd_cfg_iir_tuning_filter_params *iir, unsigned len)
+{
+	return msm_adsp_write(the_audpreproc_state.mod,
+				QDSP_uPAudPreProcCmdQueue, iir, len);
+}
+EXPORT_SYMBOL(audpreproc_dsp_set_iir);
+
+int audpreproc_dsp_set_gain_tx(
+		struct audpreproc_cmd_cfg_cal_gain *calib_gain_tx, unsigned len)
+{
+	return msm_adsp_write(the_audpreproc_state.mod,
+			QDSP_uPAudPreProcCmdQueue, calib_gain_tx, len);
+}
+EXPORT_SYMBOL(audpreproc_dsp_set_gain_tx);
+
+void get_audrec_session_info(int id, struct audrec_session_info *info)
+{
+	if (id >= MAX_ENC_COUNT) {
+		MM_ERR("invalid session id = %d\n", id);
+		return;
+	}
+	memcpy(info, &session_info[id], sizeof(struct audrec_session_info));
+}
+EXPORT_SYMBOL(get_audrec_session_info);
+
+int audpreproc_dsp_set_lvnv(
+	struct audpreproc_cmd_cfg_lvnv_param *preproc_lvnv, unsigned len)
+{
+	return msm_adsp_write(the_audpreproc_state.mod,
+		QDSP_uPAudPreProcCmdQueue, preproc_lvnv, len);
+}
+EXPORT_SYMBOL(audpreproc_dsp_set_lvnv);
+
diff --git a/arch/arm/mach-msm/qdsp5v2/aux_pcm.c b/arch/arm/mach-msm/qdsp5v2/aux_pcm.c
new file mode 100644
index 0000000..712766d
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/aux_pcm.c
@@ -0,0 +1,280 @@
+/* Copyright (c) 2009-2011, 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/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <mach/qdsp5v2/aux_pcm.h>
+#include <mach/gpio.h>
+#include <linux/delay.h>
+#include <mach/debug_mm.h>
+
+/*----------------------------------------------------------------------------
+ * Preprocessor Definitions and Constants
+ * -------------------------------------------------------------------------*/
+
+/* define offset of registers here, may put them into platform data */
+#define AUX_CODEC_CTL_OFFSET 0x00
+#define PCM_PATH_CTL_OFFSET 0x04
+#define AUX_CODEC_CTL_OUT_OFFSET 0x08
+
+/* define some bit values in PCM_PATH_CTL register */
+#define PCM_PATH_CTL__ADSP_CTL_EN_BMSK 0x8
+
+/* mask and shift */
+#define AUX_CODEC_CTL_ADSP_CODEC_CTL_EN_BMSK 0x800
+#define AUX_CODEC_CTL_PCM_SYNC_LONG_BMSK 0x400
+#define AUX_CODEC_CTL_PCM_SYNC_SHORT_BMSK 0x200
+#define AUX_CODEC_CTL_I2S_SAMPLE_CLK_SRC_BMSK 0x80
+#define AUX_CODEC_CTL_I2S_SAMPLE_CLK_MODE_BMSK 0x40
+#define AUX_CODEC_CTL_I2S_RX_MODE_BMSK 0x20
+#define AUX_CODEC_CTL_I2S_CLK_MODE_BMSK 0x10
+#define AUX_CODEC_CTL_AUX_PCM_MODE_BMSK 0x0b
+#define AUX_CODEC_CTL_AUX_CODEC_MODE_BMSK 0x02
+
+/* AUX PCM MODE */
+#define MASTER_PRIM_PCM_SHORT 0
+#define MASTER_AUX_PCM_LONG 1
+#define SLAVE_PRIM_PCM_SHORT 2
+
+struct aux_pcm_state {
+	void __iomem *aux_pcm_base;  /* configure aux pcm through Scorpion */
+	int     dout;
+	int     din;
+	int     syncout;
+	int     clkin_a;
+};
+
+static struct aux_pcm_state the_aux_pcm_state;
+
+static void __iomem *get_base_addr(struct aux_pcm_state *aux_pcm)
+{
+	return aux_pcm->aux_pcm_base;
+}
+
+/* Set who control aux pcm : adsp or MSM */
+void aux_codec_adsp_codec_ctl_en(bool msm_adsp_en)
+{
+	void __iomem *baddr = get_base_addr(&the_aux_pcm_state);
+	uint32_t val;
+
+	if (!IS_ERR(baddr)) {
+		val = readl(baddr + AUX_CODEC_CTL_OFFSET);
+		if (msm_adsp_en) { /* adsp */
+			writel(
+			((val & ~AUX_CODEC_CTL_ADSP_CODEC_CTL_EN_BMSK) |
+			AUX_CODEC_CTL__ADSP_CODEC_CTL_EN__ADSP_V),
+			baddr + AUX_CODEC_CTL_OFFSET);
+		} else { /* MSM */
+			writel(
+			((val & ~AUX_CODEC_CTL_ADSP_CODEC_CTL_EN_BMSK) |
+			AUX_CODEC_CTL__ADSP_CODEC_CTL_EN__MSM_V),
+			baddr + AUX_CODEC_CTL_OFFSET);
+		}
+	}
+	mb();
+}
+
+/* Set who control aux pcm path: adsp or MSM */
+void aux_codec_pcm_path_ctl_en(bool msm_adsp_en)
+{
+	void __iomem *baddr = get_base_addr(&the_aux_pcm_state);
+	uint32_t val;
+
+	 if (!IS_ERR(baddr)) {
+		val = readl(baddr + PCM_PATH_CTL_OFFSET);
+		if (msm_adsp_en) { /* adsp */
+			writel(
+			((val & ~PCM_PATH_CTL__ADSP_CTL_EN_BMSK) |
+			PCM_PATH_CTL__ADSP_CTL_EN__ADSP_V),
+			baddr + PCM_PATH_CTL_OFFSET);
+		} else { /* MSM */
+			writel(
+			((val & ~PCM_PATH_CTL__ADSP_CTL_EN_BMSK) |
+			PCM_PATH_CTL__ADSP_CTL_EN__MSM_V),
+			baddr + PCM_PATH_CTL_OFFSET);
+		}
+	}
+	mb();
+	return;
+}
+EXPORT_SYMBOL(aux_codec_pcm_path_ctl_en);
+
+int aux_pcm_gpios_request(void)
+{
+	int rc = 0;
+
+	MM_DBG("aux_pcm_gpios_request\n");
+	rc = gpio_request(the_aux_pcm_state.dout, "AUX PCM DOUT");
+	if (rc) {
+		MM_ERR("GPIO request for AUX PCM DOUT failed\n");
+		return rc;
+	}
+
+	rc = gpio_request(the_aux_pcm_state.din, "AUX PCM DIN");
+	if (rc) {
+		MM_ERR("GPIO request for AUX PCM DIN failed\n");
+		gpio_free(the_aux_pcm_state.dout);
+		return rc;
+	}
+
+	rc = gpio_request(the_aux_pcm_state.syncout, "AUX PCM SYNC OUT");
+	if (rc) {
+		MM_ERR("GPIO request for AUX PCM SYNC OUT failed\n");
+		gpio_free(the_aux_pcm_state.dout);
+		gpio_free(the_aux_pcm_state.din);
+		return rc;
+	}
+
+	rc = gpio_request(the_aux_pcm_state.clkin_a, "AUX PCM CLKIN A");
+	if (rc) {
+		MM_ERR("GPIO request for AUX PCM CLKIN A failed\n");
+		gpio_free(the_aux_pcm_state.dout);
+		gpio_free(the_aux_pcm_state.din);
+		gpio_free(the_aux_pcm_state.syncout);
+		return rc;
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(aux_pcm_gpios_request);
+
+
+void aux_pcm_gpios_free(void)
+{
+	MM_DBG(" aux_pcm_gpios_free \n");
+
+	/*
+	 * Feed silence frames before close to prevent buzzing sound in BT at
+	 * call end. This fix is applicable only to Marimba BT.
+	 */
+	gpio_tlmm_config(GPIO_CFG(the_aux_pcm_state.dout, 0, GPIO_CFG_OUTPUT,
+		GPIO_CFG_NO_PULL, GPIO_CFG_2MA), GPIO_CFG_ENABLE);
+	gpio_set_value(the_aux_pcm_state.dout, 0);
+	msleep(20);
+	gpio_tlmm_config(GPIO_CFG(the_aux_pcm_state.dout, 1, GPIO_CFG_OUTPUT,
+		GPIO_CFG_NO_PULL, GPIO_CFG_2MA), GPIO_CFG_ENABLE);
+
+	gpio_free(the_aux_pcm_state.dout);
+	gpio_free(the_aux_pcm_state.din);
+	gpio_free(the_aux_pcm_state.syncout);
+	gpio_free(the_aux_pcm_state.clkin_a);
+}
+EXPORT_SYMBOL(aux_pcm_gpios_free);
+
+
+static int get_aux_pcm_gpios(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct resource         *res;
+
+	/* Claim all of the GPIOs. */
+	res = platform_get_resource_byname(pdev, IORESOURCE_IO,
+					"aux_pcm_dout");
+	if  (!res) {
+		MM_ERR("%s: failed to get gpio AUX PCM DOUT\n", __func__);
+		return -ENODEV;
+	}
+
+	the_aux_pcm_state.dout = res->start;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IO,
+					"aux_pcm_din");
+	if  (!res) {
+		MM_ERR("%s: failed to get gpio AUX PCM DIN\n", __func__);
+		return -ENODEV;
+	}
+
+	the_aux_pcm_state.din = res->start;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IO,
+					"aux_pcm_syncout");
+	if  (!res) {
+		MM_ERR("%s: failed to get gpio AUX PCM SYNC OUT\n", __func__);
+		return -ENODEV;
+	}
+
+	the_aux_pcm_state.syncout = res->start;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IO,
+					"aux_pcm_clkin_a");
+	if  (!res) {
+		MM_ERR("%s: failed to get gpio AUX PCM CLKIN A\n", __func__);
+		return -ENODEV;
+	}
+
+	the_aux_pcm_state.clkin_a = res->start;
+
+	return rc;
+}
+static int aux_pcm_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct resource *mem_src;
+
+	MM_DBG("aux_pcm_probe \n");
+	mem_src = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						"aux_codec_reg_addr");
+	if (!mem_src) {
+		rc = -ENODEV;
+		goto done;
+	}
+
+	the_aux_pcm_state.aux_pcm_base = ioremap(mem_src->start,
+		(mem_src->end - mem_src->start) + 1);
+	if (!the_aux_pcm_state.aux_pcm_base) {
+		rc = -ENOMEM;
+		goto done;
+	}
+	rc = get_aux_pcm_gpios(pdev);
+	if (rc) {
+		MM_ERR("GPIO configuration failed\n");
+		rc = -ENODEV;
+	}
+
+done:	return rc;
+
+}
+
+static int aux_pcm_remove(struct platform_device *pdev)
+{
+	iounmap(the_aux_pcm_state.aux_pcm_base);
+	return 0;
+}
+
+static struct platform_driver aux_pcm_driver = {
+	.probe = aux_pcm_probe,
+	.remove = aux_pcm_remove,
+	.driver = {
+		.name = "msm_aux_pcm",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init aux_pcm_init(void)
+{
+
+	return platform_driver_register(&aux_pcm_driver);
+}
+
+static void __exit aux_pcm_exit(void)
+{
+	platform_driver_unregister(&aux_pcm_driver);
+}
+
+module_init(aux_pcm_init);
+module_exit(aux_pcm_exit);
+
+MODULE_DESCRIPTION("MSM AUX PCM driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5v2/lpa.c b/arch/arm/mach-msm/qdsp5v2/lpa.c
new file mode 100644
index 0000000..c4e0fee
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/lpa.c
@@ -0,0 +1,608 @@
+/* Copyright (c) 2009-2011, 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/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <mach/qdsp5v2/lpa.h>
+#include <mach/qdsp5v2/lpa_hw.h>
+#include <mach/qdsp5v2/msm_lpa.h>
+#include <mach/debug_mm.h>
+
+#define LPA_REG_WRITEL(drv, val, reg)  writel(val, drv->baseaddr + reg)
+#define LPA_REG_READL(drv, reg)  readl(drv->baseaddr + reg)
+
+/* bit 2:0 is reserved because watermarks have to be 64-bit aligned */
+#define LLB_WATERMARK_VAL_MASK 0x00000003
+
+#define LPA_STATUS_SBUF_EN 0x01
+
+struct lpa_drv {
+	void __iomem *baseaddr;
+	u32 obuf_hlb_size;
+	u32 dsp_proc_id;
+	u32 app_proc_id;
+	struct lpa_mem_config nosb_config;
+	struct lpa_mem_config sb_config;
+	u32 status;
+	u32 watermark_bytes;
+	u32 watermark_aheadtime;
+	u32 sample_boundary;
+};
+
+struct lpa_state {
+	struct lpa_drv lpa_drv; /* One instance for now */
+	u32 assigned;
+	struct mutex lpa_lock;
+};
+
+struct lpa_state the_lpa_state;
+
+static void lpa_enable_codec(struct lpa_drv *lpa, bool enable)
+{
+	u32 val;
+
+	val = LPA_REG_READL(lpa, LPA_OBUF_CODEC);
+	val = enable ? (val | LPA_OBUF_CODEC_CODEC_INTF_EN_BMSK) :
+		(val & ~LPA_OBUF_CODEC_CODEC_INTF_EN_BMSK);
+	val |= LPA_OBUF_CODEC_LOAD_BMSK;
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_CODEC);
+	mb();
+}
+
+static void lpa_reset(struct lpa_drv *lpa)
+{
+	u32 status;
+	struct clk *adsp_clk;
+	/* Need to make sure not disable clock while other device is enabled */
+	adsp_clk = clk_get(NULL, "adsp_clk");
+	if (!adsp_clk) {
+		MM_ERR("failed to get adsp clk\n");
+		goto error;
+	}
+	clk_enable(adsp_clk);
+	lpa_enable_codec(lpa, 0);
+	LPA_REG_WRITEL(lpa, (LPA_OBUF_RESETS_MISR_RESET |
+		LPA_OBUF_RESETS_OVERALL_RESET), LPA_OBUF_RESETS);
+	do {
+		status = LPA_REG_READL(lpa, LPA_OBUF_STATUS);
+	} while (!(status & LPA_OBUF_STATUS_RESET_DONE));
+
+	LPA_REG_WRITEL(lpa, LPA_OBUF_ACK_RESET_DONE_BMSK, LPA_OBUF_ACK);
+	mb();
+	clk_disable(adsp_clk);
+	clk_put(adsp_clk);
+error:
+	return;
+}
+
+static void lpa_config_hlb_addr(struct lpa_drv *lpa)
+{
+	u32 val, min_addr = 0, max_addr = min_addr + lpa->obuf_hlb_size;
+
+	val = (min_addr & LPA_OBUF_HLB_MIN_ADDR_SEG_BMSK) |
+	LPA_OBUF_HLB_MIN_ADDR_LOAD_BMSK;
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_HLB_MIN_ADDR);
+	val = max_addr & LPA_OBUF_HLB_MAX_ADDR_SEG_BMSK;
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_HLB_MAX_ADDR);
+}
+
+static void lpa_powerup_mem_bank(struct lpa_drv *lpa,
+	struct lpa_mem_bank_select *bank)
+{
+	u32 status, val;
+
+	status = LPA_REG_READL(lpa, LPA_OBUF_MEMORY_CONTROL);
+	val = ((*((u32 *) bank)) << LPA_OBUF_MEM_CTL_PWRUP_SHFT) &
+	LPA_OBUF_MEM_CTL_PWRUP_BMSK;
+	val |= status;
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_MEMORY_CONTROL);
+}
+
+static void lpa_enable_interrupt(struct lpa_drv *lpa, u32 proc_id)
+{
+	u32 val;
+
+	proc_id &= LPA_OBUF_INTR_EN_BMSK;
+	val = 0x1 << proc_id;
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_INTR_ENABLE);
+}
+
+static void lpa_config_llb_addr(struct lpa_drv *lpa, u32 min_addr, u32 max_addr)
+{
+	u32 val;
+
+	val = (min_addr & LPA_OBUF_LLB_MIN_ADDR_SEG_BMSK) |
+	LPA_OBUF_LLB_MIN_ADDR_LOAD_BMSK;
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_LLB_MIN_ADDR);
+	val = max_addr & LPA_OBUF_LLB_MAX_ADDR_SEG_BMSK;
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_LLB_MAX_ADDR);
+}
+
+static void lpa_config_sb_addr(struct lpa_drv *lpa, u32 min_addr, u32 max_addr)
+{
+	u32 val;
+
+	val = (min_addr & LPA_OBUF_SB_MIN_ADDR_SEG_BMSK) |
+	LPA_OBUF_SB_MIN_ADDR_LOAD_BMSK;
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_SB_MIN_ADDR);
+	val = max_addr & LPA_OBUF_SB_MAX_ADDR_SEG_BMSK;
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_SB_MAX_ADDR);
+}
+
+static void lpa_switch_sb(struct lpa_drv *lpa)
+{
+	if (lpa->status & LPA_STATUS_SBUF_EN) {
+		lpa_config_llb_addr(lpa, lpa->sb_config.llb_min_addr,
+		lpa->sb_config.llb_max_addr);
+		lpa_config_sb_addr(lpa, lpa->sb_config.sb_min_addr,
+		lpa->sb_config.sb_max_addr);
+	} else {
+		lpa_config_llb_addr(lpa, lpa->nosb_config.llb_min_addr,
+		lpa->nosb_config.llb_max_addr);
+		lpa_config_sb_addr(lpa, lpa->nosb_config.sb_min_addr,
+		lpa->nosb_config.sb_max_addr);
+	}
+}
+
+static u8 lpa_req_wmark_id(struct lpa_drv *lpa)
+{
+  return (u8) (LPA_REG_READL(lpa, LPA_OBUF_WMARK_ASSIGN) &
+	LPA_OBUF_WMARK_ASSIGN_BMSK);
+}
+
+static void lpa_enable_llb_wmark(struct lpa_drv *lpa, u32 wmark_ctrl,
+	u32 wmark_id, u32 cpu_id)
+{
+	u32 val;
+
+	wmark_id = (wmark_id > 3) ? 0 : wmark_id;
+	val = LPA_REG_READL(lpa, LPA_OBUF_WMARK_n_LLB_ADDR(wmark_id));
+	val &= ~LPA_OBUF_LLB_WMARK_CTRL_BMSK;
+	val &= ~LPA_OBUF_LLB_WMARK_MAP_BMSK;
+	val |= (wmark_ctrl << LPA_OBUF_LLB_WMARK_CTRL_SHFT) &
+	LPA_OBUF_LLB_WMARK_CTRL_BMSK;
+	val |= (cpu_id << LPA_OBUF_LLB_WMARK_MAP_SHFT) &
+	LPA_OBUF_LLB_WMARK_MAP_BMSK;
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_WMARK_n_LLB_ADDR(wmark_id));
+}
+
+static void lpa_enable_sb_wmark(struct lpa_drv *lpa, u32 wmark_ctrl,
+	u32 cpu_id)
+{
+	u32 val;
+
+	val = LPA_REG_READL(lpa, LPA_OBUF_WMARK_SB);
+	val &= ~LPA_OBUF_SB_WMARK_CTRL_BMSK;
+	val &= ~LPA_OBUF_SB_WMARK_MAP_BMSK;
+	val |= (wmark_ctrl << LPA_OBUF_SB_WMARK_CTRL_SHFT) &
+	LPA_OBUF_SB_WMARK_CTRL_BMSK;
+	val |= (cpu_id << LPA_OBUF_SB_WMARK_MAP_SHFT) &
+	LPA_OBUF_SB_WMARK_MAP_BMSK;
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_WMARK_SB);
+}
+static void lpa_enable_hlb_wmark(struct lpa_drv *lpa, u32 wmark_ctrl,
+	u32 cpu_id)
+{
+	u32 val;
+
+	val = LPA_REG_READL(lpa, LPA_OBUF_WMARK_HLB);
+	val &= ~LPA_OBUF_HLB_WMARK_CTRL_BMSK;
+	val &= ~LPA_OBUF_HLB_WMARK_MAP_BMSK;
+	val |= (wmark_ctrl << LPA_OBUF_HLB_WMARK_CTRL_SHFT) &
+	LPA_OBUF_HLB_WMARK_CTRL_BMSK;
+	val |= (cpu_id << LPA_OBUF_HLB_WMARK_MAP_SHFT) &
+	LPA_OBUF_HLB_WMARK_MAP_BMSK;
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_WMARK_HLB);
+}
+
+static void lpa_enable_utc(struct lpa_drv *lpa, bool enable, u32 cpu_id)
+{
+	u32 val;
+
+	val = (cpu_id << LPA_OBUF_UTC_CONFIG_MAP_SHFT) &
+	LPA_OBUF_UTC_CONFIG_MAP_BMSK;
+	enable = (enable ? 1 : 0);
+	val = (enable << LPA_OBUF_UTC_CONFIG_EN_SHFT) &
+	LPA_OBUF_UTC_CONFIG_EN_BMSK;
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_UTC_CONFIG);
+}
+
+static void lpa_enable_mixing(struct lpa_drv *lpa, bool enable)
+{
+	u32 val;
+
+	val = LPA_REG_READL(lpa, LPA_OBUF_CONTROL);
+	val = (enable ? val | LPA_OBUF_CONTROL_LLB_EN_BMSK :
+		val & ~LPA_OBUF_CONTROL_LLB_EN_BMSK);
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_CONTROL);
+}
+
+static void lpa_enable_mixer_saturation(struct lpa_drv *lpa, u32 buf_id,
+	bool enable)
+{
+	u32 val;
+
+	val = LPA_REG_READL(lpa, LPA_OBUF_CONTROL);
+
+	switch (buf_id) {
+	case LPA_BUF_ID_LLB:
+		val = enable ? (val | LPA_OBUF_CONTROL_LLB_SAT_EN_BMSK) :
+		(val & ~LPA_OBUF_CONTROL_LLB_SAT_EN_BMSK);
+		break;
+
+	case LPA_BUF_ID_SB:
+		val = enable ? (val | LPA_OBUF_CONTROL_SB_SAT_EN_BMSK) :
+		(val & ~LPA_OBUF_CONTROL_SB_SAT_EN_BMSK);
+		break;
+	}
+
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_CONTROL);
+}
+
+static void lpa_enable_obuf(struct lpa_drv *lpa, u32 buf_id, bool enable)
+{
+	u32 val;
+
+	val = LPA_REG_READL(lpa, LPA_OBUF_CONTROL);
+
+	switch (buf_id) {
+	case LPA_BUF_ID_HLB:
+		val = enable ? (val | LPA_OBUF_CONTROL_HLB_EN_BMSK) :
+			(val & ~LPA_OBUF_CONTROL_HLB_EN_BMSK);
+		break;
+
+	case LPA_BUF_ID_LLB:
+		val = enable ? (val | LPA_OBUF_CONTROL_LLB_EN_BMSK) :
+		(val & ~LPA_OBUF_CONTROL_LLB_EN_BMSK);
+		break;
+
+	case LPA_BUF_ID_SB:
+		val = enable ? (val | LPA_OBUF_CONTROL_SB_EN_BMSK) :
+			(val & ~LPA_OBUF_CONTROL_SB_EN_BMSK);
+		break;
+	}
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_CONTROL);
+}
+
+struct lpa_drv *lpa_get(void)
+{
+	struct lpa_mem_bank_select mem_bank;
+	struct lpa_drv *ret_lpa = &the_lpa_state.lpa_drv;
+
+	mutex_lock(&the_lpa_state.lpa_lock);
+	if (the_lpa_state.assigned) {
+		MM_ERR("LPA HW accupied\n");
+		ret_lpa = ERR_PTR(-EBUSY);
+		goto error;
+	}
+	/* perform initialization */
+	lpa_reset(ret_lpa);
+	/* Config adec param */
+	/* Initialize LLB/SB min/max address */
+	lpa_switch_sb(ret_lpa);
+	/* Config HLB minx/max address */
+	lpa_config_hlb_addr(ret_lpa);
+
+	/* Power up all memory bank for now */
+	mem_bank.b0 = 1;
+	mem_bank.b1 = 1;
+	mem_bank.b2 = 1;
+	mem_bank.b3 = 1;
+	mem_bank.b4 = 1;
+	mem_bank.b5 = 1;
+	mem_bank.b6 = 1;
+	mem_bank.b7 = 1;
+	mem_bank.b8 = 1;
+	mem_bank.b9 = 1;
+	mem_bank.b10 = 1;
+	mem_bank.llb = 1;
+	lpa_powerup_mem_bank(ret_lpa, &mem_bank);
+
+	while
+	(lpa_req_wmark_id(ret_lpa) != LPA_OBUF_WMARK_ASSIGN_DONE);
+
+	lpa_enable_llb_wmark(ret_lpa, LPA_WMARK_CTL_DISABLED, 0,
+	ret_lpa->dsp_proc_id);
+	lpa_enable_llb_wmark(ret_lpa, LPA_WMARK_CTL_DISABLED, 1,
+	ret_lpa->dsp_proc_id);
+	lpa_enable_llb_wmark(ret_lpa, LPA_WMARK_CTL_DISABLED, 2,
+	ret_lpa->app_proc_id);
+	lpa_enable_llb_wmark(ret_lpa, LPA_WMARK_CTL_DISABLED, 3,
+	ret_lpa->app_proc_id);
+	lpa_enable_hlb_wmark(ret_lpa, LPA_WMARK_CTL_DISABLED,
+	ret_lpa->dsp_proc_id);
+	lpa_enable_sb_wmark(ret_lpa, LPA_WMARK_CTL_DISABLED,
+	ret_lpa->dsp_proc_id);
+	lpa_enable_utc(ret_lpa, 0, LPA_OBUF_UTC_CONFIG_NO_INTR);
+
+	lpa_enable_mixing(ret_lpa, 1);
+	lpa_enable_mixer_saturation(ret_lpa, LPA_BUF_ID_LLB, 1);
+
+	lpa_enable_obuf(ret_lpa, LPA_BUF_ID_HLB, 0);
+	lpa_enable_obuf(ret_lpa, LPA_BUF_ID_LLB, 1);
+	if (ret_lpa->status & LPA_STATUS_SBUF_EN) {
+		lpa_enable_mixer_saturation(ret_lpa, LPA_BUF_ID_SB, 1);
+		lpa_enable_obuf(ret_lpa, LPA_BUF_ID_SB, 1);
+	}
+
+	lpa_enable_interrupt(ret_lpa, ret_lpa->dsp_proc_id);
+	mb();
+	the_lpa_state.assigned++;
+error:
+	mutex_unlock(&the_lpa_state.lpa_lock);
+	return ret_lpa;
+}
+EXPORT_SYMBOL(lpa_get);
+
+void lpa_put(struct lpa_drv *lpa)
+{
+
+	mutex_lock(&the_lpa_state.lpa_lock);
+	if (!lpa || &the_lpa_state.lpa_drv != lpa) {
+		MM_ERR("invalid arg\n");
+		goto error;
+	}
+	/* Deinitialize */
+	the_lpa_state.assigned--;
+error:
+	mutex_unlock(&the_lpa_state.lpa_lock);
+}
+EXPORT_SYMBOL(lpa_put);
+
+int lpa_cmd_codec_config(struct lpa_drv *lpa,
+	struct lpa_codec_config *config_ptr)
+{
+	u32 sample_rate;
+	u32 num_channels;
+	u32 width;
+	u32 val = 0;
+
+	if (!lpa || !config_ptr) {
+		MM_ERR("invalid parameters\n");
+		return -EINVAL;
+	}
+
+	switch (config_ptr->num_channels) {
+	case 8:
+		num_channels = LPA_NUM_CHAN_7P1;
+		break;
+	case 6:
+		num_channels = LPA_NUM_CHAN_5P1;
+		break;
+	case 4:
+		num_channels = LPA_NUM_CHAN_4_CHANNEL;
+		break;
+	case 2:
+		num_channels = LPA_NUM_CHAN_STEREO;
+		break;
+	case 1:
+		num_channels = LPA_NUM_CHAN_MONO;
+		break;
+	default:
+		MM_ERR("unsupported number of channel\n");
+		goto error;
+	}
+	val |= (num_channels << LPA_OBUF_CODEC_NUM_CHAN_SHFT) &
+	LPA_OBUF_CODEC_NUM_CHAN_BMSK;
+
+	switch (config_ptr->sample_rate) {
+	case 96000:
+		sample_rate = LPA_SAMPLE_RATE_96KHZ;
+		break;
+	case 64000:
+		sample_rate = LPA_SAMPLE_RATE_64KHZ;
+		break;
+	case 48000:
+		sample_rate = LPA_SAMPLE_RATE_48KHZ;
+		break;
+	case 44100:
+		sample_rate = LPA_SAMPLE_RATE_44P1KHZ;
+		break;
+	case 32000:
+		sample_rate = LPA_SAMPLE_RATE_32KHZ;
+		break;
+	case 22050:
+		sample_rate = LPA_SAMPLE_RATE_22P05KHZ;
+		break;
+	case 16000:
+		sample_rate = LPA_SAMPLE_RATE_16KHZ;
+		break;
+	case 11025:
+		sample_rate = LPA_SAMPLE_RATE_11P025KHZ;
+		break;
+	case 8000:
+		sample_rate = LPA_SAMPLE_RATE_8KHZ;
+		break;
+	default:
+		MM_ERR("unsupported sample rate \n");
+		goto error;
+	}
+	val |= (sample_rate << LPA_OBUF_CODEC_SAMP_SHFT) &
+		LPA_OBUF_CODEC_SAMP_BMSK;
+	switch (config_ptr->sample_width) {
+	case 32:
+		width = LPA_BITS_PER_CHAN_32BITS;
+		break;
+	case 24:
+		width = LPA_BITS_PER_CHAN_24BITS;
+		break;
+	case 16:
+		width = LPA_BITS_PER_CHAN_16BITS;
+		break;
+	default:
+		MM_ERR("unsupported sample width \n");
+		goto error;
+	}
+	val |= (width << LPA_OBUF_CODEC_BITS_PER_CHAN_SHFT) &
+		LPA_OBUF_CODEC_BITS_PER_CHAN_BMSK;
+
+	val |= LPA_OBUF_CODEC_LOAD_BMSK;
+	val |= (config_ptr->output_interface << LPA_OBUF_CODEC_INTF_SHFT) &
+	LPA_OBUF_CODEC_INTF_BMSK;
+
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_CODEC);
+	mb();
+
+	return 0;
+error:
+	return -EINVAL;
+}
+EXPORT_SYMBOL(lpa_cmd_codec_config);
+
+static int lpa_check_llb_clear(struct lpa_drv *lpa)
+{
+	u32 val;
+	val = LPA_REG_READL(lpa, LPA_OBUF_STATUS);
+
+	return !(val & LPA_OBUF_STATUS_LLB_CLR_BMSK);
+}
+
+static void lpa_clear_llb(struct lpa_drv *lpa)
+{
+	u32 val;
+
+	val = LPA_REG_READL(lpa, LPA_OBUF_CONTROL);
+	LPA_REG_WRITEL(lpa, (val | LPA_OBUF_CONTROL_LLB_CLR_CMD_BMSK),
+	LPA_OBUF_CONTROL);
+	lpa_enable_obuf(lpa, LPA_BUF_ID_LLB, 0);
+
+	while (!lpa_check_llb_clear(lpa))
+		udelay(100);
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_CONTROL);
+}
+
+int lpa_cmd_enable_codec(struct lpa_drv *lpa, bool enable)
+{
+	u32 val;
+	struct lpa_mem_bank_select mem_bank;
+
+	MM_DBG(" %s\n", (enable ? "enable" : "disable"));
+
+	if (!lpa)
+		return -EINVAL;
+
+	val = LPA_REG_READL(lpa, LPA_OBUF_CODEC);
+
+	if (enable) {
+		if (val & LPA_OBUF_CODEC_CODEC_INTF_EN_BMSK)
+			return -EBUSY;
+		/* Power up all memory bank for now */
+		mem_bank.b0 = 1;
+		mem_bank.b1 = 1;
+		mem_bank.b2 = 1;
+		mem_bank.b3 = 1;
+		mem_bank.b4 = 1;
+		mem_bank.b5 = 1;
+		mem_bank.b6 = 1;
+		mem_bank.b7 = 1;
+		mem_bank.b8 = 1;
+		mem_bank.b9 = 1;
+		mem_bank.b10 = 1;
+		mem_bank.llb = 1;
+		lpa_powerup_mem_bank(lpa, &mem_bank);
+
+		/*clear LLB*/
+		lpa_clear_llb(lpa);
+
+		lpa_enable_codec(lpa, 1);
+		MM_DBG("LPA codec is enabled\n");
+	} else {
+		if (val & LPA_OBUF_CODEC_CODEC_INTF_EN_BMSK) {
+			lpa_enable_codec(lpa, 0);
+			MM_DBG("LPA codec is disabled\n");
+		} else
+			MM_ERR("LPA codec is already disable\n");
+	}
+	mb();
+	return 0;
+}
+EXPORT_SYMBOL(lpa_cmd_enable_codec);
+
+static int lpa_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct resource *mem_src;
+	struct msm_lpa_platform_data *pdata;
+
+	MM_INFO("lpa probe\n");
+
+	if (!pdev || !pdev->dev.platform_data) {
+		MM_ERR("no plaform data\n");
+		rc = -ENODEV;
+		goto error;
+	}
+
+	mem_src = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpa");
+	if (!mem_src) {
+		MM_ERR("LPA base address undefined\n");
+		rc = -ENODEV;
+		goto error;
+	}
+
+	pdata = pdev->dev.platform_data;
+	the_lpa_state.lpa_drv.baseaddr = ioremap(mem_src->start,
+	(mem_src->end - mem_src->start) + 1);
+	if (!the_lpa_state.lpa_drv.baseaddr) {
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	the_lpa_state.lpa_drv.obuf_hlb_size = pdata->obuf_hlb_size;
+	the_lpa_state.lpa_drv.dsp_proc_id = pdata->dsp_proc_id;
+	the_lpa_state.lpa_drv.app_proc_id = pdata->app_proc_id;
+	the_lpa_state.lpa_drv.nosb_config = pdata->nosb_config;
+	the_lpa_state.lpa_drv.sb_config = pdata->sb_config;
+	/* default to enable summing buffer */
+	the_lpa_state.lpa_drv.status = LPA_STATUS_SBUF_EN;
+
+error:
+	return rc;
+
+}
+
+static int lpa_remove(struct platform_device *pdev)
+{
+	iounmap(the_lpa_state.lpa_drv.baseaddr);
+	return 0;
+}
+
+static struct platform_driver lpa_driver = {
+	.probe = lpa_probe,
+	.remove = lpa_remove,
+	.driver = {
+		.name = "lpa",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init lpa_init(void)
+{
+	the_lpa_state.assigned = 0;
+	mutex_init(&the_lpa_state.lpa_lock);
+	return platform_driver_register(&lpa_driver);
+}
+
+static void __exit lpa_exit(void)
+{
+	platform_driver_unregister(&lpa_driver);
+}
+
+module_init(lpa_init);
+module_exit(lpa_exit);
+
+MODULE_DESCRIPTION("MSM LPA driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5v2/mi2s.c b/arch/arm/mach-msm/qdsp5v2/mi2s.c
new file mode 100644
index 0000000..e38f164
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/mi2s.c
@@ -0,0 +1,885 @@
+/* Copyright (c) 2009,2011 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/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+
+#include <mach/qdsp5v2/mi2s.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+
+#define DEBUG
+#ifdef DEBUG
+#define dprintk(format, arg...) \
+printk(KERN_DEBUG format, ## arg)
+#else
+#define dprintk(format, arg...) do {} while (0)
+#endif
+
+/*----------------------------------------------------------------------------
+ * Preprocessor Definitions and Constants
+ * -------------------------------------------------------------------------*/
+
+/* Device Types */
+#define HDMI 0
+#define CODEC_RX 1
+#define CODEC_TX 2
+
+/* Static offset for now. If different target have different
+ * offset, update to platform data model
+ */
+#define MI2S_RESET_OFFSET   0x0
+#define MI2S_MODE_OFFSET    0x4
+#define MI2S_TX_MODE_OFFSET 0x8
+#define MI2S_RX_MODE_OFFSET 0xc
+
+#define MI2S_SD_N_EN_MASK 0xF0
+#define MI2S_TX_RX_N_MASK 0x0F
+
+#define MI2S_RESET__MI2S_RESET__RESET  0x1
+#define MI2S_RESET__MI2S_RESET__ACTIVE 0x0
+#define MI2S_MODE__MI2S_MASTER__MASTER 0x1
+#define MI2S_MODE__MI2S_MASTER__SLAVE  0x0
+#define MI2S_MODE__MI2S_TX_RX_WORD_TYPE__16_BIT 0x1
+#define MI2S_MODE__MI2S_TX_RX_WORD_TYPE__24_BIT 0x2
+#define MI2S_MODE__MI2S_TX_RX_WORD_TYPE__32_BIT 0x3
+#define MI2S_TX_MODE__MI2S_TX_CODEC_16_MONO_MODE__RAW 0x0
+#define MI2S_TX_MODE__MI2S_TX_CODEC_16_MONO_MODE__PACKED 0x1
+#define MI2S_TX_MODE__MI2S_TX_STEREO_MODE__MONO_SAMPLE   0x0
+#define MI2S_TX_MODE__MI2S_TX_STEREO_MODE__STEREO_SAMPLE 0x1
+#define MI2S_TX_MODE__MI2S_TX_CH_TYPE__2_CHANNEL 0x0
+#define MI2S_TX_MODE__MI2S_TX_CH_TYPE__4_CHANNEL 0x1
+#define MI2S_TX_MODE__MI2S_TX_CH_TYPE__6_CHANNEL 0x2
+#define MI2S_TX_MODE__MI2S_TX_CH_TYPE__8_CHANNEL 0x3
+#define MI2S_TX_MODE__MI2S_TX_DMA_ACK_SYNCH_EN__SYNC_ENABLE 0x1
+#define MI2S_RX_MODE__MI2S_RX_CODEC_16_MONO_MODE__RAW 0x0
+#define MI2S_RX_MODE__MI2S_RX_CODEC_16_MONO_MODE__PACKED 0x1
+#define MI2S_RX_MODE__MI2S_RX_STEREO_MODE__MONO_SAMPLE   0x0
+#define MI2S_RX_MODE__MI2S_RX_STEREO_MODE__STEREO_SAMPLE 0x1
+#define MI2S_RX_MODE__MI2S_RX_CH_TYPE__2_CH 0x0
+#define MI2S_RX_MODE__MI2S_RX_DMA_ACK_SYNCH_EN__SYNC_ENABLE 0x1
+
+#define HWIO_AUDIO1_MI2S_MODE_MI2S_MASTER_BMSK				0x1000
+#define HWIO_AUDIO1_MI2S_MODE_MI2S_MASTER_SHFT				0xC
+#define HWIO_AUDIO1_MI2S_MODE_MI2S_TX_RX_WORD_TYPE_BMSK  		0x300
+#define HWIO_AUDIO1_MI2S_MODE_MI2S_TX_RX_WORD_TYPE_SHFT  		0x8
+#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_BMSK		0x4
+#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_SHFT		0x2
+#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_P_MONO_BMSK                    0x2
+#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_P_MONO_SHFT                    0x1
+#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_BMSK			0x18
+#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_SHFT			0x3
+#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_4_0_CH_MAP_BMSK			0x80
+#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_4_0_CH_MAP_SHFT			0x7
+#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_2_0_CH_MAP_BMSK			0x60
+#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_2_0_CH_MAP_SHFT			0x5
+#define HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_DMA_ACK_SYNCH_EN_BMSK		0x1
+#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_I2S_LINE_BMSK			0x60
+#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_I2S_LINE_SHFT			0x5
+#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_BMSK		0x4
+#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_SHFT		0x2
+#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CODEC_P_MONO_BMSK              0x2
+#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CODEC_P_MONO_SHFT              0x1
+#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CH_TYPE_BMSK			0x18
+#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CH_TYPE_SHFT			0x3
+#define HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_DMA_ACK_SYNCH_EN_BMSK		0x1
+
+/* Max number of channels */
+#define MAX_NUM_CHANNELS_OUT 8
+#define MAX_NUM_CHANNELS_IN  2
+
+/* Num of SD Lines */
+#define MAX_SD_LINES 4
+
+#define MI2S_SD_0_EN_MAP  0x10
+#define MI2S_SD_1_EN_MAP  0x20
+#define MI2S_SD_2_EN_MAP  0x40
+#define MI2S_SD_3_EN_MAP  0x80
+#define MI2S_SD_0_TX_MAP  0x01
+#define MI2S_SD_1_TX_MAP  0x02
+#define MI2S_SD_2_TX_MAP  0x04
+#define MI2S_SD_3_TX_MAP  0x08
+
+struct mi2s_state {
+	void __iomem *mi2s_hdmi_base;
+	void __iomem *mi2s_rx_base;
+	void __iomem *mi2s_tx_base;
+	struct mutex mutex_lock;
+
+};
+
+static struct mi2s_state the_mi2s_state;
+
+static void __iomem *get_base_addr(struct mi2s_state *mi2s, uint8_t dev_id)
+{
+	switch (dev_id) {
+	case HDMI:
+		return mi2s->mi2s_hdmi_base;
+	case CODEC_RX:
+		return mi2s->mi2s_rx_base;
+	case CODEC_TX:
+		return mi2s->mi2s_tx_base;
+	default:
+		break;
+	}
+	return ERR_PTR(-ENODEV);
+}
+
+static void mi2s_reset(struct mi2s_state *mi2s, uint8_t dev_id)
+{
+	void __iomem *baddr = get_base_addr(mi2s, dev_id);
+	if (!IS_ERR(baddr))
+		writel(MI2S_RESET__MI2S_RESET__RESET,
+		baddr + MI2S_RESET_OFFSET);
+}
+
+static void mi2s_release(struct mi2s_state *mi2s, uint8_t dev_id)
+{
+	void __iomem *baddr = get_base_addr(mi2s, dev_id);
+	if (!IS_ERR(baddr))
+		writel(MI2S_RESET__MI2S_RESET__ACTIVE,
+		baddr + MI2S_RESET_OFFSET);
+}
+
+static void mi2s_master(struct mi2s_state *mi2s, uint8_t dev_id, bool master)
+{
+	void __iomem *baddr = get_base_addr(mi2s, dev_id);
+	uint32_t val;
+	if (!IS_ERR(baddr)) {
+		val = readl(baddr + MI2S_MODE_OFFSET);
+		if (master) {
+			writel(
+			((val & ~HWIO_AUDIO1_MI2S_MODE_MI2S_MASTER_BMSK) |
+			 (MI2S_MODE__MI2S_MASTER__MASTER <<
+			  HWIO_AUDIO1_MI2S_MODE_MI2S_MASTER_SHFT)),
+			baddr + MI2S_MODE_OFFSET);
+		} else {
+			writel(
+			((val & ~HWIO_AUDIO1_MI2S_MODE_MI2S_MASTER_BMSK) |
+			 (MI2S_MODE__MI2S_MASTER__SLAVE <<
+			  HWIO_AUDIO1_MI2S_MODE_MI2S_MASTER_SHFT)),
+			baddr + MI2S_MODE_OFFSET);
+		}
+	}
+}
+
+static void mi2s_set_word_type(struct mi2s_state *mi2s, uint8_t dev_id,
+	uint8_t size)
+{
+	void __iomem *baddr = get_base_addr(mi2s, dev_id);
+	uint32_t val;
+	if (!IS_ERR(baddr)) {
+		val = readl(baddr + MI2S_MODE_OFFSET);
+		switch (size) {
+		case WT_16_BIT:
+			writel(
+			((val &
+			~HWIO_AUDIO1_MI2S_MODE_MI2S_TX_RX_WORD_TYPE_BMSK) |
+			(MI2S_MODE__MI2S_TX_RX_WORD_TYPE__16_BIT <<
+			HWIO_AUDIO1_MI2S_MODE_MI2S_TX_RX_WORD_TYPE_SHFT)),
+			baddr + MI2S_MODE_OFFSET);
+			break;
+		case WT_24_BIT:
+			writel(
+			((val &
+			~HWIO_AUDIO1_MI2S_MODE_MI2S_TX_RX_WORD_TYPE_BMSK) |
+			(MI2S_MODE__MI2S_TX_RX_WORD_TYPE__24_BIT <<
+			HWIO_AUDIO1_MI2S_MODE_MI2S_TX_RX_WORD_TYPE_SHFT)),
+			baddr + MI2S_MODE_OFFSET);
+			break;
+		case WT_32_BIT:
+			writel(
+			((val &
+			~HWIO_AUDIO1_MI2S_MODE_MI2S_TX_RX_WORD_TYPE_BMSK) |
+			(MI2S_MODE__MI2S_TX_RX_WORD_TYPE__32_BIT <<
+			HWIO_AUDIO1_MI2S_MODE_MI2S_TX_RX_WORD_TYPE_SHFT)),
+			baddr + MI2S_MODE_OFFSET);
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+static void mi2s_set_sd(struct mi2s_state *mi2s, uint8_t dev_id, uint8_t sd_map)
+{
+	void __iomem *baddr = get_base_addr(mi2s, dev_id);
+	uint32_t val;
+	if (!IS_ERR(baddr)) {
+		val = readl(baddr + MI2S_MODE_OFFSET) &
+			~(MI2S_SD_N_EN_MASK | MI2S_TX_RX_N_MASK);
+		writel(val | sd_map, baddr + MI2S_MODE_OFFSET);
+	}
+}
+
+static void mi2s_set_output_num_channels(struct mi2s_state *mi2s,
+	uint8_t dev_id, uint8_t channels)
+{
+	void __iomem *baddr = get_base_addr(mi2s, dev_id);
+	uint32_t val;
+	if (!IS_ERR(baddr)) {
+		val = readl(baddr + MI2S_TX_MODE_OFFSET);
+		if (channels == MI2S_CHAN_MONO_RAW) {
+			val = (val &
+			~(HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_BMSK |
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_P_MONO_BMSK)) |
+			((MI2S_TX_MODE__MI2S_TX_STEREO_MODE__MONO_SAMPLE <<
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_SHFT) |
+			(MI2S_TX_MODE__MI2S_TX_CODEC_16_MONO_MODE__RAW <<
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_P_MONO_SHFT));
+		} else if (channels == MI2S_CHAN_MONO_PACKED) {
+			val = (val &
+			~(HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_BMSK |
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_P_MONO_BMSK)) |
+			((MI2S_TX_MODE__MI2S_TX_STEREO_MODE__MONO_SAMPLE <<
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_SHFT) |
+			(MI2S_TX_MODE__MI2S_TX_CODEC_16_MONO_MODE__PACKED <<
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_P_MONO_SHFT));
+		} else if (channels == MI2S_CHAN_STEREO) {
+			val = (val &
+			~(HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_BMSK |
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_BMSK)) |
+			((MI2S_TX_MODE__MI2S_TX_STEREO_MODE__STEREO_SAMPLE <<
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_SHFT) |
+			(MI2S_TX_MODE__MI2S_TX_CH_TYPE__2_CHANNEL <<
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_SHFT));
+		} else if (channels == MI2S_CHAN_4CHANNELS) {
+			val = (val &
+			~(HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_BMSK |
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_BMSK)) |
+			((MI2S_TX_MODE__MI2S_TX_STEREO_MODE__STEREO_SAMPLE <<
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_SHFT) |
+			(MI2S_TX_MODE__MI2S_TX_CH_TYPE__4_CHANNEL <<
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_SHFT));
+		} else if (channels == MI2S_CHAN_6CHANNELS) {
+			val = (val &
+			~(HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_BMSK |
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_BMSK)) |
+			((MI2S_TX_MODE__MI2S_TX_STEREO_MODE__STEREO_SAMPLE <<
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_SHFT) |
+			(MI2S_TX_MODE__MI2S_TX_CH_TYPE__6_CHANNEL <<
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_SHFT));
+		} else if (channels == MI2S_CHAN_8CHANNELS) {
+			val = (val &
+			~(HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_BMSK |
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_BMSK)) |
+			((MI2S_TX_MODE__MI2S_TX_STEREO_MODE__STEREO_SAMPLE <<
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_STEREO_MODE_SHFT) |
+			(MI2S_TX_MODE__MI2S_TX_CH_TYPE__8_CHANNEL <<
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_CH_TYPE_SHFT));
+		}
+		writel(val, baddr + MI2S_TX_MODE_OFFSET);
+	}
+}
+
+static void mi2s_set_output_4ch_map(struct mi2s_state *mi2s, uint8_t dev_id,
+	bool high_low)
+{
+	void __iomem *baddr = get_base_addr(mi2s, dev_id);
+	uint32_t val;
+	if (!IS_ERR(baddr)) {
+		val = readl(baddr + MI2S_TX_MODE_OFFSET);
+		val = (val & ~HWIO_AUDIO1_MI2S_TX_MODE_MI2S_4_0_CH_MAP_BMSK) |
+			(high_low <<
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_4_0_CH_MAP_SHFT);
+		writel(val, baddr + MI2S_TX_MODE_OFFSET);
+	}
+}
+
+static void mi2s_set_output_2ch_map(struct mi2s_state *mi2s, uint8_t dev_id,
+	uint8_t sd_line)
+{
+	void __iomem *baddr = get_base_addr(mi2s, dev_id);
+	uint32_t val;
+
+	if (!IS_ERR(baddr)) {
+		val = readl(baddr + MI2S_TX_MODE_OFFSET);
+		if (sd_line < 4) {
+			val = (val &
+			~HWIO_AUDIO1_MI2S_TX_MODE_MI2S_2_0_CH_MAP_BMSK) |
+			(sd_line <<
+			HWIO_AUDIO1_MI2S_TX_MODE_MI2S_2_0_CH_MAP_SHFT);
+			writel(val, baddr + MI2S_TX_MODE_OFFSET);
+		}
+	}
+}
+
+static void mi2s_set_output_clk_synch(struct mi2s_state *mi2s, uint8_t dev_id)
+{
+	void __iomem *baddr = get_base_addr(mi2s, dev_id);
+	uint32_t val;
+
+	if (!IS_ERR(baddr)) {
+		val = readl(baddr + MI2S_TX_MODE_OFFSET);
+		writel(((val &
+		~HWIO_AUDIO1_MI2S_TX_MODE_MI2S_TX_DMA_ACK_SYNCH_EN_BMSK) |
+		MI2S_TX_MODE__MI2S_TX_DMA_ACK_SYNCH_EN__SYNC_ENABLE),
+		baddr + MI2S_TX_MODE_OFFSET);
+	}
+}
+
+static void mi2s_set_input_sd_line(struct mi2s_state *mi2s, uint8_t dev_id,
+	uint8_t sd_line)
+{
+	void __iomem *baddr = get_base_addr(mi2s, dev_id);
+	uint32_t val;
+
+	if (!IS_ERR(baddr)) {
+		val = readl(baddr + MI2S_RX_MODE_OFFSET);
+		if (sd_line < 4) {
+			val = (val &
+			~HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_I2S_LINE_BMSK) |
+			(sd_line <<
+			HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_I2S_LINE_SHFT);
+			writel(val, baddr + MI2S_RX_MODE_OFFSET);
+		}
+	}
+}
+
+static void mi2s_set_input_num_channels(struct mi2s_state *mi2s, uint8_t dev_id,
+	uint8_t channels)
+{
+	void __iomem *baddr = get_base_addr(mi2s, dev_id);
+	uint32_t val;
+
+	if (!IS_ERR(baddr)) {
+		val = readl(baddr + MI2S_RX_MODE_OFFSET);
+		if (channels == MI2S_CHAN_MONO_RAW) {
+			val = (val &
+			~(HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_BMSK |
+			HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CODEC_P_MONO_BMSK)) |
+			((MI2S_RX_MODE__MI2S_RX_STEREO_MODE__MONO_SAMPLE <<
+			HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_SHFT) |
+			(MI2S_RX_MODE__MI2S_RX_CODEC_16_MONO_MODE__RAW <<
+			HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CODEC_P_MONO_SHFT));
+		} else if (channels == MI2S_CHAN_MONO_PACKED) {
+			val = (val &
+			~(HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_BMSK |
+			HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CODEC_P_MONO_BMSK)) |
+			((MI2S_RX_MODE__MI2S_RX_STEREO_MODE__MONO_SAMPLE <<
+			HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_SHFT) |
+			(MI2S_RX_MODE__MI2S_RX_CODEC_16_MONO_MODE__PACKED <<
+			HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CODEC_P_MONO_SHFT));
+		} else if (channels == MI2S_CHAN_STEREO) {
+
+			if (dev_id == HDMI)
+				val = (val &
+			~(HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_BMSK |
+			HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CH_TYPE_BMSK)) |
+			((MI2S_RX_MODE__MI2S_RX_STEREO_MODE__STEREO_SAMPLE <<
+			HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_SHFT) |
+			(MI2S_RX_MODE__MI2S_RX_CH_TYPE__2_CH <<
+			HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CH_TYPE_SHFT));
+
+			else
+				val = (val &
+			~(HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_BMSK |
+			HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CH_TYPE_BMSK)) |
+			((MI2S_RX_MODE__MI2S_RX_STEREO_MODE__STEREO_SAMPLE <<
+			HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_STEREO_MODE_SHFT) |
+			(MI2S_RX_MODE__MI2S_RX_CODEC_16_MONO_MODE__PACKED <<
+			HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CODEC_P_MONO_SHFT) |
+			(MI2S_RX_MODE__MI2S_RX_CH_TYPE__2_CH <<
+			HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_CH_TYPE_SHFT));
+
+
+		}
+		writel(val, baddr + MI2S_RX_MODE_OFFSET);
+	}
+}
+
+static void mi2s_set_input_clk_synch(struct mi2s_state *mi2s, uint8_t dev_id)
+{
+	void __iomem *baddr = get_base_addr(mi2s, dev_id);
+	uint32_t val;
+
+	if (!IS_ERR(baddr)) {
+		val = readl(baddr + MI2S_RX_MODE_OFFSET);
+		writel(
+		((val &
+		~HWIO_AUDIO1_MI2S_RX_MODE_MI2S_RX_DMA_ACK_SYNCH_EN_BMSK) |
+		MI2S_RX_MODE__MI2S_RX_DMA_ACK_SYNCH_EN__SYNC_ENABLE),
+		baddr + MI2S_RX_MODE_OFFSET);
+	}
+}
+
+
+static u8 num_of_bits_set(u8 sd_line_mask)
+{
+	u8 num_bits_set = 0;
+
+	while (sd_line_mask) {
+
+		if (sd_line_mask & 1)
+			num_bits_set++;
+		sd_line_mask = sd_line_mask >> 1;
+	}
+	return num_bits_set;
+}
+
+
+bool mi2s_set_hdmi_output_path(uint8_t channels, uint8_t size,
+		uint8_t sd_line_mask)
+{
+	bool ret_val = MI2S_TRUE;
+	struct mi2s_state *mi2s = &the_mi2s_state;
+	u8 sd_line, num_of_sd_lines = 0;
+	void __iomem *baddr;
+	uint32_t val;
+
+	pr_debug("%s: channels = %u size = %u sd_line_mask = 0x%x\n", __func__,
+		channels, size, sd_line_mask);
+
+	if ((channels == 0) ||  (channels > MAX_NUM_CHANNELS_OUT) ||
+		((channels != 1) && (channels % 2 != 0))) {
+
+		pr_err("%s: invalid number of channels. channels = %u\n",
+				__func__, channels);
+		return  MI2S_FALSE;
+	}
+
+	sd_line_mask &=  MI2S_SD_LINE_MASK;
+
+	if (!sd_line_mask) {
+		pr_err("%s: Did not set any data lines to use "
+			" sd_line_mask =0x%x\n", __func__, sd_line_mask);
+		return  MI2S_FALSE;
+	}
+
+	mutex_lock(&mi2s->mutex_lock);
+	/* Put device in reset */
+	mi2s_reset(mi2s, HDMI);
+
+	mi2s_master(mi2s, HDMI, 1);
+
+	/* Set word type */
+	if (size <= WT_MAX)
+		mi2s_set_word_type(mi2s, HDMI, size);
+	else
+		ret_val = MI2S_FALSE;
+
+	/* Enable clock crossing synchronization of RD DMA ACK */
+	mi2s_set_output_clk_synch(mi2s, HDMI);
+
+	mi2s_set_output_num_channels(mi2s, HDMI, channels);
+
+	num_of_sd_lines = num_of_bits_set(sd_line_mask);
+	/*Second argument to find_first_bit should be maximum number of
+	bit*/
+
+	sd_line = find_first_bit((unsigned long *)&sd_line_mask,
+			sizeof(sd_line_mask) * 8);
+	pr_debug("sd_line = %d\n", sd_line);
+
+	if (channels == 1) {
+
+		if (num_of_sd_lines != 1) {
+			pr_err("%s: for one channel only one SD lines is"
+				" needed. num_of_sd_lines = %u\n",
+				__func__, num_of_sd_lines);
+
+			ret_val = MI2S_FALSE;
+			goto error;
+		}
+
+		if (sd_line != 0) {
+			pr_err("%s: for one channel tx, need to use SD_0 "
+					"sd_line = %u\n", __func__, sd_line);
+
+			ret_val = MI2S_FALSE;
+			goto error;
+		}
+
+		/* Enable SD line 0 for Tx (only option for
+			 * mono audio)
+		 */
+		mi2s_set_sd(mi2s, HDMI, MI2S_SD_0_EN_MAP | MI2S_SD_0_TX_MAP);
+
+	} else if (channels == 2) {
+
+		if (num_of_sd_lines != 1) {
+			pr_err("%s: for two channel only one SD lines is"
+				" needed. num_of_sd_lines = %u\n",
+				__func__, num_of_sd_lines);
+			ret_val = MI2S_FALSE;
+			goto error;
+		}
+
+		/* Enable single SD line for Tx */
+		mi2s_set_sd(mi2s, HDMI, (MI2S_SD_0_EN_MAP << sd_line) |
+				(MI2S_SD_0_TX_MAP << sd_line));
+
+		/* Set 2-channel mapping */
+		mi2s_set_output_2ch_map(mi2s, HDMI, sd_line);
+
+	} else if (channels == 4) {
+
+		if (num_of_sd_lines != 2) {
+			pr_err("%s: for 4 channels two SD lines are"
+				" needed. num_of_sd_lines = %u\\n",
+				__func__, num_of_sd_lines);
+			ret_val = MI2S_FALSE;
+			goto error;
+		}
+
+		if ((sd_line_mask && MI2S_SD_0) &&
+				(sd_line_mask && MI2S_SD_1)) {
+
+			mi2s_set_sd(mi2s, HDMI, (MI2S_SD_0_EN_MAP |
+				MI2S_SD_1_EN_MAP) | (MI2S_SD_0_TX_MAP |
+				MI2S_SD_1_TX_MAP));
+			mi2s_set_output_4ch_map(mi2s, HDMI, MI2S_FALSE);
+
+		} else if ((sd_line_mask && MI2S_SD_2) &&
+				(sd_line_mask && MI2S_SD_3)) {
+
+			mi2s_set_sd(mi2s, HDMI, (MI2S_SD_2_EN_MAP |
+				MI2S_SD_3_EN_MAP) | (MI2S_SD_2_TX_MAP |
+				MI2S_SD_3_TX_MAP));
+
+			mi2s_set_output_4ch_map(mi2s, HDMI, MI2S_TRUE);
+		} else {
+
+			pr_err("%s: for 4 channels invalid SD lines usage"
+				" sd_line_mask = 0x%x\n",
+				__func__, sd_line_mask);
+			ret_val = MI2S_FALSE;
+			goto error;
+		}
+	} else if (channels == 6) {
+
+		if (num_of_sd_lines != 3) {
+			pr_err("%s: for 6 channels three SD lines are"
+				" needed. num_of_sd_lines = %u\n",
+				__func__, num_of_sd_lines);
+			ret_val = MI2S_FALSE;
+			goto error;
+		}
+
+		if ((sd_line_mask && MI2S_SD_0) &&
+			(sd_line_mask && MI2S_SD_1) &&
+			(sd_line_mask && MI2S_SD_2)) {
+
+			mi2s_set_sd(mi2s, HDMI, (MI2S_SD_0_EN_MAP |
+				MI2S_SD_1_EN_MAP | MI2S_SD_2_EN_MAP) |
+				(MI2S_SD_0_TX_MAP | MI2S_SD_1_TX_MAP |
+				MI2S_SD_2_TX_MAP));
+
+		} else if ((sd_line_mask && MI2S_SD_1) &&
+				(sd_line_mask && MI2S_SD_2) &&
+				(sd_line_mask && MI2S_SD_3)) {
+
+			mi2s_set_sd(mi2s, HDMI, (MI2S_SD_1_EN_MAP |
+				MI2S_SD_2_EN_MAP | MI2S_SD_3_EN_MAP) |
+				(MI2S_SD_1_TX_MAP | MI2S_SD_2_TX_MAP |
+				MI2S_SD_3_TX_MAP));
+
+		} else {
+
+			pr_err("%s: for 6 channels invalid SD lines usage"
+				" sd_line_mask = 0x%x\n",
+				__func__, sd_line_mask);
+			ret_val = MI2S_FALSE;
+			goto error;
+		}
+	} else if (channels == 8) {
+
+		if (num_of_sd_lines != 4) {
+			pr_err("%s: for 8 channels four SD lines are"
+				" needed. num_of_sd_lines = %u\n",
+				__func__, num_of_sd_lines);
+			ret_val = MI2S_FALSE;
+			goto error;
+		}
+
+		mi2s_set_sd(mi2s, HDMI, (MI2S_SD_0_EN_MAP |
+			MI2S_SD_1_EN_MAP | MI2S_SD_2_EN_MAP |
+			MI2S_SD_3_EN_MAP) | (MI2S_SD_0_TX_MAP |
+			MI2S_SD_1_TX_MAP | MI2S_SD_2_TX_MAP |
+			MI2S_SD_3_TX_MAP));
+	} else {
+		pr_err("%s: invalid number channels = %u\n",
+				__func__, channels);
+			ret_val = MI2S_FALSE;
+			goto error;
+	}
+
+	baddr = get_base_addr(mi2s, HDMI);
+
+	val = readl(baddr + MI2S_MODE_OFFSET);
+	pr_debug("%s(): MI2S_MODE = 0x%x\n", __func__, val);
+
+	val = readl(baddr + MI2S_TX_MODE_OFFSET);
+	pr_debug("%s(): MI2S_TX_MODE = 0x%x\n", __func__, val);
+
+
+error:
+	/* Release device from reset */
+	mi2s_release(mi2s, HDMI);
+
+	mutex_unlock(&mi2s->mutex_lock);
+	mb();
+	return ret_val;
+}
+EXPORT_SYMBOL(mi2s_set_hdmi_output_path);
+
+bool mi2s_set_hdmi_input_path(uint8_t channels, uint8_t size,
+		uint8_t sd_line_mask)
+{
+	bool ret_val = MI2S_TRUE;
+	struct mi2s_state *mi2s = &the_mi2s_state;
+	u8 sd_line, num_of_sd_lines = 0;
+	void __iomem *baddr;
+	uint32_t val;
+
+	pr_debug("%s: channels = %u size = %u sd_line_mask = 0x%x\n", __func__,
+		channels, size, sd_line_mask);
+
+	if ((channels != 1) && (channels != MAX_NUM_CHANNELS_IN)) {
+
+		pr_err("%s: invalid number of channels. channels = %u\n",
+				__func__, channels);
+		return  MI2S_FALSE;
+	}
+
+	if (size > WT_MAX) {
+
+		pr_err("%s: mi2s word size can not be greater than 32 bits\n",
+				__func__);
+		return MI2S_FALSE;
+	}
+
+	sd_line_mask &=  MI2S_SD_LINE_MASK;
+
+	if (!sd_line_mask) {
+		pr_err("%s: Did not set any data lines to use "
+			" sd_line_mask =0x%x\n", __func__, sd_line_mask);
+		return  MI2S_FALSE;
+	}
+
+	num_of_sd_lines = num_of_bits_set(sd_line_mask);
+
+	if (num_of_sd_lines != 1) {
+		pr_err("%s: for two channel input only one SD lines is"
+			" needed. num_of_sd_lines = %u sd_line_mask = 0x%x\n",
+			__func__, num_of_sd_lines, sd_line_mask);
+		return MI2S_FALSE;
+	}
+
+	/*Second argument to find_first_bit should be maximum number of
+	bits interested*/
+	sd_line = find_first_bit((unsigned long *)&sd_line_mask,
+			sizeof(sd_line_mask) * 8);
+	pr_debug("sd_line = %d\n", sd_line);
+
+	/* Ensure sd_line parameter is valid (0-max) */
+	if (sd_line > MAX_SD_LINES) {
+		pr_err("%s: Line number can not be greater than = %u\n",
+			__func__, MAX_SD_LINES);
+		return MI2S_FALSE;
+	}
+
+	mutex_lock(&mi2s->mutex_lock);
+	/* Put device in reset */
+	mi2s_reset(mi2s, HDMI);
+
+	mi2s_master(mi2s, HDMI, 1);
+
+	/* Set word type */
+	mi2s_set_word_type(mi2s, HDMI, size);
+
+	/* Enable clock crossing synchronization of WR DMA ACK */
+	mi2s_set_input_clk_synch(mi2s, HDMI);
+
+	/* Ensure channels parameter is valid (non-zero, less than max,
+	 * and even or mono)
+	 */
+	mi2s_set_input_num_channels(mi2s, HDMI, channels);
+
+	mi2s_set_input_sd_line(mi2s, HDMI, sd_line);
+
+	mi2s_set_sd(mi2s, HDMI, (MI2S_SD_0_EN_MAP << sd_line));
+
+	baddr = get_base_addr(mi2s, HDMI);
+
+	val = readl(baddr + MI2S_MODE_OFFSET);
+	pr_debug("%s(): MI2S_MODE = 0x%x\n", __func__, val);
+
+	val = readl(baddr + MI2S_RX_MODE_OFFSET);
+	pr_debug("%s(): MI2S_RX_MODE = 0x%x\n", __func__, val);
+
+	/* Release device from reset */
+	mi2s_release(mi2s, HDMI);
+
+	mutex_unlock(&mi2s->mutex_lock);
+	mb();
+	return ret_val;
+}
+EXPORT_SYMBOL(mi2s_set_hdmi_input_path);
+
+bool mi2s_set_codec_output_path(uint8_t channels, uint8_t size)
+{
+	bool ret_val = MI2S_TRUE;
+	struct mi2s_state *mi2s = &the_mi2s_state;
+
+	mutex_lock(&mi2s->mutex_lock);
+	/* Put device in reset */
+	mi2s_reset(mi2s, CODEC_TX);
+
+	mi2s_master(mi2s, CODEC_TX, 1);
+
+	/* Enable clock crossing synchronization of RD DMA ACK */
+	mi2s_set_output_clk_synch(mi2s, CODEC_TX);
+
+	/* Set word type */
+	if (size <= WT_MAX)
+		mi2s_set_word_type(mi2s, CODEC_TX, size);
+	else
+		ret_val = MI2S_FALSE;
+
+	mi2s_set_output_num_channels(mi2s, CODEC_TX, channels);
+
+	/* Enable SD line */
+	mi2s_set_sd(mi2s, CODEC_TX, MI2S_SD_0_EN_MAP | MI2S_SD_0_TX_MAP);
+
+	/* Release device from reset */
+	mi2s_release(mi2s, CODEC_TX);
+
+	mutex_unlock(&mi2s->mutex_lock);
+	mb();
+	return ret_val;
+}
+EXPORT_SYMBOL(mi2s_set_codec_output_path);
+
+bool mi2s_set_codec_input_path(uint8_t channels, uint8_t size)
+{
+	bool ret_val = MI2S_TRUE;
+	struct mi2s_state *mi2s = &the_mi2s_state;
+
+	mutex_lock(&the_mi2s_state.mutex_lock);
+	/* Put device in reset */
+	mi2s_reset(mi2s, CODEC_RX);
+
+	mi2s_master(mi2s, CODEC_RX, 1);
+
+	/* Enable clock crossing synchronization of WR DMA ACK */
+	mi2s_set_input_clk_synch(mi2s, CODEC_RX);
+
+	/* Set word type */
+	if (size <= WT_MAX)
+		mi2s_set_word_type(mi2s, CODEC_RX, size);
+	else
+		ret_val = MI2S_FALSE;
+
+	mi2s_set_input_num_channels(mi2s, CODEC_RX, channels);
+
+	/* Enable SD line */
+	mi2s_set_sd(mi2s, CODEC_RX, MI2S_SD_0_EN_MAP);
+
+	/* Release device from reset */
+	mi2s_release(mi2s, CODEC_RX);
+
+	mutex_unlock(&mi2s->mutex_lock);
+	mb();
+	return ret_val;
+}
+EXPORT_SYMBOL(mi2s_set_codec_input_path);
+
+
+static int mi2s_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct resource *mem_src;
+
+	mem_src = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hdmi");
+	if (!mem_src) {
+		rc = -ENODEV;
+		goto error_hdmi;
+	}
+	the_mi2s_state.mi2s_hdmi_base = ioremap(mem_src->start,
+		(mem_src->end - mem_src->start) + 1);
+	if (!the_mi2s_state.mi2s_hdmi_base) {
+		rc = -ENOMEM;
+		goto error_hdmi;
+	}
+	mem_src = platform_get_resource_byname(pdev,
+		IORESOURCE_MEM, "codec_rx");
+	if (!mem_src) {
+		rc = -ENODEV;
+		goto error_codec_rx;
+	}
+	the_mi2s_state.mi2s_rx_base = ioremap(mem_src->start,
+		(mem_src->end - mem_src->start) + 1);
+	if (!the_mi2s_state.mi2s_rx_base) {
+		rc = -ENOMEM;
+		goto error_codec_rx;
+	}
+	mem_src = platform_get_resource_byname(pdev,
+		IORESOURCE_MEM, "codec_tx");
+	if (!mem_src) {
+		rc = -ENODEV;
+		goto error_codec_tx;
+	}
+	the_mi2s_state.mi2s_tx_base = ioremap(mem_src->start,
+		(mem_src->end - mem_src->start) + 1);
+	if (!the_mi2s_state.mi2s_tx_base) {
+		rc = -ENOMEM;
+		goto error_codec_tx;
+	}
+	mutex_init(&the_mi2s_state.mutex_lock);
+
+	return rc;
+
+error_codec_tx:
+	iounmap(the_mi2s_state.mi2s_rx_base);
+error_codec_rx:
+	iounmap(the_mi2s_state.mi2s_hdmi_base);
+error_hdmi:
+	return rc;
+
+}
+
+static int mi2s_remove(struct platform_device *pdev)
+{
+	iounmap(the_mi2s_state.mi2s_tx_base);
+	iounmap(the_mi2s_state.mi2s_rx_base);
+	iounmap(the_mi2s_state.mi2s_hdmi_base);
+	return 0;
+}
+
+static struct platform_driver mi2s_driver = {
+	.probe = mi2s_probe,
+	.remove = mi2s_remove,
+	.driver = {
+		.name = "mi2s",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init mi2s_init(void)
+{
+	return platform_driver_register(&mi2s_driver);
+}
+
+static void __exit mi2s_exit(void)
+{
+	platform_driver_unregister(&mi2s_driver);
+}
+
+module_init(mi2s_init);
+module_exit(mi2s_exit);
+
+MODULE_DESCRIPTION("MSM MI2S driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5v2/mp3_funcs.c b/arch/arm/mach-msm/qdsp5v2/mp3_funcs.c
new file mode 100644
index 0000000..0b20be0
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/mp3_funcs.c
@@ -0,0 +1,45 @@
+/* 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/fs.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+
+#include <linux/msm_audio.h>
+
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/qdsp5audplaycmdi.h>
+#include <mach/qdsp5v2/qdsp5audplaymsg.h>
+#include <mach/qdsp5v2/audpp.h>
+#include <mach/qdsp5v2/codec_utils.h>
+#include <mach/qdsp5v2/mp3_funcs.h>
+#include <mach/debug_mm.h>
+
+long mp3_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	MM_DBG("mp3_ioctl() cmd = %d\b", cmd);
+
+	return -EINVAL;
+}
+
+void audpp_cmd_cfg_mp3_params(struct audio *audio)
+{
+	struct 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));
+}
diff --git a/arch/arm/mach-msm/qdsp5v2/pcm_funcs.c b/arch/arm/mach-msm/qdsp5v2/pcm_funcs.c
new file mode 100644
index 0000000..d7935a7
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/pcm_funcs.c
@@ -0,0 +1,47 @@
+/* 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/fs.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+
+#include <linux/msm_audio.h>
+
+#include <mach/qdsp5v2/qdsp5audppmsg.h>
+#include <mach/qdsp5v2/qdsp5audplaycmdi.h>
+#include <mach/qdsp5v2/qdsp5audplaymsg.h>
+#include <mach/qdsp5v2/audpp.h>
+#include <mach/qdsp5v2/codec_utils.h>
+#include <mach/qdsp5v2/pcm_funcs.h>
+#include <mach/debug_mm.h>
+
+long pcm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	MM_DBG("pcm_ioctl() cmd = %d\n", cmd);
+
+	return -EINVAL;
+}
+
+void audpp_cmd_cfg_pcm_params(struct audio *audio)
+{
+	struct 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 >> 1;
+	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));
+}
diff --git a/arch/arm/mach-msm/qdsp5v2/snddev_data_marimba.c b/arch/arm/mach-msm/qdsp5v2/snddev_data_marimba.c
new file mode 100644
index 0000000..b15d4c4
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/snddev_data_marimba.c
@@ -0,0 +1,1537 @@
+/* Copyright (c) 2009-2011, 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/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/mfd/msm-adie-codec.h>
+#include <linux/uaccess.h>
+#include <mach/qdsp5v2/snddev_icodec.h>
+#include <mach/qdsp5v2/marimba_profile.h>
+#include <mach/qdsp5v2/aux_pcm.h>
+#include <mach/qdsp5v2/snddev_ecodec.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/qdsp5v2/snddev_virtual.h>
+#include <mach/board.h>
+#include <asm/mach-types.h>
+#include <mach/gpio.h>
+#include <mach/qdsp5v2/snddev_mi2s.h>
+#include <mach/qdsp5v2/mi2s.h>
+#include <mach/qdsp5v2/audio_acdb_def.h>
+
+/* define the value for BT_SCO */
+#define BT_SCO_PCM_CTL_VAL (PCM_CTL__RPCM_WIDTH__LINEAR_V |\
+				PCM_CTL__TPCM_WIDTH__LINEAR_V)
+#define BT_SCO_DATA_FORMAT_PADDING (DATA_FORMAT_PADDING_INFO__RPCM_FORMAT_V |\
+				DATA_FORMAT_PADDING_INFO__TPCM_FORMAT_V)
+#define BT_SCO_AUX_CODEC_INTF   AUX_CODEC_INTF_CTL__PCMINTF_DATA_EN_V
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *debugfs_hsed_config;
+static void snddev_hsed_config_modify_setting(int type);
+static void snddev_hsed_config_restore_setting(void);
+#endif
+
+static struct adie_codec_action_unit iearpiece_48KHz_osr256_actions[] =
+	HANDSET_RX_48000_OSR_256;
+
+static struct adie_codec_hwsetting_entry iearpiece_settings[] = {
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions = iearpiece_48KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(iearpiece_48KHz_osr256_actions),
+	}
+};
+
+static struct adie_codec_dev_profile iearpiece_profile = {
+	.path_type = ADIE_CODEC_RX,
+	.settings = iearpiece_settings,
+	.setting_sz = ARRAY_SIZE(iearpiece_settings),
+};
+
+static struct snddev_icodec_data snddev_iearpiece_data = {
+	.capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE),
+	.name = "handset_rx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_HANDSET_SPKR,
+	.profile = &iearpiece_profile,
+	.channel_mode = 1,
+	.pmctl_id = NULL,
+	.pmctl_id_sz = 0,
+	.default_sample_rate = 48000,
+	.pamp_on = NULL,
+	.pamp_off = NULL,
+	.property = SIDE_TONE_MASK,
+	.max_voice_rx_vol[VOC_NB_INDEX] = -200,
+	.min_voice_rx_vol[VOC_NB_INDEX] = -1700,
+	.max_voice_rx_vol[VOC_WB_INDEX] = -200,
+	.min_voice_rx_vol[VOC_WB_INDEX] = -1700
+};
+
+static struct platform_device msm_iearpiece_device = {
+	.name = "snddev_icodec",
+	.id = 0,
+	.dev = { .platform_data = &snddev_iearpiece_data },
+};
+
+static struct adie_codec_action_unit imic_8KHz_osr256_actions[] =
+	HANDSET_TX_8000_OSR_256;
+
+static struct adie_codec_action_unit imic_16KHz_osr256_actions[] =
+	HANDSET_TX_16000_OSR_256;
+
+static struct adie_codec_action_unit imic_48KHz_osr256_actions[] =
+	HANDSET_TX_48000_OSR_256;
+
+static struct adie_codec_hwsetting_entry imic_settings[] = {
+	{
+		.freq_plan = 8000,
+		.osr = 256,
+		.actions = imic_8KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(imic_8KHz_osr256_actions),
+	},
+	{
+		.freq_plan = 16000,
+		.osr = 256,
+		.actions = imic_16KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(imic_16KHz_osr256_actions),
+	},
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions = imic_48KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(imic_48KHz_osr256_actions),
+	}
+};
+
+static struct adie_codec_dev_profile imic_profile = {
+	.path_type = ADIE_CODEC_TX,
+	.settings = imic_settings,
+	.setting_sz = ARRAY_SIZE(imic_settings),
+};
+
+static enum hsed_controller imic_pmctl_id[] = {PM_HSED_CONTROLLER_0};
+
+static struct snddev_icodec_data snddev_imic_data = {
+	.capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+	.name = "handset_tx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_HANDSET_MIC,
+	.profile = &imic_profile,
+	.channel_mode = 1,
+	.pmctl_id = imic_pmctl_id,
+	.pmctl_id_sz = ARRAY_SIZE(imic_pmctl_id),
+	.default_sample_rate = 48000,
+	.pamp_on = NULL,
+	.pamp_off = NULL,
+};
+
+static struct platform_device msm_imic_device = {
+	.name = "snddev_icodec",
+	.id = 1,
+	.dev = { .platform_data = &snddev_imic_data },
+};
+
+static struct adie_codec_action_unit ihs_stereo_rx_48KHz_osr256_actions[] =
+	HEADSET_STEREO_RX_LEGACY_48000_OSR_256;
+
+static struct adie_codec_hwsetting_entry ihs_stereo_rx_settings[] = {
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions = ihs_stereo_rx_48KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(ihs_stereo_rx_48KHz_osr256_actions),
+	}
+};
+
+static struct adie_codec_dev_profile ihs_stereo_rx_profile = {
+	.path_type = ADIE_CODEC_RX,
+	.settings = ihs_stereo_rx_settings,
+	.setting_sz = ARRAY_SIZE(ihs_stereo_rx_settings),
+};
+
+static struct snddev_icodec_data snddev_ihs_stereo_rx_data = {
+	.capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE),
+	.name = "headset_stereo_rx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_HEADSET_SPKR_STEREO,
+	.profile = &ihs_stereo_rx_profile,
+	.channel_mode = 2,
+	.default_sample_rate = 48000,
+	.pamp_on = NULL,
+	.pamp_off = NULL,
+	.property = SIDE_TONE_MASK,
+	.max_voice_rx_vol[VOC_NB_INDEX] = -700,
+	.min_voice_rx_vol[VOC_NB_INDEX] = -2200,
+	.max_voice_rx_vol[VOC_WB_INDEX] = -900,
+	.min_voice_rx_vol[VOC_WB_INDEX] = -2400
+};
+
+static struct platform_device msm_ihs_stereo_rx_device = {
+	.name = "snddev_icodec",
+	.id = 2,
+	.dev = { .platform_data = &snddev_ihs_stereo_rx_data },
+};
+
+static struct adie_codec_action_unit ihs_mono_rx_48KHz_osr256_actions[] =
+	HEADSET_RX_LEGACY_48000_OSR_256;
+
+static struct adie_codec_hwsetting_entry ihs_mono_rx_settings[] = {
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions = ihs_mono_rx_48KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(ihs_mono_rx_48KHz_osr256_actions),
+	}
+};
+
+static struct adie_codec_dev_profile ihs_mono_rx_profile = {
+	.path_type = ADIE_CODEC_RX,
+	.settings = ihs_mono_rx_settings,
+	.setting_sz = ARRAY_SIZE(ihs_mono_rx_settings),
+};
+
+static struct snddev_icodec_data snddev_ihs_mono_rx_data = {
+	.capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE),
+	.name = "headset_mono_rx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_HEADSET_SPKR_MONO,
+	.profile = &ihs_mono_rx_profile,
+	.channel_mode = 1,
+	.default_sample_rate = 48000,
+	.pamp_on = NULL,
+	.pamp_off = NULL,
+	.property = SIDE_TONE_MASK,
+	.max_voice_rx_vol[VOC_NB_INDEX] = -700,
+	.min_voice_rx_vol[VOC_NB_INDEX] = -2200,
+	.max_voice_rx_vol[VOC_WB_INDEX] = -900,
+	.min_voice_rx_vol[VOC_WB_INDEX] = -2400,
+
+};
+
+static struct platform_device msm_ihs_mono_rx_device = {
+	.name = "snddev_icodec",
+	.id = 3,
+	.dev = { .platform_data = &snddev_ihs_mono_rx_data },
+};
+
+static struct adie_codec_action_unit ihs_ffa_stereo_rx_48KHz_osr256_actions[] =
+	HEADSET_STEREO_RX_CAPLESS_48000_OSR_256;
+
+static struct adie_codec_hwsetting_entry ihs_ffa_stereo_rx_settings[] = {
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions = ihs_ffa_stereo_rx_48KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(ihs_ffa_stereo_rx_48KHz_osr256_actions),
+	}
+};
+
+#ifdef CONFIG_DEBUG_FS
+static struct adie_codec_action_unit
+	ihs_ffa_stereo_rx_class_d_legacy_48KHz_osr256_actions[] =
+	HEADSET_STEREO_RX_CLASS_D_LEGACY_48000_OSR_256;
+
+static struct adie_codec_hwsetting_entry
+	ihs_ffa_stereo_rx_class_d_legacy_settings[] = {
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions =
+		ihs_ffa_stereo_rx_class_d_legacy_48KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE
+		(ihs_ffa_stereo_rx_class_d_legacy_48KHz_osr256_actions),
+	}
+};
+
+static struct adie_codec_action_unit
+	ihs_ffa_stereo_rx_class_ab_legacy_48KHz_osr256_actions[] =
+	HEADSET_STEREO_RX_LEGACY_48000_OSR_256;
+
+static struct adie_codec_hwsetting_entry
+	ihs_ffa_stereo_rx_class_ab_legacy_settings[] = {
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions =
+		ihs_ffa_stereo_rx_class_ab_legacy_48KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE
+		(ihs_ffa_stereo_rx_class_ab_legacy_48KHz_osr256_actions),
+	}
+};
+#endif
+
+static struct adie_codec_dev_profile ihs_ffa_stereo_rx_profile = {
+	.path_type = ADIE_CODEC_RX,
+	.settings = ihs_ffa_stereo_rx_settings,
+	.setting_sz = ARRAY_SIZE(ihs_ffa_stereo_rx_settings),
+};
+
+static struct snddev_icodec_data snddev_ihs_ffa_stereo_rx_data = {
+	.capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE),
+	.name = "headset_stereo_rx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_HEADSET_SPKR_STEREO,
+	.profile = &ihs_ffa_stereo_rx_profile,
+	.channel_mode = 2,
+	.default_sample_rate = 48000,
+	.voltage_on = msm_snddev_hsed_voltage_on,
+	.voltage_off = msm_snddev_hsed_voltage_off,
+	.max_voice_rx_vol[VOC_NB_INDEX] = -700,
+	.min_voice_rx_vol[VOC_NB_INDEX] = -2200,
+	.max_voice_rx_vol[VOC_WB_INDEX] = -900,
+	.min_voice_rx_vol[VOC_WB_INDEX] = -2400,
+};
+
+static struct platform_device msm_ihs_ffa_stereo_rx_device = {
+	.name = "snddev_icodec",
+	.id = 4,
+	.dev = { .platform_data = &snddev_ihs_ffa_stereo_rx_data },
+};
+
+static struct adie_codec_action_unit ihs_ffa_mono_rx_48KHz_osr256_actions[] =
+	HEADSET_RX_CAPLESS_48000_OSR_256;
+
+static struct adie_codec_hwsetting_entry ihs_ffa_mono_rx_settings[] = {
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions = ihs_ffa_mono_rx_48KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(ihs_ffa_mono_rx_48KHz_osr256_actions),
+	}
+};
+
+static struct adie_codec_dev_profile ihs_ffa_mono_rx_profile = {
+	.path_type = ADIE_CODEC_RX,
+	.settings = ihs_ffa_mono_rx_settings,
+	.setting_sz = ARRAY_SIZE(ihs_ffa_mono_rx_settings),
+};
+
+static struct snddev_icodec_data snddev_ihs_ffa_mono_rx_data = {
+	.capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE),
+	.name = "headset_mono_rx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_HEADSET_SPKR_MONO,
+	.profile = &ihs_ffa_mono_rx_profile,
+	.channel_mode = 1,
+	.default_sample_rate = 48000,
+	.pamp_on = msm_snddev_hsed_voltage_on,
+	.pamp_off = msm_snddev_hsed_voltage_off,
+	.max_voice_rx_vol[VOC_NB_INDEX] = -700,
+	.min_voice_rx_vol[VOC_NB_INDEX] = -2200,
+	.max_voice_rx_vol[VOC_WB_INDEX] = -900,
+	.min_voice_rx_vol[VOC_WB_INDEX] = -2400,
+};
+
+static struct platform_device msm_ihs_ffa_mono_rx_device = {
+	.name = "snddev_icodec",
+	.id = 5,
+	.dev = { .platform_data = &snddev_ihs_ffa_mono_rx_data },
+};
+
+static struct adie_codec_action_unit ihs_mono_tx_8KHz_osr256_actions[] =
+	HEADSET_MONO_TX_8000_OSR_256;
+
+static struct adie_codec_action_unit ihs_mono_tx_16KHz_osr256_actions[] =
+	HEADSET_MONO_TX_16000_OSR_256;
+
+static struct adie_codec_action_unit ihs_mono_tx_48KHz_osr256_actions[] =
+	HEADSET_MONO_TX_48000_OSR_256;
+
+static struct adie_codec_hwsetting_entry ihs_mono_tx_settings[] = {
+	{
+		.freq_plan = 8000,
+		.osr = 256,
+		.actions = ihs_mono_tx_8KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(ihs_mono_tx_8KHz_osr256_actions),
+	},
+	{
+		.freq_plan = 16000,
+		.osr = 256,
+		.actions = ihs_mono_tx_16KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(ihs_mono_tx_16KHz_osr256_actions),
+	},
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions = ihs_mono_tx_48KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(ihs_mono_tx_48KHz_osr256_actions),
+	}
+};
+
+static struct adie_codec_dev_profile ihs_mono_tx_profile = {
+	.path_type = ADIE_CODEC_TX,
+	.settings = ihs_mono_tx_settings,
+	.setting_sz = ARRAY_SIZE(ihs_mono_tx_settings),
+};
+
+static struct snddev_icodec_data snddev_ihs_mono_tx_data = {
+	.capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+	.name = "headset_mono_tx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_HEADSET_MIC,
+	.profile = &ihs_mono_tx_profile,
+	.channel_mode = 1,
+	.pmctl_id = NULL,
+	.pmctl_id_sz = 0,
+	.default_sample_rate = 48000,
+	.pamp_on = msm_snddev_tx_route_config,
+	.pamp_off = msm_snddev_tx_route_deconfig,
+};
+
+static struct platform_device msm_ihs_mono_tx_device = {
+	.name = "snddev_icodec",
+	.id = 6,
+	.dev = { .platform_data = &snddev_ihs_mono_tx_data },
+};
+
+static struct adie_codec_action_unit ifmradio_handset_osr64_actions[] =
+	FM_HANDSET_OSR_64;
+
+static struct adie_codec_hwsetting_entry ifmradio_handset_settings[] = {
+	{
+		.freq_plan = 8000,
+		.osr = 256,
+		.actions = ifmradio_handset_osr64_actions,
+		.action_sz = ARRAY_SIZE(ifmradio_handset_osr64_actions),
+	}
+};
+
+static struct adie_codec_dev_profile ifmradio_handset_profile = {
+	.path_type = ADIE_CODEC_RX,
+	.settings = ifmradio_handset_settings,
+	.setting_sz = ARRAY_SIZE(ifmradio_handset_settings),
+};
+
+static struct snddev_icodec_data snddev_ifmradio_handset_data = {
+	.capability = (SNDDEV_CAP_RX | SNDDEV_CAP_FM),
+	.name = "fmradio_handset_rx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_LP_FM_SPKR_PHONE_STEREO_RX,
+	.profile = &ifmradio_handset_profile,
+	.channel_mode = 1,
+	.default_sample_rate = 8000,
+	.pamp_on = NULL,
+	.pamp_off = NULL,
+	.dev_vol_type = SNDDEV_DEV_VOL_DIGITAL,
+};
+
+static struct platform_device msm_ifmradio_handset_device = {
+	.name = "snddev_icodec",
+	.id = 7,
+	.dev = { .platform_data = &snddev_ifmradio_handset_data },
+};
+
+
+static struct adie_codec_action_unit ispeaker_rx_48KHz_osr256_actions[] =
+   SPEAKER_STEREO_RX_48000_OSR_256;
+
+static struct adie_codec_hwsetting_entry ispeaker_rx_settings[] = {
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions = ispeaker_rx_48KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(ispeaker_rx_48KHz_osr256_actions),
+	}
+};
+
+static struct adie_codec_dev_profile ispeaker_rx_profile = {
+	.path_type = ADIE_CODEC_RX,
+	.settings = ispeaker_rx_settings,
+	.setting_sz = ARRAY_SIZE(ispeaker_rx_settings),
+};
+
+static struct snddev_icodec_data snddev_ispeaker_rx_data = {
+	.capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE),
+	.name = "speaker_stereo_rx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_SPKR_PHONE_STEREO,
+	.profile = &ispeaker_rx_profile,
+	.channel_mode = 2,
+	.pmctl_id = NULL,
+	.pmctl_id_sz = 0,
+	.default_sample_rate = 48000,
+	.pamp_on = &msm_snddev_poweramp_on,
+	.pamp_off = &msm_snddev_poweramp_off,
+	.max_voice_rx_vol[VOC_NB_INDEX] = 1000,
+	.min_voice_rx_vol[VOC_NB_INDEX] = -500,
+	.max_voice_rx_vol[VOC_WB_INDEX] = 1000,
+	.min_voice_rx_vol[VOC_WB_INDEX] = -500,
+};
+
+static struct platform_device msm_ispeaker_rx_device = {
+	.name = "snddev_icodec",
+	.id = 8,
+	.dev = { .platform_data = &snddev_ispeaker_rx_data },
+
+};
+
+static struct adie_codec_action_unit ifmradio_speaker_osr64_actions[] =
+	FM_SPEAKER_OSR_64;
+
+static struct adie_codec_hwsetting_entry ifmradio_speaker_settings[] = {
+	{
+		.freq_plan = 8000,
+		.osr = 256,
+		.actions = ifmradio_speaker_osr64_actions,
+		.action_sz = ARRAY_SIZE(ifmradio_speaker_osr64_actions),
+	}
+};
+
+static struct adie_codec_dev_profile ifmradio_speaker_profile = {
+	.path_type = ADIE_CODEC_RX,
+	.settings = ifmradio_speaker_settings,
+	.setting_sz = ARRAY_SIZE(ifmradio_speaker_settings),
+};
+
+static struct snddev_icodec_data snddev_ifmradio_speaker_data = {
+	.capability = (SNDDEV_CAP_RX | SNDDEV_CAP_FM),
+	.name = "fmradio_speaker_rx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_LP_FM_SPKR_PHONE_STEREO_RX,
+	.profile = &ifmradio_speaker_profile,
+	.channel_mode = 1,
+	.default_sample_rate = 8000,
+	.pamp_on = &msm_snddev_poweramp_on,
+	.pamp_off = &msm_snddev_poweramp_off,
+	.dev_vol_type = SNDDEV_DEV_VOL_DIGITAL,
+};
+
+static struct platform_device msm_ifmradio_speaker_device = {
+	.name = "snddev_icodec",
+	.id = 9,
+	.dev = { .platform_data = &snddev_ifmradio_speaker_data },
+};
+
+static struct adie_codec_action_unit ifmradio_headset_osr64_actions[] =
+	FM_HEADSET_STEREO_CLASS_D_LEGACY_OSR_64;
+
+static struct adie_codec_hwsetting_entry ifmradio_headset_settings[] = {
+	{
+		.freq_plan = 8000,
+		.osr = 256,
+		.actions = ifmradio_headset_osr64_actions,
+		.action_sz = ARRAY_SIZE(ifmradio_headset_osr64_actions),
+	}
+};
+
+static struct adie_codec_dev_profile ifmradio_headset_profile = {
+	.path_type = ADIE_CODEC_RX,
+	.settings = ifmradio_headset_settings,
+	.setting_sz = ARRAY_SIZE(ifmradio_headset_settings),
+};
+
+static struct snddev_icodec_data snddev_ifmradio_headset_data = {
+	.capability = (SNDDEV_CAP_RX | SNDDEV_CAP_FM),
+	.name = "fmradio_headset_rx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_LP_FM_HEADSET_SPKR_STEREO_RX,
+	.profile = &ifmradio_headset_profile,
+	.channel_mode = 1,
+	.default_sample_rate = 8000,
+	.pamp_on = NULL,
+	.pamp_off = NULL,
+	.dev_vol_type = SNDDEV_DEV_VOL_DIGITAL,
+};
+
+static struct platform_device msm_ifmradio_headset_device = {
+	.name = "snddev_icodec",
+	.id = 10,
+	.dev = { .platform_data = &snddev_ifmradio_headset_data },
+};
+
+
+static struct adie_codec_action_unit ifmradio_ffa_headset_osr64_actions[] =
+	FM_HEADSET_CLASS_AB_STEREO_CAPLESS_OSR_64;
+
+static struct adie_codec_hwsetting_entry ifmradio_ffa_headset_settings[] = {
+	{
+		.freq_plan = 8000,
+		.osr = 256,
+		.actions = ifmradio_ffa_headset_osr64_actions,
+		.action_sz = ARRAY_SIZE(ifmradio_ffa_headset_osr64_actions),
+	}
+};
+
+static struct adie_codec_dev_profile ifmradio_ffa_headset_profile = {
+	.path_type = ADIE_CODEC_RX,
+	.settings = ifmradio_ffa_headset_settings,
+	.setting_sz = ARRAY_SIZE(ifmradio_ffa_headset_settings),
+};
+
+static struct snddev_icodec_data snddev_ifmradio_ffa_headset_data = {
+	.capability = (SNDDEV_CAP_RX | SNDDEV_CAP_FM),
+	.name = "fmradio_headset_rx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_LP_FM_HEADSET_SPKR_STEREO_RX,
+	.profile = &ifmradio_ffa_headset_profile,
+	.channel_mode = 1,
+	.default_sample_rate = 8000,
+	.pamp_on = msm_snddev_hsed_voltage_on,
+	.pamp_off = msm_snddev_hsed_voltage_off,
+	.dev_vol_type = SNDDEV_DEV_VOL_DIGITAL,
+};
+
+static struct platform_device msm_ifmradio_ffa_headset_device = {
+	.name = "snddev_icodec",
+	.id = 11,
+	.dev = { .platform_data = &snddev_ifmradio_ffa_headset_data },
+};
+
+static struct snddev_ecodec_data snddev_bt_sco_earpiece_data = {
+	.capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE),
+	.name = "bt_sco_rx",
+	.copp_id = 1,
+	.acdb_id = ACDB_ID_BT_SCO_SPKR,
+	.channel_mode = 1,
+	.conf_pcm_ctl_val = BT_SCO_PCM_CTL_VAL,
+	.conf_aux_codec_intf = BT_SCO_AUX_CODEC_INTF,
+	.conf_data_format_padding_val = BT_SCO_DATA_FORMAT_PADDING,
+	.max_voice_rx_vol[VOC_NB_INDEX] = 400,
+	.min_voice_rx_vol[VOC_NB_INDEX] = -1100,
+	.max_voice_rx_vol[VOC_WB_INDEX] = 400,
+	.min_voice_rx_vol[VOC_WB_INDEX] = -1100,
+};
+
+static struct snddev_ecodec_data snddev_bt_sco_mic_data = {
+	.capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+	.name = "bt_sco_tx",
+	.copp_id = 1,
+	.acdb_id = ACDB_ID_BT_SCO_MIC,
+	.channel_mode = 1,
+	.conf_pcm_ctl_val = BT_SCO_PCM_CTL_VAL,
+	.conf_aux_codec_intf = BT_SCO_AUX_CODEC_INTF,
+	.conf_data_format_padding_val = BT_SCO_DATA_FORMAT_PADDING,
+};
+
+struct platform_device msm_bt_sco_earpiece_device = {
+	.name = "msm_snddev_ecodec",
+	.id = 0,
+	.dev = { .platform_data = &snddev_bt_sco_earpiece_data },
+};
+
+struct platform_device msm_bt_sco_mic_device = {
+	.name = "msm_snddev_ecodec",
+	.id = 1,
+	.dev = { .platform_data = &snddev_bt_sco_mic_data },
+};
+
+static struct adie_codec_action_unit idual_mic_endfire_8KHz_osr256_actions[] =
+	MIC1_LEFT_LINE_IN_RIGHT_8000_OSR_256;
+
+static struct adie_codec_hwsetting_entry idual_mic_endfire_settings[] = {
+	{
+		.freq_plan = 8000,
+		.osr = 256,
+		.actions = idual_mic_endfire_8KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(idual_mic_endfire_8KHz_osr256_actions),
+	}, /* 8KHz profile can be used for 16KHz */
+	{
+		.freq_plan = 16000,
+		.osr = 256,
+		.actions = idual_mic_endfire_8KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(idual_mic_endfire_8KHz_osr256_actions),
+	}, /* 8KHz profile can be used for 48KHz */
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions = idual_mic_endfire_8KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(idual_mic_endfire_8KHz_osr256_actions),
+	}
+};
+
+static struct adie_codec_dev_profile idual_mic_endfire_profile = {
+	.path_type = ADIE_CODEC_TX,
+	.settings = idual_mic_endfire_settings,
+	.setting_sz = ARRAY_SIZE(idual_mic_endfire_settings),
+};
+
+static enum hsed_controller idual_mic_endfire_pmctl_id[] = {
+	PM_HSED_CONTROLLER_0, PM_HSED_CONTROLLER_2
+};
+
+static struct snddev_icodec_data snddev_idual_mic_endfire_data = {
+	.capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+	.name = "handset_dual_mic_endfire_tx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_HANDSET_MIC_ENDFIRE,
+	.profile = &idual_mic_endfire_profile,
+	.channel_mode = 2,
+	.default_sample_rate = 48000,
+	.pmctl_id = idual_mic_endfire_pmctl_id,
+	.pmctl_id_sz = ARRAY_SIZE(idual_mic_endfire_pmctl_id),
+	.pamp_on = NULL,
+	.pamp_off = NULL,
+};
+
+static struct platform_device msm_idual_mic_endfire_device = {
+	.name = "snddev_icodec",
+	.id = 12,
+	.dev = { .platform_data = &snddev_idual_mic_endfire_data },
+};
+
+
+static struct snddev_icodec_data\
+		snddev_idual_mic_endfire_real_stereo_data = {
+	.capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+	.name = "handset_dual_mic_endfire_tx_real_stereo",
+	.copp_id = 0,
+	.acdb_id = PSEUDO_ACDB_ID,
+	.profile = &idual_mic_endfire_profile,
+	.channel_mode = REAL_STEREO_CHANNEL_MODE,
+	.default_sample_rate = 48000,
+	.pmctl_id = idual_mic_endfire_pmctl_id,
+	.pmctl_id_sz = ARRAY_SIZE(idual_mic_endfire_pmctl_id),
+	.pamp_on = NULL,
+	.pamp_off = NULL,
+};
+
+static struct platform_device msm_real_stereo_tx_device = {
+	.name = "snddev_icodec",
+	.id = 26,
+	.dev = { .platform_data =
+			&snddev_idual_mic_endfire_real_stereo_data },
+};
+
+static struct adie_codec_action_unit idual_mic_bs_8KHz_osr256_actions[] =
+	MIC1_LEFT_AUX_IN_RIGHT_8000_OSR_256;
+
+static struct adie_codec_hwsetting_entry idual_mic_broadside_settings[] = {
+	{
+		.freq_plan = 8000,
+		.osr = 256,
+		.actions = idual_mic_bs_8KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(idual_mic_bs_8KHz_osr256_actions),
+	}, /* 8KHz profile can be used for 16KHz */
+	{
+		.freq_plan = 16000,
+		.osr = 256,
+		.actions = idual_mic_bs_8KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(idual_mic_bs_8KHz_osr256_actions),
+	}, /* 8KHz profile can be used for 16KHz */
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions = idual_mic_bs_8KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(idual_mic_bs_8KHz_osr256_actions),
+	}
+};
+
+static struct adie_codec_dev_profile idual_mic_broadside_profile = {
+	.path_type = ADIE_CODEC_TX,
+	.settings = idual_mic_broadside_settings,
+	.setting_sz = ARRAY_SIZE(idual_mic_broadside_settings),
+};
+
+static enum hsed_controller idual_mic_broadside_pmctl_id[] = {
+	PM_HSED_CONTROLLER_0, PM_HSED_CONTROLLER_2
+};
+
+static struct snddev_icodec_data snddev_idual_mic_broadside_data = {
+	.capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+	.name = "handset_dual_mic_broadside_tx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_HANDSET_MIC_BROADSIDE,
+	.profile = &idual_mic_broadside_profile,
+	.channel_mode = 2,
+	.default_sample_rate = 48000,
+	.pmctl_id = idual_mic_broadside_pmctl_id,
+	.pmctl_id_sz = ARRAY_SIZE(idual_mic_broadside_pmctl_id),
+	.pamp_on = NULL,
+	.pamp_off = NULL,
+};
+
+static struct platform_device msm_idual_mic_broadside_device = {
+	.name = "snddev_icodec",
+	.id = 13,
+	.dev = { .platform_data = &snddev_idual_mic_broadside_data },
+};
+
+static struct adie_codec_action_unit ispk_dual_mic_ef_8KHz_osr256_actions[] =
+	SPEAKER_MIC1_LEFT_LINE_IN_RIGHT_8000_OSR_256;
+
+static struct adie_codec_hwsetting_entry ispk_dual_mic_ef_settings[] = {
+	{
+		.freq_plan = 8000,
+		.osr = 256,
+		.actions = ispk_dual_mic_ef_8KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(ispk_dual_mic_ef_8KHz_osr256_actions),
+	}, /* 8KHz profile can be used for 16Khz */
+	{
+		.freq_plan = 16000,
+		.osr = 256,
+		.actions = ispk_dual_mic_ef_8KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(ispk_dual_mic_ef_8KHz_osr256_actions),
+	}, /* 8KHz profile can be used for 48KHz */
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions = ispk_dual_mic_ef_8KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(ispk_dual_mic_ef_8KHz_osr256_actions),
+	},
+};
+
+static struct adie_codec_dev_profile ispk_dual_mic_ef_profile = {
+	.path_type = ADIE_CODEC_TX,
+	.settings = ispk_dual_mic_ef_settings,
+	.setting_sz = ARRAY_SIZE(ispk_dual_mic_ef_settings),
+};
+
+static struct snddev_icodec_data snddev_spk_idual_mic_endfire_data = {
+	.capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+	.name = "speaker_dual_mic_endfire_tx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_SPKR_PHONE_MIC_ENDFIRE,
+	.profile = &ispk_dual_mic_ef_profile,
+	.channel_mode = 2,
+	.default_sample_rate = 48000,
+	.pmctl_id = idual_mic_endfire_pmctl_id,
+	.pmctl_id_sz = ARRAY_SIZE(idual_mic_endfire_pmctl_id),
+	.pamp_on = NULL,
+	.pamp_off = NULL,
+};
+
+static struct platform_device msm_spk_idual_mic_endfire_device = {
+	.name = "snddev_icodec",
+	.id = 14,
+	.dev = { .platform_data = &snddev_spk_idual_mic_endfire_data },
+};
+
+static struct adie_codec_action_unit ispk_dual_mic_bs_8KHz_osr256_actions[] =
+	SPEAKER_MIC1_LEFT_AUX_IN_RIGHT_8000_OSR_256;
+
+static struct adie_codec_hwsetting_entry ispk_dual_mic_bs_settings[] = {
+	{
+		.freq_plan = 8000,
+		.osr = 256,
+		.actions = ispk_dual_mic_bs_8KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(ispk_dual_mic_bs_8KHz_osr256_actions),
+	}, /* 8KHz profile can be used for 16Khz */
+	{
+		.freq_plan = 16000,
+		.osr = 256,
+		.actions = ispk_dual_mic_bs_8KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(ispk_dual_mic_bs_8KHz_osr256_actions),
+	}, /* 8KHz profile can be used for 48KHz */
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions = ispk_dual_mic_bs_8KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(ispk_dual_mic_bs_8KHz_osr256_actions),
+	},
+};
+
+static struct adie_codec_dev_profile ispk_dual_mic_bs_profile = {
+	.path_type = ADIE_CODEC_TX,
+	.settings = ispk_dual_mic_bs_settings,
+	.setting_sz = ARRAY_SIZE(ispk_dual_mic_bs_settings),
+};
+static struct snddev_icodec_data snddev_spk_idual_mic_broadside_data = {
+	.capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+	.name = "speaker_dual_mic_broadside_tx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_SPKR_PHONE_MIC_BROADSIDE,
+	.profile = &ispk_dual_mic_bs_profile,
+	.channel_mode = 2,
+	.default_sample_rate = 48000,
+	.pmctl_id = idual_mic_broadside_pmctl_id,
+	.pmctl_id_sz = ARRAY_SIZE(idual_mic_broadside_pmctl_id),
+	.pamp_on = NULL,
+	.pamp_off = NULL,
+};
+
+static struct platform_device msm_spk_idual_mic_broadside_device = {
+	.name = "snddev_icodec",
+	.id = 15,
+	.dev = { .platform_data = &snddev_spk_idual_mic_broadside_data },
+};
+
+static struct adie_codec_action_unit itty_hs_mono_tx_8KHz_osr256_actions[] =
+	TTY_HEADSET_MONO_TX_8000_OSR_256;
+
+static struct adie_codec_hwsetting_entry itty_hs_mono_tx_settings[] = {
+	/* 8KHz, 16KHz, 48KHz TTY Tx devices can shared same set of actions */
+	{
+		.freq_plan = 8000,
+		.osr = 256,
+		.actions = itty_hs_mono_tx_8KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(itty_hs_mono_tx_8KHz_osr256_actions),
+	},
+	{
+		.freq_plan = 16000,
+		.osr = 256,
+		.actions = itty_hs_mono_tx_8KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(itty_hs_mono_tx_8KHz_osr256_actions),
+	},
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions = itty_hs_mono_tx_8KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(itty_hs_mono_tx_8KHz_osr256_actions),
+	}
+};
+
+static struct adie_codec_dev_profile itty_hs_mono_tx_profile = {
+	.path_type = ADIE_CODEC_TX,
+	.settings = itty_hs_mono_tx_settings,
+	.setting_sz = ARRAY_SIZE(itty_hs_mono_tx_settings),
+};
+
+static struct snddev_icodec_data snddev_itty_hs_mono_tx_data = {
+	.capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE | SNDDEV_CAP_TTY),
+	.name = "tty_headset_mono_tx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_TTY_HEADSET_MIC,
+	.profile = &itty_hs_mono_tx_profile,
+	.channel_mode = 1,
+	.default_sample_rate = 48000,
+	.pmctl_id = NULL,
+	.pmctl_id_sz = 0,
+	.pamp_on = NULL,
+	.pamp_off = NULL,
+};
+
+static struct platform_device msm_itty_hs_mono_tx_device = {
+	.name = "snddev_icodec",
+	.id = 16,
+	.dev = { .platform_data = &snddev_itty_hs_mono_tx_data },
+};
+
+static struct adie_codec_action_unit itty_hs_mono_rx_8KHz_osr256_actions[] =
+	TTY_HEADSET_MONO_RX_CLASS_D_8000_OSR_256;
+
+static struct adie_codec_action_unit itty_hs_mono_rx_16KHz_osr256_actions[] =
+	TTY_HEADSET_MONO_RX_CLASS_D_16000_OSR_256;
+
+static struct adie_codec_action_unit itty_hs_mono_rx_48KHz_osr256_actions[] =
+	TTY_HEADSET_MONO_RX_CLASS_D_48000_OSR_256;
+
+static struct adie_codec_hwsetting_entry itty_hs_mono_rx_settings[] = {
+	{
+		.freq_plan = 8000,
+		.osr = 256,
+		.actions = itty_hs_mono_rx_8KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(itty_hs_mono_rx_8KHz_osr256_actions),
+	},
+	{
+		.freq_plan = 16000,
+		.osr = 256,
+		.actions = itty_hs_mono_rx_16KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(itty_hs_mono_rx_16KHz_osr256_actions),
+	},
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions = itty_hs_mono_rx_48KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(itty_hs_mono_rx_48KHz_osr256_actions),
+	}
+};
+
+static struct adie_codec_dev_profile itty_hs_mono_rx_profile = {
+	.path_type = ADIE_CODEC_RX,
+	.settings = itty_hs_mono_rx_settings,
+	.setting_sz = ARRAY_SIZE(itty_hs_mono_rx_settings),
+};
+
+static struct snddev_icodec_data snddev_itty_hs_mono_rx_data = {
+	.capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE | SNDDEV_CAP_TTY),
+	.name = "tty_headset_mono_rx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_TTY_HEADSET_SPKR,
+	.profile = &itty_hs_mono_rx_profile,
+	.channel_mode = 1,
+	.default_sample_rate = 48000,
+	.pamp_on = NULL,
+	.pamp_off = NULL,
+	.max_voice_rx_vol[VOC_NB_INDEX] = 0,
+	.min_voice_rx_vol[VOC_NB_INDEX] = 0,
+	.max_voice_rx_vol[VOC_WB_INDEX] = 0,
+	.min_voice_rx_vol[VOC_WB_INDEX] = 0,
+};
+
+static struct platform_device msm_itty_hs_mono_rx_device = {
+	.name = "snddev_icodec",
+	.id = 17,
+	.dev = { .platform_data = &snddev_itty_hs_mono_rx_data },
+};
+
+static struct adie_codec_action_unit ispeaker_tx_8KHz_osr256_actions[] =
+	SPEAKER_TX_8000_OSR_256;
+
+static struct adie_codec_action_unit ispeaker_tx_48KHz_osr256_actions[] =
+	SPEAKER_TX_48000_OSR_256;
+
+static struct adie_codec_hwsetting_entry ispeaker_tx_settings[] = {
+	{
+		.freq_plan = 8000,
+		.osr = 256,
+		.actions = ispeaker_tx_8KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(ispeaker_tx_8KHz_osr256_actions),
+	},
+	{ /* 8KHz profile is good for 16KHz */
+		.freq_plan = 16000,
+		.osr = 256,
+		.actions = ispeaker_tx_8KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(ispeaker_tx_8KHz_osr256_actions),
+	},
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions = ispeaker_tx_48KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(ispeaker_tx_48KHz_osr256_actions),
+	}
+};
+
+static struct adie_codec_dev_profile ispeaker_tx_profile = {
+	.path_type = ADIE_CODEC_TX,
+	.settings = ispeaker_tx_settings,
+	.setting_sz = ARRAY_SIZE(ispeaker_tx_settings),
+};
+
+static enum hsed_controller ispk_pmctl_id[] = {PM_HSED_CONTROLLER_0};
+
+static struct snddev_icodec_data snddev_ispeaker_tx_data = {
+	.capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+	.name = "speaker_mono_tx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_SPKR_PHONE_MIC,
+	.profile = &ispeaker_tx_profile,
+	.channel_mode = 1,
+	.pmctl_id = ispk_pmctl_id,
+	.pmctl_id_sz = ARRAY_SIZE(ispk_pmctl_id),
+	.default_sample_rate = 48000,
+	.pamp_on = msm_snddev_tx_route_config,
+	.pamp_off = msm_snddev_tx_route_deconfig,
+};
+
+static struct platform_device msm_ispeaker_tx_device = {
+	.name = "snddev_icodec",
+	.id = 18,
+	.dev = { .platform_data = &snddev_ispeaker_tx_data },
+};
+
+static struct adie_codec_action_unit iearpiece_ffa_48KHz_osr256_actions[] =
+	HANDSET_RX_48000_OSR_256_FFA;
+
+static struct adie_codec_hwsetting_entry iearpiece_ffa_settings[] = {
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions = iearpiece_ffa_48KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(iearpiece_ffa_48KHz_osr256_actions),
+	}
+};
+
+static struct adie_codec_dev_profile iearpiece_ffa_profile = {
+	.path_type = ADIE_CODEC_RX,
+	.settings = iearpiece_ffa_settings,
+	.setting_sz = ARRAY_SIZE(iearpiece_ffa_settings),
+};
+
+static struct snddev_icodec_data snddev_iearpiece_ffa_data = {
+	.capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE),
+	.name = "handset_rx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_HANDSET_SPKR,
+	.profile = &iearpiece_ffa_profile,
+	.channel_mode = 1,
+	.pmctl_id = NULL,
+	.pmctl_id_sz = 0,
+	.default_sample_rate = 48000,
+	.pamp_on = NULL,
+	.pamp_off = NULL,
+	.max_voice_rx_vol[VOC_NB_INDEX] = -700,
+	.min_voice_rx_vol[VOC_NB_INDEX] = -2200,
+	.max_voice_rx_vol[VOC_WB_INDEX] = -1400,
+	.min_voice_rx_vol[VOC_WB_INDEX] = -2900,
+};
+
+static struct platform_device msm_iearpiece_ffa_device = {
+	.name = "snddev_icodec",
+	.id = 19,
+	.dev = { .platform_data = &snddev_iearpiece_ffa_data },
+};
+
+static struct adie_codec_action_unit imic_ffa_8KHz_osr256_actions[] =
+	HANDSET_TX_8000_OSR_256_FFA;
+
+static struct adie_codec_action_unit imic_ffa_16KHz_osr256_actions[] =
+	HANDSET_TX_16000_OSR_256_FFA;
+
+static struct adie_codec_action_unit imic_ffa_48KHz_osr256_actions[] =
+	HANDSET_TX_48000_OSR_256_FFA;
+
+static struct adie_codec_hwsetting_entry imic_ffa_settings[] = {
+	{
+		.freq_plan = 8000,
+		.osr = 256,
+		.actions = imic_ffa_8KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(imic_ffa_8KHz_osr256_actions),
+	},
+	{
+		.freq_plan = 16000,
+		.osr = 256,
+		.actions = imic_ffa_16KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(imic_ffa_16KHz_osr256_actions),
+	},
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions = imic_ffa_48KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(imic_ffa_48KHz_osr256_actions),
+	}
+};
+
+static struct adie_codec_dev_profile imic_ffa_profile = {
+	.path_type = ADIE_CODEC_TX,
+	.settings = imic_ffa_settings,
+	.setting_sz = ARRAY_SIZE(imic_ffa_settings),
+};
+
+static struct snddev_icodec_data snddev_imic_ffa_data = {
+	.capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+	.name = "handset_tx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_HANDSET_MIC,
+	.profile = &imic_ffa_profile,
+	.channel_mode = 1,
+	.pmctl_id = imic_pmctl_id,
+	.pmctl_id_sz = ARRAY_SIZE(imic_pmctl_id),
+	.default_sample_rate = 48000,
+	.pamp_on = NULL,
+	.pamp_off = NULL,
+};
+
+static struct platform_device msm_imic_ffa_device = {
+	.name = "snddev_icodec",
+	.id = 20,
+	.dev = { .platform_data = &snddev_imic_ffa_data },
+};
+
+
+static struct adie_codec_action_unit
+	ihs_stereo_speaker_stereo_rx_48KHz_osr256_actions[] =
+	HEADSET_STEREO_SPEAKER_STEREO_RX_CAPLESS_48000_OSR_256;
+
+
+static struct adie_codec_hwsetting_entry
+	ihs_stereo_speaker_stereo_rx_settings[] = {
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions = ihs_stereo_speaker_stereo_rx_48KHz_osr256_actions,
+		.action_sz =
+		ARRAY_SIZE(ihs_stereo_speaker_stereo_rx_48KHz_osr256_actions),
+	}
+};
+
+static struct adie_codec_dev_profile ihs_stereo_speaker_stereo_rx_profile = {
+	.path_type = ADIE_CODEC_RX,
+	.settings = ihs_stereo_speaker_stereo_rx_settings,
+	.setting_sz = ARRAY_SIZE(ihs_stereo_speaker_stereo_rx_settings),
+};
+
+static struct snddev_icodec_data snddev_ihs_stereo_speaker_stereo_rx_data = {
+	.capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE),
+	.name = "headset_stereo_speaker_stereo_rx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_HEADSET_STEREO_PLUS_SPKR_STEREO_RX,
+	.profile = &ihs_stereo_speaker_stereo_rx_profile,
+	.channel_mode = 2,
+	.default_sample_rate = 48000,
+	.pamp_on = msm_snddev_poweramp_on,
+	.pamp_off = msm_snddev_poweramp_off,
+	.voltage_on = msm_snddev_hsed_voltage_on,
+	.voltage_off = msm_snddev_hsed_voltage_off,
+	.max_voice_rx_vol[VOC_NB_INDEX] = -500,
+	.min_voice_rx_vol[VOC_NB_INDEX] = -2000,
+	.max_voice_rx_vol[VOC_WB_INDEX] = -500,
+	.min_voice_rx_vol[VOC_WB_INDEX] = -2000,
+};
+
+static struct platform_device msm_ihs_stereo_speaker_stereo_rx_device = {
+	.name = "snddev_icodec",
+	.id = 21,
+	.dev = { .platform_data = &snddev_ihs_stereo_speaker_stereo_rx_data },
+};
+
+static struct snddev_mi2s_data snddev_mi2s_stereo_rx_data = {
+	.capability = SNDDEV_CAP_RX ,
+	.name = "hdmi_stereo_rx",
+	.copp_id = 3,
+	.acdb_id = ACDB_ID_HDMI,
+	.channel_mode = 2,
+	.sd_lines = MI2S_SD_0,
+	.route = msm_snddev_tx_route_config,
+	.deroute = msm_snddev_tx_route_deconfig,
+	.default_sample_rate = 48000,
+};
+
+static struct platform_device msm_snddev_mi2s_stereo_rx_device = {
+	.name = "snddev_mi2s",
+	.id = 0,
+	.dev = { .platform_data = &snddev_mi2s_stereo_rx_data },
+};
+
+
+static struct snddev_mi2s_data snddev_mi2s_fm_tx_data = {
+	.capability = SNDDEV_CAP_TX ,
+	.name = "fmradio_stereo_tx",
+	.copp_id = 2,
+	.acdb_id = ACDB_ID_FM_TX,
+	.channel_mode = 2,
+	.sd_lines = MI2S_SD_3,
+	.route = NULL,
+	.deroute = NULL,
+	.default_sample_rate = 48000,
+};
+
+static struct platform_device  msm_snddev_mi2s_fm_tx_device = {
+	.name = "snddev_mi2s",
+	.id = 1,
+	.dev = { .platform_data = &snddev_mi2s_fm_tx_data},
+};
+
+static struct snddev_icodec_data snddev_fluid_imic_tx_data = {
+	.capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+	.name = "handset_tx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_SPKR_PHONE_MIC,
+	.profile = &ispeaker_tx_profile,
+	.channel_mode = 1,
+	.pmctl_id = ispk_pmctl_id,
+	.pmctl_id_sz = ARRAY_SIZE(ispk_pmctl_id),
+	.default_sample_rate = 48000,
+	.pamp_on = msm_snddev_tx_route_config,
+	.pamp_off = msm_snddev_tx_route_deconfig,
+};
+
+static struct platform_device msm_fluid_imic_tx_device = {
+	.name = "snddev_icodec",
+	.id = 22,
+	.dev = { .platform_data = &snddev_fluid_imic_tx_data },
+};
+
+static struct snddev_icodec_data snddev_fluid_iearpiece_rx_data = {
+	.capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE),
+	.name = "handset_rx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_SPKR_PHONE_STEREO,
+	.profile = &ispeaker_rx_profile,
+	.channel_mode = 2,
+	.pmctl_id = NULL,
+	.pmctl_id_sz = 0,
+	.default_sample_rate = 48000,
+	.pamp_on = &msm_snddev_poweramp_on,
+	.pamp_off = &msm_snddev_poweramp_off,
+	.max_voice_rx_vol[VOC_NB_INDEX] = -500,
+	.min_voice_rx_vol[VOC_NB_INDEX] = -1000,
+	.max_voice_rx_vol[VOC_WB_INDEX] = -500,
+	.min_voice_rx_vol[VOC_WB_INDEX] = -1000,
+};
+
+static struct platform_device msm_fluid_iearpeice_rx_device = {
+	.name = "snddev_icodec",
+	.id = 23,
+	.dev = { .platform_data = &snddev_fluid_iearpiece_rx_data },
+};
+
+static struct adie_codec_action_unit fluid_idual_mic_ef_8KHz_osr256_actions[] =
+	MIC1_LEFT_AUX_IN_RIGHT_8000_OSR_256;
+
+static struct adie_codec_hwsetting_entry fluid_idual_mic_endfire_settings[] = {
+	{
+		.freq_plan = 8000,
+		.osr = 256,
+		.actions = fluid_idual_mic_ef_8KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(fluid_idual_mic_ef_8KHz_osr256_actions),
+	}, /* 8KHz profile can be used for 16KHz */
+	{
+		.freq_plan = 16000,
+		.osr = 256,
+		.actions = fluid_idual_mic_ef_8KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(fluid_idual_mic_ef_8KHz_osr256_actions),
+	}, /* 8KHz profile can also be used for 48KHz */
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions = fluid_idual_mic_ef_8KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(fluid_idual_mic_ef_8KHz_osr256_actions),
+	}
+};
+
+static struct adie_codec_dev_profile fluid_idual_mic_endfire_profile = {
+	.path_type = ADIE_CODEC_TX,
+	.settings = fluid_idual_mic_endfire_settings,
+	.setting_sz = ARRAY_SIZE(fluid_idual_mic_endfire_settings),
+};
+
+static enum hsed_controller fluid_idual_mic_endfire_pmctl_id[] = {
+	PM_HSED_CONTROLLER_0, PM_HSED_CONTROLLER_2
+};
+
+static struct snddev_icodec_data snddev_fluid_idual_mic_endfire_data = {
+	.capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+	.name = "handset_dual_mic_endfire_tx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_SPKR_PHONE_MIC_ENDFIRE,
+	.profile = &fluid_idual_mic_endfire_profile,
+	.channel_mode = 2,
+	.default_sample_rate = 48000,
+	.pmctl_id = fluid_idual_mic_endfire_pmctl_id,
+	.pmctl_id_sz = ARRAY_SIZE(fluid_idual_mic_endfire_pmctl_id),
+	.pamp_on = msm_snddev_tx_route_config,
+	.pamp_off = msm_snddev_tx_route_deconfig,
+};
+
+static struct platform_device msm_fluid_idual_mic_endfire_device = {
+	.name = "snddev_icodec",
+	.id = 24,
+	.dev = { .platform_data = &snddev_fluid_idual_mic_endfire_data },
+};
+
+static struct snddev_icodec_data snddev_fluid_spk_idual_mic_endfire_data = {
+	.capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+	.name = "speaker_dual_mic_endfire_tx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_SPKR_PHONE_MIC_ENDFIRE,
+	.profile = &fluid_idual_mic_endfire_profile,
+	.channel_mode = 2,
+	.default_sample_rate = 48000,
+	.pmctl_id = fluid_idual_mic_endfire_pmctl_id,
+	.pmctl_id_sz = ARRAY_SIZE(fluid_idual_mic_endfire_pmctl_id),
+	.pamp_on = msm_snddev_tx_route_config,
+	.pamp_off = msm_snddev_tx_route_deconfig,
+};
+
+static struct platform_device msm_fluid_spk_idual_mic_endfire_device = {
+	.name = "snddev_icodec",
+	.id = 25,
+	.dev = { .platform_data = &snddev_fluid_spk_idual_mic_endfire_data },
+};
+
+static struct snddev_virtual_data snddev_a2dp_tx_data = {
+	.capability = SNDDEV_CAP_TX,
+	.name = "a2dp_tx",
+	.copp_id = 5,
+	.acdb_id = PSEUDO_ACDB_ID,
+};
+
+static struct snddev_virtual_data snddev_a2dp_rx_data = {
+	.capability = SNDDEV_CAP_RX,
+	.name = "a2dp_rx",
+	.copp_id = 2,
+	.acdb_id = PSEUDO_ACDB_ID,
+};
+
+static struct platform_device msm_a2dp_rx_device = {
+	.name = "snddev_virtual",
+	.id = 0,
+	.dev = { .platform_data = &snddev_a2dp_rx_data },
+};
+
+static struct platform_device msm_a2dp_tx_device = {
+	.name = "snddev_virtual",
+	.id = 1,
+	.dev = { .platform_data = &snddev_a2dp_tx_data },
+};
+
+static struct snddev_virtual_data snddev_uplink_rx_data = {
+	.capability = SNDDEV_CAP_RX,
+	.name = "uplink_rx",
+	.copp_id = 5,
+	.acdb_id = PSEUDO_ACDB_ID,
+};
+
+static struct platform_device msm_uplink_rx_device = {
+	.name = "snddev_virtual",
+	.id = 2,
+	.dev = { .platform_data = &snddev_uplink_rx_data },
+};
+
+static struct platform_device *snd_devices_ffa[] __initdata = {
+	&msm_iearpiece_ffa_device,
+	&msm_imic_ffa_device,
+	&msm_ifmradio_handset_device,
+	&msm_ihs_ffa_stereo_rx_device,
+	&msm_ihs_ffa_mono_rx_device,
+	&msm_ihs_mono_tx_device,
+	&msm_bt_sco_earpiece_device,
+	&msm_bt_sco_mic_device,
+	&msm_ispeaker_rx_device,
+	&msm_ifmradio_speaker_device,
+	&msm_ifmradio_ffa_headset_device,
+	&msm_idual_mic_endfire_device,
+	&msm_idual_mic_broadside_device,
+	&msm_spk_idual_mic_endfire_device,
+	&msm_spk_idual_mic_broadside_device,
+	&msm_itty_hs_mono_tx_device,
+	&msm_itty_hs_mono_rx_device,
+	&msm_ispeaker_tx_device,
+	&msm_ihs_stereo_speaker_stereo_rx_device,
+	&msm_a2dp_rx_device,
+	&msm_a2dp_tx_device,
+	&msm_snddev_mi2s_stereo_rx_device,
+	&msm_snddev_mi2s_fm_tx_device,
+	&msm_uplink_rx_device,
+	&msm_real_stereo_tx_device,
+};
+
+static struct platform_device *snd_devices_surf[] __initdata = {
+	&msm_iearpiece_device,
+	&msm_imic_device,
+	&msm_ihs_stereo_rx_device,
+	&msm_ihs_mono_rx_device,
+	&msm_ihs_mono_tx_device,
+	&msm_bt_sco_earpiece_device,
+	&msm_bt_sco_mic_device,
+	&msm_ifmradio_handset_device,
+	&msm_ispeaker_rx_device,
+	&msm_ifmradio_speaker_device,
+	&msm_ifmradio_headset_device,
+	&msm_itty_hs_mono_tx_device,
+	&msm_itty_hs_mono_rx_device,
+	&msm_ispeaker_tx_device,
+	&msm_ihs_stereo_speaker_stereo_rx_device,
+	&msm_a2dp_rx_device,
+	&msm_a2dp_tx_device,
+	&msm_snddev_mi2s_stereo_rx_device,
+	&msm_snddev_mi2s_fm_tx_device,
+	&msm_uplink_rx_device,
+};
+
+static struct platform_device *snd_devices_fluid[] __initdata = {
+	&msm_ihs_stereo_rx_device,
+	&msm_ihs_mono_rx_device,
+	&msm_ihs_mono_tx_device,
+	&msm_ispeaker_rx_device,
+	&msm_ispeaker_tx_device,
+	&msm_fluid_imic_tx_device,
+	&msm_fluid_iearpeice_rx_device,
+	&msm_fluid_idual_mic_endfire_device,
+	&msm_fluid_spk_idual_mic_endfire_device,
+	&msm_a2dp_rx_device,
+	&msm_a2dp_tx_device,
+	&msm_snddev_mi2s_stereo_rx_device,
+	&msm_uplink_rx_device,
+	&msm_ifmradio_speaker_device,
+	&msm_ifmradio_headset_device,
+};
+
+#ifdef CONFIG_DEBUG_FS
+static void snddev_hsed_config_modify_setting(int type)
+{
+	struct platform_device *device;
+	struct snddev_icodec_data *icodec_data;
+
+	device = &msm_ihs_ffa_stereo_rx_device;
+	icodec_data = (struct snddev_icodec_data *)device->dev.platform_data;
+
+	if (icodec_data) {
+		if (type == 1) {
+			icodec_data->voltage_on = NULL;
+			icodec_data->voltage_off = NULL;
+			icodec_data->profile->settings =
+				ihs_ffa_stereo_rx_class_d_legacy_settings;
+			icodec_data->profile->setting_sz =
+			ARRAY_SIZE(ihs_ffa_stereo_rx_class_d_legacy_settings);
+		} else if (type == 2) {
+			icodec_data->voltage_on = NULL;
+			icodec_data->voltage_off = NULL;
+			icodec_data->profile->settings =
+				ihs_ffa_stereo_rx_class_ab_legacy_settings;
+			icodec_data->profile->setting_sz =
+			ARRAY_SIZE(ihs_ffa_stereo_rx_class_ab_legacy_settings);
+		}
+	}
+}
+
+static void snddev_hsed_config_restore_setting(void)
+{
+	struct platform_device *device;
+	struct snddev_icodec_data *icodec_data;
+
+	device = &msm_ihs_ffa_stereo_rx_device;
+	icodec_data = (struct snddev_icodec_data *)device->dev.platform_data;
+
+	if (icodec_data) {
+		icodec_data->voltage_on = msm_snddev_hsed_voltage_on;
+		icodec_data->voltage_off = msm_snddev_hsed_voltage_off;
+		icodec_data->profile->settings = ihs_ffa_stereo_rx_settings;
+		icodec_data->profile->setting_sz =
+			ARRAY_SIZE(ihs_ffa_stereo_rx_settings);
+	}
+}
+
+static ssize_t snddev_hsed_config_debug_write(struct file *filp,
+	const char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+	char *lb_str = filp->private_data;
+	char cmd;
+
+	if (get_user(cmd, ubuf))
+		return -EFAULT;
+
+	if (!strcmp(lb_str, "msm_hsed_config")) {
+		switch (cmd) {
+		case '0':
+			snddev_hsed_config_restore_setting();
+			break;
+
+		case '1':
+			snddev_hsed_config_modify_setting(1);
+			break;
+
+		case '2':
+			snddev_hsed_config_modify_setting(2);
+			break;
+
+		default:
+			break;
+		}
+	}
+	return cnt;
+}
+
+static int snddev_hsed_config_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static const struct file_operations snddev_hsed_config_debug_fops = {
+	.open = snddev_hsed_config_debug_open,
+	.write = snddev_hsed_config_debug_write
+};
+#endif
+
+void __ref msm_snddev_init(void)
+{
+	if (machine_is_msm7x30_ffa() || machine_is_msm8x55_ffa() ||
+		machine_is_msm8x55_svlte_ffa()) {
+		platform_add_devices(snd_devices_ffa,
+		ARRAY_SIZE(snd_devices_ffa));
+#ifdef CONFIG_DEBUG_FS
+		debugfs_hsed_config = debugfs_create_file("msm_hsed_config",
+					S_IFREG | S_IRUGO, NULL,
+		(void *) "msm_hsed_config", &snddev_hsed_config_debug_fops);
+#endif
+	} else if (machine_is_msm7x30_surf() || machine_is_msm8x55_surf() ||
+		machine_is_msm8x55_svlte_surf())
+		platform_add_devices(snd_devices_surf,
+		ARRAY_SIZE(snd_devices_surf));
+	else if (machine_is_msm7x30_fluid())
+		platform_add_devices(snd_devices_fluid,
+		ARRAY_SIZE(snd_devices_fluid));
+	else
+		pr_err("%s: Unknown machine type\n", __func__);
+}
diff --git a/arch/arm/mach-msm/qdsp5v2/snddev_data_timpani.c b/arch/arm/mach-msm/qdsp5v2/snddev_data_timpani.c
new file mode 100644
index 0000000..c0a48c8
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/snddev_data_timpani.c
@@ -0,0 +1,1006 @@
+/* Copyright (c) 2010-2011, 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/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/mfd/msm-adie-codec.h>
+#include <linux/uaccess.h>
+#include <asm/mach-types.h>
+#include <mach/qdsp5v2/aux_pcm.h>
+#include <mach/qdsp5v2/snddev_ecodec.h>
+#include <mach/board.h>
+#include <mach/qdsp5v2/snddev_icodec.h>
+#include <mach/qdsp5v2/snddev_mi2s.h>
+#include <mach/qdsp5v2/mi2s.h>
+#include <mach/qdsp5v2/audio_acdb_def.h>
+#include <mach/qdsp5v2/snddev_virtual.h>
+#include "timpani_profile_7x30.h"
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+
+/* define the value for BT_SCO */
+#define BT_SCO_PCM_CTL_VAL (PCM_CTL__RPCM_WIDTH__LINEAR_V |\
+		PCM_CTL__TPCM_WIDTH__LINEAR_V)
+#define BT_SCO_DATA_FORMAT_PADDING (DATA_FORMAT_PADDING_INFO__RPCM_FORMAT_V |\
+		DATA_FORMAT_PADDING_INFO__TPCM_FORMAT_V)
+#define BT_SCO_AUX_CODEC_INTF   AUX_CODEC_INTF_CTL__PCMINTF_DATA_EN_V
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *debugfs_hsed_config;
+static void snddev_hsed_config_modify_setting(int type);
+static void snddev_hsed_config_restore_setting(void);
+#endif
+
+static struct adie_codec_action_unit iearpiece_ffa_48KHz_osr256_actions[] =
+	EAR_PRI_MONO_8000_OSR_256; /* 8000 profile also works for 48k */
+
+static struct adie_codec_hwsetting_entry iearpiece_ffa_settings[] = {
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions = iearpiece_ffa_48KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(iearpiece_ffa_48KHz_osr256_actions),
+	}
+};
+
+static struct adie_codec_dev_profile iearpiece_ffa_profile = {
+	.path_type = ADIE_CODEC_RX,
+	.settings = iearpiece_ffa_settings,
+	.setting_sz = ARRAY_SIZE(iearpiece_ffa_settings),
+};
+
+static struct snddev_icodec_data snddev_iearpiece_ffa_data = {
+	.capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE),
+	.name = "handset_rx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_HANDSET_SPKR,
+	.profile = &iearpiece_ffa_profile,
+	.channel_mode = 1,
+	.pmctl_id = NULL,
+	.pmctl_id_sz = 0,
+	.default_sample_rate = 48000,
+	.pamp_on = NULL,
+	.pamp_off = NULL,
+	.property = SIDE_TONE_MASK,
+	.max_voice_rx_vol[VOC_NB_INDEX] = -700,
+	.min_voice_rx_vol[VOC_NB_INDEX] = -2200,
+	.max_voice_rx_vol[VOC_WB_INDEX] = -1400,
+	.min_voice_rx_vol[VOC_WB_INDEX] = -2900,
+};
+
+static struct platform_device msm_iearpiece_ffa_device = {
+	.name = "snddev_icodec",
+	.id = 19,
+	.dev = { .platform_data = &snddev_iearpiece_ffa_data },
+};
+
+static struct adie_codec_action_unit imic_ffa_48KHz_osr256_actions[] =
+	AMIC_PRI_MONO_8000_OSR_256; /* 8000 profile also works for 48k */
+
+static struct adie_codec_hwsetting_entry imic_ffa_settings[] = {
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions = imic_ffa_48KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(imic_ffa_48KHz_osr256_actions),
+	}
+};
+
+static enum hsed_controller imic_pmctl_id[] = {PM_HSED_CONTROLLER_0};
+
+static struct adie_codec_dev_profile imic_ffa_profile = {
+	.path_type = ADIE_CODEC_TX,
+	.settings = imic_ffa_settings,
+	.setting_sz = ARRAY_SIZE(imic_ffa_settings),
+};
+
+static struct snddev_icodec_data snddev_imic_ffa_data = {
+	.capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+	.name = "handset_tx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_HANDSET_MIC,
+	.profile = &imic_ffa_profile,
+	.channel_mode = 1,
+	.pmctl_id = imic_pmctl_id,
+	.pmctl_id_sz = ARRAY_SIZE(imic_pmctl_id),
+	.default_sample_rate = 48000,
+	.pamp_on = NULL,
+	.pamp_off = NULL,
+};
+
+static struct platform_device msm_imic_ffa_device = {
+	.name = "snddev_icodec",
+	.id = 20,
+	.dev = { .platform_data = &snddev_imic_ffa_data },
+};
+
+static struct adie_codec_action_unit ispkr_stereo_48KHz_osr256_actions[] =
+	SPEAKER_PRI_STEREO_48000_OSR_256;
+
+static struct adie_codec_hwsetting_entry ispkr_stereo_settings[] = {
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions = ispkr_stereo_48KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(ispkr_stereo_48KHz_osr256_actions),
+	}
+};
+
+static struct adie_codec_dev_profile ispkr_stereo_profile = {
+	.path_type = ADIE_CODEC_RX,
+	.settings = ispkr_stereo_settings,
+	.setting_sz = ARRAY_SIZE(ispkr_stereo_settings),
+};
+
+static struct snddev_icodec_data snddev_ispkr_stereo_data = {
+	.capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE),
+	.name = "speaker_stereo_rx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_SPKR_PHONE_STEREO,
+	.profile = &ispkr_stereo_profile,
+	.channel_mode = 2,
+	.pmctl_id = NULL,
+	.pmctl_id_sz = 0,
+	.default_sample_rate = 48000,
+	.pamp_on = msm_snddev_poweramp_on,
+	.pamp_off = msm_snddev_poweramp_off,
+	.max_voice_rx_vol[VOC_NB_INDEX] = 1000,
+	.min_voice_rx_vol[VOC_NB_INDEX] = -500,
+	.max_voice_rx_vol[VOC_WB_INDEX] = 1000,
+	.min_voice_rx_vol[VOC_WB_INDEX] = -500
+};
+
+static struct platform_device msm_ispkr_stereo_device = {
+	.name = "snddev_icodec",
+	.id = 8,
+	.dev = { .platform_data = &snddev_ispkr_stereo_data },
+};
+
+static struct adie_codec_action_unit iheadset_mic_tx_osr256_actions[] =
+	AMIC1_HEADSET_TX_MONO_PRIMARY_OSR256;
+
+static struct adie_codec_hwsetting_entry iheadset_mic_tx_settings[] = {
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions = iheadset_mic_tx_osr256_actions,
+		.action_sz = ARRAY_SIZE(iheadset_mic_tx_osr256_actions),
+	}
+};
+
+static struct adie_codec_dev_profile iheadset_mic_profile = {
+	.path_type = ADIE_CODEC_TX,
+	.settings = iheadset_mic_tx_settings,
+	.setting_sz = ARRAY_SIZE(iheadset_mic_tx_settings),
+};
+
+static struct snddev_icodec_data snddev_headset_mic_data = {
+	.capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+	.name = "headset_mono_tx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_HEADSET_MIC,
+	.profile = &iheadset_mic_profile,
+	.channel_mode = 1,
+	.pmctl_id = NULL,
+	.pmctl_id_sz = 0,
+	.default_sample_rate = 48000,
+	.pamp_on = msm_snddev_tx_route_config,
+	.pamp_off = msm_snddev_tx_route_deconfig,
+};
+
+static struct platform_device msm_headset_mic_device = {
+	.name = "snddev_icodec",
+	.id = 6,
+	.dev = { .platform_data = &snddev_headset_mic_data },
+};
+
+static struct snddev_mi2s_data snddev_mi2s_fm_tx_data = {
+	.capability = SNDDEV_CAP_TX ,
+	.name = "fmradio_stereo_tx",
+	.copp_id = 2,
+	.acdb_id = ACDB_ID_FM_TX,
+	.channel_mode = 2,
+	.sd_lines = MI2S_SD_3,
+	.route = NULL,
+	.deroute = NULL,
+	.default_sample_rate = 48000,
+};
+
+static struct platform_device  msm_snddev_mi2s_fm_tx_device = {
+	.name = "snddev_mi2s",
+	.id = 1,
+	.dev = { .platform_data = &snddev_mi2s_fm_tx_data},
+};
+
+static struct snddev_mi2s_data snddev_mi2s_fm_rx_data = {
+	.capability = SNDDEV_CAP_RX ,
+	.name = "fmradio_stereo_rx",
+	.copp_id = 3,
+	.acdb_id = ACDB_ID_FM_RX,
+	.channel_mode = 2,
+	.sd_lines = MI2S_SD_3,
+	.route = NULL,
+	.deroute = NULL,
+	.default_sample_rate = 48000,
+};
+
+static struct platform_device  msm_snddev_mi2s_fm_rx_device = {
+	.name = "snddev_mi2s",
+	.id = 2,
+	.dev = { .platform_data = &snddev_mi2s_fm_rx_data},
+};
+
+static struct snddev_ecodec_data snddev_bt_sco_earpiece_data = {
+	.capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE),
+	.name = "bt_sco_rx",
+	.copp_id = 1,
+	.acdb_id = ACDB_ID_BT_SCO_SPKR,
+	.channel_mode = 1,
+	.conf_pcm_ctl_val = BT_SCO_PCM_CTL_VAL,
+	.conf_aux_codec_intf = BT_SCO_AUX_CODEC_INTF,
+	.conf_data_format_padding_val = BT_SCO_DATA_FORMAT_PADDING,
+	.max_voice_rx_vol[VOC_NB_INDEX] = 400,
+	.min_voice_rx_vol[VOC_NB_INDEX] = -1100,
+	.max_voice_rx_vol[VOC_WB_INDEX] = 400,
+	.min_voice_rx_vol[VOC_WB_INDEX] = -1100,
+};
+
+static struct snddev_ecodec_data snddev_bt_sco_mic_data = {
+	.capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+	.name = "bt_sco_tx",
+	.copp_id = 1,
+	.acdb_id = ACDB_ID_BT_SCO_MIC,
+	.channel_mode = 1,
+	.conf_pcm_ctl_val = BT_SCO_PCM_CTL_VAL,
+	.conf_aux_codec_intf = BT_SCO_AUX_CODEC_INTF,
+	.conf_data_format_padding_val = BT_SCO_DATA_FORMAT_PADDING,
+};
+
+static struct platform_device msm_bt_sco_earpiece_device = {
+	.name = "msm_snddev_ecodec",
+	.id = 0,
+	.dev = { .platform_data = &snddev_bt_sco_earpiece_data },
+};
+
+static struct platform_device msm_bt_sco_mic_device = {
+	.name = "msm_snddev_ecodec",
+	.id = 1,
+	.dev = { .platform_data = &snddev_bt_sco_mic_data },
+};
+
+static struct adie_codec_action_unit headset_ab_cpls_48KHz_osr256_actions[] =
+	HEADSET_AB_CPLS_48000_OSR_256;
+
+static struct adie_codec_hwsetting_entry headset_ab_cpls_settings[] = {
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions = headset_ab_cpls_48KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(headset_ab_cpls_48KHz_osr256_actions),
+	}
+};
+
+static struct adie_codec_dev_profile headset_ab_cpls_profile = {
+	.path_type = ADIE_CODEC_RX,
+	.settings = headset_ab_cpls_settings,
+	.setting_sz = ARRAY_SIZE(headset_ab_cpls_settings),
+};
+
+static struct snddev_icodec_data snddev_ihs_stereo_rx_data = {
+	.capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE),
+	.name = "headset_stereo_rx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_HEADSET_SPKR_STEREO,
+	.profile = &headset_ab_cpls_profile,
+	.channel_mode = 2,
+	.pmctl_id = NULL,
+	.pmctl_id_sz = 0,
+	.default_sample_rate = 48000,
+	.pamp_on = NULL,
+	.pamp_off = NULL,
+	.property = SIDE_TONE_MASK,
+	.voltage_on = msm_snddev_hsed_voltage_on,
+	.voltage_off = msm_snddev_hsed_voltage_off,
+	.max_voice_rx_vol[VOC_NB_INDEX] = -700,
+	.min_voice_rx_vol[VOC_NB_INDEX] = -2200,
+	.max_voice_rx_vol[VOC_WB_INDEX] = -900,
+	.min_voice_rx_vol[VOC_WB_INDEX] = -2400,
+};
+
+static struct platform_device msm_headset_stereo_device = {
+	.name = "snddev_icodec",
+	.id = 2,
+	.dev = { .platform_data = &snddev_ihs_stereo_rx_data },
+};
+
+/*debug FS interface is exposed to test Class D and class AB mode
+ * amplifers for headset device folloowing options are supported
+ * 0 -> settings will be restored
+ * 1 -> Cladd D mode is selected
+ * 2 -> Class AB mode is selected
+*/
+#ifdef CONFIG_DEBUG_FS
+static struct adie_codec_action_unit
+	ihs_stereo_rx_class_d_legacy_48KHz_osr256_actions[] =
+	HPH_PRI_D_LEG_STEREO;
+
+static struct adie_codec_hwsetting_entry
+	ihs_stereo_rx_class_d_legacy_settings[] = {
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions =
+		ihs_stereo_rx_class_d_legacy_48KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE
+		(ihs_stereo_rx_class_d_legacy_48KHz_osr256_actions),
+	}
+};
+
+static struct adie_codec_action_unit
+	ihs_stereo_rx_class_ab_legacy_48KHz_osr256_actions[] =
+	HPH_PRI_AB_LEG_STEREO;
+
+static struct adie_codec_hwsetting_entry
+	ihs_stereo_rx_class_ab_legacy_settings[] = {
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions =
+		ihs_stereo_rx_class_ab_legacy_48KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE
+		(ihs_stereo_rx_class_ab_legacy_48KHz_osr256_actions),
+	}
+};
+
+static void snddev_hsed_config_modify_setting(int type)
+{
+	struct platform_device *device;
+	struct snddev_icodec_data *icodec_data;
+
+	device = &msm_headset_stereo_device;
+	icodec_data = (struct snddev_icodec_data *)device->dev.platform_data;
+
+	if (icodec_data) {
+		if (type == 1) {
+			icodec_data->voltage_on = NULL;
+			icodec_data->voltage_off = NULL;
+			icodec_data->profile->settings =
+				ihs_stereo_rx_class_d_legacy_settings;
+			icodec_data->profile->setting_sz =
+			ARRAY_SIZE(ihs_stereo_rx_class_d_legacy_settings);
+		} else if (type == 2) {
+			icodec_data->voltage_on = NULL;
+			icodec_data->voltage_off = NULL;
+			icodec_data->profile->settings =
+				ihs_stereo_rx_class_ab_legacy_settings;
+			icodec_data->profile->setting_sz =
+			ARRAY_SIZE(ihs_stereo_rx_class_ab_legacy_settings);
+		}
+	}
+}
+
+static void snddev_hsed_config_restore_setting(void)
+{
+	struct platform_device *device;
+	struct snddev_icodec_data *icodec_data;
+
+	device = &msm_headset_stereo_device;
+	icodec_data = device->dev.platform_data;
+
+	if (icodec_data) {
+		icodec_data->voltage_on = msm_snddev_hsed_voltage_on;
+		icodec_data->voltage_off = msm_snddev_hsed_voltage_off;
+		icodec_data->profile->settings = headset_ab_cpls_settings;
+		icodec_data->profile->setting_sz =
+			ARRAY_SIZE(headset_ab_cpls_settings);
+	}
+}
+
+static ssize_t snddev_hsed_config_debug_write(struct file *filp,
+	const char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+	char *lb_str = filp->private_data;
+	char cmd;
+
+	if (get_user(cmd, ubuf))
+		return -EFAULT;
+
+	if (!strcmp(lb_str, "msm_hsed_config")) {
+		switch (cmd) {
+		case '0':
+			snddev_hsed_config_restore_setting();
+			break;
+
+		case '1':
+			snddev_hsed_config_modify_setting(1);
+			break;
+
+		case '2':
+			snddev_hsed_config_modify_setting(2);
+			break;
+
+		default:
+			break;
+		}
+	}
+	return cnt;
+}
+
+static int snddev_hsed_config_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static const struct file_operations snddev_hsed_config_debug_fops = {
+	.open = snddev_hsed_config_debug_open,
+	.write = snddev_hsed_config_debug_write
+};
+#endif
+
+static enum hsed_controller ispk_pmctl_id[] = {PM_HSED_CONTROLLER_0};
+
+static struct snddev_icodec_data snddev_ispkr_mic_data = {
+	.capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+	.name = "speaker_mono_tx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_SPKR_PHONE_MIC,
+	.profile = &imic_ffa_profile,
+	.channel_mode = 1,
+	.pmctl_id = ispk_pmctl_id,
+	.pmctl_id_sz = ARRAY_SIZE(ispk_pmctl_id),
+	.default_sample_rate = 48000,
+	.pamp_on = msm_snddev_tx_route_config,
+	.pamp_off = msm_snddev_tx_route_deconfig,
+};
+
+static struct platform_device msm_ispkr_mic_device = {
+	.name = "snddev_icodec",
+	.id = 18,
+	.dev = { .platform_data = &snddev_ispkr_mic_data },
+};
+
+static struct adie_codec_action_unit idual_mic_endfire_8KHz_osr256_actions[] =
+	AMIC_DUAL_8000_OSR_256;
+
+static struct adie_codec_hwsetting_entry idual_mic_endfire_settings[] = {
+	{
+		.freq_plan = 8000,
+		.osr = 256,
+		.actions = idual_mic_endfire_8KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(idual_mic_endfire_8KHz_osr256_actions),
+	}, /* 8KHz profile can be used for 16KHz */
+	{
+		.freq_plan = 16000,
+		.osr = 256,
+		.actions = idual_mic_endfire_8KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(idual_mic_endfire_8KHz_osr256_actions),
+	}, /* 8KHz profile can be used for 48KHz */
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions = idual_mic_endfire_8KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(idual_mic_endfire_8KHz_osr256_actions),
+	}
+};
+
+static struct adie_codec_dev_profile idual_mic_endfire_profile = {
+	.path_type = ADIE_CODEC_TX,
+	.settings = idual_mic_endfire_settings,
+	.setting_sz = ARRAY_SIZE(idual_mic_endfire_settings),
+};
+
+static enum hsed_controller idual_mic_endfire_pmctl_id[] = {
+	PM_HSED_CONTROLLER_0, PM_HSED_CONTROLLER_2
+};
+
+static struct snddev_icodec_data snddev_idual_mic_endfire_data = {
+	.capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+	.name = "handset_dual_mic_endfire_tx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_HANDSET_MIC_ENDFIRE,
+	.profile = &idual_mic_endfire_profile,
+	.channel_mode = 2,
+	.default_sample_rate = 48000,
+	.pmctl_id = idual_mic_endfire_pmctl_id,
+	.pmctl_id_sz = ARRAY_SIZE(idual_mic_endfire_pmctl_id),
+	.pamp_on = NULL,
+	.pamp_off = NULL,
+};
+
+static struct platform_device msm_idual_mic_endfire_device = {
+	.name = "snddev_icodec",
+	.id = 12,
+	.dev = { .platform_data = &snddev_idual_mic_endfire_data },
+};
+
+static struct snddev_icodec_data snddev_spk_idual_mic_endfire_data = {
+	.capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+	.name = "speaker_dual_mic_endfire_tx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_SPKR_PHONE_MIC_ENDFIRE,
+	.profile = &idual_mic_endfire_profile,
+	.channel_mode = 2,
+	.default_sample_rate = 48000,
+	.pmctl_id = idual_mic_endfire_pmctl_id,
+	.pmctl_id_sz = ARRAY_SIZE(idual_mic_endfire_pmctl_id),
+	.pamp_on = NULL,
+	.pamp_off = NULL,
+};
+
+static struct platform_device msm_spk_idual_mic_endfire_device = {
+	.name = "snddev_icodec",
+	.id = 14,
+	.dev = { .platform_data = &snddev_spk_idual_mic_endfire_data },
+};
+
+static struct adie_codec_action_unit itty_mono_tx_actions[] =
+	TTY_HEADSET_MONO_TX_8000_OSR_256;
+
+static struct adie_codec_hwsetting_entry itty_mono_tx_settings[] = {
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions = itty_mono_tx_actions,
+		.action_sz = ARRAY_SIZE(itty_mono_tx_actions),
+	},
+};
+
+static struct adie_codec_dev_profile itty_mono_tx_profile = {
+	.path_type = ADIE_CODEC_TX,
+	.settings = itty_mono_tx_settings,
+	.setting_sz = ARRAY_SIZE(itty_mono_tx_settings),
+};
+
+static struct snddev_icodec_data snddev_itty_mono_tx_data = {
+	.capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE | SNDDEV_CAP_TTY),
+	.name = "tty_headset_mono_tx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_TTY_HEADSET_MIC,
+	.profile = &itty_mono_tx_profile,
+	.channel_mode = 1,
+	.default_sample_rate = 48000,
+	.pmctl_id = NULL,
+	.pmctl_id_sz = 0,
+	.pamp_on = NULL,
+	.pamp_off = NULL,
+};
+
+static struct platform_device msm_itty_mono_tx_device = {
+	.name = "snddev_icodec",
+	.id = 16,
+	.dev = { .platform_data = &snddev_itty_mono_tx_data },
+};
+
+static struct adie_codec_action_unit itty_mono_rx_actions[] =
+	TTY_HEADSET_MONO_RX_8000_OSR_256;
+
+static struct adie_codec_hwsetting_entry itty_mono_rx_settings[] = {
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions = itty_mono_rx_actions,
+		.action_sz = ARRAY_SIZE(itty_mono_rx_actions),
+	},
+};
+
+static struct adie_codec_dev_profile itty_mono_rx_profile = {
+	.path_type = ADIE_CODEC_RX,
+	.settings = itty_mono_rx_settings,
+	.setting_sz = ARRAY_SIZE(itty_mono_rx_settings),
+};
+
+static struct snddev_icodec_data snddev_itty_mono_rx_data = {
+	.capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE | SNDDEV_CAP_TTY),
+	.name = "tty_headset_mono_rx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_TTY_HEADSET_SPKR,
+	.profile = &itty_mono_rx_profile,
+	.channel_mode = 1,
+	.default_sample_rate = 48000,
+	.pamp_on = NULL,
+	.pamp_off = NULL,
+	.max_voice_rx_vol[VOC_NB_INDEX] = 0,
+	.min_voice_rx_vol[VOC_NB_INDEX] = 0,
+	.max_voice_rx_vol[VOC_WB_INDEX] = 0,
+	.min_voice_rx_vol[VOC_WB_INDEX] = 0,
+};
+
+static struct platform_device msm_itty_mono_rx_device = {
+	.name = "snddev_icodec",
+	.id = 17,
+	.dev = { .platform_data = &snddev_itty_mono_rx_data },
+};
+
+static struct snddev_virtual_data snddev_a2dp_tx_data = {
+	.capability = SNDDEV_CAP_TX,
+	.name = "a2dp_tx",
+	.copp_id = 5,
+	.acdb_id = PSEUDO_ACDB_ID,
+};
+
+static struct snddev_virtual_data snddev_a2dp_rx_data = {
+	.capability = SNDDEV_CAP_RX,
+	.name = "a2dp_rx",
+	.copp_id = 2,
+	.acdb_id = PSEUDO_ACDB_ID,
+};
+
+static struct platform_device msm_a2dp_rx_device = {
+	.name = "snddev_virtual",
+	.id = 0,
+	.dev = { .platform_data = &snddev_a2dp_rx_data },
+};
+
+static struct platform_device msm_a2dp_tx_device = {
+	.name = "snddev_virtual",
+	.id = 1,
+	.dev = { .platform_data = &snddev_a2dp_tx_data },
+};
+
+static struct snddev_virtual_data snddev_uplink_rx_data = {
+	.capability = SNDDEV_CAP_RX,
+	.name = "uplink_rx",
+	.copp_id = 5,
+	.acdb_id = PSEUDO_ACDB_ID,
+};
+
+static struct platform_device msm_uplink_rx_device = {
+	.name = "snddev_virtual",
+	.id = 2,
+	.dev = { .platform_data = &snddev_uplink_rx_data },
+};
+
+static struct snddev_icodec_data\
+		snddev_idual_mic_endfire_real_stereo_data = {
+	.capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+	.name = "handset_dual_mic_endfire_tx_real_stereo",
+	.copp_id = 0,
+	.acdb_id = PSEUDO_ACDB_ID,
+	.profile = &idual_mic_endfire_profile,
+	.channel_mode = REAL_STEREO_CHANNEL_MODE,
+	.default_sample_rate = 48000,
+	.pmctl_id = idual_mic_endfire_pmctl_id,
+	.pmctl_id_sz = ARRAY_SIZE(idual_mic_endfire_pmctl_id),
+	.pamp_on = NULL,
+	.pamp_off = NULL,
+};
+
+static struct platform_device msm_real_stereo_tx_device = {
+	.name = "snddev_icodec",
+	.id = 26,
+	.dev = { .platform_data =
+			&snddev_idual_mic_endfire_real_stereo_data },
+};
+
+static struct adie_codec_action_unit ihs_ffa_mono_rx_48KHz_osr256_actions[] =
+	HEADSET_RX_CAPLESS_48000_OSR_256;
+
+static struct adie_codec_hwsetting_entry ihs_ffa_mono_rx_settings[] = {
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions = ihs_ffa_mono_rx_48KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(ihs_ffa_mono_rx_48KHz_osr256_actions),
+	}
+};
+
+static struct adie_codec_dev_profile ihs_ffa_mono_rx_profile = {
+	.path_type = ADIE_CODEC_RX,
+	.settings = ihs_ffa_mono_rx_settings,
+	.setting_sz = ARRAY_SIZE(ihs_ffa_mono_rx_settings),
+};
+
+static struct snddev_icodec_data snddev_ihs_ffa_mono_rx_data = {
+	.capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE),
+	.name = "headset_mono_rx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_HEADSET_SPKR_MONO,
+	.profile = &ihs_ffa_mono_rx_profile,
+	.channel_mode = 1,
+	.default_sample_rate = 48000,
+	.pamp_on = msm_snddev_hsed_voltage_on,
+	.pamp_off = msm_snddev_hsed_voltage_off,
+	.max_voice_rx_vol[VOC_NB_INDEX] = -700,
+	.min_voice_rx_vol[VOC_NB_INDEX] = -2200,
+	.max_voice_rx_vol[VOC_WB_INDEX] = -900,
+	.min_voice_rx_vol[VOC_WB_INDEX] = -2400,
+	.property = SIDE_TONE_MASK,
+};
+
+static struct platform_device msm_ihs_ffa_mono_rx_device = {
+	.name = "snddev_icodec",
+	.id = 5,
+	.dev = { .platform_data = &snddev_ihs_ffa_mono_rx_data },
+};
+
+static struct adie_codec_action_unit
+	ihs_stereo_speaker_stereo_rx_48KHz_osr256_actions[] =
+	HEADSET_STEREO_SPEAKER_STEREO_RX_CAPLESS_48000_OSR_256;
+
+
+static struct adie_codec_hwsetting_entry
+	ihs_stereo_speaker_stereo_rx_settings[] = {
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions = ihs_stereo_speaker_stereo_rx_48KHz_osr256_actions,
+		.action_sz =
+		ARRAY_SIZE(ihs_stereo_speaker_stereo_rx_48KHz_osr256_actions),
+	}
+};
+
+static struct adie_codec_dev_profile ihs_stereo_speaker_stereo_rx_profile = {
+	.path_type = ADIE_CODEC_RX,
+	.settings = ihs_stereo_speaker_stereo_rx_settings,
+	.setting_sz = ARRAY_SIZE(ihs_stereo_speaker_stereo_rx_settings),
+};
+
+static struct snddev_icodec_data snddev_ihs_stereo_speaker_stereo_rx_data = {
+	.capability = (SNDDEV_CAP_RX | SNDDEV_CAP_VOICE),
+	.name = "headset_stereo_speaker_stereo_rx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_HEADSET_STEREO_PLUS_SPKR_STEREO_RX,
+	.profile = &ihs_stereo_speaker_stereo_rx_profile,
+	.channel_mode = 2,
+	.default_sample_rate = 48000,
+	.pamp_on = msm_snddev_poweramp_on,
+	.pamp_off = msm_snddev_poweramp_off,
+	.voltage_on = msm_snddev_hsed_voltage_on,
+	.voltage_off = msm_snddev_hsed_voltage_off,
+	.max_voice_rx_vol[VOC_NB_INDEX] = -500,
+	.min_voice_rx_vol[VOC_NB_INDEX] = -2000,
+	.max_voice_rx_vol[VOC_WB_INDEX] = -900,
+	.min_voice_rx_vol[VOC_WB_INDEX] = -2400,
+};
+
+static struct platform_device msm_ihs_stereo_speaker_stereo_rx_device = {
+	.name = "snddev_icodec",
+	.id = 21,
+	.dev = { .platform_data = &snddev_ihs_stereo_speaker_stereo_rx_data },
+};
+
+static struct adie_codec_action_unit ispk_dual_mic_bs_8KHz_osr256_actions[] =
+	HS_DMIC2_STEREO_8000_OSR_256;
+
+static struct adie_codec_hwsetting_entry ispk_dual_mic_bs_settings[] = {
+	{
+		.freq_plan = 8000,
+		.osr = 256,
+		.actions = ispk_dual_mic_bs_8KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(ispk_dual_mic_bs_8KHz_osr256_actions),
+	}, /* 8KHz profile can be used for 16Khz */
+	{
+		.freq_plan = 16000,
+		.osr = 256,
+		.actions = ispk_dual_mic_bs_8KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(ispk_dual_mic_bs_8KHz_osr256_actions),
+	}, /* 8KHz profile can be used for 48KHz */
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions = ispk_dual_mic_bs_8KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(ispk_dual_mic_bs_8KHz_osr256_actions),
+	},
+};
+
+static enum hsed_controller idual_mic_broadside_pmctl_id[] = {
+	PM_HSED_CONTROLLER_0, PM_HSED_CONTROLLER_2
+};
+
+static struct adie_codec_dev_profile ispk_dual_mic_bs_profile = {
+	.path_type = ADIE_CODEC_TX,
+	.settings = ispk_dual_mic_bs_settings,
+	.setting_sz = ARRAY_SIZE(ispk_dual_mic_bs_settings),
+};
+static struct snddev_icodec_data snddev_spk_idual_mic_broadside_data = {
+	.capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+	.name = "speaker_dual_mic_broadside_tx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_SPKR_PHONE_MIC_BROADSIDE,
+	.profile = &ispk_dual_mic_bs_profile,
+	.channel_mode = 2,
+	.default_sample_rate = 48000,
+	.pmctl_id = idual_mic_broadside_pmctl_id,
+	.pmctl_id_sz = ARRAY_SIZE(idual_mic_broadside_pmctl_id),
+	.pamp_on = NULL,
+	.pamp_off = NULL,
+};
+
+static struct platform_device msm_spk_idual_mic_broadside_device = {
+	.name = "snddev_icodec",
+	.id = 15,
+	.dev = { .platform_data = &snddev_spk_idual_mic_broadside_data },
+};
+
+static struct adie_codec_action_unit idual_mic_bs_8KHz_osr256_actions[] =
+	HS_DMIC2_STEREO_8000_OSR_256;
+
+static struct adie_codec_hwsetting_entry idual_mic_broadside_settings[] = {
+	{
+		.freq_plan = 8000,
+		.osr = 256,
+		.actions = idual_mic_bs_8KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(idual_mic_bs_8KHz_osr256_actions),
+	}, /* 8KHz profile can be used for 16KHz */
+	{
+		.freq_plan = 16000,
+		.osr = 256,
+		.actions = idual_mic_bs_8KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(idual_mic_bs_8KHz_osr256_actions),
+	}, /* 8KHz profile can be used for 16KHz */
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions = idual_mic_bs_8KHz_osr256_actions,
+		.action_sz = ARRAY_SIZE(idual_mic_bs_8KHz_osr256_actions),
+	}
+};
+
+static struct adie_codec_dev_profile idual_mic_broadside_profile = {
+	.path_type = ADIE_CODEC_TX,
+	.settings = idual_mic_broadside_settings,
+	.setting_sz = ARRAY_SIZE(idual_mic_broadside_settings),
+};
+
+static struct snddev_icodec_data snddev_idual_mic_broadside_data = {
+	.capability = (SNDDEV_CAP_TX | SNDDEV_CAP_VOICE),
+	.name = "handset_dual_mic_broadside_tx",
+	.copp_id = 0,
+	.acdb_id = ACDB_ID_HANDSET_MIC_BROADSIDE,
+	.profile = &idual_mic_broadside_profile,
+	.channel_mode = 2,
+	.default_sample_rate = 48000,
+	.pmctl_id = idual_mic_broadside_pmctl_id,
+	.pmctl_id_sz = ARRAY_SIZE(idual_mic_broadside_pmctl_id),
+	.pamp_on = NULL,
+	.pamp_off = NULL,
+};
+
+static struct platform_device msm_idual_mic_broadside_device = {
+	.name = "snddev_icodec",
+	.id = 13,
+	.dev = { .platform_data = &snddev_idual_mic_broadside_data },
+};
+
+static struct snddev_mi2s_data snddev_mi2s_stereo_rx_data = {
+	.capability = SNDDEV_CAP_RX ,
+	.name = "hdmi_stereo_rx",
+	.copp_id = 3,
+	.acdb_id = ACDB_ID_HDMI,
+	.channel_mode = 2,
+	.sd_lines = MI2S_SD_0,
+	.route = msm_snddev_tx_route_config,
+	.deroute = msm_snddev_tx_route_deconfig,
+	.default_sample_rate = 48000,
+};
+
+static struct platform_device msm_snddev_mi2s_stereo_rx_device = {
+	.name = "snddev_mi2s",
+	.id = 0,
+	.dev = { .platform_data = &snddev_mi2s_stereo_rx_data },
+};
+
+static struct adie_codec_action_unit auxpga_lb_lo_actions[] =
+	LB_AUXPGA_LO_STEREO;
+
+static struct adie_codec_hwsetting_entry auxpga_lb_lo_settings[] = {
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions = auxpga_lb_lo_actions,
+		.action_sz = ARRAY_SIZE(auxpga_lb_lo_actions),
+	},
+};
+
+static struct adie_codec_dev_profile auxpga_lb_lo_profile = {
+	.path_type = ADIE_CODEC_LB,
+	.settings = auxpga_lb_lo_settings,
+	.setting_sz = ARRAY_SIZE(auxpga_lb_lo_settings),
+};
+
+static struct snddev_icodec_data snddev_auxpga_lb_lo_data = {
+	.capability = SNDDEV_CAP_LB,
+	.name = "auxpga_loopback_lo",
+	.copp_id = 0,
+	.acdb_id = PSEUDO_ACDB_ID,
+	.profile = &auxpga_lb_lo_profile,
+	.channel_mode = 2,
+	.default_sample_rate = 48000,
+	.pamp_on = msm_snddev_poweramp_on,
+	.pamp_off = msm_snddev_poweramp_off,
+	.dev_vol_type = SNDDEV_DEV_VOL_ANALOG,
+};
+
+static struct platform_device msm_auxpga_lb_lo_device = {
+	.name = "snddev_icodec",
+	.id = 27,
+	.dev = { .platform_data = &snddev_auxpga_lb_lo_data },
+};
+
+static struct adie_codec_action_unit auxpga_lb_hs_actions[] =
+	LB_AUXPGA_HPH_AB_CPLS_STEREO;
+
+static struct adie_codec_hwsetting_entry auxpga_lb_hs_settings[] = {
+	{
+		.freq_plan = 48000,
+		.osr = 256,
+		.actions = auxpga_lb_hs_actions,
+		.action_sz = ARRAY_SIZE(auxpga_lb_hs_actions),
+	},
+};
+
+static struct adie_codec_dev_profile auxpga_lb_hs_profile = {
+	.path_type = ADIE_CODEC_LB,
+	.settings = auxpga_lb_hs_settings,
+	.setting_sz = ARRAY_SIZE(auxpga_lb_hs_settings),
+};
+
+static struct snddev_icodec_data snddev_auxpga_lb_hs_data = {
+	.capability = SNDDEV_CAP_LB,
+	.name = "auxpga_loopback_hs",
+	.copp_id = 0,
+	.acdb_id = PSEUDO_ACDB_ID,
+	.profile = &auxpga_lb_hs_profile,
+	.channel_mode = 2,
+	.default_sample_rate = 48000,
+	.voltage_on = msm_snddev_hsed_voltage_on,
+	.voltage_off = msm_snddev_hsed_voltage_off,
+	.dev_vol_type = SNDDEV_DEV_VOL_ANALOG,
+};
+
+static struct platform_device msm_auxpga_lb_hs_device = {
+	.name = "snddev_icodec",
+	.id = 25,
+	.dev = { .platform_data = &snddev_auxpga_lb_hs_data },
+};
+
+static struct platform_device *snd_devices_ffa[] __initdata = {
+	&msm_iearpiece_ffa_device,
+	&msm_imic_ffa_device,
+	&msm_ispkr_stereo_device,
+	&msm_headset_mic_device,
+	&msm_ihs_ffa_mono_rx_device,
+	&msm_snddev_mi2s_fm_rx_device,
+	&msm_snddev_mi2s_fm_tx_device,
+	&msm_bt_sco_earpiece_device,
+	&msm_bt_sco_mic_device,
+	&msm_ispkr_mic_device,
+	&msm_headset_stereo_device,
+	&msm_idual_mic_endfire_device,
+	&msm_spk_idual_mic_endfire_device,
+	&msm_itty_mono_tx_device,
+	&msm_itty_mono_rx_device,
+	&msm_a2dp_rx_device,
+	&msm_a2dp_tx_device,
+	&msm_uplink_rx_device,
+	&msm_real_stereo_tx_device,
+	&msm_ihs_stereo_speaker_stereo_rx_device,
+	&msm_spk_idual_mic_broadside_device,
+	&msm_idual_mic_broadside_device,
+	&msm_snddev_mi2s_stereo_rx_device,
+	&msm_auxpga_lb_hs_device,
+	&msm_auxpga_lb_lo_device,
+};
+
+void __ref msm_snddev_init_timpani(void)
+{
+	platform_add_devices(snd_devices_ffa,
+			ARRAY_SIZE(snd_devices_ffa));
+#ifdef CONFIG_DEBUG_FS
+	debugfs_hsed_config = debugfs_create_file("msm_hsed_config",
+				S_IFREG | S_IWUGO, NULL,
+		(void *) "msm_hsed_config", &snddev_hsed_config_debug_fops);
+	if (!debugfs_hsed_config)
+		pr_err("failed to create msm_head_config debug fs entry\n");
+#endif
+
+}
diff --git a/arch/arm/mach-msm/qdsp5v2/snddev_ecodec.c b/arch/arm/mach-msm/qdsp5v2/snddev_ecodec.c
new file mode 100644
index 0000000..a5da912
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/snddev_ecodec.c
@@ -0,0 +1,484 @@
+/* Copyright (c) 2009,2011 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/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <asm/uaccess.h>
+#include <mach/qdsp5v2/snddev_ecodec.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/qdsp5v2/audio_interct.h>
+#include <mach/qdsp5v2/aux_pcm.h>
+#include <mach/qdsp5v2/afe.h>
+#include <mach/debug_mm.h>
+#include <linux/slab.h>
+
+/* Context for each external codec device */
+struct snddev_ecodec_state {
+	struct snddev_ecodec_data *data;
+	u32 sample_rate;
+	bool enabled;
+};
+
+/* Global state for the driver */
+struct snddev_ecodec_drv_state {
+	struct mutex dev_lock;
+	u32 rx_active; /* ensure one rx device at a time */
+	u32 tx_active; /* ensure one tx device at a time */
+	struct clk *lpa_core_clk;
+	struct clk *ecodec_clk;
+};
+
+#define ADSP_CTL 1
+
+static struct snddev_ecodec_drv_state snddev_ecodec_drv;
+
+static int snddev_ecodec_open_rx(struct snddev_ecodec_state *ecodec)
+{
+	int rc = 0;
+	struct snddev_ecodec_drv_state *drv = &snddev_ecodec_drv;
+	struct msm_afe_config afe_config;
+	int ret = 0;
+
+	MM_DBG("snddev_ecodec_open_rx\n");
+
+	if (!drv->tx_active) {
+		/* request GPIO */
+		rc = aux_pcm_gpios_request();
+		if (rc) {
+			MM_ERR("GPIO enable failed\n");
+			goto done;
+		}
+		/* config clocks */
+		clk_enable(drv->lpa_core_clk);
+
+		/*if long sync is selected in aux PCM interface
+		ecodec clock is updated to work with 128KHz,
+		if short sync is selected ecodec clock is updated to
+		work with 2.048MHz frequency, actual clock output is
+		different than the SW configuration by factor of two*/
+		if (!(ecodec->data->conf_aux_codec_intf &
+			AUX_CODEC_CTL__AUX_CODEC_MODE__I2S_V)) {
+			if (ecodec->data->conf_aux_codec_intf &
+				AUX_CODEC_CTL__AUX_PCM_MODE__AUX_MASTER_V) {
+				MM_DBG("Update ecodec clock to 128 KHz, long "
+					"sync in master mode is selected\n");
+				ret = clk_set_rate(drv->ecodec_clk, 256000);
+				if (ret < 0)
+					MM_ERR("Error updating ecodec clock"
+							" to 128KHz\n");
+			} else if (ecodec->data->conf_aux_codec_intf &
+				AUX_CODEC_CTL__AUX_PCM_MODE__PRIM_SLAVE_V) {
+				MM_DBG("Update ecodec clock to 2 MHz, short"
+					" sync in slave mode is selected\n");
+				ret = clk_set_rate(drv->ecodec_clk, 4096000);
+				if (ret < 0)
+					MM_ERR("Error updating ecodec clock"
+							" to 2.048MHz\n");
+			} else {
+				MM_DBG("Update ecodec clock to 2 MHz, short"
+					" sync in master mode is selected\n");
+				ret = clk_set_rate(drv->ecodec_clk, 4096000);
+				if (ret < 0)
+					MM_ERR("Error updating ecodec clock"
+							" to 2.048MHz\n");
+			}
+		}
+
+		/* enable ecodec clk */
+		clk_enable(drv->ecodec_clk);
+
+		/* let ADSP confiure AUX PCM regs */
+		aux_codec_adsp_codec_ctl_en(ADSP_CTL);
+
+		/* let adsp configure pcm path */
+		aux_codec_pcm_path_ctl_en(ADSP_CTL);
+
+		/* choose ADSP_A */
+		audio_interct_aux_regsel(AUDIO_ADSP_A);
+		audio_interct_tpcm_source(AUDIO_ADSP_A);
+		audio_interct_rpcm_source(AUDIO_ADSP_A);
+
+		clk_disable(drv->lpa_core_clk);
+
+		/* send AUX_CODEC_CONFIG to AFE */
+		rc = afe_config_aux_codec(ecodec->data->conf_pcm_ctl_val,
+				ecodec->data->conf_aux_codec_intf,
+				ecodec->data->conf_data_format_padding_val);
+		if (IS_ERR_VALUE(rc))
+			goto error;
+	}
+	/* send CODEC CONFIG to AFE */
+	afe_config.sample_rate = ecodec->sample_rate / 1000;
+	afe_config.channel_mode = ecodec->data->channel_mode;
+	afe_config.volume = AFE_VOLUME_UNITY;
+	rc = afe_enable(AFE_HW_PATH_AUXPCM_RX, &afe_config);
+	if (IS_ERR_VALUE(rc)) {
+		if (!drv->tx_active) {
+			aux_pcm_gpios_free();
+			clk_disable(drv->ecodec_clk);
+		}
+		goto done;
+	}
+
+	ecodec->enabled = 1;
+	return 0;
+
+error:
+	aux_pcm_gpios_free();
+	clk_disable(drv->ecodec_clk);
+done:
+	return rc;
+}
+
+static int snddev_ecodec_close_rx(struct snddev_ecodec_state *ecodec)
+{
+	struct snddev_ecodec_drv_state *drv = &snddev_ecodec_drv;
+
+	/* free GPIO */
+	if (!drv->tx_active) {
+		aux_pcm_gpios_free();
+		clk_disable(drv->ecodec_clk);
+	}
+
+	/* disable AFE */
+	afe_disable(AFE_HW_PATH_AUXPCM_RX);
+
+	ecodec->enabled = 0;
+
+	return 0;
+}
+
+static int snddev_ecodec_open_tx(struct snddev_ecodec_state *ecodec)
+{
+	int rc = 0;
+	struct snddev_ecodec_drv_state *drv = &snddev_ecodec_drv;
+	struct msm_afe_config afe_config;
+	int ret = 0;
+
+	MM_DBG("snddev_ecodec_open_tx\n");
+
+	/* request GPIO */
+	if (!drv->rx_active) {
+		rc = aux_pcm_gpios_request();
+		if (rc) {
+			MM_ERR("GPIO enable failed\n");
+			goto done;
+		}
+		/* config clocks */
+		clk_enable(drv->lpa_core_clk);
+
+		/*if long sync is selected in aux PCM interface
+		ecodec clock is updated to work with 128KHz,
+		if short sync is selected ecodec clock is updated to
+		work with 2.048MHz frequency, actual clock output is
+		different than the SW configuration by factor of two*/
+		if (!(ecodec->data->conf_aux_codec_intf &
+			AUX_CODEC_CTL__AUX_CODEC_MODE__I2S_V)) {
+			if (ecodec->data->conf_aux_codec_intf &
+				AUX_CODEC_CTL__AUX_PCM_MODE__AUX_MASTER_V) {
+				MM_DBG("Update ecodec clock to 128 KHz, long "
+					"sync in master mode is selected\n");
+				ret = clk_set_rate(drv->ecodec_clk, 256000);
+				if (ret < 0)
+					MM_ERR("Error updating ecodec clock"
+							" to 128KHz\n");
+			} else if (ecodec->data->conf_aux_codec_intf &
+				AUX_CODEC_CTL__AUX_PCM_MODE__PRIM_SLAVE_V) {
+				MM_DBG("Update ecodec clock to 2 MHz, short"
+					" sync in slave mode is selected\n");
+				ret = clk_set_rate(drv->ecodec_clk, 4096000);
+				if (ret < 0)
+					MM_ERR("Error updating ecodec clock"
+							" to 2.048MHz\n");
+			} else {
+				MM_DBG("Update ecodec clock to 2 MHz, short"
+					" sync in master mode is selected\n");
+				ret = clk_set_rate(drv->ecodec_clk, 4096000);
+				if (ret < 0)
+					MM_ERR("Error updating ecodec clock"
+							" to 2.048MHz\n");
+			}
+		}
+
+		/* enable ecodec clk */
+		clk_enable(drv->ecodec_clk);
+
+		/* let ADSP confiure AUX PCM regs */
+		aux_codec_adsp_codec_ctl_en(ADSP_CTL);
+
+		/* let adsp configure pcm path */
+		aux_codec_pcm_path_ctl_en(ADSP_CTL);
+
+		/* choose ADSP_A */
+		audio_interct_aux_regsel(AUDIO_ADSP_A);
+		audio_interct_tpcm_source(AUDIO_ADSP_A);
+		audio_interct_rpcm_source(AUDIO_ADSP_A);
+
+		clk_disable(drv->lpa_core_clk);
+
+		/* send AUX_CODEC_CONFIG to AFE */
+		rc = afe_config_aux_codec(ecodec->data->conf_pcm_ctl_val,
+			ecodec->data->conf_aux_codec_intf,
+			ecodec->data->conf_data_format_padding_val);
+		if (IS_ERR_VALUE(rc))
+			goto error;
+	}
+	/* send CODEC CONFIG to AFE */
+	afe_config.sample_rate = ecodec->sample_rate / 1000;
+	afe_config.channel_mode = ecodec->data->channel_mode;
+	afe_config.volume = AFE_VOLUME_UNITY;
+	rc = afe_enable(AFE_HW_PATH_AUXPCM_TX, &afe_config);
+	if (IS_ERR_VALUE(rc)) {
+		if (!drv->rx_active) {
+			aux_pcm_gpios_free();
+			clk_disable(drv->ecodec_clk);
+		}
+		goto done;
+	}
+
+	ecodec->enabled = 1;
+	return 0;
+
+error:
+	clk_disable(drv->ecodec_clk);
+	aux_pcm_gpios_free();
+done:
+	return rc;
+}
+
+static int snddev_ecodec_close_tx(struct snddev_ecodec_state *ecodec)
+{
+	struct snddev_ecodec_drv_state *drv = &snddev_ecodec_drv;
+
+	/* free GPIO */
+	if (!drv->rx_active) {
+		aux_pcm_gpios_free();
+		clk_disable(drv->ecodec_clk);
+	}
+
+	/* disable AFE */
+	afe_disable(AFE_HW_PATH_AUXPCM_TX);
+
+	ecodec->enabled = 0;
+
+	return 0;
+}
+
+
+static int snddev_ecodec_open(struct msm_snddev_info *dev_info)
+{
+	int rc = 0;
+	struct snddev_ecodec_state *ecodec;
+	struct snddev_ecodec_drv_state *drv = &snddev_ecodec_drv;
+
+	if (!dev_info) {
+		rc = -EINVAL;
+		goto error;
+	}
+
+	ecodec = dev_info->private_data;
+
+	if (ecodec->data->capability & SNDDEV_CAP_RX) {
+		mutex_lock(&drv->dev_lock);
+		if (drv->rx_active) {
+			mutex_unlock(&drv->dev_lock);
+			rc = -EBUSY;
+			goto error;
+		}
+		rc = snddev_ecodec_open_rx(ecodec);
+		if (!IS_ERR_VALUE(rc))
+			drv->rx_active = 1;
+		mutex_unlock(&drv->dev_lock);
+	} else {
+		mutex_lock(&drv->dev_lock);
+		if (drv->tx_active) {
+			mutex_unlock(&drv->dev_lock);
+			rc = -EBUSY;
+			goto error;
+		}
+		rc = snddev_ecodec_open_tx(ecodec);
+		if (!IS_ERR_VALUE(rc))
+			drv->tx_active = 1;
+		mutex_unlock(&drv->dev_lock);
+	}
+error:
+	return rc;
+}
+
+static int snddev_ecodec_close(struct msm_snddev_info *dev_info)
+{
+	int rc = 0;
+	struct snddev_ecodec_state *ecodec;
+	struct snddev_ecodec_drv_state *drv = &snddev_ecodec_drv;
+	if (!dev_info) {
+		rc = -EINVAL;
+		goto error;
+	}
+
+	ecodec = dev_info->private_data;
+
+	if (ecodec->data->capability & SNDDEV_CAP_RX) {
+		mutex_lock(&drv->dev_lock);
+		if (!drv->rx_active) {
+			mutex_unlock(&drv->dev_lock);
+			rc = -EPERM;
+			goto error;
+		}
+		rc = snddev_ecodec_close_rx(ecodec);
+		if (!IS_ERR_VALUE(rc))
+			drv->rx_active = 0;
+		mutex_unlock(&drv->dev_lock);
+	} else {
+		mutex_lock(&drv->dev_lock);
+		if (!drv->tx_active) {
+			mutex_unlock(&drv->dev_lock);
+			rc = -EPERM;
+			goto error;
+		}
+		rc = snddev_ecodec_close_tx(ecodec);
+		if (!IS_ERR_VALUE(rc))
+			drv->tx_active = 0;
+		mutex_unlock(&drv->dev_lock);
+	}
+
+error:
+	return rc;
+}
+
+static int snddev_ecodec_set_freq(struct msm_snddev_info *dev_info, u32 rate)
+{
+	int rc = 0;
+
+	if (!dev_info) {
+		rc = -EINVAL;
+		goto error;
+	}
+	return 8000;
+
+error:
+	return rc;
+}
+
+static int snddev_ecodec_probe(struct platform_device *pdev)
+{
+	int rc = 0, i;
+	struct snddev_ecodec_data *pdata;
+	struct msm_snddev_info *dev_info;
+	struct snddev_ecodec_state *ecodec;
+
+	if (!pdev || !pdev->dev.platform_data) {
+		printk(KERN_ALERT "Invalid caller \n");
+		rc = -1;
+		goto error;
+	}
+	pdata = pdev->dev.platform_data;
+
+	ecodec = kzalloc(sizeof(struct snddev_ecodec_state), GFP_KERNEL);
+	if (!ecodec) {
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	dev_info = kzalloc(sizeof(struct msm_snddev_info), GFP_KERNEL);
+	if (!dev_info) {
+		kfree(ecodec);
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	dev_info->name = pdata->name;
+	dev_info->copp_id = pdata->copp_id;
+	dev_info->acdb_id = pdata->acdb_id;
+	dev_info->private_data = (void *) ecodec;
+	dev_info->dev_ops.open = snddev_ecodec_open;
+	dev_info->dev_ops.close = snddev_ecodec_close;
+	dev_info->dev_ops.set_freq = snddev_ecodec_set_freq;
+	dev_info->dev_ops.enable_sidetone = NULL;
+	dev_info->capability = pdata->capability;
+	dev_info->opened = 0;
+
+	msm_snddev_register(dev_info);
+	ecodec->data = pdata;
+	ecodec->sample_rate = 8000; /* Default to 8KHz */
+	 if (pdata->capability & SNDDEV_CAP_RX) {
+		for (i = 0; i < VOC_RX_VOL_ARRAY_NUM; i++) {
+			dev_info->max_voc_rx_vol[i] =
+				pdata->max_voice_rx_vol[i];
+			dev_info->min_voc_rx_vol[i] =
+				pdata->min_voice_rx_vol[i];
+		}
+	}
+error:
+	return rc;
+}
+
+static int snddev_ecodec_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static struct platform_driver snddev_ecodec_driver = {
+	.probe = snddev_ecodec_probe,
+	.remove = snddev_ecodec_remove,
+	.driver = { .name = "msm_snddev_ecodec" }
+};
+
+static int __init snddev_ecodec_init(void)
+{
+	int rc = 0;
+	struct snddev_ecodec_drv_state *ecodec_drv = &snddev_ecodec_drv;
+
+	MM_INFO("snddev_ecodec_init\n");
+	rc = platform_driver_register(&snddev_ecodec_driver);
+	if (IS_ERR_VALUE(rc))
+		goto error_platform_driver;
+	ecodec_drv->ecodec_clk = clk_get(NULL, "ecodec_clk");
+	if (IS_ERR(ecodec_drv->ecodec_clk))
+		goto error_ecodec_clk;
+	ecodec_drv->lpa_core_clk = clk_get(NULL, "lpa_core_clk");
+	if (IS_ERR(ecodec_drv->lpa_core_clk))
+		goto error_lpa_core_clk;
+
+
+	mutex_init(&ecodec_drv->dev_lock);
+	ecodec_drv->rx_active = 0;
+	ecodec_drv->tx_active = 0;
+	return 0;
+
+error_lpa_core_clk:
+	clk_put(ecodec_drv->ecodec_clk);
+error_ecodec_clk:
+	platform_driver_unregister(&snddev_ecodec_driver);
+error_platform_driver:
+
+	MM_ERR("encounter error\n");
+	return -ENODEV;
+}
+
+static void __exit snddev_ecodec_exit(void)
+{
+	struct snddev_ecodec_drv_state *ecodec_drv = &snddev_ecodec_drv;
+
+	platform_driver_unregister(&snddev_ecodec_driver);
+	clk_put(ecodec_drv->ecodec_clk);
+
+	return;
+}
+
+module_init(snddev_ecodec_init);
+module_exit(snddev_ecodec_exit);
+
+MODULE_DESCRIPTION("ECodec Sound Device driver");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5v2/snddev_icodec.c b/arch/arm/mach-msm/qdsp5v2/snddev_icodec.c
new file mode 100644
index 0000000..dbeda82
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/snddev_icodec.c
@@ -0,0 +1,1211 @@
+/* Copyright (c) 2009-2011, 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/platform_device.h>
+#include <linux/mfd/msm-adie-codec.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/debugfs.h>
+#include <asm/uaccess.h>
+#include <mach/qdsp5v2/snddev_icodec.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/qdsp5v2/audio_interct.h>
+#include <mach/qdsp5v2/mi2s.h>
+#include <mach/qdsp5v2/afe.h>
+#include <mach/qdsp5v2/lpa.h>
+#include <mach/qdsp5v2/marimba_profile.h>
+#include <mach/vreg.h>
+#include <mach/pmic.h>
+#include <linux/wakelock.h>
+#include <mach/debug_mm.h>
+#include <mach/rpc_pmapp.h>
+#include <mach/qdsp5v2/audio_acdb_def.h>
+#include <linux/slab.h>
+
+#define SMPS_AUDIO_PLAYBACK_ID	"AUPB"
+#define SMPS_AUDIO_RECORD_ID	"AURC"
+
+#define SNDDEV_ICODEC_PCM_SZ 32 /* 16 bit / sample stereo mode */
+#define SNDDEV_ICODEC_MUL_FACTOR 3 /* Multi by 8 Shift by 3  */
+#define SNDDEV_ICODEC_CLK_RATE(freq) \
+	(((freq) * (SNDDEV_ICODEC_PCM_SZ)) << (SNDDEV_ICODEC_MUL_FACTOR))
+
+#ifdef CONFIG_DEBUG_FS
+static struct adie_codec_action_unit debug_rx_actions[] =
+		HANDSET_RX_8000_OSR_256;
+
+static struct adie_codec_action_unit debug_tx_lb_actions[] = {
+	{ ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF },
+	{ ADIE_CODEC_ACTION_ENTRY,
+	ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x01)},
+	{ ADIE_CODEC_ACTION_ENTRY,
+	ADIE_CODEC_PACK_ENTRY(0x80, 0x01, 0x00) },
+	{ ADIE_CODEC_ACTION_ENTRY,
+	ADIE_CODEC_PACK_ENTRY(0x8A, 0x30, 0x30)},
+	{ ADIE_CODEC_ACTION_ENTRY,
+	ADIE_CODEC_PACK_ENTRY(0x11, 0xfc, 0xfc)},
+	{ ADIE_CODEC_ACTION_ENTRY,
+	ADIE_CODEC_PACK_ENTRY(0x13, 0xfc, 0x58)},
+	{ ADIE_CODEC_ACTION_ENTRY,
+	ADIE_CODEC_PACK_ENTRY(0x14, 0xff, 0x65)},
+	{ ADIE_CODEC_ACTION_ENTRY,
+	ADIE_CODEC_PACK_ENTRY(0x15, 0xff, 0x64)},
+	{ ADIE_CODEC_ACTION_ENTRY,
+	ADIE_CODEC_PACK_ENTRY(0x82, 0xff, 0x5C)},
+	{ ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY },
+	{ ADIE_CODEC_ACTION_ENTRY,
+	ADIE_CODEC_PACK_ENTRY(0x0D, 0xF0, 0xd0)},
+	{ ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8},
+	{ ADIE_CODEC_ACTION_ENTRY,
+	ADIE_CODEC_PACK_ENTRY(0x83, 0x14, 0x14)},
+	{ ADIE_CODEC_ACTION_ENTRY,
+	ADIE_CODEC_PACK_ENTRY(0x86, 0xff, 0x00)},
+	{ ADIE_CODEC_ACTION_ENTRY,
+	ADIE_CODEC_PACK_ENTRY(0x8A, 0x50, 0x40)},
+	{ ADIE_CODEC_ACTION_ENTRY,
+	ADIE_CODEC_PACK_ENTRY(0x91, 0xFF, 0x01)}, /* Start loop back */
+	{ ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY},
+	{ ADIE_CODEC_ACTION_ENTRY,
+	ADIE_CODEC_PACK_ENTRY(0x8A, 0x10, 0x30)},
+	{ ADIE_CODEC_ACTION_ENTRY,
+	ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)},
+	{ ADIE_CODEC_ACTION_ENTRY,
+	ADIE_CODEC_PACK_ENTRY(0x83, 0x14, 0x00)},
+	{ ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF},
+	{ ADIE_CODEC_ACTION_ENTRY,
+	ADIE_CODEC_PACK_ENTRY(0x11, 0xff, 0x00)}
+};
+
+static struct adie_codec_action_unit debug_tx_actions[] =
+		HANDSET_TX_8000_OSR_256;
+
+static struct adie_codec_hwsetting_entry debug_rx_settings[] = {
+	{
+		.freq_plan = 8000,
+		.osr = 256,
+		.actions = debug_rx_actions,
+		.action_sz = ARRAY_SIZE(debug_rx_actions),
+	}
+};
+
+static struct adie_codec_hwsetting_entry debug_tx_settings[] = {
+	{
+		.freq_plan = 8000,
+		.osr = 256,
+		.actions = debug_tx_actions,
+		.action_sz = ARRAY_SIZE(debug_tx_actions),
+	}
+};
+
+static struct adie_codec_hwsetting_entry debug_tx_lb_settings[] = {
+	{
+		.freq_plan = 8000,
+		.osr = 256,
+		.actions = debug_tx_lb_actions,
+		.action_sz = ARRAY_SIZE(debug_tx_lb_actions),
+	}
+};
+
+static struct adie_codec_dev_profile debug_rx_profile = {
+	.path_type = ADIE_CODEC_RX,
+	.settings = debug_rx_settings,
+	.setting_sz = ARRAY_SIZE(debug_rx_settings),
+};
+
+static struct adie_codec_dev_profile debug_tx_profile = {
+	.path_type = ADIE_CODEC_TX,
+	.settings = debug_tx_settings,
+	.setting_sz = ARRAY_SIZE(debug_tx_settings),
+};
+
+static struct adie_codec_dev_profile debug_tx_lb_profile = {
+	.path_type = ADIE_CODEC_TX,
+	.settings = debug_tx_lb_settings,
+	.setting_sz = ARRAY_SIZE(debug_tx_lb_settings),
+};
+#endif /* CONFIG_DEBUG_FS */
+
+/* Context for each internal codec sound device */
+struct snddev_icodec_state {
+	struct snddev_icodec_data *data;
+	struct adie_codec_path *adie_path;
+	u32 sample_rate;
+	u32 enabled;
+};
+
+/* Global state for the driver */
+struct snddev_icodec_drv_state {
+	struct mutex rx_lock;
+	struct mutex lb_lock;
+	struct mutex tx_lock;
+	u32 rx_active; /* ensure one rx device at a time */
+	u32 tx_active; /* ensure one tx device at a time */
+	struct clk *rx_mclk;
+	struct clk *rx_sclk;
+	struct clk *tx_mclk;
+	struct clk *tx_sclk;
+	struct clk *lpa_codec_clk;
+	struct clk *lpa_core_clk;
+	struct clk *lpa_p_clk;
+	struct lpa_drv *lpa;
+
+	struct wake_lock rx_idlelock;
+	struct wake_lock tx_idlelock;
+};
+
+static struct snddev_icodec_drv_state snddev_icodec_drv;
+
+static int snddev_icodec_open_rx(struct snddev_icodec_state *icodec)
+{
+	int trc, err;
+	int smps_mode = PMAPP_SMPS_MODE_VOTE_PWM;
+	struct msm_afe_config afe_config;
+	struct snddev_icodec_drv_state *drv = &snddev_icodec_drv;
+	struct lpa_codec_config lpa_config;
+
+	wake_lock(&drv->rx_idlelock);
+
+	if ((icodec->data->acdb_id == ACDB_ID_HEADSET_SPKR_MONO) ||
+		(icodec->data->acdb_id == ACDB_ID_HEADSET_SPKR_STEREO)) {
+		/* Vote PMAPP_SMPS_MODE_VOTE_PFM for headset */
+		smps_mode = PMAPP_SMPS_MODE_VOTE_PFM;
+		MM_DBG("snddev_icodec_open_rx: PMAPP_SMPS_MODE_VOTE_PFM \n");
+	} else
+		MM_DBG("snddev_icodec_open_rx: PMAPP_SMPS_MODE_VOTE_PWM \n");
+
+	/* Vote for SMPS mode*/
+	err = pmapp_smps_mode_vote(SMPS_AUDIO_PLAYBACK_ID,
+				PMAPP_VREG_S4, smps_mode);
+	if (err != 0)
+		MM_ERR("pmapp_smps_mode_vote error %d\n", err);
+
+	/* enable MI2S RX master block */
+	/* enable MI2S RX bit clock */
+	trc = clk_set_rate(drv->rx_mclk,
+		SNDDEV_ICODEC_CLK_RATE(icodec->sample_rate));
+	if (IS_ERR_VALUE(trc))
+		goto error_invalid_freq;
+	clk_enable(drv->rx_mclk);
+	clk_enable(drv->rx_sclk);
+	/* clk_set_rate(drv->lpa_codec_clk, 1); */ /* Remove if use pcom */
+	clk_enable(drv->lpa_p_clk);
+	clk_enable(drv->lpa_codec_clk);
+	clk_enable(drv->lpa_core_clk);
+
+	/* Enable LPA sub system
+	 */
+	drv->lpa = lpa_get();
+	if (!drv->lpa)
+		goto error_lpa;
+	lpa_config.sample_rate = icodec->sample_rate;
+	lpa_config.sample_width = 16;
+	lpa_config.output_interface = LPA_OUTPUT_INTF_WB_CODEC;
+	lpa_config.num_channels = icodec->data->channel_mode;
+	lpa_cmd_codec_config(drv->lpa, &lpa_config);
+
+	/* Set audio interconnect reg to LPA */
+	audio_interct_codec(AUDIO_INTERCT_LPA);
+
+	/* Set MI2S */
+	mi2s_set_codec_output_path((icodec->data->channel_mode == 2 ?
+	MI2S_CHAN_STEREO : MI2S_CHAN_MONO_PACKED), WT_16_BIT);
+
+	if (icodec->data->voltage_on)
+		icodec->data->voltage_on();
+
+	/* Configure ADIE */
+	trc = adie_codec_open(icodec->data->profile, &icodec->adie_path);
+	if (IS_ERR_VALUE(trc))
+		goto error_adie;
+	/* OSR default to 256, can be changed for power optimization
+	 * If OSR is to be changed, need clock API for setting the divider
+	 */
+	adie_codec_setpath(icodec->adie_path, icodec->sample_rate, 256);
+	/* Start AFE */
+	afe_config.sample_rate = icodec->sample_rate / 1000;
+	afe_config.channel_mode = icodec->data->channel_mode;
+	afe_config.volume = AFE_VOLUME_UNITY;
+	trc = afe_enable(AFE_HW_PATH_CODEC_RX, &afe_config);
+	if (IS_ERR_VALUE(trc))
+		goto error_afe;
+	lpa_cmd_enable_codec(drv->lpa, 1);
+	/* Enable ADIE */
+	adie_codec_proceed_stage(icodec->adie_path, ADIE_CODEC_DIGITAL_READY);
+	adie_codec_proceed_stage(icodec->adie_path,
+					ADIE_CODEC_DIGITAL_ANALOG_READY);
+
+	/* Enable power amplifier */
+	if (icodec->data->pamp_on)
+		icodec->data->pamp_on();
+
+	icodec->enabled = 1;
+
+	wake_unlock(&drv->rx_idlelock);
+	return 0;
+
+error_afe:
+	adie_codec_close(icodec->adie_path);
+	icodec->adie_path = NULL;
+error_adie:
+	lpa_put(drv->lpa);
+error_lpa:
+	clk_disable(drv->lpa_p_clk);
+	clk_disable(drv->lpa_codec_clk);
+	clk_disable(drv->lpa_core_clk);
+	clk_disable(drv->rx_sclk);
+	clk_disable(drv->rx_mclk);
+error_invalid_freq:
+
+	MM_ERR("encounter error\n");
+
+	wake_unlock(&drv->rx_idlelock);
+	return -ENODEV;
+}
+
+static int snddev_icodec_open_tx(struct snddev_icodec_state *icodec)
+{
+	int trc;
+	int i, err;
+	struct msm_afe_config afe_config;
+	struct snddev_icodec_drv_state *drv = &snddev_icodec_drv;;
+
+	wake_lock(&drv->tx_idlelock);
+
+	/* Vote for PWM mode*/
+	err = pmapp_smps_mode_vote(SMPS_AUDIO_RECORD_ID,
+			PMAPP_VREG_S4, PMAPP_SMPS_MODE_VOTE_PWM);
+	if (err != 0)
+		MM_ERR("pmapp_smps_mode_vote error %d\n", err);
+
+	/* Reuse pamp_on for TX platform-specific setup  */
+	if (icodec->data->pamp_on)
+		icodec->data->pamp_on();
+
+	for (i = 0; i < icodec->data->pmctl_id_sz; i++) {
+		pmic_hsed_enable(icodec->data->pmctl_id[i],
+			 PM_HSED_ENABLE_PWM_TCXO);
+	}
+
+	/* enable MI2S TX master block */
+	/* enable MI2S TX bit clock */
+	trc = clk_set_rate(drv->tx_mclk,
+		SNDDEV_ICODEC_CLK_RATE(icodec->sample_rate));
+	if (IS_ERR_VALUE(trc))
+		goto error_invalid_freq;
+	clk_enable(drv->tx_mclk);
+	clk_enable(drv->tx_sclk);
+
+	/* Set MI2S */
+	mi2s_set_codec_input_path((icodec->data->channel_mode ==
+				REAL_STEREO_CHANNEL_MODE ? MI2S_CHAN_STEREO :
+				(icodec->data->channel_mode == 2 ?
+				 MI2S_CHAN_STEREO : MI2S_CHAN_MONO_RAW)),
+				WT_16_BIT);
+	/* Configure ADIE */
+	trc = adie_codec_open(icodec->data->profile, &icodec->adie_path);
+	if (IS_ERR_VALUE(trc))
+		goto error_adie;
+	/* Enable ADIE */
+	adie_codec_setpath(icodec->adie_path, icodec->sample_rate, 256);
+	adie_codec_proceed_stage(icodec->adie_path, ADIE_CODEC_DIGITAL_READY);
+	adie_codec_proceed_stage(icodec->adie_path,
+	ADIE_CODEC_DIGITAL_ANALOG_READY);
+
+	/* Start AFE */
+	afe_config.sample_rate = icodec->sample_rate / 1000;
+	afe_config.channel_mode = icodec->data->channel_mode;
+	afe_config.volume = AFE_VOLUME_UNITY;
+	trc = afe_enable(AFE_HW_PATH_CODEC_TX, &afe_config);
+	if (IS_ERR_VALUE(trc))
+		goto error_afe;
+
+
+	icodec->enabled = 1;
+
+	wake_unlock(&drv->tx_idlelock);
+	return 0;
+
+error_afe:
+	adie_codec_close(icodec->adie_path);
+	icodec->adie_path = NULL;
+error_adie:
+	clk_disable(drv->tx_sclk);
+	clk_disable(drv->tx_mclk);
+error_invalid_freq:
+
+	/* Disable mic bias */
+	for (i = 0; i < icodec->data->pmctl_id_sz; i++) {
+		pmic_hsed_enable(icodec->data->pmctl_id[i],
+			 PM_HSED_ENABLE_OFF);
+	}
+
+	if (icodec->data->pamp_off)
+		icodec->data->pamp_off();
+
+	MM_ERR("encounter error\n");
+
+	wake_unlock(&drv->tx_idlelock);
+	return -ENODEV;
+}
+
+static int snddev_icodec_close_lb(struct snddev_icodec_state *icodec)
+{
+	/* Disable power amplifier */
+	if (icodec->data->pamp_off)
+		icodec->data->pamp_off();
+
+	if (icodec->adie_path) {
+		adie_codec_proceed_stage(icodec->adie_path,
+						ADIE_CODEC_DIGITAL_OFF);
+		adie_codec_close(icodec->adie_path);
+		icodec->adie_path = NULL;
+	}
+	if (icodec->data->voltage_off)
+		icodec->data->voltage_off();
+
+	return 0;
+}
+
+static int snddev_icodec_close_rx(struct snddev_icodec_state *icodec)
+{
+	int err;
+	struct snddev_icodec_drv_state *drv = &snddev_icodec_drv;
+
+	wake_lock(&drv->rx_idlelock);
+
+	/* Remove the vote for SMPS mode*/
+	err = pmapp_smps_mode_vote(SMPS_AUDIO_PLAYBACK_ID,
+			PMAPP_VREG_S4, PMAPP_SMPS_MODE_VOTE_DONTCARE);
+	if (err != 0)
+		MM_ERR("pmapp_smps_mode_vote error %d\n", err);
+
+	/* Disable power amplifier */
+	if (icodec->data->pamp_off)
+		icodec->data->pamp_off();
+
+	/* Disable ADIE */
+	adie_codec_proceed_stage(icodec->adie_path, ADIE_CODEC_DIGITAL_OFF);
+	adie_codec_close(icodec->adie_path);
+	icodec->adie_path = NULL;
+
+	afe_disable(AFE_HW_PATH_CODEC_RX);
+
+	if (icodec->data->voltage_off)
+		icodec->data->voltage_off();
+
+	/* Disable LPA Sub system */
+	lpa_cmd_enable_codec(drv->lpa, 0);
+	lpa_put(drv->lpa);
+
+	/* Disable LPA clocks */
+	clk_disable(drv->lpa_p_clk);
+	clk_disable(drv->lpa_codec_clk);
+	clk_disable(drv->lpa_core_clk);
+
+	/* Disable MI2S RX master block */
+	/* Disable MI2S RX bit clock */
+	clk_disable(drv->rx_sclk);
+	clk_disable(drv->rx_mclk);
+
+	icodec->enabled = 0;
+
+	wake_unlock(&drv->rx_idlelock);
+	return 0;
+}
+
+static int snddev_icodec_close_tx(struct snddev_icodec_state *icodec)
+{
+	struct snddev_icodec_drv_state *drv = &snddev_icodec_drv;
+	int i, err;
+
+	wake_lock(&drv->tx_idlelock);
+
+	/* Remove the vote for SMPS mode*/
+	err = pmapp_smps_mode_vote(SMPS_AUDIO_RECORD_ID,
+			PMAPP_VREG_S4, PMAPP_SMPS_MODE_VOTE_DONTCARE);
+	if (err != 0)
+		MM_ERR("pmapp_smps_mode_vote error %d\n", err);
+
+	afe_disable(AFE_HW_PATH_CODEC_TX);
+
+	/* Disable ADIE */
+	adie_codec_proceed_stage(icodec->adie_path, ADIE_CODEC_DIGITAL_OFF);
+	adie_codec_close(icodec->adie_path);
+	icodec->adie_path = NULL;
+
+	/* Disable MI2S TX master block */
+	/* Disable MI2S TX bit clock */
+	clk_disable(drv->tx_sclk);
+	clk_disable(drv->tx_mclk);
+
+	/* Disable mic bias */
+	for (i = 0; i < icodec->data->pmctl_id_sz; i++) {
+		pmic_hsed_enable(icodec->data->pmctl_id[i],
+			 PM_HSED_ENABLE_OFF);
+	}
+
+	/* Reuse pamp_off for TX platform-specific setup  */
+	if (icodec->data->pamp_off)
+		icodec->data->pamp_off();
+
+	icodec->enabled = 0;
+
+	wake_unlock(&drv->tx_idlelock);
+	return 0;
+}
+
+static int snddev_icodec_open_lb(struct snddev_icodec_state *icodec)
+{
+	int trc;
+	trc = adie_codec_open(icodec->data->profile, &icodec->adie_path);
+	if (IS_ERR_VALUE(trc))
+		pr_err("%s: adie codec open failed\n", __func__);
+	else
+		adie_codec_setpath(icodec->adie_path,
+						icodec->sample_rate, 256);
+
+	if (icodec->adie_path)
+		adie_codec_proceed_stage(icodec->adie_path,
+					ADIE_CODEC_DIGITAL_ANALOG_READY);
+	if (icodec->data->pamp_on)
+		icodec->data->pamp_on();
+
+	icodec->enabled = 1;
+	return 0;
+}
+
+static int snddev_icodec_set_device_volume_impl(
+		struct msm_snddev_info *dev_info, u32 volume)
+{
+	struct snddev_icodec_state *icodec;
+	u8 afe_path_id;
+
+	int rc = 0;
+
+	icodec = dev_info->private_data;
+
+	if (icodec->data->capability & SNDDEV_CAP_RX)
+		afe_path_id = AFE_HW_PATH_CODEC_RX;
+	else
+		afe_path_id = AFE_HW_PATH_CODEC_TX;
+
+	if (icodec->data->dev_vol_type & SNDDEV_DEV_VOL_DIGITAL) {
+
+		rc = adie_codec_set_device_digital_volume(icodec->adie_path,
+				icodec->data->channel_mode ==
+						REAL_STEREO_CHANNEL_MODE ?
+					2 : icodec->data->channel_mode, volume);
+		if (rc < 0) {
+			MM_ERR("unable to set_device_digital_volume for"
+				"%s volume in percentage = %u\n",
+				dev_info->name, volume);
+			return rc;
+		}
+
+	} else if (icodec->data->dev_vol_type & SNDDEV_DEV_VOL_ANALOG) {
+		rc = adie_codec_set_device_analog_volume(icodec->adie_path,
+				icodec->data->channel_mode ==
+						REAL_STEREO_CHANNEL_MODE ?
+					2 : icodec->data->channel_mode, volume);
+		if (rc < 0) {
+			MM_ERR("unable to set_device_analog_volume for"
+				"%s volume in percentage = %u\n",
+				dev_info->name, volume);
+			return rc;
+		}
+	}
+	else {
+		MM_ERR("Invalid device volume control\n");
+		return -EPERM;
+	}
+	return rc;
+}
+
+static int snddev_icodec_close(struct msm_snddev_info *dev_info)
+{
+	int rc = 0;
+	struct snddev_icodec_state *icodec;
+	struct snddev_icodec_drv_state *drv = &snddev_icodec_drv;
+	if (!dev_info) {
+		rc = -EINVAL;
+		goto error;
+	}
+
+	icodec = dev_info->private_data;
+
+	if (icodec->data->capability & SNDDEV_CAP_RX) {
+		mutex_lock(&drv->rx_lock);
+		if (!drv->rx_active) {
+			mutex_unlock(&drv->rx_lock);
+			rc = -EPERM;
+			goto error;
+		}
+		rc = snddev_icodec_close_rx(icodec);
+		if (!IS_ERR_VALUE(rc))
+			drv->rx_active = 0;
+		mutex_unlock(&drv->rx_lock);
+	} else if (icodec->data->capability & SNDDEV_CAP_LB) {
+		mutex_lock(&drv->lb_lock);
+		rc = snddev_icodec_close_lb(icodec);
+		mutex_unlock(&drv->lb_lock);
+	} else {
+		mutex_lock(&drv->tx_lock);
+		if (!drv->tx_active) {
+			mutex_unlock(&drv->tx_lock);
+			rc = -EPERM;
+			goto error;
+		}
+		rc = snddev_icodec_close_tx(icodec);
+		if (!IS_ERR_VALUE(rc))
+			drv->tx_active = 0;
+		mutex_unlock(&drv->tx_lock);
+	}
+
+error:
+	return rc;
+}
+
+static int snddev_icodec_open(struct msm_snddev_info *dev_info)
+{
+	int rc = 0;
+	struct snddev_icodec_state *icodec;
+	struct snddev_icodec_drv_state *drv = &snddev_icodec_drv;
+
+	if (!dev_info) {
+		rc = -EINVAL;
+		goto error;
+	}
+
+	icodec = dev_info->private_data;
+
+	if (icodec->data->capability & SNDDEV_CAP_RX) {
+		mutex_lock(&drv->rx_lock);
+		if (drv->rx_active) {
+			mutex_unlock(&drv->rx_lock);
+			rc = -EBUSY;
+			goto error;
+		}
+		rc = snddev_icodec_open_rx(icodec);
+
+		if (!IS_ERR_VALUE(rc)) {
+			drv->rx_active = 1;
+			if ((icodec->data->dev_vol_type & (
+				SNDDEV_DEV_VOL_DIGITAL |
+				SNDDEV_DEV_VOL_ANALOG)))
+				rc = snddev_icodec_set_device_volume_impl(
+						dev_info, dev_info->dev_volume);
+				if (IS_ERR_VALUE(rc)) {
+					MM_ERR("Failed to set device volume"
+						" impl for rx device\n");
+					snddev_icodec_close(dev_info);
+					mutex_unlock(&drv->rx_lock);
+					goto error;
+				}
+		}
+		mutex_unlock(&drv->rx_lock);
+	} else if (icodec->data->capability & SNDDEV_CAP_LB) {
+		mutex_lock(&drv->lb_lock);
+		rc = snddev_icodec_open_lb(icodec);
+		if (!IS_ERR_VALUE(rc)) {
+			if ((icodec->data->dev_vol_type & (
+							SNDDEV_DEV_VOL_DIGITAL |
+							SNDDEV_DEV_VOL_ANALOG)))
+				rc = snddev_icodec_set_device_volume_impl(
+							dev_info,
+							 dev_info->dev_volume);
+				if (rc < 0)
+					MM_ERR("failed to set device volume\n");
+		}
+		mutex_unlock(&drv->lb_lock);
+	} else {
+		mutex_lock(&drv->tx_lock);
+		if (drv->tx_active) {
+			mutex_unlock(&drv->tx_lock);
+			rc = -EBUSY;
+			goto error;
+		}
+		rc = snddev_icodec_open_tx(icodec);
+
+		if (!IS_ERR_VALUE(rc)) {
+			drv->tx_active = 1;
+			if ((icodec->data->dev_vol_type & (
+				SNDDEV_DEV_VOL_DIGITAL |
+				SNDDEV_DEV_VOL_ANALOG)))
+				rc = snddev_icodec_set_device_volume_impl(
+						dev_info, dev_info->dev_volume);
+				if (IS_ERR_VALUE(rc)) {
+					MM_ERR("Failed to set device volume"
+						" impl for tx device\n");
+					snddev_icodec_close(dev_info);
+					mutex_unlock(&drv->tx_lock);
+					goto error;
+				}
+		}
+		mutex_unlock(&drv->tx_lock);
+	}
+error:
+	return rc;
+}
+
+static int snddev_icodec_check_freq(u32 req_freq)
+{
+	int rc = -EINVAL;
+
+	if ((req_freq != 0) && (req_freq >= 8000) && (req_freq <= 48000)) {
+		if ((req_freq == 8000) || (req_freq == 11025) ||
+			(req_freq == 12000) || (req_freq == 16000) ||
+			(req_freq == 22050) || (req_freq == 24000) ||
+			(req_freq == 32000) || (req_freq == 44100) ||
+			(req_freq == 48000)) {
+				rc = 0;
+		} else
+			MM_INFO("Unsupported Frequency:%d\n", req_freq);
+		}
+		return rc;
+}
+
+static int snddev_icodec_set_freq(struct msm_snddev_info *dev_info, u32 rate)
+{
+	int rc;
+	struct snddev_icodec_state *icodec;
+
+	if (!dev_info) {
+		rc = -EINVAL;
+		goto error;
+	}
+
+	icodec = dev_info->private_data;
+	if (adie_codec_freq_supported(icodec->data->profile, rate) != 0) {
+		rc = -EINVAL;
+		goto error;
+	} else {
+		if (snddev_icodec_check_freq(rate) != 0) {
+			rc = -EINVAL;
+			goto error;
+		} else
+			icodec->sample_rate = rate;
+	}
+
+	if (icodec->enabled) {
+		snddev_icodec_close(dev_info);
+		snddev_icodec_open(dev_info);
+	}
+
+	return icodec->sample_rate;
+
+error:
+	return rc;
+}
+
+static int snddev_icodec_enable_sidetone(struct msm_snddev_info *dev_info,
+	u32 enable)
+{
+	int rc = 0;
+	struct snddev_icodec_state *icodec;
+	struct snddev_icodec_drv_state *drv = &snddev_icodec_drv;
+
+	if (!dev_info) {
+		MM_ERR("invalid dev_info\n");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	icodec = dev_info->private_data;
+
+	if (icodec->data->capability & SNDDEV_CAP_RX) {
+		mutex_lock(&drv->rx_lock);
+		if (!drv->rx_active || !dev_info->opened) {
+			MM_ERR("dev not active\n");
+			rc = -EPERM;
+			mutex_unlock(&drv->rx_lock);
+			goto error;
+		}
+		rc = adie_codec_enable_sidetone(icodec->adie_path, enable);
+		mutex_unlock(&drv->rx_lock);
+	} else {
+		rc = -EINVAL;
+		MM_ERR("rx device only\n");
+	}
+
+error:
+	return rc;
+
+}
+
+int snddev_icodec_set_device_volume(struct msm_snddev_info *dev_info,
+		u32 volume)
+{
+	struct snddev_icodec_state *icodec;
+	struct mutex *lock;
+	struct snddev_icodec_drv_state *drv = &snddev_icodec_drv;
+	int rc = -EPERM;
+
+	if (!dev_info) {
+		MM_INFO("device not intilized.\n");
+		return  -EINVAL;
+	}
+
+	icodec = dev_info->private_data;
+
+	if (!(icodec->data->dev_vol_type & (SNDDEV_DEV_VOL_DIGITAL
+				| SNDDEV_DEV_VOL_ANALOG))) {
+
+		MM_INFO("device %s does not support device volume "
+				"control.", dev_info->name);
+		return -EPERM;
+	}
+	dev_info->dev_volume =  volume;
+
+	if (icodec->data->capability & SNDDEV_CAP_RX)
+		lock = &drv->rx_lock;
+	else if (icodec->data->capability & SNDDEV_CAP_LB)
+		lock = &drv->lb_lock;
+	else
+		lock = &drv->tx_lock;
+
+	mutex_lock(lock);
+
+	rc = snddev_icodec_set_device_volume_impl(dev_info,
+			dev_info->dev_volume);
+	mutex_unlock(lock);
+	return rc;
+}
+
+static int snddev_icodec_probe(struct platform_device *pdev)
+{
+	int rc = 0, i;
+	struct snddev_icodec_data *pdata;
+	struct msm_snddev_info *dev_info;
+	struct snddev_icodec_state *icodec;
+
+	if (!pdev || !pdev->dev.platform_data) {
+		printk(KERN_ALERT "Invalid caller \n");
+		rc = -1;
+		goto error;
+	}
+	pdata = pdev->dev.platform_data;
+	if ((pdata->capability & SNDDEV_CAP_RX) &&
+	   (pdata->capability & SNDDEV_CAP_TX)) {
+		MM_ERR("invalid device data either RX or TX\n");
+		goto error;
+	}
+	icodec = kzalloc(sizeof(struct snddev_icodec_state), GFP_KERNEL);
+	if (!icodec) {
+		rc = -ENOMEM;
+		goto error;
+	}
+	dev_info = kmalloc(sizeof(struct msm_snddev_info), GFP_KERNEL);
+	if (!dev_info) {
+		kfree(icodec);
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	dev_info->name = pdata->name;
+	dev_info->copp_id = pdata->copp_id;
+	dev_info->acdb_id = pdata->acdb_id;
+	dev_info->private_data = (void *) icodec;
+	dev_info->dev_ops.open = snddev_icodec_open;
+	dev_info->dev_ops.close = snddev_icodec_close;
+	dev_info->dev_ops.set_freq = snddev_icodec_set_freq;
+	dev_info->dev_ops.set_device_volume = snddev_icodec_set_device_volume;
+	dev_info->capability = pdata->capability;
+	dev_info->opened = 0;
+	msm_snddev_register(dev_info);
+	icodec->data = pdata;
+	icodec->sample_rate = pdata->default_sample_rate;
+	dev_info->sample_rate = pdata->default_sample_rate;
+	if (pdata->capability & SNDDEV_CAP_RX) {
+		for (i = 0; i < VOC_RX_VOL_ARRAY_NUM; i++) {
+			dev_info->max_voc_rx_vol[i] =
+				pdata->max_voice_rx_vol[i];
+			dev_info->min_voc_rx_vol[i] =
+				pdata->min_voice_rx_vol[i];
+		}
+		/*sidetone is enabled only for  the device which
+		property set for side tone*/
+		if (pdata->property & SIDE_TONE_MASK)
+			dev_info->dev_ops.enable_sidetone =
+				snddev_icodec_enable_sidetone;
+		else
+			dev_info->dev_ops.enable_sidetone = NULL;
+	} else {
+		dev_info->dev_ops.enable_sidetone = NULL;
+	}
+
+error:
+	return rc;
+}
+
+static int snddev_icodec_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static struct platform_driver snddev_icodec_driver = {
+  .probe = snddev_icodec_probe,
+  .remove = snddev_icodec_remove,
+  .driver = { .name = "snddev_icodec" }
+};
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *debugfs_sdev_dent;
+static struct dentry *debugfs_afelb;
+static struct dentry *debugfs_adielb;
+static struct adie_codec_path *debugfs_rx_adie;
+static struct adie_codec_path *debugfs_tx_adie;
+
+static int snddev_icodec_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	MM_INFO("snddev_icodec: debug intf %s\n", (char *) file->private_data);
+	return 0;
+}
+
+static void debugfs_adie_loopback(u32 loop)
+{
+	struct snddev_icodec_drv_state *drv = &snddev_icodec_drv;
+
+	if (loop) {
+
+		/* enable MI2S RX master block */
+		/* enable MI2S RX bit clock */
+		clk_set_rate(drv->rx_mclk,
+			SNDDEV_ICODEC_CLK_RATE(8000));
+		clk_enable(drv->rx_mclk);
+		clk_enable(drv->rx_sclk);
+
+		MM_INFO("configure ADIE RX path\n");
+		/* Configure ADIE */
+		adie_codec_open(&debug_rx_profile, &debugfs_rx_adie);
+		adie_codec_setpath(debugfs_rx_adie, 8000, 256);
+		adie_codec_proceed_stage(debugfs_rx_adie,
+		ADIE_CODEC_DIGITAL_ANALOG_READY);
+
+		MM_INFO("Enable Handset Mic bias\n");
+		pmic_hsed_enable(PM_HSED_CONTROLLER_0, PM_HSED_ENABLE_PWM_TCXO);
+		/* enable MI2S TX master block */
+		/* enable MI2S TX bit clock */
+		clk_set_rate(drv->tx_mclk,
+			SNDDEV_ICODEC_CLK_RATE(8000));
+		clk_enable(drv->tx_mclk);
+		clk_enable(drv->tx_sclk);
+
+		MM_INFO("configure ADIE TX path\n");
+		/* Configure ADIE */
+		adie_codec_open(&debug_tx_lb_profile, &debugfs_tx_adie);
+		adie_codec_setpath(debugfs_tx_adie, 8000, 256);
+		adie_codec_proceed_stage(debugfs_tx_adie,
+		ADIE_CODEC_DIGITAL_ANALOG_READY);
+	} else {
+		/* Disable ADIE */
+		adie_codec_proceed_stage(debugfs_rx_adie,
+		ADIE_CODEC_DIGITAL_OFF);
+		adie_codec_close(debugfs_rx_adie);
+		adie_codec_proceed_stage(debugfs_tx_adie,
+		ADIE_CODEC_DIGITAL_OFF);
+		adie_codec_close(debugfs_tx_adie);
+
+		pmic_hsed_enable(PM_HSED_CONTROLLER_0, PM_HSED_ENABLE_OFF);
+
+		/* Disable MI2S RX master block */
+		/* Disable MI2S RX bit clock */
+		clk_disable(drv->rx_sclk);
+		clk_disable(drv->rx_mclk);
+
+		/* Disable MI2S TX master block */
+		/* Disable MI2S TX bit clock */
+		clk_disable(drv->tx_sclk);
+		clk_disable(drv->tx_mclk);
+	}
+}
+
+static void debugfs_afe_loopback(u32 loop)
+{
+	int trc;
+	struct msm_afe_config afe_config;
+	struct snddev_icodec_drv_state *drv = &snddev_icodec_drv;
+	struct lpa_codec_config lpa_config;
+
+	if (loop) {
+		/* Vote for SMPS mode*/
+		pmapp_smps_mode_vote(SMPS_AUDIO_PLAYBACK_ID,
+				PMAPP_VREG_S4, PMAPP_SMPS_MODE_VOTE_PWM);
+
+		/* enable MI2S RX master block */
+		/* enable MI2S RX bit clock */
+		trc = clk_set_rate(drv->rx_mclk,
+		SNDDEV_ICODEC_CLK_RATE(8000));
+		if (IS_ERR_VALUE(trc))
+			MM_ERR("failed to set clk rate\n");
+		clk_enable(drv->rx_mclk);
+		clk_enable(drv->rx_sclk);
+		clk_enable(drv->lpa_p_clk);
+		clk_enable(drv->lpa_codec_clk);
+		clk_enable(drv->lpa_core_clk);
+		/* Enable LPA sub system
+		 */
+		drv->lpa = lpa_get();
+		if (!drv->lpa)
+			MM_ERR("failed to enable lpa\n");
+		lpa_config.sample_rate = 8000;
+		lpa_config.sample_width = 16;
+		lpa_config.output_interface = LPA_OUTPUT_INTF_WB_CODEC;
+		lpa_config.num_channels = 1;
+		lpa_cmd_codec_config(drv->lpa, &lpa_config);
+		/* Set audio interconnect reg to LPA */
+		audio_interct_codec(AUDIO_INTERCT_LPA);
+		mi2s_set_codec_output_path(MI2S_CHAN_MONO_PACKED, WT_16_BIT);
+		MM_INFO("configure ADIE RX path\n");
+		/* Configure ADIE */
+		adie_codec_open(&debug_rx_profile, &debugfs_rx_adie);
+		adie_codec_setpath(debugfs_rx_adie, 8000, 256);
+		lpa_cmd_enable_codec(drv->lpa, 1);
+
+		/* Start AFE for RX */
+		afe_config.sample_rate = 0x8;
+		afe_config.channel_mode = 1;
+		afe_config.volume = AFE_VOLUME_UNITY;
+		MM_INFO("enable afe\n");
+		trc = afe_enable(AFE_HW_PATH_CODEC_RX, &afe_config);
+		if (IS_ERR_VALUE(trc))
+			MM_ERR("fail to enable afe RX\n");
+		adie_codec_proceed_stage(debugfs_rx_adie,
+		ADIE_CODEC_DIGITAL_READY);
+		adie_codec_proceed_stage(debugfs_rx_adie,
+		ADIE_CODEC_DIGITAL_ANALOG_READY);
+
+		/* Vote for PWM mode*/
+		pmapp_smps_mode_vote(SMPS_AUDIO_RECORD_ID,
+			PMAPP_VREG_S4, PMAPP_SMPS_MODE_VOTE_PWM);
+
+		MM_INFO("Enable Handset Mic bias\n");
+		pmic_hsed_enable(PM_HSED_CONTROLLER_0, PM_HSED_ENABLE_PWM_TCXO);
+
+		/* enable MI2S TX master block */
+		/* enable MI2S TX bit clock */
+		clk_set_rate(drv->tx_mclk,
+			SNDDEV_ICODEC_CLK_RATE(8000));
+		clk_enable(drv->tx_mclk);
+		clk_enable(drv->tx_sclk);
+		/* Set MI2S */
+		mi2s_set_codec_input_path(MI2S_CHAN_MONO_PACKED, WT_16_BIT);
+		MM_INFO("configure ADIE TX path\n");
+		/* Configure ADIE */
+		adie_codec_open(&debug_tx_profile, &debugfs_tx_adie);
+		adie_codec_setpath(debugfs_tx_adie, 8000, 256);
+		adie_codec_proceed_stage(debugfs_tx_adie,
+		ADIE_CODEC_DIGITAL_READY);
+		adie_codec_proceed_stage(debugfs_tx_adie,
+		ADIE_CODEC_DIGITAL_ANALOG_READY);
+		/* Start AFE for TX */
+		afe_config.sample_rate = 0x8;
+		afe_config.channel_mode = 1;
+		afe_config.volume = AFE_VOLUME_UNITY;
+		trc = afe_enable(AFE_HW_PATH_CODEC_TX, &afe_config);
+		if (IS_ERR_VALUE(trc))
+			MM_ERR("failed to enable AFE TX\n");
+		/* Set the volume level to non unity, to avoid
+		   loopback effect */
+		afe_device_volume_ctrl(AFE_HW_PATH_CODEC_RX, 0x0500);
+
+		/* enable afe loopback */
+		afe_loopback(1);
+		MM_INFO("AFE loopback enabled\n");
+	} else {
+		/* disable afe loopback */
+		afe_loopback(0);
+		/* Remove the vote for SMPS mode*/
+		pmapp_smps_mode_vote(SMPS_AUDIO_PLAYBACK_ID,
+			PMAPP_VREG_S4, PMAPP_SMPS_MODE_VOTE_DONTCARE);
+
+		/* Disable ADIE */
+		adie_codec_proceed_stage(debugfs_rx_adie,
+		ADIE_CODEC_DIGITAL_OFF);
+		adie_codec_close(debugfs_rx_adie);
+		/* Disable AFE for RX */
+		afe_disable(AFE_HW_PATH_CODEC_RX);
+
+		/* Disable LPA Sub system */
+		lpa_cmd_enable_codec(drv->lpa, 0);
+		lpa_put(drv->lpa);
+
+		/* Disable LPA clocks */
+		clk_disable(drv->lpa_p_clk);
+		clk_disable(drv->lpa_codec_clk);
+		clk_disable(drv->lpa_core_clk);
+
+		/* Disable MI2S RX master block */
+		/* Disable MI2S RX bit clock */
+		clk_disable(drv->rx_sclk);
+		clk_disable(drv->rx_mclk);
+
+		pmapp_smps_mode_vote(SMPS_AUDIO_RECORD_ID,
+			PMAPP_VREG_S4, PMAPP_SMPS_MODE_VOTE_DONTCARE);
+
+		/* Disable AFE for TX */
+		afe_disable(AFE_HW_PATH_CODEC_TX);
+
+		/* Disable ADIE */
+		adie_codec_proceed_stage(debugfs_tx_adie,
+		ADIE_CODEC_DIGITAL_OFF);
+		adie_codec_close(debugfs_tx_adie);
+		/* Disable MI2S TX master block */
+		/* Disable MI2S TX bit clock */
+		clk_disable(drv->tx_sclk);
+		clk_disable(drv->tx_mclk);
+		pmic_hsed_enable(PM_HSED_CONTROLLER_0, PM_HSED_ENABLE_OFF);
+		MM_INFO("AFE loopback disabled\n");
+	}
+}
+
+static ssize_t snddev_icodec_debug_write(struct file *filp,
+	const char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+	char *lb_str = filp->private_data;
+	char cmd;
+
+	if (get_user(cmd, ubuf))
+		return -EFAULT;
+
+	MM_INFO("%s %c\n", lb_str, cmd);
+
+	if (!strcmp(lb_str, "adie_loopback")) {
+		switch (cmd) {
+		case '1':
+			debugfs_adie_loopback(1);
+			break;
+		case '0':
+			debugfs_adie_loopback(0);
+			break;
+		}
+	} else if (!strcmp(lb_str, "afe_loopback")) {
+		switch (cmd) {
+		case '1':
+			debugfs_afe_loopback(1);
+			break;
+		case '0':
+			debugfs_afe_loopback(0);
+			break;
+		}
+	}
+
+	return cnt;
+}
+
+static const struct file_operations snddev_icodec_debug_fops = {
+	.open = snddev_icodec_debug_open,
+	.write = snddev_icodec_debug_write
+};
+#endif
+
+static int __init snddev_icodec_init(void)
+{
+	s32 rc;
+	struct snddev_icodec_drv_state *icodec_drv = &snddev_icodec_drv;
+
+	rc = platform_driver_register(&snddev_icodec_driver);
+	if (IS_ERR_VALUE(rc))
+		goto error_platform_driver;
+	icodec_drv->rx_mclk = clk_get(NULL, "mi2s_codec_rx_m_clk");
+	if (IS_ERR(icodec_drv->rx_mclk))
+		goto error_rx_mclk;
+	icodec_drv->rx_sclk = clk_get(NULL, "mi2s_codec_rx_s_clk");
+	if (IS_ERR(icodec_drv->rx_sclk))
+		goto error_rx_sclk;
+	icodec_drv->tx_mclk = clk_get(NULL, "mi2s_codec_tx_m_clk");
+	if (IS_ERR(icodec_drv->tx_mclk))
+		goto error_tx_mclk;
+	icodec_drv->tx_sclk = clk_get(NULL, "mi2s_codec_tx_s_clk");
+	if (IS_ERR(icodec_drv->tx_sclk))
+		goto error_tx_sclk;
+	icodec_drv->lpa_codec_clk = clk_get(NULL, "lpa_codec_clk");
+	if (IS_ERR(icodec_drv->lpa_codec_clk))
+		goto error_lpa_codec_clk;
+	icodec_drv->lpa_core_clk = clk_get(NULL, "lpa_core_clk");
+	if (IS_ERR(icodec_drv->lpa_core_clk))
+		goto error_lpa_core_clk;
+	icodec_drv->lpa_p_clk = clk_get(NULL, "lpa_pclk");
+	if (IS_ERR(icodec_drv->lpa_p_clk))
+		goto error_lpa_p_clk;
+
+#ifdef CONFIG_DEBUG_FS
+	debugfs_sdev_dent = debugfs_create_dir("snddev_icodec", 0);
+	if (debugfs_sdev_dent) {
+		debugfs_afelb = debugfs_create_file("afe_loopback",
+		S_IFREG | S_IWUGO, debugfs_sdev_dent,
+		(void *) "afe_loopback", &snddev_icodec_debug_fops);
+		debugfs_adielb = debugfs_create_file("adie_loopback",
+		S_IFREG | S_IWUGO, debugfs_sdev_dent,
+		(void *) "adie_loopback", &snddev_icodec_debug_fops);
+	}
+#endif
+	mutex_init(&icodec_drv->rx_lock);
+	mutex_init(&icodec_drv->lb_lock);
+	mutex_init(&icodec_drv->tx_lock);
+	icodec_drv->rx_active = 0;
+	icodec_drv->tx_active = 0;
+	icodec_drv->lpa = NULL;
+	wake_lock_init(&icodec_drv->tx_idlelock, WAKE_LOCK_IDLE,
+			"snddev_tx_idle");
+	wake_lock_init(&icodec_drv->rx_idlelock, WAKE_LOCK_IDLE,
+			"snddev_rx_idle");
+	return 0;
+
+error_lpa_p_clk:
+	clk_put(icodec_drv->lpa_core_clk);
+error_lpa_core_clk:
+	clk_put(icodec_drv->lpa_codec_clk);
+error_lpa_codec_clk:
+	clk_put(icodec_drv->tx_sclk);
+error_tx_sclk:
+	clk_put(icodec_drv->tx_mclk);
+error_tx_mclk:
+	clk_put(icodec_drv->rx_sclk);
+error_rx_sclk:
+	clk_put(icodec_drv->rx_mclk);
+error_rx_mclk:
+	platform_driver_unregister(&snddev_icodec_driver);
+error_platform_driver:
+
+	MM_ERR("encounter error\n");
+	return -ENODEV;
+}
+
+static void __exit snddev_icodec_exit(void)
+{
+	struct snddev_icodec_drv_state *icodec_drv = &snddev_icodec_drv;
+
+#ifdef CONFIG_DEBUG_FS
+	if (debugfs_afelb)
+		debugfs_remove(debugfs_afelb);
+	if (debugfs_adielb)
+		debugfs_remove(debugfs_adielb);
+	if (debugfs_sdev_dent)
+		debugfs_remove(debugfs_sdev_dent);
+#endif
+	platform_driver_unregister(&snddev_icodec_driver);
+
+	clk_put(icodec_drv->rx_sclk);
+	clk_put(icodec_drv->rx_mclk);
+	clk_put(icodec_drv->tx_sclk);
+	clk_put(icodec_drv->tx_mclk);
+	return;
+}
+
+module_init(snddev_icodec_init);
+module_exit(snddev_icodec_exit);
+
+MODULE_DESCRIPTION("ICodec Sound Device driver");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5v2/snddev_mi2s.c b/arch/arm/mach-msm/qdsp5v2/snddev_mi2s.c
new file mode 100644
index 0000000..939cc8b
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/snddev_mi2s.c
@@ -0,0 +1,405 @@
+/* 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/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/qdsp5v2/audio_interct.h>
+#include <mach/qdsp5v2/mi2s.h>
+#include <mach/qdsp5v2/afe.h>
+#include <mach/debug_mm.h>
+#include <mach/qdsp5v2/snddev_mi2s.h>
+
+/* Global state for the driver */
+struct snddev_mi2s_drv_state {
+	struct clk *mclk;
+	struct clk *sclk;
+	struct mutex lock;
+	u8 sd_lines_used;
+	u8 clocks_enabled;
+};
+
+static struct snddev_mi2s_drv_state snddev_mi2s_drv;
+
+static int snddev_mi2s_open_tx(struct msm_snddev_info *dev_info)
+{
+	u8 channels;
+	struct msm_afe_config afe_config;
+	int rc;
+	struct snddev_mi2s_data *snddev_mi2s_data = dev_info->private_data;
+
+	MM_DBG("%s: channel_mode = %u sd_line_mask = 0x%x "
+		"default_sample_rate = %u\n", __func__,
+		snddev_mi2s_data->channel_mode, snddev_mi2s_data->sd_lines,
+		snddev_mi2s_data->default_sample_rate);
+
+	if (snddev_mi2s_data->channel_mode == 2) {
+		channels = MI2S_CHAN_STEREO;
+	} else {
+		MM_ERR("%s: Invalid number of channels = %u\n", __func__,
+			snddev_mi2s_data->channel_mode);
+		return -EINVAL;
+	}
+
+	/* Set MI2S */
+	mi2s_set_hdmi_input_path(channels, WT_16_BIT,
+				 snddev_mi2s_data->sd_lines);
+
+	afe_config.sample_rate = snddev_mi2s_data->default_sample_rate / 1000;
+	afe_config.channel_mode = snddev_mi2s_data->channel_mode;
+	afe_config.volume = AFE_VOLUME_UNITY;
+	rc = afe_enable(AFE_HW_PATH_MI2S_TX, &afe_config);
+
+	if (IS_ERR_VALUE(rc)) {
+		MM_ERR("%s: afe_enable failed for AFE_HW_PATH_MI2S_TX "
+		       "rc = %d\n", __func__, rc);
+		return -ENODEV;
+	}
+
+	/* Enable audio path */
+	if (snddev_mi2s_data->route)
+		snddev_mi2s_data->route();
+
+	return 0;
+}
+
+static int snddev_mi2s_open_rx(struct msm_snddev_info *dev_info)
+{
+	int rc;
+	struct msm_afe_config afe_config;
+	u8 channels;
+	struct snddev_mi2s_data *snddev_mi2s_data = dev_info->private_data;
+
+	MM_DBG("%s: channel_mode = %u sd_line_mask = 0x%x "
+		"default_sample_rate = %u\n", __func__,
+		snddev_mi2s_data->channel_mode, snddev_mi2s_data->sd_lines,
+		snddev_mi2s_data->default_sample_rate);
+
+	if (snddev_mi2s_data->channel_mode == 2)
+		channels = MI2S_CHAN_STEREO;
+	else if (snddev_mi2s_data->channel_mode == 4)
+		channels = MI2S_CHAN_4CHANNELS;
+	else if (snddev_mi2s_data->channel_mode == 6)
+		channels = MI2S_CHAN_6CHANNELS;
+	else if (snddev_mi2s_data->channel_mode == 8)
+		channels = MI2S_CHAN_8CHANNELS;
+	else
+		channels = MI2S_CHAN_MONO_RAW;
+
+	/* Set MI2S */
+	mi2s_set_hdmi_output_path(channels, WT_16_BIT,
+				  snddev_mi2s_data->sd_lines);
+
+	/* Start AFE */
+	afe_config.sample_rate = snddev_mi2s_data->default_sample_rate / 1000;
+	afe_config.channel_mode = snddev_mi2s_data->channel_mode;
+	afe_config.volume = AFE_VOLUME_UNITY;
+	rc = afe_enable(AFE_HW_PATH_MI2S_RX, &afe_config);
+
+	if (IS_ERR_VALUE(rc)) {
+		MM_ERR("%s: encounter error\n", __func__);
+		return -ENODEV;
+	}
+
+	/* Enable audio path */
+	if (snddev_mi2s_data->route)
+		snddev_mi2s_data->route();
+
+	MM_DBG("%s: enabled %s \n", __func__, snddev_mi2s_data->name);
+
+	return 0;
+}
+
+static int snddev_mi2s_open(struct msm_snddev_info *dev_info)
+{
+	int rc = 0;
+	struct snddev_mi2s_drv_state *drv = &snddev_mi2s_drv;
+	u32 dir;
+	struct snddev_mi2s_data *snddev_mi2s_data = dev_info->private_data;
+
+	if (!dev_info) {
+		MM_ERR("%s:  msm_snddev_info is null \n", __func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&drv->lock);
+
+	if (drv->sd_lines_used & snddev_mi2s_data->sd_lines) {
+		MM_ERR("%s: conflict in SD data line. can not use the device\n",
+		       __func__);
+		mutex_unlock(&drv->lock);
+		return -EBUSY;
+	}
+
+	if (!drv->clocks_enabled) {
+
+		rc = mi2s_config_clk_gpio();
+		if (rc) {
+			MM_ERR("%s: mi2s GPIO config failed for %s\n",
+			       __func__, snddev_mi2s_data->name);
+			mutex_unlock(&drv->lock);
+			return -EIO;
+		}
+		clk_enable(drv->mclk);
+		clk_enable(drv->sclk);
+		drv->clocks_enabled = 1;
+		MM_DBG("%s: clks enabled \n", __func__);
+	} else
+		MM_DBG("%s: clks already enabled \n", __func__);
+
+	if (snddev_mi2s_data->capability & SNDDEV_CAP_RX) {
+
+		dir = DIR_RX;
+		rc = mi2s_config_data_gpio(dir, snddev_mi2s_data->sd_lines);
+
+		if (rc) {
+			rc = -EIO;
+			MM_ERR("%s: mi2s GPIO config failed for %s\n",
+			       __func__, snddev_mi2s_data->name);
+			goto mi2s_data_gpio_failure;
+		}
+
+		MM_DBG("%s: done gpio config rx SD lines\n", __func__);
+
+		rc = snddev_mi2s_open_rx(dev_info);
+
+		if (IS_ERR_VALUE(rc)) {
+			MM_ERR(" snddev_mi2s_open_rx failed \n");
+			goto mi2s_cleanup_open;
+		}
+
+		drv->sd_lines_used |= snddev_mi2s_data->sd_lines;
+
+		MM_DBG("%s: sd_lines_used = 0x%x\n", __func__,
+			drv->sd_lines_used);
+		mutex_unlock(&drv->lock);
+
+	} else {
+		dir = DIR_TX;
+		rc = mi2s_config_data_gpio(dir, snddev_mi2s_data->sd_lines);
+
+		if (rc) {
+			rc = -EIO;
+			MM_ERR("%s: mi2s GPIO config failed for %s\n",
+			       __func__, snddev_mi2s_data->name);
+			goto mi2s_data_gpio_failure;
+		}
+		MM_DBG("%s: done data line gpio config for %s\n",
+			__func__, snddev_mi2s_data->name);
+
+		rc = snddev_mi2s_open_tx(dev_info);
+
+		if (IS_ERR_VALUE(rc)) {
+			MM_ERR(" snddev_mi2s_open_tx failed \n");
+			goto mi2s_cleanup_open;
+		}
+
+		drv->sd_lines_used |= snddev_mi2s_data->sd_lines;
+		MM_DBG("%s: sd_lines_used = 0x%x\n", __func__,
+			drv->sd_lines_used);
+		mutex_unlock(&drv->lock);
+	}
+
+	return 0;
+
+mi2s_cleanup_open:
+	mi2s_unconfig_data_gpio(dir, snddev_mi2s_data->sd_lines);
+
+	/* Disable audio path */
+	if (snddev_mi2s_data->deroute)
+		snddev_mi2s_data->deroute();
+
+mi2s_data_gpio_failure:
+	if (!drv->sd_lines_used) {
+		clk_disable(drv->sclk);
+		clk_disable(drv->mclk);
+		drv->clocks_enabled = 0;
+		mi2s_unconfig_clk_gpio();
+	}
+	mutex_unlock(&drv->lock);
+	return rc;
+}
+
+static int snddev_mi2s_close(struct msm_snddev_info *dev_info)
+{
+	struct snddev_mi2s_drv_state *drv = &snddev_mi2s_drv;
+	int dir;
+	struct snddev_mi2s_data *snddev_mi2s_data = dev_info->private_data;
+
+	if (!dev_info) {
+		MM_ERR("%s:  msm_snddev_info is null \n", __func__);
+		return -EINVAL;
+	}
+
+	if (!dev_info->opened) {
+		MM_ERR(" %s: calling close device with out opening the"
+		       " device \n", __func__);
+		return -EIO;
+	}
+
+	mutex_lock(&drv->lock);
+
+	drv->sd_lines_used &= ~snddev_mi2s_data->sd_lines;
+
+	MM_DBG("%s: sd_lines in use = 0x%x\n", __func__, drv->sd_lines_used);
+
+	if (snddev_mi2s_data->capability & SNDDEV_CAP_RX) {
+		dir = DIR_RX;
+		afe_disable(AFE_HW_PATH_MI2S_RX);
+	} else {
+		dir = DIR_TX;
+		afe_disable(AFE_HW_PATH_MI2S_TX);
+	}
+
+	mi2s_unconfig_data_gpio(dir, snddev_mi2s_data->sd_lines);
+
+	if (!drv->sd_lines_used) {
+		clk_disable(drv->sclk);
+		clk_disable(drv->mclk);
+		drv->clocks_enabled = 0;
+		mi2s_unconfig_clk_gpio();
+	}
+
+	/* Disable audio path */
+	if (snddev_mi2s_data->deroute)
+		snddev_mi2s_data->deroute();
+
+	mutex_unlock(&drv->lock);
+
+	return 0;
+}
+
+static int snddev_mi2s_set_freq(struct msm_snddev_info *dev_info, u32 req_freq)
+{
+	if (req_freq != 48000) {
+		MM_DBG("%s: Unsupported Frequency:%d\n", __func__, req_freq);
+		return -EINVAL;
+	}
+	return 48000;
+}
+
+static int snddev_mi2s_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct snddev_mi2s_data *pdata;
+	struct msm_snddev_info *dev_info;
+
+	if (!pdev || !pdev->dev.platform_data) {
+		printk(KERN_ALERT "Invalid caller \n");
+		return -ENODEV;
+	}
+
+	pdata = pdev->dev.platform_data;
+	if ((pdata->capability & SNDDEV_CAP_RX) &&
+	    (pdata->capability & SNDDEV_CAP_TX)) {
+		MM_ERR("%s: invalid device data either RX or TX\n", __func__);
+		return -ENODEV;
+	}
+
+	dev_info = kzalloc(sizeof(struct msm_snddev_info), GFP_KERNEL);
+	if (!dev_info) {
+		MM_ERR("%s: uneable to allocate memeory for msm_snddev_info \n",
+		       __func__);
+
+		return -ENOMEM;
+	}
+
+	dev_info->name = pdata->name;
+	dev_info->copp_id = pdata->copp_id;
+	dev_info->acdb_id = pdata->acdb_id;
+	dev_info->private_data = (void *)pdata;
+	dev_info->dev_ops.open = snddev_mi2s_open;
+	dev_info->dev_ops.close = snddev_mi2s_close;
+	dev_info->dev_ops.set_freq = snddev_mi2s_set_freq;
+	dev_info->capability = pdata->capability;
+	dev_info->opened = 0;
+	msm_snddev_register(dev_info);
+	dev_info->sample_rate = pdata->default_sample_rate;
+
+	MM_DBG("%s: probe done for %s\n", __func__, pdata->name);
+	return rc;
+}
+
+static int snddev_mi2s_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static struct platform_driver snddev_mi2s_driver = {
+	.probe = snddev_mi2s_probe,
+	.remove = snddev_mi2s_remove,
+	.driver = {.name = "snddev_mi2s"}
+};
+
+static int __init snddev_mi2s_init(void)
+{
+	s32 rc;
+	struct snddev_mi2s_drv_state *drv = &snddev_mi2s_drv;
+
+	rc = platform_driver_register(&snddev_mi2s_driver);
+	if (IS_ERR_VALUE(rc)) {
+
+		MM_ERR("%s: platform_driver_register failed  \n", __func__);
+		goto error_platform_driver;
+	}
+
+	drv->mclk = clk_get(NULL, "mi2s_m_clk");
+	if (IS_ERR(drv->mclk)) {
+		MM_ERR("%s:  clk_get mi2s_mclk failed  \n", __func__);
+		goto error_mclk;
+	}
+
+	drv->sclk = clk_get(NULL, "mi2s_s_clk");
+	if (IS_ERR(drv->sclk)) {
+		MM_ERR("%s:  clk_get mi2s_sclk failed  \n", __func__);
+
+		goto error_sclk;
+	}
+
+	mutex_init(&drv->lock);
+
+	MM_DBG("snddev_mi2s_init : done \n");
+
+	return 0;
+
+error_sclk:
+	clk_put(drv->mclk);
+error_mclk:
+	platform_driver_unregister(&snddev_mi2s_driver);
+error_platform_driver:
+
+	MM_ERR("%s: encounter error\n", __func__);
+	return -ENODEV;
+}
+
+static void __exit snddev_mi2s_exit(void)
+{
+	struct snddev_mi2s_drv_state *drv = &snddev_mi2s_drv;
+
+	platform_driver_unregister(&snddev_mi2s_driver);
+
+	clk_put(drv->sclk);
+	clk_put(drv->mclk);
+	return;
+}
+
+module_init(snddev_mi2s_init);
+module_exit(snddev_mi2s_exit);
+
+MODULE_DESCRIPTION("mi2s Sound Device driver");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5v2/snddev_virtual.c b/arch/arm/mach-msm/qdsp5v2/snddev_virtual.c
new file mode 100644
index 0000000..cd93345
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/snddev_virtual.c
@@ -0,0 +1,122 @@
+/* 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/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <asm/uaccess.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/qdsp5v2/snddev_virtual.h>
+#include <mach/debug_mm.h>
+#include <linux/slab.h>
+
+static int snddev_virtual_open(struct msm_snddev_info *dev_info)
+{
+	int rc = 0;
+
+	if (!dev_info)
+		rc = -EINVAL;
+	return rc;
+}
+
+static int snddev_virtual_close(struct msm_snddev_info *dev_info)
+{
+	int rc = 0;
+
+	if (!dev_info)
+		rc = -EINVAL;
+	return rc;
+}
+
+static int snddev_virtual_set_freq(struct msm_snddev_info *dev_info, u32 rate)
+{
+	int rc = 0;
+
+	if (!dev_info)
+		rc = -EINVAL;
+	return rate;
+}
+
+static int snddev_virtual_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct snddev_virtual_data *pdata;
+	struct msm_snddev_info *dev_info;
+
+	if (!pdev || !pdev->dev.platform_data) {
+		MM_ERR("Invalid caller\n");
+		rc = -EPERM;
+		goto error;
+	}
+	pdata = pdev->dev.platform_data;
+
+	dev_info = kmalloc(sizeof(struct msm_snddev_info), GFP_KERNEL);
+	if (!dev_info) {
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	dev_info->name = pdata->name;
+	dev_info->copp_id = pdata->copp_id;
+	dev_info->acdb_id = pdata->acdb_id;
+	dev_info->private_data = (void *) NULL;
+	dev_info->dev_ops.open = snddev_virtual_open;
+	dev_info->dev_ops.close = snddev_virtual_close;
+	dev_info->dev_ops.set_freq = snddev_virtual_set_freq;
+	dev_info->capability = pdata->capability;
+	dev_info->sample_rate = 8000;
+	dev_info->opened = 0;
+	dev_info->sessions = 0;
+
+	msm_snddev_register(dev_info);
+
+error:
+	return rc;
+}
+
+static int snddev_virtual_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static struct platform_driver snddev_virtual_driver = {
+	.probe = snddev_virtual_probe,
+	.remove = snddev_virtual_remove,
+	.driver = { .name = "snddev_virtual" }
+};
+
+static int __init snddev_virtual_init(void)
+{
+	int rc = 0;
+
+	MM_DBG(" snddev_virtual_init \n");
+	rc = platform_driver_register(&snddev_virtual_driver);
+	if (IS_ERR_VALUE(rc)) {
+		MM_ERR("platform driver register failure\n");
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static void __exit snddev_virtual_exit(void)
+{
+	platform_driver_unregister(&snddev_virtual_driver);
+
+	return;
+}
+
+module_init(snddev_virtual_init);
+module_exit(snddev_virtual_exit);
+
+MODULE_DESCRIPTION("Virtual Sound Device driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/qdsp5v2/timpani_profile_7x30.h b/arch/arm/mach-msm/qdsp5v2/timpani_profile_7x30.h
new file mode 100644
index 0000000..f336597
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/timpani_profile_7x30.h
@@ -0,0 +1,622 @@
+/* Copyright (c) 2010-2011, 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.
+ *
+ */
+
+#ifndef __MACH_QDSP5_V2_TIMPANI_PROFILE_H__
+#define __MACH_QDSP5_V2_MTIMPANI_PROFILE_H__
+
+/*
+ * TX Device Profiles
+ */
+
+/* Analog MIC */
+/* AMIC Primary mono */
+#define AMIC_PRI_MONO_8000_OSR_256 \
+	{{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xD0)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \
+	{ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x04, 0x04)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8b, 0xff, 0xE6)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8c, 0x03, 0x02)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x09, 0x09)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} }
+
+/* Headset MIC */
+#define AMIC1_HEADSET_TX_MONO_PRIMARY_OSR256 \
+	{{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xC8)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \
+	{ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x04, 0x04)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8b, 0xff, 0xE7)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8c, 0x03, 0x02)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)} }
+
+/*
+ * RX Device Profiles
+ */
+
+/* RX EAR */
+#define EAR_PRI_MONO_8000_OSR_256 \
+	{{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0F)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x97, 0xFF, 0x01)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0xFF, 0x4C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x01, 0x01)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x03, 0x03)}, \
+	{ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x01, 0x01)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0E)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x03, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x39, 0x01, 0x00)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} }
+
+/* RX SPEAKER */
+#define SPEAKER_PRI_STEREO_48000_OSR_256 \
+	{{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x08)}, \
+	{ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x48)}, \
+	{ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x24)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x10)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x10)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFC, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFC, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x05, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x00)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF},       \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} };
+
+/*
+ * RX HPH PRIMARY
+ */
+
+/* RX HPH CLASS AB CAPLESS */
+
+#define HEADSET_AB_CPLS_48000_OSR_256 \
+	{{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x55)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFF, 0x29)}, \
+	{ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xF5)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFE, 0xC8)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x24)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} }
+
+/* AMIC dual */
+#define AMIC_DUAL_8000_OSR_256 \
+	{{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x04)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xD0)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xFF, 0xC2)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \
+	{ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8b, 0xff, 0xCE)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8c, 0x03, 0x02)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0E, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} }
+
+/* TTY RX */
+#define TTY_HEADSET_MONO_RX_8000_OSR_256 \
+	{{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x97, 0xFF, 0x01)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x06)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x01)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x4C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x45)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFF, 0x29)}, \
+	{ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xC5)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFE, 0xC8)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x20)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x10)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x08)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} }
+
+/* TTY TX */
+#define TTY_HEADSET_MONO_TX_8000_OSR_256 \
+	{{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0xFF, 0x1E)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x01, 0x01)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x93, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x99, 0x0F, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x9F, 0x03, 0x03)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0xA8)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0xBC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0xBC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x65)}, \
+	{ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x04, 0x04)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x0D, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x14, 0xFF, 0x64)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x11, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x12, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} }
+
+#define HEADSET_RX_CAPLESS_48000_OSR_256 \
+	{{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x4e)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x04, 0xff, 0xBC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x64)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x25, 0x0F, 0x0B)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xfc, 0xfc)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xff, 0xa2)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0xFF, 0xab)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x01, 0x01)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x80)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0xf0)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x23, 0xff, 0x20)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3B, 0xFF, 0x04)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFF, 0x04)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xff, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xff, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0f, 0x0c)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8a, 0x03, 0x03)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3b, 0xFF, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3c, 0xFF, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x34, 0xf0, 0x00)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x80, 0x00)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} }
+
+#define HEADSET_STEREO_SPEAKER_STEREO_RX_CAPLESS_48000_OSR_256 \
+	{{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x04)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x55)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x08)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFF, 0x29)}, \
+	{ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xF5)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x48)}, \
+	{ADIE_CODEC_ACTION_DELAY_WAIT, 0x1388}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x24)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x10)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x10)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x24)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFC, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFC, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x24, 0x00)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF},	\
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} }
+
+#define HS_DMIC2_STEREO_8000_OSR_256 \
+	{{ADIE_CODEC_ACTION_DELAY_WAIT, 0xbb8 }, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x05)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x05, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0x30)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x82, 0x1F, 0x1E)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x0C, 0x0C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x92, 0x3F, 0x19)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0x3F, 0x24)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x39, 0x01)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA8, 0x0F, 0x0E)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xAB, 0x3F, 0x00)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x86, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x87, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xC0)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0xF0, 0xF0)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0xC0, 0x00)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x92, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x94, 0xFF, 0x1B)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} }
+
+#define HPH_PRI_AB_LEG_STEREO \
+	{{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x09)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x59)}, \
+	{ADIE_CODEC_ACTION_DELAY_WAIT, 0x186A0}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0xF9)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x27, 0x27)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x10)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x10)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} }
+
+#define HPH_PRI_D_LEG_STEREO \
+	{{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x02)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x80, 0x02, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xA3, 0x02, 0x02)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x84, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x85, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x0C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x81, 0xFF, 0x0E)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x83, 0x03, 0x03)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x21, 0xFF, 0x60)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x22, 0xFF, 0xE1)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x24, 0x6F, 0x6C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x26, 0xFF, 0xD0)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2D, 0xFF, 0x6F)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2E, 0xFF, 0x55)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3F, 0xFF, 0x0F)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x40, 0xFF, 0x08)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x41, 0x08, 0x08)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x42, 0xFF, 0xBB)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x43, 0xFF, 0xF2)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x44, 0xF7, 0x37)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x45, 0xFF, 0xFF)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x46, 0xFF, 0x77)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x47, 0xFF, 0xF2)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x48, 0xF7, 0x37)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x49, 0xFF, 0xFF)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4A, 0xFF, 0x77)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x05)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0xFF, 0x8C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x0F, 0x0A)}, \
+	{ADIE_CODEC_ACTION_DELAY_WAIT, 300000}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x0F, 0x0F)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x10)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x10)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x10)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x10)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3E, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x0F, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} }
+
+#define LB_AUXPGA_HPH_AB_CPLS_STEREO \
+	{{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2F, 0xFF, 0x44)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x30, 0xFF, 0x92)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0x05)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0x55)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x30)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xAA)}, \
+	{ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0xF5)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x4C, 0xFF, 0x29)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0x90, 0x90)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x04)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x04)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x1C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x1C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFE, 0x3C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFE, 0x3C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE2, 0xFC, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE3, 0xFC, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0x30, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3C, 0xFF, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0xFD, 0x00)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} }
+
+#define LB_AUXPGA_LO_STEREO \
+	{{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_READY},	\
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x2F, 0xFF, 0x44)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x30, 0xFF, 0x92)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x01)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x08)}, \
+	{ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x58)}, \
+	{ADIE_CODEC_ACTION_DELAY_WAIT, 0xBB8}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0xF8)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0xF0, 0xF0)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x38, 0xFF, 0xAA)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x90, 0x90)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0xF0, 0x30)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xB7, 0x01, 0x01)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x10)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x10)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_ANALOG_READY}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0xF0, 0xF0)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x8A, 0x0F, 0x03)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x1C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x1C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFE, 0x3C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFE, 0x3C)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE0, 0xFC, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0xE1, 0xFC, 0xAC)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x33, 0xF0, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x32, 0xF8, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x31, 0x01, 0x00)}, \
+	{ADIE_CODEC_ACTION_ENTRY, ADIE_CODEC_PACK_ENTRY(0x3A, 0x90, 0x00)}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_ANALOG_OFF}, \
+	{ADIE_CODEC_ACTION_STAGE_REACHED, ADIE_CODEC_DIGITAL_OFF} }
+
+#endif
diff --git a/arch/arm/mach-msm/qdsp5v2/voice.c b/arch/arm/mach-msm/qdsp5v2/voice.c
new file mode 100644
index 0000000..c4560ed
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/voice.c
@@ -0,0 +1,748 @@
+/* Copyright (c) 2009-2011, 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/kernel.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/msm_audio.h>
+#include <mach/qdsp5v2/audio_dev_ctl.h>
+#include <mach/dal.h>
+#include <linux/kthread.h>
+#include <linux/completion.h>
+#include <linux/wait.h>
+#include <mach/qdsp5v2/voice.h>
+#include <mach/debug_mm.h>
+
+struct voice_data {
+	void *handle; /* DALRPC handle */
+	void *cb_handle; /* DALRPC callback handle */
+	int network; /* Network information */
+	int dev_state;/*READY, CHANGE, REL_DONE,INIT*/
+	int voc_state;/*INIT, CHANGE, RELEASE, ACQUIRE */
+	struct mutex voc_lock;
+	struct mutex vol_lock;
+	int voc_event;
+	int dev_event;
+	atomic_t rel_start_flag;
+	atomic_t acq_start_flag;
+	atomic_t chg_start_flag;
+	struct task_struct *task;
+	struct completion complete;
+	wait_queue_head_t dev_wait;
+	wait_queue_head_t voc_wait;
+	uint32_t device_events;
+	/* cache the values related to Rx and Tx */
+	struct device_data dev_rx;
+	struct device_data dev_tx;
+	/* these default values are for all devices */
+	uint32_t default_mute_val;
+	uint32_t default_vol_val;
+	uint32_t default_sample_val;
+	/* call status */
+	int v_call_status; /* Start or End */
+	s32 max_rx_vol[VOC_RX_VOL_ARRAY_NUM]; /* [0] is for NB, [1] for WB */
+	s32 min_rx_vol[VOC_RX_VOL_ARRAY_NUM];
+};
+
+static struct voice_data voice;
+
+static int voice_cmd_device_info(struct voice_data *);
+static int voice_cmd_acquire_done(struct voice_data *);
+static void voice_auddev_cb_function(u32 evt_id,
+			union auddev_evt_data *evt_payload,
+			void *private_data);
+
+static int voice_cmd_change(void)
+{
+
+	struct voice_header hdr;
+	struct voice_data *v = &voice;
+	int err;
+
+	hdr.id = CMD_DEVICE_CHANGE;
+	hdr.data_len = 0;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+
+	err = dalrpc_fcn_5(VOICE_DALRPC_CMD, v->handle, &hdr,
+			sizeof(struct voice_header));
+
+	if (err)
+		MM_ERR("Voice change command failed\n");
+	return err;
+}
+
+static void voice_auddev_cb_function(u32 evt_id,
+			union auddev_evt_data *evt_payload,
+			void *private_data)
+{
+	struct voice_data *v = &voice;
+	int rc = 0, i;
+
+	MM_INFO("auddev_cb_function, evt_id=%d, dev_state=%d, voc_state=%d\n",
+		evt_id, v->dev_state, v->voc_state);
+	if ((evt_id != AUDDEV_EVT_START_VOICE) ||
+			(evt_id != AUDDEV_EVT_END_VOICE)) {
+		if (evt_payload == NULL) {
+			MM_ERR(" evt_payload is NULL pointer\n");
+			return;
+		}
+	}
+	switch (evt_id) {
+	case AUDDEV_EVT_START_VOICE:
+		if ((v->dev_state == DEV_INIT) ||
+				(v->dev_state == DEV_REL_DONE)) {
+			v->v_call_status = VOICE_CALL_START;
+			if ((v->dev_rx.enabled == VOICE_DEV_ENABLED)
+				&& (v->dev_tx.enabled == VOICE_DEV_ENABLED)) {
+				v->dev_state = DEV_READY;
+				MM_DBG("dev_state into ready\n");
+				wake_up(&v->dev_wait);
+			}
+		}
+		break;
+	case AUDDEV_EVT_DEV_CHG_VOICE:
+		if (v->dev_state == DEV_READY) {
+			v->dev_rx.enabled = VOICE_DEV_DISABLED;
+			v->dev_tx.enabled = VOICE_DEV_DISABLED;
+			v->dev_state = DEV_CHANGE;
+			mutex_lock(&voice.voc_lock);
+			if (v->voc_state == VOICE_ACQUIRE) {
+				/* send device change to modem */
+				voice_cmd_change();
+				mutex_unlock(&voice.voc_lock);
+				msm_snddev_enable_sidetone(v->dev_rx.dev_id,
+				0);
+				/* block to wait for CHANGE_START */
+				rc = wait_event_interruptible(
+				v->voc_wait, (v->voc_state == VOICE_CHANGE)
+				|| (atomic_read(&v->chg_start_flag) == 1)
+				|| (atomic_read(&v->rel_start_flag) == 1));
+			} else {
+				mutex_unlock(&voice.voc_lock);
+				MM_ERR(" Voice is not at ACQUIRE state\n");
+			}
+		} else if ((v->dev_state == DEV_INIT) ||
+				(v->dev_state == DEV_REL_DONE)) {
+				v->dev_rx.enabled = VOICE_DEV_DISABLED;
+				v->dev_tx.enabled = VOICE_DEV_DISABLED;
+		} else
+			MM_ERR(" device is not at proper state\n");
+		break;
+	case AUDDEV_EVT_DEV_RDY:
+		/* update the dev info */
+		if (evt_payload->voc_devinfo.dev_type == DIR_RX) {
+			for (i = 0; i < VOC_RX_VOL_ARRAY_NUM; i++) {
+				v->max_rx_vol[i] =
+					evt_payload->voc_devinfo.max_rx_vol[i];
+				v->min_rx_vol[i] =
+					evt_payload->voc_devinfo.min_rx_vol[i];
+			}
+		}
+		if (v->dev_state == DEV_CHANGE) {
+			if (evt_payload->voc_devinfo.dev_type == DIR_RX) {
+				v->dev_rx.dev_acdb_id =
+					evt_payload->voc_devinfo.acdb_dev_id;
+				v->dev_rx.sample =
+					evt_payload->voc_devinfo.dev_sample;
+				v->dev_rx.dev_id =
+				evt_payload->voc_devinfo.dev_id;
+				v->dev_rx.enabled = VOICE_DEV_ENABLED;
+			} else {
+				v->dev_tx.dev_acdb_id =
+					evt_payload->voc_devinfo.acdb_dev_id;
+				v->dev_tx.sample =
+					evt_payload->voc_devinfo.dev_sample;
+				v->dev_tx.enabled = VOICE_DEV_ENABLED;
+				v->dev_tx.dev_id =
+				evt_payload->voc_devinfo.dev_id;
+			}
+			if ((v->dev_rx.enabled == VOICE_DEV_ENABLED) &&
+				(v->dev_tx.enabled == VOICE_DEV_ENABLED)) {
+				v->dev_state = DEV_READY;
+				MM_DBG("dev state into ready\n");
+				voice_cmd_device_info(v);
+				wake_up(&v->dev_wait);
+				mutex_lock(&voice.voc_lock);
+				if (v->voc_state == VOICE_CHANGE) {
+					v->dev_event = DEV_CHANGE_READY;
+					complete(&v->complete);
+				}
+				mutex_unlock(&voice.voc_lock);
+			}
+		} else if ((v->dev_state == DEV_INIT) ||
+			(v->dev_state == DEV_REL_DONE)) {
+			if (evt_payload->voc_devinfo.dev_type == DIR_RX) {
+				v->dev_rx.dev_acdb_id =
+					evt_payload->voc_devinfo.acdb_dev_id;
+				v->dev_rx.sample =
+					evt_payload->voc_devinfo.dev_sample;
+				v->dev_rx.dev_id =
+				evt_payload->voc_devinfo.dev_id;
+				v->dev_rx.enabled = VOICE_DEV_ENABLED;
+			} else {
+				v->dev_tx.dev_acdb_id =
+					evt_payload->voc_devinfo.acdb_dev_id;
+				v->dev_tx.sample =
+					evt_payload->voc_devinfo.dev_sample;
+				v->dev_tx.dev_id =
+				evt_payload->voc_devinfo.dev_id;
+				v->dev_tx.enabled = VOICE_DEV_ENABLED;
+			}
+			if ((v->dev_rx.enabled == VOICE_DEV_ENABLED) &&
+				(v->dev_tx.enabled == VOICE_DEV_ENABLED) &&
+				(v->v_call_status == VOICE_CALL_START)) {
+				v->dev_state = DEV_READY;
+				MM_DBG("dev state into ready\n");
+				voice_cmd_device_info(v);
+				wake_up(&v->dev_wait);
+				mutex_lock(&voice.voc_lock);
+				if (v->voc_state == VOICE_CHANGE) {
+					v->dev_event = DEV_CHANGE_READY;
+					complete(&v->complete);
+				}
+				mutex_unlock(&voice.voc_lock);
+			}
+		} else
+			MM_ERR("Receive READY not at the proper state =%d\n",
+				v->dev_state);
+		break;
+	case AUDDEV_EVT_DEVICE_VOL_MUTE_CHG:
+		if (evt_payload->voc_devinfo.dev_type == DIR_TX)
+			v->dev_tx.mute =
+				evt_payload->voc_vm_info.dev_vm_val.mute;
+		else
+			v->dev_rx.volume = evt_payload->
+						voc_vm_info.dev_vm_val.vol;
+		/* send device info */
+		voice_cmd_device_info(v);
+		break;
+	case AUDDEV_EVT_REL_PENDING:
+		/* recover the tx mute and rx volume to the default values */
+		if (v->dev_state == DEV_READY) {
+			if (atomic_read(&v->rel_start_flag)) {
+				atomic_dec(&v->rel_start_flag);
+				if (evt_payload->voc_devinfo.dev_type == DIR_RX)
+					v->dev_rx.enabled = VOICE_DEV_DISABLED;
+				else
+					v->dev_tx.enabled = VOICE_DEV_DISABLED;
+				v->dev_state = DEV_REL_DONE;
+				wake_up(&v->dev_wait);
+				break;
+			}
+			mutex_lock(&voice.voc_lock);
+			if ((v->voc_state == VOICE_RELEASE) ||
+					(v->voc_state == VOICE_INIT)) {
+				if (evt_payload->voc_devinfo.dev_type
+							== DIR_RX) {
+					v->dev_rx.enabled = VOICE_DEV_DISABLED;
+				} else {
+					v->dev_tx.enabled = VOICE_DEV_DISABLED;
+				}
+				v->dev_state = DEV_REL_DONE;
+				mutex_unlock(&voice.voc_lock);
+				wake_up(&v->dev_wait);
+			} else {
+				/* send device change to modem */
+				voice_cmd_change();
+				mutex_unlock(&voice.voc_lock);
+				rc = wait_event_interruptible(
+				v->voc_wait, (v->voc_state == VOICE_CHANGE)
+				|| (atomic_read(&v->chg_start_flag) == 1)
+				|| (atomic_read(&v->rel_start_flag) == 1));
+				if (atomic_read(&v->rel_start_flag) == 1)
+					atomic_dec(&v->rel_start_flag);
+				/* clear Rx/Tx to Disable */
+				if (evt_payload->voc_devinfo.dev_type == DIR_RX)
+					v->dev_rx.enabled = VOICE_DEV_DISABLED;
+				else
+					v->dev_tx.enabled = VOICE_DEV_DISABLED;
+				v->dev_state = DEV_REL_DONE;
+				wake_up(&v->dev_wait);
+			}
+		} else if ((v->dev_state == DEV_INIT) ||
+				(v->dev_state == DEV_REL_DONE)) {
+			if (evt_payload->voc_devinfo.dev_type == DIR_RX)
+				v->dev_rx.enabled = VOICE_DEV_DISABLED;
+			else
+				v->dev_tx.enabled = VOICE_DEV_DISABLED;
+		}
+		break;
+	case AUDDEV_EVT_END_VOICE:
+		/* recover the tx mute and rx volume to the default values */
+		v->dev_tx.mute = v->default_mute_val;
+		v->dev_rx.volume = v->default_vol_val;
+
+		if (v->dev_rx.enabled == VOICE_DEV_ENABLED)
+			msm_snddev_enable_sidetone(v->dev_rx.dev_id, 0);
+
+		if ((v->dev_state == DEV_READY) ||
+			(v->dev_state == DEV_CHANGE)) {
+			if (atomic_read(&v->rel_start_flag)) {
+				atomic_dec(&v->rel_start_flag);
+				v->v_call_status = VOICE_CALL_END;
+				v->dev_state = DEV_REL_DONE;
+				wake_up(&v->dev_wait);
+				break;
+			}
+			mutex_lock(&voice.voc_lock);
+			if ((v->voc_state == VOICE_RELEASE) ||
+					(v->voc_state == VOICE_INIT)) {
+				v->v_call_status = VOICE_CALL_END;
+				v->dev_state = DEV_REL_DONE;
+				mutex_unlock(&voice.voc_lock);
+				wake_up(&v->dev_wait);
+			} else {
+				/* send mute and default volume value to MCAD */
+				voice_cmd_device_info(v);
+				/* send device change to modem */
+				voice_cmd_change();
+				mutex_unlock(&voice.voc_lock);
+				/* block to wait for RELEASE_START
+						or CHANGE_START */
+				rc = wait_event_interruptible(
+				v->voc_wait, (v->voc_state == VOICE_CHANGE)
+				|| (atomic_read(&v->chg_start_flag) == 1)
+				|| (atomic_read(&v->rel_start_flag) == 1));
+				if (atomic_read(&v->rel_start_flag) == 1)
+					atomic_dec(&v->rel_start_flag);
+				/* set voice call to END state */
+				v->v_call_status = VOICE_CALL_END;
+				v->dev_state = DEV_REL_DONE;
+				wake_up(&v->dev_wait);
+			}
+		} else
+			v->v_call_status = VOICE_CALL_END;
+		break;
+	case AUDDEV_EVT_FREQ_CHG:
+		MM_DBG("Voice Driver got sample rate change Event\n");
+		MM_DBG("sample rate %d\n", evt_payload->freq_info.sample_rate);
+		MM_DBG("dev_type %d\n", evt_payload->freq_info.dev_type);
+		MM_DBG("acdb_dev_id %d\n", evt_payload->freq_info.acdb_dev_id);
+		if (v->dev_state == DEV_READY) {
+			v->dev_tx.enabled = VOICE_DEV_DISABLED;
+			v->dev_state = DEV_CHANGE;
+			mutex_lock(&voice.voc_lock);
+			if (v->voc_state == VOICE_ACQUIRE) {
+				msm_snddev_enable_sidetone(v->dev_rx.dev_id,
+				0);
+				/* send device change to modem */
+				voice_cmd_change();
+				mutex_unlock(&voice.voc_lock);
+				/* block to wait for CHANGE_START */
+				rc = wait_event_interruptible(
+				v->voc_wait, (v->voc_state == VOICE_CHANGE)
+				|| (atomic_read(&v->chg_start_flag) == 1)
+				|| (atomic_read(&v->rel_start_flag) == 1));
+			} else {
+				mutex_unlock(&voice.voc_lock);
+				MM_ERR(" Voice is not at ACQUIRE state\n");
+			}
+		} else if ((v->dev_state == DEV_INIT) ||
+				(v->dev_state == DEV_REL_DONE)) {
+				v->dev_tx.enabled = VOICE_DEV_DISABLED;
+		} else
+			MM_ERR("Event not at the proper state =%d\n",
+				v->dev_state);
+		break;
+	default:
+		MM_ERR("UNKNOWN EVENT\n");
+	}
+	return;
+}
+EXPORT_SYMBOL(voice_auddev_cb_function);
+
+static void remote_cb_function(void *context, u32 param,
+				void *evt_buf, u32 len)
+{
+	struct voice_header *hdr;
+	struct voice_data *v = context;
+
+	hdr = (struct voice_header *)evt_buf;
+
+	MM_INFO("len=%d id=%d\n", len, hdr->id);
+
+	if (len <= 0) {
+		MM_ERR("unexpected event with length %d \n", len);
+		return;
+	}
+
+	switch (hdr->id) {
+	case EVENT_ACQUIRE_START:
+		atomic_inc(&v->acq_start_flag);
+		wake_up(&v->dev_wait);
+		v->voc_event = VOICE_ACQUIRE_START;
+		v->network = ((struct voice_network *)evt_buf)->network_info;
+		complete(&v->complete);
+		break;
+	case EVENT_RELEASE_START:
+		/* If ACQUIRED come in before the RELEASE,
+		* will only services the RELEASE */
+		atomic_inc(&v->rel_start_flag);
+		wake_up(&v->voc_wait);
+		wake_up(&v->dev_wait);
+		v->voc_event = VOICE_RELEASE_START;
+		complete(&v->complete);
+		break;
+	case EVENT_CHANGE_START:
+		atomic_inc(&v->chg_start_flag);
+		wake_up(&v->voc_wait);
+		v->voc_event = VOICE_CHANGE_START;
+		complete(&v->complete);
+		break;
+	case EVENT_NETWORK_RECONFIG:
+		/* send network change to audio_dev,
+		if sample rate is less than 16k,
+		otherwise, send acquire done */
+		v->voc_event = VOICE_NETWORK_RECONFIG;
+		v->network = ((struct voice_network *)evt_buf)->network_info;
+		complete(&v->complete);
+		break;
+	default:
+		MM_ERR("Undefined event %d \n", hdr->id);
+	}
+
+}
+
+static int voice_cmd_init(struct voice_data *v)
+{
+
+	struct voice_init cmd;
+	int err;
+
+	MM_DBG("\n"); /* Macro prints the file name and function */
+
+	cmd.hdr.id = CMD_VOICE_INIT;
+	cmd.hdr.data_len = sizeof(struct voice_init) -
+				sizeof(struct voice_header);
+	cmd.cb_handle = v->cb_handle;
+
+	err = dalrpc_fcn_5(VOICE_DALRPC_CMD, v->handle, &cmd,
+			 sizeof(struct voice_init));
+
+	if (err)
+		MM_ERR("Voice init command failed\n");
+	return err;
+}
+
+static int voice_cmd_acquire_done(struct voice_data *v)
+{
+	struct voice_header hdr;
+	int err;
+
+	hdr.id = CMD_ACQUIRE_DONE;
+	hdr.data_len = 0;
+
+	MM_INFO("\n"); /* Macro prints the file name and function */
+
+	/* Enable HW sidetone if device supports it  */
+	msm_snddev_enable_sidetone(v->dev_rx.dev_id, 1);
+
+	err = dalrpc_fcn_5(VOICE_DALRPC_CMD, v->handle, &hdr,
+			 sizeof(struct voice_header));
+
+	if (err)
+		MM_ERR("Voice acquire done command failed\n");
+	return err;
+}
+
+static int voice_cmd_device_info(struct voice_data *v)
+{
+	struct voice_device cmd;
+	int err, vol;
+
+	MM_INFO("tx_dev=%d, rx_dev=%d, tx_sample=%d, tx_mute=%d\n",
+			v->dev_tx.dev_acdb_id, v->dev_rx.dev_acdb_id,
+			v->dev_tx.sample, v->dev_tx.mute);
+
+	mutex_lock(&voice.vol_lock);
+
+	cmd.hdr.id = CMD_DEVICE_INFO;
+	cmd.hdr.data_len = sizeof(struct voice_device) -
+			sizeof(struct voice_header);
+	cmd.tx_device = v->dev_tx.dev_acdb_id;
+	cmd.rx_device = v->dev_rx.dev_acdb_id;
+	if (v->network == NETWORK_WCDMA_WB)
+		vol = v->min_rx_vol[VOC_WB_INDEX] +
+			((v->max_rx_vol[VOC_WB_INDEX] -
+			v->min_rx_vol[VOC_WB_INDEX]) * v->dev_rx.volume)/100;
+	else
+		vol = v->min_rx_vol[VOC_NB_INDEX] +
+			((v->max_rx_vol[VOC_NB_INDEX] -
+			v->min_rx_vol[VOC_NB_INDEX]) * v->dev_rx.volume)/100;
+	cmd.rx_volume = (u32)vol; /* in mb */
+	cmd.rx_mute = 0;
+	cmd.tx_mute = v->dev_tx.mute;
+	cmd.rx_sample = v->dev_rx.sample/1000;
+	cmd.tx_sample = v->dev_tx.sample/1000;
+
+	MM_DBG("rx_vol=%d, rx_sample=%d\n", cmd.rx_volume, v->dev_rx.sample);
+
+	err = dalrpc_fcn_5(VOICE_DALRPC_CMD, v->handle, &cmd,
+			 sizeof(struct voice_device));
+
+	mutex_unlock(&voice.vol_lock);
+
+	if (err)
+		MM_ERR("Voice device command failed\n");
+	return err;
+}
+EXPORT_SYMBOL(voice_cmd_device_info);
+
+void voice_change_sample_rate(struct voice_data *v)
+{
+	int freq = 48000;
+	int rc = 0;
+
+	MM_DBG("network =%d, vote freq=%d\n", v->network, freq);
+	if (freq != v->dev_tx.sample) {
+		rc = msm_snddev_request_freq(&freq, 0,
+				SNDDEV_CAP_TX, AUDDEV_CLNT_VOC);
+		if (rc >= 0) {
+			v->dev_tx.sample = freq;
+			MM_DBG(" vote for freq=%d successfully \n", freq);
+		} else
+			MM_ERR(" voting for freq=%d failed.\n", freq);
+	}
+}
+
+static int voice_thread(void *data)
+{
+	struct voice_data *v = (struct voice_data *)data;
+	int rc = 0;
+
+	MM_INFO("voice_thread() start\n");
+
+	while (!kthread_should_stop()) {
+		wait_for_completion(&v->complete);
+		init_completion(&v->complete);
+
+		MM_DBG(" voc_event=%d, voice state =%d, dev_event=%d\n",
+				v->voc_event, v->voc_state, v->dev_event);
+		switch (v->voc_event) {
+		case VOICE_ACQUIRE_START:
+			/* check if dev_state = READY */
+			/* if ready, send device_info and acquire_done */
+			/* if not ready, block to wait the dev_state = READY */
+			if ((v->voc_state == VOICE_INIT) ||
+				(v->voc_state == VOICE_RELEASE)) {
+				if (v->dev_state == DEV_READY) {
+					mutex_lock(&voice.voc_lock);
+					voice_change_sample_rate(v);
+					rc = voice_cmd_device_info(v);
+					rc = voice_cmd_acquire_done(v);
+					v->voc_state = VOICE_ACQUIRE;
+					mutex_unlock(&voice.voc_lock);
+					broadcast_event(
+					AUDDEV_EVT_VOICE_STATE_CHG,
+					VOICE_STATE_INCALL, SESSION_IGNORE);
+				} else {
+					rc = wait_event_interruptible(
+					v->dev_wait,
+					(v->dev_state == DEV_READY)
+					|| (atomic_read(&v->rel_start_flag)
+						== 1));
+					if (atomic_read(&v->rel_start_flag)
+						== 1) {
+						v->voc_state = VOICE_RELEASE;
+						atomic_dec(&v->rel_start_flag);
+						msm_snddev_withdraw_freq(0,
+						SNDDEV_CAP_TX, AUDDEV_CLNT_VOC);
+						broadcast_event(
+						AUDDEV_EVT_VOICE_STATE_CHG,
+						VOICE_STATE_OFFCALL,
+						SESSION_IGNORE);
+					} else {
+						mutex_lock(&voice.voc_lock);
+						voice_change_sample_rate(v);
+						rc = voice_cmd_device_info(v);
+						rc = voice_cmd_acquire_done(v);
+						v->voc_state = VOICE_ACQUIRE;
+						mutex_unlock(&voice.voc_lock);
+						broadcast_event(
+						AUDDEV_EVT_VOICE_STATE_CHG,
+						VOICE_STATE_INCALL,
+						SESSION_IGNORE);
+					}
+				}
+			} else
+				MM_ERR("Get this event at the wrong state\n");
+			if (atomic_read(&v->acq_start_flag))
+				atomic_dec(&v->acq_start_flag);
+			break;
+		case VOICE_RELEASE_START:
+			MM_DBG("broadcast voice call end\n");
+			broadcast_event(AUDDEV_EVT_VOICE_STATE_CHG,
+					VOICE_STATE_OFFCALL, SESSION_IGNORE);
+			if ((v->dev_state == DEV_REL_DONE) ||
+					(v->dev_state == DEV_INIT)) {
+				v->voc_state = VOICE_RELEASE;
+				msm_snddev_withdraw_freq(0, SNDDEV_CAP_TX,
+					AUDDEV_CLNT_VOC);
+			} else {
+				/* wait for the dev_state = RELEASE */
+				rc = wait_event_interruptible(v->dev_wait,
+					(v->dev_state == DEV_REL_DONE)
+				|| (atomic_read(&v->acq_start_flag) == 1));
+				if (atomic_read(&v->acq_start_flag) == 1)
+					atomic_dec(&v->acq_start_flag);
+				v->voc_state = VOICE_RELEASE;
+				msm_snddev_withdraw_freq(0, SNDDEV_CAP_TX,
+					AUDDEV_CLNT_VOC);
+			}
+			if (atomic_read(&v->rel_start_flag))
+				atomic_dec(&v->rel_start_flag);
+			break;
+		case VOICE_CHANGE_START:
+			if (v->voc_state == VOICE_ACQUIRE)
+				v->voc_state = VOICE_CHANGE;
+			else
+				MM_ERR("Get this event at the wrong state\n");
+			wake_up(&v->voc_wait);
+			if (atomic_read(&v->chg_start_flag))
+				atomic_dec(&v->chg_start_flag);
+			break;
+		case VOICE_NETWORK_RECONFIG:
+			if ((v->voc_state == VOICE_ACQUIRE)
+				|| (v->voc_state == VOICE_CHANGE)) {
+				voice_change_sample_rate(v);
+				rc = voice_cmd_device_info(v);
+				rc = voice_cmd_acquire_done(v);
+			}
+			break;
+		default:
+			break;
+		}
+
+		switch (v->dev_event) {
+		case DEV_CHANGE_READY:
+			if (v->voc_state == VOICE_CHANGE) {
+				mutex_lock(&voice.voc_lock);
+				msm_snddev_enable_sidetone(v->dev_rx.dev_id,
+				1);
+				/* update voice state */
+				v->voc_state = VOICE_ACQUIRE;
+				v->dev_event = 0;
+				mutex_unlock(&voice.voc_lock);
+				broadcast_event(AUDDEV_EVT_VOICE_STATE_CHG,
+					VOICE_STATE_INCALL, SESSION_IGNORE);
+			} else {
+				mutex_lock(&voice.voc_lock);
+				v->dev_event = 0;
+				mutex_unlock(&voice.voc_lock);
+				MM_ERR("Get this event at the wrong state\n");
+			}
+			break;
+		default:
+			mutex_lock(&voice.voc_lock);
+			v->dev_event = 0;
+			mutex_unlock(&voice.voc_lock);
+			break;
+		}
+	}
+	return 0;
+}
+
+static int __init voice_init(void)
+{
+	int rc, i;
+	struct voice_data *v = &voice;
+	MM_INFO("\n"); /* Macro prints the file name and function */
+
+	mutex_init(&voice.voc_lock);
+	mutex_init(&voice.vol_lock);
+	v->handle = NULL;
+	v->cb_handle = NULL;
+
+	/* set default value */
+	v->default_mute_val = 1;  /* default is mute */
+	v->default_vol_val = 0;
+	v->default_sample_val = 8000;
+	for (i = 0; i < VOC_RX_VOL_ARRAY_NUM; i++) {
+		v->max_rx_vol[i] = 0;
+		v->min_rx_vol[i] = 0;
+	}
+	v->network = NETWORK_GSM;
+
+	/* initialize dev_rx and dev_tx */
+	memset(&v->dev_tx, 0, sizeof(struct device_data));
+	memset(&v->dev_rx, 0, sizeof(struct device_data));
+	v->dev_rx.volume = v->default_vol_val;
+	v->dev_tx.mute = v->default_mute_val;
+
+	v->dev_state = DEV_INIT;
+	v->voc_state = VOICE_INIT;
+	atomic_set(&v->rel_start_flag, 0);
+	atomic_set(&v->acq_start_flag, 0);
+	v->dev_event = 0;
+	v->voc_event = 0;
+	init_completion(&voice.complete);
+	init_waitqueue_head(&v->dev_wait);
+	init_waitqueue_head(&v->voc_wait);
+
+	 /* get device handle */
+	rc = daldevice_attach(VOICE_DALRPC_DEVICEID,
+				VOICE_DALRPC_PORT_NAME,
+				VOICE_DALRPC_CPU,
+				&v->handle);
+	if (rc) {
+		MM_ERR("Voc DALRPC call to Modem attach failed\n");
+		goto done;
+	}
+
+	/* Allocate the callback handle */
+	v->cb_handle = dalrpc_alloc_cb(v->handle, remote_cb_function, v);
+	if (v->cb_handle == NULL) {
+		MM_ERR("Allocate Callback failure\n");
+		goto err;
+	}
+
+	/* setup the callback */
+	rc = voice_cmd_init(v);
+	if (rc)
+		goto err1;
+
+	v->device_events = AUDDEV_EVT_DEV_CHG_VOICE |
+			AUDDEV_EVT_DEV_RDY |
+			AUDDEV_EVT_REL_PENDING |
+			AUDDEV_EVT_START_VOICE |
+			AUDDEV_EVT_END_VOICE |
+			AUDDEV_EVT_DEVICE_VOL_MUTE_CHG |
+			AUDDEV_EVT_FREQ_CHG;
+
+	MM_DBG(" to register call back \n");
+	/* register callback to auddev */
+	auddev_register_evt_listner(v->device_events, AUDDEV_CLNT_VOC,
+				0, voice_auddev_cb_function, v);
+
+	/* create and start thread */
+	v->task = kthread_run(voice_thread, v, "voice");
+	if (IS_ERR(v->task)) {
+		rc = PTR_ERR(v->task);
+		v->task = NULL;
+	} else
+		goto done;
+
+err1:   dalrpc_dealloc_cb(v->handle, v->cb_handle);
+err:
+	daldevice_detach(v->handle);
+	v->handle = NULL;
+done:
+	return rc;
+}
+
+late_initcall(voice_init);