Merge "Merge Android Pie into master"
diff --git a/Android.mk b/Android.mk
index 65bf578..d6f03fb 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 msm8998,$(TARGET_BOARD_PLATFORM)),)
+  ifneq ($(filter msm8960 msm8226 msm8x26 msm8x84 msm8084 msm8992 msm8994 msm8996 msm8909 msm8952 msm8998 sdm845,$(TARGET_BOARD_PLATFORM)),)
 
     MY_LOCAL_PATH := $(call my-dir)
 
diff --git a/hal/Android.mk b/hal/Android.mk
index 923879c..3ce2d2b 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 msm8998,$(TARGET_BOARD_PLATFORM)),)
+ifneq ($(filter msm8974 msm8226 msm8084 msm8992 msm8994 msm8996 msm8998 sdm845,$(TARGET_BOARD_PLATFORM)),)
   # B-family platform uses msm8974 code base
   AUDIO_PLATFORM = msm8974
 ifneq ($(filter msm8974,$(TARGET_BOARD_PLATFORM)),)
@@ -39,12 +39,20 @@
   LOCAL_CFLAGS := -DPLATFORM_MSM8996
   LOCAL_CFLAGS += -DMAX_TARGET_SPECIFIC_CHANNEL_CNT="4"
   LOCAL_CFLAGS += -DKPI_OPTIMIZE_ENABLED
+  LOCAL_CFLAGS += -DINCALL_MUSIC_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
+  LOCAL_CFLAGS += -DINCALL_MUSIC_ENABLED
+  MULTIPLE_HW_VARIANTS_ENABLED := true
+endif
+ifneq ($(filter sdm845,$(TARGET_BOARD_PLATFORM)),)
+  LOCAL_CFLAGS := -DPLATFORM_SDM845
+  LOCAL_CFLAGS += -DMAX_TARGET_SPECIFIC_CHANNEL_CNT="4"
+  LOCAL_CFLAGS += -DINCALL_MUSIC_ENABLED
+  LOCAL_CFLAGS += -DINCALL_STEREO_CAPTURE_ENABLED
   MULTIPLE_HW_VARIANTS_ENABLED := true
 endif
 endif
@@ -124,6 +132,10 @@
     LOCAL_CFLAGS += -DEXTERNAL_BT_SUPPORTED
 endif
 
+ifeq ($(strip $(AUDIO_FEATURE_FLICKER_SENSOR_INPUT)),true)
+    LOCAL_CFLAGS += -DFLICKER_SENSOR_INPUT
+endif
+
 ifeq ($(strip $(AUDIO_FEATURE_NO_AUDIO_OUT)),true)
     LOCAL_CFLAGS += -DNO_AUDIO_OUT
 endif
@@ -144,12 +156,22 @@
     LOCAL_SRC_FILES += audio_extn/spkr_protection.c
 endif
 
+ifeq ($(strip $(AUDIO_FEATURE_ENABLED_CIRRUS_SPKR_PROTECTION)),true)
+    LOCAL_CFLAGS += -DSPKR_PROT_ENABLED
+    LOCAL_SRC_FILES += audio_extn/cirrus_playback.c
+endif
+
 ifeq ($(strip $(AUDIO_FEATURE_ENABLED_DSM_FEEDBACK)),true)
     LOCAL_CFLAGS += -DDSM_FEEDBACK_ENABLED
     LOCAL_SRC_FILES += audio_extn/dsm_feedback.c
 endif
 
-ifneq ($(filter msm8992 msm8994 msm8996 msm8998,$(TARGET_BOARD_PLATFORM)),)
+ifeq ($(strip $(AUDIO_FEATURE_ENABLED_A2DP_OFFLOAD)),true)
+    LOCAL_CFLAGS += -DA2DP_OFFLOAD_ENABLED
+    LOCAL_SRC_FILES += audio_extn/a2dp.c
+endif
+
+ifneq ($(filter msm8992 msm8994 msm8996 msm8998 sdm845,$(TARGET_BOARD_PLATFORM)),)
   # push codec/mad calibration to HW dep node
   # applicable to msm8992/8994 or newer platforms
   LOCAL_CFLAGS += -DHWDEP_CAL_ENABLED
@@ -161,6 +183,24 @@
     LOCAL_SRC_FILES += audio_extn/sndmonitor.c
 endif
 
+
+ifeq ($(strip $(AUDIO_FEATURE_ENABLED_USB_SERVICE_INTERVAL)), true)
+    LOCAL_CFLAGS += -DUSB_SERVICE_INTERVAL_ENABLED
+endif
+
+ifeq ($(strip $(AUDIO_FEATURE_ENABLED_MAXX_AUDIO)), true)
+    LOCAL_CFLAGS += -DMAXXAUDIO_QDSP_ENABLED
+    LOCAL_SRC_FILES += audio_extn/maxxaudio.c
+endif
+
+ifeq ($(strip $(AUDIO_FEATURE_ENABLED_BG_CAL)),true)
+    LOCAL_CFLAGS += -DBG_CODEC_CAL
+endif
+
+LOCAL_SHARED_LIBRARIES += libbase libhidlbase libhwbinder libutils android.hardware.power@1.2 liblog
+
+LOCAL_SRC_FILES += audio_perf.cpp
+
 LOCAL_HEADER_LIBRARIES += libhardware_headers
 
 LOCAL_MODULE := audio.primary.$(TARGET_BOARD_PLATFORM)
diff --git a/hal/acdb.c b/hal/acdb.c
index 10d4101..58f219a 100644
--- a/hal/acdb.c
+++ b/hal/acdb.c
@@ -22,7 +22,7 @@
 #include <stdlib.h>
 #include <stdbool.h>
 #include <dlfcn.h>
-#include <cutils/log.h>
+#include <log/log.h>
 #include <cutils/str_parms.h>
 #include <system/audio.h>
 #include <tinyalsa/asoundlib.h>
diff --git a/hal/acdb.h b/hal/acdb.h
index 08026fb..e6410d6 100644
--- a/hal/acdb.h
+++ b/hal/acdb.h
@@ -33,9 +33,11 @@
 typedef int  (*acdb_init_t)();
 typedef void (*acdb_send_audio_cal_t)(int, int);
 typedef void (*acdb_send_voice_cal_t)(int, int);
+typedef int (*acdb_get_audio_cal_t) (void *, void *, uint32_t*);
 typedef int (*acdb_reload_vocvoltable_t)(int);
 typedef int (*acdb_send_gain_dep_cal_t)(int, int, int, int, int);
 typedef int (*acdb_send_custom_top_t) (void);
+typedef int (*acdb_set_audio_cal_t) (void *, void *, uint32_t);
 
 struct meta_key_list {
     struct listnode list;
diff --git a/hal/audio_extn/a2dp.c b/hal/audio_extn/a2dp.c
new file mode 100644
index 0000000..d1671a8
--- /dev/null
+++ b/hal/audio_extn/a2dp.c
@@ -0,0 +1,1739 @@
+/*
+ * Copyright (C) 2013-2018 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 "a2dp_offload"
+/*#define LOG_NDEBUG 0*/
+#define LOG_NDDEBUG 0
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+
+#include <cutils/log.h>
+#include <cutils/str_parms.h>
+#include <cutils/properties.h>
+#include <hardware/audio.h>
+
+#include "audio_hw.h"
+#include "audio_extn.h"
+#include "platform_api.h"
+
+#ifdef A2DP_OFFLOAD_ENABLED
+#define BT_IPC_LIB_NAME  "libbthost_if.so"
+
+// Media format definitions
+#define ENC_MEDIA_FMT_AAC                                  0x00010DA6
+#define ENC_MEDIA_FMT_APTX                                 0x000131ff
+#define ENC_MEDIA_FMT_APTX_HD                              0x00013200
+#define ENC_MEDIA_FMT_LDAC                                 0x00013224
+#define ENC_MEDIA_FMT_SBC                                  0x00010BF2
+#define ENC_MEDIA_FMT_NONE                                 0
+#define MEDIA_FMT_SBC_ALLOCATION_METHOD_LOUDNESS           0
+#define MEDIA_FMT_SBC_ALLOCATION_METHOD_SNR                1
+#define MEDIA_FMT_AAC_AOT_LC                               2
+#define MEDIA_FMT_AAC_AOT_SBR                              5
+#define MEDIA_FMT_AAC_AOT_PS                               29
+#define MEDIA_FMT_SBC_CHANNEL_MODE_MONO                    1
+#define MEDIA_FMT_SBC_CHANNEL_MODE_STEREO                  2
+#define MEDIA_FMT_SBC_CHANNEL_MODE_DUAL_MONO               8
+#define MEDIA_FMT_SBC_CHANNEL_MODE_JOINT_STEREO            9
+
+// PCM channels
+#define PCM_CHANNEL_L                                      1
+#define PCM_CHANNEL_R                                      2
+#define PCM_CHANNEL_C                                      3
+
+// Mixer controls sent to ALSA
+#define MIXER_ENC_CONFIG_BLOCK     "SLIM_7_RX Encoder Config"
+#define MIXER_DEC_CONFIG_BLOCK     "SLIM_7_TX Decoder Config"
+#define MIXER_ENC_BIT_FORMAT       "AFE Input Bit Format"
+#define MIXER_SCRAMBLER_MODE       "AFE Scrambler Mode"
+#define MIXER_SAMPLE_RATE_RX       "BT SampleRate RX"
+#define MIXER_SAMPLE_RATE_TX       "BT SampleRate TX"
+#define MIXER_AFE_IN_CHANNELS      "AFE Input Channels"
+#define MIXER_ABR_TX_FEEDBACK_PATH "A2DP_SLIM7_UL_HL Switch"
+#define MIXER_SET_FEEDBACK_CHANNEL "BT set feedback channel"
+
+// Encoder format strings
+#define ENC_FMT_AAC                "aac"
+#define ENC_FMT_APTX               "aptx"
+#define ENC_FMT_APTXHD             "aptxhd"
+#define ENC_FMT_LDAC               "ldac"
+#define ENC_FMT_SBC                "sbc"
+
+// System properties used for A2DP Offload
+#define SYSPROP_A2DP_OFFLOAD_SUPPORTED "ro.bluetooth.a2dp_offload.supported"
+#define SYSPROP_A2DP_OFFLOAD_DISABLED  "persist.bluetooth.a2dp_offload.disabled"
+#define SYSPROP_A2DP_CODEC_LATENCIES   "vendor.audio.a2dp.codec.latency"
+
+// Default encoder bit width
+#define DEFAULT_ENCODER_BIT_FORMAT 16
+
+// Default encoder latency
+#define DEFAULT_ENCODER_LATENCY    200
+
+// Encoder latency offset for codecs supported
+#define ENCODER_LATENCY_AAC        70
+#define ENCODER_LATENCY_APTX       40
+#define ENCODER_LATENCY_APTX_HD    20
+#define ENCODER_LATENCY_LDAC       40
+#define ENCODER_LATENCY_SBC        10
+#define ENCODER_LATENCY_PCM        50
+
+// Default A2DP sink latency offset
+#define DEFAULT_SINK_LATENCY_AAC       180
+#define DEFAULT_SINK_LATENCY_APTX      160
+#define DEFAULT_SINK_LATENCY_APTX_HD   180
+#define DEFAULT_SINK_LATENCY_LDAC      180
+#define DEFAULT_SINK_LATENCY_SBC       140
+#define DEFAULT_SINK_LATENCY_PCM       140
+
+// Slimbus Tx sample rate for ABR feedback channel
+#define ABR_TX_SAMPLE_RATE             "KHZ_8"
+
+// Purpose ID for Inter Module Communication (IMC) in AFE
+#define IMC_PURPOSE_ID_BT_INFO         0x000132E2
+
+// Maximum quality levels for ABR
+#define MAX_ABR_QUALITY_LEVELS             5
+
+// Instance identifier for A2DP
+#define MAX_INSTANCE_ID                (UINT32_MAX / 2)
+
+/*
+ * Below enum values are extended from audio-base.h to
+ * keep encoder codec type local to bthost_ipc
+ * and audio_hal as these are intended only for handshake
+ * between IPC lib and Audio HAL.
+ */
+typedef enum {
+    ENC_CODEC_TYPE_INVALID = AUDIO_FORMAT_INVALID, // 0xFFFFFFFFUL
+    ENC_CODEC_TYPE_AAC = AUDIO_FORMAT_AAC, // 0x04000000UL
+    ENC_CODEC_TYPE_SBC = AUDIO_FORMAT_SBC, // 0x1F000000UL
+    ENC_CODEC_TYPE_APTX = AUDIO_FORMAT_APTX, // 0x20000000UL
+    ENC_CODEC_TYPE_APTX_HD = AUDIO_FORMAT_APTX_HD, // 0x21000000UL
+    ENC_CODEC_TYPE_LDAC = AUDIO_FORMAT_LDAC, // 0x23000000UL
+    ENC_CODEC_TYPE_PCM = AUDIO_FORMAT_PCM_16_BIT, // 0x1u
+} enc_codec_t;
+
+typedef int (*audio_stream_open_t)(void);
+typedef int (*audio_stream_close_t)(void);
+typedef int (*audio_stream_start_t)(void);
+typedef int (*audio_stream_stop_t)(void);
+typedef int (*audio_stream_suspend_t)(void);
+typedef void (*audio_handoff_triggered_t)(void);
+typedef void (*clear_a2dp_suspend_flag_t)(void);
+typedef void * (*audio_get_codec_config_t)(uint8_t *multicast_status, uint8_t *num_dev,
+                               enc_codec_t *codec_type);
+typedef int (*audio_check_a2dp_ready_t)(void);
+typedef int (*audio_is_scrambling_enabled_t)(void);
+
+enum A2DP_STATE {
+    A2DP_STATE_CONNECTED,
+    A2DP_STATE_STARTED,
+    A2DP_STATE_STOPPED,
+    A2DP_STATE_DISCONNECTED,
+};
+
+typedef enum {
+    IMC_TRANSMIT,
+    IMC_RECEIVE,
+} imc_direction_t;
+
+typedef enum {
+    IMC_DISABLE,
+    IMC_ENABLE,
+} imc_status_t;
+
+/* PCM config for ABR Feedback hostless front end */
+static struct pcm_config pcm_config_abr = {
+    .channels = 1,
+    .rate = 8000,
+    .period_size = 240,
+    .period_count = 2,
+    .format = PCM_FORMAT_S16_LE,
+    .start_threshold = 0,
+    .stop_threshold = INT_MAX,
+    .avail_min = 0,
+};
+
+/* Adaptive bitrate config for A2DP codecs */
+struct a2dp_abr_config {
+    /* Flag to denote whether Adaptive bitrate is enabled for codec */
+    bool is_abr_enabled;
+    /* Flag to denote whether front end has been opened for ABR */
+    bool abr_started;
+    /* ABR Tx path pcm handle */
+    struct pcm *abr_tx_handle;
+    /* ABR Inter Module Communication (IMC) instance ID */
+    uint32_t imc_instance;
+};
+
+static uint32_t instance_id = MAX_INSTANCE_ID;
+
+/* Data structure used to:
+ * - Update the A2DP state machine
+ * - Communicate with the libbthost_if.so IPC library
+ * - Store DSP encoder configuration information
+ */
+struct a2dp_data {
+    /* Audio device handle */
+    struct audio_device *adev;
+    /* Bluetooth IPC library handle */
+    void *bt_lib_handle;
+    /* Open A2DP audio stream. Initialize audio datapath */
+    audio_stream_open_t audio_stream_open;
+    /* Close A2DP audio stream */
+    audio_stream_close_t audio_stream_close;
+    /* Start A2DP audio stream. Start audio datapath */
+    audio_stream_start_t audio_stream_start;
+    /* Stop A2DP audio stream */
+    audio_stream_stop_t audio_stream_stop;
+    /* Suspend A2DP audio stream */
+    audio_stream_suspend_t audio_stream_suspend;
+    /* Notify Bluetooth IPC library of handoff being triggered */
+    audio_handoff_triggered_t audio_handoff_triggered;
+    /* Clear A2DP suspend flag in Bluetooth IPC library */
+    clear_a2dp_suspend_flag_t clear_a2dp_suspend_flag;
+    /* Get codec configuration from Bluetooth stack via
+     * Bluetooth IPC library */
+    audio_get_codec_config_t audio_get_codec_config;
+    /* Check if A2DP is ready */
+    audio_check_a2dp_ready_t audio_check_a2dp_ready;
+    /* Check if scrambling is enabled on BTSoC */
+    audio_is_scrambling_enabled_t audio_is_scrambling_enabled;
+    /* Internal A2DP state identifier */
+    enum A2DP_STATE bt_state;
+    /* A2DP codec type configured */
+    enc_codec_t bt_encoder_format;
+    /* Sampling rate configured with A2DP encoder on DSP */
+    uint32_t enc_sampling_rate;
+    /* Channel configuration of A2DP on DSP */
+    uint32_t enc_channels;
+    /* Flag to denote whether A2DP audio datapath has started */
+    bool a2dp_started;
+    /* Flag to denote whether A2DP audio datapath is suspended */
+    bool a2dp_suspended;
+    /* Number of active sessions on A2DP output */
+    int  a2dp_total_active_session_request;
+    /* Flag to denote whether A2DP offload is enabled */
+    bool is_a2dp_offload_enabled;
+    /* Flag to denote whether codec reconfiguration/soft handoff is in progress */
+    bool is_handoff_in_progress;
+    /* Flag to denote whether APTX Dual Mono encoder is supported */
+    bool is_aptx_dual_mono_supported;
+    /* Adaptive bitrate config for A2DP codecs */
+    struct a2dp_abr_config abr_config;
+};
+
+struct a2dp_data a2dp;
+
+/* Adaptive bitrate (ABR) is supported by certain Bluetooth codecs.
+ * Structures sent to configure DSP for ABR are defined below.
+ * This data helps DSP configure feedback path (BTSoC to LPASS)
+ * for link quality levels and mapping quality levels to codec
+ * specific bitrate.
+ */
+
+/* Key value pair for link quality level to bitrate mapping. */
+struct bit_rate_level_map_t {
+    uint32_t link_quality_level;
+    uint32_t bitrate;
+};
+
+/* Link quality level to bitrate mapping info sent to DSP. */
+struct quality_level_to_bitrate_info {
+    /* Number of quality levels being mapped.
+     * This will be equal to the size of mapping table.
+     */
+    uint32_t num_levels;
+    /* Quality level to bitrate mapping table */
+    struct bit_rate_level_map_t bit_rate_level_map[MAX_ABR_QUALITY_LEVELS];
+};
+
+/* Structure to set up Inter Module Communication (IMC) between
+ * AFE Decoder and Encoder.
+ */
+struct imc_dec_enc_info {
+    /* Decoder to encoder communication direction.
+     * Transmit = 0 / Receive = 1
+     */
+    uint32_t direction;
+    /* Enable / disable IMC between decoder and encoder */
+    uint32_t enable;
+    /* Purpose of IMC being set up between decoder and encoder.
+     * IMC_PURPOSE_ID_BT_INFO defined for link quality feedback
+     * is the default value to be sent as purpose.
+     */
+    uint32_t purpose;
+    /* Unique communication instance ID.
+     * purpose and comm_instance together form the actual key
+     * used in IMC registration, which must be the same for
+     * encoder and decoder for which IMC is being set up.
+     */
+    uint32_t comm_instance;
+};
+
+/* Structure used for ABR config of AFE encoder and decoder. */
+struct abr_enc_cfg_t {
+    /* Link quality level to bitrate mapping info sent to DSP. */
+    struct quality_level_to_bitrate_info mapping_info;
+    /* Information to set up IMC between decoder and encoder */
+    struct imc_dec_enc_info imc_info;
+}  __attribute__ ((packed));
+
+/* Structure to send configuration for decoder introduced
+ * on AFE Tx path for ABR link quality feedback to BT encoder.
+ */
+struct abr_dec_cfg_t {
+    /* Decoder media format */
+    uint32_t dec_format;
+    /* Information to set up IMC between decoder and encoder */
+    struct imc_dec_enc_info imc_info;
+} __attribute__ ((packed));
+
+/* START of DSP configurable structures
+ * These values should match with DSP interface defintion
+ */
+
+/* AAC encoder configuration structure. */
+typedef struct aac_enc_cfg_t aac_enc_cfg_t;
+
+struct aac_enc_cfg_t {
+    /* Encoder media format for AAC */
+    uint32_t      enc_format;
+
+    /* Encoding rate in bits per second */
+    uint32_t      bit_rate;
+
+   /* supported enc_mode are AAC_LC, AAC_SBR, AAC_PS */
+    uint32_t      enc_mode;
+
+    /* supported aac_fmt_flag are ADTS/RAW */
+    uint16_t      aac_fmt_flag;
+
+    /* supported channel_cfg are Native mode, Mono , Stereo */
+    uint16_t      channel_cfg;
+
+    /* Number of samples per second */
+    uint32_t      sample_rate;
+} __attribute__ ((packed));
+
+/* SBC encoder configuration structure. */
+typedef struct sbc_enc_cfg_t sbc_enc_cfg_t;
+
+struct sbc_enc_cfg_t {
+    /* Encoder media format for SBC */
+    uint32_t      enc_format;
+
+    /* supported num_subbands are 4/8 */
+    uint32_t      num_subbands;
+
+    /* supported blk_len are 4, 8, 12, 16 */
+    uint32_t      blk_len;
+
+    /* supported channel_mode are MONO, STEREO, DUAL_MONO, JOINT_STEREO */
+    uint32_t      channel_mode;
+
+    /* supported alloc_method are LOUNDNESS/SNR */
+    uint32_t      alloc_method;
+
+    /* supported bit_rate for mono channel is max 320kbps
+     * supported bit rate for stereo channel is max 512 kbps */
+    uint32_t      bit_rate;
+
+    /* Number of samples per second */
+    uint32_t      sample_rate;
+} __attribute__ ((packed));
+
+struct custom_enc_cfg_t {
+    /* Custom encoder media format */
+    uint32_t      enc_format;
+
+    /* Number of samples per second */
+    uint32_t      sample_rate;
+
+    /* supported num_channels are Mono/Stereo */
+    uint16_t      num_channels;
+
+    /* Reserved for future enhancement */
+    uint16_t      reserved;
+
+    /* supported channel_mapping for mono is CHANNEL_C
+     * supported channel mapping for stereo is CHANNEL_L and CHANNEL_R */
+    uint8_t       channel_mapping[8];
+
+    /* Reserved for future enhancement */
+    uint32_t      custom_size;
+} __attribute__ ((packed));
+
+struct aptx_v2_enc_cfg_ext_t {
+/* sync_mode introduced with APTX V2 libraries
+ * sync mode: 0x0 = stereo sync mode
+ *            0x01 = dual mono sync mode
+ *            0x02 = dual mono with no sync on either L or R codewords
+ */
+    uint32_t       sync_mode;
+} __attribute__ ((packed));
+
+/* APTX struct for combining custom enc and V2 members */
+struct aptx_enc_cfg_t {
+    struct custom_enc_cfg_t  custom_cfg;
+    struct aptx_v2_enc_cfg_ext_t aptx_v2_cfg;
+} __attribute__ ((packed));
+
+struct ldac_specific_enc_cfg_t {
+   /*
+    * This is used to calculate the encoder output
+    * bytes per frame (i.e. bytes per packet).
+    * Bit rate also configures the EQMID.
+    * The min bit rate 303000 bps is calculated for
+    * 44.1 kHz and 88.2 KHz sampling frequencies with
+    * Mobile use Quality.
+    * The max bit rate of 990000 bps is calculated for
+    * 96kHz and 48 KHz with High Quality
+    * @Range(in bits per second)
+    * 303000 for Mobile use Quality
+    * 606000 for standard Quality
+    * 909000 for High Quality
+    */
+    uint32_t      bit_rate;
+
+   /*
+    * The channel setting information for LDAC specification
+    * of Bluetooth A2DP which is determined by SRC and SNK
+    * devices in Bluetooth transmission.
+    * @Range:
+    * 0 for native mode
+    * 4 for mono
+    * 2 for dual channel
+    * 1 for stereo
+    */
+    uint16_t      channel_mode;
+
+   /*
+    * Maximum Transmission Unit (MTU).
+    * The minimum MTU that a L2CAP implementation for LDAC shall
+    * support is 679 bytes, because LDAC is optimized with 2-DH5
+    * packet as its target.
+    * @Range : 679
+    * @Default: 679 for LDACBT_MTU_2DH5
+    */
+    uint16_t      mtu;
+} __attribute__ ((packed));
+
+/* LDAC struct for combining custom enc and standard members */
+struct ldac_enc_cfg_t {
+    struct custom_enc_cfg_t  custom_cfg;
+    struct ldac_specific_enc_cfg_t ldac_cfg;
+    struct abr_enc_cfg_t abr_cfg;
+} __attribute__ ((packed));
+
+/* Information about Bluetooth SBC encoder configuration
+ * This data is used between audio HAL module and
+ * Bluetooth IPC library to configure DSP encoder
+ */
+typedef struct {
+    uint32_t subband;          /* 4, 8 */
+    uint32_t blk_len;          /* 4, 8, 12, 16 */
+    uint16_t sampling_rate;    /* 44.1khz, 48khz */
+    uint8_t  channels;         /* 0(Mono), 1(Dual_mono), 2(Stereo), 3(JS) */
+    uint8_t  alloc;            /* 0(Loudness), 1(SNR) */
+    uint8_t  min_bitpool;      /* 2 */
+    uint8_t  max_bitpool;      /* 53(44.1khz), 51 (48khz) */
+    uint32_t bitrate;          /* 320kbps to 512kbps */
+    uint32_t bits_per_sample;  /* 16 bit */
+} audio_sbc_encoder_config;
+
+/* Information about Bluetooth APTX encoder configuration
+ * This data is used between audio HAL module and
+ * Bluetooth IPC library to configure DSP encoder
+ */
+typedef struct {
+    uint16_t sampling_rate;
+    uint8_t  channels;
+    uint32_t bitrate;
+    uint32_t bits_per_sample;
+} audio_aptx_default_config;
+
+typedef struct {
+    uint16_t sampling_rate;
+    uint8_t  channels;
+    uint32_t bitrate;
+    uint32_t sync_mode;
+} audio_aptx_dual_mono_config;
+
+typedef union {
+    audio_aptx_default_config *default_cfg;
+    audio_aptx_dual_mono_config *dual_mono_cfg;
+} audio_aptx_encoder_config;
+
+/* Information about Bluetooth AAC encoder configuration
+ * This data is used between audio HAL module and
+ * Bluetooth IPC library to configure DSP encoder
+ */
+typedef struct {
+    uint32_t enc_mode; /* LC, SBR, PS */
+    uint16_t format_flag; /* RAW, ADTS */
+    uint16_t channels; /* 1-Mono, 2-Stereo */
+    uint32_t sampling_rate;
+    uint32_t bitrate;
+    uint32_t bits_per_sample;
+} audio_aac_encoder_config;
+
+/* Information about Bluetooth LDAC encoder configuration
+ * This data is used between audio HAL module and
+ * Bluetooth IPC library to configure DSP encoder
+ */
+typedef struct {
+    uint32_t sampling_rate; /* 44100, 48000, 88200, 96000 */
+    uint32_t bit_rate; /* 303000, 606000, 909000 (in bits per second) */
+    uint16_t channel_mode; /* 0, 4, 2, 1 */
+    uint16_t mtu;
+    uint32_t bits_per_sample; /* 16, 24, 32 (bits) */
+    bool is_abr_enabled;
+    struct quality_level_to_bitrate_info level_to_bitrate_map;
+} audio_ldac_encoder_config;
+
+/*********** END of DSP configurable structures ********************/
+
+static void a2dp_common_init()
+{
+    a2dp.a2dp_started = false;
+    a2dp.a2dp_total_active_session_request = 0;
+    a2dp.a2dp_suspended = false;
+    a2dp.bt_encoder_format = ENC_CODEC_TYPE_INVALID;
+    a2dp.bt_state = A2DP_STATE_DISCONNECTED;
+    a2dp.abr_config.is_abr_enabled = false;
+    a2dp.abr_config.abr_started = false;
+    a2dp.abr_config.imc_instance = 0;
+    a2dp.abr_config.abr_tx_handle = NULL;
+}
+
+static void update_offload_codec_support()
+{
+    a2dp.is_a2dp_offload_enabled =
+            property_get_bool(SYSPROP_A2DP_OFFLOAD_SUPPORTED, false) &&
+            !property_get_bool(SYSPROP_A2DP_OFFLOAD_DISABLED, false);
+
+    ALOGD("%s: A2DP offload enabled = %d", __func__,
+          a2dp.is_a2dp_offload_enabled);
+}
+
+static int stop_abr()
+{
+    struct mixer_ctl *ctl_abr_tx_path = NULL;
+    struct mixer_ctl *ctl_set_bt_feedback_channel = NULL;
+
+    /* This function can be used if !abr_started for clean up */
+    ALOGV("%s: enter", __func__);
+
+    // Close hostless front end
+    if (a2dp.abr_config.abr_tx_handle != NULL) {
+        pcm_close(a2dp.abr_config.abr_tx_handle);
+        a2dp.abr_config.abr_tx_handle = NULL;
+    }
+    a2dp.abr_config.abr_started = false;
+    a2dp.abr_config.imc_instance = 0;
+
+    // Reset BT driver mixer control for ABR usecase
+    ctl_set_bt_feedback_channel = mixer_get_ctl_by_name(a2dp.adev->mixer,
+                                        MIXER_SET_FEEDBACK_CHANNEL);
+    if (!ctl_set_bt_feedback_channel) {
+        ALOGE("%s: ERROR Set usecase mixer control not identifed", __func__);
+        return -ENOSYS;
+    }
+    if (mixer_ctl_set_value(ctl_set_bt_feedback_channel, 0, 0) != 0) {
+        ALOGE("%s: Failed to set BT usecase", __func__);
+        return -ENOSYS;
+    }
+
+    // Reset ABR Tx feedback path
+    ALOGV("%s: Disable ABR Tx feedback path", __func__);
+    ctl_abr_tx_path = mixer_get_ctl_by_name(a2dp.adev->mixer,
+                                        MIXER_ABR_TX_FEEDBACK_PATH);
+    if (!ctl_abr_tx_path) {
+        ALOGE("%s: ERROR ABR Tx feedback path mixer control not identifed", __func__);
+        return -ENOSYS;
+    }
+    if (mixer_ctl_set_value(ctl_abr_tx_path, 0, 0) != 0) {
+        ALOGE("%s: Failed to set ABR Tx feedback path", __func__);
+        return -ENOSYS;
+    }
+
+   return 0;
+}
+
+static int start_abr()
+{
+    struct mixer_ctl *ctl_abr_tx_path = NULL;
+    struct mixer_ctl *ctl_set_bt_feedback_channel = NULL;
+    int abr_device_id;
+    int ret = 0;
+
+    if (!a2dp.abr_config.is_abr_enabled) {
+        ALOGE("%s: Cannot start if ABR is not enabled", __func__);
+        return -ENOSYS;
+    }
+
+    if (a2dp.abr_config.abr_started) {
+        ALOGI("%s: ABR has already started", __func__);
+        return ret;
+    }
+
+    // Enable Slimbus 7 Tx feedback path
+    ALOGV("%s: Enable ABR Tx feedback path", __func__);
+    ctl_abr_tx_path = mixer_get_ctl_by_name(a2dp.adev->mixer,
+                                        MIXER_ABR_TX_FEEDBACK_PATH);
+    if (!ctl_abr_tx_path) {
+        ALOGE("%s: ERROR ABR Tx feedback path mixer control not identifed", __func__);
+        return -ENOSYS;
+    }
+    if (mixer_ctl_set_value(ctl_abr_tx_path, 0, 1) != 0) {
+        ALOGE("%s: Failed to set ABR Tx feedback path", __func__);
+        return -ENOSYS;
+    }
+
+    // Notify ABR usecase information to BT driver to distinguish
+    // between SCO and feedback usecase
+    ctl_set_bt_feedback_channel = mixer_get_ctl_by_name(a2dp.adev->mixer,
+                                        MIXER_SET_FEEDBACK_CHANNEL);
+    if (!ctl_set_bt_feedback_channel) {
+        ALOGE("%s: ERROR Set usecase mixer control not identifed", __func__);
+        return -ENOSYS;
+    }
+    if (mixer_ctl_set_value(ctl_set_bt_feedback_channel, 0, 1) != 0) {
+        ALOGE("%s: Failed to set BT usecase", __func__);
+        return -ENOSYS;
+    }
+
+    // Open hostless front end and prepare ABR Tx path
+    abr_device_id = platform_get_pcm_device_id(USECASE_AUDIO_A2DP_ABR_FEEDBACK,
+                                               PCM_CAPTURE);
+    if (!a2dp.abr_config.abr_tx_handle) {
+        a2dp.abr_config.abr_tx_handle = pcm_open(a2dp.adev->snd_card,
+                                                 abr_device_id, PCM_IN,
+                                                 &pcm_config_abr);
+        if (a2dp.abr_config.abr_tx_handle == NULL ||
+            !pcm_is_ready(a2dp.abr_config.abr_tx_handle))
+            goto fail;
+    }
+    ret = pcm_start(a2dp.abr_config.abr_tx_handle);
+    if (ret < 0)
+        goto fail;
+    a2dp.abr_config.abr_started = true;
+
+    return ret;
+
+fail:
+    ALOGE("%s: %s", __func__, pcm_get_error(a2dp.abr_config.abr_tx_handle));
+    stop_abr();
+    return -ENOSYS;
+}
+
+/* API to open Bluetooth IPC library to start IPC communication */
+static int open_a2dp_output()
+{
+    int ret = 0;
+
+    ALOGD("%s: Open A2DP output start", __func__);
+
+    if (a2dp.bt_state != A2DP_STATE_DISCONNECTED) {
+        ALOGD("%s: Called A2DP open with improper state, Ignoring request state %d",
+                   __func__, a2dp.bt_state);
+        return -ENOSYS;
+    }
+
+    if (a2dp.bt_lib_handle == NULL) {
+        ALOGD("%s: Requesting for Bluetooth IPC lib handle", __func__);
+        a2dp.bt_lib_handle = dlopen(BT_IPC_LIB_NAME, RTLD_NOW);
+
+        if (a2dp.bt_lib_handle == NULL) {
+            ret = -errno;
+            ALOGE("%s: DLOPEN failed for %s errno %d strerror %s", __func__,
+                      BT_IPC_LIB_NAME, errno, strerror(errno));
+            a2dp.bt_state = A2DP_STATE_DISCONNECTED;
+            return ret;
+        } else {
+            a2dp.audio_stream_open = (audio_stream_open_t)
+                          dlsym(a2dp.bt_lib_handle, "audio_stream_open");
+            a2dp.audio_stream_start = (audio_stream_start_t)
+                          dlsym(a2dp.bt_lib_handle, "audio_stream_start");
+            a2dp.audio_get_codec_config = (audio_get_codec_config_t)
+                          dlsym(a2dp.bt_lib_handle, "audio_get_codec_config");
+            a2dp.audio_stream_suspend = (audio_stream_suspend_t)
+                          dlsym(a2dp.bt_lib_handle, "audio_stream_suspend");
+            a2dp.audio_handoff_triggered = (audio_handoff_triggered_t)
+                          dlsym(a2dp.bt_lib_handle, "audio_handoff_triggered");
+            a2dp.clear_a2dp_suspend_flag = (clear_a2dp_suspend_flag_t)
+                          dlsym(a2dp.bt_lib_handle, "clear_a2dp_suspend_flag");
+            a2dp.audio_stream_stop = (audio_stream_stop_t)
+                          dlsym(a2dp.bt_lib_handle, "audio_stream_stop");
+            a2dp.audio_stream_close = (audio_stream_close_t)
+                          dlsym(a2dp.bt_lib_handle, "audio_stream_close");
+            a2dp.audio_check_a2dp_ready = (audio_check_a2dp_ready_t)
+                        dlsym(a2dp.bt_lib_handle,"audio_check_a2dp_ready");
+            a2dp.audio_is_scrambling_enabled = (audio_is_scrambling_enabled_t)
+                        dlsym(a2dp.bt_lib_handle,"audio_is_scrambling_enabled");
+        }
+    }
+
+    if (a2dp.bt_lib_handle && a2dp.audio_stream_open) {
+        ALOGD("%s: calling Bluetooth stream open", __func__);
+        ret = a2dp.audio_stream_open();
+        if (ret != 0) {
+            ALOGE("%s: Failed to open output stream for A2DP: status %d", __func__, ret);
+            dlclose(a2dp.bt_lib_handle);
+            a2dp.bt_lib_handle = NULL;
+            a2dp.bt_state = A2DP_STATE_DISCONNECTED;
+            return ret;
+        }
+        a2dp.bt_state = A2DP_STATE_CONNECTED;
+    } else {
+        ALOGE("%s: A2DP handle is not identified, Ignoring open request", __func__);
+        a2dp.bt_state = A2DP_STATE_DISCONNECTED;
+        return -ENOSYS;
+    }
+
+    return ret;
+}
+
+static int close_a2dp_output()
+{
+    ALOGV("%s\n",__func__);
+    if (!(a2dp.bt_lib_handle && a2dp.audio_stream_close)) {
+        ALOGE("%s: A2DP handle is not identified, Ignoring close request", __func__);
+        return -ENOSYS;
+    }
+    if (a2dp.bt_state != A2DP_STATE_DISCONNECTED) {
+        ALOGD("%s: calling Bluetooth stream close", __func__);
+        if (a2dp.audio_stream_close() == false)
+            ALOGE("%s: failed close A2DP control path from Bluetooth IPC library", __func__);
+    }
+    if (a2dp.abr_config.is_abr_enabled && a2dp.abr_config.abr_started)
+        stop_abr();
+    a2dp_common_init();
+    a2dp.enc_sampling_rate = 0;
+    a2dp.enc_channels = 0;
+
+    return 0;
+}
+
+static int a2dp_check_and_set_scrambler()
+{
+    bool scrambler_mode = false;
+    struct mixer_ctl *ctrl_scrambler_mode = NULL;
+    int ret = 0;
+    if (a2dp.audio_is_scrambling_enabled && (a2dp.bt_state != A2DP_STATE_DISCONNECTED))
+        scrambler_mode = a2dp.audio_is_scrambling_enabled();
+
+    // Scrambling needs to be enabled in DSP if scrambler mode is set
+    // disable scrambling not required
+    if (scrambler_mode) {
+        // enable scrambler in dsp
+        ctrl_scrambler_mode = mixer_get_ctl_by_name(a2dp.adev->mixer,
+                                            MIXER_SCRAMBLER_MODE);
+        if (!ctrl_scrambler_mode) {
+            ALOGE("%s: ERROR scrambler mode mixer control not identifed", __func__);
+            return -ENOSYS;
+        } else {
+            ret = mixer_ctl_set_value(ctrl_scrambler_mode, 0, true);
+            if (ret != 0) {
+                ALOGE("%s: Could not set scrambler mode", __func__);
+                return ret;
+            }
+        }
+    }
+    return 0;
+}
+
+static int a2dp_set_backend_cfg()
+{
+    const char *rate_str = NULL, *in_channels = NULL;
+    uint32_t sampling_rate_rx = a2dp.enc_sampling_rate;
+    struct mixer_ctl *ctl_sample_rate = NULL, *ctrl_in_channels = NULL;
+
+    // For LDAC encoder open slimbus port at 96Khz for 48Khz input
+    // and 88.2Khz for 44.1Khz input.
+    if ((a2dp.bt_encoder_format == ENC_CODEC_TYPE_LDAC) &&
+        (sampling_rate_rx == 48000 || sampling_rate_rx == 44100 )) {
+        sampling_rate_rx *= 2;
+    }
+    // No need to configure backend for PCM format.
+    if (a2dp.bt_encoder_format == ENC_CODEC_TYPE_PCM) {
+        return 0;
+    }
+    // Set Rx backend sample rate
+    switch (sampling_rate_rx) {
+    case 44100:
+        rate_str = "KHZ_44P1";
+        break;
+    case 88200:
+        rate_str = "KHZ_88P2";
+        break;
+    case 96000:
+        rate_str = "KHZ_96";
+        break;
+    case 48000:
+    default:
+        rate_str = "KHZ_48";
+        break;
+    }
+
+    ALOGV("%s: set backend rx sample rate = %s", __func__, rate_str);
+    ctl_sample_rate = mixer_get_ctl_by_name(a2dp.adev->mixer,
+                                        MIXER_SAMPLE_RATE_RX);
+    if (!ctl_sample_rate) {
+        ALOGE("%s: ERROR backend sample rate mixer control not identifed", __func__);
+        return -ENOSYS;
+    }
+    if (mixer_ctl_set_enum_by_string(ctl_sample_rate, rate_str) != 0) {
+        ALOGE("%s: Failed to set backend sample rate = %s", __func__, rate_str);
+        return -ENOSYS;
+    }
+
+    // Set Tx backend sample rate
+    if (a2dp.abr_config.is_abr_enabled)
+        rate_str = ABR_TX_SAMPLE_RATE;
+
+    ALOGV("%s: set backend tx sample rate = %s", __func__, rate_str);
+    ctl_sample_rate = mixer_get_ctl_by_name(a2dp.adev->mixer,
+                                        MIXER_SAMPLE_RATE_TX);
+    if (!ctl_sample_rate) {
+        ALOGE("%s: ERROR backend sample rate mixer control not identifed", __func__);
+        return -ENOSYS;
+    }
+    if (mixer_ctl_set_enum_by_string(ctl_sample_rate, rate_str) != 0) {
+        ALOGE("%s: Failed to set backend sample rate = %s",
+                                    __func__, rate_str);
+        return -ENOSYS;
+    }
+
+    // Configure AFE input channels
+    switch (a2dp.enc_channels) {
+    case 1:
+        in_channels = "One";
+        break;
+    case 2:
+    default:
+        in_channels = "Two";
+        break;
+    }
+
+    ALOGV("%s: set AFE input channels = %d", __func__, a2dp.enc_channels);
+    ctrl_in_channels = mixer_get_ctl_by_name(a2dp.adev->mixer,
+                                        MIXER_AFE_IN_CHANNELS);
+    if (!ctrl_in_channels) {
+        ALOGE("%s: ERROR AFE input channels mixer control not identifed", __func__);
+        return -ENOSYS;
+    }
+    if (mixer_ctl_set_enum_by_string(ctrl_in_channels, in_channels) != 0) {
+        ALOGE("%s: Failed to set AFE in channels = %d", __func__, a2dp.enc_channels);
+        return -ENOSYS;
+    }
+
+    return 0;
+}
+
+static int a2dp_set_bit_format(uint32_t enc_bit_format)
+{
+    const char *bit_format = NULL;
+    struct mixer_ctl *ctrl_bit_format = NULL;
+
+    // Configure AFE Input Bit Format
+    switch (enc_bit_format) {
+    case 32:
+        bit_format = "S32_LE";
+        break;
+    case 24:
+        bit_format = "S24_LE";
+        break;
+    case 16:
+    default:
+        bit_format = "S16_LE";
+        break;
+    }
+
+    ALOGD("%s: set AFE input bit format = %d", __func__, enc_bit_format);
+    ctrl_bit_format = mixer_get_ctl_by_name(a2dp.adev->mixer,
+                                        MIXER_ENC_BIT_FORMAT);
+    if (!ctrl_bit_format) {
+        ALOGE("%s: ERROR AFE input bit format mixer control not identifed", __func__);
+        return -ENOSYS;
+    }
+    if (mixer_ctl_set_enum_by_string(ctrl_bit_format, bit_format) != 0) {
+        ALOGE("%s: Failed to set AFE input bit format = %d", __func__, enc_bit_format);
+        return -ENOSYS;
+    }
+    return 0;
+}
+
+static int a2dp_reset_backend_cfg()
+{
+    const char *rate_str = "KHZ_8", *in_channels = "Zero";
+    struct mixer_ctl *ctl_sample_rate_rx = NULL, *ctl_sample_rate_tx = NULL;
+    struct mixer_ctl *ctrl_in_channels = NULL;
+
+    // Reset backend sampling rate
+    ALOGV("%s: reset backend sample rate = %s", __func__, rate_str);
+    ctl_sample_rate_rx = mixer_get_ctl_by_name(a2dp.adev->mixer,
+                                        MIXER_SAMPLE_RATE_RX);
+    if (!ctl_sample_rate_rx) {
+        ALOGE("%s: ERROR Rx backend sample rate mixer control not identifed", __func__);
+        return -ENOSYS;
+    }
+    if (mixer_ctl_set_enum_by_string(ctl_sample_rate_rx, rate_str) != 0) {
+        ALOGE("%s: Failed to reset Rx backend sample rate = %s", __func__, rate_str);
+        return -ENOSYS;
+    }
+
+    ctl_sample_rate_tx = mixer_get_ctl_by_name(a2dp.adev->mixer,
+                                        MIXER_SAMPLE_RATE_TX);
+    if (!ctl_sample_rate_tx) {
+        ALOGE("%s: ERROR Tx backend sample rate mixer control not identifed", __func__);
+        return -ENOSYS;
+    }
+    if (mixer_ctl_set_enum_by_string(ctl_sample_rate_tx, rate_str) != 0) {
+        ALOGE("%s: Failed to reset Tx backend sample rate = %s", __func__, rate_str);
+        return -ENOSYS;
+    }
+
+    // Reset AFE input channels
+    ALOGV("%s: reset AFE input channels = %s", __func__, in_channels);
+    ctrl_in_channels = mixer_get_ctl_by_name(a2dp.adev->mixer,
+                                        MIXER_AFE_IN_CHANNELS);
+    if (!ctrl_in_channels) {
+        ALOGE("%s: ERROR AFE input channels mixer control not identifed", __func__);
+        return -ENOSYS;
+    }
+    if (mixer_ctl_set_enum_by_string(ctrl_in_channels, in_channels) != 0) {
+        ALOGE("%s: Failed to reset AFE in channels = %d", __func__, a2dp.enc_channels);
+        return -ENOSYS;
+    }
+
+    return 0;
+}
+
+/* API to configure AFE decoder in DSP */
+static bool configure_a2dp_decoder_format(int dec_format)
+{
+    struct mixer_ctl *ctl_dec_data = NULL;
+    struct abr_dec_cfg_t dec_cfg;
+    int ret = 0;
+
+    if (a2dp.abr_config.is_abr_enabled) {
+        ctl_dec_data = mixer_get_ctl_by_name(a2dp.adev->mixer, MIXER_DEC_CONFIG_BLOCK);
+        if (!ctl_dec_data) {
+            ALOGE("%s: ERROR A2DP codec config data mixer control not identifed", __func__);
+            return false;
+        }
+        memset(&dec_cfg, 0x0, sizeof(dec_cfg));
+        dec_cfg.dec_format = dec_format;
+        dec_cfg.imc_info.direction = IMC_TRANSMIT;
+        dec_cfg.imc_info.enable = IMC_ENABLE;
+        dec_cfg.imc_info.purpose = IMC_PURPOSE_ID_BT_INFO;
+        dec_cfg.imc_info.comm_instance = a2dp.abr_config.imc_instance;
+
+        ret = mixer_ctl_set_array(ctl_dec_data, (void *)&dec_cfg,
+                                  sizeof(dec_cfg));
+        if (ret != 0) {
+            ALOGE("%s: Failed to set decoder config", __func__);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+/* API to configure SBC DSP encoder */
+static bool configure_sbc_enc_format(audio_sbc_encoder_config *sbc_bt_cfg)
+{
+    struct mixer_ctl *ctl_enc_data = NULL, *ctrl_bit_format = NULL;
+    struct sbc_enc_cfg_t sbc_dsp_cfg;
+    bool is_configured = false;
+    int ret = 0;
+
+    if (sbc_bt_cfg == NULL) {
+        ALOGE("%s: Failed to get SBC encoder config from BT", __func__);
+        return false;
+    }
+
+    ctl_enc_data = mixer_get_ctl_by_name(a2dp.adev->mixer, MIXER_ENC_CONFIG_BLOCK);
+    if (!ctl_enc_data) {
+        ALOGE("%s: ERROR A2DP encoder config data mixer control not identifed", __func__);
+        is_configured = false;
+        goto exit;
+    }
+    memset(&sbc_dsp_cfg, 0x0, sizeof(sbc_dsp_cfg));
+    sbc_dsp_cfg.enc_format = ENC_MEDIA_FMT_SBC;
+    sbc_dsp_cfg.num_subbands = sbc_bt_cfg->subband;
+    sbc_dsp_cfg.blk_len = sbc_bt_cfg->blk_len;
+    switch (sbc_bt_cfg->channels) {
+        case 0:
+            sbc_dsp_cfg.channel_mode = MEDIA_FMT_SBC_CHANNEL_MODE_MONO;
+            break;
+        case 1:
+            sbc_dsp_cfg.channel_mode = MEDIA_FMT_SBC_CHANNEL_MODE_DUAL_MONO;
+            break;
+        case 3:
+            sbc_dsp_cfg.channel_mode = MEDIA_FMT_SBC_CHANNEL_MODE_JOINT_STEREO;
+            break;
+        case 2:
+        default:
+            sbc_dsp_cfg.channel_mode = MEDIA_FMT_SBC_CHANNEL_MODE_STEREO;
+            break;
+    }
+    if (sbc_bt_cfg->alloc)
+        sbc_dsp_cfg.alloc_method = MEDIA_FMT_SBC_ALLOCATION_METHOD_LOUDNESS;
+    else
+        sbc_dsp_cfg.alloc_method = MEDIA_FMT_SBC_ALLOCATION_METHOD_SNR;
+    sbc_dsp_cfg.bit_rate = sbc_bt_cfg->bitrate;
+    sbc_dsp_cfg.sample_rate = sbc_bt_cfg->sampling_rate;
+    ret = mixer_ctl_set_array(ctl_enc_data, (void *)&sbc_dsp_cfg,
+                                    sizeof(sbc_dsp_cfg));
+    if (ret != 0) {
+        ALOGE("%s: failed to set SBC encoder config", __func__);
+        is_configured = false;
+        goto exit;
+    }
+    ret = a2dp_set_bit_format(sbc_bt_cfg->bits_per_sample);
+    if (ret != 0) {
+        is_configured = false;
+        goto exit;
+    }
+    is_configured = true;
+    a2dp.bt_encoder_format = ENC_CODEC_TYPE_SBC;
+    a2dp.enc_sampling_rate = sbc_bt_cfg->sampling_rate;
+
+    if (sbc_dsp_cfg.channel_mode == MEDIA_FMT_SBC_CHANNEL_MODE_MONO)
+        a2dp.enc_channels = 1;
+    else
+        a2dp.enc_channels = 2;
+
+    ALOGV("%s: Successfully updated SBC enc format with sampling rate: %d channel mode:%d",
+           __func__, sbc_dsp_cfg.sample_rate, sbc_dsp_cfg.channel_mode);
+exit:
+    return is_configured;
+}
+
+/* API to configure APTX DSP encoder */
+static bool configure_aptx_enc_format(audio_aptx_encoder_config *aptx_bt_cfg)
+{
+    struct mixer_ctl *ctl_enc_data = NULL, *ctrl_bit_format = NULL;
+    int mixer_size;
+    bool is_configured = false;
+    int ret = 0;
+    struct aptx_enc_cfg_t aptx_dsp_cfg;
+    mixer_size = sizeof(aptx_dsp_cfg);
+
+    if (aptx_bt_cfg == NULL) {
+        ALOGE("%s: Failed to get APTX encoder config from BT", __func__);
+        return false;
+    }
+
+    ctl_enc_data = mixer_get_ctl_by_name(a2dp.adev->mixer, MIXER_ENC_CONFIG_BLOCK);
+    if (!ctl_enc_data) {
+        ALOGE("%s: ERROR A2DP encoder config data mixer control not identifed", __func__);
+        is_configured = false;
+        goto exit;
+    }
+
+    memset(&aptx_dsp_cfg, 0x0, sizeof(aptx_dsp_cfg));
+    aptx_dsp_cfg.custom_cfg.enc_format = ENC_MEDIA_FMT_APTX;
+
+    if (!a2dp.is_aptx_dual_mono_supported) {
+        aptx_dsp_cfg.custom_cfg.sample_rate = aptx_bt_cfg->default_cfg->sampling_rate;
+        aptx_dsp_cfg.custom_cfg.num_channels = aptx_bt_cfg->default_cfg->channels;
+    } else {
+        aptx_dsp_cfg.custom_cfg.sample_rate = aptx_bt_cfg->dual_mono_cfg->sampling_rate;
+        aptx_dsp_cfg.custom_cfg.num_channels = aptx_bt_cfg->dual_mono_cfg->channels;
+        aptx_dsp_cfg.aptx_v2_cfg.sync_mode = aptx_bt_cfg->dual_mono_cfg->sync_mode;
+    }
+
+    switch (aptx_dsp_cfg.custom_cfg.num_channels) {
+        case 1:
+            aptx_dsp_cfg.custom_cfg.channel_mapping[0] = PCM_CHANNEL_C;
+            break;
+        case 2:
+        default:
+            aptx_dsp_cfg.custom_cfg.channel_mapping[0] = PCM_CHANNEL_L;
+            aptx_dsp_cfg.custom_cfg.channel_mapping[1] = PCM_CHANNEL_R;
+            break;
+    }
+    ret = mixer_ctl_set_array(ctl_enc_data, (void *)&aptx_dsp_cfg,
+                              mixer_size);
+    if (ret != 0) {
+        ALOGE("%s: Failed to set APTX encoder config", __func__);
+        is_configured = false;
+        goto exit;
+    }
+    ret = a2dp_set_bit_format(aptx_bt_cfg->default_cfg->bits_per_sample);
+    if (ret != 0) {
+        is_configured = false;
+        goto exit;
+    }
+    is_configured = true;
+    a2dp.bt_encoder_format = ENC_CODEC_TYPE_APTX;
+    a2dp.enc_channels = aptx_dsp_cfg.custom_cfg.num_channels;
+    if (!a2dp.is_aptx_dual_mono_supported) {
+        a2dp.enc_sampling_rate = aptx_bt_cfg->default_cfg->sampling_rate;
+        ALOGV("%s: Successfully updated APTX enc format with sampling rate: %d \
+               channels:%d", __func__, aptx_dsp_cfg.custom_cfg.sample_rate,
+               aptx_dsp_cfg.custom_cfg.num_channels);
+    } else {
+        a2dp.enc_sampling_rate = aptx_bt_cfg->dual_mono_cfg->sampling_rate;
+        ALOGV("%s: Successfully updated APTX dual mono enc format with \
+               sampling rate: %d channels:%d sync mode %d", __func__,
+               aptx_dsp_cfg.custom_cfg.sample_rate,
+               aptx_dsp_cfg.custom_cfg.num_channels,
+               aptx_dsp_cfg.aptx_v2_cfg.sync_mode);
+    }
+
+exit:
+    return is_configured;
+}
+
+/* API to configure APTX HD DSP encoder
+ */
+static bool configure_aptx_hd_enc_format(audio_aptx_default_config *aptx_bt_cfg)
+{
+    struct mixer_ctl *ctl_enc_data = NULL, *ctrl_bit_format = NULL;
+    struct custom_enc_cfg_t aptx_dsp_cfg;
+    bool is_configured = false;
+    int ret = 0;
+
+    if (aptx_bt_cfg == NULL) {
+        ALOGE("%s: Failed to get APTX HD encoder config from BT", __func__);
+        return false;
+    }
+
+    ctl_enc_data = mixer_get_ctl_by_name(a2dp.adev->mixer, MIXER_ENC_CONFIG_BLOCK);
+    if (!ctl_enc_data) {
+        ALOGE("%s: ERROR A2DP encoder config data mixer control not identifed", __func__);
+        is_configured = false;
+        goto exit;
+    }
+
+    memset(&aptx_dsp_cfg, 0x0, sizeof(aptx_dsp_cfg));
+    aptx_dsp_cfg.enc_format = ENC_MEDIA_FMT_APTX_HD;
+    aptx_dsp_cfg.sample_rate = aptx_bt_cfg->sampling_rate;
+    aptx_dsp_cfg.num_channels = aptx_bt_cfg->channels;
+    switch (aptx_dsp_cfg.num_channels) {
+        case 1:
+            aptx_dsp_cfg.channel_mapping[0] = PCM_CHANNEL_C;
+            break;
+        case 2:
+        default:
+            aptx_dsp_cfg.channel_mapping[0] = PCM_CHANNEL_L;
+            aptx_dsp_cfg.channel_mapping[1] = PCM_CHANNEL_R;
+            break;
+    }
+    ret = mixer_ctl_set_array(ctl_enc_data, (void *)&aptx_dsp_cfg,
+                              sizeof(aptx_dsp_cfg));
+    if (ret != 0) {
+        ALOGE("%s: Failed to set APTX HD encoder config", __func__);
+        is_configured = false;
+        goto exit;
+    }
+    ret = a2dp_set_bit_format(aptx_bt_cfg->bits_per_sample);
+    if (ret != 0) {
+        is_configured = false;
+        goto exit;
+    }
+    is_configured = true;
+    a2dp.bt_encoder_format = ENC_CODEC_TYPE_APTX_HD;
+    a2dp.enc_sampling_rate = aptx_bt_cfg->sampling_rate;
+    a2dp.enc_channels = aptx_bt_cfg->channels;
+    ALOGV("%s: Successfully updated APTX HD encformat with sampling rate: %d channels:%d",
+           __func__, aptx_dsp_cfg.sample_rate, aptx_dsp_cfg.num_channels);
+exit:
+    return is_configured;
+}
+
+/* API to configure AAC DSP encoder */
+static bool configure_aac_enc_format(audio_aac_encoder_config *aac_bt_cfg)
+{
+    struct mixer_ctl *ctl_enc_data = NULL, *ctrl_bit_format = NULL;
+    struct aac_enc_cfg_t aac_dsp_cfg;
+    bool is_configured = false;
+    int ret = 0;
+
+    if (aac_bt_cfg == NULL) {
+        ALOGE("%s: Failed to get AAC encoder config from BT", __func__);
+        return false;
+    }
+
+    ctl_enc_data = mixer_get_ctl_by_name(a2dp.adev->mixer, MIXER_ENC_CONFIG_BLOCK);
+    if (!ctl_enc_data) {
+        ALOGE("%s: ERROR A2DP encoder config data mixer control not identifed", __func__);
+        is_configured = false;
+        goto exit;
+    }
+    memset(&aac_dsp_cfg, 0x0, sizeof(aac_dsp_cfg));
+    aac_dsp_cfg.enc_format = ENC_MEDIA_FMT_AAC;
+    aac_dsp_cfg.bit_rate = aac_bt_cfg->bitrate;
+    aac_dsp_cfg.sample_rate = aac_bt_cfg->sampling_rate;
+    switch (aac_bt_cfg->enc_mode) {
+        case 0:
+            aac_dsp_cfg.enc_mode = MEDIA_FMT_AAC_AOT_LC;
+            break;
+        case 2:
+            aac_dsp_cfg.enc_mode = MEDIA_FMT_AAC_AOT_PS;
+            break;
+        case 1:
+        default:
+            aac_dsp_cfg.enc_mode = MEDIA_FMT_AAC_AOT_SBR;
+            break;
+    }
+    aac_dsp_cfg.aac_fmt_flag = aac_bt_cfg->format_flag;
+    aac_dsp_cfg.channel_cfg = aac_bt_cfg->channels;
+    ret = mixer_ctl_set_array(ctl_enc_data, (void *)&aac_dsp_cfg,
+                              sizeof(aac_dsp_cfg));
+    if (ret != 0) {
+        ALOGE("%s: failed to set AAC encoder config", __func__);
+        is_configured = false;
+        goto exit;
+    }
+    ret = a2dp_set_bit_format(aac_bt_cfg->bits_per_sample);
+    if (ret != 0) {
+        is_configured = false;
+        goto exit;
+    }
+    is_configured = true;
+    a2dp.bt_encoder_format = ENC_CODEC_TYPE_AAC;
+    a2dp.enc_sampling_rate = aac_bt_cfg->sampling_rate;
+    a2dp.enc_channels = aac_bt_cfg->channels;
+    ALOGV("%s: Successfully updated AAC enc format with sampling rate: %d channels:%d",
+           __func__, aac_dsp_cfg.sample_rate, aac_dsp_cfg.channel_cfg);
+exit:
+    return is_configured;
+}
+
+static bool configure_ldac_enc_format(audio_ldac_encoder_config *ldac_bt_cfg)
+{
+    struct mixer_ctl *ldac_enc_data = NULL, *ctrl_bit_format = NULL;
+    struct ldac_enc_cfg_t ldac_dsp_cfg;
+    bool is_configured = false;
+    int ret = 0;
+
+    if (ldac_bt_cfg == NULL) {
+        ALOGE("%s: Failed to get LDAC encoder config from BT", __func__);
+        return false;
+    }
+
+    ldac_enc_data = mixer_get_ctl_by_name(a2dp.adev->mixer, MIXER_ENC_CONFIG_BLOCK);
+    if (!ldac_enc_data) {
+        ALOGE("%s: ERROR A2DP encoder config data mixer control not identifed", __func__);
+        is_configured = false;
+        goto exit;
+    }
+    memset(&ldac_dsp_cfg, 0x0, sizeof(ldac_dsp_cfg));
+
+    ldac_dsp_cfg.custom_cfg.enc_format = ENC_MEDIA_FMT_LDAC;
+    ldac_dsp_cfg.custom_cfg.sample_rate = ldac_bt_cfg->sampling_rate;
+    ldac_dsp_cfg.ldac_cfg.channel_mode = ldac_bt_cfg->channel_mode;
+    switch (ldac_dsp_cfg.ldac_cfg.channel_mode) {
+        case 4:
+            ldac_dsp_cfg.custom_cfg.channel_mapping[0] = PCM_CHANNEL_C;
+            ldac_dsp_cfg.custom_cfg.num_channels = 1;
+            break;
+        case 2:
+        case 1:
+        default:
+            ldac_dsp_cfg.custom_cfg.channel_mapping[0] = PCM_CHANNEL_L;
+            ldac_dsp_cfg.custom_cfg.channel_mapping[1] = PCM_CHANNEL_R;
+            ldac_dsp_cfg.custom_cfg.num_channels = 2;
+            break;
+    }
+
+    ldac_dsp_cfg.custom_cfg.custom_size = sizeof(ldac_dsp_cfg);
+    ldac_dsp_cfg.ldac_cfg.mtu = ldac_bt_cfg->mtu;
+    ldac_dsp_cfg.ldac_cfg.bit_rate = ldac_bt_cfg->bit_rate;
+    if (ldac_bt_cfg->is_abr_enabled) {
+        ldac_dsp_cfg.abr_cfg.mapping_info = ldac_bt_cfg->level_to_bitrate_map;
+        ldac_dsp_cfg.abr_cfg.imc_info.direction = IMC_RECEIVE;
+        ldac_dsp_cfg.abr_cfg.imc_info.enable = IMC_ENABLE;
+        ldac_dsp_cfg.abr_cfg.imc_info.purpose = IMC_PURPOSE_ID_BT_INFO;
+        ldac_dsp_cfg.abr_cfg.imc_info.comm_instance = a2dp.abr_config.imc_instance;
+    }
+
+    ret = mixer_ctl_set_array(ldac_enc_data, (void *)&ldac_dsp_cfg,
+                              sizeof(ldac_dsp_cfg));
+    if (ret != 0) {
+        ALOGE("%s: Failed to set LDAC encoder config", __func__);
+        is_configured = false;
+        goto exit;
+    }
+    ret = a2dp_set_bit_format(ldac_bt_cfg->bits_per_sample);
+    if (ret != 0) {
+        is_configured = false;
+        goto exit;
+    }
+    is_configured = true;
+    a2dp.bt_encoder_format = ENC_CODEC_TYPE_LDAC;
+    a2dp.enc_sampling_rate = ldac_bt_cfg->sampling_rate;
+    a2dp.enc_channels = ldac_dsp_cfg.custom_cfg.num_channels;
+    a2dp.abr_config.is_abr_enabled = ldac_bt_cfg->is_abr_enabled;
+    ALOGV("%s: Successfully updated LDAC encformat with sampling rate: %d channels:%d",
+           __func__, ldac_dsp_cfg.custom_cfg.sample_rate,
+           ldac_dsp_cfg.custom_cfg.num_channels);
+exit:
+    return is_configured;
+}
+
+bool configure_a2dp_encoder_format()
+{
+    void *codec_info = NULL;
+    uint8_t multi_cast = 0, num_dev = 1;
+    enc_codec_t codec_type = ENC_CODEC_TYPE_INVALID;
+    bool is_configured = false;
+    audio_aptx_encoder_config aptx_encoder_cfg;
+
+    if (!a2dp.audio_get_codec_config) {
+        ALOGE("%s: A2DP handle is not identified, ignoring A2DP encoder config", __func__);
+        return false;
+    }
+    ALOGD("%s: start", __func__);
+    codec_info = a2dp.audio_get_codec_config(&multi_cast, &num_dev,
+                               &codec_type);
+
+    // ABR disabled by default for all codecs
+    a2dp.abr_config.is_abr_enabled = false;
+
+    switch (codec_type) {
+        case ENC_CODEC_TYPE_SBC:
+            ALOGD("%s: Received SBC encoder supported Bluetooth device", __func__);
+            is_configured =
+              configure_sbc_enc_format((audio_sbc_encoder_config *)codec_info);
+            break;
+        case ENC_CODEC_TYPE_APTX:
+            ALOGD("%s: Received APTX encoder supported Bluetooth device", __func__);
+            a2dp.is_aptx_dual_mono_supported = false;
+            aptx_encoder_cfg.default_cfg = (audio_aptx_default_config *)codec_info;
+            is_configured =
+              configure_aptx_enc_format(&aptx_encoder_cfg);
+            break;
+        case ENC_CODEC_TYPE_APTX_HD:
+            ALOGD("%s: Received APTX HD encoder supported Bluetooth device", __func__);
+            is_configured =
+              configure_aptx_hd_enc_format((audio_aptx_default_config *)codec_info);
+            break;
+        case ENC_CODEC_TYPE_AAC:
+            ALOGD("%s: Received AAC encoder supported Bluetooth device", __func__);
+            is_configured =
+              configure_aac_enc_format((audio_aac_encoder_config *)codec_info);
+            break;
+        case ENC_CODEC_TYPE_LDAC:
+            ALOGD("%s: Received LDAC encoder supported Bluetooth device", __func__);
+            if (!instance_id || instance_id > MAX_INSTANCE_ID)
+                instance_id = MAX_INSTANCE_ID;
+            a2dp.abr_config.imc_instance = instance_id--;
+            is_configured =
+                (configure_ldac_enc_format((audio_ldac_encoder_config *)codec_info) &&
+                 configure_a2dp_decoder_format(ENC_CODEC_TYPE_LDAC));
+            break;
+        case ENC_CODEC_TYPE_PCM:
+            ALOGD("Received PCM format for BT device");
+            a2dp.bt_encoder_format = ENC_CODEC_TYPE_PCM;
+            is_configured = true;
+            break;
+        default:
+            ALOGD("%s: Received unsupported encoder format", __func__);
+            is_configured = false;
+            break;
+    }
+    return is_configured;
+}
+
+int audio_extn_a2dp_start_playback()
+{
+    int ret = 0;
+
+    ALOGD("%s: start", __func__);
+
+    if (!(a2dp.bt_lib_handle && a2dp.audio_stream_start
+       && a2dp.audio_get_codec_config)) {
+        ALOGE("%s: A2DP handle is not identified, Ignoring start request", __func__);
+        return -ENOSYS;
+    }
+
+    if (a2dp.a2dp_suspended) {
+        // session will be restarted after suspend completion
+        ALOGD("%s: A2DP start requested during suspend state", __func__);
+        return -ENOSYS;
+    }
+
+    if (!a2dp.a2dp_started && !a2dp.a2dp_total_active_session_request) {
+        ALOGD("%s: calling Bluetooth module stream start", __func__);
+        /* This call indicates Bluetooth IPC lib to start playback */
+        ret =  a2dp.audio_stream_start();
+        if (ret != 0 ) {
+           ALOGE("%s: Bluetooth controller start failed", __func__);
+           a2dp.a2dp_started = false;
+        } else {
+           if (configure_a2dp_encoder_format() == true) {
+                a2dp.a2dp_started = true;
+                ret = 0;
+                ALOGD("%s: Start playback successful to Bluetooth IPC library", __func__);
+           } else {
+                ALOGD("%s: unable to configure DSP encoder", __func__);
+                a2dp.a2dp_started = false;
+                ret = -ETIMEDOUT;
+           }
+        }
+    }
+
+    if (a2dp.a2dp_started) {
+        a2dp.a2dp_total_active_session_request++;
+        a2dp_check_and_set_scrambler();
+        a2dp_set_backend_cfg();
+        if (a2dp.abr_config.is_abr_enabled)
+            start_abr();
+    }
+
+    ALOGD("%s: start A2DP playback total active sessions :%d", __func__,
+          a2dp.a2dp_total_active_session_request);
+    return ret;
+}
+
+static int reset_a2dp_enc_config_params()
+{
+    int ret = 0;
+
+    struct mixer_ctl *ctl_enc_config, *ctrl_bit_format;
+    struct sbc_enc_cfg_t dummy_reset_config;
+
+    memset(&dummy_reset_config, 0x0, sizeof(dummy_reset_config));
+    ctl_enc_config = mixer_get_ctl_by_name(a2dp.adev->mixer,
+                                           MIXER_ENC_CONFIG_BLOCK);
+    if (!ctl_enc_config) {
+        ALOGE("%s: ERROR A2DP encoder format mixer control not identifed", __func__);
+    } else {
+        ret = mixer_ctl_set_array(ctl_enc_config, (void *)&dummy_reset_config,
+                                        sizeof(dummy_reset_config));
+         a2dp.bt_encoder_format = ENC_MEDIA_FMT_NONE;
+    }
+
+    ret = a2dp_set_bit_format(DEFAULT_ENCODER_BIT_FORMAT);
+
+    return ret;
+}
+
+static int reset_a2dp_dec_config_params()
+{
+    struct mixer_ctl *ctl_dec_data = NULL;
+    struct abr_dec_cfg_t dummy_reset_cfg;
+    int ret = 0;
+
+    if (a2dp.abr_config.is_abr_enabled) {
+        ctl_dec_data = mixer_get_ctl_by_name(a2dp.adev->mixer, MIXER_DEC_CONFIG_BLOCK);
+        if (!ctl_dec_data) {
+            ALOGE("%s: ERROR A2DP decoder config mixer control not identifed", __func__);
+            return -EINVAL;
+        }
+        memset(&dummy_reset_cfg, 0x0, sizeof(dummy_reset_cfg));
+        ret = mixer_ctl_set_array(ctl_dec_data, (void *)&dummy_reset_cfg,
+                                  sizeof(dummy_reset_cfg));
+        if (ret != 0) {
+            ALOGE("%s: Failed to set dummy decoder config", __func__);
+            return ret;
+        }
+    }
+
+    return ret;
+}
+
+int audio_extn_a2dp_stop_playback()
+{
+    int ret = 0;
+
+    ALOGV("%s: stop", __func__);
+    if (!(a2dp.bt_lib_handle && a2dp.audio_stream_stop)) {
+        ALOGE("%s: A2DP handle is not identified, Ignoring start request", __func__);
+        return -ENOSYS;
+    }
+
+    if (a2dp.a2dp_total_active_session_request > 0)
+        a2dp.a2dp_total_active_session_request--;
+    else
+        ALOGE("%s: No active playback session requests on A2DP", __func__);
+
+    if (a2dp.a2dp_started && !a2dp.a2dp_total_active_session_request) {
+        ALOGV("%s: calling Bluetooth module stream stop", __func__);
+        ret = a2dp.audio_stream_stop();
+        if (ret < 0)
+            ALOGE("%s: stop stream to Bluetooth IPC lib failed", __func__);
+        else
+            ALOGV("%s: stop steam to Bluetooth IPC lib successful", __func__);
+        reset_a2dp_enc_config_params();
+        reset_a2dp_dec_config_params();
+        a2dp_reset_backend_cfg();
+        if (a2dp.abr_config.is_abr_enabled && a2dp.abr_config.abr_started)
+            stop_abr();
+        a2dp.abr_config.is_abr_enabled = false;
+        a2dp.a2dp_started = false;
+    }
+    ALOGD("%s: Stop A2DP playback total active sessions :%d", __func__,
+          a2dp.a2dp_total_active_session_request);
+    return 0;
+}
+
+int audio_extn_a2dp_set_parameters(struct str_parms *parms, bool *reconfig)
+{
+     int ret = 0, val;
+     char value[32] = {0};
+     struct audio_usecase *uc_info;
+     struct listnode *node;
+
+     if (a2dp.is_a2dp_offload_enabled == false) {
+        ALOGV("%s: No supported encoders identified,ignoring A2DP setparam", __func__);
+        ret = -EINVAL;
+        goto param_handled;
+     }
+
+     ret = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_CONNECT, value,
+                            sizeof(value));
+     if (ret >= 0) {
+         val = atoi(value);
+         if (audio_is_a2dp_out_device(val)) {
+             ALOGV("%s: Received device connect request for A2DP", __func__);
+             open_a2dp_output();
+         }
+         goto param_handled;
+     }
+
+     ret = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_DISCONNECT, value,
+                         sizeof(value));
+
+     if (ret >= 0) {
+         val = atoi(value);
+         if (audio_is_a2dp_out_device(val)) {
+             ALOGV("%s: Received device disconnect request", __func__);
+             reset_a2dp_enc_config_params();
+             reset_a2dp_dec_config_params();
+             close_a2dp_output();
+         }
+         goto param_handled;
+     }
+
+     ret = str_parms_get_str(parms, "A2dpSuspended", value, sizeof(value));
+     if (ret >= 0) {
+         if (a2dp.bt_lib_handle && (a2dp.bt_state != A2DP_STATE_DISCONNECTED)) {
+             if (strncmp(value, "true", sizeof(value)) == 0) {
+                if (a2dp.a2dp_suspended) {
+                    ALOGD("%s: A2DP is already suspended", __func__);
+                    goto param_handled;
+                }
+                ALOGD("%s: Setting A2DP to suspend state", __func__);
+                a2dp.a2dp_suspended = true;
+                list_for_each(node, &a2dp.adev->usecase_list) {
+                    uc_info = node_to_item(node, struct audio_usecase, list);
+                    if (uc_info->type == PCM_PLAYBACK &&
+                         (uc_info->stream.out->devices & AUDIO_DEVICE_OUT_ALL_A2DP)) {
+                        pthread_mutex_unlock(&a2dp.adev->lock);
+                        check_a2dp_restore(a2dp.adev, uc_info->stream.out, false);
+                        pthread_mutex_lock(&a2dp.adev->lock);
+                    }
+                }
+                reset_a2dp_enc_config_params();
+                reset_a2dp_dec_config_params();
+                if (a2dp.audio_stream_suspend) {
+                   a2dp.audio_stream_suspend();
+                }
+            } else {
+                if (!a2dp.a2dp_suspended) {
+                    ALOGD("%s: A2DP is already unsuspended", __func__);
+                    goto param_handled;
+                }
+                ALOGD("%s: Resetting A2DP suspend state", __func__);
+                struct audio_usecase *uc_info;
+                struct listnode *node;
+                if (a2dp.clear_a2dp_suspend_flag) {
+                    a2dp.clear_a2dp_suspend_flag();
+                }
+                a2dp.a2dp_suspended = false;
+                /*
+                 * It is possible that before suspend, A2DP sessions can be active.
+                 * For example, during music + voice activation concurrency,
+                 * A2DP suspend will be called & Bluetooth will change to SCO mode.
+                 * Though music is paused as a part of voice activation,
+                 * compress session close happens only after pause timeout(10 secs).
+                 * So, if resume request comes before pause timeout, as A2DP session
+                 * is already active, IPC start will not be called from APM/audio_hw.
+                 * Fix this by calling A2DP start for IPC library post suspend
+                 * based on number of active session count.
+                 */
+                if (a2dp.a2dp_total_active_session_request > 0) {
+                    ALOGD("%s: Calling Bluetooth IPC lib start post suspend state", __func__);
+                    if (a2dp.audio_stream_start) {
+                        ret =  a2dp.audio_stream_start();
+                        if (ret != 0) {
+                            ALOGE("%s: Bluetooth controller start failed", __func__);
+                            a2dp.a2dp_started = false;
+                        }
+                    }
+                }
+                list_for_each(node, &a2dp.adev->usecase_list) {
+                    uc_info = node_to_item(node, struct audio_usecase, list);
+                    if (uc_info->type == PCM_PLAYBACK &&
+                         (uc_info->stream.out->devices & AUDIO_DEVICE_OUT_ALL_A2DP)) {
+                        pthread_mutex_unlock(&a2dp.adev->lock);
+                        check_a2dp_restore(a2dp.adev, uc_info->stream.out, true);
+                        pthread_mutex_lock(&a2dp.adev->lock);
+                    }
+                }
+            }
+        }
+        goto param_handled;
+     }
+
+     ret = str_parms_get_str(parms, AUDIO_PARAMETER_RECONFIG_A2DP, value,
+                         sizeof(value));
+     if (ret >= 0) {
+         if (a2dp.is_a2dp_offload_enabled &&
+                a2dp.bt_state != A2DP_STATE_DISCONNECTED) {
+             *reconfig = true;
+         }
+         goto param_handled;
+     }
+
+param_handled:
+     ALOGV("%s: end of A2DP setparam", __func__);
+     return ret;
+}
+
+void audio_extn_a2dp_set_handoff_mode(bool is_on)
+{
+    a2dp.is_handoff_in_progress = is_on;
+}
+
+bool audio_extn_a2dp_is_force_device_switch()
+{
+    // During encoder reconfiguration mode, force A2DP device switch
+    // Or if A2DP device is selected but earlier start failed as A2DP
+    // was suspended, force retry.
+    return a2dp.is_handoff_in_progress || !a2dp.a2dp_started;
+}
+
+void audio_extn_a2dp_get_sample_rate(int *sample_rate)
+{
+    *sample_rate = a2dp.enc_sampling_rate;
+}
+
+bool audio_extn_a2dp_is_ready()
+{
+    bool ret = false;
+
+    if (a2dp.a2dp_suspended)
+        goto exit;
+
+    if ((a2dp.bt_state != A2DP_STATE_DISCONNECTED) &&
+        (a2dp.is_a2dp_offload_enabled) &&
+        (a2dp.audio_check_a2dp_ready))
+           ret = a2dp.audio_check_a2dp_ready();
+
+exit:
+    return ret;
+}
+
+bool audio_extn_a2dp_is_suspended()
+{
+    return a2dp.a2dp_suspended;
+}
+
+void audio_extn_a2dp_init(void *adev)
+{
+  a2dp.adev = (struct audio_device*)adev;
+  a2dp.bt_lib_handle = NULL;
+  a2dp_common_init();
+  a2dp.enc_sampling_rate = 48000;
+  a2dp.is_a2dp_offload_enabled = false;
+  a2dp.is_handoff_in_progress = false;
+  a2dp.is_aptx_dual_mono_supported = false;
+  reset_a2dp_enc_config_params();
+  reset_a2dp_dec_config_params();
+  update_offload_codec_support();
+}
+
+uint32_t audio_extn_a2dp_get_encoder_latency()
+{
+    uint32_t latency = 0;
+    int avsync_runtime_prop = 0;
+    int sbc_offset = 0, aptx_offset = 0, aptxhd_offset = 0,
+        aac_offset = 0, ldac_offset = 0;
+    char value[PROPERTY_VALUE_MAX];
+
+    memset(value, '\0', sizeof(char) * PROPERTY_VALUE_MAX);
+    avsync_runtime_prop = property_get(SYSPROP_A2DP_CODEC_LATENCIES, value, NULL);
+    if (avsync_runtime_prop > 0) {
+        if (sscanf(value, "%d/%d/%d/%d/%d",
+            &sbc_offset, &aptx_offset, &aptxhd_offset, &aac_offset,
+            &ldac_offset) != 5) {
+            ALOGI("%s: Failed to parse avsync offset params from '%s'.", __func__, value);
+            avsync_runtime_prop = 0;
+        }
+    }
+
+    switch (a2dp.bt_encoder_format) {
+        case ENC_CODEC_TYPE_SBC:
+            latency = (avsync_runtime_prop > 0) ? sbc_offset : ENCODER_LATENCY_SBC;
+            latency += DEFAULT_SINK_LATENCY_SBC;
+            break;
+        case ENC_CODEC_TYPE_APTX:
+            latency = (avsync_runtime_prop > 0) ? aptx_offset : ENCODER_LATENCY_APTX;
+            latency += DEFAULT_SINK_LATENCY_APTX;
+            break;
+        case ENC_CODEC_TYPE_APTX_HD:
+            latency = (avsync_runtime_prop > 0) ? aptxhd_offset : ENCODER_LATENCY_APTX_HD;
+            latency += DEFAULT_SINK_LATENCY_APTX_HD;
+            break;
+        case ENC_CODEC_TYPE_AAC:
+            latency = (avsync_runtime_prop > 0) ? aac_offset : ENCODER_LATENCY_AAC;
+            latency += DEFAULT_SINK_LATENCY_AAC;
+            break;
+        case ENC_CODEC_TYPE_LDAC:
+            latency = (avsync_runtime_prop > 0) ? ldac_offset : ENCODER_LATENCY_LDAC;
+            latency += DEFAULT_SINK_LATENCY_LDAC;
+            break;
+        case ENC_CODEC_TYPE_PCM:
+            latency = ENCODER_LATENCY_PCM;
+            latency += DEFAULT_SINK_LATENCY_PCM;
+            break;
+        default:
+            latency = DEFAULT_ENCODER_LATENCY;
+            break;
+    }
+    return latency;
+}
+
+int audio_extn_a2dp_get_parameters(struct str_parms *query,
+                                   struct str_parms *reply)
+{
+    int ret, val = 0;
+    char value[32]={0};
+
+    ret = str_parms_get_str(query, AUDIO_PARAMETER_A2DP_RECONFIG_SUPPORTED,
+                            value, sizeof(value));
+    if (ret >= 0) {
+        val = a2dp.is_a2dp_offload_enabled;
+        str_parms_add_int(reply, AUDIO_PARAMETER_A2DP_RECONFIG_SUPPORTED, val);
+        ALOGV("%s: called ... isReconfigA2dpSupported %d", __func__, val);
+    }
+
+    return 0;
+}
+#endif // A2DP_OFFLOAD_ENABLED
diff --git a/hal/audio_extn/audio_extn.c b/hal/audio_extn/audio_extn.c
index 4edbf9b..e127fef 100644
--- a/hal/audio_extn/audio_extn.c
+++ b/hal/audio_extn/audio_extn.c
@@ -22,7 +22,7 @@
 #include <errno.h>
 #include <dlfcn.h>
 #include <cutils/properties.h>
-#include <cutils/log.h>
+#include <log/log.h>
 
 #include "audio_hw.h"
 #include "audio_extn.h"
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index c7d6768..b1c701d 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -41,14 +41,12 @@
 #define audio_extn_spkr_prot_calib_cancel(adev) (0)
 #define audio_extn_spkr_prot_stop_processing(snd_device)     (0)
 #define audio_extn_spkr_prot_is_enabled() (false)
-#define audio_extn_spkr_prot_get_acdb_id(snd_device)         (-EINVAL)
 #define audio_extn_get_spkr_prot_snd_device(snd_device) (snd_device)
 #else
 void audio_extn_spkr_prot_init(void *adev);
 int audio_extn_spkr_prot_start_processing(snd_device_t snd_device);
 void audio_extn_spkr_prot_stop_processing(snd_device_t snd_device);
 bool audio_extn_spkr_prot_is_enabled();
-int audio_extn_spkr_prot_get_acdb_id(snd_device_t snd_device);
 int audio_extn_get_spkr_prot_snd_device(snd_device_t snd_device);
 void audio_extn_spkr_prot_calib_cancel(void *adev);
 #endif
@@ -81,8 +79,10 @@
 #define audio_extn_usb_is_capture_supported()                          (false)
 #define audio_extn_usb_get_max_channels(dir)                           (0)
 #define audio_extn_usb_get_max_bit_width(dir)                          (0)
-#define audio_extn_usb_sup_sample_rates(t, s, l)                       (0)
+#define audio_extn_usb_sup_sample_rates(t, s, l)        ((t), (s), (l), 0) /* fix unused warn */
 #define audio_extn_usb_alive(adev)                                     (false)
+#define audio_extn_usb_find_service_interval(m, p)      ((m), (p), 0) /* fix unused warn */
+#define audio_extn_usb_altset_for_service_interval(p, si, bw, sr, ch) (-1)
 #else
 void audio_extn_usb_init(void *adev);
 void audio_extn_usb_deinit();
@@ -100,6 +100,12 @@
 int audio_extn_usb_get_max_bit_width(bool is_playback);
 int audio_extn_usb_sup_sample_rates(bool is_playback, uint32_t *sr, uint32_t l);
 bool audio_extn_usb_alive(int card);
+unsigned long audio_extn_usb_find_service_interval(bool min, bool playback);
+int audio_extn_usb_altset_for_service_interval(bool is_playback,
+                                               unsigned long service_interval,
+                                               uint32_t *bit_width,
+                                               uint32_t *sample_rate,
+                                               uint32_t *channel_count);
 #endif
 
 
@@ -107,6 +113,7 @@
 #define audio_extn_sound_trigger_init(adev)                            (0)
 #define audio_extn_sound_trigger_deinit(adev)                          (0)
 #define audio_extn_sound_trigger_update_device_status(snd_dev, event)  (0)
+#define audio_extn_sound_trigger_update_stream_status(uc_info, event)  (0)
 #define audio_extn_sound_trigger_set_parameters(adev, parms)           (0)
 #define audio_extn_sound_trigger_check_and_get_session(in)             (0)
 #define audio_extn_sound_trigger_stop_lab(in)                          (0)
@@ -126,6 +133,8 @@
 void audio_extn_sound_trigger_deinit(struct audio_device *adev);
 void audio_extn_sound_trigger_update_device_status(snd_device_t snd_device,
                                      st_event_type_t event);
+void audio_extn_sound_trigger_update_stream_status(struct audio_usecase *uc_info,
+                                     st_event_type_t event);
 void audio_extn_sound_trigger_set_parameters(struct audio_device *adev,
                                              struct str_parms *parms);
 void audio_extn_sound_trigger_check_and_get_session(struct stream_in *in);
@@ -134,6 +143,33 @@
                                   size_t bytes);
 #endif
 
+#ifndef A2DP_OFFLOAD_ENABLED
+#define audio_extn_a2dp_init(adev)                       (0)
+#define audio_extn_a2dp_start_playback()                 (0)
+#define audio_extn_a2dp_stop_playback()                  (0)
+#define audio_extn_a2dp_set_parameters(parms, reconfig)  (0)
+#define audio_extn_a2dp_get_parameters(query, reply)     (0)
+#define audio_extn_a2dp_is_force_device_switch()         (0)
+#define audio_extn_a2dp_set_handoff_mode(is_on)          (0)
+#define audio_extn_a2dp_get_sample_rate(sample_rate)     (0)
+#define audio_extn_a2dp_get_encoder_latency()            (0)
+#define audio_extn_a2dp_is_ready()                       (0)
+#define audio_extn_a2dp_is_suspended()                   (0)
+#else
+void audio_extn_a2dp_init(void *adev);
+int audio_extn_a2dp_start_playback();
+int audio_extn_a2dp_stop_playback();
+int audio_extn_a2dp_set_parameters(struct str_parms *parms, bool *reconfig);
+int audio_extn_a2dp_get_parameters(struct str_parms *query,
+                                   struct str_parms *reply);
+bool audio_extn_a2dp_is_force_device_switch();
+void audio_extn_a2dp_set_handoff_mode(bool is_on);
+void audio_extn_a2dp_get_sample_rate(int *sample_rate);
+uint32_t audio_extn_a2dp_get_encoder_latency();
+bool audio_extn_a2dp_is_ready();
+bool audio_extn_a2dp_is_suspended();
+#endif
+
 #ifndef DSM_FEEDBACK_ENABLED
 #define audio_extn_dsm_feedback_enable(adev, snd_device, benable)                (0)
 #else
diff --git a/hal/audio_extn/cirrus_playback.c b/hal/audio_extn/cirrus_playback.c
new file mode 100644
index 0000000..21dbc10
--- /dev/null
+++ b/hal/audio_extn/cirrus_playback.c
@@ -0,0 +1,837 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "audio_hw_cirrus_playback"
+/*#define LOG_NDEBUG 0*/
+
+#include <errno.h>
+#include <math.h>
+#include <log/log.h>
+#include <fcntl.h>
+#include "../audio_hw.h"
+#include "platform.h"
+#include "platform_api.h"
+#include <sys/stat.h>
+#include <linux/types.h>
+#include <linux/ioctl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <dlfcn.h>
+#include <math.h>
+#include <pthread.h>
+#include <time.h>
+#include <unistd.h>
+#include <cutils/properties.h>
+#include "audio_extn.h"
+
+struct cirrus_playback_session {
+    void *adev_handle;
+    pthread_mutex_t fb_prot_mutex;
+    pthread_t calibration_thread;
+#ifdef ENABLE_CIRRUS_DETECTION
+    pthread_t failure_detect_thread;
+#endif
+    struct pcm *pcm_rx;
+    struct pcm *pcm_tx;
+    volatile int32_t state;
+};
+
+enum cirrus_playback_state {
+    INIT = 0,
+    CALIBRATING = 1,
+    IDLE = 2,
+    PLAYBACK = 3
+};
+
+struct crus_sp_ioctl_header {
+    uint32_t size;
+    uint32_t module_id;
+    uint32_t param_id;
+    uint32_t data_length;
+    void *data;
+};
+
+/* Payload struct for getting calibration result from DSP module */
+struct cirrus_cal_result_t {
+    int32_t status_l;
+    int32_t checksum_l;
+    int32_t z_l;
+    int32_t status_r;
+    int32_t checksum_r;
+    int32_t z_r;
+};
+
+/* Payload struct for setting the RX and TX use cases */
+struct crus_rx_run_case_ctrl_t {
+    int32_t value;
+    int32_t status_l;
+    int32_t checksum_l;
+    int32_t z_l;
+    int32_t status_r;
+    int32_t checksum_r;
+    int32_t z_r;
+};
+
+#define CRUS_SP_FILE "/dev/msm_cirrus_playback"
+#define CRUS_CAL_FILE "/persist/audio/audio.cal"
+#define CRUS_TX_CONF_FILE "vendor/firmware/crus_sp_config_%s_tx.bin"
+#define CRUS_RX_CONF_FILE "vendor/firmware/crus_sp_config_%s_rx.bin"
+#define CONFIG_FILE_SIZE 128
+
+#define CRUS_SP_USECASE_MIXER   "Cirrus SP Usecase"
+#define CRUS_SP_LOAD_CONF_MIXER "Cirrus SP Load Config"
+#define CRUS_SP_FAIL_DET_MIXER  "Cirrus SP Failure Detection"
+
+#define CIRRUS_SP 0x10027053
+
+#define CRUS_MODULE_ID_TX 0x00000002
+#define CRUS_MODULE_ID_RX 0x00000001
+
+#define CRUS_PARAM_RX_SET_USECASE 0x00A1AF02
+#define CRUS_PARAM_TX_SET_USECASE 0x00A1BF0A
+
+#define CRUS_PARAM_RX_SET_CALIB 0x00A1AF03
+#define CRUS_PARAM_TX_SET_CALIB 0x00A1BF03
+
+#define CRUS_PARAM_RX_SET_EXT_CONFIG 0x00A1AF05
+#define CRUS_PARAM_TX_SET_EXT_CONFIG 0x00A1BF08
+
+#define CRUS_PARAM_RX_GET_TEMP 0x00A1AF07
+#define CRUS_PARAM_TX_GET_TEMP_CAL 0x00A1BF06
+// variables based on CSPL tuning file, max parameter length is 96 integers (384 bytes)
+#define CRUS_PARAM_TEMP_MAX_LENGTH 384
+
+#define CRUS_AFE_PARAM_ID_ENABLE 0x00010203
+
+#define FAIL_DETECT_INIT_WAIT_US 500000
+#define FAIL_DETECT_LOOP_WAIT_US 300000
+
+#define CRUS_DEFAULT_CAL_L 0x2A11
+#define CRUS_DEFAULT_CAL_R 0x29CB
+
+#define CRUS_SP_IOCTL_MAGIC 'a'
+
+#define CRUS_SP_IOCTL_GET _IOWR(CRUS_SP_IOCTL_MAGIC, 219, void *)
+#define CRUS_SP_IOCTL_SET _IOWR(CRUS_SP_IOCTL_MAGIC, 220, void *)
+#define CRUS_SP_IOCTL_GET_CALIB _IOWR(CRUS_SP_IOCTL_MAGIC, 221, void *)
+#define CRUS_SP_IOCTL_SET_CALIB _IOWR(CRUS_SP_IOCTL_MAGIC, 222, void *)
+
+
+
+static struct pcm_config pcm_config_cirrus_tx = {
+    .channels = 2,
+    .rate = 48000,
+    .period_size = 320,
+    .period_count = 4,
+    .format = PCM_FORMAT_S16_LE,
+    .start_threshold = 0,
+    .stop_threshold = INT_MAX,
+    .avail_min = 0,
+};
+
+static struct pcm_config pcm_config_cirrus_rx = {
+    .channels = 8,
+    .rate = 48000,
+    .period_size = 320,
+    .period_count = 4,
+    .format = PCM_FORMAT_S32_LE,
+    .start_threshold = 0,
+    .stop_threshold = INT_MAX,
+    .avail_min = 0,
+};
+
+static struct cirrus_playback_session handle;
+
+static void *audio_extn_cirrus_calibration_thread();
+
+#ifdef ENABLE_CIRRUS_DETECTION
+static void *audio_extn_cirrus_failure_detect_thread();
+#endif
+
+void audio_extn_spkr_prot_init(void *adev) {
+    ALOGI("%s: Initialize Cirrus Logic Playback module", __func__);
+
+    memset(&handle, 0, sizeof(handle));
+    if (!adev) {
+        ALOGE("%s: Invalid params", __func__);
+        return;
+    }
+
+    handle.adev_handle = adev;
+    handle.state = INIT;
+
+    pthread_mutex_init(&handle.fb_prot_mutex, NULL);
+
+    (void)pthread_create(&handle.calibration_thread,
+                (const pthread_attr_t *) NULL,
+                audio_extn_cirrus_calibration_thread, &handle);
+}
+
+static int audio_extn_cirrus_run_calibration() {
+    struct audio_device *adev = handle.adev_handle;
+    struct crus_sp_ioctl_header header;
+    struct cirrus_cal_result_t result;
+    struct mixer_ctl *ctl = NULL;
+    FILE *cal_file = NULL;
+    int ret = 0, dev_file = -1;
+    char *buffer = NULL;
+    uint32_t option = 1;
+
+    ALOGI("%s: Running speaker calibration", __func__);
+
+    dev_file = open(CRUS_SP_FILE, O_RDWR | O_NONBLOCK);
+    if (dev_file < 0) {
+        ALOGE("%s: Failed to open Cirrus Playback IOCTL (%d)",
+              __func__, dev_file);
+        ret = -EINVAL;
+        goto exit;
+    }
+
+    buffer = calloc(1, CRUS_PARAM_TEMP_MAX_LENGTH);
+    if (!buffer) {
+        ALOGE("%s: allocate memory failed", __func__);
+        ret = -ENOMEM;
+        goto exit;
+    }
+
+    cal_file = fopen(CRUS_CAL_FILE, "r");
+    if (cal_file) {
+        ret = fread(&result, sizeof(result), 1, cal_file);
+        if (ret != 1) {
+            ALOGE("%s: Cirrus SP calibration file cannot be read , read size: %lu file error: %d",
+                  __func__, (unsigned long)ret * sizeof(result), ferror(cal_file));
+            ret = -EINVAL;
+            fclose(cal_file);
+            goto exit;
+        }
+
+        fclose(cal_file);
+    } else {
+
+        ALOGV("%s: Calibrating...", __func__);
+
+        header.size = sizeof(header);
+        header.module_id = CRUS_MODULE_ID_RX;
+        header.param_id = CRUS_PARAM_RX_SET_CALIB;
+        header.data_length = sizeof(option);
+        header.data = &option;
+
+        ret = ioctl(dev_file, CRUS_SP_IOCTL_SET, &header);
+        if (ret < 0) {
+            ALOGE("%s: Cirrus SP calibration IOCTL failure (%d)",
+                  __func__, ret);
+            ret = -EINVAL;
+            goto exit;
+        }
+
+        header.size = sizeof(header);
+        header.module_id = CRUS_MODULE_ID_TX;
+        header.param_id = CRUS_PARAM_TX_SET_CALIB;
+        header.data_length = sizeof(option);
+        header.data = &option;
+
+        ret = ioctl(dev_file, CRUS_SP_IOCTL_SET, &header);
+        if (ret < 0) {
+            ALOGE("%s: Cirrus SP calibration IOCTL failure (%d)",
+                  __func__, ret);
+            ret = -EINVAL;
+            goto exit;
+        }
+
+        sleep(2);
+
+        header.size = sizeof(header);
+        header.module_id = CRUS_MODULE_ID_TX;
+        header.param_id = CRUS_PARAM_TX_GET_TEMP_CAL;
+        header.data_length = sizeof(result);
+        header.data = &result;
+
+        ret = ioctl(dev_file, CRUS_SP_IOCTL_GET, &header);
+        if (ret < 0) {
+            ALOGE("%s: Cirrus SP calibration IOCTL failure (%d)",
+                  __func__, ret);
+            ret = -EINVAL;
+            goto exit;
+        }
+
+        if (result.status_l != 1) {
+            ALOGE("%s: Left calibration failure. Please check speakers",
+                    __func__);
+            ret = -EINVAL;
+        }
+
+        if (result.status_r != 1) {
+            ALOGE("%s: Right calibration failure. Please check speakers",
+                    __func__);
+            ret = -EINVAL;
+        }
+
+        if (ret < 0)
+            goto exit;
+
+#ifdef ENABLED_CIRRUS_WRITE_CAL_FILE
+        cal_file = fopen(CRUS_CAL_FILE, "wb");
+        if (cal_file == NULL) {
+            ALOGE("%s: Cannot create Cirrus SP calibration file (%s)",
+                  __func__, strerror(errno));
+            ret = -EINVAL;
+            goto exit;
+        }
+
+        ret = fwrite(&result, sizeof(result), 1, cal_file);
+
+        if (ret != 1) {
+            ALOGE("%s: Unable to save Cirrus SP calibration data, write size %lu, file error %d",
+                  __func__, (unsigned long)ret * sizeof(result), ferror(cal_file));
+            fclose(cal_file);
+            ret = -EINVAL;
+            goto exit;
+        }
+
+        fclose(cal_file);
+
+        ALOGI("%s: Cirrus calibration file successfully written",
+              __func__);
+#endif
+    }
+
+    header.size = sizeof(header);
+    header.module_id = CRUS_MODULE_ID_TX;
+    header.param_id = 0;
+    header.data_length = sizeof(result);
+    header.data = &result;
+
+    ret = ioctl(dev_file, CRUS_SP_IOCTL_SET_CALIB, &header);
+
+    if (ret < 0) {
+        ALOGE("%s: Cirrus SP calibration IOCTL failure (%d)", __func__, ret);
+        ret = -EINVAL;
+        goto exit;
+    }
+
+    ctl = mixer_get_ctl_by_name(adev->mixer,
+                    CRUS_SP_USECASE_MIXER);
+    if (!ctl) {
+        ALOGE("%s: Could not get ctl for mixer cmd - %s",
+             __func__, CRUS_SP_USECASE_MIXER);
+        ret = -EINVAL;
+        goto exit;
+    }
+
+    ret = mixer_ctl_set_value(ctl, 0, 0); // Set RX external firmware config
+    if (ret < 0) {
+        ALOGE("%s: set default usecase failed", __func__);
+        goto exit;
+    }
+
+    sleep(1);
+
+    header.size = sizeof(header);
+    header.module_id = CRUS_MODULE_ID_RX;
+    header.param_id = CRUS_PARAM_RX_GET_TEMP;
+    header.data_length = CRUS_PARAM_TEMP_MAX_LENGTH;
+    header.data = buffer;
+
+    ret = ioctl(dev_file, CRUS_SP_IOCTL_GET, &header);
+    if (ret < 0) {
+        ALOGE("%s: Cirrus SP temperature IOCTL failure (%d)", __func__, ret);
+        ret = -EINVAL;
+        goto exit;
+    }
+
+    ALOGI("%s: Cirrus SP successfully calibrated", __func__);
+
+exit:
+    if (dev_file >= 0)
+        close(dev_file);
+    free(buffer);
+    ALOGV("%s: Exit", __func__);
+
+    return ret;
+}
+
+static int audio_extn_cirrus_load_usecase_configs(void) {
+    struct audio_device *adev = handle.adev_handle;
+    struct mixer_ctl *ctl_uc = NULL, *ctl_config = NULL;
+    char *filename = NULL;
+    int ret = 0, default_uc = 0;
+    struct snd_card_split *snd_split_handle = NULL;
+    snd_split_handle = audio_extn_get_snd_card_split();
+
+    ALOGI("%s: Loading usecase tuning configs", __func__);
+
+    ctl_uc = mixer_get_ctl_by_name(adev->mixer, CRUS_SP_USECASE_MIXER);
+    ctl_config = mixer_get_ctl_by_name(adev->mixer,
+                    CRUS_SP_LOAD_CONF_MIXER);
+    if (!ctl_uc || !ctl_config) {
+        ALOGE("%s: Could not get ctl for mixer commands", __func__);
+        ret = -EINVAL;
+        goto exit;
+    }
+
+    filename = calloc(1 , CONFIG_FILE_SIZE);
+    if (!filename) {
+        ALOGE("%s: allocate memory failed", __func__);
+        ret = -ENOMEM;
+        goto exit;
+    }
+
+    default_uc = mixer_ctl_get_value(ctl_uc, 0);
+
+    ret = mixer_ctl_set_value(ctl_uc, 0, default_uc);
+    if (ret < 0) {
+        ALOGE("%s set uscase %d failed", __func__, default_uc);
+        goto exit;
+    }
+
+    /* Load TX Tuning Config (if available) */
+    snprintf(filename, CONFIG_FILE_SIZE, CRUS_TX_CONF_FILE, snd_split_handle->form_factor);
+    if (access(filename, R_OK) == 0) {
+        ret = mixer_ctl_set_value(ctl_config, 0, 2);
+        if (ret < 0) {
+            ALOGE("%s set tx config failed", __func__);
+            goto exit;
+        }
+    } else {
+        ALOGE("%s: Tuning file not found (%s)", __func__,
+              filename);
+        ret = -EINVAL;
+        goto exit;
+    }
+    /* Load RX Tuning Config (if available) */
+    snprintf(filename, CONFIG_FILE_SIZE, CRUS_RX_CONF_FILE, snd_split_handle->form_factor);
+    if (access(filename, R_OK) == 0) {
+        ret = mixer_ctl_set_value(ctl_config, 0, 1);
+        if (ret < 0) {
+            ALOGE("%s set rx config failed", __func__);
+            goto exit;
+        }
+    } else {
+        ALOGE("%s: Tuning file not found (%s)", __func__,
+              filename);
+        ret = -EINVAL;
+        goto exit;
+    }
+
+    ALOGI("%s: Cirrus SP loaded available usecase configs", __func__);
+exit:
+    free(filename);
+    ALOGI("%s: Exit", __func__);
+
+    return ret;
+}
+
+static void *audio_extn_cirrus_calibration_thread() {
+    struct audio_device *adev = handle.adev_handle;
+    struct audio_usecase *uc_info_rx = NULL;
+    int ret = 0;
+    int32_t pcm_dev_rx_id, prev_state;
+    uint32_t retries = 5;
+
+    ALOGI("%s: PCM Stream thread", __func__);
+
+    while (!adev->platform && retries) {
+        sleep(1);
+        ALOGI("%s: Waiting...", __func__);
+        retries--;
+    }
+
+    prev_state = handle.state;
+    handle.state = CALIBRATING;
+
+    uc_info_rx = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
+    if (!uc_info_rx) {
+        ALOGE("%s: rx usecase can not be found", __func__);
+        goto exit;
+    }
+    pthread_mutex_lock(&adev->lock);
+
+    uc_info_rx->id = USECASE_AUDIO_PLAYBACK_DEEP_BUFFER;
+    uc_info_rx->type = PCM_PLAYBACK;
+    uc_info_rx->in_snd_device = SND_DEVICE_NONE;
+    uc_info_rx->stream.out = adev->primary_output;
+    uc_info_rx->out_snd_device = SND_DEVICE_OUT_SPEAKER;
+    list_add_tail(&adev->usecase_list, &uc_info_rx->list);
+
+    enable_snd_device(adev, SND_DEVICE_OUT_SPEAKER);
+    enable_audio_route(adev, uc_info_rx);
+    pcm_dev_rx_id = platform_get_pcm_device_id(uc_info_rx->id, PCM_PLAYBACK);
+
+    if (pcm_dev_rx_id < 0) {
+        ALOGE("%s: Invalid pcm device for usecase (%d)",
+              __func__, uc_info_rx->id);
+        pthread_mutex_unlock(&adev->lock);
+        goto exit;
+    }
+
+    handle.pcm_rx = pcm_open(adev->snd_card, pcm_dev_rx_id,
+                PCM_OUT, &pcm_config_cirrus_rx);
+
+    if (handle.pcm_rx && !pcm_is_ready(handle.pcm_rx)) {
+        ALOGE("%s: PCM device not ready: %s", __func__,
+              pcm_get_error(handle.pcm_rx));
+        pthread_mutex_unlock(&adev->lock);
+        goto close_stream;
+    }
+
+    if (pcm_start(handle.pcm_rx) < 0) {
+        ALOGE("%s: pcm start for RX failed; error = %s", __func__,
+              pcm_get_error(handle.pcm_rx));
+        pthread_mutex_unlock(&adev->lock);
+        goto close_stream;
+    }
+    pthread_mutex_unlock(&adev->lock);
+    ALOGI("%s: PCM thread streaming", __func__);
+
+    ret = audio_extn_cirrus_run_calibration();
+    ALOGE_IF(ret < 0, "%s: Calibration procedure failed (%d)", __func__, ret);
+
+    ret = audio_extn_cirrus_load_usecase_configs();
+    ALOGE_IF(ret < 0, "%s: Set tuning configs failed (%d)", __func__, ret);
+
+close_stream:
+    pthread_mutex_lock(&adev->lock);
+    if (handle.pcm_rx) {
+        ALOGI("%s: pcm_rx_close", __func__);
+        pcm_close(handle.pcm_rx);
+        handle.pcm_rx = NULL;
+    }
+    disable_audio_route(adev, uc_info_rx);
+    disable_snd_device(adev, SND_DEVICE_OUT_SPEAKER);
+    list_remove(&uc_info_rx->list);
+    free(uc_info_rx);
+    pthread_mutex_unlock(&adev->lock);
+exit:
+    handle.state = (prev_state == PLAYBACK) ? PLAYBACK : IDLE;
+
+#ifdef ENABLE_CIRRUS_DETECTION
+    if (handle.state == PLAYBACK)
+        (void)pthread_create(&handle.failure_detect_thread,
+                    (const pthread_attr_t *) NULL,
+                    audio_extn_cirrus_failure_detect_thread,
+                    &handle);
+#endif
+
+    ALOGV("%s: Exit", __func__);
+
+    pthread_exit(0);
+    return NULL;
+}
+
+#ifdef ENABLE_CIRRUS_DETECTION
+void *audio_extn_cirrus_failure_detect_thread() {
+    struct audio_device *adev = handle.adev_handle;
+    struct crus_sp_ioctl_header header;
+    struct mixer_ctl *ctl = NULL;
+    const int32_t r_scale_factor = 100000000;
+    const int32_t t_scale_factor = 100000;
+    const int32_t r_err_range = 70000000;
+    const int32_t t_err_range = 210000;
+    const int32_t amp_factor = 71498;
+    const int32_t material = 250;
+    int32_t *buffer = NULL;
+    int ret = 0, dev_file = -1, out_cal0 = 0, out_cal1 = 0;
+    int rL = 0, rR = 0, zL = 0, zR = 0, tL = 0, tR = 0;
+    int rdL = 0, rdR = 0, tdL = 0, tdR = 0, ambL = 0, ambR = 0;
+    bool left_cal_done = false, right_cal_done = false;
+    bool det_en = false;
+
+    ALOGI("%s: Entry", __func__);
+
+    ctl = mixer_get_ctl_by_name(adev->mixer, CRUS_SP_FAIL_DET_MIXER);
+    det_en = mixer_ctl_get_value(ctl, 0);
+
+    if (!det_en)
+        goto exit;
+
+    dev_file = open(CRUS_SP_FILE, O_RDWR | O_NONBLOCK);
+    if (dev_file < 0) {
+        ALOGE("%s: Failed to open Cirrus Playback IOCTL (%d)",
+                __func__, dev_file);
+        goto exit;
+    }
+
+    buffer = calloc(1, CRUS_PARAM_TEMP_MAX_LENGTH);
+    if (!buffer) {
+        ALOGE("%s: allocate memory failed", __func__);
+        goto exit;
+    }
+
+    header.size = sizeof(header);
+    header.module_id = CRUS_MODULE_ID_RX;
+    header.param_id = CRUS_PARAM_RX_GET_TEMP;
+    header.data_length = CRUS_PARAM_TEMP_MAX_LENGTH;
+    header.data = buffer;
+
+    usleep(FAIL_DETECT_INIT_WAIT_US);
+
+    pthread_mutex_lock(&handle.fb_prot_mutex);
+    ret = ioctl(dev_file, CRUS_SP_IOCTL_GET, &header);
+    pthread_mutex_unlock(&handle.fb_prot_mutex);
+    if (ret < 0) {
+        ALOGE("%s: Cirrus SP IOCTL failure (%d)",
+               __func__, ret);
+        goto exit;
+    }
+
+    zL = buffer[2] * amp_factor;
+    zR = buffer[4] * amp_factor;
+
+    ambL = buffer[10];
+    ambR = buffer[6];
+
+    out_cal0 = buffer[12];
+    out_cal1 = buffer[13];
+
+    left_cal_done = (out_cal0 == 2) && (out_cal1 == 2) &&
+                    (buffer[2] != CRUS_DEFAULT_CAL_L);
+
+    out_cal0 = buffer[14];
+    out_cal1 = buffer[15];
+
+    right_cal_done = (out_cal0 == 2) && (out_cal1 == 2) &&
+                     (buffer[4] != CRUS_DEFAULT_CAL_R);
+
+    if (left_cal_done) {
+        ALOGI("%s: L Speaker Impedance: %d.%08d ohms", __func__,
+              zL / r_scale_factor, abs(zL) % r_scale_factor);
+        ALOGI("%s: L Calibration Temperature: %d C", __func__, ambL);
+    } else
+        ALOGE("%s: Left speaker uncalibrated", __func__);
+
+    if (right_cal_done) {
+        ALOGI("%s: R Speaker Impedance: %d.%08d ohms", __func__,
+               zR / r_scale_factor, abs(zR) % r_scale_factor);
+        ALOGI("%s: R Calibration Temperature: %d C", __func__, ambR);
+    } else
+        ALOGE("%s: Right speaker uncalibrated", __func__);
+
+    if (!left_cal_done && !right_cal_done)
+        goto exit;
+
+    ALOGI("%s: Monitoring speaker impedance & temperature...", __func__);
+
+    while ((handle.state == PLAYBACK) && det_en) {
+        pthread_mutex_lock(&handle.fb_prot_mutex);
+        ret = ioctl(dev_file, CRUS_SP_IOCTL_GET, &header);
+        pthread_mutex_unlock(&handle.fb_prot_mutex);
+        if (ret < 0) {
+            ALOGE("%s: Cirrus SP IOCTL failure (%d)",
+                  __func__, ret);
+            goto loop;
+        }
+
+        rL = buffer[3];
+        rR = buffer[1];
+
+        zL = buffer[2];
+        zR = buffer[4];
+
+        if ((zL == 0) || (zR == 0))
+            goto loop;
+
+        tdL = (material * t_scale_factor * (rL-zL) / zL);
+        tdR = (material * t_scale_factor * (rR-zR) / zR);
+
+        rL *= amp_factor;
+        rR *= amp_factor;
+
+        zL *= amp_factor;
+        zR *= amp_factor;
+
+        tL = tdL + (ambL * t_scale_factor);
+        tR = tdR + (ambR * t_scale_factor);
+
+        rdL = abs(zL - rL);
+        rdR = abs(zR - rR);
+
+        if (left_cal_done && (rL != 0) && (rdL > r_err_range))
+            ALOGI("%s: Left speaker impedance out of range (%d.%08d ohms)",
+                  __func__, rL / r_scale_factor,
+                  abs(rL % r_scale_factor));
+
+        if (right_cal_done && (rR != 0) && (rdR > r_err_range))
+            ALOGI("%s: Right speaker impedance out of range (%d.%08d ohms)",
+                  __func__, rR / r_scale_factor,
+                  abs(rR % r_scale_factor));
+
+        if (left_cal_done && (rL != 0) && (tdL > t_err_range))
+            ALOGI("%s: Left speaker temperature out of range (%d.%05d C)",
+                  __func__, tL / t_scale_factor,
+                  abs(tL % t_scale_factor));
+
+        if (right_cal_done && (rR != 0) && (tdR > t_err_range))
+            ALOGI("%s: Right speaker temperature out of range (%d.%05d C)",
+                  __func__, tR / t_scale_factor,
+                  abs(tR % t_scale_factor));
+
+loop:
+        det_en = mixer_ctl_get_value(ctl, 0);
+        usleep(FAIL_DETECT_LOOP_WAIT_US);
+    }
+
+exit:
+    if (dev_file >= 0)
+        close(dev_file);
+    free(buffer);
+    ALOGI("%s: Exit ", __func__);
+
+    pthread_exit(0);
+    return NULL;
+}
+#endif
+
+int audio_extn_spkr_prot_start_processing(snd_device_t snd_device) {
+    struct audio_usecase *uc_info_tx;
+    struct audio_device *adev = handle.adev_handle;
+    int32_t pcm_dev_tx_id = -1, ret = 0;
+
+    ALOGV("%s: Entry", __func__);
+
+    if (!adev) {
+        ALOGE("%s: Invalid params", __func__);
+        return -EINVAL;
+    }
+
+    uc_info_tx = (struct audio_usecase *)calloc(1, sizeof(*uc_info_tx));
+    if (!uc_info_tx) {
+        ALOGE("%s: allocate memory failed", __func__);
+        return -ENOMEM;
+    }
+
+    audio_route_apply_and_update_path(adev->audio_route,
+                                      platform_get_snd_device_name(snd_device));
+
+    pthread_mutex_lock(&handle.fb_prot_mutex);
+    uc_info_tx->id = USECASE_AUDIO_SPKR_CALIB_TX;
+    uc_info_tx->type = PCM_CAPTURE;
+    uc_info_tx->in_snd_device = SND_DEVICE_IN_CAPTURE_VI_FEEDBACK;
+    uc_info_tx->out_snd_device = SND_DEVICE_NONE;
+    handle.pcm_tx = NULL;
+
+    list_add_tail(&adev->usecase_list, &uc_info_tx->list);
+
+    enable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
+    enable_audio_route(adev, uc_info_tx);
+
+    pcm_dev_tx_id = platform_get_pcm_device_id(uc_info_tx->id, PCM_CAPTURE);
+
+    if (pcm_dev_tx_id < 0) {
+        ALOGE("%s: Invalid pcm device for usecase (%d)",
+              __func__, uc_info_tx->id);
+        ret = -ENODEV;
+        goto exit;
+    }
+
+    handle.pcm_tx = pcm_open(adev->snd_card,
+                             pcm_dev_tx_id,
+                             PCM_IN, &pcm_config_cirrus_tx);
+
+    if (handle.pcm_tx && !pcm_is_ready(handle.pcm_tx)) {
+        ALOGE("%s: PCM device not ready: %s", __func__, pcm_get_error(handle.pcm_tx));
+        ret = -EIO;
+        goto exit;
+    }
+
+    if (pcm_start(handle.pcm_tx) < 0) {
+        ALOGE("%s: pcm start for TX failed; error = %s", __func__,
+              pcm_get_error(handle.pcm_tx));
+        ret = -EINVAL;
+        goto exit;
+    }
+
+#ifdef ENABLE_CIRRUS_DETECTION
+    if (handle.state == IDLE)
+        (void)pthread_create(&handle.failure_detect_thread,
+                    (const pthread_attr_t *) NULL,
+                    audio_extn_cirrus_failure_detect_thread,
+                    &handle);
+#endif
+
+    handle.state = PLAYBACK;
+exit:
+    if (ret) {
+        handle.state = IDLE;
+        if (handle.pcm_tx) {
+            ALOGI("%s: pcm_tx_close", __func__);
+            pcm_close(handle.pcm_tx);
+            handle.pcm_tx = NULL;
+        }
+
+        disable_audio_route(adev, uc_info_tx);
+        disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
+        list_remove(&uc_info_tx->list);
+        free(uc_info_tx);
+    }
+
+    pthread_mutex_unlock(&handle.fb_prot_mutex);
+    ALOGV("%s: Exit", __func__);
+    return ret;
+}
+
+void audio_extn_spkr_prot_stop_processing(snd_device_t snd_device) {
+    struct audio_usecase *uc_info_tx;
+    struct audio_device *adev = handle.adev_handle;
+
+    ALOGV("%s: Entry", __func__);
+
+    pthread_mutex_lock(&handle.fb_prot_mutex);
+
+    handle.state = IDLE;
+    uc_info_tx = get_usecase_from_list(adev, USECASE_AUDIO_SPKR_CALIB_TX);
+
+    if (uc_info_tx) {
+        if (handle.pcm_tx) {
+            ALOGI("%s: pcm_tx_close", __func__);
+            pcm_close(handle.pcm_tx);
+            handle.pcm_tx = NULL;
+        }
+
+        disable_audio_route(adev, uc_info_tx);
+        disable_snd_device(adev, SND_DEVICE_IN_CAPTURE_VI_FEEDBACK);
+        list_remove(&uc_info_tx->list);
+        free(uc_info_tx);
+
+        audio_route_reset_path(adev->audio_route,
+                               platform_get_snd_device_name(snd_device));
+    }
+
+    pthread_mutex_unlock(&handle.fb_prot_mutex);
+
+    ALOGV("%s: Exit", __func__);
+}
+
+bool audio_extn_spkr_prot_is_enabled() {
+    return true;
+}
+
+int audio_extn_get_spkr_prot_snd_device(snd_device_t snd_device) {
+    switch(snd_device) {
+    case SND_DEVICE_OUT_SPEAKER:
+    case SND_DEVICE_OUT_SPEAKER_REVERSE:
+        return SND_DEVICE_OUT_SPEAKER_PROTECTED;
+    case SND_DEVICE_OUT_SPEAKER_SAFE:
+        return SND_DEVICE_OUT_SPEAKER_SAFE;
+    case SND_DEVICE_OUT_VOICE_SPEAKER:
+        return SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED;
+    default:
+        return snd_device;
+    }
+}
+
+void audio_extn_spkr_prot_calib_cancel(__unused void *adev) {
+    // FIXME: wait or cancel audio_extn_cirrus_run_calibration
+}
diff --git a/hal/audio_extn/dsm_feedback.c b/hal/audio_extn/dsm_feedback.c
index e340dae..f212df0 100755
--- a/hal/audio_extn/dsm_feedback.c
+++ b/hal/audio_extn/dsm_feedback.c
@@ -20,7 +20,7 @@
 
 #include <errno.h>
 #include <math.h>
-#include <cutils/log.h>
+#include <log/log.h>
 
 #include "audio_hw.h"
 #include "platform.h"
diff --git a/hal/audio_extn/ext_speaker.c b/hal/audio_extn/ext_speaker.c
index 1bf8e5b..038e81f 100644
--- a/hal/audio_extn/ext_speaker.c
+++ b/hal/audio_extn/ext_speaker.c
@@ -16,7 +16,7 @@
 
 #define LOG_TAG "ext_speaker"
 /*#define LOG_NDEBUG 0*/
-#include <cutils/log.h>
+#include <log/log.h>
 
 #include <stdlib.h>
 #include <audio_hw.h>
diff --git a/hal/audio_extn/hfp.c b/hal/audio_extn/hfp.c
index 07a9711..a4db98f 100644
--- a/hal/audio_extn/hfp.c
+++ b/hal/audio_extn/hfp.c
@@ -20,7 +20,7 @@
 
 #include <errno.h>
 #include <math.h>
-#include <cutils/log.h>
+#include <log/log.h>
 
 #include "audio_hw.h"
 #include "platform.h"
@@ -438,10 +438,17 @@
     ret = str_parms_get_str(parms, AUDIO_PARAMETER_HFP_ENABLE, value,
                             sizeof(value));
     if (ret >= 0) {
-           if (!strncmp(value,"true",sizeof(value)))
-               ret = start_hfp(adev,parms);
-           else
-               stop_hfp(adev);
+           if (!strncmp(value,"true",sizeof(value))) {
+               if (!hfpmod.is_hfp_running)
+                   start_hfp(adev,parms);
+               else
+                   ALOGW("%s: HFP is already active.", __func__);
+           } else {
+               if (hfpmod.is_hfp_running)
+                   stop_hfp(adev);
+               else
+                   ALOGW("%s: ignore STOP, HFC not active", __func__);
+           }
     }
     memset(value, 0, sizeof(value));
     ret = str_parms_get_str(parms,AUDIO_PARAMETER_HFP_SET_SAMPLING_RATE, value,
diff --git a/hal/audio_extn/hwdep_cal.c b/hal/audio_extn/hwdep_cal.c
index 07269d1..3b96843 100644
--- a/hal/audio_extn/hwdep_cal.c
+++ b/hal/audio_extn/hwdep_cal.c
@@ -25,7 +25,7 @@
 #include <fcntl.h>
 #include <sys/ioctl.h>
 #include <unistd.h>
-#include <cutils/log.h>
+#include <log/log.h>
 #include <audio_hw.h>
 #include "audio_extn.h"
 #include "sound/msmcal-hwdep.h"
diff --git a/hal/audio_extn/maxxaudio.c b/hal/audio_extn/maxxaudio.c
new file mode 100644
index 0000000..9007da7
--- /dev/null
+++ b/hal/audio_extn/maxxaudio.c
@@ -0,0 +1,635 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "audio_hw_waves"
+/*#define LOG_NDEBUG 0*/
+
+#include <audio_hw.h>
+#include <cutils/str_parms.h>
+#include <dlfcn.h>
+#include <log/log.h>
+#include <math.h>
+#include <platform_api.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <system/audio.h>
+#include <unistd.h>
+
+#include "audio_extn.h"
+#include "maxxaudio.h"
+
+#define LIB_MA_PARAM "libmaxxaudioqdsp.so"
+#define LIB_MA_PATH "vendor/lib/"
+#define PRESET_PATH "/vendor/etc"
+#define MPS_BASE_STRING "default"
+#define USER_PRESET_PATH ""
+#define CONFIG_PATH "/vendor/etc/maxx_conf.ini"
+#define CAL_PRESIST_STR "cal_persist"
+#define CAL_SAMPLERATE_STR "cal_samplerate"
+
+#define MA_QDSP_PARAM_INIT "maxxaudio_qdsp_initialize"
+#define MA_QDSP_PARAM_DEINIT "maxxaudio_qdsp_uninitialize"
+#define MA_QDSP_SET_LR_SWAP "maxxaudio_qdsp_set_lr_swap"
+#define MA_QDSP_SET_MODE "maxxaudio_qdsp_set_sound_mode"
+#define MA_QDSP_SET_VOL "maxxaudio_qdsp_set_volume"
+#define MA_QDSP_SET_VOLT "maxxaudio_qdsp_set_volume_table"
+
+#define SUPPORT_DEV "Blackbird"
+#define SUPPORTED_USB 0x01
+
+struct ma_audio_cal_settings {
+    int app_type;
+    audio_devices_t device;
+};
+
+struct ma_state {
+    float vol;
+    bool active;
+};
+
+typedef enum MA_STREAM_TYPE {
+    STREAM_MIN_STREAM_TYPES,
+    STREAM_VOICE = STREAM_MIN_STREAM_TYPES,
+    STREAM_SYSTEM,
+    STREAM_RING,
+    STREAM_MUSIC,
+    STREAM_ALARM,
+    STREAM_NOTIFICATION ,
+    STREAM_MAX_TYPES,
+} ma_stream_type_t;
+
+typedef enum MA_CMD {
+    MA_CMD_VOL,
+    MA_CMD_SWAP_ENABLE,
+    MA_CMD_SWAP_DISABLE,
+} ma_cmd_t;
+
+typedef void *ma_audio_cal_handle_t;
+typedef int (*set_audio_cal_t)(const char *);
+
+typedef bool (*ma_param_init_t)(ma_audio_cal_handle_t *, const char *,
+                                const char *, const char *, set_audio_cal_t);
+
+typedef bool (*ma_param_deinit_t)(ma_audio_cal_handle_t *);
+
+typedef bool (*ma_set_lr_swap_t)(ma_audio_cal_handle_t,
+                                 const struct ma_audio_cal_settings *, bool);
+
+typedef bool (*ma_set_sound_mode_t)(ma_audio_cal_handle_t,
+                                    const struct ma_audio_cal_settings *,
+                                    unsigned int);
+
+typedef bool (*ma_set_volume_t)(ma_audio_cal_handle_t,
+                                const struct ma_audio_cal_settings *, double);
+
+typedef bool (*ma_set_volume_table_t)(ma_audio_cal_handle_t,
+                                      const struct ma_audio_cal_settings *,
+                                      size_t, struct ma_state *);
+
+struct ma_platform_data {
+    void *waves_handle;
+    void *platform;
+    pthread_mutex_t lock;
+    ma_param_init_t          ma_param_init;
+    ma_param_deinit_t        ma_param_deinit;
+    ma_set_lr_swap_t         ma_set_lr_swap;
+    ma_set_sound_mode_t      ma_set_sound_mode;
+    ma_set_volume_t          ma_set_volume;
+    ma_set_volume_table_t    ma_set_volume_table;
+};
+
+ma_audio_cal_handle_t g_ma_audio_cal_handle = NULL;
+static uint16_t g_supported_dev = 0;
+static struct ma_state ma_cur_state_table[STREAM_MAX_TYPES];
+static struct ma_platform_data *my_data = NULL;
+
+static int set_audio_cal(const char *audio_cal)
+{
+    ALOGV("set_audio_cal: %s", audio_cal);
+
+    return platform_set_parameters(my_data->platform,
+                                   str_parms_create_str(audio_cal));
+}
+
+static bool ma_set_lr_swap_l(
+    const struct ma_audio_cal_settings *audio_cal_settings, bool swap)
+{
+    return my_data->ma_set_lr_swap(g_ma_audio_cal_handle,
+                                   audio_cal_settings, swap);
+}
+
+static bool ma_set_sound_mode_l(
+    const struct ma_audio_cal_settings *audio_cal_settings, int sound_mode)
+{
+    return my_data->ma_set_sound_mode(g_ma_audio_cal_handle,
+                                      audio_cal_settings, sound_mode);
+}
+
+static bool ma_set_volume_l(
+    const struct ma_audio_cal_settings *audio_cal_settings, double volume)
+{
+    return my_data->ma_set_volume(g_ma_audio_cal_handle, audio_cal_settings,
+                                  volume);
+}
+
+static bool ma_set_volume_table_l(
+    const struct ma_audio_cal_settings *audio_cal_settings,
+    size_t num_streams, struct ma_state *volume_table)
+{
+    return my_data->ma_set_volume_table(g_ma_audio_cal_handle,
+                                        audio_cal_settings, num_streams,
+                                        volume_table);
+}
+
+static inline bool valid_usecase(struct audio_usecase *usecase)
+{
+    if ((usecase->type == PCM_PLAYBACK) &&
+        /* supported usecases */
+        ((usecase->id == USECASE_AUDIO_PLAYBACK_DEEP_BUFFER) ||
+         (usecase->id == USECASE_AUDIO_PLAYBACK_LOW_LATENCY) ||
+         (usecase->id == USECASE_AUDIO_PLAYBACK_OFFLOAD)) &&
+        /* support devices */
+        ((usecase->devices & AUDIO_DEVICE_OUT_SPEAKER) ||
+         (usecase->devices & AUDIO_DEVICE_OUT_SPEAKER_SAFE) ||
+         /* TODO: enable A2DP when it is ready */
+         (usecase->devices & AUDIO_DEVICE_OUT_ALL_USB)))
+
+        return true;
+
+    ALOGV("%s: not support type %d usecase %d device %d",
+           __func__, usecase->type, usecase->id, usecase->devices);
+
+    return false;
+}
+
+// already hold lock
+static inline bool is_active()
+{
+    ma_stream_type_t i = 0;
+
+    for (i = 0; i < STREAM_MAX_TYPES; i++)
+        if (ma_cur_state_table[i].active &&
+                (ma_cur_state_table[i].vol != 0))
+            return true;
+
+    return false;
+}
+
+static bool check_and_send_all_audio_cal(struct audio_device *adev, ma_cmd_t cmd)
+{
+    int i = 0;
+    bool ret = false;
+    float vol = 0;
+    struct listnode *node;
+    struct audio_usecase *usecase;
+    struct ma_audio_cal_settings *ma_cal = NULL;
+
+    // alloct
+    ma_cal = (struct ma_audio_cal_settings *)malloc(sizeof(struct ma_audio_cal_settings));
+
+    if (ma_cal == NULL) {
+        ALOGE("%s: ma_cal alloct fail", __func__);
+        return ret;
+    }
+
+    list_for_each(node, &adev->usecase_list) {
+        usecase = node_to_item(node, struct audio_usecase, list);
+        if (valid_usecase(usecase)) {
+            ma_cal->app_type = usecase->stream.out->app_type_cfg.app_type;
+            ma_cal->device = usecase->stream.out->devices;
+            ALOGV("%s: send usecase(%d) app_type(%d) device(%d)",
+                      __func__, usecase->id, ma_cal->app_type, ma_cal->device);
+
+            switch (cmd) {
+                case MA_CMD_VOL:
+                    ret = ma_set_volume_table_l(ma_cal, STREAM_MAX_TYPES,
+                                                ma_cur_state_table);
+                    if (ret)
+                        ALOGV("Waves: ma_set_volume_table_l success");
+                    else
+                        ALOGE("Waves: ma_set_volume_table_l %f returned with error.", vol);
+
+                    ALOGV("%s: send volume table === Start", __func__);
+                    for (i = 0; i < STREAM_MAX_TYPES; i++)
+                        ALOGV("%s: stream(%d) volume(%f) active(%s)", __func__,
+                              i, ma_cur_state_table[i].vol,
+                              ma_cur_state_table[i].active ? "T" : "F");
+                    ALOGV("%s: send volume table === End", __func__);
+                    break;
+                case MA_CMD_SWAP_ENABLE:
+                    ret = ma_set_lr_swap_l(ma_cal, true);
+                    if (ret)
+                        ALOGV("Waves: ma_set_lr_swap_l enable returned with success.");
+                    else
+                        ALOGE("Waves: ma_set_lr_swap_l enable returned with error.");
+                    break;
+                case MA_CMD_SWAP_DISABLE:
+                    ret = ma_set_lr_swap_l(ma_cal, false);
+                    if (ret)
+                        ALOGV("Waves: ma_set_lr_swap_l disable returned with success.");
+                    else
+                        ALOGE("Waves: ma_set_lr_swap_l disable returned with error.");
+                    break;
+                default:
+                    ALOGE("%s: unsupported cmd %d", __func__, cmd);
+            }
+        }
+    }
+    free(ma_cal);
+
+    return ret;
+}
+
+static bool find_sup_dev(char *name)
+{
+    char *token;
+    const char s[2] = ",";
+    bool ret = false;
+    char sup_devs[128];
+
+    // the rule of comforming suppored dev's name
+    // 1. Both string len are equal
+    // 2. Both string content are equal
+
+    strncpy(sup_devs, SUPPORT_DEV, sizeof(sup_devs));
+    token = strtok(sup_devs, s);
+    while (token != NULL) {
+        if (strncmp(token, name, strlen(token)) == 0 &&
+            strlen(token) == strlen(name)) {
+            ALOGD("%s: support dev %s", __func__, token);
+            ret = true;
+            break;
+        }
+        token = strtok(NULL, s);
+    }
+
+    return ret;
+}
+
+static void ma_set_swap_l(struct audio_device *adev, bool enable)
+{
+    // do platform LR swap if it enables on Waves effect
+    // but there is no Waves implementation
+    if (!my_data) {
+        platform_check_and_set_swap_lr_channels(adev, enable);
+        ALOGV("%s: maxxaudio isn't initialized.", __func__);
+        return;
+    }
+
+    if (enable)
+        check_and_send_all_audio_cal(adev, MA_CMD_SWAP_ENABLE);
+    else
+        check_and_send_all_audio_cal(adev, MA_CMD_SWAP_DISABLE);
+}
+
+static void ma_support_usb(bool enable, int card)
+{
+    char path[128];
+    char id[32];
+    int ret = 0;
+    int32_t fd = -1;
+    char *idd;
+
+    if (enable) {
+        ret = snprintf(path, sizeof(path), "/proc/asound/card%u/id", card);
+        if (ret < 0) {
+            ALOGE("%s: failed on snprintf (%d) to path %s\n",
+                  __func__, ret, path);
+            goto done;
+        }
+        fd = open(path, O_RDONLY);
+        if (fd < 0) {
+            ALOGE("%s: error failed to open id file %s error: %d\n",
+                  __func__, path, errno);
+            goto done;
+        }
+        if (read(fd, id, sizeof(id)) < 0) {
+            ALOGE("%s: file read error", __func__);
+            goto done;
+        }
+        //replace '\n' to '\0'
+        idd = strtok(id, "\n");
+
+        if (find_sup_dev(idd)) {
+            ALOGV("%s: support device name is %s", __func__, id);
+            g_supported_dev |= SUPPORTED_USB;
+        } else
+            ALOGV("%s: device %s isn't found from %s", __func__, id, SUPPORT_DEV);
+    } else {
+        g_supported_dev &= ~SUPPORTED_USB;
+    }
+
+done:
+    if (fd >= 0) close(fd);
+}
+
+// adev_init lock held
+void audio_extn_ma_init(void *platform)
+{
+    ma_stream_type_t i = 0;
+    int ret = 0;
+    char lib_path[128] = {0};
+    char mps_path[128] = {0};
+    struct snd_card_split *snd_split_handle = NULL;
+    snd_split_handle = audio_extn_get_snd_card_split();
+
+    if (platform == NULL) {
+        ALOGE("%s: platform is NULL", __func__);
+        goto error;
+    }
+
+    if (my_data) { free(my_data); }
+    my_data = calloc(1, sizeof(struct ma_platform_data));
+    if (my_data == NULL) {
+        ALOGE("%s: ma_cal alloct fail", __func__);
+        goto error;
+    }
+
+    pthread_mutex_init(&my_data->lock, NULL);
+
+    my_data->platform = platform;
+    ret = snprintf(lib_path, sizeof(lib_path), "%s/%s", LIB_MA_PATH, LIB_MA_PARAM);
+    if (ret < 0) {
+        ALOGE("%s: snprintf failed for lib %s, ret %d", __func__, LIB_MA_PARAM, ret);
+        goto error;
+    }
+
+    my_data->waves_handle = dlopen(lib_path, RTLD_NOW);
+    if (my_data->waves_handle == NULL) {
+         ALOGE("%s: DLOPEN failed for %s", __func__, LIB_MA_PARAM);
+         goto error;
+    } else {
+         ALOGV("%s: DLOPEN successful for %s", __func__, LIB_MA_PARAM);
+
+         my_data->ma_param_init = (ma_param_init_t)dlsym(my_data->waves_handle,
+                                   MA_QDSP_PARAM_INIT);
+         if (!my_data->ma_param_init) {
+             ALOGE("%s: dlsym error %s for ma_param_init", __func__, dlerror());
+             goto error;
+         }
+
+         my_data->ma_param_deinit = (ma_param_deinit_t)dlsym(
+                                     my_data->waves_handle, MA_QDSP_PARAM_DEINIT);
+         if (!my_data->ma_param_deinit) {
+             ALOGE("%s: dlsym error %s for ma_param_deinit", __func__, dlerror());
+             goto error;
+         }
+
+         my_data->ma_set_lr_swap = (ma_set_lr_swap_t)dlsym(my_data->waves_handle,
+                                    MA_QDSP_SET_LR_SWAP);
+         if (!my_data->ma_set_lr_swap) {
+             ALOGE("%s: dlsym error %s for ma_set_lr_swap", __func__, dlerror());
+             goto error;
+         }
+
+         my_data->ma_set_sound_mode = (ma_set_sound_mode_t)dlsym(
+                                       my_data->waves_handle, MA_QDSP_SET_MODE);
+         if (!my_data->ma_set_sound_mode) {
+             ALOGE("%s: dlsym error %s for ma_set_sound_mode", __func__, dlerror());
+             goto error;
+         }
+
+         my_data->ma_set_volume = (ma_set_volume_t)dlsym(my_data->waves_handle,
+                                   MA_QDSP_SET_VOL);
+         if (!my_data->ma_set_volume) {
+             ALOGE("%s: dlsym error %s for ma_set_volume", __func__, dlerror());
+             goto error;
+         }
+
+         my_data->ma_set_volume_table = (ma_set_volume_table_t)dlsym(
+                                         my_data->waves_handle, MA_QDSP_SET_VOLT);
+         if (!my_data->ma_set_volume_table) {
+             ALOGE("%s: dlsym error %s for ma_set_volume_table", __func__, dlerror());
+             goto error;
+         }
+    }
+
+    /* get preset table */
+    if (snd_split_handle == NULL) {
+        snprintf(mps_path, sizeof(mps_path), "%s/%s.mps",
+                 PRESET_PATH, MPS_BASE_STRING);
+    } else {
+        snprintf(mps_path, sizeof(mps_path), "%s/%s_%s.mps",
+                 PRESET_PATH, MPS_BASE_STRING, snd_split_handle->form_factor);
+    }
+
+    /* check file */
+    if (access(mps_path, F_OK) < 0) {
+        ALOGW("%s: file %s isn't existed.", __func__, mps_path);
+        goto error;
+    } else
+        ALOGD("%s: Loading mps file: %s", __func__, mps_path);
+
+    /* TODO: check user preset table once the feature is enabled
+    if (access(USER_PRESET_PATH, F_OK) < 0 ){
+        ALOGW("%s: file %s isn't existed.", __func__, USER_PRESET_PATH);
+        goto error;
+    }
+    */
+    if (access(CONFIG_PATH, F_OK) < 0) {
+        ALOGW("%s: file %s isn't existed.", __func__, CONFIG_PATH);
+        goto error;
+    }
+
+    /* init ma parameter */
+    if (my_data->ma_param_init(&g_ma_audio_cal_handle,
+                               mps_path,
+                               USER_PRESET_PATH, /* unused */
+                               CONFIG_PATH,
+                               &set_audio_cal)) {
+        if (!g_ma_audio_cal_handle) {
+            ALOGE("%s: ma parameters initialize failed", __func__);
+            my_data->ma_param_deinit(&g_ma_audio_cal_handle);
+            goto error;
+        }
+        ALOGD("%s: ma parameters initialize successful", __func__);
+    } else {
+        ALOGE("%s: ma parameters initialize failed", __func__);
+        goto error;
+    }
+
+    /* init volume table */
+    for (i = 0; i < STREAM_MAX_TYPES; i++) {
+        ma_cur_state_table[i].vol = 0.0;
+        ma_cur_state_table[i].active = false;
+    }
+
+    return;
+
+error:
+    if (my_data) { free(my_data); }
+    my_data = NULL;
+}
+
+//adev_init lock held
+void audio_extn_ma_deinit()
+{
+    if (my_data) {
+        /* deinit ma parameter */
+        if (my_data->ma_param_deinit &&
+            my_data->ma_param_deinit(&g_ma_audio_cal_handle))
+            ALOGD("%s: ma parameters uninitialize successful", __func__);
+        else
+            ALOGD("%s: ma parameters uninitialize failed", __func__);
+
+        pthread_mutex_destroy(&my_data->lock);
+        free(my_data);
+        my_data = NULL;
+    }
+}
+
+// adev_init and adev lock held
+bool audio_extn_ma_set_state(struct audio_device *adev, int stream_type,
+                             float vol, bool active)
+{
+    bool ret = false;
+    ma_stream_type_t stype = (ma_stream_type_t)stream_type;
+
+    ALOGV("%s: stream[%d] vol[%f] active[%s]",
+          __func__, stream_type, vol, active ? "true" : "false");
+
+    if (!my_data) {
+        ALOGV("%s: maxxaudio isn't initialized.", __func__);
+        return ret;
+    }
+
+    // update condition
+    // 1. start track: active and volume isn't zero
+    // 2. stop track: no tracks are active
+    if ((active && vol != 0) ||
+        (!active)) {
+        pthread_mutex_lock(&my_data->lock);
+
+        ma_cur_state_table[stype].vol = vol;
+        ma_cur_state_table[stype].active = active;
+        if (is_active())
+            ret = check_and_send_all_audio_cal(adev, MA_CMD_VOL);
+
+        pthread_mutex_unlock(&my_data->lock);
+    }
+
+    return ret;
+}
+
+void audio_extn_ma_set_device(struct audio_usecase *usecase)
+{
+    int i = 0;
+    int u_index = -1;
+    float vol = 0;
+    struct ma_audio_cal_settings *ma_cal = NULL;
+
+    if (!my_data) {
+        ALOGV("%s: maxxaudio isn't initialized.", __func__);
+        return;
+    }
+
+    if (!valid_usecase(usecase)) {
+        ALOGV("%s: %d is not supported usecase", __func__, usecase->id);
+        return;
+    }
+
+    ma_cal = (struct ma_audio_cal_settings *)malloc(sizeof(struct ma_audio_cal_settings));
+
+    /* update audio_cal and send it */
+    if (ma_cal != NULL){
+        ma_cal->app_type = usecase->stream.out->app_type_cfg.app_type;
+        ma_cal->device = usecase->stream.out->devices;
+        ALOGV("%s: send usecase(%d) app_type(%d) device(%d)",
+                      __func__, usecase->id, ma_cal->app_type, ma_cal->device);
+
+        pthread_mutex_lock(&my_data->lock);
+
+        if (is_active()) {
+            ALOGV("%s: send volume table === Start", __func__);
+            for (i = 0; i < STREAM_MAX_TYPES; i++)
+                ALOGV("%s: stream(%d) volume(%f) active(%s)", __func__, i,
+                    ma_cur_state_table[i].vol,
+                    ma_cur_state_table[i].active ? "T" : "F");
+            ALOGV("%s: send volume table === End", __func__);
+
+            if (!ma_set_volume_table_l(ma_cal,
+                                       STREAM_MAX_TYPES,
+                                       ma_cur_state_table))
+                ALOGE("Waves: ma_set_volume_table_l %f returned with error.", vol);
+            else
+                ALOGV("Waves: ma_set_volume_table_l success");
+
+        }
+        pthread_mutex_unlock(&my_data->lock);
+        free(ma_cal);
+    } else {
+        ALOGE("%s: ma_cal alloct fail", __func__);
+    }
+}
+
+void audio_extn_ma_set_parameters(struct audio_device *adev,
+                                  struct str_parms *parms)
+{
+    int ret;
+    bool ret_b;
+    int val;
+    char value[128];
+
+    // do LR swap and usb recognition
+    ret = str_parms_get_int(parms, "rotation", &val);
+    if (ret >= 0) {
+        switch (val) {
+        case 270:
+            ma_set_swap_l(adev, true);
+            break;
+        case 0:
+        case 90:
+        case 180:
+            ma_set_swap_l(adev, false);
+            break;
+        }
+    }
+
+    // check connect status
+    ret = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_CONNECT, value,
+                            sizeof(value));
+    if (ret >= 0) {
+        audio_devices_t device = (audio_devices_t)strtoul(value, NULL, 10);
+        if (audio_is_usb_out_device(device)) {
+            ret = str_parms_get_str(parms, "card", value, sizeof(value));
+            if (ret >= 0) {
+                const int card = atoi(value);
+                ma_support_usb(true, card);
+            }
+        }
+    }
+
+    // check disconnect status
+    ret = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_DISCONNECT, value,
+                            sizeof(value));
+    if (ret >= 0) {
+        audio_devices_t device = (audio_devices_t)strtoul(value, NULL, 10);
+        if (audio_is_usb_out_device(device)) {
+            ret = str_parms_get_str(parms, "card", value, sizeof(value));
+            if (ret >= 0) {
+                const int card = atoi(value);
+                ma_support_usb(false, card /*useless*/);
+            }
+        }
+    }
+}
+
+bool audio_extn_ma_supported_usb()
+{
+    ALOGV("%s: current support 0x%x", __func__, g_supported_dev);
+    return (g_supported_dev & SUPPORTED_USB) ? true : false;
+}
diff --git a/hal/audio_extn/maxxaudio.h b/hal/audio_extn/maxxaudio.h
new file mode 100644
index 0000000..4c91107
--- /dev/null
+++ b/hal/audio_extn/maxxaudio.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 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 MAXXAUDIO_H_
+#define MAXXAUDIO_H_
+
+#ifndef MAXXAUDIO_QDSP_ENABLED
+#define audio_extn_ma_init(platform)                                (0)
+#define audio_extn_ma_deinit()                                      (0)
+#define audio_extn_ma_set_state(adev, type, vol, active)            (false)
+#define audio_extn_ma_set_device(usecase)                           (0)
+#define audio_extn_ma_set_parameters(adev, param)                   (0)
+#define audio_extn_ma_supported_usb()                               (false)
+#else
+void audio_extn_ma_init(void *platform);
+void audio_extn_ma_deinit();
+bool audio_extn_ma_set_state(struct audio_device *adev, int stream_type,
+                             float vol, bool active);
+void audio_extn_ma_set_device(struct audio_usecase *usecase);
+void audio_extn_ma_set_parameters(struct audio_device *adev,
+                                  struct str_parms *parms);
+bool audio_extn_ma_supported_usb();
+#endif /* MAXXAUDIO_QDSP_ENABLED */
+
+#endif /* MAXXAUDIO_H_ */
+
diff --git a/hal/audio_extn/sndmonitor.c b/hal/audio_extn/sndmonitor.c
index 9b44742..6e06d03 100644
--- a/hal/audio_extn/sndmonitor.c
+++ b/hal/audio_extn/sndmonitor.c
@@ -39,7 +39,7 @@
 #include <pthread.h>
 #include <cutils/list.h>
 #include <cutils/hashmap.h>
-#include <cutils/log.h>
+#include <log/log.h>
 #include <cutils/str_parms.h>
 #include <ctype.h>
 
diff --git a/hal/audio_extn/soundtrigger.c b/hal/audio_extn/soundtrigger.c
index 3417c72..c11e747 100644
--- a/hal/audio_extn/soundtrigger.c
+++ b/hal/audio_extn/soundtrigger.c
@@ -23,22 +23,126 @@
 #include <dlfcn.h>
 #include <pthread.h>
 #include <unistd.h>
-#include <cutils/log.h>
+#include <log/log.h>
 #include "audio_hw.h"
 #include "audio_extn.h"
 #include "platform.h"
 #include "platform_api.h"
-#include "sound_trigger_prop_intf.h"
+
+/*-------------------- Begin: AHAL-STHAL Interface ---------------------------*/
+/*
+ * Maintain the proprietary interface between AHAL and STHAL locally to avoid
+ * the compilation dependency of interface header file from STHAL.
+ */
+
+#define MAKE_HAL_VERSION(maj, min) ((((maj) & 0xff) << 8) | ((min) & 0xff))
+#define MAJOR_VERSION(ver) (((ver) & 0xff00) >> 8)
+#define MINOR_VERSION(ver) ((ver) & 0x00ff)
+
+/* Proprietary interface version used for compatibility with STHAL */
+#define STHAL_PROP_API_VERSION_1_0 MAKE_HAL_VERSION(1, 0)
+#define STHAL_PROP_API_CURRENT_VERSION STHAL_PROP_API_VERSION_1_0
+
+#define ST_EVENT_CONFIG_MAX_STR_VALUE 32
+
+typedef enum {
+    ST_EVENT_SESSION_REGISTER,
+    ST_EVENT_SESSION_DEREGISTER
+} sound_trigger_event_type_t;
+
+typedef enum {
+    AUDIO_EVENT_CAPTURE_DEVICE_INACTIVE,
+    AUDIO_EVENT_CAPTURE_DEVICE_ACTIVE,
+    AUDIO_EVENT_PLAYBACK_STREAM_INACTIVE,
+    AUDIO_EVENT_PLAYBACK_STREAM_ACTIVE,
+    AUDIO_EVENT_STOP_LAB,
+    AUDIO_EVENT_SSR,
+    AUDIO_EVENT_NUM_ST_SESSIONS,
+    AUDIO_EVENT_READ_SAMPLES,
+    AUDIO_EVENT_DEVICE_CONNECT,
+    AUDIO_EVENT_DEVICE_DISCONNECT,
+    AUDIO_EVENT_SVA_EXEC_MODE,
+    AUDIO_EVENT_SVA_EXEC_MODE_STATUS,
+    AUDIO_EVENT_CAPTURE_STREAM_INACTIVE,
+    AUDIO_EVENT_CAPTURE_STREAM_ACTIVE,
+} audio_event_type_t;
+
+typedef enum {
+    USECASE_TYPE_PCM_PLAYBACK,
+    USECASE_TYPE_PCM_CAPTURE,
+    USECASE_TYPE_VOICE_CALL,
+    USECASE_TYPE_VOIP_CALL,
+} audio_stream_usecase_type_t;
+
+typedef enum {
+    SND_CARD_STATUS_OFFLINE,
+    SND_CARD_STATUS_ONLINE,
+    CPE_STATUS_OFFLINE,
+    CPE_STATUS_ONLINE
+} ssr_event_status_t;
+
+struct sound_trigger_session_info {
+    void* p_ses; /* opaque pointer to st_session obj */
+    int capture_handle;
+    struct pcm *pcm;
+    struct pcm_config config;
+};
+
+struct audio_read_samples_info {
+    struct sound_trigger_session_info *ses_info;
+    void *buf;
+    size_t num_bytes;
+};
+
+struct audio_hal_usecase {
+    audio_stream_usecase_type_t type;
+};
+
+struct sound_trigger_event_info {
+    struct sound_trigger_session_info st_ses;
+};
+
+struct audio_event_info {
+    union {
+        ssr_event_status_t status;
+        int value;
+        struct sound_trigger_session_info ses_info;
+        struct audio_read_samples_info aud_info;
+        char str_value[ST_EVENT_CONFIG_MAX_STR_VALUE];
+        struct audio_hal_usecase usecase;
+    }u;
+};
+
+/* STHAL callback which is called by AHAL */
+typedef int (*sound_trigger_hw_call_back_t)(audio_event_type_t,
+                                  struct audio_event_info*);
+
+/*---------------- End: AHAL-STHAL Interface ----------------------------------*/
 
 #define XSTR(x) STR(x)
 #define STR(x) #x
 
+#define DLSYM(handle, ptr, symbol, err)                                   \
+do {                                                                      \
+    ptr = dlsym(handle, #symbol);                                         \
+    if (ptr == NULL) {                                                    \
+        ALOGE("%s: dlsym %s failed . %s", __func__, #symbol, dlerror());  \
+        err = -ENODEV;                                                    \
+    }                                                                     \
+} while(0)
+
 #ifdef __LP64__
-#define SOUND_TRIGGER_LIBRARY_PATH "/system/vendor/lib64/hw/sound_trigger.primary.%s.so"
+#define SOUND_TRIGGER_LIBRARY_PATH "/vendor/lib64/hw/sound_trigger.primary.%s.so"
 #else
-#define SOUND_TRIGGER_LIBRARY_PATH "/system/vendor/lib/hw/sound_trigger.primary.%s.so"
+#define SOUND_TRIGGER_LIBRARY_PATH "/vendor/lib/hw/sound_trigger.primary.%s.so"
 #endif
 
+/*
+ * Current proprietary API version used by AHAL. Queried by STHAL
+ * for compatibility check with AHAL
+ */
+const unsigned int sthal_prop_api_version = STHAL_PROP_API_CURRENT_VERSION;
+
 struct sound_trigger_info  {
     struct sound_trigger_session_info st_ses;
     bool lab_stopped;
@@ -51,6 +155,7 @@
     sound_trigger_hw_call_back_t st_callback;
     struct listnode st_ses_list;
     pthread_mutex_t lock;
+    unsigned int sthal_prop_api_version;
 };
 
 static struct sound_trigger_audio_device *st_dev;
@@ -60,7 +165,7 @@
 {
     struct sound_trigger_info  *st_ses_info = NULL;
     struct listnode *node;
-    ALOGV("%s: list %d capture_handle %d", __func__,
+    ALOGV("%s: list empty %d capture_handle %d", __func__,
            list_empty(&st_dev->st_ses_list), capture_handle);
     list_for_each(node, &st_dev->st_ses_list) {
         st_ses_info = node_to_item(node, struct sound_trigger_info , list);
@@ -70,6 +175,37 @@
     return NULL;
 }
 
+static int populate_usecase(struct audio_hal_usecase *usecase,
+                       struct audio_usecase *uc_info)
+{
+    int status  = 0;
+
+    switch (uc_info->type) {
+    case PCM_PLAYBACK:
+        if (uc_info->id == USECASE_AUDIO_PLAYBACK_VOIP)
+            usecase->type = USECASE_TYPE_VOIP_CALL;
+        else
+            usecase->type = USECASE_TYPE_PCM_PLAYBACK;
+        break;
+
+    case PCM_CAPTURE:
+        if (uc_info->id == USECASE_AUDIO_RECORD_VOIP)
+            usecase->type = USECASE_TYPE_VOIP_CALL;
+        else
+            usecase->type = USECASE_TYPE_PCM_CAPTURE;
+        break;
+
+    case VOICE_CALL:
+        usecase->type = USECASE_TYPE_VOICE_CALL;
+        break;
+
+    default:
+        ALOGE("%s: unsupported usecase type %d", __func__, uc_info->type);
+        status = -EINVAL;
+    }
+    return status;
+}
+
 static void stdev_snd_mon_cb(void * stream __unused, struct str_parms * parms)
 {
     if (!parms)
@@ -80,7 +216,7 @@
 }
 
 int audio_hw_call_back(sound_trigger_event_type_t event,
-                       sound_trigger_event_info_t* config)
+                       struct sound_trigger_event_info* config)
 {
     int status = 0;
     struct sound_trigger_info  *st_ses_info;
@@ -138,7 +274,7 @@
 {
     int ret = -1;
     struct sound_trigger_info  *st_info = NULL;
-    audio_event_info_t event;
+    struct audio_event_info event;
 
     if (!st_dev)
        return ret;
@@ -174,11 +310,10 @@
 
 void audio_extn_sound_trigger_stop_lab(struct stream_in *in)
 {
-    int status = 0;
     struct sound_trigger_info  *st_ses_info = NULL;
-    audio_event_info_t event;
+    struct audio_event_info event;
 
-    if (!st_dev || !in)
+    if (!st_dev || !in || !in->is_st_session_active)
        return;
 
     pthread_mutex_lock(&st_dev->lock);
@@ -188,8 +323,10 @@
         event.u.ses_info = st_ses_info->st_ses;
         ALOGV("%s: AUDIO_EVENT_STOP_LAB pcm %p", __func__, st_ses_info->st_ses.pcm);
         st_dev->st_callback(AUDIO_EVENT_STOP_LAB, &event);
+        in->is_st_session_active = false;
     }
 }
+
 void audio_extn_sound_trigger_check_and_get_session(struct stream_in *in)
 {
     struct sound_trigger_info  *st_ses_info = NULL;
@@ -225,6 +362,9 @@
     if (!st_dev)
        return;
 
+    if (st_dev->sthal_prop_api_version >= STHAL_PROP_API_VERSION_1_0)
+        return;
+
     if (snd_device >= SND_DEVICE_OUT_BEGIN &&
         snd_device < SND_DEVICE_OUT_END) {
         device_type = PCM_PLAYBACK;
@@ -256,10 +396,52 @@
     }/*Events for output device, if required can be placed here in else*/
 }
 
+void audio_extn_sound_trigger_update_stream_status(struct audio_usecase *uc_info,
+                                     st_event_type_t event)
+{
+    bool raise_event = false;
+    struct audio_event_info ev_info;
+    audio_event_type_t ev;
+
+    if (!st_dev)
+       return;
+
+    if (st_dev->sthal_prop_api_version < STHAL_PROP_API_VERSION_1_0)
+        return;
+
+    if (uc_info == NULL) {
+        ALOGE("%s: null usecase", __func__);
+        return;
+    }
+
+    bool valid_type =
+            (uc_info->type == PCM_PLAYBACK &&
+             platform_snd_device_has_speaker(uc_info->out_snd_device)) ||
+            (uc_info->type == PCM_CAPTURE) ||
+            (uc_info->type == VOICE_CALL);
+
+    if (valid_type && platform_sound_trigger_usecase_needs_event(uc_info->id)) {
+        ALOGV("%s: uc_id %d of type %d for Event %d, with Raise=%d",
+              __func__, uc_info->id, uc_info->type, event, raise_event);
+        if (uc_info->type == PCM_CAPTURE) {
+            ev = (event == ST_EVENT_STREAM_BUSY) ? AUDIO_EVENT_CAPTURE_STREAM_ACTIVE :
+                                                   AUDIO_EVENT_CAPTURE_STREAM_INACTIVE;
+        } else {
+            ev = (event == ST_EVENT_STREAM_BUSY) ? AUDIO_EVENT_PLAYBACK_STREAM_ACTIVE :
+                                                   AUDIO_EVENT_PLAYBACK_STREAM_INACTIVE;
+        }
+        if (!populate_usecase(&ev_info.u.usecase, uc_info)) {
+            ALOGI("%s: send event %d: usecase id %d, type %d",
+                  __func__, ev, uc_info->id, uc_info->type);
+            st_dev->st_callback(ev, &ev_info);
+        }
+    }
+}
+
 void audio_extn_sound_trigger_set_parameters(struct audio_device *adev __unused,
                                struct str_parms *params)
 {
-    audio_event_info_t event;
+    struct audio_event_info event;
     char value[32];
     int ret, val;
 
@@ -308,7 +490,7 @@
 {
     int status = 0;
     char sound_trigger_lib[100];
-    void *lib_handle;
+    void *sthal_prop_api_version;
 
     ALOGV("%s: Enter", __func__);
 
@@ -326,20 +508,33 @@
     st_dev->lib_handle = dlopen(sound_trigger_lib, RTLD_NOW);
 
     if (st_dev->lib_handle == NULL) {
-        ALOGE("%s: DLOPEN failed for %s. error = %s", __func__, sound_trigger_lib,
-                dlerror());
-        status = -EINVAL;
+        ALOGE("%s: error %s", __func__, dlerror());
+        status = -ENODEV;
         goto cleanup;
     }
     ALOGV("%s: DLOPEN successful for %s", __func__, sound_trigger_lib);
 
-    st_dev->st_callback = (sound_trigger_hw_call_back_t)
-              dlsym(st_dev->lib_handle, "sound_trigger_hw_call_back");
+    DLSYM(st_dev->lib_handle, st_dev->st_callback, sound_trigger_hw_call_back,
+          status);
+    if (status)
+        goto cleanup;
 
-    if (st_dev->st_callback == NULL) {
-       ALOGE("%s: ERROR. dlsym Error:%s sound_trigger_hw_call_back", __func__,
-               dlerror());
-       goto cleanup;
+    DLSYM(st_dev->lib_handle, sthal_prop_api_version,
+          sthal_prop_api_version, status);
+    if (status) {
+        st_dev->sthal_prop_api_version = 0;
+        status  = 0; /* passthru for backward compability */
+    } else {
+        st_dev->sthal_prop_api_version = *(int*)sthal_prop_api_version;
+        if (MAJOR_VERSION(st_dev->sthal_prop_api_version) !=
+            MAJOR_VERSION(STHAL_PROP_API_CURRENT_VERSION)) {
+            ALOGE("%s: Incompatible API versions ahal:0x%x != sthal:0x%x",
+                  __func__, STHAL_PROP_API_CURRENT_VERSION,
+                  st_dev->sthal_prop_api_version);
+            goto cleanup;
+        }
+        ALOGD("%s: sthal is using proprietary API version 0x%04x", __func__,
+              st_dev->sthal_prop_api_version);
     }
 
     st_dev->adev = adev;
@@ -354,7 +549,6 @@
     free(st_dev);
     st_dev = NULL;
     return status;
-
 }
 
 void audio_extn_sound_trigger_deinit(struct audio_device *adev)
diff --git a/hal/audio_extn/spkr_protection.c b/hal/audio_extn/spkr_protection.c
index 8afb0dc..8c09a29 100644
--- a/hal/audio_extn/spkr_protection.c
+++ b/hal/audio_extn/spkr_protection.c
@@ -20,7 +20,7 @@
 
 #include <errno.h>
 #include <math.h>
-#include <cutils/log.h>
+#include <log/log.h>
 #include <fcntl.h>
 #include "audio_hw.h"
 #include "platform.h"
@@ -51,7 +51,7 @@
 #define MAX_RESISTANCE_SPKR_Q24 (40 * (1 << 24))
 
 /*Path where the calibration file will be stored*/
-#define CALIB_FILE "/data/misc/audio/audio.cal"
+#define CALIB_FILE "/data/vendor/audio/audio.cal"
 
 /*Time between retries for calibartion or intial wait time
   after boot up*/
@@ -304,6 +304,7 @@
     int32_t pcm_dev_rx_id = -1, pcm_dev_tx_id = -1;
     struct timespec ts;
     int retry_duration;
+    int app_type = 0;
 
     if (!adev) {
         ALOGE("%s: Invalid params", __func__);
@@ -472,7 +473,20 @@
     handle.pcm_tx = NULL;
 
     /* Clear TX calibration to handset mic */
-    platform_send_audio_calibration(adev->platform, SND_DEVICE_IN_HANDSET_MIC);
+    if (platform_supports_app_type_cfg()) {
+        ALOGD("%s: Platform supports APP type configuration, using V2\n", __func__);
+        if (uc_info_tx != NULL) {
+            ALOGD("%s: UC Info TX is not NULL, updating and sending calibration\n", __func__);
+            uc_info_tx->in_snd_device = SND_DEVICE_IN_HANDSET_MIC;
+            uc_info_tx->out_snd_device = SND_DEVICE_NONE;
+            platform_get_default_app_type_v2(adev->platform, PCM_CAPTURE, &app_type);
+            platform_send_audio_calibration_v2(adev->platform, uc_info_tx,
+                                               app_type, 8000);
+        }
+    } else {
+        ALOGW("%s: Platform does NOT support APP type configuration, using V1\n", __func__);
+        platform_send_audio_calibration(adev->platform, SND_DEVICE_IN_HANDSET_MIC);
+    }
     if (!status.status) {
         protCfg.mode = MSM_SPKR_PROT_CALIBRATED;
         protCfg.r0[SP_V2_SPKR_1] = status.r0[SP_V2_SPKR_1];
@@ -766,24 +780,6 @@
     }
 }
 
-int audio_extn_spkr_prot_get_acdb_id(snd_device_t snd_device)
-{
-    int acdb_id;
-
-    switch(snd_device) {
-    case SND_DEVICE_OUT_SPEAKER:
-        acdb_id = platform_get_snd_device_acdb_id(SND_DEVICE_OUT_SPEAKER_PROTECTED);
-        break;
-    case SND_DEVICE_OUT_VOICE_SPEAKER:
-        acdb_id = platform_get_snd_device_acdb_id(SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED);
-        break;
-    default:
-        acdb_id = -EINVAL;
-        break;
-    }
-    return acdb_id;
-}
-
 int audio_extn_get_spkr_prot_snd_device(snd_device_t snd_device)
 {
     if (!handle.spkr_prot_enable)
@@ -804,6 +800,7 @@
     struct audio_usecase *uc_info_tx;
     struct audio_device *adev = handle.adev_handle;
     int32_t pcm_dev_tx_id = -1, ret = 0;
+    int app_type = 0;
 
     ALOGV("%s: Entry", __func__);
     if (!adev) {
@@ -855,7 +852,20 @@
 
 exit:
     /* Clear VI feedback cal and replace with handset MIC  */
-    platform_send_audio_calibration(adev->platform, SND_DEVICE_IN_HANDSET_MIC);
+    if (platform_supports_app_type_cfg()) {
+        ALOGD("%s: Platform supports APP type configuration, using V2\n", __func__);
+        if (uc_info_tx != NULL) {
+            ALOGD("%s: UC Info TX is not NULL, updating and sending calibration\n", __func__);
+            uc_info_tx->in_snd_device = SND_DEVICE_IN_HANDSET_MIC;
+            uc_info_tx->out_snd_device = SND_DEVICE_NONE;
+            platform_get_default_app_type_v2(adev->platform, PCM_CAPTURE, &app_type);
+            platform_send_audio_calibration_v2(adev->platform, uc_info_tx,
+                                               app_type, 8000);
+        }
+    } else {
+        ALOGW("%s: Platform does not support APP type configuration, using V1\n", __func__);
+        platform_send_audio_calibration(adev->platform, SND_DEVICE_IN_HANDSET_MIC);
+    }
     if (ret) {
         if (handle.pcm_tx)
             pcm_close(handle.pcm_tx);
diff --git a/hal/audio_extn/tfa_98xx.c b/hal/audio_extn/tfa_98xx.c
index 41523b5..c449f72 100644
--- a/hal/audio_extn/tfa_98xx.c
+++ b/hal/audio_extn/tfa_98xx.c
@@ -16,7 +16,7 @@
 
 #define LOG_TAG "tfa_98xx"
 /*#define LOG_NDEBUG 0*/
-#include <cutils/log.h>
+#include <log/log.h>
 
 #include <stdlib.h>
 #include <audio_hw.h>
diff --git a/hal/audio_extn/usb.c b/hal/audio_extn/usb.c
index dc1afa1..71e1aed 100644
--- a/hal/audio_extn/usb.c
+++ b/hal/audio_extn/usb.c
@@ -19,7 +19,7 @@
 #include <errno.h>
 #include <pthread.h>
 #include <stdlib.h>
-#include <cutils/log.h>
+#include <log/log.h>
 #include <cutils/str_parms.h>
 #include <sys/ioctl.h>
 #include <unistd.h>
@@ -37,10 +37,13 @@
 #define CHANNEL_NUMBER_STR      "Channels: "
 #define PLAYBACK_PROFILE_STR    "Playback:"
 #define CAPTURE_PROFILE_STR     "Capture:"
+#define DATA_PACKET_INTERVAL_STR "Data packet interval: "
 #define USB_SIDETONE_GAIN_STR   "usb_sidetone_gain"
 #define ABS_SUB(A, B) (((A) > (B)) ? ((A) - (B)):((B) - (A)))
 #define SAMPLE_RATE_8000          8000
 #define SAMPLE_RATE_11025         11025
+#define DEFAULT_SERVICE_INTERVAL_US    1000
+
 /* TODO: dynamically populate supported sample rates */
 static uint32_t supported_sample_rates[] =
     {192000, 176400, 96000, 88200, 64000, 48000, 44100};
@@ -50,10 +53,10 @@
 
 // assert on sizeof bm v/s size of rates if needed
 
-enum usb_usecase_type{
+typedef enum usb_usecase_type{
     USB_PLAYBACK = 0,
     USB_CAPTURE,
-};
+} usb_usecase_type_t;
 
 enum {
     USB_SIDETONE_ENABLE_INDEX = 0,
@@ -67,6 +70,8 @@
     unsigned int channel_count;
     unsigned int rate_size;
     unsigned int rates[MAX_SAMPLE_RATE_SIZE];
+    unsigned long service_interval_us;
+    usb_usecase_type_t type;
 };
 
 struct usb_card_config {
@@ -271,6 +276,43 @@
     return 0;
 }
 
+static int usb_get_service_interval(const char *interval_str_start,
+                                    struct usb_device_config *usb_device_info)
+{
+    unsigned long interval = 0;
+    char time_unit[8] = {0};
+    int multiplier = 0;
+    char *eol = strchr(interval_str_start, '\n');
+    if (!eol) {
+        ALOGE("%s: No EOL found", __func__);
+        return -1;
+    }
+    char *tmp = (char *)calloc(1, eol-interval_str_start+1);
+    if (!tmp) {
+        ALOGE("%s: failed to allocate tmp", __func__);
+        return -1;
+    }
+    memcpy(tmp, interval_str_start, eol-interval_str_start);
+    sscanf(tmp, "%lu %2s", &interval, &time_unit[0]);
+    if (!strcmp(time_unit, "us")) {
+        multiplier = 1;
+    } else if (!strcmp(time_unit, "ms")) {
+        multiplier = 1000;
+    } else if (!strcmp(time_unit, "s")) {
+        multiplier = 1000000;
+    } else {
+        ALOGE("%s: unknown time_unit %s, assume default", __func__, time_unit);
+        interval = DEFAULT_SERVICE_INTERVAL_US;
+        multiplier = 1;
+    }
+    interval *= multiplier;
+    ALOGV("%s: set service_interval_us %lu", __func__, interval);
+    usb_device_info->service_interval_us = interval;
+    free(tmp);
+    return 0;
+}
+
+
 static int usb_get_capability(int type,
                               struct usb_card_config *usb_card_info,
                               int card)
@@ -286,6 +328,7 @@
     char *target = NULL;
     char *read_buf = NULL;
     char *rates_str = NULL;
+    char *interval_str_start = NULL;
     char path[128];
     int ret = 0;
     char *bit_width_str = NULL;
@@ -366,6 +409,7 @@
             ret = -ENOMEM;
             break;
         }
+        usb_device_info->type = type;
         /* Bit bit_width parsing */
         bit_width_start = strstr(str_start, "Format: ");
         if (bit_width_start == NULL) {
@@ -443,6 +487,18 @@
             free(usb_device_info);
             continue;
         }
+        // Data packet interval is an optional field.
+        // Assume 1ms interval if this cannot be read
+        usb_device_info->service_interval_us = DEFAULT_SERVICE_INTERVAL_US;
+        interval_str_start = strstr(str_start, DATA_PACKET_INTERVAL_STR);
+        if (interval_str_start != NULL) {
+            interval_str_start += strlen(DATA_PACKET_INTERVAL_STR);
+            ret = usb_get_service_interval(interval_str_start, usb_device_info);
+            if (ret < 0) {
+                ALOGE("%s: error unable to get service interval, assume default",
+                      __func__);
+            }
+        }
         /* Add to list if every field is valid */
         list_add_tail(&usb_card_info->usb_device_conf_list,
                       &usb_device_info->list);
@@ -697,12 +753,14 @@
     return true;
 }
 
-static bool usb_get_best_match_for_sample_rate(
+static bool usb_get_best_match_for_sample_rate (
                             struct listnode *dev_list,
                             unsigned int bit_width,
                             unsigned int channel_count,
                             unsigned int stream_sample_rate,
-                            unsigned int *sr)
+                            unsigned int *sr,
+                            unsigned int service_interval,
+                            bool do_service_interval_check)
 {
     struct listnode *node_i;
     struct usb_device_config *dev_info;
@@ -720,7 +778,10 @@
                  "%s: USB ch(%d)bw(%d), stm ch(%d)bw(%d)sr(%d), candidate(%d)",
                  __func__, dev_info->channel_count, dev_info->bit_width,
                  channel_count, bit_width, stream_sample_rate, candidate);
-        if ((dev_info->bit_width != bit_width) || dev_info->channel_count != channel_count)
+        if ((dev_info->bit_width != bit_width) ||
+            (dev_info->channel_count != channel_count) ||
+            (do_service_interval_check && (dev_info->service_interval_us !=
+                                           service_interval)))
             continue;
 
         candidate = 0;
@@ -787,7 +848,9 @@
                                        *bit_width,
                                        *channel_count,
                                        *sample_rate,
-                                       sample_rate);
+                                       sample_rate,
+                                       0 /*service int*/,
+                                       false /*do service int check*/);
 exit:
     ALOGV("%s: Updated sample rate per profile: bit-width(%d) rate(%d) chs(%d)",
            __func__, *bit_width, *sample_rate, *channel_count);
@@ -1110,6 +1173,84 @@
     return access(path, F_OK) == 0;
 }
 
+unsigned long audio_extn_usb_find_service_interval(bool min,
+                                                   bool playback) {
+    struct usb_card_config *card_info;
+    struct usb_device_config *dev_info;
+    struct listnode *node_i;
+    struct listnode *node_j;
+    unsigned long interval_us = min ? ULONG_MAX : 1; // 0 is invalid
+    list_for_each(node_i, &usbmod->usb_card_conf_list) {
+        card_info = node_to_item(node_i, struct usb_card_config, list);
+        list_for_each(node_j, &card_info->usb_device_conf_list) {
+            dev_info = node_to_item(node_j, struct usb_device_config, list);
+            if ((playback && (dev_info->type == USB_PLAYBACK)) ||
+                (!playback && (dev_info->type == USB_CAPTURE))) {
+                interval_us = min ?
+                        _MIN(interval_us, dev_info->service_interval_us) :
+                        _MAX(interval_us, dev_info->service_interval_us);
+            }
+        }
+        break;
+    }
+    return interval_us;
+}
+
+int audio_extn_usb_altset_for_service_interval(bool playback,
+                                               unsigned long service_interval,
+                                               uint32_t *bit_width,
+                                               uint32_t *sample_rate,
+                                               uint32_t *channel_count)
+{
+    struct usb_card_config *card_info;
+    struct usb_device_config *dev_info;
+    struct listnode *node_i;
+    struct listnode *node_j;
+    uint32_t bw = 0;
+    uint32_t ch = 0;
+    uint32_t sr = 0;
+    list_for_each(node_i, &usbmod->usb_card_conf_list) {
+        /* Currently only apply the first playback sound card configuration */
+        card_info = node_to_item(node_i, struct usb_card_config, list);
+        list_for_each(node_j, &card_info->usb_device_conf_list) {
+            dev_info = node_to_item(node_j, struct usb_device_config, list);
+            if ((playback && dev_info->type == USB_PLAYBACK) ||
+                (!playback && dev_info->type == USB_CAPTURE)) {
+                if (dev_info->service_interval_us != service_interval)
+                    continue;
+                if (dev_info->bit_width > bw) {
+                    bw = dev_info->bit_width;
+                    ch = dev_info->channel_count;
+                } else if (dev_info->bit_width == bw &&
+                           dev_info->channel_count > ch) {
+                    ch = dev_info->channel_count;
+                }
+            }
+        }
+        break;
+    }
+    if (bw == 0 || ch == 0)
+        return -1;
+    list_for_each(node_i, &usbmod->usb_card_conf_list) {
+        /* Currently only apply the first playback sound card configuration */
+        card_info = node_to_item(node_i, struct usb_card_config, list);
+        if ((playback && usb_output_device(card_info->usb_device_type)) ||
+            (!playback && usb_input_device(card_info->usb_device_type))) {
+            usb_get_best_match_for_sample_rate(&card_info->usb_device_conf_list,
+                                               bw, ch, sr, &sr,
+                                               service_interval,
+                                               true);
+        }
+        break;
+    }
+    if (sr == 0)
+        return -1;
+    *bit_width = bw;
+    *sample_rate = sr;
+    *channel_count = ch;
+    return 0;
+}
+
 void audio_extn_usb_init(void *adev)
 {
     if (usbmod == NULL) {
diff --git a/hal/audio_extn/utils.c b/hal/audio_extn/utils.c
index 12957d5..1751a30 100644
--- a/hal/audio_extn/utils.c
+++ b/hal/audio_extn/utils.c
@@ -24,7 +24,7 @@
 #include <dlfcn.h>
 #include <unistd.h>
 #include <cutils/str_parms.h>
-#include <cutils/log.h>
+#include <log/log.h>
 #include <cutils/misc.h>
 
 #include "acdb.h"
@@ -141,8 +141,6 @@
     snd_device = usecase->out_snd_device;
     pcm_device_id = platform_get_pcm_device_id(usecase->id, PCM_PLAYBACK);
 
-    snd_device = (snd_device == SND_DEVICE_OUT_SPEAKER) ?
-                 audio_extn_get_spkr_prot_snd_device(snd_device) : snd_device;
     acdb_dev_id = platform_get_snd_device_acdb_id(snd_device);
     if (acdb_dev_id < 0) {
         ALOGE("%s: Couldn't get the acdb dev id", __func__);
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 7267931..0a12cff 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -35,7 +35,7 @@
 #include <sys/prctl.h>
 #include <limits.h>
 
-#include <cutils/log.h>
+#include <log/log.h>
 #include <cutils/trace.h>
 #include <cutils/str_parms.h>
 #include <cutils/properties.h>
@@ -51,12 +51,14 @@
 #include <audio_utils/clock.h>
 #include "audio_hw.h"
 #include "audio_extn.h"
+#include "audio_perf.h"
 #include "platform_api.h"
 #include <platform.h>
 #include "voice_extn.h"
 
 #include "sound/compress_params.h"
 #include "audio_extn/tfa_98xx.h"
+#include "audio_extn/maxxaudio.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. */
@@ -72,6 +74,10 @@
 /* treat as unsigned Q1.13 */
 #define VOIP_PLAYBACK_VOLUME_MAX 0x2000
 
+#define RECORD_GAIN_MIN 0.0f
+#define RECORD_GAIN_MAX 1.0f
+#define RECORD_VOLUME_CTL_MAX 0x2000
+
 #define PROXY_OPEN_RETRY_COUNT           100
 #define PROXY_OPEN_WAIT_TIME             20
 
@@ -98,8 +104,6 @@
 #define MMAP_PERIOD_COUNT_MAX 512
 #define MMAP_PERIOD_COUNT_DEFAULT (MMAP_PERIOD_COUNT_MAX)
 
-static const int64_t NANOS_PER_SECOND = 1000000000;
-
 /* This constant enables extended precision handling.
  * TODO The flag is off until more testing is done.
  */
@@ -245,7 +249,7 @@
     .period_count = AFE_PROXY_RECORD_PERIOD_COUNT,
     .format = PCM_FORMAT_S16_LE,
     .start_threshold = AFE_PROXY_RECORD_PERIOD_SIZE,
-    .stop_threshold = INT_MAX,
+    .stop_threshold = AFE_PROXY_RECORD_PERIOD_SIZE * AFE_PROXY_RECORD_PERIOD_COUNT,
     .avail_min = AFE_PROXY_RECORD_PERIOD_SIZE,
 };
 
@@ -286,6 +290,10 @@
 
     [USECASE_AUDIO_PLAYBACK_VOIP] = "audio-playback-voip",
     [USECASE_AUDIO_RECORD_VOIP] = "audio-record-voip",
+
+    [USECASE_INCALL_MUSIC_UPLINK] = "incall-music-uplink",
+
+    [USECASE_AUDIO_A2DP_ABR_FEEDBACK] = "a2dp-abr-feedback",
 };
 
 
@@ -320,6 +328,9 @@
 //cache last MBDRC cal step level
 static int last_known_cal_step = -1 ;
 
+static int check_a2dp_restore_l(struct audio_device *adev, struct stream_out *out, bool restore);
+static int set_compr_volume(struct audio_stream_out *stream, float left, float right);
+
 static bool may_use_noirq_mode(struct audio_device *adev, audio_usecase_t uc_id,
                                int flags __unused)
 {
@@ -471,6 +482,29 @@
     return ret_val;
 }
 
+#ifdef MAXXAUDIO_QDSP_ENABLED
+bool audio_hw_send_ma_parameter(int stream_type, float vol, bool active)
+{
+    bool ret = false;
+    ALOGV("%s: enter ...", __func__);
+
+    pthread_mutex_lock(&adev_init_lock);
+
+    if (adev != NULL && adev->platform != NULL) {
+        pthread_mutex_lock(&adev->lock);
+        ret = audio_extn_ma_set_state(adev, stream_type, vol, active);
+        pthread_mutex_unlock(&adev->lock);
+    }
+
+    pthread_mutex_unlock(&adev_init_lock);
+
+    ALOGV("%s: exit with ret %d", __func__, ret);
+    return ret;
+}
+#else
+#define audio_hw_send_ma_parameter(stream_type, vol, active) (0)
+#endif
+
 __attribute__ ((visibility ("default")))
 int audio_hw_get_gain_level_mapping(struct amp_db_and_gain_table *mapping_tbl,
                                     int table_size) {
@@ -547,6 +581,22 @@
     cfg->gain[0] = cfg->gain[1] = APP_TYPE_GAIN_DEFAULT;
 }
 
+static bool is_btsco_device(snd_device_t out_snd_device, snd_device_t in_snd_device)
+{
+   return out_snd_device == SND_DEVICE_OUT_BT_SCO ||
+          out_snd_device == SND_DEVICE_OUT_BT_SCO_WB ||
+          in_snd_device == SND_DEVICE_IN_BT_SCO_MIC_WB_NREC ||
+          in_snd_device == SND_DEVICE_IN_BT_SCO_MIC_WB ||
+          in_snd_device == SND_DEVICE_IN_BT_SCO_MIC_NREC ||
+          in_snd_device == SND_DEVICE_IN_BT_SCO_MIC;
+
+}
+
+static bool is_a2dp_device(snd_device_t out_snd_device)
+{
+   return out_snd_device == SND_DEVICE_OUT_BT_A2DP;
+}
+
 int enable_audio_route(struct audio_device *adev,
                        struct audio_usecase *usecase)
 {
@@ -562,11 +612,11 @@
         snd_device = usecase->in_snd_device;
     else
         snd_device = usecase->out_snd_device;
-
     audio_extn_utils_send_app_type_cfg(adev, usecase);
     audio_extn_utils_send_audio_calibration(adev, usecase);
     strcpy(mixer_path, use_case_table[usecase->id]);
     platform_add_backend_name(adev->platform, mixer_path, snd_device);
+    audio_extn_sound_trigger_update_stream_status(usecase, ST_EVENT_STREAM_BUSY);
     ALOGD("%s: usecase(%d) apply and update mixer path: %s", __func__,  usecase->id, mixer_path);
     audio_route_apply_and_update_path(adev->audio_route, mixer_path);
 
@@ -592,6 +642,7 @@
     platform_add_backend_name(adev->platform, mixer_path, snd_device);
     ALOGD("%s: usecase(%d) reset and update mixer path: %s", __func__, usecase->id, mixer_path);
     audio_route_reset_and_update_path(adev->audio_route, mixer_path);
+    audio_extn_sound_trigger_update_stream_status(usecase, ST_EVENT_STREAM_FREE);
 
     ALOGV("%s: exit", __func__);
     return 0;
@@ -628,9 +679,11 @@
     audio_extn_dsm_feedback_enable(adev, snd_device, true);
 
     if ((snd_device == SND_DEVICE_OUT_SPEAKER ||
+        snd_device == SND_DEVICE_OUT_SPEAKER_SAFE ||
+        snd_device == SND_DEVICE_OUT_SPEAKER_REVERSE ||
         snd_device == SND_DEVICE_OUT_VOICE_SPEAKER) &&
         audio_extn_spkr_prot_is_enabled()) {
-        if (audio_extn_spkr_prot_get_acdb_id(snd_device) < 0) {
+        if (platform_get_snd_device_acdb_id(snd_device) < 0) {
             goto on_error;
         }
         if (audio_extn_spkr_prot_start_processing(snd_device)) {
@@ -652,6 +705,13 @@
         }
 
         ALOGD("%s: snd_device(%d: %s)", __func__, snd_device, device_name);
+
+        if (is_a2dp_device(snd_device) &&
+            (audio_extn_a2dp_start_playback() < 0)) {
+               ALOGE("%s: failed to configure A2DP control path", __func__);
+               goto on_error;
+        }
+
         audio_route_apply_and_update_path(adev->audio_route, device_name);
     }
 on_success:
@@ -681,7 +741,12 @@
     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);
+
+        if (is_a2dp_device(snd_device))
+            audio_extn_a2dp_stop_playback();
+
         if ((snd_device == SND_DEVICE_OUT_SPEAKER ||
+            snd_device == SND_DEVICE_OUT_SPEAKER_SAFE ||
             snd_device == SND_DEVICE_OUT_SPEAKER_REVERSE ||
             snd_device == SND_DEVICE_OUT_VOICE_SPEAKER) &&
             audio_extn_spkr_prot_is_enabled()) {
@@ -839,6 +904,16 @@
                                                                       uc_info,
                                                                       snd_device);
 
+    /* For a2dp device reconfigure all active sessions
+     * with new AFE encoder format based on a2dp state
+     */
+    if ((SND_DEVICE_OUT_BT_A2DP == snd_device ||
+         SND_DEVICE_OUT_SPEAKER_AND_BT_A2DP == snd_device ||
+         SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_A2DP == snd_device) &&
+         audio_extn_a2dp_is_force_device_switch()) {
+         force_routing = true;
+    }
+
     /*
      * This function is to make sure that all the usecases that are active on
      * the hardware codec backend are always routed to any one device that is
@@ -999,9 +1074,9 @@
     return ret;
 }
 
-static ssize_t read_usb_sup_sample_rates(bool is_playback __unused,
-                                         uint32_t *supported_sample_rates __unused,
-                                         uint32_t max_rates __unused)
+static ssize_t read_usb_sup_sample_rates(bool is_playback,
+                                         uint32_t *supported_sample_rates,
+                                         uint32_t max_rates)
 {
     ssize_t count = audio_extn_usb_sup_sample_rates(is_playback,
                                                     supported_sample_rates,
@@ -1026,14 +1101,16 @@
         channels = MAX_HIFI_CHANNEL_COUNT;
     }
     if (is_playback) {
-        // For playback we never report mono because the framework always outputs stereo
-        channel_count = DEFAULT_CHANNEL_COUNT;
-        // audio_channel_out_mask_from_count() does return positional masks for channel counts
-        // above 2 but we want indexed masks here. So we
-        for ( ; channel_count <= channels && num_masks < max_masks; channel_count++) {
+        // start from 2 channels as framework currently doesn't support mono.
+        // TODO: consider only supporting channel index masks beyond stereo here.
+        for (channel_count = FCC_2;
+                channel_count <= channels && num_masks < max_masks;
+                ++channel_count) {
             supported_channel_masks[num_masks++] = audio_channel_out_mask_from_count(channel_count);
         }
-        for ( ; channel_count <= channels && num_masks < max_masks; channel_count++) {
+        for (channel_count = FCC_2;
+                channel_count <= channels && num_masks < max_masks;
+                ++channel_count) {
             supported_channel_masks[num_masks++] =
                     audio_channel_mask_for_index_assignment_from_count(channel_count);
         }
@@ -1047,8 +1124,12 @@
                     audio_channel_in_mask_from_count(channel_count);
         }
     }
-    ALOGV("%s: %s supported ch %d supported_channel_masks[0] %08x num_masks %d", __func__,
-          is_playback ? "P" : "C", channels, supported_channel_masks[0], num_masks);
+#ifdef NDEBUG
+    for (size_t i = 0; i < num_masks; ++i) {
+        ALOGV("%s: %s supported ch %d supported_channel_masks[%zu] %08x num_masks %d", __func__,
+              is_playback ? "P" : "C", channels, i, supported_channel_masks[i], num_masks);
+    }
+#endif
     return num_masks;
 }
 
@@ -1156,6 +1237,25 @@
     return NULL;
 }
 
+static bool force_device_switch(struct audio_usecase *usecase)
+{
+    if (usecase->stream.out == NULL) {
+        ALOGE("%s: stream.out is NULL", __func__);
+        return false;
+    }
+
+    // Force all A2DP output devices to reconfigure for proper AFE encode format
+    // Also handle a case where in earlier A2DP start failed as A2DP stream was
+    // in suspended state, hence try to trigger a retry when we again get a routing request.
+    if ((usecase->stream.out->devices & AUDIO_DEVICE_OUT_ALL_A2DP) &&
+        audio_extn_a2dp_is_force_device_switch()) {
+         ALOGD("%s: Force A2DP device switch to update new encoder config", __func__);
+         return true;
+    }
+
+    return false;
+}
+
 int select_devices(struct audio_device *adev,
                    audio_usecase_t uc_id)
 {
@@ -1254,7 +1354,23 @@
 
     if (out_snd_device == usecase->out_snd_device &&
         in_snd_device == usecase->in_snd_device) {
-        return 0;
+        if (!force_device_switch(usecase))
+            return 0;
+    }
+
+    if (is_a2dp_device(out_snd_device) && !audio_extn_a2dp_is_ready()) {
+          ALOGD("SCO/A2DP is selected but they are not connected/ready hence dont route");
+          return 0;
+    }
+
+    if ((out_snd_device == SND_DEVICE_OUT_SPEAKER_AND_BT_A2DP ||
+         out_snd_device == SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_A2DP) &&
+        (!audio_extn_a2dp_is_ready())) {
+        ALOGW("%s: A2DP profile is not ready, routing to speaker only", __func__);
+        if (out_snd_device == SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_A2DP)
+            out_snd_device = SND_DEVICE_OUT_SPEAKER_SAFE;
+        else
+            out_snd_device = SND_DEVICE_OUT_SPEAKER;
     }
 
     if (out_snd_device != SND_DEVICE_NONE &&
@@ -1328,7 +1444,8 @@
     /* Enable new sound devices */
     if (out_snd_device != SND_DEVICE_NONE) {
         if ((usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND) ||
-            (usecase->devices & (AUDIO_DEVICE_OUT_USB_DEVICE|AUDIO_DEVICE_OUT_USB_HEADSET)))
+            (usecase->devices & (AUDIO_DEVICE_OUT_USB_DEVICE|AUDIO_DEVICE_OUT_USB_HEADSET)) ||
+            (usecase->devices & AUDIO_DEVICE_OUT_ALL_A2DP))
             check_and_route_playback_usecases(adev, usecase, out_snd_device);
         enable_snd_device(adev, out_snd_device);
     }
@@ -1350,6 +1467,8 @@
 
     enable_audio_route(adev, usecase);
 
+    audio_extn_ma_set_device(usecase);
+
     /* Applicable only on the targets that has external modem.
      * Enable device command should be sent to modem only after
      * enabling voice call mixer controls
@@ -1460,6 +1579,7 @@
 
     list_add_tail(&adev->usecase_list, &uc_info->list);
 
+    audio_streaming_hint_start();
     audio_extn_perf_lock_acquire();
 
     select_devices(adev, in->usecase);
@@ -1526,6 +1646,7 @@
         }
     }
     register_in_stream(in);
+    audio_streaming_hint_end();
     audio_extn_perf_lock_release();
     ALOGV("%s: exit", __func__);
 
@@ -1533,6 +1654,7 @@
 
 error_open:
     stop_input_stream(in);
+    audio_streaming_hint_end();
     audio_extn_perf_lock_release();
 
 error_config:
@@ -1632,7 +1754,7 @@
         out->offload_thread_blocked = true;
         pthread_mutex_unlock(&out->lock);
         send_callback = false;
-        switch(cmd->cmd) {
+        switch (cmd->cmd) {
         case OFFLOAD_CMD_WAIT_FOR_BUFFER:
             compress_wait(out->compr, -1);
             send_callback = true;
@@ -1779,6 +1901,54 @@
     return 0;
 }
 
+static int check_and_set_usb_service_interval(struct audio_device *adev,
+                                              struct audio_usecase *uc_info,
+                                              bool min)
+{
+    struct listnode *node;
+    struct audio_usecase *usecase;
+    bool switch_usecases = false;
+    bool reconfig = false;
+
+    if ((uc_info->id != USECASE_AUDIO_PLAYBACK_MMAP) &&
+        (uc_info->id != USECASE_AUDIO_PLAYBACK_ULL))
+        return -1;
+
+    /* set if the valid usecase do not already exist */
+    list_for_each(node, &adev->usecase_list) {
+        usecase = node_to_item(node, struct audio_usecase, list);
+        if (usecase->type == PCM_PLAYBACK &&
+            (audio_is_usb_out_device(usecase->devices & AUDIO_DEVICE_OUT_ALL_USB))) {
+            switch (usecase->id) {
+                case USECASE_AUDIO_PLAYBACK_MMAP:
+                case USECASE_AUDIO_PLAYBACK_ULL:
+                    // cannot reconfig while mmap/ull is present.
+                    return -1;
+                default:
+                    switch_usecases = true;
+                    break;
+            }
+        }
+        if (switch_usecases)
+            break;
+    }
+    /*
+     * client can try to set service interval in start_output_stream
+     * to min or to 0 (i.e reset) in stop_output_stream .
+     */
+    unsigned long service_interval =
+            audio_extn_usb_find_service_interval(min, true /*playback*/);
+    int ret = platform_set_usb_service_interval(adev->platform,
+                                                true /*playback*/,
+                                                service_interval,
+                                                &reconfig);
+    /* no change or not supported or no active usecases */
+    if (ret || !reconfig || !switch_usecases)
+        return -1;
+    return 0;
+#undef VALID_USECASE
+}
+
 static int stop_output_stream(struct stream_out *out)
 {
     int i, ret = 0;
@@ -1799,6 +1969,9 @@
             adev->visualizer_stop_output(out->handle, out->pcm_device_id);
         if (adev->offload_effects_stop_output != NULL)
             adev->offload_effects_stop_output(out->handle, out->pcm_device_id);
+    } else if (out->usecase == USECASE_AUDIO_PLAYBACK_ULL ||
+               out->usecase == USECASE_AUDIO_PLAYBACK_MMAP) {
+        audio_low_latency_hint_end();
     }
 
     /* 1. Get and set stream specific mixer controls */
@@ -1808,7 +1981,6 @@
     disable_snd_device(adev, uc_info->out_snd_device);
 
     list_remove(&uc_info->list);
-    free(uc_info);
 
     audio_extn_extspk_update(adev->extspk);
 
@@ -1823,8 +1995,17 @@
             if (usecase->devices & AUDIO_DEVICE_OUT_SPEAKER)
                 select_devices(adev, usecase->id);
         }
+    } else if (audio_is_usb_out_device(out->devices & AUDIO_DEVICE_OUT_ALL_USB)) {
+        ret = check_and_set_usb_service_interval(adev, uc_info, false /*min*/);
+        if (ret == 0) {
+            /* default service interval was successfully updated,
+               reopen USB backend with new service interval */
+            check_and_route_playback_usecases(adev, uc_info, uc_info->out_snd_device);
+        }
+        ret = 0;
     }
 
+    free(uc_info);
     ALOGV("%s: exit: status(%d)", __func__, ret);
     return ret;
 }
@@ -1834,6 +2015,7 @@
     int ret = 0;
     struct audio_usecase *uc_info;
     struct audio_device *adev = out->dev;
+    bool a2dp_combo = false;
 
     ALOGV("%s: enter: usecase(%d: %s) devices(%#x)",
           __func__, out->usecase, use_case_table[out->usecase], out->devices);
@@ -1845,6 +2027,19 @@
         goto error_config;
     }
 
+    if (out->devices & AUDIO_DEVICE_OUT_ALL_A2DP) {
+        if (!audio_extn_a2dp_is_ready()) {
+            if (out->devices & (AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_SPEAKER_SAFE)) {
+                a2dp_combo = true;
+            } else {
+                if (!(out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)) {
+                    ALOGE("%s: A2DP profile is not ready, return error", __func__);
+                    ret = -EAGAIN;
+                    goto error_config;
+                }
+            }
+        }
+    }
     out->pcm_device_id = platform_get_pcm_device_id(out->usecase, PCM_PLAYBACK);
     if (out->pcm_device_id < 0) {
         ALOGE("%s: Invalid PCM device id(%d) for the usecase(%d)",
@@ -1864,12 +2059,33 @@
     /* This must be called before adding this usecase to the list */
     if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL)
         check_and_set_hdmi_channels(adev, out->config.channels);
+    else if (audio_is_usb_out_device(out->devices & AUDIO_DEVICE_OUT_ALL_USB)) {
+        check_and_set_usb_service_interval(adev, uc_info, true /*min*/);
+        /* USB backend is not reopened immediately.
+           This is eventually done as part of select_devices */
+    }
 
     list_add_tail(&adev->usecase_list, &uc_info->list);
 
+    audio_streaming_hint_start();
     audio_extn_perf_lock_acquire();
 
-    select_devices(adev, out->usecase);
+    if ((out->devices & AUDIO_DEVICE_OUT_ALL_A2DP) &&
+        (!audio_extn_a2dp_is_ready())) {
+        if (!a2dp_combo) {
+            check_a2dp_restore_l(adev, out, false);
+        } else {
+            audio_devices_t dev = out->devices;
+            if (dev & AUDIO_DEVICE_OUT_SPEAKER_SAFE)
+                out->devices = AUDIO_DEVICE_OUT_SPEAKER_SAFE;
+            else
+                out->devices = AUDIO_DEVICE_OUT_SPEAKER;
+            select_devices(adev, out->usecase);
+            out->devices = dev;
+        }
+    } else {
+         select_devices(adev, out->usecase);
+    }
 
     audio_extn_extspk_update(adev->extspk);
 
@@ -1953,9 +2169,15 @@
         }
     }
     register_out_stream(out);
+    audio_streaming_hint_end();
     audio_extn_perf_lock_release();
     audio_extn_tfa_98xx_enable_speaker();
 
+    if (out->usecase == USECASE_AUDIO_PLAYBACK_ULL ||
+        out->usecase == USECASE_AUDIO_PLAYBACK_MMAP) {
+        audio_low_latency_hint_start();
+    }
+
     // consider a scenario where on pause lower layers are tear down.
     // so on resume, swap mixer control need to be sent only when
     // backend is active, hence rather than sending from enable device
@@ -1966,6 +2188,7 @@
     ALOGV("%s: exit", __func__);
     return 0;
 error_open:
+    audio_streaming_hint_end();
     audio_extn_perf_lock_release();
     stop_output_stream(out);
 error_config:
@@ -2011,6 +2234,50 @@
     return 0;
 }
 
+/** Add a value in a list if not already present.
+ * @return true if value was successfully inserted or already present,
+ *         false if the list is full and does not contain the value.
+ */
+static bool register_uint(uint32_t value, uint32_t* list, size_t list_length) {
+    for (size_t i = 0; i < list_length; i++) {
+        if (list[i] == value) return true; // value is already present
+        if (list[i] == 0) { // no values in this slot
+            list[i] = value;
+            return true; // value inserted
+        }
+    }
+    return false; // could not insert value
+}
+
+/** Add channel_mask in supported_channel_masks if not already present.
+ * @return true if channel_mask was successfully inserted or already present,
+ *         false if supported_channel_masks is full and does not contain channel_mask.
+ */
+static void register_channel_mask(audio_channel_mask_t channel_mask,
+            audio_channel_mask_t supported_channel_masks[static MAX_SUPPORTED_CHANNEL_MASKS]) {
+    ALOGE_IF(!register_uint(channel_mask, supported_channel_masks, MAX_SUPPORTED_CHANNEL_MASKS),
+        "%s: stream can not declare supporting its channel_mask %x", __func__, channel_mask);
+}
+
+/** Add format in supported_formats if not already present.
+ * @return true if format was successfully inserted or already present,
+ *         false if supported_formats is full and does not contain format.
+ */
+static void register_format(audio_format_t format,
+            audio_format_t supported_formats[static MAX_SUPPORTED_FORMATS]) {
+    ALOGE_IF(!register_uint(format, supported_formats, MAX_SUPPORTED_FORMATS),
+             "%s: stream can not declare supporting its format %x", __func__, format);
+}
+/** Add sample_rate in supported_sample_rates if not already present.
+ * @return true if sample_rate was successfully inserted or already present,
+ *         false if supported_sample_rates is full and does not contain sample_rate.
+ */
+static void register_sample_rate(uint32_t sample_rate,
+            uint32_t supported_sample_rates[static MAX_SUPPORTED_SAMPLE_RATES]) {
+    ALOGE_IF(!register_uint(sample_rate, supported_sample_rates, MAX_SUPPORTED_SAMPLE_RATES),
+             "%s: stream can not declare supporting its sample rate %x", __func__, sample_rate);
+}
+
 static size_t get_stream_buffer_size(size_t duration_ms,
                                      uint32_t sample_rate,
                                      audio_format_t format,
@@ -2231,6 +2498,7 @@
     int ret, val = 0;
     bool select_new_device = false;
     int status = 0;
+    bool bypass_a2dp = false;
 
     ALOGD("%s: enter: usecase(%d: %s) kvpairs: %s",
           __func__, out->usecase, use_case_table[out->usecase], kvpairs);
@@ -2265,6 +2533,43 @@
             val = AUDIO_DEVICE_OUT_SPEAKER;
         }
 
+        /*
+         * When A2DP is disconnected the
+         * music playback is paused and the policy manager sends routing=0
+         * But the audioflingercontinues to write data until standby time
+         * (3sec). As BT is turned off, the write gets blocked.
+         * Avoid this by routing audio to speaker until standby.
+         */
+        if ((out->devices & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP) &&
+                (val == AUDIO_DEVICE_NONE) &&
+                !audio_extn_a2dp_is_ready()) {
+                val = AUDIO_DEVICE_OUT_SPEAKER;
+        }
+
+        /* To avoid a2dp to sco overlapping / BT device improper state
+         * check with BT lib about a2dp streaming support before routing
+         */
+        if (val & AUDIO_DEVICE_OUT_ALL_A2DP) {
+            if (!audio_extn_a2dp_is_ready()) {
+                if (val & (AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_SPEAKER_SAFE)) {
+                    //combo usecase just by pass a2dp
+                    ALOGW("%s: A2DP profile is not ready,routing to speaker only", __func__);
+                    bypass_a2dp = true;
+                } else {
+                    ALOGE("%s: A2DP profile is not ready,ignoring routing request", __func__);
+                    /* update device to a2dp and don't route as BT returned error
+                     * However it is still possible a2dp routing called because
+                     * of current active device disconnection (like wired headset)
+                     */
+                    out->devices = val;
+                    pthread_mutex_unlock(&out->lock);
+                    pthread_mutex_unlock(&adev->lock);
+                    status = -ENOSYS;
+                    goto routing_fail;
+                }
+            }
+        }
+
         audio_devices_t new_dev = val;
 
         // Workaround: If routing to an non existing usb device, fail gracefully
@@ -2303,7 +2608,7 @@
             out->devices = new_dev;
 
             if (output_drives_call(adev, out)) {
-                if (!voice_is_in_call(adev)) {
+                if (!voice_is_call_state_active(adev)) {
                     if (adev->mode == AUDIO_MODE_IN_CALL) {
                         adev->current_call_output = out;
                         ret = voice_start_call(adev);
@@ -2323,7 +2628,16 @@
                                                     out->handle);
                     }
                 }
-                select_devices(adev, out->usecase);
+                if (!bypass_a2dp) {
+                    select_devices(adev, out->usecase);
+                } else {
+                    if (new_dev & AUDIO_DEVICE_OUT_SPEAKER_SAFE)
+                        out->devices = AUDIO_DEVICE_OUT_SPEAKER_SAFE;
+                    else
+                        out->devices = AUDIO_DEVICE_OUT_SPEAKER;
+                    select_devices(adev, out->usecase);
+                    out->devices = new_dev;
+                }
                 audio_extn_tfa_98xx_update();
 
                 // on device switch force swap, lower functions will make sure
@@ -2331,6 +2645,15 @@
 
                 if (!same_dev)
                     platform_set_swap_channels(adev, true);
+
+                if ((out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) &&
+                    out->a2dp_compress_mute &&
+                    (!(out->devices & AUDIO_DEVICE_OUT_ALL_A2DP) || audio_extn_a2dp_is_ready())) {
+                    pthread_mutex_lock(&out->compr_mute_lock);
+                    out->a2dp_compress_mute = false;
+                    set_compr_volume(&out->stream, out->volume_l, out->volume_r);
+                    pthread_mutex_unlock(&out->compr_mute_lock);
+                }
             }
 
         }
@@ -2479,6 +2802,7 @@
 {
     uint32_t hw_delay, period_ms;
     struct stream_out *out = (struct stream_out *)stream;
+    uint32_t latency;
 
     if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD)
         return COMPRESS_OFFLOAD_PLAYBACK_LATENCY;
@@ -2492,42 +2816,62 @@
         return period_ms + hw_delay;
     }
 
-    return (out->config.period_count * out->config.period_size * 1000) /
-           (out->config.rate);
+    latency = (out->config.period_count * out->config.period_size * 1000) /
+              (out->config.rate);
+
+    if (AUDIO_DEVICE_OUT_ALL_A2DP & out->devices)
+        latency += audio_extn_a2dp_get_encoder_latency();
+
+    return latency;
+}
+
+static int set_compr_volume(struct audio_stream_out *stream, float left,
+                          float right)
+{
+    struct stream_out *out = (struct stream_out *)stream;
+    int volume[2];
+    char mixer_ctl_name[128];
+    struct audio_device *adev = out->dev;
+    struct mixer_ctl *ctl;
+    int pcm_device_id = platform_get_pcm_device_id(out->usecase,
+                                               PCM_PLAYBACK);
+
+    snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
+             "Compress 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;
+    }
+    ALOGV("%s: ctl for mixer cmd - %s, left %f, right %f",
+           __func__, mixer_ctl_name, left, right);
+    volume[0] = (int)(left * COMPRESS_PLAYBACK_VOLUME_MAX);
+    volume[1] = (int)(right * COMPRESS_PLAYBACK_VOLUME_MAX);
+    mixer_ctl_set_array(ctl, volume, sizeof(volume) / sizeof(volume[0]));
+
+    return 0;
 }
 
 static int out_set_volume(struct audio_stream_out *stream, float left,
                           float right)
 {
     struct stream_out *out = (struct stream_out *)stream;
-    int volume[2];
+    int ret = 0;
 
     if (out->usecase == USECASE_AUDIO_PLAYBACK_HIFI) {
         /* only take left channel into account: the API is for stereo anyway */
         out->muted = (left == 0.0f);
         return 0;
     } else if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD) {
-        const char *mixer_ctl_name = "Compress Playback Volume";
-        struct audio_device *adev = out->dev;
-        struct mixer_ctl *ctl;
-        ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
-        if (!ctl) {
-            /* try with the control based on device id */
-            int pcm_device_id = platform_get_pcm_device_id(out->usecase,
-                                                       PCM_PLAYBACK);
-            char ctl_name[128] = {0};
-            snprintf(ctl_name, sizeof(ctl_name),
-                     "Compress Playback %d Volume", pcm_device_id);
-            ctl = mixer_get_ctl_by_name(adev->mixer, ctl_name);
-            if (!ctl) {
-                ALOGE("%s: Could not get volume ctl mixer cmd", __func__);
-                return -EINVAL;
-            }
-        }
-        volume[0] = (int)(left * COMPRESS_PLAYBACK_VOLUME_MAX);
-        volume[1] = (int)(right * COMPRESS_PLAYBACK_VOLUME_MAX);
-        mixer_ctl_set_array(ctl, volume, sizeof(volume)/sizeof(volume[0]));
-        return 0;
+        pthread_mutex_lock(&out->compr_mute_lock);
+        ALOGV("%s: compress mute %d", __func__, out->a2dp_compress_mute);
+        if (!out->a2dp_compress_mute)
+            ret = set_compr_volume(stream, left, right);
+        out->volume_l = left;
+        out->volume_r = right;
+        pthread_mutex_unlock(&out->compr_mute_lock);
+        return ret;
     } else if (out->usecase == USECASE_AUDIO_PLAYBACK_VOIP) {
         out->app_type_cfg.gain[0] = (int)(left * VOIP_PLAYBACK_VOLUME_MAX);
         out->app_type_cfg.gain[1] = (int)(right * VOIP_PLAYBACK_VOLUME_MAX);
@@ -2615,6 +2959,17 @@
         error_code = ERROR_CODE_WRITE;
         goto exit;
     }
+
+    if ((out->devices & AUDIO_DEVICE_OUT_ALL_A2DP) &&
+        (audio_extn_a2dp_is_suspended())) {
+        if (!(out->devices & (AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_SPEAKER_SAFE))) {
+            if (!(out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)) {
+                ret = -EIO;
+                goto exit;
+            }
+        }
+    }
+
     if (out->standby) {
         out->standby = false;
         pthread_mutex_lock(&adev->lock);
@@ -2683,7 +3038,7 @@
             if (out->muted)
                 memset((void *)buffer, 0, bytes);
             // FIXME: this can be removed once audio flinger mixer supports mono output
-            if (out->usecase == USECASE_AUDIO_PLAYBACK_VOIP) {
+            if (out->usecase == USECASE_AUDIO_PLAYBACK_VOIP || out->usecase == USECASE_INCALL_MUSIC_UPLINK) {
                 size_t channel_count = audio_channel_count_from_out_mask(out->channel_mask);
                 int16_t *src = (int16_t *)buffer;
                 int16_t *dst = (int16_t *)buffer;
@@ -2699,7 +3054,7 @@
             }
             ALOGVV("%s: writing buffer (%zu bytes) to pcm device", __func__, bytes_to_write);
 
-            long ns = (frames * NANOS_PER_SECOND) / out->config.rate;
+            long ns = (frames * (int64_t) NANOS_PER_SECOND) / out->config.rate;
             request_out_focus(out, ns);
 
             bool use_mmap = is_mmap_usecase(out->usecase) || out->realtime;
@@ -2794,6 +3149,13 @@
             // TODO: check return value
             compress_get_tstamp(out->compr, &dsp_frames,
                     &out->sample_rate);
+            // Adjustment accounts for A2DP encoder latency with offload usecases
+            // Note: Encoder latency is returned in ms.
+            if (AUDIO_DEVICE_OUT_ALL_A2DP & out->devices) {
+                unsigned long offset =
+                            (audio_extn_a2dp_get_encoder_latency() * out->sample_rate / 1000);
+                dsp_frames = (dsp_frames > offset) ? (dsp_frames - offset) : 0;
+            }
             ALOGVV("%s rendered frames %ld sample_rate %d",
                    __func__, dsp_frames, out->sample_rate);
             *frames = dsp_frames;
@@ -2812,6 +3174,13 @@
                 signed_frames -=
                     (platform_render_latency(out->usecase) * out->sample_rate / 1000000LL);
 
+                // Adjustment accounts for A2DP encoder latency with non-offload usecases
+                // Note: Encoder latency is returned in ms, while platform_render_latency in us.
+                if (AUDIO_DEVICE_OUT_ALL_A2DP & out->devices) {
+                    signed_frames -=
+                            (audio_extn_a2dp_get_encoder_latency() * out->sample_rate / 1000);
+                }
+
                 // It would be unusual for this value to be negative, but check just in case ...
                 if (signed_frames >= 0) {
                     *frames = signed_frames;
@@ -2972,6 +3341,7 @@
     uint32_t buffer_size;
 
     ALOGV("%s", __func__);
+    lock_output_stream(out);
     pthread_mutex_lock(&adev->lock);
 
     if (info == NULL || min_size_frames == 0) {
@@ -3051,32 +3421,36 @@
         }
     }
     pthread_mutex_unlock(&adev->lock);
+    pthread_mutex_unlock(&out->lock);
     return ret;
 }
 
 static int out_get_mmap_position(const struct audio_stream_out *stream,
                                   struct audio_mmap_position *position)
 {
+    int ret = 0;
     struct stream_out *out = (struct stream_out *)stream;
     ALOGVV("%s", __func__);
     if (position == NULL) {
         return -EINVAL;
     }
-    if (out->usecase != USECASE_AUDIO_PLAYBACK_MMAP) {
-        return -ENOSYS;
-    }
-    if (out->pcm == NULL) {
-        return -ENOSYS;
+    lock_output_stream(out);
+    if (out->usecase != USECASE_AUDIO_PLAYBACK_MMAP ||
+        out->pcm == NULL) {
+        ret = -ENOSYS;
+        goto exit;
     }
 
     struct timespec ts = { 0, 0 };
-    int ret = pcm_mmap_get_hw_ptr(out->pcm, (unsigned int *)&position->position_frames, &ts);
+    ret = pcm_mmap_get_hw_ptr(out->pcm, (unsigned int *)&position->position_frames, &ts);
     if (ret < 0) {
         ALOGE("%s: %s", __func__, pcm_get_error(out->pcm));
-        return ret;
+        goto exit;
     }
     position->time_nanoseconds = audio_utils_ns_from_timespec(&ts);
-    return 0;
+exit:
+    pthread_mutex_unlock(&out->lock);
+    return ret;
 }
 
 
@@ -3275,9 +3649,39 @@
     return str;
 }
 
-static int in_set_gain(struct audio_stream_in *stream __unused, float gain __unused)
+static int in_set_gain(struct audio_stream_in *stream, float gain)
 {
-    return -ENOSYS;
+    struct stream_in *in = (struct stream_in *)stream;
+    char mixer_ctl_name[128];
+    struct mixer_ctl *ctl;
+    int ctl_value;
+
+    ALOGV("%s: gain %f", __func__, gain);
+
+    if (stream == NULL)
+        return -EINVAL;
+
+    /* in_set_gain() only used to silence MMAP capture for now */
+    if (in->usecase != USECASE_AUDIO_RECORD_MMAP)
+        return -ENOSYS;
+
+    snprintf(mixer_ctl_name, sizeof(mixer_ctl_name), "Capture %d Volume", in->pcm_device_id);
+
+    ctl = mixer_get_ctl_by_name(in->dev->mixer, mixer_ctl_name);
+    if (!ctl) {
+        ALOGW("%s: Could not get ctl for mixer cmd - %s",
+              __func__, mixer_ctl_name);
+        return -ENOSYS;
+    }
+
+    if (gain < RECORD_GAIN_MIN)
+        gain  = RECORD_GAIN_MIN;
+    else if (gain > RECORD_GAIN_MAX)
+         gain = RECORD_GAIN_MAX;
+    ctl_value = (int)(RECORD_VOLUME_CTL_MAX * gain);
+
+    mixer_ctl_set_value(ctl, 0, ctl_value);
+    return 0;
 }
 
 static void in_snd_mon_cb(void * stream, struct str_parms * parms)
@@ -3394,7 +3798,9 @@
      * to always provide zeroes when muted.
      * No need to acquire adev->lock to read mic_muted here as we don't change its state.
      */
-    if (ret == 0 && adev->mic_muted && in->usecase != USECASE_AUDIO_RECORD_AFE_PROXY) {
+    if (ret == 0 && adev->mic_muted &&
+        !voice_is_in_call_rec_stream(in) &&
+        in->usecase != USECASE_AUDIO_RECORD_AFE_PROXY) {
         memset(buffer, 0, bytes);
         in->frames_muted += frames;
     }
@@ -3431,6 +3837,14 @@
     int ret = -ENOSYS;
 
     lock_input_stream(in);
+    // note: ST sessions do not close the alsa pcm driver synchronously
+    // on standby. Therefore, we may return an error even though the
+    // pcm stream is still opened.
+    if (in->standby) {
+        ALOGE_IF(in->pcm != NULL && !in->is_st_session,
+                 "%s stream in standby but pcm not NULL for non ST session", __func__);
+        goto exit;
+    }
     if (in->pcm) {
         struct timespec timestamp;
         unsigned int avail;
@@ -3440,6 +3854,7 @@
             ret = 0;
         }
     }
+exit:
     pthread_mutex_unlock(&in->lock);
     return ret;
 }
@@ -3559,6 +3974,7 @@
     uint32_t mmap_size;
     uint32_t buffer_size;
 
+    lock_input_stream(in);
     pthread_mutex_lock(&adev->lock);
     ALOGV("%s in %p", __func__, in);
 
@@ -3642,33 +4058,67 @@
         }
     }
     pthread_mutex_unlock(&adev->lock);
+    pthread_mutex_unlock(&in->lock);
     return ret;
 }
 
 static int in_get_mmap_position(const struct audio_stream_in *stream,
                                   struct audio_mmap_position *position)
 {
+    int ret = 0;
     struct stream_in *in = (struct stream_in *)stream;
     ALOGVV("%s", __func__);
     if (position == NULL) {
         return -EINVAL;
     }
-    if (in->usecase != USECASE_AUDIO_RECORD_MMAP) {
-        return -ENOSYS;
-    }
-    if (in->pcm == NULL) {
-        return -ENOSYS;
+    lock_input_stream(in);
+    if (in->usecase != USECASE_AUDIO_RECORD_MMAP ||
+        in->pcm == NULL) {
+        ret = -ENOSYS;
+        goto exit;
     }
     struct timespec ts = { 0, 0 };
-    int ret = pcm_mmap_get_hw_ptr(in->pcm, (unsigned int *)&position->position_frames, &ts);
+    ret = pcm_mmap_get_hw_ptr(in->pcm, (unsigned int *)&position->position_frames, &ts);
     if (ret < 0) {
         ALOGE("%s: %s", __func__, pcm_get_error(in->pcm));
-        return ret;
+        goto exit;
     }
     position->time_nanoseconds = audio_utils_ns_from_timespec(&ts);
-    return 0;
+exit:
+    pthread_mutex_unlock(&in->lock);
+    return ret;
 }
 
+static int in_get_active_microphones(const struct audio_stream_in *stream,
+                                     struct audio_microphone_characteristic_t *mic_array,
+                                     size_t *mic_count) {
+    struct stream_in *in = (struct stream_in *)stream;
+    struct audio_device *adev = in->dev;
+    ALOGVV("%s", __func__);
+
+    lock_input_stream(in);
+    pthread_mutex_lock(&adev->lock);
+    int ret = platform_get_active_microphones(adev->platform,
+                                              audio_channel_count_from_in_mask(in->channel_mask),
+                                              in->usecase, mic_array, mic_count);
+    pthread_mutex_unlock(&adev->lock);
+    pthread_mutex_unlock(&in->lock);
+
+    return ret;
+}
+
+static int adev_get_microphones(const struct audio_hw_device *dev,
+                                struct audio_microphone_characteristic_t *mic_array,
+                                size_t *mic_count) {
+    struct audio_device *adev = (struct audio_device *)dev;
+    ALOGVV("%s", __func__);
+
+    pthread_mutex_lock(&adev->lock);
+    int ret = platform_get_microphones(adev->platform, mic_array, mic_count);
+    pthread_mutex_unlock(&adev->lock);
+
+    return ret;
+}
 
 static int adev_open_output_stream(struct audio_hw_device *dev,
                                    audio_io_handle_t handle,
@@ -3680,43 +4130,46 @@
 {
     struct audio_device *adev = (struct audio_device *)dev;
     struct stream_out *out;
-    int i, ret;
+    int i, ret = 0;
     bool is_hdmi = devices & AUDIO_DEVICE_OUT_AUX_DIGITAL;
     bool is_usb_dev = audio_is_usb_out_device(devices) &&
                       (devices != AUDIO_DEVICE_OUT_USB_ACCESSORY);
-    bool direct_dev = is_hdmi || is_usb_dev;
 
     if (is_usb_dev && !is_usb_ready(adev, true /* is_playback */)) {
         return -ENOSYS;
     }
 
-    ALOGV("%s: enter: sample_rate(%d) channel_mask(%#x) devices(%#x) flags(%#x)",
-          __func__, config->sample_rate, config->channel_mask, devices, flags);
+    ALOGV("%s: enter: format(%#x) sample_rate(%d) channel_mask(%#x) devices(%#x) flags(%#x)",
+          __func__, config->format, config->sample_rate, config->channel_mask, devices, flags);
     *stream_out = NULL;
     out = (struct stream_out *)calloc(1, sizeof(struct stream_out));
 
+    pthread_mutex_init(&out->compr_mute_lock, (const pthread_mutexattr_t *) NULL);
+
     if (devices == AUDIO_DEVICE_NONE)
         devices = AUDIO_DEVICE_OUT_SPEAKER;
 
     out->flags = flags;
     out->devices = devices;
     out->dev = adev;
-    out->format = config->format;
-    out->sample_rate = config->sample_rate;
-    out->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
-    out->supported_channel_masks[0] = AUDIO_CHANNEL_OUT_STEREO;
     out->handle = handle;
+    out->a2dp_compress_mute = false;
 
     /* Init use case and pcm_config */
-    if (audio_is_linear_pcm(out->format) &&
-        (out->flags == AUDIO_OUTPUT_FLAG_NONE ||
-         out->flags == AUDIO_OUTPUT_FLAG_DIRECT) && direct_dev) {
+    if ((is_hdmi || is_usb_dev) &&
+        (audio_is_linear_pcm(config->format) || config->format == AUDIO_FORMAT_DEFAULT) &&
+        (flags == AUDIO_OUTPUT_FLAG_NONE ||
+        (flags & AUDIO_OUTPUT_FLAG_DIRECT) != 0)) {
+        audio_format_t req_format = config->format;
+        audio_channel_mask_t req_channel_mask = config->channel_mask;
+        uint32_t req_sample_rate = config->sample_rate;
+
         pthread_mutex_lock(&adev->lock);
         if (is_hdmi) {
             ret = read_hdmi_channel_masks(out);
             if (config->sample_rate == 0)
                 config->sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
-            if (config->channel_mask == 0)
+            if (config->channel_mask == AUDIO_CHANNEL_NONE)
                 config->channel_mask = AUDIO_CHANNEL_OUT_5POINT1;
             if (config->format == AUDIO_FORMAT_DEFAULT)
                 config->format = AUDIO_FORMAT_PCM_16_BIT;
@@ -3732,25 +4185,47 @@
                                                   &out->supported_sample_rates[0],
                                                   MAX_SUPPORTED_SAMPLE_RATES);
             ALOGV("plugged dev USB ret %d", ret);
-        } else {
-            ret = -1;
         }
         pthread_mutex_unlock(&adev->lock);
-        if (ret != 0)
-            goto error_open;
+        if (ret != 0) {
+            // For MMAP NO IRQ, allow conversions in ADSP
+            if (is_hdmi || (flags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ) == 0)
+                goto error_open;
 
-        out->channel_mask = config->channel_mask;
+            if (req_sample_rate != 0 && config->sample_rate != req_sample_rate)
+                config->sample_rate = req_sample_rate;
+            if (req_channel_mask != AUDIO_CHANNEL_NONE && config->channel_mask != req_channel_mask)
+                config->channel_mask = req_channel_mask;
+            if (req_format != AUDIO_FORMAT_DEFAULT && config->format != req_format)
+                config->format = req_format;
+        }
+
         out->sample_rate = config->sample_rate;
+        out->channel_mask = config->channel_mask;
         out->format = config->format;
-        out->usecase = USECASE_AUDIO_PLAYBACK_HIFI;
-        // does this change?
-        out->config = is_hdmi ? pcm_config_hdmi_multi : pcm_config_hifi;
-        out->config.rate = config->sample_rate;
+        if (is_hdmi) {
+            out->usecase = USECASE_AUDIO_PLAYBACK_HIFI;
+            out->config = pcm_config_hdmi_multi;
+        } else if (flags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ) {
+            out->usecase = USECASE_AUDIO_PLAYBACK_MMAP;
+            out->config = pcm_config_mmap_playback;
+            out->stream.start = out_start;
+            out->stream.stop = out_stop;
+            out->stream.create_mmap_buffer = out_create_mmap_buffer;
+            out->stream.get_mmap_position = out_get_mmap_position;
+        } else {
+            out->usecase = USECASE_AUDIO_PLAYBACK_HIFI;
+            out->config = pcm_config_hifi;
+        }
+
+        out->config.rate = out->sample_rate;
         out->config.channels = audio_channel_count_from_out_mask(out->channel_mask);
-        out->config.period_size = HDMI_MULTI_PERIOD_BYTES / (out->config.channels *
-                                                         audio_bytes_per_sample(config->format));
+        if (is_hdmi) {
+            out->config.period_size = HDMI_MULTI_PERIOD_BYTES / (out->config.channels *
+                                                         audio_bytes_per_sample(out->format));
+        }
         out->config.format = pcm_format_from_audio_format(out->format);
-    } else if (out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
+    } else if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
         pthread_mutex_lock(&adev->lock);
         bool offline = (adev->card_status == CARD_STATUS_OFFLINE);
         pthread_mutex_unlock(&adev->lock);
@@ -3773,17 +4248,20 @@
             ret = -EINVAL;
             goto error_open;
         }
+        out->sample_rate = config->offload_info.sample_rate;
+        if (config->offload_info.channel_mask != AUDIO_CHANNEL_NONE)
+            out->channel_mask = config->offload_info.channel_mask;
+        else if (config->channel_mask != AUDIO_CHANNEL_NONE)
+            out->channel_mask = config->channel_mask;
+        else
+            out->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+
+        out->format = config->offload_info.format;
 
         out->compr_config.codec = (struct snd_codec *)
                                     calloc(1, sizeof(struct snd_codec));
 
         out->usecase = USECASE_AUDIO_PLAYBACK_OFFLOAD;
-        if (config->offload_info.channel_mask)
-            out->channel_mask = config->offload_info.channel_mask;
-        else if (config->channel_mask)
-            out->channel_mask = config->channel_mask;
-        out->format = config->offload_info.format;
-        out->sample_rate = config->offload_info.sample_rate;
 
         out->stream.set_callback = out_set_callback;
         out->stream.pause = out_pause;
@@ -3795,11 +4273,11 @@
                 get_snd_codec_id(config->offload_info.format);
         out->compr_config.fragment_size = COMPRESS_OFFLOAD_FRAGMENT_SIZE;
         out->compr_config.fragments = COMPRESS_OFFLOAD_NUM_FRAGMENTS;
-        out->compr_config.codec->sample_rate = config->offload_info.sample_rate;
+        out->compr_config.codec->sample_rate = out->sample_rate;
         out->compr_config.codec->bit_rate =
                     config->offload_info.bit_rate;
         out->compr_config.codec->ch_in =
-                audio_channel_count_from_out_mask(config->channel_mask);
+                audio_channel_count_from_out_mask(out->channel_mask);
         out->compr_config.codec->ch_out = out->compr_config.codec->ch_in;
 
         if (flags & AUDIO_OUTPUT_FLAG_NON_BLOCKING)
@@ -3810,48 +4288,182 @@
         ALOGV("%s: offloaded output offload_info version %04x bit rate %d",
                 __func__, config->offload_info.version,
                 config->offload_info.bit_rate);
-    } else  if (out->devices == AUDIO_DEVICE_OUT_TELEPHONY_TX) {
+    } else if (out->flags & AUDIO_OUTPUT_FLAG_INCALL_MUSIC) {
         switch (config->sample_rate) {
+            case 0:
+                out->sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
+                break;
             case 8000:
             case 16000:
             case 48000:
                 out->sample_rate = config->sample_rate;
                 break;
             default:
-                out->sample_rate = AFE_PROXY_SAMPLING_RATE;
+                ALOGE("%s: Unsupported sampling rate %d for Incall Music", __func__,
+                      config->sample_rate);
+                config->sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
+                ret = -EINVAL;
+                goto error_open;
         }
-        out->format = AUDIO_FORMAT_PCM_16_BIT;
+        //FIXME: add support for MONO stream configuration when audioflinger mixer supports it
+        switch (config->channel_mask) {
+            case AUDIO_CHANNEL_NONE:
+            case AUDIO_CHANNEL_OUT_STEREO:
+                out->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+                break;
+            default:
+                ALOGE("%s: Unsupported channel mask %#x for Incall Music", __func__,
+                      config->channel_mask);
+                config->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+                ret = -EINVAL;
+                goto error_open;
+        }
+        switch (config->format) {
+            case AUDIO_FORMAT_DEFAULT:
+            case AUDIO_FORMAT_PCM_16_BIT:
+                out->format = AUDIO_FORMAT_PCM_16_BIT;
+                break;
+            default:
+                ALOGE("%s: Unsupported format %#x for Incall Music", __func__,
+                      config->format);
+                config->format = AUDIO_FORMAT_PCM_16_BIT;
+                ret = -EINVAL;
+                goto error_open;
+        }
+
+        voice_extn_check_and_set_incall_music_usecase(adev, out);
+    } else  if (out->devices == AUDIO_DEVICE_OUT_TELEPHONY_TX) {
+        switch (config->sample_rate) {
+            case 0:
+                out->sample_rate = AFE_PROXY_SAMPLING_RATE;
+                break;
+            case 8000:
+            case 16000:
+            case 48000:
+                out->sample_rate = config->sample_rate;
+                break;
+            default:
+                ALOGE("%s: Unsupported sampling rate %d for Telephony TX", __func__,
+                      config->sample_rate);
+                config->sample_rate = AFE_PROXY_SAMPLING_RATE;
+                ret = -EINVAL;
+                break;
+        }
+        //FIXME: add support for MONO stream configuration when audioflinger mixer supports it
+        switch (config->channel_mask) {
+            case AUDIO_CHANNEL_NONE:
+                out->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+                break;
+            case AUDIO_CHANNEL_OUT_STEREO:
+                out->channel_mask = config->channel_mask;
+                break;
+            default:
+                ALOGE("%s: Unsupported channel mask %#x for Telephony TX", __func__,
+                      config->channel_mask);
+                config->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+                ret = -EINVAL;
+                break;
+        }
+        switch (config->format) {
+            case AUDIO_FORMAT_DEFAULT:
+                out->format = AUDIO_FORMAT_PCM_16_BIT;
+                break;
+            case AUDIO_FORMAT_PCM_16_BIT:
+                out->format = config->format;
+                break;
+            default:
+                ALOGE("%s: Unsupported format %#x for Telephony TX", __func__,
+                      config->format);
+                config->format = AUDIO_FORMAT_PCM_16_BIT;
+                ret = -EINVAL;
+                break;
+        }
+        if (ret != 0)
+            goto error_open;
+
         out->usecase = USECASE_AUDIO_PLAYBACK_AFE_PROXY;
         out->config = pcm_config_afe_proxy_playback;
+        out->config.rate = out->sample_rate;
+        out->config.channels =
+                audio_channel_count_from_out_mask(out->channel_mask);
+        out->config.format = pcm_format_from_audio_format(out->format);
         adev->voice_tx_output = out;
-    } else if (out->flags == AUDIO_OUTPUT_FLAG_VOIP_RX) {
+    } else if (flags == AUDIO_OUTPUT_FLAG_VOIP_RX) {
+        switch (config->sample_rate) {
+            case 0:
+                out->sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
+                break;
+            case 8000:
+            case 16000:
+            case 32000:
+            case 48000:
+                out->sample_rate = config->sample_rate;
+                break;
+            default:
+                ALOGE("%s: Unsupported sampling rate %d for Voip RX", __func__,
+                      config->sample_rate);
+                config->sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
+                ret = -EINVAL;
+                break;
+        }
         //FIXME: add support for MONO stream configuration when audioflinger mixer supports it
+        switch (config->channel_mask) {
+            case AUDIO_CHANNEL_NONE:
+                out->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+                break;
+            case AUDIO_CHANNEL_OUT_STEREO:
+                out->channel_mask = config->channel_mask;
+                break;
+            default:
+                ALOGE("%s: Unsupported channel mask %#x for Voip RX", __func__,
+                      config->channel_mask);
+                config->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+                ret = -EINVAL;
+                break;
+        }
+        switch (config->format) {
+            case AUDIO_FORMAT_DEFAULT:
+                out->format = AUDIO_FORMAT_PCM_16_BIT;
+                break;
+            case AUDIO_FORMAT_PCM_16_BIT:
+                out->format = config->format;
+                break;
+            default:
+                ALOGE("%s: Unsupported format %#x for Voip RX", __func__,
+                      config->format);
+                config->format = AUDIO_FORMAT_PCM_16_BIT;
+                ret = -EINVAL;
+                break;
+        }
+        if (ret != 0)
+            goto error_open;
+
         uint32_t buffer_size, frame_size;
         out->usecase = USECASE_AUDIO_PLAYBACK_VOIP;
         out->config = pcm_config_voip;
-        out->config.format = pcm_format_from_audio_format(config->format);
-        out->config.rate = config->sample_rate;
+        out->config.rate = out->sample_rate;
+        out->config.format = pcm_format_from_audio_format(out->format);
         buffer_size = get_stream_buffer_size(VOIP_PLAYBACK_PERIOD_DURATION_MSEC,
-                                             config->sample_rate,
-                                             config->format,
+                                             out->sample_rate,
+                                             out->format,
                                              out->config.channels,
                                              false /*is_low_latency*/);
-        frame_size = audio_bytes_per_sample(config->format) * out->config.channels;
+        frame_size = audio_bytes_per_sample(out->format) * out->config.channels;
         out->config.period_size = buffer_size / frame_size;
         out->config.period_count = VOIP_PLAYBACK_PERIOD_COUNT;
         out->af_period_multiplier = 1;
     } else {
-        if (out->flags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER) {
+        if (flags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER) {
             out->usecase = USECASE_AUDIO_PLAYBACK_DEEP_BUFFER;
             out->config = pcm_config_deep_buffer;
-        } else if (out->flags & AUDIO_OUTPUT_FLAG_TTS) {
+        } else if (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) {
+        } else if (flags & AUDIO_OUTPUT_FLAG_RAW) {
             out->usecase = USECASE_AUDIO_PLAYBACK_ULL;
             out->realtime = may_use_noirq_mode(adev, USECASE_AUDIO_PLAYBACK_ULL, out->flags);
             out->config = out->realtime ? pcm_config_rt : pcm_config_low_latency;
-        } else if (out->flags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ) {
+        } else if (flags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ) {
             out->usecase = USECASE_AUDIO_PLAYBACK_MMAP;
             out->config = pcm_config_mmap_playback;
             out->stream.start = out_start;
@@ -3862,15 +4474,38 @@
             out->usecase = USECASE_AUDIO_PLAYBACK_LOW_LATENCY;
             out->config = pcm_config_low_latency;
         }
-        if (config->format != audio_format_from_pcm_format(out->config.format)) {
-            out->config.format = pcm_format_from_audio_format(config->format);
+
+        if (config->sample_rate == 0) {
+            out->sample_rate = out->config.rate;
+        } else {
+            out->sample_rate = config->sample_rate;
         }
-        out->sample_rate = out->config.rate;
+        if (config->channel_mask == AUDIO_CHANNEL_NONE) {
+            out->channel_mask = audio_channel_out_mask_from_count(out->config.channels);
+        } else {
+            out->channel_mask = config->channel_mask;
+        }
+        if (config->format == AUDIO_FORMAT_DEFAULT)
+            out->format = audio_format_from_pcm_format(out->config.format);
+        else if (!audio_is_linear_pcm(config->format)) {
+            config->format = AUDIO_FORMAT_PCM_16_BIT;
+            ret = -EINVAL;
+            goto error_open;
+        } else {
+            out->format = config->format;
+        }
+
+        out->config.rate = out->sample_rate;
+        out->config.channels =
+                audio_channel_count_from_out_mask(out->channel_mask);
+        if (out->format != audio_format_from_pcm_format(out->config.format)) {
+            out->config.format = pcm_format_from_audio_format(out->format);
+        }
     }
 
     if ((config->sample_rate != 0 && config->sample_rate != out->sample_rate) ||
         (config->format != AUDIO_FORMAT_DEFAULT && config->format != out->format) ||
-        (config->channel_mask != 0 && config->channel_mask != out->channel_mask)) {
+        (config->channel_mask != AUDIO_CHANNEL_NONE && config->channel_mask != out->channel_mask)) {
         ALOGI("%s: Unsupported output config. sample_rate:%u format:%#x channel_mask:%#x",
               __func__, config->sample_rate, config->format, config->channel_mask);
         config->sample_rate = out->sample_rate;
@@ -3943,6 +4578,10 @@
     config->channel_mask = out->stream.common.get_channels(&out->stream.common);
     config->sample_rate = out->stream.common.get_sample_rate(&out->stream.common);
 
+    register_format(out->format, out->supported_formats);
+    register_channel_mask(out->channel_mask, out->supported_channel_masks);
+    register_sample_rate(out->sample_rate, out->supported_sample_rates);
+
     out->error_log = error_log_create(
             ERROR_LOG_ENTRIES,
             1000000000 /* aggregate consecutive identical errors within one second in ns */);
@@ -3992,6 +4631,8 @@
             free(out->compr_config.codec);
     }
 
+    out->a2dp_compress_mute = false;
+
     if (adev->voice_tx_output == out)
         adev->voice_tx_output = NULL;
 
@@ -3999,6 +4640,7 @@
     out->error_log = NULL;
 
     pthread_cond_destroy(&out->cond);
+    pthread_mutex_destroy(&out->pre_lock);
     pthread_mutex_destroy(&out->lock);
     free(stream);
     ALOGV("%s: exit", __func__);
@@ -4013,6 +4655,7 @@
     int val;
     int ret;
     int status = 0;
+    bool a2dp_reconfig = false;
 
     ALOGV("%s: enter: %s", __func__, kvpairs);
 
@@ -4041,10 +4684,11 @@
             adev->screen_off = true;
     }
 
+#ifndef MAXXAUDIO_QDSP_ENABLED
     ret = str_parms_get_int(parms, "rotation", &val);
     if (ret >= 0) {
         bool reverse_speakers = false;
-        switch(val) {
+        switch (val) {
         // FIXME: note that the code below assumes that the speakers are in the correct placement
         //   relative to the user when the device is rotated 90deg from its default rotation. This
         //   assumption is device-specific, not platform-specific like this code.
@@ -4066,6 +4710,7 @@
             platform_check_and_set_swap_lr_channels(adev, reverse_speakers);
         }
     }
+#endif
 
     ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_BT_SCO_WB, value, sizeof(value));
     if (ret >= 0) {
@@ -4097,7 +4742,6 @@
             ret = str_parms_get_str(parms, "card", value, sizeof(value));
             if (ret >= 0) {
                 const int card = atoi(value);
-
                 audio_extn_usb_remove_device(device, card);
             }
         } else if (audio_is_usb_in_device(device)) {
@@ -4110,6 +4754,31 @@
     }
 
     audio_extn_hfp_set_parameters(adev, parms);
+    audio_extn_ma_set_parameters(adev, parms);
+
+    status = audio_extn_a2dp_set_parameters(parms, &a2dp_reconfig);
+    if (status >= 0 && a2dp_reconfig) {
+        struct audio_usecase *usecase;
+        struct listnode *node;
+        list_for_each(node, &adev->usecase_list) {
+            usecase = node_to_item(node, struct audio_usecase, list);
+            if ((usecase->type == PCM_PLAYBACK) &&
+                (usecase->devices & AUDIO_DEVICE_OUT_ALL_A2DP)) {
+                ALOGD("%s: reconfigure A2DP... forcing device switch", __func__);
+
+                pthread_mutex_unlock(&adev->lock);
+                lock_output_stream(usecase->stream.out);
+                pthread_mutex_lock(&adev->lock);
+                audio_extn_a2dp_set_handoff_mode(true);
+                // force device switch to reconfigure encoder
+                select_devices(adev, usecase->id);
+                audio_extn_a2dp_set_handoff_mode(false);
+                pthread_mutex_unlock(&usecase->stream.out->lock);
+                break;
+            }
+        }
+    }
+
 done:
     str_parms_destroy(parms);
     pthread_mutex_unlock(&adev->lock);
@@ -4128,6 +4797,8 @@
     pthread_mutex_lock(&adev->lock);
 
     voice_get_parameters(adev, query, reply);
+    audio_extn_a2dp_get_parameters(query, reply);
+
     str = str_parms_to_str(reply);
     str_parms_destroy(query);
     str_parms_destroy(reply);
@@ -4339,6 +5010,7 @@
     in->stream.read = in_read;
     in->stream.get_input_frames_lost = in_get_input_frames_lost;
     in->stream.get_capture_position = in_get_capture_position;
+    in->stream.get_active_microphones = in_get_active_microphones;
 
     in->device = devices;
     in->source = source;
@@ -4347,6 +5019,18 @@
     in->capture_handle = handle;
     in->flags = flags;
 
+    ALOGV("%s: source = %d, config->channel_mask = %d", __func__, source, config->channel_mask);
+    if (source == AUDIO_SOURCE_VOICE_UPLINK ||
+         source == AUDIO_SOURCE_VOICE_DOWNLINK) {
+        /* Force channel config requested to mono if incall
+           record is being requested for only uplink/downlink */
+        if (config->channel_mask != AUDIO_CHANNEL_IN_MONO) {
+            config->channel_mask = AUDIO_CHANNEL_IN_MONO;
+            ret = -EINVAL;
+            goto err_open;
+        }
+    }
+
     if (is_usb_dev && may_use_hifi_record) {
         /* HiFi record selects an appropriate format, channel, rate combo
            depending on sink capabilities*/
@@ -4503,6 +5187,11 @@
     in->config.channels = channel_count;
     in->sample_rate  = in->config.rate;
 
+
+    register_format(in->format, in->supported_formats);
+    register_channel_mask(in->channel_mask, in->supported_channel_masks);
+    register_sample_rate(in->sample_rate, in->supported_sample_rates);
+
     in->error_log = error_log_create(
             ERROR_LOG_ENTRIES,
             NANOS_PER_SECOND /* aggregate consecutive identical errors within one second */);
@@ -4544,6 +5233,9 @@
     error_log_destroy(in->error_log);
     in->error_log = NULL;
 
+    pthread_mutex_destroy(&in->pre_lock);
+    pthread_mutex_destroy(&in->lock);
+
     free(stream);
 
     return;
@@ -4700,6 +5392,7 @@
     if ((--audio_device_ref_count) == 0) {
         audio_extn_snd_mon_unregister_listener(adev);
         audio_extn_tfa_98xx_deinit();
+        audio_extn_ma_deinit();
         audio_route_free(adev->audio_route);
         free(adev->snd_dev_ref_cnt);
         platform_deinit(adev->platform);
@@ -4711,6 +5404,7 @@
         }
         if (adev->adm_deinit)
             adev->adm_deinit(adev->adm_data);
+        pthread_mutex_destroy(&adev->lock);
         free(device);
     }
 
@@ -4763,6 +5457,81 @@
     return;
 }
 
+/* out and adev lock held */
+static int check_a2dp_restore_l(struct audio_device *adev, struct stream_out *out, bool restore)
+{
+    struct audio_usecase *uc_info;
+    float left_p;
+    float right_p;
+    audio_devices_t devices;
+
+    uc_info = get_usecase_from_list(adev, out->usecase);
+    if (uc_info == NULL) {
+        ALOGE("%s: Could not find the usecase (%d) in the list",
+              __func__, out->usecase);
+        return -EINVAL;
+    }
+
+    ALOGD("%s: enter: usecase(%d: %s)", __func__,
+          out->usecase, use_case_table[out->usecase]);
+
+    if (restore) {
+        // restore A2DP device for active usecases and unmute if required
+        if ((out->devices & AUDIO_DEVICE_OUT_ALL_A2DP) &&
+            !is_a2dp_device(uc_info->out_snd_device)) {
+            ALOGD("%s: restoring A2DP and unmuting stream", __func__);
+            select_devices(adev, uc_info->id);
+            pthread_mutex_lock(&out->compr_mute_lock);
+            if ((out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) &&
+                (out->a2dp_compress_mute)) {
+                out->a2dp_compress_mute = false;
+                set_compr_volume(&out->stream, out->volume_l, out->volume_r);
+            }
+            pthread_mutex_unlock(&out->compr_mute_lock);
+        }
+    } else {
+        // mute compress stream if suspended
+        pthread_mutex_lock(&out->compr_mute_lock);
+        if ((out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) &&
+            (!out->a2dp_compress_mute)) {
+            if (!out->standby) {
+                ALOGD("%s: selecting speaker and muting stream", __func__);
+                devices = out->devices;
+                out->devices = AUDIO_DEVICE_OUT_SPEAKER;
+                left_p = out->volume_l;
+                right_p = out->volume_r;
+                if (out->offload_state == OFFLOAD_STATE_PLAYING)
+                    compress_pause(out->compr);
+                set_compr_volume(&out->stream, 0.0f, 0.0f);
+                out->a2dp_compress_mute = true;
+                select_devices(adev, out->usecase);
+                if (out->offload_state == OFFLOAD_STATE_PLAYING)
+                    compress_resume(out->compr);
+                out->devices = devices;
+                out->volume_l = left_p;
+                out->volume_r = right_p;
+            }
+        }
+        pthread_mutex_unlock(&out->compr_mute_lock);
+    }
+    ALOGV("%s: exit", __func__);
+    return 0;
+}
+
+int check_a2dp_restore(struct audio_device *adev, struct stream_out *out, bool restore)
+{
+    int ret = 0;
+
+    lock_output_stream(out);
+    pthread_mutex_lock(&adev->lock);
+
+    ret = check_a2dp_restore_l(adev, out, restore);
+
+    pthread_mutex_unlock(&adev->lock);
+    pthread_mutex_unlock(&out->lock);
+    return ret;
+}
+
 static int adev_open(const hw_module_t *module, const char *name,
                      hw_device_t **device)
 {
@@ -4806,6 +5575,7 @@
 
     adev->device.close_input_stream = adev_close_input_stream;
     adev->device.dump = adev_dump;
+    adev->device.get_microphones = adev_get_microphones;
 
     /* Set the default route before the PCM stream is opened */
     pthread_mutex_lock(&adev->lock);
@@ -4915,6 +5685,8 @@
         }
     }
 
+    adev->mic_break_enabled = property_get_bool("vendor.audio.mic_break", false);
+
     // commented as full set of app type cfg is sent from platform
     // audio_extn_utils_send_default_app_type_cfg(adev->platform, adev->mixer);
     audio_device_ref_count++;
@@ -4930,6 +5702,7 @@
     }
 
     audio_extn_tfa_98xx_init(adev);
+    audio_extn_ma_init(adev->platform);
 
     pthread_mutex_unlock(&adev_init_lock);
 
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index a588eef..921c249 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -49,7 +49,7 @@
 #define ACDB_DEV_TYPE_OUT 1
 #define ACDB_DEV_TYPE_IN 2
 
-#define MAX_SUPPORTED_CHANNEL_MASKS 8
+#define MAX_SUPPORTED_CHANNEL_MASKS (2 * FCC_8) /* support positional and index masks to 8ch */
 #define MAX_SUPPORTED_FORMATS 15
 #define MAX_SUPPORTED_SAMPLE_RATES 7
 #define DEFAULT_HDMI_OUT_CHANNELS   2
@@ -131,6 +131,10 @@
     USECASE_AUDIO_PLAYBACK_VOIP,
     USECASE_AUDIO_RECORD_VOIP,
 
+    USECASE_INCALL_MUSIC_UPLINK,
+
+    USECASE_AUDIO_A2DP_ABR_FEEDBACK,
+
     AUDIO_USECASE_MAX
 };
 
@@ -179,6 +183,7 @@
     struct audio_stream_out stream;
     pthread_mutex_t lock; /* see note below on mutex acquisition order */
     pthread_mutex_t pre_lock; /* acquire before lock to avoid DOS by playback thread */
+    pthread_mutex_t compr_mute_lock; /* acquire before setting compress volume */
     pthread_cond_t  cond;
     struct pcm_config config;
     struct compr_config compr_config;
@@ -216,6 +221,9 @@
     int af_period_multiplier;
     struct audio_device *dev;
     card_status_t card_status;
+    bool a2dp_compress_mute;
+    float volume_l;
+    float volume_r;
 
     error_log_t *error_log;
 
@@ -320,6 +328,7 @@
     bool mic_muted;
     bool enable_voicerx;
     bool enable_hfp;
+    bool mic_break_enabled;
 
     int snd_card;
     void *platform;
@@ -378,6 +387,8 @@
 struct audio_usecase *get_usecase_from_list(struct audio_device *adev,
                                             audio_usecase_t uc_id);
 
+int check_a2dp_restore(struct audio_device *adev, struct stream_out *out, bool restore);
+
 #define LITERAL_TO_STRING(x) #x
 #define CHECK(condition) LOG_ALWAYS_FATAL_IF(!(condition), "%s",\
             __FILE__ ":" LITERAL_TO_STRING(__LINE__)\
diff --git a/hal/audio_perf.cpp b/hal/audio_perf.cpp
new file mode 100644
index 0000000..671a324
--- /dev/null
+++ b/hal/audio_perf.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "audio_hw_primary"
+
+#include <cinttypes>
+
+#include <utils/Log.h>
+#include <utils/Mutex.h>
+
+#include <android/hardware/power/1.2/IPower.h>
+
+#include "audio_perf.h"
+
+using android::hardware::power::V1_2::IPower;
+using android::hardware::power::V1_2::PowerHint;
+using android::hardware::power::V1_2::toString;
+using android::hardware::Return;
+using android::hardware::Void;
+using android::hardware::hidl_death_recipient;
+using android::hidl::base::V1_0::IBase;
+
+// Do not use gPowerHAL, use getPowerHal to retrieve a copy instead
+static android::sp<IPower> gPowerHal_ = nullptr;
+// Protect gPowerHal_
+static std::mutex gPowerHalMutex;
+
+// PowerHalDeathRecipient to invalid the client when service dies
+struct PowerHalDeathRecipient : virtual public hidl_death_recipient {
+    // hidl_death_recipient interface
+    virtual void serviceDied(uint64_t, const android::wp<IBase>&) override {
+        std::lock_guard<std::mutex> lock(gPowerHalMutex);
+        ALOGE("PowerHAL just died");
+        gPowerHal_ = nullptr;
+    }
+};
+
+// Retrieve a copy of client
+static android::sp<IPower> getPowerHal() {
+    std::lock_guard<std::mutex> lock(gPowerHalMutex);
+    static android::sp<PowerHalDeathRecipient> gPowerHalDeathRecipient = nullptr;
+    static bool gPowerHalExists = true;
+
+    if (gPowerHalExists && gPowerHal_ == nullptr) {
+        gPowerHal_ = IPower::getService();
+
+        if (gPowerHal_ == nullptr) {
+            ALOGE("Unable to get Power service");
+            gPowerHalExists = false;
+        } else {
+            if (gPowerHalDeathRecipient == nullptr) {
+                gPowerHalDeathRecipient = new PowerHalDeathRecipient();
+            }
+            Return<bool> linked = gPowerHal_->linkToDeath(
+                gPowerHalDeathRecipient, 0 /* cookie */);
+            if (!linked.isOk()) {
+                ALOGE("Transaction error in linking to PowerHAL death: %s",
+                      linked.description().c_str());
+                gPowerHal_ = nullptr;
+            } else if (!linked) {
+                ALOGW("Unable to link to PowerHal death notifications");
+                gPowerHal_ = nullptr;
+            } else {
+                ALOGD("Connect to PowerHAL and link to death "
+                      "notification successfully");
+            }
+        }
+    }
+    return gPowerHal_;
+}
+
+static bool powerHint(PowerHint hint, int32_t data) {
+    android::sp<IPower> powerHal = getPowerHal();
+    if (powerHal == nullptr) {
+        return false;
+    }
+
+    auto ret = powerHal->powerHintAsync_1_2(hint, data);
+
+    if (!ret.isOk()) {
+        ALOGE("powerHint failed, hint: %s, data: %" PRId32 ",  error: %s",
+              toString(hint).c_str(),
+              data,
+              ret.description().c_str());
+    }
+    return ret.isOk();
+}
+
+int audio_streaming_hint_start() {
+    return powerHint(PowerHint::AUDIO_STREAMING, 1);
+}
+
+int audio_streaming_hint_end() {
+    return powerHint(PowerHint::AUDIO_STREAMING, 0);
+}
+
+int audio_low_latency_hint_start() {
+    return powerHint(PowerHint::AUDIO_LOW_LATENCY, 1);
+}
+
+int audio_low_latency_hint_end() {
+    return powerHint(PowerHint::AUDIO_LOW_LATENCY, 0);
+}
diff --git a/hal/audio_perf.h b/hal/audio_perf.h
new file mode 100644
index 0000000..b564938
--- /dev/null
+++ b/hal/audio_perf.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 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 __QAUDIOPERF_H__
+#define __QAUDIOPERF_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int audio_streaming_hint_start();
+int audio_streaming_hint_end();
+
+int audio_low_latency_hint_start();
+int audio_low_latency_hint_end();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //__QAUDIOPERF_H__
diff --git a/hal/msm8916/hw_info.c b/hal/msm8916/hw_info.c
index b66bec4..0afb510 100644
--- a/hal/msm8916/hw_info.c
+++ b/hal/msm8916/hw_info.c
@@ -20,7 +20,7 @@
 
 #include <stdlib.h>
 #include <dlfcn.h>
-#include <cutils/log.h>
+#include <log/log.h>
 #include <cutils/str_parms.h>
 #include "audio_hw.h"
 #include "platform.h"
@@ -47,6 +47,8 @@
     } else if (!strcmp(snd_card_name, "msm8909-snd-card") ||
                !strcmp(snd_card_name, "msm8909-pm8916-snd-card")) {
         strlcpy(hw_info->name, "msm8909", sizeof(hw_info->name));
+    } else if (!strcmp(snd_card_name, "msm-bg-snd-card")) {
+        strlcpy(hw_info->name, "msm8909", sizeof(hw_info->name));
     }  else if (!strcmp(snd_card_name, "msm8952-snd-card") ||
                 !strcmp(snd_card_name, "msm8952-snd-card-mtp")) {
         strlcpy(hw_info->name, "msm8952", sizeof(hw_info->name));
@@ -68,7 +70,8 @@
     }
 
     if (strstr(snd_card_name, "msm8x16") || strstr(snd_card_name, "msm8909")
-        || strstr(snd_card_name, "msm8952")) {
+        || strstr(snd_card_name, "msm8952") ||
+        strstr(snd_card_name, "msm-bg-snd-card")) {
         ALOGV("8x16 - variant soundcard");
 
         strlcpy(hw_info->type, "", sizeof(hw_info->type));
diff --git a/hal/msm8916/platform.c b/hal/msm8916/platform.c
index c113eca..d92243e 100644
--- a/hal/msm8916/platform.c
+++ b/hal/msm8916/platform.c
@@ -22,22 +22,24 @@
 #include <dlfcn.h>
 #include <fcntl.h>
 #include <sys/ioctl.h>
-#include <cutils/log.h>
+#include <log/log.h>
 #include <cutils/properties.h>
 #include <cutils/str_parms.h>
 #include <audio_hw.h>
 #include <platform_api.h>
 #include "platform.h"
 #include "audio_extn.h"
+#include "acdb.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"
-#define MIXER_XML_PATH_MTP "/system/etc/mixer_paths_mtp.xml"
-#define MIXER_XML_PATH_MSM8909_PM8916 "/system/etc/mixer_paths_msm8909_pm8916.xml"
-#define MIXER_XML_PATH_L9300 "/system/etc/mixer_paths_l9300.xml"
+#define MIXER_XML_PATH "mixer_paths.xml"
+#define MIXER_XML_PATH_MTP "mixer_paths_mtp.xml"
+#define MIXER_XML_PATH_MSM8909_PM8916 "mixer_paths_msm8909_pm8916.xml"
+#define MIXER_XML_PATH_BG "mixer_paths_bg.xml"
+#define MIXER_XML_PATH_L9300 "mixer_paths_l9300.xml"
 
 #define LIB_ACDB_LOADER "libacdbloader.so"
 #define AUDIO_DATA_BLOCK_MIXER_CTL "HDMI EDID"
@@ -89,6 +91,19 @@
     int length;
 };
 
+typedef struct acdb_audio_cal_cfg {
+    uint32_t             persist;
+    uint32_t             snd_dev_id;
+    audio_devices_t      dev_id;
+    int32_t              acdb_dev_id;
+    uint32_t             app_type;
+    uint32_t             topo_id;
+    uint32_t             sampling_rate;
+    uint32_t             cal_type;
+    uint32_t             module_id;
+    uint32_t             param_id;
+} acdb_audio_cal_cfg_t;
+
 enum {
     CAL_MODE_SEND           = 0x1,
     CAL_MODE_PERSIST        = 0x2,
@@ -119,14 +134,10 @@
 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)(const char *, char *, int);
-typedef void (*acdb_send_audio_cal_t)(int, int);
 typedef void (*acdb_send_audio_cal_v3_t)(int, int, int , int, int);
-typedef void (*acdb_send_voice_cal_t)(int, int);
-typedef int (*acdb_reload_vocvoltable_t)(int);
 typedef int (*acdb_loader_get_calibration_t)(char *attr, int size, void *data);
 acdb_loader_get_calibration_t acdb_loader_get_calibration;
+static int platform_get_meta_info_key_from_list(void *platform, char *mod_name);
 
 struct platform_data {
     struct audio_device *adev;
@@ -140,10 +151,12 @@
     bool gsm_mode_enabled;
     /* Audio calibration related functions */
     void                       *acdb_handle;
+    acdb_init_v3_t             acdb_init_v3;
     acdb_init_v2_cvd_t         acdb_init;
     acdb_deallocate_t          acdb_deallocate;
     acdb_send_audio_cal_t      acdb_send_audio_cal;
     acdb_send_audio_cal_v3_t   acdb_send_audio_cal_v3;
+    acdb_get_audio_cal_t       acdb_get_audio_cal;
     acdb_send_voice_cal_t      acdb_send_voice_cal;
     acdb_reload_vocvoltable_t  acdb_reload_vocvoltable;
     void *hw_info;
@@ -151,6 +164,7 @@
     bool speaker_lr_swap;
 
     int max_vol_index;
+    struct listnode acdb_meta_key_list;
 };
 
 int pcm_device_table[AUDIO_USECASE_MAX][2] = {
@@ -196,6 +210,7 @@
     [SND_DEVICE_OUT_HANDSET] = "handset",
     [SND_DEVICE_OUT_SPEAKER] = "speaker",
     [SND_DEVICE_OUT_SPEAKER_REVERSE] = "speaker-reverse",
+    [SND_DEVICE_OUT_SPEAKER_SAFE] = "speaker-safe",
     [SND_DEVICE_OUT_HEADPHONES] = "headphones",
     [SND_DEVICE_OUT_LINE] = "line",
     [SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES] = "speaker-and-headphones",
@@ -210,6 +225,9 @@
     [SND_DEVICE_OUT_SPEAKER_AND_HDMI] = "speaker-and-hdmi",
     [SND_DEVICE_OUT_BT_SCO] = "bt-sco-headset",
     [SND_DEVICE_OUT_BT_SCO_WB] = "bt-sco-headset-wb",
+    [SND_DEVICE_OUT_BT_A2DP] = "bt-a2dp",
+    [SND_DEVICE_OUT_SPEAKER_AND_BT_A2DP] = "speaker-and-bt-a2dp",
+    [SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_A2DP] = "speaker-safe-and-bt-a2dp",
     [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",
@@ -287,6 +305,7 @@
     [SND_DEVICE_OUT_HANDSET] = 7,
     [SND_DEVICE_OUT_SPEAKER] = 14,
     [SND_DEVICE_OUT_SPEAKER_REVERSE] = 14,
+    [SND_DEVICE_OUT_SPEAKER_SAFE] = 14,
     [SND_DEVICE_OUT_LINE] = 10,
     [SND_DEVICE_OUT_HEADPHONES] = 10,
     [SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES] = 10,
@@ -300,6 +319,9 @@
     [SND_DEVICE_OUT_SPEAKER_AND_HDMI] = 14,
     [SND_DEVICE_OUT_BT_SCO] = 22,
     [SND_DEVICE_OUT_BT_SCO_WB] = 39,
+    [SND_DEVICE_OUT_BT_A2DP] = 20,
+    [SND_DEVICE_OUT_SPEAKER_AND_BT_A2DP] = 14,
+    [SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_A2DP] = 14,
     [SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES] = 17,
     [SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = 17,
     [SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET] = 37,
@@ -383,6 +405,7 @@
     {TO_NAME_INDEX(SND_DEVICE_OUT_HANDSET)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_REVERSE)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_SAFE)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_HEADPHONES)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_LINE)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES)},
@@ -397,6 +420,9 @@
     {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_HDMI)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_BT_SCO)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_BT_SCO_WB)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_BT_A2DP)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_BT_A2DP)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_A2DP)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET)},
@@ -485,7 +511,9 @@
     {TO_NAME_INDEX(USECASE_VOICEMMODE1_CALL)},
     {TO_NAME_INDEX(USECASE_VOICEMMODE2_CALL)},
     {TO_NAME_INDEX(USECASE_AUDIO_HFP_SCO)},
+    {TO_NAME_INDEX(USECASE_AUDIO_HFP_SCO_WB)},
     {TO_NAME_INDEX(USECASE_AUDIO_SPKR_CALIB_TX)},
+    {TO_NAME_INDEX(USECASE_AUDIO_A2DP_ABR_FEEDBACK)},
 };
 
 #define DEEP_BUFFER_PLATFORM_DELAY (29*1000LL)
@@ -502,6 +530,10 @@
                  sizeof("msm8909-pm8916-snd-card"))) {
         strlcpy(mixer_xml_path, MIXER_XML_PATH_MSM8909_PM8916,
                 sizeof(MIXER_XML_PATH_MSM8909_PM8916));
+    } else if (!strncmp(snd_card_name, "msm-bg-snd-card",
+                sizeof("msm-bg-snd-card"))) {
+        strlcpy(mixer_xml_path, MIXER_XML_PATH_BG,
+                sizeof(MIXER_XML_PATH_BG));
     } else if (!strncmp(snd_card_name, "msm8952-snd-card-mtp",
                  sizeof("msm8952-snd-card-mtp"))) {
         strlcpy(mixer_xml_path, MIXER_XML_PATH_MTP,
@@ -667,6 +699,10 @@
     backend_table[SND_DEVICE_IN_BT_SCO_MIC_WB_NREC] = strdup("bt-sco-wb");
     backend_table[SND_DEVICE_OUT_BT_SCO] = strdup("bt-sco");
     backend_table[SND_DEVICE_OUT_BT_SCO_WB] = strdup("bt-sco-wb");
+    backend_table[SND_DEVICE_OUT_BT_A2DP] = strdup("bt-a2dp");
+    backend_table[SND_DEVICE_OUT_SPEAKER_AND_BT_A2DP] = strdup("speaker-and-bt-a2dp");
+    backend_table[SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_A2DP] =
+        strdup("speaker-safe-and-bt-a2dp");
     backend_table[SND_DEVICE_OUT_HDMI] = strdup("hdmi");
     backend_table[SND_DEVICE_OUT_SPEAKER_AND_HDMI] = strdup("speaker-and-hdmi");
     backend_table[SND_DEVICE_OUT_VOICE_TX] = strdup("afe-proxy");
@@ -735,8 +771,84 @@
     void   *buff;
 };
 
+static int send_bg_cal(struct platform_data *plat_data,
+                        int type, int fd)
+{
+    /*
+     * This is done to avoid compiler failure due to unused varialbes
+     * if both the below #defines are not present
+     */
+    (void)plat_data;
+    (void)type;
+    (void)fd;
+
+#ifdef BG_CAL_SUPPORT
+    if ((type == BG_CODEC_MIC_CAL) ||
+        (type == BG_CODEC_SPEAKER_CAL)) {
+#ifdef BG_CODEC_CAL
+        int ret = 0, key = 0;
+        uint32_t param_len;
+        uint8_t *dptr = NULL;
+        struct wcdcal_ioctl_buffer codec_buffer;
+        acdb_audio_cal_cfg_t cal;
+
+        memset(&cal, 0, sizeof(cal));
+        cal.persist = 1;
+        cal.cal_type = AUDIO_CORE_METAINFO_CAL_TYPE;
+        param_len = MAX_SET_CAL_BYTE_SIZE;
+        dptr = (unsigned char*) calloc(param_len, sizeof(unsigned char*));
+        if (dptr == NULL) {
+            ALOGE("%s Memory allocation failed for length %d",
+                    __func__, param_len);
+            return 0;
+        }
+        if (type == BG_CODEC_MIC_CAL) {
+            key = platform_get_meta_info_key_from_list(plat_data,
+                                                       "bg_miccal");
+            if (!key) {
+                ALOGE("%s Failed to fetch mic metakey info", __func__);
+                goto done;
+            }
+            ALOGV("%s BG mic with key:0x%x", __func__, key);
+            codec_buffer.cal_type = BG_CODEC_MIC_CAL;
+        } else if (type == BG_CODEC_SPEAKER_CAL) {
+            key = platform_get_meta_info_key_from_list(plat_data,
+                                                       "bg_speakercal");
+            if (!key) {
+                ALOGE("%s Failed to fetch metakey info", __func__);
+                goto done;
+            }
+            ALOGV("%s BG speaker with key:0x%x", __func__, key);
+            codec_buffer.cal_type = BG_CODEC_SPEAKER_CAL;
+        }
+        cal.acdb_dev_id = key;
+        ret = plat_data->acdb_get_audio_cal((void*)&cal, (void*)dptr,
+                                            &param_len);
+        if (ret) {
+            ALOGE("%s failed to get meta info for key 0x%x error %d",
+                    __func__, key, ret);
+            goto done;
+        }
+        codec_buffer.buffer = dptr;
+        codec_buffer.size = param_len;
+
+        if (ioctl(fd, SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE, &codec_buffer) < 0)
+            ALOGE("Failed to call ioctl  for mic err=%d calib.size=%d",
+                    errno, codec_buffer.size);
+        else
+            ALOGD("%s cal sent for %d calib.size=%d",
+                    __func__, cal.acdb_dev_id, codec_buffer.size);
+    done:
+        free(dptr);
+#endif /* #ifdef BG_CODEC_CAL */
+        return 0;
+    } else
+#endif /* #ifdef BG_CAL_SUPPORT */
+      return -1;
+}
+
 static int send_codec_cal(acdb_loader_get_calibration_t acdb_loader_get_calibration,
-                          struct platform_data *plat_data __unused, int fd)
+                          struct platform_data *plat_data , int fd)
 {
     int ret = 0, type;
 
@@ -744,6 +856,9 @@
         struct wcdcal_ioctl_buffer codec_buffer;
         struct param_data calib;
 
+        if (send_bg_cal(plat_data, type, fd) == 0)
+            continue;
+
         if (type != WCD9XXX_MBHC_CAL)
             continue;
 
@@ -811,8 +926,11 @@
     char *cvd_version = NULL;
     int key = 0;
     const char *snd_card_name;
-    int result;
+    int result = 0;
     char value[PROPERTY_VALUE_MAX];
+    struct listnode *node;
+    struct meta_key_list *key_info;
+
     cvd_version = calloc(1, MAX_CVD_VERSION_STRING_SIZE);
     if (!cvd_version)
         ALOGE("Failed to allocate cvd version");
@@ -823,7 +941,12 @@
     key = atoi(value);
     snd_card_name = mixer_get_name(my_data->adev->mixer);
 
-    result = my_data->acdb_init(snd_card_name, cvd_version, key);
+    if (my_data->acdb_init_v3) {
+        result = my_data->acdb_init_v3(snd_card_name, cvd_version,
+                                           &my_data->acdb_meta_key_list);
+    } else if (my_data->acdb_init) {
+        result = my_data->acdb_init((char *)snd_card_name, cvd_version, key);
+    }
 
     if (cvd_version)
         free(cvd_version);
@@ -836,6 +959,28 @@
     return result;
 }
 
+// Treblized config files will be located in /odm/etc or /vendor/etc.
+static const char *kConfigLocationList[] =
+        {"/odm/etc", "/vendor/etc", "/system/etc"};
+static const int kConfigLocationListSize =
+        (sizeof(kConfigLocationList) / sizeof(kConfigLocationList[0]));
+
+bool resolve_config_file(char file_name[MIXER_PATH_MAX_LENGTH]) {
+    char full_config_path[MIXER_PATH_MAX_LENGTH];
+    for (int i = 0; i < kConfigLocationListSize; i++) {
+        snprintf(full_config_path,
+                 MIXER_PATH_MAX_LENGTH,
+                 "%s/%s",
+                 kConfigLocationList[i],
+                 file_name);
+        if (F_OK == access(full_config_path, 0)) {
+            strcpy(file_name, full_config_path);
+            return true;
+        }
+    }
+    return false;
+}
+
 void *platform_init(struct audio_device *adev)
 {
     char platform[PROPERTY_VALUE_MAX] = {0};
@@ -845,6 +990,7 @@
     int retry_num = 0, snd_card_num = 0, key = 0;
     const char *snd_card_name;
     char mixer_xml_path[MAX_MIXER_XML_PATH] = {0};
+    char platform_info_path[MAX_MIXER_XML_PATH] = {0};
     char ffspEnable[PROPERTY_VALUE_MAX] = {0};
     char *cvd_version = NULL;
     int idx;
@@ -897,6 +1043,11 @@
             ALOGE("%s: Failed to init hardware info", __func__);
         } else {
             query_platform(snd_card_name, mixer_xml_path);
+            if (!resolve_config_file(mixer_xml_path)) {
+                memset(mixer_xml_path, 0, sizeof(mixer_xml_path));
+                strlcpy(mixer_xml_path, MIXER_XML_PATH, MAX_MIXER_XML_PATH);
+                resolve_config_file(mixer_xml_path);
+            }
             ALOGD("%s: mixer path file is %s", __func__,
                                     mixer_xml_path);
             adev->audio_route = audio_route_init(snd_card_num,
@@ -971,6 +1122,13 @@
         acdb_device_table[SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET] = 131;
     }
 
+    list_init(&my_data->acdb_meta_key_list);
+    set_platform_defaults();
+    /* Initialize ACDB and PCM ID's */
+    strlcpy(platform_info_path, PLATFORM_INFO_XML_PATH, MAX_MIXER_XML_PATH);
+    resolve_config_file(platform_info_path);
+    platform_info_init(platform_info_path, my_data);
+
     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);
@@ -994,6 +1152,12 @@
             ALOGE("%s: Could not find the symbol acdb_send_audio_cal from %s",
                   __func__, LIB_ACDB_LOADER);
 
+        my_data->acdb_get_audio_cal = (acdb_get_audio_cal_t)dlsym(my_data->acdb_handle,
+                                                  "acdb_loader_get_audio_cal_v2");
+        if (!my_data->acdb_get_audio_cal)
+            ALOGE("%s: Could not find the symbol acdb_get_audio_cal_v2 from %s",
+                  __func__, LIB_ACDB_LOADER);
+
         my_data->acdb_send_voice_cal = (acdb_send_voice_cal_t)dlsym(my_data->acdb_handle,
                                                     "acdb_loader_send_voice_cal");
         if (!my_data->acdb_send_voice_cal)
@@ -1012,16 +1176,19 @@
             ALOGE("%s: dlsym error %s for acdb_loader_init_v2", __func__, dlerror());
             goto acdb_init_fail;
         }
+
+        my_data->acdb_init_v3 = (acdb_init_v3_t)dlsym(my_data->acdb_handle,
+                                                   "acdb_loader_init_v3");
+        if (my_data->acdb_init_v3 == NULL) {
+            ALOGI("%s: dlsym error %s for acdb_loader_init_v3", __func__, dlerror());
+        }
         platform_acdb_init(my_data);
     }
 
 acdb_init_fail:
 
-    set_platform_defaults();
-
-    /* Initialize ACDB and PCM ID's */
-    platform_info_init(PLATFORM_INFO_XML_PATH, my_data);
-
+    /*init a2dp*/
+    audio_extn_a2dp_init(adev);
 
     /* Read one time ssr property */
     audio_extn_spkr_prot_init(adev);
@@ -1284,6 +1451,48 @@
     return ret;
 }
 
+int platform_set_acdb_metainfo_key(void *platform, char *name, int key)
+{
+    struct meta_key_list *key_info;
+    struct platform_data *pdata = (struct platform_data *)platform;
+
+    if (key < 0) {
+        ALOGE("%s: Incorrect Meta key\n", __func__);
+        return -EINVAL;
+    }
+    key_info = (struct meta_key_list *)calloc(1, sizeof(struct meta_key_list));
+    if (!key_info) {
+        ALOGE("%s: Could not allocate memory for key %d", __func__, key);
+        return -ENOMEM;
+    }
+
+    key_info->cal_info.nKey = key;
+    strlcpy(key_info->name, name, sizeof(key_info->name));
+    list_add_tail(&pdata->acdb_meta_key_list, &key_info->list);
+    ALOGD("%s: successfully added module %s and key %d to the list", __func__,
+                   key_info->name, key_info->cal_info.nKey);
+    return 0;
+}
+
+static int platform_get_meta_info_key_from_list(void *platform, char *mod_name)
+{
+    struct listnode *node;
+    struct meta_key_list *key_info;
+    struct platform_data *pdata = (struct platform_data *)platform;
+    int key = 0;
+
+    ALOGV("%s: for module %s", __func__, mod_name);
+    list_for_each(node, &pdata->acdb_meta_key_list) {
+        key_info = node_to_item(node, struct meta_key_list, list);
+        if (strcmp(key_info->name, mod_name) == 0) {
+            key = key_info->cal_info.nKey;
+            ALOGD("%s: Found key %d for module %s", __func__, key, mod_name);
+            break;
+        }
+    }
+    return key;
+}
+
 int platform_get_default_app_type_v2(void *platform, usecase_type_t type, int *app_type)
 {
     ALOGV("%s: platform: %p, type: %d", __func__, platform, type);
@@ -1305,6 +1514,12 @@
         return -EINVAL;
     }
 
+    /*
+     * If speaker protection is enabled, function returns supported
+     * sound device for speaker. Else same sound device is returned.
+     */
+    snd_device = audio_extn_get_spkr_prot_snd_device(snd_device);
+
     if (operator_specific_device_table[snd_device] != NULL)
         return get_operator_specific_device_acdb_id(snd_device);
     else
@@ -1317,7 +1532,7 @@
     int acdb_dev_id, acdb_dev_type;
     int sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE;
 
-    acdb_dev_id = acdb_device_table[audio_extn_get_spkr_prot_snd_device(snd_device)];
+    acdb_dev_id = platform_get_snd_device_acdb_id(snd_device);
     if (acdb_dev_id < 0) {
         ALOGE("%s: Could not find acdb id for device(%d)",
               __func__, snd_device);
@@ -1376,10 +1591,6 @@
     if (my_data->acdb_send_voice_cal == NULL) {
         ALOGE("%s: dlsym error for acdb_send_voice_call", __func__);
     } else {
-        if (out_snd_device == SND_DEVICE_OUT_VOICE_SPEAKER &&
-            audio_extn_spkr_prot_is_enabled())
-            out_snd_device = SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED;
-
         acdb_rx_id = platform_get_snd_device_acdb_id(out_snd_device);
         acdb_tx_id = platform_get_snd_device_acdb_id(in_snd_device);
 
@@ -1410,6 +1621,11 @@
     return 0;
 }
 
+int platform_set_mic_break_det(void *platform __unused, bool enable __unused)
+{
+    return 0;
+}
+
 int platform_get_sample_rate(void *platform __unused, uint32_t *rate __unused)
 {
     return 0;
@@ -1584,6 +1800,12 @@
         } else if (devices == (AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET |
                                AUDIO_DEVICE_OUT_SPEAKER)) {
             snd_device = SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET;
+        } else if ((devices & AUDIO_DEVICE_OUT_SPEAKER) &&
+                   (devices & AUDIO_DEVICE_OUT_ALL_A2DP)) {
+            snd_device = SND_DEVICE_OUT_SPEAKER_AND_BT_A2DP;
+        } else if ((devices & AUDIO_DEVICE_OUT_SPEAKER_SAFE) &&
+                   (devices & AUDIO_DEVICE_OUT_ALL_A2DP)) {
+            snd_device = SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_A2DP;
         } else {
             ALOGE("%s: Invalid combo device(%#x)", __func__, devices);
             goto exit;
@@ -1627,6 +1849,8 @@
                 snd_device = SND_DEVICE_OUT_BT_SCO_WB;
             else
                 snd_device = SND_DEVICE_OUT_BT_SCO;
+        } else if (devices & AUDIO_DEVICE_OUT_ALL_A2DP) {
+                snd_device = SND_DEVICE_OUT_BT_A2DP;
         } else if (devices & AUDIO_DEVICE_OUT_SPEAKER) {
             if (audio_extn_hfp_is_active(adev))
                 snd_device = SND_DEVICE_OUT_VOICE_SPEAKER_HFP;
@@ -1651,7 +1875,13 @@
     } else if (devices & AUDIO_DEVICE_OUT_LINE) {
         snd_device = SND_DEVICE_OUT_LINE;
     } else if (devices & AUDIO_DEVICE_OUT_SPEAKER) {
-        if (my_data->speaker_lr_swap)
+        /*
+         * Perform device switch only if acdb tuning is different between SPEAKER & SPEAKER_REVERSE,
+         * Or there will be a small pause while performing device switch.
+         */
+        if (my_data->speaker_lr_swap &&
+            (acdb_device_table[SND_DEVICE_OUT_SPEAKER] !=
+            acdb_device_table[SND_DEVICE_OUT_SPEAKER_REVERSE]))
             snd_device = SND_DEVICE_OUT_SPEAKER_REVERSE;
         else
             snd_device = SND_DEVICE_OUT_SPEAKER;
@@ -1660,6 +1890,8 @@
             snd_device = SND_DEVICE_OUT_BT_SCO_WB;
         else
             snd_device = SND_DEVICE_OUT_BT_SCO;
+    } else if (devices & AUDIO_DEVICE_OUT_ALL_A2DP) {
+        snd_device = SND_DEVICE_OUT_BT_A2DP;
     } else if (devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
         snd_device = SND_DEVICE_OUT_HDMI ;
     } else if (devices & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET ||
@@ -2086,6 +2318,13 @@
     return ret;
 }
 
+int platform_set_incall_recording_session_channels(void *platform __unused,
+                                             uint32_t channel_count __unused)
+{
+    return 0;
+}
+
+
 int platform_stop_incall_recording_usecase(void *platform __unused)
 {
     return 0;
@@ -2423,3 +2662,51 @@
 {
     return -ENOSYS;
 }
+
+bool platform_sound_trigger_usecase_needs_event(audio_usecase_t uc_id __unused)
+{
+    return false;
+}
+
+bool platform_snd_device_has_speaker(snd_device_t dev __unused) {
+    return false;
+}
+
+bool platform_set_microphone_characteristic(void *platform __unused,
+                                            struct audio_microphone_characteristic_t mic __unused) {
+    return -ENOSYS;
+}
+
+int platform_get_microphones(void *platform __unused,
+                             struct audio_microphone_characteristic_t *mic_array __unused,
+                             size_t *mic_count __unused) {
+    return -ENOSYS;
+}
+
+bool platform_set_microphone_map(void *platform __unused, snd_device_t in_snd_device __unused,
+                                 const struct mic_info *info __unused) {
+    return false;
+}
+
+int platform_get_active_microphones(void *platform __unused, unsigned int channels __unused,
+                                    audio_usecase_t usecase __unused,
+                                    struct audio_microphone_characteristic_t *mic_array __unused,
+                                    size_t *mic_count __unused) {
+    return -ENOSYS;
+}
+
+int platform_set_usb_service_interval(void *platform __unused,
+                                      bool playback __unused,
+                                      unsigned long service_interval __unused,
+                                      bool *reconfig)
+{
+    *reconfig = false;
+    return 0;
+}
+
+int platform_set_backend_cfg(const struct audio_device* adev __unused,
+                             snd_device_t snd_device __unused,
+                             const struct audio_backend_cfg *backend_cfg __unused)
+{
+    return -1;
+}
diff --git a/hal/msm8916/platform.h b/hal/msm8916/platform.h
index c61331a..b69b6c9 100644
--- a/hal/msm8916/platform.h
+++ b/hal/msm8916/platform.h
@@ -56,6 +56,7 @@
     SND_DEVICE_OUT_HANDSET = SND_DEVICE_OUT_BEGIN,
     SND_DEVICE_OUT_SPEAKER,
     SND_DEVICE_OUT_SPEAKER_REVERSE,
+    SND_DEVICE_OUT_SPEAKER_SAFE,
     SND_DEVICE_OUT_LINE,
     SND_DEVICE_OUT_HEADPHONES,
     SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES,
@@ -70,6 +71,9 @@
     SND_DEVICE_OUT_SPEAKER_AND_HDMI,
     SND_DEVICE_OUT_BT_SCO,
     SND_DEVICE_OUT_BT_SCO_WB,
+    SND_DEVICE_OUT_BT_A2DP,
+    SND_DEVICE_OUT_SPEAKER_AND_BT_A2DP,
+    SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_A2DP,
     SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES,
     SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES,
     SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET,
@@ -242,6 +246,8 @@
 #define AFE_PROXY_PLAYBACK_PCM_DEVICE 7
 #define AFE_PROXY_RECORD_PCM_DEVICE 8
 
+#define AUDIO_MAKE_STRING_FROM_ENUM(X)   { #X, X }
+
 #define LIB_CSD_CLIENT "libcsd-client.so"
 /* CSD-CLIENT related functions */
 typedef int (*init_t)();
@@ -277,7 +283,7 @@
     stop_record_t stop_record;
 };
 
-#define PLATFORM_INFO_XML_PATH          "/system/etc/audio_platform_info.xml"
-#define PLATFORM_INFO_XML_BASE_STRING   "/system/etc/audio_platform_info"
+#define PLATFORM_INFO_XML_PATH          "audio_platform_info.xml"
+#define PLATFORM_INFO_XML_BASE_STRING   "audio_platform_info"
 
 #endif // QCOM_AUDIO_PLATFORM_H
diff --git a/hal/msm8960/platform.c b/hal/msm8960/platform.c
index d3c0aea..9e06a0c 100644
--- a/hal/msm8960/platform.c
+++ b/hal/msm8960/platform.c
@@ -20,7 +20,7 @@
 
 #include <stdlib.h>
 #include <dlfcn.h>
-#include <cutils/log.h>
+#include <log/log.h>
 #include <cutils/properties.h>
 #include <audio_hw.h>
 #include <platform_api.h>
@@ -118,6 +118,7 @@
     [SND_DEVICE_OUT_HANDSET] = "handset",
     [SND_DEVICE_OUT_SPEAKER] = "speaker",
     [SND_DEVICE_OUT_SPEAKER_REVERSE] = "speaker-reverse",
+    [SND_DEVICE_OUT_SPEAKER_SAFE] = "speaker-safe",
     [SND_DEVICE_OUT_HEADPHONES] = "headphones",
     [SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES] = "speaker-and-headphones",
     [SND_DEVICE_OUT_VOICE_SPEAKER] = "voice-speaker",
@@ -172,6 +173,7 @@
     [SND_DEVICE_OUT_HANDSET] = 7,
     [SND_DEVICE_OUT_SPEAKER] = 14,
     [SND_DEVICE_OUT_SPEAKER_REVERSE] = 14,
+    [SND_DEVICE_OUT_SPEAKER_SAFE] = 14,
     [SND_DEVICE_OUT_HEADPHONES] = 10,
     [SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES] = 10,
     [SND_DEVICE_OUT_VOICE_SPEAKER] = 14,
@@ -695,7 +697,13 @@
         devices & AUDIO_DEVICE_OUT_WIRED_HEADSET) {
         snd_device = SND_DEVICE_OUT_HEADPHONES;
     } else if (devices & AUDIO_DEVICE_OUT_SPEAKER) {
-        if (my_data->speaker_lr_swap)
+        /*
+         * Perform device switch only if acdb tuning is different between SPEAKER & SPEAKER_REVERSE,
+         * Or there will be a small pause while performing device switch.
+         */
+        if (my_data->speaker_lr_swap &&
+            (acdb_device_table[SND_DEVICE_OUT_SPEAKER] !=
+            acdb_device_table[SND_DEVICE_OUT_SPEAKER_REVERSE]))
             snd_device = SND_DEVICE_OUT_SPEAKER_REVERSE;
         else
             snd_device = SND_DEVICE_OUT_SPEAKER;
@@ -1324,3 +1332,52 @@
 {
     return -ENOSYS;
 }
+
+bool platform_sound_trigger_usecase_needs_event(audio_usecase_t uc_id __unused)
+{
+    return false;
+}
+
+bool platform_snd_device_has_speaker(snd_device_t dev __unused) {
+    return false;
+}
+
+bool platform_set_microphone_characteristic(void *platform __unused,
+                                            struct audio_microphone_characteristic_t mic __unused) {
+    return -ENOSYS;
+}
+
+int platform_get_microphones(void *platform __unused,
+                             struct audio_microphone_characteristic_t *mic_array __unused,
+                             size_t *mic_count __unused) {
+    return -ENOSYS;
+}
+
+int platform_get_active_microphones(void *platform __unused, unsigned int channels __unused,
+                                    audio_usecase_t usecase __unused,
+                                    struct audio_microphone_characteristic_t *mic_array __unused,
+                                    size_t *mic_count __unused) {
+    return -ENOSYS;
+}
+
+int platform_set_usb_service_interval(void *platform __unused,
+                                      bool playback __unused,
+                                      unsigned long service_interval __unused,
+                                      bool *reconfig)
+{
+    *reconfig = false;
+    return 0;
+}
+
+int platform_set_backend_cfg(const struct audio_device* adev __unused,
+                             snd_device_t snd_device __unused,
+                             const struct audio_backend_cfg *backend_cfg __unused)
+{
+    return -1;
+}
+
+int platform_set_acdb_metainfo_key(void *platform __unused,
+                                   char *name __unused,
+                                   int key __unused) {
+    return -ENOSYS;
+}
diff --git a/hal/msm8960/platform.h b/hal/msm8960/platform.h
index afbc7b3..b0bd98a 100644
--- a/hal/msm8960/platform.h
+++ b/hal/msm8960/platform.h
@@ -39,6 +39,7 @@
     SND_DEVICE_OUT_HANDSET = SND_DEVICE_OUT_BEGIN,
     SND_DEVICE_OUT_SPEAKER,
     SND_DEVICE_OUT_SPEAKER_REVERSE,
+    SND_DEVICE_OUT_SPEAKER_SAFE,
     SND_DEVICE_OUT_HEADPHONES,
     SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES,
     SND_DEVICE_OUT_VOICE_SPEAKER,
@@ -47,6 +48,8 @@
     SND_DEVICE_OUT_SPEAKER_AND_HDMI,
     SND_DEVICE_OUT_BT_SCO,
     SND_DEVICE_OUT_BT_SCO_WB,
+    SND_DEVICE_OUT_BT_A2DP,
+    SND_DEVICE_OUT_SPEAKER_AND_BT_A2DP,
     SND_DEVICE_OUT_VOICE_HANDSET_TMUS,
     SND_DEVICE_OUT_VOICE_HANDSET,
     SND_DEVICE_OUT_VOICE_HAC_HANDSET,
@@ -144,5 +147,7 @@
 
 #define DEVICE_NAME_MAX_SIZE 128
 
+#define AUDIO_MAKE_STRING_FROM_ENUM(X)   { #X, X }
+
 
 #endif // QCOM_AUDIO_PLATFORM_H
diff --git a/hal/msm8974/hw_info.c b/hal/msm8974/hw_info.c
index 6723362..5c8d3ba 100644
--- a/hal/msm8974/hw_info.c
+++ b/hal/msm8974/hw_info.c
@@ -19,7 +19,7 @@
 #define LOG_NDDEBUG 0
 
 #include <stdlib.h>
-#include <cutils/log.h>
+#include <log/log.h>
 #include "audio_hw.h"
 #include "platform.h"
 #include "audio_extn.h"
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index 6560d14..429291c 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -21,7 +21,7 @@
 #include <dlfcn.h>
 #include <pthread.h>
 #include <unistd.h>
-#include <cutils/log.h>
+#include <log/log.h>
 #include <cutils/str_parms.h>
 #include <cutils/properties.h>
 #include <audio_hw.h>
@@ -30,10 +30,13 @@
 #include "platform.h"
 #include "audio_extn.h"
 #include <linux/msm_audio.h>
-#if defined (PLATFORM_MSM8996) || (PLATFORM_MSM8998)
+#if defined (PLATFORM_MSM8996) || (PLATFORM_MSM8998) || (PLATFORM_SDM845)
 #include <sound/devdep_params.h>
 #endif
 
+#include "maxxaudio.h"
+#include <resolv.h>
+
 #define MIXER_XML_DEFAULT_PATH "mixer_paths.xml"
 #define MIXER_XML_BASE_STRING "mixer_paths"
 #define TOMTOM_8226_SND_CARD_NAME "msm8226-tomtom-snd-card"
@@ -102,9 +105,29 @@
     char be_name[BE_DAI_NAME_MAX_LENGTH];
 };
 
+struct snd_device_to_mic_map {
+    struct mic_info microphones[AUDIO_MICROPHONE_MAX_COUNT];
+    size_t mic_count;
+};
+
 static struct listnode operator_info_list;
 static struct listnode *operator_specific_device_table[SND_DEVICE_MAX];
 
+#define AUDIO_PARAMETER_KEY_AUD_CALDATA "cal_data"
+
+typedef struct acdb_audio_cal_cfg {
+    uint32_t             persist;
+    uint32_t             snd_dev_id;
+    audio_devices_t      dev_id;
+    int32_t              acdb_dev_id;
+    uint32_t             app_type;
+    uint32_t             topo_id;
+    uint32_t             sampling_rate;
+    uint32_t             cal_type;
+    uint32_t             module_id;
+    uint32_t             param_id;
+} acdb_audio_cal_cfg_t;
+
 /* Audio calibration related functions */
 typedef void (*acdb_send_audio_cal_v3_t)(int, int, int, int, int);
 
@@ -120,7 +143,7 @@
     bool speaker_lr_swap;
 
     void *acdb_handle;
-#if defined (PLATFORM_MSM8994) || (PLATFORM_MSM8996) || (PLATFORM_MSM8998)
+#if defined (PLATFORM_MSM8994) || (PLATFORM_MSM8996) || (PLATFORM_MSM8998) || (PLATFORM_SDM845)
     acdb_init_v2_cvd_t acdb_init;
 #elif defined (PLATFORM_MSM8084)
     acdb_init_v2_t acdb_init;
@@ -130,6 +153,7 @@
     acdb_deallocate_t          acdb_deallocate;
     acdb_send_audio_cal_t      acdb_send_audio_cal;
     acdb_send_audio_cal_v3_t   acdb_send_audio_cal_v3;
+    acdb_set_audio_cal_t       acdb_set_audio_cal;
     acdb_send_voice_cal_t      acdb_send_voice_cal;
     acdb_reload_vocvoltable_t  acdb_reload_vocvoltable;
     acdb_send_gain_dep_cal_t   acdb_send_gain_dep_cal;
@@ -145,6 +169,10 @@
     int max_mic_count;
 
     void *hw_info;
+
+    uint32_t declared_mic_count;
+    struct audio_microphone_characteristic_t microphones[AUDIO_MICROPHONE_MAX_COUNT];
+    struct snd_device_to_mic_map mic_map[SND_DEVICE_MAX];
 };
 
 static int pcm_device_table[AUDIO_USECASE_MAX][2] = {
@@ -205,6 +233,9 @@
                                      AUDIO_PLAYBACK_VOIP_PCM_DEVICE},
     [USECASE_AUDIO_RECORD_VOIP] = {AUDIO_RECORD_VOIP_PCM_DEVICE,
                                    AUDIO_RECORD_VOIP_PCM_DEVICE},
+
+    [USECASE_INCALL_MUSIC_UPLINK] = {INCALL_MUSIC_UPLINK_PCM_DEVICE,
+                                     INCALL_MUSIC_UPLINK_PCM_DEVICE},
 };
 
 /* Array to store sound devices */
@@ -230,6 +261,9 @@
     [SND_DEVICE_OUT_SPEAKER_AND_HDMI] = "speaker-and-hdmi",
     [SND_DEVICE_OUT_BT_SCO] = "bt-sco-headset",
     [SND_DEVICE_OUT_BT_SCO_WB] = "bt-sco-headset-wb",
+    [SND_DEVICE_OUT_BT_A2DP] = "bt-a2dp",
+    [SND_DEVICE_OUT_SPEAKER_AND_BT_A2DP] = "speaker-and-bt-a2dp",
+    [SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_A2DP] = "speaker-safe-and-bt-a2dp",
     [SND_DEVICE_OUT_VOICE_HANDSET_TMUS] = "voice-handset-tmus",
     [SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES] = "voice-tty-full-headphones",
     [SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = "voice-tty-vco-headphones",
@@ -240,6 +274,7 @@
     [SND_DEVICE_OUT_USB_HEADSET] = "usb-headset",
     [SND_DEVICE_OUT_VOICE_USB_HEADSET] = "usb-headset",
     [SND_DEVICE_OUT_USB_HEADPHONES] = "usb-headphones",
+    [SND_DEVICE_OUT_USB_HEADSET_SPEC] = "usb-headset",
     [SND_DEVICE_OUT_VOICE_USB_HEADPHONES] = "usb-headphones",
     [SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET] = "speaker-and-usb-headphones",
     [SND_DEVICE_OUT_SPEAKER_SAFE_AND_USB_HEADSET] = "speaker-safe-and-usb-headphones",
@@ -247,7 +282,9 @@
     [SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED] = "voice-speaker-protected",
     [SND_DEVICE_OUT_VOICE_SPEAKER_HFP] = "voice-speaker-hfp",
     [SND_DEVICE_OUT_SPEAKER_AND_BT_SCO] = "speaker-and-bt-sco",
+    [SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO] = "speaker-safe-and-bt-sco",
     [SND_DEVICE_OUT_SPEAKER_AND_BT_SCO_WB] = "speaker-and-bt-sco-wb",
+    [SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO_WB] = "speaker-safe-and-bt-sco-wb",
 
     /* Capture sound devices */
     [SND_DEVICE_IN_HANDSET_MIC] = "handset-mic",
@@ -343,7 +380,12 @@
     [SND_DEVICE_OUT_HDMI] = 18,
     [SND_DEVICE_OUT_SPEAKER_AND_HDMI] = 15,
     [SND_DEVICE_OUT_BT_SCO] = 22,
+    [SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO] = 14,
     [SND_DEVICE_OUT_BT_SCO_WB] = 39,
+    [SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO_WB] = 14,
+    [SND_DEVICE_OUT_BT_A2DP] = 20,
+    [SND_DEVICE_OUT_SPEAKER_AND_BT_A2DP] = 14,
+    [SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_A2DP] = 14,
     [SND_DEVICE_OUT_VOICE_HANDSET_TMUS] = ACDB_ID_VOICE_HANDSET_TMUS,
     [SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES] = 17,
     [SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = 17,
@@ -354,6 +396,7 @@
     [SND_DEVICE_OUT_USB_HEADSET] = 45,
     [SND_DEVICE_OUT_VOICE_USB_HEADSET] = 45,
     [SND_DEVICE_OUT_USB_HEADPHONES] = 45,
+    [SND_DEVICE_OUT_USB_HEADSET_SPEC] = 45,
     [SND_DEVICE_OUT_VOICE_USB_HEADPHONES] = 45,
     [SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET] = 14,
     [SND_DEVICE_OUT_SPEAKER_SAFE_AND_USB_HEADSET] = 14,
@@ -463,7 +506,12 @@
     {TO_NAME_INDEX(SND_DEVICE_OUT_HDMI)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_HDMI)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_BT_SCO)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_BT_SCO_WB)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO_WB)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_BT_A2DP)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_BT_A2DP)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_A2DP)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_HANDSET_TMUS)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_HAC_HANDSET)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES)},
@@ -481,6 +529,7 @@
     {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_SAFE_AND_USB_HEADSET)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_PROTECTED)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_USB_HEADSET_SPEC)},
 
     /* in */
     {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_MIC)},
@@ -587,6 +636,8 @@
     {TO_NAME_INDEX(USECASE_AUDIO_DSM_FEEDBACK)},
     {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_VOIP)},
     {TO_NAME_INDEX(USECASE_AUDIO_RECORD_VOIP)},
+    {TO_NAME_INDEX(USECASE_INCALL_MUSIC_UPLINK)},
+    {TO_NAME_INDEX(USECASE_AUDIO_A2DP_ABR_FEEDBACK)},
 };
 
 static const struct name_to_index usecase_type_index[USECASE_TYPE_MAX] = {
@@ -720,13 +771,146 @@
 
 inline bool platform_supports_app_type_cfg()
 {
-#ifdef PLATFORM_MSM8998
+#if defined (PLATFORM_MSM8998) || (PLATFORM_SDM845)
     return true;
 #else
     return false;
 #endif
 }
 
+static int parse_audiocal_cfg(struct str_parms *parms, acdb_audio_cal_cfg_t *cal)
+{
+    int err;
+    char value[64];
+    int ret = 0;
+
+    if (parms == NULL || cal == NULL)
+        return ret;
+
+    err = str_parms_get_str(parms, "cal_persist", value, sizeof(value));
+    if (err >= 0) {
+        str_parms_del(parms, "cal_persist");
+        cal->persist = (uint32_t)strtoul(value, NULL, 0);
+        ret = ret | 0x1;
+    }
+    err = str_parms_get_str(parms, "cal_apptype", value, sizeof(value));
+    if (err >= 0) {
+        str_parms_del(parms, "cal_apptype");
+        cal->app_type = (uint32_t)strtoul(value, NULL, 0);
+        ret = ret | 0x2;
+    }
+    err = str_parms_get_str(parms, "cal_caltype", value, sizeof(value));
+    if (err >= 0) {
+        str_parms_del(parms, "cal_caltype");
+        cal->cal_type = (uint32_t)strtoul(value, NULL, 0);
+        ret = ret | 0x4;
+    }
+    err = str_parms_get_str(parms, "cal_samplerate", value, sizeof(value));
+    if (err >= 0) {
+        str_parms_del(parms, "cal_samplerate");
+        cal->sampling_rate = (uint32_t)strtoul(value, NULL, 0);
+        ret = ret | 0x8;
+    }
+    err = str_parms_get_str(parms, "cal_devid", value, sizeof(value));
+    if (err >= 0) {
+        str_parms_del(parms, "cal_devid");
+        cal->dev_id = (uint32_t)strtoul(value, NULL, 0);
+        ret = ret | 0x10;
+    }
+    err = str_parms_get_str(parms, "cal_snddevid", value, sizeof(value));
+    if (err >= 0) {
+        str_parms_del(parms, "cal_snddevid");
+        cal->snd_dev_id = (uint32_t)strtoul(value, NULL, 0);
+        ret = ret | 0x20;
+    }
+    err = str_parms_get_str(parms, "cal_topoid", value, sizeof(value));
+    if (err >= 0) {
+        str_parms_del(parms, "cal_topoid");
+        cal->topo_id = (uint32_t)strtoul(value, NULL, 0);
+        ret = ret | 0x40;
+    }
+    err = str_parms_get_str(parms, "cal_moduleid", value, sizeof(value));
+    if (err >= 0) {
+        str_parms_del(parms, "cal_moduleid");
+        cal->module_id = (uint32_t)strtoul(value, NULL, 0);
+        ret = ret | 0x80;
+    }
+    err = str_parms_get_str(parms, "cal_paramid", value, sizeof(value));
+    if (err >= 0) {
+        str_parms_del(parms, "cal_paramid");
+        cal->param_id = (uint32_t)strtoul(value, NULL, 0);
+        ret = ret | 0x100;
+    }
+    return ret;
+}
+
+static void set_audiocal(void *platform, struct str_parms *parms, char *value, int len)
+{
+    struct platform_data *my_data = (struct platform_data *)platform;
+    acdb_audio_cal_cfg_t cal;
+    uint8_t *dptr = NULL;
+    int32_t dlen = 0;
+    int err ,ret;
+
+    if (value == NULL || platform == NULL || parms == NULL) {
+        ALOGE("[%s] received null pointer, failed", __func__);
+        goto done_key_audcal;
+    }
+
+    memset(&cal, 0, sizeof(acdb_audio_cal_cfg_t));
+    /* parse audio calibration keys */
+    ret = parse_audiocal_cfg(parms, &cal);
+
+    /* handle audio calibration data now */
+    err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_AUD_CALDATA, value, len);
+    if (err >= 0) {
+        str_parms_del(parms, AUDIO_PARAMETER_KEY_AUD_CALDATA);
+        dlen = strlen(value);
+        if (dlen <= 0) {
+            ALOGE("[%s] null data received", __func__);
+            goto done_key_audcal;
+        }
+        /*
+           The base64 encoded string is always larger than the binary data,
+           so b64_pton will always output less data than provided (around 1/3
+           less than the input data). That's why we can allocate input buffer
+           length and then get function work.
+        */
+        dptr = (uint8_t *)calloc(dlen, sizeof(uint8_t));
+        if (dptr == NULL) {
+            ALOGE("[%s] memory allocation failed for %d", __func__, dlen);
+            goto done_key_audcal;
+        }
+        dlen = b64_pton(value, dptr, dlen);
+        if (dlen <= 0) {
+            ALOGE("[%s] data decoding failed %d", __func__, dlen);
+            goto done_key_audcal;
+        }
+
+        if (cal.dev_id) {
+            if (audio_is_input_device(cal.dev_id)) {
+                cal.snd_dev_id = platform_get_input_snd_device(platform, cal.dev_id);
+            } else {
+                cal.snd_dev_id = platform_get_output_snd_device(platform, cal.dev_id);
+            }
+        }
+        cal.acdb_dev_id = platform_get_snd_device_acdb_id(cal.snd_dev_id);
+        ALOGD("Setting audio calibration for snd_device(%d) acdb_id(%d)",
+                cal.snd_dev_id, cal.acdb_dev_id);
+        if (cal.acdb_dev_id == -EINVAL) {
+            ALOGE("[%s] Invalid acdb_device id %d for snd device id %d",
+                       __func__, cal.acdb_dev_id, cal.snd_dev_id);
+            goto done_key_audcal;
+        }
+        if (my_data->acdb_set_audio_cal) {
+            ret = my_data->acdb_set_audio_cal((void *)&cal, (void *)dptr, dlen);
+        }
+    }
+done_key_audcal:
+    if (dptr != NULL)
+        free(dptr);
+}
+
 bool platform_send_gain_dep_cal(void *platform, int level)
 {
     bool ret_val = false;
@@ -770,10 +954,7 @@
                 else
                     app_type = DEFAULT_APP_TYPE_RX_PATH;
 
-                if (audio_extn_spkr_prot_is_enabled())
-                    acdb_dev_id = audio_extn_spkr_prot_get_acdb_id(usecase->out_snd_device);
-                else
-                    acdb_dev_id = acdb_device_table[usecase->out_snd_device];
+                acdb_dev_id = platform_get_snd_device_acdb_id(usecase->out_snd_device);
 
                 if (!my_data->acdb_send_gain_dep_cal(acdb_dev_id, app_type,
                                                      acdb_dev_type, mode, level)) {
@@ -1031,11 +1212,20 @@
         strdup("speaker-and-usb-headphones");
     backend_tag_table[SND_DEVICE_OUT_SPEAKER_SAFE_AND_USB_HEADSET] =
         strdup("speaker-safe-and-usb-headphones");
+    backend_tag_table[SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO] =
+        strdup("speaker-safe-and-bt-sco"),
+    backend_tag_table[SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO_WB] =
+        strdup("speaker-safe-and-bt-sco-wb"),
     backend_tag_table[SND_DEVICE_IN_USB_HEADSET_MIC] = strdup("usb-headset-mic");
     backend_tag_table[SND_DEVICE_IN_VOICE_USB_HEADSET_MIC] = strdup("usb-headset-mic");
     backend_tag_table[SND_DEVICE_IN_UNPROCESSED_USB_HEADSET_MIC] = strdup("usb-headset-mic");
     backend_tag_table[SND_DEVICE_IN_VOICE_RECOG_USB_HEADSET_MIC] = strdup("usb-headset-mic");
     backend_tag_table[SND_DEVICE_IN_USB_HEADSET_MIC_AEC] = strdup("usb-headset-mic");
+    backend_tag_table[SND_DEVICE_OUT_BT_A2DP] = strdup("bt-a2dp");
+    backend_tag_table[SND_DEVICE_OUT_SPEAKER_AND_BT_A2DP] = strdup("speaker-and-bt-a2dp");
+    backend_tag_table[SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_A2DP] = strdup("speaker-safe-and-bt-a2dp");
+    backend_tag_table[SND_DEVICE_OUT_USB_HEADSET_SPEC] = strdup("usb-headset");
+
     hw_interface_table[SND_DEVICE_OUT_HANDSET] = strdup("SLIMBUS_0_RX");
     hw_interface_table[SND_DEVICE_OUT_SPEAKER] = strdup("SLIMBUS_0_RX");
     hw_interface_table[SND_DEVICE_OUT_SPEAKER_REVERSE] = strdup("SLIMBUS_0_RX");
@@ -1055,6 +1245,11 @@
     hw_interface_table[SND_DEVICE_OUT_SPEAKER_AND_HDMI] = strdup("SLIMBUS_0_RX-and-HDMI_RX");
     hw_interface_table[SND_DEVICE_OUT_BT_SCO] = strdup("SEC_AUX_PCM_RX");
     hw_interface_table[SND_DEVICE_OUT_BT_SCO_WB] = strdup("SEC_AUX_PCM_RX");
+    hw_interface_table[SND_DEVICE_OUT_BT_A2DP] = strdup("SLIMBUS_7_RX");
+    hw_interface_table[SND_DEVICE_OUT_SPEAKER_AND_BT_A2DP] =
+        strdup("SLIMBUS_0_RX-and-SLIMBUS_7_RX");
+    hw_interface_table[SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_A2DP] =
+        strdup("SLIMBUS_0_RX-and-SLIMBUS_7_RX");
     hw_interface_table[SND_DEVICE_OUT_VOICE_HANDSET_TMUS] = strdup("SLIMBUS_0_RX");
     hw_interface_table[SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES] = strdup("SLIMBUS_0_RX");
     hw_interface_table[SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = strdup("SLIMBUS_0_RX");
@@ -1065,11 +1260,17 @@
     hw_interface_table[SND_DEVICE_OUT_VOICE_USB_HEADSET] = strdup("USB_AUDIO_RX");
     hw_interface_table[SND_DEVICE_OUT_USB_HEADPHONES] = strdup("USB_AUDIO_RX");
     hw_interface_table[SND_DEVICE_OUT_VOICE_USB_HEADPHONES] = strdup("USB_AUDIO_RX");
+    hw_interface_table[SND_DEVICE_OUT_USB_HEADSET_SPEC] = strdup("USB_AUDIO_RX");
     hw_interface_table[SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET] = strdup("SLIMBUS_0_RX-and-USB_AUDIO_RX");
     hw_interface_table[SND_DEVICE_OUT_SPEAKER_SAFE_AND_USB_HEADSET] = strdup("SLIMBUS_0_RX-and-USB_AUDIO_RX");
     hw_interface_table[SND_DEVICE_OUT_VOICE_TX] = strdup("AFE_PCM_RX");
     hw_interface_table[SND_DEVICE_OUT_SPEAKER_PROTECTED] = strdup("SLIMBUS_0_RX");
     hw_interface_table[SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED] = strdup("SLIMBUS_0_RX");
+    hw_interface_table[SND_DEVICE_OUT_VOICE_SPEAKER_HFP] = strdup("SLIMBUS_0_RX");
+    hw_interface_table[SND_DEVICE_OUT_SPEAKER_AND_BT_SCO] = strdup("SLIMBUS_0_RX-and-SEC_AUX_PCM_RX");
+    hw_interface_table[SND_DEVICE_OUT_SPEAKER_AND_BT_SCO_WB] = strdup("SLIMBUS_0_RX-and-SEC_AUX_PCM_RX");
+    hw_interface_table[SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO] = strdup("QUAT_TDM_RX_0-and-SLIMBUS_7_RX"),
+    hw_interface_table[SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO_WB] = strdup("QUAT_TDM_RX_0-and-SLIMBUS_7_RX"),
     hw_interface_table[SND_DEVICE_IN_USB_HEADSET_MIC] = strdup("USB_AUDIO_TX");
     hw_interface_table[SND_DEVICE_IN_VOICE_USB_HEADSET_MIC] = strdup("USB_AUDIO_TX");
     hw_interface_table[SND_DEVICE_IN_USB_HEADSET_MIC_AEC] =  strdup("USB_AUDIO_TX");
@@ -1077,13 +1278,22 @@
     hw_interface_table[SND_DEVICE_IN_VOICE_RECOG_USB_HEADSET_MIC] = strdup("USB_AUDIO_TX");
     hw_interface_table[SND_DEVICE_IN_VOICE_TTY_FULL_USB_MIC] = strdup("USB_AUDIO_TX");
     hw_interface_table[SND_DEVICE_IN_VOICE_TTY_HCO_USB_MIC] = strdup("USB_AUDIO_TX");
-    hw_interface_table[SND_DEVICE_IN_SPEAKER_MIC] = strdup("SLIMBUS_0_TX");
     hw_interface_table[SND_DEVICE_IN_HANDSET_MIC] = strdup("SLIMBUS_0_TX");
+    hw_interface_table[SND_DEVICE_IN_HANDSET_MIC_AEC] = strdup("SLIMBUS_0_TX");
+    hw_interface_table[SND_DEVICE_IN_HANDSET_MIC_NS] = strdup("SLIMBUS_0_TX");
+    hw_interface_table[SND_DEVICE_IN_HANDSET_MIC_AEC_NS] = strdup("SLIMBUS_0_TX");
+    hw_interface_table[SND_DEVICE_IN_HANDSET_DMIC] = strdup("SLIMBUS_0_TX");
+    hw_interface_table[SND_DEVICE_IN_HANDSET_DMIC_AEC] = strdup("SLIMBUS_0_TX");
+    hw_interface_table[SND_DEVICE_IN_HANDSET_DMIC_NS] = strdup("SLIMBUS_0_TX");
+    hw_interface_table[SND_DEVICE_IN_HANDSET_DMIC_AEC_NS] = strdup("SLIMBUS_0_TX");
+    hw_interface_table[SND_DEVICE_IN_HANDSET_DMIC_STEREO] = strdup("SLIMBUS_0_TX");
     hw_interface_table[SND_DEVICE_IN_HEADSET_MIC] = strdup("SLIMBUS_0_TX");
-    hw_interface_table[SND_DEVICE_IN_UNPROCESSED_MIC] = strdup("SLIMBUS_0_TX");
+    hw_interface_table[SND_DEVICE_IN_HEADSET_MIC_AEC] = strdup("SLIMBUS_0_TX");
     hw_interface_table[SND_DEVICE_IN_CAMCORDER_MIC] = strdup("SLIMBUS_0_TX");
     hw_interface_table[SND_DEVICE_IN_VOICE_REC_MIC] = strdup("SLIMBUS_0_TX");
     hw_interface_table[SND_DEVICE_IN_VOICE_REC_MIC_NS] = strdup("SLIMBUS_0_TX");
+    hw_interface_table[SND_DEVICE_IN_VOICE_REC_MIC_AEC] = strdup("SLIMBUS_0_TX");
+    hw_interface_table[SND_DEVICE_IN_VOICE_REC_MIC_AEC_NS] = strdup("SLIMBUS_0_TX");
     hw_interface_table[SND_DEVICE_IN_VOICE_REC_DMIC_STEREO] = strdup("SLIMBUS_0_TX");
     hw_interface_table[SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE] = strdup("SLIMBUS_0_TX");
     hw_interface_table[SND_DEVICE_IN_UNPROCESSED_MIC] = strdup("SLIMBUS_0_TX");
@@ -1099,8 +1309,28 @@
     hw_interface_table[SND_DEVICE_IN_SPEAKER_DMIC_AEC] = strdup("SLIMBUS_0_TX");
     hw_interface_table[SND_DEVICE_IN_SPEAKER_DMIC_NS] = strdup("SLIMBUS_0_TX");
     hw_interface_table[SND_DEVICE_IN_SPEAKER_DMIC_AEC_NS] = strdup("SLIMBUS_0_TX");
+    hw_interface_table[SND_DEVICE_IN_SPEAKER_DMIC_STEREO] = strdup("SLIMBUS_0_TX");
+    hw_interface_table[SND_DEVICE_IN_VOICE_DMIC] = strdup("SLIMBUS_0_TX");
+    hw_interface_table[SND_DEVICE_IN_VOICE_DMIC_TMUS] = strdup("SLIMBUS_0_TX");
     hw_interface_table[SND_DEVICE_IN_VOICE_SPEAKER_MIC] = strdup("SLIMBUS_0_TX");
+    hw_interface_table[SND_DEVICE_IN_VOICE_SPEAKER_DMIC] = strdup("SLIMBUS_0_TX");
+    hw_interface_table[SND_DEVICE_IN_VOICE_SPEAKER_MIC_HFP] = strdup("SLIMBUS_0_TX");
     hw_interface_table[SND_DEVICE_IN_VOICE_HEADSET_MIC] = strdup("SLIMBUS_0_TX");
+    hw_interface_table[SND_DEVICE_IN_VOICE_TTY_FULL_HEADSET_MIC] = strdup("SLIMBUS_0_TX");
+    hw_interface_table[SND_DEVICE_IN_VOICE_TTY_VCO_HANDSET_MIC] = strdup("SLIMBUS_0_TX");
+    hw_interface_table[SND_DEVICE_IN_VOICE_TTY_HCO_HEADSET_MIC] = strdup("SLIMBUS_0_TX");
+    hw_interface_table[SND_DEVICE_IN_VOICE_REC_HEADSET_MIC] = strdup("SLIMBUS_0_TX");
+    hw_interface_table[SND_DEVICE_IN_BT_SCO_MIC] = strdup("SEC_AUX_PCM_TX");
+    hw_interface_table[SND_DEVICE_IN_BT_SCO_MIC_NREC] = strdup("SEC_AUX_PCM_TX");
+    hw_interface_table[SND_DEVICE_IN_BT_SCO_MIC_WB] = strdup("SEC_AUX_PCM_TX");
+    hw_interface_table[SND_DEVICE_IN_BT_SCO_MIC_WB_NREC] = strdup("SEC_AUX_PCM_TX");
+    hw_interface_table[SND_DEVICE_IN_VOICE_RX] = strdup("AFE_PCM_TX");
+    hw_interface_table[SND_DEVICE_IN_THREE_MIC] = strdup("SLIMBUS_0_TX");
+    hw_interface_table[SND_DEVICE_IN_QUAD_MIC] = strdup("SLIMBUS_0_TX");
+    hw_interface_table[SND_DEVICE_IN_HANDSET_TMIC] = strdup("SLIMBUS_0_TX");
+    hw_interface_table[SND_DEVICE_IN_HANDSET_QMIC] = strdup("SLIMBUS_0_TX");
+    hw_interface_table[SND_DEVICE_IN_HANDSET_TMIC_AEC] = strdup("SLIMBUS_0_TX");
+    hw_interface_table[SND_DEVICE_IN_HANDSET_QMIC_AEC] = strdup("SLIMBUS_0_TX");
     my_data->max_mic_count = PLATFORM_DEFAULT_MIC_COUNT;
 }
 
@@ -1146,7 +1376,7 @@
         return 0;
     }
 
-#if defined (PLATFORM_MSM8994) || (PLATFORM_MSM8996) || (PLATFORM_MSM8998)
+#if defined (PLATFORM_MSM8994) || (PLATFORM_MSM8996) || (PLATFORM_MSM8998) || (PLATFORM_SDM845)
     char *cvd_version = calloc(1, MAX_CVD_VERSION_STRING_SIZE);
     if (!cvd_version)
         ALOGE("failed to allocate cvd_version");
@@ -1262,6 +1492,44 @@
     return 0;
 }
 
+static void configure_flicker_sensor_input(struct mixer *mixer)
+{
+    struct mixer_ctl *ctl;
+    const char* ctl1 = "AIF3_CAP Mixer SLIM TX2";
+    int setting1 = 1;
+    const char* ctl2 = "CDC_IF TX2 MUX";
+    const char* setting2 = "DEC2";
+    const char* ctl3 = "SLIM_1_TX Channels";
+    const char* setting3 = "One";
+    const char* ctl4 = "ADC MUX2";
+    const char* setting4 = "AMIC";
+    const char* ctl5 = "AMIC MUX2";
+    const char* setting5 = "ADC1";
+    const char* ctl6 = "DEC2 Volume";
+    int setting6 = 84;
+    const char* ctl7 = "MultiMedia9 Mixer SLIM_1_TX";
+    int setting7 = 1;
+    const char* ctl8 = "SLIM_1_TX SampleRate";
+    const char* setting8 = "KHZ_8";
+
+    ctl = mixer_get_ctl_by_name(mixer, ctl1);
+    mixer_ctl_set_value(ctl, 0, setting1);
+    ctl = mixer_get_ctl_by_name(mixer, ctl2);
+    mixer_ctl_set_enum_by_string(ctl, setting2);
+    ctl = mixer_get_ctl_by_name(mixer, ctl3);
+    mixer_ctl_set_enum_by_string(ctl, setting3);
+    ctl = mixer_get_ctl_by_name(mixer, ctl4);
+    mixer_ctl_set_enum_by_string(ctl, setting4);
+    ctl = mixer_get_ctl_by_name(mixer, ctl5);
+    mixer_ctl_set_enum_by_string(ctl, setting5);
+    ctl = mixer_get_ctl_by_name(mixer, ctl6);
+    mixer_ctl_set_value(ctl, 0, setting6);
+    ctl = mixer_get_ctl_by_name(mixer, ctl7);
+    mixer_ctl_set_value(ctl, 0, setting7);
+    ctl = mixer_get_ctl_by_name(mixer, ctl8);
+    mixer_ctl_set_enum_by_string(ctl, setting8);
+}
+
 void *platform_init(struct audio_device *adev)
 {
     char value[PROPERTY_VALUE_MAX];
@@ -1343,6 +1611,7 @@
 
     audio_extn_utils_get_platform_info(snd_card_name, platform_info_file);
 
+    my_data->declared_mic_count = 0;
     /* Initialize platform specific ids and/or backends*/
     platform_info_init(platform_info_file, my_data);
 
@@ -1369,7 +1638,7 @@
         dual_mic_config = true;
     }
 
-    my_data->source_mic_type = SOURCE_DUAL_MIC;
+    my_data->source_mic_type = 0;
 
     my_data->fluence_in_spkr_mode = false;
     my_data->fluence_in_voice_call = false;
@@ -1472,7 +1741,11 @@
             ALOGV("%s: Could not find the symbol acdb_loader_send_gain_dep_cal from %s",
                   __func__, LIB_ACDB_LOADER);
 
-#if defined (PLATFORM_MSM8994) || (PLATFORM_MSM8996) || (PLATFORM_MSM8998)
+#if defined (FLICKER_SENSOR_INPUT)
+        configure_flicker_sensor_input(adev->mixer);
+#endif
+
+#if defined (PLATFORM_MSM8994) || (PLATFORM_MSM8996) || (PLATFORM_MSM8998) || (PLATFORM_SDM845)
         acdb_init_v2_cvd_t acdb_init_local;
         acdb_init_local = (acdb_init_v2_cvd_t)dlsym(my_data->acdb_handle,
                                               "acdb_loader_init_v2");
@@ -1506,6 +1779,12 @@
             ALOGE("%s: Could not find the symbol acdb_get_default_app_type from %s",
                   __func__, LIB_ACDB_LOADER);
 
+        my_data->acdb_set_audio_cal = (acdb_set_audio_cal_t)dlsym(my_data->acdb_handle,
+                                                    "acdb_loader_set_audio_cal_v2");
+        if (!my_data->acdb_set_audio_cal)
+            ALOGE("%s: Could not find the symbol acdb_set_audio_cal_v2 from %s",
+                  __func__, LIB_ACDB_LOADER);
+
         int result = acdb_init(adev->snd_card);
         if (!result) {
             my_data->acdb_initialized = true;
@@ -1519,6 +1798,9 @@
     /* init usb */
     audio_extn_usb_init(adev);
 
+    /* init a2dp */
+    audio_extn_a2dp_init(adev);
+
     audio_extn_spkr_prot_init(adev);
 
     audio_extn_hwdep_cal_send(adev->snd_card, my_data->acdb_handle);
@@ -1629,6 +1911,7 @@
         hw_info_append_hw_type(my_data->hw_info, snd_device, device_name);
     } else {
         strlcpy(device_name, "none", DEVICE_NAME_MAX_SIZE);
+        return -EINVAL;
     }
 
     return 0;
@@ -1790,6 +2073,12 @@
         return -EINVAL;
     }
 
+    /*
+     * If speaker protection is enabled, function returns supported
+     * sound device for speaker. Else same sound device is returned.
+     */
+    snd_device = audio_extn_get_spkr_prot_snd_device(snd_device);
+
     if (operator_specific_device_table[snd_device] != NULL)
         return get_operator_specific_device_acdb_id(snd_device);
     else
@@ -1836,7 +2125,7 @@
     if (platform_supports_app_type_cfg()) // use v2 instead
         return -ENOSYS;
 
-    acdb_dev_id = acdb_device_table[audio_extn_get_spkr_prot_snd_device(snd_device)];
+    acdb_dev_id = platform_get_snd_device_acdb_id(snd_device);
     if (acdb_dev_id < 0) {
         ALOGE("%s: Could not find acdb id for device(%d)",
               __func__, snd_device);
@@ -1860,20 +2149,18 @@
 {
     struct platform_data *my_data = (struct platform_data *)platform;
     int acdb_dev_id, acdb_dev_type;
-    int snd_device = SND_DEVICE_OUT_SPEAKER;
+    int snd_device = usecase->out_snd_device;
     int new_snd_device[SND_DEVICE_OUT_END] = {0};
     int i, num_devices = 1;
 
     if (!platform_supports_app_type_cfg()) // use v1 instead
         return -ENOSYS;
 
-    if (usecase->type == PCM_PLAYBACK)
-        snd_device = usecase->out_snd_device;
-    else if (usecase->type == PCM_CAPTURE)
+    if ((usecase->type == PCM_HFP_CALL) || (usecase->type == PCM_CAPTURE))
         snd_device = usecase->in_snd_device;
 
     // skipped over get_spkr_prot_device
-    acdb_dev_id = acdb_device_table[snd_device];
+    acdb_dev_id = platform_get_snd_device_acdb_id(snd_device);
     if (acdb_dev_id < 0) {
         ALOGE("%s: Could not find acdb id for device(%d)",
               __func__, snd_device);
@@ -1886,7 +2173,7 @@
     }
 
     for (i = 0; i < num_devices; i++) {
-        acdb_dev_id = acdb_device_table[new_snd_device[i]];
+        acdb_dev_id = platform_get_snd_device_acdb_id(new_snd_device[i]);
         if (acdb_dev_id < 0) {
             ALOGE("%s: Could not find acdb id for device(%d)",
                   __func__, new_snd_device[i]);
@@ -1940,12 +2227,7 @@
     if (my_data->csd == NULL)
         return ret;
 
-    if (out_snd_device == SND_DEVICE_OUT_VOICE_SPEAKER &&
-        audio_extn_spkr_prot_is_enabled())
-        acdb_rx_id = platform_get_snd_device_acdb_id(SND_DEVICE_OUT_SPEAKER_PROTECTED);
-    else
-        acdb_rx_id = platform_get_snd_device_acdb_id(out_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) {
@@ -1972,10 +2254,6 @@
     if (my_data->acdb_send_voice_cal == NULL) {
         ALOGE("%s: dlsym error for acdb_send_voice_call", __func__);
     } else {
-        if (out_snd_device == SND_DEVICE_OUT_VOICE_SPEAKER &&
-            audio_extn_spkr_prot_is_enabled())
-            out_snd_device = SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED;
-
         acdb_rx_id = platform_get_snd_device_acdb_id(out_snd_device);
         acdb_tx_id = platform_get_snd_device_acdb_id(in_snd_device);
 
@@ -2000,12 +2278,7 @@
     if (my_data->csd == NULL)
         return ret;
 
-    if (out_snd_device == SND_DEVICE_OUT_VOICE_SPEAKER &&
-        audio_extn_spkr_prot_is_enabled())
-        acdb_rx_id = platform_get_snd_device_acdb_id(SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED);
-    else
-        acdb_rx_id = platform_get_snd_device_acdb_id(out_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) {
@@ -2050,6 +2323,26 @@
     return ret;
 }
 
+int platform_set_mic_break_det(void *platform, bool enable)
+{
+    int ret = 0;
+    struct platform_data *my_data = (struct platform_data *)platform;
+    struct audio_device *adev = my_data->adev;
+    const char *mixer_ctl_name = "Voice Mic Break Enable";
+    struct mixer_ctl *ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
+    if (!ctl) {
+        ALOGE("%s: Could not get ctl for mixer cmd - %s",
+              __func__, mixer_ctl_name);
+        return -EINVAL;
+    }
+
+    ret = mixer_ctl_set_value(ctl, 0, enable);
+    if(ret)
+        ALOGE("%s: Failed to set mixer ctl: %s", __func__, mixer_ctl_name);
+
+    return ret;
+}
+
 int platform_get_sample_rate(void *platform, uint32_t *rate)
 {
     struct platform_data *my_data = (struct platform_data *)platform;
@@ -2107,6 +2400,7 @@
     struct audio_device *adev = my_data->adev;
     struct mixer_ctl *ctl;
     const char *mixer_ctl_name = "Voice Rx Gain";
+    const char *mute_mixer_ctl_name = "Voice Rx Device Mute";
     int vol_index = 0, ret = 0;
     uint32_t set_values[ ] = {0,
                               ALL_SESSION_VSID,
@@ -2127,6 +2421,24 @@
     ALOGV("Setting voice volume index: %d", set_values[0]);
     mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values));
 
+    // Send mute command in case volume index is max since indexes are inverted
+    // for mixer controls.
+    if (vol_index == my_data->max_vol_index) {
+        set_values[0] = 1;
+    }
+    else {
+        set_values[0] = 0;
+    }
+
+    ctl = mixer_get_ctl_by_name(adev->mixer, mute_mixer_ctl_name);
+    if (!ctl) {
+        ALOGE("%s: Could not get ctl for mixer cmd - %s",
+              __func__, mute_mixer_ctl_name);
+        return -EINVAL;
+    }
+    ALOGV("%s: Setting RX Device Mute to: %d", __func__, set_values[0]);
+    mixer_ctl_set_array(ctl, set_values, ARRAY_SIZE(set_values));
+
     if (my_data->csd != NULL) {
         ret = my_data->csd->volume(ALL_SESSION_VSID, volume,
                                    DEFAULT_VOLUME_RAMP_DURATION_MS);
@@ -2258,6 +2570,13 @@
         new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
         new_snd_devices[1] = SND_DEVICE_OUT_BT_SCO;
         ret = 0;
+    } else if (snd_device == SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO &&
+               !platform_check_backends_match(SND_DEVICE_OUT_SPEAKER_SAFE,
+                                              SND_DEVICE_OUT_BT_SCO)) {
+        *num_devices = 2;
+        new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER_SAFE;
+        new_snd_devices[1] = SND_DEVICE_OUT_BT_SCO;
+        ret = 0;
     } else if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_BT_SCO_WB &&
                !platform_check_backends_match(SND_DEVICE_OUT_SPEAKER,
                                               SND_DEVICE_OUT_BT_SCO_WB)) {
@@ -2265,6 +2584,13 @@
         new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
         new_snd_devices[1] = SND_DEVICE_OUT_BT_SCO_WB;
         ret = 0;
+    } else if (snd_device == SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO_WB &&
+               !platform_check_backends_match(SND_DEVICE_OUT_SPEAKER_SAFE,
+                                              SND_DEVICE_OUT_BT_SCO_WB)) {
+        *num_devices = 2;
+        new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER_SAFE;
+        new_snd_devices[1] = SND_DEVICE_OUT_BT_SCO_WB;
+        ret = 0;
     } else if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET &&
                !platform_check_backends_match(SND_DEVICE_OUT_SPEAKER, SND_DEVICE_OUT_USB_HEADSET)) {
         *num_devices = 2;
@@ -2277,7 +2603,22 @@
         new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER_SAFE;
         new_snd_devices[1] = SND_DEVICE_OUT_USB_HEADSET;
         ret = 0;
+    } else if (SND_DEVICE_OUT_SPEAKER_AND_BT_A2DP == snd_device &&
+               !platform_check_backends_match(SND_DEVICE_OUT_SPEAKER,
+                                              SND_DEVICE_OUT_BT_A2DP)) {
+        *num_devices = 2;
+        new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
+        new_snd_devices[1] = SND_DEVICE_OUT_BT_A2DP;
+        ret = 0;
+    } else if (SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_A2DP == snd_device &&
+               !platform_check_backends_match(SND_DEVICE_OUT_SPEAKER_SAFE,
+                                              SND_DEVICE_OUT_BT_A2DP)) {
+        *num_devices = 2;
+        new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER_SAFE;
+        new_snd_devices[1] = SND_DEVICE_OUT_BT_A2DP;
+        ret = 0;
     }
+
     return ret;
 }
 
@@ -2320,6 +2661,11 @@
             snd_device = adev->bt_wb_speech_enabled ?
                     SND_DEVICE_OUT_SPEAKER_AND_BT_SCO_WB :
                     SND_DEVICE_OUT_SPEAKER_AND_BT_SCO;
+        } else if ((devices & AUDIO_DEVICE_OUT_ALL_SCO) &&
+                         ((devices & ~AUDIO_DEVICE_OUT_ALL_SCO) == AUDIO_DEVICE_OUT_SPEAKER_SAFE)) {
+            snd_device = adev->bt_wb_speech_enabled ?
+                    SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO_WB :
+                    SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO;
         } else if ((devices == (AUDIO_DEVICE_OUT_USB_DEVICE |
                                AUDIO_DEVICE_OUT_SPEAKER)) ||
                 (devices == (AUDIO_DEVICE_OUT_USB_HEADSET |
@@ -2330,6 +2676,12 @@
                 (devices == (AUDIO_DEVICE_OUT_USB_HEADSET |
                                                AUDIO_DEVICE_OUT_SPEAKER_SAFE))) {
             snd_device = SND_DEVICE_OUT_SPEAKER_SAFE_AND_USB_HEADSET;
+        } else if ((devices & AUDIO_DEVICE_OUT_SPEAKER) &&
+                   (devices & AUDIO_DEVICE_OUT_ALL_A2DP)) {
+            snd_device = SND_DEVICE_OUT_SPEAKER_AND_BT_A2DP;
+        }  else if ((devices & AUDIO_DEVICE_OUT_SPEAKER_SAFE) &&
+                   (devices & AUDIO_DEVICE_OUT_ALL_A2DP)) {
+            snd_device = SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_A2DP;
         } else {
             ALOGE("%s: Invalid combo device(%#x)", __func__, devices);
             goto exit;
@@ -2394,6 +2746,8 @@
             } else {
                 snd_device = SND_DEVICE_OUT_BT_SCO;
             }
+        } else if (devices & AUDIO_DEVICE_OUT_ALL_A2DP) {
+            snd_device = SND_DEVICE_OUT_BT_A2DP;
         } else if (devices & (AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_SPEAKER_SAFE)) {
             if (!adev->enable_hfp) {
                 snd_device = SND_DEVICE_OUT_VOICE_SPEAKER;
@@ -2423,7 +2777,13 @@
     } else if (devices & AUDIO_DEVICE_OUT_SPEAKER_SAFE) {
         snd_device = SND_DEVICE_OUT_SPEAKER_SAFE;
     } else if (devices & AUDIO_DEVICE_OUT_SPEAKER) {
-        if (my_data->speaker_lr_swap)
+        /*
+         * Perform device switch only if acdb tuning is different between SPEAKER & SPEAKER_REVERSE,
+         * Or there will be a small pause while performing device switch.
+         */
+        if (my_data->speaker_lr_swap &&
+            (acdb_device_table[SND_DEVICE_OUT_SPEAKER] !=
+            acdb_device_table[SND_DEVICE_OUT_SPEAKER_REVERSE]))
             snd_device = SND_DEVICE_OUT_SPEAKER_REVERSE;
         else
             snd_device = SND_DEVICE_OUT_SPEAKER;
@@ -2433,10 +2793,14 @@
         } else {
             snd_device = SND_DEVICE_OUT_BT_SCO;
         }
+    } else if (devices & AUDIO_DEVICE_OUT_ALL_A2DP) {
+        snd_device = SND_DEVICE_OUT_BT_A2DP;
     } else if (devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
         snd_device = SND_DEVICE_OUT_HDMI ;
     } else if (audio_is_usb_out_device(devices)) {
-        if (audio_extn_usb_is_capture_supported())
+        if (audio_extn_ma_supported_usb())
+            snd_device = SND_DEVICE_OUT_USB_HEADSET_SPEC;
+        else if (audio_extn_usb_is_capture_supported())
             snd_device = SND_DEVICE_OUT_USB_HEADSET;
         else
             snd_device = SND_DEVICE_OUT_USB_HEADPHONES;
@@ -2963,6 +3327,35 @@
     return ret;
 }
 
+int platform_set_incall_recording_session_channels(void *platform,
+                                             uint32_t channel_count)
+{
+    int ret = 0;
+    struct platform_data *my_data = (struct platform_data *)platform;
+    struct audio_device *adev = my_data->adev;
+    const char *mixer_ctl_name = "Voc Rec Config";
+    int num_ctl_values;
+    int i;
+    struct mixer_ctl *ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
+
+    if (!ctl) {
+        ALOGE("%s: Could not get ctl for mixer cmd - %s",
+              __func__, mixer_ctl_name);
+        ret = -EINVAL;
+    } else {
+        num_ctl_values = mixer_ctl_get_num_values(ctl);
+        for (i = 0; i < num_ctl_values; i++) {
+            if (mixer_ctl_set_value(ctl, i, channel_count)) {
+                ALOGE("Error: invalid channel count: %x", channel_count);
+                ret = -EINVAL;
+                break;
+            }
+        }
+    }
+
+    return ret;
+}
+
 int platform_stop_incall_recording_usecase(void *platform)
 {
     int ret = 0;
@@ -3014,20 +3407,29 @@
 int platform_set_parameters(void *platform, struct str_parms *parms)
 {
     struct platform_data *my_data = (struct platform_data *)platform;
-    char value[128];
+    char *value = NULL;
     char *kv_pairs = str_parms_to_str(parms);
+    int len;
     int ret = 0, err;
 
     if (kv_pairs == NULL) {
         ret = -EINVAL;
-        ALOGE("%s: key-value pair is NULL",__func__);
+        ALOGE("%s: key-value pair is NULL", __func__);
         goto done;
     }
 
     ALOGV("%s: enter: %s", __func__, kv_pairs);
 
+    len = strlen(kv_pairs);
+    value = (char*)calloc(len + 1, sizeof(char));
+    if (value == NULL) {
+        ret = -ENOMEM;
+        ALOGE("[%s] failed to allocate memory", __func__);
+        goto done;
+    }
+
     err = str_parms_get_str(parms, PLATFORM_CONFIG_KEY_SOUNDCARD_NAME,
-                            value, sizeof(value));
+                            value, len);
     if (err >= 0) {
         str_parms_del(parms, PLATFORM_CONFIG_KEY_SOUNDCARD_NAME);
         my_data->snd_card_name = strdup(value);
@@ -3035,7 +3437,7 @@
     }
 
     err = str_parms_get_str(parms, PLATFORM_CONFIG_KEY_OPERATOR_INFO,
-                            value, sizeof(value));
+                            value, len);
     if (err >= 0) {
         struct operator_info *info;
         char *str = value;
@@ -3051,21 +3453,26 @@
         ALOGV("%s: add operator[%s] mccmnc[%s]", __func__, info->name, info->mccmnc);
     }
 
-    memset(value, 0, sizeof(value));
+    memset(value, 0, len + 1);
     err = str_parms_get_str(parms, PLATFORM_CONFIG_KEY_MAX_MIC_COUNT,
-                            value, sizeof(value));
+                            value, len);
     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);
     }
 
+    /* handle audio calibration parameters */
+    set_audiocal(platform, parms, value, len);
+
     // to-do: disable setting sidetone gain, will revist this later
     // audio_extn_usb_set_sidetone_gain(parms, value, len);
 done:
     ALOGV("%s: exit with code(%d)", __func__, ret);
     if (kv_pairs != NULL)
         free(kv_pairs);
+    if (value != NULL)
+        free(value);
 
     return ret;
 }
@@ -3354,9 +3761,9 @@
 /*
  * configures afe with bit width and Sample Rate
  */
-static int platform_set_backend_cfg(const struct audio_device* adev,
-                                          snd_device_t snd_device,
-                                          const struct audio_backend_cfg *backend_cfg)
+int platform_set_backend_cfg(const struct audio_device* adev,
+                             snd_device_t snd_device,
+                             const struct audio_backend_cfg *backend_cfg)
 {
 
     int ret = 0;
@@ -3681,6 +4088,7 @@
     unsigned int sample_rate;
     unsigned int channels;
     int backend_idx = DEFAULT_CODEC_BACKEND;
+    unsigned long service_interval = 0; // 0 is invalid
     struct platform_data *my_data = (struct platform_data *)adev->platform;
 
     if (snd_device == SND_DEVICE_OUT_BT_SCO ||
@@ -3725,6 +4133,21 @@
         case USB_AUDIO_RX_BACKEND:
             audio_extn_usb_is_config_supported(&bit_width,
                                                &sample_rate, &channels, true);
+            if (platform_get_usb_service_interval(adev->platform, true,
+                                                  &service_interval) == 0) {
+                /* overwrite with best altset for this service interval */
+                int ret =
+                        audio_extn_usb_altset_for_service_interval(true /*playback*/,
+                                                                   service_interval,
+                                                                   &bit_width,
+                                                                   &sample_rate,
+                                                                   &channels);
+                if (ret < 0) {
+                    ALOGE("Failed to find altset for service interval %lu, skip reconfig",
+                          service_interval);
+                    return false;
+                }
+            }
             ALOGV("%s: USB BE configured as bit_width(%d)sample_rate(%d)channels(%d)",
                   __func__, bit_width, sample_rate, channels);
             break;
@@ -4112,7 +4535,7 @@
 int platform_get_mmap_data_fd(void *platform __unused, int fe_dev __unused, int dir __unused,
                               int *fd __unused, uint32_t *size __unused)
 {
-#if defined (PLATFORM_MSM8996) || (PLATFORM_MSM8998)
+#if defined (PLATFORM_MSM8996) || (PLATFORM_MSM8998) || (PLATFORM_SDM845)
     struct platform_data *my_data = (struct platform_data *)platform;
     struct audio_device *adev = my_data->adev;
     int hw_fd = -1;
@@ -4140,3 +4563,218 @@
     return -1;
 #endif
 }
+
+bool platform_sound_trigger_usecase_needs_event(audio_usecase_t uc_id)
+{
+    bool needs_event = false;
+
+    switch (uc_id) {
+    /* concurrent capture usecases which needs event */
+    case USECASE_AUDIO_RECORD:
+    case USECASE_AUDIO_RECORD_LOW_LATENCY:
+    case USECASE_AUDIO_RECORD_MMAP:
+    case USECASE_AUDIO_RECORD_HIFI:
+    case USECASE_AUDIO_RECORD_VOIP:
+    case USECASE_VOICEMMODE1_CALL:
+    case USECASE_VOICEMMODE2_CALL:
+    /* concurrent playback usecases that needs event */
+    case USECASE_AUDIO_PLAYBACK_DEEP_BUFFER:
+    case USECASE_AUDIO_PLAYBACK_OFFLOAD:
+        needs_event = true;
+        break;
+    default:
+        ALOGV("%s:usecase_id[%d] no need to raise event.", __func__, uc_id);
+    }
+    return needs_event;
+}
+
+bool platform_snd_device_has_speaker(snd_device_t dev) {
+    int num_devs = 2;
+    snd_device_t split_devs[2] = {SND_DEVICE_NONE, SND_DEVICE_NONE};
+    if (platform_can_split_snd_device(dev, &num_devs, split_devs) == 0) {
+        return platform_snd_device_has_speaker(split_devs[0]) ||
+                platform_snd_device_has_speaker(split_devs[1]);
+    }
+
+    switch (dev) {
+        case SND_DEVICE_OUT_SPEAKER:
+        case SND_DEVICE_OUT_SPEAKER_SAFE:
+        case SND_DEVICE_OUT_SPEAKER_REVERSE:
+        case SND_DEVICE_OUT_SPEAKER_PROTECTED:
+        case SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED:
+        case SND_DEVICE_OUT_VOICE_SPEAKER_HFP:
+            return true;
+        default:
+            break;
+    }
+    return false;
+}
+
+bool platform_set_microphone_characteristic(void *platform,
+                                            struct audio_microphone_characteristic_t mic) {
+    struct platform_data *my_data = (struct platform_data *)platform;
+    if (my_data->declared_mic_count >= AUDIO_MICROPHONE_MAX_COUNT) {
+        ALOGE("mic number is more than maximum number");
+        return false;
+    }
+    for (size_t ch = 0; ch < AUDIO_CHANNEL_COUNT_MAX; ch++) {
+        mic.channel_mapping[ch] = AUDIO_MICROPHONE_CHANNEL_MAPPING_UNUSED;
+    }
+    my_data->microphones[my_data->declared_mic_count++] = mic;
+    return true;
+}
+
+int platform_get_microphones(void *platform,
+                             struct audio_microphone_characteristic_t *mic_array,
+                             size_t *mic_count) {
+    struct platform_data *my_data = (struct platform_data *)platform;
+    if (mic_count == NULL) {
+        return -EINVAL;
+    }
+    if (mic_array == NULL) {
+        return -EINVAL;
+    }
+
+    if (*mic_count == 0) {
+        *mic_count = my_data->declared_mic_count;
+        return 0;
+    }
+
+    size_t max_mic_count = *mic_count;
+    size_t actual_mic_count = 0;
+    for (size_t i = 0; i < max_mic_count && i < my_data->declared_mic_count; i++) {
+        mic_array[i] = my_data->microphones[i];
+        actual_mic_count++;
+    }
+    *mic_count = actual_mic_count;
+    return 0;
+}
+
+bool platform_set_microphone_map(void *platform, snd_device_t in_snd_device,
+                                 const struct mic_info *info) {
+    struct platform_data *my_data = (struct platform_data *)platform;
+    if (in_snd_device < SND_DEVICE_IN_BEGIN || in_snd_device >= SND_DEVICE_IN_END) {
+        ALOGE("%s: Sound device not valid", __func__);
+        return false;
+    }
+    size_t m_count = my_data->mic_map[in_snd_device].mic_count++;
+    if (m_count >= AUDIO_MICROPHONE_MAX_COUNT) {
+        ALOGE("%s: Microphone count is greater than max allowed value", __func__);
+        my_data->mic_map[in_snd_device].mic_count--;
+        return false;
+    }
+    my_data->mic_map[in_snd_device].microphones[m_count] = *info;
+    return true;
+}
+
+int platform_get_active_microphones(void *platform, unsigned int channels,
+                                    audio_usecase_t uc_id,
+                                    struct audio_microphone_characteristic_t *mic_array,
+                                    size_t *mic_count) {
+    struct platform_data *my_data = (struct platform_data *)platform;
+    struct audio_usecase *usecase = get_usecase_from_list(my_data->adev, uc_id);
+    if (mic_count == NULL || mic_array == NULL || usecase == NULL) {
+        return -EINVAL;
+    }
+    size_t max_mic_count = my_data->declared_mic_count;
+    size_t actual_mic_count = 0;
+
+    snd_device_t active_input_snd_device =
+            platform_get_input_snd_device(platform, usecase->stream.in->device);
+    if (active_input_snd_device == SND_DEVICE_NONE) {
+        ALOGI("%s: No active microphones found", __func__);
+        goto end;
+    }
+
+    size_t  active_mic_count = my_data->mic_map[active_input_snd_device].mic_count;
+    struct mic_info *m_info = my_data->mic_map[active_input_snd_device].microphones;
+
+    for (size_t i = 0; i < active_mic_count; i++) {
+        unsigned int channels_for_active_mic = channels;
+        if (channels_for_active_mic > m_info[i].channel_count) {
+            channels_for_active_mic = m_info[i].channel_count;
+        }
+        for (size_t j = 0; j < max_mic_count; j++) {
+            if (strcmp(my_data->microphones[j].device_id,
+                       m_info[i].device_id) == 0) {
+                mic_array[actual_mic_count] = my_data->microphones[j];
+                for (size_t ch = 0; ch < channels_for_active_mic; ch++) {
+                     mic_array[actual_mic_count].channel_mapping[ch] =
+                             m_info[i].channel_mapping[ch];
+                }
+                actual_mic_count++;
+                break;
+            }
+        }
+    }
+end:
+    *mic_count = actual_mic_count;
+    return 0;
+}
+
+int platform_set_usb_service_interval(void *platform,
+                                      bool playback,
+                                      unsigned long service_interval,
+                                      bool *reconfig)
+{
+#if defined (USB_SERVICE_INTERVAL_ENABLED)
+    struct platform_data *_platform = (struct platform_data *)platform;
+    *reconfig = false;
+    if (!playback) {
+        ALOGE("%s not valid for capture", __func__);
+        return -1;
+    }
+    const char *ctl_name = "USB_AUDIO_RX service_interval";
+    struct mixer_ctl *ctl = mixer_get_ctl_by_name(_platform->adev->mixer,
+                                                  ctl_name);
+    if (!ctl) {
+        ALOGV("%s: could not get mixer %s", __func__, ctl_name);
+        return -1;
+    }
+    if (mixer_ctl_get_value(ctl, 0) != (int)service_interval) {
+        mixer_ctl_set_value(ctl, 0, service_interval);
+        *reconfig = true;
+    }
+    return 0;
+#else
+    *reconfig = false;
+    (void)platform;
+    (void)playback;
+    (void)service_interval;
+    return -1;
+#endif
+}
+
+int platform_get_usb_service_interval(void *platform,
+                                      bool playback,
+                                      unsigned long *service_interval)
+{
+#if defined (USB_SERVICE_INTERVAL_ENABLED)
+    struct platform_data *_platform = (struct platform_data *)platform;
+    if (!playback) {
+        ALOGE("%s not valid for capture", __func__);
+        return -1;
+    }
+    const char *ctl_name = "USB_AUDIO_RX service_interval";
+    struct mixer_ctl *ctl = mixer_get_ctl_by_name(_platform->adev->mixer,
+                                                  ctl_name);
+    if (!ctl) {
+        ALOGV("%s: could not get mixer %s", __func__, ctl_name);
+        return -1;
+    }
+    *service_interval = mixer_ctl_get_value(ctl, 0);
+    return 0;
+#else
+    (void)platform;
+    (void)playback;
+    (void)service_interval;
+    return -1;
+#endif
+}
+
+int platform_set_acdb_metainfo_key(void *platform __unused,
+                                   char *name __unused,
+                                   int key __unused)
+{
+    return -ENOSYS;
+}
diff --git a/hal/msm8974/platform.h b/hal/msm8974/platform.h
index 7c5133b..2c7eebf 100644
--- a/hal/msm8974/platform.h
+++ b/hal/msm8974/platform.h
@@ -78,6 +78,9 @@
     SND_DEVICE_OUT_SPEAKER_AND_HDMI,
     SND_DEVICE_OUT_BT_SCO,
     SND_DEVICE_OUT_BT_SCO_WB,
+    SND_DEVICE_OUT_BT_A2DP,
+    SND_DEVICE_OUT_SPEAKER_AND_BT_A2DP,
+    SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_A2DP,
     SND_DEVICE_OUT_VOICE_HANDSET_TMUS,
     SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES,
     SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES,
@@ -90,13 +93,17 @@
     SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED,
     SND_DEVICE_OUT_VOICE_SPEAKER_HFP,
     SND_DEVICE_OUT_SPEAKER_AND_BT_SCO,
+    SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO,
     SND_DEVICE_OUT_SPEAKER_AND_BT_SCO_WB,
+    SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_SCO_WB,
     SND_DEVICE_OUT_USB_HEADSET,
     SND_DEVICE_OUT_USB_HEADPHONES,
     SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET,
     SND_DEVICE_OUT_SPEAKER_SAFE_AND_USB_HEADSET,
     SND_DEVICE_OUT_VOICE_USB_HEADPHONES,
     SND_DEVICE_OUT_VOICE_USB_HEADSET,
+    /* Specific snd_devices */
+    SND_DEVICE_OUT_USB_HEADSET_SPEC,
     SND_DEVICE_OUT_END,
 
     /*
@@ -201,7 +208,6 @@
     MAX_CODEC_BACKENDS
 };
 
-
 #define DEVICE_NAME_MAX_SIZE   128
 #define HW_INFO_ARRAY_MAX_SIZE 32
 
@@ -314,6 +320,8 @@
 #define AFE_PROXY_PLAYBACK_PCM_DEVICE 7
 #define AFE_PROXY_RECORD_PCM_DEVICE 8
 
+#define INCALL_MUSIC_UPLINK_PCM_DEVICE 27
+
 #define HFP_PCM_RX 5
 #ifdef PLATFORM_MSM8x26
 #ifdef EXTERNAL_BT_SUPPORTED
@@ -334,6 +342,8 @@
 #define PLATFORM_CONFIG_KEY_MAX_MIC_COUNT "input_mic_max_count"
 #define PLATFORM_DEFAULT_MIC_COUNT 2
 
+#define AUDIO_MAKE_STRING_FROM_ENUM(X)   { #X, X }
+
 /* CSD-CLIENT related functions */
 typedef int (*init_t)(bool);
 typedef int (*deinit_t)();
diff --git a/hal/platform_api.h b/hal/platform_api.h
index e7c26e7..1a7d2c3 100644
--- a/hal/platform_api.h
+++ b/hal/platform_api.h
@@ -33,9 +33,16 @@
     uint32_t level;
 };
 
+struct mic_info {
+    char device_id[AUDIO_MICROPHONE_ID_MAX_LEN];
+    size_t channel_count;
+    audio_microphone_channel_mapping_t channel_mapping[AUDIO_CHANNEL_COUNT_MAX];
+};
+
 enum card_status_t;
 struct audio_usecase;
 enum usecase_type_t;
+struct audio_backend_cfg;
 
 void *platform_init(struct audio_device *adev);
 void platform_deinit(void *platform);
@@ -52,6 +59,7 @@
 int platform_send_audio_calibration(void *platform, snd_device_t snd_device);
 int platform_send_audio_calibration_v2(void *platform, struct audio_usecase *usecase,
                                        int app_type, int sample_rate);
+int platform_set_acdb_metainfo_key(void *platform, char *name, int key);
 int platform_get_default_app_type_v2(void *platform, enum usecase_type_t type, int *app_type);
 int platform_switch_voice_call_device_pre(void *platform);
 int platform_switch_voice_call_enable_device_config(void *platform,
@@ -65,6 +73,7 @@
                                                   snd_device_t in_snd_device);
 int platform_start_voice_call(void *platform, uint32_t vsid);
 int platform_stop_voice_call(void *platform, uint32_t vsid);
+int platform_set_mic_break_det(void *platform, bool enable);
 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,
@@ -97,6 +106,8 @@
 
 int platform_set_incall_recording_session_id(void *platform,
                                              uint32_t session_id, int rec_mode);
+int platform_set_incall_recording_session_channels(void *platform,
+                                                   uint32_t session_channels);
 int platform_stop_incall_recording_usecase(void *platform);
 int platform_start_incall_music_usecase(void *platform);
 int platform_stop_incall_music_usecase(void *platform);
@@ -104,6 +115,8 @@
 int platform_set_snd_device_backend(snd_device_t snd_device, const char * backend,
                                     const char * hw_interface);
 
+bool platform_sound_trigger_usecase_needs_event(audio_usecase_t uc_id);
+
 /* From platform_info.c */
 int platform_info_init(const char *filename, void *);
 
@@ -148,4 +161,25 @@
                           bool enable, char * str);
 int platform_get_mmap_data_fd(void *platform, int dev, int dir,
                               int *fd, uint32_t *size);
+bool platform_sound_trigger_usecase_needs_event(audio_usecase_t uc_id);
+bool platform_snd_device_has_speaker(snd_device_t dev);
+
+bool platform_set_microphone_characteristic(void *platform,
+                                            struct audio_microphone_characteristic_t mic);
+bool platform_set_microphone_map(void *platform, snd_device_t in_snd_device,
+                                 const struct mic_info *info);
+int platform_get_microphones(void *platform,
+                             struct audio_microphone_characteristic_t *mic_array,
+                             size_t *mic_count);
+int platform_get_active_microphones(void *platform, unsigned int channels,
+                                    audio_usecase_t usecase,
+                                    struct audio_microphone_characteristic_t *mic_array,
+                                    size_t *mic_count);
+int platform_set_usb_service_interval(void *platform,
+                                      bool playback,
+                                      unsigned long service_interval,
+                                      bool *reconfig);
+int platform_get_usb_service_interval(void *platform,
+                                      bool playback,
+                                      unsigned long *service_interval);
 #endif // AUDIO_PLATFORM_API_H
diff --git a/hal/platform_info.c b/hal/platform_info.c
index 41b59c8..f5fbe3f 100644
--- a/hal/platform_info.c
+++ b/hal/platform_info.c
@@ -20,7 +20,7 @@
 #include <errno.h>
 #include <stdio.h>
 #include <expat.h>
-#include <cutils/log.h>
+#include <log/log.h>
 #include <audio_hw.h>
 #include "platform_api.h"
 #include <platform.h>
@@ -35,6 +35,13 @@
     OPERATOR_SPECIFIC,
     GAIN_LEVEL_MAPPING,
     APP_TYPE,
+    MICROPHONE_CHARACTERISTIC,
+    SND_DEVICES,
+    INPUT_SND_DEVICE,
+    INPUT_SND_DEVICE_TO_MIC_MAPPING,
+    SND_DEV,
+    MIC_INFO,
+    ACDB_METAINFO_KEY,
 } section_t;
 
 typedef void (* section_process_fn)(const XML_Char **attr);
@@ -47,6 +54,10 @@
 static void process_operator_specific(const XML_Char **attr);
 static void process_gain_db_to_level_map(const XML_Char **attr);
 static void process_app_type(const XML_Char **attr);
+static void process_microphone_characteristic(const XML_Char **attr);
+static void process_snd_dev(const XML_Char **attr);
+static void process_mic_info(const XML_Char **attr);
+static void process_acdb_metainfo_key(const XML_Char **attr);
 
 static section_process_fn section_table[] = {
     [ROOT] = process_root,
@@ -57,6 +68,10 @@
     [OPERATOR_SPECIFIC] = process_operator_specific,
     [GAIN_LEVEL_MAPPING] = process_gain_db_to_level_map,
     [APP_TYPE] = process_app_type,
+    [MICROPHONE_CHARACTERISTIC] = process_microphone_characteristic,
+    [SND_DEV] = process_snd_dev,
+    [MIC_INFO] = process_mic_info,
+    [ACDB_METAINFO_KEY] = process_acdb_metainfo_key,
 };
 
 static set_parameters_fn set_parameters = &platform_set_parameters;
@@ -71,6 +86,98 @@
 
 static struct platform_info my_data = {true, NULL, NULL};
 
+struct audio_string_to_enum {
+    const char* name;
+    unsigned int value;
+};
+
+static snd_device_t in_snd_device;
+
+static const struct audio_string_to_enum mic_locations[AUDIO_MICROPHONE_LOCATION_CNT] = {
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_MICROPHONE_LOCATION_UNKNOWN),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_MICROPHONE_LOCATION_MAINBODY),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_MICROPHONE_LOCATION_MAINBODY_MOVABLE),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_MICROPHONE_LOCATION_PERIPHERAL),
+};
+
+static const struct audio_string_to_enum mic_directionalities[AUDIO_MICROPHONE_DIRECTIONALITY_CNT] = {
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_MICROPHONE_DIRECTIONALITY_OMNI),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_MICROPHONE_DIRECTIONALITY_BI_DIRECTIONAL),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_MICROPHONE_DIRECTIONALITY_UNKNOWN),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_MICROPHONE_DIRECTIONALITY_CARDIOID),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_MICROPHONE_DIRECTIONALITY_HYPER_CARDIOID),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_MICROPHONE_DIRECTIONALITY_SUPER_CARDIOID),
+};
+
+static const struct audio_string_to_enum mic_channel_mapping[AUDIO_MICROPHONE_CHANNEL_MAPPING_CNT] = {
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_MICROPHONE_CHANNEL_MAPPING_UNUSED),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_MICROPHONE_CHANNEL_MAPPING_DIRECT),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_MICROPHONE_CHANNEL_MAPPING_PROCESSED),
+};
+
+static const struct audio_string_to_enum device_in_types[] = {
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_AMBIENT),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_COMMUNICATION),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_BUILTIN_MIC),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_WIRED_HEADSET),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_AUX_DIGITAL),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_HDMI),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_VOICE_CALL),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_TELEPHONY_RX),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_BACK_MIC),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_REMOTE_SUBMIX),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_USB_ACCESSORY),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_USB_DEVICE),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_FM_TUNER),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_TV_TUNER),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_LINE),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_SPDIF),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_BLUETOOTH_A2DP),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_LOOPBACK),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_IP),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_BUS),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_PROXY),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_USB_HEADSET),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_BLUETOOTH_BLE),
+    AUDIO_MAKE_STRING_FROM_ENUM(AUDIO_DEVICE_IN_DEFAULT),
+};
+
+enum {
+    AUDIO_MICROPHONE_CHARACTERISTIC_NONE = 0u, // 0x0
+    AUDIO_MICROPHONE_CHARACTERISTIC_SENSITIVITY = 1u, // 0x1
+    AUDIO_MICROPHONE_CHARACTERISTIC_MAX_SPL = 2u, // 0x2
+    AUDIO_MICROPHONE_CHARACTERISTIC_MIN_SPL = 4u, // 0x4
+    AUDIO_MICROPHONE_CHARACTERISTIC_ORIENTATION = 8u, // 0x8
+    AUDIO_MICROPHONE_CHARACTERISTIC_GEOMETRIC_LOCATION = 16u, // 0x10
+    AUDIO_MICROPHONE_CHARACTERISTIC_ALL = 31u, /* ((((SENSITIVITY | MAX_SPL) | MIN_SPL)
+                                                  | ORIENTATION) | GEOMETRIC_LOCATION) */
+};
+
+static bool find_enum_by_string(const struct audio_string_to_enum * table, const char * name,
+                                int32_t len, unsigned int *value)
+{
+    if (table == NULL) {
+        ALOGE("%s: table is NULL", __func__);
+        return false;
+    }
+
+    if (name == NULL) {
+        ALOGE("null key");
+        return false;
+    }
+
+    for (int i = 0; i < len; i++) {
+        if (!strcmp(table[i].name, name)) {
+            *value = table[i].value;
+            return true;
+        }
+    }
+    return false;
+}
+
 /*
  * <audio_platform_info>
  * <acdb_ids>
@@ -349,6 +456,312 @@
     return;
 }
 
+static void process_microphone_characteristic(const XML_Char **attr) {
+    struct audio_microphone_characteristic_t microphone;
+    uint32_t curIdx = 0;
+
+    if (strcmp(attr[curIdx++], "valid_mask")) {
+        ALOGE("%s: valid_mask not found", __func__);
+        goto done;
+    }
+    uint32_t valid_mask = atoi(attr[curIdx++]);
+
+    if (strcmp(attr[curIdx++], "device_id")) {
+        ALOGE("%s: device_id not found", __func__);
+        goto done;
+    }
+    if (strlen(attr[curIdx]) > AUDIO_MICROPHONE_ID_MAX_LEN) {
+        ALOGE("%s: device_id %s is too long", __func__, attr[curIdx]);
+        goto done;
+    }
+    strcpy(microphone.device_id, attr[curIdx++]);
+
+    if (strcmp(attr[curIdx++], "type")) {
+        ALOGE("%s: device not found", __func__);
+        goto done;
+    }
+    if (!find_enum_by_string(device_in_types, (char*)attr[curIdx++],
+            ARRAY_SIZE(device_in_types), &microphone.device)) {
+        ALOGE("%s: type %s in %s not found!",
+              __func__, attr[--curIdx], PLATFORM_INFO_XML_PATH);
+        goto done;
+    }
+
+    if (strcmp(attr[curIdx++], "address")) {
+        ALOGE("%s: address not found", __func__);
+        goto done;
+    }
+    if (strlen(attr[curIdx]) > AUDIO_DEVICE_MAX_ADDRESS_LEN) {
+        ALOGE("%s, address %s is too long", __func__, attr[curIdx]);
+        goto done;
+    }
+    strcpy(microphone.address, attr[curIdx++]);
+    if (strlen(microphone.address) == 0) {
+        // If the address is empty, populate the address according to device type.
+        if (microphone.device == AUDIO_DEVICE_IN_BUILTIN_MIC) {
+            strcpy(microphone.address, AUDIO_BOTTOM_MICROPHONE_ADDRESS);
+        } else if (microphone.device == AUDIO_DEVICE_IN_BACK_MIC) {
+            strcpy(microphone.address, AUDIO_BACK_MICROPHONE_ADDRESS);
+        }
+    }
+
+    if (strcmp(attr[curIdx++], "location")) {
+        ALOGE("%s: location not found", __func__);
+        goto done;
+    }
+    if (!find_enum_by_string(mic_locations, (char*)attr[curIdx++],
+            AUDIO_MICROPHONE_LOCATION_CNT, &microphone.location)) {
+        ALOGE("%s: location %s in %s not found!",
+              __func__, attr[--curIdx], PLATFORM_INFO_XML_PATH);
+        goto done;
+    }
+
+    if (strcmp(attr[curIdx++], "group")) {
+        ALOGE("%s: group not found", __func__);
+        goto done;
+    }
+    microphone.group = atoi(attr[curIdx++]);
+
+    if (strcmp(attr[curIdx++], "index_in_the_group")) {
+        ALOGE("%s: index_in_the_group not found", __func__);
+        goto done;
+    }
+    microphone.index_in_the_group = atoi(attr[curIdx++]);
+
+    if (strcmp(attr[curIdx++], "directionality")) {
+        ALOGE("%s: directionality not found", __func__);
+        goto done;
+    }
+    if (!find_enum_by_string(mic_directionalities, (char*)attr[curIdx++],
+                AUDIO_MICROPHONE_DIRECTIONALITY_CNT, &microphone.directionality)) {
+        ALOGE("%s: directionality %s in %s not found!",
+              __func__, attr[--curIdx], PLATFORM_INFO_XML_PATH);
+        goto done;
+    }
+
+    if (strcmp(attr[curIdx++], "num_frequency_responses")) {
+        ALOGE("%s: num_frequency_responses not found", __func__);
+        goto done;
+    }
+    microphone.num_frequency_responses = atoi(attr[curIdx++]);
+    if (microphone.num_frequency_responses > AUDIO_MICROPHONE_MAX_FREQUENCY_RESPONSES) {
+        ALOGE("%s: num_frequency_responses is too large", __func__);
+        goto done;
+    }
+    if (microphone.num_frequency_responses > 0) {
+        if (strcmp(attr[curIdx++], "frequencies")) {
+            ALOGE("%s: frequencies not found", __func__);
+            goto done;
+        }
+        char *token = strtok((char *)attr[curIdx++], " ");
+        uint32_t num_frequencies = 0;
+        while (token) {
+            microphone.frequency_responses[0][num_frequencies++] = atof(token);
+            if (num_frequencies > AUDIO_MICROPHONE_MAX_FREQUENCY_RESPONSES) {
+                ALOGE("%s: num %u of frequency is too large", __func__, num_frequencies);
+                goto done;
+            }
+            token = strtok(NULL, " ");
+        }
+
+        if (strcmp(attr[curIdx++], "responses")) {
+            ALOGE("%s: responses not found", __func__);
+            goto done;
+        }
+        token = strtok((char *)attr[curIdx++], " ");
+        uint32_t num_responses = 0;
+        while (token) {
+            microphone.frequency_responses[1][num_responses++] = atof(token);
+            if (num_responses > AUDIO_MICROPHONE_MAX_FREQUENCY_RESPONSES) {
+                ALOGE("%s: num %u of response is too large", __func__, num_responses);
+                goto done;
+            }
+            token = strtok(NULL, " ");
+        }
+
+        if (num_frequencies != num_responses
+                || num_frequencies != microphone.num_frequency_responses) {
+            ALOGE("%s: num of frequency and response not match: %u, %u, %u",
+                  __func__, num_frequencies, num_responses, microphone.num_frequency_responses);
+            goto done;
+        }
+    }
+
+    if (valid_mask & AUDIO_MICROPHONE_CHARACTERISTIC_SENSITIVITY) {
+        if (strcmp(attr[curIdx++], "sensitivity")) {
+            ALOGE("%s: sensitivity not found", __func__);
+            goto done;
+        }
+        microphone.sensitivity = atof(attr[curIdx++]);
+    } else {
+        microphone.sensitivity = AUDIO_MICROPHONE_SENSITIVITY_UNKNOWN;
+    }
+
+    if (valid_mask & AUDIO_MICROPHONE_CHARACTERISTIC_MAX_SPL) {
+        if (strcmp(attr[curIdx++], "max_spl")) {
+            ALOGE("%s: max_spl not found", __func__);
+            goto done;
+        }
+        microphone.max_spl = atof(attr[curIdx++]);
+    } else {
+        microphone.max_spl = AUDIO_MICROPHONE_SPL_UNKNOWN;
+    }
+
+    if (valid_mask & AUDIO_MICROPHONE_CHARACTERISTIC_MIN_SPL) {
+        if (strcmp(attr[curIdx++], "min_spl")) {
+            ALOGE("%s: min_spl not found", __func__);
+            goto done;
+        }
+        microphone.min_spl = atof(attr[curIdx++]);
+    } else {
+        microphone.min_spl = AUDIO_MICROPHONE_SPL_UNKNOWN;
+    }
+
+    if (valid_mask & AUDIO_MICROPHONE_CHARACTERISTIC_ORIENTATION) {
+        if (strcmp(attr[curIdx++], "orientation")) {
+            ALOGE("%s: orientation not found", __func__);
+            goto done;
+        }
+        char *token = strtok((char *)attr[curIdx++], " ");
+        float orientation[3];
+        uint32_t idx = 0;
+        while (token) {
+            orientation[idx++] = atof(token);
+            if (idx > 3) {
+                ALOGE("%s: orientation invalid", __func__);
+                goto done;
+            }
+            token = strtok(NULL, " ");
+        }
+        if (idx != 3) {
+            ALOGE("%s: orientation invalid", __func__);
+            goto done;
+        }
+        microphone.orientation.x = orientation[0];
+        microphone.orientation.y = orientation[1];
+        microphone.orientation.z = orientation[2];
+    } else {
+        microphone.orientation.x = 0.0f;
+        microphone.orientation.y = 0.0f;
+        microphone.orientation.z = 0.0f;
+    }
+
+    if (valid_mask & AUDIO_MICROPHONE_CHARACTERISTIC_GEOMETRIC_LOCATION) {
+        if (strcmp(attr[curIdx++], "geometric_location")) {
+            ALOGE("%s: geometric_location not found", __func__);
+            goto done;
+        }
+        char *token = strtok((char *)attr[curIdx++], " ");
+        float geometric_location[3];
+        uint32_t idx = 0;
+        while (token) {
+            geometric_location[idx++] = atof(token);
+            if (idx > 3) {
+                ALOGE("%s: geometric_location invalid", __func__);
+                goto done;
+            }
+            token = strtok(NULL, " ");
+        }
+        if (idx != 3) {
+            ALOGE("%s: geometric_location invalid", __func__);
+            goto done;
+        }
+        microphone.geometric_location.x = geometric_location[0];
+        microphone.geometric_location.y = geometric_location[1];
+        microphone.geometric_location.z = geometric_location[2];
+    } else {
+        microphone.geometric_location.x = AUDIO_MICROPHONE_COORDINATE_UNKNOWN;
+        microphone.geometric_location.y = AUDIO_MICROPHONE_COORDINATE_UNKNOWN;
+        microphone.geometric_location.z = AUDIO_MICROPHONE_COORDINATE_UNKNOWN;
+    }
+
+    platform_set_microphone_characteristic(my_data.platform, microphone);
+done:
+    return;
+}
+
+static void process_snd_dev(const XML_Char **attr)
+{
+    uint32_t curIdx = 0;
+    in_snd_device = SND_DEVICE_NONE;
+
+    if (strcmp(attr[curIdx++], "in_snd_device")) {
+        ALOGE("%s: snd_device not found", __func__);
+        return;
+    }
+    in_snd_device = platform_get_snd_device_index((char *)attr[curIdx++]);
+    if (in_snd_device < SND_DEVICE_IN_BEGIN ||
+            in_snd_device >= SND_DEVICE_IN_END) {
+        ALOGE("%s: Sound device not valid", __func__);
+        in_snd_device = SND_DEVICE_NONE;
+    }
+
+    return;
+}
+
+static void process_mic_info(const XML_Char **attr)
+{
+    uint32_t curIdx = 0;
+    struct mic_info microphone;
+
+    memset(&microphone.channel_mapping, AUDIO_MICROPHONE_CHANNEL_MAPPING_UNUSED,
+               sizeof(microphone.channel_mapping));
+
+    if (strcmp(attr[curIdx++], "mic_device_id")) {
+        ALOGE("%s: mic_device_id not found", __func__);
+        goto on_error;
+    }
+    strlcpy(microphone.device_id,
+                (char *)attr[curIdx++], AUDIO_MICROPHONE_ID_MAX_LEN);
+
+    if (strcmp(attr[curIdx++], "channel_mapping")) {
+        ALOGE("%s: channel_mapping not found", __func__);
+        goto on_error;
+    }
+    const char *token = strtok((char *)attr[curIdx++], " ");
+    uint32_t idx = 0;
+    while (token) {
+        if (!find_enum_by_string(mic_channel_mapping, token,
+                AUDIO_MICROPHONE_CHANNEL_MAPPING_CNT,
+                &microphone.channel_mapping[idx++])) {
+            ALOGE("%s: channel_mapping %s in %s not found!",
+                      __func__, attr[--curIdx], PLATFORM_INFO_XML_PATH);
+            goto on_error;
+        }
+        token = strtok(NULL, " ");
+    }
+    microphone.channel_count = idx;
+
+    platform_set_microphone_map(my_data.platform, in_snd_device,
+                                    &microphone);
+    return;
+on_error:
+    in_snd_device = SND_DEVICE_NONE;
+    return;
+}
+
+/* process acdb meta info key value */
+static void process_acdb_metainfo_key(const XML_Char **attr)
+{
+    if (strcmp(attr[0], "name") != 0) {
+        ALOGE("%s: 'name' not found", __func__);
+        goto done;
+    }
+    if (strcmp(attr[2], "value") != 0) {
+        ALOGE("%s: 'value' not found", __func__);
+        goto done;
+    }
+
+    int key = atoi((char *)attr[3]);
+    if (platform_set_acdb_metainfo_key(my_data.platform,
+                                       (char*)attr[1], key) < 0) {
+        ALOGE("%s: key %d was not set!", __func__, key);
+    }
+
+done:
+    return;
+}
+
 static void start_tag(void *userdata __unused, const XML_Char *tag_name,
                       const XML_Char **attr)
 {
@@ -372,6 +785,12 @@
             section = GAIN_LEVEL_MAPPING;
         } else if (strcmp(tag_name, "app_types") == 0) {
             section = APP_TYPE;
+        } else if (strcmp(tag_name, "microphone_characteristics") == 0) {
+            section = MICROPHONE_CHARACTERISTIC;
+        } else if (strcmp(tag_name, "snd_devices") == 0) {
+            section = SND_DEVICES;
+        } else if(strcmp(tag_name, "acdb_metainfo_key") == 0) {
+            section = ACDB_METAINFO_KEY;
         } else if (strcmp(tag_name, "device") == 0) {
             if ((section != ACDB) && (section != BACKEND_NAME) && (section != OPERATOR_SPECIFIC)) {
                 ALOGE("device tag only supported for acdb/backend names");
@@ -390,7 +809,7 @@
             section_process_fn fn = section_table[PCM_ID];
             fn(attr);
         } else if (strcmp(tag_name, "param") == 0) {
-            if (section != CONFIG_PARAMS) {
+            if ((section != CONFIG_PARAMS) && (section != ACDB_METAINFO_KEY)) {
                 ALOGE("param tag only supported with CONFIG_PARAMS section");
                 return;
             }
@@ -399,7 +818,7 @@
             fn(attr);
         } else if (strcmp(tag_name, "gain_level_map") == 0) {
             if (section != GAIN_LEVEL_MAPPING) {
-                ALOGE("usecase tag only supported with GAIN_LEVEL_MAPPING section");
+                ALOGE("gain_level_map tag only supported with GAIN_LEVEL_MAPPING section");
                 return;
             }
 
@@ -413,6 +832,43 @@
 
             section_process_fn fn = section_table[APP_TYPE];
             fn(attr);
+        } else if (strcmp(tag_name, "microphone") == 0) {
+            if (section != MICROPHONE_CHARACTERISTIC) {
+                ALOGE("microphone tag only supported with MICROPHONE_CHARACTERISTIC section");
+                return;
+            }
+            section_process_fn fn = section_table[MICROPHONE_CHARACTERISTIC];
+            fn(attr);
+        } else if (strcmp(tag_name, "input_snd_device") == 0) {
+            if (section != SND_DEVICES) {
+                ALOGE("input_snd_device tag only supported with SND_DEVICES section");
+                return;
+            }
+            section = INPUT_SND_DEVICE;
+        } else if (strcmp(tag_name, "input_snd_device_mic_mapping") == 0) {
+            if (section != INPUT_SND_DEVICE) {
+                ALOGE("input_snd_device_mic_mapping tag only supported with INPUT_SND_DEVICE section");
+                return;
+            }
+            section = INPUT_SND_DEVICE_TO_MIC_MAPPING;
+        } else if (strcmp(tag_name, "snd_dev") == 0) {
+            if (section != INPUT_SND_DEVICE_TO_MIC_MAPPING) {
+                ALOGE("snd_dev tag only supported with INPUT_SND_DEVICE_TO_MIC_MAPPING section");
+                return;
+            }
+            section_process_fn fn = section_table[SND_DEV];
+            fn(attr);
+        } else if (strcmp(tag_name, "mic_info") == 0) {
+            if (section != INPUT_SND_DEVICE_TO_MIC_MAPPING) {
+                ALOGE("mic_info tag only supported with INPUT_SND_DEVICE_TO_MIC_MAPPING section");
+                return;
+            }
+            if (in_snd_device == SND_DEVICE_NONE) {
+                ALOGE("%s: Error in previous tags, do not process mic info", __func__);
+                return;
+            }
+            section_process_fn fn = section_table[MIC_INFO];
+            fn(attr);
         }
     } else {
         if(strcmp(tag_name, "config_params") == 0) {
@@ -447,6 +903,16 @@
         section = ROOT;
     } else if (strcmp(tag_name, "app_types") == 0) {
         section = ROOT;
+    } else if (strcmp(tag_name, "microphone_characteristics") == 0) {
+        section = ROOT;
+    } else if (strcmp(tag_name, "snd_devices") == 0) {
+        section = ROOT;
+    } else if (strcmp(tag_name, "input_snd_device") == 0) {
+        section = SND_DEVICES;
+    } else if (strcmp(tag_name, "input_snd_device_mic_mapping") == 0) {
+        section = INPUT_SND_DEVICE;
+    } else if (strcmp(tag_name, "acdb_metainfo_key") == 0) {
+        section = ROOT;
     }
 }
 
diff --git a/hal/voice.c b/hal/voice.c
index 6f16137..f456ce1 100644
--- a/hal/voice.c
+++ b/hal/voice.c
@@ -21,7 +21,7 @@
 #include <stdlib.h>
 #include <errno.h>
 #include <math.h>
-#include <cutils/log.h>
+#include <log/log.h>
 #include <cutils/str_parms.h>
 
 #include "audio_hw.h"
@@ -207,6 +207,9 @@
         goto error_start_voice;
     }
 
+    if (adev->mic_break_enabled)
+        platform_set_mic_break_det(adev->platform, true);
+
     pcm_start(session->pcm_tx);
     pcm_start(session->pcm_rx);
 
@@ -309,6 +312,10 @@
         session_id = voice_get_active_session_id(adev);
         ret = platform_set_incall_recording_session_id(adev->platform,
                                                        session_id, rec_mode);
+#ifdef INCALL_STEREO_CAPTURE_ENABLED
+        ret = platform_set_incall_recording_session_channels(adev->platform,
+                                                        in->config.channels);
+#endif
         ALOGV("%s: Update usecase to %d",__func__, in->usecase);
     } else {
         ALOGV("%s: voice call not active", __func__);
diff --git a/hal/voice_extn/voice_extn.c b/hal/voice_extn/voice_extn.c
index edf5523..c4b8cff 100644
--- a/hal/voice_extn/voice_extn.c
+++ b/hal/voice_extn/voice_extn.c
@@ -21,7 +21,7 @@
 #include <errno.h>
 #include <math.h>
 #include <stdlib.h>
-#include <cutils/log.h>
+#include <log/log.h>
 #include <cutils/str_parms.h>
 #include <sys/ioctl.h>
 #include <sound/voice_params.h>
@@ -576,22 +576,14 @@
 int voice_extn_check_and_set_incall_music_usecase(struct audio_device *adev,
                                                   struct stream_out *out)
 {
-    uint32_t session_id = 0;
-
-    session_id = get_session_id_with_state(adev, CALL_LOCAL_HOLD);
-    if (session_id == VOICE_VSID) {
-        out->usecase = USECASE_INCALL_MUSIC_UPLINK;
-    } else if (session_id == VOICE2_VSID) {
-        out->usecase = USECASE_INCALL_MUSIC_UPLINK2;
-    } else {
-        ALOGE("%s: Invalid session id %x", __func__, session_id);
-        return -EINVAL;
-    }
-
+    out->usecase = USECASE_INCALL_MUSIC_UPLINK;
     out->config = pcm_config_incall_music;
-    out->supported_channel_masks[0] = AUDIO_CHANNEL_OUT_MONO;
-    out->channel_mask = AUDIO_CHANNEL_OUT_MONO;
+    //FIXME: add support for MONO stream configuration when audioflinger mixer supports it
+    out->supported_channel_masks[0] = AUDIO_CHANNEL_OUT_STEREO;
+    out->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+    out->config.rate = out->sample_rate;
 
+    ALOGV("%s: mode=%d, usecase id=%d", __func__, adev->mode, out->usecase);
     return 0;
 }
 #endif
diff --git a/post_proc/Android.mk b/post_proc/Android.mk
index 1a2f2e8..dc0e260 100644
--- a/post_proc/Android.mk
+++ b/post_proc/Android.mk
@@ -1,4 +1,4 @@
-ifneq ($(filter msm8974 msm8226 msm8084 msm8992 msm8994 msm8996 msm8909 msm8998,$(TARGET_BOARD_PLATFORM)),)
+ifneq ($(filter msm8974 msm8226 msm8084 msm8992 msm8994 msm8996 msm8909 msm8998 sdm845,$(TARGET_BOARD_PLATFORM)),)
 
 LOCAL_PATH:= $(call my-dir)
 
@@ -43,7 +43,7 @@
 
 ################################################################################
 
-ifneq ($(filter msm8992 msm8994 msm8996 msm8909 msm8998,$(TARGET_BOARD_PLATFORM)),)
+ifneq ($(filter msm8992 msm8994 msm8996 msm8909 msm8998 sdm845,$(TARGET_BOARD_PLATFORM)),)
 
 include $(CLEAR_VARS)
 
@@ -73,3 +73,36 @@
 include $(BUILD_SHARED_LIBRARY)
 
 endif
+
+################################################################################
+ifeq ($(strip $(AUDIO_FEATURE_ENABLED_MAXX_AUDIO)), true)
+
+include $(CLEAR_VARS)
+
+LOCAL_CFLAGS := -D HAL_LIB_NAME=\"audio.primary."$(TARGET_BOARD_PLATFORM)".so\"
+
+LOCAL_SRC_FILES:= \
+	ma_listener.c
+
+LOCAL_CFLAGS += $(qcom_post_proc_common_cflags)
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+	liblog \
+	libdl
+
+LOCAL_MODULE_RELATIVE_PATH := soundfx
+LOCAL_MODULE:= libmalistener
+LOCAL_MODULE_OWNER := google
+LOCAL_PROPRIETARY_MODULE := true
+
+LOCAL_C_INCLUDES := \
+	hardware/qcom/audio/hal \
+	system/media/audio/include/system \
+	$(call include-path-for, audio-effects)
+
+LOCAL_HEADER_LIBRARIES += libhardware_headers
+LOCAL_HEADER_LIBRARIES += libsystem_headers
+include $(BUILD_SHARED_LIBRARY)
+
+endif
diff --git a/post_proc/ma_listener.c b/post_proc/ma_listener.c
new file mode 100644
index 0000000..02d45d3
--- /dev/null
+++ b/post_proc/ma_listener.c
@@ -0,0 +1,629 @@
+/*
+ * Copyright (C) 2018 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 "ma_listener"
+/*#define LOG_NDEBUG 0*/
+
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <math.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <cutils/list.h>
+#include <cutils/log.h>
+#include <hardware/audio_effect.h>
+#include <audio-base.h>
+
+#define MA_FLAG ( EFFECT_FLAG_TYPE_INSERT | \
+                   EFFECT_FLAG_VOLUME_IND | \
+                   EFFECT_FLAG_DEVICE_IND | \
+                   EFFECT_FLAG_OFFLOAD_SUPPORTED | \
+                   EFFECT_FLAG_NO_PROCESS)
+
+#define PRINT_STREAM_TYPE(i) ALOGV("descriptor found and is of stream type %s ",\
+                                                            i == AUDIO_STREAM_MUSIC ? "MUSIC": \
+                                                            i == AUDIO_STREAM_RING ? "RING": \
+                                                            i == AUDIO_STREAM_ALARM ? "ALARM": \
+                                                            i == AUDIO_STREAM_VOICE_CALL ? "Voice_call": \
+                                                            i == AUDIO_STREAM_SYSTEM ? "SYSTEM":       \
+                                                            i == AUDIO_STREAM_NOTIFICATION ? "Notification":\
+                                                            "--INVALID--"); \
+
+#define MA_SET_STATE "audio_hw_send_ma_parameter"
+#define HAL_VENDOR_PATH "/vendor/lib/hw"
+
+enum {
+    MA_LISTENER_STATE_UNINITIALIZED,
+    MA_LISTENER_STATE_INITIALIZED,
+    MA_LISTENER_STATE_ACTIVE,
+};
+
+typedef struct ma_listener_context_s ma_listener_context_t;
+static const struct effect_interface_s effect_interface;
+
+struct ma_state {
+    float vol;
+    bool active;
+};
+
+static const audio_stream_type_t MIN_STREAM_TYPES = AUDIO_STREAM_VOICE_CALL;
+static const audio_stream_type_t MAX_STREAM_TYPES = AUDIO_STREAM_NOTIFICATION;
+static struct ma_state g_cur_state[MAX_STREAM_TYPES + 1];
+
+struct ma_listener_context_s {
+    const struct effect_interface_s *itfe;
+    struct listnode effect_list_node;
+    effect_config_t config;
+    const effect_descriptor_t *desc;
+    uint32_t stream_type;
+    uint32_t session_id;
+    uint32_t state;
+    uint32_t dev_id;
+    float left_vol;
+    float right_vol;
+};
+
+/* voice UUID: 4ece09c2-3728-11e8-a9f9-fc4dd4486b6d */
+const effect_descriptor_t ma_listener_voice_descriptor = {
+    { 0x46f924ed, 0x25c3, 0x4272, 0x85b1, { 0x41, 0x78, 0x3f, 0x0d, 0xc6, 0x38 } },  // type
+    { 0x4ece09c2, 0x3728, 0x11e8, 0xa9f9, { 0xfc, 0x4d, 0xd4, 0x48, 0x6b, 0x6d } },  // uuid
+    EFFECT_CONTROL_API_VERSION,
+    MA_FLAG,
+    0, /* TODO */
+    1,
+    "MAXXAUDIO listener for Voice",
+    "The Android Open Source Project",
+};
+
+/* system UUID: 4f705ff6-3728-11e8-a0c6-fc4dd4486b6d */
+const effect_descriptor_t ma_listener_system_descriptor = {
+    { 0x8bd0f979, 0x5266, 0x4791, 0x9213, { 0x11, 0x3a, 0xbc, 0xf7, 0xd3, 0xdc } },  // type
+    { 0x4f705ff6, 0x3728, 0x11e8, 0xa0c6, { 0xfc, 0x4d, 0xd4, 0x48, 0x6b, 0x6d } },  // uuid
+    EFFECT_CONTROL_API_VERSION,
+    MA_FLAG,
+    0, /* TODO */
+    1,
+    "MAXXAUDIO listener for System",
+    "The Android Open Source Project",
+};
+
+/* ring UUID: 4fd6e5c8-3728-11e8-8303-fc4dd4486b6d */
+const effect_descriptor_t ma_listener_ring_descriptor = {
+    { 0x5380692a, 0x872e, 0x4697, 0x8e38, { 0xcd, 0xd1, 0x09, 0xf6, 0xcb, 0x87 } },  // type
+    { 0x4fd6e5c8, 0x3728, 0x11e8, 0x8303, { 0xfc, 0x4d, 0xd4, 0x48, 0x6b, 0x6d } },  // uuid
+    EFFECT_CONTROL_API_VERSION,
+    MA_FLAG,
+    0, /* TODO */
+    1,
+    "MAXXAUDIO listener for Ring",
+    "The Android Open Source Project",
+};
+/* music UUID: 5036194e-3728-11e8-8db9-fc4dd4486b6d */
+const effect_descriptor_t ma_listener_music_descriptor = {
+    { 0x3a3a19b2, 0x62b1, 0x4785, 0xb55e, { 0xb2, 0x8f, 0xd4, 0x1b, 0x83, 0x58 } },  // type
+    { 0x5036194e, 0x3728, 0x11e8, 0x8db9, { 0xfc, 0x4d, 0xd4, 0x48, 0x6b, 0x6d } },  // uuid
+    EFFECT_CONTROL_API_VERSION,
+    MA_FLAG,
+    0, /* TODO */
+    1,
+    "MAXXAUDIO listener for Music",
+    "The Android Open Source Project",
+};
+/* alarm UUID: 50b9f084-3728-11e8-9225-fc4dd4486b6d */
+const effect_descriptor_t ma_listener_alarm_descriptor = {
+    { 0x8d08d30f, 0xb4c3, 0x4600, 0x8f99, { 0xfc, 0xbb, 0x5d, 0x05, 0x8b, 0x60 } },  // type
+    { 0x50b9f084, 0x3728, 0x11e8, 0x9225, { 0xfc, 0x4d, 0xd4, 0x48, 0x6b, 0x6d } },  // uuid
+    EFFECT_CONTROL_API_VERSION,
+    MA_FLAG,
+    0, /* TODO */
+    1,
+    "MAXXAUDIO listener for Alarm",
+    "The Android Open Source Project",
+};
+/* notification UUID: 50fe4d56-3728-11e8-ac73-fc4dd4486b6d */
+const effect_descriptor_t ma_listener_notification_descriptor = {
+    { 0x513d09f5, 0xae7f, 0x483d, 0x922a, { 0x5c, 0x72, 0xc5, 0xe5, 0x68, 0x4c } },  // type
+    { 0x50fe4d56, 0x3728, 0x11e8, 0xac73, { 0xfc, 0x4d, 0xd4, 0x48, 0x6b, 0x6d } },  // uuid
+    EFFECT_CONTROL_API_VERSION,
+    MA_FLAG,
+    0, /* TODO */
+    1,
+    "MAXXAUDIO listener for Notification",
+    "The Android Open Source Project",
+};
+
+static const effect_descriptor_t *descriptors[] = {
+    &ma_listener_voice_descriptor,
+    &ma_listener_system_descriptor,
+    &ma_listener_ring_descriptor,
+    &ma_listener_music_descriptor,
+    &ma_listener_alarm_descriptor,
+    &ma_listener_notification_descriptor,
+    NULL,
+};
+
+/* function of sending state to HAL */
+static bool (*send_ma_parameter)(int, float, bool);
+
+static int init_state = -1;
+pthread_once_t once = PTHREAD_ONCE_INIT;
+
+struct listnode ma_effect_list;
+pthread_mutex_t ma_listner_init_lock;
+
+/*
+ *  Local functions
+ */
+static inline bool valid_dev_in_context(struct ma_listener_context_s *context)
+{
+    /* check device */
+    if ((context->dev_id & AUDIO_DEVICE_OUT_SPEAKER) ||
+        (context->dev_id & AUDIO_DEVICE_OUT_SPEAKER_SAFE) ||
+        /* TODO: it should be dynamic if hybird split A2SP is implemented. */
+        (context->dev_id & AUDIO_DEVICE_OUT_ALL_A2DP) ||
+        (context->dev_id & AUDIO_DEVICE_OUT_ALL_USB)) {
+        return true;
+    }
+
+    return false;
+}
+
+static void check_and_set_ma_parameter(uint32_t stream_type)
+{
+    bool active = false;
+    float temp_vol = 0.0;
+    float max_vol = 0.0;
+    struct listnode *node = NULL;
+    ma_listener_context_t *context = NULL;
+
+    ALOGV("%s .. called ..", __func__);
+    // get maximum volume for the active session with same strem type
+    list_for_each(node, &ma_effect_list) {
+        context = node_to_item(node, struct ma_listener_context_s, effect_list_node);
+        if (context->stream_type == stream_type &&
+            valid_dev_in_context(context)) {
+            if (context->state == MA_LISTENER_STATE_ACTIVE) {
+                active = true;
+                temp_vol = fmax(context->left_vol, context->right_vol);
+                if (max_vol < temp_vol)
+                    max_vol = temp_vol;
+                ALOGV("%s: check session(%d) volume(%f) for stream(%d)",
+                       __func__, context->session_id, temp_vol, stream_type);
+            }
+        }
+    }
+
+    // check volume
+    if (max_vol < 0.0) max_vol = 0;
+    else if (max_vol > 1.0) max_vol = 1.0;
+
+    if (send_ma_parameter != NULL &&
+        stream_type >= MIN_STREAM_TYPES &&
+        stream_type <= MAX_STREAM_TYPES &&
+        (g_cur_state[stream_type].vol != max_vol ||
+         g_cur_state[stream_type].active != active)) {
+
+        ALOGV("%s: set stream(%d) active(%s->%s) volume(%f->%f)",
+              __func__, stream_type,
+              g_cur_state[stream_type].active ? "T" : "F", active ? "T" : "F",
+              g_cur_state[stream_type].vol, max_vol);
+
+        // update changes to hal
+        send_ma_parameter(stream_type, max_vol, active);
+        g_cur_state[stream_type].vol = max_vol;
+        g_cur_state[stream_type].active = active;
+    }
+
+    return;
+}
+
+/*
+ * Effect Control Interface Implementation
+ */
+static int ma_effect_command(effect_handle_t self,
+                              uint32_t cmd_code, uint32_t cmd_size,
+                              void *p_cmd_data, uint32_t *reply_size,
+                              void *p_reply_data)
+{
+    ma_listener_context_t *context = (ma_listener_context_t *)self;
+    int status = 0;
+
+    ALOGV("%s .. called ..", __func__);
+    pthread_mutex_lock(&ma_listner_init_lock);
+
+    if (context == NULL || context->state == MA_LISTENER_STATE_UNINITIALIZED) {
+        ALOGE("%s: %s is NULL", __func__, (context == NULL) ?
+              "context" : "context->state");
+        status = -EINVAL;
+        goto exit;
+    }
+
+    switch (cmd_code) {
+    case EFFECT_CMD_INIT:
+        ALOGV("%s :: cmd called EFFECT_CMD_INIT", __func__);
+        if (p_reply_data == NULL || *reply_size != sizeof(int)) {
+            ALOGE("%s: EFFECT_CMD_INIT: %s, sending -EINVAL", __func__,
+                  (p_reply_data == NULL) ? "p_reply_data is NULL" :
+                  "*reply_size != sizeof(int)");
+            status = -EINVAL;
+            goto exit;
+        }
+        *(int *)p_reply_data = 0;
+        break;
+
+    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)) {
+            status = -EINVAL;
+            goto exit;
+        }
+        context->config = *(effect_config_t *)p_cmd_data;
+        *(int *)p_reply_data = 0;
+        break;
+
+    case EFFECT_CMD_GET_CONFIG:
+        ALOGV("%s :: cmd called EFFECT_CMD_GET_CONFIG", __func__);
+        break;
+
+    case EFFECT_CMD_RESET:
+        ALOGV("%s :: cmd called EFFECT_CMD_RESET", __func__);
+        break;
+
+    case EFFECT_CMD_SET_AUDIO_MODE:
+        ALOGV("%s :: cmd called EFFECT_CMD_SET_AUDIO_MODE", __func__);
+        break;
+
+    case EFFECT_CMD_OFFLOAD:
+        ALOGV("%s :: cmd called EFFECT_CMD_OFFLOAD", __func__);
+        if (p_reply_data == NULL || *reply_size != sizeof(int)) {
+            ALOGE("%s: EFFECT_CMD_OFFLOAD: %s, sending -EINVAL", __func__,
+                  (p_reply_data == NULL) ? "p_reply_data is NULL" :
+                  "*reply_size != sizeof(int)");
+            status = -EINVAL;
+            goto exit;
+        }
+        *(int *)p_reply_data = 0;
+        break;
+
+    case EFFECT_CMD_ENABLE:
+        ALOGV("%s :: cmd called EFFECT_CMD_ENABLE", __func__);
+        if (p_reply_data == NULL || *reply_size != sizeof(int)) {
+            ALOGE("%s: EFFECT_CMD_ENABLE: %s, sending -EINVAL", __func__,
+                   (p_reply_data == NULL) ? "p_reply_data is NULL" :
+                   "*reply_size != sizeof(int)");
+            status = -EINVAL;
+            goto exit;
+        }
+
+        if (context->state != MA_LISTENER_STATE_INITIALIZED) {
+            ALOGE("%s: EFFECT_CMD_ENABLE : state not INITIALIZED", __func__);
+            status = -ENOSYS;
+            goto exit;
+        }
+
+        context->state = MA_LISTENER_STATE_ACTIVE;
+        *(int *)p_reply_data = 0;
+
+        // After changing the state and if device is valid
+        // check and send state
+        if (valid_dev_in_context(context)) {
+            check_and_set_ma_parameter(context->stream_type);
+        }
+
+        break;
+
+    case EFFECT_CMD_DISABLE:
+        ALOGV("%s :: cmd called EFFECT_CMD_DISABLE", __func__);
+        if (p_reply_data == NULL || *reply_size != sizeof(int)) {
+            ALOGE("%s: EFFECT_CMD_DISABLE: %s, sending -EINVAL", __func__,
+                  (p_reply_data == NULL) ? "p_reply_data is NULL" :
+                  "*reply_size != sizeof(int)");
+            status = -EINVAL;
+            goto exit;
+        }
+
+        if (context->state != MA_LISTENER_STATE_ACTIVE) {
+            ALOGE("%s: EFFECT_CMD_ENABLE : state not ACTIVE", __func__);
+            status = -ENOSYS;
+            goto exit;
+        }
+
+        context->state = MA_LISTENER_STATE_INITIALIZED;
+        *(int *)p_reply_data = 0;
+
+        // After changing the state and if device is valid
+        // check and send state
+        if (valid_dev_in_context(context)) {
+            check_and_set_ma_parameter(context->stream_type);
+        }
+
+        break;
+
+    case EFFECT_CMD_GET_PARAM:
+        ALOGV("%s :: cmd called EFFECT_CMD_GET_PARAM", __func__);
+        break;
+
+    case EFFECT_CMD_SET_PARAM:
+        ALOGV("%s :: cmd called EFFECT_CMD_SET_PARAM", __func__);
+        break;
+
+    case EFFECT_CMD_SET_DEVICE:
+    {
+        uint32_t new_device;
+
+        if (p_cmd_data == NULL) {
+            ALOGE("%s: EFFECT_CMD_SET_DEVICE: cmd data NULL", __func__);
+            status = -EINVAL;
+            goto exit;
+        }
+
+        new_device = *(uint32_t *)p_cmd_data;
+        ALOGV("%s :: EFFECT_CMD_SET_DEVICE: (current/new) device (0x%x / 0x%x)",
+               __func__, context->dev_id, new_device);
+
+        context->dev_id = new_device;
+        // After changing the state and if device is valid
+        // check and send parameter
+        if (valid_dev_in_context(context)) {
+            check_and_set_ma_parameter(context->stream_type);
+        }
+    }
+    break;
+
+    case EFFECT_CMD_SET_VOLUME:
+    {
+        float left_vol = 0, right_vol = 0;
+
+        ALOGV("cmd called EFFECT_CMD_SET_VOLUME");
+        if (p_cmd_data == NULL || cmd_size != 2 * sizeof(uint32_t)) {
+            ALOGE("%s: EFFECT_CMD_SET_VOLUME: %s", __func__, (p_cmd_data == NULL) ?
+                  "p_cmd_data is NULL" : "cmd_size issue");
+            status = -EINVAL;
+            goto exit;
+        }
+
+        left_vol = (float)(*(uint32_t *)p_cmd_data) / (1 << 24);
+        right_vol = (float)(*((uint32_t *)p_cmd_data + 1)) / (1 << 24);
+        ALOGV("Current Volume (%f / %f ) new Volume (%f / %f)", context->left_vol,
+              context->right_vol, left_vol, right_vol);
+
+        context->left_vol = left_vol;
+        context->right_vol = right_vol;
+
+        // After changing the state and if device is valid
+        // check and send volume
+        if (valid_dev_in_context(context)) {
+            check_and_set_ma_parameter(context->stream_type);
+        }
+    }
+    break;
+
+    default:
+        ALOGW("%s: unknow command %d", __func__, cmd_code);
+        status = -ENOSYS;
+        break;
+    }
+
+exit:
+    pthread_mutex_unlock(&ma_listner_init_lock);
+    return status;
+}
+
+/* Effect Control Interface Implementation: get_descriptor */
+static int ma_effect_get_descriptor(effect_handle_t   self,
+                                     effect_descriptor_t *descriptor)
+{
+    ma_listener_context_t *context = (ma_listener_context_t *)self;
+    ALOGV("%s .. called ..", __func__);
+
+    if (descriptor == NULL) {
+        ALOGE("%s: descriptor is NULL", __func__);
+        return -EINVAL;
+    }
+
+    *descriptor = *context->desc;
+    return 0;
+}
+
+static void init_once()
+{
+    int ret = 0;
+    void *handle = NULL;
+    char lib_path[PATH_MAX] = {0};
+
+    if (init_state == 0) {
+        ALOGD("%s : already init ... do nothing", __func__);
+        return;
+    }
+
+    ALOGV("%s .. called ..", __func__);
+
+    send_ma_parameter = NULL;
+
+    ret = snprintf(lib_path, PATH_MAX, "%s/%s", HAL_VENDOR_PATH, HAL_LIB_NAME);
+    if (ret < 0) {
+        ALOGE("%s: snprintf failed for lib %s ret %d", __func__, HAL_LIB_NAME, ret);
+        return;
+    }
+
+    handle = dlopen(lib_path, RTLD_NOW);
+    if (handle == NULL) {
+        ALOGE("%s: DLOPEN failed for %s", __func__, HAL_LIB_NAME);
+        return;
+    } else {
+        ALOGV("%s: DLOPEN successful for %s", __func__, HAL_LIB_NAME);
+        send_ma_parameter = (bool (*)(int, float, bool))dlsym(handle, MA_SET_STATE);
+
+        if (!send_ma_parameter) {
+            ALOGE("%s: dlsym error %s for send_ma_parameter", __func__, dlerror());
+            return;
+        }
+    }
+
+    pthread_mutex_init(&ma_listner_init_lock, NULL);
+    list_init(&ma_effect_list);
+    init_state = 0;
+
+    ALOGD("%s: exit ret %d", __func__, init_state);
+}
+
+static bool lib_init()
+{
+    pthread_once(&once, init_once);
+    return init_state;
+}
+
+static int ma_prc_lib_create(const effect_uuid_t *uuid,
+                              int32_t session_id,
+                              int32_t io_id __unused,
+                              effect_handle_t *p_handle)
+{
+    int itt = 0;
+    ma_listener_context_t *context = NULL;
+
+    ALOGV("%s .. called ..", __func__);
+
+    if (lib_init() != 0) {
+        return init_state;
+    }
+
+    if (p_handle == NULL || uuid == NULL) {
+        ALOGE("%s: %s is NULL", __func__, (p_handle == NULL) ? "p_handle" : "uuid");
+        return -EINVAL;
+    }
+
+    context = (ma_listener_context_t *)calloc(1, sizeof(ma_listener_context_t));
+
+    if (context == NULL) {
+        ALOGE("%s: failed to allocate for context .. oops !!", __func__);
+        return -EINVAL;
+    }
+
+    // check if UUID is supported
+    for (itt = 0; descriptors[itt] != NULL; itt++) {
+        if (memcmp(uuid, &descriptors[itt]->uuid, sizeof(effect_uuid_t)) == 0) {
+            context->desc = descriptors[itt];
+            context->stream_type = itt;
+            PRINT_STREAM_TYPE(itt)
+            break;
+        }
+    }
+
+    if (descriptors[itt] == NULL) {
+        ALOGE("%s .. couldnt find passed uuid, something wrong", __func__);
+        free(context);
+        return -EINVAL;
+    }
+
+    ALOGV("%s CREATED_CONTEXT %p", __func__, context);
+
+    context->itfe = &effect_interface;
+    context->state = MA_LISTENER_STATE_INITIALIZED;
+    context->dev_id = AUDIO_DEVICE_NONE;
+    context->session_id = session_id;
+
+    // Add this to master list
+    pthread_mutex_lock(&ma_listner_init_lock);
+    list_add_tail(&ma_effect_list, &context->effect_list_node);
+
+    pthread_mutex_unlock(&ma_listner_init_lock);
+
+    *p_handle = (effect_handle_t)context;
+    return 0;
+}
+
+static int ma_prc_lib_release(effect_handle_t handle)
+{
+    struct listnode *node, *temp_node_next;
+    ma_listener_context_t *context = NULL;
+    ma_listener_context_t *recv_contex = (ma_listener_context_t *)handle;
+    int status = -EINVAL;
+
+    ALOGV("%s: context %p", __func__, handle);
+
+    if (recv_contex == NULL) {
+        return status;
+    }
+
+    pthread_mutex_lock(&ma_listner_init_lock);
+    // check if the handle/context provided is valid
+    list_for_each_safe(node, temp_node_next, &ma_effect_list) {
+        context = node_to_item(node, struct ma_listener_context_s, effect_list_node);
+        if (context == recv_contex) {
+            ALOGV("--- Found something to remove ---");
+            list_remove(node);
+            PRINT_STREAM_TYPE(context->stream_type);
+            free(context);
+            status = 0;
+        }
+    }
+
+    if (status != 0) {
+        ALOGE("%s: nothing to remove, ret %d", __func__, status);
+        pthread_mutex_unlock(&ma_listner_init_lock);
+        return status;
+    }
+
+    pthread_mutex_unlock(&ma_listner_init_lock);
+    return status;
+}
+
+static int ma_prc_lib_get_descriptor(const effect_uuid_t *uuid,
+                                      effect_descriptor_t *descriptor)
+{
+    int i = 0;
+
+    ALOGV("%s .. called ..", __func__);
+    if (lib_init() != 0) {
+        return init_state;
+    }
+
+    if (descriptor == NULL || uuid == NULL) {
+        ALOGE("%s: %s is NULL", __func__, (descriptor == NULL) ? "descriptor" : "uuid");
+        return -EINVAL;
+    }
+
+    for (i = 0; descriptors[i] != NULL; i++) {
+        if (memcmp(uuid, &descriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) {
+            *descriptor = *descriptors[i];
+            return 0;
+        }
+    }
+
+    ALOGE("%s: couldnt found uuid passed, oops", __func__);
+    return -EINVAL;
+}
+
+
+/* effect_handle_t interface implementation for volume listener effect */
+static const struct effect_interface_s effect_interface = {
+    NULL,
+    ma_effect_command,
+    ma_effect_get_descriptor,
+    NULL,
+};
+
+__attribute__((visibility("default")))
+audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
+    .tag = AUDIO_EFFECT_LIBRARY_TAG,
+    .version = EFFECT_LIBRARY_API_VERSION,
+    .name = "MAXXAUDIO Listener Effect Library",
+    .implementor = "The Android Open Source Project",
+    .create_effect = ma_prc_lib_create,
+    .release_effect = ma_prc_lib_release,
+    .get_descriptor = ma_prc_lib_get_descriptor,
+};