hal: Add support for multiple hardware variants

- Most of the targets supports multiple hardware variants such as
  MTP, CDP, Fluid, Liquid etc. The mixer paths for certain devices
  may vary on some of the variants. For example, the mixer controls
  for speaker device are different on msm8974 MTP and Fluid variants.
- Update audio HAL to select corresponding entry in the
  mixer_paths.xml file based on HW information.

Change-Id: I060f2e6c331344dd41417c19fb688ff27a129308

Conflicts:

	hal/Android.mk
diff --git a/hal/Android.mk b/hal/Android.mk
index 9e9be9e..6a8da10 100644
--- a/hal/Android.mk
+++ b/hal/Android.mk
@@ -7,9 +7,11 @@
 LOCAL_ARM_MODE := arm
 
 AUDIO_PLATFORM := $(TARGET_BOARD_PLATFORM)
+
 ifneq ($(filter msm8974 msm8226 msm8610 apq8084,$(TARGET_BOARD_PLATFORM)),)
   # B-family platform uses msm8974 code base
   AUDIO_PLATFORM = msm8974
+  MULTIPLE_HW_VARIANTS_ENABLED := true
 ifneq ($(filter msm8610,$(TARGET_BOARD_PLATFORM)),)
   LOCAL_CFLAGS := -DPLATFORM_MSM8610
 endif
@@ -61,6 +63,11 @@
 
 endif
 
+ifdef MULTIPLE_HW_VARIANTS_ENABLED
+  LOCAL_CFLAGS += -DHW_VARIANTS_ENABLED
+  LOCAL_SRC_FILES += $(AUDIO_PLATFORM)/hw_info.c
+endif
+
 LOCAL_SHARED_LIBRARIES := \
 	liblog \
 	libcutils \
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index ee03f5a..3676c08 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -81,4 +81,16 @@
                        void *buffer, size_t bytes);
 #endif
 
+#ifndef HW_VARIANTS_ENABLED
+#define hw_info_init(snd_card_name)                  (0)
+#define hw_info_deinit(hw_info)                      (0)
+#define hw_info_append_hw_type(hw_info,\
+        snd_device, device_name)                     (0)
+#else
+void *hw_info_init(const char *snd_card_name);
+void hw_info_deinit(void *hw_info);
+void hw_info_append_hw_type(void *hw_info, snd_device_t snd_device,
+                             char *device_name);
+#endif
+
 #endif /* AUDIO_EXTN_H */
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index aa7e6a6..28035d3 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -222,6 +222,8 @@
                              snd_device_t snd_device,
                              bool update_mixer)
 {
+    char device_name[DEVICE_NAME_MAX_SIZE] = {0};
+
     if (snd_device < SND_DEVICE_MIN ||
         snd_device >= SND_DEVICE_MAX) {
         ALOGE("%s: Invalid sound device %d", __func__, snd_device);
@@ -229,9 +231,14 @@
     }
 
     adev->snd_dev_ref_cnt[snd_device]++;
+
+    if(platform_get_snd_device_name_extn(adev->platform, snd_device, device_name) < 0 ) {
+        ALOGE("%s: Invalid sound device returned", __func__);
+        return -EINVAL;
+    }
     if (adev->snd_dev_ref_cnt[snd_device] > 1) {
         ALOGV("%s: snd_device(%d: %s) is already active",
-              __func__, snd_device, platform_get_snd_device_name(snd_device));
+              __func__, snd_device, device_name);
         return 0;
     }
 
@@ -250,8 +257,8 @@
     }
 
     ALOGV("%s: snd_device(%d: %s)", __func__,
-          snd_device, platform_get_snd_device_name(snd_device));
-    audio_route_apply_path(adev->audio_route, platform_get_snd_device_name(snd_device));
+          snd_device, device_name);
+    audio_route_apply_path(adev->audio_route, device_name);
     if (update_mixer)
         audio_route_update_mixer(adev->audio_route);
 
