| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * S/PDIF info interface |
| * |
| * Copyright (C) 2019 Amlogic,inc |
| * |
| */ |
| |
| #define DEBUG |
| #undef pr_fmt |
| #define pr_fmt(fmt) "spdif_info: " fmt |
| |
| #include <linux/amlogic/media/sound/aout_notify.h> |
| #include <linux/amlogic/media/sound/spdif_info.h> |
| #ifdef CONFIG_AMLOGIC_HDMITX |
| #include <linux/amlogic/media/vout/hdmi_tx/hdmi_tx_ext.h> |
| #endif |
| |
| static const unsigned int rates[] = { |
| 5512, 8000, 11025, 16000, 22050, 32000, 44100, |
| 48000, 64000, 88200, 96000, 176400, 192000 |
| }; |
| |
| const struct snd_pcm_hw_constraint_list snd_pcm_known_rates_spdif = { |
| .count = ARRAY_SIZE(rates), |
| .list = rates, |
| }; |
| |
| /* |
| * 0 -- other formats except(DD,DD+,DTS) |
| * 1 -- DTS |
| * 2 -- DD |
| * 3 -- DTS with 958 PCM RAW package mode |
| * 4 -- DD+ |
| */ |
| unsigned int IEC958_mode_codec; |
| EXPORT_SYMBOL(IEC958_mode_codec); |
| |
| bool spdif_is_4x_clk(void) |
| { |
| bool is_4x = false; |
| |
| if (IEC958_mode_codec == 4 || IEC958_mode_codec == 5 || |
| IEC958_mode_codec == 7 || IEC958_mode_codec == 8 |
| ) |
| is_4x = true; |
| |
| return is_4x; |
| } |
| EXPORT_SYMBOL(spdif_is_4x_clk); |
| |
| /** |
| * snd_pcm_rate_to_rate_bit - converts sample rate to SNDRV_PCM_RATE_xxx bit |
| * @rate: the sample rate to convert |
| * |
| * Return: The SNDRV_PCM_RATE_xxx flag that corresponds to the given rate, or |
| * SNDRV_PCM_RATE_KNOT for an unknown rate. |
| */ |
| unsigned int snd_pcm_rate_to_rate_bit_spdif(unsigned int rate) |
| { |
| unsigned int i; |
| |
| for (i = 0; i < snd_pcm_known_rates_spdif.count; i++) |
| if (snd_pcm_known_rates_spdif.list[i] == rate) |
| return 1u << i; |
| return SNDRV_PCM_RATE_KNOT; |
| } |
| |
| void spdif_get_channel_status_info(struct iec958_chsts *chsts, |
| unsigned int rate) |
| { |
| int rate_bit = snd_pcm_rate_to_rate_bit_spdif(rate); |
| |
| if (rate_bit == SNDRV_PCM_RATE_KNOT) { |
| pr_err("Unsupport sample rate\n"); |
| return; |
| } |
| |
| if (IEC958_mode_codec && IEC958_mode_codec != 9) { |
| if (IEC958_mode_codec == 1) { |
| /* dts, use raw sync-word mode */ |
| pr_info("iec958 mode RAW\n"); |
| } else { |
| /* ac3,use the same pcm mode as i2s */ |
| } |
| |
| chsts->chstat0_l = 0x1902; |
| chsts->chstat0_r = 0x1902; |
| if (IEC958_mode_codec == 4 || IEC958_mode_codec == 5) { |
| /* 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 { |
| /* 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; |
| } |
| } |
| pr_debug("rate: %d, channel status ch0_l:0x%x, ch0_r:0x%x, ch1_l:0x%x, ch1_r:0x%x\n", |
| rate, |
| chsts->chstat0_l, |
| chsts->chstat0_r, |
| chsts->chstat1_l, |
| chsts->chstat1_r |
| ); |
| } |
| EXPORT_SYMBOL(spdif_get_channel_status_info); |
| |
| void spdif_notify_to_hdmitx(struct snd_pcm_substream *substream) |
| { |
| if (IEC958_mode_codec == 2) { |
| aout_notifier_call_chain(AOUT_EVENT_RAWDATA_AC_3, substream); |
| } else if (IEC958_mode_codec == 3) { |
| aout_notifier_call_chain(AOUT_EVENT_RAWDATA_DTS, substream); |
| } else if (IEC958_mode_codec == 4) { |
| aout_notifier_call_chain(AOUT_EVENT_RAWDATA_DOBLY_DIGITAL_PLUS, |
| substream); |
| } else if (IEC958_mode_codec == 5) { |
| aout_notifier_call_chain(AOUT_EVENT_RAWDATA_DTS_HD, substream); |
| } else if (IEC958_mode_codec == 7 || |
| IEC958_mode_codec == 8) { |
| //aml_aiu_write(AIU_958_CHSTAT_L0, 0x1902); |
| //aml_aiu_write(AIU_958_CHSTAT_L1, 0x900); |
| //aml_aiu_write(AIU_958_CHSTAT_R0, 0x1902); |
| //aml_aiu_write(AIU_958_CHSTAT_R1, 0x900); |
| if (IEC958_mode_codec == 8) |
| aout_notifier_call_chain(AOUT_EVENT_RAWDATA_DTS_HD_MA, |
| substream); |
| else |
| aout_notifier_call_chain(AOUT_EVENT_RAWDATA_MAT_MLP, |
| substream); |
| |
| } else { |
| aout_notifier_call_chain(AOUT_EVENT_IEC_60958_PCM, substream); |
| } |
| } |
| EXPORT_SYMBOL(spdif_notify_to_hdmitx); |
| |
| static const char *const spdif_format_texts[10] = { |
| "2 CH PCM", "DTS RAW Mode", "Dolby Digital", "DTS", |
| "DD+", "DTS-HD", "Multi-channel LPCM", "TrueHD", "DTS-HD MA", |
| "HIGH SR Stereo LPCM" |
| }; |
| |
| const struct soc_enum spdif_format_enum = |
| SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(spdif_format_texts), |
| spdif_format_texts); |
| EXPORT_SYMBOL(spdif_format_enum); |
| |
| int spdif_format_get_enum(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| ucontrol->value.enumerated.item[0] = IEC958_mode_codec; |
| return 0; |
| } |
| EXPORT_SYMBOL(spdif_format_get_enum); |
| |
| int spdif_format_set_enum(struct snd_kcontrol *kcontrol, |
| struct snd_ctl_elem_value *ucontrol) |
| { |
| int index = ucontrol->value.enumerated.item[0]; |
| |
| if (index >= 10) { |
| pr_err("bad parameter for spdif format set\n"); |
| return -1; |
| } |
| IEC958_mode_codec = index; |
| return 0; |
| } |
| EXPORT_SYMBOL(spdif_format_set_enum); |
| |
| #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 |