hal: Update keep_alive to support silence playback on non-hdmi devices

Keep_alive can now handle silence playback on multiple concurrent
devices such as primary output devices as well as HDMI

Change-Id: I08cbec51394d23255e1a61928e0b2cad552b2ac0
Signed-off-by: Md Mansoor Ahmed <mansoor@codeaurora.org>
diff --git a/hal/audio_extn/audio_defs.h b/hal/audio_extn/audio_defs.h
old mode 100644
new mode 100755
index 2a81ba1..3921f49
--- a/hal/audio_extn/audio_defs.h
+++ b/hal/audio_extn/audio_defs.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014-2015, 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2015, 2017-2018, The Linux Foundation. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -190,6 +190,13 @@
     int64_t        adjust_time;
 };
 
+/* Device playback mode passed to keep_alive_start & keep_alive_stop*/
+typedef enum {
+    KEEP_ALIVE_OUT_NONE = 0,
+    KEEP_ALIVE_OUT_PRIMARY,
+    KEEP_ALIVE_OUT_HDMI,
+} ka_mode_t;
+
 /* type of asynchronous write callback events. Mutually exclusive
  * event enums append those defined for stream_callback_event_t in audio.h */
 typedef enum {
diff --git a/hal/audio_extn/audio_extn.c b/hal/audio_extn/audio_extn.c
old mode 100644
new mode 100755
index b3ba566..e267e39
--- a/hal/audio_extn/audio_extn.c
+++ b/hal/audio_extn/audio_extn.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
  * Not a Contribution.
  *
  * Copyright (C) 2013 The Android Open Source Project
@@ -833,6 +833,7 @@
    audio_extn_source_track_set_parameters(adev, parms);
    audio_extn_fbsp_set_parameters(parms);
    audio_extn_keep_alive_set_parameters(adev, parms);
+   audio_extn_passthru_set_parameters(adev, parms);
    audio_extn_ext_disp_set_parameters(adev, parms);
    audio_extn_qaf_set_parameters(adev, parms);
    if (adev->offload_effects_set_parameters != NULL)
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
old mode 100644
new mode 100755
index 2239338..f29ca30
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
  * Not a Contribution.
  *
  * Copyright (C) 2013 The Android Open Source Project
@@ -769,15 +769,17 @@
 #endif
 
 #ifndef KEEP_ALIVE_ENABLED
-#define audio_extn_keep_alive_init(a) do {} while(0)
-#define audio_extn_keep_alive_start() do {} while(0)
-#define audio_extn_keep_alive_stop() do {} while(0)
+#define audio_extn_keep_alive_init(adev) do {} while(0)
+#define audio_extn_keep_alive_deinit() do {} while(0)
+#define audio_extn_keep_alive_start(ka_mode) do {} while(0)
+#define audio_extn_keep_alive_stop(ka_mode) do {} while(0)
 #define audio_extn_keep_alive_is_active() (false)
 #define audio_extn_keep_alive_set_parameters(adev, parms) (0)
 #else
 void audio_extn_keep_alive_init(struct audio_device *adev);
-void audio_extn_keep_alive_start();
-void audio_extn_keep_alive_stop();
+void audio_extn_keep_alive_deinit();
+void audio_extn_keep_alive_start(ka_mode_t ka_mode);
+void audio_extn_keep_alive_stop(ka_mode_t ka_mode);
 bool audio_extn_keep_alive_is_active();
 int audio_extn_keep_alive_set_parameters(struct audio_device *adev,
                                          struct str_parms *parms);
diff --git a/hal/audio_extn/keep_alive.c b/hal/audio_extn/keep_alive.c
old mode 100644
new mode 100755
index 698d8d0..0866a35
--- a/hal/audio_extn/keep_alive.c
+++ b/hal/audio_extn/keep_alive.c
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
@@ -55,6 +55,7 @@
 
 typedef enum {
     REQUEST_WRITE,
+    REQUEST_QUIT,
 } request_t;
 
 typedef struct {
@@ -66,8 +67,11 @@
     state_t state;
     struct listnode cmd_list;
     struct pcm *pcm;
+    struct stream_out *out;
+    ka_mode_t prev_mode;
     bool done;
     void * userdata;
+    audio_devices_t active_devices;
 } keep_alive_t;
 
 struct keep_alive_cmd {
@@ -89,31 +93,8 @@
 };
 
 static void * keep_alive_loop(void * context);
-
-void audio_extn_keep_alive_init(struct audio_device *adev)
-{
-    ka.userdata = adev;
-    ka.state = STATE_IDLE;
-    ka.pcm = NULL;
-
-    if (property_get_bool("audio.keep_alive.disabled", false)) {
-        ALOGE("keep alive disabled");
-        ka.state = STATE_DISABLED;
-        return;
-    }
-
-    pthread_mutex_init(&ka.lock, (const pthread_mutexattr_t *) NULL);
-    pthread_cond_init(&ka.cond, (const pthread_condattr_t *) NULL);
-    pthread_cond_init(&ka.wake_up_cond, (const pthread_condattr_t *) NULL);
-    pthread_mutex_init(&ka.sleep_lock, (const pthread_mutexattr_t *) NULL);
-    list_init(&ka.cmd_list);
-    if (pthread_create(&ka.thread,  (const pthread_attr_t *) NULL,
-                       keep_alive_loop, NULL) < 0) {
-        ALOGW("Failed to create keep_alive_thread");
-        /* can continue without keep alive */
-        ka.state = STATE_DEINIT;
-    }
-}
+static int keep_alive_cleanup();
+static int keep_alive_start_l();
 
 static void send_cmd_l(request_t r)
 {
@@ -133,277 +114,275 @@
     pthread_cond_signal(&ka.cond);
 }
 
-static int close_silence_stream()
+void audio_extn_keep_alive_init(struct audio_device *adev)
 {
-    if (!ka.pcm)
-        return -ENODEV;
-
-    pcm_close(ka.pcm);
+    ka.userdata = adev;
+    ka.state = STATE_IDLE;
     ka.pcm = NULL;
-    return 0;
+    if (property_get_bool("audio.keep_alive.disabled", false)) {
+        ALOGE("keep alive disabled");
+        ka.state = STATE_DISABLED;
+        return;
+    }
+    ka.done = false;
+    ka.prev_mode = KEEP_ALIVE_OUT_NONE;
+    ka.active_devices = AUDIO_DEVICE_NONE;
+    pthread_mutex_init(&ka.lock, (const pthread_mutexattr_t *) NULL);
+    pthread_cond_init(&ka.cond, (const pthread_condattr_t *) NULL);
+    pthread_cond_init(&ka.wake_up_cond, (const pthread_condattr_t *) NULL);
+    pthread_mutex_init(&ka.sleep_lock, (const pthread_mutexattr_t *) NULL);
+    list_init(&ka.cmd_list);
+    if (pthread_create(&ka.thread,  (const pthread_attr_t *) NULL,
+                       keep_alive_loop, NULL) < 0) {
+        ALOGW("Failed to create keep_alive_thread");
+        /* can continue without keep alive */
+        ka.state = STATE_DEINIT;
+        return;
+    }
+    ALOGV("%s init done", __func__);
 }
 
-static int open_silence_stream()
+void audio_extn_keep_alive_deinit()
 {
-    unsigned int flags = PCM_OUT|PCM_MONOTONIC;
+    if (ka.state == STATE_DEINIT || ka.state == STATE_DISABLED)
+        return;
+    ka.userdata = NULL;
+    ka.done = true;
+    pthread_mutex_lock(&ka.lock);
+    send_cmd_l(REQUEST_QUIT);
+    pthread_mutex_unlock(&ka.lock);
+    pthread_join(ka.thread, (void **) NULL);
+    pthread_mutex_destroy(&ka.lock);
+    pthread_cond_destroy(&ka.cond);
+    pthread_cond_destroy(&ka.wake_up_cond);
+    pthread_mutex_destroy(&ka.sleep_lock);
+    ALOGV("%s deinit done", __func__);
+}
 
-    if (ka.pcm)
-        return -EEXIST;
-
-    int silence_pcm_dev_id = platform_get_pcm_device_id(USECASE_AUDIO_PLAYBACK_EXT_DISP_SILENCE,
-                                                        PCM_PLAYBACK);
-
-    ALOGD("opening silence device %d", silence_pcm_dev_id);
+audio_devices_t get_device_id_from_mode(ka_mode_t ka_mode)
+{
     struct audio_device * adev = (struct audio_device *)ka.userdata;
+    audio_devices_t out_device = AUDIO_DEVICE_NONE;
+    switch (ka_mode)
+    {
+        case KEEP_ALIVE_OUT_PRIMARY:
+            if (adev->primary_output) {
+                if (adev->primary_output->devices & AUDIO_DEVICE_OUT_ALL)
+                    out_device = adev->primary_output->devices & AUDIO_DEVICE_OUT_ALL;
+                else
+                    out_device = AUDIO_DEVICE_OUT_SPEAKER;
+            }
+            else {
+                out_device = AUDIO_DEVICE_OUT_SPEAKER;
+            }
+            break;
+
+        case KEEP_ALIVE_OUT_HDMI:
+            out_device = AUDIO_DEVICE_OUT_AUX_DIGITAL;
+            break;
+
+        default:
+            out_device = AUDIO_DEVICE_NONE;
+    }
+    return out_device;
+}
+
+void audio_extn_keep_alive_start(ka_mode_t ka_mode)
+{
+    struct audio_device * adev = (struct audio_device *)ka.userdata;
+    audio_devices_t out_devices = AUDIO_DEVICE_NONE;
+
+    pthread_mutex_lock(&ka.lock);
+    ALOGV("%s: mode %x", __func__, ka_mode);
+    if ((ka.state == STATE_DISABLED)||(ka.state == STATE_DEINIT)) {
+        ALOGE(" %s : Unexpected state %x",__func__, ka.state);
+        goto exit;
+    }
+
+    out_devices = get_device_id_from_mode(ka_mode);
+    if ((out_devices == ka.active_devices) && (ka.state == STATE_ACTIVE)) {
+        ALOGV(" %s : Already feeding silence to device %x",__func__, out_devices);
+        ka.prev_mode |= ka_mode;
+        goto exit;
+    }
+    ALOGV(" %s : active devices %x, new device %x",__func__, ka.active_devices, out_devices);
+
+    if (out_devices == AUDIO_DEVICE_NONE)
+        goto exit;
+
+    if (audio_extn_passthru_is_active()) {
+        ka.active_devices &= ~AUDIO_DEVICE_OUT_AUX_DIGITAL;
+        if(ka.active_devices == AUDIO_DEVICE_NONE)
+            goto exit;
+    }
+
+    ka.active_devices |= out_devices;
+    ka.prev_mode |= ka_mode;
+    if (ka.state == STATE_ACTIVE) {
+        ka.out->devices = ka.active_devices;
+        select_devices(adev, USECASE_AUDIO_PLAYBACK_SILENCE);
+    } else if (ka.state == STATE_IDLE) {
+        keep_alive_start_l();
+    }
+
+exit:
+    pthread_mutex_unlock(&ka.lock);
+}
+
+/* must be called with adev lock held */
+static int keep_alive_start_l()
+{
+    struct audio_device * adev = (struct audio_device *)ka.userdata;
+    unsigned int flags = PCM_OUT|PCM_MONOTONIC;
+    struct audio_usecase *usecase;
+    int rc = 0;
+
+    int silence_pcm_dev_id =
+            platform_get_pcm_device_id(USECASE_AUDIO_PLAYBACK_SILENCE,
+                                       PCM_PLAYBACK);
+
+    ka.done = false;
+    usecase = calloc(1, sizeof(struct audio_usecase));
+    if (usecase == NULL) {
+        ALOGE("%s: usecase is NULL", __func__);
+        rc = -ENOMEM;
+        goto exit;
+    }
+
+    ka.out = (struct stream_out *)calloc(1, sizeof(struct stream_out));
+    if (ka.out == NULL) {
+        ALOGE("%s: keep_alive out is NULL", __func__);
+        free(usecase);
+        rc = -ENOMEM;
+        goto exit;
+    }
+
+    ka.out->flags = 0;
+    ka.out->devices = ka.active_devices;
+    ka.out->dev = adev;
+    ka.out->format = AUDIO_FORMAT_PCM_16_BIT;
+    ka.out->sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
+    ka.out->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+    ka.out->supported_channel_masks[0] = AUDIO_CHANNEL_OUT_STEREO;
+    ka.out->config = silence_config;
+
+    usecase->stream.out = ka.out;
+    usecase->type = PCM_PLAYBACK;
+    usecase->id = USECASE_AUDIO_PLAYBACK_SILENCE;
+    usecase->out_snd_device = SND_DEVICE_NONE;
+    usecase->in_snd_device = SND_DEVICE_NONE;
+
+    list_add_tail(&adev->usecase_list, &usecase->list);
+    select_devices(adev, USECASE_AUDIO_PLAYBACK_SILENCE);
+
+    ALOGD("opening pcm device for silence playback %x", silence_pcm_dev_id);
     ka.pcm = pcm_open(adev->snd_card, silence_pcm_dev_id,
                       flags, &silence_config);
-    ALOGD("opened silence device %d", silence_pcm_dev_id);
     if (ka.pcm == NULL || !pcm_is_ready(ka.pcm)) {
         ALOGE("%s: %s", __func__, pcm_get_error(ka.pcm));
         if (ka.pcm != NULL) {
             pcm_close(ka.pcm);
             ka.pcm = NULL;
         }
-        return -1;
+        goto exit;
     }
-    return 0;
+    send_cmd_l(REQUEST_WRITE);
+    while (ka.state != STATE_ACTIVE) {
+        pthread_cond_wait(&ka.cond, &ka.lock);
+    }
+    return rc;
+exit:
+    keep_alive_cleanup();
+    return rc;
 }
 
-
-static int set_mixer_control(struct mixer *mixer,
-                             const char * mixer_ctl_name,
-                             const char *mixer_val)
-{
-    struct mixer_ctl *ctl;
-    if ((mixer == NULL) || (mixer_ctl_name == NULL) || (mixer_val == NULL)) {
-       ALOGE("%s: Invalid input", __func__);
-       return -EINVAL;
-    }
-    ALOGD("setting mixer ctl %s with value %s", mixer_ctl_name, mixer_val);
-    ctl = mixer_get_ctl_by_name(mixer, mixer_ctl_name);
-    if (!ctl) {
-        ALOGE("%s: could not get ctl for mixer cmd - %s",
-              __func__, mixer_ctl_name);
-        return -EINVAL;
-    }
-
-    return mixer_ctl_set_enum_by_string(ctl, mixer_val);
-}
-
-/* must be called with adev lock held */
-void audio_extn_keep_alive_start()
+void audio_extn_keep_alive_stop(ka_mode_t ka_mode)
 {
     struct audio_device * adev = (struct audio_device *)ka.userdata;
-    char mixer_ctl_name[MAX_LENGTH_MIXER_CONTROL_IN_INT];
-    long app_type_cfg[MAX_LENGTH_MIXER_CONTROL_IN_INT];
-    int len = 0, rc;
-    struct mixer_ctl *ctl;
-    int acdb_dev_id, snd_device;
-    struct listnode *node;
-    struct audio_usecase *usecase;
-    int32_t sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
-
+    audio_devices_t out_devices;
     if (ka.state == STATE_DISABLED)
         return;
 
     pthread_mutex_lock(&ka.lock);
 
-    if (ka.state == STATE_DEINIT) {
-        ALOGE(" %s : Invalid state ",__func__);
+    ALOGV("%s: mode %x", __func__, ka_mode);
+    if (ka_mode && (ka.state != STATE_ACTIVE)) {
+        ALOGV(" %s : Can't stop, keep_alive",__func__);
+        ALOGV(" %s : keep_alive is not running on device %x",__func__, get_device_id_from_mode(ka_mode));
+        ka.prev_mode |= ka_mode;
         goto exit;
     }
-
-    if (audio_extn_passthru_is_active()) {
-        ALOGE(" %s : Pass through is already active", __func__);
-        goto exit;
+    out_devices = get_device_id_from_mode(ka_mode);
+    if (ka.prev_mode & ka_mode) {
+        ka.prev_mode &= ~ka_mode;
+        ka.active_devices = get_device_id_from_mode(ka.prev_mode);
     }
 
-    if (ka.state == STATE_ACTIVE) {
-        ALOGV(" %s : Keep alive state is already Active",__func__ );
-        goto exit;
+    if (ka.active_devices == AUDIO_DEVICE_NONE) {
+        keep_alive_cleanup();
+    } else if (ka.out->devices != ka.active_devices){
+        ka.out->devices = ka.active_devices;
+        select_devices(adev, USECASE_AUDIO_PLAYBACK_SILENCE);
     }
-
-    /* Dont start keep_alive if any other PCM session is routed to HDMI*/
-    list_for_each(node, &adev->usecase_list) {
-         usecase = node_to_item(node, struct audio_usecase, list);
-         if (usecase->type == PCM_PLAYBACK &&
-                 usecase->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL)
-             goto exit;
-    }
-
-    ka.done = false;
-
-    /*configure app type */
-    int silence_pcm_dev_id = platform_get_pcm_device_id(USECASE_AUDIO_PLAYBACK_EXT_DISP_SILENCE,
-                                                        PCM_PLAYBACK);
-    snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
-             "Audio Stream %d App Type Cfg", silence_pcm_dev_id);
-
-    ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
-    if (!ctl) {
-        ALOGE("%s: Could not get ctl for mixer cmd - %s", __func__,
-              mixer_ctl_name);
-        rc = -EINVAL;
-        goto exit;
-    }
-
-    /* Configure HDMI/DP Backend with default values, this as well
-     * helps reconfigure HDMI/DP backend after passthrough.
-     */
-    int ext_disp_type = platform_get_ext_disp_type(adev->platform);
-    switch(ext_disp_type) {
-        case EXT_DISPLAY_TYPE_HDMI:
-            snd_device = SND_DEVICE_OUT_HDMI;
-            set_mixer_control(adev->mixer, "HDMI RX Format", "LPCM");
-            set_mixer_control(adev->mixer, "HDMI_RX SampleRate", "KHZ_48");
-            set_mixer_control(adev->mixer, "HDMI_RX Channels", "Two");
-            break;
-        case EXT_DISPLAY_TYPE_DP:
-            snd_device = SND_DEVICE_OUT_DISPLAY_PORT;
-            set_mixer_control(adev->mixer, "Display Port RX Format", "LPCM");
-            set_mixer_control(adev->mixer, "Display Port RX SampleRate", "KHZ_48");
-            set_mixer_control(adev->mixer, "Display Port RX Channels", "Two");
-            break;
-        default:
-            ALOGE("%s: Invalid external display type:%d", __func__, ext_disp_type);
-            goto exit;
-    }
-
-    acdb_dev_id = platform_get_snd_device_acdb_id(snd_device);
-    if (acdb_dev_id < 0) {
-        ALOGE("%s: Couldn't get the acdb dev id", __func__);
-        rc = -EINVAL;
-        goto exit;
-    }
-
-    sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
-    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:%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);
-    mixer_ctl_set_array(ctl, app_type_cfg, len);
-
-    /*send calibration*/
-    usecase = calloc(1, sizeof(struct audio_usecase));
-
-    if (usecase == NULL) {
-        ALOGE("%s: usecase is NULL", __func__);
-        rc = -ENOMEM;
-        goto exit;
-    }
-    usecase->type = PCM_PLAYBACK;
-    usecase->out_snd_device = snd_device;
-
-    platform_send_audio_calibration(adev->platform, usecase,
-                platform_get_default_app_type(adev->platform), sample_rate);
-
-    /*apply audio route */
-    switch(ext_disp_type) {
-        case EXT_DISPLAY_TYPE_HDMI:
-            audio_route_apply_and_update_path(adev->audio_route, "silence-playback hdmi");
-            break;
-        case EXT_DISPLAY_TYPE_DP:
-            audio_route_apply_and_update_path(adev->audio_route, "silence-playback display-port");
-            break;
-        default:
-            ALOGE("%s: Invalid external display type:%d", __func__, ext_disp_type);
-            goto exit;
-    }
-
-    if (open_silence_stream() == 0) {
-        send_cmd_l(REQUEST_WRITE);
-        while (ka.state != STATE_ACTIVE) {
-            pthread_cond_wait(&ka.cond, &ka.lock);
-        }
-    }
-
 exit:
     pthread_mutex_unlock(&ka.lock);
 }
 
 /* must be called with adev lock held */
-void audio_extn_keep_alive_stop()
+static int keep_alive_cleanup()
 {
     struct audio_device * adev = (struct audio_device *)ka.userdata;
+    struct audio_usecase *uc_info;
 
-    if (ka.state == STATE_DISABLED)
-        return;
-
-    pthread_mutex_lock(&ka.lock);
-
-    if ((ka.state == STATE_DEINIT) || (ka.state == STATE_IDLE))
-        goto exit;
+    ka.done = true;
+    if (ka.out != NULL)
+        free(ka.out);
 
     pthread_mutex_lock(&ka.sleep_lock);
-    ka.done = true;
     pthread_cond_signal(&ka.wake_up_cond);
     pthread_mutex_unlock(&ka.sleep_lock);
     while (ka.state != STATE_IDLE) {
         pthread_cond_wait(&ka.cond, &ka.lock);
     }
-    close_silence_stream();
+    ALOGV("%s: keep_alive state changed to %x", __func__, ka.state);
 
-    /*apply audio route */
-    int ext_disp_type = platform_get_ext_disp_type(adev->platform);
-    switch(ext_disp_type) {
-        case EXT_DISPLAY_TYPE_HDMI:
-            audio_route_reset_and_update_path(adev->audio_route, "silence-playback hdmi");
-            break;
-        case EXT_DISPLAY_TYPE_DP:
-            audio_route_reset_and_update_path(adev->audio_route, "silence-playback display-port");
-            break;
-        default:
-            ALOGE("%s: Invalid external display type:%d", __func__, ext_disp_type);
+    uc_info = get_usecase_from_list(adev, USECASE_AUDIO_PLAYBACK_SILENCE);
+    if (uc_info == NULL) {
+        ALOGE("%s: Could not find keep alive usecase in the list", __func__);
+    } else {
+        disable_audio_route(adev, uc_info);
+        disable_snd_device(adev, uc_info->out_snd_device);
+        list_remove(&uc_info->list);
+        free(uc_info);
     }
-
-exit:
-    pthread_mutex_unlock(&ka.lock);
-}
-
-bool audio_extn_keep_alive_is_active()
-{
-    return ka.state == STATE_ACTIVE;
+    pcm_close(ka.pcm);
+    ka.pcm = NULL;
+    ka.active_devices = KEEP_ALIVE_OUT_NONE;
+    return 0;
 }
 
 int audio_extn_keep_alive_set_parameters(struct audio_device *adev __unused,
-                                         struct str_parms *parms)
+                                         struct str_parms *parms __unused)
 {
     char value[32];
-    int ret;
-
+    int ret, pcm_device_id=0;
     if (ka.state == STATE_DISABLED)
         return 0;
 
-    ret = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_CONNECT, value, sizeof(value));
-    if (ret >= 0) {
-        int val = atoi(value);
-        if (audio_is_output_devices(val) &&
-            (val & AUDIO_DEVICE_OUT_AUX_DIGITAL)) {
-            if (!audio_extn_passthru_is_active()) {
-                ALOGV("start keep alive");
-                audio_extn_keep_alive_start();
+    if ((ka.state == STATE_ACTIVE) && (ka.prev_mode & KEEP_ALIVE_OUT_PRIMARY)){
+        ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING,
+                                value, sizeof(value));
+        if (ret >= 0) {
+            pcm_device_id = atoi(value);
+            if(pcm_device_id > 0)
+            {
+                audio_extn_keep_alive_start(KEEP_ALIVE_OUT_PRIMARY);
             }
         }
     }
-
-    ret = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_DISCONNECT, value,
-                            sizeof(value));
-    if (ret >= 0) {
-        int val = atoi(value);
-        if (audio_is_output_devices(val) &&
-            (val & AUDIO_DEVICE_OUT_AUX_DIGITAL)) {
-            ALOGV("stop keep_alive");
-            audio_extn_keep_alive_stop();
-        }
-    }
     return 0;
 }
 
-
 static void * keep_alive_loop(void * context __unused)
 {
     struct keep_alive_cmd *cmd = NULL;
@@ -424,7 +403,11 @@
         cmd = node_to_item(item, struct keep_alive_cmd, node);
         list_remove(item);
 
-        if (cmd->req != REQUEST_WRITE) {
+        if (cmd->req == REQUEST_QUIT) {
+            free(cmd);
+            pthread_mutex_unlock(&ka.lock);
+            break;
+        } else if (cmd->req != REQUEST_WRITE) {
             free(cmd);
             pthread_mutex_unlock(&ka.lock);
             continue;
@@ -432,6 +415,7 @@
 
         free(cmd);
         ka.state = STATE_ACTIVE;
+        ALOGV("%s: state changed to %x", __func__, ka.state);
         pthread_cond_signal(&ka.cond);
         pthread_mutex_unlock(&ka.lock);
 
@@ -462,6 +446,7 @@
         }
         pthread_mutex_lock(&ka.lock);
         ka.state = STATE_IDLE;
+        ALOGV("%s: state changed to %x", __func__, ka.state);
         pthread_cond_signal(&ka.cond);
         pthread_mutex_unlock(&ka.lock);
     }
diff --git a/hal/audio_extn/passthru.c b/hal/audio_extn/passthru.c
old mode 100644
new mode 100755
index f8974c7..99949fc
--- a/hal/audio_extn/passthru.c
+++ b/hal/audio_extn/passthru.c
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
@@ -309,8 +309,8 @@
     }
 
     if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
-        ALOGI("passthru on aux digital, start keep alive");
-        audio_extn_keep_alive_start();
+        ALOGD("%s: passthru on aux digital, start keep alive", __func__);
+        audio_extn_keep_alive_start(KEEP_ALIVE_OUT_HDMI);
     }
 }
 
