Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b357a77d authored by SathishKumar Mani's avatar SathishKumar Mani Committed by Iliyan Malchev
Browse files

alsa_sound: Add hdmi audio sink capability discovery



- Add hdmi sink capabilities parsing utility to support
  multi-channel output configuration.
- Update getParameters to calculate supported channels by
  hdmi sink.
- Update alsa_default to calculate channel count to set control
  option.

Bug: 7156174
Change-Id: Iabb9844c1e5a8b7aa7f168992f8beef79b7df8d2
Signed-off-by: default avatarIliyan Malchev <malchev@google.com>
parent 87459f08
Loading
Loading
Loading
Loading
+36 −1
Original line number Diff line number Diff line
@@ -33,12 +33,16 @@
#include <cutils/properties.h>
#include <media/AudioRecord.h>
#include <hardware_legacy/power.h>

#include "AudioUtil.h"
#include "AudioHardwareALSA.h"

namespace android_audio_legacy
{

// unused 'enumVal;' is to catch error at compile time if enumVal ever changes
// or applied on a non-existent enum
#define ENUM_TO_STRING(var, enumVal) {var = #enumVal; enumVal;}

// ----------------------------------------------------------------------------

ALSAStreamOps::ALSAStreamOps(AudioHardwareALSA *parent, alsa_handle_t *handle) :
@@ -254,6 +258,37 @@ String8 ALSAStreamOps::getParameters(const String8& keys)
        }
#endif
    }
    key = String8(AUDIO_PARAMETER_STREAM_SUP_CHANNELS);
    if (param.get(key, value) == NO_ERROR) {
        EDID_AUDIO_INFO info = { 0 };
        bool first = true;
        value = String8();
        if (AudioUtil::getHDMIAudioSinkCaps(&info)) {
            for (int i = 0; i < info.nAudioBlocks && i < MAX_EDID_BLOCKS; i++) {
                String8 append;
                switch (info.AudioBlocksArray[i].nChannels) {
                //Do not handle stereo output in Multi-channel cases
                //Stereo case is handled in normal playback path
                case 6:
                    ENUM_TO_STRING(append, AUDIO_CHANNEL_OUT_5POINT1);
                    break;
                case 8:
                    ENUM_TO_STRING(append, AUDIO_CHANNEL_OUT_7POINT1);
                    break;
                default:
                    ALOGD("Unsupported number of channels %d", info.AudioBlocksArray[i].nChannels);
                    break;
                }
                if (!append.isEmpty()) {
                    value += (first ? append : String8("|") + append);
                    first = false;
                }
            }
        } else {
            ALOGE("Failed to get HDMI sink capabilities");
        }
        param.add(key, value);
    }
    ALOGV("getParameters() %s", param.toString().string());
    return param.toString();
}
+4 −2
Original line number Diff line number Diff line
@@ -28,7 +28,8 @@ LOCAL_SRC_FILES := \
  AudioStreamInALSA.cpp 	\
  ALSAStreamOps.cpp		\
  audio_hw_hal.cpp \
  AudioUsbALSA.cpp
  AudioUsbALSA.cpp \
  AudioUtil.cpp

LOCAL_STATIC_LIBRARIES := \
    libmedia_helper \
@@ -127,7 +128,8 @@ LOCAL_C_INCLUDES += $(TARGET_OUT_HEADERS)/mm-audio/libalsa-intf

LOCAL_SRC_FILES:= \
    alsa_default.cpp \
    ALSAControl.cpp
    ALSAControl.cpp \
    AudioUtil.cpp

LOCAL_SHARED_LIBRARIES := \
    libcutils \
