All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/2] regulator: Implement deferred disable support
@ 2011-09-12 16:59 Mark Brown
  2011-09-12 16:59 ` [PATCH 2/2] ASoC: Disable WM8996 CPVDD supply when not in use Mark Brown
  0 siblings, 1 reply; 5+ messages in thread
From: Mark Brown @ 2011-09-12 16:59 UTC (permalink / raw)
  To: Liam Girdwood; +Cc: alsa-devel, patches, Mark Brown

It is a reasonably common pattern for hardware to require some delay after
being quiesced before the disable has finalised, especially in mixed signal
devices. For example, an active discharge may be required to ensure that
the circuit starts up again in a known state. Avoid having to implement
such delays in the regulator API by providing regulator_deferred_disable()
which will do a regulator_disable() a specified number of milliseconds
after it is called.

Due to the reference counting done on regulators a deferred disable can
be cancelled by doing another regulator_enable().

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
---
 drivers/regulator/core.c           |   59 ++++++++++++++++++++++++++++++++++++
 include/linux/regulator/consumer.h |    7 ++++
 include/linux/regulator/driver.h   |    3 ++
 3 files changed, 69 insertions(+), 0 deletions(-)

diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 9a33fe2..026f3d4 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -1552,6 +1552,63 @@ int regulator_force_disable(struct regulator *regulator)
 }
 EXPORT_SYMBOL_GPL(regulator_force_disable);
 
+static void regulator_disable_work(struct work_struct *work)
+{
+	struct regulator_dev *rdev = container_of(work, struct regulator_dev,
+						  disable_work.work);
+	int count, i, ret;
+
+	mutex_lock(&rdev->mutex);
+
+	BUG_ON(!rdev->deferred_disables);
+
+	count = rdev->deferred_disables;
+	rdev->deferred_disables = 0;
+
+	for (i = 0; i < count; i++) {
+		ret = _regulator_disable(rdev);
+		if (ret != 0)
+			rdev_err(rdev, "Deferred disable failed: %d\n", ret);
+	}
+
+	mutex_unlock(&rdev->mutex);
+
+	if (rdev->supply) {
+		for (i = 0; i < count; i++) {
+			ret = regulator_disable(rdev->supply);
+			if (ret != 0) {
+				rdev_err(rdev,
+					 "Supply disable failed: %d\n", ret);
+			}
+		}
+	}
+}
+
+/**
+ * regulator_disable_deferred - disable regulator output with delay
+ * @regulator: regulator source
+ * @ms: miliseconds until the regulator is disabled
+ *
+ * Execute regulator_disable() on the regulator after a delay.  This
+ * is intended for use with devices that require some time to quiesce.
+ *
+ * NOTE: this will only disable the regulator output if no other consumer
+ * devices have it enabled, the regulator device supports disabling and
+ * machine constraints permit this operation.
+ */
+int regulator_disable_deferred(struct regulator *regulator, int ms)
+{
+	struct regulator_dev *rdev = regulator->rdev;
+
+	mutex_lock(&rdev->mutex);
+	rdev->deferred_disables++;
+	mutex_unlock(&rdev->mutex);
+
+	return schedule_delayed_work(&rdev->disable_work,
+				     msecs_to_jiffies(ms));
+}
+EXPORT_SYMBOL_GPL(regulator_disable_deferred);
+
 static int _regulator_is_enabled(struct regulator_dev *rdev)
 {
 	/* If we don't know then assume that the regulator is always on */
@@ -2622,6 +2679,7 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
 	INIT_LIST_HEAD(&rdev->consumer_list);
 	INIT_LIST_HEAD(&rdev->list);
 	BLOCKING_INIT_NOTIFIER_HEAD(&rdev->notifier);
+	INIT_DELAYED_WORK(&rdev->disable_work, regulator_disable_work);
 
 	/* preform any regulator specific init */
 	if (init_data->regulator_init) {
@@ -2729,6 +2787,7 @@ void regulator_unregister(struct regulator_dev *rdev)
 #ifdef CONFIG_DEBUG_FS
 	debugfs_remove_recursive(rdev->debugfs);
 #endif
+	flush_work_sync(&rdev->disable_work.work);
 	WARN_ON(rdev->open_count);
 	unset_regulator_supplies(rdev);
 	list_del(&rdev->list);
diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h
index b47771a..f7756d1 100644
--- a/include/linux/regulator/consumer.h
+++ b/include/linux/regulator/consumer.h
@@ -141,6 +141,7 @@ int regulator_enable(struct regulator *regulator);
 int regulator_disable(struct regulator *regulator);
 int regulator_force_disable(struct regulator *regulator);
 int regulator_is_enabled(struct regulator *regulator);
+int regulator_disable_deferred(struct regulator *regulator, int ms);
 
 int regulator_bulk_get(struct device *dev, int num_consumers,
 		       struct regulator_bulk_data *consumers);
@@ -211,6 +212,12 @@ static inline int regulator_disable(struct regulator *regulator)
 	return 0;
 }
 
+static inline int regulator_disable_deferred(struct regulator *regulator,
+					     int ms)
+{
+	return 0;
+}
+
 static inline int regulator_is_enabled(struct regulator *regulator)
 {
 	return 1;
diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h
index 03a1798..52c89ae 100644
--- a/include/linux/regulator/driver.h
+++ b/include/linux/regulator/driver.h
@@ -200,6 +200,9 @@ struct regulator_dev {
 	struct regulation_constraints *constraints;
 	struct regulator *supply;	/* for tree */
 
+	struct delayed_work disable_work;
+	int deferred_disables;
+
 	void *reg_data;		/* regulator_dev data */
 
 #ifdef CONFIG_DEBUG_FS
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 2/2] ASoC: Disable WM8996 CPVDD supply when not in use
  2011-09-12 16:59 [PATCH 1/2] regulator: Implement deferred disable support Mark Brown
@ 2011-09-12 16:59 ` Mark Brown
  2011-09-13 14:53   ` Liam Girdwood
  0 siblings, 1 reply; 5+ messages in thread
From: Mark Brown @ 2011-09-12 16:59 UTC (permalink / raw)
  To: Liam Girdwood; +Cc: alsa-devel, patches, Mark Brown

The WM8996 only requires CPVDD when the charge pump is active so control
it separately to the other supplies, only enabling it when the charge pump
is active. This will result in a small power saving on systems which are
able to provide independent software control of the supply.

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
---
 sound/soc/codecs/wm8996.c |   38 ++++++++++++++++++++++++++++++--------
 1 files changed, 30 insertions(+), 8 deletions(-)

diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c
index b3b0a9d..0fed436 100644
--- a/sound/soc/codecs/wm8996.c
+++ b/sound/soc/codecs/wm8996.c
@@ -41,12 +41,11 @@
 #define HPOUT2L 4
 #define HPOUT2R 8
 
-#define WM8996_NUM_SUPPLIES 4
+#define WM8996_NUM_SUPPLIES 3
 static const char *wm8996_supply_names[WM8996_NUM_SUPPLIES] = {
 	"DBVDD",
 	"AVDD1",
 	"AVDD2",
-	"CPVDD",
 };
 
 struct wm8996_priv {
@@ -71,6 +70,7 @@ struct wm8996_priv {
 
 	struct regulator_bulk_data supplies[WM8996_NUM_SUPPLIES];
 	struct notifier_block disable_nb[WM8996_NUM_SUPPLIES];
+	struct regulator *cpvdd;
 
 	struct wm8996_pdata pdata;
 
@@ -112,7 +112,6 @@ static int wm8996_regulator_event_##n(struct notifier_block *nb, \
 WM8996_REGULATOR_EVENT(0)
 WM8996_REGULATOR_EVENT(1)
 WM8996_REGULATOR_EVENT(2)
-WM8996_REGULATOR_EVENT(3)
 
 static const u16 wm8996_reg[WM8996_MAX_REGISTER] = {
 	[WM8996_SOFTWARE_RESET] = 0x8996,
@@ -670,16 +669,29 @@ SOC_SINGLE_TLV("DSP2 EQ B5 Volume", WM8996_DSP2_RX_EQ_GAINS_2, 6, 31, 0,
 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();
-		return -EINVAL;
+		ret = -EINVAL;
 	}
 
-	return 0;
+	return ret;
 }
 
 static int rmv_short_event(struct snd_soc_dapm_widget *w,
@@ -988,7 +1000,8 @@ 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_POST_PMU),
+		      SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		      SND_SOC_DAPM_POST_PMD),
 
 SND_SOC_DAPM_SUPPLY("LDO2", WM8996_POWER_MANAGEMENT_2, 1, 0, NULL, 0),
 SND_SOC_DAPM_SUPPLY("MICB1 Audio", WM8996_MICBIAS_1, 4, 1, NULL, 0),
@@ -2639,7 +2652,13 @@ static int wm8996_probe(struct snd_soc_codec *codec)
 	wm8996->disable_nb[0].notifier_call = wm8996_regulator_event_0;
 	wm8996->disable_nb[1].notifier_call = wm8996_regulator_event_1;
 	wm8996->disable_nb[2].notifier_call = wm8996_regulator_event_2;
-	wm8996->disable_nb[3].notifier_call = wm8996_regulator_event_3;
+
+	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;
+	}
 
 	/* This should really be moved into the regulator core */
 	for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++) {
@@ -2656,7 +2675,7 @@ static int wm8996_probe(struct snd_soc_codec *codec)
 				    wm8996->supplies);
 	if (ret != 0) {
 		dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
-		goto err_get;
+		goto err_cpvdd;
 	}
 
 	if (wm8996->pdata.ldo_ena >= 0) {
@@ -2899,6 +2918,8 @@ err_enable:
 		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:
@@ -2922,6 +2943,7 @@ 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;
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH 2/2] ASoC: Disable WM8996 CPVDD supply when not in use
  2011-09-12 16:59 ` [PATCH 2/2] ASoC: Disable WM8996 CPVDD supply when not in use Mark Brown
