hal: update combo device handling

If the wired headset/headphone/line devices are handled by
a different backend than speaker/earpiece devices, the combo
devices such as speaker+headphones can be split into individual
devices and enabled/disabled independently.

Conflicts:
	hal/msm8916/platform.c
	hal/msm8974/platform.c

Change-Id: I30e90f398ef204d106b5d8dac8fba166583bdbc8
diff --git a/hal/audio_extn/spkr_protection.c b/hal/audio_extn/spkr_protection.c
index 1a616df..e0b63ac 100644
--- a/hal/audio_extn/spkr_protection.c
+++ b/hal/audio_extn/spkr_protection.c
@@ -1337,7 +1337,8 @@
         property_get("ro.board.platform", platform, "");
         if (!strncmp("apq8084", platform, sizeof("apq8084"))) {
             platform_set_snd_device_backend(SND_DEVICE_OUT_VOICE_SPEAKER,
-                                            "speaker-protected");
+                                            "speaker-protected",
+                                            "SLIMBUS_0_RX");
         }
     }
 }
diff --git a/hal/audio_extn/utils.c b/hal/audio_extn/utils.c
index 8eb557e..d0c2d1a 100644
--- a/hal/audio_extn/utils.c
+++ b/hal/audio_extn/utils.c
@@ -615,8 +615,10 @@
          app_type_cfg[len++] = platform_get_default_app_type(adev->platform);
          app_type_cfg[len++] = acdb_dev_id;
          app_type_cfg[len++] = sample_rate;
-         ALOGI("%s PLAYBACK app_type %d, acdb_dev_id %d, sample_rate %d",
-               __func__, platform_get_default_app_type(adev->platform), acdb_dev_id, sample_rate);
+         ALOGI("%s:%d PLAYBACK app_type %d, acdb_dev_id %d, sample_rate %d",
+               __func__, __LINE__,
+               platform_get_default_app_type(adev->platform),
+               acdb_dev_id, sample_rate);
     } else if (usecase->type == PCM_PLAYBACK) {
 
          if (usecase->stream.out->devices & AUDIO_DEVICE_OUT_SPEAKER) {
@@ -646,8 +648,10 @@
          else
              app_type_cfg[len++] = sample_rate;
 
-         ALOGI("%s PLAYBACK app_type %d, acdb_dev_id %d, sample_rate %d",
-             __func__, usecase->stream.out->app_type_cfg.app_type, acdb_dev_id, sample_rate);
+         ALOGI("%s:%d PLAYBACK app_type %d, acdb_dev_id %d, sample_rate %d",
+               __func__, __LINE__,
+               platform_get_default_app_type(adev->platform),
+               acdb_dev_id, sample_rate);
 
     } else if (usecase->type == PCM_CAPTURE) {
          app_type_cfg[len++] = platform_get_default_app_type_v2(adev->platform, usecase->type);
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index d10bc18..5bd6651 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -483,6 +483,8 @@
 int enable_snd_device(struct audio_device *adev,
                       snd_device_t snd_device)
 {
+    int i, num_devices = 0;
+    snd_device_t new_snd_devices[SND_DEVICE_OUT_END];
     char device_name[DEVICE_NAME_MAX_SIZE] = {0};
 
     if (snd_device < SND_DEVICE_MIN ||
@@ -527,6 +529,11 @@
             audio_extn_dev_arbi_release(snd_device);
             return -EINVAL;
         }
+    } else if (platform_can_split_snd_device(adev->platform, snd_device,
+            &num_devices, new_snd_devices)) {
+        for (i = 0; i < num_devices; i++) {
+            enable_snd_device(adev, new_snd_devices[i]);
+        }
     } else {
         ALOGD("%s: snd_device(%d: %s)", __func__, snd_device, device_name);
         /* due to the possibility of calibration overwrite between listen
@@ -562,6 +569,8 @@
 int disable_snd_device(struct audio_device *adev,
                        snd_device_t snd_device)
 {
+    int i, num_devices = 0;
+    snd_device_t new_snd_devices[SND_DEVICE_OUT_END];
     char device_name[DEVICE_NAME_MAX_SIZE] = {0};
 
     if (snd_device < SND_DEVICE_MIN ||
@@ -595,6 +604,11 @@
         if (platform_can_enable_spkr_prot_on_device(snd_device) &&
              audio_extn_spkr_prot_is_enabled()) {
             audio_extn_spkr_prot_stop_processing(snd_device);
+        } else if (platform_can_split_snd_device(adev->platform, snd_device,
+                    &num_devices, new_snd_devices)) {
+            for (i = 0; i < num_devices; i++) {
+                disable_snd_device(adev, new_snd_devices[i]);
+            }
         } else {
             audio_route_reset_and_update_path(adev->audio_route, device_name);
         }
@@ -621,15 +635,13 @@
 }
 
 static void check_usecases_codec_backend(struct audio_device *adev,
-                                          struct audio_usecase *uc_info,
-                                          snd_device_t snd_device)
+                                              struct audio_usecase *uc_info,
+                                              snd_device_t snd_device)
 {
     struct listnode *node;
     struct audio_usecase *usecase;
     bool switch_device[AUDIO_USECASE_MAX];
     int i, num_uc_to_switch = 0;
-    int backend_idx = DEFAULT_CODEC_BACKEND;
-    int usecase_backend_idx = DEFAULT_CODEC_BACKEND;
 
     /*
      * This function is to make sure that all the usecases that are active on
@@ -653,7 +665,6 @@
 
     ALOGD("%s:becf: force routing %d", __func__, force_routing);
 
-    backend_idx = platform_get_backend_index(snd_device);
     /* Disable all the usecases on the shared backend other than the
      * specified usecase.
      */
@@ -663,26 +674,23 @@
     list_for_each(node, &adev->usecase_list) {
         usecase = node_to_item(node, struct audio_usecase, list);
 
-        if (usecase == uc_info)
-            continue;
-        usecase_backend_idx = platform_get_backend_index(usecase->out_snd_device);
-
-        ALOGD("%s:becf: (%d) check_usecases backend_idx: %d,"
-              "usecase_backend_idx: %d, curr device: %s, usecase device:%s",
-              __func__, i, backend_idx, usecase_backend_idx,
+        ALOGD("%s:becf: (%d) check_usecases curr device: %s, usecase device:%s "
+            "backends match %d",__func__, i,
               platform_get_snd_device_name(snd_device),
-              platform_get_snd_device_name(usecase->out_snd_device));
+              platform_get_snd_device_name(usecase->out_snd_device),
+              platform_check_backends_match(snd_device, usecase->out_snd_device));
 
         if (usecase->type != PCM_CAPTURE &&
-                (usecase->out_snd_device != snd_device || force_routing)  &&
-                usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND &&
-                usecase_backend_idx == backend_idx) {
-            ALOGD("%s:becf: check_usecases (%s) is active on (%s) - disabling ..",
-                __func__, use_case_table[usecase->id],
-                  platform_get_snd_device_name(usecase->out_snd_device));
-            disable_audio_route(adev, usecase);
-            switch_device[usecase->id] = true;
-            num_uc_to_switch++;
+            usecase != uc_info &&
+            (usecase->out_snd_device != snd_device || force_routing)  &&
+            usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND &&
+            platform_check_backends_match(snd_device, usecase->out_snd_device)) {
+                ALOGD("%s:becf: check_usecases (%s) is active on (%s) - disabling ..",
+                    __func__, use_case_table[usecase->id],
+                      platform_get_snd_device_name(usecase->out_snd_device));
+                disable_audio_route(adev, usecase);
+                switch_device[usecase->id] = true;
+                num_uc_to_switch++;
         }
     }
 
diff --git a/hal/msm8916/platform.c b/hal/msm8916/platform.c
index 17e496d..e66aad7 100644
--- a/hal/msm8916/platform.c
+++ b/hal/msm8916/platform.c
@@ -659,7 +659,8 @@
     {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_QMIC_AEC_NS)},
 };
 
-static char * backend_table[SND_DEVICE_MAX] = {0};
+static char * backend_tag_table[SND_DEVICE_MAX] = {0};
+static char * hw_interface_table[SND_DEVICE_MAX] = {0};
 
 static struct name_to_index usecase_name_index[AUDIO_USECASE_MAX] = {
     {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_DEEP_BUFFER)},
@@ -1139,33 +1140,37 @@
     const char *MEDIA_MIMETYPE_AUDIO_APE = "audio/x-ape";
 
     for (dev = 0; dev < SND_DEVICE_MAX; dev++) {
-        backend_table[dev] = NULL;
+        backend_tag_table[dev] = NULL;
+        hw_interface_table[dev] = NULL;
     }
     for (dev = 0; dev < SND_DEVICE_MAX; dev++) {
         backend_bit_width_table[dev] = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
     }
 
-    // TBD - do these go to the platform-info.xml file.
-    // will help in avoiding strdups here
-    backend_table[SND_DEVICE_IN_BT_SCO_MIC] = strdup("bt-sco");
-    backend_table[SND_DEVICE_IN_BT_SCO_MIC_WB] = strdup("bt-sco-wb");
-    backend_table[SND_DEVICE_IN_BT_SCO_MIC_NREC] = strdup("bt-sco");
-    backend_table[SND_DEVICE_IN_BT_SCO_MIC_WB_NREC] = strdup("bt-sco-wb");
-    backend_table[SND_DEVICE_OUT_BT_SCO] = strdup("bt-sco");
-    backend_table[SND_DEVICE_OUT_BT_SCO_WB] = strdup("bt-sco-wb");
-    backend_table[SND_DEVICE_OUT_HDMI] = strdup("hdmi");
-    backend_table[SND_DEVICE_OUT_SPEAKER_AND_HDMI] = strdup("speaker-and-hdmi");
-    backend_table[SND_DEVICE_OUT_VOICE_TX] = strdup("afe-proxy");
-    backend_table[SND_DEVICE_IN_VOICE_RX] = strdup("afe-proxy");
-    backend_table[SND_DEVICE_OUT_AFE_PROXY] = strdup("afe-proxy");
-    backend_table[SND_DEVICE_OUT_USB_HEADSET] = strdup("usb-headphones");
-    backend_table[SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET] =
+    // To overwrite these go to the audio_platform_info.xml file.
+    backend_tag_table[SND_DEVICE_IN_BT_SCO_MIC] = strdup("bt-sco");
+    backend_tag_table[SND_DEVICE_IN_BT_SCO_MIC_WB] = strdup("bt-sco-wb");
+    backend_tag_table[SND_DEVICE_IN_BT_SCO_MIC_NREC] = strdup("bt-sco");
+    backend_tag_table[SND_DEVICE_IN_BT_SCO_MIC_WB_NREC] = strdup("bt-sco-wb");
+    backend_tag_table[SND_DEVICE_OUT_BT_SCO] = strdup("bt-sco");
+    backend_tag_table[SND_DEVICE_OUT_BT_SCO_WB] = strdup("bt-sco-wb");
+    backend_tag_table[SND_DEVICE_OUT_HDMI] = strdup("hdmi");
+    backend_tag_table[SND_DEVICE_OUT_SPEAKER_AND_HDMI] = strdup("speaker-and-hdmi");
+    backend_tag_table[SND_DEVICE_OUT_VOICE_TX] = strdup("afe-proxy");
+    backend_tag_table[SND_DEVICE_IN_VOICE_RX] = strdup("afe-proxy");
+    backend_tag_table[SND_DEVICE_OUT_AFE_PROXY] = strdup("afe-proxy");
+    backend_tag_table[SND_DEVICE_OUT_USB_HEADSET] = strdup("usb-headphones");
+    backend_tag_table[SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET] =
         strdup("speaker-and-usb-headphones");
-    backend_table[SND_DEVICE_IN_USB_HEADSET_MIC] = strdup("usb-headset-mic");
-    backend_table[SND_DEVICE_IN_CAPTURE_FM] = strdup("capture-fm");
-    backend_table[SND_DEVICE_OUT_TRANSMISSION_FM] = strdup("transmission-fm");
-    backend_table[SND_DEVICE_OUT_HEADPHONES_44_1] = strdup("headphones-44.1");
-    backend_table[SND_DEVICE_OUT_VOICE_SPEAKER_VBAT] = strdup("vbat-voice-speaker");
+    backend_tag_table[SND_DEVICE_IN_USB_HEADSET_MIC] = strdup("usb-headset-mic");
+    backend_tag_table[SND_DEVICE_IN_CAPTURE_FM] = strdup("capture-fm");
+    backend_tag_table[SND_DEVICE_OUT_TRANSMISSION_FM] = strdup("transmission-fm");
+    backend_tag_table[SND_DEVICE_OUT_HEADPHONES_44_1] = strdup("headphones-44.1");
+    backend_tag_table[SND_DEVICE_OUT_VOICE_SPEAKER_VBAT] = strdup("vbat-voice-speaker");
+
+    hw_interface_table[SND_DEVICE_OUT_HDMI] = strdup("HDMI_RX");
+    hw_interface_table[SND_DEVICE_OUT_SPEAKER_AND_HDMI] = strdup("SLIMBUS_0_RX-and-HDMI_RX");
+    hw_interface_table[SND_DEVICE_OUT_VOICE_TX] = strdup("AFE_PCM_RX");
 
     /*remove ALAC & APE from DSP decoder list based on software decoder availability*/
     for (count = 0; count < (int32_t) (sizeof(dsp_only_decoders_mime)/sizeof(dsp_only_decoders_mime[0]));
@@ -1792,6 +1797,11 @@
             strdup("SLIM_5_RX Format");
         my_data->current_backend_cfg[HEADPHONE_44_1_BACKEND].samplerate_mixer_ctl =
             strdup("SLIM_5_RX SampleRate");
+
+        my_data->current_backend_cfg[HEADPHONE_BACKEND].bitwidth_mixer_ctl =
+            strdup("SLIM_6_RX Format");
+        my_data->current_backend_cfg[HEADPHONE_BACKEND].samplerate_mixer_ctl =
+            strdup("SLIM_6_RX SampleRate");
     } else {
         my_data->current_backend_cfg[DEFAULT_CODEC_BACKEND].bitwidth_mixer_ctl =
             strdup("MI2S_RX Format");
@@ -1842,9 +1852,9 @@
 
     int32_t dev;
     for (dev = 0; dev < SND_DEVICE_MAX; dev++) {
-        if (backend_table[dev]) {
-            free(backend_table[dev]);
-            backend_table[dev]= NULL;
+        if (backend_tag_table[dev]) {
+            free(backend_tag_table[dev]);
+            backend_tag_table[dev]= NULL;
         }
     }
 
@@ -1921,7 +1931,7 @@
         return;
     }
 
-    const char * suffix = backend_table[snd_device];
+    const char * suffix = backend_tag_table[snd_device];
 
     if (suffix != NULL) {
         strlcat(mixer_path, " ", MIXER_PATH_MAX_LENGTH);
@@ -1929,6 +1939,40 @@
     }
 }
 
+bool platform_check_backends_match(snd_device_t snd_device1, snd_device_t snd_device2)
+{
+    bool result = true;
+
+    ALOGV("%s: snd_device1 = %s, snd_device2 = %s", __func__,
+                platform_get_snd_device_name(snd_device1),
+                platform_get_snd_device_name(snd_device2));
+
+    if ((snd_device1 < SND_DEVICE_MIN) || (snd_device1 >= SND_DEVICE_OUT_END)) {
+        ALOGE("%s: Invalid snd_device = %s", __func__,
+                platform_get_snd_device_name(snd_device1));
+        return false;
+    }
+    if ((snd_device2 < SND_DEVICE_MIN) || (snd_device2 >= SND_DEVICE_OUT_END)) {
+        ALOGE("%s: Invalid snd_device = %s", __func__,
+                platform_get_snd_device_name(snd_device2));
+        return false;
+    }
+    const char * be_itf1 = hw_interface_table[snd_device1];
+    const char * be_itf2 = hw_interface_table[snd_device2];
+
+    if (NULL != be_itf1 && NULL != be_itf2) {
+        if (0 != strcmp(be_itf1, be_itf2))
+            result = false;
+    } else if (NULL == be_itf1 && NULL != be_itf2) {
+            result = false;
+    } else if (NULL != be_itf1 && NULL == be_itf2) {
+            result = false;
+    }
+
+    ALOGV("%s: be_itf1 = %s, be_itf2 = %s, match %d", __func__, be_itf1, be_itf2, result);
+    return result;
+}
+
 int platform_get_pcm_device_id(audio_usecase_t usecase, int device_type)
 {
     int device_id = -1;
@@ -2060,6 +2104,8 @@
         goto done;
     }
 
+    ALOGV("%s: acdb_device_table[%s]: old = %d new = %d", __func__,
+          platform_get_snd_device_name(snd_device), acdb_device_table[snd_device], acdb_id);
     acdb_device_table[snd_device] = acdb_id;
 done:
     return ret;
@@ -2122,7 +2168,7 @@
     if (NATIVE_AUDIO_MODE_SRC == na_mode || NATIVE_AUDIO_MODE_TRUE_44_1 == na_mode) {
         na_props.platform_na_prop_enabled = na_props.ui_na_prop_enabled = true;
         na_props.na_mode = na_mode;
-        ALOGD("%s:napb: native audio playback enabled in (%s) mode", __func__,
+        ALOGD("%s:napb: native audio playback enabled in (%s) mode v2.0", __func__,
               ((na_mode == NATIVE_AUDIO_MODE_SRC)?"SRC mode":"True 44.1 mode"));
     } else {
         na_props.platform_na_prop_enabled = false;
@@ -2253,21 +2299,27 @@
 }
 
 
-int platform_get_backend_index(snd_device_t snd_device)
+static int platform_get_backend_index(snd_device_t snd_device)
 {
     int32_t port = DEFAULT_CODEC_BACKEND;
 
     if (snd_device >= SND_DEVICE_MIN && snd_device < SND_DEVICE_MAX) {
-        if (backend_table[snd_device] != NULL &&
-            !strcmp(backend_table[snd_device], "headphones-44.1"))
-            port = HEADPHONE_44_1_BACKEND;
-        else
-            port = DEFAULT_CODEC_BACKEND;
+        if (backend_tag_table[snd_device] != NULL) {
+                if (strncmp(backend_tag_table[snd_device], "headphones-44.1",
+                            sizeof("headphones-44.1")) == 0)
+                        port = HEADPHONE_44_1_BACKEND;
+                else if (strncmp(backend_tag_table[snd_device], "headphones",
+                            sizeof("headphones")) == 0)
+                        port = HEADPHONE_BACKEND;
+                else if (strcmp(backend_tag_table[snd_device], "hdmi") == 0)
+                        port = HDMI_RX_BACKEND;
+        }
     } else {
         ALOGV("%s:napb: Invalid device - %d ", __func__, snd_device);
     }
 
-    ALOGV("%s:napb: backend port - %d", __func__, port);
+    ALOGV("%s:napb: backend port - %d device - %d ", __func__, port,
+        snd_device);
     return port;
 }
 
@@ -2277,6 +2329,8 @@
     struct platform_data *my_data = (struct platform_data *)platform;
     int acdb_dev_id, acdb_dev_type;
     int snd_device = SND_DEVICE_OUT_SPEAKER;
+    int new_snd_device[SND_DEVICE_OUT_END];
+    int i, num_devices = 1;
 
     if (usecase->type == PCM_PLAYBACK)
         snd_device = usecase->out_snd_device;
@@ -2287,26 +2341,34 @@
         snd_device = usecase->in_snd_device;
     acdb_dev_id = acdb_device_table[platform_get_spkr_prot_snd_device(snd_device)];
 
-    // Do not use Rx path default app type for TX path
-    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;
+    if(!platform_can_split_snd_device(platform, snd_device, &num_devices, new_snd_device)) {
+        new_snd_device[0] = snd_device;
     }
-    if (acdb_dev_id < 0) {
-        ALOGE("%s: Could not find acdb id for device(%d)",
-              __func__, snd_device);
-        return -EINVAL;
-    }
-    if (my_data->acdb_send_audio_cal) {
-        ALOGV("%s: sending audio calibration for snd_device(%d) acdb_id(%d)",
-              __func__, snd_device, acdb_dev_id);
-        if (snd_device >= SND_DEVICE_OUT_BEGIN &&
-                snd_device < SND_DEVICE_OUT_END)
-            acdb_dev_type = ACDB_DEV_TYPE_OUT;
-        else
-            acdb_dev_type = ACDB_DEV_TYPE_IN;
-        my_data->acdb_send_audio_cal(acdb_dev_id, acdb_dev_type, app_type,
-                                     sample_rate);
+
+    for (i = 0; i < num_devices; i++) {
+        acdb_dev_id = acdb_device_table[platform_get_spkr_prot_snd_device(new_snd_device[i])];
+
+        // Do not use Rx path default app type for TX path
+        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;
+        }
+        if (acdb_dev_id < 0) {
+            ALOGE("%s: Could not find acdb id for device(%d)",
+                  __func__, new_snd_device[i]);
+            return -EINVAL;
+        }
+        if (my_data->acdb_send_audio_cal) {
+            ALOGV("%s: sending audio calibration for snd_device(%d) acdb_id(%d)",
+                  __func__, new_snd_device[i], acdb_dev_id);
+            if (new_snd_device[i] >= SND_DEVICE_OUT_BEGIN &&
+                    new_snd_device[i] < SND_DEVICE_OUT_END)
+                acdb_dev_type = ACDB_DEV_TYPE_OUT;
+            else
+                acdb_dev_type = ACDB_DEV_TYPE_IN;
+            my_data->acdb_send_audio_cal(acdb_dev_id, acdb_dev_type, app_type,
+                                         sample_rate);
+        }
     }
     return 0;
 }
@@ -2568,6 +2630,57 @@
     return ret;
 }
 
+bool platform_can_split_snd_device(void *platform,
+                                   snd_device_t snd_device,
+                                   int *num_devices,
+                                   snd_device_t *new_snd_devices)
+{
+    bool status = false;
+    struct platform_data *my_data = (struct platform_data *)platform;
+
+    if (NULL == num_devices || NULL == new_snd_devices) {
+        ALOGE("%s: NULL pointer ..", __func__);
+        return false;
+    }
+
+    /*
+     * If wired headset/headphones/line devices share the same backend
+     * with speaker/earpiece this routine returns false.
+     */
+    if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES &&
+        !platform_check_backends_match(SND_DEVICE_OUT_SPEAKER, SND_DEVICE_OUT_HEADPHONES)) {
+        *num_devices = 2;
+
+         if (my_data->is_vbat_speaker)
+             new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER_VBAT;
+         else if (my_data->is_wsa_speaker)
+             new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER_WSA;
+         else
+             new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
+
+        new_snd_devices[1] = SND_DEVICE_OUT_HEADPHONES;
+        status = true;
+    } else if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_HDMI &&
+               !platform_check_backends_match(SND_DEVICE_OUT_SPEAKER, SND_DEVICE_OUT_HDMI)) {
+        *num_devices = 2;
+
+        if (my_data->is_vbat_speaker)
+            new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER_VBAT;
+        else if (my_data->is_wsa_speaker)
+            new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER_WSA;
+        else
+            new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
+
+        new_snd_devices[1] = SND_DEVICE_OUT_HDMI;
+        status = true;
+    }
+
+    ALOGD("%s: snd_device(%d) num devices(%d) new_snd_devices(%d)", __func__,
+        snd_device, *num_devices, *new_snd_devices);
+
+    return status;
+}
+
 snd_device_t platform_get_output_snd_device(void *platform, struct stream_out *out)
 {
     struct platform_data *my_data = (struct platform_data *)platform;
@@ -3720,7 +3833,7 @@
 /*
  * configures afe with bit width and Sample Rate
  */
-int platform_set_codec_backend_cfg(struct audio_device* adev,
+static int platform_set_codec_backend_cfg(struct audio_device* adev,
                          snd_device_t snd_device,
                          unsigned int bit_width, unsigned int sample_rate)
 {
@@ -3824,7 +3937,7 @@
  * goes through all the current usecases and picks the highest
  * bitwidth & samplerate
  */
-bool platform_check_codec_backend_cfg(struct audio_device* adev,
+static bool platform_check_codec_backend_cfg(struct audio_device* adev,
                                    struct audio_usecase* usecase,
                                    snd_device_t snd_device,
                                    unsigned int* new_bit_width,
@@ -3837,7 +3950,6 @@
     unsigned int bit_width;
     unsigned int sample_rate;
     int backend_idx = DEFAULT_CODEC_BACKEND;
-    int usecase_backend_idx = DEFAULT_CODEC_BACKEND;
     struct platform_data *my_data = (struct platform_data *)adev->platform;
     int na_mode = platform_get_native_support();
 
@@ -3847,8 +3959,7 @@
     sample_rate = *new_sample_rate;
 
     ALOGI("%s:becf: afe: Codec selected backend: %d current bit width: %d and sample rate: %d",
-          __func__,
-        backend_idx, bit_width, sample_rate);
+          __func__, backend_idx, bit_width, sample_rate);
 
     // For voice calls use default configuration i.e. 16b/48K, only applicable to
     // default backend
@@ -3875,8 +3986,6 @@
             uc = node_to_item(node, struct audio_usecase, list);
             struct stream_out *out = (struct stream_out*) uc->stream.out;
             if (uc->type == PCM_PLAYBACK && out && usecase != uc) {
-                usecase_backend_idx =
-                    platform_get_backend_index(uc->out_snd_device);
 
                 ALOGD("%s:napb: (%d) - (%s)id (%d) sr %d bw "
                       "(%d) device %s", __func__, i++, use_case_table[uc->id],
@@ -3884,7 +3993,7 @@
                       out->bit_width,
                       platform_get_snd_device_name(uc->out_snd_device));
 
-                if (usecase_backend_idx == backend_idx) {
+                if (platform_check_backends_match(snd_device, uc->out_snd_device)) {
                         if (bit_width < out->bit_width)
                             bit_width = out->bit_width;
                         if (sample_rate < out->sample_rate)
@@ -3924,10 +4033,12 @@
      * hifi playback not supported on spkr devices, limit the Sample Rate
      * to 48 khz.
      */
-    if (usecase->stream.out->devices & AUDIO_DEVICE_OUT_SPEAKER) {
+    if (SND_DEVICE_OUT_SPEAKER == snd_device ||
+        SND_DEVICE_OUT_SPEAKER_WSA == snd_device ||
+        SND_DEVICE_OUT_SPEAKER_VBAT == snd_device) {
         sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE;
-        ALOGD("%s:becf: afe: playback on speakers Configure afe to \
-            default Sample Rate(48k)", __func__);
+        ALOGD("%s:becf: afe: playback on speaker device Configure afe to "
+            "default Sample Rate(48k)", __func__);
     }
 
     /*
@@ -3944,8 +4055,8 @@
      * reset the sample rate to default value(48K), if hifi audio is not supported
      */
     if (!my_data->hifi_audio) {
-               ALOGD("%s:becf: afe: only 48KHZ sample rate is supported \
-                      Configure afe to default Sample Rate(48k)", __func__);
+               ALOGD("%s:becf: afe: only 48KHZ sample rate is supported "
+                      "Configure afe to default Sample Rate(48k)", __func__);
                sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE;
     }
 
@@ -3988,6 +4099,9 @@
     unsigned int new_bit_width;
     unsigned int new_sample_rate;
     int backend_idx = DEFAULT_CODEC_BACKEND;
+    int new_snd_devices[SND_DEVICE_OUT_END];
+    int i, num_devices = 1;
+    bool ret = false;
 
     backend_idx = platform_get_backend_index(snd_device);
 
@@ -3998,17 +4112,27 @@
           ", backend_idx %d usecase = %d device (%s)", __func__, new_bit_width,
           new_sample_rate, backend_idx, usecase->id,
           platform_get_snd_device_name(snd_device));
-    if (platform_check_codec_backend_cfg(adev, usecase, snd_device,
-                                      &new_bit_width, &new_sample_rate)) {
-        platform_set_codec_backend_cfg(adev, snd_device,
-                                       new_bit_width, new_sample_rate);
-        return true;
+
+    if (!platform_can_split_snd_device(adev->platform, snd_device,
+            &num_devices, new_snd_devices))
+        new_snd_devices[0] = snd_device;
+
+    for (i = 0; i < num_devices; i++) {
+        ALOGI("%s: becf: new_snd_devices[%d] is %s", __func__, i,
+            platform_get_snd_device_name(new_snd_devices[i]));
+        if (platform_check_codec_backend_cfg(adev, usecase, new_snd_devices[i],
+                                             &new_bit_width, &new_sample_rate)) {
+                platform_set_codec_backend_cfg(adev, new_snd_devices[i],
+                                               new_bit_width, new_sample_rate);
+                ret = true;
+        }
     }
 
-    return false;
+    return ret;
 }
 
-int platform_set_snd_device_backend(snd_device_t device, const char *backend)
+int platform_set_snd_device_backend(snd_device_t device, const char *backend_tag,
+                                    const char * hw_interface)
 {
     int ret = 0;
 
@@ -4019,10 +4143,22 @@
         goto done;
     }
 
-    if (backend_table[device]) {
-        free(backend_table[device]);
+    ALOGD("%s: backend_tag_table[%s]: old = %s new = %s", __func__,
+          platform_get_snd_device_name(device),
+          backend_tag_table[device] != NULL ? backend_tag_table[device]: "null",
+          backend_tag);
+    if (backend_tag_table[device]) {
+        free(backend_tag_table[device]);
     }
-    backend_table[device] = strdup(backend);
+    backend_tag_table[device] = strdup(backend_tag);
+
+    if (hw_interface != NULL) {
+        if (hw_interface_table[device])
+            free(hw_interface_table[device]);
+
+        ALOGD("%s: hw_interface_table[%d] = %s", __func__, device, hw_interface);
+        hw_interface_table[device] = strdup(hw_interface);
+    }
 done:
     return ret;
 }
@@ -4040,6 +4176,7 @@
         ALOGE("%s: invalid usecase type", __func__);
         ret = -EINVAL;
     }
+    ALOGV("%s: pcm_device_table[%d][%d] = %d", __func__, usecase, type, pcm_id);
     pcm_device_table[usecase][type] = pcm_id;
 done:
     return ret;
diff --git a/hal/msm8916/platform.h b/hal/msm8916/platform.h
index da16feb..23435ea 100644
--- a/hal/msm8916/platform.h
+++ b/hal/msm8916/platform.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
  * Not a Contribution.
  *
  * Copyright (C) 2013 The Android Open Source Project
@@ -191,7 +191,12 @@
 #define HEADPHONE_44_1_BACKEND_PORT     5
 enum {
     DEFAULT_CODEC_BACKEND,
+    SLIMBUS_0_RX = DEFAULT_CODEC_BACKEND,
     HEADPHONE_44_1_BACKEND,
+    SLIMBUS_5_RX = HEADPHONE_44_1_BACKEND,
+    HEADPHONE_BACKEND,
+    SLIMBUS_6_RX = HEADPHONE_BACKEND,
+    HDMI_RX_BACKEND,
     MAX_CODEC_BACKENDS
 };
 #define AUDIO_PARAMETER_KEY_NATIVE_AUDIO "audio.nat.codec.enabled"
diff --git a/hal/msm8960/platform.c b/hal/msm8960/platform.c
index e32a73d..48470b9 100644
--- a/hal/msm8960/platform.c
+++ b/hal/msm8960/platform.c
@@ -1080,8 +1080,9 @@
     return -ENOSYS;
 }
 
-int platform_set_snd_device_backend(snd_device_t snd_device __unused,
-                                    const char * backend __unused)
+int platform_set_snd_device_backend(snd_device_t device __unused,
+                                    const char *backend __unused,
+                                    const char *hw_interface __unused)
 {
     return -ENOSYS;
 }
@@ -1210,4 +1211,16 @@
 int platform_spkr_prot_is_wsa_analog_mode(void *adev __unused)
 {
     return 0;
+bool platform_can_split_snd_device(void *platform __unused,
+                                   snd_device_t in_snd_device __unused,
+                                   int *num_devices __unused,
+                                   snd_device_t *out_snd_devices __unused)
+{
+    return false;
+}
+
+bool platform_check_backends_match(snd_device_t snd_device1 __unused,
+                                   snd_device_t snd_device2 __unused)
+{
+    return true;
 }
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index 785a202..87c4f7d 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -636,7 +636,8 @@
     {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_QMIC_AEC_NS)},
 };
 
-static char * backend_table[SND_DEVICE_MAX] = {0};
+static char * backend_tag_table[SND_DEVICE_MAX] = {0};
+static char * hw_interface_table[SND_DEVICE_MAX] = {0};
 
 static struct name_to_index usecase_name_index[AUDIO_USECASE_MAX] = {
     {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_DEEP_BUFFER)},
@@ -1035,35 +1036,39 @@
     const char *MEDIA_MIMETYPE_AUDIO_APE = "audio/x-ape";
 
     for (dev = 0; dev < SND_DEVICE_MAX; dev++) {
-        backend_table[dev] = NULL;
+        backend_tag_table[dev] = NULL;
+        hw_interface_table[dev] = NULL;
     }
     for (dev = 0; dev < SND_DEVICE_MAX; dev++) {
         backend_bit_width_table[dev] = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
     }
 
-    // TBD - do these go to the platform-info.xml file.
-    // will help in avoiding strdups here
-    backend_table[SND_DEVICE_IN_BT_SCO_MIC] = strdup("bt-sco");
-    backend_table[SND_DEVICE_IN_BT_SCO_MIC_WB] = strdup("bt-sco-wb");
-    backend_table[SND_DEVICE_IN_BT_SCO_MIC_NREC] = strdup("bt-sco");
-    backend_table[SND_DEVICE_IN_BT_SCO_MIC_WB_NREC] = strdup("bt-sco-wb");
-    backend_table[SND_DEVICE_OUT_BT_SCO] = strdup("bt-sco");
-    backend_table[SND_DEVICE_OUT_BT_SCO_WB] = strdup("bt-sco-wb");
-    backend_table[SND_DEVICE_OUT_HDMI] = strdup("hdmi");
-    backend_table[SND_DEVICE_OUT_SPEAKER_AND_HDMI] = strdup("speaker-and-hdmi");
-    backend_table[SND_DEVICE_OUT_VOICE_TX] = strdup("afe-proxy");
-    backend_table[SND_DEVICE_IN_VOICE_RX] = strdup("afe-proxy");
-
-    backend_table[SND_DEVICE_OUT_AFE_PROXY] = strdup("afe-proxy");
-    backend_table[SND_DEVICE_OUT_USB_HEADSET] = strdup("usb-headphones");
-    backend_table[SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET] =
+    // To overwrite these go to the audio_platform_info.xml file.
+    backend_tag_table[SND_DEVICE_IN_BT_SCO_MIC] = strdup("bt-sco");
+    backend_tag_table[SND_DEVICE_IN_BT_SCO_MIC_WB] = strdup("bt-sco-wb");
+    backend_tag_table[SND_DEVICE_IN_BT_SCO_MIC_NREC] = strdup("bt-sco");
+    backend_tag_table[SND_DEVICE_IN_BT_SCO_MIC_WB_NREC] = strdup("bt-sco-wb");
+    backend_tag_table[SND_DEVICE_OUT_BT_SCO] = strdup("bt-sco");
+    backend_tag_table[SND_DEVICE_OUT_BT_SCO_WB] = strdup("bt-sco-wb");
+    backend_tag_table[SND_DEVICE_OUT_HDMI] = strdup("hdmi");
+    backend_tag_table[SND_DEVICE_OUT_SPEAKER_AND_HDMI] = strdup("speaker-and-hdmi");
+    backend_tag_table[SND_DEVICE_OUT_VOICE_TX] = strdup("afe-proxy");
+    backend_tag_table[SND_DEVICE_IN_VOICE_RX] = strdup("afe-proxy");
+    backend_tag_table[SND_DEVICE_OUT_AFE_PROXY] = strdup("afe-proxy");
+    backend_tag_table[SND_DEVICE_OUT_USB_HEADSET] = strdup("usb-headphones");
+    backend_tag_table[SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET] =
         strdup("speaker-and-usb-headphones");
-    backend_table[SND_DEVICE_IN_USB_HEADSET_MIC] = strdup("usb-headset-mic");
-    backend_table[SND_DEVICE_IN_CAPTURE_FM] = strdup("capture-fm");
-    backend_table[SND_DEVICE_OUT_TRANSMISSION_FM] = strdup("transmission-fm");
-    backend_table[SND_DEVICE_OUT_HEADPHONES] = strdup("headphones");
-    backend_table[SND_DEVICE_OUT_HEADPHONES_44_1] = strdup("headphones-44.1");
-    backend_table[SND_DEVICE_OUT_VOICE_SPEAKER_VBAT] = strdup("voice-speaker-vbat");
+    backend_tag_table[SND_DEVICE_IN_USB_HEADSET_MIC] = strdup("usb-headset-mic");
+    backend_tag_table[SND_DEVICE_IN_CAPTURE_FM] = strdup("capture-fm");
+    backend_tag_table[SND_DEVICE_OUT_TRANSMISSION_FM] = strdup("transmission-fm");
+    backend_tag_table[SND_DEVICE_OUT_HEADPHONES] = strdup("headphones");
+    backend_tag_table[SND_DEVICE_OUT_HEADPHONES_44_1] = strdup("headphones-44.1");
+    backend_tag_table[SND_DEVICE_OUT_VOICE_SPEAKER_VBAT] = strdup("voice-speaker-vbat");
+
+    hw_interface_table[SND_DEVICE_OUT_HEADPHONES_44_1] = strdup("SLIMBUS_5_RX");
+    hw_interface_table[SND_DEVICE_OUT_HDMI] = strdup("HDMI_RX");
+    hw_interface_table[SND_DEVICE_OUT_SPEAKER_AND_HDMI] = strdup("SLIMBUS_0_RX-and-HDMI_RX");
+    hw_interface_table[SND_DEVICE_OUT_VOICE_TX] = strdup("AFE_PCM_RX");
 
 
      /*remove ALAC & APE from DSP decoder list based on software decoder availability*/
@@ -1643,6 +1648,11 @@
         }
     }
 
+    my_data->current_backend_cfg[HEADPHONE_BACKEND].bitwidth_mixer_ctl =
+        strdup("SLIM_6_RX Format");
+    my_data->current_backend_cfg[HEADPHONE_BACKEND].samplerate_mixer_ctl =
+        strdup("SLIM_6_RX SampleRate");
+
     my_data->edid_info = NULL;
     free(snd_card_name);
     free(snd_card_name_t);
@@ -1668,9 +1678,9 @@
 
     int32_t dev;
     for (dev = 0; dev < SND_DEVICE_MAX; dev++) {
-        if (backend_table[dev]) {
-            free(backend_table[dev]);
-            backend_table[dev]= NULL;
+        if (backend_tag_table[dev]) {
+            free(backend_tag_table[dev]);
+            backend_tag_table[dev]= NULL;
         }
     }
 
@@ -1747,7 +1757,7 @@
         return;
     }
 
-    const char * suffix = backend_table[snd_device];
+    const char * suffix = backend_tag_table[snd_device];
 
     if (suffix != NULL) {
         strlcat(mixer_path, " ", MIXER_PATH_MAX_LENGTH);
@@ -1755,6 +1765,40 @@
     }
 }
 
+bool platform_check_backends_match(snd_device_t snd_device1, snd_device_t snd_device2)
+{
+    bool result = true;
+
+    ALOGV("%s: snd_device1 = %s, snd_device2 = %s", __func__,
+                platform_get_snd_device_name(snd_device1),
+                platform_get_snd_device_name(snd_device2));
+
+    if ((snd_device1 < SND_DEVICE_MIN) || (snd_device1 >= SND_DEVICE_OUT_END)) {
+        ALOGE("%s: Invalid snd_device = %s", __func__,
+                platform_get_snd_device_name(snd_device1));
+        return false;
+    }
+    if ((snd_device2 < SND_DEVICE_MIN) || (snd_device2 >= SND_DEVICE_OUT_END)) {
+        ALOGE("%s: Invalid snd_device = %s", __func__,
+                platform_get_snd_device_name(snd_device2));
+        return false;
+    }
+    const char * be_itf1 = hw_interface_table[snd_device1];
+    const char * be_itf2 = hw_interface_table[snd_device2];
+
+    if (NULL != be_itf1 && NULL != be_itf2) {
+        if (0 != strcmp(be_itf1, be_itf2))
+            result = false;
+    } else if (NULL == be_itf1 && NULL != be_itf2) {
+            result = false;
+    } else if (NULL != be_itf1 && NULL == be_itf2) {
+            result = false;
+    }
+
+    ALOGV("%s: be_itf1 = %s, be_itf2 = %s, match %d", __func__, be_itf1, be_itf2, result);
+    return result;
+}
+
 int platform_get_pcm_device_id(audio_usecase_t usecase, int device_type)
 {
     int device_id;
@@ -1878,6 +1922,8 @@
         goto done;
     }
 
+    ALOGV("%s: acdb_device_table[%s]: old = %d new = %d", __func__,
+          platform_get_snd_device_name(snd_device), acdb_device_table[snd_device], acdb_id);
     acdb_device_table[snd_device] = acdb_id;
 done:
     return ret;
@@ -1941,7 +1987,7 @@
     if (NATIVE_AUDIO_MODE_SRC == na_mode || NATIVE_AUDIO_MODE_TRUE_44_1 == na_mode) {
         na_props.platform_na_prop_enabled = na_props.ui_na_prop_enabled = true;
         na_props.na_mode = na_mode;
-        ALOGD("%s:napb: native audio playback enabled in (%s) mode", __func__,
+        ALOGD("%s:napb: native audio playback enabled in (%s) mode v2.0", __func__,
               ((na_mode == NATIVE_AUDIO_MODE_SRC)?"SRC mode":"True 44.1 mode"));
     }
     else {
@@ -2068,21 +2114,26 @@
 
     return ret;
 }
-int platform_get_backend_index(snd_device_t snd_device)
+static int platform_get_backend_index(snd_device_t snd_device)
 {
     int32_t port = DEFAULT_CODEC_BACKEND;
 
     if (snd_device >= SND_DEVICE_MIN && snd_device < SND_DEVICE_MAX) {
-        if (backend_table[snd_device] != NULL &&
-            strcmp(backend_table[snd_device], "headphones-44.1") == 0)
-            port = HEADPHONE_44_1_BACKEND;
-        else
-            port = DEFAULT_CODEC_BACKEND;
+        if (backend_tag_table[snd_device] != NULL) {
+                if (strncmp(backend_tag_table[snd_device], "headphones-44.1",
+                            sizeof("headphones-44.1")) == 0)
+                        port = HEADPHONE_44_1_BACKEND;
+                else if (strncmp(backend_tag_table[snd_device], "headphones",
+                            sizeof("headphones")) == 0)
+                        port = HEADPHONE_BACKEND;
+                else if (strcmp(backend_tag_table[snd_device], "hdmi") == 0)
+                        port = HDMI_RX_BACKEND;
+        }
     } else {
         ALOGV("%s:napb: Invalid device - %d ", __func__, snd_device);
     }
 
-    ALOGV("%s:napb: backend port - %d", __func__, port);
+    ALOGV("%s:napb: backend port - %d snd_device %d", __func__, port, snd_device);
     return port;
 }
 
@@ -2092,9 +2143,11 @@
     struct platform_data *my_data = (struct platform_data *)platform;
     int acdb_dev_id, acdb_dev_type;
     int snd_device = SND_DEVICE_OUT_SPEAKER;
+    int new_snd_device[SND_DEVICE_OUT_END];
+    int i, num_devices = 1;
 
     if (usecase->type == PCM_PLAYBACK)
-        snd_device =  usecase->out_snd_device;
+        snd_device = usecase->out_snd_device;
     else if ((usecase->type == PCM_CAPTURE) &&
                    voice_is_in_call_rec_stream(usecase->stream.in))
         snd_device = voice_get_incall_rec_snd_device(usecase->in_snd_device);
@@ -2107,17 +2160,32 @@
               __func__, snd_device);
         return -EINVAL;
     }
