Fix potential NULL dereference in Visualizer effect am: 0c39abc483 am: 18b2de20a7 am: bcd048aea2 am: 67c5eb2059 am: 996c0f33a8
am: ae2849ff1d

Change-Id: Ic71b709d17623bef1c8580c9d9c194a029f1d368
diff --git a/Android.mk b/Android.mk
index 6dbcccd..1e26411 100644
--- a/Android.mk
+++ b/Android.mk
@@ -1,4 +1,4 @@
-ifneq ($(filter msm8960 msm8226 msm8x26 msm8974 msm8x74 msm8x84 msm8084,$(TARGET_BOARD_PLATFORM)),)
+ifneq ($(filter msm8960 msm8226 msm8x26 msm8974 msm8x74 msm8x84 msm8084 msm8992 msm8994,$(TARGET_BOARD_PLATFORM)),)
 
 MY_LOCAL_PATH := $(call my-dir)
 
diff --git a/hal/Android.mk b/hal/Android.mk
index 18b8bf4..0d931c1 100644
--- a/hal/Android.mk
+++ b/hal/Android.mk
@@ -7,7 +7,7 @@
 LOCAL_ARM_MODE := arm
 
 AUDIO_PLATFORM := $(TARGET_BOARD_PLATFORM)
-ifneq ($(filter msm8974 msm8226 msm8084,$(TARGET_BOARD_PLATFORM)),)
+ifneq ($(filter msm8974 msm8226 msm8084 msm8992 msm8994,$(TARGET_BOARD_PLATFORM)),)
   # B-family platform uses msm8974 code base
   AUDIO_PLATFORM = msm8974
 ifneq ($(filter msm8226,$(TARGET_BOARD_PLATFORM)),)
@@ -16,6 +16,12 @@
 ifneq ($(filter msm8084,$(TARGET_BOARD_PLATFORM)),)
   LOCAL_CFLAGS := -DPLATFORM_MSM8084
 endif
+ifneq ($(filter msm8992,$(TARGET_BOARD_PLATFORM)),)
+  LOCAL_CFLAGS := -DPLATFORM_MSM8994
+endif
+ifneq ($(filter msm8994,$(TARGET_BOARD_PLATFORM)),)
+  LOCAL_CFLAGS := -DPLATFORM_MSM8994
+endif
 endif
 
 LOCAL_SRC_FILES := \
@@ -54,9 +60,37 @@
     LOCAL_SRC_FILES += audio_extn/hfp.c
 endif
 
+ifeq ($(strip $(AUDIO_FEATURE_NO_AUDIO_OUT)),true)
+    LOCAL_CFLAGS += -DNO_AUDIO_OUT
+endif
+
+ifeq ($(strip $(BOARD_SUPPORTS_SOUND_TRIGGER)),true)
+    LOCAL_CFLAGS += -DSOUND_TRIGGER_ENABLED
+    LOCAL_CFLAGS += -DSOUND_TRIGGER_PLATFORM_NAME=$(TARGET_BOARD_PLATFORM)
+    LOCAL_C_INCLUDES += $(TARGET_OUT_HEADERS)/mm-audio/sound_trigger
+    LOCAL_SRC_FILES += audio_extn/soundtrigger.c
+endif
+
+ifeq ($(strip $(AUDIO_FEATURE_ENABLED_SPKR_PROTECTION)),true)
+    LOCAL_CFLAGS += -DSPKR_PROT_ENABLED
+    LOCAL_SRC_FILES += audio_extn/spkr_protection.c
+endif
+
+ifeq ($(strip $(AUDIO_FEATURE_ENABLED_DSM_FEEDBACK)),true)
+    LOCAL_CFLAGS += -DDSM_FEEDBACK_ENABLED
+    LOCAL_SRC_FILES += audio_extn/dsm_feedback.c
+endif
+
+ifneq ($(filter msm8992 msm8994,$(TARGET_BOARD_PLATFORM)),)
+  # push codec/mad calibration to HW dep node
+  # applicable to msm8992/8994 or newer platforms
+  LOCAL_CFLAGS += -DHWDEP_CAL_ENABLED
+  LOCAL_SRC_FILES += audio_extn/hwdep_cal.c
+endif
+
 LOCAL_MODULE := audio.primary.$(TARGET_BOARD_PLATFORM)
 
-LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+LOCAL_MODULE_RELATIVE_PATH := hw
 
 LOCAL_MODULE_TAGS := optional
 
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index 26c2fb4..b99378e 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -25,6 +25,24 @@
 void audio_extn_extspk_set_mode(void* extn, audio_mode_t mode);
 void audio_extn_extspk_set_voice_vol(void* extn, float vol);
 
+#ifndef SPKR_PROT_ENABLED
+#define audio_extn_spkr_prot_init(adev)       (0)
+#define audio_extn_spkr_prot_start_processing(snd_device)    (-EINVAL)
+#define audio_extn_spkr_prot_calib_cancel(adev) (0)
+#define audio_extn_spkr_prot_stop_processing(snd_device)     (0)
+#define audio_extn_spkr_prot_is_enabled() (false)
+#define audio_extn_spkr_prot_get_acdb_id(snd_device)         (-EINVAL)
+#define audio_extn_get_spkr_prot_snd_device(snd_device) (snd_device)
+#else
+void audio_extn_spkr_prot_init(void *adev);
+int audio_extn_spkr_prot_start_processing(snd_device_t snd_device);
+void audio_extn_spkr_prot_stop_processing(snd_device_t snd_device);
+bool audio_extn_spkr_prot_is_enabled();
+int audio_extn_spkr_prot_get_acdb_id(snd_device_t snd_device);
+int audio_extn_get_spkr_prot_snd_device(snd_device_t snd_device);
+void audio_extn_spkr_prot_calib_cancel(void *adev);
+#endif
+
 #ifndef HFP_ENABLED
 #define audio_extn_hfp_is_active(adev)                  (0)
 #define audio_extn_hfp_get_usecase()                    (-1)
@@ -38,4 +56,49 @@
                                     struct str_parms *parms);
 #endif
 
+#ifndef SOUND_TRIGGER_ENABLED
+#define audio_extn_sound_trigger_init(adev)                            (0)
+#define audio_extn_sound_trigger_deinit(adev)                          (0)
+#define audio_extn_sound_trigger_update_device_status(snd_dev, event)  (0)
+#define audio_extn_sound_trigger_set_parameters(adev, parms)           (0)
+#define audio_extn_sound_trigger_check_and_get_session(in)             (0)
+#define audio_extn_sound_trigger_stop_lab(in)                          (0)
+#define audio_extn_sound_trigger_read(in, buffer, bytes)               (0)
+
+#else
+
+enum st_event_type {
+    ST_EVENT_SND_DEVICE_FREE,
+    ST_EVENT_SND_DEVICE_BUSY,
+    ST_EVENT_STREAM_FREE,
+    ST_EVENT_STREAM_BUSY
+};
+typedef enum st_event_type st_event_type_t;
+
+int audio_extn_sound_trigger_init(struct audio_device *adev);
+void audio_extn_sound_trigger_deinit(struct audio_device *adev);
+void audio_extn_sound_trigger_update_device_status(snd_device_t snd_device,
+                                     st_event_type_t event);
+void audio_extn_sound_trigger_set_parameters(struct audio_device *adev,
+                                             struct str_parms *parms);
+void audio_extn_sound_trigger_check_and_get_session(struct stream_in *in);
+void audio_extn_sound_trigger_stop_lab(struct stream_in *in);
+int audio_extn_sound_trigger_read(struct stream_in *in, void *buffer,
+                                  size_t bytes);
+#endif
+
+#ifndef DSM_FEEDBACK_ENABLED
+#define audio_extn_dsm_feedback_enable(adev, snd_device, benable)                (0)
+#else
+void audio_extn_dsm_feedback_enable(struct audio_device *adev,
+                         snd_device_t snd_device,
+                         bool benable);
+#endif
+
+#ifndef HWDEP_CAL_ENABLED
+#define  audio_extn_hwdep_cal_send(snd_card, acdb_handle) (0)
+#else
+void audio_extn_hwdep_cal_send(int snd_card, void *acdb_handle);
+#endif
+
 #endif /* AUDIO_EXTN_H */
diff --git a/hal/audio_extn/dsm_feedback.c b/hal/audio_extn/dsm_feedback.c
new file mode 100755
index 0000000..e340dae
--- /dev/null
+++ b/hal/audio_extn/dsm_feedback.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2015 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_dsm_feedback"
+/*#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>
+
+
+static struct pcm_config pcm_config_dsm = {
+    .channels = 2,
+    .rate = 48000,
+    .period_size = 256,
+    .period_count = 4,
+    .format = PCM_FORMAT_S16_LE,
+    .start_threshold = 0,
+    .stop_threshold = INT_MAX,
+    .avail_min = 0,
+};
+
+int start_dsm_feedback_processing(struct audio_device *adev, int enable)
+{
+    int ret = 0;
+    int32_t pcm_dev_tx_id = -1;
+    static struct pcm *dsm_pcm_handle = NULL;
+
+    if (enable) {
+        /*do nothing if already enabled*/
+        if (dsm_pcm_handle)
+            return ret;
+
+        pcm_dev_tx_id = platform_get_pcm_device_id(USECASE_AUDIO_DSM_FEEDBACK, PCM_CAPTURE);
+        if (pcm_dev_tx_id < 0) {
+            ALOGE("%s: Invalid pcm device for usecase (%d)",
+                  __func__, USECASE_AUDIO_DSM_FEEDBACK);
+            ret = -ENODEV;

+            goto close;
+        }
+
+        dsm_pcm_handle = pcm_open(adev->snd_card,
+                                 pcm_dev_tx_id,
+                                 PCM_IN, &pcm_config_dsm);
+        if (dsm_pcm_handle && !pcm_is_ready(dsm_pcm_handle)) {
+            ALOGE("%s: %s", __func__, pcm_get_error(dsm_pcm_handle));
+            ret = -EIO;
+            goto close;
+        }
+
+        if (pcm_start(dsm_pcm_handle) < 0) {
+            ALOGE("%s: pcm start for RX failed", __func__);
+            ret = -EINVAL;
+            goto close;
+        }
+
+        return ret;
+    }
+
+close:
+    /*close pcm if disable or error happend in opening*/
+    if (dsm_pcm_handle) {
+        pcm_close(dsm_pcm_handle);
+        dsm_pcm_handle = NULL;
+    }
+
+    return ret;
+}
+
+void audio_extn_dsm_feedback_enable(struct audio_device *adev,
+                         snd_device_t snd_device,
+                         int benable)
+{
+    if ( NULL == adev )
+        return;
+
+    if( snd_device == SND_DEVICE_OUT_SPEAKER ||
+        snd_device == SND_DEVICE_OUT_SPEAKER_REVERSE ||
+        snd_device == SND_DEVICE_OUT_VOICE_SPEAKER ||
+        snd_device == SND_DEVICE_OUT_SPEAKER_SAFE ||
+        snd_device == SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES ||
+        snd_device == SND_DEVICE_OUT_SPEAKER_AND_LINE ||
+        snd_device == SND_DEVICE_OUT_SPEAKER_SAFE_AND_HEADPHONES ||
+        snd_device == SND_DEVICE_OUT_SPEAKER_SAFE_AND_LINE )
+        start_dsm_feedback_processing(adev, benable);
+}
diff --git a/hal/audio_extn/ext_speaker.c b/hal/audio_extn/ext_speaker.c
index a551fb3..55cbb4c 100644
--- a/hal/audio_extn/ext_speaker.c
+++ b/hal/audio_extn/ext_speaker.c
@@ -18,6 +18,7 @@
 /*#define LOG_NDEBUG 0*/
 #include <cutils/log.h>
 
+#include <stdlib.h>
 #include <audio_hw.h>
 #include <dlfcn.h>
 