+279 −0
Original line number Diff line number Diff line
/* AudioUtil.cpp
 *
 * Copyright (C) 2012 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 "AudioUtil"
//#define LOG_NDEBUG 0
#include <utils/Log.h>

#include "AudioUtil.h"

int AudioUtil::printFormatFromEDID(unsigned char format) {
    switch (format) {
    case LPCM:
        ALOGV("Format:LPCM");
        break;
    case AC3:
        ALOGV("Format:AC-3");
        break;
    case MPEG1:
        ALOGV("Format:MPEG1 (Layers 1 & 2)");
        break;
    case MP3:
        ALOGV("Format:MP3 (MPEG1 Layer 3)");
        break;
    case MPEG2_MULTI_CHANNEL:
        ALOGV("Format:MPEG2 (multichannel)");
        break;
    case AAC:
        ALOGV("Format:AAC");
        break;
    case DTS:
        ALOGV("Format:DTS");
        break;
    case ATRAC:
        ALOGV("Format:ATRAC");
        break;
    case SACD:
        ALOGV("Format:One-bit audio aka SACD");
        break;
    case DOLBY_DIGITAL_PLUS:
        ALOGV("Format:Dolby Digital +");
        break;
    case DTS_HD:
        ALOGV("Format:DTS-HD");
        break;
    case MAT:
        ALOGV("Format:MAT (MLP)");
        break;
    case DST:
        ALOGV("Format:DST");
        break;
    case WMA_PRO:
        ALOGV("Format:WMA Pro");
        break;
    default:
        ALOGV("Invalid format ID....");
        break;
    }
    return format;
}

int AudioUtil::getSamplingFrequencyFromEDID(unsigned char byte) {
    int nFreq = 0;

    if (byte & BIT(6)) {
        ALOGV("192kHz");
        nFreq = 192000;
    } else if (byte & BIT(5)) {
        ALOGV("176kHz");
        nFreq = 176000;
    } else if (byte & BIT(4)) {
        ALOGV("96kHz");
        nFreq = 96000;
    } else if (byte & BIT(3)) {
        ALOGV("88.2kHz");
        nFreq = 88200;
    } else if (byte & BIT(2)) {
        ALOGV("48kHz");
        nFreq = 48000;
    } else if (byte & BIT(1)) {
        ALOGV("44.1kHz");
        nFreq = 44100;
    } else if (byte & BIT(0)) {
        ALOGV("32kHz");
        nFreq = 32000;
    }
    return nFreq;
}

int AudioUtil::getBitsPerSampleFromEDID(unsigned char byte,
    unsigned char format) {
    int nBitsPerSample = 0;
    if (format == 1) {
        if (byte & BIT(2)) {
            ALOGV("24bit");
            nBitsPerSample = 24;
        } else if (byte & BIT(1)) {
            ALOGV("20bit");
            nBitsPerSample = 20;
        } else if (byte & BIT(0)) {
            ALOGV("16bit");
            nBitsPerSample = 16;
        }
    } else {
        ALOGV("not lpcm format, return 0");
        return 0;
    }
    return nBitsPerSample;
}

bool AudioUtil::getHDMIAudioSinkCaps(EDID_AUDIO_INFO* pInfo) {
    unsigned char channels[16];
    unsigned char formats[16];
    unsigned char frequency[16];
    unsigned char bitrate[16];
    unsigned char* data = NULL;
    unsigned char* original_data_ptr = NULL;
    int count = 0;
    bool bRet = false;
    const char* file = "/sys/class/graphics/fb1/audio_data_block";
    FILE* fpaudiocaps = fopen(file, "rb");
    if (fpaudiocaps) {
        ALOGV("opened audio_caps successfully...");
        fseek(fpaudiocaps, 0, SEEK_END);
        long size = ftell(fpaudiocaps);
        ALOGV("audiocaps size is %ld\n",size);
        data = (unsigned char*) malloc(size);
        if (data) {
            fseek(fpaudiocaps, 0, SEEK_SET);
            original_data_ptr = data;
            fread(data, 1, size, fpaudiocaps);
        }
        fclose(fpaudiocaps);
    } else {
        ALOGE("failed to open audio_caps");
    }

    if (pInfo && data) {
        int length = 0;
        memcpy(&count,  data, sizeof(int));
        data+= sizeof(int);
        ALOGV("#Audio Block Count is %d",count);
        memcpy(&length, data, sizeof(int));
        data += sizeof(int);
        ALOGV("Total length is %d",length);
        unsigned int sad[MAX_SHORT_AUDIO_DESC_CNT];
        int nblockindex = 0;
        int nCountDesc = 0;
        while (length >= MIN_AUDIO_DESC_LENGTH && count < MAX_SHORT_AUDIO_DESC_CNT) {
            sad[nblockindex] = (unsigned int)data[0] + ((unsigned int)data[1] << 8)
                               + ((unsigned int)data[2] << 16);
            nblockindex+=1;
            nCountDesc++;
            length -= MIN_AUDIO_DESC_LENGTH;
            data += MIN_AUDIO_DESC_LENGTH;
        }
        memset(pInfo, 0, sizeof(EDID_AUDIO_INFO));
        pInfo->nAudioBlocks = nCountDesc;
        ALOGV("Total # of audio descriptors %d",nCountDesc);
        int nIndex = 0;
        while (nCountDesc--) {
              channels [nIndex]   = (sad[nIndex] & 0x7) + 1;
              formats  [nIndex]   = (sad[nIndex] & 0xFF) >> 3;
              frequency[nIndex]   = (sad[nIndex] >> 8) & 0xFF;
              bitrate  [nIndex]   = (sad[nIndex] >> 16) & 0xFF;
              nIndex++;
        }
        bRet = true;
        for (int i = 0; i < pInfo->nAudioBlocks; i++) {
            ALOGV("AUDIO DESC BLOCK # %d\n",i);

            pInfo->AudioBlocksArray[i].nChannels = channels[i];
            ALOGV("pInfo->AudioBlocksArray[i].nChannels %d\n", pInfo->AudioBlocksArray[i].nChannels);

            ALOGV("Format Byte %d\n", formats[i]);
            pInfo->AudioBlocksArray[i].nFormatId = (EDID_AUDIO_FORMAT_ID)printFormatFromEDID(formats[i]);
            ALOGV("pInfo->AudioBlocksArray[i].nFormatId %d",pInfo->AudioBlocksArray[i].nFormatId);

            ALOGV("Frequency Byte %d\n", frequency[i]);
            pInfo->AudioBlocksArray[i].nSamplingFreq = getSamplingFrequencyFromEDID(frequency[i]);
            ALOGV("pInfo->AudioBlocksArray[i].nSamplingFreq %d",pInfo->AudioBlocksArray[i].nSamplingFreq);

            ALOGV("BitsPerSample Byte %d\n", bitrate[i]);
            pInfo->AudioBlocksArray[i].nBitsPerSample = getBitsPerSampleFromEDID(bitrate[i],formats[i]);
            ALOGV("pInfo->AudioBlocksArray[i].nBitsPerSample %d",pInfo->AudioBlocksArray[i].nBitsPerSample);
        }
            getSpeakerAllocation(pInfo);
    }
    if (original_data_ptr)
        free(original_data_ptr);

    return bRet;
}

bool AudioUtil::getSpeakerAllocation(EDID_AUDIO_INFO* pInfo) {
    int count = 0;
    bool bRet = false;
    unsigned char* data = NULL;
    unsigned char* original_data_ptr = NULL;
    const char* spkrfile = "/sys/class/graphics/fb1/spkr_alloc_data_block";
    FILE* fpspkrfile = fopen(spkrfile, "rb");
    if(fpspkrfile) {
        ALOGV("opened spkr_alloc_data_block successfully...");
        fseek(fpspkrfile,0,SEEK_END);
        long size = ftell(fpspkrfile);
        ALOGV("fpspkrfile size is %ld\n",size);
        data = (unsigned char*)malloc(size);
        if(data) {
            original_data_ptr = data;
            fseek(fpspkrfile,0,SEEK_SET);
            fread(data,1,size,fpspkrfile);
        }
        fclose(fpspkrfile);
    } else {
        ALOGE("failed to open fpspkrfile");
    }

    if(pInfo && data) {
        int length = 0;
        memcpy(&count,  data, sizeof(int));
        ALOGV("Count is %d",count);
        data += sizeof(int);
        memcpy(&length, data, sizeof(int));
        ALOGV("Total length is %d",length);
        data+= sizeof(int);
        ALOGV("Total speaker allocation Block count # %d\n",count);
        bRet = true;
        for (int i = 0; i < count; i++) {
            ALOGV("Speaker Allocation BLOCK # %d\n",i);
            pInfo->nSpeakerAllocation[0] = data[0];
            pInfo->nSpeakerAllocation[1] = data[1];
            pInfo->nSpeakerAllocation[2] = data[2];
            ALOGV("pInfo->nSpeakerAllocation %x %x %x\n", data[0],data[1],data[2]);


            if (pInfo->nSpeakerAllocation[0] & BIT(7)) {
                 ALOGV("FLW/FRW");
            } else if (pInfo->nSpeakerAllocation[0] & BIT(6)) {
                 ALOGV("RLC/RRC");
            } else if (pInfo->nSpeakerAllocation[0] & BIT(5)) {
                 ALOGV("FLC/FRC");
            } else if (pInfo->nSpeakerAllocation[0] & BIT(4)) {
                ALOGV("RC");
            } else if (pInfo->nSpeakerAllocation[0] & BIT(3)) {
                ALOGV("RL/RR");
            } else if (pInfo->nSpeakerAllocation[0] & BIT(2)) {
                ALOGV("FC");
            } else if (pInfo->nSpeakerAllocation[0] & BIT(1)) {
                ALOGV("LFE");
            } else if (pInfo->nSpeakerAllocation[0] & BIT(0)) {
                ALOGV("FL/FR");
            }

            if (pInfo->nSpeakerAllocation[1] & BIT(2)) {
                ALOGV("FCH");
            } else if (pInfo->nSpeakerAllocation[1] & BIT(1)) {
                ALOGV("TC");
            } else if (pInfo->nSpeakerAllocation[1] & BIT(0)) {
                ALOGV("FLH/FRH");
            }
        }
    }
    if (original_data_ptr)
        free(original_data_ptr);
    return bRet;
}

alsa_sound/AudioUtil.h

0 → 100644
+71 −0
Original line number Diff line number Diff line
/* AudioUtil.h
 *
 * Copyright (C) 2012 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.
 */