@ 2011-09-13 14:53   ` Liam Girdwood
  2011-09-13 15:15     ` Mark Brown
  0 siblings, 1 reply; 5+ messages in thread
From: Liam Girdwood @ 2011-09-13 14:53 UTC (permalink / raw)
  To: Mark Brown; +Cc: alsa-devel, patches

On Mon, 2011-09-12 at 17:59 +0100, Mark Brown wrote:
> The WM8996 only requires CPVDD when the charge pump is active so control
> it separately to the other supplies, only enabling it when the charge pump
> is active. This will result in a small power saving on systems which are
> able to provide independent software control of the supply.
> 
> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
> ---
>  sound/soc/codecs/wm8996.c |   38 ++++++++++++++++++++++++++++++--------
>  1 files changed, 30 insertions(+), 8 deletions(-)

Acked-by: Liam Girdwood <lrg@ti.com>

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 2/2] ASoC: Disable WM8996 CPVDD supply when not in use
  2011-09-13 14:53   ` Liam Girdwood
@ 2011-09-13 15:15     ` Mark Brown
  2011-09-13 16:12       ` Liam Girdwood
  0 siblings, 1 reply; 5+ messages in thread
From: Mark Brown @ 2011-09-13 15:15 UTC (permalink / raw)
  To: Liam Girdwood; +Cc: alsa-devel, patches

On Tue, Sep 13, 2011 at 03:53:20PM +0100, Liam Girdwood wrote:
> On Mon, 2011-09-12 at 17:59 +0100, Mark Brown wrote:

> >  sound/soc/codecs/wm8996.c |   38 ++++++++++++++++++++++++++++++--------
> >  1 files changed, 30 insertions(+), 8 deletions(-)

> Acked-by: Liam Girdwood <lrg@ti.com>

Should I apply the regulator change to ASoC as well?  Obviously this
won't build without that.  Providing there aren't any merge issues this
could go via either tree (or I could just hang onto this until after the
merge window, it's not exactly urgent).

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 2/2] ASoC: Disable WM8996 CPVDD supply when not in use
  2011-09-13 15:15     ` Mark Brown