@@ -321,8 +321,30 @@
 }
 
 int audio_extn_passthru_set_parameters(struct audio_device *adev __unused,
-                                       struct str_parms *parms __unused)
+                                       struct str_parms *parms)
 {
+    char value[32];
+    int ret;
+    ret = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_CONNECT, value, sizeof(value));
+    if (ret >= 0) {
+        int val = atoi(value);
+        if (val & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
+            if (!audio_extn_passthru_is_active()) {
+                ALOGV("%s: start keep alive on aux digital", __func__);
+                audio_extn_keep_alive_start(KEEP_ALIVE_OUT_HDMI);
+            }
+        }
+    }
+
+    ret = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_DISCONNECT, value,
+                            sizeof(value));
+    if (ret >= 0) {
+        int val = atoi(value);
+        if (val & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
+            ALOGV("%s: stop keep_alive on aux digital on device", __func__);
+            audio_extn_keep_alive_stop(KEEP_ALIVE_OUT_HDMI);
+        }
+    }
     return 0;
 }
 
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
old mode 100644
new mode 100755
index 0444766..1a8f0a1
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -334,7 +334,7 @@
 
     [USECASE_AUDIO_PLAYBACK_AFE_PROXY] = "afe-proxy-playback",
     [USECASE_AUDIO_RECORD_AFE_PROXY] = "afe-proxy-record",
