[alsa-devel] [PATCH] ASoC: tpa6130a2: fix volume setting when no stream is running
After moving tpa6130a2 power management to DAPM, if chip can be physically powered off (either reset_gpio is defined, or regulator indeed removes power), then volume change no longer works unless chip is on due to a running stream.
Fix that by entering regcache cache_only mode while chip is off.
Move regcache calls to tpa6130a2_power() to get them at driver init time as well.
Signed-off-by: Nikita Yushchenko nikita.yoush@cogentembedded.com --- sound/soc/codecs/tpa6130a2.c | 49 +++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 23 deletions(-)
diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c index f1ea052..3b6faed 100644 --- a/sound/soc/codecs/tpa6130a2.c +++ b/sound/soc/codecs/tpa6130a2.c @@ -52,7 +52,7 @@ struct tpa6130a2_data {
static int tpa6130a2_power(struct tpa6130a2_data *data, bool enable) { - int ret; + int ret = 0, ret2;
if (enable) { ret = regulator_enable(data->supply); @@ -64,20 +64,34 @@ static int tpa6130a2_power(struct tpa6130a2_data *data, bool enable) /* Power on */ if (data->power_gpio >= 0) gpio_set_value(data->power_gpio, 1); + + /* Sync registers */ + regcache_cache_only(data->regmap, false); + ret = regcache_sync(data->regmap); + if (ret != 0) { + dev_err(data->dev, + "Failed to sync registers: %d\n", ret); + goto regcache_sync_failed; + } } else { + /* Powered off device does not retain registers. While device + * is off, any register updates (i.e. volume changes) should + * happen in cache only. + */ + regcache_mark_dirty(data->regmap); +regcache_sync_failed: + regcache_cache_only(data->regmap, true); + /* Power off */ if (data->power_gpio >= 0) gpio_set_value(data->power_gpio, 0);
- ret = regulator_disable(data->supply); - if (ret != 0) { + ret2 = regulator_disable(data->supply); + if (ret2 != 0) { dev_err(data->dev, - "Failed to disable supply: %d\n", ret); - return ret; + "Failed to disable supply: %d\n", ret2); + return ret ? ret : ret2; } - - /* device regs does not match the cache state anymore */ - regcache_mark_dirty(data->regmap); }
return ret; @@ -88,25 +102,14 @@ static int tpa6130a2_power_event(struct snd_soc_dapm_widget *w, { struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm); struct tpa6130a2_data *data = snd_soc_component_get_drvdata(c); - int ret;
- /* before widget power up */ if (SND_SOC_DAPM_EVENT_ON(event)) { - /* Turn on the chip */ - tpa6130a2_power(data, true); - /* Sync the registers */ - ret = regcache_sync(data->regmap); - if (ret < 0) { - dev_err(c->dev, "Failed to initialize chip\n"); - tpa6130a2_power(data, false); - return ret; - } - /* after widget power down */ + /* Before widget power up: turn chip on, sync registers */ + return tpa6130a2_power(data, true); } else { - tpa6130a2_power(data, false); + /* After widget power down: turn chip off */ + return tpa6130a2_power(data, false); } - - return 0; }
/*
On Thu, Sep 22, 2016 at 01:10:40PM +0300, Nikita Yushchenko wrote:
ret = regulator_disable(data->supply);
if (ret != 0) {
ret2 = regulator_disable(data->supply);
if (ret2 != 0) { dev_err(data->dev,
"Failed to disable supply: %d\n", ret);
return ret;
"Failed to disable supply: %d\n", ret2);
}return ret ? ret : ret2;
The ternery operator to save the error handling block is a bit too cute to be clear, it'd be better to just handle each error directly. Can you please send a followup patch fixing this?
Code undo operations in power enable errror path explicitly, instead of reusing power disable path and playing with return values there.
Signed-off-by: Nikita Yushchenko nikita.yoush@cogentembedded.com --- I doubt that copying 7 lines into error path is better than having a tiny logic with return values, but still here it is.
sound/soc/codecs/tpa6130a2.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c index 3b6faed91d7e..3712db0881d0 100644 --- a/sound/soc/codecs/tpa6130a2.c +++ b/sound/soc/codecs/tpa6130a2.c @@ -71,7 +71,14 @@ static int tpa6130a2_power(struct tpa6130a2_data *data, bool enable) if (ret != 0) { dev_err(data->dev, "Failed to sync registers: %d\n", ret); - goto regcache_sync_failed; + regcache_cache_only(data->regmap, true); + if (data->power_gpio >= 0) + gpio_set_value(data->power_gpio, 0); + ret2 = regulator_disable(data->supply); + if (ret2 != 0) + dev_err(data->dev, + "Failed to disable supply: %d\n", ret2); + return ret; } } else { /* Powered off device does not retain registers. While device @@ -79,18 +86,17 @@ static int tpa6130a2_power(struct tpa6130a2_data *data, bool enable) * happen in cache only. */ regcache_mark_dirty(data->regmap); -regcache_sync_failed: regcache_cache_only(data->regmap, true);
/* Power off */ if (data->power_gpio >= 0) gpio_set_value(data->power_gpio, 0);
- ret2 = regulator_disable(data->supply); - if (ret2 != 0) { + ret = regulator_disable(data->supply); + if (ret != 0) { dev_err(data->dev, - "Failed to disable supply: %d\n", ret2); - return ret ? ret : ret2; + "Failed to disable supply: %d\n", ret); + return ret; } }
On Mon, Sep 26, 2016 at 01:35:50PM +0300, Nikita Yushchenko wrote:
I doubt that copying 7 lines into error path is better than having a tiny logic with return values, but still here it is.
Consider what happens if someone wants to change the code...
26.09.2016 19:08, Mark Brown пишет:
On Mon, Sep 26, 2016 at 01:35:50PM +0300, Nikita Yushchenko wrote:
I doubt that copying 7 lines into error path is better than having a tiny logic with return values, but still here it is.
Consider what happens if someone wants to change the code...
I believe that "undo poweron" and "poweroff" sequences still will have to remain the same. This having two copies of code is more error-prone than having one.
Nikita
The patch
ASoC: tpa6130a2: unmerge power enable error path from power disable path
has been applied to the asoc tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From 020eab35390b976e6b93b61805002d3e2cd195de Mon Sep 17 00:00:00 2001
From: Nikita Yushchenko nikita.yoush@cogentembedded.com Date: Mon, 26 Sep 2016 13:35:50 +0300 Subject: [PATCH] ASoC: tpa6130a2: unmerge power enable error path from power disable path
Code undo operations in power enable errror path explicitly, instead of reusing power disable path and playing with return values there.
Signed-off-by: Nikita Yushchenko nikita.yoush@cogentembedded.com Signed-off-by: Mark Brown broonie@kernel.org --- sound/soc/codecs/tpa6130a2.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c index 3b6faed91d7e..3712db0881d0 100644 --- a/sound/soc/codecs/tpa6130a2.c +++ b/sound/soc/codecs/tpa6130a2.c @@ -71,7 +71,14 @@ static int tpa6130a2_power(struct tpa6130a2_data *data, bool enable) if (ret != 0) { dev_err(data->dev, "Failed to sync registers: %d\n", ret); - goto regcache_sync_failed; + regcache_cache_only(data->regmap, true); + if (data->power_gpio >= 0) + gpio_set_value(data->power_gpio, 0); + ret2 = regulator_disable(data->supply); + if (ret2 != 0) + dev_err(data->dev, + "Failed to disable supply: %d\n", ret2); + return ret; } } else { /* Powered off device does not retain registers. While device @@ -79,18 +86,17 @@ static int tpa6130a2_power(struct tpa6130a2_data *data, bool enable) * happen in cache only. */ regcache_mark_dirty(data->regmap); -regcache_sync_failed: regcache_cache_only(data->regmap, true);
/* Power off */ if (data->power_gpio >= 0) gpio_set_value(data->power_gpio, 0);
- ret2 = regulator_disable(data->supply); - if (ret2 != 0) { + ret = regulator_disable(data->supply); + if (ret != 0) { dev_err(data->dev, - "Failed to disable supply: %d\n", ret2); - return ret ? ret : ret2; + "Failed to disable supply: %d\n", ret); + return ret; } }
participants (2)
-
Mark Brown
-
Nikita Yushchenko