All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] ASoC: stm32: Add I2S driver
@ 2017-04-06 15:40 ` olivier moysan
  0 siblings, 0 replies; 32+ messages in thread
From: olivier moysan @ 2017-04-06 15:40 UTC (permalink / raw)
  To: lgirdwood, broonie, perex, tiwai, mcoquelin.stm32,
	alexandre.torgue, alsa-devel, robh, mark.rutland, devicetree,
	linux-arm-kernel, olivier.moysan
  Cc: arnaud.pouliquen, benjamin.gaignard

This patch-set handles the SPI/I2S IP on STM32 platforms.

It applies to STM32 platforms implementing version 2 of SPI/I2S IP like STM32H7 series.

The SPI/I2S block is a serial peripheral interface (SPI), 
which can also be configured to work on I2S/PCM mode.
ASoC STM32 I2S driver only supports this I2S/PCM mode.

The I2S/PCM interface can either support full duplex or half-duplex communication
(transmitter or receiver only).

I2S driver exhibits 3 DAIs corresponding to a playback, capture or full duplex PCM device.
When the interface is configured as full duplex, the related PCM device must be opened twice. 
The PCM streams are started when both playback and capture devices are opened.
The relevant DAI can be selected through Device Tree using sound-dai-cells parameter.

The I2S/PCM interface supports four audio standards:
I2S Philips standard, MSB justified standard, LSB justified standard, PCM standard
PCM standard is declined in two versions, PCM short and long,
according frame synchronization duration.

These standards can be mapped to ASoC standards as follows:
	- I2S: i2s
	- MSB justified standard: left_j
	- LSB justified standard: right_j
	- PCM short: dsp_a
PCM long format is not exposed as it does not match supported ASoC standards.

olivier moysan (2):
  dt-bindings: Document STM32 I2S bindings
  ASoC: stm32: Add I2S driver

 .../devicetree/bindings/sound/st,stm32h7-i2s.txt   |   71 ++
 sound/soc/Kconfig                                  |    1 +
 sound/soc/Makefile                                 |    1 +
 sound/soc/stm/Kconfig                              |    8 +
 sound/soc/stm/Makefile                             |    2 +
 sound/soc/stm/stm32_i2s.c                          | 1069 ++++++++++++++++++++
 6 files changed, 1152 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/st,stm32h7-i2s.txt
 create mode 100644 sound/soc/stm/Kconfig
 create mode 100644 sound/soc/stm/Makefile
 create mode 100644 sound/soc/stm/stm32_i2s.c

-- 
1.9.1

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

* [PATCH 0/2] ASoC: stm32: Add I2S driver
@ 2017-04-06 15:40 ` olivier moysan
  0 siblings, 0 replies; 32+ messages in thread
From: olivier moysan @ 2017-04-06 15:40 UTC (permalink / raw)
  To: linux-arm-kernel

This patch-set handles the SPI/I2S IP on STM32 platforms.

It applies to STM32 platforms implementing version 2 of SPI/I2S IP like STM32H7 series.

The SPI/I2S block is a serial peripheral interface (SPI), 
which can also be configured to work on I2S/PCM mode.
ASoC STM32 I2S driver only supports this I2S/PCM mode.

The I2S/PCM interface can either support full duplex or half-duplex communication
(transmitter or receiver only).

I2S driver exhibits 3 DAIs corresponding to a playback, capture or full duplex PCM device.
When the interface is configured as full duplex, the related PCM device must be opened twice. 
The PCM streams are started when both playback and capture devices are opened.
The relevant DAI can be selected through Device Tree using sound-dai-cells parameter.

The I2S/PCM interface supports four audio standards:
I2S Philips standard, MSB justified standard, LSB justified standard, PCM standard
PCM standard is declined in two versions, PCM short and long,
according frame synchronization duration.

These standards can be mapped to ASoC standards as follows:
	- I2S: i2s
	- MSB justified standard: left_j
	- LSB justified standard: right_j
	- PCM short: dsp_a
PCM long format is not exposed as it does not match supported ASoC standards.

olivier moysan (2):
  dt-bindings: Document STM32 I2S bindings
  ASoC: stm32: Add I2S driver

 .../devicetree/bindings/sound/st,stm32h7-i2s.txt   |   71 ++
 sound/soc/Kconfig                                  |    1 +
 sound/soc/Makefile                                 |    1 +
 sound/soc/stm/Kconfig                              |    8 +
 sound/soc/stm/Makefile                             |    2 +
 sound/soc/stm/stm32_i2s.c                          | 1069 ++++++++++++++++++++
 6 files changed, 1152 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/st,stm32h7-i2s.txt
 create mode 100644 sound/soc/stm/Kconfig
 create mode 100644 sound/soc/stm/Makefile
 create mode 100644 sound/soc/stm/stm32_i2s.c

-- 
1.9.1

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

* [PATCH 1/2] dt-bindings: Document STM32 I2S bindings
  2017-04-06 15:40 ` olivier moysan
@ 2017-04-06 15:40   ` olivier moysan
  -1 siblings, 0 replies; 32+ messages in thread
From: olivier moysan @ 2017-04-06 15:40 UTC (permalink / raw)
  To: lgirdwood, broonie, perex, tiwai, mcoquelin.stm32,
	alexandre.torgue, alsa-devel, robh, mark.rutland, devicetree,
	linux-arm-kernel, olivier.moysan
  Cc: arnaud.pouliquen, benjamin.gaignard

Add documentation of device tree bindings for STM32 SPI/I2S.

Signed-off-by: olivier moysan <olivier.moysan@st.com>
---
 .../devicetree/bindings/sound/st,stm32h7-i2s.txt   | 71 ++++++++++++++++++++++
 1 file changed, 71 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/st,stm32h7-i2s.txt

