diff options
author | Jarkko Nikula <jhnikula@gmail.com> | 2010-09-20 07:39:13 (GMT) |
---|---|---|
committer | Liam Girdwood <lrg@slimlogic.co.uk> | 2010-09-20 17:40:47 (GMT) |
commit | 5a895f8a09218716654fb89e0f876e4e3ca606a8 (patch) | |
tree | cef536f517a54d24a637c2734d24c8795be5eaf2 /sound/soc/codecs/tlv320aic3x.c | |
parent | 6c1a7d40c2237ff7690ca682336e22777c847ffe (diff) | |
download | linux-5a895f8a09218716654fb89e0f876e4e3ca606a8.tar.xz |
ASoC: tlv320aic3x: Use regulator notifiers for optimizing the cache sync
There is no need to reset the codec and perform cache sync if none of the
supply regulators were not disabled. Patch registers a notifier callback for
each supply and callback then sets a flag to indicate when cache sync is
required.
HW writes are also needless when codec bias is off so cache_only flag is set
independently of actual supply regulators state.
Signed-off-by: Jarkko Nikula <jhnikula@gmail.com>
Acked-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: Liam Girdwood <lrg@slimlogic.co.uk>
Diffstat (limited to 'sound/soc/codecs/tlv320aic3x.c')
-rw-r--r-- | sound/soc/codecs/tlv320aic3x.c | 63 |
1 files changed, 61 insertions, 2 deletions
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 5f8a7c4..5356946 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -61,9 +61,18 @@ static const char *aic3x_supply_names[AIC3X_NUM_SUPPLIES] = { "DRVDD", /* ADC Analog and Output Driver Voltage */ }; +struct aic3x_priv; + +struct aic3x_disable_nb { + struct notifier_block nb; + struct aic3x_priv *aic3x; +}; + /* codec private data */ struct aic3x_priv { + struct snd_soc_codec *codec; struct regulator_bulk_data supplies[AIC3X_NUM_SUPPLIES]; + struct aic3x_disable_nb disable_nb[AIC3X_NUM_SUPPLIES]; enum snd_soc_control_type control_type; struct aic3x_setup_data *setup; void *control_data; @@ -122,6 +131,8 @@ static int aic3x_read(struct snd_soc_codec *codec, unsigned int reg, { u8 *cache = codec->reg_cache; + if (codec->cache_only) + return -EINVAL; if (reg >= AIC3X_CACHEREGNUM) return -1; @@ -1052,6 +1063,26 @@ static int aic3x_init_3007(struct snd_soc_codec *codec) return 0; } +static int aic3x_regulator_event(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct aic3x_disable_nb *disable_nb = + container_of(nb, struct aic3x_disable_nb, nb); + struct aic3x_priv *aic3x = disable_nb->aic3x; + + if (event & REGULATOR_EVENT_DISABLE) { + /* + * Put codec to reset and require cache sync as at least one + * of the supplies was disabled + */ + if (aic3x->gpio_reset >= 0) + gpio_set_value(aic3x->gpio_reset, 0); + aic3x->codec->cache_sync = 1; + } + + return 0; +} + static int aic3x_set_power(struct snd_soc_codec *codec, int power) { struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); @@ -1064,6 +1095,13 @@ static int aic3x_set_power(struct snd_soc_codec *codec, int power) if (ret) goto out; aic3x->power = 1; + /* + * Reset release and cache sync is necessary only if some + * supply was off or if there were cached writes + */ + if (!codec->cache_sync) + goto out; + if (aic3x->gpio_reset >= 0) { udelay(1); gpio_set_value(aic3x->gpio_reset, 1); @@ -1078,8 +1116,8 @@ static int aic3x_set_power(struct snd_soc_codec *codec, int power) codec->cache_sync = 0; } else { aic3x->power = 0; - if (aic3x->gpio_reset >= 0) - gpio_set_value(aic3x->gpio_reset, 0); + /* HW writes are needless when bias is off */ + codec->cache_only = 1; ret = regulator_bulk_disable(ARRAY_SIZE(aic3x->supplies), aic3x->supplies); } @@ -1315,6 +1353,7 @@ static int aic3x_probe(struct snd_soc_codec *codec) int ret, i; codec->control_data = aic3x->control_data; + aic3x->codec = codec; ret = snd_soc_codec_set_cache_io(codec, 8, 8, aic3x->control_type); if (ret != 0) { @@ -1338,6 +1377,18 @@ static int aic3x_probe(struct snd_soc_codec *codec) dev_err(codec->dev, "Failed to request supplies: %d\n", ret); goto err_get; } + for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++) { + aic3x->disable_nb[i].nb.notifier_call = aic3x_regulator_event; + aic3x->disable_nb[i].aic3x = aic3x; + ret = regulator_register_notifier(aic3x->supplies[i].consumer, + &aic3x->disable_nb[i].nb); + if (ret) { + dev_err(codec->dev, + "Failed to request regulator notifier: %d\n", + ret); + goto err_notif; + } + } ret = regulator_bulk_enable(ARRAY_SIZE(aic3x->supplies), aic3x->supplies); @@ -1372,6 +1423,10 @@ static int aic3x_probe(struct snd_soc_codec *codec) return 0; err_enable: +err_notif: + while (i--) + regulator_unregister_notifier(aic3x->supplies[i].consumer, + &aic3x->disable_nb[i].nb); regulator_bulk_free(ARRAY_SIZE(aic3x->supplies), aic3x->supplies); err_get: if (aic3x->gpio_reset >= 0) @@ -1384,6 +1439,7 @@ err_gpio: static int aic3x_remove(struct snd_soc_codec *codec) { struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); + int i; aic3x_set_bias_level(codec, SND_SOC_BIAS_OFF); if (aic3x->gpio_reset >= 0) { @@ -1391,6 +1447,9 @@ static int aic3x_remove(struct snd_soc_codec *codec) gpio_free(aic3x->gpio_reset); } regulator_bulk_disable(ARRAY_SIZE(aic3x->supplies), aic3x->supplies); + for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++) + regulator_unregister_notifier(aic3x->supplies[i].consumer, + &aic3x->disable_nb[i].nb); regulator_bulk_free(ARRAY_SIZE(aic3x->supplies), aic3x->supplies); return 0; |