mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-12 05:48:40 +00:00
ASoC: mediatek: mt8183: add platform driver
add mt8183 audio platform and affiliated drivers. Signed-off-by: Shunli Wang <shunli.wang@mediatek.com> Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
parent
4ffdca62e2
commit
a94aec035a
14 changed files with 6333 additions and 0 deletions
|
@ -105,3 +105,13 @@ config SND_SOC_MT8173_RT5650_RT5676
|
|||
with the RT5650 and RT5676 codecs.
|
||||
Select Y if you have such device.
|
||||
If unsure select "N".
|
||||
|
||||
config SND_SOC_MT8183
|
||||
tristate "ASoC support for Mediatek MT8183 chip"
|
||||
depends on ARCH_MEDIATEK
|
||||
select SND_SOC_MEDIATEK
|
||||
help
|
||||
This adds ASoC platform driver support for Mediatek MT8183 chip
|
||||
that can be used with other codecs.
|
||||
Select Y if you have such device.
|
||||
If unsure select "N".
|
||||
|
|
|
@ -3,3 +3,4 @@ obj-$(CONFIG_SND_SOC_MEDIATEK) += common/
|
|||
obj-$(CONFIG_SND_SOC_MT2701) += mt2701/
|
||||
obj-$(CONFIG_SND_SOC_MT6797) += mt6797/
|
||||
obj-$(CONFIG_SND_SOC_MT8173) += mt8173/
|
||||
obj-$(CONFIG_SND_SOC_MT8183) += mt8183/
|
||||
|
|
13
sound/soc/mediatek/mt8183/Makefile
Normal file
13
sound/soc/mediatek/mt8183/Makefile
Normal file
|
@ -0,0 +1,13 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
# platform driver
|
||||
snd-soc-mt8183-afe-objs := \
|
||||
mt8183-afe-pcm.o \
|
||||
mt8183-afe-clk.o \
|
||||
mt8183-dai-i2s.o \
|
||||
mt8183-dai-tdm.o \
|
||||
mt8183-dai-pcm.o \
|
||||
mt8183-dai-hostless.o \
|
||||
mt8183-dai-adda.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC_MT8183) += snd-soc-mt8183-afe.o
|
611
sound/soc/mediatek/mt8183/mt8183-afe-clk.c
Normal file
611
sound/soc/mediatek/mt8183/mt8183-afe-clk.c
Normal file
|
@ -0,0 +1,611 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// mt8183-afe-clk.c -- Mediatek 8183 afe clock ctrl
|
||||
//
|
||||
// Copyright (c) 2018 MediaTek Inc.
|
||||
// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
|
||||
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include "mt8183-afe-common.h"
|
||||
#include "mt8183-afe-clk.h"
|
||||
#include "mt8183-reg.h"
|
||||
|
||||
enum {
|
||||
CLK_AFE = 0,
|
||||
CLK_TML,
|
||||
CLK_APLL22M,
|
||||
CLK_APLL24M,
|
||||
CLK_APLL1_TUNER,
|
||||
CLK_APLL2_TUNER,
|
||||
CLK_I2S1_BCLK_SW,
|
||||
CLK_I2S2_BCLK_SW,
|
||||
CLK_I2S3_BCLK_SW,
|
||||
CLK_I2S4_BCLK_SW,
|
||||
CLK_INFRA_SYS_AUDIO,
|
||||
CLK_MUX_AUDIO,
|
||||
CLK_MUX_AUDIOINTBUS,
|
||||
CLK_TOP_SYSPLL_D2_D4,
|
||||
/* apll related mux */
|
||||
CLK_TOP_MUX_AUD_1,
|
||||
CLK_TOP_APLL1_CK,
|
||||
CLK_TOP_MUX_AUD_2,
|
||||
CLK_TOP_APLL2_CK,
|
||||
CLK_TOP_MUX_AUD_ENG1,
|
||||
CLK_TOP_APLL1_D8,
|
||||
CLK_TOP_MUX_AUD_ENG2,
|
||||
CLK_TOP_APLL2_D8,
|
||||
CLK_TOP_I2S0_M_SEL,
|
||||
CLK_TOP_I2S1_M_SEL,
|
||||
CLK_TOP_I2S2_M_SEL,
|
||||
CLK_TOP_I2S3_M_SEL,
|
||||
CLK_TOP_I2S4_M_SEL,
|
||||
CLK_TOP_I2S5_M_SEL,
|
||||
CLK_TOP_APLL12_DIV0,
|
||||
CLK_TOP_APLL12_DIV1,
|
||||
CLK_TOP_APLL12_DIV2,
|
||||
CLK_TOP_APLL12_DIV3,
|
||||
CLK_TOP_APLL12_DIV4,
|
||||
CLK_TOP_APLL12_DIVB,
|
||||
CLK_CLK26M,
|
||||
CLK_NUM
|
||||
};
|
||||
|
||||
static const char *aud_clks[CLK_NUM] = {
|
||||
[CLK_AFE] = "aud_afe_clk",
|
||||
[CLK_TML] = "aud_tml_clk",
|
||||
[CLK_APLL22M] = "aud_apll22m_clk",
|
||||
[CLK_APLL24M] = "aud_apll24m_clk",
|
||||
[CLK_APLL1_TUNER] = "aud_apll1_tuner_clk",
|
||||
[CLK_APLL2_TUNER] = "aud_apll2_tuner_clk",
|
||||
[CLK_I2S1_BCLK_SW] = "aud_i2s1_bclk_sw",
|
||||
[CLK_I2S2_BCLK_SW] = "aud_i2s2_bclk_sw",
|
||||
[CLK_I2S3_BCLK_SW] = "aud_i2s3_bclk_sw",
|
||||
[CLK_I2S4_BCLK_SW] = "aud_i2s4_bclk_sw",
|
||||
[CLK_INFRA_SYS_AUDIO] = "aud_infra_clk",
|
||||
[CLK_MUX_AUDIO] = "top_mux_audio",
|
||||
[CLK_MUX_AUDIOINTBUS] = "top_mux_aud_intbus",
|
||||
[CLK_TOP_SYSPLL_D2_D4] = "top_syspll_d2_d4",
|
||||
[CLK_TOP_MUX_AUD_1] = "top_mux_aud_1",
|
||||
[CLK_TOP_APLL1_CK] = "top_apll1_ck",
|
||||
[CLK_TOP_MUX_AUD_2] = "top_mux_aud_2",
|
||||
[CLK_TOP_APLL2_CK] = "top_apll2_ck",
|
||||
[CLK_TOP_MUX_AUD_ENG1] = "top_mux_aud_eng1",
|
||||
[CLK_TOP_APLL1_D8] = "top_apll1_d8",
|
||||
[CLK_TOP_MUX_AUD_ENG2] = "top_mux_aud_eng2",
|
||||
[CLK_TOP_APLL2_D8] = "top_apll2_d8",
|
||||
[CLK_TOP_I2S0_M_SEL] = "top_i2s0_m_sel",
|
||||
[CLK_TOP_I2S1_M_SEL] = "top_i2s1_m_sel",
|
||||
[CLK_TOP_I2S2_M_SEL] = "top_i2s2_m_sel",
|
||||
[CLK_TOP_I2S3_M_SEL] = "top_i2s3_m_sel",
|
||||
[CLK_TOP_I2S4_M_SEL] = "top_i2s4_m_sel",
|
||||
[CLK_TOP_I2S5_M_SEL] = "top_i2s5_m_sel",
|
||||
[CLK_TOP_APLL12_DIV0] = "top_apll12_div0",
|
||||
[CLK_TOP_APLL12_DIV1] = "top_apll12_div1",
|
||||
[CLK_TOP_APLL12_DIV2] = "top_apll12_div2",
|
||||
[CLK_TOP_APLL12_DIV3] = "top_apll12_div3",
|
||||
[CLK_TOP_APLL12_DIV4] = "top_apll12_div4",
|
||||
[CLK_TOP_APLL12_DIVB] = "top_apll12_divb",
|
||||
[CLK_CLK26M] = "top_clk26m_clk",
|
||||
};
|
||||
|
||||
int mt8183_init_clock(struct mtk_base_afe *afe)
|
||||
{
|
||||
struct mt8183_afe_private *afe_priv = afe->platform_priv;
|
||||
int i;
|
||||
|
||||
afe_priv->clk = devm_kcalloc(afe->dev, CLK_NUM, sizeof(*afe_priv->clk),
|
||||
GFP_KERNEL);
|
||||
if (!afe_priv->clk)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < CLK_NUM; i++) {
|
||||
afe_priv->clk[i] = devm_clk_get(afe->dev, aud_clks[i]);
|
||||
if (IS_ERR(afe_priv->clk[i])) {
|
||||
dev_err(afe->dev, "%s(), devm_clk_get %s fail, ret %ld\n",
|
||||
__func__, aud_clks[i],
|
||||
PTR_ERR(afe_priv->clk[i]));
|
||||
return PTR_ERR(afe_priv->clk[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mt8183_afe_enable_clock(struct mtk_base_afe *afe)
|
||||
{
|
||||
struct mt8183_afe_private *afe_priv = afe->platform_priv;
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(afe_priv->clk[CLK_INFRA_SYS_AUDIO]);
|
||||
if (ret) {
|
||||
dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n",
|
||||
__func__, aud_clks[CLK_INFRA_SYS_AUDIO], ret);
|
||||
goto CLK_INFRA_SYS_AUDIO_ERR;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(afe_priv->clk[CLK_MUX_AUDIO]);
|
||||
if (ret) {
|
||||
dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n",
|
||||
__func__, aud_clks[CLK_MUX_AUDIO], ret);
|
||||
goto CLK_MUX_AUDIO_ERR;
|
||||
}
|
||||
|
||||
ret = clk_set_parent(afe_priv->clk[CLK_MUX_AUDIO],
|
||||
afe_priv->clk[CLK_CLK26M]);
|
||||
if (ret) {
|
||||
dev_err(afe->dev, "%s(), clk_set_parent %s-%s fail %d\n",
|
||||
__func__, aud_clks[CLK_MUX_AUDIO],
|
||||
aud_clks[CLK_CLK26M], ret);
|
||||
goto CLK_MUX_AUDIO_ERR;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(afe_priv->clk[CLK_MUX_AUDIOINTBUS]);
|
||||
if (ret) {
|
||||
dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n",
|
||||
__func__, aud_clks[CLK_MUX_AUDIOINTBUS], ret);
|
||||
goto CLK_MUX_AUDIO_INTBUS_ERR;
|
||||
}
|
||||
|
||||
ret = clk_set_parent(afe_priv->clk[CLK_MUX_AUDIOINTBUS],
|
||||
afe_priv->clk[CLK_TOP_SYSPLL_D2_D4]);
|
||||
if (ret) {
|
||||
dev_err(afe->dev, "%s(), clk_set_parent %s-%s fail %d\n",
|
||||
__func__, aud_clks[CLK_MUX_AUDIOINTBUS],
|
||||
aud_clks[CLK_TOP_SYSPLL_D2_D4], ret);
|
||||
goto CLK_MUX_AUDIO_INTBUS_ERR;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(afe_priv->clk[CLK_AFE]);
|
||||
if (ret) {
|
||||
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
|
||||
__func__, aud_clks[CLK_AFE], ret);
|
||||
goto CLK_AFE_ERR;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(afe_priv->clk[CLK_I2S1_BCLK_SW]);
|
||||
if (ret) {
|
||||
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
|
||||
__func__, aud_clks[CLK_I2S1_BCLK_SW], ret);
|
||||
goto CLK_I2S1_BCLK_SW_ERR;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(afe_priv->clk[CLK_I2S2_BCLK_SW]);
|
||||
if (ret) {
|
||||
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
|
||||
__func__, aud_clks[CLK_I2S2_BCLK_SW], ret);
|
||||
goto CLK_I2S2_BCLK_SW_ERR;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(afe_priv->clk[CLK_I2S3_BCLK_SW]);
|
||||
if (ret) {
|
||||
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
|
||||
__func__, aud_clks[CLK_I2S3_BCLK_SW], ret);
|
||||
goto CLK_I2S3_BCLK_SW_ERR;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(afe_priv->clk[CLK_I2S4_BCLK_SW]);
|
||||
if (ret) {
|
||||
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
|
||||
__func__, aud_clks[CLK_I2S4_BCLK_SW], ret);
|
||||
goto CLK_I2S4_BCLK_SW_ERR;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
CLK_I2S4_BCLK_SW_ERR:
|
||||
clk_disable_unprepare(afe_priv->clk[CLK_I2S3_BCLK_SW]);
|
||||
CLK_I2S3_BCLK_SW_ERR:
|
||||
clk_disable_unprepare(afe_priv->clk[CLK_I2S2_BCLK_SW]);
|
||||
CLK_I2S2_BCLK_SW_ERR:
|
||||
clk_disable_unprepare(afe_priv->clk[CLK_I2S1_BCLK_SW]);
|
||||
CLK_I2S1_BCLK_SW_ERR:
|
||||
clk_disable_unprepare(afe_priv->clk[CLK_AFE]);
|
||||
CLK_AFE_ERR:
|
||||
clk_disable_unprepare(afe_priv->clk[CLK_MUX_AUDIOINTBUS]);
|
||||
CLK_MUX_AUDIO_INTBUS_ERR:
|
||||
clk_disable_unprepare(afe_priv->clk[CLK_MUX_AUDIO]);
|
||||
CLK_MUX_AUDIO_ERR:
|
||||
clk_disable_unprepare(afe_priv->clk[CLK_INFRA_SYS_AUDIO]);
|
||||
CLK_INFRA_SYS_AUDIO_ERR:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mt8183_afe_disable_clock(struct mtk_base_afe *afe)
|
||||
{
|
||||
struct mt8183_afe_private *afe_priv = afe->platform_priv;
|
||||
|
||||
clk_disable_unprepare(afe_priv->clk[CLK_I2S4_BCLK_SW]);
|
||||
clk_disable_unprepare(afe_priv->clk[CLK_I2S3_BCLK_SW]);
|
||||
clk_disable_unprepare(afe_priv->clk[CLK_I2S2_BCLK_SW]);
|
||||
clk_disable_unprepare(afe_priv->clk[CLK_I2S1_BCLK_SW]);
|
||||
clk_disable_unprepare(afe_priv->clk[CLK_AFE]);
|
||||
clk_disable_unprepare(afe_priv->clk[CLK_MUX_AUDIOINTBUS]);
|
||||
clk_disable_unprepare(afe_priv->clk[CLK_MUX_AUDIO]);
|
||||
clk_disable_unprepare(afe_priv->clk[CLK_INFRA_SYS_AUDIO]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* apll */
|
||||
static int apll1_mux_setting(struct mtk_base_afe *afe, bool enable)
|
||||
{
|
||||
struct mt8183_afe_private *afe_priv = afe->platform_priv;
|
||||
int ret;
|
||||
|
||||
if (enable) {
|
||||
ret = clk_prepare_enable(afe_priv->clk[CLK_TOP_MUX_AUD_1]);
|
||||
if (ret) {
|
||||
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
|
||||
__func__, aud_clks[CLK_TOP_MUX_AUD_1], ret);
|
||||
goto ERR_ENABLE_CLK_TOP_MUX_AUD_1;
|
||||
}
|
||||
ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_1],
|
||||
afe_priv->clk[CLK_TOP_APLL1_CK]);
|
||||
if (ret) {
|
||||
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
|
||||
__func__, aud_clks[CLK_TOP_MUX_AUD_1],
|
||||
aud_clks[CLK_TOP_APLL1_CK], ret);
|
||||
goto ERR_SELECT_CLK_TOP_MUX_AUD_1;
|
||||
}
|
||||
|
||||
/* 180.6336 / 8 = 22.5792MHz */
|
||||
ret = clk_prepare_enable(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1]);
|
||||
if (ret) {
|
||||
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
|
||||
__func__, aud_clks[CLK_TOP_MUX_AUD_ENG1], ret);
|
||||
goto ERR_ENABLE_CLK_TOP_MUX_AUD_ENG1;
|
||||
}
|
||||
ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1],
|
||||
afe_priv->clk[CLK_TOP_APLL1_D8]);
|
||||
if (ret) {
|
||||
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
|
||||
__func__, aud_clks[CLK_TOP_MUX_AUD_ENG1],
|
||||
aud_clks[CLK_TOP_APLL1_D8], ret);
|
||||
goto ERR_SELECT_CLK_TOP_MUX_AUD_ENG1;
|
||||
}
|
||||
} else {
|
||||
ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1],
|
||||
afe_priv->clk[CLK_CLK26M]);
|
||||
if (ret) {
|
||||
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
|
||||
__func__, aud_clks[CLK_TOP_MUX_AUD_ENG1],
|
||||
aud_clks[CLK_CLK26M], ret);
|
||||
goto EXIT;
|
||||
}
|
||||
clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1]);
|
||||
|
||||
ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_1],
|
||||
afe_priv->clk[CLK_CLK26M]);
|
||||
if (ret) {
|
||||
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
|
||||
__func__, aud_clks[CLK_TOP_MUX_AUD_1],
|
||||
aud_clks[CLK_CLK26M], ret);
|
||||
goto EXIT;
|
||||
}
|
||||
clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_1]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
ERR_SELECT_CLK_TOP_MUX_AUD_ENG1:
|
||||
clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1],
|
||||
afe_priv->clk[CLK_CLK26M]);
|
||||
clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1]);
|
||||
ERR_ENABLE_CLK_TOP_MUX_AUD_ENG1:
|
||||
ERR_SELECT_CLK_TOP_MUX_AUD_1:
|
||||
clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_1],
|
||||
afe_priv->clk[CLK_CLK26M]);
|
||||
clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_1]);
|
||||
ERR_ENABLE_CLK_TOP_MUX_AUD_1:
|
||||
EXIT:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int apll2_mux_setting(struct mtk_base_afe *afe, bool enable)
|
||||
{
|
||||
struct mt8183_afe_private *afe_priv = afe->platform_priv;
|
||||
int ret;
|
||||
|
||||
if (enable) {
|
||||
ret = clk_prepare_enable(afe_priv->clk[CLK_TOP_MUX_AUD_2]);
|
||||
if (ret) {
|
||||
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
|
||||
__func__, aud_clks[CLK_TOP_MUX_AUD_2], ret);
|
||||
goto ERR_ENABLE_CLK_TOP_MUX_AUD_2;
|
||||
}
|
||||
ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_2],
|
||||
afe_priv->clk[CLK_TOP_APLL2_CK]);
|
||||
if (ret) {
|
||||
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
|
||||
__func__, aud_clks[CLK_TOP_MUX_AUD_2],
|
||||
aud_clks[CLK_TOP_APLL2_CK], ret);
|
||||
goto ERR_SELECT_CLK_TOP_MUX_AUD_2;
|
||||
}
|
||||
|
||||
/* 196.608 / 8 = 24.576MHz */
|
||||
ret = clk_prepare_enable(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2]);
|
||||
if (ret) {
|
||||
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
|
||||
__func__, aud_clks[CLK_TOP_MUX_AUD_ENG2], ret);
|
||||
goto ERR_ENABLE_CLK_TOP_MUX_AUD_ENG2;
|
||||
}
|
||||
ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2],
|
||||
afe_priv->clk[CLK_TOP_APLL2_D8]);
|
||||
if (ret) {
|
||||
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
|
||||
__func__, aud_clks[CLK_TOP_MUX_AUD_ENG2],
|
||||
aud_clks[CLK_TOP_APLL2_D8], ret);
|
||||
goto ERR_SELECT_CLK_TOP_MUX_AUD_ENG2;
|
||||
}
|
||||
} else {
|
||||
ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2],
|
||||
afe_priv->clk[CLK_CLK26M]);
|
||||
if (ret) {
|
||||
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
|
||||
__func__, aud_clks[CLK_TOP_MUX_AUD_ENG2],
|
||||
aud_clks[CLK_CLK26M], ret);
|
||||
goto EXIT;
|
||||
}
|
||||
clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2]);
|
||||
|
||||
ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_2],
|
||||
afe_priv->clk[CLK_CLK26M]);
|
||||
if (ret) {
|
||||
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
|
||||
__func__, aud_clks[CLK_TOP_MUX_AUD_2],
|
||||
aud_clks[CLK_CLK26M], ret);
|
||||
goto EXIT;
|
||||
}
|
||||
clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_2]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
ERR_SELECT_CLK_TOP_MUX_AUD_ENG2:
|
||||
clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2],
|
||||
afe_priv->clk[CLK_CLK26M]);
|
||||
clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2]);
|
||||
ERR_ENABLE_CLK_TOP_MUX_AUD_ENG2:
|
||||
ERR_SELECT_CLK_TOP_MUX_AUD_2:
|
||||
clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_2],
|
||||
afe_priv->clk[CLK_CLK26M]);
|
||||
clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_2]);
|
||||
ERR_ENABLE_CLK_TOP_MUX_AUD_2:
|
||||
EXIT:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mt8183_apll1_enable(struct mtk_base_afe *afe)
|
||||
{
|
||||
struct mt8183_afe_private *afe_priv = afe->platform_priv;
|
||||
int ret;
|
||||
|
||||
/* setting for APLL */
|
||||
apll1_mux_setting(afe, true);
|
||||
|
||||
ret = clk_prepare_enable(afe_priv->clk[CLK_APLL22M]);
|
||||
if (ret) {
|
||||
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
|
||||
__func__, aud_clks[CLK_APLL22M], ret);
|
||||
goto ERR_CLK_APLL22M;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(afe_priv->clk[CLK_APLL1_TUNER]);
|
||||
if (ret) {
|
||||
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
|
||||
__func__, aud_clks[CLK_APLL1_TUNER], ret);
|
||||
goto ERR_CLK_APLL1_TUNER;
|
||||
}
|
||||
|
||||
regmap_update_bits(afe->regmap, AFE_APLL1_TUNER_CFG,
|
||||
0x0000FFF7, 0x00000832);
|
||||
regmap_update_bits(afe->regmap, AFE_APLL1_TUNER_CFG, 0x1, 0x1);
|
||||
|
||||
regmap_update_bits(afe->regmap, AFE_HD_ENGEN_ENABLE,
|
||||
AFE_22M_ON_MASK_SFT,
|
||||
0x1 << AFE_22M_ON_SFT);
|
||||
|
||||
return 0;
|
||||
|
||||
ERR_CLK_APLL1_TUNER:
|
||||
clk_disable_unprepare(afe_priv->clk[CLK_APLL22M]);
|
||||
ERR_CLK_APLL22M:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void mt8183_apll1_disable(struct mtk_base_afe *afe)
|
||||
{
|
||||
struct mt8183_afe_private *afe_priv = afe->platform_priv;
|
||||
|
||||
regmap_update_bits(afe->regmap, AFE_HD_ENGEN_ENABLE,
|
||||
AFE_22M_ON_MASK_SFT,
|
||||
0x0 << AFE_22M_ON_SFT);
|
||||
|
||||
regmap_update_bits(afe->regmap, AFE_APLL1_TUNER_CFG, 0x1, 0x0);
|
||||
|
||||
clk_disable_unprepare(afe_priv->clk[CLK_APLL1_TUNER]);
|
||||
clk_disable_unprepare(afe_priv->clk[CLK_APLL22M]);
|
||||
|
||||
apll1_mux_setting(afe, false);
|
||||
}
|
||||
|
||||
int mt8183_apll2_enable(struct mtk_base_afe *afe)
|
||||
{
|
||||
struct mt8183_afe_private *afe_priv = afe->platform_priv;
|
||||
int ret;
|
||||
|
||||
/* setting for APLL */
|
||||
apll2_mux_setting(afe, true);
|
||||
|
||||
ret = clk_prepare_enable(afe_priv->clk[CLK_APLL24M]);
|
||||
if (ret) {
|
||||
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
|
||||
__func__, aud_clks[CLK_APLL24M], ret);
|
||||
goto ERR_CLK_APLL24M;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(afe_priv->clk[CLK_APLL2_TUNER]);
|
||||
if (ret) {
|
||||
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
|
||||
__func__, aud_clks[CLK_APLL2_TUNER], ret);
|
||||
goto ERR_CLK_APLL2_TUNER;
|
||||
}
|
||||
|
||||
regmap_update_bits(afe->regmap, AFE_APLL2_TUNER_CFG,
|
||||
0x0000FFF7, 0x00000634);
|
||||
regmap_update_bits(afe->regmap, AFE_APLL2_TUNER_CFG, 0x1, 0x1);
|
||||
|
||||
regmap_update_bits(afe->regmap, AFE_HD_ENGEN_ENABLE,
|
||||
AFE_24M_ON_MASK_SFT,
|
||||
0x1 << AFE_24M_ON_SFT);
|
||||
|
||||
return 0;
|
||||
|
||||
ERR_CLK_APLL2_TUNER:
|
||||
clk_disable_unprepare(afe_priv->clk[CLK_APLL24M]);
|
||||
ERR_CLK_APLL24M:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void mt8183_apll2_disable(struct mtk_base_afe *afe)
|
||||
{
|
||||
struct mt8183_afe_private *afe_priv = afe->platform_priv;
|
||||
|
||||
regmap_update_bits(afe->regmap, AFE_HD_ENGEN_ENABLE,
|
||||
AFE_24M_ON_MASK_SFT,
|
||||
0x0 << AFE_24M_ON_SFT);
|
||||
|
||||
regmap_update_bits(afe->regmap, AFE_APLL2_TUNER_CFG, 0x1, 0x0);
|
||||
|
||||
clk_disable_unprepare(afe_priv->clk[CLK_APLL2_TUNER]);
|
||||
clk_disable_unprepare(afe_priv->clk[CLK_APLL24M]);
|
||||
|
||||
apll2_mux_setting(afe, false);
|
||||
}
|
||||
|
||||
int mt8183_get_apll_rate(struct mtk_base_afe *afe, int apll)
|
||||
{
|
||||
return (apll == MT8183_APLL1) ? 180633600 : 196608000;
|
||||
}
|
||||
|
||||
int mt8183_get_apll_by_rate(struct mtk_base_afe *afe, int rate)
|
||||
{
|
||||
return ((rate % 8000) == 0) ? MT8183_APLL2 : MT8183_APLL1;
|
||||
}
|
||||
|
||||
int mt8183_get_apll_by_name(struct mtk_base_afe *afe, const char *name)
|
||||
{
|
||||
if (strcmp(name, APLL1_W_NAME) == 0)
|
||||
return MT8183_APLL1;
|
||||
else
|
||||
return MT8183_APLL2;
|
||||
}
|
||||
|
||||
/* mck */
|
||||
struct mt8183_mck_div {
|
||||
int m_sel_id;
|
||||
int div_clk_id;
|
||||
};
|
||||
|
||||
static const struct mt8183_mck_div mck_div[MT8183_MCK_NUM] = {
|
||||
[MT8183_I2S0_MCK] = {
|
||||
.m_sel_id = CLK_TOP_I2S0_M_SEL,
|
||||
.div_clk_id = CLK_TOP_APLL12_DIV0,
|
||||
},
|
||||
[MT8183_I2S1_MCK] = {
|
||||
.m_sel_id = CLK_TOP_I2S1_M_SEL,
|
||||
.div_clk_id = CLK_TOP_APLL12_DIV1,
|
||||
},
|
||||
[MT8183_I2S2_MCK] = {
|
||||
.m_sel_id = CLK_TOP_I2S2_M_SEL,
|
||||
.div_clk_id = CLK_TOP_APLL12_DIV2,
|
||||
},
|
||||
[MT8183_I2S3_MCK] = {
|
||||
.m_sel_id = CLK_TOP_I2S3_M_SEL,
|
||||
.div_clk_id = CLK_TOP_APLL12_DIV3,
|
||||
},
|
||||
[MT8183_I2S4_MCK] = {
|
||||
.m_sel_id = CLK_TOP_I2S4_M_SEL,
|
||||
.div_clk_id = CLK_TOP_APLL12_DIV4,
|
||||
},
|
||||
[MT8183_I2S4_BCK] = {
|
||||
.m_sel_id = -1,
|
||||
.div_clk_id = CLK_TOP_APLL12_DIVB,
|
||||
},
|
||||
[MT8183_I2S5_MCK] = {
|
||||
.m_sel_id = -1,
|
||||
.div_clk_id = -1,
|
||||
},
|
||||
};
|
||||
|
||||
int mt8183_mck_enable(struct mtk_base_afe *afe, int mck_id, int rate)
|
||||
{
|
||||
struct mt8183_afe_private *afe_priv = afe->platform_priv;
|
||||
int apll = mt8183_get_apll_by_rate(afe, rate);
|
||||
int apll_clk_id = apll == MT8183_APLL1 ?
|
||||
CLK_TOP_MUX_AUD_1 : CLK_TOP_MUX_AUD_2;
|
||||
int m_sel_id = mck_div[mck_id].m_sel_id;
|
||||
int div_clk_id = mck_div[mck_id].div_clk_id;
|
||||
int ret;
|
||||
|
||||
/* i2s5 mck not support */
|
||||
if (mck_id == MT8183_I2S5_MCK)
|
||||
return 0;
|
||||
|
||||
/* select apll */
|
||||
if (m_sel_id >= 0) {
|
||||
ret = clk_prepare_enable(afe_priv->clk[m_sel_id]);
|
||||
if (ret) {
|
||||
dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n",
|
||||
__func__, aud_clks[m_sel_id], ret);
|
||||
goto ERR_ENABLE_MCLK;
|
||||
}
|
||||
ret = clk_set_parent(afe_priv->clk[m_sel_id],
|
||||
afe_priv->clk[apll_clk_id]);
|
||||
if (ret) {
|
||||
dev_err(afe->dev, "%s(), clk_set_parent %s-%s fail %d\n",
|
||||
__func__, aud_clks[m_sel_id],
|
||||
aud_clks[apll_clk_id], ret);
|
||||
goto ERR_SELECT_MCLK;
|
||||
}
|
||||
}
|
||||
|
||||
/* enable div, set rate */
|
||||
ret = clk_prepare_enable(afe_priv->clk[div_clk_id]);
|
||||
if (ret) {
|
||||
dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n",
|
||||
__func__, aud_clks[div_clk_id], ret);
|
||||
goto ERR_ENABLE_MCLK_DIV;
|
||||
}
|
||||
ret = clk_set_rate(afe_priv->clk[div_clk_id], rate);
|
||||
if (ret) {
|
||||
dev_err(afe->dev, "%s(), clk_set_rate %s, rate %d, fail %d\n",
|
||||
__func__, aud_clks[div_clk_id],
|
||||
rate, ret);
|
||||
goto ERR_SET_MCLK_RATE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
ERR_SET_MCLK_RATE:
|
||||
clk_disable_unprepare(afe_priv->clk[div_clk_id]);
|
||||
ERR_ENABLE_MCLK_DIV:
|
||||
ERR_SELECT_MCLK:
|
||||
if (m_sel_id >= 0)
|
||||
clk_disable_unprepare(afe_priv->clk[m_sel_id]);
|
||||
ERR_ENABLE_MCLK:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void mt8183_mck_disable(struct mtk_base_afe *afe, int mck_id)
|
||||
{
|
||||
struct mt8183_afe_private *afe_priv = afe->platform_priv;
|
||||
int m_sel_id = mck_div[mck_id].m_sel_id;
|
||||
int div_clk_id = mck_div[mck_id].div_clk_id;
|
||||
|
||||
clk_disable_unprepare(afe_priv->clk[div_clk_id]);
|
||||
if (m_sel_id >= 0)
|
||||
clk_disable_unprepare(afe_priv->clk[m_sel_id]);
|
||||
}
|
38
sound/soc/mediatek/mt8183/mt8183-afe-clk.h
Normal file
38
sound/soc/mediatek/mt8183/mt8183-afe-clk.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* mt8183-afe-clk.h -- Mediatek 8183 afe clock ctrl definition
|
||||
*
|
||||
* Copyright (c) 2018 MediaTek Inc.
|
||||
* Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
|
||||
*/
|
||||
|
||||
#ifndef _MT8183_AFE_CLK_H_
|
||||
#define _MT8183_AFE_CLK_H_
|
||||
|
||||
/* APLL */
|
||||
#define APLL1_W_NAME "APLL1"
|
||||
#define APLL2_W_NAME "APLL2"
|
||||
enum {
|
||||
MT8183_APLL1 = 0,
|
||||
MT8183_APLL2,
|
||||
};
|
||||
|
||||
struct mtk_base_afe;
|
||||
|
||||
int mt8183_init_clock(struct mtk_base_afe *afe);
|
||||
int mt8183_afe_enable_clock(struct mtk_base_afe *afe);
|
||||
int mt8183_afe_disable_clock(struct mtk_base_afe *afe);
|
||||
|
||||
int mt8183_apll1_enable(struct mtk_base_afe *afe);
|
||||
void mt8183_apll1_disable(struct mtk_base_afe *afe);
|
||||
|
||||
int mt8183_apll2_enable(struct mtk_base_afe *afe);
|
||||
void mt8183_apll2_disable(struct mtk_base_afe *afe);
|
||||
|
||||
int mt8183_get_apll_rate(struct mtk_base_afe *afe, int apll);
|
||||
int mt8183_get_apll_by_rate(struct mtk_base_afe *afe, int rate);
|
||||
int mt8183_get_apll_by_name(struct mtk_base_afe *afe, const char *name);
|
||||
|
||||
int mt8183_mck_enable(struct mtk_base_afe *afe, int mck_id, int rate);
|
||||
void mt8183_mck_disable(struct mtk_base_afe *afe, int mck_id);
|
||||
#endif
|
108
sound/soc/mediatek/mt8183/mt8183-afe-common.h
Normal file
108
sound/soc/mediatek/mt8183/mt8183-afe-common.h
Normal file
|
@ -0,0 +1,108 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* mt8183-afe-common.h -- Mediatek 8183 audio driver definitions
|
||||
*
|
||||
* Copyright (c) 2018 MediaTek Inc.
|
||||
* Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
|
||||
*/
|
||||
|
||||
#ifndef _MT_8183_AFE_COMMON_H_
|
||||
#define _MT_8183_AFE_COMMON_H_
|
||||
|
||||
#include <sound/soc.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/regmap.h>
|
||||
#include "../common/mtk-base-afe.h"
|
||||
|
||||
enum {
|
||||
MT8183_MEMIF_DL1,
|
||||
MT8183_MEMIF_DL2,
|
||||
MT8183_MEMIF_DL3,
|
||||
MT8183_MEMIF_VUL12,
|
||||
MT8183_MEMIF_VUL2,
|
||||
MT8183_MEMIF_AWB,
|
||||
MT8183_MEMIF_AWB2,
|
||||
MT8183_MEMIF_MOD_DAI,
|
||||
MT8183_MEMIF_HDMI,
|
||||
MT8183_MEMIF_NUM,
|
||||
MT8183_DAI_ADDA = MT8183_MEMIF_NUM,
|
||||
MT8183_DAI_PCM_1,
|
||||
MT8183_DAI_PCM_2,
|
||||
MT8183_DAI_I2S_0,
|
||||
MT8183_DAI_I2S_1,
|
||||
MT8183_DAI_I2S_2,
|
||||
MT8183_DAI_I2S_3,
|
||||
MT8183_DAI_I2S_5,
|
||||
MT8183_DAI_TDM,
|
||||
MT8183_DAI_HOSTLESS_LPBK,
|
||||
MT8183_DAI_HOSTLESS_SPEECH,
|
||||
MT8183_DAI_NUM,
|
||||
};
|
||||
|
||||
enum {
|
||||
MT8183_IRQ_0,
|
||||
MT8183_IRQ_1,
|
||||
MT8183_IRQ_2,
|
||||
MT8183_IRQ_3,
|
||||
MT8183_IRQ_4,
|
||||
MT8183_IRQ_5,
|
||||
MT8183_IRQ_6,
|
||||
MT8183_IRQ_7,
|
||||
MT8183_IRQ_8, /* hw bundle to TDM */
|
||||
MT8183_IRQ_11,
|
||||
MT8183_IRQ_12,
|
||||
MT8183_IRQ_NUM,
|
||||
};
|
||||
|
||||
enum {
|
||||
MT8183_MTKAIF_PROTOCOL_1 = 0,
|
||||
MT8183_MTKAIF_PROTOCOL_2,
|
||||
MT8183_MTKAIF_PROTOCOL_2_CLK_P2,
|
||||
};
|
||||
|
||||
/* MCLK */
|
||||
enum {
|
||||
MT8183_I2S0_MCK = 0,
|
||||
MT8183_I2S1_MCK,
|
||||
MT8183_I2S2_MCK,
|
||||
MT8183_I2S3_MCK,
|
||||
MT8183_I2S4_MCK,
|
||||
MT8183_I2S4_BCK,
|
||||
MT8183_I2S5_MCK,
|
||||
MT8183_MCK_NUM,
|
||||
};
|
||||
|
||||
struct clk;
|
||||
|
||||
struct mt8183_afe_private {
|
||||
struct clk **clk;
|
||||
|
||||
int pm_runtime_bypass_reg_ctl;
|
||||
|
||||
/* dai */
|
||||
void *dai_priv[MT8183_DAI_NUM];
|
||||
|
||||
/* adda */
|
||||
int mtkaif_protocol;
|
||||
int mtkaif_calibration_ok;
|
||||
int mtkaif_chosen_phase[4];
|
||||
int mtkaif_phase_cycle[4];
|
||||
int mtkaif_calibration_num_phase;
|
||||
int mtkaif_dmic;
|
||||
|
||||
/* mck */
|
||||
int mck_rate[MT8183_MCK_NUM];
|
||||
};
|
||||
|
||||
unsigned int mt8183_general_rate_transform(struct device *dev,
|
||||
unsigned int rate);
|
||||
unsigned int mt8183_rate_transform(struct device *dev,
|
||||
unsigned int rate, int aud_blk);
|
||||
|
||||
/* dai register */
|
||||
int mt8183_dai_adda_register(struct mtk_base_afe *afe);
|
||||
int mt8183_dai_pcm_register(struct mtk_base_afe *afe);
|
||||
int mt8183_dai_i2s_register(struct mtk_base_afe *afe);
|
||||
int mt8183_dai_tdm_register(struct mtk_base_afe *afe);
|
||||
int mt8183_dai_hostless_register(struct mtk_base_afe *afe);
|
||||
#endif
|
1237
sound/soc/mediatek/mt8183/mt8183-afe-pcm.c
Normal file
1237
sound/soc/mediatek/mt8183/mt8183-afe-pcm.c
Normal file
File diff suppressed because it is too large
Load diff
501
sound/soc/mediatek/mt8183/mt8183-dai-adda.c
Normal file
501
sound/soc/mediatek/mt8183/mt8183-dai-adda.c
Normal file
|
@ -0,0 +1,501 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// MediaTek ALSA SoC Audio DAI ADDA Control
|
||||
//
|
||||
// Copyright (c) 2018 MediaTek Inc.
|
||||
// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
|
||||
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/delay.h>
|
||||
#include "mt8183-afe-common.h"
|
||||
#include "mt8183-interconnection.h"
|
||||
#include "mt8183-reg.h"
|
||||
|
||||
enum {
|
||||
AUDIO_SDM_LEVEL_MUTE = 0,
|
||||
AUDIO_SDM_LEVEL_NORMAL = 0x1d,
|
||||
/* if you change level normal */
|
||||
/* you need to change formula of hp impedance and dc trim too */
|
||||
};
|
||||
|
||||
enum {
|
||||
DELAY_DATA_MISO1 = 0,
|
||||
DELAY_DATA_MISO2,
|
||||
};
|
||||
|
||||
enum {
|
||||
MTK_AFE_ADDA_DL_RATE_8K = 0,
|
||||
MTK_AFE_ADDA_DL_RATE_11K = 1,
|
||||
MTK_AFE_ADDA_DL_RATE_12K = 2,
|
||||
MTK_AFE_ADDA_DL_RATE_16K = 3,
|
||||
MTK_AFE_ADDA_DL_RATE_22K = 4,
|
||||
MTK_AFE_ADDA_DL_RATE_24K = 5,
|
||||
MTK_AFE_ADDA_DL_RATE_32K = 6,
|
||||
MTK_AFE_ADDA_DL_RATE_44K = 7,
|
||||
MTK_AFE_ADDA_DL_RATE_48K = 8,
|
||||
MTK_AFE_ADDA_DL_RATE_96K = 9,
|
||||
MTK_AFE_ADDA_DL_RATE_192K = 10,
|
||||
};
|
||||
|
||||
enum {
|
||||
MTK_AFE_ADDA_UL_RATE_8K = 0,
|
||||
MTK_AFE_ADDA_UL_RATE_16K = 1,
|
||||
MTK_AFE_ADDA_UL_RATE_32K = 2,
|
||||
MTK_AFE_ADDA_UL_RATE_48K = 3,
|
||||
MTK_AFE_ADDA_UL_RATE_96K = 4,
|
||||
MTK_AFE_ADDA_UL_RATE_192K = 5,
|
||||
MTK_AFE_ADDA_UL_RATE_48K_HD = 6,
|
||||
};
|
||||
|
||||
static unsigned int adda_dl_rate_transform(struct mtk_base_afe *afe,
|
||||
unsigned int rate)
|
||||
{
|
||||
switch (rate) {
|
||||
case 8000:
|
||||
return MTK_AFE_ADDA_DL_RATE_8K;
|
||||
case 11025:
|
||||
return MTK_AFE_ADDA_DL_RATE_11K;
|
||||
case 12000:
|
||||
return MTK_AFE_ADDA_DL_RATE_12K;
|
||||
case 16000:
|
||||
return MTK_AFE_ADDA_DL_RATE_16K;
|
||||
case 22050:
|
||||
return MTK_AFE_ADDA_DL_RATE_22K;
|
||||
case 24000:
|
||||
return MTK_AFE_ADDA_DL_RATE_24K;
|
||||
case 32000:
|
||||
return MTK_AFE_ADDA_DL_RATE_32K;
|
||||
case 44100:
|
||||
return MTK_AFE_ADDA_DL_RATE_44K;
|
||||
case 48000:
|
||||
return MTK_AFE_ADDA_DL_RATE_48K;
|
||||
case 96000:
|
||||
return MTK_AFE_ADDA_DL_RATE_96K;
|
||||
case 192000:
|
||||
return MTK_AFE_ADDA_DL_RATE_192K;
|
||||
default:
|
||||
dev_warn(afe->dev, "%s(), rate %d invalid, use 48kHz!!!\n",
|
||||
__func__, rate);
|
||||
return MTK_AFE_ADDA_DL_RATE_48K;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int adda_ul_rate_transform(struct mtk_base_afe *afe,
|
||||
unsigned int rate)
|
||||
{
|
||||
switch (rate) {
|
||||
case 8000:
|
||||
return MTK_AFE_ADDA_UL_RATE_8K;
|
||||
case 16000:
|
||||
return MTK_AFE_ADDA_UL_RATE_16K;
|
||||
case 32000:
|
||||
return MTK_AFE_ADDA_UL_RATE_32K;
|
||||
case 48000:
|
||||
return MTK_AFE_ADDA_UL_RATE_48K;
|
||||
case 96000:
|
||||
return MTK_AFE_ADDA_UL_RATE_96K;
|
||||
case 192000:
|
||||
return MTK_AFE_ADDA_UL_RATE_192K;
|
||||
default:
|
||||
dev_warn(afe->dev, "%s(), rate %d invalid, use 48kHz!!!\n",
|
||||
__func__, rate);
|
||||
return MTK_AFE_ADDA_UL_RATE_48K;
|
||||
}
|
||||
}
|
||||
|
||||
/* dai component */
|
||||
static const struct snd_kcontrol_new mtk_adda_dl_ch1_mix[] = {
|
||||
SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN3, I_DL1_CH1, 1, 0),
|
||||
SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN3, I_DL2_CH1, 1, 0),
|
||||
SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN3, I_DL3_CH1, 1, 0),
|
||||
SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN3,
|
||||
I_ADDA_UL_CH2, 1, 0),
|
||||
SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN3,
|
||||
I_ADDA_UL_CH1, 1, 0),
|
||||
SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN3,
|
||||
I_PCM_1_CAP_CH1, 1, 0),
|
||||
SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN3,
|
||||
I_PCM_2_CAP_CH1, 1, 0),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new mtk_adda_dl_ch2_mix[] = {
|
||||
SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN4, I_DL1_CH1, 1, 0),
|
||||
SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH2", AFE_CONN4, I_DL1_CH2, 1, 0),
|
||||
SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN4, I_DL2_CH1, 1, 0),
|
||||
SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN4, I_DL2_CH2, 1, 0),
|
||||
SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH1", AFE_CONN4, I_DL3_CH1, 1, 0),
|
||||
SOC_DAPM_SINGLE_AUTODISABLE("DL3_CH2", AFE_CONN4, I_DL3_CH2, 1, 0),
|
||||
SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN4,
|
||||
I_ADDA_UL_CH2, 1, 0),
|
||||
SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN4,
|
||||
I_ADDA_UL_CH1, 1, 0),
|
||||
SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH1", AFE_CONN4,
|
||||
I_PCM_1_CAP_CH1, 1, 0),
|
||||
SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH1", AFE_CONN4,
|
||||
I_PCM_2_CAP_CH1, 1, 0),
|
||||
SOC_DAPM_SINGLE_AUTODISABLE("PCM_1_CAP_CH2", AFE_CONN4,
|
||||
I_PCM_1_CAP_CH2, 1, 0),
|
||||
SOC_DAPM_SINGLE_AUTODISABLE("PCM_2_CAP_CH2", AFE_CONN4,
|
||||
I_PCM_2_CAP_CH2, 1, 0),
|
||||
};
|
||||
|
||||
static int mtk_adda_ul_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 mt8183_afe_private *afe_priv = afe->platform_priv;
|
||||
|
||||
dev_dbg(afe->dev, "%s(), name %s, event 0x%x\n",
|
||||
__func__, w->name, event);
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_PRE_PMU:
|
||||
/* update setting to dmic */
|
||||
if (afe_priv->mtkaif_dmic) {
|
||||
/* mtkaif_rxif_data_mode = 1, dmic */
|
||||
regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIF_RX_CFG0,
|
||||
0x1, 0x1);
|
||||
|
||||
/* dmic mode, 3.25M*/
|
||||
regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIF_RX_CFG0,
|
||||
0x0, 0xf << 20);
|
||||
regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0,
|
||||
0x0, 0x1 << 5);
|
||||
regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0,
|
||||
0x0, 0x3 << 14);
|
||||
|
||||
/* turn on dmic, ch1, ch2 */
|
||||
regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0,
|
||||
0x1 << 1, 0x1 << 1);
|
||||
regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0,
|
||||
0x3 << 21, 0x3 << 21);
|
||||
}
|
||||
break;
|
||||
case SND_SOC_DAPM_POST_PMD:
|
||||
/* should delayed 1/fs(smallest is 8k) = 125us before afe off */
|
||||
usleep_range(125, 135);
|
||||
|
||||
/* reset dmic */
|
||||
afe_priv->mtkaif_dmic = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* mtkaif dmic */
|
||||
static const char * const mt8183_adda_off_on_str[] = {
|
||||
"Off", "On"
|
||||
};
|
||||
|
||||
static const struct soc_enum mt8183_adda_enum[] = {
|
||||
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(mt8183_adda_off_on_str),
|
||||
mt8183_adda_off_on_str),
|
||||
};
|
||||
|
||||
static int mt8183_adda_dmic_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
|
||||
struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
|
||||
struct mt8183_afe_private *afe_priv = afe->platform_priv;
|
||||
|
||||
ucontrol->value.integer.value[0] = afe_priv->mtkaif_dmic;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mt8183_adda_dmic_set(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
|
||||
struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
|
||||
struct mt8183_afe_private *afe_priv = afe->platform_priv;
|
||||
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
|
||||
|
||||
if (ucontrol->value.enumerated.item[0] >= e->items)
|
||||
return -EINVAL;
|
||||
|
||||
afe_priv->mtkaif_dmic = ucontrol->value.integer.value[0];
|
||||
|
||||
dev_info(afe->dev, "%s(), kcontrol name %s, mtkaif_dmic %d\n",
|
||||
__func__, kcontrol->id.name, afe_priv->mtkaif_dmic);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new mtk_adda_controls[] = {
|
||||
SOC_ENUM_EXT("MTKAIF_DMIC", mt8183_adda_enum[0],
|
||||
mt8183_adda_dmic_get, mt8183_adda_dmic_set),
|
||||
};
|
||||
|
||||
enum {
|
||||
SUPPLY_SEQ_ADDA_AFE_ON,
|
||||
SUPPLY_SEQ_ADDA_DL_ON,
|
||||
SUPPLY_SEQ_ADDA_UL_ON,
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget mtk_dai_adda_widgets[] = {
|
||||
/* adda */
|
||||
SND_SOC_DAPM_MIXER("ADDA_DL_CH1", SND_SOC_NOPM, 0, 0,
|
||||
mtk_adda_dl_ch1_mix,
|
||||
ARRAY_SIZE(mtk_adda_dl_ch1_mix)),
|
||||
SND_SOC_DAPM_MIXER("ADDA_DL_CH2", SND_SOC_NOPM, 0, 0,
|
||||
mtk_adda_dl_ch2_mix,
|
||||
ARRAY_SIZE(mtk_adda_dl_ch2_mix)),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY_S("ADDA Enable", SUPPLY_SEQ_ADDA_AFE_ON,
|
||||
AFE_ADDA_UL_DL_CON0, ADDA_AFE_ON_SFT, 0,
|
||||
NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY_S("ADDA Playback Enable", SUPPLY_SEQ_ADDA_DL_ON,
|
||||
AFE_ADDA_DL_SRC2_CON0,
|
||||
DL_2_SRC_ON_TMP_CTL_PRE_SFT, 0,
|
||||
NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY_S("ADDA Capture Enable", SUPPLY_SEQ_ADDA_UL_ON,
|
||||
AFE_ADDA_UL_SRC_CON0,
|
||||
UL_SRC_ON_TMP_CTL_SFT, 0,
|
||||
mtk_adda_ul_event,
|
||||
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
||||
|
||||
SND_SOC_DAPM_CLOCK_SUPPLY("aud_dac_clk"),
|
||||
SND_SOC_DAPM_CLOCK_SUPPLY("aud_dac_predis_clk"),
|
||||
SND_SOC_DAPM_CLOCK_SUPPLY("aud_adc_clk"),
|
||||
SND_SOC_DAPM_CLOCK_SUPPLY("mtkaif_26m_clk"),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route mtk_dai_adda_routes[] = {
|
||||
/* playback */
|
||||
{"ADDA_DL_CH1", "DL1_CH1", "DL1"},
|
||||
{"ADDA_DL_CH2", "DL1_CH1", "DL1"},
|
||||
{"ADDA_DL_CH2", "DL1_CH2", "DL1"},
|
||||
|
||||
{"ADDA_DL_CH1", "DL2_CH1", "DL2"},
|
||||
{"ADDA_DL_CH2", "DL2_CH1", "DL2"},
|
||||
{"ADDA_DL_CH2", "DL2_CH2", "DL2"},
|
||||
|
||||
{"ADDA_DL_CH1", "DL3_CH1", "DL3"},
|
||||
{"ADDA_DL_CH2", "DL3_CH1", "DL3"},
|
||||
{"ADDA_DL_CH2", "DL3_CH2", "DL3"},
|
||||
|
||||
{"ADDA Playback", NULL, "ADDA_DL_CH1"},
|
||||
{"ADDA Playback", NULL, "ADDA_DL_CH2"},
|
||||
|
||||
/* adda enable */
|
||||
{"ADDA Playback", NULL, "ADDA Enable"},
|
||||
{"ADDA Playback", NULL, "ADDA Playback Enable"},
|
||||
{"ADDA Capture", NULL, "ADDA Enable"},
|
||||
{"ADDA Capture", NULL, "ADDA Capture Enable"},
|
||||
|
||||
/* clk */
|
||||
{"ADDA Playback", NULL, "mtkaif_26m_clk"},
|
||||
{"ADDA Playback", NULL, "aud_dac_clk"},
|
||||
{"ADDA Playback", NULL, "aud_dac_predis_clk"},
|
||||
|
||||
{"ADDA Capture", NULL, "mtkaif_26m_clk"},
|
||||
{"ADDA Capture", NULL, "aud_adc_clk"},
|
||||
};
|
||||
|
||||
static int set_mtkaif_rx(struct mtk_base_afe *afe)
|
||||
{
|
||||
struct mt8183_afe_private *afe_priv = afe->platform_priv;
|
||||
int delay_data;
|
||||
int delay_cycle;
|
||||
|
||||
switch (afe_priv->mtkaif_protocol) {
|
||||
case MT8183_MTKAIF_PROTOCOL_2_CLK_P2:
|
||||
regmap_write(afe->regmap, AFE_AUD_PAD_TOP, 0x38);
|
||||
regmap_write(afe->regmap, AFE_AUD_PAD_TOP, 0x39);
|
||||
/* mtkaif_rxif_clkinv_adc inverse for calibration */
|
||||
regmap_write(afe->regmap, AFE_ADDA_MTKAIF_CFG0,
|
||||
0x80010000);
|
||||
|
||||
if (afe_priv->mtkaif_phase_cycle[0] >=
|
||||
afe_priv->mtkaif_phase_cycle[1]) {
|
||||
delay_data = DELAY_DATA_MISO1;
|
||||
delay_cycle = afe_priv->mtkaif_phase_cycle[0] -
|
||||
afe_priv->mtkaif_phase_cycle[1];
|
||||
} else {
|
||||
delay_data = DELAY_DATA_MISO2;
|
||||
delay_cycle = afe_priv->mtkaif_phase_cycle[1] -
|
||||
afe_priv->mtkaif_phase_cycle[0];
|
||||
}
|
||||
|
||||
regmap_update_bits(afe->regmap,
|
||||
AFE_ADDA_MTKAIF_RX_CFG2,
|
||||
MTKAIF_RXIF_DELAY_DATA_MASK_SFT,
|
||||
delay_data << MTKAIF_RXIF_DELAY_DATA_SFT);
|
||||
|
||||
regmap_update_bits(afe->regmap,
|
||||
AFE_ADDA_MTKAIF_RX_CFG2,
|
||||
MTKAIF_RXIF_DELAY_CYCLE_MASK_SFT,
|
||||
delay_cycle << MTKAIF_RXIF_DELAY_CYCLE_SFT);
|
||||
break;
|
||||
case MT8183_MTKAIF_PROTOCOL_2:
|
||||
regmap_write(afe->regmap, AFE_AUD_PAD_TOP, 0x31);
|
||||
regmap_write(afe->regmap, AFE_ADDA_MTKAIF_CFG0,
|
||||
0x00010000);
|
||||
break;
|
||||
case MT8183_MTKAIF_PROTOCOL_1:
|
||||
regmap_write(afe->regmap, AFE_AUD_PAD_TOP, 0x31);
|
||||
regmap_write(afe->regmap, AFE_ADDA_MTKAIF_CFG0, 0x0);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* dai ops */
|
||||
static int mtk_dai_adda_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);
|
||||
unsigned int rate = params_rate(params);
|
||||
|
||||
dev_dbg(afe->dev, "%s(), id %d, stream %d, rate %d\n",
|
||||
__func__, dai->id, substream->stream, rate);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
unsigned int dl_src2_con0 = 0;
|
||||
unsigned int dl_src2_con1 = 0;
|
||||
|
||||
/* clean predistortion */
|
||||
regmap_write(afe->regmap, AFE_ADDA_PREDIS_CON0, 0);
|
||||
regmap_write(afe->regmap, AFE_ADDA_PREDIS_CON1, 0);
|
||||
|
||||
/* set sampling rate */
|
||||
dl_src2_con0 = adda_dl_rate_transform(afe, rate) << 28;
|
||||
|
||||
/* set output mode */
|
||||
switch (rate) {
|
||||
case 192000:
|
||||
dl_src2_con0 |= (0x1 << 24); /* UP_SAMPLING_RATE_X2 */
|
||||
dl_src2_con0 |= 1 << 14;
|
||||
break;
|
||||
case 96000:
|
||||
dl_src2_con0 |= (0x2 << 24); /* UP_SAMPLING_RATE_X4 */
|
||||
dl_src2_con0 |= 1 << 14;
|
||||
break;
|
||||
default:
|
||||
dl_src2_con0 |= (0x3 << 24); /* UP_SAMPLING_RATE_X8 */
|
||||
break;
|
||||
}
|
||||
|
||||
/* turn off mute function */
|
||||
dl_src2_con0 |= (0x03 << 11);
|
||||
|
||||
/* set voice input data if input sample rate is 8k or 16k */
|
||||
if (rate == 8000 || rate == 16000)
|
||||
dl_src2_con0 |= 0x01 << 5;
|
||||
|
||||
/* SA suggest apply -0.3db to audio/speech path */
|
||||
dl_src2_con1 = 0xf74f0000;
|
||||
|
||||
/* turn on down-link gain */
|
||||
dl_src2_con0 = dl_src2_con0 | (0x01 << 1);
|
||||
|
||||
regmap_write(afe->regmap, AFE_ADDA_DL_SRC2_CON0, dl_src2_con0);
|
||||
regmap_write(afe->regmap, AFE_ADDA_DL_SRC2_CON1, dl_src2_con1);
|
||||
|
||||
/* set sdm gain */
|
||||
regmap_update_bits(afe->regmap,
|
||||
AFE_ADDA_DL_SDM_DCCOMP_CON,
|
||||
ATTGAIN_CTL_MASK_SFT,
|
||||
AUDIO_SDM_LEVEL_NORMAL << ATTGAIN_CTL_SFT);
|
||||
} else {
|
||||
unsigned int voice_mode = 0;
|
||||
unsigned int ul_src_con0 = 0; /* default value */
|
||||
|
||||
/* set mtkaif protocol */
|
||||
set_mtkaif_rx(afe);
|
||||
|
||||
/* Using Internal ADC */
|
||||
regmap_update_bits(afe->regmap,
|
||||
AFE_ADDA_TOP_CON0,
|
||||
0x1 << 0,
|
||||
0x0 << 0);
|
||||
|
||||
voice_mode = adda_ul_rate_transform(afe, rate);
|
||||
|
||||
ul_src_con0 |= (voice_mode << 17) & (0x7 << 17);
|
||||
|
||||
regmap_write(afe->regmap, AFE_ADDA_UL_SRC_CON0, ul_src_con0);
|
||||
|
||||
/* mtkaif_rxif_data_mode = 0, amic */
|
||||
regmap_update_bits(afe->regmap,
|
||||
AFE_ADDA_MTKAIF_RX_CFG0,
|
||||
0x1 << 0,
|
||||
0x0 << 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops mtk_dai_adda_ops = {
|
||||
.hw_params = mtk_dai_adda_hw_params,
|
||||
};
|
||||
|
||||
/* dai driver */
|
||||
#define MTK_ADDA_PLAYBACK_RATES (SNDRV_PCM_RATE_8000_48000 |\
|
||||
SNDRV_PCM_RATE_96000 |\
|
||||
SNDRV_PCM_RATE_192000)
|
||||
|
||||
#define MTK_ADDA_CAPTURE_RATES (SNDRV_PCM_RATE_8000 |\
|
||||
SNDRV_PCM_RATE_16000 |\
|
||||
SNDRV_PCM_RATE_32000 |\
|
||||
SNDRV_PCM_RATE_48000)
|
||||
|
||||
#define MTK_ADDA_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
|
||||
SNDRV_PCM_FMTBIT_S24_LE |\
|
||||
SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
static struct snd_soc_dai_driver mtk_dai_adda_driver[] = {
|
||||
{
|
||||
.name = "ADDA",
|
||||
.id = MT8183_DAI_ADDA,
|
||||
.playback = {
|
||||
.stream_name = "ADDA Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = MTK_ADDA_PLAYBACK_RATES,
|
||||
.formats = MTK_ADDA_FORMATS,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "ADDA Capture",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = MTK_ADDA_CAPTURE_RATES,
|
||||
.formats = MTK_ADDA_FORMATS,
|
||||
},
|
||||
.ops = &mtk_dai_adda_ops,
|
||||
},
|
||||
};
|
||||
|
||||
int mt8183_dai_adda_register(struct mtk_base_afe *afe)
|
||||
{
|
||||
struct mtk_base_afe_dai *dai;
|
||||
|
||||
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_adda_driver;
|
||||
dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_adda_driver);
|
||||
|
||||
dai->controls = mtk_adda_controls;
|
||||
dai->num_controls = ARRAY_SIZE(mtk_adda_controls);
|
||||
dai->dapm_widgets = mtk_dai_adda_widgets;
|
||||
dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_adda_widgets);
|
||||
dai->dapm_routes = mtk_dai_adda_routes;
|
||||
dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_adda_routes);
|
||||
return 0;
|
||||
}
|
118
sound/soc/mediatek/mt8183/mt8183-dai-hostless.c
Normal file
118
sound/soc/mediatek/mt8183/mt8183-dai-hostless.c
Normal file
|
@ -0,0 +1,118 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// MediaTek ALSA SoC Audio DAI Hostless Control
|
||||
//
|
||||
// Copyright (c) 2018 MediaTek Inc.
|
||||
// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
|
||||
|
||||
#include "mt8183-afe-common.h"
|
||||
|
||||
/* dai component */
|
||||
static const struct snd_soc_dapm_route mtk_dai_hostless_routes[] = {
|
||||
/* Hostless ADDA Loopback */
|
||||
{"ADDA_DL_CH1", "ADDA_UL_CH1", "Hostless LPBK DL"},
|
||||
{"ADDA_DL_CH1", "ADDA_UL_CH2", "Hostless LPBK DL"},
|
||||
{"ADDA_DL_CH2", "ADDA_UL_CH1", "Hostless LPBK DL"},
|
||||
{"ADDA_DL_CH2", "ADDA_UL_CH2", "Hostless LPBK DL"},
|
||||
{"Hostless LPBK UL", NULL, "ADDA Capture"},
|
||||
|
||||
/* Hostless Speech */
|
||||
{"ADDA_DL_CH1", "PCM_1_CAP_CH1", "Hostless Speech DL"},
|
||||
{"ADDA_DL_CH2", "PCM_1_CAP_CH1", "Hostless Speech DL"},
|
||||
{"ADDA_DL_CH2", "PCM_1_CAP_CH2", "Hostless Speech DL"},
|
||||
{"ADDA_DL_CH1", "PCM_2_CAP_CH1", "Hostless Speech DL"},
|
||||
{"ADDA_DL_CH2", "PCM_2_CAP_CH1", "Hostless Speech DL"},
|
||||
{"ADDA_DL_CH2", "PCM_2_CAP_CH2", "Hostless Speech DL"},
|
||||
{"PCM_1_PB_CH1", "ADDA_UL_CH1", "Hostless Speech DL"},
|
||||
{"PCM_1_PB_CH2", "ADDA_UL_CH2", "Hostless Speech DL"},
|
||||
{"PCM_2_PB_CH1", "ADDA_UL_CH1", "Hostless Speech DL"},
|
||||
{"PCM_2_PB_CH2", "ADDA_UL_CH2", "Hostless Speech DL"},
|
||||
|
||||
{"Hostless Speech UL", NULL, "PCM 1 Capture"},
|
||||
{"Hostless Speech UL", NULL, "PCM 2 Capture"},
|
||||
{"Hostless Speech UL", NULL, "ADDA Capture"},
|
||||
};
|
||||
|
||||
/* dai ops */
|
||||
static int mtk_dai_hostless_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
return snd_soc_set_runtime_hwparams(substream, afe->mtk_afe_hardware);
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops mtk_dai_hostless_ops = {
|
||||
.startup = mtk_dai_hostless_startup,
|
||||
};
|
||||
|
||||
/* dai driver */
|
||||
#define MTK_HOSTLESS_RATES (SNDRV_PCM_RATE_8000_48000 |\
|
||||
SNDRV_PCM_RATE_88200 |\
|
||||
SNDRV_PCM_RATE_96000 |\
|
||||
SNDRV_PCM_RATE_176400 |\
|
||||
SNDRV_PCM_RATE_192000)
|
||||
|
||||
#define MTK_HOSTLESS_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
|
||||
SNDRV_PCM_FMTBIT_S24_LE |\
|
||||
SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
static struct snd_soc_dai_driver mtk_dai_hostless_driver[] = {
|
||||
{
|
||||
.name = "Hostless LPBK DAI",
|
||||
.id = MT8183_DAI_HOSTLESS_LPBK,
|
||||
.playback = {
|
||||
.stream_name = "Hostless LPBK DL",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = MTK_HOSTLESS_RATES,
|
||||
.formats = MTK_HOSTLESS_FORMATS,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "Hostless LPBK UL",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = MTK_HOSTLESS_RATES,
|
||||
.formats = MTK_HOSTLESS_FORMATS,
|
||||
},
|
||||
.ops = &mtk_dai_hostless_ops,
|
||||
},
|
||||
{
|
||||
.name = "Hostless Speech DAI",
|
||||
.id = MT8183_DAI_HOSTLESS_SPEECH,
|
||||
.playback = {
|
||||
.stream_name = "Hostless Speech DL",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = MTK_HOSTLESS_RATES,
|
||||
.formats = MTK_HOSTLESS_FORMATS,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "Hostless Speech UL",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = MTK_HOSTLESS_RATES,
|
||||
.formats = MTK_HOSTLESS_FORMATS,
|
||||
},
|
||||
.ops = &mtk_dai_hostless_ops,
|
||||
},
|
||||
};
|
||||
|
||||
int mt8183_dai_hostless_register(struct mtk_base_afe *afe)
|
||||
{
|
||||
struct mtk_base_afe_dai *dai;
|
||||
|
||||
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_hostless_driver;
|
||||
dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_hostless_driver);
|
||||
|
||||
dai->dapm_routes = mtk_dai_hostless_routes;
|
||||
dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_hostless_routes);
|
||||
|
||||
return 0;
|
||||
}
|
1040
sound/soc/mediatek/mt8183/mt8183-dai-i2s.c
Normal file
1040
sound/soc/mediatek/mt8183/mt8183-dai-i2s.c
Normal file
File diff suppressed because it is too large
Load diff
318
sound/soc/mediatek/mt8183/mt8183-dai-pcm.c
Normal file
318
sound/soc/mediatek/mt8183/mt8183-dai-pcm.c
Normal file
|
@ -0,0 +1,318 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// MediaTek ALSA SoC Audio DAI I2S Control
|
||||
//
|
||||
// Copyright (c) 2018 MediaTek Inc.
|
||||
// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
|
||||
|
||||
#include <linux/regmap.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include "mt8183-afe-common.h"
|
||||
#include "mt8183-interconnection.h"
|
||||
#include "mt8183-reg.h"
|
||||
|
||||
enum AUD_TX_LCH_RPT {
|
||||
AUD_TX_LCH_RPT_NO_REPEAT = 0,
|
||||
AUD_TX_LCH_RPT_REPEAT = 1
|
||||
};
|
||||
|
||||
enum AUD_VBT_16K_MODE {
|
||||
AUD_VBT_16K_MODE_DISABLE = 0,
|
||||
AUD_VBT_16K_MODE_ENABLE = 1
|
||||
};
|
||||
|
||||
enum AUD_EXT_MODEM {
|
||||
AUD_EXT_MODEM_SELECT_INTERNAL = 0,
|
||||
AUD_EXT_MODEM_SELECT_EXTERNAL = 1
|
||||
};
|
||||
|
||||
enum AUD_PCM_SYNC_TYPE {
|
||||
/* bck sync length = 1 */
|
||||
AUD_PCM_ONE_BCK_CYCLE_SYNC = 0,
|
||||
/* bck sync length = PCM_INTF_CON1[9:13] */
|
||||
AUD_PCM_EXTENDED_BCK_CYCLE_SYNC = 1
|
||||
};
|
||||
|
||||
enum AUD_BT_MODE {
|
||||
AUD_BT_MODE_DUAL_MIC_ON_TX = 0,
|
||||
AUD_BT_MODE_SINGLE_MIC_ON_TX = 1
|
||||
};
|
||||
|
||||
enum AUD_PCM_AFIFO_SRC {
|
||||
/* slave mode & external modem uses different crystal */
|
||||
AUD_PCM_AFIFO_ASRC = 0,
|
||||
/* slave mode & external modem uses the same crystal */
|
||||
AUD_PCM_AFIFO_AFIFO = 1
|
||||
};
|
||||
|
||||
enum AUD_PCM_CLOCK_SOURCE {
|
||||
AUD_PCM_CLOCK_MASTER_MODE = 0,
|
||||
AUD_PCM_CLOCK_SLAVE_MODE = 1
|
||||
};
|
||||
|
||||
enum AUD_PCM_WLEN {
|
||||
AUD_PCM_WLEN_PCM_32_BCK_CYCLES = 0,
|
||||
AUD_PCM_WLEN_PCM_64_BCK_CYCLES = 1
|
||||
};
|
||||
|
||||
enum AUD_PCM_MODE {
|
||||
AUD_PCM_MODE_PCM_MODE_8K = 0,
|
||||
AUD_PCM_MODE_PCM_MODE_16K = 1,
|
||||
AUD_PCM_MODE_PCM_MODE_32K = 2,
|
||||
AUD_PCM_MODE_PCM_MODE_48K = 3,
|
||||
};
|
||||
|
||||
enum AUD_PCM_FMT {
|
||||
AUD_PCM_FMT_I2S = 0,
|
||||
AUD_PCM_FMT_EIAJ = 1,
|
||||
AUD_PCM_FMT_PCM_MODE_A = 2,
|
||||
AUD_PCM_FMT_PCM_MODE_B = 3
|
||||
};
|
||||
|
||||
enum AUD_BCLK_OUT_INV {
|
||||
AUD_BCLK_OUT_INV_NO_INVERSE = 0,
|
||||
AUD_BCLK_OUT_INV_INVERSE = 1
|
||||
};
|
||||
|
||||
enum AUD_PCM_EN {
|
||||
AUD_PCM_EN_DISABLE = 0,
|
||||
AUD_PCM_EN_ENABLE = 1
|
||||
};
|
||||
|
||||
/* dai component */
|
||||
static const struct snd_kcontrol_new mtk_pcm_1_playback_ch1_mix[] = {
|
||||
SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN7,
|
||||
I_ADDA_UL_CH1, 1, 0),
|
||||
SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN7,
|
||||
I_DL2_CH1, 1, 0),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new mtk_pcm_1_playback_ch2_mix[] = {
|
||||
SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN8,
|
||||
I_ADDA_UL_CH2, 1, 0),
|
||||
SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN8,
|
||||
I_DL2_CH2, 1, 0),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new mtk_pcm_1_playback_ch4_mix[] = {
|
||||
SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN27,
|
||||
I_DL1_CH1, 1, 0),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new mtk_pcm_2_playback_ch1_mix[] = {
|
||||
SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH1", AFE_CONN17,
|
||||
I_ADDA_UL_CH1, 1, 0),
|
||||
SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1", AFE_CONN17,
|
||||
I_DL2_CH1, 1, 0),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new mtk_pcm_2_playback_ch2_mix[] = {
|
||||
SOC_DAPM_SINGLE_AUTODISABLE("ADDA_UL_CH2", AFE_CONN18,
|
||||
I_ADDA_UL_CH2, 1, 0),
|
||||
SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH2", AFE_CONN18,
|
||||
I_DL2_CH2, 1, 0),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new mtk_pcm_2_playback_ch4_mix[] = {
|
||||
SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN24,
|
||||
I_DL1_CH1, 1, 0),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget mtk_dai_pcm_widgets[] = {
|
||||
/* inter-connections */
|
||||
SND_SOC_DAPM_MIXER("PCM_1_PB_CH1", SND_SOC_NOPM, 0, 0,
|
||||
mtk_pcm_1_playback_ch1_mix,
|
||||
ARRAY_SIZE(mtk_pcm_1_playback_ch1_mix)),
|
||||
SND_SOC_DAPM_MIXER("PCM_1_PB_CH2", SND_SOC_NOPM, 0, 0,
|
||||
mtk_pcm_1_playback_ch2_mix,
|
||||
ARRAY_SIZE(mtk_pcm_1_playback_ch2_mix)),
|
||||
SND_SOC_DAPM_MIXER("PCM_1_PB_CH4", SND_SOC_NOPM, 0, 0,
|
||||
mtk_pcm_1_playback_ch4_mix,
|
||||
ARRAY_SIZE(mtk_pcm_1_playback_ch4_mix)),
|
||||
SND_SOC_DAPM_MIXER("PCM_2_PB_CH1", SND_SOC_NOPM, 0, 0,
|
||||
mtk_pcm_2_playback_ch1_mix,
|
||||
ARRAY_SIZE(mtk_pcm_2_playback_ch1_mix)),
|
||||
SND_SOC_DAPM_MIXER("PCM_2_PB_CH2", SND_SOC_NOPM, 0, 0,
|
||||
mtk_pcm_2_playback_ch2_mix,
|
||||
ARRAY_SIZE(mtk_pcm_2_playback_ch2_mix)),
|
||||
SND_SOC_DAPM_MIXER("PCM_2_PB_CH4", SND_SOC_NOPM, 0, 0,
|
||||
mtk_pcm_2_playback_ch4_mix,
|
||||
ARRAY_SIZE(mtk_pcm_2_playback_ch4_mix)),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("PCM_1_EN", PCM_INTF_CON1, PCM_EN_SFT, 0,
|
||||
NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("PCM_2_EN", PCM2_INTF_CON, PCM2_EN_SFT, 0,
|
||||
NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_INPUT("MD1_TO_AFE"),
|
||||
SND_SOC_DAPM_INPUT("MD2_TO_AFE"),
|
||||
SND_SOC_DAPM_OUTPUT("AFE_TO_MD1"),
|
||||
SND_SOC_DAPM_OUTPUT("AFE_TO_MD2"),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route mtk_dai_pcm_routes[] = {
|
||||
{"PCM 1 Playback", NULL, "PCM_1_PB_CH1"},
|
||||
{"PCM 1 Playback", NULL, "PCM_1_PB_CH2"},
|
||||
{"PCM 1 Playback", NULL, "PCM_1_PB_CH4"},
|
||||
{"PCM 2 Playback", NULL, "PCM_2_PB_CH1"},
|
||||
{"PCM 2 Playback", NULL, "PCM_2_PB_CH2"},
|
||||
{"PCM 2 Playback", NULL, "PCM_2_PB_CH4"},
|
||||
|
||||
{"PCM 1 Playback", NULL, "PCM_1_EN"},
|
||||
{"PCM 2 Playback", NULL, "PCM_2_EN"},
|
||||
{"PCM 1 Capture", NULL, "PCM_1_EN"},
|
||||
{"PCM 2 Capture", NULL, "PCM_2_EN"},
|
||||
|
||||
{"AFE_TO_MD1", NULL, "PCM 2 Playback"},
|
||||
{"AFE_TO_MD2", NULL, "PCM 1 Playback"},
|
||||
{"PCM 2 Capture", NULL, "MD1_TO_AFE"},
|
||||
{"PCM 1 Capture", NULL, "MD2_TO_AFE"},
|
||||
|
||||
{"PCM_1_PB_CH1", "DL2_CH1", "DL2"},
|
||||
{"PCM_1_PB_CH2", "DL2_CH2", "DL2"},
|
||||
{"PCM_1_PB_CH4", "DL1_CH1", "DL1"},
|
||||
{"PCM_2_PB_CH1", "DL2_CH1", "DL2"},
|
||||
{"PCM_2_PB_CH2", "DL2_CH2", "DL2"},
|
||||
{"PCM_2_PB_CH4", "DL1_CH1", "DL1"},
|
||||
};
|
||||
|
||||
/* dai ops */
|
||||
static int mtk_dai_pcm_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);
|
||||
unsigned int rate = params_rate(params);
|
||||
unsigned int rate_reg = mt8183_rate_transform(afe->dev, rate, dai->id);
|
||||
unsigned int pcm_con = 0;
|
||||
|
||||
dev_dbg(afe->dev, "%s(), id %d, stream %d, rate %d, rate_reg %d, widget active p %d, c %d\n",
|
||||
__func__,
|
||||
dai->id,
|
||||
substream->stream,
|
||||
rate,
|
||||
rate_reg,
|
||||
dai->playback_widget->active,
|
||||
dai->capture_widget->active);
|
||||
|
||||
if (dai->playback_widget->active || dai->capture_widget->active)
|
||||
return 0;
|
||||
|
||||
switch (dai->id) {
|
||||
case MT8183_DAI_PCM_1:
|
||||
pcm_con |= AUD_BCLK_OUT_INV_NO_INVERSE << PCM_BCLK_OUT_INV_SFT;
|
||||
pcm_con |= AUD_TX_LCH_RPT_NO_REPEAT << PCM_TX_LCH_RPT_SFT;
|
||||
pcm_con |= AUD_VBT_16K_MODE_DISABLE << PCM_VBT_16K_MODE_SFT;
|
||||
pcm_con |= AUD_EXT_MODEM_SELECT_INTERNAL << PCM_EXT_MODEM_SFT;
|
||||
pcm_con |= 0 << PCM_SYNC_LENGTH_SFT;
|
||||
pcm_con |= AUD_PCM_ONE_BCK_CYCLE_SYNC << PCM_SYNC_TYPE_SFT;
|
||||
pcm_con |= AUD_BT_MODE_DUAL_MIC_ON_TX << PCM_BT_MODE_SFT;
|
||||
pcm_con |= AUD_PCM_AFIFO_AFIFO << PCM_BYP_ASRC_SFT;
|
||||
pcm_con |= AUD_PCM_CLOCK_SLAVE_MODE << PCM_SLAVE_SFT;
|
||||
pcm_con |= rate_reg << PCM_MODE_SFT;
|
||||
pcm_con |= AUD_PCM_FMT_PCM_MODE_B << PCM_FMT_SFT;
|
||||
|
||||
regmap_update_bits(afe->regmap, PCM_INTF_CON1,
|
||||
0xfffffffe, pcm_con);
|
||||
break;
|
||||
case MT8183_DAI_PCM_2:
|
||||
pcm_con |= AUD_TX_LCH_RPT_NO_REPEAT << PCM2_TX_LCH_RPT_SFT;
|
||||
pcm_con |= AUD_VBT_16K_MODE_DISABLE << PCM2_VBT_16K_MODE_SFT;
|
||||
pcm_con |= AUD_BT_MODE_DUAL_MIC_ON_TX << PCM2_BT_MODE_SFT;
|
||||
pcm_con |= AUD_PCM_AFIFO_AFIFO << PCM2_AFIFO_SFT;
|
||||
pcm_con |= AUD_PCM_WLEN_PCM_32_BCK_CYCLES << PCM2_WLEN_SFT;
|
||||
pcm_con |= rate_reg << PCM2_MODE_SFT;
|
||||
pcm_con |= AUD_PCM_FMT_PCM_MODE_B << PCM2_FMT_SFT;
|
||||
|
||||
regmap_update_bits(afe->regmap, PCM2_INTF_CON,
|
||||
0xfffffffe, pcm_con);
|
||||
break;
|
||||
default:
|
||||
dev_warn(afe->dev, "%s(), id %d not support\n",
|
||||
__func__, dai->id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops mtk_dai_pcm_ops = {
|
||||
.hw_params = mtk_dai_pcm_hw_params,
|
||||
};
|
||||
|
||||
/* dai driver */
|
||||
#define MTK_PCM_RATES (SNDRV_PCM_RATE_8000 |\
|
||||
SNDRV_PCM_RATE_16000 |\
|
||||
SNDRV_PCM_RATE_32000 |\
|
||||
SNDRV_PCM_RATE_48000)
|
||||
|
||||
#define MTK_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
|
||||
SNDRV_PCM_FMTBIT_S24_LE |\
|
||||
SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
static struct snd_soc_dai_driver mtk_dai_pcm_driver[] = {
|
||||
{
|
||||
.name = "PCM 1",
|
||||
.id = MT8183_DAI_PCM_1,
|
||||
.playback = {
|
||||
.stream_name = "PCM 1 Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = MTK_PCM_RATES,
|
||||
.formats = MTK_PCM_FORMATS,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "PCM 1 Capture",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = MTK_PCM_RATES,
|
||||
.formats = MTK_PCM_FORMATS,
|
||||
},
|
||||
.ops = &mtk_dai_pcm_ops,
|
||||
.symmetric_rates = 1,
|
||||
.symmetric_samplebits = 1,
|
||||
},
|
||||
{
|
||||
.name = "PCM 2",
|
||||
.id = MT8183_DAI_PCM_2,
|
||||
.playback = {
|
||||
.stream_name = "PCM 2 Playback",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = MTK_PCM_RATES,
|
||||
.formats = MTK_PCM_FORMATS,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "PCM 2 Capture",
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.rates = MTK_PCM_RATES,
|
||||
.formats = MTK_PCM_FORMATS,
|
||||
},
|
||||
.ops = &mtk_dai_pcm_ops,
|
||||
.symmetric_rates = 1,
|
||||
.symmetric_samplebits = 1,
|
||||
},
|
||||
};
|
||||
|
||||
int mt8183_dai_pcm_register(struct mtk_base_afe *afe)
|
||||
{
|
||||
struct mtk_base_afe_dai *dai;
|
||||
|
||||
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_pcm_driver;
|
||||
dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_pcm_driver);
|
||||
|
||||
dai->dapm_widgets = mtk_dai_pcm_widgets;
|
||||
dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_pcm_widgets);
|
||||
dai->dapm_routes = mtk_dai_pcm_routes;
|
||||
dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_pcm_routes);
|
||||
|
||||
return 0;
|
||||
}
|
639
sound/soc/mediatek/mt8183/mt8183-dai-tdm.c
Normal file
639
sound/soc/mediatek/mt8183/mt8183-dai-tdm.c
Normal file
|
@ -0,0 +1,639 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// MediaTek ALSA SoC Audio DAI TDM Control
|
||||
//
|
||||
// Copyright (c) 2018 MediaTek Inc.
|
||||
// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
|
||||
|
||||
#include <linux/regmap.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include "mt8183-afe-clk.h"
|
||||
#include "mt8183-afe-common.h"
|
||||
#include "mt8183-interconnection.h"
|
||||
#include "mt8183-reg.h"
|
||||
|
||||
struct mtk_afe_tdm_priv {
|
||||
int bck_id;
|
||||
int bck_rate;
|
||||
|
||||
int mclk_id;
|
||||
int mclk_multiple; /* according to sample rate */
|
||||
int mclk_rate;
|
||||
int mclk_apll;
|
||||
};
|
||||
|
||||
enum {
|
||||
TDM_WLEN_16_BIT = 1,
|
||||
TDM_WLEN_32_BIT = 2,
|
||||
};
|
||||
|
||||
enum {
|
||||
TDM_CHANNEL_BCK_16 = 0,
|
||||
TDM_CHANNEL_BCK_24 = 1,
|
||||
TDM_CHANNEL_BCK_32 = 2,
|
||||
};
|
||||
|
||||
enum {
|
||||
TDM_CHANNEL_NUM_2 = 0,
|
||||
TDM_CHANNEL_NUM_4 = 1,
|
||||
TDM_CHANNEL_NUM_8 = 2,
|
||||
};
|
||||
|
||||
enum {
|
||||
TDM_CH_START_O30_O31 = 0,
|
||||
TDM_CH_START_O32_O33,
|
||||
TDM_CH_START_O34_O35,
|
||||
TDM_CH_START_O36_O37,
|
||||
TDM_CH_ZERO,
|
||||
};
|
||||
|
||||
enum {
|
||||
HDMI_BIT_WIDTH_16_BIT = 0,
|
||||
HDMI_BIT_WIDTH_32_BIT = 1,
|
||||
};
|
||||
|
||||
static unsigned int get_hdmi_wlen(snd_pcm_format_t format)
|
||||
{
|
||||
return snd_pcm_format_physical_width(format) <= 16 ?
|
||||
HDMI_BIT_WIDTH_16_BIT : HDMI_BIT_WIDTH_32_BIT;
|
||||
}
|
||||
|
||||
static unsigned int get_tdm_wlen(snd_pcm_format_t format)
|
||||
{
|
||||
return snd_pcm_format_physical_width(format) <= 16 ?
|
||||
TDM_WLEN_16_BIT : TDM_WLEN_32_BIT;
|
||||
}
|
||||
|
||||
static unsigned int get_tdm_channel_bck(snd_pcm_format_t format)
|
||||
{
|
||||
return snd_pcm_format_physical_width(format) <= 16 ?
|
||||
TDM_CHANNEL_BCK_16 : TDM_CHANNEL_BCK_32;
|
||||
}
|
||||
|
||||
static unsigned int get_tdm_lrck_width(snd_pcm_format_t format)
|
||||
{
|
||||
return snd_pcm_format_physical_width(format) - 1;
|
||||
}
|
||||
|
||||
static unsigned int get_tdm_ch(unsigned int ch)
|
||||
{
|
||||
switch (ch) {
|
||||
case 1:
|
||||
case 2:
|
||||
return TDM_CHANNEL_NUM_2;
|
||||
case 3:
|
||||
case 4:
|
||||
return TDM_CHANNEL_NUM_4;
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
default:
|
||||
return TDM_CHANNEL_NUM_8;
|
||||
}
|
||||
}
|
||||
|
||||
/* interconnection */
|
||||
enum {
|
||||
HDMI_CONN_CH0 = 0,
|
||||
HDMI_CONN_CH1,
|
||||
HDMI_CONN_CH2,
|
||||
HDMI_CONN_CH3,
|
||||
HDMI_CONN_CH4,
|
||||
HDMI_CONN_CH5,
|
||||
HDMI_CONN_CH6,
|
||||
HDMI_CONN_CH7,
|
||||
};
|
||||
|
||||
static const char *const hdmi_conn_mux_map[] = {
|
||||
"CH0", "CH1", "CH2", "CH3",
|
||||
"CH4", "CH5", "CH6", "CH7",
|
||||
};
|
||||
|
||||
static int hdmi_conn_mux_map_value[] = {
|
||||
HDMI_CONN_CH0,
|
||||
HDMI_CONN_CH1,
|
||||
HDMI_CONN_CH2,
|
||||
HDMI_CONN_CH3,
|
||||
HDMI_CONN_CH4,
|
||||
HDMI_CONN_CH5,
|
||||
HDMI_CONN_CH6,
|
||||
HDMI_CONN_CH7,
|
||||
};
|
||||
|
||||
static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch0_mux_map_enum,
|
||||
AFE_HDMI_CONN0,
|
||||
HDMI_O_0_SFT,
|
||||
HDMI_O_0_MASK,
|
||||
hdmi_conn_mux_map,
|
||||
hdmi_conn_mux_map_value);
|
||||
|
||||
static const struct snd_kcontrol_new hdmi_ch0_mux_control =
|
||||
SOC_DAPM_ENUM("HDMI_CH0_MUX", hdmi_ch0_mux_map_enum);
|
||||
|
||||
static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch1_mux_map_enum,
|
||||
AFE_HDMI_CONN0,
|
||||
HDMI_O_1_SFT,
|
||||
HDMI_O_1_MASK,
|
||||
hdmi_conn_mux_map,
|
||||
hdmi_conn_mux_map_value);
|
||||
|
||||
static const struct snd_kcontrol_new hdmi_ch1_mux_control =
|
||||
SOC_DAPM_ENUM("HDMI_CH1_MUX", hdmi_ch1_mux_map_enum);
|
||||
|
||||
static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch2_mux_map_enum,
|
||||
AFE_HDMI_CONN0,
|
||||
HDMI_O_2_SFT,
|
||||
HDMI_O_2_MASK,
|
||||
hdmi_conn_mux_map,
|
||||
hdmi_conn_mux_map_value);
|
||||
|
||||
static const struct snd_kcontrol_new hdmi_ch2_mux_control =
|
||||
SOC_DAPM_ENUM("HDMI_CH2_MUX", hdmi_ch2_mux_map_enum);
|
||||
|
||||
static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch3_mux_map_enum,
|
||||
AFE_HDMI_CONN0,
|
||||
HDMI_O_3_SFT,
|
||||
HDMI_O_3_MASK,
|
||||
hdmi_conn_mux_map,
|
||||
hdmi_conn_mux_map_value);
|
||||
|
||||
static const struct snd_kcontrol_new hdmi_ch3_mux_control =
|
||||
SOC_DAPM_ENUM("HDMI_CH3_MUX", hdmi_ch3_mux_map_enum);
|
||||
|
||||
static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch4_mux_map_enum,
|
||||
AFE_HDMI_CONN0,
|
||||
HDMI_O_4_SFT,
|
||||
HDMI_O_4_MASK,
|
||||
hdmi_conn_mux_map,
|
||||
hdmi_conn_mux_map_value);
|
||||
|
||||
static const struct snd_kcontrol_new hdmi_ch4_mux_control =
|
||||
SOC_DAPM_ENUM("HDMI_CH4_MUX", hdmi_ch4_mux_map_enum);
|
||||
|
||||
static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch5_mux_map_enum,
|
||||
AFE_HDMI_CONN0,
|
||||
HDMI_O_5_SFT,
|
||||
HDMI_O_5_MASK,
|
||||
hdmi_conn_mux_map,
|
||||
hdmi_conn_mux_map_value);
|
||||
|
||||
static const struct snd_kcontrol_new hdmi_ch5_mux_control =
|
||||
SOC_DAPM_ENUM("HDMI_CH5_MUX", hdmi_ch5_mux_map_enum);
|
||||
|
||||
static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch6_mux_map_enum,
|
||||
AFE_HDMI_CONN0,
|
||||
HDMI_O_6_SFT,
|
||||
HDMI_O_6_MASK,
|
||||
hdmi_conn_mux_map,
|
||||
hdmi_conn_mux_map_value);
|
||||
|
||||
static const struct snd_kcontrol_new hdmi_ch6_mux_control =
|
||||
SOC_DAPM_ENUM("HDMI_CH6_MUX", hdmi_ch6_mux_map_enum);
|
||||
|
||||
static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch7_mux_map_enum,
|
||||
AFE_HDMI_CONN0,
|
||||
HDMI_O_7_SFT,
|
||||
HDMI_O_7_MASK,
|
||||
hdmi_conn_mux_map,
|
||||
hdmi_conn_mux_map_value);
|
||||
|
||||
static const struct snd_kcontrol_new hdmi_ch7_mux_control =
|
||||
SOC_DAPM_ENUM("HDMI_CH7_MUX", hdmi_ch7_mux_map_enum);
|
||||
|
||||
enum {
|
||||
SUPPLY_SEQ_APLL,
|
||||
SUPPLY_SEQ_TDM_MCK_EN,
|
||||
SUPPLY_SEQ_TDM_BCK_EN,
|
||||
};
|
||||
|
||||
static int mtk_tdm_bck_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 mt8183_afe_private *afe_priv = afe->platform_priv;
|
||||
struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[MT8183_DAI_TDM];
|
||||
|
||||
dev_info(cmpnt->dev, "%s(), name %s, event 0x%x\n",
|
||||
__func__, w->name, event);
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_PRE_PMU:
|
||||
mt8183_mck_enable(afe, tdm_priv->bck_id, tdm_priv->bck_rate);
|
||||
break;
|
||||
case SND_SOC_DAPM_POST_PMD:
|
||||
mt8183_mck_disable(afe, tdm_priv->bck_id);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_tdm_mck_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 mt8183_afe_private *afe_priv = afe->platform_priv;
|
||||
struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[MT8183_DAI_TDM];
|
||||
|
||||
dev_info(cmpnt->dev, "%s(), name %s, event 0x%x\n",
|
||||
__func__, w->name, event);
|
||||
|
||||
switch (event) {
|
||||
case SND_SOC_DAPM_PRE_PMU:
|
||||
mt8183_mck_enable(afe, tdm_priv->mclk_id, tdm_priv->mclk_rate);
|
||||
break;
|
||||
case SND_SOC_DAPM_POST_PMD:
|
||||
tdm_priv->mclk_rate = 0;
|
||||
mt8183_mck_disable(afe, tdm_priv->mclk_id);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dapm_widget mtk_dai_tdm_widgets[] = {
|
||||
SND_SOC_DAPM_MUX("HDMI_CH0_MUX", SND_SOC_NOPM, 0, 0,
|
||||
&hdmi_ch0_mux_control),
|
||||
SND_SOC_DAPM_MUX("HDMI_CH1_MUX", SND_SOC_NOPM, 0, 0,
|
||||
&hdmi_ch1_mux_control),
|
||||
SND_SOC_DAPM_MUX("HDMI_CH2_MUX", SND_SOC_NOPM, 0, 0,
|
||||
&hdmi_ch2_mux_control),
|
||||
SND_SOC_DAPM_MUX("HDMI_CH3_MUX", SND_SOC_NOPM, 0, 0,
|
||||
&hdmi_ch3_mux_control),
|
||||
SND_SOC_DAPM_MUX("HDMI_CH4_MUX", SND_SOC_NOPM, 0, 0,
|
||||
&hdmi_ch4_mux_control),
|
||||
SND_SOC_DAPM_MUX("HDMI_CH5_MUX", SND_SOC_NOPM, 0, 0,
|
||||
&hdmi_ch5_mux_control),
|
||||
SND_SOC_DAPM_MUX("HDMI_CH6_MUX", SND_SOC_NOPM, 0, 0,
|
||||
&hdmi_ch6_mux_control),
|
||||
SND_SOC_DAPM_MUX("HDMI_CH7_MUX", SND_SOC_NOPM, 0, 0,
|
||||
&hdmi_ch7_mux_control),
|
||||
|
||||
SND_SOC_DAPM_CLOCK_SUPPLY("aud_tdm_clk"),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY_S("TDM_BCK", SUPPLY_SEQ_TDM_BCK_EN,
|
||||
SND_SOC_NOPM, 0, 0,
|
||||
mtk_tdm_bck_en_event,
|
||||
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY_S("TDM_MCK", SUPPLY_SEQ_TDM_MCK_EN,
|
||||
SND_SOC_NOPM, 0, 0,
|
||||
mtk_tdm_mck_en_event,
|
||||
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
||||
};
|
||||
|
||||
static int mtk_afe_tdm_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 mt8183_afe_private *afe_priv = afe->platform_priv;
|
||||
struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[MT8183_DAI_TDM];
|
||||
int cur_apll;
|
||||
|
||||
/* which apll */
|
||||
cur_apll = mt8183_get_apll_by_name(afe, source->name);
|
||||
|
||||
return (tdm_priv->mclk_apll == cur_apll) ? 1 : 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dapm_route mtk_dai_tdm_routes[] = {
|
||||
{"HDMI_CH0_MUX", "CH0", "HDMI"},
|
||||
{"HDMI_CH0_MUX", "CH1", "HDMI"},
|
||||
{"HDMI_CH0_MUX", "CH2", "HDMI"},
|
||||
{"HDMI_CH0_MUX", "CH3", "HDMI"},
|
||||
{"HDMI_CH0_MUX", "CH4", "HDMI"},
|
||||
{"HDMI_CH0_MUX", "CH5", "HDMI"},
|
||||
{"HDMI_CH0_MUX", "CH6", "HDMI"},
|
||||
{"HDMI_CH0_MUX", "CH7", "HDMI"},
|
||||
|
||||
{"HDMI_CH1_MUX", "CH0", "HDMI"},
|
||||
{"HDMI_CH1_MUX", "CH1", "HDMI"},
|
||||
{"HDMI_CH1_MUX", "CH2", "HDMI"},
|
||||
{"HDMI_CH1_MUX", "CH3", "HDMI"},
|
||||
{"HDMI_CH1_MUX", "CH4", "HDMI"},
|
||||
{"HDMI_CH1_MUX", "CH5", "HDMI"},
|
||||
{"HDMI_CH1_MUX", "CH6", "HDMI"},
|
||||
{"HDMI_CH1_MUX", "CH7", "HDMI"},
|
||||
|
||||
{"HDMI_CH2_MUX", "CH0", "HDMI"},
|
||||
{"HDMI_CH2_MUX", "CH1", "HDMI"},
|
||||
{"HDMI_CH2_MUX", "CH2", "HDMI"},
|
||||
{"HDMI_CH2_MUX", "CH3", "HDMI"},
|
||||
{"HDMI_CH2_MUX", "CH4", "HDMI"},
|
||||
{"HDMI_CH2_MUX", "CH5", "HDMI"},
|
||||
{"HDMI_CH2_MUX", "CH6", "HDMI"},
|
||||
{"HDMI_CH2_MUX", "CH7", "HDMI"},
|
||||
|
||||
{"HDMI_CH3_MUX", "CH0", "HDMI"},
|
||||
{"HDMI_CH3_MUX", "CH1", "HDMI"},
|
||||
{"HDMI_CH3_MUX", "CH2", "HDMI"},
|
||||
{"HDMI_CH3_MUX", "CH3", "HDMI"},
|
||||
{"HDMI_CH3_MUX", "CH4", "HDMI"},
|
||||
{"HDMI_CH3_MUX", "CH5", "HDMI"},
|
||||
{"HDMI_CH3_MUX", "CH6", "HDMI"},
|
||||
{"HDMI_CH3_MUX", "CH7", "HDMI"},
|
||||
|
||||
{"HDMI_CH4_MUX", "CH0", "HDMI"},
|
||||
{"HDMI_CH4_MUX", "CH1", "HDMI"},
|
||||
{"HDMI_CH4_MUX", "CH2", "HDMI"},
|
||||
{"HDMI_CH4_MUX", "CH3", "HDMI"},
|
||||
{"HDMI_CH4_MUX", "CH4", "HDMI"},
|
||||
{"HDMI_CH4_MUX", "CH5", "HDMI"},
|
||||
{"HDMI_CH4_MUX", "CH6", "HDMI"},
|
||||
{"HDMI_CH4_MUX", "CH7", "HDMI"},
|
||||
|
||||
{"HDMI_CH5_MUX", "CH0", "HDMI"},
|
||||
{"HDMI_CH5_MUX", "CH1", "HDMI"},
|
||||
{"HDMI_CH5_MUX", "CH2", "HDMI"},
|
||||
{"HDMI_CH5_MUX", "CH3", "HDMI"},
|
||||
{"HDMI_CH5_MUX", "CH4", "HDMI"},
|
||||
{"HDMI_CH5_MUX", "CH5", "HDMI"},
|
||||
{"HDMI_CH5_MUX", "CH6", "HDMI"},
|
||||
{"HDMI_CH5_MUX", "CH7", "HDMI"},
|
||||
|
||||
{"HDMI_CH6_MUX", "CH0", "HDMI"},
|
||||
{"HDMI_CH6_MUX", "CH1", "HDMI"},
|
||||
{"HDMI_CH6_MUX", "CH2", "HDMI"},
|
||||
{"HDMI_CH6_MUX", "CH3", "HDMI"},
|
||||
{"HDMI_CH6_MUX", "CH4", "HDMI"},
|
||||
{"HDMI_CH6_MUX", "CH5", "HDMI"},
|
||||
{"HDMI_CH6_MUX", "CH6", "HDMI"},
|
||||
{"HDMI_CH6_MUX", "CH7", "HDMI"},
|
||||
|
||||
{"HDMI_CH7_MUX", "CH0", "HDMI"},
|
||||
{"HDMI_CH7_MUX", "CH1", "HDMI"},
|
||||
{"HDMI_CH7_MUX", "CH2", "HDMI"},
|
||||
{"HDMI_CH7_MUX", "CH3", "HDMI"},
|
||||
{"HDMI_CH7_MUX", "CH4", "HDMI"},
|
||||
{"HDMI_CH7_MUX", "CH5", "HDMI"},
|
||||
{"HDMI_CH7_MUX", "CH6", "HDMI"},
|
||||
{"HDMI_CH7_MUX", "CH7", "HDMI"},
|
||||
|
||||
{"TDM", NULL, "HDMI_CH0_MUX"},
|
||||
{"TDM", NULL, "HDMI_CH1_MUX"},
|
||||
{"TDM", NULL, "HDMI_CH2_MUX"},
|
||||
{"TDM", NULL, "HDMI_CH3_MUX"},
|
||||
{"TDM", NULL, "HDMI_CH4_MUX"},
|
||||
{"TDM", NULL, "HDMI_CH5_MUX"},
|
||||
{"TDM", NULL, "HDMI_CH6_MUX"},
|
||||
{"TDM", NULL, "HDMI_CH7_MUX"},
|
||||
|
||||
{"TDM", NULL, "aud_tdm_clk"},
|
||||
{"TDM", NULL, "TDM_BCK"},
|
||||
{"TDM_BCK", NULL, "TDM_MCK"},
|
||||
{"TDM_MCK", NULL, APLL1_W_NAME, mtk_afe_tdm_apll_connect},
|
||||
{"TDM_MCK", NULL, APLL2_W_NAME, mtk_afe_tdm_apll_connect},
|
||||
};
|
||||
|
||||
/* dai ops */
|
||||
static int mtk_dai_tdm_cal_mclk(struct mtk_base_afe *afe,
|
||||
struct mtk_afe_tdm_priv *tdm_priv,
|
||||
int freq)
|
||||
{
|
||||
int apll;
|
||||
int apll_rate;
|
||||
|
||||
apll = mt8183_get_apll_by_rate(afe, freq);
|
||||
apll_rate = mt8183_get_apll_rate(afe, apll);
|
||||
|
||||
if (!freq || freq > apll_rate) {
|
||||
dev_warn(afe->dev,
|
||||
"%s(), freq(%d Hz) invalid\n", __func__, freq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (apll_rate % freq != 0) {
|
||||
dev_warn(afe->dev,
|
||||
"%s(), APLL cannot generate %d Hz", __func__, freq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tdm_priv->mclk_rate = freq;
|
||||
tdm_priv->mclk_apll = apll;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_dai_tdm_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 mt8183_afe_private *afe_priv = afe->platform_priv;
|
||||
int tdm_id = dai->id;
|
||||
struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[tdm_id];
|
||||
unsigned int rate = params_rate(params);
|
||||
unsigned int channels = params_channels(params);
|
||||
snd_pcm_format_t format = params_format(params);
|
||||
unsigned int tdm_con = 0;
|
||||
|
||||
/* calculate mclk_rate, if not set explicitly */
|
||||
if (!tdm_priv->mclk_rate) {
|
||||
tdm_priv->mclk_rate = rate * tdm_priv->mclk_multiple;
|
||||
mtk_dai_tdm_cal_mclk(afe,
|
||||
tdm_priv,
|
||||
tdm_priv->mclk_rate);
|
||||
}
|
||||
|
||||
/* calculate bck */
|
||||
tdm_priv->bck_rate = rate *
|
||||
channels *
|
||||
snd_pcm_format_physical_width(format);
|
||||
|
||||
if (tdm_priv->bck_rate > tdm_priv->mclk_rate)
|
||||
dev_warn(afe->dev, "%s(), bck_rate > mclk_rate rate", __func__);
|
||||
|
||||
if (tdm_priv->mclk_rate % tdm_priv->bck_rate != 0)
|
||||
dev_warn(afe->dev, "%s(), bck cannot generate", __func__);
|
||||
|
||||
dev_info(afe->dev, "%s(), id %d, rate %d, channels %d, format %d, mclk_rate %d, bck_rate %d\n",
|
||||
__func__,
|
||||
tdm_id, rate, channels, format,
|
||||
tdm_priv->mclk_rate, tdm_priv->bck_rate);
|
||||
|
||||
/* set tdm */
|
||||
tdm_con = 1 << BCK_INVERSE_SFT;
|
||||
tdm_con |= 1 << LRCK_INVERSE_SFT;
|
||||
tdm_con |= 1 << DELAY_DATA_SFT;
|
||||
tdm_con |= 1 << LEFT_ALIGN_SFT;
|
||||
tdm_con |= get_tdm_wlen(format) << WLEN_SFT;
|
||||
tdm_con |= get_tdm_ch(channels) << CHANNEL_NUM_SFT;
|
||||
tdm_con |= get_tdm_channel_bck(format) << CHANNEL_BCK_CYCLES_SFT;
|
||||
tdm_con |= get_tdm_lrck_width(format) << LRCK_TDM_WIDTH_SFT;
|
||||
regmap_write(afe->regmap, AFE_TDM_CON1, tdm_con);
|
||||
|
||||
switch (channels) {
|
||||
case 1:
|
||||
case 2:
|
||||
tdm_con = TDM_CH_START_O30_O31 << ST_CH_PAIR_SOUT0_SFT;
|
||||
tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT1_SFT;
|
||||
tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT2_SFT;
|
||||
tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT3_SFT;
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
tdm_con = TDM_CH_START_O30_O31 << ST_CH_PAIR_SOUT0_SFT;
|
||||
tdm_con |= TDM_CH_START_O32_O33 << ST_CH_PAIR_SOUT1_SFT;
|
||||
tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT2_SFT;
|
||||
tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT3_SFT;
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
tdm_con = TDM_CH_START_O30_O31 << ST_CH_PAIR_SOUT0_SFT;
|
||||
tdm_con |= TDM_CH_START_O32_O33 << ST_CH_PAIR_SOUT1_SFT;
|
||||
tdm_con |= TDM_CH_START_O34_O35 << ST_CH_PAIR_SOUT2_SFT;
|
||||
tdm_con |= TDM_CH_ZERO << ST_CH_PAIR_SOUT3_SFT;
|
||||
break;
|
||||
case 7:
|
||||
case 8:
|
||||
tdm_con = TDM_CH_START_O30_O31 << ST_CH_PAIR_SOUT0_SFT;
|
||||
tdm_con |= TDM_CH_START_O32_O33 << ST_CH_PAIR_SOUT1_SFT;
|
||||
tdm_con |= TDM_CH_START_O34_O35 << ST_CH_PAIR_SOUT2_SFT;
|
||||
tdm_con |= TDM_CH_START_O36_O37 << ST_CH_PAIR_SOUT3_SFT;
|
||||
break;
|
||||
default:
|
||||
tdm_con = 0;
|
||||
}
|
||||
regmap_write(afe->regmap, AFE_TDM_CON2, tdm_con);
|
||||
|
||||
regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0,
|
||||
AFE_HDMI_OUT_CH_NUM_MASK_SFT,
|
||||
channels << AFE_HDMI_OUT_CH_NUM_SFT);
|
||||
|
||||
regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0,
|
||||
AFE_HDMI_OUT_BIT_WIDTH_MASK_SFT,
|
||||
get_hdmi_wlen(format) << AFE_HDMI_OUT_BIT_WIDTH_SFT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_dai_tdm_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
/* enable Out control */
|
||||
regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0,
|
||||
AFE_HDMI_OUT_ON_MASK_SFT,
|
||||
0x1 << AFE_HDMI_OUT_ON_SFT);
|
||||
/* enable tdm */
|
||||
regmap_update_bits(afe->regmap, AFE_TDM_CON1,
|
||||
TDM_EN_MASK_SFT, 0x1 << TDM_EN_SFT);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
/* disable tdm */
|
||||
regmap_update_bits(afe->regmap, AFE_TDM_CON1,
|
||||
TDM_EN_MASK_SFT, 0);
|
||||
/* disable Out control */
|
||||
regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0,
|
||||
AFE_HDMI_OUT_ON_MASK_SFT,
|
||||
0);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_dai_tdm_set_sysclk(struct snd_soc_dai *dai,
|
||||
int clk_id, unsigned int freq, int dir)
|
||||
{
|
||||
struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
|
||||
struct mt8183_afe_private *afe_priv = afe->platform_priv;
|
||||
struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai->id];
|
||||
|
||||
if (!tdm_priv) {
|
||||
dev_warn(afe->dev, "%s(), tdm_priv == NULL", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (dir != SND_SOC_CLOCK_OUT) {
|
||||
dev_warn(afe->dev, "%s(), dir != SND_SOC_CLOCK_OUT", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_info(afe->dev, "%s(), freq %d\n", __func__, freq);
|
||||
|
||||
return mtk_dai_tdm_cal_mclk(afe, tdm_priv, freq);
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops mtk_dai_tdm_ops = {
|
||||
.hw_params = mtk_dai_tdm_hw_params,
|
||||
.trigger = mtk_dai_tdm_trigger,
|
||||
.set_sysclk = mtk_dai_tdm_set_sysclk,
|
||||
};
|
||||
|
||||
/* dai driver */
|
||||
#define MTK_TDM_RATES (SNDRV_PCM_RATE_8000_48000 |\
|
||||
SNDRV_PCM_RATE_88200 |\
|
||||
SNDRV_PCM_RATE_96000 |\
|
||||
SNDRV_PCM_RATE_176400 |\
|
||||
SNDRV_PCM_RATE_192000)
|
||||
|
||||
#define MTK_TDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
|
||||
SNDRV_PCM_FMTBIT_S24_LE |\
|
||||
SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
static struct snd_soc_dai_driver mtk_dai_tdm_driver[] = {
|
||||
{
|
||||
.name = "TDM",
|
||||
.id = MT8183_DAI_TDM,
|
||||
.playback = {
|
||||
.stream_name = "TDM",
|
||||
.channels_min = 2,
|
||||
.channels_max = 8,
|
||||
.rates = MTK_TDM_RATES,
|
||||
.formats = MTK_TDM_FORMATS,
|
||||
},
|
||||
.ops = &mtk_dai_tdm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
int mt8183_dai_tdm_register(struct mtk_base_afe *afe)
|
||||
{
|
||||
struct mt8183_afe_private *afe_priv = afe->platform_priv;
|
||||
struct mtk_afe_tdm_priv *tdm_priv;
|
||||
struct mtk_base_afe_dai *dai;
|
||||
|
||||
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_tdm_driver;
|
||||
dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_tdm_driver);
|
||||
|
||||
dai->dapm_widgets = mtk_dai_tdm_widgets;
|
||||
dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_tdm_widgets);
|
||||
dai->dapm_routes = mtk_dai_tdm_routes;
|
||||
dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_tdm_routes);
|
||||
|
||||
tdm_priv = devm_kzalloc(afe->dev, sizeof(struct mtk_afe_tdm_priv),
|
||||
GFP_KERNEL);
|
||||
if (!tdm_priv)
|
||||
return -ENOMEM;
|
||||
|
||||
tdm_priv->mclk_multiple = 128;
|
||||
tdm_priv->bck_id = MT8183_I2S4_BCK;
|
||||
tdm_priv->mclk_id = MT8183_I2S4_MCK;
|
||||
|
||||
afe_priv->dai_priv[MT8183_DAI_TDM] = tdm_priv;
|
||||
return 0;
|
||||
}
|
33
sound/soc/mediatek/mt8183/mt8183-interconnection.h
Normal file
33
sound/soc/mediatek/mt8183/mt8183-interconnection.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Mediatek MT8183 audio driver interconnection definition
|
||||
*
|
||||
* Copyright (c) 2018 MediaTek Inc.
|
||||
* Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
|
||||
*/
|
||||
|
||||
#ifndef _MT8183_INTERCONNECTION_H_
|
||||
#define _MT8183_INTERCONNECTION_H_
|
||||
|
||||
#define I_I2S0_CH1 0
|
||||
#define I_I2S0_CH2 1
|
||||
#define I_ADDA_UL_CH1 3
|
||||
#define I_ADDA_UL_CH2 4
|
||||
#define I_DL1_CH1 5
|
||||
#define I_DL1_CH2 6
|
||||
#define I_DL2_CH1 7
|
||||
#define I_DL2_CH2 8
|
||||
#define I_PCM_1_CAP_CH1 9
|
||||
#define I_GAIN1_OUT_CH1 10
|
||||
#define I_GAIN1_OUT_CH2 11
|
||||
#define I_GAIN2_OUT_CH1 12
|
||||
#define I_GAIN2_OUT_CH2 13
|
||||
#define I_PCM_2_CAP_CH1 14
|
||||
#define I_PCM_2_CAP_CH2 21
|
||||
#define I_PCM_1_CAP_CH2 22
|
||||
#define I_DL3_CH1 23
|
||||
#define I_DL3_CH2 24
|
||||
#define I_I2S2_CH1 25
|
||||
#define I_I2S2_CH2 26
|
||||
|
||||
#endif
|
1666
sound/soc/mediatek/mt8183/mt8183-reg.h
Normal file
1666
sound/soc/mediatek/mt8183/mt8183-reg.h
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue