summaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorJarkko Nikula <jhnikula@gmail.com>2010-09-20 10:39:13 +0300
committerLiam Girdwood <lrg@slimlogic.co.uk>2010-09-20 18:40:47 +0100
commit5a895f8a09218716654fb89e0f876e4e3ca606a8 (patch)
treecef536f517a54d24a637c2734d24c8795be5eaf2 /sound
parent6c1a7d40c2237ff7690ca682336e22777c847ffe (diff)
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')
-rw-r--r--sound/soc/codecs/tlv320aic3x.c63
1 files changed, 61 insertions, 2 deletions
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 5f8a7c4045c..5356946fb6b 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;