forked from Minki/linux
ASoC: mediatek: mt8183: tdm hw support tdm out and 8ch i2s out
This patch refined tdm driver code, and allow tdm hw to support two configurations in machine driver to output tdm signal or i2s signal. Signed-off-by: Jiaxin Yu <jiaxin.yu@mediatek.com> Link: https://lore.kernel.org/r/1566621445-26989-3-git-send-email-jiaxin.yu@mediatek.com Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
parent
376142b7bb
commit
8e58c521bc
@ -15,13 +15,30 @@
|
||||
struct mtk_afe_tdm_priv {
|
||||
int bck_id;
|
||||
int bck_rate;
|
||||
|
||||
int tdm_out_mode;
|
||||
int bck_invert;
|
||||
int lck_invert;
|
||||
int mclk_id;
|
||||
int mclk_multiple; /* according to sample rate */
|
||||
int mclk_rate;
|
||||
int mclk_apll;
|
||||
};
|
||||
|
||||
enum {
|
||||
TDM_OUT_I2S = 0,
|
||||
TDM_OUT_TDM = 1,
|
||||
};
|
||||
|
||||
enum {
|
||||
TDM_BCK_NON_INV = 0,
|
||||
TDM_BCK_INV = 1,
|
||||
};
|
||||
|
||||
enum {
|
||||
TDM_LCK_NON_INV = 0,
|
||||
TDM_LCK_INV = 1,
|
||||
};
|
||||
|
||||
enum {
|
||||
TDM_WLEN_16_BIT = 1,
|
||||
TDM_WLEN_32_BIT = 2,
|
||||
@ -93,6 +110,25 @@ static unsigned int get_tdm_ch(unsigned int ch)
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int get_tdm_ch_fixup(unsigned int channels)
|
||||
{
|
||||
if (channels > 4)
|
||||
return 8;
|
||||
else if (channels > 2)
|
||||
return 4;
|
||||
else
|
||||
return 2;
|
||||
}
|
||||
|
||||
static unsigned int get_tdm_ch_per_sdata(unsigned int mode,
|
||||
unsigned int channels)
|
||||
{
|
||||
if (mode == TDM_OUT_TDM)
|
||||
return get_tdm_ch_fixup(channels);
|
||||
else
|
||||
return 2;
|
||||
}
|
||||
|
||||
/* interconnection */
|
||||
enum {
|
||||
HDMI_CONN_CH0 = 0,
|
||||
@ -433,8 +469,11 @@ static int mtk_dai_tdm_hw_params(struct snd_pcm_substream *substream,
|
||||
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 tdm_out_mode = tdm_priv->tdm_out_mode;
|
||||
unsigned int rate = params_rate(params);
|
||||
unsigned int channels = params_channels(params);
|
||||
unsigned int out_channels_per_sdata =
|
||||
get_tdm_ch_per_sdata(tdm_out_mode, channels);
|
||||
snd_pcm_format_t format = params_format(params);
|
||||
unsigned int tdm_con = 0;
|
||||
|
||||
@ -448,7 +487,7 @@ static int mtk_dai_tdm_hw_params(struct snd_pcm_substream *substream,
|
||||
|
||||
/* calculate bck */
|
||||
tdm_priv->bck_rate = rate *
|
||||
channels *
|
||||
out_channels_per_sdata *
|
||||
snd_pcm_format_physical_width(format);
|
||||
|
||||
if (tdm_priv->bck_rate > tdm_priv->mclk_rate)
|
||||
@ -461,50 +500,70 @@ static int mtk_dai_tdm_hw_params(struct snd_pcm_substream *substream,
|
||||
__func__,
|
||||
tdm_id, rate, channels, format,
|
||||
tdm_priv->mclk_rate, tdm_priv->bck_rate);
|
||||
dev_info(afe->dev, "%s(), out_channels_per_sdata = %d\n",
|
||||
__func__, out_channels_per_sdata);
|
||||
|
||||
/* set tdm */
|
||||
tdm_con = 1 << BCK_INVERSE_SFT;
|
||||
tdm_con |= 1 << LRCK_INVERSE_SFT;
|
||||
tdm_con |= 1 << DELAY_DATA_SFT;
|
||||
if (tdm_priv->bck_invert)
|
||||
tdm_con |= 1 << BCK_INVERSE_SFT;
|
||||
|
||||
if (tdm_priv->lck_invert)
|
||||
tdm_con |= 1 << LRCK_INVERSE_SFT;
|
||||
|
||||
if (tdm_priv->tdm_out_mode == TDM_OUT_I2S) {
|
||||
tdm_con |= 1 << DELAY_DATA_SFT;
|
||||
tdm_con |= get_tdm_lrck_width(format) << LRCK_TDM_WIDTH_SFT;
|
||||
} else if (tdm_priv->tdm_out_mode == TDM_OUT_TDM) {
|
||||
tdm_con |= 0 << DELAY_DATA_SFT;
|
||||
tdm_con |= 0 << LRCK_TDM_WIDTH_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_ch(out_channels_per_sdata) << 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:
|
||||
if (out_channels_per_sdata == 2) {
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
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,
|
||||
@ -573,10 +632,58 @@ static int mtk_dai_tdm_set_sysclk(struct snd_soc_dai *dai,
|
||||
return mtk_dai_tdm_cal_mclk(afe, tdm_priv, freq);
|
||||
}
|
||||
|
||||
static int mtk_dai_tdm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/* DAI mode*/
|
||||
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
case SND_SOC_DAIFMT_I2S:
|
||||
tdm_priv->tdm_out_mode = TDM_OUT_I2S;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_DSP_A:
|
||||
tdm_priv->tdm_out_mode = TDM_OUT_TDM;
|
||||
break;
|
||||
default:
|
||||
tdm_priv->tdm_out_mode = TDM_OUT_I2S;
|
||||
}
|
||||
|
||||
/* DAI clock inversion*/
|
||||
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
case SND_SOC_DAIFMT_NB_NF:
|
||||
tdm_priv->bck_invert = TDM_BCK_NON_INV;
|
||||
tdm_priv->lck_invert = TDM_LCK_NON_INV;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_NB_IF:
|
||||
tdm_priv->bck_invert = TDM_BCK_NON_INV;
|
||||
tdm_priv->lck_invert = TDM_LCK_INV;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_NF:
|
||||
tdm_priv->bck_invert = TDM_BCK_INV;
|
||||
tdm_priv->lck_invert = TDM_LCK_NON_INV;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_IB_IF:
|
||||
default:
|
||||
tdm_priv->bck_invert = TDM_BCK_INV;
|
||||
tdm_priv->lck_invert = TDM_LCK_INV;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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,
|
||||
.set_fmt = mtk_dai_tdm_set_fmt,
|
||||
};
|
||||
|
||||
/* dai driver */
|
||||
|
@ -380,6 +380,9 @@ mt8183_mt6358_ts3a227_max98357_dai_links[] = {
|
||||
{
|
||||
.name = "TDM",
|
||||
.no_pcm = 1,
|
||||
.dai_fmt = SND_SOC_DAIFMT_I2S |
|
||||
SND_SOC_DAIFMT_IB_IF |
|
||||
SND_SOC_DAIFMT_CBM_CFM,
|
||||
.dpcm_playback = 1,
|
||||
.ignore_suspend = 1,
|
||||
.be_hw_params_fixup = mt8183_i2s_hw_params_fixup,
|
||||
|
Loading…
Reference in New Issue
Block a user