[alsa-devel] [patch] tlv320aic3x: disable ADC/DAC while changing clock

Daniel Glöckner dg at emlix.com
Thu Mar 26 14:21:46 CET 2009


While using a tlv320aic3101 as a clock master, it often happened that the
clock lines got stuck while changing the sampling rate. In a discussion
with TI technical support it was suggested to shut down the ADC and DAC
while there are changes being made to the clock.

Following that rule resolves the problem, but merely writing the
registers to switch the ADC/DAC off is not enough. One needs to poll the
power status registers to wait until they have completely shut down.

The aforementioned rule implies that we can not disable the PLL in
stand-by bias level.

Signed-off-by: Daniel Glöckner <dg at emlix.com>
---
 sound/soc/codecs/tlv320aic3x.c |   89 ++++++++++++++++++++++++++++------------
 sound/soc/codecs/tlv320aic3x.h |    8 ++++
 2 files changed, 71 insertions(+), 26 deletions(-)

diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index aea0cb7..4c44022 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -740,6 +740,57 @@ static int aic3x_add_widgets(struct snd_soc_codec *codec)
 	return 0;
 }
 
+static u8 aic3x_power_codec(struct snd_soc_codec *codec, u8 new)
+{
+	u8 val, old;
+
+	val = aic3x_read_reg_cache(codec, LINE1L_2_LADC_CTRL);
+	old = (val & LADC_PWR_ON) ? 1 : 0;
+	if ((old ^ new) & 1) {
+		val ^= LADC_PWR_ON;
+		aic3x_write(codec, LINE1L_2_LADC_CTRL, val);
+	}
+
+	val = aic3x_read_reg_cache(codec, LINE1R_2_RADC_CTRL);
+	old += (val & RADC_PWR_ON) ? 2 : 0;
+	if ((old ^ new) & 2) {
+		val ^= RADC_PWR_ON;
+		aic3x_write(codec, LINE1R_2_RADC_CTRL, val);
+	}
+
+	val = aic3x_read_reg_cache(codec, DAC_PWR);
+	old += (val & LDAC_PWR_ON) ? 4 : 0;
+	old += (val & RDAC_PWR_ON) ? 8 : 0;
+	if ((old ^ new) & 4)
+		val ^= LDAC_PWR_ON;
+	if ((old ^ new) & 8)
+		val ^= RDAC_PWR_ON;
+	if ((old ^ new) & 12)
+		aic3x_write(codec, DAC_PWR, val);
+
+	return old;
+}
+
+static u8 aic3x_power_off_wait(struct snd_soc_codec *codec)
+{
+	int timeout = 1000;
+	u8 old, data;
+	old = aic3x_power_codec(codec, 0);
+
+	do {
+		aic3x_read(codec, ADC_FLAGS, &data);
+	} while ((data & (LADC_PWR_STATUS | RADC_PWR_STATUS)) && --timeout > 0);
+
+	do {
+		aic3x_read(codec, MODULE_PWR_STATUS, &data);
+	} while ((data & (LDAC_PWR_STATUS | RDAC_PWR_STATUS)) && --timeout > 0);
+
+	if (timeout < 0)
+		printk(KERN_WARNING "aic3x: timeout waiting for ADC/DAC to shut down");
+
+	return old;
+}
+
 static int aic3x_hw_params(struct snd_pcm_substream *substream,
 			   struct snd_pcm_hw_params *params,
 			   struct snd_soc_dai *dai)
@@ -749,9 +800,11 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
 	struct snd_soc_codec *codec = socdev->codec;
 	struct aic3x_priv *aic3x = codec->private_data;
 	int codec_clk = 0, bypass_pll = 0, fsref, last_clk = 0;
-	u8 data, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1;
+	u8 data, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1, state_saved;
 	u16 pll_d = 1;
 
+	state_saved = aic3x_power_off_wait(codec);
+
 	/* select data word length */
 	data =
 	    aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLB) & (~(0x3 << 4));
@@ -805,8 +858,10 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
 	data |= (data << 4);
 	aic3x_write(codec, AIC3X_SAMPLE_RATE_SEL_REG, data);
 
