blob: 175842f00e4f44a228332b0076ed6734ab255226 [file] [log] [blame]
/*
* 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;
}