Merge "configs: Modify headphone power mode to low power"
diff --git a/configs/lito/mixer_paths_qrd.xml b/configs/lito/mixer_paths_qrd.xml
index 25b0b13..7203e77 100644
--- a/configs/lito/mixer_paths_qrd.xml
+++ b/configs/lito/mixer_paths_qrd.xml
@@ -2605,8 +2605,8 @@
          <ctl name="TX DMIC MUX1" value="DMIC1" />
          <ctl name="TX_AIF1_CAP Mixer DEC2" value="1" />
          <ctl name="TX DMIC MUX2" value="DMIC3" />
-         <ctl name="TX_AIF1_CAP Mixer DEC4" value="1" />
-         <ctl name="TX DMIC MUX2" value="DMIC3" />
+         <ctl name="TX_AIF1_CAP Mixer DEC3" value="1" />
+         <ctl name="TX DMIC MUX3" value="DMIC4" />
     </path>
 
     <path name="voice-speaker-qmic">
diff --git a/configs/lito/sound_trigger_mixer_paths_qrd.xml b/configs/lito/sound_trigger_mixer_paths_qrd.xml
index ccbdd94..8e6513c 100644
--- a/configs/lito/sound_trigger_mixer_paths_qrd.xml
+++ b/configs/lito/sound_trigger_mixer_paths_qrd.xml
@@ -234,13 +234,13 @@
         <ctl name="VA_CDC_DMA_TX_0 Channels" value="Three" />
         <ctl name="VA_AIF1_CAP Mixer DEC0" value="1" />
         <ctl name="VA_AIF1_CAP Mixer DEC1" value="1" />
-        <ctl name="VA_AIF1_CAP Mixer DEC5" value="1" />
+        <ctl name="VA_AIF1_CAP Mixer DEC2" value="1" />
         <ctl name="VA DEC0 MUX" value="MSM_DMIC" />
         <ctl name="VA DEC1 MUX" value="MSM_DMIC" />
-        <ctl name="VA DEC5 MUX" value="MSM_DMIC" />
+        <ctl name="VA DEC2 MUX" value="MSM_DMIC" />
         <ctl name="VA DMIC MUX0" value="DMIC1" />
         <ctl name="VA DMIC MUX1" value="DMIC2" />
-        <ctl name="VA DMIC MUX5" value="DMIC5" />
+        <ctl name="VA DMIC MUX2" value="DMIC4" />
     </path>
 
     <path name="listen-ape-handset-qmic">
@@ -248,15 +248,15 @@
         <ctl name="VA_AIF1_CAP Mixer DEC0" value="1" />
         <ctl name="VA_AIF1_CAP Mixer DEC1" value="1" />
         <ctl name="VA_AIF1_CAP Mixer DEC2" value="1" />
-        <ctl name="VA_AIF1_CAP Mixer DEC5" value="1" />
+        <ctl name="VA_AIF1_CAP Mixer DEC3" value="1" />
         <ctl name="VA DEC0 MUX" value="MSM_DMIC" />
         <ctl name="VA DEC1 MUX" value="MSM_DMIC" />
         <ctl name="VA DEC2 MUX" value="MSM_DMIC" />
-        <ctl name="VA DEC5 MUX" value="MSM_DMIC" />
+        <ctl name="VA DEC3 MUX" value="MSM_DMIC" />
         <ctl name="VA DMIC MUX0" value="DMIC1" />
         <ctl name="VA DMIC MUX1" value="DMIC2" />
         <ctl name="VA DMIC MUX2" value="DMIC3" />
-        <ctl name="VA DMIC MUX5" value="DMIC5" />
+        <ctl name="VA DMIC MUX3" value="DMIC4" />
     </path>
 
     <path name="listen-ape-headset-mic">
diff --git a/configs/msmnile/audio_platform_info.xml b/configs/msmnile/audio_platform_info.xml
index 6bfadc8..80924e2 100644
--- a/configs/msmnile/audio_platform_info.xml
+++ b/configs/msmnile/audio_platform_info.xml
@@ -143,7 +143,9 @@
         <device name="SND_DEVICE_OUT_VOICE_ANC_FB_HEADSET" backend="headphones" interface="SLIMBUS_6_RX"/>
         <device name="SND_DEVICE_OUT_VOICE_LINE" backend="headphones" interface="SLIMBUS_6_RX"/>
         <device name="SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES" backend="headphones" interface="SLIMBUS_6_RX"/>
+        <device name="SND_DEVICE_OUT_VOICE_TTY_FULL_HEADSET" backend="headset" interface="SLIMBUS_6_RX"/>
         <device name="SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES" backend="headphones" interface="SLIMBUS_6_RX"/>
+        <device name="SND_DEVICE_OUT_VOICE_TTY_VCO_HEADSET" backend="headset" interface="SLIMBUS_6_RX"/>
         <device name="SND_DEVICE_OUT_SPEAKER_AND_BT_SCO" backend="speaker-and-bt-sco" interface="SLIMBUS_0_RX-and-SLIMBUS_7_RX"/>
         <device name="SND_DEVICE_OUT_SPEAKER_AND_BT_SCO_WB" backend="speaker-and-bt-sco-wb" interface="SLIMBUS_0_RX-and-SLIMBUS_7_RX"/>
         <device name="SND_DEVICE_IN_HEADSET_MIC" backend="headset-mic" interface="SLIMBUS_1_TX"/>
diff --git a/configs/msmnile/mixer_paths_tavil.xml b/configs/msmnile/mixer_paths_tavil.xml
index f2e4842..fb315bf 100644
--- a/configs/msmnile/mixer_paths_tavil.xml
+++ b/configs/msmnile/mixer_paths_tavil.xml
@@ -2988,11 +2988,21 @@
         <path name="tty-headphones" />
     </path>
 
+    <path name="voice-tty-full-headset">
+        <ctl name="TTY Mode" value="FULL" />
+        <path name="tty-headphones" />
+    </path>
+
     <path name="voice-tty-vco-headphones">
         <ctl name="TTY Mode" value="VCO" />
         <path name="tty-headphones" />
     </path>
 
+    <path name="voice-tty-vco-headset">
+        <ctl name="TTY Mode" value="VCO" />
+        <path name="tty-headphones" />
+    </path>
+
     <path name="voice-tty-hco-handset">
         <ctl name="TTY Mode" value="HCO" />
         <path name="handset" />
@@ -3011,7 +3021,7 @@
     <path name="voice-tty-full-headset-mic">
         <path name="amic2" />
         <ctl name="ADC2 Volume" value="0" />
-        <ctl name="DEC0 Volume" value="84" />
+        <ctl name="DEC1 Volume" value="84" />
     </path>
 
     <path name="voice-tty-hco-headset-mic">
diff --git a/configs/msmsteppe/msmsteppe.mk b/configs/msmsteppe/msmsteppe.mk
index 9529d40..9f05f07 100644
--- a/configs/msmsteppe/msmsteppe.mk
+++ b/configs/msmsteppe/msmsteppe.mk
@@ -240,6 +240,10 @@
 PRODUCT_PROPERTY_OVERRIDES += \
 vendor.audio.offload.buffer.size.kb=32
 
+#Minimum duration for offload playback in secs
+PRODUCT_PROPERTY_OVERRIDES += \
+audio.offload.min.duration.secs=30
+
 #Enable offload audio video playback by default
 PRODUCT_PROPERTY_OVERRIDES += \
 audio.offload.video=true
@@ -388,7 +392,7 @@
 vendor.audio.feature.spkr_prot.enable=true \
 vendor.audio.feature.ssrec.enable=true \
 vendor.audio.feature.usb_offload.enable=true \
-vendor.audio.feature.usb_offload_burst_mode.enable=false \
+vendor.audio.feature.usb_offload_burst_mode.enable=true \
 vendor.audio.feature.usb_offload_sidetone_volume.enable=false \
 vendor.audio.feature.deepbuffer_as_primary.enable=false \
 vendor.audio.feature.vbat.enable=true \
diff --git a/configs/sdm845/sdm845.mk b/configs/sdm845/sdm845.mk
index 4fb1485..0ede765 100644
--- a/configs/sdm845/sdm845.mk
+++ b/configs/sdm845/sdm845.mk
@@ -186,6 +186,10 @@
 PRODUCT_PROPERTY_OVERRIDES += \
 vendor.audio.offload.buffer.size.kb=32
 
+#Minimum duration for offload playback in secs
+PRODUCT_PROPERTY_OVERRIDES += \
+audio.offload.min.duration.secs=30
+
 #Enable offload audio video playback by default
 PRODUCT_PROPERTY_OVERRIDES += \
 audio.offload.video=true
diff --git a/hal/audio_extn/audio_extn.c b/hal/audio_extn/audio_extn.c
index b07e901..f47cf70 100644
--- a/hal/audio_extn/audio_extn.c
+++ b/hal/audio_extn/audio_extn.c
@@ -366,9 +366,9 @@
     }
 }
 
-static int update_custom_mtmx_coefficients(struct audio_device *adev,
-                                           struct audio_custom_mtmx_params *params,
-                                           int pcm_device_id)
+static int update_custom_mtmx_coefficients_v2(struct audio_device *adev,
+                                              struct audio_custom_mtmx_params *params,
+                                              int pcm_device_id)
 {
     struct mixer_ctl *ctl = NULL;
     char *mixer_name_prefix = "AudStr";
@@ -430,9 +430,9 @@
     return 0;
 }
 
-static void set_custom_mtmx_params(struct audio_device *adev,
-                                   struct audio_custom_mtmx_params_info *pinfo,
-                                   int pcm_device_id, bool enable)
+static void set_custom_mtmx_params_v2(struct audio_device *adev,
+                                      struct audio_custom_mtmx_params_info *pinfo,
+                                      int pcm_device_id, bool enable)
 {
     struct mixer_ctl *ctl = NULL;
     char *mixer_name_prefix = "AudStr";
@@ -465,7 +465,7 @@
         ALOGE("%s: ERROR. Mixer ctl set failed", __func__);
 }
 
-void audio_extn_set_custom_mtmx_params(struct audio_device *adev,
+void audio_extn_set_custom_mtmx_params_v2(struct audio_device *adev,
                                         struct audio_usecase *usecase,
                                         bool enable)
 {
@@ -535,16 +535,402 @@
         params = platform_get_custom_mtmx_params(adev->platform, &info);
         if (params) {
             if (enable)
-                ret = update_custom_mtmx_coefficients(adev, params,
+                ret = update_custom_mtmx_coefficients_v2(adev, params,
                                                       pcm_device_id);
             if (ret < 0)
                 ALOGE("%s: error updating mtmx coeffs err:%d", __func__, ret);
             else
-                set_custom_mtmx_params(adev, &info, pcm_device_id, enable);
+                set_custom_mtmx_params_v2(adev, &info, pcm_device_id, enable);
         }
     }
 }
 
+static int set_custom_mtmx_output_channel_map(struct audio_device *adev,
+                                              char *mixer_name_prefix,
+                                              uint32_t ch_count,
+                                              bool enable)
+{
+    struct mixer_ctl *ctl = NULL;
+    char mixer_ctl_name[128] = {0};
+    int ret = 0;
+    int channel_map[AUDIO_MAX_DSP_CHANNELS] = {0};
+
+    ALOGV("%s channel_count %d", __func__, ch_count);
+
+    if (!enable) {
+        ALOGV("%s: reset output channel map", __func__);
+        goto exit;
+    }
+
+    switch (ch_count) {
+    case 2:
+        channel_map[0] = PCM_CHANNEL_FL;
+        channel_map[1] = PCM_CHANNEL_FR;
+        break;
+    case 4:
+        channel_map[0] = PCM_CHANNEL_FL;
+        channel_map[1] = PCM_CHANNEL_FR;
+        channel_map[2] = PCM_CHANNEL_LS;
+        channel_map[3] = PCM_CHANNEL_RS;
+        break;
+    case 6:
+        channel_map[0] = PCM_CHANNEL_FL;
+        channel_map[1] = PCM_CHANNEL_FR;
+        channel_map[2] = PCM_CHANNEL_FC;
+        channel_map[3] = PCM_CHANNEL_LFE;
+        channel_map[4] = PCM_CHANNEL_LS;
+        channel_map[5] = PCM_CHANNEL_RS;
+        break;
+    case 8:
+        channel_map[0] = PCM_CHANNEL_FL;
+        channel_map[1] = PCM_CHANNEL_FR;
+        channel_map[2] = PCM_CHANNEL_FC;
+        channel_map[3] = PCM_CHANNEL_LFE;
+        channel_map[4] = PCM_CHANNEL_LB;
+        channel_map[5] = PCM_CHANNEL_RB;
+        channel_map[6] = PCM_CHANNEL_LS;
+        channel_map[7] = PCM_CHANNEL_RS;
+        break;
+    case 10:
+        channel_map[0] = PCM_CHANNEL_FL;
+        channel_map[1] = PCM_CHANNEL_FR;
+        channel_map[2] = PCM_CHANNEL_LFE;
+        channel_map[3] = PCM_CHANNEL_FC;
+        channel_map[4] = PCM_CHANNEL_LB;
+        channel_map[5] = PCM_CHANNEL_RB;
+        channel_map[6] = PCM_CHANNEL_LS;
+        channel_map[7] = PCM_CHANNEL_RS;
+        channel_map[8] = PCM_CHANNEL_TFL;
+        channel_map[9] = PCM_CHANNEL_TFR;
+        break;
+    case 12:
+        channel_map[0] = PCM_CHANNEL_FL;
+        channel_map[1] = PCM_CHANNEL_FR;
+        channel_map[2] = PCM_CHANNEL_FC;
+        channel_map[3] = PCM_CHANNEL_LFE;
+        channel_map[4] = PCM_CHANNEL_LB;
+        channel_map[5] = PCM_CHANNEL_RB;
+        channel_map[6] = PCM_CHANNEL_LS;
+        channel_map[7] = PCM_CHANNEL_RS;
+        channel_map[8] = PCM_CHANNEL_TFL;
+        channel_map[9] = PCM_CHANNEL_TFR;
+        channel_map[10] = PCM_CHANNEL_TSL;
+        channel_map[11] = PCM_CHANNEL_TSR;
+        break;
+    case 14:
+        channel_map[0] = PCM_CHANNEL_FL;
+        channel_map[1] = PCM_CHANNEL_FR;
+        channel_map[2] = PCM_CHANNEL_LFE;
+        channel_map[3] = PCM_CHANNEL_FC;
+        channel_map[4] = PCM_CHANNEL_LB;
+        channel_map[5] = PCM_CHANNEL_RB;
+        channel_map[6] = PCM_CHANNEL_LS;
+        channel_map[7] = PCM_CHANNEL_RS;
+        channel_map[8] = PCM_CHANNEL_TFL;
+        channel_map[9] = PCM_CHANNEL_TFR;
+        channel_map[10] = PCM_CHANNEL_TSL;
+        channel_map[11] = PCM_CHANNEL_TSR;
+        channel_map[12] = PCM_CHANNEL_FLC;
+        channel_map[13] = PCM_CHANNEL_FRC;
+        break;
+    case 16:
+        channel_map[0] = PCM_CHANNEL_FL;
+        channel_map[1] = PCM_CHANNEL_FR;
+        channel_map[2] = PCM_CHANNEL_FC;
+        channel_map[3] = PCM_CHANNEL_LFE;
+        channel_map[4] = PCM_CHANNEL_LB;
+        channel_map[5] = PCM_CHANNEL_RB;
+        channel_map[6] = PCM_CHANNEL_LS;
+        channel_map[7] = PCM_CHANNEL_RS;
+        channel_map[8] = PCM_CHANNEL_TFL;
+        channel_map[9] = PCM_CHANNEL_TFR;
+        channel_map[10] = PCM_CHANNEL_TSL;
+        channel_map[11] = PCM_CHANNEL_TSR;
+        channel_map[12] = PCM_CHANNEL_FLC;
+        channel_map[13] = PCM_CHANNEL_FRC;
+        channel_map[14] = PCM_CHANNEL_RLC;
+        channel_map[15] = PCM_CHANNEL_RRC;
+        break;
+    default:
+        ALOGE("%s: unsupported channels(%d) for setting channel map",
+               __func__, ch_count);
+        return -EINVAL;
+    }
+
+exit:
+    snprintf(mixer_ctl_name, sizeof(mixer_ctl_name), "%s %s",
+             mixer_name_prefix, "Output Channel Map");
+
+    ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
+    if (!ctl) {
+        ALOGE("%s: ERROR. Could not get ctl for mixer cmd - %s",
+               __func__, mixer_ctl_name);
+        return -EINVAL;
+    }
+
+    ret = mixer_ctl_set_array(ctl, channel_map, ch_count);
+    return ret;
+}
+
+static int update_custom_mtmx_coefficients_v1(struct audio_device *adev,
+                                           struct audio_custom_mtmx_params *params,
+                                           struct audio_custom_mtmx_in_params *in_params,
+                                           int pcm_device_id,
+                                           usecase_type_t type,
+                                           bool enable)
+{
+    struct mixer_ctl *ctl = NULL;
+    char mixer_ctl_name[128] = {0};
+    struct audio_custom_mtmx_params_info *pinfo = &params->info;
+    char mixer_name_prefix[100];
+    int i = 0, err = 0, rule = 0;
+    uint32_t mtrx_row_cnt = 0, mtrx_column_cnt = 0;
+    int reset_coeffs[AUDIO_MAX_DSP_CHANNELS] = {0};
+
+    ALOGI("%s: ip_channels %d, op_channels %d, pcm_device_id %d, usecase type %d, enable %d",
+          __func__, pinfo->ip_channels, pinfo->op_channels, pcm_device_id,
+          type, enable);
+
+    if (!strcmp(pinfo->fe_name, "")) {
+        ALOGE("%s: Error. no front end defined", __func__);
+        return -EINVAL;
+    }
+
+    strlcpy(mixer_name_prefix, pinfo->fe_name, sizeof(mixer_name_prefix));
+
+    /*
+     * Enable/Disable channel mixer.
+     * If enable, use params and in_params to configure mixer.
+     * If disable, reset previously configured mixer.
+    */
+    snprintf(mixer_ctl_name, sizeof(mixer_ctl_name), "%s %s",
+             mixer_name_prefix, "Channel Mixer");
+
+    ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
+    if (!ctl) {
+        ALOGE("%s: ERROR. Could not get ctl for mixer cmd - %s",
+               __func__, mixer_ctl_name);
+        return -EINVAL;
+    }
+
+    if (enable)
+        err = mixer_ctl_set_enum_by_string(ctl, "Enable");
+    else
+        err = mixer_ctl_set_enum_by_string(ctl, "Disable");
+
+    if (err) {
+        ALOGE("%s: ERROR. %s channel mixer failed", __func__,
+              enable ? "Enable" : "Disable");
+        return -EINVAL;
+    }
+
+    /* Configure output channels of channel mixer */
+    snprintf(mixer_ctl_name, sizeof(mixer_ctl_name), "%s %s",
+             mixer_name_prefix, "Channels");
+
+    ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
+    if (!ctl) {
+        ALOGE("%s: ERROR. Could not get ctl for mixer cmd - %s",
+               __func__, mixer_ctl_name);
+        return -EINVAL;
+    }
+
+    mtrx_row_cnt = pinfo->op_channels;
+    mtrx_column_cnt = pinfo->ip_channels;
+
+    if (enable)
+        err = mixer_ctl_set_value(ctl, 0, mtrx_row_cnt);
+    else
+        err = mixer_ctl_set_value(ctl, 0, 0);
+
+    if (err) {
+        ALOGE("%s: ERROR. %s mixer output channels failed", __func__,
+              enable ? "Set" : "Reset");
+        return -EINVAL;
+    }
+
+
+    /* To keep output channel map in sync with asm driver channel mapping */
+    err = set_custom_mtmx_output_channel_map(adev, mixer_name_prefix, mtrx_row_cnt,
+                                       enable);
+    if (err) {
+        ALOGE("%s: ERROR. %s mtmx output channel map failed", __func__,
+              enable ? "Set" : "Reset");
+        return -EINVAL;
+    }
+
+    /* Send channel mixer rule */
+    snprintf(mixer_ctl_name, sizeof(mixer_ctl_name), "%s %s",
+             mixer_name_prefix, "Channel Rule");
+
+    ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
+    if (!ctl) {
+        ALOGE("%s: ERROR. Could not get ctl for mixer cmd - %s",
+               __func__, mixer_ctl_name);
+        return -EINVAL;
+    }
+
+    mixer_ctl_set_value(ctl, 0, rule);
+
+    /* Send channel coefficients for each output channel */
+    for (i = 0; i < mtrx_row_cnt; i++) {
+        snprintf(mixer_ctl_name, sizeof(mixer_ctl_name), "%s %s%d",
+                 mixer_name_prefix, "Output Channel", i+1);
+        ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
+        if (!ctl) {
+            ALOGE("%s: ERROR. Could not get ctl for mixer cmd - %s",
+                  __func__, mixer_ctl_name);
+            return -EINVAL;
+        }
+
+        if (enable)
+            err = mixer_ctl_set_array(ctl,
+                                  &params->coeffs[mtrx_column_cnt * i],
+                                  mtrx_column_cnt);
+        else
+            err = mixer_ctl_set_array(ctl,
+                                  reset_coeffs,
+                                  mtrx_column_cnt);
+        if (err) {
+            ALOGE("%s: ERROR. %s coefficients failed for output channel %d",
+                   __func__, enable ? "Set" : "Reset", i);
+            return -EINVAL;
+        }
+    }
+
+    /* Configure backend interfaces with information provided in xml */
+    i = 0;
+    while (in_params->in_ch_info[i].ch_count != 0) {
+        snprintf(mixer_ctl_name, sizeof(mixer_ctl_name), "%s %s%d",
+                 mixer_name_prefix, "Channel", i+1);
+        ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
+        if (!ctl) {
+            ALOGE("%s: ERROR. Could not get ctl for mixer cmd - %s",
+                  __func__, mixer_ctl_name);
+            return -EINVAL;
+        }
+        if (enable) {
+            ALOGD("%s: mixer %s, interface %s", __func__, mixer_ctl_name,
+                   in_params->in_ch_info[i].hw_interface);
+            err = mixer_ctl_set_enum_by_string(ctl,
+                      in_params->in_ch_info[i].hw_interface);
+        } else {
+            err = mixer_ctl_set_enum_by_string(ctl, "ZERO");
+        }
+
+        if (err) {
+            ALOGE("%s: ERROR. %s channel backend interface failed", __func__,
+                   enable ? "Set" : "Reset");
+            return -EINVAL;
+        }
+        i++;
+    }
+
+    return 0;
+}
+
+
+void audio_extn_set_custom_mtmx_params_v1(struct audio_device *adev,
+                                       struct audio_usecase *usecase,
+                                       bool enable)
+{
+    struct audio_custom_mtmx_params_info info = {0};
+    struct audio_custom_mtmx_params *params = NULL;
+    struct audio_custom_mtmx_in_params_info in_info = {0};
+    struct audio_custom_mtmx_in_params *in_params = NULL;
+    int pcm_device_id = -1, ret = 0;
+    uint32_t feature_id = 0;
+
+    switch(usecase->type) {
+    case PCM_CAPTURE:
+        if (usecase->stream.in) {
+            pcm_device_id =
+                platform_get_pcm_device_id(usecase->id, PCM_CAPTURE);
+            info.snd_device = usecase->in_snd_device;
+        } else {
+            ALOGE("%s: invalid input stream for capture usecase id:%d",
+                  __func__, usecase->id);
+            return;
+        }
+        break;
+    case PCM_PLAYBACK:
+    default:
+        ALOGV("%s: unsupported usecase id:%d", __func__, usecase->id);
+        return;
+    }
+
+    ALOGD("%s: snd device %d", __func__, info.snd_device);
+    info.id = feature_id;
+    info.usecase_id = usecase->id;
+    info.op_channels = audio_channel_count_from_in_mask(
+                                usecase->stream.in->channel_mask);
+
+    in_info.usecase_id = info.usecase_id;
+    in_info.op_channels = info.op_channels;
+    in_params = platform_get_custom_mtmx_in_params(adev->platform, &in_info);
+    if (!in_params) {
+        ALOGE("%s: Could not get in params for usecase %d, channels %d",
+               __func__, in_info.usecase_id, in_info.op_channels);
+        return;
+    }
+
+    info.ip_channels = in_params->ip_channels;
+    ALOGD("%s: ip channels %d, op channels %d", __func__, info.ip_channels, info.op_channels);
+
+    params = platform_get_custom_mtmx_params(adev->platform, &info);
+    if (params) {
+        ret = update_custom_mtmx_coefficients_v1(adev, params, in_params,
+                             pcm_device_id, usecase->type, enable);
+        if (ret < 0)
+            ALOGE("%s: error updating mtmx coeffs err:%d", __func__, ret);
+    }
+}
+
+snd_device_t audio_extn_get_loopback_snd_device(struct audio_device *adev,
+                                                struct audio_usecase *usecase,
+                                                int channel_count)
+{
+    snd_device_t snd_device = SND_DEVICE_NONE;
+    struct audio_custom_mtmx_in_params_info in_info = {0};
+    struct audio_custom_mtmx_in_params *in_params = NULL;
+
+    if (!adev || !usecase) {
+        ALOGE("%s: Invalid params", __func__);
+        return snd_device;
+    }
+
+    in_info.usecase_id = usecase->id;
+    in_info.op_channels = channel_count;
+    in_params = platform_get_custom_mtmx_in_params(adev->platform, &in_info);
+    if (!in_params) {
+        ALOGE("%s: Could not get in params for usecase %d, channels %d",
+               __func__, in_info.usecase_id, in_info.op_channels);
+        return snd_device;
+    }
+
+    switch(in_params->mic_ch) {
+    case 2:
+        snd_device = SND_DEVICE_IN_HANDSET_DMIC_AND_EC_REF_LOOPBACK;
+        break;
+    case 4:
+        snd_device = SND_DEVICE_IN_HANDSET_QMIC_AND_EC_REF_LOOPBACK;
+        break;
+    case 6:
+        snd_device = SND_DEVICE_IN_HANDSET_6MIC_AND_EC_REF_LOOPBACK;
+        break;
+    case 8:
+        snd_device = SND_DEVICE_IN_HANDSET_8MIC_AND_EC_REF_LOOPBACK;
+        break;
+    default:
+        ALOGE("%s: Unsupported mic channels %d",
+               __func__, in_params->mic_ch);
+        break;
+    }
+
+    ALOGD("%s: return snd device %d", __func__, snd_device);
+    return snd_device;
+}
+
 #ifndef DTS_EAGLE
 #define audio_extn_hpx_set_parameters(adev, parms)         (0)
 #define audio_extn_hpx_get_parameters(query, reply)  (0)
@@ -2893,10 +3279,10 @@
     *channel_mask_updated = false;
 
     int max_mic_count = platform_get_max_mic_count(adev->platform);
-    /* validate input params*/
+    /* validate input params. Avoid updated channel mask if loopback device */
     if ((channel_count == 6) &&
-        (in->format == AUDIO_FORMAT_PCM_16_BIT)) {
-
+        (in->format == AUDIO_FORMAT_PCM_16_BIT) &&
+        (!is_loopback_input_device(in->device))) {
         switch (max_mic_count) {
             case 4:
                 config->channel_mask = AUDIO_CHANNEL_INDEX_MASK_4;
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index fa9e4c2..b7942ae 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -1269,7 +1269,13 @@
 void audio_extn_set_cpu_affinity();
 bool audio_extn_is_record_play_concurrency_enabled();
 bool audio_extn_is_concurrent_capture_enabled();
-void audio_extn_set_custom_mtmx_params(struct audio_device *adev,
+void audio_extn_set_custom_mtmx_params_v2(struct audio_device *adev,
                                         struct audio_usecase *usecase,
                                         bool enable);
+void audio_extn_set_custom_mtmx_params_v1(struct audio_device *adev,
+                                        struct audio_usecase *usecase,
+                                        bool enable);
+snd_device_t audio_extn_get_loopback_snd_device(struct audio_device *adev,
+                                                struct audio_usecase *usecase,
+                                                int channel_count);
 #endif /* AUDIO_EXTN_H */
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 434ece8..a8f3ad8 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -1081,6 +1081,7 @@
     snd_device_t snd_device;
     char mixer_path[MIXER_PATH_MAX_LENGTH];
     struct stream_out *out = NULL;
+    struct stream_in *in = NULL;
     int ret = 0;
 
     if (usecase == NULL)
@@ -1139,7 +1140,16 @@
         if (out && out->compr)
             audio_extn_utils_compress_set_clk_rec_mode(usecase);
     }
-    audio_extn_set_custom_mtmx_params(adev, usecase, true);
+
+    if (usecase->type == PCM_CAPTURE) {
+        in = usecase->stream.in;
+        if (in && is_loopback_input_device(in->device)) {
+            ALOGD("%s: set custom mtmx params v1", __func__);
+            audio_extn_set_custom_mtmx_params_v1(adev, usecase, true);
+        }
+    } else {
+        audio_extn_set_custom_mtmx_params_v2(adev, usecase, true);
+    }
 
     // we shouldn't truncate mixer_path
     ALOGW_IF(strlcpy(mixer_path, use_case_table[usecase->id], sizeof(mixer_path))
@@ -1164,6 +1174,7 @@
 {
     snd_device_t snd_device;
     char mixer_path[MIXER_PATH_MAX_LENGTH];
+    struct stream_in *in = NULL;
 
     if (usecase == NULL || usecase->id == USECASE_INVALID)
         return -EINVAL;
@@ -1189,10 +1200,21 @@
     }
     audio_extn_sound_trigger_update_stream_status(usecase, ST_EVENT_STREAM_FREE);
     audio_extn_listen_update_stream_status(usecase, LISTEN_EVENT_STREAM_FREE);
-    audio_extn_set_custom_mtmx_params(adev, usecase, false);
+
+    if (usecase->type == PCM_CAPTURE) {
+        in = usecase->stream.in;
+        if (in && is_loopback_input_device(in->device)) {
+            ALOGD("%s: reset custom mtmx params v1", __func__);
+            audio_extn_set_custom_mtmx_params_v1(adev, usecase, false);
+        }
+    } else {
+        audio_extn_set_custom_mtmx_params_v2(adev, usecase, false);
+    }
+
     if ((usecase->type == PCM_PLAYBACK) &&
             (usecase->stream.out != NULL))
         usecase->stream.out->pspd_coeff_sent = false;
+
     ALOGV("%s: exit", __func__);
     return 0;
 }
@@ -2790,6 +2812,9 @@
     /* 2. Disable the tx device */
     disable_snd_device(adev, uc_info->in_snd_device);
 
+    if (is_loopback_input_device(in->device))
+        audio_extn_keep_alive_stop(KEEP_ALIVE_OUT_PRIMARY);
+
     list_remove(&uc_info->list);
     free(uc_info);
 
@@ -2977,6 +3002,9 @@
     audio_extn_audiozoom_set_microphone_direction(in, in->zoom);
     audio_extn_audiozoom_set_microphone_field_dimension(in, in->direction);
 
+    if (is_loopback_input_device(in->device))
+        audio_extn_keep_alive_start(KEEP_ALIVE_OUT_PRIMARY);
+
 done_open:
     audio_streaming_hint_end();
     audio_extn_perf_lock_release(&adev->perf_lock_handle);
@@ -3834,6 +3862,9 @@
     case 4:
     case 6:
     case 8:
+    case 10:
+    case 12:
+    case 14:
         break;
     default:
         ret = -EINVAL;
@@ -5397,7 +5428,7 @@
          */
         usecase = get_usecase_from_list(adev, out->usecase);
         if (usecase != NULL) {
-            audio_extn_set_custom_mtmx_params(adev, usecase, true);
+            audio_extn_set_custom_mtmx_params_v2(adev, usecase, true);
             out->pspd_coeff_sent = true;
         }
     }
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index 03ded5c..df29413 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -729,6 +729,14 @@
             __FILE__ ":" LITERAL_TO_STRING(__LINE__)\
             " ASSERT_FATAL(" #condition ") failed.")
 
+static inline bool is_loopback_input_device(audio_devices_t device) {
+    if (!audio_is_output_device(device) &&
+         ((device & AUDIO_DEVICE_IN_LOOPBACK) == AUDIO_DEVICE_IN_LOOPBACK))
+        return true;
+    else
+        return false;
+}
+
 /*
  * NOTE: when multiple mutexes have to be acquired, always take the
  * stream_in or stream_out mutex first, followed by the audio_device mutex.
diff --git a/hal/msm8916/platform.c b/hal/msm8916/platform.c
index 41c5941..e6989f6 100644
--- a/hal/msm8916/platform.c
+++ b/hal/msm8916/platform.c
@@ -2845,6 +2845,20 @@
     }
 }
 
+struct audio_custom_mtmx_in_params *platform_get_custom_mtmx_in_params(void *platform,
+                                   struct audio_custom_mtmx_in_params_info *info)
+{
+    ALOGW("%s: not implemented!", __func__);
+    return -ENOSYS;
+}
+
+int platform_add_custom_mtmx_in_params(void *platform,
+                                    struct audio_custom_mtmx_in_params_info *info)
+{
+    ALOGW("%s: not implemented!", __func__);
+    return -ENOSYS;
+}
+
 void platform_release_acdb_metainfo_key(void *platform)
 {
     struct platform_data *my_data = (struct platform_data *)platform;
diff --git a/hal/msm8916/platform.h b/hal/msm8916/platform.h
index e337870..3c94532 100644
--- a/hal/msm8916/platform.h
+++ b/hal/msm8916/platform.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
  * Not a Contribution.
  *
  * Copyright (C) 2013 The Android Open Source Project
@@ -246,6 +246,11 @@
     SND_DEVICE_IN_INCALL_REC_TX,
     SND_DEVICE_IN_INCALL_REC_RX_TX,
     SND_DEVICE_IN_LINE,
+    SND_DEVICE_IN_EC_REF_LOOPBACK,
+    SND_DEVICE_IN_HANDSET_DMIC_AND_EC_REF_LOOPBACK,
+    SND_DEVICE_IN_HANDSET_QMIC_AND_EC_REF_LOOPBACK,
+    SND_DEVICE_IN_HANDSET_6MIC_AND_EC_REF_LOOPBACK,
+    SND_DEVICE_IN_HANDSET_8MIC_AND_EC_REF_LOOPBACK,
     SND_DEVICE_IN_END,
 
     SND_DEVICE_MAX = SND_DEVICE_IN_END,
diff --git a/hal/msm8960/platform.c b/hal/msm8960/platform.c
index 8122827..137e700 100644
--- a/hal/msm8960/platform.c
+++ b/hal/msm8960/platform.c
@@ -374,6 +374,20 @@
     return -ENOSYS;
 }
 
+struct audio_custom_mtmx_in_params *platform_get_custom_mtmx_in_params(void *platform,
+                                   struct audio_custom_mtmx_in_params_info *info)
+{
+    ALOGW("%s: not implemented!", __func__);
+    return -ENOSYS;
+}
+
+int platform_add_custom_mtmx_in_params(void *platform,
+                                    struct audio_custom_mtmx_in_params_info *info)
+{
+    ALOGW("%s: not implemented!", __func__);
+    return -ENOSYS;
+}
+
 void platform_deinit(void *platform)
 {
     struct platform_data *my_data = (struct platform_data *)platform;
diff --git a/hal/msm8960/platform.h b/hal/msm8960/platform.h
index 727f906..2c66208 100644
--- a/hal/msm8960/platform.h
+++ b/hal/msm8960/platform.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013 - 2018 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013 - 2019 The Linux Foundation. All rights reserved.
  * Not a contribution.
  *
  * Copyright (C) 2013 The Android Open Source Project
@@ -104,6 +104,11 @@
     SND_DEVICE_IN_VOICE_REC_DMIC,
     SND_DEVICE_IN_VOICE_REC_DMIC_FLUENCE,
     SND_DEVICE_IN_USB_HEADSET_MIC,
+    SND_DEVICE_IN_EC_REF_LOOPBACK,
+    SND_DEVICE_IN_HANDSET_DMIC_AND_EC_REF_LOOPBACK,
+    SND_DEVICE_IN_HANDSET_QMIC_AND_EC_REF_LOOPBACK,
+    SND_DEVICE_IN_HANDSET_6MIC_AND_EC_REF_LOOPBACK,
+    SND_DEVICE_IN_HANDSET_8MIC_AND_EC_REF_LOOPBACK,
     SND_DEVICE_IN_END,
 
     SND_DEVICE_MAX = SND_DEVICE_IN_END,
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index 1a570f9..459c37c 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -350,6 +350,7 @@
     struct  spkr_device_chmap *spkr_ch_map;
     bool use_sprk_default_sample_rate;
     struct listnode custom_mtmx_params_list;
+    struct listnode custom_mtmx_in_params_list;
 };
 
 struct  spkr_device_chmap {
@@ -526,7 +527,9 @@
     [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_FULL_HEADSET] = "voice-tty-full-headset",
     [SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = "voice-tty-vco-headphones",
+    [SND_DEVICE_OUT_VOICE_TTY_VCO_HEADSET] = "voice-tty-vco-headset",
     [SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET] = "voice-tty-hco-handset",
     [SND_DEVICE_OUT_VOICE_TTY_FULL_USB] = "voice-tty-full-usb",
     [SND_DEVICE_OUT_VOICE_TTY_VCO_USB] = "voice-tty-vco-usb",
@@ -711,6 +714,11 @@
     [SND_DEVICE_OUT_VOIP_HEADPHONES] = "voip-headphones",
     [SND_DEVICE_IN_VOICE_HEARING_AID] = "hearing-aid-mic",
     [SND_DEVICE_IN_BUS] = "bus-mic",
+    [SND_DEVICE_IN_EC_REF_LOOPBACK] = "ec-ref-loopback",
+    [SND_DEVICE_IN_HANDSET_DMIC_AND_EC_REF_LOOPBACK] = "handset-dmic-and-ec-ref-loopback",
+    [SND_DEVICE_IN_HANDSET_QMIC_AND_EC_REF_LOOPBACK] = "handset-qmic-and-ec-ref-loopback",
+    [SND_DEVICE_IN_HANDSET_6MIC_AND_EC_REF_LOOPBACK] = "handset-6mic-and-ec-ref-loopback",
+    [SND_DEVICE_IN_HANDSET_8MIC_AND_EC_REF_LOOPBACK] = "handset-8mic-and-ec-ref-loopback",
 };
 
 // Platform specific backend bit width table
@@ -800,7 +808,9 @@
     [SND_DEVICE_OUT_SPEAKER_SAFE_AND_BT_A2DP] = 14,
     [SND_DEVICE_OUT_VOICE_HANDSET_TMUS] = 88,
     [SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES] = 17,
+    [SND_DEVICE_OUT_VOICE_TTY_FULL_HEADSET] = 17,
     [SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = 17,
+    [SND_DEVICE_OUT_VOICE_TTY_VCO_HEADSET] = 17,
     [SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET] = 37,
     [SND_DEVICE_OUT_VOICE_TTY_FULL_USB] = 17,
     [SND_DEVICE_OUT_VOICE_TTY_VCO_USB] = 17,
@@ -1022,7 +1032,9 @@
     {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)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TTY_FULL_HEADSET)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TTY_VCO_HEADSET)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_BT_SCO)},
     {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_BT_SCO_WB)},
@@ -1196,6 +1208,11 @@
     /* For legacy xml file parsing */
     {TO_NAME_INDEX(SND_DEVICE_IN_CAMCORDER_MIC)},
     {TO_NAME_INDEX(SND_DEVICE_IN_BUS)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_EC_REF_LOOPBACK)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_DMIC_AND_EC_REF_LOOPBACK)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_QMIC_AND_EC_REF_LOOPBACK)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_6MIC_AND_EC_REF_LOOPBACK)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_8MIC_AND_EC_REF_LOOPBACK)},
 };
 
 static char * backend_tag_table[SND_DEVICE_MAX] = {0};
@@ -2139,7 +2156,9 @@
         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_6_RX");
+    hw_interface_table[SND_DEVICE_OUT_VOICE_TTY_FULL_HEADSET] = strdup("SLIMBUS_6_RX");
     hw_interface_table[SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES] = strdup("SLIMBUS_6_RX");
+    hw_interface_table[SND_DEVICE_OUT_VOICE_TTY_VCO_HEADSET] = strdup("SLIMBUS_6_RX");
     hw_interface_table[SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET] = strdup("SLIMBUS_0_RX");
     hw_interface_table[SND_DEVICE_OUT_VOICE_TTY_FULL_USB] = strdup("USB_AUDIO_RX");
     hw_interface_table[SND_DEVICE_OUT_VOICE_TTY_VCO_USB] = strdup("USB_AUDIO_RX");
@@ -3133,6 +3152,7 @@
 
     list_init(&my_data->acdb_meta_key_list);
     list_init(&my_data->custom_mtmx_params_list);
+    list_init(&my_data->custom_mtmx_in_params_list);
 
     ret = audio_extn_is_hifi_audio_supported();
     if (ret || !my_data->is_internal_codec)
@@ -3727,6 +3747,66 @@
     }
 }
 
+struct audio_custom_mtmx_in_params *platform_get_custom_mtmx_in_params(void *platform,
+                                    struct audio_custom_mtmx_in_params_info *info)
+{
+    struct platform_data *my_data = (struct platform_data *)platform;
+    struct listnode *node = NULL;
+    struct audio_custom_mtmx_in_params *params = NULL;
+
+    list_for_each(node, &my_data->custom_mtmx_in_params_list) {
+        params = node_to_item(node, struct audio_custom_mtmx_in_params, list);
+        if (params &&
+            params->in_info.op_channels == info->op_channels &&
+            params->in_info.usecase_id == info->usecase_id) {
+            ALOGV("%s: found params with op_ch %d uc_id %d",
+                  __func__, info->op_channels, info->usecase_id);
+            return params;
+        }
+    }
+
+    ALOGI("%s: no matching param with op_ch %d uc_id %d",
+           __func__, info->op_channels, info->usecase_id);
+    return NULL;
+}
+
+int platform_add_custom_mtmx_in_params(void *platform,
+                                    struct audio_custom_mtmx_in_params_info *info)
+{
+    struct platform_data *my_data = (struct platform_data *)platform;
+    struct audio_custom_mtmx_in_params *params = NULL;
+    uint32_t size = sizeof(*params);
+
+    if (info->op_channels > AUDIO_CHANNEL_COUNT_MAX) {
+        ALOGE("%s: unusupported channels in %d", __func__, info->op_channels);
+        return -EINVAL;
+    }
+
+    params = (struct audio_custom_mtmx_in_params *)calloc(1, size);
+    if (!params) {
+        ALOGE("%s: failed to add custom mtmx in params", __func__);
+        return -ENOMEM;
+    }
+
+    ALOGI("%s: adding mtmx in params with op_ch %d uc_id %d",
+          __func__, info->op_channels, info->usecase_id);
+
+    params->in_info = *info;
+    list_add_tail(&my_data->custom_mtmx_in_params_list, &params->list);
+    return 0;
+}
+
+static void platform_release_custom_mtmx_in_params(void *platform)
+{
+    struct platform_data *my_data = (struct platform_data *)platform;
+    struct listnode *node = NULL, *tempnode = NULL;
+
+    list_for_each_safe(node, tempnode, &my_data->custom_mtmx_in_params_list) {
+        list_remove(node);
+        free(node_to_item(node, struct audio_custom_mtmx_in_params, list));
+    }
+}
+
 void platform_release_acdb_metainfo_key(void *platform)
 {
     struct platform_data *my_data = (struct platform_data *)platform;
@@ -3876,6 +3956,7 @@
     /* free acdb_meta_key_list */
     platform_release_acdb_metainfo_key(platform);
     platform_release_custom_mtmx_params(platform);
+    platform_release_custom_mtmx_in_params(platform);
 
     if (my_data->acdb_deallocate)
         my_data->acdb_deallocate();
@@ -5414,8 +5495,29 @@
         new_snd_devices[0] = SND_DEVICE_IN_INCALL_REC_RX;
         new_snd_devices[1] = SND_DEVICE_IN_INCALL_REC_TX;
         ret = 0;
+    } else if (SND_DEVICE_IN_HANDSET_DMIC_AND_EC_REF_LOOPBACK == snd_device) {
+        *num_devices = 2;
+        new_snd_devices[0] = SND_DEVICE_IN_HANDSET_DMIC;
+        new_snd_devices[1] = SND_DEVICE_IN_EC_REF_LOOPBACK;
+        ret = 0;
+    } else if (SND_DEVICE_IN_HANDSET_QMIC_AND_EC_REF_LOOPBACK == snd_device) {
+        *num_devices = 2;
+        new_snd_devices[0] = SND_DEVICE_IN_UNPROCESSED_QUAD_MIC;
+        new_snd_devices[1] = SND_DEVICE_IN_EC_REF_LOOPBACK;
+        ret = 0;
+    } else if (SND_DEVICE_IN_HANDSET_6MIC_AND_EC_REF_LOOPBACK == snd_device) {
+        *num_devices = 2;
+        new_snd_devices[0] = SND_DEVICE_IN_HANDSET_6MIC;
+        new_snd_devices[1] = SND_DEVICE_IN_EC_REF_LOOPBACK;
+        ret = 0;
+    } else if (SND_DEVICE_IN_HANDSET_8MIC_AND_EC_REF_LOOPBACK == snd_device) {
+        *num_devices = 2;
+        new_snd_devices[0] = SND_DEVICE_IN_HANDSET_8MIC;
+        new_snd_devices[1] = SND_DEVICE_IN_EC_REF_LOOPBACK;
+        ret = 0;
     }
 
