ASoC: nau8825: Add TDM support

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>
This commit is contained in:
David Lin 2022-09-30 15:28:05 +08:00 committed by Mark Brown
parent d488b28502
commit dacdef1bd2
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
2 changed files with 111 additions and 0 deletions

View 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

View File

@ -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