blob: 3c691713eac2eb61930e6f2f5afde2f07a2aaae1 [file] [log] [blame]
// 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