DO NOT MERGE Fix AudioEffect reply overflow am: 7ffa39c454 am: 0dac1009bb
am: 02bdc83136  -s ours

* commit '02bdc83136bd6d360d513d9a9a4015e4dab9eea3':
  DO NOT MERGE Fix AudioEffect reply overflow

Change-Id: Ib0b37de80c6c9e23aa15967e019e7ea945061019
diff --git a/Android.mk b/Android.mk
index 1228876..6dbcccd 100644
--- a/Android.mk
+++ b/Android.mk
@@ -1,4 +1,4 @@
-ifneq ($(filter msm8960 msm8226 msm8x26 msm8974 msm8x74,$(TARGET_BOARD_PLATFORM)),)
+ifneq ($(filter msm8960 msm8226 msm8x26 msm8974 msm8x74 msm8x84 msm8084,$(TARGET_BOARD_PLATFORM)),)
 
 MY_LOCAL_PATH := $(call my-dir)
 
@@ -8,6 +8,7 @@
 include $(MY_LOCAL_PATH)/hal/Android.mk
 include $(MY_LOCAL_PATH)/voice_processing/Android.mk
 include $(MY_LOCAL_PATH)/visualizer/Android.mk
+include $(MY_LOCAL_PATH)/post_proc/Android.mk
 endif
 
 endif
diff --git a/hal/Android.mk b/hal/Android.mk
index b5949da..18b8bf4 100644
--- a/hal/Android.mk
+++ b/hal/Android.mk
@@ -7,13 +7,22 @@
 LOCAL_ARM_MODE := arm
 
 AUDIO_PLATFORM := $(TARGET_BOARD_PLATFORM)
-ifneq ($(filter msm8974 msm8226,$(TARGET_BOARD_PLATFORM)),)
+ifneq ($(filter msm8974 msm8226 msm8084,$(TARGET_BOARD_PLATFORM)),)
   # B-family platform uses msm8974 code base
   AUDIO_PLATFORM = msm8974
+ifneq ($(filter msm8226,$(TARGET_BOARD_PLATFORM)),)
+  LOCAL_CFLAGS := -DPLATFORM_MSM8x26
+endif
+ifneq ($(filter msm8084,$(TARGET_BOARD_PLATFORM)),)
+  LOCAL_CFLAGS := -DPLATFORM_MSM8084
+endif
 endif
 
 LOCAL_SRC_FILES := \
 	audio_hw.c \
+	voice.c \
+	platform_info.c \
+	audio_extn/ext_speaker.c \
 	$(AUDIO_PLATFORM)/platform.c
 
 LOCAL_SHARED_LIBRARIES := \
@@ -22,17 +31,30 @@
 	libtinyalsa \
 	libtinycompress \
 	libaudioroute \
-	libdl
-
+	libdl \
+	libexpat
 
 LOCAL_C_INCLUDES += \
 	external/tinyalsa/include \
 	external/tinycompress/include \
 	$(call include-path-for, audio-route) \
 	$(call include-path-for, audio-effects) \
-	$(LOCAL_PATH)/$(AUDIO_PLATFORM)
+	$(LOCAL_PATH)/$(AUDIO_PLATFORM) \
+	$(LOCAL_PATH)/audio_extn \
+	$(LOCAL_PATH)/voice_extn \
+	external/expat/lib
 
-LOCAL_MODULE := audio.primary.$(AUDIO_PLATFORM)
+ifeq ($(strip $(AUDIO_FEATURE_ENABLED_MULTI_VOICE_SESSIONS)),true)
+    LOCAL_CFLAGS += -DMULTI_VOICE_SESSION_ENABLED
+    LOCAL_SRC_FILES += voice_extn/voice_extn.c
+endif
+
+ifeq ($(strip $(AUDIO_FEATURE_ENABLED_HFP)),true)
+    LOCAL_CFLAGS += -DHFP_ENABLED
+    LOCAL_SRC_FILES += audio_extn/hfp.c
+endif
+
+LOCAL_MODULE := audio.primary.$(TARGET_BOARD_PLATFORM)
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
 
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
new file mode 100644
index 0000000..26c2fb4
--- /dev/null
+++ b/hal/audio_extn/audio_extn.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AUDIO_EXTN_H
+#define AUDIO_EXTN_H
+
+#include <cutils/str_parms.h>
+
+void *audio_extn_extspk_init(struct audio_device *adev);
+void audio_extn_extspk_deinit(void *extn);
+void audio_extn_extspk_update(void* extn);
+void audio_extn_extspk_set_mode(void* extn, audio_mode_t mode);
+void audio_extn_extspk_set_voice_vol(void* extn, float vol);
+
+#ifndef HFP_ENABLED
+#define audio_extn_hfp_is_active(adev)                  (0)
+#define audio_extn_hfp_get_usecase()                    (-1)
+#define audio_extn_hfp_set_parameters(adev, params)     (0)
+#else
+bool audio_extn_hfp_is_active(struct audio_device *adev);
+
+audio_usecase_t audio_extn_hfp_get_usecase();
+
+void audio_extn_hfp_set_parameters(struct audio_device *adev,
+                                    struct str_parms *parms);
+#endif
+
+#endif /* AUDIO_EXTN_H */
diff --git a/hal/audio_extn/ext_speaker.c b/hal/audio_extn/ext_speaker.c
new file mode 100644
index 0000000..a551fb3
--- /dev/null
+++ b/hal/audio_extn/ext_speaker.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "ext_speaker"
+/*#define LOG_NDEBUG 0*/
+#include <cutils/log.h>
+
+#include <audio_hw.h>
+#include <dlfcn.h>
+
+#ifdef __LP64__
+        #define LIB_SPEAKER_BUNDLE "/system/lib64/soundfx/libspeakerbundle.so"
+#else
+        #define LIB_SPEAKER_BUNDLE "/system/lib/soundfx/libspeakerbundle.so"
+#endif
+
+typedef void (*set_mode_t)(int);
+typedef void (*set_speaker_on_t)(bool);
+typedef void (*set_earpiece_on_t)(bool);
+typedef void (*set_voice_vol_t)(float);
+
+struct speaker_data {
+    struct audio_device *adev;
+    void *speaker_bundle;
+    set_mode_t set_mode;
+    set_speaker_on_t set_speaker_on;
+    set_earpiece_on_t set_earpiece_on;
+    set_voice_vol_t set_voice_vol;
+};
+
+static struct speaker_data* open_speaker_bundle()
+{
+    struct speaker_data *sd = calloc(1, sizeof(struct speaker_data));
+
+    sd->speaker_bundle = dlopen(LIB_SPEAKER_BUNDLE, RTLD_NOW);
+    if (sd->speaker_bundle == NULL) {
+        ALOGE("%s: DLOPEN failed for %s", __func__, LIB_SPEAKER_BUNDLE);
+        goto error;
+    } else {
+        ALOGV("%s: DLOPEN successful for %s", __func__, LIB_SPEAKER_BUNDLE);
+
+        sd->set_mode = (set_mode_t)dlsym(sd->speaker_bundle,
+                                             "set_mode");
+        if (sd->set_mode == NULL) {
+            ALOGE("%s: dlsym error %s for set_mode", __func__,
+                  dlerror());
+            goto error;
+        }
+        sd->set_speaker_on = (set_speaker_on_t)dlsym(sd->speaker_bundle,
+                                             "set_speaker_on");
+        if (sd->set_speaker_on == NULL) {
+            ALOGE("%s: dlsym error %s for set_speaker_on", __func__,
+                  dlerror());
+            goto error;
+        }
+        sd->set_earpiece_on = (set_earpiece_on_t)dlsym(sd->speaker_bundle,
+                                             "set_earpiece_on");
+        if (sd->set_earpiece_on == NULL) {
+            ALOGE("%s: dlsym error %s for set_earpiece_on", __func__,
+                  dlerror());
+            goto error;
+        }
+        sd->set_voice_vol = (set_voice_vol_t)dlsym(sd->speaker_bundle,
+                                             "set_voice_volume");
+        if (sd->set_voice_vol == NULL) {
+            ALOGE("%s: dlsym error %s for set_voice_volume",
+                  __func__, dlerror());
+            goto error;
+        }
+    }
+    return sd;
+
+error:
+    free(sd);
+    return 0;
+}
+
+static void close_speaker_bundle(struct speaker_data *sd)
+{
+    if (sd != NULL) {
+        dlclose(sd->speaker_bundle);
+        free(sd);
+        sd = NULL;
+    }
+}
+
+void *audio_extn_extspk_init(struct audio_device *adev)
+{
+    struct speaker_data *data = open_speaker_bundle();
+
+    if (data)
+        data->adev = adev;
+
+    return data;
+}
+
+void audio_extn_extspk_deinit(void *extn)
+{
+    struct speaker_data *data = (struct speaker_data*)extn;
+    close_speaker_bundle(data);
+}
+
+void audio_extn_extspk_update(void* extn)
+{
+    struct speaker_data *data = (struct speaker_data*)extn;
+
+    if (data) {
+        bool speaker_on = false;
+        bool earpiece_on = false;
+        struct listnode *node;
+        struct audio_usecase *usecase;
+        list_for_each(node, &data->adev->usecase_list) {
+            usecase = node_to_item(node, struct audio_usecase, list);
+            if (usecase->devices & AUDIO_DEVICE_OUT_EARPIECE) {
+                if(data->adev->snd_dev_ref_cnt[usecase->out_snd_device] != 0) {
+                    earpiece_on = true;
+                }
+            }
+            if (usecase->devices & AUDIO_DEVICE_OUT_SPEAKER) {
+                if(data->adev->snd_dev_ref_cnt[usecase->out_snd_device] != 0) {
+                    speaker_on = true;
+                }
+            }
+        }
+        data->set_earpiece_on(earpiece_on);
+        data->set_speaker_on(speaker_on);
+    }
+}
+
+void audio_extn_extspk_set_mode(void* extn, audio_mode_t mode)
+{
+    struct speaker_data *data = (struct speaker_data*)extn;
+
+    if (data)
+        data->set_mode(mode);
+}
+
+void audio_extn_extspk_set_voice_vol(void* extn, float vol)
+{
+    struct speaker_data *data = (struct speaker_data*)extn;
+
+    if (data)
+        data->set_voice_vol(vol);
+}
diff --git a/hal/audio_extn/hfp.c b/hal/audio_extn/hfp.c
new file mode 100644
index 0000000..a108730
--- /dev/null
+++ b/hal/audio_extn/hfp.c
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "audio_hw_hfp"
+/*#define LOG_NDEBUG 0*/
+#define LOG_NDDEBUG 0
+
+#include <errno.h>
+#include <math.h>
+#include <cutils/log.h>
+
+#include "audio_hw.h"
+#include "platform.h"
+#include "platform_api.h"
+#include <stdlib.h>
+#include <cutils/str_parms.h>
+
+#define AUDIO_PARAMETER_HFP_ENABLE      "hfp_enable"
+#define AUDIO_PARAMETER_HFP_SET_SAMPLING_RATE "hfp_set_sampling_rate"
+#define AUDIO_PARAMETER_KEY_HFP_VOLUME "hfp_volume"
+
+static int32_t start_hfp(struct audio_device *adev,
+                               struct str_parms *parms);
+
+static int32_t stop_hfp(struct audio_device *adev);
+
+struct hfp_module {
+    struct pcm *hfp_sco_rx;
+    struct pcm *hfp_sco_tx;
+    struct pcm *hfp_pcm_rx;
+    struct pcm *hfp_pcm_tx;
+    float  hfp_volume;
+    bool   is_hfp_running;
+    audio_usecase_t ucid;
+};
+
+static struct hfp_module hfpmod = {
+    .hfp_sco_rx = NULL,
+    .hfp_sco_tx = NULL,
+    .hfp_pcm_rx = NULL,
+    .hfp_pcm_tx = NULL,
+    .hfp_volume = 0,
+    .is_hfp_running = 0,
+    .ucid = USECASE_AUDIO_HFP_SCO,
+};
+static struct pcm_config pcm_config_hfp = {
+    .channels = 1,
+    .rate = 8000,
+    .period_size = 240,
+    .period_count = 2,
+    .format = PCM_FORMAT_S16_LE,
+    .start_threshold = 0,
+    .stop_threshold = INT_MAX,
+    .avail_min = 0,
+};
+
+static int32_t hfp_set_volume(struct audio_device *adev, float value)
+{
+    int32_t vol, ret = 0;
+    struct mixer_ctl *ctl;
+    const char *mixer_ctl_name = "Internal HFP RX Volume";
+
+    ALOGV("%s: entry", __func__);
+    ALOGD("%s: (%f)\n", __func__, value);
+
+    if (value < 0.0) {
+        ALOGW("%s: (%f) Under 0.0, assuming 0.0\n", __func__, value);
+        value = 0.0;
+    } else {
+        value = ((value > 15.000000) ? 1.0 : (value / 15));
+        ALOGW("%s: Volume brought with in range (%f)\n", __func__, value);
+    }
+    vol  = lrint((value * 0x2000) + 0.5);
+    hfpmod.hfp_volume = value;
+
+    if (!hfpmod.is_hfp_running) {
+        ALOGV("%s: HFP not active, ignoring set_hfp_volume call", __func__);
+        return -EIO;
+    }
+
+    ALOGD("%s: Setting HFP volume to %d \n", __func__, vol);
+    ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
+    if (!ctl) {
+        ALOGE("%s: Could not get ctl for mixer cmd - %s",
+              __func__, mixer_ctl_name);
+        return -EINVAL;
+    }
+    if(mixer_ctl_set_value(ctl, 0, vol) < 0) {
+        ALOGE("%s: Couldn't set HFP Volume: [%d]", __func__, vol);
+        return -EINVAL;
+    }
+
+    ALOGV("%s: exit", __func__);
+    return ret;
+}
+
+static int32_t start_hfp(struct audio_device *adev,
+                         struct str_parms *parms __unused)
+{
+    int32_t i, ret = 0;
+    struct audio_usecase *uc_info;
+    int32_t pcm_dev_rx_id, pcm_dev_tx_id, pcm_dev_asm_rx_id, pcm_dev_asm_tx_id;
+
+    ALOGD("%s: enter", __func__);
+
+    uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
+    uc_info->id = hfpmod.ucid;
+    uc_info->type = PCM_HFP_CALL;
+    uc_info->stream.out = adev->primary_output;
+    uc_info->devices = adev->primary_output->devices;
+    uc_info->in_snd_device = SND_DEVICE_NONE;
+    uc_info->out_snd_device = SND_DEVICE_NONE;
+
+    list_add_tail(&adev->usecase_list, &uc_info->list);
+
+    select_devices(adev, hfpmod.ucid);
+
+    pcm_dev_rx_id = platform_get_pcm_device_id(uc_info->id, PCM_PLAYBACK);
+    pcm_dev_tx_id = platform_get_pcm_device_id(uc_info->id, PCM_CAPTURE);
+    pcm_dev_asm_rx_id = HFP_ASM_RX_TX;
+    pcm_dev_asm_tx_id = HFP_ASM_RX_TX;
+    if (pcm_dev_rx_id < 0 || pcm_dev_tx_id < 0 ||
+        pcm_dev_asm_rx_id < 0 || pcm_dev_asm_tx_id < 0 ) {
+        ALOGE("%s: Invalid PCM devices (rx: %d tx: %d asm: rx tx %d) for the usecase(%d)",
+              __func__, pcm_dev_rx_id, pcm_dev_tx_id, pcm_dev_asm_rx_id, uc_info->id);
+        ret = -EIO;
+        goto exit;
+    }
+
+    ALOGV("%s: HFP PCM devices (hfp rx tx: %d pcm rx tx: %d) for the usecase(%d)",
+              __func__, pcm_dev_rx_id, pcm_dev_tx_id, uc_info->id);
+
+    ALOGV("%s: Opening PCM playback device card_id(%d) device_id(%d)",
+          __func__, adev->snd_card, pcm_dev_rx_id);
+    hfpmod.hfp_sco_rx = pcm_open(adev->snd_card,
+                                  pcm_dev_asm_rx_id,
+                                  PCM_OUT, &pcm_config_hfp);
+    if (hfpmod.hfp_sco_rx && !pcm_is_ready(hfpmod.hfp_sco_rx)) {
+        ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_sco_rx));
+        ret = -EIO;
+        goto exit;
+    }
+    ALOGD("%s: Opening PCM capture device card_id(%d) device_id(%d)",
+          __func__, adev->snd_card, pcm_dev_tx_id);
+    hfpmod.hfp_pcm_rx = pcm_open(adev->snd_card,
+                                   pcm_dev_rx_id,
+                                   PCM_OUT, &pcm_config_hfp);
+    if (hfpmod.hfp_pcm_rx && !pcm_is_ready(hfpmod.hfp_pcm_rx)) {
+        ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_pcm_rx));
+        ret = -EIO;
+        goto exit;
+    }
+    hfpmod.hfp_sco_tx = pcm_open(adev->snd_card,
+                                  pcm_dev_asm_tx_id,
+                                  PCM_IN, &pcm_config_hfp);
+    if (hfpmod.hfp_sco_tx && !pcm_is_ready(hfpmod.hfp_sco_tx)) {
+        ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_sco_tx));
+        ret = -EIO;
+        goto exit;
+    }
+    ALOGV("%s: Opening PCM capture device card_id(%d) device_id(%d)",
+          __func__, adev->snd_card, pcm_dev_tx_id);
+    hfpmod.hfp_pcm_tx = pcm_open(adev->snd_card,
+                                   pcm_dev_tx_id,
+                                   PCM_IN, &pcm_config_hfp);
+    if (hfpmod.hfp_pcm_tx && !pcm_is_ready(hfpmod.hfp_pcm_tx)) {
+        ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_pcm_tx));
+        ret = -EIO;
+        goto exit;
+    }
+    pcm_start(hfpmod.hfp_sco_rx);
+    pcm_start(hfpmod.hfp_sco_tx);
+    pcm_start(hfpmod.hfp_pcm_rx);
+    pcm_start(hfpmod.hfp_pcm_tx);
+
+    hfpmod.is_hfp_running = true;
+    hfp_set_volume(adev, hfpmod.hfp_volume);
+
+    ALOGD("%s: exit: status(%d)", __func__, ret);
+    return 0;
+
+exit:
+    stop_hfp(adev);
+    ALOGE("%s: Problem in HFP start: status(%d)", __func__, ret);
+    return ret;
+}
+
+static int32_t stop_hfp(struct audio_device *adev)
+{
+    int32_t i, ret = 0;
+    struct audio_usecase *uc_info;
+
+    ALOGD("%s: enter", __func__);
+    hfpmod.is_hfp_running = false;
+
+    /* 1. Close the PCM devices */
+    if (hfpmod.hfp_sco_rx) {
+        pcm_close(hfpmod.hfp_sco_rx);
+        hfpmod.hfp_sco_rx = NULL;
+    }
+    if (hfpmod.hfp_sco_tx) {
+        pcm_close(hfpmod.hfp_sco_tx);
+        hfpmod.hfp_sco_tx = NULL;
+    }
+    if (hfpmod.hfp_pcm_rx) {
+        pcm_close(hfpmod.hfp_pcm_rx);
+        hfpmod.hfp_pcm_rx = NULL;
+    }
+    if (hfpmod.hfp_pcm_tx) {
+        pcm_close(hfpmod.hfp_pcm_tx);
+        hfpmod.hfp_pcm_tx = NULL;
+    }
+
+    uc_info = get_usecase_from_list(adev, hfpmod.ucid);
+    if (uc_info == NULL) {
+        ALOGE("%s: Could not find the usecase (%d) in the list",
+              __func__, hfpmod.ucid);
+        return -EINVAL;
+    }
+
+    /* 2. Get and set stream specific mixer controls */
+    disable_audio_route(adev, uc_info);
+
+    /* 3. Disable the rx and tx devices */
+    disable_snd_device(adev, uc_info->out_snd_device);
+    disable_snd_device(adev, uc_info->in_snd_device);
+
+    list_remove(&uc_info->list);
+    free(uc_info);
+
+    ALOGD("%s: exit: status(%d)", __func__, ret);
+    return ret;
+}
+
+bool audio_extn_hfp_is_active(struct audio_device *adev)
+{
+    struct audio_usecase *hfp_usecase = NULL;
+    hfp_usecase = get_usecase_from_list(adev, hfpmod.ucid);
+
+    if (hfp_usecase != NULL)
+        return true;
+    else
+        return false;
+}
+
+audio_usecase_t audio_extn_hfp_get_usecase()
+{
+    return hfpmod.ucid;
+}
+
+void audio_extn_hfp_set_parameters(struct audio_device *adev, struct str_parms *parms)
+{
+    int ret;
+    int rate;
+    int val;
+    float vol;
+    char value[32]={0};
+
+    ret = str_parms_get_str(parms, AUDIO_PARAMETER_HFP_ENABLE, value,
+                            sizeof(value));
+    if (ret >= 0) {
+           if (!strncmp(value,"true",sizeof(value)))
+               ret = start_hfp(adev,parms);
+           else
+               stop_hfp(adev);
+    }
+    memset(value, 0, sizeof(value));
+    ret = str_parms_get_str(parms,AUDIO_PARAMETER_HFP_SET_SAMPLING_RATE, value,
+                            sizeof(value));
+    if (ret >= 0) {
+           rate = atoi(value);
+           if (rate == 8000){
+               hfpmod.ucid = USECASE_AUDIO_HFP_SCO;
+               pcm_config_hfp.rate = rate;
+           } else if (rate == 16000){
+               hfpmod.ucid = USECASE_AUDIO_HFP_SCO_WB;
+               pcm_config_hfp.rate = rate;
+           } else
+               ALOGE("Unsupported rate..");
+    }
+
+    if (hfpmod.is_hfp_running) {
+        memset(value, 0, sizeof(value));
+        ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING,
+                                value, sizeof(value));
+        if (ret >= 0) {
+            val = atoi(value);
+            if (val > 0)
+                select_devices(adev, hfpmod.ucid);
+        }
+    }
+
+    memset(value, 0, sizeof(value));
+    ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_HFP_VOLUME,
+                            value, sizeof(value));
+    if (ret >= 0) {
+        if (sscanf(value, "%f", &vol) != 1){
+            ALOGE("%s: error in retrieving hfp volume", __func__);
+            ret = -EIO;
+            goto exit;
+        }
+        ALOGD("%s: set_hfp_volume usecase, Vol: [%f]", __func__, vol);
+        hfp_set_volume(adev, vol);
+    }
+exit:
+    ALOGV("%s Exit",__func__);
+}
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 48c426d..cd9ded8 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2013-2014 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -40,12 +40,15 @@
 #include <cutils/sched_policy.h>
 
 #include <hardware/audio_effect.h>
+#include <hardware/audio_alsaops.h>
 #include <system/thread_defs.h>
 #include <audio_effects/effect_aec.h>
 #include <audio_effects/effect_ns.h>
 #include "audio_hw.h"
+#include "audio_extn.h"
 #include "platform_api.h"
 #include <platform.h>
+#include "voice_extn.h"
 
 #include "sound/compress_params.h"
 
@@ -55,6 +58,17 @@
 #define COMPRESS_OFFLOAD_PLAYBACK_LATENCY 96
 #define COMPRESS_PLAYBACK_VOLUME_MAX 0x2000
 
+#define PROXY_OPEN_RETRY_COUNT           100
+#define PROXY_OPEN_WAIT_TIME             20
+
+static unsigned int configured_low_latency_capture_period_size =
+        LOW_LATENCY_CAPTURE_PERIOD_SIZE;
+
+/* This constant enables extended precision handling.
+ * TODO The flag is off until more testing is done.
+ */
+static const bool k_enable_extended_precision = false;
+
 struct pcm_config pcm_config_deep_buffer = {
     .channels = 2,
     .rate = DEFAULT_OUTPUT_SAMPLING_RATE,
@@ -94,22 +108,57 @@
     .format = PCM_FORMAT_S16_LE,
 };
 
-struct pcm_config pcm_config_voice_call = {
-    .channels = 1,
-    .rate = 8000,
-    .period_size = 160,
-    .period_count = 2,
+#define AFE_PROXY_CHANNEL_COUNT 2
+#define AFE_PROXY_SAMPLING_RATE 48000
+
+#define AFE_PROXY_PLAYBACK_PERIOD_SIZE  768
+#define AFE_PROXY_PLAYBACK_PERIOD_COUNT 4
+
+struct pcm_config pcm_config_afe_proxy_playback = {
+    .channels = AFE_PROXY_CHANNEL_COUNT,
+    .rate = AFE_PROXY_SAMPLING_RATE,
+    .period_size = AFE_PROXY_PLAYBACK_PERIOD_SIZE,
+    .period_count = AFE_PROXY_PLAYBACK_PERIOD_COUNT,
     .format = PCM_FORMAT_S16_LE,
+    .start_threshold = AFE_PROXY_PLAYBACK_PERIOD_SIZE,
+    .stop_threshold = INT_MAX,
+    .avail_min = AFE_PROXY_PLAYBACK_PERIOD_SIZE,
 };
 
-static const char * const use_case_table[AUDIO_USECASE_MAX] = {
+#define AFE_PROXY_RECORD_PERIOD_SIZE  768
+#define AFE_PROXY_RECORD_PERIOD_COUNT 4
+
+struct pcm_config pcm_config_afe_proxy_record = {
+    .channels = AFE_PROXY_CHANNEL_COUNT,
+    .rate = AFE_PROXY_SAMPLING_RATE,
+    .period_size = AFE_PROXY_RECORD_PERIOD_SIZE,
+    .period_count = AFE_PROXY_RECORD_PERIOD_COUNT,
+    .format = PCM_FORMAT_S16_LE,
+    .start_threshold = AFE_PROXY_RECORD_PERIOD_SIZE,
+    .stop_threshold = INT_MAX,
+    .avail_min = AFE_PROXY_RECORD_PERIOD_SIZE,
+};
+
+const char * const use_case_table[AUDIO_USECASE_MAX] = {
     [USECASE_AUDIO_PLAYBACK_DEEP_BUFFER] = "deep-buffer-playback",
     [USECASE_AUDIO_PLAYBACK_LOW_LATENCY] = "low-latency-playback",
     [USECASE_AUDIO_PLAYBACK_MULTI_CH] = "multi-channel-playback",
+    [USECASE_AUDIO_PLAYBACK_OFFLOAD] = "compress-offload-playback",
+
     [USECASE_AUDIO_RECORD] = "audio-record",
     [USECASE_AUDIO_RECORD_LOW_LATENCY] = "low-latency-record",
+
+    [USECASE_AUDIO_HFP_SCO] = "hfp-sco",
+    [USECASE_AUDIO_HFP_SCO_WB] = "hfp-sco-wb",
+
     [USECASE_VOICE_CALL] = "voice-call",
-    [USECASE_AUDIO_PLAYBACK_OFFLOAD] = "compress-offload-playback",
+    [USECASE_VOICE2_CALL] = "voice2-call",
+    [USECASE_VOLTE_CALL] = "volte-call",
+    [USECASE_QCHAT_CALL] = "qchat-call",
+    [USECASE_VOWLAN_CALL] = "vowlan-call",
+
+    [USECASE_AUDIO_PLAYBACK_AFE_PROXY] = "afe-proxy-playback",
+    [USECASE_AUDIO_RECORD_AFE_PROXY] = "afe-proxy-record",
 };
 
 
@@ -130,10 +179,15 @@
 
 static bool is_supported_format(audio_format_t format)
 {
-    if (format == AUDIO_FORMAT_MP3 ||
-            format == AUDIO_FORMAT_AAC)
-        return true;
-
+    switch (format) {
+        case AUDIO_FORMAT_MP3:
+        case AUDIO_FORMAT_AAC_LC:
+        case AUDIO_FORMAT_AAC_HE_V1:
+        case AUDIO_FORMAT_AAC_HE_V2:
+            return true;
+        default:
+            break;
+    }
     return false;
 }
 
@@ -141,7 +195,7 @@
 {
     int id = 0;
 
-    switch (format) {
+    switch (format & AUDIO_FORMAT_MAIN_MASK) {
     case AUDIO_FORMAT_MP3:
         id = SND_AUDIOCODEC_MP3;
         break;
@@ -155,9 +209,21 @@
     return id;
 }
 
-static int enable_audio_route(struct audio_device *adev,
-                              struct audio_usecase *usecase,
-                              bool update_mixer)
+int pcm_ioctl(void *pcm, int request, ...)
+{
+    va_list ap;
+    void * arg;
+    int pcm_fd = *(int*)pcm;
+
+    va_start(ap, request);
+    arg = va_arg(ap, void *);
+    va_end(ap);
+
+    return ioctl(pcm_fd, request, arg);
+}
+
+int enable_audio_route(struct audio_device *adev,
+                       struct audio_usecase *usecase)
 {
     snd_device_t snd_device;
     char mixer_path[50];
@@ -173,19 +239,16 @@
         snd_device = usecase->out_snd_device;
 
     strcpy(mixer_path, use_case_table[usecase->id]);
-    platform_add_backend_name(mixer_path, snd_device);
-    ALOGV("%s: apply mixer path: %s", __func__, mixer_path);
-    audio_route_apply_path(adev->audio_route, mixer_path);
-    if (update_mixer)
-        audio_route_update_mixer(adev->audio_route);
+    platform_add_backend_name(adev->platform, mixer_path, snd_device);
+    ALOGD("%s: apply and update mixer path: %s", __func__, mixer_path);
+    audio_route_apply_and_update_path(adev->audio_route, mixer_path);
 
     ALOGV("%s: exit", __func__);
     return 0;
 }
 
-static int disable_audio_route(struct audio_device *adev,
-                               struct audio_usecase *usecase,
-                               bool update_mixer)
+int disable_audio_route(struct audio_device *adev,
+                        struct audio_usecase *usecase)
 {
     snd_device_t snd_device;
     char mixer_path[50];
@@ -199,19 +262,16 @@
     else
         snd_device = usecase->out_snd_device;
     strcpy(mixer_path, use_case_table[usecase->id]);
-    platform_add_backend_name(mixer_path, snd_device);
-    ALOGV("%s: reset mixer path: %s", __func__, mixer_path);
-    audio_route_reset_path(adev->audio_route, mixer_path);
-    if (update_mixer)
-        audio_route_update_mixer(adev->audio_route);
+    platform_add_backend_name(adev->platform, mixer_path, snd_device);
+    ALOGD("%s: reset and update mixer path: %s", __func__, mixer_path);
+    audio_route_reset_and_update_path(adev->audio_route, mixer_path);
 
     ALOGV("%s: exit", __func__);
     return 0;
 }
 
-static int enable_snd_device(struct audio_device *adev,
-                             snd_device_t snd_device,
-                             bool update_mixer)
+int enable_snd_device(struct audio_device *adev,
+                      snd_device_t snd_device)
 {
     if (snd_device < SND_DEVICE_MIN ||
         snd_device >= SND_DEVICE_MAX) {
@@ -231,18 +291,15 @@
         return -EINVAL;
     }
 
-    ALOGV("%s: snd_device(%d: %s)", __func__,
-          snd_device, platform_get_snd_device_name(snd_device));
-    audio_route_apply_path(adev->audio_route, platform_get_snd_device_name(snd_device));
-    if (update_mixer)
-        audio_route_update_mixer(adev->audio_route);
+    const char * dev_path = platform_get_snd_device_name(snd_device);
+    ALOGD("%s: snd_device(%d: %s)", __func__, snd_device, dev_path);
+    audio_route_apply_and_update_path(adev->audio_route, dev_path);
 
     return 0;
 }
 
-static int disable_snd_device(struct audio_device *adev,
-                              snd_device_t snd_device,
-                              bool update_mixer)
+int disable_snd_device(struct audio_device *adev,
+                       snd_device_t snd_device)
 {
     if (snd_device < SND_DEVICE_MIN ||
         snd_device >= SND_DEVICE_MAX) {
@@ -255,11 +312,9 @@
     }
     adev->snd_dev_ref_cnt[snd_device]--;
     if (adev->snd_dev_ref_cnt[snd_device] == 0) {
-        ALOGV("%s: snd_device(%d: %s)", __func__,
-              snd_device, platform_get_snd_device_name(snd_device));
-        audio_route_reset_path(adev->audio_route, platform_get_snd_device_name(snd_device));
-        if (update_mixer)
-            audio_route_update_mixer(adev->audio_route);
+        const char * dev_path = platform_get_snd_device_name(snd_device);
+        ALOGD("%s: snd_device(%d: %s)", __func__, snd_device, dev_path);
+        audio_route_reset_and_update_path(adev->audio_route, dev_path);
     }
     return 0;
 }
@@ -297,33 +352,27 @@
             ALOGV("%s: Usecase (%s) is active on (%s) - disabling ..",
                   __func__, use_case_table[usecase->id],
                   platform_get_snd_device_name(usecase->out_snd_device));
-            disable_audio_route(adev, usecase, false);
+            disable_audio_route(adev, usecase);
             switch_device[usecase->id] = true;
             num_uc_to_switch++;
         }
     }
 
     if (num_uc_to_switch) {
-        /* Make sure all the streams are de-routed before disabling the device */
-        audio_route_update_mixer(adev->audio_route);
-
         list_for_each(node, &adev->usecase_list) {
             usecase = node_to_item(node, struct audio_usecase, list);
             if (switch_device[usecase->id]) {
-                disable_snd_device(adev, usecase->out_snd_device, false);
+                disable_snd_device(adev, usecase->out_snd_device);
             }
         }
 
         list_for_each(node, &adev->usecase_list) {
             usecase = node_to_item(node, struct audio_usecase, list);
             if (switch_device[usecase->id]) {
-                enable_snd_device(adev, snd_device, false);
+                enable_snd_device(adev, snd_device);
             }
         }
 
-        /* Make sure new snd device is enabled before re-routing the streams */
-        audio_route_update_mixer(adev->audio_route);
-
         /* Re-route all the usecases on the shared backend other than the
            specified usecase to new snd devices */
         list_for_each(node, &adev->usecase_list) {
@@ -331,11 +380,9 @@
             /* Update the out_snd_device only before enabling the audio route */
             if (switch_device[usecase->id] ) {
                 usecase->out_snd_device = snd_device;
-                enable_audio_route(adev, usecase, false);
+                enable_audio_route(adev, usecase);
             }
         }
-
-        audio_route_update_mixer(adev->audio_route);
     }
 }
 
@@ -369,27 +416,27 @@
             ALOGV("%s: Usecase (%s) is active on (%s) - disabling ..",
                   __func__, use_case_table[usecase->id],
                   platform_get_snd_device_name(usecase->in_snd_device));
-            disable_audio_route(adev, usecase, false);
+            disable_audio_route(adev, usecase);
             switch_device[usecase->id] = true;
             num_uc_to_switch++;
         }
     }
 
     if (num_uc_to_switch) {
-        /* Make sure all the streams are de-routed before disabling the device */
-        audio_route_update_mixer(adev->audio_route);
+        list_for_each(node, &adev->usecase_list) {
+            usecase = node_to_item(node, struct audio_usecase, list);
+            if (switch_device[usecase->id]) {
+                disable_snd_device(adev, usecase->in_snd_device);
+            }
+        }
 
         list_for_each(node, &adev->usecase_list) {
             usecase = node_to_item(node, struct audio_usecase, list);
             if (switch_device[usecase->id]) {
-                disable_snd_device(adev, usecase->in_snd_device, false);
-                enable_snd_device(adev, snd_device, false);
+                enable_snd_device(adev, snd_device);
             }
         }
 
-        /* Make sure new snd device is enabled before re-routing the streams */
-        audio_route_update_mixer(adev->audio_route);
-
         /* Re-route all the usecases on the shared backend other than the
            specified usecase to new snd devices */
         list_for_each(node, &adev->usecase_list) {
@@ -397,15 +444,12 @@
             /* Update the in_snd_device only before enabling the audio route */
             if (switch_device[usecase->id] ) {
                 usecase->in_snd_device = snd_device;
-                enable_audio_route(adev, usecase, false);
+                enable_audio_route(adev, usecase);
             }
         }
-
-        audio_route_update_mixer(adev->audio_route);
     }
 }
 
-
 /* must be called with hw device mutex locked */
 static int read_hdmi_channel_masks(struct stream_out *out)
 {
@@ -434,8 +478,23 @@
     return ret;
 }
 
-static struct audio_usecase *get_usecase_from_list(struct audio_device *adev,
-                                                   audio_usecase_t uc_id)
+static audio_usecase_t get_voice_usecase_id_from_list(struct audio_device *adev)
+{
+    struct audio_usecase *usecase;
+    struct listnode *node;
+
+    list_for_each(node, &adev->usecase_list) {
+        usecase = node_to_item(node, struct audio_usecase, list);
+        if (usecase->type == VOICE_CALL) {
+            ALOGV("%s: usecase id %d", __func__, usecase->id);
+            return usecase->id;
+        }
+    }
+    return USECASE_INVALID;
+}
+
+struct audio_usecase *get_usecase_from_list(struct audio_device *adev,
+                                            audio_usecase_t uc_id)
 {
     struct audio_usecase *usecase;
     struct listnode *node;
@@ -448,13 +507,15 @@
     return NULL;
 }
 
-static int select_devices(struct audio_device *adev,
-                          audio_usecase_t uc_id)
+int select_devices(struct audio_device *adev,
+                   audio_usecase_t uc_id)
 {
     snd_device_t out_snd_device = SND_DEVICE_NONE;
     snd_device_t in_snd_device = SND_DEVICE_NONE;
     struct audio_usecase *usecase = NULL;
     struct audio_usecase *vc_usecase = NULL;
+    struct audio_usecase *hfp_usecase = NULL;
+    audio_usecase_t hfp_ucid;
     struct listnode *node;
     int status = 0;
 
@@ -464,7 +525,8 @@
         return -EINVAL;
     }
 
-    if (usecase->type == VOICE_CALL) {
+    if ((usecase->type == VOICE_CALL) ||
+        (usecase->type == PCM_HFP_CALL)) {
         out_snd_device = platform_get_output_snd_device(adev->platform,
                                                         usecase->stream.out->devices);
         in_snd_device = platform_get_input_snd_device(adev->platform, usecase->stream.out->devices);
@@ -477,12 +539,22 @@
          * usecase. This is to avoid switching devices for voice call when
          * check_usecases_codec_backend() is called below.
          */
-        if (adev->in_call) {
-            vc_usecase = get_usecase_from_list(adev, USECASE_VOICE_CALL);
-            if (vc_usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND) {
+        if (voice_is_in_call(adev)) {
+            vc_usecase = get_usecase_from_list(adev,
+                                               get_voice_usecase_id_from_list(adev));
+            if ((vc_usecase != NULL) &&
+                ((vc_usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND) ||
+                (usecase->devices == AUDIO_DEVICE_IN_VOICE_CALL))) {
                 in_snd_device = vc_usecase->in_snd_device;
                 out_snd_device = vc_usecase->out_snd_device;
             }
+        } else if (audio_extn_hfp_is_active(adev)) {
+            hfp_ucid = audio_extn_hfp_get_usecase();
+            hfp_usecase = get_usecase_from_list(adev, hfp_ucid);
+            if (hfp_usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND) {
+                   in_snd_device = hfp_usecase->in_snd_device;
+                   out_snd_device = hfp_usecase->out_snd_device;
+            }
         }
         if (usecase->type == PCM_PLAYBACK) {
             usecase->devices = usecase->stream.out->devices;
@@ -500,14 +572,14 @@
             usecase->devices = usecase->stream.in->device;
             out_snd_device = SND_DEVICE_NONE;
             if (in_snd_device == SND_DEVICE_NONE) {
+                audio_devices_t out_device = AUDIO_DEVICE_NONE;
                 if (adev->active_input->source == AUDIO_SOURCE_VOICE_COMMUNICATION &&
                         adev->primary_output && !adev->primary_output->standby) {
-                    in_snd_device = platform_get_input_snd_device(adev->platform,
-                                        adev->primary_output->devices);
-                } else {
-                    in_snd_device = platform_get_input_snd_device(adev->platform,
-                                                                  AUDIO_DEVICE_NONE);
+                    out_device = adev->primary_output->devices;
+                } else if (usecase->id == USECASE_AUDIO_RECORD_AFE_PROXY) {
+                    out_device = AUDIO_DEVICE_OUT_TELEPHONY_TX;
                 }
+                in_snd_device = platform_get_input_snd_device(adev->platform, out_device);
             }
         }
     }
@@ -526,31 +598,45 @@
      * and enable both RX and TX devices though one of them is same as current
      * device.
      */
-    if (usecase->type == VOICE_CALL) {
+    if ((usecase->type == VOICE_CALL) &&
+        (usecase->in_snd_device != SND_DEVICE_NONE) &&
+        (usecase->out_snd_device != SND_DEVICE_NONE)) {
         status = platform_switch_voice_call_device_pre(adev->platform);
     }
 
     /* Disable current sound devices */
     if (usecase->out_snd_device != SND_DEVICE_NONE) {
-        disable_audio_route(adev, usecase, true);
-        disable_snd_device(adev, usecase->out_snd_device, false);
+        disable_audio_route(adev, usecase);
+        disable_snd_device(adev, usecase->out_snd_device);
     }
 
     if (usecase->in_snd_device != SND_DEVICE_NONE) {
-        disable_audio_route(adev, usecase, true);
-        disable_snd_device(adev, usecase->in_snd_device, false);
+        disable_audio_route(adev, usecase);
+        disable_snd_device(adev, usecase->in_snd_device);
+    }
+
+    /* Applicable only on the targets that has external modem.
+     * New device information should be sent to modem before enabling
+     * the devices to reduce in-call device switch time.
+     */
+    if ((usecase->type == VOICE_CALL) &&
+        (usecase->in_snd_device != SND_DEVICE_NONE) &&
+        (usecase->out_snd_device != SND_DEVICE_NONE)) {
+        status = platform_switch_voice_call_enable_device_config(adev->platform,
+                                                                 out_snd_device,
+                                                                 in_snd_device);
     }
 
     /* Enable new sound devices */
     if (out_snd_device != SND_DEVICE_NONE) {
         if (usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND)
             check_usecases_codec_backend(adev, usecase, out_snd_device);
-        enable_snd_device(adev, out_snd_device, false);
+        enable_snd_device(adev, out_snd_device);
     }
 
     if (in_snd_device != SND_DEVICE_NONE) {
         check_and_route_capture_usecases(adev, usecase, in_snd_device);
-        enable_snd_device(adev, in_snd_device, false);
+        enable_snd_device(adev, in_snd_device);
     }
 
     if (usecase->type == VOICE_CALL)
@@ -558,12 +644,19 @@
                                                         out_snd_device,
                                                         in_snd_device);
 
-    audio_route_update_mixer(adev->audio_route);
-
     usecase->in_snd_device = in_snd_device;
     usecase->out_snd_device = out_snd_device;
 
-    enable_audio_route(adev, usecase, true);
+    enable_audio_route(adev, usecase);
+
+    /* Applicable only on the targets that has external modem.
+     * Enable device command should be sent to modem only after
+     * enabling voice call mixer controls
+     */
+    if (usecase->type == VOICE_CALL)
+        status = platform_switch_voice_call_usecase_route_post(adev->platform,
+                                                               out_snd_device,
+                                                               in_snd_device);
 
     return status;
 }
@@ -586,10 +679,10 @@
     }
 
     /* 1. Disable stream specific mixer controls */
-    disable_audio_route(adev, uc_info, true);
+    disable_audio_route(adev, uc_info);
 
     /* 2. Disable the tx device */
-    disable_snd_device(adev, uc_info->in_snd_device, true);
+    disable_snd_device(adev, uc_info->in_snd_device);
 
     list_remove(&uc_info->list);
     free(uc_info);
@@ -627,16 +720,35 @@
     select_devices(adev, in->usecase);
 
     ALOGV("%s: Opening PCM device card_id(%d) device_id(%d), channels %d",
-          __func__, SOUND_CARD, in->pcm_device_id, in->config.channels);
-    in->pcm = pcm_open(SOUND_CARD, in->pcm_device_id,
-                           PCM_IN, &in->config);
-    if (in->pcm && !pcm_is_ready(in->pcm)) {
-        ALOGE("%s: %s", __func__, pcm_get_error(in->pcm));
-        pcm_close(in->pcm);
-        in->pcm = NULL;
-        ret = -EIO;
-        goto error_open;
+          __func__, adev->snd_card, in->pcm_device_id, in->config.channels);
+
+    unsigned int flags = PCM_IN;
+    unsigned int pcm_open_retry_count = 0;
+
+    if (in->usecase == USECASE_AUDIO_RECORD_AFE_PROXY) {
+        flags |= PCM_MMAP | PCM_NOIRQ;
+        pcm_open_retry_count = PROXY_OPEN_RETRY_COUNT;
     }
+
+    while (1) {
+        in->pcm = pcm_open(adev->snd_card, in->pcm_device_id,
+                           flags, &in->config);
+        if (in->pcm == NULL || !pcm_is_ready(in->pcm)) {
+            ALOGE("%s: %s", __func__, pcm_get_error(in->pcm));
+            if (in->pcm != NULL) {
+                pcm_close(in->pcm);
+                in->pcm = NULL;
+            }
+            if (pcm_open_retry_count-- == 0) {
+                ret = -EIO;
+                goto error_open;
+            }
+            usleep(PROXY_OPEN_WAIT_TIME * 1000);
+            continue;
+        }
+        break;
+    }
+
     ALOGV("%s: exit", __func__);
     return ret;
 
@@ -847,7 +959,7 @@
         usecase = node_to_item(node, struct audio_usecase, list);
         if (usecase->type == PCM_PLAYBACK &&
                 usecase->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
-            disable_audio_route(adev, usecase, true);
+            disable_audio_route(adev, usecase);
         }
     }
 
@@ -859,7 +971,7 @@
         usecase = node_to_item(node, struct audio_usecase, list);
         if (usecase->type == PCM_PLAYBACK &&
                 usecase->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
-            enable_audio_route(adev, usecase, true);
+            enable_audio_route(adev, usecase);
         }
     }
 
@@ -881,19 +993,24 @@
         return -EINVAL;
     }
 
-    if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD &&
-            adev->visualizer_stop_output != NULL)
-        adev->visualizer_stop_output(out->handle);
+    if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD) {
+        if (adev->visualizer_stop_output != NULL)
+            adev->visualizer_stop_output(out->handle, out->pcm_device_id);
+        if (adev->offload_effects_stop_output != NULL)
+            adev->offload_effects_stop_output(out->handle, out->pcm_device_id);
+    }
 
     /* 1. Get and set stream specific mixer controls */
-    disable_audio_route(adev, uc_info, true);
+    disable_audio_route(adev, uc_info);
 
     /* 2. Disable the rx device */
-    disable_snd_device(adev, uc_info->out_snd_device, true);
+    disable_snd_device(adev, uc_info->out_snd_device);
 
     list_remove(&uc_info->list);
     free(uc_info);
 
+    audio_extn_extspk_update(adev->extspk);
+
     /* Must be called after removing the usecase from list */
     if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL)
         check_and_set_hdmi_channels(adev, DEFAULT_HDMI_OUT_CHANNELS);
@@ -934,21 +1051,40 @@
 
     select_devices(adev, out->usecase);
 
-    ALOGV("%s: Opening PCM device card_id(%d) device_id(%d)",
-          __func__, 0, out->pcm_device_id);
+    audio_extn_extspk_update(adev->extspk);
+
+    ALOGV("%s: Opening PCM device card_id(%d) device_id(%d) format(%#x)",
+          __func__, adev->snd_card, out->pcm_device_id, out->config.format);
     if (out->usecase != USECASE_AUDIO_PLAYBACK_OFFLOAD) {
-        out->pcm = pcm_open(SOUND_CARD, out->pcm_device_id,
-                               PCM_OUT | PCM_MONOTONIC, &out->config);
-        if (out->pcm && !pcm_is_ready(out->pcm)) {
-            ALOGE("%s: %s", __func__, pcm_get_error(out->pcm));
-            pcm_close(out->pcm);
-            out->pcm = NULL;
-            ret = -EIO;
-            goto error_open;
+        unsigned int flags = PCM_OUT;
+        unsigned int pcm_open_retry_count = 0;
+        if (out->usecase == USECASE_AUDIO_PLAYBACK_AFE_PROXY) {
+            flags |= PCM_MMAP | PCM_NOIRQ;
+            pcm_open_retry_count = PROXY_OPEN_RETRY_COUNT;
+        } else
+            flags |= PCM_MONOTONIC;
+
+        while (1) {
+            out->pcm = pcm_open(adev->snd_card, out->pcm_device_id,
+                               flags, &out->config);
+            if (out->pcm == NULL || !pcm_is_ready(out->pcm)) {
+                ALOGE("%s: %s", __func__, pcm_get_error(out->pcm));
+                if (out->pcm != NULL) {
+                    pcm_close(out->pcm);
+                    out->pcm = NULL;
+                }
+                if (pcm_open_retry_count-- == 0) {
+                    ret = -EIO;
+                    goto error_open;
+                }
+                usleep(PROXY_OPEN_WAIT_TIME * 1000);
+                continue;
+            }
+            break;
         }
     } else {
         out->pcm = NULL;
-        out->compr = compress_open(SOUND_CARD, out->pcm_device_id,
+        out->compr = compress_open(adev->snd_card, out->pcm_device_id,
                                    COMPRESS_IN, &out->compr_config);
         if (out->compr && !is_compress_ready(out->compr)) {
             ALOGE("%s: %s", __func__, compress_get_error(out->compr));
@@ -961,7 +1097,9 @@
             compress_nonblock(out->compr, out->non_blocking);
 
         if (adev->visualizer_start_output != NULL)
-            adev->visualizer_start_output(out->handle);
+            adev->visualizer_start_output(out->handle, out->pcm_device_id);
+        if (adev->offload_effects_start_output != NULL)
+            adev->offload_effects_start_output(out->handle, out->pcm_device_id);
     }
     ALOGV("%s: exit", __func__);
     return 0;
@@ -971,120 +1109,6 @@
     return ret;
 }
 
-static int stop_voice_call(struct audio_device *adev)
-{
-    int i, ret = 0;
-    struct audio_usecase *uc_info;
-
-    ALOGV("%s: enter", __func__);
-    adev->in_call = false;
-
-    ret = platform_stop_voice_call(adev->platform);
-
-    /* 1. Close the PCM devices */
-    if (adev->voice_call_rx) {
-        pcm_close(adev->voice_call_rx);
-        adev->voice_call_rx = NULL;
-    }
-    if (adev->voice_call_tx) {
-        pcm_close(adev->voice_call_tx);
-        adev->voice_call_tx = NULL;
-    }
-
-    uc_info = get_usecase_from_list(adev, USECASE_VOICE_CALL);
-    if (uc_info == NULL) {
-        ALOGE("%s: Could not find the usecase (%d) in the list",
-              __func__, USECASE_VOICE_CALL);
-        return -EINVAL;
-    }
-
-    /* 2. Get and set stream specific mixer controls */
-    disable_audio_route(adev, uc_info, true);
-
-    /* 3. Disable the rx and tx devices */
-    disable_snd_device(adev, uc_info->out_snd_device, false);
-    disable_snd_device(adev, uc_info->in_snd_device, true);
-
-    list_remove(&uc_info->list);
-    free(uc_info);
-
-    ALOGV("%s: exit: status(%d)", __func__, ret);
-    return ret;
-}
-
-static int start_voice_call(struct audio_device *adev)
-{
-    int i, ret = 0;
-    struct audio_usecase *uc_info;
-    int pcm_dev_rx_id, pcm_dev_tx_id;
-
-    ALOGV("%s: enter", __func__);
-
-    uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
-    uc_info->id = USECASE_VOICE_CALL;
-    uc_info->type = VOICE_CALL;
-    uc_info->stream.out = adev->primary_output;
-    uc_info->devices = adev->primary_output->devices;
-    uc_info->in_snd_device = SND_DEVICE_NONE;
-    uc_info->out_snd_device = SND_DEVICE_NONE;
-
-    list_add_tail(&adev->usecase_list, &uc_info->list);
-
-    select_devices(adev, USECASE_VOICE_CALL);
-
-    pcm_dev_rx_id = platform_get_pcm_device_id(uc_info->id, PCM_PLAYBACK);
-    pcm_dev_tx_id = platform_get_pcm_device_id(uc_info->id, PCM_CAPTURE);
-
-    if (pcm_dev_rx_id < 0 || pcm_dev_tx_id < 0) {
-        ALOGE("%s: Invalid PCM devices (rx: %d tx: %d) for the usecase(%d)",
-              __func__, pcm_dev_rx_id, pcm_dev_tx_id, uc_info->id);
-        ret = -EIO;
-        goto error_start_voice;
-    }
-
-    ALOGV("%s: Opening PCM playback device card_id(%d) device_id(%d)",
-          __func__, SOUND_CARD, pcm_dev_rx_id);
-    adev->voice_call_rx = pcm_open(SOUND_CARD,
-                                  pcm_dev_rx_id,
-                                  PCM_OUT | PCM_MONOTONIC, &pcm_config_voice_call);
-    if (adev->voice_call_rx && !pcm_is_ready(adev->voice_call_rx)) {
-        ALOGE("%s: %s", __func__, pcm_get_error(adev->voice_call_rx));
-        ret = -EIO;
-        goto error_start_voice;
-    }
-
-    ALOGV("%s: Opening PCM capture device card_id(%d) device_id(%d)",
-          __func__, SOUND_CARD, pcm_dev_tx_id);
-    adev->voice_call_tx = pcm_open(SOUND_CARD,
-                                   pcm_dev_tx_id,
-                                   PCM_IN, &pcm_config_voice_call);
-    if (adev->voice_call_tx && !pcm_is_ready(adev->voice_call_tx)) {
-        ALOGE("%s: %s", __func__, pcm_get_error(adev->voice_call_tx));
-        ret = -EIO;
-        goto error_start_voice;
-    }
-
-    /* set cached volume */
-    set_voice_volume_l(adev, adev->voice_volume);
-
-    pcm_start(adev->voice_call_rx);
-    pcm_start(adev->voice_call_tx);
-
-    ret = platform_start_voice_call(adev->platform);
-    if (ret < 0) {
-        ALOGE("%s: platform_start_voice_call error %d\n", __func__, ret);
-        goto error_start_voice;
-    }
-    adev->in_call = true;
-    return 0;
-
-error_start_voice:
-    stop_voice_call(adev);
-
-    ALOGD("%s: exit: status(%d)", __func__, ret);
-    return ret;
-}
-
 static int check_input_parameters(uint32_t sample_rate,
                                   audio_format_t format,
                                   int channel_count)
@@ -1113,7 +1137,8 @@
 
 static size_t get_input_buffer_size(uint32_t sample_rate,
                                     audio_format_t format,
-                                    int channel_count)
+                                    int channel_count,
+                                    bool is_low_latency)
 {
     size_t size = 0;
 
@@ -1121,13 +1146,19 @@
         return 0;
 
     size = (sample_rate * AUDIO_CAPTURE_PERIOD_DURATION_MSEC) / 1000;
+    if (is_low_latency)
+        size = configured_low_latency_capture_period_size;
     /* ToDo: should use frame_size computed based on the format and
        channel_count here. */
     size *= sizeof(short) * channel_count;
 
-    /* make sure the size is multiple of 64 */
-    size += 0x3f;
-    size &= ~0x3f;
+    /* make sure the size is multiple of 32 bytes
+     * At 48 kHz mono 16-bit PCM:
+     *  5.000 ms = 240 frames = 15*16*1*2 = 480, a whole multiple of 32 (15)
+     *  3.333 ms = 160 frames = 10*16*1*2 = 320, a whole multiple of 32 (10)
+     */
+    size += 0x1f;
+    size &= ~0x1f;
 
     return size;
 }
@@ -1139,7 +1170,7 @@
     return out->sample_rate;
 }
 
-static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate)
+static int out_set_sample_rate(struct audio_stream *stream __unused, uint32_t rate __unused)
 {
     return -ENOSYS;
 }
@@ -1151,8 +1182,8 @@
     if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD) {
         return out->compr_config.fragment_size;
     }
-
-    return out->config.period_size * audio_stream_frame_size(stream);
+    return out->config.period_size *
+                audio_stream_out_frame_size((const struct audio_stream_out *)stream);
 }
 
 static uint32_t out_get_channels(const struct audio_stream *stream)
@@ -1169,7 +1200,7 @@
     return out->format;
 }
 
-static int out_set_format(struct audio_stream *stream, audio_format_t format)
+static int out_set_format(struct audio_stream *stream __unused, audio_format_t format __unused)
 {
     return -ENOSYS;
 }
@@ -1208,7 +1239,7 @@
     return 0;
 }
 
-static int out_dump(const struct audio_stream *stream, int fd)
+static int out_dump(const struct audio_stream *stream __unused, int fd __unused)
 {
     return 0;
 }
@@ -1245,6 +1276,10 @@
     return 0;
 }
 
+static bool output_drives_call(struct audio_device *adev, struct stream_out *out)
+{
+    return out == adev->primary_output || out == adev->voice_tx_output;
+}
 
 static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
 {
@@ -1256,6 +1291,7 @@
     char value[32];
     int ret, val = 0;
     bool select_new_device = false;
+    int status = 0;
 
     ALOGD("%s: enter: usecase(%d: %s) kvpairs: %s",
           __func__, out->usecase, use_case_table[out->usecase], kvpairs);
@@ -1302,22 +1338,24 @@
             if (!out->standby)
                 select_devices(adev, out->usecase);
 
-            if ((adev->mode == AUDIO_MODE_IN_CALL) && !adev->in_call &&
-                    (out == adev->primary_output)) {
-                start_voice_call(adev);
-            } else if ((adev->mode == AUDIO_MODE_IN_CALL) && adev->in_call &&
-                       (out == adev->primary_output)) {
-                select_devices(adev, USECASE_VOICE_CALL);
+            if (output_drives_call(adev, out)) {
+                if (!voice_is_in_call(adev)) {
+                    if (adev->mode == AUDIO_MODE_IN_CALL) {
+                        adev->current_call_output = out;
+                        ret = voice_start_call(adev);
+                    }
+                } else {
+                    adev->current_call_output = out;
+                    voice_update_devices_for_all_voice_usecases(adev);
+                }
             }
         }
 
-        if ((adev->mode == AUDIO_MODE_NORMAL) && adev->in_call &&
-                (out == adev->primary_output)) {
-            stop_voice_call(adev);
-        }
-
         pthread_mutex_unlock(&adev->lock);
         pthread_mutex_unlock(&out->lock);
+
+        /*handles device and call state changes*/
+        audio_extn_extspk_update(adev->extspk);
     }
 
     if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD) {
@@ -1325,8 +1363,8 @@
     }
 
     str_parms_destroy(parms);
-    ALOGV("%s: exit: code(%d)", __func__, ret);
-    return ret;
+    ALOGV("%s: exit: code(%d)", __func__, status);
+    return status;
 }
 
 static char* out_get_parameters(const struct audio_stream *stream, const char *keys)
@@ -1393,12 +1431,19 @@
         const char *mixer_ctl_name = "Compress Playback Volume";
         struct audio_device *adev = out->dev;
         struct mixer_ctl *ctl;
-
         ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
         if (!ctl) {
-            ALOGE("%s: Could not get ctl for mixer cmd - %s",
-                  __func__, mixer_ctl_name);
-            return -EINVAL;
+            /* try with the control based on device id */
+            int pcm_device_id = platform_get_pcm_device_id(out->usecase,
+                                                       PCM_PLAYBACK);
+            char ctl_name[128] = {0};
+            snprintf(ctl_name, sizeof(ctl_name),
+                     "Compress Playback %d Volume", pcm_device_id);
+            ctl = mixer_get_ctl_by_name(adev->mixer, ctl_name);
+            if (!ctl) {
+                ALOGE("%s: Could not get volume ctl mixer cmd", __func__);
+                return -EINVAL;
+            }
         }
         volume[0] = (int)(left * COMPRESS_PLAYBACK_VOLUME_MAX);
         volume[1] = (int)(right * COMPRESS_PLAYBACK_VOLUME_MAX);
@@ -1454,7 +1499,11 @@
             if (out->muted)
                 memset((void *)buffer, 0, bytes);
             ALOGVV("%s: writing buffer (%d bytes) to pcm device", __func__, bytes);
-            ret = pcm_write(out->pcm, (void *)buffer, bytes);
+            if (out->usecase == USECASE_AUDIO_PLAYBACK_AFE_PROXY) {
+                ret = pcm_mmap_write(out->pcm, (void *)buffer, bytes);
+            }
+            else
+                ret = pcm_write(out->pcm, (void *)buffer, bytes);
             if (ret == 0)
                 out->written += bytes / (out->config.channels * sizeof(short));
         }
@@ -1467,7 +1516,7 @@
         if (out->pcm)
             ALOGE("%s: error %d - %s", __func__, ret, pcm_get_error(out->pcm));
         out_standby(&out->stream.common);
-        usleep(bytes * 1000000 / audio_stream_frame_size(&out->stream.common) /
+        usleep(bytes * 1000000 / audio_stream_out_frame_size(stream) /
                out_get_sample_rate(&out->stream.common));
     }
     return bytes;
@@ -1492,18 +1541,20 @@
         return -EINVAL;
 }
 
-static int out_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect)
+static int out_add_audio_effect(const struct audio_stream *stream __unused,
+                                effect_handle_t effect __unused)
 {
     return 0;
 }
 
-static int out_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect)
+static int out_remove_audio_effect(const struct audio_stream *stream __unused,
+                                   effect_handle_t effect __unused)
 {
     return 0;
 }
 
-static int out_get_next_write_timestamp(const struct audio_stream_out *stream,
-                                        int64_t *timestamp)
+static int out_get_next_write_timestamp(const struct audio_stream_out *stream __unused,
+                                        int64_t *timestamp __unused)
 {
     return -EINVAL;
 }
@@ -1636,7 +1687,7 @@
     return in->config.rate;
 }
 
-static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate)
+static int in_set_sample_rate(struct audio_stream *stream __unused, uint32_t rate __unused)
 {
     return -ENOSYS;
 }
@@ -1645,7 +1696,8 @@
 {
     struct stream_in *in = (struct stream_in *)stream;
 
-    return in->config.period_size * audio_stream_frame_size(stream);
+    return in->config.period_size *
+                audio_stream_in_frame_size((const struct audio_stream_in *)stream);
 }
 
 static uint32_t in_get_channels(const struct audio_stream *stream)
@@ -1655,12 +1707,12 @@
     return in->channel_mask;
 }
 
-static audio_format_t in_get_format(const struct audio_stream *stream)
+static audio_format_t in_get_format(const struct audio_stream *stream __unused)
 {
     return AUDIO_FORMAT_PCM_16_BIT;
 }
 
-static int in_set_format(struct audio_stream *stream, audio_format_t format)
+static int in_set_format(struct audio_stream *stream __unused, audio_format_t format __unused)
 {
     return -ENOSYS;
 }
@@ -1679,6 +1731,8 @@
             pcm_close(in->pcm);
             in->pcm = NULL;
         }
+        adev->enable_voicerx = false;
+        platform_set_echo_reference(adev, false, AUDIO_DEVICE_NONE );
         status = stop_input_stream(in);
         pthread_mutex_unlock(&adev->lock);
     }
@@ -1687,7 +1741,7 @@
     return status;
 }
 
-static int in_dump(const struct audio_stream *stream, int fd)
+static int in_dump(const struct audio_stream *stream __unused, int fd __unused)
 {
     return 0;
 }
@@ -1700,6 +1754,7 @@
     char *str;
     char value[32];
     int ret, val = 0;
+    int status = 0;
 
     ALOGV("%s: enter: kvpairs=%s", __func__, kvpairs);
     parms = str_parms_create_str(kvpairs);
@@ -1717,13 +1772,14 @@
     }
 
     ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value));
+
     if (ret >= 0) {
         val = atoi(value);
-        if ((in->device != val) && (val != 0)) {
+        if (((int)in->device != val) && (val != 0)) {
             in->device = val;
             /* If recording is in progress, change the tx device to new device */
             if (!in->standby)
-                ret = select_devices(adev, in->usecase);
+                status = select_devices(adev, in->usecase);
         }
     }
 
@@ -1731,17 +1787,17 @@
     pthread_mutex_unlock(&in->lock);
 
     str_parms_destroy(parms);
-    ALOGV("%s: exit: status(%d)", __func__, ret);
-    return ret;
+    ALOGV("%s: exit: status(%d)", __func__, status);
+    return status;
 }
 
-static char* in_get_parameters(const struct audio_stream *stream,
-                               const char *keys)
+static char* in_get_parameters(const struct audio_stream *stream __unused,
+                               const char *keys __unused)
 {
     return strdup("");
 }
 
-static int in_set_gain(struct audio_stream_in *stream, float gain)
+static int in_set_gain(struct audio_stream_in *stream __unused, float gain __unused)
 {
     return 0;
 }
@@ -1765,14 +1821,18 @@
     }
 
     if (in->pcm) {
-        ret = pcm_read(in->pcm, buffer, bytes);
+        if (in->usecase == USECASE_AUDIO_RECORD_AFE_PROXY) {
+            ret = pcm_mmap_read(in->pcm, buffer, bytes);
+        } else
+            ret = pcm_read(in->pcm, buffer, bytes);
     }
 
     /*
      * Instead of writing zeroes here, we could trust the hardware
      * to always provide zeroes when muted.
+     * No need to acquire adev->lock to read mic_muted here as we don't change its state.
      */
-    if (ret == 0 && adev->mic_mute)
+    if (ret == 0 && adev->mic_muted && in->usecase != USECASE_AUDIO_RECORD_AFE_PROXY)
         memset(buffer, 0, bytes);
 
 exit:
@@ -1781,13 +1841,13 @@
     if (ret != 0) {
         in_standby(&in->stream.common);
         ALOGV("%s: read failed - sleeping for buffer duration", __func__);
-        usleep(bytes * 1000000 / audio_stream_frame_size(&in->stream.common) /
+        usleep(bytes * 1000000 / audio_stream_in_frame_size(stream) /
                in_get_sample_rate(&in->stream.common));
     }
     return bytes;
 }
 
-static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream)
+static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream __unused)
 {
     return 0;
 }
@@ -1797,6 +1857,7 @@
                                    bool enable)
 {
     struct stream_in *in = (struct stream_in *)stream;
+    struct audio_device *adev = in->dev;
     int status = 0;
     effect_descriptor_t desc;
 
@@ -1810,6 +1871,24 @@
             in->enable_aec != enable &&
             (memcmp(&desc.type, FX_IID_AEC, sizeof(effect_uuid_t)) == 0)) {
         in->enable_aec = enable;
+        if (!enable)
+            platform_set_echo_reference(in->dev, enable, AUDIO_DEVICE_NONE);
+        adev->enable_voicerx = enable;
+        struct audio_usecase *usecase;
+        struct listnode *node;
+        list_for_each(node, &adev->usecase_list) {
+            usecase = node_to_item(node, struct audio_usecase, list);
+            if (usecase->type == PCM_PLAYBACK) {
+                select_devices(adev, usecase->id);
+            break;
+            }
+        }
+        if (!in->standby)
+            select_devices(in->dev, in->usecase);
+    }
+    if (in->enable_ns != enable &&
+            (memcmp(&desc.type, FX_IID_NS, sizeof(effect_uuid_t)) == 0)) {
+        in->enable_ns = enable;
         if (!in->standby)
             select_devices(in->dev, in->usecase);
     }
@@ -1838,7 +1917,8 @@
                                    audio_devices_t devices,
                                    audio_output_flags_t flags,
                                    struct audio_config *config,
-                                   struct audio_stream_out **stream_out)
+                                   struct audio_stream_out **stream_out,
+                                   const char *address __unused)
 {
     struct audio_device *adev = (struct audio_device *)dev;
     struct stream_out *out;
@@ -1881,12 +1961,8 @@
         out->usecase = USECASE_AUDIO_PLAYBACK_MULTI_CH;
         out->config = pcm_config_hdmi_multi;
         out->config.rate = config->sample_rate;
-        out->config.channels = popcount(out->channel_mask);
+        out->config.channels = audio_channel_count_from_out_mask(out->channel_mask);
         out->config.period_size = HDMI_MULTI_PERIOD_BYTES / (out->config.channels * 2);
-    } else if (out->flags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER) {
-        out->usecase = USECASE_AUDIO_PLAYBACK_DEEP_BUFFER;
-        out->config = pcm_config_deep_buffer;
-        out->sample_rate = out->config.rate;
     } else if (out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
         if (config->offload_info.version != AUDIO_INFO_INITIALIZER.version ||
             config->offload_info.size != AUDIO_INFO_INITIALIZER.size) {
@@ -1926,7 +2002,7 @@
         out->compr_config.codec->bit_rate =
                     config->offload_info.bit_rate;
         out->compr_config.codec->ch_in =
-                    popcount(config->channel_mask);
+                audio_channel_count_from_out_mask(config->channel_mask);
         out->compr_config.codec->ch_out = out->compr_config.codec->ch_in;
 
         if (flags & AUDIO_OUTPUT_FLAG_NON_BLOCKING)
@@ -1937,14 +2013,58 @@
         ALOGV("%s: offloaded output offload_info version %04x bit rate %d",
                 __func__, config->offload_info.version,
                 config->offload_info.bit_rate);
+    } else  if (out->devices == AUDIO_DEVICE_OUT_TELEPHONY_TX) {
+        if (config->sample_rate == 0)
+            config->sample_rate = AFE_PROXY_SAMPLING_RATE;
+        if (config->sample_rate != 48000 && config->sample_rate != 16000 &&
+                config->sample_rate != 8000) {
+            config->sample_rate = AFE_PROXY_SAMPLING_RATE;
+            ret = -EINVAL;
+            goto error_open;
+        }
+        out->sample_rate = config->sample_rate;
+        out->config.rate = config->sample_rate;
+        if (config->format == AUDIO_FORMAT_DEFAULT)
+            config->format = AUDIO_FORMAT_PCM_16_BIT;
+        if (config->format != AUDIO_FORMAT_PCM_16_BIT) {
+            config->format = AUDIO_FORMAT_PCM_16_BIT;
+            ret = -EINVAL;
+            goto error_open;
+        }
+        out->format = config->format;
+        out->usecase = USECASE_AUDIO_PLAYBACK_AFE_PROXY;
+        out->config = pcm_config_afe_proxy_playback;
+        adev->voice_tx_output = out;
     } else {
-        out->usecase = USECASE_AUDIO_PLAYBACK_LOW_LATENCY;
-        out->config = pcm_config_low_latency;
+        if (out->flags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER) {
+            out->usecase = USECASE_AUDIO_PLAYBACK_DEEP_BUFFER;
+            out->config = pcm_config_deep_buffer;
+        } else {
+            out->usecase = USECASE_AUDIO_PLAYBACK_LOW_LATENCY;
+            out->config = pcm_config_low_latency;
+        }
+        if (config->format != audio_format_from_pcm_format(out->config.format)) {
+            if (k_enable_extended_precision
+                    && pcm_params_format_test(adev->use_case_table[out->usecase],
+                            pcm_format_from_audio_format(config->format))) {
+                out->config.format = pcm_format_from_audio_format(config->format);
+                /* out->format already set to config->format */
+            } else {
+                /* deny the externally proposed config format
+                 * and use the one specified in audio_hw layer configuration.
+                 * Note: out->format is returned by out->stream.common.get_format()
+                 * and is used to set config->format in the code several lines below.
+                 */
+                out->format = audio_format_from_pcm_format(out->config.format);
+            }
+        }
         out->sample_rate = out->config.rate;
     }
+    ALOGV("%s: Usecase(%s) config->format %#x  out->config.format %#x\n",
+            __func__, use_case_table[out->usecase], config->format, out->config.format);
 
     if (flags & AUDIO_OUTPUT_FLAG_PRIMARY) {
-        if(adev->primary_output == NULL)
+        if (adev->primary_output == NULL)
             adev->primary_output = out;
         else {
             ALOGE("%s: Primary output is already opened", __func__);
@@ -2004,7 +2124,7 @@
     return ret;
 }
 
-static void adev_close_output_stream(struct audio_hw_device *dev,
+static void adev_close_output_stream(struct audio_hw_device *dev __unused,
                                      struct audio_stream_out *stream)
 {
     struct stream_out *out = (struct stream_out *)stream;
@@ -2018,6 +2138,10 @@
         if (out->compr_config.codec != NULL)
             free(out->compr_config.codec);
     }
+
+    if (adev->voice_tx_output == out)
+        adev->voice_tx_output = NULL;
+
     pthread_cond_destroy(&out->cond);
     pthread_mutex_destroy(&out->lock);
     free(stream);
@@ -2032,33 +2156,16 @@
     char value[32];
     int val;
     int ret;
+    int status = 0;
 
-    ALOGV("%s: enter: %s", __func__, kvpairs);
+    ALOGD("%s: enter: %s", __func__, kvpairs);
+
+    pthread_mutex_lock(&adev->lock);
 
     parms = str_parms_create_str(kvpairs);
-    ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_TTY_MODE, value, sizeof(value));
-    if (ret >= 0) {
-        int tty_mode;
-
-        if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_OFF) == 0)
-            tty_mode = TTY_MODE_OFF;
-        else if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_VCO) == 0)
-            tty_mode = TTY_MODE_VCO;
-        else if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_HCO) == 0)
-            tty_mode = TTY_MODE_HCO;
-        else if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_FULL) == 0)
-            tty_mode = TTY_MODE_FULL;
-        else
-            return -EINVAL;
-
-        pthread_mutex_lock(&adev->lock);
-        if (tty_mode != adev->tty_mode) {
-            adev->tty_mode = tty_mode;
-            adev->acdb_settings = (adev->acdb_settings & TTY_MODE_CLEAR) | tty_mode;
-            if (adev->in_call)
-                select_devices(adev, USECASE_VOICE_CALL);
-        }
-        pthread_mutex_unlock(&adev->lock);
+    status = voice_set_parameters(adev, parms);
+    if (status != 0) {
+        goto done;
     }
 
     ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_BT_NREC, value, sizeof(value));
@@ -2096,93 +2203,94 @@
             break;
         default:
             ALOGE("%s: unexpected rotation of %d", __func__, val);
+            status = -EINVAL;
         }
-        pthread_mutex_lock(&adev->lock);
-        if (adev->speaker_lr_swap != reverse_speakers) {
-            adev->speaker_lr_swap = reverse_speakers;
-            // only update the selected device if there is active pcm playback
-            struct audio_usecase *usecase;
-            struct listnode *node;
-            list_for_each(node, &adev->usecase_list) {
-                usecase = node_to_item(node, struct audio_usecase, list);
-                if (usecase->type == PCM_PLAYBACK) {
-                    select_devices(adev, usecase->id);
-                    break;
+        if (status == 0) {
+            if (adev->speaker_lr_swap != reverse_speakers) {
+                adev->speaker_lr_swap = reverse_speakers;
+                // only update the selected device if there is active pcm playback
+                struct audio_usecase *usecase;
+                struct listnode *node;
+                list_for_each(node, &adev->usecase_list) {
+                    usecase = node_to_item(node, struct audio_usecase, list);
+                    if (usecase->type == PCM_PLAYBACK) {
+                        select_devices(adev, usecase->id);
+                        break;
+                    }
                 }
             }
         }
-        pthread_mutex_unlock(&adev->lock);
     }
 
+    ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_BT_SCO_WB, value, sizeof(value));
+    if (ret >= 0) {
+        adev->bt_wb_speech_enabled = !strcmp(value, AUDIO_PARAMETER_VALUE_ON);
+    }
+
+    audio_extn_hfp_set_parameters(adev, parms);
+done:
     str_parms_destroy(parms);
-    ALOGV("%s: exit with code(%d)", __func__, ret);
-    return ret;
+    pthread_mutex_unlock(&adev->lock);
+    ALOGV("%s: exit with code(%d)", __func__, status);
+    return status;
 }
 
 static char* adev_get_parameters(const struct audio_hw_device *dev,
                                  const char *keys)
 {
-    return strdup("");
+    struct audio_device *adev = (struct audio_device *)dev;
+    struct str_parms *reply = str_parms_create();
+    struct str_parms *query = str_parms_create_str(keys);
+    char *str;
+
+    pthread_mutex_lock(&adev->lock);
+
+    voice_get_parameters(adev, query, reply);
+    str = str_parms_to_str(reply);
+    str_parms_destroy(query);
+    str_parms_destroy(reply);
+
+    pthread_mutex_unlock(&adev->lock);
+    ALOGV("%s: exit: returns - %s", __func__, str);
+    return str;
 }
 
-static int adev_init_check(const struct audio_hw_device *dev)
+static int adev_init_check(const struct audio_hw_device *dev __unused)
 {
     return 0;
 }
 
-/* always called with adev lock held */
-static int set_voice_volume_l(struct audio_device *adev, float volume)
-{
-    int vol, err = 0;
-
-    if (adev->mode == AUDIO_MODE_IN_CALL) {
-        if (volume < 0.0) {
-            volume = 0.0;
-        } else if (volume > 1.0) {
-            volume = 1.0;
-        }
-
-        vol = lrint(volume * 100.0);
-
-        // Voice volume levels from android are mapped to driver volume levels as follows.
-        // 0 -> 5, 20 -> 4, 40 ->3, 60 -> 2, 80 -> 1, 100 -> 0
-        // So adjust the volume to get the correct volume index in driver
-        vol = 100 - vol;
-
-        err = platform_set_voice_volume(adev->platform, vol);
-    }
-    return err;
-}
-
 static int adev_set_voice_volume(struct audio_hw_device *dev, float volume)
 {
     int ret;
     struct audio_device *adev = (struct audio_device *)dev;
+
+    audio_extn_extspk_set_voice_vol(adev->extspk, volume);
+
     pthread_mutex_lock(&adev->lock);
-    /* cache volume */
-    adev->voice_volume = volume;
-    ret = set_voice_volume_l(adev, adev->voice_volume);
+    ret = voice_set_volume(adev, volume);
     pthread_mutex_unlock(&adev->lock);
+
     return ret;
 }
 
-static int adev_set_master_volume(struct audio_hw_device *dev, float volume)
+static int adev_set_master_volume(struct audio_hw_device *dev __unused, float volume __unused)
 {
     return -ENOSYS;
 }
 
-static int adev_get_master_volume(struct audio_hw_device *dev,
-                                  float *volume)
+static int adev_get_master_volume(struct audio_hw_device *dev __unused,
+                                  float *volume __unused)
 {
     return -ENOSYS;
 }
 
-static int adev_set_master_mute(struct audio_hw_device *dev, bool muted)
+static int adev_set_master_mute(struct audio_hw_device *dev __unused, bool muted __unused)
 {
     return -ENOSYS;
 }
 
-static int adev_get_master_mute(struct audio_hw_device *dev, bool *muted)
+static int adev_get_master_mute(struct audio_hw_device *dev __unused, bool *muted __unused)
 {
     return -ENOSYS;
 }
@@ -2193,52 +2301,64 @@
 
     pthread_mutex_lock(&adev->lock);
     if (adev->mode != mode) {
+        ALOGD("%s: mode %d\n", __func__, mode);
         adev->mode = mode;
+        if ((mode == AUDIO_MODE_NORMAL || mode == AUDIO_MODE_IN_COMMUNICATION) &&
+                voice_is_in_call(adev)) {
+            voice_stop_call(adev);
+            adev->current_call_output = NULL;
+        }
     }
     pthread_mutex_unlock(&adev->lock);
+
+    audio_extn_extspk_set_mode(adev->extspk, mode);
+
     return 0;
 }
 
 static int adev_set_mic_mute(struct audio_hw_device *dev, bool state)
 {
+    int ret;
     struct audio_device *adev = (struct audio_device *)dev;
-    int err = 0;
 
+    ALOGD("%s: state %d\n", __func__, state);
     pthread_mutex_lock(&adev->lock);
-    adev->mic_mute = state;
-
-    err = platform_set_mic_mute(adev->platform, state);
+    ret = voice_set_mic_mute(adev, state);
+    adev->mic_muted = state;
     pthread_mutex_unlock(&adev->lock);
-    return err;
+
+    return ret;
 }
 
 static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state)
 {
-    struct audio_device *adev = (struct audio_device *)dev;
-
-    *state = adev->mic_mute;
-
+    *state = voice_get_mic_mute((struct audio_device *)dev);
     return 0;
 }
 
-static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev,
+static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev __unused,
                                          const struct audio_config *config)
 {
-    int channel_count = popcount(config->channel_mask);
+    int channel_count = audio_channel_count_from_in_mask(config->channel_mask);
 
-    return get_input_buffer_size(config->sample_rate, config->format, channel_count);
+    return get_input_buffer_size(config->sample_rate, config->format, channel_count,
+            false /* is_low_latency: since we don't know, be conservative */);
 }
 
 static int adev_open_input_stream(struct audio_hw_device *dev,
-                                  audio_io_handle_t handle,
+                                  audio_io_handle_t handle __unused,
                                   audio_devices_t devices,
                                   struct audio_config *config,
-                                  struct audio_stream_in **stream_in)
+                                  struct audio_stream_in **stream_in,
+                                  audio_input_flags_t flags,
+                                  const char *address __unused,
+                                  audio_source_t source )
 {
     struct audio_device *adev = (struct audio_device *)dev;
     struct stream_in *in;
-    int ret, buffer_size, frame_size;
-    int channel_count = popcount(config->channel_mask);
+    int ret = 0, buffer_size, frame_size;
+    int channel_count = audio_channel_count_from_in_mask(config->channel_mask);
+    bool is_low_latency = false;
 
     ALOGV("%s: enter", __func__);
     *stream_in = NULL;
@@ -2247,6 +2367,8 @@
 
     in = (struct stream_in *)calloc(1, sizeof(struct stream_in));
 
+    pthread_mutex_init(&in->lock, (const pthread_mutexattr_t *) NULL);
+
     in->stream.common.get_sample_rate = in_get_sample_rate;
     in->stream.common.set_sample_rate = in_set_sample_rate;
     in->stream.common.get_buffer_size = in_get_buffer_size;
@@ -2264,22 +2386,52 @@
     in->stream.get_input_frames_lost = in_get_input_frames_lost;
 
     in->device = devices;
-    in->source = AUDIO_SOURCE_DEFAULT;
+    in->source = source;
     in->dev = adev;
     in->standby = 1;
     in->channel_mask = config->channel_mask;
 
     /* Update config params with the requested sample rate and channels */
-    in->usecase = USECASE_AUDIO_RECORD;
-    in->config = pcm_config_audio_capture;
+    if (in->device == AUDIO_DEVICE_IN_TELEPHONY_RX) {
+        if (config->sample_rate == 0)
+            config->sample_rate = AFE_PROXY_SAMPLING_RATE;
+        if (config->sample_rate != 48000 && config->sample_rate != 16000 &&
+                config->sample_rate != 8000) {
+            config->sample_rate = AFE_PROXY_SAMPLING_RATE;
+            ret = -EINVAL;
+            goto err_open;
+        }
+        if (config->format == AUDIO_FORMAT_DEFAULT)
+            config->format = AUDIO_FORMAT_PCM_16_BIT;
+        if (config->format != AUDIO_FORMAT_PCM_16_BIT) {
+            config->format = AUDIO_FORMAT_PCM_16_BIT;
+            ret = -EINVAL;
+            goto err_open;
+        }
+
+        in->usecase = USECASE_AUDIO_RECORD_AFE_PROXY;
+        in->config = pcm_config_afe_proxy_record;
+    } else {
+        in->usecase = USECASE_AUDIO_RECORD;
+        if (config->sample_rate == LOW_LATENCY_CAPTURE_SAMPLE_RATE &&
+                (flags & AUDIO_INPUT_FLAG_FAST) != 0) {
+            is_low_latency = true;
+#if LOW_LATENCY_CAPTURE_USE_CASE
+            in->usecase = USECASE_AUDIO_RECORD_LOW_LATENCY;
+#endif
+        }
+        in->config = pcm_config_audio_capture;
+
+        frame_size = audio_stream_in_frame_size(&in->stream);
+        buffer_size = get_input_buffer_size(config->sample_rate,
+                                            config->format,
+                                            channel_count,
+                                            is_low_latency);
+        in->config.period_size = buffer_size / frame_size;
+    }
     in->config.channels = channel_count;
     in->config.rate = config->sample_rate;
 
-    frame_size = audio_stream_frame_size((struct audio_stream *)in);
-    buffer_size = get_input_buffer_size(config->sample_rate,
-                                        config->format,
-                                        channel_count);
-    in->config.period_size = buffer_size / frame_size;
 
     *stream_in = &in->stream;
     ALOGV("%s: exit", __func__);
@@ -2291,7 +2443,7 @@
     return ret;
 }
 
-static void adev_close_input_stream(struct audio_hw_device *dev,
+static void adev_close_input_stream(struct audio_hw_device *dev __unused,
                                     struct audio_stream_in *stream)
 {
     ALOGV("%s", __func__);
@@ -2302,21 +2454,177 @@
     return;
 }
 
-static int adev_dump(const audio_hw_device_t *device, int fd)
+static int adev_dump(const audio_hw_device_t *device __unused, int fd __unused)
 {
     return 0;
 }
 
+/* verifies input and output devices and their capabilities.
+ *
+ * This verification is required when enabling extended bit-depth or
+ * sampling rates, as not all qcom products support it.
+ *
+ * Suitable for calling only on initialization such as adev_open().
+ * It fills the audio_device use_case_table[] array.
+ *
+ * Has a side-effect that it needs to configure audio routing / devices
+ * in order to power up the devices and read the device parameters.
+ * It does not acquire any hw device lock. Should restore the devices
+ * back to "normal state" upon completion.
+ */
+static int adev_verify_devices(struct audio_device *adev)
+{
+    /* enumeration is a bit difficult because one really wants to pull
+     * the use_case, device id, etc from the hidden pcm_device_table[].
+     * In this case there are the following use cases and device ids.
+     *
+     * [USECASE_AUDIO_PLAYBACK_DEEP_BUFFER] = {0, 0},
+     * [USECASE_AUDIO_PLAYBACK_LOW_LATENCY] = {15, 15},
+     * [USECASE_AUDIO_PLAYBACK_MULTI_CH] = {1, 1},
+     * [USECASE_AUDIO_PLAYBACK_OFFLOAD] = {9, 9},
+     * [USECASE_AUDIO_RECORD] = {0, 0},
+     * [USECASE_AUDIO_RECORD_LOW_LATENCY] = {15, 15},
+     * [USECASE_VOICE_CALL] = {2, 2},
+     *
+     * USECASE_AUDIO_PLAYBACK_OFFLOAD, USECASE_AUDIO_PLAYBACK_MULTI_CH omitted.
+     * USECASE_VOICE_CALL omitted, but possible for either input or output.
+     */
+
+    /* should be the usecases enabled in adev_open_input_stream() */
+    static const int test_in_usecases[] = {
+             USECASE_AUDIO_RECORD,
+             USECASE_AUDIO_RECORD_LOW_LATENCY, /* does not appear to be used */
+    };
+    /* should be the usecases enabled in adev_open_output_stream()*/
+    static const int test_out_usecases[] = {
+            USECASE_AUDIO_PLAYBACK_DEEP_BUFFER,
+            USECASE_AUDIO_PLAYBACK_LOW_LATENCY,
+    };
+    static const usecase_type_t usecase_type_by_dir[] = {
+            PCM_PLAYBACK,
+            PCM_CAPTURE,
+    };
+    static const unsigned flags_by_dir[] = {
+            PCM_OUT,
+            PCM_IN,
+    };
+
+    size_t i;
+    unsigned dir;
+    const unsigned card_id = adev->snd_card;
+    char info[512]; /* for possible debug info */
+
+    for (dir = 0; dir < 2; ++dir) {
+        const usecase_type_t usecase_type = usecase_type_by_dir[dir];
+        const unsigned flags_dir = flags_by_dir[dir];
+        const size_t testsize =
+                dir ? ARRAY_SIZE(test_in_usecases) : ARRAY_SIZE(test_out_usecases);
+        const int *testcases =
+                dir ? test_in_usecases : test_out_usecases;
+        const audio_devices_t audio_device =
+                dir ? AUDIO_DEVICE_IN_BUILTIN_MIC : AUDIO_DEVICE_OUT_SPEAKER;
+
+        for (i = 0; i < testsize; ++i) {
+            const audio_usecase_t audio_usecase = testcases[i];
+            int device_id;
+            snd_device_t snd_device;
+            struct pcm_params **pparams;
+            struct stream_out out;
+            struct stream_in in;
+            struct audio_usecase uc_info;
+            int retval;
+
+            pparams = &adev->use_case_table[audio_usecase];
+            pcm_params_free(*pparams); /* can accept null input */
+            *pparams = NULL;
+
+            /* find the device ID for the use case (signed, for error) */
+            device_id = platform_get_pcm_device_id(audio_usecase, usecase_type);
+            if (device_id < 0)
+                continue;
+
+            /* prepare structures for device probing */
+            memset(&uc_info, 0, sizeof(uc_info));
+            uc_info.id = audio_usecase;
+            uc_info.type = usecase_type;
+            if (dir) {
+                adev->active_input = &in;
+                memset(&in, 0, sizeof(in));
+                in.device = audio_device;
+                in.source = AUDIO_SOURCE_VOICE_COMMUNICATION;
+                uc_info.stream.in = &in;
+            }  else {
+                adev->active_input = NULL;
+            }
+            memset(&out, 0, sizeof(out));
+            out.devices = audio_device; /* only field needed in select_devices */
+            uc_info.stream.out = &out;
+            uc_info.devices = audio_device;
+            uc_info.in_snd_device = SND_DEVICE_NONE;
+            uc_info.out_snd_device = SND_DEVICE_NONE;
+            list_add_tail(&adev->usecase_list, &uc_info.list);
+
+            /* select device - similar to start_(in/out)put_stream() */
+            retval = select_devices(adev, audio_usecase);
+            if (retval >= 0) {
+                *pparams = pcm_params_get(card_id, device_id, flags_dir);
+#if LOG_NDEBUG == 0
+                if (*pparams) {
+                    ALOGV("%s: (%s) card %d  device %d", __func__,
+                            dir ? "input" : "output", card_id, device_id);
+                    pcm_params_to_string(*pparams, info, ARRAY_SIZE(info));
+                    ALOGV(info); /* print parameters */
+                } else {
+                    ALOGV("%s: cannot locate card %d  device %d", __func__, card_id, device_id);
+                }
+#endif
+            }
+
+            /* deselect device - similar to stop_(in/out)put_stream() */
+            /* 1. Get and set stream specific mixer controls */
+            retval = disable_audio_route(adev, &uc_info);
+            /* 2. Disable the rx device */
+            retval = disable_snd_device(adev,
+                    dir ? uc_info.in_snd_device : uc_info.out_snd_device);
+            list_remove(&uc_info.list);
+        }
+    }
+    adev->active_input = NULL; /* restore adev state */
+    return 0;
+}
+
 static int adev_close(hw_device_t *device)
 {
+    size_t i;
     struct audio_device *adev = (struct audio_device *)device;
     audio_route_free(adev->audio_route);
     free(adev->snd_dev_ref_cnt);
     platform_deinit(adev->platform);
+    audio_extn_extspk_deinit(adev->extspk);
+    for (i = 0; i < ARRAY_SIZE(adev->use_case_table); ++i) {
+        pcm_params_free(adev->use_case_table[i]);
+    }
     free(device);
     return 0;
 }
 
+/* This returns 1 if the input parameter looks at all plausible as a low latency period size,
+ * or 0 otherwise.  A return value of 1 doesn't mean the value is guaranteed to work,
+ * just that it _might_ work.
+ */
+static int period_size_is_plausible_for_low_latency(int period_size)
+{
+    switch (period_size) {
+    case 160:
+    case 240:
+    case 320:
+    case 480:
+        return 1;
+    default:
+        return 0;
+    }
+}
+
 static int adev_open(const hw_module_t *module, const char *name,
                      hw_device_t **device)
 {
@@ -2328,6 +2636,8 @@
 
     adev = calloc(1, sizeof(struct audio_device));
 
+    pthread_mutex_init(&adev->lock, (const pthread_mutexattr_t *) NULL);
+
     adev->device.common.tag = HARDWARE_DEVICE_TAG;
     adev->device.common.version = AUDIO_DEVICE_API_VERSION_2_0;
     adev->device.common.module = (struct hw_module_t *)module;
@@ -2356,16 +2666,11 @@
     adev->mode = AUDIO_MODE_NORMAL;
     adev->active_input = NULL;
     adev->primary_output = NULL;
-    adev->out_device = AUDIO_DEVICE_NONE;
-    adev->voice_call_rx = NULL;
-    adev->voice_call_tx = NULL;
-    adev->voice_volume = 1.0f;
-    adev->tty_mode = TTY_MODE_OFF;
     adev->bluetooth_nrec = true;
-    adev->in_call = false;
     adev->acdb_settings = TTY_MODE_OFF;
     /* adev->cur_hdmi_channels = 0;  by calloc() */
     adev->snd_dev_ref_cnt = calloc(SND_DEVICE_MAX, sizeof(int));
+    voice_init(adev);
     list_init(&adev->usecase_list);
     pthread_mutex_unlock(&adev->lock);
 
@@ -2379,6 +2684,8 @@
         return -EINVAL;
     }
 
+    adev->extspk = audio_extn_extspk_init(adev);
+
     if (access(VISUALIZER_LIBRARY_PATH, R_OK) == 0) {
         adev->visualizer_lib = dlopen(VISUALIZER_LIBRARY_PATH, RTLD_NOW);
         if (adev->visualizer_lib == NULL) {
@@ -2386,15 +2693,55 @@
         } else {
             ALOGV("%s: DLOPEN successful for %s", __func__, VISUALIZER_LIBRARY_PATH);
             adev->visualizer_start_output =
-                        (int (*)(audio_io_handle_t))dlsym(adev->visualizer_lib,
+                        (int (*)(audio_io_handle_t, int))dlsym(adev->visualizer_lib,
                                                         "visualizer_hal_start_output");
             adev->visualizer_stop_output =
-                        (int (*)(audio_io_handle_t))dlsym(adev->visualizer_lib,
+                        (int (*)(audio_io_handle_t, int))dlsym(adev->visualizer_lib,
                                                         "visualizer_hal_stop_output");
         }
     }
 
+    if (access(OFFLOAD_EFFECTS_BUNDLE_LIBRARY_PATH, R_OK) == 0) {
+        adev->offload_effects_lib = dlopen(OFFLOAD_EFFECTS_BUNDLE_LIBRARY_PATH, RTLD_NOW);
+        if (adev->offload_effects_lib == NULL) {
+            ALOGE("%s: DLOPEN failed for %s", __func__,
+                  OFFLOAD_EFFECTS_BUNDLE_LIBRARY_PATH);
+        } else {
+            ALOGV("%s: DLOPEN successful for %s", __func__,
+                  OFFLOAD_EFFECTS_BUNDLE_LIBRARY_PATH);
+            adev->offload_effects_start_output =
+                        (int (*)(audio_io_handle_t, int))dlsym(adev->offload_effects_lib,
+                                         "offload_effects_bundle_hal_start_output");
+            adev->offload_effects_stop_output =
+                        (int (*)(audio_io_handle_t, int))dlsym(adev->offload_effects_lib,
+                                         "offload_effects_bundle_hal_stop_output");
+        }
+    }
+
+    adev->bt_wb_speech_enabled = false;
+    adev->enable_voicerx = false;
+
     *device = &adev->device.common;
+    if (k_enable_extended_precision)
+        adev_verify_devices(adev);
+
+    char value[PROPERTY_VALUE_MAX];
+    int trial;
+    if (property_get("audio_hal.period_size", value, NULL) > 0) {
+        trial = atoi(value);
+        if (period_size_is_plausible_for_low_latency(trial)) {
+            pcm_config_low_latency.period_size = trial;
+            pcm_config_low_latency.start_threshold = trial / 4;
+            pcm_config_low_latency.avail_min = trial / 4;
+            configured_low_latency_capture_period_size = trial;
+        }
+    }
+    if (property_get("audio_hal.in_period_size", value, NULL) > 0) {
+        trial = atoi(value);
+        if (period_size_is_plausible_for_low_latency(trial)) {
+            configured_low_latency_capture_period_size = trial;
+        }
+    }
 
     ALOGV("%s: exit", __func__);
     return 0;
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index 0da4324..bec19d0 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2013-2014 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
 #ifndef QCOM_AUDIO_HW_H
 #define QCOM_AUDIO_HW_H
 
+#include <cutils/str_parms.h>
 #include <cutils/list.h>
 #include <hardware/audio.h>
 
@@ -24,8 +25,10 @@
 #include <tinycompress/tinycompress.h>
 
 #include <audio_route/audio_route.h>
+#include "voice.h"
 
 #define VISUALIZER_LIBRARY_PATH "/system/lib/soundfx/libqcomvisualizer.so"
+#define OFFLOAD_EFFECTS_BUNDLE_LIBRARY_PATH "/system/lib/soundfx/libqcompostprocbundle.so"
 
 /* Flags used to initialize acdb_settings variable that goes to ACDB library */
 #define DMIC_FLAG       0x00000002
@@ -47,7 +50,7 @@
  * Each usecase is mapped to a specific PCM device.
  * Refer to pcm_device_table[].
  */
-typedef enum {
+enum {
     USECASE_INVALID = -1,
     /* Playback usecases */
     USECASE_AUDIO_PLAYBACK_DEEP_BUFFER = 0,
@@ -55,13 +58,32 @@
     USECASE_AUDIO_PLAYBACK_MULTI_CH,
     USECASE_AUDIO_PLAYBACK_OFFLOAD,
 
+    /* HFP Use case*/
+    USECASE_AUDIO_HFP_SCO,
+    USECASE_AUDIO_HFP_SCO_WB,
+
     /* Capture usecases */
     USECASE_AUDIO_RECORD,
     USECASE_AUDIO_RECORD_LOW_LATENCY,
 
     USECASE_VOICE_CALL,
+
+    /* Voice extension usecases */
+    USECASE_VOICE2_CALL,
+    USECASE_VOLTE_CALL,
+    USECASE_QCHAT_CALL,
+    USECASE_VOWLAN_CALL,
+    USECASE_INCALL_REC_UPLINK,
+    USECASE_INCALL_REC_DOWNLINK,
+    USECASE_INCALL_REC_UPLINK_AND_DOWNLINK,
+
+    USECASE_AUDIO_PLAYBACK_AFE_PROXY,
+    USECASE_AUDIO_RECORD_AFE_PROXY,
+
     AUDIO_USECASE_MAX
-} audio_usecase_t;
+};
+
+const char * const use_case_table[AUDIO_USECASE_MAX];
 
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
 
@@ -139,10 +161,11 @@
     int standby;
     int source;
     int pcm_device_id;
-    int device;
+    audio_devices_t device;
     audio_channel_mask_t channel_mask;
     audio_usecase_t usecase;
     bool enable_aec;
+    bool enable_ns;
 
     struct audio_device *dev;
 };
@@ -150,7 +173,8 @@
 typedef enum {
     PCM_PLAYBACK,
     PCM_CAPTURE,
-    VOICE_CALL
+    VOICE_CALL,
+    PCM_HFP_CALL
 } usecase_type_t;
 
 union stream_ptr {
@@ -173,31 +197,68 @@
     pthread_mutex_t lock; /* see note below on mutex acquisition order */
     struct mixer *mixer;
     audio_mode_t mode;
-    audio_devices_t out_device;
     struct stream_in *active_input;
     struct stream_out *primary_output;
-    int in_call;
-    float voice_volume;
-    bool mic_mute;
-    int tty_mode;
+    struct stream_out *voice_tx_output;
+    struct stream_out *current_call_output;
     bool bluetooth_nrec;
     bool screen_off;
-    struct pcm *voice_call_rx;
-    struct pcm *voice_call_tx;
     int *snd_dev_ref_cnt;
     struct listnode usecase_list;
     struct audio_route *audio_route;
     int acdb_settings;
     bool speaker_lr_swap;
+    struct voice voice;
     unsigned int cur_hdmi_channels;
+    bool bt_wb_speech_enabled;
+    bool mic_muted;
+    bool enable_voicerx;
 
+    int snd_card;
     void *platform;
+    void *extspk;
 
     void *visualizer_lib;
-    int (*visualizer_start_output)(audio_io_handle_t);
-    int (*visualizer_stop_output)(audio_io_handle_t);
+    int (*visualizer_start_output)(audio_io_handle_t, int);
+    int (*visualizer_stop_output)(audio_io_handle_t, int);
+
+    /* The pcm_params use_case_table is loaded by adev_verify_devices() upon
+     * calling adev_open().
+     *
+     * If an entry is not NULL, it can be used to determine if extended precision
+     * or other capabilities are present for the device corresponding to that usecase.
+     */
+    struct pcm_params *use_case_table[AUDIO_USECASE_MAX];
+    void *offload_effects_lib;
+    int (*offload_effects_start_output)(audio_io_handle_t, int);
+    int (*offload_effects_stop_output)(audio_io_handle_t, int);
 };
 
+int pcm_ioctl(void *pcm, int request, ...);
+
+int select_devices(struct audio_device *adev,
+                   audio_usecase_t uc_id);
+
+int disable_audio_route(struct audio_device *adev,
+                        struct audio_usecase *usecase);
+
+int disable_snd_device(struct audio_device *adev,
+                       snd_device_t snd_device);
+
+int enable_snd_device(struct audio_device *adev,
+                      snd_device_t snd_device);
+
+int enable_audio_route(struct audio_device *adev,
+                       struct audio_usecase *usecase);
+
+struct audio_usecase *get_usecase_from_list(struct audio_device *adev,
+                                            audio_usecase_t uc_id);
+
+#define LITERAL_TO_STRING(x) #x
+#define CHECK(condition) LOG_ALWAYS_FATAL_IF(!(condition), "%s",\
+            __FILE__ ":" LITERAL_TO_STRING(__LINE__)\
+            " ASSERT_FATAL(" #condition ") failed.")
+
 /*
  * NOTE: when multiple mutexes have to be acquired, always take the
  * stream_in or stream_out mutex first, followed by the audio_device mutex.
diff --git a/hal/msm8960/platform.c b/hal/msm8960/platform.c
index b200e27..4eaf488 100644
--- a/hal/msm8960/platform.c
+++ b/hal/msm8960/platform.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2013-2014 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -123,6 +123,7 @@
     [SND_DEVICE_OUT_HDMI] = "hdmi",
     [SND_DEVICE_OUT_SPEAKER_AND_HDMI] = "speaker-and-hdmi",
     [SND_DEVICE_OUT_BT_SCO] = "bt-sco-headset",
+    [SND_DEVICE_OUT_BT_SCO_WB] = "bt-sco-headset-wb",
     [SND_DEVICE_OUT_VOICE_HANDSET_TMUS] = "voice-handset-tmus",
     [SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES] = "voice-tty-full-headphones",
     [SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = "voice-tty-vco-headphones",
@@ -139,6 +140,7 @@
     [SND_DEVICE_IN_VOICE_HEADSET_MIC] = "voice-headset-mic",
     [SND_DEVICE_IN_HDMI_MIC] = "hdmi-mic",
     [SND_DEVICE_IN_BT_SCO_MIC] = "bt-sco-mic",
+    [SND_DEVICE_IN_BT_SCO_MIC_WB] = "bt-sco-mic-wb",
     [SND_DEVICE_IN_CAMCORDER_MIC] = "camcorder-mic",
     [SND_DEVICE_IN_VOICE_DMIC_EF] = "voice-dmic-ef",
     [SND_DEVICE_IN_VOICE_DMIC_BS] = "voice-dmic-bs",
@@ -168,6 +170,7 @@
     [SND_DEVICE_OUT_HDMI] = 18,
     [SND_DEVICE_OUT_SPEAKER_AND_HDMI] = 14,
     [SND_DEVICE_OUT_BT_SCO] = 22,
+    [SND_DEVICE_OUT_BT_SCO_WB] = 39,
     [SND_DEVICE_OUT_VOICE_HANDSET_TMUS] = 81,
     [SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES] = 17,
     [SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = 17,
@@ -183,6 +186,7 @@
     [SND_DEVICE_IN_VOICE_HEADSET_MIC] = 8,
     [SND_DEVICE_IN_HDMI_MIC] = 4,
     [SND_DEVICE_IN_BT_SCO_MIC] = 21,
+    [SND_DEVICE_IN_BT_SCO_MIC_WB] = 38,
     [SND_DEVICE_IN_CAMCORDER_MIC] = 61,
     [SND_DEVICE_IN_VOICE_DMIC_EF] = 6,
     [SND_DEVICE_IN_VOICE_DMIC_BS] = 5,
@@ -372,10 +376,11 @@
     if (snd_device >= SND_DEVICE_MIN && snd_device < SND_DEVICE_MAX)
         return device_table[snd_device];
     else
-        return "";
+        return "none";
 }
 
-void platform_add_backend_name(char *mixer_path, snd_device_t snd_device)
+void platform_add_backend_name(void *platform __unused, char *mixer_path,
+                               snd_device_t snd_device)
 {
     if (snd_device == SND_DEVICE_IN_BT_SCO_MIC)
         strcat(mixer_path, " bt-sco");
@@ -385,6 +390,9 @@
         strcat(mixer_path, " hdmi");
     else if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_HDMI)
         strcat(mixer_path, " speaker-and-hdmi");
+    else if (snd_device == SND_DEVICE_OUT_BT_SCO_WB ||
+             snd_device == SND_DEVICE_IN_BT_SCO_MIC_WB)
+        strcat(mixer_path, " bt-sco-wb");
 }
 
 int platform_get_pcm_device_id(audio_usecase_t usecase, int device_type)
@@ -397,6 +405,17 @@
     return device_id;
 }
 
+int platform_get_snd_device_index(char *snd_device_index_name __unused)
+{
+    return -ENODEV;
+}
+
+int platform_set_snd_device_acdb_id(snd_device_t snd_device __unused,
+                                    unsigned int acdb_id __unused)
+{
+    return -ENODEV;
+}
+
 int platform_send_audio_calibration(void *platform, snd_device_t snd_device)
 {
     struct platform_data *my_data = (struct platform_data *)platform;
@@ -426,7 +445,8 @@
     struct platform_data *my_data = (struct platform_data *)platform;
     int ret = 0;
 
-    if (my_data->csd_client != NULL) {
+    if (my_data->csd_client != NULL &&
+        voice_is_in_call(my_data->adev)) {
         /* This must be called before disabling the mixer controls on APQ side */
         if (my_data->csd_disable_device == NULL) {
             ALOGE("%s: dlsym error for csd_disable_device", __func__);
@@ -474,7 +494,7 @@
     return ret;
 }
 
-int platform_start_voice_call(void *platform)
+int platform_start_voice_call(void *platform, uint32_t vsid __unused)
 {
     struct platform_data *my_data = (struct platform_data *)platform;
     int ret = 0;
@@ -494,7 +514,7 @@
     return ret;
 }
 
-int platform_stop_voice_call(void *platform)
+int platform_stop_voice_call(void *platform, uint32_t vsid __unused)
 {
     struct platform_data *my_data = (struct platform_data *)platform;
     int ret = 0;
@@ -557,6 +577,12 @@
     return ret;
 }
 
+int platform_set_device_mute(void *platform __unused, bool state __unused, char *dir __unused)
+{
+    ALOGE("%s: Not implemented", __func__);
+    return -ENOSYS;
+}
+
 snd_device_t platform_get_output_snd_device(void *platform, audio_devices_t devices)
 {
     struct platform_data *my_data = (struct platform_data *)platform;
@@ -571,19 +597,23 @@
         goto exit;
     }
 
-    if (mode == AUDIO_MODE_IN_CALL) {
+    if (voice_is_in_call(adev)) {
         if (devices & AUDIO_DEVICE_OUT_WIRED_HEADPHONE ||
             devices & AUDIO_DEVICE_OUT_WIRED_HEADSET) {
-            if (adev->tty_mode == TTY_MODE_FULL)
+            if (adev->voice.tty_mode == TTY_MODE_FULL)
                 snd_device = SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES;
-            else if (adev->tty_mode == TTY_MODE_VCO)
+            else if (adev->voice.tty_mode == TTY_MODE_VCO)
                 snd_device = SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES;
-            else if (adev->tty_mode == TTY_MODE_HCO)
+            else if (adev->voice.tty_mode == TTY_MODE_HCO)
                 snd_device = SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET;
             else
                 snd_device = SND_DEVICE_OUT_VOICE_HEADPHONES;
         } else if (devices & AUDIO_DEVICE_OUT_ALL_SCO) {
-            snd_device = SND_DEVICE_OUT_BT_SCO;
+            if (adev->bt_wb_speech_enabled) {
+                snd_device = SND_DEVICE_OUT_BT_SCO_WB;
+            } else {
+                snd_device = SND_DEVICE_OUT_BT_SCO;
+            }
         } else if (devices & AUDIO_DEVICE_OUT_SPEAKER) {
             snd_device = SND_DEVICE_OUT_VOICE_SPEAKER;
         } else if (devices & AUDIO_DEVICE_OUT_EARPIECE) {
@@ -630,7 +660,11 @@
         else
             snd_device = SND_DEVICE_OUT_SPEAKER;
     } else if (devices & AUDIO_DEVICE_OUT_ALL_SCO) {
-        snd_device = SND_DEVICE_OUT_BT_SCO;
+        if (adev->bt_wb_speech_enabled) {
+            snd_device = SND_DEVICE_OUT_BT_SCO_WB;
+        } else {
+            snd_device = SND_DEVICE_OUT_BT_SCO;
+        }
     } else if (devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
         snd_device = SND_DEVICE_OUT_HDMI ;
     } else if (devices & AUDIO_DEVICE_OUT_EARPIECE) {
@@ -660,15 +694,11 @@
 
     ALOGV("%s: enter: out_device(%#x) in_device(%#x)",
           __func__, out_device, in_device);
-    if (mode == AUDIO_MODE_IN_CALL) {
-        if (out_device == AUDIO_DEVICE_NONE) {
-            ALOGE("%s: No output device set for voice call", __func__);
-            goto exit;
-        }
-        if (adev->tty_mode != TTY_MODE_OFF) {
+    if ((out_device != AUDIO_DEVICE_NONE) && voice_is_in_call(adev)) {
+        if (adev->voice.tty_mode != TTY_MODE_OFF) {
             if (out_device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE ||
                 out_device & AUDIO_DEVICE_OUT_WIRED_HEADSET) {
-                switch (adev->tty_mode) {
+                switch (adev->voice.tty_mode) {
                 case TTY_MODE_FULL:
                     snd_device = SND_DEVICE_IN_VOICE_TTY_FULL_HEADSET_MIC;
                     break;
@@ -679,7 +709,7 @@
                     snd_device = SND_DEVICE_IN_VOICE_TTY_HCO_HEADSET_MIC;
                     break;
                 default:
-                    ALOGE("%s: Invalid TTY mode (%#x)", __func__, adev->tty_mode);
+                    ALOGE("%s: Invalid TTY mode (%#x)", __func__, adev->voice.tty_mode);
                 }
                 goto exit;
             }
@@ -702,7 +732,11 @@
         } else if (out_device & AUDIO_DEVICE_OUT_WIRED_HEADSET) {
             snd_device = SND_DEVICE_IN_VOICE_HEADSET_MIC;
         } else if (out_device & AUDIO_DEVICE_OUT_ALL_SCO) {
-            snd_device = SND_DEVICE_IN_BT_SCO_MIC ;
+            if (adev->bt_wb_speech_enabled) {
+                snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB;
+            } else {
+                snd_device = SND_DEVICE_IN_BT_SCO_MIC;
+            }
         } else if (out_device & AUDIO_DEVICE_OUT_SPEAKER) {
             if (my_data->fluence_in_voice_call && my_data->fluence_in_spkr_mode &&
                     my_data->dualmic_config == DUALMIC_CONFIG_ENDFIRE) {
@@ -772,7 +806,11 @@
         } else if (in_device & AUDIO_DEVICE_IN_WIRED_HEADSET) {
             snd_device = SND_DEVICE_IN_HEADSET_MIC;
         } else if (in_device & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) {
-            snd_device = SND_DEVICE_IN_BT_SCO_MIC ;
+            if (adev->bt_wb_speech_enabled) {
+                snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB;
+            } else {
+                snd_device = SND_DEVICE_IN_BT_SCO_MIC;
+            }
         } else if (in_device & AUDIO_DEVICE_IN_AUX_DIGITAL) {
             snd_device = SND_DEVICE_IN_HDMI_MIC;
         } else {
@@ -790,7 +828,11 @@
         } else if (out_device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE) {
             snd_device = SND_DEVICE_IN_HANDSET_MIC;
         } else if (out_device & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET) {
-            snd_device = SND_DEVICE_IN_BT_SCO_MIC;
+            if (adev->bt_wb_speech_enabled) {
+                snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB;
+            } else {
+                snd_device = SND_DEVICE_IN_BT_SCO_MIC;
+            }
         } else if (out_device & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
             snd_device = SND_DEVICE_IN_HDMI_MIC;
         } else {
@@ -838,7 +880,7 @@
     return 0;
 }
 
-int platform_edid_get_max_channels(void *platform)
+int platform_edid_get_max_channels(void *platform __unused)
 {
     FILE *file;
     struct audio_block_header header;
@@ -884,6 +926,31 @@
     return max_channels;
 }
 
+int platform_set_incall_recording_session_id(void *platform __unused,
+                                             uint32_t session_id __unused, int rec_mode __unused)
+{
+    ALOGE("%s: Not implemented", __func__);
+    return -ENOSYS;
+}
+
+int platform_stop_incall_recording_usecase(void *platform __unused)
+{
+    ALOGE("%s: Not implemented", __func__);
+    return -ENOSYS;
+}
+
+int platform_start_incall_music_usecase(void *platform __unused)
+{
+    ALOGE("%s: Not implemented", __func__);
+    return -ENOSYS;
+}
+
+int platform_stop_incall_music_usecase(void *platform __unused)
+{
+    ALOGE("%s: Not implemented", __func__);
+    return -ENOSYS;
+}
+
 /* Delay in Us */
 int64_t platform_render_latency(audio_usecase_t usecase)
 {
@@ -896,3 +963,44 @@
             return 0;
     }
 }
+
+int platform_switch_voice_call_enable_device_config(void *platform __unused,
+                                                    snd_device_t out_snd_device __unused,
+                                                    snd_device_t in_snd_device __unused)
+{
+    return 0;
+}
+
+int platform_switch_voice_call_usecase_route_post(void *platform __unused,
+                                                  snd_device_t out_snd_device __unused,
+                                                  snd_device_t in_snd_device __unused)
+{
+    return 0;
+}
+
+int platform_get_sample_rate(void *platform __unused, uint32_t *rate __unused)
+{
+    return -ENOSYS;
+}
+
+int platform_get_usecase_index(const char * usecase __unused)
+{
+    return -ENOSYS;
+}
+
+int platform_set_usecase_pcm_id(audio_usecase_t usecase __unused, int32_t type __unused,
+                                int32_t pcm_id __unused)
+{
+    return -ENOSYS;
+}
+
+int platform_set_snd_device_backend(snd_device_t device __unused,
+                                    const char *backend __unused)
+{
+    return -ENOSYS;
+}
+
+void platform_set_echo_reference(struct audio_device *adev, bool enable, audio_devices_t out_device)
+{
+    return;
+}
diff --git a/hal/msm8960/platform.h b/hal/msm8960/platform.h
index 4bc5003..0201772 100644
--- a/hal/msm8960/platform.h
+++ b/hal/msm8960/platform.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2013-2014 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -46,6 +46,7 @@
     SND_DEVICE_OUT_HDMI,
     SND_DEVICE_OUT_SPEAKER_AND_HDMI,
     SND_DEVICE_OUT_BT_SCO,
+    SND_DEVICE_OUT_BT_SCO_WB,
     SND_DEVICE_OUT_VOICE_HANDSET_TMUS,
     SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES,
     SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES,
@@ -68,6 +69,7 @@
     SND_DEVICE_IN_VOICE_HEADSET_MIC,
     SND_DEVICE_IN_HDMI_MIC,
     SND_DEVICE_IN_BT_SCO_MIC,
+    SND_DEVICE_IN_BT_SCO_MIC_WB,
     SND_DEVICE_IN_CAMCORDER_MIC,
     SND_DEVICE_IN_VOICE_DMIC_EF,
     SND_DEVICE_IN_VOICE_DMIC_BS,
@@ -114,4 +116,12 @@
 #define AUDIO_CAPTURE_PERIOD_DURATION_MSEC 20
 #define AUDIO_CAPTURE_PERIOD_COUNT 2
 
+#define LOW_LATENCY_CAPTURE_SAMPLE_RATE 48000
+#define LOW_LATENCY_CAPTURE_PERIOD_SIZE 240
+#define LOW_LATENCY_CAPTURE_USE_CASE 0
+
+#define AFE_PROXY_PLAYBACK_PCM_DEVICE 7
+#define AFE_PROXY_RECORD_PCM_DEVICE 8
+
+
 #endif // QCOM_AUDIO_PLATFORM_H
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index 15f8476..7248098 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2013-2014 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@
 #include <stdlib.h>
 #include <dlfcn.h>
 #include <cutils/log.h>
+#include <cutils/str_parms.h>
 #include <cutils/properties.h>
 #include <audio_hw.h>
 #include <platform_api.h>
@@ -50,11 +51,7 @@
 /* Retry for delay in FW loading*/
 #define RETRY_NUMBER 10
 #define RETRY_US 500000
-
-#define MAX_VOL_INDEX 5
-#define MIN_VOL_INDEX 0
-#define percent_to_index(val, min, max) \
-	        ((val) * ((max) - (min)) * 0.01 + (min) + .5)
+#define MAX_SND_CARD 8
 
 struct audio_block_header
 {
@@ -62,34 +59,68 @@
     int length;
 };
 
+/* Audio calibration related functions */
 typedef void (*acdb_deallocate_t)();
+#ifdef PLATFORM_MSM8084
+typedef int  (*acdb_init_t)(char *);
+#else
 typedef int  (*acdb_init_t)();
+#endif
 typedef void (*acdb_send_audio_cal_t)(int, int);
 typedef void (*acdb_send_voice_cal_t)(int, int);
+typedef int (*acdb_reload_vocvoltable_t)(int);
 
 /* Audio calibration related functions */
 struct platform_data {
     struct audio_device *adev;
     bool fluence_in_spkr_mode;
     bool fluence_in_voice_call;
+    bool fluence_in_voice_comm;
     bool fluence_in_voice_rec;
     int  dualmic_config;
-
     void *acdb_handle;
-    acdb_init_t acdb_init;
-    acdb_deallocate_t acdb_deallocate;
-    acdb_send_audio_cal_t acdb_send_audio_cal;
-    acdb_send_voice_cal_t acdb_send_voice_cal;
+    acdb_init_t                acdb_init;
+    acdb_deallocate_t          acdb_deallocate;
+    acdb_send_audio_cal_t      acdb_send_audio_cal;
+    acdb_send_voice_cal_t      acdb_send_voice_cal;
+    acdb_reload_vocvoltable_t  acdb_reload_vocvoltable;
+    struct csd_data *csd;
+    bool ext_speaker;
+    bool ext_earpiece;
 };
 
-static const int pcm_device_table[AUDIO_USECASE_MAX][2] = {
-    [USECASE_AUDIO_PLAYBACK_DEEP_BUFFER] = {0, 0},
-    [USECASE_AUDIO_PLAYBACK_LOW_LATENCY] = {15, 15},
-    [USECASE_AUDIO_PLAYBACK_MULTI_CH] = {1, 1},
-    [USECASE_AUDIO_PLAYBACK_OFFLOAD] = {9, 9},
-    [USECASE_AUDIO_RECORD] = {0, 0},
-    [USECASE_AUDIO_RECORD_LOW_LATENCY] = {15, 15},
-    [USECASE_VOICE_CALL] = {2, 2},
+static int pcm_device_table[AUDIO_USECASE_MAX][2] = {
+    [USECASE_AUDIO_PLAYBACK_DEEP_BUFFER] = {DEEP_BUFFER_PCM_DEVICE,
+                                            DEEP_BUFFER_PCM_DEVICE},
+    [USECASE_AUDIO_PLAYBACK_LOW_LATENCY] = {LOWLATENCY_PCM_DEVICE,
+                                            LOWLATENCY_PCM_DEVICE},
+    [USECASE_AUDIO_PLAYBACK_MULTI_CH] = {MULTIMEDIA2_PCM_DEVICE,
+                                         MULTIMEDIA2_PCM_DEVICE},
+    [USECASE_AUDIO_PLAYBACK_OFFLOAD] = {PLAYBACK_OFFLOAD_DEVICE,
+                                        PLAYBACK_OFFLOAD_DEVICE},
+    [USECASE_AUDIO_RECORD] = {AUDIO_RECORD_PCM_DEVICE,
+                              AUDIO_RECORD_PCM_DEVICE},
+    [USECASE_AUDIO_RECORD_LOW_LATENCY] = {LOWLATENCY_PCM_DEVICE,
+                                          LOWLATENCY_PCM_DEVICE},
+    [USECASE_VOICE_CALL] = {VOICE_CALL_PCM_DEVICE,
+                            VOICE_CALL_PCM_DEVICE},
+    [USECASE_VOICE2_CALL] = {VOICE2_CALL_PCM_DEVICE, VOICE2_CALL_PCM_DEVICE},
+    [USECASE_VOLTE_CALL] = {VOLTE_CALL_PCM_DEVICE, VOLTE_CALL_PCM_DEVICE},
+    [USECASE_QCHAT_CALL] = {QCHAT_CALL_PCM_DEVICE, QCHAT_CALL_PCM_DEVICE},
+    [USECASE_VOWLAN_CALL] = {VOWLAN_CALL_PCM_DEVICE, VOWLAN_CALL_PCM_DEVICE},
+    [USECASE_INCALL_REC_UPLINK] = {AUDIO_RECORD_PCM_DEVICE,
+                                   AUDIO_RECORD_PCM_DEVICE},
+    [USECASE_INCALL_REC_DOWNLINK] = {AUDIO_RECORD_PCM_DEVICE,
+                                     AUDIO_RECORD_PCM_DEVICE},
+    [USECASE_INCALL_REC_UPLINK_AND_DOWNLINK] = {AUDIO_RECORD_PCM_DEVICE,
+                                                AUDIO_RECORD_PCM_DEVICE},
+    [USECASE_AUDIO_HFP_SCO] = {HFP_PCM_RX, HFP_SCO_RX},
+
+    [USECASE_AUDIO_PLAYBACK_AFE_PROXY] = {AFE_PROXY_PLAYBACK_PCM_DEVICE,
+                                          AFE_PROXY_RECORD_PCM_DEVICE},
+    [USECASE_AUDIO_RECORD_AFE_PROXY] = {AFE_PROXY_PLAYBACK_PCM_DEVICE,
+                                        AFE_PROXY_RECORD_PCM_DEVICE},
+
 };
 
 /* Array to store sound devices */
@@ -99,90 +130,239 @@
     [SND_DEVICE_OUT_HANDSET] = "handset",
     [SND_DEVICE_OUT_SPEAKER] = "speaker",
     [SND_DEVICE_OUT_SPEAKER_REVERSE] = "speaker-reverse",
+    [SND_DEVICE_OUT_SPEAKER_SAFE] = "speaker-safe",
     [SND_DEVICE_OUT_HEADPHONES] = "headphones",
+    [SND_DEVICE_OUT_LINE] = "line",
     [SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES] = "speaker-and-headphones",
+    [SND_DEVICE_OUT_SPEAKER_AND_LINE] = "speaker-and-line",
     [SND_DEVICE_OUT_VOICE_HANDSET] = "voice-handset",
+    [SND_DEVICE_OUT_VOICE_HAC_HANDSET] = "voice-hac-handset",
     [SND_DEVICE_OUT_VOICE_SPEAKER] = "voice-speaker",
     [SND_DEVICE_OUT_VOICE_HEADPHONES] = "voice-headphones",
+    [SND_DEVICE_OUT_VOICE_LINE] = "voice-line",
     [SND_DEVICE_OUT_HDMI] = "hdmi",
     [SND_DEVICE_OUT_SPEAKER_AND_HDMI] = "speaker-and-hdmi",
     [SND_DEVICE_OUT_BT_SCO] = "bt-sco-headset",
+    [SND_DEVICE_OUT_BT_SCO_WB] = "bt-sco-headset-wb",
     [SND_DEVICE_OUT_VOICE_HANDSET_TMUS] = "voice-handset-tmus",
     [SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES] = "voice-tty-full-headphones",
     [SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = "voice-tty-vco-headphones",
     [SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET] = "voice-tty-hco-handset",
+    [SND_DEVICE_OUT_VOICE_TX] = "voice-tx",
 
     /* Capture sound devices */
     [SND_DEVICE_IN_HANDSET_MIC] = "handset-mic",
-    [SND_DEVICE_IN_SPEAKER_MIC] = "speaker-mic",
-    [SND_DEVICE_IN_HEADSET_MIC] = "headset-mic",
     [SND_DEVICE_IN_HANDSET_MIC_AEC] = "handset-mic",
-    [SND_DEVICE_IN_SPEAKER_MIC_AEC] = "speaker-mic-aec",
+    [SND_DEVICE_IN_HANDSET_MIC_NS] = "handset-mic",
+    [SND_DEVICE_IN_HANDSET_MIC_AEC_NS] = "handset-mic",
+    [SND_DEVICE_IN_HANDSET_DMIC] = "dmic-endfire",
+    [SND_DEVICE_IN_HANDSET_DMIC_AEC] = "dmic-endfire",
+    [SND_DEVICE_IN_HANDSET_DMIC_NS] = "dmic-endfire",
+    [SND_DEVICE_IN_HANDSET_DMIC_AEC_NS] = "dmic-endfire",
+    [SND_DEVICE_IN_HANDSET_DMIC_STEREO] = "dmic-endfire",
+
+    [SND_DEVICE_IN_SPEAKER_MIC] = "speaker-mic",
+    [SND_DEVICE_IN_SPEAKER_MIC_AEC] = "speaker-mic",
+    [SND_DEVICE_IN_SPEAKER_MIC_NS] = "speaker-mic",
+    [SND_DEVICE_IN_SPEAKER_MIC_AEC_NS] = "speaker-mic",
+    [SND_DEVICE_IN_SPEAKER_DMIC] = "speaker-dmic-endfire",
+    [SND_DEVICE_IN_SPEAKER_DMIC_AEC] = "speaker-dmic-endfire",
+    [SND_DEVICE_IN_SPEAKER_DMIC_NS] = "speaker-dmic-endfire",
+    [SND_DEVICE_IN_SPEAKER_DMIC_AEC_NS] = "speaker-dmic-endfire",
+    [SND_DEVICE_IN_SPEAKER_DMIC_STEREO] = "speaker-dmic-endfire",
+
+    [SND_DEVICE_IN_HEADSET_MIC] = "headset-mic",
     [SND_DEVICE_IN_HEADSET_MIC_AEC] = "headset-mic",
-    [SND_DEVICE_IN_VOICE_SPEAKER_MIC] = "voice-speaker-mic",
-    [SND_DEVICE_IN_VOICE_HEADSET_MIC] = "voice-headset-mic",
+
     [SND_DEVICE_IN_HDMI_MIC] = "hdmi-mic",
     [SND_DEVICE_IN_BT_SCO_MIC] = "bt-sco-mic",
+    [SND_DEVICE_IN_BT_SCO_MIC_WB] = "bt-sco-mic-wb",
+
     [SND_DEVICE_IN_CAMCORDER_MIC] = "camcorder-mic",
-    [SND_DEVICE_IN_VOICE_DMIC_EF] = "voice-dmic-ef",
-    [SND_DEVICE_IN_VOICE_DMIC_BS] = "voice-dmic-bs",
-    [SND_DEVICE_IN_VOICE_DMIC_EF_TMUS] = "voice-dmic-ef-tmus",
-    [SND_DEVICE_IN_VOICE_SPEAKER_DMIC_EF] = "voice-speaker-dmic-ef",
-    [SND_DEVICE_IN_VOICE_SPEAKER_DMIC_BS] = "voice-speaker-dmic-bs",
+
+    [SND_DEVICE_IN_VOICE_DMIC] = "voice-dmic-ef",
+    [SND_DEVICE_IN_VOICE_DMIC_TMUS] = "voice-dmic-ef-tmus",
+    [SND_DEVICE_IN_VOICE_SPEAKER_MIC] = "voice-speaker-mic",
+    [SND_DEVICE_IN_VOICE_SPEAKER_DMIC] = "voice-speaker-dmic-ef",
+    [SND_DEVICE_IN_VOICE_HEADSET_MIC] = "voice-headset-mic",
     [SND_DEVICE_IN_VOICE_TTY_FULL_HEADSET_MIC] = "voice-tty-full-headset-mic",
     [SND_DEVICE_IN_VOICE_TTY_VCO_HANDSET_MIC] = "voice-tty-vco-handset-mic",
     [SND_DEVICE_IN_VOICE_TTY_HCO_HEADSET_MIC] = "voice-tty-hco-headset-mic",
+
     [SND_DEVICE_IN_VOICE_REC_MIC] = "voice-rec-mic",
-    [SND_DEVICE_IN_VOICE_REC_DMIC_EF] = "voice-rec-dmic-ef",
-    [SND_DEVICE_IN_VOICE_REC_DMIC_BS] = "voice-rec-dmic-bs",
-    [SND_DEVICE_IN_VOICE_REC_DMIC_EF_FLUENCE] = "voice-rec-dmic-ef-fluence",
-    [SND_DEVICE_IN_VOICE_REC_DMIC_BS_FLUENCE] = "voice-rec-dmic-bs-fluence",
+    [SND_DEVICE_IN_VOICE_REC_MIC_NS] = "voice-rec-mic",
+    [SND_DEVICE_IN_VOICE_REC_DMIC_STEREO] = "voice-rec-dmic-ef",
+    [SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE] = "voice-rec-dmic-ef-fluence",
+
+    [SND_DEVICE_IN_VOICE_RX] = "voice-rx",
 };
 
 /* ACDB IDs (audio DSP path configuration IDs) for each sound device */
-static const int acdb_device_table[SND_DEVICE_MAX] = {
+static int acdb_device_table[SND_DEVICE_MAX] = {
     [SND_DEVICE_NONE] = -1,
     [SND_DEVICE_OUT_HANDSET] = 7,
     [SND_DEVICE_OUT_SPEAKER] = 15,
     [SND_DEVICE_OUT_SPEAKER_REVERSE] = 15,
+    [SND_DEVICE_OUT_SPEAKER_SAFE] = 15,
     [SND_DEVICE_OUT_HEADPHONES] = 10,
+    [SND_DEVICE_OUT_LINE] = 77,
     [SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES] = 10,
-    [SND_DEVICE_OUT_VOICE_HANDSET] = 7,
-    [SND_DEVICE_OUT_VOICE_SPEAKER] = 15,
+    [SND_DEVICE_OUT_SPEAKER_AND_LINE] = 77,
+    [SND_DEVICE_OUT_VOICE_HANDSET] = ACDB_ID_VOICE_HANDSET,
+    [SND_DEVICE_OUT_VOICE_SPEAKER] = ACDB_ID_VOICE_SPEAKER,
+    [SND_DEVICE_OUT_VOICE_HAC_HANDSET] = 53,
     [SND_DEVICE_OUT_VOICE_HEADPHONES] = 10,
+    [SND_DEVICE_OUT_VOICE_LINE] = 77,
     [SND_DEVICE_OUT_HDMI] = 18,
     [SND_DEVICE_OUT_SPEAKER_AND_HDMI] = 15,
     [SND_DEVICE_OUT_BT_SCO] = 22,
-    [SND_DEVICE_OUT_VOICE_HANDSET_TMUS] = 88,
+    [SND_DEVICE_OUT_BT_SCO_WB] = 39,
+    [SND_DEVICE_OUT_VOICE_HANDSET_TMUS] = ACDB_ID_VOICE_HANDSET_TMUS,
     [SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES] = 17,
     [SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = 17,
     [SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET] = 37,
+    [SND_DEVICE_OUT_VOICE_TX] = 45,
 
     [SND_DEVICE_IN_HANDSET_MIC] = 4,
-    [SND_DEVICE_IN_SPEAKER_MIC] = 4, /* ToDo: Check if this needs to changed to 11 */
+    [SND_DEVICE_IN_HANDSET_MIC_AEC] = 106,
+    [SND_DEVICE_IN_HANDSET_MIC_NS] = 107,
+    [SND_DEVICE_IN_HANDSET_MIC_AEC_NS] = 108,
+    [SND_DEVICE_IN_HANDSET_DMIC] = 41,
+    [SND_DEVICE_IN_HANDSET_DMIC_AEC] = 109,
+    [SND_DEVICE_IN_HANDSET_DMIC_NS] = 110,
+    [SND_DEVICE_IN_HANDSET_DMIC_AEC_NS] = 111,
+    [SND_DEVICE_IN_HANDSET_DMIC_STEREO] = 34,
+
+    [SND_DEVICE_IN_SPEAKER_MIC] = 11,
+    [SND_DEVICE_IN_SPEAKER_MIC_AEC] = 112,
+    [SND_DEVICE_IN_SPEAKER_MIC_NS] = 113,
+    [SND_DEVICE_IN_SPEAKER_MIC_AEC_NS] = 114,
+    [SND_DEVICE_IN_SPEAKER_DMIC] = 43,
+    [SND_DEVICE_IN_SPEAKER_DMIC_AEC] = 115,
+    [SND_DEVICE_IN_SPEAKER_DMIC_NS] = 116,
+    [SND_DEVICE_IN_SPEAKER_DMIC_AEC_NS] = 117,
+    [SND_DEVICE_IN_SPEAKER_DMIC_STEREO] = 35,
+
     [SND_DEVICE_IN_HEADSET_MIC] = 8,
-    [SND_DEVICE_IN_HANDSET_MIC_AEC] = 40,
-    [SND_DEVICE_IN_SPEAKER_MIC_AEC] = 42,
-    [SND_DEVICE_IN_HEADSET_MIC_AEC] = 47,
-    [SND_DEVICE_IN_VOICE_SPEAKER_MIC] = 11,
-    [SND_DEVICE_IN_VOICE_HEADSET_MIC] = 8,
+    [SND_DEVICE_IN_HEADSET_MIC_AEC] = ACDB_ID_HEADSET_MIC_AEC,
+
     [SND_DEVICE_IN_HDMI_MIC] = 4,
     [SND_DEVICE_IN_BT_SCO_MIC] = 21,
+    [SND_DEVICE_IN_BT_SCO_MIC_WB] = 38,
+
     [SND_DEVICE_IN_CAMCORDER_MIC] = 61,
-    [SND_DEVICE_IN_VOICE_DMIC_EF] = 41,
-    [SND_DEVICE_IN_VOICE_DMIC_BS] = 5,
-    [SND_DEVICE_IN_VOICE_DMIC_EF_TMUS] = 89,
-    [SND_DEVICE_IN_VOICE_SPEAKER_DMIC_EF] = 43,
-    [SND_DEVICE_IN_VOICE_SPEAKER_DMIC_BS] = 12,
+
+    [SND_DEVICE_IN_VOICE_DMIC] = 41,
+    [SND_DEVICE_IN_VOICE_DMIC_TMUS] = ACDB_ID_VOICE_DMIC_EF_TMUS,
+    [SND_DEVICE_IN_VOICE_SPEAKER_MIC] = 11,
+    [SND_DEVICE_IN_VOICE_SPEAKER_DMIC] = 43,
+    [SND_DEVICE_IN_VOICE_HEADSET_MIC] = 8,
     [SND_DEVICE_IN_VOICE_TTY_FULL_HEADSET_MIC] = 16,
     [SND_DEVICE_IN_VOICE_TTY_VCO_HANDSET_MIC] = 36,
     [SND_DEVICE_IN_VOICE_TTY_HCO_HEADSET_MIC] = 16,
+
     [SND_DEVICE_IN_VOICE_REC_MIC] = 62,
-    /* TODO: Update with proper acdb ids */
-    [SND_DEVICE_IN_VOICE_REC_DMIC_EF] = 62,
-    [SND_DEVICE_IN_VOICE_REC_DMIC_BS] = 62,
-    [SND_DEVICE_IN_VOICE_REC_DMIC_EF_FLUENCE] = 6,
-    [SND_DEVICE_IN_VOICE_REC_DMIC_BS_FLUENCE] = 5,
+    [SND_DEVICE_IN_VOICE_REC_MIC_NS] = 113,
+    [SND_DEVICE_IN_VOICE_REC_DMIC_STEREO] = 35,
+    [SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE] = 43,
+
+    [SND_DEVICE_IN_VOICE_RX] = 44,
+};
+
+struct name_to_index {
+    char name[100];
+    unsigned int index;
+};
+
+#define TO_NAME_INDEX(X)   #X, X
+
+/* Used to get index from parsed string */
+static const struct name_to_index snd_device_name_index[SND_DEVICE_MAX] = {
+    /* out */
+    {TO_NAME_INDEX(SND_DEVICE_OUT_HANDSET)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_REVERSE)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_SAFE)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_HEADPHONES)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_LINE)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_LINE)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_HANDSET)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_SPEAKER)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_HEADPHONES)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_LINE)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_HDMI)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_HDMI)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_BT_SCO)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_BT_SCO_WB)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_HANDSET_TMUS)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_HAC_HANDSET)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET)},
+
+    /* in */
+    {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_MIC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_MIC_AEC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_MIC_NS)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_MIC_AEC_NS)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_DMIC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_DMIC_AEC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_DMIC_NS)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_DMIC_AEC_NS)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_DMIC_STEREO)},
+
+    {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_MIC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_MIC_AEC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_MIC_NS)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_MIC_AEC_NS)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_DMIC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_DMIC_AEC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_DMIC_NS)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_DMIC_AEC_NS)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_DMIC_STEREO)},
+
+    {TO_NAME_INDEX(SND_DEVICE_IN_HEADSET_MIC)},
+
+    {TO_NAME_INDEX(SND_DEVICE_IN_HDMI_MIC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_BT_SCO_MIC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_BT_SCO_MIC_WB)},
+
+    {TO_NAME_INDEX(SND_DEVICE_IN_CAMCORDER_MIC)},
+
+    {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_DMIC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_DMIC_TMUS)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_SPEAKER_MIC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_SPEAKER_DMIC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_HEADSET_MIC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_TTY_FULL_HEADSET_MIC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_TTY_VCO_HANDSET_MIC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_TTY_HCO_HEADSET_MIC)},
+
+    {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_REC_MIC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_REC_MIC_NS)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_REC_DMIC_STEREO)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE)},
+};
+
+static char * backend_table[SND_DEVICE_MAX] = {0};
+
+static const struct name_to_index usecase_name_index[AUDIO_USECASE_MAX] = {
+    {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_DEEP_BUFFER)},
+    {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_LOW_LATENCY)},
+    {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_MULTI_CH)},
+    {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_OFFLOAD)},
+    {TO_NAME_INDEX(USECASE_AUDIO_RECORD)},
+    {TO_NAME_INDEX(USECASE_AUDIO_RECORD_LOW_LATENCY)},
+    {TO_NAME_INDEX(USECASE_VOICE_CALL)},
+    {TO_NAME_INDEX(USECASE_VOICE2_CALL)},
+    {TO_NAME_INDEX(USECASE_VOLTE_CALL)},
+    {TO_NAME_INDEX(USECASE_QCHAT_CALL)},
+    {TO_NAME_INDEX(USECASE_VOWLAN_CALL)},
+    {TO_NAME_INDEX(USECASE_INCALL_REC_UPLINK)},
+    {TO_NAME_INDEX(USECASE_INCALL_REC_DOWNLINK)},
+    {TO_NAME_INDEX(USECASE_INCALL_REC_UPLINK_AND_DOWNLINK)},
+    {TO_NAME_INDEX(USECASE_AUDIO_HFP_SCO)},
 };
 
 #define DEEP_BUFFER_PLATFORM_DELAY (29*1000LL)
@@ -227,62 +407,287 @@
     return is_tmus;
 }
 
-static int set_volume_values(int type, int volume, int* values)
+void platform_set_echo_reference(struct audio_device *adev, bool enable, audio_devices_t out_device)
 {
-    values[0] = volume;
-    values[1] = ALL_SESSION_VSID;
+    char mixer_path[50] = { 0 } ;
+    snd_device_t snd_device = SND_DEVICE_NONE;
+    struct listnode *node;
+    struct audio_usecase *usecase;
 
-    switch(type) {
-    case VOLUME_SET:
-        values[2] = DEFAULT_VOLUME_RAMP_DURATION_MS;
-        break;
-    case MUTE_SET:
-        values[2] = DEFAULT_MUTE_RAMP_DURATION;
-        break;
-    default:
-        return -EINVAL;
+    strcpy(mixer_path, "echo-reference");
+    if (out_device != AUDIO_DEVICE_NONE) {
+        snd_device = platform_get_output_snd_device(adev->platform, out_device);
+        platform_add_backend_name(adev->platform, mixer_path, snd_device);
     }
-    return 0;
+
+    if (enable)
+        audio_route_apply_and_update_path(adev->audio_route, mixer_path);
+    else
+        audio_route_reset_and_update_path(adev->audio_route, mixer_path);
+
+    ALOGV("Setting EC Reference: %d for %s", enable, mixer_path);
 }
 
-static int set_echo_reference(struct mixer *mixer, const char* ec_ref)
+static struct csd_data *open_csd_client(bool i2s_ext_modem)
 {
-    struct mixer_ctl *ctl;
-    const char *mixer_ctl_name = "EC_REF_RX";
+    struct csd_data *csd = calloc(1, sizeof(struct csd_data));
 
-    ctl = mixer_get_ctl_by_name(mixer, mixer_ctl_name);
-    if (!ctl) {
-        ALOGE("%s: Could not get ctl for mixer cmd - %s",
-              __func__, mixer_ctl_name);
-        return -EINVAL;
+    csd->csd_client = dlopen(LIB_CSD_CLIENT, RTLD_NOW);
+    if (csd->csd_client == NULL) {
+        ALOGE("%s: DLOPEN failed for %s", __func__, LIB_CSD_CLIENT);
+        goto error;
+    } else {
+        ALOGV("%s: DLOPEN successful for %s", __func__, LIB_CSD_CLIENT);
+
+        csd->deinit = (deinit_t)dlsym(csd->csd_client,
+                                             "csd_client_deinit");
+        if (csd->deinit == NULL) {
+            ALOGE("%s: dlsym error %s for csd_client_deinit", __func__,
+                  dlerror());
+            goto error;
+        }
+        csd->disable_device = (disable_device_t)dlsym(csd->csd_client,
+                                             "csd_client_disable_device");
+        if (csd->disable_device == NULL) {
+            ALOGE("%s: dlsym error %s for csd_client_disable_device",
+                  __func__, dlerror());
+            goto error;
+        }
+        csd->enable_device_config = (enable_device_config_t)dlsym(csd->csd_client,
+                                               "csd_client_enable_device_config");
+        if (csd->enable_device_config == NULL) {
+            ALOGE("%s: dlsym error %s for csd_client_enable_device_config",
+                  __func__, dlerror());
+            goto error;
+        }
+        csd->enable_device = (enable_device_t)dlsym(csd->csd_client,
+                                             "csd_client_enable_device");
+        if (csd->enable_device == NULL) {
+            ALOGE("%s: dlsym error %s for csd_client_enable_device",
+                  __func__, dlerror());
+            goto error;
+        }
+        csd->start_voice = (start_voice_t)dlsym(csd->csd_client,
+                                             "csd_client_start_voice");
+        if (csd->start_voice == NULL) {
+            ALOGE("%s: dlsym error %s for csd_client_start_voice",
+                  __func__, dlerror());
+            goto error;
+        }
+        csd->stop_voice = (stop_voice_t)dlsym(csd->csd_client,
+                                             "csd_client_stop_voice");
+        if (csd->stop_voice == NULL) {
+            ALOGE("%s: dlsym error %s for csd_client_stop_voice",
+                  __func__, dlerror());
+            goto error;
+        }
+        csd->volume = (volume_t)dlsym(csd->csd_client,
+                                             "csd_client_volume");
+        if (csd->volume == NULL) {
+            ALOGE("%s: dlsym error %s for csd_client_volume",
+                  __func__, dlerror());
+            goto error;
+        }
+        csd->mic_mute = (mic_mute_t)dlsym(csd->csd_client,
+                                             "csd_client_mic_mute");
+        if (csd->mic_mute == NULL) {
+            ALOGE("%s: dlsym error %s for csd_client_mic_mute",
+                  __func__, dlerror());
+            goto error;
+        }
+        csd->slow_talk = (slow_talk_t)dlsym(csd->csd_client,
+                                             "csd_client_slow_talk");
+        if (csd->slow_talk == NULL) {
+            ALOGE("%s: dlsym error %s for csd_client_slow_talk",
+                  __func__, dlerror());
+            goto error;
+        }
+        csd->start_playback = (start_playback_t)dlsym(csd->csd_client,
+                                             "csd_client_start_playback");
+        if (csd->start_playback == NULL) {
+            ALOGE("%s: dlsym error %s for csd_client_start_playback",
+                  __func__, dlerror());
+            goto error;
+        }
+        csd->stop_playback = (stop_playback_t)dlsym(csd->csd_client,
+                                             "csd_client_stop_playback");
+        if (csd->stop_playback == NULL) {
+            ALOGE("%s: dlsym error %s for csd_client_stop_playback",
+                  __func__, dlerror());
+            goto error;
+        }
+        csd->start_record = (start_record_t)dlsym(csd->csd_client,
+                                             "csd_client_start_record");
+        if (csd->start_record == NULL) {
+            ALOGE("%s: dlsym error %s for csd_client_start_record",
+                  __func__, dlerror());
+            goto error;
+        }
+        csd->stop_record = (stop_record_t)dlsym(csd->csd_client,
+                                             "csd_client_stop_record");
+        if (csd->stop_record == NULL) {
+            ALOGE("%s: dlsym error %s for csd_client_stop_record",
+                  __func__, dlerror());
+            goto error;
+        }
+
+        csd->get_sample_rate = (get_sample_rate_t)dlsym(csd->csd_client,
+                                             "csd_client_get_sample_rate");
+        if (csd->get_sample_rate == NULL) {
+            ALOGE("%s: dlsym error %s for csd_client_get_sample_rate",
+                  __func__, dlerror());
+
+            goto error;
+        }
+
+        csd->init = (init_t)dlsym(csd->csd_client, "csd_client_init");
+
+        if (csd->init == NULL) {
+            ALOGE("%s: dlsym error %s for csd_client_init",
+                  __func__, dlerror());
+            goto error;
+        } else {
+            csd->init(i2s_ext_modem);
+        }
     }
-    ALOGV("Setting EC Reference: %s", ec_ref);
-    mixer_ctl_set_enum_by_string(ctl, ec_ref);
-    return 0;
+    return csd;
+
+error:
+    free(csd);
+    csd = NULL;
+    return csd;
+}
+
+void close_csd_client(struct csd_data *csd)
+{
+    if (csd != NULL) {
+        csd->deinit();
+        dlclose(csd->csd_client);
+        free(csd);
+        csd = NULL;
+    }
+}
+
+static void platform_csd_init(struct platform_data *my_data)
+{
+#ifdef PLATFORM_MSM8084
+    int32_t modems, (*count_modems)(void);
+    const char *name = "libdetectmodem.so";
+    const char *func = "count_modems";
+    const char *error;
+
+    my_data->csd = NULL;
+
+    void *lib = dlopen(name, RTLD_NOW);
+    error = dlerror();
+    if (!lib) {
+        ALOGE("%s: could not find %s: %s", __func__, name, error);
+        return;
+    }
+
+    count_modems = NULL;
+    *(void **)(&count_modems) = dlsym(lib, func);
+    error = dlerror();
+    if (!count_modems) {
+        ALOGE("%s: could not find symbol %s in %s: %s",
+              __func__, func, name, error);
+        goto done;
+    }
+
+    modems = count_modems();
+    if (modems < 0) {
+        ALOGE("%s: count_modems failed\n", __func__);
+        goto done;
+    }
+
+    ALOGD("%s: num_modems %d\n", __func__, modems);
+    if (modems > 0)
+        my_data->csd = open_csd_client(false /*is_i2s_ext_modem*/);
+
+done:
+    dlclose(lib);
+#else
+     my_data->csd = NULL;
+#endif
+}
+
+static void set_platform_defaults(struct platform_data * my_data)
+{
+    int32_t dev;
+    for (dev = 0; dev < SND_DEVICE_MAX; dev++) {
+        backend_table[dev] = NULL;
+    }
+
+    // TBD - do these go to the platform-info.xml file.
+    // will help in avoiding strdups here
+    backend_table[SND_DEVICE_IN_BT_SCO_MIC] = strdup("bt-sco");
+    backend_table[SND_DEVICE_OUT_BT_SCO] = strdup("bt-sco");
+    backend_table[SND_DEVICE_OUT_HDMI] = strdup("hdmi");
+    backend_table[SND_DEVICE_OUT_SPEAKER_AND_HDMI] = strdup("speaker-and-hdmi");
+    backend_table[SND_DEVICE_OUT_BT_SCO_WB] = strdup("bt-sco-wb");
+    backend_table[SND_DEVICE_IN_BT_SCO_MIC_WB] = strdup("bt-sco-wb");
+    backend_table[SND_DEVICE_OUT_VOICE_TX] = strdup("afe-proxy");
+    backend_table[SND_DEVICE_IN_VOICE_RX] = strdup("afe-proxy");
+
+    if (my_data->ext_speaker) {
+        backend_table[SND_DEVICE_OUT_SPEAKER] = strdup("speaker");
+        backend_table[SND_DEVICE_OUT_SPEAKER_SAFE] = strdup("speaker");
+        backend_table[SND_DEVICE_OUT_VOICE_SPEAKER] = strdup("speaker");
+        backend_table[SND_DEVICE_OUT_SPEAKER_REVERSE] = strdup("speaker");
+        backend_table[SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES] =
+            strdup("speaker-and-headphones");
+        backend_table[SND_DEVICE_OUT_SPEAKER_AND_LINE] =
+            strdup("speaker-and-line");
+    }
+
+    if (my_data->ext_earpiece) {
+        backend_table[SND_DEVICE_OUT_VOICE_HANDSET] = strdup("handset");
+        backend_table[SND_DEVICE_OUT_VOICE_HAC_HANDSET] = strdup("handset");
+        backend_table[SND_DEVICE_OUT_VOICE_HANDSET_TMUS] = strdup("handset");
+        backend_table[SND_DEVICE_OUT_HANDSET] = strdup("handset");
+        backend_table[SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET] = strdup("handset");
+    }
 }
 
 void *platform_init(struct audio_device *adev)
 {
     char value[PROPERTY_VALUE_MAX];
     struct platform_data *my_data;
-    int retry_num = 0;
+    int retry_num = 0, snd_card_num = 0;
+    const char *snd_card_name;
 
-    adev->mixer = mixer_open(MIXER_CARD);
+    while (snd_card_num < MAX_SND_CARD) {
+        adev->mixer = mixer_open(snd_card_num);
 
-    while (!adev->mixer && retry_num < RETRY_NUMBER) {
-        usleep(RETRY_US);
-        adev->mixer = mixer_open(MIXER_CARD);
-        retry_num++;
+        while (!adev->mixer && retry_num < RETRY_NUMBER) {
+            usleep(RETRY_US);
+            adev->mixer = mixer_open(snd_card_num);
+            retry_num++;
+        }
+
+        if (!adev->mixer) {
+            ALOGE("%s: Unable to open the mixer card: %d", __func__,
+                   snd_card_num);
+            retry_num = 0;
+            snd_card_num++;
+            continue;
+        }
+
+        snd_card_name = mixer_get_name(adev->mixer);
+        ALOGD("%s: snd_card_name: %s", __func__, snd_card_name);
+
+        adev->audio_route = audio_route_init(snd_card_num, MIXER_XML_PATH);
+        if (!adev->audio_route) {
+            ALOGE("%s: Failed to init audio route controls, aborting.", __func__);
+            return NULL;
+        }
+        adev->snd_card = snd_card_num;
+        ALOGD("%s: Opened sound card:%d", __func__, snd_card_num);
+        break;
     }
 
-    if (!adev->mixer) {
-        ALOGE("Unable to open the mixer, aborting.");
-        return NULL;
-    }
-
-    adev->audio_route = audio_route_init(MIXER_CARD, MIXER_XML_PATH);
-    if (!adev->audio_route) {
-        ALOGE("%s: Failed to init audio route controls, aborting.", __func__);
+    if (snd_card_num >= MAX_SND_CARD) {
+        ALOGE("%s: Unable to find correct sound card, aborting.", __func__);
         return NULL;
     }
 
@@ -292,15 +697,28 @@
     my_data->dualmic_config = DUALMIC_CONFIG_NONE;
     my_data->fluence_in_spkr_mode = false;
     my_data->fluence_in_voice_call = false;
+    my_data->fluence_in_voice_comm = false;
     my_data->fluence_in_voice_rec = false;
 
+    /*
+     * The default assumption is that earpiece (handset), speaker and headphones
+     * devices are connected to internal HW codec and communicated through
+     * slimbus backend. If any platform communicates with speaker or earpiece
+     * or headphones through non-slimbus backend such as MI2S or AUXPCM etc.,
+     * the ext_xxxx flags must be set accordingly.
+     */
+    if (strstr(snd_card_name, "tfa9890_stereo")) {
+        my_data->ext_speaker = true;
+        my_data->ext_earpiece = true;
+    } else if (strstr(snd_card_name, "tfa9890")) {
+        my_data->ext_speaker = true;
+    }
+
     property_get("persist.audio.dualmic.config",value,"");
     if (!strcmp("broadside", value)) {
-        my_data->dualmic_config = DUALMIC_CONFIG_BROADSIDE;
-        adev->acdb_settings |= DMIC_FLAG;
+        ALOGE("%s: Unsupported dualmic configuration", __func__);
     } else if (!strcmp("endfire", value)) {
         my_data->dualmic_config = DUALMIC_CONFIG_ENDFIRE;
-        adev->acdb_settings |= DMIC_FLAG;
     }
 
     if (my_data->dualmic_config != DUALMIC_CONFIG_NONE) {
@@ -309,6 +727,11 @@
             my_data->fluence_in_voice_call = true;
         }
 
+        property_get("persist.audio.fluence.voicecomm",value,"");
+        if (!strcmp("true", value)) {
+            my_data->fluence_in_voice_comm = true;
+        }
+
         property_get("persist.audio.fluence.voicerec",value,"");
         if (!strcmp("true", value)) {
             my_data->fluence_in_voice_rec = true;
@@ -327,26 +750,60 @@
         ALOGV("%s: DLOPEN successful for %s", __func__, LIB_ACDB_LOADER);
         my_data->acdb_deallocate = (acdb_deallocate_t)dlsym(my_data->acdb_handle,
                                                     "acdb_loader_deallocate_ACDB");
+        if (!my_data->acdb_deallocate)
+            ALOGE("%s: Could not find the symbol acdb_loader_deallocate_ACDB from %s",
+                  __func__, LIB_ACDB_LOADER);
+
         my_data->acdb_send_audio_cal = (acdb_send_audio_cal_t)dlsym(my_data->acdb_handle,
                                                     "acdb_loader_send_audio_cal");
         if (!my_data->acdb_send_audio_cal)
-            ALOGW("%s: Could not find the symbol acdb_send_audio_cal from %s",
+            ALOGE("%s: Could not find the symbol acdb_send_audio_cal from %s",
                   __func__, LIB_ACDB_LOADER);
+
         my_data->acdb_send_voice_cal = (acdb_send_voice_cal_t)dlsym(my_data->acdb_handle,
                                                     "acdb_loader_send_voice_cal");
+        if (!my_data->acdb_send_voice_cal)
+            ALOGE("%s: Could not find the symbol acdb_loader_send_voice_cal from %s",
+                  __func__, LIB_ACDB_LOADER);
+
+        my_data->acdb_reload_vocvoltable = (acdb_reload_vocvoltable_t)dlsym(my_data->acdb_handle,
+                                                    "acdb_loader_reload_vocvoltable");
+        if (!my_data->acdb_reload_vocvoltable)
+            ALOGE("%s: Could not find the symbol acdb_loader_reload_vocvoltable from %s",
+                  __func__, LIB_ACDB_LOADER);
+#ifdef PLATFORM_MSM8084
+        my_data->acdb_init = (acdb_init_t)dlsym(my_data->acdb_handle,
+                                                    "acdb_loader_init_v2");
+        if (my_data->acdb_init == NULL)
+            ALOGE("%s: dlsym error %s for acdb_loader_init_v2", __func__, dlerror());
+        else
+            my_data->acdb_init((char *)snd_card_name);
+#else
         my_data->acdb_init = (acdb_init_t)dlsym(my_data->acdb_handle,
                                                     "acdb_loader_init_ACDB");
         if (my_data->acdb_init == NULL)
             ALOGE("%s: dlsym error %s for acdb_loader_init_ACDB", __func__, dlerror());
         else
             my_data->acdb_init();
+#endif
+
     }
 
+    set_platform_defaults(my_data);
+
+    /* Initialize platform specific ids and/or backends*/
+    platform_info_init();
+
+    /* load csd client */
+    platform_csd_init(my_data);
+
     return my_data;
 }
 
 void platform_deinit(void *platform)
 {
+    struct platform_data *my_data = (struct platform_data *)platform;
+    close_csd_client(my_data->csd);
     free(platform);
 }
 
@@ -355,19 +812,25 @@
     if (snd_device >= SND_DEVICE_MIN && snd_device < SND_DEVICE_MAX)
         return device_table[snd_device];
     else
-        return "";
+        return "none";
 }
 
-void platform_add_backend_name(char *mixer_path, snd_device_t snd_device)
+void platform_add_backend_name(void *platform, char *mixer_path,
+                               snd_device_t snd_device)
 {
-    if (snd_device == SND_DEVICE_IN_BT_SCO_MIC)
-        strcat(mixer_path, " bt-sco");
-    else if(snd_device == SND_DEVICE_OUT_BT_SCO)
-        strcat(mixer_path, " bt-sco");
-    else if (snd_device == SND_DEVICE_OUT_HDMI)
-        strcat(mixer_path, " hdmi");
-    else if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_HDMI)
-        strcat(mixer_path, " speaker-and-hdmi");
+    struct platform_data *my_data = (struct platform_data *)platform;
+
+    if ((snd_device < SND_DEVICE_MIN) || (snd_device >= SND_DEVICE_MAX)) {
+        ALOGE("%s: Invalid snd_device = %d", __func__, snd_device);
+        return;
+    }
+
+    const char * suffix = backend_table[snd_device];
+
+    if (suffix != NULL) {
+        strcat(mixer_path, " ");
+        strcat(mixer_path, suffix);
+    }
 }
 
 int platform_get_pcm_device_id(audio_usecase_t usecase, int device_type)
@@ -380,6 +843,63 @@
     return device_id;
 }
 
+static int find_index(const struct name_to_index * table, int32_t len,
+                      const char * name)
+{
+    int ret = 0;
+    int32_t i;
+
+    if (table == NULL) {
+        ALOGE("%s: table is NULL", __func__);
+        ret = -ENODEV;
+        goto done;
+    }
+
+    if (name == NULL) {
+        ALOGE("null key");
+        ret = -ENODEV;
+        goto done;
+    }
+
+    for (i=0; i < len; i++) {
+        if (!strcmp(table[i].name, name)) {
+            ret = table[i].index;
+            goto done;
+        }
+    }
+    ALOGE("%s: Could not find index for name = %s",
+            __func__, name);
+    ret = -ENODEV;
+done:
+    return ret;
+}
+
+int platform_get_snd_device_index(char *device_name)
+{
+    return find_index(snd_device_name_index, SND_DEVICE_MAX, device_name);
+}
+
+int platform_get_usecase_index(const char *usecase_name)
+{
+    return find_index(usecase_name_index, AUDIO_USECASE_MAX, usecase_name);
+}
+
+int platform_set_snd_device_acdb_id(snd_device_t snd_device, unsigned int acdb_id)
+{
+    int ret = 0;
+
+    if ((snd_device < SND_DEVICE_MIN) || (snd_device >= SND_DEVICE_MAX)) {
+        ALOGE("%s: Invalid snd_device = %d",
+            __func__, snd_device);
+        ret = -EINVAL;
+        goto done;
+    }
+
+    acdb_device_table[snd_device] = acdb_id;
+done:
+    return ret;
+}
+
 int platform_send_audio_calibration(void *platform, snd_device_t snd_device)
 {
     struct platform_data *my_data = (struct platform_data *)platform;
@@ -392,7 +912,7 @@
         return -EINVAL;
     }
     if (my_data->acdb_send_audio_cal) {
-        ("%s: sending audio calibration for snd_device(%d) acdb_id(%d)",
+        ALOGD("%s: sending audio calibration for snd_device(%d) acdb_id(%d)",
               __func__, snd_device, acdb_dev_id);
         if (snd_device >= SND_DEVICE_OUT_BEGIN &&
                 snd_device < SND_DEVICE_OUT_END)
@@ -406,7 +926,48 @@
 
 int platform_switch_voice_call_device_pre(void *platform)
 {
-    return 0;
+    struct platform_data *my_data = (struct platform_data *)platform;
+    int ret = 0;
+
+    if (my_data->csd != NULL &&
+        voice_is_in_call(my_data->adev)) {
+        /* This must be called before disabling mixer controls on APQ side */
+        ret = my_data->csd->disable_device();
+        if (ret < 0) {
+            ALOGE("%s: csd_client_disable_device, failed, error %d",
+                  __func__, ret);
+        }
+    }
+    return ret;
+}
+
+int platform_switch_voice_call_enable_device_config(void *platform,
+                                                    snd_device_t out_snd_device,
+                                                    snd_device_t in_snd_device)
+{
+    struct platform_data *my_data = (struct platform_data *)platform;
+    int acdb_rx_id, acdb_tx_id;
+    int ret = 0;
+
+    if (my_data->csd == NULL)
+        return ret;
+
+    acdb_rx_id = acdb_device_table[out_snd_device];
+
+    acdb_tx_id = acdb_device_table[in_snd_device];
+
+    if (acdb_rx_id > 0 && acdb_tx_id > 0) {
+        ret = my_data->csd->enable_device_config(acdb_rx_id, acdb_tx_id);
+        if (ret < 0) {
+            ALOGE("%s: csd_enable_device_config, failed, error %d",
+                  __func__, ret);
+        }
+    } else {
+        ALOGE("%s: Incorrect ACDB IDs (rx: %d tx: %d)", __func__,
+              acdb_rx_id, acdb_tx_id);
+    }
+
+    return ret;
 }
 
 int platform_switch_voice_call_device_post(void *platform,
@@ -432,14 +993,75 @@
     return 0;
 }
 
-int platform_start_voice_call(void *platform)
+int platform_switch_voice_call_usecase_route_post(void *platform,
+                                                  snd_device_t out_snd_device,
+                                                  snd_device_t in_snd_device)
 {
-    return 0;
+    struct platform_data *my_data = (struct platform_data *)platform;
+    int acdb_rx_id, acdb_tx_id;
+    int ret = 0;
+
+    if (my_data->csd == NULL)
+        return ret;
+
+    acdb_rx_id = acdb_device_table[out_snd_device];
+
+    acdb_tx_id = acdb_device_table[in_snd_device];
+
+    if (acdb_rx_id > 0 && acdb_tx_id > 0) {
+        ret = my_data->csd->enable_device(acdb_rx_id, acdb_tx_id,
+                                          my_data->adev->acdb_settings);
+        if (ret < 0) {
+            ALOGE("%s: csd_enable_device, failed, error %d", __func__, ret);
+        }
+    } else {
+        ALOGE("%s: Incorrect ACDB IDs (rx: %d tx: %d)", __func__,
+              acdb_rx_id, acdb_tx_id);
+    }
+
+    return ret;
 }
 
-int platform_stop_voice_call(void *platform)
+int platform_start_voice_call(void *platform, uint32_t vsid)
 {
-    return 0;
+    struct platform_data *my_data = (struct platform_data *)platform;
+    int ret = 0;
+
+    if (my_data->csd != NULL) {
+        ret = my_data->csd->start_voice(vsid);
+        if (ret < 0) {
+            ALOGE("%s: csd_start_voice error %d\n", __func__, ret);
+        }
+    }
+    return ret;
+}
+
+int platform_stop_voice_call(void *platform, uint32_t vsid)
+{
+    struct platform_data *my_data = (struct platform_data *)platform;
+    int ret = 0;
+
+    if (my_data->csd != NULL) {
+        ret = my_data->csd->stop_voice(vsid);
+        if (ret < 0) {
+            ALOGE("%s: csd_stop_voice error %d\n", __func__, ret);
+        }
+    }
+    return ret;
+}
+
+int platform_get_sample_rate(void *platform, uint32_t *rate)
+{
+    struct platform_data *my_data = (struct platform_data *)platform;
+    int ret = 0;
+
+    if (my_data->csd != NULL) {
+        ret = my_data->csd->get_sample_rate(rate);
+        if (ret < 0) {
+            ALOGE("%s: csd_get_sample_rate error %d\n", __func__, ret);
+        }
+    }
+    return ret;
 }
 
 int platform_set_voice_volume(void *platform, int volume)
@@ -448,13 +1070,16 @@
     struct audio_device *adev = my_data->adev;
     struct mixer_ctl *ctl;
     const char *mixer_ctl_name = "Voice Rx Gain";
-    int values[VOLUME_CTL_PARAM_NUM];
-    int ret = 0;
+    int vol_index = 0, ret = 0;
+    uint32_t set_values[ ] = {0,
+                              ALL_SESSION_VSID,
+                              DEFAULT_VOLUME_RAMP_DURATION_MS};
 
     // Voice volume levels are mapped to adsp volume levels as follows.
     // 100 -> 5, 80 -> 4, 60 -> 3, 40 -> 2, 20 -> 1  0 -> 0
     // But this values don't changed in kernel. So, below change is need.
-    volume = (int)percent_to_index(volume, MIN_VOL_INDEX, MAX_VOL_INDEX);
+    vol_index = (int)percent_to_index(volume, MIN_VOL_INDEX, MAX_VOL_INDEX);
+    set_values[0] = vol_index;
 
     ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
     if (!ctl) {
@@ -462,18 +1087,17 @@
               __func__, mixer_ctl_name);
         return -EINVAL;
     }
-    ret = set_volume_values(VOLUME_SET, volume, values);
-    if (ret < 0) {
-        ALOGV("%s: failed setting volume by incorrect type", __func__);
-        return -EINVAL;
-    }
-    ret = mixer_ctl_set_array(ctl, values, sizeof(values)/sizeof(int));
-    if (ret < 0) {
-        ALOGV("%s: failed set mixer ctl by %d", __func__, ret);
-        return -EINVAL;
-    }
+    ALOGV("Setting voice volume index: %d", set_values[0]);
+    mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values));
 
-    return 0;
+    if (my_data->csd != NULL) {
+        ret = my_data->csd->volume(ALL_SESSION_VSID, volume,
+                                   DEFAULT_VOLUME_RAMP_DURATION_MS);
+        if (ret < 0) {
+            ALOGE("%s: csd_volume error %d", __func__, ret);
+        }
+    }
+    return ret;
 }
 
 int platform_set_mic_mute(void *platform, bool state)
@@ -482,30 +1106,70 @@
     struct audio_device *adev = my_data->adev;
     struct mixer_ctl *ctl;
     const char *mixer_ctl_name = "Voice Tx Mute";
-    int values[VOLUME_CTL_PARAM_NUM];
     int ret = 0;
+    uint32_t set_values[ ] = {0,
+                              ALL_SESSION_VSID,
+                              DEFAULT_MUTE_RAMP_DURATION_MS};
 
-    if (adev->mode == AUDIO_MODE_IN_CALL) {
-        ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
-        if (!ctl) {
-            ALOGE("%s: Could not get ctl for mixer cmd - %s",
-                  __func__, mixer_ctl_name);
-            return -EINVAL;
-        }
-        ALOGV("Setting mic mute: %d", state);
-        ret = set_volume_values(MUTE_SET, state, values);
+    if (adev->mode != AUDIO_MODE_IN_CALL)
+        return 0;
+
+    set_values[0] = state;
+    ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
+    if (!ctl) {
+        ALOGE("%s: Could not get ctl for mixer cmd - %s",
+              __func__, mixer_ctl_name);
+        return -EINVAL;
+    }
+    ALOGV("Setting voice mute state: %d", state);
+    mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values));
+
+    if (my_data->csd != NULL) {
+        ret = my_data->csd->mic_mute(ALL_SESSION_VSID, state,
+                                     DEFAULT_MUTE_RAMP_DURATION_MS);
         if (ret < 0) {
-            ALOGV("%s: failed setting mute by incorrect type", __func__);
-            return -EINVAL;
-        }
-        ret = mixer_ctl_set_array(ctl, values, sizeof(values)/sizeof(int));
-        if (ret < 0) {
-            ALOGV("%s: failed set mixer ctl by %d", __func__, ret);
-            return -EINVAL;
+            ALOGE("%s: csd_mic_mute error %d", __func__, ret);
         }
     }
+    return ret;
+}
 
-    return 0;
+int platform_set_device_mute(void *platform, bool state, char *dir)
+{
+    struct platform_data *my_data = (struct platform_data *)platform;
+    struct audio_device *adev = my_data->adev;
+    struct mixer_ctl *ctl;
+    char *mixer_ctl_name = NULL;
+    int ret = 0;
+    uint32_t set_values[ ] = {0,
+                              ALL_SESSION_VSID,
+                              0};
+    if(dir == NULL) {
+        ALOGE("%s: Invalid direction:%s", __func__, dir);
+        return -EINVAL;
+    }
+
+    if (!strncmp("rx", dir, sizeof("rx"))) {
+        mixer_ctl_name = "Voice Rx Device Mute";
+    } else if (!strncmp("tx", dir, sizeof("tx"))) {
+        mixer_ctl_name = "Voice Tx Device Mute";
+    } else {
+        return -EINVAL;
+    }
+
+    set_values[0] = state;
+    ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
+    if (!ctl) {
+        ALOGE("%s: Could not get ctl for mixer cmd - %s",
+              __func__, mixer_ctl_name);
+        return -EINVAL;
+    }
+
+    ALOGV("%s: Setting device mute state: %d, mixer ctrl:%s",
+          __func__,state, mixer_ctl_name);
+    mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values));
+
+    return ret;
 }
 
 snd_device_t platform_get_output_snd_device(void *platform, audio_devices_t devices)
@@ -522,27 +1186,43 @@
         goto exit;
     }
 
-    if (mode == AUDIO_MODE_IN_CALL) {
+    if (voice_is_in_call(adev) || adev->enable_voicerx) {
         if (devices & AUDIO_DEVICE_OUT_WIRED_HEADPHONE ||
-            devices & AUDIO_DEVICE_OUT_WIRED_HEADSET) {
-            if (adev->tty_mode == TTY_MODE_FULL)
+            devices & AUDIO_DEVICE_OUT_WIRED_HEADSET ||
+            devices & AUDIO_DEVICE_OUT_LINE) {
+            if (voice_is_in_call(adev) &&
+                (adev->voice.tty_mode == TTY_MODE_FULL))
                 snd_device = SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES;
-            else if (adev->tty_mode == TTY_MODE_VCO)
+            else if (voice_is_in_call(adev) &&
+                (adev->voice.tty_mode == TTY_MODE_VCO))
                 snd_device = SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES;
-            else if (adev->tty_mode == TTY_MODE_HCO)
+            else if (voice_is_in_call(adev) &&
+                (adev->voice.tty_mode == TTY_MODE_HCO))
                 snd_device = SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET;
-            else
-                snd_device = SND_DEVICE_OUT_VOICE_HEADPHONES;
+            else {
+                if (devices & AUDIO_DEVICE_OUT_LINE)
+                    snd_device = SND_DEVICE_OUT_VOICE_LINE;
+                else
+                    snd_device = SND_DEVICE_OUT_VOICE_HEADPHONES;
+                }
         } else if (devices & AUDIO_DEVICE_OUT_ALL_SCO) {
-            snd_device = SND_DEVICE_OUT_BT_SCO;
-        } else if (devices & AUDIO_DEVICE_OUT_SPEAKER) {
+            if (adev->bt_wb_speech_enabled) {
+                snd_device = SND_DEVICE_OUT_BT_SCO_WB;
+            } else {
+                snd_device = SND_DEVICE_OUT_BT_SCO;
+            }
+        } else if (devices & (AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_SPEAKER_SAFE)) {
             snd_device = SND_DEVICE_OUT_VOICE_SPEAKER;
         } else if (devices & AUDIO_DEVICE_OUT_EARPIECE) {
-            if (is_operator_tmus())
+            if(adev->voice.hac)
+                snd_device = SND_DEVICE_OUT_VOICE_HAC_HANDSET;
+            else if (is_operator_tmus())
                 snd_device = SND_DEVICE_OUT_VOICE_HANDSET_TMUS;
             else
-                snd_device = SND_DEVICE_OUT_HANDSET;
-        }
+                snd_device = SND_DEVICE_OUT_VOICE_HANDSET;
+        } else if (devices & AUDIO_DEVICE_OUT_TELEPHONY_TX)
+            snd_device = SND_DEVICE_OUT_VOICE_TX;
+
         if (snd_device != SND_DEVICE_NONE) {
             goto exit;
         }
@@ -555,6 +1235,9 @@
         } else if (devices == (AUDIO_DEVICE_OUT_WIRED_HEADSET |
                                AUDIO_DEVICE_OUT_SPEAKER)) {
             snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES;
+        } else if (devices == (AUDIO_DEVICE_OUT_LINE |
+                               AUDIO_DEVICE_OUT_SPEAKER)) {
+            snd_device = SND_DEVICE_OUT_SPEAKER_AND_LINE;
         } else if (devices == (AUDIO_DEVICE_OUT_AUX_DIGITAL |
                                AUDIO_DEVICE_OUT_SPEAKER)) {
             snd_device = SND_DEVICE_OUT_SPEAKER_AND_HDMI;
@@ -575,17 +1258,29 @@
     if (devices & AUDIO_DEVICE_OUT_WIRED_HEADPHONE ||
         devices & AUDIO_DEVICE_OUT_WIRED_HEADSET) {
         snd_device = SND_DEVICE_OUT_HEADPHONES;
+    } else if (devices & AUDIO_DEVICE_OUT_LINE) {
+        snd_device = SND_DEVICE_OUT_LINE;
+    } else if (devices & AUDIO_DEVICE_OUT_SPEAKER_SAFE) {
+        snd_device = SND_DEVICE_OUT_SPEAKER_SAFE;
     } else if (devices & AUDIO_DEVICE_OUT_SPEAKER) {
         if (adev->speaker_lr_swap)
             snd_device = SND_DEVICE_OUT_SPEAKER_REVERSE;
         else
             snd_device = SND_DEVICE_OUT_SPEAKER;
     } else if (devices & AUDIO_DEVICE_OUT_ALL_SCO) {
-        snd_device = SND_DEVICE_OUT_BT_SCO;
+        if (adev->bt_wb_speech_enabled) {
+            snd_device = SND_DEVICE_OUT_BT_SCO_WB;
+        } else {
+            snd_device = SND_DEVICE_OUT_BT_SCO;
+        }
     } else if (devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
         snd_device = SND_DEVICE_OUT_HDMI ;
     } else if (devices & AUDIO_DEVICE_OUT_EARPIECE) {
-        snd_device = SND_DEVICE_OUT_HANDSET;
+        /*HAC support for voice-ish audio (eg visual voicemail)*/
+        if(adev->voice.hac)
+            snd_device = SND_DEVICE_OUT_VOICE_HAC_HANDSET;
+        else
+            snd_device = SND_DEVICE_OUT_HANDSET;
     } else {
         ALOGE("%s: Unknown device(s) %#x", __func__, devices);
     }
@@ -608,18 +1303,16 @@
     audio_channel_mask_t channel_mask = (adev->active_input == NULL) ?
                                 AUDIO_CHANNEL_IN_MONO : adev->active_input->channel_mask;
     snd_device_t snd_device = SND_DEVICE_NONE;
+    int channel_count = popcount(channel_mask);
 
     ALOGV("%s: enter: out_device(%#x) in_device(%#x)",
           __func__, out_device, in_device);
-    if (mode == AUDIO_MODE_IN_CALL) {
-        if (out_device == AUDIO_DEVICE_NONE) {
-            ALOGE("%s: No output device set for voice call", __func__);
-            goto exit;
-        }
-        if (adev->tty_mode != TTY_MODE_OFF) {
+    if ((out_device != AUDIO_DEVICE_NONE) && voice_is_in_call(adev)) {
+        if (adev->voice.tty_mode != TTY_MODE_OFF) {
             if (out_device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE ||
-                out_device & AUDIO_DEVICE_OUT_WIRED_HEADSET) {
-                switch (adev->tty_mode) {
+                out_device & AUDIO_DEVICE_OUT_WIRED_HEADSET ||
+                out_device & AUDIO_DEVICE_OUT_LINE) {
+                switch (adev->voice.tty_mode) {
                 case TTY_MODE_FULL:
                     snd_device = SND_DEVICE_IN_VOICE_TTY_FULL_HEADSET_MIC;
                     break;
@@ -630,41 +1323,40 @@
                     snd_device = SND_DEVICE_IN_VOICE_TTY_HCO_HEADSET_MIC;
                     break;
                 default:
-                    ALOGE("%s: Invalid TTY mode (%#x)", __func__, adev->tty_mode);
+                    ALOGE("%s: Invalid TTY mode (%#x)", __func__, adev->voice.tty_mode);
                 }
                 goto exit;
             }
         }
-        if (out_device & AUDIO_DEVICE_OUT_EARPIECE ||
-            out_device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE) {
+        if (out_device & AUDIO_DEVICE_OUT_EARPIECE) {
             if (my_data->fluence_in_voice_call == false) {
                 snd_device = SND_DEVICE_IN_HANDSET_MIC;
             } else {
-                if (my_data->dualmic_config == DUALMIC_CONFIG_ENDFIRE) {
-                    if (is_operator_tmus())
-                        snd_device = SND_DEVICE_IN_VOICE_DMIC_EF_TMUS;
-                    else
-                        snd_device = SND_DEVICE_IN_VOICE_DMIC_EF;
-                } else if(my_data->dualmic_config == DUALMIC_CONFIG_BROADSIDE)
-                    snd_device = SND_DEVICE_IN_VOICE_DMIC_BS;
+                if (is_operator_tmus())
+                    snd_device = SND_DEVICE_IN_VOICE_DMIC_TMUS;
                 else
-                    snd_device = SND_DEVICE_IN_HANDSET_MIC;
+                    snd_device = SND_DEVICE_IN_VOICE_DMIC;
             }
         } else if (out_device & AUDIO_DEVICE_OUT_WIRED_HEADSET) {
             snd_device = SND_DEVICE_IN_VOICE_HEADSET_MIC;
         } else if (out_device & AUDIO_DEVICE_OUT_ALL_SCO) {
-            snd_device = SND_DEVICE_IN_BT_SCO_MIC ;
-        } else if (out_device & AUDIO_DEVICE_OUT_SPEAKER) {
+            if (adev->bt_wb_speech_enabled) {
+                snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB;
+            } else {
+                snd_device = SND_DEVICE_IN_BT_SCO_MIC;
+            }
+        } else if (out_device & AUDIO_DEVICE_OUT_SPEAKER ||
+            out_device & AUDIO_DEVICE_OUT_SPEAKER_SAFE ||
+            out_device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE ||
+            out_device & AUDIO_DEVICE_OUT_LINE) {
             if (my_data->fluence_in_voice_call && my_data->fluence_in_spkr_mode &&
-                    my_data->dualmic_config == DUALMIC_CONFIG_ENDFIRE) {
-                snd_device = SND_DEVICE_IN_VOICE_SPEAKER_DMIC_EF;
-            } else if (my_data->fluence_in_voice_call && my_data->fluence_in_spkr_mode &&
-                    my_data->dualmic_config == DUALMIC_CONFIG_BROADSIDE) {
-                snd_device = SND_DEVICE_IN_VOICE_SPEAKER_DMIC_BS;
+                    my_data->dualmic_config != DUALMIC_CONFIG_NONE) {
+                snd_device = SND_DEVICE_IN_VOICE_SPEAKER_DMIC;
             } else {
                 snd_device = SND_DEVICE_IN_VOICE_SPEAKER_MIC;
             }
-        }
+        } else if (out_device & AUDIO_DEVICE_OUT_TELEPHONY_TX)
+            snd_device = SND_DEVICE_IN_VOICE_RX;
     } else if (source == AUDIO_SOURCE_CAMCORDER) {
         if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC ||
             in_device & AUDIO_DEVICE_IN_BACK_MIC) {
@@ -672,37 +1364,78 @@
         }
     } else if (source == AUDIO_SOURCE_VOICE_RECOGNITION) {
         if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC) {
-            if (my_data->dualmic_config == DUALMIC_CONFIG_ENDFIRE) {
+            if (my_data->dualmic_config != DUALMIC_CONFIG_NONE) {
                 if (channel_mask == AUDIO_CHANNEL_IN_FRONT_BACK)
-                    snd_device = SND_DEVICE_IN_VOICE_REC_DMIC_EF;
-                else if (my_data->fluence_in_voice_rec)
-                    snd_device = SND_DEVICE_IN_VOICE_REC_DMIC_EF_FLUENCE;
-            } else if (my_data->dualmic_config == DUALMIC_CONFIG_BROADSIDE) {
-                if (channel_mask == AUDIO_CHANNEL_IN_FRONT_BACK)
-                    snd_device = SND_DEVICE_IN_VOICE_REC_DMIC_BS;
-                else if (my_data->fluence_in_voice_rec)
-                    snd_device = SND_DEVICE_IN_VOICE_REC_DMIC_BS_FLUENCE;
+                    snd_device = SND_DEVICE_IN_VOICE_REC_DMIC_STEREO;
+                else if (my_data->fluence_in_voice_rec &&
+                         adev->active_input->enable_ns)
+                    snd_device = SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE;
             }
 
             if (snd_device == SND_DEVICE_NONE) {
-                snd_device = SND_DEVICE_IN_VOICE_REC_MIC;
+                if (adev->active_input->enable_ns)
+                    snd_device = SND_DEVICE_IN_VOICE_REC_MIC_NS;
+                else
+                    snd_device = SND_DEVICE_IN_VOICE_REC_MIC;
             }
         }
     } else if (source == AUDIO_SOURCE_VOICE_COMMUNICATION) {
-        if (out_device & AUDIO_DEVICE_OUT_SPEAKER)
+        if (out_device & (AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_SPEAKER_SAFE))
             in_device = AUDIO_DEVICE_IN_BACK_MIC;
         if (adev->active_input) {
-            if (adev->active_input->enable_aec) {
+            if (adev->active_input->enable_aec &&
+                    adev->active_input->enable_ns) {
                 if (in_device & AUDIO_DEVICE_IN_BACK_MIC) {
-                    snd_device = SND_DEVICE_IN_SPEAKER_MIC_AEC;
+                    if (my_data->fluence_in_spkr_mode &&
+                            my_data->fluence_in_voice_comm &&
+                            my_data->dualmic_config != DUALMIC_CONFIG_NONE) {
+                        snd_device = SND_DEVICE_IN_SPEAKER_DMIC_AEC_NS;
+                    } else
+                        snd_device = SND_DEVICE_IN_SPEAKER_MIC_AEC_NS;
                 } else if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC) {
-                    snd_device = SND_DEVICE_IN_HANDSET_MIC_AEC;
+                    if (my_data->fluence_in_voice_comm &&
+                            my_data->dualmic_config != DUALMIC_CONFIG_NONE) {
+                        snd_device = SND_DEVICE_IN_HANDSET_DMIC_AEC_NS;
+                    } else
+                        snd_device = SND_DEVICE_IN_HANDSET_MIC_AEC_NS;
                 } else if (in_device & AUDIO_DEVICE_IN_WIRED_HEADSET) {
                     snd_device = SND_DEVICE_IN_HEADSET_MIC_AEC;
                 }
-                set_echo_reference(adev->mixer, "SLIM_RX");
-            } else
-                set_echo_reference(adev->mixer, "NONE");
+                platform_set_echo_reference(adev, true, out_device);
+            } else if (adev->active_input->enable_aec) {
+                if (in_device & AUDIO_DEVICE_IN_BACK_MIC) {
+                    if (my_data->fluence_in_spkr_mode &&
+                            my_data->fluence_in_voice_comm &&
+                            my_data->dualmic_config != DUALMIC_CONFIG_NONE) {
+                        snd_device = SND_DEVICE_IN_SPEAKER_DMIC_AEC;
+                    } else
+                        snd_device = SND_DEVICE_IN_SPEAKER_MIC_AEC;
+                } else if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC) {
+                    if (my_data->fluence_in_voice_comm &&
+                            my_data->dualmic_config != DUALMIC_CONFIG_NONE) {
+                        snd_device = SND_DEVICE_IN_HANDSET_DMIC_AEC;
+                    } else
+                        snd_device = SND_DEVICE_IN_HANDSET_MIC_AEC;
+               } else if (in_device & AUDIO_DEVICE_IN_WIRED_HEADSET) {
+                   snd_device = SND_DEVICE_IN_HEADSET_MIC_AEC;
+               }
+                platform_set_echo_reference(adev, true, out_device);
+            } else if (adev->active_input->enable_ns) {
+                if (in_device & AUDIO_DEVICE_IN_BACK_MIC) {
+                    if (my_data->fluence_in_spkr_mode &&
+                            my_data->fluence_in_voice_comm &&
+                            my_data->dualmic_config != DUALMIC_CONFIG_NONE) {
+                        snd_device = SND_DEVICE_IN_SPEAKER_DMIC_NS;
+                    } else
+                        snd_device = SND_DEVICE_IN_SPEAKER_MIC_NS;
+                } else if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC) {
+                    if (my_data->fluence_in_voice_comm &&
+                            my_data->dualmic_config != DUALMIC_CONFIG_NONE) {
+                        snd_device = SND_DEVICE_IN_HANDSET_DMIC_NS;
+                    } else
+                        snd_device = SND_DEVICE_IN_HANDSET_MIC_NS;
+                }
+            }
         }
     } else if (source == AUDIO_SOURCE_DEFAULT) {
         goto exit;
@@ -717,13 +1450,25 @@
             !(in_device & AUDIO_DEVICE_IN_VOICE_CALL) &&
             !(in_device & AUDIO_DEVICE_IN_COMMUNICATION)) {
         if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC) {
-            snd_device = SND_DEVICE_IN_HANDSET_MIC;
+            if (my_data->dualmic_config != DUALMIC_CONFIG_NONE &&
+                    channel_count == 2)
+                snd_device = SND_DEVICE_IN_HANDSET_DMIC_STEREO;
+            else
+                snd_device = SND_DEVICE_IN_HANDSET_MIC;
         } else if (in_device & AUDIO_DEVICE_IN_BACK_MIC) {
-            snd_device = SND_DEVICE_IN_SPEAKER_MIC;
+            if (my_data->dualmic_config != DUALMIC_CONFIG_NONE &&
+                    channel_count == 2)
+                snd_device = SND_DEVICE_IN_SPEAKER_DMIC_STEREO;
+            else
+                snd_device = SND_DEVICE_IN_SPEAKER_MIC;
         } else if (in_device & AUDIO_DEVICE_IN_WIRED_HEADSET) {
             snd_device = SND_DEVICE_IN_HEADSET_MIC;
         } else if (in_device & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) {
-            snd_device = SND_DEVICE_IN_BT_SCO_MIC ;
+            if (adev->bt_wb_speech_enabled) {
+                snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB;
+            } else {
+                snd_device = SND_DEVICE_IN_BT_SCO_MIC;
+            }
         } else if (in_device & AUDIO_DEVICE_IN_AUX_DIGITAL) {
             snd_device = SND_DEVICE_IN_HDMI_MIC;
         } else {
@@ -736,12 +1481,20 @@
             snd_device = SND_DEVICE_IN_HANDSET_MIC;
         } else if (out_device & AUDIO_DEVICE_OUT_WIRED_HEADSET) {
             snd_device = SND_DEVICE_IN_HEADSET_MIC;
-        } else if (out_device & AUDIO_DEVICE_OUT_SPEAKER) {
-            snd_device = SND_DEVICE_IN_SPEAKER_MIC;
-        } else if (out_device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE) {
-            snd_device = SND_DEVICE_IN_HANDSET_MIC;
+        } else if (out_device & AUDIO_DEVICE_OUT_SPEAKER ||
+                   out_device & AUDIO_DEVICE_OUT_SPEAKER_SAFE ||
+                   out_device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE ||
+                   out_device & AUDIO_DEVICE_OUT_LINE) {
+            if (channel_count == 2)
+                snd_device = SND_DEVICE_IN_SPEAKER_DMIC_STEREO;
+            else
+                snd_device = SND_DEVICE_IN_SPEAKER_MIC;
         } else if (out_device & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET) {
-            snd_device = SND_DEVICE_IN_BT_SCO_MIC;
+            if (adev->bt_wb_speech_enabled) {
+                snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB;
+            } else {
+                snd_device = SND_DEVICE_IN_BT_SCO_MIC;
+            }
         } else if (out_device & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
             snd_device = SND_DEVICE_IN_HDMI_MIC;
         } else {
@@ -844,6 +1597,92 @@
     return max_channels;
 }
 
+int platform_set_incall_recording_session_id(void *platform,
+                                             uint32_t session_id, int rec_mode)
+{
+    int ret = 0;
+    struct platform_data *my_data = (struct platform_data *)platform;
+    struct audio_device *adev = my_data->adev;
+    struct mixer_ctl *ctl;
+    const char *mixer_ctl_name = "Voc VSID";
+    int num_ctl_values;
+    int i;
+
+    ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
+    if (!ctl) {
+        ALOGE("%s: Could not get ctl for mixer cmd - %s",
+              __func__, mixer_ctl_name);
+        ret = -EINVAL;
+    } else {
+        num_ctl_values = mixer_ctl_get_num_values(ctl);
+        for (i = 0; i < num_ctl_values; i++) {
+            if (mixer_ctl_set_value(ctl, i, session_id)) {
+                ALOGV("Error: invalid session_id: %x", session_id);
+                ret = -EINVAL;
+                break;
+            }
+        }
+    }
+
+    if (my_data->csd != NULL) {
+        ret = my_data->csd->start_record(ALL_SESSION_VSID, rec_mode);
+        if (ret < 0) {
+            ALOGE("%s: csd_client_start_record failed, error %d",
+                  __func__, ret);
+        }
+    }
+
+    return ret;
+}
+
+int platform_stop_incall_recording_usecase(void *platform)
+{
+    int ret = 0;
+    struct platform_data *my_data = (struct platform_data *)platform;
+
+    if (my_data->csd != NULL) {
+        ret = my_data->csd->stop_record(ALL_SESSION_VSID);
+        if (ret < 0) {
+            ALOGE("%s: csd_client_stop_record failed, error %d",
+                  __func__, ret);
+        }
+    }
+
+    return ret;
+}
+
+int platform_start_incall_music_usecase(void *platform)
+{
+    int ret = 0;
+    struct platform_data *my_data = (struct platform_data *)platform;
+
+    if (my_data->csd != NULL) {
+        ret = my_data->csd->start_playback(ALL_SESSION_VSID);
+        if (ret < 0) {
+            ALOGE("%s: csd_client_start_playback failed, error %d",
+                  __func__, ret);
+        }
+    }
+
+    return ret;
+}
+
+int platform_stop_incall_music_usecase(void *platform)
+{
+    int ret = 0;
+    struct platform_data *my_data = (struct platform_data *)platform;
+
+    if (my_data->csd != NULL) {
+        ret = my_data->csd->stop_playback(ALL_SESSION_VSID);
+        if (ret < 0) {
+            ALOGE("%s: csd_client_stop_playback failed, error %d",
+                  __func__, ret);
+        }
+    }
+
+    return ret;
+}
+
 /* Delay in Us */
 int64_t platform_render_latency(audio_usecase_t usecase)
 {
@@ -856,3 +1695,40 @@
             return 0;
     }
 }
+
+int platform_set_snd_device_backend(snd_device_t device, const char *backend)
+{
+    int ret = 0;
+
+    if ((device < SND_DEVICE_MIN) || (device >= SND_DEVICE_MAX)) {
+        ALOGE("%s: Invalid snd_device = %d",
+            __func__, device);
+        ret = -EINVAL;
+        goto done;
+    }
+
+    if (backend_table[device]) {
+        free(backend_table[device]);
+    }
+    backend_table[device] = strdup(backend);
+done:
+    return ret;
+}
+
+int platform_set_usecase_pcm_id(audio_usecase_t usecase, int32_t type, int32_t pcm_id)
+{
+    int ret = 0;
+    if ((usecase <= USECASE_INVALID) || (usecase >= AUDIO_USECASE_MAX)) {
+        ALOGE("%s: invalid usecase case idx %d", __func__, usecase);
+        ret = -EINVAL;
+        goto done;
+    }
+
+    if ((type != 0) && (type != 1)) {
+        ALOGE("%s: invalid usecase type", __func__);
+        ret = -EINVAL;
+    }
+    pcm_device_table[usecase][type] = pcm_id;
+done:
+    return ret;
+}
diff --git a/hal/msm8974/platform.h b/hal/msm8974/platform.h
index a6b3c31..4dc83bb 100644
--- a/hal/msm8974/platform.h
+++ b/hal/msm8974/platform.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2013-2014 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@
  */
 #define AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND \
     (AUDIO_DEVICE_OUT_EARPIECE | AUDIO_DEVICE_OUT_SPEAKER | \
+     AUDIO_DEVICE_OUT_SPEAKER_SAFE | \
      AUDIO_DEVICE_OUT_WIRED_HEADSET | AUDIO_DEVICE_OUT_WIRED_HEADPHONE)
 
 /* Sound devices specific to the platform
@@ -39,18 +40,25 @@
     SND_DEVICE_OUT_HANDSET = SND_DEVICE_OUT_BEGIN,
     SND_DEVICE_OUT_SPEAKER,
     SND_DEVICE_OUT_SPEAKER_REVERSE,
+    SND_DEVICE_OUT_SPEAKER_SAFE,
     SND_DEVICE_OUT_HEADPHONES,
+    SND_DEVICE_OUT_LINE,
     SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES,
+    SND_DEVICE_OUT_SPEAKER_AND_LINE,
     SND_DEVICE_OUT_VOICE_HANDSET,
     SND_DEVICE_OUT_VOICE_SPEAKER,
     SND_DEVICE_OUT_VOICE_HEADPHONES,
+    SND_DEVICE_OUT_VOICE_LINE,
     SND_DEVICE_OUT_HDMI,
     SND_DEVICE_OUT_SPEAKER_AND_HDMI,
     SND_DEVICE_OUT_BT_SCO,
+    SND_DEVICE_OUT_BT_SCO_WB,
     SND_DEVICE_OUT_VOICE_HANDSET_TMUS,
     SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES,
     SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES,
     SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET,
+    SND_DEVICE_OUT_VOICE_HAC_HANDSET,
+    SND_DEVICE_OUT_VOICE_TX,
     SND_DEVICE_OUT_END,
 
     /*
@@ -60,46 +68,80 @@
     /* Capture devices */
     SND_DEVICE_IN_BEGIN = SND_DEVICE_OUT_END,
     SND_DEVICE_IN_HANDSET_MIC  = SND_DEVICE_IN_BEGIN,
-    SND_DEVICE_IN_SPEAKER_MIC,
-    SND_DEVICE_IN_HEADSET_MIC,
     SND_DEVICE_IN_HANDSET_MIC_AEC,
+    SND_DEVICE_IN_HANDSET_MIC_NS,
+    SND_DEVICE_IN_HANDSET_MIC_AEC_NS,
+    SND_DEVICE_IN_HANDSET_DMIC,
+    SND_DEVICE_IN_HANDSET_DMIC_AEC,
+    SND_DEVICE_IN_HANDSET_DMIC_NS,
+    SND_DEVICE_IN_HANDSET_DMIC_AEC_NS,
+    SND_DEVICE_IN_HANDSET_DMIC_STEREO,
+
+    SND_DEVICE_IN_SPEAKER_MIC,
     SND_DEVICE_IN_SPEAKER_MIC_AEC,
+    SND_DEVICE_IN_SPEAKER_MIC_NS,
+    SND_DEVICE_IN_SPEAKER_MIC_AEC_NS,
+    SND_DEVICE_IN_SPEAKER_DMIC,
+    SND_DEVICE_IN_SPEAKER_DMIC_AEC,
+    SND_DEVICE_IN_SPEAKER_DMIC_NS,
+    SND_DEVICE_IN_SPEAKER_DMIC_AEC_NS,
+    SND_DEVICE_IN_SPEAKER_DMIC_STEREO,
+
+    SND_DEVICE_IN_HEADSET_MIC,
     SND_DEVICE_IN_HEADSET_MIC_AEC,
-    SND_DEVICE_IN_VOICE_SPEAKER_MIC,
-    SND_DEVICE_IN_VOICE_HEADSET_MIC,
+
     SND_DEVICE_IN_HDMI_MIC,
     SND_DEVICE_IN_BT_SCO_MIC,
+    SND_DEVICE_IN_BT_SCO_MIC_WB,
+
     SND_DEVICE_IN_CAMCORDER_MIC,
-    SND_DEVICE_IN_VOICE_DMIC_EF,
-    SND_DEVICE_IN_VOICE_DMIC_BS,
-    SND_DEVICE_IN_VOICE_DMIC_EF_TMUS,
-    SND_DEVICE_IN_VOICE_SPEAKER_DMIC_EF,
-    SND_DEVICE_IN_VOICE_SPEAKER_DMIC_BS,
+
+    SND_DEVICE_IN_VOICE_DMIC,
+    SND_DEVICE_IN_VOICE_DMIC_TMUS,
+    SND_DEVICE_IN_VOICE_SPEAKER_MIC,
+    SND_DEVICE_IN_VOICE_SPEAKER_DMIC,
+    SND_DEVICE_IN_VOICE_HEADSET_MIC,
     SND_DEVICE_IN_VOICE_TTY_FULL_HEADSET_MIC,
     SND_DEVICE_IN_VOICE_TTY_VCO_HANDSET_MIC,
     SND_DEVICE_IN_VOICE_TTY_HCO_HEADSET_MIC,
+
     SND_DEVICE_IN_VOICE_REC_MIC,
-    SND_DEVICE_IN_VOICE_REC_DMIC_EF,
-    SND_DEVICE_IN_VOICE_REC_DMIC_BS,
-    SND_DEVICE_IN_VOICE_REC_DMIC_EF_FLUENCE,
-    SND_DEVICE_IN_VOICE_REC_DMIC_BS_FLUENCE,
+    SND_DEVICE_IN_VOICE_REC_MIC_NS,
+    SND_DEVICE_IN_VOICE_REC_DMIC_STEREO,
+    SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE,
+
+    SND_DEVICE_IN_VOICE_RX,
+
     SND_DEVICE_IN_END,
 
     SND_DEVICE_MAX = SND_DEVICE_IN_END,
 
 };
 
-#define MIXER_CARD 0
-#define SOUND_CARD 0
-
 #define DEFAULT_OUTPUT_SAMPLING_RATE 48000
 
-#define ALL_SESSION_VSID    0xFFFFFFFF
-#define DEFAULT_MUTE_RAMP_DURATION      500
+#define ALL_SESSION_VSID                0xFFFFFFFF
+#define DEFAULT_MUTE_RAMP_DURATION_MS   20
 #define DEFAULT_VOLUME_RAMP_DURATION_MS 20
-#define VOLUME_SET 0
-#define MUTE_SET 1
-#define VOLUME_CTL_PARAM_NUM 3
+
+#ifdef PLATFORM_MSM8084
+#define ACDB_ID_VOICE_SPEAKER 66
+#define ACDB_ID_VOICE_HANDSET 67
+#define ACDB_ID_VOICE_HANDSET_TMUS 67
+#define ACDB_ID_VOICE_DMIC_EF_TMUS 89
+#define ACDB_ID_HEADSET_MIC_AEC 47
+#else
+#define ACDB_ID_VOICE_SPEAKER 15
+#define ACDB_ID_VOICE_HANDSET 7
+#define ACDB_ID_VOICE_HANDSET_TMUS 88
+#define ACDB_ID_VOICE_DMIC_EF_TMUS 89
+#define ACDB_ID_HEADSET_MIC_AEC 10
+#endif
+
+#define MAX_VOL_INDEX 5
+#define MIN_VOL_INDEX 0
+#define percent_to_index(val, min, max) \
+            ((val) * ((max) - (min)) * 0.01 + (min) + .5)
 
 /*
  * tinyAlsa library interprets period size as number of frames
@@ -122,4 +164,79 @@
 #define AUDIO_CAPTURE_PERIOD_DURATION_MSEC 20
 #define AUDIO_CAPTURE_PERIOD_COUNT 2
 
+#define LOW_LATENCY_CAPTURE_SAMPLE_RATE 48000
+#define LOW_LATENCY_CAPTURE_PERIOD_SIZE 240
+#define LOW_LATENCY_CAPTURE_USE_CASE 1
+
+#define DEEP_BUFFER_PCM_DEVICE 0
+#define AUDIO_RECORD_PCM_DEVICE 0
+#define MULTIMEDIA2_PCM_DEVICE 1
+#define PLAYBACK_OFFLOAD_DEVICE 9
+#define LOWLATENCY_PCM_DEVICE 15
+#define VOICE_VSID  0x10C01000
+#ifdef PLATFORM_MSM8084
+#define VOICE_CALL_PCM_DEVICE 20
+#define VOICE2_CALL_PCM_DEVICE 25
+#define VOLTE_CALL_PCM_DEVICE 21
+#define QCHAT_CALL_PCM_DEVICE 33
+#define VOWLAN_CALL_PCM_DEVICE -1
+#else
+#define VOICE_CALL_PCM_DEVICE 2
+#define VOICE2_CALL_PCM_DEVICE 22
+#define VOLTE_CALL_PCM_DEVICE 14
+#define QCHAT_CALL_PCM_DEVICE 20
+#define VOWLAN_CALL_PCM_DEVICE 36
+#endif
+
+#define AFE_PROXY_PLAYBACK_PCM_DEVICE 7
+#define AFE_PROXY_RECORD_PCM_DEVICE 8
+
+#define HFP_PCM_RX 5
+#ifdef PLATFORM_MSM8x26
+#define HFP_SCO_RX 28
+#define HFP_ASM_RX_TX 29
+#else
+#define HFP_SCO_RX 23
+#define HFP_ASM_RX_TX 24
+#endif
+
+#define LIB_CSD_CLIENT "libcsd-client.so"
+#define LIB_MDM_DETECT "libmdmdetect.so"
+
+/* CSD-CLIENT related functions */
+typedef int (*init_t)(bool);
+typedef int (*deinit_t)();
+typedef int (*disable_device_t)();
+typedef int (*enable_device_config_t)(int, int);
+typedef int (*enable_device_t)(int, int, uint32_t);
+typedef int (*volume_t)(uint32_t, int, uint16_t);
+typedef int (*mic_mute_t)(uint32_t, int, uint16_t);
+typedef int (*slow_talk_t)(uint32_t, uint8_t);
+typedef int (*start_voice_t)(uint32_t);
+typedef int (*stop_voice_t)(uint32_t);
+typedef int (*start_playback_t)(uint32_t);
+typedef int (*stop_playback_t)(uint32_t);
+typedef int (*start_record_t)(uint32_t, int);
+typedef int (*stop_record_t)(uint32_t);
+typedef int (*get_sample_rate_t)(uint32_t *);
+/* CSD Client structure */
+struct csd_data {
+    void *csd_client;
+    init_t init;
+    deinit_t deinit;
+    disable_device_t disable_device;
+    enable_device_config_t enable_device_config;
+    enable_device_t enable_device;
+    volume_t volume;
+    mic_mute_t mic_mute;
+    slow_talk_t slow_talk;
+    start_voice_t start_voice;
+    stop_voice_t stop_voice;
+    start_playback_t start_playback;
+    stop_playback_t stop_playback;
+    start_record_t start_record;
+    stop_record_t stop_record;
+    get_sample_rate_t get_sample_rate;
+};
+
 #endif // QCOM_AUDIO_PLATFORM_H
diff --git a/hal/platform_api.h b/hal/platform_api.h
index afd2ee4..e50e06d 100644
--- a/hal/platform_api.h
+++ b/hal/platform_api.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2013-2014 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,23 +14,34 @@
  * limitations under the License.
  */
 
-#ifndef QCOM_AUDIO_PLATFORM_API_H
-#define QCOM_AUDIO_PLATFORM_API_H
+#ifndef AUDIO_PLATFORM_API_H
+#define AUDIO_PLATFORM_API_H
 
 void *platform_init(struct audio_device *adev);
 void platform_deinit(void *platform);
 const char *platform_get_snd_device_name(snd_device_t snd_device);
-void platform_add_backend_name(char *mixer_path, snd_device_t snd_device);
+void platform_add_backend_name(void *platform, char *mixer_path,
+                                                    snd_device_t snd_device);
 int platform_get_pcm_device_id(audio_usecase_t usecase, int device_type);
+int platform_get_snd_device_index(char *snd_device_index_name);
+int platform_set_snd_device_acdb_id(snd_device_t snd_device, unsigned int acdb_id);
 int platform_send_audio_calibration(void *platform, snd_device_t snd_device);
 int platform_switch_voice_call_device_pre(void *platform);
+int platform_switch_voice_call_enable_device_config(void *platform,
+                                                    snd_device_t out_snd_device,
+                                                    snd_device_t in_snd_device);
 int platform_switch_voice_call_device_post(void *platform,
                                            snd_device_t out_snd_device,
                                            snd_device_t in_snd_device);
-int platform_start_voice_call(void *platform);
-int platform_stop_voice_call(void *platform);
+int platform_switch_voice_call_usecase_route_post(void *platform,
+                                                  snd_device_t out_snd_device,
+                                                  snd_device_t in_snd_device);
+int platform_start_voice_call(void *platform, uint32_t vsid);
+int platform_stop_voice_call(void *platform, uint32_t vsid);
 int platform_set_voice_volume(void *platform, int volume);
 int platform_set_mic_mute(void *platform, bool state);
+int platform_get_sample_rate(void *platform, uint32_t *rate);
+int platform_set_device_mute(void *platform, bool state, char *dir);
 snd_device_t platform_get_output_snd_device(void *platform, audio_devices_t devices);
 snd_device_t platform_get_input_snd_device(void *platform, audio_devices_t out_device);
 int platform_set_hdmi_channels(void *platform, int channel_count);
@@ -39,4 +50,19 @@
 /* returns the latency for a usecase in Us */
 int64_t platform_render_latency(audio_usecase_t usecase);
 
-#endif // QCOM_AUDIO_PLATFORM_API_H
+int platform_set_incall_recording_session_id(void *platform,
+                                             uint32_t session_id, int rec_mode);
+int platform_stop_incall_recording_usecase(void *platform);
+int platform_start_incall_music_usecase(void *platform);
+int platform_stop_incall_music_usecase(void *platform);
+
+int platform_set_snd_device_backend(snd_device_t snd_device, const char * backend);
+
+/* From platform_info_parser.c */
+int platform_info_init(void);
+
+int platform_get_usecase_index(const char * usecase);
+int platform_set_usecase_pcm_id(audio_usecase_t usecase, int32_t type, int32_t pcm_id);
+void platform_set_echo_reference(struct audio_device *adev, bool enable, audio_devices_t out_device);
+
+#endif // AUDIO_PLATFORM_API_H
diff --git a/hal/platform_info.c b/hal/platform_info.c
new file mode 100644
index 0000000..832c0f0
--- /dev/null
+++ b/hal/platform_info.c
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+#define LOG_TAG "platform_info"
+#define LOG_NDDEBUG 0
+
+#include <errno.h>
+#include <stdio.h>
+#include <expat.h>
+#include <cutils/log.h>
+#include <audio_hw.h>
+#include "platform_api.h"
+#include <platform.h>
+
+#define PLATFORM_INFO_XML_PATH      "/system/etc/audio_platform_info.xml"
+
+typedef enum {
+    ROOT,
+    ACDB,
+    PCM_ID,
+    BACKEND_NAME,
+} section_t;
+
+typedef void (* section_process_fn)(const XML_Char **attr);
+
+static void process_acdb_id(const XML_Char **attr);
+static void process_pcm_id(const XML_Char **attr);
+static void process_backend_name(const XML_Char **attr);
+static void process_root(const XML_Char **attr);
+
+static section_process_fn section_table[] = {
+    [ROOT] = process_root,
+    [ACDB] = process_acdb_id,
+    [PCM_ID] = process_pcm_id,
+    [BACKEND_NAME] = process_backend_name,
+};
+
+static section_t section;
+
+/*
+ * <audio_platform_info>
+ * <acdb_ids>
+ * <device name="???" acdb_id="???"/>
+ * ...
+ * ...
+ * </acdb_ids>
+ * <backend_names>
+ * <device name="???" backend="???"/>
+ * ...
+ * ...
+ * </backend_names>
+ * <pcm_ids>
+ * <usecase name="???" type="in/out" id="???"/>
+ * ...
+ * ...
+ * </pcm_ids>
+ * </audio_platform_info>
+ */
+
+static void process_root(const XML_Char **attr __unused)
+{
+}
+
+/* mapping from usecase to pcm dev id */
+static void process_pcm_id(const XML_Char **attr)
+{
+    int index;
+
+    if (strcmp(attr[0], "name") != 0) {
+        ALOGE("%s: 'name' not found, no ACDB ID set!", __func__);
+        goto done;
+    }
+
+    index = platform_get_usecase_index((char *)attr[1]);
+    if (index < 0) {
+        ALOGE("%s: usecase %s in %s not found!",
+              __func__, attr[1], PLATFORM_INFO_XML_PATH);
+        goto done;
+    }
+
+    if (strcmp(attr[2], "type") != 0) {
+        ALOGE("%s: usecase type not mentioned", __func__);
+        goto done;
+    }
+
+    int type = -1;
+
+    if (!strcasecmp((char *)attr[3], "in")) {
+        type = 1;
+    } else if (!strcasecmp((char *)attr[3], "out")) {
+        type = 0;
+    } else {
+        ALOGE("%s: type must be IN or OUT", __func__);
+        goto done;
+    }
+
+    if (strcmp(attr[4], "id") != 0) {
+        ALOGE("%s: usecase id not mentioned", __func__);
+        goto done;
+    }
+
+    int id = atoi((char *)attr[5]);
+
+    if (platform_set_usecase_pcm_id(index, type, id) < 0) {
+        ALOGE("%s: usecase %s in %s, type %d id %d was not set!",
+              __func__, attr[1], PLATFORM_INFO_XML_PATH, type, id);
+        goto done;
+    }
+
+done:
+    return;
+}
+
+/* backend to be used for a device */
+static void process_backend_name(const XML_Char **attr)
+{
+    int index;
+
+    if (strcmp(attr[0], "name") != 0) {
+        ALOGE("%s: 'name' not found, no ACDB ID set!", __func__);
+        goto done;
+    }
+
+    index = platform_get_snd_device_index((char *)attr[1]);
+    if (index < 0) {
+        ALOGE("%s: Device %s in %s not found, no ACDB ID set!",
+              __func__, attr[1], PLATFORM_INFO_XML_PATH);
+        goto done;
+    }
+
+    if (strcmp(attr[2], "backend") != 0) {
+        ALOGE("%s: Device %s in %s has no backed set!",
+              __func__, attr[1], PLATFORM_INFO_XML_PATH);
+        goto done;
+    }
+
+    if (platform_set_snd_device_backend(index, attr[3]) < 0) {
+        ALOGE("%s: Device %s in %s, backend %s was not set!",
+              __func__, attr[1], PLATFORM_INFO_XML_PATH, attr[3]);
+        goto done;
+    }
+
+done:
+    return;
+}
+
+static void process_acdb_id(const XML_Char **attr)
+{
+    int index;
+
+    if (strcmp(attr[0], "name") != 0) {
+        ALOGE("%s: 'name' not found, no ACDB ID set!", __func__);
+        goto done;
+    }
+
+    index = platform_get_snd_device_index((char *)attr[1]);
+    if (index < 0) {
+        ALOGE("%s: Device %s in %s not found, no ACDB ID set!",
+              __func__, attr[1], PLATFORM_INFO_XML_PATH);
+        goto done;
+    }
+
+    if (strcmp(attr[2], "acdb_id") != 0) {
+        ALOGE("%s: Device %s in %s has no acdb_id, no ACDB ID set!",
+              __func__, attr[1], PLATFORM_INFO_XML_PATH);
+        goto done;
+    }
+
+    if (platform_set_snd_device_acdb_id(index, atoi((char *)attr[3])) < 0) {
+        ALOGE("%s: Device %s in %s, ACDB ID %d was not set!",
+              __func__, attr[1], PLATFORM_INFO_XML_PATH, atoi((char *)attr[3]));
+        goto done;
+    }
+
+done:
+    return;
+}
+
+static void start_tag(void *userdata __unused, const XML_Char *tag_name,
+                      const XML_Char **attr)
+{
+    const XML_Char              *attr_name = NULL;
+    const XML_Char              *attr_value = NULL;
+    unsigned int                i;
+
+    if (strcmp(tag_name, "acdb_ids") == 0) {
+        section = ACDB;
+    } else if (strcmp(tag_name, "pcm_ids") == 0) {
+        section = PCM_ID;
+    } else if (strcmp(tag_name, "backend_names") == 0) {
+        section = BACKEND_NAME;
+    } else if (strcmp(tag_name, "device") == 0) {
+        if ((section != ACDB) && (section != BACKEND_NAME)) {
+            ALOGE("device tag only supported for acdb/backend names");
+            return;
+        }
+
+        /* call into process function for the current section */
+        section_process_fn fn = section_table[section];
+        fn(attr);
+    } else if (strcmp(tag_name, "usecase") == 0) {
+        if (section != PCM_ID) {
+            ALOGE("usecase tag only supported with PCM_ID section");
+            return;
+        }
+
+        section_process_fn fn = section_table[PCM_ID];
+        fn(attr);
+    }
+
+    return;
+}
+
+static void end_tag(void *userdata __unused, const XML_Char *tag_name)
+{
+    if (strcmp(tag_name, "acdb_ids") == 0) {
+        section = ROOT;
+    } else if (strcmp(tag_name, "pcm_ids") == 0) {
+        section = ROOT;
+    } else if (strcmp(tag_name, "backend_names") == 0) {
+        section = ROOT;
+    }
+}
+
+int platform_info_init(void)
+{
+    XML_Parser      parser;
+    FILE            *file;
+    int             ret = 0;
+    int             bytes_read;
+    void            *buf;
+    static const uint32_t kBufSize = 1024;
+
+    section = ROOT;
+
+    file = fopen(PLATFORM_INFO_XML_PATH, "r");
+    if (!file) {
+        ALOGD("%s: Failed to open %s, using defaults.",
+            __func__, PLATFORM_INFO_XML_PATH);
+        ret = -ENODEV;
+        goto done;
+    }
+
+    parser = XML_ParserCreate(NULL);
+    if (!parser) {
+        ALOGE("%s: Failed to create XML parser!", __func__);
+        ret = -ENODEV;
+        goto err_close_file;
+    }
+
+    XML_SetElementHandler(parser, start_tag, end_tag);
+
+    while (1) {
+        buf = XML_GetBuffer(parser, kBufSize);
+        if (buf == NULL) {
+            ALOGE("%s: XML_GetBuffer failed", __func__);
+            ret = -ENOMEM;
+            goto err_free_parser;
+        }
+
+        bytes_read = fread(buf, 1, kBufSize, file);
+        if (bytes_read < 0) {
+            ALOGE("%s: fread failed, bytes read = %d", __func__, bytes_read);
+             ret = bytes_read;
+            goto err_free_parser;
+        }
+
+        if (XML_ParseBuffer(parser, bytes_read,
+                            bytes_read == 0) == XML_STATUS_ERROR) {
+            ALOGE("%s: XML_ParseBuffer failed, for %s",
+                __func__, PLATFORM_INFO_XML_PATH);
+            ret = -EINVAL;
+            goto err_free_parser;
+        }
+
+        if (bytes_read == 0)
+            break;
+    }
+
+err_free_parser:
+    XML_ParserFree(parser);
+err_close_file:
+    fclose(file);
+done:
+    return ret;
+}
diff --git a/hal/voice.c b/hal/voice.c
new file mode 100644
index 0000000..044dc28
--- /dev/null
+++ b/hal/voice.c
@@ -0,0 +1,471 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "voice"
+/*#define LOG_NDEBUG 0*/
+#define LOG_NDDEBUG 0
+
+#include <errno.h>
+#include <math.h>
+#include <cutils/log.h>
+#include <cutils/str_parms.h>
+
+#include "audio_hw.h"
+#include "voice.h"
+#include "voice_extn/voice_extn.h"
+#include "platform.h"
+#include "platform_api.h"
+
+struct pcm_config pcm_config_voice_call = {
+    .channels = 1,
+    .rate = 8000,
+    .period_size = 160,
+    .period_count = 2,
+    .format = PCM_FORMAT_S16_LE,
+};
+
+static struct voice_session *voice_get_session_from_use_case(struct audio_device *adev,
+                              audio_usecase_t usecase_id)
+{
+    struct voice_session *session = NULL;
+    int ret = 0;
+
+    ret = voice_extn_get_session_from_use_case(adev, usecase_id, &session);
+    if (ret == -ENOSYS) {
+        session = &adev->voice.session[VOICE_SESS_IDX];
+    }
+
+    return session;
+}
+
+int voice_stop_usecase(struct audio_device *adev, audio_usecase_t usecase_id)
+{
+    int i, ret = 0;
+    struct audio_usecase *uc_info;
+    struct voice_session *session = NULL;
+
+    ALOGD("%s: enter usecase:%s", __func__, use_case_table[usecase_id]);
+
+    session = (struct voice_session *)voice_get_session_from_use_case(adev, usecase_id);
+    session->state.current = CALL_INACTIVE;
+
+    ret = platform_stop_voice_call(adev->platform, session->vsid);
+
+    /* 1. Close the PCM devices */
+    if (session->pcm_rx) {
+        pcm_close(session->pcm_rx);
+        session->pcm_rx = NULL;
+    }
+    if (session->pcm_tx) {
+        pcm_close(session->pcm_tx);
+        session->pcm_tx = NULL;
+    }
+
+    uc_info = get_usecase_from_list(adev, usecase_id);
+    if (uc_info == NULL) {
+        ALOGE("%s: Could not find the usecase (%d) in the list",
+              __func__, usecase_id);
+        return -EINVAL;
+    }
+
+    /* 2. Get and set stream specific mixer controls */
+    disable_audio_route(adev, uc_info);
+
+    /* 3. Disable the rx and tx devices */
+    disable_snd_device(adev, uc_info->out_snd_device);
+    disable_snd_device(adev, uc_info->in_snd_device);
+
+    list_remove(&uc_info->list);
+    free(uc_info);
+
+    ALOGD("%s: exit: status(%d)", __func__, ret);
+    return ret;
+}
+
+int voice_start_usecase(struct audio_device *adev, audio_usecase_t usecase_id)
+{
+    int i, ret = 0;
+    struct audio_usecase *uc_info;
+    int pcm_dev_rx_id, pcm_dev_tx_id;
+    struct voice_session *session = NULL;
+    struct pcm_config voice_config = pcm_config_voice_call;
+
+    ALOGD("%s: enter usecase:%s", __func__, use_case_table[usecase_id]);
+
+    session = (struct voice_session *)voice_get_session_from_use_case(adev, usecase_id);
+    uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
+    uc_info->id = usecase_id;
+    uc_info->type = VOICE_CALL;
+    uc_info->stream.out = adev->current_call_output ;
+    uc_info->devices = adev->current_call_output ->devices;
+    uc_info->in_snd_device = SND_DEVICE_NONE;
+    uc_info->out_snd_device = SND_DEVICE_NONE;
+
+    list_add_tail(&adev->usecase_list, &uc_info->list);
+
+    select_devices(adev, usecase_id);
+
+    pcm_dev_rx_id = platform_get_pcm_device_id(uc_info->id, PCM_PLAYBACK);
+    pcm_dev_tx_id = platform_get_pcm_device_id(uc_info->id, PCM_CAPTURE);
+
+    if (pcm_dev_rx_id < 0 || pcm_dev_tx_id < 0) {
+        ALOGE("%s: Invalid PCM devices (rx: %d tx: %d) for the usecase(%d)",
+              __func__, pcm_dev_rx_id, pcm_dev_tx_id, uc_info->id);
+        ret = -EIO;
+        goto error_start_voice;
+    }
+
+    ALOGV("%s: Opening PCM playback device card_id(%d) device_id(%d)",
+          __func__, adev->snd_card, pcm_dev_rx_id);
+    session->pcm_rx = pcm_open(adev->snd_card,
+                               pcm_dev_rx_id,
+                               PCM_OUT, &voice_config);
+    if (session->pcm_rx && !pcm_is_ready(session->pcm_rx)) {
+        ALOGE("%s: %s", __func__, pcm_get_error(session->pcm_rx));
+        ret = -EIO;
+        goto error_start_voice;
+    }
+
+    ALOGV("%s: Opening PCM capture device card_id(%d) device_id(%d)",
+          __func__, adev->snd_card, pcm_dev_tx_id);
+    session->pcm_tx = pcm_open(adev->snd_card,
+                               pcm_dev_tx_id,
+                               PCM_IN, &voice_config);
+    if (session->pcm_tx && !pcm_is_ready(session->pcm_tx)) {
+        ALOGE("%s: %s", __func__, pcm_get_error(session->pcm_tx));
+        ret = -EIO;
+        goto error_start_voice;
+    }
+    pcm_start(session->pcm_rx);
+    pcm_start(session->pcm_tx);
+
+    voice_set_volume(adev, adev->voice.volume);
+
+    ret = platform_start_voice_call(adev->platform, session->vsid);
+    if (ret < 0) {
+        ALOGE("%s: platform_start_voice_call error %d\n", __func__, ret);
+        goto error_start_voice;
+    }
+
+    session->state.current = CALL_ACTIVE;
+    goto done;
+
+error_start_voice:
+    voice_stop_usecase(adev, usecase_id);
+
+done:
+    ALOGD("%s: exit: status(%d)", __func__, ret);
+    return ret;
+}
+
+bool voice_is_call_state_active(struct audio_device *adev)
+{
+    bool call_state = false;
+    int ret = 0;
+
+    ret = voice_extn_is_call_state_active(adev, &call_state);
+    if (ret == -ENOSYS) {
+        call_state = (adev->voice.session[VOICE_SESS_IDX].state.current == CALL_ACTIVE) ? true : false;
+    }
+
+    return call_state;
+}
+
+bool voice_is_in_call(struct audio_device *adev)
+{
+    return adev->voice.in_call;
+}
+
+bool voice_is_in_call_rec_stream(struct stream_in *in)
+{
+    bool in_call_rec = false;
+    int ret = 0;
+
+    ret = voice_extn_is_in_call_rec_stream(in, &in_call_rec);
+    if (ret == -ENOSYS) {
+        in_call_rec = false;
+    }
+
+    return in_call_rec;
+}
+
+uint32_t voice_get_active_session_id(struct audio_device *adev)
+{
+    int ret = 0;
+    uint32_t session_id;
+
+    ret = voice_extn_get_active_session_id(adev, &session_id);
+    if (ret == -ENOSYS) {
+        session_id = VOICE_VSID;
+    }
+    return session_id;
+}
+
+int voice_check_and_set_incall_rec_usecase(struct audio_device *adev,
+                                           struct stream_in *in)
+{
+    int ret = 0;
+    uint32_t session_id;
+    int usecase_id;
+    int rec_mode = INCALL_REC_NONE;
+
+    if (voice_is_call_state_active(adev)) {
+        switch (in->source) {
+        case AUDIO_SOURCE_VOICE_UPLINK:
+            in->usecase = USECASE_INCALL_REC_UPLINK;
+            rec_mode = INCALL_REC_UPLINK;
+            break;
+        case AUDIO_SOURCE_VOICE_DOWNLINK:
+            in->usecase = USECASE_INCALL_REC_DOWNLINK;
+            rec_mode = INCALL_REC_DOWNLINK;
+            break;
+        case AUDIO_SOURCE_VOICE_CALL:
+            in->usecase = USECASE_INCALL_REC_UPLINK_AND_DOWNLINK;
+            rec_mode = INCALL_REC_UPLINK_AND_DOWNLINK;
+            break;
+        default:
+            ALOGV("%s: Source type %d doesnt match incall recording criteria",
+                  __func__, in->source);
+            return ret;
+        }
+
+        session_id = voice_get_active_session_id(adev);
+        ret = platform_set_incall_recording_session_id(adev->platform,
+                                                       session_id, rec_mode);
+        ALOGV("%s: Update usecase to %d",__func__, in->usecase);
+    } else {
+        ALOGV("%s: voice call not active", __func__);
+    }
+
+    return ret;
+}
+
+int voice_check_and_stop_incall_rec_usecase(struct audio_device *adev,
+                                            struct stream_in *in)
+{
+    int ret = 0;
+
+    if (in->source == AUDIO_SOURCE_VOICE_UPLINK ||
+        in->source == AUDIO_SOURCE_VOICE_DOWNLINK ||
+        in->source == AUDIO_SOURCE_VOICE_CALL) {
+        ret = platform_stop_incall_recording_usecase(adev->platform);
+        ALOGV("%s: Stop In-call recording", __func__);
+    }
+
+    return ret;
+}
+
+int voice_check_and_set_incall_music_usecase(struct audio_device *adev,
+                                             struct stream_out *out)
+{
+    int ret = 0;
+
+    ret = voice_extn_check_and_set_incall_music_usecase(adev, out);
+    if (ret == -ENOSYS) {
+        /* Incall music delivery is used only for LCH call state */
+        ret = -EINVAL;
+    }
+
+    return ret;
+}
+
+int voice_set_mic_mute(struct audio_device *adev, bool state)
+{
+    int err = 0;
+
+    adev->voice.mic_mute = state;
+    if (adev->mode == AUDIO_MODE_IN_CALL)
+        err = platform_set_mic_mute(adev->platform, state);
+
+    return err;
+}
+
+bool voice_get_mic_mute(struct audio_device *adev)
+{
+    return adev->voice.mic_mute;
+}
+
+int voice_set_volume(struct audio_device *adev, float volume)
+{
+    int vol, err = 0;
+
+    adev->voice.volume = volume;
+    if (adev->mode == AUDIO_MODE_IN_CALL) {
+        if (volume < 0.0) {
+            volume = 0.0;
+        } else if (volume > 1.0) {
+            volume = 1.0;
+        }
+
+        vol = lrint(volume * 100.0);
+
+        // Voice volume levels from android are mapped to driver volume levels as follows.
+        // 0 -> 5, 20 -> 4, 40 ->3, 60 -> 2, 80 -> 1, 100 -> 0
+        // So adjust the volume to get the correct volume index in driver
+        vol = 100 - vol;
+
+        err = platform_set_voice_volume(adev->platform, vol);
+    }
+
+    return err;
+}
+
+int voice_start_call(struct audio_device *adev)
+{
+    int ret = 0;
+
+    adev->voice.in_call = true;
+    ret = voice_extn_start_call(adev);
+    if (ret == -ENOSYS) {
+        ret = voice_start_usecase(adev, USECASE_VOICE_CALL);
+    }
+
+    return ret;
+}
+
+int voice_stop_call(struct audio_device *adev)
+{
+    int ret = 0;
+
+    adev->voice.in_call = false;
+    ret = voice_extn_stop_call(adev);
+    if (ret == -ENOSYS) {
+        ret = voice_stop_usecase(adev, USECASE_VOICE_CALL);
+    }
+
+    return ret;
+}
+
+void voice_get_parameters(struct audio_device *adev,
+                          struct str_parms *query,
+                          struct str_parms *reply)
+{
+    voice_extn_get_parameters(adev, query, reply);
+}
+
+int voice_set_parameters(struct audio_device *adev, struct str_parms *parms)
+{
+    char *str;
+    char value[32];
+    int val;
+    int ret = 0, err;
+    char *kv_pairs = str_parms_to_str(parms);
+
+    ALOGV_IF(kv_pairs != NULL, "%s: enter: %s", __func__, kv_pairs);
+
+    ret = voice_extn_set_parameters(adev, parms);
+    if (ret != 0) {
+        if (ret == -ENOSYS) {
+            ret = 0; /* ignore error */
+        } else {
+            goto done;
+        }
+    }
+
+    err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_TTY_MODE, value, sizeof(value));
+    if (err >= 0) {
+        int tty_mode;
+        str_parms_del(parms, AUDIO_PARAMETER_KEY_TTY_MODE);
+        if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_OFF) == 0)
+            tty_mode = TTY_MODE_OFF;
+        else if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_VCO) == 0)
+            tty_mode = TTY_MODE_VCO;
+        else if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_HCO) == 0)
+            tty_mode = TTY_MODE_HCO;
+        else if (strcmp(value, AUDIO_PARAMETER_VALUE_TTY_FULL) == 0)
+            tty_mode = TTY_MODE_FULL;
+        else {
+            ret = -EINVAL;
+            goto done;
+        }
+
+        if (tty_mode != adev->voice.tty_mode) {
+            adev->voice.tty_mode = tty_mode;
+            adev->acdb_settings = (adev->acdb_settings & TTY_MODE_CLEAR) | tty_mode;
+            if (voice_is_call_state_active(adev))
+               voice_update_devices_for_all_voice_usecases(adev);
+        }
+    }
+
+    err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_HAC,
+                            value, sizeof(value));
+    if (err >= 0) {
+        bool hac = false;
+        str_parms_del(parms, AUDIO_PARAMETER_KEY_HAC);
+        if (strcmp(value, AUDIO_PARAMETER_VALUE_HAC_ON) == 0)
+            hac = true;
+
+        if (hac != adev->voice.hac) {
+            adev->voice.hac = hac;
+            if (voice_is_in_call(adev))
+                voice_update_devices_for_all_voice_usecases(adev);
+        }
+     }
+
+    err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_INCALLMUSIC,
+                            value, sizeof(value));
+    if (err >= 0) {
+        str_parms_del(parms, AUDIO_PARAMETER_KEY_INCALLMUSIC);
+        if (strcmp(value, AUDIO_PARAMETER_VALUE_TRUE) == 0)
+            platform_start_incall_music_usecase(adev->platform);
+        else
+            platform_stop_incall_music_usecase(adev->platform);
+     }
+
+done:
+    ALOGV("%s: exit with code(%d)", __func__, ret);
+    free(kv_pairs);
+    return ret;
+}
+
+void voice_init(struct audio_device *adev)
+{
+    int i = 0;
+
+    memset(&adev->voice, 0, sizeof(adev->voice));
+    adev->voice.tty_mode = TTY_MODE_OFF;
+    adev->voice.hac = false;
+    adev->voice.volume = 1.0f;
+    adev->voice.mic_mute = false;
+    adev->voice.in_call = false;
+    for (i = 0; i < MAX_VOICE_SESSIONS; i++) {
+        adev->voice.session[i].pcm_rx = NULL;
+        adev->voice.session[i].pcm_tx = NULL;
+        adev->voice.session[i].state.current = CALL_INACTIVE;
+        adev->voice.session[i].state.new = CALL_INACTIVE;
+        adev->voice.session[i].vsid = VOICE_VSID;
+    }
+
+    voice_extn_init(adev);
+}
+
+void voice_update_devices_for_all_voice_usecases(struct audio_device *adev)
+{
+    struct listnode *node;
+    struct audio_usecase *usecase;
+
+    list_for_each(node, &adev->usecase_list) {
+        usecase = node_to_item(node, struct audio_usecase, list);
+        if (usecase->type == VOICE_CALL) {
+            ALOGV("%s: updating device for usecase:%s", __func__,
+                  use_case_table[usecase->id]);
+            usecase->stream.out = adev->current_call_output;
+            select_devices(adev, usecase->id);
+        }
+    }
+}
+
+
diff --git a/hal/voice.h b/hal/voice.h
new file mode 100644
index 0000000..76f9d0d
--- /dev/null
+++ b/hal/voice.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VOICE_H
+#define VOICE_H
+
+#define BASE_SESS_IDX       0
+#define VOICE_SESS_IDX     (BASE_SESS_IDX)
+
+#ifdef MULTI_VOICE_SESSION_ENABLED
+#define MAX_VOICE_SESSIONS 5
+#else
+#define MAX_VOICE_SESSIONS 1
+#endif
+
+#define BASE_CALL_STATE     1
+#define CALL_INACTIVE       (BASE_CALL_STATE)
+#define CALL_ACTIVE         (BASE_CALL_STATE + 1)
+
+#define VOICE_VSID  0x10C01000
+
+#define AUDIO_PARAMETER_KEY_INCALLMUSIC "incall_music_enabled"
+#define AUDIO_PARAMETER_VALUE_TRUE "true"
+
+struct audio_device;
+struct str_parms;
+struct stream_in;
+struct stream_out;
+typedef int audio_usecase_t;
+
+struct call_state {
+    int current;
+    int new;
+};
+
+struct voice_session {
+    struct pcm *pcm_rx;
+    struct pcm *pcm_tx;
+    struct call_state state;
+    uint32_t vsid;
+};
+
+struct voice {
+    struct voice_session session[MAX_VOICE_SESSIONS];
+    int tty_mode;
+    bool hac;
+    bool mic_mute;
+    float volume;
+    bool in_call;
+};
+
+enum {
+    INCALL_REC_NONE = -1,
+    INCALL_REC_UPLINK,
+    INCALL_REC_DOWNLINK,
+    INCALL_REC_UPLINK_AND_DOWNLINK,
+};
+
+int voice_start_usecase(struct audio_device *adev, audio_usecase_t usecase_id);
+int voice_stop_usecase(struct audio_device *adev, audio_usecase_t usecase_id);
+
+int voice_start_call(struct audio_device *adev);
+int voice_stop_call(struct audio_device *adev);
+int voice_set_parameters(struct audio_device *adev, struct str_parms *parms);
+void voice_get_parameters(struct audio_device *adev, struct str_parms *query,
+                          struct str_parms *reply);
+void voice_init(struct audio_device *adev);
+bool voice_is_in_call(struct audio_device *adev);
+bool voice_is_in_call_rec_stream(struct stream_in *in);
+int voice_set_mic_mute(struct audio_device *dev, bool state);
+bool voice_get_mic_mute(struct audio_device *dev);
+int voice_set_volume(struct audio_device *adev, float volume);
+int voice_check_and_set_incall_rec_usecase(struct audio_device *adev,
+                                           struct stream_in *in);
+int voice_check_and_set_incall_music_usecase(struct audio_device *adev,
+                                             struct stream_out *out);
+int voice_check_and_stop_incall_rec_usecase(struct audio_device *adev,
+                                            struct stream_in *in);
+void voice_update_devices_for_all_voice_usecases(struct audio_device *adev);
+#endif //VOICE_H
diff --git a/hal/voice_extn/voice_extn.c b/hal/voice_extn/voice_extn.c
new file mode 100644
index 0000000..89b659c
--- /dev/null
+++ b/hal/voice_extn/voice_extn.c
@@ -0,0 +1,573 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "voice_extn"
+/*#define LOG_NDEBUG 0*/
+#define LOG_NDDEBUG 0
+
+#include <errno.h>
+#include <math.h>
+#include <cutils/log.h>
+#include <cutils/str_parms.h>
+#include <sys/ioctl.h>
+#include <sound/voice_params.h>
+
+#include "audio_hw.h"
+#include "voice.h"
+#include "platform.h"
+#include "platform_api.h"
+#include "voice_extn.h"
+
+#define AUDIO_PARAMETER_KEY_VSID                "vsid"
+#define AUDIO_PARAMETER_KEY_CALL_STATE          "call_state"
+#define AUDIO_PARAMETER_KEY_AUDIO_MODE          "audio_mode"
+#define AUDIO_PARAMETER_KEY_ALL_CALL_STATES     "all_call_states"
+#define AUDIO_PARAMETER_KEY_DEVICE_MUTE         "device_mute"
+#define AUDIO_PARAMETER_KEY_DIRECTION           "direction"
+
+#define VOICE_EXTN_PARAMETER_VALUE_MAX_LEN 256
+
+#define VOICE2_VSID 0x10DC1000
+#define VOLTE_VSID  0x10C02000
+#define QCHAT_VSID  0x10803000
+#define VOWLAN_VSID 0x10002000
+#define ALL_VSID    0xFFFFFFFF
+
+/* Voice Session Indices */
+#define VOICE2_SESS_IDX    (VOICE_SESS_IDX + 1)
+#define VOLTE_SESS_IDX     (VOICE_SESS_IDX + 2)
+#define QCHAT_SESS_IDX     (VOICE_SESS_IDX + 3)
+#define VOWLAN_SESS_IDX    (VOICE_SESS_IDX + 4)
+
+/* Call States */
+#define CALL_HOLD           (BASE_CALL_STATE + 2)
+#define CALL_LOCAL_HOLD     (BASE_CALL_STATE + 3)
+
+struct pcm_config pcm_config_incall_music = {
+    .channels = 1,
+    .rate = DEFAULT_OUTPUT_SAMPLING_RATE,
+    .period_size = LOW_LATENCY_OUTPUT_PERIOD_SIZE,
+    .period_count = LOW_LATENCY_OUTPUT_PERIOD_COUNT,
+    .format = PCM_FORMAT_S16_LE,
+    .start_threshold = LOW_LATENCY_OUTPUT_PERIOD_SIZE / 4,
+    .stop_threshold = INT_MAX,
+    .avail_min = LOW_LATENCY_OUTPUT_PERIOD_SIZE / 4,
+};
+
+int voice_extn_is_call_state_active(struct audio_device *adev, bool *is_call_active);
+
+static bool is_valid_call_state(int call_state)
+{
+    if (call_state < CALL_INACTIVE || call_state > CALL_LOCAL_HOLD)
+        return false;
+    else
+        return true;
+}
+
+static bool is_valid_vsid(uint32_t vsid)
+{
+    if (vsid == VOICE_VSID ||
+        vsid == VOICE2_VSID ||
+        vsid == VOLTE_VSID ||
+        vsid == QCHAT_VSID ||
+        vsid == VOWLAN_VSID)
+        return true;
+    else
+        return false;
+}
+
+static audio_usecase_t voice_extn_get_usecase_for_session_idx(const int index)
+{
+    audio_usecase_t usecase_id = -1;
+
+    switch(index) {
+    case VOICE_SESS_IDX:
+        usecase_id = USECASE_VOICE_CALL;
+        break;
+
+    case VOICE2_SESS_IDX:
+        usecase_id = USECASE_VOICE2_CALL;
+        break;
+
+    case VOLTE_SESS_IDX:
+        usecase_id = USECASE_VOLTE_CALL;
+        break;
+
+    case QCHAT_SESS_IDX:
+        usecase_id = USECASE_QCHAT_CALL;
+        break;
+
+    case VOWLAN_SESS_IDX:
+        usecase_id = USECASE_VOWLAN_CALL;
+        break;
+
+    default:
+        ALOGE("%s: Invalid voice session index\n", __func__);
+    }
+
+    return usecase_id;
+}
+
+static uint32_t get_session_id_with_state(struct audio_device *adev,
+                                          int call_state)
+{
+    struct voice_session *session = NULL;
+    int i = 0;
+    uint32_t session_id = 0;
+
+    for (i = 0; i < MAX_VOICE_SESSIONS; i++) {
+        session = &adev->voice.session[i];
+        if(session->state.current == call_state){
+            session_id = session->vsid;
+            break;
+        }
+    }
+
+    return session_id;
+}
+
+static int update_calls(struct audio_device *adev)
+{
+    int i = 0;
+    audio_usecase_t usecase_id = 0;
+    enum voice_lch_mode lch_mode;
+    struct voice_session *session = NULL;
+    int fd = 0;
+    int ret = 0;
+
+    ALOGD("%s: enter:", __func__);
+
+    for (i = 0; i < MAX_VOICE_SESSIONS; i++) {
+        usecase_id = voice_extn_get_usecase_for_session_idx(i);
+        session = &adev->voice.session[i];
+        ALOGD("%s: cur_state=%d new_state=%d vsid=%x",
+              __func__, session->state.current, session->state.new, session->vsid);
+
+        switch(session->state.new)
+        {
+        case CALL_ACTIVE:
+            switch(session->state.current)
+            {
+            case CALL_INACTIVE:
+                ALOGD("%s: INACTIVE -> ACTIVE vsid:%x", __func__, session->vsid);
+                ret = voice_start_usecase(adev, usecase_id);
+                if(ret < 0) {
+                    ALOGE("%s: voice_start_usecase() failed for usecase: %d\n",
+                          __func__, usecase_id);
+                } else {
+                    session->state.current = session->state.new;
+                }
+                break;
+
+            case CALL_HOLD:
+                ALOGD("%s: HOLD -> ACTIVE vsid:%x", __func__, session->vsid);
+                session->state.current = session->state.new;
+                break;
+
+            case CALL_LOCAL_HOLD:
+                ALOGD("%s: LOCAL_HOLD -> ACTIVE vsid:%x", __func__, session->vsid);
+                lch_mode = VOICE_LCH_STOP;
+                if (pcm_ioctl(session->pcm_tx, SNDRV_VOICE_IOCTL_LCH, &lch_mode) < 0) {
+                    ALOGE("LOCAL_HOLD -> ACTIVE failed");
+                } else {
+                    session->state.current = session->state.new;
+                }
+                break;
+
+            default:
+                ALOGV("%s: CALL_ACTIVE cannot be handled in state=%d vsid:%x",
+                      __func__, session->state.current, session->vsid);
+                break;
+            }
+            break;
+
+        case CALL_INACTIVE:
+            switch(session->state.current)
+            {
+            case CALL_ACTIVE:
+            case CALL_HOLD:
+            case CALL_LOCAL_HOLD:
+                ALOGD("%s: ACTIVE/HOLD/LOCAL_HOLD -> INACTIVE vsid:%x", __func__, session->vsid);
+                ret = voice_stop_usecase(adev, usecase_id);
+                if(ret < 0) {
+                    ALOGE("%s: voice_stop_usecase() failed for usecase: %d\n",
+                          __func__, usecase_id);
+                } else {
+                    session->state.current = session->state.new;
+                }
+                break;
+
+            default:
+                ALOGV("%s: CALL_INACTIVE cannot be handled in state=%d vsid:%x",
+                      __func__, session->state.current, session->vsid);
+                break;
+            }
+            break;
+
+        case CALL_HOLD:
+            switch(session->state.current)
+            {
+            case CALL_ACTIVE:
+                ALOGD("%s: CALL_ACTIVE -> HOLD vsid:%x", __func__, session->vsid);
+                session->state.current = session->state.new;
+                break;
+
+            case CALL_LOCAL_HOLD:
+                ALOGD("%s: CALL_LOCAL_HOLD -> HOLD vsid:%x", __func__, session->vsid);
+                lch_mode = VOICE_LCH_STOP;
+                if (pcm_ioctl(session->pcm_tx, SNDRV_VOICE_IOCTL_LCH, &lch_mode) < 0) {
+                    ALOGE("LOCAL_HOLD -> HOLD failed");
+                } else {
+                    session->state.current = session->state.new;
+                }
+                break;
+
+            default:
+                ALOGV("%s: CALL_HOLD cannot be handled in state=%d vsid:%x",
+                      __func__, session->state.current, session->vsid);
+                break;
+            }
+            break;
+
+        case CALL_LOCAL_HOLD:
+            switch(session->state.current)
+            {
+            case CALL_ACTIVE:
+            case CALL_HOLD:
+                ALOGD("%s: ACTIVE/CALL_HOLD -> LOCAL_HOLD vsid:%x", __func__,
+                      session->vsid);
+                lch_mode = VOICE_LCH_START;
+                if (pcm_ioctl(session->pcm_tx, SNDRV_VOICE_IOCTL_LCH, &lch_mode) < 0) {
+                    ALOGE("LOCAL_HOLD -> HOLD failed");
+                } else {
+                    session->state.current = session->state.new;
+                }
+                break;
+
+            default:
+                ALOGV("%s: CALL_LOCAL_HOLD cannot be handled in state=%d vsid:%x",
+                      __func__, session->state.current, session->vsid);
+                break;
+            }
+            break;
+
+        default:
+            break;
+        } //end out switch loop
+    } //end for loop
+
+    return ret;
+}
+
+static int update_call_states(struct audio_device *adev,
+                                    const uint32_t vsid, const int call_state)
+{
+    struct voice_session *session = NULL;
+    int i = 0;
+    bool is_call_active;
+
+    for (i = 0; i < MAX_VOICE_SESSIONS; i++) {
+        if (vsid == adev->voice.session[i].vsid) {
+            session = &adev->voice.session[i];
+            break;
+        }
+    }
+
+    if (session) {
+        session->state.new = call_state;
+        voice_extn_is_call_state_active(adev, &is_call_active);
+        ALOGD("%s is_call_active:%d in_call:%d, mode:%d\n",
+              __func__, is_call_active, adev->voice.in_call, adev->mode);
+        /* Dont start voice call before device routing for voice usescases has
+         * occured, otherwise voice calls will be started unintendedly on
+         * speaker.
+         */
+        if (is_call_active ||
+                (adev->voice.in_call && adev->mode == AUDIO_MODE_IN_CALL)) {
+            /* Device routing is not triggered for voice calls on the subsequent
+             * subs, Hence update the call states if voice call is already
+             * active on other sub.
+             */
+            update_calls(adev);
+        }
+    } else {
+        return -EINVAL;
+    }
+
+    return 0;
+
+}
+
+int voice_extn_get_active_session_id(struct audio_device *adev,
+                                     uint32_t *session_id)
+{
+    *session_id = get_session_id_with_state(adev, CALL_ACTIVE);
+    return 0;
+}
+
+int voice_extn_is_call_state_active(struct audio_device *adev, bool *is_call_active)
+{
+    struct voice_session *session = NULL;
+    int i = 0;
+    *is_call_active = false;
+
+    for (i = 0; i < MAX_VOICE_SESSIONS; i++) {
+        session = &adev->voice.session[i];
+        if(session->state.current != CALL_INACTIVE){
+            *is_call_active = true;
+            break;
+        }
+    }
+
+    return 0;
+}
+
+int voice_extn_is_in_call_rec_stream(struct stream_in *in, bool *in_call_rec)
+{
+    *in_call_rec = false;
+
+    if(in->source == AUDIO_SOURCE_VOICE_DOWNLINK ||
+       in->source == AUDIO_SOURCE_VOICE_UPLINK ||
+       in->source == AUDIO_SOURCE_VOICE_CALL) {
+       *in_call_rec = true;
+    }
+
+    return 0;
+}
+
+void voice_extn_init(struct audio_device *adev)
+{
+    adev->voice.session[VOICE_SESS_IDX].vsid =  VOICE_VSID;
+    adev->voice.session[VOICE2_SESS_IDX].vsid = VOICE2_VSID;
+    adev->voice.session[VOLTE_SESS_IDX].vsid =  VOLTE_VSID;
+    adev->voice.session[QCHAT_SESS_IDX].vsid =  QCHAT_VSID;
+    adev->voice.session[VOWLAN_SESS_IDX].vsid = VOWLAN_VSID;
+}
+
+int voice_extn_get_session_from_use_case(struct audio_device *adev,
+                                         const audio_usecase_t usecase_id,
+                                         struct voice_session **session)
+{
+
+    switch(usecase_id)
+    {
+    case USECASE_VOICE_CALL:
+        *session = &adev->voice.session[VOICE_SESS_IDX];
+        break;
+
+    case USECASE_VOICE2_CALL:
+        *session = &adev->voice.session[VOICE2_SESS_IDX];
+        break;
+
+    case USECASE_VOLTE_CALL:
+        *session = &adev->voice.session[VOLTE_SESS_IDX];
+        break;
+
+    case USECASE_QCHAT_CALL:
+        *session = &adev->voice.session[QCHAT_SESS_IDX];
+        break;
+
+    case USECASE_VOWLAN_CALL:
+        *session = &adev->voice.session[VOWLAN_SESS_IDX];
+        break;
+
+    default:
+        ALOGE("%s: Invalid usecase_id:%d\n", __func__, usecase_id);
+        *session = NULL;
+        return -EINVAL;
+    }
+
+    return 0;
+}
+
+int voice_extn_start_call(struct audio_device *adev)
+{
+    /* Start voice calls on sessions whose call state has been
+     * udpated.
+     */
+    ALOGV("%s: enter:", __func__);
+    return update_calls(adev);
+}
+
+int voice_extn_stop_call(struct audio_device *adev)
+{
+    int i;
+    int ret = 0;
+
+    ALOGV("%s: enter:", __func__);
+
+    /* If BT device is enabled and voice calls are ended, telephony will call
+     * set_mode(AUDIO_MODE_NORMAL) which will trigger audio policy manager to
+     * set routing with device BT A2DP profile. Hence end all voice calls when
+     * set_mode(AUDIO_MODE_NORMAL) before BT A2DP profile is selected.
+     */
+    if (adev->mode == AUDIO_MODE_NORMAL) {
+        ALOGD("%s: end all calls", __func__);
+        for (i = 0; i < MAX_VOICE_SESSIONS; i++) {
+            adev->voice.session[i].state.new = CALL_INACTIVE;
+        }
+
+        ret = update_calls(adev);
+    }
+
+    return ret;
+}
+
+int voice_extn_set_parameters(struct audio_device *adev,
+                              struct str_parms *parms)
+{
+    char *str;
+    int value;
+    int ret = 0, err;
+    char *kv_pairs = str_parms_to_str(parms);
+    char str_value[256] = {0};
+
+    ALOGV_IF(kv_pairs != NULL, "%s: enter: %s", __func__, kv_pairs);
+
+    err = str_parms_get_int(parms, AUDIO_PARAMETER_KEY_VSID, &value);
+    if (err >= 0) {
+        str_parms_del(parms, AUDIO_PARAMETER_KEY_VSID);
+        uint32_t vsid = value;
+        int call_state = -1;
+        err = str_parms_get_int(parms, AUDIO_PARAMETER_KEY_CALL_STATE, &value);
+        if (err >= 0) {
+            call_state = value;
+            str_parms_del(parms, AUDIO_PARAMETER_KEY_CALL_STATE);
+        } else {
+            ALOGE("%s: call_state key not found", __func__);
+            ret = -EINVAL;
+            goto done;
+        }
+
+        if (is_valid_vsid(vsid) && is_valid_call_state(call_state)) {
+            ret = update_call_states(adev, vsid, call_state);
+        } else {
+            ALOGE("%s: invalid vsid:%x or call_state:%d",
+                  __func__, vsid, call_state);
+            ret = -EINVAL;
+            goto done;
+        }
+    }
+
+    err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_DEVICE_MUTE, str_value,
+                            sizeof(str_value));
+    if (err >= 0) {
+        str_parms_del(parms, AUDIO_PARAMETER_KEY_DEVICE_MUTE);
+        bool mute = false;
+
+        if (!strncmp("true", str_value, sizeof("true"))) {
+            mute = true;
+        }
+
+        err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_DIRECTION, str_value,
+                                sizeof(str_value));
+        if (err >= 0) {
+            str_parms_del(parms, AUDIO_PARAMETER_KEY_DIRECTION);
+        } else {
+            ALOGE("%s: direction key not found", __func__);
+            ret = -EINVAL;
+            goto done;
+        }
+
+        ret = platform_set_device_mute(adev->platform, mute, str_value);
+        if (ret != 0) {
+            ALOGE("%s: Failed to set mute err:%d", __func__, ret);
+            ret = -EINVAL;
+            goto done;
+        }
+    }
+
+done:
+    ALOGV("%s: exit with code(%d)", __func__, ret);
+    free(kv_pairs);
+    return ret;
+}
+
+static int get_all_call_states_str(const struct audio_device *adev,
+                            char *value)
+{
+    int ret = 0;
+    char *cur_ptr = value;
+    int i, len=0;
+
+    for (i = 0; i < MAX_VOICE_SESSIONS; i++) {
+        snprintf(cur_ptr, VOICE_EXTN_PARAMETER_VALUE_MAX_LEN - len,
+                 "%d:%d,",adev->voice.session[i].vsid,
+                 adev->voice.session[i].state.current);
+        len = strlen(cur_ptr);
+        cur_ptr = cur_ptr + len;
+    }
+    ALOGV("%s:value=%s", __func__, value);
+    return ret;
+}
+
+void voice_extn_get_parameters(const struct audio_device *adev,
+                               struct str_parms *query,
+                               struct str_parms *reply)
+{
+    int ret;
+    char value[VOICE_EXTN_PARAMETER_VALUE_MAX_LEN] = {0};
+    char *str = str_parms_to_str(query);
+
+    ALOGV_IF(str != NULL, "%s: enter %s", __func__, str);
+    free(str);
+
+    ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_AUDIO_MODE, value,
+                            sizeof(value));
+    if (ret >= 0) {
+        str_parms_add_int(reply, AUDIO_PARAMETER_KEY_AUDIO_MODE, adev->mode);
+    }
+
+    ret = str_parms_get_str(query, AUDIO_PARAMETER_KEY_ALL_CALL_STATES,
+                            value, sizeof(value));
+    if (ret >= 0) {
+        ret = get_all_call_states_str(adev, value);
+        if (ret) {
+            ALOGE("%s: Error fetching call states, err:%d", __func__, ret);
+            return;
+        }
+        str_parms_add_str(reply, AUDIO_PARAMETER_KEY_ALL_CALL_STATES, value);
+    }
+
+    str = str_parms_to_str(reply);
+    ALOGV_IF(str != NULL, "%s: exit: returns \"%s\"", __func__, str);
+    free(str);
+}
+
+#ifdef INCALL_MUSIC_ENABLED
+int voice_extn_check_and_set_incall_music_usecase(struct audio_device *adev,
+                                                  struct stream_out *out)
+{
+    uint32_t session_id = 0;
+
+    session_id = get_session_id_with_state(adev, CALL_LOCAL_HOLD);
+    if (session_id == VOICE_VSID) {
+        out->usecase = USECASE_INCALL_MUSIC_UPLINK;
+    } else if (session_id == VOICE2_VSID) {
+        out->usecase = USECASE_INCALL_MUSIC_UPLINK2;
+    } else {
+        ALOGE("%s: Invalid session id %x", __func__, session_id);
+        return -EINVAL;
+    }
+
+    out->config = pcm_config_incall_music;
+    out->supported_channel_masks[0] = AUDIO_CHANNEL_OUT_MONO;
+    out->channel_mask = AUDIO_CHANNEL_OUT_MONO;
+
+    return 0;
+}
+#endif
+
diff --git a/hal/voice_extn/voice_extn.h b/hal/voice_extn/voice_extn.h
new file mode 100644
index 0000000..8689c9c
--- /dev/null
+++ b/hal/voice_extn/voice_extn.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VOICE_EXTN_H
+#define VOICE_EXTN_H
+
+#ifdef MULTI_VOICE_SESSION_ENABLED
+int voice_extn_start_call(struct audio_device *adev);
+int voice_extn_stop_call(struct audio_device *adev);
+int voice_extn_get_session_from_use_case(struct audio_device *adev,
+                                         const audio_usecase_t usecase_id,
+                                         struct voice_session **session);
+void voice_extn_init(struct audio_device *adev);
+int voice_extn_set_parameters(struct audio_device *adev,
+                              struct str_parms *parms);
+void voice_extn_get_parameters(const struct audio_device *adev,
+                               struct str_parms *query,
+                               struct str_parms *reply);
+int voice_extn_is_in_call_rec_stream(struct stream_in *in, bool *in_call_rec);
+int voice_extn_get_active_session_id(struct audio_device *adev,
+                                     uint32_t *session_id);
+int voice_extn_is_call_state_active(struct audio_device *adev,
+                                    bool *is_call_active);
+#else
+static int voice_extn_start_call(struct audio_device *adev __unused)
+{
+    return -ENOSYS;
+}
+
+static int voice_extn_stop_call(struct audio_device *adev __unused)
+{
+    return -ENOSYS;
+}
+
+static int voice_extn_get_session_from_use_case(struct audio_device *adev __unused,
+                                                const audio_usecase_t usecase_id __unused,
+                                                struct voice_session **session __unused)
+{
+    return -ENOSYS;
+}
+
+static void voice_extn_init(struct audio_device *adev __unused)
+{
+}
+
+static int voice_extn_set_parameters(struct audio_device *adev __unused,
+                                     struct str_parms *parms __unused)
+{
+    return -ENOSYS;
+}
+
+static void voice_extn_get_parameters(const struct audio_device *adev __unused,
+                                      struct str_parms *query __unused,
+                                      struct str_parms *reply __unused)
+{
+}
+
+static int voice_extn_is_call_state_active(struct audio_device *adev __unused,
+                                           bool *is_call_active __unused)
+{
+    return -ENOSYS;
+}
+
+static int voice_extn_is_in_call_rec_stream(struct stream_in *in __unused, bool *in_call_rec __unused)
+{
+    return -ENOSYS;
+}
+
+static int voice_extn_get_active_session_id(struct audio_device *adev __unused,
+                                            uint32_t *session_id __unused)
+{
+    return -ENOSYS;
+}
+
+#endif
+
+#ifdef INCALL_MUSIC_ENABLED
+int voice_extn_check_and_set_incall_music_usecase(struct audio_device *adev,
+                                                  struct stream_out *out);
+#else
+static int voice_extn_check_and_set_incall_music_usecase(struct audio_device *adev __unused,
+                                                         struct stream_out *out __unused)
+{
+    return -ENOSYS;
+}
+#endif
+
+#endif //VOICE_EXTN_H
diff --git a/legacy/alsa_sound/audio_hw_hal.cpp b/legacy/alsa_sound/audio_hw_hal.cpp
index ab15e27..f8b02e8 100644
--- a/legacy/alsa_sound/audio_hw_hal.cpp
+++ b/legacy/alsa_sound/audio_hw_hal.cpp
@@ -554,7 +554,8 @@
                                    audio_devices_t devices,
                                    audio_output_flags_t flags,
                                    struct audio_config *config,
-                                   struct audio_stream_out **stream_out)
+                                   struct audio_stream_out **stream_out,
+                                   const char *address __unused)
 {
     struct qcom_audio_device *qadev = to_ladev(dev);
     status_t status;
@@ -620,7 +621,10 @@
                                   audio_io_handle_t handle,
                                   audio_devices_t devices,
                                   audio_config *config,
-                                  audio_stream_in **stream_in)
+                                  audio_stream_in **stream_in,
+                                  audio_input_flags_t flags __unused,
+                                  const char *address __unused,
+                                  audio_source_t source __unused)
 {
     struct qcom_audio_device *qadev = to_ladev(dev);
     status_t status;
diff --git a/post_proc/Android.mk b/post_proc/Android.mk
new file mode 100644
index 0000000..91ed2bc
--- /dev/null
+++ b/post_proc/Android.mk
@@ -0,0 +1,33 @@
+ifneq ($(filter msm8974 msm8226 msm8084,$(TARGET_BOARD_PLATFORM)),)
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	bundle.c \
+	equalizer.c \
+	bass_boost.c \
+	virtualizer.c \
+	reverb.c \
+	effect_api.c
+
+LOCAL_CFLAGS+= -O2 -fvisibility=hidden
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+	liblog \
+	libtinyalsa
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx
+LOCAL_MODULE:= libqcompostprocbundle
+
+LOCAL_C_INCLUDES := \
+	external/tinyalsa/include \
+	$(call include-path-for, audio-effects)
+
+include $(BUILD_SHARED_LIBRARY)
+
+endif
\ No newline at end of file
diff --git a/post_proc/bass_boost.c b/post_proc/bass_boost.c
new file mode 100644
index 0000000..f303886
--- /dev/null
+++ b/post_proc/bass_boost.c
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "offload_effect_bass_boost"
+//#define LOG_NDEBUG 0
+
+#include <cutils/list.h>
+#include <cutils/log.h>
+#include <tinyalsa/asoundlib.h>
+#include <sound/audio_effects.h>
+#include <audio_effects/effect_bassboost.h>
+
+#include "effect_api.h"
+#include "bass_boost.h"
+
+/* Offload bassboost UUID: 2c4a8c24-1581-487f-94f6-0002a5d5c51b */
+const effect_descriptor_t bassboost_descriptor = {
+        {0x0634f220, 0xddd4, 0x11db, 0xa0fc, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b }},
+        {0x2c4a8c24, 0x1581, 0x487f, 0x94f6, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // uuid
+        EFFECT_CONTROL_API_VERSION,
+        (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_DEVICE_IND | EFFECT_FLAG_HW_ACC_TUNNEL),
+        0, /* TODO */
+        1,
+        "MSM offload bassboost",
+        "The Android Open Source Project",
+};
+
+/*
+ * Bassboost operations
+ */
+
+int bassboost_get_strength(bassboost_context_t *context)
+{
+    ALOGV("%s: strength: %d", __func__, context->strength);
+    return context->strength;
+}
+
+int bassboost_set_strength(bassboost_context_t *context, uint32_t strength)
+{
+    ALOGV("%s: strength: %d", __func__, strength);
+    context->strength = strength;
+
+    offload_bassboost_set_strength(&(context->offload_bass), strength);
+    if (context->ctl)
+        offload_bassboost_send_params(context->ctl, &context->offload_bass,
+                                      OFFLOAD_SEND_BASSBOOST_ENABLE_FLAG |
+                                      OFFLOAD_SEND_BASSBOOST_STRENGTH);
+    return 0;
+}
+
+int bassboost_get_parameter(effect_context_t *context, effect_param_t *p,
+                            uint32_t *size)
+{
+    bassboost_context_t *bass_ctxt = (bassboost_context_t *)context;
+    int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t);
+    int32_t *param_tmp = (int32_t *)p->data;
+    int32_t param = *param_tmp++;
+    void *value = p->data + voffset;
+    int i;
+
+    ALOGV("%s", __func__);
+
+    p->status = 0;
+
+    switch (param) {
+    case BASSBOOST_PARAM_STRENGTH_SUPPORTED:
+        if (p->vsize < sizeof(uint32_t))
+           p->status = -EINVAL;
+        p->vsize = sizeof(uint32_t);
+        break;
+    case BASSBOOST_PARAM_STRENGTH:
+        if (p->vsize < sizeof(int16_t))
+           p->status = -EINVAL;
+        p->vsize = sizeof(int16_t);
+        break;
+    default:
+        p->status = -EINVAL;
+    }
+
+    *size = sizeof(effect_param_t) + voffset + p->vsize;
+
+    if (p->status != 0)
+        return 0;
+
+    switch (param) {
+    case BASSBOOST_PARAM_STRENGTH_SUPPORTED:
+        ALOGV("%s: BASSBOOST_PARAM_STRENGTH_SUPPORTED", __func__);
+        *(uint32_t *)value = 1;
+        break;
+
+    case BASSBOOST_PARAM_STRENGTH:
+        ALOGV("%s: BASSBOOST_PARAM_STRENGTH", __func__);
+        *(int16_t *)value = bassboost_get_strength(bass_ctxt);
+        break;
+
+    default:
+        p->status = -EINVAL;
+        break;
+    }
+
+    return 0;
+}
+
+int bassboost_set_parameter(effect_context_t *context, effect_param_t *p,
+                            uint32_t size __unused)
+{
+    bassboost_context_t *bass_ctxt = (bassboost_context_t *)context;
+    int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t);
+    void *value = p->data + voffset;
+    int32_t *param_tmp = (int32_t *)p->data;
+    int32_t param = *param_tmp++;
+    uint32_t strength;
+
+    ALOGV("%s", __func__);
+
+    p->status = 0;
+
+    switch (param) {
+    case BASSBOOST_PARAM_STRENGTH:
+        ALOGV("%s BASSBOOST_PARAM_STRENGTH", __func__);
+        strength = (uint32_t)(*(int16_t *)value);
+        bassboost_set_strength(bass_ctxt, strength);
+        break;
+    default:
+        p->status = -EINVAL;
+        break;
+    }
+
+    return 0;
+}
+
+int bassboost_set_device(effect_context_t *context, uint32_t device)
+{
+    bassboost_context_t *bass_ctxt = (bassboost_context_t *)context;
+
+    ALOGV("%s: device: %d", __func__, device);
+    bass_ctxt->device = device;
+    if ((device == AUDIO_DEVICE_OUT_SPEAKER) ||
+       (device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT) ||
+       (device == AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER) ||
+       (device == AUDIO_DEVICE_OUT_AUX_DIGITAL) ||
+       (device == AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET)) {
+        if (!bass_ctxt->temp_disabled) {
+            if (effect_is_active(&bass_ctxt->common)) {
+                offload_bassboost_set_enable_flag(&(bass_ctxt->offload_bass), false);
+                if (bass_ctxt->ctl)
+                    offload_bassboost_send_params(bass_ctxt->ctl,
+                                                  &bass_ctxt->offload_bass,
+                                                  OFFLOAD_SEND_BASSBOOST_ENABLE_FLAG);
+            }
+            bass_ctxt->temp_disabled = true;
+        }
+    } else {
+        if (bass_ctxt->temp_disabled) {
+            if (effect_is_active(&bass_ctxt->common)) {
+                offload_bassboost_set_enable_flag(&(bass_ctxt->offload_bass), true);
+                if (bass_ctxt->ctl)
+                    offload_bassboost_send_params(bass_ctxt->ctl,
+                                                  &bass_ctxt->offload_bass,
+                                                  OFFLOAD_SEND_BASSBOOST_ENABLE_FLAG);
+            }
+            bass_ctxt->temp_disabled = false;
+        }
+    }
+    offload_bassboost_set_device(&(bass_ctxt->offload_bass), device);
+    return 0;
+}
+
+int bassboost_reset(effect_context_t *context)
+{
+    bassboost_context_t *bass_ctxt = (bassboost_context_t *)context;
+
+    return 0;
+}
+
+int bassboost_init(effect_context_t *context)
+{
+    bassboost_context_t *bass_ctxt = (bassboost_context_t *)context;
+
+    ALOGV("%s", __func__);
+    context->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
+    context->config.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
+    context->config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+    context->config.inputCfg.samplingRate = 44100;
+    context->config.inputCfg.bufferProvider.getBuffer = NULL;
+    context->config.inputCfg.bufferProvider.releaseBuffer = NULL;
+    context->config.inputCfg.bufferProvider.cookie = NULL;
+    context->config.inputCfg.mask = EFFECT_CONFIG_ALL;
+    context->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
+    context->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
+    context->config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+    context->config.outputCfg.samplingRate = 44100;
+    context->config.outputCfg.bufferProvider.getBuffer = NULL;
+    context->config.outputCfg.bufferProvider.releaseBuffer = NULL;
+    context->config.outputCfg.bufferProvider.cookie = NULL;
+    context->config.outputCfg.mask = EFFECT_CONFIG_ALL;
+
+    set_config(context, &context->config);
+
+    bass_ctxt->temp_disabled = false;
+    memset(&(bass_ctxt->offload_bass), 0, sizeof(struct bass_boost_params));
+
+    return 0;
+}
+
+int bassboost_enable(effect_context_t *context)
+{
+    bassboost_context_t *bass_ctxt = (bassboost_context_t *)context;
+
+    ALOGV("%s", __func__);
+
+    if (!offload_bassboost_get_enable_flag(&(bass_ctxt->offload_bass)) &&
+        !(bass_ctxt->temp_disabled)) {
+        offload_bassboost_set_enable_flag(&(bass_ctxt->offload_bass), true);
+        if (bass_ctxt->ctl && bass_ctxt->strength)
+            offload_bassboost_send_params(bass_ctxt->ctl,
+                                          &bass_ctxt->offload_bass,
+                                          OFFLOAD_SEND_BASSBOOST_ENABLE_FLAG |
+                                          OFFLOAD_SEND_BASSBOOST_STRENGTH);
+    }
+    return 0;
+}
+
+int bassboost_disable(effect_context_t *context)
+{
+    bassboost_context_t *bass_ctxt = (bassboost_context_t *)context;
+
+    ALOGV("%s", __func__);
+    if (offload_bassboost_get_enable_flag(&(bass_ctxt->offload_bass))) {
+        offload_bassboost_set_enable_flag(&(bass_ctxt->offload_bass), false);
+        if (bass_ctxt->ctl)
+            offload_bassboost_send_params(bass_ctxt->ctl,
+                                          &bass_ctxt->offload_bass,
+                                          OFFLOAD_SEND_BASSBOOST_ENABLE_FLAG);
+    }
+    return 0;
+}
+
+int bassboost_start(effect_context_t *context, output_context_t *output)
+{
+    bassboost_context_t *bass_ctxt = (bassboost_context_t *)context;
+
+    ALOGV("%s", __func__);
+    bass_ctxt->ctl = output->ctl;
+    ALOGV("output->ctl: %p", output->ctl);
+    if (offload_bassboost_get_enable_flag(&(bass_ctxt->offload_bass)))
+        if (bass_ctxt->ctl)
+            offload_bassboost_send_params(bass_ctxt->ctl, &bass_ctxt->offload_bass,
+                                          OFFLOAD_SEND_BASSBOOST_ENABLE_FLAG |
+                                          OFFLOAD_SEND_BASSBOOST_STRENGTH);
+    return 0;
+}
+
+int bassboost_stop(effect_context_t *context, output_context_t *output __unused)
+{
+    bassboost_context_t *bass_ctxt = (bassboost_context_t *)context;
+
+    ALOGV("%s", __func__);
+    bass_ctxt->ctl = NULL;
+    return 0;
+}
diff --git a/post_proc/bass_boost.h b/post_proc/bass_boost.h
new file mode 100644
index 0000000..7717b12
--- /dev/null
+++ b/post_proc/bass_boost.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OFFLOAD_EFFECT_BASS_BOOST_H_
+#define OFFLOAD_EFFECT_BASS_BOOST_H_
+
+#include "bundle.h"
+
+extern const effect_descriptor_t bassboost_descriptor;
+
+typedef struct bassboost_context_s {
+    effect_context_t common;
+
+    int strength;
+
+    // Offload vars
+    struct mixer_ctl *ctl;
+    bool temp_disabled;
+    uint32_t device;
+    struct bass_boost_params offload_bass;
+} bassboost_context_t;
+
+int bassboost_get_parameter(effect_context_t *context, effect_param_t *p,
+                            uint32_t *size);
+
+int bassboost_set_parameter(effect_context_t *context, effect_param_t *p,
+                            uint32_t size);
+
+int bassboost_set_device(effect_context_t *context,  uint32_t device);
+
+int bassboost_reset(effect_context_t *context);
+
+int bassboost_init(effect_context_t *context);
+
+int bassboost_enable(effect_context_t *context);
+
+int bassboost_disable(effect_context_t *context);
+
+int bassboost_start(effect_context_t *context, output_context_t *output);
+
+int bassboost_stop(effect_context_t *context, output_context_t *output);
+
+#endif /* OFFLOAD_EFFECT_BASS_BOOST_H_ */
diff --git a/post_proc/bundle.c b/post_proc/bundle.c
new file mode 100644
index 0000000..8518e54
--- /dev/null
+++ b/post_proc/bundle.c
@@ -0,0 +1,758 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "offload_effect_bundle"
+//#define LOG_NDEBUG 0
+
+#include <cutils/list.h>
+#include <cutils/log.h>
+#include <system/thread_defs.h>
+#include <tinyalsa/asoundlib.h>
+#include <hardware/audio_effect.h>
+
+#include "bundle.h"
+#include "equalizer.h"
+#include "bass_boost.h"
+#include "virtualizer.h"
+#include "reverb.h"
+
+enum {
+    EFFECT_STATE_UNINITIALIZED,
+    EFFECT_STATE_INITIALIZED,
+    EFFECT_STATE_ACTIVE,
+};
+
+const effect_descriptor_t *descriptors[] = {
+        &equalizer_descriptor,
+        &bassboost_descriptor,
+        &virtualizer_descriptor,
+        &aux_env_reverb_descriptor,
+        &ins_env_reverb_descriptor,
+        &aux_preset_reverb_descriptor,
+        &ins_preset_reverb_descriptor,
+        NULL,
+};
+
+pthread_once_t once = PTHREAD_ONCE_INIT;
+int init_status;
+/*
+ * list of created effects.
+ * Updated by offload_effects_bundle_hal_start_output()
+ * and offload_effects_bundle_hal_stop_output()
+ */
+struct listnode created_effects_list;
+/*
+ * list of active output streams.
+ * Updated by offload_effects_bundle_hal_start_output()
+ * and offload_effects_bundle_hal_stop_output()
+ */
+struct listnode active_outputs_list;
+/*
+ * lock must be held when modifying or accessing
+ * created_effects_list or active_outputs_list
+ */
+pthread_mutex_t lock;
+
+
+/*
+ *  Local functions
+ */
+static void init_once() {
+    list_init(&created_effects_list);
+    list_init(&active_outputs_list);
+
+    pthread_mutex_init(&lock, NULL);
+
+    init_status = 0;
+}
+
+int lib_init()
+{
+    pthread_once(&once, init_once);
+    return init_status;
+}
+
+bool effect_exists(effect_context_t *context)
+{
+    struct listnode *node;
+
+    list_for_each(node, &created_effects_list) {
+        effect_context_t *fx_ctxt = node_to_item(node,
+                                                 effect_context_t,
+                                                 effects_list_node);
+        if (fx_ctxt == context) {
+            return true;
+        }
+    }
+    return false;
+}
+
+output_context_t *get_output(audio_io_handle_t output)
+{
+    struct listnode *node;
+
+    list_for_each(node, &active_outputs_list) {
+        output_context_t *out_ctxt = node_to_item(node,
+                                                  output_context_t,
+                                                  outputs_list_node);
+        if (out_ctxt->handle == output)
+            return out_ctxt;
+    }
+    return NULL;
+}
+
+void add_effect_to_output(output_context_t * output, effect_context_t *context)
+{
+    struct listnode *fx_node;
+
+    list_for_each(fx_node, &output->effects_list) {
+        effect_context_t *fx_ctxt = node_to_item(fx_node,
+                                                 effect_context_t,
+                                                 output_node);
+        if (fx_ctxt == context)
+            return;
+    }
+    list_add_tail(&output->effects_list, &context->output_node);
+    if (context->ops.start)
+        context->ops.start(context, output);
+
+}
+
+void remove_effect_from_output(output_context_t * output,
+                               effect_context_t *context)
+{
+    struct listnode *fx_node;
+
+    list_for_each(fx_node, &output->effects_list) {
+        effect_context_t *fx_ctxt = node_to_item(fx_node,
+                                                 effect_context_t,
+                                                 output_node);
+        if (fx_ctxt == context) {
+            if (context->ops.stop)
+                context->ops.stop(context, output);
+            list_remove(&context->output_node);
+            return;
+        }
+    }
+}
+
+bool effects_enabled()
+{
+    struct listnode *out_node;
+
+    list_for_each(out_node, &active_outputs_list) {
+        struct listnode *fx_node;
+        output_context_t *out_ctxt = node_to_item(out_node,
+                                                  output_context_t,
+                                                  outputs_list_node);
+
+        list_for_each(fx_node, &out_ctxt->effects_list) {
+            effect_context_t *fx_ctxt = node_to_item(fx_node,
+                                                     effect_context_t,
+                                                     output_node);
+            if ((fx_ctxt->state == EFFECT_STATE_ACTIVE) &&
+                (fx_ctxt->ops.process != NULL))
+                return true;
+        }
+    }
+    return false;
+}
+
+
+/*
+ * Interface from audio HAL
+ */
+__attribute__ ((visibility ("default")))
+int offload_effects_bundle_hal_start_output(audio_io_handle_t output, int pcm_id)
+{
+    int ret = 0;
+    struct listnode *node;
+    char mixer_string[128];
+    output_context_t * out_ctxt = NULL;
+
+    ALOGV("%s output %d pcm_id %d", __func__, output, pcm_id);
+
+    if (lib_init() != 0)
+        return init_status;
+
+    pthread_mutex_lock(&lock);
+    if (get_output(output) != NULL) {
+        ALOGW("%s output already started", __func__);
+        ret = -ENOSYS;
+        goto exit;
+    }
+
+    out_ctxt = (output_context_t *)
+                                 malloc(sizeof(output_context_t));
+    out_ctxt->handle = output;
+    out_ctxt->pcm_device_id = pcm_id;
+
+    /* populate the mixer control to send offload parameters */
+    snprintf(mixer_string, sizeof(mixer_string),
+             "%s %d", "Audio Effects Config", out_ctxt->pcm_device_id);
+    out_ctxt->mixer = mixer_open(MIXER_CARD);
+    if (!out_ctxt->mixer) {
+        ALOGE("Failed to open mixer");
+        out_ctxt->ctl = NULL;
+        ret = -EINVAL;
+        free(out_ctxt);
+        goto exit;
+    } else {
+        out_ctxt->ctl = mixer_get_ctl_by_name(out_ctxt->mixer, mixer_string);
+        if (!out_ctxt->ctl) {
+            ALOGE("mixer_get_ctl_by_name failed");
+            mixer_close(out_ctxt->mixer);
+            out_ctxt->mixer = NULL;
+            ret = -EINVAL;
+            free(out_ctxt);
+            goto exit;
+        }
+    }
+
+    list_init(&out_ctxt->effects_list);
+
+    list_for_each(node, &created_effects_list) {
+        effect_context_t *fx_ctxt = node_to_item(node,
+                                                 effect_context_t,
+                                                 effects_list_node);
+        if (fx_ctxt->out_handle == output) {
+            if (fx_ctxt->ops.start)
+                fx_ctxt->ops.start(fx_ctxt, out_ctxt);
+            list_add_tail(&out_ctxt->effects_list, &fx_ctxt->output_node);
+        }
+    }
+    list_add_tail(&active_outputs_list, &out_ctxt->outputs_list_node);
+exit:
+    pthread_mutex_unlock(&lock);
+    return ret;
+}
+
+__attribute__ ((visibility ("default")))
+int offload_effects_bundle_hal_stop_output(audio_io_handle_t output, int pcm_id)
+{
+    int ret;
+    struct listnode *node;
+    struct listnode *fx_node;
+    output_context_t *out_ctxt;
+
+    ALOGV("%s output %d pcm_id %d", __func__, output, pcm_id);
+
+    if (lib_init() != 0)
+        return init_status;
+
+    pthread_mutex_lock(&lock);
+
+    out_ctxt = get_output(output);
+    if (out_ctxt == NULL) {
+        ALOGW("%s output not started", __func__);
+        ret = -ENOSYS;
+        goto exit;
+    }
+
+    if (out_ctxt->mixer)
+        mixer_close(out_ctxt->mixer);
+
+    list_for_each(fx_node, &out_ctxt->effects_list) {
+        effect_context_t *fx_ctxt = node_to_item(fx_node,
+                                                 effect_context_t,
+                                                 output_node);
+        if (fx_ctxt->ops.stop)
+            fx_ctxt->ops.stop(fx_ctxt, out_ctxt);
+    }
+
+    list_remove(&out_ctxt->outputs_list_node);
+
+    free(out_ctxt);
+
+exit:
+    pthread_mutex_unlock(&lock);
+    return ret;
+}
+
+
+/*
+ * Effect operations
+ */
+int set_config(effect_context_t *context, effect_config_t *config)
+{
+    context->config = *config;
+
+    if (context->ops.reset)
+        context->ops.reset(context);
+
+    return 0;
+}
+
+void get_config(effect_context_t *context, effect_config_t *config)
+{
+    *config = context->config;
+}
+
+
+/*
+ * Effect Library Interface Implementation
+ */
+int effect_lib_create(const effect_uuid_t *uuid,
+                         int32_t sessionId,
+                         int32_t ioId,
+                         effect_handle_t *pHandle) {
+    int ret;
+    int i;
+
+    ALOGV("%s: sessionId: %d, ioId: %d", __func__, sessionId, ioId);
+    if (lib_init() != 0)
+        return init_status;
+
+    if (pHandle == NULL || uuid == NULL)
+        return -EINVAL;
+
+    for (i = 0; descriptors[i] != NULL; i++) {
+        if (memcmp(uuid, &descriptors[i]->uuid, sizeof(effect_uuid_t)) == 0)
+            break;
+    }
+
+    if (descriptors[i] == NULL)
+        return -EINVAL;
+
+    effect_context_t *context;
+    if (memcmp(uuid, &equalizer_descriptor.uuid,
+        sizeof(effect_uuid_t)) == 0) {
+        equalizer_context_t *eq_ctxt = (equalizer_context_t *)
+                                       calloc(1, sizeof(equalizer_context_t));
+        context = (effect_context_t *)eq_ctxt;
+        context->ops.init = equalizer_init;
+        context->ops.reset = equalizer_reset;
+        context->ops.set_parameter = equalizer_set_parameter;
+        context->ops.get_parameter = equalizer_get_parameter;
+        context->ops.set_device = equalizer_set_device;
+        context->ops.enable = equalizer_enable;
+        context->ops.disable = equalizer_disable;
+        context->ops.start = equalizer_start;
+        context->ops.stop = equalizer_stop;
+
+        context->desc = &equalizer_descriptor;
+        eq_ctxt->ctl = NULL;
+    } else if (memcmp(uuid, &bassboost_descriptor.uuid,
+               sizeof(effect_uuid_t)) == 0) {
+        bassboost_context_t *bass_ctxt = (bassboost_context_t *)
+                                         calloc(1, sizeof(bassboost_context_t));
+        context = (effect_context_t *)bass_ctxt;
+        context->ops.init = bassboost_init;
+        context->ops.reset = bassboost_reset;
+        context->ops.set_parameter = bassboost_set_parameter;
+        context->ops.get_parameter = bassboost_get_parameter;
+        context->ops.set_device = bassboost_set_device;
+        context->ops.enable = bassboost_enable;
+        context->ops.disable = bassboost_disable;
+        context->ops.start = bassboost_start;
+        context->ops.stop = bassboost_stop;
+
+        context->desc = &bassboost_descriptor;
+        bass_ctxt->ctl = NULL;
+    } else if (memcmp(uuid, &virtualizer_descriptor.uuid,
+               sizeof(effect_uuid_t)) == 0) {
+        virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)
+                                           calloc(1, sizeof(virtualizer_context_t));
+        context = (effect_context_t *)virt_ctxt;
+        context->ops.init = virtualizer_init;
+        context->ops.reset = virtualizer_reset;
+        context->ops.set_parameter = virtualizer_set_parameter;
+        context->ops.get_parameter = virtualizer_get_parameter;
+        context->ops.set_device = virtualizer_set_device;
+        context->ops.enable = virtualizer_enable;
+        context->ops.disable = virtualizer_disable;
+        context->ops.start = virtualizer_start;
+        context->ops.stop = virtualizer_stop;
+
+        context->desc = &virtualizer_descriptor;
+        virt_ctxt->ctl = NULL;
+    } else if ((memcmp(uuid, &aux_env_reverb_descriptor.uuid,
+                sizeof(effect_uuid_t)) == 0) ||
+               (memcmp(uuid, &ins_env_reverb_descriptor.uuid,
+                sizeof(effect_uuid_t)) == 0) ||
+               (memcmp(uuid, &aux_preset_reverb_descriptor.uuid,
+                sizeof(effect_uuid_t)) == 0) ||
+               (memcmp(uuid, &ins_preset_reverb_descriptor.uuid,
+                sizeof(effect_uuid_t)) == 0)) {
+        reverb_context_t *reverb_ctxt = (reverb_context_t *)
+                                        calloc(1, sizeof(reverb_context_t));
+        context = (effect_context_t *)reverb_ctxt;
+        context->ops.init = reverb_init;
+        context->ops.reset = reverb_reset;
+        context->ops.set_parameter = reverb_set_parameter;
+        context->ops.get_parameter = reverb_get_parameter;
+        context->ops.set_device = reverb_set_device;
+        context->ops.enable = reverb_enable;
+        context->ops.disable = reverb_disable;
+        context->ops.start = reverb_start;
+        context->ops.stop = reverb_stop;
+
+        if (memcmp(uuid, &aux_env_reverb_descriptor.uuid,
+                   sizeof(effect_uuid_t)) == 0) {
+            context->desc = &aux_env_reverb_descriptor;
+            reverb_auxiliary_init(reverb_ctxt);
+        } else if (memcmp(uuid, &ins_env_reverb_descriptor.uuid,
+                   sizeof(effect_uuid_t)) == 0) {
+            context->desc = &ins_env_reverb_descriptor;
+            reverb_insert_init(reverb_ctxt);
+        } else if (memcmp(uuid, &aux_preset_reverb_descriptor.uuid,
+                   sizeof(effect_uuid_t)) == 0) {
+            context->desc = &aux_preset_reverb_descriptor;
+            reverb_auxiliary_init(reverb_ctxt);
+        } else if (memcmp(uuid, &ins_preset_reverb_descriptor.uuid,
+                   sizeof(effect_uuid_t)) == 0) {
+            context->desc = &ins_preset_reverb_descriptor;
+            reverb_preset_init(reverb_ctxt);
+        }
+        reverb_ctxt->ctl = NULL;
+    } else {
+        return -EINVAL;
+    }
+
+    context->itfe = &effect_interface;
+    context->state = EFFECT_STATE_UNINITIALIZED;
+    context->out_handle = (audio_io_handle_t)ioId;
+
+    ret = context->ops.init(context);
+    if (ret < 0) {
+        ALOGW("%s init failed", __func__);
+        free(context);
+        return ret;
+    }
+
+    context->state = EFFECT_STATE_INITIALIZED;
+
+    pthread_mutex_lock(&lock);
+    list_add_tail(&created_effects_list, &context->effects_list_node);
+    output_context_t *out_ctxt = get_output(ioId);
+    if (out_ctxt != NULL)
+        add_effect_to_output(out_ctxt, context);
+    pthread_mutex_unlock(&lock);
+
+    *pHandle = (effect_handle_t)context;
+
+    ALOGV("%s created context %p", __func__, context);
+
+    return 0;
+
+}
+
+int effect_lib_release(effect_handle_t handle)
+{
+    effect_context_t *context = (effect_context_t *)handle;
+    int status;
+
+    if (lib_init() != 0)
+        return init_status;
+
+    ALOGV("%s context %p", __func__, handle);
+    pthread_mutex_lock(&lock);
+    status = -EINVAL;
+    if (effect_exists(context)) {
+        output_context_t *out_ctxt = get_output(context->out_handle);
+        if (out_ctxt != NULL)
+            remove_effect_from_output(out_ctxt, context);
+        list_remove(&context->effects_list_node);
+        if (context->ops.release)
+            context->ops.release(context);
+        free(context);
+        status = 0;
+    }
+    pthread_mutex_unlock(&lock);
+
+    return status;
+}
+
+int effect_lib_get_descriptor(const effect_uuid_t *uuid,
+                              effect_descriptor_t *descriptor)
+{
+    int i;
+
+    if (lib_init() != 0)
+        return init_status;
+
+    if (descriptor == NULL || uuid == NULL) {
+        ALOGV("%s called with NULL pointer", __func__);
+        return -EINVAL;
+    }
+
+    for (i = 0; descriptors[i] != NULL; i++) {
+        if (memcmp(uuid, &descriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) {
+            *descriptor = *descriptors[i];
+            return 0;
+        }
+    }
+
+    return  -EINVAL;
+}
+
+
+/*
+ * Effect Control Interface Implementation
+ */
+
+/* Stub function for effect interface: never called for offloaded effects */
+int effect_process(effect_handle_t self,
+                       audio_buffer_t *inBuffer __unused,
+                       audio_buffer_t *outBuffer __unused)
+{
+    effect_context_t * context = (effect_context_t *)self;
+    int status = 0;
+
+    ALOGW("%s Called ?????", __func__);
+
+    pthread_mutex_lock(&lock);
+    if (!effect_exists(context)) {
+        status = -ENOSYS;
+        goto exit;
+    }
+
+    if (context->state != EFFECT_STATE_ACTIVE) {
+        status = -ENODATA;
+        goto exit;
+    }
+
+exit:
+    pthread_mutex_unlock(&lock);
+    return status;
+}
+
+int effect_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
+                   void *pCmdData, uint32_t *replySize, void *pReplyData)
+{
+
+    effect_context_t * context = (effect_context_t *)self;
+    int retsize;
+    int status = 0;
+
+    pthread_mutex_lock(&lock);
+
+    if (!effect_exists(context)) {
+        status = -ENOSYS;
+        goto exit;
+    }
+
+    if (context == NULL || context->state == EFFECT_STATE_UNINITIALIZED) {
+        status = -ENOSYS;
+        goto exit;
+    }
+
+    switch (cmdCode) {
+    case EFFECT_CMD_INIT:
+        if (pReplyData == NULL || *replySize != sizeof(int)) {
+            status = -EINVAL;
+            goto exit;
+        }
+        if (context->ops.init)
+            *(int *) pReplyData = context->ops.init(context);
+        else
+            *(int *) pReplyData = 0;
+        break;
+    case EFFECT_CMD_SET_CONFIG:
+        if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
+                || pReplyData == NULL || *replySize != sizeof(int)) {
+            status = -EINVAL;
+            goto exit;
+        }
+        *(int *) pReplyData = set_config(context, (effect_config_t *) pCmdData);
+        break;
+    case EFFECT_CMD_GET_CONFIG:
+        if (pReplyData == NULL ||
+            *replySize != sizeof(effect_config_t)) {
+            status = -EINVAL;
+            goto exit;
+        }
+        if (!context->offload_enabled) {
+            status = -EINVAL;
+            goto exit;
+        }
+
+        get_config(context, (effect_config_t *)pReplyData);
+        break;
+    case EFFECT_CMD_RESET:
+        if (context->ops.reset)
+            context->ops.reset(context);
+        break;
+    case EFFECT_CMD_ENABLE:
+        if (pReplyData == NULL || *replySize != sizeof(int)) {
+            status = -EINVAL;
+            goto exit;
+        }
+        if (context->state != EFFECT_STATE_INITIALIZED) {
+            status = -ENOSYS;
+            goto exit;
+        }
+        context->state = EFFECT_STATE_ACTIVE;
+        if (context->ops.enable)
+            context->ops.enable(context);
+        ALOGV("%s EFFECT_CMD_ENABLE", __func__);
+        *(int *)pReplyData = 0;
+        break;
+    case EFFECT_CMD_DISABLE:
+        if (pReplyData == NULL || *replySize != sizeof(int)) {
+            status = -EINVAL;
+            goto exit;
+        }
+        if (context->state != EFFECT_STATE_ACTIVE) {
+            status = -ENOSYS;
+            goto exit;
+        }
+        context->state = EFFECT_STATE_INITIALIZED;
+        if (context->ops.disable)
+            context->ops.disable(context);
+        ALOGV("%s EFFECT_CMD_DISABLE", __func__);
+        *(int *)pReplyData = 0;
+        break;
+    case EFFECT_CMD_GET_PARAM: {
+        if (pCmdData == NULL ||
+            cmdSize < (int)(sizeof(effect_param_t) + sizeof(uint32_t)) ||
+            pReplyData == NULL ||
+            *replySize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) +
+                               sizeof(uint16_t))) {
+            status = -EINVAL;
+            ALOGV("EFFECT_CMD_GET_PARAM invalid command cmdSize %d *replySize %d",
+                  cmdSize, *replySize);
+            goto exit;
+        }
+        if (!context->offload_enabled) {
+            status = -EINVAL;
+            goto exit;
+        }
+        effect_param_t *q = (effect_param_t *)pCmdData;
+        memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + q->psize);
+        effect_param_t *p = (effect_param_t *)pReplyData;
+        if (context->ops.get_parameter)
+            context->ops.get_parameter(context, p, replySize);
+        } break;
+    case EFFECT_CMD_SET_PARAM: {
+        if (pCmdData == NULL ||
+            cmdSize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) +
+                            sizeof(uint16_t)) ||
+            pReplyData == NULL || *replySize != sizeof(int32_t)) {
+            status = -EINVAL;
+            ALOGV("EFFECT_CMD_SET_PARAM invalid command cmdSize %d *replySize %d",
+                  cmdSize, *replySize);
+            goto exit;
+        }
+        *(int32_t *)pReplyData = 0;
+        effect_param_t *p = (effect_param_t *)pCmdData;
+        if (context->ops.set_parameter)
+            *(int32_t *)pReplyData = context->ops.set_parameter(context, p,
+                                                                *replySize);
+
+        } break;
+    case EFFECT_CMD_SET_DEVICE: {
+        uint32_t device;
+        ALOGV("\t EFFECT_CMD_SET_DEVICE start");
+        if (pCmdData == NULL || cmdSize < sizeof(uint32_t)) {
+            status = -EINVAL;
+            ALOGV("EFFECT_CMD_SET_DEVICE invalid command cmdSize %d", cmdSize);
+            goto exit;
+        }
+        device = *(uint32_t *)pCmdData;
+        if (context->ops.set_device)
+            context->ops.set_device(context, device);
+        } break;
+    case EFFECT_CMD_SET_VOLUME:
+    case EFFECT_CMD_SET_AUDIO_MODE:
+        break;
+
+    case EFFECT_CMD_OFFLOAD: {
+        output_context_t *out_ctxt;
+
+        if (cmdSize != sizeof(effect_offload_param_t) || pCmdData == NULL
+                || pReplyData == NULL || *replySize != sizeof(int)) {
+            ALOGV("%s EFFECT_CMD_OFFLOAD bad format", __func__);
+            status = -EINVAL;
+            break;
+        }
+
+        effect_offload_param_t* offload_param = (effect_offload_param_t*)pCmdData;
+
+        ALOGV("%s EFFECT_CMD_OFFLOAD offload %d output %d", __func__,
+              offload_param->isOffload, offload_param->ioHandle);
+
+        *(int *)pReplyData = 0;
+
+        context->offload_enabled = offload_param->isOffload;
+        if (context->out_handle == offload_param->ioHandle)
+            break;
+
+        out_ctxt = get_output(context->out_handle);
+        if (out_ctxt != NULL)
+            remove_effect_from_output(out_ctxt, context);
+
+        context->out_handle = offload_param->ioHandle;
+        out_ctxt = get_output(context->out_handle);
+        if (out_ctxt != NULL)
+            add_effect_to_output(out_ctxt, context);
+
+        } break;
+
+
+    default:
+        if (cmdCode >= EFFECT_CMD_FIRST_PROPRIETARY && context->ops.command)
+            status = context->ops.command(context, cmdCode, cmdSize,
+                                          pCmdData, replySize, pReplyData);
+        else {
+            ALOGW("%s invalid command %d", __func__, cmdCode);
+            status = -EINVAL;
+        }
+        break;
+    }
+
+exit:
+    pthread_mutex_unlock(&lock);
+
+    return status;
+}
+
+/* Effect Control Interface Implementation: get_descriptor */
+int effect_get_descriptor(effect_handle_t   self,
+                          effect_descriptor_t *descriptor)
+{
+    effect_context_t *context = (effect_context_t *)self;
+
+    if (!effect_exists(context) || (descriptor == NULL))
+        return -EINVAL;
+
+    *descriptor = *context->desc;
+
+    return 0;
+}
+
+bool effect_is_active(effect_context_t * ctxt) {
+    return ctxt->state == EFFECT_STATE_ACTIVE;
+}
+
+/* effect_handle_t interface implementation for offload effects */
+const struct effect_interface_s effect_interface = {
+    effect_process,
+    effect_command,
+    effect_get_descriptor,
+    NULL,
+};
+
+__attribute__ ((visibility ("default")))
+audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
+    tag : AUDIO_EFFECT_LIBRARY_TAG,
+    version : EFFECT_LIBRARY_API_VERSION,
+    name : "Offload Effects Bundle Library",
+    implementor : "The Android Open Source Project",
+    create_effect : effect_lib_create,
+    release_effect : effect_lib_release,
+    get_descriptor : effect_lib_get_descriptor,
+};
diff --git a/post_proc/bundle.h b/post_proc/bundle.h
new file mode 100644
index 0000000..70a06d4
--- /dev/null
+++ b/post_proc/bundle.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OFFLOAD_EFFECT_BUNDLE_H
+#define OFFLOAD_EFFECT_BUNDLE_H
+
+#include <tinyalsa/asoundlib.h>
+#include <sound/audio_effects.h>
+#include "effect_api.h"
+
+/* Retry for delay for mixer open */
+#define RETRY_NUMBER 10
+#define RETRY_US 500000
+
+#define MIXER_CARD 0
+#define SOUND_CARD 0
+
+extern const struct effect_interface_s effect_interface;
+
+typedef struct output_context_s output_context_t;
+typedef struct effect_ops_s effect_ops_t;
+typedef struct effect_context_s effect_context_t;
+
+struct output_context_s {
+    /* node in active_outputs_list */
+    struct listnode outputs_list_node;
+    /* io handle */
+    audio_io_handle_t handle;
+    /* list of effects attached to this output */
+    struct listnode effects_list;
+    /* pcm device id */
+    int pcm_device_id;
+    struct mixer *mixer;
+    struct mixer_ctl *ctl;
+};
+
+/* effect specific operations.
+ * Only the init() and process() operations must be defined.
+ * Others are optional.
+ */
+struct effect_ops_s {
+    int (*init)(effect_context_t *context);
+    int (*release)(effect_context_t *context);
+    int (*reset)(effect_context_t *context);
+    int (*enable)(effect_context_t *context);
+    int (*start)(effect_context_t *context, output_context_t *output);
+    int (*stop)(effect_context_t *context, output_context_t *output);
+    int (*disable)(effect_context_t *context);
+    int (*process)(effect_context_t *context, audio_buffer_t *in, audio_buffer_t *out);
+    int (*set_parameter)(effect_context_t *context, effect_param_t *param, uint32_t size);
+    int (*get_parameter)(effect_context_t *context, effect_param_t *param, uint32_t *size);
+    int (*set_device)(effect_context_t *context, uint32_t device);
+    int (*command)(effect_context_t *context, uint32_t cmdCode, uint32_t cmdSize,
+            void *pCmdData, uint32_t *replySize, void *pReplyData);
+};
+
+struct effect_context_s {
+    const struct effect_interface_s *itfe;
+    /* node in created_effects_list */
+    struct listnode effects_list_node;
+    /* node in output_context_t.effects_list */
+    struct listnode output_node;
+    effect_config_t config;
+    const effect_descriptor_t *desc;
+    /* io handle of the output the effect is attached to */
+    audio_io_handle_t out_handle;
+    uint32_t state;
+    bool offload_enabled;
+    effect_ops_t ops;
+};
+
+int set_config(effect_context_t *context, effect_config_t *config);
+
+bool effect_is_active(effect_context_t *context);
+
+#endif /* OFFLOAD_EFFECT_BUNDLE_H */
diff --git a/post_proc/effect_api.c b/post_proc/effect_api.c
new file mode 100644
index 0000000..cf3968b
--- /dev/null
+++ b/post_proc/effect_api.c
@@ -0,0 +1,599 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "offload_effect_api"
+//#define LOG_NDEBUG 0
+
+#include <stdbool.h>
+#include <cutils/log.h>
+#include <tinyalsa/asoundlib.h>
+#include <sound/audio_effects.h>
+
+#include "effect_api.h"
+
+#define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
+
+#define OFFLOAD_PRESET_START_OFFSET_FOR_OPENSL 19
+const int map_eq_opensl_preset_2_offload_preset[] = {
+    OFFLOAD_PRESET_START_OFFSET_FOR_OPENSL,   /* Normal Preset */
+    OFFLOAD_PRESET_START_OFFSET_FOR_OPENSL+1, /* Classical Preset */
+    OFFLOAD_PRESET_START_OFFSET_FOR_OPENSL+2, /* Dance Preset */
+    OFFLOAD_PRESET_START_OFFSET_FOR_OPENSL+3, /* Flat Preset */
+    OFFLOAD_PRESET_START_OFFSET_FOR_OPENSL+4, /* Folk Preset */
+    OFFLOAD_PRESET_START_OFFSET_FOR_OPENSL+5, /* Heavy Metal Preset */
+    OFFLOAD_PRESET_START_OFFSET_FOR_OPENSL+6, /* Hip Hop Preset */
+    OFFLOAD_PRESET_START_OFFSET_FOR_OPENSL+7, /* Jazz Preset */
+    OFFLOAD_PRESET_START_OFFSET_FOR_OPENSL+8, /* Pop Preset */
+    OFFLOAD_PRESET_START_OFFSET_FOR_OPENSL+9, /* Rock Preset */
+    OFFLOAD_PRESET_START_OFFSET_FOR_OPENSL+10 /* FX Booster */
+};
+
+const int map_reverb_opensl_preset_2_offload_preset
+                  [NUM_OSL_REVERB_PRESETS_SUPPORTED][2] = {
+    {1, 15},
+    {2, 16},
+    {3, 17},
+    {4, 18},
+    {5, 3},
+    {6, 20}
+};
+
+int offload_update_mixer_and_effects_ctl(int card, int device_id,
+                                         struct mixer *mixer,
+                                         struct mixer_ctl *ctl)
+{
+    char mixer_string[128];
+
+    snprintf(mixer_string, sizeof(mixer_string),
+             "%s %d", "Audio Effects Config", device_id);
+    ALOGV("%s: mixer_string: %s", __func__, mixer_string);
+    mixer = mixer_open(card);
+    if (!mixer) {
+        ALOGE("Failed to open mixer");
+        ctl = NULL;
+        return -EINVAL;
+    } else {
+        ctl = mixer_get_ctl_by_name(mixer, mixer_string);
+        if (!ctl) {
+            ALOGE("mixer_get_ctl_by_name failed");
+            mixer_close(mixer);
+            mixer = NULL;
+            return -EINVAL;
+        }
+    }
+    ALOGV("mixer: %p, ctl: %p", mixer, ctl);
+    return 0;
+}
+
+void offload_close_mixer(struct mixer *mixer)
+{
+    mixer_close(mixer);
+}
+
+void offload_bassboost_set_device(struct bass_boost_params *bassboost,
+                                  uint32_t device)
+{
+    ALOGV("%s", __func__);
+    bassboost->device = device;
+}
+
+void offload_bassboost_set_enable_flag(struct bass_boost_params *bassboost,
+                                       bool enable)
+{
+    ALOGV("%s", __func__);
+    bassboost->enable_flag = enable;
+}
+
+int offload_bassboost_get_enable_flag(struct bass_boost_params *bassboost)
+{
+    ALOGV("%s", __func__);
+    return bassboost->enable_flag;
+}
+
+void offload_bassboost_set_strength(struct bass_boost_params *bassboost,
+                                    int strength)
+{
+    ALOGV("%s", __func__);
+    bassboost->strength = strength;
+}
+
+void offload_bassboost_set_mode(struct bass_boost_params *bassboost,
+                                int mode)
+{
+    ALOGV("%s", __func__);
+    bassboost->mode = mode;
+}
+
+int offload_bassboost_send_params(struct mixer_ctl *ctl,
+                                  struct bass_boost_params *bassboost,
+                                  unsigned param_send_flags)
+{
+    int param_values[128] = {0};
+    int *p_param_values = param_values;
+
+    ALOGV("%s", __func__);
+    *p_param_values++ = BASS_BOOST_MODULE;
+    *p_param_values++ = bassboost->device;
+    *p_param_values++ = 0; /* num of commands*/
+    if (param_send_flags & OFFLOAD_SEND_BASSBOOST_ENABLE_FLAG) {
+        *p_param_values++ = BASS_BOOST_ENABLE;
+        *p_param_values++ = CONFIG_SET;
+        *p_param_values++ = 0; /* start offset if param size if greater than 128  */
+        *p_param_values++ = BASS_BOOST_ENABLE_PARAM_LEN;
+        *p_param_values++ = bassboost->enable_flag;
+        param_values[2] += 1;
+    }
+    if (param_send_flags & OFFLOAD_SEND_BASSBOOST_STRENGTH) {
+        *p_param_values++ = BASS_BOOST_STRENGTH;
+        *p_param_values++ = CONFIG_SET;
+        *p_param_values++ = 0; /* start offset if param size if greater than 128  */
+        *p_param_values++ = BASS_BOOST_STRENGTH_PARAM_LEN;
+        *p_param_values++ = bassboost->strength;
+        param_values[2] += 1;
+    }
+    if (param_send_flags & OFFLOAD_SEND_BASSBOOST_MODE) {
+        *p_param_values++ = BASS_BOOST_MODE;
+        *p_param_values++ = CONFIG_SET;
+        *p_param_values++ = 0; /* start offset if param size if greater than 128  */
+        *p_param_values++ = BASS_BOOST_MODE_PARAM_LEN;
+        *p_param_values++ = bassboost->mode;
+        param_values[2] += 1;
+    }
+
+    if (param_values[2] && ctl)
+        mixer_ctl_set_array(ctl, param_values, ARRAY_SIZE(param_values));
+
+    return 0;
+}
+
+void offload_virtualizer_set_device(struct virtualizer_params *virtualizer,
+                                    uint32_t device)
+{
+    ALOGV("%s", __func__);
+    virtualizer->device = device;
+}
+
+void offload_virtualizer_set_enable_flag(struct virtualizer_params *virtualizer,
+                                         bool enable)
+{
+    ALOGV("%s", __func__);
+    virtualizer->enable_flag = enable;
+}
+
+int offload_virtualizer_get_enable_flag(struct virtualizer_params *virtualizer)
+{
+    ALOGV("%s", __func__);
+    return virtualizer->enable_flag;
+}
+
+void offload_virtualizer_set_strength(struct virtualizer_params *virtualizer,
+                                      int strength)
+{
+    ALOGV("%s", __func__);
+    virtualizer->strength = strength;
+}
+
+void offload_virtualizer_set_out_type(struct virtualizer_params *virtualizer,
+                                      int out_type)
+{
+    ALOGV("%s", __func__);
+    virtualizer->out_type = out_type;
+}
+
+void offload_virtualizer_set_gain_adjust(struct virtualizer_params *virtualizer,
+                                         int gain_adjust)
+{
+    ALOGV("%s", __func__);
+    virtualizer->gain_adjust = gain_adjust;
+}
+
+int offload_virtualizer_send_params(struct mixer_ctl *ctl,
+                                    struct virtualizer_params *virtualizer,
+                                    unsigned param_send_flags)
+{
+    int param_values[128] = {0};
+    int *p_param_values = param_values;
+
+    ALOGV("%s", __func__);
+    *p_param_values++ = VIRTUALIZER_MODULE;
+    *p_param_values++ = virtualizer->device;
+    *p_param_values++ = 0; /* num of commands*/
+    if (param_send_flags & OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG) {
+        *p_param_values++ = VIRTUALIZER_ENABLE;
+        *p_param_values++ = CONFIG_SET;
+        *p_param_values++ = 0; /* start offset if param size if greater than 128  */
+        *p_param_values++ = VIRTUALIZER_ENABLE_PARAM_LEN;
+        *p_param_values++ = virtualizer->enable_flag;
+        param_values[2] += 1;
+    }
+    if (param_send_flags & OFFLOAD_SEND_VIRTUALIZER_STRENGTH) {
+        *p_param_values++ = VIRTUALIZER_STRENGTH;
+        *p_param_values++ = CONFIG_SET;
+        *p_param_values++ = 0; /* start offset if param size if greater than 128  */
+        *p_param_values++ = VIRTUALIZER_STRENGTH_PARAM_LEN;
+        *p_param_values++ = virtualizer->strength;
+        param_values[2] += 1;
+    }
+    if (param_send_flags & OFFLOAD_SEND_VIRTUALIZER_OUT_TYPE) {
+        *p_param_values++ = VIRTUALIZER_OUT_TYPE;
+        *p_param_values++ = CONFIG_SET;
+        *p_param_values++ = 0; /* start offset if param size if greater than 128  */
+        *p_param_values++ = VIRTUALIZER_OUT_TYPE_PARAM_LEN;
+        *p_param_values++ = virtualizer->out_type;
+        param_values[2] += 1;
+    }
+    if (param_send_flags & OFFLOAD_SEND_VIRTUALIZER_GAIN_ADJUST) {
+        *p_param_values++ = VIRTUALIZER_GAIN_ADJUST;
+        *p_param_values++ = CONFIG_SET;
+        *p_param_values++ = 0; /* start offset if param size if greater than 128  */
+        *p_param_values++ = VIRTUALIZER_GAIN_ADJUST_PARAM_LEN;
+        *p_param_values++ = virtualizer->gain_adjust;
+        param_values[2] += 1;
+    }
+
+    if (param_values[2] && ctl)
+        mixer_ctl_set_array(ctl, param_values, ARRAY_SIZE(param_values));
+
+    return 0;
+}
+
+void offload_eq_set_device(struct eq_params *eq, uint32_t device)
+{
+    ALOGV("%s", __func__);
+    eq->device = device;
+}
+
+void offload_eq_set_enable_flag(struct eq_params *eq, bool enable)
+{
+    ALOGV("%s", __func__);
+    eq->enable_flag = enable;
+}
+
+int offload_eq_get_enable_flag(struct eq_params *eq)
+{
+    ALOGV("%s", __func__);
+    return eq->enable_flag;
+}
+
+void offload_eq_set_preset(struct eq_params *eq, int preset)
+{
+    ALOGV("%s", __func__);
+    eq->config.preset_id = preset;
+    eq->config.eq_pregain = Q27_UNITY;
+}
+
+void offload_eq_set_bands_level(struct eq_params *eq, int num_bands,
+                                const uint16_t *band_freq_list,
+                                int *band_gain_list)
+{
+    int i;
+    ALOGV("%s", __func__);
+    eq->config.num_bands = num_bands;
+    for (i=0; i<num_bands; i++) {
+        eq->per_band_cfg[i].band_idx = i;
+        eq->per_band_cfg[i].filter_type = EQ_BAND_BOOST;
+        eq->per_band_cfg[i].freq_millihertz = band_freq_list[i] * 1000;
+        eq->per_band_cfg[i].gain_millibels = band_gain_list[i] * 100;
+        eq->per_band_cfg[i].quality_factor = Q8_UNITY;
+    }
+}
+
+int offload_eq_send_params(struct mixer_ctl *ctl, struct eq_params *eq,
+                           unsigned param_send_flags)
+{
+    int param_values[128] = {0};
+    int *p_param_values = param_values;
+    uint32_t i;
+
+    ALOGV("%s", __func__);
+    if (eq->config.preset_id < -1 ) {
+        ALOGV("No Valid preset to set");
+        return 0;
+    }
+    *p_param_values++ = EQ_MODULE;
+    *p_param_values++ = eq->device;
+    *p_param_values++ = 0; /* num of commands*/
+    if (param_send_flags & OFFLOAD_SEND_EQ_ENABLE_FLAG) {
+        *p_param_values++ = EQ_ENABLE;
+        *p_param_values++ = CONFIG_SET;
+        *p_param_values++ = 0; /* start offset if param size if greater than 128  */
+        *p_param_values++ = EQ_ENABLE_PARAM_LEN;
+        *p_param_values++ = eq->enable_flag;
+        param_values[2] += 1;
+    }
+    if (param_send_flags & OFFLOAD_SEND_EQ_PRESET) {
+        *p_param_values++ = EQ_CONFIG;
+        *p_param_values++ = CONFIG_SET;
+        *p_param_values++ = 0; /* start offset if param size if greater than 128  */
+        *p_param_values++ = EQ_CONFIG_PARAM_LEN;
+        *p_param_values++ = eq->config.eq_pregain;
+        *p_param_values++ =
+                     map_eq_opensl_preset_2_offload_preset[eq->config.preset_id];
+        *p_param_values++ = 0;
+        param_values[2] += 1;
+    }
+    if (param_send_flags & OFFLOAD_SEND_EQ_BANDS_LEVEL) {
+        *p_param_values++ = EQ_CONFIG;
+        *p_param_values++ = CONFIG_SET;
+        *p_param_values++ = 0; /* start offset if param size if greater than 128  */
+        *p_param_values++ = EQ_CONFIG_PARAM_LEN +
+                            eq->config.num_bands * EQ_CONFIG_PER_BAND_PARAM_LEN;
+        *p_param_values++ = eq->config.eq_pregain;
+        *p_param_values++ = CUSTOM_OPENSL_PRESET;
+        *p_param_values++ = eq->config.num_bands;
+        for (i=0; i<eq->config.num_bands; i++) {
+            *p_param_values++ = eq->per_band_cfg[i].band_idx;
+            *p_param_values++ = eq->per_band_cfg[i].filter_type;
+	    *p_param_values++ = eq->per_band_cfg[i].freq_millihertz;
+            *p_param_values++ = eq->per_band_cfg[i].gain_millibels;
+            *p_param_values++ = eq->per_band_cfg[i].quality_factor;
+        }
+        param_values[2] += 1;
+    }
+
+    if (param_values[2] && ctl)
+        mixer_ctl_set_array(ctl, param_values, ARRAY_SIZE(param_values));
+
+    return 0;
+}
+
+void offload_reverb_set_device(struct reverb_params *reverb, uint32_t device)
+{
+    ALOGV("%s", __func__);
+    reverb->device = device;
+}
+
+void offload_reverb_set_enable_flag(struct reverb_params *reverb, bool enable)
+{
+    ALOGV("%s", __func__);
+    reverb->enable_flag = enable;
+}
+
+int offload_reverb_get_enable_flag(struct reverb_params *reverb)
+{
+    ALOGV("%s", __func__);
+    return reverb->enable_flag;
+}
+
+void offload_reverb_set_mode(struct reverb_params *reverb, int mode)
+{
+    ALOGV("%s", __func__);
+    reverb->mode = mode;
+}
+
+void offload_reverb_set_preset(struct reverb_params *reverb, int preset)
+{
+    ALOGV("%s", __func__);
+    if (preset && (preset <= NUM_OSL_REVERB_PRESETS_SUPPORTED))
+        reverb->preset = map_reverb_opensl_preset_2_offload_preset[preset-1][1];
+}
+
+void offload_reverb_set_wet_mix(struct reverb_params *reverb, int wet_mix)
+{
+    ALOGV("%s", __func__);
+    reverb->wet_mix = wet_mix;
+}
+
+void offload_reverb_set_gain_adjust(struct reverb_params *reverb,
+                                    int gain_adjust)
+{
+    ALOGV("%s", __func__);
+    reverb->gain_adjust = gain_adjust;
+}
+
+void offload_reverb_set_room_level(struct reverb_params *reverb, int room_level)
+{
+    ALOGV("%s", __func__);
+    reverb->room_level = room_level;
+}
+
+void offload_reverb_set_room_hf_level(struct reverb_params *reverb,
+                                      int room_hf_level)
+{
+    ALOGV("%s", __func__);
+    reverb->room_hf_level = room_hf_level;
+}
+
+void offload_reverb_set_decay_time(struct reverb_params *reverb, int decay_time)
+{
+    ALOGV("%s", __func__);
+    reverb->decay_time = decay_time;
+}
+
+void offload_reverb_set_decay_hf_ratio(struct reverb_params *reverb,
+                                       int decay_hf_ratio)
+{
+    ALOGV("%s", __func__);
+    reverb->decay_hf_ratio = decay_hf_ratio;
+}
+
+void offload_reverb_set_reflections_level(struct reverb_params *reverb,
+                                          int reflections_level)
+{
+    ALOGV("%s", __func__);
+    reverb->reflections_level = reflections_level;
+}
+
+void offload_reverb_set_reflections_delay(struct reverb_params *reverb,
+                                          int reflections_delay)
+{
+    ALOGV("%s", __func__);
+    reverb->reflections_delay = reflections_delay;
+}
+
+void offload_reverb_set_reverb_level(struct reverb_params *reverb,
+                                     int reverb_level)
+{
+    ALOGV("%s", __func__);
+    reverb->level = reverb_level;
+}
+
+void offload_reverb_set_delay(struct reverb_params *reverb, int delay)
+{
+    ALOGV("%s", __func__);
+    reverb->delay = delay;
+}
+
+void offload_reverb_set_diffusion(struct reverb_params *reverb, int diffusion)
+{
+    ALOGV("%s", __func__);
+    reverb->diffusion = diffusion;
+}
+
+void offload_reverb_set_density(struct reverb_params *reverb, int density)
+{
+    ALOGV("%s", __func__);
+    reverb->density = density;
+}
+
+int offload_reverb_send_params(struct mixer_ctl *ctl,
+                               struct reverb_params *reverb,
+                               unsigned param_send_flags)
+{
+    int param_values[128] = {0};
+    int *p_param_values = param_values;
+
+    ALOGV("%s", __func__);
+    *p_param_values++ = REVERB_MODULE;
+    *p_param_values++ = reverb->device;
+    *p_param_values++ = 0; /* num of commands*/
+
+    if (param_send_flags & OFFLOAD_SEND_REVERB_ENABLE_FLAG) {
+        *p_param_values++ = REVERB_ENABLE;
+        *p_param_values++ = CONFIG_SET;
+        *p_param_values++ = 0; /* start offset if param size if greater than 128  */
+        *p_param_values++ = REVERB_ENABLE_PARAM_LEN;
+        *p_param_values++ = reverb->enable_flag;
+        param_values[2] += 1;
+    }
+    if (param_send_flags & OFFLOAD_SEND_REVERB_MODE) {
+        *p_param_values++ = REVERB_MODE;
+        *p_param_values++ = CONFIG_SET;
+        *p_param_values++ = 0; /* start offset if param size if greater than 128  */
+        *p_param_values++ = REVERB_MODE_PARAM_LEN;
+        *p_param_values++ = reverb->mode;
+        param_values[2] += 1;
+    }
+    if (param_send_flags & OFFLOAD_SEND_REVERB_PRESET) {
+        *p_param_values++ = REVERB_PRESET;
+        *p_param_values++ = CONFIG_SET;
+        *p_param_values++ = 0; /* start offset if param size if greater than 128  */
+        *p_param_values++ = REVERB_PRESET_PARAM_LEN;
+        *p_param_values++ = reverb->preset;
+        param_values[2] += 1;
+    }
+    if (param_send_flags & OFFLOAD_SEND_REVERB_WET_MIX) {
+        *p_param_values++ = REVERB_WET_MIX;
+        *p_param_values++ = CONFIG_SET;
+        *p_param_values++ = 0; /* start offset if param size if greater than 128  */
+        *p_param_values++ = REVERB_WET_MIX_PARAM_LEN;
+        *p_param_values++ = reverb->wet_mix;
+        param_values[2] += 1;
+    }
+    if (param_send_flags & OFFLOAD_SEND_REVERB_GAIN_ADJUST) {
+        *p_param_values++ = REVERB_GAIN_ADJUST;
+        *p_param_values++ = CONFIG_SET;
+        *p_param_values++ = 0; /* start offset if param size if greater than 128  */
+        *p_param_values++ = REVERB_GAIN_ADJUST_PARAM_LEN;
+        *p_param_values++ = reverb->gain_adjust;
+        param_values[2] += 1;
+    }
+    if (param_send_flags & OFFLOAD_SEND_REVERB_ROOM_LEVEL) {
+        *p_param_values++ = REVERB_ROOM_LEVEL;
+        *p_param_values++ = CONFIG_SET;
+        *p_param_values++ = 0; /* start offset if param size if greater than 128  */
+        *p_param_values++ = REVERB_ROOM_LEVEL_PARAM_LEN;
+        *p_param_values++ = reverb->room_level;
+        param_values[2] += 1;
+    }
+    if (param_send_flags & OFFLOAD_SEND_REVERB_ROOM_HF_LEVEL) {
+        *p_param_values++ = REVERB_ROOM_HF_LEVEL;
+        *p_param_values++ = CONFIG_SET;
+        *p_param_values++ = 0; /* start offset if param size if greater than 128  */
+        *p_param_values++ = REVERB_ROOM_HF_LEVEL_PARAM_LEN;
+        *p_param_values++ = reverb->room_hf_level;
+        param_values[2] += 1;
+    }
+    if (param_send_flags & OFFLOAD_SEND_REVERB_DECAY_TIME) {
+        *p_param_values++ = REVERB_DECAY_TIME;
+        *p_param_values++ = CONFIG_SET;
+        *p_param_values++ = 0; /* start offset if param size if greater than 128  */
+        *p_param_values++ = REVERB_DECAY_TIME_PARAM_LEN;
+        *p_param_values++ = reverb->decay_time;
+        param_values[2] += 1;
+    }
+    if (param_send_flags & OFFLOAD_SEND_REVERB_DECAY_HF_RATIO) {
+        *p_param_values++ = REVERB_DECAY_HF_RATIO;
+        *p_param_values++ = CONFIG_SET;
+        *p_param_values++ = 0; /* start offset if param size if greater than 128  */
+        *p_param_values++ = REVERB_DECAY_HF_RATIO_PARAM_LEN;
+        *p_param_values++ = reverb->decay_hf_ratio;
+        param_values[2] += 1;
+    }
+    if (param_send_flags & OFFLOAD_SEND_REVERB_REFLECTIONS_LEVEL) {
+        *p_param_values++ = REVERB_REFLECTIONS_LEVEL;
+        *p_param_values++ = CONFIG_SET;
+        *p_param_values++ = 0; /* start offset if param size if greater than 128  */
+        *p_param_values++ = REVERB_REFLECTIONS_LEVEL_PARAM_LEN;
+        *p_param_values++ = reverb->reflections_level;
+        param_values[2] += 1;
+    }
+    if (param_send_flags & OFFLOAD_SEND_REVERB_REFLECTIONS_DELAY) {
+        *p_param_values++ = REVERB_REFLECTIONS_DELAY;
+        *p_param_values++ = CONFIG_SET;
+        *p_param_values++ = 0; /* start offset if param size if greater than 128  */
+        *p_param_values++ = REVERB_REFLECTIONS_DELAY_PARAM_LEN;
+        *p_param_values++ = reverb->reflections_delay;
+        param_values[2] += 1;
+    }
+    if (param_send_flags & OFFLOAD_SEND_REVERB_LEVEL) {
+        *p_param_values++ = REVERB_LEVEL;
+        *p_param_values++ = CONFIG_SET;
+        *p_param_values++ = 0; /* start offset if param size if greater than 128  */
+        *p_param_values++ = REVERB_LEVEL_PARAM_LEN;
+        *p_param_values++ = reverb->level;
+        param_values[2] += 1;
+    }
+    if (param_send_flags & OFFLOAD_SEND_REVERB_DELAY) {
+        *p_param_values++ = REVERB_DELAY;
+        *p_param_values++ = CONFIG_SET;
+        *p_param_values++ = 0; /* start offset if param size if greater than 128  */
+        *p_param_values++ = REVERB_DELAY_PARAM_LEN;
+        *p_param_values++ = reverb->delay;
+        param_values[2] += 1;
+    }
+    if (param_send_flags & OFFLOAD_SEND_REVERB_DIFFUSION) {
+        *p_param_values++ = REVERB_DIFFUSION;
+        *p_param_values++ = CONFIG_SET;
+        *p_param_values++ = 0; /* start offset if param size if greater than 128  */
+        *p_param_values++ = REVERB_DIFFUSION_PARAM_LEN;
+        *p_param_values++ = reverb->diffusion;
+        param_values[2] += 1;
+    }
+    if (param_send_flags & OFFLOAD_SEND_REVERB_DENSITY) {
+        *p_param_values++ = REVERB_DENSITY;
+        *p_param_values++ = CONFIG_SET;
+        *p_param_values++ = 0; /* start offset if param size if greater than 128  */
+        *p_param_values++ = REVERB_DENSITY_PARAM_LEN;
+        *p_param_values++ = reverb->density;
+        param_values[2] += 1;
+    }
+
+    if (param_values[2] && ctl)
+        mixer_ctl_set_array(ctl, param_values, ARRAY_SIZE(param_values));
+
+    return 0;
+}
diff --git a/post_proc/effect_api.h b/post_proc/effect_api.h
new file mode 100644
index 0000000..b169319
--- /dev/null
+++ b/post_proc/effect_api.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OFFLOAD_EFFECT_API_H_
+#define OFFLOAD_EFFECT_API_H_
+
+int offload_update_mixer_and_effects_ctl(int card, int device_id,
+                                         struct mixer *mixer,
+                                         struct mixer_ctl *ctl);
+void offload_close_mixer(struct mixer *mixer);
+
+#define OFFLOAD_SEND_BASSBOOST_ENABLE_FLAG      (1 << 0)
+#define OFFLOAD_SEND_BASSBOOST_STRENGTH         \
+                                          (OFFLOAD_SEND_BASSBOOST_ENABLE_FLAG << 1)
+#define OFFLOAD_SEND_BASSBOOST_MODE             \
+                                          (OFFLOAD_SEND_BASSBOOST_STRENGTH << 1)
+void offload_bassboost_set_device(struct bass_boost_params *bassboost,
+                                  uint32_t device);
+void offload_bassboost_set_enable_flag(struct bass_boost_params *bassboost,
+                                       bool enable);
+int offload_bassboost_get_enable_flag(struct bass_boost_params *bassboost);
+void offload_bassboost_set_strength(struct bass_boost_params *bassboost,
+                                    int strength);
+void offload_bassboost_set_mode(struct bass_boost_params *bassboost,
+                                int mode);
+int offload_bassboost_send_params(struct mixer_ctl *ctl,
+                                  struct bass_boost_params *bassboost,
+                                  unsigned param_send_flags);
+
+#define OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG    (1 << 0)
+#define OFFLOAD_SEND_VIRTUALIZER_STRENGTH       \
+                                          (OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG << 1)
+#define OFFLOAD_SEND_VIRTUALIZER_OUT_TYPE       \
+                                          (OFFLOAD_SEND_VIRTUALIZER_STRENGTH << 1)
+#define OFFLOAD_SEND_VIRTUALIZER_GAIN_ADJUST    \
+                                          (OFFLOAD_SEND_VIRTUALIZER_OUT_TYPE << 1)
+void offload_virtualizer_set_device(struct virtualizer_params *virtualizer,
+                                    uint32_t device);
+void offload_virtualizer_set_enable_flag(struct virtualizer_params *virtualizer,
+                                         bool enable);
+int offload_virtualizer_get_enable_flag(struct virtualizer_params *virtualizer);
+void offload_virtualizer_set_strength(struct virtualizer_params *virtualizer,
+                                      int strength);
+void offload_virtualizer_set_out_type(struct virtualizer_params *virtualizer,
+                                      int out_type);
+void offload_virtualizer_set_gain_adjust(struct virtualizer_params *virtualizer,
+                                         int gain_adjust);
+int offload_virtualizer_send_params(struct mixer_ctl *ctl,
+                                  struct virtualizer_params *virtualizer,
+                                  unsigned param_send_flags);
+
+#define OFFLOAD_SEND_EQ_ENABLE_FLAG             (1 << 0)
+#define OFFLOAD_SEND_EQ_PRESET                  \
+                                          (OFFLOAD_SEND_EQ_ENABLE_FLAG << 1)
+#define OFFLOAD_SEND_EQ_BANDS_LEVEL             \
+                                          (OFFLOAD_SEND_EQ_PRESET << 1)
+void offload_eq_set_device(struct eq_params *eq, uint32_t device);
+void offload_eq_set_enable_flag(struct eq_params *eq, bool enable);
+int offload_eq_get_enable_flag(struct eq_params *eq);
+void offload_eq_set_preset(struct eq_params *eq, int preset);
+void offload_eq_set_bands_level(struct eq_params *eq, int num_bands,
+                                const uint16_t *band_freq_list,
+                                int *band_gain_list);
+int offload_eq_send_params(struct mixer_ctl *ctl, struct eq_params *eq,
+                           unsigned param_send_flags);
+
+#define OFFLOAD_SEND_REVERB_ENABLE_FLAG         (1 << 0)
+#define OFFLOAD_SEND_REVERB_MODE                \
+                                          (OFFLOAD_SEND_REVERB_ENABLE_FLAG << 1)
+#define OFFLOAD_SEND_REVERB_PRESET              \
+                                          (OFFLOAD_SEND_REVERB_MODE << 1)
+#define OFFLOAD_SEND_REVERB_WET_MIX             \
+                                          (OFFLOAD_SEND_REVERB_PRESET << 1)
+#define OFFLOAD_SEND_REVERB_GAIN_ADJUST	        \
+                                          (OFFLOAD_SEND_REVERB_WET_MIX << 1)
+#define OFFLOAD_SEND_REVERB_ROOM_LEVEL	        \
+                                          (OFFLOAD_SEND_REVERB_GAIN_ADJUST << 1)
+#define OFFLOAD_SEND_REVERB_ROOM_HF_LEVEL       \
+                                          (OFFLOAD_SEND_REVERB_ROOM_LEVEL << 1)
+#define OFFLOAD_SEND_REVERB_DECAY_TIME          \
+                                          (OFFLOAD_SEND_REVERB_ROOM_HF_LEVEL << 1)
+#define OFFLOAD_SEND_REVERB_DECAY_HF_RATIO      \
+                                          (OFFLOAD_SEND_REVERB_DECAY_TIME << 1)
+#define OFFLOAD_SEND_REVERB_REFLECTIONS_LEVEL   \
+                                          (OFFLOAD_SEND_REVERB_DECAY_HF_RATIO << 1)
+#define OFFLOAD_SEND_REVERB_REFLECTIONS_DELAY   \
+                                          (OFFLOAD_SEND_REVERB_REFLECTIONS_LEVEL << 1)
+#define OFFLOAD_SEND_REVERB_LEVEL               \
+                                          (OFFLOAD_SEND_REVERB_REFLECTIONS_DELAY << 1)
+#define OFFLOAD_SEND_REVERB_DELAY               \
+                                          (OFFLOAD_SEND_REVERB_LEVEL << 1)
+#define OFFLOAD_SEND_REVERB_DIFFUSION           \
+                                          (OFFLOAD_SEND_REVERB_DELAY << 1)
+#define OFFLOAD_SEND_REVERB_DENSITY             \
+                                          (OFFLOAD_SEND_REVERB_DIFFUSION << 1)
+void offload_reverb_set_device(struct reverb_params *reverb, uint32_t device);
+void offload_reverb_set_enable_flag(struct reverb_params *reverb, bool enable);
+int offload_reverb_get_enable_flag(struct reverb_params *reverb);
+void offload_reverb_set_mode(struct reverb_params *reverb, int mode);
+void offload_reverb_set_preset(struct reverb_params *reverb, int preset);
+void offload_reverb_set_wet_mix(struct reverb_params *reverb, int wet_mix);
+void offload_reverb_set_gain_adjust(struct reverb_params *reverb,
+                                    int gain_adjust);
+void offload_reverb_set_room_level(struct reverb_params *reverb,
+                                   int room_level);
+void offload_reverb_set_room_hf_level(struct reverb_params *reverb,
+                                      int room_hf_level);
+void offload_reverb_set_decay_time(struct reverb_params *reverb,
+                                   int decay_time);
+void offload_reverb_set_decay_hf_ratio(struct reverb_params *reverb,
+                                       int decay_hf_ratio);
+void offload_reverb_set_reflections_level(struct reverb_params *reverb,
+                                          int reflections_level);
+void offload_reverb_set_reflections_delay(struct reverb_params *reverb,
+                                          int reflections_delay);
+void offload_reverb_set_reverb_level(struct reverb_params *reverb,
+                                     int reverb_level);
+void offload_reverb_set_delay(struct reverb_params *reverb, int delay);
+void offload_reverb_set_diffusion(struct reverb_params *reverb, int diffusion);
+void offload_reverb_set_density(struct reverb_params *reverb, int density);
+int offload_reverb_send_params(struct mixer_ctl *ctl,
+                               struct reverb_params *reverb,
+                               unsigned param_send_flags);
+
+#endif /*OFFLOAD_EFFECT_API_H_*/
diff --git a/post_proc/equalizer.c b/post_proc/equalizer.c
new file mode 100644
index 0000000..7cff348
--- /dev/null
+++ b/post_proc/equalizer.c
@@ -0,0 +1,506 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "offload_effect_equalizer"
+//#define LOG_NDEBUG 0
+
+#include <cutils/list.h>
+#include <cutils/log.h>
+#include <tinyalsa/asoundlib.h>
+#include <sound/audio_effects.h>
+#include <audio_effects/effect_equalizer.h>
+
+#include "effect_api.h"
+#include "equalizer.h"
+
+/* Offload equalizer UUID: a0dac280-401c-11e3-9379-0002a5d5c51b */
+const effect_descriptor_t equalizer_descriptor = {
+        {0x0bed4300, 0xddd6, 0x11db, 0x8f34, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // type
+        {0xa0dac280, 0x401c, 0x11e3, 0x9379, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // uuid
+        EFFECT_CONTROL_API_VERSION,
+        (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_HW_ACC_TUNNEL),
+        0, /* TODO */
+        1,
+        "MSM offload equalizer",
+        "The Android Open Source Project",
+};
+
+static const char *equalizer_preset_names[] = {
+                                        "Normal",
+                                        "Classical",
+                                        "Dance",
+                                        "Flat",
+                                        "Folk",
+                                        "Heavy Metal",
+                                        "Hip Hop",
+                                        "Jazz",
+                                        "Pop",
+                                        "Rock"
+					};
+
+static const uint32_t equalizer_band_freq_range[NUM_EQ_BANDS][2] = {
+                                       {30000, 120000},
+                                       {120001, 460000},
+                                       {460001, 1800000},
+                                       {1800001, 7000000},
+                                       {7000001, 20000000}};
+
+static const int16_t equalizer_band_presets_level[] = {
+                                        3, 0, 0, 0, 3,      /* Normal Preset */
+                                        5, 3, -2, 4, 4,     /* Classical Preset */
+                                        6, 0, 2, 4, 1,      /* Dance Preset */
+                                        0, 0, 0, 0, 0,      /* Flat Preset */
+                                        3, 0, 0, 2, -1,     /* Folk Preset */
+                                        4, 1, 9, 3, 0,      /* Heavy Metal Preset */
+                                        5, 3, 0, 1, 3,      /* Hip Hop Preset */
+                                        4, 2, -2, 2, 5,     /* Jazz Preset */
+                                       -1, 2, 5, 1, -2,     /* Pop Preset */
+                                        5, 3, -1, 3, 5};    /* Rock Preset */
+
+const uint16_t equalizer_band_presets_freq[NUM_EQ_BANDS] = {
+                                        60,      /* Frequencies in Hz */
+                                        230,
+                                        910,
+                                        3600,
+                                        14000
+};
+
+/*
+ * Equalizer operations
+ */
+
+int equalizer_get_band_level(equalizer_context_t *context, int32_t band)
+{
+    ALOGV("%s: band: %d level: %d", __func__, band,
+           context->band_levels[band] * 100);
+    return context->band_levels[band] * 100;
+}
+
+int equalizer_set_band_level(equalizer_context_t *context, int32_t band,
+                             int32_t level)
+{
+    ALOGV("%s: band: %d, level: %d", __func__, band, level);
+    if (level > 0) {
+        level = (int)((level+50)/100);
+    } else {
+        level = (int)((level-50)/100);
+    }
+    context->band_levels[band] = level;
+    context->preset = PRESET_CUSTOM;
+
+    offload_eq_set_preset(&(context->offload_eq), PRESET_CUSTOM);
+    offload_eq_set_bands_level(&(context->offload_eq),
+                               NUM_EQ_BANDS,
+                               equalizer_band_presets_freq,
+                               context->band_levels);
+    if (context->ctl)
+        offload_eq_send_params(context->ctl, &context->offload_eq,
+                               OFFLOAD_SEND_EQ_ENABLE_FLAG |
+                               OFFLOAD_SEND_EQ_BANDS_LEVEL);
+    return 0;
+}
+
+int equalizer_get_center_frequency(equalizer_context_t *context __unused, int32_t band)
+{
+    ALOGV("%s: band: %d", __func__, band);
+    return (equalizer_band_freq_range[band][0] +
+            equalizer_band_freq_range[band][1]) / 2;
+}
+
+int equalizer_get_band_freq_range(equalizer_context_t *context __unused, int32_t band,
+                                  uint32_t *low, uint32_t *high)
+{
+    ALOGV("%s: band: %d", __func__, band);
+    *low = equalizer_band_freq_range[band][0];
+    *high = equalizer_band_freq_range[band][1];
+   return 0;
+}
+
+int equalizer_get_band(equalizer_context_t *context __unused, uint32_t freq)
+{
+    int i;
+
+    ALOGV("%s: freq: %d", __func__, freq);
+    for (i = 0; i < NUM_EQ_BANDS; i++) {
+        if (freq <= equalizer_band_freq_range[i][1]) {
+            return i;
+        }
+    }
+    return NUM_EQ_BANDS - 1;
+}
+
+int equalizer_get_preset(equalizer_context_t *context)
+{
+    ALOGV("%s: preset: %d", __func__, context->preset);
+    return context->preset;
+}
+
+int equalizer_set_preset(equalizer_context_t *context, int preset)
+{
+    int i;
+
+    ALOGV("%s: preset: %d", __func__, preset);
+    context->preset = preset;
+    for (i=0; i<NUM_EQ_BANDS; i++)
+        context->band_levels[i] =
+                 equalizer_band_presets_level[i + preset * NUM_EQ_BANDS];
+
+    offload_eq_set_preset(&(context->offload_eq), preset);
+    offload_eq_set_bands_level(&(context->offload_eq),
+                               NUM_EQ_BANDS,
+                               equalizer_band_presets_freq,
+                               context->band_levels);
+    if(context->ctl)
+        offload_eq_send_params(context->ctl, &context->offload_eq,
+                               OFFLOAD_SEND_EQ_ENABLE_FLAG |
+                               OFFLOAD_SEND_EQ_PRESET);
+    return 0;
+}
+
+const char * equalizer_get_preset_name(equalizer_context_t *context __unused,
+                                       int32_t preset)
+{
+    ALOGV("%s: preset: %s", __func__, equalizer_preset_names[preset]);
+    if (preset == PRESET_CUSTOM) {
+        return "Custom";
+    } else {
+        return equalizer_preset_names[preset];
+    }
+}
+
+int equalizer_get_num_presets(equalizer_context_t *context __unused)
+{
+    ALOGV("%s: presets_num: %d", __func__,
+           sizeof(equalizer_preset_names)/sizeof(char *));
+    return sizeof(equalizer_preset_names)/sizeof(char *);
+}
+
+int equalizer_get_parameter(effect_context_t *context, effect_param_t *p,
+                            uint32_t *size)
+{
+    equalizer_context_t *eq_ctxt = (equalizer_context_t *)context;
+    int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t);
+    int32_t *param_tmp = (int32_t *)p->data;
+    int32_t param = *param_tmp++;
+    int32_t param2;
+    char *name;
+    void *value = p->data + voffset;
+    int i;
+
+    ALOGV("%s", __func__);
+
+    p->status = 0;
+
+    switch (param) {
+    case EQ_PARAM_NUM_BANDS:
+    case EQ_PARAM_CUR_PRESET:
+    case EQ_PARAM_GET_NUM_OF_PRESETS:
+    case EQ_PARAM_BAND_LEVEL:
+    case EQ_PARAM_GET_BAND:
+        if (p->vsize < sizeof(int16_t))
+           p->status = -EINVAL;
+        p->vsize = sizeof(int16_t);
+        break;
+
+    case EQ_PARAM_LEVEL_RANGE:
+        if (p->vsize < 2 * sizeof(int16_t))
+            p->status = -EINVAL;
+        p->vsize = 2 * sizeof(int16_t);
+        break;
+    case EQ_PARAM_BAND_FREQ_RANGE:
+       if (p->vsize < 2 * sizeof(int32_t))
+            p->status = -EINVAL;
+        p->vsize = 2 * sizeof(int32_t);
+        break;
+
+   case EQ_PARAM_CENTER_FREQ:
+        if (p->vsize < sizeof(int32_t))
+            p->status = -EINVAL;
+        p->vsize = sizeof(int32_t);
+        break;
+
+    case EQ_PARAM_GET_PRESET_NAME:
+        break;
+
+    case EQ_PARAM_PROPERTIES:
+        if (p->vsize < (2 + NUM_EQ_BANDS) * sizeof(uint16_t))
+            p->status = -EINVAL;
+        p->vsize = (2 + NUM_EQ_BANDS) * sizeof(uint16_t);
+        break;
+
+    default:
+        p->status = -EINVAL;
+    }
+
+    *size = sizeof(effect_param_t) + voffset + p->vsize;
+
+    if (p->status != 0)
+        return 0;
+
+    switch (param) {
+    case EQ_PARAM_NUM_BANDS:
+	ALOGV("%s: EQ_PARAM_NUM_BANDS", __func__);
+        *(uint16_t *)value = (uint16_t)NUM_EQ_BANDS;
+        break;
+
+    case EQ_PARAM_LEVEL_RANGE:
+	ALOGV("%s: EQ_PARAM_LEVEL_RANGE", __func__);
+        *(int16_t *)value = -1500;
+        *((int16_t *)value + 1) = 1500;
+        break;
+
+    case EQ_PARAM_BAND_LEVEL:
+	ALOGV("%s: EQ_PARAM_BAND_LEVEL", __func__);
+        param2 = *param_tmp;
+        if (param2 >= NUM_EQ_BANDS) {
+            p->status = -EINVAL;
+            break;
+        }
+        *(int16_t *)value = (int16_t)equalizer_get_band_level(eq_ctxt, param2);
+        break;
+
+    case EQ_PARAM_CENTER_FREQ:
+	ALOGV("%s: EQ_PARAM_CENTER_FREQ", __func__);
+        param2 = *param_tmp;
+        if (param2 >= NUM_EQ_BANDS) {
+           p->status = -EINVAL;
+            break;
+        }
+        *(int32_t *)value = equalizer_get_center_frequency(eq_ctxt, param2);
+        break;
+
+    case EQ_PARAM_BAND_FREQ_RANGE:
+	ALOGV("%s: EQ_PARAM_BAND_FREQ_RANGE", __func__);
+        param2 = *param_tmp;
+        if (param2 >= NUM_EQ_BANDS) {
+            p->status = -EINVAL;
+           break;
+        }
+       equalizer_get_band_freq_range(eq_ctxt, param2, (uint32_t *)value,
+                                     ((uint32_t *)value + 1));
+        break;
+
+    case EQ_PARAM_GET_BAND:
+	ALOGV("%s: EQ_PARAM_GET_BAND", __func__);
+        param2 = *param_tmp;
+        *(uint16_t *)value = (uint16_t)equalizer_get_band(eq_ctxt, param2);
+        break;
+
+    case EQ_PARAM_CUR_PRESET:
+	ALOGV("%s: EQ_PARAM_CUR_PRESET", __func__);
+        *(uint16_t *)value = (uint16_t)equalizer_get_preset(eq_ctxt);
+        break;
+
+    case EQ_PARAM_GET_NUM_OF_PRESETS:
+	ALOGV("%s: EQ_PARAM_GET_NUM_OF_PRESETS", __func__);
+        *(uint16_t *)value = (uint16_t)equalizer_get_num_presets(eq_ctxt);
+        break;
+
+    case EQ_PARAM_GET_PRESET_NAME:
+	ALOGV("%s: EQ_PARAM_GET_PRESET_NAME", __func__);
+        param2 = *param_tmp;
+	ALOGV("param2: %d", param2);
+        if (param2 >= equalizer_get_num_presets(eq_ctxt)) {
+            p->status = -EINVAL;
+            break;
+        }
+        name = (char *)value;
+        strlcpy(name, equalizer_get_preset_name(eq_ctxt, param2), p->vsize - 1);
+        name[p->vsize - 1] = 0;
+        p->vsize = strlen(name) + 1;
+        break;
+
+    case EQ_PARAM_PROPERTIES: {
+	ALOGV("%s: EQ_PARAM_PROPERTIES", __func__);
+        int16_t *prop = (int16_t *)value;
+        prop[0] = (int16_t)equalizer_get_preset(eq_ctxt);
+        prop[1] = (int16_t)NUM_EQ_BANDS;
+        for (i = 0; i < NUM_EQ_BANDS; i++) {
+            prop[2 + i] = (int16_t)equalizer_get_band_level(eq_ctxt, i);
+        }
+    } break;
+
+    default:
+        p->status = -EINVAL;
+        break;
+    }
+
+    return 0;
+}
+
+int equalizer_set_parameter(effect_context_t *context, effect_param_t *p,
+                            uint32_t size __unused)
+{
+    equalizer_context_t *eq_ctxt = (equalizer_context_t *)context;
+    int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t);
+    void *value = p->data + voffset;
+    int32_t *param_tmp = (int32_t *)p->data;
+    int32_t param = *param_tmp++;
+    int32_t preset;
+    int32_t band;
+    int32_t level;
+    int i;
+
+    ALOGV("%s", __func__);
+
+    p->status = 0;
+
+    switch (param) {
+    case EQ_PARAM_CUR_PRESET:
+	ALOGV("EQ_PARAM_CUR_PRESET");
+        preset = (int32_t)(*(uint16_t *)value);
+
+        if ((preset >= equalizer_get_num_presets(eq_ctxt)) || (preset < 0)) {
+           p->status = -EINVAL;
+            break;
+        }
+        equalizer_set_preset(eq_ctxt, preset);
+        break;
+    case EQ_PARAM_BAND_LEVEL:
+	ALOGV("EQ_PARAM_BAND_LEVEL");
+        band =  *param_tmp;
+        level = (int32_t)(*(int16_t *)value);
+        if (band >= NUM_EQ_BANDS) {
+           p->status = -EINVAL;
+            break;
+        }
+        equalizer_set_band_level(eq_ctxt, band, level);
+        break;
+    case EQ_PARAM_PROPERTIES: {
+	ALOGV("EQ_PARAM_PROPERTIES");
+        int16_t *prop = (int16_t *)value;
+        if ((int)prop[0] >= equalizer_get_num_presets(eq_ctxt)) {
+            p->status = -EINVAL;
+            break;
+        }
+        if (prop[0] >= 0) {
+            equalizer_set_preset(eq_ctxt, (int)prop[0]);
+        } else {
+            if ((int)prop[1] != NUM_EQ_BANDS) {
+                p->status = -EINVAL;
+                break;
+            }
+            for (i = 0; i < NUM_EQ_BANDS; i++) {
+               equalizer_set_band_level(eq_ctxt, i, (int)prop[2 + i]);
+            }
+        }
+    } break;
+    default:
+        p->status = -EINVAL;
+        break;
+    }
+
+    return 0;
+}
+
+int equalizer_set_device(effect_context_t *context,  uint32_t device)
+{
+    ALOGV("%s: device: %d", __func__, device);
+    equalizer_context_t *eq_ctxt = (equalizer_context_t *)context;
+    eq_ctxt->device = device;
+    offload_eq_set_device(&(eq_ctxt->offload_eq), device);
+    return 0;
+}
+
+int equalizer_reset(effect_context_t *context)
+{
+    equalizer_context_t *eq_ctxt = (equalizer_context_t *)context;
+
+    return 0;
+}
+
+int equalizer_init(effect_context_t *context)
+{
+    ALOGV("%s", __func__);
+    equalizer_context_t *eq_ctxt = (equalizer_context_t *)context;
+
+    context->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
+    context->config.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
+    context->config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+    context->config.inputCfg.samplingRate = 44100;
+    context->config.inputCfg.bufferProvider.getBuffer = NULL;
+    context->config.inputCfg.bufferProvider.releaseBuffer = NULL;
+    context->config.inputCfg.bufferProvider.cookie = NULL;
+    context->config.inputCfg.mask = EFFECT_CONFIG_ALL;
+    context->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
+    context->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
+    context->config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+    context->config.outputCfg.samplingRate = 44100;
+    context->config.outputCfg.bufferProvider.getBuffer = NULL;
+    context->config.outputCfg.bufferProvider.releaseBuffer = NULL;
+    context->config.outputCfg.bufferProvider.cookie = NULL;
+    context->config.outputCfg.mask = EFFECT_CONFIG_ALL;
+
+    set_config(context, &context->config);
+
+    memset(&(eq_ctxt->offload_eq), 0, sizeof(struct eq_params));
+    offload_eq_set_preset(&(eq_ctxt->offload_eq), INVALID_PRESET);
+
+    return 0;
+}
+
+int equalizer_enable(effect_context_t *context)
+{
+    equalizer_context_t *eq_ctxt = (equalizer_context_t *)context;
+
+    ALOGV("%s", __func__);
+
+    if (!offload_eq_get_enable_flag(&(eq_ctxt->offload_eq))) {
+        offload_eq_set_enable_flag(&(eq_ctxt->offload_eq), true);
+        if (eq_ctxt->ctl)
+            offload_eq_send_params(eq_ctxt->ctl, &eq_ctxt->offload_eq,
+                                   OFFLOAD_SEND_EQ_ENABLE_FLAG |
+                                   OFFLOAD_SEND_EQ_BANDS_LEVEL);
+    }
+    return 0;
+}
+
+int equalizer_disable(effect_context_t *context)
+{
+    equalizer_context_t *eq_ctxt = (equalizer_context_t *)context;
+
+    ALOGV("%s", __func__);
+    if (offload_eq_get_enable_flag(&(eq_ctxt->offload_eq))) {
+        offload_eq_set_enable_flag(&(eq_ctxt->offload_eq), false);
+        if (eq_ctxt->ctl)
+            offload_eq_send_params(eq_ctxt->ctl, &eq_ctxt->offload_eq,
+                                   OFFLOAD_SEND_EQ_ENABLE_FLAG);
+    }
+    return 0;
+}
+
+int equalizer_start(effect_context_t *context, output_context_t *output)
+{
+    equalizer_context_t *eq_ctxt = (equalizer_context_t *)context;
+
+    ALOGV("%s: %p", __func__, output->ctl);
+    eq_ctxt->ctl = output->ctl;
+    if (offload_eq_get_enable_flag(&(eq_ctxt->offload_eq)))
+        if (eq_ctxt->ctl)
+            offload_eq_send_params(eq_ctxt->ctl, &eq_ctxt->offload_eq,
+                                   OFFLOAD_SEND_EQ_ENABLE_FLAG |
+                                   OFFLOAD_SEND_EQ_BANDS_LEVEL);
+    return 0;
+}
+
+int equalizer_stop(effect_context_t *context, output_context_t *output __unused)
+{
+    equalizer_context_t *eq_ctxt = (equalizer_context_t *)context;
+
+    ALOGV("%s", __func__);
+    eq_ctxt->ctl = NULL;
+    return 0;
+}
diff --git a/post_proc/equalizer.h b/post_proc/equalizer.h
new file mode 100644
index 0000000..00ad616
--- /dev/null
+++ b/post_proc/equalizer.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OFFLOAD_EQUALIZER_H_
+#define OFFLOAD_EQUALIZER_H_
+
+#include "bundle.h"
+
+#define NUM_EQ_BANDS              5
+#define INVALID_PRESET		 -2
+#define PRESET_CUSTOM		 -1
+
+extern const effect_descriptor_t equalizer_descriptor;
+
+typedef struct equalizer_context_s {
+    effect_context_t common;
+
+    int preset;
+    int band_levels[NUM_EQ_BANDS];
+
+    // Offload vars
+    struct mixer_ctl *ctl;
+    uint32_t device;
+    struct eq_params offload_eq;
+} equalizer_context_t;
+
+int equalizer_get_parameter(effect_context_t *context, effect_param_t *p,
+                            uint32_t *size);
+
+int equalizer_set_parameter(effect_context_t *context, effect_param_t *p,
+                            uint32_t size);
+
+int equalizer_set_device(effect_context_t *context,  uint32_t device);
+
+int equalizer_reset(effect_context_t *context);
+
+int equalizer_init(effect_context_t *context);
+
+int equalizer_enable(effect_context_t *context);
+
+int equalizer_disable(effect_context_t *context);
+
+int equalizer_start(effect_context_t *context, output_context_t *output);
+
+int equalizer_stop(effect_context_t *context, output_context_t *output);
+
+#endif /*OFFLOAD_EQUALIZER_H_*/
diff --git a/post_proc/reverb.c b/post_proc/reverb.c
new file mode 100644
index 0000000..e5fc950
--- /dev/null
+++ b/post_proc/reverb.c
@@ -0,0 +1,632 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "offload_effect_reverb"
+//#define LOG_NDEBUG 0
+
+#include <cutils/list.h>
+#include <cutils/log.h>
+#include <tinyalsa/asoundlib.h>
+#include <sound/audio_effects.h>
+#include <audio_effects/effect_environmentalreverb.h>
+#include <audio_effects/effect_presetreverb.h>
+
+#include "effect_api.h"
+#include "reverb.h"
+
+/* Offload auxiliary environmental reverb UUID: 79a18026-18fd-4185-8233-0002a5d5c51b */
+const effect_descriptor_t aux_env_reverb_descriptor = {
+        { 0xc2e5d5f0, 0x94bd, 0x4763, 0x9cac, { 0x4e, 0x23, 0x4d, 0x06, 0x83, 0x9e } },
+        { 0x79a18026, 0x18fd, 0x4185, 0x8233, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } },
+        EFFECT_CONTROL_API_VERSION,
+        (EFFECT_FLAG_TYPE_AUXILIARY | EFFECT_FLAG_HW_ACC_TUNNEL),
+        0, /* TODO */
+        1,
+        "MSM offload Auxiliary Environmental Reverb",
+        "The Android Open Source Project",
+};
+
+/* Offload insert environmental reverb UUID: eb64ea04-973b-43d2-8f5e-0002a5d5c51b */
+const effect_descriptor_t ins_env_reverb_descriptor = {
+        {0xc2e5d5f0, 0x94bd, 0x4763, 0x9cac, {0x4e, 0x23, 0x4d, 0x06, 0x83, 0x9e}},
+        {0xeb64ea04, 0x973b, 0x43d2, 0x8f5e, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+        EFFECT_CONTROL_API_VERSION,
+        (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST | EFFECT_FLAG_HW_ACC_TUNNEL),
+        0, /* TODO */
+        1,
+        "MSM offload Insert Environmental Reverb",
+        "The Android Open Source Project",
+};
+
+// Offload auxiliary preset reverb UUID: 6987be09-b142-4b41-9056-0002a5d5c51b */
+const effect_descriptor_t aux_preset_reverb_descriptor = {
+        {0x47382d60, 0xddd8, 0x11db, 0xbf3a, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+        {0x6987be09, 0xb142, 0x4b41, 0x9056, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+        EFFECT_CONTROL_API_VERSION,
+        (EFFECT_FLAG_TYPE_AUXILIARY | EFFECT_FLAG_HW_ACC_TUNNEL),
+        0, /* TODO */
+        1,
+        "MSM offload Auxiliary Preset Reverb",
+        "The Android Open Source Project",
+};
+
+// Offload insert preset reverb UUID: aa2bebf6-47cf-4613-9bca-0002a5d5c51b */
+const effect_descriptor_t ins_preset_reverb_descriptor = {
+        {0x47382d60, 0xddd8, 0x11db, 0xbf3a, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+        {0xaa2bebf6, 0x47cf, 0x4613, 0x9bca, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+        EFFECT_CONTROL_API_VERSION,
+        (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST | EFFECT_FLAG_HW_ACC_TUNNEL),
+        0, /* TODO */
+        1,
+        "MSM offload Insert Preset Reverb",
+        "The Android Open Source Project",
+};
+
+static const reverb_settings_t reverb_presets[] = {
+        // REVERB_PRESET_NONE: values are unused
+        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+        // REVERB_PRESET_SMALLROOM
+        {-400, -600, 1100, 830, -400, 5, 500, 10, 1000, 1000},
+        // REVERB_PRESET_MEDIUMROOM
+        {-400, -600, 1300, 830, -1000, 20, -200, 20, 1000, 1000},
+        // REVERB_PRESET_LARGEROOM
+        {-400, -600, 1500, 830, -1600, 5, -1000, 40, 1000, 1000},
+        // REVERB_PRESET_MEDIUMHALL
+        {-400, -600, 1800, 700, -1300, 15, -800, 30, 1000, 1000},
+        // REVERB_PRESET_LARGEHALL
+        {-400, -600, 1800, 700, -2000, 30, -1400, 60, 1000, 1000},
+        // REVERB_PRESET_PLATE
+        {-400, -200, 1300, 900, 0, 2, 0, 10, 1000, 750},
+};
+
+
+void reverb_auxiliary_init(reverb_context_t *context)
+{
+    context->auxiliary = true;
+    context->preset = false;
+}
+
+void reverb_insert_init(reverb_context_t *context)
+{
+    context->auxiliary = false;
+    context->preset = true;
+    context->cur_preset = REVERB_PRESET_LAST + 1;
+    context->next_preset = REVERB_DEFAULT_PRESET;
+}
+
+void reverb_preset_init(reverb_context_t *context)
+{
+    context->auxiliary = false;
+    context->preset = true;
+    context->cur_preset = REVERB_PRESET_LAST + 1;
+    context->next_preset = REVERB_DEFAULT_PRESET;
+}
+
+/*
+ * Reverb operations
+ */
+int16_t reverb_get_room_level(reverb_context_t *context)
+{
+    ALOGV("%s: room level: %d", __func__, context->reverb_settings.roomLevel);
+    return context->reverb_settings.roomLevel;
+}
+
+void reverb_set_room_level(reverb_context_t *context, int16_t room_level)
+{
+    ALOGV("%s: room level: %d", __func__, room_level);
+    context->reverb_settings.roomLevel = room_level;
+    offload_reverb_set_room_level(&(context->offload_reverb), room_level);
+    if (context->ctl)
+        offload_reverb_send_params(context->ctl, &context->offload_reverb,
+                                   OFFLOAD_SEND_REVERB_ENABLE_FLAG |
+                                   OFFLOAD_SEND_REVERB_ROOM_LEVEL);
+}
+
+int16_t reverb_get_room_hf_level(reverb_context_t *context)
+{
+    ALOGV("%s: room hf level: %d", __func__,
+          context->reverb_settings.roomHFLevel);
+    return context->reverb_settings.roomHFLevel;
+}
+
+void reverb_set_room_hf_level(reverb_context_t *context, int16_t room_hf_level)
+{
+    ALOGV("%s: room hf level: %d", __func__, room_hf_level);
+    context->reverb_settings.roomHFLevel = room_hf_level;
+    offload_reverb_set_room_hf_level(&(context->offload_reverb), room_hf_level);
+    if (context->ctl)
+        offload_reverb_send_params(context->ctl, &context->offload_reverb,
+                                   OFFLOAD_SEND_REVERB_ENABLE_FLAG |
+                                   OFFLOAD_SEND_REVERB_ROOM_HF_LEVEL);
+}
+
+uint32_t reverb_get_decay_time(reverb_context_t *context)
+{
+    ALOGV("%s: decay time: %d", __func__, context->reverb_settings.decayTime);
+    return context->reverb_settings.decayTime;
+}
+
+void reverb_set_decay_time(reverb_context_t *context, uint32_t decay_time)
+{
+    ALOGV("%s: decay_time: %d", __func__, decay_time);
+    context->reverb_settings.decayTime = decay_time;
+    offload_reverb_set_decay_time(&(context->offload_reverb), decay_time);
+    if (context->ctl)
+        offload_reverb_send_params(context->ctl, &context->offload_reverb,
+                                   OFFLOAD_SEND_REVERB_ENABLE_FLAG |
+                                   OFFLOAD_SEND_REVERB_DECAY_TIME);
+}
+
+int16_t reverb_get_decay_hf_ratio(reverb_context_t *context)
+{
+    ALOGV("%s: decay hf ratio: %d", __func__,
+          context->reverb_settings.decayHFRatio);
+    return context->reverb_settings.decayHFRatio;
+}
+
+void reverb_set_decay_hf_ratio(reverb_context_t *context, int16_t decay_hf_ratio)
+{
+    ALOGV("%s: decay_hf_ratio: %d", __func__, decay_hf_ratio);
+    context->reverb_settings.decayHFRatio = decay_hf_ratio;
+    offload_reverb_set_decay_hf_ratio(&(context->offload_reverb), decay_hf_ratio);
+    if (context->ctl)
+        offload_reverb_send_params(context->ctl, &context->offload_reverb,
+                                   OFFLOAD_SEND_REVERB_ENABLE_FLAG |
+                                   OFFLOAD_SEND_REVERB_DECAY_HF_RATIO);
+}
+
+int16_t reverb_get_reverb_level(reverb_context_t *context)
+{
+    ALOGV("%s: reverb level: %d", __func__, context->reverb_settings.reverbLevel);
+    return context->reverb_settings.reverbLevel;
+}
+
+void reverb_set_reverb_level(reverb_context_t *context, int16_t reverb_level)
+{
+    ALOGV("%s: reverb level: %d", __func__, reverb_level);
+    context->reverb_settings.reverbLevel = reverb_level;
+    offload_reverb_set_reverb_level(&(context->offload_reverb), reverb_level);
+    if (context->ctl)
+        offload_reverb_send_params(context->ctl, &context->offload_reverb,
+                                   OFFLOAD_SEND_REVERB_ENABLE_FLAG |
+                                   OFFLOAD_SEND_REVERB_LEVEL);
+}
+
+int16_t reverb_get_diffusion(reverb_context_t *context)
+{
+    ALOGV("%s: diffusion: %d", __func__, context->reverb_settings.diffusion);
+    return context->reverb_settings.diffusion;
+}
+
+void reverb_set_diffusion(reverb_context_t *context, int16_t diffusion)
+{
+    ALOGV("%s: diffusion: %d", __func__, diffusion);
+    context->reverb_settings.diffusion = diffusion;
+    offload_reverb_set_diffusion(&(context->offload_reverb), diffusion);
+    if (context->ctl)
+        offload_reverb_send_params(context->ctl, &context->offload_reverb,
+                                   OFFLOAD_SEND_REVERB_ENABLE_FLAG |
+                                   OFFLOAD_SEND_REVERB_DIFFUSION);
+}
+
+int16_t reverb_get_density(reverb_context_t *context)
+{
+    ALOGV("%s: density: %d", __func__, context->reverb_settings.density);
+    return context->reverb_settings.density;
+}
+
+void reverb_set_density(reverb_context_t *context, int16_t density)
+{
+    ALOGV("%s: density: %d", __func__, density);
+    context->reverb_settings.density = density;
+    offload_reverb_set_density(&(context->offload_reverb), density);
+    if (context->ctl)
+        offload_reverb_send_params(context->ctl, &context->offload_reverb,
+                                   OFFLOAD_SEND_REVERB_ENABLE_FLAG |
+                                   OFFLOAD_SEND_REVERB_DENSITY);
+}
+
+void reverb_set_preset(reverb_context_t *context, int16_t preset)
+{
+    bool enable;
+    ALOGV("%s: preset: %d", __func__, preset);
+    context->next_preset = preset;
+    offload_reverb_set_preset(&(context->offload_reverb), preset);
+
+    enable = (preset == REVERB_PRESET_NONE) ? false: true;
+    offload_reverb_set_enable_flag(&(context->offload_reverb), enable);
+
+    if (context->ctl)
+        offload_reverb_send_params(context->ctl, &context->offload_reverb,
+                                   OFFLOAD_SEND_REVERB_ENABLE_FLAG |
+                                   OFFLOAD_SEND_REVERB_PRESET);
+}
+
+void reverb_set_all_properties(reverb_context_t *context,
+                               reverb_settings_t *reverb_settings)
+{
+    ALOGV("%s", __func__);
+    context->reverb_settings.roomLevel = reverb_settings->roomLevel;
+    context->reverb_settings.roomHFLevel = reverb_settings->roomHFLevel;
+    context->reverb_settings.decayTime = reverb_settings->decayTime;
+    context->reverb_settings.decayHFRatio = reverb_settings->decayHFRatio;
+    context->reverb_settings.reverbLevel = reverb_settings->reverbLevel;
+    context->reverb_settings.diffusion = reverb_settings->diffusion;
+    context->reverb_settings.density = reverb_settings->density;
+    if (context->ctl)
+        offload_reverb_send_params(context->ctl, &context->offload_reverb,
+                                   OFFLOAD_SEND_REVERB_ENABLE_FLAG |
+                                   OFFLOAD_SEND_REVERB_ROOM_LEVEL |
+                                   OFFLOAD_SEND_REVERB_ROOM_HF_LEVEL |
+                                   OFFLOAD_SEND_REVERB_DECAY_TIME |
+                                   OFFLOAD_SEND_REVERB_DECAY_HF_RATIO |
+                                   OFFLOAD_SEND_REVERB_LEVEL |
+                                   OFFLOAD_SEND_REVERB_DIFFUSION |
+                                   OFFLOAD_SEND_REVERB_DENSITY);
+}
+
+void reverb_load_preset(reverb_context_t *context)
+{
+    context->cur_preset = context->next_preset;
+
+    if (context->cur_preset != REVERB_PRESET_NONE) {
+        const reverb_settings_t *preset = &reverb_presets[context->cur_preset];
+        reverb_set_room_level(context, preset->roomLevel);
+        reverb_set_room_hf_level(context, preset->roomHFLevel);
+        reverb_set_decay_time(context, preset->decayTime);
+        reverb_set_decay_hf_ratio(context, preset->decayHFRatio);
+        reverb_set_reverb_level(context, preset->reverbLevel);
+        reverb_set_diffusion(context, preset->diffusion);
+        reverb_set_density(context, preset->density);
+    }
+}
+
+int reverb_get_parameter(effect_context_t *context, effect_param_t *p,
+                         uint32_t *size)
+{
+    reverb_context_t *reverb_ctxt = (reverb_context_t *)context;
+    int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t);
+    int32_t *param_tmp = (int32_t *)p->data;
+    int32_t param = *param_tmp++;
+    void *value = p->data + voffset;
+    reverb_settings_t *reverb_settings;
+    int i;
+
+    ALOGV("%s", __func__);
+
+    p->status = 0;
+
+    if (reverb_ctxt->preset) {
+        if (param != REVERB_PARAM_PRESET || p->vsize < sizeof(uint16_t))
+            return -EINVAL;
+        *(uint16_t *)value = reverb_ctxt->next_preset;
+        ALOGV("get REVERB_PARAM_PRESET, preset %d", reverb_ctxt->next_preset);
+        return 0;
+    }
+    switch (param) {
+    case REVERB_PARAM_ROOM_LEVEL:
+        if (p->vsize < sizeof(uint16_t))
+           p->status = -EINVAL;
+        p->vsize = sizeof(uint16_t);
+        break;
+    case REVERB_PARAM_ROOM_HF_LEVEL:
+        if (p->vsize < sizeof(uint16_t))
+           p->status = -EINVAL;
+        p->vsize = sizeof(uint16_t);
+        break;
+    case REVERB_PARAM_DECAY_TIME:
+        if (p->vsize < sizeof(uint32_t))
+           p->status = -EINVAL;
+        p->vsize = sizeof(uint32_t);
+        break;
+    case REVERB_PARAM_DECAY_HF_RATIO:
+        if (p->vsize < sizeof(uint16_t))
+           p->status = -EINVAL;
+        p->vsize = sizeof(uint16_t);
+        break;
+    case REVERB_PARAM_REFLECTIONS_LEVEL:
+        if (p->vsize < sizeof(uint16_t))
+           p->status = -EINVAL;
+        p->vsize = sizeof(uint16_t);
+        break;
+    case REVERB_PARAM_REFLECTIONS_DELAY:
+        if (p->vsize < sizeof(uint32_t))
+           p->status = -EINVAL;
+        p->vsize = sizeof(uint32_t);
+        break;
+    case REVERB_PARAM_REVERB_LEVEL:
+        if (p->vsize < sizeof(uint16_t))
+           p->status = -EINVAL;
+        p->vsize = sizeof(uint16_t);
+        break;
+    case REVERB_PARAM_REVERB_DELAY:
+        if (p->vsize < sizeof(uint32_t))
+           p->status = -EINVAL;
+        p->vsize = sizeof(uint32_t);
+        break;
+    case REVERB_PARAM_DIFFUSION:
+        if (p->vsize < sizeof(uint16_t))
+           p->status = -EINVAL;
+        p->vsize = sizeof(uint16_t);
+        break;
+    case REVERB_PARAM_DENSITY:
+        if (p->vsize < sizeof(uint16_t))
+           p->status = -EINVAL;
+        p->vsize = sizeof(uint16_t);
+        break;
+    case REVERB_PARAM_PROPERTIES:
+        if (p->vsize < sizeof(reverb_settings_t))
+           p->status = -EINVAL;
+        p->vsize = sizeof(reverb_settings_t);
+        break;
+    default:
+        p->status = -EINVAL;
+    }
+
+    *size = sizeof(effect_param_t) + voffset + p->vsize;
+
+    if (p->status != 0)
+        return 0;
+
+    switch (param) {
+    case REVERB_PARAM_PROPERTIES:
+	ALOGV("%s: REVERB_PARAM_PROPERTIES", __func__);
+        reverb_settings = (reverb_settings_t *)value;
+        reverb_settings->roomLevel = reverb_get_room_level(reverb_ctxt);
+        reverb_settings->roomHFLevel = reverb_get_room_hf_level(reverb_ctxt);
+        reverb_settings->decayTime = reverb_get_decay_time(reverb_ctxt);
+        reverb_settings->decayHFRatio = reverb_get_decay_hf_ratio(reverb_ctxt);
+        reverb_settings->reflectionsLevel = 0;
+        reverb_settings->reflectionsDelay = 0;
+        reverb_settings->reverbDelay = 0;
+        reverb_settings->reverbLevel = reverb_get_reverb_level(reverb_ctxt);
+        reverb_settings->diffusion = reverb_get_diffusion(reverb_ctxt);
+        reverb_settings->density = reverb_get_density(reverb_ctxt);
+        break;
+    case REVERB_PARAM_ROOM_LEVEL:
+	ALOGV("%s: REVERB_PARAM_ROOM_LEVEL", __func__);
+        *(int16_t *)value = reverb_get_room_level(reverb_ctxt);
+        break;
+    case REVERB_PARAM_ROOM_HF_LEVEL:
+	ALOGV("%s: REVERB_PARAM_ROOM_HF_LEVEL", __func__);
+        *(int16_t *)value = reverb_get_room_hf_level(reverb_ctxt);
+        break;
+    case REVERB_PARAM_DECAY_TIME:
+	ALOGV("%s: REVERB_PARAM_DECAY_TIME", __func__);
+        *(uint32_t *)value = reverb_get_decay_time(reverb_ctxt);
+        break;
+    case REVERB_PARAM_DECAY_HF_RATIO:
+	ALOGV("%s: REVERB_PARAM_DECAY_HF_RATIO", __func__);
+        *(int16_t *)value = reverb_get_decay_hf_ratio(reverb_ctxt);
+        break;
+    case REVERB_PARAM_REVERB_LEVEL:
+	ALOGV("%s: REVERB_PARAM_REVERB_LEVEL", __func__);
+        *(int16_t *)value = reverb_get_reverb_level(reverb_ctxt);
+        break;
+    case REVERB_PARAM_DIFFUSION:
+	ALOGV("%s: REVERB_PARAM_DIFFUSION", __func__);
+        *(int16_t *)value = reverb_get_diffusion(reverb_ctxt);
+        break;
+    case REVERB_PARAM_DENSITY:
+	ALOGV("%s: REVERB_PARAM_DENSITY", __func__);
+        *(int16_t *)value = reverb_get_density(reverb_ctxt);
+        break;
+    case REVERB_PARAM_REFLECTIONS_LEVEL:
+	ALOGV("%s: REVERB_PARAM_REFLECTIONS_LEVEL", __func__);
+        *(uint16_t *)value = 0;
+        break;
+    case REVERB_PARAM_REFLECTIONS_DELAY:
+	ALOGV("%s: REVERB_PARAM_REFLECTIONS_DELAY", __func__);
+        *(uint32_t *)value = 0;
+        break;
+    case REVERB_PARAM_REVERB_DELAY:
+	ALOGV("%s: REVERB_PARAM_REVERB_DELAY", __func__);
+        *(uint32_t *)value = 0;
+        break;
+    default:
+        p->status = -EINVAL;
+        break;
+    }
+
+    return 0;
+}
+
+int reverb_set_parameter(effect_context_t *context, effect_param_t *p,
+                         uint32_t size __unused)
+{
+    reverb_context_t *reverb_ctxt = (reverb_context_t *)context;
+    int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t);
+    void *value = p->data + voffset;
+    int32_t *param_tmp = (int32_t *)p->data;
+    int32_t param = *param_tmp++;
+    reverb_settings_t *reverb_settings;
+    int16_t level;
+    int16_t ratio;
+    uint32_t time;
+
+    ALOGV("%s", __func__);
+
+    p->status = 0;
+
+    if (reverb_ctxt->preset) {
+        if (param != REVERB_PARAM_PRESET)
+            return -EINVAL;
+        uint16_t preset = *(uint16_t *)value;
+        ALOGV("set REVERB_PARAM_PRESET, preset %d", preset);
+        if (preset > REVERB_PRESET_LAST) {
+            return -EINVAL;
+        }
+        reverb_set_preset(reverb_ctxt, preset);
+        return 0;
+    }
+    switch (param) {
+    case REVERB_PARAM_PROPERTIES:
+	ALOGV("%s: REVERB_PARAM_PROPERTIES", __func__);
+        reverb_settings = (reverb_settings_t *)value;
+        break;
+    case REVERB_PARAM_ROOM_LEVEL:
+	ALOGV("%s: REVERB_PARAM_ROOM_LEVEL", __func__);
+        level = *(int16_t *)value;
+        reverb_set_room_level(reverb_ctxt, level);
+        break;
+    case REVERB_PARAM_ROOM_HF_LEVEL:
+	ALOGV("%s: REVERB_PARAM_ROOM_HF_LEVEL", __func__);
+        level = *(int16_t *)value;
+        reverb_set_room_hf_level(reverb_ctxt, level);
+        break;
+    case REVERB_PARAM_DECAY_TIME:
+	ALOGV("%s: REVERB_PARAM_DECAY_TIME", __func__);
+        time = *(uint32_t *)value;
+        reverb_set_decay_time(reverb_ctxt, time);
+        break;
+    case REVERB_PARAM_DECAY_HF_RATIO:
+	ALOGV("%s: REVERB_PARAM_DECAY_HF_RATIO", __func__);
+        ratio = *(int16_t *)value;
+        reverb_set_decay_hf_ratio(reverb_ctxt, ratio);
+        break;
+    case REVERB_PARAM_REVERB_LEVEL:
+	ALOGV("%s: REVERB_PARAM_REVERB_LEVEL", __func__);
+        level = *(int16_t *)value;
+        reverb_set_reverb_level(reverb_ctxt, level);
+        break;
+    case REVERB_PARAM_DIFFUSION:
+	ALOGV("%s: REVERB_PARAM_DIFFUSION", __func__);
+        ratio = *(int16_t *)value;
+        reverb_set_diffusion(reverb_ctxt, ratio);
+        break;
+    case REVERB_PARAM_DENSITY:
+	ALOGV("%s: REVERB_PARAM_DENSITY", __func__);
+        ratio = *(int16_t *)value;
+        reverb_set_density(reverb_ctxt, ratio);
+        break;
+    case REVERB_PARAM_REFLECTIONS_LEVEL:
+    case REVERB_PARAM_REFLECTIONS_DELAY:
+    case REVERB_PARAM_REVERB_DELAY:
+        break;
+    default:
+        p->status = -EINVAL;
+        break;
+    }
+
+    return 0;
+}
+
+int reverb_set_device(effect_context_t *context, uint32_t device)
+{
+    reverb_context_t *reverb_ctxt = (reverb_context_t *)context;
+
+    ALOGV("%s: device: %d", __func__, device);
+    reverb_ctxt->device = device;
+    offload_reverb_set_device(&(reverb_ctxt->offload_reverb), device);
+    return 0;
+}
+
+int reverb_reset(effect_context_t *context)
+{
+    reverb_context_t *reverb_ctxt = (reverb_context_t *)context;
+
+    return 0;
+}
+
+int reverb_init(effect_context_t *context)
+{
+    reverb_context_t *reverb_ctxt = (reverb_context_t *)context;
+
+    context->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
+    /*
+       FIXME: channel mode is mono for auxiliary. is it needed for offload ?
+              If so, this set config needs to be updated accordingly
+    */
+    context->config.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
+    context->config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+    context->config.inputCfg.samplingRate = 44100;
+    context->config.inputCfg.bufferProvider.getBuffer = NULL;
+    context->config.inputCfg.bufferProvider.releaseBuffer = NULL;
+    context->config.inputCfg.bufferProvider.cookie = NULL;
+    context->config.inputCfg.mask = EFFECT_CONFIG_ALL;
+    context->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
+    context->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
+    context->config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+    context->config.outputCfg.samplingRate = 44100;
+    context->config.outputCfg.bufferProvider.getBuffer = NULL;
+    context->config.outputCfg.bufferProvider.releaseBuffer = NULL;
+    context->config.outputCfg.bufferProvider.cookie = NULL;
+    context->config.outputCfg.mask = EFFECT_CONFIG_ALL;
+
+    set_config(context, &context->config);
+
+    memset(&(reverb_ctxt->reverb_settings), 0, sizeof(reverb_settings_t));
+    memset(&(reverb_ctxt->offload_reverb), 0, sizeof(struct reverb_params));
+
+    if (reverb_ctxt->preset &&
+        reverb_ctxt->next_preset != reverb_ctxt->cur_preset)
+        reverb_load_preset(reverb_ctxt);
+
+    return 0;
+}
+
+int reverb_enable(effect_context_t *context)
+{
+    reverb_context_t *reverb_ctxt = (reverb_context_t *)context;
+
+    ALOGV("%s", __func__);
+
+    if (!offload_reverb_get_enable_flag(&(reverb_ctxt->offload_reverb)))
+        offload_reverb_set_enable_flag(&(reverb_ctxt->offload_reverb), true);
+    return 0;
+}
+
+int reverb_disable(effect_context_t *context)
+{
+    reverb_context_t *reverb_ctxt = (reverb_context_t *)context;
+
+    ALOGV("%s", __func__);
+    if (offload_reverb_get_enable_flag(&(reverb_ctxt->offload_reverb))) {
+        offload_reverb_set_enable_flag(&(reverb_ctxt->offload_reverb), false);
+        if (reverb_ctxt->ctl)
+            offload_reverb_send_params(reverb_ctxt->ctl,
+                                       &reverb_ctxt->offload_reverb,
+                                       OFFLOAD_SEND_REVERB_ENABLE_FLAG);
+    }
+    return 0;
+}
+
+int reverb_start(effect_context_t *context, output_context_t *output)
+{
+    reverb_context_t *reverb_ctxt = (reverb_context_t *)context;
+
+    ALOGV("%s", __func__);
+    reverb_ctxt->ctl = output->ctl;
+    if (offload_reverb_get_enable_flag(&(reverb_ctxt->offload_reverb))) {
+        if (reverb_ctxt->ctl && reverb_ctxt->preset) {
+            offload_reverb_send_params(reverb_ctxt->ctl, &reverb_ctxt->offload_reverb,
+                                       OFFLOAD_SEND_REVERB_ENABLE_FLAG |
+                                       OFFLOAD_SEND_REVERB_PRESET);
+        }
+    }
+
+    return 0;
+}
+
+int reverb_stop(effect_context_t *context, output_context_t *output __unused)
+{
+    reverb_context_t *reverb_ctxt = (reverb_context_t *)context;
+
+    ALOGV("%s", __func__);
+    reverb_ctxt->ctl = NULL;
+    return 0;
+}
+
diff --git a/post_proc/reverb.h b/post_proc/reverb.h
new file mode 100644
index 0000000..aba2b27
--- /dev/null
+++ b/post_proc/reverb.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OFFLOAD_REVERB_H_
+#define OFFLOAD_REVERB_H_
+
+#include "bundle.h"
+
+#define REVERB_DEFAULT_PRESET REVERB_PRESET_NONE
+
+extern const effect_descriptor_t aux_env_reverb_descriptor;
+extern const effect_descriptor_t ins_env_reverb_descriptor;
+extern const effect_descriptor_t aux_preset_reverb_descriptor;
+extern const effect_descriptor_t ins_preset_reverb_descriptor;
+
+typedef struct reverb_settings_s {
+    int16_t     roomLevel;
+    int16_t     roomHFLevel;
+    uint32_t    decayTime;
+    int16_t     decayHFRatio;
+    int16_t     reflectionsLevel;
+    uint32_t    reflectionsDelay;
+    int16_t     reverbLevel;
+    uint32_t    reverbDelay;
+    int16_t     diffusion;
+    int16_t     density;
+} reverb_settings_t;
+
+typedef struct reverb_context_s {
+    effect_context_t common;
+
+    // Offload vars
+    struct mixer_ctl *ctl;
+    bool auxiliary;
+    bool preset;
+    uint16_t cur_preset;
+    uint16_t next_preset;
+    reverb_settings_t reverb_settings;
+    uint32_t device;
+    struct reverb_params offload_reverb;
+} reverb_context_t;
+
+
+void reverb_auxiliary_init(reverb_context_t *context);
+
+void reverb_preset_init(reverb_context_t *context);
+
+void reverb_insert_init(reverb_context_t *context);
+
+int reverb_get_parameter(effect_context_t *context, effect_param_t *p,
+                            uint32_t *size);
+
+int reverb_set_parameter(effect_context_t *context, effect_param_t *p,
+                            uint32_t size);
+
+int reverb_set_device(effect_context_t *context,  uint32_t device);
+
+int reverb_reset(effect_context_t *context);
+
+int reverb_init(effect_context_t *context);
+
+int reverb_enable(effect_context_t *context);
+
+int reverb_disable(effect_context_t *context);
+
+int reverb_start(effect_context_t *context, output_context_t *output);
+
+int reverb_stop(effect_context_t *context, output_context_t *output);
+
+#endif /* OFFLOAD_REVERB_H_ */
diff --git a/post_proc/virtualizer.c b/post_proc/virtualizer.c
new file mode 100644
index 0000000..40bbf38
--- /dev/null
+++ b/post_proc/virtualizer.c
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "offload_effect_virtualizer"
+//#define LOG_NDEBUG 0
+
+#include <cutils/list.h>
+#include <cutils/log.h>
+#include <tinyalsa/asoundlib.h>
+#include <sound/audio_effects.h>
+#include <audio_effects/effect_virtualizer.h>
+
+#include "effect_api.h"
+#include "virtualizer.h"
+
+/* Offload Virtualizer UUID: 509a4498-561a-4bea-b3b1-0002a5d5c51b */
+const effect_descriptor_t virtualizer_descriptor = {
+        {0x37cc2c00, 0xdddd, 0x11db, 0x8577, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
+        {0x509a4498, 0x561a, 0x4bea, 0xb3b1, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // uuid
+        EFFECT_CONTROL_API_VERSION,
+        (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_DEVICE_IND | EFFECT_FLAG_HW_ACC_TUNNEL),
+        0, /* TODO */
+        1,
+        "MSM offload virtualizer",
+        "The Android Open Source Project",
+};
+
+/*
+ * Virtualizer operations
+ */
+
+int virtualizer_get_strength(virtualizer_context_t *context)
+{
+    ALOGV("%s: strength: %d", __func__, context->strength);
+    return context->strength;
+}
+
+int virtualizer_set_strength(virtualizer_context_t *context, uint32_t strength)
+{
+    ALOGV("%s: strength: %d", __func__, strength);
+    context->strength = strength;
+
+    offload_virtualizer_set_strength(&(context->offload_virt), strength);
+    if (context->ctl)
+        offload_virtualizer_send_params(context->ctl, &context->offload_virt,
+                                        OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG |
+                                        OFFLOAD_SEND_VIRTUALIZER_STRENGTH);
+    return 0;
+}
+
+int virtualizer_get_parameter(effect_context_t *context, effect_param_t *p,
+                              uint32_t *size)
+{
+    virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context;
+    int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t);
+    int32_t *param_tmp = (int32_t *)p->data;
+    int32_t param = *param_tmp++;
+    void *value = p->data + voffset;
+    int i;
+
+    ALOGV("%s", __func__);
+
+    p->status = 0;
+
+    switch (param) {
+    case VIRTUALIZER_PARAM_STRENGTH_SUPPORTED:
+        if (p->vsize < sizeof(uint32_t))
+           p->status = -EINVAL;
+        p->vsize = sizeof(uint32_t);
+        break;
+    case VIRTUALIZER_PARAM_STRENGTH:
+        if (p->vsize < sizeof(int16_t))
+           p->status = -EINVAL;
+        p->vsize = sizeof(int16_t);
+        break;
+    default:
+        p->status = -EINVAL;
+    }
+
+    *size = sizeof(effect_param_t) + voffset + p->vsize;
+
+    if (p->status != 0)
+        return 0;
+
+    switch (param) {
+    case VIRTUALIZER_PARAM_STRENGTH_SUPPORTED:
+	ALOGV("%s: VIRTUALIZER_PARAM_STRENGTH_SUPPORTED", __func__);
+        *(uint32_t *)value = 1;
+        break;
+
+    case VIRTUALIZER_PARAM_STRENGTH:
+	ALOGV("%s: VIRTUALIZER_PARAM_STRENGTH", __func__);
+        *(int16_t *)value = virtualizer_get_strength(virt_ctxt);
+        break;
+
+    default:
+        p->status = -EINVAL;
+        break;
+    }
+
+    return 0;
+}
+
+int virtualizer_set_parameter(effect_context_t *context, effect_param_t *p,
+                              uint32_t size __unused)
+{
+    virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context;
+    int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t);
+    void *value = p->data + voffset;
+    int32_t *param_tmp = (int32_t *)p->data;
+    int32_t param = *param_tmp++;
+    uint32_t strength;
+
+    ALOGV("%s", __func__);
+
+    p->status = 0;
+
+    switch (param) {
+    case VIRTUALIZER_PARAM_STRENGTH:
+	ALOGV("%s VIRTUALIZER_PARAM_STRENGTH", __func__);
+        strength = (uint32_t)(*(int16_t *)value);
+        virtualizer_set_strength(virt_ctxt, strength);
+        break;
+    default:
+        p->status = -EINVAL;
+        break;
+    }
+
+    return 0;
+}
+
+int virtualizer_set_device(effect_context_t *context, uint32_t device)
+{
+    virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context;
+
+    ALOGV("%s: device: %d", __func__, device);
+    virt_ctxt->device = device;
+    if ((device == AUDIO_DEVICE_OUT_SPEAKER) ||
+       (device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT) ||
+       (device == AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER) ||
+       (device == AUDIO_DEVICE_OUT_AUX_DIGITAL) ||
+       (device == AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET)) {
+        if (!virt_ctxt->temp_disabled) {
+            if (effect_is_active(&virt_ctxt->common)) {
+                offload_virtualizer_set_enable_flag(&(virt_ctxt->offload_virt), false);
+                if (virt_ctxt->ctl)
+                    offload_virtualizer_send_params(virt_ctxt->ctl,
+                                                    &virt_ctxt->offload_virt,
+                                                    OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG);
+            }
+            virt_ctxt->temp_disabled = true;
+        }
+    } else {
+        if (virt_ctxt->temp_disabled) {
+            if (effect_is_active(&virt_ctxt->common)) {
+                offload_virtualizer_set_enable_flag(&(virt_ctxt->offload_virt), true);
+                if (virt_ctxt->ctl)
+                    offload_virtualizer_send_params(virt_ctxt->ctl,
+                                                    &virt_ctxt->offload_virt,
+                                                    OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG);
+            }
+            virt_ctxt->temp_disabled = false;
+        }
+    }
+    offload_virtualizer_set_device(&(virt_ctxt->offload_virt), device);
+    return 0;
+}
+
+int virtualizer_reset(effect_context_t *context)
+{
+    virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context;
+
+    return 0;
+}
+
+int virtualizer_init(effect_context_t *context)
+{
+    ALOGV("%s", __func__);
+    virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context;
+
+    context->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
+    context->config.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
+    context->config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+    context->config.inputCfg.samplingRate = 44100;
+    context->config.inputCfg.bufferProvider.getBuffer = NULL;
+    context->config.inputCfg.bufferProvider.releaseBuffer = NULL;
+    context->config.inputCfg.bufferProvider.cookie = NULL;
+    context->config.inputCfg.mask = EFFECT_CONFIG_ALL;
+    context->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
+    context->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
+    context->config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+    context->config.outputCfg.samplingRate = 44100;
+    context->config.outputCfg.bufferProvider.getBuffer = NULL;
+    context->config.outputCfg.bufferProvider.releaseBuffer = NULL;
+    context->config.outputCfg.bufferProvider.cookie = NULL;
+    context->config.outputCfg.mask = EFFECT_CONFIG_ALL;
+
+    set_config(context, &context->config);
+
+    virt_ctxt->temp_disabled = false;
+    memset(&(virt_ctxt->offload_virt), 0, sizeof(struct virtualizer_params));
+
+    return 0;
+}
+
+int virtualizer_enable(effect_context_t *context)
+{
+    virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context;
+
+    ALOGV("%s", __func__);
+
+    if (!offload_virtualizer_get_enable_flag(&(virt_ctxt->offload_virt)) &&
+        !(virt_ctxt->temp_disabled)) {
+        offload_virtualizer_set_enable_flag(&(virt_ctxt->offload_virt), true);
+        if (virt_ctxt->ctl && virt_ctxt->strength)
+            offload_virtualizer_send_params(virt_ctxt->ctl,
+                                          &virt_ctxt->offload_virt,
+                                          OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG |
+                                          OFFLOAD_SEND_BASSBOOST_STRENGTH);
+    }
+    return 0;
+}
+
+int virtualizer_disable(effect_context_t *context)
+{
+    virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context;
+
+    ALOGV("%s", __func__);
+    if (offload_virtualizer_get_enable_flag(&(virt_ctxt->offload_virt))) {
+        offload_virtualizer_set_enable_flag(&(virt_ctxt->offload_virt), false);
+        if (virt_ctxt->ctl)
+            offload_virtualizer_send_params(virt_ctxt->ctl,
+                                          &virt_ctxt->offload_virt,
+                                          OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG);
+    }
+    return 0;
+}
+
+int virtualizer_start(effect_context_t *context, output_context_t *output)
+{
+    virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context;
+
+    ALOGV("%s", __func__);
+    virt_ctxt->ctl = output->ctl;
+    if (offload_virtualizer_get_enable_flag(&(virt_ctxt->offload_virt)))
+        if (virt_ctxt->ctl)
+            offload_virtualizer_send_params(virt_ctxt->ctl, &virt_ctxt->offload_virt,
+                                          OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG |
+                                          OFFLOAD_SEND_VIRTUALIZER_STRENGTH);
+    return 0;
+}
+
+int virtualizer_stop(effect_context_t *context, output_context_t *output __unused)
+{
+    virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context;
+
+    ALOGV("%s", __func__);
+    virt_ctxt->ctl = NULL;
+    return 0;
+}
diff --git a/post_proc/virtualizer.h b/post_proc/virtualizer.h
new file mode 100644
index 0000000..978dc86
--- /dev/null
+++ b/post_proc/virtualizer.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OFFLOAD_VIRTUALIZER_H_
+#define OFFLOAD_VIRTUALIZER_H_
+
+#include "bundle.h"
+
+extern const effect_descriptor_t virtualizer_descriptor;
+
+typedef struct virtualizer_context_s {
+    effect_context_t common;
+
+    int strength;
+
+    // Offload vars
+    struct mixer_ctl *ctl;
+    bool temp_disabled;
+    uint32_t device;
+    struct virtualizer_params offload_virt;
+} virtualizer_context_t;
+
+int virtualizer_get_parameter(effect_context_t *context, effect_param_t *p,
+                            uint32_t *size);
+
+int virtualizer_set_parameter(effect_context_t *context, effect_param_t *p,
+                            uint32_t size);
+
+int virtualizer_set_device(effect_context_t *context,  uint32_t device);
+
+int virtualizer_reset(effect_context_t *context);
+
+int virtualizer_init(effect_context_t *context);
+
+int virtualizer_enable(effect_context_t *context);
+
+int virtualizer_disable(effect_context_t *context);
+
+int virtualizer_start(effect_context_t *context, output_context_t *output);
+
+int virtualizer_stop(effect_context_t *context, output_context_t *output);
+
+#endif /* OFFLOAD_VIRTUALIZER_H_ */
diff --git a/visualizer/offload_visualizer.c b/visualizer/offload_visualizer.c
index 2f2bb14..9b11e20 100644
--- a/visualizer/offload_visualizer.c
+++ b/visualizer/offload_visualizer.c
@@ -37,6 +37,7 @@
 };
 
 typedef struct effect_context_s effect_context_t;
+typedef struct output_context_s output_context_t;
 
 /* effect specific operations. Only the init() and process() operations must be defined.
  * Others are optional.
@@ -47,6 +48,8 @@
     int (*reset)(effect_context_t *context);
     int (*enable)(effect_context_t *context);
     int (*disable)(effect_context_t *context);
+    int (*start)(effect_context_t *context, output_context_t *output);
+    int (*stop)(effect_context_t *context, output_context_t *output);
     int (*process)(effect_context_t *context, audio_buffer_t *in, audio_buffer_t *out);
     int (*set_parameter)(effect_context_t *context, effect_param_t *param, uint32_t size);
     int (*get_parameter)(effect_context_t *context, effect_param_t *param, uint32_t *size);
@@ -247,6 +250,8 @@
             return;
     }
     list_add_tail(&output->effects_list, &context->output_node);
+    if (context->ops.start)
+        context->ops.start(context, output);
 }
 
 void remove_effect_from_output(output_context_t * output, effect_context_t *context) {
@@ -257,6 +262,8 @@
                                                      effect_context_t,
                                                      output_node);
         if (fx_ctxt == context) {
+            if (context->ops.stop)
+                context->ops.stop(context, output);
             list_remove(&context->output_node);
             return;
         }
@@ -276,7 +283,7 @@
             effect_context_t *fx_ctxt = node_to_item(fx_node,
                                                          effect_context_t,
                                                          output_node);
-            if (fx_ctxt->state == EFFECT_STATE_ACTIVE)
+            if (fx_ctxt->state == EFFECT_STATE_ACTIVE && fx_ctxt->ops.process != NULL)
                 return true;
         }
     }
@@ -299,7 +306,7 @@
 }
 
 
-void *capture_thread_loop(void *arg)
+void *capture_thread_loop(void *arg __unused)
 {
     int16_t data[AUDIO_CAPTURE_PERIOD_SIZE * AUDIO_CAPTURE_CHANNEL_COUNT * sizeof(int16_t)];
     audio_buffer_t buf;
@@ -379,7 +386,8 @@
                     effect_context_t *fx_ctxt = node_to_item(fx_node,
                                                                 effect_context_t,
                                                                 output_node);
-                    fx_ctxt->ops.process(fx_ctxt, &buf, &buf);
+                    if (fx_ctxt->ops.process != NULL)
+                        fx_ctxt->ops.process(fx_ctxt, &buf, &buf);
                 }
             }
         } else {
@@ -405,11 +413,11 @@
  */
 
 __attribute__ ((visibility ("default")))
-int visualizer_hal_start_output(audio_io_handle_t output) {
+int visualizer_hal_start_output(audio_io_handle_t output, int pcm_id) {
     int ret;
     struct listnode *node;
 
-    ALOGV("%s", __func__);
+    ALOGV("%s output %d pcm_id %d", __func__, output, pcm_id);
 
     if (lib_init() != 0)
         return init_status;
@@ -431,6 +439,8 @@
                                                      effect_context_t,
                                                      effects_list_node);
         if (fx_ctxt->out_handle == output) {
+            if (fx_ctxt->ops.start)
+                fx_ctxt->ops.start(fx_ctxt, out_ctxt);
             list_add_tail(&out_ctxt->effects_list, &fx_ctxt->output_node);
         }
     }
@@ -449,12 +459,13 @@
 }
 
 __attribute__ ((visibility ("default")))
-int visualizer_hal_stop_output(audio_io_handle_t output) {
+int visualizer_hal_stop_output(audio_io_handle_t output, int pcm_id) {
     int ret;
     struct listnode *node;
+    struct listnode *fx_node;
     output_context_t *out_ctxt;
 
-    ALOGV("%s", __func__);
+    ALOGV("%s output %d pcm_id %d", __func__, output, pcm_id);
 
     if (lib_init() != 0)
         return init_status;
@@ -468,7 +479,13 @@
         ret = -ENOSYS;
         goto exit;
     }
-
+    list_for_each(fx_node, &out_ctxt->effects_list) {
+        effect_context_t *fx_ctxt = node_to_item(fx_node,
+                                                 effect_context_t,
+                                                 output_node);
+        if (fx_ctxt->ops.stop)
+            fx_ctxt->ops.stop(fx_ctxt, out_ctxt);
+    }
     list_remove(&out_ctxt->outputs_list_node);
     pthread_cond_signal(&cond);
 
@@ -580,7 +597,7 @@
     visu_ctxt->scaling_mode = VISUALIZER_SCALING_MODE_NORMALIZED;
 
     // measurement initialization
-    visu_ctxt->channel_count = popcount(context->config.inputCfg.channels);
+    visu_ctxt->channel_count = audio_channel_count_from_out_mask(context->config.inputCfg.channels);
     visu_ctxt->meas_mode = MEASUREMENT_MODE_NONE;
     visu_ctxt->meas_wndw_size_in_buffers = MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS;
     visu_ctxt->meas_buffer_idx = 0;
@@ -630,7 +647,7 @@
     return 0;
 }
 
-int visualizer_set_parameter(effect_context_t *context, effect_param_t *p, uint32_t size)
+int visualizer_set_parameter(effect_context_t *context, effect_param_t *p, uint32_t size __unused)
 {
     visualizer_context_t *visu_ctxt = (visualizer_context_t *)context;
 
@@ -763,8 +780,8 @@
     return 0;
 }
 
-int visualizer_command(effect_context_t * context, uint32_t cmdCode, uint32_t cmdSize,
-        void *pCmdData, uint32_t *replySize, void *pReplyData)
+int visualizer_command(effect_context_t * context, uint32_t cmdCode, uint32_t cmdSize __unused,
+        void *pCmdData __unused, uint32_t *replySize, void *pReplyData)
 {
     visualizer_context_t * visu_ctxt = (visualizer_context_t *)context;
 
@@ -886,7 +903,7 @@
  */
 
 int effect_lib_create(const effect_uuid_t *uuid,
-                         int32_t sessionId,
+                         int32_t sessionId __unused,
                          int32_t ioId,
                          effect_handle_t *pHandle) {
     int ret;
@@ -917,6 +934,7 @@
         context->ops.set_parameter = visualizer_set_parameter;
         context->ops.get_parameter = visualizer_get_parameter;
         context->ops.command = visualizer_command;
+        context->desc = &visualizer_descriptor;
     } else {
         return -EINVAL;
     }
@@ -924,7 +942,6 @@
     context->itfe = &effect_interface;
     context->state = EFFECT_STATE_UNINITIALIZED;
     context->out_handle = (audio_io_handle_t)ioId;
-    context->desc = &visualizer_descriptor;
 
     ret = context->ops.init(context);
     if (ret < 0) {
@@ -1003,8 +1020,8 @@
 
  /* Stub function for effect interface: never called for offloaded effects */
 int effect_process(effect_handle_t self,
-                       audio_buffer_t *inBuffer,
-                       audio_buffer_t *outBuffer)
+                       audio_buffer_t *inBuffer __unused,
+                       audio_buffer_t *outBuffer __unused)
 {
     effect_context_t * context = (effect_context_t *)self;
     int status = 0;
@@ -1177,12 +1194,12 @@
         out_ctxt = get_output(context->out_handle);
         if (out_ctxt != NULL)
             remove_effect_from_output(out_ctxt, context);
+
+        context->out_handle = offload_param->ioHandle;
         out_ctxt = get_output(offload_param->ioHandle);
         if (out_ctxt != NULL)
             add_effect_to_output(out_ctxt, context);
 
-        context->out_handle = offload_param->ioHandle;
-
         } break;
 
 
diff --git a/voice_processing/voice_processing.c b/voice_processing/voice_processing.c
index e556333..08acdb4 100644
--- a/voice_processing/voice_processing.c
+++ b/voice_processing/voice_processing.c
@@ -559,9 +559,7 @@
             if (pCmdData == NULL ||
                     cmdSize < (int)sizeof(effect_param_t) ||
                     pReplyData == NULL ||
-                    *replySize < (int)sizeof(effect_param_t) ||
-                    // constrain memcpy below
-                    ((effect_param_t *)pCmdData)->psize > *replySize - sizeof(effect_param_t)) {
+                    *replySize < (int)sizeof(effect_param_t)) {
                 ALOGV("fx_command() EFFECT_CMD_GET_PARAM invalid args");
                 return -EINVAL;
             }