Merge "hal: add support for source tracking feature"
diff --git a/Android.mk b/Android.mk
index 36a85f9..6e129a1 100644
--- a/Android.mk
+++ b/Android.mk
@@ -1,4 +1,4 @@
-ifneq ($(filter mpq8092 msm8960 msm8226 msm8x26 msm8610 msm8974 msm8x74 apq8084 msm8916 msm8994 msm8992 msm8909 thulium,$(TARGET_BOARD_PLATFORM)),)
+ifneq ($(filter mpq8092 msm8960 msm8226 msm8x26 msm8610 msm8974 msm8x74 apq8084 msm8916 msm8994 msm8992 msm8909 msm8996 msm8952,$(TARGET_BOARD_PLATFORM)),)
 
 MY_LOCAL_PATH := $(call my-dir)
 
diff --git a/hal/Android.mk b/hal/Android.mk
index 4e1364b..99b6b39 100644
--- a/hal/Android.mk
+++ b/hal/Android.mk
@@ -8,7 +8,7 @@
 
 AUDIO_PLATFORM := $(TARGET_BOARD_PLATFORM)
 
-ifneq ($(filter msm8974 msm8226 msm8610 apq8084 msm8994 msm8992 thulium,$(TARGET_BOARD_PLATFORM)),)
+ifneq ($(filter msm8974 msm8226 msm8610 apq8084 msm8994 msm8992 msm8996,$(TARGET_BOARD_PLATFORM)),)
   # B-family platform uses msm8974 code base
   AUDIO_PLATFORM = msm8974
   MULTIPLE_HW_VARIANTS_ENABLED := true
@@ -27,12 +27,12 @@
 ifneq ($(filter msm8992,$(TARGET_BOARD_PLATFORM)),)
   LOCAL_CFLAGS := -DPLATFORM_MSM8994
 endif
-ifneq ($(filter thulium,$(TARGET_BOARD_PLATFORM)),)
-  LOCAL_CFLAGS := -DPLATFORM_THULIUM
+ifneq ($(filter msm8996,$(TARGET_BOARD_PLATFORM)),)
+  LOCAL_CFLAGS := -DPLATFORM_MSM8996
 endif
 endif
 
-ifneq ($(filter msm8916 msm8909,$(TARGET_BOARD_PLATFORM)),)
+ifneq ($(filter msm8916 msm8909 msm8952,$(TARGET_BOARD_PLATFORM)),)
   AUDIO_PLATFORM = msm8916
   MULTIPLE_HW_VARIANTS_ENABLED := true
   LOCAL_CFLAGS := -DPLATFORM_MSM8916
@@ -52,10 +52,14 @@
 LOCAL_C_INCLUDES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include
 LOCAL_ADDITIONAL_DEPENDENCIES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr
 
-ifneq ($(filter msm8994 msm8992,$(TARGET_BOARD_PLATFORM)),)
+ifeq ($(strip $(AUDIO_FEATURE_ENABLED_HDMI_EDID)),true)
     LOCAL_SRC_FILES += edid.c
 endif
 
+ifeq ($(strip $(AUDIO_USE_LL_AS_PRIMARY_OUTPUT)),true)
+    LOCAL_CFLAGS += -DUSE_LL_AS_PRIMARY_OUTPUT
+endif
+
 ifeq ($(strip $(AUDIO_FEATURE_ENABLED_PCM_OFFLOAD)),true)
     LOCAL_CFLAGS += -DPCM_OFFLOAD_ENABLED
 endif
diff --git a/hal/audio_extn/AudioUtil.c b/hal/audio_extn/AudioUtil.c
deleted file mode 100644
index 45bf55f..0000000
--- a/hal/audio_extn/AudioUtil.c
+++ /dev/null
@@ -1,130 +0,0 @@
-/*Copyright (C) 2014 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-* This file was modified by DTS, Inc. The portions of the
-* code modified by DTS, Inc are copyrighted and
-* licensed separately, as follows:
-*
-*  (C) 2014 DTS, Inc.
-*
-* 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 "AudioUtil"
-//#define LOG_NDEBUG 0
-#include <utils/Log.h>
-#include <stdlib.h>
-
-#include <cutils/properties.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <sound/devdep_params.h>
-#include <sound/asound.h>
-#include "AudioUtil.h"
-
-#define ROUTE_PATH    "/data/data/dts/route"
-#define DEVICE_NODE   "/dev/snd/hwC0D3"
-
-static int32_t mDevices = 0;
-static int32_t mCurrDevice = 0;
-
-void create_route_node(void)
-{
-    char prop[PROPERTY_VALUE_MAX] = "true";
-    int fd;
-    property_get("use.dts_eagle", prop, "0");
-    if (!strncmp("true", prop, sizeof("true")) || atoi(prop)) {
-        ALOGV("create_route_node");
-        if ((fd=open(ROUTE_PATH, O_RDONLY)) < 0) {
-            ALOGV("No File exisit");
-        } else {
-            ALOGV("A file with the same name exist. Remove it before creating it");
-            close(fd);
-            remove(ROUTE_PATH);
-        }
-        if ((fd=creat(ROUTE_PATH, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0) {
-            ALOGE("opening route node failed returned");
-            return;
-        }
-        chmod(ROUTE_PATH, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH);
-        ALOGV("opening route  node successful");
-        close(fd);
-    }
-}
-
-void notify_route_node(int active_device, int devices)
-{
-    char prop[PROPERTY_VALUE_MAX] = "true";
-    char buf[1024];
-    int fd;
-    if ((mCurrDevice == active_device) &&
-        (mDevices == devices)) {
-        ALOGV("nothing to update to route node");
-        return;
-    }
-    mDevices = devices;
-    mCurrDevice = active_device;
-    property_get("use.dts_eagle", prop, "0");
-    if (!strncmp("true", prop, sizeof("true")) || atoi(prop)) {
-        ALOGV("notify active device : %d all_devices : %d", active_device, devices);
-        if ((fd=open(ROUTE_PATH, O_TRUNC|O_WRONLY)) < 0) {
-            ALOGV("Write device to route node failed");
-        } else {
-            ALOGV("Write device to route node successful");
-            snprintf(buf, sizeof(buf), "device=%d;all_devices=%d", active_device, devices);
-            int n = write(fd, buf, strlen(buf));
-            ALOGV("number of bytes written: %d", n);
-            close(fd);
-        }
-        int eaglefd = open(DEVICE_NODE, O_RDWR);
-        int32_t params[2] = {active_device, 1 /*is primary device*/};
-        if (eaglefd > 0) {
-            if(ioctl(eaglefd, DTS_EAGLE_IOCTL_SET_ACTIVE_DEVICE, &params) < 0) {
-                ALOGE("DTS_EAGLE (%s): error sending primary device\n", __func__);
-            }
-            ALOGD("DTS_EAGLE (%s): sent primary device\n", __func__);
-            close(eaglefd);
-        } else {
-            ALOGE("DTS_EAGLE (%s): error opening eagle\n", __func__);
-        }
-    }
-}
-
-void remove_route_node(void)
-{
-    char prop[PROPERTY_VALUE_MAX] = "true";
-    int fd;
-    property_get("use.dts_eagle", prop, "0");
-    if (!strncmp("true", prop, sizeof("true")) || atoi(prop)) {
-        ALOGV("remove_route_node");
-        if ((fd=open(ROUTE_PATH, O_RDONLY)) < 0) {
-            ALOGV("open route  node failed");
-        } else {
-            ALOGV("open route node successful");
-            ALOGV("Remove the file");
-            close(fd);
-            remove(ROUTE_PATH);
-        }
-    }
-}
diff --git a/hal/audio_extn/AudioUtil.h b/hal/audio_extn/AudioUtil.h
deleted file mode 100644
index 02789cc..0000000
--- a/hal/audio_extn/AudioUtil.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*Copyright (C) 2014 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-* This file was modified by DTS, Inc. The portions of the
-* code modified by DTS, Inc are copyrighted and
-* licensed separately, as follows:
-*
-*  (C) 2014 DTS, Inc.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*    http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-
-#ifndef AUDIO_UTIL_H_
-#define AUDIO_UTIL_H_
-
-#ifndef DTS_EAGLE
-#define create_route_node() (0)
-#define notify_route_node(active_device, devices) (0)
-#define remove_route_node() (0)
-#else
-void create_route_node(void);
-void notify_route_node(int active_device, int devices);
-void remove_route_node(void);
-#endif
-
-#endif  //AUDIO_UTIL_H_
diff --git a/hal/audio_extn/audio_extn.c b/hal/audio_extn/audio_extn.c
index fad793d..688e265 100644
--- a/hal/audio_extn/audio_extn.c
+++ b/hal/audio_extn/audio_extn.c
@@ -343,7 +343,7 @@
                                                      int channel_count)
 {
     struct mixer_ctl *ctl;
-    const char *mixer_ctl_name = "Playback Channel Map";
+    const char *mixer_ctl_name = "Playback Device Channel Map";
     int set_values[8] = {0};
     int ret;
     ALOGV("%s channel_count:%d",__func__, channel_count);
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index 643c593..71f2285 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -275,7 +275,7 @@
 #ifndef DTS_EAGLE
 #define audio_extn_dts_eagle_set_parameters(adev, parms)     (0)
 #define audio_extn_dts_eagle_get_parameters(adev, query, reply) (0)
-#define audio_extn_dts_eagle_fade(adev, fade_in) (0)
+#define audio_extn_dts_eagle_fade(adev, fade_in, out) (0)
 #define audio_extn_dts_create_state_notifier_node(stream_out) (0)
 #define audio_extn_dts_notify_playback_state(stream_out, has_video, sample_rate, \
                                     channels, is_playing) (0)
@@ -286,7 +286,7 @@
                                          struct str_parms *parms);
 int audio_extn_dts_eagle_get_parameters(const struct audio_device *adev,
                   struct str_parms *query, struct str_parms *reply);
-int audio_extn_dts_eagle_fade(const struct audio_device *adev, bool fade_in);
+int audio_extn_dts_eagle_fade(const struct audio_device *adev, bool fade_in, const struct stream_out *out);
 void audio_extn_dts_create_state_notifier_node(int stream_out);
 void audio_extn_dts_notify_playback_state(int stream_out, int has_video, int sample_rate,
                                   int channels, int is_playing);
diff --git a/hal/audio_extn/dev_arbi.c b/hal/audio_extn/dev_arbi.c
index d3c01c5..d7ab5ff 100644
--- a/hal/audio_extn/dev_arbi.c
+++ b/hal/audio_extn/dev_arbi.c
@@ -128,7 +128,13 @@
 {
     static snd_aud_dev_mapping_t snd_aud_dev_map[] = {
         {SND_DEVICE_OUT_HANDSET, AUDIO_DEVICE_OUT_EARPIECE},
-        {SND_DEVICE_OUT_VOICE_HANDSET, AUDIO_DEVICE_OUT_EARPIECE}
+        {SND_DEVICE_OUT_VOICE_HANDSET, AUDIO_DEVICE_OUT_EARPIECE},
+        {SND_DEVICE_OUT_SPEAKER, AUDIO_DEVICE_OUT_SPEAKER},
+        {SND_DEVICE_OUT_VOICE_SPEAKER, AUDIO_DEVICE_OUT_SPEAKER},
+        {SND_DEVICE_OUT_HEADPHONES, AUDIO_DEVICE_OUT_WIRED_HEADPHONE},
+        {SND_DEVICE_OUT_VOICE_HEADPHONES, AUDIO_DEVICE_OUT_WIRED_HEADPHONE},
+        {SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES,
+            AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_WIRED_HEADPHONE}
     };
 
     audio_devices_t aud_device = AUDIO_DEVICE_NONE;
diff --git a/hal/audio_extn/dts_eagle.c b/hal/audio_extn/dts_eagle.c
index b4bbb9f..52d7abb 100644
--- a/hal/audio_extn/dts_eagle.c
+++ b/hal/audio_extn/dts_eagle.c
@@ -36,8 +36,8 @@
 #ifdef DTS_EAGLE
 
 #define AUDIO_PARAMETER_KEY_DTS_EAGLE   "DTS_EAGLE"
-#define STATE_NOTIFY_FILE               "/data/data/dts/stream"
-#define FADE_NOTIFY_FILE                "/data/data/dts/fade"
+#define STATE_NOTIFY_FILE               "/data/misc/dts/stream"
+#define FADE_NOTIFY_FILE                "/data/misc/dts/fade"
 #define DTS_EAGLE_KEY                   "DTS_EAGLE"
 #define DEVICE_NODE                     "/dev/snd/hwC0D3"
 #define MAX_LENGTH_OF_INTEGER_IN_STRING 13
@@ -88,22 +88,31 @@
     return -EINVAL;
 }
 
-static int do_DTS_Eagle_params(const struct audio_device *adev, struct dts_eagle_param_desc_alsa *t, bool get) {
+static int do_DTS_Eagle_params(const struct audio_device *adev, struct dts_eagle_param_desc_alsa *t, bool get, const struct stream_out *out) {
     struct listnode *node;
     struct audio_usecase *usecase;
-    int ret = 0, sent = 0;
+    int ret = 0, sent = 0, tret = 0;
 
     ALOGV("DTS_EAGLE_HAL (%s): enter", __func__);
 
-    list_for_each(node, &adev->usecase_list) {
-        usecase = node_to_item(node, struct audio_usecase, list);
-        /* set/get eagle params for offload usecases only */
-        if ((usecase->type == PCM_PLAYBACK) && is_offload_usecase(usecase->id)) {
-            int tret = do_DTS_Eagle_params_stream(usecase->stream.out, t, get);
-            if (tret < 0)
-                ret = tret;
-            else
-                sent = 1;
+    if (out) {
+        /* if valid out stream is given, then send params to this stream only */
+        tret = do_DTS_Eagle_params_stream(out, t, get);
+        if (tret < 0)
+            ret = tret;
+        else
+            sent = 1;
+    } else {
+        list_for_each(node, &adev->usecase_list) {
+            usecase = node_to_item(node, struct audio_usecase, list);
+            /* set/get eagle params for offload usecases only */
+            if ((usecase->type == PCM_PLAYBACK) && is_offload_usecase(usecase->id)) {
+                tret = do_DTS_Eagle_params_stream(usecase->stream.out, t, get);
+                if (tret < 0)
+                    ret = tret;
+                else
+                    sent = 1;
+            }
         }
     }
 
@@ -136,13 +145,21 @@
 }
 
 static void fade_node(bool need_data) {
-    int fd = creat(FADE_NOTIFY_FILE, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH), n = 0;
-    char *str = need_data ? "need" : "have";
-    if (fd < 0) {
-        ALOGE("DTS_EAGLE_HAL (%s): opening fade notifier node failed", __func__);
+    char prop[PROPERTY_VALUE_MAX];
+    property_get("use.dts_eagle", prop, "0");
+    if (strncmp("true", prop, sizeof("true")))
         return;
+    int fd, n = 0;
+    if ((fd = open(FADE_NOTIFY_FILE, O_TRUNC|O_WRONLY)) < 0) {
+        ALOGV("No fade node, create one");
+        fd = creat(FADE_NOTIFY_FILE, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+        if (fd < 0) {
+            ALOGE("DTS_EAGLE_HAL (%s): Creating fade notifier node failed", __func__);
+            return;
+        }
+        chmod(FADE_NOTIFY_FILE, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH);
     }
-    chmod(FADE_NOTIFY_FILE, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH);
+    char *str = need_data ? "need" : "have";
     n = write(fd, str, strlen(str));
     close(fd);
     if (n > 0)
@@ -151,7 +168,7 @@
         ALOGE("DTS_EAGLE_HAL (%s): error writing to fade notifier node", __func__);
 }
 
-int audio_extn_dts_eagle_fade(const struct audio_device *adev, bool fade_in) {
+int audio_extn_dts_eagle_fade(const struct audio_device *adev, bool fade_in, const struct stream_out *out) {
     char prop[PROPERTY_VALUE_MAX];
 
     ALOGV("DTS_EAGLE_HAL (%s): enter with fade %s requested", __func__, fade_in ? "in" : "out");
@@ -165,10 +182,10 @@
 
     if (fade_in) {
         if (fade_in_data)
-            return do_DTS_Eagle_params(adev, fade_in_data, false);
+            return do_DTS_Eagle_params(adev, fade_in_data, false, out);
     } else {
         if (fade_out_data)
-            return do_DTS_Eagle_params(adev, fade_out_data, false);
+            return do_DTS_Eagle_params(adev, fade_out_data, false, out);
     }
     return 0;
 }
@@ -285,7 +302,7 @@
                 ALOGD("DTS_EAGLE_HAL (%s): id: 0x%X, size: %d, offset: %d, device: %d", __func__,
                        (*t)->d.id, (*t)->d.size, (*t)->d.offset, (*t)->d.device);
                 if (!fade_in) {
-                    ret = do_DTS_Eagle_params(adev, *t, false);
+                    ret = do_DTS_Eagle_params(adev, *t, false, NULL);
                     if (ret < 0)
                         ALOGE("DTS_EAGLE_HAL (%s): failed setting params in kernel with error %i", __func__, ret);
                 }
@@ -301,7 +318,6 @@
         if (fade_in > 0 && fade_in_data && fade_out_data)
             fade_node(false);
     }
-
     ALOGV("DTS_EAGLE_HAL (%s): exit", __func__);
 }
 
@@ -367,7 +383,7 @@
                     ALOGE("%s: requested data too large", __func__);
                     return -1;
                 }
-                ret = do_DTS_Eagle_params(adev, t, true);
+                ret = do_DTS_Eagle_params(adev, t, true, NULL);
                 if (ret >= 0) {
                     data = (int*)(params + sizeof(struct dts_eagle_param_desc_alsa));
                     for (i = 0; i < count; i++)
@@ -427,10 +443,9 @@
         chmod(path, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH);
         ALOGV("DTS_EAGLE_NODE_STREAM (%s): opening state notifier node successful", __func__);
         close(fd);
+        if (!fade_in_data || !fade_out_data)
+            fade_node(true);
     }
-
-    if (!fade_in_data || !fade_out_data)
-        fade_node(true);
 }
 
 void audio_extn_dts_notify_playback_state(int stream_out, int has_video, int sample_rate,
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 24852b6..369cf12 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -75,13 +75,19 @@
 
 #define COMPRESS_OFFLOAD_NUM_FRAGMENTS 4
 /* ToDo: Check and update a proper value in msec */
-#define COMPRESS_OFFLOAD_PLAYBACK_LATENCY 96
+#define COMPRESS_OFFLOAD_PLAYBACK_LATENCY 50
 #define COMPRESS_PLAYBACK_VOLUME_MAX 0x2000
 
 #define PROXY_OPEN_RETRY_COUNT           100
 #define PROXY_OPEN_WAIT_TIME             20
 
+#ifdef USE_LL_AS_PRIMARY_OUTPUT
+#define USECASE_AUDIO_PLAYBACK_PRIMARY USECASE_AUDIO_PLAYBACK_LOW_LATENCY
+#define PCM_CONFIG_AUDIO_PLAYBACK_PRIMARY pcm_config_low_latency
+#else
 #define USECASE_AUDIO_PLAYBACK_PRIMARY USECASE_AUDIO_PLAYBACK_DEEP_BUFFER
+#define PCM_CONFIG_AUDIO_PLAYBACK_PRIMARY pcm_config_deep_buffer
+#endif
 
 static unsigned int configured_low_latency_capture_period_size =
         LOW_LATENCY_CAPTURE_PERIOD_SIZE;
@@ -279,7 +285,9 @@
         format == AUDIO_FORMAT_AAC_HE_V2 ||
         format == AUDIO_FORMAT_PCM_16_BIT_OFFLOAD ||
         format == AUDIO_FORMAT_PCM_24_BIT_OFFLOAD ||
-        format == AUDIO_FORMAT_FLAC)
+        format == AUDIO_FORMAT_FLAC ||
+        format == AUDIO_FORMAT_WMA ||
+        format == AUDIO_FORMAT_WMA_PRO)
            return true;
 
     return false;
@@ -302,6 +310,12 @@
     case AUDIO_FORMAT_FLAC:
         id = SND_AUDIOCODEC_FLAC;
         break;
+    case AUDIO_FORMAT_WMA:
+        id = SND_AUDIOCODEC_WMA;
+        break;
+    case AUDIO_FORMAT_WMA_PRO:
+        id = SND_AUDIOCODEC_WMA_PRO;
+        break;
     default:
         ALOGE("%s: Unsupported audio format :%x", __func__, format);
     }
@@ -465,8 +479,10 @@
            adev->snd_dev_ref_cnt[snd_device]--;
            return -EINVAL;
        }
+       audio_extn_dev_arbi_acquire(snd_device);
         if (audio_extn_spkr_prot_start_processing(snd_device)) {
             ALOGE("%s: spkr_start_processing failed", __func__);
+            audio_extn_dev_arbi_release(snd_device);
             return -EINVAL;
         }
     } else {
@@ -532,9 +548,9 @@
             audio_extn_spkr_prot_stop_processing(snd_device);
         } else {
             audio_route_reset_and_update_path(adev->audio_route, device_name);
-            audio_extn_dev_arbi_release(snd_device);
         }
 
+        audio_extn_dev_arbi_release(snd_device);
         audio_extn_sound_trigger_update_device_status(snd_device,
                                         ST_EVENT_SND_DEVICE_FREE);
         audio_extn_listen_update_device_status(snd_device,
@@ -652,6 +668,7 @@
         if (usecase->type != PCM_PLAYBACK &&
                 usecase != uc_info &&
                 usecase->in_snd_device != snd_device &&
+                (usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND) &&
                 (usecase->id != USECASE_AUDIO_SPKR_CALIB_TX)) {
             ALOGV("%s: Usecase (%s) is active on (%s) - disabling ..",
                   __func__, use_case_table[usecase->id],
@@ -1547,6 +1564,14 @@
         if (out->offload_callback)
             compress_nonblock(out->compr, out->non_blocking);
 
+        /* Since small bufs uses blocking writes, a write will be blocked
+           for the default max poll time (20s) in the event of an SSR.
+           Reduce the poll time to observe and deal with SSR faster.
+        */
+        if (out->use_small_bufs) {
+            compress_set_max_poll_wait(out->compr, 1000);
+        }
+
         audio_extn_dts_create_state_notifier_node(out->usecase);
         audio_extn_dts_notify_playback_state(out->usecase, 0, out->sample_rate,
                                              popcount(out->channel_mask),
@@ -1781,6 +1806,52 @@
         }
     }
 
+    if (out->format == AUDIO_FORMAT_WMA || out->format == AUDIO_FORMAT_WMA_PRO) {
+        ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_WMA_FORMAT_TAG, value, sizeof(value));
+        if (ret >= 0) {
+            out->compr_config.codec->format = atoi(value);
+            out->send_new_metadata = 1;
+        }
+        ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_WMA_BLOCK_ALIGN, value, sizeof(value));
+        if (ret >= 0) {
+            out->compr_config.codec->options.wma.super_block_align = atoi(value);
+            out->send_new_metadata = 1;
+        }
+        ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_WMA_BIT_PER_SAMPLE, value, sizeof(value));
+        if (ret >= 0) {
+            out->compr_config.codec->options.wma.bits_per_sample = atoi(value);
+            out->send_new_metadata = 1;
+        }
+        ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_WMA_CHANNEL_MASK, value, sizeof(value));
+        if (ret >= 0) {
+            out->compr_config.codec->options.wma.channelmask = atoi(value);
+            out->send_new_metadata = 1;
+        }
+        ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_WMA_ENCODE_OPTION, value, sizeof(value));
+        if (ret >= 0) {
+            out->compr_config.codec->options.wma.encodeopt = atoi(value);
+            out->send_new_metadata = 1;
+        }
+        ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_WMA_ENCODE_OPTION1, value, sizeof(value));
+        if (ret >= 0) {
+            out->compr_config.codec->options.wma.encodeopt1 = atoi(value);
+            out->send_new_metadata = 1;
+        }
+        ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_WMA_ENCODE_OPTION2, value, sizeof(value));
+        if (ret >= 0) {
+            out->compr_config.codec->options.wma.encodeopt2 = atoi(value);
+            out->send_new_metadata = 1;
+        }
+        ALOGV("WMA params: fmt %x, balgn %x, sr %d, chmsk %x, encop %x, op1 %x, op2 %x",
+              out->compr_config.codec->format,
+              out->compr_config.codec->options.wma.super_block_align,
+              out->compr_config.codec->options.wma.bits_per_sample,
+              out->compr_config.codec->options.wma.channelmask,
+              out->compr_config.codec->options.wma.encodeopt,
+              out->compr_config.codec->options.wma.encodeopt1,
+              out->compr_config.codec->options.wma.encodeopt2);
+    }
+
     ret = str_parms_get_str(parms, AUDIO_OFFLOAD_CODEC_SAMPLE_RATE, value, sizeof(value));
     if(ret >= 0)
         is_meta_data_params = true;
