Merge "hal: Add support for parsing hdmi edid information"
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 4e33dfb..3dea397 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,
@@ -2960,6 +2985,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 6184e5e..1683d0a 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] = {
@@ -1013,6 +1022,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"))) {
@@ -1167,6 +1177,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();
@@ -2149,56 +2164,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;
 }
 
@@ -2855,3 +2842,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