ASoC: nau8825: Add TDM support
authorDavid Lin <CTLIN0@nuvoton.com>
Fri, 30 Sep 2022 07:28:05 +0000 (15:28 +0800)
committerMark Brown <broonie@kernel.org>
Fri, 30 Sep 2022 08:16:31 +0000 (09:16 +0100)
Support TDM format for NAU88L25.

Signed-off-by: David Lin <CTLIN0@nuvoton.com>
Link: https://lore.kernel.org/r/20220930072804.2524352-1-CTLIN0@nuvoton.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/codecs/nau8825.c
sound/soc/codecs/nau8825.h

index b3cdbe8..6a2c2e3 100644 (file)
@@ -1425,10 +1425,107 @@ static int nau8825_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
        return 0;
 }
 
+/**
+ * nau8825_set_tdm_slot - configure DAI TDM.
+ * @dai: DAI
+ * @tx_mask: bitmask representing active TX slots.
+ * @rx_mask: bitmask representing active RX slots.
+ * @slots: Number of slots in use.
+ * @slot_width: Width in bits for each slot.
+ *
+ * Configures a DAI for TDM operation. Support TDM 4/8 slots.
+ * The limitation is DAC and ADC need shift 4 slots at 8 slots mode.
+ */
+static int nau8825_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+                               unsigned int rx_mask, int slots, int slot_width)
+{
+       struct snd_soc_component *component = dai->component;
+       struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
+       unsigned int ctrl_val = 0, ctrl_offset = 0, value = 0, dac_s, adc_s;
+
+       if (slots != 4 && slots != 8) {
+               dev_err(nau8825->dev, "Only support 4 or 8 slots!\n");
+               return -EINVAL;
+       }
+
+       /* The driver is limited to 1-channel for ADC, and 2-channel for DAC on TDM mode */
+       if (hweight_long((unsigned long) tx_mask) != 1 ||
+           hweight_long((unsigned long) rx_mask) != 2) {
+               dev_err(nau8825->dev,
+                       "The limitation is 1-channel for ADC, and 2-channel for DAC on TDM mode.\n");
+               return -EINVAL;
+       }
+
+       if (((tx_mask & 0xf) && (tx_mask & 0xf0)) ||
+           ((rx_mask & 0xf) && (rx_mask & 0xf0)) ||
+           ((tx_mask & 0xf) && (rx_mask & 0xf0)) ||
+           ((rx_mask & 0xf) && (tx_mask & 0xf0))) {
+               dev_err(nau8825->dev,
+                       "Slot assignment of DAC and ADC need to set same interval.\n");
+               return -EINVAL;
+       }
+
+       /* The offset of fixed 4 slots for 8 slots support */
+       if (rx_mask & 0xf0) {
+               regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2,
+                                  NAU8825_I2S_PCM_TS_EN_MASK, NAU8825_I2S_PCM_TS_EN);
+               regmap_read(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL1, &value);
+               ctrl_val |= NAU8825_TDM_OFFSET_EN;
+               ctrl_offset = 4 * slot_width;
+               if (!(value & NAU8825_I2S_PCMB_MASK))
+                       ctrl_offset += 1;
+               dac_s = (rx_mask & 0xf0) >> 4;
+               adc_s = fls((tx_mask & 0xf0) >> 4);
+       } else {
+               dac_s = rx_mask & 0xf;
+               adc_s = fls(tx_mask & 0xf);
+       }
+
+       ctrl_val |= NAU8825_TDM_MODE;
+
+       switch (dac_s) {
+       case 0x3:
+               ctrl_val |= 1 << NAU8825_TDM_DACR_RX_SFT;
+               break;
+       case 0x5:
+               ctrl_val |= 2 << NAU8825_TDM_DACR_RX_SFT;
+               break;
+       case 0x6:
+               ctrl_val |= 1 << NAU8825_TDM_DACL_RX_SFT;
+               ctrl_val |= 2 << NAU8825_TDM_DACR_RX_SFT;
+               break;
+       case 0x9:
+               ctrl_val |= 3 << NAU8825_TDM_DACR_RX_SFT;
+               break;
+       case 0xa:
+               ctrl_val |= 1 << NAU8825_TDM_DACL_RX_SFT;
+               ctrl_val |= 3 << NAU8825_TDM_DACR_RX_SFT;
+               break;
+       case 0xc:
+               ctrl_val |= 2 << NAU8825_TDM_DACL_RX_SFT;
+               ctrl_val |= 3 << NAU8825_TDM_DACR_RX_SFT;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       ctrl_val |= adc_s - 1;
+
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_TDM_CTRL,
+                          NAU8825_TDM_MODE | NAU8825_TDM_OFFSET_EN |
+                          NAU8825_TDM_DACL_RX_MASK | NAU8825_TDM_DACR_RX_MASK |
+                          NAU8825_TDM_TX_MASK, ctrl_val);
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_LEFT_TIME_SLOT,
+                          NAU8825_TSLOT_L0_MASK, ctrl_offset);
+
+       return 0;
+}
+
 static const struct snd_soc_dai_ops nau8825_dai_ops = {
        .startup        = nau8825_dai_startup,
        .hw_params      = nau8825_hw_params,
        .set_fmt        = nau8825_set_dai_fmt,
+       .set_tdm_slot   = nau8825_set_tdm_slot,
 };
 
 #define NAU8825_RATES  SNDRV_PCM_RATE_8000_192000
