blob: 4b40f905de21c18e24dc814a5ee8b41c57345c3e [file] [log] [blame]
/*
* Copyright (C) 2021 Amlogic Corporation.
*
* 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_input_hdmi"
//#define LOG_NDEBUG 0
/*
*This table shows the meaning of bytes 1 to 3 of the Short Audio Descriptor.
*|-----------------------------------------------------------------------------------------|
*| | Bits |
*| Byte |----------------------------------------------------------------------------|
*| # | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
*|-----------------------------------------------------------------------------------------|
*| 1 | F17 = 0 | Audio format code |Maximum number of channels – 1|
*|-----------------------------------------------------------------------------------------|
*| 2 | F27 = 0 | 192kHz |176.4kHz| 96kHz | 88.2kHz| 48kHz | 44.1kHz | 32kHz |
*|-----------------------------------------------------------------------------------------|
*| 3 | Audio format code–dependent value |
*|-----------------------------------------------------------------------------------------|
*/
/*
*The audio format code values (bits [6:3] of byte 1) for Dolby technologies are:
*• 0x2 = Dolby Digital
*• 0xA = Dolby Digital Plus
*• 0xC = Dolby TrueHD and Dolby MAT
*/
#define SAD_SIZE 3 /* SAD only has 3bytes */
#define DOLBY_DIGITAL 0x2
#define DOLBY_DIGITAL_PLUS 0xA
#define DOLBY_TRUEHD_AND_DOLBY_MAT 0xC
#define AUDIO_FORMAT_CODE_BYTE1_BIT3 3
#include <errno.h>
#include <cutils/log.h>
#include <pthread.h>
#include <cutils/properties.h>
#include "audio_hw.h"
#include "audio_hw_utils.h"
#include "hdmirx_utils.h"
#include "aml_alsa_mixer.h"
#include "aml_audio_stream.h"
#include "dolby_lib_api.h"
#include "audio_hw_resource_mgr.h"
//assign it to hw_resource_manager or patch_manager
typedef struct hdmi_capability_manager {
/* The HDMI ARC capability info currently set. */
struct aml_arc_hdmi_desc hdmi_descs;
/* Save the HDMI ARC actual capability info. */
struct aml_arc_hdmi_desc hdmi_arc_capability_desc;
/*it is used to save the string of last set_arc_hdmi and to check whether ARC or EARC status has changed*/
char last_arc_hdmi_array[EDID_ARRAY_MAX_LEN]; //
/* HDMIRX default EDID */
char default_EDID_array[EDID_ARRAY_MAX_LEN]; //
bool need_to_update_arc_status;
int arc_hdmi_updated;
pthread_mutex_t lock;
} hdmi_capability_manager;
struct audio_format_code_list {
AML_HDMI_FORMAT_E id;
char audio_format_code_name[32];
};
static const struct audio_format_code_list gAudioFormatList[] = {
{AML_HDMI_FORMAT_RESERVED1, "AML_HDMI_FORMAT_RESERVED1"},
{AML_HDMI_FORMAT_LPCM, "AUDIO_FORMAT_LPCM"},
{AML_HDMI_FORMAT_AC3, "AUDIO_FORMAT_AC3"},
{AML_HDMI_FORMAT_MPEG1, "AUDIO_FORMAT_MPEG1"},
{AML_HDMI_FORMAT_MP3, "AUDIO_FORMAT_MP3"},
{AML_HDMI_FORMAT_MPEG2MC, "AUDIO_FORMAT_MPEG2MC"},
{AML_HDMI_FORMAT_AAC, "AUDIO_FORMAT_AAC"},
{AML_HDMI_FORMAT_DTS, "AUDIO_FORMAT_DTS"},
{AML_HDMI_FORMAT_ATRAC, "AUDIO_FORMAT_ATRAC"},
{AML_HDMI_FORMAT_OBA, "AUDIO_FORMAT_OBA"},
{AML_HDMI_FORMAT_DDP, "AUDIO_FORMAT_DDP"},
{AML_HDMI_FORMAT_DTSHD, "AUDIO_FORMAT_DTSHD"},
{AML_HDMI_FORMAT_MAT, "AUDIO_FORMAT_MAT"},
{AML_HDMI_FORMAT_DST, "AUDIO_FORMAT_DST"},
{AML_HDMI_FORMAT_WMAPRO, "AUDIO_FORMAT_WMAPRO"},
{AML_HDMI_FORMAT_RESERVED2, "AUDIO_FORMAT_RESERVED2"},
};
static const char *get_audio_format_code_name_by_id(int fmt_id)
{
int i;
int cnt_mixer = sizeof(gAudioFormatList) / sizeof(struct audio_format_code_list);
for (i = 0; i < cnt_mixer; i++) {
if (gAudioFormatList[i].id == fmt_id) {
return gAudioFormatList[i].audio_format_code_name;
}
}
return NULL;
}
struct hdmi_capability_manager *get_hdmi_capability_manager(struct aml_audio_device *adev);
static bool is_arc_status_update(struct aml_audio_device *adev)
{
struct hdmi_capability_manager *mgr = get_hdmi_capability_manager(adev);
return mgr->need_to_update_arc_status;
}
static void set_arc_status_update(struct aml_audio_device *adev, bool enable)
{
struct hdmi_capability_manager *mgr = get_hdmi_capability_manager(adev);
mgr->need_to_update_arc_status = enable;
}
void set_arc_hdmi_updated(struct aml_audio_device *adev, bool enable)
{
struct hdmi_capability_manager *mgr = get_hdmi_capability_manager(adev);
mgr->arc_hdmi_updated = enable;
}
bool is_arc_hdmi_updated(struct aml_audio_device *adev)
{
struct hdmi_capability_manager *mgr = get_hdmi_capability_manager(adev);
return mgr->arc_hdmi_updated;
}
const char *get_default_edid_str(struct aml_audio_device *adev)
{
struct hdmi_capability_manager *mgr = get_hdmi_capability_manager(adev);
return mgr->default_EDID_array;
}
/*
* function: read edid from hdmirx and update it to default string
*/
static int read_default_edid_from_hdmirx(struct audio_hw_device *dev, int request_len)
{
struct aml_audio_device *adev = (struct aml_audio_device *)dev;
struct hdmi_capability_manager *mgr = get_hdmi_capability_manager(adev);
char EDID_audio_array[EDID_ARRAY_MAX_LEN] = {0};
int n = 0;
int ret = 0;
int edid_dbg = property_get_bool("vendor.media.audio.ediddebug", 0);//get_debug_value(AML_DEBUG_AUDIOHAL_EDID);
ret = aml_mixer_ctrl_get_array(&adev->alsa_mixer, AML_MIXER_ID_HDMIIN_AUDIO_EDID,
EDID_audio_array, EDID_ARRAY_MAX_LEN);
ALOGV("%s line %d mixer %s ret %d\n", __func__, __LINE__, "HDMIIN AUDIO EDID", ret);
if (ret == 0) {
/* got the SAD_array with first TLV_HEADER_SIZE all zero */
memmove(EDID_audio_array, EDID_audio_array + TLV_HEADER_SIZE, EDID_ARRAY_MAX_LEN - TLV_HEADER_SIZE);
}
if (edid_dbg) {
for (n = 0; n < EDID_ARRAY_MAX_LEN; n++) {
ALOGI("%s line %d EDID_cur_array(%d) [%#x]\n", __func__, __LINE__, n, EDID_audio_array[n]);
}
}
char *dest_str = mgr->default_EDID_array;
if (request_len >= EDID_ARRAY_MAX_LEN)
memcpy(dest_str, EDID_audio_array, EDID_ARRAY_MAX_LEN);
else {
ALOGE("%s line %d request_len %d is less than %d, some info may lost!\n",
__func__, __LINE__, request_len, EDID_ARRAY_MAX_LEN);
memcpy(dest_str, EDID_audio_array, request_len);
}
return 0;
}
/*
* restore default edid to hdmirx
*/
static void write_default_edid_to_hdmirx(struct audio_hw_device *dev, int length)
{
struct aml_audio_device *adev = (struct aml_audio_device *)dev;
struct hdmi_capability_manager *mgr = get_hdmi_capability_manager(adev);
struct aml_arc_hdmi_desc *hdmi_desc = &mgr->hdmi_descs;
int edid_dbg = property_get_bool("vendor.media.audio.ediddebug", 0);//get_debug_value(AML_DEBUG_AUDIOHAL_EDID);
char *edid_string = mgr->default_EDID_array;
if (edid_dbg) {
for (int n = 0; n < length + TLV_HEADER_SIZE ; n++) {
ALOGI("%s line %d SAD_array(%d) [%#x]\n", __func__, __LINE__, n, edid_string[n]);
}
}
aml_mixer_ctrl_set_array(&adev->alsa_mixer, AML_MIXER_ID_HDMIIN_AUDIO_EDID,
edid_string, TLV_HEADER_SIZE);
hdmi_desc->default_edid = true;
}
/*
* Function: restore re-construct edid to hdmirx
* Description:
* update AVR ARC capability to edid
*/
static void write_new_edid_to_hdmirx(struct audio_hw_device *dev, void *edid_array, int edid_length)
{
struct aml_audio_device *adev = (struct aml_audio_device *)dev;
struct hdmi_capability_manager *mgr = get_hdmi_capability_manager(adev);
struct aml_arc_hdmi_desc *hdmi_desc = &mgr->hdmi_descs;
int edid_dbg = property_get_bool("vendor.media.audio.ediddebug", 0);//get_debug_value(AML_DEBUG_AUDIOHAL_EDID);
int suitable_edid_len = edid_length;
ALOGD("%s() edid_length %d will %s\n", __func__, edid_length ,"");
char *EDID_audio_array = edid_array;
if (edid_dbg) {
for (int n = 0; n < edid_length + TLV_HEADER_SIZE ; n++) {
ALOGI("%s line %d SAD_array(%d) [%#x]\n", __func__, __LINE__, n, EDID_audio_array[n]);
}
}
suitable_edid_len = (edid_length <= (EDID_ARRAY_MAX_LEN - TLV_HEADER_SIZE)) ? (edid_length) : (EDID_ARRAY_MAX_LEN - TLV_HEADER_SIZE);
aml_mixer_ctrl_set_array(&adev->alsa_mixer, AML_MIXER_ID_HDMIIN_AUDIO_EDID,
edid_array, (suitable_edid_len + TLV_HEADER_SIZE));
hdmi_desc->default_edid = false;
}
/*
*|-----------------------------------------------------------------------------------------------------------|
*| | Bits [1:0] of Dolby Digital Plus Short Audio Descriptor byte 3 |
*| Meaning |----------------------------------------------------------------------------------------------|
*| | Bit1 | Bit0 |
*|-----------------------------------------------------------------------------------------------------------|
*| | Dolby Atmos decoding and rendering of joint | Dolby Atmos decoding and rendering of joint |
*| If bit = 0 | object coding content from Dolby Digital Plus | object coding content is not supported. |
*| | acmod 28 is not supported. | |
*|-----------------------------------------------------------------------------------------------------------|
*| | Dolby Atmos decoding and rendering of joint | Dolby Atmos decoding and rendering of joint |
*| If bit = 1 | object coding content from Dolby Digital Plus | object coding content is supported. |
*| | acmod 28 is supported. | |
*|-----------------------------------------------------------------------------------------------------------|
*/
int update_dolby_atmos_decoding_and_rendering_cap_for_ddp_sad(
void *array
, int count
, bool is_acmod_28_supported
, bool is_joc_supported)
{
char *ddp_sad = NULL;
int ret = -1;
if (!array || (count < SAD_SIZE)) {
ALOGE("%s line %d array %p count %d\n", __func__, __LINE__, array, count);
return -1;
}
ddp_sad = (char *)array;
//ALOGV("%s line %d ddp sad [%#x %#x %#x]\n", __func__, __LINE__, ddp_sad[0], ddp_sad[1], ddp_sad[2]);
if (DOLBY_DIGITAL_PLUS == ((ddp_sad[0] >> AUDIO_FORMAT_CODE_BYTE1_BIT3)&0xF)) {
int value = (is_acmod_28_supported << 1) | is_joc_supported;
ddp_sad[2] &= 0xFC;
ddp_sad[2] |= value;
ret = 0;
}
else
ret = -1;
//ALOGV("%s line %d ddp sad [%#x %#x %#x]\n", __func__, __LINE__, ddp_sad[0], ddp_sad[1], ddp_sad[2]);
return ret;
}
/*
*--------------------------------------------------------------------------------------------
*| | Bits [1:0] of Dolby MAT and Dolby TrueHD Short Audio Descriptor byte 3 |
*| Meaning |-----------------------------------------------------------------------------|
*| | Bit1 | Bit0 |
*|------------------------------------------------------------------------------------------|
*|If bit = 0 |EMDF hash calculation required by |Only Dolby TrueHD decoding is |
*| |the Dolby MAT decoder for PCM input. |supported. |
*|------------------------------------------------------------------------------------------|
*|If bit = 1 |EMDF hash calculation not required |Dolby Atmos decoding and rendering of |
*| |by theDolby MAT decoder for PCM input.|Dolby TrueHD and PCM are supported. |
*--------------------------------------------------------------------------------------------
*Note: If bit 0 of byte 3 is set to 0, then bits 1 through 7 of byte 3 are also set to 0.
*/
int update_dolby_MAT_decoding_cap_for_dolby_MAT_and_dolby_TRUEHD_sad(
void *array
, int count
, bool is_mat_pcm_supported
, bool is_truehd_supported)
{
char *mat_sad = NULL;
int ret = -1;
if (!array || (count < SAD_SIZE)) {
ALOGE("%s line %d array %p count %d\n", __func__, __LINE__, array, count);
return -1;
}
mat_sad = (char *)array;
/* mask Atmos bit from AVR to hdmirx edid*/
/* bytes3 bit1: bit_for_mat_pcm; bytes3 bit0: bit_for_truehd */
if (DOLBY_TRUEHD_AND_DOLBY_MAT == ((mat_sad[0] >> AUDIO_FORMAT_CODE_BYTE1_BIT3)&0xF)) {
int value = (is_mat_pcm_supported << 1) | is_truehd_supported;
mat_sad[2] &= 0xFC;
mat_sad[2] |= value;
ret = 0;
} else {
ret = -1;
}
//ALOGV("%s line %d mat sad [%#x %#x %#x]\n", __func__, __LINE__, mat_sad[0], mat_sad[1], mat_sad[2]);
return ret;
}
bool is_same_edid_str(const char *str_a, const char *str_b)
{
return (strcmp(str_a, str_b) == 0 ? true : false);
}
/********************* APIs open for other module ******************************/
struct aml_arc_hdmi_desc *get_arc_hdmi_cap(struct aml_audio_device *adev)
{
struct hdmi_capability_manager *mgr = get_hdmi_capability_manager(adev);
return &mgr->hdmi_descs;
}
void restore_hdmi_desc(struct aml_audio_device *adev)
{
struct hdmi_capability_manager *mgr = get_hdmi_capability_manager(adev);
memcpy(&mgr->hdmi_descs, &mgr->hdmi_arc_capability_desc, sizeof(struct aml_arc_hdmi_desc));
}
void clear_arc_cached_edid(struct aml_audio_device *adev)
{
struct hdmi_capability_manager *mgr = get_hdmi_capability_manager(adev);
memset(mgr->last_arc_hdmi_array, 0, EDID_ARRAY_MAX_LEN);
}
void update_arc_cached_edid(struct aml_audio_device *adev, const char *str)
{
struct hdmi_capability_manager *mgr = get_hdmi_capability_manager(adev);
memcpy(mgr->last_arc_hdmi_array, str, EDID_ARRAY_MAX_LEN);
}
const char *get_arc_cached_edid(struct aml_audio_device *adev)
{
struct hdmi_capability_manager *mgr = get_hdmi_capability_manager(adev);
return mgr->last_arc_hdmi_array;
}
/* here is the array spec, about two cases */
/* 1st case: [0, 1] 0: length of all the SAD, here is zero, 1, avr port*/
/* 2nd case: [9, 0, 21, 7, 80, 62, 31, 192, 87, 6, 3] */
/* 9: length of all the SAD, [21, 7, 80][62, 31, -64][87, 6, 3] */
/* 0, avr port */
/* [21, 7, 80][62, 31, -64][87, 6, 3]*/
int set_arc_hdmi(struct audio_hw_device *dev, char *value, size_t len)
{
struct aml_audio_device *adev = (struct aml_audio_device *) dev;
struct hdmi_capability_manager *mgr = get_hdmi_capability_manager(adev);
struct aml_arc_hdmi_desc *hdmi_desc = get_arc_hdmi_cap(adev);
char *pt = NULL, *tmp = NULL;
int i = 0;
int edid_dbg = property_get_bool("vendor.media.audio.ediddebug", 0);//get_debug_value(AML_DEBUG_AUDIOHAL_EDID);
unsigned int *ptr = (unsigned int *)(&hdmi_desc->SAD[0]);
if (strlen (value) > len) {
ALOGE ("value array overflow!");
return -EINVAL;
}
if (edid_dbg)
ALOGI("%s line %d value %s\n", __func__, __LINE__, value);
if (adev->dolby_lib_type == eDolbyNull) {
return 0;
}
/*if the latest set_arc_hdmi string is same as the last, do not update the EDID*/
/*if not, we need to update the EDID and copy the string to last_arc_hdmi_array*/
if (is_same_edid_str(value, get_arc_cached_edid(adev))) {
set_arc_status_update(adev, false);
return 0;
} else {
set_arc_status_update(adev, true);
update_arc_cached_edid(adev, value);
}
memset(hdmi_desc->SAD, 0, EDID_ARRAY_MAX_LEN);
pt = strtok_r (value, "[], ", &tmp);
while (pt != NULL) {
if (i == 0) //index 0 means avr cec length
hdmi_desc->EDID_length = atoi (pt);
else if (i == 1) //index 1 means avr port
hdmi_desc->avr_port = atoi (pt);
else
hdmi_desc->SAD[TLV_HEADER_SIZE + i - 2] = atoi (pt);
pt = strtok_r (NULL, "[], ", &tmp);
i++;
}
ptr[0] = 0;
ptr[1] = (unsigned int)hdmi_desc->EDID_length;
if (hdmi_desc->EDID_length == 0) {
ALOGI("ARC is disconnect! Reset to default EDID.");
set_arc_hdmi_updated(adev, false);
write_default_edid_to_hdmirx(dev, hdmi_desc->EDID_length);
} else {
read_default_edid_from_hdmirx(dev, EDID_ARRAY_MAX_LEN);
/* if dts decoder doesn't support, don't update dts edid */
if (!adev->dts_decode_enable) {
int edid_length = hdmi_desc->EDID_length;
for (i = 0; i < edid_length/3; ) {
char AudioFormatCodes = (hdmi_desc->SAD[TLV_HEADER_SIZE + 3*i] >> 3) & 0xF;
/* mask EDID of dts and dtshd */
if (AudioFormatCodes == AML_HDMI_FORMAT_DTS || AudioFormatCodes == AML_HDMI_FORMAT_DTSHD) {
char *pr = &hdmi_desc->SAD[TLV_HEADER_SIZE + 3*i];
memmove(pr, (pr + 3), (edid_length - 3*i - 3));
edid_length -= 3;
} else {
i++;
}
}
hdmi_desc->EDID_length = edid_length;
ptr[1] = (unsigned int)hdmi_desc->EDID_length;
}
ALOGI("ARC is connected, EDID_length = [%d], ARC HDMI AVR port = [%d]",
hdmi_desc->EDID_length, hdmi_desc->avr_port);
if (edid_dbg) {
for (i = 0; i < hdmi_desc->EDID_length/3; i++) {
ALOGI ("EDID SAD in hex [%x, %x, %x]",
hdmi_desc->SAD[TLV_HEADER_SIZE + 3*i],
hdmi_desc->SAD[TLV_HEADER_SIZE + 3*i + 1],
hdmi_desc->SAD[TLV_HEADER_SIZE + 3*i + 2]);
}
}
set_arc_hdmi_updated(adev, true);
}
return 0;
}
/*
*SAD buffer protocol for Amlogic
*Bytes[0 ~ 3] = 0
*Bytes[4 ~ 7] = EDID audio length(3*n)
*Bytes[8 ~ 38] = SAD1(3Bytes) + SAD2(3Bytes)...+SADn(3Bytes)
*/
int update_edid_after_edited_audio_sad(struct audio_hw_device *dev, struct format_desc *fmt_desc)
{
struct aml_audio_device *adev = (struct aml_audio_device *)dev;
struct aml_arc_hdmi_desc *hdmi_desc = get_arc_hdmi_cap(adev);
if (fmt_desc) {
ALOGI("--[%s] support:%d, ch:%d, sample_mask:%#x, bit_rate:%d, atmos:%d after_update_edid", hdmiFormat2Str(fmt_desc->fmt),
fmt_desc->is_support, fmt_desc->max_channels, fmt_desc->sample_rate_mask, fmt_desc->max_bit_rate, fmt_desc->atmos_supported);
}
/*
* if there is no ddp/ms12 lib, don't update edid.
*/
if (adev->dolby_lib_type == eDolbyNull) {
return 0;
}
if (BYPASS == adev->hdmi_format) {
/* update the AVR's EDID */
write_new_edid_to_hdmirx(dev, (void *)&hdmi_desc->SAD[0], hdmi_desc->EDID_length);
ALOGI("Bypass mode!, update AVR EDID.");
}
else if (AUTO == adev->hdmi_format) {
if (fmt_desc && !fmt_desc->is_support) {
//if AVR doesn't support DDP, update EDID to default EDID
write_default_edid_to_hdmirx(dev, hdmi_desc->EDID_length);
} else {
/* get the default EDID audio array */
char EDID_cur_array[EDID_ARRAY_MAX_LEN] = {0};
bool is_mat_pcm_supported = fmt_desc->atmos_supported;
bool is_truehd_supported = fmt_desc->atmos_supported;
int available_edid_len = 0;
const char *default_edid_str = get_default_edid_str(adev);
memcpy(EDID_cur_array, default_edid_str, EDID_ARRAY_MAX_LEN);
/* edit the current EDID audio array to add DDP_ATMOS and MAT_pcm*/
/* loop is less then 12 as the (EDID_cur_array + TLV_HEADER_SIZE - SAD_SIZE) is 33 */
for (int n = 0; n < EDID_ARRAY_MAX_LEN / SAD_SIZE - 1; n++) {
update_dolby_atmos_decoding_and_rendering_cap_for_ddp_sad(
(void *)(EDID_cur_array + SAD_SIZE*n)
, (EDID_ARRAY_MAX_LEN - SAD_SIZE * n)
, fmt_desc->atmos_supported
, fmt_desc->atmos_supported);
update_dolby_MAT_decoding_cap_for_dolby_MAT_and_dolby_TRUEHD_sad(
(void *)(EDID_cur_array + SAD_SIZE*n)
, (EDID_ARRAY_MAX_LEN - SAD_SIZE * n)
, is_mat_pcm_supported
, is_truehd_supported);
/* From the SAD table, one invalid SAD is like this [0, 0, 0], here filter the valid SAD */
if (EDID_cur_array[SAD_SIZE*n]) {
available_edid_len += SAD_SIZE;
}
}
for (int cnt = 0; cnt < EDID_ARRAY_MAX_LEN; cnt++) {
ALOGV("%s line %d EDID_cur_array(%d) [%#x]\n", __func__, __LINE__, cnt, EDID_cur_array[cnt]);
}
/* That array, sent to HDMIRX with amixer AML_MIXER_ID_HDMIIN_AUDIO_EDID, has an special format. */
/* Skip PCM SAD */
memmove(EDID_cur_array + TLV_HEADER_SIZE - SAD_SIZE, EDID_cur_array, available_edid_len);
available_edid_len -= SAD_SIZE;
for (int cnt = 0; cnt < EDID_ARRAY_MAX_LEN; cnt++) {
ALOGV("%s line %d EDID_cur_array(%d) [%#x]\n", __func__, __LINE__, cnt, EDID_cur_array[cnt]);
}
/* when DDP Library inside, EDID should ignore MAT after DUT is connected to eARC.*/
if (adev->dolby_lib_type_last == eDolbyDcvLib) {
int edid_length = available_edid_len;
for (int i = 0; i < available_edid_len / SAD_SIZE; ) {
char AudioFormatCodes = (EDID_cur_array[TLV_HEADER_SIZE + 3*i] >> 3) & 0xF;
if (AudioFormatCodes == AML_HDMI_FORMAT_MAT) {
char *pr = &EDID_cur_array[TLV_HEADER_SIZE + 3*i];
memmove(pr, (pr + 3), (edid_length - 3*i - 3));
edid_length -= 3;
ALOGW("%s line %d will remove MAT codec %d \n", __func__, __LINE__, AudioFormatCodes);
break;
} else {
i++;
}
}
available_edid_len = edid_length;
}
unsigned int *protocol_ptr = (unsigned int *)EDID_cur_array;
protocol_ptr[0] = 0;
protocol_ptr[1] = (unsigned int)available_edid_len;
for (int cnt = 0; cnt < EDID_ARRAY_MAX_LEN; cnt++) {
ALOGV("%s line %d EDID_cur_array(%d) [%#x]\n", __func__, __LINE__, cnt, EDID_cur_array[cnt]);
}
/* update the EDID after editing*/
write_new_edid_to_hdmirx(dev, (void *)EDID_cur_array, available_edid_len);
}
} else if (hdmi_desc->default_edid == false) {
/* Reset the audio default EDID */
write_default_edid_to_hdmirx(dev, hdmi_desc->EDID_length);
}
return 0;
}
int set_arc_format(struct audio_hw_device *dev, char *value, size_t len)
{
struct aml_audio_device *adev = (struct aml_audio_device *) dev;
struct hdmi_capability_manager *mgr = get_hdmi_capability_manager(adev);
struct aml_arc_hdmi_desc *hdmi_desc = get_arc_hdmi_cap(adev);
struct format_desc *fmt_desc = NULL;
char *pt = NULL, *tmp = NULL;
int i = 0, val = 0;
AML_HDMI_FORMAT_E format = AML_HDMI_FORMAT_LPCM;
bool is_dolby_sad = false;
bool is_dts_sad = false;
if (strlen (value) > len) {
ALOGW("[%s:%d] value array len:%zu overflow!", __func__, __LINE__, strlen(value));
return -EINVAL;
}
/*because pcm format is not updated now, so we add a check here, we can remove this after pcm format is added
* when it is set arc mode, we should use 2ch pcm
*/
if (is_arc_connected(adev)) {
if (aml_mixer_ctrl_get_int(&adev->alsa_mixer, AML_MIXER_ID_EARC_TX_ATTENDED_TYPE) == ATTEND_TYPE_EARC) {
hdmi_desc->pcm_fmt.max_channels = 8;
} else {
hdmi_desc->pcm_fmt.max_channels = 2;
}
}
/*
* ex: adev_set_parameters with (const char *kvpairs = "set_ARC_format=[10, 1, 7, 6, 3]")
* after the progress: str_parms_get_str with (const char *key = "set_ARC_format")
* get the (char *value = [10, 1, 7, 6, 3])
* it means [AML_HDMI_FORMAT_DDP, is_support(1), max_channels(7+1), sample_rate_mask(6), atmos_supported(0x3&0x1)]
* so, here to analysis the Audio EDID from CEC.
*/
pt = strtok_r (value, "[], ", &tmp);
while (pt != NULL) {
val = atoi (pt);
switch (i) {
/* the first step, strtok_r got the "[", found the value of fmt_desc->fmt */
case 0:
format = val;
if (val == AML_HDMI_FORMAT_AC3) {
fmt_desc = &hdmi_desc->dd_fmt;
} else if (val == AML_HDMI_FORMAT_DDP) {
fmt_desc = &hdmi_desc->ddp_fmt;
} else if (val == AML_HDMI_FORMAT_MAT) {
fmt_desc = &hdmi_desc->mat_fmt;
/*if arc format is changed or ARC switch to EARC, we need update it, then new output can be configured*/
if (is_arc_status_update(adev)) {
set_arc_status_update(adev, false);
set_arc_hdmi_updated(adev, true);
}
} else if (val == AML_HDMI_FORMAT_LPCM) {
fmt_desc = &hdmi_desc->pcm_fmt;
} else if (val == AML_HDMI_FORMAT_DTS) {
fmt_desc = &hdmi_desc->dts_fmt;
} else if (val == AML_HDMI_FORMAT_DTSHD) {
fmt_desc = &hdmi_desc->dtshd_fmt;
} else {
ALOGW("[%s:%d] unsupport fmt:%d", __func__, __LINE__, val);
return -EINVAL;
}
fmt_desc->fmt = val;
break;
/* the second step, strtok_r got the ",", found the value of fmt_desc->is_support */
case 1:
fmt_desc->is_support = val;
break;
/* the three step, strtok_r got the ",", found the value of fmt_desc->max_channels */
case 2:
fmt_desc->max_channels = val + 1;
break;
/* the four step, strtok_r got the ",", found the value of fmt_desc->sample_rate_mask */
case 3:
fmt_desc->sample_rate_mask = val;
break;
/* the five step, strtok_r got the ",", found the value of fmt_desc->atmos_supported */
case 4:
if (format == AML_HDMI_FORMAT_AC3) {
/* byte 3, bit 0 is atmos bit*/
fmt_desc->atmos_supported = false;
/* AC3 Bytes 3 means the "Maximum bit rate divided by 8000 (8 kHz)" */
fmt_desc->max_bit_rate = val * 80;
} else if (format == AML_HDMI_FORMAT_DDP) {
/* byte 3, bit 0 is atmos bit*/
fmt_desc->atmos_supported = (val & 0x1) > 0 ? true : false;
/* when arc is connected update AVR SAD to hdmi edid */
update_edid_after_edited_audio_sad(dev, fmt_desc);
/*
* if the ARC capability format is changed, it should not support HBR(MAT/DTS-HD)
* for the sequence is LPCM -> DD -> DTS -> DDP -> DTSHD,
* which is defined in "private void setAudioFormat()" at file:DroidLogicEarcService.java
* so, here we choose the DDP part to update the sink format.
*/
update_sink_format_after_hotplug(adev);
} else if (format == AML_HDMI_FORMAT_MAT && fmt_desc->is_support == true) {
/* byte 3, bit 0 is profile bit, if profile 1 MAT, don't output MAT PCM*/
fmt_desc->atmos_supported = (val & 0x1) > 0 ? true : false;
if (fmt_desc->atmos_supported == false)
fmt_desc->is_support = false;
/* when arc is connected update AVR SAD to hdmi edid */
update_edid_after_edited_audio_sad(dev, fmt_desc);
/*
* if the ARC capability format is changed, it should not support HBR(MAT/DTS-HD)
* for the sequence is LPCM -> DD -> DTS -> DDP -> DTSHD,
* which is defined in "private void setAudioFormat()" at file:DroidLogicEarcService.java
* so, here we choose the DDP part to update the sink format.
*/
update_sink_format_after_hotplug(adev);
} else if (format == AML_HDMI_FORMAT_DTS && fmt_desc->is_support == true) {
fmt_desc->dts_vsdb_byte3 = val;
} else if (format == AML_HDMI_FORMAT_DTSHD && fmt_desc->is_support == true) {
/*
* For some devices(AVR/Soundbar) using older versions of DTSX, it will send two
* identical DTS-HD SADs with the difference being vsdb.
* We only use SADs with maximum support capabilities. Refer to #format_desc.dts_vsdb_byte3.
*/
fmt_desc->dts_vsdb_byte3 = val > fmt_desc->dts_vsdb_byte3 ? val : fmt_desc->dts_vsdb_byte3;
} else {
//TODO, how to update the other SAD.
ALOGW("[%s:%d] this SAD fmt is %s, mark it as TODO.\n",
__func__, __LINE__, get_audio_format_code_name_by_id(format));
}
break;
default:
break;
}
pt = strtok_r (NULL, "[], ", &tmp);
i++;
}
memcpy(&mgr->hdmi_arc_capability_desc, hdmi_desc, sizeof(struct aml_arc_hdmi_desc));
if (fmt_desc) {
ALOGI("----[%s] support:%d, ch:%d, sample_mask:%#x, bit_rate:%d, atmos:%d. dts_vsdb_byte3:%d", hdmiFormat2Str(fmt_desc->fmt),fmt_desc->is_support,
fmt_desc->max_channels, fmt_desc->sample_rate_mask, fmt_desc->max_bit_rate, fmt_desc->atmos_supported, fmt_desc->dts_vsdb_byte3);
}
if (is_HDMI_connected(adev))
adev->is_hdmi_arc_interact_done = true;
return 0;
}
struct hdmi_capability_manager *get_hdmi_capability_manager(struct aml_audio_device *adev)
{
if (adev->hdmi_cap_mgr == NULL) {
struct hdmi_capability_manager *mgr = aml_audio_calloc(1, sizeof(struct hdmi_capability_manager));
adev->hdmi_cap_mgr = mgr;
}
return adev->hdmi_cap_mgr;
}
int init_hdmi_capability_manager(struct aml_audio_device *adev)
{
int ret = 0;
struct hdmi_capability_manager *mgr = get_hdmi_capability_manager(adev);
if (!mgr) {
ALOGE("%s() Error, hdmi_capability_manager = NULL, return!", __func__);
return -EINVAL;
}
pthread_mutex_init(&mgr->lock, NULL);
ALOGI("%s() OK", __func__);
return ret;
}
void destroy_hdmi_capability_manager(struct aml_audio_device *adev)
{
if (!adev->hdmi_cap_mgr) {
return;
}
struct hdmi_capability_manager *mgr = get_hdmi_capability_manager(adev);
pthread_mutex_destroy(&mgr->lock);
aml_audio_free(mgr);
adev->hdmi_cap_mgr = NULL;
ALOGI("%s() done!", __func__);
}