#ifndef ALSA_SOUND_AUDIO_UTIL_H
#define ALSA_SOUND_AUDIO_UTIL_H

#define BIT(nr)     (1UL << (nr))
#define MAX_EDID_BLOCKS 10
#define MAX_SHORT_AUDIO_DESC_CNT        30
#define MIN_AUDIO_DESC_LENGTH           3
#define MIN_SPKR_ALLOCATION_DATA_LENGTH 3

typedef enum EDID_AUDIO_FORMAT_ID {
    LPCM = 1,
    AC3,
    MPEG1,
    MP3,
    MPEG2_MULTI_CHANNEL,
    AAC,
    DTS,
    ATRAC,
    SACD,
    DOLBY_DIGITAL_PLUS,
    DTS_HD,
    MAT,
    DST,
    WMA_PRO
} EDID_AUDIO_FORMAT_ID;

typedef struct EDID_AUDIO_BLOCK_INFO {
    EDID_AUDIO_FORMAT_ID nFormatId;
    int nSamplingFreq;
    int nBitsPerSample;
    int nChannels;
} EDID_AUDIO_BLOCK_INFO;

typedef struct EDID_AUDIO_INFO {
    int nAudioBlocks;
    unsigned char nSpeakerAllocation[MIN_SPKR_ALLOCATION_DATA_LENGTH];
    EDID_AUDIO_BLOCK_INFO AudioBlocksArray[MAX_EDID_BLOCKS];
} EDID_AUDIO_INFO;