index 6d112b6..d84191a 100644 (file)
 #define NAU8825_JKDET_PULL_EN  (1 << 9) /* 0 - enable pull, 1 - disable */
 #define NAU8825_JKDET_OUTPUT_EN        (1 << 8) /* 0 - enable input, 1 - enable output */
 
+/* TDM_CTRL (0x1b) */
+#define NAU8825_TDM_MODE               (0x1 << 15)
+#define NAU8825_TDM_OFFSET_EN          (0x1 << 14)
+#define NAU8825_TDM_DACL_RX_SFT                6
+#define NAU8825_TDM_DACL_RX_MASK       (0x3 << NAU8825_TDM_DACL_RX_SFT)
+#define NAU8825_TDM_DACR_RX_SFT                4
+#define NAU8825_TDM_DACR_RX_MASK       (0x3 << NAU8825_TDM_DACR_RX_SFT)
+#define NAU8825_TDM_TX_MASK            0x3
+
 /* I2S_PCM_CTRL1 (0x1c) */
 #define NAU8825_I2S_BP_SFT     7
 #define NAU8825_I2S_BP_MASK    (1 << NAU8825_I2S_BP_SFT)
 #define NAU8825_I2S_TRISTATE   (1 << 15) /* 0 - normal mode, 1 - Hi-Z output */
 #define NAU8825_I2S_LRC_DIV_SFT        12
 #define NAU8825_I2S_LRC_DIV_MASK       (0x3 << NAU8825_I2S_LRC_DIV_SFT)
+#define NAU8825_I2S_PCM_TS_EN_SFT      10
+#define NAU8825_I2S_PCM_TS_EN_MASK     (1 << NAU8825_I2S_PCM_TS_EN_SFT)
+#define NAU8825_I2S_PCM_TS_EN          (1 << NAU8825_I2S_PCM_TS_EN_SFT)
 #define NAU8825_I2S_MS_SFT     3
 #define NAU8825_I2S_MS_MASK    (1 << NAU8825_I2S_MS_SFT)
 #define NAU8825_I2S_MS_MASTER  (1 << NAU8825_I2S_MS_SFT)
 #define NAU8825_FS_ERR_CMP_SEL_SFT     14
 #define NAU8825_FS_ERR_CMP_SEL_MASK    (0x3 << NAU8825_FS_ERR_CMP_SEL_SFT)
 #define NAU8825_DIS_FS_SHORT_DET       (1 << 13)
+#define NAU8825_TSLOT_L0_MASK          0x3ff
+#define NAU8825_TSLOT_R0_MASK          0x3ff
 
 /* BIQ_CTRL (0x20) */
 #define NAU8825_BIQ_WRT_SFT   4