qcom/audio/hal: Fix the routing logic to route streams independently

- Current implementation assumes that the output devices for all
  the output streams and voice call will be same. So it updates
  the devices on all the output streams when out_set_parameters()
  is called on any stream.
- Update the routing logic to support all the streams independently
  based on the devices set by audio policy manager on each stream.
- However, on this target there is a limitation that earpiece,
  speaker, and headset devices cannot be enabled concurrently as
  they share the same backend. Updated routing logic takes care of
  this limitation.

Bug: 8239898
Change-Id: I3091be6894210c77c479b872cec39d821d10bd90
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 1c1adb2..15b1be0 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -121,6 +121,7 @@
 
 /* ACDB IDs (audio DSP path configuration IDs) for each sound device */
 static const int acdb_device_table[SND_DEVICE_MAX] = {
+    [SND_DEVICE_NONE] = -1,
     [SND_DEVICE_OUT_HANDSET] = 7,
     [SND_DEVICE_OUT_SPEAKER] = 14,
     [SND_DEVICE_OUT_HEADPHONES] = 10,
@@ -191,48 +192,37 @@
                              audio_usecase_t usecase,
                              int device_type)
 {
-    ALOGV("%s: enter: usecase(%d)", __func__, usecase);
     int device_id;
     if (device_type == PCM_PLAYBACK)
         device_id = pcm_device_table[usecase][0];
     else
         device_id = pcm_device_table[usecase][1];
-    ALOGV("%s: exit: device_id(%d)", __func__, device_id);
     return device_id;
 }
 
 static int get_acdb_device_id(snd_device_t snd_device)
 {
-    ALOGV("%s: enter: snd_devie(%d)", __func__, snd_device);
-    int acdb_dev_id = acdb_device_table[snd_device];
-    ALOGV("%s: exit: acdb_dev_id(%d)", __func__, acdb_dev_id);
-    return acdb_dev_id;
+    return acdb_device_table[snd_device];
 }
 
 static void add_backend_name(char *mixer_path,
-                             struct audio_usecase *usecase)
+                             snd_device_t snd_device)
 {
-    audio_devices_t in_device;
-    if (usecase->devices & AUDIO_DEVICE_BIT_IN) {
-        in_device = usecase->devices & ~AUDIO_DEVICE_BIT_IN;
-        if (in_device & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) {
-            strcat(mixer_path, " bt-sco");
-        }
-    } else {
-        if (usecase->devices & AUDIO_DEVICE_OUT_ALL_SCO) {
-            strcat(mixer_path, " bt-sco");
-        } else if ((usecase->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) &&
-                   (usecase->devices & AUDIO_DEVICE_OUT_SPEAKER)) {
-            strcat(mixer_path, " speaker-and-hdmi");
-        } else if (usecase->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
-            strcat(mixer_path, " hdmi");
-        }
-    }
+    if (snd_device == SND_DEVICE_IN_BT_SCO_MIC)
+        strcat(mixer_path, " bt-sco");
+    else if(snd_device == SND_DEVICE_OUT_BT_SCO)
+        strcat(mixer_path, " bt-sco");
+    else if (snd_device == SND_DEVICE_OUT_HDMI)
+        strcat(mixer_path, " hdmi");
+    else if (snd_device == SND_DEVICE_OUT_SPEAKER_AND_HDMI)
+        strcat(mixer_path, " speaker-and-hdmi");
 }
 
-static int enable_audio_route(struct audio_route *ar,
-                              struct audio_usecase *usecase)
+static int enable_audio_route(struct audio_device *adev,
+                              struct audio_usecase *usecase,
+                              bool update_mixer)
 {
+    snd_device_t snd_device;
     char mixer_path[50];
 
     if (usecase == NULL)
@@ -240,91 +230,190 @@
 
     ALOGV("%s: enter: usecase(%d)", __func__, usecase->id);
 
-    /* Get the updated devices from associated stream */
     if (usecase->type == PCM_CAPTURE)
-        usecase->devices = usecase->stream.in->device;
+        snd_device = usecase->in_snd_device;
     else
-        usecase->devices = usecase->stream.out->devices;
+        snd_device = usecase->out_snd_device;
 
     strcpy(mixer_path, use_case_table[usecase->id]);
-    add_backend_name(mixer_path, usecase);
+    add_backend_name(mixer_path, snd_device);
     ALOGD("%s: apply mixer path: %s", __func__, mixer_path);
-    audio_route_apply_path(ar, mixer_path);
+    audio_route_apply_path(adev->audio_route, mixer_path);
+    if (update_mixer)
+        audio_route_update_mixer(adev->audio_route);
 
     ALOGV("%s: exit", __func__);
     return 0;
 }
 
