777 lines
32 KiB
C
777 lines
32 KiB
C
/*
|
||
* 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 "aml_audio_log.h"
|
||
#include <pthread.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"
|
||
#include "aml_audio_property.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_MPEGH, "AUDIO_FORMAT_MPEGH"},
|
||
};
|
||
|
||
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 = aml_audio_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)
|
||
{
|
||
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 = aml_audio_property_get_bool("vendor.media.audio.ediddebug", 0);
|
||
char *edid_string = NULL;
|
||
int suitable_edid_len = 0;
|
||
|
||
if (adev->Hdmi_EDID_len > 0) {
|
||
suitable_edid_len = adev->Hdmi_EDID_len;
|
||
edid_string = adev->Hdmi_EDID;
|
||
AM_LOGI("Hdmi_EDID_len:%d, set EDID from json config", adev->Hdmi_EDID_len);
|
||
if (edid_dbg) {
|
||
for (int n = 0; n < adev->Hdmi_EDID_len ; n++) {
|
||
AM_LOGI("SAD_array(%d) [%#x]\n", n, edid_string[n]);
|
||
}
|
||
}
|
||
} else {
|
||
edid_string = mgr->default_EDID_array;
|
||
AM_LOGI("update default edid in driver!");
|
||
}
|
||
|
||
//when suitable_edid_len is 0, it will update default edid in driver
|
||
aml_mixer_ctrl_set_array(&adev->alsa_mixer, AML_MIXER_ID_HDMIIN_AUDIO_EDID,
|
||
edid_string, (suitable_edid_len + 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 = aml_audio_property_get_bool("vendor.media.audio.ediddebug", 0);//get_debug_value(AML_DEBUG_AUDIOHAL_EDID);
|
||
|
||
ALOGD("%s() edid_length %d will %s\n", __func__, edid_length ,"");
|
||
if (edid_length > 0) {
|
||
int suitable_edid_len = 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;
|
||
} else {
|
||
write_default_edid_to_hdmirx(dev);
|
||
}
|
||
}
|
||
|
||
/*
|
||
*|-----------------------------------------------------------------------------------------------------------|
|
||
*| | 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 aml_arc_hdmi_desc *hdmi_desc = get_arc_hdmi_cap(adev);
|
||
char *pt = NULL, *tmp = NULL;
|
||
int i = 0;
|
||
int edid_dbg = aml_audio_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);
|
||
} 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 aml_audio_device *adev = (struct aml_audio_device *)dev;
|
||
struct aml_arc_hdmi_desc *hdmi_desc = get_arc_hdmi_cap(adev);
|
||
int output_device = aml_mixer_ctrl_get_int(&adev->alsa_mixer, AML_MIXER_ID_EARC_TX_ATTENDED_TYPE);
|
||
|
||
AM_LOGI("hdmi_format:%d output_device:%d ddp atmos:%d mat atmos:%d",
|
||
adev->hdmi_format, output_device, hdmi_desc->ddp_fmt.atmos_supported, hdmi_desc->mat_fmt.atmos_supported);
|
||
|
||
/*
|
||
* if there is no ddp/ms12 lib, don't update edid.
|
||
*/
|
||
if (adev->dolby_lib_type == eDolbyNull) {
|
||
return 0;
|
||
}
|
||
|
||
/* if can't get arc/earc type from driver, it only supports arc output. */
|
||
if (output_device == ATTEND_TYPE_NONE) {
|
||
output_device = ATTEND_TYPE_ARC;
|
||
}
|
||
|
||
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) {
|
||
/* get the default EDID audio array */
|
||
char EDID_cur_array[EDID_ARRAY_MAX_LEN] = {0};
|
||
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-SAD(byte3-bit0~1) and MAT-SAD(byte3-bit0~1) */
|
||
/* loop is less than 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++) {
|
||
if (output_device == ATTEND_TYPE_ARC || output_device == ATTEND_TYPE_EARC) {
|
||
update_dolby_atmos_decoding_and_rendering_cap_for_ddp_sad(
|
||
(void *)(EDID_cur_array + SAD_SIZE*n)
|
||
, (EDID_ARRAY_MAX_LEN - SAD_SIZE * n)
|
||
, 0 , hdmi_desc->ddp_fmt.atmos_supported);
|
||
|
||
/* MAT dependent value is chanegd with ddp atmos flag: case 1948,1954 */
|
||
if (output_device == ATTEND_TYPE_ARC)
|
||
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)
|
||
, hdmi_desc->ddp_fmt.atmos_supported
|
||
, hdmi_desc->ddp_fmt.atmos_supported);
|
||
else
|
||
/* MAT dependent value is chanegd with ddp atmos flag: case 1952, 1958 */
|
||
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)
|
||
, hdmi_desc->mat_fmt.atmos_supported
|
||
, hdmi_desc->mat_fmt.atmos_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);
|
||
|
||
if (available_edid_len >= SAD_SIZE) {
|
||
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);
|
||
}
|
||
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;
|
||
|
||
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 if (val == AML_HDMI_FORMAT_MPEGH) {
|
||
fmt_desc = &hdmi_desc->mpegh_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);
|
||
/*
|
||
* 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 & 0b11111) > 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);
|
||
/*
|
||
* 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__);
|
||
}
|
||
|