From f4d593280652025f797d432e90f49d49b9334689 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 23 Apr 2010 10:09:57 +0300 Subject: [PATCH 1/9] ASoC: tlv320dac33: Fix for early interrupt in FIFO Mode1 Alarm threshold interrupt is triggered right after the playback start. This interrupt is recieved during the first burst period, and caused the state machine to write additional nSample command, which has to be avoided. To fix this issue move the DAC33 interrupt unmasking after we configured the PREFILL register with a small delay. Signed-off-by: Peter Ujfalusi Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- sound/soc/codecs/tlv320dac33.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index 824bb354ebc9..520377bdb61c 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -557,9 +557,13 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) switch (dac33->fifo_mode) { case DAC33_FIFO_MODE1: dac33_write16(codec, DAC33_NSAMPLE_MSB, - DAC33_THRREG(dac33->nsample)); + DAC33_THRREG(dac33->nsample + dac33->alarm_threshold)); dac33_write16(codec, DAC33_PREFILL_MSB, DAC33_THRREG(dac33->alarm_threshold)); + /* Enable Alarm Threshold IRQ with a delay */ + udelay(SAMPLES_TO_US(dac33->burst_rate, + dac33->alarm_threshold)); + dac33_write(codec, DAC33_FIFO_IRQ_MASK, DAC33_MAT); break; case DAC33_FIFO_MODE7: dac33_write16(codec, DAC33_PREFILL_MSB, @@ -782,7 +786,6 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream) case DAC33_FIFO_MODE1: dac33_write(codec, DAC33_FIFO_IRQ_MODE_B, DAC33_ATM(DAC33_FIFO_IRQ_MODE_LEVEL)); - dac33_write(codec, DAC33_FIFO_IRQ_MASK, DAC33_MAT); break; case DAC33_FIFO_MODE7: /* Disable all interrupts */ From 55abb59c9a03d3aceda61e97b5ff19d9274fc611 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 23 Apr 2010 10:09:58 +0300 Subject: [PATCH 2/9] ASoC: tlv320dac33: Skip calculations in FIFO Bypass mode There is no need for calculations for FIFO bypass mode. Just in case set the nsample maximum limit, which has been done in the calculation phase. Signed-off-by: Peter Ujfalusi Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- sound/soc/codecs/tlv320dac33.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index 520377bdb61c..b101cbd95740 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -889,6 +889,10 @@ static void dac33_calculate_times(struct snd_pcm_substream *substream) struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); unsigned int nsample_limit; + /* In bypass mode we don't need to calculate */ + if (!dac33->fifo_mode) + return; + /* Number of samples (16bit, stereo) in one period */ dac33->nsample_min = snd_pcm_lib_period_bytes(substream) / 4; @@ -1244,6 +1248,7 @@ static int __devinit dac33_i2c_probe(struct i2c_client *client, dac33->keep_bclk = pdata->keep_bclk; dac33->irq = client->irq; dac33->nsample = NSAMPLE_MAX; + dac33->nsample_max = NSAMPLE_MAX; /* Disable FIFO use by default */ dac33->fifo_mode = DAC33_FIFO_BYPASS; From 4260393e71f1e99c2f4fcde10fcb98fe00188d21 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 23 Apr 2010 10:09:59 +0300 Subject: [PATCH 3/9] ASoC: tlv320dac33: Change magic numbers used in Mode7 Upper and Lower threshold values are used as magic numbers. Replace them with defines for later use. Signed-off-by: Peter Ujfalusi Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- sound/soc/codecs/tlv320dac33.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index b101cbd95740..fa1994262c98 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -50,6 +50,9 @@ #define LATENCY_TIME_MS 20 +#define MODE7_LTHR 10 +#define MODE7_UTHR (DAC33_BUFFER_SIZE_SAMPLES - 10) + static struct snd_soc_codec *tlv320dac33_codec; enum dac33_state { @@ -567,7 +570,7 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) break; case DAC33_FIFO_MODE7: dac33_write16(codec, DAC33_PREFILL_MSB, - DAC33_THRREG(10)); + DAC33_THRREG(MODE7_LTHR)); break; default: dev_warn(codec->dev, "Unhandled FIFO mode: %d\n", @@ -867,10 +870,8 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream) * Configure the threshold levels, and leave 10 sample space * at the bottom, and also at the top of the FIFO */ - dac33_write16(codec, DAC33_UTHR_MSB, - DAC33_THRREG(DAC33_BUFFER_SIZE_SAMPLES - 10)); - dac33_write16(codec, DAC33_LTHR_MSB, - DAC33_THRREG(10)); + dac33_write16(codec, DAC33_UTHR_MSB, DAC33_THRREG(MODE7_UTHR)); + dac33_write16(codec, DAC33_LTHR_MSB, DAC33_THRREG(MODE7_LTHR)); break; default: break; From 76f471274dc9acacd521f151ac9171fd7bbc34c3 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 23 Apr 2010 10:10:00 +0300 Subject: [PATCH 4/9] ASoC: tlv320dac33: Calculate the interface speed during bursts When the DAC33 FIFO is in use the dai interface is running in much higher speed than the sampling frequency. Calculate the rate based on the internal base frequency and the bclk divider. Signed-off-by: Peter Ujfalusi Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- sound/soc/codecs/tlv320dac33.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index fa1994262c98..a8eb19b9b6c5 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -53,6 +53,8 @@ #define MODE7_LTHR 10 #define MODE7_UTHR (DAC33_BUFFER_SIZE_SAMPLES - 10) +#define BURST_BASEFREQ_HZ 49152000 + static struct snd_soc_codec *tlv320dac33_codec; enum dac33_state { @@ -95,6 +97,7 @@ struct tlv320dac33_priv { enum dac33_fifo_modes fifo_mode;/* FIFO mode selection */ unsigned int nsample; /* burst read amount from host */ u8 burst_bclkdiv; /* BCLK divider value in burst mode */ + unsigned int burst_rate; /* Interface speed in Burst modes */ int keep_bclk; /* Keep the BCLK continuously running * in FIFO modes */ @@ -1246,6 +1249,8 @@ static int __devinit dac33_i2c_probe(struct i2c_client *client, dac33->power_gpio = pdata->power_gpio; dac33->burst_bclkdiv = pdata->burst_bclkdiv; + /* Pre calculate the burst rate */ + dac33->burst_rate = BURST_BASEFREQ_HZ / dac33->burst_bclkdiv / 32; dac33->keep_bclk = pdata->keep_bclk; dac33->irq = client->irq; dac33->nsample = NSAMPLE_MAX; From f57d2cfaad0d6858d700b5671e01cf3aba6ef779 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 23 Apr 2010 10:10:01 +0300 Subject: [PATCH 5/9] ASoC: tlv320dac33: FIFO caused delay reporting Delay reporting for the three implemented DAC33 FIFO modes. DAC33 has FIFO depth status register(s), but it can not be used, since inside of pcm_pointer we can not send I2C commands. Timestamp based estimation need to be used. The method of calculating the delay depends on the active FIFO mode. Bypass mode: FIFO is bypassed, report 0 as delay Mode1: nSample fill mode. In this mode I need to use two timestamp ts1: taken when the interrupt has been received ts2: taken before writing to nSample register. Interrupts are coming when DAC33 FIFO depth goes under alarm threshold. Phase1: when we received the alarm threshold, but our workqueue has not been executed (safeguard phase). Just count the played out samples since ts1 and subtract it from the alarm threshold value. Phase2: During nSample burst (after writing to nSample register), count the played out samples since ts1, count the samples received since ts2 (in a burst). Estimate the FIFO depth using these and alarm threshold value. Phase3: Draining phase (after the burst read), count the played out samples since ts1. Estimate the FIFO depth using the nSample configuration and the alarm threshold value. Mode7: Threshold based fill mode. In this mode one timestamp is enough. ts1: taken when the interrupt has been received Interrupts are coming when DAC33 FIFO depth reaches upper threshold. Phase1: Draining phase (after the burst), counting the played out samples since ts1, and subtract it from the upper threshold value. Phase2: During burst operation. Using the pre calculated time needed to play out samples from the buffer during the drain period (from upper to lower threshold), move the time window to cover the estimated time from the burst start to the current time. Calculate the samples played out since lower threshold and also the samples received during the same time. Signed-off-by: Peter Ujfalusi Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- sound/soc/codecs/tlv320dac33.c | 222 ++++++++++++++++++++++++++++++++- 1 file changed, 217 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index a8eb19b9b6c5..3eddaec728c1 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -55,6 +55,13 @@ #define BURST_BASEFREQ_HZ 49152000 +#define SAMPLES_TO_US(rate, samples) \ + (1000000000 / ((rate * 1000) / samples)) + +#define US_TO_SAMPLES(rate, us) \ + (rate / (1000000 / us)) + + static struct snd_soc_codec *tlv320dac33_codec; enum dac33_state { @@ -101,6 +108,14 @@ struct tlv320dac33_priv { int keep_bclk; /* Keep the BCLK continuously running * in FIFO modes */ + spinlock_t lock; + unsigned long long t_stamp1; /* Time stamp for FIFO modes to */ + unsigned long long t_stamp2; /* calculate the FIFO caused delay */ + + unsigned int mode1_us_burst; /* Time to burst read n number of + * samples */ + unsigned int mode7_us_to_lthr; /* Time to reach lthr from uthr */ + enum dac33_state state; }; @@ -390,10 +405,14 @@ static int dac33_set_nsample(struct snd_kcontrol *kcontrol, return 0; if (ucontrol->value.integer.value[0] < dac33->nsample_min || - ucontrol->value.integer.value[0] > dac33->nsample_max) + ucontrol->value.integer.value[0] > dac33->nsample_max) { ret = -EINVAL; - else + } else { dac33->nsample = ucontrol->value.integer.value[0]; + /* Re calculate the burst time */ + dac33->mode1_us_burst = SAMPLES_TO_US(dac33->burst_rate, + dac33->nsample); + } return ret; } @@ -564,6 +583,13 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) case DAC33_FIFO_MODE1: dac33_write16(codec, DAC33_NSAMPLE_MSB, DAC33_THRREG(dac33->nsample + dac33->alarm_threshold)); + + /* Take the timestamps */ + spin_lock_irq(&dac33->lock); + dac33->t_stamp2 = ktime_to_us(ktime_get()); + dac33->t_stamp1 = dac33->t_stamp2; + spin_unlock_irq(&dac33->lock); + dac33_write16(codec, DAC33_PREFILL_MSB, DAC33_THRREG(dac33->alarm_threshold)); /* Enable Alarm Threshold IRQ with a delay */ @@ -572,8 +598,18 @@ static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) dac33_write(codec, DAC33_FIFO_IRQ_MASK, DAC33_MAT); break; case DAC33_FIFO_MODE7: + /* Take the timestamp */ + spin_lock_irq(&dac33->lock); + dac33->t_stamp1 = ktime_to_us(ktime_get()); + /* Move back the timestamp with drain time */ + dac33->t_stamp1 -= dac33->mode7_us_to_lthr; + spin_unlock_irq(&dac33->lock); + dac33_write16(codec, DAC33_PREFILL_MSB, DAC33_THRREG(MODE7_LTHR)); + + /* Enable Upper Threshold IRQ */ + dac33_write(codec, DAC33_FIFO_IRQ_MASK, DAC33_MUT); break; default: dev_warn(codec->dev, "Unhandled FIFO mode: %d\n", @@ -590,6 +626,11 @@ static inline void dac33_playback_handler(struct tlv320dac33_priv *dac33) switch (dac33->fifo_mode) { case DAC33_FIFO_MODE1: + /* Take the timestamp */ + spin_lock_irq(&dac33->lock); + dac33->t_stamp2 = ktime_to_us(ktime_get()); + spin_unlock_irq(&dac33->lock); + dac33_write16(codec, DAC33_NSAMPLE_MSB, DAC33_THRREG(dac33->nsample)); break; @@ -642,7 +683,13 @@ static irqreturn_t dac33_interrupt_handler(int irq, void *dev) struct snd_soc_codec *codec = dev; struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); - queue_work(dac33->dac33_wq, &dac33->work); + spin_lock(&dac33->lock); + dac33->t_stamp1 = ktime_to_us(ktime_get()); + spin_unlock(&dac33->lock); + + /* Do not schedule the workqueue in Mode7 */ + if (dac33->fifo_mode != DAC33_FIFO_MODE7) + queue_work(dac33->dac33_wq, &dac33->work); return IRQ_HANDLED; } @@ -794,8 +841,8 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream) DAC33_ATM(DAC33_FIFO_IRQ_MODE_LEVEL)); break; case DAC33_FIFO_MODE7: - /* Disable all interrupts */ - dac33_write(codec, DAC33_FIFO_IRQ_MASK, 0); + dac33_write(codec, DAC33_FIFO_IRQ_MODE_A, + DAC33_UTM(DAC33_FIFO_IRQ_MODE_LEVEL)); break; default: /* in FIFO bypass mode, the interrupts are not used */ @@ -930,6 +977,24 @@ static void dac33_calculate_times(struct snd_pcm_substream *substream) if (dac33->nsample > dac33->nsample_max) dac33->nsample = dac33->nsample_max; + + switch (dac33->fifo_mode) { + case DAC33_FIFO_MODE1: + dac33->mode1_us_burst = SAMPLES_TO_US(dac33->burst_rate, + dac33->nsample); + dac33->t_stamp1 = 0; + dac33->t_stamp2 = 0; + break; + case DAC33_FIFO_MODE7: + dac33->mode7_us_to_lthr = + SAMPLES_TO_US(substream->runtime->rate, + MODE7_UTHR - MODE7_LTHR + 1); + dac33->t_stamp1 = 0; + break; + default: + break; + } + } static int dac33_pcm_prepare(struct snd_pcm_substream *substream, @@ -974,6 +1039,151 @@ static int dac33_pcm_trigger(struct snd_pcm_substream *substream, int cmd, return ret; } +static snd_pcm_sframes_t dac33_dai_delay( + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->card->codec; + struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec); + unsigned long long t0, t1, t_now; + unsigned int time_delta; + int samples_out, samples_in, samples; + snd_pcm_sframes_t delay = 0; + + switch (dac33->fifo_mode) { + case DAC33_FIFO_BYPASS: + break; + case DAC33_FIFO_MODE1: + spin_lock(&dac33->lock); + t0 = dac33->t_stamp1; + t1 = dac33->t_stamp2; + spin_unlock(&dac33->lock); + t_now = ktime_to_us(ktime_get()); + + /* We have not started to fill the FIFO yet, delay is 0 */ + if (!t1) + goto out; + + if (t0 > t1) { + /* + * Phase 1: + * After Alarm threshold, and before nSample write + */ + time_delta = t_now - t0; + samples_out = time_delta ? US_TO_SAMPLES( + substream->runtime->rate, + time_delta) : 0; + + if (likely(dac33->alarm_threshold > samples_out)) + delay = dac33->alarm_threshold - samples_out; + else + delay = 0; + } else if ((t_now - t1) <= dac33->mode1_us_burst) { + /* + * Phase 2: + * After nSample write (during burst operation) + */ + time_delta = t_now - t0; + samples_out = time_delta ? US_TO_SAMPLES( + substream->runtime->rate, + time_delta) : 0; + + time_delta = t_now - t1; + samples_in = time_delta ? US_TO_SAMPLES( + dac33->burst_rate, + time_delta) : 0; + + samples = dac33->alarm_threshold; + samples += (samples_in - samples_out); + + if (likely(samples > 0)) + delay = samples; + else + delay = 0; + } else { + /* + * Phase 3: + * After burst operation, before next alarm threshold + */ + time_delta = t_now - t0; + samples_out = time_delta ? US_TO_SAMPLES( + substream->runtime->rate, + time_delta) : 0; + + samples_in = dac33->nsample; + samples = dac33->alarm_threshold; + samples += (samples_in - samples_out); + + if (likely(samples > 0)) + delay = samples > DAC33_BUFFER_SIZE_SAMPLES ? + DAC33_BUFFER_SIZE_SAMPLES : samples; + else + delay = 0; + } + break; + case DAC33_FIFO_MODE7: + spin_lock(&dac33->lock); + t0 = dac33->t_stamp1; + spin_unlock(&dac33->lock); + t_now = ktime_to_us(ktime_get()); + + /* We have not started to fill the FIFO yet, delay is 0 */ + if (!t0) + goto out; + + if (t_now <= t0) { + /* + * Either the timestamps are messed or equal. Report + * maximum delay + */ + delay = MODE7_UTHR; + goto out; + } + + time_delta = t_now - t0; + if (time_delta <= dac33->mode7_us_to_lthr) { + /* + * Phase 1: + * After burst (draining phase) + */ + samples_out = US_TO_SAMPLES( + substream->runtime->rate, + time_delta); + + if (likely(MODE7_UTHR > samples_out)) + delay = MODE7_UTHR - samples_out; + else + delay = 0; + } else { + /* + * Phase 2: + * During burst operation + */ + time_delta = time_delta - dac33->mode7_us_to_lthr; + + samples_out = US_TO_SAMPLES( + substream->runtime->rate, + time_delta); + samples_in = US_TO_SAMPLES( + dac33->burst_rate, + time_delta); + delay = MODE7_LTHR + samples_in - samples_out; + + if (unlikely(delay > MODE7_UTHR)) + delay = MODE7_UTHR; + } + break; + default: + dev_warn(codec->dev, "Unhandled FIFO mode: %d\n", + dac33->fifo_mode); + break; + } +out: + return delay; +} + static int dac33_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsigned int freq, int dir) { @@ -1185,6 +1395,7 @@ static struct snd_soc_dai_ops dac33_dai_ops = { .hw_params = dac33_hw_params, .prepare = dac33_pcm_prepare, .trigger = dac33_pcm_trigger, + .delay = dac33_dai_delay, .set_sysclk = dac33_set_dai_sysclk, .set_fmt = dac33_set_dai_fmt, }; @@ -1225,6 +1436,7 @@ static int __devinit dac33_i2c_probe(struct i2c_client *client, mutex_init(&codec->mutex); mutex_init(&dac33->mutex); + spin_lock_init(&dac33->lock); INIT_LIST_HEAD(&codec->dapm_widgets); INIT_LIST_HEAD(&codec->dapm_paths); From c6de6e03009efaa56baeb98da8bbcce0c6c889a6 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Mon, 26 Apr 2010 15:49:11 +0300 Subject: [PATCH 6/9] ASoC: tlv320aic3x: Remove unused version string Signed-off-by: Jarkko Nikula Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- sound/soc/codecs/tlv320aic3x.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 556123b4059c..260a2bef72c0 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -49,8 +49,6 @@ #include "tlv320aic3x.h" -#define AIC3X_VERSION "0.2" - /* codec private data */ struct aic3x_priv { struct snd_soc_codec codec; From d3235c4ac17b7f2a9c64cc3ebc71e23d1d1fa466 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Mon, 26 Apr 2010 15:49:12 +0300 Subject: [PATCH 7/9] ASoC: tlv320aic3x: Remove needless power off from aic3x_set_bias_level These ADC, DAC and output pin power off commands are needless in aic3x_set_bias_level since they are not enabled in aic3x_init and they are defined in aic3x_dapm_widgets so the ASoC DAPM will take care of them anyway. Signed-off-by: Jarkko Nikula Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- sound/soc/codecs/tlv320aic3x.c | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 260a2bef72c0..19602eca4924 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -1012,41 +1012,7 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec, * 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)); - - reg = aic3x_read_reg_cache(codec, HPLOUT_CTRL); - aic3x_write(codec, HPLOUT_CTRL, reg & ~HPLOUT_PWR_ON); - reg = aic3x_read_reg_cache(codec, HPROUT_CTRL); - aic3x_write(codec, HPROUT_CTRL, reg & ~HPROUT_PWR_ON); - - reg = aic3x_read_reg_cache(codec, HPLCOM_CTRL); - aic3x_write(codec, HPLCOM_CTRL, reg & ~HPLCOM_PWR_ON); - reg = aic3x_read_reg_cache(codec, HPRCOM_CTRL); - aic3x_write(codec, HPRCOM_CTRL, reg & ~HPRCOM_PWR_ON); - - reg = aic3x_read_reg_cache(codec, MONOLOPM_CTRL); - aic3x_write(codec, MONOLOPM_CTRL, reg & ~MONOLOPM_PWR_ON); - - reg = aic3x_read_reg_cache(codec, LLOPM_CTRL); - aic3x_write(codec, LLOPM_CTRL, reg & ~LLOPM_PWR_ON); - reg = aic3x_read_reg_cache(codec, RLOPM_CTRL); - aic3x_write(codec, RLOPM_CTRL, reg & ~RLOPM_PWR_ON); - if (aic3x->master) { /* disable pll */ reg = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG); From db13802e51f29c40b404038485121a3973c0947d Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Mon, 26 Apr 2010 15:49:13 +0300 Subject: [PATCH 8/9] ASoC: tlv320aic3x: Change bias management semantics Move PLL enable from BIAS_ON state to BIAS_PREPARE to be pair with BIAS_STANDBY where PLL is disabled. Remove also old comments about power control. Signed-off-by: Jarkko Nikula Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- sound/soc/codecs/tlv320aic3x.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 19602eca4924..6b74ad808a09 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -997,7 +997,8 @@ 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 */ + break; + case SND_SOC_BIAS_PREPARE: if (aic3x->master) { /* enable pll */ reg = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG); @@ -1005,13 +1006,8 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec, 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 - */ + /* fall through and disable pll */ case SND_SOC_BIAS_OFF: if (aic3x->master) { /* disable pll */ From 07779fdd1a236145b5f5dc6916c6b84d9712b305 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Mon, 26 Apr 2010 15:49:14 +0300 Subject: [PATCH 9/9] ASoC: tlv320aic3x: Add basic regulator support This patch adds the TLV320AIC3x supplies and enables all of them for the entire lifetime of the device. Signed-off-by: Jarkko Nikula Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- sound/soc/codecs/tlv320aic3x.c | 37 ++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 6b74ad808a09..584bc1e67f76 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -49,9 +50,18 @@ #include "tlv320aic3x.h" +#define AIC3X_NUM_SUPPLIES 4 +static const char *aic3x_supply_names[AIC3X_NUM_SUPPLIES] = { + "IOVDD", /* I/O Voltage */ + "DVDD", /* Digital Core Voltage */ + "AVDD", /* Analog DAC Voltage */ + "DRVDD", /* ADC Analog and Output Driver Voltage */ +}; + /* codec private data */ struct aic3x_priv { struct snd_soc_codec codec; + struct regulator_bulk_data supplies[AIC3X_NUM_SUPPLIES]; unsigned int sysclk; int master; }; @@ -1268,6 +1278,9 @@ static int aic3x_unregister(struct aic3x_priv *aic3x) snd_soc_unregister_dai(&aic3x_dai); snd_soc_unregister_codec(&aic3x->codec); + regulator_bulk_disable(ARRAY_SIZE(aic3x->supplies), aic3x->supplies); + regulator_bulk_free(ARRAY_SIZE(aic3x->supplies), aic3x->supplies); + kfree(aic3x); aic3x_codec = NULL; @@ -1289,6 +1302,7 @@ static int aic3x_i2c_probe(struct i2c_client *i2c, { struct snd_soc_codec *codec; struct aic3x_priv *aic3x; + int ret, i; aic3x = kzalloc(sizeof(struct aic3x_priv), GFP_KERNEL); if (aic3x == NULL) { @@ -1304,7 +1318,30 @@ static int aic3x_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, aic3x); + for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++) + aic3x->supplies[i].supply = aic3x_supply_names[i]; + + ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(aic3x->supplies), + aic3x->supplies); + if (ret != 0) { + dev_err(codec->dev, "Failed to request supplies: %d\n", ret); + goto err_get; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(aic3x->supplies), + aic3x->supplies); + if (ret != 0) { + dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); + goto err_enable; + } + return aic3x_register(codec); + +err_enable: + regulator_bulk_free(ARRAY_SIZE(aic3x->supplies), aic3x->supplies); +err_get: + kfree(aic3x); + return ret; } static int aic3x_i2c_remove(struct i2c_client *client)