diff --git a/Documentation/devicetree/bindings/sound/st,stm32h7-i2s.txt b/Documentation/devicetree/bindings/sound/st,stm32h7-i2s.txt
new file mode 100644
index 0000000..b99467a
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/st,stm32h7-i2s.txt
@@ -0,0 +1,71 @@
+STMicroelectronics STM32 SPI/I2S Controller
+
+The SPI/I2S block supports I2S/PCM protocols when configured on I2S mode.
+Only some SPI instances support I2S.
+
+Required properties:
+  - compatible: Must be "st,stm32h7-i2s"
+  - #sound-dai-cells: Must be 1. (one parameter)
+    This parameter allows to specify CPU DAI index in soundcard CPU dai link.
+	index 0: playback DAI
+	index 1: capture DAI
+	index 2: full duplex DAI
+  - reg: Offset and length of the device's register set.
+  - interrupts: Must contain the interrupt line id.
+  - clocks: Must contain phandle and clock specifier pairs for each entry
+	in clock-names.
+  - clock-names: Must contain "i2sclk", "pclk", "x8k" and "x11k".
+	"i2sclk": clock which feeds the internal clock generator
+	"pclk": clock which feeds the peripheral bus interface
+	"x8k": I2S parent clock for sampling rates multiple of 8kHz.
+	"x11k": I2S parent clock for sampling rates multiple of 11.025kHz.
+  - dmas: DMA specifiers for tx and rx dma.
+    See Documentation/devicetree/bindings/dma/stm32-dma.txt.
+  - dma-names: Identifier for each DMA request line. Must be "tx" and "rx".
+  - pinctrl-names: should contain only value "default"
+  - pinctrl-0: see Documentation/devicetree/bindings/pinctrl/pinctrl-stm32.txt
+
+Optional properties:
+  - resets: Reference to a reset controller asserting the reset controller
+
+Example:
+sound_card {
+	compatible = "audio-graph-card";
+	dais = <&i2s2_port 0>;
+};
+
+i2s2: audio-controller@40003800 {
+	compatible = "st,stm32h7-i2s";
+	#sound-dai-cells = <1>;
+	reg = <0x40003800 0x400>;
+	interrupts = <36>;
+	clocks = <&rcc PCLK1>, <&rcc SPI2_CK>, <&rcc PLL1_Q>, <&rcc PLL2_P>;
+	clock-names = "pclk", "i2sclk",  "x8k", "x11k";
+	dmas = <&dmamux2 2 39 0x400 0x1>,
+		       <&dmamux2 3 40 0x400 0x1>;
+	dma-names = "rx", "tx";
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_i2s2>;
+
+	ports {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		i2s2_port: port@0 {
+			reg = <0>;
+			cpu_endpoint: endpoint {
+				remote-endpoint = <&codec_endpoint>;
+				audio-graph-card,format = "i2s";
+				audio-graph-card,bitclock-master = <&codec_endpoint>;
+				audio-graph-card,frame-master = <&codec_endpoint>;
+			};
+		};
+};
+
+audio-codec {
+		codec_port: port {
+			codec_endpoint: endpoint {
+				remote-endpoint = <&cpu_endpoint>;
+		};
+	};
+};
-- 
1.9.1

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

* [PATCH 1/2] dt-bindings: Document STM32 I2S bindings
@ 2017-04-06 15:40   ` olivier moysan
  0 siblings, 0 replies; 32+ messages in thread
From: olivier moysan @ 2017-04-06 15:40 UTC (permalink / raw)
  To: linux-arm-kernel

Add documentation of device tree bindings for STM32 SPI/I2S.

Signed-off-by: olivier moysan <olivier.moysan@st.com>
---
 .../devicetree/bindings/sound/st,stm32h7-i2s.txt   | 71 ++++++++++++++++++++++
 1 file changed, 71 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/st,stm32h7-i2s.txt

diff --git a/Documentation/devicetree/bindings/sound/st,stm32h7-i2s.txt b/Documentation/devicetree/bindings/sound/st,stm32h7-i2s.txt
new file mode 100644
index 0000000..b99467a
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/st,stm32h7-i2s.txt
@@ -0,0 +1,71 @@
+STMicroelectronics STM32 SPI/I2S Controller
+
+The SPI/I2S block supports I2S/PCM protocols when configured on I2S mode.
+Only some SPI instances support I2S.
+
+Required properties:
+  - compatible: Must be "st,stm32h7-i2s"
+  - #sound-dai-cells: Must be 1. (one parameter)
+    This parameter allows to specify CPU DAI index in soundcard CPU dai link.
+	index 0: playback DAI
+	index 1: capture DAI
+	index 2: full duplex DAI
+  - reg: Offset and length of the device's register set.
+  - interrupts: Must contain the interrupt line id.
+  - clocks: Must contain phandle and clock specifier pairs for each entry
+	in clock-names.
+  - clock-names: Must contain "i2sclk", "pclk", "x8k" and "x11k".
+	"i2sclk": clock which feeds the internal clock generator
+	"pclk": clock which feeds the peripheral bus interface
+	"x8k": I2S parent clock for sampling rates multiple of 8kHz.
+	"x11k": I2S parent clock for sampling rates multiple of 11.025kHz.
+  - dmas: DMA specifiers for tx and rx dma.
+    See Documentation/devicetree/bindings/dma/stm32-dma.txt.
+  - dma-names: Identifier for each DMA request line. Must be "tx" and "rx".
+  - pinctrl-names: should contain only value "default"
+  - pinctrl-0: see Documentation/devicetree/bindings/pinctrl/pinctrl-stm32.txt
+
+Optional properties:
+  - resets: Reference to a reset controller asserting the reset controller
+
+Example:
+sound_card {
+	compatible = "audio-graph-card";
+	dais = <&i2s2_port 0>;
+};
+
+i2s2: audio-controller at 40003800 {
+	compatible = "st,stm32h7-i2s";
+	#sound-dai-cells = <1>;
+	reg = <0x40003800 0x400>;
+	interrupts = <36>;
+	clocks = <&rcc PCLK1>, <&rcc SPI2_CK>, <&rcc PLL1_Q>, <&rcc PLL2_P>;
+	clock-names = "pclk", "i2sclk",  "x8k", "x11k";
+	dmas = <&dmamux2 2 39 0x400 0x1>,
+		       <&dmamux2 3 40 0x400 0x1>;
+	dma-names = "rx", "tx";
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_i2s2>;
+
+	ports {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		i2s2_port: port at 0 {
+			reg = <0>;
+			cpu_endpoint: endpoint {
+				remote-endpoint = <&codec_endpoint>;
+				audio-graph-card,format = "i2s";
+				audio-graph-card,bitclock-master = <&codec_endpoint>;
+				audio-graph-card,frame-master = <&codec_endpoint>;
+			};
+		};
+};
+
+audio-codec {
+		codec_port: port {
+			codec_endpoint: endpoint {
+				remote-endpoint = <&cpu_endpoint>;
+		};
+	};
+};
-- 
1.9.1

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

* [PATCH 2/2] ASoC: stm32: Add I2S driver
  2017-04-06 15:40 ` olivier moysan
@ 2017-04-06 15:40     ` olivier moysan
  -1 siblings, 0 replies; 32+ messages in thread
From: olivier moysan @ 2017-04-06 15:40 UTC (permalink / raw)
  To: lgirdwood-Re5JQEeQqe8AvxtiuMwx3w, broonie-DgEjT+Ai2ygdnm+yROfE0A,
	perex-/Fr2/VpizcU, tiwai-IBi9RG/b67k,
	mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w,
	alexandre.torgue-qxv4g6HH51o, alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw,
	robh-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	olivier.moysan-qxv4g6HH51o
  Cc: arnaud.pouliquen-qxv4g6HH51o, benjamin.gaignard-qxv4g6HH51o

Add I2S ASoC driver for STM32.

Signed-off-by: olivier moysan <olivier.moysan-qxv4g6HH51o@public.gmane.org>
---
 sound/soc/Kconfig         |    1 +
 sound/soc/Makefile        |    1 +
 sound/soc/stm/Kconfig     |    8 +
 sound/soc/stm/Makefile    |    2 +
 sound/soc/stm/stm32_i2s.c | 1069 +++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 1081 insertions(+)
 create mode 100644 sound/soc/stm/Kconfig
 create mode 100644 sound/soc/stm/Makefile
 create mode 100644 sound/soc/stm/stm32_i2s.c

diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 182d92e..3836ebe 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -63,6 +63,7 @@ source "sound/soc/sh/Kconfig"
 source "sound/soc/sirf/Kconfig"
 source "sound/soc/spear/Kconfig"
 source "sound/soc/sti/Kconfig"
+source "sound/soc/stm/Kconfig"
 source "sound/soc/sunxi/Kconfig"
 source "sound/soc/tegra/Kconfig"
 source "sound/soc/txx9/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 9a30f21..5440cf7 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_SND_SOC)	+= sh/
 obj-$(CONFIG_SND_SOC)	+= sirf/
 obj-$(CONFIG_SND_SOC)	+= spear/
 obj-$(CONFIG_SND_SOC)	+= sti/
+obj-$(CONFIG_SND_SOC)	+= stm/
 obj-$(CONFIG_SND_SOC)	+= sunxi/
 obj-$(CONFIG_SND_SOC)	+= tegra/
 obj-$(CONFIG_SND_SOC)	+= txx9/
diff --git a/sound/soc/stm/Kconfig b/sound/soc/stm/Kconfig
new file mode 100644
index 0000000..972970f
--- /dev/null
+++ b/sound/soc/stm/Kconfig
@@ -0,0 +1,8 @@
+menuconfig SND_SOC_STM32
+	tristate "STMicroelectronics STM32 SOC audio support"
+	depends on ARCH_STM32 || COMPILE_TEST
+	depends on SND_SOC
+	select SND_SOC_GENERIC_DMAENGINE_PCM
+	select REGMAP_MMIO
+	help
+	  Say Y if you want to enable ASoC-support for STM32
diff --git a/sound/soc/stm/Makefile b/sound/soc/stm/Makefile
new file mode 100644
index 0000000..090836b
--- /dev/null
+++ b/sound/soc/stm/Makefile
@@ -0,0 +1,2 @@
+snd-soc-stm32-i2s-objs := stm32_i2s.o
+obj-$(CONFIG_SND_SOC_STM32) += snd-soc-stm32-i2s.o
diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c
new file mode 100644
index 0000000..2ca9719
--- /dev/null
+++ b/sound/soc/stm/stm32_i2s.c
@@ -0,0 +1,1069 @@
+/*
+ *  STM32 ALSA SoC Digital Audio Interface (I2S) driver.
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author(s): Olivier Moysan <olivier.moysan-qxv4g6HH51o@public.gmane.org> for STMicroelectronics.
+ *
+ * License terms: GPL V2.0.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/spinlock.h>
+
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+
+#define STM32_I2S_CR1_REG	0x0
+#define STM32_I2S_CFG1_REG	0x08
+#define STM32_I2S_CFG2_REG	0x0C
+#define STM32_I2S_IER_REG	0x10
+#define STM32_I2S_SR_REG	0x14
+#define STM32_I2S_IFCR_REG	0x18
+#define STM32_I2S_TXDR_REG	0X20
+#define STM32_I2S_RXDR_REG	0x30
+#define STM32_I2S_CGFR_REG	0X50
+
+/* Bit definition for SPI2S_CR1 register */
+#define I2S_CR1_SPE		BIT(0)
+#define I2S_CR1_CSTART		BIT(9)
+#define I2S_CR1_CSUSP		BIT(10)
+#define I2S_CR1_HDDIR		BIT(11)
+#define I2S_CR1_SSI		BIT(12)
+#define I2S_CR1_CRC33_17	BIT(13)
+#define I2S_CR1_RCRCI		BIT(14)
+#define I2S_CR1_TCRCI		BIT(15)
+
+/* Bit definition for SPI_CFG2 register */
+#define I2S_CFG2_IOSWP_SHIFT	15
+#define I2S_CFG2_IOSWP		BIT(I2S_CFG2_IOSWP_SHIFT)
+#define I2S_CFG2_LSBFRST	BIT(23)
+#define I2S_CFG2_AFCNTR		BIT(31)
+
+/* Bit definition for SPI_CFG1 register */
+#define I2S_CFG1_FTHVL_SHIFT	5
+#define I2S_CFG1_FTHVL_MASK	GENMASK(8, I2S_CFG1_FTHVL_SHIFT)
+#define I2S_CFG1_FTHVL_SET(x)	((x) << I2S_CFG1_FTHVL_SHIFT)
+
+#define I2S_CFG1_TXDMAEN	BIT(15)
+#define I2S_CFG1_RXDMAEN	BIT(14)
+
+/* Bit definition for SPI2S_IER register */
+#define I2S_IER_RXPIE		BIT(0)
+#define I2S_IER_TXPIE		BIT(1)
+#define I2S_IER_DPXPIE		BIT(2)
+#define I2S_IER_EOTIE		BIT(3)
+#define I2S_IER_TXTFIE		BIT(4)
+#define I2S_IER_UDRIE		BIT(5)
+#define I2S_IER_OVRIE		BIT(6)
+#define I2S_IER_CRCEIE		BIT(7)
+#define I2S_IER_TIFREIE		BIT(8)
+#define I2S_IER_MODFIE		BIT(9)
+#define I2S_IER_TSERFIE		BIT(10)
+
+/* Bit definition for SPI2S_SR register */
+#define I2S_SR_RXP		BIT(0)
+#define I2S_SR_TXP		BIT(1)
+#define I2S_SR_DPXP		BIT(2)
+#define I2S_SR_EOT		BIT(3)
+#define I2S_SR_TXTF		BIT(4)
+#define I2S_SR_UDR		BIT(5)
+#define I2S_SR_OVR		BIT(6)
+#define I2S_SR_CRCERR		BIT(7)
+#define I2S_SR_TIFRE		BIT(8)
+#define I2S_SR_MODF		BIT(9)
+#define I2S_SR_TSERF		BIT(10)
+#define I2S_SR_SUSP		BIT(11)
+#define I2S_SR_TXC		BIT(12)
+#define I2S_SR_RXPLVL		GENMASK(14, 13)
+#define I2S_SR_RXWNE		BIT(15)
+
+#define I2S_SR_MASK		GENMASK(15, 0)
+
+/* Bit definition for SPI_IFCR register */
+#define I2S_IFCR_EOTC		BIT(3)
+#define I2S_IFCR_TXTFC		BIT(4)
+#define I2S_IFCR_UDRC		BIT(5)
+#define I2S_IFCR_OVRC		BIT(6)
+#define I2S_IFCR_CRCEC		BIT(7)
+#define I2S_IFCR_TIFREC		BIT(8)
+#define I2S_IFCR_MODFC		BIT(9)
+#define I2S_IFCR_TSERFC		BIT(10)
+#define I2S_IFCR_SUSPC		BIT(11)
+
+#define I2S_IFCR_MASK		GENMASK(11, 3)
+
+/* Bit definition for SPI_I2SCGFR register */
+#define I2S_CGFR_I2SMOD		BIT(0)
+
+#define I2S_CGFR_I2SCFG_SHIFT	1
+#define I2S_CGFR_I2SCFG_MASK	GENMASK(3, I2S_CGFR_I2SCFG_SHIFT)
+#define I2S_CGFR_I2SCFG_SET(x)	((x) << I2S_CGFR_I2SCFG_SHIFT)
+
+#define I2S_CGFR_I2SSTD_SHIFT	4
+#define I2S_CGFR_I2SSTD_MASK	GENMASK(5, I2S_CGFR_I2SSTD_SHIFT)
+#define I2S_CGFR_I2SSTD_SET(x)	((x) << I2S_CGFR_I2SSTD_SHIFT)
+
+#define I2S_CGFR_PCMSYNC	BIT(7)
+
+#define I2S_CGFR_DATLEN_SHIFT	8
+#define I2S_CGFR_DATLEN_MASK	GENMASK(9, I2S_CGFR_DATLEN_SHIFT)
+#define I2S_CGFR_DATLEN_SET(x)	((x) << I2S_CGFR_DATLEN_SHIFT)
+
+#define I2S_CGFR_CHLEN_SHIFT	10
+#define I2S_CGFR_CHLEN		BIT(I2S_CGFR_CHLEN_SHIFT)
+#define I2S_CGFR_CKPOL		BIT(11)
+#define I2S_CGFR_FIXCH		BIT(12)
+#define I2S_CGFR_WSINV		BIT(13)
+#define I2S_CGFR_DATFMT		BIT(14)
+
+#define I2S_CGFR_I2SDIV_SHIFT	16
+#define I2S_CGFR_I2SDIV_BIT_H	23
+#define I2S_CGFR_I2SDIV_MASK	GENMASK(I2S_CGFR_I2SDIV_BIT_H,\
+				I2S_CGFR_I2SDIV_SHIFT)
+#define I2S_CGFR_I2SDIV_SET(x)	((x) << I2S_CGFR_I2SDIV_SHIFT)
+#define	I2S_CGFR_I2SDIV_MAX	((1 << (I2S_CGFR_I2SDIV_BIT_H -\
+				I2S_CGFR_I2SDIV_SHIFT)) - 1)
+
+#define I2S_CGFR_ODD_SHIFT	24
+#define I2S_CGFR_ODD		BIT(I2S_CGFR_ODD_SHIFT)
+#define I2S_CGFR_MCKOE		BIT(25)
+
+enum i2s_master_mode {
+	I2S_MS_NOT_SET,
+	I2S_MS_MASTER,
+	I2S_MS_SLAVE,
+};
+
+enum i2s_mode {
+	I2S_I2SMOD_TX_SLAVE,
+	I2S_I2SMOD_RX_SLAVE,
+	I2S_I2SMOD_TX_MASTER,
+	I2S_I2SMOD_RX_MASTER,
+	I2S_I2SMOD_FD_SLAVE,
+	I2S_I2SMOD_FD_MASTER,
+};
+
+enum i2s_fifo_th {
+	I2S_FIFO_TH_NONE,
+	I2S_FIFO_TH_ONE_QUARTER,
+	I2S_FIFO_TH_HALF,
+	I2S_FIFO_TH_THREE_QUARTER,
+	I2S_FIFO_TH_FULL,
+};
+
+enum i2s_std {
+	I2S_STD_I2S,
+	I2S_STD_LEFT_J,
+	I2S_STD_RIGHT_J,
+	I2S_STD_DSP,
+};
+
+enum i2s_dir {
+	I2S_DIR_TX,
+	I2S_DIR_RX,
+	I2S_DIR_FD,
+};
+
+enum i2s_datlen {
+	I2S_I2SMOD_DATLEN_16,
+	I2S_I2SMOD_DATLEN_24,
+	I2S_I2SMOD_DATLEN_32,
+};
+
+#define STM32_I2S_DAI_NAME_SIZE		20
+#define STM32_I2S_DAIS_NB		3
+#define STM32_I2S_IP_NAME_LENGTH	5
+#define STM32_I2S_FIFO_SIZE		16
+
+#define STM32_I2S_IS_MASTER(x)		((x)->ms_flg == I2S_MS_MASTER)
+#define STM32_I2S_IS_SLAVE(x)		((x)->ms_flg == I2S_MS_SLAVE)
+
+#define STM32_I2S_IS_PLAYBACK(x)	((x)->dir == I2S_DIR_TX)
+#define STM32_I2S_IS_CAPTURE(x)		((x)->dir == I2S_DIR_RX)
+#define STM32_I2S_IS_FULL_DUPLEX(x)	((x)->dir == I2S_DIR_FD)
+
+/**
+ * @regmap_conf: I2S register map configuration pointer
+ * @egmap: I2S register map pointer
+ * @pdev: device data pointer
+ * @dai_drv: DAI driver pointer
+ * @dma_data_tx: dma configuration data for tx channel
+ * @dma_data_rx: dma configuration data for tx channel
+ * @substream: PCM substream data pointer
+ * @i2sclk: kernel clock feeding the I2S clock generator
+ * @pclk: peripheral clock driving bus interface
+ * @x8kclk: I2S parent clock for sampling frequencies multiple of 8kHz
+ * @x11kclk: I2S parent clock for sampling frequencies multiple of 11kHz
+ * @base:  mmio register base virtual address
+ * @phys_addr: I2S registers physical base address
+ * @lock_fd: lock to manage race conditions in full duplex mode
+ * @ip_name: I2S name
+ * @dais_name: playback, capture and fd DAI names
+ * @fifo_th: fifo threshold setting
+ * @mclk_rate: master clock frequency (Hz)
+ * @fmt: DAI protocol
+ * @refcount: keep count of opened streams on I2S
+ * @startcount: keep count of started streams on I2S
+ * @dir: I2S direction. tx, rx or full duplex.
+ * @ms_flg: master mode flag.
+ * @format: pcm stream width (16/32 bits), used for consistency check in fd mode
+ * @rate: pcm stream rate, used for consistency check in fd mode
+ */
+struct stm32_i2s_data {
+	const struct regmap_config *regmap_conf;
+	struct regmap *regmap;
+	struct platform_device *pdev;
+	struct snd_soc_dai_driver *dai_drv;
+	struct snd_dmaengine_dai_dma_data dma_data_tx;
+	struct snd_dmaengine_dai_dma_data dma_data_rx;
+	struct snd_pcm_substream *substream;
+	struct clk *i2sclk;
+	struct clk *pclk;
+	struct clk *x8kclk;
+	struct clk *x11kclk;
+	void __iomem *base;
+	dma_addr_t phys_addr;
+	spinlock_t lock_fd; /* Manage race conditions for full duplex */
+	const char *ip_name;
+	char dais_name[STM32_I2S_DAIS_NB][STM32_I2S_DAI_NAME_SIZE];
+	unsigned int fifo_th;
+	unsigned int mclk_rate;
+	unsigned int fmt;
+	int refcount;
+	int startcount;
+	int dir;
+	int ms_flg;
+	int format;
+	unsigned int rate;
+};
+
+static irqreturn_t stm32_i2s_isr(int irq, void *devid)
+{
+	struct stm32_i2s_data *i2s = (struct stm32_i2s_data *)devid;
+	struct platform_device *pdev = i2s->pdev;
+	u32 sr, ier;
+	unsigned long flags;
+	int err = 0;
+
+	regmap_read(i2s->regmap, STM32_I2S_SR_REG, &sr);
+	regmap_read(i2s->regmap, STM32_I2S_IER_REG, &ier);
+
+	flags = sr & ier;
+	if (!flags) {
+		dev_dbg(&pdev->dev, "Spurious IT sr=0x%08x, ier=0x%08x\n",
+			sr, ier);
+		return IRQ_NONE;
+	}
+
+	/* Clear ITs */
+	regmap_update_bits(i2s->regmap, STM32_I2S_IFCR_REG,
+			   I2S_IFCR_MASK, flags);
+
+	if (flags & I2S_SR_OVR) {
+		dev_dbg(&pdev->dev, "Overrun: received value discarded\n");
+		err = 1;
+	}
+
+	if (flags & I2S_SR_UDR) {
+		dev_dbg(&pdev->dev, "Underrun\n");
+		err = 1;
+	}
+
+	if (flags & I2S_SR_TIFRE)
+		dev_dbg(&pdev->dev, "Frame error\n");
+
+	if (err)
+		snd_pcm_stop_xrun(i2s->substream);
+
+	return IRQ_HANDLED;
+}
+
+static bool stm32_i2s_readable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case STM32_I2S_CR1_REG:
+	case STM32_I2S_CFG1_REG:
+	case STM32_I2S_CFG2_REG:
+	case STM32_I2S_IER_REG:
+	case STM32_I2S_SR_REG:
+	case STM32_I2S_IFCR_REG:
+	case STM32_I2S_TXDR_REG:
+	case STM32_I2S_RXDR_REG:
+	case STM32_I2S_CGFR_REG:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool stm32_i2s_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case STM32_I2S_TXDR_REG:
+	case STM32_I2S_RXDR_REG:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool stm32_i2s_writeable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case STM32_I2S_CR1_REG:
+	case STM32_I2S_CFG1_REG:
+	case STM32_I2S_CFG2_REG:
+	case STM32_I2S_IER_REG:
+	case STM32_I2S_IFCR_REG:
+	case STM32_I2S_TXDR_REG:
+	case STM32_I2S_CGFR_REG:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static int stm32_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+	struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai);
+	u32 cgfr;
+	u32 cgfr_mask =  I2S_CGFR_I2SSTD_MASK | I2S_CGFR_CKPOL |
+			 I2S_CGFR_WSINV | I2S_CGFR_I2SCFG_MASK;
+
+	dev_dbg(cpu_dai->dev, "fmt %x\n", fmt);
+
+	/*
+	 * winv = 0 : default behavior (high/low) for all standards
+	 * ckpol 0 for all standards.
+	 */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		cgfr = I2S_CGFR_I2SSTD_SET(I2S_STD_I2S);
+		break;
+	case SND_SOC_DAIFMT_MSB:
+		cgfr = I2S_CGFR_I2SSTD_SET(I2S_STD_LEFT_J);
+		break;
+	case SND_SOC_DAIFMT_LSB:
+		cgfr = I2S_CGFR_I2SSTD_SET(I2S_STD_RIGHT_J);
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		cgfr = I2S_CGFR_I2SSTD_SET(I2S_STD_DSP);
+		break;
+	/* DSP_B not mapped on I2S PCM long format. 1 bit offset does not fit */
+	default:
+		dev_err(cpu_dai->dev, "Unsupported protocol %#x\n",
+			fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+		return -EINVAL;
+	}
+
+	/* DAI clock strobing */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		cgfr |= I2S_CGFR_CKPOL;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		cgfr |= I2S_CGFR_WSINV;
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		cgfr |= I2S_CGFR_CKPOL;
+		cgfr |= I2S_CGFR_WSINV;
+		break;
+	default:
+		dev_err(cpu_dai->dev, "Unsupported strobing %#x\n",
+			fmt & SND_SOC_DAIFMT_INV_MASK);
+		return -EINVAL;
+	}
+
+	/* DAI clock master masks */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		if (STM32_I2S_IS_MASTER(i2s)) {
+			dev_err(cpu_dai->dev, "previous DAI set master mode\n");
+			return -EINVAL;
+		}
+		i2s->ms_flg = I2S_MS_SLAVE;
+
+		if (STM32_I2S_IS_FULL_DUPLEX(i2s))
+			cgfr |= I2S_CGFR_I2SCFG_SET(I2S_I2SMOD_FD_SLAVE);
+		else if (STM32_I2S_IS_PLAYBACK(i2s))
+			cgfr |= I2S_CGFR_I2SCFG_SET(I2S_I2SMOD_TX_SLAVE);
+		else
+			cgfr |= I2S_CGFR_I2SCFG_SET(I2S_I2SMOD_RX_SLAVE);
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		if (STM32_I2S_IS_SLAVE(i2s)) {
+			dev_err(cpu_dai->dev, "previous DAI set slave mode\n");
+			return -EINVAL;
+		}
+		i2s->ms_flg = I2S_MS_MASTER;
+
+		if (STM32_I2S_IS_FULL_DUPLEX(i2s))
+			cgfr |= I2S_CGFR_I2SCFG_SET(I2S_I2SMOD_FD_MASTER);
+		else if (STM32_I2S_IS_PLAYBACK(i2s))
+			cgfr |= I2S_CGFR_I2SCFG_SET(I2S_I2SMOD_TX_MASTER);
+		else
+			cgfr |= I2S_CGFR_I2SCFG_SET(I2S_I2SMOD_RX_MASTER);
+		break;
+	default:
+		dev_err(cpu_dai->dev, "Unsupported mode %#x\n",
+			fmt & SND_SOC_DAIFMT_MASTER_MASK);
+		return -EINVAL;
+	}
+
+	i2s->fmt = fmt;
+	return regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG,
+				  cgfr_mask, cgfr);
+}
+
+static int stm32_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
+				int clk_id, unsigned int freq, int dir)
+{
+	struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai);
+
+	dev_dbg(cpu_dai->dev, "I2S MCLK frequency is %uHz\n", freq);
+
+	if ((dir == SND_SOC_CLOCK_OUT) && STM32_I2S_IS_MASTER(i2s)) {
+		i2s->mclk_rate = freq;
+
+		/* Enable master clock if master mode and mclk-fs are set */
+		return regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG,
+					  I2S_CGFR_MCKOE, I2S_CGFR_MCKOE);
+	}
+
+	return 0;
+}
+
+static int stm32_i2s_configure_clock(struct snd_soc_dai *cpu_dai,
+				     struct snd_pcm_hw_params *params)
+{
+	struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai);
+	unsigned long i2s_clock_rate;
+	unsigned int tmp, div, real_div, nb_bits, frame_len;
+	unsigned int rate = params_rate(params);
+	int ret;
+	u32 cgfr, cgfr_mask;
+	bool odd;
+
+	if (i2s->refcount > 1) {
+		if (rate != i2s->rate) {
+			dev_err(cpu_dai->dev,
+				"rate not compatible with active stream");
+			return -EINVAL;
+		}
+		return 0;
+	}
+	i2s->rate = rate;
+
+	if (!(rate % 11025))
+		clk_set_parent(i2s->i2sclk, i2s->x11kclk);
+	else
+		clk_set_parent(i2s->i2sclk, i2s->x8kclk);
+	i2s_clock_rate = clk_get_rate(i2s->i2sclk);
+
+	/*
+	 * mckl = mclk_ratio x ws
+	 *   i2s mode : mclk_ratio = 256
+	 *   dsp mode : mclk_ratio = 128
+	 *
+	 * mclk on
+	 *   i2s mode : div = i2s_clk / (mclk_ratio * ws)
+	 *   dsp mode : div = i2s_clk / (mclk_ratio * ws)
+	 * mclk off
+	 *   i2s mode : div = i2s_clk / (nb_bits x ws)
+	 *   dsp mode : div = i2s_clk / (nb_bits x ws)
+	 */
+	if (i2s->mclk_rate) {
+		tmp = DIV_ROUND_CLOSEST(i2s_clock_rate, i2s->mclk_rate);
+	} else {
+		frame_len = 32;
+		if ((i2s->fmt & SND_SOC_DAIFMT_FORMAT_MASK) ==
+		    SND_SOC_DAIFMT_DSP_A)
+			frame_len = 16;
+
+		/* master clock not enabled */
+		ret = regmap_read(i2s->regmap, STM32_I2S_CGFR_REG, &cgfr);
+		if (ret < 0)
+			return ret;
+
+		nb_bits = frame_len * ((cgfr & I2S_CGFR_CHLEN) + 1);
+		tmp = DIV_ROUND_CLOSEST(i2s_clock_rate, (nb_bits * rate));
+	}
+
+	/* Check the parity of the divider */
+	odd = tmp & 0x1;
+
+	/* Compute the div prescaler */
+	div = tmp >> 1;
+
+	cgfr = I2S_CGFR_I2SDIV_SET(div) | (odd << I2S_CGFR_ODD_SHIFT);
+	cgfr_mask = I2S_CGFR_I2SDIV_MASK | I2S_CGFR_ODD;
+
+	real_div = ((2 * div) + odd);
+	dev_dbg(cpu_dai->dev, "I2S clk: %ld, SCLK: %d\n",
+		i2s_clock_rate, rate);
+	dev_dbg(cpu_dai->dev, "Divider: 2*%d(div)+%d(odd) = %d\n",
+		div, odd, real_div);
+
+	if (((div == 1) && odd) || (div > I2S_CGFR_I2SDIV_MAX)) {
+		dev_err(cpu_dai->dev, "Wrong divider setting\n");
+		return -EINVAL;
+	}
+
+	if (!div && !odd)
+		dev_warn(cpu_dai->dev, "real divider forced to 1\n");
+
+	ret = regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG,
+				 cgfr_mask, cgfr);
+	if (ret < 0)
+		return ret;
+
+	/* Set bitclock and frameclock to their inactive state */
+	return regmap_update_bits(i2s->regmap, STM32_I2S_CFG2_REG,
+				  I2S_CFG2_AFCNTR, I2S_CFG2_AFCNTR);
+}
+
+static int stm32_i2s_configure_channel(struct snd_soc_dai *cpu_dai,
+				       struct snd_pcm_hw_params *params)
+{
+	struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai);
+	int format = params_width(params);
+	u32 cfgr, cfgr_mask;
+
+	if (i2s->refcount > 1) {
+		if (format != i2s->format) {
+			dev_err(cpu_dai->dev,
+				"format not compatible with active stream");
+			return -EINVAL;
+		}
+		return 0;
+	}
+	i2s->format = format;
+
+	switch (format) {
+	case 16:
+		cfgr = I2S_CGFR_DATLEN_SET(I2S_I2SMOD_DATLEN_16);
+		cfgr_mask = I2S_CGFR_DATLEN_MASK;
+		break;
+	case 32:
+		cfgr = I2S_CGFR_DATLEN_SET(I2S_I2SMOD_DATLEN_32) |
+					   I2S_CGFR_CHLEN;
+		cfgr_mask = I2S_CGFR_DATLEN_MASK | I2S_CGFR_CHLEN;
+		break;
+	default:
+		dev_err(cpu_dai->dev, "Unexpected format %d", format);
+		return -EINVAL;
+	}
+
+	if (STM32_I2S_IS_SLAVE(i2s)) {
+		/* As data length is either 16 or 32 bits, fixch always set */
+		cfgr |= I2S_CGFR_FIXCH;
+		cfgr_mask |= I2S_CGFR_FIXCH;
+	}
+
+	return regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG,
+				  cfgr_mask, cfgr);
+}
+
+static int stm32_i2s_startup(struct snd_pcm_substream *substream,
+			     struct snd_soc_dai *cpu_dai)
+{
+	struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai);
+	int ret, ier;
+
+	i2s->substream = substream;
+
+	spin_lock(&i2s->lock_fd);
+	i2s->refcount++;
+	spin_unlock(&i2s->lock_fd);
+
+	ret = regmap_update_bits(i2s->regmap, STM32_I2S_IFCR_REG,
+				 I2S_IFCR_MASK, 0);
+	if (ret < 0)
+		return ret;
+
+	/* Enable ITs */
+	ier = I2S_IER_OVRIE | I2S_IER_UDRIE;
+	if (STM32_I2S_IS_SLAVE(i2s))
+		ier |= I2S_IER_TIFREIE;
+
+	return regmap_update_bits(i2s->regmap, STM32_I2S_IER_REG, ier, ier);
+}
+
+static int stm32_i2s_hw_params(struct snd_pcm_substream *substream,
+			       struct snd_pcm_hw_params *params,
+			       struct snd_soc_dai *cpu_dai)
+{
+	struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai);
+	int format = params_width(params);
+	unsigned int fthlv;
+	int ret;
+
+	if ((params_channels(params) == 1) &&
+	    ((i2s->fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_DSP_A)) {
+		dev_err(cpu_dai->dev, "Mono mode supported only by DSP_A\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * fthlv is fifo threshold expressed as sample number
+	 * fthlv = fifo size * threshold_ratio / (format / 8)
+	 * where threshold_ratio = fifo_th / 4
+	 */
+	fthlv = STM32_I2S_FIFO_SIZE * 8 * i2s->fifo_th / (format * 4);
+
+	regmap_update_bits(i2s->regmap, STM32_I2S_CFG1_REG, I2S_CFG1_FTHVL_MASK,
+			   I2S_CFG1_FTHVL_SET(fthlv - 1));
+
+	spin_lock(&i2s->lock_fd);
+
+	ret = stm32_i2s_configure_channel(cpu_dai, params);
+	if (ret < 0) {
+		spin_unlock(&i2s->lock_fd);
+		return ret;
+	}
+
+	if (STM32_I2S_IS_MASTER(i2s)) {
+		ret = stm32_i2s_configure_clock(cpu_dai, params);
+		if (ret < 0) {
+			spin_unlock(&i2s->lock_fd);
+			return ret;
+		}
+	}
+
+	spin_unlock(&i2s->lock_fd);
+
+	return 0;
+}
+
+static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+			     struct snd_soc_dai *cpu_dai)
+{
+	struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai);
+	int ret;
+	bool stream_is_playback = false;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		stream_is_playback = true;
+
+	/* This lock protects both start flag and SPE bit accesses */
+	spin_lock(&i2s->lock_fd);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		if (stream_is_playback) {
+			ret = regmap_update_bits(i2s->regmap,
+						 STM32_I2S_CFG1_REG,
+						 I2S_CFG1_TXDMAEN,
+						 I2S_CFG1_TXDMAEN);
+		} else {
+			ret = regmap_update_bits(i2s->regmap,
+						 STM32_I2S_CFG1_REG,
+						 I2S_CFG1_RXDMAEN,
+						 I2S_CFG1_RXDMAEN);
+		}
+		if (ret < 0)
+			goto err_trigger;
+
+		i2s->startcount++;
+		if (STM32_I2S_IS_FULL_DUPLEX(i2s) && (i2s->startcount < 2)) {
+			dev_dbg(cpu_dai->dev,
+				"Full duplex device waiting for %s stream",
+				stream_is_playback ? "capture" : "playback");
+			spin_unlock(&i2s->lock_fd);
+			return 0;
+		}
+
+		/* Enable i2s */
+		dev_dbg(cpu_dai->dev, "start I2S\n");
+
+		ret = regmap_update_bits(i2s->regmap, STM32_I2S_CR1_REG,
+					 I2S_CR1_SPE, I2S_CR1_SPE);
+		if (ret < 0) {
+			dev_err(cpu_dai->dev, "Error %d enabling I2S\n", ret);
+			goto err_trigger;
+		}
+
+		ret = regmap_update_bits(i2s->regmap, STM32_I2S_CR1_REG,
+					 I2S_CR1_CSTART, I2S_CR1_CSTART);
+		if (ret < 0) {
+			dev_err(cpu_dai->dev, "Error %d starting I2S\n", ret);
+			goto err_trigger;
+		}
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		dev_dbg(cpu_dai->dev, "stop I2S\n");
+
+		ret = regmap_update_bits(i2s->regmap, STM32_I2S_CR1_REG,
+					 I2S_CR1_SPE, 0);
+		if (ret < 0) {
+			dev_err(cpu_dai->dev, "Error %d disabling I2S\n", ret);
+			goto err_trigger;
+		}
+		i2s->startcount = 0;
+
+		if (stream_is_playback) {
+			ret = regmap_update_bits(i2s->regmap,
+						 STM32_I2S_CFG1_REG,
+						 I2S_CFG1_TXDMAEN, 0);
+		} else {
+			ret = regmap_update_bits(i2s->regmap,
+						 STM32_I2S_CFG1_REG,
+						 I2S_CFG1_RXDMAEN, 0);
+		}
+		if (ret < 0)
+			goto err_trigger;
+
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	spin_unlock(&i2s->lock_fd);
+	return 0;
+
+err_trigger:
+	spin_unlock(&i2s->lock_fd);
+	return ret;
+}
+
+static void stm32_i2s_shutdown(struct snd_pcm_substream *substream,
+			       struct snd_soc_dai *cpu_dai)
+{
+	struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai);
+
+	i2s->substream = NULL;
+
+	spin_lock(&i2s->lock_fd);
+	if (i2s->refcount > 0)
+		i2s->refcount--;
+	spin_unlock(&i2s->lock_fd);
+
+	regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG,
+			   I2S_CGFR_MCKOE, (unsigned int)~I2S_CGFR_MCKOE);
+}
+
+static int stm32_i2s_dai_probe(struct snd_soc_dai *cpu_dai)
+{
+	struct stm32_i2s_data *i2s = dev_get_drvdata(cpu_dai->dev);
+	struct snd_dmaengine_dai_dma_data *dma_data_tx = &i2s->dma_data_tx;
+	struct snd_dmaengine_dai_dma_data *dma_data_rx = &i2s->dma_data_rx;
+
+	/* Buswidth will be set by framework */
+	dma_data_tx->addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED;
+	dma_data_tx->addr = (dma_addr_t)(i2s->phys_addr) + STM32_I2S_TXDR_REG;
+	dma_data_tx->maxburst = 1;
+	dma_data_rx->addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED;
+	dma_data_rx->addr = (dma_addr_t)(i2s->phys_addr) + STM32_I2S_RXDR_REG;
+	dma_data_rx->maxburst = 1;
+
+	i2s->dir = cpu_dai->id;
+	switch (i2s->dir) {
+	case I2S_DIR_TX:
+		i2s->fifo_th = I2S_FIFO_TH_FULL;
+		snd_soc_dai_init_dma_data(cpu_dai, dma_data_tx, NULL);
+		break;
+	case I2S_DIR_RX:
+		i2s->fifo_th = I2S_FIFO_TH_HALF;
+		snd_soc_dai_init_dma_data(cpu_dai, NULL, dma_data_rx);
+		break;
+	case I2S_DIR_FD:
+		i2s->fifo_th = I2S_FIFO_TH_NONE;
+		snd_soc_dai_init_dma_data(cpu_dai, dma_data_tx, dma_data_rx);
+		break;
+	}
+
+	return 0;
+}
+
+static const struct regmap_config stm32_h7_i2s_regmap_conf = {
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+	.max_register = STM32_I2S_CGFR_REG,
+	.readable_reg = stm32_i2s_readable_reg,
+	.volatile_reg = stm32_i2s_volatile_reg,
+	.writeable_reg = stm32_i2s_writeable_reg,
+	.fast_io = true,
+};
+
+static const struct snd_soc_dai_ops stm32_i2s_pcm_dai_ops = {
+	.set_sysclk	= stm32_i2s_set_sysclk,
+	.set_fmt	= stm32_i2s_set_dai_fmt,
+	.startup	= stm32_i2s_startup,
+	.hw_params	= stm32_i2s_hw_params,
+	.trigger	= stm32_i2s_trigger,
+	.shutdown	= stm32_i2s_shutdown,
+};
+
+static const struct snd_pcm_hardware stm32_i2s_pcm_hw = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP,
+	.buffer_bytes_max = 8 * PAGE_SIZE,
+	.period_bytes_max = 2048,
+	.periods_min = 2,
+	.periods_max = 8,
+};
+
+static const struct snd_dmaengine_pcm_config stm32_i2s_pcm_config = {
+	.pcm_hardware	= &stm32_i2s_pcm_hw,
+	.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+	.prealloc_buffer_size = PAGE_SIZE * 8,
+};
+
+static const struct snd_soc_component_driver stm32_i2s_component = {
+	.name = "stm32-i2s",
+};
+
+static void stm32_i2s_dai_init(struct snd_soc_pcm_stream *stream,
+			       char *stream_name)
+{
+	stream->stream_name = stream_name;
+	stream->channels_min = 1;
+	stream->channels_max = 2;
+	stream->rates = SNDRV_PCM_RATE_8000_192000;
+	stream->formats = SNDRV_PCM_FMTBIT_S16_LE |
+				   SNDRV_PCM_FMTBIT_S32_LE;
+}
+
+static int stm32_i2s_dais_init(struct platform_device *pdev,
+			       struct stm32_i2s_data *i2s)
+{
+	struct snd_soc_dai_driver *dai_ptr;
+	int i;
+
+	dai_ptr = devm_kzalloc(&pdev->dev, STM32_I2S_DAIS_NB *
+			       sizeof(struct snd_soc_dai_driver), GFP_KERNEL);
+	if (!dai_ptr)
+		return -ENOMEM;
+
+	i2s->dai_drv = dai_ptr;
+	for (i = 0; i < STM32_I2S_DAIS_NB; i++) {
+		dai_ptr->probe = stm32_i2s_dai_probe;
+		dai_ptr->ops = &stm32_i2s_pcm_dai_ops;
+		if (i == 0) {
+			snprintf(i2s->dais_name[i], STM32_I2S_DAI_NAME_SIZE,
+				 "%s-playback", i2s->ip_name);
+			dai_ptr->id = I2S_DIR_TX;
+			stm32_i2s_dai_init(&dai_ptr->playback, "CPU-Playback");
+		}
+		if (i == 1) {
+			snprintf(i2s->dais_name[i], STM32_I2S_DAI_NAME_SIZE,
+				 "%s-capture", i2s->ip_name);
+			dai_ptr->id = I2S_DIR_RX;
+			stm32_i2s_dai_init(&dai_ptr->capture, "CPU-Capture");
+		}
+		if (i == 2) {
+			snprintf(i2s->dais_name[i], STM32_I2S_DAI_NAME_SIZE,
+				 "%s-full-duplex", i2s->ip_name);
+			dai_ptr->id = I2S_DIR_FD;
+			stm32_i2s_dai_init(&dai_ptr->playback,
+					   "CPU-FD-Playback");
+			stm32_i2s_dai_init(&dai_ptr->capture,
+					   "CPU-FD-Capture");
+		}
+		dai_ptr->name = i2s->dais_name[i];
+		dai_ptr++;
+	}
+
+	return 0;
+}
+
+static const struct of_device_id stm32_i2s_ids[] = {
+	{
+		.compatible = "st,stm32h7-i2s",
+		.data = &stm32_h7_i2s_regmap_conf
+	},
+	{},
+};
+
+static int stm32_i2s_parse_dt(struct platform_device *pdev,
+			      struct stm32_i2s_data *i2s)
+{
+	struct device_node *np = pdev->dev.of_node;
+	const struct of_device_id *of_id;
+	struct reset_control *rst;
+	struct resource *res;
+	int irq, ret;
+
+	if (!np)
+		return -ENODEV;
+
+	of_id = of_match_device(stm32_i2s_ids, &pdev->dev);
+	if (of_id)
+		i2s->regmap_conf = (const struct regmap_config *)of_id->data;
+	else
+		return -EINVAL;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	i2s->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(i2s->base))
+		return PTR_ERR(i2s->base);
+
+	i2s->phys_addr = res->start;
+
+	/* Get clocks */
+	i2s->pclk = devm_clk_get(&pdev->dev, "pclk");
+	if (IS_ERR(i2s->pclk)) {
+		dev_err(&pdev->dev, "Could not get pclk\n");
+		return PTR_ERR(i2s->pclk);
+	}
+
+	i2s->i2sclk = devm_clk_get(&pdev->dev, "i2sclk");
+	if (IS_ERR(i2s->i2sclk)) {
+		dev_err(&pdev->dev, "Could not get i2sclk\n");
+		return PTR_ERR(i2s->i2sclk);
+	}
+
+	i2s->x8kclk = devm_clk_get(&pdev->dev, "x8k");
+	if (IS_ERR(i2s->x8kclk)) {
+		dev_err(&pdev->dev, "missing x8k parent clock\n");
+		return PTR_ERR(i2s->x8kclk);
+	}
+
+	i2s->x11kclk = devm_clk_get(&pdev->dev, "x11k");
+	if (IS_ERR(i2s->x11kclk)) {
+		dev_err(&pdev->dev, "missing x11k parent clock\n");
+		return PTR_ERR(i2s->x11kclk);
+	}
+
+	/* Get irqs */
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "no irq for node %s\n", pdev->name);
+		return -ENOENT;
+	}
+
+	ret = devm_request_irq(&pdev->dev, irq, stm32_i2s_isr, IRQF_ONESHOT,
+			       dev_name(&pdev->dev), i2s);
+	if (ret) {
+		dev_err(&pdev->dev, "irq request returned %d\n", ret);
+		return ret;
+	}
+
+	/* Reset */
+	rst = devm_reset_control_get(&pdev->dev, NULL);
+	if (!IS_ERR(rst)) {
+		reset_control_assert(rst);
+		udelay(2);
+		reset_control_deassert(rst);
+	}
+
+	return 0;
+}
+
+static int stm32_i2s_probe(struct platform_device *pdev)
+{
+	struct stm32_i2s_data *i2s;
+	int ret;
+
+	i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
+	if (!i2s)
+		return -ENOMEM;
+
+	i2s->pdev = pdev;
+	i2s->ms_flg = I2S_MS_NOT_SET;
+	i2s->ip_name = strstr(dev_name(&pdev->dev), ".") + 1;
+	spin_lock_init(&i2s->lock_fd);
+	platform_set_drvdata(pdev, i2s);
+
+	ret = stm32_i2s_parse_dt(pdev, i2s);
+	if (ret)
+		return ret;
+
+	ret = stm32_i2s_dais_init(pdev, i2s);
+	if (ret)
+		return ret;
+
+	i2s->regmap = devm_regmap_init_mmio(&pdev->dev, i2s->base,
+					    i2s->regmap_conf);
+	if (IS_ERR(i2s->regmap)) {
+		dev_err(&pdev->dev, "regmap init failed\n");
+		return PTR_ERR(i2s->regmap);
+	}
+
+	ret = clk_prepare_enable(i2s->pclk);
+	if (ret) {
+		dev_err(&pdev->dev, "Enable pclk failed: %d\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(i2s->i2sclk);
+	if (ret) {
+		dev_err(&pdev->dev, "Enable i2sclk failed: %d\n", ret);
+		goto err_pclk_disable;
+	}
+
+	ret = devm_snd_soc_register_component(&pdev->dev,
+					      &stm32_i2s_component,
+					      i2s->dai_drv, STM32_I2S_DAIS_NB);
+	if (ret)
+		goto err_clocks_disable;
+
+	ret = devm_snd_dmaengine_pcm_register(&pdev->dev,
+					      &stm32_i2s_pcm_config, 0);
+	if (ret)
+		goto err_clocks_disable;
+
+	/* Set SPI/I2S in i2s mode */
+	ret = regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG,
+				 I2S_CGFR_I2SMOD, I2S_CGFR_I2SMOD);
+	if (ret)
+		goto err_clocks_disable;
+
+	return ret;
+
+err_clocks_disable:
+	clk_disable_unprepare(i2s->i2sclk);
+err_pclk_disable:
+	clk_disable_unprepare(i2s->pclk);
+
+	return ret;
+}
+
+static int stm32_i2s_remove(struct platform_device *pdev)
+{
+	struct stm32_i2s_data *i2s = platform_get_drvdata(pdev);
+
+	clk_disable_unprepare(i2s->i2sclk);
+	clk_disable_unprepare(i2s->pclk);
+
+	return 0;
+}
+
+MODULE_DEVICE_TABLE(of, stm32_i2s_ids);
+
+static struct platform_driver stm32_i2s_driver = {
+	.driver = {
+		.name = "st,stm32-i2s",
+		.of_match_table = stm32_i2s_ids,
+	},
+	.probe = stm32_i2s_probe,
+	.remove = stm32_i2s_remove,
+};
+
+module_platform_driver(stm32_i2s_driver);
+
+MODULE_DESCRIPTION("STM32 Soc i2s Interface");
+MODULE_AUTHOR("Olivier Moysan, <olivier.moysan-qxv4g6HH51o@public.gmane.org>");
+MODULE_ALIAS("platform:stm32-i2s");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 2/2] ASoC: stm32: Add I2S driver
@ 2017-04-06 15:40     ` olivier moysan
  0 siblings, 0 replies; 32+ messages in thread
From: olivier moysan @ 2017-04-06 15:40 UTC (permalink / raw)
  To: linux-arm-kernel

Add I2S ASoC driver for STM32.

Signed-off-by: olivier moysan <olivier.moysan@st.com>
---
 sound/soc/Kconfig         |    1 +
 sound/soc/Makefile        |    1 +
 sound/soc/stm/Kconfig     |    8 +
 sound/soc/stm/Makefile    |    2 +
 sound/soc/stm/stm32_i2s.c | 1069 +++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 1081 insertions(+)
 create mode 100644 sound/soc/stm/Kconfig
 create mode 100644 sound/soc/stm/Makefile
 create mode 100644 sound/soc/stm/stm32_i2s.c

diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 182d92e..3836ebe 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -63,6 +63,7 @@ source "sound/soc/sh/Kconfig"
 source "sound/soc/sirf/Kconfig"
 source "sound/soc/spear/Kconfig"
 source "sound/soc/sti/Kconfig"
+source "sound/soc/stm/Kconfig"
 source "sound/soc/sunxi/Kconfig"
 source "sound/soc/tegra/Kconfig"
 source "sound/soc/txx9/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 9a30f21..5440cf7 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_SND_SOC)	+= sh/
 obj-$(CONFIG_SND_SOC)	+= sirf/
 obj-$(CONFIG_SND_SOC)	+= spear/
 obj-$(CONFIG_SND_SOC)	+= sti/
+obj-$(CONFIG_SND_SOC)	+= stm/
 obj-$(CONFIG_SND_SOC)	+= sunxi/
 obj-$(CONFIG_SND_SOC)	+= tegra/
 obj-$(CONFIG_SND_SOC)	+= txx9/
diff --git a/sound/soc/stm/Kconfig b/sound/soc/stm/Kconfig
new file mode 100644
index 0000000..972970f
--- /dev/null
+++ b/sound/soc/stm/Kconfig
@@ -0,0 +1,8 @@
+menuconfig SND_SOC_STM32
+	tristate "STMicroelectronics STM32 SOC audio support"
+	depends on ARCH_STM32 || COMPILE_TEST
+	depends on SND_SOC
+	select SND_SOC_GENERIC_DMAENGINE_PCM
+	select REGMAP_MMIO
+	help
+	  Say Y if you want to enable ASoC-support for STM32
diff --git a/sound/soc/stm/Makefile b/sound/soc/stm/Makefile
new file mode 100644
index 0000000..090836b
--- /dev/null
+++ b/sound/soc/stm/Makefile
@@ -0,0 +1,2 @@
+snd-soc-stm32-i2s-objs := stm32_i2s.o
+obj-$(CONFIG_SND_SOC_STM32) += snd-soc-stm32-i2s.o
diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c
new file mode 100644
index 0000000..2ca9719
--- /dev/null
+++ b/sound/soc/stm/stm32_i2s.c
@@ -0,0 +1,1069 @@
+/*
+ *  STM32 ALSA SoC Digital Audio Interface (I2S) driver.
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author(s): Olivier Moysan <olivier.moysan@st.com> for STMicroelectronics.
+ *
+ * License terms: GPL V2.0.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/spinlock.h>
+
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+
+#define STM32_I2S_CR1_REG	0x0
+#define STM32_I2S_CFG1_REG	0x08
+#define STM32_I2S_CFG2_REG	0x0C
+#define STM32_I2S_IER_REG	0x10
+#define STM32_I2S_SR_REG	0x14
+#define STM32_I2S_IFCR_REG	0x18
+#define STM32_I2S_TXDR_REG	0X20
+#define STM32_I2S_RXDR_REG	0x30
+#define STM32_I2S_CGFR_REG	0X50
+
+/* Bit definition for SPI2S_CR1 register */
+#define I2S_CR1_SPE		BIT(0)
+#define I2S_CR1_CSTART		BIT(9)
+#define I2S_CR1_CSUSP		BIT(10)
+#define I2S_CR1_HDDIR		BIT(11)
+#define I2S_CR1_SSI		BIT(12)
+#define I2S_CR1_CRC33_17	BIT(13)
+#define I2S_CR1_RCRCI		BIT(14)
+#define I2S_CR1_TCRCI		BIT(15)
+
+/* Bit definition for SPI_CFG2 register */
+#define I2S_CFG2_IOSWP_SHIFT	15
+#define I2S_CFG2_IOSWP		BIT(I2S_CFG2_IOSWP_SHIFT)
+#define I2S_CFG2_LSBFRST	BIT(23)
+#define I2S_CFG2_AFCNTR		BIT(31)
+
+/* Bit definition for SPI_CFG1 register */
+#define I2S_CFG1_FTHVL_SHIFT	5
+#define I2S_CFG1_FTHVL_MASK	GENMASK(8, I2S_CFG1_FTHVL_SHIFT)
+#define I2S_CFG1_FTHVL_SET(x)	((x) << I2S_CFG1_FTHVL_SHIFT)
+
+#define I2S_CFG1_TXDMAEN	BIT(15)
+#define I2S_CFG1_RXDMAEN	BIT(14)
+
+/* Bit definition for SPI2S_IER register */
+#define I2S_IER_RXPIE		BIT(0)
+#define I2S_IER_TXPIE		BIT(1)
+#define I2S_IER_DPXPIE		BIT(2)
+#define I2S_IER_EOTIE		BIT(3)
+#define I2S_IER_TXTFIE		BIT(4)
+#define I2S_IER_UDRIE		BIT(5)
+#define I2S_IER_OVRIE		BIT(6)
+#define I2S_IER_CRCEIE		BIT(7)
+#define I2S_IER_TIFREIE		BIT(8)
+#define I2S_IER_MODFIE		BIT(9)
+#define I2S_IER_TSERFIE		BIT(10)
+
+/* Bit definition for SPI2S_SR register */
+#define I2S_SR_RXP		BIT(0)
+#define I2S_SR_TXP		BIT(1)
+#define I2S_SR_DPXP		BIT(2)
+#define I2S_SR_EOT		BIT(3)
+#define I2S_SR_TXTF		BIT(4)
+#define I2S_SR_UDR		BIT(5)
+#define I2S_SR_OVR		BIT(6)
+#define I2S_SR_CRCERR		BIT(7)
+#define I2S_SR_TIFRE		BIT(8)
+#define I2S_SR_MODF		BIT(9)
+#define I2S_SR_TSERF		BIT(10)
+#define I2S_SR_SUSP		BIT(11)
+#define I2S_SR_TXC		BIT(12)
+#define I2S_SR_RXPLVL		GENMASK(14, 13)
+#define I2S_SR_RXWNE		BIT(15)
+
+#define I2S_SR_MASK		GENMASK(15, 0)
+
+/* Bit definition for SPI_IFCR register */
+#define I2S_IFCR_EOTC		BIT(3)
+#define I2S_IFCR_TXTFC		BIT(4)
+#define I2S_IFCR_UDRC		BIT(5)
+#define I2S_IFCR_OVRC		BIT(6)
+#define I2S_IFCR_CRCEC		BIT(7)
+#define I2S_IFCR_TIFREC		BIT(8)
+#define I2S_IFCR_MODFC		BIT(9)
+#define I2S_IFCR_TSERFC		BIT(10)
+#define I2S_IFCR_SUSPC		BIT(11)
+
+#define I2S_IFCR_MASK		GENMASK(11, 3)
+
+/* Bit definition for SPI_I2SCGFR register */
+#define I2S_CGFR_I2SMOD		BIT(0)
+
+#define I2S_CGFR_I2SCFG_SHIFT	1
+#define I2S_CGFR_I2SCFG_MASK	GENMASK(3, I2S_CGFR_I2SCFG_SHIFT)
+#define I2S_CGFR_I2SCFG_SET(x)	((x) << I2S_CGFR_I2SCFG_SHIFT)
+
+#define I2S_CGFR_I2SSTD_SHIFT	4
+#define I2S_CGFR_I2SSTD_MASK	GENMASK(5, I2S_CGFR_I2SSTD_SHIFT)
+#define I2S_CGFR_I2SSTD_SET(x)	((x) << I2S_CGFR_I2SSTD_SHIFT)
+
+#define I2S_CGFR_PCMSYNC	BIT(7)
+
+#define I2S_CGFR_DATLEN_SHIFT	8
+#define I2S_CGFR_DATLEN_MASK	GENMASK(9, I2S_CGFR_DATLEN_SHIFT)
+#define I2S_CGFR_DATLEN_SET(x)	((x) << I2S_CGFR_DATLEN_SHIFT)
+
+#define I2S_CGFR_CHLEN_SHIFT	10
+#define I2S_CGFR_CHLEN		BIT(I2S_CGFR_CHLEN_SHIFT)
+#define I2S_CGFR_CKPOL		BIT(11)
+#define I2S_CGFR_FIXCH		BIT(12)
+#define I2S_CGFR_WSINV		BIT(13)
+#define I2S_CGFR_DATFMT		BIT(14)
+
+#define I2S_CGFR_I2SDIV_SHIFT	16
+#define I2S_CGFR_I2SDIV_BIT_H	23
+#define I2S_CGFR_I2SDIV_MASK	GENMASK(I2S_CGFR_I2SDIV_BIT_H,\
+				I2S_CGFR_I2SDIV_SHIFT)
+#define I2S_CGFR_I2SDIV_SET(x)	((x) << I2S_CGFR_I2SDIV_SHIFT)
+#define	I2S_CGFR_I2SDIV_MAX	((1 << (I2S_CGFR_I2SDIV_BIT_H -\
+				I2S_CGFR_I2SDIV_SHIFT)) - 1)
+
+#define I2S_CGFR_ODD_SHIFT	24
+#define I2S_CGFR_ODD		BIT(I2S_CGFR_ODD_SHIFT)
+#define I2S_CGFR_MCKOE		BIT(25)
+
+enum i2s_master_mode {
+	I2S_MS_NOT_SET,
+	I2S_MS_MASTER,
+	I2S_MS_SLAVE,
+};
+
+enum i2s_mode {
+	I2S_I2SMOD_TX_SLAVE,
+	I2S_I2SMOD_RX_SLAVE,
+	I2S_I2SMOD_TX_MASTER,
+	I2S_I2SMOD_RX_MASTER,
+	I2S_I2SMOD_FD_SLAVE,
+	I2S_I2SMOD_FD_MASTER,
+};
+
+enum i2s_fifo_th {
+	I2S_FIFO_TH_NONE,
+	I2S_FIFO_TH_ONE_QUARTER,
+	I2S_FIFO_TH_HALF,
+	I2S_FIFO_TH_THREE_QUARTER,
+	I2S_FIFO_TH_FULL,
+};
+
+enum i2s_std {
+	I2S_STD_I2S,
+	I2S_STD_LEFT_J,
+	I2S_STD_RIGHT_J,
+	I2S_STD_DSP,
+};
+
+enum i2s_dir {
+	I2S_DIR_TX,
+	I2S_DIR_RX,
+	I2S_DIR_FD,
+};
+
+enum i2s_datlen {
+	I2S_I2SMOD_DATLEN_16,
+	I2S_I2SMOD_DATLEN_24,
+	I2S_I2SMOD_DATLEN_32,
+};
+
+#define STM32_I2S_DAI_NAME_SIZE		20
+#define STM32_I2S_DAIS_NB		3
+#define STM32_I2S_IP_NAME_LENGTH	5
+#define STM32_I2S_FIFO_SIZE		16
+
+#define STM32_I2S_IS_MASTER(x)		((x)->ms_flg == I2S_MS_MASTER)
+#define STM32_I2S_IS_SLAVE(x)		((x)->ms_flg == I2S_MS_SLAVE)
+
+#define STM32_I2S_IS_PLAYBACK(x)	((x)->dir == I2S_DIR_TX)
+#define STM32_I2S_IS_CAPTURE(x)		((x)->dir == I2S_DIR_RX)
+#define STM32_I2S_IS_FULL_DUPLEX(x)	((x)->dir == I2S_DIR_FD)
+
+/**
+ * @regmap_conf: I2S register map configuration pointer
+ * @egmap: I2S register map pointer
+ * @pdev: device data pointer
+ * @dai_drv: DAI driver pointer
+ * @dma_data_tx: dma configuration data for tx channel
+ * @dma_data_rx: dma configuration data for tx channel
+ * @substream: PCM substream data pointer
+ * @i2sclk: kernel clock feeding the I2S clock generator
+ * @pclk: peripheral clock driving bus interface
+ * @x8kclk: I2S parent clock for sampling frequencies multiple of 8kHz
+ * @x11kclk: I2S parent clock for sampling frequencies multiple of 11kHz
+ * @base:  mmio register base virtual address
+ * @phys_addr: I2S registers physical base address
+ * @lock_fd: lock to manage race conditions in full duplex mode
+ * @ip_name: I2S name
+ * @dais_name: playback, capture and fd DAI names
+ * @fifo_th: fifo threshold setting
+ * @mclk_rate: master clock frequency (Hz)
+ * @fmt: DAI protocol
+ * @refcount: keep count of opened streams on I2S
+ * @startcount: keep count of started streams on I2S
+ * @dir: I2S direction. tx, rx or full duplex.
+ * @ms_flg: master mode flag.
+ * @format: pcm stream width (16/32 bits), used for consistency check in fd mode
+ * @rate: pcm stream rate, used for consistency check in fd mode
+ */
+struct stm32_i2s_data {
+	const struct regmap_config *regmap_conf;
+	struct regmap *regmap;
+	struct platform_device *pdev;
+	struct snd_soc_dai_driver *dai_drv;
+	struct snd_dmaengine_dai_dma_data dma_data_tx;
+	struct snd_dmaengine_dai_dma_data dma_data_rx;
+	struct snd_pcm_substream *substream;
+	struct clk *i2sclk;
+	struct clk *pclk;
+	struct clk *x8kclk;
+	struct clk *x11kclk;
+	void __iomem *base;
+	dma_addr_t phys_addr;
+	spinlock_t lock_fd; /* Manage race conditions for full duplex */
+	const char *ip_name;
+	char dais_name[STM32_I2S_DAIS_NB][STM32_I2S_DAI_NAME_SIZE];
+	unsigned int fifo_th;
+	unsigned int mclk_rate;
+	unsigned int fmt;
+	int refcount;
+	int startcount;
+	int dir;
+	int ms_flg;
+	int format;
+	unsigned int rate;
+};
+
+static irqreturn_t stm32_i2s_isr(int irq, void *devid)
+{
+	struct stm32_i2s_data *i2s = (struct stm32_i2s_data *)devid;
+	struct platform_device *pdev = i2s->pdev;
+	u32 sr, ier;
+	unsigned long flags;
+	int err = 0;
+
+	regmap_read(i2s->regmap, STM32_I2S_SR_REG, &sr);
+	regmap_read(i2s->regmap, STM32_I2S_IER_REG, &ier);
+
+	flags = sr & ier;
+	if (!flags) {
+		dev_dbg(&pdev->dev, "Spurious IT sr=0x%08x, ier=0x%08x\n",
+			sr, ier);
+		return IRQ_NONE;
+	}
+
+	/* Clear ITs */
+	regmap_update_bits(i2s->regmap, STM32_I2S_IFCR_REG,
+			   I2S_IFCR_MASK, flags);
+
+	if (flags & I2S_SR_OVR) {
+		dev_dbg(&pdev->dev, "Overrun: received value discarded\n");
+		err = 1;
+	}
+
+	if (flags & I2S_SR_UDR) {
+		dev_dbg(&pdev->dev, "Underrun\n");
+		err = 1;
+	}
+
+	if (flags & I2S_SR_TIFRE)
+		dev_dbg(&pdev->dev, "Frame error\n");
+
+	if (err)
+		snd_pcm_stop_xrun(i2s->substream);
+
+	return IRQ_HANDLED;
+}
+
+static bool stm32_i2s_readable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case STM32_I2S_CR1_REG:
+	case STM32_I2S_CFG1_REG:
+	case STM32_I2S_CFG2_REG:
+	case STM32_I2S_IER_REG:
+	case STM32_I2S_SR_REG:
+	case STM32_I2S_IFCR_REG:
+	case STM32_I2S_TXDR_REG:
+	case STM32_I2S_RXDR_REG:
+	case STM32_I2S_CGFR_REG:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool stm32_i2s_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case STM32_I2S_TXDR_REG:
+	case STM32_I2S_RXDR_REG:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool stm32_i2s_writeable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case STM32_I2S_CR1_REG:
+	case STM32_I2S_CFG1_REG:
+	case STM32_I2S_CFG2_REG:
+	case STM32_I2S_IER_REG:
+	case STM32_I2S_IFCR_REG:
+	case STM32_I2S_TXDR_REG:
+	case STM32_I2S_CGFR_REG:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static int stm32_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+	struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai);
+	u32 cgfr;
+	u32 cgfr_mask =  I2S_CGFR_I2SSTD_MASK | I2S_CGFR_CKPOL |
+			 I2S_CGFR_WSINV | I2S_CGFR_I2SCFG_MASK;
+
+	dev_dbg(cpu_dai->dev, "fmt %x\n", fmt);
+
+	/*
+	 * winv = 0 : default behavior (high/low) for all standards
+	 * ckpol 0 for all standards.
+	 */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		cgfr = I2S_CGFR_I2SSTD_SET(I2S_STD_I2S);
+		break;
+	case SND_SOC_DAIFMT_MSB:
+		cgfr = I2S_CGFR_I2SSTD_SET(I2S_STD_LEFT_J);
+		break;
+	case SND_SOC_DAIFMT_LSB:
+		cgfr = I2S_CGFR_I2SSTD_SET(I2S_STD_RIGHT_J);
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		cgfr = I2S_CGFR_I2SSTD_SET(I2S_STD_DSP);
+		break;
+	/* DSP_B not mapped on I2S PCM long format. 1 bit offset does not fit */
+	default:
+		dev_err(cpu_dai->dev, "Unsupported protocol %#x\n",
+			fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+		return -EINVAL;
+	}
+
+	/* DAI clock strobing */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		cgfr |= I2S_CGFR_CKPOL;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		cgfr |= I2S_CGFR_WSINV;
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		cgfr |= I2S_CGFR_CKPOL;
+		cgfr |= I2S_CGFR_WSINV;
+		break;
+	default:
+		dev_err(cpu_dai->dev, "Unsupported strobing %#x\n",
+			fmt & SND_SOC_DAIFMT_INV_MASK);
+		return -EINVAL;
+	}
+
+	/* DAI clock master masks */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		if (STM32_I2S_IS_MASTER(i2s)) {
+			dev_err(cpu_dai->dev, "previous DAI set master mode\n");
+			return -EINVAL;
+		}
+		i2s->ms_flg = I2S_MS_SLAVE;
+
+		if (STM32_I2S_IS_FULL_DUPLEX(i2s))
+			cgfr |= I2S_CGFR_I2SCFG_SET(I2S_I2SMOD_FD_SLAVE);
+		else if (STM32_I2S_IS_PLAYBACK(i2s))
+			cgfr |= I2S_CGFR_I2SCFG_SET(I2S_I2SMOD_TX_SLAVE);
+		else
+			cgfr |= I2S_CGFR_I2SCFG_SET(I2S_I2SMOD_RX_SLAVE);
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		if (STM32_I2S_IS_SLAVE(i2s)) {
+			dev_err(cpu_dai->dev, "previous DAI set slave mode\n");
+			return -EINVAL;
+		}
+		i2s->ms_flg = I2S_MS_MASTER;
+
+		if (STM32_I2S_IS_FULL_DUPLEX(i2s))
+			cgfr |= I2S_CGFR_I2SCFG_SET(I2S_I2SMOD_FD_MASTER);
+		else if (STM32_I2S_IS_PLAYBACK(i2s))
+			cgfr |= I2S_CGFR_I2SCFG_SET(I2S_I2SMOD_TX_MASTER);
+		else
+			cgfr |= I2S_CGFR_I2SCFG_SET(I2S_I2SMOD_RX_MASTER);
+		break;
+	default:
+		dev_err(cpu_dai->dev, "Unsupported mode %#x\n",
+			fmt & SND_SOC_DAIFMT_MASTER_MASK);
+		return -EINVAL;
+	}
+
+	i2s->fmt = fmt;
+	return regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG,
+				  cgfr_mask, cgfr);
+}
+
+static int stm32_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
+				int clk_id, unsigned int freq, int dir)
+{
+	struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai);
+
+	dev_dbg(cpu_dai->dev, "I2S MCLK frequency is %uHz\n", freq);
+
+	if ((dir == SND_SOC_CLOCK_OUT) && STM32_I2S_IS_MASTER(i2s)) {
+		i2s->mclk_rate = freq;
+
+		/* Enable master clock if master mode and mclk-fs are set */
+		return regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG,
+					  I2S_CGFR_MCKOE, I2S_CGFR_MCKOE);
+	}
+
+	return 0;
+}
+
+static int stm32_i2s_configure_clock(struct snd_soc_dai *cpu_dai,
+				     struct snd_pcm_hw_params *params)
+{
+	struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai);
+	unsigned long i2s_clock_rate;
+	unsigned int tmp, div, real_div, nb_bits, frame_len;
+	unsigned int rate = params_rate(params);
+	int ret;
+	u32 cgfr, cgfr_mask;
+	bool odd;
+
+	if (i2s->refcount > 1) {
+		if (rate != i2s->rate) {
+			dev_err(cpu_dai->dev,
+				"rate not compatible with active stream");
+			return -EINVAL;
+		}
+		return 0;
+	}
+	i2s->rate = rate;
+
+	if (!(rate % 11025))
+		clk_set_parent(i2s->i2sclk, i2s->x11kclk);
+	else
+		clk_set_parent(i2s->i2sclk, i2s->x8kclk);
+	i2s_clock_rate = clk_get_rate(i2s->i2sclk);
+
+	/*
+	 * mckl = mclk_ratio x ws
+	 *   i2s mode : mclk_ratio = 256
+	 *   dsp mode : mclk_ratio = 128
+	 *
+	 * mclk on
+	 *   i2s mode : div = i2s_clk / (mclk_ratio * ws)
+	 *   dsp mode : div = i2s_clk / (mclk_ratio * ws)
+	 * mclk off
+	 *   i2s mode : div = i2s_clk / (nb_bits x ws)
+	 *   dsp mode : div = i2s_clk / (nb_bits x ws)
+	 */
+	if (i2s->mclk_rate) {
+		tmp = DIV_ROUND_CLOSEST(i2s_clock_rate, i2s->mclk_rate);
+	} else {
+		frame_len = 32;
+		if ((i2s->fmt & SND_SOC_DAIFMT_FORMAT_MASK) ==
+		    SND_SOC_DAIFMT_DSP_A)
+			frame_len = 16;
+
+		/* master clock not enabled */
+		ret = regmap_read(i2s->regmap, STM32_I2S_CGFR_REG, &cgfr);
+		if (ret < 0)
+			return ret;
+
+		nb_bits = frame_len * ((cgfr & I2S_CGFR_CHLEN) + 1);
+		tmp = DIV_ROUND_CLOSEST(i2s_clock_rate, (nb_bits * rate));
+	}
+
+	/* Check the parity of the divider */
+	odd = tmp & 0x1;
+
+	/* Compute the div prescaler */
+	div = tmp >> 1;
+
+	cgfr = I2S_CGFR_I2SDIV_SET(div) | (odd << I2S_CGFR_ODD_SHIFT);
+	cgfr_mask = I2S_CGFR_I2SDIV_MASK | I2S_CGFR_ODD;
+
+	real_div = ((2 * div) + odd);
+	dev_dbg(cpu_dai->dev, "I2S clk: %ld, SCLK: %d\n",
+		i2s_clock_rate, rate);
+	dev_dbg(cpu_dai->dev, "Divider: 2*%d(div)+%d(odd) = %d\n",
+		div, odd, real_div);
+
+	if (((div == 1) && odd) || (div > I2S_CGFR_I2SDIV_MAX)) {
+		dev_err(cpu_dai->dev, "Wrong divider setting\n");
+		return -EINVAL;
+	}
+
+	if (!div && !odd)
+		dev_warn(cpu_dai->dev, "real divider forced to 1\n");
+
+	ret = regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG,
+				 cgfr_mask, cgfr);
+	if (ret < 0)
+		return ret;
+
+	/* Set bitclock and frameclock to their inactive state */
+	return regmap_update_bits(i2s->regmap, STM32_I2S_CFG2_REG,
+				  I2S_CFG2_AFCNTR, I2S_CFG2_AFCNTR);
+}
+
+static int stm32_i2s_configure_channel(struct snd_soc_dai *cpu_dai,
+				       struct snd_pcm_hw_params *params)
+{
+	struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai);
+	int format = params_width(params);
+	u32 cfgr, cfgr_mask;
+
+	if (i2s->refcount > 1) {
+		if (format != i2s->format) {
+			dev_err(cpu_dai->dev,
+				"format not compatible with active stream");
+			return -EINVAL;
+		}
+		return 0;
+	}
+	i2s->format = format;
+
+	switch (format) {
+	case 16:
+		cfgr = I2S_CGFR_DATLEN_SET(I2S_I2SMOD_DATLEN_16);
+		cfgr_mask = I2S_CGFR_DATLEN_MASK;
+		break;
+	case 32:
+		cfgr = I2S_CGFR_DATLEN_SET(I2S_I2SMOD_DATLEN_32) |
+					   I2S_CGFR_CHLEN;
+		cfgr_mask = I2S_CGFR_DATLEN_MASK | I2S_CGFR_CHLEN;
+		break;
+	default:
+		dev_err(cpu_dai->dev, "Unexpected format %d", format);
+		return -EINVAL;
+	}
+
+	if (STM32_I2S_IS_SLAVE(i2s)) {
+		/* As data length is either 16 or 32 bits, fixch always set */
+		cfgr |= I2S_CGFR_FIXCH;
+		cfgr_mask |= I2S_CGFR_FIXCH;
+	}
+
+	return regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG,
+				  cfgr_mask, cfgr);
+}
+
+static int stm32_i2s_startup(struct snd_pcm_substream *substream,
+			     struct snd_soc_dai *cpu_dai)
+{
+	struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai);
+	int ret, ier;
+
+	i2s->substream = substream;
+
+	spin_lock(&i2s->lock_fd);
+	i2s->refcount++;
+	spin_unlock(&i2s->lock_fd);
+
+	ret = regmap_update_bits(i2s->regmap, STM32_I2S_IFCR_REG,
+				 I2S_IFCR_MASK, 0);
+	if (ret < 0)
+		return ret;
+
+	/* Enable ITs */
+	ier = I2S_IER_OVRIE | I2S_IER_UDRIE;
+	if (STM32_I2S_IS_SLAVE(i2s))
+		ier |= I2S_IER_TIFREIE;
+
+	return regmap_update_bits(i2s->regmap, STM32_I2S_IER_REG, ier, ier);
+}
+
+static int stm32_i2s_hw_params(struct snd_pcm_substream *substream,
+			       struct snd_pcm_hw_params *params,
+			       struct snd_soc_dai *cpu_dai)
+{
+	struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai);
+	int format = params_width(params);
+	unsigned int fthlv;
+	int ret;
+
+	if ((params_channels(params) == 1) &&
+	    ((i2s->fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_DSP_A)) {
+		dev_err(cpu_dai->dev, "Mono mode supported only by DSP_A\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * fthlv is fifo threshold expressed as sample number
+	 * fthlv = fifo size * threshold_ratio / (format / 8)
+	 * where threshold_ratio = fifo_th / 4
+	 */
+	fthlv = STM32_I2S_FIFO_SIZE * 8 * i2s->fifo_th / (format * 4);
+
+	regmap_update_bits(i2s->regmap, STM32_I2S_CFG1_REG, I2S_CFG1_FTHVL_MASK,
+			   I2S_CFG1_FTHVL_SET(fthlv - 1));
+
+	spin_lock(&i2s->lock_fd);
+
+	ret = stm32_i2s_configure_channel(cpu_dai, params);
+	if (ret < 0) {
+		spin_unlock(&i2s->lock_fd);
+		return ret;
+	}
+
+	if (STM32_I2S_IS_MASTER(i2s)) {
+		ret = stm32_i2s_configure_clock(cpu_dai, params);
+		if (ret < 0) {
+			spin_unlock(&i2s->lock_fd);
+			return ret;
+		}
+	}
+
+	spin_unlock(&i2s->lock_fd);
+
+	return 0;
+}
+
+static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+			     struct snd_soc_dai *cpu_dai)
+{
+	struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai);
+	int ret;
+	bool stream_is_playback = false;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		stream_is_playback = true;
+
+	/* This lock protects both start flag and SPE bit accesses */
+	spin_lock(&i2s->lock_fd);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		if (stream_is_playback) {
+			ret = regmap_update_bits(i2s->regmap,
+						 STM32_I2S_CFG1_REG,
+						 I2S_CFG1_TXDMAEN,
+						 I2S_CFG1_TXDMAEN);
+		} else {
+			ret = regmap_update_bits(i2s->regmap,
+						 STM32_I2S_CFG1_REG,
+						 I2S_CFG1_RXDMAEN,
+						 I2S_CFG1_RXDMAEN);
+		}
+		if (ret < 0)
+			goto err_trigger;
+
+		i2s->startcount++;
+		if (STM32_I2S_IS_FULL_DUPLEX(i2s) && (i2s->startcount < 2)) {
+			dev_dbg(cpu_dai->dev,
+				"Full duplex device waiting for %s stream",
+				stream_is_playback ? "capture" : "playback");
+			spin_unlock(&i2s->lock_fd);
+			return 0;
+		}
+
+		/* Enable i2s */
+		dev_dbg(cpu_dai->dev, "start I2S\n");
+
+		ret = regmap_update_bits(i2s->regmap, STM32_I2S_CR1_REG,
+					 I2S_CR1_SPE, I2S_CR1_SPE);
+		if (ret < 0) {
+			dev_err(cpu_dai->dev, "Error %d enabling I2S\n", ret);
+			goto err_trigger;
+		}
+
+		ret = regmap_update_bits(i2s->regmap, STM32_I2S_CR1_REG,
+					 I2S_CR1_CSTART, I2S_CR1_CSTART);
+		if (ret < 0) {
+			dev_err(cpu_dai->dev, "Error %d starting I2S\n", ret);
+			goto err_trigger;
+		}
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		dev_dbg(cpu_dai->dev, "stop I2S\n");
+
+		ret = regmap_update_bits(i2s->regmap, STM32_I2S_CR1_REG,
+					 I2S_CR1_SPE, 0);
+		if (ret < 0) {
+			dev_err(cpu_dai->dev, "Error %d disabling I2S\n", ret);
+			goto err_trigger;
+		}
+		i2s->startcount = 0;
+
+		if (stream_is_playback) {
+			ret = regmap_update_bits(i2s->regmap,
+						 STM32_I2S_CFG1_REG,
+						 I2S_CFG1_TXDMAEN, 0);
+		} else {
+			ret = regmap_update_bits(i2s->regmap,
+						 STM32_I2S_CFG1_REG,
+						 I2S_CFG1_RXDMAEN, 0);
+		}
+		if (ret < 0)
+			goto err_trigger;
+
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	spin_unlock(&i2s->lock_fd);
+	return 0;
+
+err_trigger:
+	spin_unlock(&i2s->lock_fd);
+	return ret;
+}
+
+static void stm32_i2s_shutdown(struct snd_pcm_substream *substream,
+			       struct snd_soc_dai *cpu_dai)
+{
+	struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai);
+
+	i2s->substream = NULL;
+
+	spin_lock(&i2s->lock_fd);
+	if (i2s->refcount > 0)
+		i2s->refcount--;
+	spin_unlock(&i2s->lock_fd);
+
+	regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG,
+			   I2S_CGFR_MCKOE, (unsigned int)~I2S_CGFR_MCKOE);
+}
+
+static int stm32_i2s_dai_probe(struct snd_soc_dai *cpu_dai)
+{
+	struct stm32_i2s_data *i2s = dev_get_drvdata(cpu_dai->dev);
+	struct snd_dmaengine_dai_dma_data *dma_data_tx = &i2s->dma_data_tx;
+	struct snd_dmaengine_dai_dma_data *dma_data_rx = &i2s->dma_data_rx;
+
+	/* Buswidth will be set by framework */
+	dma_data_tx->addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED;
+	dma_data_tx->addr = (dma_addr_t)(i2s->phys_addr) + STM32_I2S_TXDR_REG;
+	dma_data_tx->maxburst = 1;
+	dma_data_rx->addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED;
+	dma_data_rx->addr = (dma_addr_t)(i2s->phys_addr) + STM32_I2S_RXDR_REG;
+	dma_data_rx->maxburst = 1;
+
+	i2s->dir = cpu_dai->id;
+	switch (i2s->dir) {
+	case I2S_DIR_TX:
+		i2s->fifo_th = I2S_FIFO_TH_FULL;
+		snd_soc_dai_init_dma_data(cpu_dai, dma_data_tx, NULL);
+		break;
+	case I2S_DIR_RX:
+		i2s->fifo_th = I2S_FIFO_TH_HALF;
+		snd_soc_dai_init_dma_data(cpu_dai, NULL, dma_data_rx);
+		break;
+	case I2S_DIR_FD:
+		i2s->fifo_th = I2S_FIFO_TH_NONE;
+		snd_soc_dai_init_dma_data(cpu_dai, dma_data_tx, dma_data_rx);
+		break;
+	}
+
+	return 0;
+}
+
+static const struct regmap_config stm32_h7_i2s_regmap_conf = {
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+	.max_register = STM32_I2S_CGFR_REG,
+	.readable_reg = stm32_i2s_readable_reg,
+	.volatile_reg = stm32_i2s_volatile_reg,
+	.writeable_reg = stm32_i2s_writeable_reg,
+	.fast_io = true,
+};
+
+static const struct snd_soc_dai_ops stm32_i2s_pcm_dai_ops = {
+	.set_sysclk	= stm32_i2s_set_sysclk,
+	.set_fmt	= stm32_i2s_set_dai_fmt,
+	.startup	= stm32_i2s_startup,
+	.hw_params	= stm32_i2s_hw_params,
+	.trigger	= stm32_i2s_trigger,
+	.shutdown	= stm32_i2s_shutdown,
+};
+
+static const struct snd_pcm_hardware stm32_i2s_pcm_hw = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP,
+	.buffer_bytes_max = 8 * PAGE_SIZE,
+	.period_bytes_max = 2048,
+	.periods_min = 2,
+	.periods_max = 8,
+};
+
+static const struct snd_dmaengine_pcm_config stm32_i2s_pcm_config = {
+	.pcm_hardware	= &stm32_i2s_pcm_hw,
+	.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+	.prealloc_buffer_size = PAGE_SIZE * 8,
+};
+
+static const struct snd_soc_component_driver stm32_i2s_component = {
+	.name = "stm32-i2s",
+};
+
+static void stm32_i2s_dai_init(struct snd_soc_pcm_stream *stream,
+			       char *stream_name)
+{
+	stream->stream_name = stream_name;
+	stream->channels_min = 1;
+	stream->channels_max = 2;
+	stream->rates = SNDRV_PCM_RATE_8000_192000;
+	stream->formats = SNDRV_PCM_FMTBIT_S16_LE |
+				   SNDRV_PCM_FMTBIT_S32_LE;
+}
+
+static int stm32_i2s_dais_init(struct platform_device *pdev,
+			       struct stm32_i2s_data *i2s)
+{
+	struct snd_soc_dai_driver *dai_ptr;
+	int i;
+
+	dai_ptr = devm_kzalloc(&pdev->dev, STM32_I2S_DAIS_NB *
+			       sizeof(struct snd_soc_dai_driver), GFP_KERNEL);
+	if (!dai_ptr)
+		return -ENOMEM;
+
+	i2s->dai_drv = dai_ptr;
+	for (i = 0; i < STM32_I2S_DAIS_NB; i++) {
+		dai_ptr->probe = stm32_i2s_dai_probe;
+		dai_ptr->ops = &stm32_i2s_pcm_dai_ops;
+		if (i == 0) {
+			snprintf(i2s->dais_name[i], STM32_I2S_DAI_NAME_SIZE,
+				 "%s-playback", i2s->ip_name);
+			dai_ptr->id = I2S_DIR_TX;
+			stm32_i2s_dai_init(&dai_ptr->playback, "CPU-Playback");
+		}
+		if (i == 1) {
+			snprintf(i2s->dais_name[i], STM32_I2S_DAI_NAME_SIZE,
+				 "%s-capture", i2s->ip_name);
+			dai_ptr->id = I2S_DIR_RX;
+			stm32_i2s_dai_init(&dai_ptr->capture, "CPU-Capture");
+		}
+		if (i == 2) {
+			snprintf(i2s->dais_name[i], STM32_I2S_DAI_NAME_SIZE,
+				 "%s-full-duplex", i2s->ip_name);
+			dai_ptr->id = I2S_DIR_FD;
+			stm32_i2s_dai_init(&dai_ptr->playback,
+					   "CPU-FD-Playback");
+			stm32_i2s_dai_init(&dai_ptr->capture,
+					   "CPU-FD-Capture");
+		}
+		dai_ptr->name = i2s->dais_name[i];
+		dai_ptr++;
+	}
+
+	return 0;
+}
+
+static const struct of_device_id stm32_i2s_ids[] = {
+	{
+		.compatible = "st,stm32h7-i2s",
+		.data = &stm32_h7_i2s_regmap_conf
+	},
+	{},
+};
+
+static int stm32_i2s_parse_dt(struct platform_device *pdev,
+			      struct stm32_i2s_data *i2s)
+{
+	struct device_node *np = pdev->dev.of_node;
+	const struct of_device_id *of_id;
+	struct reset_control *rst;
+	struct resource *res;
+	int irq, ret;
+
+	if (!np)
+		return -ENODEV;
+
+	of_id = of_match_device(stm32_i2s_ids, &pdev->dev);
+	if (of_id)
+		i2s->regmap_conf = (const struct regmap_config *)of_id->data;
+	else
+		return -EINVAL;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	i2s->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(i2s->base))
+		return PTR_ERR(i2s->base);
+
+	i2s->phys_addr = res->start;
+
+	/* Get clocks */
+	i2s->pclk = devm_clk_get(&pdev->dev, "pclk");
+	if (IS_ERR(i2s->pclk)) {
+		dev_err(&pdev->dev, "Could not get pclk\n");
+		return PTR_ERR(i2s->pclk);
+	}
+
+	i2s->i2sclk = devm_clk_get(&pdev->dev, "i2sclk");
+	if (IS_ERR(i2s->i2sclk)) {
+		dev_err(&pdev->dev, "Could not get i2sclk\n");
+		return PTR_ERR(i2s->i2sclk);
+	}
+
+	i2s->x8kclk = devm_clk_get(&pdev->dev, "x8k");
+	if (IS_ERR(i2s->x8kclk)) {
+		dev_err(&pdev->dev, "missing x8k parent clock\n");
+		return PTR_ERR(i2s->x8kclk);
+	}
+
+	i2s->x11kclk = devm_clk_get(&pdev->dev, "x11k");
+	if (IS_ERR(i2s->x11kclk)) {
+		dev_err(&pdev->dev, "missing x11k parent clock\n");
+		return PTR_ERR(i2s->x11kclk);
+	}
+
+	/* Get irqs */
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "no irq for node %s\n", pdev->name);
+		return -ENOENT;
+	}
+
+	ret = devm_request_irq(&pdev->dev, irq, stm32_i2s_isr, IRQF_ONESHOT,
+			       dev_name(&pdev->dev), i2s);
+	if (ret) {
+		dev_err(&pdev->dev, "irq request returned %d\n", ret);
+		return ret;
+	}
+
+	/* Reset */
+	rst = devm_reset_control_get(&pdev->dev, NULL);
+	if (!IS_ERR(rst)) {
+		reset_control_assert(rst);
+		udelay(2);
+		reset_control_deassert(rst);
+	}
+
+	return 0;
+}
+
+static int stm32_i2s_probe(struct platform_device *pdev)
+{
+	struct stm32_i2s_data *i2s;
+	int ret;
+
+	i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
+	if (!i2s)
+		return -ENOMEM;
+
+	i2s->pdev = pdev;
+	i2s->ms_flg = I2S_MS_NOT_SET;
+	i2s->ip_name = strstr(dev_name(&pdev->dev), ".") + 1;
+	spin_lock_init(&i2s->lock_fd);
+	platform_set_drvdata(pdev, i2s);
+
+	ret = stm32_i2s_parse_dt(pdev, i2s);
+	if (ret)
+		return ret;
+
+	ret = stm32_i2s_dais_init(pdev, i2s);
+	if (ret)
+		return ret;
+
+	i2s->regmap = devm_regmap_init_mmio(&pdev->dev, i2s->base,
+					    i2s->regmap_conf);
+	if (IS_ERR(i2s->regmap)) {
+		dev_err(&pdev->dev, "regmap init failed\n");
+		return PTR_ERR(i2s->regmap);
+	}
+
+	ret = clk_prepare_enable(i2s->pclk);
+	if (ret) {
+		dev_err(&pdev->dev, "Enable pclk failed: %d\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(i2s->i2sclk);
+	if (ret) {
+		dev_err(&pdev->dev, "Enable i2sclk failed: %d\n", ret);
+		goto err_pclk_disable;
+	}
+
+	ret = devm_snd_soc_register_component(&pdev->dev,
+					      &stm32_i2s_component,
+					      i2s->dai_drv, STM32_I2S_DAIS_NB);
+	if (ret)
+		goto err_clocks_disable;
+
+	ret = devm_snd_dmaengine_pcm_register(&pdev->dev,
+					      &stm32_i2s_pcm_config, 0);
+	if (ret)
+		goto err_clocks_disable;
+
+	/* Set SPI/I2S in i2s mode */
+	ret = regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG,
+				 I2S_CGFR_I2SMOD, I2S_CGFR_I2SMOD);
+	if (ret)
+		goto err_clocks_disable;
+
+	return ret;
+
+err_clocks_disable:
+	clk_disable_unprepare(i2s->i2sclk);
+err_pclk_disable:
+	clk_disable_unprepare(i2s->pclk);
+
+	return ret;
+}
+
+static int stm32_i2s_remove(struct platform_device *pdev)
+{
+	struct stm32_i2s_data *i2s = platform_get_drvdata(pdev);
+
+	clk_disable_unprepare(i2s->i2sclk);
+	clk_disable_unprepare(i2s->pclk);
+
+	return 0;
+}
+
+MODULE_DEVICE_TABLE(of, stm32_i2s_ids);
+
+static struct platform_driver stm32_i2s_driver = {
+	.driver = {
+		.name = "st,stm32-i2s",
+		.of_match_table = stm32_i2s_ids,
+	},
+	.probe = stm32_i2s_probe,
+	.remove = stm32_i2s_remove,
+};
+
+module_platform_driver(stm32_i2s_driver);
+
+MODULE_DESCRIPTION("STM32 Soc i2s Interface");
+MODULE_AUTHOR("Olivier Moysan, <olivier.moysan@st.com>");
+MODULE_ALIAS("platform:stm32-i2s");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

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

* Re: [PATCH 1/2] dt-bindings: Document STM32 I2S bindings
  2017-04-06 15:40   ` olivier moysan
@ 2017-04-10 19:48     ` Rob Herring
  -1 siblings, 0 replies; 32+ messages in thread
From: Rob Herring @ 2017-04-10 19:48 UTC (permalink / raw)
  To: olivier moysan
  Cc: mark.rutland, devicetree, alsa-devel, alexandre.torgue,
	arnaud.pouliquen, tiwai, lgirdwood, broonie, mcoquelin.stm32,
	linux-arm-kernel, benjamin.gaignard

On Thu, Apr 06, 2017 at 05:40:35PM +0200, olivier moysan wrote:
> Add documentation of device tree bindings for STM32 SPI/I2S.
> 
> Signed-off-by: olivier moysan <olivier.moysan@st.com>
> ---
>  .../devicetree/bindings/sound/st,stm32h7-i2s.txt   | 71 ++++++++++++++++++++++
>  1 file changed, 71 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/sound/st,stm32h7-i2s.txt
> 
> diff --git a/Documentation/devicetree/bindings/sound/st,stm32h7-i2s.txt b/Documentation/devicetree/bindings/sound/st,stm32h7-i2s.txt
> new file mode 100644
> index 0000000..b99467a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/sound/st,stm32h7-i2s.txt
> @@ -0,0 +1,71 @@
> +STMicroelectronics STM32 SPI/I2S Controller
> +
> +The SPI/I2S block supports I2S/PCM protocols when configured on I2S mode.
> +Only some SPI instances support I2S.
> +
> +Required properties:
> +  - compatible: Must be "st,stm32h7-i2s"
> +  - #sound-dai-cells: Must be 1. (one parameter)
> +    This parameter allows to specify CPU DAI index in soundcard CPU dai link.
> +	index 0: playback DAI
> +	index 1: capture DAI
> +	index 2: full duplex DAI

Is this still needed for graph-card?

> +  - reg: Offset and length of the device's register set.
> +  - interrupts: Must contain the interrupt line id.
> +  - clocks: Must contain phandle and clock specifier pairs for each entry
> +	in clock-names.
> +  - clock-names: Must contain "i2sclk", "pclk", "x8k" and "x11k".
> +	"i2sclk": clock which feeds the internal clock generator
> +	"pclk": clock which feeds the peripheral bus interface
> +	"x8k": I2S parent clock for sampling rates multiple of 8kHz.
> +	"x11k": I2S parent clock for sampling rates multiple of 11.025kHz.
> +  - dmas: DMA specifiers for tx and rx dma.
> +    See Documentation/devicetree/bindings/dma/stm32-dma.txt.
> +  - dma-names: Identifier for each DMA request line. Must be "tx" and "rx".
> +  - pinctrl-names: should contain only value "default"
> +  - pinctrl-0: see Documentation/devicetree/bindings/pinctrl/pinctrl-stm32.txt
> +
> +Optional properties:
> +  - resets: Reference to a reset controller asserting the reset controller
> +
> +Example:
> +sound_card {
> +	compatible = "audio-graph-card";
> +	dais = <&i2s2_port 0>;
> +};
> +
> +i2s2: audio-controller@40003800 {
> +	compatible = "st,stm32h7-i2s";
> +	#sound-dai-cells = <1>;
> +	reg = <0x40003800 0x400>;
> +	interrupts = <36>;
> +	clocks = <&rcc PCLK1>, <&rcc SPI2_CK>, <&rcc PLL1_Q>, <&rcc PLL2_P>;
> +	clock-names = "pclk", "i2sclk",  "x8k", "x11k";
> +	dmas = <&dmamux2 2 39 0x400 0x1>,
> +		       <&dmamux2 3 40 0x400 0x1>;
> +	dma-names = "rx", "tx";
> +	pinctrl-names = "default";
> +	pinctrl-0 = <&pinctrl_i2s2>;
> +
> +	ports {
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +
> +		i2s2_port: port@0 {
> +			reg = <0>;
> +			cpu_endpoint: endpoint {
> +				remote-endpoint = <&codec_endpoint>;
> +				audio-graph-card,format = "i2s";
> +				audio-graph-card,bitclock-master = <&codec_endpoint>;
> +				audio-graph-card,frame-master = <&codec_endpoint>;

The 'audio-graph-card,' part has been dropped.

> +			};
> +		};
> +};
> +
> +audio-codec {
> +		codec_port: port {
> +			codec_endpoint: endpoint {
> +				remote-endpoint = <&cpu_endpoint>;
> +		};
> +	};
> +};
> -- 
> 1.9.1
> 

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

* [PATCH 1/2] dt-bindings: Document STM32 I2S bindings
@ 2017-04-10 19:48     ` Rob Herring
  0 siblings, 0 replies; 32+ messages in thread
From: Rob Herring @ 2017-04-10 19:48 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Apr 06, 2017 at 05:40:35PM +0200, olivier moysan wrote:
> Add documentation of device tree bindings for STM32 SPI/I2S.
> 
> Signed-off-by: olivier moysan <olivier.moysan@st.com>
> ---
>  .../devicetree/bindings/sound/st,stm32h7-i2s.txt   | 71 ++++++++++++++++++++++
>  1 file changed, 71 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/sound/st,stm32h7-i2s.txt
> 
> diff --git a/Documentation/devicetree/bindings/sound/st,stm32h7-i2s.txt b/Documentation/devicetree/bindings/sound/st,stm32h7-i2s.txt
> new file mode 100644
> index 0000000..b99467a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/sound/st,stm32h7-i2s.txt
> @@ -0,0 +1,71 @@
> +STMicroelectronics STM32 SPI/I2S Controller
> +
> +The SPI/I2S block supports I2S/PCM protocols when configured on I2S mode.
> +Only some SPI instances support I2S.
> +
> +Required properties:
> +  - compatible: Must be "st,stm32h7-i2s"
> +  - #sound-dai-cells: Must be 1. (one parameter)
> +    This parameter allows to specify CPU DAI index in soundcard CPU dai link.
> +	index 0: playback DAI
> +	index 1: capture DAI
> +	index 2: full duplex DAI

Is this still needed for graph-card?

> +  - reg: Offset and length of the device's register set.
> +  - interrupts: Must contain the interrupt line id.
> +  - clocks: Must contain phandle and clock specifier pairs for each entry
> +	in clock-names.
> +  - clock-names: Must contain "i2sclk", "pclk", "x8k" and "x11k".
> +	"i2sclk": clock which feeds the internal clock generator
> +	"pclk": clock which feeds the peripheral bus interface
> +	"x8k": I2S parent clock for sampling rates multiple of 8kHz.
> +	"x11k": I2S parent clock for sampling rates multiple of 11.025kHz.
> +  - dmas: DMA specifiers for tx and rx dma.
> +    See Documentation/devicetree/bindings/dma/stm32-dma.txt.
> +  - dma-names: Identifier for each DMA request line. Must be "tx" and "rx".
> +  - pinctrl-names: should contain only value "default"
> +  - pinctrl-0: see Documentation/devicetree/bindings/pinctrl/pinctrl-stm32.txt
> +
> +Optional properties:
> +  - resets: Reference to a reset controller asserting the reset controller
> +
> +Example:
> +sound_card {
> +	compatible = "audio-graph-card";
> +	dais = <&i2s2_port 0>;
> +};
> +
> +i2s2: audio-controller at 40003800 {
> +	compatible = "st,stm32h7-i2s";
> +	#sound-dai-cells = <1>;
> +	reg = <0x40003800 0x400>;
> +	interrupts = <36>;
> +	clocks = <&rcc PCLK1>, <&rcc SPI2_CK>, <&rcc PLL1_Q>, <&rcc PLL2_P>;
> +	clock-names = "pclk", "i2sclk",  "x8k", "x11k";
> +	dmas = <&dmamux2 2 39 0x400 0x1>,
> +		       <&dmamux2 3 40 0x400 0x1>;
> +	dma-names = "rx", "tx";
> +	pinctrl-names = "default";
> +	pinctrl-0 = <&pinctrl_i2s2>;
> +
> +	ports {
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +
> +		i2s2_port: port at 0 {
> +			reg = <0>;
> +			cpu_endpoint: endpoint {
> +				remote-endpoint = <&codec_endpoint>;
> +				audio-graph-card,format = "i2s";
> +				audio-graph-card,bitclock-master = <&codec_endpoint>;
> +				audio-graph-card,frame-master = <&codec_endpoint>;

The 'audio-graph-card,' part has been dropped.

> +			};
> +		};
> +};
> +
> +audio-codec {
> +		codec_port: port {
> +			codec_endpoint: endpoint {
> +				remote-endpoint = <&cpu_endpoint>;
> +		};
> +	};
> +};
> -- 
> 1.9.1
> 

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

* Re: [PATCH 1/2] dt-bindings: Document STM32 I2S bindings
  2017-04-10 19:48     ` Rob Herring
@ 2017-04-11 14:32       ` Mark Brown
  -1 siblings, 0 replies; 32+ messages in thread
From: Mark Brown @ 2017-04-11 14:32 UTC (permalink / raw)
  To: Rob Herring
  Cc: olivier moysan, lgirdwood-Re5JQEeQqe8AvxtiuMwx3w,
	perex-/Fr2/VpizcU, tiwai-IBi9RG/b67k,
	mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w,
	alexandre.torgue-qxv4g6HH51o, alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw,
	mark.rutland-5wv7dgnIgG8, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	arnaud.pouliquen-qxv4g6HH51o, benjamin.gaignard-qxv4g6HH51o

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

On Mon, Apr 10, 2017 at 02:48:32PM -0500, Rob Herring wrote:
> On Thu, Apr 06, 2017 at 05:40:35PM +0200, olivier moysan wrote:

> > +Required properties:
> > +  - compatible: Must be "st,stm32h7-i2s"
> > +  - #sound-dai-cells: Must be 1. (one parameter)
> > +    This parameter allows to specify CPU DAI index in soundcard CPU dai link.
> > +	index 0: playback DAI
> > +	index 1: capture DAI
> > +	index 2: full duplex DAI

> Is this still needed for graph-card?

The graph card is blocked on your review...  I'm also not clear how
without something like this we'd be able to identify a specific DAI
within a device if we don't have a way of identifying them.

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

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

* [PATCH 1/2] dt-bindings: Document STM32 I2S bindings
@ 2017-04-11 14:32       ` Mark Brown
  0 siblings, 0 replies; 32+ messages in thread
From: Mark Brown @ 2017-04-11 14:32 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Apr 10, 2017 at 02:48:32PM -0500, Rob Herring wrote:
> On Thu, Apr 06, 2017 at 05:40:35PM +0200, olivier moysan wrote:

> > +Required properties:
> > +  - compatible: Must be "st,stm32h7-i2s"
> > +  - #sound-dai-cells: Must be 1. (one parameter)
> > +    This parameter allows to specify CPU DAI index in soundcard CPU dai link.
> > +	index 0: playback DAI
> > +	index 1: capture DAI
> > +	index 2: full duplex DAI

> Is this still needed for graph-card?

The graph card is blocked on your review...  I'm also not clear how
without something like this we'd be able to identify a specific DAI
within a device if we don't have a way of identifying them.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 488 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170411/493117dc/attachment.sig>

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

* Re: [PATCH 1/2] dt-bindings: Document STM32 I2S bindings
  2017-04-11 14:32       ` Mark Brown
@ 2017-04-11 15:44         ` Olivier MOYSAN
  -1 siblings, 0 replies; 32+ messages in thread
From: Olivier MOYSAN @ 2017-04-11 15:44 UTC (permalink / raw)
  To: Mark Brown, Rob Herring
  Cc: mark.rutland, devicetree, alsa-devel, Alexandre TORGUE,
	Arnaud POULIQUEN, tiwai, lgirdwood, mcoquelin.stm32,
	linux-arm-kernel, Benjamin GAIGNARD

Hello Rob, Mark

Thanks for your review.
Please find my comments below.

On 04/11/2017 04:32 PM, Mark Brown wrote:
> On Mon, Apr 10, 2017 at 02:48:32PM -0500, Rob Herring wrote:
>> On Thu, Apr 06, 2017 at 05:40:35PM +0200, olivier moysan wrote:
>
>>> +Required properties:
>>> +  - compatible: Must be "st,stm32h7-i2s"
>>> +  - #sound-dai-cells: Must be 1. (one parameter)
>>> +    This parameter allows to specify CPU DAI index in soundcard CPU dai link.
>>> +	index 0: playback DAI
>>> +	index 1: capture DAI
>>> +	index 2: full duplex DAI
>
>> Is this still needed for graph-card?
>
> The graph card is blocked on your review...  I'm also not clear how
> without something like this we'd be able to identify a specific DAI
> within a device if we don't have a way of identifying them.
>
Yes, it seems that audio graph card does not allow to select a specific 
DAI. This will probably be necessary.

However, regarding STM32 I2S driver, I'm wondering if selecting DAI
is the best way to configure interface as tx, rx or fd.
Maybe, it is more relevant to configure DAI according to DMA
configuration from I2S node.
This would moreover avoid to allocate 2 dmas channels when not
necessary (tx or rx only).
If you agree with this, I will implement this change in a v2.

BRs
olivier

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

* [PATCH 1/2] dt-bindings: Document STM32 I2S bindings
@ 2017-04-11 15:44         ` Olivier MOYSAN
  0 siblings, 0 replies; 32+ messages in thread
From: Olivier MOYSAN @ 2017-04-11 15:44 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Rob, Mark

Thanks for your review.
Please find my comments below.

On 04/11/2017 04:32 PM, Mark Brown wrote:
> On Mon, Apr 10, 2017 at 02:48:32PM -0500, Rob Herring wrote:
>> On Thu, Apr 06, 2017 at 05:40:35PM +0200, olivier moysan wrote:
>
>>> +Required properties:
>>> +  - compatible: Must be "st,stm32h7-i2s"
>>> +  - #sound-dai-cells: Must be 1. (one parameter)
>>> +    This parameter allows to specify CPU DAI index in soundcard CPU dai link.
>>> +	index 0: playback DAI
>>> +	index 1: capture DAI
>>> +	index 2: full duplex DAI
>
>> Is this still needed for graph-card?
>
> The graph card is blocked on your review...  I'm also not clear how
> without something like this we'd be able to identify a specific DAI
> within a device if we don't have a way of identifying them.
>
Yes, it seems that audio graph card does not allow to select a specific 
DAI. This will probably be necessary.

However, regarding STM32 I2S driver, I'm wondering if selecting DAI
is the best way to configure interface as tx, rx or fd.
Maybe, it is more relevant to configure DAI according to DMA
configuration from I2S node.
This would moreover avoid to allocate 2 dmas channels when not
necessary (tx or rx only).
If you agree with this, I will implement this change in a v2.

BRs
olivier

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

* Re: [PATCH 1/2] dt-bindings: Document STM32 I2S bindings
  2017-04-11 14:32       ` Mark Brown
@ 2017-04-11 16:02           ` Rob Herring
  -1 siblings, 0 replies; 32+ messages in thread
From: Rob Herring @ 2017-04-11 16:02 UTC (permalink / raw)
  To: Mark Brown
  Cc: olivier moysan, Liam Girdwood, Jaroslav Kysela, Takashi Iwai,
	Maxime Coquelin, Alexandre Torgue, Linux-ALSA, Mark Rutland,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Arnaud POULIQUEN, Benjamin Gaignard

On Tue, Apr 11, 2017 at 9:32 AM, Mark Brown <broonie-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
> On Mon, Apr 10, 2017 at 02:48:32PM -0500, Rob Herring wrote:
>> On Thu, Apr 06, 2017 at 05:40:35PM +0200, olivier moysan wrote:
>
>> > +Required properties:
>> > +  - compatible: Must be "st,stm32h7-i2s"
>> > +  - #sound-dai-cells: Must be 1. (one parameter)
>> > +    This parameter allows to specify CPU DAI index in soundcard CPU dai link.
>> > +   index 0: playback DAI
>> > +   index 1: capture DAI
>> > +   index 2: full duplex DAI
>
>> Is this still needed for graph-card?
>
> The graph card is blocked on your review...

Maybe if there were more reviewers it would move faster. I don't know
ASoC that well.

> I'm also not clear how
> without something like this we'd be able to identify a specific DAI
> within a device if we don't have a way of identifying them.

Isn't that what the graph does? "dais" points to a list of ports which
are the specific DAIs whether there are multiple ones in a single
device or multiple devices with a single DAI each.

Rob
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 1/2] dt-bindings: Document STM32 I2S bindings
@ 2017-04-11 16:02           ` Rob Herring
  0 siblings, 0 replies; 32+ messages in thread
From: Rob Herring @ 2017-04-11 16:02 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Apr 11, 2017 at 9:32 AM, Mark Brown <broonie@kernel.org> wrote:
> On Mon, Apr 10, 2017 at 02:48:32PM -0500, Rob Herring wrote:
>> On Thu, Apr 06, 2017 at 05:40:35PM +0200, olivier moysan wrote:
>
>> > +Required properties:
>> > +  - compatible: Must be "st,stm32h7-i2s"
>> > +  - #sound-dai-cells: Must be 1. (one parameter)
>> > +    This parameter allows to specify CPU DAI index in soundcard CPU dai link.
>> > +   index 0: playback DAI
>> > +   index 1: capture DAI
>> > +   index 2: full duplex DAI
>
>> Is this still needed for graph-card?
>
> The graph card is blocked on your review...

Maybe if there were more reviewers it would move faster. I don't know
ASoC that well.

> I'm also not clear how
> without something like this we'd be able to identify a specific DAI
> within a device if we don't have a way of identifying them.

Isn't that what the graph does? "dais" points to a list of ports which
are the specific DAIs whether there are multiple ones in a single
device or multiple devices with a single DAI each.

Rob

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

* Re: [PATCH 1/2] dt-bindings: Document STM32 I2S bindings
  2017-04-11 16:02           ` Rob Herring
@ 2017-04-11 20:19             ` Mark Brown
  -1 siblings, 0 replies; 32+ messages in thread
From: Mark Brown @ 2017-04-11 20:19 UTC (permalink / raw)
  To: Rob Herring
  Cc: Mark Rutland, devicetree, Linux-ALSA, olivier moysan,
	Alexandre Torgue, Arnaud POULIQUEN, Takashi Iwai, Liam Girdwood,
	Maxime Coquelin, linux-arm-kernel, Benjamin Gaignard


[-- Attachment #1.1: Type: text/plain, Size: 1535 bytes --]

On Tue, Apr 11, 2017 at 11:02:57AM -0500, Rob Herring wrote:
> On Tue, Apr 11, 2017 at 9:32 AM, Mark Brown <broonie@kernel.org> wrote:

> > The graph card is blocked on your review...

> Maybe if there were more reviewers it would move faster. I don't know
> ASoC that well.

The times I've looked at it recently it's been stuck in DT style issues
rather than anything substantially ASoC related, as far as I can tell
the binding is essentially empty from an ASoC point of view and
inherited from the of_graph binding.  There's bits in that are a bit
random like specifically listing the CPU DAIs and only them but that
just looks like one of these random DT things that's predetermined.  I
really can't see anything at all in there to review from an ASoC point
of view, I've applied the changes that don't seem blocked on the
binding.  If there's something you're looking for then please say...

We never seem to make any progress on the generic changes in drivers/of at
the start of the series either...

> > I'm also not clear how
> > without something like this we'd be able to identify a specific DAI
> > within a device if we don't have a way of identifying them.

> Isn't that what the graph does? "dais" points to a list of ports which
> are the specific DAIs whether there are multiple ones in a single
> device or multiple devices with a single DAI each.

But the ports can still have indexes AFAICT (the examples show port@0,
port@1 and so on) so we still need to define what those indexes mean
which is what this is doing?

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

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



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

* [PATCH 1/2] dt-bindings: Document STM32 I2S bindings
@ 2017-04-11 20:19             ` Mark Brown
  0 siblings, 0 replies; 32+ messages in thread
From: Mark Brown @ 2017-04-11 20:19 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Apr 11, 2017 at 11:02:57AM -0500, Rob Herring wrote:
> On Tue, Apr 11, 2017 at 9:32 AM, Mark Brown <broonie@kernel.org> wrote:

> > The graph card is blocked on your review...

> Maybe if there were more reviewers it would move faster. I don't know
> ASoC that well.

The times I've looked at it recently it's been stuck in DT style issues
rather than anything substantially ASoC related, as far as I can tell
the binding is essentially empty from an ASoC point of view and
inherited from the of_graph binding.  There's bits in that are a bit
random like specifically listing the CPU DAIs and only them but that
just looks like one of these random DT things that's predetermined.  I
really can't see anything at all in there to review from an ASoC point
of view, I've applied the changes that don't seem blocked on the
binding.  If there's something you're looking for then please say...

We never seem to make any progress on the generic changes in drivers/of at
the start of the series either...

> > I'm also not clear how
> > without something like this we'd be able to identify a specific DAI
> > within a device if we don't have a way of identifying them.

> Isn't that what the graph does? "dais" points to a list of ports which
> are the specific DAIs whether there are multiple ones in a single
> device or multiple devices with a single DAI each.

But the ports can still have indexes AFAICT (the examples show port at 0,
port at 1 and so on) so we still need to define what those indexes mean
which is what this is doing?
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 488 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170411/43cb8efb/attachment-0001.sig>

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

* Re: [PATCH 1/2] dt-bindings: Document STM32 I2S bindings
  2017-04-11 15:44         ` Olivier MOYSAN
@ 2017-04-11 21:10           ` Mark Brown
  -1 siblings, 0 replies; 32+ messages in thread
From: Mark Brown @ 2017-04-11 21:10 UTC (permalink / raw)
  To: Olivier MOYSAN
  Cc: mark.rutland, Rob Herring, alsa-devel, Alexandre TORGUE,
	devicetree, Arnaud POULIQUEN, tiwai, lgirdwood, mcoquelin.stm32,
	linux-arm-kernel, Benjamin GAIGNARD


[-- Attachment #1.1: Type: text/plain, Size: 853 bytes --]

On Tue, Apr 11, 2017 at 03:44:52PM +0000, Olivier MOYSAN wrote:

> However, regarding STM32 I2S driver, I'm wondering if selecting DAI
> is the best way to configure interface as tx, rx or fd.

Why do you even need to configure this?

> Maybe, it is more relevant to configure DAI according to DMA
> configuration from I2S node.
> This would moreover avoid to allocate 2 dmas channels when not
> necessary (tx or rx only).
> If you agree with this, I will implement this change in a v2.

That sounds wrong, I'd expect this wiring to be done statically as part
of the .dtsi for the SoC (or just grabbed as needed at runtime if
things are flexbile enough) rather than being a configuration thing done
per board...  I had thought that this was configuration reflecting
different ways of taping out the IP with different feature sets, is that
not the case?

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

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



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

* [PATCH 1/2] dt-bindings: Document STM32 I2S bindings
@ 2017-04-11 21:10           ` Mark Brown
  0 siblings, 0 replies; 32+ messages in thread
From: Mark Brown @ 2017-04-11 21:10 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Apr 11, 2017 at 03:44:52PM +0000, Olivier MOYSAN wrote:

> However, regarding STM32 I2S driver, I'm wondering if selecting DAI
> is the best way to configure interface as tx, rx or fd.

Why do you even need to configure this?

> Maybe, it is more relevant to configure DAI according to DMA
> configuration from I2S node.
> This would moreover avoid to allocate 2 dmas channels when not
> necessary (tx or rx only).
> If you agree with this, I will implement this change in a v2.

That sounds wrong, I'd expect this wiring to be done statically as part
of the .dtsi for the SoC (or just grabbed as needed at runtime if
things are flexbile enough) rather than being a configuration thing done
per board...  I had thought that this was configuration reflecting
different ways of taping out the IP with different feature sets, is that
not the case?
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 488 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170411/1dcedbfa/attachment.sig>

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

* Re: [PATCH 1/2] dt-bindings: Document STM32 I2S bindings
  2017-04-11 21:10           ` Mark Brown
@ 2017-04-12  8:30             ` Olivier MOYSAN
  -1 siblings, 0 replies; 32+ messages in thread
From: Olivier MOYSAN @ 2017-04-12  8:30 UTC (permalink / raw)
  To: Mark Brown
  Cc: mark.rutland, Rob Herring, alsa-devel, Alexandre TORGUE,
	devicetree, Arnaud POULIQUEN, tiwai, lgirdwood, mcoquelin.stm32,
	linux-arm-kernel, Benjamin GAIGNARD

Hello Mark,

On 04/11/2017 11:10 PM, Mark Brown wrote:
> On Tue, Apr 11, 2017 at 03:44:52PM +0000, Olivier MOYSAN wrote:
>
>> However, regarding STM32 I2S driver, I'm wondering if selecting DAI
>> is the best way to configure interface as tx, rx or fd.
>
> Why do you even need to configure this?
>

The IP provides two data wires, SD in and SD out. So it can be 
configured either as capture or playback only, or full-duplex.
This corresponds to a mode selection through a register configuration.

>> Maybe, it is more relevant to configure DAI according to DMA
>> configuration from I2S node.
>> This would moreover avoid to allocate 2 dmas channels when not
>> necessary (tx or rx only).
>> If you agree with this, I will implement this change in a v2.
>
> That sounds wrong, I'd expect this wiring to be done statically as part
> of the .dtsi for the SoC (or just grabbed as needed at runtime if
> things are flexbile enough) rather than being a configuration thing done
> per board...  I had thought that this was configuration reflecting
> different ways of taping out the IP with different feature sets, is that
> not the case?
>

This configuration is board dependent. The IP may be used as rx, tx or 
fd depending on board. So I think it can make sense to have a DMA 
configuration linked to board, and to set IP mode accordingly.

BRs
olivier

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

* [PATCH 1/2] dt-bindings: Document STM32 I2S bindings
@ 2017-04-12  8:30             ` Olivier MOYSAN
  0 siblings, 0 replies; 32+ messages in thread
From: Olivier MOYSAN @ 2017-04-12  8:30 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Mark,

On 04/11/2017 11:10 PM, Mark Brown wrote:
> On Tue, Apr 11, 2017 at 03:44:52PM +0000, Olivier MOYSAN wrote:
>
>> However, regarding STM32 I2S driver, I'm wondering if selecting DAI
>> is the best way to configure interface as tx, rx or fd.
>
> Why do you even need to configure this?
>

The IP provides two data wires, SD in and SD out. So it can be 
configured either as capture or playback only, or full-duplex.
This corresponds to a mode selection through a register configuration.

>> Maybe, it is more relevant to configure DAI according to DMA
>> configuration from I2S node.
>> This would moreover avoid to allocate 2 dmas channels when not
>> necessary (tx or rx only).
>> If you agree with this, I will implement this change in a v2.
>
> That sounds wrong, I'd expect this wiring to be done statically as part
> of the .dtsi for the SoC (or just grabbed as needed at runtime if
> things are flexbile enough) rather than being a configuration thing done
> per board...  I had thought that this was configuration reflecting
> different ways of taping out the IP with different feature sets, is that
> not the case?
>

This configuration is board dependent. The IP may be used as rx, tx or 
fd depending on board. So I think it can make sense to have a DMA 
configuration linked to board, and to set IP mode accordingly.

BRs
olivier

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

* Re: [PATCH 1/2] dt-bindings: Document STM32 I2S bindings
  2017-04-12  8:30             ` Olivier MOYSAN
@ 2017-04-12 11:32                 ` Mark Brown
  -1 siblings, 0 replies; 32+ messages in thread
From: Mark Brown @ 2017-04-12 11:32 UTC (permalink / raw)
  To: Olivier MOYSAN
  Cc: Rob Herring, lgirdwood-Re5JQEeQqe8AvxtiuMwx3w, perex-/Fr2/VpizcU,
	tiwai-IBi9RG/b67k, mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w,
	Alexandre TORGUE, alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw,
	mark.rutland-5wv7dgnIgG8, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Arnaud POULIQUEN, Benjamin GAIGNARD

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

On Wed, Apr 12, 2017 at 08:30:31AM +0000, Olivier MOYSAN wrote:
> On 04/11/2017 11:10 PM, Mark Brown wrote:

> > That sounds wrong, I'd expect this wiring to be done statically as part
> > of the .dtsi for the SoC (or just grabbed as needed at runtime if
> > things are flexbile enough) rather than being a configuration thing done
> > per board...  I had thought that this was configuration reflecting
> > different ways of taping out the IP with different feature sets, is that
> > not the case?

> This configuration is board dependent. The IP may be used as rx, tx or 
> fd depending on board. So I think it can make sense to have a DMA 
> configuration linked to board, and to set IP mode accordingly.

It is totally normal to just not use one direction in a given system, we
don't normally need to do anything special to handle things.  I'm a bit
confused as to what's different here and needs configuring?

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

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

* [PATCH 1/2] dt-bindings: Document STM32 I2S bindings
@ 2017-04-12 11:32                 ` Mark Brown
  0 siblings, 0 replies; 32+ messages in thread
From: Mark Brown @ 2017-04-12 11:32 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Apr 12, 2017 at 08:30:31AM +0000, Olivier MOYSAN wrote:
> On 04/11/2017 11:10 PM, Mark Brown wrote:

> > That sounds wrong, I'd expect this wiring to be done statically as part
> > of the .dtsi for the SoC (or just grabbed as needed at runtime if
> > things are flexbile enough) rather than being a configuration thing done
> > per board...  I had thought that this was configuration reflecting
> > different ways of taping out the IP with different feature sets, is that
> > not the case?

> This configuration is board dependent. The IP may be used as rx, tx or 
> fd depending on board. So I think it can make sense to have a DMA 
> configuration linked to board, and to set IP mode accordingly.

It is totally normal to just not use one direction in a given system, we
don't normally need to do anything special to handle things.  I'm a bit
confused as to what's different here and needs configuring?
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 488 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170412/7caea188/attachment.sig>

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

* Re: [PATCH 1/2] dt-bindings: Document STM32 I2S bindings
  2017-04-12 11:32                 ` Mark Brown
@ 2017-04-12 12:58                   ` Olivier MOYSAN
  -1 siblings, 0 replies; 32+ messages in thread
From: Olivier MOYSAN @ 2017-04-12 12:58 UTC (permalink / raw)
  To: Mark Brown
  Cc: mark.rutland, Rob Herring, alsa-devel, Alexandre TORGUE,
	devicetree, Arnaud POULIQUEN, tiwai, lgirdwood, mcoquelin.stm32,
	linux-arm-kernel, Benjamin GAIGNARD

Hello Mark,

On 04/12/2017 01:32 PM, Mark Brown wrote:
> On Wed, Apr 12, 2017 at 08:30:31AM +0000, Olivier MOYSAN wrote:
>> On 04/11/2017 11:10 PM, Mark Brown wrote:
>
>>> That sounds wrong, I'd expect this wiring to be done statically as part
>>> of the .dtsi for the SoC (or just grabbed as needed at runtime if
>>> things are flexbile enough) rather than being a configuration thing done
>>> per board...  I had thought that this was configuration reflecting
>>> different ways of taping out the IP with different feature sets, is that
>>> not the case?
>
>> This configuration is board dependent. The IP may be used as rx, tx or
>> fd depending on board. So I think it can make sense to have a DMA
>> configuration linked to board, and to set IP mode accordingly.
>
> It is totally normal to just not use one direction in a given system, we
> don't normally need to do anything special to handle things.  I'm a bit
> confused as to what's different here and needs configuring?
>

The IP does not provide an audio channel configured either as rx or tx.
I agree, that in such case, the cpu driver does not generally need
to worry about direction and there is nothing special required to handle it.

Here the IP provides 2 channels, 1 tx and 1 rx,  which may be active or 
not. The driver has to know which channel is used, and if both channels
have to be managed. The affected registers depend on selected channel. 
Moreover specific processing has to be performed if both channels are used.

BRs
olivier

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

* [PATCH 1/2] dt-bindings: Document STM32 I2S bindings
@ 2017-04-12 12:58                   ` Olivier MOYSAN
  0 siblings, 0 replies; 32+ messages in thread
From: Olivier MOYSAN @ 2017-04-12 12:58 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Mark,

On 04/12/2017 01:32 PM, Mark Brown wrote:
> On Wed, Apr 12, 2017 at 08:30:31AM +0000, Olivier MOYSAN wrote:
>> On 04/11/2017 11:10 PM, Mark Brown wrote:
>
>>> That sounds wrong, I'd expect this wiring to be done statically as part
>>> of the .dtsi for the SoC (or just grabbed as needed at runtime if
>>> things are flexbile enough) rather than being a configuration thing done
>>> per board...  I had thought that this was configuration reflecting
>>> different ways of taping out the IP with different feature sets, is that
>>> not the case?
>
>> This configuration is board dependent. The IP may be used as rx, tx or
>> fd depending on board. So I think it can make sense to have a DMA
>> configuration linked to board, and to set IP mode accordingly.
>
> It is totally normal to just not use one direction in a given system, we
> don't normally need to do anything special to handle things.  I'm a bit
> confused as to what's different here and needs configuring?
>

The IP does not provide an audio channel configured either as rx or tx.
I agree, that in such case, the cpu driver does not generally need
to worry about direction and there is nothing special required to handle it.

Here the IP provides 2 channels, 1 tx and 1 rx,  which may be active or 
not. The driver has to know which channel is used, and if both channels
have to be managed. The affected registers depend on selected channel. 
Moreover specific processing has to be performed if both channels are used.

BRs
olivier

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

* Re: [PATCH 1/2] dt-bindings: Document STM32 I2S bindings
  2017-04-12 12:58                   ` Olivier MOYSAN
@ 2017-04-12 15:38                       ` Mark Brown
  -1 siblings, 0 replies; 32+ messages in thread
From: Mark Brown @ 2017-04-12 15:38 UTC (permalink / raw)
  To: Olivier MOYSAN
  Cc: Rob Herring, lgirdwood-Re5JQEeQqe8AvxtiuMwx3w, perex-/Fr2/VpizcU,
	tiwai-IBi9RG/b67k, mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w,
	Alexandre TORGUE, alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw,
	mark.rutland-5wv7dgnIgG8, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Arnaud POULIQUEN, Benjamin GAIGNARD

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

On Wed, Apr 12, 2017 at 12:58:16PM +0000, Olivier MOYSAN wrote:
> On 04/12/2017 01:32 PM, Mark Brown wrote:

> > It is totally normal to just not use one direction in a given system, we
> > don't normally need to do anything special to handle things.  I'm a bit
> > confused as to what's different here and needs configuring?

> The IP does not provide an audio channel configured either as rx or tx.
> I agree, that in such case, the cpu driver does not generally need
> to worry about direction and there is nothing special required to handle it.

No, that's not what I'm saying - such hardware would be extremely
unusual.

> Here the IP provides 2 channels, 1 tx and 1 rx,  which may be active or 
> not. The driver has to know which channel is used, and if both channels
> have to be managed. The affected registers depend on selected channel. 

This sounds like essentially every audio controller out there.  The
overwhelming majority of controllers do exactly as you describe and have
both directions in the same IP, this really doesn't seem at all unusual.
Off the top of my head I can only think of one SoC family which combines
multiple IPs to do bidirectional audio (though I didn't check).

It really feels like there is something different here and I'm just
missing it.

> Moreover specific processing has to be performed if both channels are used.

Given that this case has to be supported anyway I'd be more inclined to
ask the question the other way around TBH.

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

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

* [PATCH 1/2] dt-bindings: Document STM32 I2S bindings
@ 2017-04-12 15:38                       ` Mark Brown
  0 siblings, 0 replies; 32+ messages in thread
From: Mark Brown @ 2017-04-12 15:38 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Apr 12, 2017 at 12:58:16PM +0000, Olivier MOYSAN wrote:
> On 04/12/2017 01:32 PM, Mark Brown wrote:

> > It is totally normal to just not use one direction in a given system, we
> > don't normally need to do anything special to handle things.  I'm a bit
> > confused as to what's different here and needs configuring?

> The IP does not provide an audio channel configured either as rx or tx.
> I agree, that in such case, the cpu driver does not generally need
> to worry about direction and there is nothing special required to handle it.

No, that's not what I'm saying - such hardware would be extremely
unusual.

> Here the IP provides 2 channels, 1 tx and 1 rx,  which may be active or 
> not. The driver has to know which channel is used, and if both channels
> have to be managed. The affected registers depend on selected channel. 

This sounds like essentially every audio controller out there.  The
overwhelming majority of controllers do exactly as you describe and have
both directions in the same IP, this really doesn't seem at all unusual.
Off the top of my head I can only think of one SoC family which combines
multiple IPs to do bidirectional audio (though I didn't check).

It really feels like there is something different here and I'm just
missing it.

> Moreover specific processing has to be performed if both channels are used.

Given that this case has to be supported anyway I'd be more inclined to
ask the question the other way around TBH.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 488 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170412/24a83019/attachment.sig>

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

* Re: [PATCH 1/2] dt-bindings: Document STM32 I2S bindings
  2017-04-12 15:38                       ` Mark Brown
@ 2017-04-13  8:01                         ` Olivier MOYSAN
  -1 siblings, 0 replies; 32+ messages in thread
From: Olivier MOYSAN @ 2017-04-13  8:01 UTC (permalink / raw)
  To: Mark Brown
  Cc: mark.rutland, Rob Herring, alsa-devel, Alexandre TORGUE,
	devicetree, Arnaud POULIQUEN, tiwai, lgirdwood, mcoquelin.stm32,
	perex, linux-arm-kernel, Benjamin GAIGNARD

Hello Mark,

On 04/12/2017 05:38 PM, Mark Brown wrote:
> On Wed, Apr 12, 2017 at 12:58:16PM +0000, Olivier MOYSAN wrote:
>> On 04/12/2017 01:32 PM, Mark Brown wrote:
>
>>> It is totally normal to just not use one direction in a given system, we
>>> don't normally need to do anything special to handle things.  I'm a bit
>>> confused as to what's different here and needs configuring?
>
>> The IP does not provide an audio channel configured either as rx or tx.
>> I agree, that in such case, the cpu driver does not generally need
>> to worry about direction and there is nothing special required to handle it.
>
> No, that's not what I'm saying - such hardware would be extremely
> unusual.
>
>> Here the IP provides 2 channels, 1 tx and 1 rx,  which may be active or
>> not. The driver has to know which channel is used, and if both channels
>> have to be managed. The affected registers depend on selected channel.
>
> This sounds like essentially every audio controller out there.  The
> overwhelming majority of controllers do exactly as you describe and have
> both directions in the same IP, this really doesn't seem at all unusual.
> Off the top of my head I can only think of one SoC family which combines
> multiple IPs to do bidirectional audio (though I didn't check).
>
> It really feels like there is something different here and I'm just
> missing it.
>

Yes, I think I need to give more details on design choices.

The IP provides 2 channels.
I can see mainly 3 solutions to link dais to these channels:

1) 2 static dais NOT exclusive
	- dai tx
	- dai rx
The IP exhibits a mode register, where you select mode TX, RX or FD.
There are 2 two options to manage this register.
option 1:
	start first channel with mode RX or TX
	when second channel is started, mode has to be changed to FD.
	Transfers have to be stopped before changing configuration
	registers, so this leads to cuts in audio stream.
option 2:
	start a first channel with mode FD.
	In this case, we may have unpredictable behavior for the stream
	which is not already started. probably underrun/overrun.
So, this solution rises problem for full-duplex management.

2) 3 static dais exclusive
	- dai tx
	- dai rx
	- dai rx-tx (fd)
This is the current implementation.
The choice of the dai is done at probe time. It is provided by DT 
through sound-dai parameter.
When dai fd is selected, after starting first stream, we assume that
second stream will be started. In this case we wait for second stream
to be available before enabling IP and starting transfers.

3) 1 dynamic dai
	- dai rx or tx or fd (according to dma conf in IP node)
Here the driver exposes only a single dai constructed from dma 
configuration provided by IP DT node.
This allows to get ride of sound-dai parameter.
This was my suggestion after Rob comments on DT bindings.

Hope this will help clarifying design constraints.

>> Moreover specific processing has to be performed if both channels are used.
>
> Given that this case has to be supported anyway I'd be more inclined to
> ask the question the other way around TBH.
>

Best regards
olivier

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

* [PATCH 1/2] dt-bindings: Document STM32 I2S bindings
@ 2017-04-13  8:01                         ` Olivier MOYSAN
  0 siblings, 0 replies; 32+ messages in thread
From: Olivier MOYSAN @ 2017-04-13  8:01 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Mark,

On 04/12/2017 05:38 PM, Mark Brown wrote:
> On Wed, Apr 12, 2017 at 12:58:16PM +0000, Olivier MOYSAN wrote:
>> On 04/12/2017 01:32 PM, Mark Brown wrote:
>
>>> It is totally normal to just not use one direction in a given system, we
>>> don't normally need to do anything special to handle things.  I'm a bit
>>> confused as to what's different here and needs configuring?
>
>> The IP does not provide an audio channel configured either as rx or tx.
>> I agree, that in such case, the cpu driver does not generally need
>> to worry about direction and there is nothing special required to handle it.
>
> No, that's not what I'm saying - such hardware would be extremely
> unusual.
>
>> Here the IP provides 2 channels, 1 tx and 1 rx,  which may be active or
>> not. The driver has to know which channel is used, and if both channels
>> have to be managed. The affected registers depend on selected channel.
>
> This sounds like essentially every audio controller out there.  The
> overwhelming majority of controllers do exactly as you describe and have
> both directions in the same IP, this really doesn't seem at all unusual.
> Off the top of my head I can only think of one SoC family which combines
> multiple IPs to do bidirectional audio (though I didn't check).
>
> It really feels like there is something different here and I'm just
> missing it.
>

Yes, I think I need to give more details on design choices.

The IP provides 2 channels.
I can see mainly 3 solutions to link dais to these channels:

1) 2 static dais NOT exclusive
	- dai tx
	- dai rx
The IP exhibits a mode register, where you select mode TX, RX or FD.
There are 2 two options to manage this register.
option 1:
	start first channel with mode RX or TX
	when second channel is started, mode has to be changed to FD.
	Transfers have to be stopped before changing configuration
	registers, so this leads to cuts in audio stream.
option 2:
	start a first channel with mode FD.
	In this case, we may have unpredictable behavior for the stream
	which is not already started. probably underrun/overrun.
So, this solution rises problem for full-duplex management.

2) 3 static dais exclusive
	- dai tx
	- dai rx
	- dai rx-tx (fd)
This is the current implementation.
The choice of the dai is done at probe time. It is provided by DT 
through sound-dai parameter.
When dai fd is selected, after starting first stream, we assume that
second stream will be started. In this case we wait for second stream
to be available before enabling IP and starting transfers.

3) 1 dynamic dai
	- dai rx or tx or fd (according to dma conf in IP node)
Here the driver exposes only a single dai constructed from dma 
configuration provided by IP DT node.
This allows to get ride of sound-dai parameter.
This was my suggestion after Rob comments on DT bindings.

Hope this will help clarifying design constraints.

>> Moreover specific processing has to be performed if both channels are used.
>
> Given that this case has to be supported anyway I'd be more inclined to
> ask the question the other way around TBH.
>

Best regards
olivier

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

* Re: [PATCH 1/2] dt-bindings: Document STM32 I2S bindings
  2017-04-13  8:01                         ` Olivier MOYSAN
@ 2017-04-26 16:15                           ` Mark Brown
  -1 siblings, 0 replies; 32+ messages in thread
From: Mark Brown @ 2017-04-26 16:15 UTC (permalink / raw)
  To: Olivier MOYSAN
  Cc: mark.rutland, Rob Herring, alsa-devel, Alexandre TORGUE,
	devicetree, Arnaud POULIQUEN, tiwai, lgirdwood, mcoquelin.stm32,
	linux-arm-kernel, Benjamin GAIGNARD


[-- Attachment #1.1: Type: text/plain, Size: 2168 bytes --]

On Thu, Apr 13, 2017 at 08:01:34AM +0000, Olivier MOYSAN wrote:

> 1) 2 static dais NOT exclusive
> 	- dai tx
> 	- dai rx
> The IP exhibits a mode register, where you select mode TX, RX or FD.
> There are 2 two options to manage this register.
> option 1:
> 	start first channel with mode RX or TX
> 	when second channel is started, mode has to be changed to FD.
> 	Transfers have to be stopped before changing configuration
> 	registers, so this leads to cuts in audio stream.
> option 2:
> 	start a first channel with mode FD.
> 	In this case, we may have unpredictable behavior for the stream
> 	which is not already started. probably underrun/overrun.
> So, this solution rises problem for full-duplex management.

> 2) 3 static dais exclusive
> 	- dai tx
> 	- dai rx
> 	- dai rx-tx (fd)
> This is the current implementation.
> The choice of the dai is done at probe time. It is provided by DT 
> through sound-dai parameter.
> When dai fd is selected, after starting first stream, we assume that
> second stream will be started. In this case we wait for second stream
> to be available before enabling IP and starting transfers.

> 3) 1 dynamic dai
> 	- dai rx or tx or fd (according to dma conf in IP node)
> Here the driver exposes only a single dai constructed from dma 
> configuration provided by IP DT node.
> This allows to get ride of sound-dai parameter.

None of these options reflect how normal I2S controllers present
themselves in DT.  To repeat, you should present a single bidirectional
DAI for the single physical bidirectional I2S controller that your
hardware has.

If it's not possible to figure out a way to make the controller support
simultaneous playback and record with the two started independently then
the driver should just return an error if userspace tries to start the
second direction up.  This will severely limit the utility of the driver
as Linux generally treats playback and record independently but that's
going to apply just as much with any of the options involving multiple
DAIs or configuration in DT.  You might be able to do something with
feeding it dummy data I guess?

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

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



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

* [PATCH 1/2] dt-bindings: Document STM32 I2S bindings
@ 2017-04-26 16:15                           ` Mark Brown
  0 siblings, 0 replies; 32+ messages in thread
From: Mark Brown @ 2017-04-26 16:15 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Apr 13, 2017 at 08:01:34AM +0000, Olivier MOYSAN wrote:

> 1) 2 static dais NOT exclusive
> 	- dai tx
> 	- dai rx
> The IP exhibits a mode register, where you select mode TX, RX or FD.
> There are 2 two options to manage this register.
> option 1:
> 	start first channel with mode RX or TX
> 	when second channel is started, mode has to be changed to FD.
> 	Transfers have to be stopped before changing configuration
> 	registers, so this leads to cuts in audio stream.
> option 2:
> 	start a first channel with mode FD.
> 	In this case, we may have unpredictable behavior for the stream
> 	which is not already started. probably underrun/overrun.
> So, this solution rises problem for full-duplex management.

> 2) 3 static dais exclusive
> 	- dai tx
> 	- dai rx
> 	- dai rx-tx (fd)
> This is the current implementation.
> The choice of the dai is done at probe time. It is provided by DT 
> through sound-dai parameter.
> When dai fd is selected, after starting first stream, we assume that
> second stream will be started. In this case we wait for second stream
> to be available before enabling IP and starting transfers.

> 3) 1 dynamic dai
> 	- dai rx or tx or fd (according to dma conf in IP node)
> Here the driver exposes only a single dai constructed from dma 
> configuration provided by IP DT node.
> This allows to get ride of sound-dai parameter.

None of these options reflect how normal I2S controllers present
themselves in DT.  To repeat, you should present a single bidirectional
DAI for the single physical bidirectional I2S controller that your
hardware has.

If it's not possible to figure out a way to make the controller support
simultaneous playback and record with the two started independently then
the driver should just return an error if userspace tries to start the
second direction up.  This will severely limit the utility of the driver
as Linux generally treats playback and record independently but that's
going to apply just as much with any of the options involving multiple
DAIs or configuration in DT.  You might be able to do something with
feeding it dummy data I guess?
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 488 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170426/48459d98/attachment-0001.sig>

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

* Re: [PATCH 1/2] dt-bindings: Document STM32 I2S bindings
  2017-04-26 16:15                           ` Mark Brown
@ 2017-04-27 15:57                             ` Olivier MOYSAN
  -1 siblings, 0 replies; 32+ messages in thread
From: Olivier MOYSAN @ 2017-04-27 15:57 UTC (permalink / raw)
  To: Mark Brown
  Cc: mark.rutland, Rob Herring, alsa-devel, Alexandre TORGUE,
	devicetree, Arnaud POULIQUEN, tiwai, lgirdwood, mcoquelin.stm32,
	linux-arm-kernel, Benjamin GAIGNARD