@@ -262,6 +269,8 @@
                        snd_device_t snd_device,
                        bool update_mixer)
 {
+    char device_name[DEVICE_NAME_MAX_SIZE] = {0};
+
     if (snd_device < SND_DEVICE_MIN ||
         snd_device >= SND_DEVICE_MAX) {
         ALOGE("%s: Invalid sound device %d", __func__, snd_device);
@@ -271,6 +280,7 @@
         ALOGE("%s: device ref cnt is already 0", __func__);
         return -EINVAL;
     }
+
     adev->snd_dev_ref_cnt[snd_device]--;
 
     /* exit usb play back thread */
@@ -282,13 +292,19 @@
     if(SND_DEVICE_IN_USB_HEADSET_MIC == snd_device)
         audio_extn_usb_stop_capture(adev);
 
+    if(platform_get_snd_device_name_extn(adev->platform, snd_device, device_name) < 0) {
+        ALOGE("%s: Invalid sound device returned", __func__);
+        return -EINVAL;
+    }
+
     if (adev->snd_dev_ref_cnt[snd_device] == 0) {
         ALOGV("%s: snd_device(%d: %s)", __func__,
-              snd_device, platform_get_snd_device_name(snd_device));
-        audio_route_reset_path(adev->audio_route, platform_get_snd_device_name(snd_device));
+              snd_device, device_name);
+        audio_route_reset_path(adev->audio_route, device_name);
         if (update_mixer)
             audio_route_update_mixer(adev->audio_route);
     }
+
     return 0;
 }
 
diff --git a/hal/msm8960/platform.c b/hal/msm8960/platform.c
index ac36f87..e80aa27 100644
--- a/hal/msm8960/platform.c
+++ b/hal/msm8960/platform.c
@@ -73,7 +73,6 @@
 typedef int (*csd_stop_voice_t)();
 
 
-/* Audio calibration related functions */
 struct platform_data {
     struct audio_device *adev;
     bool fluence_in_spkr_mode;
@@ -82,6 +81,9 @@
     int  fluence_type;
     int  dualmic_config;
 
+    void *hw_info;
+
+    /* Audio calibration related functions */
     void *acdb_handle;
     acdb_init_t acdb_init;
     acdb_deallocate_t acdb_deallocate;
@@ -243,6 +245,7 @@
     char baseband[PROPERTY_VALUE_MAX];
     char value[PROPERTY_VALUE_MAX];
     struct platform_data *my_data;
+    const char *snd_card_name;
 
     adev->mixer = mixer_open(MIXER_CARD);
 
@@ -259,6 +262,12 @@
 
     my_data = calloc(1, sizeof(struct platform_data));
 
+    snd_card_name = mixer_get_name(adev->mixer);
+    my_data->hw_info = hw_info_init(snd_card_name);
+    if (!my_data->hw_info) {
+        ALOGE("%s: Failed to init hardware info", __func__);
+    }
+
     my_data->adev = adev;
     my_data->fluence_in_spkr_mode = false;
     my_data->fluence_in_voice_call = false;
@@ -356,6 +365,9 @@
 
 void platform_deinit(void *platform)
 {
+    struct platform_data *my_data = (struct platform_data *)platform;
+
+    hw_info_deinit(my_data->hw_info);
     free(platform);
 }
 
@@ -367,6 +379,22 @@
         return "";
 }
 
+int platform_get_snd_device_name_extn(void *platform, snd_device_t snd_device,
+                                      char *device_name)
+{
+    struct platform_data *my_data = (struct platform_data *)platform;
+
+    if (snd_device >= SND_DEVICE_MIN && snd_device < SND_DEVICE_MAX) {
+        strlcpy(device_name, device_table[snd_device], DEVICE_NAME_MAX_SIZE);
+        hw_info_append_hw_type(my_data->hw_info, snd_device, device_name);
+    } else {
+        strlcpy(device_name, "", DEVICE_NAME_MAX_SIZE);
+        return -EINVAL;
+    }
+
+    return 0;
+}
+
 void platform_add_backend_name(char *mixer_path, snd_device_t snd_device)
 {
     if (snd_device == SND_DEVICE_IN_BT_SCO_MIC)
diff --git a/hal/msm8974/hw_info.c b/hal/msm8974/hw_info.c
new file mode 100644
index 0000000..02eb148
--- /dev/null
+++ b/hal/msm8974/hw_info.c
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2013, 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 "hardware_info"
+/*#define LOG_NDEBUG 0*/
+#define LOG_NDDEBUG 0
+
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <cutils/log.h>
+#include <cutils/str_parms.h>
+#include "audio_hw.h"
+#include "platform.h"
+#include "platform_api.h"
+
+
+struct hardware_info {
+    char name[HW_INFO_ARRAY_MAX_SIZE];
+    char type[HW_INFO_ARRAY_MAX_SIZE];
+    /* variables for handling target variants */
+    uint32_t num_snd_devices;
+    char dev_extn[HW_INFO_ARRAY_MAX_SIZE];
+    snd_device_t  *snd_devices;
+};
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+#define LITERAL_TO_STRING(x) #x
+#define CHECK(condition) LOG_ALWAYS_FATAL_IF(!(condition), "%s",\
+            __FILE__ ":" LITERAL_TO_STRING(__LINE__)\
+            " ASSERT_FATAL(" #condition ") failed.")
+
+static const snd_device_t taiko_fluid_variant_devices[] = {
+    SND_DEVICE_OUT_SPEAKER,
+    SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES,
+    SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET,
+};
+
+static const snd_device_t taiko_CDP_variant_devices[] = {
+    SND_DEVICE_OUT_SPEAKER,
+    SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES,
+    SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET,
+};
+
+static const snd_device_t taiko_liquid_variant_devices[] = {
+    SND_DEVICE_OUT_SPEAKER,
+    SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES,
+    SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET,
+    SND_DEVICE_IN_SPEAKER_MIC,
+    SND_DEVICE_IN_HEADSET_MIC,
+};
+
+static const snd_device_t taiko_DB_variant_devices[] = {
+    SND_DEVICE_OUT_SPEAKER,
+    SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES,
+    SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET,
+    SND_DEVICE_IN_SPEAKER_MIC,
+};
+
+static const snd_device_t tapan_lite_variant_devices[] = {
+    SND_DEVICE_OUT_SPEAKER,
+    SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES,
+    SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET,
+};
+
+static const snd_device_t tapan_skuf_variant_devices[] = {
+    SND_DEVICE_OUT_SPEAKER,
+    SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES,
+    SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET,
+};
+
+static const snd_device_t tapan_lite_skuf_variant_devices[] = {
+    SND_DEVICE_OUT_SPEAKER,
+    SND_DEVICE_OUT_ANC_HANDSET,
+    SND_DEVICE_OUT_ANC_HEADSET,
+    SND_DEVICE_OUT_ANC_FB_HEADSET,
+    SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES,
+    SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET,
+    SND_DEVICE_IN_AANC_HANDSET_MIC,
+};
+
+static const snd_device_t helicon_skuab_variant_devices[] = {
+    SND_DEVICE_OUT_SPEAKER,
+    SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES,
+    SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET,
+};
+
+static void  update_hardware_info_8084(struct hardware_info *hw_info, const char *snd_card_name)
+{
+    if (!strcmp(snd_card_name, "apq8084-taiko-mtp-snd-card")) {
+        strlcpy(hw_info->type, "mtp", sizeof(hw_info->type));
+        strlcpy(hw_info->name, "apq8084", sizeof(hw_info->name));
+        hw_info->snd_devices = NULL;
+        hw_info->num_snd_devices = 0;
+        strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn));
+    } else if (!strcmp(snd_card_name, "apq8084-taiko-cdp-snd-card")) {
+        strlcpy(hw_info->type, " cdp", sizeof(hw_info->type));
+        strlcpy(hw_info->name, "apq8084", sizeof(hw_info->name));
+        hw_info->snd_devices = (snd_device_t *)taiko_CDP_variant_devices;
+        hw_info->num_snd_devices = ARRAY_SIZE(taiko_CDP_variant_devices);
+        strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn));
+    } else if (!strcmp(snd_card_name, "apq8084-taiko-liquid-snd-card")) {
+        strlcpy(hw_info->type , " liquid", sizeof(hw_info->type));
+        strlcpy(hw_info->name, "apq8084", sizeof(hw_info->type));
+        hw_info->snd_devices = (snd_device_t *)taiko_liquid_variant_devices;
+        hw_info->num_snd_devices = ARRAY_SIZE(taiko_liquid_variant_devices);
+        strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn));
+    } else {
+        ALOGW("%s: Not an 8084 device", __func__);
+    }
+}
+
+static void  update_hardware_info_8974(struct hardware_info *hw_info, const char *snd_card_name)
+{
+    if (!strcmp(snd_card_name, "msm8974-taiko-mtp-snd-card")) {
+        strlcpy(hw_info->type, " mtp", sizeof(hw_info->type));
+        strlcpy(hw_info->name, "msm8974", sizeof(hw_info->name));
+        hw_info->snd_devices = NULL;
+        hw_info->num_snd_devices = 0;
+        strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn));
+    } else if (!strcmp(snd_card_name, "msm8974-taiko-cdp-snd-card")) {
+        strlcpy(hw_info->type, " cdp", sizeof(hw_info->type));
+        strlcpy(hw_info->name, "msm8974", sizeof(hw_info->name));
+        hw_info->snd_devices = (snd_device_t *)taiko_CDP_variant_devices;
+        hw_info->num_snd_devices = ARRAY_SIZE(taiko_CDP_variant_devices);
+        strlcpy(hw_info->dev_extn, "-fluid", sizeof(hw_info->dev_extn));
+    } else if (!strcmp(snd_card_name, "msm8974-taiko-fluid-snd-card")) {
+        strlcpy(hw_info->type, " fluid", sizeof(hw_info->type));
+        strlcpy(hw_info->name, "msm8974", sizeof(hw_info->name));
+        hw_info->snd_devices = (snd_device_t *) taiko_fluid_variant_devices;
+        hw_info->num_snd_devices = ARRAY_SIZE(taiko_fluid_variant_devices);
+        strlcpy(hw_info->dev_extn, "-fluid", sizeof(hw_info->dev_extn));
+    } else if (!strcmp(snd_card_name, "msm8974-taiko-liquid-snd-card")) {
+        strlcpy(hw_info->type, " liquid", sizeof(hw_info->type));
+        strlcpy(hw_info->name, "msm8974", sizeof(hw_info->name));
+        hw_info->snd_devices = (snd_device_t *)taiko_liquid_variant_devices;
+        hw_info->num_snd_devices = ARRAY_SIZE(taiko_liquid_variant_devices);
+        strlcpy(hw_info->dev_extn, "-liquid", sizeof(hw_info->dev_extn));
+    } else if (!strcmp(snd_card_name, "apq8074-taiko-db-snd-card")) {
+        strlcpy(hw_info->type, " dragon-board", sizeof(hw_info->type));
+        strlcpy(hw_info->name, "msm8974", sizeof(hw_info->name));
+        hw_info->snd_devices = (snd_device_t *)taiko_DB_variant_devices;
+        hw_info->num_snd_devices = ARRAY_SIZE(taiko_DB_variant_devices);
+        strlcpy(hw_info->dev_extn, "-DB", sizeof(hw_info->dev_extn));
+    } else {
+        ALOGW("%s: Not an 8974 device", __func__);
+    }
+}
+
+static void update_hardware_info_8610(struct hardware_info *hw_info, const char *snd_card_name)
+{
+    if (!strcmp(snd_card_name, "msm8x10-snd-card")) {
+        strlcpy(hw_info->type, "", sizeof(hw_info->type));
+        strlcpy(hw_info->name, "msm8x10", sizeof(hw_info->name));
+        hw_info->snd_devices = NULL;
+        hw_info->num_snd_devices = 0;
+        strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn));
+    } else if (!strcmp(snd_card_name, "msm8x10-skuab-snd-card")) {
+        strlcpy(hw_info->type, "skuab", sizeof(hw_info->type));
+        strlcpy(hw_info->name, "msm8x10", sizeof(hw_info->name));
+        hw_info->snd_devices = (snd_device_t *)helicon_skuab_variant_devices;
+        hw_info->num_snd_devices = ARRAY_SIZE(helicon_skuab_variant_devices);
+        strlcpy(hw_info->dev_extn, "-skuab", sizeof(hw_info->dev_extn));
+    } else if (!strcmp(snd_card_name, "msm8x10-skuaa-snd-card")) {
+        strlcpy(hw_info->type, " skuaa", sizeof(hw_info->type));
+        strlcpy(hw_info->name, "msm8x10", sizeof(hw_info->name));
+        hw_info->snd_devices = NULL;
+        hw_info->num_snd_devices = 0;
+        strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn));
+    } else {
+        ALOGW("%s: Not an  8x10 device", __func__);
+    }
+}
+
+static void update_hardware_info_8226(struct hardware_info *hw_info, const char *snd_card_name)
+{
+    if (!strcmp(snd_card_name, "msm8226-tapan-snd-card")) {
+        strlcpy(hw_info->type, "", sizeof(hw_info->type));
+        strlcpy(hw_info->name, "msm8226", sizeof(hw_info->name));
+        hw_info->snd_devices = NULL;
+        hw_info->num_snd_devices = 0;
+        strlcpy(hw_info->dev_extn, "", sizeof(hw_info->dev_extn));
+    } else if (!strcmp(snd_card_name, "msm8226-tapan9302-snd-card")) {
+        strlcpy(hw_info->type, "tapan_lite", sizeof(hw_info->type));
+        strlcpy(hw_info->name, "msm8226", sizeof(hw_info->name));
+        hw_info->snd_devices = (snd_device_t *)tapan_lite_variant_devices;
+        hw_info->num_snd_devices = ARRAY_SIZE(tapan_lite_variant_devices);
+        strlcpy(hw_info->dev_extn, "-lite", sizeof(hw_info->dev_extn));
+    } else if (!strcmp(snd_card_name, "msm8226-tapan-skuf-snd-card")) {
+        strlcpy(hw_info->type, " skuf", sizeof(hw_info->type));
+        strlcpy(hw_info->name, "msm8226", sizeof(hw_info->name));
+        hw_info->snd_devices = (snd_device_t *) tapan_skuf_variant_devices;
+        hw_info->num_snd_devices = ARRAY_SIZE(tapan_skuf_variant_devices);
+        strlcpy(hw_info->dev_extn, "-skuf", sizeof(hw_info->dev_extn));
+    } else if (!strcmp(snd_card_name, "msm8226-tapan9302-skuf-snd-card")) {
+        strlcpy(hw_info->type, " tapan9302-skuf", sizeof(hw_info->type));
+        strlcpy(hw_info->name, "msm8226", sizeof(hw_info->name));
+        hw_info->snd_devices = (snd_device_t *)tapan_lite_skuf_variant_devices;
+        hw_info->num_snd_devices = ARRAY_SIZE(tapan_lite_skuf_variant_devices);
+        strlcpy(hw_info->dev_extn, "-skuf-lite", sizeof(hw_info->dev_extn));
+    } else {
+        ALOGW("%s: Not an  8x26 device", __func__);
+    }
+}
+
+void *hw_info_init(const char *snd_card_name)
+{
+    struct hardware_info *hw_info;
+
+    hw_info = malloc(sizeof(struct hardware_info));
+
+    if(strstr(snd_card_name, "msm8974") ||
+              strstr(snd_card_name, "apq8074")) {
+        ALOGV("8974 - variant soundcard");
+        update_hardware_info_8974(hw_info, snd_card_name);
+    } else if(strstr(snd_card_name, "msm8226")) {
+        ALOGV("8x26 - variant soundcard");
+        update_hardware_info_8226(hw_info, snd_card_name);
+    } else if(strstr(snd_card_name, "msm8x10")) {
+        ALOGV("8x10 - variant soundcard");
+        update_hardware_info_8610(hw_info, snd_card_name);
+    } else if(strstr(snd_card_name, "apq8084")) {
+        ALOGV("8084 - variant soundcard");
+        update_hardware_info_8084(hw_info, snd_card_name);
+    } else {
+        ALOGE("%s: Unupported target %s:",__func__, snd_card_name);
+        CHECK(0);
+        free(hw_info);
+        hw_info = NULL;
+    }
+
+    return hw_info;
+}
+
+void hw_info_deinit(void *hw_info)
+{
+    struct hardware_info *my_data = (struct hardware_info*) hw_info;
+
+    if(!my_data)
+        free(my_data);
+}
+
+void hw_info_append_hw_type(void *hw_info, snd_device_t snd_device,
+                            char *device_name)
+{
+    struct hardware_info *my_data = (struct hardware_info*) hw_info;
+    uint32_t i = 0;
+
+    snd_device_t *snd_devices =
+            (snd_device_t *) my_data->snd_devices;
+
+    if(snd_devices != NULL) {
+        for (i = 0; i <  my_data->num_snd_devices; i++) {
+            if (snd_device == (snd_device_t)snd_devices[i]) {
+                ALOGV("extract dev_extn device %d, extn = %s",
+                        (snd_device_t)snd_devices[i],  my_data->dev_extn);
+                CHECK(strlcat(device_name,  my_data->dev_extn,
+                        DEVICE_NAME_MAX_SIZE) < DEVICE_NAME_MAX_SIZE);
+                break;
+            }
+        }
+    }
+    ALOGD("%s : device_name = %s", __func__,device_name);
+}
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index 1734ced..1888253 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -75,7 +75,6 @@
 typedef void (*acdb_send_audio_cal_t)(int, int);
 typedef void (*acdb_send_voice_cal_t)(int, int);
 
-/* Audio calibration related functions */
 struct platform_data {
     struct audio_device *adev;
     bool fluence_in_spkr_mode;
@@ -85,11 +84,14 @@
     int  btsco_sample_rate;
     bool slowtalk;
 
+    /* Audio calibration related functions */
     void *acdb_handle;
     acdb_init_t acdb_init;
     acdb_deallocate_t acdb_deallocate;
     acdb_send_audio_cal_t acdb_send_audio_cal;
     acdb_send_voice_cal_t acdb_send_voice_cal;
+
+    void *hw_info;
 };
 
 static const int pcm_device_table[AUDIO_USECASE_MAX][2] = {
@@ -204,9 +206,9 @@
     [SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET] = 14,
     [SND_DEVICE_OUT_TRANSMISSION_FM] = 0,
     [SND_DEVICE_OUT_ANC_HEADSET] = 26,
-    [SND_DEVICE_OUT_ANC_FB_HEADSET] = 26,
+    [SND_DEVICE_OUT_ANC_FB_HEADSET] = 27,
     [SND_DEVICE_OUT_VOICE_ANC_HEADSET] = 26,
-    [SND_DEVICE_OUT_VOICE_ANC_FB_HEADSET] = 26,
+    [SND_DEVICE_OUT_VOICE_ANC_FB_HEADSET] = 27,
     [SND_DEVICE_OUT_SPEAKER_AND_ANC_HEADSET] = 26,
     [SND_DEVICE_OUT_ANC_HANDSET] = 103,
 
@@ -302,7 +304,8 @@
 {
     char value[PROPERTY_VALUE_MAX];
     struct platform_data *my_data;
-    int retry_num = 0, ret;
+    int retry_num = 0;
+    const char *snd_card_name;
 
     adev->mixer = mixer_open(MIXER_CARD);
 
@@ -325,6 +328,12 @@
 
     my_data = calloc(1, sizeof(struct platform_data));
 
+    snd_card_name = mixer_get_name(adev->mixer);
+    my_data->hw_info = hw_info_init(snd_card_name);
+    if (!my_data->hw_info) {
+        ALOGE("%s: Failed to init hardware info", __func__);
+    }
+
     my_data->adev = adev;
     my_data->btsco_sample_rate = SAMPLE_RATE_8KHZ;
     my_data->fluence_in_spkr_mode = false;
@@ -391,6 +400,9 @@
 
 void platform_deinit(void *platform)
 {
+    struct platform_data *my_data = (struct platform_data *)platform;
+
+    hw_info_deinit(my_data->hw_info);
     free(platform);
     /* deinit usb */
     audio_extn_usb_deinit();
@@ -404,6 +416,22 @@
         return "";
 }
 
+int platform_get_snd_device_name_extn(void *platform, snd_device_t snd_device,
+                                      char *device_name)
+{
+    struct platform_data *my_data = (struct platform_data *)platform;
+
+    if (snd_device >= SND_DEVICE_MIN && snd_device < SND_DEVICE_MAX) {
+        strlcpy(device_name, device_table[snd_device], DEVICE_NAME_MAX_SIZE);
+        hw_info_append_hw_type(my_data->hw_info, snd_device, device_name);
+    } else {
+        strlcpy(device_name, "", DEVICE_NAME_MAX_SIZE);
+        return -EINVAL;
+    }
+
+    return 0;
+}
+
 void platform_add_backend_name(char *mixer_path, snd_device_t snd_device)
 {
     if (snd_device == SND_DEVICE_IN_BT_SCO_MIC)
diff --git a/hal/msm8974/platform.h b/hal/msm8974/platform.h
index 9212307..86dbe1b 100644
--- a/hal/msm8974/platform.h
+++ b/hal/msm8974/platform.h
@@ -169,4 +169,6 @@
 #define LOWLATENCY_PCM_DEVICE 15
 #endif
 
+#define DEVICE_NAME_MAX_SIZE 128
+#define HW_INFO_ARRAY_MAX_SIZE 32
 #endif // QCOM_AUDIO_PLATFORM_H
diff --git a/hal/platform_api.h b/hal/platform_api.h
index 04049f4..256bb76 100644
--- a/hal/platform_api.h
+++ b/hal/platform_api.h
@@ -23,6 +23,8 @@
 void *platform_init(struct audio_device *adev);
 void platform_deinit(void *platform);
 const char *platform_get_snd_device_name(snd_device_t snd_device);
+int platform_get_snd_device_name_extn(void *platform, snd_device_t snd_device,
+                                      char *device_name);
 void platform_add_backend_name(char *mixer_path, snd_device_t snd_device);
 int platform_get_pcm_device_id(audio_usecase_t usecase, int device_type);
 int platform_send_audio_calibration(void *platform, snd_device_t snd_device);
diff --git a/hal/voice.c b/hal/voice.c
old mode 100755
new mode 100644