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/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);