@@ -1957,7 +2028,7 @@
     } else {
         voice_extn_out_get_parameters(out, query, reply);
         str = str_parms_to_str(reply);
-        if (!strncmp(str, "", sizeof(""))) {
+        if (str && !strncmp(str, "", sizeof(""))) {
             free(str);
             str = strdup(keys);
         }
@@ -1996,13 +2067,7 @@
     uint32_t latency = 0;
 
     if (is_offload_usecase(out->usecase)) {
-        if (out->use_small_bufs == true)
-            latency = ((out->compr_config.fragments *
-                   out->compr_config.fragment_size * 1000) /
-                   (out->sample_rate * out->compr_config.codec->ch_in *
-                   audio_bytes_per_sample(out->format)));
-        else
-            latency = COMPRESS_OFFLOAD_PLAYBACK_LATENCY;
+        latency = COMPRESS_OFFLOAD_PLAYBACK_LATENCY;
     } else {
         latency = (out->config.period_count * out->config.period_size * 1000) /
            (out->config.rate);
@@ -2066,6 +2131,11 @@
     pthread_mutex_lock(&out->lock);
 
     if (SND_CARD_STATE_OFFLINE == snd_scard_state) {
+        // increase written size during SSR to avoid mismatch
+        // with the written frames count in AF
+        if (!is_offload_usecase(out->usecase))
+            out->written += bytes / (out->config.channels * sizeof(short));
+
         if (out->pcm) {
             ALOGD(" %s: sound card is not active/SSR state", __func__);
             ret= -EIO;
@@ -2094,7 +2164,7 @@
     }
 
     if (is_offload_usecase(out->usecase)) {
-        ALOGD("copl(%p): writing buffer (%zu bytes) to compress device", out, bytes);
+        ALOGVV("copl(%p): writing buffer (%zu bytes) to compress device", out, bytes);
         if (out->send_new_metadata) {
             ALOGD("copl(%p):send new gapless metadata", out);
             compress_set_gapless_metadata(out->compr, &out->gapless_mdata);
@@ -2117,6 +2187,7 @@
         }
         if (!out->playback_started && ret >= 0) {
             compress_start(out->compr);
+            audio_extn_dts_eagle_fade(adev, true, out);
             out->playback_started = 1;
             out->offload_state = OFFLOAD_STATE_PLAYING;
 
@@ -2308,7 +2379,7 @@
 
             out->offload_state = OFFLOAD_STATE_PAUSED;
 
-            audio_extn_dts_eagle_fade(adev, false);
+            audio_extn_dts_eagle_fade(adev, false, out);
             audio_extn_dts_notify_playback_state(out->usecase, 0,
                                                  out->sample_rate, popcount(out->channel_mask),
                                                  0);
@@ -2336,7 +2407,7 @@
 
             out->offload_state = OFFLOAD_STATE_PLAYING;
 
-            audio_extn_dts_eagle_fade(adev, true);
+            audio_extn_dts_eagle_fade(adev, true, out);
             audio_extn_dts_notify_playback_state(out->usecase, 0, out->sample_rate,
                                                      popcount(out->channel_mask), 1);
         }
@@ -2937,11 +3008,16 @@
         out->usecase = USECASE_AUDIO_PLAYBACK_LOW_LATENCY;
         out->config = pcm_config_low_latency;
         out->sample_rate = out->config.rate;
+    } else if (out->flags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER) {
+        format = AUDIO_FORMAT_PCM_16_BIT;
+        out->usecase = USECASE_AUDIO_PLAYBACK_DEEP_BUFFER;
+        out->config = pcm_config_deep_buffer;
+        out->sample_rate = out->config.rate;
     } else {
         /* primary path is the default path selected if no other outputs are available/suitable */
         format = AUDIO_FORMAT_PCM_16_BIT;
         out->usecase = USECASE_AUDIO_PLAYBACK_PRIMARY;
-        out->config = pcm_config_deep_buffer;
+        out->config = PCM_CONFIG_AUDIO_PLAYBACK_PRIMARY;
         out->sample_rate = out->config.rate;
     }
 
@@ -3063,14 +3139,13 @@
 
 static void close_compress_sessions(struct audio_device *adev)
 {
-    struct stream_out *out = NULL;
-    struct listnode *node = NULL;
-    struct listnode *tmp = NULL;
-    struct audio_usecase *usecase = NULL;
+    struct stream_out *out;
+    struct listnode *node;
+    struct audio_usecase *usecase;
     pthread_mutex_lock(&adev->lock);
-    list_for_each_safe(node, tmp, &adev->usecase_list) {
+    list_for_each(node, &adev->usecase_list) {
         usecase = node_to_item(node, struct audio_usecase, list);
-        if (is_offload_usecase(usecase->id)) {
+        if (usecase && is_offload_usecase(usecase->id)) {
             if (usecase && usecase->stream.out) {
                 ALOGI(" %s closing compress session %d on OFFLINE state", __func__, usecase->id);
                 out = usecase->stream.out;
@@ -3345,7 +3420,7 @@
                                   struct audio_stream_in **stream_in,
                                   audio_input_flags_t flags __unused,
                                   const char *address __unused,
-                                  audio_source_t source __unused)
+                                  audio_source_t source)
 {
     struct audio_device *adev = (struct audio_device *)dev;
     struct stream_in *in;
@@ -3365,8 +3440,8 @@
     }
 
     ALOGD("%s: enter: sample_rate(%d) channel_mask(%#x) devices(%#x)\
-        stream_handle(%p) io_handle(%d)",__func__, config->sample_rate, config->channel_mask,
-        devices, &in->stream, handle);
+        stream_handle(%p) io_handle(%d) source(%d)",__func__, config->sample_rate, config->channel_mask,
+        devices, &in->stream, handle, source);
 
     pthread_mutex_init(&in->lock, (const pthread_mutexattr_t *) NULL);
 
@@ -3387,7 +3462,7 @@
     in->stream.get_input_frames_lost = in_get_input_frames_lost;
 
     in->device = devices;
-    in->source = AUDIO_SOURCE_DEFAULT;
+    in->source = source;
     in->dev = adev;
     in->standby = 1;
     in->channel_mask = config->channel_mask;
@@ -3453,6 +3528,13 @@
                                             channel_count,
                                             is_low_latency);
         in->config.period_size = buffer_size / frame_size;
+        if ((in->source == AUDIO_SOURCE_VOICE_COMMUNICATION) &&
+               (in->dev->mode == AUDIO_MODE_IN_COMMUNICATION) &&
+               (voice_extn_compress_voip_is_format_supported(in->format)) &&
+               (in->config.rate == 8000 || in->config.rate == 16000) &&
+               (audio_channel_count_from_in_mask(in->channel_mask) == 1)) {
+            voice_extn_compress_voip_open_input_stream(in);
+        }
     }
 
     /* This stream could be for sound trigger lab,
@@ -3492,7 +3574,7 @@
     } else
         in_standby(&stream->common);
 
-    if (audio_extn_ssr_get_enabled() && 
+    if (audio_extn_ssr_get_enabled() &&
             (audio_channel_count_from_in_mask(in->channel_mask) == 6)) {
         audio_extn_ssr_deinit();
     }
diff --git a/hal/msm8916/hw_info.c b/hal/msm8916/hw_info.c
index 689e834..5fb5606 100644
--- a/hal/msm8916/hw_info.c
+++ b/hal/msm8916/hw_info.c
@@ -216,8 +216,26 @@
         hw_info->snd_devices = NULL;
         hw_info->num_snd_devices = 0;
         strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn));
+    }  else if (!strcmp(snd_card_name, "msm8952-snd-card")) {
+        strlcpy(hw_info->type, "", sizeof(hw_info->type));
+        strlcpy(hw_info->name, "msm8952", sizeof(hw_info->name));
+        hw_info->snd_devices = NULL;
+        hw_info->num_snd_devices = 0;
+        strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn));
+    } else if (!strcmp(snd_card_name, "msm8952-snd-card-mtp")) {
+        strlcpy(hw_info->type, "", sizeof(hw_info->type));
+        strlcpy(hw_info->name, "msm8952", sizeof(hw_info->name));
+        hw_info->snd_devices = NULL;
+        hw_info->num_snd_devices = 0;
+        strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn));
+    } else if (!strcmp(snd_card_name, "msm8952-tomtom-snd-card")) {
+        strlcpy(hw_info->type, "", sizeof(hw_info->type));
+        strlcpy(hw_info->name, "msm8952", sizeof(hw_info->name));
+        hw_info->snd_devices = NULL;
+        hw_info->num_snd_devices = 0;
+        strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn));
     } else {
-        ALOGW("%s: Not an  8x16/8939/8909 device", __func__);
+        ALOGW("%s: Not an  8x16/8939/8909/8952 device", __func__);
     }
 }
 
@@ -232,7 +250,7 @@
     }
 
     if (strstr(snd_card_name, "msm8x16") || strstr(snd_card_name, "msm8939") ||
-        strstr(snd_card_name, "msm8909")) {
+        strstr(snd_card_name, "msm8909") || strstr(snd_card_name, "msm8952")) {
         ALOGV("8x16 - variant soundcard");
         update_hardware_info_8x16(hw_info, snd_card_name);
     } else {
diff --git a/hal/msm8916/platform.c b/hal/msm8916/platform.c
index c23ee09..1ebff09 100644
--- a/hal/msm8916/platform.c
+++ b/hal/msm8916/platform.c
@@ -33,7 +33,10 @@
 #include "platform.h"
 #include "audio_extn.h"
 #include "voice_extn.h"
+#include "edid.h"
+#include "sound/compress_params.h"
 #include "sound/msmcal-hwdep.h"
+#include <dirent.h>
 #define SOUND_TRIGGER_DEVICE_HANDSET_MONO_LOW_POWER_ACDB_ID (100)
 
 #define MIXER_XML_PATH "/system/etc/mixer_paths.xml"
@@ -49,9 +52,12 @@
 #define MIXER_XML_PATH_SKUL "/system/etc/mixer_paths_skul.xml"
 #define MIXER_XML_PATH_AUXPCM "/system/etc/mixer_paths_auxpcm.xml"
 #define MIXER_XML_PATH_AUXPCM "/system/etc/mixer_paths_auxpcm.xml"
+#define MIXER_XML_PATH_I2S "/system/etc/mixer_paths_i2s.xml"
 #define MIXER_XML_PATH_WCD9306 "/system/etc/mixer_paths_wcd9306.xml"
 #define MIXER_XML_PATH_WCD9330 "/system/etc/mixer_paths_wcd9330.xml"
 #define PLATFORM_INFO_XML_PATH      "/system/etc/audio_platform_info.xml"
+#define PLATFORM_INFO_XML_PATH_I2S  "/system/etc/audio_platform_info_i2s.xml"
+
 #define LIB_ACDB_LOADER "libacdbloader.so"
 #define AUDIO_DATA_BLOCK_MIXER_CTL "HDMI EDID"
 #define CVD_VERSION_MIXER_CTL "CVD Version"
@@ -61,15 +67,23 @@
 #define COMPRESS_OFFLOAD_FRAGMENT_SIZE_FOR_AV_STREAMING (2 * 1024)
 #define COMPRESS_OFFLOAD_FRAGMENT_SIZE (32 * 1024)
 /* Used in calculating fragment size for pcm offload */
-#define PCM_OFFLOAD_BUFFER_DURATION_FOR_AV 2000 /* 2 secs */
-#define PCM_OFFLOAD_BUFFER_DURATION_FOR_AV_STREAMING 100 /* 100 millisecs */
+#define PCM_OFFLOAD_BUFFER_DURATION_FOR_AV 1000 /* 1 sec */
+#define PCM_OFFLOAD_BUFFER_DURATION_FOR_AV_STREAMING 80 /* 80 millisecs */
+#define PCM_OFFLOAD_BUFFER_DURATION_FOR_SMALL_BUFFERS 20 /* 20 millisecs */
+#define PCM_OFFLOAD_BUFFER_DURATION_MAX 1200  /* 1200 millisecs */
 
 /* MAX PCM fragment size cannot be increased  further due
  * to flinger's cblk size of 1mb,and it has to be a multiple of
  * 24 - lcm of channels supported by DSP
  */
 #define MAX_PCM_OFFLOAD_FRAGMENT_SIZE (240 * 1024)
-#define MIN_PCM_OFFLOAD_FRAGMENT_SIZE (32 * 1024)
+#define MIN_PCM_OFFLOAD_FRAGMENT_SIZE (4 * 1024)
+
+/*
+ * Offload buffer size for compress passthrough
+ */
+#define MIN_COMPRESS_PASSTHROUGH_FRAGMENT_SIZE (2 * 1024)
+#define MAX_COMPRESS_PASSTHROUGH_FRAGMENT_SIZE (8 * 1024)
 
 #define ALIGN( num, to ) (((num) + (to-1)) & (~(to-1)))
 /*
@@ -97,13 +111,23 @@
 #define SAMPLE_RATE_8KHZ  8000
 #define SAMPLE_RATE_16KHZ 16000
 
+#define MAX_SET_CAL_BYTE_SIZE 65536
+
 #define AUDIO_PARAMETER_KEY_FLUENCE_TYPE  "fluence"
 #define AUDIO_PARAMETER_KEY_SLOWTALK      "st_enable"
 #define AUDIO_PARAMETER_KEY_HD_VOICE      "hd_voice"
 #define AUDIO_PARAMETER_KEY_VOLUME_BOOST  "volume_boost"
+#define AUDIO_PARAMETER_KEY_AUD_CALDATA   "cal_data"
+#define AUDIO_PARAMETER_KEY_AUD_CALRESULT "cal_result"
+
+
+/* Query external audio device connection status */
+#define AUDIO_PARAMETER_KEY_EXT_AUDIO_DEVICE "ext_audio_device"
+
+#define EVENT_EXTERNAL_SPK_1 "qc_ext_spk_1"
+#define EVENT_EXTERNAL_SPK_2 "qc_ext_spk_2"
+#define EVENT_EXTERNAL_MIC   "qc_ext_mic"
 #define MAX_CAL_NAME 20
-#define APP_TYPE_SYSTEM_SOUNDS 0x00011131
-#define APP_TYPE_GENERAL_RECORDING 0x00011132
 
 char cal_name_info[WCD9XXX_MAX_CAL][MAX_CAL_NAME] = {
         [WCD9XXX_ANC_CAL] = "anc_cal",
@@ -124,15 +148,30 @@
     int length;
 };
 
+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;
+
 /* Audio calibration related functions */
 typedef void (*acdb_deallocate_t)();
-typedef int  (*acdb_init_t)(char *, char *, int);
-typedef void (*acdb_send_audio_cal_t)(int, int, int, int);
+typedef int  (*acdb_init_t)(const char *, char *, int);
+typedef void (*acdb_send_audio_cal_t)(int, int, int , int);
 typedef void (*acdb_send_voice_cal_t)(int, int);
 typedef int (*acdb_reload_vocvoltable_t)(int);
 typedef int  (*acdb_get_default_app_type_t)(void);
 typedef int (*acdb_loader_get_calibration_t)(char *attr, int size, void *data);
 acdb_loader_get_calibration_t acdb_loader_get_calibration;
+typedef int (*acdb_set_audio_cal_t) (void *, void *, uint32_t);
+typedef int (*acdb_get_audio_cal_t) (void *, void *, uint32_t*);
 
 struct platform_data {
     struct audio_device *adev;
@@ -140,18 +179,24 @@
     bool fluence_in_voice_call;
     bool fluence_in_voice_rec;
     bool fluence_in_audio_rec;
+    bool external_spk_1;
+    bool external_spk_2;
+    bool external_mic;
     int  fluence_type;
     char fluence_cap[PROPERTY_VALUE_MAX];
     int  fluence_mode;
     bool slowtalk;
     bool hd_voice;
     bool ec_ref_enabled;
+    bool is_wsa_speaker;
     /* Audio calibration related functions */
     void                       *acdb_handle;
     int                        voice_feature_set;
     acdb_init_t                acdb_init;
     acdb_deallocate_t          acdb_deallocate;
     acdb_send_audio_cal_t      acdb_send_audio_cal;
+    acdb_set_audio_cal_t       acdb_set_audio_cal;
+    acdb_get_audio_cal_t       acdb_get_audio_cal;
     acdb_send_voice_cal_t      acdb_send_voice_cal;
     acdb_reload_vocvoltable_t  acdb_reload_vocvoltable;
     acdb_get_default_app_type_t acdb_get_default_app_type;
@@ -160,9 +205,11 @@
 #endif
     void *hw_info;
     struct csd_data *csd;
+    void *edid_info;
+    bool edid_valid;
 };
 
-static const int pcm_device_table[AUDIO_USECASE_MAX][2] = {
+int pcm_device_table[AUDIO_USECASE_MAX][2] = {
     [USECASE_AUDIO_PLAYBACK_DEEP_BUFFER] = {DEEP_BUFFER_PCM_DEVICE,
                                             DEEP_BUFFER_PCM_DEVICE},
     [USECASE_AUDIO_PLAYBACK_LOW_LATENCY] = {LOWLATENCY_PCM_DEVICE,
@@ -171,6 +218,24 @@
                                         MULTIMEDIA2_PCM_DEVICE},
     [USECASE_AUDIO_PLAYBACK_OFFLOAD] =
                      {PLAYBACK_OFFLOAD_DEVICE, PLAYBACK_OFFLOAD_DEVICE},
+#ifdef MULTIPLE_OFFLOAD_ENABLED
+    [USECASE_AUDIO_PLAYBACK_OFFLOAD2] =
+                     {PLAYBACK_OFFLOAD_DEVICE2, PLAYBACK_OFFLOAD_DEVICE2},
+    [USECASE_AUDIO_PLAYBACK_OFFLOAD3] =
+                     {PLAYBACK_OFFLOAD_DEVICE3, PLAYBACK_OFFLOAD_DEVICE3},
+    [USECASE_AUDIO_PLAYBACK_OFFLOAD4] =
+                     {PLAYBACK_OFFLOAD_DEVICE4, PLAYBACK_OFFLOAD_DEVICE4},
+    [USECASE_AUDIO_PLAYBACK_OFFLOAD5] =
+                     {PLAYBACK_OFFLOAD_DEVICE5, PLAYBACK_OFFLOAD_DEVICE5},
+    [USECASE_AUDIO_PLAYBACK_OFFLOAD6] =
+                     {PLAYBACK_OFFLOAD_DEVICE6, PLAYBACK_OFFLOAD_DEVICE6},
+    [USECASE_AUDIO_PLAYBACK_OFFLOAD7] =
+                     {PLAYBACK_OFFLOAD_DEVICE7, PLAYBACK_OFFLOAD_DEVICE7},
+    [USECASE_AUDIO_PLAYBACK_OFFLOAD8] =
+                     {PLAYBACK_OFFLOAD_DEVICE8, PLAYBACK_OFFLOAD_DEVICE8},
+    [USECASE_AUDIO_PLAYBACK_OFFLOAD9] =
+                     {PLAYBACK_OFFLOAD_DEVICE9, PLAYBACK_OFFLOAD_DEVICE9},
+#endif
     [USECASE_AUDIO_RECORD] = {AUDIO_RECORD_PCM_DEVICE, AUDIO_RECORD_PCM_DEVICE},
     [USECASE_AUDIO_RECORD_COMPRESS] = {COMPRESS_CAPTURE_DEVICE, COMPRESS_CAPTURE_DEVICE},
     [USECASE_AUDIO_RECORD_LOW_LATENCY] = {LOWLATENCY_PCM_DEVICE,
@@ -216,11 +281,17 @@
     /* Playback sound devices */
     [SND_DEVICE_OUT_HANDSET] = "handset",
     [SND_DEVICE_OUT_SPEAKER] = "speaker",
+    [SND_DEVICE_OUT_SPEAKER_EXTERNAL_1] = "speaker-ext-1",
+    [SND_DEVICE_OUT_SPEAKER_EXTERNAL_2] = "speaker-ext-2",
+    [SND_DEVICE_OUT_SPEAKER_WSA] = "wsa-speaker",
     [SND_DEVICE_OUT_SPEAKER_REVERSE] = "speaker-reverse",
     [SND_DEVICE_OUT_HEADPHONES] = "headphones",
     [SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES] = "speaker-and-headphones",
+    [SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_EXTERNAL_1] = "speaker-and-headphones-ext-1",
+    [SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_EXTERNAL_2] = "speaker-and-headphones-ext-2",
     [SND_DEVICE_OUT_VOICE_HANDSET] = "voice-handset",
     [SND_DEVICE_OUT_VOICE_SPEAKER] = "voice-speaker",
+    [SND_DEVICE_OUT_VOICE_SPEAKER_WSA] = "wsa-voice-speaker",
     [SND_DEVICE_OUT_VOICE_HEADPHONES] = "voice-headphones",
     [SND_DEVICE_OUT_HDMI] = "hdmi",
     [SND_DEVICE_OUT_SPEAKER_AND_HDMI] = "speaker-and-hdmi",
@@ -241,6 +312,7 @@
     [SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET] = "speaker-and-anc-headphones",
     [SND_DEVICE_OUT_ANC_HANDSET] = "anc-handset",
     [SND_DEVICE_OUT_SPEAKER_PROTECTED] = "speaker-protected",
+    [SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED] = "voice-speaker-protected",
 #ifdef RECORD_PLAY_CONCURRENCY
     [SND_DEVICE_OUT_VOIP_HANDSET] = "voip-handset",
     [SND_DEVICE_OUT_VOIP_SPEAKER] = "voip-speaker",
@@ -249,6 +321,7 @@
 
     /* Capture sound devices */
     [SND_DEVICE_IN_HANDSET_MIC] = "handset-mic",
+    [SND_DEVICE_IN_HANDSET_MIC_EXTERNAL] = "handset-mic-ext",
     [SND_DEVICE_IN_HANDSET_MIC_AEC] = "handset-mic",
     [SND_DEVICE_IN_HANDSET_MIC_NS] = "handset-mic",
     [SND_DEVICE_IN_HANDSET_MIC_AEC_NS] = "handset-mic",
@@ -304,16 +377,25 @@
     [SND_DEVICE_IN_SPEAKER_QMIC_AEC_NS] = "quad-mic",
 };
 
+// Platform specific backend bit width table
+static int backend_bit_width_table[SND_DEVICE_MAX] = {0};
+
 /* ACDB IDs (audio DSP path configuration IDs) for each sound device */
 static int acdb_device_table[SND_DEVICE_MAX] = {
     [SND_DEVICE_NONE] = -1,
     [SND_DEVICE_OUT_HANDSET] = 7,
     [SND_DEVICE_OUT_SPEAKER] = 14,
+    [SND_DEVICE_OUT_SPEAKER_EXTERNAL_1] = 14,
+    [SND_DEVICE_OUT_SPEAKER_EXTERNAL_2] = 14,
+    [SND_DEVICE_OUT_SPEAKER_WSA] = 135,
     [SND_DEVICE_OUT_SPEAKER_REVERSE] = 14,
     [SND_DEVICE_OUT_HEADPHONES] = 10,
     [SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES] = 10,
+    [SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_EXTERNAL_1] = 10,
+    [SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_EXTERNAL_2] = 10,
     [SND_DEVICE_OUT_VOICE_HANDSET] = 7,
     [SND_DEVICE_OUT_VOICE_SPEAKER] = 14,
+    [SND_DEVICE_OUT_VOICE_SPEAKER_WSA] = 135,
     [SND_DEVICE_OUT_VOICE_HEADPHONES] = 10,
     [SND_DEVICE_OUT_HDMI] = 18,
     [SND_DEVICE_OUT_SPEAKER_AND_HDMI] = 14,
@@ -333,7 +415,8 @@
     [SND_DEVICE_OUT_VOICE_ANC_FB_HEADSET] = 27,
     [SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET] = 26,
     [SND_DEVICE_OUT_ANC_HANDSET] = 103,
-    [SND_DEVICE_OUT_SPEAKER_PROTECTED] = 101,
+    [SND_DEVICE_OUT_SPEAKER_PROTECTED] = 124,
+    [SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED] = 101,
 #ifdef RECORD_PLAY_CONCURRENCY
     [SND_DEVICE_OUT_VOIP_HANDSET] = 133,
     [SND_DEVICE_OUT_VOIP_SPEAKER] = 132,
@@ -341,6 +424,7 @@
 #endif
 
     [SND_DEVICE_IN_HANDSET_MIC] = 4,
+    [SND_DEVICE_IN_HANDSET_MIC_EXTERNAL] = 4,
     [SND_DEVICE_IN_HANDSET_MIC_AEC] = 106,
     [SND_DEVICE_IN_HANDSET_MIC_NS] = 107,
     [SND_DEVICE_IN_HANDSET_MIC_AEC_NS] = 108,
@@ -396,7 +480,7 @@
     [SND_DEVICE_IN_SPEAKER_QMIC_AEC_NS] = 129,
 };
 
-struct snd_device_index {
+struct name_to_index {
     char name[100];
     unsigned int index;
 };
@@ -404,14 +488,20 @@
 #define TO_NAME_INDEX(X)   #X, X
 
 /* Used to get index from parsed sting */
-struct snd_device_index snd_device_name_index[SND_DEVICE_MAX] = {
+static struct name_to_index snd_device_name_index[SND_DEVICE_MAX] = {
     {TO_NAME_INDEX(SND_DEVICE_OUT_HANDSET)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_EXTERNAL_1)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_EXTERNAL_2)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_WSA)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_REVERSE)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_HEADPHONES)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_EXTERNAL_1)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_EXTERNAL_2)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_HANDSET)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_SPEAKER)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_SPEAKER_WSA)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_HEADPHONES)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_HDMI)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_HDMI)},
@@ -432,12 +522,14 @@
     {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_ANC_HANDSET)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_PROTECTED)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED)},
 #ifdef RECORD_PLAY_CONCURRENCY
     {TO_NAME_INDEX(SND_DEVICE_OUT_VOIP_HANDSET)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_VOIP_SPEAKER)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_VOIP_HEADPHONES)},
 #endif
     {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_MIC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_MIC_EXTERNAL)},
     {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_MIC_AEC)},
     {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_MIC_NS)},
     {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_MIC_AEC_NS)},
@@ -482,12 +574,47 @@
     {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_STEREO_DMIC)},
     {TO_NAME_INDEX(SND_DEVICE_IN_CAPTURE_VI_FEEDBACK)},
     {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_FLUENCE_DMIC_AANC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_SPEAKER_DMIC_BROADSIDE)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_DMIC_BROADSIDE)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_DMIC_AEC_BROADSIDE)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_DMIC_NS_BROADSIDE)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_DMIC_AEC_NS_BROADSIDE)},
     {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_QMIC)},
     {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_QMIC_AEC)},
     {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_QMIC_NS)},
     {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_QMIC_AEC_NS)},
 };
 
+static char * backend_table[SND_DEVICE_MAX] = {0};
+
+static struct name_to_index usecase_name_index[AUDIO_USECASE_MAX] = {
+    {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_DEEP_BUFFER)},
+    {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_LOW_LATENCY)},
+    {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_MULTI_CH)},
+    {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_OFFLOAD)},
+#ifdef MULTIPLE_OFFLOAD_ENABLED
+    {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_OFFLOAD2)},
+    {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_OFFLOAD3)},
+    {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_OFFLOAD4)},
+    {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_OFFLOAD5)},
+    {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_OFFLOAD6)},
+    {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_OFFLOAD7)},
+    {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_OFFLOAD8)},
+    {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_OFFLOAD9)},
+#endif
+    {TO_NAME_INDEX(USECASE_AUDIO_RECORD)},
+    {TO_NAME_INDEX(USECASE_AUDIO_RECORD_LOW_LATENCY)},
+    {TO_NAME_INDEX(USECASE_VOICE_CALL)},
+    {TO_NAME_INDEX(USECASE_VOICE2_CALL)},
+    {TO_NAME_INDEX(USECASE_VOLTE_CALL)},
+    {TO_NAME_INDEX(USECASE_QCHAT_CALL)},
+    {TO_NAME_INDEX(USECASE_VOWLAN_CALL)},
+    {TO_NAME_INDEX(USECASE_INCALL_REC_UPLINK)},
+    {TO_NAME_INDEX(USECASE_INCALL_REC_DOWNLINK)},
+    {TO_NAME_INDEX(USECASE_INCALL_REC_UPLINK_AND_DOWNLINK)},
+    {TO_NAME_INDEX(USECASE_AUDIO_HFP_SCO)},
+};
+
 #define NO_COLS 2
 static int msm_be_id_array_len;
 static int (*msm_device_to_be_id)[];
@@ -670,6 +797,21 @@
         msm_device_to_be_id = msm_device_to_be_id_internal_codec;
         msm_be_id_array_len  =
             sizeof(msm_device_to_be_id_external_codec) / sizeof(msm_device_to_be_id_internal_codec[0]);
+    } else if (!strncmp(snd_card_name, "msm8952-snd-card-mtp",
+                 sizeof("msm8952-snd-card-mtpmsm8952-snd-card-mtp"))) {
+        strlcpy(mixer_xml_path, MIXER_XML_PATH_MTP,
+                sizeof(MIXER_XML_PATH_MTP));
+        msm_device_to_be_id = msm_device_to_be_id_internal_codec;
+        msm_be_id_array_len  =
+            sizeof(msm_device_to_be_id_internal_codec) / sizeof(msm_device_to_be_id_internal_codec[0]);
+    }  else if (!strncmp(snd_card_name, "msm8952-tomtom-snd-card",
+                 sizeof("msm8952-tomtom-snd-card"))) {
+        strlcpy(mixer_xml_path, MIXER_XML_PATH_WCD9330,
+                sizeof(MIXER_XML_PATH_WCD9330));
+        msm_device_to_be_id = msm_device_to_be_id_external_codec;
+        msm_be_id_array_len  =
+            sizeof(msm_device_to_be_id_external_codec) / sizeof(msm_device_to_be_id_external_codec[0]);
+
     } else {
         strlcpy(mixer_xml_path, MIXER_XML_PATH,
                 sizeof(MIXER_XML_PATH));
@@ -686,18 +828,17 @@
     struct platform_data *my_data = (struct platform_data *)platform;
     struct audio_device *adev = my_data->adev;
 
-    if (enable) {
-        my_data->ec_ref_enabled = enable;
-        audio_route_apply_and_update_path(adev->audio_route, "echo-reference");
-    } else {
-        if (my_data->ec_ref_enabled) {
-            audio_route_reset_and_update_path(adev->audio_route, "echo-reference");
-            my_data->ec_ref_enabled = enable;
-        } else {
-            ALOGV("EC reference is already disabled : %d", my_data->ec_ref_enabled);
-        }
+    if (my_data->ec_ref_enabled) {
+        my_data->ec_ref_enabled = false;
+        ALOGV("%s: disabling echo-reference", __func__);
+        audio_route_reset_and_update_path(adev->audio_route, "echo-reference");
     }
-    ALOGV("Setting EC Reference: %d", enable);
+
+    if (enable) {
+         my_data->ec_ref_enabled = true;
+         ALOGD("%s: enabling echo-reference", __func__);
+         audio_route_apply_and_update_path(adev->audio_route, "echo-reference");
+    }
 }
 
 static struct csd_data *open_csd_client()
@@ -814,6 +955,8 @@
                   __func__, dlerror());
             goto error;
         }
+
+
         csd->init = (init_t)dlsym(csd->csd_client, "csd_client_init");
 
         if (csd->init == NULL) {
@@ -842,6 +985,38 @@
     }
 }
 
+
+static void set_platform_defaults()
+{
+    int32_t dev;
+    for (dev = 0; dev < SND_DEVICE_MAX; dev++) {
+        backend_table[dev] = NULL;
+    }
+    for (dev = 0; dev < SND_DEVICE_MAX; dev++) {
+        backend_bit_width_table[dev] = 16;
+    }
+
+    // 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_IN_BT_SCO_MIC_WB] = strdup("bt-sco-wb");
+    backend_table[SND_DEVICE_IN_BT_SCO_MIC_NREC] = strdup("bt-sco");
+    backend_table[SND_DEVICE_IN_BT_SCO_MIC_WB_NREC] = strdup("bt-sco-wb");
+    backend_table[SND_DEVICE_OUT_BT_SCO] = strdup("bt-sco");
+    backend_table[SND_DEVICE_OUT_BT_SCO_WB] = strdup("bt-sco-wb");
+    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_VOICE_TX] = strdup("afe-proxy");
+    backend_table[SND_DEVICE_IN_VOICE_RX] = strdup("afe-proxy");
+    backend_table[SND_DEVICE_OUT_AFE_PROXY] = strdup("afe-proxy");
+    backend_table[SND_DEVICE_OUT_USB_HEADSET] = strdup("usb-headphones");
+    backend_table[SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET] =
+        strdup("speaker-and-usb-headphones");
+    backend_table[SND_DEVICE_IN_USB_HEADSET_MIC] = strdup("usb-headset-mic");
+    backend_table[SND_DEVICE_IN_CAPTURE_FM] = strdup("capture-fm");
+    backend_table[SND_DEVICE_OUT_TRANSMISSION_FM] = strdup("transmission-fm");
+}
+
 void get_cvd_version(char *cvd_version, struct audio_device *adev)
 {
     struct mixer_ctl *ctl;
@@ -1036,10 +1211,15 @@
     my_data->fluence_in_voice_call = false;
     my_data->fluence_in_voice_rec = false;
     my_data->fluence_in_audio_rec = false;
+    my_data->external_spk_1 = false;
+    my_data->external_spk_2 = false;
+    my_data->external_mic = false;
     my_data->fluence_type = FLUENCE_NONE;
     my_data->fluence_mode = FLUENCE_ENDFIRE;
     my_data->slowtalk = false;
     my_data->hd_voice = false;
+    my_data->edid_info = NULL;
+    my_data->is_wsa_speaker = false;
 
     property_get("ro.qc.sdk.audio.fluencetype", my_data->fluence_cap, "");
     if (!strncmp("fluencepro", my_data->fluence_cap, sizeof("fluencepro"))) {
@@ -1079,6 +1259,7 @@
     property_get("persist.audio.FFSP.enable", ffspEnable, "");
     if (!strncmp("true", ffspEnable, sizeof("true"))) {
         acdb_device_table[SND_DEVICE_OUT_SPEAKER] = 131;
+        acdb_device_table[SND_DEVICE_OUT_SPEAKER_WSA] = 131;
         acdb_device_table[SND_DEVICE_OUT_SPEAKER_REVERSE] = 131;
         acdb_device_table[SND_DEVICE_OUT_SPEAKER_AND_HDMI] = 131;
         acdb_device_table[SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET] = 131;
@@ -1104,6 +1285,18 @@
             ALOGE("%s: Could not find the symbol acdb_send_audio_cal from %s",
                   __func__, LIB_ACDB_LOADER);
 
+        my_data->acdb_set_audio_cal = (acdb_set_audio_cal_t)dlsym(my_data->acdb_handle,
+                                                    "acdb_loader_set_audio_cal_v2");
+        if (!my_data->acdb_set_audio_cal)
+            ALOGE("%s: Could not find the symbol acdb_set_audio_cal_v2 from %s",
+                  __func__, LIB_ACDB_LOADER);
+
+        my_data->acdb_get_audio_cal = (acdb_get_audio_cal_t)dlsym(my_data->acdb_handle,
+                                                    "acdb_loader_get_audio_cal_v2");
+        if (!my_data->acdb_get_audio_cal)
+            ALOGE("%s: Could not find the symbol acdb_get_audio_cal_v2 from %s",
+                  __func__, LIB_ACDB_LOADER);
+
         my_data->acdb_send_voice_cal = (acdb_send_voice_cal_t)dlsym(my_data->acdb_handle,
                                                     "acdb_loader_send_voice_cal");
         if (!my_data->acdb_send_voice_cal)
@@ -1141,7 +1334,29 @@
     }
     audio_extn_pm_vote();
 
+    // Check if WSA speaker is supported in codec
+    char CodecPeek[1024] = "/sys/kernel/debug/asoc/";
+    DIR *dir;
+    struct dirent *dirent;
+    char file_name[10] = "wsa";
+    strcat(CodecPeek, snd_card_name);
+
+    dir = opendir(CodecPeek);
+    if (dir != NULL) {
+        while (NULL != (dirent = readdir(dir))) {
+            if (strstr (dirent->d_name,file_name))
+            {
+                my_data->is_wsa_speaker = true;
+                break;
+            }
+        }
+        closedir(dir);
+    }
+
 acdb_init_fail:
+
+    set_platform_defaults();
+
     /* Initialize ACDB ID's */
     platform_info_init(PLATFORM_INFO_XML_PATH);
 
@@ -1160,6 +1375,10 @@
     audio_extn_dolby_set_license(adev);
     audio_hwdep_send_cal(my_data);
 
+    /* init audio device arbitration */
+    audio_extn_dev_arbi_init();
+
+    my_data->edid_info = NULL;
     return my_data;
 }
 
@@ -1167,9 +1386,30 @@
 {
     struct platform_data *my_data = (struct platform_data *)platform;
 
+    if (my_data->edid_info) {
+        free(my_data->edid_info);
+        my_data->edid_info = NULL;
+    }
+
     hw_info_deinit(my_data->hw_info);
     close_csd_client(my_data->csd);
 
+    int32_t dev;
+    for (dev = 0; dev < SND_DEVICE_MAX; dev++) {
+        if (backend_table[dev]) {
+            free(backend_table[dev]);
+            backend_table[dev]= NULL;
+        }
+    }
+
+    /* deinit audio device arbitration */
+    audio_extn_dev_arbi_deinit();
+
+    if (my_data->edid_info) {
+        free(my_data->edid_info);
+        my_data->edid_info = NULL;
+    }
+
     free(platform);
     /* deinit usb */
     audio_extn_usb_deinit();
@@ -1202,37 +1442,17 @@
 
 void platform_add_backend_name(char *mixer_path, snd_device_t snd_device)
 {
-    if ((snd_device == SND_DEVICE_IN_BT_SCO_MIC) ||
-         (snd_device == SND_DEVICE_IN_BT_SCO_MIC_NREC))
-        strlcat(mixer_path, " bt-sco", MIXER_PATH_MAX_LENGTH);
-    else if ((snd_device == SND_DEVICE_IN_BT_SCO_MIC_WB) ||
-              (snd_device == SND_DEVICE_IN_BT_SCO_MIC_WB_NREC))
-        strlcat(mixer_path, " bt-sco-wb", MIXER_PATH_MAX_LENGTH);
-    else if(snd_device == SND_DEVICE_OUT_BT_SCO)
-        strlcat(mixer_path, " bt-sco", MIXER_PATH_MAX_LENGTH);
-    else if(snd_device == SND_DEVICE_OUT_BT_SCO_WB)
-        strlcat(mixer_path, " bt-sco-wb", MIXER_PATH_MAX_LENGTH);
-    else if (snd_device == SND_DEVICE_OUT_HDMI)
-        strlcat(mixer_path, " hdmi", MIXER_PATH_MAX_LENGTH);
-    else if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_HDMI)
-        strlcat(mixer_path, " speaker-and-hdmi", MIXER_PATH_MAX_LENGTH);
-    else if (snd_device == SND_DEVICE_OUT_VOICE_TX)
-        strlcat(mixer_path, " afe-proxy", MIXER_PATH_MAX_LENGTH);
-    else if (snd_device == SND_DEVICE_IN_VOICE_RX)
-        strlcat(mixer_path, " afe-proxy", MIXER_PATH_MAX_LENGTH);
-    else if (snd_device == SND_DEVICE_OUT_AFE_PROXY)
-        strlcat(mixer_path, " afe-proxy", MIXER_PATH_MAX_LENGTH);
-    else if (snd_device == SND_DEVICE_OUT_USB_HEADSET)
-        strlcat(mixer_path, " usb-headphones", MIXER_PATH_MAX_LENGTH);
-    else if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET)
-        strlcat(mixer_path, " speaker-and-usb-headphones",
-                MIXER_PATH_MAX_LENGTH);
-    else if (snd_device == SND_DEVICE_IN_USB_HEADSET_MIC)
-        strlcat(mixer_path, " usb-headset-mic", MIXER_PATH_MAX_LENGTH);
-    else if (snd_device == SND_DEVICE_IN_CAPTURE_FM)
-        strlcat(mixer_path, " capture-fm", MIXER_PATH_MAX_LENGTH);
-    else if (snd_device == SND_DEVICE_OUT_TRANSMISSION_FM)
-        strlcat(mixer_path, " transmission-fm", MIXER_PATH_MAX_LENGTH);
+    if ((snd_device < SND_DEVICE_MIN) || (snd_device >= SND_DEVICE_MAX)) {
+        ALOGE("%s: Invalid snd_device = %d", __func__, snd_device);
+        return;
+    }
+
+    const char * suffix = backend_table[snd_device];
+
+    if (suffix != NULL) {
+        strlcat(mixer_path, " ", MIXER_PATH_MAX_LENGTH);
+        strlcat(mixer_path, suffix, MIXER_PATH_MAX_LENGTH);
+    }
 }
 
 int platform_get_pcm_device_id(audio_usecase_t usecase, int device_type)
@@ -1245,25 +1465,36 @@
     return device_id;
 }
 
-int platform_get_snd_device_index(char *snd_device_index_name)
+static int find_index(struct name_to_index * table, int32_t len, const char * name)
 {
     int ret = 0;
-    int i;
+    int32_t i;
 
-    if (snd_device_index_name == NULL) {
-        ALOGE("%s: snd_device_index_name is NULL", __func__);
+    if (table == NULL) {
+        ALOGE("%s: table is NULL", __func__);
         ret = -ENODEV;
         goto done;
     }
 
-    for (i=0; i < SND_DEVICE_MAX; i++) {
-        if(strcmp(snd_device_name_index[i].name, snd_device_index_name) == 0) {
-            ret = snd_device_name_index[i].index;
+    if (name == NULL) {
+        ALOGE("null key");
+        ret = -ENODEV;
+        goto done;
+    }
+
+    for (i=0; i < len; i++) {
+        const char* tn = table[i].name;
+        size_t len = strlen(tn);
+        if (strncmp(tn, name, len) == 0) {
+            if (strlen(name) != len) {
+                continue; // substring
+            }
+            ret = table[i].index;
             goto done;
         }
     }
-    ALOGE("%s: Could not find index for snd_device_index_name = %s",
-            __func__, snd_device_index_name);
+    ALOGE("%s: Could not find index for name = %s",
+            __func__, name);
     ret = -ENODEV;
 done:
     return ret;
@@ -1326,6 +1557,16 @@
     return ret;
 }
 
+int platform_get_snd_device_index(char *device_name)
+{
+    return find_index(snd_device_name_index, SND_DEVICE_MAX, device_name);
+}
+
+int platform_get_usecase_index(const char *usecase_name)
+{
+    return find_index(usecase_name_index, AUDIO_USECASE_MAX, usecase_name);
+}
+
 int platform_set_snd_device_acdb_id(snd_device_t snd_device, unsigned int acdb_id)
 {
     int ret = 0;
@@ -1360,17 +1601,30 @@
     }
     return acdb_device_table[snd_device];
 }
-int platform_set_snd_device_bit_width(snd_device_t snd_device __unused,
-                                      unsigned int bit_width __unused)
+
+int platform_set_snd_device_bit_width(snd_device_t snd_device, unsigned int bit_width)
 {
-    ALOGE("%s: Not implemented", __func__);
-    return -ENOSYS;
+    int ret = 0;
+
+    if ((snd_device < SND_DEVICE_MIN) || (snd_device >= SND_DEVICE_MAX)) {
+        ALOGE("%s: Invalid snd_device = %d",
+            __func__, snd_device);
+        ret = -EINVAL;
+        goto done;
+    }
+
+    backend_bit_width_table[snd_device] = bit_width;
+done:
+    return ret;
 }
 
-int platform_get_snd_device_bit_width(snd_device_t snd_device __unused)
+int platform_get_snd_device_bit_width(snd_device_t snd_device)
 {
-    ALOGE("%s: Not implemented", __func__);
-    return -ENOSYS;
+    if ((snd_device < SND_DEVICE_MIN) || (snd_device >= SND_DEVICE_MAX)) {
+        ALOGE("%s: Invalid snd_device = %d", __func__, snd_device);
+        return DEFAULT_OUTPUT_SAMPLING_RATE;
+    }
+    return backend_bit_width_table[snd_device];
 }
 
 int platform_send_audio_calibration(void *platform, struct audio_usecase *usecase,
@@ -1381,18 +1635,12 @@
     struct audio_device *adev = my_data->adev;
     int snd_device = SND_DEVICE_OUT_SPEAKER;
 
-    if (usecase->type == PCM_PLAYBACK) {
-        snd_device = platform_get_output_snd_device(adev->platform,
-                                            usecase->stream.out->devices);
-        if(usecase->id != USECASE_AUDIO_PLAYBACK_OFFLOAD)
-            app_type = APP_TYPE_SYSTEM_SOUNDS;
-    } else if ((usecase->type == PCM_HFP_CALL) || (usecase->type == PCM_CAPTURE)) {
-        snd_device = platform_get_input_snd_device(adev->platform,
-                                            adev->primary_output->devices);
-        app_type = APP_TYPE_GENERAL_RECORDING;
-    }
+    if (usecase->type == PCM_PLAYBACK)
+        snd_device = usecase->out_snd_device;
+    else if ((usecase->type == PCM_HFP_CALL) || (usecase->type == PCM_CAPTURE))
+        snd_device = usecase->in_snd_device;
+    acdb_dev_id = acdb_device_table[audio_extn_get_spkr_prot_snd_device(snd_device)];
 
-    acdb_dev_id = acdb_device_table[snd_device];
     if (acdb_dev_id < 0) {
         ALOGE("%s: Could not find acdb id for device(%d)",
               __func__, snd_device);
@@ -1428,6 +1676,7 @@
     }
     return ret;
 }
+
 int platform_switch_voice_call_enable_device_config(void *platform,
                                                     snd_device_t out_snd_device,
                                                     snd_device_t in_snd_device)
@@ -1436,25 +1685,31 @@
     int acdb_rx_id, acdb_tx_id;
     int ret = 0;
 
-    acdb_rx_id = acdb_device_table[out_snd_device];
+    if (my_data->csd == NULL)
+        return ret;
+
+    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];
 
-    if (my_data->csd != NULL) {
-        if (acdb_rx_id > 0 && acdb_tx_id > 0) {
-            ret = my_data->csd->enable_device_config(acdb_rx_id, acdb_tx_id);
-            if (ret < 0) {
-                ALOGE("%s: csd_enable_device_config, failed, error %d",
-                      __func__, ret);
-            }
-        } else {
-            ALOGE("%s: Incorrect ACDB IDs (rx: %d tx: %d)", __func__,
-                  acdb_rx_id, acdb_tx_id);
+    if (acdb_rx_id > 0 && acdb_tx_id > 0) {
+        ret = my_data->csd->enable_device_config(acdb_rx_id, acdb_tx_id);
+        if (ret < 0) {
+            ALOGE("%s: csd_enable_device_config, failed, error %d",
+                  __func__, ret);
         }
+    } else {
+        ALOGE("%s: Incorrect ACDB IDs (rx: %d tx: %d)", __func__,
+              acdb_rx_id, acdb_tx_id);
     }
+
     return ret;
 }
 
-
 int platform_switch_voice_call_device_post(void *platform,
                                            snd_device_t out_snd_device,
                                            snd_device_t in_snd_device)
@@ -1465,6 +1720,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];
 
@@ -1486,22 +1745,28 @@
     int acdb_rx_id, acdb_tx_id;
     int ret = 0;
 
-    acdb_rx_id = acdb_device_table[out_snd_device];
+    if (my_data->csd == NULL)
+        return ret;
+
+    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];
 
-    if (my_data->csd != NULL) {
-        if (acdb_rx_id > 0 && acdb_tx_id > 0) {
-            ret = my_data->csd->enable_device(acdb_rx_id, acdb_tx_id,
-                                              my_data->adev->acdb_settings);
-            if (ret < 0) {
-                ALOGE("%s: csd_enable_device, failed, error %d",
-                      __func__, ret);
-            }
-        } else {
-            ALOGE("%s: Incorrect ACDB IDs (rx: %d tx: %d)", __func__,
-                  acdb_rx_id, acdb_tx_id);
+    if (acdb_rx_id > 0 && acdb_tx_id > 0) {
+        ret = my_data->csd->enable_device(acdb_rx_id, acdb_tx_id,
+                                          my_data->adev->acdb_settings);
+        if (ret < 0) {
+            ALOGE("%s: csd_enable_device, failed, error %d", __func__, ret);
         }
+    } else {
+        ALOGE("%s: Incorrect ACDB IDs (rx: %d tx: %d)", __func__,
+              acdb_rx_id, acdb_tx_id);
     }
+
     return ret;
 }
 
@@ -1533,7 +1798,7 @@
     return ret;
 }
 
-int platform_get_sample_rate(void *platform __unused, uint32_t *rate __unused)
+int platform_get_sample_rate(void *platform, uint32_t *rate)
 {
     return 0;
 }
@@ -1565,7 +1830,8 @@
     mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values));
 
     if (my_data->csd != NULL) {
-        ret = my_data->csd->volume(ALL_SESSION_VSID, volume);
+        ret = my_data->csd->volume(ALL_SESSION_VSID, volume,
+                                   DEFAULT_VOLUME_RAMP_DURATION_MS);
         if (ret < 0) {
             ALOGE("%s: csd_volume error %d", __func__, ret);
         }
@@ -1582,7 +1848,7 @@
     int ret = 0;
     uint32_t set_values[ ] = {0,
                               ALL_SESSION_VSID,
-                              DEFAULT_VOLUME_RAMP_DURATION_MS};
+                              DEFAULT_MUTE_RAMP_DURATION_MS};
 
     set_values[0] = state;
     ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
@@ -1595,7 +1861,8 @@
     mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values));
 
     if (my_data->csd != NULL) {
-        ret = my_data->csd->mic_mute(ALL_SESSION_VSID, state);
+        ret = my_data->csd->mic_mute(ALL_SESSION_VSID, state,
+                                     DEFAULT_MUTE_RAMP_DURATION_MS);
         if (ret < 0) {
             ALOGE("%s: csd_mic_mute error %d", __func__, ret);
         }
@@ -1674,11 +1941,20 @@
     if (popcount(devices) == 2) {
         if (devices == (AUDIO_DEVICE_OUT_WIRED_HEADPHONE |
                         AUDIO_DEVICE_OUT_SPEAKER)) {
-            snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES;
+            if (my_data->external_spk_1)
+                snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_EXTERNAL_1;
+            else if (my_data->external_spk_2)
+                snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_EXTERNAL_2;
+            else
+                snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES;
         } else if (devices == (AUDIO_DEVICE_OUT_WIRED_HEADSET |
                                AUDIO_DEVICE_OUT_SPEAKER)) {
             if (audio_extn_get_anc_enabled())
                 snd_device = SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET;
+            else if (my_data->external_spk_1)
+                snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_EXTERNAL_1;
+            else if (my_data->external_spk_2)
+                snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_EXTERNAL_2;
             else
                 snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES;
         } else if (devices == (AUDIO_DEVICE_OUT_AUX_DIGITAL |
@@ -1734,7 +2010,10 @@
             else
                 snd_device = SND_DEVICE_OUT_BT_SCO;
         } else if (devices & AUDIO_DEVICE_OUT_SPEAKER) {
-            snd_device = SND_DEVICE_OUT_VOICE_SPEAKER;
+                if (my_data->is_wsa_speaker)
+                    snd_device = SND_DEVICE_OUT_VOICE_SPEAKER_WSA;
+                else
+                    snd_device = SND_DEVICE_OUT_VOICE_SPEAKER;
         } else if (devices & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET ||
                    devices & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET) {
             snd_device = SND_DEVICE_OUT_USB_HEADSET;
@@ -1787,7 +2066,12 @@
             if (adev->speaker_lr_swap)
                 snd_device = SND_DEVICE_OUT_SPEAKER_REVERSE;
             else
-                snd_device = SND_DEVICE_OUT_SPEAKER;
+            {
+                if (my_data->is_wsa_speaker)
+                    snd_device = SND_DEVICE_OUT_SPEAKER_WSA;
+                else
+                    snd_device = SND_DEVICE_OUT_SPEAKER;
+            }
         }
     } else if (devices & AUDIO_DEVICE_OUT_ALL_SCO) {
         if (adev->bt_wb_speech_enabled)
@@ -1841,7 +2125,23 @@
 
     ALOGV("%s: enter: out_device(%#x) in_device(%#x)",
           __func__, out_device, in_device);
-    if ((out_device != AUDIO_DEVICE_NONE) && (voice_is_in_call(adev) ||
+    if (my_data->external_mic) {
+        if ((out_device != AUDIO_DEVICE_NONE && voice_is_in_call(adev)) ||
+            voice_extn_compress_voip_is_active(adev) || audio_extn_hfp_is_active(adev)) {
+            if (out_device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE ||
+               out_device & AUDIO_DEVICE_OUT_EARPIECE ||
+               out_device & AUDIO_DEVICE_OUT_SPEAKER )
+                snd_device = SND_DEVICE_IN_HANDSET_MIC_EXTERNAL;
+        } else if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC ||
+                   in_device & AUDIO_DEVICE_IN_BACK_MIC) {
+            snd_device = SND_DEVICE_IN_HANDSET_MIC_EXTERNAL;
+        }
+    }
+
+    if (snd_device != AUDIO_DEVICE_NONE)
+        goto exit;
+
+    if ((out_device != AUDIO_DEVICE_NONE) && ((voice_is_in_call(adev)) ||
         voice_extn_compress_voip_is_active(adev) || audio_extn_hfp_is_active(adev))) {
         if ((adev->voice.tty_mode != TTY_MODE_OFF) &&
             !voice_extn_compress_voip_is_active(adev)) {
@@ -1875,6 +2175,7 @@
             } else if (out_device & AUDIO_DEVICE_OUT_EARPIECE &&
                 audio_extn_should_use_handset_anc(channel_count)) {
                 snd_device = SND_DEVICE_IN_AANC_HANDSET_MIC;
+                adev->acdb_settings |= ANC_FLAG;
             } else if (my_data->fluence_type == FLUENCE_NONE ||
                 my_data->fluence_in_voice_call == false) {
                 snd_device = SND_DEVICE_IN_HANDSET_MIC;
@@ -1886,8 +2187,8 @@
             }
         } else if (out_device & AUDIO_DEVICE_OUT_WIRED_HEADSET) {
             snd_device = SND_DEVICE_IN_VOICE_HEADSET_MIC;
-               if (audio_extn_hfp_is_active(adev))
-                   platform_set_echo_reference(adev->platform, true);
+            if (audio_extn_hfp_is_active(adev))
+                platform_set_echo_reference(adev->platform, true);
         } else if (out_device & AUDIO_DEVICE_OUT_ALL_SCO) {
             if (adev->bt_wb_speech_enabled) {
                 if (adev->bluetooth_nrec)
@@ -2057,7 +2358,8 @@
         if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC) {
             if (audio_extn_ssr_get_enabled() && channel_count == 6)
                 snd_device = SND_DEVICE_IN_QUAD_MIC;
-            else if (channel_count == 2)
+            else if (my_data->fluence_type & (FLUENCE_DUAL_MIC | FLUENCE_QUAD_MIC) &&
+                    channel_count == 2)
                 snd_device = SND_DEVICE_IN_HANDSET_STEREO_DMIC;
             else
                 snd_device = SND_DEVICE_IN_HANDSET_MIC;
@@ -2165,54 +2467,27 @@
 
 int platform_edid_get_max_channels(void *platform)
 {
+    int channel_count;
+    int max_channels = 2;
+    int i = 0, ret = 0;
     struct platform_data *my_data = (struct platform_data *)platform;
     struct audio_device *adev = my_data->adev;
-    char block[MAX_SAD_BLOCKS * SAD_BLOCK_SIZE];
-    char *sad = block;
-    int num_audio_blocks;
-    int channel_count;
-    int max_channels = 0;
-    int i, ret, count;
+    edid_audio_info *info = NULL;
+    ret = platform_get_edid_info(platform);
+    info = (edid_audio_info *)my_data->edid_info;
 
-    struct mixer_ctl *ctl;
-
-    ctl = mixer_get_ctl_by_name(adev->mixer, AUDIO_DATA_BLOCK_MIXER_CTL);
-    if (!ctl) {
-        ALOGE("%s: Could not get ctl for mixer cmd - %s",
-              __func__, AUDIO_DATA_BLOCK_MIXER_CTL);
-        return 0;
-    }
-
-    mixer_ctl_update(ctl);
-
-    count = mixer_ctl_get_num_values(ctl);
-
-    /* Read SAD blocks, clamping the maximum size for safety */
-    if (count > (int)sizeof(block))
-        count = (int)sizeof(block);
-
-    ret = mixer_ctl_get_array(ctl, block, count);
-    if (ret != 0) {
-        ALOGE("%s: mixer_ctl_get_array() failed to get EDID info", __func__);
-        return 0;
-    }
-
-    /* Calculate the number of SAD blocks */
-    num_audio_blocks = count / SAD_BLOCK_SIZE;
-
-    for (i = 0; i < num_audio_blocks; i++) {
-        /* Only consider LPCM blocks */
-        if ((sad[0] >> 3) != EDID_FORMAT_LPCM) {
-            sad += 3;
-            continue;
+    if(ret == 0 && info != NULL) {
+        for (i = 0; i < info->audio_blocks && i < MAX_EDID_BLOCKS; i++) {
+            ALOGV("%s:format %d channel %d", __func__,
+                   info->audio_blocks_array[i].format_id,
+                   info->audio_blocks_array[i].channels);
+            if (info->audio_blocks_array[i].format_id == LPCM) {
+                channel_count = info->audio_blocks_array[i].channels;
+                if (channel_count > max_channels) {
+                   max_channels = channel_count;
+                }
+            }
         }
-
-        channel_count = (sad[0] & 0x7) + 1;
-        if (channel_count > max_channels)
-            max_channels = channel_count;
-
-        /* Advance to next block */
-        sad += 3;
     }
 
     return max_channels;
@@ -2273,6 +2548,159 @@
     return ret;
 }
 
+static int update_external_device_status(struct platform_data *my_data,
+                                 char* event_name, bool status)
+{
+    int ret = 0;
+    struct audio_usecase *usecase;
+    struct listnode *node;
+
+    ALOGD("Recieved  external event switch %s", event_name);
+
+    if (!strcmp(event_name, EVENT_EXTERNAL_SPK_1))
+        my_data->external_spk_1 = status;
+    else if (!strcmp(event_name, EVENT_EXTERNAL_SPK_2))
+        my_data->external_spk_2 = status;
+    else if (!strcmp(event_name, EVENT_EXTERNAL_MIC))
+        my_data->external_mic = status;
+    else {
+        ALOGE("The audio event type is not found");
+        return -EINVAL;
+    }
+
+    list_for_each(node, &my_data->adev->usecase_list) {
+        usecase = node_to_item(node, struct audio_usecase, list);
+        select_devices(my_data->adev, usecase->id);
+    }
+
+    return ret;
+}
+
+static int parse_audiocal_cfg(struct str_parms *parms, acdb_audio_cal_cfg_t *cal)
+{
+    int err;
+    unsigned int val;
+    char value[64];
+    int ret = 0;
+
+    if(parms == NULL || cal == NULL)
+        return ret;
+
+    err = str_parms_get_str(parms, "cal_persist", value, sizeof(value));
+    if (err >= 0) {
+        str_parms_del(parms, "cal_persist");
+        cal->persist = (uint32_t) strtoul(value, NULL, 0);
+        ret = ret | 0x1;
+    }
+    err = str_parms_get_str(parms, "cal_apptype", value, sizeof(value));
+    if (err >= 0) {
+        str_parms_del(parms, "cal_apptype");
+        cal->app_type = (uint32_t) strtoul(value, NULL, 0);
+        ret = ret | 0x2;
+    }
+    err = str_parms_get_str(parms, "cal_caltype", value, sizeof(value));
+    if (err >= 0) {
+        str_parms_del(parms, "cal_caltype");
+        cal->cal_type = (uint32_t) strtoul(value, NULL, 0);
+        ret = ret | 0x4;
+    }
+    err = str_parms_get_str(parms, "cal_samplerate", value, sizeof(value));
+    if (err >= 0) {
+        str_parms_del(parms, "cal_samplerate");
+        cal->sampling_rate = (uint32_t) strtoul(value, NULL, 0);
+        ret = ret | 0x8;
+    }
+    err = str_parms_get_str(parms, "cal_devid", value, sizeof(value));
+    if (err >= 0) {
+        str_parms_del(parms, "cal_devid");
+        cal->dev_id = (uint32_t) strtoul(value, NULL, 0);
+        ret = ret | 0x10;
+    }
+    err = str_parms_get_str(parms, "cal_snddevid", value, sizeof(value));
+    if (err >= 0) {
+        str_parms_del(parms, "cal_snddevid");
+        cal->snd_dev_id = (uint32_t) strtoul(value, NULL, 0);
+        ret = ret | 0x20;
+    }
+    err = str_parms_get_str(parms, "cal_topoid", value, sizeof(value));
+    if (err >= 0) {
+        str_parms_del(parms, "cal_topoid");
+        cal->topo_id = (uint32_t) strtoul(value, NULL, 0);
+        ret = ret | 0x40;
+    }
+    err = str_parms_get_str(parms, "cal_moduleid", value, sizeof(value));
+    if (err >= 0) {
+        str_parms_del(parms, "cal_moduleid");
+        cal->module_id = (uint32_t) strtoul(value, NULL, 0);
+        ret = ret | 0x80;
+    }
+    err = str_parms_get_str(parms, "cal_paramid", value, sizeof(value));
+    if (err >= 0) {
+        str_parms_del(parms, "cal_paramid");
+        cal->param_id = (uint32_t) strtoul(value, NULL, 0);
+        ret = ret | 0x100;
+    }
+    return ret;
+}
+
+static void set_audiocal(void *platform, struct str_parms *parms, char *value, int len) {
+    struct platform_data *my_data = (struct platform_data *)platform;
+    acdb_audio_cal_cfg_t cal={0};
+    uint8_t *dptr = NULL;
+    int32_t dlen;
+    int err, ret;
+    if(value == NULL || platform == NULL || parms == NULL) {
+        ALOGE("[%s] received null pointer, failed",__func__);
+        goto done_key_audcal;
+    }
+
+    /* parse audio calibration keys */
+    ret = parse_audiocal_cfg(parms, &cal);
+
+    /* handle audio calibration data now */
+    err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_AUD_CALDATA, value, len);
+    if (err >= 0) {
+        str_parms_del(parms, AUDIO_PARAMETER_KEY_AUD_CALDATA);
+        dlen = strlen(value);
+        if(dlen <= 0) {
+            ALOGE("[%s] null data received",__func__);
+            goto done_key_audcal;
+        }
+        dptr = (uint8_t*) calloc(dlen, sizeof(uint8_t));
+        if(dptr == NULL) {
+            ALOGE("[%s] memory allocation failed for %d",__func__, dlen);
+            goto done_key_audcal;
+        }
+        dlen = b64decode(value, strlen(value), dptr);
+        if(dlen<=0) {
+            ALOGE("[%s] data decoding failed %d", __func__, dlen);
+            goto done_key_audcal;
+        }
+
+        if(cal.dev_id) {
+          if(audio_is_input_device(cal.dev_id)) {
+              cal.snd_dev_id = platform_get_input_snd_device(platform, cal.dev_id);
+          } else {
+              cal.snd_dev_id = platform_get_output_snd_device(platform, cal.dev_id);
+          }
+        }
+        cal.acdb_dev_id = platform_get_snd_device_acdb_id(cal.snd_dev_id);
+        ALOGD("Setting audio calibration for snd_device(%d) acdb_id(%d)",
+                cal.snd_dev_id, cal.acdb_dev_id);
+        if(cal.acdb_dev_id == -EINVAL) {
+            ALOGE("[%s] Invalid acdb_device id %d for snd device id %d",
+                       __func__, cal.acdb_dev_id, cal.snd_dev_id);
+            goto done_key_audcal;
+        }
+        if(my_data->acdb_set_audio_cal) {
+            ret = my_data->acdb_set_audio_cal((void *)&cal, (void*)dptr, dlen);
+        }
+    }
+done_key_audcal:
+    if(dptr != NULL)
+        free(dptr);
+}
+
 int platform_set_parameters(void *platform, struct str_parms *parms)
 {
     struct platform_data *my_data = (struct platform_data *)platform;
@@ -2450,13 +2878,106 @@
     return ret;
 }
 
+static void get_audiocal(void *platform, void *keys, void *pReply) {
+    struct platform_data *my_data = (struct platform_data *)platform;
+    struct str_parms *query = (struct str_parms *)keys;
+    struct str_parms *reply=(struct str_parms *)pReply;
+    acdb_audio_cal_cfg_t cal={0};
+    uint8_t *dptr = NULL;
+    char value[512] = {0};
+    char *rparms=NULL;
+    int ret=0, err;
+    uint32_t param_len;
+
+    if(query==NULL || platform==NULL || reply==NULL) {
+        ALOGE("[%s] received null pointer",__func__);
+        ret=-EINVAL;
+        goto done;
+    }
+    /* parse audiocal configuration keys */
+    ret = parse_audiocal_cfg(query, &cal);
+    if(ret == 0) {
+        /* No calibration keys found */
+        goto done;
+    }
+    err = str_parms_get_str(query, AUDIO_PARAMETER_KEY_AUD_CALDATA, value, sizeof(value));
+    if (err >= 0) {
+        str_parms_del(query, AUDIO_PARAMETER_KEY_AUD_CALDATA);
+    } else {
+        goto done;
+    }
+
+    if(cal.dev_id & AUDIO_DEVICE_BIT_IN) {
+        cal.snd_dev_id = platform_get_input_snd_device(platform, cal.dev_id);
+    } else if(cal.dev_id) {
+        cal.snd_dev_id = platform_get_output_snd_device(platform, cal.dev_id);
+    }
+    cal.acdb_dev_id =  platform_get_snd_device_acdb_id(cal.snd_dev_id);
+    if (cal.acdb_dev_id < 0) {
+        ALOGE("%s: Failed. Could not find acdb id for snd device(%d)",
+              __func__, cal.snd_dev_id);
+        ret = -EINVAL;
+        goto done_key_audcal;
+    }
+    ALOGD("[%s] Getting audio calibration for snd_device(%d) acdb_id(%d)",
+           __func__, cal.snd_dev_id, cal.acdb_dev_id);
+
+    param_len = MAX_SET_CAL_BYTE_SIZE;
+    dptr = (uint8_t*)calloc(param_len, sizeof(uint8_t));
+    if(dptr == NULL) {
+        ALOGE("[%s] Memory allocation failed for length %d",__func__,param_len);
+        ret = -ENOMEM;
+        goto done_key_audcal;
+    }
+    if (my_data->acdb_get_audio_cal != NULL) {
+        ret = my_data->acdb_get_audio_cal((void*)&cal, (void*)dptr, &param_len);
+        if (ret == 0) {
+            int dlen;
+            if(param_len == 0 || param_len == MAX_SET_CAL_BYTE_SIZE) {
+                ret = -EINVAL;
+                goto done_key_audcal;
+            }
+            /* Allocate memory for encoding */
+            rparms = (char*)calloc((param_len*2), sizeof(char));
+            if(rparms == NULL) {
+                ALOGE("[%s] Memory allocation failed for size %d",
+                            __func__, param_len*2);
+                ret = -ENOMEM;
+                goto done_key_audcal;
+            }
+            if(cal.persist==0 && cal.module_id && cal.param_id) {
+                err = b64encode(dptr+12, param_len-12, rparms);
+            } else {
+                err = b64encode(dptr, param_len, rparms);
+            }
+            if(err < 0) {
+                ALOGE("[%s] failed to convert data to string", __func__);
+                ret = -EINVAL;
+                goto done_key_audcal;
+            }
+            str_parms_add_int(reply, AUDIO_PARAMETER_KEY_AUD_CALRESULT, ret);
+            str_parms_add_str(reply, AUDIO_PARAMETER_KEY_AUD_CALDATA, rparms);
+        }
+    }
+done_key_audcal:
+    if(ret != 0) {
+        str_parms_add_int(reply, AUDIO_PARAMETER_KEY_AUD_CALRESULT, ret);
+        str_parms_add_str(reply, AUDIO_PARAMETER_KEY_AUD_CALDATA, "");
+    }
+done:
+    if(dptr != NULL)
+        free(dptr);
+    if(rparms != NULL)
+        free(rparms);
+}
+
 void platform_get_parameters(void *platform,
                             struct str_parms *query,
                             struct str_parms *reply)
 {
     struct platform_data *my_data = (struct platform_data *)platform;
     char *str = NULL;
-    char value[256] = {0};
+    char value[512] = {0};
     int ret;
     char *kv_pairs = NULL;
 
@@ -2486,6 +3007,7 @@
         str_parms_add_str(reply, AUDIO_PARAMETER_KEY_VOLUME_BOOST, value);
     }
 
+    /* Handle audio calibration keys */
     kv_pairs = str_parms_to_str(reply);
     ALOGV("%s: exit: returns - %s", __func__, kv_pairs);
     free(kv_pairs);
@@ -2647,6 +3169,14 @@
         fragment_size =  atoi(value) * 1024;
     }
 
+    // For FLAC use max size since it is loss less, and has sampling rates
+    // upto 192kHZ
+    if (info != NULL && !info->has_video &&
+        info->format == AUDIO_FORMAT_FLAC) {
+       fragment_size = MAX_COMPRESS_OFFLOAD_FRAGMENT_SIZE;
+       ALOGV("FLAC fragment size %d", fragment_size);
+    }
+
     if (info != NULL && info->has_video && info->is_streaming) {
         fragment_size = COMPRESS_OFFLOAD_FRAGMENT_SIZE_FOR_AV_STREAMING;
         ALOGV("%s: offload fragment size reduced for AV streaming to %d",
@@ -2665,103 +3195,749 @@
 
 uint32_t platform_get_pcm_offload_buffer_size(audio_offload_info_t* info)
 {
-    uint32_t fragment_size = MIN_PCM_OFFLOAD_FRAGMENT_SIZE;
+    uint32_t fragment_size = 0;
     uint32_t bits_per_sample = 16;
+    uint32_t pcm_offload_time = PCM_OFFLOAD_BUFFER_DURATION_FOR_SMALL_BUFFERS;
 
     if (info->format == AUDIO_FORMAT_PCM_24_BIT_OFFLOAD) {
         bits_per_sample = 32;
     }
 
-    if (!info->has_video) {
-        fragment_size = MAX_PCM_OFFLOAD_FRAGMENT_SIZE;
-
-    } else if (info->has_video && info->is_streaming) {
-        fragment_size = (PCM_OFFLOAD_BUFFER_DURATION_FOR_AV_STREAMING
-                                     * info->sample_rate
-                                     * bits_per_sample
-                                     * popcount(info->channel_mask))/1000;
-
-    } else if (info->has_video) {
-        fragment_size = (PCM_OFFLOAD_BUFFER_DURATION_FOR_AV
-                                     * info->sample_rate
-                                     * bits_per_sample
-                                     * popcount(info->channel_mask))/1000;
+    if (info->use_small_bufs) {
+        pcm_offload_time = PCM_OFFLOAD_BUFFER_DURATION_FOR_SMALL_BUFFERS;
+    } else {
+        if (!info->has_video) {
+            pcm_offload_time = PCM_OFFLOAD_BUFFER_DURATION_MAX;
+        } else if (info->has_video && info->is_streaming) {
+            pcm_offload_time = PCM_OFFLOAD_BUFFER_DURATION_FOR_AV_STREAMING;
+        } else if (info->has_video) {
+            pcm_offload_time = PCM_OFFLOAD_BUFFER_DURATION_FOR_AV;
+        }
     }
 
-    fragment_size = ALIGN( fragment_size, 1024);
-
+    //duration is set to 20 ms worth of stereo data at 48Khz
+    //with 16 bit per sample, modify this when the channel
+    //configuration is different
+    fragment_size = (pcm_offload_time
+                     * info->sample_rate
+                     * (bits_per_sample >> 3)
+                     * popcount(info->channel_mask))/1000;
+    // align with LCM of 2, 4, 6, 8
+    fragment_size = ALIGN( fragment_size, 24 );
     if(fragment_size < MIN_PCM_OFFLOAD_FRAGMENT_SIZE)
         fragment_size = MIN_PCM_OFFLOAD_FRAGMENT_SIZE;
     else if(fragment_size > MAX_PCM_OFFLOAD_FRAGMENT_SIZE)
         fragment_size = MAX_PCM_OFFLOAD_FRAGMENT_SIZE;
 
-    ALOGV("%s: fragment_size %d", __func__, fragment_size);
+    ALOGI("PCM offload Fragment size to %d bytes", fragment_size);
     return fragment_size;
 }
 
+int platform_set_codec_backend_cfg(struct audio_device* adev,
+                         unsigned int bit_width, unsigned int sample_rate)
+{
+    ALOGV("%s bit width: %d, sample rate: %d", __func__, bit_width, sample_rate);
+
+    int ret = 0;
+    if (bit_width != adev->cur_codec_backend_bit_width) {
+        const char * mixer_ctl_name = "SLIM_0_RX Format";
+        struct  mixer_ctl *ctl;
+        ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
+        if (!ctl) {
+            ALOGE("%s: Could not get ctl for mixer command - %s",
+                    __func__, mixer_ctl_name);
+            return -EINVAL;
+        }
+
+        if (bit_width == 24) {
+                mixer_ctl_set_enum_by_string(ctl, "S24_LE");
+        } else {
+            mixer_ctl_set_enum_by_string(ctl, "S16_LE");
+            sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE;
+        }
+        adev->cur_codec_backend_bit_width = bit_width;
+        ALOGE("Backend bit width is set to %d ", bit_width);
+    }
+
+    /*
+     * Backend sample rate configuration follows:
+     * 16 bit playback - 48khz for streams at any valid sample rate
+     * 24 bit playback - 48khz for stream sample rate less than 48khz
+     * 24 bit playback - 96khz for sample rate range of 48khz to 96khz
+     * 24 bit playback - 192khz for sample rate range of 96khz to 192 khz
+     * Upper limit is inclusive in the sample rate range.
+     */
+    // TODO: This has to be more dynamic based on policy file
+    if (sample_rate != adev->cur_codec_backend_samplerate) {
+            char *rate_str = NULL;
+            const char * mixer_ctl_name = "SLIM_0_RX SampleRate";
+            struct  mixer_ctl *ctl;
+
+            switch (sample_rate) {
+            case 8000:
+            case 11025:
+            case 16000:
+            case 22050:
+            case 32000:
+            case 44100:
+            case 48000:
+                rate_str = "KHZ_48";
+                break;
+            case 64000:
+            case 88200:
+            case 96000:
+                rate_str = "KHZ_96";
+                break;
+            case 176400:
+            case 192000:
+                rate_str = "KHZ_192";
+                break;
+            default:
+                rate_str = "KHZ_48";
+                break;
+            }
+
+            ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
+            if(!ctl) {
+                ALOGE("%s: Could not get ctl for mixer command - %s",
+                    __func__, mixer_ctl_name);
+                return -EINVAL;
+            }
+
+            ALOGV("Set sample rate as rate_str = %s", rate_str);
+            mixer_ctl_set_enum_by_string(ctl, rate_str);
+            adev->cur_codec_backend_samplerate = sample_rate;
+    }
+
+    return ret;
+}
+
+bool platform_check_codec_backend_cfg(struct audio_device* adev,
+                                   struct audio_usecase* usecase __unused,
+                                   unsigned int* new_bit_width,
+                                   unsigned int* new_sample_rate)
+{
+    bool backend_change = false;
+    struct listnode *node;
+    struct stream_out *out = NULL;
+    unsigned int bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
+    unsigned int sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE;
+
+    // For voice calls use default configuration
+    // force routing is not required here, caller will do it anyway
+    if (voice_is_in_call(adev) || adev->mode == AUDIO_MODE_IN_COMMUNICATION) {
+        ALOGW("%s:Use default bw and sr for voice/voip calls ",__func__);
+        bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
+        sample_rate =  CODEC_BACKEND_DEFAULT_SAMPLE_RATE;
+    } else {
+        /*
+         * The backend should be configured at highest bit width and/or
+         * sample rate amongst all playback usecases.
+         * If the selected sample rate and/or bit width differ with
+         * current backend sample rate and/or bit width, then, we set the
+         * backend re-configuration flag.
+         *
+         * Exception: 16 bit playbacks is allowed through 16 bit/48 khz backend only
+         */
+        list_for_each(node, &adev->usecase_list) {
+            struct audio_usecase *curr_usecase;
+            curr_usecase = node_to_item(node, struct audio_usecase, list);
+            if (curr_usecase->type == PCM_PLAYBACK) {
+                struct stream_out *out =
+                           (struct stream_out*) curr_usecase->stream.out;
+                if (out != NULL ) {
+                    ALOGV("Offload playback running bw %d sr %d",
+                              out->bit_width, out->sample_rate);
+                        if (bit_width < out->bit_width)
+                            bit_width = out->bit_width;
+                        if (sample_rate < out->sample_rate)
+                            sample_rate = out->sample_rate;
+                }
+            }
+        }
+    }
+
+    // 16 bit playback on speakers is allowed through 48 khz backend only
+    if (16 == bit_width) {
+        sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE;
+    }
+    // 24 bit playback on speakers is allowed through 48 khz backend only
+    // bit width re-configured based on platform info
+    if ((24 == bit_width) &&
+        (usecase->stream.out->devices & AUDIO_DEVICE_OUT_SPEAKER)) {
+        bit_width = (uint32_t)platform_get_snd_device_bit_width(SND_DEVICE_OUT_SPEAKER);
+        sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE;
+    }
+    // Force routing if the expected bitwdith or samplerate
+    // is not same as current backend comfiguration
+    if ((bit_width != adev->cur_codec_backend_bit_width) ||
+        (sample_rate != adev->cur_codec_backend_samplerate)) {
+        *new_bit_width = bit_width;
+        *new_sample_rate = sample_rate;
+        backend_change = true;
+        ALOGI("%s Codec backend needs to be updated. new bit width: %d new sample rate: %d",
+               __func__, *new_bit_width, *new_sample_rate);
+    }
+
+    return backend_change;
+}
+
+bool platform_check_and_set_codec_backend_cfg(struct audio_device* adev, struct audio_usecase *usecase)
+{
+    ALOGV("platform_check_and_set_codec_backend_cfg usecase = %d",usecase->id );
+
+    unsigned int new_bit_width, old_bit_width;
+    unsigned int new_sample_rate, old_sample_rate;
+
+    new_bit_width = old_bit_width = adev->cur_codec_backend_bit_width;
+    new_sample_rate = old_sample_rate = adev->cur_codec_backend_samplerate;
+
+    ALOGW("Codec backend bitwidth %d, samplerate %d", old_bit_width, old_sample_rate);
+    if (platform_check_codec_backend_cfg(adev, usecase,
+                                      &new_bit_width, &new_sample_rate)) {
+        platform_set_codec_backend_cfg(adev, new_bit_width, new_sample_rate);
+        return true;
+    }
+
+    return false;
+}
+
+int platform_set_snd_device_backend(snd_device_t device, const char *backend)
+{
+    int ret = 0;
+
+    if ((device < SND_DEVICE_MIN) || (device >= SND_DEVICE_MAX)) {
+        ALOGE("%s: Invalid snd_device = %d",
+            __func__, device);
+        ret = -EINVAL;
+        goto done;
+    }
+
+    if (backend_table[device]) {
+        free(backend_table[device]);
+    }
+    backend_table[device] = strdup(backend);
+done:
+    return ret;
+}
+
+int platform_set_usecase_pcm_id(audio_usecase_t usecase, int32_t type, int32_t pcm_id)
+{
+    int ret = 0;
+    if ((usecase <= USECASE_INVALID) || (usecase >= AUDIO_USECASE_MAX)) {
+        ALOGE("%s: invalid usecase case idx %d", __func__, usecase);
+        ret = -EINVAL;
+        goto done;
+    }
+
+    if ((type != 0) && (type != 1)) {
+        ALOGE("%s: invalid usecase type", __func__);
+        ret = -EINVAL;
+    }
+    pcm_device_table[usecase][type] = pcm_id;
+done:
+    return ret;
+}
+
 void platform_get_device_to_be_id_map(int **device_to_be_id, int *length)
 {
      *device_to_be_id = msm_device_to_be_id;
      *length = msm_be_id_array_len;
 }
+int platform_set_stream_channel_map(void *platform, audio_channel_mask_t channel_mask, int snd_id)
+{
+    int ret = 0;
+    int channels = audio_channel_count_from_out_mask(channel_mask);
 
-bool platform_check_24_bit_support() {
+    char channel_map[8];
+    memset(channel_map, 0, sizeof(channel_map));
+    /* Following are all most common standard WAV channel layouts
+       overridden by channel mask if its allowed and different */
+    switch (channels) {
+        case 1:
+            /* AUDIO_CHANNEL_OUT_MONO */
+            channel_map[0] = PCM_CHANNEL_FC;
+            break;
+        case 2:
+            /* AUDIO_CHANNEL_OUT_STEREO */
+            channel_map[0] = PCM_CHANNEL_FL;
+            channel_map[1] = PCM_CHANNEL_FR;
+            break;
+        case 3:
+            /* AUDIO_CHANNEL_OUT_2POINT1 */
+            channel_map[0] = PCM_CHANNEL_FL;
+            channel_map[1] = PCM_CHANNEL_FR;
+            channel_map[2] = PCM_CHANNEL_FC;
+            break;
+        case 4:
+            /* AUDIO_CHANNEL_OUT_QUAD_SIDE */
+            channel_map[0] = PCM_CHANNEL_FL;
+            channel_map[1] = PCM_CHANNEL_FR;
+            channel_map[2] = PCM_CHANNEL_LS;
+            channel_map[3] = PCM_CHANNEL_RS;
+            if (channel_mask == AUDIO_CHANNEL_OUT_QUAD_BACK)
+            {
+                channel_map[2] = PCM_CHANNEL_LB;
+                channel_map[3] = PCM_CHANNEL_RB;
+            }
+            if (channel_mask == AUDIO_CHANNEL_OUT_SURROUND)
+            {
+                channel_map[2] = PCM_CHANNEL_FC;
+                channel_map[3] = PCM_CHANNEL_CS;
+            }
+            break;
+        case 5:
+            /* AUDIO_CHANNEL_OUT_PENTA */
+            channel_map[0] = PCM_CHANNEL_FL;
+            channel_map[1] = PCM_CHANNEL_FR;
+            channel_map[2] = PCM_CHANNEL_FC;
+            channel_map[3] = PCM_CHANNEL_LB;
+            channel_map[4] = PCM_CHANNEL_RB;
+            break;
+        case 6:
+            /* AUDIO_CHANNEL_OUT_5POINT1 */
+            channel_map[0] = PCM_CHANNEL_FL;
+            channel_map[1] = PCM_CHANNEL_FR;
+            channel_map[2] = PCM_CHANNEL_FC;
+            channel_map[3] = PCM_CHANNEL_LFE;
+            channel_map[4] = PCM_CHANNEL_LB;
+            channel_map[5] = PCM_CHANNEL_RB;
+            if (channel_mask == AUDIO_CHANNEL_OUT_5POINT1_SIDE)
+            {
+                channel_map[4] = PCM_CHANNEL_LS;
+                channel_map[5] = PCM_CHANNEL_RS;
+            }
+            break;
+        case 7:
+            /* AUDIO_CHANNEL_OUT_6POINT1 */
+            channel_map[0] = PCM_CHANNEL_FL;
+            channel_map[1] = PCM_CHANNEL_FR;
+            channel_map[2] = PCM_CHANNEL_FC;
+            channel_map[3] = PCM_CHANNEL_LFE;
+            channel_map[4] = PCM_CHANNEL_LB;
+            channel_map[5] = PCM_CHANNEL_RB;
+            channel_map[6] = PCM_CHANNEL_CS;
+            break;
+        case 8:
+            /* AUDIO_CHANNEL_OUT_7POINT1 */
+            channel_map[0] = PCM_CHANNEL_FL;
+            channel_map[1] = PCM_CHANNEL_FR;
+            channel_map[2] = PCM_CHANNEL_FC;
+            channel_map[3] = PCM_CHANNEL_LFE;
+            channel_map[4] = PCM_CHANNEL_LB;
+            channel_map[5] = PCM_CHANNEL_RB;
+            channel_map[6] = PCM_CHANNEL_LS;
+            channel_map[7] = PCM_CHANNEL_RS;
+            break;
+        default:
+            ALOGE("unsupported channels %d for setting channel map", channels);
+            return -1;
+    }
+    ret = platform_set_channel_map(platform, channels, channel_map, snd_id);
+    return ret;
+}
+
+int platform_get_edid_info(void *platform)
+{
+    struct platform_data *my_data = (struct platform_data *)platform;
+    struct audio_device *adev = my_data->adev;
+    char block[MAX_SAD_BLOCKS * SAD_BLOCK_SIZE];
+    char *sad = block;
+    int num_audio_blocks;
+    int channel_count = 2;
+    int i, ret, count;
+
+    struct mixer_ctl *ctl;
+    char edid_data[MAX_SAD_BLOCKS * SAD_BLOCK_SIZE + 1] = {0};
+    edid_audio_info *info;
+
+    if (my_data->edid_valid) {
+        /* use cached edid */
+        return 0;
+    }
+
+    if (my_data->edid_info == NULL) {
+        my_data->edid_info =
+            (struct edid_audio_info *)calloc(1, sizeof(struct edid_audio_info));
+    }
+
+    info = my_data->edid_info;
+
+    ctl = mixer_get_ctl_by_name(adev->mixer, AUDIO_DATA_BLOCK_MIXER_CTL);
+    if (!ctl) {
+        ALOGE("%s: Could not get ctl for mixer cmd - %s",
+              __func__, AUDIO_DATA_BLOCK_MIXER_CTL);
+        goto fail;
+    }
+
+    mixer_ctl_update(ctl);
+
+    count = mixer_ctl_get_num_values(ctl);
+
+    /* Read SAD blocks, clamping the maximum size for safety */
+    if (count > (int)sizeof(block))
+        count = (int)sizeof(block);
+
+    ret = mixer_ctl_get_array(ctl, block, count);
+    if (ret != 0) {
+        ALOGE("%s: mixer_ctl_get_array() failed to get EDID info", __func__);
+        goto fail;
+    }
+    edid_data[0] = count;
+    memcpy(&edid_data[1], block, count);
+
+#ifdef AUDIO_FEATURE_ENABLED_HDMI_EDID
+    if (!edid_get_sink_caps(info, edid_data)) {
+        ALOGE("%s: Failed to get HDMI sink capabilities", __func__);
+        goto fail;
+    }
+    my_data->edid_valid = true;
+    return 0;
+#endif
+fail:
+    if (my_data->edid_info) {
+        free(my_data->edid_info);
+        my_data->edid_info = NULL;
+        my_data->edid_valid = false;
+    }
+    ALOGE("%s: return -EINVAL", __func__);
+    return -EINVAL;
+}
+
+
+int platform_set_channel_allocation(void *platform, int channel_alloc)
+{
+    struct mixer_ctl *ctl;
+    const char *mixer_ctl_name = "HDMI RX CA";
+    int ret;
+    struct platform_data *my_data = (struct platform_data *)platform;
+    struct audio_device *adev = my_data->adev;
+
+    ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
+    if (!ctl) {
+        ALOGE("%s: Could not get ctl for mixer cmd - %s",
+              __func__, mixer_ctl_name);
+        ret = EINVAL;
+    }
+    ALOGD(":%s channel allocation = 0x%x", __func__, channel_alloc);
+    ret = mixer_ctl_set_value(ctl, 0, channel_alloc);
+
+    if (ret < 0) {
+        ALOGE("%s: Could not set ctl, error:%d ", __func__, ret);
+    }
+
+    return ret;
+}
+
+int platform_set_channel_map(void *platform, int ch_count, char *ch_map, int snd_id)
+{
+    struct mixer_ctl *ctl;
+    char mixer_ctl_name[44]; // max length of name is 44 as defined
+    int ret;
+    unsigned int i;
+    int set_values[8] = {0};
+    char device_num[13]; // device number up to 2 digit
+    struct platform_data *my_data = (struct platform_data *)platform;
+    struct audio_device *adev = my_data->adev;
+    ALOGV("%s channel_count:%d",__func__, ch_count);
+    if (NULL == ch_map) {
+        ALOGE("%s: Invalid channel mapping used", __func__);
+        return -EINVAL;
+    }
+    strlcpy(mixer_ctl_name, "Playback Channel Map", sizeof(mixer_ctl_name));
+    if (snd_id >= 0) {
+        snprintf(device_num, sizeof(device_num), "%d", snd_id);
+        strncat(mixer_ctl_name, device_num, 13);
+    }
+
+    ALOGD("%s mixer_ctl_name:%s", __func__, mixer_ctl_name);
+
+    ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
+    if (!ctl) {
+        ALOGE("%s: Could not get ctl for mixer cmd - %s",
+              __func__, mixer_ctl_name);
+        return -EINVAL;
+    }
+    for (i = 0; i< ARRAY_SIZE(set_values); i++) {
+        set_values[i] = ch_map[i];
+    }
+
+    ALOGD("%s: set mapping(%d %d %d %d %d %d %d %d) for channel:%d", __func__,
+        set_values[0], set_values[1], set_values[2], set_values[3], set_values[4],
+        set_values[5], set_values[6], set_values[7], ch_count);
+
+    ret = mixer_ctl_set_array(ctl, set_values, ch_count);
+    if (ret < 0) {
+        ALOGE("%s: Could not set ctl, error:%d ch_count:%d",
+              __func__, ret, ch_count);
+    }
+    return ret;
+}
+
+unsigned char platform_map_to_edid_format(int audio_format)
+{
+    unsigned char format;
+    switch (audio_format & AUDIO_FORMAT_MAIN_MASK) {
+    case AUDIO_FORMAT_AC3:
+        ALOGV("%s: AC3", __func__);
+        format = AC3;
+        break;
+    case AUDIO_FORMAT_AAC:
+        ALOGV("%s:AAC", __func__);
+        format = AAC;
+        break;
+    case AUDIO_FORMAT_E_AC3:
+        ALOGV("%s:E_AC3", __func__);
+        format = DOLBY_DIGITAL_PLUS;
+        break;
+    case AUDIO_FORMAT_PCM_16_BIT:
+    case AUDIO_FORMAT_PCM_16_BIT_OFFLOAD:
+    case AUDIO_FORMAT_PCM_24_BIT_OFFLOAD:
+    default:
+        ALOGV("%s:PCM", __func__);
+        format =  LPCM;
+        break;
+    }
+    return format;
+}
+
+uint32_t platform_get_compress_passthrough_buffer_size(
+                                          audio_offload_info_t* info)
+{
+    uint32_t fragment_size = MIN_COMPRESS_PASSTHROUGH_FRAGMENT_SIZE;
+    if (!info->has_video)
+        fragment_size = MIN_COMPRESS_PASSTHROUGH_FRAGMENT_SIZE;
+
+    return fragment_size;
+}
+
+void platform_reset_edid_info(void *platform) {
+
+    ALOGV("%s:", __func__);
+    struct platform_data *my_data = (struct platform_data *)platform;
+    if (my_data->edid_info) {
+        ALOGV("%s :free edid", __func__);
+        free(my_data->edid_info);
+        my_data->edid_info = NULL;
+    }
+}
+
+bool platform_is_edid_supported_format(void *platform, int format)
+{
+    struct platform_data *my_data = (struct platform_data *)platform;
+    struct audio_device *adev = my_data->adev;
+    edid_audio_info *info = NULL;
+    int num_audio_blocks;
+    int i, ret, count;
+    unsigned char format_id = platform_map_to_edid_format(format);
+
+    ret = platform_get_edid_info(platform);
+    info = (edid_audio_info *)my_data->edid_info;
+    if (ret == 0 && info != NULL) {
+        for (i = 0; i < info->audio_blocks && i < MAX_EDID_BLOCKS; i++) {
+             /*
+              * To check
+              *  is there any special for CONFIG_HDMI_PASSTHROUGH_CONVERT
+              *  & DOLBY_DIGITAL_PLUS
+              */
+            if (info->audio_blocks_array[i].format_id == format_id) {
+                ALOGV("%s:platform_is_edid_supported_format true %x",
+                      __func__, format);
+                return true;
+            }
+        }
+    }
+    ALOGV("%s:platform_is_edid_supported_format false %x",
+           __func__, format);
     return false;
 }
 
-bool platform_check_and_set_codec_backend_cfg(struct audio_device* adev __unused,
-                                              struct audio_usecase *usecase __unused)
-{
-    return false;
-}
+int platform_set_edid_channels_configuration(void *platform, int channels) {
 
-int platform_get_usecase_index(const char * usecase __unused)
-{
-    return -ENOSYS;
-}
+    struct platform_data *my_data = (struct platform_data *)platform;
+    struct audio_device *adev = my_data->adev;
+    edid_audio_info *info = NULL;
+    int num_audio_blocks;
+    int channel_count = 2;
+    int i, ret, count;
+    char default_channelMap[MAX_CHANNELS_SUPPORTED] = {0};
 
-int platform_set_usecase_pcm_id(audio_usecase_t usecase __unused, int32_t type __unused,
-                                int32_t pcm_id __unused)
-{
-    return -ENOSYS;
-}
+    ret = platform_get_edid_info(platform);
+    info = (edid_audio_info *)my_data->edid_info;
+    if(ret == 0 && info != NULL) {
+        if (channels > 2) {
 
-int platform_set_snd_device_backend(snd_device_t snd_device __unused,
-                                    const char * backend __unused)
-{
-    return -ENOSYS;
-}
-int platform_get_edid_info(void *platform __unused)
-{
-   return -ENOSYS;
-}
+            ALOGV("%s:able to get HDMI sink capabilities multi channel playback",
+                   __func__);
+            for (i = 0; i < info->audio_blocks && i < MAX_EDID_BLOCKS; i++) {
+                if (info->audio_blocks_array[i].format_id == LPCM &&
+                      info->audio_blocks_array[i].channels > channel_count &&
+                      info->audio_blocks_array[i].channels <= MAX_HDMI_CHANNEL_CNT) {
+                    channel_count = info->audio_blocks_array[i].channels;
+                }
+            }
+            ALOGV("%s:channel_count:%d", __func__, channel_count);
+            /*
+             * Channel map is set for supported hdmi max channel count even
+             * though the input channel count set on adm is less than or equal to
+             * max supported channel count
+             */
+            platform_set_channel_map(platform, channel_count, info->channel_map, -1);
+            platform_set_channel_allocation(platform, info->channel_allocation);
+        } else {
+            default_channelMap[0] = PCM_CHANNEL_FL;
+            default_channelMap[1] = PCM_CHANNEL_FR;
+            platform_set_channel_map(platform,2,default_channelMap,-1);
+            platform_set_channel_allocation(platform,0);
+        }
+    }
 
-int platform_set_channel_map(void *platform __unused, int ch_count __unused,
-                             char *ch_map __unused, int snd_id __unused)
-{
-    return -ENOSYS;
-}
-
-int platform_set_stream_channel_map(void *platform __unused,
-                                    audio_channel_mask_t channel_mask __unused,
-                                    int snd_id __unused)
-{
-    return -ENOSYS;
-}
-
-int platform_set_edid_channels_configuration(void *platform __unused,
-                                         int channels __unused)
-{
     return 0;
 }
 
-void platform_cache_edid(void * platform __unused)
+void platform_cache_edid(void * platform)
 {
+    platform_get_edid_info(platform);
 }
 
-void platform_invalidate_edid(void * platform __unused)
+void platform_invalidate_edid(void * platform)
 {
+    struct platform_data *my_data = (struct platform_data *)platform;
+    my_data->edid_valid = false;
+    if (my_data->edid_info) {
+        memset(my_data->edid_info, 0, sizeof(struct edid_audio_info));
+    }
 }
+
+int platform_set_mixer_control(struct stream_out *out, const char * mixer_ctl_name,
+                      const char *mixer_val)
+{
+    struct audio_device *adev = out->dev;
+    struct mixer_ctl *ctl = NULL;
+    ALOGD("setting mixer ctl %s with value %s", mixer_ctl_name, mixer_val);
+    ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
+    if (!ctl) {
+        ALOGE("%s: could not get ctl for mixer cmd - %s",
+              __func__, mixer_ctl_name);
+        return -EINVAL;
+    }
+
+    return mixer_ctl_set_enum_by_string(ctl, mixer_val);
+}
+
+int platform_set_hdmi_config(struct stream_out *out)
+{
+    struct listnode *node;
+    struct audio_usecase *usecase;
+    struct audio_device *adev = out->dev;
+    const char *hdmi_format_ctrl = "HDMI RX Format";
+    const char *hdmi_rate_ctrl = "HDMI_RX SampleRate";
+    int sample_rate = out->sample_rate;
+    /*TODO: Add rules and check if this needs to be done.*/
+    if((is_offload_usecase(out->usecase)) &&
+        (out->compr_config.codec->compr_passthr == PASSTHROUGH ||
+        out->compr_config.codec->compr_passthr == PASSTHROUGH_CONVERT)) {
+        /* TODO: can we add mixer control for channels here avoid setting */
+        if ((out->format == AUDIO_FORMAT_E_AC3 ||
+            out->format == AUDIO_FORMAT_E_AC3_JOC) &&
+            (out->compr_config.codec->compr_passthr == PASSTHROUGH))
+            sample_rate = out->sample_rate * 4;
+        ALOGD("%s:HDMI compress format and samplerate %d, sample_rate %d",
+               __func__, out->sample_rate, sample_rate);
+        platform_set_mixer_control(out, hdmi_format_ctrl, "Compr");
+        switch (sample_rate) {
+            case 32000:
+                platform_set_mixer_control(out, hdmi_rate_ctrl, "KHZ_32");
+                break;
+            case 44100:
+                platform_set_mixer_control(out, hdmi_rate_ctrl, "KHZ_44_1");
+                break;
+            case 96000:
+                platform_set_mixer_control(out, hdmi_rate_ctrl, "KHZ_96");
+                break;
+            case 176400:
+                platform_set_mixer_control(out, hdmi_rate_ctrl, "KHZ_176_4");
+                break;
+            case 192000:
+                platform_set_mixer_control(out, hdmi_rate_ctrl, "KHZ_192");
+                break;
+            case 128000:
+                if (out->format != AUDIO_FORMAT_E_AC3) {
+                    platform_set_mixer_control(out, hdmi_rate_ctrl, "KHZ_128");
+                    break;
+                } else
+                   ALOGW("Unsupported sample rate for E_AC3 32K");
+            default:
+            case 48000:
+                platform_set_mixer_control(out, hdmi_rate_ctrl, "KHZ_48");
+                break;
+        }
+    } else {
+        ALOGD("%s: HDMI pcm and samplerate %d", __func__,
+               out->sample_rate);
+        platform_set_mixer_control(out, hdmi_format_ctrl, "LPCM");
+        platform_set_mixer_control(out, hdmi_rate_ctrl, "KHZ_48");
+    }
+
+    /*
+     * Deroute all the playback streams routed to HDMI so that
+     * the back end is deactivated. Note that backend will not
+     * be deactivated if any one stream is connected to it.
+     */
+    list_for_each(node, &adev->usecase_list) {
+        usecase = node_to_item(node, struct audio_usecase, list);
+        ALOGV("%s:disable: usecase type %d, devices 0x%x", __func__,
+               usecase->type, usecase->devices);
+        if (usecase->type == PCM_PLAYBACK &&
+                usecase->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
+            disable_audio_route(adev, usecase);
+        }
+    }
+
+    /*
+     * Enable all the streams disabled above. Now the HDMI backend
+     * will be activated with new channel configuration
+     */
+    list_for_each(node, &adev->usecase_list) {
+        usecase = node_to_item(node, struct audio_usecase, list);
+        ALOGV("%s:enable: usecase type %d, devices 0x%x", __func__,
+               usecase->type, usecase->devices);
+        if (usecase->type == PCM_PLAYBACK &&
+                usecase->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
+            enable_audio_route(adev, usecase);
+        }
+    }
+
+    return 0;
+}
+
+int platform_set_device_params(struct stream_out *out, int param, int value)
+{
+    struct audio_device *adev = out->dev;
+    struct mixer_ctl *ctl;
+    char *mixer_ctl_name = "Device PP Params";
+    int ret = 0;
+    uint32_t set_values[] = {0,0};
+
+    set_values[0] = param;
+    set_values[1] = value;
+
+    ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
+    if (!ctl) {
+        ALOGE("%s: Could not get ctl for mixer cmd - %s",
+              __func__, mixer_ctl_name);
+        ret = -EINVAL;
+        goto end;
+    }
+
+    ALOGV("%s: Setting device pp params param: %d, value %d mixer ctrl:%s",
+          __func__,param, value, mixer_ctl_name);
+    mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values));
+
+end:
+    return ret;
+}
+
 int platform_get_subsys_image_name(char *buf)
 {
     strlcpy(buf, PLATFORM_IMAGE_NAME, sizeof(PLATFORM_IMAGE_NAME));
diff --git a/hal/msm8916/platform.h b/hal/msm8916/platform.h
index 1cdb013..7e87a3c 100644
--- a/hal/msm8916/platform.h
+++ b/hal/msm8916/platform.h
@@ -55,11 +55,17 @@
     SND_DEVICE_OUT_BEGIN = SND_DEVICE_MIN,
     SND_DEVICE_OUT_HANDSET = SND_DEVICE_OUT_BEGIN,
     SND_DEVICE_OUT_SPEAKER,
+    SND_DEVICE_OUT_SPEAKER_EXTERNAL_1,
+    SND_DEVICE_OUT_SPEAKER_EXTERNAL_2,
     SND_DEVICE_OUT_SPEAKER_REVERSE,
+    SND_DEVICE_OUT_SPEAKER_WSA,
     SND_DEVICE_OUT_HEADPHONES,
     SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES,
+    SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_EXTERNAL_1,
+    SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES_EXTERNAL_2,
     SND_DEVICE_OUT_VOICE_HANDSET,
     SND_DEVICE_OUT_VOICE_SPEAKER,
+    SND_DEVICE_OUT_VOICE_SPEAKER_WSA,
     SND_DEVICE_OUT_VOICE_HEADPHONES,
     SND_DEVICE_OUT_HDMI,
     SND_DEVICE_OUT_SPEAKER_AND_HDMI,
@@ -80,6 +86,7 @@
     SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET,
     SND_DEVICE_OUT_ANC_HANDSET,
     SND_DEVICE_OUT_SPEAKER_PROTECTED,
+    SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED,
 #ifdef RECORD_PLAY_CONCURRENCY
     SND_DEVICE_OUT_VOIP_HANDSET,
     SND_DEVICE_OUT_VOIP_SPEAKER,
@@ -94,6 +101,7 @@
     /* Capture devices */
     SND_DEVICE_IN_BEGIN = SND_DEVICE_OUT_END,
     SND_DEVICE_IN_HANDSET_MIC  = SND_DEVICE_IN_BEGIN,
+    SND_DEVICE_IN_HANDSET_MIC_EXTERNAL,
     SND_DEVICE_IN_HANDSET_MIC_AEC,
     SND_DEVICE_IN_HANDSET_MIC_NS,
     SND_DEVICE_IN_HANDSET_MIC_AEC_NS,
@@ -156,7 +164,7 @@
 #define DEFAULT_OUTPUT_SAMPLING_RATE 48000
 
 #define ALL_SESSION_VSID                0xFFFFFFFF
-#define DEFAULT_MUTE_RAMP_DURATION      500
+#define DEFAULT_MUTE_RAMP_DURATION_MS   20
 #define DEFAULT_VOLUME_RAMP_DURATION_MS 20
 #define MIXER_PATH_MAX_LENGTH 100
 
@@ -174,7 +182,7 @@
  * the buffer size of an input/output stream
  */
 #define DEEP_BUFFER_OUTPUT_PERIOD_SIZE 960
-#define DEEP_BUFFER_OUTPUT_PERIOD_COUNT 4
+#define DEEP_BUFFER_OUTPUT_PERIOD_COUNT 5
 #define LOW_LATENCY_OUTPUT_PERIOD_SIZE 240
 #define LOW_LATENCY_OUTPUT_PERIOD_COUNT 2
 
@@ -190,6 +198,10 @@
 #define AUDIO_CAPTURE_PERIOD_DURATION_MSEC 20
 #define AUDIO_CAPTURE_PERIOD_COUNT 2
 
+#define LOW_LATENCY_CAPTURE_SAMPLE_RATE 48000
+#define LOW_LATENCY_CAPTURE_PERIOD_SIZE 240
+#define LOW_LATENCY_CAPTURE_USE_CASE 1
+
 #define DEVICE_NAME_MAX_SIZE 128
 #define HW_INFO_ARRAY_MAX_SIZE 32
 
@@ -207,6 +219,18 @@
 #define SPKR_PROT_CALIB_RX_PCM_DEVICE 5
 #define SPKR_PROT_CALIB_TX_PCM_DEVICE 22
 #define PLAYBACK_OFFLOAD_DEVICE 9
+
+#ifdef MULTIPLE_OFFLOAD_ENABLED
+#define PLAYBACK_OFFLOAD_DEVICE2 17
+#define PLAYBACK_OFFLOAD_DEVICE3 18
+#define PLAYBACK_OFFLOAD_DEVICE4 37
+#define PLAYBACK_OFFLOAD_DEVICE5 38
+#define PLAYBACK_OFFLOAD_DEVICE6 39
+#define PLAYBACK_OFFLOAD_DEVICE7 40
+#define PLAYBACK_OFFLOAD_DEVICE8 41
+#define PLAYBACK_OFFLOAD_DEVICE9 42
+#endif
+
 #define COMPRESS_VOIP_CALL_PCM_DEVICE 3
 
 /* Define macro for Internal FM volume mixer */
@@ -219,7 +243,7 @@
 #define VOICE_CALL_PCM_DEVICE 2
 #define VOICE2_CALL_PCM_DEVICE 13
 #define VOLTE_CALL_PCM_DEVICE 15
-#define QCHAT_CALL_PCM_DEVICE 14
+#define QCHAT_CALL_PCM_DEVICE 26
 #define VOWLAN_CALL_PCM_DEVICE 16
 
 #define AFE_PROXY_PLAYBACK_PCM_DEVICE 7
@@ -232,8 +256,8 @@
 typedef int (*disable_device_t)();
 typedef int (*enable_device_config_t)(int, int);
 typedef int (*enable_device_t)(int, int, uint32_t);
-typedef int (*volume_t)(uint32_t, int);
-typedef int (*mic_mute_t)(uint32_t, int);
+typedef int (*volume_t)(uint32_t, int, uint16_t);
+typedef int (*mic_mute_t)(uint32_t, int, uint16_t);
 typedef int (*slow_talk_t)(uint32_t, uint8_t);
 typedef int (*start_voice_t)(uint32_t);
 typedef int (*stop_voice_t)(uint32_t);
@@ -264,4 +288,16 @@
 
 int platform_get_subsys_image_name (char *buf);
 
+/* HDMI Passthrough defines */
+enum {
+    LEGACY_PCM = 0,
+    PASSTHROUGH,
+    PASSTHROUGH_CONVERT
+};
+/*
+ * ID for setting mute and lateny on the device side
+ * through Device PP Params mixer control.
+ */
+#define DEVICE_PARAM_MUTE_ID    0
+#define DEVICE_PARAM_LATENCY_ID 1
 #endif // QCOM_AUDIO_PLATFORM_H
diff --git a/hal/msm8974/hw_info.c b/hal/msm8974/hw_info.c
index e5ab181..c96d11e 100644
--- a/hal/msm8974/hw_info.c
+++ b/hal/msm8974/hw_info.c
@@ -87,7 +87,7 @@
     SND_DEVICE_IN_HANDSET_MIC,
 };
 
-static const snd_device_t tomtom_thulium_CDP_variant_devices[] = {
+static const snd_device_t tomtom_8996_CDP_variant_devices[] = {
 };
 
 static const snd_device_t tomtom_liquid_variant_devices[] = {
@@ -252,34 +252,34 @@
     }
 }
 
-static void  update_hardware_info_thulium(struct hardware_info *hw_info, const char *snd_card_name)
+static void  update_hardware_info_8996(struct hardware_info *hw_info, const char *snd_card_name)
 {
-    if (!strcmp(snd_card_name, "thulium-tomtom-mtp-snd-card")) {
+    if (!strcmp(snd_card_name, "msm8996-tomtom-mtp-snd-card")) {
         strlcpy(hw_info->type, " mtp", sizeof(hw_info->type));
-        strlcpy(hw_info->name, "thulium", sizeof(hw_info->name));
+        strlcpy(hw_info->name, "msm8996", sizeof(hw_info->name));
         hw_info->snd_devices = NULL;
         hw_info->num_snd_devices = 0;
         strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn));
-    } else if (!strcmp(snd_card_name, "thulium-tomtom-cdp-snd-card")) {
+    } else if (!strcmp(snd_card_name, "msm8996-tomtom-cdp-snd-card")) {
         strlcpy(hw_info->type, " cdp", sizeof(hw_info->type));
-        strlcpy(hw_info->name, "thulium", sizeof(hw_info->name));
-        hw_info->snd_devices = (snd_device_t *)tomtom_thulium_CDP_variant_devices;
-        hw_info->num_snd_devices = ARRAY_SIZE(tomtom_thulium_CDP_variant_devices);
+        strlcpy(hw_info->name, "msm8996", sizeof(hw_info->name));
+        hw_info->snd_devices = (snd_device_t *)tomtom_8996_CDP_variant_devices;
+        hw_info->num_snd_devices = ARRAY_SIZE(tomtom_8996_CDP_variant_devices);
         strlcpy(hw_info->dev_extn, "-cdp", sizeof(hw_info->dev_extn));
-    } else if (!strcmp(snd_card_name, "thulium-tomtom-stp-snd-card")) {
+    } else if (!strcmp(snd_card_name, "msm8996-tomtom-stp-snd-card")) {
         strlcpy(hw_info->type, " stp", sizeof(hw_info->type));
-        strlcpy(hw_info->name, "thulium", sizeof(hw_info->name));
+        strlcpy(hw_info->name, "msm8996", sizeof(hw_info->name));
         hw_info->snd_devices = (snd_device_t *)tomtom_stp_variant_devices;
         hw_info->num_snd_devices = ARRAY_SIZE(tomtom_stp_variant_devices);
         strlcpy(hw_info->dev_extn, "-stp", sizeof(hw_info->dev_extn));
-    } else if (!strcmp(snd_card_name, "thulium-tomtom-liquid-snd-card")) {
+    } else if (!strcmp(snd_card_name, "msm8996-tomtom-liquid-snd-card")) {
         strlcpy(hw_info->type, " liquid", sizeof(hw_info->type));
-        strlcpy(hw_info->name, "thulium", sizeof(hw_info->name));
+        strlcpy(hw_info->name, "msm8996", sizeof(hw_info->name));
         hw_info->snd_devices = (snd_device_t *)tomtom_liquid_variant_devices;
         hw_info->num_snd_devices = ARRAY_SIZE(tomtom_liquid_variant_devices);
         strlcpy(hw_info->dev_extn, "-liquid", sizeof(hw_info->dev_extn));
     } else {
-        ALOGW("%s: Not a thulium device", __func__);
+        ALOGW("%s: Not a 8996 device", __func__);
     }
 }
 
@@ -408,9 +408,9 @@
     } else if(strstr(snd_card_name, "msm8994")) {
         ALOGV("8994 - variant soundcard");
         update_hardware_info_8994(hw_info, snd_card_name);
-    } else if(strstr(snd_card_name, "thulium")) {
-        ALOGV("thulium - variant soundcard");
-        update_hardware_info_thulium(hw_info, snd_card_name);
+    } else if(strstr(snd_card_name, "msm8996")) {
+        ALOGV("8996 - variant soundcard");
+        update_hardware_info_8996(hw_info, snd_card_name);
     } else {
         ALOGE("%s: Unsupported target %s:",__func__, snd_card_name);
         free(hw_info);
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index 635bcb7..dcabaf8 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -79,7 +79,8 @@
 #define MIN_COMPRESS_PASSTHROUGH_FRAGMENT_SIZE (2 * 1024)
 #define MAX_COMPRESS_PASSTHROUGH_FRAGMENT_SIZE (8 * 1024)
 
-#define ALIGN( num, to ) (((num) + (to-1)) & (~(to-1)))
+#define DIV_ROUND_UP(x, y) (((x) + (y) - 1)/(y))
+#define ALIGN(x, y) ((y) * DIV_ROUND_UP((x), (y)))
 /*
  * This file will have a maximum of 38 bytes:
  *
@@ -2489,6 +2490,12 @@
     int ret = 0, err;
     char *kv_pairs = str_parms_to_str(parms);
 
+    if(kv_pairs == NULL) {
+        ret = -ENOMEM;
+        ALOGE("[%s] key-value pair is NULL",__func__);
+        goto done;
+    }
+
     ALOGV_IF(kv_pairs != NULL, "%s: enter: %s", __func__, kv_pairs);
 
     len = strlen(kv_pairs);
@@ -2568,7 +2575,8 @@
 
 done:
     ALOGV("%s: exit with code(%d)", __func__, ret);
-    free(kv_pairs);
+    if(kv_pairs != NULL)
+        free(kv_pairs);
     if(value != NULL)
         free(value);
     return ret;
@@ -2935,8 +2943,11 @@
                      * info->sample_rate
                      * (bits_per_sample >> 3)
                      * popcount(info->channel_mask))/1000;
-    // align with LCM of 2, 4, 6, 8
-    fragment_size = ALIGN( fragment_size, 24 );
+    // To have same PCM samples for all channels, the buffer size requires to
+    // be multiple of (number of channels * bytes per sample)
+    // For writes to succeed, the buffer must be written at address which is multiple of 32
+    // Alignment of 96 satsfies both of the above requirements
+    fragment_size = ALIGN(fragment_size, 96);
     if(fragment_size < MIN_PCM_OFFLOAD_FRAGMENT_SIZE)
         fragment_size = MIN_PCM_OFFLOAD_FRAGMENT_SIZE;
     else if(fragment_size > MAX_PCM_OFFLOAD_FRAGMENT_SIZE)
@@ -3355,10 +3366,15 @@
         ALOGE("%s: Invalid channel mapping used", __func__);
         return -EINVAL;
     }
-    strlcpy(mixer_ctl_name, "Playback Channel Map", sizeof(mixer_ctl_name));
+
+    /*
+     * If snd_id is greater than 0, stream channel mapping
+     * If snd_id is below 0, typically -1, device channel mapping
+     */
     if (snd_id >= 0) {
-        snprintf(device_num, sizeof(device_num), "%d", snd_id);
-        strncat(mixer_ctl_name, device_num, 13);
+        snprintf(mixer_ctl_name, sizeof(mixer_ctl_name), "Playback Channel Map%d", snd_id);
+    } else {
+        strlcpy(mixer_ctl_name, "Playback Device Channel Map", sizeof(mixer_ctl_name));
     }
 
     ALOGD("%s mixer_ctl_name:%s", __func__, mixer_ctl_name);
diff --git a/hal/msm8974/platform.h b/hal/msm8974/platform.h
index 552dd41..42725fb 100644
--- a/hal/msm8974/platform.h
+++ b/hal/msm8974/platform.h
@@ -229,7 +229,7 @@
 #define PLAYBACK_OFFLOAD_DEVICE8 38
 #define PLAYBACK_OFFLOAD_DEVICE9 39
 #endif
-#if defined (PLATFORM_MSM8994) || defined (PLATFORM_THULIUM)
+#if defined (PLATFORM_MSM8994) || defined (PLATFORM_MSM8996)
 #define PLAYBACK_OFFLOAD_DEVICE2 17
 #define PLAYBACK_OFFLOAD_DEVICE3 18
 #define PLAYBACK_OFFLOAD_DEVICE4 37
@@ -303,7 +303,7 @@
 #define FM_RX_VOLUME "Quat MI2S FM RX Volume"
 #elif PLATFORM_MSM8994
 #define FM_RX_VOLUME "PRI MI2S LOOPBACK Volume"
-#elif PLATFORM_THULIUM
+#elif PLATFORM_MSM8996
 #define FM_RX_VOLUME "Tert MI2S LOOPBACK Volume"
 #else
 #define FM_RX_VOLUME "Internal FM RX Volume"
diff --git a/hal/voice.c b/hal/voice.c
index 5bc0a4e..9fc1081 100644
--- a/hal/voice.c
+++ b/hal/voice.c
@@ -157,6 +157,8 @@
     }
     ALOGD("voice_config.rate %d\n", voice_config.rate);
 
+    voice_set_mic_mute(adev, adev->voice.mic_mute);
+
     ALOGV("%s: Opening PCM playback device card_id(%d) device_id(%d)",
           __func__, adev->snd_card, pcm_dev_rx_id);
     session->pcm_rx = pcm_open(adev->snd_card,
@@ -293,6 +295,18 @@
                                                        session_id, rec_mode);
         ALOGV("%s: Update usecase to %d",__func__, in->usecase);
     } else {
+        /*
+         * Reject the recording instances, where the recording is started
+         * with In-call voice recording source types but voice call is not
+         * active by the time input is started
+         */
+        if ((in->source == AUDIO_SOURCE_VOICE_UPLINK) ||
+            (in->source == AUDIO_SOURCE_VOICE_DOWNLINK) ||
+            (in->source == AUDIO_SOURCE_VOICE_CALL)) {
+            ret = -EINVAL;
+            ALOGE("%s: As voice call is not active, Incall rec usecase can't be \
+                   selected for requested source:%d",__func__, in->source);
+        }
         ALOGV("%s: voice call not active", __func__);
     }
 
diff --git a/hal/voice_extn/compress_voip.c b/hal/voice_extn/compress_voip.c
index 26636db..14af6fc 100644
--- a/hal/voice_extn/compress_voip.c
+++ b/hal/voice_extn/compress_voip.c
@@ -328,6 +328,7 @@
     int i, ret = 0;
     struct audio_usecase *uc_info;
     int pcm_dev_rx_id, pcm_dev_tx_id;
+    unsigned int flags = PCM_OUT | PCM_MONOTONIC;
 
     ALOGD("%s: enter", __func__);
 
@@ -368,7 +369,7 @@
               __func__, adev->snd_card, pcm_dev_rx_id);
         voip_data.pcm_rx = pcm_open(adev->snd_card,
                                     pcm_dev_rx_id,
-                                    PCM_OUT, voip_config);
+                                    flags, voip_config);
         if (voip_data.pcm_rx && !pcm_is_ready(voip_data.pcm_rx)) {
             ALOGE("%s: %s", __func__, pcm_get_error(voip_data.pcm_rx));
             pcm_close(voip_data.pcm_rx);
@@ -696,6 +697,10 @@
         voip_data.sample_rate = in->config.rate;
     }
 
+    ret = voip_set_mode(in->dev, in->format);
+    if (ret < 0)
+        goto done;
+
     in->usecase = USECASE_COMPRESS_VOIP_CALL;
     if (in->config.rate == 16000)
         in->config = pcm_config_voip_wb;
@@ -703,7 +708,6 @@
         in->config = pcm_config_voip_nb;
 
     voip_data.in_stream_count++;
-    ret = voip_set_mode(in->dev, in->format);
 
 done:
     ALOGV("%s: exit, ret=%d", __func__, ret);
diff --git a/post_proc/Android.mk b/post_proc/Android.mk
index 880838a..be70166 100644
--- a/post_proc/Android.mk
+++ b/post_proc/Android.mk
@@ -26,7 +26,8 @@
 LOCAL_SHARED_LIBRARIES := \
 	libcutils \
 	liblog \
-	libtinyalsa
+	libtinyalsa \
+	libdl
 
 LOCAL_MODULE_TAGS := optional
 
diff --git a/post_proc/bass_boost.c b/post_proc/bass_boost.c
index 57cf8ef..ad1e7c9 100644
--- a/post_proc/bass_boost.c
+++ b/post_proc/bass_boost.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
  * Not a Contribution.
  *
  * Copyright (C) 2013 The Android Open Source Project
@@ -17,14 +17,17 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "offload_effect_bass_boost"
+#define LOG_TAG "offload_effect_bass"
 //#define LOG_NDEBUG 0
 
 #include <cutils/list.h>
 #include <cutils/log.h>
+#include <cutils/properties.h>
 #include <tinyalsa/asoundlib.h>
 #include <sound/audio_effects.h>
 #include <audio_effects/effect_bassboost.h>
+#include <stdlib.h>
+#include <dlfcn.h>
 
 #include "effect_api.h"
 #include "bass_boost.h"
@@ -41,6 +44,243 @@
         "The Android Open Source Project",
 };
 
+#define LIB_ACDB_LOADER "libacdbloader.so"
+#define PBE_CONF_APP_ID 0x00011134
+
+enum {
+        AUDIO_DEVICE_CAL_TYPE = 0,
+        AUDIO_STREAM_CAL_TYPE,
+};
+
+typedef struct acdb_audio_cal_cfg {
+        uint32_t persist;
+        uint32_t snd_dev_id;
+        uint32_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;
+
+typedef int (*acdb_get_audio_cal_t) (void *, void *, uint32_t*);
+static int pbe_load_config(struct pbe_params *params);
+
+/*
+ * Bass operations
+ */
+int bass_get_parameter(effect_context_t *context, effect_param_t *p,
+                       uint32_t *size)
+{
+    bass_context_t *bass_ctxt = (bass_context_t *)context;
+    int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t);
+    int32_t *param_tmp = (int32_t *)p->data;
+    int32_t param = *param_tmp++;
+    void *value = p->data + voffset;
+    int i;
+
+    ALOGV("%s", __func__);
+
+    p->status = 0;
+
+    switch (param) {
+    case BASSBOOST_PARAM_STRENGTH_SUPPORTED:
+        if (p->vsize < sizeof(uint32_t))
+           p->status = -EINVAL;
+        p->vsize = sizeof(uint32_t);
+        break;
+    case BASSBOOST_PARAM_STRENGTH:
+        if (p->vsize < sizeof(int16_t))
+           p->status = -EINVAL;
+        p->vsize = sizeof(int16_t);
+        break;
+    default:
+        p->status = -EINVAL;
+    }
+
+    *size = sizeof(effect_param_t) + voffset + p->vsize;
+
+    if (p->status != 0)
+        return 0;
+
+    switch (param) {
+    case BASSBOOST_PARAM_STRENGTH_SUPPORTED:
+        ALOGV("%s: BASSBOOST_PARAM_STRENGTH_SUPPORTED", __func__);
+        if (bass_ctxt->active_index == BASS_BOOST)
+            *(uint32_t *)value = 1;
+        else
+            *(uint32_t *)value = 0;
+        break;
+
+    case BASSBOOST_PARAM_STRENGTH:
+        ALOGV("%s: BASSBOOST_PARAM_STRENGTH", __func__);
+        if (bass_ctxt->active_index == BASS_BOOST)
+            *(int16_t *)value = bassboost_get_strength(&(bass_ctxt->bassboost_ctxt));
+        else
+            *(int16_t *)value = 0;
+        break;
+
+    default:
+        p->status = -EINVAL;
+        break;
+    }
+
+    return 0;
+}
+
+int bass_set_parameter(effect_context_t *context, effect_param_t *p,
+                       uint32_t size __unused)
+{
+    bass_context_t *bass_ctxt = (bass_context_t *)context;
+    int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t);
+    void *value = p->data + voffset;
+    int32_t *param_tmp = (int32_t *)p->data;
+    int32_t param = *param_tmp++;
+    uint32_t strength;
+
+    ALOGV("%s", __func__);
+
+    p->status = 0;
+
+    switch (param) {
+    case BASSBOOST_PARAM_STRENGTH:
+        ALOGV("%s BASSBOOST_PARAM_STRENGTH", __func__);
+        if (bass_ctxt->active_index == BASS_BOOST) {
+            strength = (uint32_t)(*(int16_t *)value);
+            bassboost_set_strength(&(bass_ctxt->bassboost_ctxt), strength);
+        } else {
+            /* stength supported only for BB and not for PBE, but do not
+             * return error for unsupported case, as it fails cts test
+             */
+            ALOGD("%s ignore set strength, index %d",
+                  __func__, bass_ctxt->active_index);
+            break;
+        }
+        break;
+    default:
+        p->status = -EINVAL;
+        break;
+    }
+
+    return 0;
+}
+
+int bass_set_device(effect_context_t *context, uint32_t device)
+{
+    bass_context_t *bass_ctxt = (bass_context_t *)context;
+
+    if (device == AUDIO_DEVICE_OUT_SPEAKER) {
+        bass_ctxt->active_index = BASS_PBE;
+        ALOGV("%s: set PBE mode, device: %x", __func__, device);
+    } else if (device == AUDIO_DEVICE_OUT_WIRED_HEADSET ||
+        device == AUDIO_DEVICE_OUT_WIRED_HEADPHONE ||
+        device == AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES) {
+        ALOGV("%s: set BB mode, device: %x", __func__, device);
+        bass_ctxt->active_index = BASS_BOOST;
+    } else {
+        ALOGI("%s: disabled by device: %x", __func__, device);
+        bass_ctxt->active_index = BASS_INVALID;
+    }
+
+    bassboost_set_device((effect_context_t *)&(bass_ctxt->bassboost_ctxt), device);
+    pbe_set_device((effect_context_t *)&(bass_ctxt->pbe_ctxt), device);
+
+    return 0;
+}
+
+int bass_reset(effect_context_t *context)
+{
+    bass_context_t *bass_ctxt = (bass_context_t *)context;
+
+    bassboost_reset((effect_context_t *)&(bass_ctxt->bassboost_ctxt));
+    pbe_reset((effect_context_t *)&(bass_ctxt->pbe_ctxt));
+
+    return 0;
+}
+
+int bass_init(effect_context_t *context)
+{
+    bass_context_t *bass_ctxt = (bass_context_t *)context;
+
+    // convery i/o channel config to sub effects
+    bass_ctxt->bassboost_ctxt.common.config = context->config;
+    bass_ctxt->pbe_ctxt.common.config = context->config;
+
+    ALOGV("%s", __func__);
+
+    bass_ctxt->active_index = BASS_BOOST;
+
+
+    bassboost_init((effect_context_t *)&(bass_ctxt->bassboost_ctxt));
+    pbe_init((effect_context_t *)&(bass_ctxt->pbe_ctxt));
+
+    return 0;
+}
+
+int bass_enable(effect_context_t *context)
+{
+    bass_context_t *bass_ctxt = (bass_context_t *)context;
+
+    ALOGV("%s", __func__);
+
+    bassboost_enable((effect_context_t *)&(bass_ctxt->bassboost_ctxt));
+    pbe_enable((effect_context_t *)&(bass_ctxt->pbe_ctxt));
+
+    return 0;
+}
+
+int bass_disable(effect_context_t *context)
+{
+    bass_context_t *bass_ctxt = (bass_context_t *)context;
+
+    ALOGV("%s", __func__);
+
+    bassboost_disable((effect_context_t *)&(bass_ctxt->bassboost_ctxt));
+    pbe_disable((effect_context_t *)&(bass_ctxt->pbe_ctxt));
+
+    return 0;
+}
+
+int bass_start(effect_context_t *context, output_context_t *output)
+{
+    bass_context_t *bass_ctxt = (bass_context_t *)context;
+
+    ALOGV("%s", __func__);
+
+    bassboost_start((effect_context_t *)&(bass_ctxt->bassboost_ctxt), output);
+    pbe_start((effect_context_t *)&(bass_ctxt->pbe_ctxt), output);
+
+    return 0;
+}
+
+int bass_stop(effect_context_t *context, output_context_t *output)
+{
+    bass_context_t *bass_ctxt = (bass_context_t *)context;
+
+    ALOGV("%s", __func__);
+
+    bassboost_stop((effect_context_t *)&(bass_ctxt->bassboost_ctxt), output);
+    pbe_stop((effect_context_t *)&(bass_ctxt->pbe_ctxt), output);
+
+    return 0;
+}
+
+int bass_set_mode(effect_context_t *context,  int32_t hw_acc_fd)
+{
+    bass_context_t *bass_ctxt = (bass_context_t *)context;
+
+    ALOGV("%s", __func__);
+
+    bassboost_set_mode((effect_context_t *)&(bass_ctxt->bassboost_ctxt), hw_acc_fd);
+    pbe_set_mode((effect_context_t *)&(bass_ctxt->pbe_ctxt), hw_acc_fd);
+
+    return 0;
+}
+
+#undef LOG_TAG
+#define LOG_TAG "offload_effect_bb"
 /*
  * Bassboost operations
  */
@@ -70,99 +310,30 @@
     return 0;
 }
 
-int bassboost_get_parameter(effect_context_t *context, effect_param_t *p,
-                            uint32_t *size)
-{
-    bassboost_context_t *bass_ctxt = (bassboost_context_t *)context;
-    int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t);
-    int32_t *param_tmp = (int32_t *)p->data;
-    int32_t param = *param_tmp++;
-    void *value = p->data + voffset;
-    int i;
-
-    ALOGV("%s: ctxt %p, param %d", __func__, bass_ctxt, param);
-
-    p->status = 0;
-
-    switch (param) {
-    case BASSBOOST_PARAM_STRENGTH_SUPPORTED:
-        if (p->vsize < sizeof(uint32_t))
-           p->status = -EINVAL;
-        p->vsize = sizeof(uint32_t);
-        break;
-    case BASSBOOST_PARAM_STRENGTH:
-        if (p->vsize < sizeof(int16_t))
-           p->status = -EINVAL;
-        p->vsize = sizeof(int16_t);
-        break;
-    default:
-        p->status = -EINVAL;
-    }
-
-    *size = sizeof(effect_param_t) + voffset + p->vsize;
-
-    if (p->status != 0)
-        return 0;
-
-    switch (param) {
-    case BASSBOOST_PARAM_STRENGTH_SUPPORTED:
-        *(uint32_t *)value = 1;
-        break;
-
-    case BASSBOOST_PARAM_STRENGTH:
-        *(int16_t *)value = bassboost_get_strength(bass_ctxt);
-        break;
-
-    default:
-        p->status = -EINVAL;
-        break;
-    }
-
-    return 0;
-}
-
-int bassboost_set_parameter(effect_context_t *context, effect_param_t *p,
-                            uint32_t size __unused)
-{
-    bassboost_context_t *bass_ctxt = (bassboost_context_t *)context;
-    int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t);
-    void *value = p->data + voffset;
-    int32_t *param_tmp = (int32_t *)p->data;
-    int32_t param = *param_tmp++;
-    uint32_t strength;
-
-    ALOGV("%s: ctxt %p, param %d", __func__, bass_ctxt, param);
-
-    p->status = 0;
-
-    switch (param) {
-    case BASSBOOST_PARAM_STRENGTH:
-        strength = (uint32_t)(*(int16_t *)value);
-        bassboost_set_strength(bass_ctxt, strength);
-        break;
-    default:
-        p->status = -EINVAL;
-        break;
-    }
-
-    return 0;
-}
-
 int bassboost_set_device(effect_context_t *context, uint32_t device)
 {
     bassboost_context_t *bass_ctxt = (bassboost_context_t *)context;
 
     ALOGV("%s: ctxt %p, device 0x%x", __func__, bass_ctxt, device);
     bass_ctxt->device = device;
-    if((device == AUDIO_DEVICE_OUT_SPEAKER) ||
-       (device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT) ||
-       (device == AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER) ||
-#ifdef AFE_PROXY_ENABLED
-       (device == AUDIO_DEVICE_OUT_PROXY) ||
-#endif
-       (device == AUDIO_DEVICE_OUT_AUX_DIGITAL) ||
-       (device == AUDIO_DEVICE_OUT_USB_ACCESSORY) ||
-       (device == AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET)) {
+    if (device == AUDIO_DEVICE_OUT_WIRED_HEADSET ||
+        device == AUDIO_DEVICE_OUT_WIRED_HEADPHONE ||
+        device == AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES) {
+        if (bass_ctxt->temp_disabled) {
+            if (effect_is_active(&bass_ctxt->common)) {
+                offload_bassboost_set_enable_flag(&(bass_ctxt->offload_bass), true);
+                if (bass_ctxt->ctl)
+                    offload_bassboost_send_params(bass_ctxt->ctl,
+                                                  &bass_ctxt->offload_bass,
+                                                  OFFLOAD_SEND_BASSBOOST_ENABLE_FLAG);
+                if (bass_ctxt->hw_acc_fd > 0)
+                    hw_acc_bassboost_send_params(bass_ctxt->hw_acc_fd,
+                                                 &bass_ctxt->offload_bass,
+                                                 OFFLOAD_SEND_BASSBOOST_ENABLE_FLAG);
+            }
+            bass_ctxt->temp_disabled = false;
+        }
+    } else {
         if (!bass_ctxt->temp_disabled) {
             if (effect_is_active(&bass_ctxt->common)) {
                 offload_bassboost_set_enable_flag(&(bass_ctxt->offload_bass), false);
@@ -178,21 +349,6 @@
             bass_ctxt->temp_disabled = true;
         }
         ALOGI("%s: ctxt %p, disabled based on device", __func__, bass_ctxt);
-    } else {
-        if (bass_ctxt->temp_disabled) {
-            if (effect_is_active(&bass_ctxt->common)) {
-                offload_bassboost_set_enable_flag(&(bass_ctxt->offload_bass), true);
-                if (bass_ctxt->ctl)
-                    offload_bassboost_send_params(bass_ctxt->ctl,
-                                                  &bass_ctxt->offload_bass,
-                                                  OFFLOAD_SEND_BASSBOOST_ENABLE_FLAG);
-                if (bass_ctxt->hw_acc_fd > 0)
-                    hw_acc_bassboost_send_params(bass_ctxt->hw_acc_fd,
-                                                 &bass_ctxt->offload_bass,
-                                                 OFFLOAD_SEND_BASSBOOST_ENABLE_FLAG);
-            }
-            bass_ctxt->temp_disabled = false;
-        }
     }
     offload_bassboost_set_device(&(bass_ctxt->offload_bass), device);
     return 0;
@@ -329,3 +485,234 @@
                                      OFFLOAD_SEND_BASSBOOST_STRENGTH);
     return 0;
 }
+
+#undef LOG_TAG
+#define LOG_TAG "offload_effect_pbe"
+/*
+ * PBE operations
+ */
+
+int pbe_set_device(effect_context_t *context, uint32_t device)
+{
+    pbe_context_t *pbe_ctxt = (pbe_context_t *)context;
+    char          propValue[PROPERTY_VALUE_MAX];
+    bool          pbe_enabled_by_prop = false;
+
+    ALOGV("%s: device: %d", __func__, device);
+    pbe_ctxt->device = device;
+
+    if (property_get("audio.safx.pbe.enabled", propValue, NULL)) {
+        pbe_enabled_by_prop = atoi(propValue) ||
+                              !strncmp("true", propValue, 4);
+    }
+
+    if (device == AUDIO_DEVICE_OUT_SPEAKER && pbe_enabled_by_prop == true) {
+        if (pbe_ctxt->temp_disabled) {
+            if (effect_is_active(&pbe_ctxt->common)) {
+                offload_pbe_set_enable_flag(&(pbe_ctxt->offload_pbe), true);
+                if (pbe_ctxt->ctl)
+                    offload_pbe_send_params(pbe_ctxt->ctl,
+                                        &pbe_ctxt->offload_pbe,
+                                        OFFLOAD_SEND_PBE_ENABLE_FLAG |
+                                        OFFLOAD_SEND_PBE_CONFIG);
+                if (pbe_ctxt->hw_acc_fd > 0)
+                    hw_acc_pbe_send_params(pbe_ctxt->hw_acc_fd,
+                                        &pbe_ctxt->offload_pbe,
+                                        OFFLOAD_SEND_PBE_ENABLE_FLAG |
+                                        OFFLOAD_SEND_PBE_CONFIG);
+            }
+            pbe_ctxt->temp_disabled = false;
+        }
+    } else {
+        if (!pbe_ctxt->temp_disabled) {
+            if (effect_is_active(&pbe_ctxt->common)) {
+                offload_pbe_set_enable_flag(&(pbe_ctxt->offload_pbe), false);
+                if (pbe_ctxt->ctl)
+                    offload_pbe_send_params(pbe_ctxt->ctl,
+                                        &pbe_ctxt->offload_pbe,
+                                        OFFLOAD_SEND_PBE_ENABLE_FLAG);
+                if (pbe_ctxt->hw_acc_fd > 0)
+                    hw_acc_pbe_send_params(pbe_ctxt->hw_acc_fd,
+                                        &pbe_ctxt->offload_pbe,
+                                        OFFLOAD_SEND_PBE_ENABLE_FLAG);
+            }
+            pbe_ctxt->temp_disabled = true;
+        }
+    }
+    offload_pbe_set_device(&(pbe_ctxt->offload_pbe), device);
+    return 0;
+}
+
+int pbe_reset(effect_context_t *context)
+{
+    pbe_context_t *pbe_ctxt = (pbe_context_t *)context;
+
+    return 0;
+}
+
+int pbe_init(effect_context_t *context)
+{
+    pbe_context_t *pbe_ctxt = (pbe_context_t *)context;
+
+    ALOGV("%s", __func__);
+    context->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
+    context->config.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
+    context->config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+    context->config.inputCfg.samplingRate = 44100;
+    context->config.inputCfg.bufferProvider.getBuffer = NULL;
+    context->config.inputCfg.bufferProvider.releaseBuffer = NULL;
+    context->config.inputCfg.bufferProvider.cookie = NULL;
+    context->config.inputCfg.mask = EFFECT_CONFIG_ALL;
+    context->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
+    context->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
+    context->config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+    context->config.outputCfg.samplingRate = 44100;
+    context->config.outputCfg.bufferProvider.getBuffer = NULL;
+    context->config.outputCfg.bufferProvider.releaseBuffer = NULL;
+    context->config.outputCfg.bufferProvider.cookie = NULL;
+    context->config.outputCfg.mask = EFFECT_CONFIG_ALL;
+
+    set_config(context, &context->config);
+
+    pbe_ctxt->hw_acc_fd = -1;
+    pbe_ctxt->temp_disabled = false;
+    memset(&(pbe_ctxt->offload_pbe), 0, sizeof(struct pbe_params));
+    pbe_load_config(&(pbe_ctxt->offload_pbe));
+
+    return 0;
+}
+
+int pbe_enable(effect_context_t *context)
+{
+    pbe_context_t *pbe_ctxt = (pbe_context_t *)context;
+
+    ALOGV("%s", __func__);
+
+    if (!offload_pbe_get_enable_flag(&(pbe_ctxt->offload_pbe)) &&
+        !(pbe_ctxt->temp_disabled)) {
+        offload_pbe_set_enable_flag(&(pbe_ctxt->offload_pbe), true);
+        if (pbe_ctxt->ctl)
+            offload_pbe_send_params(pbe_ctxt->ctl,
+                                    &pbe_ctxt->offload_pbe,
+                                    OFFLOAD_SEND_PBE_ENABLE_FLAG |
+                                    OFFLOAD_SEND_PBE_CONFIG);
+        if (pbe_ctxt->hw_acc_fd > 0)
+            hw_acc_pbe_send_params(pbe_ctxt->hw_acc_fd,
+                                   &pbe_ctxt->offload_pbe,
+                                   OFFLOAD_SEND_PBE_ENABLE_FLAG |
+                                   OFFLOAD_SEND_PBE_CONFIG);
+    }
+    return 0;
+}
+
+int pbe_disable(effect_context_t *context)
+{
+    pbe_context_t *pbe_ctxt = (pbe_context_t *)context;
+
+    ALOGV("%s", __func__);
+    if (offload_pbe_get_enable_flag(&(pbe_ctxt->offload_pbe))) {
+        offload_pbe_set_enable_flag(&(pbe_ctxt->offload_pbe), false);
+        if (pbe_ctxt->ctl)
+            offload_pbe_send_params(pbe_ctxt->ctl,
+                                    &pbe_ctxt->offload_pbe,
+                                    OFFLOAD_SEND_PBE_ENABLE_FLAG);
+        if (pbe_ctxt->hw_acc_fd > 0)
+            hw_acc_pbe_send_params(pbe_ctxt->hw_acc_fd,
+                                   &pbe_ctxt->offload_pbe,
+                                   OFFLOAD_SEND_PBE_ENABLE_FLAG);
+    }
+    return 0;
+}
+
+int pbe_start(effect_context_t *context, output_context_t *output)
+{
+    pbe_context_t *pbe_ctxt = (pbe_context_t *)context;
+
+    ALOGV("%s", __func__);
+    pbe_ctxt->ctl = output->ctl;
+    ALOGV("output->ctl: %p", output->ctl);
+    if (offload_pbe_get_enable_flag(&(pbe_ctxt->offload_pbe))) {
+        if (pbe_ctxt->ctl)
+            offload_pbe_send_params(pbe_ctxt->ctl, &pbe_ctxt->offload_pbe,
+                                    OFFLOAD_SEND_PBE_ENABLE_FLAG |
+                                    OFFLOAD_SEND_PBE_CONFIG);
+        if (pbe_ctxt->hw_acc_fd > 0)
+            hw_acc_pbe_send_params(pbe_ctxt->hw_acc_fd,
+                                   &pbe_ctxt->offload_pbe,
+                                   OFFLOAD_SEND_PBE_ENABLE_FLAG |
+                                   OFFLOAD_SEND_PBE_CONFIG);
+    }
+    return 0;
+}
+
+int pbe_stop(effect_context_t *context, output_context_t *output  __unused)
+{
+    pbe_context_t *pbe_ctxt = (pbe_context_t *)context;
+
+    ALOGV("%s", __func__);
+    pbe_ctxt->ctl = NULL;
+    return 0;
+}
+
+int pbe_set_mode(effect_context_t *context, int32_t hw_acc_fd)
+{
+    pbe_context_t *pbe_ctxt = (pbe_context_t *)context;
+
+    ALOGV("%s: ctxt %p", __func__, pbe_ctxt);
+    pbe_ctxt->hw_acc_fd = hw_acc_fd;
+    if ((pbe_ctxt->hw_acc_fd > 0) &&
+        (offload_pbe_get_enable_flag(&(pbe_ctxt->offload_pbe))))
+        hw_acc_pbe_send_params(pbe_ctxt->hw_acc_fd,
+                               &pbe_ctxt->offload_pbe,
+                               OFFLOAD_SEND_PBE_ENABLE_FLAG |
+                               OFFLOAD_SEND_PBE_CONFIG);
+    return 0;
+}
+
+static int pbe_load_config(struct pbe_params *params)
+{
+    int                  ret = 0;
+    uint32_t             len = 0;
+    uint32_t             propValue = 0;
+    uint32_t             pbe_app_type = PBE_CONF_APP_ID;
+    char                 propValueStr[PROPERTY_VALUE_MAX];
+    void                 *acdb_handle = NULL;
+    acdb_get_audio_cal_t acdb_get_audio_cal = NULL;
+    acdb_audio_cal_cfg_t cal_cfg = {0};
+
+    acdb_handle = dlopen(LIB_ACDB_LOADER, RTLD_NOW);
+    if (acdb_handle == NULL) {
+        ALOGE("%s error opening library %s", __func__, LIB_ACDB_LOADER);
+        return -EFAULT;
+    }
+
+    acdb_get_audio_cal = (acdb_get_audio_cal_t)dlsym(acdb_handle,
+                                  "acdb_loader_get_audio_cal_v2");
+    if (acdb_get_audio_cal == NULL) {
+        dlclose(acdb_handle);
+        ALOGE("%s error resolving acdb func symbols", __func__);
+        return -EFAULT;
+    }
+    if (property_get("audio.safx.pbe.app.type", propValueStr, "0")) {
+        propValue = atoll(propValueStr);
+        if (propValue != 0) {
+            pbe_app_type = propValue;
+        }
+    }
+    ALOGD("%s pbe_app_type = 0x%.8x", __func__, pbe_app_type);
+
+    cal_cfg.persist              = 1;
+    cal_cfg.cal_type             = AUDIO_STREAM_CAL_TYPE;
+    cal_cfg.app_type             = pbe_app_type;
+    cal_cfg.module_id            = PBE_CONF_MODULE_ID;
+    cal_cfg.param_id             = PBE_CONF_PARAM_ID;
+
+    len = sizeof(params->config);
+    ret = acdb_get_audio_cal((void *)&cal_cfg, (void*)&(params->config), &len);
+    ALOGD("%s ret = %d, len = %u", __func__, ret, len);
+    if (ret == 0)
+        params->cfg_len = len;
+
+    dlclose(acdb_handle);
+    return ret;
+}
diff --git a/post_proc/bass_boost.h b/post_proc/bass_boost.h
index 6f0e61b..14d6c97 100644
--- a/post_proc/bass_boost.h
+++ b/post_proc/bass_boost.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
  * Not a Contribution.
  *
  * Copyright (C) 2013 The Android Open Source Project
@@ -22,6 +22,13 @@
 
 #include "bundle.h"
 
+enum {
+    BASS_INVALID = -1,
+    BASS_BOOST = 0,      // index of bassboost
+    BASS_PBE,        // index of PBE
+    BASS_COUNT       // totol number of bass type
+};
+
 extern const effect_descriptor_t bassboost_descriptor;
 
 typedef struct bassboost_context_s {
@@ -37,11 +44,50 @@
     struct bass_boost_params offload_bass;
 } bassboost_context_t;
 
-int bassboost_get_parameter(effect_context_t *context, effect_param_t *p,
-                            uint32_t *size);
+typedef struct pbe_context_s {
+    effect_context_t common;
 
-int bassboost_set_parameter(effect_context_t *context, effect_param_t *p,
-                            uint32_t size);
+    // Offload vars
+    struct mixer_ctl *ctl;
+    int hw_acc_fd;
+    bool temp_disabled;
+    uint32_t device;
+    struct pbe_params offload_pbe;
+} pbe_context_t;
+
+typedef struct bass_context_s {
+    effect_context_t    common;
+    bassboost_context_t bassboost_ctxt;
+    pbe_context_t       pbe_ctxt;
+    int                 active_index;
+} bass_context_t;
+
+int bass_get_parameter(effect_context_t *context, effect_param_t *p,
+                       uint32_t *size);
+
+int bass_set_parameter(effect_context_t *context, effect_param_t *p,
+                       uint32_t size);
+
+int bass_set_device(effect_context_t *context,  uint32_t device);
+
+int bass_set_mode(effect_context_t *context,  int32_t hw_acc_fd);
+
+int bass_reset(effect_context_t *context);
+
+int bass_init(effect_context_t *context);
+
+int bass_enable(effect_context_t *context);
+
+int bass_disable(effect_context_t *context);
+
+int bass_start(effect_context_t *context, output_context_t *output);
+
+int bass_stop(effect_context_t *context, output_context_t *output);
+
+
+int bassboost_get_strength(bassboost_context_t *context);
+
+int bassboost_set_strength(bassboost_context_t *context, uint32_t strength);
 
 int bassboost_set_device(effect_context_t *context,  uint32_t device);
 
@@ -59,4 +105,20 @@
 
 int bassboost_stop(effect_context_t *context, output_context_t *output);
 
+int pbe_set_device(effect_context_t *context,  uint32_t device);
+
+int pbe_set_mode(effect_context_t *context,  int32_t hw_acc_fd);
+
+int pbe_reset(effect_context_t *context);
+
+int pbe_init(effect_context_t *context);
+
+int pbe_enable(effect_context_t *context);
+
+int pbe_disable(effect_context_t *context);
+
+int pbe_start(effect_context_t *context, output_context_t *output);
+
+int pbe_stop(effect_context_t *context, output_context_t *output);
+
 #endif /* OFFLOAD_EFFECT_BASS_BOOST_H_ */
diff --git a/post_proc/bundle.c b/post_proc/bundle.c
index 410e17b..e38a41c 100644
--- a/post_proc/bundle.c
+++ b/post_proc/bundle.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
  * Not a Contribution.
  *
  * Copyright (C) 2013 The Android Open Source Project
@@ -520,25 +520,26 @@
         eq_ctxt->ctl = NULL;
     } else if (memcmp(uuid, &bassboost_descriptor.uuid,
                sizeof(effect_uuid_t)) == 0) {
-        bassboost_context_t *bass_ctxt = (bassboost_context_t *)
-                                         calloc(1, sizeof(bassboost_context_t));
+        bass_context_t *bass_ctxt = (bass_context_t *)
+                                         calloc(1, sizeof(bass_context_t));
         if (bass_ctxt == NULL) {
             return -ENOMEM;
         }
         context = (effect_context_t *)bass_ctxt;
-        context->ops.init = bassboost_init;
-        context->ops.reset = bassboost_reset;
-        context->ops.set_parameter = bassboost_set_parameter;
-        context->ops.get_parameter = bassboost_get_parameter;
-        context->ops.set_device = bassboost_set_device;
-        context->ops.set_hw_acc_mode = bassboost_set_mode;
-        context->ops.enable = bassboost_enable;
-        context->ops.disable = bassboost_disable;
-        context->ops.start = bassboost_start;
-        context->ops.stop = bassboost_stop;
+        context->ops.init = bass_init;
+        context->ops.reset = bass_reset;
+        context->ops.set_parameter = bass_set_parameter;
+        context->ops.get_parameter = bass_get_parameter;
+        context->ops.set_device = bass_set_device;
+        context->ops.set_hw_acc_mode = bass_set_mode;
+        context->ops.enable = bass_enable;
+        context->ops.disable = bass_disable;
+        context->ops.start = bass_start;
+        context->ops.stop = bass_stop;
 
         context->desc = &bassboost_descriptor;
-        bass_ctxt->ctl = NULL;
+        bass_ctxt->bassboost_ctxt.ctl = NULL;
+        bass_ctxt->pbe_ctxt.ctl = NULL;
     } else if (memcmp(uuid, &virtualizer_descriptor.uuid,
                sizeof(effect_uuid_t)) == 0) {
         virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)
