// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) 2008 Google, Inc.
 * Copyright (C) 2008 HTC Corporation
 * Copyright (c) 2009-2020, The Linux Foundation. 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/slab.h>
#include <linux/atomic.h>
#include <asm/ioctls.h>
#include <linux/debugfs.h>
#include <linux/compat.h>
#include <dsp/msm_audio_ion.h>
#include "audio_utils_aio.h"
#ifdef CONFIG_USE_DEV_CTRL_VOLUME
#include <linux/qdsp6v2/audio_dev_ctl.h>
#endif /*CONFIG_USE_DEV_CTRL_VOLUME*/
static DEFINE_MUTEX(lock);
#ifdef CONFIG_DEBUG_FS

int audio_aio_debug_open(struct inode *inode, struct file *file)
{
	file->private_data = inode->i_private;
	return 0;
}

ssize_t audio_aio_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 q6audio_aio *audio;

	mutex_lock(&lock);
	if (file->private_data != NULL) {
		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,
				"feedback %d\n", audio->feedback);
		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,
				"inqueue empty %d\n",
				list_empty(&audio->in_queue));
		n += scnprintf(buffer + n, debug_bufmax - n,
				"outqueue empty %d\n",
				list_empty(&audio->out_queue));
	}
	mutex_unlock(&lock);
	buffer[n] = 0;
	return simple_read_from_buffer(buf, count, ppos, buffer, n);
}
#endif

static long audio_aio_ioctl(struct file *file, unsigned int cmd,
				unsigned long arg);
#ifdef CONFIG_COMPAT
static long audio_aio_compat_ioctl(struct file *file, unsigned int cmd,
				unsigned long arg);
#else
#define audio_aio_compat_ioctl NULL
#endif
int insert_eos_buf(struct q6audio_aio *audio,
		struct audio_aio_buffer_node *buf_node)
{
	struct dec_meta_out *eos_buf = buf_node->kvaddr;

	pr_debug("%s[%pK]:insert_eos_buf\n", __func__, audio);
	eos_buf->num_of_frames = 0xFFFFFFFF;
	eos_buf->meta_out_dsp[0].offset_to_frame = 0x0;
	eos_buf->meta_out_dsp[0].nflags = AUDIO_DEC_EOS_SET;
	return sizeof(struct dec_meta_out) +
		sizeof(eos_buf->meta_out_dsp[0]);
}

/* Routine which updates read buffers of driver/dsp,
 * for flush operation as DSP output might not have proper
 * value set
 */
static int insert_meta_data_flush(struct q6audio_aio *audio,
	struct audio_aio_buffer_node *buf_node)
{
	struct dec_meta_out *meta_data = buf_node->kvaddr;

	meta_data->num_of_frames = 0x0;
	meta_data->meta_out_dsp[0].offset_to_frame = 0x0;
	meta_data->meta_out_dsp[0].nflags = 0x0;
	return sizeof(struct dec_meta_out) +
		sizeof(meta_data->meta_out_dsp[0]);
}

static int audio_aio_ion_lookup_vaddr(struct q6audio_aio *audio, void *addr,
					unsigned long len,
					struct audio_aio_ion_region **region)
{
	struct audio_aio_ion_region *region_elt;

	int match_count = 0;

	*region = NULL;

	/* returns physical address or zero */
	list_for_each_entry(region_elt, &audio->ion_region_queue, list) {
		if (addr >= region_elt->vaddr &&
			addr < region_elt->vaddr + region_elt->len &&
			addr + len <= region_elt->vaddr + region_elt->len &&
			addr + len > addr) {
			/* to avoid integer addition overflow */

			/* offset since we could pass vaddr inside a registered
			 * ion buffer
			 */

			match_count++;
			if (!*region)
				*region = region_elt;
		}
	}

	if (match_count > 1) {
		pr_err("%s[%pK]:multiple hits for vaddr %pK, len %ld\n",
			__func__, audio, addr, len);
		list_for_each_entry(region_elt, &audio->ion_region_queue,
					list) {
			if (addr >= region_elt->vaddr &&
			addr < region_elt->vaddr + region_elt->len &&
			addr + len <= region_elt->vaddr + region_elt->len &&
			addr + len > addr)
				pr_err("\t%s[%pK]:%pK, %ld --> %pK\n",
					__func__, audio,
					region_elt->vaddr,
					region_elt->len,
					&region_elt->paddr);
		}
	}

	return *region ? 0 : -1;
}

static phys_addr_t audio_aio_ion_fixup(struct q6audio_aio *audio, void *addr,
				unsigned long len, int ref_up, void **kvaddr)
{
	struct audio_aio_ion_region *region;
	phys_addr_t paddr;
	int ret;

	ret = audio_aio_ion_lookup_vaddr(audio, addr, len, &region);
	if (ret) {
		pr_err("%s[%pK]:lookup (%pK, %ld) failed\n",
				__func__, audio, addr, len);
		return 0;
	}
	if (ref_up)
		region->ref_cnt++;
	else
		region->ref_cnt--;
	pr_debug("%s[%pK]:found region %pK ref_cnt %d\n",
			__func__, audio, region, region->ref_cnt);
	paddr = region->paddr + (addr - region->vaddr);
	/* provide kernel virtual address for accessing meta information */
	if (kvaddr)
		*kvaddr = (void *) (region->kvaddr + (addr - region->vaddr));
	return paddr;
}

static int audio_aio_pause(struct q6audio_aio  *audio)
{
	int rc = -EINVAL;

	pr_debug("%s[%pK], enabled = %d\n", __func__, audio,
			audio->enabled);
	if (audio->enabled) {
		rc = q6asm_cmd(audio->ac, CMD_PAUSE);
		if (rc < 0)
			pr_err_ratelimited("%s[%pK]: pause cmd failed rc=%d\n",
				__func__, audio, rc);

		if (rc == 0) {
			/* Send suspend only if pause was successful */
			rc = q6asm_cmd(audio->ac, CMD_SUSPEND);
			if (rc < 0)
				pr_err_ratelimited("%s[%pK]: suspend cmd failed rc=%d\n",
					__func__, audio, rc);
		} else
			pr_err_ratelimited("%s[%pK]: not sending suspend since pause failed\n",
				__func__, audio);

	} else
		pr_err("%s[%pK]: Driver not enabled\n", __func__, audio);
	return rc;
}

static int audio_aio_flush(struct q6audio_aio  *audio)
{
	int rc = 0;

	if (audio->enabled) {
		/* Implicitly issue a pause to the decoder before flushing if
		 * it is not in pause state
		 */
		if (!(audio->drv_status & ADRV_STATUS_PAUSE)) {
			rc = audio_aio_pause(audio);
			if (rc < 0)
				pr_err_ratelimited("%s[%pK}: pause cmd failed rc=%d\n",
					__func__, audio,
					rc);
			else
				audio->drv_status |= ADRV_STATUS_PAUSE;
		}
		rc = q6asm_cmd(audio->ac, CMD_FLUSH);
		if (rc < 0)
			pr_err_ratelimited("%s[%pK]: flush cmd failed rc=%d\n",
				__func__, audio, rc);
		/* Not in stop state, reenable the stream */
		if (audio->stopped == 0) {
			rc = audio_aio_enable(audio);
			if (rc)
				pr_err_ratelimited("%s[%pK]:audio re-enable failed\n",
					__func__, audio);
			else {
				audio->enabled = 1;
				if (audio->drv_status & ADRV_STATUS_PAUSE)
					audio->drv_status &= ~ADRV_STATUS_PAUSE;
			}
		}
	}
	pr_debug("%s[%pK]:in_bytes %d\n",
			__func__, audio, atomic_read(&audio->in_bytes));
	pr_debug("%s[%pK]:in_samples %d\n",
			__func__, audio, atomic_read(&audio->in_samples));
	atomic_set(&audio->in_bytes, 0);
	atomic_set(&audio->in_samples, 0);
	return rc;
}

static int audio_aio_outport_flush(struct q6audio_aio *audio)
{
	int rc;

	rc = q6asm_cmd(audio->ac, CMD_OUT_FLUSH);
	if (rc < 0)
		pr_err_ratelimited("%s[%pK}: output port flush cmd failed rc=%d\n",
			__func__, audio, rc);
	return rc;
}

/* Write buffer to DSP / Handle Ack from DSP */
void audio_aio_async_write_ack(struct q6audio_aio *audio, uint32_t token,
				uint32_t *payload)
{
	unsigned long flags;
	union msm_audio_event_payload event_payload;
	struct audio_aio_buffer_node *used_buf;

	/* No active flush in progress */
	if (audio->wflush)
		return;

	spin_lock_irqsave(&audio->dsp_lock, flags);
	if (list_empty(&audio->out_queue)) {
		pr_warn("%s: ignore unexpected event from dsp\n", __func__);
		spin_unlock_irqrestore(&audio->dsp_lock, flags);
		return;
	}
	used_buf = list_first_entry(&audio->out_queue,
					struct audio_aio_buffer_node, list);
	if (token == used_buf->token) {
		list_del(&used_buf->list);
		spin_unlock_irqrestore(&audio->dsp_lock, flags);
		pr_debug("%s[%pK]:consumed buffer\n", __func__, audio);
		event_payload.aio_buf = used_buf->buf;
		audio_aio_post_event(audio, AUDIO_EVENT_WRITE_DONE,
					event_payload);
		kfree(used_buf);
		if (list_empty(&audio->out_queue) &&
			(audio->drv_status & ADRV_STATUS_FSYNC)) {
			pr_debug("%s[%pK]: list is empty, reached EOS in Tunnel\n",
				 __func__, audio);
			wake_up(&audio->write_wait);
		}
	} else {
		pr_err("%s[%pK]:expected=%x ret=%x\n",
			__func__, audio, used_buf->token, token);
		spin_unlock_irqrestore(&audio->dsp_lock, flags);
	}
}

