Unverified Commit dacdef1b authored by David Lin's avatar David Lin Committed by Mark Brown
Browse files

ASoC: nau8825: Add TDM support

parent d488b285
Loading
Loading
Loading
Loading
+97 −0
Original line number Diff line number Diff line
@@ -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
+14 −0
Original line number Diff line number Diff line
@@ -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