[alsa-devel] [PATCH 1/3] ASoC: Provide REGULATOR_SUPPLY widget type
Modern devices allow systems to enable and disable individual supplies on the device, allowing additional power saving by switching off regulators which power portions of the device which are not currently in use. Add a new SND_SOC_DAPM_REGULATOR_SUPPLY widget type factoring out the code for managing such widgets from individual drivers.
The widget name will be used as the supply name when requesting the regulator from the regulator API.
Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- include/sound/soc-dapm.h | 9 ++++++++ sound/soc/soc-dapm.c | 51 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 2 deletions(-)
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index d26a9b7..bfa0d3c 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -243,6 +243,10 @@ { .id = snd_soc_dapm_supply, .name = wname, .reg = wreg, \ .shift = wshift, .invert = winvert, .event = wevent, \ .event_flags = wflags} +#define SND_SOC_DAPM_REGULATOR_SUPPLY(wname, wdelay) \ +{ .id = snd_soc_dapm_regulator_supply, .name = wname, \ + .reg = SND_SOC_NOPM, .shift = wdelay, .event = dapm_regulator_event, \ + .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD }
/* dapm kcontrol types */ #define SOC_DAPM_SINGLE(xname, reg, shift, max, invert) \ @@ -322,6 +326,8 @@ struct snd_soc_dapm_context;
int dapm_reg_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); +int dapm_regulator_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event);
/* dapm controls */ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, @@ -411,6 +417,7 @@ enum snd_soc_dapm_type { snd_soc_dapm_pre, /* machine specific pre widget - exec first */ snd_soc_dapm_post, /* machine specific post widget - exec last */ snd_soc_dapm_supply, /* power/clock supply */ + snd_soc_dapm_regulator_supply, /* external regulator */ snd_soc_dapm_aif_in, /* audio interface input */ snd_soc_dapm_aif_out, /* audio interface output */ snd_soc_dapm_siggen, /* signal generator */ @@ -465,6 +472,8 @@ struct snd_soc_dapm_widget { struct list_head list; struct snd_soc_dapm_context *dapm;
+ void *priv; /* widget specific data */ + /* dapm control */ short reg; /* negative reg = no direct dapm */ unsigned char shift; /* bits to shift */ diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 541bcbf..131508a 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -40,6 +40,7 @@ #include <linux/jiffies.h> #include <linux/debugfs.h> #include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> #include <linux/slab.h> #include <sound/core.h> #include <sound/pcm.h> @@ -55,6 +56,7 @@ static int dapm_up_seq[] = { [snd_soc_dapm_pre] = 0, [snd_soc_dapm_supply] = 1, + [snd_soc_dapm_regulator_supply] = 1, [snd_soc_dapm_micbias] = 2, [snd_soc_dapm_aif_in] = 3, [snd_soc_dapm_aif_out] = 3, @@ -90,6 +92,7 @@ static int dapm_down_seq[] = { [snd_soc_dapm_value_mux] = 9, [snd_soc_dapm_aif_in] = 10, [snd_soc_dapm_aif_out] = 10, + [snd_soc_dapm_regulator_supply] = 11, [snd_soc_dapm_supply] = 11, [snd_soc_dapm_post] = 12, }; @@ -352,6 +355,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, case snd_soc_dapm_micbias: case snd_soc_dapm_vmid: case snd_soc_dapm_supply: + case snd_soc_dapm_regulator_supply: case snd_soc_dapm_aif_in: case snd_soc_dapm_aif_out: case snd_soc_dapm_hp: @@ -680,8 +684,13 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget)
DAPM_UPDATE_STAT(widget, path_checks);
- if (widget->id == snd_soc_dapm_supply) + switch (widget->id) { + case snd_soc_dapm_supply: + case snd_soc_dapm_regulator_supply: return 0; + default: + break; + }
switch (widget->id) { case snd_soc_dapm_adc: @@ -745,8 +754,13 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget)
DAPM_UPDATE_STAT(widget, path_checks);
- if (widget->id == snd_soc_dapm_supply) + switch (widget->id) { + case snd_soc_dapm_supply: + case snd_soc_dapm_regulator_supply: return 0; + default: + break; + }
/* active stream ? */ switch (widget->id) { @@ -828,6 +842,19 @@ int dapm_reg_event(struct snd_soc_dapm_widget *w, } EXPORT_SYMBOL_GPL(dapm_reg_event);
+/* + * Handler for regulator supply widget. + */ +int dapm_regulator_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) + return regulator_enable(w->priv); + else + return regulator_disable_deferred(w->priv, w->shift); +} +EXPORT_SYMBOL_GPL(dapm_regulator_event); + static int dapm_widget_power_check(struct snd_soc_dapm_widget *w) { if (w->power_checked) @@ -1308,6 +1335,7 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power, } switch (w->id) { case snd_soc_dapm_supply: + case snd_soc_dapm_regulator_supply: /* Supplies can't affect their outputs, only their inputs */ break; default: @@ -1411,6 +1439,7 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) */ switch (w->id) { case snd_soc_dapm_supply: + case snd_soc_dapm_regulator_supply: case snd_soc_dapm_micbias: if (d->target_bias_level < SND_SOC_BIAS_STANDBY) d->target_bias_level = SND_SOC_BIAS_STANDBY; @@ -1769,6 +1798,7 @@ static ssize_t dapm_widget_show(struct device *dev, case snd_soc_dapm_mixer: case snd_soc_dapm_mixer_named_ctl: case snd_soc_dapm_supply: + case snd_soc_dapm_regulator_supply: if (w->name) count += sprintf(buf + count, "%s: %s\n", w->name, w->power ? "On":"Off"); @@ -2007,6 +2037,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, case snd_soc_dapm_pre: case snd_soc_dapm_post: case snd_soc_dapm_supply: + case snd_soc_dapm_regulator_supply: case snd_soc_dapm_aif_in: case snd_soc_dapm_aif_out: list_add(&path->list, &dapm->card->paths); @@ -2673,10 +2704,25 @@ int snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, { struct snd_soc_dapm_widget *w; size_t name_len; + int ret;
if ((w = dapm_cnew_widget(widget)) == NULL) return -ENOMEM;
+ switch (w->id) { + case snd_soc_dapm_regulator_supply: + w->priv = devm_regulator_get(dapm->dev, w->name); + if (IS_ERR(w->priv)) { + ret = PTR_ERR(w->priv); + dev_err(dapm->dev, "Failed to request %s: %d\n", + w->name, ret); + return ret; + } + break; + default: + break; + } + name_len = strlen(widget->name) + 1; if (dapm->codec && dapm->codec->name_prefix) name_len += 1 + strlen(dapm->codec->name_prefix); @@ -2722,6 +2768,7 @@ int snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, w->power_check = dapm_generic_check_power; break; case snd_soc_dapm_supply: + case snd_soc_dapm_regulator_supply: w->power_check = dapm_supply_check_power; break; default:
Nice code saving.
Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/codecs/wm8996.c | 32 +++++--------------------------- 1 files changed, 5 insertions(+), 27 deletions(-)
diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index 390ccdd..ed7af8c 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -73,7 +73,6 @@ struct wm8996_priv {
struct regulator_bulk_data supplies[WM8996_NUM_SUPPLIES]; struct notifier_block disable_nb[WM8996_NUM_SUPPLIES]; - struct regulator *cpvdd; int bg_ena;
struct wm8996_pdata pdata; @@ -792,29 +791,18 @@ static int bg_event(struct snd_soc_dapm_widget *w, static int cp_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { - struct snd_soc_codec *codec = w->codec; - struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec); int ret = 0;
switch (event) { - case SND_SOC_DAPM_PRE_PMU: - ret = regulator_enable(wm8996->cpvdd); - if (ret != 0) - dev_err(codec->dev, "Failed to enable CPVDD: %d\n", - ret); - break; case SND_SOC_DAPM_POST_PMU: msleep(5); break; - case SND_SOC_DAPM_POST_PMD: - regulator_disable_deferred(wm8996->cpvdd, 20); - break; default: BUG(); ret = -EINVAL; }
- return ret; + return 0; }
static int rmv_short_event(struct snd_soc_dapm_widget *w, @@ -1116,12 +1104,12 @@ SND_SOC_DAPM_INPUT("IN2RP"), SND_SOC_DAPM_INPUT("DMIC1DAT"), SND_SOC_DAPM_INPUT("DMIC2DAT"),
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20), SND_SOC_DAPM_SUPPLY_S("SYSCLK", 1, WM8996_AIF_CLOCKING_1, 0, 0, NULL, 0), SND_SOC_DAPM_SUPPLY_S("SYSDSPCLK", 2, WM8996_CLOCKING_1, 1, 0, NULL, 0), SND_SOC_DAPM_SUPPLY_S("AIFCLK", 2, WM8996_CLOCKING_1, 2, 0, NULL, 0), SND_SOC_DAPM_SUPPLY_S("Charge Pump", 2, WM8996_CHARGE_PUMP_1, 15, 0, cp_event, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_SUPPLY("Bandgap", SND_SOC_NOPM, 0, 0, bg_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("LDO2", WM8996_POWER_MANAGEMENT_2, 1, 0, NULL, 0), @@ -1280,6 +1268,7 @@ static const struct snd_soc_dapm_route wm8996_dapm_routes[] = { { "AIFCLK", NULL, "SYSCLK" }, { "SYSDSPCLK", NULL, "SYSCLK" }, { "Charge Pump", NULL, "SYSCLK" }, + { "Charge Pump", NULL, "CPVDD" },
{ "MICB1", NULL, "LDO2" }, { "MICB1", NULL, "MICB1 Audio" }, @@ -3038,7 +3027,6 @@ static int wm8996_remove(struct snd_soc_codec *codec) for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++) regulator_unregister_notifier(wm8996->supplies[i].consumer, &wm8996->disable_nb[i]); - regulator_put(wm8996->cpvdd); regulator_bulk_free(ARRAY_SIZE(wm8996->supplies), wm8996->supplies);
return 0; @@ -3160,18 +3148,11 @@ static __devinit int wm8996_i2c_probe(struct i2c_client *i2c, goto err_gpio; }
- wm8996->cpvdd = regulator_get(&i2c->dev, "CPVDD"); - if (IS_ERR(wm8996->cpvdd)) { - ret = PTR_ERR(wm8996->cpvdd); - dev_err(&i2c->dev, "Failed to get CPVDD: %d\n", ret); - goto err_get; - } - ret = regulator_bulk_enable(ARRAY_SIZE(wm8996->supplies), wm8996->supplies); if (ret != 0) { dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); - goto err_cpvdd; + goto err_get; }
if (wm8996->pdata.ldo_ena > 0) { @@ -3233,8 +3214,6 @@ err_enable: if (wm8996->pdata.ldo_ena > 0) gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0); regulator_bulk_disable(ARRAY_SIZE(wm8996->supplies), wm8996->supplies); -err_cpvdd: - regulator_put(wm8996->cpvdd); err_get: regulator_bulk_free(ARRAY_SIZE(wm8996->supplies), wm8996->supplies); err_gpio: @@ -3251,7 +3230,6 @@ static __devexit int wm8996_i2c_remove(struct i2c_client *client)
snd_soc_unregister_codec(&client->dev); wm8996_free_gpio(wm8996); - regulator_put(wm8996->cpvdd); regulator_bulk_free(ARRAY_SIZE(wm8996->supplies), wm8996->supplies); regmap_exit(wm8996->regmap); if (wm8996->pdata.ldo_ena > 0) {
Saves a nice block of code.
Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/codecs/wm5100.c | 131 ++++----------------------------------------- 1 files changed, 10 insertions(+), 121 deletions(-)
diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index f916043..ba852f2 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -56,9 +56,6 @@ struct wm5100_priv { struct snd_soc_codec *codec;
struct regulator_bulk_data core_supplies[WM5100_NUM_CORE_SUPPLIES]; - struct regulator *cpvdd; - struct regulator *dbvdd2; - struct regulator *dbvdd3;
int rev;
@@ -813,85 +810,6 @@ static int wm5100_out_ev(struct snd_soc_dapm_widget *w, return 0; }
-static int wm5100_cp_ev(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, - int event) -{ - struct snd_soc_codec *codec = w->codec; - struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec); - int ret; - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - ret = regulator_enable(wm5100->cpvdd); - if (ret != 0) { - dev_err(codec->dev, "Failed to enable CPVDD: %d\n", - ret); - return ret; - } - return ret; - - case SND_SOC_DAPM_POST_PMD: - ret = regulator_disable_deferred(wm5100->cpvdd, 20); - if (ret != 0) { - dev_err(codec->dev, "Failed to disable CPVDD: %d\n", - ret); - return ret; - } - return ret; - - default: - BUG(); - return 0; - } -} - -static int wm5100_dbvdd_ev(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, - int event) -{ - struct snd_soc_codec *codec = w->codec; - struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec); - struct regulator *regulator; - int ret; - - switch (w->shift) { - case 2: - regulator = wm5100->dbvdd2; - break; - case 3: - regulator = wm5100->dbvdd3; - break; - default: - BUG(); - return 0; - } - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - ret = regulator_enable(regulator); - if (ret != 0) { - dev_err(codec->dev, "Failed to enable DBVDD%d: %d\n", - w->shift, ret); - return ret; - } - return ret; - - case SND_SOC_DAPM_POST_PMD: - ret = regulator_disable(regulator); - if (ret != 0) { - dev_err(codec->dev, "Failed to enable DBVDD%d: %d\n", - w->shift, ret); - return ret; - } - return ret; - - default: - BUG(); - return 0; - } -} - static void wm5100_log_status3(struct wm5100_priv *wm5100, int val) { if (val & WM5100_SPK_SHUTDOWN_WARN_EINT) @@ -972,18 +890,16 @@ SND_SOC_DAPM_SUPPLY("SYSCLK", WM5100_CLOCKING_3, WM5100_SYSCLK_ENA_SHIFT, 0, SND_SOC_DAPM_SUPPLY("ASYNCCLK", WM5100_CLOCKING_6, WM5100_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20), +SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0), + SND_SOC_DAPM_SUPPLY("CP1", WM5100_HP_CHARGE_PUMP_1, WM5100_CP1_ENA_SHIFT, 0, - wm5100_cp_ev, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + NULL, 0), SND_SOC_DAPM_SUPPLY("CP2", WM5100_MIC_CHARGE_PUMP_1, WM5100_CP2_ENA_SHIFT, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("CP2 Active", WM5100_MIC_CHARGE_PUMP_1, - WM5100_CP2_BYPASS_SHIFT, 1, wm5100_cp_ev, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), -SND_SOC_DAPM_SUPPLY("DBVDD2", SND_SOC_NOPM, 2, 0, wm5100_dbvdd_ev, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), -SND_SOC_DAPM_SUPPLY("DBVDD3", SND_SOC_NOPM, 3, 0, wm5100_dbvdd_ev, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + WM5100_CP2_BYPASS_SHIFT, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("MICBIAS1", WM5100_MIC_BIAS_CTRL_1, WM5100_MICB1_ENA_SHIFT, 0, NULL, 0), @@ -1215,6 +1131,9 @@ SND_SOC_DAPM_POST("Post", wm5100_post_ev), };
static const struct snd_soc_dapm_route wm5100_dapm_routes[] = { + { "CP1", NULL, "CPVDD" }, + { "CP2 Active", NULL, "CPVDD" }, + { "IN1L", NULL, "SYSCLK" }, { "IN1R", NULL, "SYSCLK" }, { "IN2L", NULL, "SYSCLK" }, @@ -2729,33 +2648,12 @@ static __devinit int wm5100_i2c_probe(struct i2c_client *i2c, goto err_regmap; }
- wm5100->cpvdd = regulator_get(&i2c->dev, "CPVDD"); - if (IS_ERR(wm5100->cpvdd)) { - ret = PTR_ERR(wm5100->cpvdd); - dev_err(&i2c->dev, "Failed to get CPVDD: %d\n", ret); - goto err_core; - } - - wm5100->dbvdd2 = regulator_get(&i2c->dev, "DBVDD2"); - if (IS_ERR(wm5100->dbvdd2)) { - ret = PTR_ERR(wm5100->dbvdd2); - dev_err(&i2c->dev, "Failed to get DBVDD2: %d\n", ret); - goto err_cpvdd; - } - - wm5100->dbvdd3 = regulator_get(&i2c->dev, "DBVDD3"); - if (IS_ERR(wm5100->dbvdd3)) { - ret = PTR_ERR(wm5100->dbvdd3); - dev_err(&i2c->dev, "Failed to get DBVDD2: %d\n", ret); - goto err_dbvdd2; - } - ret = regulator_bulk_enable(ARRAY_SIZE(wm5100->core_supplies), wm5100->core_supplies); if (ret != 0) { dev_err(&i2c->dev, "Failed to enable core supplies: %d\n", ret); - goto err_dbvdd3; + goto err_core; }
if (wm5100->pdata.ldo_ena) { @@ -2924,12 +2822,6 @@ err_ldo: err_enable: regulator_bulk_disable(ARRAY_SIZE(wm5100->core_supplies), wm5100->core_supplies); -err_dbvdd3: - regulator_put(wm5100->dbvdd3); -err_dbvdd2: - regulator_put(wm5100->dbvdd2); -err_cpvdd: - regulator_put(wm5100->cpvdd); err_core: regulator_bulk_free(ARRAY_SIZE(wm5100->core_supplies), wm5100->core_supplies); @@ -2955,9 +2847,6 @@ static __devexit int wm5100_i2c_remove(struct i2c_client *i2c) gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 0); gpio_free(wm5100->pdata.ldo_ena); } - regulator_put(wm5100->dbvdd3); - regulator_put(wm5100->dbvdd2); - regulator_put(wm5100->cpvdd); regulator_bulk_free(ARRAY_SIZE(wm5100->core_supplies), wm5100->core_supplies); regmap_exit(wm5100->regmap);
participants (1)
-
Mark Brown