diff --git a/hal/audio_extn/hwdep_cal.c b/hal/audio_extn/hwdep_cal.c
new file mode 100644
index 0000000..811db3e
--- /dev/null
+++ b/hal/audio_extn/hwdep_cal.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2015 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 "hardware_cal"
+/*#define LOG_NDEBUG 0*/
+#define LOG_NDDEBUG 0
+
+#ifdef HWDEP_CAL_ENABLED
+
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <cutils/log.h>
+#include <audio_hw.h>
+#include "audio_extn.h"
+#include "sound/msmcal-hwdep.h"
+
+#define SOUND_TRIGGER_DEVICE_HANDSET_MONO_LOW_POWER_ACDB_ID (100)
+#define MAX_CAL_NAME 20
+
+typedef struct acdb_audio_cal_cfg {
+    uint32_t             persist;
+    uint32_t             snd_dev_id;
+    audio_devices_t      dev_id;
+    int32_t              acdb_dev_id;
+    uint32_t             app_type;
+    uint32_t             topo_id;
+    uint32_t             sampling_rate;
+    uint32_t             cal_type;
+    uint32_t             module_id;
+    uint32_t             param_id;
+} acdb_audio_cal_cfg_t;
+
+struct param_data {
+    int    use_case;
+    int    acdb_id;
+    int    get_size;
+    int    buff_size;
+    int    data_size;
+    void   *buff;
+};
+
+char cal_name_info[WCD9XXX_MAX_CAL][MAX_CAL_NAME] = {
+        [WCD9XXX_ANC_CAL] = "anc_cal",
+        [WCD9XXX_MBHC_CAL] = "mbhc_cal",
+        [WCD9XXX_MAD_CAL] = "mad_cal",
+};
+
+typedef int (*acdb_get_calibration_t)(char *attr, int size, void *data);
+acdb_get_calibration_t     acdb_get_calibration;
+
+static int hw_util_open(int card_no)
+{
+    int fd = -1;
+    char dev_name[256];
+
+    snprintf(dev_name, sizeof(dev_name), "/dev/snd/hwC%uD%u",
+                               card_no, WCD9XXX_CODEC_HWDEP_NODE);
+    ALOGD("%s: Opening device %s\n", __func__, dev_name);
+    fd = open(dev_name, O_WRONLY);
+    if (fd < 0) {
+        ALOGE("%s: cannot open device '%s'\n", __func__, dev_name);
+        return fd;
+    }
+    ALOGD("%s: success", __func__);
+    return fd;
+}
+
+static int send_codec_cal(acdb_get_calibration_t acdb_loader_get_calibration, int fd)
+{
+    int ret = 0, type;
+
+    for (type = WCD9XXX_ANC_CAL; type < WCD9XXX_MAX_CAL; type++) {
+        struct wcdcal_ioctl_buffer codec_buffer;
+        struct param_data calib;
+
+        if (!strcmp(cal_name_info[type], "mad_cal"))
+            calib.acdb_id = SOUND_TRIGGER_DEVICE_HANDSET_MONO_LOW_POWER_ACDB_ID;
+        calib.get_size = 1;
+        ret = acdb_loader_get_calibration(cal_name_info[type], sizeof(struct param_data),
+                                                                 &calib);
+        if (ret < 0) {
+            ALOGE("%s get_calibration failed\n", __func__);
+            return ret;
+        }
+        calib.get_size = 0;
+        calib.buff = malloc(calib.buff_size);
+        ret = acdb_loader_get_calibration(cal_name_info[type],
+                              sizeof(struct param_data), &calib);
+        if (ret < 0) {
+            ALOGE("%s get_calibration failed\n", __func__);
+            free(calib.buff);
+            return ret;
+        }
+        codec_buffer.buffer = calib.buff;
+        codec_buffer.size = calib.data_size;
+        codec_buffer.cal_type = type;
+        if (ioctl(fd, SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE, &codec_buffer) < 0)
+            ALOGE("Failed to call ioctl  for %s err=%d",
+                                  cal_name_info[type], errno);
+        ALOGD("%s cal sent for %s", __func__, cal_name_info[type]);
+        free(calib.buff);
+    }
+    return ret;
+}
+
+
+void audio_extn_hwdep_cal_send(int snd_card, void *acdb_handle)
+{
+    int fd;
+
+    fd = hw_util_open(snd_card);
+    if (fd == -1) {
+        ALOGE("%s error open\n", __func__);
+        return;
+    }
+
+    acdb_get_calibration = (acdb_get_calibration_t)
+          dlsym(acdb_handle, "acdb_loader_get_calibration");
+
+    if (acdb_get_calibration == NULL) {
+        ALOGE("%s: ERROR. dlsym Error:%s acdb_loader_get_calibration", __func__,
+           dlerror());
+        return;
+    }
+    if (send_codec_cal(acdb_get_calibration, fd) < 0)
+        ALOGE("%s: Could not send anc cal", __FUNCTION__);
+
+    close(fd);
+}
+
+#endif
diff --git a/hal/audio_extn/soundtrigger.c b/hal/audio_extn/soundtrigger.c
new file mode 100644
index 0000000..4d09387
--- /dev/null
+++ b/hal/audio_extn/soundtrigger.c
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2015 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 "soundtrigger"
+/* #define LOG_NDEBUG 0 */
+#define LOG_NDDEBUG 0
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <cutils/log.h>
+#include "audio_hw.h"
+#include "audio_extn.h"
+#include "platform.h"
+#include "platform_api.h"
+#include "sound_trigger_prop_intf.h"
+
+#define XSTR(x) STR(x)
+#define STR(x) #x
+
+struct sound_trigger_info  {
+    struct sound_trigger_session_info st_ses;
+    bool lab_stopped;
+    struct listnode list;
+};
+
+struct sound_trigger_audio_device {
+    void *lib_handle;
+    struct audio_device *adev;
+    sound_trigger_hw_call_back_t st_callback;
+    struct listnode st_ses_list;
+    pthread_mutex_t lock;
+};
+
+static struct sound_trigger_audio_device *st_dev;
+
+static struct sound_trigger_info *
+get_sound_trigger_info(int capture_handle)
+{
+    struct sound_trigger_info  *st_ses_info = NULL;
+    struct listnode *node;
+    ALOGV("%s: list %d capture_handle %d", __func__,
+           list_empty(&st_dev->st_ses_list), capture_handle);
+    list_for_each(node, &st_dev->st_ses_list) {
+        st_ses_info = node_to_item(node, struct sound_trigger_info , list);
+        if (st_ses_info->st_ses.capture_handle == capture_handle)
+            return st_ses_info;
+    }
+    return NULL;
+}
+
+int audio_hw_call_back(sound_trigger_event_type_t event,
+                       sound_trigger_event_info_t* config)
+{
+    int status = 0;
+    struct sound_trigger_info  *st_ses_info;
+
+    if (!st_dev)
+       return -EINVAL;
+
+    pthread_mutex_lock(&st_dev->lock);
+    switch (event) {
+    case ST_EVENT_SESSION_REGISTER:
+        if (!config) {
+            ALOGE("%s: NULL config", __func__);
+            status = -EINVAL;
+            break;
+        }
+        st_ses_info= calloc(1, sizeof(struct sound_trigger_info ));
+        if (!st_ses_info) {
+            ALOGE("%s: st_ses_info alloc failed", __func__);
+            status = -ENOMEM;
+            break;
+        }
+        memcpy(&st_ses_info->st_ses, &config->st_ses, sizeof (config->st_ses));
+        ALOGV("%s: add capture_handle %d pcm %p", __func__,
+              st_ses_info->st_ses.capture_handle, st_ses_info->st_ses.pcm);
+        list_add_tail(&st_dev->st_ses_list, &st_ses_info->list);
+        break;
+
+    case ST_EVENT_SESSION_DEREGISTER:
+        if (!config) {
+            ALOGE("%s: NULL config", __func__);
+            status = -EINVAL;
+            break;
+        }
+        st_ses_info = get_sound_trigger_info(config->st_ses.capture_handle);
+        if (!st_ses_info) {
+            ALOGE("%s: pcm %p not in the list!", __func__, config->st_ses.pcm);
+            status = -EINVAL;
+            break;
+        }
+        ALOGV("%s: remove capture_handle %d pcm %p", __func__,
+              st_ses_info->st_ses.capture_handle, st_ses_info->st_ses.pcm);
+        list_remove(&st_ses_info->list);
+        free(st_ses_info);
+        break;
+    default:
+        ALOGW("%s: Unknown event %d", __func__, event);
+        break;
+    }
+    pthread_mutex_unlock(&st_dev->lock);
+    return status;
+}
+
+int audio_extn_sound_trigger_read(struct stream_in *in, void *buffer,
+                       size_t bytes)
+{
+    int ret = -1;
+    struct sound_trigger_info  *st_info = NULL;
+    audio_event_info_t event;
+
+    if (!st_dev)
+       return ret;
+
+    if (!in->is_st_session_active) {
+        ALOGE(" %s: Sound trigger is not active", __func__);
+        goto exit;
+    }
+    if (in->standby)
+        in->standby = false;
+
+    pthread_mutex_lock(&st_dev->lock);
+    st_info = get_sound_trigger_info(in->capture_handle);
+    pthread_mutex_unlock(&st_dev->lock);
+    if (st_info) {
+        event.u.aud_info.ses_info = &st_info->st_ses;
+        event.u.aud_info.buf = buffer;
+        event.u.aud_info.num_bytes = bytes;
+        ret = st_dev->st_callback(AUDIO_EVENT_READ_SAMPLES, &event);
+    }
+
+exit:
+    if (ret) {
+        if (-ENETRESET == ret)
+            in->is_st_session_active = false;
+        memset(buffer, 0, bytes);
+        ALOGV("%s: read failed status %d - sleep", __func__, ret);
+        usleep((bytes * 1000000) / (audio_stream_in_frame_size((struct audio_stream_in *)in) *
+                                   in->config.rate));
+    }
+    return ret;
+}
+
+void audio_extn_sound_trigger_stop_lab(struct stream_in *in)
+{
+    int status = 0;
+    struct sound_trigger_info  *st_ses_info = NULL;
+    audio_event_info_t event;
+
+    if (!st_dev || !in)
+       return;
+
+    pthread_mutex_lock(&st_dev->lock);
+    st_ses_info = get_sound_trigger_info(in->capture_handle);
+    pthread_mutex_unlock(&st_dev->lock);
+    if (st_ses_info) {
+        event.u.ses_info = st_ses_info->st_ses;
+        ALOGV("%s: AUDIO_EVENT_STOP_LAB pcm %p", __func__, st_ses_info->st_ses.pcm);
+        st_dev->st_callback(AUDIO_EVENT_STOP_LAB, &event);
+    }
+}
+void audio_extn_sound_trigger_check_and_get_session(struct stream_in *in)
+{
+    struct sound_trigger_info  *st_ses_info = NULL;
+    struct listnode *node;
+
+    if (!st_dev || !in)
+       return;
+
+    pthread_mutex_lock(&st_dev->lock);
+    in->is_st_session = false;
+    ALOGV("%s: list %d capture_handle %d", __func__,
+          list_empty(&st_dev->st_ses_list), in->capture_handle);
+    list_for_each(node, &st_dev->st_ses_list) {
+        st_ses_info = node_to_item(node, struct sound_trigger_info , list);
+        if (st_ses_info->st_ses.capture_handle == in->capture_handle) {
+            in->pcm = st_ses_info->st_ses.pcm;
+            in->config = st_ses_info->st_ses.config;
+            in->channel_mask = audio_channel_in_mask_from_count(in->config.channels);
+            in->is_st_session = true;
+            in->is_st_session_active = true;
+            ALOGD("%s: capture_handle %d is sound trigger", __func__, in->capture_handle);
+            break;
+        }
+    }
+    pthread_mutex_unlock(&st_dev->lock);
+}
+
+void audio_extn_sound_trigger_update_device_status(snd_device_t snd_device,
+                                     st_event_type_t event)
+{
+    int device_type = -1;
+
+    if (!st_dev)
+       return;
+
+    if (snd_device >= SND_DEVICE_OUT_BEGIN &&
+        snd_device < SND_DEVICE_OUT_END)
+        device_type = PCM_PLAYBACK;
+    else if (snd_device >= SND_DEVICE_IN_BEGIN &&
+        snd_device < SND_DEVICE_IN_END)
+        device_type = PCM_CAPTURE;
+    else {
+        ALOGE("%s: invalid device 0x%x, for event %d",
+                           __func__, snd_device, event);
+        return;
+    }
+
+    ALOGI("%s: device 0x%x of type %d for Event %d",
+        __func__, snd_device, device_type, event);
+    if (device_type == PCM_CAPTURE) {
+        switch(event) {
+        case ST_EVENT_SND_DEVICE_FREE:
+            st_dev->st_callback(AUDIO_EVENT_CAPTURE_DEVICE_INACTIVE, NULL);
+            break;
+        case ST_EVENT_SND_DEVICE_BUSY:
+            st_dev->st_callback(AUDIO_EVENT_CAPTURE_DEVICE_ACTIVE, NULL);
+            break;
+        default:
+            ALOGW("%s:invalid event %d for device 0x%x",
+                                  __func__, event, snd_device);
+        }
+    }/*Events for output device, if required can be placed here in else*/
+}
+
+void audio_extn_sound_trigger_set_parameters(struct audio_device *adev __unused,
+                               struct str_parms *params)
+{
+    audio_event_info_t event;
+    char value[32];
+    int ret, val;
+
+    if(!st_dev || !params) {
+        ALOGE("%s: str_params NULL", __func__);
+        return;
+    }
+
+    ret = str_parms_get_str(params, "SND_CARD_STATUS", value,
+                            sizeof(value));
+    if (ret > 0) {
+        if (strstr(value, "OFFLINE")) {
+            event.u.status = SND_CARD_STATUS_OFFLINE;
+            st_dev->st_callback(AUDIO_EVENT_SSR, &event);
+        }
+        else if (strstr(value, "ONLINE")) {
+            event.u.status = SND_CARD_STATUS_ONLINE;
+            st_dev->st_callback(AUDIO_EVENT_SSR, &event);
+        }
+        else
+            ALOGE("%s: unknown snd_card_status", __func__);
+    }
+
+    ret = str_parms_get_str(params, "CPE_STATUS", value, sizeof(value));
+    if (ret > 0) {
+        if (strstr(value, "OFFLINE")) {
+            event.u.status = CPE_STATUS_OFFLINE;
+            st_dev->st_callback(AUDIO_EVENT_SSR, &event);
+        }
+        else if (strstr(value, "ONLINE")) {
+            event.u.status = CPE_STATUS_ONLINE;
+            st_dev->st_callback(AUDIO_EVENT_SSR, &event);
+        }
+        else
+            ALOGE("%s: unknown CPE status", __func__);
+    }
+
+    ret = str_parms_get_int(params, "SVA_NUM_SESSIONS", &val);
+    if (ret >= 0) {
+        event.u.value = val;
+        st_dev->st_callback(AUDIO_EVENT_NUM_ST_SESSIONS, &event);
+    }
+}
+
+int audio_extn_sound_trigger_init(struct audio_device *adev)
+{
+    int status = 0;
+    char sound_trigger_lib[100];
+    void *lib_handle;
+
+    ALOGI("%s: Enter", __func__);
+
+    st_dev = (struct sound_trigger_audio_device*)
+                        calloc(1, sizeof(struct sound_trigger_audio_device));
+    if (!st_dev) {
+        ALOGE("%s: ERROR. sound trigger alloc failed", __func__);
+        return -ENOMEM;
+    }
+
+    snprintf(sound_trigger_lib, sizeof(sound_trigger_lib),
+             "/system/vendor/lib/hw/sound_trigger.primary.%s.so",
+              XSTR(SOUND_TRIGGER_PLATFORM_NAME));
+
+    st_dev->lib_handle = dlopen(sound_trigger_lib, RTLD_NOW);
+
+    if (st_dev->lib_handle == NULL) {
+        ALOGE("%s: DLOPEN failed for %s. error = %s", __func__, sound_trigger_lib,
+                dlerror());
+        status = -EINVAL;
+        goto cleanup;
+    }
+    ALOGI("%s: DLOPEN successful for %s", __func__, sound_trigger_lib);
+
+    st_dev->st_callback = (sound_trigger_hw_call_back_t)
+              dlsym(st_dev->lib_handle, "sound_trigger_hw_call_back");
+
+    if (st_dev->st_callback == NULL) {
+       ALOGE("%s: ERROR. dlsym Error:%s sound_trigger_hw_call_back", __func__,
+               dlerror());
+       goto cleanup;
+    }
+
+    st_dev->adev = adev;
+    list_init(&st_dev->st_ses_list);
+
+    return 0;
+
+cleanup:
+    if (st_dev->lib_handle)
+        dlclose(st_dev->lib_handle);
+    free(st_dev);
+    st_dev = NULL;
+    return status;
+
+}
+
+void audio_extn_sound_trigger_deinit(struct audio_device *adev)
+{
+    ALOGI("%s: Enter", __func__);
+    if (st_dev && (st_dev->adev == adev) && st_dev->lib_handle) {
+        dlclose(st_dev->lib_handle);
+        free(st_dev);
+        st_dev = NULL;
+    }
+}
diff --git a/hal/audio_extn/spkr_protection.c b/hal/audio_extn/spkr_protection.c
new file mode 100644
index 0000000..4d8b233
--- /dev/null
+++ b/hal/audio_extn/spkr_protection.c
@@ -0,0 +1,905 @@
+/*
+ * Copyright (C) 2015 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_spkr_prot"
+/*#define LOG_NDEBUG 0*/
+//#define LOG_NDDEBUG 0
+
+#include <errno.h>
+#include <math.h>
+#include <cutils/log.h>
+#include <fcntl.h>
+#include "audio_hw.h"
+#include "platform.h"
+#include "platform_api.h"
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <math.h>
+#include <cutils/properties.h>
+#include "audio_extn.h"
+#include <linux/msm_audio_calibration.h>
+
+#ifdef SPKR_PROT_ENABLED
+
+/*Range of spkr temparatures -30C to 80C*/
+#define MIN_SPKR_TEMP_Q6 (-30 * (1 << 6))
+#define MAX_SPKR_TEMP_Q6 (80 * (1 << 6))
+#define VI_FEED_CHANNEL "VI_FEED_TX Channels"
+
+/*Set safe temp value to 40C*/
+#define SAFE_SPKR_TEMP 40
+#define SAFE_SPKR_TEMP_Q6 (SAFE_SPKR_TEMP * (1 << 6))
+
+/*Range of resistance values 2ohms to 40 ohms*/
+#define MIN_RESISTANCE_SPKR_Q24 (2 * (1 << 24))
+#define MAX_RESISTANCE_SPKR_Q24 (40 * (1 << 24))
+
+/*Path where the calibration file will be stored*/
+#define CALIB_FILE "/data/misc/audio/audio.cal"
+
+/*Time between retries for calibartion or intial wait time
+  after boot up*/
+#define WAIT_TIME_SPKR_CALIB (60 * 1000 * 1000)
+
+#define MIN_SPKR_IDLE_SEC (60 * 30)
+
+/*Once calibration is started sleep for 1 sec to allow
+  the calibration to kick off*/
+#define SLEEP_AFTER_CALIB_START (3000)
+
+/*If calibration is in progress wait for 200 msec before querying
+  for status again*/
+#define WAIT_FOR_GET_CALIB_STATUS (200)
+#define GET_SPKR_PROT_CAL_TIMEOUT_MSEC (5000)
+
+/*Speaker states*/
+#define SPKR_NOT_CALIBRATED -1
+#define SPKR_CALIBRATED 1
+
+/*Speaker processing state*/
+#define SPKR_PROCESSING_IN_PROGRESS 1
+#define SPKR_PROCESSING_IN_IDLE 0
+
+/*Modes of Speaker Protection*/
+enum speaker_protection_mode {
+    SPKR_PROTECTION_DISABLED = -1,
+    SPKR_PROTECTION_MODE_PROCESSING = 0,
+    SPKR_PROTECTION_MODE_CALIBRATE = 1,
+};
+
+struct speaker_prot_session {
+    int spkr_prot_mode;
+    int spkr_processing_state;
+    int thermal_client_handle;
+    pthread_mutex_t mutex_spkr_prot;
+    pthread_t spkr_calibration_thread;
+    pthread_mutex_t spkr_prot_thermalsync_mutex;
+    pthread_cond_t spkr_prot_thermalsync;
+    int cancel_spkr_calib;
+    pthread_cond_t spkr_calib_cancel;
+    pthread_mutex_t spkr_calib_cancelack_mutex;
+    pthread_cond_t spkr_calibcancel_ack;
+    pthread_t speaker_prot_threadid;
+    void *thermal_handle;
+    void *adev_handle;
+    int spkr_prot_t0;
+    struct pcm *pcm_rx;
+    struct pcm *pcm_tx;
+    int (*thermal_client_register_callback)
+    (char *client_name, int (*callback)(int), void *data);
+    void (*thermal_client_unregister_callback)(int handle);
+    int (*thermal_client_request)(char *client_name, int req_data);
+    bool spkr_prot_enable;
+    bool spkr_in_use;
+   struct timespec spkr_last_time_used;
+};
+
+static struct pcm_config pcm_config_skr_prot = {
+    .channels = 4,
+    .rate = 48000,
+    .period_size = 256,
+    .period_count = 4,
+    .format = PCM_FORMAT_S16_LE,
+    .start_threshold = 0,
+    .stop_threshold = INT_MAX,
+    .avail_min = 0,
+};
+
+static struct speaker_prot_session handle;
+static int vi_feed_no_channels;
+
+static void spkr_prot_set_spkrstatus(bool enable)
+{
+    struct timespec ts;
+    if (enable)
+       handle.spkr_in_use = true;
+    else {
+       handle.spkr_in_use = false;
+       clock_gettime(CLOCK_BOOTTIME, &handle.spkr_last_time_used);
+   }
+}
+
+void audio_extn_spkr_prot_calib_cancel(void *adev)
+{
+    pthread_t threadid;
+    struct audio_usecase *uc_info;
+    int count = 0;
+    threadid = pthread_self();
+    ALOGV("%s: Entry", __func__);
+    if (pthread_equal(handle.speaker_prot_threadid, threadid) || !adev) {
+        ALOGV("%s: Calibration not in progress.. nothihg to cancel", __func__);
+        return;
+    }
+    uc_info = get_usecase_from_list(adev, USECASE_AUDIO_SPKR_CALIB_RX);
+    if (uc_info) {
+            pthread_mutex_lock(&handle.mutex_spkr_prot);
+            pthread_mutex_lock(&handle.spkr_calib_cancelack_mutex);
+            handle.cancel_spkr_calib = 1;
+            pthread_cond_signal(&handle.spkr_calib_cancel);
+            pthread_mutex_unlock(&handle.mutex_spkr_prot);
+            pthread_cond_wait(&handle.spkr_calibcancel_ack,
+            &handle.spkr_calib_cancelack_mutex);
+            pthread_mutex_unlock(&handle.spkr_calib_cancelack_mutex);
+    }
+    ALOGV("%s: Exit", __func__);
+}
+
+static bool is_speaker_in_use(unsigned long *sec)
+{
+    struct timespec temp;
+    if (!sec) {
+        ALOGE("%s: Invalid params", __func__);
+        return true;
+    }
+     if (handle.spkr_in_use) {
+        *sec = 0;
+         return true;
+     } else {
+         clock_gettime(CLOCK_BOOTTIME, &temp);
+         *sec = temp.tv_sec - handle.spkr_last_time_used.tv_sec;
+         return false;
+     }
+}
+
+
+static int get_spkr_prot_cal(int cal_fd,
+				struct audio_cal_info_msm_spk_prot_status *status)
+{
+    int ret = 0;
+    struct audio_cal_fb_spk_prot_status    cal_data;
+
+    if (cal_fd < 0) {
+        ALOGE("%s: Error: cal_fd = %d", __func__, cal_fd);
+        ret = -EINVAL;
+        goto done;
+    }
+
+    if (status == NULL) {
+        ALOGE("%s: Error: status NULL", __func__);
+        ret = -EINVAL;
+        goto done;
+    }
+
+    cal_data.hdr.data_size = sizeof(cal_data);
+    cal_data.hdr.version = VERSION_0_0;
+    cal_data.hdr.cal_type = AFE_FB_SPKR_PROT_CAL_TYPE;
+    cal_data.hdr.cal_type_size = sizeof(cal_data.cal_type);
+    cal_data.cal_type.cal_hdr.version = VERSION_0_0;
+    cal_data.cal_type.cal_hdr.buffer_number = 0;
+    cal_data.cal_type.cal_data.mem_handle = -1;
+
+    if (ioctl(cal_fd, AUDIO_GET_CALIBRATION, &cal_data)) {
+        ALOGE("%s: Error: AUDIO_GET_CALIBRATION failed!",
+            __func__);
+        ret = -ENODEV;
+        goto done;
+    }
+
+    status->r0[SP_V2_SPKR_1] = cal_data.cal_type.cal_info.r0[SP_V2_SPKR_1];
+    status->r0[SP_V2_SPKR_2] = cal_data.cal_type.cal_info.r0[SP_V2_SPKR_2];
+    status->status = cal_data.cal_type.cal_info.status;
+done:
+    return ret;
+}
+
+static int set_spkr_prot_cal(int cal_fd,
+				struct audio_cal_info_spk_prot_cfg *protCfg)
+{
+    int ret = 0;
+    struct audio_cal_fb_spk_prot_cfg    cal_data;
+    char value[PROPERTY_VALUE_MAX];
+
+    if (cal_fd < 0) {
+        ALOGE("%s: Error: cal_fd = %d", __func__, cal_fd);
+        ret = -EINVAL;
+        goto done;
+    }
+
+    if (protCfg == NULL) {
+        ALOGE("%s: Error: status NULL", __func__);
+        ret = -EINVAL;
+        goto done;
+    }
+
+    memset(&cal_data, 0, sizeof(cal_data));
+    cal_data.hdr.data_size = sizeof(cal_data);
+    cal_data.hdr.version = VERSION_0_0;
+    cal_data.hdr.cal_type = AFE_FB_SPKR_PROT_CAL_TYPE;
+    cal_data.hdr.cal_type_size = sizeof(cal_data.cal_type);
+    cal_data.cal_type.cal_hdr.version = VERSION_0_0;
+    cal_data.cal_type.cal_hdr.buffer_number = 0;
+    cal_data.cal_type.cal_info.r0[SP_V2_SPKR_1] = protCfg->r0[SP_V2_SPKR_1];
+    cal_data.cal_type.cal_info.r0[SP_V2_SPKR_2] = protCfg->r0[SP_V2_SPKR_2];
+    cal_data.cal_type.cal_info.t0[SP_V2_SPKR_1] = protCfg->t0[SP_V2_SPKR_1];
+    cal_data.cal_type.cal_info.t0[SP_V2_SPKR_2] = protCfg->t0[SP_V2_SPKR_2];
+    cal_data.cal_type.cal_info.mode = protCfg->mode;
+    property_get("persist.spkr.cal.duration", value, "0");
+    if (atoi(value) > 0) {
+        ALOGD("%s: quick calibration enabled", __func__);
+        cal_data.cal_type.cal_info.quick_calib_flag = 1;
+    } else {
+        ALOGD("%s: quick calibration disabled", __func__);
+        cal_data.cal_type.cal_info.quick_calib_flag = 0;
+    }
+
+    cal_data.cal_type.cal_data.mem_handle = -1;
+
+    if (ioctl(cal_fd, AUDIO_SET_CALIBRATION, &cal_data)) {
+        ALOGE("%s: Error: AUDIO_SET_CALIBRATION failed!",
+            __func__);
+        ret = -ENODEV;
+        goto done;
+    }
+done:
+    return ret;
+}
+
+static int vi_feed_get_channels(struct audio_device *adev)
+{
+    struct mixer_ctl *ctl;
+    const char *mixer_ctl_name = VI_FEED_CHANNEL;
+    int value;
+
+    ALOGV("%s: entry", __func__);
+    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);
+        goto error;
+    }
+    value = mixer_ctl_get_value(ctl, 0);
+    if (value < 0)
+        goto error;
+    else
+        return value+1;
+error:
+     return -EINVAL;
+}
+
+// must be called with adev->lock acquired
+static int spkr_calibrate(int t0)
+{
+    struct audio_device *adev = handle.adev_handle;
+    struct audio_cal_info_spk_prot_cfg protCfg;
+    struct audio_cal_info_msm_spk_prot_status status;
+    bool cleanup = false, disable_rx = false, disable_tx = false;
+    int acdb_fd = -1;
+    struct audio_usecase *uc_info_rx = NULL, *uc_info_tx = NULL;
+    int32_t pcm_dev_rx_id = -1, pcm_dev_tx_id = -1;
+    struct timespec ts;
+    int retry_duration;
+
+    if (!adev) {
+        ALOGE("%s: Invalid params", __func__);
+        return -EINVAL;
+    }
+    if (!list_empty(&adev->usecase_list)) {
+        ALOGD("%s: Usecase present retry speaker protection", __func__);
+        return -EAGAIN;
+    }
+    acdb_fd = open("/dev/msm_audio_cal",O_RDWR | O_NONBLOCK);
+    if (acdb_fd < 0) {
+        ALOGE("%s: spkr_prot_thread open msm_acdb failed", __func__);
+        return -ENODEV;
+    } else {
+        protCfg.mode = MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS;
+        /* HAL for speaker protection gets only one Temperature */
+        protCfg.t0[SP_V2_SPKR_1] = t0;
+        protCfg.t0[SP_V2_SPKR_2] = t0;
+        if (set_spkr_prot_cal(acdb_fd, &protCfg)) {
+            ALOGE("%s: spkr_prot_thread set failed AUDIO_SET_SPEAKER_PROT",
+            __func__);
+            status.status = -ENODEV;
+            goto exit;
+        }
+    }
+    uc_info_rx = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
+    if (!uc_info_rx) {
+        return -ENOMEM;
+    }
+    uc_info_rx->id = USECASE_AUDIO_SPKR_CALIB_RX;
+    uc_info_rx->type = PCM_PLAYBACK;
+    uc_info_rx->in_snd_device = SND_DEVICE_NONE;
+    uc_info_rx->stream.out = adev->primary_output;
+    uc_info_rx->out_snd_device = SND_DEVICE_OUT_SPEAKER_PROTECTED;
+    disable_rx = true;
+    list_add_tail(&adev->usecase_list, &uc_info_rx->list);
+    enable_snd_device(adev, SND_DEVICE_OUT_SPEAKER_PROTECTED);
+    enable_audio_route(adev, uc_info_rx);
+
+    pcm_dev_rx_id = platform_get_pcm_device_id(uc_info_rx->id, PCM_PLAYBACK);
+    ALOGV("%s: pcm device id %d", __func__, pcm_dev_rx_id);
+    if (pcm_dev_rx_id < 0) {
+        ALOGE("%s: Invalid pcm device for usecase (%d)",
+              __func__, uc_info_rx->id);
+        status.status = -ENODEV;
+        goto exit;
+    }
+    handle.pcm_rx = handle.pcm_tx = NULL;
+    handle.pcm_rx = pcm_open(adev->snd_card,
+                             pcm_dev_rx_id,
+                             PCM_OUT, &pcm_config_skr_prot);
+    if (handle.pcm_rx && !pcm_is_ready(handle.pcm_rx)) {
+        ALOGE("%s: %s", __func__, pcm_get_error(handle.pcm_rx));
+        status.status = -EIO;
+        goto exit;
+    }
+    uc_info_tx = (struct audio_usecase *)
+    calloc(1, sizeof(struct audio_usecase));
+    if (!uc_info_tx) {
+        status.status = -ENOMEM;
+        goto exit;
+    }
+    uc_info_tx->id = USECASE_AUDIO_SPKR_CALIB_TX;
+    uc_info_tx->type = PCM_CAPTURE;
+    uc_info_tx->in_snd_device = SND_DEVICE_IN_CAPTURE_VI_FEEDBACK;
+    uc_info_tx->out_snd_device = SND_DEVICE_NONE;
+
+    disable_tx = true;
+    list_add_tail(&adev->usecase_list, &uc_info_tx->list);
+    enable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
+    enable_audio_route(adev, uc_info_tx);
+
+    pcm_dev_tx_id = platform_get_pcm_device_id(uc_info_tx->id, PCM_CAPTURE);
+    if (pcm_dev_tx_id < 0) {
+        ALOGE("%s: Invalid pcm device for usecase (%d)",
+              __func__, uc_info_tx->id);
+        status.status = -ENODEV;
+        goto exit;
+    }
+    handle.pcm_tx = pcm_open(adev->snd_card,
+                             pcm_dev_tx_id,
+                             PCM_IN, &pcm_config_skr_prot);
+    if (handle.pcm_tx && !pcm_is_ready(handle.pcm_tx)) {
+        ALOGE("%s: %s", __func__, pcm_get_error(handle.pcm_tx));
+        status.status = -EIO;
+        goto exit;
+    }
+    if (pcm_start(handle.pcm_rx) < 0) {
+        ALOGE("%s: pcm start for RX failed", __func__);
+        status.status = -EINVAL;
+        goto exit;
+    }
+    if (pcm_start(handle.pcm_tx) < 0) {
+        ALOGE("%s: pcm start for TX failed", __func__);
+        status.status = -EINVAL;
+        goto exit;
+    }
+    cleanup = true;
+    clock_gettime(CLOCK_REALTIME, &ts);
+    ts.tv_sec += (SLEEP_AFTER_CALIB_START/1000);
+    ts.tv_nsec = 0;
+    pthread_mutex_lock(&handle.mutex_spkr_prot);
+    pthread_mutex_unlock(&adev->lock);
+
+    (void)pthread_cond_timedwait(&handle.spkr_calib_cancel,
+                                 &handle.mutex_spkr_prot, &ts);
+    ALOGD("%s: Speaker calibration done", __func__);
+    pthread_mutex_lock(&handle.spkr_calib_cancelack_mutex);
+    if (handle.cancel_spkr_calib) {
+        status.status = -EAGAIN;
+        goto exit;
+    }
+
+    if (acdb_fd >= 0) {
+        status.status = -EINVAL;
+        retry_duration = 0;
+        while (!get_spkr_prot_cal(acdb_fd, &status) &&
+               retry_duration < GET_SPKR_PROT_CAL_TIMEOUT_MSEC) {
+            if (!status.status) {
+                ALOGD("%s: spkr_prot_thread calib Success R0 %d %d",
+                 __func__, status.r0[SP_V2_SPKR_1], status.r0[SP_V2_SPKR_2]);
+                FILE *fp;
+
+                vi_feed_no_channels = vi_feed_get_channels(adev);
+                ALOGD("%s: vi_feed_no_channels %d", __func__, vi_feed_no_channels);
+                if (vi_feed_no_channels < 0) {
+                    ALOGE("%s: no of channels negative !!", __func__);
+                    /* limit the number of channels to 2*/
+                    vi_feed_no_channels = 2;
+                }
+
+                fp = fopen(CALIB_FILE,"wb");
+                if (!fp) {
+                    ALOGE("%s: spkr_prot_thread File open failed %s",
+                    __func__, strerror(errno));
+                    status.status = -ENODEV;
+                } else {
+                    int i;
+                    /* HAL for speaker protection is always calibrating for stereo usecase*/
+                    for (i = 0; i < vi_feed_no_channels; i++) {
+                        fwrite(&status.r0[i], sizeof(status.r0[i]), 1, fp);
+                        fwrite(&protCfg.t0[i], sizeof(protCfg.t0[i]), 1, fp);
+                    }
+                    fclose(fp);
+                }
+                break;
+            } else if (status.status == -EAGAIN) {
+                  ALOGD("%s: spkr_prot_thread try again", __func__);
+                  usleep(WAIT_FOR_GET_CALIB_STATUS * 1000);
+                  retry_duration += WAIT_FOR_GET_CALIB_STATUS;
+            } else {
+                ALOGE("%s: spkr_prot_thread get failed status %d",
+                __func__, status.status);
+                break;
+            }
+        }
+    }
+
+exit:
+    if (handle.pcm_rx)
+        pcm_close(handle.pcm_rx);
+    handle.pcm_rx = NULL;
+
+    if (handle.pcm_tx)
+        pcm_close(handle.pcm_tx);
+    handle.pcm_tx = NULL;
+
+    /* Clear TX calibration to handset mic */
+    platform_send_audio_calibration(adev->platform, SND_DEVICE_IN_HANDSET_MIC);
+    if (!status.status) {
+        protCfg.mode = MSM_SPKR_PROT_CALIBRATED;
+        protCfg.r0[SP_V2_SPKR_1] = status.r0[SP_V2_SPKR_1];
+        protCfg.r0[SP_V2_SPKR_2] = status.r0[SP_V2_SPKR_2];
+        if (set_spkr_prot_cal(acdb_fd, &protCfg))
+            ALOGE("%s: spkr_prot_thread disable calib mode", __func__);
+        else
+            handle.spkr_prot_mode = MSM_SPKR_PROT_CALIBRATED;
+    } else {
+        protCfg.mode = MSM_SPKR_PROT_NOT_CALIBRATED;
+        handle.spkr_prot_mode = MSM_SPKR_PROT_NOT_CALIBRATED;
+        if (set_spkr_prot_cal(acdb_fd, &protCfg))
+            ALOGE("%s: spkr_prot_thread disable calib mode failed", __func__);
+    }
+    if (acdb_fd >= 0)
+        close(acdb_fd);
+
+    if (!handle.cancel_spkr_calib && cleanup) {
+        pthread_mutex_unlock(&handle.spkr_calib_cancelack_mutex);
+        pthread_cond_wait(&handle.spkr_calib_cancel, &handle.mutex_spkr_prot);
+        pthread_mutex_lock(&handle.spkr_calib_cancelack_mutex);
+    }
+    if (disable_rx) {
+        list_remove(&uc_info_rx->list);
+        disable_snd_device(adev, SND_DEVICE_OUT_SPEAKER_PROTECTED);
+        disable_audio_route(adev, uc_info_rx);
+    }
+    if (disable_tx) {
+        list_remove(&uc_info_tx->list);
+        disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
+        disable_audio_route(adev, uc_info_tx);
+    }
+    if (uc_info_rx) free(uc_info_rx);
+    if (uc_info_tx) free(uc_info_tx);
+    if (cleanup) {
+        if (handle.cancel_spkr_calib)
+            pthread_cond_signal(&handle.spkr_calibcancel_ack);
+        handle.cancel_spkr_calib = 0;
+        pthread_mutex_unlock(&handle.spkr_calib_cancelack_mutex);
+        pthread_mutex_unlock(&handle.mutex_spkr_prot);
+        pthread_mutex_lock(&adev->lock);
+    }
+
+    return status.status;
+}
+
+static void* spkr_calibration_thread()
+{
+    unsigned long sec = 0;
+    int t0;
+    bool goahead = false;
+    struct audio_cal_info_spk_prot_cfg protCfg;
+    FILE *fp;
+    int acdb_fd;
+    struct audio_device *adev = handle.adev_handle;
+    unsigned long min_idle_time = MIN_SPKR_IDLE_SEC;
+    char value[PROPERTY_VALUE_MAX];
+
+    /* If the value of this persist.spkr.cal.duration is 0
+     * then it means it will take 30min to calibrate
+     * and if the value is greater than zero then it would take
+     * that much amount of time to calibrate.
+     */
+    property_get("persist.spkr.cal.duration", value, "0");
+    if (atoi(value) > 0)
+        min_idle_time = atoi(value);
+    handle.speaker_prot_threadid = pthread_self();
+    ALOGD("spkr_prot_thread enable prot Entry");
+    acdb_fd = open("/dev/msm_audio_cal",O_RDWR | O_NONBLOCK);
+    if (acdb_fd >= 0) {
+        /*Set processing mode with t0/r0*/
+        protCfg.mode = MSM_SPKR_PROT_NOT_CALIBRATED;
+        if (set_spkr_prot_cal(acdb_fd, &protCfg)) {
+            ALOGE("%s: spkr_prot_thread enable prot failed", __func__);
+            handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED;
+            close(acdb_fd);
+        } else
+            handle.spkr_prot_mode = MSM_SPKR_PROT_NOT_CALIBRATED;
+    } else {
+        handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED;
+        ALOGE("%s: Failed to open acdb node", __func__);
+    }
+    if (handle.spkr_prot_mode == MSM_SPKR_PROT_DISABLED) {
+        ALOGD("%s: Speaker protection disabled", __func__);
+        pthread_exit(0);
+        return NULL;
+    }
+
+    fp = fopen(CALIB_FILE,"rb");
+    if (fp) {
+        int i;
+        bool spkr_calibrated = true;
+        /* HAL for speaker protection is always calibrating for stereo usecase*/
+        vi_feed_no_channels = vi_feed_get_channels(adev);
+        ALOGD("%s: vi_feed_no_channels %d", __func__, vi_feed_no_channels);
+        if (vi_feed_no_channels < 0) {
+            ALOGE("%s: no of channels negative !!", __func__);
+            /* limit the number of channels to 2*/
+            vi_feed_no_channels = 2;
+        }
+        for (i = 0; i < vi_feed_no_channels; i++) {
+            fread(&protCfg.r0[i], sizeof(protCfg.r0[i]), 1, fp);
+            fread(&protCfg.t0[i], sizeof(protCfg.t0[i]), 1, fp);
+        }
+        ALOGD("%s: spkr_prot_thread r0 value %d %d",
+               __func__, protCfg.r0[SP_V2_SPKR_1], protCfg.r0[SP_V2_SPKR_2]);
+        ALOGD("%s: spkr_prot_thread t0 value %d %d",
+               __func__, protCfg.t0[SP_V2_SPKR_1], protCfg.t0[SP_V2_SPKR_2]);
+        fclose(fp);
+        /*Valid tempature range: -30C to 80C(in q6 format)
+          Valid Resistance range: 2 ohms to 40 ohms(in q24 format)*/
+        for (i = 0; i < vi_feed_no_channels; i++) {
+            if (!((protCfg.t0[i] > MIN_SPKR_TEMP_Q6) && (protCfg.t0[i] < MAX_SPKR_TEMP_Q6)
+                && (protCfg.r0[i] >= MIN_RESISTANCE_SPKR_Q24)
+                && (protCfg.r0[i] < MAX_RESISTANCE_SPKR_Q24))) {
+                spkr_calibrated = false;
+                break;
+            }
+        }
+        if (spkr_calibrated) {
+            ALOGD("%s: Spkr calibrated", __func__);
+            protCfg.mode = MSM_SPKR_PROT_CALIBRATED;
+            if (set_spkr_prot_cal(acdb_fd, &protCfg)) {
+                ALOGE("%s: enable prot failed", __func__);
+                handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED;
+            } else
+                handle.spkr_prot_mode = MSM_SPKR_PROT_CALIBRATED;
+            close(acdb_fd);
+            pthread_exit(0);
+            return NULL;
+        }
+        close(acdb_fd);
+    }
+
+    while (1) {
+        ALOGV("%s: start calibration", __func__);
+        if (!handle.thermal_client_request("spkr",1)) {
+            ALOGD("%s: wait for callback from thermal daemon", __func__);
+            pthread_mutex_lock(&handle.spkr_prot_thermalsync_mutex);
+            pthread_cond_wait(&handle.spkr_prot_thermalsync,
+            &handle.spkr_prot_thermalsync_mutex);
+            /*Convert temp into q6 format*/
+            t0 = (handle.spkr_prot_t0 * (1 << 6));
+            pthread_mutex_unlock(&handle.spkr_prot_thermalsync_mutex);
+            if (t0 < MIN_SPKR_TEMP_Q6 || t0 > MAX_SPKR_TEMP_Q6) {
+                ALOGE("%s: Calibration temparature error %d", __func__,
+                      handle.spkr_prot_t0);
+                continue;
+            }
+            ALOGD("%s: Request t0 success value %d", __func__,
+            handle.spkr_prot_t0);
+        } else {
+            ALOGE("%s: Request t0 failed", __func__);
+            /*Assume safe value for temparature*/
+            t0 = SAFE_SPKR_TEMP_Q6;
+        }
+        goahead = false;
+        pthread_mutex_lock(&adev->lock);
+        if (is_speaker_in_use(&sec)) {
+            ALOGD("%s: Speaker in use retry calibration", __func__);
+            pthread_mutex_unlock(&adev->lock);
+            continue;
+        } else {
+            ALOGD("%s: speaker idle %ld min time %ld", __func__, sec, min_idle_time);
+            if (sec < min_idle_time) {
+                ALOGD("%s: speaker idle is less retry", __func__);
+                pthread_mutex_unlock(&adev->lock);
+                continue;
+            }
+            goahead = true;
+        }
+        if (!list_empty(&adev->usecase_list)) {
+            ALOGD("%s: Usecase active re-try calibration", __func__);
+            goahead = false;
+            pthread_mutex_unlock(&adev->lock);
+        }
+        if (goahead) {
+                int status;
+                status = spkr_calibrate(t0);
+                pthread_mutex_unlock(&adev->lock);
+                if (status == -EAGAIN) {
+                    ALOGE("%s: failed to calibrate try again %s",
+                    __func__, strerror(status));
+                    continue;
+                } else {
+                    ALOGE("%s: calibrate status %s", __func__, strerror(status));
+                }
+                ALOGD("%s: spkr_prot_thread end calibration", __func__);
+                break;
+        }
+    }
+    if (handle.thermal_client_handle)
+        handle.thermal_client_unregister_callback(handle.thermal_client_handle);
+    handle.thermal_client_handle = 0;
+    if (handle.thermal_handle)
+        dlclose(handle.thermal_handle);
+    handle.thermal_handle = NULL;
+    pthread_exit(0);
+    return NULL;
+}
+
+static int thermal_client_callback(int temp)
+{
+    pthread_mutex_lock(&handle.spkr_prot_thermalsync_mutex);
+    ALOGD("%s: spkr_prot set t0 %d and signal", __func__, temp);
+    if (handle.spkr_prot_mode == MSM_SPKR_PROT_NOT_CALIBRATED)
+        handle.spkr_prot_t0 = temp;
+    pthread_cond_signal(&handle.spkr_prot_thermalsync);
+    pthread_mutex_unlock(&handle.spkr_prot_thermalsync_mutex);
+    return 0;
+}
+
+void audio_extn_spkr_prot_init(void *adev)
+{
+    char value[PROPERTY_VALUE_MAX];
+    ALOGD("%s: Initialize speaker protection module", __func__);
+    memset(&handle, 0, sizeof(handle));
+    if (!adev) {
+        ALOGE("%s: Invalid params", __func__);
+        return;
+    }
+    property_get("persist.speaker.prot.enable", value, "");
+    handle.spkr_prot_enable = false;
+    if (!strncmp("true", value, 4))
+       handle.spkr_prot_enable = true;
+    if (!handle.spkr_prot_enable) {
+        ALOGD("%s: Speaker protection disabled", __func__);
+        return;
+    }
+    handle.adev_handle = adev;
+    handle.spkr_prot_mode = MSM_SPKR_PROT_DISABLED;
+    handle.spkr_processing_state = SPKR_PROCESSING_IN_IDLE;
+    handle.spkr_prot_t0 = -1;
+    pthread_cond_init(&handle.spkr_prot_thermalsync, NULL);
+    pthread_cond_init(&handle.spkr_calib_cancel, NULL);
+    pthread_cond_init(&handle.spkr_calibcancel_ack, NULL);
+    pthread_mutex_init(&handle.mutex_spkr_prot, NULL);
+    pthread_mutex_init(&handle.spkr_calib_cancelack_mutex, NULL);
+    pthread_mutex_init(&handle.spkr_prot_thermalsync_mutex, NULL);
+    handle.thermal_handle = dlopen("/vendor/lib/libthermalclient.so",
+            RTLD_NOW);
+    if (!handle.thermal_handle) {
+        ALOGE("%s: DLOPEN for thermal client failed", __func__);
+    } else {
+        /*Query callback function symbol*/
+        handle.thermal_client_register_callback =
+       (int (*)(char *, int (*)(int),void *))
+        dlsym(handle.thermal_handle, "thermal_client_register_callback");
+        handle.thermal_client_unregister_callback =
+        (void (*)(int) )
+        dlsym(handle.thermal_handle, "thermal_client_unregister_callback");
+        if (!handle.thermal_client_register_callback ||
+            !handle.thermal_client_unregister_callback) {
+            ALOGE("%s: DLSYM thermal_client_register_callback failed", __func__);
+        } else {
+            /*Register callback function*/
+            handle.thermal_client_handle =
+            handle.thermal_client_register_callback("spkr", thermal_client_callback, NULL);
+            if (!handle.thermal_client_handle) {
+                ALOGE("%s: thermal_client_register_callback failed", __func__);
+            } else {
+                ALOGD("%s: spkr_prot thermal_client_register_callback success", __func__);
+                handle.thermal_client_request = (int (*)(char *, int))
+                dlsym(handle.thermal_handle, "thermal_client_request");
+            }
+        }
+    }
+    if (handle.thermal_client_request) {
+        ALOGD("%s: Create calibration thread", __func__);
+        (void)pthread_create(&handle.spkr_calibration_thread,
+        (const pthread_attr_t *) NULL, spkr_calibration_thread, &handle);
+    } else {
+        ALOGE("%s: thermal_client_request failed", __func__);
+        if (handle.thermal_client_handle &&
+            handle.thermal_client_unregister_callback)
+            handle.thermal_client_unregister_callback(handle.thermal_client_handle);
+        if (handle.thermal_handle)
+            dlclose(handle.thermal_handle);
+        handle.thermal_handle = NULL;
+        handle.spkr_prot_enable = false;
+    }
+
+    if (handle.spkr_prot_enable) {
+        char platform[PROPERTY_VALUE_MAX];
+        property_get("ro.board.platform", platform, "");
+        if (!strncmp("apq8084", platform, sizeof("apq8084"))) {
+            platform_set_snd_device_backend(SND_DEVICE_OUT_VOICE_SPEAKER,
+                                            "speaker-protected",
+                                            "SLIMBUS_0_RX");
+        }
+    }
+}
+
+int audio_extn_spkr_prot_get_acdb_id(snd_device_t snd_device)
+{
+    int acdb_id;
+
+    switch(snd_device) {
+    case SND_DEVICE_OUT_SPEAKER:
+        acdb_id = platform_get_snd_device_acdb_id(SND_DEVICE_OUT_SPEAKER_PROTECTED);
+        break;
+    case SND_DEVICE_OUT_VOICE_SPEAKER:
+        acdb_id = platform_get_snd_device_acdb_id(SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED);
+        break;
+    default:
+        acdb_id = -EINVAL;
+        break;
+    }
+    return acdb_id;
+}
+
+int audio_extn_get_spkr_prot_snd_device(snd_device_t snd_device)
+{
+    if (!handle.spkr_prot_enable)
+        return snd_device;
+
+    switch(snd_device) {
+    case SND_DEVICE_OUT_SPEAKER:
+        return SND_DEVICE_OUT_SPEAKER_PROTECTED;
+    case SND_DEVICE_OUT_VOICE_SPEAKER:
+        return SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED;
+    default:
+        return snd_device;
+    }
+}
+
+int audio_extn_spkr_prot_start_processing(snd_device_t snd_device)
+{
+    struct audio_usecase *uc_info_tx;
+    struct audio_device *adev = handle.adev_handle;
+    int32_t pcm_dev_tx_id = -1, ret = 0;
+
+    ALOGV("%s: Entry", __func__);
+    if (!adev) {
+       ALOGE("%s: Invalid params", __func__);
+       return -EINVAL;
+    }
+    snd_device = audio_extn_get_spkr_prot_snd_device(snd_device);
+    spkr_prot_set_spkrstatus(true);
+    uc_info_tx = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
+    if (!uc_info_tx) {
+        return -ENOMEM;
+    }
+    ALOGV("%s: snd_device(%d: %s)", __func__, snd_device,
+           platform_get_snd_device_name(snd_device));
+    audio_route_apply_and_update_path(adev->audio_route,
+           platform_get_snd_device_name(snd_device));
+
+    pthread_mutex_lock(&handle.mutex_spkr_prot);
+    if (handle.spkr_processing_state == SPKR_PROCESSING_IN_IDLE) {
+        uc_info_tx->id = USECASE_AUDIO_SPKR_CALIB_TX;
+        uc_info_tx->type = PCM_CAPTURE;
+        uc_info_tx->in_snd_device = SND_DEVICE_IN_CAPTURE_VI_FEEDBACK;
+        uc_info_tx->out_snd_device = SND_DEVICE_NONE;
+        handle.pcm_tx = NULL;
+        list_add_tail(&adev->usecase_list, &uc_info_tx->list);
+        enable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
+        enable_audio_route(adev, uc_info_tx);
+
+        pcm_dev_tx_id = platform_get_pcm_device_id(uc_info_tx->id, PCM_CAPTURE);
+        if (pcm_dev_tx_id < 0) {
+            ALOGE("%s: Invalid pcm device for usecase (%d)",
+                  __func__, uc_info_tx->id);
+            ret = -ENODEV;
+            goto exit;
+        }
+        handle.pcm_tx = pcm_open(adev->snd_card,
+                                 pcm_dev_tx_id,
+                                 PCM_IN, &pcm_config_skr_prot);
+        if (handle.pcm_tx && !pcm_is_ready(handle.pcm_tx)) {
+            ALOGE("%s: %s", __func__, pcm_get_error(handle.pcm_tx));
+            ret = -EIO;
+            goto exit;
+        }
+        if (pcm_start(handle.pcm_tx) < 0) {
+            ALOGE("%s: pcm start for TX failed", __func__);
+            ret = -EINVAL;
+        }
+    }
+
+exit:
+    /* Clear VI feedback cal and replace with handset MIC  */
+    platform_send_audio_calibration(adev->platform, SND_DEVICE_IN_HANDSET_MIC);
+    if (ret) {
+        if (handle.pcm_tx)
+            pcm_close(handle.pcm_tx);
+        handle.pcm_tx = NULL;
+        list_remove(&uc_info_tx->list);
+        disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
+        disable_audio_route(adev, uc_info_tx);
+        free(uc_info_tx);
+    } else
+        handle.spkr_processing_state = SPKR_PROCESSING_IN_PROGRESS;
+    pthread_mutex_unlock(&handle.mutex_spkr_prot);
+    ALOGV("%s: Exit", __func__);
+    return ret;
+}
+
+void audio_extn_spkr_prot_stop_processing(snd_device_t snd_device)
+{
+    struct audio_usecase *uc_info_tx;
+    struct audio_device *adev = handle.adev_handle;
+
+    ALOGV("%s: Entry", __func__);
+    snd_device = audio_extn_get_spkr_prot_snd_device(snd_device);
+    spkr_prot_set_spkrstatus(false);
+    pthread_mutex_lock(&handle.mutex_spkr_prot);
+    if (adev && handle.spkr_processing_state == SPKR_PROCESSING_IN_PROGRESS) {
+        uc_info_tx = get_usecase_from_list(adev, USECASE_AUDIO_SPKR_CALIB_TX);
+        if (handle.pcm_tx)
+            pcm_close(handle.pcm_tx);
+        handle.pcm_tx = NULL;
+        disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
+        if (uc_info_tx) {
+            list_remove(&uc_info_tx->list);
+            disable_audio_route(adev, uc_info_tx);
+            free(uc_info_tx);
+        }
+    }
+    handle.spkr_processing_state = SPKR_PROCESSING_IN_IDLE;
+    pthread_mutex_unlock(&handle.mutex_spkr_prot);
+    if (adev)
+        audio_route_reset_and_update_path(adev->audio_route,
+                                      platform_get_snd_device_name(snd_device));
+    ALOGV("%s: Exit", __func__);
+}
+
+bool audio_extn_spkr_prot_is_enabled()
+{
+    return handle.spkr_prot_enable;
+}
+#endif /*SPKR_PROT_ENABLED*/
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 864ab5c..8fc7764 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -52,8 +52,9 @@
 
 #include "sound/compress_params.h"
 