-    [USECASE_AUDIO_PLAYBACK_EXT_DISP_SILENCE] = "silence-playback",
+    [USECASE_AUDIO_PLAYBACK_SILENCE] = "silence-playback",
 
     /* Transcode loopback cases */
     [USECASE_AUDIO_TRANSCODE_LOOPBACK] = "audio-transcode-loopback",
@@ -2879,7 +2879,7 @@
 
     /* Must be called after removing the usecase from list */
     if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL)
-        audio_extn_keep_alive_start();
+        audio_extn_keep_alive_start(KEEP_ALIVE_OUT_HDMI);
 
     if (out->ip_hdlr_handle) {
         ret = audio_extn_ip_hdlr_intf_close(out->ip_hdlr_handle, true, out);
@@ -2973,7 +2973,7 @@
                                  adev->perf_lock_opts_size);
 
     if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
-        audio_extn_keep_alive_stop();
+        audio_extn_keep_alive_stop(KEEP_ALIVE_OUT_HDMI);
         if (audio_extn_passthru_is_enabled() &&
             audio_extn_passthru_is_passthrough_stream(out)) {
             audio_extn_passthru_on_start(out);
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
old mode 100644
new mode 100755
index 7d1888f..0dc9115
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
  * Not a contribution.
  *
  * Copyright (C) 2013 The Android Open Source Project
@@ -187,7 +187,7 @@
     USECASE_AUDIO_PLAYBACK_AFE_PROXY,
     USECASE_AUDIO_RECORD_AFE_PROXY,
 
-    USECASE_AUDIO_PLAYBACK_EXT_DISP_SILENCE,
+    USECASE_AUDIO_PLAYBACK_SILENCE,
 
     USECASE_AUDIO_TRANSCODE_LOOPBACK,
 
diff --git a/hal/msm8916/platform.c b/hal/msm8916/platform.c
old mode 100644
new mode 100755
index 2ee54ee..930c5d4
--- a/hal/msm8916/platform.c
+++ b/hal/msm8916/platform.c
@@ -385,7 +385,7 @@
                                           AFE_PROXY_RECORD_PCM_DEVICE},
     [USECASE_AUDIO_RECORD_AFE_PROXY] = {AFE_PROXY_PLAYBACK_PCM_DEVICE,
                                         AFE_PROXY_RECORD_PCM_DEVICE},
