hal: read HDMI sink capability for dynamic output profile
- update read_hdmi_sink_caps to support dynamic sample
rate, channel, and format based on HDMI EDID information
Change-Id: I69b3f19cb1d81c3e51695adae4f2379cbb439dc8
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index d10bc18..dff662a 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;
@@ -799,39 +817,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;
}
@@ -1653,7 +1713,7 @@
ALOGV("Disable passthrough , reset mixer to pcm");
/* NO_PASSTHROUGH */
out->compr_config.codec->compr_passthr = 0;
- audio_extn_dolby_set_hdmi_config(adev, out);
+ platform_set_hdmi_config(out);
audio_extn_dolby_set_dap_bypass(adev, DAP_STATE_ON);
}
/* Must be called after removing the usecase from list */
@@ -1732,7 +1792,7 @@
} else
check_and_set_hdmi_channels(adev, out->config.channels);
}
- audio_extn_dolby_set_hdmi_config(adev, out);
+ platform_set_hdmi_config(out);
}
list_add_tail(&adev->usecase_list, &uc_info->list);
@@ -2234,6 +2294,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);
@@ -3009,8 +3094,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);
@@ -3037,6 +3122,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 ||
@@ -3045,8 +3147,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);
@@ -3058,9 +3166,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;
@@ -3085,15 +3196,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) &&
@@ -3125,9 +3232,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 cap",__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)