ASoC: amd: acp: Add TDM support for acp i2s stream
authorVenkata Prasad Potturu <venkataprasad.potturu@amd.com>
Wed, 10 Aug 2022 13:29:13 +0000 (18:59 +0530)
committerMark Brown <broonie@kernel.org>
Mon, 15 Aug 2022 00:19:42 +0000 (01:19 +0100)
Add callback and code changes to enable ACP I2S controller in TDM
mode. Add new fields in acp_stream and acp_dev_data struct to configure
tdm related registers for ACP i2s controllers.

Signed-off-by: Venkata Prasad Potturu <venkataprasad.potturu@amd.com>
Link: https://lore.kernel.org/r/20220810132913.1181247-3-venkataprasad.potturu@amd.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/amd/acp/acp-i2s.c
sound/soc/amd/acp/amd.h

index 393f729..ac41657 100644 (file)
 
 #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)
 {
@@ -33,7 +92,7 @@ static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_
        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);
@@ -62,12 +121,15 @@ static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_
                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);
@@ -77,12 +139,15 @@ static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_
                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);
@@ -95,6 +160,16 @@ static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_
        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:
@@ -443,6 +518,7 @@ static int acp_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_d
        stream->id = dai->driver->id + dir;
        stream->dai_id = dai->driver->id;
        stream->irq_bit = irq_bit;
+       stream->dir = substream->stream;
 
        return 0;
 }
@@ -452,6 +528,8 @@ const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops = {
        .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);
 
index e49269f..afa4e25 100644 (file)
 
 #define ACP_MAX_STREAM                 8
 
+#define TDM_ENABLE     1
+#define TDM_DISABLE    0
+
+#define SLOT_WIDTH_8   0x8
+#define SLOT_WIDTH_16  0x10
+#define SLOT_WIDTH_24  0x18
+#define SLOT_WIDTH_32  0x20
+
 struct acp_chip_info {
        char *name;             /* Platform name */
        unsigned int acp_rev;   /* ACP Revision id */
@@ -96,6 +104,7 @@ struct acp_stream {
        int irq_bit;
        int dai_id;
        int id;
+       int dir;
        u64 bytescount;
        u32 reg_offset;
        u32 pte_offset;
@@ -120,6 +129,7 @@ struct acp_dev_data {
        void __iomem *acp_base;
        unsigned int i2s_irq;
 
+       bool tdm_mode;
        /* SOC specific dais */
        struct snd_soc_dai_driver *dai_driver;
        int num_dai;
@@ -134,6 +144,8 @@ struct acp_dev_data {
        u32 lrclk_div;
 
        struct acp_resource *rsrc;
+       u32 tdm_tx_fmt[3];
+       u32 tdm_rx_fmt[3];
 };
 
 union acp_i2stdm_mstrclkgen {