[PATCH v1 0/4] Add tfa9897 speaker/receiver modes switching
Two tfa9897 are used in tandem on some devices (eg. alcatel-idol347) to drive two speakers, for both receiver (earpiece) and speaker modes.
This series: 1. adds a control to allow enabling only one tfa9897/speaker (for the typical phone-call usecase) 2. adds a control to switch between speaker and receiver modes 3. adds support for an optional gpio also used to switch modes
Stephan Gerhold (1): ASoC: codecs: tfa989x: Add switch to allow disabling amplifier
Vincent Knecht (3): ASoC: codecs: tfa989x: Add support for tfa9897 RCV bit ASoC: dt-bindings: nxp, tfa989x: Add rcv-gpios property for tfa9897 ASoC: codecs: tfa989x: Add support for tfa9897 optional rcv-gpios
.../bindings/sound/nxp,tfa989x.yaml | 10 ++++ sound/soc/codecs/tfa989x.c | 47 ++++++++++++++++++- 2 files changed, 55 insertions(+), 2 deletions(-)
From: Stephan Gerhold stephan@gerhold.net
In some configurations it may be necessary to explicitly disable the amplifier with an ALSA mixer. An example for this is a stereo setup with two TFA989X. If only one of them should be used (e.g. to use it as an earpiece) the other one must be explicitly disabled.
Add a virtual "Amp Switch" to implement that. There is no register for this (SND_SOC_NOPM) so it only prevents DAPM from activating the amplifier. Also it is inverted (= enabled by default) for compatibility with devices that do not need this functionality.
Signed-off-by: Stephan Gerhold stephan@gerhold.net Signed-off-by: Vincent Knecht vincent.knecht@mailoo.org --- sound/soc/codecs/tfa989x.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/sound/soc/codecs/tfa989x.c b/sound/soc/codecs/tfa989x.c index 643b45188b6f..1ae47d4f1ca7 100644 --- a/sound/soc/codecs/tfa989x.c +++ b/sound/soc/codecs/tfa989x.c @@ -77,13 +77,16 @@ static const struct regmap_config tfa989x_regmap = {
static const char * const chsa_text[] = { "Left", "Right", /* "DSP" */ }; static SOC_ENUM_SINGLE_DECL(chsa_enum, TFA989X_I2SREG, TFA989X_I2SREG_CHSA, chsa_text); -static const struct snd_kcontrol_new chsa_mux = SOC_DAPM_ENUM("Amp Input", chsa_enum); +static const struct snd_kcontrol_new + chsa_mux = SOC_DAPM_ENUM("Amp Input", chsa_enum), + amp_switch = SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 1);
static const struct snd_soc_dapm_widget tfa989x_dapm_widgets[] = { SND_SOC_DAPM_OUTPUT("OUT"), SND_SOC_DAPM_SUPPLY("POWER", TFA989X_SYS_CTRL, TFA989X_SYS_CTRL_PWDN, 1, NULL, 0), SND_SOC_DAPM_OUT_DRV("AMPE", TFA989X_SYS_CTRL, TFA989X_SYS_CTRL_AMPE, 0, NULL, 0),
+ SND_SOC_DAPM_SWITCH("Amp", SND_SOC_NOPM, 0, 0, &_switch), SND_SOC_DAPM_MUX("Amp Input", SND_SOC_NOPM, 0, 0, &chsa_mux), SND_SOC_DAPM_AIF_IN("AIFINL", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_IN("AIFINR", "HiFi Playback", 1, SND_SOC_NOPM, 0, 0), @@ -92,7 +95,8 @@ static const struct snd_soc_dapm_widget tfa989x_dapm_widgets[] = { static const struct snd_soc_dapm_route tfa989x_dapm_routes[] = { {"OUT", NULL, "AMPE"}, {"AMPE", NULL, "POWER"}, - {"AMPE", NULL, "Amp Input"}, + {"AMPE", NULL, "Amp"}, + {"Amp", "Switch", "Amp Input"}, {"Amp Input", "Left", "AIFINL"}, {"Amp Input", "Right", "AIFINR"}, };
On Sun, Oct 24, 2021 at 10:58:37AM +0200, Vincent Knecht wrote:
From: Stephan Gerhold stephan@gerhold.net
In some configurations it may be necessary to explicitly disable the amplifier with an ALSA mixer. An example for this is a stereo setup with two TFA989X. If only one of them should be used (e.g. to use it as an earpiece) the other one must be explicitly disabled.
Add a virtual "Amp Switch" to implement that. There is no register for this (SND_SOC_NOPM) so it only prevents DAPM from activating the amplifier. Also it is inverted (= enabled by default) for compatibility with devices that do not need this functionality.
Why can you not use a standard pin switch on the speaker output for this?
On Mon, Oct 25, 2021 at 11:52:04AM +0100, Mark Brown wrote:
On Sun, Oct 24, 2021 at 10:58:37AM +0200, Vincent Knecht wrote:
From: Stephan Gerhold stephan@gerhold.net
In some configurations it may be necessary to explicitly disable the amplifier with an ALSA mixer. An example for this is a stereo setup with two TFA989X. If only one of them should be used (e.g. to use it as an earpiece) the other one must be explicitly disabled.
Add a virtual "Amp Switch" to implement that. There is no register for this (SND_SOC_NOPM) so it only prevents DAPM from activating the amplifier. Also it is inverted (= enabled by default) for compatibility with devices that do not need this functionality.
Why can you not use a standard pin switch on the speaker output for this?
Thanks for the suggestion! For some reason I was not aware of the pin switch mechanism... :) We have tried this and it seems to work fine. I will submit a separate series to allow setting up the pin switches for the qcom sound cards instead.
The other patches in this series are independent of this one and still apply cleanly for me. Can you just ignore PATCH 1/4 or would you prefer a resend without this patch?
Thanks, Stephan
TFA9897 has an internal 'rcv' switch so that it can manage both loudspeaker and earpiece modes with the same physical speaker.
Signed-off-by: Vincent Knecht vincent.knecht@mailoo.org --- sound/soc/codecs/tfa989x.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
diff --git a/sound/soc/codecs/tfa989x.c b/sound/soc/codecs/tfa989x.c index 1ae47d4f1ca7..ada516acefc0 100644 --- a/sound/soc/codecs/tfa989x.c +++ b/sound/soc/codecs/tfa989x.c @@ -19,6 +19,7 @@ #define TFA989X_REVISIONNUMBER 0x03 #define TFA989X_REVISIONNUMBER_REV_MSK GENMASK(7, 0) /* device revision */ #define TFA989X_I2SREG 0x04 +#define TFA989X_I2SREG_RCV 2 /* receiver mode */ #define TFA989X_I2SREG_CHSA 6 /* amplifier input select */ #define TFA989X_I2SREG_CHSA_MSK GENMASK(7, 6) #define TFA989X_I2SREG_I2SSR 12 /* sample rate */ @@ -53,6 +54,7 @@ struct tfa989x_rev { };
struct tfa989x { + const struct tfa989x_rev *rev; struct regulator *vddd_supply; };
@@ -101,7 +103,25 @@ static const struct snd_soc_dapm_route tfa989x_dapm_routes[] = { {"Amp Input", "Right", "AIFINR"}, };
+static const char * const mode_text[] = { "Speaker", "Receiver" }; +static SOC_ENUM_SINGLE_DECL(mode_enum, TFA989X_I2SREG, TFA989X_I2SREG_RCV, mode_text); +static const struct snd_kcontrol_new tfa989x_mode_controls[] = { + SOC_ENUM("Mode", mode_enum), +}; + +static int tfa989x_probe(struct snd_soc_component *component) +{ + struct tfa989x *tfa989x = snd_soc_component_get_drvdata(component); + + if (tfa989x->rev->rev == TFA9897_REVISION) + return snd_soc_add_component_controls(component, tfa989x_mode_controls, + ARRAY_SIZE(tfa989x_mode_controls)); + + return 0; +} + static const struct snd_soc_component_driver tfa989x_component = { + .probe = tfa989x_probe, .dapm_widgets = tfa989x_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(tfa989x_dapm_widgets), .dapm_routes = tfa989x_dapm_routes, @@ -277,6 +297,7 @@ static int tfa989x_i2c_probe(struct i2c_client *i2c) if (!tfa989x) return -ENOMEM;
+ tfa989x->rev = rev; i2c_set_clientdata(i2c, tfa989x);
tfa989x->vddd_supply = devm_regulator_get(dev, "vddd");
Add optional rcv-gpios property specific to tfa9897 receiver mode.
Signed-off-by: Vincent Knecht vincent.knecht@mailoo.org --- .../devicetree/bindings/sound/nxp,tfa989x.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/Documentation/devicetree/bindings/sound/nxp,tfa989x.yaml b/Documentation/devicetree/bindings/sound/nxp,tfa989x.yaml index 7667471be1e4..a9e15baedafd 100644 --- a/Documentation/devicetree/bindings/sound/nxp,tfa989x.yaml +++ b/Documentation/devicetree/bindings/sound/nxp,tfa989x.yaml @@ -12,6 +12,16 @@ maintainers: allOf: - $ref: name-prefix.yaml#
+ - if: + properties: + compatible: + contains: + const: nxp,tfa9897 + then: + properties: + rcv-gpios: + description: optional GPIO to be asserted when receiver mode is enabled. + properties: compatible: enum:
On Sun, Oct 24, 2021 at 10:58:39AM +0200, Vincent Knecht wrote:
Add optional rcv-gpios property specific to tfa9897 receiver mode.
Signed-off-by: Vincent Knecht vincent.knecht@mailoo.org
.../devicetree/bindings/sound/nxp,tfa989x.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/Documentation/devicetree/bindings/sound/nxp,tfa989x.yaml b/Documentation/devicetree/bindings/sound/nxp,tfa989x.yaml index 7667471be1e4..a9e15baedafd 100644 --- a/Documentation/devicetree/bindings/sound/nxp,tfa989x.yaml +++ b/Documentation/devicetree/bindings/sound/nxp,tfa989x.yaml @@ -12,6 +12,16 @@ maintainers: allOf:
- $ref: name-prefix.yaml#
- if:
properties:
compatible:
contains:
const: nxp,tfa9897
- then:
properties:
rcv-gpios:
description: optional GPIO to be asserted when receiver mode is enabled.
Did you test this works?
You have to define the property outside the if/then schema at the top level. Then use an if/then schema to restrict it (rcv-gpios: false).
properties: compatible: enum: -- 2.31.1
Le jeudi 28 octobre 2021 à 21:13 -0500, Rob Herring a écrit :
On Sun, Oct 24, 2021 at 10:58:39AM +0200, Vincent Knecht wrote:
Add optional rcv-gpios property specific to tfa9897 receiver mode.
Signed-off-by: Vincent Knecht vincent.knecht@mailoo.org
.../devicetree/bindings/sound/nxp,tfa989x.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/Documentation/devicetree/bindings/sound/nxp,tfa989x.yaml b/Documentation/devicetree/bindings/sound/nxp,tfa989x.yaml index 7667471be1e4..a9e15baedafd 100644 --- a/Documentation/devicetree/bindings/sound/nxp,tfa989x.yaml +++ b/Documentation/devicetree/bindings/sound/nxp,tfa989x.yaml @@ -12,6 +12,16 @@ maintainers: allOf: - $ref: name-prefix.yaml# + - if: + properties: + compatible: + contains: + const: nxp,tfa9897 + then: + properties: + rcv-gpios: + description: optional GPIO to be asserted when receiver mode is enabled.
Did you test this works?
You have to define the property outside the if/then schema at the top level. Then use an if/then schema to restrict it (rcv-gpios: false).
Sorry, I only tested validation with dt_binding_check.
I'll send another version later today with if: not: properties: compatible: const: nxp,tfa9897 then: properties: rcv-gpios: false and a tfa9897 example section, which passes the check and fails if rcv-gpio is added to tfa9895.
Thank you
Some OEM use a GPIO in addition to the tfa9897 RCV bit to switch between loudspeaker and earpiece/receiver mode.
Add support for the GPIO switching by specifying rcv-gpios in DT.
Signed-off-by: Vincent Knecht vincent.knecht@mailoo.org --- sound/soc/codecs/tfa989x.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-)
diff --git a/sound/soc/codecs/tfa989x.c b/sound/soc/codecs/tfa989x.c index ada516acefc0..c96e941da1a9 100644 --- a/sound/soc/codecs/tfa989x.c +++ b/sound/soc/codecs/tfa989x.c @@ -7,6 +7,7 @@ * Copyright (C) 2013 Sony Mobile Communications Inc. */
+#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/module.h> #include <linux/regmap.h> @@ -56,6 +57,7 @@ struct tfa989x_rev { struct tfa989x { const struct tfa989x_rev *rev; struct regulator *vddd_supply; + struct gpio_desc *rcv_gpiod; };
static bool tfa989x_writeable_reg(struct device *dev, unsigned int reg) @@ -103,10 +105,20 @@ static const struct snd_soc_dapm_route tfa989x_dapm_routes[] = { {"Amp Input", "Right", "AIFINR"}, };
+static int tfa989x_put_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct tfa989x *tfa989x = snd_soc_component_get_drvdata(component); + + gpiod_set_value_cansleep(tfa989x->rcv_gpiod, ucontrol->value.enumerated.item[0]); + + return snd_soc_put_enum_double(kcontrol, ucontrol); +} + static const char * const mode_text[] = { "Speaker", "Receiver" }; static SOC_ENUM_SINGLE_DECL(mode_enum, TFA989X_I2SREG, TFA989X_I2SREG_RCV, mode_text); static const struct snd_kcontrol_new tfa989x_mode_controls[] = { - SOC_ENUM("Mode", mode_enum), + SOC_ENUM_EXT("Mode", mode_enum, snd_soc_get_enum_double, tfa989x_put_mode), };
static int tfa989x_probe(struct snd_soc_component *component) @@ -305,6 +317,12 @@ static int tfa989x_i2c_probe(struct i2c_client *i2c) return dev_err_probe(dev, PTR_ERR(tfa989x->vddd_supply), "Failed to get vddd regulator\n");
+ if (tfa989x->rev->rev == TFA9897_REVISION) { + tfa989x->rcv_gpiod = devm_gpiod_get_optional(dev, "rcv", GPIOD_OUT_LOW); + if (IS_ERR(tfa989x->rcv_gpiod)) + return PTR_ERR(tfa989x->rcv_gpiod); + } + regmap = devm_regmap_init_i2c(i2c, &tfa989x_regmap); if (IS_ERR(regmap)) return PTR_ERR(regmap);
On Sun, 24 Oct 2021 10:58:36 +0200, Vincent Knecht wrote:
Two tfa9897 are used in tandem on some devices (eg. alcatel-idol347) to drive two speakers, for both receiver (earpiece) and speaker modes.
This series:
- adds a control to allow enabling only one tfa9897/speaker (for the typical phone-call usecase)
- adds a control to switch between speaker and receiver modes
- adds support for an optional gpio also used to switch modes
[...]
Applied to
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
Thanks!
[2/4] ASoC: codecs: tfa989x: Add support for tfa9897 RCV bit commit: b6a4e209fb7da1b49cb72fedb405f90e485d5a48
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
participants (4)
-
Mark Brown
-
Rob Herring
-
Stephan Gerhold
-
Vincent Knecht