Merge "audio: enable compress passthrough"
diff --git a/configs/msm8996/audio_output_policy.conf b/configs/msm8996/audio_output_policy.conf
index 33aba71..2f01bc9 100644
--- a/configs/msm8996/audio_output_policy.conf
+++ b/configs/msm8996/audio_output_policy.conf
@@ -39,6 +39,13 @@
     bit_width 16
     app_type 69936
   }
+  compress_passthrough {
+    flags AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD|AUDIO_OUTPUT_FLAG_NON_BLOCKING|AUDIO_OUTPUT_FLAG_COMPRESS_PASSTHROUGH
+    formats AUDIO_FORMAT_AC3|AUDIO_FORMAT_E_AC3|AUDIO_FORMAT_E_AC3_JOC|AUDIO_FORMAT_DTS|AUDIO_FORMAT_DTS_HD
+    sampling_rates 32000|44100|48000|88200|96000|176400|192000
+    bit_width 16|24
+    app_type 69941
+  }
   compress_offload_16 {
     flags AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD|AUDIO_OUTPUT_FLAG_NON_BLOCKING
     formats AUDIO_FORMAT_MP3|AUDIO_FORMAT_PCM_16_BIT_OFFLOAD|AUDIO_FORMAT_PCM_24_BIT_OFFLOAD|AUDIO_FORMAT_FLAC|AUDIO_FORMAT_ALAC|AUDIO_FORMAT_APE|AUDIO_FORMAT_AAC_LC|AUDIO_FORMAT_AAC_HE_V1|AUDIO_FORMAT_AAC_HE_V2|AUDIO_FORMAT_WMA|AUDIO_FORMAT_WMA_PRO|AUDIO_FORMAT_VORBIS|AUDIO_FORMAT_AAC_ADTS_LC|AUDIO_FORMAT_AAC_ADTS_HE_V1|AUDIO_FORMAT_AAC_ADTS_HE_V2
diff --git a/configs/msm8996/audio_policy.conf b/configs/msm8996/audio_policy.conf
index bfb2380..eaf896f 100644
--- a/configs/msm8996/audio_policy.conf
+++ b/configs/msm8996/audio_policy.conf
@@ -43,6 +43,13 @@
          devices AUDIO_DEVICE_OUT_SPEAKER|AUDIO_DEVICE_OUT_EARPIECE|AUDIO_DEVICE_OUT_WIRED_HEADSET|AUDIO_DEVICE_OUT_WIRED_HEADPHONE|AUDIO_DEVICE_OUT_LINE|AUDIO_DEVICE_OUT_ALL_SCO|AUDIO_DEVICE_OUT_AUX_DIGITAL|AUDIO_DEVICE_OUT_PROXY|AUDIO_DEVICE_OUT_FM
          flags AUDIO_OUTPUT_FLAG_DEEP_BUFFER
       }
+      compress_passthrough {
+        sampling_rates dynamic
+        channel_masks dynamic
+        formats dynamic
+        devices AUDIO_DEVICE_OUT_AUX_DIGITAL
+        flags AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD|AUDIO_OUTPUT_FLAG_NON_BLOCKING
+      }
       multichannel {
         sampling_rates 8000|11025|16000|22050|32000|44100|48000|64000|88200|96000|128000|176400|192000
         channel_masks dynamic
diff --git a/configs/msm8996/msm8996.mk b/configs/msm8996/msm8996.mk
index a4336d7..a71ed38 100644
--- a/configs/msm8996/msm8996.mk
+++ b/configs/msm8996/msm8996.mk
@@ -18,6 +18,7 @@
 AUDIO_FEATURE_ENABLED_FLUENCE := true
 AUDIO_FEATURE_ENABLED_HDMI_SPK := true
 AUDIO_FEATURE_ENABLED_HDMI_EDID := true
+AUDIO_FEATURE_ENABLED_HDMI_PASSTHROUGH := true
 AUDIO_FEATURE_ENABLED_HFP := true
 AUDIO_FEATURE_ENABLED_INCALL_MUSIC := false
 AUDIO_FEATURE_ENABLED_MULTI_VOICE_SESSIONS := true
@@ -134,6 +135,10 @@
 audio.dolby.ds2.enabled=true\
 audio.dolby.ds2.hardbypass=true
 
+#Enable compress passthrough session
+PRODUCT_PROPERTY_OVERRIDES += \
+audio.offload.passthrough=true
+
 #Disable Multiple offload sesison
 PRODUCT_PROPERTY_OVERRIDES += \
 audio.offload.multiple.enabled=false
diff --git a/hal/Android.mk b/hal/Android.mk
index c1e122d..9888396 100644
--- a/hal/Android.mk
+++ b/hal/Android.mk
@@ -227,6 +227,12 @@
 
 ifeq ($(strip $(AUDIO_FEATURE_ENABLED_HDMI_PASSTHROUGH)),true)
     LOCAL_CFLAGS += -DHDMI_PASSTHROUGH_ENABLED
+    LOCAL_SRC_FILES += audio_extn/passthru.c
+endif
+
+ifeq ($(strip $(AUDIO_FEATURE_ENABLED_KEEP_ALIVE)),true)
+    LOCAL_CFLAGS += -DKEEP_ALIVE_ENABLED
+    LOCAL_SRC_FILES += audio_extn/keep_alive.c
 endif
 
 ifeq ($(strip $(AUDIO_FEATURE_ENABLED_SOURCE_TRACKING)),true)
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index 8980026..91d7c0a 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -400,31 +400,48 @@
 #endif
 
 #ifndef HDMI_PASSTHROUGH_ENABLED
-#define audio_extn_dolby_update_passt_formats(adev, out)                   (0)
 #define audio_extn_dolby_update_passt_stream_configuration(adev, out)      (0)
 #define audio_extn_dolby_is_passt_convert_supported(adev, out)             (0)
 #define audio_extn_dolby_is_passt_supported(adev, out)                     (0)
-#define audio_extn_dolby_is_passthrough_stream(flags)                      (0)
-#define audio_extn_dolby_set_hdmi_config(adev, out)                        (0)
+#define audio_extn_dolby_is_passthrough_stream(out)                        (0)
 #define audio_extn_dolby_get_passt_buffer_size(info)                       (0)
 #define audio_extn_dolby_set_passt_volume(out, mute)                       (0)
 #define audio_extn_dolby_set_passt_latency(out, latency)                   (0)
-#define AUDIO_OUTPUT_FLAG_COMPRESS_PASSTHROUGH  0x4000
+#define audio_extn_passthru_is_supported_format(f) (0)
+#define audio_extn_passthru_should_drop_data(o) (0)
+#define audio_extn_passthru_on_start(o) do {} while(0)
+#define audio_extn_passthru_on_stop(o) do {} while(0)
+#define audio_extn_passthru_on_pause(o) do {} while(0)
+#define audio_extn_passthru_is_enabled() (0)
+#define audio_extn_passthru_is_active() (0)
+#define audio_extn_passthru_set_parameters(a, p) (-ENOSYS)
+#define audio_extn_passthru_init(a) do {} while(0)
+#define audio_extn_passthru_should_standby(o) (1)
+
+#define AUDIO_OUTPUT_FLAG_COMPRESS_PASSTHROUGH  0x1000
 #else
-int audio_extn_dolby_update_passt_formats(struct audio_device *adev,
-                                          struct stream_out *out);
 bool audio_extn_dolby_is_passt_convert_supported(struct audio_device *adev,
                                                  struct stream_out *out);
 bool audio_extn_dolby_is_passt_supported(struct audio_device *adev,
                                          struct stream_out *out);
 void audio_extn_dolby_update_passt_stream_configuration(struct audio_device *adev,
                                                  struct stream_out *out);
-bool audio_extn_dolby_is_passthrough_stream(int flags);
-int audio_extn_dolby_set_hdmi_config(struct audio_device *adev,
-                                     struct stream_out *out);
+bool audio_extn_dolby_is_passthrough_stream(struct stream_out *out);
 int audio_extn_dolby_get_passt_buffer_size(audio_offload_info_t* info);
 int audio_extn_dolby_set_passt_volume(struct stream_out *out, int mute);
 int audio_extn_dolby_set_passt_latency(struct stream_out *out, int latency);
+bool audio_extn_passthru_is_supported_format(audio_format_t format);
+bool audio_extn_passthru_should_drop_data(struct stream_out * out);
+void audio_extn_passthru_on_start(struct stream_out *out);
+void audio_extn_passthru_on_stop(struct stream_out *out);
+void audio_extn_passthru_on_pause(struct stream_out *out);
+int audio_extn_passthru_set_parameters(struct audio_device *adev,
+                                       struct str_parms *parms);
+bool audio_extn_passthru_is_enabled();
+bool audio_extn_passthru_is_active();
+void audio_extn_passthru_init(struct audio_device *adev);
+bool audio_extn_passthru_should_standby(struct stream_out *out);
+
 #endif
 
 #ifndef HFP_ENABLED
@@ -541,4 +558,21 @@
 #else
 void audio_utils_set_hdmi_channel_status(struct stream_out *out, char * buffer, size_t bytes);
 #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_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();
+bool audio_extn_keep_alive_is_active();
+int audio_extn_keep_alive_set_parameters(struct audio_device *adev,
+                                         struct str_parms *parms);
+#endif
+
+
 #endif /* AUDIO_EXTN_H */
diff --git a/hal/audio_extn/dolby.c b/hal/audio_extn/dolby.c
index 115057c..ad8f8a4 100644
--- a/hal/audio_extn/dolby.c
+++ b/hal/audio_extn/dolby.c
@@ -408,22 +408,6 @@
 #endif /* DS1_DOLBY_DDP_ENABLED || DS2_DOLBY_DAP_ENABLED */
 
 #ifdef HDMI_PASSTHROUGH_ENABLED
-int audio_extn_dolby_update_passt_formats(struct audio_device *adev,
-                                          struct stream_out *out) {
-    int32_t i = 0, ret = -ENOSYS;
-
-    if (platform_is_edid_supported_format(adev->platform, AUDIO_FORMAT_AC3) ||
-        platform_is_edid_supported_format(adev->platform, AUDIO_FORMAT_E_AC3)) {
-        out->supported_formats[i++] = AUDIO_FORMAT_AC3;
-        out->supported_formats[i++] = AUDIO_FORMAT_E_AC3;
-        /* Reciever must support JOC and advertise, otherwise JOC is treated as DDP */
-        out->supported_formats[i++] = AUDIO_FORMAT_E_AC3_JOC;
-        ret = 0;
-    }
-    ALOGV("%s: ret = %d", __func__, ret);
-    return ret;
-}
-
 bool audio_extn_dolby_is_passt_convert_supported(struct audio_device *adev,
                                                  struct stream_out *out) {
 
@@ -494,18 +478,38 @@
     }
 }
 
