mirror of https://lore.kernel.org/linux-amlogic/
 help / color / mirror / Atom feed
* [PATCH v2 0/6] ASoC: meson: gx: add base support for I2S audio input
@ 2026-09-17 21:02 Valerio Setti
  2026-09-17 21:02 ` [PATCH v2 1/6] ASoC: dt-bindings: amlogic,gx-audin: add schema Valerio Setti
                   ` (5 more replies)
  0 siblings, 6 replies; 10+ messages in thread
From: Valerio Setti @ 2026-09-17 21:02 UTC (permalink / raw)
  To: Jerome Brunet, Liam Girdwood, Mark Brown, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Neil Armstrong, Kevin Hilman,
	Martin Blumenstingl, Jaroslav Kysela, Takashi Iwai
  Cc: linux-sound, devicetree, linux-arm-kernel, linux-amlogic,
	linux-kernel, Valerio Setti

This series is the 2nd part of the initial RFC [1] proposed to add audio
input support to the Meson GX SoCs. After the AIU reshaping series [2] that
has recently been merged, this one adds the audio input path itself.

Apart from some glue commits for gx-formatter Kconfig split, DT, bindings
and integration with gx-card, the core commit is the 3rd one, which adds
the AUDIN driver.
This is the counterpart of AIU for capture: it gets the entire register
range, it handles peripheral clock and reset, it instantiates the I2S data
formatter as widget and FIFOs as DAIs.

Resulting capture topology is:
  pads --> AIU I2S Encoder -> AUDIN I2S Formatter -> AUDIN FIFO -> RAM

This series includes all the changes related to this work (dt-bindings,
drivers and device-tree) in order to give the full picture in one single
place.

Unfortunately, since no Meson GXBB/GXL board available in the tree features
an I2S capture capable codec, this work cannot be directly tested out of
the box. To test it an external codec must be connected to the SoC.
When working on this series, a custom hat board with an NXP SGTL5000 codec
has been plugged on top of an Odroid-C2 (GXBB). The last commit of this
series fully details all the device-tree changes which are required to
finalize the support for this scenario.
With this hardware configuration it has been possible to successfully
validate the implementation with:
- alsa tools (arecord)
- pcm-tests

[1]: https://lore.kernel.org/linux-sound/20260411-audin-rfc-v2-0-4c8a6ec5fcab@baylibre.com/
[2]: https://lore.kernel.org/linux-sound/20260610-reshape-aiu-as-axg-v2-0-cac3663a8b51@baylibre.com/

Signed-off-by: Valerio Setti <vsetti@baylibre.com>
---
Changes in v2:
- Dropped FIFO child nodes from AUDIN in device-tree. Now AUDIN is the
  only sound component and FIFOs are its DAIs. This is the same design
  used in AIU. [Krzysztof]
- Renamed driver compatible, using 'amlogic,gx-audin' as fallback and
  'amlogic,gx[bb|l]-audin' as SoC specific. Fixed also other reported
  comments on the binding. [Krzysztof]
- Adjusted headers inclusion in 'audin.c' and spacing in the empty entry
  of 'meson_gx_audin_of_match'. [Uwe]
- Replaced 'snd_soc_component_read' with 'regmap_read' in 'audin_fifo_pos'
  in order to prevent sleeping in atomic context. Also changed the hrtimer
  from hard-irq to a soft-irq one. [Sashiko]
- Added 'sync_stop' callback in 'audin_component' to ensure the hrtimer is
  fully stopped before attempting to restart the stream in a
  "start->stop->prepare->start" scenario.
- Link to v1: https://patch.msgid.link/20260912-audin-v1-0-7fbefe928711@baylibre.com

To: Jerome Brunet <jbrunet@baylibre.com>
To: Liam Girdwood <lgirdwood@gmail.com>
To: Mark Brown <broonie@kernel.org>
To: Rob Herring <robh@kernel.org>
To: Krzysztof Kozlowski <krzk+dt@kernel.org>
To: Conor Dooley <conor+dt@kernel.org>
To: Valerio Setti <vsetti@baylibre.com>
To: Jaroslav Kysela <perex@perex.cz>
To: Takashi Iwai <tiwai@suse.com>
To: Neil Armstrong <neil.armstrong@linaro.org>
To: Kevin Hilman <khilman@baylibre.com>
To: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Cc: linux-sound@vger.kernel.org
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-amlogic@lists.infradead.org

---
Valerio Setti (6):
      ASoC: dt-bindings: amlogic,gx-audin: add schema
      ASoC: meson: build gx-formatter as a separate module
      ASoC: meson: add AUDIN driver
      ASoC: meson: aiu: add I2S Capture DAI
      ASoC: meson: gx-card: add support for audin FIFO
      arm64: dts: amlogic: gx: add AUDIN node

 .../bindings/sound/amlogic,gx-audin.yaml           |  66 ++++
 arch/arm64/boot/dts/amlogic/meson-gx.dtsi          |   9 +
 arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi        |   6 +
 arch/arm64/boot/dts/amlogic/meson-gxl.dtsi         |   6 +
 sound/soc/meson/Kconfig                            |  14 +
 sound/soc/meson/Makefile                           |   7 +-
 sound/soc/meson/aiu.c                              |   7 +
 sound/soc/meson/audin-fifo.c                       | 367 +++++++++++++++++++++
 sound/soc/meson/audin-formatter-i2s.c              | 174 ++++++++++
 sound/soc/meson/audin.c                            | 186 +++++++++++
 sound/soc/meson/audin.h                            |  30 ++
 sound/soc/meson/gx-card.c                          |  16 +-
 12 files changed, 880 insertions(+), 8 deletions(-)
---
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
change-id: 20260831-audin-9cabb450cc94

Best regards,
--  
Valerio Setti <vsetti@baylibre.com>


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* [PATCH v2 1/6] ASoC: dt-bindings: amlogic,gx-audin: add schema
  2026-09-17 21:02 [PATCH v2 0/6] ASoC: meson: gx: add base support for I2S audio input Valerio Setti
@ 2026-09-17 21:02 ` Valerio Setti
  2026-09-17 21:07   ` sashiko-bot
  2026-09-17 21:02 ` [PATCH v2 2/6] ASoC: meson: build gx-formatter as a separate module Valerio Setti
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 10+ messages in thread
From: Valerio Setti @ 2026-09-17 21:02 UTC (permalink / raw)
  To: Jerome Brunet, Liam Girdwood, Mark Brown, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Neil Armstrong, Kevin Hilman,
	Martin Blumenstingl, Jaroslav Kysela, Takashi Iwai
  Cc: linux-sound, devicetree, linux-arm-kernel, linux-amlogic,
	linux-kernel, Valerio Setti

AUDIN provides I2S audio input support to the Meson GX platforms
(GXBB/GXL).

Note: GXBB and GXL are not known to differ so the driver binds on the
'amlogic,gx-audin' fallback.

Signed-off-by: Valerio Setti <vsetti@baylibre.com>
---
 .../bindings/sound/amlogic,gx-audin.yaml           | 66 ++++++++++++++++++++++
 1 file changed, 66 insertions(+)

diff --git a/Documentation/devicetree/bindings/sound/amlogic,gx-audin.yaml b/Documentation/devicetree/bindings/sound/amlogic,gx-audin.yaml
new file mode 100644
index 000000000000..76e6a6ddef36
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/amlogic,gx-audin.yaml
@@ -0,0 +1,66 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/amlogic,gx-audin.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Amlogic GX AUDIN
+
+maintainers:
+  - Valerio Setti <vsetti@baylibre.com>
+
+description:
+  AUDIN is the audio input block of the Amlogic GXBB/GXL SoCs. It supports
+  various input formatters (I2S, SPDIF, PCM, HDMI) feeding three FIFOs that
+  transfer captured samples to memory. The sound-dai cell selects the FIFO.
+
+allOf:
+  - $ref: dai-common.yaml#
+
+properties:
+  compatible:
+    items:
+      - enum:
+          - amlogic,gxbb-audin
+          - amlogic,gxl-audin
+      - const: amlogic,gx-audin
+
+  reg:
+    maxItems: 1
+
+  resets:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: AUDIN peripheral clock
+
+required:
+  - compatible
+  - reg
+  - resets
+  - interrupts
+  - clocks
+  - "#sound-dai-cells"
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/gxbb-clkc.h>
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/interrupt-controller/irq.h>
+    #include <dt-bindings/reset/amlogic,meson-gxbb-reset.h>
+
+    audio-controller@a000 {
+      compatible = "amlogic,gxbb-audin", "amlogic,gx-audin";
+      reg = <0xa000 0x148>;
+      resets = <&reset RESET_AUDIN>;
+      interrupts = <GIC_SPI 7 IRQ_TYPE_EDGE_RISING>;
+      sound-name-prefix = "AUDIN";
+      #sound-dai-cells = <1>;
+      clocks = <&clkc CLKID_I2S_SPDIF>;
+    };

-- 
2.47.3


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* [PATCH v2 2/6] ASoC: meson: build gx-formatter as a separate module
  2026-09-17 21:02 [PATCH v2 0/6] ASoC: meson: gx: add base support for I2S audio input Valerio Setti
  2026-09-17 21:02 ` [PATCH v2 1/6] ASoC: dt-bindings: amlogic,gx-audin: add schema Valerio Setti