@ 2011-09-13 16:12       ` Liam Girdwood
  0 siblings, 0 replies; 5+ messages in thread
From: Liam Girdwood @ 2011-09-13 16:12 UTC (permalink / raw)
  To: Mark Brown; +Cc: alsa-devel, patches

On Tue, 2011-09-13 at 16:15 +0100, Mark Brown wrote:
> On Tue, Sep 13, 2011 at 03:53:20PM +0100, Liam Girdwood wrote:
> > On Mon, 2011-09-12 at 17:59 +0100, Mark Brown wrote:
> 
> > >  sound/soc/codecs/wm8996.c |   38 ++++++++++++++++++++++++++++++--------
> > >  1 files changed, 30 insertions(+), 8 deletions(-)
> 
> > Acked-by: Liam Girdwood <lrg@ti.com>
> 
> Should I apply the regulator change to ASoC as well?  Obviously this
> won't build without that.  Providing there aren't any merge issues this
> could go via either tree (or I could just hang onto this until after the
> merge window, it's not exactly urgent).

Yeah, lets take 1/2 via ASoC. It has my Ack.

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2011-09-13 16:12 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-09-12 16:59 [PATCH 1/2] regulator: Implement deferred disable support Mark Brown
2011-09-12 16:59 ` [PATCH 2/2] ASoC: Disable WM8996 CPVDD supply when not in use Mark Brown
2011-09-13 14:53   ` Liam Girdwood
2011-09-13 15:15     ` Mark Brown
2011-09-13 16:12       ` Liam Girdwood

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.