-bool audio_extn_dolby_is_passthrough_stream(int flags) {
+bool audio_extn_dolby_is_passthrough_stream(struct stream_out *out) {
 
-    if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_PASSTHROUGH)
-        return true;
+    //check passthrough system property
+    if (!property_get_bool("audio.offload.passthrough", false)) {
+        return false;
+    }
+
+    //check supported device, currently only on HDMI.
+    if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
+        //passthrough flag
+        if (out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_PASSTHROUGH)
+            return true;
+        //direct flag, check supported formats.
+        if (out->flags & AUDIO_OUTPUT_FLAG_DIRECT) {
+            if (audio_extn_passthru_is_supported_format(out->format)) {
+                if (platform_is_edid_supported_format(out->dev->platform,
+                        out->format)) {
+                    return true;
+                } else if (audio_extn_is_dolby_format(out->format) &&
+                            platform_is_edid_supported_format(out->dev->platform,
+                                AUDIO_FORMAT_AC3)){
+                    //return true for EAC3/EAC3_JOC formats
+                    //if sink supports only AC3
+                    return true;
+                }
+            }
+        }
+    }
+
     return false;
 }
 
-int audio_extn_dolby_set_hdmi_config(struct audio_device *adev,
-                                                    struct stream_out *out) {
-    return platform_set_hdmi_config(out);
-}
-
 int audio_extn_dolby_get_passt_buffer_size(audio_offload_info_t* info) {
     return platform_get_compress_passthrough_buffer_size(info);
 }
diff --git a/hal/audio_extn/keep_alive.c b/hal/audio_extn/keep_alive.c
new file mode 100644
index 0000000..ab663ef
--- /dev/null
+++ b/hal/audio_extn/keep_alive.c
@@ -0,0 +1,293 @@
+/*
+* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above
+*       copyright notice, this list of conditions and the following
+*       disclaimer in the documentation and/or other materials provided
+*       with the distribution.
+*     * Neither the name of The Linux Foundation nor the names of its
+*       contributors may be used to endorse or promote products derived
+*       from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define LOG_TAG "keep_alive"
+/*#define LOG_NDEBUG 0*/
+#include <stdlib.h>
+#include <cutils/log.h>
+#include "audio_hw.h"
+#include "audio_extn.h"
+#include "platform_api.h"
+#include <platform.h>
+
+#define SILENCE_MIXER_PATH "silence-playback hdmi"
+#define SILENCE_DEV_ID 5            /* index into machine driver */
+#define SILENCE_INTERVAL_US 2000000
+
+typedef enum {
+    STATE_DEINIT = -1,
+    STATE_IDLE,
+    STATE_ACTIVE,
+} state_t;
+
+typedef enum {
+    REQUEST_WRITE,
+} request_t;
+
+typedef struct {
+    pthread_mutex_t lock;
+    pthread_cond_t  cond;
+    pthread_t thread;
+    state_t state;
+    struct listnode cmd_list;
+    struct pcm *pcm;
+    bool done;
+    void * userdata;
+} keep_alive_t;
+
+struct keep_alive_cmd {
+    struct listnode node;
+    request_t req;
+};
+
+static keep_alive_t ka;
+
+static struct pcm_config silence_config = {
+    .channels = 2,
+    .rate = DEFAULT_OUTPUT_SAMPLING_RATE,
+    .period_size = DEEP_BUFFER_OUTPUT_PERIOD_SIZE,
+    .period_count = DEEP_BUFFER_OUTPUT_PERIOD_COUNT,
+    .format = PCM_FORMAT_S16_LE,
+    .start_threshold = DEEP_BUFFER_OUTPUT_PERIOD_SIZE / 4,
+    .stop_threshold = INT_MAX,
+    .avail_min = DEEP_BUFFER_OUTPUT_PERIOD_SIZE / 4,
+};
+
+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;
+    pthread_mutex_init(&ka.lock, (const pthread_mutexattr_t *) NULL);
+    pthread_cond_init(&ka.cond, (const pthread_condattr_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 void send_cmd_l(request_t r)
+{
+    if (ka.state == STATE_DEINIT)
+        return;
+
+    struct keep_alive_cmd *cmd =
+        (struct keep_alive_cmd *)calloc(1, sizeof(struct keep_alive_cmd));
+
+    cmd->req = r;
+    list_add_tail(&ka.cmd_list, &cmd->node);
+    pthread_cond_signal(&ka.cond);
+}
+
+static int close_silence_stream()
+{
+    if (!ka.pcm)
+        return -ENODEV;
+
+    pcm_close(ka.pcm);
+    ka.pcm = NULL;
+    return 0;
+}
+
+static int open_silence_stream()
+{
+    unsigned int flags = PCM_OUT|PCM_MONOTONIC;
+
+    if (ka.pcm)
+        return -EEXIST;
+
+    ALOGD("opening silence device %d", SILENCE_DEV_ID);
+    struct audio_device * adev = (struct audio_device *)ka.userdata;
+    ka.pcm = pcm_open(adev->snd_card, SILENCE_DEV_ID,
+                      flags, &silence_config);
+    ALOGD("opened silence device %d", SILENCE_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;
+    }
+    return 0;
+}
+
+/* must be called with adev lock held */
+void audio_extn_keep_alive_start()
+{
+    struct audio_device * adev = (struct audio_device *)ka.userdata;
+
+    if (ka.state == STATE_DEINIT)
+        return;
+
+    if (audio_extn_passthru_is_active())
+        return;
+
+    pthread_mutex_lock(&ka.lock);
+
+    if (ka.state == STATE_ACTIVE)
+        goto exit;
+
+    ka.done = false;
+    //todo: platform_send_audio_calibration is replaced by audio_extn_utils_send_audio_calibration
+    //check why audio cal needs to be set
+    //platform_send_audio_calibration(adev->platform, SND_DEVICE_OUT_HDMI);
+    audio_route_apply_and_update_path(adev->audio_route, SILENCE_MIXER_PATH);
+
+    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()
+{
+    struct audio_device * adev = (struct audio_device *)ka.userdata;
+
+    if (ka.state == STATE_DEINIT)
+        return;
+
+    pthread_mutex_lock(&ka.lock);
+
+    if (ka.state == STATE_IDLE)
+        goto exit;
+
+    ka.done = true;
+    while (ka.state != STATE_IDLE) {
+        pthread_cond_wait(&ka.cond, &ka.lock);
+    }
+    close_silence_stream();
+    audio_route_reset_and_update_path(adev->audio_route, SILENCE_MIXER_PATH);
+
+exit:
+    pthread_mutex_unlock(&ka.lock);
+}
+
+bool audio_extn_keep_alive_is_active()
+{
+    return ka.state == STATE_ACTIVE;
+}
+
+int audio_extn_keep_alive_set_parameters(struct audio_device *adev __unused,
+                                         struct str_parms *parms)
+{
+    char value[32];
+    int ret;
+
+    ret = str_parms_get_str(parms, "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("start keep alive");
+                audio_extn_keep_alive_start();
+            }
+        }
+    }
+
+    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("stop keep_alive");
+            audio_extn_keep_alive_stop();
+        }
+    }
+    return 0;
+}
+
+
+static void * keep_alive_loop(void * context __unused)
+{
+    struct audio_device *adev = (struct audio_device *)ka.userdata;
+    struct keep_alive_cmd *cmd = NULL;
+    struct listnode *item;
+    uint8_t * silence = NULL;
+    int32_t bytes = 0, count = 0, i;
+    struct stream_out * p_out = NULL;
+
+    while (true) {
+        pthread_mutex_lock(&ka.lock);
+        if (list_empty(&ka.cmd_list)) {
+            pthread_cond_wait(&ka.cond, &ka.lock);
+            pthread_mutex_unlock(&ka.lock);
+            continue;
+        }
+
+        item = list_head(&ka.cmd_list);
+        cmd = node_to_item(item, struct keep_alive_cmd, node);
+        list_remove(item);
+
+        if (cmd->req != REQUEST_WRITE) {
+            free(cmd);
+            pthread_mutex_unlock(&ka.lock);
+            continue;
+        }
+
+        free(cmd);
+        ka.state = STATE_ACTIVE;
+        pthread_cond_signal(&ka.cond);
+        pthread_mutex_unlock(&ka.lock);
+
+        if (!silence) {
+            /* 50 ms */
+            bytes =
+                (silence_config.rate * silence_config.channels * sizeof(int16_t)) / 20;
+            silence = (uint8_t *)calloc(1, bytes);
+        }
+
+        while (!ka.done) {
+            ALOGV("write %d bytes of silence", bytes);
+            pcm_write(ka.pcm, (void *)silence, bytes);
+            /* This thread does not have to write silence continuously.
+             * Just something to keep the connection alive is sufficient.
+             * Hence a short burst of silence periodically.
+             */
+            usleep(SILENCE_INTERVAL_US);
+        }
+
+        pthread_mutex_lock(&ka.lock);
+        ka.state = STATE_IDLE;
+        pthread_cond_signal(&ka.cond);
+        pthread_mutex_unlock(&ka.lock);
+    }
+    return 0;
+}
diff --git a/hal/audio_extn/passthru.c b/hal/audio_extn/passthru.c
new file mode 100644
index 0000000..005b74c
--- /dev/null
+++ b/hal/audio_extn/passthru.c
@@ -0,0 +1,186 @@
+/*
+* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above
+*       copyright notice, this list of conditions and the following
+*       disclaimer in the documentation and/or other materials provided
+*       with the distribution.
+*     * Neither the name of The Linux Foundation nor the names of its
+*       contributors may be used to endorse or promote products derived
+*       from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define LOG_TAG "passthru"
+/*#define LOG_NDEBUG 0*/
+#include <stdlib.h>
+#include <cutils/atomic.h>
+#include <cutils/str_parms.h>
+#include <cutils/log.h>
+#include "audio_hw.h"
+#include "audio_extn.h"
+#include "platform_api.h"
+#include <platform.h>
+
+static const audio_format_t audio_passthru_formats[] = {
+    AUDIO_FORMAT_AC3,
+    AUDIO_FORMAT_E_AC3,
+    AUDIO_FORMAT_DTS,
+    AUDIO_FORMAT_DTS_HD
+};
+
+/*
+ * This atomic var is incremented/decremented by the offload stream to notify
+ * other pcm playback streams that a pass thru session is about to start or has
+ * finished. This hint can be used by the other streams to move to standby or
+ * start calling pcm_write respectively.
+ * This behavior is necessary as the DSP backend can only be configured to one
+ * of PCM or compressed.
+ */
+static volatile int32_t compress_passthru_active;
+
+bool audio_extn_passthru_is_supported_format(audio_format_t format)
+{
+    int32_t num_passthru_formats = sizeof(audio_passthru_formats) /
+                                    sizeof(audio_passthru_formats[0]);
+    int32_t i;
+
+    for (i = 0; i < num_passthru_formats; i++) {
+        if (format == audio_passthru_formats[i]) {
+            return true;
+        }
+    }
+    return false;
+}
+
+/*
+ * must be called with stream lock held
+ * This function decides based on some rules whether the data
+ * coming on stream out must be rendered or dropped.
+ */
+bool audio_extn_passthru_should_drop_data(struct stream_out * out)
+{
+    /* Make this product specific */
+    if (!(out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL)) {
+        ALOGI("drop data as end device 0x%x is unsupported", out->devices);
+        return true;
+    }
+
+    if (out->usecase != USECASE_AUDIO_PLAYBACK_OFFLOAD) {
+        if (android_atomic_acquire_load(&compress_passthru_active) > 0) {
+            ALOGI("drop data as pass thru is active");
+            return true;
+        }
+    }
+
+    return false;
+}
+
+/* called with adev lock held */
+void audio_extn_passthru_on_start(struct stream_out * out)
+{
+
+    uint64_t max_period_us = 0;
+    uint64_t temp;
+    struct audio_usecase * usecase;
+    struct listnode *node;
+    struct stream_out * o;
+    struct audio_device *adev = out->dev;
+
+    if (android_atomic_acquire_load(&compress_passthru_active) > 0) {
+        ALOGI("pass thru is already active");
+        return;
+    }
+
+    ALOGV("inc pass thru count to notify other streams");
+    android_atomic_inc(&compress_passthru_active);
+
+    ALOGV("keep_alive_stop");
+    audio_extn_keep_alive_stop();
+
+    while (true) {
+        /* find max period time among active playback use cases */
+        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) {
+                o = usecase->stream.out;
+                temp = o->config.period_size * 1000000LL / o->sample_rate;
+                if (temp > max_period_us)
+                    max_period_us = temp;
+            }
+        }
+
+        if (max_period_us) {
+            pthread_mutex_unlock(&adev->lock);
+            usleep(2*max_period_us);
+            max_period_us = 0;
+            pthread_mutex_lock(&adev->lock);
+        } else
+            break;
+    }
+}
+
+/* called with adev lock held */
+void audio_extn_passthru_on_stop(struct stream_out * out)
+{
+    struct audio_device * adev = out->dev;
+    if (android_atomic_acquire_load(&compress_passthru_active) > 0) {
+        /*
+         * its possible the count is already zero if pause was called before
+         * stop output stream
+         */
+        android_atomic_dec(&compress_passthru_active);
+    }
+
+    if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
+        ALOGI("passthru on aux digital, start keep alive");
+        audio_extn_keep_alive_start();
+    }
+}
+
+void audio_extn_passthru_on_pause(struct stream_out * out __unused)
+{
+    if (android_atomic_acquire_load(&compress_passthru_active) == 0)
+        return;
+
+    android_atomic_dec(&compress_passthru_active);
+}
+
+int audio_extn_passthru_set_parameters(struct audio_device *adev __unused,
+                                       struct str_parms *parms __unused)
+{
+    return 0;
+}
+
+bool audio_extn_passthru_is_active()
+{
+    return android_atomic_acquire_load(&compress_passthru_active) > 0;
+}
+
+bool audio_extn_passthru_is_enabled() { return true; }
+
+void audio_extn_passthru_init(struct audio_device *adev __unused)
+{
+}
+
+bool audio_extn_passthru_should_standby(struct stream_out * out __unused)
+{
+    return true;
+}
diff --git a/hal/audio_extn/utils.c b/hal/audio_extn/utils.c
index d0c2d1a..b3ba2b5 100644
--- a/hal/audio_extn/utils.c
+++ b/hal/audio_extn/utils.c
@@ -653,6 +653,27 @@
                platform_get_default_app_type(adev->platform),
                acdb_dev_id, sample_rate);
 