@ 2026-09-17 21:02 ` Valerio Setti
  2026-09-17 21:02 ` [PATCH v2 3/6] ASoC: meson: add AUDIN driver Valerio Setti
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 10+ messages in thread
From: Valerio Setti @ 2026-09-17 21:02 UTC (permalink / raw)
  To: Jerome Brunet, Liam Girdwood, Mark Brown, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Neil Armstrong, Kevin Hilman,
	Martin Blumenstingl, Jaroslav Kysela, Takashi Iwai
  Cc: linux-sound, devicetree, linux-arm-kernel, linux-amlogic,
	linux-kernel, Valerio Setti

Instead of including it in 'snd-soc-meson-aiu', create a separate module
for it. This way it can be independently selected by either AIU or the
(upcoming) AUDIN.

Signed-off-by: Valerio Setti <vsetti@baylibre.com>
---
 sound/soc/meson/Kconfig  | 5 +++++
 sound/soc/meson/Makefile | 3 ++-
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/sound/soc/meson/Kconfig b/sound/soc/meson/Kconfig
index d9a730994a2a..4c40bf60dc4e 100644
--- a/sound/soc/meson/Kconfig
+++ b/sound/soc/meson/Kconfig
@@ -2,10 +2,15 @@
 menu "Amlogic"
 	depends on ARCH_MESON || (COMPILE_TEST && COMMON_CLK)
 
+config SND_MESON_GX_FORMATTER
+	tristate
+	select REGMAP_MMIO
+
 config SND_MESON_AIU
 	tristate "Amlogic AIU"
 	select SND_MESON_CODEC_GLUE
 	select SND_PCM_IEC958
+	select SND_MESON_GX_FORMATTER
 	imply SND_SOC_MESON_T9015
 	imply SND_SOC_HDMI_CODEC if DRM_MESON_DW_HDMI
 	help
diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile
index f9ec0ebb01f0..1b1fbe8a0458 100644
--- a/sound/soc/meson/Makefile
+++ b/sound/soc/meson/Makefile
@@ -4,12 +4,12 @@ snd-soc-meson-aiu-y := aiu.o
 snd-soc-meson-aiu-y += aiu-acodec-ctrl.o
 snd-soc-meson-aiu-y += aiu-codec-ctrl.o
 snd-soc-meson-aiu-y += aiu-encoder-i2s.o
-snd-soc-meson-aiu-y += gx-formatter.o
 snd-soc-meson-aiu-y += aiu-formatter-i2s.o
 snd-soc-meson-aiu-y += aiu-encoder-spdif.o
 snd-soc-meson-aiu-y += aiu-fifo.o
 snd-soc-meson-aiu-y += aiu-fifo-i2s.o
 snd-soc-meson-aiu-y += aiu-fifo-spdif.o
+snd-soc-meson-gx-formatter-y := gx-formatter.o
 snd-soc-meson-axg-fifo-y := axg-fifo.o
 snd-soc-meson-axg-frddr-y := axg-frddr.o
 snd-soc-meson-axg-toddr-y := axg-toddr.o
@@ -29,6 +29,7 @@ snd-soc-meson-g12a-tohdmitx-y := g12a-tohdmitx.o
 snd-soc-meson-t9015-y := t9015.o
 
 obj-$(CONFIG_SND_MESON_AIU) += snd-soc-meson-aiu.o
+obj-$(CONFIG_SND_MESON_GX_FORMATTER) += snd-soc-meson-gx-formatter.o
 obj-$(CONFIG_SND_MESON_AXG_FIFO) += snd-soc-meson-axg-fifo.o
 obj-$(CONFIG_SND_MESON_AXG_FRDDR) += snd-soc-meson-axg-frddr.o
 obj-$(CONFIG_SND_MESON_AXG_TODDR) += snd-soc-meson-axg-toddr.o

-- 
2.47.3


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* [PATCH v2 3/6] ASoC: meson: add AUDIN driver
  2026-09-17 21:02 [PATCH v2 0/6] ASoC: meson: gx: add base support for I2S audio input Valerio Setti
  2026-09-17 21:02 ` [PATCH v2 1/6] ASoC: dt-bindings: amlogic,gx-audin: add schema Valerio Setti
  2026-09-17 21:02 ` [PATCH v2 2/6] ASoC: meson: build gx-formatter as a separate module Valerio Setti