-    [USECASE_AUDIO_PLAYBACK_EXT_DISP_SILENCE] = {MULTIMEDIA9_PCM_DEVICE, -1},
+    [USECASE_AUDIO_PLAYBACK_SILENCE] = {MULTIMEDIA9_PCM_DEVICE, -1},
     [USECASE_AUDIO_TRANSCODE_LOOPBACK] = {TRANSCODE_LOOPBACK_RX_DEV_ID, TRANSCODE_LOOPBACK_TX_DEV_ID},
     [USECASE_AUDIO_PLAYBACK_VOIP] = {AUDIO_PLAYBACK_VOIP_PCM_DEVICE, AUDIO_PLAYBACK_VOIP_PCM_DEVICE},
     [USECASE_AUDIO_RECORD_VOIP] = {AUDIO_RECORD_VOIP_PCM_DEVICE, AUDIO_RECORD_VOIP_PCM_DEVICE},
@@ -910,7 +910,7 @@
     {TO_NAME_INDEX(USECASE_AUDIO_HFP_SCO)},
     {TO_NAME_INDEX(USECASE_AUDIO_HFP_SCO_WB)},
     {TO_NAME_INDEX(USECASE_AUDIO_SPKR_CALIB_TX)},
-    {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_EXT_DISP_SILENCE)},
+    {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_SILENCE)},
     {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_FM)},
     {TO_NAME_INDEX(USECASE_AUDIO_RECORD_FM_VIRTUAL)},
     {TO_NAME_INDEX(USECASE_AUDIO_SPKR_CALIB_RX)},