+
     ALOGD("%s: snd_device(%d) num devices(%d) new_snd_devices(%d)", __func__,
         snd_device, *num_devices, *new_snd_devices);
 
@@ -5624,10 +5726,22 @@
                 !voice_extn_compress_voip_is_active(adev)) {
                 switch (adev->voice.tty_mode) {
                 case TTY_MODE_FULL:
-                    snd_device = SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES;
+                    if (audio_extn_is_concurrent_capture_enabled() &&
+                         (devices & AUDIO_DEVICE_OUT_WIRED_HEADSET)) {
+                        //Separate backend is added for headset-mic as part of concurrent capture
+                        snd_device = SND_DEVICE_OUT_VOICE_TTY_FULL_HEADSET;
+                    } else {
+                        snd_device = SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES;
+                    }
                     break;
                 case TTY_MODE_VCO:
-                    snd_device = SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES;
+                    if (audio_extn_is_concurrent_capture_enabled() &&
+                         (devices & AUDIO_DEVICE_OUT_WIRED_HEADSET)) {
+                        //Separate backend is added for headset-mic as part of concurrent capture
+                        snd_device = SND_DEVICE_OUT_VOICE_TTY_VCO_HEADSET;
+                    } else {
+                        snd_device = SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES;
+                    }
                     break;
                 case TTY_MODE_HCO:
                     snd_device = SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET;
@@ -6093,6 +6207,7 @@
     int channel_count = audio_channel_count_from_in_mask(channel_mask);
     int str_bitwidth = (in == NULL) ? CODEC_BACKEND_DEFAULT_BIT_WIDTH : in->bit_width;
     int sample_rate = (in == NULL) ? 8000 : in->sample_rate;
+    struct audio_usecase *usecase = NULL;
 
     ALOGV("%s: enter: out_device(%#x) in_device(%#x) channel_count (%d) channel_mask (0x%x)",
           __func__, out_device, in_device, channel_count, channel_mask);
@@ -6496,6 +6611,20 @@
                     platform_set_echo_reference(adev, true, out_device);
                 }
             }
+        } else if (in_device & AUDIO_DEVICE_IN_LOOPBACK) {
+            if (in_device & AUDIO_DEVICE_IN_BUILTIN_MIC) {
+                usecase = get_usecase_from_list(adev, USECASE_AUDIO_RECORD);
+                if (usecase == NULL) {
+                    ALOGE("%s: Could not find the record usecase", __func__);
+                    snd_device = SND_DEVICE_NONE;
+                    goto exit;
+                }
+
+                int ch_count = audio_channel_count_from_in_mask(channel_mask);
+                snd_device = audio_extn_get_loopback_snd_device(adev, usecase,
+                                  ch_count);
+                ALOGD("%s: snd device %d", __func__, snd_device);
+            }
         }
     } else if (source == AUDIO_SOURCE_FM_TUNER) {
         snd_device = SND_DEVICE_IN_CAPTURE_FM;
@@ -8767,6 +8896,14 @@
         backend_cfg.bit_width= usecase->stream.in->bit_width;
         backend_cfg.format= usecase->stream.in->format;
         backend_cfg.channels = audio_channel_count_from_in_mask(usecase->stream.in->channel_mask);
+        if (is_loopback_input_device(usecase->stream.in->device)) {
+            int bw = platform_get_snd_device_bit_width(snd_device);
+            if ((-ENOSYS != bw) && (backend_cfg.bit_width > (uint32_t)bw)) {
+                backend_cfg.bit_width = bw;
+                ALOGD("%s:txbecf: set bitwidth to %d from platform info",
+                       __func__, bw);
+            }
+        }
     } else {
         backend_cfg.bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
         backend_cfg.sample_rate =  CODEC_BACKEND_DEFAULT_SAMPLE_RATE;
diff --git a/hal/msm8974/platform.h b/hal/msm8974/platform.h
index b1e10f8..1d56a7e 100644
--- a/hal/msm8974/platform.h
+++ b/hal/msm8974/platform.h
@@ -129,7 +129,9 @@
     SND_DEVICE_OUT_SPEAKER_WSA_AND_BT_SCO_WB,
     SND_DEVICE_OUT_SPEAKER_WSA_AND_BT_SCO_SWB,
     SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES,
+    SND_DEVICE_OUT_VOICE_TTY_FULL_HEADSET,
     SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES,
+    SND_DEVICE_OUT_VOICE_TTY_VCO_HEADSET,
     SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET,
     SND_DEVICE_OUT_VOICE_TTY_FULL_USB,
     SND_DEVICE_OUT_VOICE_TTY_VCO_USB,
@@ -316,6 +318,11 @@
     SND_DEVICE_IN_CAMCORDER_SELFIE_PORTRAIT,
     SND_DEVICE_IN_VOICE_HEARING_AID,
     SND_DEVICE_IN_BUS,
+    SND_DEVICE_IN_EC_REF_LOOPBACK,
+    SND_DEVICE_IN_HANDSET_DMIC_AND_EC_REF_LOOPBACK,
+    SND_DEVICE_IN_HANDSET_QMIC_AND_EC_REF_LOOPBACK,
+    SND_DEVICE_IN_HANDSET_6MIC_AND_EC_REF_LOOPBACK,
+    SND_DEVICE_IN_HANDSET_8MIC_AND_EC_REF_LOOPBACK,
     SND_DEVICE_IN_END,
 
     SND_DEVICE_MAX = SND_DEVICE_IN_END,
diff --git a/hal/platform_api.h b/hal/platform_api.h
index 192022c..394310a 100644
--- a/hal/platform_api.h
+++ b/hal/platform_api.h
@@ -34,6 +34,7 @@
 #define LICENSE_STR_MAX_LEN  (64)
 #define PRODUCT_FFV      "ffv"
 #define PRODUCT_ALLPLAY  "allplay"
+#define MAX_IN_CHANNELS 32
 
 typedef enum {
     PLATFORM,
@@ -104,6 +105,7 @@
     uint32_t op_channels;
     uint32_t usecase_id;
     uint32_t snd_device;
+    char fe_name[128];
 };
 
 struct audio_custom_mtmx_params {
@@ -112,6 +114,26 @@
     uint32_t coeffs[0];
 };
 
+struct audio_custom_mtmx_in_params_info {
+    uint32_t op_channels;
+    uint32_t usecase_id;
+};
+
+struct audio_custom_mtmx_params_in_ch_info {
+    uint32_t ch_count;
+    char device[128];
+    char hw_interface[128];
+};
+
+struct audio_custom_mtmx_in_params {
+    struct listnode list;
+    struct audio_custom_mtmx_in_params_info in_info;
+    uint32_t ip_channels;
+    uint32_t mic_ch;
+    uint32_t ec_ref_ch;
+    struct audio_custom_mtmx_params_in_ch_info in_ch_info[MAX_IN_CHANNELS];
+};
+
 enum card_status_t;
 
 void *platform_init(struct audio_device *adev);
@@ -361,4 +383,8 @@
 /* callback functions from platform to common audio HAL */
 struct stream_in *adev_get_active_input(const struct audio_device *adev);
 
+struct audio_custom_mtmx_in_params * platform_get_custom_mtmx_in_params(void *platform,
+                                    struct audio_custom_mtmx_in_params_info *info);
+int platform_add_custom_mtmx_in_params(void *platform,
+                                    struct audio_custom_mtmx_in_params_info *info);
 #endif // AUDIO_PLATFORM_API_H
diff --git a/hal/platform_info.c b/hal/platform_info.c
index 05ee9cd..8ee8b07 100644
--- a/hal/platform_info.c
+++ b/hal/platform_info.c
@@ -73,6 +73,8 @@
     CUSTOM_MTMX_PARAMS,
     CUSTOM_MTMX_PARAM_COEFFS,
     EXTERNAL_DEVICE_SPECIFIC,
+    CUSTOM_MTMX_IN_PARAMS,
+    CUSTOM_MTMX_PARAM_IN_CH_INFO,
 } section_t;
 
 typedef void (* section_process_fn)(const XML_Char **attr);
@@ -97,6 +99,8 @@
 static void process_custom_mtmx_params(const XML_Char **attr);
 static void process_custom_mtmx_param_coeffs(const XML_Char **attr);
 static void process_external_dev(const XML_Char **attr);
+static void process_custom_mtmx_in_params(const XML_Char **attr);
+static void process_custom_mtmx_param_in_ch_info(const XML_Char **attr);
 
 static section_process_fn section_table[] = {
     [ROOT] = process_root,
@@ -118,6 +122,8 @@
     [CUSTOM_MTMX_PARAMS] = process_custom_mtmx_params,
     [CUSTOM_MTMX_PARAM_COEFFS] = process_custom_mtmx_param_coeffs,
     [EXTERNAL_DEVICE_SPECIFIC] = process_external_dev,
+    [CUSTOM_MTMX_IN_PARAMS] = process_custom_mtmx_in_params,
+    [CUSTOM_MTMX_PARAM_IN_CH_INFO] = process_custom_mtmx_param_in_ch_info,
 };
 
 static section_t section;
@@ -224,6 +230,7 @@
 }
 
 static struct audio_custom_mtmx_params_info mtmx_params_info;
+static struct audio_custom_mtmx_in_params_info mtmx_in_params_info;
 
 /*
  * <audio_platform_info>
@@ -1003,6 +1010,82 @@
     return;
 }
 
+static void process_custom_mtmx_param_in_ch_info(const XML_Char **attr)
+{
+    uint32_t attr_idx = 0;
+    int32_t in_ch_idx = -1;
+    struct audio_custom_mtmx_in_params *mtmx_in_params = NULL;
+
+    mtmx_in_params = platform_get_custom_mtmx_in_params((void *)my_data.platform,
+                                                  &mtmx_in_params_info);
+    if (mtmx_in_params == NULL) {
+        ALOGE("%s: mtmx in params with given param info, not found", __func__);
+        return;
+    }
+
+    if (strcmp(attr[attr_idx++], "in_channel_index") != 0) {
+        ALOGE("%s: 'in_channel_index' not found", __func__);
+        return;
+    }
+
+    in_ch_idx = atoi((char *)attr[attr_idx++]);
+    if (in_ch_idx < 0 || in_ch_idx >= MAX_IN_CHANNELS) {
+        ALOGE("%s: invalid input channel index(%d)", __func__, in_ch_idx);
+        return;
+    }
+
+    if (strcmp(attr[attr_idx++], "channel_count") != 0) {
+        ALOGE("%s: 'channel_count' not found", __func__);
+        return;
+    }
+    mtmx_in_params->in_ch_info[in_ch_idx].ch_count = atoi((char *)attr[attr_idx++]);
+
+    if (strcmp(attr[attr_idx++], "device") != 0) {
+        ALOGE("%s: 'device' not found", __func__);
+        return;
+    }
+    strlcpy(mtmx_in_params->in_ch_info[in_ch_idx].device, attr[attr_idx++],
+            sizeof(mtmx_in_params->in_ch_info[in_ch_idx].device));
+
+    if (strcmp(attr[attr_idx++], "interface") != 0) {
+        ALOGE("%s: 'interface' not found", __func__);
+        return;
+    }
+    strlcpy(mtmx_in_params->in_ch_info[in_ch_idx].hw_interface, attr[attr_idx++],
+            sizeof(mtmx_in_params->in_ch_info[in_ch_idx].hw_interface));
+
+    if (!strncmp(mtmx_in_params->in_ch_info[in_ch_idx].device,
+                 ENUM_TO_STRING(AUDIO_DEVICE_IN_BUILTIN_MIC),
+                 sizeof(mtmx_in_params->in_ch_info[in_ch_idx].device)))
+        mtmx_in_params->mic_ch = mtmx_in_params->in_ch_info[in_ch_idx].ch_count;
+    else if (!strncmp(mtmx_in_params->in_ch_info[in_ch_idx].device,
+              ENUM_TO_STRING(AUDIO_DEVICE_IN_LOOPBACK),
+              sizeof(mtmx_in_params->in_ch_info[in_ch_idx].device)))
+        mtmx_in_params->ec_ref_ch = mtmx_in_params->in_ch_info[in_ch_idx].ch_count;
+
+    mtmx_in_params->ip_channels += mtmx_in_params->in_ch_info[in_ch_idx].ch_count;
+}
+
+static void process_custom_mtmx_in_params(const XML_Char **attr)
+{
+    int attr_idx = 0;
+
+    if (strcmp(attr[attr_idx++], "usecase") != 0) {
+        ALOGE("%s: 'usecase' not found", __func__);
+        return;
+    }
+    mtmx_in_params_info.usecase_id = platform_get_usecase_index((char *)attr[attr_idx++]);
+
+    if (strcmp(attr[attr_idx++], "out_channel_count") != 0) {
+        ALOGE("%s: 'out_channel_count' not found", __func__);
+        return;
+    }
+    mtmx_in_params_info.op_channels = atoi((char *)attr[attr_idx++]);
+
+    platform_add_custom_mtmx_in_params((void *)my_data.platform, &mtmx_in_params_info);
+
+}
+
 static void process_custom_mtmx_param_coeffs(const XML_Char **attr)
 {
     uint32_t attr_idx = 0, out_ch_idx = -1, ch_coeff_count = 0;
@@ -1034,7 +1117,7 @@
     ch_coeff_value = strtok_r((char *)attr[attr_idx++], " ", &context);
     ip_channels = mtmx_params->info.ip_channels;
     op_channels = mtmx_params->info.op_channels;
-    while(ch_coeff_value && ch_coeff_count < op_channels) {
+    while(ch_coeff_value && ch_coeff_count < ip_channels) {
         mtmx_params->coeffs[ip_channels * out_ch_idx + ch_coeff_count++]
                            = atoi(ch_coeff_value);
         ch_coeff_value = strtok_r(NULL, " ", &context);
@@ -1077,6 +1160,15 @@
         return;
     }
     mtmx_params_info.snd_device = platform_get_snd_device_index((char *)attr[attr_idx++]);
+
+    if ((attr[attr_idx] != NULL) && (strcmp(attr[attr_idx++], "fe_name") == 0)) {
+        strlcpy(mtmx_params_info.fe_name, (char *)attr[attr_idx++],
+                sizeof(mtmx_params_info.fe_name));
+    } else {
+        ALOGD("%s: 'fe_name' not found", __func__);
+        mtmx_params_info.fe_name[0] = '\0';
+    }
+
     platform_add_custom_mtmx_params((void *)my_data.platform, &mtmx_params_info);
 
 }
@@ -1244,6 +1336,22 @@
         } else if (strcmp(tag_name, "ext_device") == 0) {
             section_process_fn fn = section_table[section];
             fn(attr);
+        } else if (strcmp(tag_name, "custom_mtmx_in_params") == 0) {
+            if (section != ROOT) {
+                ALOGE("custom_mtmx_in_params tag supported only in ROOT section");
+                return;
+            }
+            section = CUSTOM_MTMX_IN_PARAMS;
+            section_process_fn fn = section_table[section];
+            fn(attr);
+        } else if (strcmp(tag_name, "custom_mtmx_param_in_chs") == 0) {
+            if (section != CUSTOM_MTMX_IN_PARAMS) {
+                ALOGE("custom_mtmx_param_in_chs tag supported only with CUSTOM_MTMX_IN_PARAMS section");
+                return;
+            }
+            section = CUSTOM_MTMX_PARAM_IN_CH_INFO;
+            section_process_fn fn = section_table[section];
+            fn(attr);
         }
     } else {
         if(strcmp(tag_name, "config_params") == 0) {
@@ -1306,6 +1414,10 @@
         section = ROOT;
     } else if (strcmp(tag_name, "custom_mtmx_param_coeffs") == 0) {
         section = CUSTOM_MTMX_PARAMS;
+    } else if (strcmp(tag_name, "custom_mtmx_in_params") == 0) {
+        section = ROOT;
+    } else if (strcmp(tag_name, "custom_mtmx_param_in_chs") == 0) {
+        section = CUSTOM_MTMX_IN_PARAMS;
     }
 }
 
diff --git a/qahw_api/test/qahw_multi_record_test.c b/qahw_api/test/qahw_multi_record_test.c
index eccfe76..e033921 100644
--- a/qahw_api/test/qahw_multi_record_test.c
+++ b/qahw_api/test/qahw_multi_record_test.c
@@ -283,6 +283,15 @@
   case 8:
       params->config.channel_mask = AUDIO_CHANNEL_INDEX_MASK_8;
       break;
+  case 10:
+      params->config.channel_mask = AUDIO_CHANNEL_INDEX_MASK_10;
+      break;
+  case 12:
+      params->config.channel_mask = AUDIO_CHANNEL_INDEX_MASK_12;
+      break;
+  case 14:
+      params->config.channel_mask = AUDIO_CHANNEL_INDEX_MASK_14;
+      break;
   default:
       fprintf(log_file, "ERROR :::: channle count %d not supported, handle(%d)", params->channels, params->handle);
       if (log_file != stdout)