ASoC: tas2552: Add TDM support
authorPeter Ujfalusi <peter.ujfalusi@ti.com>
Thu, 4 Jun 2015 13:04:25 +0000 (16:04 +0300)
committerMark Brown <broonie@kernel.org>
Fri, 5 Jun 2015 17:53:34 +0000 (18:53 +0100)
TDM support is achieved using DSP transfer mode and setting a programmable
offset which specifies where data begins with respect to the frame sync.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/codecs/tas2552.c
sound/soc/codecs/tas2552.h

index 7615d1bc5f5dc362059f1b1730c340723e464ccb..432aa54fe707ae722d2afd075d23d605f37f22ee 100644 (file)
@@ -78,6 +78,9 @@ struct tas2552_data {
        unsigned char regs[TAS2552_VBAT_DATA];
        unsigned int pll_clkin;
        unsigned int pdm_clk;
+
+       unsigned int dai_fmt;
+       unsigned int tdm_delay;
 };
 
 /* Input mux controls */
@@ -191,10 +194,29 @@ static int tas2552_hw_params(struct snd_pcm_substream *substream,
 #define TAS2552_DAI_FMT_MASK   (TAS2552_BCLKDIR | \
                                 TAS2552_WCLKDIR | \
                                 TAS2552_DATAFORMAT_MASK)
+static int tas2552_prepare(struct snd_pcm_substream *substream,
+                          struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
+       int delay = 0;
+
+       /* TDM slot selection only valid in DSP_A/_B mode */
+       if (tas2552->dai_fmt == SND_SOC_DAIFMT_DSP_A)
+               delay += (tas2552->tdm_delay + 1);
+       else if (tas2552->dai_fmt == SND_SOC_DAIFMT_DSP_B)
+               delay += tas2552->tdm_delay;
+
+       /* Configure data delay */
+       snd_soc_write(codec, TAS2552_SER_CTRL_2, delay);
+
+       return 0;
+}
+
 static int tas2552_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 {
        struct snd_soc_codec *codec = dai->codec;
-       u8 delay = 0;
+       struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev);
        u8 serial_format;
 
        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
@@ -220,7 +242,6 @@ static int tas2552_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
        case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF):
                break;
        case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF):
-               delay = 1;
        case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF):
                serial_format |= TAS2552_DATAFORMAT_DSP;
                break;
@@ -234,11 +255,10 @@ static int tas2552_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
                dev_vdbg(codec->dev, "DAI Format is not found\n");
                return -EINVAL;
        }
+       tas2552->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
 
        snd_soc_update_bits(codec, TAS2552_SER_CTRL_1, TAS2552_DAI_FMT_MASK,
                            serial_format);
-       snd_soc_write(codec, TAS2552_SER_CTRL_2, delay);
-
        return 0;
 }
 
@@ -278,6 +298,35 @@ static int tas2552_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
        return 0;
 }
 
+static int tas2552_set_dai_tdm_slot(struct snd_soc_dai *dai,
+                                   unsigned int tx_mask, unsigned int rx_mask,
+                                   int slots, int slot_width)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
+       unsigned int lsb;
+
+       if (unlikely(!tx_mask)) {
+               dev_err(codec->dev, "tx masks need to be non 0\n");
+               return -EINVAL;
+       }
+
+       /* TDM based on DSP mode requires slots to be adjacent */
+       lsb = __ffs(tx_mask);
+       if ((lsb + 1) != __fls(tx_mask)) {
+               dev_err(codec->dev, "Invalid mask, slots must be adjacent\n");
+               return -EINVAL;
+       }
+
+       tas2552->tdm_delay = lsb * slot_width;
+
+       /* DOUT in high-impedance on inactive bit clocks */
+       snd_soc_update_bits(codec, TAS2552_DOUT,
+                           TAS2552_SDOUT_TRISTATE, TAS2552_SDOUT_TRISTATE);
+
+       return 0;
+}
+
 static int tas2552_mute(struct snd_soc_dai *dai, int mute)
 {
        u8 cfg1_reg = 0;
@@ -330,8 +379,10 @@ static const struct dev_pm_ops tas2552_pm = {
 
 static struct snd_soc_dai_ops tas2552_speaker_dai_ops = {
        .hw_params      = tas2552_hw_params,
+       .prepare        = tas2552_prepare,
        .set_sysclk     = tas2552_set_dai_sysclk,
        .set_fmt        = tas2552_set_dai_fmt,
+       .set_tdm_slot   = tas2552_set_dai_tdm_slot,
        .digital_mute = tas2552_mute,
 };
 
index 0a12b511e9515a8dcc627000dedb2ad6e96b8ee9..5bdc7eaaddea92b49969fca847473bb022ccb2c3 100644 (file)
@@ -62,6 +62,9 @@
 #define TAS2552_LIM_EN                 (1 << 2)
 #define TAS2552_IVSENSE_EN             (1 << 1)
 
+/* DOUT Register Masks */
+#define TAS2552_SDOUT_TRISTATE         (1 << 2)
+
 /* Serial Interface Control Register Masks */
 #define TAS2552_DATAFORMAT_I2S         (0x0 << 2)
 #define TAS2552_DATAFORMAT_DSP         (0x1 << 2)