[PATCH] ASoC: nau8825: Add TDM support

David Lin CTLIN0 at nuvoton.com
Fri Sep 30 09:28:05 CEST 2022


Support TDM format for NAU88L25.

Signed-off-by: David Lin <CTLIN0 at nuvoton.com>
---
 sound/soc/codecs/nau8825.c | 97 ++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/nau8825.h | 14 ++++++
 2 files changed, 111 insertions(+)

diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c
index b3cdbe884c05..6a2c2e373efd 100644
--- a/sound/soc/codecs/nau8825.c
+++ b/sound/soc/codecs/nau8825.c
@@ -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
diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h
index 6d112b6145df..d84191a7beb2 100644
--- a/sound/soc/codecs/nau8825.h
+++ b/sound/soc/codecs/nau8825.h
@@ -225,6 +225,15 @@
 #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)
@@ -249,6 +258,9 @@
 #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)
@@ -259,6 +271,8 @@
 #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
-- 
2.25.1



More information about the Alsa-devel mailing list