+        if ((24 == usecase->stream.out->bit_width) &&
+            (usecase->stream.out->devices & AUDIO_DEVICE_OUT_SPEAKER)) {
+            usecase->stream.out->app_type_cfg.sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
+        } else if ((snd_device != SND_DEVICE_OUT_HEADPHONES_44_1 &&
+            usecase->stream.out->sample_rate == OUTPUT_SAMPLING_RATE_44100) ||
+            (usecase->stream.out->sample_rate < OUTPUT_SAMPLING_RATE_44100)) {
+            usecase->stream.out->app_type_cfg.sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
+        }
+        sample_rate = usecase->stream.out->app_type_cfg.sample_rate;
+
+        app_type_cfg[len++] = usecase->stream.out->app_type_cfg.app_type;
+        app_type_cfg[len++] = acdb_dev_id;
+        if (((usecase->stream.out->format == AUDIO_FORMAT_E_AC3) ||
+            (usecase->stream.out->format == AUDIO_FORMAT_E_AC3_JOC))
+            && audio_extn_dolby_is_passthrough_stream(usecase->stream.out)) {
+            app_type_cfg[len++] = sample_rate * 4;
+        } 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);
     } else if (usecase->type == PCM_CAPTURE) {
          app_type_cfg[len++] = platform_get_default_app_type_v2(adev->platform, usecase->type);
          app_type_cfg[len++] = acdb_dev_id;
@@ -988,7 +1009,7 @@
     if (audio_extn_is_dolby_format(out->format) &&
         /*TODO:Extend code to support DTS passthrough*/
         /*set compressed channel status bits*/
-        audio_extn_dolby_is_passthrough_stream(out->flags)){
+        audio_extn_dolby_is_passthrough_stream(out)){
         get_compressed_channel_status(buffer, bytes, channel_status, AUDIO_PARSER_CODEC_AC3);
     } else
 #endif
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index a7938ce..c582bd2 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -231,11 +231,12 @@
 
 static const struct string_to_enum out_channels_name_to_enum_table[] = {
     STRING_TO_ENUM(AUDIO_CHANNEL_OUT_STEREO),
-    STRING_TO_ENUM(AUDIO_CHANNEL_OUT_QUAD),/* QUAD_BACK is same as QUAD */
-    STRING_TO_ENUM(AUDIO_CHANNEL_OUT_QUAD_SIDE),
+    STRING_TO_ENUM(AUDIO_CHANNEL_OUT_2POINT1),
+    STRING_TO_ENUM(AUDIO_CHANNEL_OUT_QUAD),
+    STRING_TO_ENUM(AUDIO_CHANNEL_OUT_SURROUND),
     STRING_TO_ENUM(AUDIO_CHANNEL_OUT_PENTA),
-    STRING_TO_ENUM(AUDIO_CHANNEL_OUT_5POINT1), /* 5POINT1_BACK is same as 5POINT1 */
-    STRING_TO_ENUM(AUDIO_CHANNEL_OUT_5POINT1_SIDE),
+    STRING_TO_ENUM(AUDIO_CHANNEL_OUT_5POINT1),
+    STRING_TO_ENUM(AUDIO_CHANNEL_OUT_6POINT1),
     STRING_TO_ENUM(AUDIO_CHANNEL_OUT_7POINT1),
 };
 
@@ -243,6 +244,23 @@
     STRING_TO_ENUM(AUDIO_FORMAT_AC3),
     STRING_TO_ENUM(AUDIO_FORMAT_E_AC3),
     STRING_TO_ENUM(AUDIO_FORMAT_E_AC3_JOC),
+    STRING_TO_ENUM(AUDIO_FORMAT_DTS),
+    STRING_TO_ENUM(AUDIO_FORMAT_DTS_HD),
+};
+
+//list of all supported sample rates by HDMI specification.
+static const int out_hdmi_sample_rates[] = {
+    32000, 44100, 48000, 88200, 96000, 176400, 192000,
+};
+
+static const struct string_to_enum out_hdmi_sample_rates_name_to_enum_table[] = {
+    STRING_TO_ENUM(32000),
+    STRING_TO_ENUM(44100),
+    STRING_TO_ENUM(48000),
+    STRING_TO_ENUM(88200),
+    STRING_TO_ENUM(96000),
+    STRING_TO_ENUM(176400),
+    STRING_TO_ENUM(192000),
 };
 
 static struct audio_device *adev = NULL;
@@ -806,39 +824,81 @@
     }
 }
 
+static void reset_hdmi_sink_caps(struct stream_out *out) {
+    int i = 0;
+
+    for (i = 0; i<= MAX_SUPPORTED_CHANNEL_MASKS; i++) {
+        out->supported_channel_masks[i] = 0;
+    }
+    for (i = 0; i<= MAX_SUPPORTED_FORMATS; i++) {
+        out->supported_formats[i] = 0;
+    }
+    for (i = 0; i<= MAX_SUPPORTED_SAMPLE_RATES; i++) {
+        out->supported_sample_rates[i] = 0;
+    }
+}
+
 /* must be called with hw device mutex locked */
-static int read_hdmi_channel_masks(struct stream_out *out)
+static int read_hdmi_sink_caps(struct stream_out *out)
 {
-    int ret = 0, i = 0;
+    int ret = 0, i = 0, j = 0;
     int channels = platform_edid_get_max_channels(out->dev->platform);
 
+    reset_hdmi_sink_caps(out);
+
     switch (channels) {
-        /*
-         * Do not handle stereo output in Multi-channel cases
-         * Stereo case is handled in normal playback path
-         */
-    case 6:
-        ALOGV("%s: HDMI supports Quad and 5.1", __func__);
-        out->supported_channel_masks[i++] = AUDIO_CHANNEL_OUT_QUAD;
-        out->supported_channel_masks[i++] = AUDIO_CHANNEL_OUT_QUAD_SIDE;
-        out->supported_channel_masks[i++] = AUDIO_CHANNEL_OUT_PENTA;
-        out->supported_channel_masks[i++] = AUDIO_CHANNEL_OUT_5POINT1;
-        out->supported_channel_masks[i++] = AUDIO_CHANNEL_OUT_5POINT1_SIDE;
-        break;
     case 8:
-        ALOGV("%s: HDMI supports Quad, 5.1 and 7.1 channels", __func__);
-        out->supported_channel_masks[i++] = AUDIO_CHANNEL_OUT_QUAD;
-        out->supported_channel_masks[i++] = AUDIO_CHANNEL_OUT_QUAD_SIDE;
-        out->supported_channel_masks[i++] = AUDIO_CHANNEL_OUT_PENTA;
-        out->supported_channel_masks[i++] = AUDIO_CHANNEL_OUT_5POINT1;
-        out->supported_channel_masks[i++] = AUDIO_CHANNEL_OUT_5POINT1_SIDE;
+        ALOGV("%s: HDMI supports 7.1 channels", __func__);
         out->supported_channel_masks[i++] = AUDIO_CHANNEL_OUT_7POINT1;
+        out->supported_channel_masks[i++] = AUDIO_CHANNEL_OUT_6POINT1;
+    case 6:
+        ALOGV("%s: HDMI supports 5.1 channels", __func__);
+        out->supported_channel_masks[i++] = AUDIO_CHANNEL_OUT_5POINT1;
+        out->supported_channel_masks[i++] = AUDIO_CHANNEL_OUT_PENTA;
+        out->supported_channel_masks[i++] = AUDIO_CHANNEL_OUT_QUAD;
+        out->supported_channel_masks[i++] = AUDIO_CHANNEL_OUT_SURROUND;
+        out->supported_channel_masks[i++] = AUDIO_CHANNEL_OUT_2POINT1;
+    case 2:
+        ALOGV("%s: HDMI supports 2 channels", __func__);
+        out->supported_channel_masks[i++] = AUDIO_CHANNEL_OUT_STEREO;
         break;
     default:
-        ALOGE("HDMI does not support multi channel playback");
+        ALOGE("invalid/nonstandard channal count[%d]",channels);
         ret = -ENOSYS;
         break;
     }
+
+    // check channel format caps
+    i = 0;
+    if (platform_is_edid_supported_format(out->dev->platform, AUDIO_FORMAT_AC3)) {
+        ALOGV(":%s HDMI supports AC3/EAC3 formats", __func__);
+        out->supported_formats[i++] = AUDIO_FORMAT_AC3;
+        //Adding EAC3/EAC3_JOC formats if AC3 is supported by the sink.
+        //EAC3/EAC3_JOC will be converted to AC3 for decoding if needed
+        out->supported_formats[i++] = AUDIO_FORMAT_E_AC3;
+        out->supported_formats[i++] = AUDIO_FORMAT_E_AC3_JOC;
+    }
+
+    if (platform_is_edid_supported_format(out->dev->platform, AUDIO_FORMAT_DTS)) {
+        ALOGV(":%s HDMI supports DTS format", __func__);
+        out->supported_formats[i++] = AUDIO_FORMAT_DTS;
+    }
+
+    if (platform_is_edid_supported_format(out->dev->platform, AUDIO_FORMAT_DTS_HD)) {
+        ALOGV(":%s HDMI supports DTS HD format", __func__);
+        out->supported_formats[i++] = AUDIO_FORMAT_DTS_HD;
+    }
+
+
+    // check sample rate caps
+    i = 0;
+    for (j = 0; j < MAX_SUPPORTED_SAMPLE_RATES; j++) {
+        if (platform_is_edid_supported_sample_rate(out->dev->platform, out_hdmi_sample_rates[j])) {
+            ALOGV(":%s HDMI supports sample rate:%d", __func__, out_hdmi_sample_rates[j]);
+            out->supported_sample_rates[i++] = out_hdmi_sample_rates[j];
+        }
+    }
+
     return ret;
 }
 
@@ -1529,12 +1589,25 @@
     return 0;
 }
 
-static bool allow_hdmi_channel_config(struct audio_device *adev)
+static bool allow_hdmi_channel_config(struct audio_device *adev,
+                                      bool enable_passthru)
 {
     struct listnode *node;
     struct audio_usecase *usecase;
     bool ret = true;
 
+    if (enable_passthru && !audio_extn_passthru_is_enabled()) {
+        ret = false;
+        goto exit;
+    }
+
+    if (audio_extn_passthru_is_active()) {
+        ALOGI("%s: Compress audio passthrough is active,"
+              "no HDMI config change allowed", __func__);
+        ret = false;
+        goto exit;
+    }
+
     list_for_each(node, &adev->usecase_list) {
         usecase = node_to_item(node, struct audio_usecase, list);
         if (usecase->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
@@ -1545,54 +1618,80 @@
              * max channels of remaining use cases.
              */
             if (usecase->id == USECASE_VOICE_CALL) {
-                ALOGD("%s: voice call is active, no change in HDMI channels",
+                ALOGV("%s: voice call is active, no change in HDMI channels",
                       __func__);
                 ret = false;
                 break;
             } else if (usecase->id == USECASE_AUDIO_PLAYBACK_MULTI_CH) {
-                ALOGD("%s: multi channel playback is active, "
-                      "no change in HDMI channels", __func__);
-                ret = false;
-                break;
+                if (!enable_passthru) {
+                    ALOGV("%s: multi channel playback is active, "
+                          "no change in HDMI channels", __func__);
+                    ret = false;
+                    break;
+                }
             } else if (is_offload_usecase(usecase->id) &&
                        audio_channel_count_from_out_mask(usecase->stream.out->channel_mask) > 2) {
-                ALOGD("%s:multi-channel(%x) compress offload playback is active"
-                      ", no change in HDMI channels", __func__,
-                      usecase->stream.out->channel_mask);
-                ret = false;
-                break;
+                if (!enable_passthru) {
+                    ALOGD("%s:multi-channel(%x) compress offload playback is active"
+                        ", no change in HDMI channels", __func__,
+                        usecase->stream.out->channel_mask);
+                    ret = false;
+                    break;
+                }
             }
         }
     }
+    ALOGV("allow hdmi config %d", ret);
+exit:
     return ret;
 }
 
-static int check_and_set_hdmi_channels(struct audio_device *adev,
-                                       unsigned int channels)
+static int check_and_set_hdmi_config(struct audio_device *adev,
+                                     uint32_t channels,
+                                     uint32_t sample_rate,
+                                     audio_format_t format,
+                                     bool enable_passthru)
 {
     struct listnode *node;
     struct audio_usecase *usecase;
+    int32_t factor = 1;
+    bool config = false;
 
-    unsigned int supported_channels = platform_edid_get_max_channels(
-                                          adev->platform);
-    ALOGV("supported_channels %d, channels %d", supported_channels, channels);
-    /* Check if change in HDMI channel config is allowed */
-    if (!allow_hdmi_channel_config(adev))
-        return 0;
+    ALOGV("%s channels %d sample_rate %d format:%x enable_passthru:%d",
+         __func__, channels, sample_rate, format, enable_passthru);
 
-    if (channels > supported_channels)
-        channels = supported_channels;
+    if (channels != adev->cur_hdmi_channels) {
+        ALOGV("channel does not match current hdmi channels");
+        config = true;
+    }
 
-    if (channels == adev->cur_hdmi_channels) {
-        ALOGD("%s: Requested channels are same as current channels(%d)",
-               __func__, channels);
+    if (sample_rate != adev->cur_hdmi_sample_rate) {
+        ALOGV("sample rate does not match current hdmi sample rate");
+        config = true;
+    }
+
+    if (format != adev->cur_hdmi_format) {
+        ALOGV("format does not match current hdmi format");
+        config = true;
+    }
+
+    /* TBD - add check for bit width */
+    if (!config) {
+        ALOGV("No need to config hdmi");
         return 0;
     }
 
-    /*TODO: CHECK for passthrough don't set channel map for passthrough*/
-    platform_set_hdmi_channels(adev->platform, channels);
-    platform_set_edid_channels_configuration(adev->platform, channels);
+    if (enable_passthru &&
+        (format == AUDIO_FORMAT_E_AC3)) {
+        ALOGV("factor 4 for E_AC3 passthru");
+        factor = 4;
+    }
+
+    platform_set_hdmi_config(adev->platform, channels, factor * sample_rate,
+                             enable_passthru);
     adev->cur_hdmi_channels = channels;
+    adev->cur_hdmi_format = format;
+    adev->cur_hdmi_sample_rate = sample_rate;
 
     /*
      * Deroute all the playback streams routed to HDMI so that
@@ -1607,6 +1706,10 @@
         }
     }
 
+    bool was_active = audio_extn_keep_alive_is_active();
+    if (was_active)
+        audio_extn_keep_alive_stop();
+
     /*
      * Enable all the streams disabled above. Now the HDMI backend
      * will be activated with new channel configuration
@@ -1619,9 +1722,59 @@
         }
     }
 
+    if (was_active)
+        audio_extn_keep_alive_start();
+
     return 0;
 }
 
+/* called with out lock taken */
+static int check_and_set_hdmi_backend(struct stream_out *out)
+{
+    struct audio_device *adev = out->dev;
+    int ret;
+    bool enable_passthru = false;
+
+    if (!(out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL))
+        return -1;
+
+    ALOGV("%s usecase %s out->format:%x out->bit_width:%d", __func__, use_case_table[out->usecase],out->format,out->bit_width);
+
+    if (is_offload_usecase(out->usecase) &&
+        audio_extn_dolby_is_passthrough_stream(out)) {
+        enable_passthru = true;
+    }
+
+    /* Check if change in HDMI channel config is allowed */
+    if (!allow_hdmi_channel_config(adev, enable_passthru)) {
+        return -EPERM;
+    }
+
+    if (out->usecase == USECASE_AUDIO_PLAYBACK_OFFLOAD) {
+        uint32_t channels;
+        ALOGV("Offload usecase, enable passthru %d", enable_passthru);
+
+        if (enable_passthru) {
+            audio_extn_passthru_on_start(out);
+            audio_extn_dolby_update_passt_stream_configuration(adev, out);
+        }
+
+        /* For pass through case, the backend should be configured as stereo */
+        channels = enable_passthru ? DEFAULT_HDMI_OUT_CHANNELS :
+                                     out->compr_config.codec->ch_in;
+
+        ret = check_and_set_hdmi_config(adev, channels,
+                                        out->sample_rate, out->format,
+                                        enable_passthru);
+    } else
+        ret = check_and_set_hdmi_config(adev, out->config.channels,
+                                        out->config.rate,
+                                        out->format,
+                                        false);
+    return ret;
+}
+
+
 static int stop_output_stream(struct stream_out *out)
 {
     int ret = 0;
@@ -1638,7 +1791,7 @@
     }
 
     if (is_offload_usecase(out->usecase) &&
-        !(audio_extn_dolby_is_passthrough_stream(out->flags))) {
+        !(audio_extn_dolby_is_passthrough_stream(out))) {
         if (adev->visualizer_stop_output != NULL)
             adev->visualizer_stop_output(out->handle, out->pcm_device_id);
 
@@ -1658,17 +1811,20 @@
     free(uc_info);
 
     if (is_offload_usecase(out->usecase) &&
-        (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) &&
-        (audio_extn_dolby_is_passthrough_stream(out->flags))) {
+        (audio_extn_dolby_is_passthrough_stream(out))) {
         ALOGV("Disable passthrough , reset mixer to pcm");
         /* NO_PASSTHROUGH */
         out->compr_config.codec->compr_passthr = 0;
-        audio_extn_dolby_set_hdmi_config(adev, out);
+
+        /* Must be called after removing the usecase from list */
+        if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL)
+                check_and_set_hdmi_config(adev, DEFAULT_HDMI_OUT_CHANNELS,
+                                          DEFAULT_HDMI_OUT_SAMPLE_RATE,
+                                          DEFAULT_HDMI_OUT_FORMAT,
+                                          false);
+        audio_extn_passthru_on_stop(out);
         audio_extn_dolby_set_dap_bypass(adev, DAP_STATE_ON);
     }
-    /* Must be called after removing the usecase from list */
-    if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL)
-        check_and_set_hdmi_channels(adev, DEFAULT_HDMI_OUT_CHANNELS);
 
     ALOGV("%s: exit: status(%d)", __func__, ret);
     return ret;
@@ -1677,7 +1833,6 @@
 int start_output_stream(struct stream_out *out)
 {
     int ret = 0;
-    int sink_channels = 0;
     char prop_value[PROPERTY_VALUE_MAX] = {0};
     struct audio_usecase *uc_info;
     struct audio_device *adev = out->dev;
@@ -1713,37 +1868,18 @@
         goto error_config;
     }
 
+    /* This must be called before adding this usecase to the list */
+    if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
+        /* This call can fail if compress pass thru is already active */
+        check_and_set_hdmi_backend(out);
+    }
+
     uc_info->id = out->usecase;
     uc_info->type = PCM_PLAYBACK;
     uc_info->stream.out = out;
     uc_info->devices = out->devices;
     uc_info->in_snd_device = SND_DEVICE_NONE;
     uc_info->out_snd_device = SND_DEVICE_NONE;
-    /* This must be called before adding this usecase to the list */
-    if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
-        if (is_offload_usecase(out->usecase)) {
-            if (audio_extn_dolby_is_passthrough_stream(out->flags)) {
-                audio_extn_dolby_update_passt_stream_configuration(adev, out);
-            }
-        }
-        property_get("audio.use.hdmi.sink.cap", prop_value, NULL);
-        if (!strncmp("true", prop_value, 4)) {
-            sink_channels = platform_edid_get_max_channels(out->dev->platform);
-            ALOGD("%s: set HDMI channel count[%d] based on sink capability",
-                   __func__, sink_channels);
-            check_and_set_hdmi_channels(adev, sink_channels);
-        } else {
-            if (is_offload_usecase(out->usecase)) {
-                unsigned int ch_count =  out->compr_config.codec->ch_in;
-                if (audio_extn_dolby_is_passthrough_stream(out->flags))
-                    /* backend channel config for passthrough stream is stereo */
-                    ch_count = 2;
-                check_and_set_hdmi_channels(adev, ch_count);
-            } else
-                check_and_set_hdmi_channels(adev, out->config.channels);
-        }
-        audio_extn_dolby_set_hdmi_config(adev, out);
-    }
     list_add_tail(&adev->usecase_list, &uc_info->list);
 
     audio_extn_perf_lock_acquire(&adev->perf_lock_handle, 0,
@@ -1831,7 +1967,7 @@
         if (audio_extn_is_dolby_format(out->format))
             audio_extn_dolby_send_ddp_endp_params(adev);
 #endif
-        if (!(audio_extn_dolby_is_passthrough_stream(out->flags))) {
+        if (!(audio_extn_dolby_is_passthrough_stream(out))) {
             if (adev->visualizer_start_output != NULL)
                 adev->visualizer_start_output(out->handle, out->pcm_device_id);
             if (adev->offload_effects_start_output != NULL)
@@ -2080,7 +2216,7 @@
         if ((out->devices == AUDIO_DEVICE_OUT_AUX_DIGITAL ||
                 out->devices == AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET) &&
                 val == AUDIO_DEVICE_NONE) {
-            if (!audio_extn_dolby_is_passthrough_stream(out->flags))
+            if (!audio_extn_dolby_is_passthrough_stream(out))
                 val = AUDIO_DEVICE_OUT_SPEAKER;
         }
 
@@ -2244,6 +2380,31 @@
             free(str);
         str = str_parms_to_str(reply);
     }
+
+    ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES, value, sizeof(value));
+    if (ret >= 0) {
+        value[0] = '\0';
+        i = 0;
+        first = true;
+        while (out->supported_sample_rates[i] != 0) {
+            for (j = 0; j < ARRAY_SIZE(out_hdmi_sample_rates_name_to_enum_table); j++) {
+                if (out_hdmi_sample_rates_name_to_enum_table[j].value == out->supported_sample_rates[i]) {
+                    if (!first) {
+                        strlcat(value, "|", sizeof(value));
+                    }
+                    strlcat(value, out_hdmi_sample_rates_name_to_enum_table[j].name, sizeof(value));
+                    first = false;
+                    break;
+                }
+            }
+            i++;
+        }
+        str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES, value);
+        if (str)
+            free(str);
+        str = str_parms_to_str(reply);
+    }
+
     str_parms_destroy(query);
     str_parms_destroy(reply);
     ALOGV("%s: exit: returns - %s", __func__, str);