-static int disable_audio_route(struct audio_route *ar,
-                               struct audio_usecase *usecase)
+static int disable_audio_route(struct audio_device *adev,
+                               struct audio_usecase *usecase,
+                               bool update_mixer)
 {
+    snd_device_t snd_device;
     char mixer_path[50];
 
     if (usecase == NULL)
         return -EINVAL;
 
     ALOGV("%s: enter: usecase(%d)", __func__, usecase->id);
-
+    if (usecase->type == PCM_CAPTURE)
+        snd_device = usecase->in_snd_device;
+    else
+        snd_device = usecase->out_snd_device;
     strcpy(mixer_path, use_case_table[usecase->id]);
-    add_backend_name(mixer_path, usecase);
+    add_backend_name(mixer_path, snd_device);
     ALOGD("%s: reset mixer path: %s", __func__, mixer_path);
-    audio_route_reset_path(ar, mixer_path);
+    audio_route_reset_path(adev->audio_route, mixer_path);
+    if (update_mixer)
+        audio_route_update_mixer(adev->audio_route);
 
     ALOGV("%s: exit", __func__);
     return 0;
 }
 
 static int enable_snd_device(struct audio_device *adev,
-                             snd_device_t snd_device)
+                             snd_device_t snd_device,
+                             bool update_mixer)
 {
     int acdb_dev_id, acdb_dev_type;
 
-    ALOGD("%s: snd_device(%d: %s)", __func__,
-          snd_device, device_table[snd_device]);
     if (snd_device < SND_DEVICE_MIN ||
         snd_device >= SND_DEVICE_MAX) {
         ALOGE("%s: Invalid sound device %d", __func__, snd_device);
         return -EINVAL;
     }
+
+    adev->snd_dev_ref_cnt[snd_device]++;
+    if (adev->snd_dev_ref_cnt[snd_device] > 1) {
+        ALOGD("%s: snd_device(%d: %s) is already active",
+              __func__, snd_device, device_table[snd_device]);
+        return 0;
+    }
+
     acdb_dev_id = get_acdb_device_id(snd_device);
     if (acdb_dev_id < 0) {
         ALOGE("%s: Could not find acdb id for device(%d)",
               __func__, snd_device);
+        adev->snd_dev_ref_cnt[snd_device]--;
         return -EINVAL;
     }
-    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;
-    }
     if (adev->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;
         adev->acdb_send_audio_cal(acdb_dev_id, acdb_dev_type);
     } else {
         ALOGW("%s: Could find the symbol acdb_send_audio_cal from %s",
               __func__, LIB_ACDB_LOADER);
     }
 
+    ALOGD("%s: snd_device(%d: %s)", __func__,
+          snd_device, device_table[snd_device]);
     audio_route_apply_path(adev->audio_route, device_table[snd_device]);
+    if (update_mixer)
+        audio_route_update_mixer(adev->audio_route);
+
     return 0;
 }
 
-static int disable_snd_device(struct audio_route *ar,
-                              snd_device_t    snd_device)
+static int disable_snd_device(struct audio_device *adev,
+                              snd_device_t snd_device,
+                              bool update_mixer)
 {
-    ALOGD("%s: snd_device(%d: %s)", __func__,
-          snd_device, device_table[snd_device]);
     if (snd_device < SND_DEVICE_MIN ||
         snd_device >= SND_DEVICE_MAX) {
         ALOGE("%s: Invalid sound device %d", __func__, snd_device);
         return -EINVAL;
     }
-    audio_route_reset_path(ar, device_table[snd_device]);
+    if (adev->snd_dev_ref_cnt[snd_device] <= 0) {
+        ALOGE("%s: device ref cnt is already 0", __func__);
+        return -EINVAL;
+    }
+    adev->snd_dev_ref_cnt[snd_device]--;
+    if (adev->snd_dev_ref_cnt[snd_device] == 0) {
+        ALOGD("%s: snd_device(%d: %s)", __func__,
+              snd_device, device_table[snd_device]);
+        audio_route_reset_path(adev->audio_route, device_table[snd_device]);
+        if (update_mixer)
+            audio_route_update_mixer(adev->audio_route);
+    }
     return 0;
 }
 
+static void check_usecases_codec_backend(struct audio_device *adev,
+                                          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;
+
+    /*
+     * This function is to make sure that all the usecases that are active on
+     * the hardware codec backend are always routed to any one device that is
+     * handled by the hardware codec.
+     * For example, if low-latency and deep-buffer usecases are currently active
+     * on speaker and out_set_parameters(headset) is received on low-latency
+     * output, then we have to make sure deep-buffer is also switched to headset,
+     * because of the limitation that both the devices cannot be enabled
+     * at the same time as they share the same backend.
+     */
+    /* Disable all the usecases on the shared backend other than the
+       specified usecase */
+    for (i = 0; i < AUDIO_USECASE_MAX; i++)
+        switch_device[i] = false;
+
+    list_for_each(node, &adev->usecase_list) {
+        usecase = node_to_item(node, struct audio_usecase, list);
+        if (usecase->type != PCM_CAPTURE &&
+                usecase != uc_info &&
+                usecase->out_snd_device != snd_device &&
+                usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND) {
+            ALOGV("%s: Usecase (%s) is active on (%s) - disabling ..",
+                  __func__, use_case_table[usecase->id],
+                  device_table[usecase->out_snd_device]);
+            disable_audio_route(adev, usecase, false);
+            switch_device[usecase->id] = true;
+            num_uc_to_switch++;
+        }
+    }
+
+    if (num_uc_to_switch) {
+        /* Make sure all the streams are de-routed before disabling the device */
+        audio_route_update_mixer(adev->audio_route);
+
+        list_for_each(node, &adev->usecase_list) {
+            usecase = node_to_item(node, struct audio_usecase, list);
+            if (switch_device[usecase->id]) {
+                disable_snd_device(adev, usecase->out_snd_device, false);
+                enable_snd_device(adev, snd_device, false);
+            }
+        }
+
+        /* Make sure new snd device is enabled before re-routing the streams */
+        audio_route_update_mixer(adev->audio_route);
+
+        /* Re-route all the usecases on the shared backend other than the
+           specified usecase to new snd devices */
+        list_for_each(node, &adev->usecase_list) {
+            usecase = node_to_item(node, struct audio_usecase, list);
+            /* Update the out_snd_device only before enabling the audio route */
+            if (switch_device[usecase->id] ) {
+                usecase->out_snd_device = snd_device;
+                enable_audio_route(adev, usecase, false);
+            }
+        }
+
+        audio_route_update_mixer(adev->audio_route);
+    }
+}
+
 static int set_hdmi_channels(struct mixer *mixer,
                              int channel_count)
 {
@@ -386,13 +475,11 @@
     ALOGV("%s: exit", __func__);
 }
 
-static snd_device_t get_output_snd_device(struct audio_device *adev)
+static snd_device_t get_output_snd_device(struct audio_device *adev,
+                                          audio_devices_t devices)
 {
-    audio_source_t  source = (adev->active_input == NULL) ?
-                                AUDIO_SOURCE_DEFAULT : adev->active_input->source;
-    audio_mode_t    mode   = adev->mode;
-    audio_devices_t devices = adev->out_device;
-    snd_device_t    snd_device = SND_DEVICE_NONE;
+    audio_mode_t mode = adev->mode;
+    snd_device_t snd_device = SND_DEVICE_NONE;
 
     ALOGV("%s: enter: output devices(%#x)", __func__, devices);
     if (devices == AUDIO_DEVICE_NONE ||
@@ -470,13 +557,13 @@
     return snd_device;
 }
 
-static snd_device_t get_input_snd_device(struct audio_device *adev)
+static snd_device_t get_input_snd_device(struct audio_device *adev,
+                                         audio_devices_t out_device)
 {
     audio_source_t  source = (adev->active_input == NULL) ?
                                 AUDIO_SOURCE_DEFAULT : adev->active_input->source;
 
     audio_mode_t    mode   = adev->mode;
-    audio_devices_t out_device = adev->out_device;
     audio_devices_t in_device = ((adev->active_input == NULL) ?
                                     AUDIO_DEVICE_NONE : adev->active_input->device)
                                 & ~AUDIO_DEVICE_BIT_IN;
@@ -620,24 +707,72 @@
     return snd_device;
 }
 
-static int select_devices(struct audio_device *adev)
+static struct audio_usecase *get_usecase_from_list(struct audio_device *adev,
+                                                   audio_usecase_t uc_id)
+{
+    struct audio_usecase *usecase;
+    struct listnode *node;
+
+    list_for_each(node, &adev->usecase_list) {
+        usecase = node_to_item(node, struct audio_usecase, list);
+        if (usecase->id == uc_id)
+            return usecase;
+    }
+    return NULL;
+}
+
+static int select_devices(struct audio_device *adev,
+                          audio_usecase_t uc_id)
 {
     snd_device_t out_snd_device = SND_DEVICE_NONE;
     snd_device_t in_snd_device = SND_DEVICE_NONE;
-    struct audio_usecase *usecase;
+    struct audio_usecase *usecase = NULL;
+    struct audio_usecase *vc_usecase = NULL;
     struct listnode *node;
-    int status = 0;
     int acdb_rx_id, acdb_tx_id;
-    bool in_call_device_switch = false;
+    int status = 0;
 
-    ALOGV("%s: enter", __func__);
-    out_snd_device = get_output_snd_device(adev);
-    in_snd_device  = get_input_snd_device(adev);
+    usecase = get_usecase_from_list(adev, uc_id);
+    if (usecase == NULL) {
+        ALOGE("%s: Could not find the usecase(%d)", __func__, uc_id);
+        return -EINVAL;
+    }
 
-    if (out_snd_device == adev->cur_out_snd_device && adev->out_snd_device_active &&
-        in_snd_device == adev->cur_in_snd_device && adev->in_snd_device_active) {
-        ALOGV("%s: exit: snd_devices (%d and %d) are already active",
-              __func__, out_snd_device, in_snd_device);
+    if (usecase->type == VOICE_CALL) {
+        out_snd_device = get_output_snd_device(adev, usecase->stream.out->devices);
+        in_snd_device = get_input_snd_device(adev, usecase->stream.out->devices);
+        usecase->devices = usecase->stream.out->devices;
+    } else {
+        /*
+         * If the voice call is active, use the sound devices of voice call usecase
+         * so that it would not result any device switch. All the usecases will
+         * be switched to new device when select_devices() is called for voice call
+         * usecase. This is to avoid switching devices for voice call when
+         * check_usecases_codec_backend() is called below.
+         */
+        if (adev->in_call) {
+            vc_usecase = get_usecase_from_list(adev, USECASE_VOICE_CALL);
+            if (vc_usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND) {
+                in_snd_device = vc_usecase->in_snd_device;
+                out_snd_device = vc_usecase->out_snd_device;
+            }
+        }
+        if (usecase->type == PCM_PLAYBACK) {
+            usecase->devices = usecase->stream.out->devices;
+            in_snd_device = SND_DEVICE_NONE;
+            if (out_snd_device == SND_DEVICE_NONE)
+                out_snd_device = get_output_snd_device(adev,
+                                            usecase->stream.out->devices);
+        } else if (usecase->type == PCM_CAPTURE) {
+            usecase->devices = usecase->stream.in->device;
+            out_snd_device = SND_DEVICE_NONE;
+            if (in_snd_device == SND_DEVICE_NONE)
+                in_snd_device = get_input_snd_device(adev, SND_DEVICE_NONE);
+        }
+    }
+
+    if (out_snd_device == usecase->out_snd_device &&
+        in_snd_device == usecase->in_snd_device) {
         return 0;
     }
 
@@ -650,8 +785,9 @@
      * and enable both RX and TX devices though one of them is same as current
      * device.
      */
-    if (adev->in_call && adev->csd_client != NULL) {
-        in_call_device_switch = true;
+    if (usecase->type == VOICE_CALL && adev->csd_client != NULL &&
+            usecase->in_snd_device != SND_DEVICE_NONE &&
+            usecase->out_snd_device != SND_DEVICE_NONE) {
         /* This must be called before disabling the mixer controls on APQ side */
         if (adev->csd_disable_device == NULL) {
             ALOGE("%s: dlsym error for csd_client_disable_device", __func__);
@@ -664,66 +800,35 @@
         }
     }
 
-    if ((out_snd_device != adev->cur_out_snd_device || in_call_device_switch)
-            && adev->out_snd_device_active) {
-        list_for_each(node, &adev->usecase_list) {
-            usecase = node_to_item(node, struct audio_usecase, list);
-            if (usecase->type == PCM_PLAYBACK || usecase->type == VOICE_CALL)
-                disable_audio_route(adev->audio_route, usecase);
-        }
-        audio_route_update_mixer(adev->audio_route);
-        /* Disable current rx device */
-        disable_snd_device(adev->audio_route, adev->cur_out_snd_device);
-        adev->out_snd_device_active = false;
+    /* Disable current sound devices */
+    if (usecase->out_snd_device != SND_DEVICE_NONE) {
+        disable_audio_route(adev, usecase, true);
+        disable_snd_device(adev, usecase->out_snd_device, false);
     }
 
-    if ((in_snd_device != adev->cur_in_snd_device || in_call_device_switch)
-            && adev->in_snd_device_active) {
-        list_for_each(node, &adev->usecase_list) {
-            usecase = node_to_item(node, struct audio_usecase, list);
-            if (usecase->type == PCM_CAPTURE)
-                disable_audio_route(adev->audio_route, usecase);
-        }
-        audio_route_update_mixer(adev->audio_route);
-        /* Disable current tx device */
-        disable_snd_device(adev->audio_route, adev->cur_in_snd_device);
-        adev->in_snd_device_active = false;
+    if (usecase->in_snd_device != SND_DEVICE_NONE) {
+        disable_audio_route(adev, usecase, true);
+        disable_snd_device(adev, usecase->in_snd_device, false);
     }
 
-    if (out_snd_device != SND_DEVICE_NONE && !adev->out_snd_device_active) {
-        /* Enable new rx device */
-        status = enable_snd_device(adev, out_snd_device);
-        if (status != 0) {
-            ALOGE("%s: Failed to set mixer ctls for snd_device(%d)",
-                  __func__, out_snd_device);
-            return status;
-        }
-        adev->out_snd_device_active = true;
-        adev->cur_out_snd_device = out_snd_device;
+    /* Enable new sound devices */
+    if (out_snd_device != SND_DEVICE_NONE) {
+        if (usecase->devices & AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND)
+            check_usecases_codec_backend(adev, usecase, out_snd_device);
+        enable_snd_device(adev, out_snd_device, false);
     }
 
-    if (in_snd_device != SND_DEVICE_NONE && !adev->in_snd_device_active) {
-        /* Enable new tx device */
-        status = enable_snd_device(adev, in_snd_device);
-        if (status != 0) {
-            ALOGE("%s: Failed to set mixer ctls for snd_device(%d)",
-                  __func__, out_snd_device);
-            return status;
-        }
-        adev->in_snd_device_active = true;
-        adev->cur_in_snd_device = in_snd_device;
-    }
+    if (in_snd_device != SND_DEVICE_NONE)
+        enable_snd_device(adev, in_snd_device, false);
+
     audio_route_update_mixer(adev->audio_route);
 
-    if (!list_empty(&adev->usecase_list)) {
-        list_for_each(node, &adev->usecase_list) {
-            usecase = node_to_item(node, struct audio_usecase, list);
-            enable_audio_route(adev->audio_route, usecase);
-        }
-        audio_route_update_mixer(adev->audio_route);
-    }
+    usecase->in_snd_device = in_snd_device;
+    usecase->out_snd_device = out_snd_device;
 
-    if (adev->mode == AUDIO_MODE_IN_CALL && adev->csd_client) {
+    enable_audio_route(adev, usecase, true);
+
+    if (usecase->type == VOICE_CALL && adev->csd_client) {
         if (adev->csd_enable_device == NULL) {
             ALOGE("%s: dlsym error for csd_client_enable_device",
                   __func__);
@@ -731,64 +836,23 @@
             acdb_rx_id = get_acdb_device_id(out_snd_device);
             acdb_tx_id = get_acdb_device_id(in_snd_device);
 
-            status = adev->csd_enable_device(acdb_rx_id, acdb_tx_id,
-                                             adev->acdb_settings);
-            if (status < 0) {
-                ALOGE("%s: csd_client_enable_device, failed, error %d",
-                      __func__, status);
+            if (acdb_rx_id > 0 || acdb_tx_id > 0) {
+                status = adev->csd_enable_device(acdb_rx_id, acdb_tx_id,
+                                                 adev->acdb_settings);
+                if (status < 0) {
+                    ALOGE("%s: csd_client_enable_device, failed, error %d",
+                          __func__, status);
+                }
+            } else {
+                ALOGE("%s: Incorrect ACDB IDs (rx: %d tx: %d)", __func__,
+                      acdb_rx_id, acdb_tx_id);
             }
         }
     }
 
-    ALOGV("%s: exit: status(%d)", __func__, status);
     return status;
 }
 
-static struct audio_usecase *get_usecase_from_list(struct audio_device *adev,
-                                                   audio_usecase_t uc_id)
-{
-    struct audio_usecase *usecase = NULL;
-    struct listnode *node;
-
-    list_for_each(node, &adev->usecase_list) {
-        usecase = node_to_item(node, struct audio_usecase, list);
-        if (usecase->id == uc_id)
-            break;
-    }
-    return usecase;
-}
-
-static audio_devices_t get_active_out_devices(struct audio_device *adev,
-                                              audio_usecase_t uc_id)
-{
-    audio_devices_t devices = AUDIO_DEVICE_NONE;
-    struct audio_usecase *usecase;
-    struct listnode *node;
-
-    /* Return the output devices of usecases other than given usecase */
-    list_for_each(node, &adev->usecase_list) {
-        usecase = node_to_item(node, struct audio_usecase, list);
-        if (usecase->type == PCM_PLAYBACK && usecase->id != uc_id)
-            devices |= usecase->stream.out->devices;
-    }
-
-    return devices;
-}
-
-static audio_devices_t get_voice_call_out_device(struct audio_device *adev)
-{
-    struct audio_usecase *usecase;
-    struct listnode *node;
-
-    list_for_each(node, &adev->usecase_list) {
-        usecase = node_to_item(node, struct audio_usecase, list);
-        if (usecase->type == VOICE_CALL)
-            return usecase->stream.out->devices;
-    }
-
-    return AUDIO_DEVICE_NONE;
-}
-
 static int stop_input_stream(struct stream_in *in)
 {
     int i, ret = 0;
@@ -797,7 +861,8 @@
 
     adev->active_input = NULL;
 
-    ALOGD("%s: enter: usecase(%d)", __func__, in->usecase);
+    ALOGD("%s: enter: usecase(%d: %s)", __func__,
+          in->usecase, use_case_table[in->usecase]);
     uc_info = get_usecase_from_list(adev, in->usecase);
     if (uc_info == NULL) {
         ALOGE("%s: Could not find the usecase (%d) in the list",
@@ -806,15 +871,14 @@
     }
 
     /* 1. Disable stream specific mixer controls */
-    disable_audio_route(adev->audio_route, uc_info);
-    audio_route_update_mixer(adev->audio_route);
+    disable_audio_route(adev, uc_info, true);
+
+    /* 2. Disable the tx device */
+    disable_snd_device(adev, uc_info->in_snd_device, true);
 
     list_remove(&uc_info->list);
     free(uc_info);
 
-    /* 2. Disable the tx device */
-    ret = select_devices(adev);
-
     ALOGD("%s: exit: status(%d)", __func__, ret);
     return ret;
 }
@@ -823,19 +887,10 @@
 {
     /* 1. Enable output device and stream routing controls */
     int ret = 0;
-    snd_device_t in_snd_device;
     struct audio_usecase *uc_info;
     struct audio_device *adev = in->dev;
 
     ALOGD("%s: enter: usecase(%d)", __func__, in->usecase);
-    adev->active_input = in;
-    in_snd_device = get_input_snd_device(adev);
-    if (in_snd_device == SND_DEVICE_NONE) {
-        ALOGE("%s: Could not get valid input sound device", __func__);
-        ret = -EINVAL;
-        goto error_config;
-    }
-
     in->pcm_device_id = get_pcm_device_id(adev->audio_route,
                                           in->usecase,
                                           PCM_CAPTURE);
@@ -845,29 +900,19 @@
         ret = -EINVAL;
         goto error_config;
     }
+
+    adev->active_input = in;
     uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
     uc_info->id = in->usecase;
     uc_info->type = PCM_CAPTURE;
-    uc_info->devices = in->device;
     uc_info->stream.in = in;
+    uc_info->devices = in->device;
+    uc_info->in_snd_device = SND_DEVICE_NONE;
+    uc_info->out_snd_device = SND_DEVICE_NONE;
 
-    /* 1. Enable the TX device */
-    ret = select_devices(adev);
-    if (ret) {
-        ALOGE("%s: Failed to enable device(%#x)",
-              __func__, in->device);
-        free(uc_info);
-        goto error_config;
-    }
-
-    /* 2. Enable the mixer controls for the audio route */
-    enable_audio_route(adev->audio_route, uc_info);
-    audio_route_update_mixer(adev->audio_route);
-
-    /* 3. Add the usecase info to usecase list */
     list_add_tail(&adev->usecase_list, &uc_info->list);
+    select_devices(adev, in->usecase);
 
-    /* 2. Open the pcm device */
     ALOGV("%s: Opening PCM device card_id(%d) device_id(%d), channels %d",
           __func__, SOUND_CARD, in->pcm_device_id, in->config.channels);
     in->pcm = pcm_open(SOUND_CARD, in->pcm_device_id,
@@ -887,7 +932,7 @@
 
 error_config:
     adev->active_input = NULL;
-    ALOGV("%s: exit: status(%d)", __func__, ret);
+    ALOGD("%s: exit: status(%d)", __func__, ret);
 
     return ret;
 }
@@ -898,7 +943,8 @@
     struct audio_usecase *uc_info;
     struct audio_device *adev = out->dev;
 
-    ALOGD("%s: enter: usecase(%d)", __func__, out->usecase);
+    ALOGD("%s: enter: usecase(%d: %s)", __func__,
+          out->usecase, use_case_table[out->usecase]);
     uc_info = get_usecase_from_list(adev, out->usecase);
     if (uc_info == NULL) {
         ALOGE("%s: Could not find the usecase (%d) in the list",
@@ -907,40 +953,26 @@
     }
 
     /* 1. Get and set stream specific mixer controls */
-    disable_audio_route(adev->audio_route, uc_info);
-    audio_route_update_mixer(adev->audio_route);
+    disable_audio_route(adev, uc_info, true);
+
+    /* 2. Disable the rx device */
+    disable_snd_device(adev, uc_info->out_snd_device, true);
 
     list_remove(&uc_info->list);
     free(uc_info);
 
-    /* 2. Disable the rx device */
-    adev->out_device = get_active_out_devices(adev, out->usecase);
-    adev->out_device |= get_voice_call_out_device(adev);
-    ret = select_devices(adev);
-
-    ALOGD("%s: exit: status(%d) adev->out_device(%#x)",
-          __func__, ret, adev->out_device);
+    ALOGD("%s: exit: status(%d)", __func__, ret);
     return ret;
 }
 
 int start_output_stream(struct stream_out *out)
 {
     int ret = 0;
-    snd_device_t out_snd_device;
     struct audio_usecase *uc_info;
     struct audio_device *adev = out->dev;
 
-    /* 1. Enable output device and stream routing controls */
-    ALOGD("%s: enter: usecase(%d) devices(%#x)",
-          __func__, out->usecase, out->devices);
-    adev->out_device |= out->devices;
-    out_snd_device = get_output_snd_device(adev);
-    if (out_snd_device == SND_DEVICE_NONE) {
-        ALOGE("%s: Could not get valid output sound device", __func__);
-        ret = -EINVAL;
-        goto error_config;
-    }
-
+    ALOGD("%s: enter: usecase(%d: %s) devices(%#x)",
+          __func__, out->usecase, use_case_table[out->usecase], out->devices);
     out->pcm_device_id = get_pcm_device_id(adev->audio_route,
                                            out->usecase,
                                            PCM_PLAYBACK);
@@ -954,22 +986,15 @@
     uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
     uc_info->id = out->usecase;
     uc_info->type = PCM_PLAYBACK;
-    uc_info->devices = out->devices;
     uc_info->stream.out = out;
-
-    ret = select_devices(adev);
-    if (ret) {
-        ALOGE("%s: Failed to enable device(%#x)",
-              __func__, adev->out_device);
-        free(uc_info);
-        goto error_config;
-    }
-
-    enable_audio_route(adev->audio_route, uc_info);
-    audio_route_update_mixer(adev->audio_route);
+    uc_info->devices = out->devices;
+    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);
 
+    select_devices(adev, out->usecase);
+
     ALOGV("%s: Opening PCM device card_id(%d) device_id(%d)",
           __func__, 0, out->pcm_device_id);
     out->pcm = pcm_open(SOUND_CARD, out->pcm_device_id,
@@ -979,15 +1004,13 @@
         pcm_close(out->pcm);
         out->pcm = NULL;
         ret = -EIO;
-        goto error;
+        goto error_pcm_open;
     }
     ALOGD("%s: exit", __func__);
     return 0;
-error:
+error_pcm_open:
     stop_output_stream(out);
 error_config:
-    adev->out_device = get_active_out_devices(adev, out->usecase);
-    adev->out_device |= get_voice_call_out_device(adev);
     return ret;
 }
 
@@ -1027,15 +1050,15 @@
     }
 
     /* 2. Get and set stream specific mixer controls */
-    disable_audio_route(adev->audio_route, uc_info);
-    audio_route_update_mixer(adev->audio_route);
+    disable_audio_route(adev, uc_info, true);
+
+    /* 3. Disable the rx and tx devices */
+    disable_snd_device(adev, uc_info->out_snd_device, false);
+    disable_snd_device(adev, uc_info->in_snd_device, true);
 
     list_remove(&uc_info->list);
     free(uc_info);
 
-    /* 3. Disable the rx and tx devices */
-    ret = select_devices(adev);
-
     ALOGD("%s: exit: status(%d)", __func__, ret);
     return ret;
 }
@@ -1051,20 +1074,15 @@
     uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
     uc_info->id = USECASE_VOICE_CALL;
     uc_info->type = VOICE_CALL;
-    uc_info->devices = adev->primary_output->devices;
     uc_info->stream.out = adev->primary_output;
-
-    ret = select_devices(adev);
-    if (ret) {
-        free(uc_info);
-        return ret;
-    }
-
-    enable_audio_route(adev->audio_route, uc_info);
-    audio_route_update_mixer(adev->audio_route);
+    uc_info->devices = adev->primary_output->devices;
+    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);
 
+    select_devices(adev, USECASE_VOICE_CALL);
+
     pcm_dev_rx_id = get_pcm_device_id(adev->audio_route, uc_info->id,
                                       PCM_PLAYBACK);
     pcm_dev_tx_id = get_pcm_device_id(adev->audio_route, uc_info->id,
@@ -1211,7 +1229,8 @@
 {
     struct stream_out *out = (struct stream_out *)stream;
     struct audio_device *adev = out->dev;
-    ALOGD("%s: enter: usecase(%d)", __func__, out->usecase);
+    ALOGD("%s: enter: usecase(%d: %s)", __func__,
+          out->usecase, use_case_table[out->usecase]);
     pthread_mutex_lock(&out->lock);
 
     if (!out->standby) {
@@ -1245,8 +1264,8 @@
     int ret, val = 0;
     bool select_new_device = false;
 
-    ALOGD("%s: enter: usecase(%d) kvpairs: %s",
-          __func__, out->usecase, kvpairs);
+    ALOGD("%s: enter: usecase(%d: %s) kvpairs: %s",
+          __func__, out->usecase, use_case_table[out->usecase], kvpairs);
     parms = str_parms_create_str(kvpairs);
     ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value));
     if (ret >= 0) {
@@ -1254,34 +1273,54 @@
         pthread_mutex_lock(&out->lock);
         pthread_mutex_lock(&adev->lock);
 
+        /*
+         * When HDMI cable is unplugged the music playback is paused and
+         * the policy manager sends routing=0. But the audioflinger
+         * continues to write data until standby time (3sec).
+         * As the HDMI core is turned off, the write gets blocked.
+         * Avoid this by routing audio to speaker until standby.
+         */
+        if (out->devices == AUDIO_DEVICE_OUT_AUX_DIGITAL &&
+                val == AUDIO_DEVICE_NONE) {
+            val = AUDIO_DEVICE_OUT_SPEAKER;
+        }
+
+        /*
+         * select_devices() call below switches all the usecases on the same
+         * backend to the new device. Refer to check_usecases_codec_backend() in
+         * the select_devices(). But how do we undo this?
+         *
+         * For example, music playback is active on headset (deep-buffer usecase)
+         * and if we go to ringtones and select a ringtone, low-latency usecase
+         * will be started on headset+speaker. As we can't enable headset+speaker
+         * and headset devices at the same time, select_devices() switches the music
+         * playback to headset+speaker while starting low-lateny usecase for ringtone.
+         * So when the ringtone playback is completed, how do we undo the same?
+         *
+         * We are relying on the out_set_parameters() call on deep-buffer output,
+         * once the ringtone playback is ended.
+         * NOTE: We should not check if the current devices are same as new devices.
+         *       Because select_devices() must be called to switch back the music
+         *       playback to headset.
+         */
         if (val != 0) {
-            /* ToDo: Fix device updation logic */
-            list_for_each(node, &adev->usecase_list) {
-                usecase = node_to_item(node, struct audio_usecase, list);
-                if ((usecase->type == PCM_PLAYBACK || usecase->type == VOICE_CALL)
-                        && usecase->stream.out != out)
-                    usecase->stream.out->devices = val;
+            out->devices = val;
+
+            if (!out->standby)
+                select_devices(adev, out->usecase);
+
+            if ((adev->mode == AUDIO_MODE_IN_CALL) && !adev->in_call &&
+                    (out == adev->primary_output)) {
+                start_voice_call(adev);
+            } else if ((adev->mode == AUDIO_MODE_IN_CALL) && adev->in_call &&
+                       (out == adev->primary_output)) {
+                select_devices(adev, USECASE_VOICE_CALL);
             }
         }
 
-        adev->out_device = get_active_out_devices(adev, out->usecase) | val;
-        if ((adev->mode == AUDIO_MODE_IN_CALL) && !adev->in_call &&
-            (out == adev->primary_output) && (val != 0)) {
-            out->devices = val;
-            start_voice_call(adev);
-        } else if ((adev->mode != AUDIO_MODE_IN_CALL) && adev->in_call &&
-                   (out == adev->primary_output)) {
-            if (val != 0) {
-                out->devices = val;
-            }
+        if ((adev->mode != AUDIO_MODE_IN_CALL) && adev->in_call &&
+                (out == adev->primary_output)) {
             stop_voice_call(adev);
-        } else if ((out->devices != (audio_devices_t)val) && (val != 0)) {
-            /* Update the devices so that select_devices() enables the new devies */
-            out->devices = val;
-            if ((adev->in_call && (out == adev->primary_output)) ||
-                    !out->standby) {
-                ret = select_devices(adev);
-            }
         }
 
         pthread_mutex_unlock(&adev->lock);
@@ -1494,9 +1533,8 @@
         if ((in->device != val) && (val != 0)) {
             in->device = val;
             /* If recording is in progress, change the tx device to new device */
-            if (!in->standby) {
-                ret = select_devices(adev);
-            }
+            if (!in->standby)
+                ret = select_devices(adev, in->usecase);
         }
     }
 
@@ -1526,10 +1564,9 @@
     struct audio_device *adev = in->dev;
     int i, ret = -1;
 
-    //ALOGV("%s: buffer(%p) bytes(%d)", __func__, buffer, bytes);
     pthread_mutex_lock(&in->lock);
     if (in->standby) {
-      pthread_mutex_lock(&adev->lock);
+        pthread_mutex_lock(&adev->lock);
         ret = start_input_stream(in);
         pthread_mutex_unlock(&adev->lock);
         if (ret != 0) {
@@ -1715,7 +1752,7 @@
             adev->tty_mode = tty_mode;
             adev->acdb_settings = (adev->acdb_settings & TTY_MODE_CLEAR) | tty_mode;
             if (adev->in_call)
-                select_devices(adev);
+                select_devices(adev, USECASE_VOICE_CALL);
         }
         pthread_mutex_unlock(&adev->lock);
     }
@@ -1831,6 +1868,7 @@
     struct audio_device *adev = (struct audio_device *)dev;
     int err = 0;
 
+    pthread_mutex_lock(&adev->lock);
     adev->mic_mute = state;
     if (adev->mode == AUDIO_MODE_IN_CALL) {
         if (adev->csd_client) {
@@ -1846,6 +1884,7 @@
             ALOGE("%s: No CSD Client present", __func__);
         }
     }
+    pthread_mutex_unlock(&adev->lock);
     return err;
 }
 
@@ -1965,31 +2004,31 @@
     adev->mic_type_analog = false;
 
     property_get("persist.audio.handset.mic.type",value,"");
-    if (!strncmp("analog", value, 6))
+    if (!strcmp("analog", value))
         adev->mic_type_analog = true;
 
     property_get("persist.audio.dualmic.config",value,"");
-    if (!strncmp("broadside", value, 9)) {
+    if (!strcmp("broadside", value)) {
         adev->dualmic_config = DUALMIC_CONFIG_BROADSIDE;
         adev->acdb_settings |= DMIC_FLAG;
-    } else if (!strncmp("endfire", value, 7)) {
+    } else if (!strcmp("endfire", value)) {
         adev->dualmic_config = DUALMIC_CONFIG_ENDFIRE;
         adev->acdb_settings |= DMIC_FLAG;
     }
 
     if (adev->dualmic_config != DUALMIC_CONFIG_NONE) {
         property_get("persist.audio.fluence.voicecall",value,"");
-        if (!strncmp("true", value, 4)) {
+        if (!strcmp("true", value)) {
             adev->fluence_in_voice_call = true;
         }
 
         property_get("persist.audio.fluence.voicerec",value,"");
-        if (!strncmp("true", value, 4)) {
+        if (!strcmp("true", value)) {
             adev->fluence_in_voice_rec = true;
         }
 
         property_get("persist.audio.fluence.speaker",value,"");
-        if (!strncmp("true", value, 4)) {
+        if (!strcmp("true", value)) {
             adev->fluence_in_spkr_mode = true;
         }
     }
@@ -2056,7 +2095,7 @@
                      hw_device_t **device)
 {
     struct audio_device *adev;
-    int ret;
+    int i, ret;
 
     ALOGD("%s: enter", __func__);
     if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0) return -EINVAL;
@@ -2111,12 +2150,11 @@
     adev->voice_volume = 1.0f;
     adev->tty_mode = TTY_MODE_OFF;
     adev->bluetooth_nrec = true;
-    adev->cur_out_snd_device = 0;
-    adev->cur_in_snd_device = 0;
-    adev->out_snd_device_active = false;
-    adev->in_snd_device_active = false;
     adev->in_call = false;
     adev->acdb_settings = TTY_MODE_OFF;
+    for (i = 0; i < SND_DEVICE_MAX; i++) {
+        adev->snd_dev_ref_cnt[i] = 0;
+    }
     list_init(&adev->usecase_list);
     pthread_mutex_unlock(&adev->lock);
 
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index 9a47389..0432106 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -27,6 +27,15 @@
 #define DUALMIC_CONFIG_ENDFIRE 1
 #define DUALMIC_CONFIG_BROADSIDE 2
 
+/*
+ * Below are the devices for which is back end is same, SLIMBUS_0_RX.
+ * All these devices are handled by the internal HW codec. We can
+ * enable any one of these devices at any time
+ */
+#define AUDIO_DEVICE_OUT_ALL_CODEC_BACKEND \
+    (AUDIO_DEVICE_OUT_EARPIECE | AUDIO_DEVICE_OUT_SPEAKER | \
+     AUDIO_DEVICE_OUT_WIRED_HEADSET | AUDIO_DEVICE_OUT_WIRED_HEADPHONE)
+
 /* Sound devices specific to the platform
  * The DEVICE_OUT_* and DEVICE_IN_* should be mapped to these sound
  * devices to enable corresponding mixer paths
@@ -184,6 +193,8 @@
     audio_usecase_t id;
     usecase_type_t  type;
     audio_devices_t devices;
+    snd_device_t out_snd_device;
+    snd_device_t in_snd_device;
     union stream_ptr stream;
 };
 
@@ -217,10 +228,7 @@
     bool screen_off;
     struct pcm *voice_call_rx;
     struct pcm *voice_call_tx;
-    snd_device_t cur_out_snd_device;
-    snd_device_t cur_in_snd_device;
-    bool out_snd_device_active;
-    bool in_snd_device_active;
+    int snd_dev_ref_cnt[SND_DEVICE_MAX];
     struct listnode usecase_list;
     struct audio_route *audio_route;
     int acdb_settings;