/* ------------------- device --------------------- */
void audio_aio_async_out_flush(struct q6audio_aio *audio)
{
	struct audio_aio_buffer_node *buf_node;
	struct list_head *ptr, *next;
	union msm_audio_event_payload payload;
	unsigned long flags;

	pr_debug("%s[%pK}\n", __func__, audio);
	/* EOS followed by flush, EOS response not guranteed, free EOS i/p
	 * buffer
	 */
	spin_lock_irqsave(&audio->dsp_lock, flags);

	if (audio->eos_flag && (audio->eos_write_payload.aio_buf.buf_addr)) {
		pr_debug("%s[%pK]: EOS followed by flush received,acknowledge eos i/p buffer immediately\n",
			__func__, audio);
		audio_aio_post_event(audio, AUDIO_EVENT_WRITE_DONE,
				audio->eos_write_payload);
		memset(&audio->eos_write_payload, 0,
			sizeof(union msm_audio_event_payload));
	}
	spin_unlock_irqrestore(&audio->dsp_lock, flags);
	list_for_each_safe(ptr, next, &audio->out_queue) {
		buf_node = list_entry(ptr, struct audio_aio_buffer_node, list);
		list_del(&buf_node->list);
		payload.aio_buf = buf_node->buf;
		audio_aio_post_event(audio, AUDIO_EVENT_WRITE_DONE, payload);
		kfree(buf_node);
		pr_debug("%s[%pK]: Propagate WRITE_DONE during flush\n",
				__func__, audio);
	}
}

void audio_aio_async_in_flush(struct q6audio_aio *audio)
{
	struct audio_aio_buffer_node *buf_node;
	struct list_head *ptr, *next;
	union msm_audio_event_payload payload;

	pr_debug("%s[%pK]\n", __func__, audio);
	list_for_each_safe(ptr, next, &audio->in_queue) {
		buf_node = list_entry(ptr, struct audio_aio_buffer_node, list);
		list_del(&buf_node->list);
		/* Forcefull send o/p eos buffer after flush, if no eos response
		 * received by dsp even after sending eos command
		 */
		if ((audio->eos_rsp != 1) && audio->eos_flag) {
			pr_debug("%s[%pK]: send eos on o/p buffer during flush\n",
				 __func__, audio);
			payload.aio_buf = buf_node->buf;
			payload.aio_buf.data_len =
					insert_eos_buf(audio, buf_node);
			audio->eos_flag = 0;
		} else {
			payload.aio_buf = buf_node->buf;
			payload.aio_buf.data_len =
					insert_meta_data_flush(audio, buf_node);
		}
		audio_aio_post_event(audio, AUDIO_EVENT_READ_DONE, payload);
		kfree(buf_node);
		pr_debug("%s[%pK]: Propagate READ_DONE during flush\n",
				__func__, audio);
	}
}

int audio_aio_enable(struct q6audio_aio  *audio)
{
	/* 2nd arg: 0 -> run immediately
	 * 3rd arg: 0 -> msw_ts,
	 * 4th arg: 0 ->lsw_ts
	 */
	return q6asm_run(audio->ac, 0x00, 0x00, 0x00);
}

int audio_aio_disable(struct q6audio_aio *audio)
{
	int rc = 0;

	if (audio->opened) {
		audio->enabled = 0;
		audio->opened = 0;
		pr_debug("%s[%pK]: inbytes[%d] insamples[%d]\n", __func__,
			audio, atomic_read(&audio->in_bytes),
			atomic_read(&audio->in_samples));
		/* Close the session */
		rc = q6asm_cmd(audio->ac, CMD_CLOSE);
		if (rc < 0)
			pr_err_ratelimited("%s[%pK]:Failed to close the session rc=%d\n",
				__func__, audio, rc);
		audio->stopped = 1;
		wake_up(&audio->write_wait);
		wake_up(&audio->cmd_wait);
	}
	pr_debug("%s[%pK]:enabled[%d]\n", __func__, audio, audio->enabled);
	return rc;
}

void audio_aio_reset_ion_region(struct q6audio_aio *audio)
{
	struct audio_aio_ion_region *region;
	struct list_head *ptr, *next;

	list_for_each_safe(ptr, next, &audio->ion_region_queue) {
		region = list_entry(ptr, struct audio_aio_ion_region, list);
		list_del(&region->list);
		msm_audio_ion_free(region->dma_buf);
		kfree(region);
	}
}

void audio_aio_reset_event_queue(struct q6audio_aio *audio)
{
	unsigned long flags;
	struct audio_aio_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 audio_aio_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 audio_aio_event, list);
		list_del(&drv_evt->list);
		kfree(drv_evt);
	}
	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
}

static void audio_aio_unmap_ion_region(struct q6audio_aio *audio)
{
	struct audio_aio_ion_region *region;
	struct list_head *ptr, *next;
	int rc = -EINVAL;

	pr_debug("%s[%pK]:\n", __func__, audio);
	list_for_each_safe(ptr, next, &audio->ion_region_queue) {
		region = list_entry(ptr, struct audio_aio_ion_region, list);
		if (region != NULL) {
			pr_debug("%s[%pK]: phy_address = 0x%pK\n",
				__func__, audio, &region->paddr);
			rc = q6asm_memory_unmap(audio->ac,
						region->paddr, IN);
			if (rc < 0)
				pr_err("%s[%pK]: memory unmap failed\n",
					__func__, audio);
		}
	}
}

#ifdef CONFIG_USE_DEV_CTRL_VOLUME

static void audio_aio_listner(u32 evt_id, union auddev_evt_data *evt_payload,
			void *private_data)
{
	struct q6audio_aio *audio = (struct q6audio_aio *) private_data;
	int rc  = 0;

	switch (evt_id) {
	case AUDDEV_EVT_STREAM_VOL_CHG:
		audio->volume = evt_payload->session_vol;
		pr_debug("%s[%pK]: AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d, enabled = %d\n",
			__func__, audio, audio->volume, audio->enabled);
		if (audio->enabled == 1) {
			if (audio->ac) {
				rc = q6asm_set_volume(audio->ac, audio->volume);
				if (rc < 0) {
					pr_err("%s[%pK]: Send Volume command failed rc=%d\n",
						__func__, audio, rc);
				}
			}
		}
		break;
	default:
		pr_err("%s[%pK]:ERROR:wrong event\n", __func__, audio);
		break;
	}
}

int register_volume_listener(struct q6audio_aio *audio)
{
	int rc  = 0;

	audio->device_events = AUDDEV_EVT_STREAM_VOL_CHG;
	audio->drv_status &= ~ADRV_STATUS_PAUSE;

	rc = auddev_register_evt_listner(audio->device_events,
					AUDDEV_CLNT_DEC,
					audio->ac->session,
					audio_aio_listner,
					(void *)audio);
	if (rc < 0) {
		pr_err("%s[%pK]: Event listener failed\n", __func__, audio);
		rc = -EACCES;
	}
	return rc;
}
void unregister_volume_listener(struct q6audio_aio *audio)
{
	auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->ac->session);
}

int enable_volume_ramp(struct q6audio_aio *audio)
{
	int rc = 0;
	struct asm_softpause_params softpause;
	struct asm_softvolume_params softvol;

	if (audio->ac == NULL)
		return -EINVAL;
	pr_debug("%s[%pK]\n", __func__, audio);
	softpause.enable = SOFT_PAUSE_ENABLE;
	softpause.period = SOFT_PAUSE_PERIOD;
	softpause.step = SOFT_PAUSE_STEP;
	softpause.rampingcurve = SOFT_PAUSE_CURVE_LINEAR;

	softvol.period = SOFT_VOLUME_PERIOD;
	softvol.step = SOFT_VOLUME_STEP;
	softvol.rampingcurve = SOFT_VOLUME_CURVE_LINEAR;

	if (softpause.rampingcurve == SOFT_PAUSE_CURVE_LINEAR)
		softpause.step = SOFT_PAUSE_STEP_LINEAR;
	if (softvol.rampingcurve == SOFT_VOLUME_CURVE_LINEAR)
		softvol.step = SOFT_VOLUME_STEP_LINEAR;
	rc = q6asm_set_volume(audio->ac, audio->volume);
	if (rc < 0) {
		pr_err("%s: Send Volume command failed rc=%d\n",
			__func__, rc);
		return rc;
	}
	rc = q6asm_set_softpause(audio->ac, &softpause);
	if (rc < 0) {
		pr_err("%s: Send SoftPause Param failed rc=%d\n",
			__func__, rc);
		return rc;
	}
	rc = q6asm_set_softvolume(audio->ac, &softvol);
	if (rc < 0) {
		pr_err("%s: Send SoftVolume Param failed rc=%d\n",
		__func__, rc);
		return rc;
	}
	/* disable mute by default */
	rc = q6asm_set_mute(audio->ac, 0);
	if (rc < 0) {
		pr_err("%s: Send mute command failed rc=%d\n",
			__func__, rc);
		return rc;
	}
	return rc;
}

#else /*CONFIG_USE_DEV_CTRL_VOLUME*/
int register_volume_listener(struct q6audio_aio *audio)
{
	return 0;/* do nothing */
}
void unregister_volume_listener(struct q6audio_aio *audio)
{
	return;/* do nothing */
}
int enable_volume_ramp(struct q6audio_aio *audio)
{
	return 0; /* do nothing */
}
#endif /*CONFIG_USE_DEV_CTRL_VOLUME*/

int audio_aio_release(struct inode *inode, struct file *file)
{
	unsigned long flags = 0;
	struct q6audio_aio *audio = file->private_data;

	pr_debug("%s[%pK]\n", __func__, audio);
	mutex_lock(&lock);
	mutex_lock(&audio->lock);
	mutex_lock(&audio->read_lock);
	mutex_lock(&audio->write_lock);
	audio->wflush = 1;
	if (audio->wakelock_voted &&
		(audio->audio_ws_mgr != NULL) &&
		(audio->miscdevice != NULL)) {
		audio->wakelock_voted = false;
		mutex_lock(&audio->audio_ws_mgr->ws_lock);
		if ((audio->audio_ws_mgr->ref_cnt > 0) &&
				(--audio->audio_ws_mgr->ref_cnt == 0)) {
			pm_relax(audio->miscdevice->this_device);
		}
		mutex_unlock(&audio->audio_ws_mgr->ws_lock);
	}
	if (audio->enabled)
		audio_aio_flush(audio);
	audio->wflush = 0;
	audio->drv_ops.out_flush(audio);
	audio->drv_ops.in_flush(audio);
	audio_aio_disable(audio);
	audio_aio_unmap_ion_region(audio);
	audio_aio_reset_ion_region(audio);
	audio->event_abort = 1;
	wake_up(&audio->event_wait);
	audio_aio_reset_event_queue(audio);
	q6asm_audio_client_free(audio->ac);
	mutex_unlock(&audio->write_lock);
	mutex_unlock(&audio->read_lock);
	mutex_unlock(&audio->lock);
	mutex_destroy(&audio->lock);
	mutex_destroy(&audio->read_lock);
	mutex_destroy(&audio->write_lock);
	mutex_destroy(&audio->get_event_lock);
	unregister_volume_listener(audio);

#ifdef CONFIG_DEBUG_FS
	debugfs_remove(audio->dentry);
#endif
	spin_lock_irqsave(&enc_dec_lock, flags);
	kfree(audio->codec_cfg);
	kfree(audio);
	file->private_data = NULL;
	spin_unlock_irqrestore(&enc_dec_lock, flags);
	mutex_unlock(&lock);
	return 0;
}

int audio_aio_fsync(struct file *file, loff_t start, loff_t end, int datasync)
{
	int rc = 0;
	struct q6audio_aio *audio = file->private_data;

	if (!audio->enabled || audio->feedback)
		return -EINVAL;

	/* Blocking client sends more data */
	mutex_lock(&audio->lock);
	audio->drv_status |= ADRV_STATUS_FSYNC;
	mutex_unlock(&audio->lock);

	pr_debug("%s[%pK]:\n", __func__, audio);

	audio->eos_rsp = 0;

	pr_debug("%s[%pK]Wait for write done from DSP\n", __func__, audio);
	rc = wait_event_interruptible(audio->write_wait,
					(list_empty(&audio->out_queue)) ||
					audio->wflush || audio->stopped);

	if (audio->stopped || audio->wflush) {
		pr_debug("%s[%pK]: Audio Flushed or Stopped,this is not EOS\n"
			, __func__, audio);
		audio->wflush = 0;
		rc = -EBUSY;
	}

	if (rc < 0) {
		pr_err("%s[%pK]: wait event for list_empty failed, rc = %d\n",
			__func__, audio, rc);
		goto done;
	}

	rc = q6asm_cmd(audio->ac, CMD_EOS);
	pr_debug("%s[%pK]: EOS cmd sent to DSP\n", __func__, audio);

	if (rc < 0)
		pr_err_ratelimited("%s[%pK]: q6asm_cmd failed, rc = %d",
			__func__, audio, rc);

	pr_debug("%s[%pK]: wait for RENDERED_EOS from DSP\n"
		, __func__, audio);
	rc = wait_event_interruptible(audio->write_wait,
					(audio->eos_rsp || audio->wflush ||
					audio->stopped));

	if (rc < 0) {
		pr_err("%s[%pK]: wait event for eos_rsp failed, rc = %d\n",
			__func__, audio, rc);
		goto done;
	}

	if (audio->stopped || audio->wflush) {
		audio->wflush = 0;
		pr_debug("%s[%pK]: Audio Flushed or Stopped,this is not EOS\n"
			, __func__, audio);
		rc = -EBUSY;
	}

	if (audio->eos_rsp == 1)
		pr_debug("%s[%pK]: EOS\n", __func__, audio);


done:
	mutex_lock(&audio->lock);
	audio->drv_status &= ~ADRV_STATUS_FSYNC;
	mutex_unlock(&audio->lock);

	return rc;
}

static int audio_aio_events_pending(struct q6audio_aio *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 || audio->reset_event;
}

static long audio_aio_process_event_req_common(struct q6audio_aio *audio,
					struct msm_audio_event *usr_evt)
{
	long rc;
	struct audio_aio_event *drv_evt = NULL;
	int timeout;
	unsigned long flags;

	timeout = usr_evt->timeout_ms;

	if (timeout > 0) {
		rc = wait_event_interruptible_timeout(audio->event_wait,
						audio_aio_events_pending
						(audio),
						msecs_to_jiffies
						(timeout));
		if (rc == 0)
			return -ETIMEDOUT;
	} else {
		rc = wait_event_interruptible(audio->event_wait,
		audio_aio_events_pending(audio));
	}
	if (rc < 0)
		return rc;

	if (audio->reset_event) {
		audio->reset_event = false;
		pr_err("In SSR, post ENETRESET err\n");
		return -ENETRESET;
	}

	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 audio_aio_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 {
		pr_err("%s[%pK]:Unexpected path\n", __func__, audio);
		spin_unlock_irqrestore(&audio->event_queue_lock, flags);
		return -EPERM;
	}
	spin_unlock_irqrestore(&audio->event_queue_lock, flags);

	if (drv_evt->event_type == AUDIO_EVENT_WRITE_DONE) {
		pr_debug("%s[%pK]:posted AUDIO_EVENT_WRITE_DONE to user\n",
			__func__, audio);
		mutex_lock(&audio->write_lock);
		audio_aio_ion_fixup(audio, drv_evt->payload.aio_buf.buf_addr,
		drv_evt->payload.aio_buf.buf_len, 0, 0);
		mutex_unlock(&audio->write_lock);
	} else if (drv_evt->event_type == AUDIO_EVENT_READ_DONE) {
		pr_debug("%s[%pK]:posted AUDIO_EVENT_READ_DONE to user\n",
			__func__, audio);
		mutex_lock(&audio->read_lock);
		audio_aio_ion_fixup(audio, drv_evt->payload.aio_buf.buf_addr,
		drv_evt->payload.aio_buf.buf_len, 0, 0);
		mutex_unlock(&audio->read_lock);
	}

	/* Some read buffer might be held up in DSP,release all
	 * Once EOS indicated
	 */
	if (audio->eos_rsp && !list_empty(&audio->in_queue)) {
		pr_debug("%s[%pK]:Send flush command to release read buffers held up in DSP\n",
			__func__, audio);
		mutex_lock(&audio->lock);
		audio_aio_flush(audio);
		mutex_unlock(&audio->lock);
	}

	return rc;
}

static long audio_aio_process_event_req(struct q6audio_aio *audio,
					void __user *arg)
{
	long rc;
	struct msm_audio_event usr_evt;

	if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event))) {
		pr_err("%s: copy_from_user failed\n", __func__);
		return -EFAULT;
	}

	rc = audio_aio_process_event_req_common(audio, &usr_evt);

	if (copy_to_user(arg, &usr_evt, sizeof(usr_evt))) {
		pr_err("%s: copy_to_user failed\n", __func__);
		rc = -EFAULT;
	}
	return rc;
}

#ifdef CONFIG_COMPAT

struct msm_audio_aio_buf32 {
	compat_uptr_t buf_addr;
	u32 buf_len;
	u32 data_len;
	compat_uptr_t private_data;
	u16 mfield_sz; /*only useful for data has meta field */
};

struct msm_audio_bitstream_info32 {
	u32 codec_type;
	u32 chan_info;
	u32 sample_rate;
	u32 bit_stream_info;
	u32 bit_rate;
	u32 unused[3];
};

struct msm_audio_bitstream_error_info32 {
	u32 dec_id;
	u32 err_msg_indicator;
	u32 err_type;
};

union msm_audio_event_payload32 {
	struct msm_audio_aio_buf32 aio_buf;
	struct msm_audio_bitstream_info32 stream_info;
	struct msm_audio_bitstream_error_info32 error_info;
	s32 reserved;
};

struct msm_audio_event32 {
	s32 event_type;
	s32 timeout_ms;
	union msm_audio_event_payload32 event_payload;
};