@ 2026-09-17 21:02 ` Valerio Setti
  2026-09-17 21:17   ` sashiko-bot
  2026-09-17 21:02 ` [PATCH v2 4/6] ASoC: meson: aiu: add I2S Capture DAI Valerio Setti
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 10+ messages in thread
From: Valerio Setti @ 2026-09-17 21:02 UTC (permalink / raw)
  To: Jerome Brunet, Liam Girdwood, Mark Brown, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Neil Armstrong, Kevin Hilman,
	Martin Blumenstingl, Jaroslav Kysela, Takashi Iwai
  Cc: linux-sound, devicetree, linux-arm-kernel, linux-amlogic,
	linux-kernel, Valerio Setti

AUDIN provides audio input support to the Amlogic GX based platforms
(GXBB/GXL). It is the counterpart of AIU for capture and it strictly
follows the same structure:
- It takes the ownership of the entire register range;
- It sets up peripheral clock and handles the reset;
- It adds support for I2S data formatter through a widget;
- It instantiates each of the FIFOs as component's DAI.

Both I2S data formatter and FIFO code live separate files
('audin-formatter-i2s' and 'audin-fifo', respectively) for a better
code organization. Unfortunately it wasn't possible to add them as
independent devices due to the messy register layout organization that
has been implemented by the manufacturer on this platform.

This component could ideally handle multiple input sources (SPDIF, I2S,
PCM, HDMI), but for the time being only I2S is added and has been tested.

Note: due to default register configuration, at boot all FIFO input sources
are set to "SPDIF". In order to be able to test I2S they must be switched
to "I2S". This can easily be achieved with:

  $ amixer sset 'AUDIN FIFO0 SRC SEL' 'I2S'

Signed-off-by: Valerio Setti <vsetti@baylibre.com>
---
 sound/soc/meson/Kconfig               |   9 +
 sound/soc/meson/Makefile              |   4 +
 sound/soc/meson/audin-fifo.c          | 367 ++++++++++++++++++++++++++++++++++
 sound/soc/meson/audin-formatter-i2s.c | 174 ++++++++++++++++
 sound/soc/meson/audin.c               | 186 +++++++++++++++++
 sound/soc/meson/audin.h               |  30 +++
 6 files changed, 770 insertions(+)

diff --git a/sound/soc/meson/Kconfig b/sound/soc/meson/Kconfig
index 4c40bf60dc4e..9d8589b5a88f 100644
--- a/sound/soc/meson/Kconfig
+++ b/sound/soc/meson/Kconfig
@@ -17,6 +17,14 @@ config SND_MESON_AIU
 	  Select Y or M to add support for the Audio output subsystem found
 	  in the Amlogic Meson8, Meson8b and GX SoC families
 
+config SND_MESON_AUDIN
+	tristate "Amlogic AUDIN"
+	select REGMAP_MMIO
+	select SND_MESON_GX_FORMATTER
+	help
+	  Select Y or M to add support for the audio input subsystem found
+	  in the Amlogic GX SoC family. Currently only I2S input is supported.
+
 config SND_MESON_AXG_FIFO
 	tristate
 	select REGMAP_MMIO
@@ -113,6 +121,7 @@ config SND_MESON_GX_SOUND_CARD
 	tristate "Amlogic GX Sound Card Support"
 	select SND_MESON_CARD_UTILS
 	imply SND_MESON_AIU
+	imply SND_MESON_AUDIN
 	help
 	  Select Y or M to add support for the GXBB/GXL SoC sound card
 
diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile
index 1b1fbe8a0458..4af75df0d19b 100644
--- a/sound/soc/meson/Makefile
+++ b/sound/soc/meson/Makefile
@@ -10,6 +10,9 @@ snd-soc-meson-aiu-y += aiu-fifo.o
 snd-soc-meson-aiu-y += aiu-fifo-i2s.o
 snd-soc-meson-aiu-y += aiu-fifo-spdif.o
 snd-soc-meson-gx-formatter-y := gx-formatter.o
+snd-soc-meson-audin-y := audin.o
+snd-soc-meson-audin-y += audin-formatter-i2s.o
+snd-soc-meson-audin-y += audin-fifo.o
 snd-soc-meson-axg-fifo-y := axg-fifo.o
 snd-soc-meson-axg-frddr-y := axg-frddr.o
 snd-soc-meson-axg-toddr-y := axg-toddr.o
@@ -30,6 +33,7 @@ snd-soc-meson-t9015-y := t9015.o
 
 obj-$(CONFIG_SND_MESON_AIU) += snd-soc-meson-aiu.o
 obj-$(CONFIG_SND_MESON_GX_FORMATTER) += snd-soc-meson-gx-formatter.o
+obj-$(CONFIG_SND_MESON_AUDIN) += snd-soc-meson-audin.o
 obj-$(CONFIG_SND_MESON_AXG_FIFO) += snd-soc-meson-axg-fifo.o
 obj-$(CONFIG_SND_MESON_AXG_FRDDR) += snd-soc-meson-axg-frddr.o
 obj-$(CONFIG_SND_MESON_AXG_TODDR) += snd-soc-meson-axg-toddr.o
diff --git a/sound/soc/meson/audin-fifo.c b/sound/soc/meson/audin-fifo.c
new file mode 100644
index 000000000000..39ca65e3cee9
--- /dev/null
+++ b/sound/soc/meson/audin-fifo.c
@@ -0,0 +1,367 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (c) 2026 BayLibre, SAS.
+// Author: Valerio Setti <vsetti@baylibre.com>
+
+#include <linux/bitfield.h>
+#include <linux/dma-mapping.h>
+#include <linux/hrtimer.h>
+#include <linux/math64.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/wordpart.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "audin.h"
+
+/* FIFO registers */
+#define AUDIN_FIFO_START	0x00
+#define AUDIN_FIFO_END		0x04
+#define AUDIN_FIFO_PTR		0x08
+
+/* FIFOx_CTRL1 registers and bits */
+#define AUDIN_FIFO_CTRL1			0x18
+#define  AUDIN_FIFO_CTRL1_DIN_BYTE_NUM_MASK	GENMASK(3, 2)
+#define  AUDIN_FIFO_CTRL1_DIN_POS_01_MASK	GENMASK(1, 0)
+
+/* When the FIFO is full the data is bulk transferred to memory. */
+#define AUDIN_FIFO_LANE_SIZE		8 /* bytes */
+#define AUDIN_FIFO_I2S_BLOCK		(AUDIN_FIFO_LANE_SIZE * 32) /* 256 bytes */
+
+static const struct snd_pcm_hardware audin_fifo_pcm_hw = {
+	.info = (SNDRV_PCM_INFO_INTERLEAVED |
+		 SNDRV_PCM_INFO_MMAP |
+		 SNDRV_PCM_INFO_MMAP_VALID |
+		 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		 SNDRV_PCM_INFO_PAUSE),
+	.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	.rate_min = 5512,
+	.rate_max = 192000,
+	.channels_min = 2,
+	.channels_max = 2,
+	/*
+	 * The FIFO only moves data to the memory once it is full, so its pointer
+	 * progresses one block at a time.
+	 */
+	.period_bytes_min = 2 * AUDIN_FIFO_I2S_BLOCK,
+	.period_bytes_max = UINT_MAX, /* Bounded by buffer_bytes_max */
+	.periods_min = 2,
+	.periods_max = UINT_MAX,
+	/*
+	 * It's 1 MB. There is no physical constraint for this; it's only meant to
+	 * provide some decently large buffer size where to store incoming data
+	 * and fit various period_bytes requests.
+	 */
+	.buffer_bytes_max = 4096 * AUDIN_FIFO_I2S_BLOCK,
+};
+
+struct audin_fifo_dai_data {
+	/*
+	 * The AUDIN peripheral has an IRQ to signal when data is received, but
+	 * it cannot grant a periodic behavior. The reason is that the register
+	 * which holds the address which triggers the IRQ must be updated
+	 * continuously. This creates a risk of overflow if for any reason the
+	 * ISR execution is delayed. Using a periodic timer is therefore simpler
+	 * and more reliable.
+	 */
+	struct hrtimer polling_timer;
+	struct snd_soc_dai *dai;
+	struct snd_pcm_substream *substream;
+	unsigned int period_bytes;
+	unsigned int byte_rate;
+	unsigned int last_pos; /* Position inside the allocated read buffer. */
+	bool running;
+};
+
+static unsigned int audin_fifo_pos(struct snd_soc_dai *dai,
+				   struct snd_pcm_runtime *runtime)
+{
+	unsigned int ptr = lower_32_bits(runtime->dma_addr);
+
+	regmap_read(dai->component->regmap, dai->driver->base + AUDIN_FIFO_PTR, &ptr);
+
+	return ptr - lower_32_bits(runtime->dma_addr);
+}
+
+static ktime_t audin_fifo_bytes_to_ns(struct audin_fifo_dai_data *data,
+				      unsigned int bytes)
+{
+	return div_u64((u64)bytes * NSEC_PER_SEC, data->byte_rate);
+}
+
+static int audin_fifo_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+				  struct snd_soc_dai *dai)
+{
+	struct audin_fifo_dai_data *data = snd_soc_dai_dma_data_get_capture(dai);
+	struct snd_soc_component *component = dai->component;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		snd_soc_component_update_bits(component, dai->driver->base + AUDIN_FIFO_CTRL,
+					      AUDIN_FIFO_CTRL_EN,
+					      AUDIN_FIFO_CTRL_EN);
+		WRITE_ONCE(data->running, true);
+		hrtimer_start(&data->polling_timer,
+			      audin_fifo_bytes_to_ns(data, data->period_bytes),
+			      HRTIMER_MODE_REL_SOFT);
+		break;
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_STOP:
+		WRITE_ONCE(data->running, false);
+		/*
+		 * PCM stream lock is held here. If the timer callback is running
+		 * (i.e. it cannot be stopped) there is also hrtimer_cancel()
+		 * in hw_free().
+		 */
+		hrtimer_try_to_cancel(&data->polling_timer);
+		snd_soc_component_update_bits(component, dai->driver->base + AUDIN_FIFO_CTRL,
+					      AUDIN_FIFO_CTRL_EN, 0);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int audin_fifo_dai_prepare(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
+{
+	struct audin_fifo_dai_data *data = snd_soc_dai_dma_data_get_capture(dai);
+	struct snd_soc_component *component = dai->component;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	dma_addr_t dma_end = runtime->dma_addr + runtime->dma_bytes - AUDIN_FIFO_LANE_SIZE;
+	unsigned int val;
+
+	data->last_pos = 0;
+
+	/*
+	 * Setup memory boundaries.
+	 * Note: DMA has a 32-bit mask, so it's OK to take lower 32 bits.
+	 */
+	snd_soc_component_write(component, dai->driver->base + AUDIN_FIFO_START,
+				lower_32_bits(runtime->dma_addr));
+	snd_soc_component_write(component, dai->driver->base + AUDIN_FIFO_PTR,
+				lower_32_bits(runtime->dma_addr));
+	snd_soc_component_write(component, dai->driver->base + AUDIN_FIFO_END,
+				lower_32_bits(dma_end));
+
+	/* Load new addresses (both LOAD and UG are self-clearing). */
+	val = AUDIN_FIFO_CTRL_LOAD | AUDIN_FIFO_CTRL_UG;
+	snd_soc_component_update_bits(component, dai->driver->base + AUDIN_FIFO_CTRL, val, val);
+
+	/* Reset (RST is self-clearing). */
+	snd_soc_component_update_bits(component, dai->driver->base + AUDIN_FIFO_CTRL,
+				      AUDIN_FIFO_CTRL_RST,
+				      AUDIN_FIFO_CTRL_RST);
+
+	return 0;
+}
+
+static int audin_fifo_dai_hw_params(struct snd_pcm_substream *substream,
+				    struct snd_pcm_hw_params *params,
+				    struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *component = dai->component;
+	struct audin_fifo_dai_data *data = snd_soc_dai_dma_data_get_capture(dai);
+	unsigned int val;
+
+	/*
+	 * The I2S input decoder passes 24 bits of left-justified data
+	 * but for the time being we only support 16 bit formatted samples
+	 * which means that we drop the LSB.
+	 */
+	val = FIELD_PREP(AUDIN_FIFO_CTRL1_DIN_POS_01_MASK, 1);
+	snd_soc_component_update_bits(component, dai->driver->base + AUDIN_FIFO_CTRL1,
+				      AUDIN_FIFO_CTRL1_DIN_POS_01_MASK,
+				      val);
+
+	/* Set sample size to 2 bytes (16 bit) */
+	val = FIELD_PREP(AUDIN_FIFO_CTRL1_DIN_BYTE_NUM_MASK, 1);
+	snd_soc_component_update_bits(component, dai->driver->base + AUDIN_FIFO_CTRL1,
+				      AUDIN_FIFO_CTRL1_DIN_BYTE_NUM_MASK,
+				      val);
+
+	/*
+	 * This is a bit counterintuitive. Even though the platform has a single
+	 * pin for I2S input which would mean that we can only support 2
+	 * channels, doing so would cause samples to be stored in a weird way
+	 * into the FIFO: all the samples from the 1st channel on the 1st half
+	 * of the FIFO, then samples from the 2nd channel in the other half. Of
+	 * course extra work would be required to properly interleave them
+	 * before returning to the userspace.
+	 * Setting a single channel mode instead solves the problem: samples
+	 * from 1st and 2nd channel are stored interleaved and sequentially in
+	 * the FIFO.
+	 */
+	val = FIELD_PREP(AUDIN_FIFO_CTRL_CHAN_MASK, 1);
+	snd_soc_component_update_bits(component, dai->driver->base + AUDIN_FIFO_CTRL,
+				      AUDIN_FIFO_CTRL_CHAN_MASK, val);
+
+	/*
+	 * FIFO is filled line by line and each of them is 8 bytes. The
+	 * problem is that each line is filled starting from the end,
+	 * so we need to properly reorder them before moving to the
+	 * RAM. This is the value required to properly re-order samples stored
+	 * in 16 bit format.
+	 */
+	val = FIELD_PREP(AUDIN_FIFO_CTRL_ENDIAN_MASK, 6);
+	snd_soc_component_update_bits(component, dai->driver->base + AUDIN_FIFO_CTRL,
+				      AUDIN_FIFO_CTRL_ENDIAN_MASK, val);
+
+	/* Used by the polling timer to convert a byte count into a delay. */
+	data->period_bytes = params_period_bytes(params);
+	data->byte_rate = params_rate(params) * params_channels(params) *
+			  params_physical_width(params) / BITS_PER_BYTE;
+
+	data->substream = substream;
+
+	return 0;
+}
+
+static int audin_fifo_dai_startup(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
+{
+	int ret;
+
+	snd_soc_set_runtime_hwparams(substream, &audin_fifo_pcm_hw);
+
+	ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
+					 SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+					 AUDIN_FIFO_I2S_BLOCK);
+	if (ret < 0) {
+		dev_err(dai->dev, "Failed to set constraint on buffer_bytes %d\n", ret);
+		return ret;
+	}
+
+	ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
+					 SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+					 AUDIN_FIFO_I2S_BLOCK);
+	if (ret < 0) {
+		dev_err(dai->dev, "Failed to set constraint on period_bytes %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int audin_fifo_dai_pcm_new(struct snd_soc_pcm_runtime *rtd,
+				  struct snd_soc_dai *dai)
+{
+	int ret;
+
+	ret = dma_coerce_mask_and_coherent(dai->dev, DMA_BIT_MASK(32));
+	if (ret) {
+		dev_err(dai->dev, "Failed to set DMA mask %d\n", ret);
+		return ret;
+	}
+
+	ret = snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+					     dai->dev,
+					     audin_fifo_pcm_hw.buffer_bytes_max,
+					     audin_fifo_pcm_hw.buffer_bytes_max);
+	if (ret) {
+		dev_err(dai->dev, "Failed to set PCM managed buffer %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static enum hrtimer_restart audin_fifo_timer_cb(struct hrtimer *timer)
+{
+	struct audin_fifo_dai_data *data =
+		container_of(timer, struct audin_fifo_dai_data, polling_timer);
+	struct snd_pcm_runtime *runtime = data->substream->runtime;
+	unsigned int curr_pos, delta, sleep_time;
+
+	if (!READ_ONCE(data->running))
+		return HRTIMER_NORESTART;
+
+	curr_pos = audin_fifo_pos(data->dai, runtime);
+	delta = (curr_pos >= data->last_pos) ?
+		curr_pos - data->last_pos :
+		(runtime->dma_bytes - data->last_pos) + curr_pos;
+
+	/* Report only when the period is completed. */
+	if (delta >= data->period_bytes) {
+		data->last_pos = curr_pos;
+		delta = delta % data->period_bytes;
+		snd_pcm_period_elapsed(data->substream);
+	}
+
+	/* Sleep until the period is completed. */
+	sleep_time = round_up(data->period_bytes - delta, AUDIN_FIFO_I2S_BLOCK);
+	hrtimer_forward_now(timer, audin_fifo_bytes_to_ns(data, sleep_time));
+
+	if (!READ_ONCE(data->running))
+		return HRTIMER_NORESTART;
+	return HRTIMER_RESTART;
+}
+
+static int audin_fifo_dai_probe(struct snd_soc_dai *dai)
+{
+	struct audin_fifo_dai_data *data;
+
+	data = kzalloc_obj(*data);
+	if (!data)
+		return -ENOMEM;
+
+	data->dai = dai;
+
+	hrtimer_setup(&data->polling_timer, audin_fifo_timer_cb, CLOCK_MONOTONIC,
+		      HRTIMER_MODE_REL_SOFT);
+
+	snd_soc_dai_dma_data_set_capture(dai, data);
+
+	return 0;
+}
+
+static int audin_fifo_dai_remove(struct snd_soc_dai *dai)
+{
+	kfree(snd_soc_dai_dma_data_get_capture(dai));
+
+	return 0;
+}
+
+const struct snd_soc_dai_ops audin_fifo_dai_ops = {
+	.trigger	= audin_fifo_dai_trigger,
+	.prepare	= audin_fifo_dai_prepare,
+	.hw_params	= audin_fifo_dai_hw_params,
+	.startup	= audin_fifo_dai_startup,
+	.pcm_new	= audin_fifo_dai_pcm_new,
+	.probe		= audin_fifo_dai_probe,
+	.remove		= audin_fifo_dai_remove,
+};
+
+snd_pcm_uframes_t audin_fifo_component_pointer(struct snd_soc_component *component,
+					       struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+	struct snd_soc_dai *dai = snd_soc_rtd_to_cpu(rtd, 0);
+
+	return bytes_to_frames(runtime, audin_fifo_pos(dai, runtime));
+}
+
+/*
+ * sync_stop() is called before hw_params(), hw_free(), suspend(), prepare().
+ * Cancelling the hrtimer here ensures the timer is really stopped even in
+ * case the stream is run->stop->prepare->start.
+ */
+int audin_fifo_sync_stop(struct snd_soc_component *component, struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+	struct snd_soc_dai *dai = snd_soc_rtd_to_cpu(rtd, 0);
+	struct audin_fifo_dai_data *data = snd_soc_dai_dma_data_get_capture(dai);
+
+	hrtimer_cancel(&data->polling_timer);
+	WRITE_ONCE(data->running, false);
+
+	return 0;
+}
diff --git a/sound/soc/meson/audin-formatter-i2s.c b/sound/soc/meson/audin-formatter-i2s.c
new file mode 100644
index 000000000000..8bbb5cdb52dc
--- /dev/null
+++ b/sound/soc/meson/audin-formatter-i2s.c
@@ -0,0 +1,174 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (c) 2026 BayLibre, SAS.
+// Author: Valerio Setti <vsetti@baylibre.com>
+
+#include <linux/bitfield.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "audin.h"
+#include "gx-formatter.h"
+
+/* I2SIN_CTRL register and bits */
+#define AUDIN_I2SIN_CTRL			0x40
+#define  AUDIN_I2SIN_CTRL_I2SIN_DIR		BIT(0)
+#define  AUDIN_I2SIN_CTRL_I2SIN_CLK_SEL		BIT(1)
+#define  AUDIN_I2SIN_CTRL_I2SIN_LRCLK_SEL	BIT(2)
+#define  AUDIN_I2SIN_CTRL_I2SIN_BCLK_INV	BIT(3)
+#define  AUDIN_I2SIN_CTRL_I2SIN_LRCLK_SKEW_MASK	GENMASK(6, 4)
+#define  AUDIN_I2SIN_CTRL_I2SIN_LRCLK_INV	BIT(7)
+#define  AUDIN_I2SIN_CTRL_I2SIN_SIZE_MASK	GENMASK(9, 8)
+#define  AUDIN_I2SIN_CTRL_I2SIN_CHAN_EN_MASK	GENMASK(13, 10)
+#define  AUDIN_I2SIN_CTRL_I2SIN_EN		BIT(15)
+
+static struct snd_soc_dai *
+audin_formatter_i2s_get_be(struct snd_soc_dapm_widget *w)
+{
+	struct snd_soc_dapm_path *p;
+	struct snd_soc_dai *be;
+
+	snd_soc_dapm_widget_for_each_source_path(w, p) {
+		if (!p->connect)
+			continue;
+
+		if (p->source->id == snd_soc_dapm_dai_out)
+			return (struct snd_soc_dai *)p->source->priv;
+
+		be = audin_formatter_i2s_get_be(p->source);
+		if (be)
+			return be;
+	}
+
+	return NULL;
+}
+
+static struct gx_stream *
+audin_formatter_i2s_get_stream(struct snd_soc_dapm_widget *w)
+{
+	struct snd_soc_dai *be = audin_formatter_i2s_get_be(w);
+
+	if (!be)
+		return NULL;
+
+	return snd_soc_dai_dma_data_get_capture(be);
+}
+
+static void audin_formatter_i2s_enable(struct regmap *map)
+{
+	regmap_update_bits(map, AUDIN_I2SIN_CTRL,
+			   AUDIN_I2SIN_CTRL_I2SIN_EN,
+			   AUDIN_I2SIN_CTRL_I2SIN_EN);
+}
+
+static void audin_formatter_i2s_disable(struct regmap *map)
+{
+	regmap_update_bits(map, AUDIN_I2SIN_CTRL,
+			   AUDIN_I2SIN_CTRL_I2SIN_EN, 0);
+}
+
+static int audin_formatter_i2s_prepare(struct regmap *map,
+				       const struct gx_formatter_hw *quirks,
+				       struct gx_stream *ts)
+{
+	unsigned int val;
+	int ret;
+
+	/*
+	 * I2S decoder always outputs 24 bits to the FIFO according to the
+	 * manual. The only thing we can change through
+	 * AUDIN_I2SIN_CTRL_I2SIN_SIZE_MASK is the following:
+	 * - 0 -> output[23:0] = {original[23:8],8’d0}
+	 * - 1 -> output[23:0] = {original[23:6],6’d0}
+	 * - 2 -> output[23:0] = {original[23:4],4’d0}
+	 * - 3 -> output[23:0] = {original[23:0]}
+	 *
+	 * We use 3 here and, in case of 16 bit format, we filter unnecessary
+	 * bytes at FIFO stage.
+	 * Note: data is left-justified, so in case of 16 bits samples, this
+	 *       means that the LSB is to be discarded at FIFO level and the
+	 *       relevant part is in bits [23:8].
+	 */
+	val = FIELD_PREP(AUDIN_I2SIN_CTRL_I2SIN_SIZE_MASK, 3);
+	ret = regmap_update_bits(map, AUDIN_I2SIN_CTRL,
+				 AUDIN_I2SIN_CTRL_I2SIN_SIZE_MASK, val);
+	if (ret)
+		return ret;
+
+	/*
+	 * The manual claims that this platform supports up to 4 streams
+	 * (8 channels), but currently only 1 stream (2 channels) has been
+	 * tested and it's supported.
+	 */
+	val = FIELD_PREP(AUDIN_I2SIN_CTRL_I2SIN_CHAN_EN_MASK, 1);
+	ret = regmap_update_bits(map, AUDIN_I2SIN_CTRL,
+				 AUDIN_I2SIN_CTRL_I2SIN_CHAN_EN_MASK, val);
+	if (ret)
+		return ret;
+
+	/*
+	 * Use clocks from AIU and not from the pads since we only want to
+	 * support master mode.
+	 */
+	val = AUDIN_I2SIN_CTRL_I2SIN_CLK_SEL |
+	      AUDIN_I2SIN_CTRL_I2SIN_LRCLK_SEL |
+	      AUDIN_I2SIN_CTRL_I2SIN_DIR;
+	ret = regmap_update_bits(map, AUDIN_I2SIN_CTRL, val, val);
+	if (ret)
+		return ret;
+
+	switch (ts->iface->fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_IB_NF:
+		val = AUDIN_I2SIN_CTRL_I2SIN_BCLK_INV;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		val = AUDIN_I2SIN_CTRL_I2SIN_LRCLK_INV;
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		val = AUDIN_I2SIN_CTRL_I2SIN_BCLK_INV | AUDIN_I2SIN_CTRL_I2SIN_LRCLK_INV;
+		break;
+	case SND_SOC_DAIFMT_NB_NF:
+		val = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = regmap_update_bits(map, AUDIN_I2SIN_CTRL,
+				 AUDIN_I2SIN_CTRL_I2SIN_LRCLK_INV |
+				 AUDIN_I2SIN_CTRL_I2SIN_BCLK_INV, val);
+	if (ret)
+		return ret;
+
+	switch (ts->iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		val = 1;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		val = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	val = FIELD_PREP(AUDIN_I2SIN_CTRL_I2SIN_LRCLK_SKEW_MASK, val);
+	ret = regmap_update_bits(map, AUDIN_I2SIN_CTRL,
+				 AUDIN_I2SIN_CTRL_I2SIN_LRCLK_SKEW_MASK,
+				 val);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static const struct gx_formatter_ops audin_formatter_i2s_ops = {
+	.get_stream	= audin_formatter_i2s_get_stream,
+	.prepare	= audin_formatter_i2s_prepare,
+	.enable		= audin_formatter_i2s_enable,
+	.disable	= audin_formatter_i2s_disable,
+};
+
+const struct gx_formatter_driver audin_formatter_i2s_drv = {
+	.ops		= &audin_formatter_i2s_ops,
+};
diff --git a/sound/soc/meson/audin.c b/sound/soc/meson/audin.c
new file mode 100644
index 000000000000..ec024267ac29
--- /dev/null
+++ b/sound/soc/meson/audin.c
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (c) 2026 BayLibre, SAS.
+// Author: Valerio Setti <vsetti@baylibre.com>
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <sound/soc.h>
+
+#include "audin.h"
+#include "gx-formatter.h"
+
+#define AUDIN_FIFO0_BASE	0x80
+#define AUDIN_FIFO1_BASE	0xCC
+#define AUDIN_FIFO2_BASE	0x114
+
+static const char * const audin_fifo_input_sel_texts[] = {
+	"SPDIF", "I2S", "PCM", "HDMI", "Demodulator"
+};
+
+static SOC_ENUM_SINGLE_DECL(audin_fifo0_input_sel_enum, AUDIN_FIFO0_BASE + AUDIN_FIFO_CTRL,
+			    AUDIN_FIFO_CTRL_DIN_SEL_OFF,
+			    audin_fifo_input_sel_texts);
+static SOC_ENUM_SINGLE_DECL(audin_fifo1_input_sel_enum, AUDIN_FIFO1_BASE + AUDIN_FIFO_CTRL,
+			    AUDIN_FIFO_CTRL_DIN_SEL_OFF,
+			    audin_fifo_input_sel_texts);
+static SOC_ENUM_SINGLE_DECL(audin_fifo2_input_sel_enum, AUDIN_FIFO2_BASE + AUDIN_FIFO_CTRL,
+			    AUDIN_FIFO_CTRL_DIN_SEL_OFF,
+			    audin_fifo_input_sel_texts);
+
+static const struct snd_kcontrol_new audin_fifo0_input_sel_mux =
+	SOC_DAPM_ENUM("FIFO0 Input Source", audin_fifo0_input_sel_enum);
+static const struct snd_kcontrol_new audin_fifo1_input_sel_mux =
+	SOC_DAPM_ENUM("FIFO1 Input Source", audin_fifo1_input_sel_enum);
+static const struct snd_kcontrol_new audin_fifo2_input_sel_mux =
+	SOC_DAPM_ENUM("FIFO2 Input Source", audin_fifo2_input_sel_enum);
+
+#define AUDIN_WIDGET_I2S_FORMATTER	0
+
+static struct snd_soc_dapm_widget audin_dapm_widgets[] = {
+	[AUDIN_WIDGET_I2S_FORMATTER] =
+		SND_SOC_DAPM_PGA_E("I2S Formatter", SND_SOC_NOPM, 0, 0, NULL, 0,
+				   gx_formatter_event,
+				   (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD)),
+	SND_SOC_DAPM_MUX("FIFO0 SRC SEL", SND_SOC_NOPM, 0, 0,
+			 &audin_fifo0_input_sel_mux),
+	SND_SOC_DAPM_MUX("FIFO1 SRC SEL", SND_SOC_NOPM, 0, 0,
+			 &audin_fifo1_input_sel_mux),
+	SND_SOC_DAPM_MUX("FIFO2 SRC SEL", SND_SOC_NOPM, 0, 0,
+			 &audin_fifo2_input_sel_mux),
+};
+
+static const struct snd_soc_dapm_route audin_dapm_routes[] = {
+	{ "FIFO0 SRC SEL", "I2S", "I2S Formatter" },
+	{ "FIFO0 Capture", NULL, "FIFO0 SRC SEL" },
+	{ "FIFO1 SRC SEL", "I2S", "I2S Formatter" },
+	{ "FIFO1 Capture", NULL, "FIFO1 SRC SEL" },
+	{ "FIFO2 SRC SEL", "I2S", "I2S Formatter" },
+	{ "FIFO2 Capture", NULL, "FIFO2 SRC SEL" },
+};
+
+static struct snd_soc_dai_driver audin_dai_drv[] = {
+	{
+		.name = "FIFO0",
+		.base = AUDIN_FIFO0_BASE,
+		.capture = {
+			.stream_name	= "FIFO0 Capture",
+			.channels_min	= 2,
+			.channels_max	= 2,
+			.rates		= SNDRV_PCM_RATE_CONTINUOUS,
+			.rate_min	= 5512,
+			.rate_max	= 192000,
+			.formats	= SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.ops = &audin_fifo_dai_ops,
+	},
+	{
+		.name = "FIFO1",
+		.base = AUDIN_FIFO1_BASE,
+		.capture = {
+			.stream_name	= "FIFO1 Capture",
+			.channels_min	= 2,
+			.channels_max	= 2,
+			.rates		= SNDRV_PCM_RATE_CONTINUOUS,
+			.rate_min	= 5512,
+			.rate_max	= 192000,
+			.formats	= SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.ops = &audin_fifo_dai_ops,
+	},
+	{
+		.name = "FIFO2",
+		.base = AUDIN_FIFO2_BASE,
+		.capture = {
+			.stream_name	= "FIFO2 Capture",
+			.channels_min	= 2,
+			.channels_max	= 2,
+			.rates		= SNDRV_PCM_RATE_CONTINUOUS,
+			.rate_min	= 5512,
+			.rate_max	= 192000,
+			.formats	= SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.ops = &audin_fifo_dai_ops,
+	},
+};
+
+static const struct snd_soc_component_driver audin_component = {
+	.dapm_widgets = audin_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(audin_dapm_widgets),
+	.dapm_routes = audin_dapm_routes,
+	.num_dapm_routes = ARRAY_SIZE(audin_dapm_routes),
+	.pointer = audin_fifo_component_pointer,
+	.sync_stop = audin_fifo_sync_stop,
+};
+
+static const struct regmap_config audin_regmap_cfg = {
+	.reg_bits	= 32,
+	.val_bits	= 32,
+	.reg_stride	= 4,
+	.max_register	= 0x148,
+};
+
+static int audin_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	void __iomem *mmio;
+	struct regmap *regmap;
+	struct clk *clk;
+	int ret;
+
+	ret = device_reset(dev);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to reset device\n");
+
+	mmio = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(mmio))
+		return dev_err_probe(dev, PTR_ERR(mmio), "Failed to ioremap memory\n");
+
+	regmap = devm_regmap_init_mmio(dev, mmio, &audin_regmap_cfg);
+	if (IS_ERR(regmap))
+		return dev_err_probe(dev, PTR_ERR(regmap), "Failed to init regmap\n");
+
+	clk = devm_clk_get_enabled(dev, NULL);
+	if (IS_ERR(clk))
+		return dev_err_probe(dev, PTR_ERR(clk), "Failed to get clock\n");
+
+	ret = gx_formatter_create(dev, &audin_dapm_widgets[AUDIN_WIDGET_I2S_FORMATTER],
+				  &audin_formatter_i2s_drv, regmap);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to allocate formatter\n");
+
+	ret = devm_snd_soc_register_component(dev, &audin_component,
+					      audin_dai_drv, ARRAY_SIZE(audin_dai_drv));
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to register component\n");
+
+	return 0;
+}
+
+static void audin_remove(struct platform_device *pdev)
+{
+	gx_formatter_free(&audin_dapm_widgets[AUDIN_WIDGET_I2S_FORMATTER]);
+}
+
+static const struct of_device_id audin_of_match[] = {
+	{ .compatible = "amlogic,gx-audin" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, audin_of_match);
+
+static struct platform_driver audin_driver = {
+	.driver = {
+		.name = "gx-audin",
+		.of_match_table = audin_of_match,
+	},
+	.probe = audin_probe,
+	.remove = audin_remove,
+};
+module_platform_driver(audin_driver);
+
+MODULE_DESCRIPTION("Meson GX AUDIN driver");
+MODULE_AUTHOR("Valerio Setti <vsetti@baylibre.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/meson/audin.h b/sound/soc/meson/audin.h
new file mode 100644
index 000000000000..7f3b58a780c0
--- /dev/null
+++ b/sound/soc/meson/audin.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * Copyright (c) 2026 BayLibre, SAS.
+ * Author: Valerio Setti <vsetti@baylibre.com>
+ */
+
+#ifndef _MESON_AUDIN_H
+#define _MESON_AUDIN_H
+
+#include "gx-formatter.h"
+
+/* FIFOx CTRL registers and bits */
+#define AUDIN_FIFO_CTRL			0x14
+#define  AUDIN_FIFO_CTRL_EN		BIT(0)
+#define  AUDIN_FIFO_CTRL_RST		BIT(1)
+#define  AUDIN_FIFO_CTRL_LOAD		BIT(2)
+#define  AUDIN_FIFO_CTRL_DIN_SEL_OFF	3
+#define  AUDIN_FIFO_CTRL_ENDIAN_MASK	GENMASK(10, 8)
+#define  AUDIN_FIFO_CTRL_CHAN_MASK	GENMASK(14, 11)
+#define  AUDIN_FIFO_CTRL_UG		BIT(15)
+
+extern const struct gx_formatter_driver audin_formatter_i2s_drv;
+
+extern const struct snd_soc_dai_ops audin_fifo_dai_ops;
+extern snd_pcm_uframes_t audin_fifo_component_pointer(struct snd_soc_component *component,
+						      struct snd_pcm_substream *substream);
+extern int audin_fifo_sync_stop(struct snd_soc_component *component,
+				struct snd_pcm_substream *substream);
+
+#endif

-- 
2.47.3


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* [PATCH v2 4/6] ASoC: meson: aiu: add I2S Capture DAI
  2026-09-17 21:02 [PATCH v2 0/6] ASoC: meson: gx: add base support for I2S audio input Valerio Setti
                   ` (2 preceding siblings ...)
  2026-09-17 21:02 ` [PATCH v2 3/6] ASoC: meson: add AUDIN driver Valerio Setti
@ 2026-09-17 21:02 ` Valerio Setti
  2026-09-17 21:20   ` sashiko-bot
  2026-09-17 21:02 ` [PATCH v2 5/6] ASoC: meson: gx-card: add support for audin FIFO Valerio Setti
  2026-09-17 21:02 ` [PATCH v2 6/6] arm64: dts: amlogic: gx: add AUDIN node Valerio Setti
  5 siblings, 1 reply; 10+ messages in thread
From: Valerio Setti @ 2026-09-17 21:02 UTC (permalink / raw)
  To: Jerome Brunet, Liam Girdwood, Mark Brown, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Neil Armstrong, Kevin Hilman,
	Martin Blumenstingl, Jaroslav Kysela, Takashi Iwai
  Cc: linux-sound, devicetree, linux-arm-kernel, linux-amlogic,
	linux-kernel, Valerio Setti

Add capture stream to CPU_I2S_ENCODER making it able to handle both
playback and capture streams. Captured data is formatted from
"audin-formatter-i2s" widget, buffered from "audin-fifo" into an internal
FIFO and then bulk transmitted into RAM when the FIFO is full.

As for the naming of the stream "I2S Encoder" is kept as base following
the same pattern used for the playback stream and with the goal to minimize
changes.

Supported format for capture is currently limited to S16_LE because it's
the only one that has been tested. According to the manual also S24_LE
should be supported, but this is kept as follow-up.

This capture support also takes advantage of the following commits that
has recently been merged:

commit df3c987ab3d7 ("ASoC: meson: aiu-encoder-i2s: reflect bs quirk in hw constraints")
commit e82159384a50 ("ASoC: meson: aiu-encoder-i2s: use the core symmetric_rate handling")

They ensure bs_quirk and rate symmetry across playback and capture streams.

Signed-off-by: Valerio Setti <vsetti@baylibre.com>
---
 sound/soc/meson/aiu.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/sound/soc/meson/aiu.c b/sound/soc/meson/aiu.c
index 535fbf7c1639..14d0e20a4bd6 100644
--- a/sound/soc/meson/aiu.c
+++ b/sound/soc/meson/aiu.c
@@ -153,6 +153,13 @@ static struct snd_soc_dai_driver aiu_cpu_dai_drv[] = {
 			.rates = SNDRV_PCM_RATE_8000_192000,
 			.formats = AIU_FORMATS,
 		},
+		.capture = {
+			.stream_name = "I2S Encoder Capture",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
 		.ops = &aiu_encoder_i2s_dai_ops,
 		.symmetric_rate = 1,
 	},

-- 
2.47.3


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* [PATCH v2 5/6] ASoC: meson: gx-card: add support for audin FIFO
  2026-09-17 21:02 [PATCH v2 0/6] ASoC: meson: gx: add base support for I2S audio input Valerio Setti
                   ` (3 preceding siblings ...)
  2026-09-17 21:02 ` [PATCH v2 4/6] ASoC: meson: aiu: add I2S Capture DAI Valerio Setti
@ 2026-09-17 21:02 ` Valerio Setti
  2026-09-17 21:02 ` [PATCH v2 6/6] arm64: dts: amlogic: gx: add AUDIN node Valerio Setti
  5 siblings, 0 replies; 10+ messages in thread
From: Valerio Setti @ 2026-09-17 21:02 UTC (permalink / raw)
  To: Jerome Brunet, Liam Girdwood, Mark Brown, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Neil Armstrong, Kevin Hilman,
	Martin Blumenstingl, Jaroslav Kysela, Takashi Iwai
  Cc: linux-sound, devicetree, linux-arm-kernel, linux-amlogic,
	linux-kernel, Valerio Setti

Slightly modify "gx_card_cpu_identify()" by making the compatible matching
string an input parameter. This allows to easily support also AUDIN's FIFO
with minimal changes.

Signed-off-by: Valerio Setti <vsetti@baylibre.com>
---
 sound/soc/meson/gx-card.c | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/sound/soc/meson/gx-card.c b/sound/soc/meson/gx-card.c
index 932be0a09306..35bc47254752 100644
--- a/sound/soc/meson/gx-card.c
+++ b/sound/soc/meson/gx-card.c
@@ -66,10 +66,10 @@ static int gx_card_parse_i2s(struct snd_soc_card *card,
 }
 
 static int gx_card_cpu_identify(struct snd_soc_dai_link_component *c,
-				char *match)
+				const char *compatible_match, const char *dai_match)
 {
-	if (of_device_is_compatible(c->of_node, DT_PREFIX "aiu")) {
-		if (strstr(c->dai_name, match))
+	if (of_device_is_compatible(c->of_node, compatible_match)) {
+		if (strstr(c->dai_name, dai_match))
 			return 1;
 	}
 
@@ -96,21 +96,23 @@ static int gx_card_add_link(struct snd_soc_card *card, struct device_node *np,
 	if (ret)
 		return ret;
 
-	if (gx_card_cpu_identify(dai_link->cpus, "FIFO"))
-		return  meson_card_set_fe_link(card, dai_link, np, true);
+	if (gx_card_cpu_identify(dai_link->cpus, DT_PREFIX "aiu", "FIFO"))
+		return meson_card_set_fe_link(card, dai_link, np, true);
+	if (gx_card_cpu_identify(dai_link->cpus, DT_PREFIX "gx-audin", "FIFO"))
+		return meson_card_set_fe_link(card, dai_link, np, false);
 
 	ret = meson_card_set_be_link(card, dai_link, np);
 	if (ret)
 		return ret;
 
 	/* Or apply codec to codec params if necessary */
-	if (gx_card_cpu_identify(dai_link->cpus, "CODEC CTRL")) {
+	if (gx_card_cpu_identify(dai_link->cpus, DT_PREFIX "aiu", "CODEC CTRL")) {
 		dai_link->c2c_params = &codec_params;
 		dai_link->num_c2c_params = 1;
 	} else {
 		dai_link->no_pcm = 1;
 		/* Check if the cpu is the i2s encoder and parse i2s data */
-		if (gx_card_cpu_identify(dai_link->cpus, "I2S Encoder"))
+		if (gx_card_cpu_identify(dai_link->cpus, DT_PREFIX "aiu", "I2S Encoder"))
 			ret = gx_card_parse_i2s(card, np, index);
 	}
 

-- 
2.47.3


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* [PATCH v2 6/6] arm64: dts: amlogic: gx: add AUDIN node
  2026-09-17 21:02 [PATCH v2 0/6] ASoC: meson: gx: add base support for I2S audio input Valerio Setti
                   ` (4 preceding siblings ...)
  2026-09-17 21:02 ` [PATCH v2 5/6] ASoC: meson: gx-card: add support for audin FIFO Valerio Setti
@ 2026-09-17 21:02 ` Valerio Setti
  5 siblings, 0 replies; 10+ messages in thread