class AudioUtil {
public:

    //Parses EDID audio block when if HDMI is connected to determine audio sink capabilities.
    static bool getHDMIAudioSinkCaps(EDID_AUDIO_INFO*);

private:
    static int printFormatFromEDID(unsigned char format);
    static int getSamplingFrequencyFromEDID(unsigned char byte);
    static int getBitsPerSampleFromEDID(unsigned char byte,
        unsigned char format);
    static bool getSpeakerAllocation(EDID_AUDIO_INFO* pInfo);
};

#endif /* ALSA_SOUND_AUDIO_UTIL_H */
+39 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
#include <utils/Log.h>
#include <cutils/properties.h>
#include <linux/ioctl.h>
#include "AudioUtil.h"
#include "AudioHardwareALSA.h"
#include <media/AudioRecord.h>
#include <dlfcn.h>
@@ -46,6 +47,7 @@ static int (*csd_stop_voice)();
#define BTSCO_RATE_16KHZ 16000
#define USECASE_TYPE_RX 1
#define USECASE_TYPE_TX 2
#define MAX_HDMI_CHANNEL_CNT 6

namespace android_audio_legacy
{
@@ -226,6 +228,36 @@ int deviceName(alsa_handle_t *handle, unsigned flags, char **value)
    return ret;
}

status_t setHDMIChannelCount()
{
    status_t err = NO_ERROR;
    int channel_count = 0;
    const char *channel_cnt_str = NULL;
    EDID_AUDIO_INFO info = { 0 };

    ALSAControl control("/dev/snd/controlC0");
    if (AudioUtil::getHDMIAudioSinkCaps(&info)) {
        for (int i = 0; i < info.nAudioBlocks && i < MAX_EDID_BLOCKS; i++) {
            if (info.AudioBlocksArray[i].nChannels > channel_count &&
                  info.AudioBlocksArray[i].nChannels <= MAX_HDMI_CHANNEL_CNT) {
                channel_count = info.AudioBlocksArray[i].nChannels;
            }
        }
    }

    switch (channel_count) {
    case 6: channel_cnt_str = "Six"; break;
    case 5: channel_cnt_str = "Five"; break;
    case 4: channel_cnt_str = "Four"; break;
    case 3: channel_cnt_str = "Three"; break;
    default: channel_cnt_str = "Two"; break;
    }
    ALOGD("HDMI channel count: %s", channel_cnt_str);
    control.set("HDMI_RX Channels", channel_cnt_str);

    return err;
}

status_t setHardwareParams(alsa_handle_t *handle)
{
    struct snd_pcm_hw_params *params;
@@ -608,6 +640,13 @@ static status_t s_open(alsa_handle_t *handle)
    unsigned flags = 0;
    int err = NO_ERROR;

    if(handle->devices & AudioSystem::DEVICE_OUT_AUX_DIGITAL) {
        err = setHDMIChannelCount();
        if(err != OK) {
            ALOGE("setHDMIChannelCount err = %d", err);
            return err;
        }
    }
    /* No need to call s_close for LPA as pcm device open and close is handled by LPAPlayer in stagefright */
    if((!strcmp(handle->useCase, SND_USE_CASE_VERB_HIFI_LOW_POWER)) || (!strcmp(handle->useCase, SND_USE_CASE_MOD_PLAY_LPA))
    ||(!strcmp(handle->useCase, SND_USE_CASE_VERB_HIFI_TUNNEL)) || (!strcmp(handle->useCase, SND_USE_CASE_MOD_PLAY_TUNNEL))) {