hal: add support for compress passthrough
- Add support for compress passthrough.
Change-Id: I4934470ac8b23cb8de9b2d7d1b0014afe74b5a27
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 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
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index dff662a..b92d809 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -1579,12 +1579,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) {
@@ -1595,54 +1608,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
@@ -1657,6 +1696,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
@@ -1669,9 +1712,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;
@@ -1688,7 +1781,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);
@@ -1708,17 +1801,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;
- platform_set_hdmi_config(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;
@@ -1727,7 +1823,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;
@@ -1763,37 +1858,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);
- }
- platform_set_hdmi_config(out);
- }
list_add_tail(&adev->usecase_list, &uc_info->list);
audio_extn_perf_lock_acquire(&adev->perf_lock_handle, 0,
@@ -1881,7 +1957,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)
@@ -2130,7 +2206,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;
}
@@ -2352,7 +2428,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.
@@ -2673,6 +2749,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),
@@ -2696,11 +2777,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);
@@ -3235,7 +3323,7 @@
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 cap",__func__);
+ ALOGI("%s dummy open to query sink capability",__func__);
out->usecase = USECASE_AUDIO_PLAYBACK_OFFLOAD;
} else {
ALOGE("%s, Max allowed OFFLOAD usecase reached ... ", __func__);
@@ -3267,7 +3355,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 {
@@ -3329,7 +3417,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) {
@@ -3381,8 +3476,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 942ad5e..a1d69a6 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -71,6 +71,8 @@
#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
@@ -323,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 03e7c89..a5c3905 100644
--- a/hal/msm8916/platform.c
+++ b/hal/msm8916/platform.c
@@ -4473,92 +4473,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 9bcd388..a761209 100644
--- a/hal/msm8960/platform.c
+++ b/hal/msm8960/platform.c
@@ -1168,7 +1168,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 4c82175..fe93d6b 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -4425,87 +4425,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 8a0b5a4..c4510b2 100644
--- a/hal/platform_api.h
+++ b/hal/platform_api.h
@@ -130,7 +130,9 @@
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);