diff mbox series

[v2,20/29] ASoC: tas2764: Add SDZ regulator

Message ID 20250218-apple-codec-changes-v2-20-932760fd7e07@gmail.com (mailing list archive)
State New
Headers show
Series ASoC: tas27{64,70}: improve support for Apple codec variants | expand

Commit Message

James Calligeros Feb. 18, 2025, 8:35 a.m. UTC
From: Hector Martin <marcan@marcan.st>

Multiple amps can be connected to the same SDZ GPIO. Using raw GPIOs for
this breaks, as there is no concept of refcounting/sharing. In order to
model these platforms, introduce support for an SDZ "regulator". This
allows us to represent the SDZ GPIO as a simple regulator-fixed, and
then the regulator core takes care of refcounting so that all codecs are
only powered down once all the driver instances are in the suspend
state.

Reviewed-by: Neal Gompa <neal@gompa.dev>
Signed-off-by: Hector Martin <marcan@marcan.st>
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
---
 sound/soc/codecs/tas2764.c | 39 ++++++++++++++++++++-----
 1 file changed, 32 insertions(+), 7 deletions(-)

Comments

Mark Brown Feb. 18, 2025, 3:33 p.m. UTC | #1
On Tue, Feb 18, 2025 at 06:35:54PM +1000, James Calligeros wrote:

> Multiple amps can be connected to the same SDZ GPIO. Using raw GPIOs for
> this breaks, as there is no concept of refcounting/sharing. In order to
> model these platforms, introduce support for an SDZ "regulator". This
> allows us to represent the SDZ GPIO as a simple regulator-fixed, and
> then the regulator core takes care of refcounting so that all codecs are
> only powered down once all the driver instances are in the suspend
> state.

I get that the reference counting that the regulator API does is useful
here but this isn't a regulator so shouldn't be exposed as such,
particularly since this winds up being visible in the DT ABI.  I
could've sworn that someone did some helpers for this case but now I go
looking I can't find them, we certainly don't use any in the regulator
core.

> @@ -210,7 +220,7 @@ static const struct snd_soc_dapm_widget tas2764_dapm_widgets[] = {
>  	SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
>  	SND_SOC_DAPM_OUTPUT("OUT"),
>  	SND_SOC_DAPM_SIGGEN("VMON"),
> -	SND_SOC_DAPM_SIGGEN("IMON")
> +	SND_SOC_DAPM_SIGGEN("IMON"),
>  };
>  
Spurious unrelated change.
James Calligeros Feb. 19, 2025, 4:47 a.m. UTC | #2
Hi Mark,

On Wed, Feb 19, 2025 at 1:33 AM Mark Brown <broonie@kernel.org> wrote:
>
> On Tue, Feb 18, 2025 at 06:35:54PM +1000, James Calligeros wrote:
>
> > Multiple amps can be connected to the same SDZ GPIO. Using raw GPIOs for
> > this breaks, as there is no concept of refcounting/sharing. In order to
> > model these platforms, introduce support for an SDZ "regulator". This
> > allows us to represent the SDZ GPIO as a simple regulator-fixed, and
> > then the regulator core takes care of refcounting so that all codecs are
> > only powered down once all the driver instances are in the suspend
> > state.
>
> I get that the reference counting that the regulator API does is useful
> here but this isn't a regulator so shouldn't be exposed as such,
> particularly since this winds up being visible in the DT ABI.  I
> could've sworn that someone did some helpers for this case but now I go
> looking I can't find them, we certainly don't use any in the regulator
> core.

From what I recall, no attempt at shared GPIO infrastructure has actually
landed. The multiple {de}assertions of SDZ put each chip on the same line
into an unusable state that requires a full power cycle to clear, so
we can't live without
handling the shared GPIO somewhat sensibly.

One alternative off the top of my head is adding a dummy reset controller
to the DTs and integrating it into the ASoC machine driver (which we have
downstream). We could then put the GPIO behind a shared reset line, and hit
that instead of the GPIO. This does seem a little complex/odd, and IIRC we
considered this at some point and decided against it.

Is there any other option that may work here? I'm open to ideas.

Regards,
James
Mark Brown Feb. 20, 2025, 1:45 a.m. UTC | #3
On Wed, Feb 19, 2025 at 02:47:04PM +1000, James Calligeros wrote:
> On Wed, Feb 19, 2025 at 1:33 AM Mark Brown <broonie@kernel.org> wrote:
> > On Tue, Feb 18, 2025 at 06:35:54PM +1000, James Calligeros wrote:

> > I get that the reference counting that the regulator API does is useful
> > here but this isn't a regulator so shouldn't be exposed as such,
> > particularly since this winds up being visible in the DT ABI.  I
> > could've sworn that someone did some helpers for this case but now I go
> > looking I can't find them, we certainly don't use any in the regulator
> > core.

> From what I recall, no attempt at shared GPIO infrastructure has actually
> landed. The multiple {de}assertions of SDZ put each chip on the same line

Yeah, I can't find anything.  Perhaps I was thinking of the reset API,
most of the other users were reset lines so it's plausible someone
started and then just ended up with the reset API instead.