From: Valerio Setti @ 2026-09-17 21:02 UTC (permalink / raw)
  To: Jerome Brunet, Liam Girdwood, Mark Brown, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Neil Armstrong, Kevin Hilman,
	Martin Blumenstingl, Jaroslav Kysela, Takashi Iwai
  Cc: linux-sound, devicetree, linux-arm-kernel, linux-amlogic,
	linux-kernel, Valerio Setti

AUDIN provides audio input support to Amlogic Meson GXBB/GXL platforms.

What's missing after this to get capture support working is to connect a
capture capable codec to the SoC. For example to test these changes an
NXP SGTL5000 chip has been connected to an OdroidC2 (Meson GXBB) board
using a custom hat board.

The following additions/overlay are required on top of
'amlogic/meson-gxbb-odroidc2.dts' to make that hat board to work:

    &sound {
            audio-routing = "AUDIN I2S Formatter", "AIU I2S Encoder Capture";

            dai-link-1 {
                    codec-1 {
                            sound-dai = <&sgtl5000>;
                    };
            };

            dai-link-3 {
                    sound-dai = <&audin 0>;
            };
    };

    &audin {
            status = "okay";
    };

Then, from userspace:

    $ amixer sset 'AUDIN FIFO0 SRC SEL' 'I2S'
    $ arecord -l  # find the "FIFO Capture" device
    $ arecord -D hw:0,<N> -f S16_LE -r 48000 -c 2 /tmp/in.wav

