// 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/bitops.h>
#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"
enum {
I2S_FMT_EIAJ = 0,
I2S_FMT_I2S = 1,
};
enum {
I2S_WLEN_16_BIT = 0,
I2S_WLEN_32_BIT = 1,
};
enum {
I2S_HD_NORMAL = 0,
I2S_HD_LOW_JITTER = 1,
};
enum {
I2S1_SEL_O28_O29 = 0,
I2S1_SEL_O03_O04 = 1,
};
enum {
I2S_IN_PAD_CONNSYS = 0,
I2S_IN_PAD_IO_MUX = 1,
};
struct mtk_afe_i2s_priv {
int id;
int rate; /* for determine which apll to use */
int low_jitter_en;
int share_i2s_id;
int mclk_id;
int mclk_rate;
int mclk_apll;
int use_eiaj;
};
static unsigned int get_i2s_wlen(snd_pcm_format_t format)
{
return snd_pcm_format_physical_width(format) <= 16 ?
I2S_WLEN_16_BIT : I2S_WLEN_32_BIT;
}
#define MTK_AFE_I2S0_KCONTROL_NAME "I2S0_HD_Mux"
#define MTK_AFE_I2S1_KCONTROL_NAME "I2S1_HD_Mux"
#define MTK_AFE_I2S2_KCONTROL_NAME "I2S2_HD_Mux"
#define MTK_AFE_I2S3_KCONTROL_NAME "I2S3_HD_Mux"
#define MTK_AFE_I2S5_KCONTROL_NAME "I2S5_HD_Mux"
#define I2S0_HD_EN_W_NAME "I2S0_HD_EN"
#define I2S1_HD_EN_W_NAME "I2S1_HD_EN"
#define I2S2_HD_EN_W_NAME "I2S2_HD_EN"
#define I2S3_HD_EN_W_NAME "I2S3_HD_EN"
#define I2S5_HD_EN_W_NAME "I2S5_HD_EN"
#define I2S0_MCLK_EN_W_NAME "I2S0_MCLK_EN"
#define I2S1_MCLK_EN_W_NAME "I2S1_MCLK_EN"
#define I2S2_MCLK_EN_W_NAME "I2S2_MCLK_EN"
#define I2S3_MCLK_EN_W_NAME "I2S3_MCLK_EN"
#define I2S5_MCLK_EN_W_NAME "I2S5_MCLK_EN"
static int get_i2s_id_by_name(struct mtk_base_afe *afe,
const char *name)
{
if (strncmp(name, "I2S0", 4) == 0)
return MT8183_DAI_I2S_0;
else if (strncmp(name, "I2S1", 4) == 0)
return MT8183_DAI_I2S_1;
else if (strncmp(name, "I2S2", 4) == 0)
return MT8183_DAI_I2S_2;
else if (strncmp(name, "I2S3", 4) == 0)
return MT8183_DAI_I2S_3;
else if (strncmp(name, "I2S5", 4) == 0)
return MT8183_DAI_I2S_5;
else
return -EINVAL;
}
static struct mtk_afe_i2s_priv *get_i2s_priv_by_name(struct mtk_base_afe *afe,
const char *name)
{
struct mt8183_afe_private *afe_priv = afe->platform_priv;
int dai_id = get_i2s_id_by_name(afe, name);
if (dai_id < 0)
return NULL;
return afe_priv->dai_priv[dai_id];
}
/* low jitter control */
static const char * const mt8183_i2s_hd_str[] = {
"Normal", "Low_Jitter"
};
static const struct soc_enum mt8183_i2s_enum[] = {
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(mt8183_i2s_hd_str),
mt8183_i2s_hd_str),
};
static int mt8183_i2s_hd_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 mtk_afe_i2s_priv *i2s_priv;
i2s_priv = get_i2s_priv_by_name(afe, kcontrol->id.name);
if (!i2s_priv) {
dev_warn(afe->dev, "%s(), i2s_priv == NULL", __func__);
return -EINVAL;
}
ucontrol->value.integer.value[0] = i2s_priv->low_jitter_en;
return 0;
}
static int mt8183_i2s_hd_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 mtk_afe_i2s_priv *i2s_priv;
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
int hd_en, change;
if (ucontrol->value.enumerated.item[0] >= e->items)
return -EINVAL;
hd_en = ucontrol->value.integer.value[0];
i2s_priv = get_i2s_priv_by_name(afe, kcontrol->id.name);
if (!i2s_priv) {
dev_warn(afe->dev, "%s(), i2s_priv == NULL", __func__);
return -EINVAL;
}
change = i2s_priv->low_jitter_en != hd_en;
i2s_priv->low_jitter_en = hd_en;
return change;
}
static const struct snd_kcontrol_new mtk_dai_i2s_controls[] = {
SOC_ENUM_EXT(MTK_AFE_I2S0_KCONTROL_NAME, mt8183_i2s_enum[0],
mt8183_i2s_hd_get, mt8183_i2s_hd_set),
SOC_ENUM_EXT(MTK_AFE_I2S1_KCONTROL_NAME, mt8183_i2s_enum[0],
mt8183_i2s_hd_get, mt8183_i2s_hd_set),
SOC_ENUM_EXT(MTK_AFE_I2S2_KCONTROL_NAME, mt8183_i2s_enum[0],
mt8183_i2s_hd_get, mt8183_i2s_hd_set),
SOC_ENUM_EXT(MTK_AFE_I2S3_KCONTROL_NAME, mt8183_i2s_enum[0],
mt8183_i2s_hd_get, mt8183_i2s_hd_set),
SOC_ENUM_EXT(MTK_AFE_I2S5_KCONTROL_NAME, mt8183_i2s_enum[0],
mt8183_i2s_hd_get, mt8183_i2s_hd_set),
};
/* dai component */
/* interconnection */
static const struct snd_kcontrol_new mtk_i2s3_ch1_mix[] = {
SOC_DAPM_SINGLE_AUTODISABLE("DL1_CH1", AFE_CONN0, I_DL1_CH1, 1, 0),
SOC_DAPM_SINGLE_AUTODISABLE("DL2_CH1",<