| /* |
| * 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 <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" |
| |
| #ifdef DYNAMIC_LOG_ENABLED |
| #include <log_xml_parser.h> |
| #define LOG_MASK HAL_MOD_FILE_EDID |
| #include <log_utils.h> |
| #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; 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); |
| update_channel_mask(info); |
| |
| for (i=0; i<info->audio_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; |
| } |