-#define COMPRESS_OFFLOAD_FRAGMENT_SIZE (32 * 1024)
-#define COMPRESS_OFFLOAD_NUM_FRAGMENTS 4
+#define COMPRESS_OFFLOAD_FRAGMENT_SIZE (256 * 1024)
+// 2 buffers causes problems with high bitrate files
+#define COMPRESS_OFFLOAD_NUM_FRAGMENTS 3
 /* ToDo: Check and update a proper value in msec */
 #define COMPRESS_OFFLOAD_PLAYBACK_LATENCY 96
 #define COMPRESS_PLAYBACK_VOLUME_MAX 0x2000
@@ -106,6 +107,8 @@
     .channels = 2,
     .period_count = AUDIO_CAPTURE_PERIOD_COUNT,
     .format = PCM_FORMAT_S16_LE,
+    .stop_threshold = INT_MAX,
+    .avail_min = 0,
 };
 
 #define AFE_PROXY_CHANNEL_COUNT 2
@@ -144,6 +147,7 @@
     [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_PLAYBACK_TTS] = "audio-tts-playback",
 
     [USECASE_AUDIO_RECORD] = "audio-record",
     [USECASE_AUDIO_RECORD_LOW_LATENCY] = "low-latency-record",
@@ -157,6 +161,9 @@
     [USECASE_QCHAT_CALL] = "qchat-call",
     [USECASE_VOWLAN_CALL] = "vowlan-call",
 
+    [USECASE_AUDIO_SPKR_CALIB_RX] = "spkr-rx-calib",
+    [USECASE_AUDIO_SPKR_CALIB_TX] = "spkr-vi-record",
+
     [USECASE_AUDIO_PLAYBACK_AFE_PROXY] = "afe-proxy-playback",
     [USECASE_AUDIO_RECORD_AFE_PROXY] = "afe-proxy-record",
 };
@@ -176,6 +183,30 @@
 };
 
 static int set_voice_volume_l(struct audio_device *adev, float volume);
+static struct audio_device *adev = NULL;
+static pthread_mutex_t adev_init_lock;
+static unsigned int audio_device_ref_count;
+
+__attribute__ ((visibility ("default")))
+bool audio_hw_send_gain_dep_calibration(int level) {
+    bool ret_val = false;
+    ALOGV("%s: enter ... ", __func__);
+
+    pthread_mutex_lock(&adev_init_lock);
+
+    if (adev != NULL && adev->platform != NULL) {
+        pthread_mutex_lock(&adev->lock);
+        ret_val = platform_send_gain_dep_cal(adev->platform, level);
+        pthread_mutex_unlock(&adev->lock);
+    } else {
+        ALOGE("%s: %s is NULL", __func__, adev == NULL ? "adev" : "adev->platform");
+    }
+
+    pthread_mutex_unlock(&adev_init_lock);
+
+    ALOGV("%s: exit with ret_val %d ", __func__, ret_val);
+    return ret_val;
+}
 
 static bool is_supported_format(audio_format_t format)
 {
@@ -260,6 +291,9 @@
 int enable_snd_device(struct audio_device *adev,
                       snd_device_t snd_device)
 {
+    int i, num_devices = 0;
+    snd_device_t new_snd_devices[2];
+
     if (snd_device < SND_DEVICE_MIN ||
         snd_device >= SND_DEVICE_MAX) {
         ALOGE("%s: Invalid sound device %d", __func__, snd_device);
@@ -273,14 +307,43 @@
         return 0;
     }
 
+    /* due to the possibility of calibration overwrite between listen
+        and audio, notify sound trigger hal before audio calibration is sent */
+    audio_extn_sound_trigger_update_device_status(snd_device,
+                                    ST_EVENT_SND_DEVICE_BUSY);
+
+    if (audio_extn_spkr_prot_is_enabled())
+         audio_extn_spkr_prot_calib_cancel(adev);
+
     if (platform_send_audio_calibration(adev->platform, snd_device) < 0) {
         adev->snd_dev_ref_cnt[snd_device]--;
+        audio_extn_sound_trigger_update_device_status(snd_device,
+                                    ST_EVENT_SND_DEVICE_FREE);
         return -EINVAL;
     }
 
-    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);
+    audio_extn_dsm_feedback_enable(adev, snd_device, true);
+
+    if ((snd_device == SND_DEVICE_OUT_SPEAKER ||
+        snd_device == SND_DEVICE_OUT_VOICE_SPEAKER) &&
+        audio_extn_spkr_prot_is_enabled()) {
+        if (audio_extn_spkr_prot_get_acdb_id(snd_device) < 0) {
+            adev->snd_dev_ref_cnt[snd_device]--;
+            return -EINVAL;
+        }
+        if (audio_extn_spkr_prot_start_processing(snd_device)) {
+            ALOGE("%s: spkr_start_processing failed", __func__);
+            return -EINVAL;
+        }
+    } else if (platform_can_split_snd_device(snd_device, &num_devices, new_snd_devices)) {
+        for (i = 0; i < num_devices; i++) {
+            enable_snd_device(adev, new_snd_devices[i]);
+        }
+    } else {
+        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;
 }
@@ -288,6 +351,9 @@
 int disable_snd_device(struct audio_device *adev,
                        snd_device_t snd_device)
 {
+    int i, num_devices = 0;
+    snd_device_t new_snd_devices[2];
+
     if (snd_device < SND_DEVICE_MIN ||
         snd_device >= SND_DEVICE_MAX) {
         ALOGE("%s: Invalid sound device %d", __func__, snd_device);
@@ -301,14 +367,28 @@
     if (adev->snd_dev_ref_cnt[snd_device] == 0) {
         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);
+
+        audio_extn_dsm_feedback_enable(adev, snd_device, false);
+        if ((snd_device == SND_DEVICE_OUT_SPEAKER ||
+            snd_device == SND_DEVICE_OUT_VOICE_SPEAKER) &&
+            audio_extn_spkr_prot_is_enabled()) {
+            audio_extn_spkr_prot_stop_processing(snd_device);
+        } else if (platform_can_split_snd_device(snd_device, &num_devices, new_snd_devices)) {
+            for (i = 0; i < num_devices; i++) {
+                disable_snd_device(adev, new_snd_devices[i]);
+            }
+        } else {
+            audio_route_reset_and_update_path(adev->audio_route, dev_path);
+        }
+        audio_extn_sound_trigger_update_device_status(snd_device,
+                                        ST_EVENT_SND_DEVICE_FREE);
     }
     return 0;
 }
 
-static void check_usecases_codec_backend(struct audio_device *adev,
-                                          struct audio_usecase *uc_info,
-                                          snd_device_t snd_device)
+static void check_and_route_playback_usecases(struct audio_device *adev,
+                                              struct audio_usecase *uc_info,
+                                              snd_device_t snd_device)
 {
     struct listnode *node;
     struct audio_usecase *usecase;
@@ -335,7 +415,8 @@
         if (usecase->type != PCM_CAPTURE &&
                 usecase != uc_info &&
                 usecase->out_snd_device != snd_device &&
-                usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND) {
+                usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND &&
+                platform_check_backends_match(snd_device, usecase->out_snd_device)) {
             ALOGV("%s: Usecase (%s) is active on (%s) - disabling ..",
                   __func__, use_case_table[usecase->id],
                   platform_get_snd_device_name(usecase->out_snd_device));
@@ -399,7 +480,8 @@
         usecase = node_to_item(node, struct audio_usecase, list);
         if (usecase->type != PCM_PLAYBACK &&
                 usecase != uc_info &&
-                usecase->in_snd_device != snd_device) {
+                usecase->in_snd_device != snd_device &&
+                (usecase->id != USECASE_AUDIO_SPKR_CALIB_TX)) {
             ALOGV("%s: Usecase (%s) is active on (%s) - disabling ..",
                   __func__, use_case_table[usecase->id],
                   platform_get_snd_device_name(usecase->in_snd_device));
@@ -524,7 +606,7 @@
          * so that it would not result any device switch. All the usecases will
          * be switched to new device when select_devices() is called for voice call
          * usecase. This is to avoid switching devices for voice call when
-         * check_usecases_codec_backend() is called below.
+         * check_and_route_playback_usecases() is called below.
          */
         if (voice_is_in_call(adev)) {
             vc_usecase = get_usecase_from_list(adev,
@@ -619,7 +701,7 @@
     /* 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);
+            check_and_route_playback_usecases(adev, usecase, out_snd_device);
         enable_snd_device(adev, out_snd_device);
     }
 
@@ -1308,7 +1390,7 @@
 
         /*
          * select_devices() call below switches all the usecases on the same
-         * backend to the new device. Refer to check_usecases_codec_backend() in
+         * backend to the new device. Refer to check_and_route_playback_usecases() in
          * the select_devices(). But how do we undo this?
          *
          * For example, music playback is active on headset (deep-buffer usecase)
@@ -1446,6 +1528,23 @@
     return -ENOSYS;
 }
 
+#ifdef NO_AUDIO_OUT
+static ssize_t out_write_for_no_output(struct audio_stream_out *stream,
+                                       const void *buffer, size_t bytes)
+{
+    struct stream_out *out = (struct stream_out *)stream;
+
+    /* No Output device supported other than BT for playback.
+     * Sleep for the amount of buffer duration
+     */
+    pthread_mutex_lock(&out->lock);
+    usleep(bytes * 1000000 / audio_stream_frame_size(&out->stream.common) /
+            out_get_sample_rate(&out->stream.common));
+    pthread_mutex_unlock(&out->lock);
+    return bytes;
+}
+#endif
+
 static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
                          size_t bytes)
 {
@@ -1506,7 +1605,7 @@
 
     if (ret != 0) {
         if (out->pcm)
-            ALOGE("%s: error %d - %s", __func__, ret, pcm_get_error(out->pcm));
+            ALOGE("%s: error %zu - %s", __func__, ret, pcm_get_error(out->pcm));
         out_standby(&out->stream.common);
         usleep(bytes * 1000000 / audio_stream_out_frame_size(stream) /
                out_get_sample_rate(&out->stream.common));
@@ -1573,7 +1672,7 @@
         }
     } else {
         if (out->pcm) {
-            size_t avail;
+            unsigned int avail;
             if (pcm_get_htimestamp(out->pcm, &avail, timestamp) == 0) {
                 size_t kernel_buffer_size = out->config.period_size * out->config.period_count;
                 int64_t signed_frames = out->written - kernel_buffer_size + avail;
@@ -1716,6 +1815,13 @@
     int status = 0;
     ALOGV("%s: enter", __func__);
     pthread_mutex_lock(&in->lock);
+
+    if (!in->standby && in->is_st_session) {
+        ALOGD("%s: sound trigger pcm stop lab", __func__);
+        audio_extn_sound_trigger_stop_lab(in);
+        in->standby = true;
+    }
+
     if (!in->standby) {
         pthread_mutex_lock(&adev->lock);
         in->standby = true;
@@ -1802,6 +1908,14 @@
     int i, ret = -1;
 
     pthread_mutex_lock(&in->lock);
+    if (in->is_st_session) {
+        ALOGVV(" %s: reading on st session bytes=%d", __func__, bytes);
+        /* Read from sound trigger HAL */
+        audio_extn_sound_trigger_read(in, buffer, bytes);
+        pthread_mutex_unlock(&in->lock);
+        return bytes;
+    }
+
     if (in->standby) {
         pthread_mutex_lock(&adev->lock);
         ret = start_input_stream(in);
@@ -2030,6 +2144,9 @@
         if (out->flags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER) {
             out->usecase = USECASE_AUDIO_PLAYBACK_DEEP_BUFFER;
             out->config = pcm_config_deep_buffer;
+        } else if (out->flags & AUDIO_OUTPUT_FLAG_TTS) {
+            out->usecase = USECASE_AUDIO_PLAYBACK_TTS;
+            out->config = pcm_config_deep_buffer;
         } else {
             out->usecase = USECASE_AUDIO_PLAYBACK_LOW_LATENCY;
             out->config = pcm_config_low_latency;
@@ -2088,7 +2205,11 @@
     out->stream.common.remove_audio_effect = out_remove_audio_effect;
     out->stream.get_latency = out_get_latency;
     out->stream.set_volume = out_set_volume;
+#ifdef NO_AUDIO_OUT
+    out->stream.write = out_write_for_no_output;
+#else
     out->stream.write = out_write;
+#endif
     out->stream.get_render_position = out_get_render_position;
     out->stream.get_next_write_timestamp = out_get_next_write_timestamp;
     out->stream.get_presentation_position = out_get_presentation_position;
@@ -2161,9 +2282,7 @@
 
     ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_BT_NREC, value, sizeof(value));
     if (ret >= 0) {
-        /* When set to false, HAL should disable EC and NS
-         * But it is currently not supported.
-         */
+        /* When set to false, HAL should disable EC and NS */
         if (strcmp(value, AUDIO_PARAMETER_VALUE_ON) == 0)
             adev->bluetooth_nrec = true;
         else
@@ -2197,19 +2316,7 @@
             status = -EINVAL;
         }
         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;
-                    }
-                }
-            }
+            platform_swap_lr_channels(adev, reverse_speakers);
         }
     }
 
@@ -2337,7 +2444,7 @@
 }
 
 static int adev_open_input_stream(struct audio_hw_device *dev,
-                                  audio_io_handle_t handle __unused,
+                                  audio_io_handle_t handle,
                                   audio_devices_t devices,
                                   struct audio_config *config,
                                   struct audio_stream_in **stream_in,
@@ -2381,6 +2488,7 @@
     in->dev = adev;
     in->standby = 1;
     in->channel_mask = config->channel_mask;
+    in->capture_handle = handle;
 
     /* Update config params with the requested sample rate and channels */
     if (in->device == AUDIO_DEVICE_IN_TELEPHONY_RX) {
@@ -2423,6 +2531,9 @@
     in->config.channels = channel_count;
     in->config.rate = config->sample_rate;
 
+    /* This stream could be for sound trigger lab,
+       get sound trigger pcm if present */
+    audio_extn_sound_trigger_check_and_get_session(in);
 
     *stream_in = &in->stream;
     ALOGV("%s: exit", __func__);
@@ -2588,14 +2699,25 @@
 {
     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]);
+
+    if (!adev)
+        return 0;
+
+    pthread_mutex_lock(&adev_init_lock);
+
+    if ((--audio_device_ref_count) == 0) {
+        audio_route_free(adev->audio_route);
+        free(adev->snd_dev_ref_cnt);
+        platform_deinit(adev->platform);
+        audio_extn_extspk_deinit(adev->extspk);
+        audio_extn_sound_trigger_deinit(adev);
+        for (i = 0; i < ARRAY_SIZE(adev->use_case_table); ++i) {
+            pcm_params_free(adev->use_case_table[i]);
+        }
+        free(device);
     }
-    free(device);
+
+    pthread_mutex_unlock(&adev_init_lock);
     return 0;
 }
 
@@ -2606,7 +2728,11 @@
 static int period_size_is_plausible_for_low_latency(int period_size)
 {
     switch (period_size) {
+    case 48:
+    case 96:
+    case 144:
     case 160:
+    case 192:
     case 240:
     case 320:
     case 480:
@@ -2619,12 +2745,19 @@
 static int adev_open(const hw_module_t *module, const char *name,
                      hw_device_t **device)
 {
-    struct audio_device *adev;
     int i, ret;
 
     ALOGD("%s: enter", __func__);
     if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0) return -EINVAL;
-
+    pthread_mutex_lock(&adev_init_lock);
+    if (audio_device_ref_count != 0) {
+        *device = &adev->device.common;
+        audio_device_ref_count++;
+        ALOGV("%s: returning existing instance of adev", __func__);
+        ALOGV("%s: exit", __func__);
+        pthread_mutex_unlock(&adev_init_lock);
+        return 0;
+    }
     adev = calloc(1, sizeof(struct audio_device));
 
     pthread_mutex_init(&adev->lock, (const pthread_mutexattr_t *) NULL);
@@ -2672,10 +2805,12 @@
         free(adev);
         ALOGE("%s: Failed to init platform data, aborting.", __func__);
         *device = NULL;
+        pthread_mutex_unlock(&adev_init_lock);
         return -EINVAL;
     }
 
     adev->extspk = audio_extn_extspk_init(adev);
+    audio_extn_sound_trigger_init(adev);
 
     if (access(VISUALIZER_LIBRARY_PATH, R_OK) == 0) {
         adev->visualizer_lib = dlopen(VISUALIZER_LIBRARY_PATH, RTLD_NOW);
@@ -2713,6 +2848,7 @@
     adev->enable_voicerx = false;
 
     *device = &adev->device.common;
+
     if (k_enable_extended_precision)
         adev_verify_devices(adev);
 
@@ -2734,6 +2870,9 @@
         }
     }
 
+    audio_device_ref_count++;
+    pthread_mutex_unlock(&adev_init_lock);
+
     ALOGV("%s: exit", __func__);
     return 0;
 }
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index 1888aa1..b4b2583 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -57,6 +57,7 @@
     USECASE_AUDIO_PLAYBACK_LOW_LATENCY,
     USECASE_AUDIO_PLAYBACK_MULTI_CH,
     USECASE_AUDIO_PLAYBACK_OFFLOAD,
+    USECASE_AUDIO_PLAYBACK_TTS,
 
     /* HFP Use case*/
     USECASE_AUDIO_HFP_SCO,
@@ -77,8 +78,12 @@
     USECASE_INCALL_REC_DOWNLINK,
     USECASE_INCALL_REC_UPLINK_AND_DOWNLINK,
 
+    USECASE_AUDIO_SPKR_CALIB_RX,
+    USECASE_AUDIO_SPKR_CALIB_TX,
+
     USECASE_AUDIO_PLAYBACK_AFE_PROXY,
     USECASE_AUDIO_RECORD_AFE_PROXY,
+    USECASE_AUDIO_DSM_FEEDBACK,
 
     AUDIO_USECASE_MAX
 };
@@ -167,6 +172,10 @@
     bool enable_aec;
     bool enable_ns;
 
+    audio_io_handle_t capture_handle;
+    bool is_st_session;
+    bool is_st_session_active;
+
     struct audio_device *dev;
 };
 
@@ -207,7 +216,6 @@
     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;
diff --git a/hal/msm8960/platform.c b/hal/msm8960/platform.c
index 4eaf488..2f596ba 100644
--- a/hal/msm8960/platform.c
+++ b/hal/msm8960/platform.c
@@ -81,6 +81,7 @@
     bool fluence_in_voice_call;
     bool fluence_in_voice_rec;
     int  dualmic_config;
+    bool speaker_lr_swap;
 
     void *acdb_handle;
     acdb_init_t acdb_init;
@@ -416,6 +417,12 @@
     return -ENODEV;
 }
 
+int platform_get_snd_device_acdb_id(snd_device_t snd_device __unused)
+{
+    ALOGE("%s: Not implemented", __func__);
+    return -ENOSYS;
+}
+
 int platform_send_audio_calibration(void *platform, snd_device_t snd_device)
 {
     struct platform_data *my_data = (struct platform_data *)platform;
@@ -655,7 +662,7 @@
         devices & AUDIO_DEVICE_OUT_WIRED_HEADSET) {
         snd_device = SND_DEVICE_OUT_HEADPHONES;
     } else if (devices & AUDIO_DEVICE_OUT_SPEAKER) {
-        if (adev->speaker_lr_swap)
+        if (my_data->speaker_lr_swap)
             snd_device = SND_DEVICE_OUT_SPEAKER_REVERSE;
         else
             snd_device = SND_DEVICE_OUT_SPEAKER;
@@ -951,6 +958,13 @@
     return -ENOSYS;
 }
 
+int platform_set_parameters(void *platform __unused,
+                            struct str_parms *parms __unused)
+{
+    ALOGE("%s: Not implemented", __func__);
+    return -ENOSYS;
+}
+
 /* Delay in Us */
 int64_t platform_render_latency(audio_usecase_t usecase)
 {
@@ -995,12 +1009,64 @@
 }
 
 int platform_set_snd_device_backend(snd_device_t device __unused,
-                                    const char *backend __unused)
+                                    const char *backend __unused,
+                                    const char *hw_interface __unused)
 {
     return -ENOSYS;
 }
 
-void platform_set_echo_reference(struct audio_device *adev, bool enable, audio_devices_t out_device)
+void platform_set_echo_reference(struct audio_device *adev __unused,
+                                 bool enable __unused,
+                                 audio_devices_t out_device __unused)
 {
     return;
 }
+
+int platform_swap_lr_channels(struct audio_device *adev, bool swap_channels)
+{
+    // only update the selected device if there is active pcm playback
+    struct audio_usecase *usecase;
+    struct listnode *node;
+    struct platform_data *my_data = (struct platform_data *)adev->platform;
+    int status = 0;
+
+    if (my_data->speaker_lr_swap != swap_channels) {
+        my_data->speaker_lr_swap = swap_channels;
+
+        list_for_each(node, &adev->usecase_list) {
+            usecase = node_to_item(node, struct audio_usecase, list);
+            if (usecase->type == PCM_PLAYBACK &&
+                usecase->stream.out->devices & AUDIO_DEVICE_OUT_SPEAKER) {
+                const char *mixer_path;
+                if (swap_channels) {
+                    mixer_path = platform_get_snd_device_name(SND_DEVICE_OUT_SPEAKER_REVERSE);
+                    audio_route_apply_and_update_path(adev->audio_route, mixer_path);
+                } else {
+                    mixer_path = platform_get_snd_device_name(SND_DEVICE_OUT_SPEAKER);
+                    audio_route_apply_and_update_path(adev->audio_route, mixer_path);
+                }
+                break;
+            }
+        }
+    }
+    return status;
+}
+
+bool platform_send_gain_dep_cal(void *platform __unused,
+                                int level __unused)
+{
+    return 0;
+}
+
+bool platform_can_split_snd_device(snd_device_t in_snd_device __unused,
+                                   int *num_devices __unused,
+                                   snd_device_t *out_snd_devices __unused)
+{
+    return false;
+}
+
+bool platform_check_backends_match(snd_device_t snd_device1 __unused,
+                                   snd_device_t snd_device2 __unused)
+{
+    return true;
+}
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index 7ba0bf0..03aa596 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -26,10 +26,13 @@
 #include <audio_hw.h>
 #include <platform_api.h>
 #include "platform.h"
+#include "audio_extn.h"
+#include <linux/msm_audio.h>
 
 #define MIXER_XML_PATH "/system/etc/mixer_paths.xml"
 #define LIB_ACDB_LOADER "libacdbloader.so"
 #define AUDIO_DATA_BLOCK_MIXER_CTL "HDMI EDID"
+#define CVD_VERSION_MIXER_CTL "CVD Version"
 
 #define DUALMIC_CONFIG_NONE 0      /* Target does not contain 2 mics */
 #define DUALMIC_CONFIG_ENDFIRE 1
@@ -45,6 +48,8 @@
 #define MAX_SAD_BLOCKS      10
 #define SAD_BLOCK_SIZE      3
 
+#define MAX_CVD_VERSION_STRING_SIZE    100
+
 /* EDID format ID for LPCM audio */
 #define EDID_FORMAT_LPCM    1
 
@@ -53,22 +58,31 @@
 #define RETRY_US 500000
 #define MAX_SND_CARD 8
 
+#define MAX_SND_CARD_NAME_LEN 31
+
+#define DEFAULT_APP_TYPE_RX_PATH  0x11130
+
 struct audio_block_header
 {
     int reserved;
     int length;
 };
 
+enum {
+    CAL_MODE_SEND           = 0x1,
+    CAL_MODE_PERSIST        = 0x2,
+    CAL_MODE_RTAC           = 0x4
+};
+
 /* Audio calibration related functions */
 typedef void (*acdb_deallocate_t)();
-#ifdef PLATFORM_MSM8084
-typedef int  (*acdb_init_t)(char *);
-#else
+typedef int  (*acdb_init_v2_cvd_t)(char *, char *);
+typedef int  (*acdb_init_v2_t)(char *);
 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);
+typedef int (*acdb_send_gain_dep_cal_t)(int, int, int, int, int);
 
 /* Audio calibration related functions */
 struct platform_data {
@@ -78,16 +92,18 @@
     bool fluence_in_voice_comm;
     bool fluence_in_voice_rec;
     int  dualmic_config;
+    bool speaker_lr_swap;
+
     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_reload_vocvoltable_t  acdb_reload_vocvoltable;
+    acdb_send_gain_dep_cal_t   acdb_send_gain_dep_cal;
     struct csd_data *csd;
-    bool ext_speaker;
-    bool ext_earpiece;
     char ec_ref_mixer_path[64];
+
+    char *snd_card_name;
 };
 
 static int pcm_device_table[AUDIO_USECASE_MAX][2] = {
@@ -99,6 +115,8 @@
                                          MULTIMEDIA2_PCM_DEVICE},
     [USECASE_AUDIO_PLAYBACK_OFFLOAD] = {PLAYBACK_OFFLOAD_DEVICE,
                                         PLAYBACK_OFFLOAD_DEVICE},
+    [USECASE_AUDIO_PLAYBACK_TTS] = {MULTIMEDIA3_PCM_DEVICE,
+                                        MULTIMEDIA3_PCM_DEVICE},
     [USECASE_AUDIO_RECORD] = {AUDIO_RECORD_PCM_DEVICE,
                               AUDIO_RECORD_PCM_DEVICE},
     [USECASE_AUDIO_RECORD_LOW_LATENCY] = {LOWLATENCY_PCM_DEVICE,
@@ -117,10 +135,14 @@
                                                 AUDIO_RECORD_PCM_DEVICE},
     [USECASE_AUDIO_HFP_SCO] = {HFP_PCM_RX, HFP_SCO_RX},
 
+    [USECASE_AUDIO_SPKR_CALIB_RX] = {SPKR_PROT_CALIB_RX_PCM_DEVICE, -1},
+    [USECASE_AUDIO_SPKR_CALIB_TX] = {-1, SPKR_PROT_CALIB_TX_PCM_DEVICE},
+
     [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},
+    [USECASE_AUDIO_DSM_FEEDBACK] = {QUAT_MI2S_PCM_DEVICE, QUAT_MI2S_PCM_DEVICE},
 
 };
 
@@ -135,7 +157,9 @@
     [SND_DEVICE_OUT_HEADPHONES] = "headphones",
     [SND_DEVICE_OUT_LINE] = "line",
     [SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES] = "speaker-and-headphones",
+    [SND_DEVICE_OUT_SPEAKER_SAFE_AND_HEADPHONES] = "speaker-safe-and-headphones",
     [SND_DEVICE_OUT_SPEAKER_AND_LINE] = "speaker-and-line",
+    [SND_DEVICE_OUT_SPEAKER_SAFE_AND_LINE] = "speaker-safe-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",
@@ -150,6 +174,8 @@
     [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",
+    [SND_DEVICE_OUT_SPEAKER_PROTECTED] = "speaker-protected",
+    [SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED] = "voice-speaker-protected",
 
     /* Capture sound devices */
     [SND_DEVICE_IN_HANDSET_MIC] = "handset-mic",
@@ -177,8 +203,9 @@
 
     [SND_DEVICE_IN_HDMI_MIC] = "hdmi-mic",
     [SND_DEVICE_IN_BT_SCO_MIC] = "bt-sco-mic",
+    [SND_DEVICE_IN_BT_SCO_MIC_NREC] = "bt-sco-mic",
     [SND_DEVICE_IN_BT_SCO_MIC_WB] = "bt-sco-mic-wb",
-
+    [SND_DEVICE_IN_BT_SCO_MIC_WB_NREC] = "bt-sco-mic-wb",
     [SND_DEVICE_IN_CAMCORDER_MIC] = "camcorder-mic",
 
     [SND_DEVICE_IN_VOICE_DMIC] = "voice-dmic-ef",
@@ -196,6 +223,8 @@
     [SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE] = "voice-rec-dmic-ef-fluence",
 
     [SND_DEVICE_IN_VOICE_RX] = "voice-rx",
+
+    [SND_DEVICE_IN_CAPTURE_VI_FEEDBACK] = "vi-feedback",
 };
 
 /* ACDB IDs (audio DSP path configuration IDs) for each sound device */
@@ -208,7 +237,9 @@
     [SND_DEVICE_OUT_HEADPHONES] = 10,
     [SND_DEVICE_OUT_LINE] = 77,
     [SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES] = 10,
+    [SND_DEVICE_OUT_SPEAKER_SAFE_AND_HEADPHONES] = 10,
     [SND_DEVICE_OUT_SPEAKER_AND_LINE] = 77,
+    [SND_DEVICE_OUT_SPEAKER_SAFE_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,
@@ -223,6 +254,8 @@
     [SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = 17,
     [SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET] = 37,
     [SND_DEVICE_OUT_VOICE_TX] = 45,
+    [SND_DEVICE_OUT_SPEAKER_PROTECTED] = 124,
+    [SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED] = 101,
 
     [SND_DEVICE_IN_HANDSET_MIC] = 4,
     [SND_DEVICE_IN_HANDSET_MIC_AEC] = 106,
@@ -249,8 +282,9 @@
 
     [SND_DEVICE_IN_HDMI_MIC] = 4,
     [SND_DEVICE_IN_BT_SCO_MIC] = 21,
+    [SND_DEVICE_IN_BT_SCO_MIC_NREC] = 21,
     [SND_DEVICE_IN_BT_SCO_MIC_WB] = 38,
-
+    [SND_DEVICE_IN_BT_SCO_MIC_WB_NREC] = 38,
     [SND_DEVICE_IN_CAMCORDER_MIC] = 61,
 
     [SND_DEVICE_IN_VOICE_DMIC] = 41,
@@ -268,6 +302,8 @@
     [SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE] = 43,
 
     [SND_DEVICE_IN_VOICE_RX] = 44,
+
+    [SND_DEVICE_IN_CAPTURE_VI_FEEDBACK] = 102,
 };
 
 struct name_to_index {
@@ -287,7 +323,9 @@
     {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_SAFE_AND_HEADPHONES)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_LINE)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_SAFE_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)},
@@ -303,6 +341,8 @@
     {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET)},
 
     /* in */
+    {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_PROTECTED)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED)},
     {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)},
@@ -324,11 +364,13 @@
     {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_DMIC_STEREO)},
 
     {TO_NAME_INDEX(SND_DEVICE_IN_HEADSET_MIC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_HEADSET_MIC_AEC)},
 
     {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_NREC)},
     {TO_NAME_INDEX(SND_DEVICE_IN_BT_SCO_MIC_WB)},
-
+    {TO_NAME_INDEX(SND_DEVICE_IN_BT_SCO_MIC_WB_NREC)},
     {TO_NAME_INDEX(SND_DEVICE_IN_CAMCORDER_MIC)},
 
     {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_DMIC)},
@@ -344,9 +386,12 @@
     {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)},
+
+    {TO_NAME_INDEX(SND_DEVICE_IN_CAPTURE_VI_FEEDBACK)},
 };
 
-static char * backend_table[SND_DEVICE_MAX] = {0};
+static char * backend_tag_table[SND_DEVICE_MAX] = {0};
+static char * hw_interface_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)},
@@ -408,6 +453,60 @@
     return is_tmus;
 }
 
+bool platform_send_gain_dep_cal(void *platform, int level)
+{
+    bool ret_val = false;
+    struct platform_data *my_data = (struct platform_data *)platform;
+    struct audio_device *adev = my_data->adev;
+    int acdb_dev_id, app_type;
+    int acdb_dev_type = MSM_SNDDEV_CAP_RX;
+    int mode = CAL_MODE_RTAC;
+    struct listnode *node;
+    struct audio_usecase *usecase;
+
+    if (my_data->acdb_send_gain_dep_cal == NULL) {
+        ALOGE("%s: dlsym error for acdb_send_gain_dep_cal", __func__);
+        return ret_val;
+    }
+
+    if (!voice_is_in_call(adev)) {
+        ALOGV("%s: Not Voice call usecase, apply new cal for level %d",
+               __func__, level);
+        app_type = DEFAULT_APP_TYPE_RX_PATH;
+
+        // find the current active sound device
+        list_for_each(node, &adev->usecase_list) {
+            usecase = node_to_item(node, struct audio_usecase, list);
+
+            if (usecase != NULL &&
+                usecase->type == PCM_PLAYBACK &&
+                (usecase->stream.out->devices == AUDIO_DEVICE_OUT_SPEAKER)) {
+
+                ALOGV("%s: out device is %d", __func__,  usecase->out_snd_device);
+                if (audio_extn_spkr_prot_is_enabled()) {
+                    acdb_dev_id = audio_extn_spkr_prot_get_acdb_id(usecase->out_snd_device);
+                } else {
+                    acdb_dev_id = acdb_device_table[usecase->out_snd_device];
+                }
+
+                if (!my_data->acdb_send_gain_dep_cal(acdb_dev_id, app_type,
+                                                     acdb_dev_type, mode, level)) {
+                    // set ret_val true if at least one calibration is set successfully
+                    ret_val = true;
+                } else {
+                    ALOGE("%s: my_data->acdb_send_gain_dep_cal failed ", __func__);
+                }
+            } else {
+                ALOGW("%s: Usecase list is empty", __func__);
+            }
+        }
+    } else {
+        ALOGW("%s: Voice call in progress .. ignore setting new cal",
+              __func__);
+    }
+    return ret_val;
+}
+
 void platform_set_echo_reference(struct audio_device *adev, bool enable, audio_devices_t out_device)
 {
     struct platform_data *my_data = (struct platform_data *)adev->platform;
@@ -613,42 +712,79 @@
 #endif
 }
 
-static void set_platform_defaults(struct platform_data * my_data)
+static void set_platform_defaults(struct platform_data * my_data __unused)
 {
     int32_t dev;
     for (dev = 0; dev < SND_DEVICE_MAX; dev++) {
-        backend_table[dev] = NULL;
+        backend_tag_table[dev] = NULL;
+        hw_interface_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");
+    // To overwrite these go to the audio_platform_info.xml file.
+    backend_tag_table[SND_DEVICE_IN_BT_SCO_MIC] = strdup("bt-sco");
+    backend_tag_table[SND_DEVICE_IN_BT_SCO_MIC_NREC] = strdup("bt-sco");
+    backend_tag_table[SND_DEVICE_OUT_BT_SCO] = strdup("bt-sco");
+    backend_tag_table[SND_DEVICE_OUT_HDMI] = strdup("hdmi");
+    backend_tag_table[SND_DEVICE_OUT_SPEAKER_AND_HDMI] = strdup("speaker-and-hdmi");
+    backend_tag_table[SND_DEVICE_OUT_BT_SCO_WB] = strdup("bt-sco-wb");
+    backend_tag_table[SND_DEVICE_IN_BT_SCO_MIC_WB] = strdup("bt-sco-wb");
+    backend_tag_table[SND_DEVICE_IN_BT_SCO_MIC_WB_NREC] = strdup("bt-sco-wb");
+    backend_tag_table[SND_DEVICE_OUT_VOICE_TX] = strdup("afe-proxy");
+    backend_tag_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");
+    hw_interface_table[SND_DEVICE_OUT_HANDSET] = strdup("SLIMBUS_0_RX");
+    hw_interface_table[SND_DEVICE_OUT_SPEAKER] = strdup("SLIMBUS_0_RX");
+    hw_interface_table[SND_DEVICE_OUT_SPEAKER_REVERSE] = strdup("SLIMBUS_0_RX");
+    hw_interface_table[SND_DEVICE_OUT_SPEAKER_SAFE] = strdup("SLIMBUS_0_RX");
+    hw_interface_table[SND_DEVICE_OUT_HEADPHONES] = strdup("SLIMBUS_0_RX");
+    hw_interface_table[SND_DEVICE_OUT_LINE] = strdup("SLIMBUS_0_RX");
+    hw_interface_table[SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES] = strdup("SLIMBUS_0_RX");
+    hw_interface_table[SND_DEVICE_OUT_SPEAKER_SAFE_AND_HEADPHONES] = strdup("SLIMBUS_0_RX");
+    hw_interface_table[SND_DEVICE_OUT_SPEAKER_AND_LINE] = strdup("SLIMBUS_0_RX");
+    hw_interface_table[SND_DEVICE_OUT_SPEAKER_SAFE_AND_LINE] = strdup("SLIMBUS_0_RX");
+    hw_interface_table[SND_DEVICE_OUT_VOICE_HANDSET] = strdup("SLIMBUS_0_RX");
+    hw_interface_table[SND_DEVICE_OUT_VOICE_HAC_HANDSET] = strdup("SLIMBUS_0_RX");
+    hw_interface_table[SND_DEVICE_OUT_VOICE_SPEAKER] = strdup("SLIMBUS_0_RX");
+    hw_interface_table[SND_DEVICE_OUT_VOICE_HEADPHONES] = strdup("SLIMBUS_0_RX");
+    hw_interface_table[SND_DEVICE_OUT_VOICE_LINE] = strdup("SLIMBUS_0_RX");
+    hw_interface_table[SND_DEVICE_OUT_HDMI] = strdup("HDMI_RX");
+    hw_interface_table[SND_DEVICE_OUT_SPEAKER_AND_HDMI] = strdup("SLIMBUS_0_RX-and-HDMI_RX");
+    hw_interface_table[SND_DEVICE_OUT_BT_SCO] = strdup("SEC_AUX_PCM_RX");
+    hw_interface_table[SND_DEVICE_OUT_BT_SCO_WB] = strdup("SEC_AUX_PCM_RX");
+    hw_interface_table[SND_DEVICE_OUT_VOICE_HANDSET_TMUS] = strdup("SLIMBUS_0_RX");
+    hw_interface_table[SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES] = strdup("SLIMBUS_0_RX");
+    hw_interface_table[SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = strdup("SLIMBUS_0_RX");
+    hw_interface_table[SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET] = strdup("SLIMBUS_0_RX");
+    hw_interface_table[SND_DEVICE_OUT_VOICE_TX] = strdup("AFE_PCM_RX");
+    hw_interface_table[SND_DEVICE_OUT_SPEAKER_PROTECTED] = strdup("SLIMBUS_0_RX");
+    hw_interface_table[SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED] = strdup("SLIMBUS_0_RX");
+}
+
+void get_cvd_version(char *cvd_version, struct audio_device *adev)
+{
+    struct mixer_ctl *ctl;
+    int count;
+    int ret = 0;
+
+    ctl = mixer_get_ctl_by_name(adev->mixer, CVD_VERSION_MIXER_CTL);
+    if (!ctl) {
+        ALOGE("%s: Could not get ctl for mixer cmd - %s",  __func__, CVD_VERSION_MIXER_CTL);
+        goto done;
+    }
+    mixer_ctl_update(ctl);
+
+    count = mixer_ctl_get_num_values(ctl);
+    if (count > MAX_CVD_VERSION_STRING_SIZE)
+        count = MAX_CVD_VERSION_STRING_SIZE - 1;
+
+    ret = mixer_ctl_get_array(ctl, cvd_version, count);
+    if (ret != 0) {
+        ALOGE("%s: ERROR! mixer_ctl_get_array() failed to get CVD Version", __func__);
+        goto done;
     }
 
-    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");
-    }
+done:
+    return;
 }
 
 void *platform_init(struct audio_device *adev)
@@ -657,6 +793,16 @@
     struct platform_data *my_data;
     int retry_num = 0, snd_card_num = 0;
     const char *snd_card_name;
+    char *cvd_version = NULL;
+
+    my_data = calloc(1, sizeof(struct platform_data));
+
+    my_data->adev = adev;
+
+    set_platform_defaults(my_data);
+
+    /* Initialize platform specific ids and/or backends*/
+    platform_info_init(my_data);
 
     while (snd_card_num < MAX_SND_CARD) {
         adev->mixer = mixer_open(snd_card_num);
@@ -676,12 +822,23 @@
         }
 
         snd_card_name = mixer_get_name(adev->mixer);
+
+        /* validate the sound card name */
+        if (my_data->snd_card_name != NULL &&
+                strncmp(snd_card_name, my_data->snd_card_name, MAX_SND_CARD_NAME_LEN) != 0) {
+            ALOGI("%s: found valid sound card %s, but not primary sound card %s",
+                   __func__, snd_card_name, my_data->snd_card_name);
+            retry_num = 0;
+            snd_card_num++;
+            continue;
+        }
+
         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;
+            goto init_failed;
         }
         adev->snd_card = snd_card_num;
         ALOGD("%s: Opened sound card:%d", __func__, snd_card_num);
@@ -690,32 +847,15 @@
 
     if (snd_card_num >= MAX_SND_CARD) {
         ALOGE("%s: Unable to find correct sound card, aborting.", __func__);
-        return NULL;
+        goto init_failed;
     }
 
-    my_data = calloc(1, sizeof(struct platform_data));
-
-    my_data->adev = adev;
     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)) {
         ALOGE("%s: Unsupported dualmic configuration", __func__);
@@ -773,39 +913,83 @@
         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)
+
+        my_data->acdb_send_gain_dep_cal = (acdb_send_gain_dep_cal_t)dlsym(my_data->acdb_handle,
+                                                    "acdb_loader_send_gain_dep_cal");
+        if (!my_data->acdb_send_gain_dep_cal)
+            ALOGV("%s: Could not find the symbol acdb_loader_send_gain_dep_cal from %s",
+                  __func__, LIB_ACDB_LOADER);
+
+#if defined (PLATFORM_MSM8994)
+        acdb_init_v2_cvd_t acdb_init;
+        acdb_init = (acdb_init_v2_cvd_t)dlsym(my_data->acdb_handle,
+                                              "acdb_loader_init_v2");
+        if (acdb_init == NULL) {
             ALOGE("%s: dlsym error %s for acdb_loader_init_v2", __func__, dlerror());
+            goto acdb_init_fail;
+        }
+
+        cvd_version = calloc(1, MAX_CVD_VERSION_STRING_SIZE);
+        get_cvd_version(cvd_version, adev);
+        if (!cvd_version)
+            ALOGE("failed to allocate cvd_version");
         else
-            my_data->acdb_init((char *)snd_card_name);
+            acdb_init((char *)snd_card_name, cvd_version);
+        free(cvd_version);
+#elif defined (PLATFORM_MSM8084)
+        acdb_init_v2_t acdb_init;
+        acdb_init = (acdb_init_v2_t)dlsym(my_data->acdb_handle,
+                                          "acdb_loader_init_v2");
+        if (acdb_init == NULL) {
+            ALOGE("%s: dlsym error %s for acdb_loader_init_v2", __func__, dlerror());
+            goto acdb_init_fail;
+        }
+        acdb_init((char *)snd_card_name);
 #else
-        my_data->acdb_init = (acdb_init_t)dlsym(my_data->acdb_handle,
+        acdb_init_t acdb_init;
+        acdb_init = (acdb_init_t)dlsym(my_data->acdb_handle,
                                                     "acdb_loader_init_ACDB");
-        if (my_data->acdb_init == NULL)
+        if (acdb_init == NULL)
             ALOGE("%s: dlsym error %s for acdb_loader_init_ACDB", __func__, dlerror());
         else
-            my_data->acdb_init();
+            acdb_init();
 #endif
-
     }
 
-    set_platform_defaults(my_data);
+acdb_init_fail:
 
-    /* Initialize platform specific ids and/or backends*/
-    platform_info_init();
+    audio_extn_spkr_prot_init(adev);
+
+    audio_extn_hwdep_cal_send(adev->snd_card, my_data->acdb_handle);
 
     /* load csd client */
     platform_csd_init(my_data);
 
     return my_data;
+
+init_failed:
+    if (my_data)
+        free(my_data);
+    return NULL;
 }
 
 void platform_deinit(void *platform)
 {
+    int32_t dev;
+
     struct platform_data *my_data = (struct platform_data *)platform;
     close_csd_client(my_data->csd);
+
+    for (dev = 0; dev < SND_DEVICE_MAX; dev++) {
+        if (backend_tag_table[dev])
+            free(backend_tag_table[dev]);
+        if (hw_interface_table[dev])
+            free(hw_interface_table[dev]);
+    }
+
+    if (my_data->snd_card_name)
+        free(my_data->snd_card_name);
+
     free(platform);
 }
 
@@ -827,7 +1011,7 @@
         return;
     }
 
-    const char * suffix = backend_table[snd_device];
+    const char * suffix = backend_tag_table[snd_device];
 
     if (suffix != NULL) {
         strcat(mixer_path, " ");
@@ -835,6 +1019,36 @@
     }
 }
 
+bool platform_check_backends_match(snd_device_t snd_device1, snd_device_t snd_device2)
+{
+    bool result = true;
+
+    ALOGV("%s: snd_device1 = %s, snd_device2 = %s", __func__,
+                platform_get_snd_device_name(snd_device1),
+                platform_get_snd_device_name(snd_device2));
+
+    if ((snd_device1 < SND_DEVICE_MIN) || (snd_device1 >= SND_DEVICE_MAX)) {
+        ALOGE("%s: Invalid snd_device = %s", __func__,
+                platform_get_snd_device_name(snd_device1));
+        return false;
+    }
+    if ((snd_device2 < SND_DEVICE_MIN) || (snd_device2 >= SND_DEVICE_MAX)) {
+        ALOGE("%s: Invalid snd_device = %s", __func__,
+                platform_get_snd_device_name(snd_device2));
+        return false;
+    }
+    const char * be_itf1 = hw_interface_table[snd_device1];
+    const char * be_itf2 = hw_interface_table[snd_device2];
+
+    if (NULL != be_itf1 && NULL != be_itf2) {
+        if (0 != strcmp(be_itf1, be_itf2))
+            result = false;
+    }
+
+    ALOGV("%s: be_itf1 = %s, be_itf2 = %s, match %d", __func__, be_itf1, be_itf2, result);
+    return result;
+}
+
 int platform_get_pcm_device_id(audio_usecase_t usecase, int device_type)
 {
     int device_id;
@@ -897,17 +1111,28 @@
         goto done;
     }
 
+    ALOGV("%s: acdb_device_table[%s]: old = %d new = %d", __func__,
+          platform_get_snd_device_name(snd_device), acdb_device_table[snd_device], acdb_id);
     acdb_device_table[snd_device] = acdb_id;
 done:
     return ret;
 }
 
+int platform_get_snd_device_acdb_id(snd_device_t snd_device)
+{
+    if ((snd_device < SND_DEVICE_MIN) || (snd_device >= SND_DEVICE_MAX)) {
+        ALOGE("%s: Invalid snd_device = %d", __func__, snd_device);
+        return -EINVAL;
+    }
+    return acdb_device_table[snd_device];
+}
+
 int platform_send_audio_calibration(void *platform, snd_device_t snd_device)
 {
     struct platform_data *my_data = (struct platform_data *)platform;
     int acdb_dev_id, acdb_dev_type;
 
-    acdb_dev_id = acdb_device_table[snd_device];
+    acdb_dev_id = acdb_device_table[audio_extn_get_spkr_prot_snd_device(snd_device)];
     if (acdb_dev_id < 0) {
         ALOGE("%s: Could not find acdb id for device(%d)",
               __func__, snd_device);
@@ -954,7 +1179,11 @@
     if (my_data->csd == NULL)
         return ret;
 
-    acdb_rx_id = acdb_device_table[out_snd_device];
+    if (out_snd_device == SND_DEVICE_OUT_VOICE_SPEAKER &&
+        audio_extn_spkr_prot_is_enabled())
+        acdb_rx_id = acdb_device_table[SND_DEVICE_OUT_SPEAKER_PROTECTED];
+    else
+    	acdb_rx_id = acdb_device_table[out_snd_device];
 
     acdb_tx_id = acdb_device_table[in_snd_device];
 
@@ -982,6 +1211,10 @@
     if (my_data->acdb_send_voice_cal == NULL) {
         ALOGE("%s: dlsym error for acdb_send_voice_call", __func__);
     } else {
+        if (out_snd_device == SND_DEVICE_OUT_VOICE_SPEAKER &&
+            audio_extn_spkr_prot_is_enabled())
+            out_snd_device = SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED;
+
         acdb_rx_id = acdb_device_table[out_snd_device];
         acdb_tx_id = acdb_device_table[in_snd_device];
 
@@ -1006,7 +1239,11 @@
     if (my_data->csd == NULL)
         return ret;
 
-    acdb_rx_id = acdb_device_table[out_snd_device];
+    if (out_snd_device == SND_DEVICE_OUT_VOICE_SPEAKER &&
+        audio_extn_spkr_prot_is_enabled())
+        acdb_rx_id = acdb_device_table[SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED];
+    else
+        acdb_rx_id = acdb_device_table[out_snd_device];
 
     acdb_tx_id = acdb_device_table[in_snd_device];
 
@@ -1174,6 +1411,49 @@
     return ret;
 }
 
+bool platform_can_split_snd_device(snd_device_t snd_device,
+                                   int *num_devices,
+                                   snd_device_t *new_snd_devices)
+{
+    bool status = false;
+
+    if (NULL == num_devices || NULL == new_snd_devices) {
+        ALOGE("%s: NULL pointer ..", __func__);
+        return false;
+    }
+
+    /*
+     * If wired headset/headphones/line devices share the same backend
+     * with speaker/earpiece this routine returns false.
+     */
+    if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES &&
+        !platform_check_backends_match(SND_DEVICE_OUT_SPEAKER, SND_DEVICE_OUT_HEADPHONES)) {
+        *num_devices = 2;
+        new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
+        new_snd_devices[1] = SND_DEVICE_OUT_HEADPHONES;
+        status = true;
+    } else if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_LINE &&
+               !platform_check_backends_match(SND_DEVICE_OUT_SPEAKER, SND_DEVICE_OUT_LINE)) {
+        *num_devices = 2;
+        new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
+        new_snd_devices[1] = SND_DEVICE_OUT_LINE;
+        status = true;
+    } else if (snd_device == SND_DEVICE_OUT_SPEAKER_SAFE_AND_HEADPHONES &&
+               !platform_check_backends_match(SND_DEVICE_OUT_SPEAKER_SAFE, SND_DEVICE_OUT_HEADPHONES)) {
+        *num_devices = 2;
+        new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER_SAFE;
+        new_snd_devices[1] = SND_DEVICE_OUT_HEADPHONES;
+        status = true;
+    } else if (snd_device == SND_DEVICE_OUT_SPEAKER_SAFE_AND_LINE &&
+               !platform_check_backends_match(SND_DEVICE_OUT_SPEAKER_SAFE, SND_DEVICE_OUT_LINE)) {
+        *num_devices = 2;
+        new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER_SAFE;
+        new_snd_devices[1] = SND_DEVICE_OUT_LINE;
+        status = true;
+    }
+    return status;
+}
+
 snd_device_t platform_get_output_snd_device(void *platform, audio_devices_t devices)
 {
     struct platform_data *my_data = (struct platform_data *)platform;
@@ -1232,14 +1512,21 @@
 
     if (popcount(devices) == 2) {
         if (devices == (AUDIO_DEVICE_OUT_WIRED_HEADPHONE |
-                        AUDIO_DEVICE_OUT_SPEAKER)) {
-            snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES;
-        } else if (devices == (AUDIO_DEVICE_OUT_WIRED_HEADSET |
-                               AUDIO_DEVICE_OUT_SPEAKER)) {
+                        AUDIO_DEVICE_OUT_SPEAKER) ||
+                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_WIRED_HEADPHONE |
+                               AUDIO_DEVICE_OUT_SPEAKER_SAFE) ||
+                   devices == (AUDIO_DEVICE_OUT_WIRED_HEADSET |
+                               AUDIO_DEVICE_OUT_SPEAKER_SAFE)) {
+            snd_device = SND_DEVICE_OUT_SPEAKER_SAFE_AND_HEADPHONES;
+        } else if (devices == (AUDIO_DEVICE_OUT_LINE |
+                               AUDIO_DEVICE_OUT_SPEAKER_SAFE)) {
+            snd_device = SND_DEVICE_OUT_SPEAKER_SAFE_AND_LINE;
         } else if (devices == (AUDIO_DEVICE_OUT_AUX_DIGITAL |
                                AUDIO_DEVICE_OUT_SPEAKER)) {
             snd_device = SND_DEVICE_OUT_SPEAKER_AND_HDMI;
@@ -1265,7 +1552,7 @@
     } 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)
+        if (my_data->speaker_lr_swap)
             snd_device = SND_DEVICE_OUT_SPEAKER_REVERSE;
         else
             snd_device = SND_DEVICE_OUT_SPEAKER;
@@ -1343,9 +1630,15 @@
             snd_device = SND_DEVICE_IN_VOICE_HEADSET_MIC;
         } else if (out_device & AUDIO_DEVICE_OUT_ALL_SCO) {
             if (adev->bt_wb_speech_enabled) {
-                snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB;
+                if (adev->bluetooth_nrec)
+                    snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB_NREC;
+                else
+                    snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB;
             } else {
-                snd_device = SND_DEVICE_IN_BT_SCO_MIC;
+                if (adev->bluetooth_nrec)
+                    snd_device = SND_DEVICE_IN_BT_SCO_MIC_NREC;
+                else
+                    snd_device = SND_DEVICE_IN_BT_SCO_MIC;
             }
         } else if (out_device & AUDIO_DEVICE_OUT_SPEAKER ||
             out_device & AUDIO_DEVICE_OUT_SPEAKER_SAFE ||
@@ -1467,9 +1760,15 @@
             snd_device = SND_DEVICE_IN_HEADSET_MIC;
         } else if (in_device & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) {
             if (adev->bt_wb_speech_enabled) {
-                snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB;
+                if (adev->bluetooth_nrec)
+                    snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB_NREC;
+                else
+                    snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB;
             } else {
-                snd_device = SND_DEVICE_IN_BT_SCO_MIC;
+                if (adev->bluetooth_nrec)
+                    snd_device = SND_DEVICE_IN_BT_SCO_MIC_NREC;
+                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;
@@ -1493,9 +1792,15 @@
                 snd_device = SND_DEVICE_IN_SPEAKER_MIC;
         } else if (out_device & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET) {
             if (adev->bt_wb_speech_enabled) {
-                snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB;
+                if (adev->bluetooth_nrec)
+                    snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB_NREC;
+                else
+                    snd_device = SND_DEVICE_IN_BT_SCO_MIC_WB;
             } else {
-                snd_device = SND_DEVICE_IN_BT_SCO_MIC;
+                if (adev->bluetooth_nrec)
+                    snd_device = SND_DEVICE_IN_BT_SCO_MIC_NREC;
+                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;
@@ -1685,6 +1990,37 @@
     return ret;
 }
 
+int platform_set_parameters(void *platform, struct str_parms *parms)
+{
+    struct platform_data *my_data = (struct platform_data *)platform;
+    char value[64];
+    char *kv_pairs = str_parms_to_str(parms);
+    int ret = 0, err;
+
+    if (kv_pairs == NULL) {
+        ret = -EINVAL;
+        ALOGE("%s: key-value pair is NULL",__func__);
+        goto done;
+    }
+
+    ALOGV("%s: enter: %s", __func__, kv_pairs);
+
+    err = str_parms_get_str(parms, PLATFORM_CONFIG_KEY_SOUNDCARD_NAME,
+                            value, sizeof(value));
+    if (err >= 0) {
+        str_parms_del(parms, PLATFORM_CONFIG_KEY_SOUNDCARD_NAME);
+        my_data->snd_card_name = strdup(value);
+        ALOGV("%s: sound card name %s", __func__, my_data->snd_card_name);
+    }
+
+done:
+    ALOGV("%s: exit with code(%d)", __func__, ret);
+    if (kv_pairs != NULL)
+        free(kv_pairs);
+
+    return ret;
+}
+
 /* Delay in Us */
 int64_t platform_render_latency(audio_usecase_t usecase)
 {
@@ -1698,7 +2034,8 @@
     }
 }
 
-int platform_set_snd_device_backend(snd_device_t device, const char *backend)
+int platform_set_snd_device_backend(snd_device_t device, const char *backend_tag,
+                                    const char * hw_interface)
 {
     int ret = 0;
 
@@ -1709,10 +2046,20 @@
         goto done;
     }
 
-    if (backend_table[device]) {
-        free(backend_table[device]);
+    ALOGV("%s: backend_tag_table[%s]: old = %s new = %s", __func__,
+          platform_get_snd_device_name(device),
+          backend_tag_table[device] != NULL ? backend_tag_table[device]: "null", backend_tag);
+    if (backend_tag_table[device]) {
+        free(backend_tag_table[device]);
     }
-    backend_table[device] = strdup(backend);
+    backend_tag_table[device] = strdup(backend_tag);
+
+    if (hw_interface != NULL) {
+        if (hw_interface_table[device])
+            free(hw_interface_table[device]);
+        ALOGV("%s: hw_interface_table[%d] = %s", __func__, device, hw_interface);
+        hw_interface_table[device] = strdup(hw_interface);
+    }
 done:
     return ret;
 }
@@ -1730,7 +2077,37 @@
         ALOGE("%s: invalid usecase type", __func__);
         ret = -EINVAL;
     }
+    ALOGV("%s: pcm_device_table[%d][%d] = %d", __func__, usecase, type, pcm_id);
     pcm_device_table[usecase][type] = pcm_id;
 done:
     return ret;
 }
+
+int platform_swap_lr_channels(struct audio_device *adev, bool swap_channels)
+{
+    // only update if there is active pcm playback on speaker
+    struct audio_usecase *usecase;
+    struct listnode *node;
+    struct platform_data *my_data = (struct platform_data *)adev->platform;
+
+    if (my_data->speaker_lr_swap != swap_channels) {
+        my_data->speaker_lr_swap = swap_channels;
+
+        list_for_each(node, &adev->usecase_list) {
+            usecase = node_to_item(node, struct audio_usecase, list);
+            if (usecase->type == PCM_PLAYBACK &&
+                usecase->stream.out->devices & AUDIO_DEVICE_OUT_SPEAKER) {
+                const char *mixer_path;
+                if (swap_channels) {
+                    mixer_path = platform_get_snd_device_name(SND_DEVICE_OUT_SPEAKER_REVERSE);
+                    audio_route_apply_and_update_path(adev->audio_route, mixer_path);
+                } else {
+                    mixer_path = platform_get_snd_device_name(SND_DEVICE_OUT_SPEAKER);
+                    audio_route_apply_and_update_path(adev->audio_route, mixer_path);
+                }
+                break;
+            }
+        }
+    }
+    return 0;
+}
diff --git a/hal/msm8974/platform.h b/hal/msm8974/platform.h
index bb91061..f1c5239 100644
--- a/hal/msm8974/platform.h
+++ b/hal/msm8974/platform.h
@@ -45,7 +45,9 @@
     SND_DEVICE_OUT_HEADPHONES,
     SND_DEVICE_OUT_LINE,
     SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES,
+    SND_DEVICE_OUT_SPEAKER_SAFE_AND_HEADPHONES,
     SND_DEVICE_OUT_SPEAKER_AND_LINE,
+    SND_DEVICE_OUT_SPEAKER_SAFE_AND_LINE,
     SND_DEVICE_OUT_VOICE_HANDSET,
     SND_DEVICE_OUT_VOICE_SPEAKER,
     SND_DEVICE_OUT_VOICE_HEADPHONES,
@@ -60,6 +62,8 @@
     SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET,
     SND_DEVICE_OUT_VOICE_HAC_HANDSET,
     SND_DEVICE_OUT_VOICE_TX,
+    SND_DEVICE_OUT_SPEAKER_PROTECTED,
+    SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED,
     SND_DEVICE_OUT_END,
 
     /*
@@ -93,8 +97,9 @@
 
     SND_DEVICE_IN_HDMI_MIC,
     SND_DEVICE_IN_BT_SCO_MIC,
+    SND_DEVICE_IN_BT_SCO_MIC_NREC,
     SND_DEVICE_IN_BT_SCO_MIC_WB,
-
+    SND_DEVICE_IN_BT_SCO_MIC_WB_NREC,
     SND_DEVICE_IN_CAMCORDER_MIC,
 
     SND_DEVICE_IN_VOICE_DMIC,
@@ -113,6 +118,8 @@
 
     SND_DEVICE_IN_VOICE_RX,
 
+    SND_DEVICE_IN_CAPTURE_VI_FEEDBACK,
+
     SND_DEVICE_IN_END,
 
     SND_DEVICE_MAX = SND_DEVICE_IN_END,
@@ -125,19 +132,11 @@
 #define DEFAULT_MUTE_RAMP_DURATION_MS   20
 #define DEFAULT_VOLUME_RAMP_DURATION_MS 20
 
-#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 8
-#endif
 
 #define MAX_VOL_INDEX 5
 #define MIN_VOL_INDEX 0
@@ -152,8 +151,11 @@
  * We should take care of returning proper size when AudioFlinger queries for
  * the buffer size of an input/output stream
  */
-#define DEEP_BUFFER_OUTPUT_PERIOD_SIZE 960
-#define DEEP_BUFFER_OUTPUT_PERIOD_COUNT 8
+
+/* 1920 frames(40ms) at 2 buffers gives a good tradeoff between power and latency */
+#define DEEP_BUFFER_OUTPUT_PERIOD_SIZE 1920
+#define DEEP_BUFFER_OUTPUT_PERIOD_COUNT 2
+
 #define LOW_LATENCY_OUTPUT_PERIOD_SIZE 240
 #define LOW_LATENCY_OUTPUT_PERIOD_COUNT 2
 
@@ -172,22 +174,22 @@
 #define DEEP_BUFFER_PCM_DEVICE 0
 #define AUDIO_RECORD_PCM_DEVICE 0
 #define MULTIMEDIA2_PCM_DEVICE 1
+
+#define SPKR_PROT_CALIB_RX_PCM_DEVICE 5
+#define SPKR_PROT_CALIB_TX_PCM_DEVICE 25
+
+#define MULTIMEDIA3_PCM_DEVICE 4
+
+#define QUAT_MI2S_PCM_DEVICE    44
 #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
@@ -204,6 +206,8 @@
 #define LIB_CSD_CLIENT "libcsd-client.so"
 #define LIB_MDM_DETECT "libmdmdetect.so"
 
+#define PLATFORM_CONFIG_KEY_SOUNDCARD_NAME "snd_card_name"
+
 /* CSD-CLIENT related functions */
 typedef int (*init_t)(bool);
 typedef int (*deinit_t)();
diff --git a/hal/platform_api.h b/hal/platform_api.h
index e50e06d..61bb92f 100644
--- a/hal/platform_api.h
+++ b/hal/platform_api.h
@@ -22,9 +22,11 @@
 const char *platform_get_snd_device_name(snd_device_t snd_device);
 void platform_add_backend_name(void *platform, char *mixer_path,
                                                     snd_device_t snd_device);
