Merge "Revert "For msm8996, add dep on sound_trigger.primary_headers""
am: 4bc96ee9d6

Change-Id: Icc2cea6990b9702eef510cf56e067290caed0432
diff --git a/Android.mk b/Android.mk
index 9c5e7ff..65bf578 100644
--- a/Android.mk
+++ b/Android.mk
@@ -1,6 +1,6 @@
 # TODO:  Find a better way to separate build configs for ADP vs non-ADP devices
 ifneq ($(TARGET_BOARD_AUTO),true)
-  ifneq ($(filter msm8960 msm8226 msm8x26 msm8x84 msm8084 msm8992 msm8994 msm8996 msm8909 msm8952,$(TARGET_BOARD_PLATFORM)),)
+  ifneq ($(filter msm8960 msm8226 msm8x26 msm8x84 msm8084 msm8992 msm8994 msm8996 msm8909 msm8952 msm8998,$(TARGET_BOARD_PLATFORM)),)
 
     MY_LOCAL_PATH := $(call my-dir)
 
diff --git a/hal/Android.mk b/hal/Android.mk
index 781e573..f3d2798 100644
--- a/hal/Android.mk
+++ b/hal/Android.mk
@@ -10,7 +10,7 @@
 ifneq ($(filter msm8960,$(TARGET_BOARD_PLATFORM)),)
   LOCAL_CFLAGS += -DMAX_TARGET_SPECIFIC_CHANNEL_CNT="2"
 endif
-ifneq ($(filter msm8974 msm8226 msm8084 msm8992 msm8994 msm8996,$(TARGET_BOARD_PLATFORM)),)
+ifneq ($(filter msm8974 msm8226 msm8084 msm8992 msm8994 msm8996 msm8998,$(TARGET_BOARD_PLATFORM)),)
   # B-family platform uses msm8974 code base
   AUDIO_PLATFORM = msm8974
 ifneq ($(filter msm8974,$(TARGET_BOARD_PLATFORM)),)
@@ -41,6 +41,12 @@
   LOCAL_CFLAGS += -DKPI_OPTIMIZE_ENABLED
   MULTIPLE_HW_VARIANTS_ENABLED := true
 endif
+ifneq ($(filter msm8998,$(TARGET_BOARD_PLATFORM)),)
+  LOCAL_CFLAGS := -DPLATFORM_MSM8998
+  LOCAL_CFLAGS += -DMAX_TARGET_SPECIFIC_CHANNEL_CNT="4"
+  LOCAL_CFLAGS += -DKPI_OPTIMIZE_ENABLED
+  MULTIPLE_HW_VARIANTS_ENABLED := true
+endif
 endif
 
 ifneq ($(filter msm8916 msm8909 msm8952,$(TARGET_BOARD_PLATFORM)),)
@@ -87,6 +93,12 @@
 	$(LOCAL_PATH)/voice_extn \
 	external/expat/lib
 
+ifeq ($(strip $(AUDIO_FEATURE_ENABLED_SMART_PA_TFA_98XX)),true)
+    LOCAL_SHARED_LIBRARIES += libexTfa98xx
+    LOCAL_CFLAGS += -DSMART_PA_TFA_98XX_SUPPORTED
+    LOCAL_SRC_FILES += audio_extn/tfa_98xx.c
+endif
+
 ifeq ($(strip $(AUDIO_FEATURE_ENABLED_MULTI_VOICE_SESSIONS)),true)
     LOCAL_CFLAGS += -DMULTI_VOICE_SESSION_ENABLED
     LOCAL_SRC_FILES += voice_extn/voice_extn.c
@@ -122,7 +134,7 @@
     LOCAL_SRC_FILES += audio_extn/dsm_feedback.c
 endif
 
-ifneq ($(filter msm8992 msm8994 msm8996,$(TARGET_BOARD_PLATFORM)),)
+ifneq ($(filter msm8992 msm8994 msm8996 msm8998,$(TARGET_BOARD_PLATFORM)),)
   # push codec/mad calibration to HW dep node
   # applicable to msm8992/8994 or newer platforms
   LOCAL_CFLAGS += -DHWDEP_CAL_ENABLED
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index 4758fc8..f53c9f2 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -57,6 +57,8 @@
 #define audio_extn_hfp_is_active(adev)                  (0)
 #define audio_extn_hfp_get_usecase()                    (-1)
 #define audio_extn_hfp_set_parameters(adev, params)     (0)
+#define audio_extn_hfp_set_mic_mute(adev, state)        (0)
+
 #else
 bool audio_extn_hfp_is_active(struct audio_device *adev);
 
@@ -64,6 +66,8 @@
 
 void audio_extn_hfp_set_parameters(struct audio_device *adev,
                                     struct str_parms *parms);
+int audio_extn_hfp_set_mic_mute(struct audio_device *adev, bool state);
+
 #endif
 
 #ifndef SOUND_TRIGGER_ENABLED
diff --git a/hal/audio_extn/hfp.c b/hal/audio_extn/hfp.c
index ebc0fd2..07d5469 100644
--- a/hal/audio_extn/hfp.c
+++ b/hal/audio_extn/hfp.c
@@ -27,6 +27,7 @@
 #include "platform_api.h"
 #include <stdlib.h>
 #include <cutils/str_parms.h>
+#include "audio_extn/tfa_98xx.h"
 
 #define AUDIO_PARAMETER_HFP_ENABLE            "hfp_enable"
 #define AUDIO_PARAMETER_HFP_SET_SAMPLING_RATE "hfp_set_sampling_rate"
@@ -34,6 +35,10 @@
 #define AUDIO_PARAMETER_HFP_VOL_MIXER_CTL     "hfp_vol_mixer_ctl"
 #define AUDIO_PARAMATER_HFP_VALUE_MAX         128
 
+#define AUDIO_PARAMETER_KEY_HFP_MIC_VOLUME "hfp_mic_volume"
+#define PLAYBACK_VOLUME_MAX 0x2000
+#define CAPTURE_VOLUME_DEFAULT                (15.0)
+
 static int32_t start_hfp(struct audio_device *adev,
                                struct str_parms *parms);
 
@@ -45,8 +50,10 @@
     struct pcm *hfp_pcm_rx;
     struct pcm *hfp_pcm_tx;
     float  hfp_volume;
+    float  mic_volume;
     char   hfp_vol_mixer_ctl[AUDIO_PARAMATER_HFP_VALUE_MAX];
     bool   is_hfp_running;
+    bool   mic_mute;
     audio_usecase_t ucid;
 };
 
@@ -56,8 +63,10 @@
     .hfp_pcm_rx = NULL,
     .hfp_pcm_tx = NULL,
     .hfp_volume = 0,
+    .mic_volume = CAPTURE_VOLUME_DEFAULT,
     .hfp_vol_mixer_ctl = {0, },
     .is_hfp_running = 0,
+    .mic_mute = 0,
     .ucid = USECASE_AUDIO_HFP_SCO,
 };
 static struct pcm_config pcm_config_hfp = {
@@ -79,6 +88,9 @@
     ALOGV("%s: entry", __func__);
     ALOGD("%s: (%f)\n", __func__, value);
 
+    hfpmod.hfp_volume = value;
+    audio_extn_tfa_98xx_set_voice_vol(value);
+
     if (value < 0.0) {
         ALOGW("%s: (%f) Under 0.0, assuming 0.0\n", __func__, value);
         value = 0.0;
@@ -87,7 +99,6 @@
         ALOGW("%s: Volume brought with in range (%f)\n", __func__, value);
     }
     vol  = lrint((value * 0x2000) + 0.5);
-    hfpmod.hfp_volume = value;
 
     if (!hfpmod.is_hfp_running) {
         ALOGV("%s: HFP not active, ignoring set_hfp_volume call", __func__);
@@ -119,6 +130,115 @@
     return ret;
 }
 
+
+/*Set mic volume to value.
+*
+* This interface is used for mic volume control, set mic volume as value(range 0 ~ 15).
+*/
+static int hfp_set_mic_volume(struct audio_device *adev, float value)
+{
+    int volume, ret = 0;
+    char mixer_ctl_name[128];
+    struct mixer_ctl *ctl;
+    int pcm_device_id = HFP_ASM_RX_TX;
+
+    if (!hfpmod.is_hfp_running) {
+        ALOGE("%s: HFP not active, ignoring set_hfp_mic_volume call", __func__);
+        return -EIO;
+    }
+
+    if (value < 0.0) {
+        ALOGW("%s: (%f) Under 0.0, assuming 0.0\n", __func__, value);
+        value = 0.0;
+    } else if (value > CAPTURE_VOLUME_DEFAULT) {
+        value = CAPTURE_VOLUME_DEFAULT;
+        ALOGW("%s: Volume brought within range (%f)\n", __func__, value);
+    }
+
+    value = value / CAPTURE_VOLUME_DEFAULT;
+    memset(mixer_ctl_name, 0, sizeof(mixer_ctl_name));
+    snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
+             "Playback %d Volume", pcm_device_id);
+    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;
+    }
+    volume = (int)(value * PLAYBACK_VOLUME_MAX);
+
+    ALOGD("%s: Setting volume to %d (%s)\n", __func__, volume, mixer_ctl_name);
+    if (mixer_ctl_set_value(ctl, 0, volume) < 0) {
+        ALOGE("%s: Couldn't set HFP Volume: [%d]", __func__, volume);
+        return -EINVAL;
+    }
+
+    return ret;
+}
+
+static float hfp_get_mic_volume(struct audio_device *adev)
+{
+    int volume, ret = 0;
+    char mixer_ctl_name[128];
+    struct mixer_ctl *ctl;
+    int pcm_device_id = HFP_ASM_RX_TX;
+    float value = 0.0;
+
+    if (!hfpmod.is_hfp_running) {
+        ALOGE("%s: HFP not active, ignoring set_hfp_mic_volume call", __func__);
+        return -EIO;
+    }
+
+    memset(mixer_ctl_name, 0, sizeof(mixer_ctl_name));
+    snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
+             "Playback %d Volume", pcm_device_id);
+    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;
+    }
+
+    volume = mixer_ctl_get_value(ctl, 0);
+    if ( volume < 0) {
+        ALOGE("%s: Couldn't set HFP Volume: [%d]", __func__, volume);
+        return -EINVAL;
+    }
+    ALOGD("%s: getting mic volume %d \n", __func__, volume);
+
+    value = (volume / PLAYBACK_VOLUME_MAX) * CAPTURE_VOLUME_DEFAULT;
+    if (value < 0.0) {
+        ALOGW("%s: (%f) Under 0.0, assuming 0.0\n", __func__, value);
+        value = 0.0;
+    } else if (value > CAPTURE_VOLUME_DEFAULT) {
+        value = CAPTURE_VOLUME_DEFAULT;
+        ALOGW("%s: Volume brought within range (%f)\n", __func__, value);
+    }
+
+    return value;
+}
+
+/*Set mic mute state.
+*
+* This interface is used for mic mute state control
+*/
+int audio_extn_hfp_set_mic_mute(struct audio_device *adev, bool state)
+{
+    int rc = 0;
+
+    if (state == hfpmod.mic_mute)
+        return rc;
+
+    if (state == true) {
+        hfpmod.mic_volume = hfp_get_mic_volume(adev);
+    }
+    rc = hfp_set_mic_volume(adev, (state == true) ? 0.0 : hfpmod.mic_volume);
+    adev->voice.mic_mute = state;
+    hfpmod.mic_mute = state;
+    ALOGD("%s: Setting mute state %d, rc %d\n", __func__, state, rc);
+    return rc;
+}
+
 static int32_t start_hfp(struct audio_device *adev,
                          struct str_parms *parms __unused)
 {
@@ -140,6 +260,8 @@
 
     list_add_tail(&adev->usecase_list, &uc_info->list);
 
+    audio_extn_tfa_98xx_set_mode_bt();
+
     select_devices(adev, hfpmod.ucid);
 
     pcm_dev_rx_id = platform_get_pcm_device_id(uc_info->id, PCM_PLAYBACK);
@@ -169,13 +291,16 @@
     }
     ALOGD("%s: Opening PCM capture device card_id(%d) device_id(%d)",
           __func__, adev->snd_card, pcm_dev_tx_id);
-    hfpmod.hfp_pcm_rx = pcm_open(adev->snd_card,
-                                   pcm_dev_rx_id,
-                                   PCM_OUT, &pcm_config_hfp);
-    if (hfpmod.hfp_pcm_rx && !pcm_is_ready(hfpmod.hfp_pcm_rx)) {
-        ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_pcm_rx));
-        ret = -EIO;
-        goto exit;
+
+    if (audio_extn_tfa_98xx_is_supported() == false) {
+        hfpmod.hfp_pcm_rx = pcm_open(adev->snd_card,
+                                       pcm_dev_rx_id,
+                                       PCM_OUT, &pcm_config_hfp);
+        if (hfpmod.hfp_pcm_rx && !pcm_is_ready(hfpmod.hfp_pcm_rx)) {
+            ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_pcm_rx));
+            ret = -EIO;
+            goto exit;
+        }
     }
     hfpmod.hfp_sco_tx = pcm_open(adev->snd_card,
                                   pcm_dev_asm_tx_id,
@@ -187,22 +312,33 @@
     }
     ALOGV("%s: Opening PCM capture device card_id(%d) device_id(%d)",
           __func__, adev->snd_card, pcm_dev_tx_id);
-    hfpmod.hfp_pcm_tx = pcm_open(adev->snd_card,
-                                   pcm_dev_tx_id,
-                                   PCM_IN, &pcm_config_hfp);
-    if (hfpmod.hfp_pcm_tx && !pcm_is_ready(hfpmod.hfp_pcm_tx)) {
-        ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_pcm_tx));
-        ret = -EIO;
-        goto exit;
+
+    if (audio_extn_tfa_98xx_is_supported() == false) {
+        hfpmod.hfp_pcm_tx = pcm_open(adev->snd_card,
+                                       pcm_dev_tx_id,
+                                       PCM_IN, &pcm_config_hfp);
+        if (hfpmod.hfp_pcm_tx && !pcm_is_ready(hfpmod.hfp_pcm_tx)) {
+            ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_pcm_tx));
+            ret = -EIO;
+            goto exit;
+        }
     }
     pcm_start(hfpmod.hfp_sco_rx);
     pcm_start(hfpmod.hfp_sco_tx);
-    pcm_start(hfpmod.hfp_pcm_rx);
-    pcm_start(hfpmod.hfp_pcm_tx);
+    if (audio_extn_tfa_98xx_is_supported() == false) {
+        pcm_start(hfpmod.hfp_pcm_rx);
+        pcm_start(hfpmod.hfp_pcm_tx);
+    }
+
+    audio_extn_tfa_98xx_enable_speaker();
 
     hfpmod.is_hfp_running = true;
     hfp_set_volume(adev, hfpmod.hfp_volume);
 
+    /* Set mic volume by mute status, we don't provide set mic volume in phone app, only
+    provide mute and unmute. */
+    audio_extn_hfp_set_mic_mute(adev, adev->mic_muted);
+
     ALOGD("%s: exit: status(%d)", __func__, ret);
     return 0;
 
@@ -348,6 +484,20 @@
         ALOGD("%s: set_hfp_volume usecase, Vol: [%f]", __func__, vol);
         hfp_set_volume(adev, vol);
     }
+
+    memset(value, 0, sizeof(value));
+    ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_HFP_MIC_VOLUME,
+                            value, sizeof(value));
+    if (ret >= 0) {
+        if (sscanf(value, "%f", &vol) != 1){
+            ALOGE("%s: error in retrieving hfp mic volume", __func__);
+            ret = -EIO;
+            goto exit;
+        }
+        ALOGD("%s: set_hfp_mic_volume usecase, Vol: [%f]", __func__, vol);
+        hfp_set_mic_volume(adev, vol);
+    }
+
 exit:
     ALOGV("%s Exit",__func__);
 }
diff --git a/hal/audio_extn/tfa_98xx.c b/hal/audio_extn/tfa_98xx.c
new file mode 100644
index 0000000..41523b5
--- /dev/null
+++ b/hal/audio_extn/tfa_98xx.c
@@ -0,0 +1,545 @@
+/*
+ * Copyright (C) 2013-2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "tfa_98xx"
+/*#define LOG_NDEBUG 0*/
+#include <cutils/log.h>
+
+#include <stdlib.h>
+#include <audio_hw.h>
+#include <dlfcn.h>
+#include "audio_extn.h"
+#include <platform.h>
+#include <math.h>
+
+#define LIB_SPEAKER_BUNDLE "/system/lib/libexTfa98xx.so"
+
+
+enum exTfa98xx_Audio_Mode
+{
+    Audio_Mode_None = -1,
+    Audio_Mode_Music_Normal,
+    Audio_Mode_Hfp_Client,
+    Audio_Mode_Voice,
+    Audio_Mode_Hs_Hfp,
+    Audio_Mode_Max
+};
+typedef enum exTfa98xx_Audio_Mode exTfa98xx_audio_mode_t;
+
+enum exTfa98xx_Func_Mode
+{
+    Func_Mode_None = -1,
+    Func_Mode_Speaker,
+    Func_Mode_BT
+};
+typedef enum exTfa98xx_Func_Mode exTfa98xx_func_mode_t;
+
+#define I2S_CLOCK_ENABLE        1
+#define I2S_CLOCK_DISABLE       0
+#define HFP_MAX_VOLUME          (15.000000)
+#define TFA_98XX_HFP_VSETPS     (5.0)
+
+exTfa98xx_audio_mode_t current_audio_mode = Audio_Mode_None;
+
+typedef int (*set_speaker_on_t)(exTfa98xx_audio_mode_t);
+typedef int (*set_speaker_off_t)(void);
+typedef int (*set_speaker_calibration_t)(int);
+typedef void (*set_speaker_volume_step_t)(int, int);
+
+
+struct speaker_data {
+    struct audio_device *adev;
+    void *speaker_bundle;
+    set_speaker_on_t set_speaker_on;
+    set_speaker_off_t set_speaker_off;
+    set_speaker_calibration_t set_speaker_calibration;
+    set_speaker_volume_step_t set_speaker_volume_step;
+    int ref_cnt[Audio_Mode_Max];
+    int route_cnt[Audio_Mode_Max];
+    bool update_ref_cnt;
+};
+
+struct speaker_data *tfa98xx_speaker_data = NULL;
+
+static struct speaker_data* open_speaker_bundle()
+{
+    struct speaker_data *sd = calloc(1, sizeof(struct speaker_data));
+
+    sd->speaker_bundle = dlopen(LIB_SPEAKER_BUNDLE, RTLD_NOW);
+    if (sd->speaker_bundle == NULL) {
+        ALOGE("%s: DLOPEN failed for %s", __func__, LIB_SPEAKER_BUNDLE);
+        goto error;
+    } else {
+        ALOGV("%s: DLOPEN successful for %s", __func__, LIB_SPEAKER_BUNDLE);
+
+        sd->set_speaker_on = (set_speaker_on_t)dlsym(sd->speaker_bundle,
+                                             "exTfa98xx_speakeron");
+        if (sd->set_speaker_on == NULL) {
+            ALOGE("%s: dlsym error %s for exTfa98xx_speakeron", __func__,
+                  dlerror());
+            goto error;
+        }
+        sd->set_speaker_off = (set_speaker_off_t)dlsym(sd->speaker_bundle,
+                                             "exTfa98xx_speakeroff");
+        if (sd->set_speaker_off == NULL) {
+            ALOGE("%s: dlsym error %s for exTfa98xx_speakeroff", __func__,
+                  dlerror());
+            goto error;
+        }
+        sd->set_speaker_volume_step = (set_speaker_volume_step_t)dlsym(sd->speaker_bundle,
+                                             "exTfa98xx_setvolumestep");
+        if (sd->set_speaker_volume_step == NULL) {
+            ALOGE("%s: dlsym error %s for exTfa98xx_setvolumestep",
+                  __func__, dlerror());
+            goto error;
+        }
+        sd->set_speaker_calibration = (set_speaker_calibration_t)dlsym(sd->speaker_bundle,
+                                             "exTfa98xx_calibration");
+        if (sd->set_speaker_calibration == NULL) {
+            ALOGE("%s: dlsym error %s for exTfa98xx_calibration",
+                  __func__, dlerror());
+            goto error;
+        }
+    }
+    return sd;
+
+error:
+    free(sd);
+    return 0;
+}
+
+static void close_speaker_bundle(struct speaker_data *sd)
+{
+    if (sd != NULL) {
+        dlclose(sd->speaker_bundle);
+        free(sd);
+        sd = NULL;
+    }
+}
+
+static int adev_i2s_clock_operation(int enable, struct audio_device *adev, char *paths)
+{
+    int ret = -1;
+
+    ALOGD("%s: mixer paths is: %s, enable: %d\n", __func__, paths, enable);
+    if(I2S_CLOCK_ENABLE == enable) {
+        ret = audio_route_apply_and_update_path(adev->audio_route, paths);
+        if(ret) {
+            ALOGE("%s: audio_route_apply_and_update_path return %d\n", __func__, ret);
+            return ret;
+        }
+    } else {
+        ret = audio_route_reset_and_update_path(adev->audio_route, paths);
+        if(ret) {
+            ALOGE("%s: audio_route_reset_and_update_path return %d\n", __func__, ret);
+            return ret;
+        }
+    }
+    return 0;
+}
+
+static int tfa_98xx_set_audio_mode(int enable, struct audio_device *adev, exTfa98xx_audio_mode_t audio_mode)
+{
+    char paths[32] = "init_smart_pa";
+
+    switch(audio_mode) {
+        case Audio_Mode_Music_Normal:
+            strcat(paths, " music");
+            break;
+        case Audio_Mode_Voice:
+        case Audio_Mode_Hfp_Client:
+        case Audio_Mode_Hs_Hfp:
+            strcat(paths, " voice");
+            break;
+        default:
+            ALOGE("%s: function %d not support!\n",__func__, audio_mode);
+            return -EINVAL;
+    }
+
+    ALOGV("%s: mixer paths is: %s, enable: %d\n", __func__, paths, enable);
+    adev_i2s_clock_operation(enable, adev, paths);
+    return 0;
+
+}
+
+static exTfa98xx_audio_mode_t tfa_98xx_get_audio_mode(struct speaker_data *data)
+{
+    exTfa98xx_audio_mode_t tfa_98xx_audio_mode = Audio_Mode_None;
+    struct listnode *node;
+    struct audio_usecase *usecase;
+    audio_mode_t mode = data->adev->mode;
+    int i = 0;
+
+    ALOGV("%s: enter\n", __func__);
+
+    for (i = 0; i < Audio_Mode_Max; i++)
+        data->route_cnt[i] = 0;
+
+    list_for_each(node, &data->adev->usecase_list) {
+        usecase = node_to_item(node, struct audio_usecase, list);
+        if (usecase->devices & AUDIO_DEVICE_OUT_ALL_SCO) {
+            if(data->adev->snd_dev_ref_cnt[usecase->out_snd_device] != 0) {
+                tfa_98xx_audio_mode = Audio_Mode_Hs_Hfp;
+                data->route_cnt[tfa_98xx_audio_mode]++;
+                ALOGV("%s: audio_mode hs_hfp\n", __func__);
+            }
+        } else if (usecase->devices & AUDIO_DEVICE_OUT_SPEAKER) {
+            if ((mode == AUDIO_MODE_IN_CALL) || audio_extn_hfp_is_active(data->adev)) {
+                if (audio_extn_hfp_is_active(data->adev)) {
+                    if(data->adev->snd_dev_ref_cnt[usecase->out_snd_device] != 0) {
+                        tfa_98xx_audio_mode = Audio_Mode_Hfp_Client;
+                        data->route_cnt[tfa_98xx_audio_mode]++;
+                        ALOGV("%s: audio_mode hfp client\n", __func__);
+                    }
+                } else {
+                    if(data->adev->snd_dev_ref_cnt[usecase->out_snd_device] != 0) {
+                        tfa_98xx_audio_mode = Audio_Mode_Voice;
+                        data->route_cnt[tfa_98xx_audio_mode]++;
+                        ALOGV("%s: audio_mode voice\n", __func__);
+                    }
+                }
+            } else {
+                if (data->adev->snd_dev_ref_cnt[usecase->out_snd_device] != 0) {
+                    tfa_98xx_audio_mode = Audio_Mode_Music_Normal;
+                    data->route_cnt[tfa_98xx_audio_mode]++;
+                    ALOGV("%s: tfa_98xx_audio_mode music\n", __func__);
+                }
+            }
+        } else {
+            ALOGE("%s: no device match \n", __func__);
+        }
+    }
+    ALOGV("%s: tfa_98xx_audio_mode %d exit\n", __func__, tfa_98xx_audio_mode);
+
+    return tfa_98xx_audio_mode;
+}
+
+static int tfa_98xx_set_func_mode(int enable, struct audio_device *adev, exTfa98xx_func_mode_t func_mode)
+{
+    struct speaker_data *data = tfa98xx_speaker_data;
+    char paths[32] = "init_smart_pa";
+
+    if (data) {
+        switch(func_mode) {
+            case Func_Mode_Speaker:
+                strcat(paths, " func_speaker");
+                break;
+            case Func_Mode_BT:
+                strcat(paths, " func_bt");
+                break;
+            default:
+                ALOGE("%s: function %d not support!\n",__func__, func_mode);
+                return -EINVAL;
+        }
+
+        ALOGV("%s: mixer paths is: %s, enable: %d\n", __func__, paths, enable);
+        adev_i2s_clock_operation(enable, adev, paths);
+    }
+    return 0;
+}
+
+static exTfa98xx_func_mode_t tfa_98xx_get_func_mode(exTfa98xx_audio_mode_t audio_mode)
+{
+    exTfa98xx_func_mode_t func_mode = Func_Mode_None;
+
+    switch(audio_mode) {
+        case Audio_Mode_Music_Normal:
+        case Audio_Mode_Voice:
+            ALOGV("%s: tfa_98xx_func_mode speaker \n", __func__);
+            func_mode = Func_Mode_Speaker;
+            break;
+        case Audio_Mode_Hfp_Client:
+        case Audio_Mode_Hs_Hfp:
+            ALOGV("%s: tfa_98xx_func_mode bt \n", __func__);
+            func_mode = Func_Mode_BT;
+            break;
+        default:
+            break;
+    }
+    return func_mode;
+}
+
+static void tfa_98xx_disable_speaker(void)
+{
+    struct speaker_data *data = tfa98xx_speaker_data;
+    int ret = 0;
+
+    ret = data->set_speaker_off();
+    if (ret) {
+        ALOGE("%s: exTfa98xx_speakeroff failed result = %d\n", __func__, ret);
+        goto on_error;
+    }
+
+    ret = tfa_98xx_set_audio_mode(I2S_CLOCK_DISABLE, data->adev, current_audio_mode);
+    if (ret) {
+        ALOGE("%s: tfa_98xx_set_audio_mode disable failed return %d\n", __func__, ret);
+        goto on_error;
+    }
+    current_audio_mode = Audio_Mode_None;
+on_error:
+    return;
+
+}
+
+
+void audio_extn_tfa_98xx_disable_speaker(snd_device_t snd_device)
+{
+    struct speaker_data *data = tfa98xx_speaker_data;
+    int i = 0;
+    exTfa98xx_audio_mode_t new_audio_mode = Audio_Mode_None;
+
+    ALOGV("%s: enter\n", __func__);
+
+    if (data) {
+        if ((current_audio_mode == Audio_Mode_None) || (snd_device > SND_DEVICE_OUT_END))
+            goto on_exit;
+
+        switch(snd_device) {
+            case SND_DEVICE_OUT_SPEAKER:
+                new_audio_mode = Audio_Mode_Music_Normal;
+                break;
+            case SND_DEVICE_OUT_VOICE_SPEAKER:
+                new_audio_mode = Audio_Mode_Voice;
+                break;
+            case SND_DEVICE_OUT_VOICE_SPEAKER_HFP:
+                new_audio_mode = Audio_Mode_Hfp_Client;
+                break;
+            case SND_DEVICE_OUT_BT_SCO:
+                new_audio_mode = Audio_Mode_Hs_Hfp;
+                break;
+            default:
+                break;
+        }
+
+        if ((new_audio_mode == Audio_Mode_None) || (data->ref_cnt[new_audio_mode] <= 0)) {
+            ALOGE("%s: device ref cnt is already 0", __func__);
+            goto on_exit;
+        }
+
+        data->ref_cnt[new_audio_mode]--;
+
+        for (i = 0; i < Audio_Mode_Max; i++) {
+            if (data->ref_cnt[i] > 0) {
+                ALOGD("%s: exTfa98xx_speaker still in use\n", __func__);
+                goto on_exit;
+            }
+        }
+
+        if (data->adev->enable_hfp)
+            data->set_speaker_volume_step(0, 0);
+
+        tfa_98xx_disable_speaker();
+    }
+
+    ALOGV("%s: exit\n", __func__);
+on_exit:
+    return;
+}
+
+int audio_extn_tfa_98xx_enable_speaker(void)
+{
+    struct speaker_data *data = tfa98xx_speaker_data;
+    exTfa98xx_audio_mode_t new_audio_mode = Audio_Mode_Music_Normal;
+    int ret = 0;
+    int i = 0;
+
+    ALOGV("%s: enter\n", __func__);
+
+    if (data) {
+
+        new_audio_mode = tfa_98xx_get_audio_mode(data);
+        if ((new_audio_mode != Audio_Mode_None) && (data->ref_cnt[new_audio_mode] >= 1)) {
+            ALOGD("%s, mode %d already active!", __func__, new_audio_mode);
+            data->ref_cnt[new_audio_mode]++;
+            goto on_exit;
+        }
+
+        ret = tfa_98xx_set_audio_mode(I2S_CLOCK_ENABLE, data->adev, new_audio_mode);
+        if (ret) {
+            ALOGE("%s: tfa_98xx_set_audio_mode enable failed return %d\n", __func__, ret);
+            goto on_exit;
+        }
+
+        ret = data->set_speaker_on(new_audio_mode);
+        if (ret) {
+            ALOGE("%s: exTfa98xx_speakeron failed result = %d\n", __func__, ret);
+            goto on_exit;
+        }
+
+        current_audio_mode = new_audio_mode;
+        for (i = 0; i < Audio_Mode_Max; i++) {
+            data->ref_cnt[i] = data->route_cnt[i];
+        }
+        data->update_ref_cnt = false;
+    }
+
+    ALOGV("%s: exit\n", __func__);
+
+on_exit:
+    return ret;
+
+}
+
+void audio_extn_tfa_98xx_set_mode(void)
+{
+    int ret = 0;
+    struct speaker_data *data = tfa98xx_speaker_data;
+    exTfa98xx_audio_mode_t new_audio_mode = Audio_Mode_None;
+    exTfa98xx_func_mode_t new_func_mode = Func_Mode_None;
+
+    ALOGV("%s: enter\n", __func__);
+
+    if (data) {
+        new_audio_mode = tfa_98xx_get_audio_mode(data);
+
+        new_func_mode = tfa_98xx_get_func_mode(new_audio_mode);
+        if (new_func_mode == Func_Mode_None)
+            return;
+
+        ret = tfa_98xx_set_func_mode(I2S_CLOCK_ENABLE, data->adev, new_func_mode);
+        if (ret) {
+            ALOGE("%s: tfa_98xx_set_func_mode enable return %d\n", __func__, ret);
+        }
+        data->update_ref_cnt = true;
+    }
+
+    ALOGV("%s: exit\n", __func__);
+}
+
+void audio_extn_tfa_98xx_set_mode_bt(void)
+{
+    struct speaker_data *data = tfa98xx_speaker_data;
+    int ret = 0;
+
+    if (data) {
+        ret = tfa_98xx_set_func_mode(I2S_CLOCK_ENABLE, data->adev, Func_Mode_BT);
+        if (ret) {
+            ALOGE("%s: tfa_98xx_set_func_mode enable return %d\n", __func__, ret);
+        }
+    }
+}
+
+void audio_extn_tfa_98xx_update(void)
+{
+    struct speaker_data *data = tfa98xx_speaker_data;
+    exTfa98xx_audio_mode_t new_audio_mode = Audio_Mode_Music_Normal;
+
+    ALOGD("%s: enter\n", __func__);
+
+    if (data) {
+
+        new_audio_mode = tfa_98xx_get_audio_mode(data);
+        if (new_audio_mode <= current_audio_mode) {
+            ALOGE("%s: audio_extn_tfa_98xx_update same mode\n", __func__);
+            if (data->update_ref_cnt == true) {
+                data->ref_cnt[new_audio_mode]++;
+                data->update_ref_cnt = false;
+            }
+            goto on_error;
+        }
+
+        if (current_audio_mode != Audio_Mode_None) {
+            tfa_98xx_disable_speaker();
+        }
+
+        audio_extn_tfa_98xx_enable_speaker();
+
+    }
+
+    ALOGV("%s: exit\n", __func__);
+on_error:
+    return;
+
+}
+
+void audio_extn_tfa_98xx_set_voice_vol(float vol)
+{
+    struct speaker_data *data = tfa98xx_speaker_data;
+    int vsteps = 0;
+
+    if (data) {
+        if (data->adev->enable_hfp) {
+            if (vol < 0.0) {
+                vol = 0.0;
+            } else {
+                vol = ((vol > HFP_MAX_VOLUME) ? 1.0 : (vol / HFP_MAX_VOLUME));
+            }
+            vsteps = (int)floorf((1.0 - vol) * TFA_98XX_HFP_VSETPS);
+        } else {
+            return;
+        }
+        ALOGD("%s: vsteps %d\n", __func__, vsteps);
+        data->set_speaker_volume_step(vsteps, vsteps);
+    }
+}
+
+bool audio_extn_tfa_98xx_is_supported(void)
+{
+    struct speaker_data *data = tfa98xx_speaker_data;
+    if (data)
+        return true;
+    else
+        return false;
+}
+
+int audio_extn_tfa_98xx_init(struct audio_device *adev)
+{
+    int ret = 0;
+    struct speaker_data *data = open_speaker_bundle();
+
+    ALOGV("%s: enter\n", __func__);
+
+    if (data) {
+        ret = tfa_98xx_set_audio_mode(I2S_CLOCK_ENABLE, adev, Audio_Mode_Music_Normal);
+        if (ret) {
+            ALOGE("%s: tfa_98xx_set_audio_mode enable return %d\n", __func__, ret);
+            goto err_init;
+        }
+
+        ret = data->set_speaker_calibration(0);
+        if (ret) {
+            ALOGE("%s: exTfa98xx_calibration return %d\n", __func__, ret);
+        }
+
+        ret = tfa_98xx_set_audio_mode(I2S_CLOCK_DISABLE, adev, Audio_Mode_Music_Normal);
+        if (ret) {
+            ALOGE("%s: tfa_98xx_set_audio_mode disable return %d\n", __func__, ret);
+            goto err_init;
+        }
+
+        data->adev = adev;
+        tfa98xx_speaker_data = data;
+        ALOGV("%s: exit\n", __func__);
+        return 0;
+
+    }
+
+err_init:
+    close_speaker_bundle(data);
+    return -EINVAL;
+}
+
+void audio_extn_tfa_98xx_deinit(void)
+{
+    struct speaker_data *data = tfa98xx_speaker_data;
+
+    if (data) {
+        data->set_speaker_off();
+        close_speaker_bundle(data);
+        tfa98xx_speaker_data = NULL;
+    }
+}
+
diff --git a/hal/audio_extn/tfa_98xx.h b/hal/audio_extn/tfa_98xx.h
new file mode 100644
index 0000000..444429a
--- /dev/null
+++ b/hal/audio_extn/tfa_98xx.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2013-2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TFA_98XX_H
+#define TFA_98XX_H
+
+#ifdef SMART_PA_TFA_98XX_SUPPORTED
+int audio_extn_tfa_98xx_enable_speaker(void);
+void audio_extn_tfa_98xx_disable_speaker(snd_device_t snd_device);
+void audio_extn_tfa_98xx_set_mode();
+void audio_extn_tfa_98xx_set_mode_bt(void);
+void audio_extn_tfa_98xx_update(void);
+void audio_extn_tfa_98xx_set_voice_vol(float vol);
+int audio_extn_tfa_98xx_init(struct audio_device *adev);
+void audio_extn_tfa_98xx_deinit(void);
+bool audio_extn_tfa_98xx_is_supported(void);
+#else
+#define audio_extn_tfa_98xx_enable_speaker(void)                (0)
+#define audio_extn_tfa_98xx_disable_speaker(snd_device)         (0)
+#define audio_extn_tfa_98xx_set_mode()                          (0)
+#define audio_extn_tfa_98xx_set_mode_bt()                       (0)
+#define audio_extn_tfa_98xx_update(void)                        (0)
+#define audio_extn_tfa_98xx_set_voice_vol(vol)                  (0)
+#define audio_extn_tfa_98xx_init(adev)                          (0)
+#define audio_extn_tfa_98xx_deinit(void)                        (0)
+#define audio_extn_tfa_98xx_is_supported(void)                  (false)
+#endif
+
+#endif /* TFA_98XX_H */
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index d25dd0a..0a5bccb 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -53,6 +53,7 @@
 #include "voice_extn.h"
 
 #include "sound/compress_params.h"
+#include "audio_extn/tfa_98xx.h"
 
 /* COMPRESS_OFFLOAD_FRAGMENT_SIZE must be more than 8KB and a multiple of 32KB if more than 32KB.
  * COMPRESS_OFFLOAD_FRAGMENT_SIZE * COMPRESS_OFFLOAD_NUM_FRAGMENTS must be less than 8MB. */
@@ -531,6 +532,18 @@
     return id;
 }
 
+static int audio_ssr_status(struct audio_device *adev)
+{
+    int ret = 0;
+    struct mixer_ctl *ctl;
+    const char *mixer_ctl_name = "Audio SSR Status";
+
+    ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
+    ret = mixer_ctl_get_value(ctl, 0);
+    ALOGD("%s: value: %d", __func__, ret);
+    return ret;
+}
+
 int enable_audio_route(struct audio_device *adev,
                        struct audio_usecase *usecase)
 {
@@ -659,6 +672,8 @@
         ALOGE("%s: device ref cnt is already 0", __func__);
         return -EINVAL;
     }
+    audio_extn_tfa_98xx_disable_speaker(snd_device);
+
     adev->snd_dev_ref_cnt[snd_device]--;
     if (adev->snd_dev_ref_cnt[snd_device] == 0) {
         audio_extn_dsm_feedback_enable(adev, snd_device, false);
@@ -1169,6 +1184,8 @@
     usecase->in_snd_device = in_snd_device;
     usecase->out_snd_device = out_snd_device;
 
+    audio_extn_tfa_98xx_set_mode();
+
     enable_audio_route(adev, usecase);
 
     /* Applicable only on the targets that has external modem.
@@ -1226,6 +1243,9 @@
 
     ALOGV("%s: enter: usecase(%d)", __func__, in->usecase);
 
+    if (audio_extn_tfa_98xx_is_supported() && !audio_ssr_status(adev))
+        return -EIO;
+
     if (in->card_status == CARD_STATUS_OFFLINE ||
         adev->card_status == CARD_STATUS_OFFLINE) {
         ALOGW("in->card_status or adev->card_status offline, try again");
@@ -1718,6 +1738,8 @@
     }
     register_out_stream(out);
     audio_extn_perf_lock_release();
+    audio_extn_tfa_98xx_enable_speaker();
+
     ALOGV("%s: exit", __func__);
     return ret;
 error_open:
@@ -2022,6 +2044,7 @@
                     out->routing_change = true;
                 }
                 select_devices(adev, out->usecase);
+                audio_extn_tfa_98xx_update();
             }
 
         }
@@ -2180,14 +2203,36 @@
                                        const void *buffer, size_t bytes)
 {
     struct stream_out *out = (struct stream_out *)stream;
+    struct timespec t = { .tv_sec = 0, .tv_nsec = 0 };
+    int64_t now;
+    int64_t elapsed_time_since_last_write = 0;
+    int64_t sleep_time;
 
-    /* No Output device supported other than BT for playback.
-     * Sleep for the amount of buffer duration
-     */
+    clock_gettime(CLOCK_MONOTONIC, &t);
+    now = (t.tv_sec * 1000000000LL + t.tv_nsec) / 1000;
+
     lock_output_stream(out);
-    usleep(bytes * 1000000 / audio_stream_out_frame_size(&out->stream.common) /
-            out_get_sample_rate(&out->stream.common));
+    if (out->last_write_time_us)
+        elapsed_time_since_last_write = now - out->last_write_time_us;
+    sleep_time = bytes * 1000000LL / audio_stream_out_frame_size(stream) /
+               out_get_sample_rate(&stream->common) - elapsed_time_since_last_write;
+    if (sleep_time > 0) {
+        usleep(sleep_time);
+    } else {
+        // we don't sleep when we exit standby (this is typical for a real alsa buffer).
+        sleep_time = 0;
+    }
+    out->last_write_time_us = now + sleep_time;
     pthread_mutex_unlock(&out->lock);
+    // last_write_time_us is an approximation of when the (simulated) alsa
+    // buffer is believed completely full. The usleep above waits for more space
+    // in the buffer, but by the end of the sleep the buffer is considered
+    // topped-off.
+    //
+    // On the subsequent out_write(), we measure the elapsed time spent in
+    // the mixer. This is subtracted from the sleep estimate based on frames,
+    // thereby accounting for drain in the alsa buffer during mixing.
+    // This is a crude approximation; we don't handle underruns precisely.
     return bytes;
 }
 #endif
@@ -3262,7 +3307,11 @@
 
     ALOGD("%s: state %d", __func__, (int)state);
     pthread_mutex_lock(&adev->lock);
-    ret = voice_set_mic_mute(adev, state);
+    if (audio_extn_tfa_98xx_is_supported() && adev->enable_hfp) {
+        ret = audio_extn_hfp_set_mic_mute(adev, state);
+    } else {
+        ret = voice_set_mic_mute(adev, state);
+    }
     adev->mic_muted = state;
     pthread_mutex_unlock(&adev->lock);
 
@@ -3304,6 +3353,9 @@
     if (check_input_parameters(config->sample_rate, config->format, channel_count) != 0)
         return -EINVAL;
 
+    if (audio_extn_tfa_98xx_is_supported() && (audio_extn_hfp_is_active(adev) || voice_is_in_call(adev)))
+        return -EINVAL;
+
     in = (struct stream_in *)calloc(1, sizeof(struct stream_in));
 
     pthread_mutex_init(&in->lock, (const pthread_mutexattr_t *) NULL);
@@ -3602,6 +3654,8 @@
     if (!adev)
         return 0;
 
+    audio_extn_tfa_98xx_deinit();
+
     audio_extn_snd_mon_unregister_listener(adev);
     pthread_mutex_lock(&adev_init_lock);
 
@@ -3833,6 +3887,8 @@
         ALOGV("new period_multiplier = %d", af_period_multiplier);
     }
 
+    audio_extn_tfa_98xx_init(adev);
+
     pthread_mutex_unlock(&adev_init_lock);
 
     if (adev->adm_init)
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index 798d954..2cf72e1 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -210,6 +210,8 @@
     card_status_t card_status;
 
     struct error_log error_log;
+
+    int64_t last_write_time_us;
 };
 
 struct stream_in {
diff --git a/hal/msm8916/platform.c b/hal/msm8916/platform.c
index 900a7e6..4995bd8 100644
--- a/hal/msm8916/platform.c
+++ b/hal/msm8916/platform.c
@@ -31,6 +31,7 @@
 #include "audio_extn.h"
 #include "voice_extn.h"
 #include "sound/msmcal-hwdep.h"
+#include "audio_extn/tfa_98xx.h"
 #include <dirent.h>
 #define MAX_MIXER_XML_PATH  100
 #define MIXER_XML_PATH "/system/etc/mixer_paths.xml"
@@ -551,7 +552,7 @@
     char mccmnc[PROPERTY_VALUE_MAX];
     char *ret = NULL;
 
-    property_get("gsm.sim.operator.numeric",mccmnc,"0");
+    property_get("gsm.sim.operator.numeric",mccmnc,"00000");
 
     list_for_each(node, &operator_info_list) {
         info_item = node_to_item(node, struct operator_info, list);
@@ -842,24 +843,39 @@
     }
 
     list_init(&operator_info_list);
+    bool card_verifed[MAX_SND_CARD] = {0};
+    const int retry_limit = property_get_int32("audio.snd_card.open.retries", RETRY_NUMBER);
 
-    while (snd_card_num < MAX_SND_CARD) {
-        adev->mixer = mixer_open(snd_card_num);
+    for (;;) {
+        if (snd_card_num >= MAX_SND_CARD) {
+            if (retry_num++ >= retry_limit) {
+                ALOGE("%s: Unable to find correct sound card, aborting.", __func__);
+                free(my_data);
+                my_data = NULL;
+                return NULL;
+            }
 
-        while (!adev->mixer && retry_num < RETRY_NUMBER) {
+            snd_card_num = 0;
             usleep(RETRY_US);
-            adev->mixer = mixer_open(snd_card_num);
-            retry_num++;
+            continue;
         }
 
+        if (card_verifed[snd_card_num]) {
+            ++snd_card_num;
+            continue;
+        }
+
+        adev->mixer = mixer_open(snd_card_num);
+
         if (!adev->mixer) {
             ALOGE("%s: Unable to open the mixer card: %d", __func__,
-                   snd_card_num);
-            retry_num = 0;
-            snd_card_num++;
+               snd_card_num);
+            ++snd_card_num;
             continue;
         }
 
+        card_verifed[snd_card_num] = true;
+
         snd_card_name = mixer_get_name(adev->mixer);
         ALOGV("%s: snd_card_name: %s", __func__, snd_card_name);
 
@@ -875,21 +891,21 @@
             if (!adev->audio_route) {
                 ALOGE("%s: Failed to init audio route controls, aborting.",
                        __func__);
+                hw_info_deinit(my_data->hw_info);
+                my_data->hw_info = NULL;
                 free(my_data);
+                my_data = NULL;
+                mixer_close(adev->mixer);
+                adev->mixer = NULL;
                 return NULL;
             }
             adev->snd_card = snd_card_num;
             ALOGD("%s: Opened sound card:%d", __func__, snd_card_num);
             break;
         }
-        retry_num = 0;
-        snd_card_num++;
-    }
-
-    if (snd_card_num >= MAX_SND_CARD) {
-        ALOGE("%s: Unable to find correct sound card, aborting.", __func__);
-        free(my_data);
-        return NULL;
+        ++snd_card_num;
+        mixer_close(adev->mixer);
+        adev->mixer = NULL;
     }
 
     //set max volume step for voice call
@@ -1289,13 +1305,17 @@
     else
         acdb_dev_type = ACDB_DEV_TYPE_IN;
 
-    if ((my_data->acdb_send_audio_cal_v3) && (snd_device == SND_DEVICE_IN_VOICE_SPEAKER_MIC_HFP)) {
+    if ((my_data->acdb_send_audio_cal_v3) &&
+        (snd_device == SND_DEVICE_IN_VOICE_SPEAKER_MIC_HFP) &&
+        !audio_extn_tfa_98xx_is_supported() ) {
             /* TX path calibration */
             my_data->acdb_send_audio_cal_v3(acdb_dev_id, ACDB_DEV_TYPE_IN,
                                 DEFAULT_APP_TYPE_TX_PATH, sample_rate, BUFF_IDX_0);
             my_data->acdb_send_audio_cal_v3(acdb_dev_id, ACDB_DEV_TYPE_OUT,
                                 DEFAULT_APP_TYPE_RX_PATH, sample_rate, BUFF_IDX_0);
-    } else if ((my_data->acdb_send_audio_cal_v3) && (snd_device == SND_DEVICE_OUT_VOICE_SPEAKER_HFP)) {
+    } else if ((my_data->acdb_send_audio_cal_v3) &&
+               (snd_device == SND_DEVICE_OUT_VOICE_SPEAKER_HFP) &&
+               !audio_extn_tfa_98xx_is_supported()) {
             /* RX path calibration */
             ALOGV("%s: sending audio calibration for snd_device(%d) acdb_id(%d)",
                        __func__, snd_device, acdb_dev_id);
@@ -1413,7 +1433,9 @@
     struct mixer_ctl *ctl;
     const char *mixer_ctl_name = "Voice Tx Mute";
     int ret = 0;
-    uint32_t set_values[ ] = {0};
+    uint32_t set_values[ ] = {0,
+                              ALL_SESSION_VSID,
+                              DEFAULT_MUTE_RAMP_DURATION_MS};
 
     if (audio_extn_hfp_is_active(adev))
         mixer_ctl_name = "HFP TX Mute";
@@ -1426,7 +1448,11 @@
         return -EINVAL;
     }
     ALOGV("Setting voice mute state: %d", state);
-    ret = mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values));
+    // "HFP TX mute" mixer control has xcount of 1.
+    if (audio_extn_hfp_is_active(adev))
+        ret = mixer_ctl_set_array(ctl, set_values, 1 /*count*/);
+    else
+        ret = mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values));
     return ret;
 }
 
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index 62baf5b..9d4abec 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -510,6 +510,8 @@
     {TO_NAME_INDEX(USECASE_INCALL_REC_DOWNLINK)},
     {TO_NAME_INDEX(USECASE_INCALL_REC_UPLINK_AND_DOWNLINK)},
     {TO_NAME_INDEX(USECASE_AUDIO_HFP_SCO)},
+    {TO_NAME_INDEX(USECASE_VOICEMMODE1_CALL)},
+    {TO_NAME_INDEX(USECASE_VOICEMMODE2_CALL)},
 };
 
 #define DEEP_BUFFER_PLATFORM_DELAY (29*1000LL)
@@ -562,7 +564,7 @@
     char mccmnc[PROPERTY_VALUE_MAX];
     char *ret = NULL;
 
