public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/4] ASoC: Add support for GPIOs driven amplifiers
@ 2026-03-30 10:16 Herve Codina
  2026-03-30 10:16 ` [PATCH 1/4] of: Introduce of_property_read_s32_index() Herve Codina
                   ` (4 more replies)
  0 siblings, 5 replies; 11+ messages in thread
From: Herve Codina @ 2026-03-30 10:16 UTC (permalink / raw)
  To: Herve Codina, Liam Girdwood, Mark Brown, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
	Jaroslav Kysela, Takashi Iwai
  Cc: linux-sound, devicetree, linux-kernel, Christophe Leroy,
	Thomas Petazzoni

On some embedded system boards, audio amplifiers are designed using
discrete components such as op-amp, several resistors and switches to
either adjust the gain (switching resistors) or fully switch the
audio signal path (mute and/or bypass features).

Those switches are usually driven by simple GPIOs.

This kind of amplifiers are not handled in ASoC and the fallback is to
let the user-space handle those GPIOs out of the ALSA world.

In order to have those kind of amplifiers fully integrated in the audio
stack, this series introduces the audio-gpio-amp to handle them.

This new ASoC component allows to have the amplifiers seen as ASoC
auxiliarty devices and so it allows to control them through audio mixer
controls.

Best regards,
Hervé

Herve Codina (4):
  of: Introduce of_property_read_s32_index()
  ASoC: dt-bindings: Add support for the GPIOs driven amplifier
  ASoC: codecs: Add support for the GPIOs driven amplifier
  MAINTAINERS: Add the ASoC gpio amplifier entry

 .../bindings/sound/audio-gpio-amp.yaml        | 309 +++++++
 MAINTAINERS                                   |   7 +
 include/linux/of.h                            |   7 +
 sound/soc/codecs/Kconfig                      |  12 +
 sound/soc/codecs/Makefile                     |   2 +
 sound/soc/codecs/audio-gpio-amp.c             | 765 ++++++++++++++++++
 6 files changed, 1102 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/audio-gpio-amp.yaml
 create mode 100644 sound/soc/codecs/audio-gpio-amp.c

-- 
2.53.0


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

* [PATCH 1/4] of: Introduce of_property_read_s32_index()
  2026-03-30 10:16 [PATCH 0/4] ASoC: Add support for GPIOs driven amplifiers Herve Codina
@ 2026-03-30 10:16 ` Herve Codina
  2026-03-30 10:16 ` [PATCH 2/4] ASoC: dt-bindings: Add support for the GPIOs driven amplifier Herve Codina
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 11+ messages in thread
From: Herve Codina @ 2026-03-30 10:16 UTC (permalink / raw)
  To: Herve Codina, Liam Girdwood, Mark Brown, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
	Jaroslav Kysela, Takashi Iwai
  Cc: linux-sound, devicetree, linux-kernel, Christophe Leroy,
	Thomas Petazzoni

Signed integers can be read from single value properties using
of_property_read_s32() but nothing exist to read signed integers
from multi-value properties.

Fix this lack adding of_property_read_s32_index().

Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
 include/linux/of.h | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/include/linux/of.h b/include/linux/of.h
index be6ec4916adf..526c1ee819cd 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -1457,6 +1457,13 @@ static inline int of_property_read_s32(const struct device_node *np,
 	return of_property_read_u32(np, propname, (u32*) out_value);
 }
 
