am 7106b08e: DO NOT MERGE - volume_listener: fix release

* commit '7106b08ee43b84978e370b9c25e89f1ed7bfbf0d':
  DO NOT MERGE - volume_listener: fix release
diff --git a/hal/Android.mk b/hal/Android.mk
index 0d931c1..0bb29b5 100644
--- a/hal/Android.mk
+++ b/hal/Android.mk
@@ -12,15 +12,19 @@
   AUDIO_PLATFORM = msm8974
 ifneq ($(filter msm8226,$(TARGET_BOARD_PLATFORM)),)
   LOCAL_CFLAGS := -DPLATFORM_MSM8x26
+  LOCAL_CFLAGS += -DMAX_TARGET_SPECIFIC_CHANNEL_CNT="2"
 endif
 ifneq ($(filter msm8084,$(TARGET_BOARD_PLATFORM)),)
   LOCAL_CFLAGS := -DPLATFORM_MSM8084
+  LOCAL_CFLAGS += -DMAX_TARGET_SPECIFIC_CHANNEL_CNT="2"
 endif
 ifneq ($(filter msm8992,$(TARGET_BOARD_PLATFORM)),)
   LOCAL_CFLAGS := -DPLATFORM_MSM8994
+  LOCAL_CFLAGS += -DMAX_TARGET_SPECIFIC_CHANNEL_CNT="4"
 endif
 ifneq ($(filter msm8994,$(TARGET_BOARD_PLATFORM)),)
   LOCAL_CFLAGS := -DPLATFORM_MSM8994
+  LOCAL_CFLAGS += -DMAX_TARGET_SPECIFIC_CHANNEL_CNT="4"
 endif
 endif
 
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 8fc7764..86072c0 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -62,6 +62,13 @@
 #define PROXY_OPEN_RETRY_COUNT           100
 #define PROXY_OPEN_WAIT_TIME             20
 
+#define MIN_CHANNEL_COUNT                1
+#define DEFAULT_CHANNEL_COUNT            2
+
+#define MAX_CHANNEL_COUNT atoi(XSTR(MAX_TARGET_SPECIFIC_CHANNEL_CNT))
+#define XSTR(x) STR(x)
+#define STR(x) #x
+
 static unsigned int configured_low_latency_capture_period_size =
         LOW_LATENCY_CAPTURE_PERIOD_SIZE;
 
@@ -71,7 +78,7 @@
 static const bool k_enable_extended_precision = false;
 
 struct pcm_config pcm_config_deep_buffer = {
-    .channels = 2,
+    .channels = DEFAULT_CHANNEL_COUNT,
     .rate = DEFAULT_OUTPUT_SAMPLING_RATE,
     .period_size = DEEP_BUFFER_OUTPUT_PERIOD_SIZE,
     .period_count = DEEP_BUFFER_OUTPUT_PERIOD_COUNT,
@@ -82,7 +89,7 @@
 };
 
 struct pcm_config pcm_config_low_latency = {
-    .channels = 2,
+    .channels = DEFAULT_CHANNEL_COUNT,
     .rate = DEFAULT_OUTPUT_SAMPLING_RATE,
     .period_size = LOW_LATENCY_OUTPUT_PERIOD_SIZE,
     .period_count = LOW_LATENCY_OUTPUT_PERIOD_COUNT,
@@ -104,7 +111,7 @@
 };
 
 struct pcm_config pcm_config_audio_capture = {
-    .channels = 2,
+    .channels = DEFAULT_CHANNEL_COUNT,
     .period_count = AUDIO_CAPTURE_PERIOD_COUNT,
     .format = PCM_FORMAT_S16_LE,
     .stop_threshold = INT_MAX,
@@ -148,6 +155,7 @@
     [USECASE_AUDIO_PLAYBACK_MULTI_CH] = "multi-channel-playback",
     [USECASE_AUDIO_PLAYBACK_OFFLOAD] = "compress-offload-playback",
     [USECASE_AUDIO_PLAYBACK_TTS] = "audio-tts-playback",
+    [USECASE_AUDIO_PLAYBACK_ULL] = "audio-ull-playback",
 
     [USECASE_AUDIO_RECORD] = "audio-record",
     [USECASE_AUDIO_RECORD_LOW_LATENCY] = "low-latency-record",
@@ -300,6 +308,8 @@
         return -EINVAL;
     }
 
+    platform_send_audio_calibration(adev->platform, snd_device);
+
     adev->snd_dev_ref_cnt[snd_device]++;
     if (adev->snd_dev_ref_cnt[snd_device] > 1) {
         ALOGV("%s: snd_device(%d: %s) is already active",
@@ -315,13 +325,6 @@
     if (audio_extn_spkr_prot_is_enabled())
          audio_extn_spkr_prot_calib_cancel(adev);
 
-    if (platform_send_audio_calibration(adev->platform, snd_device) < 0) {
-        adev->snd_dev_ref_cnt[snd_device]--;
-        audio_extn_sound_trigger_update_device_status(snd_device,
-                                    ST_EVENT_SND_DEVICE_FREE);
-        return -EINVAL;
-    }
-
     audio_extn_dsm_feedback_enable(adev, snd_device, true);
 
     if ((snd_device == SND_DEVICE_OUT_SPEAKER ||
@@ -339,6 +342,7 @@
         for (i = 0; i < num_devices; i++) {
             enable_snd_device(adev, new_snd_devices[i]);
         }
+        platform_set_speaker_gain_in_combo(adev, snd_device, true);
     } else {
         const char * dev_path = platform_get_snd_device_name(snd_device);
         ALOGD("%s: snd_device(%d: %s)", __func__, snd_device, dev_path);
@@ -377,12 +381,14 @@
             for (i = 0; i < num_devices; i++) {
                 disable_snd_device(adev, new_snd_devices[i]);
             }
+            platform_set_speaker_gain_in_combo(adev, snd_device, false);
         } else {
             audio_route_reset_and_update_path(adev->audio_route, dev_path);
         }
         audio_extn_sound_trigger_update_device_status(snd_device,
                                         ST_EVENT_SND_DEVICE_FREE);
     }
+
     return 0;
 }
 
@@ -673,6 +679,9 @@
         (usecase->in_snd_device != SND_DEVICE_NONE) &&
         (usecase->out_snd_device != SND_DEVICE_NONE)) {
         status = platform_switch_voice_call_device_pre(adev->platform);
+        /* Disable sidetone only if voice call already exists */
+        if (voice_is_call_state_active(adev))
+            voice_set_sidetone(adev, usecase->out_snd_device, false);
     }
 
     /* Disable current sound devices */
@@ -724,10 +733,14 @@
      * Enable device command should be sent to modem only after
      * enabling voice call mixer controls
      */
-    if (usecase->type == VOICE_CALL)
+    if (usecase->type == VOICE_CALL) {
         status = platform_switch_voice_call_usecase_route_post(adev->platform,
                                                                out_snd_device,
                                                                in_snd_device);
+         /* Enable sidetone only if voice call already exists */
+        if (voice_is_call_state_active(adev))
+            voice_set_sidetone(adev, out_snd_device, true);
+    }
 
     return status;
 }
@@ -820,7 +833,10 @@
         break;
     }
 
+    ALOGV("%s: pcm_prepare start", __func__);
+    pcm_prepare(in->pcm);
     ALOGV("%s: exit", __func__);
+
     return ret;
 
 error_open:
@@ -1156,6 +1172,10 @@
             }
             break;
         }
+        ALOGV("%s: pcm_prepare start", __func__);
+        if (pcm_is_ready(out->pcm))
+            pcm_prepare(out->pcm);
+
     } else {
         out->pcm = NULL;
         out->compr = compress_open(adev->snd_card, out->pcm_device_id,
@@ -1187,9 +1207,16 @@
                                   audio_format_t format,
                                   int channel_count)
 {
-    if (format != AUDIO_FORMAT_PCM_16_BIT) return -EINVAL;
+    if (format != AUDIO_FORMAT_PCM_16_BIT) {
+        ALOGE("%s: unsupported AUDIO FORMAT (%d) ", __func__, format);
+        return -EINVAL;
+    }
 
-    if ((channel_count < 1) || (channel_count > 2)) return -EINVAL;
+    if ((channel_count < MIN_CHANNEL_COUNT) || (channel_count > MAX_CHANNEL_COUNT)) {
+        ALOGE("%s: unsupported channel count (%d) passed  Min / Max (%d\%d)", __func__,
+               channel_count, MIN_CHANNEL_COUNT, MAX_CHANNEL_COUNT);
+        return -EINVAL;
+    }
 
     switch (sample_rate) {
     case 8000:
@@ -1203,6 +1230,7 @@
     case 48000:
         break;
     default:
+        ALOGE("%s: unsupported (%d) samplerate passed ", __func__, sample_rate);
         return -EINVAL;
     }
 
@@ -1289,6 +1317,9 @@
 
     pthread_mutex_lock(&out->lock);
     if (!out->standby) {
+        if (adev->adm_deregister_stream)
+            adev->adm_deregister_stream(adev->adm_data, out->handle);
+
         pthread_mutex_lock(&adev->lock);
         out->standby = true;
         if (out->usecase != USECASE_AUDIO_PLAYBACK_OFFLOAD) {
@@ -1563,6 +1594,8 @@
             out->standby = true;
             goto exit;
         }
+        if (out->usecase != USECASE_AUDIO_PLAYBACK_OFFLOAD && adev->adm_register_output_stream)
+            adev->adm_register_output_stream(adev->adm_data, out->handle, out->flags);
     }
 
     if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD) {
@@ -1589,14 +1622,22 @@
         if (out->pcm) {
             if (out->muted)
                 memset((void *)buffer, 0, bytes);
+
             ALOGVV("%s: writing buffer (%d bytes) to pcm device", __func__, bytes);
+            if (adev->adm_request_focus)
+                adev->adm_request_focus(adev->adm_data, out->handle);
+
             if (out->usecase == USECASE_AUDIO_PLAYBACK_AFE_PROXY) {
                 ret = pcm_mmap_write(out->pcm, (void *)buffer, bytes);
             }
             else
                 ret = pcm_write(out->pcm, (void *)buffer, bytes);
+
             if (ret == 0)
                 out->written += bytes / (out->config.channels * sizeof(short));
+
+            if (adev->adm_abandon_focus)
+                adev->adm_abandon_focus(adev->adm_data, out->handle);
         }
     }
 
@@ -1823,6 +1864,9 @@
     }
 
     if (!in->standby) {
+        if (adev->adm_deregister_stream)
+            adev->adm_deregister_stream(adev->adm_data, in->capture_handle);
+
         pthread_mutex_lock(&adev->lock);
         in->standby = true;
         if (in->pcm) {
@@ -1924,8 +1968,13 @@
             goto exit;
         }
         in->standby = 0;
+        if (adev->adm_register_input_stream)
+            adev->adm_register_input_stream(adev->adm_data, in->capture_handle, in->flags);
     }
 
