Commit 5a895f8a authored by Jarkko Nikula's avatar Jarkko Nikula Committed by Liam Girdwood

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: default avatarJarkko Nikula <jhnikula@gmail.com>
Acked-by: default avatarMark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: default avatarLiam Girdwood <lrg@slimlogic.co.uk>
parent 6c1a7d40
...@@ -61,9 +61,18 @@ static const char *aic3x_supply_names[AIC3X_NUM_SUPPLIES] = { ...@@ -61,9 +61,18 @@ static const char *aic3x_supply_names[AIC3X_NUM_SUPPLIES] = {
"DRVDD", /* ADC Analog and Output Driver Voltage */ "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 */ /* codec private data */
struct aic3x_priv { struct aic3x_priv {
struct snd_soc_codec *codec;
struct regulator_bulk_data supplies[AIC3X_NUM_SUPPLIES]; struct regulator_bulk_data supplies[AIC3X_NUM_SUPPLIES];
struct aic3x_disable_nb disable_nb[AIC3X_NUM_SUPPLIES];
enum snd_soc_control_type control_type; enum snd_soc_control_type control_type;
struct aic3x_setup_data *setup; struct aic3x_setup_data *setup;
void *control_data; void *control_data;
...@@ -122,6 +131,8 @@ static int aic3x_read(struct snd_soc_codec *codec, unsigned int reg, ...@@ -122,6 +131,8 @@ static int aic3x_read(struct snd_soc_codec *codec, unsigned int reg,
{ {
u8 *cache = codec->reg_cache; u8 *cache = codec->reg_cache;
if (codec->cache_only)
return -EINVAL;
if (reg >= AIC3X_CACHEREGNUM) if (reg >= AIC3X_CACHEREGNUM)
return -1; return -1;
...@@ -1052,6 +1063,26 @@ static int aic3x_init_3007(struct snd_soc_codec *codec) ...@@ -1052,6 +1063,26 @@ static int aic3x_init_3007(struct snd_soc_codec *codec)
return 0; 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) static int aic3x_set_power(struct snd_soc_codec *codec, int power)
{ {
struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); 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) ...@@ -1064,6 +1095,13 @@ static int aic3x_set_power(struct snd_soc_codec *codec, int power)
if (ret) if (ret)
goto out; goto out;
aic3x->power = 1; 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) { if (aic3x->gpio_reset >= 0) {
udelay(1); udelay(1);
gpio_set_value(aic3x->gpio_reset, 1); gpio_set_value(aic3x->gpio_reset, 1);
...@@ -1078,8 +1116,8 @@ static int aic3x_set_power(struct snd_soc_codec *codec, int power) ...@@ -1078,8 +1116,8 @@ static int aic3x_set_power(struct snd_soc_codec *codec, int power)
codec->cache_sync = 0; codec->cache_sync = 0;
} else { } else {
aic3x->power = 0; aic3x->power = 0;
if (aic3x->gpio_reset >= 0) /* HW writes are needless when bias is off */
gpio_set_value(aic3x->gpio_reset, 0); codec->cache_only = 1;
ret = regulator_bulk_disable(ARRAY_SIZE(aic3x->supplies), ret = regulator_bulk_disable(ARRAY_SIZE(aic3x->supplies),
aic3x->supplies); aic3x->supplies);
} }
...@@ -1315,6 +1353,7 @@ static int aic3x_probe(struct snd_soc_codec *codec) ...@@ -1315,6 +1353,7 @@ static int aic3x_probe(struct snd_soc_codec *codec)
int ret, i; int ret, i;
codec->control_data = aic3x->control_data; codec->control_data = aic3x->control_data;
aic3x->codec = codec;
ret = snd_soc_codec_set_cache_io(codec, 8, 8, aic3x->control_type); ret = snd_soc_codec_set_cache_io(codec, 8, 8, aic3x->control_type);
if (ret != 0) { if (ret != 0) {
...@@ -1338,6 +1377,18 @@ static int aic3x_probe(struct snd_soc_codec *codec) ...@@ -1338,6 +1377,18 @@ static int aic3x_probe(struct snd_soc_codec *codec)
dev_err(codec->dev, "Failed to request supplies: %d\n", ret); dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
goto err_get; 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), ret = regulator_bulk_enable(ARRAY_SIZE(aic3x->supplies),
aic3x->supplies); aic3x->supplies);
...@@ -1372,6 +1423,10 @@ static int aic3x_probe(struct snd_soc_codec *codec) ...@@ -1372,6 +1423,10 @@ static int aic3x_probe(struct snd_soc_codec *codec)
return 0; return 0;
err_enable: 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); regulator_bulk_free(ARRAY_SIZE(aic3x->supplies), aic3x->supplies);
err_get: err_get:
if (aic3x->gpio_reset >= 0) if (aic3x->gpio_reset >= 0)
...@@ -1384,6 +1439,7 @@ static int aic3x_probe(struct snd_soc_codec *codec) ...@@ -1384,6 +1439,7 @@ static int aic3x_probe(struct snd_soc_codec *codec)
static int aic3x_remove(struct snd_soc_codec *codec) static int aic3x_remove(struct snd_soc_codec *codec)
{ {
struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec); struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
int i;
aic3x_set_bias_level(codec, SND_SOC_BIAS_OFF); aic3x_set_bias_level(codec, SND_SOC_BIAS_OFF);
if (aic3x->gpio_reset >= 0) { if (aic3x->gpio_reset >= 0) {
...@@ -1391,6 +1447,9 @@ static int aic3x_remove(struct snd_soc_codec *codec) ...@@ -1391,6 +1447,9 @@ static int aic3x_remove(struct snd_soc_codec *codec)
gpio_free(aic3x->gpio_reset); gpio_free(aic3x->gpio_reset);
} }
regulator_bulk_disable(ARRAY_SIZE(aic3x->supplies), aic3x->supplies); 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); regulator_bulk_free(ARRAY_SIZE(aic3x->supplies), aic3x->supplies);
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment