/* * Copyright (c) 2014, 2016-2017, 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 #include #include #include #include #include #include "audio_hw.h" #include "platform.h" #include "platform_api.h" #include "edid.h" #ifdef DYNAMIC_LOG_ENABLED #include #define LOG_MASK HAL_MOD_FILE_EDID #include #endif 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 bool is_supported_sr(unsigned char sr_byte, int sampling_rate ) { int result = 0; ALOGV("%s: sr_byte: %d, sampling_freq: %d",__func__, sr_byte, sampling_rate); switch (sampling_rate) { case 192000: result = (sr_byte & BIT(6)); break; case 176400: result = (sr_byte & BIT(5)); break; case 96000: result = (sr_byte & BIT(4)); break; case 88200: result = (sr_byte & BIT(3)); break; case 48000: result = (sr_byte & BIT(2)); break; case 44100: result = (sr_byte & BIT(1)); break; case 32000: result = (sr_byte & BIT(0)); break; default: break; } if (result) return true; return false; } static unsigned char get_edid_bps_byte(unsigned char byte, unsigned char format) { if (format == 0) { ALOGV("%s: not lpcm format, return 0",__func__); return 0; } return byte; } static bool is_supported_bps(unsigned char bps_byte, int bps) { int result = 0; switch (bps) { case 24: ALOGV("24bit"); result = (bps_byte & BIT(2)); break; case 20: ALOGV("20bit"); result = (bps_byte & BIT(1)); break; case 16: ALOGV("16bit"); result = (bps_byte & BIT(0)); break; default: break; } if (result) return true; return false; } static int get_highest_edid_sf(unsigned char byte) { int nfreq = 0; if (byte & BIT(6)) { ALOGV("Highest: 192kHz"); nfreq = 192000; } else if (byte & BIT(5)) { ALOGV("Highest: 176kHz"); nfreq = 176000; } else if (byte & BIT(4)) { ALOGV("Highest: 96kHz"); nfreq = 96000; } else if (byte & BIT(3)) { ALOGV("Highest: 88.2kHz"); nfreq = 88200; } else if (byte & BIT(2)) { ALOGV("Highest: 48kHz"); nfreq = 48000; } else if (byte & BIT(1)) { ALOGV("Highest: 44.1kHz"); nfreq = 44100; } else if (byte & BIT(0)) { ALOGV("Highest: 32kHz"); nfreq = 32000; } return nfreq; } 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)) && (info->channel_allocation != 0x2f)) { 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; case 0x2f: 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] = 0; // PCM_CHANNEL_TFL; but not defined by LPASS info->channel_map[7] = 0; // PCM_CHANNEL_TFR; but not defined by LPASS 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 update_channel_mask(edid_audio_info* info) { if (!info) return; if (((info->channel_allocation < 0) || (info->channel_allocation > 0x1f)) && (info->channel_allocation != 0x2f)) { ALOGE("Channel allocation out of supported range"); return; } ALOGV("channel_allocation 0x%x", info->channel_allocation); // Don't distinguish channel mask below? // AUDIO_CHANNEL_OUT_5POINT1 and AUDIO_CHANNEL_OUT_5POINT1_SIDE // AUDIO_CHANNEL_OUT_QUAD and AUDIO_CHANNEL_OUT_QUAD_SIDE switch(info->channel_allocation) { case 0x0: info->channel_mask = AUDIO_CHANNEL_OUT_STEREO; break; case 0x1: info->channel_mask = AUDIO_CHANNEL_OUT_2POINT1; break; case 0x2: info->channel_mask = AUDIO_CHANNEL_OUT_STEREO; info->channel_mask |= AUDIO_CHANNEL_OUT_FRONT_CENTER; break; case 0x3: info->channel_mask = AUDIO_CHANNEL_OUT_2POINT1; info->channel_mask |= AUDIO_CHANNEL_OUT_FRONT_CENTER; break; case 0x4: info->channel_mask = AUDIO_CHANNEL_OUT_STEREO; info->channel_mask |= AUDIO_CHANNEL_OUT_BACK_CENTER; break; case 0x5: info->channel_mask = AUDIO_CHANNEL_OUT_2POINT1; info->channel_mask |= AUDIO_CHANNEL_OUT_LOW_FREQUENCY; info->channel_mask |= AUDIO_CHANNEL_OUT_BACK_CENTER; break; case 0x6: info->channel_mask = AUDIO_CHANNEL_OUT_SURROUND; break; case 0x7: info->channel_mask = AUDIO_CHANNEL_OUT_SURROUND; info->channel_mask |= AUDIO_CHANNEL_OUT_LOW_FREQUENCY; break; case 0x8: info->channel_mask = AUDIO_CHANNEL_OUT_QUAD; break; case 0x9: info->channel_mask = AUDIO_CHANNEL_OUT_QUAD; info->channel_mask |= AUDIO_CHANNEL_OUT_LOW_FREQUENCY; break; case 0xa: info->channel_mask = AUDIO_CHANNEL_OUT_PENTA; break; case 0xb: info->channel_mask = AUDIO_CHANNEL_OUT_5POINT1; break; case 0xc: info->channel_mask = AUDIO_CHANNEL_OUT_QUAD; info->channel_mask |= AUDIO_CHANNEL_OUT_BACK_CENTER; break; case 0xd: info->channel_mask = AUDIO_CHANNEL_OUT_QUAD; info->channel_mask |= AUDIO_CHANNEL_OUT_LOW_FREQUENCY; info->channel_mask |= AUDIO_CHANNEL_OUT_BACK_CENTER; break; case 0xe: info->channel_mask = AUDIO_CHANNEL_OUT_PENTA; info->channel_mask |= AUDIO_CHANNEL_OUT_BACK_CENTER; break; case 0xf: info->channel_mask = AUDIO_CHANNEL_OUT_5POINT1; info->channel_mask |= AUDIO_CHANNEL_OUT_BACK_CENTER; break; case 0x10: info->channel_mask = AUDIO_CHANNEL_OUT_QUAD; info->channel_mask |= AUDIO_CHANNEL_OUT_SIDE_LEFT; info->channel_mask |= AUDIO_CHANNEL_OUT_SIDE_RIGHT; break; case 0x11: info->channel_mask = AUDIO_CHANNEL_OUT_QUAD; info->channel_mask |= AUDIO_CHANNEL_OUT_LOW_FREQUENCY; info->channel_mask |= AUDIO_CHANNEL_OUT_SIDE_LEFT; info->channel_mask |= AUDIO_CHANNEL_OUT_SIDE_RIGHT; break; case 0x12: info->channel_mask = AUDIO_CHANNEL_OUT_QUAD; info->channel_mask |= AUDIO_CHANNEL_OUT_FRONT_CENTER; info->channel_mask |= AUDIO_CHANNEL_OUT_SIDE_LEFT; info->channel_mask |= AUDIO_CHANNEL_OUT_SIDE_RIGHT; break; case 0x13: info->channel_mask = AUDIO_CHANNEL_OUT_7POINT1; break; case 0x14: info->channel_mask = AUDIO_CHANNEL_OUT_FRONT_LEFT; info->channel_mask |= AUDIO_CHANNEL_OUT_FRONT_RIGHT; info->channel_mask |= AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER; info->channel_mask |= AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER; break; case 0x15: info->channel_mask = AUDIO_CHANNEL_OUT_2POINT1; info->channel_mask |= AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER; info->channel_mask |= AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER; break; case 0x16: info->channel_mask = AUDIO_CHANNEL_OUT_FRONT_LEFT; info->channel_mask |= AUDIO_CHANNEL_OUT_FRONT_RIGHT; info->channel_mask |= AUDIO_CHANNEL_OUT_FRONT_CENTER; info->channel_mask |= AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER; info->channel_mask |= AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER; break; case 0x17: info->channel_mask = AUDIO_CHANNEL_OUT_2POINT1; info->channel_mask |= AUDIO_CHANNEL_OUT_FRONT_CENTER; info->channel_mask |= AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER; info->channel_mask |= AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER; break; case 0x18: info->channel_mask = AUDIO_CHANNEL_OUT_FRONT_LEFT; info->channel_mask |= AUDIO_CHANNEL_OUT_FRONT_RIGHT; info->channel_mask |= AUDIO_CHANNEL_OUT_BACK_CENTER; info->channel_mask |= AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER; info->channel_mask |= AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER; break; case 0x19: info->channel_mask = AUDIO_CHANNEL_OUT_2POINT1; info->channel_mask |= AUDIO_CHANNEL_OUT_BACK_CENTER; info->channel_mask |= AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER; info->channel_mask |= AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER; break; case 0x1a: info->channel_mask = AUDIO_CHANNEL_OUT_SURROUND; info->channel_mask |= AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER; info->channel_mask |= AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER; break; case 0x1b: info->channel_mask = AUDIO_CHANNEL_OUT_SURROUND; info->channel_mask |= AUDIO_CHANNEL_OUT_LOW_FREQUENCY; info->channel_mask |= AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER; info->channel_mask |= AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER; break; case 0x1c: info->channel_mask = AUDIO_CHANNEL_OUT_QUAD; info->channel_mask |= AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER; info->channel_mask |= AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER; break; case 0x1d: info->channel_mask = AUDIO_CHANNEL_OUT_QUAD; info->channel_mask |= AUDIO_CHANNEL_OUT_LOW_FREQUENCY; info->channel_mask |= AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER; info->channel_mask |= AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER; break; case 0x1e: info->channel_mask = AUDIO_CHANNEL_OUT_PENTA; info->channel_mask |= AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER; info->channel_mask |= AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER; break; case 0x1f: info->channel_mask = AUDIO_CHANNEL_OUT_5POINT1; info->channel_mask |= AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER; info->channel_mask |= AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER; break; case 0x2f: info->channel_mask = AUDIO_CHANNEL_OUT_5POINT1POINT2; break; default: break; } ALOGD("%s channel mask updated to %d", __func__, info->channel_mask); } 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_bitmask, info->audio_blocks_array[i].bits_per_sample_bitmask, 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; iaudio_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); update_channel_mask(info); for (i=0; iaudio_blocks; i++) { ALOGV("AUDIO DESC BLOCK # %d\n",i); info->audio_blocks_array[i].channels = channels[i]; ALOGD("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]; ALOGD("info->audio_blocks_array[i].format_id %s", edid_format_to_str(formats[i])); ALOGV("Frequency Bitmask %d\n", frequency[i]); info->audio_blocks_array[i].sampling_freq_bitmask = frequency[i]; ALOGV("info->audio_blocks_array[i].sampling_freq_bitmask %d", info->audio_blocks_array[i].sampling_freq_bitmask); ALOGV("BitsPerSample Bitmask %d\n", bitrate[i]); info->audio_blocks_array[i].bits_per_sample_bitmask = get_edid_bps_byte(bitrate[i],formats[i]); ALOGV("info->audio_blocks_array[i].bits_per_sample_bitmask %d", info->audio_blocks_array[i].bits_per_sample_bitmask); } dump_speaker_allocation(info); dump_edid_data(info); return true; } bool edid_is_supported_sr(edid_audio_info* info, int sr) { int i = 0; if (info != NULL && sr != 0) { for (i = 0; i < info->audio_blocks && i < MAX_EDID_BLOCKS; i++) { if (is_supported_sr(info->audio_blocks_array[i].sampling_freq_bitmask , sr)) { ALOGV("%s: returns true for sample rate [%d]", __func__, sr); return true; } } } ALOGV("%s: returns false for sample rate [%d]", __func__, sr); return false; } bool edid_is_supported_bps(edid_audio_info* info, int bps) { int i = 0; if (bps == 16) { //16 bit bps is always supported //some oem may not update 16bit support in their edid info return true; } if (info != NULL && bps != 0) { for (i = 0; i < info->audio_blocks && i < MAX_EDID_BLOCKS; i++) { if (is_supported_bps(info->audio_blocks_array[i].bits_per_sample_bitmask, bps)) { ALOGV("%s: returns true for bit width [%d]", __func__, bps); return true; } } } ALOGV("%s: returns false for bit width [%d]", __func__, bps); return false; } int edid_get_highest_supported_sr(edid_audio_info* info) { int sr = 0; int highest_sr = 0; int i; if (info != NULL) { for (i = 0; i < info->audio_blocks && i < MAX_EDID_BLOCKS; i++) { sr = get_highest_edid_sf(info->audio_blocks_array[i].sampling_freq_bitmask); if (sr > highest_sr) highest_sr = sr; } } else ALOGE("%s: info is NULL", __func__); ALOGV("%s: returns [%d] for highest supported sr", __func__, highest_sr); return highest_sr; }