/* * 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 #include "aml_audio_log.h" #include #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__); }