+bool platform_send_gain_dep_cal(void *platform, int level);
 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_get_snd_device_acdb_id(snd_device_t snd_device);
 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,
@@ -56,13 +58,23 @@
 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);
+int platform_set_snd_device_backend(snd_device_t snd_device, const char * backend,
+                                    const char * hw_interface);
 
-/* From platform_info_parser.c */
-int platform_info_init(void);
+/* From platform_info.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);
+int platform_swap_lr_channels(struct audio_device *adev, bool swap_channels);
+
+bool platform_can_split_snd_device(snd_device_t in_snd_device,
+                                   int *num_devices,
+                                   snd_device_t *out_snd_devices);
+
+bool platform_check_backends_match(snd_device_t snd_device1, snd_device_t snd_device2);
+
+int platform_set_parameters(void *platform, struct str_parms *parms);
 
 #endif // AUDIO_PLATFORM_API_H
diff --git a/hal/platform_info.c b/hal/platform_info.c
index 832c0f0..c0527b4 100644
--- a/hal/platform_info.c
+++ b/hal/platform_info.c
@@ -32,6 +32,7 @@
     ACDB,
     PCM_ID,
     BACKEND_NAME,
+    CONFIG_PARAMS,
 } section_t;
 
 typedef void (* section_process_fn)(const XML_Char **attr);
@@ -39,6 +40,7 @@
 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_config_params(const XML_Char **attr);
 static void process_root(const XML_Char **attr);
 
 static section_process_fn section_table[] = {
@@ -46,10 +48,18 @@
     [ACDB] = process_acdb_id,
     [PCM_ID] = process_pcm_id,
     [BACKEND_NAME] = process_backend_name,
+    [CONFIG_PARAMS] = process_config_params,
 };
 
 static section_t section;
 
+struct platform_info {
+    void             *platform;
+    struct str_parms *kvpairs;
+};
+
+static struct platform_info my_data;
+
 /*
  * <audio_platform_info>
  * <acdb_ids>
@@ -67,6 +77,12 @@
  * ...
  * ...
  * </pcm_ids>
+ * <config_params>
+ *      <param key="snd_card_name" value="msm8994-tomtom-mtp-snd-card"/>
+ *      ...
+ *      ...
+ * </config_params>
+ *
  * </audio_platform_info>
  */
 
@@ -80,7 +96,7 @@
     int index;
 
     if (strcmp(attr[0], "name") != 0) {
-        ALOGE("%s: 'name' not found, no ACDB ID set!", __func__);
+        ALOGE("%s: 'name' not found, no pcm_id set!", __func__);
         goto done;
     }
 
