audio: Enable External HDMI support

-For HDMI compliance, channel status bits
should be configured for the external HDMI chip.
-Hence HAL based on the output format i.e compressed
or LPCM populates the channel_status information
and passes it to the dai-driver which in turn
configures the external HDMI chip.

Change-Id: Ibf192492d259a722070a08f4764fde9498dd268d
diff --git a/hal/Android.mk b/hal/Android.mk
index 74de942..713f2c1 100644
--- a/hal/Android.mk
+++ b/hal/Android.mk
@@ -54,6 +54,7 @@
 
 ifeq ($(strip $(AUDIO_FEATURE_ENABLED_HDMI_EDID)),true)
     LOCAL_SRC_FILES += edid.c
+    LOCAL_CFLAGS := -DHDMI_EDID_ENABLED
 endif
 
 ifeq ($(strip $(AUDIO_USE_LL_AS_PRIMARY_OUTPUT)),true)
@@ -251,6 +252,12 @@
     LOCAL_SRC_FILES += audio_extn/listen.c
 endif
 
+ifeq ($(strip $(AUDIO_FEATURE_ENABLED_EXT_HDMI)),true)
+    LOCAL_CFLAGS += -DAUDIO_EXTERNAL_HDMI_ENABLED
+    LOCAL_C_INCLUDES += $(TARGET_OUT_HEADERS)/mm-audio/audio-parsers
+    LOCAL_SHARED_LIBRARIES += libaudioparsers
+endif
+
 ifeq ($(strip $(BOARD_SUPPORTS_SOUND_TRIGGER)),true)
     LOCAL_CFLAGS += -DSOUND_TRIGGER_ENABLED
     LOCAL_CFLAGS += -DSOUND_TRIGGER_PLATFORM_NAME=$(TARGET_BOARD_PLATFORM)
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index 8bcc885..c47f5ed 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -486,4 +486,10 @@
 void audio_extn_perf_lock_acquire(void);
 void audio_extn_perf_lock_release(void);
 #endif /* KPI_OPTIMIZE_ENABLED */
+
+#ifndef AUDIO_EXTERNAL_HDMI_ENABLED
+#define setChannelStatus(out, buffer, bytes) (0)
+#else
+void setChannelStatus(struct stream_out *out, char * buffer, size_t bytes);
+#endif
 #endif /* AUDIO_EXTN_H */
diff --git a/hal/audio_extn/utils.c b/hal/audio_extn/utils.c
index a2b644e..c2b2f64 100644
--- a/hal/audio_extn/utils.c
+++ b/hal/audio_extn/utils.c
@@ -35,6 +35,10 @@
 #include "audio_extn.h"
 #include "voice.h"
 
+#ifdef AUDIO_EXTERNAL_HDMI_ENABLED
+#include "audio_parsers.h"
+#endif
+
 #define AUDIO_OUTPUT_POLICY_VENDOR_CONFIG_FILE "/vendor/etc/audio_output_policy.conf"
 
 #define OUTPUTS_TAG "outputs"
@@ -52,6 +56,21 @@
 #define BASE_TABLE_SIZE 64
 #define MAX_BASEINDEX_LEN 256
 
+#ifdef AUDIO_EXTERNAL_HDMI_ENABLED
+#define PROFESSIONAL        (1<<0)	/* 0 = consumer, 1 = professional */
+#define NON_LPCM	    (1<<1)	/* 0 = audio, 1 = non-audio */
+#define SR_44100	    (0<<0)	/* 44.1kHz */
+#define SR_NOTID	    (1<<0)	/* non indicated */
+#define SR_48000	    (2<<0)	/* 48kHz */
+#define SR_32000	    (3<<0)	/* 32kHz */
+#define SR_22050	    (4<<0)	/* 22.05kHz */
+#define SR_24000	    (6<<0)	/* 24kHz */
+#define SR_88200	    (8<<0)	/* 88.2kHz */
+#define SR_96000	    (10<<0)	/* 96kHz */
+#define SR_176400	    (12<<0)	/* 176.4kHz */
+#define SR_192000	    (14<<0)	/* 192kHz */
+
+#endif
 struct string_to_enum {
     const char *name;
     uint32_t value;
@@ -747,3 +766,130 @@
     outp[k] = '\0';
     return k;
 }
+
+#ifdef AUDIO_EXTERNAL_HDMI_ENABLED
+
+void get_default_compressed_channel_status(
+                                  unsigned char *channel_status)
+{
+     int32_t status = 0;
+     unsigned char bit_index;
+     memset(channel_status,0,24);
+
+     /* block start bit in preamble bit 3 */
+     channel_status[0] |= PROFESSIONAL;
+     //compre out
+     channel_status[0] |= NON_LPCM;
+     // sample rate; fixed 48K for default/transcode
+     channel_status[3] |= SR_48000;
+}
+
+int32_t get_compressed_channel_status(void *audio_stream_data,
+                                                   uint32_t audio_frame_size,
+                                                   unsigned char *channel_status,
+                                                   enum audio_parser_code_type codec_type)
+                                                   // codec_type - AUDIO_PARSER_CODEC_AC3
+                                                   //            - AUDIO_PARSER_CODEC_DTS
+{
+     unsigned char *streamPtr;
+     int ret = 0;
+     streamPtr = (unsigned char *)audio_stream_data;
+
+     if (audio_stream_data == NULL || audio_frame_size == 0) {
+         ALOGW("no buffer to get channel status, return default for compress");
+         get_default_compressed_channel_status(channel_status);
+         return ret;
+     }
+
+     memset(channel_status,0,24);
+     if(init_audio_parser(streamPtr, audio_frame_size, codec_type) == -1)
+     {
+         ALOGE("init audio parser failed");
+         return -1;
+     }
+     ret = get_channel_status(channel_status, codec_type);
+     return ret;
+
+}
+
+void get_linearpcm_channel_status(uint32_t sampleRate,
+                                                  unsigned char *channel_status)
+{
+     int32_t status = 0;
+     unsigned char bit_index;
+     memset(channel_status,0,24);
+     /* block start bit in preamble bit 3 */
+     channel_status[0] |= PROFESSIONAL;
+     //LPCM OUT
+     channel_status[0] &= ~NON_LPCM;
+
+     switch (sampleRate) {
+         case 8000:
+         case 11025:
+         case 12000:
+         case 16000:
+         case 22050:
+             channel_status[3] |= SR_NOTID;
+         case 24000:
+             channel_status[3] |= SR_24000;
+             break;
+         case 32000:
+             channel_status[3] |= SR_32000;
+             break;
+         case 44100:
+             channel_status[3] |= SR_44100;
+             break;
+         case 48000:
+             channel_status[3] |= SR_48000;
+             break;
+         case 88200:
+             channel_status[3] |= SR_88200;
+             break;
+         case 96000:
+             channel_status[3] |= SR_96000;
+             break;
+         case 176400:
+             channel_status[3] |= SR_176400;
+             break;
+         case 192000:
+            channel_status[3] |= SR_192000;
+             break;
+         default:
+             ALOGV("Invalid sample_rate %u\n", sampleRate);
+             status = -1;
+             break;
+     }
+}
+
+void setChannelStatus(struct stream_out *out, char * buffer, size_t bytes)
+{
+    unsigned char channel_status[24]={0};
+    struct snd_aes_iec958 iec958;
+    const char *mixer_ctl_name = "IEC958 Playback PCM Stream";
+    struct mixer_ctl *ctl;
+    int i=0;
+    if (audio_extn_is_dolby_format(out->format) &&
+        /*TODO:Extend code to support DTS passthrough*/
+        /*set compressed channel status bits*/
+        audio_extn_dolby_is_passthrough_stream(out->flags)){
+        get_compressed_channel_status(buffer, bytes, channel_status, AUDIO_PARSER_CODEC_AC3);
+    } else {
+        /*set channel status bit for LPCM*/
+        get_linearpcm_channel_status(out->sample_rate, channel_status);
+    }
+
+    memcpy(iec958.status, channel_status,sizeof(iec958.status));
+    ctl = mixer_get_ctl_by_name(out->dev->mixer, mixer_ctl_name);
+    if (!ctl) {
+            ALOGE("%s: Could not get ctl for mixer cmd - %s",
+                  __func__, mixer_ctl_name);
+            return;
+    }
+    if (mixer_ctl_set_array(ctl, &iec958, sizeof(iec958)) < 0) {
+        ALOGE("%s: Could not set channel status for ext HDMI ",
+              __func__);
+        return;
+    }
+
+}
+#endif
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 3286e6b..ece0a04 100755
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -564,6 +564,9 @@
             audio_route_reset_and_update_path(adev->audio_route, device_name);
         }
 
+        if (snd_device == SND_DEVICE_OUT_HDMI)
+            adev->mChannelStatusSet = false;
+
         audio_extn_dev_arbi_release(snd_device);
         audio_extn_sound_trigger_update_device_status(snd_device,
                                         ST_EVENT_SND_DEVICE_FREE);
@@ -2127,6 +2130,11 @@
         }
     }
 
+    if (adev->mChannelStatusSet == false && (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL)){
+        setChannelStatus(out, buffer, bytes);
+        adev->mChannelStatusSet = true;
+    }
+
     if (is_offload_usecase(out->usecase)) {
         ALOGVV("copl(%p): writing buffer (%zu bytes) to compress device", out, bytes);
         if (out->send_new_metadata) {
@@ -2783,7 +2791,6 @@
     out->bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH;
     out->non_blocking = 0;
     out->use_small_bufs = false;
-
     /* Init use case and pcm_config */
     if ((out->flags & AUDIO_OUTPUT_FLAG_DIRECT) &&
         !(out->flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) &&
@@ -3665,6 +3672,7 @@
     list_init(&adev->usecase_list);
     adev->cur_wfd_channels = 2;
     adev->offload_usecases_state = 0;
+    adev->mChannelStatusSet = false;
 
     pthread_mutex_init(&adev->snd_card_status.lock, (const pthread_mutexattr_t *) NULL);
     adev->snd_card_status.state = SND_CARD_STATE_OFFLINE;
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index 67f5279..45e90b7 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -311,6 +311,7 @@
     int snd_card;
     unsigned int cur_codec_backend_samplerate;
     unsigned int cur_codec_backend_bit_width;
+    bool mChannelStatusSet;
     void *platform;
     unsigned int offload_usecases_state;
     void *visualizer_lib;
diff --git a/hal/msm8916/platform.c b/hal/msm8916/platform.c
index 72f4685..a2310e4 100644
--- a/hal/msm8916/platform.c
+++ b/hal/msm8916/platform.c
@@ -4024,14 +4024,15 @@
     edid_data[0] = count;
     memcpy(&edid_data[1], block, count);
 
-#ifdef AUDIO_FEATURE_ENABLED_HDMI_EDID
+#ifdef HDMI_EDID_ENABLED
     if (!edid_get_sink_caps(info, edid_data)) {
         ALOGE("%s: Failed to get HDMI sink capabilities", __func__);
         goto fail;
     }
+#endif
+
     my_data->edid_valid = true;
     return 0;
-#endif
 fail:
     if (my_data->edid_info) {
         free(my_data->edid_info);