-    if (my_data->acdb_send_audio_cal) {
-        ALOGV("%s: sending audio calibration for snd_device(%d) acdb_id(%d)",
-              __func__, snd_device, acdb_dev_id);
-        if (snd_device >= SND_DEVICE_OUT_BEGIN &&
-                snd_device < SND_DEVICE_OUT_END)
-            acdb_dev_type = ACDB_DEV_TYPE_OUT;
-        else
-            acdb_dev_type = ACDB_DEV_TYPE_IN;
-        my_data->acdb_send_audio_cal(acdb_dev_id, acdb_dev_type, app_type,
-                                     sample_rate);
+
+    if(!platform_can_split_snd_device(my_data, snd_device,
+            &num_devices, new_snd_device)) {
+        new_snd_device[0] = snd_device;
     }
+
+    for (i = 0; i < num_devices; i++) {
+        acdb_dev_id = acdb_device_table[platform_get_spkr_prot_snd_device(new_snd_device[i])];
+        if (acdb_dev_id < 0) {
+            ALOGE("%s: Could not find acdb id for device(%d)",
+                  __func__, new_snd_device[i]);
+            return -EINVAL;
+        }
+        if (my_data->acdb_send_audio_cal) {
+            ALOGV("%s: sending audio calibration for snd_device(%d) acdb_id(%d)",
+                  __func__, new_snd_device[i], acdb_dev_id);
+            if (new_snd_device[i] >= SND_DEVICE_OUT_BEGIN &&
+                    new_snd_device[i] < SND_DEVICE_OUT_END)
+                acdb_dev_type = ACDB_DEV_TYPE_OUT;
+            else
+                acdb_dev_type = ACDB_DEV_TYPE_IN;
+            my_data->acdb_send_audio_cal(acdb_dev_id, acdb_dev_type, app_type,
+                                         sample_rate);
+        }
+    }
+
     return 0;
 }
 