@@ -128,6 +144,7 @@
 static void process_backend_name(const XML_Char **attr)
 {
     int index;
+    char *hw_interface = NULL;
 
     if (strcmp(attr[0], "name") != 0) {
         ALOGE("%s: 'name' not found, no ACDB ID set!", __func__);
@@ -147,7 +164,15 @@
         goto done;
     }
 
-    if (platform_set_snd_device_backend(index, attr[3]) < 0) {
+    if (attr[4] != NULL) {
+        if (strcmp(attr[4], "interface") != 0) {
+            hw_interface = NULL;
+        } else {
+            hw_interface = (char *)attr[5];
+        }
+    }
+
+    if (platform_set_snd_device_backend(index, attr[3], hw_interface) < 0) {
         ALOGE("%s: Device %s in %s, backend %s was not set!",
               __func__, attr[1], PLATFORM_INFO_XML_PATH, attr[3]);
         goto done;
@@ -189,6 +214,24 @@
     return;
 }
 
+/* platform specific configuration key-value pairs */
+static void process_config_params(const XML_Char **attr)
+{
+    if (strcmp(attr[0], "key") != 0) {
+        ALOGE("%s: 'key' not found", __func__);
+        goto done;
+    }
+
+    if (strcmp(attr[2], "value") != 0) {
+        ALOGE("%s: 'value' not found", __func__);
+        goto done;
+    }
+
+    str_parms_add_str(my_data.kvpairs, (char*)attr[1], (char*)attr[3]);
+done:
+    return;
+}
+
 static void start_tag(void *userdata __unused, const XML_Char *tag_name,
                       const XML_Char **attr)
 {
@@ -202,6 +245,8 @@
         section = PCM_ID;
     } else if (strcmp(tag_name, "backend_names") == 0) {
         section = BACKEND_NAME;
+    } else if (strcmp(tag_name, "config_params") == 0) {
+        section = CONFIG_PARAMS;
     } else if (strcmp(tag_name, "device") == 0) {
         if ((section != ACDB) && (section != BACKEND_NAME)) {
             ALOGE("device tag only supported for acdb/backend names");
@@ -219,6 +264,14 @@
 
         section_process_fn fn = section_table[PCM_ID];
         fn(attr);
+    } else if (strcmp(tag_name, "param") == 0) {
+        if (section != CONFIG_PARAMS) {
+            ALOGE("param tag only supported with CONFIG_PARAMS section");
+            return;
+        }
+
+        section_process_fn fn = section_table[section];
+        fn(attr);
     }
 
     return;
@@ -232,10 +285,13 @@
         section = ROOT;
     } else if (strcmp(tag_name, "backend_names") == 0) {
         section = ROOT;
+    } else if (strcmp(tag_name, "config_params") == 0) {
+        section = ROOT;
+        platform_set_parameters(my_data.platform, my_data.kvpairs);
     }
 }
 
-int platform_info_init(void)
+int platform_info_init(void *platform)
 {
     XML_Parser      parser;
     FILE            *file;
@@ -261,6 +317,9 @@
         goto err_close_file;
     }
 
+    my_data.platform = platform;
+    my_data.kvpairs = str_parms_create();
+
     XML_SetElementHandler(parser, start_tag, end_tag);
 
     while (1) {
diff --git a/hal/voice.c b/hal/voice.c
index 044dc28..1f36b36 100644
--- a/hal/voice.c
+++ b/hal/voice.c
@@ -18,6 +18,7 @@
 /*#define LOG_NDEBUG 0*/
 #define LOG_NDDEBUG 0
 
+#include <stdlib.h>
 #include <errno.h>
 #include <math.h>
 #include <cutils/log.h>
@@ -328,6 +329,9 @@
     int ret = 0;
 
     adev->voice.in_call = true;
+
+    voice_set_mic_mute(adev, adev->voice.mic_mute);
+
     ret = voice_extn_start_call(adev);
     if (ret == -ENOSYS) {
         ret = voice_start_usecase(adev, USECASE_VOICE_CALL);
diff --git a/hal/voice_extn/voice_extn.c b/hal/voice_extn/voice_extn.c
index 89b659c..6e92da8 100644
--- a/hal/voice_extn/voice_extn.c
+++ b/hal/voice_extn/voice_extn.c
@@ -20,6 +20,7 @@
 
 #include <errno.h>
 #include <math.h>
+#include <stdlib.h>
 #include <cutils/log.h>
 #include <cutils/str_parms.h>
 #include <sys/ioctl.h>
diff --git a/legacy/alsa_sound/Android.mk b/legacy/alsa_sound/Android.mk
index 534dd6a..101c22d 100644
--- a/legacy/alsa_sound/Android.mk
+++ b/legacy/alsa_sound/Android.mk
@@ -45,11 +45,7 @@
     libpower    \
     libalsa-intf
 
-ifeq ($(TARGET_SIMULATOR),true)
- LOCAL_LDLIBS += -ldl
-else
- LOCAL_SHARED_LIBRARIES += libdl
-endif
+LOCAL_SHARED_LIBRARIES += libdl
 
 LOCAL_C_INCLUDES += $(TARGET_OUT_HEADERS)/mm-audio/audio-alsa
 LOCAL_C_INCLUDES += $(TARGET_OUT_HEADERS)/mm-audio/libalsa-intf
@@ -61,7 +57,7 @@
 
 
 LOCAL_MODULE := audio.primary.msm8960
-LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+LOCAL_MODULE_RELATIVE_PATH := hw
 LOCAL_MODULE_TAGS := optional
 
 include $(BUILD_SHARED_LIBRARY)
@@ -78,7 +74,7 @@
     AudioPolicyManagerALSA.cpp
 
 LOCAL_MODULE := audio_policy.msm8960
-LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+LOCAL_MODULE_RELATIVE_PATH := hw
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_STATIC_LIBRARIES := \
@@ -97,9 +93,8 @@
 
 include $(CLEAR_VARS)
 
-LOCAL_PRELINK_MODULE := false
 
-LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+LOCAL_MODULE_RELATIVE_PATH := hw
 
 LOCAL_CFLAGS := -D_POSIX_SOURCE -Wno-multichar
 LOCAL_CFLAGS += -DQCOM_ACDB_ENABLED
@@ -128,11 +123,7 @@
     liblog    \
     libalsa-intf
 
-ifeq ($(TARGET_SIMULATOR),true)
- LOCAL_LDLIBS += -ldl
-else
- LOCAL_SHARED_LIBRARIES += libdl
-endif
+LOCAL_SHARED_LIBRARIES += libdl
 
 LOCAL_MODULE:= alsa.msm8960
 LOCAL_MODULE_TAGS := optional
diff --git a/legacy/libalsa-intf/Android.mk b/legacy/libalsa-intf/Android.mk
index 5d509b3..c259d9f 100644
--- a/legacy/libalsa-intf/Android.mk
+++ b/legacy/libalsa-intf/Android.mk
@@ -44,11 +44,6 @@
 LOCAL_SHARED_LIBRARIES:= libc libcutils #libutils #libmedia libhardware_legacy
 LOCAL_CFLAGS := -DQC_PROP -DCONFIG_DIR=\"/system/etc/snd_soc_msm/\"
 
-ifeq ($(TARGET_SIMULATOR),true)
- LOCAL_LDLIBS += -ldl
-else
- LOCAL_SHARED_LIBRARIES += libdl
-endif
-LOCAL_PRELINK_MODULE := false
+LOCAL_SHARED_LIBRARIES += libdl
 include $(BUILD_SHARED_LIBRARY)
 endif
diff --git a/post_proc/Android.mk b/post_proc/Android.mk
index 91ed2bc..b8aa9fc 100644
--- a/post_proc/Android.mk
+++ b/post_proc/Android.mk
@@ -1,4 +1,4 @@
-ifneq ($(filter msm8974 msm8226 msm8084,$(TARGET_BOARD_PLATFORM)),)
+ifneq ($(filter msm8974 msm8226 msm8084 msm8992 msm8994,$(TARGET_BOARD_PLATFORM)),)
 
 LOCAL_PATH:= $(call my-dir)
 
@@ -21,7 +21,7 @@
 
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx
+LOCAL_MODULE_RELATIVE_PATH := soundfx
 LOCAL_MODULE:= libqcompostprocbundle
 
 LOCAL_C_INCLUDES := \
@@ -29,5 +29,32 @@
 	$(call include-path-for, audio-effects)
 
 include $(BUILD_SHARED_LIBRARY)
+endif
 
-endif
\ No newline at end of file
+################################################################################
+
+ifneq ($(filter msm8992 msm8994,$(TARGET_BOARD_PLATFORM)),)
+
+include $(CLEAR_VARS)
+
+LOCAL_CFLAGS := -DLIB_AUDIO_HAL="/system/lib/hw/audio.primary."$(TARGET_BOARD_PLATFORM)".so"
+
+LOCAL_SRC_FILES:= \
+	volume_listener.c
+
+LOCAL_CFLAGS+= -O2 -fvisibility=hidden
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+	liblog \
+	libdl
+
+LOCAL_MODULE_RELATIVE_PATH := soundfx
+LOCAL_MODULE:= libvolumelistener
+
+LOCAL_C_INCLUDES := \
+	$(call include-path-for, audio-effects)
+
+include $(BUILD_SHARED_LIBRARY)
+
+endif
diff --git a/post_proc/bundle.c b/post_proc/bundle.c
index 8518e54..df327ab 100644
--- a/post_proc/bundle.c
+++ b/post_proc/bundle.c
@@ -17,6 +17,7 @@
 #define LOG_TAG "offload_effect_bundle"
 //#define LOG_NDEBUG 0
 
+#include <stdlib.h>
 #include <cutils/list.h>
 #include <cutils/log.h>
 #include <system/thread_defs.h>
@@ -620,8 +621,9 @@
         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))) {
+            *replySize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint16_t)) ||
+            // constrain memcpy below
+            ((effect_param_t *)pCmdData)->psize > *replySize - sizeof(effect_param_t)) {
             status = -EINVAL;
             ALOGV("EFFECT_CMD_GET_PARAM invalid command cmdSize %d *replySize %d",
                   cmdSize, *replySize);
diff --git a/post_proc/effect_api.c b/post_proc/effect_api.c
index cf3968b..9c15e8f 100644
--- a/post_proc/effect_api.c
+++ b/post_proc/effect_api.c
@@ -17,6 +17,7 @@
 #define LOG_TAG "offload_effect_api"
 //#define LOG_NDEBUG 0
 
+#include <errno.h>
 #include <stdbool.h>
 #include <cutils/log.h>
 #include <tinyalsa/asoundlib.h>
diff --git a/post_proc/volume_listener.c b/post_proc/volume_listener.c
new file mode 100644
index 0000000..280bfe3
--- /dev/null
+++ b/post_proc/volume_listener.c
@@ -0,0 +1,772 @@
+/*
+ * Copyright (C) 2015 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 "volume_listener"
+//#define LOG_NDEBUG 0
+#include <stdlib.h>
+#include <dlfcn.h>
+
+#include <cutils/list.h>
+#include <cutils/log.h>
+#include <hardware/audio_effect.h>
+#include <cutils/properties.h>
+
+#define PRIMARY_HAL_PATH XSTR(LIB_AUDIO_HAL)
+#define XSTR(x) STR(x)
+#define STR(x) #x
+
+#define VOL_FLAG ( EFFECT_FLAG_TYPE_INSERT | \
+                   EFFECT_FLAG_VOLUME_IND | \
+                   EFFECT_FLAG_DEVICE_IND | \
+                   EFFECT_FLAG_OFFLOAD_SUPPORTED)
+
+#define PRINT_STREAM_TYPE(i) ALOGV("descriptor found and is of stream type %s ",\
+                                                            i == MUSIC?"MUSIC": \
+                                                            i == RING?"RING": \
+                                                            i == ALARM?"ALARM": \
+                                                            i == VOICE_CALL?"Voice_call": \
+                                                            i == NOTIFICATION?"Notification":\
+                                                            "--INVALID--"); \
+
+#define MAX_GAIN_LEVELS 5
+
+#define AHAL_GAIN_DEPENDENT_INTERFACE_FUNCTION "audio_hw_send_gain_dep_calibration"
+
+enum {
+    VOL_LISTENER_STATE_UNINITIALIZED,
+    VOL_LISTENER_STATE_INITIALIZED,
+    VOL_LISTENER_STATE_ACTIVE,
+};
+
+typedef struct vol_listener_context_s vol_listener_context_t;
+static const struct effect_interface_s effect_interface;
+
+/* flag to avoid multiple initialization */
+static bool initialized = false;
+
+/* current gain dep cal level that was pushed succesfully */
+static int current_gain_dep_cal_level = -1;
+
+enum STREAM_TYPE {
+    MUSIC,
+    RING,
+    ALARM,
+    VOICE_CALL,
+    NOTIFICATION,
+    MAX_STREAM_TYPES,
+};
+
+struct vol_listener_context_s {
+    const struct effect_interface_s *itfe;
+    struct listnode effect_list_node;
+    effect_config_t config;
+    const effect_descriptor_t *desc;
+    uint32_t stream_type;
+    uint32_t session_id;
+    uint32_t state;
+    uint32_t dev_id;
+    float left_vol;
+    float right_vol;
+};
+
+/* volume listener, music UUID: 08b8b058-0590-11e5-ac71-0025b32654a0 */
+const effect_descriptor_t vol_listener_music_descriptor = {
+    { 0x08b8b058, 0x0590, 0x11e5, 0xac71, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } },  // type
+    { 0x08b8b058, 0x0590, 0x11e5, 0xac71, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } },  // uuid
+    EFFECT_CONTROL_API_VERSION,
+    VOL_FLAG,
+    0, /* TODO */
+    1,
+    "Volume listener for Music",
+    "Qualcomm Technologies Inc.",
+};
+
+/* volume listener, ring UUID: 0956df94-0590-11e5-bdbe-0025b32654a0 */
+const effect_descriptor_t vol_listener_ring_descriptor = {
+    { 0x0956df94, 0x0590, 0x11e5, 0xbdbe, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // type
+    { 0x0956df94, 0x0590, 0x11e5, 0xbdbe, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // uuid
+    EFFECT_CONTROL_API_VERSION,
+    VOL_FLAG,
+    0, /* TODO */
+    1,
+    "Volume listener for ring",
+    "Qualcomm Technologies Inc",
+};
+
+/* volume listener, alarm UUID: 09f303e2-0590-11e5-8fdb-0025b32654a0 */
+const effect_descriptor_t vol_listener_alarm_descriptor = {
+    { 0x09f303e2, 0x0590, 0x11e5, 0x8fdb, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // type
+    { 0x09f303e2, 0x0590, 0x11e5, 0x8fdb, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // uuid
+    EFFECT_CONTROL_API_VERSION,
+    VOL_FLAG,
+    0, /* TODO */
+    1,
+    "Volume listener for alarm",
+    "Qualcomm Technologies Inc",
+};
+
+/* volume listener, voice call UUID: 0ace5c08-0590-11e5-ae9e-0025b32654a0 */
+const effect_descriptor_t vol_listener_voice_call_descriptor = {
+    { 0x0ace5c08, 0x0590, 0x11e5, 0xae9e, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // type
+    { 0x0ace5c08, 0x0590, 0x11e5, 0xae9e, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // uuid
+    EFFECT_CONTROL_API_VERSION,
+    VOL_FLAG,
+    0, /* TODO */
+    1,
+    "Volume listener for voice call",
+    "Qualcomm Technologies Inc",
+};
+
+/* volume listener, notification UUID: 0b776dde-0590-11e5-81ba-0025b32654a0 */
+const effect_descriptor_t vol_listener_notification_descriptor = {
+    { 0x0b776dde, 0x0590, 0x11e5, 0x81ba, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // type
+    { 0x0b776dde, 0x0590, 0x11e5, 0x81ba, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // uuid
+    EFFECT_CONTROL_API_VERSION,
+    VOL_FLAG,
+    0, /* TODO */
+    1,
+    "Volume listener for notification",
+    "Qualcomm Technologies Inc",
+};
+
+struct amp_db_and_gain_table {
+    float amp;
+    float db;
+    uint32_t level;
+} amp_to_dBLevel_table;
+
+// using gain level for non-drc volume curve
+static const struct amp_db_and_gain_table  volume_curve_gain_mapping_table[MAX_GAIN_LEVELS] =
+{
+    /* Level 0 in the calibration database contains default calibration */
+    { 0.001774, -55, 5 },
+    { 0.501187,  -6, 4 },
+    { 0.630957,  -4, 3 },
+    { 0.794328,  -2, 2 },
+    { 1.0,        0, 1 },
+};
+
+static const effect_descriptor_t *descriptors[] = {
+    &vol_listener_music_descriptor,
+    &vol_listener_ring_descriptor,
+    &vol_listener_alarm_descriptor,
+    &vol_listener_voice_call_descriptor,
+    &vol_listener_notification_descriptor,
+    NULL,
+};
+
+pthread_once_t once = PTHREAD_ONCE_INIT;
+/* flag to indicate if init was success */
+static int init_status;
+
+/* current volume level for which gain dep cal level was selected */
+static float current_vol = 0.0;
+
+/* HAL interface to send calibration */
+static bool (*send_gain_dep_cal)(int);
+
+/* if dumping allowed */
+static bool dumping_enabled = false;
+
+/* list of created effects. */
+struct listnode vol_effect_list;
+
+/* lock must be held when modifying or accessing created_effects_list */
+pthread_mutex_t vol_listner_init_lock;
+
+/*
+ *  Local functions
+ */
+static void dump_list_l()
+{
+    struct listnode *node;
+    vol_listener_context_t *context;
+
+    ALOGW("DUMP_START :: ===========");
+
+    list_for_each(node, &vol_effect_list) {
+        context = node_to_item(node, struct vol_listener_context_s, effect_list_node);
+        // dump stream_type / Device / session_id / left / righ volume
+        ALOGW("%s: streamType [%s] Device [%d] state [%d] sessionID [%d] volume (L/R) [%f / %f] ",
+                __func__,
+                context->stream_type == MUSIC ? "MUSIC" :
+                context->stream_type == RING ? "RING" :
+                context->stream_type == ALARM ? "ALARM" :
+                context->stream_type == VOICE_CALL ? "VOICE_CALL" :
+                context->stream_type == NOTIFICATION ? "NOTIFICATION" : "--INVALID--",
+                context->dev_id, context->state, context->session_id, context->left_vol,context->right_vol);
+    }
+
+    ALOGW("DUMP_END :: ===========");
+}
+
+static void check_and_set_gain_dep_cal()
+{
+    // iterate through list and make decision to set new gain dep cal level for speaker device
+    // 1. find all usecase active on speaker
+    // 2. find average of left and right for each usecase
+    // 3. find the highest of all the active usecase
+    // 4. if new value is different than the current value then load new calibration
+
+    struct listnode *node = NULL;
+    float new_vol = 0.0;
+    int max_level = 0;
+    vol_listener_context_t *context = NULL;
+    if (dumping_enabled) {
+        dump_list_l();
+    }
+
+    ALOGV("%s ==> Start ...", __func__);
+
+    // select the highest volume on speaker device
+    list_for_each(node, &vol_effect_list) {
+        context = node_to_item(node, struct vol_listener_context_s, effect_list_node);
+        if ((context->state == VOL_LISTENER_STATE_ACTIVE) &&
+            (context->dev_id & AUDIO_DEVICE_OUT_SPEAKER) &&
+            (new_vol < (context->left_vol + context->right_vol) / 2)) {
+            new_vol = (context->left_vol + context->right_vol) / 2;
+        }
+    }
+
+    if (new_vol != current_vol) {
+        ALOGV("%s:: Change in decision :: current volume is %f new volume is %f",
+              __func__, current_vol, new_vol);
+
+        if (send_gain_dep_cal != NULL) {
+            // send Gain dep cal level
+            int gain_dep_cal_level = -1;
+
+            if (new_vol >= 1) { // max amplitude, use highest DRC level
+                gain_dep_cal_level = volume_curve_gain_mapping_table[MAX_GAIN_LEVELS - 1].level;
+            } else if (new_vol <= 0) {
+                gain_dep_cal_level = volume_curve_gain_mapping_table[0].level;
+            } else {
+                for (max_level = 0; max_level + 1 < MAX_GAIN_LEVELS; max_level++) {
+                    if (new_vol < volume_curve_gain_mapping_table[max_level + 1].amp &&
+                        new_vol >= volume_curve_gain_mapping_table[max_level].amp) {
+                        gain_dep_cal_level = volume_curve_gain_mapping_table[max_level].level;
+                        ALOGV("%s: volume(%f), gain dep cal selcetd %d ",
+                              __func__, current_vol, gain_dep_cal_level);
+                        break;
+                    }
+                }
+            }
+
+            // check here if previous gain dep cal level was not same
+            if (gain_dep_cal_level != -1) {
+                if (gain_dep_cal_level != current_gain_dep_cal_level) {
+                    // decision made .. send new level now
+                    if (!send_gain_dep_cal(gain_dep_cal_level)) {
+                        ALOGE("%s: Failed to set gain dep cal level", __func__);
+                    } else {
+                        // Success in setting the gain dep cal level, store new level and Volume
+                        if (dumping_enabled) {
+                            ALOGW("%s: (old/new) Volume (%f/%f) (old/new) level (%d/%d)",
+                                  __func__, current_vol, new_vol, current_gain_dep_cal_level,
+                                  gain_dep_cal_level);
+                        } else {
+                            ALOGV("%s: Change in Cal::(old/new) Volume (%f/%f) (old/new) level (%d/%d)",
+                                  __func__, current_vol, new_vol, current_gain_dep_cal_level,
+                                  gain_dep_cal_level);
+                        }
+                        current_gain_dep_cal_level = gain_dep_cal_level;
+                        current_vol = new_vol;
+                    }
+                } else {
+                    if (dumping_enabled) {
+                        ALOGW("%s: volume changed but gain dep cal level is still the same",
+                              __func__);
+                    } else {
+                        ALOGV("%s: volume changed but gain dep cal level is still the same",
+                              __func__);
+                    }
+                }
+            } else {
+                ALOGW("%s: Failed to find gain dep cal level for volume %f", __func__, new_vol);
+            }
+        } else {
+            ALOGE("%s: not able to send calibration, NULL function pointer",
+                  __func__);
+        }
+    } else {
+        ALOGV("%s:: volume not changed, stick to same config ..... ", __func__);
+    }
+
+    ALOGV("check_and_set_gain_dep_cal ==> End ");
+}
+
+/*
+ * Effect Control Interface Implementation
+ */
+
+static int vol_effect_process(effect_handle_t self,
+                              audio_buffer_t *in_buffer,
+                              audio_buffer_t *out_buffer)
+{
+    int status = 0;
+    ALOGV("%s Called ", __func__);
+
+    vol_listener_context_t *context = (vol_listener_context_t *)self;
+    pthread_mutex_lock(&vol_listner_init_lock);
+
+    if (context->state != VOL_LISTENER_STATE_ACTIVE) {
+        ALOGE("%s: state is not active .. return error", __func__);
+        status = -EINVAL;
+        goto exit;
+    }
+
+    // calculation based on channel count 2
+    if (in_buffer->raw != out_buffer->raw) {
+        memcpy(out_buffer->raw, in_buffer->raw, out_buffer->frameCount * 2 * sizeof(int16_t));
+    } else {
+        ALOGW("%s: something wrong, didn't handle in_buffer and out_buffer same address case",
+              __func__);
+    }
+
+exit:
+    pthread_mutex_unlock(&vol_listner_init_lock);
+    return status;
+}
+
+
+static int vol_effect_command(effect_handle_t self,
+                              uint32_t cmd_code, uint32_t cmd_size,
+                              void *p_cmd_data, uint32_t *reply_size,
+                              void *p_reply_data)
+{
+    vol_listener_context_t *context = (vol_listener_context_t *)self;
+    int status = 0;
+
+    ALOGV("%s Called ", __func__);
+    pthread_mutex_lock(&vol_listner_init_lock);
+
+    if (context == NULL || context->state == VOL_LISTENER_STATE_UNINITIALIZED) {
+        ALOGE("%s: %s is NULL", __func__, (context == NULL) ?
+              "context" : "context->state");
+        status = -EINVAL;
+        goto exit;
+    }
+
+    switch (cmd_code) {
+    case EFFECT_CMD_INIT:
+        ALOGV("%s :: cmd called EFFECT_CMD_INIT", __func__);
+        if (p_reply_data == NULL || *reply_size != sizeof(int)) {
+            ALOGE("%s: EFFECT_CMD_INIT: %s, sending -EINVAL", __func__,
+                  (p_reply_data == NULL) ? "p_reply_data is NULL" :
+                  "*reply_size != sizeof(int)");
+            return -EINVAL;
+        }
+        *(int *)p_reply_data = 0;
+        break;
+
+    case EFFECT_CMD_SET_CONFIG:
+        ALOGV("%s :: cmd called EFFECT_CMD_SET_CONFIG", __func__);
+        break;
+
+    case EFFECT_CMD_GET_CONFIG:
+        ALOGV("%s :: cmd called EFFECT_CMD_GET_CONFIG", __func__);
+        break;
+
+    case EFFECT_CMD_RESET:
+        ALOGV("%s :: cmd called EFFECT_CMD_RESET", __func__);
+        break;
+
+    case EFFECT_CMD_SET_AUDIO_MODE:
+        ALOGV("%s :: cmd called EFFECT_CMD_SET_AUDIO_MODE", __func__);
+        break;
+
+    case EFFECT_CMD_OFFLOAD:
+        ALOGV("%s :: cmd called EFFECT_CMD_OFFLOAD", __func__);
+        if (p_reply_data == NULL || *reply_size != sizeof(int)) {
+            ALOGE("%s: EFFECT_CMD_OFFLOAD: %s, sending -EINVAL", __func__,
+                  (p_reply_data == NULL) ? "p_reply_data is NULL" :
+                  "*reply_size != sizeof(int)");
+            return -EINVAL;
+        }
+        *(int *)p_reply_data = 0;
+        break;
+
+    case EFFECT_CMD_ENABLE:
+        ALOGV("%s :: cmd called EFFECT_CMD_ENABLE", __func__);
+        if (p_reply_data == NULL || *reply_size != sizeof(int)) {
+            ALOGE("%s: EFFECT_CMD_ENABLE: %s, sending -EINVAL", __func__,
+                   (p_reply_data == NULL) ? "p_reply_data is NULL" :
+                   "*reply_size != sizeof(int)");
+            status = -EINVAL;
+            goto exit;
+        }
+
+        if (context->state != VOL_LISTENER_STATE_INITIALIZED) {
+            ALOGE("%s: EFFECT_CMD_ENABLE : state not INITIALIZED", __func__);
+            status = -ENOSYS;
+            goto exit;
+        }
+
+        context->state = VOL_LISTENER_STATE_ACTIVE;
+        *(int *)p_reply_data = 0;
+
+        // After changing the state and if device is speaker
+        // recalculate gain dep cal level
+        if (context->dev_id & AUDIO_DEVICE_OUT_SPEAKER) {
+                check_and_set_gain_dep_cal();
+        }
+
+        break;
+
+    case EFFECT_CMD_DISABLE:
+        ALOGV("%s :: cmd called EFFECT_CMD_DISABLE", __func__);
+        if (p_reply_data == NULL || *reply_size != sizeof(int)) {
+            ALOGE("%s: EFFECT_CMD_DISABLE: %s, sending -EINVAL", __func__,
+                  (p_reply_data == NULL) ? "p_reply_data is NULL" :
+                  "*reply_size != sizeof(int)");
+            status = -EINVAL;
+            goto exit;
+        }
+
+        if (context->state != VOL_LISTENER_STATE_ACTIVE) {
+            ALOGE("%s: EFFECT_CMD_ENABLE : state not ACTIVE", __func__);
+            status = -ENOSYS;
+            goto exit;
+        }
+
+        context->state = VOL_LISTENER_STATE_INITIALIZED;
+        *(int *)p_reply_data = 0;
+
+        // After changing the state and if device is speaker
+        // recalculate gain dep cal level
+        if (context->dev_id & AUDIO_DEVICE_OUT_SPEAKER) {
+            check_and_set_gain_dep_cal();
+        }
+
+        break;
+
+    case EFFECT_CMD_GET_PARAM:
+        ALOGV("%s :: cmd called EFFECT_CMD_GET_PARAM", __func__);
+        break;
+
+    case EFFECT_CMD_SET_PARAM:
+        ALOGV("%s :: cmd called EFFECT_CMD_SET_PARAM", __func__);
+        break;
+
+    case EFFECT_CMD_SET_DEVICE:
+    {
+        uint32_t new_device;
+        bool recompute_gain_dep_cal_Level = false;
+        ALOGV("cmd called EFFECT_CMD_SET_DEVICE ");
+
+        if (p_cmd_data == NULL) {
+            ALOGE("%s: EFFECT_CMD_SET_DEVICE: cmd data NULL", __func__);
+            status = -EINVAL;
+            goto exit;
+        }
+
+        new_device = *(uint32_t *)p_cmd_data;
+        ALOGV("%s :: EFFECT_CMD_SET_DEVICE: (current/new) device (0x%x / 0x%x)",
+               __func__, context->dev_id, new_device);
+
+        // check if old or new device is speaker
+        if ((context->dev_id & AUDIO_DEVICE_OUT_SPEAKER) ||
+            (new_device & AUDIO_DEVICE_OUT_SPEAKER)) {
+            recompute_gain_dep_cal_Level = true;
+        }
+
+        context->dev_id = new_device;
+
+        if (recompute_gain_dep_cal_Level) {
+            check_and_set_gain_dep_cal();
+        }
+    }
+    break;
+
+    case EFFECT_CMD_SET_VOLUME:
+    {
+        float left_vol = 0, right_vol = 0;
+        bool recompute_gain_dep_cal_Level = false;
+
+        ALOGV("cmd called EFFECT_CMD_SET_VOLUME");
+        if (p_cmd_data == NULL || cmd_size != 2 * sizeof(uint32_t)) {
+            ALOGE("%s: EFFECT_CMD_SET_VOLUME: %s", __func__, (p_cmd_data == NULL) ?
+                  "p_cmd_data is NULL" : "cmd_size issue");
+            status = -EINVAL;
+            goto exit;
+        }
+
+        if (context->dev_id & AUDIO_DEVICE_OUT_SPEAKER) {
+            recompute_gain_dep_cal_Level = true;
+        }
+
+        left_vol = (float)(*(uint32_t *)p_cmd_data) / (1 << 24);
+        right_vol = (float)(*((uint32_t *)p_cmd_data + 1)) / (1 << 24);
+        ALOGV("Current Volume (%f / %f ) new Volume (%f / %f)", context->left_vol,
+              context->right_vol, left_vol, right_vol);
+
+        context->left_vol = left_vol;
+        context->right_vol = right_vol;
+
+        // recompute gan dep cal level only if volume changed on speaker device
+        if (recompute_gain_dep_cal_Level) {
+            check_and_set_gain_dep_cal();
+        }
+    }
+    break;
+
+    default:
+        ALOGW("volume_listener_command invalid command %d", cmd_code);
+        status = -ENOSYS;
+        break;
+    }
+
+exit:
+    pthread_mutex_unlock(&vol_listner_init_lock);
+    return status;
+}
+
+/* Effect Control Interface Implementation: get_descriptor */
+static int vol_effect_get_descriptor(effect_handle_t   self,
+                                     effect_descriptor_t *descriptor)
+{
+    vol_listener_context_t *context = (vol_listener_context_t *)self;
+    ALOGV("%s Called ", __func__);
+
+    if (descriptor == NULL) {
+        ALOGE("%s: descriptor is NULL", __func__);
+        return -EINVAL;
+    }
+
+    *descriptor = *context->desc;
+    return 0;
+}
+
+static void init_once()
+{
+    int i = 0;
+    if (initialized) {
+        ALOGV("%s : already init .. do nothing", __func__);
+        return;
+    }
+
+    ALOGD("%s Called ", __func__);
+    pthread_mutex_init(&vol_listner_init_lock, NULL);
+
+    // get hal function pointer
+    if (access(PRIMARY_HAL_PATH, R_OK) == 0) {
+        void *hal_lib_pointer = dlopen(PRIMARY_HAL_PATH, RTLD_NOW);
+        if (hal_lib_pointer == NULL) {
+            ALOGE("%s: DLOPEN failed for %s", __func__, PRIMARY_HAL_PATH);
+            send_gain_dep_cal = NULL;
+        } else {
+            ALOGV("%s: DLOPEN of %s Succes .. next get HAL entry function", __func__, PRIMARY_HAL_PATH);
+            send_gain_dep_cal = (bool (*)(int))dlsym(hal_lib_pointer, AHAL_GAIN_DEPENDENT_INTERFACE_FUNCTION);
+            if (send_gain_dep_cal == NULL) {
+                ALOGE("Couldnt able to get the function symbol");
+            }
+        }
+    } else {
+        ALOGE("%s: not able to acces lib %s ", __func__, PRIMARY_HAL_PATH);
+        send_gain_dep_cal = NULL;
+    }
+
+    // check system property to see if dumping is required
+    char check_dump_val[PROPERTY_VALUE_MAX];
+    property_get("audio.volume.listener.dump", check_dump_val, "0");
+    if (atoi(check_dump_val)) {
+        dumping_enabled = true;
+    }
+
+    init_status = 0;
+    list_init(&vol_effect_list);
+    initialized = true;
+}
+
+static int lib_init()
+{
+    pthread_once(&once, init_once);
+    ALOGV("%s Called ", __func__);
+    return init_status;
+}
+
+static int vol_prc_lib_create(const effect_uuid_t *uuid,
+                              int32_t session_id,
+                              int32_t io_id,
+                              effect_handle_t *p_handle)
+{
+    int itt = 0;
+    vol_listener_context_t *context = NULL;
+
+    ALOGV("volume_prc_lib_create .. called ..");
+
+    if (lib_init() != 0) {
+        return init_status;
+    }
+
+    if (p_handle == NULL || uuid == NULL) {
+        ALOGE("%s: %s is NULL", __func__, (p_handle == NULL) ? "p_handle" : "uuid");
+        return -EINVAL;
+    }
+
+    context = (vol_listener_context_t *)calloc(1, sizeof(vol_listener_context_t));
+
+    if (context == NULL) {
+        ALOGE("%s: failed to allocate for context .. oops !!", __func__);
+        return -EINVAL;
+    }
+
+    // check if UUID is supported
+    for (itt = 0; descriptors[itt] != NULL; itt++) {
+        if (memcmp(uuid, &descriptors[itt]->uuid, sizeof(effect_uuid_t)) == 0) {
+            // check if this correct .. very imp
+            context->desc = descriptors[itt];
+            context->stream_type = itt;
+            PRINT_STREAM_TYPE(itt)
+            break;
+        }
+    }
+
+    if (descriptors[itt] == NULL) {
+        ALOGE("%s .. couldnt find passed uuid, something wrong", __func__);
+        free(context);
+        return -EINVAL;
+    }
+
+    ALOGV("%s CREATED_CONTEXT %p", __func__, context);
+
+    context->itfe = &effect_interface;
+    context->state = VOL_LISTENER_STATE_INITIALIZED;
+    context->dev_id = AUDIO_DEVICE_NONE;
+    context->session_id = session_id;
+
+    // Add this to master list
+    pthread_mutex_lock(&vol_listner_init_lock);
+    list_add_tail(&vol_effect_list, &context->effect_list_node);
+
+    if (dumping_enabled) {
+        dump_list_l();
+    }
+
+    pthread_mutex_unlock(&vol_listner_init_lock);
+
+    *p_handle = (effect_handle_t)context;
+    return 0;
+}
+
+static int vol_prc_lib_release(effect_handle_t handle)
+{
+    struct listnode *node, *temp_node_next;
+    vol_listener_context_t *context = NULL;
+    vol_listener_context_t *recv_contex = (vol_listener_context_t *)handle;
+    int status = -EINVAL;
+    bool recompute_flag = false;
+    int active_stream_count = 0;
+    uint32_t session_id;
+    uint32_t stream_type;
+    effect_uuid_t uuid;
+
+    ALOGV("%s context %p", __func__, handle);
+
+    if (recv_contex == NULL) {
+        return status;
+    }
+    pthread_mutex_lock(&vol_listner_init_lock);
+    session_id = recv_contex->session_id;
+    stream_type = recv_contex->stream_type;
+    uuid = recv_contex->desc->uuid;
+
+    // check if the handle/context provided is valid
+    list_for_each_safe(node, temp_node_next, &vol_effect_list) {
+        context = node_to_item(node, struct vol_listener_context_s, effect_list_node);
+        if ((memcmp(&(context->desc->uuid), &uuid, sizeof(effect_uuid_t)) == 0)
+            && (context->session_id == session_id)
+            && (context->stream_type == stream_type)) {
+            ALOGV("--- Found something to remove ---");
+            list_remove(node);
+            PRINT_STREAM_TYPE(context->stream_type);
+            if (context->dev_id && AUDIO_DEVICE_OUT_SPEAKER) {
+                recompute_flag = true;
+            }
+            free(context);
+            status = 0;
+        } else {
+            ++active_stream_count;
+        }
+    }
+
+    if (status != 0) {
+        ALOGE("something wrong ... <<<--- Found NOTHING to remove ... ???? --->>>>>");
+        pthread_mutex_unlock(&vol_listner_init_lock);
+        return status;
+    }
+
+    // if there are no active streams, reset cal and volume level
+    if (active_stream_count == 0) {
+        current_gain_dep_cal_level = -1;
+        current_vol = 0.0;
+    }
+
+    if (recompute_flag) {
+        check_and_set_gain_dep_cal();
+    }
+
+    if (dumping_enabled) {
+        dump_list_l();
+    }
+    pthread_mutex_unlock(&vol_listner_init_lock);
+    return status;
+}
+
+static int vol_prc_lib_get_descriptor(const effect_uuid_t *uuid,
+                                      effect_descriptor_t *descriptor)
+{
+    int i = 0;
+    ALOGV("%s Called ", __func__);
+    if (lib_init() != 0) {
+        return init_status;
+    }
+
+    if (descriptor == NULL || uuid == NULL) {
+        ALOGE("%s: %s is NULL", __func__, (descriptor == NULL) ? "descriptor" : "uuid");
+        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;
+        }
+    }
+
+    ALOGE("%s: couldnt found uuid passed, oops", __func__);
+    return  -EINVAL;
+}
+
+
+/* effect_handle_t interface implementation for volume listener effect */
+static const struct effect_interface_s effect_interface = {
+    vol_effect_process,
+    vol_effect_command,
+    vol_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 = "Volume Listener Effect Library",
+    .implementor = "Qualcomm Technologies Inc.",
+    .create_effect = vol_prc_lib_create,
+    .release_effect = vol_prc_lib_release,
+    .get_descriptor = vol_prc_lib_get_descriptor,
+};
diff --git a/visualizer/Android.mk b/visualizer/Android.mk
index bec54d6..87d4987 100644
--- a/visualizer/Android.mk
+++ b/visualizer/Android.mk
@@ -27,7 +27,7 @@
 	libdl \
 	libtinyalsa
 
-LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx
+LOCAL_MODULE_RELATIVE_PATH := soundfx
 LOCAL_MODULE:= libqcomvisualizer
 
 LOCAL_C_INCLUDES := \
diff --git a/voice_processing/Android.mk b/voice_processing/Android.mk
index b64c0e3..9b86eaf 100644
--- a/voice_processing/Android.mk
+++ b/voice_processing/Android.mk
@@ -5,7 +5,7 @@
 
 LOCAL_MODULE:= libqcomvoiceprocessing
 LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx
+LOCAL_MODULE_RELATIVE_PATH := soundfx
 
 LOCAL_SRC_FILES:= \
     voice_processing.c
diff --git a/voice_processing/voice_processing.c b/voice_processing/voice_processing.c
index 08acdb4..7d2b592 100644
--- a/voice_processing/voice_processing.c
+++ b/voice_processing/voice_processing.c
@@ -16,6 +16,7 @@
 
 #define LOG_TAG "voice_processing"
 /*#define LOG_NDEBUG 0*/
+#include <stdlib.h>
 #include <dlfcn.h>
 #include <cutils/log.h>
 #include <cutils/list.h>
@@ -559,7 +560,9 @@
             if (pCmdData == NULL ||
                     cmdSize < (int)sizeof(effect_param_t) ||
                     pReplyData == NULL ||
-                    *replySize < (int)sizeof(effect_param_t)) {
+                    *replySize < (int)sizeof(effect_param_t) ||
+                    // constrain memcpy below
+                    ((effect_param_t *)pCmdData)->psize > *replySize - sizeof(effect_param_t)) {
                 ALOGV("fx_command() EFFECT_CMD_GET_PARAM invalid args");
                 return -EINVAL;
             }