@@ -713,8 +714,8 @@
 /* Stub function for effect interface: never called for offloaded effects */
 /* called for hw accelerated effects */
 int effect_process(effect_handle_t self,
-                       audio_buffer_t *inBuffer,
-                       audio_buffer_t *outBuffer)
+                       audio_buffer_t *inBuffer __unused,
+                       audio_buffer_t *outBuffer __unused)
 {
     effect_context_t * context = (effect_context_t *)self;
     int status = 0;
diff --git a/post_proc/effect_api.c b/post_proc/effect_api.c
index 7c1968e..e15db17 100644
--- a/post_proc/effect_api.c
+++ b/post_proc/effect_api.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
 
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -114,7 +114,7 @@
         return -EINVAL;
     } else {
         *ctl = mixer_get_ctl_by_name(*mixer, mixer_string);
-        if (!ctl) {
+        if (!(*ctl)) {
             ALOGE("mixer_get_ctl_by_name failed");
             mixer_close(*mixer);
             *mixer = NULL;
@@ -235,6 +235,83 @@
                                  bassboost, param_send_flags);
 }
 
+void offload_pbe_set_device(struct pbe_params *pbe,
+                            uint32_t device)
+{
+    ALOGV("%s: device=%d", __func__, device);
+    pbe->device = device;
+}
+
+void offload_pbe_set_enable_flag(struct pbe_params *pbe,
+                                 bool enable)
+{
+    ALOGV("%s: enable=%d", __func__, enable);
+    pbe->enable_flag = enable;
+}
+
+int offload_pbe_get_enable_flag(struct pbe_params *pbe)
+{
+    ALOGV("%s: enabled=%d", __func__, pbe->enable_flag);
+    return pbe->enable_flag;
+}
+
+static int pbe_send_params(eff_mode_t mode, void *ctl,
+                            struct pbe_params *pbe,
+                            unsigned param_send_flags)
+{
+    int param_values[128] = {0};
+    int i, *p_param_values = param_values, *cfg = NULL;
+
+    ALOGV("%s: enabled=%d", __func__, pbe->enable_flag);
+    *p_param_values++ = PBE_MODULE;
+    *p_param_values++ = pbe->device;
+    *p_param_values++ = 0; /* num of commands*/
+    if (param_send_flags & OFFLOAD_SEND_PBE_ENABLE_FLAG) {
+        *p_param_values++ = PBE_ENABLE;
+        *p_param_values++ = CONFIG_SET;
+        *p_param_values++ = 0; /* start offset if param size if greater than 128  */
+        *p_param_values++ = PBE_ENABLE_PARAM_LEN;
+        *p_param_values++ = pbe->enable_flag;
+        param_values[2] += 1;
+    }
+    if (param_send_flags & OFFLOAD_SEND_PBE_CONFIG) {
+        *p_param_values++ = PBE_CONFIG;
+        *p_param_values++ = CONFIG_SET;
+        *p_param_values++ = 0; /* start offset if param size if greater than 128  */
+        *p_param_values++ = pbe->cfg_len;
+        cfg = (int *)&pbe->config;
+        for (i = 0; i < (int)pbe->cfg_len ; i+= sizeof(*p_param_values))
+            *p_param_values++ = *cfg++;
+        param_values[2] += 1;
+    }
+
+    if ((mode == OFFLOAD) && param_values[2] && ctl) {
+        mixer_ctl_set_array((struct mixer_ctl *)ctl, param_values,
+                            ARRAY_SIZE(param_values));
+    } else if ((mode == HW_ACCELERATOR) && param_values[2] &&
+               ctl && *(int *)ctl) {
+        if (ioctl(*(int *)ctl, AUDIO_EFFECTS_SET_PP_PARAMS, param_values) < 0)
+            ALOGE("%s: sending h/w acc effects params fail[%d]", __func__, errno);
+    }
+
+    return 0;
+}
+
+int offload_pbe_send_params(struct mixer_ctl *ctl,
+                                  struct pbe_params *pbe,
+                                  unsigned param_send_flags)
+{
+    return pbe_send_params(OFFLOAD, (void *)ctl, pbe,
+                                 param_send_flags);
+}
+
+int hw_acc_pbe_send_params(int fd, struct pbe_params *pbe,
+                                 unsigned param_send_flags)
+{
+    return pbe_send_params(HW_ACCELERATOR, (void *)&fd,
+                                 pbe, param_send_flags);
+}
+
 void offload_virtualizer_set_device(struct virtualizer_params *virtualizer,
                                     uint32_t device)
 {
@@ -891,6 +968,11 @@
     uint32_t i;
 
     ALOGV("%s", __func__);
+    if (!ctl) {
+        ALOGE("%s: ctl is NULL, return invalid", __func__);
+        return -EINVAL;
+    }
+
     if (param_send_flags & OFFLOAD_SEND_HPX_STATE_OFF) {
         *p_param_values++ = DTS_EAGLE_MODULE_ENABLE;
         *p_param_values++ = 0; /* hpx off*/
@@ -899,7 +981,7 @@
         *p_param_values++ = 1; /* hpx on*/
     }
 
-    if ((mode == OFFLOAD) && ctl)
+    if (mode == OFFLOAD)
         mixer_ctl_set_array(ctl, param_values, ARRAY_SIZE(param_values));
     else {
         if (ioctl(*(int *)ctl, AUDIO_EFFECTS_SET_PP_PARAMS, param_values) < 0)
diff --git a/post_proc/effect_api.h b/post_proc/effect_api.h
index e05924a..ce0503a 100644
--- a/post_proc/effect_api.h
+++ b/post_proc/effect_api.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
 
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -39,6 +39,21 @@
                                          struct mixer_ctl **ctl);
 void offload_close_mixer(struct mixer **mixer);
 
+
+#define OFFLOAD_SEND_PBE_ENABLE_FLAG      (1 << 0)
+#define OFFLOAD_SEND_PBE_CONFIG           (OFFLOAD_SEND_PBE_ENABLE_FLAG << 1)
+void offload_pbe_set_device(struct pbe_params *pbe,
+                            uint32_t device);
+void offload_pbe_set_enable_flag(struct pbe_params *pbe,
+                                 bool enable);
+int offload_pbe_get_enable_flag(struct pbe_params *pbe);
+
+int offload_pbe_send_params(struct mixer_ctl *ctl,
+                            struct pbe_params *pbe,
+                            unsigned param_send_flags);
+int hw_acc_pbe_send_params(int fd,
+                           struct pbe_params *pbe,
+                           unsigned param_send_flags);
 #define OFFLOAD_SEND_BASSBOOST_ENABLE_FLAG      (1 << 0)
 #define OFFLOAD_SEND_BASSBOOST_STRENGTH         \
                                           (OFFLOAD_SEND_BASSBOOST_ENABLE_FLAG << 1)
diff --git a/post_proc/effect_util.c b/post_proc/effect_util.c
index cda89bc..8f7a604 100644
--- a/post_proc/effect_util.c
+++ b/post_proc/effect_util.c
@@ -44,7 +44,7 @@
                               "bassboost_param_strength"
 };
 
-#define EFFECT_FILE "/data/data/dts/effect"
+#define EFFECT_FILE "/data/misc/dts/effect"
 #define MAX_LENGTH_OF_INTEGER_IN_STRING 13
 
 #ifdef DTS_EAGLE
diff --git a/post_proc/virtualizer.c b/post_proc/virtualizer.c
index f6e2881..2748568 100644
--- a/post_proc/virtualizer.c
+++ b/post_proc/virtualizer.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
  * Not a Contribution.
  *
  * Copyright (C) 2013 The Android Open Source Project
@@ -69,6 +69,197 @@
     return 0;
 }
 
+/*
+ *  Check if an audio device is supported by this implementation
+ *
+ *  [in]
+ *  device    device that is intented for processing (e.g. for binaural vs transaural)
+ *  [out]
+ *  false     device is not applicable for effect
+ *  true      device is applicable for effect
+ */
+bool virtualizer_is_device_supported(audio_devices_t device) {
+    switch (device) {
+    case AUDIO_DEVICE_OUT_SPEAKER:
+    case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
+    case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER:
+#ifdef AFE_PROXY_ENABLED
+    case AUDIO_DEVICE_OUT_PROXY:
+#endif
+    case AUDIO_DEVICE_OUT_AUX_DIGITAL:
+    case AUDIO_DEVICE_OUT_USB_ACCESSORY:
+    case AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET:
+        return false;
+    default :
+        return true;
+    }
+}
+
+/*
+ *  Check if a channel mask + audio device is supported by this implementation
+ *
+ *  [in]
+ *  channel_mask channel mask of input buffer
+ *  device       device that is intented for processing (e.g. for binaural vs transaural)
+ *  [out]
+ *  false        if the configuration is not supported or it is unknown
+ *  true         if the configuration is supported
+ */
+bool virtualizer_is_configuration_supported(audio_channel_mask_t channel_mask,
+        audio_devices_t device) {
+    uint32_t channelCount = audio_channel_count_from_out_mask(channel_mask);
+    if ((channelCount == 0) || (channelCount > 2)) {
+        return false;
+    }
+
+    return virtualizer_is_device_supported(device);
+}
+
+/*
+ *  Force the virtualization mode to that of the given audio device
+ *
+ *  [in]
+ *  context       effect engine context
+ *  forced_device device whose virtualization mode we'll always use
+ *  [out]
+ *  -EINVAL       if the device is not supported or is unknown
+ *  0             if the device is supported and the virtualization mode forced
+ */
+int virtualizer_force_virtualization_mode(virtualizer_context_t *context,
+        audio_devices_t forced_device) {
+    virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context;
+    int status = 0;
+    bool use_virt = false;
+    int is_virt_enabled =
+        offload_virtualizer_get_enable_flag(&(virt_ctxt->offload_virt));
+
+    ALOGV("%s: ctxt %p, forcedDev=0x%x enabled=%d tmpDisabled=%d", __func__, virt_ctxt,
+            forced_device, is_virt_enabled, virt_ctxt->temp_disabled);
+
+    if (virtualizer_is_device_supported(forced_device) == false) {
+        if (forced_device != AUDIO_DEVICE_NONE) {
+            //forced device is not supported, make it behave as a reset of forced mode
+            forced_device = AUDIO_DEVICE_NONE;
+            // but return an error
+            status = -EINVAL;
+        }
+    }
+
+    if (forced_device == AUDIO_DEVICE_NONE) {
+        // disabling forced virtualization mode:
+        // verify whether the virtualization should be enabled or disabled
+        if (virtualizer_is_device_supported(virt_ctxt->device)) {
+            use_virt = (is_virt_enabled == true);
+        }
+        virt_ctxt->forced_device = AUDIO_DEVICE_NONE;
+    } else {
+        // forcing virtualization mode:
+        // TODO: we assume device is supported, so hard coded a fixed one.
+        virt_ctxt->forced_device = AUDIO_DEVICE_OUT_WIRED_HEADPHONE;
+        // TODO: only enable for a supported mode, when the effect is enabled
+        use_virt = (is_virt_enabled == true);
+    }
+
+    if (use_virt) {
+        if (virt_ctxt->temp_disabled == true) {
+            if (effect_is_active(&virt_ctxt->common)) {
+                offload_virtualizer_set_enable_flag(&(virt_ctxt->offload_virt), true);
+                if (virt_ctxt->ctl)
+                    offload_virtualizer_send_params(virt_ctxt->ctl,
+                                                    &virt_ctxt->offload_virt,
+                                                    OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG);
+                if (virt_ctxt->hw_acc_fd > 0)
+                    hw_acc_virtualizer_send_params(virt_ctxt->hw_acc_fd,
+                                                   &virt_ctxt->offload_virt,
+                                                   OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG);
+            }
+            ALOGV("%s: re-enable VIRTUALIZER", __func__);
+            virt_ctxt->temp_disabled = false;
+        } else {
+            ALOGV("%s: leaving VIRTUALIZER enabled", __func__);
+        }
+    } else {
+        if (virt_ctxt->temp_disabled == false) {
+            if (effect_is_active(&virt_ctxt->common)) {
+                offload_virtualizer_set_enable_flag(&(virt_ctxt->offload_virt), false);
+                if (virt_ctxt->ctl)
+                    offload_virtualizer_send_params(virt_ctxt->ctl,
+                                                    &virt_ctxt->offload_virt,
+                                                    OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG);
+                if (virt_ctxt->hw_acc_fd > 0)
+                    hw_acc_virtualizer_send_params(virt_ctxt->hw_acc_fd,
+                                                   &virt_ctxt->offload_virt,
+                                                   OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG);
+            }
+            ALOGV("%s: disable VIRTUALIZER", __func__);
+            virt_ctxt->temp_disabled = true;
+        } else {
+            ALOGV("%s: leaving VIRTUALIZER disabled", __func__);
+        }
+    }
+
+    ALOGV("after %s: ctxt %p, enabled=%d tmpDisabled=%d", __func__, virt_ctxt,
+            is_virt_enabled, virt_ctxt->temp_disabled);
+
+    return status;
+}
+
+/*
+ *  Get the virtual speaker angles for a channel mask + audio device configuration
+ *  which is guaranteed to be supported by this implementation
+ *
+ *  [in]
+ *  channel_mask   the channel mask of the input to virtualize
+ *  device         the type of device that affects the processing (e.g. for binaural vs transaural)
+ *  [in/out]
+ *  speaker_angles the array of integer where each speaker angle is written as a triplet in the
+ *                 following format:
+ *                  int32_t a bit mask with a single value selected for each speaker, following
+ *                  the convention of the audio_channel_mask_t type
+ *                  int32_t a value in degrees expressing the speaker azimuth, where 0 is in front
+ *                  of the user, 180 behind, -90 to the left, 90 to the right of the user
+ *                  int32_t a value in degrees expressing the speaker elevation, where 0 is the
+ *                  horizontal plane, +90 is directly above the user, -90 below
+ *
+ */
+void virtualizer_get_speaker_angles(audio_channel_mask_t channel_mask __unused,
+        audio_devices_t device __unused, int32_t *speaker_angles) {
+    // the channel count is guaranteed to be 1 or 2
+    // the device is guaranteed to be of type headphone
+    // this virtualizer is always 2in with speakers at -90 and 90deg of azimuth, 0deg of elevation
+    *speaker_angles++ = (int32_t) AUDIO_CHANNEL_OUT_FRONT_LEFT;
+    *speaker_angles++ = -90; // azimuth
+    *speaker_angles++ = 0;   // elevation
+    *speaker_angles++ = (int32_t) AUDIO_CHANNEL_OUT_FRONT_RIGHT;
+    *speaker_angles++ = 90;  // azimuth
+    *speaker_angles   = 0;   // elevation
+}
+
+/*
+ *  Retrieve the current device whose processing mode is used by this effect
+ *
+ *  [out]
+ *  AUDIO_DEVICE_NONE if the effect is not virtualizing
+ *  or the device type if the effect is virtualizing
+ */
+audio_devices_t virtualizer_get_virtualization_mode(virtualizer_context_t *context) {
+    virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context;
+    audio_devices_t device = AUDIO_DEVICE_NONE;
+
+    if ((offload_virtualizer_get_enable_flag(&(virt_ctxt->offload_virt)))
+            && (virt_ctxt->temp_disabled == false)) {
+        if (virt_ctxt->forced_device != AUDIO_DEVICE_NONE) {
+            // virtualization mode is forced, return that device
+            device = virt_ctxt->forced_device;
+        } else {
+            // no forced mode, return the current device
+            device = virt_ctxt->device;
+        }
+    }
+    ALOGV("%s: returning 0x%x", __func__, device);
+    return device;
+}
+
 int virtualizer_get_parameter(effect_context_t *context, effect_param_t *p,
                               uint32_t *size)
 {
@@ -94,6 +285,15 @@
            p->status = -EINVAL;
         p->vsize = sizeof(int16_t);
         break;
+    case VIRTUALIZER_PARAM_VIRTUAL_SPEAKER_ANGLES:
+        // return value size can only be interpreted as relative to input value,
+        // deferring validity check to below
+        break;
+    case VIRTUALIZER_PARAM_VIRTUALIZATION_MODE:
+        if (p->vsize != sizeof(uint32_t))
+           p->status = -EINVAL;
+        p->vsize = sizeof(uint32_t);
+        break;
     default:
         p->status = -EINVAL;
     }