static long audio_aio_process_event_req_compat(struct q6audio_aio *audio,
					void __user *arg)
{
	long rc;
	struct msm_audio_event32 usr_evt_32;
	struct msm_audio_event usr_evt;
	memset(&usr_evt, 0, sizeof(struct msm_audio_event));

	if (copy_from_user(&usr_evt_32, arg,
				sizeof(struct msm_audio_event32))) {
		pr_err("%s: copy_from_user failed\n", __func__);
		return -EFAULT;
	}
	usr_evt.timeout_ms = usr_evt_32.timeout_ms;

	rc = audio_aio_process_event_req_common(audio, &usr_evt);
	if (rc < 0) {
		pr_err("%s: audio process event failed, rc = %ld",
			__func__, rc);
		return rc;
	}

	usr_evt_32.event_type = usr_evt.event_type;
	switch (usr_evt_32.event_type) {
	case AUDIO_EVENT_SUSPEND:
	case AUDIO_EVENT_RESUME:
	case AUDIO_EVENT_WRITE_DONE:
	case AUDIO_EVENT_READ_DONE:
		usr_evt_32.event_payload.aio_buf.buf_addr =
			ptr_to_compat(usr_evt.event_payload.aio_buf.buf_addr);
		usr_evt_32.event_payload.aio_buf.buf_len =
			usr_evt.event_payload.aio_buf.buf_len;
		usr_evt_32.event_payload.aio_buf.data_len =
			usr_evt.event_payload.aio_buf.data_len;
		usr_evt_32.event_payload.aio_buf.private_data =
		ptr_to_compat(usr_evt.event_payload.aio_buf.private_data);
		usr_evt_32.event_payload.aio_buf.mfield_sz =
			usr_evt.event_payload.aio_buf.mfield_sz;
		break;
	case AUDIO_EVENT_STREAM_INFO:
		usr_evt_32.event_payload.stream_info.codec_type =
			usr_evt.event_payload.stream_info.codec_type;
		usr_evt_32.event_payload.stream_info.chan_info =
			usr_evt.event_payload.stream_info.chan_info;
		usr_evt_32.event_payload.stream_info.sample_rate =
			usr_evt.event_payload.stream_info.sample_rate;
		usr_evt_32.event_payload.stream_info.bit_stream_info =
			usr_evt.event_payload.stream_info.bit_stream_info;
		usr_evt_32.event_payload.stream_info.bit_rate =
			usr_evt.event_payload.stream_info.bit_rate;
		break;
	case AUDIO_EVENT_BITSTREAM_ERROR_INFO:
		usr_evt_32.event_payload.error_info.dec_id =
			usr_evt.event_payload.error_info.dec_id;
		usr_evt_32.event_payload.error_info.err_msg_indicator =
			usr_evt.event_payload.error_info.err_msg_indicator;
		usr_evt_32.event_payload.error_info.err_type =
			usr_evt.event_payload.error_info.err_type;
		break;
	default:
		pr_debug("%s: unknown audio event type = %d rc = %ld",
			 __func__, usr_evt_32.event_type, rc);
		return rc;
	}
	if (copy_to_user(arg, &usr_evt_32, sizeof(usr_evt_32))) {
		pr_err("%s: copy_to_user failed\n", __func__);
		rc = -EFAULT;
	}
	return rc;
}
#endif

static int audio_aio_ion_check(struct q6audio_aio *audio,
				void *vaddr, unsigned long len)
{
	struct audio_aio_ion_region *region_elt;
	struct audio_aio_ion_region t = {.vaddr = vaddr, .len = len };

	list_for_each_entry(region_elt, &audio->ion_region_queue, list) {
		if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) ||
			OVERLAPS(region_elt, &t)) {
			pr_err("%s[%pK]:region (vaddr %pK len %ld) clashes with registered region (vaddr %pK paddr %pK len %ld)\n",
				__func__, audio, vaddr, len,
				region_elt->vaddr,
				&region_elt->paddr, region_elt->len);
			return -EINVAL;
		}
	}

	return 0;
}

static int audio_aio_ion_add(struct q6audio_aio *audio,
				struct msm_audio_ion_info *info)
{
	dma_addr_t paddr = 0;
	size_t len = 0;
	struct audio_aio_ion_region *region;
	int rc = -EINVAL;
	struct dma_buf *dma_buf = NULL;
	unsigned long ionflag;
	void *kvaddr = NULL;

	pr_debug("%s[%pK]:\n", __func__, audio);
	region = kmalloc(sizeof(*region), GFP_KERNEL);

	if (!region) {
		rc = -ENOMEM;
		goto end;
	}

	rc = msm_audio_ion_import(&dma_buf, info->fd, &ionflag,
				0, &paddr, &len, &kvaddr);
	if (rc) {
		pr_err("%s: msm audio ion alloc failed\n", __func__);
		goto import_error;
	}

	rc = audio_aio_ion_check(audio, info->vaddr, len);
	if (rc < 0) {
		pr_err("%s: audio_aio_ion_check failed\n", __func__);
		goto ion_error;
	}

	region->dma_buf = dma_buf;
	region->vaddr = info->vaddr;
	region->fd = info->fd;
	region->paddr = paddr;
	region->kvaddr = kvaddr;
	region->len = len;
	region->ref_cnt = 0;
	pr_debug("%s[%pK]:add region paddr %pK vaddr %pK, len %lu kvaddr %pK\n",
		__func__, audio,
		&region->paddr, region->vaddr, region->len,
		region->kvaddr);
	list_add_tail(&region->list, &audio->ion_region_queue);
	rc = q6asm_memory_map(audio->ac,  paddr, IN, len, 1);
	if (rc < 0) {
		pr_err("%s[%pK]: memory map failed\n", __func__, audio);
		goto mmap_error;
	} else {
		goto end;
	}
mmap_error:
	list_del(&region->list);
ion_error:
	msm_audio_ion_free(dma_buf);
import_error:
	kfree(region);
end:
	return rc;
}

static int audio_aio_ion_remove(struct q6audio_aio *audio,
				struct msm_audio_ion_info *info)
{
	struct audio_aio_ion_region *region;
	struct list_head *ptr, *next;
	int rc = -EINVAL;

	pr_debug("%s[%pK]:info fd %d vaddr %pK\n",
		__func__, audio, info->fd, info->vaddr);

	list_for_each_safe(ptr, next, &audio->ion_region_queue) {
		region = list_entry(ptr, struct audio_aio_ion_region, list);

		if ((region->fd == info->fd) &&
			(region->vaddr == info->vaddr)) {
			if (region->ref_cnt) {
				pr_debug("%s[%pK]:region %pK in use ref_cnt %d\n",
					__func__, audio, region,
					region->ref_cnt);
				break;
			}
			pr_debug("%s[%pK]:remove region fd %d vaddr %pK\n",
				__func__, audio, info->fd, info->vaddr);
			rc = q6asm_memory_unmap(audio->ac,
						region->paddr, IN);
			if (rc < 0)
				pr_err("%s[%pK]: memory unmap failed\n",
					__func__, audio);

			list_del(&region->list);
			msm_audio_ion_free(region->dma_buf);
			kfree(region);
			rc = 0;
			break;
		}
	}

	return rc;
}

static int audio_aio_async_write(struct q6audio_aio *audio,
				struct audio_aio_buffer_node *buf_node)
{
	int rc;
	struct audio_client *ac;
	struct audio_aio_write_param param;

	memset(&param, 0, sizeof(param));

	if (!audio || !buf_node) {
		pr_err("%s NULL pointer audio=[0x%pK], buf_node=[0x%pK]\n",
			__func__, audio, buf_node);
		return -EINVAL;
	}
	pr_debug("%s[%pK]: Send write buff %pK phy %pK len %d meta_enable = %d\n",
		__func__, audio, buf_node, &buf_node->paddr,
		buf_node->buf.data_len,
		audio->buf_cfg.meta_info_enable);
	pr_debug("%s[%pK]: flags = 0x%x\n", __func__, audio,
		buf_node->meta_info.meta_in.nflags);

	ac = audio->ac;
	/* Offset with  appropriate meta */
	if (audio->feedback) {
		/* Non Tunnel mode */
		param.paddr = buf_node->paddr + sizeof(struct dec_meta_in);
		param.len = buf_node->buf.data_len - sizeof(struct dec_meta_in);
	} else {
		/* Tunnel mode */
		param.paddr = buf_node->paddr;
		param.len = buf_node->buf.data_len;
	}
	param.msw_ts = buf_node->meta_info.meta_in.ntimestamp.highpart;
	param.lsw_ts = buf_node->meta_info.meta_in.ntimestamp.lowpart;
	param.flags  = buf_node->meta_info.meta_in.nflags;
	/* If no meta_info enaled, indicate no time stamp valid */
	if (!audio->buf_cfg.meta_info_enable)
		param.flags = 0xFF00;

	if (buf_node->meta_info.meta_in.nflags & AUDIO_DEC_EOF_SET)
		param.flags |= AUDIO_DEC_EOF_SET;

	param.uid = ac->session;
	/* Read command will populate session id as token */
	buf_node->token = ac->session;
	rc = q6asm_async_write(ac, &param);
	if (rc < 0)
		pr_err_ratelimited("%s[%pK]:failed\n", __func__, audio);
	return rc;
}

void audio_aio_post_event(struct q6audio_aio *audio, int type,
			union msm_audio_event_payload payload)
{
	struct audio_aio_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 audio_aio_event, list);
		list_del(&e_node->list);
	} else {
		e_node = kmalloc(sizeof(struct audio_aio_event), GFP_ATOMIC);
		if (!e_node) {
			spin_unlock_irqrestore(&audio->event_queue_lock, flags);
			return;
		}
	}

	e_node->event_type = type;
	e_node->payload = payload;

	list_add_tail(&e_node->list, &audio->event_queue);
	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
	wake_up(&audio->event_wait);
}