+static inline int of_property_read_s32_index(const struct device_node *np,
+					     const char *propname, u32 index,
+					     s32 *out_value)
+{
+	return of_property_read_u32_index(np, propname, index, (u32 *)out_value);
+}
+
 #define of_for_each_phandle(it, err, np, ln, cn, cc)			\
 	for (of_phandle_iterator_init((it), (np), (ln), (cn), (cc)),	\
 	     err = of_phandle_iterator_next(it);			\
-- 
2.53.0


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

* [PATCH 2/4] ASoC: dt-bindings: Add support for the GPIOs driven amplifier
  2026-03-30 10:16 [PATCH 0/4] ASoC: Add support for GPIOs driven amplifiers Herve Codina
  2026-03-30 10:16 ` [PATCH 1/4] of: Introduce of_property_read_s32_index() Herve Codina
@ 2026-03-30 10:16 ` Herve Codina
  2026-03-30 10:16 ` [PATCH 3/4] ASoC: codecs: " Herve Codina
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 11+ messages in thread
From: Herve Codina @ 2026-03-30 10:16 UTC (permalink / raw)
  To: Herve Codina, Liam Girdwood, Mark Brown, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
	Jaroslav Kysela, Takashi Iwai
  Cc: linux-sound, devicetree, linux-kernel, Christophe Leroy,
	Thomas Petazzoni

Some amplifiers based on analog switches and op-amps can be present in
the audio path and can be driven by GPIOs in order to control their gain
value, their mute and/or bypass functions.

Those components needs to be viewed as audio components in order to be
fully integrated in the audio path.

audio-gpio-amplifier allows to consider these GPIO driven amplifiers as
auxiliary audio devices.

Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
 .../bindings/sound/audio-gpio-amp.yaml        | 309 ++++++++++++++++++
 1 file changed, 309 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/audio-gpio-amp.yaml

diff --git a/Documentation/devicetree/bindings/sound/audio-gpio-amp.yaml b/Documentation/devicetree/bindings/sound/audio-gpio-amp.yaml
new file mode 100644
index 000000000000..15dc898f8574
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/audio-gpio-amp.yaml
@@ -0,0 +1,309 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/audio-gpio-amp.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Audio amplifier driven by GPIOs
+
+maintainers:
+  - Herve Codina <herve.codina@bootlin.com>
+
+description: |
+  Audio GPIO amplifiers are driven by GPIO in order to control the gain value
+  of the amplifier, its mute function and/or its bypass function.
+
+  Those amplifiers are based on discrete components (analog switches, op-amps
+  and more) where some of them, mostly analog switches, are controlled by GPIOs
+  to adjust the gain value of the whole amplifier and/or to control
+  the mute and/or bypass function.
+
+  For instance, the following piece of hardware is a GPIO amplifier
+
+                                         +5VA
+                                           ^
+                                        |\ |
+                                        | \
+        Vin >---------------------------|+ \
+                                        |   +-------+-----> Vout
+                .--\/\/\/--+------------|- /        |
+                |          |            | /         |
+                v          |            |/ |        |
+               GND         o               v        |
+                            \             GND       |
+       gpio >----------->    \                      |
+                         o    o                     |
+                         |    |                     |
+                         |    '--\/\/\/--.          |
+                         |               +--\/\/\/--'
+                         '---------------'
+
+  A GPIO driven amplifier can work in several mode depending on the electronic
+  design.
+    - points defined:
+        The values of GPIOs used to control gain set a specific gain value
+        without any specific relationship between each value. For instance,
+        using 2 GPIOS:
+          0b00 <-> -10.0 dB
+          0b01 <-> +3.0 dB
+          0b10 <-> 0 dB
+          0b11 <-> +6.0 dB
+
+        This can be described using the gain-points property.
+
+    - range defined:
+        The values of GPIOs used to control gain set a specific gain value
+        following a linear dB range from a minimum dB value to a maximum dB
+        value. For instance, using 2 GPIOS:
+          0b00 <-> -3.0 dB
+          0b01 <-> 0 db
+          0b10 <-> +3.0 dB
+          0b11 <-> +6.0 dB
+
+        This can be described using the gain-range property.
+
+    - labels defined:
+        Some electronic design are not meant to a specific dB gain value. In
+        that case it is relevant to use labels to describe them. For instance,
+        using 2 GPIOS:
+          0b00 <-> Low boost
+          0b01 <-> Middle boost
+          0b10 <-> High boost
+          0b11 <-> Max boost
+
+        This can be described using the gain-labels property
+
+properties:
+  compatible:
+    const: audio-gpio-amp
+
+  vdd-supply:
+    description: Main power supply of the amplifier
+
+  vddio-supply:
+    description: Power supply related to the control path
+
+  vdda1-supply:
+    description: Analog power supply
+
+  vdda2-supply:
+    description: Additional analog power supply
+
+  mute-gpios:
+    description: GPIO to control the mute function
+    maxItems: 1
+
+  bypass-gpios:
+    description: GPIO to control the bypass function
+    maxItems: 1
+
+  gain-gpios:
+    description: |
+      GPIOs to control the amplifier gain
+
+      The gain value is computed from GPIOs value from 0 to 2^N-1 with N the
+      number of GPIO described. The first GPIO described is the lsb of the gain
+      value.
+
+      For instance assuming 2 gpios
+         gain-gpios = <&gpio1 GPIO_ACTIVE_HIGH> <&gpio2 GPIO_ACTIVE_HIGH>;
+      The gain value will be the following:
+
+          gpio1 | gpio2 | gain
+          ------+-------+-----
+            0   |    0  | 0b00 -> 0
+            1   |    0  | 0b01 -> 1
+            0   |    1  | 0b10 -> 2
+            1   |    1  | 0b11 -> 3
+          ------+-------+-----
+
+      Note: The gain value, bits set to 1 or 0, indicate the state active (bit
+            set) or the state inactive (bit unset) of the related GPIO. The
+            physical voltage corresponding to this active/inactive state is
+            given by the GPIO_ACTIVE_HIGH and GPIO_ACTIVE_LOW flags.
+
+    minItems: 1
+    maxItems: 32
+
+  gain-points:
+    $ref: /schemas/types.yaml#/definitions/int32-matrix
+    items:
+      items:
+        - description: The GPIOs value
+        - description: The related amplifier gain in 0.01 dB unit
+    minItems: 2
+    description: |
+      List of the GPIOs value / Gain value in dB pair defining the gain
+      set on each GPIOs value.
+
+      With 2 GPIOs controlling the gain, GPIOs value can be 0, 1, 2 and 3.
+      Assuming that GPIOs values set the hardware gains according to the
+      following table:
+
+         GPIOs | Hardware
+         value | amplification
+         ------+--------------
+           0   | -10.0 dB
+           1   | +3.0 dB
+           2   | 0 dB
+           3   | +6.0 dB
+         ------+--------------
+
+      The description using gain points can be:
+        gain-points = <0 (-1000)>, <1 300>, <2 0>, <3 600>;
+
+  gain-range:
+    $ref: /schemas/types.yaml#/definitions/int32-array
+    items:
+      - description: Gain in 0.01 dB unit when all GPIOs are inactive
+      - description: Gain in 0.01 dB unit when all GPIOs are active
+    description: |
+      Gains (in 0.01 dB unit) set by the extremum (minimal and maximum) value
+      of GPIOs. The following formula must be satisfied.
+
+               gain-range[1] - gain-range[0]
+      Gain  = ------------------------------- x GPIO_value + gain-range[0]
+                        2^N - 1
+
+      With N, the number of GPIOs used to control the gain and Gain computed in
+      0.01 dB unit.
+
+      With 2 GPIOs controlling the gain, GPIOs value can be 0, 1, 2 and 3.
+      Assuming that gain value set the hardware according to the following
+      table:
+
+         GPIOs | Hardware 1    | Hardware 2
+         value | amplification | amplification
+         ------+---------------+---------------
+           0   | -3.0 dB       |  +10.0 dB
+           1   | 0 dB          |  +5.0 dB
+           2   | +3.0 dB       |  0 dB
+           3   | +6.0 dB       |  -5.0 dB
+         ------+---------------+---------------
+
+      The description for hardware 1 using a gain range can be:
+        gain-range = <(-300) 600>;
+
+      The description for hardware 2 using a gain range can be:
+        gain-range = <1000 (-500)>;
+
+  gain-labels:
+    $ref: /schemas/types.yaml#/definitions/string-array
+    description: |
+      List of the gain labels attached to the combination of GPIOs controlling
+      the gain. The first label is related to the gain value 0, the second label
+      is related to the gain value 1 and so on.
+
+      With 2 GPIOs controlling the gain, GPIOs value can be 0, 1, 2 and 3.
+      Assuming that gain value set the hardware according to the following
+      table:
+
+         GPIOs | Hardware
+         value | amplification
+         ------+--------------
+           0   | Low
+           1   | Middle
+           2   | High
+           3   | Max
+         ------+--------------
+
+      The description using gain labels can be:
+        gain-labels = "Low", "Middle", "High", "Max";
+
+dependencies:
+  gain-points: [ gain-gpios ]
+  gain-range: [ gain-gpios ]
+  gain-labels: [ gain-gpios ]
+
+required:
+  - compatible
+  - vdd-supply
+
+anyOf:
+  - required:
+      - gain-gpios
+  - required:
+      - mute-gpios
+  - required:
+      - bypass-gpios
+
+allOf:
+  - $ref: dai-common.yaml#
+  - if:
+      required:
+        - gain-points
+    then:
+      properties:
+        gain-range: false
+        gain-labels: false
+  - if:
+      required:
+        - gain-range
+    then:
+      properties:
+        gain-points: false
+        gain-labels: false
+  - if:
+      required:
+        - gain-labels
+    then:
+      properties:
+        gain-points: false
+        gain-range: false
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+
+    /* Gain controlled by gpios */
+    amplifier0 {
+        compatible = "audio-gpio-amp";
+        vdd-supply = <&regulator>;
+        gain-gpios = <&gpio 0 GPIO_ACTIVE_HIGH>, <&gpio 1 GPIO_ACTIVE_HIGH>;
+    };
+
+    /* Gain controlled by gpio using range */
+    amplifier1 {
+        compatible = "audio-gpio-amp";
+        vdd-supply = <&regulator>;
+        gain-gpios = <&gpio 0 GPIO_ACTIVE_HIGH>, <&gpio 1 GPIO_ACTIVE_HIGH>;
+        gain-range = <(-300) 600>;
+    };
+
+    /* Gain controlled by gpio using points */
+    amplifier2 {
+        compatible = "audio-gpio-amp";
+        vdd-supply = <&regulator>;
+        gain-gpios = <&gpio 0 GPIO_ACTIVE_HIGH>, <&gpio 1 GPIO_ACTIVE_HIGH>;
+        gain-points = <0 (-1000)>, <1 300>, <2 0>, <3 600>;
+    };
+
+    /* Gain controlled by gpio with labels */
+    amplifier3 {
+        compatible = "audio-gpio-amp";
+        vdd-supply = <&regulator>;
+        gain-gpios = <&gpio 0 GPIO_ACTIVE_HIGH>;
+        gain-labels = "Low", "High";
+    };
+
+    /* A mutable amplifier without any gain control */
+    amplifier4 {
+        compatible = "audio-gpio-amp";
+        vdd-supply = <&regulator>;
+        mute-gpios = <&gpio 0 GPIO_ACTIVE_HIGH>;
+    };
+
+    /*  Several supplies, gain controlled using range, mute and bypass */
+    amplifier5 {
+        compatible = "audio-gpio-amp";
+        vdd-supply = <&regulator>;
+        vddio-supply = <&regulator1>;
+        vdda1-supply = <&regulator2>;
+        gain-gpios = <&gpio 0 GPIO_ACTIVE_HIGH>, <&gpio 1 GPIO_ACTIVE_HIGH>;
+        gain-range = <(-300) 600>;
+        mute-gpios = <&gpio 2 GPIO_ACTIVE_HIGH>;
+        bypass-gpios = <&gpio 3 GPIO_ACTIVE_HIGH>;
+    };
+...
-- 
2.53.0


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

* [PATCH 3/4] ASoC: codecs: Add support for the GPIOs driven amplifier
  2026-03-30 10:16 [PATCH 0/4] ASoC: Add support for GPIOs driven amplifiers Herve Codina
  2026-03-30 10:16 ` [PATCH 1/4] of: Introduce of_property_read_s32_index() Herve Codina
  2026-03-30 10:16 ` [PATCH 2/4] ASoC: dt-bindings: Add support for the GPIOs driven amplifier Herve Codina
@ 2026-03-30 10:16 ` Herve Codina
  2026-03-30 10:16 ` [PATCH 4/4] MAINTAINERS: Add the ASoC gpio amplifier entry Herve Codina
  2026-03-30 15:08 ` [PATCH 0/4] ASoC: Add support for GPIOs driven amplifiers Mark Brown
  4 siblings, 0 replies; 11+ messages in thread
From: Herve Codina @ 2026-03-30 10:16 UTC (permalink / raw)
  To: Herve Codina, Liam Girdwood, Mark Brown, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
	Jaroslav Kysela, Takashi Iwai
  Cc: linux-sound, devicetree, linux-kernel, Christophe Leroy,
	Thomas Petazzoni

Some amplifiers driven by GPIOs can be present in the audio path.

In order to be fully integrated in the audio path and to have audio
mixer items available to control those amplifiers, an audio component
is needed.

This support allows to handle those GPIO driven amplifiers as auxiliary
audio devices and so control them using audio mixer controls.

Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
 sound/soc/codecs/Kconfig          |  12 +
 sound/soc/codecs/Makefile         |   2 +
 sound/soc/codecs/audio-gpio-amp.c | 765 ++++++++++++++++++++++++++++++
 3 files changed, 779 insertions(+)
 create mode 100644 sound/soc/codecs/audio-gpio-amp.c

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index adb3fb923be3..9c51519d6eea 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -52,6 +52,7 @@ config SND_SOC_ALL_CODECS
 	imply SND_SOC_AK5558
 	imply SND_SOC_ALC5623
 	imply SND_SOC_ALC5632
+	imply SND_SOC_AUDIO_GPIO_AMP
 	imply SND_SOC_AUDIO_IIO_AUX
 	imply SND_SOC_AW8738
 	imply SND_SOC_AW87390
@@ -648,6 +649,17 @@ config SND_SOC_ALC5632
 	tristate
 	depends on I2C
 
+config SND_SOC_AUDIO_GPIO_AMP
+	tristate "Audio GPIO Amplifier"
+	select GPIOLIB
+	help
+	  Enable support for GPIO amplifier.
+	  This allows to have an amplifier driven by GPIOs in the audio path and
+	  controlled using mixer controls.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called snd-soc-audio-gpio-amp.
+
 config SND_SOC_AUDIO_IIO_AUX
 	tristate "Audio IIO Auxiliary device"
 	depends on IIO
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 3ddee5298721..e1794a3368ab 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -423,6 +423,7 @@ snd-soc-wsa883x-y := wsa883x.o
 snd-soc-wsa884x-y := wsa884x.o
 snd-soc-zl38060-y := zl38060.o
 # Amp
+snd-soc-audio-gpio-amp-y := audio-gpio-amp.o
 snd-soc-max9877-y := max9877.o
 snd-soc-max98504-y := max98504.o
 snd-soc-simple-amplifier-y := simple-amplifier.o
@@ -869,6 +870,7 @@ obj-$(CONFIG_SND_SOC_WSA884X)	+= snd-soc-wsa884x.o
 obj-$(CONFIG_SND_SOC_ZL38060)	+= snd-soc-zl38060.o
 
 # Amp
+obj-$(CONFIG_SND_SOC_AUDIO_GPIO_AMP)	+= snd-soc-audio-gpio-amp.o
 obj-$(CONFIG_SND_SOC_MAX9877)	+= snd-soc-max9877.o
 obj-$(CONFIG_SND_SOC_MAX98504)	+= snd-soc-max98504.o
 obj-$(CONFIG_SND_SOC_SIMPLE_AMPLIFIER)	+= snd-soc-simple-amplifier.o
diff --git a/sound/soc/codecs/audio-gpio-amp.c b/sound/soc/codecs/audio-gpio-amp.c
new file mode 100644
index 000000000000..053501cfa5d6
--- /dev/null
+++ b/sound/soc/codecs/audio-gpio-amp.c
@@ -0,0 +1,765 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// GPIOs controlled amplifier ALSA SoC driver
+//
+// Copyright 2026 CS GROUP France
+//
+// Author: Herve Codina <herve.codina@bootlin.com>
+
+#include <linux/bitmap.h>
+#include <linux/gpio/consumer.h>
+#include <linux/minmax.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/sort.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+struct audio_gpio_single {
+	struct gpio_desc *gpio;
+	bool is_inverted;
+	int kctrl_val;
+	const char *control_name;
+};
+
+struct audio_gpio_point {
+	u32 gpio_val;
+	int gain_db;
+};
+
+struct audio_gpio_points {
+	unsigned int nb_points;
+	struct audio_gpio_point *tab_points;
+};
+
+struct audio_gpio_range {
+	int min_db;
+	int max_db;
+};
+
+struct audio_gpio_labels {
+	unsigned int nb_labels;
+	const char **tab_labels;
+};
+
+enum audio_gpio_mode {
+	AUDIO_GPIO_MODE_NONE,
+	AUDIO_GPIO_MODE_RANGE,
+	AUDIO_GPIO_MODE_LABELS,
+	AUDIO_GPIO_MODE_POINTS,
+};
+
+struct audio_gpio_multi {
+	struct gpio_descs *gpios;
+	bool is_inverted;
+	u32 kctrl_val;
+	u32 kctrl_max;
+	const char *control_name;
+	unsigned int *tlv_array;
+	enum audio_gpio_mode mode;
+	union {
+		struct audio_gpio_range range;
+		struct audio_gpio_points points;
+		struct audio_gpio_labels labels;
+	};
+};
+
+struct audio_gpio_amp {
+	struct audio_gpio_single mute;
+	struct audio_gpio_single bypass;
+	struct audio_gpio_multi gain;
+};
+
+static const struct snd_soc_dapm_widget audio_gpio_amp_dapm_widgets[] = {
+	SND_SOC_DAPM_INPUT("IN"),
+	SND_SOC_DAPM_OUTPUT("OUT"),
+	SND_SOC_DAPM_PGA("PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_REGULATOR_SUPPLY("vdd", 0, 0),
+};
+
+static const struct snd_soc_dapm_route audio_gpio_amp_dapm_routes[] = {
+	{ "PGA", NULL, "IN" },
+	{ "PGA", NULL, "vdd" },
+	{ "OUT", NULL, "PGA" },
+};
+
+static int audio_gpio_amp_single_kctrl_write_gpio(struct audio_gpio_single *single,
+						  int kctrl_val)
+{
+	int gpio_val;
+
+	gpio_val = single->is_inverted ? !kctrl_val : kctrl_val;
+
+	return gpiod_set_value_cansleep(single->gpio, gpio_val);
+}
+
+static int audio_gpio_amp_single_kctrl_info(struct snd_kcontrol *kcontrol,
+					    struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	return 0;
+}
+
+static int audio_gpio_amp_single_kctrl_get(struct snd_kcontrol *kcontrol,
+					   struct snd_ctl_elem_value *ucontrol)
+{
+	struct audio_gpio_single *single = (struct audio_gpio_single *)kcontrol->private_value;
+
+	ucontrol->value.integer.value[0] = single->kctrl_val;
+
+	return 0;
+}
+
+static int audio_gpio_amp_single_kctrl_put(struct snd_kcontrol *kcontrol,
+					   struct snd_ctl_elem_value *ucontrol)
+{
+	struct audio_gpio_single *single = (struct audio_gpio_single *)kcontrol->private_value;
+	int kctrl_val;
+	int err;
+
+	kctrl_val = ucontrol->value.integer.value[0];
+
+	if (kctrl_val == single->kctrl_val)
+		return 0;
+
+	err = audio_gpio_amp_single_kctrl_write_gpio(single, kctrl_val);
+	if (err)
+		return err;
+
+	single->kctrl_val = kctrl_val;
+
+	return 1; /* The value changed */
+}
+
+static int audio_gpio_amp_single_add_kcontrol(struct snd_soc_component *component,
+					      struct audio_gpio_single *single)
+{
+	struct snd_kcontrol_new control = {
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = single->control_name,
+		.info = audio_gpio_amp_single_kctrl_info,
+		.get = audio_gpio_amp_single_kctrl_get,
+		.put = audio_gpio_amp_single_kctrl_put,
+		.private_value = (unsigned long)single,
+	};
+	int ret;
+
+	/* Be consistent between single->kctrl_val value and the GPIO value */
+	ret = audio_gpio_amp_single_kctrl_write_gpio(single, single->kctrl_val);
+	if (ret)
+		return ret;
+
+	return snd_soc_add_component_controls(component, &control, 1);
+}
+
+static int audio_gpio_amp_multi_kctrl_write_gpios(struct audio_gpio_multi *multi,
+						  u32 kctrl_val)
+{
+	DECLARE_BITMAP(bm, 32);
+	u32 gpio_val;
+
+	if (multi->mode == AUDIO_GPIO_MODE_POINTS) {
+		if (kctrl_val >= multi->points.nb_points)
+			return -EINVAL;
+
+		gpio_val = multi->points.tab_points[kctrl_val].gpio_val;
+	} else {
+		if (kctrl_val > multi->kctrl_max)
+			return -EINVAL;
+
+		gpio_val = multi->is_inverted ?
+				multi->kctrl_max - kctrl_val :
+				kctrl_val;
+	}
+
+	bitmap_from_arr32(bm, &gpio_val, multi->gpios->ndescs);
+
+	return gpiod_multi_set_value_cansleep(multi->gpios, bm);
+}
+
+static int audio_gpio_amp_multi_kctrl_int_info(struct snd_kcontrol *kcontrol,
+					       struct snd_ctl_elem_info *uinfo)
+{
+	struct audio_gpio_multi *multi = (struct audio_gpio_multi *)kcontrol->private_value;
+
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	if (multi->mode == AUDIO_GPIO_MODE_POINTS)
+		uinfo->value.integer.max = multi->points.nb_points - 1;
+	else
+		uinfo->value.integer.max = multi->kctrl_max;
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	return 0;
+}
+
+static int audio_gpio_amp_multi_kctrl_int_get(struct snd_kcontrol *kcontrol,
+					      struct snd_ctl_elem_value *ucontrol)
+{
+	struct audio_gpio_multi *multi = (struct audio_gpio_multi *)kcontrol->private_value;
+
+	ucontrol->value.integer.value[0] = multi->kctrl_val;
+	return 0;
+}
+
+static int audio_gpio_amp_multi_kctrl_int_put(struct snd_kcontrol *kcontrol,
+					      struct snd_ctl_elem_value *ucontrol)
+{
+	struct audio_gpio_multi *multi = (struct audio_gpio_multi *)kcontrol->private_value;
+	u32 kctrl_val;
+	int ret;
+
+	kctrl_val = ucontrol->value.integer.value[0];
+
+	if (kctrl_val == multi->kctrl_val)
+		return 0;
+
+	ret = audio_gpio_amp_multi_kctrl_write_gpios(multi, kctrl_val);
+	if (ret)
+		return ret;
+
+	multi->kctrl_val = kctrl_val;
+
+	return 1; /* The value changed */
+}
+
+static int audio_gpio_amp_multi_kctrl_enum_info(struct snd_kcontrol *kcontrol,
+						struct snd_ctl_elem_info *uinfo)
+{
+	struct audio_gpio_multi *multi = (struct audio_gpio_multi *)kcontrol->private_value;
+
+	return snd_ctl_enum_info(uinfo, 1, multi->labels.nb_labels,
+				 multi->labels.tab_labels);
+}
+
+static int audio_gpio_amp_multi_kctrl_enum_get(struct snd_kcontrol *kcontrol,
+					       struct snd_ctl_elem_value *ucontrol)
+{
+	struct audio_gpio_multi *multi = (struct audio_gpio_multi *)kcontrol->private_value;
+
+	ucontrol->value.enumerated.item[0] = multi->kctrl_val;
+	return 0;
+}
+
+static int audio_gpio_amp_multi_kctrl_enum_put(struct snd_kcontrol *kcontrol,
+					       struct snd_ctl_elem_value *ucontrol)
+{
+	struct audio_gpio_multi *multi = (struct audio_gpio_multi *)kcontrol->private_value;
+	u32 kctrl_val;
+	int ret;
+
+	kctrl_val = ucontrol->value.enumerated.item[0];
+
+	if (kctrl_val == multi->kctrl_val)
+		return 0;
+
+	ret = audio_gpio_amp_multi_kctrl_write_gpios(multi, kctrl_val);
+	if (ret)
+		return ret;
+
+	multi->kctrl_val = kctrl_val;
+
+	return 1; /* The value changed */
+}
+
+static int *audio_gpio_amp_alloc_tlv_range(const struct audio_gpio_range *range)
+{
+	DECLARE_TLV_DB_MINMAX(tmp, range->min_db, range->max_db);
+
+	return kmemdup(tmp, sizeof(tmp), GFP_KERNEL);
+}
+
+static int *audio_gpio_amp_alloc_tlv_points(struct audio_gpio_points *points)
+{
+	unsigned int *tlv;
+	unsigned int *t;
+	unsigned int i;
+
+	tlv = kzalloc_objs(*tlv, 2 + points->nb_points * 6, GFP_KERNEL);
+	if (!tlv)
+		return NULL;
+
+	t = tlv;
+
+	/* Fill first TLV */
+	*t++ = SNDRV_CTL_TLVT_DB_RANGE; /* Tag */
+	*t++ = points->nb_points * 6 * sizeof(*tlv); /* Len */
+	/* points are sorted from lower to higher value */
+	for (i = 0; i < points->nb_points; i++) {
+		/* Fill item i */
+		*t++ = i;  /* min */
+		*t++ = i;  /* max */
+		*t++ = SNDRV_CTL_TLVT_DB_MINMAX; /* Tag */
+		*t++ = 2 * sizeof(*tlv); /* Len */
+		*t++ = points->tab_points[i].gain_db; /* min_dB */
+		*t++ = points->tab_points[i].gain_db; /* max_dB */
+	}
+
+	return tlv;
+}
+
+static int audio_gpio_amp_multi_add_kcontrol(struct snd_soc_component *component,
+					     struct audio_gpio_multi *multi)
+{
+	struct snd_kcontrol_new control = {
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = multi->control_name,
+		.info = audio_gpio_amp_multi_kctrl_int_info,
+		.get = audio_gpio_amp_multi_kctrl_int_get,
+		.put = audio_gpio_amp_multi_kctrl_int_put,
+		.private_value = (unsigned long)multi,
+	};
+	int ret;
+
+	switch (multi->mode) {
+	case AUDIO_GPIO_MODE_RANGE:
+		multi->tlv_array = audio_gpio_amp_alloc_tlv_range(&multi->range);
+		if (!multi->tlv_array)
+			return -ENOMEM;
+
+		control.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+				 SNDRV_CTL_ELEM_ACCESS_READWRITE;
+		control.tlv.p = multi->tlv_array;
+		break;
+
+	case AUDIO_GPIO_MODE_POINTS:
+		multi->tlv_array = audio_gpio_amp_alloc_tlv_points(&multi->points);
+		if (!multi->tlv_array)
+			return -ENOMEM;
+
+		control.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+				 SNDRV_CTL_ELEM_ACCESS_READWRITE;
+		control.tlv.p = multi->tlv_array;
+		break;
+
+	case AUDIO_GPIO_MODE_LABELS:
+		/* Use enumerated values */
+		control.info = audio_gpio_amp_multi_kctrl_enum_info;
+		control.get = audio_gpio_amp_multi_kctrl_enum_get;
+		control.put = audio_gpio_amp_multi_kctrl_enum_put;
+		break;
+
+	case AUDIO_GPIO_MODE_NONE:
+		/* Already set control configuration is enough */
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	/* Be consistent between multi->kctrl_val value and the GPIOs value */
+	ret = audio_gpio_amp_multi_kctrl_write_gpios(multi, multi->kctrl_val);
+	if (ret)
+		return ret;
+
+	return snd_soc_add_component_controls(component, &control, 1);
+}
+
+struct audio_gpio_amp_supply {
+	const char *prop_name;
+	const struct snd_soc_dapm_widget dapm_widget;
+	const struct snd_soc_dapm_route dapm_route;
+};
+
+static const struct audio_gpio_amp_supply audio_gpio_amp_supplies[] = {
+	{
+		.prop_name = "vddio-supply",
+		.dapm_widget = SND_SOC_DAPM_REGULATOR_SUPPLY("vddio", 0, 0),
+		.dapm_route = { "PGA", NULL, "vddio" },
+	}, {
+		.prop_name = "vdda1-supply",
+		.dapm_widget = SND_SOC_DAPM_REGULATOR_SUPPLY("vdda1", 0, 0),
+		.dapm_route = { "PGA", NULL, "vdda1" },
+	}, {
+		.prop_name = "vdda2-supply",
+		.dapm_widget = SND_SOC_DAPM_REGULATOR_SUPPLY("vdda2", 0, 0),
+		.dapm_route = { "PGA", NULL, "vdda2" },
+	},
+	{ /* End of list */}
+};
+
+static int audio_gpio_amp_add_power_supplies(struct snd_soc_component *component)
+{
+	struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+	const struct audio_gpio_amp_supply *supply;
+	struct device *dev = component->dev;
+	int ret;
+
+	supply = audio_gpio_amp_supplies;
+	do {
+		if (!of_property_present(dev->of_node, supply->prop_name))
+			continue;
+
+		ret = snd_soc_dapm_new_controls(dapm, &supply->dapm_widget, 1);
+		if (ret) {
+			dev_err(dev, "Failed to add control for '%s' (%d)\n",
+				supply->prop_name, ret);
+			return ret;
+		}
+		ret = snd_soc_dapm_add_routes(dapm, &supply->dapm_route, 1);
+		if (ret) {
+			dev_err(dev, "Failed to add route for '%s' (%d)\n",
+				supply->prop_name, ret);
+			return ret;
+		}
+	} while ((++supply)->prop_name);
+
+	return 0;
+}
+
+static int audio_gpio_amp_component_probe(struct snd_soc_component *component)
+{
+	struct audio_gpio_amp *gpio_amp = snd_soc_component_get_drvdata(component);
+	int ret;
+
+	ret = audio_gpio_amp_add_power_supplies(component);
+	if (ret)
+		return ret;
+
+	if (gpio_amp->mute.gpio) {
+		/*
+		 * The name of the GPIO used is mute. According to this name, 1
+		 * means muted and 0 means un-muted.
+		 *
+		 * An inversion is expected by ALSA. Indeed from ALSA point of
+		 * view, 1 means 'on' (un-muted) and 0 means 'off' (muted).
+		 */
+		gpio_amp->mute.is_inverted = true;
+		gpio_amp->mute.kctrl_val = 1; /* Un-muted */
+		ret = audio_gpio_amp_single_add_kcontrol(component, &gpio_amp->mute);
+		if (ret)
+			return ret;
+	}
+
+	if (gpio_amp->bypass.gpio) {
+		ret = audio_gpio_amp_single_add_kcontrol(component, &gpio_amp->bypass);
+		if (ret)
+			return ret;
+	}
+
+	if (gpio_amp->gain.gpios) {
+		ret = audio_gpio_amp_multi_add_kcontrol(component, &gpio_amp->gain);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static void audio_gpio_amp_component_remove(struct snd_soc_component *component)
+{
+	struct audio_gpio_amp *gpio_amp = snd_soc_component_get_drvdata(component);
+
+	kfree(gpio_amp->gain.tlv_array);
+	gpio_amp->gain.tlv_array = NULL;
+}
+
+static const struct snd_soc_component_driver audio_gpio_amp_component_driver = {
+	.dapm_widgets		= audio_gpio_amp_dapm_widgets,
+	.num_dapm_widgets	= ARRAY_SIZE(audio_gpio_amp_dapm_widgets),
+	.dapm_routes		= audio_gpio_amp_dapm_routes,
+	.num_dapm_routes	= ARRAY_SIZE(audio_gpio_amp_dapm_routes),
+	.probe = audio_gpio_amp_component_probe,
+	.remove = audio_gpio_amp_component_remove,
+};
+
+static int audio_gpio_amp_parse_labels(struct device *dev,
+				       struct audio_gpio_multi *multi,
+				       const char *labels_property)
+{
+	struct audio_gpio_labels *labels = &multi->labels;
+	struct device_node *np = dev->of_node;
+	int ret;
+
+	ret = of_property_count_strings(np, labels_property);
+	if (ret <= 0)
+		return ret;
+
+	labels->nb_labels = ret;
+	if (labels->nb_labels > (1 << multi->gpios->ndescs))
+		return -EINVAL;
+
+	labels->tab_labels = devm_kcalloc(dev, labels->nb_labels,
+					  sizeof(*labels->tab_labels),
+					  GFP_KERNEL);
+	if (!labels->tab_labels)
+		return -ENOMEM;
+
+	multi->kctrl_max = labels->nb_labels - 1;
+	multi->kctrl_val = 0;
+	multi->is_inverted = false;
+
+	return of_property_read_string_array(np, labels_property, labels->tab_labels,
+					     labels->nb_labels);
+}
+
+static int audio_gpio_amp_parse_range(struct device *dev,
+				      struct audio_gpio_multi *multi,
+				      const char *range_property)
+{
+	struct audio_gpio_range *range = &multi->range;
+	struct device_node *np = dev->of_node;
+	s32 tmp;
+	int ret;
+
+	ret = of_property_read_s32_index(np, range_property, 0, &tmp);
+	if (ret)
+		return ret;
+	range->min_db = tmp;
+
+	ret = of_property_read_s32_index(np, range_property, 1, &tmp);
+	if (ret)
+		return ret;
+	range->max_db = tmp;
+
+	multi->kctrl_max = (1 << multi->gpios->ndescs) - 1;
+	multi->kctrl_val = 0;
+	multi->is_inverted = false;
+
+	if (range->min_db > range->max_db) {
+		/* Invert range */
+		swap(range->min_db, range->max_db);
+		multi->is_inverted = 1;
+
+		/*
+		 * When the range is inverted, choose to have the initial
+		 * amplification set to max_db (i.e. all GPIOs inactive).
+		 */
+		multi->kctrl_val = multi->kctrl_max;
+	}
+
+	return 0;
+}
+
+static int audio_gpio_amp_cmp_points(const void *a, const void *b)
+{
+	const struct audio_gpio_point *a_point = a;
+	const struct audio_gpio_point *b_point = b;
+
+	return a_point->gain_db - b_point->gain_db;
+}
+
+static int audio_gpio_amp_parse_points(struct device *dev,
+				       struct audio_gpio_multi *multi,
+				       const char *points_property)
+{
+	struct audio_gpio_points *points = &multi->points;
+	struct device_node *np = dev->of_node;
+	struct audio_gpio_point first_point;
+	unsigned int max_gpio_val;
+	unsigned int i;
+	int ret;
+	u32 u;
+	s32 s;
+
+	max_gpio_val = (1 << multi->gpios->ndescs) - 1;
+
+	ret = of_property_count_u32_elems(np, points_property);
+	if (ret <= 0)
+		return ret;
+
+	if (ret % 2)
+		return -EINVAL;
+
+	points->nb_points = ret / 2;
+	if (points->nb_points > max_gpio_val + 1)
+		return -EINVAL;
+
+	points->tab_points = devm_kcalloc(dev, points->nb_points,
+					  sizeof(*points->tab_points),
+					  GFP_KERNEL);
+	if (!points->tab_points)
+		return -ENOMEM;
+
+	for (i = 0; i < points->nb_points; i++) {
+		/* Gpio value */
+		ret = of_property_read_u32_index(np, points_property,
+						 i * 2, &u);
+		if (ret)
+			return ret;
+		if (u > max_gpio_val)
+			return -EINVAL;
+
+		points->tab_points[i].gpio_val = u;
+
+		/* Gain value */
+		ret = of_property_read_s32_index(np, points_property,
+						 i * 2 + 1, &s);
+		if (ret)
+			return ret;
+
+		points->tab_points[i].gain_db = s;
+	}
+
+	first_point = points->tab_points[0];
+
+	/* Sort the tab_point array by gain_db value */
+	sort(points->tab_points, points->nb_points, sizeof(*points->tab_points),
+	     audio_gpio_amp_cmp_points, NULL);
+
+	multi->kctrl_max = points->nb_points - 1;
+	multi->is_inverted = false;
+
+	/*
+	 * multi->kctrl_val is the index in tab_points.
+	 *
+	 * Choose to have the initial amplification value set to the first item
+	 * available in the tab_points array before sorting.
+	 *
+	 * This first point before sorting has been identified. Search for it in
+	 * the sorted array in order to set the multi->kctrl_val initial value.
+	 */
+	for (i = 0; i < points->nb_points; i++) {
+		if (points->tab_points[i].gpio_val == first_point.gpio_val &&
+		    points->tab_points[i].gain_db == first_point.gain_db) {
+			multi->kctrl_val = i;
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static int audio_gpio_amp_parse_multi_gpio(struct device *dev,
+					   struct audio_gpio_multi *multi,
+					   const char *gpios_property,
+					   const char *range_property,
+					   const char *points_property,
+					   const char *labels_property)
+{
+	struct device_node *np = dev->of_node;
+	int ret;
+
+	/* Start with the value 0 (GPIO inactive). Can be changed later */
+	multi->is_inverted = false;
+	multi->kctrl_val = 0;
+	multi->gpios = devm_gpiod_get_array_optional(dev, gpios_property, GPIOD_OUT_LOW);
+	if (IS_ERR(multi->gpios))
+		return dev_err_probe(dev, PTR_ERR(multi->gpios),
+				     "Failed to get '%s' gpios\n",
+				     gpios_property);
+	if (!multi->gpios)
+		return 0;
+
+	if (multi->gpios->ndescs > 32)
+		return dev_err_probe(dev, -EINVAL,
+				     "Number of '%s' gpios limited to 32\n",
+				     labels_property);
+
+	/* Set default value for the kctrl_max. Can be changed later */
+	multi->kctrl_max = (1 << multi->gpios->ndescs) - 1;
+
+	multi->mode = AUDIO_GPIO_MODE_NONE;
+	if (of_property_present(np, points_property)) {
+		ret = audio_gpio_amp_parse_points(dev, multi, points_property);
+		if (ret < 0)
+			return dev_err_probe(dev, ret, "Failed to parse '%s'\n",
+					     points_property);
+		multi->mode = AUDIO_GPIO_MODE_POINTS;
+
+	} else if (of_property_present(np, range_property)) {
+		ret = audio_gpio_amp_parse_range(dev, multi, range_property);
+		if (ret < 0)
+			return dev_err_probe(dev, ret, "Failed to parse '%s'\n",
+					     range_property);
+		multi->mode = AUDIO_GPIO_MODE_RANGE;
+
+	} else if (of_property_present(np, labels_property)) {
+		ret = audio_gpio_amp_parse_labels(dev, multi, labels_property);
+		if (ret < 0)
+			return dev_err_probe(dev, ret, "Failed to parse '%s'\n",
+					     labels_property);
+
+		multi->mode = AUDIO_GPIO_MODE_LABELS;
+	}
+
+	return 0;
+}
+
+static int audio_gpio_amp_parse_single_gpio(struct device *dev,
+					    struct audio_gpio_single *single,
+					    const char *gpio_property)
+{
+	/* Start with the inactive value */
+	single->is_inverted = false;
+	single->kctrl_val = 0;
+	single->gpio = devm_gpiod_get_optional(dev, gpio_property, GPIOD_OUT_LOW);
+	if (IS_ERR(single->gpio))
+		return dev_err_probe(dev, PTR_ERR(single->gpio),
+				     "Failed to get '%s' gpio\n",
+				     gpio_property);
+	return 0;
+}
+
+static int audio_gpio_amp_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct audio_gpio_amp *gpio_amp;
+	int ret;
+
+	gpio_amp = devm_kzalloc(dev, sizeof(*gpio_amp), GFP_KERNEL);
+	if (!gpio_amp)
+		return -ENOMEM;
+
+	ret = audio_gpio_amp_parse_single_gpio(dev, &gpio_amp->mute, "mute");
+	if (ret)
+		return ret;
+
+	ret = audio_gpio_amp_parse_single_gpio(dev, &gpio_amp->bypass, "bypass");
+	if (ret)
+		return ret;
+
+	ret = audio_gpio_amp_parse_multi_gpio(dev, &gpio_amp->gain, "gain",
+					      "gain-range", "gain-points", "gain-labels");
+	if (ret)
+		return ret;
+
+	/* Set controls name */
+	gpio_amp->gain.control_name = "Volume";
+	gpio_amp->mute.control_name = "Switch";
+	gpio_amp->bypass.control_name = "Bypass Switch";
+
+	if (gpio_amp->gain.mode == AUDIO_GPIO_MODE_LABELS) {
+		/*
+		 * The gain widget control will use enumerated values.
+		 *
+		 * Having just "Voltage" and "Switch" widget names with
+		 * enumerated values and boolean value can confuse ALSA in terms
+		 * of possible values (strings).
+		 *
+		 * Make things clear and avoid the just "Switch" name in that
+		 * case.
+		 */
+		gpio_amp->mute.control_name = "Out Switch";
+	}
+
+	platform_set_drvdata(pdev, gpio_amp);
+
+	return devm_snd_soc_register_component(dev,
+					       &audio_gpio_amp_component_driver,
+					       NULL, 0);
+}
+
+static const struct of_device_id audio_gpio_amp_ids[] = {
+	{ .compatible = "audio-gpio-amp" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, audio_gpio_amp_ids);
+
+static struct platform_driver audio_gpio_amp_driver = {
+	.driver = {
+		.name = "audio-gpio-amp",
+		.of_match_table = of_match_ptr(audio_gpio_amp_ids),
+	},
+	.probe = audio_gpio_amp_probe,
+};
+module_platform_driver(audio_gpio_amp_driver);
+
+MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>");
+MODULE_DESCRIPTION("ASoC GPIOs controlled amplifier driver");
+MODULE_LICENSE("GPL");
-- 
2.53.0


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

* [PATCH 4/4] MAINTAINERS: Add the ASoC gpio amplifier entry
  2026-03-30 10:16 [PATCH 0/4] ASoC: Add support for GPIOs driven amplifiers Herve Codina
                   ` (2 preceding siblings ...)
  2026-03-30 10:16 ` [PATCH 3/4] ASoC: codecs: " Herve Codina
@ 2026-03-30 10:16 ` Herve Codina
  2026-03-30 15:08 ` [PATCH 0/4] ASoC: Add support for GPIOs driven amplifiers Mark Brown
  4 siblings, 0 replies; 11+ messages in thread
From: Herve Codina @ 2026-03-30 10:16 UTC (permalink / raw)
  To: Herve Codina, Liam Girdwood, Mark Brown, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Saravana Kannan,
	Jaroslav Kysela, Takashi Iwai
  Cc: linux-sound, devicetree, linux-kernel, Christophe Leroy,
	Thomas Petazzoni

After contributing the component, add myself as the maintainer for the
ASoC gpio amplifier component.

Signed-off-by: Herve Codina <herve.codina@bootlin.com>
---
 MAINTAINERS | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 55af015174a5..2546eb81f233 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24765,6 +24765,13 @@ F:	include/sound/dmaengine_pcm.h
 F:	sound/core/pcm_dmaengine.c
 F:	sound/soc/soc-generic-dmaengine-pcm.c
 
+SOUND - SOC LAYER / AUDIO GPIO AMPLIFIER
+M:	Herve Codina <herve.codina@bootlin.com>
+L:	linux-sound@vger.kernel.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/sound/audio-gpio-amp.yaml
+F:	sound/soc/codecs/audio-gpio-amp.c
+
 SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT (ASoC)
 M:	Liam Girdwood <lgirdwood@gmail.com>
 M:	Mark Brown <broonie@kernel.org>
-- 
2.53.0


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

* Re: [PATCH 0/4] ASoC: Add support for GPIOs driven amplifiers
  2026-03-30 10:16 [PATCH 0/4] ASoC: Add support for GPIOs driven amplifiers Herve Codina
                   ` (3 preceding siblings ...)
  2026-03-30 10:16 ` [PATCH 4/4] MAINTAINERS: Add the ASoC gpio amplifier entry Herve Codina
@ 2026-03-30 15:08 ` Mark Brown
  2026-03-30 15:39   ` Herve Codina
  4 siblings, 1 reply; 11+ messages in thread
From: Mark Brown @ 2026-03-30 15:08 UTC (permalink / raw)
  To: Herve Codina
  Cc: Liam Girdwood, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Saravana Kannan, Jaroslav Kysela, Takashi Iwai, linux-sound,
	devicetree, linux-kernel, Christophe Leroy, Thomas Petazzoni

[-- Attachment #1: Type: text/plain, Size: 431 bytes --]

On Mon, Mar 30, 2026 at 12:16:04PM +0200, Herve Codina wrote:
> On some embedded system boards, audio amplifiers are designed using
> discrete components such as op-amp, several resistors and switches to
> either adjust the gain (switching resistors) or fully switch the
> audio signal path (mute and/or bypass features).
> 
> Those switches are usually driven by simple GPIOs.

This sounds a lot like simple-amplifier.c?

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

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

* Re: [PATCH 0/4] ASoC: Add support for GPIOs driven amplifiers
  2026-03-30 15:08 ` [PATCH 0/4] ASoC: Add support for GPIOs driven amplifiers Mark Brown
@ 2026-03-30 15:39   ` Herve Codina
  2026-03-30 15:48     ` Mark Brown
  0 siblings, 1 reply; 11+ messages in thread
From: Herve Codina @ 2026-03-30 15:39 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Saravana Kannan, Jaroslav Kysela, Takashi Iwai, linux-sound,
	devicetree, linux-kernel, Christophe Leroy, Thomas Petazzoni

On Mon, 30 Mar 2026 16:08:47 +0100
Mark Brown <broonie@kernel.org> wrote:

> On Mon, Mar 30, 2026 at 12:16:04PM +0200, Herve Codina wrote:
> > On some embedded system boards, audio amplifiers are designed using
> > discrete components such as op-amp, several resistors and switches to
> > either adjust the gain (switching resistors) or fully switch the
> > audio signal path (mute and/or bypass features).
> > 
> > Those switches are usually driven by simple GPIOs.  
> 
> This sounds a lot like simple-amplifier.c?

simple-amplifier.c doesn't handle amplifier driven by GPIOs.
The only used GPIO in simple-amplifier.c is used to handle the
enable pin the component.

simple-amplifier.c handles component such as dio2125 alone.

Here, we have op-amp but also several components around such as
switches.

The gpio driven amplifier proposed is more generic and can handle
more complex design. I.e. op-amp + resistor and/or line (mute,
bypass) switching. Hardwares handled by this driver are a superset
of just dio2125 and so simple-amplifier.c.

IMHO, it makes sense to have a specific driver for those kind
of hardware design.

Best regards,
Hervé

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

* Re: [PATCH 0/4] ASoC: Add support for GPIOs driven amplifiers
  2026-03-30 15:39   ` Herve Codina
@ 2026-03-30 15:48     ` Mark Brown
  2026-03-30 16:41       ` Herve Codina
  0 siblings, 1 reply; 11+ messages in thread
From: Mark Brown @ 2026-03-30 15:48 UTC (permalink / raw)
  To: Herve Codina
  Cc: Liam Girdwood, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Saravana Kannan, Jaroslav Kysela, Takashi Iwai, linux-sound,
	devicetree, linux-kernel, Christophe Leroy, Thomas Petazzoni

[-- Attachment #1: Type: text/plain, Size: 590 bytes --]

On Mon, Mar 30, 2026 at 05:39:44PM +0200, Herve Codina wrote:
> Mark Brown <broonie@kernel.org> wrote:

> > This sounds a lot like simple-amplifier.c?

> The gpio driven amplifier proposed is more generic and can handle
> more complex design. I.e. op-amp + resistor and/or line (mute,
> bypass) switching. Hardwares handled by this driver are a superset
> of just dio2125 and so simple-amplifier.c.

> IMHO, it makes sense to have a specific driver for those kind
> of hardware design.

Right, and if it's a superset it feels like it should all be one driver
rather than two separate ones.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

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

* Re: [PATCH 0/4] ASoC: Add support for GPIOs driven amplifiers
  2026-03-30 15:48     ` Mark Brown
@ 2026-03-30 16:41       ` Herve Codina
  2026-04-05 17:00         ` Christophe Leroy (CS GROUP)
  0 siblings, 1 reply; 11+ messages in thread
From: Herve Codina @ 2026-03-30 16:41 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Saravana Kannan, Jaroslav Kysela, Takashi Iwai, linux-sound,
	devicetree, linux-kernel, Christophe Leroy, Thomas Petazzoni

On Mon, 30 Mar 2026 16:48:54 +0100
Mark Brown <broonie@kernel.org> wrote:

> On Mon, Mar 30, 2026 at 05:39:44PM +0200, Herve Codina wrote:
> > Mark Brown <broonie@kernel.org> wrote:  
> 
> > > This sounds a lot like simple-amplifier.c?  
> 
> > The gpio driven amplifier proposed is more generic and can handle
> > more complex design. I.e. op-amp + resistor and/or line (mute,
> > bypass) switching. Hardwares handled by this driver are a superset
> > of just dio2125 and so simple-amplifier.c.  
> 
> > IMHO, it makes sense to have a specific driver for those kind
> > of hardware design.  
> 
> Right, and if it's a superset it feels like it should all be one driver
> rather than two separate ones.

Also, it is worth noting that simple-amplifier.c considered a stereo
amplifier (left + right).

Considering the two op-amp available in dio2125 as just two op-amp used
in two separated mono channel with additional component to switch related
to resistors (independant switching for each channel) means that almost
everything proposed in audio-gpio-amp have to be duplicated (gain, mute,
bypass per channel) instead of just instantiate two audio-gpio-amps.

On the other hand, there is no reason to handle a stereo component in
audio-gpio-amp. Further more with the hardware I have handling a stereo
component doesn't make sense. Indeed, I only have independent mono lines
with their own resistor switched amplification circuitry.

I could merge everything in one .c file but only a few part of source code
will be common to simple-amplifier and audio-gpio-amp. IMHO the resulting
merged code will look like two different drivers merged in one .c file.

Best regards,
Hervé

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

* Re: [PATCH 0/4] ASoC: Add support for GPIOs driven amplifiers
  2026-03-30 16:41       ` Herve Codina
@ 2026-04-05 17:00         ` Christophe Leroy (CS GROUP)
  2026-04-06 14:08           ` Mark Brown
  0 siblings, 1 reply; 11+ messages in thread
From: Christophe Leroy (CS GROUP) @ 2026-04-05 17:00 UTC (permalink / raw)
  To: Herve Codina, Mark Brown
  Cc: Liam Girdwood, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Saravana Kannan, Jaroslav Kysela, Takashi Iwai, linux-sound,
	devicetree, linux-kernel, Christophe Leroy, Thomas Petazzoni

Hi Mark,

Le 30/03/2026 à 18:41, Herve Codina a écrit :
> On Mon, 30 Mar 2026 16:48:54 +0100
> Mark Brown <broonie@kernel.org> wrote:
> 
>> On Mon, Mar 30, 2026 at 05:39:44PM +0200, Herve Codina wrote:
>>> Mark Brown <broonie@kernel.org> wrote:
>>
>>>> This sounds a lot like simple-amplifier.c?
>>
>>> The gpio driven amplifier proposed is more generic and can handle
>>> more complex design. I.e. op-amp + resistor and/or line (mute,
>>> bypass) switching. Hardwares handled by this driver are a superset
>>> of just dio2125 and so simple-amplifier.c.
>>
>>> IMHO, it makes sense to have a specific driver for those kind
>>> of hardware design.
>>
>> Right, and if it's a superset it feels like it should all be one driver
>> rather than two separate ones.
> 
> Also, it is worth noting that simple-amplifier.c considered a stereo
> amplifier (left + right).
> 
> Considering the two op-amp available in dio2125 as just two op-amp used
> in two separated mono channel with additional component to switch related
> to resistors (independant switching for each channel) means that almost
> everything proposed in audio-gpio-amp have to be duplicated (gain, mute,
> bypass per channel) instead of just instantiate two audio-gpio-amps.
> 
> On the other hand, there is no reason to handle a stereo component in
> audio-gpio-amp. Further more with the hardware I have handling a stereo
> component doesn't make sense. Indeed, I only have independent mono lines
> with their own resistor switched amplification circuitry.
> 
> I could merge everything in one .c file but only a few part of source code
> will be common to simple-amplifier and audio-gpio-amp. IMHO the resulting
> merged code will look like two different drivers merged in one .c file.

Following explanation from Herve I have the feeling that combining the 
two drivers into a single one will bring more complexity for little benefit.

Do you still think it is worth having a combined driver allthough they 
address quite different setups ?

Thanks
Christophe

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

* Re: [PATCH 0/4] ASoC: Add support for GPIOs driven amplifiers
  2026-04-05 17:00         ` Christophe Leroy (CS GROUP)
@ 2026-04-06 14:08           ` Mark Brown
  0 siblings, 0 replies; 11+ messages in thread
From: Mark Brown @ 2026-04-06 14:08 UTC (permalink / raw)
  To: Christophe Leroy (CS GROUP)
  Cc: Herve Codina, Liam Girdwood, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Saravana Kannan, Jaroslav Kysela, Takashi Iwai,
	linux-sound, devicetree, linux-kernel, Christophe Leroy,
	Thomas Petazzoni

[-- Attachment #1: Type: text/plain, Size: 875 bytes --]

On Sun, Apr 05, 2026 at 07:00:20PM +0200, Christophe Leroy (CS GROUP) wrote:
> Le 30/03/2026 à 18:41, Herve Codina a écrit :

> > I could merge everything in one .c file but only a few part of source code
> > will be common to simple-amplifier and audio-gpio-amp. IMHO the resulting
> > merged code will look like two different drivers merged in one .c file.

> Following explanation from Herve I have the feeling that combining the two
> drivers into a single one will bring more complexity for little benefit.

> Do you still think it is worth having a combined driver allthough they
> address quite different setups ?

Yes, it's just a difference in the binding not in the runtime stuff.
The two will inevitably grow together over time, keeping them separate
is just creating a long term bikeshedding problem wondering which to use
for a given situation.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

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

end of thread, other threads:[~2026-04-06 14:08 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-30 10:16 [PATCH 0/4] ASoC: Add support for GPIOs driven amplifiers Herve Codina
2026-03-30 10:16 ` [PATCH 1/4] of: Introduce of_property_read_s32_index() Herve Codina
2026-03-30 10:16 ` [PATCH 2/4] ASoC: dt-bindings: Add support for the GPIOs driven amplifier Herve Codina
2026-03-30 10:16 ` [PATCH 3/4] ASoC: codecs: " Herve Codina
2026-03-30 10:16 ` [PATCH 4/4] MAINTAINERS: Add the ASoC gpio amplifier entry Herve Codina
2026-03-30 15:08 ` [PATCH 0/4] ASoC: Add support for GPIOs driven amplifiers Mark Brown
2026-03-30 15:39   ` Herve Codina
2026-03-30 15:48     ` Mark Brown
2026-03-30 16:41       ` Herve Codina
2026-04-05 17:00         ` Christophe Leroy (CS GROUP)
2026-04-06 14:08           ` Mark Brown

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox