hal: Add support for In-Car Communication usecase

* Add ICC library functionality in icc.c
* Add support in audio_extn to open and initialize icc library
* Add platform changes for device selection in ICC usecase
* Add support for sending ICC audio calibration/app type cfg

Suggested-by: Tahir Dawson <dawson@qti.qualcomm.com>
Change-Id: I26937da282fcdd31d59a54b180dca5d7740fbfb0
diff --git a/hal/audio_extn/Android.mk b/hal/audio_extn/Android.mk
index c32d1d4..eaf8926 100644
--- a/hal/audio_extn/Android.mk
+++ b/hal/audio_extn/Android.mk
@@ -605,6 +605,74 @@
 include $(BUILD_SHARED_LIBRARY)
 
 #-------------------------------------------
+#            Build ICC LIB
+#-------------------------------------------
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libicc
+LOCAL_VENDOR_MODULE := true
+
+PRIMARY_HAL_PATH := vendor/qcom/opensource/audio-hal/primary-hal/hal
+AUDIO_PLATFORM := $(TARGET_BOARD_PLATFORM)
+
+ifneq ($(filter sdm845 sdm710 sdmshrike msmnile kona lito bengal atoll sdm660 msm8937 msm8998 $(MSMSTEPPE) $(TRINKET),$(TARGET_BOARD_PLATFORM)),)
+  # B-family platform uses msm8974 code base
+  AUDIO_PLATFORM := msm8974
+  MULTIPLE_HW_VARIANTS_ENABLED := true
+endif
+
+ifeq ($(TARGET_BOARD_AUTO),true)
+  LOCAL_CFLAGS += -DPLATFORM_AUTO
+endif
+
+LOCAL_SRC_FILES:= \
+        icc.c \
+        device_utils.c
+
+LOCAL_CFLAGS += \
+    -Wall \
+    -Werror \
+    -Wno-unused-function \
+    -Wno-unused-variable
+
+LOCAL_SHARED_LIBRARIES := \
+    libaudioroute \
+    libaudioutils \
+    libcutils \
+    libdl \
+    libexpat \
+    liblog \
+    libtinyalsa \
+    libtinycompress
+
+LOCAL_C_INCLUDES := \
+    $(PRIMARY_HAL_PATH) \
+    $(PRIMARY_HAL_PATH)/$(AUDIO_PLATFORM) \
+    external/tinyalsa/include \
+    external/tinycompress/include \
+    external/expat/lib \
+    system/media/audio_utils/include \
+    $(call include-path-for, audio-route) \
+
+LOCAL_C_INCLUDES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include
+LOCAL_C_INCLUDES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include/audio
+LOCAL_C_INCLUDES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/techpack/audio/include
+LOCAL_ADDITIONAL_DEPENDENCIES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr
+
+ifeq ($(strip $(AUDIO_FEATURE_ENABLED_DLKM)),true)
+  LOCAL_HEADER_LIBRARIES += audio_kernel_headers
+  LOCAL_C_INCLUDES += $(TARGET_OUT_INTERMEDIATES)/vendor/qcom/opensource/audio-kernel/include
+  LOCAL_ADDITIONAL_DEPENDENCIES += $(BOARD_VENDOR_KERNEL_MODULES)
+endif
+
+LOCAL_HEADER_LIBRARIES += libhardware_headers
+LOCAL_HEADER_LIBRARIES += libsystem_headers
+ifneq ($(filter kona,$(TARGET_BOARD_PLATFORM)),)
+LOCAL_SANITIZE := integer_overflow
+endif
+include $(BUILD_SHARED_LIBRARY)
+
+#-------------------------------------------
 #            Build HDMI PASSTHROUGH
 #-------------------------------------------
 include $(CLEAR_VARS)
diff --git a/hal/audio_extn/audio_extn.c b/hal/audio_extn/audio_extn.c
index 39d491d..c3c80a0 100644
--- a/hal/audio_extn/audio_extn.c
+++ b/hal/audio_extn/audio_extn.c
@@ -4989,6 +4989,107 @@
 }
 // END: HFP ========================================================================
 
+// START: ICC ======================================================================
+#ifdef __LP64__
+#define ICC_LIB_PATH "/vendor/lib64/libicc.so"
+#else
+#define ICC_LIB_PATH "/vendor/lib/libicc.so"
+#endif
+
+static void *icc_lib_handle = NULL;
+
+typedef void (*icc_init_t)(icc_init_config_t);
+static icc_init_t icc_init;
+
+typedef bool (*icc_is_active_t)(struct audio_device *adev);
+static icc_is_active_t icc_is_active;
+
+typedef audio_usecase_t (*icc_get_usecase_t)();
+static icc_get_usecase_t icc_get_usecase;
+
+typedef void (*icc_set_parameters_t)(struct audio_device *adev,
+                                           struct str_parms *parms);
+static icc_set_parameters_t icc_set_parameters;
+
+int icc_feature_init(bool is_feature_enabled)
+{
+    ALOGD("%s: Called with feature %s", __func__,
+                  is_feature_enabled ? "Enabled" : "NOT Enabled");
+    if (is_feature_enabled) {
+        // dlopen lib
+        icc_lib_handle = dlopen(ICC_LIB_PATH, RTLD_NOW);
+
+        if (!icc_lib_handle) {
+            ALOGE("%s: dlopen failed", __func__);
+            goto feature_disabled;
+        }
+        if (!(icc_init = (icc_init_t)dlsym(
+                            icc_lib_handle, "icc_init")) ||
+            !(icc_is_active =
+                 (icc_is_active_t)dlsym(
+                            icc_lib_handle, "icc_is_active")) ||
+            !(icc_get_usecase =
+                 (icc_get_usecase_t)dlsym(
+                            icc_lib_handle, "icc_get_usecase")) ||
+            !(icc_set_parameters =
+                 (icc_set_parameters_t)dlsym(
+                            icc_lib_handle, "icc_set_parameters"))) {
+            ALOGE("%s: dlsym failed", __func__);
+            goto feature_disabled;
+        }
+        icc_init_config_t init_config;
+        init_config.fp_platform_get_pcm_device_id = platform_get_pcm_device_id;
+        init_config.fp_platform_set_echo_reference = platform_set_echo_reference;
+        init_config.fp_select_devices = select_devices;
+        init_config.fp_audio_extn_ext_hw_plugin_usecase_start =
+                                        audio_extn_ext_hw_plugin_usecase_start;
+        init_config.fp_audio_extn_ext_hw_plugin_usecase_stop =
+                                        audio_extn_ext_hw_plugin_usecase_stop;
+        init_config.fp_get_usecase_from_list = get_usecase_from_list;
+        init_config.fp_disable_audio_route = disable_audio_route;
+        init_config.fp_disable_snd_device = disable_snd_device;
+
+        icc_init(init_config);
+        ALOGD("%s:: ---- Feature ICC is Enabled ----", __func__);
+        return 0;
+    }
+
+feature_disabled:
+    if (icc_lib_handle) {
+        dlclose(icc_lib_handle);
+        icc_lib_handle = NULL;
+    }
+
+    icc_init = NULL;
+    icc_is_active = NULL;
+    icc_get_usecase = NULL;
+    icc_set_parameters = NULL;
+
+    ALOGW(":: %s: ---- Feature ICC is disabled ----", __func__);
+    return -ENOSYS;
+}
+
+bool audio_extn_icc_is_active(struct audio_device *adev)
+{
+    return ((icc_is_active) ?
+                    icc_is_active(adev): false);
+}
+
+audio_usecase_t audio_extn_icc_get_usecase()
+{
+    return ((icc_get_usecase) ?
+                    icc_get_usecase(): -1);
+}
+
+void audio_extn_icc_set_parameters(struct audio_device *adev,
+                                           struct str_parms *parms)
+{
+    ((icc_set_parameters) ?
+                    icc_set_parameters(adev, parms): NULL);
+}
+
+// END: ICC ========================================================================
+
 // START: EXT_HW_PLUGIN ===================================================================
 #ifdef __LP64__
 #define EXT_HW_PLUGIN_LIB_PATH "/vendor/lib64/libexthwplugin.so"
@@ -6313,6 +6414,9 @@
     hfp_feature_init(
         property_get_bool("vendor.audio.feature.hfp.enable",
                            false));
+    icc_feature_init(
+        property_get_bool("vendor.audio.feature.icc.enable",
+                           false));
     ext_hw_plugin_feature_init(
         property_get_bool("vendor.audio.feature.ext_hw_plugin.enable",
                            false));
@@ -6372,6 +6476,7 @@
    audio_extn_set_aptx_dec_bt_addr(adev, parms);
    audio_extn_ffv_set_parameters(adev, parms);
    audio_extn_ext_hw_plugin_set_parameters(adev->ext_hw_plugin, parms);
+   audio_extn_icc_set_parameters(adev, parms);
 }
 
 void audio_extn_get_parameters(const struct audio_device *adev,
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index f5bc244..8820d01 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -722,6 +722,25 @@
 
 // END: HFP FEATURE ==================================================
 
+// START: ICC FEATURE ====================================================
+bool audio_extn_icc_is_active(struct audio_device *adev);
+audio_usecase_t audio_extn_icc_get_usecase();
+void audio_extn_icc_set_parameters(struct audio_device *adev,
+                                          struct str_parms *parms);
+
+typedef struct icc_init_config {
+    fp_platform_get_pcm_device_id_t              fp_platform_get_pcm_device_id;
+    fp_platform_set_echo_reference_t             fp_platform_set_echo_reference;
+    fp_select_devices_t                          fp_select_devices;
+    fp_audio_extn_ext_hw_plugin_usecase_start_t  fp_audio_extn_ext_hw_plugin_usecase_start;
+    fp_audio_extn_ext_hw_plugin_usecase_stop_t   fp_audio_extn_ext_hw_plugin_usecase_stop;
+    fp_get_usecase_from_list_t                   fp_get_usecase_from_list;
+    fp_disable_audio_route_t                     fp_disable_audio_route;
+    fp_disable_snd_device_t                      fp_disable_snd_device;
+} icc_init_config_t;
+
+//END: ICC FEAUTRE =======================================================
+
 // START: EXT_HW_PLUGIN FEATURE ==================================================
 void* audio_extn_ext_hw_plugin_init(struct audio_device *adev);
 int audio_extn_ext_hw_plugin_deinit(void *plugin);
diff --git a/hal/audio_extn/auto_hal.c b/hal/audio_extn/auto_hal.c
index b0f7c0c..abddeb0 100644
--- a/hal/audio_extn/auto_hal.c
+++ b/hal/audio_extn/auto_hal.c
@@ -734,6 +734,9 @@
         case USECASE_VOICE_CALL:
             snd_device = SND_DEVICE_IN_VOICE_SPEAKER_MIC;
             break;
+        case USECASE_ICC_CALL:
+            snd_device = SND_DEVICE_IN_ICC;
+            break;
         default:
             ALOGE("%s: Usecase (%d) not supported", __func__, uc_id);
             return -EINVAL;
@@ -826,6 +829,9 @@
         case USECASE_AUDIO_PLAYBACK_REAR_SEAT:
             snd_device = SND_DEVICE_OUT_BUS_RSE;
             break;
+        case USECASE_ICC_CALL:
+            snd_device = SND_DEVICE_OUT_ICC;
+            break;
         default:
             ALOGE("%s: Usecase (%d) not supported", __func__, uc_id);
             return -EINVAL;
diff --git a/hal/audio_extn/ext_hw_plugin.c b/hal/audio_extn/ext_hw_plugin.c
index 92f3f30..5c78caa 100644
--- a/hal/audio_extn/ext_hw_plugin.c
+++ b/hal/audio_extn/ext_hw_plugin.c
@@ -199,6 +199,8 @@
     case USECASE_AUDIO_FM_TUNER_EXT:
        *plugin_usecase = AUDIO_HAL_PLUGIN_USECASE_FM_TUNER;
         break;
+    case USECASE_ICC_CALL:
+        *plugin_usecase = AUDIO_HAL_PLUGIN_USECASE_ICC;
     default:
         ret = -EINVAL;
     }
@@ -242,7 +244,7 @@
 
         if (((usecase->type == PCM_CAPTURE) || (usecase->type == VOICE_CALL) ||
               (usecase->type == VOIP_CALL) || (usecase->type == PCM_HFP_CALL) ||
-              (usecase->type == PCM_PASSTHROUGH)) &&
+              (usecase->type == ICC_CALL) || (usecase->type == PCM_PASSTHROUGH)) &&
             (usecase->in_snd_device != SND_DEVICE_NONE)) {
             codec_enable.snd_dev = usecase->in_snd_device;
             /* TODO - below should be related with in_snd_dev */
@@ -316,8 +318,8 @@
         }
 
         if (((usecase->type == PCM_PLAYBACK) || (usecase->type == VOICE_CALL) ||
-                (usecase->type == VOIP_CALL) || (usecase->type == PCM_HFP_CALL)) &&
-            (usecase->out_snd_device != SND_DEVICE_NONE)) {
+                (usecase->type == VOIP_CALL) || (usecase->type == PCM_HFP_CALL) ||
+                (usecase->type == ICC_CALL)) && (usecase->out_snd_device != SND_DEVICE_NONE)) {
             codec_enable.snd_dev = usecase->out_snd_device;
             /* TODO - below should be related with out_snd_dev */
             codec_enable.sample_rate = 48000;
@@ -384,8 +386,8 @@
         }
 
         if (((usecase->type == PCM_PLAYBACK) || (usecase->type == VOICE_CALL) ||
-                (usecase->type == VOIP_CALL) || (usecase->type == PCM_HFP_CALL)) &&
-            (usecase->out_snd_device != SND_DEVICE_NONE)) {
+                (usecase->type == VOIP_CALL) || (usecase->type == PCM_HFP_CALL) ||
+                (usecase->type == ICC_CALL)) && (usecase->out_snd_device != SND_DEVICE_NONE)) {
             codec_disable.snd_dev = usecase->out_snd_device;
 
             ALOGD("%s: disable audio hal plugin output, %d, %d",
@@ -401,7 +403,7 @@
         }
         if (((usecase->type == PCM_CAPTURE) || (usecase->type == VOICE_CALL) ||
              (usecase->type == VOIP_CALL) || (usecase->type == PCM_HFP_CALL) ||
-             (usecase->type == PCM_PASSTHROUGH)) &&
+             (usecase->type == PCM_PASSTHROUGH) || (usecase->type == ICC_CALL)) &&
             (usecase->in_snd_device != SND_DEVICE_NONE)) {
             codec_disable.snd_dev = usecase->in_snd_device;
 
diff --git a/hal/audio_extn/icc.c b/hal/audio_extn/icc.c
new file mode 100644
index 0000000..a38080f
--- /dev/null
+++ b/hal/audio_extn/icc.c
@@ -0,0 +1,353 @@
+/* icc.c
+Copyright (c) 2012-2015, 2016, The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+    * Neither the name of The Linux Foundation nor the names of its
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.*/
+
+#define LOG_TAG "audio_hw_icc"
+/*#define LOG_NDEBUG 0*/
+#define LOG_NDDEBUG 0
+
+#include <errno.h>
+#include <math.h>
+#include <cutils/log.h>
+
+#include "audio_hw.h"
+#include "platform.h"
+#include "platform_api.h"
+#include <stdlib.h>
+#include <cutils/str_parms.h>
+#include "audio_extn.h"
+
+#define AUDIO_PARAMETER_ICC_ENABLE      "conversation_mode_state"
+#define AUDIO_PARAMETER_ICC_SET_SAMPLING_RATE "icc_set_sampling_rate"
+#define AUDIO_PARAMETER_KEY_ICC_VOLUME "icc_volume"
+
+#ifdef PLATFORM_AUTO
+#define ICC_RX_VOLUME     "Playback 33 Volume"
+#else
+#define ICC_RX_VOLUME     "NULL"
+#endif
+
+static int32_t start_icc(struct audio_device *adev,
+                               struct str_parms *parms);
+
+static int32_t stop_icc(struct audio_device *adev);
+
+struct icc_module {
+    struct pcm *icc_pcm_rx;
+    struct pcm *icc_pcm_tx;
+    bool is_icc_running;
+    float icc_volume;
+    audio_usecase_t ucid;
+};
+
+static struct icc_module iccmod = {
+    .icc_pcm_rx = NULL,
+    .icc_pcm_tx = NULL,
+    .icc_volume = 0,
+    .is_icc_running = 0,
+    .ucid = USECASE_ICC_CALL,
+};
+static struct pcm_config pcm_config_icc = {
+    .channels = 4,
+    .rate = 16000,
+    .period_size = 240,
+    .period_count = 2,
+    .format = PCM_FORMAT_S16_LE,
+    .start_threshold = 0,
+    .stop_threshold = INT_MAX,
+    .avail_min = 0,
+};
+
+static fp_platform_get_pcm_device_id_t              fp_platform_get_pcm_device_id;
+static fp_platform_set_echo_reference_t             fp_platform_set_echo_reference;
+static fp_select_devices_t                          fp_select_devices;
+static fp_audio_extn_ext_hw_plugin_usecase_start_t  fp_audio_extn_ext_hw_plugin_usecase_start;
+static fp_audio_extn_ext_hw_plugin_usecase_stop_t   fp_audio_extn_ext_hw_plugin_usecase_stop;
+static fp_get_usecase_from_list_t                   fp_get_usecase_from_list;
+static fp_disable_audio_route_t                     fp_disable_audio_route;
+static fp_disable_snd_device_t                      fp_disable_snd_device;
+
+static int32_t icc_set_volume(struct audio_device *adev, float value)
+{
+    int32_t ret = 0, vol = 0;
+    struct mixer_ctl *ctl;
+    const char *mixer_ctl_name = ICC_RX_VOLUME;
+
+    ALOGD("%s: enter", __func__);
+    ALOGD("%s: (%f)", __func__, value);
+
+    iccmod.icc_volume = value;
+    if (value < 0.0) {
+        ALOGW("%s: (%f) Under 0.0, assuming 0.0", __func__, value);
+        value = 0.0;
+    } else {
+        value = ((value > 15.000000) ? 1.0 : (value / 15));
+        ALOGW("%s: Volume brought with in range (%f)", __func__, value);
+    }
+    vol = lrint((value * 0x2000) + 0.5);
+
+    if(!iccmod.is_icc_running) {
+        ALOGV("%s: ICC not active, ignoring icc_set_volume call", __func__);
+        return -EIO;
+    }
+
+    ALOGD("%s: Setting ICC Volume to %d", __func__, vol);
+    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;
+    }
+    if(mixer_ctl_set_value(ctl, 0, vol) < 0) {
+        ALOGE("%s: Couldn't set ICC Volume [%d]", __func__, vol);
+        return -EINVAL;
+    }
+
+    ALOGD("%s: exit: status(%d)", __func__, ret);
+    return ret;
+}
+
+static int32_t start_icc(struct audio_device *adev,
+                         struct str_parms *parms __unused)
+{
+    int32_t ret = 0;
+    struct audio_usecase *uc_info;
+    int32_t pcm_dev_rx_id, pcm_dev_tx_id;
+
+    ALOGD("%s: enter", __func__);
+
+    uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
+
+    if (!uc_info)
+        return -ENOMEM;
+
+    uc_info->id = iccmod.ucid;
+    uc_info->type = ICC_CALL;
+    uc_info->stream.out = adev->primary_output;
+    list_init(&uc_info->device_list);
+    assign_devices(&uc_info->device_list, &adev->primary_output->device_list);
+    uc_info->in_snd_device = SND_DEVICE_NONE;
+    uc_info->out_snd_device = SND_DEVICE_NONE;
+
+    list_add_tail(&adev->usecase_list, &uc_info->list);
+
+    fp_select_devices(adev, iccmod.ucid);
+
+    if ((uc_info->out_snd_device != SND_DEVICE_NONE) ||
+        (uc_info->in_snd_device != SND_DEVICE_NONE)) {
+        if (fp_audio_extn_ext_hw_plugin_usecase_start(adev->ext_hw_plugin, uc_info))
+            ALOGE("%s: failed to start ext hw plugin", __func__);
+    }
+
+    pcm_dev_rx_id = fp_platform_get_pcm_device_id(uc_info->id, PCM_PLAYBACK);
+    pcm_dev_tx_id = fp_platform_get_pcm_device_id(uc_info->id, PCM_CAPTURE);
+    if (pcm_dev_rx_id < 0 || pcm_dev_tx_id < 0 ) {
+        ALOGE("%s: Invalid PCM devices (rx: %d tx: %d) for the usecase(%d)",
+              __func__, pcm_dev_rx_id, pcm_dev_tx_id, uc_info->id);
+        ret = -EIO;
+        goto exit;
+    }
+
+    ALOGV("%s: ICC PCM devices (ICC pcm rx: %d pcm tx: %d) for the usecase(%d)",
+              __func__, pcm_dev_rx_id, pcm_dev_tx_id, uc_info->id);
+
+    iccmod.icc_pcm_rx = pcm_open(adev->snd_card,
+                                 pcm_dev_rx_id,
+                                 PCM_OUT, &pcm_config_icc);
+    if (iccmod.icc_pcm_rx && !pcm_is_ready(iccmod.icc_pcm_rx)) {
+        ALOGE("%s: %s", __func__, pcm_get_error(iccmod.icc_pcm_rx));
+        ret = -EIO;
+        goto exit;
+    }
+
+    iccmod.icc_pcm_tx = pcm_open(adev->snd_card,
+                                 pcm_dev_tx_id,
+                                 PCM_IN, &pcm_config_icc);
+    if (iccmod.icc_pcm_tx && !pcm_is_ready(iccmod.icc_pcm_tx)) {
+        ALOGE("%s: %s", __func__, pcm_get_error(iccmod.icc_pcm_tx));
+        ret = -EIO;
+        goto exit;
+    }
+
+    if (pcm_start(iccmod.icc_pcm_rx) < 0) {
+        ALOGE("%s: pcm start for icc pcm rx failed", __func__);
+        ret = -EINVAL;
+        goto exit;
+    }
+    if (pcm_start(iccmod.icc_pcm_tx) < 0) {
+        ALOGE("%s: pcm start for icc pcm tx failed", __func__);
+        ret = -EINVAL;
+        goto exit;
+    }
+
+    iccmod.is_icc_running = true;
+    icc_set_volume(adev, iccmod.icc_volume);
+
+    ALOGD("%s: exit: status(%d)", __func__, ret);
+    return 0;
+
+exit:
+    stop_icc(adev);
+    ALOGE("%s: Problem in ICC start: status(%d)", __func__, ret);
+    return ret;
+}
+
+static int32_t stop_icc(struct audio_device *adev)
+{
+    int32_t ret = 0;
+    struct audio_usecase *uc_info;
+
+    ALOGD("%s: enter", __func__);
+    iccmod.is_icc_running = false;
+
+    /* 1. Close the PCM devices */
+
+    if (iccmod.icc_pcm_rx) {
+        pcm_close(iccmod.icc_pcm_rx);
+        iccmod.icc_pcm_rx = NULL;
+    }
+    if (iccmod.icc_pcm_tx) {
+        pcm_close(iccmod.icc_pcm_tx);
+        iccmod.icc_pcm_tx = NULL;
+    }
+
+    uc_info = fp_get_usecase_from_list(adev, iccmod.ucid);
+    if (uc_info == NULL) {
+        ALOGE("%s: Could not find the usecase (%d) in the list",
+              __func__, iccmod.ucid);
+        return -EINVAL;
+    }
+
+    if ((uc_info->out_snd_device != SND_DEVICE_NONE) ||
+        (uc_info->in_snd_device != SND_DEVICE_NONE)) {
+        if (fp_audio_extn_ext_hw_plugin_usecase_stop(adev->ext_hw_plugin, uc_info))
+            ALOGE("%s: failed to stop ext hw plugin", __func__);
+    }
+
+    /* 2. Disable echo reference while stopping icc */
+    fp_platform_set_echo_reference(adev, false, &uc_info->device_list);
+
+    /* 3. Get and set stream specific mixer controls */
+    fp_disable_audio_route(adev, uc_info);
+
+    /* 4. Disable the rx and tx devices */
+    fp_disable_snd_device(adev, uc_info->out_snd_device);
+    fp_disable_snd_device(adev, uc_info->in_snd_device);
+
+    list_remove(&uc_info->list);
+    free(uc_info);
+
+    ALOGD("%s: exit: status(%d)", __func__, ret);
+    return ret;
+}
+
+void icc_init(icc_init_config_t init_config)
+{
+    fp_platform_get_pcm_device_id = init_config.fp_platform_get_pcm_device_id;
+    fp_platform_set_echo_reference = init_config.fp_platform_set_echo_reference;
+    fp_select_devices = init_config.fp_select_devices;
+    fp_audio_extn_ext_hw_plugin_usecase_start =
+                                init_config.fp_audio_extn_ext_hw_plugin_usecase_start;
+    fp_audio_extn_ext_hw_plugin_usecase_stop =
+                                init_config.fp_audio_extn_ext_hw_plugin_usecase_stop;
+    fp_get_usecase_from_list = init_config.fp_get_usecase_from_list;
+    fp_disable_audio_route = init_config.fp_disable_audio_route;
+    fp_disable_snd_device = init_config.fp_disable_snd_device;
+}
+
+bool icc_is_active(struct audio_device *adev)
+{
+    struct audio_usecase *icc_usecase = NULL;
+    icc_usecase = fp_get_usecase_from_list(adev, iccmod.ucid);
+
+    if (icc_usecase != NULL)
+        return true;
+    else
+        return false;
+}
+
+audio_usecase_t icc_get_usecase()
+{
+    return iccmod.ucid;
+}
+
+void icc_set_parameters(struct audio_device *adev, struct str_parms *parms)
+{
+    int ret;
+    int rate;
+    int val;
+    float vol;
+    char value[32]={0};
+
+    ret = str_parms_get_str(parms, AUDIO_PARAMETER_ICC_ENABLE, value,
+                            sizeof(value));
+    if (ret >= 0) {
+           if (!strncmp(value,"true",sizeof(value)) && !iccmod.is_icc_running)
+               ret = start_icc(adev,parms);
+           else if (!strncmp(value, "false", sizeof(value)) && iccmod.is_icc_running)
+               stop_icc(adev);
+           else
+               ALOGE("%s=%s is unsupported", AUDIO_PARAMETER_ICC_ENABLE, value);
+    }
+    memset(value, 0, sizeof(value));
+    ret = str_parms_get_str(parms,AUDIO_PARAMETER_ICC_SET_SAMPLING_RATE, value,
+                            sizeof(value));
+    if (ret >= 0) {
+           rate = atoi(value);
+           if (rate == 16000){
+               iccmod.ucid = USECASE_ICC_CALL;
+               pcm_config_icc.rate = rate;
+           } else
+               ALOGE("Unsupported rate..");
+    }
+
+    if (iccmod.is_icc_running) {
+        memset(value, 0, sizeof(value));
+        ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING,
+                                value, sizeof(value));
+        if (ret >= 0) {
+            val = atoi(value);
+            if (val > 0)
+                fp_select_devices(adev, iccmod.ucid);
+        }
+    }
+
+    memset(value, 0, sizeof(value));
+    ret = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_ICC_VOLUME,
+                            value, sizeof(value));
+    if (ret >= 0) {
+        if (sscanf(value, "%f", &vol) != 1){
+            ALOGE("%s: error in retrieving icc volume", __func__);
+            ret = -EIO;
+            goto exit;
+        }
+        ALOGD("%s: icc_set_volume usecase, Vol: [%f]", __func__, vol);
+        icc_set_volume(adev, vol);
+    }
+exit:
+    ALOGV("%s Exit",__func__);
+}
diff --git a/hal/audio_extn/utils.c b/hal/audio_extn/utils.c
index 66d7441..366fc85 100644
--- a/hal/audio_extn/utils.c
+++ b/hal/audio_extn/utils.c
@@ -981,6 +981,20 @@
         ALOGV("%s Selected apptype: playback %d capture %d",
             __func__, usecase->out_app_type_cfg.app_type, usecase->in_app_type_cfg.app_type);
         break;
+    case ICC_CALL:
+        /* ICC usecase: Loopback from TERT_TDM_TX to TERT_TDM_RX */
+        /* update out_app_type_cfg */
+        usecase->out_app_type_cfg.sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE;
+        usecase->out_app_type_cfg.bit_width = platform_get_snd_device_bit_width(usecase->out_snd_device);
+        usecase->out_app_type_cfg.app_type = platform_get_default_app_type_v2(adev->platform, PCM_PLAYBACK);
+        /* update in_app_type_cfg */
+        usecase->in_app_type_cfg.sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE;
+        usecase->in_app_type_cfg.bit_width = platform_get_snd_device_bit_width(usecase->in_snd_device);
+        usecase->in_app_type_cfg.app_type = platform_get_default_app_type_v2(adev->platform, PCM_CAPTURE);
+
+        ALOGV("%s Selected apptype: playback %d capture %d",
+            __func__, usecase->out_app_type_cfg.app_type, usecase->in_app_type_cfg.app_type);
+        break;
     default:
         ALOGE("%s: app type cfg not supported for usecase type (%d)",
             __func__, usecase->type);
@@ -1142,6 +1156,64 @@
     return rc;
 }
 
+int audio_extn_utils_send_app_type_cfg_icc(struct audio_device *adev,
+                                       struct audio_usecase *usecase)
+{
+    int pcm_device_id, acdb_dev_id = 0, snd_device = usecase->out_snd_device;
+    int32_t sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
+    int app_type = 0, rc = 0;
+
+    ALOGV("%s", __func__);
+
+    if (usecase->type != ICC_CALL) {
+        ALOGV("%s: not an ICC path, no need to cfg app type", __func__);
+        rc = 0;
+        goto exit_send_app_type_cfg;
+    }
+    if (usecase->id != USECASE_ICC_CALL) {
+        ALOGV("%s: a usecase where app type cfg is not required", __func__);
+        rc = 0;
+        goto exit_send_app_type_cfg;
+    }
+
+    snd_device = usecase->out_snd_device;
+    pcm_device_id = platform_get_pcm_device_id(usecase->id, PCM_PLAYBACK);
+    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__);
+        rc = -EINVAL;
+        goto exit_send_app_type_cfg;
+    }
+    /* config ICC session: playback path */
+    app_type = usecase->out_app_type_cfg.app_type;
+    sample_rate= usecase->out_app_type_cfg.sample_rate;
+
+    rc = set_stream_app_type_mixer_ctrl(adev, pcm_device_id, app_type,
+                                        acdb_dev_id, sample_rate,
+                                        PCM_PLAYBACK,
+                                        snd_device);
+    if (rc < 0)
+        goto exit_send_app_type_cfg;
+
+    /* config ICC session: capture path */
+    snd_device = usecase->in_snd_device;
+    pcm_device_id = platform_get_pcm_device_id(usecase->id, PCM_CAPTURE);
+    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__);
+        rc = -EINVAL;
+        goto exit_send_app_type_cfg;
+    }
+    app_type = usecase->in_app_type_cfg.app_type;
+    sample_rate= usecase->in_app_type_cfg.sample_rate;
+    rc = set_stream_app_type_mixer_ctrl(adev, pcm_device_id, app_type,
+                                        acdb_dev_id, sample_rate,
+                                        PCM_CAPTURE,
+                                        snd_device);
+exit_send_app_type_cfg:
+    return rc;
+}
+
 int audio_extn_utils_get_app_sample_rate_for_device(
                               struct audio_device *adev,
                               struct audio_usecase *usecase, int snd_device)
@@ -1487,6 +1559,8 @@
 
     if (usecase->type == PCM_HFP_CALL) {
         return audio_extn_utils_send_app_type_cfg_hfp(adev, usecase);
+    } else if (usecase->type == ICC_CALL) {
+        return audio_extn_utils_send_app_type_cfg_icc(adev, usecase);
     }
 
     switch (usecase->type) {
@@ -1888,7 +1962,8 @@
         platform_send_audio_calibration(adev->platform, usecase,
                          usecase->stream.in->app_type_cfg.app_type);
     } else if ((type == PCM_HFP_CALL) || (type == PCM_CAPTURE) ||
-               (type == TRANSCODE_LOOPBACK_RX && usecase->stream.inout != NULL)) {
+               (type == TRANSCODE_LOOPBACK_RX && usecase->stream.inout != NULL) ||
+               (type == ICC_CALL)) {
         platform_send_audio_calibration(adev->platform, usecase,
                          platform_get_default_app_type_v2(adev->platform, usecase->type));
     } else {
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 7ccef0a..3ad03f4 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -421,6 +421,7 @@
     [USECASE_AUDIO_PLAYBACK_FRONT_PASSENGER] = "front-passenger-playback",
     [USECASE_AUDIO_PLAYBACK_REAR_SEAT] = "rear-seat-playback",
     [USECASE_AUDIO_FM_TUNER_EXT] = "fm-tuner-ext",
+    [USECASE_ICC_CALL] = "icc-call",
 };
 
 static const audio_usecase_t offload_usecases[] = {
@@ -2687,7 +2688,8 @@
 
     if ((usecase->type == VOICE_CALL) ||
         (usecase->type == VOIP_CALL)  ||
-        (usecase->type == PCM_HFP_CALL)) {
+        (usecase->type == PCM_HFP_CALL)||
+        (usecase->type == ICC_CALL)) {
         if(usecase->stream.out == NULL) {
             ALOGE("%s: stream.out is NULL", __func__);
             return -EINVAL;
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index 3adba6e..da68656 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -249,6 +249,9 @@
     USECASE_AUDIO_FM_TUNER_EXT,
     /*voip usecase with low latency path*/
     USECASE_AUDIO_RECORD_VOIP_LOW_LATENCY,
+
+    /*In Car Communication Usecase*/
+    USECASE_ICC_CALL,
     AUDIO_USECASE_MAX
 };
 
@@ -546,6 +549,7 @@
     TRANSCODE_LOOPBACK_RX,
     TRANSCODE_LOOPBACK_TX,
     PCM_PASSTHROUGH,
+    ICC_CALL,
     USECASE_TYPE_MAX
 } usecase_type_t;
 
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index 9550f5a..4335991 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -522,6 +522,7 @@
     [USECASE_AUDIO_PLAYBACK_REAR_SEAT] = {REAR_SEAT_PCM_DEVICE,
                                           REAR_SEAT_PCM_DEVICE},
     [USECASE_AUDIO_FM_TUNER_EXT] = {-1, -1},
+    [USECASE_ICC_CALL] = {ICC_PCM_DEVICE, ICC_PCM_DEVICE},
 };
 
 /* Array to store sound devices */
@@ -631,6 +632,7 @@
     [SND_DEVICE_OUT_BUS_RSE] = "bus-speaker",
     [SND_DEVICE_OUT_CALL_PROXY] = "call-proxy",
     [SND_DEVICE_OUT_HAPTICS] = "haptics",
+    [SND_DEVICE_OUT_ICC] = "bus-speaker",
 
     /* Capture sound devices */
     [SND_DEVICE_IN_HANDSET_MIC] = "handset-mic",
@@ -780,6 +782,7 @@
     [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",
     [SND_DEVICE_IN_CALL_PROXY] = "call-proxy-in",
+    [SND_DEVICE_IN_ICC] = "speaker-mic",
 };
 
 // Platform specific backend bit width table
@@ -923,6 +926,7 @@
     [SND_DEVICE_OUT_BUS_RSE] = 60,
     [SND_DEVICE_OUT_CALL_PROXY] = 32,
     [SND_DEVICE_OUT_HAPTICS] = 200,
+    [SND_DEVICE_OUT_ICC] = 16,
     [SND_DEVICE_IN_HANDSET_MIC] = 4,
     [SND_DEVICE_IN_HANDSET_MIC_SB] = 163,
     [SND_DEVICE_IN_HANDSET_MIC_NN] = 183,
@@ -1062,6 +1066,7 @@
     [SND_DEVICE_IN_VOICE_HEARING_AID] = 44,
     [SND_DEVICE_IN_BUS] = 11,
     [SND_DEVICE_IN_CALL_PROXY] = 33,