> into an unusable state that requires a full power cycle to clear, so
> we can't live without
> handling the shared GPIO somewhat sensibly.

> One alternative off the top of my head is adding a dummy reset controller
> to the DTs and integrating it into the ASoC machine driver (which we have
> downstream). We could then put the GPIO behind a shared reset line, and hit
> that instead of the GPIO. This does seem a little complex/odd, and IIRC we
> considered this at some point and decided against it.

I'm not sure that's particularly better than the regulator version TBH,
it's still got the problem of showing up in the device ABI.

> Is there any other option that may work here? I'm open to ideas.

Perhaps it's time to bite the bullet and do the shared GPIO API?
regulator could certainly use it (and has a bunch of code, we could
probably just pull that out and wrap an API around it?) and now there's
this too.

You could possibly also open code, but that does beg the question about
the shared API.
diff mbox series

Patch

diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c
index 7b69ab94c4bbd5f074d57a42f71b32f5fd63d560..5d89d47c1667c1067f88169575b7b76e9a25bda4 100644
--- a/sound/soc/codecs/tas2764.c
+++ b/sound/soc/codecs/tas2764.c
@@ -35,6 +35,7 @@  struct tas2764_priv {
 	struct snd_soc_component *component;
 	struct gpio_desc *reset_gpio;
 	struct gpio_desc *sdz_gpio;
+	struct regulator *sdz_reg;
 	struct regmap *regmap;
 	struct device *dev;
 	int irq;
@@ -154,6 +155,8 @@  static int tas2764_codec_suspend(struct snd_soc_component *component)
 	if (tas2764->sdz_gpio)
 		gpiod_set_value_cansleep(tas2764->sdz_gpio, 0);
 
+	regulator_disable(tas2764->sdz_reg);
+
 	regcache_cache_only(tas2764->regmap, true);
 	regcache_mark_dirty(tas2764->regmap);
 
@@ -165,19 +168,26 @@  static int tas2764_codec_resume(struct snd_soc_component *component)
 	struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
 	int ret;
 
+	ret = regulator_enable(tas2764->sdz_reg);
+
+	if (ret) {
+		dev_err(tas2764->dev, "Failed to enable regulator\n");
+		return ret;
+	}
+
 	if (tas2764->sdz_gpio) {
 		gpiod_set_value_cansleep(tas2764->sdz_gpio, 1);
-		usleep_range(1000, 2000);
 	}
 
-	ret = tas2764_update_pwr_ctrl(tas2764);
+	usleep_range(1000, 2000);
 
+	regcache_cache_only(tas2764->regmap, false);
+
+	ret = regcache_sync(tas2764->regmap);
 	if (ret < 0)
 		return ret;
 
-	regcache_cache_only(tas2764->regmap, false);
-
-	return regcache_sync(tas2764->regmap);
+	return tas2764_update_pwr_ctrl(tas2764);
 }
 #else
 #define tas2764_codec_suspend NULL
@@ -210,7 +220,7 @@  static const struct snd_soc_dapm_widget tas2764_dapm_widgets[] = {
 	SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
 	SND_SOC_DAPM_OUTPUT("OUT"),
 	SND_SOC_DAPM_SIGGEN("VMON"),
-	SND_SOC_DAPM_SIGGEN("IMON")
+	SND_SOC_DAPM_SIGGEN("IMON"),
 };
 
 static const struct snd_soc_dapm_route tas2764_audio_map[] = {
@@ -698,11 +708,18 @@  static int tas2764_codec_probe(struct snd_soc_component *component)
 
 	tas2764->component = component;
 
+	ret = regulator_enable(tas2764->sdz_reg);
+	if (ret != 0) {
+		dev_err(tas2764->dev, "Failed to enable regulator: %d\n", ret);
+		return ret;
+	}
+
 	if (tas2764->sdz_gpio) {
 		gpiod_set_value_cansleep(tas2764->sdz_gpio, 1);
-		usleep_range(1000, 2000);
 	}
 
+	usleep_range(1000, 2000);
+
 	tas2764_reset(tas2764);
 	regmap_reinit_cache(tas2764->regmap, &tas2764_i2c_regmap);
 
@@ -777,6 +794,9 @@  static int tas2764_codec_probe(struct snd_soc_component *component)
 
 static void tas2764_codec_remove(struct snd_soc_component *component)
 {
+	struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
+
+	regulator_disable(tas2764->sdz_reg);
 	sysfs_remove_groups(&component->dev->kobj, tas2764_sysfs_groups);
 }
 
@@ -879,6 +899,11 @@  static int tas2764_parse_dt(struct device *dev, struct tas2764_priv *tas2764)
 {
 	int ret = 0;
 
+	tas2764->sdz_reg = devm_regulator_get(dev, "SDZ");
+	if (IS_ERR(tas2764->sdz_reg))
+		return dev_err_probe(dev, PTR_ERR(tas2764->sdz_reg),
+				"Failed to get SDZ supply\n");
+
 	tas2764->reset_gpio = devm_gpiod_get_optional(tas2764->dev, "reset",
 						      GPIOD_OUT_HIGH);
 	if (IS_ERR(tas2764->reset_gpio)) {