@@ -2277,7 +2438,7 @@
         out->muted = (left == 0.0f);
         return 0;
     } else if (is_offload_usecase(out->usecase)) {
-        if (audio_extn_dolby_is_passthrough_stream(out->flags)) {
+        if (audio_extn_dolby_is_passthrough_stream(out)) {
             /*
              * Set mute or umute on HDMI passthrough stream.
              * Only take left channel into account.
@@ -2598,6 +2759,11 @@
 
             out->offload_state = OFFLOAD_STATE_PAUSED;
 
+            if (audio_extn_passthru_is_active()) {
+                ALOGV("offload use case, pause passthru");
+                audio_extn_passthru_on_pause(out);
+            }
+
             audio_extn_dts_eagle_fade(adev, false, out);
             audio_extn_dts_notify_playback_state(out->usecase, 0,
                                                  out->sample_rate, popcount(out->channel_mask),
@@ -2621,11 +2787,18 @@
             struct audio_device *adev = out->dev;
             int snd_scard_state = get_snd_card_state(adev);
 
-            if (SND_CARD_STATE_ONLINE == snd_scard_state)
+            if (SND_CARD_STATE_ONLINE == snd_scard_state) {
+                if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
+                    pthread_mutex_lock(&out->dev->lock);
+                    ALOGV("offload resume, check and set hdmi backend again");
+                    check_and_set_hdmi_backend(out);
+                    pthread_mutex_unlock(&out->dev->lock);
+                }
                 status = compress_resume(out->compr);
-
-            out->offload_state = OFFLOAD_STATE_PLAYING;
-
+            }
+            if (!status) {
+                out->offload_state = OFFLOAD_STATE_PLAYING;
+            }
             audio_extn_dts_eagle_fade(adev, true, out);
             audio_extn_dts_notify_playback_state(out->usecase, 0, out->sample_rate,
                                                      popcount(out->channel_mask), 1);
@@ -3019,8 +3192,8 @@
 
     out = (struct stream_out *)calloc(1, sizeof(struct stream_out));
 
-    ALOGD("%s: enter: sample_rate(%d) channel_mask(%#x) devices(%#x) flags(%#x)\
-        stream_handle(%p)",__func__, config->sample_rate, config->channel_mask,
+    ALOGD("%s: enter: format(%#x) sample_rate(%d) channel_mask(%#x) devices(%#x) flags(%#x)\
+        stream_handle(%p)", __func__, config->format, config->sample_rate, config->channel_mask,
         devices, flags, &out->stream);
 
 
@@ -3047,6 +3220,23 @@
     out->non_blocking = 0;
     out->use_small_bufs = false;
 
+    if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL &&
+        (flags & AUDIO_OUTPUT_FLAG_DIRECT)) {
+        pthread_mutex_lock(&adev->lock);
+        ALOGV("AUDIO_DEVICE_OUT_AUX_DIGITAL and DIRECT|OFFLOAD, check hdmi caps");
+        ret = read_hdmi_sink_caps(out);
+        pthread_mutex_unlock(&adev->lock);
+        if (ret != 0) {
+            if (ret == -ENOSYS) {
+                /* ignore and go with default */
+                ret = 0;
+            } else {
+                ALOGE("error reading hdmi sink caps");
+                goto error_open;
+            }
+        }
+    }
+
     /* Init use case and pcm_config */
     if ((out->flags & AUDIO_OUTPUT_FLAG_DIRECT) &&
         !(out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD ||
@@ -3055,8 +3245,14 @@
         out->devices & AUDIO_DEVICE_OUT_PROXY)) {
 
         pthread_mutex_lock(&adev->lock);
-        if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL)
-            ret = read_hdmi_channel_masks(out);
+        if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
+            /*
+            * Do not handle stereo output in Multi-channel cases
+            * Stereo case is handled in normal playback path
+            */
+            if (out->supported_channel_masks[0] == AUDIO_CHANNEL_OUT_STEREO)
+                ret = AUDIO_CHANNEL_OUT_STEREO;
+        }
 
         if (out->devices & AUDIO_DEVICE_OUT_PROXY)
             ret = audio_extn_read_afe_proxy_channel_masks(out);
@@ -3068,9 +3264,12 @@
             config->sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
         if (config->channel_mask == 0)
             config->channel_mask = AUDIO_CHANNEL_OUT_5POINT1;
+        if (config->format == 0)
+            config->format = AUDIO_FORMAT_PCM_16_BIT;
 
         out->channel_mask = config->channel_mask;
         out->sample_rate = config->sample_rate;
+        out->format = config->format;
         out->usecase = USECASE_AUDIO_PLAYBACK_MULTI_CH;
         out->config = pcm_config_hdmi_multi;
         out->config.rate = config->sample_rate;
@@ -3095,15 +3294,11 @@
             goto error_open;
         }
 
-        if ((out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) &&
-            ((audio_extn_dolby_is_passthrough_stream(out->flags)))) {
-            ALOGV("read and update_pass through formats");
-            ret = audio_extn_dolby_update_passt_formats(adev, out);
-            if(ret != 0) {
-                goto error_open;
-            }
+        if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
             if(config->offload_info.format == 0)
                 config->offload_info.format = out->supported_formats[0];
+            if (config->offload_info.sample_rate == 0)
+                config->offload_info.sample_rate = out->supported_sample_rates[0];
         }
 
         if (!is_supported_format(config->offload_info.format) &&
@@ -3135,9 +3330,16 @@
         }
 
         if (out->usecase == USECASE_INVALID) {
-            ALOGE("%s, Max allowed OFFLOAD usecase reached ... ", __func__);
-            ret = -EEXIST;
-            goto error_open;
+            if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL &&
+                    config->format == 0 && config->sample_rate == 0 &&
+                    config->channel_mask == 0) {
+                ALOGI("%s dummy open to query sink capability",__func__);
+                out->usecase = USECASE_AUDIO_PLAYBACK_OFFLOAD;
+            } else {
+                ALOGE("%s, Max allowed OFFLOAD usecase reached ... ", __func__);
+                ret = -EEXIST;
+                goto error_open;
+            }
         }
 
         if (config->offload_info.channel_mask)
@@ -3163,7 +3365,7 @@
              ((config->offload_info.format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_PCM)) {
             out->compr_config.fragment_size =
                platform_get_pcm_offload_buffer_size(&config->offload_info);
-        } else if (audio_extn_dolby_is_passthrough_stream(out->flags)) {
+        } else if (audio_extn_dolby_is_passthrough_stream(out)) {
             out->compr_config.fragment_size =
                audio_extn_dolby_get_passt_buffer_size(&config->offload_info);
         } else {
@@ -3225,7 +3427,14 @@
                 __func__, config->offload_info.version,
                 config->offload_info.bit_rate);
         //Decide if we need to use gapless mode by default
-        check_and_set_gapless_mode(adev);
+        if (!audio_extn_dolby_is_passthrough_stream(out)) {
+            ALOGV("%s: don't enable gapless for passthrough", __func__);
+            check_and_set_gapless_mode(adev);
+        }
+
+        if (audio_extn_dolby_is_passthrough_stream(out)) {
+            out->flags |= AUDIO_OUTPUT_FLAG_COMPRESS_PASSTHROUGH;
+        }
     } else if (out->flags & AUDIO_OUTPUT_FLAG_INCALL_MUSIC) {
         ret = voice_extn_check_and_set_incall_music_usecase(adev, out);
         if (ret != 0) {
@@ -3277,8 +3486,8 @@
         out->sample_rate = out->config.rate;
     }
 
-    ALOGV("%s devices %d,flags %x, format %x, out->sample_rate %d, out->bit_width %d",
-           __func__, devices, flags, format, out->sample_rate, out->bit_width);
+    ALOGV("%s devices %d,flags %x, format %x, out->sample_rate %d, out->bit_width %d out->flags:%x, flags:%x",
+           __func__, devices, flags, format, out->sample_rate, out->bit_width, out->flags, flags);
     /* TODO remove this hardcoding and check why width is zero*/
     if (out->bit_width == 0)
         out->bit_width = 16;
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index e13415d..a1d69a6 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -67,9 +67,12 @@
 #define ACDB_DEV_TYPE_OUT 1
 #define ACDB_DEV_TYPE_IN 2
 
-#define MAX_SUPPORTED_CHANNEL_MASKS 8
-#define MAX_SUPPORTED_FORMATS 3
+#define MAX_SUPPORTED_CHANNEL_MASKS 14
+#define MAX_SUPPORTED_FORMATS 15
+#define MAX_SUPPORTED_SAMPLE_RATES 7
 #define DEFAULT_HDMI_OUT_CHANNELS   2
+#define DEFAULT_HDMI_OUT_SAMPLE_RATE 48000
+#define DEFAULT_HDMI_OUT_FORMAT AUDIO_FORMAT_PCM_16_BIT
 
 #define SND_CARD_STATE_OFFLINE 0
 #define SND_CARD_STATE_ONLINE 1
@@ -199,6 +202,7 @@
     /* Array of supported channel mask configurations. +1 so that the last entry is always 0 */
     audio_channel_mask_t supported_channel_masks[MAX_SUPPORTED_CHANNEL_MASKS + 1];
     audio_format_t supported_formats[MAX_SUPPORTED_FORMATS+1];
+    uint32_t supported_sample_rates[MAX_SUPPORTED_SAMPLE_RATES+1];
     bool muted;
     uint64_t written; /* total frames written, not cleared when entering standby */
     audio_io_handle_t handle;
@@ -321,6 +325,9 @@
     bool speaker_lr_swap;
     struct voice voice;
     unsigned int cur_hdmi_channels;
+    audio_format_t cur_hdmi_format;
+    unsigned int cur_hdmi_sample_rate;
+    unsigned int cur_hdmi_bit_width;
     unsigned int cur_wfd_channels;
     bool bt_wb_speech_enabled;
     bool allow_afe_proxy_usage;
diff --git a/hal/msm8916/platform.c b/hal/msm8916/platform.c
index e66aad7..5e38197 100644
--- a/hal/msm8916/platform.c
+++ b/hal/msm8916/platform.c
@@ -4435,12 +4435,23 @@
         ALOGV("%s:E_AC3", __func__);
         format = DOLBY_DIGITAL_PLUS;
         break;
+    case AUDIO_FORMAT_DTS:
+        ALOGV("%s:DTS", __func__);
+        format = DTS;
+        break;
+    case AUDIO_FORMAT_DTS_HD:
+        ALOGV("%s:DTS_HD", __func__);
+        format = DTS_HD;
+        break;
     case AUDIO_FORMAT_PCM_16_BIT:
     case AUDIO_FORMAT_PCM_16_BIT_OFFLOAD:
     case AUDIO_FORMAT_PCM_24_BIT_OFFLOAD:
-    default:
         ALOGV("%s:PCM", __func__);
-        format =  LPCM;
+        format = LPCM;
+        break;
+    default:
+        ALOGE("%s:invalid format:%d", __func__,format);
+        format =  -1;
         break;
     }
     return format;
@@ -4474,6 +4485,11 @@
     int i, ret;
     unsigned char format_id = platform_map_to_edid_format(format);
 