Hello Mark,

On 04/26/2017 06:15 PM, Mark Brown wrote:
> On Thu, Apr 13, 2017 at 08:01:34AM +0000, Olivier MOYSAN wrote:
>
>> 1) 2 static dais NOT exclusive
>> 	- dai tx
>> 	- dai rx
>> The IP exhibits a mode register, where you select mode TX, RX or FD.
>> There are 2 two options to manage this register.
>> option 1:
>> 	start first channel with mode RX or TX
>> 	when second channel is started, mode has to be changed to FD.
>> 	Transfers have to be stopped before changing configuration
>> 	registers, so this leads to cuts in audio stream.
>> option 2:
>> 	start a first channel with mode FD.
>> 	In this case, we may have unpredictable behavior for the stream
>> 	which is not already started. probably underrun/overrun.
>> So, this solution rises problem for full-duplex management.
>
>> 2) 3 static dais exclusive
>> 	- dai tx
>> 	- dai rx
>> 	- dai rx-tx (fd)
>> This is the current implementation.
>> The choice of the dai is done at probe time. It is provided by DT
>> through sound-dai parameter.
>> When dai fd is selected, after starting first stream, we assume that
>> second stream will be started. In this case we wait for second stream
>> to be available before enabling IP and starting transfers.
>
>> 3) 1 dynamic dai
>> 	- dai rx or tx or fd (according to dma conf in IP node)
>> Here the driver exposes only a single dai constructed from dma
>> configuration provided by IP DT node.
>> This allows to get ride of sound-dai parameter.
>
> None of these options reflect how normal I2S controllers present
> themselves in DT.  To repeat, you should present a single bidirectional
> DAI for the single physical bidirectional I2S controller that your
> hardware has.
>
> If it's not possible to figure out a way to make the controller support
> simultaneous playback and record with the two started independently then
> the driver should just return an error if userspace tries to start the
> second direction up.  This will severely limit the utility of the driver
> as Linux generally treats playback and record independently but that's
> going to apply just as much with any of the options involving multiple
> DAIs or configuration in DT.  You might be able to do something with
> feeding it dummy data I guess?
>