+    if (adev->adm_request_focus)
+        adev->adm_request_focus(adev->adm_data, in->capture_handle);
+
     if (in->pcm) {
         if (in->usecase == USECASE_AUDIO_RECORD_AFE_PROXY) {
             ret = pcm_mmap_read(in->pcm, buffer, bytes);
@@ -1933,6 +1982,9 @@
             ret = pcm_read(in->pcm, buffer, bytes);
     }
 
+    if (adev->adm_abandon_focus)
+        adev->adm_abandon_focus(adev->adm_data, in->capture_handle);
+
     /*
      * Instead of writing zeroes here, we could trust the hardware
      * to always provide zeroes when muted.
@@ -2147,6 +2199,9 @@
         } else if (out->flags & AUDIO_OUTPUT_FLAG_TTS) {
             out->usecase = USECASE_AUDIO_PLAYBACK_TTS;
             out->config = pcm_config_deep_buffer;
+        } else if (out->flags & AUDIO_OUTPUT_FLAG_RAW) {
+            out->usecase = USECASE_AUDIO_PLAYBACK_ULL;
+            out->config = pcm_config_low_latency;
         } else {
             out->usecase = USECASE_AUDIO_PLAYBACK_LOW_LATENCY;
             out->config = pcm_config_low_latency;
@@ -2489,6 +2544,7 @@
     in->standby = 1;
     in->channel_mask = config->channel_mask;
     in->capture_handle = handle;
+    in->flags = flags;
 
     /* Update config params with the requested sample rate and channels */
     if (in->device == AUDIO_DEVICE_IN_TELEPHONY_RX) {
@@ -2714,10 +2770,13 @@
         for (i = 0; i < ARRAY_SIZE(adev->use_case_table); ++i) {
             pcm_params_free(adev->use_case_table[i]);
         }
+        if (adev->adm_deinit)
+            adev->adm_deinit(adev->adm_data);
         free(device);
     }
 
     pthread_mutex_unlock(&adev_init_lock);
+
     return 0;
 }
 
@@ -2844,6 +2903,29 @@
         }
     }
 
+    if (access(ADM_LIBRARY_PATH, R_OK) == 0) {
+        adev->adm_lib = dlopen(ADM_LIBRARY_PATH, RTLD_NOW);
+        if (adev->adm_lib == NULL) {
+            ALOGE("%s: DLOPEN failed for %s", __func__, ADM_LIBRARY_PATH);
+        } else {
+            ALOGV("%s: DLOPEN successful for %s", __func__, ADM_LIBRARY_PATH);
+            adev->adm_init = (adm_init_t)
+                                    dlsym(adev->adm_lib, "adm_init");
+            adev->adm_deinit = (adm_deinit_t)
+                                    dlsym(adev->adm_lib, "adm_deinit");
+            adev->adm_register_input_stream = (adm_register_input_stream_t)
+                                    dlsym(adev->adm_lib, "adm_register_input_stream");
+            adev->adm_register_output_stream = (adm_register_output_stream_t)
+                                    dlsym(adev->adm_lib, "adm_register_output_stream");
+            adev->adm_deregister_stream = (adm_deregister_stream_t)
+                                    dlsym(adev->adm_lib, "adm_deregister_stream");
+            adev->adm_request_focus = (adm_request_focus_t)
+                                    dlsym(adev->adm_lib, "adm_request_focus");
+            adev->adm_abandon_focus = (adm_abandon_focus_t)
+                                    dlsym(adev->adm_lib, "adm_abandon_focus");
+        }
+    }
+
     adev->bt_wb_speech_enabled = false;
     adev->enable_voicerx = false;
 
@@ -2873,6 +2955,9 @@
     audio_device_ref_count++;
     pthread_mutex_unlock(&adev_init_lock);
 
+    if (adev->adm_init)
+        adev->adm_data = adev->adm_init();
+
     ALOGV("%s: exit", __func__);
     return 0;
 }
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index b4b2583..39e5263 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -29,6 +29,7 @@
 
 #define VISUALIZER_LIBRARY_PATH "/system/lib/soundfx/libqcomvisualizer.so"
 #define OFFLOAD_EFFECTS_BUNDLE_LIBRARY_PATH "/system/lib/soundfx/libqcompostprocbundle.so"
+#define ADM_LIBRARY_PATH "/system/vendor/lib/libadm.so"
 
 /* Flags used to initialize acdb_settings variable that goes to ACDB library */
 #define DMIC_FLAG       0x00000002
@@ -58,6 +59,7 @@
     USECASE_AUDIO_PLAYBACK_MULTI_CH,
     USECASE_AUDIO_PLAYBACK_OFFLOAD,
     USECASE_AUDIO_PLAYBACK_TTS,
+    USECASE_AUDIO_PLAYBACK_ULL,
 
     /* HFP Use case*/
     USECASE_AUDIO_HFP_SCO,
@@ -173,6 +175,7 @@
     bool enable_ns;
 
     audio_io_handle_t capture_handle;
+    audio_input_flags_t flags;
     bool is_st_session;
     bool is_st_session_active;
 
@@ -201,6 +204,14 @@
     union stream_ptr stream;
 };
 
+typedef void* (*adm_init_t)();
+typedef void (*adm_deinit_t)(void *);
+typedef void (*adm_register_output_stream_t)(void *, audio_io_handle_t, audio_output_flags_t);
+typedef void (*adm_register_input_stream_t)(void *, audio_io_handle_t, audio_input_flags_t);
+typedef void (*adm_deregister_stream_t)(void *, audio_io_handle_t);
+typedef void (*adm_request_focus_t)(void *, audio_io_handle_t);
+typedef void (*adm_abandon_focus_t)(void *, audio_io_handle_t);
+
 struct audio_device {
     struct audio_hw_device device;
     pthread_mutex_t lock; /* see note below on mutex acquisition order */
@@ -240,6 +251,16 @@
     void *offload_effects_lib;
     int (*offload_effects_start_output)(audio_io_handle_t, int);
     int (*offload_effects_stop_output)(audio_io_handle_t, int);
+
+    void *adm_data;
+    void *adm_lib;
+    adm_init_t adm_init;
+    adm_deinit_t adm_deinit;
+    adm_register_input_stream_t adm_register_input_stream;
+    adm_register_output_stream_t adm_register_output_stream;
+    adm_deregister_stream_t adm_deregister_stream;
+    adm_request_focus_t adm_request_focus;
+    adm_abandon_focus_t adm_abandon_focus;
 };
 
 int select_devices(struct audio_device *adev,
diff --git a/hal/msm8960/platform.c b/hal/msm8960/platform.c
index 2f596ba..c78d943 100644
--- a/hal/msm8960/platform.c
+++ b/hal/msm8960/platform.c
@@ -126,6 +126,8 @@
     [SND_DEVICE_OUT_BT_SCO] = "bt-sco-headset",
     [SND_DEVICE_OUT_BT_SCO_WB] = "bt-sco-headset-wb",
     [SND_DEVICE_OUT_VOICE_HANDSET_TMUS] = "voice-handset-tmus",
+    [SND_DEVICE_OUT_VOICE_HANDSET] = "voice-handset-tmus",
+    [SND_DEVICE_OUT_VOICE_HAC_HANDSET] = "voice-handset-tmus",
     [SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES] = "voice-tty-full-headphones",
     [SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = "voice-tty-vco-headphones",
     [SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET] = "voice-tty-hco-handset",
@@ -173,6 +175,8 @@
     [SND_DEVICE_OUT_BT_SCO] = 22,
     [SND_DEVICE_OUT_BT_SCO_WB] = 39,
     [SND_DEVICE_OUT_VOICE_HANDSET_TMUS] = 81,
+    [SND_DEVICE_OUT_VOICE_HANDSET] = 81,
+    [SND_DEVICE_OUT_VOICE_HAC_HANDSET] = 81,
     [SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES] = 17,
     [SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = 17,
     [SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET] = 37,
@@ -423,6 +427,13 @@
     return -ENOSYS;
 }
 
+void platform_add_operator_specific_device(snd_device_t snd_device __unused,
+                                           const char *operator __unused,
+                                           const char *mixer_path __unused,
+                                           unsigned int acdb_id __unused)
+{
+}
+
 int platform_send_audio_calibration(void *platform, snd_device_t snd_device)
 {
     struct platform_data *my_data = (struct platform_data *)platform;
@@ -540,6 +551,11 @@
     return ret;
 }
 
+void platform_set_speaker_gain_in_combo(struct audio_device *adev __unused,
+                                        snd_device_t snd_device  __unused,
+                                        bool enable __unused) {
+}
+
 int platform_set_voice_volume(void *platform, int volume)
 {
     struct platform_data *my_data = (struct platform_data *)platform;
diff --git a/hal/msm8960/platform.h b/hal/msm8960/platform.h
index 0201772..b879c0b 100644
--- a/hal/msm8960/platform.h
+++ b/hal/msm8960/platform.h
@@ -48,6 +48,8 @@
     SND_DEVICE_OUT_BT_SCO,
     SND_DEVICE_OUT_BT_SCO_WB,
     SND_DEVICE_OUT_VOICE_HANDSET_TMUS,
+    SND_DEVICE_OUT_VOICE_HANDSET,
+    SND_DEVICE_OUT_VOICE_HAC_HANDSET,
     SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES,
     SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES,
     SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET,
@@ -92,7 +94,7 @@
 
 #define MIXER_CARD 0
 #define SOUND_CARD 0
-
+#define MIXER_PATH_MAX_LENGTH 100
 #define DEFAULT_OUTPUT_SAMPLING_RATE 48000
 
 /*
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index 03aa596..6cb46e2 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 #define LOG_TAG "msm8974_platform"
 /*#define LOG_NDEBUG 0*/
 #define LOG_NDDEBUG 0
@@ -34,9 +33,6 @@
 #define AUDIO_DATA_BLOCK_MIXER_CTL "HDMI EDID"
 #define CVD_VERSION_MIXER_CTL "CVD Version"
 
-#define DUALMIC_CONFIG_NONE 0      /* Target does not contain 2 mics */
-#define DUALMIC_CONFIG_ENDFIRE 1
-#define DUALMIC_CONFIG_BROADSIDE 2
 
 /*
  * This file will have a maximum of 38 bytes:
@@ -62,6 +58,9 @@
 
 #define DEFAULT_APP_TYPE_RX_PATH  0x11130
 
+#define TOSTRING_(x) #x
+#define TOSTRING(x) TOSTRING_(x)
+
 struct audio_block_header
 {
     int reserved;
@@ -74,6 +73,24 @@
     CAL_MODE_RTAC           = 0x4
 };
 
+#define PLATFORM_CONFIG_KEY_OPERATOR_INFO "operator_info"
+
+struct operator_info {
+    struct listnode list;
+    char *name;
+    char *mccmnc;
+};
+
+struct operator_specific_device {
+    struct listnode list;
+    char *operator;
+    char *mixer_path;
+    int acdb_id;
+};
+
+static struct listnode operator_info_list;
+static struct listnode *operator_specific_device_table[SND_DEVICE_MAX];
+
 /* Audio calibration related functions */
 typedef void (*acdb_deallocate_t)();
 typedef int  (*acdb_init_v2_cvd_t)(char *, char *);
@@ -91,7 +108,9 @@
     bool fluence_in_voice_call;
     bool fluence_in_voice_comm;
     bool fluence_in_voice_rec;
-    int  dualmic_config;
+    /* 0 = no fluence, 1 = fluence, 2 = fluence pro */
+    int  fluence_type;
+    int  source_mic_type;
     bool speaker_lr_swap;
 
     void *acdb_handle;
@@ -104,6 +123,8 @@
     char ec_ref_mixer_path[64];
 
     char *snd_card_name;
+    int max_vol_index;
+    int max_mic_count;
 };
 
 static int pcm_device_table[AUDIO_USECASE_MAX][2] = {
@@ -115,12 +136,16 @@
                                          MULTIMEDIA2_PCM_DEVICE},
     [USECASE_AUDIO_PLAYBACK_OFFLOAD] = {PLAYBACK_OFFLOAD_DEVICE,
                                         PLAYBACK_OFFLOAD_DEVICE},
-    [USECASE_AUDIO_PLAYBACK_TTS] = {MULTIMEDIA3_PCM_DEVICE,
-                                        MULTIMEDIA3_PCM_DEVICE},
+    [USECASE_AUDIO_PLAYBACK_TTS] = {MULTIMEDIA2_PCM_DEVICE,
+                                        MULTIMEDIA2_PCM_DEVICE},
+    [USECASE_AUDIO_PLAYBACK_ULL] = {MULTIMEDIA3_PCM_DEVICE,
+                                    MULTIMEDIA3_PCM_DEVICE},
+
     [USECASE_AUDIO_RECORD] = {AUDIO_RECORD_PCM_DEVICE,
                               AUDIO_RECORD_PCM_DEVICE},
     [USECASE_AUDIO_RECORD_LOW_LATENCY] = {LOWLATENCY_PCM_DEVICE,
                                           LOWLATENCY_PCM_DEVICE},
+
     [USECASE_VOICE_CALL] = {VOICE_CALL_PCM_DEVICE,
                             VOICE_CALL_PCM_DEVICE},
     [USECASE_VOICE2_CALL] = {VOICE2_CALL_PCM_DEVICE, VOICE2_CALL_PCM_DEVICE},
@@ -224,7 +249,11 @@
 
     [SND_DEVICE_IN_VOICE_RX] = "voice-rx",
 
+    [SND_DEVICE_IN_THREE_MIC] = "three-mic",
+    [SND_DEVICE_IN_QUAD_MIC] = "quad-mic",
     [SND_DEVICE_IN_CAPTURE_VI_FEEDBACK] = "vi-feedback",
+    [SND_DEVICE_IN_HANDSET_TMIC] = "three-mic",
+    [SND_DEVICE_IN_HANDSET_QMIC] = "quad-mic",
 };
 
 /* ACDB IDs (audio DSP path configuration IDs) for each sound device */
@@ -303,7 +332,11 @@
 
     [SND_DEVICE_IN_VOICE_RX] = 44,
 
+    [SND_DEVICE_IN_THREE_MIC] = 46,
+    [SND_DEVICE_IN_QUAD_MIC] = 46,
     [SND_DEVICE_IN_CAPTURE_VI_FEEDBACK] = 102,
+    [SND_DEVICE_IN_HANDSET_TMIC] = 125,
+    [SND_DEVICE_IN_HANDSET_QMIC] = 125,
 };
 
 struct name_to_index {
@@ -387,7 +420,11 @@
     {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_REC_DMIC_STEREO)},
     {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE)},
 
+    {TO_NAME_INDEX(SND_DEVICE_IN_THREE_MIC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_QUAD_MIC)},
     {TO_NAME_INDEX(SND_DEVICE_IN_CAPTURE_VI_FEEDBACK)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_TMIC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_QMIC)},
 };
 
 static char * backend_tag_table[SND_DEVICE_MAX] = {0};
@@ -398,6 +435,8 @@
     {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_LOW_LATENCY)},
     {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_MULTI_CH)},
     {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_OFFLOAD)},
+    {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_TTS)},
+    {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_ULL)},
     {TO_NAME_INDEX(USECASE_AUDIO_RECORD)},
     {TO_NAME_INDEX(USECASE_AUDIO_RECORD_LOW_LATENCY)},
     {TO_NAME_INDEX(USECASE_VOICE_CALL)},
@@ -453,6 +492,71 @@
     return is_tmus;
 }
 
+static char *get_current_operator()
+{
+    struct listnode *node;
+    struct operator_info *info_item;
+    char mccmnc[PROPERTY_VALUE_MAX];
+    char *ret = NULL;
+
+    property_get("gsm.sim.operator.numeric",mccmnc,"0");
+
+    list_for_each(node, &operator_info_list) {
+        info_item = node_to_item(node, struct operator_info, list);
+        if (strstr(info_item->mccmnc, mccmnc) != NULL) {
+            ret = info_item->name;
+        }
+    }
+
+    return ret;
+}
+
+static struct operator_specific_device *get_operator_specific_device(snd_device_t snd_device)
+{
+    struct listnode *node;
+    struct operator_specific_device *ret = NULL;
+    struct operator_specific_device *device_item;
+    char *operator_name;
+
+    operator_name = get_current_operator();
+    if (operator_name == NULL)
+        return ret;
+
+    list_for_each(node, operator_specific_device_table[snd_device]) {
+        device_item = node_to_item(node, struct operator_specific_device, list);
+        if (strcmp(operator_name, device_item->operator) == 0) {
+            ret = device_item;
+        }
+    }
+
+    return ret;
+}
+
+
+static int get_operator_specific_device_acdb_id(snd_device_t snd_device)
+{
+    struct operator_specific_device *device;
+    int ret = acdb_device_table[snd_device];
+
+    device = get_operator_specific_device(snd_device);
+    if (device != NULL)
+        ret = device->acdb_id;
+
+    return ret;
+}
+
+static const char *get_operator_specific_device_mixer_path(snd_device_t snd_device)
+{
+    struct operator_specific_device *device;
+    const char *ret = device_table[snd_device];
+
+    device = get_operator_specific_device(snd_device);
+    if (device != NULL)
+        ret = device->mixer_path;
+
+    return ret;
+}
+
 bool platform_send_gain_dep_cal(void *platform, int level)
 {
     bool ret_val = false;
@@ -712,12 +816,13 @@
 #endif
 }
 
-static void set_platform_defaults(struct platform_data * my_data __unused)
+static void set_platform_defaults(struct platform_data * my_data)
 {
     int32_t dev;
     for (dev = 0; dev < SND_DEVICE_MAX; dev++) {
         backend_tag_table[dev] = NULL;
         hw_interface_table[dev] = NULL;
+        operator_specific_device_table[dev] = NULL;
     }
 
     // To overwrite these go to the audio_platform_info.xml file.
@@ -758,6 +863,8 @@
     hw_interface_table[SND_DEVICE_OUT_VOICE_TX] = strdup("AFE_PCM_RX");
     hw_interface_table[SND_DEVICE_OUT_SPEAKER_PROTECTED] = strdup("SLIMBUS_0_RX");
     hw_interface_table[SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED] = strdup("SLIMBUS_0_RX");
+
+    my_data->max_mic_count = PLATFORM_DEFAULT_MIC_COUNT;
 }
 
 void get_cvd_version(char *cvd_version, struct audio_device *adev)
@@ -792,6 +899,7 @@
     char value[PROPERTY_VALUE_MAX];
     struct platform_data *my_data;
     int retry_num = 0, snd_card_num = 0;
+    bool dual_mic_config = false;
     const char *snd_card_name;
     char *cvd_version = NULL;
 
@@ -799,6 +907,8 @@
 
     my_data->adev = adev;
 
+    list_init(&operator_info_list);
+
     set_platform_defaults(my_data);
 
     /* Initialize platform specific ids and/or backends*/
@@ -850,20 +960,32 @@
         goto init_failed;
     }
 
-    my_data->dualmic_config = DUALMIC_CONFIG_NONE;
+    //set max volume step for voice call
+    property_get("ro.config.vc_call_vol_steps", value, TOSTRING(MAX_VOL_INDEX));
+    my_data->max_vol_index = atoi(value);
+
+    property_get("persist.audio.dualmic.config",value,"");
+    if (!strcmp("endfire", value)) {
+        dual_mic_config = true;
+    }
+
+    my_data->source_mic_type = SOURCE_DUAL_MIC;
+
     my_data->fluence_in_spkr_mode = false;
     my_data->fluence_in_voice_call = false;
     my_data->fluence_in_voice_comm = false;
     my_data->fluence_in_voice_rec = false;
 
-    property_get("persist.audio.dualmic.config",value,"");
-    if (!strcmp("broadside", value)) {
-        ALOGE("%s: Unsupported dualmic configuration", __func__);
-    } else if (!strcmp("endfire", value)) {
-        my_data->dualmic_config = DUALMIC_CONFIG_ENDFIRE;
+    property_get("ro.qc.sdk.audio.fluencetype", value, "none");
+    if (!strcmp("fluencepro", value)) {
+        my_data->fluence_type = FLUENCE_PRO_ENABLE;
+    } else if (!strcmp("fluence", value) || (dual_mic_config)) {
+        my_data->fluence_type = FLUENCE_ENABLE;
+    } else if (!strcmp("none", value)) {
+        my_data->fluence_type = FLUENCE_DISABLE;
     }
 
-    if (my_data->dualmic_config != DUALMIC_CONFIG_NONE) {
+    if (my_data->fluence_type != FLUENCE_DISABLE) {
         property_get("persist.audio.fluence.voicecall",value,"");
         if (!strcmp("true", value)) {
             my_data->fluence_in_voice_call = true;
@@ -885,6 +1007,30 @@
         }
     }
 
+    // support max to mono, example if max count is 3, usecase supports Three, dual and mono mic
+    switch (my_data->max_mic_count) {
+        case 4:
+            my_data->source_mic_type |= SOURCE_QUAD_MIC;
+        case 3:
+            my_data->source_mic_type |= SOURCE_THREE_MIC;
+        case 2:
+            my_data->source_mic_type |= SOURCE_DUAL_MIC;
+        case 1:
+            my_data->source_mic_type |= SOURCE_MONO_MIC;
+            break;
+        default:
+            ALOGE("%s: max_mic_count (%d), is not supported, setting to default",
+                   __func__, my_data->max_mic_count);
+            my_data->source_mic_type = SOURCE_MONO_MIC|SOURCE_DUAL_MIC;
+            break;
+        }
+
+    ALOGV("%s: Fluence_Type(%d) max_mic_count(%d) mic_type(0x%x) fluence_in_voice_call(%d)"
+          " fluence_in_voice_comm(%d) fluence_in_voice_rec(%d) fluence_in_spkr_mode(%d) ",
+          __func__, my_data->fluence_type, my_data->max_mic_count, my_data->source_mic_type,
+          my_data->fluence_in_voice_call, my_data->fluence_in_voice_comm,
+          my_data->fluence_in_voice_rec, my_data->fluence_in_spkr_mode);
+
     my_data->acdb_handle = dlopen(LIB_ACDB_LOADER, RTLD_NOW);
     if (my_data->acdb_handle == NULL) {
         ALOGE("%s: DLOPEN failed for %s", __func__, LIB_ACDB_LOADER);
@@ -976,6 +1122,9 @@
 void platform_deinit(void *platform)
 {
     int32_t dev;
+    struct operator_info *info_item;
+    struct operator_specific_device *device_item;
+    struct listnode *node;
 
     struct platform_data *my_data = (struct platform_data *)platform;
     close_csd_client(my_data->csd);
@@ -985,19 +1134,42 @@
             free(backend_tag_table[dev]);
         if (hw_interface_table[dev])
             free(hw_interface_table[dev]);
+        if (operator_specific_device_table[dev]) {
+            while (!list_empty(operator_specific_device_table[dev])) {
+                node = list_head(operator_specific_device_table[dev]);
+                list_remove(node);
+                device_item = node_to_item(node, struct operator_specific_device, list);
+                free(device_item->operator);
+                free(device_item->mixer_path);
+                free(device_item);
+            }
+            free(operator_specific_device_table[dev]);
+        }
     }
 
     if (my_data->snd_card_name)
         free(my_data->snd_card_name);
 
+    while (!list_empty(&operator_info_list)) {
+        node = list_head(&operator_info_list);
+        list_remove(node);
+        info_item = node_to_item(node, struct operator_info, list);
+        free(info_item->name);
+        free(info_item->mccmnc);
+        free(info_item);
+    }
+
     free(platform);
 }
 
 const char *platform_get_snd_device_name(snd_device_t snd_device)
 {
-    if (snd_device >= SND_DEVICE_MIN && snd_device < SND_DEVICE_MAX)
+    if (snd_device >= SND_DEVICE_MIN && snd_device < SND_DEVICE_MAX) {
+        if (operator_specific_device_table[snd_device] != NULL) {
+            return get_operator_specific_device_mixer_path(snd_device);
+        }
         return device_table[snd_device];
-    else
+    } else
         return "none";
 }
 
@@ -1041,7 +1213,7 @@
     const char * be_itf2 = hw_interface_table[snd_device2];
 
     if (NULL != be_itf1 && NULL != be_itf2) {
-        if (0 != strcmp(be_itf1, be_itf2))
+        if ((NULL == strstr(be_itf2, be_itf1)) && (NULL == strstr(be_itf1, be_itf2)))
             result = false;
     }
 
@@ -1100,6 +1272,32 @@
     return find_index(usecase_name_index, AUDIO_USECASE_MAX, usecase_name);
 }
 
+void platform_add_operator_specific_device(snd_device_t snd_device,
+                                           const char *operator,
+                                           const char *mixer_path,
+                                           unsigned int acdb_id)
+{
+    struct operator_specific_device *device;
+
+    if (operator_specific_device_table[snd_device] == NULL) {
+        operator_specific_device_table[snd_device] =
+            (struct listnode *)calloc(1, sizeof(struct listnode));
+        list_init(operator_specific_device_table[snd_device]);
+    }
+
+    device = (struct operator_specific_device *)calloc(1, sizeof(struct operator_specific_device));
+
+    device->operator = strdup(operator);
+    device->mixer_path = strdup(mixer_path);
+    device->acdb_id = acdb_id;
+
+    list_add_tail(operator_specific_device_table[snd_device], &device->list);
+
+    ALOGD("%s : deivce[%s] -> operator[%s] mixer_path[%s] acdb_id [%d]", __func__,
+            platform_get_snd_device_name(snd_device), operator, mixer_path, acdb_id);
+
+}
+
 int platform_set_snd_device_acdb_id(snd_device_t snd_device, unsigned int acdb_id)
 {
     int ret = 0;
@@ -1124,7 +1322,11 @@
         ALOGE("%s: Invalid snd_device = %d", __func__, snd_device);
         return -EINVAL;
     }
-    return acdb_device_table[snd_device];
+
+    if (operator_specific_device_table[snd_device] != NULL)
+        return get_operator_specific_device_acdb_id(snd_device);
+    else
+        return acdb_device_table[snd_device];
 }
 
 int platform_send_audio_calibration(void *platform, snd_device_t snd_device)
@@ -1181,11 +1383,11 @@
 
     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];
+        acdb_rx_id = platform_get_snd_device_acdb_id(SND_DEVICE_OUT_SPEAKER_PROTECTED);
     else
-    	acdb_rx_id = acdb_device_table[out_snd_device];
+        acdb_rx_id = platform_get_snd_device_acdb_id(out_snd_device);
 
-    acdb_tx_id = acdb_device_table[in_snd_device];
+    acdb_tx_id = platform_get_snd_device_acdb_id(in_snd_device);
 
     if (acdb_rx_id > 0 && acdb_tx_id > 0) {
         ret = my_data->csd->enable_device_config(acdb_rx_id, acdb_tx_id);
@@ -1215,8 +1417,8 @@
             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];
+        acdb_rx_id = platform_get_snd_device_acdb_id(out_snd_device);
+        acdb_tx_id = platform_get_snd_device_acdb_id(in_snd_device);
 
         if (acdb_rx_id > 0 && acdb_tx_id > 0)
             my_data->acdb_send_voice_cal(acdb_rx_id, acdb_tx_id);
@@ -1241,11 +1443,11 @@
 
     if (out_snd_device == SND_DEVICE_OUT_VOICE_SPEAKER &&
         audio_extn_spkr_prot_is_enabled())
-        acdb_rx_id = acdb_device_table[SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED];
+        acdb_rx_id = platform_get_snd_device_acdb_id(SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED);
     else
-        acdb_rx_id = acdb_device_table[out_snd_device];
+        acdb_rx_id = platform_get_snd_device_acdb_id(out_snd_device);
 
-    acdb_tx_id = acdb_device_table[in_snd_device];
+    acdb_tx_id = platform_get_snd_device_acdb_id(in_snd_device);
 
     if (acdb_rx_id > 0 && acdb_tx_id > 0) {
         ret = my_data->csd->enable_device(acdb_rx_id, acdb_tx_id,
@@ -1303,6 +1505,43 @@
     return ret;
 }
 
+void platform_set_speaker_gain_in_combo(struct audio_device *adev,
+                                        snd_device_t snd_device,
+                                        bool enable)
+{
+    const char* name;
+    switch (snd_device) {
+        case SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES:
+            if (enable)
+                name = "spkr-gain-in-headphone-combo";
+            else
+                name = "speaker-gain-default";
+            break;
+        case SND_DEVICE_OUT_SPEAKER_AND_LINE:
+            if (enable)
+                name = "spkr-gain-in-line-combo";
+            else
+                name = "speaker-gain-default";
+            break;
+        case SND_DEVICE_OUT_SPEAKER_SAFE_AND_HEADPHONES:
+            if (enable)
+                name = "spkr-safe-gain-in-headphone-combo";
+            else
+                name = "speaker-safe-gain-default";
+            break;
+        case SND_DEVICE_OUT_SPEAKER_SAFE_AND_LINE:
+            if (enable)
+                name = "spkr-safe-gain-in-line-combo";
+            else
+                name = "speaker-safe-gain-default";
+            break;
+        default:
+            return;
+    }
+
+    audio_route_apply_and_update_path(adev->audio_route, name);
+}
+
 int platform_set_voice_volume(void *platform, int volume)
 {
     struct platform_data *my_data = (struct platform_data *)platform;
@@ -1317,7 +1556,7 @@
     // Voice volume levels are mapped to adsp volume levels as follows.
     // 100 -> 5, 80 -> 4, 60 -> 3, 40 -> 2, 20 -> 1  0 -> 0
     // But this values don't changed in kernel. So, below change is need.
-    vol_index = (int)percent_to_index(volume, MIN_VOL_INDEX, MAX_VOL_INDEX);
+    vol_index = (int)percent_to_index(volume, MIN_VOL_INDEX, my_data->max_vol_index);
     set_values[0] = vol_index;
 
     ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
@@ -1468,6 +1707,40 @@
         goto exit;
     }
 
+    if (popcount(devices) == 2) {
+        if (devices == (AUDIO_DEVICE_OUT_WIRED_HEADPHONE |
+                        AUDIO_DEVICE_OUT_SPEAKER) ||
+                devices == (AUDIO_DEVICE_OUT_WIRED_HEADSET |
+                            AUDIO_DEVICE_OUT_SPEAKER)) {
+            snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES;
+        } else if (devices == (AUDIO_DEVICE_OUT_LINE |
+                               AUDIO_DEVICE_OUT_SPEAKER)) {
+            snd_device = SND_DEVICE_OUT_SPEAKER_AND_LINE;
+        } else if (devices == (AUDIO_DEVICE_OUT_WIRED_HEADPHONE |
+                               AUDIO_DEVICE_OUT_SPEAKER_SAFE) ||
+                   devices == (AUDIO_DEVICE_OUT_WIRED_HEADSET |
+                               AUDIO_DEVICE_OUT_SPEAKER_SAFE)) {
+            snd_device = SND_DEVICE_OUT_SPEAKER_SAFE_AND_HEADPHONES;
+        } else if (devices == (AUDIO_DEVICE_OUT_LINE |
+                               AUDIO_DEVICE_OUT_SPEAKER_SAFE)) {
+            snd_device = SND_DEVICE_OUT_SPEAKER_SAFE_AND_LINE;
+        } else if (devices == (AUDIO_DEVICE_OUT_AUX_DIGITAL |
+                               AUDIO_DEVICE_OUT_SPEAKER)) {
+            snd_device = SND_DEVICE_OUT_SPEAKER_AND_HDMI;
+        } else {
+            ALOGE("%s: Invalid combo device(%#x)", __func__, devices);
+            goto exit;
+        }
+        if (snd_device != SND_DEVICE_NONE) {
+            goto exit;
+        }
+    }
+
+    if (popcount(devices) != 1) {
+        ALOGE("%s: Invalid output devices(%#x)", __func__, devices);
+        goto exit;
+    }
+
     if (voice_is_in_call(adev) || adev->enable_voicerx) {
         if (devices & AUDIO_DEVICE_OUT_WIRED_HEADPHONE ||
             devices & AUDIO_DEVICE_OUT_WIRED_HEADSET ||
@@ -1510,40 +1783,6 @@
         }
     }
 
-    if (popcount(devices) == 2) {
-        if (devices == (AUDIO_DEVICE_OUT_WIRED_HEADPHONE |
-                        AUDIO_DEVICE_OUT_SPEAKER) ||
-                devices == (AUDIO_DEVICE_OUT_WIRED_HEADSET |
-                            AUDIO_DEVICE_OUT_SPEAKER)) {
-            snd_device = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES;
-        } else if (devices == (AUDIO_DEVICE_OUT_LINE |
-                               AUDIO_DEVICE_OUT_SPEAKER)) {
-            snd_device = SND_DEVICE_OUT_SPEAKER_AND_LINE;
-        } else if (devices == (AUDIO_DEVICE_OUT_WIRED_HEADPHONE |
-                               AUDIO_DEVICE_OUT_SPEAKER_SAFE) ||
-                   devices == (AUDIO_DEVICE_OUT_WIRED_HEADSET |
-                               AUDIO_DEVICE_OUT_SPEAKER_SAFE)) {
-            snd_device = SND_DEVICE_OUT_SPEAKER_SAFE_AND_HEADPHONES;
-        } else if (devices == (AUDIO_DEVICE_OUT_LINE |
-                               AUDIO_DEVICE_OUT_SPEAKER_SAFE)) {
-            snd_device = SND_DEVICE_OUT_SPEAKER_SAFE_AND_LINE;
-        } else if (devices == (AUDIO_DEVICE_OUT_AUX_DIGITAL |
-                               AUDIO_DEVICE_OUT_SPEAKER)) {
-            snd_device = SND_DEVICE_OUT_SPEAKER_AND_HDMI;
-        } else {
-            ALOGE("%s: Invalid combo device(%#x)", __func__, devices);
-            goto exit;
-        }
-        if (snd_device != SND_DEVICE_NONE) {
-            goto exit;
-        }
-    }
-
-    if (popcount(devices) != 1) {
-        ALOGE("%s: Invalid output devices(%#x)", __func__, devices);
-        goto exit;
-    }
-
     if (devices & AUDIO_DEVICE_OUT_WIRED_HEADPHONE ||
         devices & AUDIO_DEVICE_OUT_WIRED_HEADSET) {
         snd_device = SND_DEVICE_OUT_HEADPHONES;
@@ -1594,8 +1833,8 @@
     snd_device_t snd_device = SND_DEVICE_NONE;
     int channel_count = popcount(channel_mask);
 
-    ALOGV("%s: enter: out_device(%#x) in_device(%#x)",
-          __func__, out_device, in_device);
+    ALOGV("%s: enter: out_device(%#x) in_device(%#x) channel_count (%d) channel_mask (0x%x)",
+          __func__, out_device, in_device, channel_count, channel_mask);
     if ((out_device != AUDIO_DEVICE_NONE) && voice_is_in_call(adev)) {
         if (adev->voice.tty_mode != TTY_MODE_OFF) {
             if (out_device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE ||
@@ -1641,17 +1880,24 @@
                     snd_device = SND_DEVICE_IN_BT_SCO_MIC;
             }
         } else if (out_device & AUDIO_DEVICE_OUT_SPEAKER ||
-            out_device & AUDIO_DEVICE_OUT_SPEAKER_SAFE ||
-            out_device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE ||
-            out_device & AUDIO_DEVICE_OUT_LINE) {
-            if (my_data->fluence_in_voice_call && my_data->fluence_in_spkr_mode &&
-                    my_data->dualmic_config != DUALMIC_CONFIG_NONE) {
-                snd_device = SND_DEVICE_IN_VOICE_SPEAKER_DMIC;
-            } else {
+                   out_device & AUDIO_DEVICE_OUT_SPEAKER_SAFE ||
+                   out_device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE ||
+                   out_device & AUDIO_DEVICE_OUT_LINE) {
+            if (my_data->fluence_in_voice_call && my_data->fluence_in_spkr_mode) {
+                if (my_data->source_mic_type & SOURCE_DUAL_MIC) {
+                    snd_device = SND_DEVICE_IN_VOICE_SPEAKER_DMIC;
+                } else {
+                    snd_device = SND_DEVICE_IN_VOICE_SPEAKER_MIC;
+                }
+            }
+
+            //select default
+            if (snd_device == SND_DEVICE_NONE) {
                 snd_device = SND_DEVICE_IN_VOICE_SPEAKER_MIC;
             }
-        } else if (out_device & AUDIO_DEVICE_OUT_TELEPHONY_TX)
+        } else if (out_device & AUDIO_DEVICE_OUT_TELEPHONY_TX) {
             snd_device = SND_DEVICE_IN_VOICE_RX;
+        }
     } else if (source == AUDIO_SOURCE_CAMCORDER) {
         if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC ||
             in_device & AUDIO_DEVICE_IN_BACK_MIC) {
@@ -1659,14 +1905,29 @@
         }
     } else if (source == AUDIO_SOURCE_VOICE_RECOGNITION) {
         if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC) {
-            if (my_data->dualmic_config != DUALMIC_CONFIG_NONE) {
-                if (channel_mask == AUDIO_CHANNEL_IN_FRONT_BACK)
-                    snd_device = SND_DEVICE_IN_VOICE_REC_DMIC_STEREO;
-                else if (my_data->fluence_in_voice_rec &&
-                         adev->active_input->enable_ns)
+            if (my_data->fluence_in_voice_rec && channel_count == 1) {
+                if ((my_data->fluence_type == FLUENCE_PRO_ENABLE) &&
+                    (my_data->source_mic_type & SOURCE_QUAD_MIC)) {
+                    snd_device = SND_DEVICE_IN_HANDSET_QMIC;
+                } else if ((my_data->fluence_type == FLUENCE_PRO_ENABLE) &&
+                    (my_data->source_mic_type & SOURCE_THREE_MIC)) {
+                    snd_device = SND_DEVICE_IN_HANDSET_TMIC;
+                } else if (((my_data->fluence_type == FLUENCE_PRO_ENABLE) ||
+                    (my_data->fluence_type == FLUENCE_ENABLE)) &&
+                    (my_data->source_mic_type & SOURCE_DUAL_MIC)) {
                     snd_device = SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE;
+                }
+                platform_set_echo_reference(adev, true, out_device);
+            } else if ((channel_mask == AUDIO_CHANNEL_IN_FRONT_BACK) &&
+                       (my_data->source_mic_type & SOURCE_DUAL_MIC)) {
+                snd_device = SND_DEVICE_IN_VOICE_REC_DMIC_STEREO;
+            } else if (((int)channel_mask == AUDIO_CHANNEL_INDEX_MASK_3) &&
+                       (my_data->source_mic_type & SOURCE_THREE_MIC)) {
+                snd_device = SND_DEVICE_IN_THREE_MIC;
+            } else if (((int)channel_mask == AUDIO_CHANNEL_INDEX_MASK_4) &&
+                       (my_data->source_mic_type & SOURCE_QUAD_MIC)) {
+                snd_device = SND_DEVICE_IN_QUAD_MIC;
             }
-
             if (snd_device == SND_DEVICE_NONE) {
                 if (adev->active_input->enable_ns)
                     snd_device = SND_DEVICE_IN_VOICE_REC_MIC_NS;
@@ -1683,16 +1944,18 @@
                 if (in_device & AUDIO_DEVICE_IN_BACK_MIC) {
                     if (my_data->fluence_in_spkr_mode &&
                             my_data->fluence_in_voice_comm &&
-                            my_data->dualmic_config != DUALMIC_CONFIG_NONE) {
+                            (my_data->source_mic_type & SOURCE_DUAL_MIC)) {
                         snd_device = SND_DEVICE_IN_SPEAKER_DMIC_AEC_NS;
-                    } else
+                    } else {
                         snd_device = SND_DEVICE_IN_SPEAKER_MIC_AEC_NS;
+                    }
                 } else if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC) {
                     if (my_data->fluence_in_voice_comm &&
-                            my_data->dualmic_config != DUALMIC_CONFIG_NONE) {
+                            (my_data->source_mic_type & SOURCE_DUAL_MIC)) {
                         snd_device = SND_DEVICE_IN_HANDSET_DMIC_AEC_NS;
-                    } else
+                    } else {
                         snd_device = SND_DEVICE_IN_HANDSET_MIC_AEC_NS;
+                    }
                 } else if (in_device & AUDIO_DEVICE_IN_WIRED_HEADSET) {
                     snd_device = SND_DEVICE_IN_HEADSET_MIC_AEC;
                 }
@@ -1701,16 +1964,18 @@
                 if (in_device & AUDIO_DEVICE_IN_BACK_MIC) {
                     if (my_data->fluence_in_spkr_mode &&
                             my_data->fluence_in_voice_comm &&
-                            my_data->dualmic_config != DUALMIC_CONFIG_NONE) {
+                            (my_data->source_mic_type & SOURCE_DUAL_MIC)) {
                         snd_device = SND_DEVICE_IN_SPEAKER_DMIC_AEC;
-                    } else
+                    } else {
                         snd_device = SND_DEVICE_IN_SPEAKER_MIC_AEC;
+                    }
                 } else if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC) {
                     if (my_data->fluence_in_voice_comm &&
-                            my_data->dualmic_config != DUALMIC_CONFIG_NONE) {
+                            (my_data->source_mic_type & SOURCE_DUAL_MIC)) {
                         snd_device = SND_DEVICE_IN_HANDSET_DMIC_AEC;
-                    } else
+                    } else {
                         snd_device = SND_DEVICE_IN_HANDSET_MIC_AEC;
+                    }
                } else if (in_device & AUDIO_DEVICE_IN_WIRED_HEADSET) {
                    snd_device = SND_DEVICE_IN_HEADSET_MIC_AEC;
                }
@@ -1719,16 +1984,18 @@
                 if (in_device & AUDIO_DEVICE_IN_BACK_MIC) {
                     if (my_data->fluence_in_spkr_mode &&
                             my_data->fluence_in_voice_comm &&
-                            my_data->dualmic_config != DUALMIC_CONFIG_NONE) {
+                            (my_data->source_mic_type & SOURCE_DUAL_MIC)) {
                         snd_device = SND_DEVICE_IN_SPEAKER_DMIC_NS;
-                    } else
+                    } else {
                         snd_device = SND_DEVICE_IN_SPEAKER_MIC_NS;
+                    }
                 } else if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC) {
                     if (my_data->fluence_in_voice_comm &&
-                            my_data->dualmic_config != DUALMIC_CONFIG_NONE) {
+                            (my_data->source_mic_type & SOURCE_DUAL_MIC)) {
                         snd_device = SND_DEVICE_IN_HANDSET_DMIC_NS;
-                    } else
+                    } else {
                         snd_device = SND_DEVICE_IN_HANDSET_MIC_NS;
+                    }
                 }
             }
         }
@@ -1745,17 +2012,37 @@
             !(in_device & AUDIO_DEVICE_IN_VOICE_CALL) &&
             !(in_device & AUDIO_DEVICE_IN_COMMUNICATION)) {
         if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC) {
-            if (my_data->dualmic_config != DUALMIC_CONFIG_NONE &&
-                    channel_count == 2)
+            if ((my_data->source_mic_type & SOURCE_QUAD_MIC) &&
+                (int)channel_mask == AUDIO_CHANNEL_INDEX_MASK_4) {
+                snd_device = SND_DEVICE_IN_QUAD_MIC;
+            } else if ((my_data->source_mic_type & SOURCE_THREE_MIC) &&
+                       (int)channel_mask == AUDIO_CHANNEL_INDEX_MASK_3) {
+                snd_device = SND_DEVICE_IN_THREE_MIC;
+            } else if ((my_data->source_mic_type & SOURCE_DUAL_MIC) &&
+                       channel_count == 2) {
                 snd_device = SND_DEVICE_IN_HANDSET_DMIC_STEREO;
-            else
+            } else if ((my_data->source_mic_type & SOURCE_MONO_MIC) &&
+                       channel_count == 1) {
                 snd_device = SND_DEVICE_IN_HANDSET_MIC;
+            } else {
+                ALOGE("%s: something wrong (1): source type (%d) channel_count (%d) .."
+                      " channel mask (0x%x) no combination found .. setting to mono", __func__,
+                       my_data->source_mic_type, channel_count, channel_mask);
+                snd_device = SND_DEVICE_IN_HANDSET_MIC;
+            }
         } else if (in_device & AUDIO_DEVICE_IN_BACK_MIC) {
-            if (my_data->dualmic_config != DUALMIC_CONFIG_NONE &&
-                    channel_count == 2)
+            if ((my_data->source_mic_type & SOURCE_DUAL_MIC) &&
+                    channel_count == 2) {
                 snd_device = SND_DEVICE_IN_SPEAKER_DMIC_STEREO;
-            else
+            } else if ((my_data->source_mic_type & SOURCE_MONO_MIC) &&
+                    channel_count == 1) {
                 snd_device = SND_DEVICE_IN_SPEAKER_MIC;
+            } else {
+                ALOGE("%s: something wrong (2): source type (%d) channel_count (%d) .."
+                      " no combination found .. setting to mono", __func__,
+                       my_data->source_mic_type, channel_count);
+                snd_device = SND_DEVICE_IN_SPEAKER_MIC;
+            }
         } else if (in_device & AUDIO_DEVICE_IN_WIRED_HEADSET) {
             snd_device = SND_DEVICE_IN_HEADSET_MIC;
         } else if (in_device & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) {
@@ -1786,10 +2073,18 @@
                    out_device & AUDIO_DEVICE_OUT_SPEAKER_SAFE ||
                    out_device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE ||
                    out_device & AUDIO_DEVICE_OUT_LINE) {
-            if (channel_count == 2)
+            if ((my_data->source_mic_type & SOURCE_DUAL_MIC) &&
+                    channel_count == 2) {
                 snd_device = SND_DEVICE_IN_SPEAKER_DMIC_STEREO;
-            else
+            } else if ((my_data->source_mic_type & SOURCE_MONO_MIC) &&
+                          channel_count == 1) {
                 snd_device = SND_DEVICE_IN_SPEAKER_MIC;
+            } else {
+                ALOGE("%s: something wrong (3): source type (%d) channel_count (%d) .."
+                      " no combination found .. setting to mono", __func__,
+                       my_data->source_mic_type, channel_count);
+                snd_device = SND_DEVICE_IN_SPEAKER_MIC;
+            }
         } else if (out_device & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET) {
             if (adev->bt_wb_speech_enabled) {
                 if (adev->bluetooth_nrec)
@@ -1993,7 +2288,7 @@
 int platform_set_parameters(void *platform, struct str_parms *parms)
 {
     struct platform_data *my_data = (struct platform_data *)platform;
-    char value[64];
+    char value[128];
     char *kv_pairs = str_parms_to_str(parms);
     int ret = 0, err;
 
@@ -2013,6 +2308,32 @@
         ALOGV("%s: sound card name %s", __func__, my_data->snd_card_name);
     }
 
+    err = str_parms_get_str(parms, PLATFORM_CONFIG_KEY_OPERATOR_INFO,
+                            value, sizeof(value));
+    if (err >= 0) {
+        struct operator_info *info;
+        char *str = value;
+        char *name;
+
+        str_parms_del(parms, PLATFORM_CONFIG_KEY_OPERATOR_INFO);
+        info = (struct operator_info *)calloc(1, sizeof(struct operator_info));
+        name = strtok(str, ";");
+        info->name = strdup(name);
+        info->mccmnc = strdup(str + strlen(name) + 1);
+
+        list_add_tail(&operator_info_list, &info->list);
+        ALOGD("%s: add operator[%s] mccmnc[%s]", __func__, info->name, info->mccmnc);
+    }
+
+    memset(value, 0, sizeof(value));
+    err = str_parms_get_str(parms, PLATFORM_CONFIG_KEY_MAX_MIC_COUNT,
+                            value, sizeof(value));
+    if (err >= 0) {
+        str_parms_del(parms, PLATFORM_CONFIG_KEY_MAX_MIC_COUNT);
+        my_data->max_mic_count = atoi(value);
+        ALOGV("%s: max_mic_count %s/%d", __func__, value, my_data->max_mic_count);
+    }
+
 done:
     ALOGV("%s: exit with code(%d)", __func__, ret);
     if (kv_pairs != NULL)
diff --git a/hal/msm8974/platform.h b/hal/msm8974/platform.h
index f1c5239..c1f901c 100644
--- a/hal/msm8974/platform.h
+++ b/hal/msm8974/platform.h
@@ -17,6 +17,19 @@
 #ifndef QCOM_AUDIO_PLATFORM_H
 #define QCOM_AUDIO_PLATFORM_H
 
+enum {
+    FLUENCE_DISABLE,                  /* Target dosent support fluence */
+    FLUENCE_ENABLE      = 0x1,        /* Target supports fluence */
+    FLUENCE_PRO_ENABLE  = 0x2,        /* Target supports fluence pro */
+};
+
+enum {
+    SOURCE_MONO_MIC  = 0x1,            /* Target contains 1 mic */
+    SOURCE_DUAL_MIC  = 0x2,            /* Target contains 2 mics */
+    SOURCE_THREE_MIC = 0x4,            /* Target contains 3 mics */
+    SOURCE_QUAD_MIC  = 0x8,            /* Target contains 4 mics */
+};
+
 /*
  * Below are the devices for which is back end is same, SLIMBUS_0_RX.
  * All these devices are handled by the internal HW codec. We can
@@ -118,8 +131,12 @@
 
     SND_DEVICE_IN_VOICE_RX,
 
+    SND_DEVICE_IN_THREE_MIC,
+    SND_DEVICE_IN_QUAD_MIC,
     SND_DEVICE_IN_CAPTURE_VI_FEEDBACK,
 
+    SND_DEVICE_IN_HANDSET_TMIC,
+    SND_DEVICE_IN_HANDSET_QMIC,
     SND_DEVICE_IN_END,
 
     SND_DEVICE_MAX = SND_DEVICE_IN_END,
@@ -131,6 +148,7 @@
 #define ALL_SESSION_VSID                0xFFFFFFFF
 #define DEFAULT_MUTE_RAMP_DURATION_MS   20
 #define DEFAULT_VOLUME_RAMP_DURATION_MS 20
+#define MIXER_PATH_MAX_LENGTH 100
 
 #define ACDB_ID_VOICE_SPEAKER 15
 #define ACDB_ID_VOICE_HANDSET 7
@@ -207,6 +225,8 @@
 #define LIB_MDM_DETECT "libmdmdetect.so"
 
 #define PLATFORM_CONFIG_KEY_SOUNDCARD_NAME "snd_card_name"
+#define PLATFORM_CONFIG_KEY_MAX_MIC_COUNT "input_mic_max_count"
+#define PLATFORM_DEFAULT_MIC_COUNT 2
 
 /* CSD-CLIENT related functions */
 typedef int (*init_t)(bool);
diff --git a/hal/platform_api.h b/hal/platform_api.h
index 61bb92f..1e41358 100644
--- a/hal/platform_api.h
+++ b/hal/platform_api.h
@@ -41,6 +41,9 @@
 int platform_start_voice_call(void *platform, uint32_t vsid);
 int platform_stop_voice_call(void *platform, uint32_t vsid);
 int platform_set_voice_volume(void *platform, int volume);
+void platform_set_speaker_gain_in_combo(struct audio_device *adev,
+                                        snd_device_t snd_device,
+                                        bool enable);
 int platform_set_mic_mute(void *platform, bool state);
 int platform_get_sample_rate(void *platform, uint32_t *rate);
 int platform_set_device_mute(void *platform, bool state, char *dir);
@@ -48,6 +51,10 @@
 snd_device_t platform_get_input_snd_device(void *platform, audio_devices_t out_device);
 int platform_set_hdmi_channels(void *platform, int channel_count);
 int platform_edid_get_max_channels(void *platform);
+void platform_add_operator_specific_device(snd_device_t snd_device,
+                                           const char *operator,
+                                           const char *mixer_path,
+                                           unsigned int acdb_id);
 
 /* returns the latency for a usecase in Us */
 int64_t platform_render_latency(audio_usecase_t usecase);
diff --git a/hal/platform_info.c b/hal/platform_info.c
index c0527b4..4556294 100644
--- a/hal/platform_info.c
+++ b/hal/platform_info.c
@@ -33,6 +33,7 @@
     PCM_ID,
     BACKEND_NAME,
     CONFIG_PARAMS,
+    OPERATOR_SPECIFIC,
 } section_t;
 
 typedef void (* section_process_fn)(const XML_Char **attr);
@@ -42,6 +43,7 @@
 static void process_backend_name(const XML_Char **attr);
 static void process_config_params(const XML_Char **attr);
 static void process_root(const XML_Char **attr);
+static void process_operator_specific(const XML_Char **attr);
 
 static section_process_fn section_table[] = {
     [ROOT] = process_root,
@@ -49,6 +51,7 @@
     [PCM_ID] = process_pcm_id,
     [BACKEND_NAME] = process_backend_name,
     [CONFIG_PARAMS] = process_config_params,
+    [OPERATOR_SPECIFIC] = process_operator_specific,
 };
 
 static section_t section;
@@ -79,10 +82,18 @@
  * </pcm_ids>
  * <config_params>
  *      <param key="snd_card_name" value="msm8994-tomtom-mtp-snd-card"/>
+ *      <param key="operator_info" value="tmus;aa;bb;cc"/>
+ *      <param key="operator_info" value="sprint;xx;yy;zz"/>
  *      ...
  *      ...
  * </config_params>
  *
+ * <operator_specific>
+ *      <device name="???" operator="???" mixer_path="???" acdb_id="???"/>
+ *      ...
+ *      ...
+ * </operator_specific>
+ *
  * </audio_platform_info>
  */
 
@@ -214,6 +225,44 @@
     return;
 }
 
+
+static void process_operator_specific(const XML_Char **attr)
+{
+    snd_device_t snd_device = SND_DEVICE_NONE;
+
+    if (strcmp(attr[0], "name") != 0) {
+        ALOGE("%s: 'name' not found", __func__);
+        goto done;
+    }
+
+    snd_device = platform_get_snd_device_index((char *)attr[1]);
+    if (snd_device < 0) {
+        ALOGE("%s: Device %s in %s not found, no ACDB ID set!",
+              __func__, (char *)attr[3], PLATFORM_INFO_XML_PATH);
+        goto done;
+    }
+
+    if (strcmp(attr[2], "operator") != 0) {
+        ALOGE("%s: 'operator' not found", __func__);
+        goto done;
+    }
+
+    if (strcmp(attr[4], "mixer_path") != 0) {
+        ALOGE("%s: 'mixer_path' not found", __func__);
+        goto done;
+    }
+
+    if (strcmp(attr[6], "acdb_id") != 0) {
+        ALOGE("%s: 'acdb_id' not found", __func__);
+        goto done;
+    }
+
+    platform_add_operator_specific_device(snd_device, (char *)attr[3], (char *)attr[5], atoi((char *)attr[7]));
+
+done:
+    return;
+}
+
 /* platform specific configuration key-value pairs */
 static void process_config_params(const XML_Char **attr)
 {
@@ -228,6 +277,7 @@
     }
 
     str_parms_add_str(my_data.kvpairs, (char*)attr[1], (char*)attr[3]);
+    platform_set_parameters(my_data.platform, my_data.kvpairs);
 done:
     return;
 }
@@ -247,8 +297,10 @@
         section = BACKEND_NAME;
     } else if (strcmp(tag_name, "config_params") == 0) {
         section = CONFIG_PARAMS;
+    } else if (strcmp(tag_name, "operator_specific") == 0) {
+        section = OPERATOR_SPECIFIC;
     } else if (strcmp(tag_name, "device") == 0) {
-        if ((section != ACDB) && (section != BACKEND_NAME)) {
+        if ((section != ACDB) && (section != BACKEND_NAME) && (section != OPERATOR_SPECIFIC)) {
             ALOGE("device tag only supported for acdb/backend names");
             return;
         }
@@ -287,7 +339,8 @@
         section = ROOT;
     } else if (strcmp(tag_name, "config_params") == 0) {
         section = ROOT;
-        platform_set_parameters(my_data.platform, my_data.kvpairs);
+    } else if (strcmp(tag_name, "operator_specific") == 0) {
+        section = ROOT;
     }
 }
 
diff --git a/hal/voice.c b/hal/voice.c
index 1f36b36..a33305d 100644
--- a/hal/voice.c
+++ b/hal/voice.c
@@ -52,6 +52,58 @@
     return session;
 }
 