static int audio_aio_async_read(struct q6audio_aio *audio,
				struct audio_aio_buffer_node *buf_node)
{
	struct audio_client *ac;
	struct audio_aio_read_param param;
	int rc;

	pr_debug("%s[%pK]: Send read buff %pK phy %pK len %d\n",
		__func__, audio, buf_node,
		&buf_node->paddr, buf_node->buf.buf_len);
	ac = audio->ac;
	/* Provide address so driver can append nr frames information */
	param.paddr = buf_node->paddr +
		sizeof(struct dec_meta_out);
	param.len = buf_node->buf.buf_len -
		sizeof(struct dec_meta_out);
	param.uid = ac->session;
	/* Write command will populate session_id as token */
	buf_node->token = ac->session;
	rc = q6asm_async_read(ac, &param);
	if (rc < 0 && rc != -ENETRESET)
		pr_err_ratelimited("%s[%pK]:failed\n", __func__, audio);
	return rc;
}

static int audio_aio_buf_add_shared(struct q6audio_aio *audio, u32 dir,
				struct audio_aio_buffer_node *buf_node)
{
	unsigned long flags;
	int ret = 0;

	pr_debug("%s[%pK]:node %pK dir %x buf_addr %pK buf_len %d data_len %d\n",
		 __func__, audio, buf_node, dir, buf_node->buf.buf_addr,
		buf_node->buf.buf_len, buf_node->buf.data_len);
	buf_node->paddr = audio_aio_ion_fixup(audio, buf_node->buf.buf_addr,
						buf_node->buf.buf_len, 1,
						&buf_node->kvaddr);
	if (dir) {
		/* write */
		if (!buf_node->paddr ||
			(buf_node->paddr & 0x1) ||
			(!audio->feedback && !buf_node->buf.data_len)) {
			kfree(buf_node);
			return -EINVAL;
		}
		ret = extract_meta_out_info(audio, buf_node, 1);
		if (ret) {
			pr_debug("%s: extract meta failed with %d\n",
				  __func__, ret);
			kfree(buf_node);
			return ret;
		}
		/* Not a EOS buffer */
		if (!(buf_node->meta_info.meta_in.nflags & AUDIO_DEC_EOS_SET)) {
			spin_lock_irqsave(&audio->dsp_lock, flags);
			ret = audio_aio_async_write(audio, buf_node);
			/* EOS buffer handled in driver */
			list_add_tail(&buf_node->list, &audio->out_queue);
			spin_unlock_irqrestore(&audio->dsp_lock, flags);
		} else if (buf_node->meta_info.meta_in.nflags
				   & AUDIO_DEC_EOS_SET) {
			if (!audio->wflush) {
				pr_debug("%s[%pK]:Send EOS cmd at i/p\n",
					__func__, audio);
				/* Driver will forcefully post writedone event
				 * once eos ack recived from DSP
				 */
				audio->eos_write_payload.aio_buf =
					buf_node->buf;
				audio->eos_flag = 1;
				audio->eos_rsp = 0;
				q6asm_cmd(audio->ac, CMD_EOS);
				kfree(buf_node);
			} else { /* Flush in progress, send back i/p
				  * EOS buffer as is
				  */
				union msm_audio_event_payload event_payload;

				event_payload.aio_buf = buf_node->buf;
				audio_aio_post_event(audio,
						AUDIO_EVENT_WRITE_DONE,
						event_payload);
				kfree(buf_node);
			}
		}
	} else {
		/* read */
		if (!buf_node->paddr ||
			(buf_node->paddr & 0x1) ||
			(buf_node->buf.buf_len < PCM_BUFSZ_MIN)) {
			kfree(buf_node);
			return -EINVAL;
		}
		/* No EOS reached */
		if (!audio->eos_rsp) {
			spin_lock_irqsave(&audio->dsp_lock, flags);
			ret = audio_aio_async_read(audio, buf_node);
			/* EOS buffer handled in driver */
			list_add_tail(&buf_node->list, &audio->in_queue);
			spin_unlock_irqrestore(&audio->dsp_lock, flags);
		}
		/* EOS reached at input side fake all upcoming read buffer to
		 * indicate the same
		 */
		else {
			union msm_audio_event_payload event_payload;

			event_payload.aio_buf = buf_node->buf;
			event_payload.aio_buf.data_len =
				insert_eos_buf(audio, buf_node);
			pr_debug("%s[%pK]: propagate READ_DONE as EOS done\n",
				__func__, audio);
			audio_aio_post_event(audio, AUDIO_EVENT_READ_DONE,
					event_payload);
			kfree(buf_node);
		}
	}
	return ret;
}
#ifdef CONFIG_COMPAT
static int audio_aio_buf_add_compat(struct q6audio_aio *audio, u32 dir,
				void __user *arg)
{
	struct audio_aio_buffer_node *buf_node;
	struct msm_audio_aio_buf32 aio_buf_32;

	buf_node = kzalloc(sizeof(*buf_node), GFP_KERNEL);

	if (!buf_node)
		return -ENOMEM;

	if (copy_from_user(&aio_buf_32, arg, sizeof(aio_buf_32))) {
		kfree(buf_node);
		pr_err("%s: copy_from_user failed\n", __func__);
		return -EFAULT;
	}

	buf_node->buf.buf_addr = compat_ptr(aio_buf_32.buf_addr);
	buf_node->buf.buf_len = aio_buf_32.buf_len;
	buf_node->buf.data_len = aio_buf_32.data_len;
	buf_node->buf.private_data = compat_ptr(aio_buf_32.private_data);
	buf_node->buf.mfield_sz = aio_buf_32.mfield_sz;

	return audio_aio_buf_add_shared(audio, dir, buf_node);
}
#endif

static int audio_aio_buf_add(struct q6audio_aio *audio, u32 dir,
				void __user *arg)
{
	struct audio_aio_buffer_node *buf_node;

	buf_node = kzalloc(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);
		pr_err("%s: copy_from_user failed\n", __func__);
		return -EFAULT;
	}

	return audio_aio_buf_add_shared(audio, dir, buf_node);
}

void audio_aio_ioport_reset(struct q6audio_aio *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) {
			pr_debug("%s[%pK]:fsync in progress\n",
				 __func__, audio);
			audio->drv_ops.out_flush(audio);
		} else
			audio->drv_ops.out_flush(audio);
		if (audio->feedback == NON_TUNNEL_MODE)
			audio->drv_ops.in_flush(audio);
	}
}

int audio_aio_open(struct q6audio_aio *audio, struct file *file)
{
	int rc = 0;
	int i;
	struct audio_aio_event *e_node = NULL;
	struct list_head *ptr, *next;

	/* Settings will be re-config at AUDIO_SET_CONFIG,
	 * but at least we need to have initial config
	 */
	audio->str_cfg.buffer_size = FRAME_SIZE;
	audio->str_cfg.buffer_count = FRAME_NUM;
	audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
	audio->pcm_cfg.sample_rate = 48000;
	audio->pcm_cfg.channel_count = 2;

	/* Only AIO interface */
	if (file->f_flags & O_NONBLOCK) {
		pr_debug("%s[%pK]:set to aio interface\n", __func__, audio);
		audio->drv_status |= ADRV_STATUS_AIO_INTF;
		audio->drv_ops.out_flush = audio_aio_async_out_flush;
		audio->drv_ops.in_flush = audio_aio_async_in_flush;
		q6asm_set_io_mode(audio->ac, ASYNC_IO_MODE);
	} else {
		pr_err_ratelimited("%s[%pK]:SIO interface not supported\n",
			__func__, audio);
		rc = -EACCES;
		goto fail;
	}

	/* Initialize all locks of audio instance */
	mutex_init(&audio->lock);
	mutex_init(&audio->read_lock);
	mutex_init(&audio->write_lock);
	mutex_init(&audio->get_event_lock);
	spin_lock_init(&audio->dsp_lock);
	spin_lock_init(&audio->event_queue_lock);
	init_waitqueue_head(&audio->cmd_wait);
	init_waitqueue_head(&audio->write_wait);
	init_waitqueue_head(&audio->event_wait);
	INIT_LIST_HEAD(&audio->out_queue);
	INIT_LIST_HEAD(&audio->in_queue);
	INIT_LIST_HEAD(&audio->ion_region_queue);
	INIT_LIST_HEAD(&audio->free_event_queue);
	INIT_LIST_HEAD(&audio->event_queue);

	audio->drv_ops.out_flush(audio);
	audio->opened = 1;
	audio->reset_event = false;
	file->private_data = audio;
	audio->codec_ioctl = audio_aio_ioctl;
	audio->codec_compat_ioctl = audio_aio_compat_ioctl;
	for (i = 0; i < AUDIO_EVENT_NUM; i++) {
		e_node = kmalloc(sizeof(struct audio_aio_event), GFP_KERNEL);
		if (e_node)
			list_add_tail(&e_node->list, &audio->free_event_queue);
		else {
			rc = -ENOMEM;
			goto cleanup;
		}
	}

	rc = register_volume_listener(audio);
	if (rc < 0)
		goto cleanup;

	return 0;
cleanup:
	list_for_each_safe(ptr, next, &audio->free_event_queue) {
		e_node = list_first_entry(&audio->free_event_queue,
				   struct audio_aio_event, list);
		list_del(&e_node->list);
		kfree(e_node);
	}
fail:
	return rc;
}

