| // SPDX-License-Identifier: (GPL-2.0+ OR MIT) |
| /* |
| * Copyright (c) 2019 Amlogic, Inc. All rights reserved. |
| */ |
| |
| #define DEBUG |
| #undef pr_fmt |
| #define pr_fmt(fmt) "iec_info: " fmt |
| |
| #include <sound/asoundef.h> |
| |
| #include <linux/amlogic/media/sound/aout_notify.h> |
| #include "iec_info.h" |
| #include <linux/amlogic/media/vout/hdmi_tx_ext.h> |
| |
| const struct soc_enum audio_coding_type_enum = |
| SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(audio_coding_type_names), |
| audio_coding_type_names); |
| |
| bool audio_coding_is_lpcm(enum audio_coding_types coding_type) |
| { |
| return ((coding_type >= AUDIO_CODING_TYPE_STEREO_LPCM) && |
| (coding_type <= AUDIO_CODING_TYPE_HBR_LPCM)); |
| } |
| |
| bool audio_coding_is_non_lpcm(enum audio_coding_types coding_type) |
| { |
| return ((coding_type >= AUDIO_CODING_TYPE_AC3) && |
| (coding_type != AUDIO_CODING_TYPE_PAUSE)); |
| } |
| |
| int audio_multi_clk(enum audio_coding_types coding_type) |
| { |
| int multi = 1; |
| |
| if (coding_type == AUDIO_CODING_TYPE_MULTICH_32CH_LPCM) |
| multi = 16; |
| else if (coding_type == AUDIO_CODING_TYPE_MULTICH_16CH_LPCM) |
| multi = 8; |
| else if (coding_type == AUDIO_CODING_TYPE_MULTICH_8CH_LPCM) |
| multi = 4; |
| else if (coding_type == AUDIO_CODING_TYPE_EAC3 || |
| coding_type == AUDIO_CODING_TYPE_DTS_HD || |
| coding_type == AUDIO_CODING_TYPE_MLP || |
| coding_type == AUDIO_CODING_TYPE_DTS_HD_MA || |
| coding_type == AUDIO_CODING_TYPE_AC3_LAYOUT_B) |
| multi = 4; |
| else |
| multi = 1; |
| |
| return multi; |
| } |
| |
| static unsigned int iec_rate_to_csfs(unsigned int rate) |
| { |
| unsigned int csfs = 0; |
| |
| switch (rate) { |
| case 32000: |
| csfs = IEC958_AES3_CON_FS_32000; |
| break; |
| case 44100: |
| csfs = IEC958_AES3_CON_FS_44100; |
| break; |
| case 48000: |
| csfs = IEC958_AES3_CON_FS_48000; |
| break; |
| case 88200: |
| csfs = IEC958_AES3_CON_FS_88200; |
| break; |
| case 96000: |
| csfs = IEC958_AES3_CON_FS_96000; |
| break; |
| case 176400: |
| csfs = IEC958_AES3_CON_FS_176400; |
| break; |
| case 192000: |
| csfs = IEC958_AES3_CON_FS_192000; |
| break; |
| default: |
| csfs = IEC958_AES3_CON_FS_NOTID; |
| break; |
| } |
| |
| return csfs; |
| } |
| |
| unsigned int iec_rate_from_csfs(unsigned int csfs) |
| { |
| unsigned int rate = 0; |
| |
| switch (csfs) { |
| case IEC958_AES3_CON_FS_22050: |
| rate = 22050; |
| break; |
| case IEC958_AES3_CON_FS_24000: |
| rate = 24000; |
| break; |
| case IEC958_AES3_CON_FS_32000: |
| rate = 32000; |
| break; |
| case IEC958_AES3_CON_FS_44100: |
| rate = 44100; |
| break; |
| case IEC958_AES3_CON_FS_48000: |
| rate = 48000; |
| break; |
| case IEC958_AES3_CON_FS_768000: |
| rate = 768000; |
| break; |
| case IEC958_AES3_CON_FS_88200: |
| rate = 88200; |
| break; |
| case IEC958_AES3_CON_FS_96000: |
| rate = 96000; |
| break; |
| case IEC958_AES3_CON_FS_176400: |
| rate = 176400; |
| break; |
| case IEC958_AES3_CON_FS_192000: |
| rate = 192000; |
| break; |
| case IEC958_AES3_CON_FS_NOTID: |
| rate = 0; |
| break; |
| } |
| |
| return rate; |
| } |
| |
| void iec_get_cnsmr_cs_info(struct iec_cnsmr_cs *cs_info, |
| enum audio_coding_types coding_type, |
| unsigned int channels, |
| unsigned int rate) |
| { |
| cs_info->user = false; |
| cs_info->cp = false; |
| cs_info->emphasis = 0x0; |
| cs_info->mode = 0x0; |
| cs_info->source_num = 0x0; |
| cs_info->channel_num = channels; |
| cs_info->clock_accuracy = 0x0; |
| |
| /* defalut cs fs */ |
| cs_info->sampling_freq = iec_rate_to_csfs(rate); |
| |
| if (audio_coding_is_non_lpcm(coding_type)) { |
| cs_info->fmt = 1; |
| /* default : laser optical product, copy from projects */ |
| cs_info->category_code = 0x19; |
| |
| /* Update cs fs */ |
| if (coding_type == AUDIO_CODING_TYPE_EAC3 || |
| coding_type == AUDIO_CODING_TYPE_DTS_HD || |
| coding_type == AUDIO_CODING_TYPE_AC3_LAYOUT_B) { |
| /* DD+ */ |
| if (rate == 32000) { |
| // TODO: need to check, inherited from old codes |
| cs_info->sampling_freq = |
| IEC958_AES3_CON_FS_32000; |
| } else if (rate == 44100) |
| cs_info->sampling_freq = |
| IEC958_AES3_CON_FS_176400; |
| else |
| cs_info->sampling_freq = |
| IEC958_AES3_CON_FS_192000; |
| |
| } else if (coding_type == AUDIO_CODING_TYPE_MLP || |
| coding_type == AUDIO_CODING_TYPE_DTS_HD_MA) { |
| /* True HD, MA */ |
| cs_info->sampling_freq = IEC958_AES3_CON_FS_768000; |
| } |
| } else if ((coding_type == AUDIO_CODING_TYPE_MULTICH_32CH_LPCM) || |
| (coding_type == AUDIO_CODING_TYPE_MULTICH_16CH_LPCM)) { |
| cs_info->fmt = 0; |
| cs_info->sampling_freq = IEC958_AES3_CON_FS_NOTID; |
| } else if (coding_type == AUDIO_CODING_TYPE_MULTICH_8CH_LPCM) { |
| cs_info->fmt = 0; |
| /* fs as 4 * clk */ |
| if (rate == 44100) |
| cs_info->sampling_freq = IEC958_AES3_CON_FS_176400; |
| else if (rate == 48000) |
| cs_info->sampling_freq = IEC958_AES3_CON_FS_192000; |
| else if (rate == 192000) |
| cs_info->sampling_freq = IEC958_AES3_CON_FS_768000; |
| } else { |
| cs_info->fmt = 0; |
| /* default : laser optical product*/ |
| cs_info->category_code = 0x01; |
| } |
| } |
| |
| enum audio_coding_types iec_61937_pc_to_coding_type(unsigned int pc) |
| { |
| int data_type = pc & 0x1f; |
| int subdata_type = (pc >> 5) & 0x3; |
| int frames; |
| enum audio_coding_types coding_type = AUDIO_CODING_TYPE_UNDEFINED; |
| |
| pr_debug("%s pc:%#x, data_type:%#x, subdata_type:%#x\n", |
| __func__, pc, data_type, subdata_type); |
| |
| switch (data_type) { |
| case 1: |
| coding_type = AUDIO_CODING_TYPE_AC3; |
| break; |
| case 21: |
| coding_type = AUDIO_CODING_TYPE_EAC3; |
| if (subdata_type == 0) |
| frames = 6144; |
| break; |
| case 22: |
| coding_type = AUDIO_CODING_TYPE_MLP; |
| break; |
| case 11: |
| case 12: |
| case 13: |
| coding_type = AUDIO_CODING_TYPE_DTS; |
| break; |
| case 17: |
| coding_type = AUDIO_CODING_TYPE_DTS_HD; |
| break; |
| case 3: |
| coding_type = AUDIO_CODING_TYPE_PAUSE; |
| break; |
| case 0: |
| /* invalid data */ |
| coding_type = AUDIO_CODING_TYPE_PAUSE; |
| break; |
| default: |
| break; |
| } |
| |
| return coding_type; |
| } |
| |
| const struct soc_enum aud_codec_type_enum = |
| SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(aud_codec_type_names), |
| aud_codec_type_names); |
| |
| static bool codec_is_raw(enum aud_codec_types codec_type) |
| { |
| return ((codec_type != AUD_CODEC_TYPE_STEREO_PCM) && |
| (codec_type != AUD_CODEC_TYPE_HSR_STEREO_PCM) && |
| (codec_type != AUD_CODEC_TYPE_MULTI_LPCM)); |
| } |
| |
| bool raw_is_4x_clk(enum aud_codec_types codec_type) |
| { |
| bool is_4x = false; |
| |
| if (codec_type == AUD_CODEC_TYPE_EAC3 || |
| codec_type == AUD_CODEC_TYPE_DTS_HD || |
| codec_type == AUD_CODEC_TYPE_TRUEHD || |
| codec_type == AUD_CODEC_TYPE_DTS_HD_MA || |
| codec_type == AUD_CODEC_TYPE_AC3_LAYOUT_B) { |
| is_4x = true; |
| } |
| |
| return is_4x; |
| } |
| |
| bool raw_is_hbr_audio(enum aud_codec_types codec_type) |
| { |
| if (codec_type == AUD_CODEC_TYPE_TRUEHD || |
| codec_type == AUD_CODEC_TYPE_DTS_HD_MA) |
| return true; |
| |
| return false; |
| } |
| |
| /** |
| * TDM, SPDIF and EARC keep same mpll clk frequency(491.52M) |
| * to prevent long time drift. This is to calc multiplier |
| * to the desired clk frequency according to codec type. |
| * As driver has done multiplier to sysclk by raw_is_4x_clk() before. |
| * NOTE: HBR alsa config samplerate is 192000 while DD and DDP config 48000. |
| */ |
| unsigned int mpll2sys_clk_ratio_by_type(enum aud_codec_types codec_type) |
| { |
| /* pcm format mpll clk ratio: 491520000/6144000/EARC_DMAC_MUTIPLIER */ |
| unsigned int ratio = 16; |
| |
| if (raw_is_4x_clk(codec_type)) { |
| if (raw_is_hbr_audio(codec_type)) |
| ratio = 1; |
| else |
| ratio = 4; |
| } |
| |
| return ratio * EARC_DMAC_MUTIPLIER; |
| } |
| |
| unsigned int mpll2dmac_clk_ratio_by_type(enum audio_coding_types coding_type) |
| { |
| /* pcm format mpll clk ratio: 491520000/(6144000*EARC_DMAC_MUTIPLIER)*/ |
| unsigned int ratio; |
| |
| switch (coding_type) { |
| case AUDIO_CODING_TYPE_MULTICH_16CH_LPCM: |
| ratio = 2; |
| break; |
| case AUDIO_CODING_TYPE_MULTICH_8CH_LPCM: |
| ratio = 4; |
| break; |
| case AUDIO_CODING_TYPE_EAC3: |
| case AUDIO_CODING_TYPE_DTS_HD: |
| case AUDIO_CODING_TYPE_AC3_LAYOUT_B: |
| ratio = 4; |
| break; |
| case AUDIO_CODING_TYPE_DTS_HD_MA: |
| case AUDIO_CODING_TYPE_MLP: |
| case AUDIO_CODING_TYPE_MULTICH_32CH_LPCM: |
| ratio = 1; |
| break; |
| default: |
| ratio = 16; |
| } |
| |
| return ratio; |
| } |
| |
| void iec_get_channel_status_info(struct iec958_chsts *chsts, |
| enum aud_codec_types codec_type, |
| unsigned int rate, |
| unsigned int l_bit) |
| { |
| int rate_bit = snd_pcm_rate_to_rate_bit(rate); |
| |
| if (rate_bit == SNDRV_PCM_RATE_KNOT) { |
| pr_err("Unsupport sample rate\n"); |
| return; |
| } |
| |
| if (codec_is_raw(codec_type)) { |
| chsts->chstat0_l = 0x1902; |
| chsts->chstat0_r = 0x1902; |
| if (codec_type == AUD_CODEC_TYPE_EAC3 || |
| codec_type == AUD_CODEC_TYPE_DTS_HD) { |
| /* DD+ */ |
| if (rate_bit == SNDRV_PCM_RATE_32000) { |
| chsts->chstat1_l = 0x300; |
| chsts->chstat1_r = 0x300; |
| } else if (rate_bit == SNDRV_PCM_RATE_44100) { |
| chsts->chstat1_l = 0xc00; |
| chsts->chstat1_r = 0xc00; |
| } else { |
| chsts->chstat1_l = 0xe00; |
| chsts->chstat1_r = 0xe00; |
| } |
| } else if (codec_type == AUD_CODEC_TYPE_TRUEHD || |
| codec_type == AUD_CODEC_TYPE_DTS_HD_MA) { |
| /* True HD, MA */ |
| chsts->chstat1_l = 0x900; |
| chsts->chstat1_r = 0x900; |
| } else { |
| /* DTS,DD */ |
| if (rate_bit == SNDRV_PCM_RATE_32000) { |
| chsts->chstat1_l = 0x300; |
| chsts->chstat1_r = 0x300; |
| } else if (rate_bit == SNDRV_PCM_RATE_44100) { |
| chsts->chstat1_l = 0; |
| chsts->chstat1_r = 0; |
| } else { |
| chsts->chstat1_l = 0x200; |
| chsts->chstat1_r = 0x200; |
| } |
| } |
| } else { |
| chsts->chstat0_l = 0x0100; |
| chsts->chstat0_r = 0x0100; |
| chsts->chstat1_l = 0x200; |
| chsts->chstat1_r = 0x200; |
| |
| if (rate_bit == SNDRV_PCM_RATE_44100) { |
| chsts->chstat1_l = 0; |
| chsts->chstat1_r = 0; |
| } else if (rate_bit == SNDRV_PCM_RATE_88200) { |
| chsts->chstat1_l = 0x800; |
| chsts->chstat1_r = 0x800; |
| } else if (rate_bit == SNDRV_PCM_RATE_96000) { |
| chsts->chstat1_l = 0xa00; |
| chsts->chstat1_r = 0xa00; |
| } else if (rate_bit == SNDRV_PCM_RATE_176400) { |
| chsts->chstat1_l = 0xc00; |
| chsts->chstat1_r = 0xc00; |
| } else if (rate_bit == SNDRV_PCM_RATE_192000) { |
| chsts->chstat1_l = 0xe00; |
| chsts->chstat1_r = 0xe00; |
| } |
| } |
| |
| if (l_bit) { |
| chsts->chstat0_l |= 1 << 15; |
| chsts->chstat0_r |= 1 << 15; |
| } |
| pr_debug("rate: %d, codec_type:0x%x, channel status L:0x%x, R:0x%x\n", |
| rate, |
| codec_type, |
| ((chsts->chstat1_l >> 8) & 0xf) << 24 | chsts->chstat0_l, |
| ((chsts->chstat1_r >> 8) & 0xf) << 24 | chsts->chstat0_r); |
| } |
| |
| void spdif_notify_to_hdmitx(struct snd_pcm_substream *substream, |
| enum aud_codec_types codec_type) |
| { |
| struct aud_para aud_param; |
| |
| memset(&aud_param, 0, sizeof(aud_param)); |
| |
| aud_param.rate = substream->runtime->rate; |
| aud_param.size = substream->runtime->sample_bits; |
| aud_param.chs = substream->runtime->channels; |
| |
| if (codec_type == AUD_CODEC_TYPE_AC3) { |
| aout_notifier_call_chain(AOUT_EVENT_RAWDATA_AC_3, |
| &aud_param); |
| } else if (codec_type == AUD_CODEC_TYPE_DTS) { |
| aout_notifier_call_chain(AOUT_EVENT_RAWDATA_DTS, |
| &aud_param); |
| } else if (codec_type == AUD_CODEC_TYPE_EAC3) { |
| aout_notifier_call_chain(AOUT_EVENT_RAWDATA_DOBLY_DIGITAL_PLUS, |
| &aud_param); |
| } else if (codec_type == AUD_CODEC_TYPE_DTS_HD) { |
| aud_param.fifo_rst = 1; |
| aout_notifier_call_chain(AOUT_EVENT_RAWDATA_DTS_HD, |
| &aud_param); |
| } else if (codec_type == AUD_CODEC_TYPE_TRUEHD) { |
| aud_param.fifo_rst = 1; |
| aout_notifier_call_chain(AOUT_EVENT_RAWDATA_MAT_MLP, |
| &aud_param); |
| } else if (codec_type == AUD_CODEC_TYPE_DTS_HD_MA) { |
| aout_notifier_call_chain(AOUT_EVENT_RAWDATA_DTS_HD_MA, |
| &aud_param); |
| } else { |
| aout_notifier_call_chain(AOUT_EVENT_IEC_60958_PCM, |
| &aud_param); |
| } |
| } |
| |
| #ifdef CONFIG_AMLOGIC_HDMITX |
| unsigned int aml_audio_hdmiout_mute_flag; |
| /* call HDMITX API to enable/disable internal audio out */ |
| int aml_get_hdmi_out_audio(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| ucontrol->value.integer.value[0] = !hdmitx_ext_get_audio_status(); |
| |
| aml_audio_hdmiout_mute_flag = |
| ucontrol->value.integer.value[0]; |
| return 0; |
| } |
| |
| int aml_set_hdmi_out_audio(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| bool mute = ucontrol->value.integer.value[0]; |
| |
| if (aml_audio_hdmiout_mute_flag != mute) { |
| hdmitx_ext_set_audio_output(!mute); |
| aml_audio_hdmiout_mute_flag = mute; |
| } |
| return 0; |
| } |
| #endif |