+static bool voice_is_sidetone_device(snd_device_t out_device,
+            char *mixer_path)
+{
+    bool is_sidetone_dev = true;
+
+    switch (out_device) {
+    case SND_DEVICE_OUT_VOICE_HAC_HANDSET:
+        strlcpy(mixer_path, "sidetone-hac-handset", MIXER_PATH_MAX_LENGTH);
+        break;
+    case SND_DEVICE_OUT_VOICE_HANDSET:
+        strlcpy(mixer_path, "sidetone-handset", MIXER_PATH_MAX_LENGTH);
+        break;
+    case SND_DEVICE_OUT_VOICE_HEADPHONES:
+        strlcpy(mixer_path, "sidetone-headphones", MIXER_PATH_MAX_LENGTH);
+        break;
+    default:
+        is_sidetone_dev = false;
+        break;
+    }
+
+    return is_sidetone_dev;
+}
+
+void voice_set_sidetone(struct audio_device *adev,
+        snd_device_t out_snd_device, bool enable)
+{
+    char mixer_path[MIXER_PATH_MAX_LENGTH];
+    bool is_sidetone_dev;
+
+    ALOGD("%s: %s, out_snd_device: %d\n",
+          __func__, (enable ? "enable" : "disable"),
+          out_snd_device);
+
+    is_sidetone_dev = voice_is_sidetone_device(out_snd_device, mixer_path);
+
+    if (!is_sidetone_dev) {
+        ALOGD("%s: device %d does not support sidetone\n",
+              __func__, out_snd_device);
+        return;
+    }
+
+    ALOGD("%s: sidetone out device = %s\n",
+          __func__, mixer_path);
+
+    if (enable)
+        audio_route_apply_and_update_path(adev->audio_route, mixer_path);
+    else
+        audio_route_reset_and_update_path(adev->audio_route, mixer_path);
+
+    return;
+}
+
 int voice_stop_usecase(struct audio_device *adev, audio_usecase_t usecase_id)
 {
     int i, ret = 0;
@@ -61,8 +113,20 @@
     ALOGD("%s: enter usecase:%s", __func__, use_case_table[usecase_id]);
 
     session = (struct voice_session *)voice_get_session_from_use_case(adev, usecase_id);
+
+    uc_info = get_usecase_from_list(adev, usecase_id);
+    if (uc_info == NULL) {
+        ALOGE("%s: Could not find the usecase (%d) in the list",
+              __func__, usecase_id);
+        return -EINVAL;
+    }
+
     session->state.current = CALL_INACTIVE;
 
+    /* Disable sidetone only when no calls are active */
+    if (!voice_is_call_state_active(adev))
+        voice_set_sidetone(adev, uc_info->out_snd_device, false);
+
     ret = platform_stop_voice_call(adev->platform, session->vsid);
 
     /* 1. Close the PCM devices */
@@ -75,13 +139,6 @@
         session->pcm_tx = NULL;
     }
 
-    uc_info = get_usecase_from_list(adev, usecase_id);
-    if (uc_info == NULL) {
-        ALOGE("%s: Could not find the usecase (%d) in the list",
-              __func__, usecase_id);
-        return -EINVAL;
-    }
-
     /* 2. Get and set stream specific mixer controls */
     disable_audio_route(adev, uc_info);
 
@@ -129,6 +186,17 @@
         goto error_start_voice;
     }
 
+    ALOGV("%s: Opening PCM capture device card_id(%d) device_id(%d)",
+          __func__, adev->snd_card, pcm_dev_tx_id);
+    session->pcm_tx = pcm_open(adev->snd_card,
+                               pcm_dev_tx_id,
+                               PCM_IN, &voice_config);
+    if (session->pcm_tx && !pcm_is_ready(session->pcm_tx)) {
+        ALOGE("%s: %s", __func__, pcm_get_error(session->pcm_tx));
+        ret = -EIO;
+        goto error_start_voice;
+    }
+
     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,
@@ -140,18 +208,12 @@
         goto error_start_voice;
     }
 
-    ALOGV("%s: Opening PCM capture device card_id(%d) device_id(%d)",
-          __func__, adev->snd_card, pcm_dev_tx_id);
-    session->pcm_tx = pcm_open(adev->snd_card,
-                               pcm_dev_tx_id,
-                               PCM_IN, &voice_config);
-    if (session->pcm_tx && !pcm_is_ready(session->pcm_tx)) {
-        ALOGE("%s: %s", __func__, pcm_get_error(session->pcm_tx));
-        ret = -EIO;
-        goto error_start_voice;
-    }
-    pcm_start(session->pcm_rx);
     pcm_start(session->pcm_tx);
+    pcm_start(session->pcm_rx);
+
+    /* 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);
 
     voice_set_volume(adev, adev->voice.volume);
 
diff --git a/hal/voice.h b/hal/voice.h
index 76f9d0d..23b9ee3 100644
--- a/hal/voice.h
+++ b/hal/voice.h
@@ -40,6 +40,7 @@
 struct stream_in;
 struct stream_out;
 typedef int audio_usecase_t;
+typedef int snd_device_t;
 
 struct call_state {
     int current;
@@ -90,4 +91,8 @@
 int voice_check_and_stop_incall_rec_usecase(struct audio_device *adev,
                                             struct stream_in *in);
 void voice_update_devices_for_all_voice_usecases(struct audio_device *adev);
+void voice_set_sidetone(struct audio_device *adev,
+                       snd_device_t out_snd_device,
+                       bool enable);
+bool voice_is_call_state_active(struct audio_device *adev);
 #endif //VOICE_H
diff --git a/post_proc/volume_listener.c b/post_proc/volume_listener.c
index cf15f1c..34954bb 100644
--- a/post_proc/volume_listener.c
+++ b/post_proc/volume_listener.c
@@ -312,6 +312,13 @@
  * Effect Control Interface Implementation
  */
 
+static inline int16_t clamp16(int32_t sample)
+{
+    if ((sample>>15) ^ (sample>>31))
+        sample = 0x7FFF ^ (sample>>31);
+    return sample;
+}
+
 static int vol_effect_process(effect_handle_t self,
                               audio_buffer_t *in_buffer,
                               audio_buffer_t *out_buffer)
@@ -330,7 +337,15 @@
 
     // calculation based on channel count 2
     if (in_buffer->raw != out_buffer->raw) {
-        memcpy(out_buffer->raw, in_buffer->raw, out_buffer->frameCount * 2 * sizeof(int16_t));
+        if (context->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
+            size_t i;
+            for (i = 0; i < out_buffer->frameCount*2; i++) {
+                out_buffer->s16[i] = clamp16(out_buffer->s16[i] + in_buffer->s16[i]);
+            }
+        } else {
+            memcpy(out_buffer->raw, in_buffer->raw, out_buffer->frameCount * 2 * sizeof(int16_t));
+        }
+
     } else {
         ALOGW("%s: something wrong, didn't handle in_buffer and out_buffer same address case",
               __func__);
@@ -374,6 +389,12 @@
 
     case EFFECT_CMD_SET_CONFIG:
         ALOGV("%s :: cmd called EFFECT_CMD_SET_CONFIG", __func__);
+        if (p_cmd_data == NULL || cmd_size != sizeof(effect_config_t)
+                || p_reply_data == NULL || reply_size == NULL || *reply_size != sizeof(int)) {
+            return -EINVAL;
+        }
+        context->config = *(effect_config_t *)p_cmd_data;
+        *(int *)p_reply_data = 0;
         break;
 
     case EFFECT_CMD_GET_CONFIG:
@@ -420,7 +441,7 @@
 
         // After changing the state and if device is speaker
         // recalculate gain dep cal level
-        if (context->dev_id & AUDIO_DEVICE_OUT_SPEAKER) {
+        if (context->dev_id == AUDIO_DEVICE_OUT_SPEAKER) {
                 check_and_set_gain_dep_cal();
         }
 
@@ -447,7 +468,7 @@
 
         // After changing the state and if device is speaker
         // recalculate gain dep cal level
-        if (context->dev_id & AUDIO_DEVICE_OUT_SPEAKER) {
+        if (context->dev_id == AUDIO_DEVICE_OUT_SPEAKER) {
             check_and_set_gain_dep_cal();
         }
 
@@ -478,8 +499,8 @@
                __func__, context->dev_id, new_device);
 
         // check if old or new device is speaker
-        if ((context->dev_id & AUDIO_DEVICE_OUT_SPEAKER) ||
-            (new_device & AUDIO_DEVICE_OUT_SPEAKER)) {
+        if ((context->dev_id ==  AUDIO_DEVICE_OUT_SPEAKER) ||
+            (new_device == AUDIO_DEVICE_OUT_SPEAKER)) {
             recompute_gain_dep_cal_Level = true;
         }
 
@@ -504,7 +525,7 @@
             goto exit;
         }
 
-        if (context->dev_id & AUDIO_DEVICE_OUT_SPEAKER) {
+        if (context->dev_id == AUDIO_DEVICE_OUT_SPEAKER) {
             recompute_gain_dep_cal_Level = true;
         }
 
@@ -600,7 +621,7 @@
 
 static int vol_prc_lib_create(const effect_uuid_t *uuid,
                               int32_t session_id,
-                              int32_t io_id,
+                              int32_t io_id __unused,
                               effect_handle_t *p_handle)
 {
     int itt = 0;
@@ -682,7 +703,7 @@
             ALOGV("--- Found something to remove ---");
             list_remove(&context->effect_list_node);
             PRINT_STREAM_TYPE(context->stream_type);
-            if (context->dev_id && AUDIO_DEVICE_OUT_SPEAKER) {
+            if (context->dev_id == AUDIO_DEVICE_OUT_SPEAKER) {
                 recompute_flag = true;
             }
             free(context);