#define DRV_NAME "acp_i2s_playcap"
 
+static int acp_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
+                          unsigned int fmt)
+{
+       struct acp_dev_data *adata = snd_soc_dai_get_drvdata(cpu_dai);
+       int mode;
+
+       mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+       switch (mode) {
+       case SND_SOC_DAIFMT_I2S:
+               adata->tdm_mode = TDM_DISABLE;
+               break;
+       case SND_SOC_DAIFMT_DSP_A:
+               adata->tdm_mode = TDM_ENABLE;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int acp_i2s_set_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, u32 rx_mask,
+                               int slots, int slot_width)
+{
+       struct device *dev = dai->component->dev;
+       struct acp_dev_data *adata = snd_soc_dai_get_drvdata(dai);
+       struct acp_stream *stream;
+       int slot_len;
+
+       switch (slot_width) {
+       case SLOT_WIDTH_8:
+               slot_len = 8;
+               break;
+       case SLOT_WIDTH_16:
+               slot_len = 16;
+               break;
+       case SLOT_WIDTH_24:
+               slot_len = 24;
+               break;
+       case SLOT_WIDTH_32:
+               slot_len = 0;
+               break;
+       default:
+               dev_err(dev, "Unsupported bitdepth %d\n", slot_width);
+               return -EINVAL;
+       }
+
+       spin_lock_irq(&adata->acp_lock);
+       list_for_each_entry(stream, &adata->stream_list, list) {
+               if (tx_mask && stream->dir == SNDRV_PCM_STREAM_PLAYBACK)
+                       adata->tdm_tx_fmt[stream->dai_id - 1] =
+                                       FRM_LEN | (slots << 15) | (slot_len << 18);
+               else if (rx_mask && stream->dir == SNDRV_PCM_STREAM_CAPTURE)
+                       adata->tdm_rx_fmt[stream->dai_id - 1] =
+                                       FRM_LEN | (slots << 15) | (slot_len << 18);
+       }
+       spin_unlock_irq(&adata->acp_lock);
+       return 0;
+}
+
 static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params,
                            struct snd_soc_dai *dai)
 {
        struct acp_resource *rsrc;
        u32 val;
        u32 xfer_resolution;
-       u32 reg_val;
+       u32 reg_val, fmt_reg, tdm_fmt;
        u32 lrclk_div_val, bclk_div_val;
 
        adata = snd_soc_dai_get_drvdata(dai);
                switch (dai->driver->id) {
                case I2S_BT_INSTANCE:
                        reg_val = ACP_BTTDM_ITER;
+                       fmt_reg = ACP_BTTDM_TXFRMT;
                        break;
                case I2S_SP_INSTANCE:
                        reg_val = ACP_I2STDM_ITER;
+                       fmt_reg = ACP_I2STDM_TXFRMT;
                        break;
                case I2S_HS_INSTANCE:
                        reg_val = ACP_HSTDM_ITER;
+                       fmt_reg = ACP_HSTDM_TXFRMT;
                        break;
                default:
                        dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
                switch (dai->driver->id) {
                case I2S_BT_INSTANCE:
                        reg_val = ACP_BTTDM_IRER;
+                       fmt_reg = ACP_BTTDM_RXFRMT;
                        break;
                case I2S_SP_INSTANCE:
                        reg_val = ACP_I2STDM_IRER;
+                       fmt_reg = ACP_I2STDM_RXFRMT;
                        break;
                case I2S_HS_INSTANCE:
                        reg_val = ACP_HSTDM_IRER;
+                       fmt_reg = ACP_HSTDM_RXFRMT;
                        break;
                default:
                        dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
        val = val | (xfer_resolution  << 3);
        writel(val, adata->acp_base + reg_val);
 
+       if (adata->tdm_mode) {
+               val = readl(adata->acp_base + reg_val);
+               writel(val | BIT(1), adata->acp_base + reg_val);
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       tdm_fmt = adata->tdm_tx_fmt[dai->driver->id - 1];
+               else
+                       tdm_fmt = adata->tdm_rx_fmt[dai->driver->id - 1];
+               writel(tdm_fmt, adata->acp_base + fmt_reg);
+       }
+
        if (rsrc->soc_mclk) {
                switch (params_format(params)) {
                case SNDRV_PCM_FORMAT_S16_LE:
        stream->id = dai->driver->id + dir;
        stream->dai_id = dai->driver->id;
        stream->irq_bit = irq_bit;
+       stream->dir = substream->stream;
 
        return 0;
 }
        .hw_params = acp_i2s_hwparams,
        .prepare = acp_i2s_prepare,
        .trigger = acp_i2s_trigger,
+       .set_fmt = acp_i2s_set_fmt,
+       .set_tdm_slot = acp_i2s_set_tdm_slot,
 };
 EXPORT_SYMBOL_NS_GPL(asoc_acp_cpu_dai_ops, SND_SOC_ACP_COMMON);