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:
Jiaxin Yu 2019-08-24 12:37:24 +08:00 committed by Mark Brown
parent 376142b7bb
commit 8e58c521bc
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
2 changed files with 144 additions and 34 deletions

View File

@ -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 */

View File

@ -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,