hal: Add support for parsing hdmi edid information
Add support for parsing hdmi edid data and setting channel map
to asm
Parse edid data on hdmi connection and invalidat on disconnection.
Fix audio playback errors on HDMI when source channel count is
greater than sink channel count for offload playback.
Change-Id: I1e0a8335611a87974cb273431659938a0b345e68
diff --git a/hal/Android.mk b/hal/Android.mk
index 1af245a..ec47740 100644
--- a/hal/Android.mk
+++ b/hal/Android.mk
@@ -46,6 +46,10 @@
LOCAL_C_INCLUDES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include
LOCAL_ADDITIONAL_DEPENDENCIES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr
+ifneq ($(filter msm8994 ,$(TARGET_BOARD_PLATFORM)),)
+ LOCAL_SRC_FILES += edid.c
+endif
+
ifeq ($(strip $(AUDIO_FEATURE_ENABLED_PCM_OFFLOAD)),true)
LOCAL_CFLAGS += -DPCM_OFFLOAD_ENABLED
endif
diff --git a/hal/audio_extn/audio_extn.c b/hal/audio_extn/audio_extn.c
index 5ab7018..3cb18f5 100644
--- a/hal/audio_extn/audio_extn.c
+++ b/hal/audio_extn/audio_extn.c
@@ -30,6 +30,7 @@
#include "audio_extn.h"
#include "platform.h"
#include "platform_api.h"
+#include "edid.h"
#define MAX_SLEEP_RETRY 100
#define WIFI_INIT_WAIT_SLEEP 50
@@ -235,30 +236,6 @@
#define audio_extn_set_afe_proxy_parameters(adev, parms) (0)
#define audio_extn_get_afe_proxy_parameters(query, reply) (0)
#else
-/* Front left channel. */
-#define PCM_CHANNEL_FL 1
-
-/* Front right channel. */
-#define PCM_CHANNEL_FR 2
-
-/* Front center channel. */
-#define PCM_CHANNEL_FC 3
-
-/* Left surround channel.*/
-#define PCM_CHANNEL_LS 4
-
-/* Right surround channel.*/
-#define PCM_CHANNEL_RS 5
-
-/* Low frequency effect channel. */
-#define PCM_CHANNEL_LFE 6
-
-/* Left back channel; Rear left channel. */
-#define PCM_CHANNEL_LB 8
-
-/* Right back channel; Rear right channel. */
-#define PCM_CHANNEL_RB 9
-
static int32_t afe_proxy_set_channel_mapping(struct audio_device *adev,
int channel_count)
{
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index 02add40..05c5e84 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -354,7 +354,7 @@
typedef enum {
DAP_STATE_ON = 0,
DAP_STATE_BYPASS,
-};
+} dap_state;
#ifndef AUDIO_FORMAT_E_AC3_JOC
#define AUDIO_FORMAT_E_AC3_JOC 0x19000000UL
#endif
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 9e841df..3a30536 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -206,7 +206,11 @@
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_5POINT1),
+ 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_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_7POINT1),
};
@@ -677,7 +681,7 @@
/* must be called with hw device mutex locked */
static int read_hdmi_channel_masks(struct stream_out *out)
{
- int ret = 0;
+ int ret = 0, i = 0;
int channels = platform_edid_get_max_channels(out->dev->platform);
switch (channels) {
@@ -686,13 +690,21 @@
* Stereo case is handled in normal playback path
*/
case 6:
- ALOGV("%s: HDMI supports 5.1", __func__);
- out->supported_channel_masks[0] = AUDIO_CHANNEL_OUT_5POINT1;
+ 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 5.1 and 7.1 channels", __func__);
- out->supported_channel_masks[0] = AUDIO_CHANNEL_OUT_5POINT1;
- out->supported_channel_masks[1] = AUDIO_CHANNEL_OUT_7POINT1;
+ 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;
+ out->supported_channel_masks[i++] = AUDIO_CHANNEL_OUT_7POINT1;
break;
default:
ALOGE("HDMI does not support multi channel playback");
@@ -1278,16 +1290,24 @@
struct listnode *node;
struct audio_usecase *usecase;
+ 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;
+ if (channels > supported_channels)
+ channels = supported_channels;
+
if (channels == adev->cur_hdmi_channels) {
- ALOGD("%s: Requested channels are same as current channels(%d)", __func__, channels);
+ ALOGD("%s: Requested channels are same as current channels(%d)",
+ __func__, channels);
return 0;
}
platform_set_hdmi_channels(adev->platform, channels);
+ platform_set_edid_channels_configuration(adev->platform, channels);
adev->cur_hdmi_channels = channels;
/*
@@ -1408,7 +1428,8 @@
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);
+ 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))
@@ -1451,7 +1472,11 @@
}
break;
}
+ platform_set_stream_channel_map(adev->platform, out->channel_mask,
+ out->pcm_device_id);
} else {
+ platform_set_stream_channel_map(adev->platform, out->channel_mask,
+ out->pcm_device_id);
out->pcm = NULL;
out->compr = compress_open(adev->snd_card,
out->pcm_device_id,
@@ -2950,6 +2975,24 @@
adev->bt_wb_speech_enabled = false;
}
+ ret = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_CONNECT, value, sizeof(value));
+ if (ret >= 0) {
+ val = atoi(value);
+ if (val & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
+ ALOGV("cache new edid");
+ platform_cache_edid(adev->platform);
+ }
+ }
+
+ ret = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_DISCONNECT, value, sizeof(value));
+ if (ret >= 0) {
+ val = atoi(value);
+ if (val & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
+ ALOGV("invalidate cached edid");
+ platform_invalidate_edid(adev->platform);
+ }
+ }
+
audio_extn_set_parameters(adev, parms);
done:
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index d05f743..77f1d37 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -47,7 +47,7 @@
#define ACDB_DEV_TYPE_OUT 1
#define ACDB_DEV_TYPE_IN 2
-#define MAX_SUPPORTED_CHANNEL_MASKS 2
+#define MAX_SUPPORTED_CHANNEL_MASKS 8
#define DEFAULT_HDMI_OUT_CHANNELS 2
#define SND_CARD_STATE_OFFLINE 0
diff --git a/hal/edid.c b/hal/edid.c
new file mode 100644
index 0000000..9b05950
--- /dev/null
+++ b/hal/edid.c
@@ -0,0 +1,687 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "audio_hw_edid"
+/*#define LOG_NDEBUG 0*/
+/*#define LOG_NDDEBUG 0*/
+
+#include <errno.h>
+#include <cutils/properties.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <cutils/str_parms.h>
+#include <cutils/log.h>
+
+#include "audio_hw.h"
+#include "platform.h"
+#include "platform_api.h"
+#include "edid.h"
+
+static const char * edid_format_to_str(unsigned char format)
+{
+ char * format_str = "??";
+
+ switch (format) {
+ case LPCM:
+ format_str = "Format:LPCM";
+ break;
+ case AC3:
+ format_str = "Format:AC-3";
+ break;
+ case MPEG1:
+ format_str = "Format:MPEG1 (Layers 1 & 2)";
+ break;
+ case MP3:
+ format_str = "Format:MP3 (MPEG1 Layer 3)";
+ break;
+ case MPEG2_MULTI_CHANNEL:
+ format_str = "Format:MPEG2 (multichannel)";
+ break;
+ case AAC:
+ format_str = "Format:AAC";
+ break;
+ case DTS:
+ format_str = "Format:DTS";
+ break;
+ case ATRAC:
+ format_str = "Format:ATRAC";
+ break;
+ case SACD:
+ format_str = "Format:One-bit audio aka SACD";
+ break;
+ case DOLBY_DIGITAL_PLUS:
+ format_str = "Format:Dolby Digital +";
+ break;
+ case DTS_HD:
+ format_str = "Format:DTS-HD";
+ break;
+ case MAT:
+ format_str = "Format:MAT (MLP)";
+ break;
+ case DST:
+ format_str = "Format:DST";
+ break;
+ case WMA_PRO:
+ format_str = "Format:WMA Pro";
+ break;
+ default:
+ break;
+ }
+ return format_str;
+}
+
+static int get_edid_sf(unsigned char byte)
+{
+ int nfreq = 0;
+
+ if (byte & BIT(6)) {
+ ALOGV("192kHz");
+ nfreq = 192000;
+ } else if (byte & BIT(5)) {
+ ALOGV("176kHz");
+ nfreq = 176000;
+ } else if (byte & BIT(4)) {
+ ALOGV("96kHz");
+ nfreq = 96000;
+ } else if (byte & BIT(3)) {
+ ALOGV("88.2kHz");
+ nfreq = 88200;
+ } else if (byte & BIT(2)) {
+ ALOGV("48kHz");
+ nfreq = 48000;
+ } else if (byte & BIT(1)) {
+ ALOGV("44.1kHz");
+ nfreq = 44100;
+ } else if (byte & BIT(0)) {
+ ALOGV("32kHz");
+ nfreq = 32000;
+ }
+ return nfreq;
+}
+
+static int get_edid_bps(unsigned char byte,
+ unsigned char format)
+{
+ int bits_per_sample = 0;
+ if (format == 1) {
+ if (byte & BIT(2)) {
+ ALOGV("24bit");
+ bits_per_sample = 24;
+ } else if (byte & BIT(1)) {
+ ALOGV("20bit");
+ bits_per_sample = 20;
+ } else if (byte & BIT(0)) {
+ ALOGV("16bit");
+ bits_per_sample = 16;
+ }
+ } else {
+ ALOGV("not lpcm format, return 0");
+ return 0;
+ }
+ return bits_per_sample;
+}
+
+static void update_channel_map(edid_audio_info* info)
+{
+ /* HDMI Cable follows CEA standard so SAD is received in CEA
+ * Input source file channel map is fed to ASM in WAV standard(audio.h)
+ * so upto 7.1 SAD bits are:
+ * in CEA convention: RLC/RRC,FLC/FRC,RC,RL/RR,FC,LFE,FL/FR
+ * in WAV convention: BL/BR,FLC/FRC,BC,SL/SR,FC,LFE,FL/FR
+ * Corresponding ADSP IDs (apr-audio_v2.h):
+ * PCM_CHANNEL_FL/PCM_CHANNEL_FR,
+ * PCM_CHANNEL_LFE,
+ * PCM_CHANNEL_FC,
+ * PCM_CHANNEL_LS/PCM_CHANNEL_RS,
+ * PCM_CHANNEL_CS,
+ * PCM_CHANNEL_FLC/PCM_CHANNEL_FRC
+ * PCM_CHANNEL_LB/PCM_CHANNEL_RB
+ */
+ if (!info)
+ return;
+ memset(info->channel_map, 0, MAX_CHANNELS_SUPPORTED);
+ if(info->speaker_allocation[0] & BIT(0)) {
+ info->channel_map[0] = PCM_CHANNEL_FL;
+ info->channel_map[1] = PCM_CHANNEL_FR;
+ }
+ if(info->speaker_allocation[0] & BIT(1)) {
+ info->channel_map[2] = PCM_CHANNEL_LFE;
+ }
+ if(info->speaker_allocation[0] & BIT(2)) {
+ info->channel_map[3] = PCM_CHANNEL_FC;
+ }
+ if(info->speaker_allocation[0] & BIT(3)) {
+ /*
+ * As per CEA(HDMI Cable) standard Bit 3 is equivalent
+ * to SideLeft/SideRight of WAV standard
+ */
+ info->channel_map[4] = PCM_CHANNEL_LS;
+ info->channel_map[5] = PCM_CHANNEL_RS;
+ }
+ if(info->speaker_allocation[0] & BIT(4)) {
+ if(info->speaker_allocation[0] & BIT(3)) {
+ info->channel_map[6] = PCM_CHANNEL_CS;
+ info->channel_map[7] = 0;
+ } else if (info->speaker_allocation[1] & BIT(1)) {
+ info->channel_map[6] = PCM_CHANNEL_CS;
+ info->channel_map[7] = PCM_CHANNEL_TS;
+ } else if (info->speaker_allocation[1] & BIT(2)) {
+ info->channel_map[6] = PCM_CHANNEL_CS;
+ info->channel_map[7] = PCM_CHANNEL_CVH;
+ } else {
+ info->channel_map[4] = PCM_CHANNEL_CS;
+ info->channel_map[5] = 0;
+ }
+ }
+ if(info->speaker_allocation[0] & BIT(5)) {
+ info->channel_map[6] = PCM_CHANNEL_FLC;
+ info->channel_map[7] = PCM_CHANNEL_FRC;
+ }
+ if(info->speaker_allocation[0] & BIT(6)) {
+ // If RLC/RRC is present, RC is invalid as per specification
+ info->speaker_allocation[0] &= 0xef;
+ /*
+ * As per CEA(HDMI Cable) standard Bit 6 is equivalent
+ * to BackLeft/BackRight of WAV standard
+ */
+ info->channel_map[6] = PCM_CHANNEL_LB;
+ info->channel_map[7] = PCM_CHANNEL_RB;
+ }
+ // higher channel are not defined by LPASS
+ //info->nSpeakerAllocation[0] &= 0x3f;
+ if(info->speaker_allocation[0] & BIT(7)) {
+ info->channel_map[6] = 0; // PCM_CHANNEL_FLW; but not defined by LPASS
+ info->channel_map[7] = 0; // PCM_CHANNEL_FRW; but not defined by LPASS
+ }
+ if(info->speaker_allocation[1] & BIT(0)) {
+ info->channel_map[6] = 0; // PCM_CHANNEL_FLH; but not defined by LPASS
+ info->channel_map[7] = 0; // PCM_CHANNEL_FRH; but not defined by LPASS
+ }
+
+ ALOGI("%s channel map updated to [%d %d %d %d %d %d %d %d ] [%x %x %x]", __func__
+ , info->channel_map[0], info->channel_map[1], info->channel_map[2]
+ , info->channel_map[3], info->channel_map[4], info->channel_map[5]
+ , info->channel_map[6], info->channel_map[7]
+ , info->speaker_allocation[0], info->speaker_allocation[1]
+ , info->speaker_allocation[2]);
+}
+
+static void dump_speaker_allocation(edid_audio_info* info)
+{
+ if (!info)
+ return;
+
+ if (info->speaker_allocation[0] & BIT(7))
+ ALOGV("FLW/FRW");
+ if (info->speaker_allocation[0] & BIT(6))
+ ALOGV("RLC/RRC");
+ if (info->speaker_allocation[0] & BIT(5))
+ ALOGV("FLC/FRC");
+ if (info->speaker_allocation[0] & BIT(4))
+ ALOGV("RC");
+ if (info->speaker_allocation[0] & BIT(3))
+ ALOGV("RL/RR");
+ if (info->speaker_allocation[0] & BIT(2))
+ ALOGV("FC");
+ if (info->speaker_allocation[0] & BIT(1))
+ ALOGV("LFE");
+ if (info->speaker_allocation[0] & BIT(0))
+ ALOGV("FL/FR");
+ if (info->speaker_allocation[1] & BIT(2))
+ ALOGV("FCH");
+ if (info->speaker_allocation[1] & BIT(1))
+ ALOGV("TC");
+ if (info->speaker_allocation[1] & BIT(0))
+ ALOGV("FLH/FRH");
+}
+
+static void update_channel_allocation(edid_audio_info* info)
+{
+ int16_t ca;
+ int16_t spkr_alloc;
+
+ if (!info)
+ return;
+
+ /* Most common 5.1 SAD is 0xF, ca 0x0b
+ * and 7.1 SAD is 0x4F, ca 0x13 */
+ spkr_alloc = ((info->speaker_allocation[1]) << 8) |
+ (info->speaker_allocation[0]);
+ ALOGV("info->nSpeakerAllocation %x %x\n", info->speaker_allocation[0],
+ info->speaker_allocation[1]);
+ ALOGV("spkr_alloc: %x", spkr_alloc);
+
+ /* The below switch case calculates channel allocation values
+ as defined in CEA-861 section 6.6.2 */
+ switch (spkr_alloc) {
+ case BIT(0): ca = 0x00; break;
+ case BIT(0)|BIT(1): ca = 0x01; break;
+ case BIT(0)|BIT(2): ca = 0x02; break;
+ case BIT(0)|BIT(1)|BIT(2): ca = 0x03; break;
+ case BIT(0)|BIT(4): ca = 0x04; break;
+ case BIT(0)|BIT(1)|BIT(4): ca = 0x05; break;
+ case BIT(0)|BIT(2)|BIT(4): ca = 0x06; break;
+ case BIT(0)|BIT(1)|BIT(2)|BIT(4): ca = 0x07; break;
+ case BIT(0)|BIT(3): ca = 0x08; break;
+ case BIT(0)|BIT(1)|BIT(3): ca = 0x09; break;
+ case BIT(0)|BIT(2)|BIT(3): ca = 0x0A; break;
+ case BIT(0)|BIT(1)|BIT(2)|BIT(3): ca = 0x0B; break;
+ case BIT(0)|BIT(3)|BIT(4): ca = 0x0C; break;
+ case BIT(0)|BIT(1)|BIT(3)|BIT(4): ca = 0x0D; break;
+ case BIT(0)|BIT(2)|BIT(3)|BIT(4): ca = 0x0E; break;
+ case BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4): ca = 0x0F; break;
+ case BIT(0)|BIT(3)|BIT(6): ca = 0x10; break;
+ case BIT(0)|BIT(1)|BIT(3)|BIT(6): ca = 0x11; break;
+ case BIT(0)|BIT(2)|BIT(3)|BIT(6): ca = 0x12; break;
+ case BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(6): ca = 0x13; break;
+ case BIT(0)|BIT(5): ca = 0x14; break;
+ case BIT(0)|BIT(1)|BIT(5): ca = 0x15; break;
+ case BIT(0)|BIT(2)|BIT(5): ca = 0x16; break;
+ case BIT(0)|BIT(1)|BIT(2)|BIT(5): ca = 0x17; break;
+ case BIT(0)|BIT(4)|BIT(5): ca = 0x18; break;
+ case BIT(0)|BIT(1)|BIT(4)|BIT(5): ca = 0x19; break;
+ case BIT(0)|BIT(2)|BIT(4)|BIT(5): ca = 0x1A; break;
+ case BIT(0)|BIT(1)|BIT(2)|BIT(4)|BIT(5): ca = 0x1B; break;
+ case BIT(0)|BIT(3)|BIT(5): ca = 0x1C; break;
+ case BIT(0)|BIT(1)|BIT(3)|BIT(5): ca = 0x1D; break;
+ case BIT(0)|BIT(2)|BIT(3)|BIT(5): ca = 0x1E; break;
+ case BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(5): ca = 0x1F; break;
+ case BIT(0)|BIT(2)|BIT(3)|BIT(10): ca = 0x20; break;
+ case BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(10): ca = 0x21; break;
+ case BIT(0)|BIT(2)|BIT(3)|BIT(9): ca = 0x22; break;
+ case BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(9): ca = 0x23; break;
+ case BIT(0)|BIT(3)|BIT(8): ca = 0x24; break;
+ case BIT(0)|BIT(1)|BIT(3)|BIT(8): ca = 0x25; break;
+ case BIT(0)|BIT(3)|BIT(7): ca = 0x26; break;
+ case BIT(0)|BIT(1)|BIT(3)|BIT(7): ca = 0x27; break;
+ case BIT(0)|BIT(2)|BIT(3)|BIT(4)|BIT(9): ca = 0x28; break;
+ case BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(9): ca = 0x29; break;
+ case BIT(0)|BIT(2)|BIT(3)|BIT(4)|BIT(10): ca = 0x2A; break;
+ case BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(10): ca = 0x2B; break;
+ case BIT(0)|BIT(2)|BIT(3)|BIT(9)|BIT(10): ca = 0x2C; break;
+ case BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(9)|BIT(10): ca = 0x2D; break;
+ case BIT(0)|BIT(2)|BIT(3)|BIT(8): ca = 0x2E; break;
+ case BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(8): ca = 0x2F; break;
+ case BIT(0)|BIT(2)|BIT(3)|BIT(7): ca = 0x30; break;
+ case BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(7): ca = 0x31; break;
+ default: ca = 0x0; break;
+ }
+ ALOGD("%s channel allocation: %x", __func__, ca);
+ info->channel_allocation = ca;
+}
+
+static void update_channel_map_lpass(edid_audio_info* info)
+{
+ if (!info)
+ return;
+ if (info->channel_allocation < 0 || info->channel_allocation > 0x1f) {
+ ALOGE("Channel allocation out of supported range");
+ return;
+ }
+ ALOGV("channel_allocation 0x%x", info->channel_allocation);
+ memset(info->channel_map, 0, MAX_CHANNELS_SUPPORTED);
+ switch(info->channel_allocation) {
+ case 0x0:
+ info->channel_map[0] = PCM_CHANNEL_FL;
+ info->channel_map[1] = PCM_CHANNEL_FR;
+ break;
+ case 0x1:
+ info->channel_map[0] = PCM_CHANNEL_FL;
+ info->channel_map[1] = PCM_CHANNEL_FR;
+ info->channel_map[2] = PCM_CHANNEL_LFE;
+ break;
+ case 0x2:
+ info->channel_map[0] = PCM_CHANNEL_FL;
+ info->channel_map[1] = PCM_CHANNEL_FR;
+ info->channel_map[2] = PCM_CHANNEL_FC;
+ break;
+ case 0x3:
+ info->channel_map[0] = PCM_CHANNEL_FL;
+ info->channel_map[1] = PCM_CHANNEL_FR;
+ info->channel_map[2] = PCM_CHANNEL_LFE;
+ info->channel_map[3] = PCM_CHANNEL_FC;
+ break;
+ case 0x4:
+ info->channel_map[0] = PCM_CHANNEL_FL;
+ info->channel_map[1] = PCM_CHANNEL_FR;
+ info->channel_map[2] = PCM_CHANNEL_CS;
+ break;
+ case 0x5:
+ info->channel_map[0] = PCM_CHANNEL_FL;
+ info->channel_map[1] = PCM_CHANNEL_FR;
+ info->channel_map[2] = PCM_CHANNEL_LFE;
+ info->channel_map[3] = PCM_CHANNEL_CS;
+ break;
+ case 0x6:
+ info->channel_map[0] = PCM_CHANNEL_FL;
+ info->channel_map[1] = PCM_CHANNEL_FR;
+ info->channel_map[2] = PCM_CHANNEL_FC;
+ info->channel_map[3] = PCM_CHANNEL_CS;
+ break;
+ case 0x7:
+ info->channel_map[0] = PCM_CHANNEL_FL;
+ info->channel_map[1] = PCM_CHANNEL_FR;
+ info->channel_map[2] = PCM_CHANNEL_LFE;
+ info->channel_map[3] = PCM_CHANNEL_FC;
+ info->channel_map[4] = PCM_CHANNEL_CS;
+ break;
+ case 0x8:
+ info->channel_map[0] = PCM_CHANNEL_FL;
+ info->channel_map[1] = PCM_CHANNEL_FR;
+ info->channel_map[2] = PCM_CHANNEL_LS;
+ info->channel_map[3] = PCM_CHANNEL_RS;
+ break;
+ case 0x9:
+ info->channel_map[0] = PCM_CHANNEL_FL;
+ info->channel_map[1] = PCM_CHANNEL_FR;
+ info->channel_map[2] = PCM_CHANNEL_LFE;
+ info->channel_map[3] = PCM_CHANNEL_LS;
+ info->channel_map[4] = PCM_CHANNEL_RS;
+ break;
+ case 0xa:
+ info->channel_map[0] = PCM_CHANNEL_FL;
+ info->channel_map[1] = PCM_CHANNEL_FR;
+ info->channel_map[2] = PCM_CHANNEL_FC;
+ info->channel_map[3] = PCM_CHANNEL_LS;
+ info->channel_map[4] = PCM_CHANNEL_RS;
+ break;
+ case 0xb:
+ info->channel_map[0] = PCM_CHANNEL_FL;
+ info->channel_map[1] = PCM_CHANNEL_FR;
+ info->channel_map[2] = PCM_CHANNEL_LFE;
+ info->channel_map[3] = PCM_CHANNEL_FC;
+ info->channel_map[4] = PCM_CHANNEL_LS;
+ info->channel_map[5] = PCM_CHANNEL_RS;
+ break;
+ case 0xc:
+ info->channel_map[0] = PCM_CHANNEL_FL;
+ info->channel_map[1] = PCM_CHANNEL_FR;
+ info->channel_map[2] = PCM_CHANNEL_LS;
+ info->channel_map[3] = PCM_CHANNEL_RS;
+ info->channel_map[4] = PCM_CHANNEL_CS;
+ break;
+ case 0xd:
+ info->channel_map[0] = PCM_CHANNEL_FL;
+ info->channel_map[1] = PCM_CHANNEL_FR;
+ info->channel_map[2] = PCM_CHANNEL_LFE;
+ info->channel_map[3] = PCM_CHANNEL_LS;
+ info->channel_map[4] = PCM_CHANNEL_RS;
+ info->channel_map[5] = PCM_CHANNEL_CS;
+ break;
+ case 0xe:
+ info->channel_map[0] = PCM_CHANNEL_FL;
+ info->channel_map[1] = PCM_CHANNEL_FR;
+ info->channel_map[2] = PCM_CHANNEL_FC;
+ info->channel_map[3] = PCM_CHANNEL_LS;
+ info->channel_map[4] = PCM_CHANNEL_RS;
+ info->channel_map[5] = PCM_CHANNEL_CS;
+ break;
+ case 0xf:
+ info->channel_map[0] = PCM_CHANNEL_FL;
+ info->channel_map[1] = PCM_CHANNEL_FR;
+ info->channel_map[2] = PCM_CHANNEL_LFE;
+ info->channel_map[3] = PCM_CHANNEL_FC;
+ info->channel_map[4] = PCM_CHANNEL_LS;
+ info->channel_map[5] = PCM_CHANNEL_RS;
+ info->channel_map[6] = PCM_CHANNEL_CS;
+ break;
+ case 0x10:
+ info->channel_map[0] = PCM_CHANNEL_FL;
+ info->channel_map[1] = PCM_CHANNEL_FR;
+ info->channel_map[2] = PCM_CHANNEL_LS;
+ info->channel_map[3] = PCM_CHANNEL_RS;
+ info->channel_map[4] = PCM_CHANNEL_LB;
+ info->channel_map[5] = PCM_CHANNEL_RB;
+ break;
+ case 0x11:
+ info->channel_map[0] = PCM_CHANNEL_FL;
+ info->channel_map[1] = PCM_CHANNEL_FR;
+ info->channel_map[2] = PCM_CHANNEL_LFE;
+ info->channel_map[3] = PCM_CHANNEL_LS;
+ info->channel_map[4] = PCM_CHANNEL_RS;
+ info->channel_map[5] = PCM_CHANNEL_LB;
+ info->channel_map[6] = PCM_CHANNEL_RB;
+ break;
+ case 0x12:
+ info->channel_map[0] = PCM_CHANNEL_FL;
+ info->channel_map[1] = PCM_CHANNEL_FR;
+ info->channel_map[2] = PCM_CHANNEL_FC;
+ info->channel_map[3] = PCM_CHANNEL_LS;
+ info->channel_map[4] = PCM_CHANNEL_RS;
+ info->channel_map[5] = PCM_CHANNEL_LB;
+ info->channel_map[6] = PCM_CHANNEL_RB;
+ break;
+ case 0x13:
+ info->channel_map[0] = PCM_CHANNEL_FL;
+ info->channel_map[1] = PCM_CHANNEL_FR;
+ info->channel_map[2] = PCM_CHANNEL_LFE;
+ info->channel_map[3] = PCM_CHANNEL_FC;
+ info->channel_map[4] = PCM_CHANNEL_LS;
+ info->channel_map[5] = PCM_CHANNEL_RS;
+ info->channel_map[6] = PCM_CHANNEL_LB;
+ info->channel_map[7] = PCM_CHANNEL_RB;
+ break;
+ case 0x14:
+ info->channel_map[0] = PCM_CHANNEL_FL;
+ info->channel_map[1] = PCM_CHANNEL_FR;
+ info->channel_map[2] = PCM_CHANNEL_FLC;
+ info->channel_map[3] = PCM_CHANNEL_FRC;
+ break;
+ case 0x15:
+ info->channel_map[0] = PCM_CHANNEL_FL;
+ info->channel_map[1] = PCM_CHANNEL_FR;
+ info->channel_map[2] = PCM_CHANNEL_LFE;
+ info->channel_map[3] = PCM_CHANNEL_FLC;
+ info->channel_map[4] = PCM_CHANNEL_FRC;
+ break;
+ case 0x16:
+ info->channel_map[0] = PCM_CHANNEL_FL;
+ info->channel_map[1] = PCM_CHANNEL_FR;
+ info->channel_map[2] = PCM_CHANNEL_FC;
+ info->channel_map[3] = PCM_CHANNEL_FLC;
+ info->channel_map[4] = PCM_CHANNEL_FRC;
+ break;
+ case 0x17:
+ info->channel_map[0] = PCM_CHANNEL_FL;
+ info->channel_map[1] = PCM_CHANNEL_FR;
+ info->channel_map[2] = PCM_CHANNEL_LFE;
+ info->channel_map[3] = PCM_CHANNEL_FC;
+ info->channel_map[4] = PCM_CHANNEL_FLC;
+ info->channel_map[5] = PCM_CHANNEL_FRC;
+ break;
+ case 0x18:
+ info->channel_map[0] = PCM_CHANNEL_FL;
+ info->channel_map[1] = PCM_CHANNEL_FR;
+ info->channel_map[2] = PCM_CHANNEL_CS;
+ info->channel_map[3] = PCM_CHANNEL_FLC;
+ info->channel_map[4] = PCM_CHANNEL_FRC;
+ break;
+ case 0x19:
+ info->channel_map[0] = PCM_CHANNEL_FL;
+ info->channel_map[1] = PCM_CHANNEL_FR;
+ info->channel_map[2] = PCM_CHANNEL_LFE;
+ info->channel_map[3] = PCM_CHANNEL_CS;
+ info->channel_map[4] = PCM_CHANNEL_FLC;
+ info->channel_map[5] = PCM_CHANNEL_FRC;
+ break;
+ case 0x1a:
+ info->channel_map[0] = PCM_CHANNEL_FL;
+ info->channel_map[1] = PCM_CHANNEL_FR;
+ info->channel_map[2] = PCM_CHANNEL_FC;
+ info->channel_map[3] = PCM_CHANNEL_CS;
+ info->channel_map[4] = PCM_CHANNEL_FLC;
+ info->channel_map[5] = PCM_CHANNEL_FRC;
+ break;
+ case 0x1b:
+ info->channel_map[0] = PCM_CHANNEL_FL;
+ info->channel_map[1] = PCM_CHANNEL_FR;
+ info->channel_map[2] = PCM_CHANNEL_LFE;
+ info->channel_map[3] = PCM_CHANNEL_FC;
+ info->channel_map[4] = PCM_CHANNEL_CS;
+ info->channel_map[5] = PCM_CHANNEL_FLC;
+ info->channel_map[6] = PCM_CHANNEL_FRC;
+ break;
+ case 0x1c:
+ info->channel_map[0] = PCM_CHANNEL_FL;
+ info->channel_map[1] = PCM_CHANNEL_FR;
+ info->channel_map[2] = PCM_CHANNEL_LS;
+ info->channel_map[3] = PCM_CHANNEL_RS;
+ info->channel_map[4] = PCM_CHANNEL_FLC;
+ info->channel_map[5] = PCM_CHANNEL_FRC;
+ break;
+ case 0x1d:
+ info->channel_map[0] = PCM_CHANNEL_FL;
+ info->channel_map[1] = PCM_CHANNEL_FR;
+ info->channel_map[2] = PCM_CHANNEL_LFE;
+ info->channel_map[3] = PCM_CHANNEL_LS;
+ info->channel_map[4] = PCM_CHANNEL_RS;
+ info->channel_map[5] = PCM_CHANNEL_FLC;
+ info->channel_map[6] = PCM_CHANNEL_FRC;
+ break;
+ case 0x1e:
+ info->channel_map[0] = PCM_CHANNEL_FL;
+ info->channel_map[1] = PCM_CHANNEL_FR;
+ info->channel_map[2] = PCM_CHANNEL_FC;
+ info->channel_map[3] = PCM_CHANNEL_LS;
+ info->channel_map[4] = PCM_CHANNEL_RS;
+ info->channel_map[5] = PCM_CHANNEL_FLC;
+ info->channel_map[6] = PCM_CHANNEL_FRC;
+ break;
+ case 0x1f:
+ info->channel_map[0] = PCM_CHANNEL_FL;
+ info->channel_map[1] = PCM_CHANNEL_FR;
+ info->channel_map[2] = PCM_CHANNEL_LFE;
+ info->channel_map[3] = PCM_CHANNEL_FC;
+ info->channel_map[4] = PCM_CHANNEL_LS;
+ info->channel_map[5] = PCM_CHANNEL_RS;
+ info->channel_map[6] = PCM_CHANNEL_FLC;
+ info->channel_map[7] = PCM_CHANNEL_FRC;
+ break;
+ default:
+ break;
+ }
+ ALOGD("%s channel map updated to [%d %d %d %d %d %d %d %d ]", __func__
+ , info->channel_map[0], info->channel_map[1], info->channel_map[2]
+ , info->channel_map[3], info->channel_map[4], info->channel_map[5]
+ , info->channel_map[6], info->channel_map[7]);
+}
+
+static void dump_edid_data(edid_audio_info *info)
+{
+
+ int i;
+ for (i = 0; i < info->audio_blocks && i < MAX_EDID_BLOCKS; i++) {
+ ALOGV("%s:FormatId:%d rate:%d bps:%d channels:%d", __func__,
+ info->audio_blocks_array[i].format_id,
+ info->audio_blocks_array[i].sampling_freq,
+ info->audio_blocks_array[i].bits_per_sample,
+ info->audio_blocks_array[i].channels);
+ }
+ ALOGV("%s:no of audio blocks:%d", __func__, info->audio_blocks);
+ ALOGV("%s:speaker allocation:[%x %x %x]", __func__,
+ info->speaker_allocation[0], info->speaker_allocation[1],
+ info->speaker_allocation[2]);
+ ALOGV("%s:channel map:[%x %x %x %x %x %x %x %x]", __func__,
+ info->channel_map[0], info->channel_map[1],
+ info->channel_map[2], info->channel_map[3],
+ info->channel_map[4], info->channel_map[5],
+ info->channel_map[6], info->channel_map[7]);
+ ALOGV("%s:channel allocation:%d", __func__, info->channel_allocation);
+ ALOGV("%s:[%d %d %d %d %d %d %d %d ]", __func__,
+ info->channel_map[0], info->channel_map[1],
+ info->channel_map[2], info->channel_map[3],
+ info->channel_map[4], info->channel_map[5],
+ info->channel_map[6], info->channel_map[7]);
+}
+
+bool edid_get_sink_caps(edid_audio_info* info, char *edid_data)
+{
+ unsigned char channels[MAX_EDID_BLOCKS];
+ unsigned char formats[MAX_EDID_BLOCKS];
+ unsigned char frequency[MAX_EDID_BLOCKS];
+ unsigned char bitrate[MAX_EDID_BLOCKS];
+ int i = 0;
+ int length, count_desc;
+
+ if (!info || !edid_data) {
+ ALOGE("No valid EDID");
+ return false;
+ }
+
+ length = (int) *edid_data++;
+ ALOGV("Total length is %d",length);
+
+ count_desc = length/MIN_AUDIO_DESC_LENGTH;
+
+ if (!count_desc) {
+ ALOGE("insufficient descriptors");
+ return false;
+ }
+
+ memset(info, 0, sizeof(edid_audio_info));
+
+ info->audio_blocks = count_desc-1;
+ if (info->audio_blocks > MAX_EDID_BLOCKS) {
+ info->audio_blocks = MAX_EDID_BLOCKS;
+ }
+
+ ALOGV("Total # of audio descriptors %d",count_desc);
+
+ for (i=0; i<info->audio_blocks; i++) {
+ // last block for speaker allocation;
+ channels [i] = (*edid_data & 0x7) + 1;
+ formats [i] = (*edid_data++) >> 3;
+ frequency[i] = *edid_data++;
+ bitrate [i] = *edid_data++;
+ }
+ info->speaker_allocation[0] = *edid_data++;
+ info->speaker_allocation[1] = *edid_data++;
+ info->speaker_allocation[2] = *edid_data++;
+
+ update_channel_map(info);
+ update_channel_allocation(info);
+ update_channel_map_lpass(info);
+
+ for (i=0; i<info->audio_blocks; i++) {
+ ALOGV("AUDIO DESC BLOCK # %d\n",i);
+
+ info->audio_blocks_array[i].channels = channels[i];
+ ALOGV("info->audio_blocks_array[i].channels %d\n",
+ info->audio_blocks_array[i].channels);
+
+ ALOGV("Format Byte %d\n", formats[i]);
+ info->audio_blocks_array[i].format_id = (edid_audio_format_id)formats[i];
+ ALOGV("info->audio_blocks_array[i].format_id %s",
+ edid_format_to_str(formats[i]));
+
+ ALOGV("Frequency Byte %d\n", frequency[i]);
+ info->audio_blocks_array[i].sampling_freq = get_edid_sf(frequency[i]);
+ ALOGV("info->audio_blocks_array[i].sampling_freq %d",
+ info->audio_blocks_array[i].sampling_freq);
+
+ ALOGV("BitsPerSample Byte %d\n", bitrate[i]);
+ info->audio_blocks_array[i].bits_per_sample =
+ get_edid_bps(bitrate[i],formats[i]);
+ ALOGV("info->audio_blocks_array[i].bits_per_sample %d",
+ info->audio_blocks_array[i].bits_per_sample);
+ }
+ dump_speaker_allocation(info);
+ dump_edid_data(info);
+ return true;
+}
diff --git a/hal/edid.h b/hal/edid.h
new file mode 100644
index 0000000..ec83ec8
--- /dev/null
+++ b/hal/edid.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+#ifndef EDID_H
+#define EDID_H
+
+/* HDMI EDID Information */
+#define BIT(nr) (1UL << (nr))
+#define MAX_EDID_BLOCKS 10
+#define MAX_SHORT_AUDIO_DESC_CNT 30
+#define MIN_AUDIO_DESC_LENGTH 3
+#define MIN_SPKR_ALLOCATION_DATA_LENGTH 3
+#define MAX_CHANNELS_SUPPORTED 8
+#define MAX_DISPLAY_DEVICES 3
+#define MAX_FRAME_BUFFER_NAME_SIZE 80
+#define MAX_CHAR_PER_INT 13
+
+#define PCM_CHANNEL_FL 1 /* Front left channel. */
+#define PCM_CHANNEL_FR 2 /* Front right channel. */
+#define PCM_CHANNEL_FC 3 /* Front center channel. */
+#define PCM_CHANNEL_LS 4 /* Left surround channel. */
+#define PCM_CHANNEL_RS 5 /* Right surround channel. */
+#define PCM_CHANNEL_LFE 6 /* Low frequency effect channel. */
+#define PCM_CHANNEL_CS 7 /* Center surround channel; Rear center channel. */
+#define PCM_CHANNEL_LB 8 /* Left back channel; Rear left channel. */
+#define PCM_CHANNEL_RB 9 /* Right back channel; Rear right channel. */
+#define PCM_CHANNEL_TS 10 /* Top surround channel. */
+#define PCM_CHANNEL_CVH 11 /* Center vertical height channel. */
+#define PCM_CHANNEL_MS 12 /* Mono surround channel. */
+#define PCM_CHANNEL_FLC 13 /* Front left of center. */
+#define PCM_CHANNEL_FRC 14 /* Front right of center. */
+#define PCM_CHANNEL_RLC 15 /* Rear left of center. */
+#define PCM_CHANNEL_RRC 16 /* Rear right of center. */
+
+#define MAX_HDMI_CHANNEL_CNT 8
+
+typedef enum edid_audio_format_id {
+ LPCM = 1,
+ AC3,
+ MPEG1,
+ MP3,
+ MPEG2_MULTI_CHANNEL,
+ AAC,
+ DTS,
+ ATRAC,
+ SACD,
+ DOLBY_DIGITAL_PLUS,
+ DTS_HD,
+ MAT,
+ DST,
+ WMA_PRO
+} edid_audio_format_id;
+
+typedef struct edid_audio_block_info {
+ edid_audio_format_id format_id;
+ int sampling_freq;
+ int bits_per_sample;
+ int channels;
+} edid_audio_block_info;
+
+typedef struct edid_audio_info {
+ int audio_blocks;
+ unsigned char speaker_allocation[MIN_SPKR_ALLOCATION_DATA_LENGTH];
+ edid_audio_block_info audio_blocks_array[MAX_EDID_BLOCKS];
+ char channel_map[MAX_CHANNELS_SUPPORTED];
+ int channel_allocation;
+} edid_audio_info;
+
+bool edid_get_sink_caps(edid_audio_info* info, char *edid_data);
+#endif /* EDID_H */
diff --git a/hal/msm8916/platform.c b/hal/msm8916/platform.c
index 43cc00a..a192f5c 100644
--- a/hal/msm8916/platform.c
+++ b/hal/msm8916/platform.c
@@ -1051,7 +1051,7 @@
else
get_cvd_version(cvd_version, adev);
- my_data->acdb_init(snd_card_name, cvd_version);
+ my_data->acdb_init((char *)snd_card_name, cvd_version);
if (cvd_version)
free(cvd_version);
}
@@ -1268,13 +1268,14 @@
return acdb_device_table[snd_device];
}
-int platform_set_snd_device_bit_width(snd_device_t snd_device, unsigned int bit_width)
+int platform_set_snd_device_bit_width(snd_device_t snd_device __unused,
+ unsigned int bit_width __unused)
{
ALOGE("%s: Not implemented", __func__);
return -ENOSYS;
}
-int platform_get_snd_device_bit_width(snd_device_t snd_device)
+int platform_get_snd_device_bit_width(snd_device_t snd_device __unused)
{
ALOGE("%s: Not implemented", __func__);
return -ENOSYS;
@@ -2608,3 +2609,34 @@
return -ENOSYS;
}
+int platform_get_edid_info(void *platform __unused)
+{
+ return -ENOSYS;
+}
+
+int platform_set_channel_map(void *platform __unused, int ch_count __unused,
+ char *ch_map __unused, int snd_id __unused)
+{
+ return -ENOSYS;
+}
+
+int platform_set_stream_channel_map(void *platform __unused,
+ audio_channel_mask_t channel_mask __unused,
+ int snd_id __unused)
+{
+ return -ENOSYS;
+}
+
+int platform_set_edid_channels_configuration(void *platform __unused,
+ int channels __unused)
+{
+ return 0;
+}
+
+void platform_cache_edid(void * platform __unused)
+{
+}
+
+void platform_invalidate_edid(void * platform __unused)
+{
+}
diff --git a/hal/msm8960/platform.c b/hal/msm8960/platform.c
index 941d39f..458593c 100644
--- a/hal/msm8960/platform.c
+++ b/hal/msm8960/platform.c
@@ -28,6 +28,7 @@
#include <audio_hw.h>
#include <platform_api.h>
#include "platform.h"
+#include "audio_extn.h"
#define LIB_ACDB_LOADER "libacdbloader.so"
#define LIB_CSD_CLIENT "libcsd-client.so"
@@ -421,13 +422,14 @@
return -ENOSYS;
}
-int platform_set_snd_device_bit_width(snd_device_t snd_device, unsigned int bit_width)
+int platform_set_snd_device_bit_width(snd_device_t snd_device __unused,
+ unsigned int bit_width __unused)
{
ALOGE("%s: Not implemented", __func__);
return -ENOSYS;
}
-int platform_get_snd_device_bit_width(snd_device_t snd_device)
+int platform_get_snd_device_bit_width(snd_device_t snd_device __unused)
{
ALOGE("%s: Not implemented", __func__);
return -ENOSYS;
@@ -1087,3 +1089,47 @@
{
return 0;
}
+
+int platform_get_edid_info(void *platform __unused)
+{
+ return -ENOSYS;
+}
+
+int platform_set_channel_map(void *platform __unused, int ch_count __unused,
+ char *ch_map __unused, int snd_id __unused)
+{
+ return -ENOSYS;
+}
+
+int platform_set_stream_channel_map(void *platform __unused,
+ audio_channel_mask_t channel_mask __unused,
+ int snd_id __unused)
+{
+ return -ENOSYS;
+}
+
+int platform_set_edid_channels_configuration(void *platform __unused,
+ int channels __unused)
+{
+ return 0;
+}
+
+unsigned char platform_map_to_edid_format(int format __unused)
+{
+ return 0;
+}
+bool platform_is_edid_supported_format(void *platform __unused,
+ int format __unused)
+{
+ return false;
+}
+
+void platform_cache_edid(void * platform __unused)
+{
+
+}
+
+void platform_invalidate_edid(void * platform __unused)
+{
+
+}
diff --git a/hal/msm8960/platform.h b/hal/msm8960/platform.h
index 950ea84..4b4d14e 100644
--- a/hal/msm8960/platform.h
+++ b/hal/msm8960/platform.h
@@ -145,4 +145,6 @@
#define AFE_PROXY_PLAYBACK_PCM_DEVICE 7
#define AFE_PROXY_RECORD_PCM_DEVICE 8
+#define DEVICE_NAME_MAX_SIZE 128
+
#endif // QCOM_AUDIO_PLATFORM_H
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index e0d8a39..1dff709 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -20,6 +20,12 @@
#define LOG_TAG "msm8974_platform"
/*#define LOG_NDEBUG 0*/
#define LOG_NDDEBUG 0
+/*#define VERY_VERY_VERBOSE_LOGGING*/
+#ifdef VERY_VERY_VERBOSE_LOGGING
+#define ALOGVV ALOGV
+#else
+#define ALOGVV(a...) do { } while(0)
+#endif
#include <stdlib.h>
#include <dlfcn.h>
@@ -33,6 +39,7 @@
#include "platform.h"
#include "audio_extn.h"
#include "voice_extn.h"
+#include "edid.h"
#include "sound/compress_params.h"
#include "sound/msmcal-hwdep.h"
@@ -158,6 +165,8 @@
void *hw_info;
struct csd_data *csd;
+ void *edid_info;
+ bool edid_valid;
};
static int pcm_device_table[AUDIO_USECASE_MAX][2] = {
@@ -1003,6 +1012,7 @@
my_data->fluence_mode = FLUENCE_ENDFIRE;
my_data->slowtalk = false;
my_data->hd_voice = false;
+ my_data->edid_info = NULL;
property_get("ro.qc.sdk.audio.fluencetype", my_data->fluence_cap, "");
if (!strncmp("fluencepro", my_data->fluence_cap, sizeof("fluencepro"))) {
@@ -1157,6 +1167,11 @@
/* deinit audio device arbitration */
audio_extn_dev_arbi_deinit();
+ if (my_data->edid_info) {
+ free(my_data->edid_info);
+ my_data->edid_info = NULL;
+ }
+
free(platform);
/* deinit usb */
audio_extn_usb_deinit();
@@ -2139,56 +2154,28 @@
int platform_edid_get_max_channels(void *platform)
{
+ int channel_count;
+ int max_channels = 2;
+ int i = 0, ret = 0;
struct platform_data *my_data = (struct platform_data *)platform;
struct audio_device *adev = my_data->adev;
- char block[MAX_SAD_BLOCKS * SAD_BLOCK_SIZE];
- char *sad = block;
- int num_audio_blocks;
- int channel_count;
- int max_channels = 0;
- int i, ret, count;
+ edid_audio_info *info = NULL;
+ ret = platform_get_edid_info(platform);
+ info = (edid_audio_info *)my_data->edid_info;
- struct mixer_ctl *ctl;
-
- ctl = mixer_get_ctl_by_name(adev->mixer, AUDIO_DATA_BLOCK_MIXER_CTL);
- if (!ctl) {
- ALOGE("%s: Could not get ctl for mixer cmd - %s",
- __func__, AUDIO_DATA_BLOCK_MIXER_CTL);
- return 0;
- }
-
- mixer_ctl_update(ctl);
-
- count = mixer_ctl_get_num_values(ctl);
-
- /* Read SAD blocks, clamping the maximum size for safety */
- if (count > (int)sizeof(block))
- count = (int)sizeof(block);
-
- ret = mixer_ctl_get_array(ctl, block, count);
- if (ret != 0) {
- ALOGE("%s: mixer_ctl_get_array() failed to get EDID info", __func__);
- return 0;
- }
-
- /* Calculate the number of SAD blocks */
- num_audio_blocks = count / SAD_BLOCK_SIZE;
-
- for (i = 0; i < num_audio_blocks; i++) {
- /* Only consider LPCM blocks */
- if ((sad[0] >> 3) != EDID_FORMAT_LPCM) {
- sad += 3;
- continue;
+ if(ret == 0 && info != NULL) {
+ for (i = 0; i < info->audio_blocks && i < MAX_EDID_BLOCKS; i++) {
+ ALOGV("%s:format %d channel %d", __func__,
+ info->audio_blocks_array[i].format_id,
+ info->audio_blocks_array[i].channels);
+ if (info->audio_blocks_array[i].format_id == LPCM) {
+ channel_count = info->audio_blocks_array[i].channels;
+ if (channel_count > max_channels) {
+ max_channels = channel_count;
+ }
+ }
}
-
- channel_count = (sad[0] & 0x7) + 1;
- if (channel_count > max_channels)
- max_channels = channel_count;
-
- /* Advance to next block */
- sad += 3;
}
-
return max_channels;
}
@@ -2845,3 +2832,335 @@
*device_to_be_id = msm_device_to_be_id;
*length = msm_be_id_array_len;
}
+
+int platform_set_stream_channel_map(void *platform, audio_channel_mask_t channel_mask, int snd_id)
+{
+ int ret = 0;
+ int channels = audio_channel_count_from_out_mask(channel_mask);
+
+ char channel_map[8];
+ memset(channel_map, 0, sizeof(channel_map));
+ /* Following are all most common standard WAV channel layouts
+ overridden by channel mask if its allowed and different */
+ switch (channels) {
+ case 1:
+ /* AUDIO_CHANNEL_OUT_MONO */
+ channel_map[0] = PCM_CHANNEL_FC;
+ case 2:
+ /* AUDIO_CHANNEL_OUT_STEREO */
+ channel_map[0] = PCM_CHANNEL_FL;
+ channel_map[1] = PCM_CHANNEL_FR;
+ case 3:
+ /* AUDIO_CHANNEL_OUT_2POINT1 */
+ channel_map[0] = PCM_CHANNEL_FL;
+ channel_map[1] = PCM_CHANNEL_FR;
+ channel_map[2] = PCM_CHANNEL_FC;
+ break;
+ case 4:
+ /* AUDIO_CHANNEL_OUT_QUAD_SIDE */
+ channel_map[0] = PCM_CHANNEL_FL;
+ channel_map[1] = PCM_CHANNEL_FR;
+ channel_map[2] = PCM_CHANNEL_LS;
+ channel_map[3] = PCM_CHANNEL_RS;
+ if (channel_mask == AUDIO_CHANNEL_OUT_QUAD_BACK)
+ {
+ channel_map[2] = PCM_CHANNEL_LB;
+ channel_map[3] = PCM_CHANNEL_RB;
+ }
+ if (channel_mask == AUDIO_CHANNEL_OUT_SURROUND)
+ {
+ channel_map[2] = PCM_CHANNEL_FC;
+ channel_map[3] = PCM_CHANNEL_CS;
+ }
+ break;
+ case 5:
+ /* AUDIO_CHANNEL_OUT_PENTA */
+ channel_map[0] = PCM_CHANNEL_FL;
+ channel_map[1] = PCM_CHANNEL_FR;
+ channel_map[2] = PCM_CHANNEL_FC;
+ channel_map[3] = PCM_CHANNEL_LB;
+ channel_map[4] = PCM_CHANNEL_RB;
+ break;
+ case 6:
+ /* AUDIO_CHANNEL_OUT_5POINT1 */
+ channel_map[0] = PCM_CHANNEL_FL;
+ channel_map[1] = PCM_CHANNEL_FR;
+ channel_map[2] = PCM_CHANNEL_FC;
+ channel_map[3] = PCM_CHANNEL_LFE;
+ channel_map[4] = PCM_CHANNEL_LB;
+ channel_map[5] = PCM_CHANNEL_RB;
+ if (channel_mask == AUDIO_CHANNEL_OUT_5POINT1_SIDE)
+ {
+ channel_map[4] = PCM_CHANNEL_LS;
+ channel_map[5] = PCM_CHANNEL_RS;
+ }
+ break;
+ case 7:
+ /* AUDIO_CHANNEL_OUT_6POINT1 */
+ channel_map[0] = PCM_CHANNEL_FL;
+ channel_map[1] = PCM_CHANNEL_FR;
+ channel_map[2] = PCM_CHANNEL_FC;
+ channel_map[3] = PCM_CHANNEL_LFE;
+ channel_map[4] = PCM_CHANNEL_LB;
+ channel_map[5] = PCM_CHANNEL_RB;
+ channel_map[6] = PCM_CHANNEL_CS;
+ case 8:
+ /* AUDIO_CHANNEL_OUT_7POINT1 */
+ channel_map[0] = PCM_CHANNEL_FL;
+ channel_map[1] = PCM_CHANNEL_FR;
+ channel_map[2] = PCM_CHANNEL_FC;
+ channel_map[3] = PCM_CHANNEL_LFE;
+ channel_map[4] = PCM_CHANNEL_LB;
+ channel_map[5] = PCM_CHANNEL_RB;
+ channel_map[6] = PCM_CHANNEL_LS;
+ channel_map[7] = PCM_CHANNEL_RS;
+ break;
+ default:
+ ALOGE("unsupported channels %d for setting channel map", channels);
+ return -1;
+ }
+ ret = platform_set_channel_map(platform, channels, channel_map, snd_id);
+ return ret;
+}
+
+int platform_get_edid_info(void *platform)
+{
+ struct platform_data *my_data = (struct platform_data *)platform;
+ struct audio_device *adev = my_data->adev;
+ char block[MAX_SAD_BLOCKS * SAD_BLOCK_SIZE];
+ char *sad = block;
+ int num_audio_blocks;
+ int channel_count = 2;
+ int i, ret, count;
+
+ struct mixer_ctl *ctl;
+ char edid_data[MAX_SAD_BLOCKS * SAD_BLOCK_SIZE + 1] = {0};
+ edid_audio_info *info;
+
+ if (my_data->edid_valid) {
+ /* use cached edid */
+ return 0;
+ }
+
+ if (my_data->edid_info == NULL) {
+ my_data->edid_info =
+ (struct edid_audio_info *)calloc(1, sizeof(struct edid_audio_info));
+ }
+
+ info = my_data->edid_info;
+
+ ctl = mixer_get_ctl_by_name(adev->mixer, AUDIO_DATA_BLOCK_MIXER_CTL);
+ if (!ctl) {
+ ALOGE("%s: Could not get ctl for mixer cmd - %s",
+ __func__, AUDIO_DATA_BLOCK_MIXER_CTL);
+ goto fail;
+ }
+
+ mixer_ctl_update(ctl);
+
+ count = mixer_ctl_get_num_values(ctl);
+
+ /* Read SAD blocks, clamping the maximum size for safety */
+ if (count > (int)sizeof(block))
+ count = (int)sizeof(block);
+
+ ret = mixer_ctl_get_array(ctl, block, count);
+ if (ret != 0) {
+ ALOGE("%s: mixer_ctl_get_array() failed to get EDID info", __func__);
+ goto fail;
+ }
+ edid_data[0] = count;
+ memcpy(&edid_data[1], block, count);
+
+ if (!edid_get_sink_caps(info, edid_data)) {
+ ALOGE("%s: Failed to get HDMI sink capabilities", __func__);
+ goto fail;
+ }
+ my_data->edid_valid = true;
+ return 0;
+fail:
+ if (my_data->edid_info) {
+ free(my_data->edid_info);
+ my_data->edid_info = NULL;
+ my_data->edid_valid = false;
+ }
+ ALOGE("%s: return -EINVAL", __func__);
+ return -EINVAL;
+}
+
+
+int platform_set_channel_allocation(void *platform, int channel_alloc)
+{
+ struct mixer_ctl *ctl;
+ const char *mixer_ctl_name = "HDMI RX CA";
+ int ret;
+ struct platform_data *my_data = (struct platform_data *)platform;
+ struct audio_device *adev = my_data->adev;
+
+ ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
+ if (!ctl) {
+ ALOGE("%s: Could not get ctl for mixer cmd - %s",
+ __func__, mixer_ctl_name);
+ ret = EINVAL;
+ }
+ ALOGD(":%s channel allocation = 0x%x", __func__, channel_alloc);
+ ret = mixer_ctl_set_value(ctl, 0, channel_alloc);
+
+ if (ret < 0) {
+ ALOGE("%s: Could not set ctl, error:%d ", __func__, ret);
+ }
+
+ return ret;
+}
+
+int platform_set_channel_map(void *platform, int ch_count, char *ch_map, int snd_id)
+{
+ struct mixer_ctl *ctl;
+ char mixer_ctl_name[44]; // max length of name is 44 as defined
+ int ret;
+ unsigned int i;
+ int set_values[8] = {0};
+ char device_num[13]; // device number up to 2 digit
+ struct platform_data *my_data = (struct platform_data *)platform;
+ struct audio_device *adev = my_data->adev;
+ ALOGV("%s channel_count:%d",__func__, ch_count);
+ if (NULL == ch_map) {
+ ALOGE("%s: Invalid channel mapping used", __func__);
+ return -EINVAL;
+ }
+ strlcpy(mixer_ctl_name, "Playback Channel Map", sizeof(mixer_ctl_name));
+ if (snd_id >= 0) {
+ snprintf(device_num, sizeof(device_num), "%d", snd_id);
+ strncat(mixer_ctl_name, device_num, 13);
+ }
+
+ ALOGD("%s mixer_ctl_name:%s", __func__, mixer_ctl_name);
+
+ ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
+ if (!ctl) {
+ ALOGE("%s: Could not get ctl for mixer cmd - %s",
+ __func__, mixer_ctl_name);
+ return -EINVAL;
+ }
+ for (i = 0; i< ARRAY_SIZE(set_values); i++) {
+ set_values[i] = ch_map[i];
+ }
+
+ ALOGD("%s: set mapping(%d %d %d %d %d %d %d %d) for channel:%d", __func__,
+ set_values[0], set_values[1], set_values[2], set_values[3], set_values[4],
+ set_values[5], set_values[6], set_values[7], ch_count);
+
+ ret = mixer_ctl_set_array(ctl, set_values, ch_count);
+ if (ret < 0) {
+ ALOGE("%s: Could not set ctl, error:%d ch_count:%d",
+ __func__, ret, ch_count);
+ }
+ return ret;
+}
+
+unsigned char platform_map_to_edid_format(int audio_format)
+{
+
+ unsigned char format;
+ switch (audio_format & AUDIO_FORMAT_MAIN_MASK) {
+ case AUDIO_FORMAT_AC3:
+ ALOGV("%s: AC3", __func__);
+ format = AC3;
+ break;
+ case AUDIO_FORMAT_AAC:
+ ALOGV("%s:AAC", __func__);
+ format = AAC;
+ break;
+ case AUDIO_FORMAT_E_AC3:
+ ALOGV("%s:E_AC3", __func__);
+ format = DOLBY_DIGITAL_PLUS;
+ break;
+ case AUDIO_FORMAT_PCM_16_BIT:
+ default:
+ ALOGV("%s:PCM", __func__);
+ format = LPCM;
+ break;
+ }
+ return format;
+}
+
+bool platform_is_edid_supported_format(void *platform, int format)
+{
+ 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;
+ unsigned char format_id = platform_map_to_edid_format(format);
+
+ 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++) {
+ if (info->audio_blocks_array[i].format_id == format_id)
+ ALOGV("%s:platform_is_edid_supported_format true %x",
+ __func__, format);
+ return true;
+ }
+ }
+ ALOGV("%s:platform_is_edid_supported_format false %x",
+ __func__, format);
+ return false;
+}
+
+int platform_set_edid_channels_configuration(void *platform, int channels) {
+
+ 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 channel_count = 2;
+ int i, ret, count;
+ char default_channelMap[MAX_CHANNELS_SUPPORTED] = {0};
+
+ ret = platform_get_edid_info(platform);
+ info = (edid_audio_info *)my_data->edid_info;
+ if(ret == 0 && info != NULL) {
+ if (channels > 2) {
+
+ ALOGV("%s:able to get HDMI sink capabilities multi channel playback",
+ __func__);
+ for (i = 0; i < info->audio_blocks && i < MAX_EDID_BLOCKS; i++) {
+ if (info->audio_blocks_array[i].format_id == LPCM &&
+ info->audio_blocks_array[i].channels > channel_count &&
+ info->audio_blocks_array[i].channels <= MAX_HDMI_CHANNEL_CNT) {
+ channel_count = info->audio_blocks_array[i].channels;
+ }
+ }
+ ALOGVV("%s:channel_count:%d", __func__, channel_count);
+ /*
+ * Channel map is set for supported hdmi max channel count even
+ * though the input channel count set on adm is less than or equal to
+ * max supported channel count
+ */
+ platform_set_channel_map(platform, channel_count, info->channel_map, -1);
+ platform_set_channel_allocation(platform, info->channel_allocation);
+ } else {
+ default_channelMap[0] = PCM_CHANNEL_FL;
+ default_channelMap[1] = PCM_CHANNEL_FR;
+ platform_set_channel_map(platform,2,default_channelMap,-1);
+ platform_set_channel_allocation(platform,0);
+ }
+ }
+
+ return 0;
+}
+
+void platform_cache_edid(void * platform)
+{
+ platform_get_edid_info(platform);
+}
+
+void platform_invalidate_edid(void * platform)
+{
+ struct platform_data *my_data = (struct platform_data *)platform;
+ my_data->edid_valid = false;
+ if (my_data->edid_info) {
+ memset(my_data->edid_info, 0, sizeof(struct edid_audio_info));
+ }
+}
diff --git a/hal/platform_api.h b/hal/platform_api.h
index 368677b..381901c 100644
--- a/hal/platform_api.h
+++ b/hal/platform_api.h
@@ -96,4 +96,15 @@
void platform_set_echo_reference(void *platform, bool enable);
void platform_get_device_to_be_id_map(int **be_id_map, int *length);
+int platform_set_channel_allocation(void *platform, int channel_alloc);
+int platform_get_edid_info(void *platform);
+int platform_set_channel_map(void *platform, int ch_count, char *ch_map,
+ int snd_id);
+int platform_set_stream_channel_map(void *platform, audio_channel_mask_t channel_mask, int snd_id);
+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);
+void platform_cache_edid(void * platform);
+void platform_invalidate_edid(void * platform);
+
#endif // AUDIO_PLATFORM_API_H