-	if (bypass_pll)
+	if (bypass_pll) {
+		aic3x_power_codec(codec, state_saved);
 		return 0;
+	}
 
 	/* Use PLL
 	 * find an apropriate setup for j, d, r and p by iterating over
@@ -847,6 +902,7 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
 
 	if (last_clk == 0) {
 		printk(KERN_ERR "%s(): unable to setup PLL\n", __func__);
+		aic3x_power_codec(codec, state_saved);
 		return -EINVAL;
 	}
 
@@ -857,7 +913,11 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
 	aic3x_write(codec, AIC3X_PLL_PROGC_REG, (pll_d >> 6) << PLLD_MSB_SHIFT);
 	aic3x_write(codec, AIC3X_PLL_PROGD_REG,
 		    (pll_d & 0x3F) << PLLD_LSB_SHIFT);
+	data = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
+	aic3x_write(codec, AIC3X_PLL_PROGA_REG, data | (pll_p << PLLP_SHIFT)
+						     | PLL_ENABLE);
 
+	aic3x_power_codec(codec, state_saved);
 	return 0;
 }
 
@@ -951,37 +1011,14 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec,
 
 	switch (level) {
 	case SND_SOC_BIAS_ON:
-		/* all power is driven by DAPM system */
-		if (aic3x->master) {
-			/* enable pll */
-			reg = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
-			aic3x_write(codec, AIC3X_PLL_PROGA_REG,
-				    reg | PLL_ENABLE);
-		}
 		break;
 	case SND_SOC_BIAS_PREPARE:
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		/*
-		 * all power is driven by DAPM system,
-		 * so output power is safe if bypass was set
-		 */
-		if (aic3x->master) {
-			/* disable pll */
-			reg = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
-			aic3x_write(codec, AIC3X_PLL_PROGA_REG,
-				    reg & ~PLL_ENABLE);
-		}
 		break;
 	case SND_SOC_BIAS_OFF:
 		/* force all power off */
-		reg = aic3x_read_reg_cache(codec, LINE1L_2_LADC_CTRL);
-		aic3x_write(codec, LINE1L_2_LADC_CTRL, reg & ~LADC_PWR_ON);
-		reg = aic3x_read_reg_cache(codec, LINE1R_2_RADC_CTRL);
-		aic3x_write(codec, LINE1R_2_RADC_CTRL, reg & ~RADC_PWR_ON);
-
-		reg = aic3x_read_reg_cache(codec, DAC_PWR);
-		aic3x_write(codec, DAC_PWR, reg & ~(LDAC_PWR_ON | RDAC_PWR_ON));
+		aic3x_power_off_wait(codec);
 
 		reg = aic3x_read_reg_cache(codec, HPLOUT_CTRL);
 		aic3x_write(codec, HPLOUT_CTRL, reg & ~HPLOUT_PWR_ON);
diff --git a/sound/soc/codecs/tlv320aic3x.h b/sound/soc/codecs/tlv320aic3x.h
index ac827e5..0b4fedf 100644
--- a/sound/soc/codecs/tlv320aic3x.h
+++ b/sound/soc/codecs/tlv320aic3x.h
@@ -69,6 +69,8 @@
 #define RAGC_CTRL_B			30
 #define RAGC_CTRL_C			31
 
+/* ADC Flag register */
+#define ADC_FLAGS			34
 /* DAC Power and Left High Power Output control registers */
 #define DAC_PWR				37
 #define HPLCOM_CFG			37
@@ -126,6 +128,8 @@
 #define DACR1_2_LLOPM_VOL		85
 #define LLOPM_CTRL			86
 #define RLOPM_CTRL			93
+/* Module Power status register */
+#define MODULE_PWR_STATUS		94
 /* GPIO/IRQ registers */
 #define AIC3X_STICKY_IRQ_FLAGS_REG	96
 #define AIC3X_RT_IRQ_FLAGS_REG		97
@@ -191,6 +195,10 @@
 #define MONOLOPM_PWR_ON		0x01
 #define LLOPM_PWR_ON		0x01
 #define RLOPM_PWR_ON	0x01
+#define LADC_PWR_STATUS		0x40
+#define RADC_PWR_STATUS		0x04
+#define LDAC_PWR_STATUS		0x80
+#define RDAC_PWR_STATUS		0x40
 
 #define INVERT_VOL(val)   (0x7f - val)
 
-- 
1.6.2.107.ge47ee



More information about the Alsa-devel mailing list