static long audio_aio_shared_ioctl(struct file *file, unsigned int cmd,
			unsigned long arg)
{
	struct q6audio_aio *audio = file->private_data;
	int rc = 0;

	switch (cmd) {
	case AUDIO_ABORT_GET_EVENT: {
		audio->event_abort = 1;
		wake_up(&audio->event_wait);
		break;
	}
	case AUDIO_OUTPORT_FLUSH: {
		pr_debug("%s[%pK]:AUDIO_OUTPORT_FLUSH\n", __func__, audio);
		mutex_lock(&audio->read_lock);
		rc = audio_aio_outport_flush(audio);
		if (rc < 0) {
			pr_err_ratelimited("%s[%pK]: AUDIO_OUTPORT_FLUSH failed\n",
				__func__, audio);
			rc = -EINTR;
		}
		mutex_unlock(&audio->read_lock);
		break;
	}
	case AUDIO_STOP: {
		pr_debug("%s[%pK]: AUDIO_STOP session_id[%d]\n", __func__,
				audio, audio->ac->session);
		mutex_lock(&audio->lock);
		audio->stopped = 1;
		rc = audio_aio_flush(audio);
		if (rc < 0) {
			pr_err_ratelimited("%s[%pK]:Audio Stop procedure failed rc=%d\n",
				__func__, audio, rc);
			mutex_unlock(&audio->lock);
			break;
		}
		audio->enabled = 0;
		audio->drv_status &= ~ADRV_STATUS_PAUSE;
		if (audio->drv_status & ADRV_STATUS_FSYNC) {
			pr_debug("%s[%pK] Waking up the audio_aio_fsync\n",
					__func__, audio);
			wake_up(&audio->write_wait);
		}
		mutex_unlock(&audio->lock);
		break;
	}
	case AUDIO_PAUSE: {
		pr_debug("%s[%pK]:AUDIO_PAUSE %ld\n", __func__, audio, arg);
		mutex_lock(&audio->lock);
		if (arg == 1) {
			rc = audio_aio_pause(audio);
			if (rc < 0) {
				pr_err_ratelimited("%s[%pK]: pause FAILED rc=%d\n",
					__func__, audio, rc);
				mutex_unlock(&audio->lock);
				break;
			}
			audio->drv_status |= ADRV_STATUS_PAUSE;
		} else if (arg == 0) {
			if (audio->drv_status & ADRV_STATUS_PAUSE) {
				rc = audio_aio_enable(audio);
				if (rc)
					pr_err_ratelimited("%s[%pK]: audio enable failed\n",
					__func__, audio);
				else {
					audio->drv_status &= ~ADRV_STATUS_PAUSE;
					audio->enabled = 1;
				}
			}
		}
		mutex_unlock(&audio->lock);
		break;
	}
	case AUDIO_FLUSH: {
		pr_debug("%s[%pK]: AUDIO_FLUSH sessionid[%d]\n", __func__,
			audio, audio->ac->session);
		mutex_lock(&audio->lock);
		audio->rflush = 1;
		audio->wflush = 1;
		if (audio->drv_status & ADRV_STATUS_FSYNC) {
			pr_debug("%s[%pK] Waking up the audio_aio_fsync\n",
				__func__, audio);
			wake_up(&audio->write_wait);
		}
		/* Flush DSP */
		rc = audio_aio_flush(audio);
		/* Flush input / Output buffer in software*/
		audio_aio_ioport_reset(audio);
		if (rc < 0) {
			pr_err_ratelimited("%s[%pK]:AUDIO_FLUSH interrupted\n",
				__func__, audio);
			rc = -EINTR;
		} else {
			audio->rflush = 0;
			if (audio->drv_status & ADRV_STATUS_FSYNC)
				wake_up(&audio->write_wait);
			else
				audio->wflush = 0;

		}
		audio->eos_flag = 0;
		audio->eos_rsp = 0;
		mutex_unlock(&audio->lock);
		break;
	}
	case AUDIO_GET_SESSION_ID: {
		mutex_lock(&audio->lock);
		if (copy_to_user((void *)arg, &audio->ac->session,
			sizeof(u16))) {
			pr_err_ratelimited("%s: copy_to_user for AUDIO_GET_SESSION_ID failed\n",
				__func__);
			rc = -EFAULT;
		}
		mutex_unlock(&audio->lock);
		break;
	}
	case AUDIO_PM_AWAKE: {
		if ((audio->audio_ws_mgr ==  NULL) ||
				(audio->miscdevice == NULL)) {
			pr_err_ratelimited("%s[%pK]: invalid ws_mgr or miscdevice",
					__func__, audio);
			rc = -EACCES;
			break;
		}
		pr_debug("%s[%pK]:AUDIO_PM_AWAKE\n", __func__, audio);
		mutex_lock(&audio->lock);
		if (!audio->wakelock_voted) {
			audio->wakelock_voted = true;
			mutex_lock(&audio->audio_ws_mgr->ws_lock);
			if (audio->audio_ws_mgr->ref_cnt++ == 0)
				pm_stay_awake(audio->miscdevice->this_device);
			mutex_unlock(&audio->audio_ws_mgr->ws_lock);
		}
		mutex_unlock(&audio->lock);
		break;
	}
	case AUDIO_PM_RELAX: {
		if ((audio->audio_ws_mgr ==  NULL) ||
				(audio->miscdevice == NULL)) {
			pr_err_ratelimited("%s[%pK]: invalid ws_mgr or miscdevice",
					__func__, audio);
			rc = -EACCES;
			break;
		}
		pr_debug("%s[%pK]:AUDIO_PM_RELAX\n", __func__, audio);
		mutex_lock(&audio->lock);
		if (audio->wakelock_voted) {
			audio->wakelock_voted = false;
			mutex_lock(&audio->audio_ws_mgr->ws_lock);
			if ((audio->audio_ws_mgr->ref_cnt > 0) &&
					(--audio->audio_ws_mgr->ref_cnt == 0)) {
				pm_relax(audio->miscdevice->this_device);
			}
			mutex_unlock(&audio->audio_ws_mgr->ws_lock);
		}
		mutex_unlock(&audio->lock);
		break;
	}
	default:
		pr_err_ratelimited("%s: Unknown ioctl cmd = %d", __func__, cmd);
		rc =  -EINVAL;
	}
	return rc;


}

static long audio_aio_ioctl(struct file *file, unsigned int cmd,
				unsigned long arg)
{
	struct q6audio_aio *audio = file->private_data;
	int rc = 0;

	switch (cmd) {
	case AUDIO_ABORT_GET_EVENT:
	case AUDIO_OUTPORT_FLUSH:
	case AUDIO_STOP:
	case AUDIO_PAUSE:
	case AUDIO_FLUSH:
	case AUDIO_GET_SESSION_ID:
	case AUDIO_PM_AWAKE:
	case AUDIO_PM_RELAX:
		rc = audio_aio_shared_ioctl(file, cmd, arg);
		break;
	case AUDIO_GET_STATS: {
		struct msm_audio_stats stats;
		uint64_t timestamp;

		memset(&stats, 0, sizeof(struct msm_audio_stats));
		stats.byte_count = atomic_read(&audio->in_bytes);
		stats.sample_count = atomic_read(&audio->in_samples);
		rc = q6asm_get_session_time(audio->ac, &timestamp);
		if (rc >= 0)
			memcpy(&stats.unused[0], &timestamp, sizeof(timestamp));
		else
			pr_debug("Error while getting timestamp\n");
		if (copy_to_user((void *)arg, &stats, sizeof(stats))) {
			pr_err("%s: copy_frm_user for AUDIO_GET_STATS failed\n",
				__func__);
			rc = -EFAULT;
		}
		break;
	}
	case AUDIO_GET_EVENT: {
		pr_debug("%s[%pK]:AUDIO_GET_EVENT\n", __func__, audio);
		if (mutex_trylock(&audio->get_event_lock)) {
			rc = audio_aio_process_event_req(audio,
						(void __user *)arg);
			mutex_unlock(&audio->get_event_lock);
		} else
			rc = -EBUSY;
		break;
	}
	case AUDIO_ASYNC_WRITE: {
		mutex_lock(&audio->write_lock);
		if (audio->drv_status & ADRV_STATUS_FSYNC)
			rc = -EBUSY;
		else {
			if (audio->enabled)
				rc = audio_aio_buf_add(audio, 1,
						(void __user *)arg);
			else
				rc = -EPERM;
		}
		mutex_unlock(&audio->write_lock);
		break;
	}
	case AUDIO_ASYNC_READ: {
		mutex_lock(&audio->read_lock);
		if (audio->feedback)
			rc = audio_aio_buf_add(audio, 0,
					(void __user *)arg);
		else
			rc = -EPERM;
		mutex_unlock(&audio->read_lock);
		break;
	}

	case AUDIO_GET_STREAM_CONFIG: {
		struct msm_audio_stream_config cfg;

		mutex_lock(&audio->lock);
		memset(&cfg, 0, sizeof(cfg));
		cfg.buffer_size = audio->str_cfg.buffer_size;
		cfg.buffer_count = audio->str_cfg.buffer_count;
		pr_debug("%s[%pK]:GET STREAM CFG %d %d\n",
			__func__, audio, cfg.buffer_size, cfg.buffer_count);
		if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) {
			pr_err(
				"%s: copy_to_user for AUDIO_GET_STREAM_CONFIG failed\n",
				__func__);
			rc = -EFAULT;
		}
		mutex_unlock(&audio->lock);
		break;
	}
	case AUDIO_SET_STREAM_CONFIG: {
		struct msm_audio_stream_config cfg;

		pr_debug("%s[%pK]:SET STREAM CONFIG\n", __func__, audio);
		mutex_lock(&audio->lock);
		if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
			pr_err(
				"%s: copy_from_user for AUDIO_SET_STREAM_CONFIG failed\n",
				__func__);
			rc = -EFAULT;
			mutex_unlock(&audio->lock);
			break;
		}
		audio->str_cfg.buffer_size = FRAME_SIZE;
		audio->str_cfg.buffer_count = FRAME_NUM;
		rc = 0;
		mutex_unlock(&audio->lock);
		break;
	}
	case AUDIO_GET_CONFIG: {
		struct msm_audio_config cfg;

		mutex_lock(&audio->lock);
		if (copy_to_user((void *)arg, &audio->pcm_cfg, sizeof(cfg))) {
			pr_err(
				"%s: copy_to_user for AUDIO_GET_CONFIG failed\n",
				__func__);
			rc = -EFAULT;
		}
		mutex_unlock(&audio->lock);
		break;
	}
	case AUDIO_SET_CONFIG: {
		struct msm_audio_config config;

		pr_debug("%s[%pK]:AUDIO_SET_CONFIG\n", __func__, audio);
		mutex_lock(&audio->lock);
		if (copy_from_user(&config, (void *)arg, sizeof(config))) {
			pr_err(
				"%s: copy_from_user for AUDIO_SET_CONFIG failed\n",
				__func__);
			rc = -EFAULT;
			mutex_unlock(&audio->lock);
			break;
		}
		if (audio->feedback != NON_TUNNEL_MODE) {
			pr_err("%s[%pK]:Not sufficient permission to change the playback mode\n",
				 __func__, audio);
			rc = -EACCES;
			mutex_unlock(&audio->lock);
			break;
		}
		if ((config.buffer_count > PCM_BUF_COUNT) ||
			(config.buffer_count == 1))
			config.buffer_count = PCM_BUF_COUNT;

		if (config.buffer_size < PCM_BUFSZ_MIN)
			config.buffer_size = PCM_BUFSZ_MIN;

		audio->pcm_cfg.buffer_count = config.buffer_count;
		audio->pcm_cfg.buffer_size = config.buffer_size;
		audio->pcm_cfg.channel_count = config.channel_count;
		audio->pcm_cfg.sample_rate = config.sample_rate;
		rc = 0;
		mutex_unlock(&audio->lock);
		break;
	}
	case AUDIO_SET_BUF_CFG: {
		struct msm_audio_buf_cfg  cfg;

		mutex_lock(&audio->lock);
		if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
			pr_err(
				"%s: copy_from_user for AUDIO_GET_BUF CONFIG failed\n",
				__func__);
			rc = -EFAULT;
			mutex_unlock(&audio->lock);
			break;
		}
		if ((audio->feedback == NON_TUNNEL_MODE) &&
			!cfg.meta_info_enable) {
			rc = -EFAULT;
			mutex_unlock(&audio->lock);
			break;
		}

		audio->buf_cfg.meta_info_enable = cfg.meta_info_enable;
		pr_debug("%s[%pK]:session id %d: Set-buf-cfg: meta[%d]",
				__func__, audio,
				audio->ac->session, cfg.meta_info_enable);
		mutex_unlock(&audio->lock);
		break;
	}
	case AUDIO_GET_BUF_CFG: {
		pr_debug("%s[%pK]:session id %d: Get-buf-cfg: meta[%d] framesperbuf[%d]\n",
			 __func__, audio,
			audio->ac->session, audio->buf_cfg.meta_info_enable,
			audio->buf_cfg.frames_per_buf);

		mutex_lock(&audio->lock);
		if (copy_to_user((void *)arg, &audio->buf_cfg,
			sizeof(struct msm_audio_buf_cfg))) {
			pr_err(
				"%s: copy_to_user for AUDIO_GET_BUF_CONFIG failed\n",
				__func__);
			rc = -EFAULT;
		}
		mutex_unlock(&audio->lock);
		break;
	}
	case AUDIO_REGISTER_ION: {
		struct msm_audio_ion_info info;

		pr_debug("%s[%pK]:AUDIO_REGISTER_ION\n", __func__, audio);
		mutex_lock(&audio->lock);
		if (copy_from_user(&info, (void *)arg, sizeof(info))) {
			pr_err(
				"%s: copy_from_user for AUDIO_REGISTER_ION failed\n",
				__func__);
			rc = -EFAULT;
		} else {
			mutex_lock(&audio->read_lock);
			mutex_lock(&audio->write_lock);
			rc = audio_aio_ion_add(audio, &info);
			mutex_unlock(&audio->write_lock);
			mutex_unlock(&audio->read_lock);
		}
		mutex_unlock(&audio->lock);
		break;
	}
	case AUDIO_DEREGISTER_ION: {
		struct msm_audio_ion_info info;

		mutex_lock(&audio->lock);
		pr_debug("%s[%pK]:AUDIO_DEREGISTER_ION\n", __func__, audio);
		if (copy_from_user(&info, (void *)arg, sizeof(info))) {
			pr_err(
				"%s: copy_from_user for AUDIO_DEREGISTER_ION failed\n",
				__func__);
			rc = -EFAULT;
		} else {
			mutex_lock(&audio->read_lock);
			mutex_lock(&audio->write_lock);
			rc = audio_aio_ion_remove(audio, &info);
			mutex_unlock(&audio->write_lock);
			mutex_unlock(&audio->read_lock);
		}
		mutex_unlock(&audio->lock);
		break;
	}
	default:
		pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
		rc =  -EINVAL;
	}
	return rc;
}

#ifdef CONFIG_COMPAT
struct msm_audio_stream_config32 {
	u32 buffer_size;
	u32 buffer_count;
};

struct msm_audio_stats32 {
	u32 byte_count;
	u32 sample_count;
	u32 unused[2];
};

struct msm_audio_config32 {
	u32 buffer_size;
	u32 buffer_count;
	u32 channel_count;
	u32 sample_rate;
	u32 type;
	u32 meta_field;
	u32 bits;
	u32 unused[3];
};

struct msm_audio_buf_cfg32 {
	u32 meta_info_enable;
	u32 frames_per_buf;
};

struct msm_audio_ion_info32 {
	int fd;
	compat_uptr_t vaddr;
};

enum {
	AUDIO_GET_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, 3,
			struct msm_audio_config32),
	AUDIO_SET_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, 4,
			struct msm_audio_config32),
	AUDIO_GET_STATS_32 = _IOR(AUDIO_IOCTL_MAGIC, 5,
			struct msm_audio_stats32),
	AUDIO_GET_EVENT_32 = _IOR(AUDIO_IOCTL_MAGIC, 13,
			struct msm_audio_event32),
	AUDIO_ASYNC_WRITE_32 = _IOW(AUDIO_IOCTL_MAGIC, 17,
			struct msm_audio_aio_buf32),
	AUDIO_ASYNC_READ_32 = _IOW(AUDIO_IOCTL_MAGIC, 18,
			struct msm_audio_aio_buf32),
	AUDIO_SET_STREAM_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, 80,
			struct msm_audio_stream_config32),
	AUDIO_GET_STREAM_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, 81,
			struct msm_audio_stream_config32),
	AUDIO_GET_BUF_CFG_32 = _IOW(AUDIO_IOCTL_MAGIC, 93,
			struct msm_audio_buf_cfg32),
	AUDIO_SET_BUF_CFG_32 = _IOW(AUDIO_IOCTL_MAGIC, 94,
			struct msm_audio_buf_cfg32),
	AUDIO_REGISTER_ION_32 = _IOW(AUDIO_IOCTL_MAGIC, 97,
			struct msm_audio_ion_info32),
	AUDIO_DEREGISTER_ION_32 = _IOW(AUDIO_IOCTL_MAGIC, 98,
			struct msm_audio_ion_info32),
};

