hal: add support for compress passthrough

- Add support for compress passthrough.

Change-Id: I4934470ac8b23cb8de9b2d7d1b0014afe74b5a27
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index cc4f36b..91d7c0a 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -403,11 +403,22 @@
 #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_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
 bool audio_extn_dolby_is_passt_convert_supported(struct audio_device *adev,
                                                  struct stream_out *out);
@@ -415,10 +426,22 @@
                                          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);
+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
@@ -535,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 41494b1..ad8f8a4 100644
--- a/hal/audio_extn/dolby.c
+++ b/hal/audio_extn/dolby.c
@@ -478,10 +478,35 @@
     }
 }
 
-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;
 }
 
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 8eb557e..5a5ca52 100644
--- a/hal/audio_extn/utils.c
+++ b/hal/audio_extn/utils.c
@@ -649,6 +649,27 @@
          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);
 
+        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;
@@ -984,7 +1005,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