]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - sound/soc/mediatek/mt8183/mt8183-dai-tdm.c
ASoC: mediatek: mt8183: tdm hw support tdm out and 8ch i2s out
[linux.git] / sound / soc / mediatek / mt8183 / mt8183-dai-tdm.c
index 8983d54a9b67a9afe160ac291b6f6abe3379db9a..d34cabdbf8899b0bbe6583aaa0e91d5b9a47bf72 100644 (file)
 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 */