1251 lines
37 KiB
C
1251 lines
37 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2022 MediaTek Inc.
|
|
* Author: yiwen chiou<yiwen.chiou@mediatek.com
|
|
*/
|
|
#include <linux/regmap.h>
|
|
#include <linux/of_device.h>
|
|
#include <sound/pcm_params.h>
|
|
#include "mt6985-afe-clk.h"
|
|
#include "mt6985-afe-common.h"
|
|
#include "mt6985-afe-gpio.h"
|
|
#include "mt6985-interconnection.h"
|
|
|
|
struct mtk_afe_etdm_priv {
|
|
int id;
|
|
int etdm_rate;
|
|
int mclk_id;
|
|
int mclk_rate;
|
|
int mclk_apll;
|
|
};
|
|
|
|
enum {
|
|
ETDM_CLK_SOURCE_H26M = 0,
|
|
ETDM_CLK_SOURCE_APLL = 1,
|
|
ETDM_CLK_SOURCE_SPDIF = 2,
|
|
ETDM_CLK_SOURCE_HDMI = 3,
|
|
ETDM_CLK_SOURCE_EARC = 4,
|
|
ETDM_CLK_SOURCE_LINEIN = 5,
|
|
};
|
|
enum {
|
|
ETDM_RELATCH_SEL_H26M = 0,
|
|
ETDM_RELATCH_SEL_APLL = 1,
|
|
};
|
|
enum {
|
|
ETDM_USE_INTERCONN = 0,
|
|
ETDM_USE_TINYCONN = 1,
|
|
};
|
|
|
|
enum {
|
|
ETDM_RATE_8K = 0,
|
|
ETDM_RATE_12K = 1,
|
|
ETDM_RATE_16K = 2,
|
|
ETDM_RATE_24K = 3,
|
|
ETDM_RATE_32K = 4,
|
|
ETDM_RATE_48K = 5,
|
|
ETDM_RATE_64K = 6, //not support
|
|
ETDM_RATE_96K = 7,
|
|
ETDM_RATE_128K = 8, //not support
|
|
ETDM_RATE_192K = 9,
|
|
ETDM_RATE_256K = 10, //not support
|
|
ETDM_RATE_384K = 11,
|
|
ETDM_RATE_11025 = 16,
|
|
ETDM_RATE_22050 = 17,
|
|
ETDM_RATE_44100 = 18,
|
|
ETDM_RATE_88200 = 19,
|
|
ETDM_RATE_176400 = 20,
|
|
ETDM_RATE_352800 = 21,
|
|
};
|
|
enum {
|
|
ETDM_CONN_8K = 0,
|
|
ETDM_CONN_11K = 1,
|
|
ETDM_CONN_12K = 2,
|
|
ETDM_CONN_16K = 4,
|
|
ETDM_CONN_22K = 5,
|
|
ETDM_CONN_24K = 6,
|
|
ETDM_CONN_32K = 8,
|
|
ETDM_CONN_44K = 9,
|
|
ETDM_CONN_48K = 10,
|
|
ETDM_CONN_88K = 13,
|
|
ETDM_CONN_96K = 14,
|
|
ETDM_CONN_176K = 17,
|
|
ETDM_CONN_192K = 18,
|
|
ETDM_CONN_352K = 21,
|
|
ETDM_CONN_384K = 22,
|
|
};
|
|
enum {
|
|
ETDM_WLEN_8_BIT = 0x7,
|
|
ETDM_WLEN_16_BIT = 0xf,
|
|
ETDM_WLEN_32_BIT = 0x1f,
|
|
};
|
|
enum {
|
|
ETDM_SLAVE_SEL_ETDMIN0_MASTER = 0,
|
|
ETDM_SLAVE_SEL_ETDMIN0_SLAVE = 1,
|
|
ETDM_SLAVE_SEL_ETDMIN1_MASTER = 2,
|
|
ETDM_SLAVE_SEL_ETDMIN1_SLAVE = 3,
|
|
ETDM_SLAVE_SEL_ETDMIN2_MASTER = 4,
|
|
ETDM_SLAVE_SEL_ETDMIN2_SLAVE = 5,
|
|
ETDM_SLAVE_SEL_ETDMIN3_MASTER = 6,
|
|
ETDM_SLAVE_SEL_ETDMIN3_SLAVE = 7,
|
|
ETDM_SLAVE_SEL_ETDMOUT0_MASTER = 8,
|
|
ETDM_SLAVE_SEL_ETDMOUT0_SLAVE = 9,
|
|
ETDM_SLAVE_SEL_ETDMOUT1_MASTER = 10,
|
|
ETDM_SLAVE_SEL_ETDMOUT1_SLAVE = 11,
|
|
ETDM_SLAVE_SEL_ETDMOUT2_MASTER = 12,
|
|
ETDM_SLAVE_SEL_ETDMOUT2_SLAVE = 13,
|
|
ETDM_SLAVE_SEL_ETDMOUT3_MASTER = 14,
|
|
ETDM_SLAVE_SEL_ETDMOUT3_SLAVE = 15,
|
|
};
|
|
|
|
enum {
|
|
DAI_ETDMIN = 0,
|
|
DAI_ETDMOUT,
|
|
DAI_ETDM_NUM,
|
|
};
|
|
|
|
#define ETDMIN_MCLK_EN_W_NAME "ETDMIN_MCLK_EN"
|
|
#define ETDMOUT_MCLK_EN_W_NAME "ETDMOUT_MCLK_EN"
|
|
|
|
static const struct mtk_afe_etdm_priv mt6985_etdm_priv[DAI_ETDM_NUM] = {
|
|
[DAI_ETDMIN] = {
|
|
.id = MT6985_DAI_ETDMIN,
|
|
.etdm_rate = SNDRV_PCM_RATE_8000,
|
|
},
|
|
[DAI_ETDMOUT] = {
|
|
.id = MT6985_DAI_ETDMOUT,
|
|
.etdm_rate = SNDRV_PCM_RATE_8000,
|
|
},
|
|
};
|
|
|
|
static int get_etdm_id_by_name(struct mtk_base_afe *afe,
|
|
const char *name)
|
|
{
|
|
if (strstr(name, "ETDM Capture"))
|
|
return MT6985_DAI_ETDMIN;
|
|
else if (strstr(name, "ETDM Playback"))
|
|
return MT6985_DAI_ETDMOUT;
|
|
else
|
|
return -EINVAL;
|
|
}
|
|
|
|
static struct mtk_afe_etdm_priv *get_etdm_priv_by_name(struct mtk_base_afe *afe,
|
|
const char *name)
|
|
{
|
|
struct mt6985_afe_private *afe_priv = afe->platform_priv;
|
|
int dai_id = get_etdm_id_by_name(afe, name);
|
|
|
|
if (dai_id < 0)
|
|
return NULL;
|
|
|
|
return afe_priv->dai_priv[dai_id];
|
|
}
|
|
|
|
static unsigned int get_etdm_wlen(snd_pcm_format_t format)
|
|
{
|
|
unsigned int wlen = 0;
|
|
|
|
/* The reg_word_length should be >= reg_bit_length */
|
|
wlen = snd_pcm_format_physical_width(format);
|
|
pr_info("%s wlen %d\n", __func__, wlen);
|
|
|
|
if (wlen < 16)
|
|
return ETDM_WLEN_16_BIT;
|
|
else
|
|
return ETDM_WLEN_32_BIT;
|
|
}
|
|
|
|
static unsigned int get_etdm_lrck_width(snd_pcm_format_t format)
|
|
{
|
|
/* The valid data bit number should be large than 7 due to hardware limitation. */
|
|
return snd_pcm_format_physical_width(format) - 1;
|
|
}
|
|
|
|
static unsigned int get_etdm_rate(unsigned int rate)
|
|
{
|
|
switch (rate) {
|
|
case 8000:
|
|
return ETDM_RATE_8K;
|
|
case 12000:
|
|
return ETDM_RATE_12K;
|
|
case 16000:
|
|
return ETDM_RATE_16K;
|
|
case 24000:
|
|
return ETDM_RATE_24K;
|
|
case 32000:
|
|
return ETDM_RATE_32K;
|
|
case 48000:
|
|
return ETDM_RATE_48K;
|
|
case 64000:
|
|
return ETDM_RATE_64K;
|
|
case 96000:
|
|
return ETDM_RATE_96K;
|
|
case 128000:
|
|
return ETDM_RATE_128K;
|
|
case 192000:
|
|
return ETDM_RATE_192K;
|
|
case 256000:
|
|
return ETDM_RATE_256K;
|
|
case 384000:
|
|
return ETDM_RATE_384K;
|
|
case 11025:
|
|
return ETDM_RATE_11025;
|
|
case 22050:
|
|
return ETDM_RATE_22050;
|
|
case 44100:
|
|
return ETDM_RATE_44100;
|
|
case 88200:
|
|
return ETDM_RATE_88200;
|
|
case 176400:
|
|
return ETDM_RATE_176400;
|
|
case 352800:
|
|
return ETDM_RATE_352800;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static unsigned int get_etdm_inconn_rate(unsigned int rate)
|
|
{
|
|
pr_info("%s rate %d\n", __func__, rate);
|
|
switch (rate) {
|
|
case 8000:
|
|
return ETDM_CONN_8K;
|
|
case 12000:
|
|
return ETDM_CONN_12K;
|
|
case 16000:
|
|
return ETDM_CONN_16K;
|
|
case 24000:
|
|
return ETDM_CONN_24K;
|
|
case 32000:
|
|
return ETDM_CONN_32K;
|
|
case 48000:
|
|
return ETDM_CONN_48K;
|
|
case 96000:
|
|
return ETDM_CONN_96K;
|
|
case 192000:
|
|
return ETDM_CONN_192K;
|
|
case 384000:
|
|
return ETDM_CONN_384K;
|
|
case 11025:
|
|
return ETDM_CONN_11K;
|
|
case 22050:
|
|
return ETDM_CONN_22K;
|
|
case 44100:
|
|
return ETDM_CONN_44K;
|
|
case 88200:
|
|
return ETDM_CONN_88K;
|
|
case 176400:
|
|
return ETDM_CONN_176K;
|
|
case 352800:
|
|
return ETDM_CONN_352K;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
}
|
|
|
|
static int etdm_out_sgen_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *component =
|
|
snd_soc_kcontrol_component(kcontrol);
|
|
struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
|
|
unsigned int value = 0;
|
|
unsigned int reg = 0;
|
|
unsigned int mask = 0;
|
|
unsigned int shift = 0;
|
|
|
|
if (!strcmp(kcontrol->id.name, "ETDM_OUT1_SGEN")) {
|
|
reg = ETDM_0_3_COWORK_CON3;
|
|
mask = ETDM_OUT1_USE_SGEN_MASK_SFT;
|
|
shift = ETDM_OUT1_USE_SGEN_SFT;
|
|
} else if (!strcmp(kcontrol->id.name, "ETDM_OUT0_SGEN")) {
|
|
reg = ETDM_0_3_COWORK_CON3;
|
|
mask = ETDM_OUT1_USE_SGEN_MASK_SFT;
|
|
shift = ETDM_OUT1_USE_SGEN_SFT;
|
|
}
|
|
|
|
if (reg)
|
|
regmap_read(afe->regmap, reg, &value);
|
|
|
|
value &= mask;
|
|
value >>= shift;
|
|
ucontrol->value.enumerated.item[0] = value;
|
|
return 0;
|
|
}
|
|
|
|
static int etdm_out_sgen_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
|
struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
|
|
unsigned int value = 0;
|
|
unsigned int reg = 0;
|
|
unsigned int val = 0;
|
|
unsigned int mask = 0;
|
|
|
|
if (!strcmp(kcontrol->id.name, "ETDM_OUT1_SGEN")) {
|
|
reg = ETDM_0_3_COWORK_CON3;
|
|
mask = ETDM_OUT1_USE_SGEN_MASK_SFT;
|
|
val = value << ETDM_OUT1_USE_SGEN_SFT;
|
|
} else if (!strcmp(kcontrol->id.name, "ETDM_OUT0_SGEN")) {
|
|
reg = ETDM_0_3_COWORK_CON3;
|
|
mask = ETDM_OUT0_USE_SGEN_MASK_SFT;
|
|
val = value << ETDM_OUT0_USE_SGEN_SFT;
|
|
}
|
|
|
|
if (reg)
|
|
regmap_update_bits(afe->regmap, reg, mask, val);
|
|
return 0;
|
|
}
|
|
|
|
static const char *const etdm_out_sgen_map[] = {
|
|
"Off", "On",
|
|
};
|
|
|
|
static SOC_ENUM_SINGLE_EXT_DECL(etdm_out_sgen_map_enum,
|
|
etdm_out_sgen_map);
|
|
|
|
/* lpbk */
|
|
static const int etdm_lpbk_idx[] = {
|
|
0x0, 0xa,
|
|
};
|
|
static int etdm_lpbk_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *component =
|
|
snd_soc_kcontrol_component(kcontrol);
|
|
struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
|
|
unsigned int value = 0;
|
|
unsigned int reg = 0;
|
|
unsigned int mask = 0;
|
|
unsigned int shift = 0;
|
|
|
|
if (!strcmp(kcontrol->id.name, "ETDM_LPBK_0")) {
|
|
reg = ETDM_0_3_COWORK_CON1;
|
|
mask = ETDM_IN1_SDATA0_SEL_MASK_SFT;
|
|
shift = ETDM_IN1_SDATA0_SEL_SFT;
|
|
} else if (!strcmp(kcontrol->id.name, "ETDM_LPBK_1")) {
|
|
reg = ETDM_0_3_COWORK_CON1;
|
|
mask = ETDM_IN1_SDATA1_15_SEL_MASK_SFT;
|
|
shift = ETDM_IN1_SDATA1_15_SEL_SFT;
|
|
}
|
|
|
|
if (reg)
|
|
regmap_read(afe->regmap, reg, &value);
|
|
|
|
value &= mask;
|
|
value >>= shift;
|
|
ucontrol->value.enumerated.item[0] = value;
|
|
|
|
if (value == 0xa)
|
|
ucontrol->value.enumerated.item[0] = 1;
|
|
else
|
|
ucontrol->value.enumerated.item[0] = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int etdm_lpbk_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
|
struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
|
|
unsigned int value = ucontrol->value.integer.value[0];
|
|
unsigned int reg = 0;
|
|
unsigned int val = 0;
|
|
unsigned int mask = 0;
|
|
|
|
if (value >= ARRAY_SIZE(etdm_lpbk_idx))
|
|
return -EINVAL;
|
|
|
|
if (!strcmp(kcontrol->id.name, "ETDM_LPBK_0")) {
|
|
reg = ETDM_0_3_COWORK_CON1;
|
|
mask = ETDM_IN1_SDATA0_SEL_MASK_SFT;
|
|
val = etdm_lpbk_idx[value] << ETDM_IN1_SDATA0_SEL_SFT;
|
|
} else if (!strcmp(kcontrol->id.name, "ETDM_LPBK_1")) {
|
|
reg = ETDM_0_3_COWORK_CON1;
|
|
mask = ETDM_IN1_SDATA1_15_SEL_MASK_SFT;
|
|
val = etdm_lpbk_idx[value] << ETDM_IN1_SDATA1_15_SEL_SFT;
|
|
}
|
|
|
|
if (reg)
|
|
regmap_update_bits(afe->regmap, reg, mask, val);
|
|
return 0;
|
|
}
|
|
static const char *const etdm_lpbk_map[] = {
|
|
"Off", "On",
|
|
};
|
|
static SOC_ENUM_SINGLE_EXT_DECL(etdm_lpbk_map_enum,
|
|
etdm_lpbk_map);
|
|
/* lpbk */
|
|
|
|
/* multi-ip mode */
|
|
static const int etdm_ip_mode_idx[] = {
|
|
0x0, 0x1,
|
|
};
|
|
static int etdm_ip_mode_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *component =
|
|
snd_soc_kcontrol_component(kcontrol);
|
|
struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
|
|
unsigned int value = 0;
|
|
unsigned int reg = 0;
|
|
unsigned int mask = 0;
|
|
unsigned int shift = 0;
|
|
|
|
reg = ETDM_IN1_CON2;
|
|
mask = REG_MULTI_IP_MODE_MASK_SFT;
|
|
shift = REG_MULTI_IP_MODE_SFT;
|
|
|
|
if (reg)
|
|
regmap_read(afe->regmap, reg, &value);
|
|
|
|
value &= mask;
|
|
value >>= shift;
|
|
ucontrol->value.enumerated.item[0] = value;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int etdm_ip_mode_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
|
struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
|
|
unsigned int value = ucontrol->value.integer.value[0];
|
|
|
|
if (value >= ARRAY_SIZE(etdm_ip_mode_idx))
|
|
return -EINVAL;
|
|
|
|
/* 0: One IP multi-channel 1: Multi-IP 2-channel */
|
|
regmap_update_bits(afe->regmap, ETDM_IN1_CON2,
|
|
REG_MULTI_IP_MODE_MASK_SFT,
|
|
etdm_ip_mode_idx[value] << REG_MULTI_IP_MODE_SFT);
|
|
|
|
return 0;
|
|
}
|
|
static const char *const etdm_ip_mode_map[] = {
|
|
"Off", "On",
|
|
};
|
|
static SOC_ENUM_SINGLE_EXT_DECL(etdm_ip_mode_map_enum,
|
|
etdm_ip_mode_map);
|
|
/* multi-ip mode */
|
|
|
|
/* ch num */
|
|
static const int etdm_ch_num_idx[] = {
|
|
0x2, 0x4, 0x6, 0x8,
|
|
};
|
|
static int etdm_ch_num_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *component =
|
|
snd_soc_kcontrol_component(kcontrol);
|
|
struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
|
|
unsigned int value = 0;
|
|
unsigned int reg = 0;
|
|
unsigned int mask = 0;
|
|
unsigned int shift = 0;
|
|
|
|
if (!strcmp(kcontrol->id.name, "ETDM_IN1_CH_NUM"))
|
|
reg = ETDM_IN1_CON0;
|
|
else if (!strcmp(kcontrol->id.name, "ETDM_OUT1_CH_NUM"))
|
|
reg = ETDM_OUT1_CON0;
|
|
|
|
mask = REG_CH_NUM_MASK_SFT;
|
|
shift = REG_CH_NUM_SFT;
|
|
|
|
if (reg)
|
|
regmap_read(afe->regmap, reg, &value);
|
|
|
|
value &= mask;
|
|
value >>= shift;
|
|
|
|
if (value == 0x1)
|
|
ucontrol->value.enumerated.item[0] = 0;
|
|
else if (value == 0x3)
|
|
ucontrol->value.enumerated.item[0] = 1;
|
|
else if (value == 0x5)
|
|
ucontrol->value.enumerated.item[0] = 2;
|
|
else
|
|
ucontrol->value.enumerated.item[0] = 3;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int etdm_ch_num_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
|
struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
|
|
unsigned int value = ucontrol->value.integer.value[0];
|
|
unsigned int reg = 0;
|
|
unsigned int val = 0;
|
|
unsigned int mask = 0;
|
|
|
|
if (value >= ARRAY_SIZE(etdm_ch_num_idx))
|
|
return -EINVAL;
|
|
|
|
if (!strcmp(kcontrol->id.name, "ETDM_IN1_CH_NUM"))
|
|
reg = ETDM_IN1_CON0;
|
|
else if (!strcmp(kcontrol->id.name, "ETDM_OUT1_CH_NUM"))
|
|
reg = ETDM_OUT1_CON0;
|
|
|
|
mask = REG_CH_NUM_MASK_SFT;
|
|
val = (etdm_ch_num_idx[value] - 1) << REG_CH_NUM_SFT;
|
|
|
|
if (reg)
|
|
regmap_update_bits(afe->regmap, reg, mask, val);
|
|
|
|
return 0;
|
|
}
|
|
static const char *const etdm_ch_num_map[] = {
|
|
"2CH", "4CH", "6CH", "8CH",
|
|
};
|
|
static SOC_ENUM_SINGLE_EXT_DECL(etdm_ch_num_map_enum,
|
|
etdm_ch_num_map);
|
|
/* ch num */
|
|
|
|
/* dai component */
|
|
static const struct snd_kcontrol_new mtk_etdm_playback_ch1_mix[] = {
|
|
SOC_DAPM_SINGLE_AUTODISABLE("DL11_CH1", AFE_CONN62_2, I_DL11_CH1, 1, 0),
|
|
SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN62, I_ADDA_UL_CH1, 1, 0),
|
|
SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN62, I_PCM_2_CAP_CH1, 1, 0),
|
|
};
|
|
static const struct snd_kcontrol_new mtk_etdm_playback_ch2_mix[] = {
|
|
SOC_DAPM_SINGLE_AUTODISABLE("DL11_CH2", AFE_CONN63_2, I_DL11_CH2, 1, 0),
|
|
SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN63, I_ADDA_UL_CH2, 1, 0),
|
|
SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN63, I_ADDA_UL_CH2, 1, 0),
|
|
SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN63, I_PCM_2_CAP_CH1, 1, 0),
|
|
};
|
|
static const struct snd_kcontrol_new mtk_etdm_playback_ch3_mix[] = {
|
|
SOC_DAPM_SINGLE_AUTODISABLE("DL11_CH3", AFE_CONN64_2, I_DL11_CH3, 1, 0),
|
|
SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN62, I_ADDA_UL_CH1, 1, 0),
|
|
SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN64, I_PCM_2_CAP_CH1, 1, 0),
|
|
};
|
|
static const struct snd_kcontrol_new mtk_etdm_playback_ch4_mix[] = {
|
|
SOC_DAPM_SINGLE_AUTODISABLE("DL11_CH4", AFE_CONN65_2, I_DL11_CH4, 1, 0),
|
|
SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN63, I_ADDA_UL_CH2, 1, 0),
|
|
SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN65, I_PCM_2_CAP_CH1, 1, 0),
|
|
};
|
|
static const struct snd_kcontrol_new mtk_etdm_playback_ch5_mix[] = {
|
|
SOC_DAPM_SINGLE_AUTODISABLE("DL11_CH5", AFE_CONN66_2, I_DL11_CH5, 1, 0),
|
|
};
|
|
static const struct snd_kcontrol_new mtk_etdm_playback_ch6_mix[] = {
|
|
SOC_DAPM_SINGLE_AUTODISABLE("DL11_CH6", AFE_CONN67_2, I_DL11_CH6, 1, 0),
|
|
};
|
|
static const struct snd_kcontrol_new mtk_etdm_playback_ch7_mix[] = {
|
|
SOC_DAPM_SINGLE_AUTODISABLE("DL11_CH7", AFE_CONN68_2, I_DL11_CH7, 1, 0),
|
|
SOC_DAPM_SINGLE_AUTODISABLE("DL11_CH1", AFE_CONN68_2, I_DL11_CH1, 1, 0),
|
|
};
|
|
static const struct snd_kcontrol_new mtk_etdm_playback_ch8_mix[] = {
|
|
SOC_DAPM_SINGLE_AUTODISABLE("DL11_CH8", AFE_CONN69_2, I_DL11_CH8, 1, 0),
|
|
SOC_DAPM_SINGLE_AUTODISABLE("DL11_CH2", AFE_CONN69_2, I_DL11_CH2, 1, 0),
|
|
};
|
|
|
|
enum {
|
|
SUPPLY_SEQ_APLL,
|
|
SUPPLY_SEQ_ETDM_MCLK_EN,
|
|
SUPPLY_SEQ_ETDM_EN,
|
|
};
|
|
|
|
/* Tinyconn Mux */
|
|
enum {
|
|
TINYCONN_CH1_MUX_DL11 = 32,
|
|
TINYCONN_CH2_MUX_DL11 = 33,
|
|
TINYCONN_CH3_MUX_DL11 = 34,
|
|
TINYCONN_CH4_MUX_DL11 = 35,
|
|
TINYCONN_CH5_MUX_DL11 = 36,
|
|
TINYCONN_CH6_MUX_DL11 = 37,
|
|
TINYCONN_CH7_MUX_DL11 = 38,
|
|
TINYCONN_CH8_MUX_DL11 = 39,
|
|
TINYCONN_MUX_NONE = 0x1f,
|
|
};
|
|
|
|
static const char * const tinyconn_mux_map[] = {
|
|
"NONE",
|
|
"DL11_CH1",
|
|
"DL11_CH2",
|
|
"DL11_CH3",
|
|
"DL11_CH4",
|
|
"DL11_CH5",
|
|
"DL11_CH6",
|
|
"DL11_CH7",
|
|
"DL11_CH8",
|
|
};
|
|
|
|
static int tinyconn_mux_map_value[] = {
|
|
TINYCONN_MUX_NONE,
|
|
TINYCONN_CH1_MUX_DL11,
|
|
TINYCONN_CH2_MUX_DL11,
|
|
TINYCONN_CH3_MUX_DL11,
|
|
TINYCONN_CH4_MUX_DL11,
|
|
TINYCONN_CH5_MUX_DL11,
|
|
TINYCONN_CH6_MUX_DL11,
|
|
TINYCONN_CH7_MUX_DL11,
|
|
TINYCONN_CH8_MUX_DL11,
|
|
};
|
|
|
|
static SOC_VALUE_ENUM_SINGLE_DECL(etdm_out_ch1_tinyconn_mux_map_enum,
|
|
AFE_TINY_CONN10, O_42_CFG_SFT,
|
|
O_42_CFG_MASK, tinyconn_mux_map,
|
|
tinyconn_mux_map_value);
|
|
static const struct snd_kcontrol_new etdm_out_ch1_tinyconn_mux_control =
|
|
SOC_DAPM_ENUM("etdm ch1 tinyconn Select",
|
|
etdm_out_ch1_tinyconn_mux_map_enum);
|
|
static SOC_VALUE_ENUM_SINGLE_DECL(etdm_out_ch2_tinyconn_mux_map_enum,
|
|
AFE_TINY_CONN10, O_43_CFG_SFT,
|
|
O_43_CFG_MASK, tinyconn_mux_map,
|
|
tinyconn_mux_map_value);
|
|
static const struct snd_kcontrol_new etdm_out_ch2_tinyconn_mux_control =
|
|
SOC_DAPM_ENUM("etdm ch2 tinyconn Select",
|
|
etdm_out_ch2_tinyconn_mux_map_enum);
|
|
static SOC_VALUE_ENUM_SINGLE_DECL(etdm_out_ch3_tinyconn_mux_map_enum,
|
|
AFE_TINY_CONN11, O_44_CFG_SFT,
|
|
O_44_CFG_MASK, tinyconn_mux_map,
|
|
tinyconn_mux_map_value);
|
|
static const struct snd_kcontrol_new etdm_out_ch3_tinyconn_mux_control =
|
|
SOC_DAPM_ENUM("etdm ch3 tinyconn Select",
|
|
etdm_out_ch3_tinyconn_mux_map_enum);
|
|
static SOC_VALUE_ENUM_SINGLE_DECL(etdm_out_ch4_tinyconn_mux_map_enum,
|
|
AFE_TINY_CONN11, O_45_CFG_SFT,
|
|
O_45_CFG_MASK, tinyconn_mux_map,
|
|
tinyconn_mux_map_value);
|
|
static const struct snd_kcontrol_new etdm_out_ch4_tinyconn_mux_control =
|
|
SOC_DAPM_ENUM("etdm ch4 tinyconn Select",
|
|
etdm_out_ch4_tinyconn_mux_map_enum);
|
|
static SOC_VALUE_ENUM_SINGLE_DECL(etdm_out_ch5_tinyconn_mux_map_enum,
|
|
AFE_TINY_CONN11, O_46_CFG_SFT,
|
|
O_46_CFG_MASK, tinyconn_mux_map,
|
|
tinyconn_mux_map_value);
|
|
static const struct snd_kcontrol_new etdm_out_ch5_tinyconn_mux_control =
|
|
SOC_DAPM_ENUM("etdm ch5 tinyconn Select",
|
|
etdm_out_ch5_tinyconn_mux_map_enum);
|
|
static SOC_VALUE_ENUM_SINGLE_DECL(etdm_out_ch6_tinyconn_mux_map_enum,
|
|
AFE_TINY_CONN11, O_47_CFG_SFT,
|
|
O_47_CFG_MASK, tinyconn_mux_map,
|
|
tinyconn_mux_map_value);
|
|
static const struct snd_kcontrol_new etdm_out_ch6_tinyconn_mux_control =
|
|
SOC_DAPM_ENUM("etdm ch6 tinyconn Select",
|
|
etdm_out_ch6_tinyconn_mux_map_enum);
|
|
static SOC_VALUE_ENUM_SINGLE_DECL(etdm_out_ch7_tinyconn_mux_map_enum,
|
|
AFE_TINY_CONN12, O_48_CFG_SFT,
|
|
O_48_CFG_MASK, tinyconn_mux_map,
|
|
tinyconn_mux_map_value);
|
|
static const struct snd_kcontrol_new etdm_out_ch7_tinyconn_mux_control =
|
|
SOC_DAPM_ENUM("etdm ch7 tinyconn Select",
|
|
etdm_out_ch7_tinyconn_mux_map_enum);
|
|
static SOC_VALUE_ENUM_SINGLE_DECL(etdm_out_ch8_tinyconn_mux_map_enum,
|
|
AFE_TINY_CONN12, O_49_CFG_SFT,
|
|
O_49_CFG_MASK, tinyconn_mux_map,
|
|
tinyconn_mux_map_value);
|
|
static const struct snd_kcontrol_new etdm_out_ch8_tinyconn_mux_control =
|
|
SOC_DAPM_ENUM("etdm ch8 tinyconn Select",
|
|
etdm_out_ch8_tinyconn_mux_map_enum);
|
|
|
|
static int etdm_out_tinyconn_event(struct snd_soc_dapm_widget *w,
|
|
struct snd_kcontrol *kcontrol,
|
|
int event)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
|
|
struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
|
|
|
|
dev_info(afe->dev, "%s(), event 0x%x\n", __func__, event);
|
|
|
|
switch (event) {
|
|
case SND_SOC_DAPM_PRE_PMU:
|
|
regmap_update_bits(afe->regmap, ETDM_OUT1_CON0,
|
|
REG_USE_TINYCONN_32BIT_MASK_SFT,
|
|
ETDM_USE_TINYCONN << REG_USE_TINYCONN_32BIT_SFT);
|
|
break;
|
|
case SND_SOC_DAPM_PRE_PMD:
|
|
regmap_update_bits(afe->regmap, ETDM_OUT1_CON0,
|
|
REG_USE_TINYCONN_32BIT_MASK_SFT,
|
|
ETDM_USE_INTERCONN << REG_USE_TINYCONN_32BIT_SFT);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int mtk_mclk_en_event(struct snd_soc_dapm_widget *w,
|
|
struct snd_kcontrol *kcontrol,
|
|
int event)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
|
|
struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
|
|
struct mtk_afe_etdm_priv *etdm_priv;
|
|
|
|
dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n",
|
|
__func__, w->name, event);
|
|
|
|
etdm_priv = get_etdm_priv_by_name(afe, w->name);
|
|
|
|
if (!etdm_priv) {
|
|
AUDIO_AEE("i2s_priv == NULL");
|
|
return -EINVAL;
|
|
}
|
|
|
|
switch (event) {
|
|
case SND_SOC_DAPM_PRE_PMU:
|
|
mt6985_mck_enable(afe, etdm_priv->mclk_id, etdm_priv->mclk_rate);
|
|
break;
|
|
case SND_SOC_DAPM_POST_PMD:
|
|
etdm_priv->mclk_rate = 0;
|
|
mt6985_mck_disable(afe, etdm_priv->mclk_id);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mtk_afe_etdm_apll_connect(struct snd_soc_dapm_widget *source,
|
|
struct snd_soc_dapm_widget *sink)
|
|
{
|
|
struct snd_soc_dapm_widget *w = sink;
|
|
struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
|
|
struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
|
|
struct mtk_afe_etdm_priv *etdm_priv;
|
|
int cur_apll = 0;
|
|
int apll = 0;
|
|
|
|
etdm_priv = get_etdm_priv_by_name(afe, w->name);
|
|
|
|
/* which apll */
|
|
cur_apll = mt6985_get_apll_by_name(afe, source->name);
|
|
if (etdm_priv)
|
|
apll = mt6985_get_apll_by_rate(afe, etdm_priv->etdm_rate);
|
|
else
|
|
dev_info(cmpnt->dev, "%s(), get etdm_priv null\n", __func__);
|
|
|
|
return (apll == cur_apll) ? 1 : 0;
|
|
}
|
|
|
|
static int mtk_etdm_en_event(struct snd_soc_dapm_widget *w,
|
|
struct snd_kcontrol *kcontrol,
|
|
int event)
|
|
{
|
|
struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
|
|
struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
|
|
|
|
dev_info(cmpnt->dev, "%s(), name %s, event 0x%x\n",
|
|
__func__, w->name, event);
|
|
|
|
switch (event) {
|
|
case SND_SOC_DAPM_PRE_PMU:
|
|
mt6985_afe_gpio_request(afe, true, MT6985_DAI_ETDMIN, 0);
|
|
mt6985_afe_gpio_request(afe, true, MT6985_DAI_ETDMOUT, 0);
|
|
break;
|
|
case SND_SOC_DAPM_POST_PMD:
|
|
mt6985_afe_gpio_request(afe, false, MT6985_DAI_ETDMIN, 0);
|
|
mt6985_afe_gpio_request(afe, false, MT6985_DAI_ETDMOUT, 0);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static const struct snd_kcontrol_new mtk_dai_etdm_controls[] = {
|
|
SOC_ENUM_EXT("ETDM_OUT1_SGEN", etdm_out_sgen_map_enum,
|
|
etdm_out_sgen_get, etdm_out_sgen_put),
|
|
SOC_ENUM_EXT("ETDM_OUT0_SGEN", etdm_out_sgen_map_enum,
|
|
etdm_out_sgen_get, etdm_out_sgen_put),
|
|
SOC_ENUM_EXT("ETDM_LPBK_0", etdm_lpbk_map_enum,
|
|
etdm_lpbk_get, etdm_lpbk_put),
|
|
SOC_ENUM_EXT("ETDM_LPBK_1", etdm_lpbk_map_enum,
|
|
etdm_lpbk_get, etdm_lpbk_put),
|
|
SOC_ENUM_EXT("ETDM_IN1_IP_MODE", etdm_ip_mode_map_enum,
|
|
etdm_ip_mode_get, etdm_ip_mode_put),
|
|
SOC_ENUM_EXT("ETDM_IN1_CH_NUM", etdm_ch_num_map_enum,
|
|
etdm_ch_num_get, etdm_ch_num_put),
|
|
SOC_ENUM_EXT("ETDM_OUT1_CH_NUM", etdm_ch_num_map_enum,
|
|
etdm_ch_num_get, etdm_ch_num_put),
|
|
};
|
|
|
|
static const struct snd_soc_dapm_widget mtk_dai_etdm_widgets[] = {
|
|
/* inter-connections */
|
|
SND_SOC_DAPM_MIXER("ETDM_OUT_CH1", SND_SOC_NOPM, 0, 0,
|
|
mtk_etdm_playback_ch1_mix,
|
|
ARRAY_SIZE(mtk_etdm_playback_ch1_mix)),
|
|
SND_SOC_DAPM_MIXER("ETDM_OUT_CH2", SND_SOC_NOPM, 0, 0,
|
|
mtk_etdm_playback_ch2_mix,
|
|
ARRAY_SIZE(mtk_etdm_playback_ch2_mix)),
|
|
SND_SOC_DAPM_MIXER("ETDM_OUT_CH3", SND_SOC_NOPM, 0, 0,
|
|
mtk_etdm_playback_ch3_mix,
|
|
ARRAY_SIZE(mtk_etdm_playback_ch3_mix)),
|
|
SND_SOC_DAPM_MIXER("ETDM_OUT_CH4", SND_SOC_NOPM, 0, 0,
|
|
mtk_etdm_playback_ch4_mix,
|
|
ARRAY_SIZE(mtk_etdm_playback_ch4_mix)),
|
|
SND_SOC_DAPM_MIXER("ETDM_OUT_CH5", SND_SOC_NOPM, 0, 0,
|
|
mtk_etdm_playback_ch5_mix,
|
|
ARRAY_SIZE(mtk_etdm_playback_ch5_mix)),
|
|
SND_SOC_DAPM_MIXER("ETDM_OUT_CH6", SND_SOC_NOPM, 0, 0,
|
|
mtk_etdm_playback_ch6_mix,
|
|
ARRAY_SIZE(mtk_etdm_playback_ch6_mix)),
|
|
SND_SOC_DAPM_MIXER("ETDM_OUT_CH7", SND_SOC_NOPM, 0, 0,
|
|
mtk_etdm_playback_ch7_mix,
|
|
ARRAY_SIZE(mtk_etdm_playback_ch7_mix)),
|
|
SND_SOC_DAPM_MIXER("ETDM_OUT_CH8", SND_SOC_NOPM, 0, 0,
|
|
mtk_etdm_playback_ch8_mix,
|
|
ARRAY_SIZE(mtk_etdm_playback_ch8_mix)),
|
|
|
|
/* tiny-connections */
|
|
SND_SOC_DAPM_MUX_E("ETDM_OUT_CH1_TINYCONN_MUX", SND_SOC_NOPM, 0, 0,
|
|
&etdm_out_ch1_tinyconn_mux_control,
|
|
etdm_out_tinyconn_event,
|
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
|
|
SND_SOC_DAPM_MUX_E("ETDM_OUT_CH2_TINYCONN_MUX", SND_SOC_NOPM, 0, 0,
|
|
&etdm_out_ch2_tinyconn_mux_control,
|
|
etdm_out_tinyconn_event,
|
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
|
|
SND_SOC_DAPM_MUX_E("ETDM_OUT_CH3_TINYCONN_MUX", SND_SOC_NOPM, 0, 0,
|
|
&etdm_out_ch3_tinyconn_mux_control,
|
|
etdm_out_tinyconn_event,
|
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
|
|
SND_SOC_DAPM_MUX_E("ETDM_OUT_CH4_TINYCONN_MUX", SND_SOC_NOPM, 0, 0,
|
|
&etdm_out_ch4_tinyconn_mux_control,
|
|
etdm_out_tinyconn_event,
|
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
|
|
SND_SOC_DAPM_MUX_E("ETDM_OUT_CH5_TINYCONN_MUX", SND_SOC_NOPM, 0, 0,
|
|
&etdm_out_ch5_tinyconn_mux_control,
|
|
etdm_out_tinyconn_event,
|
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
|
|
SND_SOC_DAPM_MUX_E("ETDM_OUT_CH6_TINYCONN_MUX", SND_SOC_NOPM, 0, 0,
|
|
&etdm_out_ch6_tinyconn_mux_control,
|
|
etdm_out_tinyconn_event,
|
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
|
|
SND_SOC_DAPM_MUX_E("ETDM_OUT_CH7_TINYCONN_MUX", SND_SOC_NOPM, 0, 0,
|
|
&etdm_out_ch7_tinyconn_mux_control,
|
|
etdm_out_tinyconn_event,
|
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
|
|
SND_SOC_DAPM_MUX_E("ETDM_OUT_CH8_TINYCONN_MUX", SND_SOC_NOPM, 0, 0,
|
|
&etdm_out_ch8_tinyconn_mux_control,
|
|
etdm_out_tinyconn_event,
|
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
|
|
|
|
/* etdm mclk en */
|
|
SND_SOC_DAPM_SUPPLY_S(ETDMIN_MCLK_EN_W_NAME, SUPPLY_SEQ_ETDM_MCLK_EN,
|
|
SND_SOC_NOPM, 0, 0,
|
|
mtk_mclk_en_event,
|
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
|
SND_SOC_DAPM_SUPPLY_S(ETDMOUT_MCLK_EN_W_NAME, SUPPLY_SEQ_ETDM_MCLK_EN,
|
|
SND_SOC_NOPM, 0, 0,
|
|
mtk_mclk_en_event,
|
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
|
|
|
/* etdm en*/
|
|
SND_SOC_DAPM_SUPPLY_S("ETDM_EN", SUPPLY_SEQ_ETDM_EN,
|
|
ETDM_OUT1_CON0, REG_ETDM_OUT_EN_SFT, 0,
|
|
mtk_etdm_en_event,
|
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
|
|
|
/* endpoint */
|
|
SND_SOC_DAPM_INPUT("ETDM_INPUT"),
|
|
SND_SOC_DAPM_OUTPUT("ETDM_OUTPUT"),
|
|
};
|
|
|
|
static const struct snd_soc_dapm_route mtk_dai_etdm_routes[] = {
|
|
{"ETDM_OUT_CH1", "DL11_CH1", "DL11"},
|
|
{"ETDM_OUT_CH2", "DL11_CH2", "DL11"},
|
|
{"ETDM_OUT_CH3", "DL11_CH3", "DL11"},
|
|
{"ETDM_OUT_CH4", "DL11_CH4", "DL11"},
|
|
{"ETDM_OUT_CH5", "DL11_CH5", "DL11"},
|
|
{"ETDM_OUT_CH6", "DL11_CH6", "DL11"},
|
|
{"ETDM_OUT_CH7", "DL11_CH7", "DL11"},
|
|
{"ETDM_OUT_CH8", "DL11_CH8", "DL11"},
|
|
|
|
{"ETDM_OUT_CH7", "DL11_CH1", "DL11"},
|
|
{"ETDM_OUT_CH8", "DL11_CH2", "DL11"},
|
|
|
|
{"ETDM Playback", NULL, "ETDM_OUT_CH1"},
|
|
{"ETDM Playback", NULL, "ETDM_OUT_CH2"},
|
|
{"ETDM Playback", NULL, "ETDM_OUT_CH3"},
|
|
{"ETDM Playback", NULL, "ETDM_OUT_CH4"},
|
|
{"ETDM Playback", NULL, "ETDM_OUT_CH5"},
|
|
{"ETDM Playback", NULL, "ETDM_OUT_CH6"},
|
|
{"ETDM Playback", NULL, "ETDM_OUT_CH7"},
|
|
{"ETDM Playback", NULL, "ETDM_OUT_CH8"},
|
|
|
|
{"ETDM_OUTPUT", NULL, "ETDM Playback"},
|
|
{"ETDM Capture", NULL, "ETDM_INPUT"},
|
|
|
|
{"ETDM Playback", NULL, "aud_tdm_clk"},
|
|
{"ETDM Playback", NULL, "ETDM_EN"},
|
|
{"ETDM Playback", NULL, APLL1_W_NAME, mtk_afe_etdm_apll_connect},
|
|
{"ETDM Playback", NULL, APLL2_W_NAME, mtk_afe_etdm_apll_connect},
|
|
|
|
{"ETDM Capture", NULL, "aud_tdm_clk"},
|
|
{"ETDM Capture", NULL, "ETDM_EN"},
|
|
{"ETDM Capture", NULL, APLL1_W_NAME, mtk_afe_etdm_apll_connect},
|
|
{"ETDM Capture", NULL, APLL2_W_NAME, mtk_afe_etdm_apll_connect},
|
|
|
|
{"ETDM_OUT_CH1_TINYCONN_MUX", "DL11_CH1", "DL11"},
|
|
{"ETDM_OUT_CH2_TINYCONN_MUX", "DL11_CH2", "DL11"},
|
|
{"ETDM_OUT_CH3_TINYCONN_MUX", "DL11_CH3", "DL11"},
|
|
{"ETDM_OUT_CH4_TINYCONN_MUX", "DL11_CH4", "DL11"},
|
|
{"ETDM_OUT_CH5_TINYCONN_MUX", "DL11_CH5", "DL11"},
|
|
{"ETDM_OUT_CH6_TINYCONN_MUX", "DL11_CH6", "DL11"},
|
|
{"ETDM_OUT_CH7_TINYCONN_MUX", "DL11_CH7", "DL11"},
|
|
{"ETDM_OUT_CH8_TINYCONN_MUX", "DL11_CH8", "DL11"},
|
|
|
|
{"ETDM Playback", NULL, "ETDM_OUT_CH1_TINYCONN_MUX"},
|
|
{"ETDM Playback", NULL, "ETDM_OUT_CH2_TINYCONN_MUX"},
|
|
{"ETDM Playback", NULL, "ETDM_OUT_CH3_TINYCONN_MUX"},
|
|
{"ETDM Playback", NULL, "ETDM_OUT_CH4_TINYCONN_MUX"},
|
|
{"ETDM Playback", NULL, "ETDM_OUT_CH5_TINYCONN_MUX"},
|
|
{"ETDM Playback", NULL, "ETDM_OUT_CH6_TINYCONN_MUX"},
|
|
{"ETDM Playback", NULL, "ETDM_OUT_CH7_TINYCONN_MUX"},
|
|
{"ETDM Playback", NULL, "ETDM_OUT_CH8_TINYCONN_MUX"},
|
|
};
|
|
|
|
/* dai ops */
|
|
static int mtk_dai_etdm_hw_params(struct snd_pcm_substream *substream,
|
|
struct snd_pcm_hw_params *params,
|
|
struct snd_soc_dai *dai)
|
|
{
|
|
struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
|
|
struct mt6985_afe_private *afe_priv = afe->platform_priv;
|
|
int etdm_id = dai->id;
|
|
struct mtk_afe_etdm_priv *etdm_priv = afe_priv->dai_priv[etdm_id];
|
|
unsigned int rate = params_rate(params);
|
|
unsigned int channels = params_channels(params);
|
|
snd_pcm_format_t format = params_format(params);
|
|
|
|
dev_info(afe->dev, "%s(), %s(%d), stream %d, rate %d channels %d format %d\n",
|
|
__func__, dai->name, etdm_id, substream->stream, rate, channels, format);
|
|
|
|
etdm_priv->etdm_rate = rate;
|
|
|
|
/* ETDM_IN Supports even channel only */
|
|
if ((channels % 2) != 0)
|
|
dev_info(afe->dev, "%s(), channels(%d) not even\n", __func__, channels);
|
|
|
|
if (etdm_id == MT6985_DAI_ETDMIN) {
|
|
/* ---etdm in --- */
|
|
regmap_update_bits(afe->regmap, ETDM_IN1_CON1,
|
|
REG_INITIAL_COUNT_MASK_SFT,
|
|
0x5 << REG_INITIAL_COUNT_SFT);
|
|
/* 3: pad top 5: no pad top */
|
|
regmap_update_bits(afe->regmap, ETDM_IN1_CON1,
|
|
REG_INITIAL_POINT_MASK_SFT,
|
|
0x3 << REG_INITIAL_POINT_SFT);
|
|
regmap_update_bits(afe->regmap, ETDM_IN1_CON1,
|
|
REG_LRCK_RESET_MASK_SFT,
|
|
0x1 << REG_LRCK_RESET_SFT);
|
|
regmap_update_bits(afe->regmap, ETDM_IN1_CON2,
|
|
REG_CLOCK_SOURCE_SEL_MASK_SFT,
|
|
ETDM_CLK_SOURCE_APLL << REG_CLOCK_SOURCE_SEL_SFT);
|
|
/* 0: manual 1: auto */
|
|
regmap_update_bits(afe->regmap, ETDM_IN1_CON2,
|
|
REG_CK_EN_SEL_AUTO_MASK_SFT,
|
|
0x1 << REG_CK_EN_SEL_AUTO_SFT);
|
|
regmap_update_bits(afe->regmap, ETDM_IN1_CON3,
|
|
REG_FS_TIMING_SEL_MASK_SFT,
|
|
get_etdm_rate(rate) << REG_FS_TIMING_SEL_SFT);
|
|
regmap_update_bits(afe->regmap, ETDM_IN1_CON4,
|
|
REG_RELATCH_1X_EN_SEL_MASK_SFT,
|
|
get_etdm_inconn_rate(rate) << REG_RELATCH_1X_EN_SEL_SFT);
|
|
|
|
regmap_update_bits(afe->regmap, ETDM_IN1_CON8,
|
|
REG_ETDM_USE_AFIFO_MASK_SFT,
|
|
0x0 << REG_ETDM_USE_AFIFO_SFT);
|
|
regmap_update_bits(afe->regmap, ETDM_IN1_CON8,
|
|
REG_AFIFO_MODE_MASK_SFT,
|
|
0x0 << REG_AFIFO_MODE_SFT);
|
|
regmap_update_bits(afe->regmap, ETDM_IN1_CON9,
|
|
REG_ALMOST_END_CH_COUNT_MASK_SFT,
|
|
0x0 << REG_ALMOST_END_CH_COUNT_SFT);
|
|
regmap_update_bits(afe->regmap, ETDM_IN1_CON9,
|
|
REG_ALMOST_END_BIT_COUNT_MASK_SFT,
|
|
0x0 << REG_ALMOST_END_BIT_COUNT_SFT);
|
|
regmap_update_bits(afe->regmap, ETDM_IN1_CON9,
|
|
REG_OUT2_LATCH_TIME_MASK_SFT,
|
|
0x6 << REG_OUT2_LATCH_TIME_SFT);
|
|
|
|
/* 5: TDM Mode */
|
|
regmap_update_bits(afe->regmap, ETDM_IN1_CON0,
|
|
REG_FMT_MASK_SFT, 0x5 << REG_FMT_SFT);
|
|
|
|
/* APLL */
|
|
regmap_update_bits(afe->regmap, ETDM_IN1_CON0,
|
|
REG_RELATCH_1X_EN_SEL_DOMAIN_MASK_SFT,
|
|
ETDM_RELATCH_SEL_APLL
|
|
<< REG_RELATCH_1X_EN_SEL_DOMAIN_SFT);
|
|
regmap_update_bits(afe->regmap, ETDM_IN1_CON0,
|
|
REG_BIT_LENGTH_MASK_SFT,
|
|
get_etdm_lrck_width(format) << REG_BIT_LENGTH_SFT);
|
|
regmap_update_bits(afe->regmap, ETDM_IN1_CON0,
|
|
REG_WORD_LENGTH_MASK_SFT,
|
|
get_etdm_wlen(format) << REG_WORD_LENGTH_SFT);
|
|
} else {
|
|
/* ---etdm out --- */
|
|
regmap_update_bits(afe->regmap, ETDM_OUT1_CON1,
|
|
REG_INITIAL_COUNT_MASK_SFT,
|
|
0x5 << REG_INITIAL_COUNT_SFT);
|
|
regmap_update_bits(afe->regmap, ETDM_OUT1_CON1,
|
|
REG_INITIAL_POINT_MASK_SFT,
|
|
0x6 << REG_INITIAL_POINT_SFT);
|
|
regmap_update_bits(afe->regmap, ETDM_OUT1_CON1,
|
|
REG_LRCK_RESET_MASK_SFT,
|
|
0x1 << REG_LRCK_RESET_SFT);
|
|
regmap_update_bits(afe->regmap, ETDM_OUT1_CON4,
|
|
OUT_REG_FS_TIMING_SEL_MASK_SFT,
|
|
get_etdm_rate(rate) << OUT_REG_FS_TIMING_SEL_SFT);
|
|
regmap_update_bits(afe->regmap, ETDM_OUT1_CON4,
|
|
OUT_REG_CLOCK_SOURCE_SEL_MASK_SFT,
|
|
ETDM_CLK_SOURCE_APLL << OUT_REG_CLOCK_SOURCE_SEL_SFT);
|
|
regmap_update_bits(afe->regmap, ETDM_OUT1_CON4,
|
|
INTERCONN_OUT_EN_SEL_MASK_SFT,
|
|
get_etdm_inconn_rate(rate) << INTERCONN_OUT_EN_SEL_SFT);
|
|
/* 5: TDM Mode */
|
|
regmap_update_bits(afe->regmap, ETDM_OUT1_CON0,
|
|
REG_FMT_MASK_SFT, 0x5 << REG_FMT_SFT);
|
|
|
|
/* APLL */
|
|
regmap_update_bits(afe->regmap, ETDM_OUT1_CON0,
|
|
REG_RELATCH_1X_EN_SEL_DOMAIN_MASK_SFT,
|
|
ETDM_RELATCH_SEL_APLL
|
|
<< REG_RELATCH_1X_EN_SEL_DOMAIN_SFT);
|
|
regmap_update_bits(afe->regmap, ETDM_OUT1_CON0,
|
|
REG_BIT_LENGTH_MASK_SFT,
|
|
get_etdm_lrck_width(format) << REG_BIT_LENGTH_SFT);
|
|
regmap_update_bits(afe->regmap, ETDM_OUT1_CON0,
|
|
REG_WORD_LENGTH_MASK_SFT,
|
|
get_etdm_wlen(format) << REG_WORD_LENGTH_SFT);
|
|
}
|
|
|
|
if (etdm_id == MT6985_DAI_ETDMIN) {
|
|
/* ---etdm cowork --- */
|
|
regmap_update_bits(afe->regmap, ETDM_0_3_COWORK_CON0,
|
|
ETDM_IN0_SLAVE_SEL_MASK_SFT,
|
|
ETDM_SLAVE_SEL_ETDMOUT0_MASTER
|
|
<< ETDM_IN0_SLAVE_SEL_SFT);
|
|
regmap_update_bits(afe->regmap, ETDM_0_3_COWORK_CON1,
|
|
ETDM_IN1_SLAVE_SEL_MASK_SFT,
|
|
ETDM_SLAVE_SEL_ETDMOUT1_MASTER
|
|
<< ETDM_IN1_SLAVE_SEL_SFT);
|
|
} else {
|
|
regmap_update_bits(afe->regmap, ETDM_0_3_COWORK_CON0,
|
|
ETDM_OUT0_SLAVE_SEL_MASK_SFT,
|
|
ETDM_SLAVE_SEL_ETDMIN0_MASTER
|
|
<< ETDM_OUT0_SLAVE_SEL_SFT);
|
|
regmap_update_bits(afe->regmap, ETDM_0_3_COWORK_CON0,
|
|
ETDM_OUT1_SLAVE_SEL_MASK_SFT,
|
|
ETDM_SLAVE_SEL_ETDMIN1_MASTER
|
|
<< ETDM_OUT1_SLAVE_SEL_SFT);
|
|
}
|
|
if (etdm_id == MT6985_DAI_ETDMIN) {
|
|
/* INTERCONN mux */
|
|
regmap_update_bits(afe->regmap, ETDM_0_3_COWORK_CON1,
|
|
ETDM_IN0_INTERCONN_MUX_SEL_MASK_SFT,
|
|
0x0 << ETDM_IN0_INTERCONN_MUX_SEL_SFT);//24
|
|
} else {
|
|
regmap_update_bits(afe->regmap, ETDM_0_3_COWORK_CON1,
|
|
ETDM_OUT0_INTERCONN_MUX_SEL_MASK_SFT,
|
|
0x1 << ETDM_OUT0_INTERCONN_MUX_SEL_SFT);//25
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int mtk_dai_etdm_trigger(struct snd_pcm_substream *substream,
|
|
int cmd, struct snd_soc_dai *dai)
|
|
{
|
|
struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
|
|
|
|
dev_info(afe->dev, "%s(), %s(%d): cmd %d\n", __func__, dai->name, dai->id, cmd);
|
|
|
|
switch (cmd) {
|
|
case SNDRV_PCM_TRIGGER_START:
|
|
case SNDRV_PCM_TRIGGER_RESUME:
|
|
/* enable etdm in/out */
|
|
if (dai->id == MT6985_DAI_ETDMIN) {
|
|
mt6985_afe_gpio_request(afe, true, MT6985_DAI_ETDMIN, 0);
|
|
mt6985_afe_gpio_request(afe, true, MT6985_DAI_ETDMOUT, 0);
|
|
regmap_update_bits(afe->regmap, ETDM_IN1_CON0,
|
|
REG_ETDM_IN_EN_MASK_SFT,
|
|
0x1 << REG_ETDM_IN_EN_SFT);
|
|
} else {
|
|
mt6985_afe_gpio_request(afe, true, MT6985_DAI_ETDMOUT, 0);
|
|
regmap_update_bits(afe->regmap, ETDM_OUT1_CON0,
|
|
REG_ETDM_OUT_EN_MASK_SFT,
|
|
0x1 << REG_ETDM_OUT_EN_SFT);
|
|
}
|
|
break;
|
|
case SNDRV_PCM_TRIGGER_STOP:
|
|
case SNDRV_PCM_TRIGGER_SUSPEND:
|
|
/* disable etdm in/out */
|
|
if (dai->id == MT6985_DAI_ETDMIN) {
|
|
regmap_update_bits(afe->regmap, ETDM_IN1_CON0,
|
|
REG_ETDM_IN_EN_MASK_SFT,
|
|
0x0 << REG_ETDM_IN_EN_SFT);
|
|
mt6985_afe_gpio_request(afe, false, MT6985_DAI_ETDMIN, 0);
|
|
mt6985_afe_gpio_request(afe, false, MT6985_DAI_ETDMOUT, 0);
|
|
} else {
|
|
regmap_update_bits(afe->regmap, ETDM_OUT1_CON0,
|
|
REG_ETDM_OUT_EN_MASK_SFT,
|
|
0x0 << REG_ETDM_OUT_EN_SFT);
|
|
mt6985_afe_gpio_request(afe, false, MT6985_DAI_ETDMOUT, 0);
|
|
}
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static const struct snd_soc_dai_ops mtk_dai_etdm_ops = {
|
|
.hw_params = mtk_dai_etdm_hw_params,
|
|
.trigger = mtk_dai_etdm_trigger,
|
|
};
|
|
|
|
/* dai driver */
|
|
#define MTK_ETDM_RATES (SNDRV_PCM_RATE_8000_384000)
|
|
#define MTK_ETDM_FORMATS (SNDRV_PCM_FMTBIT_S8 |\
|
|
SNDRV_PCM_FMTBIT_S16_LE |\
|
|
SNDRV_PCM_FMTBIT_S24_LE |\
|
|
SNDRV_PCM_FMTBIT_S32_LE)
|
|
static struct snd_soc_dai_driver mtk_dai_etdm_driver[] = {
|
|
{
|
|
.name = "ETDMIN",
|
|
.id = MT6985_DAI_ETDMIN,
|
|
.capture = {
|
|
.stream_name = "ETDM Capture",
|
|
.channels_min = 2,
|
|
.channels_max = 8,
|
|
.rates = MTK_ETDM_RATES,
|
|
.formats = MTK_ETDM_FORMATS,
|
|
},
|
|
.ops = &mtk_dai_etdm_ops,
|
|
.symmetric_rate = 1,
|
|
.symmetric_sample_bits = 1,
|
|
},
|
|
{
|
|
.name = "ETDMOUT",
|
|
.id = MT6985_DAI_ETDMOUT,
|
|
.playback = {
|
|
.stream_name = "ETDM Playback",
|
|
.channels_min = 1,
|
|
.channels_max = 8,
|
|
.rates = MTK_ETDM_RATES,
|
|
.formats = MTK_ETDM_FORMATS,
|
|
},
|
|
.ops = &mtk_dai_etdm_ops,
|
|
.symmetric_rate = 1,
|
|
.symmetric_sample_bits = 1,
|
|
},
|
|
};
|
|
|
|
static int etdm_parse_dt(struct mtk_base_afe *afe)
|
|
{
|
|
int ret;
|
|
unsigned int ch_num_in;
|
|
unsigned int ch_num_out;
|
|
unsigned int sync_in;
|
|
unsigned int sync_out;
|
|
|
|
/* get etdm ch */
|
|
ret = of_property_read_u32(afe->dev->of_node, "etdm-out-ch", &ch_num_out);
|
|
if (ret) {
|
|
dev_info(afe->dev, "%s() failed to read etdm-out-ch\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
regmap_update_bits(afe->regmap, ETDM_OUT1_CON0,
|
|
REG_CH_NUM_MASK_SFT, (ch_num_out - 1) << REG_CH_NUM_SFT);
|
|
dev_info(afe->dev, "%s() etdm-out-ch: %d\n", __func__, ch_num_out);
|
|
|
|
ret = of_property_read_u32(afe->dev->of_node, "etdm-in-ch", &ch_num_in);
|
|
if (ret) {
|
|
dev_info(afe->dev, "%s() failed to read etdm-in-ch\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
regmap_update_bits(afe->regmap, ETDM_IN1_CON0,
|
|
REG_CH_NUM_MASK_SFT, (ch_num_in - 1) << REG_CH_NUM_SFT);
|
|
dev_info(afe->dev, "%s() etdm-in-ch: %d\n", __func__, ch_num_in);
|
|
|
|
/* get etdm sync */
|
|
ret = of_property_read_u32(afe->dev->of_node, "etdm-out-sync", &sync_out);
|
|
if (ret) {
|
|
dev_info(afe->dev, "%s() failed to read etdm-out-sync\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
regmap_update_bits(afe->regmap, ETDM_OUT1_CON0,
|
|
REG_SYNC_MODE_MASK_SFT, sync_out << REG_SYNC_MODE_SFT);
|
|
dev_info(afe->dev, "%s() etdm-out-sync: %d\n", __func__, sync_out);
|
|
|
|
ret = of_property_read_u32(afe->dev->of_node, "etdm-in-sync", &sync_in);
|
|
if (ret) {
|
|
dev_info(afe->dev, "%s() failed to read etdm-in-sync\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
regmap_update_bits(afe->regmap, ETDM_IN1_CON0,
|
|
REG_SYNC_MODE_MASK_SFT, sync_in << REG_SYNC_MODE_SFT);
|
|
dev_info(afe->dev, "%s() etdm-in-sync: %d\n", __func__, sync_in);
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
int init_etdm_priv_data(struct mtk_base_afe *afe)
|
|
{
|
|
int i;
|
|
int ret;
|
|
|
|
for (i = 0; i < DAI_ETDM_NUM; i++) {
|
|
ret = mt6985_dai_set_priv(afe, mt6985_etdm_priv[i].id,
|
|
sizeof(struct mtk_afe_etdm_priv),
|
|
&mt6985_etdm_priv[i]);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mt6985_dai_etdm_register(struct mtk_base_afe *afe)
|
|
{
|
|
struct mtk_base_afe_dai *dai;
|
|
int ret = 0;
|
|
|
|
dev_info(afe->dev, "%s() start success\n", __func__);
|
|
|
|
dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
|
|
if (!dai)
|
|
return -ENOMEM;
|
|
|
|
list_add(&dai->list, &afe->sub_dais);
|
|
|
|
dai->dai_drivers = mtk_dai_etdm_driver;
|
|
dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_etdm_driver);
|
|
|
|
dai->dapm_widgets = mtk_dai_etdm_widgets;
|
|
dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_etdm_widgets);
|
|
dai->dapm_routes = mtk_dai_etdm_routes;
|
|
dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_etdm_routes);
|
|
dai->controls = mtk_dai_etdm_controls;
|
|
dai->num_controls = ARRAY_SIZE(mtk_dai_etdm_controls);
|
|
|
|
ret = init_etdm_priv_data(afe);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* 0: One IP multi-channel 1: Multi-IP 2-channel */
|
|
regmap_update_bits(afe->regmap, ETDM_IN1_CON2,
|
|
REG_MULTI_IP_MODE_MASK_SFT,
|
|
0x1 << REG_MULTI_IP_MODE_SFT);
|
|
|
|
ret = etdm_parse_dt(afe);
|
|
if (ret) {
|
|
dev_info(afe->dev, "%s() fail to parse dts: %d\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|