@@ -2386,6 +2454,43 @@
     return ret;
 }
 
+bool platform_can_split_snd_device(void *platform,
+                                   snd_device_t snd_device,
+                                   int *num_devices,
+                                   snd_device_t *new_snd_devices)
+{
+    bool status = false;
+    struct platform_data *my_data = (struct platform_data *)platform;
+
+    if ( NULL == num_devices || NULL == new_snd_devices || NULL == my_data) {
+        ALOGE("%s: NULL pointer ..", __func__);
+        return false;
+    }
+
+    /*
+     * If wired headset/headphones/line devices share the same backend
+     * with speaker/earpiece this routine returns false.
+     */
+    if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES &&
+        !platform_check_backends_match(SND_DEVICE_OUT_SPEAKER, SND_DEVICE_OUT_HEADPHONES)) {
+        *num_devices = 2;
+        new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
+        new_snd_devices[1] = SND_DEVICE_OUT_HEADPHONES;
+        status = true;
+    } else if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_HDMI &&
+               !platform_check_backends_match(SND_DEVICE_OUT_SPEAKER, SND_DEVICE_OUT_HDMI)) {
+        *num_devices = 2;
+        new_snd_devices[0] = SND_DEVICE_OUT_SPEAKER;
+        new_snd_devices[1] = SND_DEVICE_OUT_HDMI;
+        status = true;
+    }
+
+    ALOGD("%s: snd_device(%d) num devices(%d) new_snd_devices(%d)", __func__,
+        snd_device, *num_devices, *new_snd_devices);
+
+    return status;
+}
+
 snd_device_t platform_get_output_snd_device(void *platform, struct stream_out *out)
 {
     struct platform_data *my_data = (struct platform_data *)platform;
@@ -3704,7 +3809,7 @@
 /*
  * configures afe with bit width and Sample Rate
  */
-int platform_set_codec_backend_cfg(struct audio_device* adev,
+static int platform_set_codec_backend_cfg(struct audio_device* adev,
                          snd_device_t snd_device,
                          unsigned int bit_width, unsigned int sample_rate)
 {
@@ -3802,7 +3907,7 @@
  * goes through all the current usecases and picks the highest
  * bitwidth & samplerate
  */
-bool platform_check_codec_backend_cfg(struct audio_device* adev,
+static bool platform_check_codec_backend_cfg(struct audio_device* adev,
                                    struct audio_usecase* usecase,
                                    snd_device_t snd_device,
                                    unsigned int* new_bit_width,
@@ -3813,7 +3918,6 @@
     unsigned int bit_width;
     unsigned int sample_rate;
     int backend_idx = DEFAULT_CODEC_BACKEND;
-    int usecase_backend_idx = DEFAULT_CODEC_BACKEND;
     struct platform_data *my_data = (struct platform_data *)adev->platform;
     int na_mode = platform_get_native_support();
 
@@ -3852,8 +3956,6 @@
             uc = node_to_item(node, struct audio_usecase, list);
             struct stream_out *out = (struct stream_out*) uc->stream.out;
             if (uc->type == PCM_PLAYBACK && out && usecase != uc) {
-                usecase_backend_idx =
-                    platform_get_backend_index(uc->out_snd_device);
 
                 ALOGD("%s:napb: (%d) - (%s)id (%d) sr %d bw "
                       "(%d) device %s", __func__, i++, use_case_table[uc->id],
@@ -3861,7 +3963,7 @@
                       out->bit_width,
                       platform_get_snd_device_name(uc->out_snd_device));
 
-                if (usecase_backend_idx == backend_idx) {
+                if (platform_check_backends_match(snd_device, uc->out_snd_device)) {
                         if (bit_width < out->bit_width)
                             bit_width = out->bit_width;
                         if (sample_rate < out->sample_rate)
@@ -3899,10 +4001,12 @@
      * hifi playback not supported on spkr devices, limit the Sample Rate
      * to 48 khz.
      */
-    if (usecase->stream.out->devices & AUDIO_DEVICE_OUT_SPEAKER) {
+    if (SND_DEVICE_OUT_SPEAKER == snd_device ||
+        SND_DEVICE_OUT_SPEAKER_WSA == snd_device ||
+        SND_DEVICE_OUT_SPEAKER_VBAT == snd_device) {
         sample_rate = CODEC_BACKEND_DEFAULT_SAMPLE_RATE;
-        ALOGD("%s:becf: afe: playback on speakers Configure afe to \
-            default Sample Rate(48k)", __func__);
+        ALOGD("%s:becf: afe: playback on speaker device Configure afe to "
+            "default Sample Rate(48k)", __func__);
     }
 
     /*
@@ -3937,6 +4041,10 @@
     unsigned int new_bit_width;
     unsigned int new_sample_rate;
     int backend_idx = DEFAULT_CODEC_BACKEND;
+    int new_snd_devices[SND_DEVICE_OUT_END];
+    int i, num_devices = 1;
+    bool ret = false;
+    struct platform_data *my_data = (struct platform_data *)adev->platform;
 
     backend_idx = platform_get_backend_index(snd_device);
 
@@ -3948,17 +4056,25 @@
           new_sample_rate, backend_idx, usecase->id,
           platform_get_snd_device_name(snd_device));
 
-    if (platform_check_codec_backend_cfg(adev, usecase, snd_device,
-                                      &new_bit_width, &new_sample_rate)) {
-        platform_set_codec_backend_cfg(adev, snd_device,
-                                       new_bit_width, new_sample_rate);
-        return true;
+
+    if (!platform_can_split_snd_device(my_data, snd_device, &num_devices, new_snd_devices))
+        new_snd_devices[0] = snd_device;
+
+    for (i = 0; i < num_devices; i++) {
+        ALOGI("%s: new_snd_devices[%d] is %d", __func__, i, new_snd_devices[i]);
+        if (platform_check_codec_backend_cfg(adev, usecase, new_snd_devices[i],
+                                             &new_bit_width, &new_sample_rate)) {
+                platform_set_codec_backend_cfg(adev, new_snd_devices[i],
+                                               new_bit_width, new_sample_rate);
+                ret = true;
+        }
     }
 
-    return false;
+    return ret;
 }
 
-int platform_set_snd_device_backend(snd_device_t device, const char *backend)
+int platform_set_snd_device_backend(snd_device_t device, const char *backend_tag,
+                                    const char * hw_interface)
 {
     int ret = 0;
 
@@ -3969,10 +4085,22 @@
         goto done;
     }
 
-    if (backend_table[device]) {
-        free(backend_table[device]);
+    ALOGD("%s: backend_tag_table[%s]: old = %s new = %s", __func__,
+          platform_get_snd_device_name(device),
+          backend_tag_table[device] != NULL ? backend_tag_table[device]: "null",
+          backend_tag);
+    if (backend_tag_table[device]) {
+        free(backend_tag_table[device]);
     }
-    backend_table[device] = strdup(backend);
+    backend_tag_table[device] = strdup(backend_tag);
+
+    if (hw_interface != NULL) {
+        if (hw_interface_table[device])
+            free(hw_interface_table[device]);
+
+        ALOGD("%s: hw_interface_table[%d] = %s", __func__, device, hw_interface);
+        hw_interface_table[device] = strdup(hw_interface);
+    }
 done:
     return ret;
 }
@@ -3990,6 +4118,7 @@
         ALOGE("%s: invalid usecase type", __func__);
         ret = -EINVAL;
     }
+    ALOGV("%s: pcm_device_table[%d][%d] = %d", __func__, usecase, type, pcm_id);
     pcm_device_table[usecase][type] = pcm_id;
 done:
     return ret;
diff --git a/hal/msm8974/platform.h b/hal/msm8974/platform.h
index be5160a..b56c40e 100644
--- a/hal/msm8974/platform.h
+++ b/hal/msm8974/platform.h
@@ -182,7 +182,12 @@
 
 enum {
     DEFAULT_CODEC_BACKEND,
+    SLIMBUS_0_RX = DEFAULT_CODEC_BACKEND,
     HEADPHONE_44_1_BACKEND,
+    SLIMBUS_5_RX = HEADPHONE_44_1_BACKEND,
+    HEADPHONE_BACKEND,
+    SLIMBUS_6_RX = HEADPHONE_BACKEND,
+    HDMI_RX_BACKEND,
     MAX_CODEC_BACKENDS
 };
 
diff --git a/hal/platform_api.h b/hal/platform_api.h
index fb72e40..65bcc98 100644
--- a/hal/platform_api.h
+++ b/hal/platform_api.h
@@ -54,7 +54,6 @@
 int platform_get_snd_device_bit_width(snd_device_t snd_device);
 int platform_set_native_support(int na_mode);
 int platform_get_native_support();
-int platform_get_backend_index(snd_device_t snd_device);
 int platform_send_audio_calibration(void *platform, struct audio_usecase *usecase,
                                     int app_type, int sample_rate);
 int platform_get_default_app_type(void *platform);
@@ -99,7 +98,8 @@
 bool platform_sound_trigger_device_needs_event(snd_device_t snd_device);
 bool platform_sound_trigger_usecase_needs_event(audio_usecase_t uc_id);
 
-int platform_set_snd_device_backend(snd_device_t snd_device, const char * backend);
+int platform_set_snd_device_backend(snd_device_t snd_device, const char * backend,
+                                    const char * hw_interface);
 
 /* From platform_info.c */
 int platform_info_init(const char *filename, void *);
@@ -138,4 +138,10 @@
 int platform_get_spkr_prot_acdb_id(snd_device_t snd_device);
 int platform_get_spkr_prot_snd_device(snd_device_t snd_device);
 int platform_spkr_prot_is_wsa_analog_mode(void *adev);
+bool platform_can_split_snd_device(void *platform,
+                                   snd_device_t snd_device,
+                                   int *num_devices,
+                                   snd_device_t *new_snd_devices);
+
+bool platform_check_backends_match(snd_device_t snd_device1, snd_device_t snd_device2);
 #endif // AUDIO_PLATFORM_API_H
diff --git a/hal/platform_info.c b/hal/platform_info.c
index 2be66c4..973d4c4 100644
--- a/hal/platform_info.c
+++ b/hal/platform_info.c
@@ -177,6 +177,7 @@
 static void process_backend_name(const XML_Char **attr)
 {
     int index;
+    char *hw_interface = NULL;
 
     if (strcmp(attr[0], "name") != 0) {
         ALOGE("%s: 'name' not found, no ACDB ID set!", __func__);
@@ -196,7 +197,15 @@
         goto done;
     }
 
-    if (platform_set_snd_device_backend(index, attr[3]) < 0) {
+    if (attr[4] != NULL) {
+        if (strcmp(attr[4], "interface") != 0) {
+            hw_interface = NULL;
+        } else {
+            hw_interface = (char *)attr[5];
+        }
+    }
+
+    if (platform_set_snd_device_backend(index, attr[3], hw_interface) < 0) {
         ALOGE("%s: Device %s backend %s was not set!",
               __func__, attr[1], attr[3]);
         goto done;