@@ -2661,6 +2661,9 @@
         }
     }
 
+    /* Initialize keep alive for HDMI/loopback silence */
+    audio_extn_keep_alive_init(adev);
+
     ret = audio_extn_utils_get_codec_version(snd_card_name,
                                              my_data->adev->snd_card,
                                              my_data->codec_version);
@@ -2696,6 +2699,8 @@
 {
     struct platform_data *my_data = (struct platform_data *)platform;
 
+    audio_extn_keep_alive_deinit();
+
     if (my_data->edid_info) {
         free(my_data->edid_info);
         my_data->edid_info = NULL;
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
old mode 100644
new mode 100755
index b708a48..da2ccb6
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -367,7 +367,7 @@
                                           AFE_PROXY_RECORD_PCM_DEVICE},
     [USECASE_AUDIO_RECORD_AFE_PROXY] = {AFE_PROXY_PLAYBACK_PCM_DEVICE,
                                         AFE_PROXY_RECORD_PCM_DEVICE},
-    [USECASE_AUDIO_PLAYBACK_EXT_DISP_SILENCE] = {MULTIMEDIA9_PCM_DEVICE, -1},
+    [USECASE_AUDIO_PLAYBACK_SILENCE] = {MULTIMEDIA9_PCM_DEVICE, -1},
     [USECASE_AUDIO_TRANSCODE_LOOPBACK] = {TRANSCODE_LOOPBACK_RX_DEV_ID, TRANSCODE_LOOPBACK_TX_DEV_ID},
 
     [USECASE_AUDIO_PLAYBACK_VOIP] = {AUDIO_PLAYBACK_VOIP_PCM_DEVICE, AUDIO_PLAYBACK_VOIP_PCM_DEVICE},
@@ -894,7 +894,7 @@
     {TO_NAME_INDEX(USECASE_AUDIO_SPKR_CALIB_TX)},
     {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_AFE_PROXY)},
     {TO_NAME_INDEX(USECASE_AUDIO_RECORD_AFE_PROXY)},
-    {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_EXT_DISP_SILENCE)},
+    {TO_NAME_INDEX(USECASE_AUDIO_PLAYBACK_SILENCE)},
 };
 
 #define NO_COLS 2
@@ -2496,6 +2496,9 @@
     if (property_get_bool("vendor.audio.apptype.multirec.enabled", false))
         my_data->use_generic_handset = true;
 
+    /* Initialize keep alive for HDMI/loopback silence */
+    audio_extn_keep_alive_init(adev);
+
     my_data->edid_info = NULL;
     free(snd_card_name);
     free(snd_card_name_t);
@@ -2506,6 +2509,8 @@
 {
     struct platform_data *my_data = (struct platform_data *)platform;
 
+    audio_extn_keep_alive_deinit();
+
     if (my_data->edid_info) {
         free(my_data->edid_info);
         my_data->edid_info = NULL;