From bf38e033d567f4d6ed207453488076db68d440a3 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Tue, 14 Jun 2011 14:39:00 +0200 Subject: Ux500 ASoC: Enhanced regulator management The mic-regulators are now only taken when needed. This involves querying the codec-driver to see if the the associated DAPM-path is enabled or not. ST-Ericsson Linux next: NA ST-Ericsson ID: 337512 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I5dec05a8a4b0cdb567bd3a3301d3c50a48fab23b Signed-off-by: Ola Lilja Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/24373 Reviewed-by: QATEST Reviewed-by: Roger NILSSON1 Reviewed-by: Linus WALLEIJ Conflicts: sound/soc/ux500/ux500_ab8500.c --- sound/soc/codecs/ab8500_audio.c | 32 +++++++- sound/soc/codecs/ab8500_audio.h | 8 ++ sound/soc/ux500/ux500_ab8500.c | 170 +++++++++++++++++++++++++--------------- 3 files changed, 144 insertions(+), 66 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/ab8500_audio.c b/sound/soc/codecs/ab8500_audio.c index 49b4bf32417..ec323e1ea8d 100644 --- a/sound/soc/codecs/ab8500_audio.c +++ b/sound/soc/codecs/ab8500_audio.c @@ -278,8 +278,6 @@ static inline int ab8500_codec_update_reg_audio(struct snd_soc_codec *codec, return ab8500_codec_write_reg_audio(codec, reg, new); } -/*--------------------------------------------------------------*/ - /* Whether widget's register definitions should be inverted or not */ enum control_inversion { NORMAL = 0, @@ -1852,6 +1850,36 @@ int ab8500_audio_setup_if1(struct snd_soc_codec *codec, return 0; } +bool ab8500_audio_dapm_path_active(enum ab8500_audio_dapm_path dapm_path) +{ + int reg, reg_mask; + + switch (dapm_path) { + case AB8500_AUDIO_DAPM_PATH_DMIC: + reg = ab8500_codec_read_reg_audio(ab8500_codec, REG_DIGMICCONF); + reg_mask = BMASK(REG_DIGMICCONF_ENDMIC1) | + BMASK(REG_DIGMICCONF_ENDMIC2) | + BMASK(REG_DIGMICCONF_ENDMIC3) | + BMASK(REG_DIGMICCONF_ENDMIC4) | + BMASK(REG_DIGMICCONF_ENDMIC5) | + BMASK(REG_DIGMICCONF_ENDMIC6); + return reg & reg_mask; + + case AB8500_AUDIO_DAPM_PATH_AMIC1: + reg = ab8500_codec_read_reg_audio(ab8500_codec, REG_ANACONF2); + reg_mask = BMASK(REG_ANACONF2_MUTMIC1); + return !(reg & reg_mask); + + case AB8500_AUDIO_DAPM_PATH_AMIC2: + reg = ab8500_codec_read_reg_audio(ab8500_codec, REG_ANACONF2); + reg_mask = BMASK(REG_ANACONF2_MUTMIC2); + return !(reg & reg_mask); + + default: + return false; + } +} + static int ab8500_codec_add_widgets(struct snd_soc_codec *codec) { int ret; diff --git a/sound/soc/codecs/ab8500_audio.h b/sound/soc/codecs/ab8500_audio.h index 1ff7dafa3e7..7cfdd1cc13a 100644 --- a/sound/soc/codecs/ab8500_audio.h +++ b/sound/soc/codecs/ab8500_audio.h @@ -23,6 +23,7 @@ extern struct snd_soc_dai_driver ab8500_codec_dai[]; extern struct snd_soc_codec_driver soc_codec_dev_ab8500; /* Extended interface for codec-driver */ + void ab8500_audio_power_control(bool power_on); int ab8500_audio_set_word_length(struct snd_soc_dai *dai, unsigned int wl); int ab8500_audio_set_bit_delay(struct snd_soc_dai *dai, unsigned int delay); @@ -31,6 +32,13 @@ int ab8500_audio_setup_if1(struct snd_soc_codec *codec, unsigned int wl, unsigned int delay); +enum ab8500_audio_dapm_path { + AB8500_AUDIO_DAPM_PATH_DMIC, + AB8500_AUDIO_DAPM_PATH_AMIC1, + AB8500_AUDIO_DAPM_PATH_AMIC2 +}; +bool ab8500_audio_dapm_path_active(enum ab8500_audio_dapm_path dapm_path); + /* AB8500 audio bank (0x0d) register definitions */ #define REG_POWERUP 0x00 diff --git a/sound/soc/ux500/ux500_ab8500.c b/sound/soc/ux500/ux500_ab8500.c index f32a6a45029..dc980ae9323 100644 --- a/sound/soc/ux500/ux500_ab8500.c +++ b/sound/soc/ux500/ux500_ab8500.c @@ -45,14 +45,37 @@ static struct snd_soc_jack jack; -static int master_clock_sel; +/* Power-control */ +static DEFINE_MUTEX(power_lock); +static int ab8500_power_count; + +/* Clocks */ /* audioclk -> intclk -> sysclk/ulpclk */ +static int master_clock_sel; static struct clk *clk_ptr_audioclk; static struct clk *clk_ptr_intclk; static struct clk *clk_ptr_sysclk; static struct clk *clk_ptr_ulpclk; -static DEFINE_MUTEX(power_lock); -static int ab8500_power_count; + +/* Regulators */ +static enum regulator_idx { + REGULATOR_AUDIO, + REGULATOR_DMIC, + REGULATOR_AMIC1, + REGULATOR_AMIC2 +}; +static struct regulator_bulk_data reg_info[4] = { + { .supply = "v-audio" }, + { .supply = "v-dmic" }, + { .supply = "v-amic1" }, + { .supply = "v-amic2" } +}; +static bool reg_enabled[4] = { + false, + false, + false, + false +}; /* Slot configuration */ static unsigned int tx_slots = DEF_TX_SLOTS; @@ -109,46 +132,46 @@ static const struct snd_kcontrol_new mclk_input_control = { /* Regulators */ -static struct regulator_bulk_data ab8500_regus[4] = { - { .supply = "v-dmic" }, - { .supply = "v-audio" }, - { .supply = "v-amic1" }, - { .supply = "v-amic2" } -}; - -static int enable_regulator(const char *name) +static int enable_regulator(enum regulator_idx idx) { - int i, status; - - for (i = 0; i < ARRAY_SIZE(ab8500_regus); ++i) { - if (strcmp(name, ab8500_regus[i].supply) != 0) - continue; - - status = regulator_enable(ab8500_regus[i].consumer); - if (status != 0) { - pr_err("%s: Failure with regulator %s (ret = %d)\n", - __func__, name, status); - return status; - }; - - pr_debug("%s: Enabled regulator %s.\n", __func__, name); + int ret; +pr_err("%s: DORIAN regulator %d.\n", __func__, (int)idx); + if (reg_enabled[idx]) return 0; - } - return -EINVAL; + ret = regulator_enable(reg_info[idx].consumer); + if (ret != 0) { + pr_err("%s: Failure to enable regulator '%s' (ret = %d)\n", + __func__, reg_info[idx].supply, ret); + return ret; + }; + + reg_enabled[idx] = true; + pr_debug("%s: Enabled regulator '%s', status: %d, %d, %d, %d\n", + __func__, + reg_info[idx].supply, + (int)reg_enabled[0], + (int)reg_enabled[1], + (int)reg_enabled[2], + (int)reg_enabled[3]); + return 0; } -static void disable_regulator(const char *name) +static void disable_regulator(enum regulator_idx idx) { - int i; + if (!reg_enabled[idx]) + return; - for (i = 0; i < ARRAY_SIZE(ab8500_regus); ++i) { - if (strcmp(name, ab8500_regus[i].supply) == 0) { - regulator_disable(ab8500_regus[i].consumer); - pr_debug("%s: Disabled regulator %s.\n", __func__, name); - return; - } - } + regulator_disable(reg_info[idx].consumer); + + reg_enabled[idx] = false; + pr_debug("%s: Disabled regulator '%s', status: %d, %d, %d, %d\n", + __func__, + reg_info[idx].supply, + (int)reg_enabled[0], + (int)reg_enabled[1], + (int)reg_enabled[2], + (int)reg_enabled[3]); } static int create_regulators(void) @@ -157,17 +180,16 @@ static int create_regulators(void) pr_debug("%s: Enter.\n", __func__); - for (i = 0; i < ARRAY_SIZE(ab8500_regus); ++i) - ab8500_regus[i].consumer = NULL; - - for (i = 0; i < ARRAY_SIZE(ab8500_regus); ++i) { - ab8500_regus[i].consumer = regulator_get(NULL, - ab8500_regus[i].supply); - if (IS_ERR(ab8500_regus[i].consumer)) { - status = PTR_ERR(ab8500_regus[i].consumer); - pr_err("%s: ERROR: Failed to get supply '%s' (ret = %d)!\n", - __func__, ab8500_regus[i].supply, status); - ab8500_regus[i].consumer = NULL; + for (i = 0; i < ARRAY_SIZE(reg_info); ++i) + reg_info[i].consumer = NULL; + + for (i = 0; i < ARRAY_SIZE(reg_info); ++i) { + reg_info[i].consumer = regulator_get(NULL, reg_info[i].supply); + if (IS_ERR(reg_info[i].consumer)) { + status = PTR_ERR(reg_info[i].consumer); + pr_err("%s: ERROR: Failed to get regulator '%s' (ret = %d)!\n", + __func__, reg_info[i].supply, status); + reg_info[i].consumer = NULL; goto err_get; } } @@ -176,17 +198,17 @@ static int create_regulators(void) err_get: - for (i = 0; i < ARRAY_SIZE(ab8500_regus); ++i) { - if (ab8500_regus[i].consumer) { - regulator_put(ab8500_regus[i].consumer); - ab8500_regus[i].consumer = NULL; + for (i = 0; i < ARRAY_SIZE(reg_info); ++i) { + if (reg_info[i].consumer) { + regulator_put(reg_info[i].consumer); + reg_info[i].consumer = NULL; } } return status; } -/* Master clock */ +/* Power/clock control */ static int ux500_ab8500_power_control_inc(void) { @@ -214,6 +236,8 @@ static int ux500_ab8500_power_control_inc(void) pr_debug("%s: Enabling master-clock (%s).", __func__, (master_clock_sel == 0) ? "SYSCLK" : "ULPCLK"); + + /* Enable audio-clock */ ret = clk_enable(clk_ptr_audioclk); if (ret) { pr_err("%s: ERROR: clk_enable failed (ret = %d)!", __func__, ret); @@ -221,7 +245,11 @@ static int ux500_ab8500_power_control_inc(void) return ret; } + /* Power on audio-parts of AB8500 */ ab8500_audio_power_control(true); + + /* Turn on audio-regulator */ + ret = enable_regulator(REGULATOR_AUDIO); } mutex_unlock(&power_lock); @@ -244,9 +272,15 @@ static void ux500_ab8500_power_control_dec(void) pr_debug("%s: Disabling master-clock (%s).", __func__, (master_clock_sel == 0) ? "SYSCLK" : "ULPCLK"); + + /* Disable audio-clock */ clk_disable(clk_ptr_audioclk); + /* Power off audio-parts of AB8500 */ ab8500_audio_power_control(false); + + /* Turn off audio-regulator */ + disable_regulator(REGULATOR_AUDIO); } mutex_unlock(&power_lock); @@ -256,24 +290,29 @@ static void ux500_ab8500_power_control_dec(void) int ux500_ab8500_startup(struct snd_pcm_substream *substream) { - int i; - int status = 0; + int ret = 0; pr_info("%s: Enter\n", __func__); - /* Enable regulators */ - for (i = 0; i < ARRAY_SIZE(ab8500_regus); ++i) - status += enable_regulator(ab8500_regus[i].supply); + /* If we start recording we better enable the needed mic-regulators */ + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + if (ab8500_audio_dapm_path_active(AB8500_AUDIO_DAPM_PATH_DMIC)) + ret += enable_regulator(REGULATOR_DMIC); + if (ab8500_audio_dapm_path_active(AB8500_AUDIO_DAPM_PATH_AMIC1)) + ret += enable_regulator(REGULATOR_AMIC1); + if (ab8500_audio_dapm_path_active(AB8500_AUDIO_DAPM_PATH_AMIC2)) + ret += enable_regulator(REGULATOR_AMIC2); + if (ret != 0) + return ret; + } return ux500_ab8500_power_control_inc(); - return status; + return ret; } void ux500_ab8500_shutdown(struct snd_pcm_substream *substream) { - int i; - pr_info("%s: Enter\n", __func__); /* Reset slots configuration to default(s) */ @@ -282,9 +321,12 @@ void ux500_ab8500_shutdown(struct snd_pcm_substream *substream) else rx_slots = DEF_RX_SLOTS; - /* Disable regulators */ - for (i = 0; i < ARRAY_SIZE(ab8500_regus); ++i) - disable_regulator(ab8500_regus[i].supply); + /* Disable all mic-regulators that were enabled when we stop recording */ + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + disable_regulator(REGULATOR_DMIC); + disable_regulator(REGULATOR_AMIC1); + disable_regulator(REGULATOR_AMIC2); + } ux500_ab8500_power_control_dec(); } @@ -496,7 +538,7 @@ void ux500_ab8500_soc_machine_drv_cleanup(void) { pr_info("%s: Enter.\n", __func__); - regulator_bulk_free(ARRAY_SIZE(ab8500_regus), ab8500_regus); + regulator_bulk_free(ARRAY_SIZE(reg_info), reg_info); if (clk_ptr_sysclk != NULL) clk_put(clk_ptr_sysclk); -- cgit v1.2.3