-    property_get("gsm.sim.operator.numeric",mccmnc,"0");
+    property_get("gsm.sim.operator.numeric",mccmnc,"00000");
 
     list_for_each(node, &operator_info_list) {
         info_item = node_to_item(node, struct operator_info, list);
diff --git a/hal/voice.c b/hal/voice.c
index d2215b6..611d875 100644
--- a/hal/voice.c
+++ b/hal/voice.c
@@ -29,6 +29,7 @@
 #include "voice_extn/voice_extn.h"
 #include "platform.h"
 #include "platform_api.h"
+#include "audio_extn/tfa_98xx.h"
 
 struct pcm_config pcm_config_voice_call = {
     .channels = 1,
@@ -146,6 +147,11 @@
     disable_snd_device(adev, uc_info->out_snd_device);
     disable_snd_device(adev, uc_info->in_snd_device);
 
+    if (audio_extn_tfa_98xx_is_supported() && voice_get_mic_mute(adev)) {
+        voice_set_mic_mute(adev, false);
+        ALOGD("%s: unMute voice Tx", __func__);
+    }
+
     list_remove(&uc_info->list);
     free(uc_info);
 
@@ -211,6 +217,8 @@
     pcm_start(session->pcm_tx);
     pcm_start(session->pcm_rx);
 
+    audio_extn_tfa_98xx_enable_speaker();
+
     /* Enable sidetone only when no calls are already active */
     if (!voice_is_call_state_active(adev))
         voice_set_sidetone(adev, uc_info->out_snd_device, true);
@@ -531,6 +539,7 @@
                   use_case_table[usecase->id]);
             usecase->stream.out = adev->current_call_output;
             select_devices(adev, usecase->id);
+            audio_extn_tfa_98xx_update();
         }
     }
 }
diff --git a/post_proc/equalizer.c b/post_proc/equalizer.c
index 7cff348..1cd40d2 100644
--- a/post_proc/equalizer.c
+++ b/post_proc/equalizer.c
@@ -265,8 +265,12 @@
     case EQ_PARAM_BAND_LEVEL:
 	ALOGV("%s: EQ_PARAM_BAND_LEVEL", __func__);
         param2 = *param_tmp;
-        if (param2 >= NUM_EQ_BANDS) {
+        if (param2 < 0 || param2 >= NUM_EQ_BANDS) {
             p->status = -EINVAL;
+            if (param2 < 0) {
+                android_errorWriteLog(0x534e4554, "32438598");
+                ALOGW("\tERROR EQ_PARAM_BAND_LEVEL band %d", param2);
+            }
             break;
         }
         *(int16_t *)value = (int16_t)equalizer_get_band_level(eq_ctxt, param2);
@@ -275,8 +279,12 @@
     case EQ_PARAM_CENTER_FREQ:
 	ALOGV("%s: EQ_PARAM_CENTER_FREQ", __func__);
         param2 = *param_tmp;
-        if (param2 >= NUM_EQ_BANDS) {
-           p->status = -EINVAL;
+        if (param2 < 0 || param2 >= NUM_EQ_BANDS) {
+            p->status = -EINVAL;
+            if (param2 < 0) {
+                android_errorWriteLog(0x534e4554, "32436341");
+                ALOGW("\tERROR EQ_PARAM_CENTER_FREQ band %d", param2);
+            }
             break;
         }
         *(int32_t *)value = equalizer_get_center_frequency(eq_ctxt, param2);
@@ -285,8 +293,12 @@
     case EQ_PARAM_BAND_FREQ_RANGE:
 	ALOGV("%s: EQ_PARAM_BAND_FREQ_RANGE", __func__);
         param2 = *param_tmp;
-        if (param2 >= NUM_EQ_BANDS) {
+        if (param2 < 0 || param2 >= NUM_EQ_BANDS) {
             p->status = -EINVAL;
+            if (param2 < 0) {
+                android_errorWriteLog(0x534e4554, "32247948");
+                ALOGW("\tERROR EQ_PARAM_BAND_FREQ_RANGE band %d", param2);
+            }
            break;
         }
        equalizer_get_band_freq_range(eq_ctxt, param2, (uint32_t *)value,
@@ -313,9 +325,14 @@
 	ALOGV("%s: EQ_PARAM_GET_PRESET_NAME", __func__);
         param2 = *param_tmp;
 	ALOGV("param2: %d", param2);
-        if (param2 >= equalizer_get_num_presets(eq_ctxt)) {
-            p->status = -EINVAL;
-            break;
+        if ((param2 < 0 && param2 != PRESET_CUSTOM) ||
+            param2 >= equalizer_get_num_presets(eq_ctxt)) {
+                p->status = -EINVAL;
+                if (param2 < 0) {
+                    android_errorWriteLog(0x534e4554, "32588016");
+                    ALOGW("\tERROR EQ_PARAM_GET_PRESET_NAME preset %d", param2);
+                }
+                break;
         }
         name = (char *)value;
         strlcpy(name, equalizer_get_preset_name(eq_ctxt, param2), p->vsize - 1);
@@ -373,8 +390,12 @@
 	ALOGV("EQ_PARAM_BAND_LEVEL");
         band =  *param_tmp;
         level = (int32_t)(*(int16_t *)value);
-        if (band >= NUM_EQ_BANDS) {
-           p->status = -EINVAL;
+        if (band < 0 || band >= NUM_EQ_BANDS) {
+            p->status = -EINVAL;
+            if (band < 0) {
+                android_errorWriteLog(0x534e4554, "32585400");
+                ALOGW("\tERROR EQ_PARAM_BAND_LEVEL band %d", band);
+            }
             break;
         }
         equalizer_set_band_level(eq_ctxt, band, level);
diff --git a/post_proc/volume_listener.c b/post_proc/volume_listener.c
index 4a48a25..1402ae6 100644
--- a/post_proc/volume_listener.c
+++ b/post_proc/volume_listener.c
@@ -18,6 +18,7 @@
 //#define LOG_NDEBUG 0
 #include <stdlib.h>
 #include <dlfcn.h>
+#include <math.h>
 
 #include <cutils/list.h>
 #include <cutils/log.h>
@@ -229,7 +230,7 @@
 {
     // iterate through list and make decision to set new gain dep cal level for speaker device
     // 1. find all usecase active on speaker
-    // 2. find average of left and right for each usecase
+    // 2. find energy sum for each usecase
     // 3. find the highest of all the active usecase
     // 4. if new value is different than the current value then load new calibration
 
@@ -243,15 +244,22 @@
 
     ALOGV("%s ==> Start ...", __func__);
 
-    // select the highest volume on speaker device
+    float sum_energy = 0.0;
+    bool sum_energy_used = false;
+    float temp_vol = 0;
+    // compute energy sum for the active speaker device (pick loudest of both channels)
     list_for_each(node, &vol_effect_list) {
         context = node_to_item(node, struct vol_listener_context_s, effect_list_node);
         if ((context->state == VOL_LISTENER_STATE_ACTIVE) &&
-            (context->dev_id & AUDIO_DEVICE_OUT_SPEAKER) &&
-            (new_vol < (context->left_vol + context->right_vol) / 2)) {
-            new_vol = (context->left_vol + context->right_vol) / 2;
+            (context->dev_id & AUDIO_DEVICE_OUT_SPEAKER)) {
+            sum_energy_used = true;
+            temp_vol = fmax(context->left_vol, context->right_vol);
+            sum_energy += temp_vol * temp_vol;
         }
     }
+    if (sum_energy_used) {
+        new_vol = fmin(sqrt(sum_energy), 1.0);
+    }
 
     if (new_vol != current_vol) {
         ALOGV("%s:: Change in decision :: current volume is %f new volume is %f",