Signed-off-by: Valerio Setti <vsetti@baylibre.com>
---
 arch/arm64/boot/dts/amlogic/meson-gx.dtsi   | 9 +++++++++
 arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi | 6 ++++++
 arch/arm64/boot/dts/amlogic/meson-gxl.dtsi  | 6 ++++++
 3 files changed, 21 insertions(+)

diff --git a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
index c1d8e81d95cb..54b6cc0bb8ff 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
@@ -436,6 +436,15 @@ watchdog@98d0 {
 				reg = <0x0 0x098d0 0x0 0x10>;
 				clocks = <&xtal>;
 			};
+
+			audin: audio-controller@a000 {
+				compatible = "amlogic,gx-audin";
+				reg = <0x0 0xa000 0x0 0x148>;
+				interrupts = <GIC_SPI 7 IRQ_TYPE_EDGE_RISING>;
+				sound-name-prefix = "AUDIN";
+				#sound-dai-cells = <1>;
+				status = "disabled";
+			};
 		};
 
 		gic: interrupt-controller@c4301000 {
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
index a9c830a570cc..7ad69992a226 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
@@ -84,6 +84,12 @@ &aiu {
 	resets = <&reset RESET_AIU>;
 };
 
+&audin {
+	compatible = "amlogic,gxbb-audin", "amlogic,gx-audin";
+	clocks = <&clkc CLKID_I2S_SPDIF>;
+	resets = <&reset RESET_AUDIN>;
+};
+
 &aobus {
 	pinctrl_aobus: pinctrl@14 {
 		compatible = "amlogic,meson-gxbb-aobus-pinctrl";
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
index d5e0f72892f8..9181eba81eb7 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
@@ -100,6 +100,12 @@ &aiu {
 	resets = <&reset RESET_AIU>;
 };
 
+&audin {
+	compatible = "amlogic,gxl-audin", "amlogic,gx-audin";
+	clocks = <&clkc CLKID_I2S_SPDIF>;
+	resets = <&reset RESET_AUDIN>;
+};
+
 &apb {
 	usb2_phy0: phy@78000 {
 		compatible = "amlogic,meson-gxl-usb2-phy";

-- 
2.47.3


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* Re: [PATCH v2 1/6] ASoC: dt-bindings: amlogic,gx-audin: add schema
  2026-09-17 21:02 ` [PATCH v2 1/6] ASoC: dt-bindings: amlogic,gx-audin: add schema Valerio Setti
@ 2026-09-17 21:07   ` sashiko-bot
  0 siblings, 0 replies; 10+ messages in thread
From: sashiko-bot @ 2026-09-17 21:07 UTC (permalink / raw)
  To: Valerio Setti; +Cc: conor+dt, devicetree, robh, neil.armstrong, linux-amlogic

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] dt-bindings: amlogic,gx-audin: missing const constraint for #sound-dai-cells
--

commit 1813eec5eb42b623e2aae169825ca6cb3dcd09e8
Author: Valerio Setti <vsetti@baylibre.com>

ASoC: dt-bindings: amlogic,gx-audin: add schema

AUDIN provides I2S audio input support to the Meson GX platforms
(GXBB/GXL).

> diff --git a/Documentation/devicetree/bindings/sound/amlogic,gx-audin.yaml b/Documentation/devicetree/bindings/sound/amlogic,gx-audin.yaml
> new file mode 100644
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/sound/amlogic,gx-audin.yaml
[ ... ]
> +allOf:
> +  - $ref: dai-common.yaml#
> +
> +properties:
> +  compatible:

[Severity: Low]
Should #sound-dai-cells be explicitly defined here with a const constraint?

The binding requires #sound-dai-cells below, but fails to define it in the
properties block with a const constraint. Relying on dai-common.yaml, which
only defines it as true (unconstrained), allows invalid configurations with
incorrect cell counts (e.g., <0> or <2>) to silently pass validation.

> +    items:
> +      - enum:
> +          - amlogic,gxbb-audin
> +          - amlogic,gxl-audin
> +      - const: amlogic,gx-audin
[ ... ]
> +required:
> +  - compatible
> +  - reg
> +  - resets
> +  - interrupts
> +  - clocks
> +  - "#sound-dai-cells"
> +

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260917-audin-v2-0-38de4bc1de34@baylibre.com?part=1

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* Re: [PATCH v2 3/6] ASoC: meson: add AUDIN driver
  2026-09-17 21:02 ` [PATCH v2 3/6] ASoC: meson: add AUDIN driver Valerio Setti
@ 2026-09-17 21:17   ` sashiko-bot
  0 siblings, 0 replies; 10+ messages in thread
From: sashiko-bot @ 2026-09-17 21:17 UTC (permalink / raw)
  To: Valerio Setti; +Cc: linux-amlogic, conor+dt, neil.armstrong, devicetree, robh

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Medium] sound/soc/meson: period drift in timer callback due to incorrect last_pos update
- [Medium] sound/soc/meson: global state pollution due to mutating static widget array
- [Medium] sound/soc/meson: stack overflow in recursive DAPM graph traversal without cycle detection
--

commit cc303f24603b3290227ada8353fd36a74cf1a83e
Author: Valerio Setti <vsetti@baylibre.com>

ASoC: meson: add AUDIN driver

AUDIN provides audio input support to the Amlogic GX based platforms
(GXBB/GXL). It adds support for I2S data formatter through a widget
and instantiates each of the FIFOs as component's DAI.

> diff --git a/sound/soc/meson/audin-fifo.c b/sound/soc/meson/audin-fifo.c
> new file mode 100644
> index 0000000000000..39ca65e3cee9b
> --- /dev/null
> +++ b/sound/soc/meson/audin-fifo.c

[ ... ]

> +static enum hrtimer_restart audin_fifo_timer_cb(struct hrtimer *timer)
> +{
> +	struct audin_fifo_dai_data *data =
> +		container_of(timer, struct audin_fifo_dai_data, polling_timer);
> +	struct snd_pcm_runtime *runtime = data->substream->runtime;
> +	unsigned int curr_pos, delta, sleep_time;
> +
> +	if (!READ_ONCE(data->running))
> +		return HRTIMER_NORESTART;
> +
> +	curr_pos = audin_fifo_pos(data->dai, runtime);
> +	delta = (curr_pos >= data->last_pos) ?
> +		curr_pos - data->last_pos :
> +		(runtime->dma_bytes - data->last_pos) + curr_pos;
> +
> +	/* Report only when the period is completed. */
> +	if (delta >= data->period_bytes) {
> +		data->last_pos = curr_pos;

[Severity: Medium]
Does setting data->last_pos directly to curr_pos introduce cumulative period 
drift? 

Because the hrtimer callback might be delayed, curr_pos might be past the
exact period boundary. Updating the base to curr_pos locks in these 
scheduling delays, which could cause the reported hw_ptr to desynchronize 
from actual hardware DMA and lead to audio stuttering or xruns.

Should data->last_pos be incremented by exact multiples of data->period_bytes 
instead?

> +		delta = delta % data->period_bytes;
> +		snd_pcm_period_elapsed(data->substream);
> +	}

[ ... ]

> diff --git a/sound/soc/meson/audin-formatter-i2s.c b/sound/soc/meson/audin-formatter-i2s.c
> new file mode 100644
> index 0000000000000..8bbb5cdb52dc8
> --- /dev/null
> +++ b/sound/soc/meson/audin-formatter-i2s.c

[ ... ]

> +static struct snd_soc_dai *
> +audin_formatter_i2s_get_be(struct snd_soc_dapm_widget *w)
> +{
> +	struct snd_soc_dapm_path *p;
> +	struct snd_soc_dai *be;
> +
> +	snd_soc_dapm_widget_for_each_source_path(w, p) {
> +		if (!p->connect)
> +			continue;
> +
> +		if (p->source->id == snd_soc_dapm_dai_out)
> +			return (struct snd_soc_dai *)p->source->priv;
> +
> +		be = audin_formatter_i2s_get_be(p->source);

[Severity: Medium]
Can this recursive call lead to a kernel stack overflow if there is a cycle 
in the DAPM graph?

The traversal using snd_soc_dapm_widget_for_each_source_path() does not 
appear to track visited nodes or limit the recursion depth. A malformed 
device tree audio-routing or an unexpected userspace mixer configuration that 
creates a cycle could trigger infinite recursion when evaluating the graph.

> +		if (be)
> +			return be;
> +	}
> +
> +	return NULL;
> +}

[ ... ]

> diff --git a/sound/soc/meson/audin.c b/sound/soc/meson/audin.c
> new file mode 100644
> index 0000000000000..ec024267ac29c
> --- /dev/null
> +++ b/sound/soc/meson/audin.c

[ ... ]

> +static struct snd_soc_dapm_widget audin_dapm_widgets[] = {
> +	[AUDIN_WIDGET_I2S_FORMATTER] =
> +		SND_SOC_DAPM_PGA_E("I2S Formatter", SND_SOC_NOPM, 0, 0, NULL, 0,
> +				   gx_formatter_event,
> +				   (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD)),

[ ... ]

> +static int audin_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	void __iomem *mmio;
> +	struct regmap *regmap;
> +	struct clk *clk;
> +	int ret;

[ ... ]

> +	ret = gx_formatter_create(dev, &audin_dapm_widgets[AUDIN_WIDGET_I2S_FORMATTER],
> +				  &audin_formatter_i2s_drv, regmap);

[Severity: Medium]
Is it safe to mutate the static global audin_dapm_widgets array during 
device probe?

Looking at gx_formatter_create(), it allocates memory with devm_kzalloc() 
and assigns it to the widget's priv pointer. If the probe defers, fails 
later, or if the device is unbound, the devm-managed memory is freed but 
the static global template retains a dangling pointer.

This could lead to a use-after-free or memory corruption if multiple device 
instances are probed, as they would be sharing and overwriting the same 
static widget state.

> +	if (ret)
> +		return dev_err_probe(dev, ret, "Failed to allocate formatter\n");

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260917-audin-v2-0-38de4bc1de34@baylibre.com?part=3

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

* Re: [PATCH v2 4/6] ASoC: meson: aiu: add I2S Capture DAI
  2026-09-17 21:02 ` [PATCH v2 4/6] ASoC: meson: aiu: add I2S Capture DAI Valerio Setti
@ 2026-09-17 21:20   ` sashiko-bot
  0 siblings, 0 replies; 10+ messages in thread
From: sashiko-bot @ 2026-09-17 21:20 UTC (permalink / raw)
  To: Valerio Setti; +Cc: robh, linux-amlogic, neil.armstrong, conor+dt, devicetree

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] ASoC: meson: permanent clock divider leak in aiu_encoder_i2s_hw_free()
--

commit 7076c7f044003c89d276b88e15e27dd0533c4d92
Author: Valerio Setti <vsetti@baylibre.com>

ASoC: meson: aiu: add I2S Capture DAI

This commit adds a capture stream to the CPU_I2S_ENCODER, enabling it to
handle both playback and capture streams concurrently. It also formats
and buffers the captured data using existing components.

> diff --git a/sound/soc/meson/aiu.c b/sound/soc/meson/aiu.c
> index 535fbf7c16394..14d0e20a4bd66 100644
> --- a/sound/soc/meson/aiu.c
> +++ b/sound/soc/meson/aiu.c
> @@ -153,6 +153,13 @@ static struct snd_soc_dai_driver aiu_cpu_dai_drv[] = {
>  			.rates = SNDRV_PCM_RATE_8000_192000,
>  			.formats = AIU_FORMATS,
>  		},
> +		.capture = {
> +			.stream_name = "I2S Encoder Capture",
> +			.channels_min = 2,
> +			.channels_max = 2,
> +			.rates = SNDRV_PCM_RATE_8000_192000,
> +			.formats = SNDRV_PCM_FMTBIT_S16_LE,
> +		},

[Severity: High]
Does adding this capture stream introduce a permanent clock divider leak when
hw_free is explicitly called?

Now that both playback and capture streams can be active simultaneously,
snd_soc_dai_active(dai) can evaluate to 2.

In sound/soc/meson/aiu-encoder-i2s.c:aiu_encoder_i2s_hw_free():

    if (snd_soc_dai_active(dai) <= 1)
        aiu_encoder_i2s_divider_enable(component, 0);

If a user opens both streams so snd_soc_dai_active(dai) evaluates to 2, and
explicitly calls SNDRV_PCM_IOCTL_HW_FREE before closing them,
snd_soc_dai_active(dai) remains 2. This skips disabling the clock divider.

When the streams are subsequently closed, the ALSA core skips invoking hw_free
again because the state was already changed to SNDRV_PCM_STATE_OPEN. This
would leave the clock divider permanently enabled, potentially draining battery
or blocking system suspend.

>  		.ops = &aiu_encoder_i2s_dai_ops,
>  		.symmetric_rate = 1,
>  	},

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260917-audin-v2-0-38de4bc1de34@baylibre.com?part=4

_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

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

end of thread, other threads:[~2026-09-17 21:20 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-17 21:02 [PATCH v2 0/6] ASoC: meson: gx: add base support for I2S audio input Valerio Setti
2026-09-17 21:02 ` [PATCH v2 1/6] ASoC: dt-bindings: amlogic,gx-audin: add schema Valerio Setti
2026-09-17 21:07   ` sashiko-bot
2026-09-17 21:02 ` [PATCH v2 2/6] ASoC: meson: build gx-formatter as a separate module Valerio Setti
2026-09-17 21:02 ` [PATCH v2 3/6] ASoC: meson: add AUDIN driver Valerio Setti
2026-09-17 21:17   ` sashiko-bot
2026-09-17 21:02 ` [PATCH v2 4/6] ASoC: meson: aiu: add I2S Capture DAI Valerio Setti
2026-09-17 21:20   ` sashiko-bot
2026-09-17 21:02 ` [PATCH v2 5/6] ASoC: meson: gx-card: add support for audin FIFO Valerio Setti
2026-09-17 21:02 ` [PATCH v2 6/6] arm64: dts: amlogic: gx: add AUDIN node Valerio Setti

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

all inboxes | Powered by JetHome®