+    if (format_id <= 0) {
+        ALOGE("%s invalid edid format mappting for :%x" ,__func__, format);
+        return false;
+    }
+
     ret = platform_get_edid_info(platform);
     info = (edid_audio_info *)my_data->edid_info;
     if (ret == 0 && info != NULL) {
@@ -4484,17 +4500,45 @@
               *  & DOLBY_DIGITAL_PLUS
               */
             if (info->audio_blocks_array[i].format_id == format_id) {
-                ALOGV("%s:platform_is_edid_supported_format true %x",
+                ALOGV("%s:returns true %x",
                       __func__, format);
                 return true;
             }
         }
     }
-    ALOGV("%s:platform_is_edid_supported_format false %x",
+    ALOGV("%s:returns false %x",
            __func__, format);
     return false;
 }
 
+bool platform_is_edid_supported_sample_rate(void *platform, int sample_rate)
+{
+    struct platform_data *my_data = (struct platform_data *)platform;
+    struct audio_device *adev = my_data->adev;
+    edid_audio_info *info = NULL;
+    int num_audio_blocks;
+    int i, ret, count;
+
+    ret = platform_get_edid_info(platform);
+    info = (edid_audio_info *)my_data->edid_info;
+    if (ret == 0 && info != NULL) {
+        for (i = 0; i < info->audio_blocks && i < MAX_EDID_BLOCKS; i++) {
+             /*
+              * To check
+              *  is there any special for CONFIG_HDMI_PASSTHROUGH_CONVERT
+              *  & DOLBY_DIGITAL_PLUS
+              */
+            if (info->audio_blocks_array[i].sampling_freq == sample_rate) {
+                ALOGV("%s: returns true %d", __func__, sample_rate);
+                return true;
+            }
+        }
+    }
+    ALOGV("%s: returns false %d", __func__, sample_rate);
+
+    return false;
+}
+
 int platform_set_edid_channels_configuration(void *platform, int channels) {
 
     struct platform_data *my_data = (struct platform_data *)platform;
@@ -4566,92 +4610,75 @@
     return mixer_ctl_set_enum_by_string(ctl, mixer_val);
 }
 