static long audio_aio_compat_ioctl(struct file *file, unsigned int cmd,
			unsigned long arg)
{
	struct q6audio_aio *audio = file->private_data;
	int rc = 0;

	switch (cmd) {
	case AUDIO_ABORT_GET_EVENT:
	case AUDIO_OUTPORT_FLUSH:
	case AUDIO_STOP:
	case AUDIO_PAUSE:
	case AUDIO_FLUSH:
	case AUDIO_GET_SESSION_ID:
	case AUDIO_PM_AWAKE:
	case AUDIO_PM_RELAX:
		rc = audio_aio_shared_ioctl(file, cmd, arg);
		break;
	case AUDIO_GET_STATS_32: {
		struct msm_audio_stats32 stats;
		uint64_t timestamp;

		memset(&stats, 0, sizeof(struct msm_audio_stats32));
		stats.byte_count = atomic_read(&audio->in_bytes);
		stats.sample_count = atomic_read(&audio->in_samples);
		rc = q6asm_get_session_time(audio->ac, &timestamp);
		if (rc >= 0)
			memcpy(&stats.unused[0], &timestamp, sizeof(timestamp));
		else
			pr_debug("Error while getting timestamp\n");
		if (copy_to_user((void *)arg, &stats, sizeof(stats))) {
			pr_err(
				"%s: copy_to_user for AUDIO_GET_STATS_32 failed\n",
				__func__);
			rc = -EFAULT;
		}
		break;
	}
	case AUDIO_GET_EVENT_32: {
		pr_debug("%s[%pK]:AUDIO_GET_EVENT\n", __func__, audio);
		if (mutex_trylock(&audio->get_event_lock)) {
			rc = audio_aio_process_event_req_compat(audio,
						(void __user *)arg);
			mutex_unlock(&audio->get_event_lock);
		} else
			rc = -EBUSY;
		break;
	}
	case AUDIO_ASYNC_WRITE_32: {
		mutex_lock(&audio->write_lock);
		if (audio->drv_status & ADRV_STATUS_FSYNC)
			rc = -EBUSY;
		else {
			if (audio->enabled)
				rc = audio_aio_buf_add_compat(audio, 1,
						(void __user *)arg);
			else
				rc = -EPERM;
		}
		mutex_unlock(&audio->write_lock);
		break;
	}
	case AUDIO_ASYNC_READ_32: {
		mutex_lock(&audio->read_lock);
		if (audio->feedback)
			rc = audio_aio_buf_add_compat(audio, 0,
					(void __user *)arg);
		else
			rc = -EPERM;
		mutex_unlock(&audio->read_lock);
		break;
	}

	case AUDIO_GET_STREAM_CONFIG_32: {
		struct msm_audio_stream_config32 cfg;

		mutex_lock(&audio->lock);
		memset(&cfg, 0, sizeof(cfg));
		cfg.buffer_size = audio->str_cfg.buffer_size;
		cfg.buffer_count = audio->str_cfg.buffer_count;
		pr_debug("%s[%pK]:GET STREAM CFG %d %d\n",
			__func__, audio, cfg.buffer_size, cfg.buffer_count);
		if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) {
			pr_err("%s: copy_to_user for AUDIO_GET_STREAM_CONFIG_32 failed\n",
				__func__);
			rc = -EFAULT;
		}
		mutex_unlock(&audio->lock);
		break;
	}
	case AUDIO_SET_STREAM_CONFIG_32: {
		struct msm_audio_stream_config32 cfg_32;
		struct msm_audio_stream_config cfg;

		pr_debug("%s[%pK]:SET STREAM CONFIG\n", __func__, audio);
		mutex_lock(&audio->lock);
		if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) {
			pr_err("%s: copy_from_user for AUDIO_SET_STREAM_CONFIG_32 failed\n",
				__func__);
			rc = -EFAULT;
			mutex_unlock(&audio->lock);
			break;
		}
		cfg.buffer_size = cfg_32.buffer_size;
		cfg.buffer_count = cfg_32.buffer_count;

		audio->str_cfg.buffer_size = FRAME_SIZE;
		audio->str_cfg.buffer_count = FRAME_NUM;
		rc = 0;
		mutex_unlock(&audio->lock);
		break;
	}
	case AUDIO_GET_CONFIG_32: {
		struct msm_audio_config32 cfg_32;

		mutex_lock(&audio->lock);
		memset(&cfg_32, 0, sizeof(cfg_32));
		cfg_32.buffer_size = audio->pcm_cfg.buffer_size;
		cfg_32.buffer_count = audio->pcm_cfg.buffer_count;
		cfg_32.channel_count = audio->pcm_cfg.channel_count;
		cfg_32.sample_rate = audio->pcm_cfg.sample_rate;
		cfg_32.type = audio->pcm_cfg.type;
		cfg_32.meta_field = audio->pcm_cfg.meta_field;
		cfg_32.bits = audio->pcm_cfg.bits;

		if (copy_to_user((void *)arg, &cfg_32, sizeof(cfg_32))) {
			pr_err("%s: copy_to_user for AUDIO_GET_CONFIG_32 failed\n",
				__func__);
			rc = -EFAULT;
		}
		mutex_unlock(&audio->lock);
		break;
	}
	case AUDIO_SET_CONFIG_32: {
		struct msm_audio_config config;
		struct msm_audio_config32 config_32;

		mutex_lock(&audio->lock);

		if (audio->feedback != NON_TUNNEL_MODE) {
			pr_err("%s[%pK]:Not sufficient permission to change the playback mode\n",
				 __func__, audio);
			rc = -EACCES;
			mutex_unlock(&audio->lock);
			break;
		}
		pr_debug("%s[%pK]:AUDIO_SET_CONFIG\n", __func__, audio);
		if (copy_from_user(&config_32, (void *)arg,
					sizeof(config_32))) {
			pr_err("%s: copy_from_user for AUDIO_SET_CONFIG_32 failed\n",
				__func__);
			rc = -EFAULT;
			mutex_unlock(&audio->lock);
			break;
		}
		config.buffer_size = config_32.buffer_size;
		config.buffer_count = config_32.buffer_count;
		config.channel_count = config_32.channel_count;
		config.sample_rate = config_32.sample_rate;
		config.type = config_32.type;
		config.meta_field = config_32.meta_field;
		config.bits = config_32.bits;

		if ((config.buffer_count > PCM_BUF_COUNT) ||
			(config.buffer_count == 1))
			config.buffer_count = PCM_BUF_COUNT;

		if (config.buffer_size < PCM_BUFSZ_MIN)
			config.buffer_size = PCM_BUFSZ_MIN;

		audio->pcm_cfg.buffer_count = config.buffer_count;
		audio->pcm_cfg.buffer_size = config.buffer_size;
		audio->pcm_cfg.channel_count = config.channel_count;
		audio->pcm_cfg.sample_rate = config.sample_rate;
		rc = 0;
		mutex_unlock(&audio->lock);
		break;
	}
	case AUDIO_SET_BUF_CFG_32: {
		struct msm_audio_buf_cfg cfg;
		struct msm_audio_buf_cfg32 cfg_32;

		mutex_lock(&audio->lock);
		if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) {
			pr_err("%s: copy_from_user for AUDIO_SET_CONFIG_32 failed\n",
				__func__);
			rc = -EFAULT;
			mutex_unlock(&audio->lock);
			break;
		}
		cfg.meta_info_enable = cfg_32.meta_info_enable;
		cfg.frames_per_buf = cfg_32.frames_per_buf;

		if ((audio->feedback == NON_TUNNEL_MODE) &&
			!cfg.meta_info_enable) {
			rc = -EFAULT;
			mutex_unlock(&audio->lock);
			break;
		}

		audio->buf_cfg.meta_info_enable = cfg.meta_info_enable;
		pr_debug("%s[%pK]:session id %d: Set-buf-cfg: meta[%d]",
				__func__, audio,
				audio->ac->session, cfg.meta_info_enable);
		mutex_unlock(&audio->lock);
		break;
	}
	case AUDIO_GET_BUF_CFG_32: {
		struct msm_audio_buf_cfg32 cfg_32;

		pr_debug("%s[%pK]:session id %d: Get-buf-cfg: meta[%d] framesperbuf[%d]\n",
			 __func__, audio,
			audio->ac->session, audio->buf_cfg.meta_info_enable,
			audio->buf_cfg.frames_per_buf);
		mutex_lock(&audio->lock);
		memset(&cfg_32, 0, sizeof(cfg_32));
		cfg_32.meta_info_enable = audio->buf_cfg.meta_info_enable;
		cfg_32.frames_per_buf = audio->buf_cfg.frames_per_buf;
		if (copy_to_user((void *)arg, &cfg_32,
			sizeof(struct msm_audio_buf_cfg32))) {
			pr_err("%s: copy_to_user for AUDIO_GET_BUF_CFG_32 failed\n",
				__func__);
			rc = -EFAULT;
		}
		mutex_unlock(&audio->lock);
		break;
	}
	case AUDIO_REGISTER_ION_32: {
		struct msm_audio_ion_info32 info_32;
		struct msm_audio_ion_info info;

		pr_debug("%s[%pK]:AUDIO_REGISTER_ION\n", __func__, audio);
		mutex_lock(&audio->lock);
		if (copy_from_user(&info_32, (void *)arg, sizeof(info_32))) {
			pr_err("%s: copy_from_user for AUDIO_REGISTER_ION_32 failed\n",
				__func__);
			rc = -EFAULT;
		} else {
			info.fd = info_32.fd;
			info.vaddr = compat_ptr(info_32.vaddr);
			mutex_lock(&audio->read_lock);
			mutex_lock(&audio->write_lock);
			rc = audio_aio_ion_add(audio, &info);
			mutex_unlock(&audio->write_lock);
			mutex_unlock(&audio->read_lock);
		}
		mutex_unlock(&audio->lock);
		break;
	}
	case AUDIO_DEREGISTER_ION_32: {
		struct msm_audio_ion_info32 info_32;
		struct msm_audio_ion_info info;

		mutex_lock(&audio->lock);
		pr_debug("%s[%pK]:AUDIO_DEREGISTER_ION\n", __func__, audio);
		if (copy_from_user(&info_32, (void *)arg, sizeof(info_32))) {
			pr_err("%s: copy_from_user for	AUDIO_DEREGISTER_ION_32 failed\n",
				__func__);
			rc = -EFAULT;
		} else {
			info.fd = info_32.fd;
			info.vaddr = compat_ptr(info_32.vaddr);
			mutex_lock(&audio->read_lock);
			mutex_lock(&audio->write_lock);
			rc = audio_aio_ion_remove(audio, &info);
			mutex_unlock(&audio->write_lock);
			mutex_unlock(&audio->read_lock);
		}
		mutex_unlock(&audio->lock);
		break;
	}
	default:
		pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
		rc =  -EINVAL;
	}
	return rc;
}
#endif