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