-int platform_set_hdmi_config(struct stream_out *out)
+int platform_set_hdmi_config(void *platform, uint32_t channel_count,
+                             uint32_t sample_rate, bool enable_passthrough)
 {
-    struct listnode *node;
-    struct audio_usecase *usecase;
-    struct audio_device *adev = out->dev;
+    struct platform_data *my_data = (struct platform_data *)platform;
+    struct audio_device *adev = my_data->adev;
     const char *hdmi_format_ctrl = "HDMI RX Format";
-    const char *hdmi_rate_ctrl = "HDMI_RX SampleRate";
-    int sample_rate = out->sample_rate;
-    /*TODO: Add rules and check if this needs to be done.*/
-    if((is_offload_usecase(out->usecase)) &&
-        (out->compr_config.codec->compr_passthr == PASSTHROUGH ||
-        out->compr_config.codec->compr_passthr == PASSTHROUGH_CONVERT)) {
-        /* TODO: can we add mixer control for channels here avoid setting */
-        if ((out->format == AUDIO_FORMAT_E_AC3 ||
-            out->format == AUDIO_FORMAT_E_AC3_JOC) &&
-            (out->compr_config.codec->compr_passthr == PASSTHROUGH))
-            sample_rate = out->sample_rate * 4;
-        ALOGD("%s:HDMI compress format and samplerate %d, sample_rate %d",
-               __func__, out->sample_rate, sample_rate);
-        platform_set_mixer_control(out, hdmi_format_ctrl, "Compr");
-        switch (sample_rate) {
-            case 32000:
-                platform_set_mixer_control(out, hdmi_rate_ctrl, "KHZ_32");
-                break;
-            case 44100:
-                platform_set_mixer_control(out, hdmi_rate_ctrl, "KHZ_44_1");
-                break;
-            case 96000:
-                platform_set_mixer_control(out, hdmi_rate_ctrl, "KHZ_96");
-                break;
-            case 176400:
-                platform_set_mixer_control(out, hdmi_rate_ctrl, "KHZ_176_4");
-                break;
-            case 192000:
-                platform_set_mixer_control(out, hdmi_rate_ctrl, "KHZ_192");
-                break;
-            case 128000:
-                if (out->format != AUDIO_FORMAT_E_AC3) {
-                    platform_set_mixer_control(out, hdmi_rate_ctrl, "KHZ_128");
-                    break;
-                } else
-                   ALOGW("Unsupported sample rate for E_AC3 32K");
-            default:
-            case 48000:
-                platform_set_mixer_control(out, hdmi_rate_ctrl, "KHZ_48");
-                break;
-        }
+    const char *hdmi_rate_ctrl   = "HDMI_RX SampleRate";
+    const char *hdmi_chans_ctrl  = "HDMI_RX Channels";
+    const char *channel_cnt_str  = NULL;
+
+    ALOGI("%s ch[%d] sr[%d], pthru[%d]", __func__,
+        channel_count, sample_rate, enable_passthrough);
+
+    switch (channel_count) {
+    case 8:
+        channel_cnt_str = "Eight"; break;
+    case 7:
+        channel_cnt_str = "Seven"; break;
+    case 6:
+        channel_cnt_str = "Six"; break;
+    case 5:
+        channel_cnt_str = "Five"; break;
+    case 4:
+        channel_cnt_str = "Four"; break;
+    case 3:
+        channel_cnt_str = "Three"; break;
+    default:
+        channel_cnt_str = "Two"; break;
+    }
+    ALOGV("%s: HDMI channel count: %s", __func__, channel_cnt_str);
+    set_mixer_control(adev->mixer, hdmi_chans_ctrl, channel_cnt_str);
+
+    if (enable_passthrough) {
+        ALOGD("%s:HDMI compress format", __func__);
+        set_mixer_control(adev->mixer, hdmi_format_ctrl, "Compr");
     } else {
-        ALOGD("%s: HDMI pcm and samplerate %d", __func__,
-               out->sample_rate);
-        platform_set_mixer_control(out, hdmi_format_ctrl, "LPCM");
-        platform_set_mixer_control(out, hdmi_rate_ctrl, "KHZ_48");
+        ALOGD("%s: HDMI PCM format", __func__);
+        set_mixer_control(adev->mixer, hdmi_format_ctrl, "LPCM");
     }
 
-    /*
-     * Deroute all the playback streams routed to HDMI so that
-     * the back end is deactivated. Note that backend will not
-     * be deactivated if any one stream is connected to it.
-     */
-    list_for_each(node, &adev->usecase_list) {
-        usecase = node_to_item(node, struct audio_usecase, list);
-        ALOGV("%s:disable: usecase type %d, devices 0x%x", __func__,
-               usecase->type, usecase->devices);
-        if (usecase->type == PCM_PLAYBACK &&
-                usecase->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
-            disable_audio_route(adev, usecase);
-        }
-    }
-
-    /*
-     * Enable all the streams disabled above. Now the HDMI backend
-     * will be activated with new channel configuration
-     */
-    list_for_each(node, &adev->usecase_list) {
-        usecase = node_to_item(node, struct audio_usecase, list);
-        ALOGV("%s:enable: usecase type %d, devices 0x%x", __func__,
-               usecase->type, usecase->devices);
-        if (usecase->type == PCM_PLAYBACK &&
-                usecase->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
-            enable_audio_route(adev, usecase);
-        }
+    switch (sample_rate) {
+    case 32000:
+        set_mixer_control(adev->mixer, hdmi_rate_ctrl, "KHZ_32");
+        break;
+    case 44100:
+        set_mixer_control(adev->mixer, hdmi_rate_ctrl, "KHZ_44P1");
+        break;
+    case 96000:
+        set_mixer_control(adev->mixer, hdmi_rate_ctrl, "KHZ_96");
+        break;
+    case 128000:
+        set_mixer_control(adev->mixer, hdmi_rate_ctrl, "KHZ_128");
+        break;
+    case 176400:
+        set_mixer_control(adev->mixer, hdmi_rate_ctrl, "KHZ_176_4");
+        break;
+    case 192000:
+        set_mixer_control(adev->mixer, hdmi_rate_ctrl, "KHZ_192");
+        break;
+    default:
+    case 48000:
+        set_mixer_control(adev->mixer, hdmi_rate_ctrl, "KHZ_48");
+        break;
     }
 
     return 0;
 }
 
+
 int platform_set_device_params(struct stream_out *out, int param, int value)
 {
     struct audio_device *adev = out->dev;
diff --git a/hal/msm8960/platform.c b/hal/msm8960/platform.c
index 48470b9..43de781 100644
--- a/hal/msm8960/platform.c
+++ b/hal/msm8960/platform.c
@@ -1153,6 +1153,12 @@
     return  false;
 }
 
+bool platform_is_edid_supported_sample_rate(void *platform __unused,
+                                    int sample_rate __unused)
+{
+    return false;
+}
+
 void platform_cache_edid(void * platform __unused)
 {
 
@@ -1163,7 +1169,10 @@
 
 }
 
-int platform_set_hdmi_config(struct stream_out *out __unused)
+int platform_set_hdmi_config(void *platform __unused,
+                                    uint32_t channel_count __unused,
+                                    uint32_t sample_rate __unused,
+                                    bool enable_passthrough __unused)
 {
     return 0;
 }
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index 87c4f7d..32eab45 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -4378,12 +4378,23 @@
         ALOGV("%s:E_AC3", __func__);
         format = DOLBY_DIGITAL_PLUS;
         break;
+    case AUDIO_FORMAT_DTS:
+        ALOGV("%s:DTS", __func__);
+        format = DTS;
+        break;
+    case AUDIO_FORMAT_DTS_HD:
+        ALOGV("%s:DTS_HD", __func__);
+        format = DTS_HD;
+        break;
     case AUDIO_FORMAT_PCM_16_BIT:
     case AUDIO_FORMAT_PCM_16_BIT_OFFLOAD:
     case AUDIO_FORMAT_PCM_24_BIT_OFFLOAD:
-    default:
         ALOGV("%s:PCM", __func__);
-        format =  LPCM;
+        format = LPCM;
+        break;
+    default:
+        ALOGE("%s:invalid format:%d", __func__,format);
+        format =  -1;
         break;
     }
     return format;
@@ -4417,6 +4428,11 @@
     int i, ret;
     unsigned char format_id = platform_map_to_edid_format(format);
 
+    if (format_id <= 0) {
+        ALOGE("%s invalid edid format mappting for :%x" ,__func__, format);
+        return false;
+    }
+
     ret = platform_get_edid_info(platform);
     info = (edid_audio_info *)my_data->edid_info;
     if (ret == 0 && info != NULL) {
@@ -4427,17 +4443,46 @@
               *  & DOLBY_DIGITAL_PLUS
               */
             if (info->audio_blocks_array[i].format_id == format_id) {
-                ALOGV("%s:platform_is_edid_supported_format true %x",
+                ALOGV("%s:returns true %x",
                       __func__, format);
                 return true;
             }
         }
     }
-    ALOGV("%s:platform_is_edid_supported_format false %x",
+    ALOGV("%s:returns false %x",
            __func__, format);
     return false;
 }
 
+bool platform_is_edid_supported_sample_rate(void *platform, int sample_rate)
+{
+    struct platform_data *my_data = (struct platform_data *)platform;
+    struct audio_device *adev = my_data->adev;
+    edid_audio_info *info = NULL;
+    int num_audio_blocks;
+    int i, ret, count;
+
+    ret = platform_get_edid_info(platform);
+    info = (edid_audio_info *)my_data->edid_info;
+    if (ret == 0 && info != NULL) {
+        for (i = 0; i < info->audio_blocks && i < MAX_EDID_BLOCKS; i++) {
+             /*
+              * To check
+              *  is there any special for CONFIG_HDMI_PASSTHROUGH_CONVERT
+              *  & DOLBY_DIGITAL_PLUS
+              */
+            if (info->audio_blocks_array[i].sampling_freq == sample_rate) {
+                ALOGV("%s: returns true %d", __func__, sample_rate);
+                return true;
+            }
+        }
+    }
+    ALOGV("%s: returns false %d", __func__, sample_rate);
+
+    return false;
+}
+
+
 int platform_set_edid_channels_configuration(void *platform, int channels) {
 
     struct platform_data *my_data = (struct platform_data *)platform;
@@ -4509,87 +4554,85 @@
     return mixer_ctl_set_enum_by_string(ctl, mixer_val);
 }
 
-int platform_set_hdmi_config(struct stream_out *out)
+static int set_mixer_control(struct mixer *mixer,
+                             const char * mixer_ctl_name,
+                             const char *mixer_val)
 {
-    struct listnode *node;
-    struct audio_usecase *usecase;
-    struct audio_device *adev = out->dev;
+    struct mixer_ctl *ctl;
+    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);
+}
+
+int platform_set_hdmi_config(void *platform, uint32_t channel_count,
+                             uint32_t sample_rate, bool enable_passthrough)
+{
+    struct platform_data *my_data = (struct platform_data *)platform;
+    struct audio_device *adev = my_data->adev;
     const char *hdmi_format_ctrl = "HDMI RX Format";
-    const char *hdmi_rate_ctrl = "HDMI_RX SampleRate";
-    int sample_rate = out->sample_rate;
-    /*TODO: Add rules and check if this needs to be done.*/
-    if((is_offload_usecase(out->usecase)) &&
-        (out->compr_config.codec->compr_passthr == PASSTHROUGH ||
-        out->compr_config.codec->compr_passthr == PASSTHROUGH_CONVERT)) {
-        /* TODO: can we add mixer control for channels here avoid setting */
-        if ((out->format == AUDIO_FORMAT_E_AC3 ||
-            out->format == AUDIO_FORMAT_E_AC3_JOC) &&
-            (out->compr_config.codec->compr_passthr == PASSTHROUGH))
-            sample_rate = out->sample_rate * 4;
-        ALOGD("%s:HDMI compress format and samplerate %d, sample_rate %d",
-               __func__, out->sample_rate, sample_rate);
-        platform_set_mixer_control(out, hdmi_format_ctrl, "Compr");
-        switch (sample_rate) {
-            case 32000:
-                platform_set_mixer_control(out, hdmi_rate_ctrl, "KHZ_32");
-                break;
-            case 44100:
-                platform_set_mixer_control(out, hdmi_rate_ctrl, "KHZ_44_1");
-                break;
-            case 96000:
-                platform_set_mixer_control(out, hdmi_rate_ctrl, "KHZ_96");
-                break;
-            case 176400:
-                platform_set_mixer_control(out, hdmi_rate_ctrl, "KHZ_176_4");
-                break;
-            case 192000:
-                platform_set_mixer_control(out, hdmi_rate_ctrl, "KHZ_192");
-                break;
-            case 128000:
-                if (out->format != AUDIO_FORMAT_E_AC3) {
-                    platform_set_mixer_control(out, hdmi_rate_ctrl, "KHZ_128");
-                    break;
-                } else
-                   ALOGW("Unsupported sample rate for E_AC3 32K");
-            default:
-            case 48000:
-                platform_set_mixer_control(out, hdmi_rate_ctrl, "KHZ_48");
-                break;
-        }
+    const char *hdmi_rate_ctrl   = "HDMI_RX SampleRate";
+    const char *hdmi_chans_ctrl  = "HDMI_RX Channels";
+    const char *channel_cnt_str  = NULL;
+
+    ALOGI("%s ch[%d] sr[%d], pthru[%d]", __func__,
+        channel_count, sample_rate, enable_passthrough);
+
+    switch (channel_count) {
+    case 8:
+        channel_cnt_str = "Eight"; break;
+    case 7:
+        channel_cnt_str = "Seven"; break;
+    case 6:
+        channel_cnt_str = "Six"; break;
+    case 5:
+        channel_cnt_str = "Five"; break;
+    case 4:
+        channel_cnt_str = "Four"; break;
+    case 3:
+        channel_cnt_str = "Three"; break;
+    default:
+        channel_cnt_str = "Two"; break;
+    }
+    ALOGV("%s: HDMI channel count: %s", __func__, channel_cnt_str);
+    set_mixer_control(adev->mixer, hdmi_chans_ctrl, channel_cnt_str);
+
+    if (enable_passthrough) {
+        ALOGD("%s:HDMI compress format", __func__);
+        set_mixer_control(adev->mixer, hdmi_format_ctrl, "Compr");
     } else {
-        ALOGD("%s: HDMI pcm and samplerate %d", __func__,
-               out->sample_rate);
-        platform_set_mixer_control(out, hdmi_format_ctrl, "LPCM");
-        platform_set_mixer_control(out, hdmi_rate_ctrl, "KHZ_48");
+        ALOGD("%s: HDMI PCM format", __func__);
+        set_mixer_control(adev->mixer, hdmi_format_ctrl, "LPCM");
     }
 
-    /*
-     * Deroute all the playback streams routed to HDMI so that
-     * the back end is deactivated. Note that backend will not
-     * be deactivated if any one stream is connected to it.
-     */
-    list_for_each(node, &adev->usecase_list) {
-        usecase = node_to_item(node, struct audio_usecase, list);
-        ALOGV("%s:disable: usecase type %d, devices 0x%x", __func__,
-               usecase->type, usecase->devices);
-        if (usecase->type == PCM_PLAYBACK &&
-                usecase->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
-            disable_audio_route(adev, usecase);
-        }
-    }
-
-    /*
-     * Enable all the streams disabled above. Now the HDMI backend
-     * will be activated with new channel configuration
-     */
-    list_for_each(node, &adev->usecase_list) {
-        usecase = node_to_item(node, struct audio_usecase, list);
-        ALOGV("%s:enable: usecase type %d, devices 0x%x", __func__,
-               usecase->type, usecase->devices);
-        if (usecase->type == PCM_PLAYBACK &&
-                usecase->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
-            enable_audio_route(adev, usecase);
-        }
+    switch (sample_rate) {
+    case 32000:
+        set_mixer_control(adev->mixer, hdmi_rate_ctrl, "KHZ_32");
+        break;
+    case 44100:
+        set_mixer_control(adev->mixer, hdmi_rate_ctrl, "KHZ_44P1");
+        break;
+    case 96000:
+        set_mixer_control(adev->mixer, hdmi_rate_ctrl, "KHZ_96");
+        break;
+    case 128000:
+        set_mixer_control(adev->mixer, hdmi_rate_ctrl, "KHZ_128");
+        break;
+    case 176400:
+        set_mixer_control(adev->mixer, hdmi_rate_ctrl, "KHZ_176_4");
+        break;
+    case 192000:
+        set_mixer_control(adev->mixer, hdmi_rate_ctrl, "KHZ_192");
+        break;
+    default:
+    case 48000:
+        set_mixer_control(adev->mixer, hdmi_rate_ctrl, "KHZ_48");
+        break;
     }
 
     return 0;
diff --git a/hal/platform_api.h b/hal/platform_api.h
index 65bcc98..04ed8f5 100644
--- a/hal/platform_api.h
+++ b/hal/platform_api.h
@@ -127,9 +127,12 @@
 int platform_set_edid_channels_configuration(void *platform, int channels);
 unsigned char platform_map_to_edid_format(int format);
 bool platform_is_edid_supported_format(void *platform, int format);
+bool platform_is_edid_supported_sample_rate(void *platform, int sample_rate);
 void platform_cache_edid(void * platform);
 void platform_invalidate_edid(void * platform);
-int platform_set_hdmi_config(struct stream_out *out);
+void platform_invalidate_hdmi_config(void * platform);
+int platform_set_hdmi_config(void *platform, uint32_t channel_count,
+                             uint32_t sample_rate, bool enable_passthrough);
 int platform_set_device_params(struct stream_out *out, int param, int value);
 int platform_set_audio_device_interface(const char * device_name, const char *intf_name,
                                         const char * codec_type);