Ok. I will implement a single bidirectional DAI in v2.

Best regards
Olivier

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

* [PATCH 1/2] dt-bindings: Document STM32 I2S bindings
@ 2017-04-27 15:57                             ` Olivier MOYSAN
  0 siblings, 0 replies; 32+ messages in thread
From: Olivier MOYSAN @ 2017-04-27 15:57 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Mark,

On 04/26/2017 06:15 PM, Mark Brown wrote:
> On Thu, Apr 13, 2017 at 08:01:34AM +0000, Olivier MOYSAN wrote:
>
>> 1) 2 static dais NOT exclusive
>> 	- dai tx
>> 	- dai rx
>> The IP exhibits a mode register, where you select mode TX, RX or FD.
>> There are 2 two options to manage this register.
>> option 1:
>> 	start first channel with mode RX or TX
>> 	when second channel is started, mode has to be changed to FD.
>> 	Transfers have to be stopped before changing configuration
>> 	registers, so this leads to cuts in audio stream.
>> option 2:
>> 	start a first channel with mode FD.
>> 	In this case, we may have unpredictable behavior for the stream
>> 	which is not already started. probably underrun/overrun.
>> So, this solution rises problem for full-duplex management.
>
>> 2) 3 static dais exclusive
>> 	- dai tx
>> 	- dai rx
>> 	- dai rx-tx (fd)
>> This is the current implementation.
>> The choice of the dai is done at probe time. It is provided by DT
>> through sound-dai parameter.
>> When dai fd is selected, after starting first stream, we assume that
>> second stream will be started. In this case we wait for second stream
>> to be available before enabling IP and starting transfers.
>
>> 3) 1 dynamic dai
>> 	- dai rx or tx or fd (according to dma conf in IP node)
>> Here the driver exposes only a single dai constructed from dma
>> configuration provided by IP DT node.
>> This allows to get ride of sound-dai parameter.
>
> None of these options reflect how normal I2S controllers present
> themselves in DT.  To repeat, you should present a single bidirectional
> DAI for the single physical bidirectional I2S controller that your
> hardware has.
>
> If it's not possible to figure out a way to make the controller support
> simultaneous playback and record with the two started independently then
> the driver should just return an error if userspace tries to start the
> second direction up.  This will severely limit the utility of the driver
> as Linux generally treats playback and record independently but that's
> going to apply just as much with any of the options involving multiple
> DAIs or configuration in DT.  You might be able to do something with
> feeding it dummy data I guess?
>

Ok. I will implement a single bidirectional DAI in v2.

Best regards
Olivier

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

end of thread, other threads:[~2017-04-27 15:57 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-04-06 15:40 [PATCH 0/2] ASoC: stm32: Add I2S driver olivier moysan
2017-04-06 15:40 ` olivier moysan
2017-04-06 15:40 ` [PATCH 1/2] dt-bindings: Document STM32 I2S bindings olivier moysan
2017-04-06 15:40   ` olivier moysan
2017-04-10 19:48   ` Rob Herring
2017-04-10 19:48     ` Rob Herring
2017-04-11 14:32     ` Mark Brown
2017-04-11 14:32       ` Mark Brown
2017-04-11 15:44       ` Olivier MOYSAN
2017-04-11 15:44         ` Olivier MOYSAN
2017-04-11 21:10         ` Mark Brown
2017-04-11 21:10           ` Mark Brown
2017-04-12  8:30           ` Olivier MOYSAN
2017-04-12  8:30             ` Olivier MOYSAN
     [not found]             ` <86a179ad-7961-18f7-8050-25e0e584c77d-qxv4g6HH51o@public.gmane.org>
2017-04-12 11:32               ` Mark Brown
2017-04-12 11:32                 ` Mark Brown
2017-04-12 12:58                 ` Olivier MOYSAN
2017-04-12 12:58                   ` Olivier MOYSAN
     [not found]                   ` <d886a9c7-ca46-f37a-0e82-6ed8d7a4e052-qxv4g6HH51o@public.gmane.org>
2017-04-12 15:38                     ` Mark Brown
2017-04-12 15:38                       ` Mark Brown
2017-04-13  8:01                       ` Olivier MOYSAN
2017-04-13  8:01                         ` Olivier MOYSAN
2017-04-26 16:15                         ` Mark Brown
2017-04-26 16:15                           ` Mark Brown
2017-04-27 15:57                           ` Olivier MOYSAN
2017-04-27 15:57                             ` Olivier MOYSAN
     [not found]       ` <20170411143250.3r7ys3rjshtxgaee-GFdadSzt00ze9xe1eoZjHA@public.gmane.org>
2017-04-11 16:02         ` Rob Herring
2017-04-11 16:02           ` Rob Herring
2017-04-11 20:19           ` Mark Brown
2017-04-11 20:19             ` Mark Brown
     [not found] ` <1491493236-2574-1-git-send-email-olivier.moysan-qxv4g6HH51o@public.gmane.org>
2017-04-06 15:40   ` [PATCH 2/2] ASoC: stm32: Add I2S driver olivier moysan
2017-04-06 15:40     ` olivier moysan

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