diff options
author | Ola Lilja <elilola@steludxu2785.(none)> | 2011-04-26 17:48:43 +0200 |
---|---|---|
committer | Jonas ABERG <jonas.aberg@stericsson.com> | 2011-04-27 10:22:35 +0200 |
commit | 1d6717f9cf501a5ab6b71427fe2f7c1fea38bb45 (patch) | |
tree | f06e7d489064cc6f274cb6c0e0e36604bed0b72b | |
parent | 8b996c96e3c5c163ef980377c88d83cb694bf2f9 (diff) |
Ux500 ASoC: Turn off AB8500 when inactiveu8500-android-2.3_v0.65
Using reference counter to be able to turn of AB8500 when none
of playback, capture or vibra is active.
Change-Id: If295d40a31e0f7529c2cd9573052ce798fdbe465
Signed-off-by: Ola Lilja <ola.o.lilja@stericsson.com>
Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/21661
Reviewed-by: Roger NILSSON1 <roger.xr.nilsson@stericsson.com>
-rw-r--r-- | sound/soc/codecs/ab8500_audio.c | 108 |
1 files changed, 76 insertions, 32 deletions
diff --git a/sound/soc/codecs/ab8500_audio.c b/sound/soc/codecs/ab8500_audio.c index a958388d947..5191328142d 100644 --- a/sound/soc/codecs/ab8500_audio.c +++ b/sound/soc/codecs/ab8500_audio.c @@ -22,6 +22,7 @@ #include <linux/delay.h> #include <linux/pm.h> #include <linux/platform_device.h> +#include <linux/mutex.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -54,7 +55,7 @@ * AB8500 register cache & default register settings */ static const u8 ab8500_reg_cache[AB8500_CACHEREGNUM] = { - 0x88, /* REG_POWERUP (0x00) */ + 0x00, /* REG_POWERUP (0x00) */ 0x00, /* REG_AUDSWRESET (0x01) */ 0x00, /* REG_ADPATHENA (0x02) */ 0x00, /* REG_DAPATHENA (0x03) */ @@ -171,7 +172,9 @@ static const u8 ab8500_reg_cache[AB8500_CACHEREGNUM] = { static struct snd_soc_codec *ab8500_codec; static struct clk *clk_ptr_audioclk; static struct clk *clk_ptr_sysclk; -static int sysclk_on; +static DEFINE_MUTEX(power_lock); +static int ab8500_power_count; +static bool ab8500_vibra_on; /* Reads an arbitrary register from the ab8500 chip. */ @@ -1616,6 +1619,61 @@ static int ab8500_codec_set_bit_delay_if1(struct snd_soc_codec *codec, unsigned return 0; } +static int ab8500_codec_power_control_inc(struct snd_soc_codec *codec) +{ + int ret; + unsigned int set_mask; + + mutex_lock(&power_lock); + + ab8500_power_count++; + pr_debug("ab8500_power_count changed from %d to %d", + ab8500_power_count-1, + ab8500_power_count); + + if (ab8500_power_count == 1) { + pr_debug("Enabling AB8500."); + set_mask = BMASK(REG_POWERUP_POWERUP) | BMASK(REG_POWERUP_ENANA); + ab8500_codec_update_reg_audio(codec, REG_POWERUP, 0x00, set_mask); + + pr_debug("Enabling sysclk."); + ret = clk_enable(clk_ptr_audioclk); + if (ret) { + pr_err("ERROR: clk_enable failed (ret = %d)!", ret); + ab8500_power_count = 0; + return ret; + } + } + + mutex_unlock(&power_lock); + + return 0; +} + +static void ab8500_codec_power_control_dec(struct snd_soc_codec *codec) +{ + unsigned int clear_mask; + + mutex_lock(&power_lock); + + ab8500_power_count--; + + pr_debug("ab8500_power_count changed from %d to %d", + ab8500_power_count+1, + ab8500_power_count); + + if (ab8500_power_count == 0) { + pr_debug("Disabling sysclk."); + clk_disable(clk_ptr_audioclk); + + pr_debug("Disabling AB8500."); + clear_mask = BMASK(REG_POWERUP_POWERUP) | BMASK(REG_POWERUP_ENANA); + ab8500_codec_update_reg_audio(codec, REG_POWERUP, clear_mask, 0x00); + } + + mutex_unlock(&power_lock); +} + /* Extended interface for codec-driver */ void ab8500_audio_pwm_vibra(unsigned char speed_left_pos, @@ -1624,23 +1682,30 @@ void ab8500_audio_pwm_vibra(unsigned char speed_left_pos, unsigned char speed_right_neg) { unsigned int clear_mask, set_mask; - bool vibra_on; + bool ab8500_vibra_on_new; if (ab8500_codec == NULL) { pr_err("%s: ERROR: AB8500 ASoC-driver not yet probed!\n", __func__); return; } - vibra_on = speed_left_pos | speed_left_neg | speed_right_pos | speed_right_neg; - - if (!vibra_on) { + ab8500_vibra_on_new = speed_left_pos | speed_left_neg | speed_right_pos | speed_right_neg; + if (!ab8500_vibra_on_new) { pr_debug("%s: PWM-vibra off.\n", __func__); clear_mask = BMASK(REG_ANACONF4_ENVIB1) | BMASK(REG_ANACONF4_ENVIB2); ab8500_codec_update_reg_audio(ab8500_codec, REG_ANACONF4, clear_mask, 0x00); + ab8500_vibra_on = false; + ab8500_codec_power_control_dec(ab8500_codec); return; } - pr_debug("%s: PWM-vibra on (%d, %d, %d, %d).\n", + if (!ab8500_vibra_on) { + pr_debug("%s: PWM-vibra on.\n", __func__); + ab8500_vibra_on = true; + ab8500_codec_power_control_inc(ab8500_codec); + } + + pr_debug("%s: PWM-vibra (%d, %d, %d, %d).\n", __func__, speed_left_pos, speed_left_neg, @@ -1800,29 +1865,13 @@ static int ab8500_codec_pcm_startup(struct snd_pcm_substream *substream, static int ab8500_codec_pcm_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - int ret = 0; - pr_debug("%s Enter.\n", __func__); /* Clear interrupt status registers by reading them. */ ab8500_codec_read_reg_audio(dai->codec, REG_AUDINTSOURCE1); ab8500_codec_read_reg_audio(dai->codec, REG_AUDINTSOURCE2); - sysclk_on++; - pr_debug("sysclk_on changed from %d to %d", sysclk_on-1, sysclk_on); - - if (sysclk_on > 1) - return 0; - - ret = clk_enable(clk_ptr_audioclk); - if (ret) { - pr_err("ERROR: clk_enable failed (ret = %d)!", ret); - sysclk_on = 0; - return ret; - } - pr_debug("sysclk enabled."); - - return ret; + return ab8500_codec_power_control_inc(dai->codec); } static void ab8500_codec_pcm_shutdown(struct snd_pcm_substream *substream, @@ -1830,13 +1879,7 @@ static void ab8500_codec_pcm_shutdown(struct snd_pcm_substream *substream, { pr_debug("%s Enter.\n", __func__); - sysclk_on--; - pr_debug("sysclk_on changed from %d to %d", sysclk_on+1, sysclk_on); - - if (sysclk_on == 0) { - clk_disable(clk_ptr_audioclk); - pr_debug("sysclk disabled."); - } + ab8500_codec_power_control_dec(dai->codec); ab8500_codec_dump_all_reg(dai->codec); } @@ -2190,8 +2233,9 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec) } ab8500_codec = codec; + ab8500_vibra_on = false; - sysclk_on = 0; + ab8500_power_count = 0; clk_ptr_sysclk = clk_get(codec->dev, "sysclk"); if (IS_ERR(clk_ptr_sysclk)) { pr_err("ERROR: clk_get failed (ret = %d)!", -EFAULT); |