+    [SND_DEVICE_IN_ICC] = 46,
 };
 
 struct name_to_index {
@@ -1317,6 +1322,9 @@
     {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_6MIC_AND_EC_REF_LOOPBACK)},
     {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_8MIC_AND_EC_REF_LOOPBACK)},
     {TO_NAME_INDEX(SND_DEVICE_IN_CALL_PROXY)},
+    /* ICC */
+    {TO_NAME_INDEX(SND_DEVICE_IN_ICC)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_ICC)},
 };
 
 static char * backend_tag_table[SND_DEVICE_MAX] = {0};
@@ -2601,6 +2609,8 @@
     hw_interface_table[SND_DEVICE_IN_VOICE_HEARING_AID] = strdup("SLIMBUS_0_TX");
     hw_interface_table[SND_DEVICE_IN_BUS] = strdup("TERT_TDM_TX_0");
     hw_interface_table[SND_DEVICE_IN_CALL_PROXY] = strdup("CALL_PROXY_TX");
+    hw_interface_table[SND_DEVICE_IN_ICC] = strdup("TERT_TDM_TX_0");
+    hw_interface_table[SND_DEVICE_OUT_ICC] = strdup("TERT_TDM_RX_0");
     my_data->max_mic_count = PLATFORM_DEFAULT_MIC_COUNT;
 
      /*remove ALAC & APE from DSP decoder list based on software decoder availability*/
@@ -5365,7 +5375,8 @@
         snd_device = usecase->out_snd_device;
     else if ((usecase->type == PCM_CAPTURE) && is_incall_rec_usecase)
         snd_device = voice_get_incall_rec_snd_device(usecase->in_snd_device);
-    else if ((usecase->type == PCM_HFP_CALL) || (usecase->type == PCM_CAPTURE))
+    else if ((usecase->type == PCM_HFP_CALL) || (usecase->type == PCM_CAPTURE)||
+            (usecase->type == ICC_CALL))
         snd_device = usecase->in_snd_device;
     else if (usecase->type == TRANSCODE_LOOPBACK_RX)
         snd_device = usecase->out_snd_device;
@@ -5389,7 +5400,8 @@
             new_snd_device[0] = snd_device;
         }
     }
-    if ((usecase->type == PCM_HFP_CALL) && is_bus_dev_usecase) {
+    if (((usecase->type == PCM_HFP_CALL) || (usecase->type == ICC_CALL)) &&
+          is_bus_dev_usecase) {
         num_devices = 2;
         new_snd_device[0] = usecase->in_snd_device;
         new_snd_device[1] = usecase->out_snd_device;
@@ -5413,7 +5425,8 @@
         if ((usecase->type == PCM_CAPTURE) && (app_type == DEFAULT_APP_TYPE_RX_PATH)) {
             ALOGD("Resetting app type for Tx path to default");
             app_type  = DEFAULT_APP_TYPE_TX_PATH;
-        } else if ((usecase->type == PCM_HFP_CALL) && is_bus_dev_usecase) {
+        } else if (((usecase->type == PCM_HFP_CALL) || (usecase->type == ICC_CALL)) &&
+                     is_bus_dev_usecase) {
             if (new_snd_device[i] >= SND_DEVICE_OUT_BEGIN &&
                 new_snd_device[i] < SND_DEVICE_OUT_END) {
                 app_type  = usecase->out_app_type_cfg.app_type;
diff --git a/hal/msm8974/platform.h b/hal/msm8974/platform.h
index 8fa4591..ca091ff 100644
--- a/hal/msm8974/platform.h
+++ b/hal/msm8974/platform.h
@@ -167,6 +167,7 @@
     SND_DEVICE_OUT_BUS_RSE,
     SND_DEVICE_OUT_CALL_PROXY,
     SND_DEVICE_OUT_HAPTICS,
+    SND_DEVICE_OUT_ICC,
     SND_DEVICE_OUT_END,
 
     /*
@@ -320,6 +321,7 @@
     SND_DEVICE_IN_HANDSET_6MIC_AND_EC_REF_LOOPBACK,
     SND_DEVICE_IN_HANDSET_8MIC_AND_EC_REF_LOOPBACK,
     SND_DEVICE_IN_CALL_PROXY,
+    SND_DEVICE_IN_ICC,
     SND_DEVICE_IN_END,
 
     SND_DEVICE_MAX = SND_DEVICE_IN_END,