@@ -112,6 +312,31 @@
         *(int16_t *)value = virtualizer_get_strength(virt_ctxt);
         break;
 
+    case VIRTUALIZER_PARAM_VIRTUAL_SPEAKER_ANGLES:
+    {
+        const audio_channel_mask_t channel_mask = (audio_channel_mask_t) *param_tmp++;
+        const audio_devices_t device = (audio_devices_t) *param_tmp;
+        uint32_t channel_cnt = audio_channel_count_from_out_mask(channel_mask);
+
+        if (p->vsize < 3 * channel_cnt * sizeof(int32_t)){
+            p->status = -EINVAL;
+            break;
+        }
+        // verify the configuration is supported
+        if(virtualizer_is_configuration_supported(channel_mask, device)) {
+            // configuration is supported, get the angles
+            virtualizer_get_speaker_angles(channel_mask, device, (int32_t *)value);
+        } else {
+            p->status = -EINVAL;
+        }
+
+        break;
+    }
+
+    case VIRTUALIZER_PARAM_VIRTUALIZATION_MODE:
+        *(uint32_t *)value  = (uint32_t) virtualizer_get_virtualization_mode(virt_ctxt);
+        break;
+
     default:
         p->status = -EINVAL;
         break;
@@ -121,7 +346,7 @@
 }
 
 int virtualizer_set_parameter(effect_context_t *context, effect_param_t *p,
-                              uint32_t size)
+                              uint32_t size __unused)
 {
     virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)context;
     int voffset = ((p->psize - 1) / sizeof(int32_t) + 1) * sizeof(int32_t);
@@ -139,6 +364,14 @@
         strength = (uint32_t)(*(int16_t *)value);
         virtualizer_set_strength(virt_ctxt, strength);
         break;
+    case VIRTUALIZER_PARAM_FORCE_VIRTUALIZATION_MODE:
+    {
+        const audio_devices_t device = *(audio_devices_t *)value;
+        if (0 != virtualizer_force_virtualization_mode(virt_ctxt, device)) {
+            p->status = -EINVAL;
+        }
+        break;
+    }
     default:
         p->status = -EINVAL;
         break;
@@ -153,46 +386,44 @@
 
     ALOGV("%s: ctxt %p, device: 0x%x", __func__, virt_ctxt, device);
     virt_ctxt->device = device;
-    if((device == AUDIO_DEVICE_OUT_SPEAKER) ||
-       (device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT) ||
-       (device == AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER) ||
-#ifdef AFE_PROXY_ENABLED
-       (device == AUDIO_DEVICE_OUT_PROXY) ||
-#endif
-       (device == AUDIO_DEVICE_OUT_AUX_DIGITAL) ||
-       (device == AUDIO_DEVICE_OUT_USB_ACCESSORY) ||
-       (device == AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET)) {
-        if (!virt_ctxt->temp_disabled) {
-            if (effect_is_active(&virt_ctxt->common)) {
-                offload_virtualizer_set_enable_flag(&(virt_ctxt->offload_virt), false);
-                if (virt_ctxt->ctl)
-                    offload_virtualizer_send_params(virt_ctxt->ctl,
-                                                    &virt_ctxt->offload_virt,
-                                                    OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG);
-                if (virt_ctxt->hw_acc_fd > 0)
-                    hw_acc_virtualizer_send_params(virt_ctxt->hw_acc_fd,
-                                                   &virt_ctxt->offload_virt,
-                                                   OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG);
+
+    if (virt_ctxt->forced_device == AUDIO_DEVICE_NONE) {
+        // default case unless configuration is forced
+        if (virtualizer_is_device_supported(device) == false) {
+            if (!virt_ctxt->temp_disabled) {
+                if (effect_is_active(&virt_ctxt->common)) {
+                    offload_virtualizer_set_enable_flag(&(virt_ctxt->offload_virt), false);
+                    if (virt_ctxt->ctl)
+                        offload_virtualizer_send_params(virt_ctxt->ctl,
+                                                        &virt_ctxt->offload_virt,
+                                                        OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG);
+                    if (virt_ctxt->hw_acc_fd > 0)
+                        hw_acc_virtualizer_send_params(virt_ctxt->hw_acc_fd,
+                                                       &virt_ctxt->offload_virt,
+                                                       OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG);
+                }
+                virt_ctxt->temp_disabled = true;
+                ALOGI("%s: ctxt %p, disabled based on device", __func__, virt_ctxt);
             }
-            virt_ctxt->temp_disabled = true;
-            ALOGI("%s: ctxt %p, disabled based on device", __func__, virt_ctxt);
-        }
-    } else {
-        if (virt_ctxt->temp_disabled) {
-            if (effect_is_active(&virt_ctxt->common)) {
-                offload_virtualizer_set_enable_flag(&(virt_ctxt->offload_virt), true);
-                if (virt_ctxt->ctl)
-                    offload_virtualizer_send_params(virt_ctxt->ctl,
-                                                    &virt_ctxt->offload_virt,
-                                                    OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG);
-                if (virt_ctxt->hw_acc_fd > 0)
-                    hw_acc_virtualizer_send_params(virt_ctxt->hw_acc_fd,
-                                                   &virt_ctxt->offload_virt,
-                                                   OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG);
+        } else {
+            if (virt_ctxt->temp_disabled) {
+                if (effect_is_active(&virt_ctxt->common)) {
+                    offload_virtualizer_set_enable_flag(&(virt_ctxt->offload_virt), true);
+                    if (virt_ctxt->ctl)
+                        offload_virtualizer_send_params(virt_ctxt->ctl,
+                                                        &virt_ctxt->offload_virt,
+                                                        OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG);
+                    if (virt_ctxt->hw_acc_fd > 0)
+                        hw_acc_virtualizer_send_params(virt_ctxt->hw_acc_fd,
+                                                       &virt_ctxt->offload_virt,
+                                                       OFFLOAD_SEND_VIRTUALIZER_ENABLE_FLAG);
+                }
+                virt_ctxt->temp_disabled = false;
             }
-            virt_ctxt->temp_disabled = false;
         }
     }
+    // else virtualization mode is forced to a certain device, nothing to do
+
     offload_virtualizer_set_device(&(virt_ctxt->offload_virt), device);
     return 0;
 }
@@ -230,6 +461,8 @@
 
     virt_ctxt->temp_disabled = false;
     virt_ctxt->hw_acc_fd = -1;
+    virt_ctxt->forced_device = AUDIO_DEVICE_NONE;
+    virt_ctxt->device = AUDIO_DEVICE_NONE;
     memset(&(virt_ctxt->offload_virt), 0, sizeof(struct virtualizer_params));
 
     return 0;
diff --git a/post_proc/virtualizer.h b/post_proc/virtualizer.h
index 440c8a2..b5293fb 100644
--- a/post_proc/virtualizer.h
+++ b/post_proc/virtualizer.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
  * Not a Contribution.
  *
  * Copyright (C) 2013 The Android Open Source Project
@@ -33,7 +33,8 @@
     struct mixer_ctl *ctl;
     int hw_acc_fd;
     bool temp_disabled;
-    uint32_t device;
+    audio_devices_t forced_device;
+    audio_devices_t device;
     struct virtualizer_params offload_virt;
 } virtualizer_context_t;