blob: cf2b5990800033dcfc3fcd493ba601cdc7d7ab11 [file] [log] [blame]
/*
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Changes from Qualcomm Innovation Center are provided under the following license:
*
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#define LOG_TAG "PAL: DisplayPort"
#include "DisplayPort.h"
#include "SessionAlsaUtils.h"
#include "ResourceManager.h"
#include "PayloadBuilder.h"
#include "Device.h"
#include "kvh2xml.h"
enum {
EXT_DISPLAY_TYPE_NONE,
EXT_DISPLAY_TYPE_HDMI,
EXT_DISPLAY_TYPE_DP
};
/*
* This file will have a maximum of 38 bytes:
*
* 4 bytes: number of audio blocks
* 4 bytes: total length of Short Audio Descriptor (SAD) blocks
* Maximum 10 * 3 bytes: SAD blocks
*/
#define MAX_SAD_BLOCKS 10
#define SAD_BLOCK_SIZE 3
#define ACK_ENABLE "Ack_Enable"
#define CONNECT "Connect"
#define DISCONNECT "Disconnect"
static struct extDispState {
void *edidInfo = NULL;
bool valid = false;
int type = EXT_DISPLAY_TYPE_NONE;
} extDisp[MAX_CONTROLLERS][MAX_STREAMS_PER_CONTROLLER];
std::shared_ptr<Device> DisplayPort::objRx = nullptr;
std::shared_ptr<Device> DisplayPort::objTx = nullptr;
std::shared_ptr<Device> DisplayPort::getInstance(struct pal_device *device,
std::shared_ptr<ResourceManager> Rm)
{
if (!device)
return NULL;
PAL_DBG(LOG_TAG, "Enter, device id %d", device->id);
if ((device->id == PAL_DEVICE_OUT_AUX_DIGITAL) ||
(device->id == PAL_DEVICE_OUT_AUX_DIGITAL_1) ||
(device->id == PAL_DEVICE_OUT_HDMI)) {
if (!objRx) {
std::shared_ptr<Device> sp(new DisplayPort(device, Rm));
objRx = sp;
}
return objRx;
} else if (device->id == PAL_DEVICE_IN_AUX_DIGITAL) {
if (!objTx) {
std::shared_ptr<Device> sp(new DisplayPort(device, Rm));
objTx = sp;
}
return objTx;
}
return NULL;
}
std::shared_ptr<Device> DisplayPort::getObject(pal_device_id_t id)
{
if ((id == PAL_DEVICE_OUT_AUX_DIGITAL) ||
(id == PAL_DEVICE_OUT_AUX_DIGITAL_1) ||
(id == PAL_DEVICE_OUT_HDMI)) {
if (objRx) {
if (objRx->getSndDeviceId() == id)
return objRx;
}
} else if (id == PAL_DEVICE_IN_AUX_DIGITAL) {
if (objTx) {
if (objTx->getSndDeviceId() == id)
return objTx;
}
}
return NULL;
}
DisplayPort::DisplayPort(struct pal_device *device, std::shared_ptr<ResourceManager> Rm) :
Device(device, Rm)
{
}
int DisplayPort::getDeviceChannelAllocation(int num_channels)
{
int channel_allocation = 0;
switch (num_channels) {
case 2:
channel_allocation = 0x0; break;
case 3:
channel_allocation = 0x02; break;
case 4:
channel_allocation = 0x06; break;
case 5:
channel_allocation = 0x0A; break;
case 6:
channel_allocation = 0x0B; break;
case 7:
channel_allocation = 0x12; break;
case 8:
channel_allocation = 0x13; break;
default:
channel_allocation = 0x0; break;
PAL_ERR(LOG_TAG, "invalid num channels: %d\n",
num_channels);
break;
}
PAL_DBG(LOG_TAG, "num channels: %d, ca: 0x%x", num_channels,
channel_allocation);
return channel_allocation;
}
int DisplayPort::getDeviceAttributes(struct pal_device *dattr)
{
int status = 0;
int channel_allocation = 0;
if (!dattr) {
status = -EINVAL;
PAL_ERR(LOG_TAG,"Invalid device attributes %d", status);
return status;
}
ar_mem_cpy(dattr, sizeof(struct pal_device), &deviceAttr, sizeof(struct pal_device));
channel_allocation = getDeviceChannelAllocation(deviceAttr.config.ch_info.channels);
retrieveChannelMapLpass(channel_allocation, &dattr->config.ch_info.ch_map[0],
PAL_MAX_CHANNELS_SUPPORTED);
return status;
}
int DisplayPort::start()
{
int status = 0;
if (customPayload)
free(customPayload);
customPayload = NULL;
customPayloadSize = 0;
status = configureDpEndpoint();
if (status != 0) {
PAL_ERR(LOG_TAG,"Endpoint Configuration Failed");
return status;
}
status = Device::start();
return status;
}
int DisplayPort::configureDpEndpoint()
{
int status = 0;
std::string backEndName;
PayloadBuilder* builder = new PayloadBuilder();
struct dpAudioConfig cfg;
uint8_t* payload = NULL;
Stream *stream = NULL;
Session *session = NULL;
size_t payloadSize = 0;
std::shared_ptr<Device> dev = nullptr;
std::vector<Stream*> activestreams;
uint32_t miid = 0;
rm->getBackendName(deviceAttr.id, backEndName);
dev = Device::getInstance(&deviceAttr, rm);
status = rm->getActiveStream_l(activestreams, dev);
if ((0 != status) || (activestreams.size() == 0)) {
PAL_ERR(LOG_TAG, "no active stream available");
status = -EINVAL;
goto exit;
}
stream = static_cast<Stream *>(activestreams[0]);
stream->getAssociatedSession(&session);
status = session->getMIID(backEndName.c_str(), DEVICE_HW_ENDPOINT_RX, &miid);
if (status) {
PAL_ERR(LOG_TAG, "Failed to get tag info %x, status = %d", DEVICE_HW_ENDPOINT_RX, status);
goto exit;
}
cfg.channel_allocation = getDeviceChannelAllocation(deviceAttr.config.ch_info.channels);
cfg.mst_idx = dp_stream;
cfg.dptx_idx = dp_controller;
builder->payloadDpAudioConfig(&payload, &payloadSize, miid, &cfg);
if (payloadSize) {
status = updateCustomPayload(payload, payloadSize);
free(payload);
if (0 != status) {
PAL_ERR(LOG_TAG," updateCustomPayload Failed\n");
goto exit;
}
}
exit:
if(builder) {
delete builder;
builder = NULL;
}
return status;
}
int DisplayPort::updateSysfsNode(const char *path, const char *data, size_t len)
{
int err = 0;
FILE *fd = NULL;
fd = fopen(path, "w");
if (!fd) {
PAL_ERR(LOG_TAG,"Failed to open file %s\n", path);
return -EIO;
}
fwrite(data, sizeof(char), len, fd);
fclose(fd);
return err;
}
int DisplayPort::getExtDispSysfsNodeIndex(int ext_disp_type)
{
int node_index = -1;
char fbvalue[80] = {0};
char fbpath[80] = {0};
int i = 0;
FILE *ext_disp_fd = NULL;
while (1) {
snprintf(fbpath, sizeof(fbpath),
"/sys/class/graphics/fb%d/msm_fb_type", i);
ext_disp_fd = fopen(fbpath, "r");
if (ext_disp_fd) {
if (fread(fbvalue, sizeof(char), 80, ext_disp_fd)) {
if(((strncmp(fbvalue, "dtv panel", strlen("dtv panel")) == 0) &&
(ext_disp_type == EXT_DISPLAY_TYPE_HDMI)) ||
((strncmp(fbvalue, "dp panel", strlen("dp panel")) == 0) &&
(ext_disp_type == EXT_DISPLAY_TYPE_DP))) {
node_index = i;
PAL_DBG(LOG_TAG, "Ext Disp:%d is at fb%d", ext_disp_type, i);
fclose(ext_disp_fd);
return node_index;
}
}
fclose(ext_disp_fd);
i++;
} else {
PAL_ERR(LOG_TAG, "Scanned till end of fbs or Failed to open fb node %d", i);
break;
}
}
return -1;
}
int DisplayPort::updateExtDispSysfsNode(int node_value, int controller, int stream)
{
char ext_disp_ack_path[80] = {0};
char ext_disp_ack_value[3] = {0};
int index, ret = -1;
struct mixer *mixer;
int ext_disp_type = -EINVAL;
int status = 0;
status = rm->getHwAudioMixer(&mixer);
if (status) {
PAL_ERR(LOG_TAG," mixer error");
return status;
}
ext_disp_type = getExtDispType(mixer, controller, stream);
if (ext_disp_type < 0) {
PAL_ERR(LOG_TAG, "Unable to get the external display type, err:%d", ext_disp_type);
return -EINVAL;
}
index = getExtDispSysfsNodeIndex(ext_disp_type);
if (index >= 0) {
snprintf(ext_disp_ack_value, sizeof(ext_disp_ack_value), "%d", node_value);
snprintf(ext_disp_ack_path, sizeof(ext_disp_ack_path),
"/sys/class/graphics/fb%d/hdmi_audio_cb", index);
ret = updateSysfsNode(ext_disp_ack_path, ext_disp_ack_value,
sizeof(ext_disp_ack_value));
PAL_DBG(LOG_TAG, "update hdmi_audio_cb at fb[%d] to:[%d] %s",
index, node_value, (ret >= 0) ? "success":"fail");
}
return ret;
}
int DisplayPort::updateAudioAckState(int node_value, int controller, int stream)
{
int ret = 0;
int ctl_index = 0;
struct mixer_ctl *ctl = NULL;
const char *ctl_prefix = "External Display";
const char *ctl_suffix = "Audio Ack";
char mixer_ctl_name[MIXER_PATH_MAX_LENGTH] = {0};
struct mixer *mixer;
ret = rm->getHwAudioMixer(&mixer);
if (ret) {
PAL_ERR(LOG_TAG," mixer error");
return ret;
}
ctl_index = getDisplayPortCtlIndex(controller, stream);
if (-EINVAL == ctl_index) {
PAL_ERR(LOG_TAG, "Unknown controller/stream %d/%d",
controller, stream);
return -EINVAL;
}
if (0 == ctl_index)
snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
"%s %s", ctl_prefix, ctl_suffix);
else
snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
"%s%d %s", ctl_prefix, ctl_index, ctl_suffix);
PAL_DBG(LOG_TAG, "mixer ctl name: %s", mixer_ctl_name);
ctl = mixer_get_ctl_by_name(mixer, mixer_ctl_name);
/* If no mixer command support, fall back to sysfs node approach */
if (!ctl) {
PAL_DBG(LOG_TAG, "could not get ctl for mixer cmd(%s), use sysfs node instead\n",
mixer_ctl_name);
ret = updateExtDispSysfsNode(node_value, controller, stream);
} else {
char *ack_str = NULL;
if (node_value == EXT_DISPLAY_PLUG_STATUS_NOTIFY_ENABLE)
ack_str = (char*) ACK_ENABLE;
else if (node_value == EXT_DISPLAY_PLUG_STATUS_NOTIFY_CONNECT)
ack_str = (char*) CONNECT;
else if (node_value == EXT_DISPLAY_PLUG_STATUS_NOTIFY_DISCONNECT)
ack_str = (char*) DISCONNECT;
else {
PAL_ERR(LOG_TAG, "Invalid input parameter - 0x%x\n", node_value);
return -EINVAL;
}
ret = mixer_ctl_set_enum_by_string(ctl, ack_str);
if (ret)
PAL_ERR(LOG_TAG, "Could not set ctl for mixer cmd - %s ret %d\n",
mixer_ctl_name, ret);
}
return ret;
}
int DisplayPort::init(pal_param_device_connection_t device_conn)
{
PAL_DBG(LOG_TAG," Enter");
int status = 0;
static bool is_hdmi_sysfs_node_init = false;
struct mixer *mixer;
status = rm->getHwAudioMixer(&mixer);
if (status) {
PAL_ERR(LOG_TAG," mixer error");
return status;
}
PAL_DBG(LOG_TAG," get mixer success");
pal_param_disp_port_config_params* dp_config = (pal_param_disp_port_config_params*) &device_conn.device_config.dp_config;
dp_controller = dp_config->controller;
dp_stream = dp_config->stream;
PAL_DBG(LOG_TAG," DP contr: %d stream: %d", dp_controller, dp_stream);
setExtDisplayDevice(mixer, dp_config->controller, dp_config->stream);
status = getExtDispType(mixer, dp_config->controller, dp_config->stream);
if (status < 0) {
PAL_ERR(LOG_TAG," Failed to query disp type, status:%d", status);
} else {
cacheEdid(mixer, dp_config->controller, dp_config->stream);
}
if (is_hdmi_sysfs_node_init == false) {
is_hdmi_sysfs_node_init = true;
updateAudioAckState(EXT_DISPLAY_PLUG_STATUS_NOTIFY_ENABLE, dp_controller, dp_stream);
}
updateAudioAckState(EXT_DISPLAY_PLUG_STATUS_NOTIFY_CONNECT, dp_controller, dp_stream);
PAL_DBG(LOG_TAG," Exit");
return 0;
}
int DisplayPort::deinit(pal_param_device_connection_t device_conn __unused)
{
updateAudioAckState(EXT_DISPLAY_PLUG_STATUS_NOTIFY_DISCONNECT, dp_controller, dp_stream);
//To-Do : Have to invalidate the cahed EDID
return 0;
}
bool DisplayPort::isDisplayPortEnabled () {
//TBD: Check for the system prop here
return true;
}
/*void DisplayPort1::resetEdidInfo()
{
DisplayPort::resetEdidInfo();
}*/
void DisplayPort::resetEdidInfo() {
PAL_VERBOSE(LOG_TAG," enter");
int i = 0, j = 0;
for (i = 0; i < MAX_CONTROLLERS; ++i) {
for (j = 0; j < MAX_STREAMS_PER_CONTROLLER; ++j) {
struct extDispState *state = &extDisp[i][j];
state->type = EXT_DISPLAY_TYPE_NONE;
if (state->edidInfo) {
free(state->edidInfo);
state->edidInfo = NULL;
}
state->valid = false;
}
}
}
/*
* returns index for mixer controls
*
* example: max controllers = 2, max streams = 4
* controller = 0, stream = 0 => Index 0
* ...
* controller = 0, stream = 3 => Index 3
* controller = 1, stream = 0 => Index 4
* ...
* controller = 1, stream = 3 => Index 7
*/
int32_t DisplayPort::getDisplayPortCtlIndex(int controller, int stream)
{
if (controller < 0 || controller >= MAX_CONTROLLERS ||
stream < 0 || stream >= MAX_STREAMS_PER_CONTROLLER) {
PAL_ERR(LOG_TAG,"Invalid controller/stream - %d/%d",
controller, stream);
return -EINVAL;
}
return ((controller % MAX_CONTROLLERS) * MAX_STREAMS_PER_CONTROLLER) +
(stream % MAX_STREAMS_PER_CONTROLLER);
}
int32_t DisplayPort::setExtDisplayDevice(struct audio_mixer *mixer, int controller, int stream)
{
struct mixer_ctl *ctl = NULL;
int ctlIndex = 0;
const char *ctlNamePrefix = "External Display";
const char *ctlNameSuffix = "Audio Device";
char mixerCtlName[MIXER_PATH_MAX_LENGTH] = {0};
int deviceValues[2] = {-1, -1};
ctlIndex = getDisplayPortCtlIndex(controller, stream);
if (-EINVAL == ctlIndex) {
PAL_ERR(LOG_TAG,"Unknown controller/stream %d/%d", controller, stream);
return -EINVAL;
}
PAL_DBG(LOG_TAG," ctlIndex: %d controller: %d stream: %d", ctlIndex, controller, stream);
if (0 == ctlIndex)
snprintf(mixerCtlName, sizeof(mixerCtlName),
"%s %s", ctlNamePrefix, ctlNameSuffix);
else
snprintf(mixerCtlName, sizeof(mixerCtlName),
"%s%d %s", ctlNamePrefix, ctlIndex, ctlNameSuffix);
deviceValues[0] = controller;
deviceValues[1] = stream;
PAL_DBG(LOG_TAG," mixer: %pK mixer ctl name: %s", mixer, mixerCtlName);
ctl = mixer_get_ctl_by_name(mixer, mixerCtlName);
if (!ctl) {
PAL_ERR(LOG_TAG,"Could not get ctl for mixer cmd - %s", mixerCtlName);
return -EINVAL;
}
PAL_DBG(LOG_TAG,"controller/stream: %ld/%ld", deviceValues[0], deviceValues[1]);
return mixer_ctl_set_array(ctl, deviceValues, ARRAY_SIZE(deviceValues));
}
int32_t DisplayPort::getExtDispType(struct audio_mixer *mixer, int controller, int stream)
{
int dispType = EXT_DISPLAY_TYPE_NONE;
int ctlIndex = 0;
struct extDispState *disp = NULL;
ctlIndex = getDisplayPortCtlIndex(controller, stream);
if (-EINVAL == ctlIndex) {
PAL_ERR(LOG_TAG,"Unknown controller/stream %d/%d", controller, stream);
return -EINVAL;
}
disp = &extDisp[controller][stream];
if (disp->type != EXT_DISPLAY_TYPE_NONE) {
PAL_DBG(LOG_TAG," Returning cached ext disp type:%s",
(disp->type == EXT_DISPLAY_TYPE_DP) ? "DisplayPort" : "HDMI");
return disp->type;
}
if (isDisplayPortEnabled()) {
struct mixer_ctl *ctl = NULL;
const char *ctlNamePrefix = "External Display";
const char *ctlNameSuffix = "Type";
char mixerCtlName[MIXER_PATH_MAX_LENGTH] = {0};
if (0 == ctlIndex)
snprintf(mixerCtlName, sizeof(mixerCtlName),
"%s %s", ctlNamePrefix, ctlNameSuffix);
else
snprintf(mixerCtlName, sizeof(mixerCtlName),
"%s%d %s", ctlNamePrefix, ctlIndex, ctlNameSuffix);
PAL_VERBOSE(LOG_TAG,"mixer ctl name: %s", mixerCtlName);
ctl = mixer_get_ctl_by_name(mixer, mixerCtlName);
if (!ctl) {
PAL_ERR(LOG_TAG,"Could not get ctl for mixer cmd - %s", mixerCtlName);
return -EINVAL;
}
dispType = mixer_ctl_get_value(ctl, 0);
if (dispType == EXT_DISPLAY_TYPE_NONE) {
PAL_ERR(LOG_TAG,"Invalid external display type: %d", dispType);
return -EINVAL;
}
} else {
dispType = EXT_DISPLAY_TYPE_HDMI;
}
disp->type = dispType;
PAL_DBG(LOG_TAG," ext disp type: %s", (dispType == EXT_DISPLAY_TYPE_DP) ? "DisplayPort" : "HDMI");
return dispType;
}
int DisplayPort::getEdidInfo(struct audio_mixer *mixer, int controller, int stream)
{
char block[MAX_SAD_BLOCKS * SAD_BLOCK_SIZE];
int ret, count;
char edidData[MAX_SAD_BLOCKS * SAD_BLOCK_SIZE + 1] = {0};
struct extDispState *state = NULL;
int ctlIndex = 0;
struct mixer_ctl *ctl = NULL;
const char *ctlNamePrefix = "Display Port";
const char *ctlNameSuffix = "EDID";
char mixerCtlName[MIXER_PATH_MAX_LENGTH] = {0};
ctlIndex = getDisplayPortCtlIndex(controller, stream);
if (-EINVAL == ctlIndex) {
PAL_ERR(LOG_TAG," Unknown controller/stream %d/%d", controller, stream);
return -EINVAL;
}
state = &extDisp[controller][stream];
if (state->valid) {
/* use cached edid */
return 0;
}
switch(state->type) {
case EXT_DISPLAY_TYPE_HDMI:
snprintf(mixerCtlName, sizeof(mixerCtlName), "HDMI EDID");
break;
case EXT_DISPLAY_TYPE_DP:
if (!isDisplayPortEnabled()) {
PAL_ERR(LOG_TAG," display port is not supported");
return -EINVAL;
}
if (0 == ctlIndex)
snprintf(mixerCtlName, sizeof(mixerCtlName),
"%s %s", ctlNamePrefix, ctlNameSuffix);
else
snprintf(mixerCtlName, sizeof(mixerCtlName),
"%s%d %s", ctlNamePrefix, ctlIndex, ctlNameSuffix);
break;
default:
PAL_ERR(LOG_TAG," Invalid disp_type %d", state->type);
return -EINVAL;
}
if (state->edidInfo == NULL)
state->edidInfo =
(struct edidAudioInfo *)calloc(1, sizeof(struct edidAudioInfo));
PAL_VERBOSE(LOG_TAG," mixer ctl name: %s", mixerCtlName);
ctl = mixer_get_ctl_by_name(mixer, mixerCtlName);
if (!ctl) {
PAL_ERR(LOG_TAG," Could not get ctl for mixer cmd - %s", mixerCtlName);
goto fail;
}
mixer_ctl_update(ctl);
count = mixer_ctl_get_num_values(ctl);
/* Read SAD blocks, clamping the maximum size for safety */
if (count > (int)sizeof(block))
count = (int)sizeof(block);
ret = mixer_ctl_get_array(ctl, block, count);
if (ret != 0) {
PAL_ERR(LOG_TAG," mixer_ctl_get_array() failed to get EDID info");
goto fail;
}
edidData[0] = count;
memcpy(&edidData[1], block, count);
PAL_VERBOSE(LOG_TAG," received edid data: count %d", edidData[0]);
if (!getSinkCaps((struct edidAudioInfo *)state->edidInfo, edidData)) {
PAL_ERR(LOG_TAG," Failed to get extn disp sink capabilities");
goto fail;
}
state->valid = true;
return 0;
fail:
if (state->edidInfo) {
free(state->edidInfo);
state->edidInfo = NULL;
state->valid = false;
}
PAL_ERR(LOG_TAG," return -EINVAL");
return -EINVAL;
}
void DisplayPort::cacheEdid(struct audio_mixer *mixer, int controller, int stream)
{
getEdidInfo(mixer, controller, stream);
}
int32_t DisplayPort::isSampleRateSupported(uint32_t sampleRate)
{
int32_t rc = 0;
PAL_ERR(LOG_TAG, "sampleRate %d", sampleRate);
if (sampleRate % SAMPLINGRATE_44K == 0)
return rc;
switch (sampleRate) {
case SAMPLINGRATE_44K:
case SAMPLINGRATE_48K:
case SAMPLINGRATE_96K:
case SAMPLINGRATE_192K:
case SAMPLINGRATE_384K:
break;
default:
rc = -EINVAL;
PAL_ERR(LOG_TAG, "sample rate not supported rc %d", rc);
break;
}
return rc;
}
//TBD why do these channels have to be supported, DisplayPorts support only 1/2?
int32_t DisplayPort::isChannelSupported(uint32_t numChannels)
{
int32_t rc = 0;
PAL_DBG(LOG_TAG, "numChannels %u", numChannels);
switch (numChannels) {
case CHANNELS_1:
case CHANNELS_2:
break;
default:
rc = -EINVAL;
PAL_ERR(LOG_TAG, "channels not supported rc %d", rc);
break;
}
return rc;
}
int32_t DisplayPort::isBitWidthSupported(uint32_t bitWidth)
{
int32_t rc = 0;
PAL_DBG(LOG_TAG, "bitWidth %u", bitWidth);
switch (bitWidth) {
case BITWIDTH_16:
case BITWIDTH_24:
case BITWIDTH_32:
break;
default:
rc = -EINVAL;
PAL_ERR(LOG_TAG, "bit width not supported rc %d", rc);
break;
}
return rc;
}
int32_t DisplayPort::checkAndUpdateBitWidth(uint32_t *bitWidth)
{
int32_t rc = 0;
PAL_DBG(LOG_TAG, "bitWidth %u", *bitWidth);
switch (*bitWidth) {
case BITWIDTH_16:
case BITWIDTH_24:
case BITWIDTH_32:
break;
default:
*bitWidth = BITWIDTH_16;
PAL_DBG(LOG_TAG, "bit width not supported, setting to default 16 bit");
break;
}
return rc;
}
int32_t DisplayPort::checkAndUpdateSampleRate(uint32_t *sampleRate)
{
int32_t rc = 0;
if (*sampleRate <= SAMPLINGRATE_48K)
*sampleRate = SAMPLINGRATE_48K;
else if (*sampleRate > SAMPLINGRATE_48K && *sampleRate <= SAMPLINGRATE_96K)
*sampleRate = SAMPLINGRATE_96K;
else if (*sampleRate > SAMPLINGRATE_96K && *sampleRate <= SAMPLINGRATE_192K)
*sampleRate = SAMPLINGRATE_192K;
else if (*sampleRate > SAMPLINGRATE_192K && *sampleRate <= SAMPLINGRATE_384K)
*sampleRate = SAMPLINGRATE_384K;
PAL_DBG(LOG_TAG, "sampleRate %d", *sampleRate);
return rc;
}
/* ----------------------------------------------------------------------------------
------------------------ Edid -------------------
----------------------------------------------------------------------------------*/
const char * DisplayPort::edidFormatToStr(unsigned char format)
{
static std::string formatStr = "??";
switch (format) {
case LPCM:
formatStr = "Format:LPCM";
break;
case AC3:
formatStr = "Format:AC-3";
break;
case MPEG1:
formatStr = "Format:MPEG1 (Layers 1 & 2)";
break;
case MP3:
formatStr = "Format:MP3 (MPEG1 Layer 3)";
break;
case MPEG2_MULTI_CHANNEL:
formatStr = "Format:MPEG2 (multichannel)";
break;
case AAC:
formatStr = "Format:AAC";
break;
case DTS:
formatStr = "Format:DTS";
break;
case ATRAC:
formatStr = "Format:ATRAC";
break;
case SACD:
formatStr = "Format:One-bit audio aka SACD";
break;
case DOLBY_DIGITAL_PLUS:
formatStr = "Format:Dolby Digital +";
break;
case DTS_HD:
formatStr = "Format:DTS-HD";
break;
case MAT:
formatStr = "Format:MAT (MLP)";
break;
case DST:
formatStr = "Format:DST";
break;
case WMA_PRO:
formatStr = "Format:WMA Pro";
break;
default:
break;
}
return formatStr.c_str();
}
bool DisplayPort::isSampleRateSupported(unsigned char srByte, int samplingRate)
{
int result = 0;
// Codec Supports Sample rate in range of 48K-192K
PAL_VERBOSE(LOG_TAG," srByte: %d, samplingRate: %d", srByte, samplingRate);
switch (samplingRate) {
case 192000:
result = (srByte & BIT(6));
break;
case 176400:
result = (srByte & BIT(5));
break;
case 96000:
result = (srByte & BIT(4));
break;
case 88200:
result = (srByte & BIT(3));
break;
case 48000:
result = (srByte & BIT(2));
break;
case 44100:
result = (srByte & BIT(1));
break;
case 32000:
result = (srByte & BIT(0));
break;
default:
break;
}
if (result)
return true;
return false;
}
unsigned char DisplayPort::getEdidBpsByte(unsigned char byte,
unsigned char format)
{
if (format == 0) {
PAL_VERBOSE(LOG_TAG," not lpcm format, return 0");
return 0;
}
return byte;
}
bool DisplayPort::isSupportedBps(unsigned char bpsByte, int bps)
{
int result = 0;
switch (bps) {
case 24:
PAL_VERBOSE(LOG_TAG,"24bit");
result = (bpsByte & BIT(2));
break;
case 16:
PAL_VERBOSE(LOG_TAG,"16bit");
result = (bpsByte & BIT(0));
break;
default:
break;
}
if (result)
return true;
return false;
}
int DisplayPort::getHighestEdidSF(unsigned char byte)
{
int nfreq = 0;
if (byte & BIT(6)) {
PAL_VERBOSE(LOG_TAG,"Highest: 192kHz");
nfreq = 192000;
} else if (byte & BIT(5)) {
PAL_VERBOSE(LOG_TAG,"Highest: 176kHz");
nfreq = 176000;
} else if (byte & BIT(4)) {
PAL_VERBOSE(LOG_TAG,"Highest: 96kHz");
nfreq = 96000;
} else if (byte & BIT(3)) {
PAL_VERBOSE(LOG_TAG,"Highest: 88.2kHz");
nfreq = 88200;
} else if (byte & BIT(2)) {
PAL_VERBOSE(LOG_TAG,"Highest: 48kHz");
nfreq = 48000;
} else if (byte & BIT(1)) {
PAL_VERBOSE(LOG_TAG,"Highest: 44.1kHz");
nfreq = 44100;
} else if (byte & BIT(0)) {
PAL_VERBOSE(LOG_TAG,"Highest: 32kHz");
nfreq = 32000;
}
return nfreq;
}
void DisplayPort::updateChannelMap(edidAudioInfo* 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->channelMap, 0, MAX_CHANNELS_SUPPORTED);
if(info->speakerAllocation[0] & BIT(0)) {
info->channelMap[0] = PCM_CHANNEL_FL;
info->channelMap[1] = PCM_CHANNEL_FR;
}
if(info->speakerAllocation[0] & BIT(1)) {
info->channelMap[2] = PCM_CHANNEL_LFE;
}
if(info->speakerAllocation[0] & BIT(2)) {
info->channelMap[3] = PCM_CHANNEL_FC;
}
if(info->speakerAllocation[0] & BIT(3)) {
/*
* As per CEA(HDMI Cable) standard Bit 3 is equivalent
* to SideLeft/SideRight of WAV standard
*/
info->channelMap[4] = PCM_CHANNEL_LS;
info->channelMap[5] = PCM_CHANNEL_RS;
}
if(info->speakerAllocation[0] & BIT(4)) {
if(info->speakerAllocation[0] & BIT(3)) {
info->channelMap[6] = PCM_CHANNEL_CS;
info->channelMap[7] = 0;
} else if (info->speakerAllocation[1] & BIT(1)) {
info->channelMap[6] = PCM_CHANNEL_CS;
info->channelMap[7] = PCM_CHANNEL_TS;
} else if (info->speakerAllocation[1] & BIT(2)) {
info->channelMap[6] = PCM_CHANNEL_CS;
info->channelMap[7] = PCM_CHANNEL_CVH;
} else {
info->channelMap[4] = PCM_CHANNEL_CS;
info->channelMap[5] = 0;
}
}
if(info->speakerAllocation[0] & BIT(5)) {
info->channelMap[6] = PCM_CHANNEL_FLC;
info->channelMap[7] = PCM_CHANNEL_FRC;
}
if(info->speakerAllocation[0] & BIT(6)) {
// If RLC/RRC is present, RC is invalid as per specification
info->speakerAllocation[0] &= 0xef;
/*
* As per CEA(HDMI Cable) standard Bit 6 is equivalent
* to BackLeft/BackRight of WAV standard
*/
info->channelMap[6] = PCM_CHANNEL_LB;
info->channelMap[7] = PCM_CHANNEL_RB;
}
// higher channel are not defined by LPASS
//info->nSpeakerAllocation[0] &= 0x3f;
if(info->speakerAllocation[0] & BIT(7)) {
info->channelMap[6] = 0; // PCM_CHANNEL_FLW; but not defined by LPASS
info->channelMap[7] = 0; // PCM_CHANNEL_FRW; but not defined by LPASS
}
if(info->speakerAllocation[1] & BIT(0)) {
info->channelMap[6] = 0; // PCM_CHANNEL_FLH; but not defined by LPASS
info->channelMap[7] = 0; // PCM_CHANNEL_FRH; but not defined by LPASS
}
PAL_VERBOSE(LOG_TAG," channel map updated to [%d %d %d %d %d %d %d %d ] [%x %x %x]"
, info->channelMap[0], info->channelMap[1], info->channelMap[2]
, info->channelMap[3], info->channelMap[4], info->channelMap[5]
, info->channelMap[6], info->channelMap[7]
, info->speakerAllocation[0], info->speakerAllocation[1]
, info->speakerAllocation[2]);
}
void DisplayPort::dumpSpeakerAllocation(edidAudioInfo* info)
{
if (!info)
return;
if (info->speakerAllocation[0] & BIT(7))
PAL_VERBOSE(LOG_TAG,"FLW/FRW");
if (info->speakerAllocation[0] & BIT(6))
PAL_VERBOSE(LOG_TAG,"RLC/RRC");
if (info->speakerAllocation[0] & BIT(5))
PAL_VERBOSE(LOG_TAG,"FLC/FRC");
if (info->speakerAllocation[0] & BIT(4))
PAL_VERBOSE(LOG_TAG,"RC");
if (info->speakerAllocation[0] & BIT(3))
PAL_VERBOSE(LOG_TAG,"RL/RR");
if (info->speakerAllocation[0] & BIT(2))
PAL_VERBOSE(LOG_TAG,"FC");
if (info->speakerAllocation[0] & BIT(1))
PAL_VERBOSE(LOG_TAG,"LFE");
if (info->speakerAllocation[0] & BIT(0))
PAL_VERBOSE(LOG_TAG,"FL/FR");
if (info->speakerAllocation[1] & BIT(2))
PAL_VERBOSE(LOG_TAG,"FCH");
if (info->speakerAllocation[1] & BIT(1))
PAL_VERBOSE(LOG_TAG,"TC");
if (info->speakerAllocation[1] & BIT(0))
PAL_VERBOSE(LOG_TAG,"FLH/FRH");
}
void DisplayPort::updateChannelAllocation(edidAudioInfo* info)
{
int16_t ca;
int16_t spkrAlloc;
if (!info)
return;
/* Most common 5.1 SAD is 0xF, ca 0x0b
* and 7.1 SAD is 0x4F, ca 0x13 */
spkrAlloc = ((info->speakerAllocation[1]) << 8) |
(info->speakerAllocation[0]);
PAL_VERBOSE(LOG_TAG,"info->nSpeakerAllocation %x %x\n", info->speakerAllocation[0],
info->speakerAllocation[1]);
PAL_VERBOSE(LOG_TAG,"spkrAlloc: %x", spkrAlloc);
/* The below switch case calculates channel allocation values
as defined in CEA-861 section 6.6.2 */
switch (spkrAlloc) {
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;
}
PAL_DBG(LOG_TAG," channel allocation: %x", ca);
info->channelAllocation = ca;
}
void DisplayPort::retrieveChannelMapLpass(int ca, uint8_t *ch_map, int ch_map_size)
{
if (!ch_map)
return;
if (((ca < 0) || (ca > 0x1f)) &&
(ca != 0x2f)) {
PAL_ERR(LOG_TAG,"Channel allocation out of supported range");
return;
}
PAL_VERBOSE(LOG_TAG,"channelAllocation 0x%x", ca);
if (ch_map_size < MAX_CHANNELS_SUPPORTED)
return;
memset(ch_map, 0, MAX_CHANNELS_SUPPORTED);
switch(ca) {
case 0x0:
ch_map[0] = PCM_CHANNEL_FL;
ch_map[1] = PCM_CHANNEL_FR;
break;
case 0x1:
ch_map[0] = PCM_CHANNEL_FL;
ch_map[1] = PCM_CHANNEL_FR;
ch_map[2] = PCM_CHANNEL_LFE;
break;
case 0x2:
ch_map[0] = PCM_CHANNEL_FL;
ch_map[1] = PCM_CHANNEL_FR;
ch_map[2] = PCM_CHANNEL_FC;
break;
case 0x3:
ch_map[0] = PCM_CHANNEL_FL;
ch_map[1] = PCM_CHANNEL_FR;
ch_map[2] = PCM_CHANNEL_LFE;
ch_map[3] = PCM_CHANNEL_FC;
break;
case 0x4:
ch_map[0] = PCM_CHANNEL_FL;
ch_map[1] = PCM_CHANNEL_FR;
ch_map[2] = PCM_CHANNEL_CS;
break;
case 0x5:
ch_map[0] = PCM_CHANNEL_FL;
ch_map[1] = PCM_CHANNEL_FR;
ch_map[2] = PCM_CHANNEL_LFE;
ch_map[3] = PCM_CHANNEL_CS;
break;
case 0x6:
ch_map[0] = PCM_CHANNEL_FL;
ch_map[1] = PCM_CHANNEL_FR;
ch_map[2] = PCM_CHANNEL_FC;
ch_map[3] = PCM_CHANNEL_CS;
break;
case 0x7:
ch_map[0] = PCM_CHANNEL_FL;
ch_map[1] = PCM_CHANNEL_FR;
ch_map[2] = PCM_CHANNEL_LFE;
ch_map[3] = PCM_CHANNEL_FC;
ch_map[4] = PCM_CHANNEL_CS;
break;
case 0x8:
ch_map[0] = PCM_CHANNEL_FL;
ch_map[1] = PCM_CHANNEL_FR;
ch_map[2] = PCM_CHANNEL_LS;
ch_map[3] = PCM_CHANNEL_RS;
break;
case 0x9:
ch_map[0] = PCM_CHANNEL_FL;
ch_map[1] = PCM_CHANNEL_FR;
ch_map[2] = PCM_CHANNEL_LFE;
ch_map[3] = PCM_CHANNEL_LS;
ch_map[4] = PCM_CHANNEL_RS;
break;
case 0xa:
ch_map[0] = PCM_CHANNEL_FL;
ch_map[1] = PCM_CHANNEL_FR;
ch_map[2] = PCM_CHANNEL_FC;
ch_map[3] = PCM_CHANNEL_LS;
ch_map[4] = PCM_CHANNEL_RS;
break;
case 0xb:
ch_map[0] = PCM_CHANNEL_FL;
ch_map[1] = PCM_CHANNEL_FR;
ch_map[2] = PCM_CHANNEL_LFE;
ch_map[3] = PCM_CHANNEL_FC;
ch_map[4] = PCM_CHANNEL_LS;
ch_map[5] = PCM_CHANNEL_RS;
break;
case 0xc:
ch_map[0] = PCM_CHANNEL_FL;
ch_map[1] = PCM_CHANNEL_FR;
ch_map[2] = PCM_CHANNEL_LS;
ch_map[3] = PCM_CHANNEL_RS;
ch_map[4] = PCM_CHANNEL_CS;
break;
case 0xd:
ch_map[0] = PCM_CHANNEL_FL;
ch_map[1] = PCM_CHANNEL_FR;
ch_map[2] = PCM_CHANNEL_LFE;
ch_map[3] = PCM_CHANNEL_LS;
ch_map[4] = PCM_CHANNEL_RS;
ch_map[5] = PCM_CHANNEL_CS;
break;
case 0xe:
ch_map[0] = PCM_CHANNEL_FL;
ch_map[1] = PCM_CHANNEL_FR;
ch_map[2] = PCM_CHANNEL_FC;
ch_map[3] = PCM_CHANNEL_LS;
ch_map[4] = PCM_CHANNEL_RS;
ch_map[5] = PCM_CHANNEL_CS;
break;
case 0xf:
ch_map[0] = PCM_CHANNEL_FL;
ch_map[1] = PCM_CHANNEL_FR;
ch_map[2] = PCM_CHANNEL_LFE;
ch_map[3] = PCM_CHANNEL_FC;
ch_map[4] = PCM_CHANNEL_LS;
ch_map[5] = PCM_CHANNEL_RS;
ch_map[6] = PCM_CHANNEL_CS;
break;
case 0x10:
ch_map[0] = PCM_CHANNEL_FL;
ch_map[1] = PCM_CHANNEL_FR;
ch_map[2] = PCM_CHANNEL_LS;
ch_map[3] = PCM_CHANNEL_RS;
ch_map[4] = PCM_CHANNEL_LB;
ch_map[5] = PCM_CHANNEL_RB;
break;
case 0x11:
ch_map[0] = PCM_CHANNEL_FL;
ch_map[1] = PCM_CHANNEL_FR;
ch_map[2] = PCM_CHANNEL_LFE;
ch_map[3] = PCM_CHANNEL_LS;
ch_map[4] = PCM_CHANNEL_RS;
ch_map[5] = PCM_CHANNEL_LB;
ch_map[6] = PCM_CHANNEL_RB;
break;
case 0x12:
ch_map[0] = PCM_CHANNEL_FL;
ch_map[1] = PCM_CHANNEL_FR;
ch_map[2] = PCM_CHANNEL_FC;
ch_map[3] = PCM_CHANNEL_LS;
ch_map[4] = PCM_CHANNEL_RS;
ch_map[5] = PCM_CHANNEL_LB;
ch_map[6] = PCM_CHANNEL_RB;
break;
case 0x13:
ch_map[0] = PCM_CHANNEL_FL;
ch_map[1] = PCM_CHANNEL_FR;
ch_map[2] = PCM_CHANNEL_LFE;
ch_map[3] = PCM_CHANNEL_FC;
ch_map[4] = PCM_CHANNEL_LS;
ch_map[5] = PCM_CHANNEL_RS;
ch_map[6] = PCM_CHANNEL_LB;
ch_map[7] = PCM_CHANNEL_RB;
break;
case 0x14:
ch_map[0] = PCM_CHANNEL_FL;
ch_map[1] = PCM_CHANNEL_FR;
ch_map[2] = PCM_CHANNEL_FLC;
ch_map[3] = PCM_CHANNEL_FRC;
break;
case 0x15:
ch_map[0] = PCM_CHANNEL_FL;
ch_map[1] = PCM_CHANNEL_FR;
ch_map[2] = PCM_CHANNEL_LFE;
ch_map[3] = PCM_CHANNEL_FLC;
ch_map[4] = PCM_CHANNEL_FRC;
break;
case 0x16:
ch_map[0] = PCM_CHANNEL_FL;
ch_map[1] = PCM_CHANNEL_FR;
ch_map[2] = PCM_CHANNEL_FC;
ch_map[3] = PCM_CHANNEL_FLC;
ch_map[4] = PCM_CHANNEL_FRC;
break;
case 0x17:
ch_map[0] = PCM_CHANNEL_FL;
ch_map[1] = PCM_CHANNEL_FR;
ch_map[2] = PCM_CHANNEL_LFE;
ch_map[3] = PCM_CHANNEL_FC;
ch_map[4] = PCM_CHANNEL_FLC;
ch_map[5] = PCM_CHANNEL_FRC;
break;
case 0x18:
ch_map[0] = PCM_CHANNEL_FL;
ch_map[1] = PCM_CHANNEL_FR;
ch_map[2] = PCM_CHANNEL_CS;
ch_map[3] = PCM_CHANNEL_FLC;
ch_map[4] = PCM_CHANNEL_FRC;
break;
case 0x19:
ch_map[0] = PCM_CHANNEL_FL;
ch_map[1] = PCM_CHANNEL_FR;
ch_map[2] = PCM_CHANNEL_LFE;
ch_map[3] = PCM_CHANNEL_CS;
ch_map[4] = PCM_CHANNEL_FLC;
ch_map[5] = PCM_CHANNEL_FRC;
break;
case 0x1a:
ch_map[0] = PCM_CHANNEL_FL;
ch_map[1] = PCM_CHANNEL_FR;
ch_map[2] = PCM_CHANNEL_FC;
ch_map[3] = PCM_CHANNEL_CS;
ch_map[4] = PCM_CHANNEL_FLC;
ch_map[5] = PCM_CHANNEL_FRC;
break;
case 0x1b:
ch_map[0] = PCM_CHANNEL_FL;
ch_map[1] = PCM_CHANNEL_FR;
ch_map[2] = PCM_CHANNEL_LFE;
ch_map[3] = PCM_CHANNEL_FC;
ch_map[4] = PCM_CHANNEL_CS;
ch_map[5] = PCM_CHANNEL_FLC;
ch_map[6] = PCM_CHANNEL_FRC;
break;
case 0x1c:
ch_map[0] = PCM_CHANNEL_FL;
ch_map[1] = PCM_CHANNEL_FR;
ch_map[2] = PCM_CHANNEL_LS;
ch_map[3] = PCM_CHANNEL_RS;
ch_map[4] = PCM_CHANNEL_FLC;
ch_map[5] = PCM_CHANNEL_FRC;
break;
case 0x1d:
ch_map[0] = PCM_CHANNEL_FL;
ch_map[1] = PCM_CHANNEL_FR;
ch_map[2] = PCM_CHANNEL_LFE;
ch_map[3] = PCM_CHANNEL_LS;
ch_map[4] = PCM_CHANNEL_RS;
ch_map[5] = PCM_CHANNEL_FLC;
ch_map[6] = PCM_CHANNEL_FRC;
break;
case 0x1e:
ch_map[0] = PCM_CHANNEL_FL;
ch_map[1] = PCM_CHANNEL_FR;
ch_map[2] = PCM_CHANNEL_FC;
ch_map[3] = PCM_CHANNEL_LS;
ch_map[4] = PCM_CHANNEL_RS;
ch_map[5] = PCM_CHANNEL_FLC;
ch_map[6] = PCM_CHANNEL_FRC;
break;
case 0x1f:
ch_map[0] = PCM_CHANNEL_FL;
ch_map[1] = PCM_CHANNEL_FR;
ch_map[2] = PCM_CHANNEL_LFE;
ch_map[3] = PCM_CHANNEL_FC;
ch_map[4] = PCM_CHANNEL_LS;
ch_map[5] = PCM_CHANNEL_RS;
ch_map[6] = PCM_CHANNEL_FLC;
ch_map[7] = PCM_CHANNEL_FRC;
break;
case 0x2f:
ch_map[0] = PCM_CHANNEL_FL;
ch_map[1] = PCM_CHANNEL_FR;
ch_map[2] = PCM_CHANNEL_LFE;
ch_map[3] = PCM_CHANNEL_FC;
ch_map[4] = PCM_CHANNEL_LS;
ch_map[5] = PCM_CHANNEL_RS;
ch_map[6] = 0; // PCM_CHANNEL_TFL; but not defined by LPASS
ch_map[7] = 0; // PCM_CHANNEL_TFR; but not defined by LPASS
break;
default:
break;
}
PAL_DBG(LOG_TAG," channel map updated to [%d %d %d %d %d %d %d %d ]",
ch_map[0], ch_map[1], ch_map[2],
ch_map[3], ch_map[4], ch_map[5],
ch_map[6], ch_map[7]);
}
void DisplayPort::updateChannelMapLpass(edidAudioInfo* info)
{
if (!info)
return;
retrieveChannelMapLpass(info->channelAllocation, (uint8_t *)&info->channelMap[0],
MAX_CHANNELS_SUPPORTED);
}
void DisplayPort::updateChannelMask(edidAudioInfo* info)
{
if (!info)
return;
if (((info->channelAllocation < 0) ||
(info->channelAllocation > 0x1f)) &&
(info->channelAllocation != 0x2f)) {
PAL_ERR(LOG_TAG,"Channel allocation out of supported range");
return;
}
PAL_VERBOSE(LOG_TAG,"channelAllocation 0x%x", info->channelAllocation);
// 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->channelAllocation) {
case 0x0:
info->channelMask = AUDIO_CHANNEL_OUT_STEREO;
break;
case 0x1:
info->channelMask = AUDIO_CHANNEL_OUT_2POINT1;
break;
case 0x2:
info->channelMask = AUDIO_CHANNEL_OUT_STEREO;
info->channelMask |= AUDIO_CHANNEL_OUT_FRONT_CENTER;
break;
case 0x3:
info->channelMask = AUDIO_CHANNEL_OUT_2POINT1;
info->channelMask |= AUDIO_CHANNEL_OUT_FRONT_CENTER;
break;
case 0x4:
info->channelMask = AUDIO_CHANNEL_OUT_STEREO;
info->channelMask |= AUDIO_CHANNEL_OUT_BACK_CENTER;
break;
case 0x5:
info->channelMask = AUDIO_CHANNEL_OUT_2POINT1;
info->channelMask |= AUDIO_CHANNEL_OUT_LOW_FREQUENCY;
info->channelMask |= AUDIO_CHANNEL_OUT_BACK_CENTER;
break;
case 0x6:
info->channelMask = AUDIO_CHANNEL_OUT_SURROUND;
break;
case 0x7:
info->channelMask = AUDIO_CHANNEL_OUT_SURROUND;
info->channelMask |= AUDIO_CHANNEL_OUT_LOW_FREQUENCY;
break;
case 0x8:
info->channelMask = AUDIO_CHANNEL_OUT_QUAD;
break;
case 0x9:
info->channelMask = AUDIO_CHANNEL_OUT_QUAD;
info->channelMask |= AUDIO_CHANNEL_OUT_LOW_FREQUENCY;
break;
case 0xa:
info->channelMask = AUDIO_CHANNEL_OUT_PENTA;
break;
case 0xb:
info->channelMask = AUDIO_CHANNEL_OUT_5POINT1;
break;
case 0xc:
info->channelMask = AUDIO_CHANNEL_OUT_QUAD;
info->channelMask |= AUDIO_CHANNEL_OUT_BACK_CENTER;
break;
case 0xd:
info->channelMask = AUDIO_CHANNEL_OUT_QUAD;
info->channelMask |= AUDIO_CHANNEL_OUT_LOW_FREQUENCY;
info->channelMask |= AUDIO_CHANNEL_OUT_BACK_CENTER;
break;
case 0xe:
info->channelMask = AUDIO_CHANNEL_OUT_PENTA;
info->channelMask |= AUDIO_CHANNEL_OUT_BACK_CENTER;
break;
case 0xf:
info->channelMask = AUDIO_CHANNEL_OUT_5POINT1;
info->channelMask |= AUDIO_CHANNEL_OUT_BACK_CENTER;
break;
case 0x10:
info->channelMask = AUDIO_CHANNEL_OUT_QUAD;
info->channelMask |= AUDIO_CHANNEL_OUT_SIDE_LEFT;
info->channelMask |= AUDIO_CHANNEL_OUT_SIDE_RIGHT;
break;
case 0x11:
info->channelMask = AUDIO_CHANNEL_OUT_QUAD;
info->channelMask |= AUDIO_CHANNEL_OUT_LOW_FREQUENCY;
info->channelMask |= AUDIO_CHANNEL_OUT_SIDE_LEFT;
info->channelMask |= AUDIO_CHANNEL_OUT_SIDE_RIGHT;
break;
case 0x12:
info->channelMask = AUDIO_CHANNEL_OUT_QUAD;
info->channelMask |= AUDIO_CHANNEL_OUT_FRONT_CENTER;
info->channelMask |= AUDIO_CHANNEL_OUT_SIDE_LEFT;
info->channelMask |= AUDIO_CHANNEL_OUT_SIDE_RIGHT;
break;
case 0x13:
info->channelMask = AUDIO_CHANNEL_OUT_7POINT1;
break;
case 0x14:
info->channelMask = AUDIO_CHANNEL_OUT_FRONT_LEFT;
info->channelMask |= AUDIO_CHANNEL_OUT_FRONT_RIGHT;
info->channelMask |= AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER;
info->channelMask |= AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER;
break;
case 0x15:
info->channelMask = AUDIO_CHANNEL_OUT_2POINT1;
info->channelMask |= AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER;
info->channelMask |= AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER;
break;
case 0x16:
info->channelMask = AUDIO_CHANNEL_OUT_FRONT_LEFT;
info->channelMask |= AUDIO_CHANNEL_OUT_FRONT_RIGHT;
info->channelMask |= AUDIO_CHANNEL_OUT_FRONT_CENTER;
info->channelMask |= AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER;
info->channelMask |= AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER;
break;
case 0x17:
info->channelMask = AUDIO_CHANNEL_OUT_2POINT1;
info->channelMask |= AUDIO_CHANNEL_OUT_FRONT_CENTER;
info->channelMask |= AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER;
info->channelMask |= AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER;
break;
case 0x18:
info->channelMask = AUDIO_CHANNEL_OUT_FRONT_LEFT;
info->channelMask |= AUDIO_CHANNEL_OUT_FRONT_RIGHT;
info->channelMask |= AUDIO_CHANNEL_OUT_BACK_CENTER;
info->channelMask |= AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER;
info->channelMask |= AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER;
break;
case 0x19:
info->channelMask = AUDIO_CHANNEL_OUT_2POINT1;
info->channelMask |= AUDIO_CHANNEL_OUT_BACK_CENTER;
info->channelMask |= AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER;
info->channelMask |= AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER;
break;
case 0x1a:
info->channelMask = AUDIO_CHANNEL_OUT_SURROUND;
info->channelMask |= AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER;
info->channelMask |= AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER;
break;
case 0x1b:
info->channelMask = AUDIO_CHANNEL_OUT_SURROUND;
info->channelMask |= AUDIO_CHANNEL_OUT_LOW_FREQUENCY;
info->channelMask |= AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER;
info->channelMask |= AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER;
break;
case 0x1c:
info->channelMask = AUDIO_CHANNEL_OUT_QUAD;
info->channelMask |= AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER;
info->channelMask |= AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER;
break;
case 0x1d:
info->channelMask = AUDIO_CHANNEL_OUT_QUAD;
info->channelMask |= AUDIO_CHANNEL_OUT_LOW_FREQUENCY;
info->channelMask |= AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER;
info->channelMask |= AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER;
break;
case 0x1e:
info->channelMask = AUDIO_CHANNEL_OUT_PENTA;
info->channelMask |= AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER;
info->channelMask |= AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER;
break;
case 0x1f:
info->channelMask = AUDIO_CHANNEL_OUT_5POINT1;
info->channelMask |= AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER;
info->channelMask |= AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER;
break;
case 0x2f:
info->channelMask = AUDIO_CHANNEL_OUT_5POINT1POINT2;
break;
default:
break;
}
PAL_DBG(LOG_TAG," channel mask updated to %d", info->channelMask);
}
void DisplayPort::dumpEdidData(edidAudioInfo *info)
{
int i;
for (i = 0; i < info->audioBlocks && i < MAX_EDID_BLOCKS; i++) {
PAL_VERBOSE(LOG_TAG,"FormatId:%d rate:%d bps:%d channels:%d",
info->audioBlocksArray[i].formatId,
info->audioBlocksArray[i].samplingFreqBitmask,
info->audioBlocksArray[i].bitsPerSampleBitmask,
info->audioBlocksArray[i].channels);
}
PAL_VERBOSE(LOG_TAG,"no of audio blocks:%d", info->audioBlocks);
PAL_VERBOSE(LOG_TAG,"speaker allocation:[%x %x %x]",
info->speakerAllocation[0], info->speakerAllocation[1],
info->speakerAllocation[2]);
PAL_VERBOSE(LOG_TAG,"channel map:[%x %x %x %x %x %x %x %x]",
info->channelMap[0], info->channelMap[1],
info->channelMap[2], info->channelMap[3],
info->channelMap[4], info->channelMap[5],
info->channelMap[6], info->channelMap[7]);
PAL_VERBOSE(LOG_TAG,"channel allocation:%d", info->channelAllocation);
PAL_VERBOSE(LOG_TAG,"[%d %d %d %d %d %d %d %d ]",
info->channelMap[0], info->channelMap[1],
info->channelMap[2], info->channelMap[3],
info->channelMap[4], info->channelMap[5],
info->channelMap[6], info->channelMap[7]);
}
bool DisplayPort::getSinkCaps(edidAudioInfo* info, char *edidData)
{
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, countDesc;
if (!info || !edidData) {
PAL_ERR(LOG_TAG,"No valid EDID");
return false;
}
length = (int) *edidData++;
PAL_VERBOSE(LOG_TAG,"Total length is %d",length);
countDesc = length/MIN_AUDIO_DESC_LENGTH;
if (!countDesc) {
PAL_ERR(LOG_TAG,"insufficient descriptors");
return false;
}
memset(info, 0, sizeof(edidAudioInfo));
info->audioBlocks = countDesc-1;
if (info->audioBlocks > MAX_EDID_BLOCKS) {
info->audioBlocks = MAX_EDID_BLOCKS;
}
PAL_VERBOSE(LOG_TAG,"Total # of audio descriptors %d",countDesc);
for (i=0; i<info->audioBlocks; i++) {
// last block for speaker allocation;
channels [i] = (*edidData & 0x7) + 1;
formats [i] = (*edidData++) >> 3;
frequency[i] = *edidData++;
bitrate [i] = *edidData++;
}
info->speakerAllocation[0] = *edidData++;
info->speakerAllocation[1] = *edidData++;
info->speakerAllocation[2] = *edidData++;
updateChannelMap(info);
updateChannelAllocation(info);
updateChannelMapLpass(info);
updateChannelMask(info);
for (i=0; i<info->audioBlocks; i++) {
PAL_VERBOSE(LOG_TAG,"AUDIO DESC BLOCK # %d\n",i);
info->audioBlocksArray[i].channels = channels[i];
PAL_DBG(LOG_TAG,"info->audioBlocksArray[i].channels %d\n",
info->audioBlocksArray[i].channels);
PAL_VERBOSE(LOG_TAG,"Format Byte %d\n", formats[i]);
info->audioBlocksArray[i].formatId = (edidAudioFormatId)formats[i];
PAL_DBG(LOG_TAG,"info->audioBlocksArray[i].formatId %s",
edidFormatToStr(formats[i]));
PAL_VERBOSE(LOG_TAG,"Frequency Bitmask %d\n", frequency[i]);
info->audioBlocksArray[i].samplingFreqBitmask = frequency[i];
PAL_VERBOSE(LOG_TAG,"info->audioBlocksArray[i].samplingFreqBitmask %d",
info->audioBlocksArray[i].samplingFreqBitmask);
PAL_VERBOSE(LOG_TAG,"BitsPerSample Bitmask %d\n", bitrate[i]);
info->audioBlocksArray[i].bitsPerSampleBitmask =
getEdidBpsByte(bitrate[i],formats[i]);
PAL_VERBOSE(LOG_TAG,"info->audioBlocksArray[i].bitsPerSampleBitmask %d",
info->audioBlocksArray[i].bitsPerSampleBitmask);
}
dumpSpeakerAllocation(info);
dumpEdidData(info);
return true;
}
bool DisplayPort::isSupportedSR(edidAudioInfo* info, int sr)
{
int i = 0;
struct extDispState *state = NULL;
state = &extDisp[dp_controller][dp_stream];
if (state && state->edidInfo)
{
info = (edidAudioInfo*) state->edidInfo;
}
if (info != NULL && sr != 0) {
for (i = 0; i < info->audioBlocks && i < MAX_EDID_BLOCKS; i++) {
if (isSampleRateSupported(info->audioBlocksArray[i].samplingFreqBitmask,
sr)) {
PAL_DBG(LOG_TAG," Returns true for sample rate [%d]", sr);
return true;
}
}
}
PAL_ERR(LOG_TAG," Returns false for sample rate [%d]", sr);
return false;
}
int DisplayPort::getMaxChannel()
{
int i = 0;
struct extDispState *state = NULL;
int max_channel = 2;
edidAudioInfo *info = NULL;
state = &extDisp[dp_controller][dp_stream];
if (state && state->edidInfo)
{
info = (edidAudioInfo*) state->edidInfo;
}
if (info != NULL) {
for (i = 0; i < info->audioBlocks && i < MAX_EDID_BLOCKS; i++) {
if (info->audioBlocksArray[i].formatId == LPCM) {
if (max_channel < info->audioBlocksArray[i].channels) {
max_channel = info->audioBlocksArray[i].channels;
PAL_DBG(LOG_TAG," Max channels updated to [%d]", max_channel);
}
}
}
}
return max_channel;
}
bool DisplayPort::isSupportedBps(edidAudioInfo* 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->audioBlocks && i < MAX_EDID_BLOCKS; i++) {
if (isSupportedBps(info->audioBlocksArray[i].bitsPerSampleBitmask, bps)) {
PAL_VERBOSE(LOG_TAG," returns true for bit width [%d]", bps);
return true;
}
}
}
PAL_VERBOSE(LOG_TAG," returns false for bit width [%d]", bps);
return false;
}
int DisplayPort::getHighestSupportedSR()
{
int sr = 0;
int highestSR = 0;
int i;
struct extDispState *state = NULL;
edidAudioInfo *info = NULL;
state = &extDisp[dp_controller][dp_stream];
if (state && state->edidInfo)
{
info = (edidAudioInfo*) state->edidInfo;
}
if (info != NULL) {
for (i = 0; i < info->audioBlocks && i < MAX_EDID_BLOCKS; i++) {
sr = getHighestEdidSF(info->audioBlocksArray[i].samplingFreqBitmask);
if (sr > highestSR)
highestSR = sr;
}
}
else {
PAL_ERR(LOG_TAG," info is NULL");
highestSR = SAMPLINGRATE_48K;
}
if (highestSR == 0) {
PAL_ERR(LOG_TAG,"Unable to get Highest SR. Setting default SR");
highestSR = SAMPLINGRATE_48K;
}
PAL_VERBOSE(LOG_TAG," returns [%d] for highest supported sr", highestSR);
return highestSR;
}
int DisplayPort::getHighestSupportedBps()
{
int bpsMask = 0;
int highestBps = 0;
int i;
struct extDispState *state = NULL;
edidAudioInfo *info = NULL;
state = &extDisp[dp_controller][dp_stream];
if (state && state->edidInfo)
{
info = (edidAudioInfo*) state->edidInfo;
}
if (info != NULL) {
for (i = 0; i < info->audioBlocks && i < MAX_EDID_BLOCKS; i++) {
bpsMask = info->audioBlocksArray[i].bitsPerSampleBitmask;
if (isSupportedBps(bpsMask, 24)) {
highestBps = 24;
break;
}
else if (isSupportedBps(bpsMask, BITWIDTH_16))
if (highestBps < BITWIDTH_16)
highestBps = BITWIDTH_16;
}
}
if (highestBps == 0) {
PAL_ERR(LOG_TAG, "None of the supported BPS is highest");
highestBps = 16;
}
return highestBps;
}