All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/7] Add STM32 DFSDM support
@ 2017-01-23 16:32 ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-23 16:32 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Lee Jones, Rob Herring,
	Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre Torgue, arnaud.pouliquen-qxv4g6HH51o

This patch-set handles the Digital Filter for Sigma Delta modulators IP on STM32 platforms.
DFSDM IP allows PDM microphone capture and sigma delta ADC conversion.

Those two features are mixed into the registers of the same hardware block which lead to introduce a multifunction 
driver on the top of them to be able to share the registers.

for This, MFD exports an API based on  get/configure/start/stop/release mechanisms.

Two kind of resources are handled by the MFD driver.
- The filters: 
	- 4 instances available for L4 and H7
        - composed with a SInC filter and an integrator 
- The transceivers or channels
	- 8 instances available for L4 and H7
        - can be connected to
        	serial transceivers (SPI or Manchester mode)
                internal parallel transceivers (from memory or internal ADC)
        -  associated  monitoring: analog watchdog, short circuit detector, extreme detector  
           (monitoring support will be part of a specific patch-set)) 

Principle OF the DFSDM IP is to connect one or several channels to a filter to process Sigma delta ADC or PDM microphone data.

Two types of child devices can de declared:
IIO for the management of Sigma Delta ADC conversion.
ASoC for PDM microphone audio capture.

For details on IP and use case examples please refer to this document:
http://www.st.com/content/ccc/resource/training/technical/product_training/96/b6/2b/ea/72/3f/4e/d5/STM32L4_System_DFSDM.pdf/files/STM32L4_System_DFSDM.pdf/jcr:content/translations/en.STM32L4_System_DFSDM.pdf

Remark:
    For audio part a patch is proposed to update core part to add copy support in soc_dmaengine_pcm.
    Rational is that output data register in DFSDM contains audio sample on 24 MSB + channel ID on the 8 LSB.
    Proposal is to allow to register a copy ops in soc_dmaengine_pcm to be able to perform post-processing.
    Back up if not accepted can be to create a pcm_engine in stm32 driver.
 
Not part of this patch-set, but should come as add-on patches:
- IIO management of Analog watchdog, short-circuit detection and clock absence detector, with associated IRQs management.
- IIO trigger and buffer management.  

Arnaud Pouliquen (6):
  MFD: add bindings for STM32 DFSDM driver
  MFD: add STM32 DFSDM support
  IIO: add bindings for STM32 DFSDM ADC driver
  IIO: add STM32 DFSDM ADC support
  ASoC: add bindings for STM32 DFSDM driver
  ASoC: add STM32 DFSDM support

olivier moysan (1):
  ASoC: dmaengine_pcm: add copy support

 .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        |   60 ++
 .../devicetree/bindings/mfd/stm32-dfsdm.txt        |   68 ++
 .../devicetree/bindings/sound/st,sm32-adfsdm.txt   |   84 ++
 drivers/iio/adc/Kconfig                            |    9 +
 drivers/iio/adc/Makefile                           |    1 +
 drivers/iio/adc/stm32-dfsdm-adc.c                  |  676 +++++++++++++
 drivers/mfd/Kconfig                                |   11 +
 drivers/mfd/Makefile                               |    2 +
 drivers/mfd/stm32-dfsdm-reg.h                      |  220 +++++
 drivers/mfd/stm32-dfsdm.c                          | 1044 ++++++++++++++++++++
 include/linux/mfd/stm32-dfsdm.h                    |  324 ++++++
 include/sound/dmaengine_pcm.h                      |    3 +
 sound/soc/Kconfig                                  |    1 +
 sound/soc/Makefile                                 |    1 +
 sound/soc/soc-generic-dmaengine-pcm.c              |   37 +-
 sound/soc/stm/Kconfig                              |   10 +
 sound/soc/stm/Makefile                             |    2 +
 sound/soc/stm/stm32_adfsdm.c                       |  686 +++++++++++++
 18 files changed, 3237 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
 create mode 100644 Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt
 create mode 100644 Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
 create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c
 create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
 create mode 100644 drivers/mfd/stm32-dfsdm.c
 create mode 100644 include/linux/mfd/stm32-dfsdm.h
 create mode 100644 sound/soc/stm/Kconfig
 create mode 100644 sound/soc/stm/Makefile
 create mode 100644 sound/soc/stm/stm32_adfsdm.c

-- 
1.9.1

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

* [PATCH 0/7] Add STM32 DFSDM support
@ 2017-01-23 16:32 ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-23 16:32 UTC (permalink / raw)
  To: devicetree, linux-arm-kernel, linux-iio, alsa-devel, Lee Jones,
	Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre Torgue, arnaud.pouliquen

This patch-set handles the Digital Filter for Sigma Delta modulators IP on STM32 platforms.
DFSDM IP allows PDM microphone capture and sigma delta ADC conversion.

Those two features are mixed into the registers of the same hardware block which lead to introduce a multifunction 
driver on the top of them to be able to share the registers.

for This, MFD exports an API based on  get/configure/start/stop/release mechanisms.

Two kind of resources are handled by the MFD driver.
- The filters: 
	- 4 instances available for L4 and H7
        - composed with a SInC filter and an integrator 
- The transceivers or channels
	- 8 instances available for L4 and H7
        - can be connected to
        	serial transceivers (SPI or Manchester mode)
                internal parallel transceivers (from memory or internal ADC)
        -  associated  monitoring: analog watchdog, short circuit detector, extreme detector  
           (monitoring support will be part of a specific patch-set)) 

Principle OF the DFSDM IP is to connect one or several channels to a filter to process Sigma delta ADC or PDM microphone data.

Two types of child devices can de declared:
IIO for the management of Sigma Delta ADC conversion.
ASoC for PDM microphone audio capture.

For details on IP and use case examples please refer to this document:
http://www.st.com/content/ccc/resource/training/technical/product_training/96/b6/2b/ea/72/3f/4e/d5/STM32L4_System_DFSDM.pdf/files/STM32L4_System_DFSDM.pdf/jcr:content/translations/en.STM32L4_System_DFSDM.pdf

Remark:
    For audio part a patch is proposed to update core part to add copy support in soc_dmaengine_pcm.
    Rational is that output data register in DFSDM contains audio sample on 24 MSB + channel ID on the 8 LSB.
    Proposal is to allow to register a copy ops in soc_dmaengine_pcm to be able to perform post-processing.
    Back up if not accepted can be to create a pcm_engine in stm32 driver.
 
Not part of this patch-set, but should come as add-on patches:
- IIO management of Analog watchdog, short-circuit detection and clock absence detector, with associated IRQs management.
- IIO trigger and buffer management.  

Arnaud Pouliquen (6):
  MFD: add bindings for STM32 DFSDM driver
  MFD: add STM32 DFSDM support
  IIO: add bindings for STM32 DFSDM ADC driver
  IIO: add STM32 DFSDM ADC support
  ASoC: add bindings for STM32 DFSDM driver
  ASoC: add STM32 DFSDM support

olivier moysan (1):
  ASoC: dmaengine_pcm: add copy support

 .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        |   60 ++
 .../devicetree/bindings/mfd/stm32-dfsdm.txt        |   68 ++
 .../devicetree/bindings/sound/st,sm32-adfsdm.txt   |   84 ++
 drivers/iio/adc/Kconfig                            |    9 +
 drivers/iio/adc/Makefile                           |    1 +
 drivers/iio/adc/stm32-dfsdm-adc.c                  |  676 +++++++++++++
 drivers/mfd/Kconfig                                |   11 +
 drivers/mfd/Makefile                               |    2 +
 drivers/mfd/stm32-dfsdm-reg.h                      |  220 +++++
 drivers/mfd/stm32-dfsdm.c                          | 1044 ++++++++++++++++++++
 include/linux/mfd/stm32-dfsdm.h                    |  324 ++++++
 include/sound/dmaengine_pcm.h                      |    3 +
 sound/soc/Kconfig                                  |    1 +
 sound/soc/Makefile                                 |    1 +
 sound/soc/soc-generic-dmaengine-pcm.c              |   37 +-
 sound/soc/stm/Kconfig                              |   10 +
 sound/soc/stm/Makefile                             |    2 +
 sound/soc/stm/stm32_adfsdm.c                       |  686 +++++++++++++
 18 files changed, 3237 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
 create mode 100644 Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt
 create mode 100644 Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
 create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c
 create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
 create mode 100644 drivers/mfd/stm32-dfsdm.c
 create mode 100644 include/linux/mfd/stm32-dfsdm.h
 create mode 100644 sound/soc/stm/Kconfig
 create mode 100644 sound/soc/stm/Makefile
 create mode 100644 sound/soc/stm/stm32_adfsdm.c

-- 
1.9.1


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

* [PATCH 0/7] Add STM32 DFSDM support
@ 2017-01-23 16:32 ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-23 16:32 UTC (permalink / raw)
  To: linux-arm-kernel

This patch-set handles the Digital Filter for Sigma Delta modulators IP on STM32 platforms.
DFSDM IP allows PDM microphone capture and sigma delta ADC conversion.

Those two features are mixed into the registers of the same hardware block which lead to introduce a multifunction 
driver on the top of them to be able to share the registers.

for This, MFD exports an API based on  get/configure/start/stop/release mechanisms.

Two kind of resources are handled by the MFD driver.
- The filters: 
	- 4 instances available for L4 and H7
        - composed with a SInC filter and an integrator 
- The transceivers or channels
	- 8 instances available for L4 and H7
        - can be connected to
        	serial transceivers (SPI or Manchester mode)
                internal parallel transceivers (from memory or internal ADC)
        -  associated  monitoring: analog watchdog, short circuit detector, extreme detector  
           (monitoring support will be part of a specific patch-set)) 

Principle OF the DFSDM IP is to connect one or several channels to a filter to process Sigma delta ADC or PDM microphone data.

Two types of child devices can de declared:
IIO for the management of Sigma Delta ADC conversion.
ASoC for PDM microphone audio capture.

For details on IP and use case examples please refer to this document:
http://www.st.com/content/ccc/resource/training/technical/product_training/96/b6/2b/ea/72/3f/4e/d5/STM32L4_System_DFSDM.pdf/files/STM32L4_System_DFSDM.pdf/jcr:content/translations/en.STM32L4_System_DFSDM.pdf

Remark:
    For audio part a patch is proposed to update core part to add copy support in soc_dmaengine_pcm.
    Rational is that output data register in DFSDM contains audio sample on 24 MSB + channel ID on the 8 LSB.
    Proposal is to allow to register a copy ops in soc_dmaengine_pcm to be able to perform post-processing.
    Back up if not accepted can be to create a pcm_engine in stm32 driver.
 
Not part of this patch-set, but should come as add-on patches:
- IIO management of Analog watchdog, short-circuit detection and clock absence detector, with associated IRQs management.
- IIO trigger and buffer management.  

Arnaud Pouliquen (6):
  MFD: add bindings for STM32 DFSDM driver
  MFD: add STM32 DFSDM support
  IIO: add bindings for STM32 DFSDM ADC driver
  IIO: add STM32 DFSDM ADC support
  ASoC: add bindings for STM32 DFSDM driver
  ASoC: add STM32 DFSDM support

olivier moysan (1):
  ASoC: dmaengine_pcm: add copy support

 .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        |   60 ++
 .../devicetree/bindings/mfd/stm32-dfsdm.txt        |   68 ++
 .../devicetree/bindings/sound/st,sm32-adfsdm.txt   |   84 ++
 drivers/iio/adc/Kconfig                            |    9 +
 drivers/iio/adc/Makefile                           |    1 +
 drivers/iio/adc/stm32-dfsdm-adc.c                  |  676 +++++++++++++
 drivers/mfd/Kconfig                                |   11 +
 drivers/mfd/Makefile                               |    2 +
 drivers/mfd/stm32-dfsdm-reg.h                      |  220 +++++
 drivers/mfd/stm32-dfsdm.c                          | 1044 ++++++++++++++++++++
 include/linux/mfd/stm32-dfsdm.h                    |  324 ++++++
 include/sound/dmaengine_pcm.h                      |    3 +
 sound/soc/Kconfig                                  |    1 +
 sound/soc/Makefile                                 |    1 +
 sound/soc/soc-generic-dmaengine-pcm.c              |   37 +-
 sound/soc/stm/Kconfig                              |   10 +
 sound/soc/stm/Makefile                             |    2 +
 sound/soc/stm/stm32_adfsdm.c                       |  686 +++++++++++++
 18 files changed, 3237 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
 create mode 100644 Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt
 create mode 100644 Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
 create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c
 create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
 create mode 100644 drivers/mfd/stm32-dfsdm.c
 create mode 100644 include/linux/mfd/stm32-dfsdm.h
 create mode 100644 sound/soc/stm/Kconfig
 create mode 100644 sound/soc/stm/Makefile
 create mode 100644 sound/soc/stm/stm32_adfsdm.c

-- 
1.9.1

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

* [PATCH 1/7] MFD: add bindings for STM32 DFSDM driver
  2017-01-23 16:32 ` Arnaud Pouliquen
  (?)
@ 2017-01-23 16:32   ` Arnaud Pouliquen
  -1 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-23 16:32 UTC (permalink / raw)
  To: devicetree, linux-arm-kernel, linux-iio, alsa-devel, Lee Jones,
	Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: arnaud.pouliquen, Alexandre Torgue, Maxime Coquelin

Add bindings information for STM32 Digital Filter for Sigma Delta modulators MFD driver

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 .../devicetree/bindings/mfd/stm32-dfsdm.txt        | 68 ++++++++++++++++++++++
 1 file changed, 68 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt

diff --git a/Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt b/Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt
new file mode 100644
index 0000000..e0b45ee
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt
@@ -0,0 +1,68 @@
+STMicroelectronics STM32 Digital Filter for Sigma Delta Modulator (DFSDM)
+ulti-function device.
+
+The STM32 DFSDM device is a multifunction device that handles the DFSDM IP.
+
+The DFSDM IP allows to add processing on Sigma Delta ADC based on SinC filters.
+For this, a pool of m filters can be connected to a pool of n channels.
+For STM32H7 : m = 4, n = 8.
+
+Each channel n is assigned to the SPI or Manchester interface n or n + 1.
+Channels 0 to 2 can also be connected to ADC IP instance 1 to 3.
+Filtering result is stored in a left aligned register, with 8 LSB reserved for
+the input channel ID.
+
+Each filter instance supports two contexts to manage conversions, each one has
+its own configurable sequence and trigger:
+- regular conversion: used for single or continuous conversion.
+- injected conversions: used for triggered conversion.
+
+Interfaces supported:
+- sigma delta ADCs trough IIO framework.
+- PDM microphones through ASoC framework.
+
+Required properties:
+- compatible:	Must be "st,stm32h7-dfsdm".
+- reg:		Specifies the DFSDM block register address and length.
+- interrupts:	IRQ lines connected to each DFSDM filter instance.
+- clocks:	IP and serial interfaces clocking. Should be set according
+		to rcc clock ID and "clock-names".
+- clock-names:	Input clock name "dfsdm_clk" must be defined,
+		"audio_clk" is optional. If defined CLKOUT is based on the audio
+		clock, else "dfsdm_clk" is used.
+
+Optional properties:
+- st,clkout-freq: clkout clock frequency (Hz).This clock must be set according
+		  to "clock" property. Frequency must be a multiple of the rcc
+		  clock frequency. If not, clkout frequency will not be
+		  accurate.
+- pinctrl-names:  set to "default".
+- pinctrl-0:	  List of phandles pointing to pin configuration nodes for DFSDM
+		  module.
+		  For Pinctrl properties see ../pinctrl/pinctrl-bindings.txt
+Example :
+	dfsdm: dfsdm@4400D000 {
+		compatible = "st,stm32h7-dfsdm";
+		reg = <0x40017000 0x400>;
+		interrupts = <110>, <111>, <112>, <113>;
+		clocks = <&timer_clk>;
+		clock-names = "dfsdm_clk";
+		pinctrl-0 = <&dfsdm_ch0 &dfsdm_ch1>;
+		pinctrl-names = "default";
+		st,clkout-freq = <2480000>;
+
+		iio_dfsdm0: iio-dfsdm@0 {
+			compatible = "st,stm32-dfsdm-adc";
+			#io-channel-cells = <1>;
+			reg = <0>;
+			status = "disabled";
+		};
+		dai_dfsdm0: dfsdm-audio@0 {
+			compatible = "st,stm32-dfsdm-audio";
+			#sound-dai-cells = <0>;
+			reg = <0>;
+			dmas = <&dmamux1 101 0x400 0x00>;
+			dma-names = "rx";
+			status = "disabled";
+		};
+	};
-- 
1.9.1

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

* [PATCH 1/7] MFD: add bindings for STM32 DFSDM driver
@ 2017-01-23 16:32   ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-23 16:32 UTC (permalink / raw)
  To: devicetree, linux-arm-kernel, linux-iio, alsa-devel, Lee Jones,
	Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre Torgue, arnaud.pouliquen

Add bindings information for STM32 Digital Filter for Sigma Delta modulators MFD driver

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 .../devicetree/bindings/mfd/stm32-dfsdm.txt        | 68 ++++++++++++++++++++++
 1 file changed, 68 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt

diff --git a/Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt b/Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt
new file mode 100644
index 0000000..e0b45ee
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt
@@ -0,0 +1,68 @@
+STMicroelectronics STM32 Digital Filter for Sigma Delta Modulator (DFSDM)
+ulti-function device.
+
+The STM32 DFSDM device is a multifunction device that handles the DFSDM IP.
+
+The DFSDM IP allows to add processing on Sigma Delta ADC based on SinC filters.
+For this, a pool of m filters can be connected to a pool of n channels.
+For STM32H7 : m = 4, n = 8.
+
+Each channel n is assigned to the SPI or Manchester interface n or n + 1.
+Channels 0 to 2 can also be connected to ADC IP instance 1 to 3.
+Filtering result is stored in a left aligned register, with 8 LSB reserved for
+the input channel ID.
+
+Each filter instance supports two contexts to manage conversions, each one has
+its own configurable sequence and trigger:
+- regular conversion: used for single or continuous conversion.
+- injected conversions: used for triggered conversion.
+
+Interfaces supported:
+- sigma delta ADCs trough IIO framework.
+- PDM microphones through ASoC framework.
+
+Required properties:
+- compatible:	Must be "st,stm32h7-dfsdm".
+- reg:		Specifies the DFSDM block register address and length.
+- interrupts:	IRQ lines connected to each DFSDM filter instance.
+- clocks:	IP and serial interfaces clocking. Should be set according
+		to rcc clock ID and "clock-names".
+- clock-names:	Input clock name "dfsdm_clk" must be defined,
+		"audio_clk" is optional. If defined CLKOUT is based on the audio
+		clock, else "dfsdm_clk" is used.
+
+Optional properties:
+- st,clkout-freq: clkout clock frequency (Hz).This clock must be set according
+		  to "clock" property. Frequency must be a multiple of the rcc
+		  clock frequency. If not, clkout frequency will not be
+		  accurate.
+- pinctrl-names:  set to "default".
+- pinctrl-0:	  List of phandles pointing to pin configuration nodes for DFSDM
+		  module.
+		  For Pinctrl properties see ../pinctrl/pinctrl-bindings.txt
+Example :
+	dfsdm: dfsdm@4400D000 {
+		compatible = "st,stm32h7-dfsdm";
+		reg = <0x40017000 0x400>;
+		interrupts = <110>, <111>, <112>, <113>;
+		clocks = <&timer_clk>;
+		clock-names = "dfsdm_clk";
+		pinctrl-0 = <&dfsdm_ch0 &dfsdm_ch1>;
+		pinctrl-names = "default";
+		st,clkout-freq = <2480000>;
+
+		iio_dfsdm0: iio-dfsdm@0 {
+			compatible = "st,stm32-dfsdm-adc";
+			#io-channel-cells = <1>;
+			reg = <0>;
+			status = "disabled";
+		};
+		dai_dfsdm0: dfsdm-audio@0 {
+			compatible = "st,stm32-dfsdm-audio";
+			#sound-dai-cells = <0>;
+			reg = <0>;
+			dmas = <&dmamux1 101 0x400 0x00>;
+			dma-names = "rx";
+			status = "disabled";
+		};
+	};
-- 
1.9.1


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

* [PATCH 1/7] MFD: add bindings for STM32 DFSDM driver
@ 2017-01-23 16:32   ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-23 16:32 UTC (permalink / raw)
  To: linux-arm-kernel

Add bindings information for STM32 Digital Filter for Sigma Delta modulators MFD driver

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 .../devicetree/bindings/mfd/stm32-dfsdm.txt        | 68 ++++++++++++++++++++++
 1 file changed, 68 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt

diff --git a/Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt b/Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt
new file mode 100644
index 0000000..e0b45ee
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt
@@ -0,0 +1,68 @@
+STMicroelectronics STM32 Digital Filter for Sigma Delta Modulator (DFSDM)
+ulti-function device.
+
+The STM32 DFSDM device is a multifunction device that handles the DFSDM IP.
+
+The DFSDM IP allows to add processing on Sigma Delta ADC based on SinC filters.
+For this, a pool of m filters can be connected to a pool of n channels.
+For STM32H7 : m = 4, n = 8.
+
+Each channel n is assigned to the SPI or Manchester interface n or n + 1.
+Channels 0 to 2 can also be connected to ADC IP instance 1 to 3.
+Filtering result is stored in a left aligned register, with 8 LSB reserved for
+the input channel ID.
+
+Each filter instance supports two contexts to manage conversions, each one has
+its own configurable sequence and trigger:
+- regular conversion: used for single or continuous conversion.
+- injected conversions: used for triggered conversion.
+
+Interfaces supported:
+- sigma delta ADCs trough IIO framework.
+- PDM microphones through ASoC framework.
+
+Required properties:
+- compatible:	Must be "st,stm32h7-dfsdm".
+- reg:		Specifies the DFSDM block register address and length.
+- interrupts:	IRQ lines connected to each DFSDM filter instance.
+- clocks:	IP and serial interfaces clocking. Should be set according
+		to rcc clock ID and "clock-names".
+- clock-names:	Input clock name "dfsdm_clk" must be defined,
+		"audio_clk" is optional. If defined CLKOUT is based on the audio
+		clock, else "dfsdm_clk" is used.
+
+Optional properties:
+- st,clkout-freq: clkout clock frequency (Hz).This clock must be set according
+		  to "clock" property. Frequency must be a multiple of the rcc
+		  clock frequency. If not, clkout frequency will not be
+		  accurate.
+- pinctrl-names:  set to "default".
+- pinctrl-0:	  List of phandles pointing to pin configuration nodes for DFSDM
+		  module.
+		  For Pinctrl properties see ../pinctrl/pinctrl-bindings.txt
+Example :
+	dfsdm: dfsdm at 4400D000 {
+		compatible = "st,stm32h7-dfsdm";
+		reg = <0x40017000 0x400>;
+		interrupts = <110>, <111>, <112>, <113>;
+		clocks = <&timer_clk>;
+		clock-names = "dfsdm_clk";
+		pinctrl-0 = <&dfsdm_ch0 &dfsdm_ch1>;
+		pinctrl-names = "default";
+		st,clkout-freq = <2480000>;
+
+		iio_dfsdm0: iio-dfsdm at 0 {
+			compatible = "st,stm32-dfsdm-adc";
+			#io-channel-cells = <1>;
+			reg = <0>;
+			status = "disabled";
+		};
+		dai_dfsdm0: dfsdm-audio at 0 {
+			compatible = "st,stm32-dfsdm-audio";
+			#sound-dai-cells = <0>;
+			reg = <0>;
+			dmas = <&dmamux1 101 0x400 0x00>;
+			dma-names = "rx";
+			status = "disabled";
+		};
+	};
-- 
1.9.1

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

* [PATCH 2/7] MFD: add STM32 DFSDM support
  2017-01-23 16:32 ` Arnaud Pouliquen
  (?)
@ 2017-01-23 16:32     ` Arnaud Pouliquen
  -1 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-23 16:32 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Lee Jones, Rob Herring,
	Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre Torgue, arnaud.pouliquen-qxv4g6HH51o

DFSDM hardware IP can be used at the same time for ADC sigma delta
conversion and audio PDM microphone.
MFD driver is in charge of configuring IP registers and managing IP clocks.
For this it exports an API to handles filters and channels resources.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
---
 drivers/mfd/Kconfig             |   11 +
 drivers/mfd/Makefile            |    2 +
 drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
 drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
 include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
 5 files changed, 1601 insertions(+)
 create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
 create mode 100644 drivers/mfd/stm32-dfsdm.c
 create mode 100644 include/linux/mfd/stm32-dfsdm.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index c6df644..4bb660b 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1607,6 +1607,17 @@ config MFD_STW481X
 	  in various ST Microelectronics and ST-Ericsson embedded
 	  Nomadik series.
 
+config MFD_STM32_DFSDM
+	tristate "ST Microelectronics STM32 DFSDM"
+	depends on (ARCH_STM32 && OF) || COMPILE_TEST
+	select MFD_CORE
+	select REGMAP
+	select REGMAP_MMIO
+	help
+	  Select this option to enable the STM32 Digital Filter
+	  for Sigma Delta Modulators (DFSDM) driver used
+	  in various STM32 series.
+
 menu "Multimedia Capabilities Port drivers"
 	depends on ARCH_SA1100
 
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 9834e66..1f095e5 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -211,3 +211,5 @@ obj-$(CONFIG_INTEL_SOC_PMIC)	+= intel-soc-pmic.o
 obj-$(CONFIG_MFD_MT6397)	+= mt6397-core.o
 
 obj-$(CONFIG_MFD_ALTERA_A10SR)	+= altera-a10sr.o
+
+obj-$(CONFIG_MFD_STM32_DFSDM)	+= stm32-dfsdm.o
\ No newline at end of file
diff --git a/drivers/mfd/stm32-dfsdm-reg.h b/drivers/mfd/stm32-dfsdm-reg.h
new file mode 100644
index 0000000..05ff702
--- /dev/null
+++ b/drivers/mfd/stm32-dfsdm-reg.h
@@ -0,0 +1,220 @@
+/*
+ * This file is part of STM32 DFSDM mfd driver
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author(s): Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>.
+ *
+ * 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.
+ */
+
+#ifndef MDF_STM32_DFSDM_REG_H
+#define MDF_STM32_DFSDM_REG_H
+
+#include <linux/bitfield.h>
+/*
+ * Channels definitions
+ */
+#define DFSDM_CHCFGR1(y)  ((y) * 0x20 + 0x00)
+#define DFSDM_CHCFGR2(y)  ((y) * 0x20 + 0x04)
+#define DFSDM_AWSCDR(y)   ((y) * 0x20 + 0x08)
+#define DFSDM_CHWDATR(y)  ((y) * 0x20 + 0x0C)
+#define DFSDM_CHDATINR(y) ((y) * 0x20 + 0x10)
+
+/* CHCFGR1: Channel configuration register 1 */
+#define DFSDM_CHCFGR1_SITP_MASK     GENMASK(1, 0)
+#define DFSDM_CHCFGR1_SITP(v)       FIELD_PREP(DFSDM_CHCFGR1_SITP_MASK, v)
+#define DFSDM_CHCFGR1_SPICKSEL_MASK GENMASK(3, 2)
+#define DFSDM_CHCFGR1_SPICKSEL(v)   FIELD_PREP(DFSDM_CHCFGR1_SPICKSEL_MASK, v)
+#define DFSDM_CHCFGR1_SCDEN_MASK    BIT(5)
+#define DFSDM_CHCFGR1_SCDEN(v)      FIELD_PREP(DFSDM_CHCFGR1_SCDEN_MASK, v)
+#define DFSDM_CHCFGR1_CKABEN_MASK   BIT(6)
+#define DFSDM_CHCFGR1_CKABEN(v)     FIELD_PREP(DFSDM_CHCFGR1_CKABEN_MASK, v)
+#define DFSDM_CHCFGR1_CHEN_MASK     BIT(7)
+#define DFSDM_CHCFGR1_CHEN(v)       FIELD_PREP(DFSDM_CHCFGR1_CHEN_MASK, v)
+#define DFSDM_CHCFGR1_CHINSEL_MASK  BIT(8)
+#define DFSDM_CHCFGR1_CHINSEL(v)    FIELD_PREP(DFSDM_CHCFGR1_CHINSEL_MASK, v)
+#define DFSDM_CHCFGR1_DATMPX_MASK   GENMASK(13, 12)
+#define DFSDM_CHCFGR1_DATMPX(v)     FIELD_PREP(DFSDM_CHCFGR1_DATMPX_MASK, v)
+#define DFSDM_CHCFGR1_DATPACK_MASK  GENMASK(15, 14)
+#define DFSDM_CHCFGR1_DATPACK(v)    FIELD_PREP(DFSDM_CHCFGR1_DATPACK_MASK, v)
+#define DFSDM_CHCFGR1_CKOUTDIV_MASK GENMASK(23, 16)
+#define DFSDM_CHCFGR1_CKOUTDIV(v)   FIELD_PREP(DFSDM_CHCFGR1_CKOUTDIV_MASK, v)
+#define DFSDM_CHCFGR1_CKOUTSRC_MASK BIT(30)
+#define DFSDM_CHCFGR1_CKOUTSRC(v)   FIELD_PREP(DFSDM_CHCFGR1_CKOUTSRC_MASK, v)
+#define DFSDM_CHCFGR1_DFSDMEN_MASK  BIT(31)
+#define DFSDM_CHCFGR1_DFSDMEN(v)    FIELD_PREP(DFSDM_CHCFGR1_DFSDMEN_MASK, v)
+
+/* CHCFGR2: Channel configuration register 2 */
+#define DFSDM_CHCFGR2_DTRBS_MASK    GENMASK(7, 3)
+#define DFSDM_CHCFGR2_DTRBS(v)      FIELD_PREP(DFSDM_CHCFGR2_DTRBS_MASK, v)
+#define DFSDM_CHCFGR2_OFFSET_MASK   GENMASK(31, 8)
+#define DFSDM_CHCFGR2_OFFSET(v)     FIELD_PREP(DFSDM_CHCFGR2_OFFSET_MASK, v)
+
+/* AWSCDR: Channel analog watchdog and short circuit detector */
+#define DFSDM_AWSCDR_SCDT_MASK    GENMASK(7, 0)
+#define DFSDM_AWSCDR_SCDT(v)      FIELD_PREP(DFSDM_AWSCDR_SCDT_MASK, v)
+#define DFSDM_AWSCDR_BKSCD_MASK   GENMASK(15, 12)
+#define DFSDM_AWSCDR_BKSCD(v)	  FIELD_PREP(DFSDM_AWSCDR_BKSCD_MASK, v)
+#define DFSDM_AWSCDR_AWFOSR_MASK  GENMASK(20, 16)
+#define DFSDM_AWSCDR_AWFOSR(v)    FIELD_PREP(DFSDM_AWSCDR_AWFOSR_MASK, v)
+#define DFSDM_AWSCDR_AWFORD_MASK  GENMASK(23, 22)
+#define DFSDM_AWSCDR_AWFORD(v)    FIELD_PREP(DFSDM_AWSCDR_AWFORD_MASK, v)
+
+/*
+ * Filters definitions
+ */
+#define DFSDM_FILTER_BASE_ADR		0x100
+#define DFSDM_FILTER_REG_MASK		0x7F
+#define DFSDM_FILTER_X_BASE_ADR(x)	((x) * 0x80 + DFSDM_FILTER_BASE_ADR)
+
+#define DFSDM_CR1(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x00)
+#define DFSDM_CR2(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x04)
+#define DFSDM_ISR(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x08)
+#define DFSDM_ICR(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x0C)
+#define DFSDM_JCHGR(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x10)
+#define DFSDM_FCR(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x14)
+#define DFSDM_JDATAR(x)  (DFSDM_FILTER_X_BASE_ADR(x)  + 0x18)
+#define DFSDM_RDATAR(x)  (DFSDM_FILTER_X_BASE_ADR(x)  + 0x1C)
+#define DFSDM_AWHTR(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x20)
+#define DFSDM_AWLTR(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x24)
+#define DFSDM_AWSR(x)    (DFSDM_FILTER_X_BASE_ADR(x)  + 0x28)
+#define DFSDM_AWCFR(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x2C)
+#define DFSDM_EXMAX(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x30)
+#define DFSDM_EXMIN(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x34)
+#define DFSDM_CNVTIMR(x) (DFSDM_FILTER_X_BASE_ADR(x)  + 0x38)
+
+/* CR1 Control register 1 */
+#define DFSDM_CR1_DFEN_MASK	BIT(0)
+#define DFSDM_CR1_DFEN(v)	FIELD_PREP(DFSDM_CR1_DFEN_MASK, v)
+#define DFSDM_CR1_JSWSTART_MASK	BIT(1)
+#define DFSDM_CR1_JSWSTART(v)	FIELD_PREP(DFSDM_CR1_JSWSTART_MASK, v)
+#define DFSDM_CR1_JSYNC_MASK	BIT(3)
+#define DFSDM_CR1_JSYNC(v)	FIELD_PREP(DFSDM_CR1_JSYNC_MASK, v)
+#define DFSDM_CR1_JSCAN_MASK	BIT(4)
+#define DFSDM_CR1_JSCAN(v)	FIELD_PREP(DFSDM_CR1_JSCAN_MASK, v)
+#define DFSDM_CR1_JDMAEN_MASK	BIT(5)
+#define DFSDM_CR1_JDMAEN(v)	FIELD_PREP(DFSDM_CR1_JDMAEN_MASK, v)
+#define DFSDM_CR1_JEXTSEL_MASK	GENMASK(12, 8)
+#define DFSDM_CR1_JEXTSEL(v)	FIELD_PREP(DFSDM_CR1_JEXTSEL_MASK, v)
+#define DFSDM_CR1_JEXTEN_MASK	GENMASK(14, 13)
+#define DFSDM_CR1_JEXTEN(v)	FIELD_PREP(DFSDM_CR1_JEXTEN_MASK, v)
+#define DFSDM_CR1_RSWSTART_MASK	BIT(17)
+#define DFSDM_CR1_RSWSTART(v)	FIELD_PREP(DFSDM_CR1_RSWSTART_MASK, v)
+#define DFSDM_CR1_RCONT_MASK	BIT(18)
+#define DFSDM_CR1_RCONT(v)	FIELD_PREP(DFSDM_CR1_RCONT_MASK, v)
+#define DFSDM_CR1_RSYNC_MASK	BIT(19)
+#define DFSDM_CR1_RSYNC(v)	FIELD_PREP(DFSDM_CR1_RSYNC_MASK, v)
+#define DFSDM_CR1_RDMAEN_MASK	BIT(21)
+#define DFSDM_CR1_RDMAEN(v)	FIELD_PREP(DFSDM_CR1_RDMAEN_MASK, v)
+#define DFSDM_CR1_RCH_MASK	GENMASK(26, 24)
+#define DFSDM_CR1_RCH(v)	FIELD_PREP(DFSDM_CR1_RCH_MASK, v)
+#define DFSDM_CR1_FAST_MASK	BIT(29)
+#define DFSDM_CR1_FAST(v)	FIELD_PREP(DFSDM_CR1_FAST_MASK, v)
+#define DFSDM_CR1_AWFSEL_MASK	BIT(30)
+#define DFSDM_CR1_AWFSEL(v)	FIELD_PREP(DFSDM_CR1_AWFSEL_MASK, v)
+
+/* CR2: Control register 2 */
+#define DFSDM_CR2_IE_MASK	GENMASK(6, 0)
+#define DFSDM_CR2_IE(v)		FIELD_PREP(DFSDM_CR2_IE_MASK, v)
+#define DFSDM_CR2_JEOCIE_MASK	BIT(0)
+#define DFSDM_CR2_JEOCIE(v)	FIELD_PREP(DFSDM_CR2_JEOCIE_MASK, v)
+#define DFSDM_CR2_REOCIE_MASK	BIT(1)
+#define DFSDM_CR2_REOCIE(v)	FIELD_PREP(DFSDM_CR2_REOCIE_MASK, v)
+#define DFSDM_CR2_JOVRIE_MASK	BIT(2)
+#define DFSDM_CR2_JOVRIE(v)	FIELD_PREP(DFSDM_CR2_JOVRIE_MASK, v)
+#define DFSDM_CR2_ROVRIE_MASK	BIT(3)
+#define DFSDM_CR2_ROVRIE(v)	FIELD_PREP(DFSDM_CR2_ROVRIE_MASK, v)
+#define DFSDM_CR2_AWDIE_MASK	BIT(4)
+#define DFSDM_CR2_AWDIE(v)	FIELD_PREP(DFSDM_CR2_AWDIE_MASK, v)
+#define DFSDM_CR2_SCDIE_MASK	BIT(5)
+#define DFSDM_CR2_SCDIE(v)	FIELD_PREP(DFSDM_CR2_SCDIE_MASK, v)
+#define DFSDM_CR2_CKABIE_MASK	BIT(6)
+#define DFSDM_CR2_CKABIE(v)	FIELD_PREP(DFSDM_CR2_CKABIE_MASK, v)
+#define DFSDM_CR2_EXCH_MASK	GENMASK(15, 8)
+#define DFSDM_CR2_EXCH(v)	FIELD_PREP(DFSDM_CR2_EXCH_MASK, v)
+#define DFSDM_CR2_AWDCH_MASK	GENMASK(23, 16)
+#define DFSDM_CR2_AWDCH(v)	FIELD_PREP(DFSDM_CR2_AWDCH_MASK, v)
+
+/* ISR: Interrupt status register */
+#define DFSDM_ISR_JEOCF_MASK	BIT(0)
+#define DFSDM_ISR_JEOCF(v)	FIELD_PREP(DFSDM_ISR_JEOCF_MASK, v)
+#define DFSDM_ISR_REOCF_MASK	BIT(1)
+#define DFSDM_ISR_REOCF(v)	FIELD_PREP(DFSDM_ISR_REOCF_MASK, v)
+#define DFSDM_ISR_JOVRF_MASK	BIT(2)
+#define DFSDM_ISR_JOVRF(v)	FIELD_PREP(DFSDM_ISR_JOVRF_MASK, v)
+#define DFSDM_ISR_ROVRF_MASK	BIT(3)
+#define DFSDM_ISR_ROVRF(v)	FIELD_PREP(DFSDM_ISR_ROVRF_MASK, v)
+#define DFSDM_ISR_AWDF_MASK	BIT(4)
+#define DFSDM_ISR_AWDF(v)	FIELD_PREP(DFSDM_ISR_AWDF_MASK, v)
+#define DFSDM_ISR_JCIP_MASK	BIT(13)
+#define DFSDM_ISR_JCIP(v)	FIELD_PREP(DFSDM_ISR_JCIP_MASK, v)
+#define DFSDM_ISR_RCIP_MASK	BIT(14)
+#define DFSDM_ISR_RCIP(v)	FIELD_PREP(DFSDM_ISR_RCIP, v)
+#define DFSDM_ISR_CKABF_MASK	GENMASK(23, 16)
+#define DFSDM_ISR_CKABF(v)	FIELD_PREP(DFSDM_ISR_CKABF_MASK, v)
+#define DFSDM_ISR_SCDF_MASK	GENMASK(31, 24)
+#define DFSDM_ISR_SCDF(v)	FIELD_PREP(DFSDM_ISR_SCDF_MASK, v)
+
+/* ICR: Interrupt flag clear register */
+#define DFSDM_ICR_CLRJOVRF_MASK	      BIT(2)
+#define DFSDM_ICR_CLRJOVRF(v)	      FIELD_PREP(DFSDM_ICR_CLRJOVRF_MASK, v)
+#define DFSDM_ICR_CLRROVRF_MASK	      BIT(3)
+#define DFSDM_ICR_CLRROVRF(v)	      FIELD_PREP(DFSDM_ICR_CLRROVRF_MASK, v)
+#define DFSDM_ICR_CLRCKABF_MASK	      GENMASK(23, 16)
+#define DFSDM_ICR_CLRCKABF(v)	      FIELD_PREP(DFSDM_ICR_CLRCKABF_MASK, v)
+#define DFSDM_ICR_CLRCKABF_CH_MASK(y) BIT(16 + (y))
+#define DFSDM_ICR_CLRCKABF_CH(v, y)   \
+			   (((v) << (16 + (y))) & DFSDM_ICR_CLRCKABF_CH_MASK(y))
+#define DFSDM_ICR_CLRSCDF_MASK	      GENMASK(31, 24)
+#define DFSDM_ICR_CLRSCDF(v)	      FIELD_PREP(DFSDM_ICR_CLRSCDF_MASK, v)
+#define DFSDM_ICR_CLRSCDF_CH_MASK(y)  BIT(24 + (y))
+#define DFSDM_ICR_CLRSCDF_CH(v, y)    \
+			       (((v) << (24 + (y))) & DFSDM_ICR_CLRSCDF_MASK(y))
+
+/* FCR: Filter control register */
+#define DFSDM_FCR_IOSR_MASK	GENMASK(7, 0)
+#define DFSDM_FCR_IOSR(v)	FIELD_PREP(DFSDM_FCR_IOSR_MASK, v)
+#define DFSDM_FCR_FOSR_MASK	GENMASK(25, 16)
+#define DFSDM_FCR_FOSR(v)	FIELD_PREP(DFSDM_FCR_FOSR_MASK, v)
+#define DFSDM_FCR_FORD_MASK	GENMASK(31, 29)
+#define DFSDM_FCR_FORD(v)	FIELD_PREP(DFSDM_FCR_FORD_MASK, v)
+
+/* RDATAR: Filter data register for regular channel */
+#define DFSDM_DATAR_CH_MASK	GENMASK(2, 0)
+#define DFSDM_DATAR_DATA_OFFSET 8
+#define DFSDM_DATAR_DATA_MASK	GENMASK(31, DFSDM_DATAR_DATA_OFFSET)
+
+/* AWLTR: Filter analog watchdog low threshold register */
+#define DFSDM_AWLTR_BKAWL_MASK	GENMASK(3, 0)
+#define DFSDM_AWLTR_BKAWL(v)	FIELD_PREP(DFSDM_AWLTR_BKAWL_MASK, v)
+#define DFSDM_AWLTR_AWLT_MASK	GENMASK(31, 8)
+#define DFSDM_AWLTR_AWLT(v)	FIELD_PREP(DFSDM_AWLTR_AWLT_MASK, v)
+
+/* AWHTR: Filter analog watchdog low threshold register */
+#define DFSDM_AWHTR_BKAWH_MASK	GENMASK(3, 0)
+#define DFSDM_AWHTR_BKAWH(v)	FIELD_PREP(DFSDM_AWHTR_BKAWH_MASK, v)
+#define DFSDM_AWHTR_AWHT_MASK	GENMASK(31, 8)
+#define DFSDM_AWHTR_AWHT(v)	FIELD_PREP(DFSDM_AWHTR_AWHT_MASK, v)
+
+/* AWSR: Filter watchdog status register */
+#define DFSDM_AWSR_AWLTF_MASK	GENMASK(7, 0)
+#define DFSDM_AWSR_AWLTF(v)	FIELD_PREP(DFSDM_AWSR_AWLTF_MASK, v)
+#define DFSDM_AWSR_AWHTF_MASK	GENMASK(15, 8)
+#define DFSDM_AWSR_AWHTF(v)	FIELD_PREP(DFSDM_AWSR_AWHTF_MASK, v)
+
+/* AWCFR: Filter watchdog status register */
+#define DFSDM_AWCFR_AWLTF_MASK	GENMASK(7, 0)
+#define DFSDM_AWCFR_AWLTF(v)	FIELD_PREP(DFSDM_AWCFR_AWLTF_MASK, v)
+#define DFSDM_AWCFR_AWHTF_MASK	GENMASK(15, 8)
+#define DFSDM_AWCFR_AWHTF(v)	FIELD_PREP(DFSDM_AWCFR_AWHTF_MASK, v)
+
+#endif
diff --git a/drivers/mfd/stm32-dfsdm.c b/drivers/mfd/stm32-dfsdm.c
new file mode 100644
index 0000000..81ca29c
--- /dev/null
+++ b/drivers/mfd/stm32-dfsdm.c
@@ -0,0 +1,1044 @@
+/*
+ * This file is part of STM32 DFSDM mfd driver
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author(s): Arnaud Pouliquen <arnaud.pouliquen-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/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/stm32-dfsdm.h>
+
+#include "stm32-dfsdm-reg.h"
+
+#define DFSDM_UPDATE_BITS(regm, reg, mask, val) \
+		WARN_ON(regmap_update_bits(regm, reg, mask, val))
+
+#define DFSDM_REG_READ(regm, reg, val) \
+		WARN_ON(regmap_read(regm, reg, val))
+
+#define DFSDM_REG_WRITE(regm, reg, val) \
+		WARN_ON(regmap_write(regm, reg, val))
+
+#define STM32H7_DFSDM_NUM_FILTERS	4
+#define STM32H7_DFSDM_NUM_INPUTS	8
+
+enum dfsdm_clkout_src {
+	DFSDM_CLK,
+	AUDIO_CLK
+};
+
+struct stm32_dev_data {
+	const struct stm32_dfsdm dfsdm;
+	const struct regmap_config *regmap_cfg;
+};
+
+struct dfsdm_priv;
+
+struct filter_params {
+	unsigned int id;
+	int irq;
+	struct stm32_dfsdm_fl_event event;
+	u32 event_mask;
+	struct dfsdm_priv *priv; /* Cross ref for context */
+	unsigned int ext_ch_mask;
+	unsigned int scan_ch;
+};
+
+struct ch_params {
+	struct stm32_dfsdm_channel ch;
+};
+
+struct dfsdm_priv {
+	struct platform_device *pdev;
+	struct stm32_dfsdm dfsdm;
+
+	spinlock_t lock; /* Used for resource sharing & interrupt lock */
+
+	/* Filters */
+	struct filter_params *filters;
+	unsigned int free_filter_mask;
+	unsigned int scd_filter_mask;
+	unsigned int ckab_filter_mask;
+
+	/* Channels */
+	struct stm32_dfsdm_channel *channels;
+	unsigned int free_channel_mask;
+	atomic_t n_active_ch;
+
+	/* Clock */
+	struct clk *clk;
+	struct clk *aclk;
+	unsigned int clkout_div;
+	unsigned int clkout_freq_req;
+
+	/* Registers*/
+	void __iomem *base;
+	struct regmap *regmap;
+	phys_addr_t phys_base;
+};
+
+/*
+ * Common
+ */
+static bool stm32_dfsdm_volatile_reg(struct device *dev, unsigned int reg)
+{
+	if (reg < DFSDM_FILTER_BASE_ADR)
+		return false;
+
+	/*
+	 * Mask is done on register to avoid to list registers of all them
+	 * filter instances.
+	 */
+	switch (reg & DFSDM_FILTER_REG_MASK) {
+	case DFSDM_CR1(0) & DFSDM_FILTER_REG_MASK:
+	case DFSDM_ISR(0) & DFSDM_FILTER_REG_MASK:
+	case DFSDM_JDATAR(0) & DFSDM_FILTER_REG_MASK:
+	case DFSDM_RDATAR(0) & DFSDM_FILTER_REG_MASK:
+		return true;
+	}
+
+	return false;
+}
+
+static const struct regmap_config stm32h7_dfsdm_regmap_cfg = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = sizeof(u32),
+	.max_register = DFSDM_CNVTIMR(STM32H7_DFSDM_NUM_FILTERS - 1),
+	.volatile_reg = stm32_dfsdm_volatile_reg,
+	.fast_io = true,
+};
+
+static const struct stm32_dev_data stm32h7_data = {
+	.dfsdm.max_channels = STM32H7_DFSDM_NUM_INPUTS,
+	.dfsdm.max_filters = STM32H7_DFSDM_NUM_FILTERS,
+	.regmap_cfg = &stm32h7_dfsdm_regmap_cfg,
+};
+
+static int stm32_dfsdm_start_dfsdm(struct dfsdm_priv *priv)
+{
+	int ret;
+	struct device *dev = &priv->pdev->dev;
+
+	if (atomic_inc_return(&priv->n_active_ch) == 1) {
+		ret = clk_prepare_enable(priv->clk);
+		if (ret < 0) {
+			dev_err(dev, "Failed to start clock\n");
+			return ret;
+		}
+		if (priv->aclk) {
+			ret = clk_prepare_enable(priv->aclk);
+			if (ret < 0) {
+				dev_err(dev, "Failed to start audio clock\n");
+				return ret;
+			}
+		}
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
+				  DFSDM_CHCFGR1_CKOUTDIV_MASK,
+				  DFSDM_CHCFGR1_CKOUTDIV(priv->clkout_div));
+
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
+				  DFSDM_CHCFGR1_DFSDMEN_MASK,
+				  DFSDM_CHCFGR1_DFSDMEN(1));
+	}
+
+	dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,
+		atomic_read(&priv->n_active_ch));
+
+	return 0;
+}
+
+static void stm32_dfsdm_stop_dfsdm(struct dfsdm_priv *priv)
+{
+	if (atomic_dec_and_test(&priv->n_active_ch)) {
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
+				  DFSDM_CHCFGR1_DFSDMEN_MASK,
+				  DFSDM_CHCFGR1_DFSDMEN(0));
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
+				  DFSDM_CHCFGR1_CKOUTDIV_MASK,
+				  DFSDM_CHCFGR1_CKOUTDIV(0));
+		clk_disable_unprepare(priv->clk);
+		if (priv->aclk)
+			clk_disable_unprepare(priv->aclk);
+	}
+	dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,
+		atomic_read(&priv->n_active_ch));
+}
+
+static unsigned int stm32_dfsdm_get_clkout_divider(struct dfsdm_priv *priv,
+						   unsigned long rate)
+{
+	unsigned int delta, div;
+
+	/* div = 0 disables the clockout */
+	if (!priv->clkout_freq_req)
+		return 0;
+
+	div = DIV_ROUND_CLOSEST(rate, priv->clkout_freq_req);
+
+	delta = rate - (priv->clkout_freq_req * div);
+	if (delta)
+		dev_warn(&priv->pdev->dev,
+			 "clkout not accurate. delta (Hz): %d\n", delta);
+
+	dev_dbg(&priv->pdev->dev, "%s: clk: %lu (Hz), div %u\n",
+		__func__, rate, div);
+
+	return (div - 1);
+}
+
+/*
+ * Filters
+ */
+
+static int stm32_dfsdm_clear_event(struct dfsdm_priv *priv, unsigned int fl_id,
+				   unsigned int event, int mask)
+{
+	int val;
+
+	switch (event) {
+	case DFSDM_EVENT_INJ_EOC:
+		DFSDM_REG_READ(priv->regmap, DFSDM_JDATAR(fl_id), &val);
+		break;
+	case DFSDM_EVENT_REG_EOC:
+		DFSDM_REG_READ(priv->regmap, DFSDM_RDATAR(fl_id), &val);
+		break;
+	case DFSDM_EVENT_INJ_XRUN:
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_ICR(fl_id),
+				  DFSDM_ICR_CLRJOVRF_MASK,
+				  DFSDM_ICR_CLRJOVRF_MASK);
+		break;
+	case DFSDM_EVENT_REG_XRUN:
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_ICR(fl_id),
+				  DFSDM_ICR_CLRROVRF_MASK,
+				  DFSDM_ICR_CLRROVRF_MASK);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
+{
+	struct filter_params *params = arg;
+	unsigned int status;
+	struct dfsdm_priv *priv = params->priv;
+	unsigned int event_mask = params->event_mask;
+
+	DFSDM_REG_READ(priv->regmap, DFSDM_ISR(params->id), &status);
+
+	if (status & DFSDM_ISR_JOVRF_MASK) {
+		if (event_mask & DFSDM_EVENT_INJ_XRUN) {
+			params->event.cb(&priv->dfsdm, params->id,
+					 DFSDM_EVENT_INJ_XRUN, 0,
+					 params->event.context);
+		}
+		stm32_dfsdm_clear_event(priv, params->id, DFSDM_EVENT_INJ_XRUN,
+					0);
+	}
+
+	if (status & DFSDM_ISR_ROVRF_MASK) {
+		if (event_mask & DFSDM_EVENT_REG_XRUN) {
+			params->event.cb(&priv->dfsdm, params->id,
+					 DFSDM_EVENT_REG_XRUN, 0,
+					 params->event.context);
+		}
+		stm32_dfsdm_clear_event(priv, params->id, DFSDM_EVENT_REG_XRUN,
+					0);
+	}
+
+	if (status & DFSDM_ISR_JEOCF_MASK) {
+		if (event_mask & DFSDM_EVENT_INJ_EOC)
+			params->event.cb(&priv->dfsdm, params->id,
+					 DFSDM_EVENT_INJ_EOC, 0,
+					 params->event.context);
+		else
+			stm32_dfsdm_clear_event(priv, params->id,
+						DFSDM_EVENT_INJ_EOC, 0);
+	}
+
+	if (status & DFSDM_ISR_REOCF_MASK) {
+		if (event_mask & DFSDM_EVENT_REG_EOC)
+			params->event.cb(&priv->dfsdm, params->id,
+					 DFSDM_EVENT_REG_EOC, 0,
+					 params->event.context);
+		else
+			stm32_dfsdm_clear_event(priv, params->id,
+						DFSDM_EVENT_REG_EOC, 0);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void stm32_dfsdm_configure_reg_conv(struct dfsdm_priv *priv,
+					   unsigned int fl_id,
+					   struct stm32_dfsdm_regular *params)
+{
+	unsigned int ch_id = params->ch_src;
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RCH_MASK,
+			  DFSDM_CR1_RCH(ch_id));
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_FAST_MASK,
+			  DFSDM_CR1_FAST(params->fast_mode));
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RCONT_MASK,
+			  DFSDM_CR1_RCONT(params->cont_mode));
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RDMAEN_MASK,
+			  DFSDM_CR1_RDMAEN(params->dma_mode));
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RSYNC_MASK,
+			  DFSDM_CR1_RSYNC(params->sync_mode));
+
+	priv->filters[fl_id].scan_ch = BIT(ch_id);
+}
+
+static void stm32_dfsdm_configure_inj_conv(struct dfsdm_priv *priv,
+					   unsigned int fl_id,
+					   struct stm32_dfsdm_injected *params)
+{
+	int val;
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_JSCAN_MASK,
+			  DFSDM_CR1_JSCAN(params->scan_mode));
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_JDMAEN_MASK,
+			  DFSDM_CR1_JDMAEN(params->dma_mode));
+
+	val = (params->trigger == DFSDM_FILTER_EXT_TRIGGER) ?
+	      params->trig_src : 0;
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id),
+			  DFSDM_CR1_JEXTSEL_MASK,
+			  DFSDM_CR1_JEXTSEL(val));
+
+	val = (params->trigger == DFSDM_FILTER_SYNC_TRIGGER) ? 1 : 0;
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_JSYNC_MASK,
+			  DFSDM_CR1_JSYNC(val));
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_JEXTEN_MASK,
+			  DFSDM_CR1_JEXTEN(params->trig_pol));
+	priv->filters[fl_id].scan_ch = params->ch_group;
+
+	DFSDM_REG_WRITE(priv->regmap, DFSDM_JCHGR(fl_id), params->ch_group);
+}
+
+/**
+ * stm32_dfsdm_configure_filter - Configure filter.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter id.
+ * @conv: Conversion type regular or injected.
+ */
+int stm32_dfsdm_configure_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+				 struct stm32_dfsdm_filter *fl_cfg)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv,
+					       dfsdm);
+	struct stm32_dfsdm_sinc_filter *sparams = &fl_cfg->sinc_params;
+
+	dev_dbg(&priv->pdev->dev, "%s:config filter %d\n", __func__, fl_id);
+
+	/* Average integrator oversampling */
+	if ((!fl_cfg->int_oversampling) ||
+	    (fl_cfg->int_oversampling > DFSDM_MAX_INT_OVERSAMPLING)) {
+		dev_err(&priv->pdev->dev, "invalid integrator oversampling %d\n",
+			fl_cfg->int_oversampling);
+		return -EINVAL;
+	}
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_FCR(fl_id), DFSDM_FCR_IOSR_MASK,
+			  DFSDM_FCR_IOSR((fl_cfg->int_oversampling - 1)));
+
+	/* Oversamplings and filter*/
+	if ((!sparams->oversampling) ||
+	    (sparams->oversampling > DFSDM_MAX_FL_OVERSAMPLING)) {
+		dev_err(&priv->pdev->dev, "invalid oversampling %d\n",
+			sparams->oversampling);
+		return -EINVAL;
+	}
+
+	if (sparams->order > DFSDM_SINC5_ORDER) {
+		dev_err(&priv->pdev->dev, "invalid filter order %d\n",
+			sparams->order);
+		return -EINVAL;
+	}
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FOSR_MASK,
+			  DFSDM_FCR_FOSR((sparams->oversampling - 1)));
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FORD_MASK,
+			  DFSDM_FCR_FORD(sparams->order));
+
+	/* Conversion */
+	if (fl_cfg->inj_params)
+		stm32_dfsdm_configure_inj_conv(priv, fl_id, fl_cfg->inj_params);
+	else if (fl_cfg->reg_params)
+		stm32_dfsdm_configure_reg_conv(priv, fl_id, fl_cfg->reg_params);
+
+	priv->filters[fl_id].event = fl_cfg->event;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dfsdm_configure_filter);
+
+/**
+ * stm32_dfsdm_start_filter - Start filter conversion.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter id.
+ * @conv: Conversion type regular or injected.
+ */
+void stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+			      enum stm32_dfsdm_conv_type conv)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+
+	dev_dbg(&priv->pdev->dev, "%s:start filter %d\n", __func__, fl_id);
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_DFEN_MASK,
+			  DFSDM_CR1_DFEN(1));
+
+	if (conv == DFSDM_FILTER_REG_CONV) {
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id),
+				  DFSDM_CR1_RSWSTART_MASK,
+				  DFSDM_CR1_RSWSTART(1));
+	} else if (conv == DFSDM_FILTER_SW_INJ_CONV) {
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id),
+				  DFSDM_CR1_JSWSTART_MASK,
+				  DFSDM_CR1_JSWSTART(1));
+	}
+}
+EXPORT_SYMBOL_GPL(dfsdm_start_filter);
+
+/**
+ * stm32_dfsdm_stop_filter - Stop filter conversion.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter id.
+ */
+void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+
+	dev_dbg(&priv->pdev->dev, "%s:stop filter %d\n", __func__, fl_id);
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_DFEN_MASK,
+			  DFSDM_CR1_DFEN(0));
+	priv->filters[fl_id].scan_ch = 0;
+}
+EXPORT_SYMBOL_GPL(dfsdm_stop_filter);
+
+/**
+ * stm32_dfsdm_read_fl_conv - Read filter conversion.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter id.
+ * @type: Regular or injected conversion.
+ */
+void stm32_dfsdm_read_fl_conv(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+			      u32 *val, int *ch_id,
+			      enum stm32_dfsdm_conv_type type)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+	int reg_v, offset;
+
+	if (type == DFSDM_FILTER_REG_CONV)
+		offset = DFSDM_RDATAR(fl_id);
+	else
+		offset = DFSDM_JDATAR(fl_id);
+
+	DFSDM_REG_READ(priv->regmap, offset, &reg_v);
+
+	*ch_id = reg_v & DFSDM_DATAR_CH_MASK;
+	*val = reg_v & DFSDM_DATAR_DATA_MASK;
+}
+EXPORT_SYMBOL_GPL(dfsdm_read_fl_conv);
+
+/**
+ * stm32_dfsdm_get_filter - Get filter instance.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter instance to reserve.
+ *
+ * Reserves a DFSDM filter resource.
+ */
+int stm32_dfsdm_get_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv,
+					       dfsdm);
+	struct device *dev = &priv->pdev->dev;
+
+	spin_lock(&priv->lock);
+	if (!(priv->free_filter_mask & BIT(fl_id))) {
+		spin_unlock(&priv->lock);
+		dev_err(dev, "filter resource %d available\n", fl_id);
+		return -EBUSY;
+	}
+	priv->free_filter_mask &= ~BIT(fl_id);
+
+	spin_unlock(&priv->lock);
+
+	dev_dbg(dev, "%s: new mask %#x\n", __func__, priv->free_filter_mask);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dfsdm_get_filter);
+
+/**
+ * stm32_dfsdm_release_filter - Release filter instance.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter id.
+ *
+ * Free the DFSDM filter resource.
+ */
+void stm32_dfsdm_release_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+
+	spin_lock(&priv->lock);
+	priv->free_filter_mask |= BIT(fl_id);
+	spin_unlock(&priv->lock);
+}
+EXPORT_SYMBOL_GPL(dfsdm_release_filter);
+
+/**
+ * stm32_dfsdm_get_filter_dma_addr - Get register address for dma transfer.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter id.
+ * @conv: Conversion type.
+ */
+dma_addr_t stm32_dfsdm_get_filter_dma_phy_addr(struct stm32_dfsdm *dfsdm,
+					       unsigned int fl_id,
+					       enum stm32_dfsdm_conv_type conv)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+
+	if (conv == DFSDM_FILTER_REG_CONV)
+		return (dma_addr_t)(priv->phys_base + DFSDM_RDATAR(fl_id));
+	else
+		return (dma_addr_t)(priv->phys_base + DFSDM_JDATAR(fl_id));
+}
+
+/**
+ * stm32_dfsdm_register_fl_event - Register filter event.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter id.
+ * @event: Event to unregister.
+ * @chan_mask: Mask of channels associated to filter.
+ *
+ * The function enables associated IRQ.
+ */
+int stm32_dfsdm_register_fl_event(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+				  enum stm32_dfsdm_events event,
+				  unsigned int chan_mask)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+	unsigned long flags, ulmask = chan_mask;
+	int ret, i;
+
+	dev_dbg(&priv->pdev->dev, "%s:for filter %d: event %#x ch_mask %#x\n",
+		__func__, fl_id, event, chan_mask);
+
+	if (event > DFSDM_EVENT_CKA)
+		return -EINVAL;
+
+	/* Clear interrupt before enable them */
+	ret = stm32_dfsdm_clear_event(priv, fl_id, event, chan_mask);
+	if (ret < 0)
+		return ret;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	/* Enable interrupts */
+	switch (event) {
+	case DFSDM_EVENT_SCD:
+		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
+			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
+					  DFSDM_CHCFGR1_SCDEN_MASK,
+					  DFSDM_CHCFGR1_SCDEN(1));
+		}
+		if (!priv->scd_filter_mask)
+			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
+					  DFSDM_CR2_SCDIE_MASK,
+					  DFSDM_CR2_SCDIE(1));
+		priv->scd_filter_mask |= BIT(fl_id);
+		break;
+	case DFSDM_EVENT_CKA:
+		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
+			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
+					  DFSDM_CHCFGR1_CKABEN_MASK,
+					  DFSDM_CHCFGR1_CKABEN(1));
+		}
+		if (!priv->ckab_filter_mask)
+			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
+					  DFSDM_CR2_CKABIE_MASK,
+					  DFSDM_CR2_CKABIE(1));
+		priv->ckab_filter_mask |= BIT(fl_id);
+		break;
+	default:
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(fl_id), event, event);
+	}
+	priv->filters[fl_id].event_mask |= event;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dfsdm_register_fl_event);
+
+/**
+ * stm32_dfsdm_unregister_fl_event - Unregister filter event.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter id.
+ * @event: Event to unregister.
+ * @chan_mask: Mask of channels associated to filter.
+ *
+ * The function disables associated IRQ.
+ */
+int stm32_dfsdm_unregister_fl_event(struct stm32_dfsdm *dfsdm,
+				    unsigned int fl_id,
+				    enum stm32_dfsdm_events event,
+				    unsigned int chan_mask)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+	unsigned long flags, ulmask = chan_mask;
+	int i;
+
+	dev_dbg(&priv->pdev->dev, "%s:for filter %d: event %#x ch_mask %#x\n",
+		__func__, fl_id, event, chan_mask);
+
+	if (event > DFSDM_EVENT_CKA)
+		return -EINVAL;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	/* Disable interrupts */
+	switch (event) {
+	case DFSDM_EVENT_SCD:
+		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
+			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
+					  DFSDM_CHCFGR1_SCDEN_MASK,
+					  DFSDM_CHCFGR1_SCDEN(0));
+		}
+		priv->scd_filter_mask &= ~BIT(fl_id);
+		if (!priv->scd_filter_mask)
+			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
+					  DFSDM_CR2_SCDIE_MASK,
+					  DFSDM_CR2_SCDIE(0));
+		break;
+	case DFSDM_EVENT_CKA:
+		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
+			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
+					  DFSDM_CHCFGR1_CKABEN_MASK,
+					  DFSDM_CHCFGR1_CKABEN(0));
+		}
+		priv->ckab_filter_mask &= ~BIT(fl_id);
+		if (!priv->ckab_filter_mask)
+			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
+					  DFSDM_CR2_CKABIE_MASK,
+					  DFSDM_CR2_CKABIE(0));
+		break;
+	default:
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(fl_id), event, 0);
+	}
+
+	priv->filters[fl_id].event_mask &= ~event;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dfsdm_unregister_fl_event);
+
+/*
+ * Channels
+ */
+static void stm32_dfsdm_init_channel(struct dfsdm_priv *priv,
+				     struct stm32_dfsdm_channel *ch)
+{
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
+			  DFSDM_CHCFGR1_DATMPX_MASK,
+			  DFSDM_CHCFGR1_DATMPX(ch->type.source));
+	if (ch->type.source == DFSDM_CHANNEL_EXTERNAL_INPUTS) {
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
+				  DFSDM_CHCFGR1_SITP_MASK,
+				  DFSDM_CHCFGR1_SITP(ch->serial_if.type));
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
+				  DFSDM_CHCFGR1_SPICKSEL_MASK,
+				DFSDM_CHCFGR1_SPICKSEL(ch->serial_if.spi_clk));
+	}
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
+			  DFSDM_CHCFGR1_DATPACK_MASK,
+			  DFSDM_CHCFGR1_DATPACK(ch->type.DataPacking));
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
+			  DFSDM_CHCFGR1_CHINSEL_MASK,
+			  DFSDM_CHCFGR1_CHINSEL(ch->serial_if.pins));
+}
+
+/**
+ * stm32_dfsdm_start_channel - Configure and activate DFSDM channel.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @ch: Filter id.
+ * @cfg: Filter configuration.
+ */
+int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id,
+			      struct stm32_dfsdm_ch_cfg *cfg)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv,
+					       dfsdm);
+	struct regmap *reg = priv->regmap;
+	int ret;
+
+	dev_dbg(&priv->pdev->dev, "%s: for channel %d\n", __func__, ch_id);
+
+	ret = stm32_dfsdm_start_dfsdm(priv);
+	if (ret < 0)
+		return ret;
+
+	DFSDM_UPDATE_BITS(reg, DFSDM_CHCFGR2(ch_id), DFSDM_CHCFGR2_DTRBS_MASK,
+			  DFSDM_CHCFGR2_DTRBS(cfg->right_bit_shift));
+	DFSDM_UPDATE_BITS(reg, DFSDM_CHCFGR2(ch_id), DFSDM_CHCFGR2_OFFSET_MASK,
+			  DFSDM_CHCFGR2_OFFSET(cfg->offset));
+
+	DFSDM_UPDATE_BITS(reg, DFSDM_CHCFGR1(ch_id), DFSDM_CHCFGR1_CHEN_MASK,
+			  DFSDM_CHCFGR1_CHEN(1));
+
+	/* Clear absence detection IRQ */
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_ICR(0),
+			  DFSDM_ICR_CLRCKABF_CH_MASK(ch_id),
+			  DFSDM_ICR_CLRCKABF_CH(1, ch_id));
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dfsdm_start_channel);
+
+/**
+ * stm32_dfsdm_stop_channel - Deactivate channel.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @ch_id: DFSDM channel identifier.
+ */
+void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+
+	dev_dbg(&priv->pdev->dev, "%s:for channel %d\n", __func__, ch_id);
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch_id),
+			  DFSDM_CHCFGR1_CHEN_MASK,
+			  DFSDM_CHCFGR1_CHEN(0));
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch_id),
+			  DFSDM_CHCFGR1_CKABEN_MASK, DFSDM_CHCFGR1_CKABEN(0));
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch_id),
+			  DFSDM_CHCFGR1_SCDEN_MASK, DFSDM_CHCFGR1_SCDEN(0));
+
+	stm32_dfsdm_stop_dfsdm(priv);
+}
+EXPORT_SYMBOL_GPL(dfsdm_stop_channel);
+
+/**
+ * stm32_dfsdm_get_channel - Get channel instance.
+ *
+ * @dfsdm: handle used to retrieve dfsdm context.
+ * @ch: DFSDM channel hardware parameters.
+ *
+ * Reserve DFSDM channel resource.
+ */
+int stm32_dfsdm_get_channel(struct stm32_dfsdm *dfsdm,
+			    struct stm32_dfsdm_channel *ch)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+	unsigned int id = ch->id;
+
+	dev_dbg(&priv->pdev->dev, "%s:get channel %d\n", __func__, id);
+
+	if (id >= priv->dfsdm.max_channels) {
+		dev_err(&priv->pdev->dev, "channel (%d) is not valid\n", id);
+		return -EINVAL;
+	}
+
+	if ((ch->type.source != DFSDM_CHANNEL_EXTERNAL_INPUTS) &
+	    (ch->serial_if.spi_clk != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL) &
+	    (!priv->clkout_freq_req)) {
+		dev_err(&priv->pdev->dev, "clkout not present\n");
+		return -EINVAL;
+	}
+
+	spin_lock(&priv->lock);
+	if (!(BIT(id) & priv->free_channel_mask)) {
+		spin_unlock(&priv->lock);
+		dev_err(&priv->pdev->dev, "channel (%d) already in use\n", id);
+		return -EBUSY;
+	}
+
+	priv->free_channel_mask &= ~BIT(id);
+	priv->channels[id] = *ch;
+	spin_unlock(&priv->lock);
+
+	dev_dbg(&priv->pdev->dev, "%s: new mask %#x\n", __func__,
+		priv->free_channel_mask);
+
+	/**
+	 * Check clock constrainst between clkout and either
+	 * dfsdm/audio clock:
+	 * - In SPI mode (clkout is used): Fclk >= 4 * Fclkout
+	 *   (e.g. CKOUTDIV >= 3)
+	 * - In mancherster mode: Fclk >= 6 * Fclkout
+	 */
+	switch (ch->serial_if.type) {
+	case DFSDM_CHANNEL_SPI_RISING:
+	case DFSDM_CHANNEL_SPI_FALLING:
+		if (priv->clkout_div && priv->clkout_div < 3)
+			dev_warn(&priv->pdev->dev,
+				 "Clock div should be higher than 3\n");
+		break;
+	case DFSDM_CHANNEL_MANCHESTER_RISING:
+	case DFSDM_CHANNEL_MANCHESTER_FALLING:
+		if (priv->clkout_div && priv->clkout_div < 5)
+			dev_warn(&priv->pdev->dev,
+				 "Clock div should be higher than 5\n");
+		break;
+	}
+
+	stm32_dfsdm_init_channel(priv, ch);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dfsdm_get_channel);
+
+/**
+ * stm32_dfsdm_release_channel - Release channel instance.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @ch_id: DFSDM channel identifier.
+ *
+ * Free the DFSDM channel resource.
+ */
+void stm32_dfsdm_release_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+
+	spin_lock(&priv->lock);
+	priv->free_channel_mask |= BIT(ch_id);
+	spin_unlock(&priv->lock);
+}
+EXPORT_SYMBOL_GPL(dfsdm_release_channel);
+
+/**
+ * stm32_dfsdm_get_clk_out_rate - get clkout frequency.
+ *
+ * @dfsdm: handle used to retrieve dfsdm context.
+ * @rate: clock out rate in Hz.
+ *
+ * Provide output frequency used for external ADC.
+ * return EINVAL if clockout is not used else return 0.
+ */
+int stm32_dfsdm_get_clk_out_rate(struct stm32_dfsdm *dfsdm, unsigned long *rate)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+	unsigned long int clk_rate;
+
+	if (!priv->clkout_div)
+		return -EINVAL;
+
+	clk_rate = clk_get_rate(priv->aclk ? priv->aclk : priv->clk);
+	*rate = clk_rate / (priv->clkout_div + 1);
+	dev_dbg(&priv->pdev->dev, "%s: clkout: %ld (Hz)\n", __func__, *rate);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dfsdm_get_clk_out_rate);
+
+static int stm32_dfsdm_parse_of(struct platform_device *pdev,
+				struct dfsdm_priv *priv)
+{
+	struct device_node *node = pdev->dev.of_node;
+	struct resource *res;
+	int ret, val;
+
+	if (!node)
+		return -EINVAL;
+
+	/* Get resources */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "Failed to get memory resource\n");
+		return -ENODEV;
+	}
+	priv->phys_base = res->start;
+	priv->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(priv->base))
+		return PTR_ERR(priv->base);
+
+	ret = of_property_read_u32(node, "st,clkout-freq", &val);
+	if (!ret) {
+		if (!val) {
+			dev_err(&priv->pdev->dev,
+				"st,clkout-freq cannot be 0\n");
+			return -EINVAL;
+		}
+		priv->clkout_freq_req = val;
+	} else if (ret != -EINVAL) {
+		dev_err(&priv->pdev->dev, "Failed to get st,clkout-freq\n");
+		return ret;
+	}
+
+	/* Source clock */
+	priv->clk = devm_clk_get(&pdev->dev, "dfsdm_clk");
+	if (IS_ERR(priv->clk)) {
+		dev_err(&pdev->dev, "No stm32_dfsdm_clk clock found\n");
+		return -EINVAL;
+	}
+
+	priv->aclk = devm_clk_get(&pdev->dev, "audio_clk");
+	if (IS_ERR(priv->aclk))
+		priv->aclk = NULL;
+
+	return 0;
+};
+
+static const struct of_device_id stm32_dfsdm_of_match[] = {
+	{
+		.compatible = "st,stm32h7-dfsdm",
+		.data = &stm32h7_data
+	},
+	{}
+};
+MODULE_DEVICE_TABLE(of, stm32_dfsdm_of_match);
+
+static int stm32_dfsdm_remove(struct platform_device *pdev)
+{
+	of_platform_depopulate(&pdev->dev);
+
+	return 0;
+}
+
+static int stm32_dfsdm_probe(struct platform_device *pdev)
+{
+	struct dfsdm_priv *priv;
+	struct device_node *pnode = pdev->dev.of_node;
+	const struct of_device_id *of_id;
+	const struct stm32_dev_data *dev_data;
+	enum dfsdm_clkout_src clk_src;
+	int ret, i;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->pdev = pdev;
+
+	/* Populate data structure depending on compatibility */
+	of_id = of_match_node(stm32_dfsdm_of_match, pnode);
+	if (!of_id->data) {
+		dev_err(&pdev->dev, "Data associated to device is missing\n");
+		return -EINVAL;
+	}
+
+	dev_data = (const struct stm32_dev_data *)of_id->data;
+
+	ret = stm32_dfsdm_parse_of(pdev, priv);
+	if (ret < 0)
+		return ret;
+
+	priv->regmap = devm_regmap_init_mmio(&pdev->dev, priv->base,
+					    dev_data->regmap_cfg);
+	if (IS_ERR(priv->regmap)) {
+		ret = PTR_ERR(priv->regmap);
+		dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	priv->dfsdm = dev_data->dfsdm;
+
+	priv->filters = devm_kcalloc(&pdev->dev, dev_data->dfsdm.max_filters,
+				     sizeof(*priv->filters), GFP_KERNEL);
+	if (IS_ERR(priv->filters)) {
+		ret = PTR_ERR(priv->filters);
+		goto probe_err;
+	}
+
+	for (i = 0; i < dev_data->dfsdm.max_filters; i++) {
+		struct filter_params *params = &priv->filters[i];
+
+		params->id = i;
+		params->irq = platform_get_irq(pdev, i);
+		if (params->irq < 0) {
+			dev_err(&pdev->dev, "Failed to get IRQ resource\n");
+			ret = params->irq;
+			goto probe_err;
+		}
+
+		ret = devm_request_irq(&pdev->dev, params->irq, stm32_dfsdm_irq,
+				       0, dev_name(&pdev->dev), params);
+		if (ret) {
+			dev_err(&pdev->dev, "Failed to register interrupt\n");
+			goto probe_err;
+		}
+
+		params->priv = priv;
+	}
+
+	priv->channels = devm_kcalloc(&pdev->dev, priv->dfsdm.max_channels,
+				      sizeof(*priv->channels), GFP_KERNEL);
+	if (IS_ERR(priv->channels)) {
+		ret = PTR_ERR(priv->channels);
+		goto probe_err;
+	}
+	priv->free_filter_mask = BIT(priv->dfsdm.max_filters) - 1;
+	priv->free_channel_mask = BIT(priv->dfsdm.max_channels) - 1;
+
+	platform_set_drvdata(pdev, &priv->dfsdm);
+	spin_lock_init(&priv->lock);
+
+	priv->clkout_div = stm32_dfsdm_get_clkout_divider(priv,
+						    clk_get_rate(priv->clk));
+
+	ret = of_platform_populate(pnode, NULL, NULL, &pdev->dev);
+	if (ret < 0)
+		goto probe_err;
+
+	clk_src = priv->aclk ? AUDIO_CLK : DFSDM_CLK;
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
+			  DFSDM_CHCFGR1_CKOUTSRC_MASK,
+			  DFSDM_CHCFGR1_CKOUTSRC(clk_src));
+	return 0;
+
+probe_err:
+	return ret;
+}
+
+static struct platform_driver stm32_dfsdm_driver = {
+	.probe = stm32_dfsdm_probe,
+	.remove = stm32_dfsdm_remove,
+	.driver = {
+		.name = "stm32-dfsdm",
+		.of_match_table = stm32_dfsdm_of_match,
+	},
+};
+
+module_platform_driver(stm32_dfsdm_driver);
+
+MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 dfsdm driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/stm32-dfsdm.h b/include/linux/mfd/stm32-dfsdm.h
new file mode 100644
index 0000000..f6eb788
--- /dev/null
+++ b/include/linux/mfd/stm32-dfsdm.h
@@ -0,0 +1,324 @@
+/*
+ * This file is part of STM32 DFSDM mfd driver API
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author(s): Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>.
+ *
+ * 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.
+ */
+#ifndef MDF_STM32_DFSDM_H
+#define MDF_STM32_DFSDM_H
+
+/*
+ * Channel definitions
+ */
+#define DFSDM_CHANNEL_0    BIT(0)
+#define DFSDM_CHANNEL_1    BIT(1)
+#define DFSDM_CHANNEL_2    BIT(2)
+#define DFSDM_CHANNEL_3    BIT(3)
+#define DFSDM_CHANNEL_4    BIT(4)
+#define DFSDM_CHANNEL_5    BIT(5)
+#define DFSDM_CHANNEL_6    BIT(6)
+#define DFSDM_CHANNEL_7    BIT(7)
+
+/* DFSDM channel input data packing */
+enum stm32_dfsdm_data_packing {
+	DFSDM_CHANNEL_STANDARD_MODE,    /* Standard data packing mode */
+	DFSDM_CHANNEL_INTERLEAVED_MODE, /* Interleaved data packing mode */
+	DFSDM_CHANNEL_DUAL_MODE         /* Dual data packing mode */
+};
+
+/* DFSDM channel input multiplexer */
+enum stm32_dfsdm_input_multiplexer {
+	DFSDM_CHANNEL_EXTERNAL_INPUTS,    /* Data taken from external inputs */
+	DFSDM_CHANNEL_INTERNAL_ADC,       /* Data taken from internal ADC */
+	DFSDM_CHANNEL_INTERNAL_REGISTER,  /* Data taken from register */
+};
+
+/* DFSDM channel serial interface type */
+enum stm32_dfsdm_serial_in_type {
+	DFSDM_CHANNEL_SPI_RISING,         /* SPI with rising edge */
+	DFSDM_CHANNEL_SPI_FALLING,        /* SPI with falling edge */
+	DFSDM_CHANNEL_MANCHESTER_RISING,  /* Manchester with rising edge */
+	DFSDM_CHANNEL_MANCHESTER_FALLING, /* Manchester with falling edge */
+};
+
+/* DFSDM channel serial spi clock source */
+enum stm32_dfsdm_spi_clk_src {
+	/* External SPI clock */
+	DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL,
+	/* Internal SPI clock */
+	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL,
+	/* Internal SPI clock divided by 2, falling edge */
+	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING,
+	/* Internal SPI clock divided by 2, rising edge */
+	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING
+};
+
+/* DFSDM channel input pins */
+enum stm32_dfsdm_serial_in_select {
+	/* Serial input taken from pins of the same channel (y) */
+	DFSDM_CHANNEL_SAME_CHANNEL_PINS,
+	/* Serial input taken from pins of the following channel (y + 1)*/
+	DFSDM_CHANNEL_NEXT_CHANNEL_PINS,
+};
+
+/**
+ * struct stm32_dfsdm_input_type - DFSDM channel init structure definition.
+ * @DataPacking: Standard, interleaved or dual mode for internal register.
+ * @source: channel source: internal DAC, serial input or memory.
+ */
+struct stm32_dfsdm_input_type {
+	enum stm32_dfsdm_data_packing DataPacking;
+	enum stm32_dfsdm_input_multiplexer source;
+};
+
+/**
+ * struct stm32_dfsdm_serial_if - DFSDM serial interface parameters.
+ * @type:	Serial interface type.
+ * @spi_clk:	SPI clock source.
+ * @pins:	select serial interface associated to the channel
+ */
+struct stm32_dfsdm_serial_if {
+	enum stm32_dfsdm_serial_in_type type;
+	enum stm32_dfsdm_spi_clk_src spi_clk;
+	enum stm32_dfsdm_serial_in_select pins;
+};
+
+/**
+ * struct stm32_dfsdm_channel - DFSDM channel hardware parameters.
+ * @id:		DFSDM channel identifier.
+ * @type:	DFSDM channel input parameters.
+ * @serial_if:	DFSDM channel serial interface parameters.
+ *		Mandatory for DFSDM_CHANNEL_EXTERNAL_INPUTS.
+ */
+struct stm32_dfsdm_channel {
+	unsigned int id;
+	struct stm32_dfsdm_input_type type;
+	struct stm32_dfsdm_serial_if serial_if;
+};
+
+/**
+ * struct stm32_dfsdm_ch_cfg - DFSDM channel config.
+ * @offset:		DFSDM channel 24 bit calibration offset.
+ * @right_bit_shift:	DFSDM channel right bit shift of the data result.
+ */
+struct stm32_dfsdm_ch_cfg {
+	unsigned int offset;
+	unsigned int right_bit_shift;
+};
+
+/*
+ * Filter definitions
+ */
+
+#define DFSDM_MIN_INT_OVERSAMPLING 1
+#define DFSDM_MAX_INT_OVERSAMPLING 256
+#define DFSDM_MIN_FL_OVERSAMPLING 1
+#define DFSDM_MAX_FL_OVERSAMPLING 1024
+
+enum stm32_dfsdm_events {
+	DFSDM_EVENT_INJ_EOC =	BIT(0), /* Injected end of conversion event */
+	DFSDM_EVENT_REG_EOC =	BIT(1), /* Regular end of conversion event */
+	DFSDM_EVENT_INJ_XRUN =	BIT(2), /* Injected conversion overrun event */
+	DFSDM_EVENT_REG_XRUN =	BIT(3), /* Regular conversion overrun event */
+	DFSDM_EVENT_AWD =	BIT(4), /* Analog watchdog event */
+	DFSDM_EVENT_SCD =	BIT(5), /* Short circuit detector event */
+	DFSDM_EVENT_CKA =	BIT(6), /* Clock abscence detection event */
+};
+
+#define STM32_DFSDM_EVENT_MASK 0x3F
+
+/* DFSDM filter order  */
+enum stm32_dfsdm_sinc_order {
+	DFSDM_FASTSINC_ORDER, /* FastSinc filter type */
+	DFSDM_SINC1_ORDER,    /* Sinc 1 filter type */
+	DFSDM_SINC2_ORDER,    /* Sinc 2 filter type */
+	DFSDM_SINC3_ORDER,    /* Sinc 3 filter type */
+	DFSDM_SINC4_ORDER,    /* Sinc 4 filter type (N.A. for watchdog) */
+	DFSDM_SINC5_ORDER,    /* Sinc 5 filter type (N.A. for watchdog) */
+	DFSDM_NB_SINC_ORDER,
+};
+
+/* DFSDM filter order */
+enum stm32_dfsdm_state {
+	DFSDM_DISABLE,
+	DFSDM_ENABLE,
+};
+
+/**
+ * struct stm32_dfsdm_sinc_filter - DFSDM Sinc filter structure definition
+ * @order: DFSM filter order.
+ * @oversampling: DFSDM filter oversampling:
+ *		  post processing filter: min = 1, max = 1024.
+ */
+struct stm32_dfsdm_sinc_filter {
+	enum stm32_dfsdm_sinc_order order;
+	unsigned int oversampling;
+};
+
+/* DFSDM filter conversion trigger */
+enum stm32_dfsdm_trigger {
+	DFSDM_FILTER_SW_TRIGGER,   /* Software trigger */
+	DFSDM_FILTER_SYNC_TRIGGER, /* Synchronous with DFSDM0 */
+	DFSDM_FILTER_EXT_TRIGGER,  /* External trigger (only for injected) */
+};
+
+/* DFSDM filter external trigger polarity */
+enum stm32_dfsdm_filter_ext_trigger_pol {
+	DFSDM_FILTER_EXT_TRIG_NO_TRIG,      /* Trigger disable */
+	DFSDM_FILTER_EXT_TRIG_RISING_EDGE,  /* Rising edge */
+	DFSDM_FILTER_EXT_TRIG_FALLING_EDGE, /* Falling edge */
+	DFSDM_FILTER_EXT_TRIG_BOTH_EDGES,   /* Rising and falling edges */
+};
+
+/* DFSDM filter conversion type */
+enum stm32_dfsdm_conv_type {
+	DFSDM_FILTER_REG_CONV,      /* Regular conversion */
+	DFSDM_FILTER_SW_INJ_CONV,   /* Injected conversion */
+	DFSDM_FILTER_TRIG_INJ_CONV, /* Injected conversion */
+};
+
+/* DFSDM filter regular synchronous mode */
+enum stm32_dfsdm_conv_rsync {
+	DFSDM_FILTER_RSYNC_OFF, /* regular conversion asynchronous */
+	DFSDM_FILTER_RSYNC_ON,  /* regular conversion synchronous with filter0*/
+};
+
+/**
+ * struct stm32_dfsdm_regular - DFSDM filter conversion parameters structure
+ * @ch_src:	Channel source from 0 to 7.
+ * @fast_mode:	Enable/disable fast mode for regular conversion.
+ * @dma_mode:	Enable/disable dma mode.
+ * @cont_mode	Enable/disable continuous conversion.
+ * @sync_mode	Enable/disable synchro mode.
+ */
+struct stm32_dfsdm_regular {
+	unsigned int ch_src;
+	bool fast_mode;
+	bool dma_mode;
+	bool cont_mode;
+	bool sync_mode;
+};
+
+/**
+ * struct stm32_dfsdm_injected - DFSDM filter  conversion parameters structure
+ * @trigger:	Trigger used to start injected conversion.
+ * @trig_src:	External trigger, 0 to 30 (refer to datasheet for details).
+ * @trig_pol:	External trigger edge: software, rising, falling or both.
+ * @scan_mode:	Enable/disable scan mode for injected conversion.
+ * @ch_group:	mask containing channels to scan ( set bit y to scan
+ *		channel y).
+ * @dma_mode:	DFSDM channel input parameters.
+ */
+struct stm32_dfsdm_injected {
+	enum stm32_dfsdm_trigger trigger;
+	unsigned int trig_src;
+	enum stm32_dfsdm_filter_ext_trigger_pol trig_pol;
+	bool scan_mode;
+	unsigned int ch_group;
+	bool dma_mode;
+};
+
+struct stm32_dfsdm;
+
+/**
+ * struct stm32_dfsdm_fl_event - DFSDM filters event
+ * @cb:	User event callback with parameters. be carful this function
+ *		is called under threaded IRQ context:
+ *			struct stm32_dfsdm *dfsdm: dfsdm handle,
+ *			unsigned int fl_id: filter id,
+ *			num stm32_dfsdm_events flag: event,
+ *			param: parameter associated to the event,
+ *			void *context: user context provided on registration.
+ * @context: User param to retrieve context.
+ */
+struct stm32_dfsdm_fl_event {
+	void (*cb)(struct stm32_dfsdm *, int, enum stm32_dfsdm_events,
+		   unsigned int, void *);
+	void *context;
+};
+
+/**
+ * struct stm32_dfsdm_filter - DFSDM filter  conversion parameters structure
+ * @reg_params:		DFSDM regular conversion parameters.
+ *			this param is optional and not taken into account if
+ *			@inj_params is defined.
+ * @inj_params:		DFSDM injected conversion parameters (optional).
+ * @filter_params:	DFSDM filter parameters.
+ * @event:		Events callback.
+ * @int_oversampling:	Integrator oversampling ratio for average purpose
+ *			(range from 1 to 256).
+ * @ext_det_ch_mask:	Extreme detector mask for channel selection
+ *			mask generated using DFSDM_CHANNEL_0 to
+ *			DFSDM_CHANNEL_7. If 0 feature is disable.
+ */
+struct stm32_dfsdm_filter {
+	struct stm32_dfsdm_regular *reg_params;
+	struct stm32_dfsdm_injected *inj_params;
+	struct stm32_dfsdm_sinc_filter sinc_params;
+	struct stm32_dfsdm_fl_event event;
+	unsigned int int_oversampling;
+};
+
+/**
+ * struct stm32_dfsdm - DFSDM context structure.
+ *
+ * @trig_info: Trigger name and id available last member name is null.
+ * @max_channels: max number of channels available.
+ * @max_filters: max number of filters available.
+ *
+ * Notice That structure is filled by mdf driver and must not be updated by
+ * user.
+ */
+struct stm32_dfsdm {
+	unsigned int max_channels;
+	unsigned int max_filters;
+};
+
+int stm32_dfsdm_get_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
+void stm32_dfsdm_release_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
+
+dma_addr_t stm32_dfsdm_get_filter_dma_phy_addr(struct stm32_dfsdm *dfsdm,
+					       unsigned int fl_id,
+					       enum stm32_dfsdm_conv_type conv);
+
+int stm32_dfsdm_configure_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+				 struct stm32_dfsdm_filter *filter);
+void stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+			      enum stm32_dfsdm_conv_type conv);
+void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
+
+void stm32_dfsdm_read_fl_conv(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+			      u32 *val, int *ch_id,
+			      enum stm32_dfsdm_conv_type type);
+
+int stm32_dfsdm_unregister_fl_event(struct stm32_dfsdm *dfsdm,
+				    unsigned int fl_id,
+				    enum stm32_dfsdm_events event,
+				    unsigned int ch_mask);
+int stm32_dfsdm_register_fl_event(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+				  enum stm32_dfsdm_events event,
+				  unsigned int ch_mask);
+
+int stm32_dfsdm_get_channel(struct stm32_dfsdm *dfsdm,
+			    struct stm32_dfsdm_channel *ch);
+void stm32_dfsdm_release_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
+
+int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id,
+			      struct stm32_dfsdm_ch_cfg *cfg);
+void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
+
+int stm32_dfsdm_get_clk_out_rate(struct stm32_dfsdm *dfsdm,
+				 unsigned long *rate);
+
+#endif
-- 
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] 130+ messages in thread

* [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-23 16:32     ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-23 16:32 UTC (permalink / raw)
  To: devicetree, linux-arm-kernel, linux-iio, alsa-devel, Lee Jones,
	Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre Torgue, arnaud.pouliquen

DFSDM hardware IP can be used at the same time for ADC sigma delta
conversion and audio PDM microphone.
MFD driver is in charge of configuring IP registers and managing IP clocks.
For this it exports an API to handles filters and channels resources.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 drivers/mfd/Kconfig             |   11 +
 drivers/mfd/Makefile            |    2 +
 drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
 drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
 include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
 5 files changed, 1601 insertions(+)
 create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
 create mode 100644 drivers/mfd/stm32-dfsdm.c
 create mode 100644 include/linux/mfd/stm32-dfsdm.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index c6df644..4bb660b 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1607,6 +1607,17 @@ config MFD_STW481X
 	  in various ST Microelectronics and ST-Ericsson embedded
 	  Nomadik series.
 
+config MFD_STM32_DFSDM
+	tristate "ST Microelectronics STM32 DFSDM"
+	depends on (ARCH_STM32 && OF) || COMPILE_TEST
+	select MFD_CORE
+	select REGMAP
+	select REGMAP_MMIO
+	help
+	  Select this option to enable the STM32 Digital Filter
+	  for Sigma Delta Modulators (DFSDM) driver used
+	  in various STM32 series.
+
 menu "Multimedia Capabilities Port drivers"
 	depends on ARCH_SA1100
 
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 9834e66..1f095e5 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -211,3 +211,5 @@ obj-$(CONFIG_INTEL_SOC_PMIC)	+= intel-soc-pmic.o
 obj-$(CONFIG_MFD_MT6397)	+= mt6397-core.o
 
 obj-$(CONFIG_MFD_ALTERA_A10SR)	+= altera-a10sr.o
+
+obj-$(CONFIG_MFD_STM32_DFSDM)	+= stm32-dfsdm.o
\ No newline at end of file
diff --git a/drivers/mfd/stm32-dfsdm-reg.h b/drivers/mfd/stm32-dfsdm-reg.h
new file mode 100644
index 0000000..05ff702
--- /dev/null
+++ b/drivers/mfd/stm32-dfsdm-reg.h
@@ -0,0 +1,220 @@
+/*
+ * This file is part of STM32 DFSDM mfd driver
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com>.
+ *
+ * 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.
+ */
+
+#ifndef MDF_STM32_DFSDM_REG_H
+#define MDF_STM32_DFSDM_REG_H
+
+#include <linux/bitfield.h>
+/*
+ * Channels definitions
+ */
+#define DFSDM_CHCFGR1(y)  ((y) * 0x20 + 0x00)
+#define DFSDM_CHCFGR2(y)  ((y) * 0x20 + 0x04)
+#define DFSDM_AWSCDR(y)   ((y) * 0x20 + 0x08)
+#define DFSDM_CHWDATR(y)  ((y) * 0x20 + 0x0C)
+#define DFSDM_CHDATINR(y) ((y) * 0x20 + 0x10)
+
+/* CHCFGR1: Channel configuration register 1 */
+#define DFSDM_CHCFGR1_SITP_MASK     GENMASK(1, 0)
+#define DFSDM_CHCFGR1_SITP(v)       FIELD_PREP(DFSDM_CHCFGR1_SITP_MASK, v)
+#define DFSDM_CHCFGR1_SPICKSEL_MASK GENMASK(3, 2)
+#define DFSDM_CHCFGR1_SPICKSEL(v)   FIELD_PREP(DFSDM_CHCFGR1_SPICKSEL_MASK, v)
+#define DFSDM_CHCFGR1_SCDEN_MASK    BIT(5)
+#define DFSDM_CHCFGR1_SCDEN(v)      FIELD_PREP(DFSDM_CHCFGR1_SCDEN_MASK, v)
+#define DFSDM_CHCFGR1_CKABEN_MASK   BIT(6)
+#define DFSDM_CHCFGR1_CKABEN(v)     FIELD_PREP(DFSDM_CHCFGR1_CKABEN_MASK, v)
+#define DFSDM_CHCFGR1_CHEN_MASK     BIT(7)
+#define DFSDM_CHCFGR1_CHEN(v)       FIELD_PREP(DFSDM_CHCFGR1_CHEN_MASK, v)
+#define DFSDM_CHCFGR1_CHINSEL_MASK  BIT(8)
+#define DFSDM_CHCFGR1_CHINSEL(v)    FIELD_PREP(DFSDM_CHCFGR1_CHINSEL_MASK, v)
+#define DFSDM_CHCFGR1_DATMPX_MASK   GENMASK(13, 12)
+#define DFSDM_CHCFGR1_DATMPX(v)     FIELD_PREP(DFSDM_CHCFGR1_DATMPX_MASK, v)
+#define DFSDM_CHCFGR1_DATPACK_MASK  GENMASK(15, 14)
+#define DFSDM_CHCFGR1_DATPACK(v)    FIELD_PREP(DFSDM_CHCFGR1_DATPACK_MASK, v)
+#define DFSDM_CHCFGR1_CKOUTDIV_MASK GENMASK(23, 16)
+#define DFSDM_CHCFGR1_CKOUTDIV(v)   FIELD_PREP(DFSDM_CHCFGR1_CKOUTDIV_MASK, v)
+#define DFSDM_CHCFGR1_CKOUTSRC_MASK BIT(30)
+#define DFSDM_CHCFGR1_CKOUTSRC(v)   FIELD_PREP(DFSDM_CHCFGR1_CKOUTSRC_MASK, v)
+#define DFSDM_CHCFGR1_DFSDMEN_MASK  BIT(31)
+#define DFSDM_CHCFGR1_DFSDMEN(v)    FIELD_PREP(DFSDM_CHCFGR1_DFSDMEN_MASK, v)
+
+/* CHCFGR2: Channel configuration register 2 */
+#define DFSDM_CHCFGR2_DTRBS_MASK    GENMASK(7, 3)
+#define DFSDM_CHCFGR2_DTRBS(v)      FIELD_PREP(DFSDM_CHCFGR2_DTRBS_MASK, v)
+#define DFSDM_CHCFGR2_OFFSET_MASK   GENMASK(31, 8)
+#define DFSDM_CHCFGR2_OFFSET(v)     FIELD_PREP(DFSDM_CHCFGR2_OFFSET_MASK, v)
+
+/* AWSCDR: Channel analog watchdog and short circuit detector */
+#define DFSDM_AWSCDR_SCDT_MASK    GENMASK(7, 0)
+#define DFSDM_AWSCDR_SCDT(v)      FIELD_PREP(DFSDM_AWSCDR_SCDT_MASK, v)
+#define DFSDM_AWSCDR_BKSCD_MASK   GENMASK(15, 12)
+#define DFSDM_AWSCDR_BKSCD(v)	  FIELD_PREP(DFSDM_AWSCDR_BKSCD_MASK, v)
+#define DFSDM_AWSCDR_AWFOSR_MASK  GENMASK(20, 16)
+#define DFSDM_AWSCDR_AWFOSR(v)    FIELD_PREP(DFSDM_AWSCDR_AWFOSR_MASK, v)
+#define DFSDM_AWSCDR_AWFORD_MASK  GENMASK(23, 22)
+#define DFSDM_AWSCDR_AWFORD(v)    FIELD_PREP(DFSDM_AWSCDR_AWFORD_MASK, v)
+
+/*
+ * Filters definitions
+ */
+#define DFSDM_FILTER_BASE_ADR		0x100
+#define DFSDM_FILTER_REG_MASK		0x7F
+#define DFSDM_FILTER_X_BASE_ADR(x)	((x) * 0x80 + DFSDM_FILTER_BASE_ADR)
+
+#define DFSDM_CR1(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x00)
+#define DFSDM_CR2(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x04)
+#define DFSDM_ISR(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x08)
+#define DFSDM_ICR(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x0C)
+#define DFSDM_JCHGR(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x10)
+#define DFSDM_FCR(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x14)
+#define DFSDM_JDATAR(x)  (DFSDM_FILTER_X_BASE_ADR(x)  + 0x18)
+#define DFSDM_RDATAR(x)  (DFSDM_FILTER_X_BASE_ADR(x)  + 0x1C)
+#define DFSDM_AWHTR(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x20)
+#define DFSDM_AWLTR(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x24)
+#define DFSDM_AWSR(x)    (DFSDM_FILTER_X_BASE_ADR(x)  + 0x28)
+#define DFSDM_AWCFR(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x2C)
+#define DFSDM_EXMAX(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x30)
+#define DFSDM_EXMIN(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x34)
+#define DFSDM_CNVTIMR(x) (DFSDM_FILTER_X_BASE_ADR(x)  + 0x38)
+
+/* CR1 Control register 1 */
+#define DFSDM_CR1_DFEN_MASK	BIT(0)
+#define DFSDM_CR1_DFEN(v)	FIELD_PREP(DFSDM_CR1_DFEN_MASK, v)
+#define DFSDM_CR1_JSWSTART_MASK	BIT(1)
+#define DFSDM_CR1_JSWSTART(v)	FIELD_PREP(DFSDM_CR1_JSWSTART_MASK, v)
+#define DFSDM_CR1_JSYNC_MASK	BIT(3)
+#define DFSDM_CR1_JSYNC(v)	FIELD_PREP(DFSDM_CR1_JSYNC_MASK, v)
+#define DFSDM_CR1_JSCAN_MASK	BIT(4)
+#define DFSDM_CR1_JSCAN(v)	FIELD_PREP(DFSDM_CR1_JSCAN_MASK, v)
+#define DFSDM_CR1_JDMAEN_MASK	BIT(5)
+#define DFSDM_CR1_JDMAEN(v)	FIELD_PREP(DFSDM_CR1_JDMAEN_MASK, v)
+#define DFSDM_CR1_JEXTSEL_MASK	GENMASK(12, 8)
+#define DFSDM_CR1_JEXTSEL(v)	FIELD_PREP(DFSDM_CR1_JEXTSEL_MASK, v)
+#define DFSDM_CR1_JEXTEN_MASK	GENMASK(14, 13)
+#define DFSDM_CR1_JEXTEN(v)	FIELD_PREP(DFSDM_CR1_JEXTEN_MASK, v)
+#define DFSDM_CR1_RSWSTART_MASK	BIT(17)
+#define DFSDM_CR1_RSWSTART(v)	FIELD_PREP(DFSDM_CR1_RSWSTART_MASK, v)
+#define DFSDM_CR1_RCONT_MASK	BIT(18)
+#define DFSDM_CR1_RCONT(v)	FIELD_PREP(DFSDM_CR1_RCONT_MASK, v)
+#define DFSDM_CR1_RSYNC_MASK	BIT(19)
+#define DFSDM_CR1_RSYNC(v)	FIELD_PREP(DFSDM_CR1_RSYNC_MASK, v)
+#define DFSDM_CR1_RDMAEN_MASK	BIT(21)
+#define DFSDM_CR1_RDMAEN(v)	FIELD_PREP(DFSDM_CR1_RDMAEN_MASK, v)
+#define DFSDM_CR1_RCH_MASK	GENMASK(26, 24)
+#define DFSDM_CR1_RCH(v)	FIELD_PREP(DFSDM_CR1_RCH_MASK, v)
+#define DFSDM_CR1_FAST_MASK	BIT(29)
+#define DFSDM_CR1_FAST(v)	FIELD_PREP(DFSDM_CR1_FAST_MASK, v)
+#define DFSDM_CR1_AWFSEL_MASK	BIT(30)
+#define DFSDM_CR1_AWFSEL(v)	FIELD_PREP(DFSDM_CR1_AWFSEL_MASK, v)
+
+/* CR2: Control register 2 */
+#define DFSDM_CR2_IE_MASK	GENMASK(6, 0)
+#define DFSDM_CR2_IE(v)		FIELD_PREP(DFSDM_CR2_IE_MASK, v)
+#define DFSDM_CR2_JEOCIE_MASK	BIT(0)
+#define DFSDM_CR2_JEOCIE(v)	FIELD_PREP(DFSDM_CR2_JEOCIE_MASK, v)
+#define DFSDM_CR2_REOCIE_MASK	BIT(1)
+#define DFSDM_CR2_REOCIE(v)	FIELD_PREP(DFSDM_CR2_REOCIE_MASK, v)
+#define DFSDM_CR2_JOVRIE_MASK	BIT(2)
+#define DFSDM_CR2_JOVRIE(v)	FIELD_PREP(DFSDM_CR2_JOVRIE_MASK, v)
+#define DFSDM_CR2_ROVRIE_MASK	BIT(3)
+#define DFSDM_CR2_ROVRIE(v)	FIELD_PREP(DFSDM_CR2_ROVRIE_MASK, v)
+#define DFSDM_CR2_AWDIE_MASK	BIT(4)
+#define DFSDM_CR2_AWDIE(v)	FIELD_PREP(DFSDM_CR2_AWDIE_MASK, v)
+#define DFSDM_CR2_SCDIE_MASK	BIT(5)
+#define DFSDM_CR2_SCDIE(v)	FIELD_PREP(DFSDM_CR2_SCDIE_MASK, v)
+#define DFSDM_CR2_CKABIE_MASK	BIT(6)
+#define DFSDM_CR2_CKABIE(v)	FIELD_PREP(DFSDM_CR2_CKABIE_MASK, v)
+#define DFSDM_CR2_EXCH_MASK	GENMASK(15, 8)
+#define DFSDM_CR2_EXCH(v)	FIELD_PREP(DFSDM_CR2_EXCH_MASK, v)
+#define DFSDM_CR2_AWDCH_MASK	GENMASK(23, 16)
+#define DFSDM_CR2_AWDCH(v)	FIELD_PREP(DFSDM_CR2_AWDCH_MASK, v)
+
+/* ISR: Interrupt status register */
+#define DFSDM_ISR_JEOCF_MASK	BIT(0)
+#define DFSDM_ISR_JEOCF(v)	FIELD_PREP(DFSDM_ISR_JEOCF_MASK, v)
+#define DFSDM_ISR_REOCF_MASK	BIT(1)
+#define DFSDM_ISR_REOCF(v)	FIELD_PREP(DFSDM_ISR_REOCF_MASK, v)
+#define DFSDM_ISR_JOVRF_MASK	BIT(2)
+#define DFSDM_ISR_JOVRF(v)	FIELD_PREP(DFSDM_ISR_JOVRF_MASK, v)
+#define DFSDM_ISR_ROVRF_MASK	BIT(3)
+#define DFSDM_ISR_ROVRF(v)	FIELD_PREP(DFSDM_ISR_ROVRF_MASK, v)
+#define DFSDM_ISR_AWDF_MASK	BIT(4)
+#define DFSDM_ISR_AWDF(v)	FIELD_PREP(DFSDM_ISR_AWDF_MASK, v)
+#define DFSDM_ISR_JCIP_MASK	BIT(13)
+#define DFSDM_ISR_JCIP(v)	FIELD_PREP(DFSDM_ISR_JCIP_MASK, v)
+#define DFSDM_ISR_RCIP_MASK	BIT(14)
+#define DFSDM_ISR_RCIP(v)	FIELD_PREP(DFSDM_ISR_RCIP, v)
+#define DFSDM_ISR_CKABF_MASK	GENMASK(23, 16)
+#define DFSDM_ISR_CKABF(v)	FIELD_PREP(DFSDM_ISR_CKABF_MASK, v)
+#define DFSDM_ISR_SCDF_MASK	GENMASK(31, 24)
+#define DFSDM_ISR_SCDF(v)	FIELD_PREP(DFSDM_ISR_SCDF_MASK, v)
+
+/* ICR: Interrupt flag clear register */
+#define DFSDM_ICR_CLRJOVRF_MASK	      BIT(2)
+#define DFSDM_ICR_CLRJOVRF(v)	      FIELD_PREP(DFSDM_ICR_CLRJOVRF_MASK, v)
+#define DFSDM_ICR_CLRROVRF_MASK	      BIT(3)
+#define DFSDM_ICR_CLRROVRF(v)	      FIELD_PREP(DFSDM_ICR_CLRROVRF_MASK, v)
+#define DFSDM_ICR_CLRCKABF_MASK	      GENMASK(23, 16)
+#define DFSDM_ICR_CLRCKABF(v)	      FIELD_PREP(DFSDM_ICR_CLRCKABF_MASK, v)
+#define DFSDM_ICR_CLRCKABF_CH_MASK(y) BIT(16 + (y))
+#define DFSDM_ICR_CLRCKABF_CH(v, y)   \
+			   (((v) << (16 + (y))) & DFSDM_ICR_CLRCKABF_CH_MASK(y))
+#define DFSDM_ICR_CLRSCDF_MASK	      GENMASK(31, 24)
+#define DFSDM_ICR_CLRSCDF(v)	      FIELD_PREP(DFSDM_ICR_CLRSCDF_MASK, v)
+#define DFSDM_ICR_CLRSCDF_CH_MASK(y)  BIT(24 + (y))
+#define DFSDM_ICR_CLRSCDF_CH(v, y)    \
+			       (((v) << (24 + (y))) & DFSDM_ICR_CLRSCDF_MASK(y))
+
+/* FCR: Filter control register */
+#define DFSDM_FCR_IOSR_MASK	GENMASK(7, 0)
+#define DFSDM_FCR_IOSR(v)	FIELD_PREP(DFSDM_FCR_IOSR_MASK, v)
+#define DFSDM_FCR_FOSR_MASK	GENMASK(25, 16)
+#define DFSDM_FCR_FOSR(v)	FIELD_PREP(DFSDM_FCR_FOSR_MASK, v)
+#define DFSDM_FCR_FORD_MASK	GENMASK(31, 29)
+#define DFSDM_FCR_FORD(v)	FIELD_PREP(DFSDM_FCR_FORD_MASK, v)
+
+/* RDATAR: Filter data register for regular channel */
+#define DFSDM_DATAR_CH_MASK	GENMASK(2, 0)
+#define DFSDM_DATAR_DATA_OFFSET 8
+#define DFSDM_DATAR_DATA_MASK	GENMASK(31, DFSDM_DATAR_DATA_OFFSET)
+
+/* AWLTR: Filter analog watchdog low threshold register */
+#define DFSDM_AWLTR_BKAWL_MASK	GENMASK(3, 0)
+#define DFSDM_AWLTR_BKAWL(v)	FIELD_PREP(DFSDM_AWLTR_BKAWL_MASK, v)
+#define DFSDM_AWLTR_AWLT_MASK	GENMASK(31, 8)
+#define DFSDM_AWLTR_AWLT(v)	FIELD_PREP(DFSDM_AWLTR_AWLT_MASK, v)
+
+/* AWHTR: Filter analog watchdog low threshold register */
+#define DFSDM_AWHTR_BKAWH_MASK	GENMASK(3, 0)
+#define DFSDM_AWHTR_BKAWH(v)	FIELD_PREP(DFSDM_AWHTR_BKAWH_MASK, v)
+#define DFSDM_AWHTR_AWHT_MASK	GENMASK(31, 8)
+#define DFSDM_AWHTR_AWHT(v)	FIELD_PREP(DFSDM_AWHTR_AWHT_MASK, v)
+
+/* AWSR: Filter watchdog status register */
+#define DFSDM_AWSR_AWLTF_MASK	GENMASK(7, 0)
+#define DFSDM_AWSR_AWLTF(v)	FIELD_PREP(DFSDM_AWSR_AWLTF_MASK, v)
+#define DFSDM_AWSR_AWHTF_MASK	GENMASK(15, 8)
+#define DFSDM_AWSR_AWHTF(v)	FIELD_PREP(DFSDM_AWSR_AWHTF_MASK, v)
+
+/* AWCFR: Filter watchdog status register */
+#define DFSDM_AWCFR_AWLTF_MASK	GENMASK(7, 0)
+#define DFSDM_AWCFR_AWLTF(v)	FIELD_PREP(DFSDM_AWCFR_AWLTF_MASK, v)
+#define DFSDM_AWCFR_AWHTF_MASK	GENMASK(15, 8)
+#define DFSDM_AWCFR_AWHTF(v)	FIELD_PREP(DFSDM_AWCFR_AWHTF_MASK, v)
+
+#endif
diff --git a/drivers/mfd/stm32-dfsdm.c b/drivers/mfd/stm32-dfsdm.c
new file mode 100644
index 0000000..81ca29c
--- /dev/null
+++ b/drivers/mfd/stm32-dfsdm.c
@@ -0,0 +1,1044 @@
+/*
+ * This file is part of STM32 DFSDM mfd driver
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author(s): Arnaud Pouliquen <arnaud.pouliquen@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/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/stm32-dfsdm.h>
+
+#include "stm32-dfsdm-reg.h"
+
+#define DFSDM_UPDATE_BITS(regm, reg, mask, val) \
+		WARN_ON(regmap_update_bits(regm, reg, mask, val))
+
+#define DFSDM_REG_READ(regm, reg, val) \
+		WARN_ON(regmap_read(regm, reg, val))
+
+#define DFSDM_REG_WRITE(regm, reg, val) \
+		WARN_ON(regmap_write(regm, reg, val))
+
+#define STM32H7_DFSDM_NUM_FILTERS	4
+#define STM32H7_DFSDM_NUM_INPUTS	8
+
+enum dfsdm_clkout_src {
+	DFSDM_CLK,
+	AUDIO_CLK
+};
+
+struct stm32_dev_data {
+	const struct stm32_dfsdm dfsdm;
+	const struct regmap_config *regmap_cfg;
+};
+
+struct dfsdm_priv;
+
+struct filter_params {
+	unsigned int id;
+	int irq;
+	struct stm32_dfsdm_fl_event event;
+	u32 event_mask;
+	struct dfsdm_priv *priv; /* Cross ref for context */
+	unsigned int ext_ch_mask;
+	unsigned int scan_ch;
+};
+
+struct ch_params {
+	struct stm32_dfsdm_channel ch;
+};
+
+struct dfsdm_priv {
+	struct platform_device *pdev;
+	struct stm32_dfsdm dfsdm;
+
+	spinlock_t lock; /* Used for resource sharing & interrupt lock */
+
+	/* Filters */
+	struct filter_params *filters;
+	unsigned int free_filter_mask;
+	unsigned int scd_filter_mask;
+	unsigned int ckab_filter_mask;
+
+	/* Channels */
+	struct stm32_dfsdm_channel *channels;
+	unsigned int free_channel_mask;
+	atomic_t n_active_ch;
+
+	/* Clock */
+	struct clk *clk;
+	struct clk *aclk;
+	unsigned int clkout_div;
+	unsigned int clkout_freq_req;
+
+	/* Registers*/
+	void __iomem *base;
+	struct regmap *regmap;
+	phys_addr_t phys_base;
+};
+
+/*
+ * Common
+ */
+static bool stm32_dfsdm_volatile_reg(struct device *dev, unsigned int reg)
+{
+	if (reg < DFSDM_FILTER_BASE_ADR)
+		return false;
+
+	/*
+	 * Mask is done on register to avoid to list registers of all them
+	 * filter instances.
+	 */
+	switch (reg & DFSDM_FILTER_REG_MASK) {
+	case DFSDM_CR1(0) & DFSDM_FILTER_REG_MASK:
+	case DFSDM_ISR(0) & DFSDM_FILTER_REG_MASK:
+	case DFSDM_JDATAR(0) & DFSDM_FILTER_REG_MASK:
+	case DFSDM_RDATAR(0) & DFSDM_FILTER_REG_MASK:
+		return true;
+	}
+
+	return false;
+}
+
+static const struct regmap_config stm32h7_dfsdm_regmap_cfg = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = sizeof(u32),
+	.max_register = DFSDM_CNVTIMR(STM32H7_DFSDM_NUM_FILTERS - 1),
+	.volatile_reg = stm32_dfsdm_volatile_reg,
+	.fast_io = true,
+};
+
+static const struct stm32_dev_data stm32h7_data = {
+	.dfsdm.max_channels = STM32H7_DFSDM_NUM_INPUTS,
+	.dfsdm.max_filters = STM32H7_DFSDM_NUM_FILTERS,
+	.regmap_cfg = &stm32h7_dfsdm_regmap_cfg,
+};
+
+static int stm32_dfsdm_start_dfsdm(struct dfsdm_priv *priv)
+{
+	int ret;
+	struct device *dev = &priv->pdev->dev;
+
+	if (atomic_inc_return(&priv->n_active_ch) == 1) {
+		ret = clk_prepare_enable(priv->clk);
+		if (ret < 0) {
+			dev_err(dev, "Failed to start clock\n");
+			return ret;
+		}
+		if (priv->aclk) {
+			ret = clk_prepare_enable(priv->aclk);
+			if (ret < 0) {
+				dev_err(dev, "Failed to start audio clock\n");
+				return ret;
+			}
+		}
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
+				  DFSDM_CHCFGR1_CKOUTDIV_MASK,
+				  DFSDM_CHCFGR1_CKOUTDIV(priv->clkout_div));
+
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
+				  DFSDM_CHCFGR1_DFSDMEN_MASK,
+				  DFSDM_CHCFGR1_DFSDMEN(1));
+	}
+
+	dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,
+		atomic_read(&priv->n_active_ch));
+
+	return 0;
+}
+
+static void stm32_dfsdm_stop_dfsdm(struct dfsdm_priv *priv)
+{
+	if (atomic_dec_and_test(&priv->n_active_ch)) {
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
+				  DFSDM_CHCFGR1_DFSDMEN_MASK,
+				  DFSDM_CHCFGR1_DFSDMEN(0));
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
+				  DFSDM_CHCFGR1_CKOUTDIV_MASK,
+				  DFSDM_CHCFGR1_CKOUTDIV(0));
+		clk_disable_unprepare(priv->clk);
+		if (priv->aclk)
+			clk_disable_unprepare(priv->aclk);
+	}
+	dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,
+		atomic_read(&priv->n_active_ch));
+}
+
+static unsigned int stm32_dfsdm_get_clkout_divider(struct dfsdm_priv *priv,
+						   unsigned long rate)
+{
+	unsigned int delta, div;
+
+	/* div = 0 disables the clockout */
+	if (!priv->clkout_freq_req)
+		return 0;
+
+	div = DIV_ROUND_CLOSEST(rate, priv->clkout_freq_req);
+
+	delta = rate - (priv->clkout_freq_req * div);
+	if (delta)
+		dev_warn(&priv->pdev->dev,
+			 "clkout not accurate. delta (Hz): %d\n", delta);
+
+	dev_dbg(&priv->pdev->dev, "%s: clk: %lu (Hz), div %u\n",
+		__func__, rate, div);
+
+	return (div - 1);
+}
+
+/*
+ * Filters
+ */
+
+static int stm32_dfsdm_clear_event(struct dfsdm_priv *priv, unsigned int fl_id,
+				   unsigned int event, int mask)
+{
+	int val;
+
+	switch (event) {
+	case DFSDM_EVENT_INJ_EOC:
+		DFSDM_REG_READ(priv->regmap, DFSDM_JDATAR(fl_id), &val);
+		break;
+	case DFSDM_EVENT_REG_EOC:
+		DFSDM_REG_READ(priv->regmap, DFSDM_RDATAR(fl_id), &val);
+		break;
+	case DFSDM_EVENT_INJ_XRUN:
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_ICR(fl_id),
+				  DFSDM_ICR_CLRJOVRF_MASK,
+				  DFSDM_ICR_CLRJOVRF_MASK);
+		break;
+	case DFSDM_EVENT_REG_XRUN:
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_ICR(fl_id),
+				  DFSDM_ICR_CLRROVRF_MASK,
+				  DFSDM_ICR_CLRROVRF_MASK);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
+{
+	struct filter_params *params = arg;
+	unsigned int status;
+	struct dfsdm_priv *priv = params->priv;
+	unsigned int event_mask = params->event_mask;
+
+	DFSDM_REG_READ(priv->regmap, DFSDM_ISR(params->id), &status);
+
+	if (status & DFSDM_ISR_JOVRF_MASK) {
+		if (event_mask & DFSDM_EVENT_INJ_XRUN) {
+			params->event.cb(&priv->dfsdm, params->id,
+					 DFSDM_EVENT_INJ_XRUN, 0,
+					 params->event.context);
+		}
+		stm32_dfsdm_clear_event(priv, params->id, DFSDM_EVENT_INJ_XRUN,
+					0);
+	}
+
+	if (status & DFSDM_ISR_ROVRF_MASK) {
+		if (event_mask & DFSDM_EVENT_REG_XRUN) {
+			params->event.cb(&priv->dfsdm, params->id,
+					 DFSDM_EVENT_REG_XRUN, 0,
+					 params->event.context);
+		}
+		stm32_dfsdm_clear_event(priv, params->id, DFSDM_EVENT_REG_XRUN,
+					0);
+	}
+
+	if (status & DFSDM_ISR_JEOCF_MASK) {
+		if (event_mask & DFSDM_EVENT_INJ_EOC)
+			params->event.cb(&priv->dfsdm, params->id,
+					 DFSDM_EVENT_INJ_EOC, 0,
+					 params->event.context);
+		else
+			stm32_dfsdm_clear_event(priv, params->id,
+						DFSDM_EVENT_INJ_EOC, 0);
+	}
+
+	if (status & DFSDM_ISR_REOCF_MASK) {
+		if (event_mask & DFSDM_EVENT_REG_EOC)
+			params->event.cb(&priv->dfsdm, params->id,
+					 DFSDM_EVENT_REG_EOC, 0,
+					 params->event.context);
+		else
+			stm32_dfsdm_clear_event(priv, params->id,
+						DFSDM_EVENT_REG_EOC, 0);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void stm32_dfsdm_configure_reg_conv(struct dfsdm_priv *priv,
+					   unsigned int fl_id,
+					   struct stm32_dfsdm_regular *params)
+{
+	unsigned int ch_id = params->ch_src;
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RCH_MASK,
+			  DFSDM_CR1_RCH(ch_id));
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_FAST_MASK,
+			  DFSDM_CR1_FAST(params->fast_mode));
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RCONT_MASK,
+			  DFSDM_CR1_RCONT(params->cont_mode));
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RDMAEN_MASK,
+			  DFSDM_CR1_RDMAEN(params->dma_mode));
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RSYNC_MASK,
+			  DFSDM_CR1_RSYNC(params->sync_mode));
+
+	priv->filters[fl_id].scan_ch = BIT(ch_id);
+}
+
+static void stm32_dfsdm_configure_inj_conv(struct dfsdm_priv *priv,
+					   unsigned int fl_id,
+					   struct stm32_dfsdm_injected *params)
+{
+	int val;
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_JSCAN_MASK,
+			  DFSDM_CR1_JSCAN(params->scan_mode));
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_JDMAEN_MASK,
+			  DFSDM_CR1_JDMAEN(params->dma_mode));
+
+	val = (params->trigger == DFSDM_FILTER_EXT_TRIGGER) ?
+	      params->trig_src : 0;
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id),
+			  DFSDM_CR1_JEXTSEL_MASK,
+			  DFSDM_CR1_JEXTSEL(val));
+
+	val = (params->trigger == DFSDM_FILTER_SYNC_TRIGGER) ? 1 : 0;
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_JSYNC_MASK,
+			  DFSDM_CR1_JSYNC(val));
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_JEXTEN_MASK,
+			  DFSDM_CR1_JEXTEN(params->trig_pol));
+	priv->filters[fl_id].scan_ch = params->ch_group;
+
+	DFSDM_REG_WRITE(priv->regmap, DFSDM_JCHGR(fl_id), params->ch_group);
+}
+
+/**
+ * stm32_dfsdm_configure_filter - Configure filter.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter id.
+ * @conv: Conversion type regular or injected.
+ */
+int stm32_dfsdm_configure_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+				 struct stm32_dfsdm_filter *fl_cfg)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv,
+					       dfsdm);
+	struct stm32_dfsdm_sinc_filter *sparams = &fl_cfg->sinc_params;
+
+	dev_dbg(&priv->pdev->dev, "%s:config filter %d\n", __func__, fl_id);
+
+	/* Average integrator oversampling */
+	if ((!fl_cfg->int_oversampling) ||
+	    (fl_cfg->int_oversampling > DFSDM_MAX_INT_OVERSAMPLING)) {
+		dev_err(&priv->pdev->dev, "invalid integrator oversampling %d\n",
+			fl_cfg->int_oversampling);
+		return -EINVAL;
+	}
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_FCR(fl_id), DFSDM_FCR_IOSR_MASK,
+			  DFSDM_FCR_IOSR((fl_cfg->int_oversampling - 1)));
+
+	/* Oversamplings and filter*/
+	if ((!sparams->oversampling) ||
+	    (sparams->oversampling > DFSDM_MAX_FL_OVERSAMPLING)) {
+		dev_err(&priv->pdev->dev, "invalid oversampling %d\n",
+			sparams->oversampling);
+		return -EINVAL;
+	}
+
+	if (sparams->order > DFSDM_SINC5_ORDER) {
+		dev_err(&priv->pdev->dev, "invalid filter order %d\n",
+			sparams->order);
+		return -EINVAL;
+	}
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FOSR_MASK,
+			  DFSDM_FCR_FOSR((sparams->oversampling - 1)));
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FORD_MASK,
+			  DFSDM_FCR_FORD(sparams->order));
+
+	/* Conversion */
+	if (fl_cfg->inj_params)
+		stm32_dfsdm_configure_inj_conv(priv, fl_id, fl_cfg->inj_params);
+	else if (fl_cfg->reg_params)
+		stm32_dfsdm_configure_reg_conv(priv, fl_id, fl_cfg->reg_params);
+
+	priv->filters[fl_id].event = fl_cfg->event;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dfsdm_configure_filter);
+
+/**
+ * stm32_dfsdm_start_filter - Start filter conversion.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter id.
+ * @conv: Conversion type regular or injected.
+ */
+void stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+			      enum stm32_dfsdm_conv_type conv)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+
+	dev_dbg(&priv->pdev->dev, "%s:start filter %d\n", __func__, fl_id);
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_DFEN_MASK,
+			  DFSDM_CR1_DFEN(1));
+
+	if (conv == DFSDM_FILTER_REG_CONV) {
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id),
+				  DFSDM_CR1_RSWSTART_MASK,
+				  DFSDM_CR1_RSWSTART(1));
+	} else if (conv == DFSDM_FILTER_SW_INJ_CONV) {
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id),
+				  DFSDM_CR1_JSWSTART_MASK,
+				  DFSDM_CR1_JSWSTART(1));
+	}
+}
+EXPORT_SYMBOL_GPL(dfsdm_start_filter);
+
+/**
+ * stm32_dfsdm_stop_filter - Stop filter conversion.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter id.
+ */
+void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+
+	dev_dbg(&priv->pdev->dev, "%s:stop filter %d\n", __func__, fl_id);
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_DFEN_MASK,
+			  DFSDM_CR1_DFEN(0));
+	priv->filters[fl_id].scan_ch = 0;
+}
+EXPORT_SYMBOL_GPL(dfsdm_stop_filter);
+
+/**
+ * stm32_dfsdm_read_fl_conv - Read filter conversion.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter id.
+ * @type: Regular or injected conversion.
+ */
+void stm32_dfsdm_read_fl_conv(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+			      u32 *val, int *ch_id,
+			      enum stm32_dfsdm_conv_type type)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+	int reg_v, offset;
+
+	if (type == DFSDM_FILTER_REG_CONV)
+		offset = DFSDM_RDATAR(fl_id);
+	else
+		offset = DFSDM_JDATAR(fl_id);
+
+	DFSDM_REG_READ(priv->regmap, offset, &reg_v);
+
+	*ch_id = reg_v & DFSDM_DATAR_CH_MASK;
+	*val = reg_v & DFSDM_DATAR_DATA_MASK;
+}
+EXPORT_SYMBOL_GPL(dfsdm_read_fl_conv);
+
+/**
+ * stm32_dfsdm_get_filter - Get filter instance.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter instance to reserve.
+ *
+ * Reserves a DFSDM filter resource.
+ */
+int stm32_dfsdm_get_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv,
+					       dfsdm);
+	struct device *dev = &priv->pdev->dev;
+
+	spin_lock(&priv->lock);
+	if (!(priv->free_filter_mask & BIT(fl_id))) {
+		spin_unlock(&priv->lock);
+		dev_err(dev, "filter resource %d available\n", fl_id);
+		return -EBUSY;
+	}
+	priv->free_filter_mask &= ~BIT(fl_id);
+
+	spin_unlock(&priv->lock);
+
+	dev_dbg(dev, "%s: new mask %#x\n", __func__, priv->free_filter_mask);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dfsdm_get_filter);
+
+/**
+ * stm32_dfsdm_release_filter - Release filter instance.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter id.
+ *
+ * Free the DFSDM filter resource.
+ */
+void stm32_dfsdm_release_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+
+	spin_lock(&priv->lock);
+	priv->free_filter_mask |= BIT(fl_id);
+	spin_unlock(&priv->lock);
+}
+EXPORT_SYMBOL_GPL(dfsdm_release_filter);
+
+/**
+ * stm32_dfsdm_get_filter_dma_addr - Get register address for dma transfer.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter id.
+ * @conv: Conversion type.
+ */
+dma_addr_t stm32_dfsdm_get_filter_dma_phy_addr(struct stm32_dfsdm *dfsdm,
+					       unsigned int fl_id,
+					       enum stm32_dfsdm_conv_type conv)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+
+	if (conv == DFSDM_FILTER_REG_CONV)
+		return (dma_addr_t)(priv->phys_base + DFSDM_RDATAR(fl_id));
+	else
+		return (dma_addr_t)(priv->phys_base + DFSDM_JDATAR(fl_id));
+}
+
+/**
+ * stm32_dfsdm_register_fl_event - Register filter event.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter id.
+ * @event: Event to unregister.
+ * @chan_mask: Mask of channels associated to filter.
+ *
+ * The function enables associated IRQ.
+ */
+int stm32_dfsdm_register_fl_event(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+				  enum stm32_dfsdm_events event,
+				  unsigned int chan_mask)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+	unsigned long flags, ulmask = chan_mask;
+	int ret, i;
+
+	dev_dbg(&priv->pdev->dev, "%s:for filter %d: event %#x ch_mask %#x\n",
+		__func__, fl_id, event, chan_mask);
+
+	if (event > DFSDM_EVENT_CKA)
+		return -EINVAL;
+
+	/* Clear interrupt before enable them */
+	ret = stm32_dfsdm_clear_event(priv, fl_id, event, chan_mask);
+	if (ret < 0)
+		return ret;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	/* Enable interrupts */
+	switch (event) {
+	case DFSDM_EVENT_SCD:
+		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
+			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
+					  DFSDM_CHCFGR1_SCDEN_MASK,
+					  DFSDM_CHCFGR1_SCDEN(1));
+		}
+		if (!priv->scd_filter_mask)
+			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
+					  DFSDM_CR2_SCDIE_MASK,
+					  DFSDM_CR2_SCDIE(1));
+		priv->scd_filter_mask |= BIT(fl_id);
+		break;
+	case DFSDM_EVENT_CKA:
+		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
+			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
+					  DFSDM_CHCFGR1_CKABEN_MASK,
+					  DFSDM_CHCFGR1_CKABEN(1));
+		}
+		if (!priv->ckab_filter_mask)
+			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
+					  DFSDM_CR2_CKABIE_MASK,
+					  DFSDM_CR2_CKABIE(1));
+		priv->ckab_filter_mask |= BIT(fl_id);
+		break;
+	default:
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(fl_id), event, event);
+	}
+	priv->filters[fl_id].event_mask |= event;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dfsdm_register_fl_event);
+
+/**
+ * stm32_dfsdm_unregister_fl_event - Unregister filter event.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter id.
+ * @event: Event to unregister.
+ * @chan_mask: Mask of channels associated to filter.
+ *
+ * The function disables associated IRQ.
+ */
+int stm32_dfsdm_unregister_fl_event(struct stm32_dfsdm *dfsdm,
+				    unsigned int fl_id,
+				    enum stm32_dfsdm_events event,
+				    unsigned int chan_mask)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+	unsigned long flags, ulmask = chan_mask;
+	int i;
+
+	dev_dbg(&priv->pdev->dev, "%s:for filter %d: event %#x ch_mask %#x\n",
+		__func__, fl_id, event, chan_mask);
+
+	if (event > DFSDM_EVENT_CKA)
+		return -EINVAL;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	/* Disable interrupts */
+	switch (event) {
+	case DFSDM_EVENT_SCD:
+		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
+			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
+					  DFSDM_CHCFGR1_SCDEN_MASK,
+					  DFSDM_CHCFGR1_SCDEN(0));
+		}
+		priv->scd_filter_mask &= ~BIT(fl_id);
+		if (!priv->scd_filter_mask)
+			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
+					  DFSDM_CR2_SCDIE_MASK,
+					  DFSDM_CR2_SCDIE(0));
+		break;
+	case DFSDM_EVENT_CKA:
+		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
+			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
+					  DFSDM_CHCFGR1_CKABEN_MASK,
+					  DFSDM_CHCFGR1_CKABEN(0));
+		}
+		priv->ckab_filter_mask &= ~BIT(fl_id);
+		if (!priv->ckab_filter_mask)
+			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
+					  DFSDM_CR2_CKABIE_MASK,
+					  DFSDM_CR2_CKABIE(0));
+		break;
+	default:
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(fl_id), event, 0);
+	}
+
+	priv->filters[fl_id].event_mask &= ~event;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dfsdm_unregister_fl_event);
+
+/*
+ * Channels
+ */
+static void stm32_dfsdm_init_channel(struct dfsdm_priv *priv,
+				     struct stm32_dfsdm_channel *ch)
+{
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
+			  DFSDM_CHCFGR1_DATMPX_MASK,
+			  DFSDM_CHCFGR1_DATMPX(ch->type.source));
+	if (ch->type.source == DFSDM_CHANNEL_EXTERNAL_INPUTS) {
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
+				  DFSDM_CHCFGR1_SITP_MASK,
+				  DFSDM_CHCFGR1_SITP(ch->serial_if.type));
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
+				  DFSDM_CHCFGR1_SPICKSEL_MASK,
+				DFSDM_CHCFGR1_SPICKSEL(ch->serial_if.spi_clk));
+	}
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
+			  DFSDM_CHCFGR1_DATPACK_MASK,
+			  DFSDM_CHCFGR1_DATPACK(ch->type.DataPacking));
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
+			  DFSDM_CHCFGR1_CHINSEL_MASK,
+			  DFSDM_CHCFGR1_CHINSEL(ch->serial_if.pins));
+}
+
+/**
+ * stm32_dfsdm_start_channel - Configure and activate DFSDM channel.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @ch: Filter id.
+ * @cfg: Filter configuration.
+ */
+int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id,
+			      struct stm32_dfsdm_ch_cfg *cfg)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv,
+					       dfsdm);
+	struct regmap *reg = priv->regmap;
+	int ret;
+
+	dev_dbg(&priv->pdev->dev, "%s: for channel %d\n", __func__, ch_id);
+
+	ret = stm32_dfsdm_start_dfsdm(priv);
+	if (ret < 0)
+		return ret;
+
+	DFSDM_UPDATE_BITS(reg, DFSDM_CHCFGR2(ch_id), DFSDM_CHCFGR2_DTRBS_MASK,
+			  DFSDM_CHCFGR2_DTRBS(cfg->right_bit_shift));
+	DFSDM_UPDATE_BITS(reg, DFSDM_CHCFGR2(ch_id), DFSDM_CHCFGR2_OFFSET_MASK,
+			  DFSDM_CHCFGR2_OFFSET(cfg->offset));
+
+	DFSDM_UPDATE_BITS(reg, DFSDM_CHCFGR1(ch_id), DFSDM_CHCFGR1_CHEN_MASK,
+			  DFSDM_CHCFGR1_CHEN(1));
+
+	/* Clear absence detection IRQ */
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_ICR(0),
+			  DFSDM_ICR_CLRCKABF_CH_MASK(ch_id),
+			  DFSDM_ICR_CLRCKABF_CH(1, ch_id));
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dfsdm_start_channel);
+
+/**
+ * stm32_dfsdm_stop_channel - Deactivate channel.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @ch_id: DFSDM channel identifier.
+ */
+void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+
+	dev_dbg(&priv->pdev->dev, "%s:for channel %d\n", __func__, ch_id);
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch_id),
+			  DFSDM_CHCFGR1_CHEN_MASK,
+			  DFSDM_CHCFGR1_CHEN(0));
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch_id),
+			  DFSDM_CHCFGR1_CKABEN_MASK, DFSDM_CHCFGR1_CKABEN(0));
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch_id),
+			  DFSDM_CHCFGR1_SCDEN_MASK, DFSDM_CHCFGR1_SCDEN(0));
+
+	stm32_dfsdm_stop_dfsdm(priv);
+}
+EXPORT_SYMBOL_GPL(dfsdm_stop_channel);
+
+/**
+ * stm32_dfsdm_get_channel - Get channel instance.
+ *
+ * @dfsdm: handle used to retrieve dfsdm context.
+ * @ch: DFSDM channel hardware parameters.
+ *
+ * Reserve DFSDM channel resource.
+ */
+int stm32_dfsdm_get_channel(struct stm32_dfsdm *dfsdm,
+			    struct stm32_dfsdm_channel *ch)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+	unsigned int id = ch->id;
+
+	dev_dbg(&priv->pdev->dev, "%s:get channel %d\n", __func__, id);
+
+	if (id >= priv->dfsdm.max_channels) {
+		dev_err(&priv->pdev->dev, "channel (%d) is not valid\n", id);
+		return -EINVAL;
+	}
+
+	if ((ch->type.source != DFSDM_CHANNEL_EXTERNAL_INPUTS) &
+	    (ch->serial_if.spi_clk != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL) &
+	    (!priv->clkout_freq_req)) {
+		dev_err(&priv->pdev->dev, "clkout not present\n");
+		return -EINVAL;
+	}
+
+	spin_lock(&priv->lock);
+	if (!(BIT(id) & priv->free_channel_mask)) {
+		spin_unlock(&priv->lock);
+		dev_err(&priv->pdev->dev, "channel (%d) already in use\n", id);
+		return -EBUSY;
+	}
+
+	priv->free_channel_mask &= ~BIT(id);
+	priv->channels[id] = *ch;
+	spin_unlock(&priv->lock);
+
+	dev_dbg(&priv->pdev->dev, "%s: new mask %#x\n", __func__,
+		priv->free_channel_mask);
+
+	/**
+	 * Check clock constrainst between clkout and either
+	 * dfsdm/audio clock:
+	 * - In SPI mode (clkout is used): Fclk >= 4 * Fclkout
+	 *   (e.g. CKOUTDIV >= 3)
+	 * - In mancherster mode: Fclk >= 6 * Fclkout
+	 */
+	switch (ch->serial_if.type) {
+	case DFSDM_CHANNEL_SPI_RISING:
+	case DFSDM_CHANNEL_SPI_FALLING:
+		if (priv->clkout_div && priv->clkout_div < 3)
+			dev_warn(&priv->pdev->dev,
+				 "Clock div should be higher than 3\n");
+		break;
+	case DFSDM_CHANNEL_MANCHESTER_RISING:
+	case DFSDM_CHANNEL_MANCHESTER_FALLING:
+		if (priv->clkout_div && priv->clkout_div < 5)
+			dev_warn(&priv->pdev->dev,
+				 "Clock div should be higher than 5\n");
+		break;
+	}
+
+	stm32_dfsdm_init_channel(priv, ch);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dfsdm_get_channel);
+
+/**
+ * stm32_dfsdm_release_channel - Release channel instance.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @ch_id: DFSDM channel identifier.
+ *
+ * Free the DFSDM channel resource.
+ */
+void stm32_dfsdm_release_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+
+	spin_lock(&priv->lock);
+	priv->free_channel_mask |= BIT(ch_id);
+	spin_unlock(&priv->lock);
+}
+EXPORT_SYMBOL_GPL(dfsdm_release_channel);
+
+/**
+ * stm32_dfsdm_get_clk_out_rate - get clkout frequency.
+ *
+ * @dfsdm: handle used to retrieve dfsdm context.
+ * @rate: clock out rate in Hz.
+ *
+ * Provide output frequency used for external ADC.
+ * return EINVAL if clockout is not used else return 0.
+ */
+int stm32_dfsdm_get_clk_out_rate(struct stm32_dfsdm *dfsdm, unsigned long *rate)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+	unsigned long int clk_rate;
+
+	if (!priv->clkout_div)
+		return -EINVAL;
+
+	clk_rate = clk_get_rate(priv->aclk ? priv->aclk : priv->clk);
+	*rate = clk_rate / (priv->clkout_div + 1);
+	dev_dbg(&priv->pdev->dev, "%s: clkout: %ld (Hz)\n", __func__, *rate);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dfsdm_get_clk_out_rate);
+
+static int stm32_dfsdm_parse_of(struct platform_device *pdev,
+				struct dfsdm_priv *priv)
+{
+	struct device_node *node = pdev->dev.of_node;
+	struct resource *res;
+	int ret, val;
+
+	if (!node)
+		return -EINVAL;
+
+	/* Get resources */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "Failed to get memory resource\n");
+		return -ENODEV;
+	}
+	priv->phys_base = res->start;
+	priv->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(priv->base))
+		return PTR_ERR(priv->base);
+
+	ret = of_property_read_u32(node, "st,clkout-freq", &val);
+	if (!ret) {
+		if (!val) {
+			dev_err(&priv->pdev->dev,
+				"st,clkout-freq cannot be 0\n");
+			return -EINVAL;
+		}
+		priv->clkout_freq_req = val;
+	} else if (ret != -EINVAL) {
+		dev_err(&priv->pdev->dev, "Failed to get st,clkout-freq\n");
+		return ret;
+	}
+
+	/* Source clock */
+	priv->clk = devm_clk_get(&pdev->dev, "dfsdm_clk");
+	if (IS_ERR(priv->clk)) {
+		dev_err(&pdev->dev, "No stm32_dfsdm_clk clock found\n");
+		return -EINVAL;
+	}
+
+	priv->aclk = devm_clk_get(&pdev->dev, "audio_clk");
+	if (IS_ERR(priv->aclk))
+		priv->aclk = NULL;
+
+	return 0;
+};
+
+static const struct of_device_id stm32_dfsdm_of_match[] = {
+	{
+		.compatible = "st,stm32h7-dfsdm",
+		.data = &stm32h7_data
+	},
+	{}
+};
+MODULE_DEVICE_TABLE(of, stm32_dfsdm_of_match);
+
+static int stm32_dfsdm_remove(struct platform_device *pdev)
+{
+	of_platform_depopulate(&pdev->dev);
+
+	return 0;
+}
+
+static int stm32_dfsdm_probe(struct platform_device *pdev)
+{
+	struct dfsdm_priv *priv;
+	struct device_node *pnode = pdev->dev.of_node;
+	const struct of_device_id *of_id;
+	const struct stm32_dev_data *dev_data;
+	enum dfsdm_clkout_src clk_src;
+	int ret, i;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->pdev = pdev;
+
+	/* Populate data structure depending on compatibility */
+	of_id = of_match_node(stm32_dfsdm_of_match, pnode);
+	if (!of_id->data) {
+		dev_err(&pdev->dev, "Data associated to device is missing\n");
+		return -EINVAL;
+	}
+
+	dev_data = (const struct stm32_dev_data *)of_id->data;
+
+	ret = stm32_dfsdm_parse_of(pdev, priv);
+	if (ret < 0)
+		return ret;
+
+	priv->regmap = devm_regmap_init_mmio(&pdev->dev, priv->base,
+					    dev_data->regmap_cfg);
+	if (IS_ERR(priv->regmap)) {
+		ret = PTR_ERR(priv->regmap);
+		dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	priv->dfsdm = dev_data->dfsdm;
+
+	priv->filters = devm_kcalloc(&pdev->dev, dev_data->dfsdm.max_filters,
+				     sizeof(*priv->filters), GFP_KERNEL);
+	if (IS_ERR(priv->filters)) {
+		ret = PTR_ERR(priv->filters);
+		goto probe_err;
+	}
+
+	for (i = 0; i < dev_data->dfsdm.max_filters; i++) {
+		struct filter_params *params = &priv->filters[i];
+
+		params->id = i;
+		params->irq = platform_get_irq(pdev, i);
+		if (params->irq < 0) {
+			dev_err(&pdev->dev, "Failed to get IRQ resource\n");
+			ret = params->irq;
+			goto probe_err;
+		}
+
+		ret = devm_request_irq(&pdev->dev, params->irq, stm32_dfsdm_irq,
+				       0, dev_name(&pdev->dev), params);
+		if (ret) {
+			dev_err(&pdev->dev, "Failed to register interrupt\n");
+			goto probe_err;
+		}
+
+		params->priv = priv;
+	}
+
+	priv->channels = devm_kcalloc(&pdev->dev, priv->dfsdm.max_channels,
+				      sizeof(*priv->channels), GFP_KERNEL);
+	if (IS_ERR(priv->channels)) {
+		ret = PTR_ERR(priv->channels);
+		goto probe_err;
+	}
+	priv->free_filter_mask = BIT(priv->dfsdm.max_filters) - 1;
+	priv->free_channel_mask = BIT(priv->dfsdm.max_channels) - 1;
+
+	platform_set_drvdata(pdev, &priv->dfsdm);
+	spin_lock_init(&priv->lock);
+
+	priv->clkout_div = stm32_dfsdm_get_clkout_divider(priv,
+						    clk_get_rate(priv->clk));
+
+	ret = of_platform_populate(pnode, NULL, NULL, &pdev->dev);
+	if (ret < 0)
+		goto probe_err;
+
+	clk_src = priv->aclk ? AUDIO_CLK : DFSDM_CLK;
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
+			  DFSDM_CHCFGR1_CKOUTSRC_MASK,
+			  DFSDM_CHCFGR1_CKOUTSRC(clk_src));
+	return 0;
+
+probe_err:
+	return ret;
+}
+
+static struct platform_driver stm32_dfsdm_driver = {
+	.probe = stm32_dfsdm_probe,
+	.remove = stm32_dfsdm_remove,
+	.driver = {
+		.name = "stm32-dfsdm",
+		.of_match_table = stm32_dfsdm_of_match,
+	},
+};
+
+module_platform_driver(stm32_dfsdm_driver);
+
+MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 dfsdm driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/stm32-dfsdm.h b/include/linux/mfd/stm32-dfsdm.h
new file mode 100644
index 0000000..f6eb788
--- /dev/null
+++ b/include/linux/mfd/stm32-dfsdm.h
@@ -0,0 +1,324 @@
+/*
+ * This file is part of STM32 DFSDM mfd driver API
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com>.
+ *
+ * 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.
+ */
+#ifndef MDF_STM32_DFSDM_H
+#define MDF_STM32_DFSDM_H
+
+/*
+ * Channel definitions
+ */
+#define DFSDM_CHANNEL_0    BIT(0)
+#define DFSDM_CHANNEL_1    BIT(1)
+#define DFSDM_CHANNEL_2    BIT(2)
+#define DFSDM_CHANNEL_3    BIT(3)
+#define DFSDM_CHANNEL_4    BIT(4)
+#define DFSDM_CHANNEL_5    BIT(5)
+#define DFSDM_CHANNEL_6    BIT(6)
+#define DFSDM_CHANNEL_7    BIT(7)
+
+/* DFSDM channel input data packing */
+enum stm32_dfsdm_data_packing {
+	DFSDM_CHANNEL_STANDARD_MODE,    /* Standard data packing mode */
+	DFSDM_CHANNEL_INTERLEAVED_MODE, /* Interleaved data packing mode */
+	DFSDM_CHANNEL_DUAL_MODE         /* Dual data packing mode */
+};
+
+/* DFSDM channel input multiplexer */
+enum stm32_dfsdm_input_multiplexer {
+	DFSDM_CHANNEL_EXTERNAL_INPUTS,    /* Data taken from external inputs */
+	DFSDM_CHANNEL_INTERNAL_ADC,       /* Data taken from internal ADC */
+	DFSDM_CHANNEL_INTERNAL_REGISTER,  /* Data taken from register */
+};
+
+/* DFSDM channel serial interface type */
+enum stm32_dfsdm_serial_in_type {
+	DFSDM_CHANNEL_SPI_RISING,         /* SPI with rising edge */
+	DFSDM_CHANNEL_SPI_FALLING,        /* SPI with falling edge */
+	DFSDM_CHANNEL_MANCHESTER_RISING,  /* Manchester with rising edge */
+	DFSDM_CHANNEL_MANCHESTER_FALLING, /* Manchester with falling edge */
+};
+
+/* DFSDM channel serial spi clock source */
+enum stm32_dfsdm_spi_clk_src {
+	/* External SPI clock */
+	DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL,
+	/* Internal SPI clock */
+	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL,
+	/* Internal SPI clock divided by 2, falling edge */
+	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING,
+	/* Internal SPI clock divided by 2, rising edge */
+	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING
+};
+
+/* DFSDM channel input pins */
+enum stm32_dfsdm_serial_in_select {
+	/* Serial input taken from pins of the same channel (y) */
+	DFSDM_CHANNEL_SAME_CHANNEL_PINS,
+	/* Serial input taken from pins of the following channel (y + 1)*/
+	DFSDM_CHANNEL_NEXT_CHANNEL_PINS,
+};
+
+/**
+ * struct stm32_dfsdm_input_type - DFSDM channel init structure definition.
+ * @DataPacking: Standard, interleaved or dual mode for internal register.
+ * @source: channel source: internal DAC, serial input or memory.
+ */
+struct stm32_dfsdm_input_type {
+	enum stm32_dfsdm_data_packing DataPacking;
+	enum stm32_dfsdm_input_multiplexer source;
+};
+
+/**
+ * struct stm32_dfsdm_serial_if - DFSDM serial interface parameters.
+ * @type:	Serial interface type.
+ * @spi_clk:	SPI clock source.
+ * @pins:	select serial interface associated to the channel
+ */
+struct stm32_dfsdm_serial_if {
+	enum stm32_dfsdm_serial_in_type type;
+	enum stm32_dfsdm_spi_clk_src spi_clk;
+	enum stm32_dfsdm_serial_in_select pins;
+};
+
+/**
+ * struct stm32_dfsdm_channel - DFSDM channel hardware parameters.
+ * @id:		DFSDM channel identifier.
+ * @type:	DFSDM channel input parameters.
+ * @serial_if:	DFSDM channel serial interface parameters.
+ *		Mandatory for DFSDM_CHANNEL_EXTERNAL_INPUTS.
+ */
+struct stm32_dfsdm_channel {
+	unsigned int id;
+	struct stm32_dfsdm_input_type type;
+	struct stm32_dfsdm_serial_if serial_if;
+};
+
+/**
+ * struct stm32_dfsdm_ch_cfg - DFSDM channel config.
+ * @offset:		DFSDM channel 24 bit calibration offset.
+ * @right_bit_shift:	DFSDM channel right bit shift of the data result.
+ */
+struct stm32_dfsdm_ch_cfg {
+	unsigned int offset;
+	unsigned int right_bit_shift;
+};
+
+/*
+ * Filter definitions
+ */
+
+#define DFSDM_MIN_INT_OVERSAMPLING 1
+#define DFSDM_MAX_INT_OVERSAMPLING 256
+#define DFSDM_MIN_FL_OVERSAMPLING 1
+#define DFSDM_MAX_FL_OVERSAMPLING 1024
+
+enum stm32_dfsdm_events {
+	DFSDM_EVENT_INJ_EOC =	BIT(0), /* Injected end of conversion event */
+	DFSDM_EVENT_REG_EOC =	BIT(1), /* Regular end of conversion event */
+	DFSDM_EVENT_INJ_XRUN =	BIT(2), /* Injected conversion overrun event */
+	DFSDM_EVENT_REG_XRUN =	BIT(3), /* Regular conversion overrun event */
+	DFSDM_EVENT_AWD =	BIT(4), /* Analog watchdog event */
+	DFSDM_EVENT_SCD =	BIT(5), /* Short circuit detector event */
+	DFSDM_EVENT_CKA =	BIT(6), /* Clock abscence detection event */
+};
+
+#define STM32_DFSDM_EVENT_MASK 0x3F
+
+/* DFSDM filter order  */
+enum stm32_dfsdm_sinc_order {
+	DFSDM_FASTSINC_ORDER, /* FastSinc filter type */
+	DFSDM_SINC1_ORDER,    /* Sinc 1 filter type */
+	DFSDM_SINC2_ORDER,    /* Sinc 2 filter type */
+	DFSDM_SINC3_ORDER,    /* Sinc 3 filter type */
+	DFSDM_SINC4_ORDER,    /* Sinc 4 filter type (N.A. for watchdog) */
+	DFSDM_SINC5_ORDER,    /* Sinc 5 filter type (N.A. for watchdog) */
+	DFSDM_NB_SINC_ORDER,
+};
+
+/* DFSDM filter order */
+enum stm32_dfsdm_state {
+	DFSDM_DISABLE,
+	DFSDM_ENABLE,
+};
+
+/**
+ * struct stm32_dfsdm_sinc_filter - DFSDM Sinc filter structure definition
+ * @order: DFSM filter order.
+ * @oversampling: DFSDM filter oversampling:
+ *		  post processing filter: min = 1, max = 1024.
+ */
+struct stm32_dfsdm_sinc_filter {
+	enum stm32_dfsdm_sinc_order order;
+	unsigned int oversampling;
+};
+
+/* DFSDM filter conversion trigger */
+enum stm32_dfsdm_trigger {
+	DFSDM_FILTER_SW_TRIGGER,   /* Software trigger */
+	DFSDM_FILTER_SYNC_TRIGGER, /* Synchronous with DFSDM0 */
+	DFSDM_FILTER_EXT_TRIGGER,  /* External trigger (only for injected) */
+};
+
+/* DFSDM filter external trigger polarity */
+enum stm32_dfsdm_filter_ext_trigger_pol {
+	DFSDM_FILTER_EXT_TRIG_NO_TRIG,      /* Trigger disable */
+	DFSDM_FILTER_EXT_TRIG_RISING_EDGE,  /* Rising edge */
+	DFSDM_FILTER_EXT_TRIG_FALLING_EDGE, /* Falling edge */
+	DFSDM_FILTER_EXT_TRIG_BOTH_EDGES,   /* Rising and falling edges */
+};
+
+/* DFSDM filter conversion type */
+enum stm32_dfsdm_conv_type {
+	DFSDM_FILTER_REG_CONV,      /* Regular conversion */
+	DFSDM_FILTER_SW_INJ_CONV,   /* Injected conversion */
+	DFSDM_FILTER_TRIG_INJ_CONV, /* Injected conversion */
+};
+
+/* DFSDM filter regular synchronous mode */
+enum stm32_dfsdm_conv_rsync {
+	DFSDM_FILTER_RSYNC_OFF, /* regular conversion asynchronous */
+	DFSDM_FILTER_RSYNC_ON,  /* regular conversion synchronous with filter0*/
+};
+
+/**
+ * struct stm32_dfsdm_regular - DFSDM filter conversion parameters structure
+ * @ch_src:	Channel source from 0 to 7.
+ * @fast_mode:	Enable/disable fast mode for regular conversion.
+ * @dma_mode:	Enable/disable dma mode.
+ * @cont_mode	Enable/disable continuous conversion.
+ * @sync_mode	Enable/disable synchro mode.
+ */
+struct stm32_dfsdm_regular {
+	unsigned int ch_src;
+	bool fast_mode;
+	bool dma_mode;
+	bool cont_mode;
+	bool sync_mode;
+};
+
+/**
+ * struct stm32_dfsdm_injected - DFSDM filter  conversion parameters structure
+ * @trigger:	Trigger used to start injected conversion.
+ * @trig_src:	External trigger, 0 to 30 (refer to datasheet for details).
+ * @trig_pol:	External trigger edge: software, rising, falling or both.
+ * @scan_mode:	Enable/disable scan mode for injected conversion.
+ * @ch_group:	mask containing channels to scan ( set bit y to scan
+ *		channel y).
+ * @dma_mode:	DFSDM channel input parameters.
+ */
+struct stm32_dfsdm_injected {
+	enum stm32_dfsdm_trigger trigger;
+	unsigned int trig_src;
+	enum stm32_dfsdm_filter_ext_trigger_pol trig_pol;
+	bool scan_mode;
+	unsigned int ch_group;
+	bool dma_mode;
+};
+
+struct stm32_dfsdm;
+
+/**
+ * struct stm32_dfsdm_fl_event - DFSDM filters event
+ * @cb:	User event callback with parameters. be carful this function
+ *		is called under threaded IRQ context:
+ *			struct stm32_dfsdm *dfsdm: dfsdm handle,
+ *			unsigned int fl_id: filter id,
+ *			num stm32_dfsdm_events flag: event,
+ *			param: parameter associated to the event,
+ *			void *context: user context provided on registration.
+ * @context: User param to retrieve context.
+ */
+struct stm32_dfsdm_fl_event {
+	void (*cb)(struct stm32_dfsdm *, int, enum stm32_dfsdm_events,
+		   unsigned int, void *);
+	void *context;
+};
+
+/**
+ * struct stm32_dfsdm_filter - DFSDM filter  conversion parameters structure
+ * @reg_params:		DFSDM regular conversion parameters.
+ *			this param is optional and not taken into account if
+ *			@inj_params is defined.
+ * @inj_params:		DFSDM injected conversion parameters (optional).
+ * @filter_params:	DFSDM filter parameters.
+ * @event:		Events callback.
+ * @int_oversampling:	Integrator oversampling ratio for average purpose
+ *			(range from 1 to 256).
+ * @ext_det_ch_mask:	Extreme detector mask for channel selection
+ *			mask generated using DFSDM_CHANNEL_0 to
+ *			DFSDM_CHANNEL_7. If 0 feature is disable.
+ */
+struct stm32_dfsdm_filter {
+	struct stm32_dfsdm_regular *reg_params;
+	struct stm32_dfsdm_injected *inj_params;
+	struct stm32_dfsdm_sinc_filter sinc_params;
+	struct stm32_dfsdm_fl_event event;
+	unsigned int int_oversampling;
+};
+
+/**
+ * struct stm32_dfsdm - DFSDM context structure.
+ *
+ * @trig_info: Trigger name and id available last member name is null.
+ * @max_channels: max number of channels available.
+ * @max_filters: max number of filters available.
+ *
+ * Notice That structure is filled by mdf driver and must not be updated by
+ * user.
+ */
+struct stm32_dfsdm {
+	unsigned int max_channels;
+	unsigned int max_filters;
+};
+
+int stm32_dfsdm_get_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
+void stm32_dfsdm_release_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
+
+dma_addr_t stm32_dfsdm_get_filter_dma_phy_addr(struct stm32_dfsdm *dfsdm,
+					       unsigned int fl_id,
+					       enum stm32_dfsdm_conv_type conv);
+
+int stm32_dfsdm_configure_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+				 struct stm32_dfsdm_filter *filter);
+void stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+			      enum stm32_dfsdm_conv_type conv);
+void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
+
+void stm32_dfsdm_read_fl_conv(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+			      u32 *val, int *ch_id,
+			      enum stm32_dfsdm_conv_type type);
+
+int stm32_dfsdm_unregister_fl_event(struct stm32_dfsdm *dfsdm,
+				    unsigned int fl_id,
+				    enum stm32_dfsdm_events event,
+				    unsigned int ch_mask);
+int stm32_dfsdm_register_fl_event(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+				  enum stm32_dfsdm_events event,
+				  unsigned int ch_mask);
+
+int stm32_dfsdm_get_channel(struct stm32_dfsdm *dfsdm,
+			    struct stm32_dfsdm_channel *ch);
+void stm32_dfsdm_release_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
+
+int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id,
+			      struct stm32_dfsdm_ch_cfg *cfg);
+void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
+
+int stm32_dfsdm_get_clk_out_rate(struct stm32_dfsdm *dfsdm,
+				 unsigned long *rate);
+
+#endif
-- 
1.9.1


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

* [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-23 16:32     ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-23 16:32 UTC (permalink / raw)
  To: linux-arm-kernel

DFSDM hardware IP can be used at the same time for ADC sigma delta
conversion and audio PDM microphone.
MFD driver is in charge of configuring IP registers and managing IP clocks.
For this it exports an API to handles filters and channels resources.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 drivers/mfd/Kconfig             |   11 +
 drivers/mfd/Makefile            |    2 +
 drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
 drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
 include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
 5 files changed, 1601 insertions(+)
 create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
 create mode 100644 drivers/mfd/stm32-dfsdm.c
 create mode 100644 include/linux/mfd/stm32-dfsdm.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index c6df644..4bb660b 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1607,6 +1607,17 @@ config MFD_STW481X
 	  in various ST Microelectronics and ST-Ericsson embedded
 	  Nomadik series.
 
+config MFD_STM32_DFSDM
+	tristate "ST Microelectronics STM32 DFSDM"
+	depends on (ARCH_STM32 && OF) || COMPILE_TEST
+	select MFD_CORE
+	select REGMAP
+	select REGMAP_MMIO
+	help
+	  Select this option to enable the STM32 Digital Filter
+	  for Sigma Delta Modulators (DFSDM) driver used
+	  in various STM32 series.
+
 menu "Multimedia Capabilities Port drivers"
 	depends on ARCH_SA1100
 
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 9834e66..1f095e5 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -211,3 +211,5 @@ obj-$(CONFIG_INTEL_SOC_PMIC)	+= intel-soc-pmic.o
 obj-$(CONFIG_MFD_MT6397)	+= mt6397-core.o
 
 obj-$(CONFIG_MFD_ALTERA_A10SR)	+= altera-a10sr.o
+
+obj-$(CONFIG_MFD_STM32_DFSDM)	+= stm32-dfsdm.o
\ No newline at end of file
diff --git a/drivers/mfd/stm32-dfsdm-reg.h b/drivers/mfd/stm32-dfsdm-reg.h
new file mode 100644
index 0000000..05ff702
--- /dev/null
+++ b/drivers/mfd/stm32-dfsdm-reg.h
@@ -0,0 +1,220 @@
+/*
+ * This file is part of STM32 DFSDM mfd driver
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com>.
+ *
+ * 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.
+ */
+
+#ifndef MDF_STM32_DFSDM_REG_H
+#define MDF_STM32_DFSDM_REG_H
+
+#include <linux/bitfield.h>
+/*
+ * Channels definitions
+ */
+#define DFSDM_CHCFGR1(y)  ((y) * 0x20 + 0x00)
+#define DFSDM_CHCFGR2(y)  ((y) * 0x20 + 0x04)
+#define DFSDM_AWSCDR(y)   ((y) * 0x20 + 0x08)
+#define DFSDM_CHWDATR(y)  ((y) * 0x20 + 0x0C)
+#define DFSDM_CHDATINR(y) ((y) * 0x20 + 0x10)
+
+/* CHCFGR1: Channel configuration register 1 */
+#define DFSDM_CHCFGR1_SITP_MASK     GENMASK(1, 0)
+#define DFSDM_CHCFGR1_SITP(v)       FIELD_PREP(DFSDM_CHCFGR1_SITP_MASK, v)
+#define DFSDM_CHCFGR1_SPICKSEL_MASK GENMASK(3, 2)
+#define DFSDM_CHCFGR1_SPICKSEL(v)   FIELD_PREP(DFSDM_CHCFGR1_SPICKSEL_MASK, v)
+#define DFSDM_CHCFGR1_SCDEN_MASK    BIT(5)
+#define DFSDM_CHCFGR1_SCDEN(v)      FIELD_PREP(DFSDM_CHCFGR1_SCDEN_MASK, v)
+#define DFSDM_CHCFGR1_CKABEN_MASK   BIT(6)
+#define DFSDM_CHCFGR1_CKABEN(v)     FIELD_PREP(DFSDM_CHCFGR1_CKABEN_MASK, v)
+#define DFSDM_CHCFGR1_CHEN_MASK     BIT(7)
+#define DFSDM_CHCFGR1_CHEN(v)       FIELD_PREP(DFSDM_CHCFGR1_CHEN_MASK, v)
+#define DFSDM_CHCFGR1_CHINSEL_MASK  BIT(8)
+#define DFSDM_CHCFGR1_CHINSEL(v)    FIELD_PREP(DFSDM_CHCFGR1_CHINSEL_MASK, v)
+#define DFSDM_CHCFGR1_DATMPX_MASK   GENMASK(13, 12)
+#define DFSDM_CHCFGR1_DATMPX(v)     FIELD_PREP(DFSDM_CHCFGR1_DATMPX_MASK, v)
+#define DFSDM_CHCFGR1_DATPACK_MASK  GENMASK(15, 14)
+#define DFSDM_CHCFGR1_DATPACK(v)    FIELD_PREP(DFSDM_CHCFGR1_DATPACK_MASK, v)
+#define DFSDM_CHCFGR1_CKOUTDIV_MASK GENMASK(23, 16)
+#define DFSDM_CHCFGR1_CKOUTDIV(v)   FIELD_PREP(DFSDM_CHCFGR1_CKOUTDIV_MASK, v)
+#define DFSDM_CHCFGR1_CKOUTSRC_MASK BIT(30)
+#define DFSDM_CHCFGR1_CKOUTSRC(v)   FIELD_PREP(DFSDM_CHCFGR1_CKOUTSRC_MASK, v)
+#define DFSDM_CHCFGR1_DFSDMEN_MASK  BIT(31)
+#define DFSDM_CHCFGR1_DFSDMEN(v)    FIELD_PREP(DFSDM_CHCFGR1_DFSDMEN_MASK, v)
+
+/* CHCFGR2: Channel configuration register 2 */
+#define DFSDM_CHCFGR2_DTRBS_MASK    GENMASK(7, 3)
+#define DFSDM_CHCFGR2_DTRBS(v)      FIELD_PREP(DFSDM_CHCFGR2_DTRBS_MASK, v)
+#define DFSDM_CHCFGR2_OFFSET_MASK   GENMASK(31, 8)
+#define DFSDM_CHCFGR2_OFFSET(v)     FIELD_PREP(DFSDM_CHCFGR2_OFFSET_MASK, v)
+
+/* AWSCDR: Channel analog watchdog and short circuit detector */
+#define DFSDM_AWSCDR_SCDT_MASK    GENMASK(7, 0)
+#define DFSDM_AWSCDR_SCDT(v)      FIELD_PREP(DFSDM_AWSCDR_SCDT_MASK, v)
+#define DFSDM_AWSCDR_BKSCD_MASK   GENMASK(15, 12)
+#define DFSDM_AWSCDR_BKSCD(v)	  FIELD_PREP(DFSDM_AWSCDR_BKSCD_MASK, v)
+#define DFSDM_AWSCDR_AWFOSR_MASK  GENMASK(20, 16)
+#define DFSDM_AWSCDR_AWFOSR(v)    FIELD_PREP(DFSDM_AWSCDR_AWFOSR_MASK, v)
+#define DFSDM_AWSCDR_AWFORD_MASK  GENMASK(23, 22)
+#define DFSDM_AWSCDR_AWFORD(v)    FIELD_PREP(DFSDM_AWSCDR_AWFORD_MASK, v)
+
+/*
+ * Filters definitions
+ */
+#define DFSDM_FILTER_BASE_ADR		0x100
+#define DFSDM_FILTER_REG_MASK		0x7F
+#define DFSDM_FILTER_X_BASE_ADR(x)	((x) * 0x80 + DFSDM_FILTER_BASE_ADR)
+
+#define DFSDM_CR1(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x00)
+#define DFSDM_CR2(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x04)
+#define DFSDM_ISR(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x08)
+#define DFSDM_ICR(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x0C)
+#define DFSDM_JCHGR(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x10)
+#define DFSDM_FCR(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x14)
+#define DFSDM_JDATAR(x)  (DFSDM_FILTER_X_BASE_ADR(x)  + 0x18)
+#define DFSDM_RDATAR(x)  (DFSDM_FILTER_X_BASE_ADR(x)  + 0x1C)
+#define DFSDM_AWHTR(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x20)
+#define DFSDM_AWLTR(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x24)
+#define DFSDM_AWSR(x)    (DFSDM_FILTER_X_BASE_ADR(x)  + 0x28)
+#define DFSDM_AWCFR(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x2C)
+#define DFSDM_EXMAX(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x30)
+#define DFSDM_EXMIN(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x34)
+#define DFSDM_CNVTIMR(x) (DFSDM_FILTER_X_BASE_ADR(x)  + 0x38)
+
+/* CR1 Control register 1 */
+#define DFSDM_CR1_DFEN_MASK	BIT(0)
+#define DFSDM_CR1_DFEN(v)	FIELD_PREP(DFSDM_CR1_DFEN_MASK, v)
+#define DFSDM_CR1_JSWSTART_MASK	BIT(1)
+#define DFSDM_CR1_JSWSTART(v)	FIELD_PREP(DFSDM_CR1_JSWSTART_MASK, v)
+#define DFSDM_CR1_JSYNC_MASK	BIT(3)
+#define DFSDM_CR1_JSYNC(v)	FIELD_PREP(DFSDM_CR1_JSYNC_MASK, v)
+#define DFSDM_CR1_JSCAN_MASK	BIT(4)
+#define DFSDM_CR1_JSCAN(v)	FIELD_PREP(DFSDM_CR1_JSCAN_MASK, v)
+#define DFSDM_CR1_JDMAEN_MASK	BIT(5)
+#define DFSDM_CR1_JDMAEN(v)	FIELD_PREP(DFSDM_CR1_JDMAEN_MASK, v)
+#define DFSDM_CR1_JEXTSEL_MASK	GENMASK(12, 8)
+#define DFSDM_CR1_JEXTSEL(v)	FIELD_PREP(DFSDM_CR1_JEXTSEL_MASK, v)
+#define DFSDM_CR1_JEXTEN_MASK	GENMASK(14, 13)
+#define DFSDM_CR1_JEXTEN(v)	FIELD_PREP(DFSDM_CR1_JEXTEN_MASK, v)
+#define DFSDM_CR1_RSWSTART_MASK	BIT(17)
+#define DFSDM_CR1_RSWSTART(v)	FIELD_PREP(DFSDM_CR1_RSWSTART_MASK, v)
+#define DFSDM_CR1_RCONT_MASK	BIT(18)
+#define DFSDM_CR1_RCONT(v)	FIELD_PREP(DFSDM_CR1_RCONT_MASK, v)
+#define DFSDM_CR1_RSYNC_MASK	BIT(19)
+#define DFSDM_CR1_RSYNC(v)	FIELD_PREP(DFSDM_CR1_RSYNC_MASK, v)
+#define DFSDM_CR1_RDMAEN_MASK	BIT(21)
+#define DFSDM_CR1_RDMAEN(v)	FIELD_PREP(DFSDM_CR1_RDMAEN_MASK, v)
+#define DFSDM_CR1_RCH_MASK	GENMASK(26, 24)
+#define DFSDM_CR1_RCH(v)	FIELD_PREP(DFSDM_CR1_RCH_MASK, v)
+#define DFSDM_CR1_FAST_MASK	BIT(29)
+#define DFSDM_CR1_FAST(v)	FIELD_PREP(DFSDM_CR1_FAST_MASK, v)
+#define DFSDM_CR1_AWFSEL_MASK	BIT(30)
+#define DFSDM_CR1_AWFSEL(v)	FIELD_PREP(DFSDM_CR1_AWFSEL_MASK, v)
+
+/* CR2: Control register 2 */
+#define DFSDM_CR2_IE_MASK	GENMASK(6, 0)
+#define DFSDM_CR2_IE(v)		FIELD_PREP(DFSDM_CR2_IE_MASK, v)
+#define DFSDM_CR2_JEOCIE_MASK	BIT(0)
+#define DFSDM_CR2_JEOCIE(v)	FIELD_PREP(DFSDM_CR2_JEOCIE_MASK, v)
+#define DFSDM_CR2_REOCIE_MASK	BIT(1)
+#define DFSDM_CR2_REOCIE(v)	FIELD_PREP(DFSDM_CR2_REOCIE_MASK, v)
+#define DFSDM_CR2_JOVRIE_MASK	BIT(2)
+#define DFSDM_CR2_JOVRIE(v)	FIELD_PREP(DFSDM_CR2_JOVRIE_MASK, v)
+#define DFSDM_CR2_ROVRIE_MASK	BIT(3)
+#define DFSDM_CR2_ROVRIE(v)	FIELD_PREP(DFSDM_CR2_ROVRIE_MASK, v)
+#define DFSDM_CR2_AWDIE_MASK	BIT(4)
+#define DFSDM_CR2_AWDIE(v)	FIELD_PREP(DFSDM_CR2_AWDIE_MASK, v)
+#define DFSDM_CR2_SCDIE_MASK	BIT(5)
+#define DFSDM_CR2_SCDIE(v)	FIELD_PREP(DFSDM_CR2_SCDIE_MASK, v)
+#define DFSDM_CR2_CKABIE_MASK	BIT(6)
+#define DFSDM_CR2_CKABIE(v)	FIELD_PREP(DFSDM_CR2_CKABIE_MASK, v)
+#define DFSDM_CR2_EXCH_MASK	GENMASK(15, 8)
+#define DFSDM_CR2_EXCH(v)	FIELD_PREP(DFSDM_CR2_EXCH_MASK, v)
+#define DFSDM_CR2_AWDCH_MASK	GENMASK(23, 16)
+#define DFSDM_CR2_AWDCH(v)	FIELD_PREP(DFSDM_CR2_AWDCH_MASK, v)
+
+/* ISR: Interrupt status register */
+#define DFSDM_ISR_JEOCF_MASK	BIT(0)
+#define DFSDM_ISR_JEOCF(v)	FIELD_PREP(DFSDM_ISR_JEOCF_MASK, v)
+#define DFSDM_ISR_REOCF_MASK	BIT(1)
+#define DFSDM_ISR_REOCF(v)	FIELD_PREP(DFSDM_ISR_REOCF_MASK, v)
+#define DFSDM_ISR_JOVRF_MASK	BIT(2)
+#define DFSDM_ISR_JOVRF(v)	FIELD_PREP(DFSDM_ISR_JOVRF_MASK, v)
+#define DFSDM_ISR_ROVRF_MASK	BIT(3)
+#define DFSDM_ISR_ROVRF(v)	FIELD_PREP(DFSDM_ISR_ROVRF_MASK, v)
+#define DFSDM_ISR_AWDF_MASK	BIT(4)
+#define DFSDM_ISR_AWDF(v)	FIELD_PREP(DFSDM_ISR_AWDF_MASK, v)
+#define DFSDM_ISR_JCIP_MASK	BIT(13)
+#define DFSDM_ISR_JCIP(v)	FIELD_PREP(DFSDM_ISR_JCIP_MASK, v)
+#define DFSDM_ISR_RCIP_MASK	BIT(14)
+#define DFSDM_ISR_RCIP(v)	FIELD_PREP(DFSDM_ISR_RCIP, v)
+#define DFSDM_ISR_CKABF_MASK	GENMASK(23, 16)
+#define DFSDM_ISR_CKABF(v)	FIELD_PREP(DFSDM_ISR_CKABF_MASK, v)
+#define DFSDM_ISR_SCDF_MASK	GENMASK(31, 24)
+#define DFSDM_ISR_SCDF(v)	FIELD_PREP(DFSDM_ISR_SCDF_MASK, v)
+
+/* ICR: Interrupt flag clear register */
+#define DFSDM_ICR_CLRJOVRF_MASK	      BIT(2)
+#define DFSDM_ICR_CLRJOVRF(v)	      FIELD_PREP(DFSDM_ICR_CLRJOVRF_MASK, v)
+#define DFSDM_ICR_CLRROVRF_MASK	      BIT(3)
+#define DFSDM_ICR_CLRROVRF(v)	      FIELD_PREP(DFSDM_ICR_CLRROVRF_MASK, v)
+#define DFSDM_ICR_CLRCKABF_MASK	      GENMASK(23, 16)
+#define DFSDM_ICR_CLRCKABF(v)	      FIELD_PREP(DFSDM_ICR_CLRCKABF_MASK, v)
+#define DFSDM_ICR_CLRCKABF_CH_MASK(y) BIT(16 + (y))
+#define DFSDM_ICR_CLRCKABF_CH(v, y)   \
+			   (((v) << (16 + (y))) & DFSDM_ICR_CLRCKABF_CH_MASK(y))
+#define DFSDM_ICR_CLRSCDF_MASK	      GENMASK(31, 24)
+#define DFSDM_ICR_CLRSCDF(v)	      FIELD_PREP(DFSDM_ICR_CLRSCDF_MASK, v)
+#define DFSDM_ICR_CLRSCDF_CH_MASK(y)  BIT(24 + (y))
+#define DFSDM_ICR_CLRSCDF_CH(v, y)    \
+			       (((v) << (24 + (y))) & DFSDM_ICR_CLRSCDF_MASK(y))
+
+/* FCR: Filter control register */
+#define DFSDM_FCR_IOSR_MASK	GENMASK(7, 0)
+#define DFSDM_FCR_IOSR(v)	FIELD_PREP(DFSDM_FCR_IOSR_MASK, v)
+#define DFSDM_FCR_FOSR_MASK	GENMASK(25, 16)
+#define DFSDM_FCR_FOSR(v)	FIELD_PREP(DFSDM_FCR_FOSR_MASK, v)
+#define DFSDM_FCR_FORD_MASK	GENMASK(31, 29)
+#define DFSDM_FCR_FORD(v)	FIELD_PREP(DFSDM_FCR_FORD_MASK, v)
+
+/* RDATAR: Filter data register for regular channel */
+#define DFSDM_DATAR_CH_MASK	GENMASK(2, 0)
+#define DFSDM_DATAR_DATA_OFFSET 8
+#define DFSDM_DATAR_DATA_MASK	GENMASK(31, DFSDM_DATAR_DATA_OFFSET)
+
+/* AWLTR: Filter analog watchdog low threshold register */
+#define DFSDM_AWLTR_BKAWL_MASK	GENMASK(3, 0)
+#define DFSDM_AWLTR_BKAWL(v)	FIELD_PREP(DFSDM_AWLTR_BKAWL_MASK, v)
+#define DFSDM_AWLTR_AWLT_MASK	GENMASK(31, 8)
+#define DFSDM_AWLTR_AWLT(v)	FIELD_PREP(DFSDM_AWLTR_AWLT_MASK, v)
+
+/* AWHTR: Filter analog watchdog low threshold register */
+#define DFSDM_AWHTR_BKAWH_MASK	GENMASK(3, 0)
+#define DFSDM_AWHTR_BKAWH(v)	FIELD_PREP(DFSDM_AWHTR_BKAWH_MASK, v)
+#define DFSDM_AWHTR_AWHT_MASK	GENMASK(31, 8)
+#define DFSDM_AWHTR_AWHT(v)	FIELD_PREP(DFSDM_AWHTR_AWHT_MASK, v)
+
+/* AWSR: Filter watchdog status register */
+#define DFSDM_AWSR_AWLTF_MASK	GENMASK(7, 0)
+#define DFSDM_AWSR_AWLTF(v)	FIELD_PREP(DFSDM_AWSR_AWLTF_MASK, v)
+#define DFSDM_AWSR_AWHTF_MASK	GENMASK(15, 8)
+#define DFSDM_AWSR_AWHTF(v)	FIELD_PREP(DFSDM_AWSR_AWHTF_MASK, v)
+
+/* AWCFR: Filter watchdog status register */
+#define DFSDM_AWCFR_AWLTF_MASK	GENMASK(7, 0)
+#define DFSDM_AWCFR_AWLTF(v)	FIELD_PREP(DFSDM_AWCFR_AWLTF_MASK, v)
+#define DFSDM_AWCFR_AWHTF_MASK	GENMASK(15, 8)
+#define DFSDM_AWCFR_AWHTF(v)	FIELD_PREP(DFSDM_AWCFR_AWHTF_MASK, v)
+
+#endif
diff --git a/drivers/mfd/stm32-dfsdm.c b/drivers/mfd/stm32-dfsdm.c
new file mode 100644
index 0000000..81ca29c
--- /dev/null
+++ b/drivers/mfd/stm32-dfsdm.c
@@ -0,0 +1,1044 @@
+/*
+ * This file is part of STM32 DFSDM mfd driver
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author(s): Arnaud Pouliquen <arnaud.pouliquen@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/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/stm32-dfsdm.h>
+
+#include "stm32-dfsdm-reg.h"
+
+#define DFSDM_UPDATE_BITS(regm, reg, mask, val) \
+		WARN_ON(regmap_update_bits(regm, reg, mask, val))
+
+#define DFSDM_REG_READ(regm, reg, val) \
+		WARN_ON(regmap_read(regm, reg, val))
+
+#define DFSDM_REG_WRITE(regm, reg, val) \
+		WARN_ON(regmap_write(regm, reg, val))
+
+#define STM32H7_DFSDM_NUM_FILTERS	4
+#define STM32H7_DFSDM_NUM_INPUTS	8
+
+enum dfsdm_clkout_src {
+	DFSDM_CLK,
+	AUDIO_CLK
+};
+
+struct stm32_dev_data {
+	const struct stm32_dfsdm dfsdm;
+	const struct regmap_config *regmap_cfg;
+};
+
+struct dfsdm_priv;
+
+struct filter_params {
+	unsigned int id;
+	int irq;
+	struct stm32_dfsdm_fl_event event;
+	u32 event_mask;
+	struct dfsdm_priv *priv; /* Cross ref for context */
+	unsigned int ext_ch_mask;
+	unsigned int scan_ch;
+};
+
+struct ch_params {
+	struct stm32_dfsdm_channel ch;
+};
+
+struct dfsdm_priv {
+	struct platform_device *pdev;
+	struct stm32_dfsdm dfsdm;
+
+	spinlock_t lock; /* Used for resource sharing & interrupt lock */
+
+	/* Filters */
+	struct filter_params *filters;
+	unsigned int free_filter_mask;
+	unsigned int scd_filter_mask;
+	unsigned int ckab_filter_mask;
+
+	/* Channels */
+	struct stm32_dfsdm_channel *channels;
+	unsigned int free_channel_mask;
+	atomic_t n_active_ch;
+
+	/* Clock */
+	struct clk *clk;
+	struct clk *aclk;
+	unsigned int clkout_div;
+	unsigned int clkout_freq_req;
+
+	/* Registers*/
+	void __iomem *base;
+	struct regmap *regmap;
+	phys_addr_t phys_base;
+};
+
+/*
+ * Common
+ */
+static bool stm32_dfsdm_volatile_reg(struct device *dev, unsigned int reg)
+{
+	if (reg < DFSDM_FILTER_BASE_ADR)
+		return false;
+
+	/*
+	 * Mask is done on register to avoid to list registers of all them
+	 * filter instances.
+	 */
+	switch (reg & DFSDM_FILTER_REG_MASK) {
+	case DFSDM_CR1(0) & DFSDM_FILTER_REG_MASK:
+	case DFSDM_ISR(0) & DFSDM_FILTER_REG_MASK:
+	case DFSDM_JDATAR(0) & DFSDM_FILTER_REG_MASK:
+	case DFSDM_RDATAR(0) & DFSDM_FILTER_REG_MASK:
+		return true;
+	}
+
+	return false;
+}
+
+static const struct regmap_config stm32h7_dfsdm_regmap_cfg = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = sizeof(u32),
+	.max_register = DFSDM_CNVTIMR(STM32H7_DFSDM_NUM_FILTERS - 1),
+	.volatile_reg = stm32_dfsdm_volatile_reg,
+	.fast_io = true,
+};
+
+static const struct stm32_dev_data stm32h7_data = {
+	.dfsdm.max_channels = STM32H7_DFSDM_NUM_INPUTS,
+	.dfsdm.max_filters = STM32H7_DFSDM_NUM_FILTERS,
+	.regmap_cfg = &stm32h7_dfsdm_regmap_cfg,
+};
+
+static int stm32_dfsdm_start_dfsdm(struct dfsdm_priv *priv)
+{
+	int ret;
+	struct device *dev = &priv->pdev->dev;
+
+	if (atomic_inc_return(&priv->n_active_ch) == 1) {
+		ret = clk_prepare_enable(priv->clk);
+		if (ret < 0) {
+			dev_err(dev, "Failed to start clock\n");
+			return ret;
+		}
+		if (priv->aclk) {
+			ret = clk_prepare_enable(priv->aclk);
+			if (ret < 0) {
+				dev_err(dev, "Failed to start audio clock\n");
+				return ret;
+			}
+		}
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
+				  DFSDM_CHCFGR1_CKOUTDIV_MASK,
+				  DFSDM_CHCFGR1_CKOUTDIV(priv->clkout_div));
+
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
+				  DFSDM_CHCFGR1_DFSDMEN_MASK,
+				  DFSDM_CHCFGR1_DFSDMEN(1));
+	}
+
+	dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,
+		atomic_read(&priv->n_active_ch));
+
+	return 0;
+}
+
+static void stm32_dfsdm_stop_dfsdm(struct dfsdm_priv *priv)
+{
+	if (atomic_dec_and_test(&priv->n_active_ch)) {
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
+				  DFSDM_CHCFGR1_DFSDMEN_MASK,
+				  DFSDM_CHCFGR1_DFSDMEN(0));
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
+				  DFSDM_CHCFGR1_CKOUTDIV_MASK,
+				  DFSDM_CHCFGR1_CKOUTDIV(0));
+		clk_disable_unprepare(priv->clk);
+		if (priv->aclk)
+			clk_disable_unprepare(priv->aclk);
+	}
+	dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,
+		atomic_read(&priv->n_active_ch));
+}
+
+static unsigned int stm32_dfsdm_get_clkout_divider(struct dfsdm_priv *priv,
+						   unsigned long rate)
+{
+	unsigned int delta, div;
+
+	/* div = 0 disables the clockout */
+	if (!priv->clkout_freq_req)
+		return 0;
+
+	div = DIV_ROUND_CLOSEST(rate, priv->clkout_freq_req);
+
+	delta = rate - (priv->clkout_freq_req * div);
+	if (delta)
+		dev_warn(&priv->pdev->dev,
+			 "clkout not accurate. delta (Hz): %d\n", delta);
+
+	dev_dbg(&priv->pdev->dev, "%s: clk: %lu (Hz), div %u\n",
+		__func__, rate, div);
+
+	return (div - 1);
+}
+
+/*
+ * Filters
+ */
+
+static int stm32_dfsdm_clear_event(struct dfsdm_priv *priv, unsigned int fl_id,
+				   unsigned int event, int mask)
+{
+	int val;
+
+	switch (event) {
+	case DFSDM_EVENT_INJ_EOC:
+		DFSDM_REG_READ(priv->regmap, DFSDM_JDATAR(fl_id), &val);
+		break;
+	case DFSDM_EVENT_REG_EOC:
+		DFSDM_REG_READ(priv->regmap, DFSDM_RDATAR(fl_id), &val);
+		break;
+	case DFSDM_EVENT_INJ_XRUN:
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_ICR(fl_id),
+				  DFSDM_ICR_CLRJOVRF_MASK,
+				  DFSDM_ICR_CLRJOVRF_MASK);
+		break;
+	case DFSDM_EVENT_REG_XRUN:
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_ICR(fl_id),
+				  DFSDM_ICR_CLRROVRF_MASK,
+				  DFSDM_ICR_CLRROVRF_MASK);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
+{
+	struct filter_params *params = arg;
+	unsigned int status;
+	struct dfsdm_priv *priv = params->priv;
+	unsigned int event_mask = params->event_mask;
+
+	DFSDM_REG_READ(priv->regmap, DFSDM_ISR(params->id), &status);
+
+	if (status & DFSDM_ISR_JOVRF_MASK) {
+		if (event_mask & DFSDM_EVENT_INJ_XRUN) {
+			params->event.cb(&priv->dfsdm, params->id,
+					 DFSDM_EVENT_INJ_XRUN, 0,
+					 params->event.context);
+		}
+		stm32_dfsdm_clear_event(priv, params->id, DFSDM_EVENT_INJ_XRUN,
+					0);
+	}
+
+	if (status & DFSDM_ISR_ROVRF_MASK) {
+		if (event_mask & DFSDM_EVENT_REG_XRUN) {
+			params->event.cb(&priv->dfsdm, params->id,
+					 DFSDM_EVENT_REG_XRUN, 0,
+					 params->event.context);
+		}
+		stm32_dfsdm_clear_event(priv, params->id, DFSDM_EVENT_REG_XRUN,
+					0);
+	}
+
+	if (status & DFSDM_ISR_JEOCF_MASK) {
+		if (event_mask & DFSDM_EVENT_INJ_EOC)
+			params->event.cb(&priv->dfsdm, params->id,
+					 DFSDM_EVENT_INJ_EOC, 0,
+					 params->event.context);
+		else
+			stm32_dfsdm_clear_event(priv, params->id,
+						DFSDM_EVENT_INJ_EOC, 0);
+	}
+
+	if (status & DFSDM_ISR_REOCF_MASK) {
+		if (event_mask & DFSDM_EVENT_REG_EOC)
+			params->event.cb(&priv->dfsdm, params->id,
+					 DFSDM_EVENT_REG_EOC, 0,
+					 params->event.context);
+		else
+			stm32_dfsdm_clear_event(priv, params->id,
+						DFSDM_EVENT_REG_EOC, 0);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void stm32_dfsdm_configure_reg_conv(struct dfsdm_priv *priv,
+					   unsigned int fl_id,
+					   struct stm32_dfsdm_regular *params)
+{
+	unsigned int ch_id = params->ch_src;
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RCH_MASK,
+			  DFSDM_CR1_RCH(ch_id));
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_FAST_MASK,
+			  DFSDM_CR1_FAST(params->fast_mode));
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RCONT_MASK,
+			  DFSDM_CR1_RCONT(params->cont_mode));
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RDMAEN_MASK,
+			  DFSDM_CR1_RDMAEN(params->dma_mode));
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RSYNC_MASK,
+			  DFSDM_CR1_RSYNC(params->sync_mode));
+
+	priv->filters[fl_id].scan_ch = BIT(ch_id);
+}
+
+static void stm32_dfsdm_configure_inj_conv(struct dfsdm_priv *priv,
+					   unsigned int fl_id,
+					   struct stm32_dfsdm_injected *params)
+{
+	int val;
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_JSCAN_MASK,
+			  DFSDM_CR1_JSCAN(params->scan_mode));
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_JDMAEN_MASK,
+			  DFSDM_CR1_JDMAEN(params->dma_mode));
+
+	val = (params->trigger == DFSDM_FILTER_EXT_TRIGGER) ?
+	      params->trig_src : 0;
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id),
+			  DFSDM_CR1_JEXTSEL_MASK,
+			  DFSDM_CR1_JEXTSEL(val));
+
+	val = (params->trigger == DFSDM_FILTER_SYNC_TRIGGER) ? 1 : 0;
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_JSYNC_MASK,
+			  DFSDM_CR1_JSYNC(val));
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_JEXTEN_MASK,
+			  DFSDM_CR1_JEXTEN(params->trig_pol));
+	priv->filters[fl_id].scan_ch = params->ch_group;
+
+	DFSDM_REG_WRITE(priv->regmap, DFSDM_JCHGR(fl_id), params->ch_group);
+}
+
+/**
+ * stm32_dfsdm_configure_filter - Configure filter.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter id.
+ * @conv: Conversion type regular or injected.
+ */
+int stm32_dfsdm_configure_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+				 struct stm32_dfsdm_filter *fl_cfg)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv,
+					       dfsdm);
+	struct stm32_dfsdm_sinc_filter *sparams = &fl_cfg->sinc_params;
+
+	dev_dbg(&priv->pdev->dev, "%s:config filter %d\n", __func__, fl_id);
+
+	/* Average integrator oversampling */
+	if ((!fl_cfg->int_oversampling) ||
+	    (fl_cfg->int_oversampling > DFSDM_MAX_INT_OVERSAMPLING)) {
+		dev_err(&priv->pdev->dev, "invalid integrator oversampling %d\n",
+			fl_cfg->int_oversampling);
+		return -EINVAL;
+	}
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_FCR(fl_id), DFSDM_FCR_IOSR_MASK,
+			  DFSDM_FCR_IOSR((fl_cfg->int_oversampling - 1)));
+
+	/* Oversamplings and filter*/
+	if ((!sparams->oversampling) ||
+	    (sparams->oversampling > DFSDM_MAX_FL_OVERSAMPLING)) {
+		dev_err(&priv->pdev->dev, "invalid oversampling %d\n",
+			sparams->oversampling);
+		return -EINVAL;
+	}
+
+	if (sparams->order > DFSDM_SINC5_ORDER) {
+		dev_err(&priv->pdev->dev, "invalid filter order %d\n",
+			sparams->order);
+		return -EINVAL;
+	}
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FOSR_MASK,
+			  DFSDM_FCR_FOSR((sparams->oversampling - 1)));
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FORD_MASK,
+			  DFSDM_FCR_FORD(sparams->order));
+
+	/* Conversion */
+	if (fl_cfg->inj_params)
+		stm32_dfsdm_configure_inj_conv(priv, fl_id, fl_cfg->inj_params);
+	else if (fl_cfg->reg_params)
+		stm32_dfsdm_configure_reg_conv(priv, fl_id, fl_cfg->reg_params);
+
+	priv->filters[fl_id].event = fl_cfg->event;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dfsdm_configure_filter);
+
+/**
+ * stm32_dfsdm_start_filter - Start filter conversion.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter id.
+ * @conv: Conversion type regular or injected.
+ */
+void stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+			      enum stm32_dfsdm_conv_type conv)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+
+	dev_dbg(&priv->pdev->dev, "%s:start filter %d\n", __func__, fl_id);
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_DFEN_MASK,
+			  DFSDM_CR1_DFEN(1));
+
+	if (conv == DFSDM_FILTER_REG_CONV) {
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id),
+				  DFSDM_CR1_RSWSTART_MASK,
+				  DFSDM_CR1_RSWSTART(1));
+	} else if (conv == DFSDM_FILTER_SW_INJ_CONV) {
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id),
+				  DFSDM_CR1_JSWSTART_MASK,
+				  DFSDM_CR1_JSWSTART(1));
+	}
+}
+EXPORT_SYMBOL_GPL(dfsdm_start_filter);
+
+/**
+ * stm32_dfsdm_stop_filter - Stop filter conversion.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter id.
+ */
+void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+
+	dev_dbg(&priv->pdev->dev, "%s:stop filter %d\n", __func__, fl_id);
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_DFEN_MASK,
+			  DFSDM_CR1_DFEN(0));
+	priv->filters[fl_id].scan_ch = 0;
+}
+EXPORT_SYMBOL_GPL(dfsdm_stop_filter);
+
+/**
+ * stm32_dfsdm_read_fl_conv - Read filter conversion.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter id.
+ * @type: Regular or injected conversion.
+ */
+void stm32_dfsdm_read_fl_conv(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+			      u32 *val, int *ch_id,
+			      enum stm32_dfsdm_conv_type type)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+	int reg_v, offset;
+
+	if (type == DFSDM_FILTER_REG_CONV)
+		offset = DFSDM_RDATAR(fl_id);
+	else
+		offset = DFSDM_JDATAR(fl_id);
+
+	DFSDM_REG_READ(priv->regmap, offset, &reg_v);
+
+	*ch_id = reg_v & DFSDM_DATAR_CH_MASK;
+	*val = reg_v & DFSDM_DATAR_DATA_MASK;
+}
+EXPORT_SYMBOL_GPL(dfsdm_read_fl_conv);
+
+/**
+ * stm32_dfsdm_get_filter - Get filter instance.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter instance to reserve.
+ *
+ * Reserves a DFSDM filter resource.
+ */
+int stm32_dfsdm_get_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv,
+					       dfsdm);
+	struct device *dev = &priv->pdev->dev;
+
+	spin_lock(&priv->lock);
+	if (!(priv->free_filter_mask & BIT(fl_id))) {
+		spin_unlock(&priv->lock);
+		dev_err(dev, "filter resource %d available\n", fl_id);
+		return -EBUSY;
+	}
+	priv->free_filter_mask &= ~BIT(fl_id);
+
+	spin_unlock(&priv->lock);
+
+	dev_dbg(dev, "%s: new mask %#x\n", __func__, priv->free_filter_mask);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dfsdm_get_filter);
+
+/**
+ * stm32_dfsdm_release_filter - Release filter instance.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter id.
+ *
+ * Free the DFSDM filter resource.
+ */
+void stm32_dfsdm_release_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+
+	spin_lock(&priv->lock);
+	priv->free_filter_mask |= BIT(fl_id);
+	spin_unlock(&priv->lock);
+}
+EXPORT_SYMBOL_GPL(dfsdm_release_filter);
+
+/**
+ * stm32_dfsdm_get_filter_dma_addr - Get register address for dma transfer.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter id.
+ * @conv: Conversion type.
+ */
+dma_addr_t stm32_dfsdm_get_filter_dma_phy_addr(struct stm32_dfsdm *dfsdm,
+					       unsigned int fl_id,
+					       enum stm32_dfsdm_conv_type conv)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+
+	if (conv == DFSDM_FILTER_REG_CONV)
+		return (dma_addr_t)(priv->phys_base + DFSDM_RDATAR(fl_id));
+	else
+		return (dma_addr_t)(priv->phys_base + DFSDM_JDATAR(fl_id));
+}
+
+/**
+ * stm32_dfsdm_register_fl_event - Register filter event.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter id.
+ * @event: Event to unregister.
+ * @chan_mask: Mask of channels associated to filter.
+ *
+ * The function enables associated IRQ.
+ */
+int stm32_dfsdm_register_fl_event(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+				  enum stm32_dfsdm_events event,
+				  unsigned int chan_mask)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+	unsigned long flags, ulmask = chan_mask;
+	int ret, i;
+
+	dev_dbg(&priv->pdev->dev, "%s:for filter %d: event %#x ch_mask %#x\n",
+		__func__, fl_id, event, chan_mask);
+
+	if (event > DFSDM_EVENT_CKA)
+		return -EINVAL;
+
+	/* Clear interrupt before enable them */
+	ret = stm32_dfsdm_clear_event(priv, fl_id, event, chan_mask);
+	if (ret < 0)
+		return ret;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	/* Enable interrupts */
+	switch (event) {
+	case DFSDM_EVENT_SCD:
+		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
+			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
+					  DFSDM_CHCFGR1_SCDEN_MASK,
+					  DFSDM_CHCFGR1_SCDEN(1));
+		}
+		if (!priv->scd_filter_mask)
+			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
+					  DFSDM_CR2_SCDIE_MASK,
+					  DFSDM_CR2_SCDIE(1));
+		priv->scd_filter_mask |= BIT(fl_id);
+		break;
+	case DFSDM_EVENT_CKA:
+		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
+			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
+					  DFSDM_CHCFGR1_CKABEN_MASK,
+					  DFSDM_CHCFGR1_CKABEN(1));
+		}
+		if (!priv->ckab_filter_mask)
+			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
+					  DFSDM_CR2_CKABIE_MASK,
+					  DFSDM_CR2_CKABIE(1));
+		priv->ckab_filter_mask |= BIT(fl_id);
+		break;
+	default:
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(fl_id), event, event);
+	}
+	priv->filters[fl_id].event_mask |= event;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dfsdm_register_fl_event);
+
+/**
+ * stm32_dfsdm_unregister_fl_event - Unregister filter event.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @fl_id: Filter id.
+ * @event: Event to unregister.
+ * @chan_mask: Mask of channels associated to filter.
+ *
+ * The function disables associated IRQ.
+ */
+int stm32_dfsdm_unregister_fl_event(struct stm32_dfsdm *dfsdm,
+				    unsigned int fl_id,
+				    enum stm32_dfsdm_events event,
+				    unsigned int chan_mask)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+	unsigned long flags, ulmask = chan_mask;
+	int i;
+
+	dev_dbg(&priv->pdev->dev, "%s:for filter %d: event %#x ch_mask %#x\n",
+		__func__, fl_id, event, chan_mask);
+
+	if (event > DFSDM_EVENT_CKA)
+		return -EINVAL;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	/* Disable interrupts */
+	switch (event) {
+	case DFSDM_EVENT_SCD:
+		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
+			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
+					  DFSDM_CHCFGR1_SCDEN_MASK,
+					  DFSDM_CHCFGR1_SCDEN(0));
+		}
+		priv->scd_filter_mask &= ~BIT(fl_id);
+		if (!priv->scd_filter_mask)
+			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
+					  DFSDM_CR2_SCDIE_MASK,
+					  DFSDM_CR2_SCDIE(0));
+		break;
+	case DFSDM_EVENT_CKA:
+		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
+			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
+					  DFSDM_CHCFGR1_CKABEN_MASK,
+					  DFSDM_CHCFGR1_CKABEN(0));
+		}
+		priv->ckab_filter_mask &= ~BIT(fl_id);
+		if (!priv->ckab_filter_mask)
+			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
+					  DFSDM_CR2_CKABIE_MASK,
+					  DFSDM_CR2_CKABIE(0));
+		break;
+	default:
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(fl_id), event, 0);
+	}
+
+	priv->filters[fl_id].event_mask &= ~event;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dfsdm_unregister_fl_event);
+
+/*
+ * Channels
+ */
+static void stm32_dfsdm_init_channel(struct dfsdm_priv *priv,
+				     struct stm32_dfsdm_channel *ch)
+{
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
+			  DFSDM_CHCFGR1_DATMPX_MASK,
+			  DFSDM_CHCFGR1_DATMPX(ch->type.source));
+	if (ch->type.source == DFSDM_CHANNEL_EXTERNAL_INPUTS) {
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
+				  DFSDM_CHCFGR1_SITP_MASK,
+				  DFSDM_CHCFGR1_SITP(ch->serial_if.type));
+		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
+				  DFSDM_CHCFGR1_SPICKSEL_MASK,
+				DFSDM_CHCFGR1_SPICKSEL(ch->serial_if.spi_clk));
+	}
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
+			  DFSDM_CHCFGR1_DATPACK_MASK,
+			  DFSDM_CHCFGR1_DATPACK(ch->type.DataPacking));
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
+			  DFSDM_CHCFGR1_CHINSEL_MASK,
+			  DFSDM_CHCFGR1_CHINSEL(ch->serial_if.pins));
+}
+
+/**
+ * stm32_dfsdm_start_channel - Configure and activate DFSDM channel.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @ch: Filter id.
+ * @cfg: Filter configuration.
+ */
+int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id,
+			      struct stm32_dfsdm_ch_cfg *cfg)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv,
+					       dfsdm);
+	struct regmap *reg = priv->regmap;
+	int ret;
+
+	dev_dbg(&priv->pdev->dev, "%s: for channel %d\n", __func__, ch_id);
+
+	ret = stm32_dfsdm_start_dfsdm(priv);
+	if (ret < 0)
+		return ret;
+
+	DFSDM_UPDATE_BITS(reg, DFSDM_CHCFGR2(ch_id), DFSDM_CHCFGR2_DTRBS_MASK,
+			  DFSDM_CHCFGR2_DTRBS(cfg->right_bit_shift));
+	DFSDM_UPDATE_BITS(reg, DFSDM_CHCFGR2(ch_id), DFSDM_CHCFGR2_OFFSET_MASK,
+			  DFSDM_CHCFGR2_OFFSET(cfg->offset));
+
+	DFSDM_UPDATE_BITS(reg, DFSDM_CHCFGR1(ch_id), DFSDM_CHCFGR1_CHEN_MASK,
+			  DFSDM_CHCFGR1_CHEN(1));
+
+	/* Clear absence detection IRQ */
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_ICR(0),
+			  DFSDM_ICR_CLRCKABF_CH_MASK(ch_id),
+			  DFSDM_ICR_CLRCKABF_CH(1, ch_id));
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dfsdm_start_channel);
+
+/**
+ * stm32_dfsdm_stop_channel - Deactivate channel.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @ch_id: DFSDM channel identifier.
+ */
+void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+
+	dev_dbg(&priv->pdev->dev, "%s:for channel %d\n", __func__, ch_id);
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch_id),
+			  DFSDM_CHCFGR1_CHEN_MASK,
+			  DFSDM_CHCFGR1_CHEN(0));
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch_id),
+			  DFSDM_CHCFGR1_CKABEN_MASK, DFSDM_CHCFGR1_CKABEN(0));
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch_id),
+			  DFSDM_CHCFGR1_SCDEN_MASK, DFSDM_CHCFGR1_SCDEN(0));
+
+	stm32_dfsdm_stop_dfsdm(priv);
+}
+EXPORT_SYMBOL_GPL(dfsdm_stop_channel);
+
+/**
+ * stm32_dfsdm_get_channel - Get channel instance.
+ *
+ * @dfsdm: handle used to retrieve dfsdm context.
+ * @ch: DFSDM channel hardware parameters.
+ *
+ * Reserve DFSDM channel resource.
+ */
+int stm32_dfsdm_get_channel(struct stm32_dfsdm *dfsdm,
+			    struct stm32_dfsdm_channel *ch)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+	unsigned int id = ch->id;
+
+	dev_dbg(&priv->pdev->dev, "%s:get channel %d\n", __func__, id);
+
+	if (id >= priv->dfsdm.max_channels) {
+		dev_err(&priv->pdev->dev, "channel (%d) is not valid\n", id);
+		return -EINVAL;
+	}
+
+	if ((ch->type.source != DFSDM_CHANNEL_EXTERNAL_INPUTS) &
+	    (ch->serial_if.spi_clk != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL) &
+	    (!priv->clkout_freq_req)) {
+		dev_err(&priv->pdev->dev, "clkout not present\n");
+		return -EINVAL;
+	}
+
+	spin_lock(&priv->lock);
+	if (!(BIT(id) & priv->free_channel_mask)) {
+		spin_unlock(&priv->lock);
+		dev_err(&priv->pdev->dev, "channel (%d) already in use\n", id);
+		return -EBUSY;
+	}
+
+	priv->free_channel_mask &= ~BIT(id);
+	priv->channels[id] = *ch;
+	spin_unlock(&priv->lock);
+
+	dev_dbg(&priv->pdev->dev, "%s: new mask %#x\n", __func__,
+		priv->free_channel_mask);
+
+	/**
+	 * Check clock constrainst between clkout and either
+	 * dfsdm/audio clock:
+	 * - In SPI mode (clkout is used): Fclk >= 4 * Fclkout
+	 *   (e.g. CKOUTDIV >= 3)
+	 * - In mancherster mode: Fclk >= 6 * Fclkout
+	 */
+	switch (ch->serial_if.type) {
+	case DFSDM_CHANNEL_SPI_RISING:
+	case DFSDM_CHANNEL_SPI_FALLING:
+		if (priv->clkout_div && priv->clkout_div < 3)
+			dev_warn(&priv->pdev->dev,
+				 "Clock div should be higher than 3\n");
+		break;
+	case DFSDM_CHANNEL_MANCHESTER_RISING:
+	case DFSDM_CHANNEL_MANCHESTER_FALLING:
+		if (priv->clkout_div && priv->clkout_div < 5)
+			dev_warn(&priv->pdev->dev,
+				 "Clock div should be higher than 5\n");
+		break;
+	}
+
+	stm32_dfsdm_init_channel(priv, ch);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dfsdm_get_channel);
+
+/**
+ * stm32_dfsdm_release_channel - Release channel instance.
+ *
+ * @dfsdm: Handle used to retrieve dfsdm context.
+ * @ch_id: DFSDM channel identifier.
+ *
+ * Free the DFSDM channel resource.
+ */
+void stm32_dfsdm_release_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+
+	spin_lock(&priv->lock);
+	priv->free_channel_mask |= BIT(ch_id);
+	spin_unlock(&priv->lock);
+}
+EXPORT_SYMBOL_GPL(dfsdm_release_channel);
+
+/**
+ * stm32_dfsdm_get_clk_out_rate - get clkout frequency.
+ *
+ * @dfsdm: handle used to retrieve dfsdm context.
+ * @rate: clock out rate in Hz.
+ *
+ * Provide output frequency used for external ADC.
+ * return EINVAL if clockout is not used else return 0.
+ */
+int stm32_dfsdm_get_clk_out_rate(struct stm32_dfsdm *dfsdm, unsigned long *rate)
+{
+	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+	unsigned long int clk_rate;
+
+	if (!priv->clkout_div)
+		return -EINVAL;
+
+	clk_rate = clk_get_rate(priv->aclk ? priv->aclk : priv->clk);
+	*rate = clk_rate / (priv->clkout_div + 1);
+	dev_dbg(&priv->pdev->dev, "%s: clkout: %ld (Hz)\n", __func__, *rate);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dfsdm_get_clk_out_rate);
+
+static int stm32_dfsdm_parse_of(struct platform_device *pdev,
+				struct dfsdm_priv *priv)
+{
+	struct device_node *node = pdev->dev.of_node;
+	struct resource *res;
+	int ret, val;
+
+	if (!node)
+		return -EINVAL;
+
+	/* Get resources */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "Failed to get memory resource\n");
+		return -ENODEV;
+	}
+	priv->phys_base = res->start;
+	priv->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(priv->base))
+		return PTR_ERR(priv->base);
+
+	ret = of_property_read_u32(node, "st,clkout-freq", &val);
+	if (!ret) {
+		if (!val) {
+			dev_err(&priv->pdev->dev,
+				"st,clkout-freq cannot be 0\n");
+			return -EINVAL;
+		}
+		priv->clkout_freq_req = val;
+	} else if (ret != -EINVAL) {
+		dev_err(&priv->pdev->dev, "Failed to get st,clkout-freq\n");
+		return ret;
+	}
+
+	/* Source clock */
+	priv->clk = devm_clk_get(&pdev->dev, "dfsdm_clk");
+	if (IS_ERR(priv->clk)) {
+		dev_err(&pdev->dev, "No stm32_dfsdm_clk clock found\n");
+		return -EINVAL;
+	}
+
+	priv->aclk = devm_clk_get(&pdev->dev, "audio_clk");
+	if (IS_ERR(priv->aclk))
+		priv->aclk = NULL;
+
+	return 0;
+};
+
+static const struct of_device_id stm32_dfsdm_of_match[] = {
+	{
+		.compatible = "st,stm32h7-dfsdm",
+		.data = &stm32h7_data
+	},
+	{}
+};
+MODULE_DEVICE_TABLE(of, stm32_dfsdm_of_match);
+
+static int stm32_dfsdm_remove(struct platform_device *pdev)
+{
+	of_platform_depopulate(&pdev->dev);
+
+	return 0;
+}
+
+static int stm32_dfsdm_probe(struct platform_device *pdev)
+{
+	struct dfsdm_priv *priv;
+	struct device_node *pnode = pdev->dev.of_node;
+	const struct of_device_id *of_id;
+	const struct stm32_dev_data *dev_data;
+	enum dfsdm_clkout_src clk_src;
+	int ret, i;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->pdev = pdev;
+
+	/* Populate data structure depending on compatibility */
+	of_id = of_match_node(stm32_dfsdm_of_match, pnode);
+	if (!of_id->data) {
+		dev_err(&pdev->dev, "Data associated to device is missing\n");
+		return -EINVAL;
+	}
+
+	dev_data = (const struct stm32_dev_data *)of_id->data;
+
+	ret = stm32_dfsdm_parse_of(pdev, priv);
+	if (ret < 0)
+		return ret;
+
+	priv->regmap = devm_regmap_init_mmio(&pdev->dev, priv->base,
+					    dev_data->regmap_cfg);
+	if (IS_ERR(priv->regmap)) {
+		ret = PTR_ERR(priv->regmap);
+		dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	priv->dfsdm = dev_data->dfsdm;
+
+	priv->filters = devm_kcalloc(&pdev->dev, dev_data->dfsdm.max_filters,
+				     sizeof(*priv->filters), GFP_KERNEL);
+	if (IS_ERR(priv->filters)) {
+		ret = PTR_ERR(priv->filters);
+		goto probe_err;
+	}
+
+	for (i = 0; i < dev_data->dfsdm.max_filters; i++) {
+		struct filter_params *params = &priv->filters[i];
+
+		params->id = i;
+		params->irq = platform_get_irq(pdev, i);
+		if (params->irq < 0) {
+			dev_err(&pdev->dev, "Failed to get IRQ resource\n");
+			ret = params->irq;
+			goto probe_err;
+		}
+
+		ret = devm_request_irq(&pdev->dev, params->irq, stm32_dfsdm_irq,
+				       0, dev_name(&pdev->dev), params);
+		if (ret) {
+			dev_err(&pdev->dev, "Failed to register interrupt\n");
+			goto probe_err;
+		}
+
+		params->priv = priv;
+	}
+
+	priv->channels = devm_kcalloc(&pdev->dev, priv->dfsdm.max_channels,
+				      sizeof(*priv->channels), GFP_KERNEL);
+	if (IS_ERR(priv->channels)) {
+		ret = PTR_ERR(priv->channels);
+		goto probe_err;
+	}
+	priv->free_filter_mask = BIT(priv->dfsdm.max_filters) - 1;
+	priv->free_channel_mask = BIT(priv->dfsdm.max_channels) - 1;
+
+	platform_set_drvdata(pdev, &priv->dfsdm);
+	spin_lock_init(&priv->lock);
+
+	priv->clkout_div = stm32_dfsdm_get_clkout_divider(priv,
+						    clk_get_rate(priv->clk));
+
+	ret = of_platform_populate(pnode, NULL, NULL, &pdev->dev);
+	if (ret < 0)
+		goto probe_err;
+
+	clk_src = priv->aclk ? AUDIO_CLK : DFSDM_CLK;
+
+	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
+			  DFSDM_CHCFGR1_CKOUTSRC_MASK,
+			  DFSDM_CHCFGR1_CKOUTSRC(clk_src));
+	return 0;
+
+probe_err:
+	return ret;
+}
+
+static struct platform_driver stm32_dfsdm_driver = {
+	.probe = stm32_dfsdm_probe,
+	.remove = stm32_dfsdm_remove,
+	.driver = {
+		.name = "stm32-dfsdm",
+		.of_match_table = stm32_dfsdm_of_match,
+	},
+};
+
+module_platform_driver(stm32_dfsdm_driver);
+
+MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 dfsdm driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/stm32-dfsdm.h b/include/linux/mfd/stm32-dfsdm.h
new file mode 100644
index 0000000..f6eb788
--- /dev/null
+++ b/include/linux/mfd/stm32-dfsdm.h
@@ -0,0 +1,324 @@
+/*
+ * This file is part of STM32 DFSDM mfd driver API
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com>.
+ *
+ * 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.
+ */
+#ifndef MDF_STM32_DFSDM_H
+#define MDF_STM32_DFSDM_H
+
+/*
+ * Channel definitions
+ */
+#define DFSDM_CHANNEL_0    BIT(0)
+#define DFSDM_CHANNEL_1    BIT(1)
+#define DFSDM_CHANNEL_2    BIT(2)
+#define DFSDM_CHANNEL_3    BIT(3)
+#define DFSDM_CHANNEL_4    BIT(4)
+#define DFSDM_CHANNEL_5    BIT(5)
+#define DFSDM_CHANNEL_6    BIT(6)
+#define DFSDM_CHANNEL_7    BIT(7)
+
+/* DFSDM channel input data packing */
+enum stm32_dfsdm_data_packing {
+	DFSDM_CHANNEL_STANDARD_MODE,    /* Standard data packing mode */
+	DFSDM_CHANNEL_INTERLEAVED_MODE, /* Interleaved data packing mode */
+	DFSDM_CHANNEL_DUAL_MODE         /* Dual data packing mode */
+};
+
+/* DFSDM channel input multiplexer */
+enum stm32_dfsdm_input_multiplexer {
+	DFSDM_CHANNEL_EXTERNAL_INPUTS,    /* Data taken from external inputs */
+	DFSDM_CHANNEL_INTERNAL_ADC,       /* Data taken from internal ADC */
+	DFSDM_CHANNEL_INTERNAL_REGISTER,  /* Data taken from register */
+};
+
+/* DFSDM channel serial interface type */
+enum stm32_dfsdm_serial_in_type {
+	DFSDM_CHANNEL_SPI_RISING,         /* SPI with rising edge */
+	DFSDM_CHANNEL_SPI_FALLING,        /* SPI with falling edge */
+	DFSDM_CHANNEL_MANCHESTER_RISING,  /* Manchester with rising edge */
+	DFSDM_CHANNEL_MANCHESTER_FALLING, /* Manchester with falling edge */
+};
+
+/* DFSDM channel serial spi clock source */
+enum stm32_dfsdm_spi_clk_src {
+	/* External SPI clock */
+	DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL,
+	/* Internal SPI clock */
+	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL,
+	/* Internal SPI clock divided by 2, falling edge */
+	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING,
+	/* Internal SPI clock divided by 2, rising edge */
+	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING
+};
+
+/* DFSDM channel input pins */
+enum stm32_dfsdm_serial_in_select {
+	/* Serial input taken from pins of the same channel (y) */
+	DFSDM_CHANNEL_SAME_CHANNEL_PINS,
+	/* Serial input taken from pins of the following channel (y + 1)*/
+	DFSDM_CHANNEL_NEXT_CHANNEL_PINS,
+};
+
+/**
+ * struct stm32_dfsdm_input_type - DFSDM channel init structure definition.
+ * @DataPacking: Standard, interleaved or dual mode for internal register.
+ * @source: channel source: internal DAC, serial input or memory.
+ */
+struct stm32_dfsdm_input_type {
+	enum stm32_dfsdm_data_packing DataPacking;
+	enum stm32_dfsdm_input_multiplexer source;
+};
+
+/**
+ * struct stm32_dfsdm_serial_if - DFSDM serial interface parameters.
+ * @type:	Serial interface type.
+ * @spi_clk:	SPI clock source.
+ * @pins:	select serial interface associated to the channel
+ */
+struct stm32_dfsdm_serial_if {
+	enum stm32_dfsdm_serial_in_type type;
+	enum stm32_dfsdm_spi_clk_src spi_clk;
+	enum stm32_dfsdm_serial_in_select pins;
+};
+
+/**
+ * struct stm32_dfsdm_channel - DFSDM channel hardware parameters.
+ * @id:		DFSDM channel identifier.
+ * @type:	DFSDM channel input parameters.
+ * @serial_if:	DFSDM channel serial interface parameters.
+ *		Mandatory for DFSDM_CHANNEL_EXTERNAL_INPUTS.
+ */
+struct stm32_dfsdm_channel {
+	unsigned int id;
+	struct stm32_dfsdm_input_type type;
+	struct stm32_dfsdm_serial_if serial_if;
+};
+
+/**
+ * struct stm32_dfsdm_ch_cfg - DFSDM channel config.
+ * @offset:		DFSDM channel 24 bit calibration offset.
+ * @right_bit_shift:	DFSDM channel right bit shift of the data result.
+ */
+struct stm32_dfsdm_ch_cfg {
+	unsigned int offset;
+	unsigned int right_bit_shift;
+};
+
+/*
+ * Filter definitions
+ */
+
+#define DFSDM_MIN_INT_OVERSAMPLING 1
+#define DFSDM_MAX_INT_OVERSAMPLING 256
+#define DFSDM_MIN_FL_OVERSAMPLING 1
+#define DFSDM_MAX_FL_OVERSAMPLING 1024
+
+enum stm32_dfsdm_events {
+	DFSDM_EVENT_INJ_EOC =	BIT(0), /* Injected end of conversion event */
+	DFSDM_EVENT_REG_EOC =	BIT(1), /* Regular end of conversion event */
+	DFSDM_EVENT_INJ_XRUN =	BIT(2), /* Injected conversion overrun event */
+	DFSDM_EVENT_REG_XRUN =	BIT(3), /* Regular conversion overrun event */
+	DFSDM_EVENT_AWD =	BIT(4), /* Analog watchdog event */
+	DFSDM_EVENT_SCD =	BIT(5), /* Short circuit detector event */
+	DFSDM_EVENT_CKA =	BIT(6), /* Clock abscence detection event */
+};
+
+#define STM32_DFSDM_EVENT_MASK 0x3F
+
+/* DFSDM filter order  */
+enum stm32_dfsdm_sinc_order {
+	DFSDM_FASTSINC_ORDER, /* FastSinc filter type */
+	DFSDM_SINC1_ORDER,    /* Sinc 1 filter type */
+	DFSDM_SINC2_ORDER,    /* Sinc 2 filter type */
+	DFSDM_SINC3_ORDER,    /* Sinc 3 filter type */
+	DFSDM_SINC4_ORDER,    /* Sinc 4 filter type (N.A. for watchdog) */
+	DFSDM_SINC5_ORDER,    /* Sinc 5 filter type (N.A. for watchdog) */
+	DFSDM_NB_SINC_ORDER,
+};
+
+/* DFSDM filter order */
+enum stm32_dfsdm_state {
+	DFSDM_DISABLE,
+	DFSDM_ENABLE,
+};
+
+/**
+ * struct stm32_dfsdm_sinc_filter - DFSDM Sinc filter structure definition
+ * @order: DFSM filter order.
+ * @oversampling: DFSDM filter oversampling:
+ *		  post processing filter: min = 1, max = 1024.
+ */
+struct stm32_dfsdm_sinc_filter {
+	enum stm32_dfsdm_sinc_order order;
+	unsigned int oversampling;
+};
+
+/* DFSDM filter conversion trigger */
+enum stm32_dfsdm_trigger {
+	DFSDM_FILTER_SW_TRIGGER,   /* Software trigger */
+	DFSDM_FILTER_SYNC_TRIGGER, /* Synchronous with DFSDM0 */
+	DFSDM_FILTER_EXT_TRIGGER,  /* External trigger (only for injected) */
+};
+
+/* DFSDM filter external trigger polarity */
+enum stm32_dfsdm_filter_ext_trigger_pol {
+	DFSDM_FILTER_EXT_TRIG_NO_TRIG,      /* Trigger disable */
+	DFSDM_FILTER_EXT_TRIG_RISING_EDGE,  /* Rising edge */
+	DFSDM_FILTER_EXT_TRIG_FALLING_EDGE, /* Falling edge */
+	DFSDM_FILTER_EXT_TRIG_BOTH_EDGES,   /* Rising and falling edges */
+};
+
+/* DFSDM filter conversion type */
+enum stm32_dfsdm_conv_type {
+	DFSDM_FILTER_REG_CONV,      /* Regular conversion */
+	DFSDM_FILTER_SW_INJ_CONV,   /* Injected conversion */
+	DFSDM_FILTER_TRIG_INJ_CONV, /* Injected conversion */
+};
+
+/* DFSDM filter regular synchronous mode */
+enum stm32_dfsdm_conv_rsync {
+	DFSDM_FILTER_RSYNC_OFF, /* regular conversion asynchronous */
+	DFSDM_FILTER_RSYNC_ON,  /* regular conversion synchronous with filter0*/
+};
+
+/**
+ * struct stm32_dfsdm_regular - DFSDM filter conversion parameters structure
+ * @ch_src:	Channel source from 0 to 7.
+ * @fast_mode:	Enable/disable fast mode for regular conversion.
+ * @dma_mode:	Enable/disable dma mode.
+ * @cont_mode	Enable/disable continuous conversion.
+ * @sync_mode	Enable/disable synchro mode.
+ */
+struct stm32_dfsdm_regular {
+	unsigned int ch_src;
+	bool fast_mode;
+	bool dma_mode;
+	bool cont_mode;
+	bool sync_mode;
+};
+
+/**
+ * struct stm32_dfsdm_injected - DFSDM filter  conversion parameters structure
+ * @trigger:	Trigger used to start injected conversion.
+ * @trig_src:	External trigger, 0 to 30 (refer to datasheet for details).
+ * @trig_pol:	External trigger edge: software, rising, falling or both.
+ * @scan_mode:	Enable/disable scan mode for injected conversion.
+ * @ch_group:	mask containing channels to scan ( set bit y to scan
+ *		channel y).
+ * @dma_mode:	DFSDM channel input parameters.
+ */
+struct stm32_dfsdm_injected {
+	enum stm32_dfsdm_trigger trigger;
+	unsigned int trig_src;
+	enum stm32_dfsdm_filter_ext_trigger_pol trig_pol;
+	bool scan_mode;
+	unsigned int ch_group;
+	bool dma_mode;
+};
+
+struct stm32_dfsdm;
+
+/**
+ * struct stm32_dfsdm_fl_event - DFSDM filters event
+ * @cb:	User event callback with parameters. be carful this function
+ *		is called under threaded IRQ context:
+ *			struct stm32_dfsdm *dfsdm: dfsdm handle,
+ *			unsigned int fl_id: filter id,
+ *			num stm32_dfsdm_events flag: event,
+ *			param: parameter associated to the event,
+ *			void *context: user context provided on registration.
+ * @context: User param to retrieve context.
+ */
+struct stm32_dfsdm_fl_event {
+	void (*cb)(struct stm32_dfsdm *, int, enum stm32_dfsdm_events,
+		   unsigned int, void *);
+	void *context;
+};
+
+/**
+ * struct stm32_dfsdm_filter - DFSDM filter  conversion parameters structure
+ * @reg_params:		DFSDM regular conversion parameters.
+ *			this param is optional and not taken into account if
+ *			@inj_params is defined.
+ * @inj_params:		DFSDM injected conversion parameters (optional).
+ * @filter_params:	DFSDM filter parameters.
+ * @event:		Events callback.
+ * @int_oversampling:	Integrator oversampling ratio for average purpose
+ *			(range from 1 to 256).
+ * @ext_det_ch_mask:	Extreme detector mask for channel selection
+ *			mask generated using DFSDM_CHANNEL_0 to
+ *			DFSDM_CHANNEL_7. If 0 feature is disable.
+ */
+struct stm32_dfsdm_filter {
+	struct stm32_dfsdm_regular *reg_params;
+	struct stm32_dfsdm_injected *inj_params;
+	struct stm32_dfsdm_sinc_filter sinc_params;
+	struct stm32_dfsdm_fl_event event;
+	unsigned int int_oversampling;
+};
+
+/**
+ * struct stm32_dfsdm - DFSDM context structure.
+ *
+ * @trig_info: Trigger name and id available last member name is null.
+ * @max_channels: max number of channels available.
+ * @max_filters: max number of filters available.
+ *
+ * Notice That structure is filled by mdf driver and must not be updated by
+ * user.
+ */
+struct stm32_dfsdm {
+	unsigned int max_channels;
+	unsigned int max_filters;
+};
+
+int stm32_dfsdm_get_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
+void stm32_dfsdm_release_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
+
+dma_addr_t stm32_dfsdm_get_filter_dma_phy_addr(struct stm32_dfsdm *dfsdm,
+					       unsigned int fl_id,
+					       enum stm32_dfsdm_conv_type conv);
+
+int stm32_dfsdm_configure_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+				 struct stm32_dfsdm_filter *filter);
+void stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+			      enum stm32_dfsdm_conv_type conv);
+void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
+
+void stm32_dfsdm_read_fl_conv(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+			      u32 *val, int *ch_id,
+			      enum stm32_dfsdm_conv_type type);
+
+int stm32_dfsdm_unregister_fl_event(struct stm32_dfsdm *dfsdm,
+				    unsigned int fl_id,
+				    enum stm32_dfsdm_events event,
+				    unsigned int ch_mask);
+int stm32_dfsdm_register_fl_event(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
+				  enum stm32_dfsdm_events event,
+				  unsigned int ch_mask);
+
+int stm32_dfsdm_get_channel(struct stm32_dfsdm *dfsdm,
+			    struct stm32_dfsdm_channel *ch);
+void stm32_dfsdm_release_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
+
+int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id,
+			      struct stm32_dfsdm_ch_cfg *cfg);
+void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
+
+int stm32_dfsdm_get_clk_out_rate(struct stm32_dfsdm *dfsdm,
+				 unsigned long *rate);
+
+#endif
-- 
1.9.1

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

* [PATCH 3/7] IIO: add bindings for STM32 DFSDM ADC driver
  2017-01-23 16:32 ` Arnaud Pouliquen
  (?)
@ 2017-01-23 16:32     ` Arnaud Pouliquen
  -1 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-23 16:32 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Lee Jones, Rob Herring,
	Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre Torgue, arnaud.pouliquen-qxv4g6HH51o

This patch adds documentation of device tree bindings for the
STM32 DFSDM ADC.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
---
 .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 60 ++++++++++++++++++++++
 1 file changed, 60 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 

diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt  b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
new file mode 100644
index 0000000..c156bcb
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 	
@@ -0,0 +1,60 @@
+STMicroelectronics STM32 DFSDM ADC device driver
+
+STM32 DFSDM ADC is a sigma delta analog-to-digital converter.
+It has to be declared in device-tree as a subnode of the DFSDM mfd node.
+
+It has several multiplexed input channels. Conversions can be performed
+in single, scan or discontinuous mode. Conversions can be launched in software
+or using hardware triggers.
+Each instance of the driver uses one filter instance handle by the DFSDM mfd
+driver.
+
+DFSDM also offers extra features:
+-The analog watchdog feature allows the application to detect if the
+ input voltage goes beyond the user-defined, higher or lower thresholds.
+-The short circuit detection allows allows the application to detect if the
+ input is in CC.
+-The clock absence detection allows application to detect if SPI input is clocked.
+
+Required properties:
+- compatible:	Must be "st,stm32-dfsdm-adc".
+- reg:		Specifies the DFSDM filter instance.
+- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
+- st,adc-channels:	List of single-ended channels muxed for this ADC.
+- st,adc-channel-names:	List of single-ended channels Name.
+
+Optional properties:
+- st,adc-channel-types:	Single-ended channel input type. Default value is 0.
+			- 0: SPI with data on rising edge (default)
+			- 1: SPI with data on falling edge
+			- 2: manchester codec, rising edage = logic 0
+			- 3: manchester codec, rising edage = logic 1
+- st,adc-channel-clk-src: Conversion clock source. default value is 1.
+			  - 0: External SPI clocl (CLKIN x)
+			  - 1: internal SPI clock (CLKOUT) (default)
+			  - 2: internal SPI clock divided by 2 (falling edge).
+			  - 2: internal SPI clock divided by 2 (rising edge).
+- st,adc-alt-channel: 	  must be defined if Two ADCs are connected on same SPI
+			  input.
+			  If not set channel n is connected to SPI input n.
+			  If set channel n is connected to SPI input n + 1.
+
+Example:
+	dfsdm: dfsdm@4400D000 {
+		iio_dfsdm0: iio-dfsdm@0 {
+			compatible = "st,stm32-dfsdm-adc";
+			#io-channel-cells = <1>;
+			reg = <0>;
+			st,adc-channels = <1>;
+			st,adc-channel-names = "in0";
+		};
+		iio_dfsdm1: iio-dfsdm@1 {
+			compatible = "st,stm32-dfsdm-adc";
+			#io-channel-cells = <1>;
+			reg = <1>;
+			st,adc-channels = <1>;
+			st,adc-channel-names = "in1";
+			st,adc-channel-types = <1>;
+			st,adc-alt-channel = <1>;
+		};
+	};
-- 
1.9.1

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

* [PATCH 3/7] IIO: add bindings for STM32 DFSDM ADC driver
@ 2017-01-23 16:32     ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-23 16:32 UTC (permalink / raw)
  To: devicetree, linux-arm-kernel, linux-iio, alsa-devel, Lee Jones,
	Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre Torgue, arnaud.pouliquen

This patch adds documentation of device tree bindings for the
STM32 DFSDM ADC.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 60 ++++++++++++++++++++++
 1 file changed, 60 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 

diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt  b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
new file mode 100644
index 0000000..c156bcb
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 	
@@ -0,0 +1,60 @@
+STMicroelectronics STM32 DFSDM ADC device driver
+
+STM32 DFSDM ADC is a sigma delta analog-to-digital converter.
+It has to be declared in device-tree as a subnode of the DFSDM mfd node.
+
+It has several multiplexed input channels. Conversions can be performed
+in single, scan or discontinuous mode. Conversions can be launched in software
+or using hardware triggers.
+Each instance of the driver uses one filter instance handle by the DFSDM mfd
+driver.
+
+DFSDM also offers extra features:
+-The analog watchdog feature allows the application to detect if the
+ input voltage goes beyond the user-defined, higher or lower thresholds.
+-The short circuit detection allows allows the application to detect if the
+ input is in CC.
+-The clock absence detection allows application to detect if SPI input is clocked.
+
+Required properties:
+- compatible:	Must be "st,stm32-dfsdm-adc".
+- reg:		Specifies the DFSDM filter instance.
+- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
+- st,adc-channels:	List of single-ended channels muxed for this ADC.
+- st,adc-channel-names:	List of single-ended channels Name.
+
+Optional properties:
+- st,adc-channel-types:	Single-ended channel input type. Default value is 0.
+			- 0: SPI with data on rising edge (default)
+			- 1: SPI with data on falling edge
+			- 2: manchester codec, rising edage = logic 0
+			- 3: manchester codec, rising edage = logic 1
+- st,adc-channel-clk-src: Conversion clock source. default value is 1.
+			  - 0: External SPI clocl (CLKIN x)
+			  - 1: internal SPI clock (CLKOUT) (default)
+			  - 2: internal SPI clock divided by 2 (falling edge).
+			  - 2: internal SPI clock divided by 2 (rising edge).
+- st,adc-alt-channel: 	  must be defined if Two ADCs are connected on same SPI
+			  input.
+			  If not set channel n is connected to SPI input n.
+			  If set channel n is connected to SPI input n + 1.
+
+Example:
+	dfsdm: dfsdm@4400D000 {
+		iio_dfsdm0: iio-dfsdm@0 {
+			compatible = "st,stm32-dfsdm-adc";
+			#io-channel-cells = <1>;
+			reg = <0>;
+			st,adc-channels = <1>;
+			st,adc-channel-names = "in0";
+		};
+		iio_dfsdm1: iio-dfsdm@1 {
+			compatible = "st,stm32-dfsdm-adc";
+			#io-channel-cells = <1>;
+			reg = <1>;
+			st,adc-channels = <1>;
+			st,adc-channel-names = "in1";
+			st,adc-channel-types = <1>;
+			st,adc-alt-channel = <1>;
+		};
+	};
-- 
1.9.1


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

* [PATCH 3/7] IIO: add bindings for STM32 DFSDM ADC driver
@ 2017-01-23 16:32     ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-23 16:32 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds documentation of device tree bindings for the
STM32 DFSDM ADC.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 60 ++++++++++++++++++++++
 1 file changed, 60 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 

diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt  b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
new file mode 100644
index 0000000..c156bcb
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 	
@@ -0,0 +1,60 @@
+STMicroelectronics STM32 DFSDM ADC device driver
+
+STM32 DFSDM ADC is a sigma delta analog-to-digital converter.
+It has to be declared in device-tree as a subnode of the DFSDM mfd node.
+
+It has several multiplexed input channels. Conversions can be performed
+in single, scan or discontinuous mode. Conversions can be launched in software
+or using hardware triggers.
+Each instance of the driver uses one filter instance handle by the DFSDM mfd
+driver.
+
+DFSDM also offers extra features:
+-The analog watchdog feature allows the application to detect if the
+ input voltage goes beyond the user-defined, higher or lower thresholds.
+-The short circuit detection allows allows the application to detect if the
+ input is in CC.
+-The clock absence detection allows application to detect if SPI input is clocked.
+
+Required properties:
+- compatible:	Must be "st,stm32-dfsdm-adc".
+- reg:		Specifies the DFSDM filter instance.
+- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
+- st,adc-channels:	List of single-ended channels muxed for this ADC.
+- st,adc-channel-names:	List of single-ended channels Name.
+
+Optional properties:
+- st,adc-channel-types:	Single-ended channel input type. Default value is 0.
+			- 0: SPI with data on rising edge (default)
+			- 1: SPI with data on falling edge
+			- 2: manchester codec, rising edage = logic 0
+			- 3: manchester codec, rising edage = logic 1
+- st,adc-channel-clk-src: Conversion clock source. default value is 1.
+			  - 0: External SPI clocl (CLKIN x)
+			  - 1: internal SPI clock (CLKOUT) (default)
+			  - 2: internal SPI clock divided by 2 (falling edge).
+			  - 2: internal SPI clock divided by 2 (rising edge).
+- st,adc-alt-channel: 	  must be defined if Two ADCs are connected on same SPI
+			  input.
+			  If not set channel n is connected to SPI input n.
+			  If set channel n is connected to SPI input n + 1.
+
+Example:
+	dfsdm: dfsdm at 4400D000 {
+		iio_dfsdm0: iio-dfsdm at 0 {
+			compatible = "st,stm32-dfsdm-adc";
+			#io-channel-cells = <1>;
+			reg = <0>;
+			st,adc-channels = <1>;
+			st,adc-channel-names = "in0";
+		};
+		iio_dfsdm1: iio-dfsdm at 1 {
+			compatible = "st,stm32-dfsdm-adc";
+			#io-channel-cells = <1>;
+			reg = <1>;
+			st,adc-channels = <1>;
+			st,adc-channel-names = "in1";
+			st,adc-channel-types = <1>;
+			st,adc-alt-channel = <1>;
+		};
+	};
-- 
1.9.1

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

* [PATCH 4/7] IIO: add STM32 DFSDM ADC support
  2017-01-23 16:32 ` Arnaud Pouliquen
  (?)
@ 2017-01-23 16:32   ` Arnaud Pouliquen
  -1 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-23 16:32 UTC (permalink / raw)
  To: devicetree, linux-arm-kernel, linux-iio, alsa-devel, Lee Jones,
	Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: arnaud.pouliquen, Alexandre Torgue, Maxime Coquelin

Add driver to handle Sigma Delta ADC conversion for ADC
connected to DFSDM IP.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 drivers/iio/adc/Kconfig           |   9 +
 drivers/iio/adc/Makefile          |   1 +
 drivers/iio/adc/stm32-dfsdm-adc.c | 676 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 686 insertions(+)
 create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index e0b3c09..4b2b886 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -441,6 +441,15 @@ config STM32_ADC
 	  This driver can also be built as a module.  If so, the module
 	  will be called stm32-adc.
 
+config STM32_DFSDM_ADC
+	tristate "STMicroelectronics STM32 DFSDM ADC driver"
+	depends on (ARCH_STM32 && OF && MFD_STM32_DFSDM) || COMPILE_TEST
+	help
+	  Say yes here to build the driver for the STMicroelectronics
+	  STM32 analog-to-digital converter with Digital filter.
+	  This driver can also be built as a module. If so, the module
+	  will be called stm32_dfsdm_adc.
+
 config STX104
 	tristate "Apex Embedded Systems STX104 driver"
 	depends on X86 && ISA_BUS_API
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 8e02a94..aed42c6 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
 obj-$(CONFIG_STX104) += stx104.o
 obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
 obj-$(CONFIG_STM32_ADC) += stm32-adc.o
+obj-$(CONFIG_STM32_DFSDM_ADC) += stm32-dfsdm-adc.o
 obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
 obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
 obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
new file mode 100644
index 0000000..727d6b1
--- /dev/null
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
@@ -0,0 +1,676 @@
+/*
+ * This file is part of STM32 DFSDM ADC driver
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author: Arnaud Pouliquen <arnaud.pouliquen@st.com>.
+ *
+ * License type: GPLv2
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/irq_work.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <linux/iio/iio.h>
+
+#include <linux/mfd/stm32-dfsdm.h>
+
+#define DFSDM_ADC_MAX_RESOLUTION 24
+#define DFSDM_ADC_STORAGE_BITS   32
+
+#define DFSDM_MAX_CH_OFFSET BIT(24)
+#define DFSDM_MAX_CH_SHIFT 24
+
+#define DFSDM_TIMEOUT_US 100000
+#define DFSDM_TIMEOUT (msecs_to_jiffies(DFSDM_TIMEOUT_US / 1000))
+
+#define CH_ID_FROM_IDX(i) (adc->inputs[i].id)
+#define CH_CFG_FROM_IDX(i) (&adc->inputs_cfg[i])
+
+struct stm32_dfsdm_adc {
+	struct device *dev;
+	struct stm32_dfsdm *dfsdm;
+	struct list_head adc_list;
+
+	/* Filter */
+	unsigned int fl_id;
+	struct stm32_dfsdm_sinc_filter sinc;
+	unsigned int int_oversampling;
+
+	/* Channels */
+	struct stm32_dfsdm_channel *inputs;
+	struct stm32_dfsdm_ch_cfg *inputs_cfg;
+
+	/* Raw mode*/
+	struct completion completion;
+	struct stm32_dfsdm_regular reg_params;
+	u32 *buffer;
+};
+
+static const char * const stm32_dfsdm_adc_sinc_order[] = {
+	[0] = "FastSinc",
+	[1] = "Sinc1",
+	[2] = "Sinc2",
+	[3] = "Sinc3",
+	[4] = "Sinc4",
+	[5] = "Sinc5",
+};
+
+static inline const struct iio_chan_spec *get_ch_from_id(
+					struct iio_dev *indio_dev, int ch_id)
+{
+	int i;
+
+	for (i = 0; i < indio_dev->num_channels; i++) {
+		if (ch_id == indio_dev->channels[i].channel)
+			return &indio_dev->channels[i];
+	}
+
+	return NULL;
+}
+
+/*
+ * Filter attributes
+ */
+
+static int stm32_dfsdm_adc_set_sinc(struct iio_dev *indio_dev,
+				    const struct iio_chan_spec *chan,
+				    unsigned int val)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+	dev_dbg(&indio_dev->dev, "%s: %s\n", __func__,
+		stm32_dfsdm_adc_sinc_order[adc->sinc.order]);
+
+	adc->sinc.order = val;
+
+	return 0;
+}
+
+static int stm32_dfsdm_adc_get_sinc(struct iio_dev *indio_dev,
+				    const struct iio_chan_spec *chan)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+	dev_dbg(&indio_dev->dev, "%s: %s\n", __func__,
+		stm32_dfsdm_adc_sinc_order[adc->sinc.order]);
+
+	return adc->sinc.order;
+}
+
+static const struct iio_enum stm32_dfsdm_adc_fl_sinc_order = {
+	.items = stm32_dfsdm_adc_sinc_order,
+	.num_items = ARRAY_SIZE(stm32_dfsdm_adc_sinc_order),
+	.get = stm32_dfsdm_adc_get_sinc,
+	.set = stm32_dfsdm_adc_set_sinc,
+};
+
+static ssize_t stm32_dfsdm_adc_get_int_os(struct iio_dev *indio_dev,
+					  uintptr_t priv,
+					  const struct iio_chan_spec *chan,
+					  char *buf)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", adc->int_oversampling);
+}
+
+static ssize_t stm32_dfsdm_adc_set_int_os(struct iio_dev *indio_dev,
+					  uintptr_t priv,
+					  const struct iio_chan_spec *chan,
+					  const char *buf, size_t len)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	int ret, val;
+
+	ret = kstrtoint(buf, 0, &val);
+	if (ret)
+		return ret;
+
+	if ((!val) || (val > DFSDM_MAX_INT_OVERSAMPLING)) {
+		dev_err(&indio_dev->dev, "invalid oversampling (0 or > %#x)",
+			DFSDM_MAX_INT_OVERSAMPLING);
+		return -EINVAL;
+	}
+	adc->int_oversampling = val;
+
+	return len;
+}
+
+static ssize_t stm32_dfsdm_adc_get_fl_os(struct iio_dev *indio_dev,
+					 uintptr_t priv,
+					 const struct iio_chan_spec *chan,
+					 char *buf)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", adc->sinc.oversampling);
+}
+
+static ssize_t stm32_dfsdm_adc_set_fl_os(struct iio_dev *indio_dev,
+					 uintptr_t priv,
+					const struct iio_chan_spec *chan,
+					const char *buf, size_t len)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	int ret, val;
+
+	ret = kstrtoint(buf, 0, &val);
+	if (ret)
+		return ret;
+
+	if ((!val) || (val > DFSDM_MAX_FL_OVERSAMPLING)) {
+		dev_err(&indio_dev->dev, "invalid oversampling (0 or > %#x)",
+			DFSDM_MAX_FL_OVERSAMPLING);
+		return -EINVAL;
+	}
+	adc->sinc.oversampling = val;
+
+	return len;
+}
+
+/*
+ * Data bit shifting attribute
+ */
+static ssize_t stm32_dfsdm_adc_get_shift(struct iio_dev *indio_dev,
+					 uintptr_t priv,
+					 const struct iio_chan_spec *chan,
+					 char *buf)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", ch_cfg->right_bit_shift);
+}
+
+static ssize_t stm32_dfsdm_adc_set_shift(struct iio_dev *indio_dev,
+					 uintptr_t priv,
+					 const struct iio_chan_spec *chan,
+					 const char *buf, size_t len)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
+	int ret, val;
+
+	ret = kstrtoint(buf, 0, &val);
+	if (ret)
+		return ret;
+
+	if (val > DFSDM_MAX_CH_SHIFT) {
+		dev_err(&indio_dev->dev, "invalid shift value (> %#x)",
+			DFSDM_MAX_CH_SHIFT);
+		return -EINVAL;
+	}
+	ch_cfg->right_bit_shift = val;
+
+	return len;
+}
+
+static const struct iio_chan_spec_ext_info stm32_dfsdm_adc_ext_info[] = {
+	/* sinc_filter_order: Configure Sinc filter order */
+	IIO_ENUM("sinc_filter_order", IIO_SHARED_BY_TYPE,
+		 &stm32_dfsdm_adc_fl_sinc_order),
+	IIO_ENUM_AVAILABLE("sinc_filter_order", &stm32_dfsdm_adc_fl_sinc_order),
+	/* filter oversampling: Post filter oversampling ratio */
+	{
+		.name = "sinc_filter_oversampling_ratio",
+		.shared = IIO_SHARED_BY_TYPE,
+		.read = stm32_dfsdm_adc_get_fl_os,
+		.write = stm32_dfsdm_adc_set_fl_os,
+	},
+	/* data_right_bit_shift : Filter output data shifting */
+	{
+		.name = "data_right_bit_shift",
+		.shared = IIO_SEPARATE,
+		.read = stm32_dfsdm_adc_get_shift,
+		.write = stm32_dfsdm_adc_set_shift,
+	},
+
+	/*
+	 * averaging_length : Mean windows of data from filter.
+	 * Defines how many filter data will be summed to one data output
+	 */
+	{
+		.name = "integrator_oversampling",
+		.shared = IIO_SHARED_BY_TYPE,
+		.read = stm32_dfsdm_adc_get_int_os,
+		.write = stm32_dfsdm_adc_set_int_os,
+	},
+	{},
+};
+
+/*
+ * Filter event routine called under IRQ context
+ */
+static void stm32_dfsdm_event_cb(struct stm32_dfsdm *dfsdm, int flt_id,
+				 enum stm32_dfsdm_events ev, unsigned int param,
+				 void *context)
+{
+	struct stm32_dfsdm_adc *adc = context;
+	unsigned int ch_id;
+
+	dev_dbg(adc->dev, "%s:\n", __func__);
+
+	switch (ev) {
+	case DFSDM_EVENT_REG_EOC:
+		stm32_dfsdm_read_fl_conv(adc->dfsdm, flt_id, adc->buffer,
+					 &ch_id, DFSDM_FILTER_REG_CONV);
+		complete(&adc->completion);
+		break;
+	case DFSDM_EVENT_REG_XRUN:
+		dev_err(adc->dev, "%s: underrun detected for filter %d\n",
+			__func__, flt_id);
+		break;
+	default:
+		dev_err(adc->dev, "%s: event %#x not implemented\n",
+			__func__, ev);
+		break;
+	}
+}
+
+static inline void stm32_dfsdm_adc_fl_config(struct stm32_dfsdm_adc *adc,
+					     u32 channel_mask,
+					     struct stm32_dfsdm_filter *filter)
+{
+	dev_dbg(adc->dev, "%s:\n", __func__);
+
+	filter->event.cb = stm32_dfsdm_event_cb;
+	filter->event.context = adc;
+
+	filter->sinc_params = adc->sinc;
+
+	filter->int_oversampling = adc->int_oversampling;
+}
+
+static int stm32_dfsdm_adc_start_raw_conv(struct stm32_dfsdm_adc *adc,
+					  const struct iio_chan_spec *chan)
+{
+	struct stm32_dfsdm_filter filter;
+	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
+	unsigned int ch_id = CH_ID_FROM_IDX(chan->scan_index);
+	int ret;
+
+	dev_dbg(adc->dev, "%s:\n", __func__);
+
+	memset(&filter, 0, sizeof(filter));
+	filter.reg_params = &adc->reg_params;
+
+	if (!filter.reg_params)
+		return -ENOMEM;
+
+	filter.reg_params->ch_src = ch_id;
+
+	stm32_dfsdm_adc_fl_config(adc, BIT(ch_id), &filter);
+
+	ret = stm32_dfsdm_configure_filter(adc->dfsdm, adc->fl_id, &filter);
+	if (ret < 0) {
+		dev_err(adc->dev, "Failed to configure filter\n");
+		return ret;
+	}
+
+	ret = stm32_dfsdm_start_channel(adc->dfsdm, ch_id, ch_cfg);
+	if (ret < 0)
+		return ret;
+
+	stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id, DFSDM_FILTER_REG_CONV);
+
+	return 0;
+}
+
+static void stm32_dfsdm_adc_stop_raw_conv(struct stm32_dfsdm_adc *adc,
+					  const struct iio_chan_spec *chan)
+{
+	unsigned int ch_id = CH_ID_FROM_IDX(chan->scan_index);
+
+	dev_dbg(adc->dev, "%s:\n", __func__);
+
+	stm32_dfsdm_stop_filter(adc->dfsdm, adc->fl_id);
+	stm32_dfsdm_stop_channel(adc->dfsdm, ch_id);
+}
+
+static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
+				   const struct iio_chan_spec *chan,
+				   u32 *result)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	long timeout;
+	int ret;
+
+	dev_dbg(&indio_dev->dev, "%s:\n", __func__);
+
+	reinit_completion(&adc->completion);
+
+	ret = stm32_dfsdm_register_fl_event(adc->dfsdm, adc->fl_id,
+					    DFSDM_EVENT_REG_EOC, 0);
+	if (ret < 0) {
+		dev_err(&indio_dev->dev, "Failed to register event\n");
+		return ret;
+	}
+
+	adc->buffer = result;
+	ret = stm32_dfsdm_adc_start_raw_conv(adc, chan);
+	if (ret) {
+		dev_err(&indio_dev->dev, "Failed to start conversion\n");
+		goto free_event;
+	}
+
+	timeout = wait_for_completion_interruptible_timeout(&adc->completion,
+							    DFSDM_TIMEOUT);
+	if (timeout == 0) {
+		dev_warn(&indio_dev->dev, "Conversion timed out!\n");
+		ret = -ETIMEDOUT;
+	} else if (timeout < 0) {
+		ret = timeout;
+	} else {
+		dev_dbg(&indio_dev->dev, "converted val %#x\n", *result);
+		ret = IIO_VAL_INT;
+	}
+
+	stm32_dfsdm_adc_stop_raw_conv(adc, chan);
+
+free_event:
+	adc->buffer = NULL;
+	stm32_dfsdm_unregister_fl_event(adc->dfsdm, adc->fl_id,
+					DFSDM_EVENT_REG_EOC, 0);
+
+	return ret;
+}
+
+static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
+				struct iio_chan_spec const *chan, int *val,
+				int *val2, long mask)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
+	int ret = -EINVAL;
+
+	dev_dbg(&indio_dev->dev, "%s channel %d\n", __func__, chan->channel);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
+		if (!ret)
+			ret = IIO_VAL_INT;
+		break;
+	case IIO_CHAN_INFO_OFFSET:
+		*val = ch_cfg->offset;
+		ret = IIO_VAL_INT;
+		break;
+	}
+
+	return ret;
+}
+
+static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev,
+				 struct iio_chan_spec const *chan, int val,
+				 int val2, long mask)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
+
+	dev_dbg(&indio_dev->dev, "%s channel%d", __func__, chan->channel);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_OFFSET:
+		if (val > DFSDM_MAX_CH_OFFSET) {
+			dev_err(&indio_dev->dev, "invalid offset (> %#lx)",
+				DFSDM_MAX_CH_OFFSET);
+			return -EINVAL;
+		}
+		ch_cfg->offset = val;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct iio_info stm32_dfsdm_iio_info = {
+	.read_raw = stm32_dfsdm_read_raw,
+	.write_raw = stm32_dfsdm_write_raw,
+	.driver_module = THIS_MODULE,
+};
+
+static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
+					 struct iio_chan_spec *chan,
+					 int chan_idx)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	struct stm32_dfsdm_channel *dfsdm_ch = &adc->inputs[chan_idx];
+	struct iio_chan_spec *ch = &chan[chan_idx];
+	int ret;
+	unsigned int alt_ch = 0;
+
+	ret = of_property_read_u32_index(indio_dev->dev.of_node,
+					 "st,adc-channels", chan_idx,
+					 &ch->channel);
+	if (ret < 0) {
+		dev_err(&indio_dev->dev,
+			" error parsing 'st,adc-channels' for idx %d\n",
+			chan_idx);
+		return ret;
+	}
+
+	ret = of_property_read_string_index(indio_dev->dev.of_node,
+					    "st,adc-channel-names", chan_idx,
+					    &ch->datasheet_name);
+	if (ret < 0) {
+		dev_err(&indio_dev->dev,
+			" error parsing 'st,adc-channel-names' for idx %d\n",
+			chan_idx);
+		return ret;
+	}
+
+	ch->extend_name = ch->datasheet_name;
+	ch->type = IIO_VOLTAGE;
+	ch->indexed = 1;
+	ch->scan_index = chan_idx;
+	ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+				 BIT(IIO_CHAN_INFO_OFFSET);
+	ch->scan_type.sign = 'u';
+	ch->scan_type.realbits = DFSDM_ADC_MAX_RESOLUTION;
+	ch->scan_type.storagebits = DFSDM_ADC_STORAGE_BITS;
+	ch->scan_type.shift = 8;
+
+	ch->ext_info = stm32_dfsdm_adc_ext_info;
+
+	of_property_read_u32_index(indio_dev->dev.of_node, "st,adc-alt-channel",
+				   chan_idx, &alt_ch);
+	/* Select the previous channel if alternate field is defined*/
+	if (alt_ch) {
+		if (!ch->channel)
+			ch->channel = adc->dfsdm->max_channels;
+		ch->channel -= 1;
+		dfsdm_ch->serial_if.pins = DFSDM_CHANNEL_NEXT_CHANNEL_PINS;
+	} else {
+		dfsdm_ch->serial_if.pins = DFSDM_CHANNEL_SAME_CHANNEL_PINS;
+	}
+	dfsdm_ch->id = ch->channel;
+
+	dfsdm_ch->type.DataPacking = DFSDM_CHANNEL_STANDARD_MODE;
+
+	dfsdm_ch->type.source = DFSDM_CHANNEL_EXTERNAL_INPUTS;
+	ret = of_property_read_u32_index(indio_dev->dev.of_node,
+					 "st,adc-channel-types",
+					 chan_idx, &dfsdm_ch->serial_if.type);
+	if (ret < 0)
+		dfsdm_ch->serial_if.type = DFSDM_CHANNEL_SPI_RISING;
+
+	ret = of_property_read_u32_index(indio_dev->dev.of_node,
+					 "st,adc-channel-clk-src",
+					 chan_idx,
+					 &dfsdm_ch->serial_if.spi_clk);
+
+	if ((dfsdm_ch->serial_if.type == DFSDM_CHANNEL_MANCHESTER_RISING)  ||
+	    (dfsdm_ch->serial_if.type == DFSDM_CHANNEL_MANCHESTER_FALLING) ||
+	    (ret < 0))
+		dfsdm_ch->serial_if.spi_clk = DFSDM_CHANNEL_SPI_CLOCK_INTERNAL;
+
+	return stm32_dfsdm_get_channel(adc->dfsdm, dfsdm_ch);
+}
+
+static int stm32_dfsdm_adc_chan_init(struct iio_dev *indio_dev)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	unsigned int num_ch;
+	struct iio_chan_spec *channels;
+	int ret, chan_idx;
+
+	num_ch = of_property_count_strings(indio_dev->dev.of_node,
+					   "st,adc-channel-names");
+
+	channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
+				GFP_KERNEL);
+	if (!channels)
+		return -ENOMEM;
+
+	adc->inputs = devm_kcalloc(&indio_dev->dev, num_ch,
+				   sizeof(*adc->inputs), GFP_KERNEL);
+	if (!adc->inputs)
+		return -ENOMEM;
+
+	adc->inputs_cfg = devm_kcalloc(&indio_dev->dev, num_ch,
+				       sizeof(*adc->inputs_cfg), GFP_KERNEL);
+	if (!adc->inputs_cfg)
+		return -ENOMEM;
+
+	for (chan_idx = 0; chan_idx < num_ch; chan_idx++) {
+		ret = stm32_dfsdm_adc_chan_init_one(indio_dev, channels,
+						    chan_idx);
+		if (ret < 0)
+			goto ch_error;
+	}
+
+	indio_dev->num_channels = num_ch;
+	indio_dev->channels = channels;
+
+	return 0;
+
+ch_error:
+	for (chan_idx--; chan_idx >= 0; chan_idx--)
+		stm32_dfsdm_release_channel(adc->dfsdm,
+					    adc->inputs[chan_idx].id);
+
+	return ret;
+}
+
+static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct stm32_dfsdm_adc *adc;
+	struct device_node *np = pdev->dev.of_node;
+	struct iio_dev *indio_dev;
+	int ret, i;
+
+	if (!np)
+		return -ENODEV;
+
+	indio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
+	if (IS_ERR(indio_dev)) {
+		dev_err(dev, "%s: failed to allocate iio", __func__);
+		return PTR_ERR(indio_dev);
+	}
+
+	indio_dev->name = np->name;
+	indio_dev->dev.parent = dev;
+	indio_dev->dev.of_node = np;
+	indio_dev->info = &stm32_dfsdm_iio_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	adc = iio_priv(indio_dev);
+	if (IS_ERR(adc)) {
+		dev_err(dev, "%s: failed to allocate adc", __func__);
+		return PTR_ERR(adc);
+	}
+
+	if (of_property_read_u32(np, "reg", &adc->fl_id)) {
+		dev_err(&pdev->dev, "missing reg property\n");
+		return -EINVAL;
+	}
+
+	adc->dev = &indio_dev->dev;
+	adc->dfsdm = dev_get_drvdata(pdev->dev.parent);
+
+	ret = stm32_dfsdm_adc_chan_init(indio_dev);
+	if (ret < 0) {
+		dev_err(dev, "iio channels init failed\n");
+		return ret;
+	}
+
+	ret = stm32_dfsdm_get_filter(adc->dfsdm, adc->fl_id);
+	if (ret < 0)
+		goto get_fl_err;
+
+	adc->int_oversampling = DFSDM_MIN_INT_OVERSAMPLING;
+	adc->sinc.oversampling = DFSDM_MIN_FL_OVERSAMPLING;
+
+	init_completion(&adc->completion);
+
+	ret = devm_iio_device_register(dev, indio_dev);
+	if (ret) {
+		dev_err(adc->dev, "failed to register iio device\n");
+		goto register_err;
+	}
+
+	platform_set_drvdata(pdev, adc);
+
+	return 0;
+
+register_err:
+	stm32_dfsdm_release_filter(adc->dfsdm, adc->fl_id);
+
+get_fl_err:
+	for (i = 0; i < indio_dev->num_channels; i++)
+		stm32_dfsdm_release_channel(adc->dfsdm, adc->inputs[i].id);
+
+	return ret;
+}
+
+static int stm32_dfsdm_adc_remove(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev;
+	struct stm32_dfsdm_adc *adc = platform_get_drvdata(pdev);
+	int i;
+
+	indio_dev = iio_priv_to_dev(adc);
+	for (i = 0; i < indio_dev->num_channels; i++)
+		stm32_dfsdm_release_channel(adc->dfsdm, adc->inputs[i].id);
+	stm32_dfsdm_release_filter(adc->dfsdm, adc->fl_id);
+
+	return 0;
+}
+
+static const struct of_device_id stm32_dfsdm_adc_match[] = {
+	{ .compatible = "st,stm32-dfsdm-adc"},
+	{}
+};
+
+static struct platform_driver stm32_dfsdm_adc_driver = {
+	.driver = {
+		.name = "stm32-dfsdm-adc",
+		.of_match_table = stm32_dfsdm_adc_match,
+	},
+	.probe = stm32_dfsdm_adc_probe,
+	.remove = stm32_dfsdm_adc_remove,
+};
+module_platform_driver(stm32_dfsdm_adc_driver);
+
+MODULE_DESCRIPTION("STM32 sigma delta ADC");
+MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

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

* [PATCH 4/7] IIO: add STM32 DFSDM ADC support
@ 2017-01-23 16:32   ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-23 16:32 UTC (permalink / raw)
  To: devicetree, linux-arm-kernel, linux-iio, alsa-devel, Lee Jones,
	Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre Torgue, arnaud.pouliquen

Add driver to handle Sigma Delta ADC conversion for ADC
connected to DFSDM IP.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 drivers/iio/adc/Kconfig           |   9 +
 drivers/iio/adc/Makefile          |   1 +
 drivers/iio/adc/stm32-dfsdm-adc.c | 676 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 686 insertions(+)
 create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index e0b3c09..4b2b886 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -441,6 +441,15 @@ config STM32_ADC
 	  This driver can also be built as a module.  If so, the module
 	  will be called stm32-adc.
 
+config STM32_DFSDM_ADC
+	tristate "STMicroelectronics STM32 DFSDM ADC driver"
+	depends on (ARCH_STM32 && OF && MFD_STM32_DFSDM) || COMPILE_TEST
+	help
+	  Say yes here to build the driver for the STMicroelectronics
+	  STM32 analog-to-digital converter with Digital filter.
+	  This driver can also be built as a module. If so, the module
+	  will be called stm32_dfsdm_adc.
+
 config STX104
 	tristate "Apex Embedded Systems STX104 driver"
 	depends on X86 && ISA_BUS_API
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 8e02a94..aed42c6 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
 obj-$(CONFIG_STX104) += stx104.o
 obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
 obj-$(CONFIG_STM32_ADC) += stm32-adc.o
+obj-$(CONFIG_STM32_DFSDM_ADC) += stm32-dfsdm-adc.o
 obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
 obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
 obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
new file mode 100644
index 0000000..727d6b1
--- /dev/null
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
@@ -0,0 +1,676 @@
+/*
+ * This file is part of STM32 DFSDM ADC driver
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author: Arnaud Pouliquen <arnaud.pouliquen@st.com>.
+ *
+ * License type: GPLv2
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/irq_work.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <linux/iio/iio.h>
+
+#include <linux/mfd/stm32-dfsdm.h>
+
+#define DFSDM_ADC_MAX_RESOLUTION 24
+#define DFSDM_ADC_STORAGE_BITS   32
+
+#define DFSDM_MAX_CH_OFFSET BIT(24)
+#define DFSDM_MAX_CH_SHIFT 24
+
+#define DFSDM_TIMEOUT_US 100000
+#define DFSDM_TIMEOUT (msecs_to_jiffies(DFSDM_TIMEOUT_US / 1000))
+
+#define CH_ID_FROM_IDX(i) (adc->inputs[i].id)
+#define CH_CFG_FROM_IDX(i) (&adc->inputs_cfg[i])
+
+struct stm32_dfsdm_adc {
+	struct device *dev;
+	struct stm32_dfsdm *dfsdm;
+	struct list_head adc_list;
+
+	/* Filter */
+	unsigned int fl_id;
+	struct stm32_dfsdm_sinc_filter sinc;
+	unsigned int int_oversampling;
+
+	/* Channels */
+	struct stm32_dfsdm_channel *inputs;
+	struct stm32_dfsdm_ch_cfg *inputs_cfg;
+
+	/* Raw mode*/
+	struct completion completion;
+	struct stm32_dfsdm_regular reg_params;
+	u32 *buffer;
+};
+
+static const char * const stm32_dfsdm_adc_sinc_order[] = {
+	[0] = "FastSinc",
+	[1] = "Sinc1",
+	[2] = "Sinc2",
+	[3] = "Sinc3",
+	[4] = "Sinc4",
+	[5] = "Sinc5",
+};
+
+static inline const struct iio_chan_spec *get_ch_from_id(
+					struct iio_dev *indio_dev, int ch_id)
+{
+	int i;
+
+	for (i = 0; i < indio_dev->num_channels; i++) {
+		if (ch_id == indio_dev->channels[i].channel)
+			return &indio_dev->channels[i];
+	}
+
+	return NULL;
+}
+
+/*
+ * Filter attributes
+ */
+
+static int stm32_dfsdm_adc_set_sinc(struct iio_dev *indio_dev,
+				    const struct iio_chan_spec *chan,
+				    unsigned int val)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+	dev_dbg(&indio_dev->dev, "%s: %s\n", __func__,
+		stm32_dfsdm_adc_sinc_order[adc->sinc.order]);
+
+	adc->sinc.order = val;
+
+	return 0;
+}
+
+static int stm32_dfsdm_adc_get_sinc(struct iio_dev *indio_dev,
+				    const struct iio_chan_spec *chan)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+	dev_dbg(&indio_dev->dev, "%s: %s\n", __func__,
+		stm32_dfsdm_adc_sinc_order[adc->sinc.order]);
+
+	return adc->sinc.order;
+}
+
+static const struct iio_enum stm32_dfsdm_adc_fl_sinc_order = {
+	.items = stm32_dfsdm_adc_sinc_order,
+	.num_items = ARRAY_SIZE(stm32_dfsdm_adc_sinc_order),
+	.get = stm32_dfsdm_adc_get_sinc,
+	.set = stm32_dfsdm_adc_set_sinc,
+};
+
+static ssize_t stm32_dfsdm_adc_get_int_os(struct iio_dev *indio_dev,
+					  uintptr_t priv,
+					  const struct iio_chan_spec *chan,
+					  char *buf)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", adc->int_oversampling);
+}
+
+static ssize_t stm32_dfsdm_adc_set_int_os(struct iio_dev *indio_dev,
+					  uintptr_t priv,
+					  const struct iio_chan_spec *chan,
+					  const char *buf, size_t len)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	int ret, val;
+
+	ret = kstrtoint(buf, 0, &val);
+	if (ret)
+		return ret;
+
+	if ((!val) || (val > DFSDM_MAX_INT_OVERSAMPLING)) {
+		dev_err(&indio_dev->dev, "invalid oversampling (0 or > %#x)",
+			DFSDM_MAX_INT_OVERSAMPLING);
+		return -EINVAL;
+	}
+	adc->int_oversampling = val;
+
+	return len;
+}
+
+static ssize_t stm32_dfsdm_adc_get_fl_os(struct iio_dev *indio_dev,
+					 uintptr_t priv,
+					 const struct iio_chan_spec *chan,
+					 char *buf)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", adc->sinc.oversampling);
+}
+
+static ssize_t stm32_dfsdm_adc_set_fl_os(struct iio_dev *indio_dev,
+					 uintptr_t priv,
+					const struct iio_chan_spec *chan,
+					const char *buf, size_t len)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	int ret, val;
+
+	ret = kstrtoint(buf, 0, &val);
+	if (ret)
+		return ret;
+
+	if ((!val) || (val > DFSDM_MAX_FL_OVERSAMPLING)) {
+		dev_err(&indio_dev->dev, "invalid oversampling (0 or > %#x)",
+			DFSDM_MAX_FL_OVERSAMPLING);
+		return -EINVAL;
+	}
+	adc->sinc.oversampling = val;
+
+	return len;
+}
+
+/*
+ * Data bit shifting attribute
+ */
+static ssize_t stm32_dfsdm_adc_get_shift(struct iio_dev *indio_dev,
+					 uintptr_t priv,
+					 const struct iio_chan_spec *chan,
+					 char *buf)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", ch_cfg->right_bit_shift);
+}
+
+static ssize_t stm32_dfsdm_adc_set_shift(struct iio_dev *indio_dev,
+					 uintptr_t priv,
+					 const struct iio_chan_spec *chan,
+					 const char *buf, size_t len)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
+	int ret, val;
+
+	ret = kstrtoint(buf, 0, &val);
+	if (ret)
+		return ret;
+
+	if (val > DFSDM_MAX_CH_SHIFT) {
+		dev_err(&indio_dev->dev, "invalid shift value (> %#x)",
+			DFSDM_MAX_CH_SHIFT);
+		return -EINVAL;
+	}
+	ch_cfg->right_bit_shift = val;
+
+	return len;
+}
+
+static const struct iio_chan_spec_ext_info stm32_dfsdm_adc_ext_info[] = {
+	/* sinc_filter_order: Configure Sinc filter order */
+	IIO_ENUM("sinc_filter_order", IIO_SHARED_BY_TYPE,
+		 &stm32_dfsdm_adc_fl_sinc_order),
+	IIO_ENUM_AVAILABLE("sinc_filter_order", &stm32_dfsdm_adc_fl_sinc_order),
+	/* filter oversampling: Post filter oversampling ratio */
+	{
+		.name = "sinc_filter_oversampling_ratio",
+		.shared = IIO_SHARED_BY_TYPE,
+		.read = stm32_dfsdm_adc_get_fl_os,
+		.write = stm32_dfsdm_adc_set_fl_os,
+	},
+	/* data_right_bit_shift : Filter output data shifting */
+	{
+		.name = "data_right_bit_shift",
+		.shared = IIO_SEPARATE,
+		.read = stm32_dfsdm_adc_get_shift,
+		.write = stm32_dfsdm_adc_set_shift,
+	},
+
+	/*
+	 * averaging_length : Mean windows of data from filter.
+	 * Defines how many filter data will be summed to one data output
+	 */
+	{
+		.name = "integrator_oversampling",
+		.shared = IIO_SHARED_BY_TYPE,
+		.read = stm32_dfsdm_adc_get_int_os,
+		.write = stm32_dfsdm_adc_set_int_os,
+	},
+	{},
+};
+
+/*
+ * Filter event routine called under IRQ context
+ */
+static void stm32_dfsdm_event_cb(struct stm32_dfsdm *dfsdm, int flt_id,
+				 enum stm32_dfsdm_events ev, unsigned int param,
+				 void *context)
+{
+	struct stm32_dfsdm_adc *adc = context;
+	unsigned int ch_id;
+
+	dev_dbg(adc->dev, "%s:\n", __func__);
+
+	switch (ev) {
+	case DFSDM_EVENT_REG_EOC:
+		stm32_dfsdm_read_fl_conv(adc->dfsdm, flt_id, adc->buffer,
+					 &ch_id, DFSDM_FILTER_REG_CONV);
+		complete(&adc->completion);
+		break;
+	case DFSDM_EVENT_REG_XRUN:
+		dev_err(adc->dev, "%s: underrun detected for filter %d\n",
+			__func__, flt_id);
+		break;
+	default:
+		dev_err(adc->dev, "%s: event %#x not implemented\n",
+			__func__, ev);
+		break;
+	}
+}
+
+static inline void stm32_dfsdm_adc_fl_config(struct stm32_dfsdm_adc *adc,
+					     u32 channel_mask,
+					     struct stm32_dfsdm_filter *filter)
+{
+	dev_dbg(adc->dev, "%s:\n", __func__);
+
+	filter->event.cb = stm32_dfsdm_event_cb;
+	filter->event.context = adc;
+
+	filter->sinc_params = adc->sinc;
+
+	filter->int_oversampling = adc->int_oversampling;
+}
+
+static int stm32_dfsdm_adc_start_raw_conv(struct stm32_dfsdm_adc *adc,
+					  const struct iio_chan_spec *chan)
+{
+	struct stm32_dfsdm_filter filter;
+	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
+	unsigned int ch_id = CH_ID_FROM_IDX(chan->scan_index);
+	int ret;
+
+	dev_dbg(adc->dev, "%s:\n", __func__);
+
+	memset(&filter, 0, sizeof(filter));
+	filter.reg_params = &adc->reg_params;
+
+	if (!filter.reg_params)
+		return -ENOMEM;
+
+	filter.reg_params->ch_src = ch_id;
+
+	stm32_dfsdm_adc_fl_config(adc, BIT(ch_id), &filter);
+
+	ret = stm32_dfsdm_configure_filter(adc->dfsdm, adc->fl_id, &filter);
+	if (ret < 0) {
+		dev_err(adc->dev, "Failed to configure filter\n");
+		return ret;
+	}
+
+	ret = stm32_dfsdm_start_channel(adc->dfsdm, ch_id, ch_cfg);
+	if (ret < 0)
+		return ret;
+
+	stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id, DFSDM_FILTER_REG_CONV);
+
+	return 0;
+}
+
+static void stm32_dfsdm_adc_stop_raw_conv(struct stm32_dfsdm_adc *adc,
+					  const struct iio_chan_spec *chan)
+{
+	unsigned int ch_id = CH_ID_FROM_IDX(chan->scan_index);
+
+	dev_dbg(adc->dev, "%s:\n", __func__);
+
+	stm32_dfsdm_stop_filter(adc->dfsdm, adc->fl_id);
+	stm32_dfsdm_stop_channel(adc->dfsdm, ch_id);
+}
+
+static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
+				   const struct iio_chan_spec *chan,
+				   u32 *result)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	long timeout;
+	int ret;
+
+	dev_dbg(&indio_dev->dev, "%s:\n", __func__);
+
+	reinit_completion(&adc->completion);
+
+	ret = stm32_dfsdm_register_fl_event(adc->dfsdm, adc->fl_id,
+					    DFSDM_EVENT_REG_EOC, 0);
+	if (ret < 0) {
+		dev_err(&indio_dev->dev, "Failed to register event\n");
+		return ret;
+	}
+
+	adc->buffer = result;
+	ret = stm32_dfsdm_adc_start_raw_conv(adc, chan);
+	if (ret) {
+		dev_err(&indio_dev->dev, "Failed to start conversion\n");
+		goto free_event;
+	}
+
+	timeout = wait_for_completion_interruptible_timeout(&adc->completion,
+							    DFSDM_TIMEOUT);
+	if (timeout == 0) {
+		dev_warn(&indio_dev->dev, "Conversion timed out!\n");
+		ret = -ETIMEDOUT;
+	} else if (timeout < 0) {
+		ret = timeout;
+	} else {
+		dev_dbg(&indio_dev->dev, "converted val %#x\n", *result);
+		ret = IIO_VAL_INT;
+	}
+
+	stm32_dfsdm_adc_stop_raw_conv(adc, chan);
+
+free_event:
+	adc->buffer = NULL;
+	stm32_dfsdm_unregister_fl_event(adc->dfsdm, adc->fl_id,
+					DFSDM_EVENT_REG_EOC, 0);
+
+	return ret;
+}
+
+static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
+				struct iio_chan_spec const *chan, int *val,
+				int *val2, long mask)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
+	int ret = -EINVAL;
+
+	dev_dbg(&indio_dev->dev, "%s channel %d\n", __func__, chan->channel);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
+		if (!ret)
+			ret = IIO_VAL_INT;
+		break;
+	case IIO_CHAN_INFO_OFFSET:
+		*val = ch_cfg->offset;
+		ret = IIO_VAL_INT;
+		break;
+	}
+
+	return ret;
+}
+
+static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev,
+				 struct iio_chan_spec const *chan, int val,
+				 int val2, long mask)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
+
+	dev_dbg(&indio_dev->dev, "%s channel%d", __func__, chan->channel);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_OFFSET:
+		if (val > DFSDM_MAX_CH_OFFSET) {
+			dev_err(&indio_dev->dev, "invalid offset (> %#lx)",
+				DFSDM_MAX_CH_OFFSET);
+			return -EINVAL;
+		}
+		ch_cfg->offset = val;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct iio_info stm32_dfsdm_iio_info = {
+	.read_raw = stm32_dfsdm_read_raw,
+	.write_raw = stm32_dfsdm_write_raw,
+	.driver_module = THIS_MODULE,
+};
+
+static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
+					 struct iio_chan_spec *chan,
+					 int chan_idx)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	struct stm32_dfsdm_channel *dfsdm_ch = &adc->inputs[chan_idx];
+	struct iio_chan_spec *ch = &chan[chan_idx];
+	int ret;
+	unsigned int alt_ch = 0;
+
+	ret = of_property_read_u32_index(indio_dev->dev.of_node,
+					 "st,adc-channels", chan_idx,
+					 &ch->channel);
+	if (ret < 0) {
+		dev_err(&indio_dev->dev,
+			" error parsing 'st,adc-channels' for idx %d\n",
+			chan_idx);
+		return ret;
+	}
+
+	ret = of_property_read_string_index(indio_dev->dev.of_node,
+					    "st,adc-channel-names", chan_idx,
+					    &ch->datasheet_name);
+	if (ret < 0) {
+		dev_err(&indio_dev->dev,
+			" error parsing 'st,adc-channel-names' for idx %d\n",
+			chan_idx);
+		return ret;
+	}
+
+	ch->extend_name = ch->datasheet_name;
+	ch->type = IIO_VOLTAGE;
+	ch->indexed = 1;
+	ch->scan_index = chan_idx;
+	ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+				 BIT(IIO_CHAN_INFO_OFFSET);
+	ch->scan_type.sign = 'u';
+	ch->scan_type.realbits = DFSDM_ADC_MAX_RESOLUTION;
+	ch->scan_type.storagebits = DFSDM_ADC_STORAGE_BITS;
+	ch->scan_type.shift = 8;
+
+	ch->ext_info = stm32_dfsdm_adc_ext_info;
+
+	of_property_read_u32_index(indio_dev->dev.of_node, "st,adc-alt-channel",
+				   chan_idx, &alt_ch);
+	/* Select the previous channel if alternate field is defined*/
+	if (alt_ch) {
+		if (!ch->channel)
+			ch->channel = adc->dfsdm->max_channels;
+		ch->channel -= 1;
+		dfsdm_ch->serial_if.pins = DFSDM_CHANNEL_NEXT_CHANNEL_PINS;
+	} else {
+		dfsdm_ch->serial_if.pins = DFSDM_CHANNEL_SAME_CHANNEL_PINS;
+	}
+	dfsdm_ch->id = ch->channel;
+
+	dfsdm_ch->type.DataPacking = DFSDM_CHANNEL_STANDARD_MODE;
+
+	dfsdm_ch->type.source = DFSDM_CHANNEL_EXTERNAL_INPUTS;
+	ret = of_property_read_u32_index(indio_dev->dev.of_node,
+					 "st,adc-channel-types",
+					 chan_idx, &dfsdm_ch->serial_if.type);
+	if (ret < 0)
+		dfsdm_ch->serial_if.type = DFSDM_CHANNEL_SPI_RISING;
+
+	ret = of_property_read_u32_index(indio_dev->dev.of_node,
+					 "st,adc-channel-clk-src",
+					 chan_idx,
+					 &dfsdm_ch->serial_if.spi_clk);
+
+	if ((dfsdm_ch->serial_if.type == DFSDM_CHANNEL_MANCHESTER_RISING)  ||
+	    (dfsdm_ch->serial_if.type == DFSDM_CHANNEL_MANCHESTER_FALLING) ||
+	    (ret < 0))
+		dfsdm_ch->serial_if.spi_clk = DFSDM_CHANNEL_SPI_CLOCK_INTERNAL;
+
+	return stm32_dfsdm_get_channel(adc->dfsdm, dfsdm_ch);
+}
+
+static int stm32_dfsdm_adc_chan_init(struct iio_dev *indio_dev)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	unsigned int num_ch;
+	struct iio_chan_spec *channels;
+	int ret, chan_idx;
+
+	num_ch = of_property_count_strings(indio_dev->dev.of_node,
+					   "st,adc-channel-names");
+
+	channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
+				GFP_KERNEL);
+	if (!channels)
+		return -ENOMEM;
+
+	adc->inputs = devm_kcalloc(&indio_dev->dev, num_ch,
+				   sizeof(*adc->inputs), GFP_KERNEL);
+	if (!adc->inputs)
+		return -ENOMEM;
+
+	adc->inputs_cfg = devm_kcalloc(&indio_dev->dev, num_ch,
+				       sizeof(*adc->inputs_cfg), GFP_KERNEL);
+	if (!adc->inputs_cfg)
+		return -ENOMEM;
+
+	for (chan_idx = 0; chan_idx < num_ch; chan_idx++) {
+		ret = stm32_dfsdm_adc_chan_init_one(indio_dev, channels,
+						    chan_idx);
+		if (ret < 0)
+			goto ch_error;
+	}
+
+	indio_dev->num_channels = num_ch;
+	indio_dev->channels = channels;
+
+	return 0;
+
+ch_error:
+	for (chan_idx--; chan_idx >= 0; chan_idx--)
+		stm32_dfsdm_release_channel(adc->dfsdm,
+					    adc->inputs[chan_idx].id);
+
+	return ret;
+}
+
+static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct stm32_dfsdm_adc *adc;
+	struct device_node *np = pdev->dev.of_node;
+	struct iio_dev *indio_dev;
+	int ret, i;
+
+	if (!np)
+		return -ENODEV;
+
+	indio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
+	if (IS_ERR(indio_dev)) {
+		dev_err(dev, "%s: failed to allocate iio", __func__);
+		return PTR_ERR(indio_dev);
+	}
+
+	indio_dev->name = np->name;
+	indio_dev->dev.parent = dev;
+	indio_dev->dev.of_node = np;
+	indio_dev->info = &stm32_dfsdm_iio_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	adc = iio_priv(indio_dev);
+	if (IS_ERR(adc)) {
+		dev_err(dev, "%s: failed to allocate adc", __func__);
+		return PTR_ERR(adc);
+	}
+
+	if (of_property_read_u32(np, "reg", &adc->fl_id)) {
+		dev_err(&pdev->dev, "missing reg property\n");
+		return -EINVAL;
+	}
+
+	adc->dev = &indio_dev->dev;
+	adc->dfsdm = dev_get_drvdata(pdev->dev.parent);
+
+	ret = stm32_dfsdm_adc_chan_init(indio_dev);
+	if (ret < 0) {
+		dev_err(dev, "iio channels init failed\n");
+		return ret;
+	}
+
+	ret = stm32_dfsdm_get_filter(adc->dfsdm, adc->fl_id);
+	if (ret < 0)
+		goto get_fl_err;
+
+	adc->int_oversampling = DFSDM_MIN_INT_OVERSAMPLING;
+	adc->sinc.oversampling = DFSDM_MIN_FL_OVERSAMPLING;
+
+	init_completion(&adc->completion);
+
+	ret = devm_iio_device_register(dev, indio_dev);
+	if (ret) {
+		dev_err(adc->dev, "failed to register iio device\n");
+		goto register_err;
+	}
+
+	platform_set_drvdata(pdev, adc);
+
+	return 0;
+
+register_err:
+	stm32_dfsdm_release_filter(adc->dfsdm, adc->fl_id);
+
+get_fl_err:
+	for (i = 0; i < indio_dev->num_channels; i++)
+		stm32_dfsdm_release_channel(adc->dfsdm, adc->inputs[i].id);
+
+	return ret;
+}
+
+static int stm32_dfsdm_adc_remove(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev;
+	struct stm32_dfsdm_adc *adc = platform_get_drvdata(pdev);
+	int i;
+
+	indio_dev = iio_priv_to_dev(adc);
+	for (i = 0; i < indio_dev->num_channels; i++)
+		stm32_dfsdm_release_channel(adc->dfsdm, adc->inputs[i].id);
+	stm32_dfsdm_release_filter(adc->dfsdm, adc->fl_id);
+
+	return 0;
+}
+
+static const struct of_device_id stm32_dfsdm_adc_match[] = {
+	{ .compatible = "st,stm32-dfsdm-adc"},
+	{}
+};
+
+static struct platform_driver stm32_dfsdm_adc_driver = {
+	.driver = {
+		.name = "stm32-dfsdm-adc",
+		.of_match_table = stm32_dfsdm_adc_match,
+	},
+	.probe = stm32_dfsdm_adc_probe,
+	.remove = stm32_dfsdm_adc_remove,
+};
+module_platform_driver(stm32_dfsdm_adc_driver);
+
+MODULE_DESCRIPTION("STM32 sigma delta ADC");
+MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1


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

* [PATCH 4/7] IIO: add STM32 DFSDM ADC support
@ 2017-01-23 16:32   ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-23 16:32 UTC (permalink / raw)
  To: linux-arm-kernel

Add driver to handle Sigma Delta ADC conversion for ADC
connected to DFSDM IP.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 drivers/iio/adc/Kconfig           |   9 +
 drivers/iio/adc/Makefile          |   1 +
 drivers/iio/adc/stm32-dfsdm-adc.c | 676 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 686 insertions(+)
 create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index e0b3c09..4b2b886 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -441,6 +441,15 @@ config STM32_ADC
 	  This driver can also be built as a module.  If so, the module
 	  will be called stm32-adc.
 
+config STM32_DFSDM_ADC
+	tristate "STMicroelectronics STM32 DFSDM ADC driver"
+	depends on (ARCH_STM32 && OF && MFD_STM32_DFSDM) || COMPILE_TEST
+	help
+	  Say yes here to build the driver for the STMicroelectronics
+	  STM32 analog-to-digital converter with Digital filter.
+	  This driver can also be built as a module. If so, the module
+	  will be called stm32_dfsdm_adc.
+
 config STX104
 	tristate "Apex Embedded Systems STX104 driver"
 	depends on X86 && ISA_BUS_API
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 8e02a94..aed42c6 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
 obj-$(CONFIG_STX104) += stx104.o
 obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
 obj-$(CONFIG_STM32_ADC) += stm32-adc.o
+obj-$(CONFIG_STM32_DFSDM_ADC) += stm32-dfsdm-adc.o
 obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
 obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
 obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
new file mode 100644
index 0000000..727d6b1
--- /dev/null
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
@@ -0,0 +1,676 @@
+/*
+ * This file is part of STM32 DFSDM ADC driver
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author: Arnaud Pouliquen <arnaud.pouliquen@st.com>.
+ *
+ * License type: GPLv2
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/irq_work.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <linux/iio/iio.h>
+
+#include <linux/mfd/stm32-dfsdm.h>
+
+#define DFSDM_ADC_MAX_RESOLUTION 24
+#define DFSDM_ADC_STORAGE_BITS   32
+
+#define DFSDM_MAX_CH_OFFSET BIT(24)
+#define DFSDM_MAX_CH_SHIFT 24
+
+#define DFSDM_TIMEOUT_US 100000
+#define DFSDM_TIMEOUT (msecs_to_jiffies(DFSDM_TIMEOUT_US / 1000))
+
+#define CH_ID_FROM_IDX(i) (adc->inputs[i].id)
+#define CH_CFG_FROM_IDX(i) (&adc->inputs_cfg[i])
+
+struct stm32_dfsdm_adc {
+	struct device *dev;
+	struct stm32_dfsdm *dfsdm;
+	struct list_head adc_list;
+
+	/* Filter */
+	unsigned int fl_id;
+	struct stm32_dfsdm_sinc_filter sinc;
+	unsigned int int_oversampling;
+
+	/* Channels */
+	struct stm32_dfsdm_channel *inputs;
+	struct stm32_dfsdm_ch_cfg *inputs_cfg;
+
+	/* Raw mode*/
+	struct completion completion;
+	struct stm32_dfsdm_regular reg_params;
+	u32 *buffer;
+};
+
+static const char * const stm32_dfsdm_adc_sinc_order[] = {
+	[0] = "FastSinc",
+	[1] = "Sinc1",
+	[2] = "Sinc2",
+	[3] = "Sinc3",
+	[4] = "Sinc4",
+	[5] = "Sinc5",
+};
+
+static inline const struct iio_chan_spec *get_ch_from_id(
+					struct iio_dev *indio_dev, int ch_id)
+{
+	int i;
+
+	for (i = 0; i < indio_dev->num_channels; i++) {
+		if (ch_id == indio_dev->channels[i].channel)
+			return &indio_dev->channels[i];
+	}
+
+	return NULL;
+}
+
+/*
+ * Filter attributes
+ */
+
+static int stm32_dfsdm_adc_set_sinc(struct iio_dev *indio_dev,
+				    const struct iio_chan_spec *chan,
+				    unsigned int val)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+	dev_dbg(&indio_dev->dev, "%s: %s\n", __func__,
+		stm32_dfsdm_adc_sinc_order[adc->sinc.order]);
+
+	adc->sinc.order = val;
+
+	return 0;
+}
+
+static int stm32_dfsdm_adc_get_sinc(struct iio_dev *indio_dev,
+				    const struct iio_chan_spec *chan)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+	dev_dbg(&indio_dev->dev, "%s: %s\n", __func__,
+		stm32_dfsdm_adc_sinc_order[adc->sinc.order]);
+
+	return adc->sinc.order;
+}
+
+static const struct iio_enum stm32_dfsdm_adc_fl_sinc_order = {
+	.items = stm32_dfsdm_adc_sinc_order,
+	.num_items = ARRAY_SIZE(stm32_dfsdm_adc_sinc_order),
+	.get = stm32_dfsdm_adc_get_sinc,
+	.set = stm32_dfsdm_adc_set_sinc,
+};
+
+static ssize_t stm32_dfsdm_adc_get_int_os(struct iio_dev *indio_dev,
+					  uintptr_t priv,
+					  const struct iio_chan_spec *chan,
+					  char *buf)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", adc->int_oversampling);
+}
+
+static ssize_t stm32_dfsdm_adc_set_int_os(struct iio_dev *indio_dev,
+					  uintptr_t priv,
+					  const struct iio_chan_spec *chan,
+					  const char *buf, size_t len)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	int ret, val;
+
+	ret = kstrtoint(buf, 0, &val);
+	if (ret)
+		return ret;
+
+	if ((!val) || (val > DFSDM_MAX_INT_OVERSAMPLING)) {
+		dev_err(&indio_dev->dev, "invalid oversampling (0 or > %#x)",
+			DFSDM_MAX_INT_OVERSAMPLING);
+		return -EINVAL;
+	}
+	adc->int_oversampling = val;
+
+	return len;
+}
+
+static ssize_t stm32_dfsdm_adc_get_fl_os(struct iio_dev *indio_dev,
+					 uintptr_t priv,
+					 const struct iio_chan_spec *chan,
+					 char *buf)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", adc->sinc.oversampling);
+}
+
+static ssize_t stm32_dfsdm_adc_set_fl_os(struct iio_dev *indio_dev,
+					 uintptr_t priv,
+					const struct iio_chan_spec *chan,
+					const char *buf, size_t len)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	int ret, val;
+
+	ret = kstrtoint(buf, 0, &val);
+	if (ret)
+		return ret;
+
+	if ((!val) || (val > DFSDM_MAX_FL_OVERSAMPLING)) {
+		dev_err(&indio_dev->dev, "invalid oversampling (0 or > %#x)",
+			DFSDM_MAX_FL_OVERSAMPLING);
+		return -EINVAL;
+	}
+	adc->sinc.oversampling = val;
+
+	return len;
+}
+
+/*
+ * Data bit shifting attribute
+ */
+static ssize_t stm32_dfsdm_adc_get_shift(struct iio_dev *indio_dev,
+					 uintptr_t priv,
+					 const struct iio_chan_spec *chan,
+					 char *buf)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", ch_cfg->right_bit_shift);
+}
+
+static ssize_t stm32_dfsdm_adc_set_shift(struct iio_dev *indio_dev,
+					 uintptr_t priv,
+					 const struct iio_chan_spec *chan,
+					 const char *buf, size_t len)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
+	int ret, val;
+
+	ret = kstrtoint(buf, 0, &val);
+	if (ret)
+		return ret;
+
+	if (val > DFSDM_MAX_CH_SHIFT) {
+		dev_err(&indio_dev->dev, "invalid shift value (> %#x)",
+			DFSDM_MAX_CH_SHIFT);
+		return -EINVAL;
+	}
+	ch_cfg->right_bit_shift = val;
+
+	return len;
+}
+
+static const struct iio_chan_spec_ext_info stm32_dfsdm_adc_ext_info[] = {
+	/* sinc_filter_order: Configure Sinc filter order */
+	IIO_ENUM("sinc_filter_order", IIO_SHARED_BY_TYPE,
+		 &stm32_dfsdm_adc_fl_sinc_order),
+	IIO_ENUM_AVAILABLE("sinc_filter_order", &stm32_dfsdm_adc_fl_sinc_order),
+	/* filter oversampling: Post filter oversampling ratio */
+	{
+		.name = "sinc_filter_oversampling_ratio",
+		.shared = IIO_SHARED_BY_TYPE,
+		.read = stm32_dfsdm_adc_get_fl_os,
+		.write = stm32_dfsdm_adc_set_fl_os,
+	},
+	/* data_right_bit_shift : Filter output data shifting */
+	{
+		.name = "data_right_bit_shift",
+		.shared = IIO_SEPARATE,
+		.read = stm32_dfsdm_adc_get_shift,
+		.write = stm32_dfsdm_adc_set_shift,
+	},
+
+	/*
+	 * averaging_length : Mean windows of data from filter.
+	 * Defines how many filter data will be summed to one data output
+	 */
+	{
+		.name = "integrator_oversampling",
+		.shared = IIO_SHARED_BY_TYPE,
+		.read = stm32_dfsdm_adc_get_int_os,
+		.write = stm32_dfsdm_adc_set_int_os,
+	},
+	{},
+};
+
+/*
+ * Filter event routine called under IRQ context
+ */
+static void stm32_dfsdm_event_cb(struct stm32_dfsdm *dfsdm, int flt_id,
+				 enum stm32_dfsdm_events ev, unsigned int param,
+				 void *context)
+{
+	struct stm32_dfsdm_adc *adc = context;
+	unsigned int ch_id;
+
+	dev_dbg(adc->dev, "%s:\n", __func__);
+
+	switch (ev) {
+	case DFSDM_EVENT_REG_EOC:
+		stm32_dfsdm_read_fl_conv(adc->dfsdm, flt_id, adc->buffer,
+					 &ch_id, DFSDM_FILTER_REG_CONV);
+		complete(&adc->completion);
+		break;
+	case DFSDM_EVENT_REG_XRUN:
+		dev_err(adc->dev, "%s: underrun detected for filter %d\n",
+			__func__, flt_id);
+		break;
+	default:
+		dev_err(adc->dev, "%s: event %#x not implemented\n",
+			__func__, ev);
+		break;
+	}
+}
+
+static inline void stm32_dfsdm_adc_fl_config(struct stm32_dfsdm_adc *adc,
+					     u32 channel_mask,
+					     struct stm32_dfsdm_filter *filter)
+{
+	dev_dbg(adc->dev, "%s:\n", __func__);
+
+	filter->event.cb = stm32_dfsdm_event_cb;
+	filter->event.context = adc;
+
+	filter->sinc_params = adc->sinc;
+
+	filter->int_oversampling = adc->int_oversampling;
+}
+
+static int stm32_dfsdm_adc_start_raw_conv(struct stm32_dfsdm_adc *adc,
+					  const struct iio_chan_spec *chan)
+{
+	struct stm32_dfsdm_filter filter;
+	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
+	unsigned int ch_id = CH_ID_FROM_IDX(chan->scan_index);
+	int ret;
+
+	dev_dbg(adc->dev, "%s:\n", __func__);
+
+	memset(&filter, 0, sizeof(filter));
+	filter.reg_params = &adc->reg_params;
+
+	if (!filter.reg_params)
+		return -ENOMEM;
+
+	filter.reg_params->ch_src = ch_id;
+
+	stm32_dfsdm_adc_fl_config(adc, BIT(ch_id), &filter);
+
+	ret = stm32_dfsdm_configure_filter(adc->dfsdm, adc->fl_id, &filter);
+	if (ret < 0) {
+		dev_err(adc->dev, "Failed to configure filter\n");
+		return ret;
+	}
+
+	ret = stm32_dfsdm_start_channel(adc->dfsdm, ch_id, ch_cfg);
+	if (ret < 0)
+		return ret;
+
+	stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id, DFSDM_FILTER_REG_CONV);
+
+	return 0;
+}
+
+static void stm32_dfsdm_adc_stop_raw_conv(struct stm32_dfsdm_adc *adc,
+					  const struct iio_chan_spec *chan)
+{
+	unsigned int ch_id = CH_ID_FROM_IDX(chan->scan_index);
+
+	dev_dbg(adc->dev, "%s:\n", __func__);
+
+	stm32_dfsdm_stop_filter(adc->dfsdm, adc->fl_id);
+	stm32_dfsdm_stop_channel(adc->dfsdm, ch_id);
+}
+
+static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
+				   const struct iio_chan_spec *chan,
+				   u32 *result)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	long timeout;
+	int ret;
+
+	dev_dbg(&indio_dev->dev, "%s:\n", __func__);
+
+	reinit_completion(&adc->completion);
+
+	ret = stm32_dfsdm_register_fl_event(adc->dfsdm, adc->fl_id,
+					    DFSDM_EVENT_REG_EOC, 0);
+	if (ret < 0) {
+		dev_err(&indio_dev->dev, "Failed to register event\n");
+		return ret;
+	}
+
+	adc->buffer = result;
+	ret = stm32_dfsdm_adc_start_raw_conv(adc, chan);
+	if (ret) {
+		dev_err(&indio_dev->dev, "Failed to start conversion\n");
+		goto free_event;
+	}
+
+	timeout = wait_for_completion_interruptible_timeout(&adc->completion,
+							    DFSDM_TIMEOUT);
+	if (timeout == 0) {
+		dev_warn(&indio_dev->dev, "Conversion timed out!\n");
+		ret = -ETIMEDOUT;
+	} else if (timeout < 0) {
+		ret = timeout;
+	} else {
+		dev_dbg(&indio_dev->dev, "converted val %#x\n", *result);
+		ret = IIO_VAL_INT;
+	}
+
+	stm32_dfsdm_adc_stop_raw_conv(adc, chan);
+
+free_event:
+	adc->buffer = NULL;
+	stm32_dfsdm_unregister_fl_event(adc->dfsdm, adc->fl_id,
+					DFSDM_EVENT_REG_EOC, 0);
+
+	return ret;
+}
+
+static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
+				struct iio_chan_spec const *chan, int *val,
+				int *val2, long mask)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
+	int ret = -EINVAL;
+
+	dev_dbg(&indio_dev->dev, "%s channel %d\n", __func__, chan->channel);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
+		if (!ret)
+			ret = IIO_VAL_INT;
+		break;
+	case IIO_CHAN_INFO_OFFSET:
+		*val = ch_cfg->offset;
+		ret = IIO_VAL_INT;
+		break;
+	}
+
+	return ret;
+}
+
+static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev,
+				 struct iio_chan_spec const *chan, int val,
+				 int val2, long mask)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
+
+	dev_dbg(&indio_dev->dev, "%s channel%d", __func__, chan->channel);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_OFFSET:
+		if (val > DFSDM_MAX_CH_OFFSET) {
+			dev_err(&indio_dev->dev, "invalid offset (> %#lx)",
+				DFSDM_MAX_CH_OFFSET);
+			return -EINVAL;
+		}
+		ch_cfg->offset = val;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct iio_info stm32_dfsdm_iio_info = {
+	.read_raw = stm32_dfsdm_read_raw,
+	.write_raw = stm32_dfsdm_write_raw,
+	.driver_module = THIS_MODULE,
+};
+
+static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
+					 struct iio_chan_spec *chan,
+					 int chan_idx)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	struct stm32_dfsdm_channel *dfsdm_ch = &adc->inputs[chan_idx];
+	struct iio_chan_spec *ch = &chan[chan_idx];
+	int ret;
+	unsigned int alt_ch = 0;
+
+	ret = of_property_read_u32_index(indio_dev->dev.of_node,
+					 "st,adc-channels", chan_idx,
+					 &ch->channel);
+	if (ret < 0) {
+		dev_err(&indio_dev->dev,
+			" error parsing 'st,adc-channels' for idx %d\n",
+			chan_idx);
+		return ret;
+	}
+
+	ret = of_property_read_string_index(indio_dev->dev.of_node,
+					    "st,adc-channel-names", chan_idx,
+					    &ch->datasheet_name);
+	if (ret < 0) {
+		dev_err(&indio_dev->dev,
+			" error parsing 'st,adc-channel-names' for idx %d\n",
+			chan_idx);
+		return ret;
+	}
+
+	ch->extend_name = ch->datasheet_name;
+	ch->type = IIO_VOLTAGE;
+	ch->indexed = 1;
+	ch->scan_index = chan_idx;
+	ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+				 BIT(IIO_CHAN_INFO_OFFSET);
+	ch->scan_type.sign = 'u';
+	ch->scan_type.realbits = DFSDM_ADC_MAX_RESOLUTION;
+	ch->scan_type.storagebits = DFSDM_ADC_STORAGE_BITS;
+	ch->scan_type.shift = 8;
+
+	ch->ext_info = stm32_dfsdm_adc_ext_info;
+
+	of_property_read_u32_index(indio_dev->dev.of_node, "st,adc-alt-channel",
+				   chan_idx, &alt_ch);
+	/* Select the previous channel if alternate field is defined*/
+	if (alt_ch) {
+		if (!ch->channel)
+			ch->channel = adc->dfsdm->max_channels;
+		ch->channel -= 1;
+		dfsdm_ch->serial_if.pins = DFSDM_CHANNEL_NEXT_CHANNEL_PINS;
+	} else {
+		dfsdm_ch->serial_if.pins = DFSDM_CHANNEL_SAME_CHANNEL_PINS;
+	}
+	dfsdm_ch->id = ch->channel;
+
+	dfsdm_ch->type.DataPacking = DFSDM_CHANNEL_STANDARD_MODE;
+
+	dfsdm_ch->type.source = DFSDM_CHANNEL_EXTERNAL_INPUTS;
+	ret = of_property_read_u32_index(indio_dev->dev.of_node,
+					 "st,adc-channel-types",
+					 chan_idx, &dfsdm_ch->serial_if.type);
+	if (ret < 0)
+		dfsdm_ch->serial_if.type = DFSDM_CHANNEL_SPI_RISING;
+
+	ret = of_property_read_u32_index(indio_dev->dev.of_node,
+					 "st,adc-channel-clk-src",
+					 chan_idx,
+					 &dfsdm_ch->serial_if.spi_clk);
+
+	if ((dfsdm_ch->serial_if.type == DFSDM_CHANNEL_MANCHESTER_RISING)  ||
+	    (dfsdm_ch->serial_if.type == DFSDM_CHANNEL_MANCHESTER_FALLING) ||
+	    (ret < 0))
+		dfsdm_ch->serial_if.spi_clk = DFSDM_CHANNEL_SPI_CLOCK_INTERNAL;
+
+	return stm32_dfsdm_get_channel(adc->dfsdm, dfsdm_ch);
+}
+
+static int stm32_dfsdm_adc_chan_init(struct iio_dev *indio_dev)
+{
+	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+	unsigned int num_ch;
+	struct iio_chan_spec *channels;
+	int ret, chan_idx;
+
+	num_ch = of_property_count_strings(indio_dev->dev.of_node,
+					   "st,adc-channel-names");
+
+	channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
+				GFP_KERNEL);
+	if (!channels)
+		return -ENOMEM;
+
+	adc->inputs = devm_kcalloc(&indio_dev->dev, num_ch,
+				   sizeof(*adc->inputs), GFP_KERNEL);
+	if (!adc->inputs)
+		return -ENOMEM;
+
+	adc->inputs_cfg = devm_kcalloc(&indio_dev->dev, num_ch,
+				       sizeof(*adc->inputs_cfg), GFP_KERNEL);
+	if (!adc->inputs_cfg)
+		return -ENOMEM;
+
+	for (chan_idx = 0; chan_idx < num_ch; chan_idx++) {
+		ret = stm32_dfsdm_adc_chan_init_one(indio_dev, channels,
+						    chan_idx);
+		if (ret < 0)
+			goto ch_error;
+	}
+
+	indio_dev->num_channels = num_ch;
+	indio_dev->channels = channels;
+
+	return 0;
+
+ch_error:
+	for (chan_idx--; chan_idx >= 0; chan_idx--)
+		stm32_dfsdm_release_channel(adc->dfsdm,
+					    adc->inputs[chan_idx].id);
+
+	return ret;
+}
+
+static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct stm32_dfsdm_adc *adc;
+	struct device_node *np = pdev->dev.of_node;
+	struct iio_dev *indio_dev;
+	int ret, i;
+
+	if (!np)
+		return -ENODEV;
+
+	indio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
+	if (IS_ERR(indio_dev)) {
+		dev_err(dev, "%s: failed to allocate iio", __func__);
+		return PTR_ERR(indio_dev);
+	}
+
+	indio_dev->name = np->name;
+	indio_dev->dev.parent = dev;
+	indio_dev->dev.of_node = np;
+	indio_dev->info = &stm32_dfsdm_iio_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	adc = iio_priv(indio_dev);
+	if (IS_ERR(adc)) {
+		dev_err(dev, "%s: failed to allocate adc", __func__);
+		return PTR_ERR(adc);
+	}
+
+	if (of_property_read_u32(np, "reg", &adc->fl_id)) {
+		dev_err(&pdev->dev, "missing reg property\n");
+		return -EINVAL;
+	}
+
+	adc->dev = &indio_dev->dev;
+	adc->dfsdm = dev_get_drvdata(pdev->dev.parent);
+
+	ret = stm32_dfsdm_adc_chan_init(indio_dev);
+	if (ret < 0) {
+		dev_err(dev, "iio channels init failed\n");
+		return ret;
+	}
+
+	ret = stm32_dfsdm_get_filter(adc->dfsdm, adc->fl_id);
+	if (ret < 0)
+		goto get_fl_err;
+
+	adc->int_oversampling = DFSDM_MIN_INT_OVERSAMPLING;
+	adc->sinc.oversampling = DFSDM_MIN_FL_OVERSAMPLING;
+
+	init_completion(&adc->completion);
+
+	ret = devm_iio_device_register(dev, indio_dev);
+	if (ret) {
+		dev_err(adc->dev, "failed to register iio device\n");
+		goto register_err;
+	}
+
+	platform_set_drvdata(pdev, adc);
+
+	return 0;
+
+register_err:
+	stm32_dfsdm_release_filter(adc->dfsdm, adc->fl_id);
+
+get_fl_err:
+	for (i = 0; i < indio_dev->num_channels; i++)
+		stm32_dfsdm_release_channel(adc->dfsdm, adc->inputs[i].id);
+
+	return ret;
+}
+
+static int stm32_dfsdm_adc_remove(struct platform_device *pdev)
+{
+	struct iio_dev *indio_dev;
+	struct stm32_dfsdm_adc *adc = platform_get_drvdata(pdev);
+	int i;
+
+	indio_dev = iio_priv_to_dev(adc);
+	for (i = 0; i < indio_dev->num_channels; i++)
+		stm32_dfsdm_release_channel(adc->dfsdm, adc->inputs[i].id);
+	stm32_dfsdm_release_filter(adc->dfsdm, adc->fl_id);
+
+	return 0;
+}
+
+static const struct of_device_id stm32_dfsdm_adc_match[] = {
+	{ .compatible = "st,stm32-dfsdm-adc"},
+	{}
+};
+
+static struct platform_driver stm32_dfsdm_adc_driver = {
+	.driver = {
+		.name = "stm32-dfsdm-adc",
+		.of_match_table = stm32_dfsdm_adc_match,
+	},
+	.probe = stm32_dfsdm_adc_probe,
+	.remove = stm32_dfsdm_adc_remove,
+};
+module_platform_driver(stm32_dfsdm_adc_driver);
+
+MODULE_DESCRIPTION("STM32 sigma delta ADC");
+MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

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

* [PATCH 5/7] ASoC: dmaengine_pcm: add copy support
  2017-01-23 16:32 ` Arnaud Pouliquen
  (?)
@ 2017-01-23 16:32   ` Arnaud Pouliquen
  -1 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-23 16:32 UTC (permalink / raw)
  To: devicetree, linux-arm-kernel, linux-iio, alsa-devel, Lee Jones,
	Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: arnaud.pouliquen, Alexandre Torgue, Maxime Coquelin

From: olivier moysan <olivier.moysan@st.com>

Add copy support in pcm damengine operations.
This allows to pre/post process samples (apply shift, mask ...)
for playback/capture to support hardware contrains.

Signed-off-by: olivier moysan <omoysan.stm32@gmail.com>
Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 include/sound/dmaengine_pcm.h         |  3 +++
 sound/soc/soc-generic-dmaengine-pcm.c | 37 +++++++++++++++++++++++++++++++++--
 2 files changed, 38 insertions(+), 2 deletions(-)

diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h
index 67be244..9d7bce8 100644
--- a/include/sound/dmaengine_pcm.h
+++ b/include/sound/dmaengine_pcm.h
@@ -137,6 +137,9 @@ struct snd_dmaengine_pcm_config {
 	int (*prepare_slave_config)(struct snd_pcm_substream *substream,
 			struct snd_pcm_hw_params *params,
 			struct dma_slave_config *slave_config);
+	int (*copy)(struct snd_pcm_substream *substream, int channel,
+		    snd_pcm_uframes_t pos,
+		    void __user *buf, snd_pcm_uframes_t count);
 	struct dma_chan *(*compat_request_channel)(
 			struct snd_soc_pcm_runtime *rtd,
 			struct snd_pcm_substream *substream);
diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c
index 6cef397..bd8332ce 100644
--- a/sound/soc/soc-generic-dmaengine-pcm.c
+++ b/sound/soc/soc-generic-dmaengine-pcm.c
@@ -329,6 +329,16 @@ static snd_pcm_uframes_t dmaengine_pcm_pointer(
 		return snd_dmaengine_pcm_pointer(substream);
 }
 
+int dmaengine_pcm_copy(struct snd_pcm_substream *substream, int channel,
+		       snd_pcm_uframes_t pos, void __user *buf,
+		       snd_pcm_uframes_t count)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
+
+	return pcm->config->copy(substream, channel, pos, buf, count);
+}
+
 static const struct snd_pcm_ops dmaengine_pcm_ops = {
 	.open		= dmaengine_pcm_open,
 	.close		= snd_dmaengine_pcm_close,
@@ -339,6 +349,17 @@ static snd_pcm_uframes_t dmaengine_pcm_pointer(
 	.pointer	= dmaengine_pcm_pointer,
 };
 
+static const struct snd_pcm_ops dmaengine_pcm_ops_with_cpy = {
+	.open		= dmaengine_pcm_open,
+	.close		= snd_dmaengine_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= dmaengine_pcm_hw_params,
+	.hw_free	= snd_pcm_lib_free_pages,
+	.trigger	= snd_dmaengine_pcm_trigger,
+	.pointer	= dmaengine_pcm_pointer,
+	.copy		= dmaengine_pcm_copy,
+};
+
 static const struct snd_soc_platform_driver dmaengine_pcm_platform = {
 	.component_driver = {
 		.probe_order = SND_SOC_COMP_ORDER_LATE,
@@ -347,6 +368,14 @@ static snd_pcm_uframes_t dmaengine_pcm_pointer(
 	.pcm_new	= dmaengine_pcm_new,
 };
 
+static const struct snd_soc_platform_driver dmaengine_pcm_platform_with_cpy = {
+	.component_driver = {
+		.probe_order = SND_SOC_COMP_ORDER_LATE,
+	},
+	.ops		= &dmaengine_pcm_ops_with_cpy,
+	.pcm_new	= dmaengine_pcm_new,
+};
+
 static const char * const dmaengine_pcm_dma_channel_names[] = {
 	[SNDRV_PCM_STREAM_PLAYBACK] = "tx",
 	[SNDRV_PCM_STREAM_CAPTURE] = "rx",
@@ -439,8 +468,12 @@ int snd_dmaengine_pcm_register(struct device *dev,
 	if (ret)
 		goto err_free_dma;
 
-	ret = snd_soc_add_platform(dev, &pcm->platform,
-		&dmaengine_pcm_platform);
+	if (config && config->copy)
+		ret = snd_soc_add_platform(dev, &pcm->platform,
+					   &dmaengine_pcm_platform_with_cpy);
+	else
+		ret = snd_soc_add_platform(dev, &pcm->platform,
+					   &dmaengine_pcm_platform);
 	if (ret)
 		goto err_free_dma;
 
-- 
1.9.1

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

* [PATCH 5/7] ASoC: dmaengine_pcm: add copy support
@ 2017-01-23 16:32   ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-23 16:32 UTC (permalink / raw)
  To: devicetree, linux-arm-kernel, linux-iio, alsa-devel, Lee Jones,
	Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre Torgue, arnaud.pouliquen

From: olivier moysan <olivier.moysan@st.com>

Add copy support in pcm damengine operations.
This allows to pre/post process samples (apply shift, mask ...)
for playback/capture to support hardware contrains.

Signed-off-by: olivier moysan <omoysan.stm32@gmail.com>
Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 include/sound/dmaengine_pcm.h         |  3 +++
 sound/soc/soc-generic-dmaengine-pcm.c | 37 +++++++++++++++++++++++++++++++++--
 2 files changed, 38 insertions(+), 2 deletions(-)

diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h
index 67be244..9d7bce8 100644
--- a/include/sound/dmaengine_pcm.h
+++ b/include/sound/dmaengine_pcm.h
@@ -137,6 +137,9 @@ struct snd_dmaengine_pcm_config {
 	int (*prepare_slave_config)(struct snd_pcm_substream *substream,
 			struct snd_pcm_hw_params *params,
 			struct dma_slave_config *slave_config);
+	int (*copy)(struct snd_pcm_substream *substream, int channel,
+		    snd_pcm_uframes_t pos,
+		    void __user *buf, snd_pcm_uframes_t count);
 	struct dma_chan *(*compat_request_channel)(
 			struct snd_soc_pcm_runtime *rtd,
 			struct snd_pcm_substream *substream);
diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c
index 6cef397..bd8332ce 100644
--- a/sound/soc/soc-generic-dmaengine-pcm.c
+++ b/sound/soc/soc-generic-dmaengine-pcm.c
@@ -329,6 +329,16 @@ static snd_pcm_uframes_t dmaengine_pcm_pointer(
 		return snd_dmaengine_pcm_pointer(substream);
 }
 
+int dmaengine_pcm_copy(struct snd_pcm_substream *substream, int channel,
+		       snd_pcm_uframes_t pos, void __user *buf,
+		       snd_pcm_uframes_t count)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
+
+	return pcm->config->copy(substream, channel, pos, buf, count);
+}
+
 static const struct snd_pcm_ops dmaengine_pcm_ops = {
 	.open		= dmaengine_pcm_open,
 	.close		= snd_dmaengine_pcm_close,
@@ -339,6 +349,17 @@ static snd_pcm_uframes_t dmaengine_pcm_pointer(
 	.pointer	= dmaengine_pcm_pointer,
 };
 
+static const struct snd_pcm_ops dmaengine_pcm_ops_with_cpy = {
+	.open		= dmaengine_pcm_open,
+	.close		= snd_dmaengine_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= dmaengine_pcm_hw_params,
+	.hw_free	= snd_pcm_lib_free_pages,
+	.trigger	= snd_dmaengine_pcm_trigger,
+	.pointer	= dmaengine_pcm_pointer,
+	.copy		= dmaengine_pcm_copy,
+};
+
 static const struct snd_soc_platform_driver dmaengine_pcm_platform = {
 	.component_driver = {
 		.probe_order = SND_SOC_COMP_ORDER_LATE,
@@ -347,6 +368,14 @@ static snd_pcm_uframes_t dmaengine_pcm_pointer(
 	.pcm_new	= dmaengine_pcm_new,
 };
 
+static const struct snd_soc_platform_driver dmaengine_pcm_platform_with_cpy = {
+	.component_driver = {
+		.probe_order = SND_SOC_COMP_ORDER_LATE,
+	},
+	.ops		= &dmaengine_pcm_ops_with_cpy,
+	.pcm_new	= dmaengine_pcm_new,
+};
+
 static const char * const dmaengine_pcm_dma_channel_names[] = {
 	[SNDRV_PCM_STREAM_PLAYBACK] = "tx",
 	[SNDRV_PCM_STREAM_CAPTURE] = "rx",
@@ -439,8 +468,12 @@ int snd_dmaengine_pcm_register(struct device *dev,
 	if (ret)
 		goto err_free_dma;
 
-	ret = snd_soc_add_platform(dev, &pcm->platform,
-		&dmaengine_pcm_platform);
+	if (config && config->copy)
+		ret = snd_soc_add_platform(dev, &pcm->platform,
+					   &dmaengine_pcm_platform_with_cpy);
+	else
+		ret = snd_soc_add_platform(dev, &pcm->platform,
+					   &dmaengine_pcm_platform);
 	if (ret)
 		goto err_free_dma;
 
-- 
1.9.1


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

* [PATCH 5/7] ASoC: dmaengine_pcm: add copy support
@ 2017-01-23 16:32   ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-23 16:32 UTC (permalink / raw)
  To: linux-arm-kernel

From: olivier moysan <olivier.moysan@st.com>

Add copy support in pcm damengine operations.
This allows to pre/post process samples (apply shift, mask ...)
for playback/capture to support hardware contrains.

Signed-off-by: olivier moysan <omoysan.stm32@gmail.com>
Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 include/sound/dmaengine_pcm.h         |  3 +++
 sound/soc/soc-generic-dmaengine-pcm.c | 37 +++++++++++++++++++++++++++++++++--
 2 files changed, 38 insertions(+), 2 deletions(-)

diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h
index 67be244..9d7bce8 100644
--- a/include/sound/dmaengine_pcm.h
+++ b/include/sound/dmaengine_pcm.h
@@ -137,6 +137,9 @@ struct snd_dmaengine_pcm_config {
 	int (*prepare_slave_config)(struct snd_pcm_substream *substream,
 			struct snd_pcm_hw_params *params,
 			struct dma_slave_config *slave_config);
+	int (*copy)(struct snd_pcm_substream *substream, int channel,
+		    snd_pcm_uframes_t pos,
+		    void __user *buf, snd_pcm_uframes_t count);
 	struct dma_chan *(*compat_request_channel)(
 			struct snd_soc_pcm_runtime *rtd,
 			struct snd_pcm_substream *substream);
diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c
index 6cef397..bd8332ce 100644
--- a/sound/soc/soc-generic-dmaengine-pcm.c
+++ b/sound/soc/soc-generic-dmaengine-pcm.c
@@ -329,6 +329,16 @@ static snd_pcm_uframes_t dmaengine_pcm_pointer(
 		return snd_dmaengine_pcm_pointer(substream);
 }
 
+int dmaengine_pcm_copy(struct snd_pcm_substream *substream, int channel,
+		       snd_pcm_uframes_t pos, void __user *buf,
+		       snd_pcm_uframes_t count)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
+
+	return pcm->config->copy(substream, channel, pos, buf, count);
+}
+
 static const struct snd_pcm_ops dmaengine_pcm_ops = {
 	.open		= dmaengine_pcm_open,
 	.close		= snd_dmaengine_pcm_close,
@@ -339,6 +349,17 @@ static snd_pcm_uframes_t dmaengine_pcm_pointer(
 	.pointer	= dmaengine_pcm_pointer,
 };
 
+static const struct snd_pcm_ops dmaengine_pcm_ops_with_cpy = {
+	.open		= dmaengine_pcm_open,
+	.close		= snd_dmaengine_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= dmaengine_pcm_hw_params,
+	.hw_free	= snd_pcm_lib_free_pages,
+	.trigger	= snd_dmaengine_pcm_trigger,
+	.pointer	= dmaengine_pcm_pointer,
+	.copy		= dmaengine_pcm_copy,
+};
+
 static const struct snd_soc_platform_driver dmaengine_pcm_platform = {
 	.component_driver = {
 		.probe_order = SND_SOC_COMP_ORDER_LATE,
@@ -347,6 +368,14 @@ static snd_pcm_uframes_t dmaengine_pcm_pointer(
 	.pcm_new	= dmaengine_pcm_new,
 };
 
+static const struct snd_soc_platform_driver dmaengine_pcm_platform_with_cpy = {
+	.component_driver = {
+		.probe_order = SND_SOC_COMP_ORDER_LATE,
+	},
+	.ops		= &dmaengine_pcm_ops_with_cpy,
+	.pcm_new	= dmaengine_pcm_new,
+};
+
 static const char * const dmaengine_pcm_dma_channel_names[] = {
 	[SNDRV_PCM_STREAM_PLAYBACK] = "tx",
 	[SNDRV_PCM_STREAM_CAPTURE] = "rx",
@@ -439,8 +468,12 @@ int snd_dmaengine_pcm_register(struct device *dev,
 	if (ret)
 		goto err_free_dma;
 
-	ret = snd_soc_add_platform(dev, &pcm->platform,
-		&dmaengine_pcm_platform);
+	if (config && config->copy)
+		ret = snd_soc_add_platform(dev, &pcm->platform,
+					   &dmaengine_pcm_platform_with_cpy);
+	else
+		ret = snd_soc_add_platform(dev, &pcm->platform,
+					   &dmaengine_pcm_platform);
 	if (ret)
 		goto err_free_dma;
 
-- 
1.9.1

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

* [PATCH 6/7] ASoC: add bindings for STM32 DFSDM driver
  2017-01-23 16:32 ` Arnaud Pouliquen
  (?)
@ 2017-01-23 16:32     ` Arnaud Pouliquen
  -1 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-23 16:32 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Lee Jones, Rob Herring,
	Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre Torgue, arnaud.pouliquen-qxv4g6HH51o

This patch adds documentation of device tree bindings for the
STM32 DFSDM ASoC driver.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
---
 .../devicetree/bindings/sound/st,sm32-adfsdm.txt   | 84 ++++++++++++++++++++++
 1 file changed, 84 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt

diff --git a/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt b/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
new file mode 100644
index 0000000..a1d27b8
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
@@ -0,0 +1,84 @@
+STMicroelectronics STM32 ADFSDM ASoC DAI device driver.
+
+STM32 ADFSDM ASoC is a sigma delta audio interface for digital microphone.
+It has to be declared in device-tree as a subnode of the DFSDM mfd node.
+
+It offers possibility to record several mono microphones, with an option to
+synchronize all microphones on a main one (that must be piped to filter 0)
+Audio interface can be exposed through the generic ASoC simple card.
+
+2 Dmics can be connected on one SPI interface instance n.
+Convention is that the DMIC that strobes data on rising edge is connected to the
+corresponding DFSDM channel n; while the Dmic that strobes data on falling edge
+is connected to the channel n-1. Simple card property "bitclock-inversion = <1>"
+is used to specify that microphone strobes data on falling edge.
+
+SPI interface allows to be scheduled by an external SPI clock. To use it
+simple card properties "bitclock-master = <&codec>" and "system-clock-frequency"
+have to be defined in dai-link node.
+
+Required properties:
+- compatible: 	Must be "st,stm32-dfsdm-audio",
+- reg:		Specifies the DFSDM filter instance.
+- interrupts: 	DFSDM filter instance interrupt line.
+- dma:		DMA controller phandle and DMA request line associated to the
+		filter instance ( specified by the field "reg")
+- dma-names: 	must be "rx"
+
+- st,input-id:		Id of the SPI/Manchester interface used.
+- st,dai-filter-order:  SinC filter order from 0 to 5.
+			0: FastSinC
+			[1-5]: order 1 to 5.
+			For audio purpose it is recommended to use order 3 to 5.
+
+Optional properties:
+ - st,dai0-synchronized: Set to 1 to synchronize DAI with DFSDM instance 0.
+
+Exemple of a card with 2 Dmics synchronized and connected on SPI interface 1.
+
+	dfsdm: dfsdm@4400D000 {
+		dai_dfsdm0: dfsdm-audio@0 {
+			compatible = "st,stm32-dfsdm-audio";
+			#sound-dai-cells = <0>;
+			reg = <0>;
+			dmas = <&dmamux1 101 0x400 0x00>;
+			dma-names = "rx";
+			st,input-id = <0>;
+			st,dai-filter-order = <5>;
+		};
+		dai_dfsdm1: dfsdm-audio@1 {
+			compatible = "st,stm32-dfsdm-audio";
+			#sound-dai-cells = <0>;
+			reg = <0>;
+			dmas = <&dmamux1 102 0x400 0x00>;
+			dma-names = "rx";
+			st,input-id = <0>;
+			st,dai0-synchronized = <1>;
+			st,dai-filter-order = <5>;
+		};
+	};
+	sound_dfsdm_pdm {
+		compatible = "simple-audio-card";
+		simple-audio-card,name = "dfsdm_pdm";
+		status = "okay";
+
+		dfsdm0_mic0: simple-audio-card,dai-link@0 {
+			format = "pdm";
+			cpu {
+				sound-dai = <&dai_dfsdm0>;
+			};
+			dmic0_codec: codec {
+				sound-dai = <&dmic0>;
+			};
+		};
+		dfsdm0_mic1: simple-audio-card,dai-link@1 {
+			format = "pdm";
+			bitclock-inversion = <1>;
+			cpu {
+				sound-dai = <&dai_dfsdm1>;
+			};
+			codec {
+				sound-dai = <&dmic1>;
+			};
+		};
+	};
-- 
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] 130+ messages in thread

* [PATCH 6/7] ASoC: add bindings for STM32 DFSDM driver
@ 2017-01-23 16:32     ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-23 16:32 UTC (permalink / raw)
  To: devicetree, linux-arm-kernel, linux-iio, alsa-devel, Lee Jones,
	Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre Torgue, arnaud.pouliquen

This patch adds documentation of device tree bindings for the
STM32 DFSDM ASoC driver.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 .../devicetree/bindings/sound/st,sm32-adfsdm.txt   | 84 ++++++++++++++++++++++
 1 file changed, 84 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt

diff --git a/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt b/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
new file mode 100644
index 0000000..a1d27b8
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
@@ -0,0 +1,84 @@
+STMicroelectronics STM32 ADFSDM ASoC DAI device driver.
+
+STM32 ADFSDM ASoC is a sigma delta audio interface for digital microphone.
+It has to be declared in device-tree as a subnode of the DFSDM mfd node.
+
+It offers possibility to record several mono microphones, with an option to
+synchronize all microphones on a main one (that must be piped to filter 0)
+Audio interface can be exposed through the generic ASoC simple card.
+
+2 Dmics can be connected on one SPI interface instance n.
+Convention is that the DMIC that strobes data on rising edge is connected to the
+corresponding DFSDM channel n; while the Dmic that strobes data on falling edge
+is connected to the channel n-1. Simple card property "bitclock-inversion = <1>"
+is used to specify that microphone strobes data on falling edge.
+
+SPI interface allows to be scheduled by an external SPI clock. To use it
+simple card properties "bitclock-master = <&codec>" and "system-clock-frequency"
+have to be defined in dai-link node.
+
+Required properties:
+- compatible: 	Must be "st,stm32-dfsdm-audio",
+- reg:		Specifies the DFSDM filter instance.
+- interrupts: 	DFSDM filter instance interrupt line.
+- dma:		DMA controller phandle and DMA request line associated to the
+		filter instance ( specified by the field "reg")
+- dma-names: 	must be "rx"
+
+- st,input-id:		Id of the SPI/Manchester interface used.
+- st,dai-filter-order:  SinC filter order from 0 to 5.
+			0: FastSinC
+			[1-5]: order 1 to 5.
+			For audio purpose it is recommended to use order 3 to 5.
+
+Optional properties:
+ - st,dai0-synchronized: Set to 1 to synchronize DAI with DFSDM instance 0.
+
+Exemple of a card with 2 Dmics synchronized and connected on SPI interface 1.
+
+	dfsdm: dfsdm@4400D000 {
+		dai_dfsdm0: dfsdm-audio@0 {
+			compatible = "st,stm32-dfsdm-audio";
+			#sound-dai-cells = <0>;
+			reg = <0>;
+			dmas = <&dmamux1 101 0x400 0x00>;
+			dma-names = "rx";
+			st,input-id = <0>;
+			st,dai-filter-order = <5>;
+		};
+		dai_dfsdm1: dfsdm-audio@1 {
+			compatible = "st,stm32-dfsdm-audio";
+			#sound-dai-cells = <0>;
+			reg = <0>;
+			dmas = <&dmamux1 102 0x400 0x00>;
+			dma-names = "rx";
+			st,input-id = <0>;
+			st,dai0-synchronized = <1>;
+			st,dai-filter-order = <5>;
+		};
+	};
+	sound_dfsdm_pdm {
+		compatible = "simple-audio-card";
+		simple-audio-card,name = "dfsdm_pdm";
+		status = "okay";
+
+		dfsdm0_mic0: simple-audio-card,dai-link@0 {
+			format = "pdm";
+			cpu {
+				sound-dai = <&dai_dfsdm0>;
+			};
+			dmic0_codec: codec {
+				sound-dai = <&dmic0>;
+			};
+		};
+		dfsdm0_mic1: simple-audio-card,dai-link@1 {
+			format = "pdm";
+			bitclock-inversion = <1>;
+			cpu {
+				sound-dai = <&dai_dfsdm1>;
+			};
+			codec {
+				sound-dai = <&dmic1>;
+			};
+		};
+	};
-- 
1.9.1


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

* [PATCH 6/7] ASoC: add bindings for STM32 DFSDM driver
@ 2017-01-23 16:32     ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-23 16:32 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds documentation of device tree bindings for the
STM32 DFSDM ASoC driver.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 .../devicetree/bindings/sound/st,sm32-adfsdm.txt   | 84 ++++++++++++++++++++++
 1 file changed, 84 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt

diff --git a/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt b/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
new file mode 100644
index 0000000..a1d27b8
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
@@ -0,0 +1,84 @@
+STMicroelectronics STM32 ADFSDM ASoC DAI device driver.
+
+STM32 ADFSDM ASoC is a sigma delta audio interface for digital microphone.
+It has to be declared in device-tree as a subnode of the DFSDM mfd node.
+
+It offers possibility to record several mono microphones, with an option to
+synchronize all microphones on a main one (that must be piped to filter 0)
+Audio interface can be exposed through the generic ASoC simple card.
+
+2 Dmics can be connected on one SPI interface instance n.
+Convention is that the DMIC that strobes data on rising edge is connected to the
+corresponding DFSDM channel n; while the Dmic that strobes data on falling edge
+is connected to the channel n-1. Simple card property "bitclock-inversion = <1>"
+is used to specify that microphone strobes data on falling edge.
+
+SPI interface allows to be scheduled by an external SPI clock. To use it
+simple card properties "bitclock-master = <&codec>" and "system-clock-frequency"
+have to be defined in dai-link node.
+
+Required properties:
+- compatible: 	Must be "st,stm32-dfsdm-audio",
+- reg:		Specifies the DFSDM filter instance.
+- interrupts: 	DFSDM filter instance interrupt line.
+- dma:		DMA controller phandle and DMA request line associated to the
+		filter instance ( specified by the field "reg")
+- dma-names: 	must be "rx"
+
+- st,input-id:		Id of the SPI/Manchester interface used.
+- st,dai-filter-order:  SinC filter order from 0 to 5.
+			0: FastSinC
+			[1-5]: order 1 to 5.
+			For audio purpose it is recommended to use order 3 to 5.
+
+Optional properties:
+ - st,dai0-synchronized: Set to 1 to synchronize DAI with DFSDM instance 0.
+
+Exemple of a card with 2 Dmics synchronized and connected on SPI interface 1.
+
+	dfsdm: dfsdm at 4400D000 {
+		dai_dfsdm0: dfsdm-audio at 0 {
+			compatible = "st,stm32-dfsdm-audio";
+			#sound-dai-cells = <0>;
+			reg = <0>;
+			dmas = <&dmamux1 101 0x400 0x00>;
+			dma-names = "rx";
+			st,input-id = <0>;
+			st,dai-filter-order = <5>;
+		};
+		dai_dfsdm1: dfsdm-audio at 1 {
+			compatible = "st,stm32-dfsdm-audio";
+			#sound-dai-cells = <0>;
+			reg = <0>;
+			dmas = <&dmamux1 102 0x400 0x00>;
+			dma-names = "rx";
+			st,input-id = <0>;
+			st,dai0-synchronized = <1>;
+			st,dai-filter-order = <5>;
+		};
+	};
+	sound_dfsdm_pdm {
+		compatible = "simple-audio-card";
+		simple-audio-card,name = "dfsdm_pdm";
+		status = "okay";
+
+		dfsdm0_mic0: simple-audio-card,dai-link at 0 {
+			format = "pdm";
+			cpu {
+				sound-dai = <&dai_dfsdm0>;
+			};
+			dmic0_codec: codec {
+				sound-dai = <&dmic0>;
+			};
+		};
+		dfsdm0_mic1: simple-audio-card,dai-link at 1 {
+			format = "pdm";
+			bitclock-inversion = <1>;
+			cpu {
+				sound-dai = <&dai_dfsdm1>;
+			};
+			codec {
+				sound-dai = <&dmic1>;
+			};
+		};
+	};
-- 
1.9.1

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

* [PATCH 7/7] ASoC: add STM32 DFSDM support
  2017-01-23 16:32 ` Arnaud Pouliquen
  (?)
@ 2017-01-23 16:32     ` Arnaud Pouliquen
  -1 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-23 16:32 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Lee Jones, Rob Herring,
	Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre Torgue, arnaud.pouliquen-qxv4g6HH51o

Add driver to handle PDM microphones connected to DFSDM IP.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
---
 sound/soc/Kconfig            |   1 +
 sound/soc/Makefile           |   1 +
 sound/soc/stm/Kconfig        |  10 +
 sound/soc/stm/Makefile       |   2 +
 sound/soc/stm/stm32_adfsdm.c | 686 +++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 700 insertions(+)
 create mode 100644 sound/soc/stm/Kconfig
 create mode 100644 sound/soc/stm/Makefile
 create mode 100644 sound/soc/stm/stm32_adfsdm.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..79aee4e
--- /dev/null
+++ b/sound/soc/stm/Kconfig
@@ -0,0 +1,10 @@
+menuconfig SND_SOC_STM32_DFSDM
+	tristate "SoC Audio support for STM32 DFSDM"
+	depends on (ARCH_STM32 && OF && MFD_STM32_DFSDM) || COMPILE_TEST
+	depends on SND_SOC
+	select SND_SOC_GENERIC_DMAENGINE_PCM
+	select SND_SOC_DMIC
+	help
+	  Select this option to enable the STM32 Digital Filter
+	  for Sigma Delta Modulators (DFSDM) driver used
+	  in various STM32 series for digital microphone capture.
\ No newline at end of file
diff --git a/sound/soc/stm/Makefile b/sound/soc/stm/Makefile
new file mode 100644
index 0000000..ea90240
--- /dev/null
+++ b/sound/soc/stm/Makefile
@@ -0,0 +1,2 @@
+#DFSDM
+obj-$(CONFIG_SND_SOC_STM32_DFSDM) += stm32_adfsdm.o
diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c
new file mode 100644
index 0000000..9d34bb7
--- /dev/null
+++ b/sound/soc/stm/stm32_adfsdm.c
@@ -0,0 +1,686 @@
+/*
+ * This file is part of STM32 DFSDM ASoC DAI driver
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Authors: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
+ *          Olivier Moysan <olivier.moysan-qxv4g6HH51o@public.gmane.org>
+ *
+ * License type: GPLv2
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <sound/dmaengine_pcm.h>
+
+#include <linux/mfd/stm32-dfsdm.h>
+
+/*
+ * Set data output resolution to 23 bits max to keep 1 extra bit for sign,
+ * as filter output is symmetric +/-2^(n-1).
+ */
+#define STM32_ADFSDM_DATA_RES BIT(23)
+#define STM32_ADFSDM_MAX_RES BIT(31)
+#define STM32_ADFSDM_DATAR_DATA_MASK	GENMASK(31, 8)
+
+struct stm32_adfsdm_data {
+	unsigned int rate;	/* SNDRV_PCM_RATE value */
+	unsigned int freq;	/* frequency in Hz */
+	unsigned int fosr;	/* filter over sampling ratio */
+	unsigned int iosr;	/* integrator over sampling ratio */
+	unsigned int fast;	/* filter fast mode */
+	unsigned long res;	/* output data resolution */
+	int shift;		/* shift on data output */
+	bool h_res_found;	/* preferred resolution higher than expected */
+};
+
+static const struct stm32_adfsdm_data stm32_dfsdm_filter[] = {
+	{ .rate = SNDRV_PCM_RATE_8000,  .freq = 8000 },
+	{ .rate = SNDRV_PCM_RATE_16000, .freq = 16000 },
+	{ .rate = SNDRV_PCM_RATE_32000, .freq = 32000 },
+};
+
+static const unsigned int stm32_dfsdm_sr_val[] = {
+	8000,
+	16000,
+	32000,
+};
+
+struct stm32_adfsdm_priv {
+	struct snd_soc_dai_driver dai;
+	struct snd_dmaengine_dai_dma_data dma_data;
+	struct snd_pcm_substream *substream;
+	struct stm32_dfsdm_sinc_filter fl;
+	struct stm32_dfsdm_channel channel;
+	struct stm32_dfsdm_ch_cfg ch_cfg;
+	struct stm32_dfsdm *dfsdm;
+	struct stm32_adfsdm_data *f_param;
+	struct device *dev;
+	struct snd_pcm_hw_constraint_list rates_const;
+	unsigned long dmic_clk;
+	unsigned int input_id;
+	unsigned int fl_id;
+	unsigned int order; /* filter order */
+	int synchro;
+};
+
+static const struct snd_pcm_hardware stm32_adfsdm_pcm_hw = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+	    SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP |
+	    SNDRV_PCM_INFO_MMAP_VALID,
+	.formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+
+	.rate_min = 8000,
+	.rate_max = 48000,
+
+	.channels_min = 1,
+	.channels_max = 1,
+
+	.periods_min = 2,
+	.periods_max = 48,
+
+	.period_bytes_min = 40, /* 8 khz 5 ms */
+	.period_bytes_max = 4 * PAGE_SIZE,
+	.buffer_bytes_max = 16 * PAGE_SIZE
+};
+
+static inline void stm32_adfsdm_get_param(struct stm32_adfsdm_priv *priv,
+					  unsigned int rate,
+					  struct stm32_adfsdm_data **fparam)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(stm32_dfsdm_filter); i++) {
+		if (rate == priv->f_param[i].freq) {
+			*fparam = &priv->f_param[i];
+			break;
+		}
+	}
+}
+
+static int stm32_adfsdm_compute_shift(struct stm32_adfsdm_priv *priv,
+				      struct stm32_adfsdm_data *param)
+{
+	int shift = 0;
+	u32 r = param->res;
+
+	if (!r) {
+		dev_err(priv->dev, "%s: resolution undefined\n", __func__);
+		return -EINVAL;
+	}
+
+	/*
+	 * If filter resolution is higher than data output resolution
+	 * compute right shift required to match data resolution.
+	 * Otherwise compute left shift to align MSB on data resolution.
+	 */
+	if (r >= STM32_ADFSDM_DATA_RES)
+		while ((r >> -shift) >= STM32_ADFSDM_DATA_RES)
+			shift--;
+	else
+		while ((r << shift) < STM32_ADFSDM_DATA_RES)
+			shift++;
+
+	param->shift = shift;
+	dev_dbg(priv->dev, "%s: output shift: %d\n", __func__, shift);
+
+	return 0;
+}
+
+static int stm32_adfsdm_get_best_osr(struct stm32_adfsdm_priv *priv,
+				     unsigned int decim, bool fast,
+				     struct stm32_adfsdm_data *param)
+{
+	unsigned int i, d, fosr, iosr;
+	u64 res;
+	s64 delta;
+	unsigned int m = 1;	/* multiplication factor */
+	unsigned int p = priv->order;	/* filter order (ford) */
+
+	/*
+	 * Decimation d depends on the filter order and the oversampling ratios.
+	 * ford: filter order
+	 * fosr: filter over sampling ratio
+	 * iosr: integrator over sampling ratio
+	 */
+	dev_dbg(priv->dev, "%s: decim = %d fast = %d\n", __func__, decim, fast);
+	if (priv->order == DFSDM_FASTSINC_ORDER) {
+		m = 2;
+		p = 2;
+	}
+
+	/*
+	 * Looks for filter and integrator oversampling ratios which allow
+	 * to reach 24 bits data output resolution.
+	 * Leave at once if exact resolution if reached.
+	 * Otherwise the higher resolution below 32 bits is kept.
+	 */
+	for (fosr = 1; fosr <= DFSDM_MAX_FL_OVERSAMPLING; fosr++) {
+		for (iosr = 1; iosr <= DFSDM_MAX_INT_OVERSAMPLING; iosr++) {
+			if (fast)
+				d = fosr * iosr;
+			else if (priv->order == DFSDM_FASTSINC_ORDER)
+				d = fosr * (iosr + 3) + 2;
+			else
+				d = fosr * (iosr - 1 + p) + p;
+
+			if (d > decim)
+				break;
+			else if (d != decim)
+				continue;
+			/*
+			 * Check resolution (limited to signed 32 bits)
+			 *   res <= 2^31
+			 * Sincx filters:
+			 *   res = m * fosr^p x iosr (with m=1, p=ford)
+			 * FastSinc filter
+			 *   res = m * fosr^p x iosr (with m=2, p=2)
+			 */
+			res = fosr;
+			for (i = p - 1; i > 0; i--) {
+				res = res * (u64)fosr;
+				if (res > STM32_ADFSDM_MAX_RES)
+					break;
+			}
+			if (res > STM32_ADFSDM_MAX_RES)
+				continue;
+			res = res * (u64)m * (u64)iosr;
+			if (res > STM32_ADFSDM_MAX_RES)
+				continue;
+
+			delta = res - STM32_ADFSDM_DATA_RES;
+
+			if (res >= param->res) {
+				param->res = res;
+				param->fosr = fosr;
+				param->iosr = iosr;
+				param->fast = fast;
+			}
+
+			if (!delta)
+				return 0;
+		}
+	}
+
+	if (!param->fosr)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int stm32_adfsdm_get_supported_rates(struct stm32_adfsdm_priv *priv,
+					    unsigned int *rates)
+{
+	unsigned long fs = priv->dmic_clk;
+	unsigned int i, decim;
+	int ret;
+
+	*rates = 0;
+
+	for (i = 0; i < ARRAY_SIZE(stm32_dfsdm_filter); i++) {
+		/* check that clkout_freq is compatible */
+		if ((fs % priv->f_param[i].freq) != 0)
+			continue;
+
+		decim = fs / priv->f_param[i].freq;
+
+		/*
+		 * Try to find one solution for filter and integrator
+		 * oversampling ratio with fast mode ON or OFF.
+		 * Fast mode on is the preferred solution.
+		 */
+		ret = stm32_adfsdm_get_best_osr(priv, decim, 0,
+						&priv->f_param[i]);
+		ret &= stm32_adfsdm_get_best_osr(priv, decim, 1,
+						 &priv->f_param[i]);
+		if (!ret) {
+			ret = stm32_adfsdm_compute_shift(priv,
+							 &priv->f_param[i]);
+			if (ret)
+				continue;
+
+			*rates |= 1 << i;
+			dev_dbg(priv->dev, "%s: %d rate supported\n", __func__,
+				priv->f_param[i].freq);
+		}
+	}
+
+	if (!*rates) {
+		dev_err(priv->dev, "%s: no matched rate found\n", __func__);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void stm32_dfsdm_xrun(struct stm32_dfsdm *dfsdm, int flt_id,
+			     enum stm32_dfsdm_events ev, unsigned int param,
+			     void *context)
+{
+	struct stm32_adfsdm_priv *priv = context;
+
+	snd_pcm_stream_lock(priv->substream);
+	dev_err(priv->dev, "%s:unexpected underrun\n", __func__);
+	/* Stop the player */
+	stm32_dfsdm_unregister_fl_event(priv->dfsdm, priv->fl_id,
+					DFSDM_EVENT_REG_XRUN, 0);
+	snd_pcm_stop(priv->substream, SNDRV_PCM_STATE_XRUN);
+	snd_pcm_stream_unlock(priv->substream);
+}
+
+static int stm32_adfsdm_copy(struct snd_pcm_substream *substream, int channel,
+			     snd_pcm_uframes_t pos,
+			     void __user *buf, snd_pcm_uframes_t count)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+	struct stm32_adfsdm_data *f_param;
+	int *ptr = (int *)(runtime->dma_area + frames_to_bytes(runtime, pos));
+	char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, pos);
+	ssize_t bytes = frames_to_bytes(runtime, count);
+	ssize_t sample_cnt = bytes_to_samples(runtime, bytes);
+
+	stm32_adfsdm_get_param(priv, runtime->rate, &f_param);
+
+	/*
+	 * Audio samples are available on 24 MSBs of the DFSDM DATAR register.
+	 * We need to mask 8 LSB control bits...
+	 * Additionnaly precision depends on decimation and can need shift
+	 * to be aligned on 32-bit word MSB.
+	 */
+	if (f_param->shift > 0) {
+		do {
+			*ptr <<= f_param->shift & STM32_ADFSDM_DATAR_DATA_MASK;
+			ptr++;
+		} while (--sample_cnt);
+	} else {
+		do {
+			*ptr &= STM32_ADFSDM_DATAR_DATA_MASK;
+			ptr++;
+		} while (--sample_cnt);
+	}
+
+	return copy_to_user(buf, hwbuf, bytes);
+}
+
+static int stm32_adfsdm_startup(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+	priv->substream = substream;
+
+	/* Fix available rate depending on CLKOUT or CKIN value */
+	return snd_pcm_hw_constraint_list(substream->runtime, 0,
+					  SNDRV_PCM_HW_PARAM_RATE,
+					  &priv->rates_const);
+}
+
+static void stm32_adfsdm_shutdown(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+	priv->substream = NULL;
+}
+
+static int stm32_adfsdm_dai_hw_params(struct snd_pcm_substream *substream,
+				      struct snd_pcm_hw_params *params,
+				      struct snd_soc_dai *dai)
+{
+	struct snd_dmaengine_dai_dma_data *dma_data;
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+	dma_data = snd_soc_dai_get_dma_data(dai, substream);
+	dma_data->maxburst = 1;
+
+	return 0;
+}
+
+static int stm32_adfsdm_prepare(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct stm32_adfsdm_data *f_param;
+	struct stm32_dfsdm_filter filter;
+	struct stm32_dfsdm_regular params;
+	int ret;
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+
+	stm32_adfsdm_get_param(priv, runtime->rate, &f_param);
+
+	memset(&filter, 0, sizeof(filter));
+	memset(&params, 0, sizeof(params));
+
+	params.ch_src = priv->channel.id;
+	params.dma_mode = 1;
+	params.cont_mode = 1;
+	params.fast_mode = f_param->fast;
+	params.sync_mode = priv->synchro ?
+	    DFSDM_FILTER_RSYNC_ON : DFSDM_FILTER_RSYNC_OFF;
+	filter.reg_params = &params;
+	filter.sinc_params.order = priv->order;
+	filter.sinc_params.oversampling = f_param->fosr;
+	filter.int_oversampling = f_param->iosr;
+
+	filter.event.cb = stm32_dfsdm_xrun;
+	filter.event.context = priv;
+
+	ret = stm32_dfsdm_configure_filter(priv->dfsdm, priv->fl_id, &filter);
+	if (ret < 0)
+		return ret;
+
+	ret = stm32_dfsdm_register_fl_event(priv->dfsdm, priv->fl_id,
+					    DFSDM_EVENT_REG_XRUN, 0);
+	if (ret < 0)
+		dev_err(priv->dev, "Failed to register xrun event\n");
+
+	return ret;
+}
+
+static int stm32_adfsdm_start(struct snd_pcm_substream *substream,
+			      struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct stm32_adfsdm_data *f_param;
+	int ret;
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+
+	stm32_adfsdm_get_param(priv, runtime->rate, &f_param);
+	if (f_param->shift < 0)
+		priv->ch_cfg.right_bit_shift = -f_param->shift;
+
+	ret = stm32_dfsdm_start_channel(priv->dfsdm, priv->channel.id,
+					&priv->ch_cfg);
+	if (ret < 0)
+		return ret;
+
+	stm32_dfsdm_start_filter(priv->dfsdm, priv->fl_id,
+				 DFSDM_FILTER_REG_CONV);
+
+	return 0;
+}
+
+static void stm32_adfsdm_stop(struct snd_pcm_substream *substream,
+			      struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+
+	stm32_dfsdm_unregister_fl_event(priv->dfsdm, priv->fl_id,
+					DFSDM_EVENT_REG_XRUN, 0);
+	stm32_dfsdm_stop_filter(priv->dfsdm, priv->fl_id);
+	stm32_dfsdm_stop_channel(priv->dfsdm, priv->channel.id);
+}
+
+static int stm32_adfsdm_trigger(struct snd_pcm_substream *substream, int cmd,
+				struct snd_soc_dai *dai)
+{
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		return stm32_adfsdm_start(substream, dai);
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_STOP:
+		stm32_adfsdm_stop(substream, dai);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int stm32_adfsdm_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	unsigned int inv = fmt & SND_SOC_DAIFMT_INV_MASK;
+	unsigned int cb = fmt & SND_SOC_DAIFMT_MASTER_MASK;
+	int ret;
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+
+	/* DAI clock strobing */
+	if ((inv == SND_SOC_DAIFMT_IB_NF) || (inv == SND_SOC_DAIFMT_IB_IF)) {
+		priv->channel.serial_if.type = DFSDM_CHANNEL_SPI_FALLING;
+		priv->channel.serial_if.pins = DFSDM_CHANNEL_NEXT_CHANNEL_PINS;
+		/*
+		 * if data on falling egde SPI connected to channel n - 1.
+		 * if data on rising egde  SPI connected to channel n.
+		 */
+		if (priv->input_id)
+			priv->channel.id = priv->input_id - 1;
+		else
+			priv->channel.id = priv->dfsdm->max_channels - 1;
+	} else {
+		priv->channel.serial_if.type = DFSDM_CHANNEL_SPI_RISING;
+		priv->channel.serial_if.pins = DFSDM_CHANNEL_SAME_CHANNEL_PINS;
+		priv->channel.id = priv->input_id;
+	}
+
+	dev_dbg(dai->dev, "%s: channel %d on input %d\n", __func__,
+		priv->channel.id, priv->input_id);
+
+	if ((cb == SND_SOC_DAIFMT_CBS_CFM) || (cb == SND_SOC_DAIFMT_CBS_CFS)) {
+		/* Digital microphone is clocked by CLKOUT */
+		stm32_dfsdm_get_clk_out_rate(priv->dfsdm, &priv->dmic_clk);
+	} else {
+		/* Digital microphone is clocked by external clock */
+		if (!priv->dmic_clk) {
+			dev_err(priv->dev,
+				"system-clock-frequency not defined\n");
+			return -EINVAL;
+		}
+	}
+
+	priv->rates_const.count = ARRAY_SIZE(stm32_dfsdm_sr_val);
+	priv->rates_const.list = stm32_dfsdm_sr_val;
+	ret = stm32_adfsdm_get_supported_rates(priv, &priv->rates_const.mask);
+	if (ret < 0)
+		return ret;
+
+	return stm32_dfsdm_get_channel(priv->dfsdm, &priv->channel);
+}
+
+static int stm32_adfsdm_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+				   unsigned int freq, int dir)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
+	if (dir == SND_SOC_CLOCK_IN)
+		priv->dmic_clk = freq;
+
+	return 0;
+}
+
+static const struct snd_soc_dai_ops stm32_adfsdm_dai_ops = {
+	.startup = stm32_adfsdm_startup,
+	.shutdown = stm32_adfsdm_shutdown,
+	.hw_params = stm32_adfsdm_dai_hw_params,
+	.set_fmt = stm32_adfsdm_set_dai_fmt,
+	.set_sysclk = stm32_adfsdm_set_sysclk,
+	.prepare = stm32_adfsdm_prepare,
+	.trigger = stm32_adfsdm_trigger,
+};
+
+static int stm32_adfsdm_dai_probe(struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	struct snd_dmaengine_dai_dma_data *dma = &priv->dma_data;
+	int ret;
+
+	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
+
+	/* filter settings */
+	ret = stm32_dfsdm_get_filter(priv->dfsdm, priv->fl_id);
+	if (ret < 0)
+		return -EBUSY;
+
+	/* DMA settings */
+	snd_soc_dai_init_dma_data(dai, NULL, dma);
+	dma->addr = stm32_dfsdm_get_filter_dma_phy_addr(priv->dfsdm,
+							priv->fl_id,
+							DFSDM_FILTER_REG_CONV);
+	dma->addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+	return 0;
+}
+
+static int stm32_adfsdm_dai_remove(struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
+
+	stm32_dfsdm_release_filter(priv->dfsdm, priv->fl_id);
+	stm32_dfsdm_release_channel(priv->dfsdm, priv->channel.id);
+
+	return 0;
+}
+
+static const struct snd_soc_dai_driver stm32_adfsdm_dai = {
+	.capture = {
+		    .channels_min = 1,
+		    .channels_max = 1,
+		    .formats = SNDRV_PCM_FMTBIT_S24_LE |
+			       SNDRV_PCM_FMTBIT_S32_LE,
+		    .rates = SNDRV_PCM_RATE_8000_48000,
+		    },
+	.probe = stm32_adfsdm_dai_probe,
+	.remove = stm32_adfsdm_dai_remove,
+	.ops = &stm32_adfsdm_dai_ops,
+};
+
+static const struct snd_soc_component_driver stm32_adfsdm_dai_component = {
+	.name = "sti_cpu_dai",
+};
+
+static const struct snd_dmaengine_pcm_config dmaengine_pcm_config = {
+	.pcm_hardware = &stm32_adfsdm_pcm_hw,
+	.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+	.copy = stm32_adfsdm_copy,
+};
+
+static int stm32_adfsdm_probe(struct platform_device *pdev)
+{
+	struct stm32_adfsdm_priv *priv;
+	struct device_node *np = pdev->dev.of_node;
+	char *_name;
+	int ret;
+
+	dev_dbg(&pdev->dev, "%s: enter for node %s\n", __func__,
+		np->name);
+
+	if (!np) {
+		dev_err(&pdev->dev, "No DT found\n");
+		return -EINVAL;
+	}
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->dev = &pdev->dev;
+	priv->dfsdm = dev_get_drvdata(pdev->dev.parent);
+
+	if (of_property_read_u32(np, "reg", &priv->fl_id)) {
+		dev_err(&pdev->dev, "missing reg property\n");
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32(np, "st,dai-filter-order", &priv->order);
+	if (ret < 0) {
+		dev_warn(&pdev->dev, "Default filter order selected\n");
+		priv->order = DFSDM_SINC5_ORDER;
+	}
+
+	ret = of_property_read_u32(np, "st,input-id", &priv->input_id);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "st,input-id property missing\n");
+		return ret;
+	}
+
+	ret = of_property_read_u32(np, "st,dai0-synchronized", &priv->synchro);
+	if (ret < 0)
+		/* default case if property not defined */
+		priv->synchro = 0;
+
+	priv->channel.type.DataPacking = DFSDM_CHANNEL_STANDARD_MODE;
+	priv->channel.type.source = DFSDM_CHANNEL_EXTERNAL_INPUTS;
+	priv->channel.serial_if.spi_clk = DFSDM_CHANNEL_SPI_CLOCK_INTERNAL;
+
+	/* DAI settings */
+	_name = devm_kzalloc(&pdev->dev, sizeof("dfsdm_pdm_0"), GFP_KERNEL);
+	if (!_name)
+		return -ENOMEM;
+
+	priv->dai = stm32_adfsdm_dai;
+
+	priv->f_param = devm_kcalloc(&pdev->dev,
+				     ARRAY_SIZE(stm32_dfsdm_filter),
+				     sizeof(stm32_dfsdm_filter[0]), GFP_KERNEL);
+	if (!priv->f_param)
+		return -ENOMEM;
+
+	memcpy(priv->f_param, stm32_dfsdm_filter,
+	       ARRAY_SIZE(stm32_dfsdm_filter) * sizeof(stm32_dfsdm_filter[0]));
+
+	snprintf(_name, sizeof("dfsdm_pdm_0"), "dfsdm_pdm_%i", priv->fl_id);
+	priv->dai.name = _name;
+	priv->dai.capture.stream_name = _name;
+
+	dev_set_drvdata(&pdev->dev, priv);
+
+	ret = devm_snd_soc_register_component(&pdev->dev,
+					      &stm32_adfsdm_dai_component,
+					      &priv->dai, 1);
+	if (ret < 0)
+		return ret;
+
+	ret = devm_snd_dmaengine_pcm_register(&pdev->dev,
+					      &dmaengine_pcm_config, 0);
+	if (ret < 0)
+		dev_err(&pdev->dev, "failed to register dma pcm config\n");
+
+	return ret;
+}
+
+static const struct of_device_id snd_soc_dfsdm_match[] = {
+	{.compatible = "st,stm32-dfsdm-audio"},
+	{},
+};
+
+static struct platform_driver stm32_adfsdm_driver = {
+	.driver = {
+		   .name = "stm32-dfsdm-audio",
+		   .of_match_table = snd_soc_dfsdm_match,
+		   },
+	.probe = stm32_adfsdm_probe,
+};
+
+module_platform_driver(stm32_adfsdm_driver);
+
+MODULE_DESCRIPTION("stm32 DFSDM DAI driver");
+MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>");
+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] 130+ messages in thread

* [PATCH 7/7] ASoC: add STM32 DFSDM support
@ 2017-01-23 16:32     ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-23 16:32 UTC (permalink / raw)
  To: devicetree, linux-arm-kernel, linux-iio, alsa-devel, Lee Jones,
	Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre Torgue, arnaud.pouliquen

Add driver to handle PDM microphones connected to DFSDM IP.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 sound/soc/Kconfig            |   1 +
 sound/soc/Makefile           |   1 +
 sound/soc/stm/Kconfig        |  10 +
 sound/soc/stm/Makefile       |   2 +
 sound/soc/stm/stm32_adfsdm.c | 686 +++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 700 insertions(+)
 create mode 100644 sound/soc/stm/Kconfig
 create mode 100644 sound/soc/stm/Makefile
 create mode 100644 sound/soc/stm/stm32_adfsdm.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..79aee4e
--- /dev/null
+++ b/sound/soc/stm/Kconfig
@@ -0,0 +1,10 @@
+menuconfig SND_SOC_STM32_DFSDM
+	tristate "SoC Audio support for STM32 DFSDM"
+	depends on (ARCH_STM32 && OF && MFD_STM32_DFSDM) || COMPILE_TEST
+	depends on SND_SOC
+	select SND_SOC_GENERIC_DMAENGINE_PCM
+	select SND_SOC_DMIC
+	help
+	  Select this option to enable the STM32 Digital Filter
+	  for Sigma Delta Modulators (DFSDM) driver used
+	  in various STM32 series for digital microphone capture.
\ No newline at end of file
diff --git a/sound/soc/stm/Makefile b/sound/soc/stm/Makefile
new file mode 100644
index 0000000..ea90240
--- /dev/null
+++ b/sound/soc/stm/Makefile
@@ -0,0 +1,2 @@
+#DFSDM
+obj-$(CONFIG_SND_SOC_STM32_DFSDM) += stm32_adfsdm.o
diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c
new file mode 100644
index 0000000..9d34bb7
--- /dev/null
+++ b/sound/soc/stm/stm32_adfsdm.c
@@ -0,0 +1,686 @@
+/*
+ * This file is part of STM32 DFSDM ASoC DAI driver
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com>
+ *          Olivier Moysan <olivier.moysan@st.com>
+ *
+ * License type: GPLv2
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <sound/dmaengine_pcm.h>
+
+#include <linux/mfd/stm32-dfsdm.h>
+
+/*
+ * Set data output resolution to 23 bits max to keep 1 extra bit for sign,
+ * as filter output is symmetric +/-2^(n-1).
+ */
+#define STM32_ADFSDM_DATA_RES BIT(23)
+#define STM32_ADFSDM_MAX_RES BIT(31)
+#define STM32_ADFSDM_DATAR_DATA_MASK	GENMASK(31, 8)
+
+struct stm32_adfsdm_data {
+	unsigned int rate;	/* SNDRV_PCM_RATE value */
+	unsigned int freq;	/* frequency in Hz */
+	unsigned int fosr;	/* filter over sampling ratio */
+	unsigned int iosr;	/* integrator over sampling ratio */
+	unsigned int fast;	/* filter fast mode */
+	unsigned long res;	/* output data resolution */
+	int shift;		/* shift on data output */
+	bool h_res_found;	/* preferred resolution higher than expected */
+};
+
+static const struct stm32_adfsdm_data stm32_dfsdm_filter[] = {
+	{ .rate = SNDRV_PCM_RATE_8000,  .freq = 8000 },
+	{ .rate = SNDRV_PCM_RATE_16000, .freq = 16000 },
+	{ .rate = SNDRV_PCM_RATE_32000, .freq = 32000 },
+};
+
+static const unsigned int stm32_dfsdm_sr_val[] = {
+	8000,
+	16000,
+	32000,
+};
+
+struct stm32_adfsdm_priv {
+	struct snd_soc_dai_driver dai;
+	struct snd_dmaengine_dai_dma_data dma_data;
+	struct snd_pcm_substream *substream;
+	struct stm32_dfsdm_sinc_filter fl;
+	struct stm32_dfsdm_channel channel;
+	struct stm32_dfsdm_ch_cfg ch_cfg;
+	struct stm32_dfsdm *dfsdm;
+	struct stm32_adfsdm_data *f_param;
+	struct device *dev;
+	struct snd_pcm_hw_constraint_list rates_const;
+	unsigned long dmic_clk;
+	unsigned int input_id;
+	unsigned int fl_id;
+	unsigned int order; /* filter order */
+	int synchro;
+};
+
+static const struct snd_pcm_hardware stm32_adfsdm_pcm_hw = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+	    SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP |
+	    SNDRV_PCM_INFO_MMAP_VALID,
+	.formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+
+	.rate_min = 8000,
+	.rate_max = 48000,
+
+	.channels_min = 1,
+	.channels_max = 1,
+
+	.periods_min = 2,
+	.periods_max = 48,
+
+	.period_bytes_min = 40, /* 8 khz 5 ms */
+	.period_bytes_max = 4 * PAGE_SIZE,
+	.buffer_bytes_max = 16 * PAGE_SIZE
+};
+
+static inline void stm32_adfsdm_get_param(struct stm32_adfsdm_priv *priv,
+					  unsigned int rate,
+					  struct stm32_adfsdm_data **fparam)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(stm32_dfsdm_filter); i++) {
+		if (rate == priv->f_param[i].freq) {
+			*fparam = &priv->f_param[i];
+			break;
+		}
+	}
+}
+
+static int stm32_adfsdm_compute_shift(struct stm32_adfsdm_priv *priv,
+				      struct stm32_adfsdm_data *param)
+{
+	int shift = 0;
+	u32 r = param->res;
+
+	if (!r) {
+		dev_err(priv->dev, "%s: resolution undefined\n", __func__);
+		return -EINVAL;
+	}
+
+	/*
+	 * If filter resolution is higher than data output resolution
+	 * compute right shift required to match data resolution.
+	 * Otherwise compute left shift to align MSB on data resolution.
+	 */
+	if (r >= STM32_ADFSDM_DATA_RES)
+		while ((r >> -shift) >= STM32_ADFSDM_DATA_RES)
+			shift--;
+	else
+		while ((r << shift) < STM32_ADFSDM_DATA_RES)
+			shift++;
+
+	param->shift = shift;
+	dev_dbg(priv->dev, "%s: output shift: %d\n", __func__, shift);
+
+	return 0;
+}
+
+static int stm32_adfsdm_get_best_osr(struct stm32_adfsdm_priv *priv,
+				     unsigned int decim, bool fast,
+				     struct stm32_adfsdm_data *param)
+{
+	unsigned int i, d, fosr, iosr;
+	u64 res;
+	s64 delta;
+	unsigned int m = 1;	/* multiplication factor */
+	unsigned int p = priv->order;	/* filter order (ford) */
+
+	/*
+	 * Decimation d depends on the filter order and the oversampling ratios.
+	 * ford: filter order
+	 * fosr: filter over sampling ratio
+	 * iosr: integrator over sampling ratio
+	 */
+	dev_dbg(priv->dev, "%s: decim = %d fast = %d\n", __func__, decim, fast);
+	if (priv->order == DFSDM_FASTSINC_ORDER) {
+		m = 2;
+		p = 2;
+	}
+
+	/*
+	 * Looks for filter and integrator oversampling ratios which allow
+	 * to reach 24 bits data output resolution.
+	 * Leave at once if exact resolution if reached.
+	 * Otherwise the higher resolution below 32 bits is kept.
+	 */
+	for (fosr = 1; fosr <= DFSDM_MAX_FL_OVERSAMPLING; fosr++) {
+		for (iosr = 1; iosr <= DFSDM_MAX_INT_OVERSAMPLING; iosr++) {
+			if (fast)
+				d = fosr * iosr;
+			else if (priv->order == DFSDM_FASTSINC_ORDER)
+				d = fosr * (iosr + 3) + 2;
+			else
+				d = fosr * (iosr - 1 + p) + p;
+
+			if (d > decim)
+				break;
+			else if (d != decim)
+				continue;
+			/*
+			 * Check resolution (limited to signed 32 bits)
+			 *   res <= 2^31
+			 * Sincx filters:
+			 *   res = m * fosr^p x iosr (with m=1, p=ford)
+			 * FastSinc filter
+			 *   res = m * fosr^p x iosr (with m=2, p=2)
+			 */
+			res = fosr;
+			for (i = p - 1; i > 0; i--) {
+				res = res * (u64)fosr;
+				if (res > STM32_ADFSDM_MAX_RES)
+					break;
+			}
+			if (res > STM32_ADFSDM_MAX_RES)
+				continue;
+			res = res * (u64)m * (u64)iosr;
+			if (res > STM32_ADFSDM_MAX_RES)
+				continue;
+
+			delta = res - STM32_ADFSDM_DATA_RES;
+
+			if (res >= param->res) {
+				param->res = res;
+				param->fosr = fosr;
+				param->iosr = iosr;
+				param->fast = fast;
+			}
+
+			if (!delta)
+				return 0;
+		}
+	}
+
+	if (!param->fosr)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int stm32_adfsdm_get_supported_rates(struct stm32_adfsdm_priv *priv,
+					    unsigned int *rates)
+{
+	unsigned long fs = priv->dmic_clk;
+	unsigned int i, decim;
+	int ret;
+
+	*rates = 0;
+
+	for (i = 0; i < ARRAY_SIZE(stm32_dfsdm_filter); i++) {
+		/* check that clkout_freq is compatible */
+		if ((fs % priv->f_param[i].freq) != 0)
+			continue;
+
+		decim = fs / priv->f_param[i].freq;
+
+		/*
+		 * Try to find one solution for filter and integrator
+		 * oversampling ratio with fast mode ON or OFF.
+		 * Fast mode on is the preferred solution.
+		 */
+		ret = stm32_adfsdm_get_best_osr(priv, decim, 0,
+						&priv->f_param[i]);
+		ret &= stm32_adfsdm_get_best_osr(priv, decim, 1,
+						 &priv->f_param[i]);
+		if (!ret) {
+			ret = stm32_adfsdm_compute_shift(priv,
+							 &priv->f_param[i]);
+			if (ret)
+				continue;
+
+			*rates |= 1 << i;
+			dev_dbg(priv->dev, "%s: %d rate supported\n", __func__,
+				priv->f_param[i].freq);
+		}
+	}
+
+	if (!*rates) {
+		dev_err(priv->dev, "%s: no matched rate found\n", __func__);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void stm32_dfsdm_xrun(struct stm32_dfsdm *dfsdm, int flt_id,
+			     enum stm32_dfsdm_events ev, unsigned int param,
+			     void *context)
+{
+	struct stm32_adfsdm_priv *priv = context;
+
+	snd_pcm_stream_lock(priv->substream);
+	dev_err(priv->dev, "%s:unexpected underrun\n", __func__);
+	/* Stop the player */
+	stm32_dfsdm_unregister_fl_event(priv->dfsdm, priv->fl_id,
+					DFSDM_EVENT_REG_XRUN, 0);
+	snd_pcm_stop(priv->substream, SNDRV_PCM_STATE_XRUN);
+	snd_pcm_stream_unlock(priv->substream);
+}
+
+static int stm32_adfsdm_copy(struct snd_pcm_substream *substream, int channel,
+			     snd_pcm_uframes_t pos,
+			     void __user *buf, snd_pcm_uframes_t count)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+	struct stm32_adfsdm_data *f_param;
+	int *ptr = (int *)(runtime->dma_area + frames_to_bytes(runtime, pos));
+	char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, pos);
+	ssize_t bytes = frames_to_bytes(runtime, count);
+	ssize_t sample_cnt = bytes_to_samples(runtime, bytes);
+
+	stm32_adfsdm_get_param(priv, runtime->rate, &f_param);
+
+	/*
+	 * Audio samples are available on 24 MSBs of the DFSDM DATAR register.
+	 * We need to mask 8 LSB control bits...
+	 * Additionnaly precision depends on decimation and can need shift
+	 * to be aligned on 32-bit word MSB.
+	 */
+	if (f_param->shift > 0) {
+		do {
+			*ptr <<= f_param->shift & STM32_ADFSDM_DATAR_DATA_MASK;
+			ptr++;
+		} while (--sample_cnt);
+	} else {
+		do {
+			*ptr &= STM32_ADFSDM_DATAR_DATA_MASK;
+			ptr++;
+		} while (--sample_cnt);
+	}
+
+	return copy_to_user(buf, hwbuf, bytes);
+}
+
+static int stm32_adfsdm_startup(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+	priv->substream = substream;
+
+	/* Fix available rate depending on CLKOUT or CKIN value */
+	return snd_pcm_hw_constraint_list(substream->runtime, 0,
+					  SNDRV_PCM_HW_PARAM_RATE,
+					  &priv->rates_const);
+}
+
+static void stm32_adfsdm_shutdown(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+	priv->substream = NULL;
+}
+
+static int stm32_adfsdm_dai_hw_params(struct snd_pcm_substream *substream,
+				      struct snd_pcm_hw_params *params,
+				      struct snd_soc_dai *dai)
+{
+	struct snd_dmaengine_dai_dma_data *dma_data;
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+	dma_data = snd_soc_dai_get_dma_data(dai, substream);
+	dma_data->maxburst = 1;
+
+	return 0;
+}
+
+static int stm32_adfsdm_prepare(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct stm32_adfsdm_data *f_param;
+	struct stm32_dfsdm_filter filter;
+	struct stm32_dfsdm_regular params;
+	int ret;
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+
+	stm32_adfsdm_get_param(priv, runtime->rate, &f_param);
+
+	memset(&filter, 0, sizeof(filter));
+	memset(&params, 0, sizeof(params));
+
+	params.ch_src = priv->channel.id;
+	params.dma_mode = 1;
+	params.cont_mode = 1;
+	params.fast_mode = f_param->fast;
+	params.sync_mode = priv->synchro ?
+	    DFSDM_FILTER_RSYNC_ON : DFSDM_FILTER_RSYNC_OFF;
+	filter.reg_params = &params;
+	filter.sinc_params.order = priv->order;
+	filter.sinc_params.oversampling = f_param->fosr;
+	filter.int_oversampling = f_param->iosr;
+
+	filter.event.cb = stm32_dfsdm_xrun;
+	filter.event.context = priv;
+
+	ret = stm32_dfsdm_configure_filter(priv->dfsdm, priv->fl_id, &filter);
+	if (ret < 0)
+		return ret;
+
+	ret = stm32_dfsdm_register_fl_event(priv->dfsdm, priv->fl_id,
+					    DFSDM_EVENT_REG_XRUN, 0);
+	if (ret < 0)
+		dev_err(priv->dev, "Failed to register xrun event\n");
+
+	return ret;
+}
+
+static int stm32_adfsdm_start(struct snd_pcm_substream *substream,
+			      struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct stm32_adfsdm_data *f_param;
+	int ret;
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+
+	stm32_adfsdm_get_param(priv, runtime->rate, &f_param);
+	if (f_param->shift < 0)
+		priv->ch_cfg.right_bit_shift = -f_param->shift;
+
+	ret = stm32_dfsdm_start_channel(priv->dfsdm, priv->channel.id,
+					&priv->ch_cfg);
+	if (ret < 0)
+		return ret;
+
+	stm32_dfsdm_start_filter(priv->dfsdm, priv->fl_id,
+				 DFSDM_FILTER_REG_CONV);
+
+	return 0;
+}
+
+static void stm32_adfsdm_stop(struct snd_pcm_substream *substream,
+			      struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+
+	stm32_dfsdm_unregister_fl_event(priv->dfsdm, priv->fl_id,
+					DFSDM_EVENT_REG_XRUN, 0);
+	stm32_dfsdm_stop_filter(priv->dfsdm, priv->fl_id);
+	stm32_dfsdm_stop_channel(priv->dfsdm, priv->channel.id);
+}
+
+static int stm32_adfsdm_trigger(struct snd_pcm_substream *substream, int cmd,
+				struct snd_soc_dai *dai)
+{
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		return stm32_adfsdm_start(substream, dai);
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_STOP:
+		stm32_adfsdm_stop(substream, dai);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int stm32_adfsdm_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	unsigned int inv = fmt & SND_SOC_DAIFMT_INV_MASK;
+	unsigned int cb = fmt & SND_SOC_DAIFMT_MASTER_MASK;
+	int ret;
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+
+	/* DAI clock strobing */
+	if ((inv == SND_SOC_DAIFMT_IB_NF) || (inv == SND_SOC_DAIFMT_IB_IF)) {
+		priv->channel.serial_if.type = DFSDM_CHANNEL_SPI_FALLING;
+		priv->channel.serial_if.pins = DFSDM_CHANNEL_NEXT_CHANNEL_PINS;
+		/*
+		 * if data on falling egde SPI connected to channel n - 1.
+		 * if data on rising egde  SPI connected to channel n.
+		 */
+		if (priv->input_id)
+			priv->channel.id = priv->input_id - 1;
+		else
+			priv->channel.id = priv->dfsdm->max_channels - 1;
+	} else {
+		priv->channel.serial_if.type = DFSDM_CHANNEL_SPI_RISING;
+		priv->channel.serial_if.pins = DFSDM_CHANNEL_SAME_CHANNEL_PINS;
+		priv->channel.id = priv->input_id;
+	}
+
+	dev_dbg(dai->dev, "%s: channel %d on input %d\n", __func__,
+		priv->channel.id, priv->input_id);
+
+	if ((cb == SND_SOC_DAIFMT_CBS_CFM) || (cb == SND_SOC_DAIFMT_CBS_CFS)) {
+		/* Digital microphone is clocked by CLKOUT */
+		stm32_dfsdm_get_clk_out_rate(priv->dfsdm, &priv->dmic_clk);
+	} else {
+		/* Digital microphone is clocked by external clock */
+		if (!priv->dmic_clk) {
+			dev_err(priv->dev,
+				"system-clock-frequency not defined\n");
+			return -EINVAL;
+		}
+	}
+
+	priv->rates_const.count = ARRAY_SIZE(stm32_dfsdm_sr_val);
+	priv->rates_const.list = stm32_dfsdm_sr_val;
+	ret = stm32_adfsdm_get_supported_rates(priv, &priv->rates_const.mask);
+	if (ret < 0)
+		return ret;
+
+	return stm32_dfsdm_get_channel(priv->dfsdm, &priv->channel);
+}
+
+static int stm32_adfsdm_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+				   unsigned int freq, int dir)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
+	if (dir == SND_SOC_CLOCK_IN)
+		priv->dmic_clk = freq;
+
+	return 0;
+}
+
+static const struct snd_soc_dai_ops stm32_adfsdm_dai_ops = {
+	.startup = stm32_adfsdm_startup,
+	.shutdown = stm32_adfsdm_shutdown,
+	.hw_params = stm32_adfsdm_dai_hw_params,
+	.set_fmt = stm32_adfsdm_set_dai_fmt,
+	.set_sysclk = stm32_adfsdm_set_sysclk,
+	.prepare = stm32_adfsdm_prepare,
+	.trigger = stm32_adfsdm_trigger,
+};
+
+static int stm32_adfsdm_dai_probe(struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	struct snd_dmaengine_dai_dma_data *dma = &priv->dma_data;
+	int ret;
+
+	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
+
+	/* filter settings */
+	ret = stm32_dfsdm_get_filter(priv->dfsdm, priv->fl_id);
+	if (ret < 0)
+		return -EBUSY;
+
+	/* DMA settings */
+	snd_soc_dai_init_dma_data(dai, NULL, dma);
+	dma->addr = stm32_dfsdm_get_filter_dma_phy_addr(priv->dfsdm,
+							priv->fl_id,
+							DFSDM_FILTER_REG_CONV);
+	dma->addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+	return 0;
+}
+
+static int stm32_adfsdm_dai_remove(struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
+
+	stm32_dfsdm_release_filter(priv->dfsdm, priv->fl_id);
+	stm32_dfsdm_release_channel(priv->dfsdm, priv->channel.id);
+
+	return 0;
+}
+
+static const struct snd_soc_dai_driver stm32_adfsdm_dai = {
+	.capture = {
+		    .channels_min = 1,
+		    .channels_max = 1,
+		    .formats = SNDRV_PCM_FMTBIT_S24_LE |
+			       SNDRV_PCM_FMTBIT_S32_LE,
+		    .rates = SNDRV_PCM_RATE_8000_48000,
+		    },
+	.probe = stm32_adfsdm_dai_probe,
+	.remove = stm32_adfsdm_dai_remove,
+	.ops = &stm32_adfsdm_dai_ops,
+};
+
+static const struct snd_soc_component_driver stm32_adfsdm_dai_component = {
+	.name = "sti_cpu_dai",
+};
+
+static const struct snd_dmaengine_pcm_config dmaengine_pcm_config = {
+	.pcm_hardware = &stm32_adfsdm_pcm_hw,
+	.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+	.copy = stm32_adfsdm_copy,
+};
+
+static int stm32_adfsdm_probe(struct platform_device *pdev)
+{
+	struct stm32_adfsdm_priv *priv;
+	struct device_node *np = pdev->dev.of_node;
+	char *_name;
+	int ret;
+
+	dev_dbg(&pdev->dev, "%s: enter for node %s\n", __func__,
+		np->name);
+
+	if (!np) {
+		dev_err(&pdev->dev, "No DT found\n");
+		return -EINVAL;
+	}
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->dev = &pdev->dev;
+	priv->dfsdm = dev_get_drvdata(pdev->dev.parent);
+
+	if (of_property_read_u32(np, "reg", &priv->fl_id)) {
+		dev_err(&pdev->dev, "missing reg property\n");
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32(np, "st,dai-filter-order", &priv->order);
+	if (ret < 0) {
+		dev_warn(&pdev->dev, "Default filter order selected\n");
+		priv->order = DFSDM_SINC5_ORDER;
+	}
+
+	ret = of_property_read_u32(np, "st,input-id", &priv->input_id);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "st,input-id property missing\n");
+		return ret;
+	}
+
+	ret = of_property_read_u32(np, "st,dai0-synchronized", &priv->synchro);
+	if (ret < 0)
+		/* default case if property not defined */
+		priv->synchro = 0;
+
+	priv->channel.type.DataPacking = DFSDM_CHANNEL_STANDARD_MODE;
+	priv->channel.type.source = DFSDM_CHANNEL_EXTERNAL_INPUTS;
+	priv->channel.serial_if.spi_clk = DFSDM_CHANNEL_SPI_CLOCK_INTERNAL;
+
+	/* DAI settings */
+	_name = devm_kzalloc(&pdev->dev, sizeof("dfsdm_pdm_0"), GFP_KERNEL);
+	if (!_name)
+		return -ENOMEM;
+
+	priv->dai = stm32_adfsdm_dai;
+
+	priv->f_param = devm_kcalloc(&pdev->dev,
+				     ARRAY_SIZE(stm32_dfsdm_filter),
+				     sizeof(stm32_dfsdm_filter[0]), GFP_KERNEL);
+	if (!priv->f_param)
+		return -ENOMEM;
+
+	memcpy(priv->f_param, stm32_dfsdm_filter,
+	       ARRAY_SIZE(stm32_dfsdm_filter) * sizeof(stm32_dfsdm_filter[0]));
+
+	snprintf(_name, sizeof("dfsdm_pdm_0"), "dfsdm_pdm_%i", priv->fl_id);
+	priv->dai.name = _name;
+	priv->dai.capture.stream_name = _name;
+
+	dev_set_drvdata(&pdev->dev, priv);
+
+	ret = devm_snd_soc_register_component(&pdev->dev,
+					      &stm32_adfsdm_dai_component,
+					      &priv->dai, 1);
+	if (ret < 0)
+		return ret;
+
+	ret = devm_snd_dmaengine_pcm_register(&pdev->dev,
+					      &dmaengine_pcm_config, 0);
+	if (ret < 0)
+		dev_err(&pdev->dev, "failed to register dma pcm config\n");
+
+	return ret;
+}
+
+static const struct of_device_id snd_soc_dfsdm_match[] = {
+	{.compatible = "st,stm32-dfsdm-audio"},
+	{},
+};
+
+static struct platform_driver stm32_adfsdm_driver = {
+	.driver = {
+		   .name = "stm32-dfsdm-audio",
+		   .of_match_table = snd_soc_dfsdm_match,
+		   },
+	.probe = stm32_adfsdm_probe,
+};
+
+module_platform_driver(stm32_adfsdm_driver);
+
+MODULE_DESCRIPTION("stm32 DFSDM DAI driver");
+MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1


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

* [PATCH 7/7] ASoC: add STM32 DFSDM support
@ 2017-01-23 16:32     ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-23 16:32 UTC (permalink / raw)
  To: linux-arm-kernel

Add driver to handle PDM microphones connected to DFSDM IP.

Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
---
 sound/soc/Kconfig            |   1 +
 sound/soc/Makefile           |   1 +
 sound/soc/stm/Kconfig        |  10 +
 sound/soc/stm/Makefile       |   2 +
 sound/soc/stm/stm32_adfsdm.c | 686 +++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 700 insertions(+)
 create mode 100644 sound/soc/stm/Kconfig
 create mode 100644 sound/soc/stm/Makefile
 create mode 100644 sound/soc/stm/stm32_adfsdm.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..79aee4e
--- /dev/null
+++ b/sound/soc/stm/Kconfig
@@ -0,0 +1,10 @@
+menuconfig SND_SOC_STM32_DFSDM
+	tristate "SoC Audio support for STM32 DFSDM"
+	depends on (ARCH_STM32 && OF && MFD_STM32_DFSDM) || COMPILE_TEST
+	depends on SND_SOC
+	select SND_SOC_GENERIC_DMAENGINE_PCM
+	select SND_SOC_DMIC
+	help
+	  Select this option to enable the STM32 Digital Filter
+	  for Sigma Delta Modulators (DFSDM) driver used
+	  in various STM32 series for digital microphone capture.
\ No newline at end of file
diff --git a/sound/soc/stm/Makefile b/sound/soc/stm/Makefile
new file mode 100644
index 0000000..ea90240
--- /dev/null
+++ b/sound/soc/stm/Makefile
@@ -0,0 +1,2 @@
+#DFSDM
+obj-$(CONFIG_SND_SOC_STM32_DFSDM) += stm32_adfsdm.o
diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c
new file mode 100644
index 0000000..9d34bb7
--- /dev/null
+++ b/sound/soc/stm/stm32_adfsdm.c
@@ -0,0 +1,686 @@
+/*
+ * This file is part of STM32 DFSDM ASoC DAI driver
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com>
+ *          Olivier Moysan <olivier.moysan@st.com>
+ *
+ * License type: GPLv2
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <sound/dmaengine_pcm.h>
+
+#include <linux/mfd/stm32-dfsdm.h>
+
+/*
+ * Set data output resolution to 23 bits max to keep 1 extra bit for sign,
+ * as filter output is symmetric +/-2^(n-1).
+ */
+#define STM32_ADFSDM_DATA_RES BIT(23)
+#define STM32_ADFSDM_MAX_RES BIT(31)
+#define STM32_ADFSDM_DATAR_DATA_MASK	GENMASK(31, 8)
+
+struct stm32_adfsdm_data {
+	unsigned int rate;	/* SNDRV_PCM_RATE value */
+	unsigned int freq;	/* frequency in Hz */
+	unsigned int fosr;	/* filter over sampling ratio */
+	unsigned int iosr;	/* integrator over sampling ratio */
+	unsigned int fast;	/* filter fast mode */
+	unsigned long res;	/* output data resolution */
+	int shift;		/* shift on data output */
+	bool h_res_found;	/* preferred resolution higher than expected */
+};
+
+static const struct stm32_adfsdm_data stm32_dfsdm_filter[] = {
+	{ .rate = SNDRV_PCM_RATE_8000,  .freq = 8000 },
+	{ .rate = SNDRV_PCM_RATE_16000, .freq = 16000 },
+	{ .rate = SNDRV_PCM_RATE_32000, .freq = 32000 },
+};
+
+static const unsigned int stm32_dfsdm_sr_val[] = {
+	8000,
+	16000,
+	32000,
+};
+
+struct stm32_adfsdm_priv {
+	struct snd_soc_dai_driver dai;
+	struct snd_dmaengine_dai_dma_data dma_data;
+	struct snd_pcm_substream *substream;
+	struct stm32_dfsdm_sinc_filter fl;
+	struct stm32_dfsdm_channel channel;
+	struct stm32_dfsdm_ch_cfg ch_cfg;
+	struct stm32_dfsdm *dfsdm;
+	struct stm32_adfsdm_data *f_param;
+	struct device *dev;
+	struct snd_pcm_hw_constraint_list rates_const;
+	unsigned long dmic_clk;
+	unsigned int input_id;
+	unsigned int fl_id;
+	unsigned int order; /* filter order */
+	int synchro;
+};
+
+static const struct snd_pcm_hardware stm32_adfsdm_pcm_hw = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+	    SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP |
+	    SNDRV_PCM_INFO_MMAP_VALID,
+	.formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+
+	.rate_min = 8000,
+	.rate_max = 48000,
+
+	.channels_min = 1,
+	.channels_max = 1,
+
+	.periods_min = 2,
+	.periods_max = 48,
+
+	.period_bytes_min = 40, /* 8 khz 5 ms */
+	.period_bytes_max = 4 * PAGE_SIZE,
+	.buffer_bytes_max = 16 * PAGE_SIZE
+};
+
+static inline void stm32_adfsdm_get_param(struct stm32_adfsdm_priv *priv,
+					  unsigned int rate,
+					  struct stm32_adfsdm_data **fparam)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(stm32_dfsdm_filter); i++) {
+		if (rate == priv->f_param[i].freq) {
+			*fparam = &priv->f_param[i];
+			break;
+		}
+	}
+}
+
+static int stm32_adfsdm_compute_shift(struct stm32_adfsdm_priv *priv,
+				      struct stm32_adfsdm_data *param)
+{
+	int shift = 0;
+	u32 r = param->res;
+
+	if (!r) {
+		dev_err(priv->dev, "%s: resolution undefined\n", __func__);
+		return -EINVAL;
+	}
+
+	/*
+	 * If filter resolution is higher than data output resolution
+	 * compute right shift required to match data resolution.
+	 * Otherwise compute left shift to align MSB on data resolution.
+	 */
+	if (r >= STM32_ADFSDM_DATA_RES)
+		while ((r >> -shift) >= STM32_ADFSDM_DATA_RES)
+			shift--;
+	else
+		while ((r << shift) < STM32_ADFSDM_DATA_RES)
+			shift++;
+
+	param->shift = shift;
+	dev_dbg(priv->dev, "%s: output shift: %d\n", __func__, shift);
+
+	return 0;
+}
+
+static int stm32_adfsdm_get_best_osr(struct stm32_adfsdm_priv *priv,
+				     unsigned int decim, bool fast,
+				     struct stm32_adfsdm_data *param)
+{
+	unsigned int i, d, fosr, iosr;
+	u64 res;
+	s64 delta;
+	unsigned int m = 1;	/* multiplication factor */
+	unsigned int p = priv->order;	/* filter order (ford) */
+
+	/*
+	 * Decimation d depends on the filter order and the oversampling ratios.
+	 * ford: filter order
+	 * fosr: filter over sampling ratio
+	 * iosr: integrator over sampling ratio
+	 */
+	dev_dbg(priv->dev, "%s: decim = %d fast = %d\n", __func__, decim, fast);
+	if (priv->order == DFSDM_FASTSINC_ORDER) {
+		m = 2;
+		p = 2;
+	}
+
+	/*
+	 * Looks for filter and integrator oversampling ratios which allow
+	 * to reach 24 bits data output resolution.
+	 * Leave at once if exact resolution if reached.
+	 * Otherwise the higher resolution below 32 bits is kept.
+	 */
+	for (fosr = 1; fosr <= DFSDM_MAX_FL_OVERSAMPLING; fosr++) {
+		for (iosr = 1; iosr <= DFSDM_MAX_INT_OVERSAMPLING; iosr++) {
+			if (fast)
+				d = fosr * iosr;
+			else if (priv->order == DFSDM_FASTSINC_ORDER)
+				d = fosr * (iosr + 3) + 2;
+			else
+				d = fosr * (iosr - 1 + p) + p;
+
+			if (d > decim)
+				break;
+			else if (d != decim)
+				continue;
+			/*
+			 * Check resolution (limited to signed 32 bits)
+			 *   res <= 2^31
+			 * Sincx filters:
+			 *   res = m * fosr^p x iosr (with m=1, p=ford)
+			 * FastSinc filter
+			 *   res = m * fosr^p x iosr (with m=2, p=2)
+			 */
+			res = fosr;
+			for (i = p - 1; i > 0; i--) {
+				res = res * (u64)fosr;
+				if (res > STM32_ADFSDM_MAX_RES)
+					break;
+			}
+			if (res > STM32_ADFSDM_MAX_RES)
+				continue;
+			res = res * (u64)m * (u64)iosr;
+			if (res > STM32_ADFSDM_MAX_RES)
+				continue;
+
+			delta = res - STM32_ADFSDM_DATA_RES;
+
+			if (res >= param->res) {
+				param->res = res;
+				param->fosr = fosr;
+				param->iosr = iosr;
+				param->fast = fast;
+			}
+
+			if (!delta)
+				return 0;
+		}
+	}
+
+	if (!param->fosr)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int stm32_adfsdm_get_supported_rates(struct stm32_adfsdm_priv *priv,
+					    unsigned int *rates)
+{
+	unsigned long fs = priv->dmic_clk;
+	unsigned int i, decim;
+	int ret;
+
+	*rates = 0;
+
+	for (i = 0; i < ARRAY_SIZE(stm32_dfsdm_filter); i++) {
+		/* check that clkout_freq is compatible */
+		if ((fs % priv->f_param[i].freq) != 0)
+			continue;
+
+		decim = fs / priv->f_param[i].freq;
+
+		/*
+		 * Try to find one solution for filter and integrator
+		 * oversampling ratio with fast mode ON or OFF.
+		 * Fast mode on is the preferred solution.
+		 */
+		ret = stm32_adfsdm_get_best_osr(priv, decim, 0,
+						&priv->f_param[i]);
+		ret &= stm32_adfsdm_get_best_osr(priv, decim, 1,
+						 &priv->f_param[i]);
+		if (!ret) {
+			ret = stm32_adfsdm_compute_shift(priv,
+							 &priv->f_param[i]);
+			if (ret)
+				continue;
+
+			*rates |= 1 << i;
+			dev_dbg(priv->dev, "%s: %d rate supported\n", __func__,
+				priv->f_param[i].freq);
+		}
+	}
+
+	if (!*rates) {
+		dev_err(priv->dev, "%s: no matched rate found\n", __func__);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void stm32_dfsdm_xrun(struct stm32_dfsdm *dfsdm, int flt_id,
+			     enum stm32_dfsdm_events ev, unsigned int param,
+			     void *context)
+{
+	struct stm32_adfsdm_priv *priv = context;
+
+	snd_pcm_stream_lock(priv->substream);
+	dev_err(priv->dev, "%s:unexpected underrun\n", __func__);
+	/* Stop the player */
+	stm32_dfsdm_unregister_fl_event(priv->dfsdm, priv->fl_id,
+					DFSDM_EVENT_REG_XRUN, 0);
+	snd_pcm_stop(priv->substream, SNDRV_PCM_STATE_XRUN);
+	snd_pcm_stream_unlock(priv->substream);
+}
+
+static int stm32_adfsdm_copy(struct snd_pcm_substream *substream, int channel,
+			     snd_pcm_uframes_t pos,
+			     void __user *buf, snd_pcm_uframes_t count)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+	struct stm32_adfsdm_data *f_param;
+	int *ptr = (int *)(runtime->dma_area + frames_to_bytes(runtime, pos));
+	char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, pos);
+	ssize_t bytes = frames_to_bytes(runtime, count);
+	ssize_t sample_cnt = bytes_to_samples(runtime, bytes);
+
+	stm32_adfsdm_get_param(priv, runtime->rate, &f_param);
+
+	/*
+	 * Audio samples are available on 24 MSBs of the DFSDM DATAR register.
+	 * We need to mask 8 LSB control bits...
+	 * Additionnaly precision depends on decimation and can need shift
+	 * to be aligned on 32-bit word MSB.
+	 */
+	if (f_param->shift > 0) {
+		do {
+			*ptr <<= f_param->shift & STM32_ADFSDM_DATAR_DATA_MASK;
+			ptr++;
+		} while (--sample_cnt);
+	} else {
+		do {
+			*ptr &= STM32_ADFSDM_DATAR_DATA_MASK;
+			ptr++;
+		} while (--sample_cnt);
+	}
+
+	return copy_to_user(buf, hwbuf, bytes);
+}
+
+static int stm32_adfsdm_startup(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+	priv->substream = substream;
+
+	/* Fix available rate depending on CLKOUT or CKIN value */
+	return snd_pcm_hw_constraint_list(substream->runtime, 0,
+					  SNDRV_PCM_HW_PARAM_RATE,
+					  &priv->rates_const);
+}
+
+static void stm32_adfsdm_shutdown(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+	priv->substream = NULL;
+}
+
+static int stm32_adfsdm_dai_hw_params(struct snd_pcm_substream *substream,
+				      struct snd_pcm_hw_params *params,
+				      struct snd_soc_dai *dai)
+{
+	struct snd_dmaengine_dai_dma_data *dma_data;
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+	dma_data = snd_soc_dai_get_dma_data(dai, substream);
+	dma_data->maxburst = 1;
+
+	return 0;
+}
+
+static int stm32_adfsdm_prepare(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct stm32_adfsdm_data *f_param;
+	struct stm32_dfsdm_filter filter;
+	struct stm32_dfsdm_regular params;
+	int ret;
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+
+	stm32_adfsdm_get_param(priv, runtime->rate, &f_param);
+
+	memset(&filter, 0, sizeof(filter));
+	memset(&params, 0, sizeof(params));
+
+	params.ch_src = priv->channel.id;
+	params.dma_mode = 1;
+	params.cont_mode = 1;
+	params.fast_mode = f_param->fast;
+	params.sync_mode = priv->synchro ?
+	    DFSDM_FILTER_RSYNC_ON : DFSDM_FILTER_RSYNC_OFF;
+	filter.reg_params = &params;
+	filter.sinc_params.order = priv->order;
+	filter.sinc_params.oversampling = f_param->fosr;
+	filter.int_oversampling = f_param->iosr;
+
+	filter.event.cb = stm32_dfsdm_xrun;
+	filter.event.context = priv;
+
+	ret = stm32_dfsdm_configure_filter(priv->dfsdm, priv->fl_id, &filter);
+	if (ret < 0)
+		return ret;
+
+	ret = stm32_dfsdm_register_fl_event(priv->dfsdm, priv->fl_id,
+					    DFSDM_EVENT_REG_XRUN, 0);
+	if (ret < 0)
+		dev_err(priv->dev, "Failed to register xrun event\n");
+
+	return ret;
+}
+
+static int stm32_adfsdm_start(struct snd_pcm_substream *substream,
+			      struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct stm32_adfsdm_data *f_param;
+	int ret;
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+
+	stm32_adfsdm_get_param(priv, runtime->rate, &f_param);
+	if (f_param->shift < 0)
+		priv->ch_cfg.right_bit_shift = -f_param->shift;
+
+	ret = stm32_dfsdm_start_channel(priv->dfsdm, priv->channel.id,
+					&priv->ch_cfg);
+	if (ret < 0)
+		return ret;
+
+	stm32_dfsdm_start_filter(priv->dfsdm, priv->fl_id,
+				 DFSDM_FILTER_REG_CONV);
+
+	return 0;
+}
+
+static void stm32_adfsdm_stop(struct snd_pcm_substream *substream,
+			      struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+
+	stm32_dfsdm_unregister_fl_event(priv->dfsdm, priv->fl_id,
+					DFSDM_EVENT_REG_XRUN, 0);
+	stm32_dfsdm_stop_filter(priv->dfsdm, priv->fl_id);
+	stm32_dfsdm_stop_channel(priv->dfsdm, priv->channel.id);
+}
+
+static int stm32_adfsdm_trigger(struct snd_pcm_substream *substream, int cmd,
+				struct snd_soc_dai *dai)
+{
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		return stm32_adfsdm_start(substream, dai);
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_STOP:
+		stm32_adfsdm_stop(substream, dai);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int stm32_adfsdm_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	unsigned int inv = fmt & SND_SOC_DAIFMT_INV_MASK;
+	unsigned int cb = fmt & SND_SOC_DAIFMT_MASTER_MASK;
+	int ret;
+
+	dev_dbg(dai->dev, "%s: enter\n", __func__);
+
+	/* DAI clock strobing */
+	if ((inv == SND_SOC_DAIFMT_IB_NF) || (inv == SND_SOC_DAIFMT_IB_IF)) {
+		priv->channel.serial_if.type = DFSDM_CHANNEL_SPI_FALLING;
+		priv->channel.serial_if.pins = DFSDM_CHANNEL_NEXT_CHANNEL_PINS;
+		/*
+		 * if data on falling egde SPI connected to channel n - 1.
+		 * if data on rising egde  SPI connected to channel n.
+		 */
+		if (priv->input_id)
+			priv->channel.id = priv->input_id - 1;
+		else
+			priv->channel.id = priv->dfsdm->max_channels - 1;
+	} else {
+		priv->channel.serial_if.type = DFSDM_CHANNEL_SPI_RISING;
+		priv->channel.serial_if.pins = DFSDM_CHANNEL_SAME_CHANNEL_PINS;
+		priv->channel.id = priv->input_id;
+	}
+
+	dev_dbg(dai->dev, "%s: channel %d on input %d\n", __func__,
+		priv->channel.id, priv->input_id);
+
+	if ((cb == SND_SOC_DAIFMT_CBS_CFM) || (cb == SND_SOC_DAIFMT_CBS_CFS)) {
+		/* Digital microphone is clocked by CLKOUT */
+		stm32_dfsdm_get_clk_out_rate(priv->dfsdm, &priv->dmic_clk);
+	} else {
+		/* Digital microphone is clocked by external clock */
+		if (!priv->dmic_clk) {
+			dev_err(priv->dev,
+				"system-clock-frequency not defined\n");
+			return -EINVAL;
+		}
+	}
+
+	priv->rates_const.count = ARRAY_SIZE(stm32_dfsdm_sr_val);
+	priv->rates_const.list = stm32_dfsdm_sr_val;
+	ret = stm32_adfsdm_get_supported_rates(priv, &priv->rates_const.mask);
+	if (ret < 0)
+		return ret;
+
+	return stm32_dfsdm_get_channel(priv->dfsdm, &priv->channel);
+}
+
+static int stm32_adfsdm_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+				   unsigned int freq, int dir)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
+	if (dir == SND_SOC_CLOCK_IN)
+		priv->dmic_clk = freq;
+
+	return 0;
+}
+
+static const struct snd_soc_dai_ops stm32_adfsdm_dai_ops = {
+	.startup = stm32_adfsdm_startup,
+	.shutdown = stm32_adfsdm_shutdown,
+	.hw_params = stm32_adfsdm_dai_hw_params,
+	.set_fmt = stm32_adfsdm_set_dai_fmt,
+	.set_sysclk = stm32_adfsdm_set_sysclk,
+	.prepare = stm32_adfsdm_prepare,
+	.trigger = stm32_adfsdm_trigger,
+};
+
+static int stm32_adfsdm_dai_probe(struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+	struct snd_dmaengine_dai_dma_data *dma = &priv->dma_data;
+	int ret;
+
+	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
+
+	/* filter settings */
+	ret = stm32_dfsdm_get_filter(priv->dfsdm, priv->fl_id);
+	if (ret < 0)
+		return -EBUSY;
+
+	/* DMA settings */
+	snd_soc_dai_init_dma_data(dai, NULL, dma);
+	dma->addr = stm32_dfsdm_get_filter_dma_phy_addr(priv->dfsdm,
+							priv->fl_id,
+							DFSDM_FILTER_REG_CONV);
+	dma->addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+	return 0;
+}
+
+static int stm32_adfsdm_dai_remove(struct snd_soc_dai *dai)
+{
+	struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+	dev_dbg(dai->dev, "%s: enter for dai %d\n", __func__, dai->id);
+
+	stm32_dfsdm_release_filter(priv->dfsdm, priv->fl_id);
+	stm32_dfsdm_release_channel(priv->dfsdm, priv->channel.id);
+
+	return 0;
+}
+
+static const struct snd_soc_dai_driver stm32_adfsdm_dai = {
+	.capture = {
+		    .channels_min = 1,
+		    .channels_max = 1,
+		    .formats = SNDRV_PCM_FMTBIT_S24_LE |
+			       SNDRV_PCM_FMTBIT_S32_LE,
+		    .rates = SNDRV_PCM_RATE_8000_48000,
+		    },
+	.probe = stm32_adfsdm_dai_probe,
+	.remove = stm32_adfsdm_dai_remove,
+	.ops = &stm32_adfsdm_dai_ops,
+};
+
+static const struct snd_soc_component_driver stm32_adfsdm_dai_component = {
+	.name = "sti_cpu_dai",
+};
+
+static const struct snd_dmaengine_pcm_config dmaengine_pcm_config = {
+	.pcm_hardware = &stm32_adfsdm_pcm_hw,
+	.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+	.copy = stm32_adfsdm_copy,
+};
+
+static int stm32_adfsdm_probe(struct platform_device *pdev)
+{
+	struct stm32_adfsdm_priv *priv;
+	struct device_node *np = pdev->dev.of_node;
+	char *_name;
+	int ret;
+
+	dev_dbg(&pdev->dev, "%s: enter for node %s\n", __func__,
+		np->name);
+
+	if (!np) {
+		dev_err(&pdev->dev, "No DT found\n");
+		return -EINVAL;
+	}
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->dev = &pdev->dev;
+	priv->dfsdm = dev_get_drvdata(pdev->dev.parent);
+
+	if (of_property_read_u32(np, "reg", &priv->fl_id)) {
+		dev_err(&pdev->dev, "missing reg property\n");
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32(np, "st,dai-filter-order", &priv->order);
+	if (ret < 0) {
+		dev_warn(&pdev->dev, "Default filter order selected\n");
+		priv->order = DFSDM_SINC5_ORDER;
+	}
+
+	ret = of_property_read_u32(np, "st,input-id", &priv->input_id);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "st,input-id property missing\n");
+		return ret;
+	}
+
+	ret = of_property_read_u32(np, "st,dai0-synchronized", &priv->synchro);
+	if (ret < 0)
+		/* default case if property not defined */
+		priv->synchro = 0;
+
+	priv->channel.type.DataPacking = DFSDM_CHANNEL_STANDARD_MODE;
+	priv->channel.type.source = DFSDM_CHANNEL_EXTERNAL_INPUTS;
+	priv->channel.serial_if.spi_clk = DFSDM_CHANNEL_SPI_CLOCK_INTERNAL;
+
+	/* DAI settings */
+	_name = devm_kzalloc(&pdev->dev, sizeof("dfsdm_pdm_0"), GFP_KERNEL);
+	if (!_name)
+		return -ENOMEM;
+
+	priv->dai = stm32_adfsdm_dai;
+
+	priv->f_param = devm_kcalloc(&pdev->dev,
+				     ARRAY_SIZE(stm32_dfsdm_filter),
+				     sizeof(stm32_dfsdm_filter[0]), GFP_KERNEL);
+	if (!priv->f_param)
+		return -ENOMEM;
+
+	memcpy(priv->f_param, stm32_dfsdm_filter,
+	       ARRAY_SIZE(stm32_dfsdm_filter) * sizeof(stm32_dfsdm_filter[0]));
+
+	snprintf(_name, sizeof("dfsdm_pdm_0"), "dfsdm_pdm_%i", priv->fl_id);
+	priv->dai.name = _name;
+	priv->dai.capture.stream_name = _name;
+
+	dev_set_drvdata(&pdev->dev, priv);
+
+	ret = devm_snd_soc_register_component(&pdev->dev,
+					      &stm32_adfsdm_dai_component,
+					      &priv->dai, 1);
+	if (ret < 0)
+		return ret;
+
+	ret = devm_snd_dmaengine_pcm_register(&pdev->dev,
+					      &dmaengine_pcm_config, 0);
+	if (ret < 0)
+		dev_err(&pdev->dev, "failed to register dma pcm config\n");
+
+	return ret;
+}
+
+static const struct of_device_id snd_soc_dfsdm_match[] = {
+	{.compatible = "st,stm32-dfsdm-audio"},
+	{},
+};
+
+static struct platform_driver stm32_adfsdm_driver = {
+	.driver = {
+		   .name = "stm32-dfsdm-audio",
+		   .of_match_table = snd_soc_dfsdm_match,
+		   },
+	.probe = stm32_adfsdm_probe,
+};
+
+module_platform_driver(stm32_adfsdm_driver);
+
+MODULE_DESCRIPTION("stm32 DFSDM DAI driver");
+MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

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

* Re: [PATCH 5/7] ASoC: dmaengine_pcm: add copy support
  2017-01-23 16:32   ` Arnaud Pouliquen
  (?)
@ 2017-01-23 17:50       ` Mark Brown
  -1 siblings, 0 replies; 130+ messages in thread
From: Mark Brown @ 2017-01-23 17:50 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Lee Jones, Rob Herring,
	Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Maxime Coquelin, Alexandre Torgue

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

On Mon, Jan 23, 2017 at 05:32:23PM +0100, Arnaud Pouliquen wrote:
> From: olivier moysan <olivier.moysan-qxv4g6HH51o@public.gmane.org>

> Signed-off-by: olivier moysan <omoysan.stm32-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

The signoff and author information don't match for this patch, they
should match.  The signoff is important for legal reasons - please see
SubmittingPatches for details.

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

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

* Re: [PATCH 5/7] ASoC: dmaengine_pcm: add copy support
@ 2017-01-23 17:50       ` Mark Brown
  0 siblings, 0 replies; 130+ messages in thread
From: Mark Brown @ 2017-01-23 17:50 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, Lee Jones,
	Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Maxime Coquelin, Alexandre Torgue

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

On Mon, Jan 23, 2017 at 05:32:23PM +0100, Arnaud Pouliquen wrote:
> From: olivier moysan <olivier.moysan@st.com>

> Signed-off-by: olivier moysan <omoysan.stm32@gmail.com>

The signoff and author information don't match for this patch, they
should match.  The signoff is important for legal reasons - please see
SubmittingPatches for details.

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

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

* [PATCH 5/7] ASoC: dmaengine_pcm: add copy support
@ 2017-01-23 17:50       ` Mark Brown
  0 siblings, 0 replies; 130+ messages in thread
From: Mark Brown @ 2017-01-23 17:50 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Jan 23, 2017 at 05:32:23PM +0100, Arnaud Pouliquen wrote:
> From: olivier moysan <olivier.moysan@st.com>

> Signed-off-by: olivier moysan <omoysan.stm32@gmail.com>

The signoff and author information don't match for this patch, they
should match.  The signoff is important for legal reasons - please see
SubmittingPatches for details.
-------------- 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/20170123/b2fadb81/attachment.sig>

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

* Re: [alsa-devel] [PATCH 4/7] IIO: add STM32 DFSDM ADC support
  2017-01-23 16:32   ` Arnaud Pouliquen
  (?)
  (?)
@ 2017-01-23 19:40   ` Peter Meerwald-Stadler
  -1 siblings, 0 replies; 130+ messages in thread
From: Peter Meerwald-Stadler @ 2017-01-23 19:40 UTC (permalink / raw)
  To: Arnaud Pouliquen; +Cc: linux-iio, Jonathan Cameron, Lars-Peter Clausen

On Mon, 23 Jan 2017, Arnaud Pouliquen wrote:

> Add driver to handle Sigma Delta ADC conversion for ADC
> connected to DFSDM IP.

mostly trivial comments below
 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
>  drivers/iio/adc/Kconfig           |   9 +
>  drivers/iio/adc/Makefile          |   1 +
>  drivers/iio/adc/stm32-dfsdm-adc.c | 676 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 686 insertions(+)
>  create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c
> 
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index e0b3c09..4b2b886 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -441,6 +441,15 @@ config STM32_ADC
>  	  This driver can also be built as a module.  If so, the module
>  	  will be called stm32-adc.
>  
> +config STM32_DFSDM_ADC
> +	tristate "STMicroelectronics STM32 DFSDM ADC driver"
> +	depends on (ARCH_STM32 && OF && MFD_STM32_DFSDM) || COMPILE_TEST
> +	help
> +	  Say yes here to build the driver for the STMicroelectronics
> +	  STM32 analog-to-digital converter with Digital filter.
> +	  This driver can also be built as a module. If so, the module
> +	  will be called stm32_dfsdm_adc.
> +
>  config STX104
>  	tristate "Apex Embedded Systems STX104 driver"
>  	depends on X86 && ISA_BUS_API
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 8e02a94..aed42c6 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -43,6 +43,7 @@ obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
>  obj-$(CONFIG_STX104) += stx104.o
>  obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
>  obj-$(CONFIG_STM32_ADC) += stm32-adc.o
> +obj-$(CONFIG_STM32_DFSDM_ADC) += stm32-dfsdm-adc.o
>  obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
>  obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
>  obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
> diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
> new file mode 100644
> index 0000000..727d6b1
> --- /dev/null
> +++ b/drivers/iio/adc/stm32-dfsdm-adc.c
> @@ -0,0 +1,676 @@
> +/*
> + * This file is part of STM32 DFSDM ADC driver
> + *
> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
> + * Author: Arnaud Pouliquen <arnaud.pouliquen@st.com>.
> + *
> + * License type: GPLv2
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/irq_work.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#include <linux/iio/iio.h>
> +
> +#include <linux/mfd/stm32-dfsdm.h>
> +
> +#define DFSDM_ADC_MAX_RESOLUTION 24
> +#define DFSDM_ADC_STORAGE_BITS   32
> +
> +#define DFSDM_MAX_CH_OFFSET BIT(24)
> +#define DFSDM_MAX_CH_SHIFT 24
> +
> +#define DFSDM_TIMEOUT_US 100000

this only use of the constant is in the line below where it is divided by 
1000

> +#define DFSDM_TIMEOUT (msecs_to_jiffies(DFSDM_TIMEOUT_US / 1000))
> +
> +#define CH_ID_FROM_IDX(i) (adc->inputs[i].id)

in IIO we like consistent prefixes for macros and all declarations

e.g. STM32_DFSDM_ or just DFSDM_


> +#define CH_CFG_FROM_IDX(i) (&adc->inputs_cfg[i])
> +
> +struct stm32_dfsdm_adc {
> +	struct device *dev;
> +	struct stm32_dfsdm *dfsdm;
> +	struct list_head adc_list;
> +
> +	/* Filter */
> +	unsigned int fl_id;
> +	struct stm32_dfsdm_sinc_filter sinc;
> +	unsigned int int_oversampling;
> +
> +	/* Channels */
> +	struct stm32_dfsdm_channel *inputs;
> +	struct stm32_dfsdm_ch_cfg *inputs_cfg;
> +
> +	/* Raw mode*/
> +	struct completion completion;
> +	struct stm32_dfsdm_regular reg_params;
> +	u32 *buffer;
> +};
> +
> +static const char * const stm32_dfsdm_adc_sinc_order[] = {
> +	[0] = "FastSinc",
> +	[1] = "Sinc1",
> +	[2] = "Sinc2",
> +	[3] = "Sinc3",
> +	[4] = "Sinc4",
> +	[5] = "Sinc5",
> +};
> +
> +static inline const struct iio_chan_spec *get_ch_from_id(

no inline, let the compiler decide, here and elsewhere
consistent prefix please

> +					struct iio_dev *indio_dev, int ch_id)
> +{
> +	int i;
> +
> +	for (i = 0; i < indio_dev->num_channels; i++) {
> +		if (ch_id == indio_dev->channels[i].channel)

channel is a channel number, not an id; the parameter chould be more 
appropriately named

> +			return &indio_dev->channels[i];
> +	}
> +
> +	return NULL;
> +}
> +
> +/*
> + * Filter attributes

single line comment style? here and elsewhere, matter of taste

> + */
> +
> +static int stm32_dfsdm_adc_set_sinc(struct iio_dev *indio_dev,
> +				    const struct iio_chan_spec *chan,
> +				    unsigned int val)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +
> +	dev_dbg(&indio_dev->dev, "%s: %s\n", __func__,
> +		stm32_dfsdm_adc_sinc_order[adc->sinc.order]);
> +
> +	adc->sinc.order = val;
> +
> +	return 0;
> +}
> +
> +static int stm32_dfsdm_adc_get_sinc(struct iio_dev *indio_dev,
> +				    const struct iio_chan_spec *chan)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +
> +	dev_dbg(&indio_dev->dev, "%s: %s\n", __func__,
> +		stm32_dfsdm_adc_sinc_order[adc->sinc.order]);
> +
> +	return adc->sinc.order;
> +}
> +
> +static const struct iio_enum stm32_dfsdm_adc_fl_sinc_order = {
> +	.items = stm32_dfsdm_adc_sinc_order,
> +	.num_items = ARRAY_SIZE(stm32_dfsdm_adc_sinc_order),
> +	.get = stm32_dfsdm_adc_get_sinc,
> +	.set = stm32_dfsdm_adc_set_sinc,
> +};
> +
> +static ssize_t stm32_dfsdm_adc_get_int_os(struct iio_dev *indio_dev,
> +					  uintptr_t priv,
> +					  const struct iio_chan_spec *chan,
> +					  char *buf)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n", adc->int_oversampling);
> +}
> +
> +static ssize_t stm32_dfsdm_adc_set_int_os(struct iio_dev *indio_dev,
> +					  uintptr_t priv,
> +					  const struct iio_chan_spec *chan,
> +					  const char *buf, size_t len)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	int ret, val;
> +
> +	ret = kstrtoint(buf, 0, &val);

we want an uint here for val?

> +	if (ret)
> +		return ret;
> +
> +	if ((!val) || (val > DFSDM_MAX_INT_OVERSAMPLING)) {

what if val is negative?

> +		dev_err(&indio_dev->dev, "invalid oversampling (0 or > %#x)",
> +			DFSDM_MAX_INT_OVERSAMPLING);

\n missing

> +		return -EINVAL;
> +	}
> +	adc->int_oversampling = val;
> +
> +	return len;
> +}
> +
> +static ssize_t stm32_dfsdm_adc_get_fl_os(struct iio_dev *indio_dev,
> +					 uintptr_t priv,
> +					 const struct iio_chan_spec *chan,
> +					 char *buf)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n", adc->sinc.oversampling);
> +}
> +
> +static ssize_t stm32_dfsdm_adc_set_fl_os(struct iio_dev *indio_dev,
> +					 uintptr_t priv,
> +					const struct iio_chan_spec *chan,
> +					const char *buf, size_t len)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	int ret, val;
> +
> +	ret = kstrtoint(buf, 0, &val);
> +	if (ret)
> +		return ret;
> +
> +	if ((!val) || (val > DFSDM_MAX_FL_OVERSAMPLING)) {

parenthesis not strictly needed, matter of taste

> +		dev_err(&indio_dev->dev, "invalid oversampling (0 or > %#x)",
> +			DFSDM_MAX_FL_OVERSAMPLING);
> +		return -EINVAL;
> +	}
> +	adc->sinc.oversampling = val;
> +
> +	return len;
> +}
> +
> +/*
> + * Data bit shifting attribute
> + */
> +static ssize_t stm32_dfsdm_adc_get_shift(struct iio_dev *indio_dev,
> +					 uintptr_t priv,
> +					 const struct iio_chan_spec *chan,
> +					 char *buf)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n", ch_cfg->right_bit_shift);
> +}
> +
> +static ssize_t stm32_dfsdm_adc_set_shift(struct iio_dev *indio_dev,
> +					 uintptr_t priv,
> +					 const struct iio_chan_spec *chan,
> +					 const char *buf, size_t len)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
> +	int ret, val;
> +
> +	ret = kstrtoint(buf, 0, &val);
> +	if (ret)
> +		return ret;
> +
> +	if (val > DFSDM_MAX_CH_SHIFT) {
> +		dev_err(&indio_dev->dev, "invalid shift value (> %#x)",
> +			DFSDM_MAX_CH_SHIFT);
> +		return -EINVAL;
> +	}
> +	ch_cfg->right_bit_shift = val;
> +
> +	return len;
> +}
> +
> +static const struct iio_chan_spec_ext_info stm32_dfsdm_adc_ext_info[] = {
> +	/* sinc_filter_order: Configure Sinc filter order */
> +	IIO_ENUM("sinc_filter_order", IIO_SHARED_BY_TYPE,
> +		 &stm32_dfsdm_adc_fl_sinc_order),
> +	IIO_ENUM_AVAILABLE("sinc_filter_order", &stm32_dfsdm_adc_fl_sinc_order),
> +	/* filter oversampling: Post filter oversampling ratio */
> +	{
> +		.name = "sinc_filter_oversampling_ratio",
> +		.shared = IIO_SHARED_BY_TYPE,
> +		.read = stm32_dfsdm_adc_get_fl_os,
> +		.write = stm32_dfsdm_adc_set_fl_os,
> +	},
> +	/* data_right_bit_shift : Filter output data shifting */

no space before :, here and elsewhere

> +	{
> +		.name = "data_right_bit_shift",
> +		.shared = IIO_SEPARATE,
> +		.read = stm32_dfsdm_adc_get_shift,
> +		.write = stm32_dfsdm_adc_set_shift,
> +	},
> +
> +	/*
> +	 * averaging_length : Mean windows of data from filter.
> +	 * Defines how many filter data will be summed to one data output
> +	 */
> +	{
> +		.name = "integrator_oversampling",
> +		.shared = IIO_SHARED_BY_TYPE,
> +		.read = stm32_dfsdm_adc_get_int_os,
> +		.write = stm32_dfsdm_adc_set_int_os,
> +	},
> +	{},
> +};
> +
> +/*
> + * Filter event routine called under IRQ context
> + */
> +static void stm32_dfsdm_event_cb(struct stm32_dfsdm *dfsdm, int flt_id,
> +				 enum stm32_dfsdm_events ev, unsigned int param,
> +				 void *context)
> +{
> +	struct stm32_dfsdm_adc *adc = context;
> +	unsigned int ch_id;
> +
> +	dev_dbg(adc->dev, "%s:\n", __func__);
> +
> +	switch (ev) {
> +	case DFSDM_EVENT_REG_EOC:
> +		stm32_dfsdm_read_fl_conv(adc->dfsdm, flt_id, adc->buffer,
> +					 &ch_id, DFSDM_FILTER_REG_CONV);
> +		complete(&adc->completion);
> +		break;
> +	case DFSDM_EVENT_REG_XRUN:
> +		dev_err(adc->dev, "%s: underrun detected for filter %d\n",
> +			__func__, flt_id);
> +		break;
> +	default:
> +		dev_err(adc->dev, "%s: event %#x not implemented\n",
> +			__func__, ev);
> +		break;
> +	}
> +}
> +
> +static inline void stm32_dfsdm_adc_fl_config(struct stm32_dfsdm_adc *adc,
> +					     u32 channel_mask,
> +					     struct stm32_dfsdm_filter *filter)
> +{
> +	dev_dbg(adc->dev, "%s:\n", __func__);
> +
> +	filter->event.cb = stm32_dfsdm_event_cb;
> +	filter->event.context = adc;
> +
> +	filter->sinc_params = adc->sinc;
> +
> +	filter->int_oversampling = adc->int_oversampling;
> +}
> +
> +static int stm32_dfsdm_adc_start_raw_conv(struct stm32_dfsdm_adc *adc,
> +					  const struct iio_chan_spec *chan)
> +{
> +	struct stm32_dfsdm_filter filter;
> +	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
> +	unsigned int ch_id = CH_ID_FROM_IDX(chan->scan_index);
> +	int ret;
> +
> +	dev_dbg(adc->dev, "%s:\n", __func__);
> +
> +	memset(&filter, 0, sizeof(filter));
> +	filter.reg_params = &adc->reg_params;
> +
> +	if (!filter.reg_params)
> +		return -ENOMEM;
> +
> +	filter.reg_params->ch_src = ch_id;
> +
> +	stm32_dfsdm_adc_fl_config(adc, BIT(ch_id), &filter);
> +
> +	ret = stm32_dfsdm_configure_filter(adc->dfsdm, adc->fl_id, &filter);
> +	if (ret < 0) {
> +		dev_err(adc->dev, "Failed to configure filter\n");
> +		return ret;
> +	}
> +
> +	ret = stm32_dfsdm_start_channel(adc->dfsdm, ch_id, ch_cfg);
> +	if (ret < 0)
> +		return ret;
> +
> +	stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id, DFSDM_FILTER_REG_CONV);
> +
> +	return 0;
> +}
> +
> +static void stm32_dfsdm_adc_stop_raw_conv(struct stm32_dfsdm_adc *adc,
> +					  const struct iio_chan_spec *chan)
> +{
> +	unsigned int ch_id = CH_ID_FROM_IDX(chan->scan_index);
> +
> +	dev_dbg(adc->dev, "%s:\n", __func__);
> +
> +	stm32_dfsdm_stop_filter(adc->dfsdm, adc->fl_id);
> +	stm32_dfsdm_stop_channel(adc->dfsdm, ch_id);
> +}
> +
> +static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
> +				   const struct iio_chan_spec *chan,
> +				   u32 *result)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	long timeout;
> +	int ret;
> +
> +	dev_dbg(&indio_dev->dev, "%s:\n", __func__);
> +
> +	reinit_completion(&adc->completion);
> +
> +	ret = stm32_dfsdm_register_fl_event(adc->dfsdm, adc->fl_id,
> +					    DFSDM_EVENT_REG_EOC, 0);
> +	if (ret < 0) {
> +		dev_err(&indio_dev->dev, "Failed to register event\n");
> +		return ret;
> +	}
> +
> +	adc->buffer = result;
> +	ret = stm32_dfsdm_adc_start_raw_conv(adc, chan);
> +	if (ret) {
> +		dev_err(&indio_dev->dev, "Failed to start conversion\n");
> +		goto free_event;
> +	}
> +
> +	timeout = wait_for_completion_interruptible_timeout(&adc->completion,
> +							    DFSDM_TIMEOUT);
> +	if (timeout == 0) {
> +		dev_warn(&indio_dev->dev, "Conversion timed out!\n");
> +		ret = -ETIMEDOUT;
> +	} else if (timeout < 0) {
> +		ret = timeout;
> +	} else {
> +		dev_dbg(&indio_dev->dev, "converted val %#x\n", *result);
> +		ret = IIO_VAL_INT;
> +	}
> +
> +	stm32_dfsdm_adc_stop_raw_conv(adc, chan);
> +
> +free_event:
> +	adc->buffer = NULL;
> +	stm32_dfsdm_unregister_fl_event(adc->dfsdm, adc->fl_id,
> +					DFSDM_EVENT_REG_EOC, 0);
> +
> +	return ret;
> +}
> +
> +static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
> +				struct iio_chan_spec const *chan, int *val,
> +				int *val2, long mask)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
> +	int ret = -EINVAL;
> +
> +	dev_dbg(&indio_dev->dev, "%s channel %d\n", __func__, chan->channel);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
> +		if (!ret)
> +			ret = IIO_VAL_INT;

_single_conv() already returns IIO_VAL_INT on success?

> +		break;
> +	case IIO_CHAN_INFO_OFFSET:
> +		*val = ch_cfg->offset;
> +		ret = IIO_VAL_INT;
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev,
> +				 struct iio_chan_spec const *chan, int val,
> +				 int val2, long mask)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
> +
> +	dev_dbg(&indio_dev->dev, "%s channel%d", __func__, chan->channel);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_OFFSET:
> +		if (val > DFSDM_MAX_CH_OFFSET) {
> +			dev_err(&indio_dev->dev, "invalid offset (> %#lx)",
> +				DFSDM_MAX_CH_OFFSET);
> +			return -EINVAL;
> +		}
> +		ch_cfg->offset = val;
> +		break;

maybe return 0; directly here

> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct iio_info stm32_dfsdm_iio_info = {
> +	.read_raw = stm32_dfsdm_read_raw,
> +	.write_raw = stm32_dfsdm_write_raw,
> +	.driver_module = THIS_MODULE,
> +};
> +
> +static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
> +					 struct iio_chan_spec *chan,
> +					 int chan_idx)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	struct stm32_dfsdm_channel *dfsdm_ch = &adc->inputs[chan_idx];
> +	struct iio_chan_spec *ch = &chan[chan_idx];
> +	int ret;
> +	unsigned int alt_ch = 0;

initialization needed?

> +
> +	ret = of_property_read_u32_index(indio_dev->dev.of_node,
> +					 "st,adc-channels", chan_idx,
> +					 &ch->channel);
> +	if (ret < 0) {
> +		dev_err(&indio_dev->dev,
> +			" error parsing 'st,adc-channels' for idx %d\n",

remove extra space at beginning

> +			chan_idx);
> +		return ret;
> +	}
> +
> +	ret = of_property_read_string_index(indio_dev->dev.of_node,
> +					    "st,adc-channel-names", chan_idx,
> +					    &ch->datasheet_name);
> +	if (ret < 0) {
> +		dev_err(&indio_dev->dev,
> +			" error parsing 'st,adc-channel-names' for idx %d\n",
> +			chan_idx);
> +		return ret;
> +	}
> +
> +	ch->extend_name = ch->datasheet_name;
> +	ch->type = IIO_VOLTAGE;
> +	ch->indexed = 1;
> +	ch->scan_index = chan_idx;
> +	ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +				 BIT(IIO_CHAN_INFO_OFFSET);
> +	ch->scan_type.sign = 'u';
> +	ch->scan_type.realbits = DFSDM_ADC_MAX_RESOLUTION;
> +	ch->scan_type.storagebits = DFSDM_ADC_STORAGE_BITS;
> +	ch->scan_type.shift = 8;

could compute _STORAGE_BITS - _MAX_RESOLUTION?

> +
> +	ch->ext_info = stm32_dfsdm_adc_ext_info;
> +
> +	of_property_read_u32_index(indio_dev->dev.of_node, "st,adc-alt-channel",
> +				   chan_idx, &alt_ch);

check retval?

> +	/* Select the previous channel if alternate field is defined*/

add space before */

> +	if (alt_ch) {
> +		if (!ch->channel)
> +			ch->channel = adc->dfsdm->max_channels;
> +		ch->channel -= 1;
> +		dfsdm_ch->serial_if.pins = DFSDM_CHANNEL_NEXT_CHANNEL_PINS;
> +	} else {
> +		dfsdm_ch->serial_if.pins = DFSDM_CHANNEL_SAME_CHANNEL_PINS;
> +	}
> +	dfsdm_ch->id = ch->channel;
> +
> +	dfsdm_ch->type.DataPacking = DFSDM_CHANNEL_STANDARD_MODE;
> +
> +	dfsdm_ch->type.source = DFSDM_CHANNEL_EXTERNAL_INPUTS;
> +	ret = of_property_read_u32_index(indio_dev->dev.of_node,
> +					 "st,adc-channel-types",
> +					 chan_idx, &dfsdm_ch->serial_if.type);
> +	if (ret < 0)
> +		dfsdm_ch->serial_if.type = DFSDM_CHANNEL_SPI_RISING;
> +
> +	ret = of_property_read_u32_index(indio_dev->dev.of_node,
> +					 "st,adc-channel-clk-src",
> +					 chan_idx,
> +					 &dfsdm_ch->serial_if.spi_clk);
> +
> +	if ((dfsdm_ch->serial_if.type == DFSDM_CHANNEL_MANCHESTER_RISING)  ||
> +	    (dfsdm_ch->serial_if.type == DFSDM_CHANNEL_MANCHESTER_FALLING) ||

inconsistent indentation

> +	    (ret < 0))
> +		dfsdm_ch->serial_if.spi_clk = DFSDM_CHANNEL_SPI_CLOCK_INTERNAL;
> +
> +	return stm32_dfsdm_get_channel(adc->dfsdm, dfsdm_ch);
> +}
> +
> +static int stm32_dfsdm_adc_chan_init(struct iio_dev *indio_dev)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	unsigned int num_ch;
> +	struct iio_chan_spec *channels;
> +	int ret, chan_idx;
> +
> +	num_ch = of_property_count_strings(indio_dev->dev.of_node,
> +					   "st,adc-channel-names");
> +
> +	channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
> +				GFP_KERNEL);
> +	if (!channels)
> +		return -ENOMEM;
> +
> +	adc->inputs = devm_kcalloc(&indio_dev->dev, num_ch,
> +				   sizeof(*adc->inputs), GFP_KERNEL);
> +	if (!adc->inputs)
> +		return -ENOMEM;
> +
> +	adc->inputs_cfg = devm_kcalloc(&indio_dev->dev, num_ch,
> +				       sizeof(*adc->inputs_cfg), GFP_KERNEL);
> +	if (!adc->inputs_cfg)
> +		return -ENOMEM;
> +
> +	for (chan_idx = 0; chan_idx < num_ch; chan_idx++) {
> +		ret = stm32_dfsdm_adc_chan_init_one(indio_dev, channels,
> +						    chan_idx);
> +		if (ret < 0)
> +			goto ch_error;
> +	}
> +
> +	indio_dev->num_channels = num_ch;
> +	indio_dev->channels = channels;
> +
> +	return 0;
> +
> +ch_error:
> +	for (chan_idx--; chan_idx >= 0; chan_idx--)
> +		stm32_dfsdm_release_channel(adc->dfsdm,
> +					    adc->inputs[chan_idx].id);
> +
> +	return ret;
> +}
> +
> +static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct stm32_dfsdm_adc *adc;
> +	struct device_node *np = pdev->dev.of_node;
> +	struct iio_dev *indio_dev;
> +	int ret, i;
> +
> +	if (!np)
> +		return -ENODEV;
> +
> +	indio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
> +	if (IS_ERR(indio_dev)) {
> +		dev_err(dev, "%s: failed to allocate iio", __func__);

\n

> +		return PTR_ERR(indio_dev);
> +	}
> +
> +	indio_dev->name = np->name;
> +	indio_dev->dev.parent = dev;
> +	indio_dev->dev.of_node = np;
> +	indio_dev->info = &stm32_dfsdm_iio_info;
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	adc = iio_priv(indio_dev);
> +	if (IS_ERR(adc)) {
> +		dev_err(dev, "%s: failed to allocate adc", __func__);
> +		return PTR_ERR(adc);
> +	}
> +
> +	if (of_property_read_u32(np, "reg", &adc->fl_id)) {
> +		dev_err(&pdev->dev, "missing reg property\n");
> +		return -EINVAL;
> +	}
> +
> +	adc->dev = &indio_dev->dev;
> +	adc->dfsdm = dev_get_drvdata(pdev->dev.parent);
> +
> +	ret = stm32_dfsdm_adc_chan_init(indio_dev);
> +	if (ret < 0) {
> +		dev_err(dev, "iio channels init failed\n");
> +		return ret;
> +	}
> +
> +	ret = stm32_dfsdm_get_filter(adc->dfsdm, adc->fl_id);
> +	if (ret < 0)
> +		goto get_fl_err;
> +
> +	adc->int_oversampling = DFSDM_MIN_INT_OVERSAMPLING;
> +	adc->sinc.oversampling = DFSDM_MIN_FL_OVERSAMPLING;
> +
> +	init_completion(&adc->completion);
> +
> +	ret = devm_iio_device_register(dev, indio_dev);
> +	if (ret) {
> +		dev_err(adc->dev, "failed to register iio device\n");
> +		goto register_err;
> +	}
> +
> +	platform_set_drvdata(pdev, adc);
> +
> +	return 0;
> +
> +register_err:
> +	stm32_dfsdm_release_filter(adc->dfsdm, adc->fl_id);
> +
> +get_fl_err:
> +	for (i = 0; i < indio_dev->num_channels; i++)
> +		stm32_dfsdm_release_channel(adc->dfsdm, adc->inputs[i].id);
> +
> +	return ret;
> +}
> +
> +static int stm32_dfsdm_adc_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *indio_dev;
> +	struct stm32_dfsdm_adc *adc = platform_get_drvdata(pdev);
> +	int i;
> +
> +	indio_dev = iio_priv_to_dev(adc);
> +	for (i = 0; i < indio_dev->num_channels; i++)
> +		stm32_dfsdm_release_channel(adc->dfsdm, adc->inputs[i].id);
> +	stm32_dfsdm_release_filter(adc->dfsdm, adc->fl_id);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id stm32_dfsdm_adc_match[] = {
> +	{ .compatible = "st,stm32-dfsdm-adc"},
> +	{}
> +};
> +
> +static struct platform_driver stm32_dfsdm_adc_driver = {
> +	.driver = {
> +		.name = "stm32-dfsdm-adc",
> +		.of_match_table = stm32_dfsdm_adc_match,
> +	},
> +	.probe = stm32_dfsdm_adc_probe,
> +	.remove = stm32_dfsdm_adc_remove,
> +};
> +module_platform_driver(stm32_dfsdm_adc_driver);
> +
> +MODULE_DESCRIPTION("STM32 sigma delta ADC");
> +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
> +MODULE_LICENSE("GPL v2");
> 

-- 

Peter Meerwald-Stadler
+43-664-2444418 (mobile)

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

* Re: [alsa-devel] [PATCH 2/7] MFD: add STM32 DFSDM support
  2017-01-23 16:32     ` Arnaud Pouliquen
  (?)
@ 2017-01-24  0:36         ` kbuild test robot
  -1 siblings, 0 replies; 130+ messages in thread
From: kbuild test robot @ 2017-01-24  0:36 UTC (permalink / raw)
  Cc: kbuild-all-JC7UmRfGjtg, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Lee Jones, Rob Herring,
	Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown,
	arnaud.pouliquen-qxv4g6HH51o, Alexandre Torgue, Maxime Coquelin

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

Hi Arnaud,

[auto build test WARNING on iio/togreg]
[cannot apply to asoc/for-next ljones-mfd/for-mfd-next v4.10-rc5 next-20170123]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Arnaud-Pouliquen/Add-STM32-DFSDM-support/20170124-065537
base:   https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git togreg
config: i386-allmodconfig (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All warnings (new ones prefixed by >>):

   In file included from include/linux/linkage.h:6:0,
                    from include/linux/kernel.h:6,
                    from include/linux/clk.h:16,
                    from drivers/mfd/stm32-dfsdm.c:19:
   drivers/mfd/stm32-dfsdm.c:397:19: error: 'dfsdm_configure_filter' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_configure_filter);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
>> drivers/mfd/stm32-dfsdm.c:397:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_configure_filter);
    ^~~~~~~~~~~~~~~~~
   drivers/mfd/stm32-dfsdm.c:426:19: error: 'dfsdm_start_filter' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_start_filter);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
   drivers/mfd/stm32-dfsdm.c:426:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_start_filter);
    ^~~~~~~~~~~~~~~~~
   drivers/mfd/stm32-dfsdm.c:444:19: error: 'dfsdm_stop_filter' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_stop_filter);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
   drivers/mfd/stm32-dfsdm.c:444:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_stop_filter);
    ^~~~~~~~~~~~~~~~~
   drivers/mfd/stm32-dfsdm.c:470:19: error: 'dfsdm_read_fl_conv' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_read_fl_conv);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
   drivers/mfd/stm32-dfsdm.c:470:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_read_fl_conv);
    ^~~~~~~~~~~~~~~~~
   drivers/mfd/stm32-dfsdm.c:500:19: error: 'dfsdm_get_filter' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_get_filter);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
   drivers/mfd/stm32-dfsdm.c:500:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_get_filter);
    ^~~~~~~~~~~~~~~~~
   drivers/mfd/stm32-dfsdm.c:518:19: error: 'dfsdm_release_filter' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_release_filter);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
   drivers/mfd/stm32-dfsdm.c:518:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_release_filter);
    ^~~~~~~~~~~~~~~~~
   drivers/mfd/stm32-dfsdm.c:603:19: error: 'dfsdm_register_fl_event' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_register_fl_event);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
   drivers/mfd/stm32-dfsdm.c:603:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_register_fl_event);
    ^~~~~~~~~~~~~~~~~
   drivers/mfd/stm32-dfsdm.c:666:19: error: 'dfsdm_unregister_fl_event' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_unregister_fl_event);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
   drivers/mfd/stm32-dfsdm.c:666:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_unregister_fl_event);
    ^~~~~~~~~~~~~~~~~
   drivers/mfd/stm32-dfsdm.c:729:19: error: 'dfsdm_start_channel' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_start_channel);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
   drivers/mfd/stm32-dfsdm.c:729:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_start_channel);
    ^~~~~~~~~~~~~~~~~
   drivers/mfd/stm32-dfsdm.c:755:19: error: 'dfsdm_stop_channel' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_stop_channel);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
   drivers/mfd/stm32-dfsdm.c:755:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_stop_channel);
    ^~~~~~~~~~~~~~~~~
   drivers/mfd/stm32-dfsdm.c:825:19: error: 'dfsdm_get_channel' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_get_channel);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
   drivers/mfd/stm32-dfsdm.c:825:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_get_channel);
    ^~~~~~~~~~~~~~~~~
   drivers/mfd/stm32-dfsdm.c:843:19: error: 'dfsdm_release_channel' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_release_channel);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
   drivers/mfd/stm32-dfsdm.c:843:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_release_channel);

vim +/EXPORT_SYMBOL_GPL +397 drivers/mfd/stm32-dfsdm.c

   381		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FOSR_MASK,
   382				  DFSDM_FCR_FOSR((sparams->oversampling - 1)));
   383	
   384		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FORD_MASK,
   385				  DFSDM_FCR_FORD(sparams->order));
   386	
   387		/* Conversion */
   388		if (fl_cfg->inj_params)
   389			stm32_dfsdm_configure_inj_conv(priv, fl_id, fl_cfg->inj_params);
   390		else if (fl_cfg->reg_params)
   391			stm32_dfsdm_configure_reg_conv(priv, fl_id, fl_cfg->reg_params);
   392	
   393		priv->filters[fl_id].event = fl_cfg->event;
   394	
   395		return 0;
   396	}
 > 397	EXPORT_SYMBOL_GPL(dfsdm_configure_filter);
   398	
   399	/**
   400	 * stm32_dfsdm_start_filter - Start filter conversion.
   401	 *
   402	 * @dfsdm: Handle used to retrieve dfsdm context.
   403	 * @fl_id: Filter id.
   404	 * @conv: Conversion type regular or injected.
   405	 */

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 57000 bytes --]

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

* Re: [alsa-devel] [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-24  0:36         ` kbuild test robot
  0 siblings, 0 replies; 130+ messages in thread
From: kbuild test robot @ 2017-01-24  0:36 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: kbuild-all, devicetree, linux-arm-kernel, linux-iio, alsa-devel,
	Lee Jones, Rob Herring, Mark Rutland, Jonathan Cameron,
	Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
	Jaroslav Kysela, Takashi Iwai, Liam Girdwood, Mark Brown,
	arnaud.pouliquen, Alexandre Torgue, Maxime Coquelin

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

Hi Arnaud,

[auto build test WARNING on iio/togreg]
[cannot apply to asoc/for-next ljones-mfd/for-mfd-next v4.10-rc5 next-20170123]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Arnaud-Pouliquen/Add-STM32-DFSDM-support/20170124-065537
base:   https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git togreg
config: i386-allmodconfig (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All warnings (new ones prefixed by >>):

   In file included from include/linux/linkage.h:6:0,
                    from include/linux/kernel.h:6,
                    from include/linux/clk.h:16,
                    from drivers/mfd/stm32-dfsdm.c:19:
   drivers/mfd/stm32-dfsdm.c:397:19: error: 'dfsdm_configure_filter' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_configure_filter);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
>> drivers/mfd/stm32-dfsdm.c:397:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_configure_filter);
    ^~~~~~~~~~~~~~~~~
   drivers/mfd/stm32-dfsdm.c:426:19: error: 'dfsdm_start_filter' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_start_filter);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
   drivers/mfd/stm32-dfsdm.c:426:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_start_filter);
    ^~~~~~~~~~~~~~~~~
   drivers/mfd/stm32-dfsdm.c:444:19: error: 'dfsdm_stop_filter' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_stop_filter);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
   drivers/mfd/stm32-dfsdm.c:444:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_stop_filter);
    ^~~~~~~~~~~~~~~~~
   drivers/mfd/stm32-dfsdm.c:470:19: error: 'dfsdm_read_fl_conv' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_read_fl_conv);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
   drivers/mfd/stm32-dfsdm.c:470:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_read_fl_conv);
    ^~~~~~~~~~~~~~~~~
   drivers/mfd/stm32-dfsdm.c:500:19: error: 'dfsdm_get_filter' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_get_filter);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
   drivers/mfd/stm32-dfsdm.c:500:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_get_filter);
    ^~~~~~~~~~~~~~~~~
   drivers/mfd/stm32-dfsdm.c:518:19: error: 'dfsdm_release_filter' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_release_filter);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
   drivers/mfd/stm32-dfsdm.c:518:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_release_filter);
    ^~~~~~~~~~~~~~~~~
   drivers/mfd/stm32-dfsdm.c:603:19: error: 'dfsdm_register_fl_event' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_register_fl_event);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
   drivers/mfd/stm32-dfsdm.c:603:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_register_fl_event);
    ^~~~~~~~~~~~~~~~~
   drivers/mfd/stm32-dfsdm.c:666:19: error: 'dfsdm_unregister_fl_event' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_unregister_fl_event);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
   drivers/mfd/stm32-dfsdm.c:666:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_unregister_fl_event);
    ^~~~~~~~~~~~~~~~~
   drivers/mfd/stm32-dfsdm.c:729:19: error: 'dfsdm_start_channel' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_start_channel);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
   drivers/mfd/stm32-dfsdm.c:729:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_start_channel);
    ^~~~~~~~~~~~~~~~~
   drivers/mfd/stm32-dfsdm.c:755:19: error: 'dfsdm_stop_channel' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_stop_channel);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
   drivers/mfd/stm32-dfsdm.c:755:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_stop_channel);
    ^~~~~~~~~~~~~~~~~
   drivers/mfd/stm32-dfsdm.c:825:19: error: 'dfsdm_get_channel' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_get_channel);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
   drivers/mfd/stm32-dfsdm.c:825:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_get_channel);
    ^~~~~~~~~~~~~~~~~
   drivers/mfd/stm32-dfsdm.c:843:19: error: 'dfsdm_release_channel' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_release_channel);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
   drivers/mfd/stm32-dfsdm.c:843:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_release_channel);

vim +/EXPORT_SYMBOL_GPL +397 drivers/mfd/stm32-dfsdm.c

   381		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FOSR_MASK,
   382				  DFSDM_FCR_FOSR((sparams->oversampling - 1)));
   383	
   384		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FORD_MASK,
   385				  DFSDM_FCR_FORD(sparams->order));
   386	
   387		/* Conversion */
   388		if (fl_cfg->inj_params)
   389			stm32_dfsdm_configure_inj_conv(priv, fl_id, fl_cfg->inj_params);
   390		else if (fl_cfg->reg_params)
   391			stm32_dfsdm_configure_reg_conv(priv, fl_id, fl_cfg->reg_params);
   392	
   393		priv->filters[fl_id].event = fl_cfg->event;
   394	
   395		return 0;
   396	}
 > 397	EXPORT_SYMBOL_GPL(dfsdm_configure_filter);
   398	
   399	/**
   400	 * stm32_dfsdm_start_filter - Start filter conversion.
   401	 *
   402	 * @dfsdm: Handle used to retrieve dfsdm context.
   403	 * @fl_id: Filter id.
   404	 * @conv: Conversion type regular or injected.
   405	 */

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 57000 bytes --]

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

* [alsa-devel] [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-24  0:36         ` kbuild test robot
  0 siblings, 0 replies; 130+ messages in thread
From: kbuild test robot @ 2017-01-24  0:36 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Arnaud,

[auto build test WARNING on iio/togreg]
[cannot apply to asoc/for-next ljones-mfd/for-mfd-next v4.10-rc5 next-20170123]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Arnaud-Pouliquen/Add-STM32-DFSDM-support/20170124-065537
base:   https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git togreg
config: i386-allmodconfig (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All warnings (new ones prefixed by >>):

   In file included from include/linux/linkage.h:6:0,
                    from include/linux/kernel.h:6,
                    from include/linux/clk.h:16,
                    from drivers/mfd/stm32-dfsdm.c:19:
   drivers/mfd/stm32-dfsdm.c:397:19: error: 'dfsdm_configure_filter' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_configure_filter);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
>> drivers/mfd/stm32-dfsdm.c:397:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_configure_filter);
    ^~~~~~~~~~~~~~~~~
   drivers/mfd/stm32-dfsdm.c:426:19: error: 'dfsdm_start_filter' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_start_filter);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
   drivers/mfd/stm32-dfsdm.c:426:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_start_filter);
    ^~~~~~~~~~~~~~~~~
   drivers/mfd/stm32-dfsdm.c:444:19: error: 'dfsdm_stop_filter' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_stop_filter);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
   drivers/mfd/stm32-dfsdm.c:444:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_stop_filter);
    ^~~~~~~~~~~~~~~~~
   drivers/mfd/stm32-dfsdm.c:470:19: error: 'dfsdm_read_fl_conv' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_read_fl_conv);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
   drivers/mfd/stm32-dfsdm.c:470:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_read_fl_conv);
    ^~~~~~~~~~~~~~~~~
   drivers/mfd/stm32-dfsdm.c:500:19: error: 'dfsdm_get_filter' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_get_filter);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
   drivers/mfd/stm32-dfsdm.c:500:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_get_filter);
    ^~~~~~~~~~~~~~~~~
   drivers/mfd/stm32-dfsdm.c:518:19: error: 'dfsdm_release_filter' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_release_filter);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
   drivers/mfd/stm32-dfsdm.c:518:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_release_filter);
    ^~~~~~~~~~~~~~~~~
   drivers/mfd/stm32-dfsdm.c:603:19: error: 'dfsdm_register_fl_event' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_register_fl_event);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
   drivers/mfd/stm32-dfsdm.c:603:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_register_fl_event);
    ^~~~~~~~~~~~~~~~~
   drivers/mfd/stm32-dfsdm.c:666:19: error: 'dfsdm_unregister_fl_event' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_unregister_fl_event);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
   drivers/mfd/stm32-dfsdm.c:666:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_unregister_fl_event);
    ^~~~~~~~~~~~~~~~~
   drivers/mfd/stm32-dfsdm.c:729:19: error: 'dfsdm_start_channel' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_start_channel);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
   drivers/mfd/stm32-dfsdm.c:729:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_start_channel);
    ^~~~~~~~~~~~~~~~~
   drivers/mfd/stm32-dfsdm.c:755:19: error: 'dfsdm_stop_channel' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_stop_channel);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
   drivers/mfd/stm32-dfsdm.c:755:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_stop_channel);
    ^~~~~~~~~~~~~~~~~
   drivers/mfd/stm32-dfsdm.c:825:19: error: 'dfsdm_get_channel' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_get_channel);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
   drivers/mfd/stm32-dfsdm.c:825:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_get_channel);
    ^~~~~~~~~~~~~~~~~
   drivers/mfd/stm32-dfsdm.c:843:19: error: 'dfsdm_release_channel' undeclared here (not in a function)
    EXPORT_SYMBOL_GPL(dfsdm_release_channel);
                      ^
   include/linux/export.h:58:16: note: in definition of macro '___EXPORT_SYMBOL'
     extern typeof(sym) sym;      \
                   ^~~
   drivers/mfd/stm32-dfsdm.c:843:1: note: in expansion of macro 'EXPORT_SYMBOL_GPL'
    EXPORT_SYMBOL_GPL(dfsdm_release_channel);

vim +/EXPORT_SYMBOL_GPL +397 drivers/mfd/stm32-dfsdm.c

   381		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FOSR_MASK,
   382				  DFSDM_FCR_FOSR((sparams->oversampling - 1)));
   383	
   384		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FORD_MASK,
   385				  DFSDM_FCR_FORD(sparams->order));
   386	
   387		/* Conversion */
   388		if (fl_cfg->inj_params)
   389			stm32_dfsdm_configure_inj_conv(priv, fl_id, fl_cfg->inj_params);
   390		else if (fl_cfg->reg_params)
   391			stm32_dfsdm_configure_reg_conv(priv, fl_id, fl_cfg->reg_params);
   392	
   393		priv->filters[fl_id].event = fl_cfg->event;
   394	
   395		return 0;
   396	}
 > 397	EXPORT_SYMBOL_GPL(dfsdm_configure_filter);
   398	
   399	/**
   400	 * stm32_dfsdm_start_filter - Start filter conversion.
   401	 *
   402	 * @dfsdm: Handle used to retrieve dfsdm context.
   403	 * @fl_id: Filter id.
   404	 * @conv: Conversion type regular or injected.
   405	 */

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
-------------- next part --------------
A non-text attachment was scrubbed...
Name: .config.gz
Type: application/gzip
Size: 57000 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170124/150c0b19/attachment-0001.gz>

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

* Re: [alsa-devel] [PATCH 5/7] ASoC: dmaengine_pcm: add copy support
  2017-01-23 16:32   ` Arnaud Pouliquen
  (?)
@ 2017-01-24  2:14       ` kbuild test robot
  -1 siblings, 0 replies; 130+ messages in thread
From: kbuild test robot @ 2017-01-24  2:14 UTC (permalink / raw)
  Cc: kbuild-all-JC7UmRfGjtg, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Lee Jones, Rob Herring,
	Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown,
	arnaud.pouliquen-qxv4g6HH51o, Alexandre Torgue, Maxime Coquelin

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

Hi olivier,

[auto build test WARNING on iio/togreg]
[cannot apply to asoc/for-next ljones-mfd/for-mfd-next v4.10-rc5 next-20170123]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Arnaud-Pouliquen/Add-STM32-DFSDM-support/20170124-065537
base:   https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git togreg
reproduce: make htmldocs

All warnings (new ones prefixed by >>):

>> include/sound/dmaengine_pcm.h:152: warning: No description found for parameter 'copy'
   include/sound/compress_driver.h:162: warning: No description found for parameter 'id[64]'
   include/sound/compress_driver.h:162: warning: No description found for parameter 'proc_root'
   include/sound/compress_driver.h:162: warning: No description found for parameter 'proc_info_entry'
   include/sound/core.h:323: warning: No description found for parameter '...'
   include/sound/core.h:334: warning: No description found for parameter '...'
   include/sound/core.h:387: warning: No description found for parameter '...'

vim +/copy +152 include/sound/dmaengine_pcm.h

28c4468b Lars-Peter Clausen 2013-04-15  136  struct snd_dmaengine_pcm_config {
28c4468b Lars-Peter Clausen 2013-04-15  137  	int (*prepare_slave_config)(struct snd_pcm_substream *substream,
28c4468b Lars-Peter Clausen 2013-04-15  138  			struct snd_pcm_hw_params *params,
28c4468b Lars-Peter Clausen 2013-04-15  139  			struct dma_slave_config *slave_config);
553a8492 olivier moysan     2017-01-23  140  	int (*copy)(struct snd_pcm_substream *substream, int channel,
553a8492 olivier moysan     2017-01-23  141  		    snd_pcm_uframes_t pos,
553a8492 olivier moysan     2017-01-23  142  		    void __user *buf, snd_pcm_uframes_t count);
c999836d Lars-Peter Clausen 2013-04-15  143  	struct dma_chan *(*compat_request_channel)(
c999836d Lars-Peter Clausen 2013-04-15  144  			struct snd_soc_pcm_runtime *rtd,
c999836d Lars-Peter Clausen 2013-04-15  145  			struct snd_pcm_substream *substream);
c999836d Lars-Peter Clausen 2013-04-15  146  	dma_filter_fn compat_filter_fn;
194c7dea Stephen Warren     2013-12-03  147  	struct device *dma_dev;
194c7dea Stephen Warren     2013-12-03  148  	const char *chan_names[SNDRV_PCM_STREAM_LAST + 1];
28c4468b Lars-Peter Clausen 2013-04-15  149  
28c4468b Lars-Peter Clausen 2013-04-15  150  	const struct snd_pcm_hardware *pcm_hardware;
28c4468b Lars-Peter Clausen 2013-04-15  151  	unsigned int prealloc_buffer_size;
28c4468b Lars-Peter Clausen 2013-04-15 @152  };
28c4468b Lars-Peter Clausen 2013-04-15  153  
28c4468b Lars-Peter Clausen 2013-04-15  154  int snd_dmaengine_pcm_register(struct device *dev,
28c4468b Lars-Peter Clausen 2013-04-15  155  	const struct snd_dmaengine_pcm_config *config,
28c4468b Lars-Peter Clausen 2013-04-15  156  	unsigned int flags);
28c4468b Lars-Peter Clausen 2013-04-15  157  void snd_dmaengine_pcm_unregister(struct device *dev);
28c4468b Lars-Peter Clausen 2013-04-15  158  
21585ee8 Lars-Peter Clausen 2013-11-28  159  int devm_snd_dmaengine_pcm_register(struct device *dev,
21585ee8 Lars-Peter Clausen 2013-11-28  160  	const struct snd_dmaengine_pcm_config *config,

:::::: The code at line 152 was first introduced by commit
:::::: 28c4468b00a1e55e08cc20117de968f7c6275441 ASoC: Add a generic dmaengine_pcm driver

:::::: TO: Lars-Peter Clausen <lars-Qo5EllUWu/uELgA04lAiVw@public.gmane.org>
:::::: CC: Mark Brown <broonie-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 6425 bytes --]

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

* Re: [alsa-devel] [PATCH 5/7] ASoC: dmaengine_pcm: add copy support
@ 2017-01-24  2:14       ` kbuild test robot
  0 siblings, 0 replies; 130+ messages in thread
From: kbuild test robot @ 2017-01-24  2:14 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: kbuild-all, devicetree, linux-arm-kernel, linux-iio, alsa-devel,
	Lee Jones, Rob Herring, Mark Rutland, Jonathan Cameron,
	Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
	Jaroslav Kysela, Takashi Iwai, Liam Girdwood, Mark Brown,
	arnaud.pouliquen, Alexandre Torgue, Maxime Coquelin

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

Hi olivier,

[auto build test WARNING on iio/togreg]
[cannot apply to asoc/for-next ljones-mfd/for-mfd-next v4.10-rc5 next-20170123]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Arnaud-Pouliquen/Add-STM32-DFSDM-support/20170124-065537
base:   https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git togreg
reproduce: make htmldocs

All warnings (new ones prefixed by >>):

>> include/sound/dmaengine_pcm.h:152: warning: No description found for parameter 'copy'
   include/sound/compress_driver.h:162: warning: No description found for parameter 'id[64]'
   include/sound/compress_driver.h:162: warning: No description found for parameter 'proc_root'
   include/sound/compress_driver.h:162: warning: No description found for parameter 'proc_info_entry'
   include/sound/core.h:323: warning: No description found for parameter '...'
   include/sound/core.h:334: warning: No description found for parameter '...'
   include/sound/core.h:387: warning: No description found for parameter '...'

vim +/copy +152 include/sound/dmaengine_pcm.h

28c4468b Lars-Peter Clausen 2013-04-15  136  struct snd_dmaengine_pcm_config {
28c4468b Lars-Peter Clausen 2013-04-15  137  	int (*prepare_slave_config)(struct snd_pcm_substream *substream,
28c4468b Lars-Peter Clausen 2013-04-15  138  			struct snd_pcm_hw_params *params,
28c4468b Lars-Peter Clausen 2013-04-15  139  			struct dma_slave_config *slave_config);
553a8492 olivier moysan     2017-01-23  140  	int (*copy)(struct snd_pcm_substream *substream, int channel,
553a8492 olivier moysan     2017-01-23  141  		    snd_pcm_uframes_t pos,
553a8492 olivier moysan     2017-01-23  142  		    void __user *buf, snd_pcm_uframes_t count);
c999836d Lars-Peter Clausen 2013-04-15  143  	struct dma_chan *(*compat_request_channel)(
c999836d Lars-Peter Clausen 2013-04-15  144  			struct snd_soc_pcm_runtime *rtd,
c999836d Lars-Peter Clausen 2013-04-15  145  			struct snd_pcm_substream *substream);
c999836d Lars-Peter Clausen 2013-04-15  146  	dma_filter_fn compat_filter_fn;
194c7dea Stephen Warren     2013-12-03  147  	struct device *dma_dev;
194c7dea Stephen Warren     2013-12-03  148  	const char *chan_names[SNDRV_PCM_STREAM_LAST + 1];
28c4468b Lars-Peter Clausen 2013-04-15  149  
28c4468b Lars-Peter Clausen 2013-04-15  150  	const struct snd_pcm_hardware *pcm_hardware;
28c4468b Lars-Peter Clausen 2013-04-15  151  	unsigned int prealloc_buffer_size;
28c4468b Lars-Peter Clausen 2013-04-15 @152  };
28c4468b Lars-Peter Clausen 2013-04-15  153  
28c4468b Lars-Peter Clausen 2013-04-15  154  int snd_dmaengine_pcm_register(struct device *dev,
28c4468b Lars-Peter Clausen 2013-04-15  155  	const struct snd_dmaengine_pcm_config *config,
28c4468b Lars-Peter Clausen 2013-04-15  156  	unsigned int flags);
28c4468b Lars-Peter Clausen 2013-04-15  157  void snd_dmaengine_pcm_unregister(struct device *dev);
28c4468b Lars-Peter Clausen 2013-04-15  158  
21585ee8 Lars-Peter Clausen 2013-11-28  159  int devm_snd_dmaengine_pcm_register(struct device *dev,
21585ee8 Lars-Peter Clausen 2013-11-28  160  	const struct snd_dmaengine_pcm_config *config,

:::::: The code at line 152 was first introduced by commit
:::::: 28c4468b00a1e55e08cc20117de968f7c6275441 ASoC: Add a generic dmaengine_pcm driver

:::::: TO: Lars-Peter Clausen <lars@metafoo.de>
:::::: CC: Mark Brown <broonie@opensource.wolfsonmicro.com>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 6425 bytes --]

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

* [alsa-devel] [PATCH 5/7] ASoC: dmaengine_pcm: add copy support
@ 2017-01-24  2:14       ` kbuild test robot
  0 siblings, 0 replies; 130+ messages in thread
From: kbuild test robot @ 2017-01-24  2:14 UTC (permalink / raw)
  To: linux-arm-kernel

Hi olivier,

[auto build test WARNING on iio/togreg]
[cannot apply to asoc/for-next ljones-mfd/for-mfd-next v4.10-rc5 next-20170123]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Arnaud-Pouliquen/Add-STM32-DFSDM-support/20170124-065537
base:   https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git togreg
reproduce: make htmldocs

All warnings (new ones prefixed by >>):

>> include/sound/dmaengine_pcm.h:152: warning: No description found for parameter 'copy'
   include/sound/compress_driver.h:162: warning: No description found for parameter 'id[64]'
   include/sound/compress_driver.h:162: warning: No description found for parameter 'proc_root'
   include/sound/compress_driver.h:162: warning: No description found for parameter 'proc_info_entry'
   include/sound/core.h:323: warning: No description found for parameter '...'
   include/sound/core.h:334: warning: No description found for parameter '...'
   include/sound/core.h:387: warning: No description found for parameter '...'

vim +/copy +152 include/sound/dmaengine_pcm.h

28c4468b Lars-Peter Clausen 2013-04-15  136  struct snd_dmaengine_pcm_config {
28c4468b Lars-Peter Clausen 2013-04-15  137  	int (*prepare_slave_config)(struct snd_pcm_substream *substream,
28c4468b Lars-Peter Clausen 2013-04-15  138  			struct snd_pcm_hw_params *params,
28c4468b Lars-Peter Clausen 2013-04-15  139  			struct dma_slave_config *slave_config);
553a8492 olivier moysan     2017-01-23  140  	int (*copy)(struct snd_pcm_substream *substream, int channel,
553a8492 olivier moysan     2017-01-23  141  		    snd_pcm_uframes_t pos,
553a8492 olivier moysan     2017-01-23  142  		    void __user *buf, snd_pcm_uframes_t count);
c999836d Lars-Peter Clausen 2013-04-15  143  	struct dma_chan *(*compat_request_channel)(
c999836d Lars-Peter Clausen 2013-04-15  144  			struct snd_soc_pcm_runtime *rtd,
c999836d Lars-Peter Clausen 2013-04-15  145  			struct snd_pcm_substream *substream);
c999836d Lars-Peter Clausen 2013-04-15  146  	dma_filter_fn compat_filter_fn;
194c7dea Stephen Warren     2013-12-03  147  	struct device *dma_dev;
194c7dea Stephen Warren     2013-12-03  148  	const char *chan_names[SNDRV_PCM_STREAM_LAST + 1];
28c4468b Lars-Peter Clausen 2013-04-15  149  
28c4468b Lars-Peter Clausen 2013-04-15  150  	const struct snd_pcm_hardware *pcm_hardware;
28c4468b Lars-Peter Clausen 2013-04-15  151  	unsigned int prealloc_buffer_size;
28c4468b Lars-Peter Clausen 2013-04-15 @152  };
28c4468b Lars-Peter Clausen 2013-04-15  153  
28c4468b Lars-Peter Clausen 2013-04-15  154  int snd_dmaengine_pcm_register(struct device *dev,
28c4468b Lars-Peter Clausen 2013-04-15  155  	const struct snd_dmaengine_pcm_config *config,
28c4468b Lars-Peter Clausen 2013-04-15  156  	unsigned int flags);
28c4468b Lars-Peter Clausen 2013-04-15  157  void snd_dmaengine_pcm_unregister(struct device *dev);
28c4468b Lars-Peter Clausen 2013-04-15  158  
21585ee8 Lars-Peter Clausen 2013-11-28  159  int devm_snd_dmaengine_pcm_register(struct device *dev,
21585ee8 Lars-Peter Clausen 2013-11-28  160  	const struct snd_dmaengine_pcm_config *config,

:::::: The code at line 152 was first introduced by commit
:::::: 28c4468b00a1e55e08cc20117de968f7c6275441 ASoC: Add a generic dmaengine_pcm driver

:::::: TO: Lars-Peter Clausen <lars@metafoo.de>
:::::: CC: Mark Brown <broonie@opensource.wolfsonmicro.com>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
-------------- next part --------------
A non-text attachment was scrubbed...
Name: .config.gz
Type: application/gzip
Size: 6425 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170124/7b032151/attachment.gz>

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
  2017-01-23 16:32     ` Arnaud Pouliquen
  (?)
@ 2017-01-24  8:22         ` Lee Jones
  -1 siblings, 0 replies; 130+ messages in thread
From: Lee Jones @ 2017-01-24  8:22 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Rob Herring, Mark Rutland,
	Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Jaroslav Kysela, Takashi Iwai,
	Liam Girdwood, Mark Brown, Maxime Coquelin, Alexandre Torgue

On Mon, 23 Jan 2017, Arnaud Pouliquen wrote:

> DFSDM hardware IP can be used at the same time for ADC sigma delta

Same time as what?

> conversion and audio PDM microphone.
> MFD driver is in charge of configuring IP registers and managing IP clocks.
> For this it exports an API to handles filters and channels resources.

This looks like an ADC driver?  What is it that makes it an MFD?

> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
> ---
>  drivers/mfd/Kconfig             |   11 +
>  drivers/mfd/Makefile            |    2 +
>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
>  5 files changed, 1601 insertions(+)
>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
>  create mode 100644 drivers/mfd/stm32-dfsdm.c
>  create mode 100644 include/linux/mfd/stm32-dfsdm.h
> 
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index c6df644..4bb660b 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -1607,6 +1607,17 @@ config MFD_STW481X
>  	  in various ST Microelectronics and ST-Ericsson embedded
>  	  Nomadik series.
>  
> +config MFD_STM32_DFSDM
> +	tristate "ST Microelectronics STM32 DFSDM"
> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
> +	select MFD_CORE
> +	select REGMAP
> +	select REGMAP_MMIO
> +	help
> +	  Select this option to enable the STM32 Digital Filter
> +	  for Sigma Delta Modulators (DFSDM) driver used
> +	  in various STM32 series.
> +
>  menu "Multimedia Capabilities Port drivers"
>  	depends on ARCH_SA1100

[...]

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
--
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] 130+ messages in thread

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-24  8:22         ` Lee Jones
  0 siblings, 0 replies; 130+ messages in thread
From: Lee Jones @ 2017-01-24  8:22 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, Rob Herring,
	Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown, Maxime Coquelin,
	Alexandre Torgue

On Mon, 23 Jan 2017, Arnaud Pouliquen wrote:

> DFSDM hardware IP can be used at the same time for ADC sigma delta

Same time as what?

> conversion and audio PDM microphone.
> MFD driver is in charge of configuring IP registers and managing IP clocks.
> For this it exports an API to handles filters and channels resources.

This looks like an ADC driver?  What is it that makes it an MFD?

> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
>  drivers/mfd/Kconfig             |   11 +
>  drivers/mfd/Makefile            |    2 +
>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
>  5 files changed, 1601 insertions(+)
>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
>  create mode 100644 drivers/mfd/stm32-dfsdm.c
>  create mode 100644 include/linux/mfd/stm32-dfsdm.h
> 
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index c6df644..4bb660b 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -1607,6 +1607,17 @@ config MFD_STW481X
>  	  in various ST Microelectronics and ST-Ericsson embedded
>  	  Nomadik series.
>  
> +config MFD_STM32_DFSDM
> +	tristate "ST Microelectronics STM32 DFSDM"
> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
> +	select MFD_CORE
> +	select REGMAP
> +	select REGMAP_MMIO
> +	help
> +	  Select this option to enable the STM32 Digital Filter
> +	  for Sigma Delta Modulators (DFSDM) driver used
> +	  in various STM32 series.
> +
>  menu "Multimedia Capabilities Port drivers"
>  	depends on ARCH_SA1100

[...]

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

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

* [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-24  8:22         ` Lee Jones
  0 siblings, 0 replies; 130+ messages in thread
From: Lee Jones @ 2017-01-24  8:22 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, 23 Jan 2017, Arnaud Pouliquen wrote:

> DFSDM hardware IP can be used at the same time for ADC sigma delta

Same time as what?

> conversion and audio PDM microphone.
> MFD driver is in charge of configuring IP registers and managing IP clocks.
> For this it exports an API to handles filters and channels resources.

This looks like an ADC driver?  What is it that makes it an MFD?

> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
>  drivers/mfd/Kconfig             |   11 +
>  drivers/mfd/Makefile            |    2 +
>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
>  5 files changed, 1601 insertions(+)
>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
>  create mode 100644 drivers/mfd/stm32-dfsdm.c
>  create mode 100644 include/linux/mfd/stm32-dfsdm.h
> 
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index c6df644..4bb660b 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -1607,6 +1607,17 @@ config MFD_STW481X
>  	  in various ST Microelectronics and ST-Ericsson embedded
>  	  Nomadik series.
>  
> +config MFD_STM32_DFSDM
> +	tristate "ST Microelectronics STM32 DFSDM"
> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
> +	select MFD_CORE
> +	select REGMAP
> +	select REGMAP_MMIO
> +	help
> +	  Select this option to enable the STM32 Digital Filter
> +	  for Sigma Delta Modulators (DFSDM) driver used
> +	  in various STM32 series.
> +
>  menu "Multimedia Capabilities Port drivers"
>  	depends on ARCH_SA1100

[...]

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
  2017-01-24  8:22         ` Lee Jones
  (?)
@ 2017-01-24  8:30           ` Arnaud Pouliquen
  -1 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-24  8:30 UTC (permalink / raw)
  To: Lee Jones
  Cc: Mark Rutland, devicetree, alsa-devel, Lars-Peter Clausen,
	Maxime Coquelin, Liam Girdwood, linux-iio, Mark Brown,
	Takashi Iwai, Jaroslav Kysela, Rob Herring, Jonathan Cameron,
	Peter Meerwald-Stadler, Hartmut Knaack, linux-arm-kernel,
	Alexandre TORGUE

Hello Lee,

On 01/24/2017 09:22 AM, Lee Jones wrote:
> On Mon, 23 Jan 2017, Arnaud Pouliquen wrote:
> 
>> DFSDM hardware IP can be used at the same time for ADC sigma delta
> 
> Same time as what?
DFSDM is used for ADC acquisition (through IIO) but also PDM microphone
capture (through ASOC).
> 
>> conversion and audio PDM microphone.
>> MFD driver is in charge of configuring IP registers and managing IP clocks.
>> For this it exports an API to handles filters and channels resources.
> 
> This looks like an ADC driver?  What is it that makes it an MFD?
Yes it a kind of ADC but that supports 2 features audio and iio.
So it has to support 2 features based on 2 separate Frameworks.

> 
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> ---
>>  drivers/mfd/Kconfig             |   11 +
>>  drivers/mfd/Makefile            |    2 +
>>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
>>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
>>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
>>  5 files changed, 1601 insertions(+)
>>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
>>  create mode 100644 drivers/mfd/stm32-dfsdm.c
>>  create mode 100644 include/linux/mfd/stm32-dfsdm.h
>>
>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>> index c6df644..4bb660b 100644
>> --- a/drivers/mfd/Kconfig
>> +++ b/drivers/mfd/Kconfig
>> @@ -1607,6 +1607,17 @@ config MFD_STW481X
>>  	  in various ST Microelectronics and ST-Ericsson embedded
>>  	  Nomadik series.
>>  
>> +config MFD_STM32_DFSDM
>> +	tristate "ST Microelectronics STM32 DFSDM"
>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>> +	select MFD_CORE
>> +	select REGMAP
>> +	select REGMAP_MMIO
>> +	help
>> +	  Select this option to enable the STM32 Digital Filter
>> +	  for Sigma Delta Modulators (DFSDM) driver used
>> +	  in various STM32 series.
>> +
>>  menu "Multimedia Capabilities Port drivers"
>>  	depends on ARCH_SA1100
> 
> [...]
> 

Regards
Arnaud

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-24  8:30           ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-24  8:30 UTC (permalink / raw)
  To: Lee Jones
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, Rob Herring,
	Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown, Maxime Coquelin,
	Alexandre TORGUE

Hello Lee,

On 01/24/2017 09:22 AM, Lee Jones wrote:
> On Mon, 23 Jan 2017, Arnaud Pouliquen wrote:
> 
>> DFSDM hardware IP can be used at the same time for ADC sigma delta
> 
> Same time as what?
DFSDM is used for ADC acquisition (through IIO) but also PDM microphone
capture (through ASOC).
> 
>> conversion and audio PDM microphone.
>> MFD driver is in charge of configuring IP registers and managing IP clocks.
>> For this it exports an API to handles filters and channels resources.
> 
> This looks like an ADC driver?  What is it that makes it an MFD?
Yes it a kind of ADC but that supports 2 features audio and iio.
So it has to support 2 features based on 2 separate Frameworks.

> 
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> ---
>>  drivers/mfd/Kconfig             |   11 +
>>  drivers/mfd/Makefile            |    2 +
>>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
>>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
>>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
>>  5 files changed, 1601 insertions(+)
>>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
>>  create mode 100644 drivers/mfd/stm32-dfsdm.c
>>  create mode 100644 include/linux/mfd/stm32-dfsdm.h
>>
>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>> index c6df644..4bb660b 100644
>> --- a/drivers/mfd/Kconfig
>> +++ b/drivers/mfd/Kconfig
>> @@ -1607,6 +1607,17 @@ config MFD_STW481X
>>  	  in various ST Microelectronics and ST-Ericsson embedded
>>  	  Nomadik series.
>>  
>> +config MFD_STM32_DFSDM
>> +	tristate "ST Microelectronics STM32 DFSDM"
>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>> +	select MFD_CORE
>> +	select REGMAP
>> +	select REGMAP_MMIO
>> +	help
>> +	  Select this option to enable the STM32 Digital Filter
>> +	  for Sigma Delta Modulators (DFSDM) driver used
>> +	  in various STM32 series.
>> +
>>  menu "Multimedia Capabilities Port drivers"
>>  	depends on ARCH_SA1100
> 
> [...]
> 

Regards
Arnaud

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

* [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-24  8:30           ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-24  8:30 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Lee,

On 01/24/2017 09:22 AM, Lee Jones wrote:
> On Mon, 23 Jan 2017, Arnaud Pouliquen wrote:
> 
>> DFSDM hardware IP can be used at the same time for ADC sigma delta
> 
> Same time as what?
DFSDM is used for ADC acquisition (through IIO) but also PDM microphone
capture (through ASOC).
> 
>> conversion and audio PDM microphone.
>> MFD driver is in charge of configuring IP registers and managing IP clocks.
>> For this it exports an API to handles filters and channels resources.
> 
> This looks like an ADC driver?  What is it that makes it an MFD?
Yes it a kind of ADC but that supports 2 features audio and iio.
So it has to support 2 features based on 2 separate Frameworks.

> 
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> ---
>>  drivers/mfd/Kconfig             |   11 +
>>  drivers/mfd/Makefile            |    2 +
>>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
>>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
>>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
>>  5 files changed, 1601 insertions(+)
>>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
>>  create mode 100644 drivers/mfd/stm32-dfsdm.c
>>  create mode 100644 include/linux/mfd/stm32-dfsdm.h
>>
>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>> index c6df644..4bb660b 100644
>> --- a/drivers/mfd/Kconfig
>> +++ b/drivers/mfd/Kconfig
>> @@ -1607,6 +1607,17 @@ config MFD_STW481X
>>  	  in various ST Microelectronics and ST-Ericsson embedded
>>  	  Nomadik series.
>>  
>> +config MFD_STM32_DFSDM
>> +	tristate "ST Microelectronics STM32 DFSDM"
>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>> +	select MFD_CORE
>> +	select REGMAP
>> +	select REGMAP_MMIO
>> +	help
>> +	  Select this option to enable the STM32 Digital Filter
>> +	  for Sigma Delta Modulators (DFSDM) driver used
>> +	  in various STM32 series.
>> +
>>  menu "Multimedia Capabilities Port drivers"
>>  	depends on ARCH_SA1100
> 
> [...]
> 

Regards
Arnaud

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
  2017-01-24  8:30           ` Arnaud Pouliquen
  (?)
@ 2017-01-24 11:36               ` Lee Jones
  -1 siblings, 0 replies; 130+ messages in thread
From: Lee Jones @ 2017-01-24 11:36 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Rob Herring, Mark Rutland,
	Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Jaroslav Kysela, Takashi Iwai,
	Liam Girdwood, Mark Brown, Maxime Coquelin, Alexandre TORGUE

On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
> On 01/24/2017 09:22 AM, Lee Jones wrote:
> > On Mon, 23 Jan 2017, Arnaud Pouliquen wrote:
> > 
> >> DFSDM hardware IP can be used at the same time for ADC sigma delta
> > 
> > Same time as what?
> DFSDM is used for ADC acquisition (through IIO) but also PDM microphone
> capture (through ASOC).
> > 
> >> conversion and audio PDM microphone.
> >> MFD driver is in charge of configuring IP registers and managing IP clocks.
> >> For this it exports an API to handles filters and channels resources.
> > 
> > This looks like an ADC driver?  What is it that makes it an MFD?
> Yes it a kind of ADC but that supports 2 features audio and iio.
> So it has to support 2 features based on 2 separate Frameworks.

I'm still unsure why it needs to live in MFD.

By the looks of it, this driver needs to move into IIO and you need to
call into it from ASoC.

> >> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
> >> ---
> >>  drivers/mfd/Kconfig             |   11 +
> >>  drivers/mfd/Makefile            |    2 +
> >>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
> >>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
> >>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
> >>  5 files changed, 1601 insertions(+)
> >>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
> >>  create mode 100644 drivers/mfd/stm32-dfsdm.c
> >>  create mode 100644 include/linux/mfd/stm32-dfsdm.h
> >>
> >> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> >> index c6df644..4bb660b 100644
> >> --- a/drivers/mfd/Kconfig
> >> +++ b/drivers/mfd/Kconfig
> >> @@ -1607,6 +1607,17 @@ config MFD_STW481X
> >>  	  in various ST Microelectronics and ST-Ericsson embedded
> >>  	  Nomadik series.
> >>  
> >> +config MFD_STM32_DFSDM
> >> +	tristate "ST Microelectronics STM32 DFSDM"
> >> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
> >> +	select MFD_CORE
> >> +	select REGMAP
> >> +	select REGMAP_MMIO
> >> +	help
> >> +	  Select this option to enable the STM32 Digital Filter
> >> +	  for Sigma Delta Modulators (DFSDM) driver used
> >> +	  in various STM32 series.
> >> +
> >>  menu "Multimedia Capabilities Port drivers"
> >>  	depends on ARCH_SA1100
> > 
> > [...]
> > 
> 
> Regards
> Arnaud

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-24 11:36               ` Lee Jones
  0 siblings, 0 replies; 130+ messages in thread
From: Lee Jones @ 2017-01-24 11:36 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, Rob Herring,
	Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown, Maxime Coquelin,
	Alexandre TORGUE

On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
> On 01/24/2017 09:22 AM, Lee Jones wrote:
> > On Mon, 23 Jan 2017, Arnaud Pouliquen wrote:
> > 
> >> DFSDM hardware IP can be used at the same time for ADC sigma delta
> > 
> > Same time as what?
> DFSDM is used for ADC acquisition (through IIO) but also PDM microphone
> capture (through ASOC).
> > 
> >> conversion and audio PDM microphone.
> >> MFD driver is in charge of configuring IP registers and managing IP clocks.
> >> For this it exports an API to handles filters and channels resources.
> > 
> > This looks like an ADC driver?  What is it that makes it an MFD?
> Yes it a kind of ADC but that supports 2 features audio and iio.
> So it has to support 2 features based on 2 separate Frameworks.

I'm still unsure why it needs to live in MFD.

By the looks of it, this driver needs to move into IIO and you need to
call into it from ASoC.

> >> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> >> ---
> >>  drivers/mfd/Kconfig             |   11 +
> >>  drivers/mfd/Makefile            |    2 +
> >>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
> >>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
> >>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
> >>  5 files changed, 1601 insertions(+)
> >>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
> >>  create mode 100644 drivers/mfd/stm32-dfsdm.c
> >>  create mode 100644 include/linux/mfd/stm32-dfsdm.h
> >>
> >> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> >> index c6df644..4bb660b 100644
> >> --- a/drivers/mfd/Kconfig
> >> +++ b/drivers/mfd/Kconfig
> >> @@ -1607,6 +1607,17 @@ config MFD_STW481X
> >>  	  in various ST Microelectronics and ST-Ericsson embedded
> >>  	  Nomadik series.
> >>  
> >> +config MFD_STM32_DFSDM
> >> +	tristate "ST Microelectronics STM32 DFSDM"
> >> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
> >> +	select MFD_CORE
> >> +	select REGMAP
> >> +	select REGMAP_MMIO
> >> +	help
> >> +	  Select this option to enable the STM32 Digital Filter
> >> +	  for Sigma Delta Modulators (DFSDM) driver used
> >> +	  in various STM32 series.
> >> +
> >>  menu "Multimedia Capabilities Port drivers"
> >>  	depends on ARCH_SA1100
> > 
> > [...]
> > 
> 
> Regards
> Arnaud

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

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

* [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-24 11:36               ` Lee Jones
  0 siblings, 0 replies; 130+ messages in thread
From: Lee Jones @ 2017-01-24 11:36 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
> On 01/24/2017 09:22 AM, Lee Jones wrote:
> > On Mon, 23 Jan 2017, Arnaud Pouliquen wrote:
> > 
> >> DFSDM hardware IP can be used at the same time for ADC sigma delta
> > 
> > Same time as what?
> DFSDM is used for ADC acquisition (through IIO) but also PDM microphone
> capture (through ASOC).
> > 
> >> conversion and audio PDM microphone.
> >> MFD driver is in charge of configuring IP registers and managing IP clocks.
> >> For this it exports an API to handles filters and channels resources.
> > 
> > This looks like an ADC driver?  What is it that makes it an MFD?
> Yes it a kind of ADC but that supports 2 features audio and iio.
> So it has to support 2 features based on 2 separate Frameworks.

I'm still unsure why it needs to live in MFD.

By the looks of it, this driver needs to move into IIO and you need to
call into it from ASoC.

> >> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> >> ---
> >>  drivers/mfd/Kconfig             |   11 +
> >>  drivers/mfd/Makefile            |    2 +
> >>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
> >>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
> >>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
> >>  5 files changed, 1601 insertions(+)
> >>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
> >>  create mode 100644 drivers/mfd/stm32-dfsdm.c
> >>  create mode 100644 include/linux/mfd/stm32-dfsdm.h
> >>
> >> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> >> index c6df644..4bb660b 100644
> >> --- a/drivers/mfd/Kconfig
> >> +++ b/drivers/mfd/Kconfig
> >> @@ -1607,6 +1607,17 @@ config MFD_STW481X
> >>  	  in various ST Microelectronics and ST-Ericsson embedded
> >>  	  Nomadik series.
> >>  
> >> +config MFD_STM32_DFSDM
> >> +	tristate "ST Microelectronics STM32 DFSDM"
> >> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
> >> +	select MFD_CORE
> >> +	select REGMAP
> >> +	select REGMAP_MMIO
> >> +	help
> >> +	  Select this option to enable the STM32 Digital Filter
> >> +	  for Sigma Delta Modulators (DFSDM) driver used
> >> +	  in various STM32 series.
> >> +
> >>  menu "Multimedia Capabilities Port drivers"
> >>  	depends on ARCH_SA1100
> > 
> > [...]
> > 
> 
> Regards
> Arnaud

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
  2017-01-24 11:36               ` Lee Jones
  (?)
@ 2017-01-24 13:32                 ` Arnaud Pouliquen
  -1 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-24 13:32 UTC (permalink / raw)
  To: Lee Jones
  Cc: Mark Rutland, devicetree, alsa-devel, Lars-Peter Clausen,
	Maxime Coquelin, Liam Girdwood, linux-iio, Mark Brown,
	Takashi Iwai, Jaroslav Kysela, Rob Herring, Jonathan Cameron,
	Peter Meerwald-Stadler, Hartmut Knaack, linux-arm-kernel,
	Alexandre TORGUE



On 01/24/2017 12:36 PM, Lee Jones wrote:
> On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
>> On 01/24/2017 09:22 AM, Lee Jones wrote:
>>> On Mon, 23 Jan 2017, Arnaud Pouliquen wrote:
>>>
>>>> DFSDM hardware IP can be used at the same time for ADC sigma delta
>>>
>>> Same time as what?
>> DFSDM is used for ADC acquisition (through IIO) but also PDM microphone
>> capture (through ASOC).
>>>
>>>> conversion and audio PDM microphone.
>>>> MFD driver is in charge of configuring IP registers and managing IP clocks.
>>>> For this it exports an API to handles filters and channels resources.
>>>
>>> This looks like an ADC driver?  What is it that makes it an MFD?
>> Yes it a kind of ADC but that supports 2 features audio and iio.
>> So it has to support 2 features based on 2 separate Frameworks.
> 
> I'm still unsure why it needs to live in MFD.
> 
> By the looks of it, this driver needs to move into IIO and you need to
> call into it from ASoC.
> 

I think i introduce confusion by speaking about ADC for audio...

1) IIO handles sigma delta ADCs that can be used as example for motor
controls. the aim is to get value based on an application request or
using some triggers.
example: http://www.ti.com/lit/ds/symlink/ads1202.pdf

2) For audio part, we speak about Digital mems microphones that generate
PDM format stream. PDM is a continuous real time stream dedicated to
audio record and must be handled in ASOC ( codec Dmic part driver is
/sound/soc/codec/dmic.c).
DMIC example:
http://www.st.com/content/ccc/resource/technical/document/datasheet/47/bd/d2/13/8d/fd/48/26/DM00121815.pdf/files/DM00121815.pdf/jcr:content/translations/en.DM00121815.pdf

Form my point of view it very strange to handle DMICs in IIO, as it is
not designed to support audio streams.it is two separate features that
are not compatible.

Now, from software point of view
That would means that IIO declares ADCs that it can not expose, because
DMIC is not IIO standard. But IIO inkern API needs that device is
declared... So i should define a specific API in IIO for ASOC driver.


>>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>>>> ---
>>>>  drivers/mfd/Kconfig             |   11 +
>>>>  drivers/mfd/Makefile            |    2 +
>>>>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
>>>>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
>>>>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
>>>>  5 files changed, 1601 insertions(+)
>>>>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
>>>>  create mode 100644 drivers/mfd/stm32-dfsdm.c
>>>>  create mode 100644 include/linux/mfd/stm32-dfsdm.h
>>>>
>>>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>>>> index c6df644..4bb660b 100644
>>>> --- a/drivers/mfd/Kconfig
>>>> +++ b/drivers/mfd/Kconfig
>>>> @@ -1607,6 +1607,17 @@ config MFD_STW481X
>>>>  	  in various ST Microelectronics and ST-Ericsson embedded
>>>>  	  Nomadik series.
>>>>  
>>>> +config MFD_STM32_DFSDM
>>>> +	tristate "ST Microelectronics STM32 DFSDM"
>>>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>>>> +	select MFD_CORE
>>>> +	select REGMAP
>>>> +	select REGMAP_MMIO
>>>> +	help
>>>> +	  Select this option to enable the STM32 Digital Filter
>>>> +	  for Sigma Delta Modulators (DFSDM) driver used
>>>> +	  in various STM32 series.
>>>> +
>>>>  menu "Multimedia Capabilities Port drivers"
>>>>  	depends on ARCH_SA1100
>>>
>>> [...]
>>>
>>
>> Regards
>> Arnaud
> 

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-24 13:32                 ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-24 13:32 UTC (permalink / raw)
  To: Lee Jones
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, Rob Herring,
	Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown, Maxime Coquelin,
	Alexandre TORGUE



On 01/24/2017 12:36 PM, Lee Jones wrote:
> On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
>> On 01/24/2017 09:22 AM, Lee Jones wrote:
>>> On Mon, 23 Jan 2017, Arnaud Pouliquen wrote:
>>>
>>>> DFSDM hardware IP can be used at the same time for ADC sigma delta
>>>
>>> Same time as what?
>> DFSDM is used for ADC acquisition (through IIO) but also PDM microphone
>> capture (through ASOC).
>>>
>>>> conversion and audio PDM microphone.
>>>> MFD driver is in charge of configuring IP registers and managing IP clocks.
>>>> For this it exports an API to handles filters and channels resources.
>>>
>>> This looks like an ADC driver?  What is it that makes it an MFD?
>> Yes it a kind of ADC but that supports 2 features audio and iio.
>> So it has to support 2 features based on 2 separate Frameworks.
> 
> I'm still unsure why it needs to live in MFD.
> 
> By the looks of it, this driver needs to move into IIO and you need to
> call into it from ASoC.
> 

I think i introduce confusion by speaking about ADC for audio...

1) IIO handles sigma delta ADCs that can be used as example for motor
controls. the aim is to get value based on an application request or
using some triggers.
example: http://www.ti.com/lit/ds/symlink/ads1202.pdf

2) For audio part, we speak about Digital mems microphones that generate
PDM format stream. PDM is a continuous real time stream dedicated to
audio record and must be handled in ASOC ( codec Dmic part driver is
/sound/soc/codec/dmic.c).
DMIC example:
http://www.st.com/content/ccc/resource/technical/document/datasheet/47/bd/d2/13/8d/fd/48/26/DM00121815.pdf/files/DM00121815.pdf/jcr:content/translations/en.DM00121815.pdf

Form my point of view it very strange to handle DMICs in IIO, as it is
not designed to support audio streams.it is two separate features that
are not compatible.

Now, from software point of view
That would means that IIO declares ADCs that it can not expose, because
DMIC is not IIO standard. But IIO inkern API needs that device is
declared... So i should define a specific API in IIO for ASOC driver.


>>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>>>> ---
>>>>  drivers/mfd/Kconfig             |   11 +
>>>>  drivers/mfd/Makefile            |    2 +
>>>>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
>>>>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
>>>>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
>>>>  5 files changed, 1601 insertions(+)
>>>>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
>>>>  create mode 100644 drivers/mfd/stm32-dfsdm.c
>>>>  create mode 100644 include/linux/mfd/stm32-dfsdm.h
>>>>
>>>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>>>> index c6df644..4bb660b 100644
>>>> --- a/drivers/mfd/Kconfig
>>>> +++ b/drivers/mfd/Kconfig
>>>> @@ -1607,6 +1607,17 @@ config MFD_STW481X
>>>>  	  in various ST Microelectronics and ST-Ericsson embedded
>>>>  	  Nomadik series.
>>>>  
>>>> +config MFD_STM32_DFSDM
>>>> +	tristate "ST Microelectronics STM32 DFSDM"
>>>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>>>> +	select MFD_CORE
>>>> +	select REGMAP
>>>> +	select REGMAP_MMIO
>>>> +	help
>>>> +	  Select this option to enable the STM32 Digital Filter
>>>> +	  for Sigma Delta Modulators (DFSDM) driver used
>>>> +	  in various STM32 series.
>>>> +
>>>>  menu "Multimedia Capabilities Port drivers"
>>>>  	depends on ARCH_SA1100
>>>
>>> [...]
>>>
>>
>> Regards
>> Arnaud
> 

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

* [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-24 13:32                 ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-24 13:32 UTC (permalink / raw)
  To: linux-arm-kernel



On 01/24/2017 12:36 PM, Lee Jones wrote:
> On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
>> On 01/24/2017 09:22 AM, Lee Jones wrote:
>>> On Mon, 23 Jan 2017, Arnaud Pouliquen wrote:
>>>
>>>> DFSDM hardware IP can be used at the same time for ADC sigma delta
>>>
>>> Same time as what?
>> DFSDM is used for ADC acquisition (through IIO) but also PDM microphone
>> capture (through ASOC).
>>>
>>>> conversion and audio PDM microphone.
>>>> MFD driver is in charge of configuring IP registers and managing IP clocks.
>>>> For this it exports an API to handles filters and channels resources.
>>>
>>> This looks like an ADC driver?  What is it that makes it an MFD?
>> Yes it a kind of ADC but that supports 2 features audio and iio.
>> So it has to support 2 features based on 2 separate Frameworks.
> 
> I'm still unsure why it needs to live in MFD.
> 
> By the looks of it, this driver needs to move into IIO and you need to
> call into it from ASoC.
> 

I think i introduce confusion by speaking about ADC for audio...

1) IIO handles sigma delta ADCs that can be used as example for motor
controls. the aim is to get value based on an application request or
using some triggers.
example: http://www.ti.com/lit/ds/symlink/ads1202.pdf

2) For audio part, we speak about Digital mems microphones that generate
PDM format stream. PDM is a continuous real time stream dedicated to
audio record and must be handled in ASOC ( codec Dmic part driver is
/sound/soc/codec/dmic.c).
DMIC example:
http://www.st.com/content/ccc/resource/technical/document/datasheet/47/bd/d2/13/8d/fd/48/26/DM00121815.pdf/files/DM00121815.pdf/jcr:content/translations/en.DM00121815.pdf

Form my point of view it very strange to handle DMICs in IIO, as it is
not designed to support audio streams.it is two separate features that
are not compatible.

Now, from software point of view
That would means that IIO declares ADCs that it can not expose, because
DMIC is not IIO standard. But IIO inkern API needs that device is
declared... So i should define a specific API in IIO for ASOC driver.


>>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>>>> ---
>>>>  drivers/mfd/Kconfig             |   11 +
>>>>  drivers/mfd/Makefile            |    2 +
>>>>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
>>>>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
>>>>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
>>>>  5 files changed, 1601 insertions(+)
>>>>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
>>>>  create mode 100644 drivers/mfd/stm32-dfsdm.c
>>>>  create mode 100644 include/linux/mfd/stm32-dfsdm.h
>>>>
>>>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>>>> index c6df644..4bb660b 100644
>>>> --- a/drivers/mfd/Kconfig
>>>> +++ b/drivers/mfd/Kconfig
>>>> @@ -1607,6 +1607,17 @@ config MFD_STW481X
>>>>  	  in various ST Microelectronics and ST-Ericsson embedded
>>>>  	  Nomadik series.
>>>>  
>>>> +config MFD_STM32_DFSDM
>>>> +	tristate "ST Microelectronics STM32 DFSDM"
>>>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>>>> +	select MFD_CORE
>>>> +	select REGMAP
>>>> +	select REGMAP_MMIO
>>>> +	help
>>>> +	  Select this option to enable the STM32 Digital Filter
>>>> +	  for Sigma Delta Modulators (DFSDM) driver used
>>>> +	  in various STM32 series.
>>>> +
>>>>  menu "Multimedia Capabilities Port drivers"
>>>>  	depends on ARCH_SA1100
>>>
>>> [...]
>>>
>>
>> Regards
>> Arnaud
> 

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
  2017-01-24 13:32                 ` Arnaud Pouliquen
  (?)
@ 2017-01-27 10:15                   ` Lee Jones
  -1 siblings, 0 replies; 130+ messages in thread
From: Lee Jones @ 2017-01-27 10:15 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: Mark Rutland, devicetree, alsa-devel, Lars-Peter Clausen,
	Maxime Coquelin, Liam Girdwood, linux-iio, Mark Brown,
	Takashi Iwai, Jaroslav Kysela, Rob Herring, Jonathan Cameron,
	Peter Meerwald-Stadler, Hartmut Knaack, linux-arm-kernel,
	Alexandre TORGUE

On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:

> 
> 
> On 01/24/2017 12:36 PM, Lee Jones wrote:
> > On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
> >> On 01/24/2017 09:22 AM, Lee Jones wrote:
> >>> On Mon, 23 Jan 2017, Arnaud Pouliquen wrote:
> >>>
> >>>> DFSDM hardware IP can be used at the same time for ADC sigma delta
> >>>
> >>> Same time as what?
> >> DFSDM is used for ADC acquisition (through IIO) but also PDM microphone
> >> capture (through ASOC).
> >>>
> >>>> conversion and audio PDM microphone.
> >>>> MFD driver is in charge of configuring IP registers and managing IP clocks.
> >>>> For this it exports an API to handles filters and channels resources.
> >>>
> >>> This looks like an ADC driver?  What is it that makes it an MFD?
> >> Yes it a kind of ADC but that supports 2 features audio and iio.
> >> So it has to support 2 features based on 2 separate Frameworks.
> > 
> > I'm still unsure why it needs to live in MFD.
> > 
> > By the looks of it, this driver needs to move into IIO and you need to
> > call into it from ASoC.
> > 
> 
> I think i introduce confusion by speaking about ADC for audio...
> 
> 1) IIO handles sigma delta ADCs that can be used as example for motor
> controls. the aim is to get value based on an application request or
> using some triggers.
> example: http://www.ti.com/lit/ds/symlink/ads1202.pdf
> 
> 2) For audio part, we speak about Digital mems microphones that generate
> PDM format stream. PDM is a continuous real time stream dedicated to
> audio record and must be handled in ASOC ( codec Dmic part driver is
> /sound/soc/codec/dmic.c).
> DMIC example:
> http://www.st.com/content/ccc/resource/technical/document/datasheet/47/bd/d2/13/8d/fd/48/26/DM00121815.pdf/files/DM00121815.pdf/jcr:content/translations/en.DM00121815.pdf
> 
> Form my point of view it very strange to handle DMICs in IIO, as it is
> not designed to support audio streams.it is two separate features that
> are not compatible.
> 
> Now, from software point of view
> That would means that IIO declares ADCs that it can not expose, because
> DMIC is not IIO standard. But IIO inkern API needs that device is
> declared

> So i should define a specific API in IIO for ASOC driver.

Yes, this is what I think you should do.

MFD is not a dumping ground for devices that do not fit anywhere else.

> >>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> >>>> ---
> >>>>  drivers/mfd/Kconfig             |   11 +
> >>>>  drivers/mfd/Makefile            |    2 +
> >>>>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
> >>>>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
> >>>>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
> >>>>  5 files changed, 1601 insertions(+)
> >>>>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
> >>>>  create mode 100644 drivers/mfd/stm32-dfsdm.c
> >>>>  create mode 100644 include/linux/mfd/stm32-dfsdm.h
> >>>>
> >>>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> >>>> index c6df644..4bb660b 100644
> >>>> --- a/drivers/mfd/Kconfig
> >>>> +++ b/drivers/mfd/Kconfig
> >>>> @@ -1607,6 +1607,17 @@ config MFD_STW481X
> >>>>  	  in various ST Microelectronics and ST-Ericsson embedded
> >>>>  	  Nomadik series.
> >>>>  
> >>>> +config MFD_STM32_DFSDM
> >>>> +	tristate "ST Microelectronics STM32 DFSDM"
> >>>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
> >>>> +	select MFD_CORE
> >>>> +	select REGMAP
> >>>> +	select REGMAP_MMIO
> >>>> +	help
> >>>> +	  Select this option to enable the STM32 Digital Filter
> >>>> +	  for Sigma Delta Modulators (DFSDM) driver used
> >>>> +	  in various STM32 series.
> >>>> +
> >>>>  menu "Multimedia Capabilities Port drivers"
> >>>>  	depends on ARCH_SA1100
> >>>
> >>> [...]
> >>>
> >>
> >> Regards
> >> Arnaud
> > 

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

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

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-27 10:15                   ` Lee Jones
  0 siblings, 0 replies; 130+ messages in thread
From: Lee Jones @ 2017-01-27 10:15 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, Rob Herring,
	Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown, Maxime Coquelin,
	Alexandre TORGUE

On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:

> 
> 
> On 01/24/2017 12:36 PM, Lee Jones wrote:
> > On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
> >> On 01/24/2017 09:22 AM, Lee Jones wrote:
> >>> On Mon, 23 Jan 2017, Arnaud Pouliquen wrote:
> >>>
> >>>> DFSDM hardware IP can be used at the same time for ADC sigma delta
> >>>
> >>> Same time as what?
> >> DFSDM is used for ADC acquisition (through IIO) but also PDM microphone
> >> capture (through ASOC).
> >>>
> >>>> conversion and audio PDM microphone.
> >>>> MFD driver is in charge of configuring IP registers and managing IP clocks.
> >>>> For this it exports an API to handles filters and channels resources.
> >>>
> >>> This looks like an ADC driver?  What is it that makes it an MFD?
> >> Yes it a kind of ADC but that supports 2 features audio and iio.
> >> So it has to support 2 features based on 2 separate Frameworks.
> > 
> > I'm still unsure why it needs to live in MFD.
> > 
> > By the looks of it, this driver needs to move into IIO and you need to
> > call into it from ASoC.
> > 
> 
> I think i introduce confusion by speaking about ADC for audio...
> 
> 1) IIO handles sigma delta ADCs that can be used as example for motor
> controls. the aim is to get value based on an application request or
> using some triggers.
> example: http://www.ti.com/lit/ds/symlink/ads1202.pdf
> 
> 2) For audio part, we speak about Digital mems microphones that generate
> PDM format stream. PDM is a continuous real time stream dedicated to
> audio record and must be handled in ASOC ( codec Dmic part driver is
> /sound/soc/codec/dmic.c).
> DMIC example:
> http://www.st.com/content/ccc/resource/technical/document/datasheet/47/bd/d2/13/8d/fd/48/26/DM00121815.pdf/files/DM00121815.pdf/jcr:content/translations/en.DM00121815.pdf
> 
> Form my point of view it very strange to handle DMICs in IIO, as it is
> not designed to support audio streams.it is two separate features that
> are not compatible.
> 
> Now, from software point of view
> That would means that IIO declares ADCs that it can not expose, because
> DMIC is not IIO standard. But IIO inkern API needs that device is
> declared

> So i should define a specific API in IIO for ASOC driver.

Yes, this is what I think you should do.

MFD is not a dumping ground for devices that do not fit anywhere else.

> >>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> >>>> ---
> >>>>  drivers/mfd/Kconfig             |   11 +
> >>>>  drivers/mfd/Makefile            |    2 +
> >>>>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
> >>>>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
> >>>>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
> >>>>  5 files changed, 1601 insertions(+)
> >>>>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
> >>>>  create mode 100644 drivers/mfd/stm32-dfsdm.c
> >>>>  create mode 100644 include/linux/mfd/stm32-dfsdm.h
> >>>>
> >>>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> >>>> index c6df644..4bb660b 100644
> >>>> --- a/drivers/mfd/Kconfig
> >>>> +++ b/drivers/mfd/Kconfig
> >>>> @@ -1607,6 +1607,17 @@ config MFD_STW481X
> >>>>  	  in various ST Microelectronics and ST-Ericsson embedded
> >>>>  	  Nomadik series.
> >>>>  
> >>>> +config MFD_STM32_DFSDM
> >>>> +	tristate "ST Microelectronics STM32 DFSDM"
> >>>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
> >>>> +	select MFD_CORE
> >>>> +	select REGMAP
> >>>> +	select REGMAP_MMIO
> >>>> +	help
> >>>> +	  Select this option to enable the STM32 Digital Filter
> >>>> +	  for Sigma Delta Modulators (DFSDM) driver used
> >>>> +	  in various STM32 series.
> >>>> +
> >>>>  menu "Multimedia Capabilities Port drivers"
> >>>>  	depends on ARCH_SA1100
> >>>
> >>> [...]
> >>>
> >>
> >> Regards
> >> Arnaud
> > 

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

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

* [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-27 10:15                   ` Lee Jones
  0 siblings, 0 replies; 130+ messages in thread
From: Lee Jones @ 2017-01-27 10:15 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:

> 
> 
> On 01/24/2017 12:36 PM, Lee Jones wrote:
> > On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
> >> On 01/24/2017 09:22 AM, Lee Jones wrote:
> >>> On Mon, 23 Jan 2017, Arnaud Pouliquen wrote:
> >>>
> >>>> DFSDM hardware IP can be used at the same time for ADC sigma delta
> >>>
> >>> Same time as what?
> >> DFSDM is used for ADC acquisition (through IIO) but also PDM microphone
> >> capture (through ASOC).
> >>>
> >>>> conversion and audio PDM microphone.
> >>>> MFD driver is in charge of configuring IP registers and managing IP clocks.
> >>>> For this it exports an API to handles filters and channels resources.
> >>>
> >>> This looks like an ADC driver?  What is it that makes it an MFD?
> >> Yes it a kind of ADC but that supports 2 features audio and iio.
> >> So it has to support 2 features based on 2 separate Frameworks.
> > 
> > I'm still unsure why it needs to live in MFD.
> > 
> > By the looks of it, this driver needs to move into IIO and you need to
> > call into it from ASoC.
> > 
> 
> I think i introduce confusion by speaking about ADC for audio...
> 
> 1) IIO handles sigma delta ADCs that can be used as example for motor
> controls. the aim is to get value based on an application request or
> using some triggers.
> example: http://www.ti.com/lit/ds/symlink/ads1202.pdf
> 
> 2) For audio part, we speak about Digital mems microphones that generate
> PDM format stream. PDM is a continuous real time stream dedicated to
> audio record and must be handled in ASOC ( codec Dmic part driver is
> /sound/soc/codec/dmic.c).
> DMIC example:
> http://www.st.com/content/ccc/resource/technical/document/datasheet/47/bd/d2/13/8d/fd/48/26/DM00121815.pdf/files/DM00121815.pdf/jcr:content/translations/en.DM00121815.pdf
> 
> Form my point of view it very strange to handle DMICs in IIO, as it is
> not designed to support audio streams.it is two separate features that
> are not compatible.
> 
> Now, from software point of view
> That would means that IIO declares ADCs that it can not expose, because
> DMIC is not IIO standard. But IIO inkern API needs that device is
> declared

> So i should define a specific API in IIO for ASOC driver.

Yes, this is what I think you should do.

MFD is not a dumping ground for devices that do not fit anywhere else.

> >>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> >>>> ---
> >>>>  drivers/mfd/Kconfig             |   11 +
> >>>>  drivers/mfd/Makefile            |    2 +
> >>>>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
> >>>>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
> >>>>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
> >>>>  5 files changed, 1601 insertions(+)
> >>>>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
> >>>>  create mode 100644 drivers/mfd/stm32-dfsdm.c
> >>>>  create mode 100644 include/linux/mfd/stm32-dfsdm.h
> >>>>
> >>>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> >>>> index c6df644..4bb660b 100644
> >>>> --- a/drivers/mfd/Kconfig
> >>>> +++ b/drivers/mfd/Kconfig
> >>>> @@ -1607,6 +1607,17 @@ config MFD_STW481X
> >>>>  	  in various ST Microelectronics and ST-Ericsson embedded
> >>>>  	  Nomadik series.
> >>>>  
> >>>> +config MFD_STM32_DFSDM
> >>>> +	tristate "ST Microelectronics STM32 DFSDM"
> >>>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
> >>>> +	select MFD_CORE
> >>>> +	select REGMAP
> >>>> +	select REGMAP_MMIO
> >>>> +	help
> >>>> +	  Select this option to enable the STM32 Digital Filter
> >>>> +	  for Sigma Delta Modulators (DFSDM) driver used
> >>>> +	  in various STM32 series.
> >>>> +
> >>>>  menu "Multimedia Capabilities Port drivers"
> >>>>  	depends on ARCH_SA1100
> >>>
> >>> [...]
> >>>
> >>
> >> Regards
> >> Arnaud
> > 

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
  2017-01-27 10:15                   ` Lee Jones
  (?)
@ 2017-01-27 13:45                     ` Arnaud Pouliquen
  -1 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-27 13:45 UTC (permalink / raw)
  To: Lee Jones
  Cc: Mark Rutland, devicetree, alsa-devel, Lars-Peter Clausen,
	Maxime Coquelin, Liam Girdwood, linux-iio, Mark Brown,
	Takashi Iwai, Jaroslav Kysela, Rob Herring, Jonathan Cameron,
	Peter Meerwald-Stadler, Hartmut Knaack, linux-arm-kernel,
	Alexandre TORGUE



On 01/27/2017 11:15 AM, Lee Jones wrote:
> On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
> 
>>
>>
>> On 01/24/2017 12:36 PM, Lee Jones wrote:
>>> On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
>>>> On 01/24/2017 09:22 AM, Lee Jones wrote:
>>>>> On Mon, 23 Jan 2017, Arnaud Pouliquen wrote:
>>>>>
>>>>>> DFSDM hardware IP can be used at the same time for ADC sigma delta
>>>>>
>>>>> Same time as what?
>>>> DFSDM is used for ADC acquisition (through IIO) but also PDM microphone
>>>> capture (through ASOC).
>>>>>
>>>>>> conversion and audio PDM microphone.
>>>>>> MFD driver is in charge of configuring IP registers and managing IP clocks.
>>>>>> For this it exports an API to handles filters and channels resources.
>>>>>
>>>>> This looks like an ADC driver?  What is it that makes it an MFD?
>>>> Yes it a kind of ADC but that supports 2 features audio and iio.
>>>> So it has to support 2 features based on 2 separate Frameworks.
>>>
>>> I'm still unsure why it needs to live in MFD.
>>>
>>> By the looks of it, this driver needs to move into IIO and you need to
>>> call into it from ASoC.
>>>
>>
>> I think i introduce confusion by speaking about ADC for audio...
>>
>> 1) IIO handles sigma delta ADCs that can be used as example for motor
>> controls. the aim is to get value based on an application request or
>> using some triggers.
>> example: http://www.ti.com/lit/ds/symlink/ads1202.pdf
>>
>> 2) For audio part, we speak about Digital mems microphones that generate
>> PDM format stream. PDM is a continuous real time stream dedicated to
>> audio record and must be handled in ASOC ( codec Dmic part driver is
>> /sound/soc/codec/dmic.c).
>> DMIC example:
>> http://www.st.com/content/ccc/resource/technical/document/datasheet/47/bd/d2/13/8d/fd/48/26/DM00121815.pdf/files/DM00121815.pdf/jcr:content/translations/en.DM00121815.pdf
>>
>> Form my point of view it very strange to handle DMICs in IIO, as it is
>> not designed to support audio streams.it is two separate features that
>> are not compatible.
>>
>> Now, from software point of view
>> That would means that IIO declares ADCs that it can not expose, because
>> DMIC is not IIO standard. But IIO inkern API needs that device is
>> declared
> 
>> So i should define a specific API in IIO for ASOC driver.
> 
> Yes, this is what I think you should do.
> 
> MFD is not a dumping ground for devices that do not fit anywhere else.

I fully understand that you don't want to create unnecessary MFD devices.
But In case of DFSDM ,it is really a device that supports 2 features.
The main evidence is that "ADC acquisition" and "digital microphone"
features are handled by two separate frameworks.
So this corresponds to two features that share the same device resources
(registers, IRQs, clocks).
Seems that this match with MFD definition.

Now, if IIO and ASOC maintainers are aligned with you proposal, i will
move MFD part in IIO.
But in this case, i can not see another way to do it, except a MFD
driver hidden in IIO?

Here is my analysis of a design in IIO:

Natural way to do this is to consider that ASOC is a customer of IIO so
use the customer.h API.
1) As Digital microphone can not be handled by IIO dev interface, i can
not expose them as an IIO channel, that would be visible by applications
in /sys/bus/iio/devices/iio
2) ASOC needs to configure DFSDM filter to match to specific frequencies
and sample formats requested by users. Not standard IIO interface to do
this. (on IIO side this is done by attribute file)
3) audio stream is a real time stream. So for audio quality best is to
minimize latencies and Xrun. That why transfer as done from HW to
application using LLI DMA transfers with a minimum of copy. This does
not match with the IIO inkern API.
 => So seems not possible to use IIO inkern API.

That's means that i would need to create a ST specific include file to
offer an API to ASoC to handle DFSDM resources linked to the DMIC.
and to probe ASOc device in IIO one.
So the solution would be to create something like a sub IIO driver That
probe and handle some IIO and ASOC devices.
Ultimately This would correspond to a MFD driver integrated in IIO...

Jonathan, Mark, Please could you share your opinion on this topic?

Regards
Arnaud

> 
>>>>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>>>>>> ---
>>>>>>  drivers/mfd/Kconfig             |   11 +
>>>>>>  drivers/mfd/Makefile            |    2 +
>>>>>>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
>>>>>>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
>>>>>>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
>>>>>>  5 files changed, 1601 insertions(+)
>>>>>>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
>>>>>>  create mode 100644 drivers/mfd/stm32-dfsdm.c
>>>>>>  create mode 100644 include/linux/mfd/stm32-dfsdm.h
>>>>>>
>>>>>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>>>>>> index c6df644..4bb660b 100644
>>>>>> --- a/drivers/mfd/Kconfig
>>>>>> +++ b/drivers/mfd/Kconfig
>>>>>> @@ -1607,6 +1607,17 @@ config MFD_STW481X
>>>>>>  	  in various ST Microelectronics and ST-Ericsson embedded
>>>>>>  	  Nomadik series.
>>>>>>  
>>>>>> +config MFD_STM32_DFSDM
>>>>>> +	tristate "ST Microelectronics STM32 DFSDM"
>>>>>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>>>>>> +	select MFD_CORE
>>>>>> +	select REGMAP
>>>>>> +	select REGMAP_MMIO
>>>>>> +	help
>>>>>> +	  Select this option to enable the STM32 Digital Filter
>>>>>> +	  for Sigma Delta Modulators (DFSDM) driver used
>>>>>> +	  in various STM32 series.
>>>>>> +
>>>>>>  menu "Multimedia Capabilities Port drivers"
>>>>>>  	depends on ARCH_SA1100
>>>>>
>>>>> [...]
>>>>>
>>>>
>>>> Regards
>>>> Arnaud
>>>
> 

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-27 13:45                     ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-27 13:45 UTC (permalink / raw)
  To: Lee Jones
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, Rob Herring,
	Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown, Maxime Coquelin,
	Alexandre TORGUE



On 01/27/2017 11:15 AM, Lee Jones wrote:
> On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
> 
>>
>>
>> On 01/24/2017 12:36 PM, Lee Jones wrote:
>>> On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
>>>> On 01/24/2017 09:22 AM, Lee Jones wrote:
>>>>> On Mon, 23 Jan 2017, Arnaud Pouliquen wrote:
>>>>>
>>>>>> DFSDM hardware IP can be used at the same time for ADC sigma delta
>>>>>
>>>>> Same time as what?
>>>> DFSDM is used for ADC acquisition (through IIO) but also PDM microphone
>>>> capture (through ASOC).
>>>>>
>>>>>> conversion and audio PDM microphone.
>>>>>> MFD driver is in charge of configuring IP registers and managing IP clocks.
>>>>>> For this it exports an API to handles filters and channels resources.
>>>>>
>>>>> This looks like an ADC driver?  What is it that makes it an MFD?
>>>> Yes it a kind of ADC but that supports 2 features audio and iio.
>>>> So it has to support 2 features based on 2 separate Frameworks.
>>>
>>> I'm still unsure why it needs to live in MFD.
>>>
>>> By the looks of it, this driver needs to move into IIO and you need to
>>> call into it from ASoC.
>>>
>>
>> I think i introduce confusion by speaking about ADC for audio...
>>
>> 1) IIO handles sigma delta ADCs that can be used as example for motor
>> controls. the aim is to get value based on an application request or
>> using some triggers.
>> example: http://www.ti.com/lit/ds/symlink/ads1202.pdf
>>
>> 2) For audio part, we speak about Digital mems microphones that generate
>> PDM format stream. PDM is a continuous real time stream dedicated to
>> audio record and must be handled in ASOC ( codec Dmic part driver is
>> /sound/soc/codec/dmic.c).
>> DMIC example:
>> http://www.st.com/content/ccc/resource/technical/document/datasheet/47/bd/d2/13/8d/fd/48/26/DM00121815.pdf/files/DM00121815.pdf/jcr:content/translations/en.DM00121815.pdf
>>
>> Form my point of view it very strange to handle DMICs in IIO, as it is
>> not designed to support audio streams.it is two separate features that
>> are not compatible.
>>
>> Now, from software point of view
>> That would means that IIO declares ADCs that it can not expose, because
>> DMIC is not IIO standard. But IIO inkern API needs that device is
>> declared
> 
>> So i should define a specific API in IIO for ASOC driver.
> 
> Yes, this is what I think you should do.
> 
> MFD is not a dumping ground for devices that do not fit anywhere else.

I fully understand that you don't want to create unnecessary MFD devices.
But In case of DFSDM ,it is really a device that supports 2 features.
The main evidence is that "ADC acquisition" and "digital microphone"
features are handled by two separate frameworks.
So this corresponds to two features that share the same device resources
(registers, IRQs, clocks).
Seems that this match with MFD definition.

Now, if IIO and ASOC maintainers are aligned with you proposal, i will
move MFD part in IIO.
But in this case, i can not see another way to do it, except a MFD
driver hidden in IIO?

Here is my analysis of a design in IIO:

Natural way to do this is to consider that ASOC is a customer of IIO so
use the customer.h API.
1) As Digital microphone can not be handled by IIO dev interface, i can
not expose them as an IIO channel, that would be visible by applications
in /sys/bus/iio/devices/iio
2) ASOC needs to configure DFSDM filter to match to specific frequencies
and sample formats requested by users. Not standard IIO interface to do
this. (on IIO side this is done by attribute file)
3) audio stream is a real time stream. So for audio quality best is to
minimize latencies and Xrun. That why transfer as done from HW to
application using LLI DMA transfers with a minimum of copy. This does
not match with the IIO inkern API.
 => So seems not possible to use IIO inkern API.

That's means that i would need to create a ST specific include file to
offer an API to ASoC to handle DFSDM resources linked to the DMIC.
and to probe ASOc device in IIO one.
So the solution would be to create something like a sub IIO driver That
probe and handle some IIO and ASOC devices.
Ultimately This would correspond to a MFD driver integrated in IIO...

Jonathan, Mark, Please could you share your opinion on this topic?

Regards
Arnaud

> 
>>>>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>>>>>> ---
>>>>>>  drivers/mfd/Kconfig             |   11 +
>>>>>>  drivers/mfd/Makefile            |    2 +
>>>>>>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
>>>>>>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
>>>>>>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
>>>>>>  5 files changed, 1601 insertions(+)
>>>>>>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
>>>>>>  create mode 100644 drivers/mfd/stm32-dfsdm.c
>>>>>>  create mode 100644 include/linux/mfd/stm32-dfsdm.h
>>>>>>
>>>>>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>>>>>> index c6df644..4bb660b 100644
>>>>>> --- a/drivers/mfd/Kconfig
>>>>>> +++ b/drivers/mfd/Kconfig
>>>>>> @@ -1607,6 +1607,17 @@ config MFD_STW481X
>>>>>>  	  in various ST Microelectronics and ST-Ericsson embedded
>>>>>>  	  Nomadik series.
>>>>>>  
>>>>>> +config MFD_STM32_DFSDM
>>>>>> +	tristate "ST Microelectronics STM32 DFSDM"
>>>>>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>>>>>> +	select MFD_CORE
>>>>>> +	select REGMAP
>>>>>> +	select REGMAP_MMIO
>>>>>> +	help
>>>>>> +	  Select this option to enable the STM32 Digital Filter
>>>>>> +	  for Sigma Delta Modulators (DFSDM) driver used
>>>>>> +	  in various STM32 series.
>>>>>> +
>>>>>>  menu "Multimedia Capabilities Port drivers"
>>>>>>  	depends on ARCH_SA1100
>>>>>
>>>>> [...]
>>>>>
>>>>
>>>> Regards
>>>> Arnaud
>>>
> 

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

* [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-27 13:45                     ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-27 13:45 UTC (permalink / raw)
  To: linux-arm-kernel



On 01/27/2017 11:15 AM, Lee Jones wrote:
> On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
> 
>>
>>
>> On 01/24/2017 12:36 PM, Lee Jones wrote:
>>> On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
>>>> On 01/24/2017 09:22 AM, Lee Jones wrote:
>>>>> On Mon, 23 Jan 2017, Arnaud Pouliquen wrote:
>>>>>
>>>>>> DFSDM hardware IP can be used at the same time for ADC sigma delta
>>>>>
>>>>> Same time as what?
>>>> DFSDM is used for ADC acquisition (through IIO) but also PDM microphone
>>>> capture (through ASOC).
>>>>>
>>>>>> conversion and audio PDM microphone.
>>>>>> MFD driver is in charge of configuring IP registers and managing IP clocks.
>>>>>> For this it exports an API to handles filters and channels resources.
>>>>>
>>>>> This looks like an ADC driver?  What is it that makes it an MFD?
>>>> Yes it a kind of ADC but that supports 2 features audio and iio.
>>>> So it has to support 2 features based on 2 separate Frameworks.
>>>
>>> I'm still unsure why it needs to live in MFD.
>>>
>>> By the looks of it, this driver needs to move into IIO and you need to
>>> call into it from ASoC.
>>>
>>
>> I think i introduce confusion by speaking about ADC for audio...
>>
>> 1) IIO handles sigma delta ADCs that can be used as example for motor
>> controls. the aim is to get value based on an application request or
>> using some triggers.
>> example: http://www.ti.com/lit/ds/symlink/ads1202.pdf
>>
>> 2) For audio part, we speak about Digital mems microphones that generate
>> PDM format stream. PDM is a continuous real time stream dedicated to
>> audio record and must be handled in ASOC ( codec Dmic part driver is
>> /sound/soc/codec/dmic.c).
>> DMIC example:
>> http://www.st.com/content/ccc/resource/technical/document/datasheet/47/bd/d2/13/8d/fd/48/26/DM00121815.pdf/files/DM00121815.pdf/jcr:content/translations/en.DM00121815.pdf
>>
>> Form my point of view it very strange to handle DMICs in IIO, as it is
>> not designed to support audio streams.it is two separate features that
>> are not compatible.
>>
>> Now, from software point of view
>> That would means that IIO declares ADCs that it can not expose, because
>> DMIC is not IIO standard. But IIO inkern API needs that device is
>> declared
> 
>> So i should define a specific API in IIO for ASOC driver.
> 
> Yes, this is what I think you should do.
> 
> MFD is not a dumping ground for devices that do not fit anywhere else.

I fully understand that you don't want to create unnecessary MFD devices.
But In case of DFSDM ,it is really a device that supports 2 features.
The main evidence is that "ADC acquisition" and "digital microphone"
features are handled by two separate frameworks.
So this corresponds to two features that share the same device resources
(registers, IRQs, clocks).
Seems that this match with MFD definition.

Now, if IIO and ASOC maintainers are aligned with you proposal, i will
move MFD part in IIO.
But in this case, i can not see another way to do it, except a MFD
driver hidden in IIO?

Here is my analysis of a design in IIO:

Natural way to do this is to consider that ASOC is a customer of IIO so
use the customer.h API.
1) As Digital microphone can not be handled by IIO dev interface, i can
not expose them as an IIO channel, that would be visible by applications
in /sys/bus/iio/devices/iio
2) ASOC needs to configure DFSDM filter to match to specific frequencies
and sample formats requested by users. Not standard IIO interface to do
this. (on IIO side this is done by attribute file)
3) audio stream is a real time stream. So for audio quality best is to
minimize latencies and Xrun. That why transfer as done from HW to
application using LLI DMA transfers with a minimum of copy. This does
not match with the IIO inkern API.
 => So seems not possible to use IIO inkern API.

That's means that i would need to create a ST specific include file to
offer an API to ASoC to handle DFSDM resources linked to the DMIC.
and to probe ASOc device in IIO one.
So the solution would be to create something like a sub IIO driver That
probe and handle some IIO and ASOC devices.
Ultimately This would correspond to a MFD driver integrated in IIO...

Jonathan, Mark, Please could you share your opinion on this topic?

Regards
Arnaud

> 
>>>>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>>>>>> ---
>>>>>>  drivers/mfd/Kconfig             |   11 +
>>>>>>  drivers/mfd/Makefile            |    2 +
>>>>>>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
>>>>>>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
>>>>>>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
>>>>>>  5 files changed, 1601 insertions(+)
>>>>>>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
>>>>>>  create mode 100644 drivers/mfd/stm32-dfsdm.c
>>>>>>  create mode 100644 include/linux/mfd/stm32-dfsdm.h
>>>>>>
>>>>>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>>>>>> index c6df644..4bb660b 100644
>>>>>> --- a/drivers/mfd/Kconfig
>>>>>> +++ b/drivers/mfd/Kconfig
>>>>>> @@ -1607,6 +1607,17 @@ config MFD_STW481X
>>>>>>  	  in various ST Microelectronics and ST-Ericsson embedded
>>>>>>  	  Nomadik series.
>>>>>>  
>>>>>> +config MFD_STM32_DFSDM
>>>>>> +	tristate "ST Microelectronics STM32 DFSDM"
>>>>>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>>>>>> +	select MFD_CORE
>>>>>> +	select REGMAP
>>>>>> +	select REGMAP_MMIO
>>>>>> +	help
>>>>>> +	  Select this option to enable the STM32 Digital Filter
>>>>>> +	  for Sigma Delta Modulators (DFSDM) driver used
>>>>>> +	  in various STM32 series.
>>>>>> +
>>>>>>  menu "Multimedia Capabilities Port drivers"
>>>>>>  	depends on ARCH_SA1100
>>>>>
>>>>> [...]
>>>>>
>>>>
>>>> Regards
>>>> Arnaud
>>>
> 

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
  2017-01-27 13:45                     ` Arnaud Pouliquen
  (?)
@ 2017-01-27 17:17                         ` Lee Jones
  -1 siblings, 0 replies; 130+ messages in thread
From: Lee Jones @ 2017-01-27 17:17 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Rob Herring, Mark Rutland,
	Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Jaroslav Kysela, Takashi Iwai,
	Liam Girdwood, Mark Brown, Maxime Coquelin, Alexandre TORGUE

On Fri, 27 Jan 2017, Arnaud Pouliquen wrote:
> On 01/27/2017 11:15 AM, Lee Jones wrote:
> > On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
> >> On 01/24/2017 12:36 PM, Lee Jones wrote:
> >>> On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
> >>>> On 01/24/2017 09:22 AM, Lee Jones wrote:
> >>>>> On Mon, 23 Jan 2017, Arnaud Pouliquen wrote:
> >>>>>
> >>>>>> DFSDM hardware IP can be used at the same time for ADC sigma delta
> >>>>>
> >>>>> Same time as what?
> >>>> DFSDM is used for ADC acquisition (through IIO) but also PDM microphone
> >>>> capture (through ASOC).
> >>>>>
> >>>>>> conversion and audio PDM microphone.
> >>>>>> MFD driver is in charge of configuring IP registers and managing IP clocks.
> >>>>>> For this it exports an API to handles filters and channels resources.
> >>>>>
> >>>>> This looks like an ADC driver?  What is it that makes it an MFD?
> >>>> Yes it a kind of ADC but that supports 2 features audio and iio.
> >>>> So it has to support 2 features based on 2 separate Frameworks.
> >>>
> >>> I'm still unsure why it needs to live in MFD.
> >>>
> >>> By the looks of it, this driver needs to move into IIO and you need to
> >>> call into it from ASoC.
> >>>
> >>
> >> I think i introduce confusion by speaking about ADC for audio...
> >>
> >> 1) IIO handles sigma delta ADCs that can be used as example for motor
> >> controls. the aim is to get value based on an application request or
> >> using some triggers.
> >> example: http://www.ti.com/lit/ds/symlink/ads1202.pdf
> >>
> >> 2) For audio part, we speak about Digital mems microphones that generate
> >> PDM format stream. PDM is a continuous real time stream dedicated to
> >> audio record and must be handled in ASOC ( codec Dmic part driver is
> >> /sound/soc/codec/dmic.c).
> >> DMIC example:
> >> http://www.st.com/content/ccc/resource/technical/document/datasheet/47/bd/d2/13/8d/fd/48/26/DM00121815.pdf/files/DM00121815.pdf/jcr:content/translations/en.DM00121815.pdf
> >>
> >> Form my point of view it very strange to handle DMICs in IIO, as it is
> >> not designed to support audio streams.it is two separate features that
> >> are not compatible.
> >>
> >> Now, from software point of view
> >> That would means that IIO declares ADCs that it can not expose, because
> >> DMIC is not IIO standard. But IIO inkern API needs that device is
> >> declared
> > 
> >> So i should define a specific API in IIO for ASOC driver.
> > 
> > Yes, this is what I think you should do.
> > 
> > MFD is not a dumping ground for devices that do not fit anywhere else.
> 
> I fully understand that you don't want to create unnecessary MFD devices.
> But In case of DFSDM ,it is really a device that supports 2 features.
> The main evidence is that "ADC acquisition" and "digital microphone"
> features are handled by two separate frameworks.
> So this corresponds to two features that share the same device resources
> (registers, IRQs, clocks).
> Seems that this match with MFD definition.
> 
> Now, if IIO and ASOC maintainers are aligned with you proposal, i will
> move MFD part in IIO.
> But in this case, i can not see another way to do it, except a MFD
> driver hidden in IIO?
> 
> Here is my analysis of a design in IIO:
> 
> Natural way to do this is to consider that ASOC is a customer of IIO so
> use the customer.h API.
> 1) As Digital microphone can not be handled by IIO dev interface, i can
> not expose them as an IIO channel, that would be visible by applications
> in /sys/bus/iio/devices/iio
> 2) ASOC needs to configure DFSDM filter to match to specific frequencies
> and sample formats requested by users. Not standard IIO interface to do
> this. (on IIO side this is done by attribute file)
> 3) audio stream is a real time stream. So for audio quality best is to
> minimize latencies and Xrun. That why transfer as done from HW to
> application using LLI DMA transfers with a minimum of copy. This does
> not match with the IIO inkern API.
>  => So seems not possible to use IIO inkern API.
> 
> That's means that i would need to create a ST specific include file to
> offer an API to ASoC to handle DFSDM resources linked to the DMIC.
> and to probe ASOc device in IIO one.
> So the solution would be to create something like a sub IIO driver That
> probe and handle some IIO and ASOC devices.
> Ultimately This would correspond to a MFD driver integrated in IIO...

The issue is not the creation of an MFD driver to create shared
resources and probe the child devices.  That's what MFD *is* for.
What I do take exception to is having lots of code in MFD which
should clearly live in a different subsystem.

Since this device only serves a couple of functions, I expect it to be
a few hundred lines, maximum.  Everything else should be farmed out.
MFD knows nothing of "channels" or "filters" so anything related to
them (init, get, start, stop, release, etc) needs to find another
home.  Whether that's in IIO is a separate discussion, but it
certainly doesn't live in MFD.

> Jonathan, Mark, Please could you share your opinion on this topic?
> 
> Regards
> Arnaud
> 
> > 
> >>>>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
> >>>>>> ---
> >>>>>>  drivers/mfd/Kconfig             |   11 +
> >>>>>>  drivers/mfd/Makefile            |    2 +
> >>>>>>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
> >>>>>>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
> >>>>>>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
> >>>>>>  5 files changed, 1601 insertions(+)
> >>>>>>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
> >>>>>>  create mode 100644 drivers/mfd/stm32-dfsdm.c
> >>>>>>  create mode 100644 include/linux/mfd/stm32-dfsdm.h
> >>>>>>
> >>>>>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> >>>>>> index c6df644..4bb660b 100644
> >>>>>> --- a/drivers/mfd/Kconfig
> >>>>>> +++ b/drivers/mfd/Kconfig
> >>>>>> @@ -1607,6 +1607,17 @@ config MFD_STW481X
> >>>>>>  	  in various ST Microelectronics and ST-Ericsson embedded
> >>>>>>  	  Nomadik series.
> >>>>>>  
> >>>>>> +config MFD_STM32_DFSDM
> >>>>>> +	tristate "ST Microelectronics STM32 DFSDM"
> >>>>>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
> >>>>>> +	select MFD_CORE
> >>>>>> +	select REGMAP
> >>>>>> +	select REGMAP_MMIO
> >>>>>> +	help
> >>>>>> +	  Select this option to enable the STM32 Digital Filter
> >>>>>> +	  for Sigma Delta Modulators (DFSDM) driver used
> >>>>>> +	  in various STM32 series.
> >>>>>> +
> >>>>>>  menu "Multimedia Capabilities Port drivers"
> >>>>>>  	depends on ARCH_SA1100
> >>>>>
> >>>>> [...]
> >>>>>
> >>>>
> >>>> Regards
> >>>> Arnaud
> >>>
> > 

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
--
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] 130+ messages in thread

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-27 17:17                         ` Lee Jones
  0 siblings, 0 replies; 130+ messages in thread
From: Lee Jones @ 2017-01-27 17:17 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, Rob Herring,
	Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown, Maxime Coquelin,
	Alexandre TORGUE

On Fri, 27 Jan 2017, Arnaud Pouliquen wrote:
> On 01/27/2017 11:15 AM, Lee Jones wrote:
> > On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
> >> On 01/24/2017 12:36 PM, Lee Jones wrote:
> >>> On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
> >>>> On 01/24/2017 09:22 AM, Lee Jones wrote:
> >>>>> On Mon, 23 Jan 2017, Arnaud Pouliquen wrote:
> >>>>>
> >>>>>> DFSDM hardware IP can be used at the same time for ADC sigma delta
> >>>>>
> >>>>> Same time as what?
> >>>> DFSDM is used for ADC acquisition (through IIO) but also PDM microphone
> >>>> capture (through ASOC).
> >>>>>
> >>>>>> conversion and audio PDM microphone.
> >>>>>> MFD driver is in charge of configuring IP registers and managing IP clocks.
> >>>>>> For this it exports an API to handles filters and channels resources.
> >>>>>
> >>>>> This looks like an ADC driver?  What is it that makes it an MFD?
> >>>> Yes it a kind of ADC but that supports 2 features audio and iio.
> >>>> So it has to support 2 features based on 2 separate Frameworks.
> >>>
> >>> I'm still unsure why it needs to live in MFD.
> >>>
> >>> By the looks of it, this driver needs to move into IIO and you need to
> >>> call into it from ASoC.
> >>>
> >>
> >> I think i introduce confusion by speaking about ADC for audio...
> >>
> >> 1) IIO handles sigma delta ADCs that can be used as example for motor
> >> controls. the aim is to get value based on an application request or
> >> using some triggers.
> >> example: http://www.ti.com/lit/ds/symlink/ads1202.pdf
> >>
> >> 2) For audio part, we speak about Digital mems microphones that generate
> >> PDM format stream. PDM is a continuous real time stream dedicated to
> >> audio record and must be handled in ASOC ( codec Dmic part driver is
> >> /sound/soc/codec/dmic.c).
> >> DMIC example:
> >> http://www.st.com/content/ccc/resource/technical/document/datasheet/47/bd/d2/13/8d/fd/48/26/DM00121815.pdf/files/DM00121815.pdf/jcr:content/translations/en.DM00121815.pdf
> >>
> >> Form my point of view it very strange to handle DMICs in IIO, as it is
> >> not designed to support audio streams.it is two separate features that
> >> are not compatible.
> >>
> >> Now, from software point of view
> >> That would means that IIO declares ADCs that it can not expose, because
> >> DMIC is not IIO standard. But IIO inkern API needs that device is
> >> declared
> > 
> >> So i should define a specific API in IIO for ASOC driver.
> > 
> > Yes, this is what I think you should do.
> > 
> > MFD is not a dumping ground for devices that do not fit anywhere else.
> 
> I fully understand that you don't want to create unnecessary MFD devices.
> But In case of DFSDM ,it is really a device that supports 2 features.
> The main evidence is that "ADC acquisition" and "digital microphone"
> features are handled by two separate frameworks.
> So this corresponds to two features that share the same device resources
> (registers, IRQs, clocks).
> Seems that this match with MFD definition.
> 
> Now, if IIO and ASOC maintainers are aligned with you proposal, i will
> move MFD part in IIO.
> But in this case, i can not see another way to do it, except a MFD
> driver hidden in IIO?
> 
> Here is my analysis of a design in IIO:
> 
> Natural way to do this is to consider that ASOC is a customer of IIO so
> use the customer.h API.
> 1) As Digital microphone can not be handled by IIO dev interface, i can
> not expose them as an IIO channel, that would be visible by applications
> in /sys/bus/iio/devices/iio
> 2) ASOC needs to configure DFSDM filter to match to specific frequencies
> and sample formats requested by users. Not standard IIO interface to do
> this. (on IIO side this is done by attribute file)
> 3) audio stream is a real time stream. So for audio quality best is to
> minimize latencies and Xrun. That why transfer as done from HW to
> application using LLI DMA transfers with a minimum of copy. This does
> not match with the IIO inkern API.
>  => So seems not possible to use IIO inkern API.
> 
> That's means that i would need to create a ST specific include file to
> offer an API to ASoC to handle DFSDM resources linked to the DMIC.
> and to probe ASOc device in IIO one.
> So the solution would be to create something like a sub IIO driver That
> probe and handle some IIO and ASOC devices.
> Ultimately This would correspond to a MFD driver integrated in IIO...

The issue is not the creation of an MFD driver to create shared
resources and probe the child devices.  That's what MFD *is* for.
What I do take exception to is having lots of code in MFD which
should clearly live in a different subsystem.

Since this device only serves a couple of functions, I expect it to be
a few hundred lines, maximum.  Everything else should be farmed out.
MFD knows nothing of "channels" or "filters" so anything related to
them (init, get, start, stop, release, etc) needs to find another
home.  Whether that's in IIO is a separate discussion, but it
certainly doesn't live in MFD.

> Jonathan, Mark, Please could you share your opinion on this topic?
> 
> Regards
> Arnaud
> 
> > 
> >>>>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> >>>>>> ---
> >>>>>>  drivers/mfd/Kconfig             |   11 +
> >>>>>>  drivers/mfd/Makefile            |    2 +
> >>>>>>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
> >>>>>>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
> >>>>>>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
> >>>>>>  5 files changed, 1601 insertions(+)
> >>>>>>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
> >>>>>>  create mode 100644 drivers/mfd/stm32-dfsdm.c
> >>>>>>  create mode 100644 include/linux/mfd/stm32-dfsdm.h
> >>>>>>
> >>>>>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> >>>>>> index c6df644..4bb660b 100644
> >>>>>> --- a/drivers/mfd/Kconfig
> >>>>>> +++ b/drivers/mfd/Kconfig
> >>>>>> @@ -1607,6 +1607,17 @@ config MFD_STW481X
> >>>>>>  	  in various ST Microelectronics and ST-Ericsson embedded
> >>>>>>  	  Nomadik series.
> >>>>>>  
> >>>>>> +config MFD_STM32_DFSDM
> >>>>>> +	tristate "ST Microelectronics STM32 DFSDM"
> >>>>>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
> >>>>>> +	select MFD_CORE
> >>>>>> +	select REGMAP
> >>>>>> +	select REGMAP_MMIO
> >>>>>> +	help
> >>>>>> +	  Select this option to enable the STM32 Digital Filter
> >>>>>> +	  for Sigma Delta Modulators (DFSDM) driver used
> >>>>>> +	  in various STM32 series.
> >>>>>> +
> >>>>>>  menu "Multimedia Capabilities Port drivers"
> >>>>>>  	depends on ARCH_SA1100
> >>>>>
> >>>>> [...]
> >>>>>
> >>>>
> >>>> Regards
> >>>> Arnaud
> >>>
> > 

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

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

* [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-27 17:17                         ` Lee Jones
  0 siblings, 0 replies; 130+ messages in thread
From: Lee Jones @ 2017-01-27 17:17 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, 27 Jan 2017, Arnaud Pouliquen wrote:
> On 01/27/2017 11:15 AM, Lee Jones wrote:
> > On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
> >> On 01/24/2017 12:36 PM, Lee Jones wrote:
> >>> On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
> >>>> On 01/24/2017 09:22 AM, Lee Jones wrote:
> >>>>> On Mon, 23 Jan 2017, Arnaud Pouliquen wrote:
> >>>>>
> >>>>>> DFSDM hardware IP can be used at the same time for ADC sigma delta
> >>>>>
> >>>>> Same time as what?
> >>>> DFSDM is used for ADC acquisition (through IIO) but also PDM microphone
> >>>> capture (through ASOC).
> >>>>>
> >>>>>> conversion and audio PDM microphone.
> >>>>>> MFD driver is in charge of configuring IP registers and managing IP clocks.
> >>>>>> For this it exports an API to handles filters and channels resources.
> >>>>>
> >>>>> This looks like an ADC driver?  What is it that makes it an MFD?
> >>>> Yes it a kind of ADC but that supports 2 features audio and iio.
> >>>> So it has to support 2 features based on 2 separate Frameworks.
> >>>
> >>> I'm still unsure why it needs to live in MFD.
> >>>
> >>> By the looks of it, this driver needs to move into IIO and you need to
> >>> call into it from ASoC.
> >>>
> >>
> >> I think i introduce confusion by speaking about ADC for audio...
> >>
> >> 1) IIO handles sigma delta ADCs that can be used as example for motor
> >> controls. the aim is to get value based on an application request or
> >> using some triggers.
> >> example: http://www.ti.com/lit/ds/symlink/ads1202.pdf
> >>
> >> 2) For audio part, we speak about Digital mems microphones that generate
> >> PDM format stream. PDM is a continuous real time stream dedicated to
> >> audio record and must be handled in ASOC ( codec Dmic part driver is
> >> /sound/soc/codec/dmic.c).
> >> DMIC example:
> >> http://www.st.com/content/ccc/resource/technical/document/datasheet/47/bd/d2/13/8d/fd/48/26/DM00121815.pdf/files/DM00121815.pdf/jcr:content/translations/en.DM00121815.pdf
> >>
> >> Form my point of view it very strange to handle DMICs in IIO, as it is
> >> not designed to support audio streams.it is two separate features that
> >> are not compatible.
> >>
> >> Now, from software point of view
> >> That would means that IIO declares ADCs that it can not expose, because
> >> DMIC is not IIO standard. But IIO inkern API needs that device is
> >> declared
> > 
> >> So i should define a specific API in IIO for ASOC driver.
> > 
> > Yes, this is what I think you should do.
> > 
> > MFD is not a dumping ground for devices that do not fit anywhere else.
> 
> I fully understand that you don't want to create unnecessary MFD devices.
> But In case of DFSDM ,it is really a device that supports 2 features.
> The main evidence is that "ADC acquisition" and "digital microphone"
> features are handled by two separate frameworks.
> So this corresponds to two features that share the same device resources
> (registers, IRQs, clocks).
> Seems that this match with MFD definition.
> 
> Now, if IIO and ASOC maintainers are aligned with you proposal, i will
> move MFD part in IIO.
> But in this case, i can not see another way to do it, except a MFD
> driver hidden in IIO?
> 
> Here is my analysis of a design in IIO:
> 
> Natural way to do this is to consider that ASOC is a customer of IIO so
> use the customer.h API.
> 1) As Digital microphone can not be handled by IIO dev interface, i can
> not expose them as an IIO channel, that would be visible by applications
> in /sys/bus/iio/devices/iio
> 2) ASOC needs to configure DFSDM filter to match to specific frequencies
> and sample formats requested by users. Not standard IIO interface to do
> this. (on IIO side this is done by attribute file)
> 3) audio stream is a real time stream. So for audio quality best is to
> minimize latencies and Xrun. That why transfer as done from HW to
> application using LLI DMA transfers with a minimum of copy. This does
> not match with the IIO inkern API.
>  => So seems not possible to use IIO inkern API.
> 
> That's means that i would need to create a ST specific include file to
> offer an API to ASoC to handle DFSDM resources linked to the DMIC.
> and to probe ASOc device in IIO one.
> So the solution would be to create something like a sub IIO driver That
> probe and handle some IIO and ASOC devices.
> Ultimately This would correspond to a MFD driver integrated in IIO...

The issue is not the creation of an MFD driver to create shared
resources and probe the child devices.  That's what MFD *is* for.
What I do take exception to is having lots of code in MFD which
should clearly live in a different subsystem.

Since this device only serves a couple of functions, I expect it to be
a few hundred lines, maximum.  Everything else should be farmed out.
MFD knows nothing of "channels" or "filters" so anything related to
them (init, get, start, stop, release, etc) needs to find another
home.  Whether that's in IIO is a separate discussion, but it
certainly doesn't live in MFD.

> Jonathan, Mark, Please could you share your opinion on this topic?
> 
> Regards
> Arnaud
> 
> > 
> >>>>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> >>>>>> ---
> >>>>>>  drivers/mfd/Kconfig             |   11 +
> >>>>>>  drivers/mfd/Makefile            |    2 +
> >>>>>>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
> >>>>>>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
> >>>>>>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
> >>>>>>  5 files changed, 1601 insertions(+)
> >>>>>>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
> >>>>>>  create mode 100644 drivers/mfd/stm32-dfsdm.c
> >>>>>>  create mode 100644 include/linux/mfd/stm32-dfsdm.h
> >>>>>>
> >>>>>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> >>>>>> index c6df644..4bb660b 100644
> >>>>>> --- a/drivers/mfd/Kconfig
> >>>>>> +++ b/drivers/mfd/Kconfig
> >>>>>> @@ -1607,6 +1607,17 @@ config MFD_STW481X
> >>>>>>  	  in various ST Microelectronics and ST-Ericsson embedded
> >>>>>>  	  Nomadik series.
> >>>>>>  
> >>>>>> +config MFD_STM32_DFSDM
> >>>>>> +	tristate "ST Microelectronics STM32 DFSDM"
> >>>>>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
> >>>>>> +	select MFD_CORE
> >>>>>> +	select REGMAP
> >>>>>> +	select REGMAP_MMIO
> >>>>>> +	help
> >>>>>> +	  Select this option to enable the STM32 Digital Filter
> >>>>>> +	  for Sigma Delta Modulators (DFSDM) driver used
> >>>>>> +	  in various STM32 series.
> >>>>>> +
> >>>>>>  menu "Multimedia Capabilities Port drivers"
> >>>>>>  	depends on ARCH_SA1100
> >>>>>
> >>>>> [...]
> >>>>>
> >>>>
> >>>> Regards
> >>>> Arnaud
> >>>
> > 

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

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

* Re: [PATCH 1/7] MFD: add bindings for STM32 DFSDM driver
  2017-01-23 16:32   ` Arnaud Pouliquen
  (?)
@ 2017-01-27 20:53     ` Rob Herring
  -1 siblings, 0 replies; 130+ messages in thread
From: Rob Herring @ 2017-01-27 20:53 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: Mark Rutland, devicetree, alsa-devel, Lars-Peter Clausen,
	Maxime Coquelin, Liam Girdwood, linux-iio, Takashi Iwai,
	Jaroslav Kysela, Mark Brown, Jonathan Cameron,
	Peter Meerwald-Stadler, Hartmut Knaack, Lee Jones,
	linux-arm-kernel, Alexandre Torgue

On Mon, Jan 23, 2017 at 05:32:19PM +0100, Arnaud Pouliquen wrote:
> Add bindings information for STM32 Digital Filter for Sigma Delta modulators MFD driver
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
>  .../devicetree/bindings/mfd/stm32-dfsdm.txt        | 68 ++++++++++++++++++++++
>  1 file changed, 68 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt
> 
> diff --git a/Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt b/Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt
> new file mode 100644
> index 0000000..e0b45ee
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt
> @@ -0,0 +1,68 @@
> +STMicroelectronics STM32 Digital Filter for Sigma Delta Modulator (DFSDM)
> +ulti-function device.
> +
> +The STM32 DFSDM device is a multifunction device that handles the DFSDM IP.
> +
> +The DFSDM IP allows to add processing on Sigma Delta ADC based on SinC filters.
> +For this, a pool of m filters can be connected to a pool of n channels.
> +For STM32H7 : m = 4, n = 8.
> +
> +Each channel n is assigned to the SPI or Manchester interface n or n + 1.
> +Channels 0 to 2 can also be connected to ADC IP instance 1 to 3.
> +Filtering result is stored in a left aligned register, with 8 LSB reserved for
> +the input channel ID.
> +
> +Each filter instance supports two contexts to manage conversions, each one has
> +its own configurable sequence and trigger:
> +- regular conversion: used for single or continuous conversion.
> +- injected conversions: used for triggered conversion.
> +
> +Interfaces supported:
> +- sigma delta ADCs trough IIO framework.
> +- PDM microphones through ASoC framework.

Bindings describe h/w, not Linux subsystems.

> +
> +Required properties:
> +- compatible:	Must be "st,stm32h7-dfsdm".
> +- reg:		Specifies the DFSDM block register address and length.
> +- interrupts:	IRQ lines connected to each DFSDM filter instance.
> +- clocks:	IP and serial interfaces clocking. Should be set according
> +		to rcc clock ID and "clock-names".
> +- clock-names:	Input clock name "dfsdm_clk" must be defined,
> +		"audio_clk" is optional. If defined CLKOUT is based on the audio
> +		clock, else "dfsdm_clk" is used.

_clk is redundant.

> +
> +Optional properties:
> +- st,clkout-freq: clkout clock frequency (Hz).This clock must be set according
> +		  to "clock" property. Frequency must be a multiple of the rcc
> +		  clock frequency. If not, clkout frequency will not be
> +		  accurate.

What is CLKOUT connected to and will you need to describe that in DT?

> +- pinctrl-names:  set to "default".
> +- pinctrl-0:	  List of phandles pointing to pin configuration nodes for DFSDM
> +		  module.
> +		  For Pinctrl properties see ../pinctrl/pinctrl-bindings.txt
> +Example :
> +	dfsdm: dfsdm@4400D000 {
> +		compatible = "st,stm32h7-dfsdm";
> +		reg = <0x40017000 0x400>;
> +		interrupts = <110>, <111>, <112>, <113>;
> +		clocks = <&timer_clk>;
> +		clock-names = "dfsdm_clk";
> +		pinctrl-0 = <&dfsdm_ch0 &dfsdm_ch1>;
> +		pinctrl-names = "default";
> +		st,clkout-freq = <2480000>;
> +
> +		iio_dfsdm0: iio-dfsdm@0 {

adc {

> +			compatible = "st,stm32-dfsdm-adc";
> +			#io-channel-cells = <1>;
> +			reg = <0>;
> +			status = "disabled";
> +		};
> +		dai_dfsdm0: dfsdm-audio@0 {

digital-mic {

> +			compatible = "st,stm32-dfsdm-audio";
> +			#sound-dai-cells = <0>;
> +			reg = <0>;

You can't have 2 children with the same unit address. Just drop reg and 
the unit address.

> +			dmas = <&dmamux1 101 0x400 0x00>;
> +			dma-names = "rx";
> +			status = "disabled";
> +		};
> +	};
> -- 
> 1.9.1
> 

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

* Re: [PATCH 1/7] MFD: add bindings for STM32 DFSDM driver
@ 2017-01-27 20:53     ` Rob Herring
  0 siblings, 0 replies; 130+ messages in thread
From: Rob Herring @ 2017-01-27 20:53 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, Lee Jones,
	Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown, Maxime Coquelin,
	Alexandre Torgue

On Mon, Jan 23, 2017 at 05:32:19PM +0100, Arnaud Pouliquen wrote:
> Add bindings information for STM32 Digital Filter for Sigma Delta modulators MFD driver
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
>  .../devicetree/bindings/mfd/stm32-dfsdm.txt        | 68 ++++++++++++++++++++++
>  1 file changed, 68 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt
> 
> diff --git a/Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt b/Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt
> new file mode 100644
> index 0000000..e0b45ee
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt
> @@ -0,0 +1,68 @@
> +STMicroelectronics STM32 Digital Filter for Sigma Delta Modulator (DFSDM)
> +ulti-function device.
> +
> +The STM32 DFSDM device is a multifunction device that handles the DFSDM IP.
> +
> +The DFSDM IP allows to add processing on Sigma Delta ADC based on SinC filters.
> +For this, a pool of m filters can be connected to a pool of n channels.
> +For STM32H7 : m = 4, n = 8.
> +
> +Each channel n is assigned to the SPI or Manchester interface n or n + 1.
> +Channels 0 to 2 can also be connected to ADC IP instance 1 to 3.
> +Filtering result is stored in a left aligned register, with 8 LSB reserved for
> +the input channel ID.
> +
> +Each filter instance supports two contexts to manage conversions, each one has
> +its own configurable sequence and trigger:
> +- regular conversion: used for single or continuous conversion.
> +- injected conversions: used for triggered conversion.
> +
> +Interfaces supported:
> +- sigma delta ADCs trough IIO framework.
> +- PDM microphones through ASoC framework.

Bindings describe h/w, not Linux subsystems.

> +
> +Required properties:
> +- compatible:	Must be "st,stm32h7-dfsdm".
> +- reg:		Specifies the DFSDM block register address and length.
> +- interrupts:	IRQ lines connected to each DFSDM filter instance.
> +- clocks:	IP and serial interfaces clocking. Should be set according
> +		to rcc clock ID and "clock-names".
> +- clock-names:	Input clock name "dfsdm_clk" must be defined,
> +		"audio_clk" is optional. If defined CLKOUT is based on the audio
> +		clock, else "dfsdm_clk" is used.

_clk is redundant.

> +
> +Optional properties:
> +- st,clkout-freq: clkout clock frequency (Hz).This clock must be set according
> +		  to "clock" property. Frequency must be a multiple of the rcc
> +		  clock frequency. If not, clkout frequency will not be
> +		  accurate.

What is CLKOUT connected to and will you need to describe that in DT?

> +- pinctrl-names:  set to "default".
> +- pinctrl-0:	  List of phandles pointing to pin configuration nodes for DFSDM
> +		  module.
> +		  For Pinctrl properties see ../pinctrl/pinctrl-bindings.txt
> +Example :
> +	dfsdm: dfsdm@4400D000 {
> +		compatible = "st,stm32h7-dfsdm";
> +		reg = <0x40017000 0x400>;
> +		interrupts = <110>, <111>, <112>, <113>;
> +		clocks = <&timer_clk>;
> +		clock-names = "dfsdm_clk";
> +		pinctrl-0 = <&dfsdm_ch0 &dfsdm_ch1>;
> +		pinctrl-names = "default";
> +		st,clkout-freq = <2480000>;
> +
> +		iio_dfsdm0: iio-dfsdm@0 {

adc {

> +			compatible = "st,stm32-dfsdm-adc";
> +			#io-channel-cells = <1>;
> +			reg = <0>;
> +			status = "disabled";
> +		};
> +		dai_dfsdm0: dfsdm-audio@0 {

digital-mic {

> +			compatible = "st,stm32-dfsdm-audio";
> +			#sound-dai-cells = <0>;
> +			reg = <0>;

You can't have 2 children with the same unit address. Just drop reg and 
the unit address.

> +			dmas = <&dmamux1 101 0x400 0x00>;
> +			dma-names = "rx";
> +			status = "disabled";
> +		};
> +	};
> -- 
> 1.9.1
> 

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

* [PATCH 1/7] MFD: add bindings for STM32 DFSDM driver
@ 2017-01-27 20:53     ` Rob Herring
  0 siblings, 0 replies; 130+ messages in thread
From: Rob Herring @ 2017-01-27 20:53 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Jan 23, 2017 at 05:32:19PM +0100, Arnaud Pouliquen wrote:
> Add bindings information for STM32 Digital Filter for Sigma Delta modulators MFD driver
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
>  .../devicetree/bindings/mfd/stm32-dfsdm.txt        | 68 ++++++++++++++++++++++
>  1 file changed, 68 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt
> 
> diff --git a/Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt b/Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt
> new file mode 100644
> index 0000000..e0b45ee
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt
> @@ -0,0 +1,68 @@
> +STMicroelectronics STM32 Digital Filter for Sigma Delta Modulator (DFSDM)
> +ulti-function device.
> +
> +The STM32 DFSDM device is a multifunction device that handles the DFSDM IP.
> +
> +The DFSDM IP allows to add processing on Sigma Delta ADC based on SinC filters.
> +For this, a pool of m filters can be connected to a pool of n channels.
> +For STM32H7 : m = 4, n = 8.
> +
> +Each channel n is assigned to the SPI or Manchester interface n or n + 1.
> +Channels 0 to 2 can also be connected to ADC IP instance 1 to 3.
> +Filtering result is stored in a left aligned register, with 8 LSB reserved for
> +the input channel ID.
> +
> +Each filter instance supports two contexts to manage conversions, each one has
> +its own configurable sequence and trigger:
> +- regular conversion: used for single or continuous conversion.
> +- injected conversions: used for triggered conversion.
> +
> +Interfaces supported:
> +- sigma delta ADCs trough IIO framework.
> +- PDM microphones through ASoC framework.

Bindings describe h/w, not Linux subsystems.

> +
> +Required properties:
> +- compatible:	Must be "st,stm32h7-dfsdm".
> +- reg:		Specifies the DFSDM block register address and length.
> +- interrupts:	IRQ lines connected to each DFSDM filter instance.
> +- clocks:	IP and serial interfaces clocking. Should be set according
> +		to rcc clock ID and "clock-names".
> +- clock-names:	Input clock name "dfsdm_clk" must be defined,
> +		"audio_clk" is optional. If defined CLKOUT is based on the audio
> +		clock, else "dfsdm_clk" is used.

_clk is redundant.

> +
> +Optional properties:
> +- st,clkout-freq: clkout clock frequency (Hz).This clock must be set according
> +		  to "clock" property. Frequency must be a multiple of the rcc
> +		  clock frequency. If not, clkout frequency will not be
> +		  accurate.

What is CLKOUT connected to and will you need to describe that in DT?

> +- pinctrl-names:  set to "default".
> +- pinctrl-0:	  List of phandles pointing to pin configuration nodes for DFSDM
> +		  module.
> +		  For Pinctrl properties see ../pinctrl/pinctrl-bindings.txt
> +Example :
> +	dfsdm: dfsdm at 4400D000 {
> +		compatible = "st,stm32h7-dfsdm";
> +		reg = <0x40017000 0x400>;
> +		interrupts = <110>, <111>, <112>, <113>;
> +		clocks = <&timer_clk>;
> +		clock-names = "dfsdm_clk";
> +		pinctrl-0 = <&dfsdm_ch0 &dfsdm_ch1>;
> +		pinctrl-names = "default";
> +		st,clkout-freq = <2480000>;
> +
> +		iio_dfsdm0: iio-dfsdm at 0 {

adc {

> +			compatible = "st,stm32-dfsdm-adc";
> +			#io-channel-cells = <1>;
> +			reg = <0>;
> +			status = "disabled";
> +		};
> +		dai_dfsdm0: dfsdm-audio at 0 {

digital-mic {

> +			compatible = "st,stm32-dfsdm-audio";
> +			#sound-dai-cells = <0>;
> +			reg = <0>;

You can't have 2 children with the same unit address. Just drop reg and 
the unit address.

> +			dmas = <&dmamux1 101 0x400 0x00>;
> +			dma-names = "rx";
> +			status = "disabled";
> +		};
> +	};
> -- 
> 1.9.1
> 

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
  2017-01-24 13:32                 ` Arnaud Pouliquen
  (?)
@ 2017-01-29 11:20                   ` Jonathan Cameron
  -1 siblings, 0 replies; 130+ messages in thread
From: Jonathan Cameron @ 2017-01-29 11:20 UTC (permalink / raw)
  To: Arnaud Pouliquen, Lee Jones
  Cc: Mark Rutland, devicetree, alsa-devel, Lars-Peter Clausen,
	Maxime Coquelin, Liam Girdwood, linux-iio, Mark Brown,
	Takashi Iwai, Jaroslav Kysela, Rob Herring,
	Peter Meerwald-Stadler, Hartmut Knaack, linux-arm-kernel,
	Alexandre TORGUE

On 24/01/17 13:32, Arnaud Pouliquen wrote:
> 
> 
> On 01/24/2017 12:36 PM, Lee Jones wrote:
>> On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
>>> On 01/24/2017 09:22 AM, Lee Jones wrote:
>>>> On Mon, 23 Jan 2017, Arnaud Pouliquen wrote:
>>>>
>>>>> DFSDM hardware IP can be used at the same time for ADC sigma delta
>>>>
>>>> Same time as what?
>>> DFSDM is used for ADC acquisition (through IIO) but also PDM microphone
>>> capture (through ASOC).
>>>>
>>>>> conversion and audio PDM microphone.
>>>>> MFD driver is in charge of configuring IP registers and managing IP clocks.
>>>>> For this it exports an API to handles filters and channels resources.
>>>>
>>>> This looks like an ADC driver?  What is it that makes it an MFD?
>>> Yes it a kind of ADC but that supports 2 features audio and iio.
>>> So it has to support 2 features based on 2 separate Frameworks.
>>
>> I'm still unsure why it needs to live in MFD.
>>
>> By the looks of it, this driver needs to move into IIO and you need to
>> call into it from ASoC.
>>
> 
> I think i introduce confusion by speaking about ADC for audio...
> 
> 1) IIO handles sigma delta ADCs that can be used as example for motor
> controls. the aim is to get value based on an application request or
> using some triggers.
> example: http://www.ti.com/lit/ds/symlink/ads1202.pdf
> 
> 2) For audio part, we speak about Digital mems microphones that generate
> PDM format stream. PDM is a continuous real time stream dedicated to
> audio record and must be handled in ASOC ( codec Dmic part driver is
> /sound/soc/codec/dmic.c).
> DMIC example:
> http://www.st.com/content/ccc/resource/technical/document/datasheet/47/bd/d2/13/8d/fd/48/26/DM00121815.pdf/files/DM00121815.pdf/jcr:content/translations/en.DM00121815.pdf
> 
> Form my point of view it very strange to handle DMICs in IIO, as it is
> not designed to support audio streams.it is two separate features that
> are not compatible.
> 
> Now, from software point of view
> That would means that IIO declares ADCs that it can not expose, because
> DMIC is not IIO standard. But IIO inkern API needs that device is
> declared... So i should define a specific API in IIO for ASOC driver.
IIO isn't designed specifically to support audio streams, but it does
have high bandwidth continuous sampling support via the dma buffer
side of things.  Analog devices use this stuff for software defined
radio applications.

What we don't currently have is an in kernel consumer interface for this.
I'm not really sure how difficult it would be to do this, but it is
certainly sounding like there is demand for it.

Probably the best person to comment is Lars who wrote the dma stuff
in the first place and also has a more than passing familiarity with
ASOC.

I've sent Lars a PM to highlight this thread.

Jonathan

> 
> 
>>>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>>>>> ---
>>>>>  drivers/mfd/Kconfig             |   11 +
>>>>>  drivers/mfd/Makefile            |    2 +
>>>>>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
>>>>>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
>>>>>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
>>>>>  5 files changed, 1601 insertions(+)
>>>>>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
>>>>>  create mode 100644 drivers/mfd/stm32-dfsdm.c
>>>>>  create mode 100644 include/linux/mfd/stm32-dfsdm.h
>>>>>
>>>>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>>>>> index c6df644..4bb660b 100644
>>>>> --- a/drivers/mfd/Kconfig
>>>>> +++ b/drivers/mfd/Kconfig
>>>>> @@ -1607,6 +1607,17 @@ config MFD_STW481X
>>>>>  	  in various ST Microelectronics and ST-Ericsson embedded
>>>>>  	  Nomadik series.
>>>>>  
>>>>> +config MFD_STM32_DFSDM
>>>>> +	tristate "ST Microelectronics STM32 DFSDM"
>>>>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>>>>> +	select MFD_CORE
>>>>> +	select REGMAP
>>>>> +	select REGMAP_MMIO
>>>>> +	help
>>>>> +	  Select this option to enable the STM32 Digital Filter
>>>>> +	  for Sigma Delta Modulators (DFSDM) driver used
>>>>> +	  in various STM32 series.
>>>>> +
>>>>>  menu "Multimedia Capabilities Port drivers"
>>>>>  	depends on ARCH_SA1100
>>>>
>>>> [...]
>>>>
>>>
>>> Regards
>>> Arnaud
>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-29 11:20                   ` Jonathan Cameron
  0 siblings, 0 replies; 130+ messages in thread
From: Jonathan Cameron @ 2017-01-29 11:20 UTC (permalink / raw)
  To: Arnaud Pouliquen, Lee Jones
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, Rob Herring,
	Mark Rutland, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Jaroslav Kysela, Takashi Iwai,
	Liam Girdwood, Mark Brown, Maxime Coquelin, Alexandre TORGUE

On 24/01/17 13:32, Arnaud Pouliquen wrote:
> 
> 
> On 01/24/2017 12:36 PM, Lee Jones wrote:
>> On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
>>> On 01/24/2017 09:22 AM, Lee Jones wrote:
>>>> On Mon, 23 Jan 2017, Arnaud Pouliquen wrote:
>>>>
>>>>> DFSDM hardware IP can be used at the same time for ADC sigma delta
>>>>
>>>> Same time as what?
>>> DFSDM is used for ADC acquisition (through IIO) but also PDM microphone
>>> capture (through ASOC).
>>>>
>>>>> conversion and audio PDM microphone.
>>>>> MFD driver is in charge of configuring IP registers and managing IP clocks.
>>>>> For this it exports an API to handles filters and channels resources.
>>>>
>>>> This looks like an ADC driver?  What is it that makes it an MFD?
>>> Yes it a kind of ADC but that supports 2 features audio and iio.
>>> So it has to support 2 features based on 2 separate Frameworks.
>>
>> I'm still unsure why it needs to live in MFD.
>>
>> By the looks of it, this driver needs to move into IIO and you need to
>> call into it from ASoC.
>>
> 
> I think i introduce confusion by speaking about ADC for audio...
> 
> 1) IIO handles sigma delta ADCs that can be used as example for motor
> controls. the aim is to get value based on an application request or
> using some triggers.
> example: http://www.ti.com/lit/ds/symlink/ads1202.pdf
> 
> 2) For audio part, we speak about Digital mems microphones that generate
> PDM format stream. PDM is a continuous real time stream dedicated to
> audio record and must be handled in ASOC ( codec Dmic part driver is
> /sound/soc/codec/dmic.c).
> DMIC example:
> http://www.st.com/content/ccc/resource/technical/document/datasheet/47/bd/d2/13/8d/fd/48/26/DM00121815.pdf/files/DM00121815.pdf/jcr:content/translations/en.DM00121815.pdf
> 
> Form my point of view it very strange to handle DMICs in IIO, as it is
> not designed to support audio streams.it is two separate features that
> are not compatible.
> 
> Now, from software point of view
> That would means that IIO declares ADCs that it can not expose, because
> DMIC is not IIO standard. But IIO inkern API needs that device is
> declared... So i should define a specific API in IIO for ASOC driver.
IIO isn't designed specifically to support audio streams, but it does
have high bandwidth continuous sampling support via the dma buffer
side of things.  Analog devices use this stuff for software defined
radio applications.

What we don't currently have is an in kernel consumer interface for this.
I'm not really sure how difficult it would be to do this, but it is
certainly sounding like there is demand for it.

Probably the best person to comment is Lars who wrote the dma stuff
in the first place and also has a more than passing familiarity with
ASOC.

I've sent Lars a PM to highlight this thread.

Jonathan

> 
> 
>>>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>>>>> ---
>>>>>  drivers/mfd/Kconfig             |   11 +
>>>>>  drivers/mfd/Makefile            |    2 +
>>>>>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
>>>>>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
>>>>>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
>>>>>  5 files changed, 1601 insertions(+)
>>>>>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
>>>>>  create mode 100644 drivers/mfd/stm32-dfsdm.c
>>>>>  create mode 100644 include/linux/mfd/stm32-dfsdm.h
>>>>>
>>>>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>>>>> index c6df644..4bb660b 100644
>>>>> --- a/drivers/mfd/Kconfig
>>>>> +++ b/drivers/mfd/Kconfig
>>>>> @@ -1607,6 +1607,17 @@ config MFD_STW481X
>>>>>  	  in various ST Microelectronics and ST-Ericsson embedded
>>>>>  	  Nomadik series.
>>>>>  
>>>>> +config MFD_STM32_DFSDM
>>>>> +	tristate "ST Microelectronics STM32 DFSDM"
>>>>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>>>>> +	select MFD_CORE
>>>>> +	select REGMAP
>>>>> +	select REGMAP_MMIO
>>>>> +	help
>>>>> +	  Select this option to enable the STM32 Digital Filter
>>>>> +	  for Sigma Delta Modulators (DFSDM) driver used
>>>>> +	  in various STM32 series.
>>>>> +
>>>>>  menu "Multimedia Capabilities Port drivers"
>>>>>  	depends on ARCH_SA1100
>>>>
>>>> [...]
>>>>
>>>
>>> Regards
>>> Arnaud
>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 


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

* [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-29 11:20                   ` Jonathan Cameron
  0 siblings, 0 replies; 130+ messages in thread
From: Jonathan Cameron @ 2017-01-29 11:20 UTC (permalink / raw)
  To: linux-arm-kernel

On 24/01/17 13:32, Arnaud Pouliquen wrote:
> 
> 
> On 01/24/2017 12:36 PM, Lee Jones wrote:
>> On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
>>> On 01/24/2017 09:22 AM, Lee Jones wrote:
>>>> On Mon, 23 Jan 2017, Arnaud Pouliquen wrote:
>>>>
>>>>> DFSDM hardware IP can be used at the same time for ADC sigma delta
>>>>
>>>> Same time as what?
>>> DFSDM is used for ADC acquisition (through IIO) but also PDM microphone
>>> capture (through ASOC).
>>>>
>>>>> conversion and audio PDM microphone.
>>>>> MFD driver is in charge of configuring IP registers and managing IP clocks.
>>>>> For this it exports an API to handles filters and channels resources.
>>>>
>>>> This looks like an ADC driver?  What is it that makes it an MFD?
>>> Yes it a kind of ADC but that supports 2 features audio and iio.
>>> So it has to support 2 features based on 2 separate Frameworks.
>>
>> I'm still unsure why it needs to live in MFD.
>>
>> By the looks of it, this driver needs to move into IIO and you need to
>> call into it from ASoC.
>>
> 
> I think i introduce confusion by speaking about ADC for audio...
> 
> 1) IIO handles sigma delta ADCs that can be used as example for motor
> controls. the aim is to get value based on an application request or
> using some triggers.
> example: http://www.ti.com/lit/ds/symlink/ads1202.pdf
> 
> 2) For audio part, we speak about Digital mems microphones that generate
> PDM format stream. PDM is a continuous real time stream dedicated to
> audio record and must be handled in ASOC ( codec Dmic part driver is
> /sound/soc/codec/dmic.c).
> DMIC example:
> http://www.st.com/content/ccc/resource/technical/document/datasheet/47/bd/d2/13/8d/fd/48/26/DM00121815.pdf/files/DM00121815.pdf/jcr:content/translations/en.DM00121815.pdf
> 
> Form my point of view it very strange to handle DMICs in IIO, as it is
> not designed to support audio streams.it is two separate features that
> are not compatible.
> 
> Now, from software point of view
> That would means that IIO declares ADCs that it can not expose, because
> DMIC is not IIO standard. But IIO inkern API needs that device is
> declared... So i should define a specific API in IIO for ASOC driver.
IIO isn't designed specifically to support audio streams, but it does
have high bandwidth continuous sampling support via the dma buffer
side of things.  Analog devices use this stuff for software defined
radio applications.

What we don't currently have is an in kernel consumer interface for this.
I'm not really sure how difficult it would be to do this, but it is
certainly sounding like there is demand for it.

Probably the best person to comment is Lars who wrote the dma stuff
in the first place and also has a more than passing familiarity with
ASOC.

I've sent Lars a PM to highlight this thread.

Jonathan

> 
> 
>>>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>>>>> ---
>>>>>  drivers/mfd/Kconfig             |   11 +
>>>>>  drivers/mfd/Makefile            |    2 +
>>>>>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
>>>>>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
>>>>>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
>>>>>  5 files changed, 1601 insertions(+)
>>>>>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
>>>>>  create mode 100644 drivers/mfd/stm32-dfsdm.c
>>>>>  create mode 100644 include/linux/mfd/stm32-dfsdm.h
>>>>>
>>>>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>>>>> index c6df644..4bb660b 100644
>>>>> --- a/drivers/mfd/Kconfig
>>>>> +++ b/drivers/mfd/Kconfig
>>>>> @@ -1607,6 +1607,17 @@ config MFD_STW481X
>>>>>  	  in various ST Microelectronics and ST-Ericsson embedded
>>>>>  	  Nomadik series.
>>>>>  
>>>>> +config MFD_STM32_DFSDM
>>>>> +	tristate "ST Microelectronics STM32 DFSDM"
>>>>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>>>>> +	select MFD_CORE
>>>>> +	select REGMAP
>>>>> +	select REGMAP_MMIO
>>>>> +	help
>>>>> +	  Select this option to enable the STM32 Digital Filter
>>>>> +	  for Sigma Delta Modulators (DFSDM) driver used
>>>>> +	  in various STM32 series.
>>>>> +
>>>>>  menu "Multimedia Capabilities Port drivers"
>>>>>  	depends on ARCH_SA1100
>>>>
>>>> [...]
>>>>
>>>
>>> Regards
>>> Arnaud
>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
  2017-01-23 16:32     ` Arnaud Pouliquen
  (?)
@ 2017-01-29 11:53         ` Jonathan Cameron
  -1 siblings, 0 replies; 130+ messages in thread
From: Jonathan Cameron @ 2017-01-29 11:53 UTC (permalink / raw)
  To: Arnaud Pouliquen, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Lee Jones, Rob Herring,
	Mark Rutland, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Jaroslav Kysela, Takashi Iwai,
	Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre Torgue

On 23/01/17 16:32, Arnaud Pouliquen wrote:
> DFSDM hardware IP can be used at the same time for ADC sigma delta
> conversion and audio PDM microphone.
> MFD driver is in charge of configuring IP registers and managing IP clocks.
> For this it exports an API to handles filters and channels resources.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
This is somewhat of a beast.  I would be tempted to build it up in more
bite sized chunks.

Obvious things to drop from a first version (basically to make it easier
to review) would be injected supported.  There may well be others but you'll
have a better feel for that than me.

I really don't like the wrappers round the regmap functions though.
They mean you are papering over errors if they occur and make the code
slightly harder to read by implying that something else is going on.

Yes the code will be longer without them, but you will also be forced to think
properly about error paths.

Anyhow, was mostly reading this to get a feel for what was going on in the
whole series so not really a terribly thorough review I'm afraid. Sorry about
that!

Jonathan
> ---
>  drivers/mfd/Kconfig             |   11 +
>  drivers/mfd/Makefile            |    2 +
>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
>  5 files changed, 1601 insertions(+)
>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
>  create mode 100644 drivers/mfd/stm32-dfsdm.c
>  create mode 100644 include/linux/mfd/stm32-dfsdm.h
> 
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index c6df644..4bb660b 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -1607,6 +1607,17 @@ config MFD_STW481X
>  	  in various ST Microelectronics and ST-Ericsson embedded
>  	  Nomadik series.
>  
> +config MFD_STM32_DFSDM
> +	tristate "ST Microelectronics STM32 DFSDM"
> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
> +	select MFD_CORE
> +	select REGMAP
> +	select REGMAP_MMIO
> +	help
> +	  Select this option to enable the STM32 Digital Filter
> +	  for Sigma Delta Modulators (DFSDM) driver used
> +	  in various STM32 series.
> +
>  menu "Multimedia Capabilities Port drivers"
>  	depends on ARCH_SA1100
>  
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index 9834e66..1f095e5 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -211,3 +211,5 @@ obj-$(CONFIG_INTEL_SOC_PMIC)	+= intel-soc-pmic.o
>  obj-$(CONFIG_MFD_MT6397)	+= mt6397-core.o
>  
>  obj-$(CONFIG_MFD_ALTERA_A10SR)	+= altera-a10sr.o
> +
> +obj-$(CONFIG_MFD_STM32_DFSDM)	+= stm32-dfsdm.o
> \ No newline at end of file
> diff --git a/drivers/mfd/stm32-dfsdm-reg.h b/drivers/mfd/stm32-dfsdm-reg.h
> new file mode 100644
> index 0000000..05ff702
> --- /dev/null
> +++ b/drivers/mfd/stm32-dfsdm-reg.h
> @@ -0,0 +1,220 @@
> +/*
> + * This file is part of STM32 DFSDM mfd driver
> + *
> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
> + * Author(s): Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>.
> + *
> + * 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.
> + */
> +
> +#ifndef MDF_STM32_DFSDM_REG_H
> +#define MDF_STM32_DFSDM_REG_H
> +
> +#include <linux/bitfield.h>
> +/*
> + * Channels definitions
> + */
> +#define DFSDM_CHCFGR1(y)  ((y) * 0x20 + 0x00)
> +#define DFSDM_CHCFGR2(y)  ((y) * 0x20 + 0x04)
> +#define DFSDM_AWSCDR(y)   ((y) * 0x20 + 0x08)
> +#define DFSDM_CHWDATR(y)  ((y) * 0x20 + 0x0C)
> +#define DFSDM_CHDATINR(y) ((y) * 0x20 + 0x10)
> +
> +/* CHCFGR1: Channel configuration register 1 */
> +#define DFSDM_CHCFGR1_SITP_MASK     GENMASK(1, 0)
> +#define DFSDM_CHCFGR1_SITP(v)       FIELD_PREP(DFSDM_CHCFGR1_SITP_MASK, v)
> +#define DFSDM_CHCFGR1_SPICKSEL_MASK GENMASK(3, 2)
> +#define DFSDM_CHCFGR1_SPICKSEL(v)   FIELD_PREP(DFSDM_CHCFGR1_SPICKSEL_MASK, v)
> +#define DFSDM_CHCFGR1_SCDEN_MASK    BIT(5)
> +#define DFSDM_CHCFGR1_SCDEN(v)      FIELD_PREP(DFSDM_CHCFGR1_SCDEN_MASK, v)
> +#define DFSDM_CHCFGR1_CKABEN_MASK   BIT(6)
> +#define DFSDM_CHCFGR1_CKABEN(v)     FIELD_PREP(DFSDM_CHCFGR1_CKABEN_MASK, v)
> +#define DFSDM_CHCFGR1_CHEN_MASK     BIT(7)
> +#define DFSDM_CHCFGR1_CHEN(v)       FIELD_PREP(DFSDM_CHCFGR1_CHEN_MASK, v)
> +#define DFSDM_CHCFGR1_CHINSEL_MASK  BIT(8)
> +#define DFSDM_CHCFGR1_CHINSEL(v)    FIELD_PREP(DFSDM_CHCFGR1_CHINSEL_MASK, v)
> +#define DFSDM_CHCFGR1_DATMPX_MASK   GENMASK(13, 12)
> +#define DFSDM_CHCFGR1_DATMPX(v)     FIELD_PREP(DFSDM_CHCFGR1_DATMPX_MASK, v)
> +#define DFSDM_CHCFGR1_DATPACK_MASK  GENMASK(15, 14)
> +#define DFSDM_CHCFGR1_DATPACK(v)    FIELD_PREP(DFSDM_CHCFGR1_DATPACK_MASK, v)
> +#define DFSDM_CHCFGR1_CKOUTDIV_MASK GENMASK(23, 16)
> +#define DFSDM_CHCFGR1_CKOUTDIV(v)   FIELD_PREP(DFSDM_CHCFGR1_CKOUTDIV_MASK, v)
> +#define DFSDM_CHCFGR1_CKOUTSRC_MASK BIT(30)
> +#define DFSDM_CHCFGR1_CKOUTSRC(v)   FIELD_PREP(DFSDM_CHCFGR1_CKOUTSRC_MASK, v)
> +#define DFSDM_CHCFGR1_DFSDMEN_MASK  BIT(31)
> +#define DFSDM_CHCFGR1_DFSDMEN(v)    FIELD_PREP(DFSDM_CHCFGR1_DFSDMEN_MASK, v)
> +
> +/* CHCFGR2: Channel configuration register 2 */
> +#define DFSDM_CHCFGR2_DTRBS_MASK    GENMASK(7, 3)
> +#define DFSDM_CHCFGR2_DTRBS(v)      FIELD_PREP(DFSDM_CHCFGR2_DTRBS_MASK, v)
> +#define DFSDM_CHCFGR2_OFFSET_MASK   GENMASK(31, 8)
> +#define DFSDM_CHCFGR2_OFFSET(v)     FIELD_PREP(DFSDM_CHCFGR2_OFFSET_MASK, v)
> +
> +/* AWSCDR: Channel analog watchdog and short circuit detector */
> +#define DFSDM_AWSCDR_SCDT_MASK    GENMASK(7, 0)
> +#define DFSDM_AWSCDR_SCDT(v)      FIELD_PREP(DFSDM_AWSCDR_SCDT_MASK, v)
> +#define DFSDM_AWSCDR_BKSCD_MASK   GENMASK(15, 12)
> +#define DFSDM_AWSCDR_BKSCD(v)	  FIELD_PREP(DFSDM_AWSCDR_BKSCD_MASK, v)
> +#define DFSDM_AWSCDR_AWFOSR_MASK  GENMASK(20, 16)
> +#define DFSDM_AWSCDR_AWFOSR(v)    FIELD_PREP(DFSDM_AWSCDR_AWFOSR_MASK, v)
> +#define DFSDM_AWSCDR_AWFORD_MASK  GENMASK(23, 22)
> +#define DFSDM_AWSCDR_AWFORD(v)    FIELD_PREP(DFSDM_AWSCDR_AWFORD_MASK, v)
> +
> +/*
> + * Filters definitions
> + */
> +#define DFSDM_FILTER_BASE_ADR		0x100
> +#define DFSDM_FILTER_REG_MASK		0x7F
> +#define DFSDM_FILTER_X_BASE_ADR(x)	((x) * 0x80 + DFSDM_FILTER_BASE_ADR)
> +
> +#define DFSDM_CR1(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x00)
> +#define DFSDM_CR2(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x04)
> +#define DFSDM_ISR(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x08)
> +#define DFSDM_ICR(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x0C)
> +#define DFSDM_JCHGR(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x10)
> +#define DFSDM_FCR(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x14)
> +#define DFSDM_JDATAR(x)  (DFSDM_FILTER_X_BASE_ADR(x)  + 0x18)
> +#define DFSDM_RDATAR(x)  (DFSDM_FILTER_X_BASE_ADR(x)  + 0x1C)
> +#define DFSDM_AWHTR(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x20)
> +#define DFSDM_AWLTR(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x24)
> +#define DFSDM_AWSR(x)    (DFSDM_FILTER_X_BASE_ADR(x)  + 0x28)
> +#define DFSDM_AWCFR(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x2C)
> +#define DFSDM_EXMAX(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x30)
> +#define DFSDM_EXMIN(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x34)
> +#define DFSDM_CNVTIMR(x) (DFSDM_FILTER_X_BASE_ADR(x)  + 0x38)
> +
> +/* CR1 Control register 1 */
> +#define DFSDM_CR1_DFEN_MASK	BIT(0)
> +#define DFSDM_CR1_DFEN(v)	FIELD_PREP(DFSDM_CR1_DFEN_MASK, v)
> +#define DFSDM_CR1_JSWSTART_MASK	BIT(1)
> +#define DFSDM_CR1_JSWSTART(v)	FIELD_PREP(DFSDM_CR1_JSWSTART_MASK, v)
> +#define DFSDM_CR1_JSYNC_MASK	BIT(3)
> +#define DFSDM_CR1_JSYNC(v)	FIELD_PREP(DFSDM_CR1_JSYNC_MASK, v)
> +#define DFSDM_CR1_JSCAN_MASK	BIT(4)
> +#define DFSDM_CR1_JSCAN(v)	FIELD_PREP(DFSDM_CR1_JSCAN_MASK, v)
> +#define DFSDM_CR1_JDMAEN_MASK	BIT(5)
> +#define DFSDM_CR1_JDMAEN(v)	FIELD_PREP(DFSDM_CR1_JDMAEN_MASK, v)
> +#define DFSDM_CR1_JEXTSEL_MASK	GENMASK(12, 8)
> +#define DFSDM_CR1_JEXTSEL(v)	FIELD_PREP(DFSDM_CR1_JEXTSEL_MASK, v)
> +#define DFSDM_CR1_JEXTEN_MASK	GENMASK(14, 13)
> +#define DFSDM_CR1_JEXTEN(v)	FIELD_PREP(DFSDM_CR1_JEXTEN_MASK, v)
> +#define DFSDM_CR1_RSWSTART_MASK	BIT(17)
> +#define DFSDM_CR1_RSWSTART(v)	FIELD_PREP(DFSDM_CR1_RSWSTART_MASK, v)
> +#define DFSDM_CR1_RCONT_MASK	BIT(18)
> +#define DFSDM_CR1_RCONT(v)	FIELD_PREP(DFSDM_CR1_RCONT_MASK, v)
> +#define DFSDM_CR1_RSYNC_MASK	BIT(19)
> +#define DFSDM_CR1_RSYNC(v)	FIELD_PREP(DFSDM_CR1_RSYNC_MASK, v)
> +#define DFSDM_CR1_RDMAEN_MASK	BIT(21)
> +#define DFSDM_CR1_RDMAEN(v)	FIELD_PREP(DFSDM_CR1_RDMAEN_MASK, v)
> +#define DFSDM_CR1_RCH_MASK	GENMASK(26, 24)
> +#define DFSDM_CR1_RCH(v)	FIELD_PREP(DFSDM_CR1_RCH_MASK, v)
> +#define DFSDM_CR1_FAST_MASK	BIT(29)
> +#define DFSDM_CR1_FAST(v)	FIELD_PREP(DFSDM_CR1_FAST_MASK, v)
> +#define DFSDM_CR1_AWFSEL_MASK	BIT(30)
> +#define DFSDM_CR1_AWFSEL(v)	FIELD_PREP(DFSDM_CR1_AWFSEL_MASK, v)
> +
> +/* CR2: Control register 2 */
> +#define DFSDM_CR2_IE_MASK	GENMASK(6, 0)
> +#define DFSDM_CR2_IE(v)		FIELD_PREP(DFSDM_CR2_IE_MASK, v)
> +#define DFSDM_CR2_JEOCIE_MASK	BIT(0)
> +#define DFSDM_CR2_JEOCIE(v)	FIELD_PREP(DFSDM_CR2_JEOCIE_MASK, v)
> +#define DFSDM_CR2_REOCIE_MASK	BIT(1)
> +#define DFSDM_CR2_REOCIE(v)	FIELD_PREP(DFSDM_CR2_REOCIE_MASK, v)
> +#define DFSDM_CR2_JOVRIE_MASK	BIT(2)
> +#define DFSDM_CR2_JOVRIE(v)	FIELD_PREP(DFSDM_CR2_JOVRIE_MASK, v)
> +#define DFSDM_CR2_ROVRIE_MASK	BIT(3)
> +#define DFSDM_CR2_ROVRIE(v)	FIELD_PREP(DFSDM_CR2_ROVRIE_MASK, v)
> +#define DFSDM_CR2_AWDIE_MASK	BIT(4)
> +#define DFSDM_CR2_AWDIE(v)	FIELD_PREP(DFSDM_CR2_AWDIE_MASK, v)
> +#define DFSDM_CR2_SCDIE_MASK	BIT(5)
> +#define DFSDM_CR2_SCDIE(v)	FIELD_PREP(DFSDM_CR2_SCDIE_MASK, v)
> +#define DFSDM_CR2_CKABIE_MASK	BIT(6)
> +#define DFSDM_CR2_CKABIE(v)	FIELD_PREP(DFSDM_CR2_CKABIE_MASK, v)
> +#define DFSDM_CR2_EXCH_MASK	GENMASK(15, 8)
> +#define DFSDM_CR2_EXCH(v)	FIELD_PREP(DFSDM_CR2_EXCH_MASK, v)
> +#define DFSDM_CR2_AWDCH_MASK	GENMASK(23, 16)
> +#define DFSDM_CR2_AWDCH(v)	FIELD_PREP(DFSDM_CR2_AWDCH_MASK, v)
> +
> +/* ISR: Interrupt status register */
> +#define DFSDM_ISR_JEOCF_MASK	BIT(0)
> +#define DFSDM_ISR_JEOCF(v)	FIELD_PREP(DFSDM_ISR_JEOCF_MASK, v)
> +#define DFSDM_ISR_REOCF_MASK	BIT(1)
> +#define DFSDM_ISR_REOCF(v)	FIELD_PREP(DFSDM_ISR_REOCF_MASK, v)
> +#define DFSDM_ISR_JOVRF_MASK	BIT(2)
> +#define DFSDM_ISR_JOVRF(v)	FIELD_PREP(DFSDM_ISR_JOVRF_MASK, v)
> +#define DFSDM_ISR_ROVRF_MASK	BIT(3)
> +#define DFSDM_ISR_ROVRF(v)	FIELD_PREP(DFSDM_ISR_ROVRF_MASK, v)
> +#define DFSDM_ISR_AWDF_MASK	BIT(4)
> +#define DFSDM_ISR_AWDF(v)	FIELD_PREP(DFSDM_ISR_AWDF_MASK, v)
> +#define DFSDM_ISR_JCIP_MASK	BIT(13)
> +#define DFSDM_ISR_JCIP(v)	FIELD_PREP(DFSDM_ISR_JCIP_MASK, v)
> +#define DFSDM_ISR_RCIP_MASK	BIT(14)
> +#define DFSDM_ISR_RCIP(v)	FIELD_PREP(DFSDM_ISR_RCIP, v)
> +#define DFSDM_ISR_CKABF_MASK	GENMASK(23, 16)
> +#define DFSDM_ISR_CKABF(v)	FIELD_PREP(DFSDM_ISR_CKABF_MASK, v)
> +#define DFSDM_ISR_SCDF_MASK	GENMASK(31, 24)
> +#define DFSDM_ISR_SCDF(v)	FIELD_PREP(DFSDM_ISR_SCDF_MASK, v)
> +
> +/* ICR: Interrupt flag clear register */
> +#define DFSDM_ICR_CLRJOVRF_MASK	      BIT(2)
> +#define DFSDM_ICR_CLRJOVRF(v)	      FIELD_PREP(DFSDM_ICR_CLRJOVRF_MASK, v)
> +#define DFSDM_ICR_CLRROVRF_MASK	      BIT(3)
> +#define DFSDM_ICR_CLRROVRF(v)	      FIELD_PREP(DFSDM_ICR_CLRROVRF_MASK, v)
> +#define DFSDM_ICR_CLRCKABF_MASK	      GENMASK(23, 16)
> +#define DFSDM_ICR_CLRCKABF(v)	      FIELD_PREP(DFSDM_ICR_CLRCKABF_MASK, v)
> +#define DFSDM_ICR_CLRCKABF_CH_MASK(y) BIT(16 + (y))
> +#define DFSDM_ICR_CLRCKABF_CH(v, y)   \
> +			   (((v) << (16 + (y))) & DFSDM_ICR_CLRCKABF_CH_MASK(y))
> +#define DFSDM_ICR_CLRSCDF_MASK	      GENMASK(31, 24)
> +#define DFSDM_ICR_CLRSCDF(v)	      FIELD_PREP(DFSDM_ICR_CLRSCDF_MASK, v)
> +#define DFSDM_ICR_CLRSCDF_CH_MASK(y)  BIT(24 + (y))
> +#define DFSDM_ICR_CLRSCDF_CH(v, y)    \
> +			       (((v) << (24 + (y))) & DFSDM_ICR_CLRSCDF_MASK(y))
> +
> +/* FCR: Filter control register */
> +#define DFSDM_FCR_IOSR_MASK	GENMASK(7, 0)
> +#define DFSDM_FCR_IOSR(v)	FIELD_PREP(DFSDM_FCR_IOSR_MASK, v)
> +#define DFSDM_FCR_FOSR_MASK	GENMASK(25, 16)
> +#define DFSDM_FCR_FOSR(v)	FIELD_PREP(DFSDM_FCR_FOSR_MASK, v)
> +#define DFSDM_FCR_FORD_MASK	GENMASK(31, 29)
> +#define DFSDM_FCR_FORD(v)	FIELD_PREP(DFSDM_FCR_FORD_MASK, v)
> +
> +/* RDATAR: Filter data register for regular channel */
> +#define DFSDM_DATAR_CH_MASK	GENMASK(2, 0)
> +#define DFSDM_DATAR_DATA_OFFSET 8
> +#define DFSDM_DATAR_DATA_MASK	GENMASK(31, DFSDM_DATAR_DATA_OFFSET)
> +
> +/* AWLTR: Filter analog watchdog low threshold register */
> +#define DFSDM_AWLTR_BKAWL_MASK	GENMASK(3, 0)
> +#define DFSDM_AWLTR_BKAWL(v)	FIELD_PREP(DFSDM_AWLTR_BKAWL_MASK, v)
> +#define DFSDM_AWLTR_AWLT_MASK	GENMASK(31, 8)
> +#define DFSDM_AWLTR_AWLT(v)	FIELD_PREP(DFSDM_AWLTR_AWLT_MASK, v)
> +
> +/* AWHTR: Filter analog watchdog low threshold register */
> +#define DFSDM_AWHTR_BKAWH_MASK	GENMASK(3, 0)
> +#define DFSDM_AWHTR_BKAWH(v)	FIELD_PREP(DFSDM_AWHTR_BKAWH_MASK, v)
> +#define DFSDM_AWHTR_AWHT_MASK	GENMASK(31, 8)
> +#define DFSDM_AWHTR_AWHT(v)	FIELD_PREP(DFSDM_AWHTR_AWHT_MASK, v)
> +
> +/* AWSR: Filter watchdog status register */
> +#define DFSDM_AWSR_AWLTF_MASK	GENMASK(7, 0)
> +#define DFSDM_AWSR_AWLTF(v)	FIELD_PREP(DFSDM_AWSR_AWLTF_MASK, v)
> +#define DFSDM_AWSR_AWHTF_MASK	GENMASK(15, 8)
> +#define DFSDM_AWSR_AWHTF(v)	FIELD_PREP(DFSDM_AWSR_AWHTF_MASK, v)
> +
> +/* AWCFR: Filter watchdog status register */
> +#define DFSDM_AWCFR_AWLTF_MASK	GENMASK(7, 0)
> +#define DFSDM_AWCFR_AWLTF(v)	FIELD_PREP(DFSDM_AWCFR_AWLTF_MASK, v)
> +#define DFSDM_AWCFR_AWHTF_MASK	GENMASK(15, 8)
> +#define DFSDM_AWCFR_AWHTF(v)	FIELD_PREP(DFSDM_AWCFR_AWHTF_MASK, v)
> +
> +#endif
> diff --git a/drivers/mfd/stm32-dfsdm.c b/drivers/mfd/stm32-dfsdm.c
> new file mode 100644
> index 0000000..81ca29c
> --- /dev/null
> +++ b/drivers/mfd/stm32-dfsdm.c
> @@ -0,0 +1,1044 @@
> +/*
> + * This file is part of STM32 DFSDM mfd driver
> + *
> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
> + * Author(s): Arnaud Pouliquen <arnaud.pouliquen-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/interrupt.h>
> +#include <linux/mfd/core.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +
> +#include <linux/mfd/stm32-dfsdm.h>
> +
> +#include "stm32-dfsdm-reg.h"
> +
> +#define DFSDM_UPDATE_BITS(regm, reg, mask, val) \
> +		WARN_ON(regmap_update_bits(regm, reg, mask, val))
Don't do these wrappers please. Handle error correctly in all cases.
Reviewing as I tend to do backwards through the driver, I thought these
were doing something interesting.

Effectively you have no error handling as a result of these which needs
fixing.

> +
> +#define DFSDM_REG_READ(regm, reg, val) \
> +		WARN_ON(regmap_read(regm, reg, val))
> +
> +#define DFSDM_REG_WRITE(regm, reg, val) \
> +		WARN_ON(regmap_write(regm, reg, val))
> +
> +#define STM32H7_DFSDM_NUM_FILTERS	4
> +#define STM32H7_DFSDM_NUM_INPUTS	8
> +
> +enum dfsdm_clkout_src {
> +	DFSDM_CLK,
> +	AUDIO_CLK
> +};
> +
> +struct stm32_dev_data {
> +	const struct stm32_dfsdm dfsdm;
> +	const struct regmap_config *regmap_cfg;
> +};
> +
> +struct dfsdm_priv;
> +
> +struct filter_params {
> +	unsigned int id;
> +	int irq;
> +	struct stm32_dfsdm_fl_event event;
> +	u32 event_mask;
> +	struct dfsdm_priv *priv; /* Cross ref for context */
> +	unsigned int ext_ch_mask;
> +	unsigned int scan_ch;
> +};
> +
> +struct ch_params {
> +	struct stm32_dfsdm_channel ch;
> +};
> +
I'd like to see a lot more comments in here.  Perhaps full kernel-doc
as some elements are not that obvious at least to a fairly casual read.

> +struct dfsdm_priv {
> +	struct platform_device *pdev;
> +	struct stm32_dfsdm dfsdm;
> +
> +	spinlock_t lock; /* Used for resource sharing & interrupt lock */
> +
> +	/* Filters */
> +	struct filter_params *filters;
> +	unsigned int free_filter_mask;
> +	unsigned int scd_filter_mask;
> +	unsigned int ckab_filter_mask;
> +
> +	/* Channels */
> +	struct stm32_dfsdm_channel *channels;
> +	unsigned int free_channel_mask;
> +	atomic_t n_active_ch;
> +
> +	/* Clock */
> +	struct clk *clk;
> +	struct clk *aclk;
> +	unsigned int clkout_div;
> +	unsigned int clkout_freq_req;
> +
> +	/* Registers*/
> +	void __iomem *base;
> +	struct regmap *regmap;
> +	phys_addr_t phys_base;
> +};
> +
> +/*
> + * Common
> + */
> +static bool stm32_dfsdm_volatile_reg(struct device *dev, unsigned int reg)
> +{
> +	if (reg < DFSDM_FILTER_BASE_ADR)
> +		return false;
> +
> +	/*
> +	 * Mask is done on register to avoid to list registers of all them
> +	 * filter instances.
> +	 */
> +	switch (reg & DFSDM_FILTER_REG_MASK) {
> +	case DFSDM_CR1(0) & DFSDM_FILTER_REG_MASK:
> +	case DFSDM_ISR(0) & DFSDM_FILTER_REG_MASK:
> +	case DFSDM_JDATAR(0) & DFSDM_FILTER_REG_MASK:
> +	case DFSDM_RDATAR(0) & DFSDM_FILTER_REG_MASK:
> +		return true;
> +	}
> +
> +	return false;
> +}
> +
> +static const struct regmap_config stm32h7_dfsdm_regmap_cfg = {
> +	.reg_bits = 32,
> +	.val_bits = 32,
> +	.reg_stride = sizeof(u32),
> +	.max_register = DFSDM_CNVTIMR(STM32H7_DFSDM_NUM_FILTERS - 1),
> +	.volatile_reg = stm32_dfsdm_volatile_reg,
> +	.fast_io = true,
> +};
> +
> +static const struct stm32_dev_data stm32h7_data = {
> +	.dfsdm.max_channels = STM32H7_DFSDM_NUM_INPUTS,
> +	.dfsdm.max_filters = STM32H7_DFSDM_NUM_FILTERS,
> +	.regmap_cfg = &stm32h7_dfsdm_regmap_cfg,
> +};
> +
> +static int stm32_dfsdm_start_dfsdm(struct dfsdm_priv *priv)
> +{
> +	int ret;
> +	struct device *dev = &priv->pdev->dev;
> +
> +	if (atomic_inc_return(&priv->n_active_ch) == 1) {
> +		ret = clk_prepare_enable(priv->clk);
> +		if (ret < 0) {
> +			dev_err(dev, "Failed to start clock\n");
> +			return ret;
> +		}
> +		if (priv->aclk) {
> +			ret = clk_prepare_enable(priv->aclk);
> +			if (ret < 0) {
> +				dev_err(dev, "Failed to start audio clock\n");
> +				return ret;
> +			}
> +		}
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
> +				  DFSDM_CHCFGR1_CKOUTDIV_MASK,
> +				  DFSDM_CHCFGR1_CKOUTDIV(priv->clkout_div));
> +
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
> +				  DFSDM_CHCFGR1_DFSDMEN_MASK,
> +				  DFSDM_CHCFGR1_DFSDMEN(1));
> +	}
> +
> +	dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,
> +		atomic_read(&priv->n_active_ch));
> +
> +	return 0;
> +}
> +
> +static void stm32_dfsdm_stop_dfsdm(struct dfsdm_priv *priv)
> +{
> +	if (atomic_dec_and_test(&priv->n_active_ch)) {
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
> +				  DFSDM_CHCFGR1_DFSDMEN_MASK,
> +				  DFSDM_CHCFGR1_DFSDMEN(0));
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
> +				  DFSDM_CHCFGR1_CKOUTDIV_MASK,
> +				  DFSDM_CHCFGR1_CKOUTDIV(0));
> +		clk_disable_unprepare(priv->clk);
> +		if (priv->aclk)
> +			clk_disable_unprepare(priv->aclk);
> +	}
> +	dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,
> +		atomic_read(&priv->n_active_ch));
> +}
> +
> +static unsigned int stm32_dfsdm_get_clkout_divider(struct dfsdm_priv *priv,
> +						   unsigned long rate)
> +{
> +	unsigned int delta, div;
> +
> +	/* div = 0 disables the clockout */
> +	if (!priv->clkout_freq_req)
> +		return 0;
> +
> +	div = DIV_ROUND_CLOSEST(rate, priv->clkout_freq_req);
> +
> +	delta = rate - (priv->clkout_freq_req * div);
> +	if (delta)
> +		dev_warn(&priv->pdev->dev,
> +			 "clkout not accurate. delta (Hz): %d\n", delta);
> +
> +	dev_dbg(&priv->pdev->dev, "%s: clk: %lu (Hz), div %u\n",
> +		__func__, rate, div);
> +
> +	return (div - 1);
> +}
> +
> +/*
> + * Filters
> + */
> +
> +static int stm32_dfsdm_clear_event(struct dfsdm_priv *priv, unsigned int fl_id,
> +				   unsigned int event, int mask)
> +{
> +	int val;
> +
> +	switch (event) {
> +	case DFSDM_EVENT_INJ_EOC:
> +		DFSDM_REG_READ(priv->regmap, DFSDM_JDATAR(fl_id), &val);
> +		break;
> +	case DFSDM_EVENT_REG_EOC:
> +		DFSDM_REG_READ(priv->regmap, DFSDM_RDATAR(fl_id), &val);
> +		break;
> +	case DFSDM_EVENT_INJ_XRUN:
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_ICR(fl_id),
> +				  DFSDM_ICR_CLRJOVRF_MASK,
> +				  DFSDM_ICR_CLRJOVRF_MASK);
> +		break;
> +	case DFSDM_EVENT_REG_XRUN:
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_ICR(fl_id),
> +				  DFSDM_ICR_CLRROVRF_MASK,
> +				  DFSDM_ICR_CLRROVRF_MASK);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
> +{
> +	struct filter_params *params = arg;
> +	unsigned int status;
> +	struct dfsdm_priv *priv = params->priv;
> +	unsigned int event_mask = params->event_mask;
> +
> +	DFSDM_REG_READ(priv->regmap, DFSDM_ISR(params->id), &status);
> +
> +	if (status & DFSDM_ISR_JOVRF_MASK) {
> +		if (event_mask & DFSDM_EVENT_INJ_XRUN) {
> +			params->event.cb(&priv->dfsdm, params->id,
> +					 DFSDM_EVENT_INJ_XRUN, 0,
> +					 params->event.context);
> +		}
> +		stm32_dfsdm_clear_event(priv, params->id, DFSDM_EVENT_INJ_XRUN,
> +					0);
> +	}
> +
> +	if (status & DFSDM_ISR_ROVRF_MASK) {
> +		if (event_mask & DFSDM_EVENT_REG_XRUN) {
> +			params->event.cb(&priv->dfsdm, params->id,
> +					 DFSDM_EVENT_REG_XRUN, 0,
> +					 params->event.context);
> +		}
> +		stm32_dfsdm_clear_event(priv, params->id, DFSDM_EVENT_REG_XRUN,
> +					0);
> +	}
> +
> +	if (status & DFSDM_ISR_JEOCF_MASK) {
> +		if (event_mask & DFSDM_EVENT_INJ_EOC)
> +			params->event.cb(&priv->dfsdm, params->id,
> +					 DFSDM_EVENT_INJ_EOC, 0,
> +					 params->event.context);
> +		else
> +			stm32_dfsdm_clear_event(priv, params->id,
> +						DFSDM_EVENT_INJ_EOC, 0);
> +	}
> +
> +	if (status & DFSDM_ISR_REOCF_MASK) {
> +		if (event_mask & DFSDM_EVENT_REG_EOC)
> +			params->event.cb(&priv->dfsdm, params->id,
> +					 DFSDM_EVENT_REG_EOC, 0,
> +					 params->event.context);
> +		else
> +			stm32_dfsdm_clear_event(priv, params->id,
> +						DFSDM_EVENT_REG_EOC, 0);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static void stm32_dfsdm_configure_reg_conv(struct dfsdm_priv *priv,
> +					   unsigned int fl_id,
> +					   struct stm32_dfsdm_regular *params)
> +{
> +	unsigned int ch_id = params->ch_src;
> +
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RCH_MASK,
> +			  DFSDM_CR1_RCH(ch_id));
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_FAST_MASK,
> +			  DFSDM_CR1_FAST(params->fast_mode));
> +
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RCONT_MASK,
> +			  DFSDM_CR1_RCONT(params->cont_mode));
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RDMAEN_MASK,
> +			  DFSDM_CR1_RDMAEN(params->dma_mode));
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RSYNC_MASK,
> +			  DFSDM_CR1_RSYNC(params->sync_mode));
> +
> +	priv->filters[fl_id].scan_ch = BIT(ch_id);
> +}
> +
> +static void stm32_dfsdm_configure_inj_conv(struct dfsdm_priv *priv,
> +					   unsigned int fl_id,
> +					   struct stm32_dfsdm_injected *params)
> +{
> +	int val;
> +
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_JSCAN_MASK,
> +			  DFSDM_CR1_JSCAN(params->scan_mode));
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_JDMAEN_MASK,
> +			  DFSDM_CR1_JDMAEN(params->dma_mode));
> +
> +	val = (params->trigger == DFSDM_FILTER_EXT_TRIGGER) ?
> +	      params->trig_src : 0;
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id),
> +			  DFSDM_CR1_JEXTSEL_MASK,
> +			  DFSDM_CR1_JEXTSEL(val));
> +
> +	val = (params->trigger == DFSDM_FILTER_SYNC_TRIGGER) ? 1 : 0;
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_JSYNC_MASK,
> +			  DFSDM_CR1_JSYNC(val));
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_JEXTEN_MASK,
> +			  DFSDM_CR1_JEXTEN(params->trig_pol));
> +	priv->filters[fl_id].scan_ch = params->ch_group;
> +
> +	DFSDM_REG_WRITE(priv->regmap, DFSDM_JCHGR(fl_id), params->ch_group);
> +}
> +
> +/**
> + * stm32_dfsdm_configure_filter - Configure filter.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: Filter id.
> + * @conv: Conversion type regular or injected.
> + */
> +int stm32_dfsdm_configure_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
> +				 struct stm32_dfsdm_filter *fl_cfg)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv,
> +					       dfsdm);
> +	struct stm32_dfsdm_sinc_filter *sparams = &fl_cfg->sinc_params;
> +
> +	dev_dbg(&priv->pdev->dev, "%s:config filter %d\n", __func__, fl_id);
> +
> +	/* Average integrator oversampling */
> +	if ((!fl_cfg->int_oversampling) ||
> +	    (fl_cfg->int_oversampling > DFSDM_MAX_INT_OVERSAMPLING)) {
> +		dev_err(&priv->pdev->dev, "invalid integrator oversampling %d\n",
> +			fl_cfg->int_oversampling);
> +		return -EINVAL;
> +	}
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_FCR(fl_id), DFSDM_FCR_IOSR_MASK,
> +			  DFSDM_FCR_IOSR((fl_cfg->int_oversampling - 1)));
> +
> +	/* Oversamplings and filter*/
> +	if ((!sparams->oversampling) ||
> +	    (sparams->oversampling > DFSDM_MAX_FL_OVERSAMPLING)) {
> +		dev_err(&priv->pdev->dev, "invalid oversampling %d\n",
> +			sparams->oversampling);
> +		return -EINVAL;
> +	}
> +
> +	if (sparams->order > DFSDM_SINC5_ORDER) {
> +		dev_err(&priv->pdev->dev, "invalid filter order %d\n",
> +			sparams->order);
> +		return -EINVAL;
> +	}
> +
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FOSR_MASK,
> +			  DFSDM_FCR_FOSR((sparams->oversampling - 1)));
> +
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FORD_MASK,
> +			  DFSDM_FCR_FORD(sparams->order));
> +
> +	/* Conversion */
> +	if (fl_cfg->inj_params)
> +		stm32_dfsdm_configure_inj_conv(priv, fl_id, fl_cfg->inj_params);
> +	else if (fl_cfg->reg_params)
> +		stm32_dfsdm_configure_reg_conv(priv, fl_id, fl_cfg->reg_params);
> +
> +	priv->filters[fl_id].event = fl_cfg->event;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_configure_filter);
> +
> +/**
> + * stm32_dfsdm_start_filter - Start filter conversion.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: Filter id.
> + * @conv: Conversion type regular or injected.
> + */
> +void stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
> +			      enum stm32_dfsdm_conv_type conv)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +
> +	dev_dbg(&priv->pdev->dev, "%s:start filter %d\n", __func__, fl_id);
> +
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_DFEN_MASK,
> +			  DFSDM_CR1_DFEN(1));
> +
> +	if (conv == DFSDM_FILTER_REG_CONV) {
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id),
> +				  DFSDM_CR1_RSWSTART_MASK,
> +				  DFSDM_CR1_RSWSTART(1));
> +	} else if (conv == DFSDM_FILTER_SW_INJ_CONV) {
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id),
> +				  DFSDM_CR1_JSWSTART_MASK,
> +				  DFSDM_CR1_JSWSTART(1));
> +	}
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_start_filter);
> +
> +/**
> + * stm32_dfsdm_stop_filter - Stop filter conversion.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: Filter id.
> + */
> +void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +
> +	dev_dbg(&priv->pdev->dev, "%s:stop filter %d\n", __func__, fl_id);
> +
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_DFEN_MASK,
> +			  DFSDM_CR1_DFEN(0));
> +	priv->filters[fl_id].scan_ch = 0;
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_stop_filter);
> +
> +/**
> + * stm32_dfsdm_read_fl_conv - Read filter conversion.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: Filter id.
> + * @type: Regular or injected conversion.
> + */
> +void stm32_dfsdm_read_fl_conv(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
> +			      u32 *val, int *ch_id,
> +			      enum stm32_dfsdm_conv_type type)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +	int reg_v, offset;
> +
> +	if (type == DFSDM_FILTER_REG_CONV)
> +		offset = DFSDM_RDATAR(fl_id);
> +	else
> +		offset = DFSDM_JDATAR(fl_id);
> +
> +	DFSDM_REG_READ(priv->regmap, offset, &reg_v);
> +
> +	*ch_id = reg_v & DFSDM_DATAR_CH_MASK;
> +	*val = reg_v & DFSDM_DATAR_DATA_MASK;
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_read_fl_conv);
> +
> +/**
> + * stm32_dfsdm_get_filter - Get filter instance.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: Filter instance to reserve.
> + *
> + * Reserves a DFSDM filter resource.
> + */
> +int stm32_dfsdm_get_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv,
> +					       dfsdm);
> +	struct device *dev = &priv->pdev->dev;
> +
> +	spin_lock(&priv->lock);
> +	if (!(priv->free_filter_mask & BIT(fl_id))) {
> +		spin_unlock(&priv->lock);
> +		dev_err(dev, "filter resource %d available\n", fl_id);
> +		return -EBUSY;
> +	}
> +	priv->free_filter_mask &= ~BIT(fl_id);
> +
> +	spin_unlock(&priv->lock);
> +
> +	dev_dbg(dev, "%s: new mask %#x\n", __func__, priv->free_filter_mask);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_get_filter);
> +
> +/**
> + * stm32_dfsdm_release_filter - Release filter instance.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: Filter id.
> + *
> + * Free the DFSDM filter resource.
> + */
> +void stm32_dfsdm_release_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +
> +	spin_lock(&priv->lock);
> +	priv->free_filter_mask |= BIT(fl_id);
> +	spin_unlock(&priv->lock);
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_release_filter);
> +
> +/**
> + * stm32_dfsdm_get_filter_dma_addr - Get register address for dma transfer.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: Filter id.
> + * @conv: Conversion type.
> + */
> +dma_addr_t stm32_dfsdm_get_filter_dma_phy_addr(struct stm32_dfsdm *dfsdm,
> +					       unsigned int fl_id,
> +					       enum stm32_dfsdm_conv_type conv)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +
> +	if (conv == DFSDM_FILTER_REG_CONV)
> +		return (dma_addr_t)(priv->phys_base + DFSDM_RDATAR(fl_id));
> +	else
> +		return (dma_addr_t)(priv->phys_base + DFSDM_JDATAR(fl_id));
> +}
> +
> +/**
> + * stm32_dfsdm_register_fl_event - Register filter event.
What is a filter event?  More details good on things that are very
device specific like this.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: Filter id.
> + * @event: Event to unregister.
> + * @chan_mask: Mask of channels associated to filter.
> + *
> + * The function enables associated IRQ.
> + */
> +int stm32_dfsdm_register_fl_event(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
> +				  enum stm32_dfsdm_events event,
> +				  unsigned int chan_mask)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +	unsigned long flags, ulmask = chan_mask;
> +	int ret, i;
> +
> +	dev_dbg(&priv->pdev->dev, "%s:for filter %d: event %#x ch_mask %#x\n",
> +		__func__, fl_id, event, chan_mask);
> +
> +	if (event > DFSDM_EVENT_CKA)
> +		return -EINVAL;
> +
> +	/* Clear interrupt before enable them */
> +	ret = stm32_dfsdm_clear_event(priv, fl_id, event, chan_mask);
> +	if (ret < 0)
> +		return ret;
> +
> +	spin_lock_irqsave(&priv->lock, flags);
> +	/* Enable interrupts */
> +	switch (event) {
> +	case DFSDM_EVENT_SCD:
> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
> +					  DFSDM_CHCFGR1_SCDEN_MASK,
> +					  DFSDM_CHCFGR1_SCDEN(1));
> +		}
> +		if (!priv->scd_filter_mask)
> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
> +					  DFSDM_CR2_SCDIE_MASK,
> +					  DFSDM_CR2_SCDIE(1));
> +		priv->scd_filter_mask |= BIT(fl_id);
> +		break;
> +	case DFSDM_EVENT_CKA:
> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
> +					  DFSDM_CHCFGR1_CKABEN_MASK,
> +					  DFSDM_CHCFGR1_CKABEN(1));
> +		}
> +		if (!priv->ckab_filter_mask)
> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
> +					  DFSDM_CR2_CKABIE_MASK,
> +					  DFSDM_CR2_CKABIE(1));
> +		priv->ckab_filter_mask |= BIT(fl_id);
> +		break;
> +	default:
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(fl_id), event, event);
> +	}
> +	priv->filters[fl_id].event_mask |= event;
> +	spin_unlock_irqrestore(&priv->lock, flags);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_register_fl_event);
> +
> +/**
> + * stm32_dfsdm_unregister_fl_event - Unregister filter event.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: Filter id.
> + * @event: Event to unregister.
> + * @chan_mask: Mask of channels associated to filter.
> + *
> + * The function disables associated IRQ.
> + */
> +int stm32_dfsdm_unregister_fl_event(struct stm32_dfsdm *dfsdm,
> +				    unsigned int fl_id,
> +				    enum stm32_dfsdm_events event,
> +				    unsigned int chan_mask)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +	unsigned long flags, ulmask = chan_mask;
> +	int i;
> +
> +	dev_dbg(&priv->pdev->dev, "%s:for filter %d: event %#x ch_mask %#x\n",
> +		__func__, fl_id, event, chan_mask);
> +
> +	if (event > DFSDM_EVENT_CKA)
> +		return -EINVAL;
> +
> +	spin_lock_irqsave(&priv->lock, flags);
> +	/* Disable interrupts */
> +	switch (event) {
> +	case DFSDM_EVENT_SCD:
> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
> +					  DFSDM_CHCFGR1_SCDEN_MASK,
> +					  DFSDM_CHCFGR1_SCDEN(0));
> +		}
> +		priv->scd_filter_mask &= ~BIT(fl_id);
> +		if (!priv->scd_filter_mask)
> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
> +					  DFSDM_CR2_SCDIE_MASK,
> +					  DFSDM_CR2_SCDIE(0));
> +		break;
> +	case DFSDM_EVENT_CKA:
> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
> +					  DFSDM_CHCFGR1_CKABEN_MASK,
> +					  DFSDM_CHCFGR1_CKABEN(0));
> +		}
> +		priv->ckab_filter_mask &= ~BIT(fl_id);
> +		if (!priv->ckab_filter_mask)
> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
> +					  DFSDM_CR2_CKABIE_MASK,
> +					  DFSDM_CR2_CKABIE(0));
> +		break;
> +	default:
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(fl_id), event, 0);
> +	}
> +
> +	priv->filters[fl_id].event_mask &= ~event;
> +	spin_unlock_irqrestore(&priv->lock, flags);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_unregister_fl_event);
> +
> +/*
> + * Channels
> + */
> +static void stm32_dfsdm_init_channel(struct dfsdm_priv *priv,
> +				     struct stm32_dfsdm_channel *ch)
> +{
Comments in here for what the various bits are doing would be great.
The naming makes it just about possible to work out, but not nice
to read!
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
> +			  DFSDM_CHCFGR1_DATMPX_MASK,
> +			  DFSDM_CHCFGR1_DATMPX(ch->type.source));
> +	if (ch->type.source == DFSDM_CHANNEL_EXTERNAL_INPUTS) {
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
> +				  DFSDM_CHCFGR1_SITP_MASK,
> +				  DFSDM_CHCFGR1_SITP(ch->serial_if.type));
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
> +				  DFSDM_CHCFGR1_SPICKSEL_MASK,
> +				DFSDM_CHCFGR1_SPICKSEL(ch->serial_if.spi_clk));
> +	}
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
> +			  DFSDM_CHCFGR1_DATPACK_MASK,
> +			  DFSDM_CHCFGR1_DATPACK(ch->type.DataPacking));
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
> +			  DFSDM_CHCFGR1_CHINSEL_MASK,
> +			  DFSDM_CHCFGR1_CHINSEL(ch->serial_if.pins));
> +}
> +
> +/**
> + * stm32_dfsdm_start_channel - Configure and activate DFSDM channel.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @ch: Filter id.
> + * @cfg: Filter configuration.
> + */
> +int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id,
> +			      struct stm32_dfsdm_ch_cfg *cfg)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv,
> +					       dfsdm);
> +	struct regmap *reg = priv->regmap;
> +	int ret;
> +
> +	dev_dbg(&priv->pdev->dev, "%s: for channel %d\n", __func__, ch_id);
> +
> +	ret = stm32_dfsdm_start_dfsdm(priv);
> +	if (ret < 0)
> +		return ret;
> +
> +	DFSDM_UPDATE_BITS(reg, DFSDM_CHCFGR2(ch_id), DFSDM_CHCFGR2_DTRBS_MASK,
> +			  DFSDM_CHCFGR2_DTRBS(cfg->right_bit_shift));
> +	DFSDM_UPDATE_BITS(reg, DFSDM_CHCFGR2(ch_id), DFSDM_CHCFGR2_OFFSET_MASK,
> +			  DFSDM_CHCFGR2_OFFSET(cfg->offset));
> +
> +	DFSDM_UPDATE_BITS(reg, DFSDM_CHCFGR1(ch_id), DFSDM_CHCFGR1_CHEN_MASK,
> +			  DFSDM_CHCFGR1_CHEN(1));
> +
> +	/* Clear absence detection IRQ */
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_ICR(0),
> +			  DFSDM_ICR_CLRCKABF_CH_MASK(ch_id),
> +			  DFSDM_ICR_CLRCKABF_CH(1, ch_id));
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_start_channel);
> +
> +/**
> + * stm32_dfsdm_stop_channel - Deactivate channel.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @ch_id: DFSDM channel identifier.
> + */
> +void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +
> +	dev_dbg(&priv->pdev->dev, "%s:for channel %d\n", __func__, ch_id);
> +
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch_id),
> +			  DFSDM_CHCFGR1_CHEN_MASK,
> +			  DFSDM_CHCFGR1_CHEN(0));
> +
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch_id),
> +			  DFSDM_CHCFGR1_CKABEN_MASK, DFSDM_CHCFGR1_CKABEN(0));
> +
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch_id),
> +			  DFSDM_CHCFGR1_SCDEN_MASK, DFSDM_CHCFGR1_SCDEN(0));
> +
> +	stm32_dfsdm_stop_dfsdm(priv);
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_stop_channel);
> +
> +/**
> + * stm32_dfsdm_get_channel - Get channel instance.
> + *
> + * @dfsdm: handle used to retrieve dfsdm context.
> + * @ch: DFSDM channel hardware parameters.
> + *
> + * Reserve DFSDM channel resource.
> + */
> +int stm32_dfsdm_get_channel(struct stm32_dfsdm *dfsdm,
> +			    struct stm32_dfsdm_channel *ch)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +	unsigned int id = ch->id;
> +
> +	dev_dbg(&priv->pdev->dev, "%s:get channel %d\n", __func__, id);
> +
> +	if (id >= priv->dfsdm.max_channels) {
> +		dev_err(&priv->pdev->dev, "channel (%d) is not valid\n", id);
> +		return -EINVAL;
> +	}
> +
> +	if ((ch->type.source != DFSDM_CHANNEL_EXTERNAL_INPUTS) &
> +	    (ch->serial_if.spi_clk != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL) &
> +	    (!priv->clkout_freq_req)) {
> +		dev_err(&priv->pdev->dev, "clkout not present\n");
> +		return -EINVAL;
> +	}
> +
> +	spin_lock(&priv->lock);
> +	if (!(BIT(id) & priv->free_channel_mask)) {
> +		spin_unlock(&priv->lock);
> +		dev_err(&priv->pdev->dev, "channel (%d) already in use\n", id);
> +		return -EBUSY;
> +	}
> +
> +	priv->free_channel_mask &= ~BIT(id);
> +	priv->channels[id] = *ch;
> +	spin_unlock(&priv->lock);
> +
> +	dev_dbg(&priv->pdev->dev, "%s: new mask %#x\n", __func__,
> +		priv->free_channel_mask);
> +
> +	/**
> +	 * Check clock constrainst between clkout and either
> +	 * dfsdm/audio clock:
> +	 * - In SPI mode (clkout is used): Fclk >= 4 * Fclkout
> +	 *   (e.g. CKOUTDIV >= 3)
> +	 * - In mancherster mode: Fclk >= 6 * Fclkout
> +	 */
> +	switch (ch->serial_if.type) {
> +	case DFSDM_CHANNEL_SPI_RISING:
> +	case DFSDM_CHANNEL_SPI_FALLING:
> +		if (priv->clkout_div && priv->clkout_div < 3)
> +			dev_warn(&priv->pdev->dev,
> +				 "Clock div should be higher than 3\n");
Really only warnings?  If requirements then error out. If not, then
a description of what the side effects of these would be would be great
here and perhaps even in the warning message.
> +		break;
> +	case DFSDM_CHANNEL_MANCHESTER_RISING:
> +	case DFSDM_CHANNEL_MANCHESTER_FALLING:
> +		if (priv->clkout_div && priv->clkout_div < 5)
> +			dev_warn(&priv->pdev->dev,
> +				 "Clock div should be higher than 5\n");
> +		break;
> +	}
> +
> +	stm32_dfsdm_init_channel(priv, ch);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_get_channel);
> +
> +/**
> + * stm32_dfsdm_release_channel - Release channel instance.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @ch_id: DFSDM channel identifier.
> + *
> + * Free the DFSDM channel resource.
> + */
> +void stm32_dfsdm_release_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +
> +	spin_lock(&priv->lock);
> +	priv->free_channel_mask |= BIT(ch_id);
> +	spin_unlock(&priv->lock);
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_release_channel);
> +
> +/**
> + * stm32_dfsdm_get_clk_out_rate - get clkout frequency.
> + *
> + * @dfsdm: handle used to retrieve dfsdm context.
> + * @rate: clock out rate in Hz.
> + *
> + * Provide output frequency used for external ADC.
> + * return EINVAL if clockout is not used else return 0.
> + */
> +int stm32_dfsdm_get_clk_out_rate(struct stm32_dfsdm *dfsdm, unsigned long *rate)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +	unsigned long int clk_rate;
> +
> +	if (!priv->clkout_div)
> +		return -EINVAL;
> +
> +	clk_rate = clk_get_rate(priv->aclk ? priv->aclk : priv->clk);
> +	*rate = clk_rate / (priv->clkout_div + 1);
> +	dev_dbg(&priv->pdev->dev, "%s: clkout: %ld (Hz)\n", __func__, *rate);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_get_clk_out_rate);
> +
> +static int stm32_dfsdm_parse_of(struct platform_device *pdev,
> +				struct dfsdm_priv *priv)
> +{
> +	struct device_node *node = pdev->dev.of_node;
> +	struct resource *res;
> +	int ret, val;
> +
> +	if (!node)
> +		return -EINVAL;
> +
> +	/* Get resources */
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(&pdev->dev, "Failed to get memory resource\n");
> +		return -ENODEV;
> +	}
> +	priv->phys_base = res->start;
> +	priv->base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(priv->base))
> +		return PTR_ERR(priv->base);
> +
> +	ret = of_property_read_u32(node, "st,clkout-freq", &val);
> +	if (!ret) {
> +		if (!val) {
> +			dev_err(&priv->pdev->dev,
> +				"st,clkout-freq cannot be 0\n");
> +			return -EINVAL;
> +		}
> +		priv->clkout_freq_req = val;
> +	} else if (ret != -EINVAL) {
> +		dev_err(&priv->pdev->dev, "Failed to get st,clkout-freq\n");
> +		return ret;
> +	}
> +
> +	/* Source clock */
> +	priv->clk = devm_clk_get(&pdev->dev, "dfsdm_clk");
> +	if (IS_ERR(priv->clk)) {
> +		dev_err(&pdev->dev, "No stm32_dfsdm_clk clock found\n");
> +		return -EINVAL;
> +	}
> +
> +	priv->aclk = devm_clk_get(&pdev->dev, "audio_clk");
> +	if (IS_ERR(priv->aclk))
> +		priv->aclk = NULL;
> +
> +	return 0;
> +};
> +
> +static const struct of_device_id stm32_dfsdm_of_match[] = {
> +	{
> +		.compatible = "st,stm32h7-dfsdm",
> +		.data = &stm32h7_data
> +	},
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, stm32_dfsdm_of_match);
> +
> +static int stm32_dfsdm_remove(struct platform_device *pdev)
> +{
> +	of_platform_depopulate(&pdev->dev);
> +
> +	return 0;
> +}
> +
> +static int stm32_dfsdm_probe(struct platform_device *pdev)
> +{
> +	struct dfsdm_priv *priv;
> +	struct device_node *pnode = pdev->dev.of_node;
> +	const struct of_device_id *of_id;
> +	const struct stm32_dev_data *dev_data;
> +	enum dfsdm_clkout_src clk_src;
> +	int ret, i;
> +
> +	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->pdev = pdev;
> +
> +	/* Populate data structure depending on compatibility */
> +	of_id = of_match_node(stm32_dfsdm_of_match, pnode);
> +	if (!of_id->data) {
> +		dev_err(&pdev->dev, "Data associated to device is missing\n");
> +		return -EINVAL;
> +	}
> +
> +	dev_data = (const struct stm32_dev_data *)of_id->data;
> +
> +	ret = stm32_dfsdm_parse_of(pdev, priv);
> +	if (ret < 0)
> +		return ret;
> +
> +	priv->regmap = devm_regmap_init_mmio(&pdev->dev, priv->base,
> +					    dev_data->regmap_cfg);
> +	if (IS_ERR(priv->regmap)) {
> +		ret = PTR_ERR(priv->regmap);
> +		dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",
> +			__func__, ret);
> +		return ret;
> +	}
> +
> +	priv->dfsdm = dev_data->dfsdm;
> +
> +	priv->filters = devm_kcalloc(&pdev->dev, dev_data->dfsdm.max_filters,
> +				     sizeof(*priv->filters), GFP_KERNEL);
> +	if (IS_ERR(priv->filters)) {
> +		ret = PTR_ERR(priv->filters);
> +		goto probe_err;
> +	}
> +
> +	for (i = 0; i < dev_data->dfsdm.max_filters; i++) {
> +		struct filter_params *params = &priv->filters[i];
> +
> +		params->id = i;
> +		params->irq = platform_get_irq(pdev, i);
> +		if (params->irq < 0) {
> +			dev_err(&pdev->dev, "Failed to get IRQ resource\n");
> +			ret = params->irq;
> +			goto probe_err;
> +		}
> +
> +		ret = devm_request_irq(&pdev->dev, params->irq, stm32_dfsdm_irq,
> +				       0, dev_name(&pdev->dev), params);
> +		if (ret) {
> +			dev_err(&pdev->dev, "Failed to register interrupt\n");
> +			goto probe_err;
> +		}
> +
> +		params->priv = priv;
> +	}
> +
> +	priv->channels = devm_kcalloc(&pdev->dev, priv->dfsdm.max_channels,
> +				      sizeof(*priv->channels), GFP_KERNEL);
> +	if (IS_ERR(priv->channels)) {
> +		ret = PTR_ERR(priv->channels);
> +		goto probe_err;
> +	}
> +	priv->free_filter_mask = BIT(priv->dfsdm.max_filters) - 1;
> +	priv->free_channel_mask = BIT(priv->dfsdm.max_channels) - 1;
> +
> +	platform_set_drvdata(pdev, &priv->dfsdm);
> +	spin_lock_init(&priv->lock);
> +
> +	priv->clkout_div = stm32_dfsdm_get_clkout_divider(priv,
> +						    clk_get_rate(priv->clk));
> +
> +	ret = of_platform_populate(pnode, NULL, NULL, &pdev->dev);
> +	if (ret < 0)
> +		goto probe_err;
> +
> +	clk_src = priv->aclk ? AUDIO_CLK : DFSDM_CLK;
> +
I'd like to see a comment here saying what this is doing.
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
> +			  DFSDM_CHCFGR1_CKOUTSRC_MASK,
> +			  DFSDM_CHCFGR1_CKOUTSRC(clk_src));
> +	return 0;
> +
> +probe_err:
> +	return ret;
> +}
> +
> +static struct platform_driver stm32_dfsdm_driver = {
> +	.probe = stm32_dfsdm_probe,
> +	.remove = stm32_dfsdm_remove,
> +	.driver = {
> +		.name = "stm32-dfsdm",
> +		.of_match_table = stm32_dfsdm_of_match,
> +	},
> +};
> +
> +module_platform_driver(stm32_dfsdm_driver);
> +
> +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>");
> +MODULE_DESCRIPTION("STMicroelectronics STM32 dfsdm driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/mfd/stm32-dfsdm.h b/include/linux/mfd/stm32-dfsdm.h
> new file mode 100644
> index 0000000..f6eb788
> --- /dev/null
> +++ b/include/linux/mfd/stm32-dfsdm.h
> @@ -0,0 +1,324 @@
> +/*
> + * This file is part of STM32 DFSDM mfd driver API
> + *
> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
> + * Author(s): Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>.
> + *
> + * 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.
> + */
> +#ifndef MDF_STM32_DFSDM_H
> +#define MDF_STM32_DFSDM_H
> +
> +/*
> + * Channel definitions
> + */
> +#define DFSDM_CHANNEL_0    BIT(0)
> +#define DFSDM_CHANNEL_1    BIT(1)
> +#define DFSDM_CHANNEL_2    BIT(2)
> +#define DFSDM_CHANNEL_3    BIT(3)
> +#define DFSDM_CHANNEL_4    BIT(4)
> +#define DFSDM_CHANNEL_5    BIT(5)
> +#define DFSDM_CHANNEL_6    BIT(6)
> +#define DFSDM_CHANNEL_7    BIT(7)
> +
> +/* DFSDM channel input data packing */
> +enum stm32_dfsdm_data_packing {
> +	DFSDM_CHANNEL_STANDARD_MODE,    /* Standard data packing mode */
> +	DFSDM_CHANNEL_INTERLEAVED_MODE, /* Interleaved data packing mode */
> +	DFSDM_CHANNEL_DUAL_MODE         /* Dual data packing mode */
> +};
> +
> +/* DFSDM channel input multiplexer */
> +enum stm32_dfsdm_input_multiplexer {
> +	DFSDM_CHANNEL_EXTERNAL_INPUTS,    /* Data taken from external inputs */
> +	DFSDM_CHANNEL_INTERNAL_ADC,       /* Data taken from internal ADC */
> +	DFSDM_CHANNEL_INTERNAL_REGISTER,  /* Data taken from register */
> +};
> +
> +/* DFSDM channel serial interface type */
> +enum stm32_dfsdm_serial_in_type {
> +	DFSDM_CHANNEL_SPI_RISING,         /* SPI with rising edge */
> +	DFSDM_CHANNEL_SPI_FALLING,        /* SPI with falling edge */
We could use standard SPI naming for these first two. That would I think
describe these as clock phases. However, perhaps alongside the machester
coding it doesn't make sense to do so.

> +	DFSDM_CHANNEL_MANCHESTER_RISING,  /* Manchester with rising edge */
> +	DFSDM_CHANNEL_MANCHESTER_FALLING, /* Manchester with falling edge */
> +};
> +
> +/* DFSDM channel serial spi clock source */
> +enum stm32_dfsdm_spi_clk_src {
> +	/* External SPI clock */
> +	DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL,
> +	/* Internal SPI clock */
> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL,
> +	/* Internal SPI clock divided by 2, falling edge */
> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING,
> +	/* Internal SPI clock divided by 2, rising edge */
> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING
> +};
> +
> +/* DFSDM channel input pins */
> +enum stm32_dfsdm_serial_in_select {
> +	/* Serial input taken from pins of the same channel (y) */
> +	DFSDM_CHANNEL_SAME_CHANNEL_PINS,
> +	/* Serial input taken from pins of the following channel (y + 1)*/
> +	DFSDM_CHANNEL_NEXT_CHANNEL_PINS,
> +};
> +
> +/**
> + * struct stm32_dfsdm_input_type - DFSDM channel init structure definition.
> + * @DataPacking: Standard, interleaved or dual mode for internal register.
> + * @source: channel source: internal DAC, serial input or memory.
> + */
> +struct stm32_dfsdm_input_type {
> +	enum stm32_dfsdm_data_packing DataPacking;
> +	enum stm32_dfsdm_input_multiplexer source;
> +};
> +
> +/**
> + * struct stm32_dfsdm_serial_if - DFSDM serial interface parameters.
> + * @type:	Serial interface type.
> + * @spi_clk:	SPI clock source.
> + * @pins:	select serial interface associated to the channel
> + */
> +struct stm32_dfsdm_serial_if {
> +	enum stm32_dfsdm_serial_in_type type;
> +	enum stm32_dfsdm_spi_clk_src spi_clk;
> +	enum stm32_dfsdm_serial_in_select pins;
> +};
> +
> +/**
> + * struct stm32_dfsdm_channel - DFSDM channel hardware parameters.
> + * @id:		DFSDM channel identifier.
> + * @type:	DFSDM channel input parameters.
> + * @serial_if:	DFSDM channel serial interface parameters.
> + *		Mandatory for DFSDM_CHANNEL_EXTERNAL_INPUTS.
> + */
> +struct stm32_dfsdm_channel {
> +	unsigned int id;
> +	struct stm32_dfsdm_input_type type;
> +	struct stm32_dfsdm_serial_if serial_if;
> +};
> +
> +/**
> + * struct stm32_dfsdm_ch_cfg - DFSDM channel config.
> + * @offset:		DFSDM channel 24 bit calibration offset.
> + * @right_bit_shift:	DFSDM channel right bit shift of the data result.
> + */
> +struct stm32_dfsdm_ch_cfg {
> +	unsigned int offset;
> +	unsigned int right_bit_shift;
> +};
> +
> +/*
> + * Filter definitions
Single line comment syntax.  Make sure this is correct throughout as otherwise
we'll only get 'fix' patches turning up after this hits linux-next.
> + */
> +
> +#define DFSDM_MIN_INT_OVERSAMPLING 1
> +#define DFSDM_MAX_INT_OVERSAMPLING 256
> +#define DFSDM_MIN_FL_OVERSAMPLING 1
Oversampling of 1 is effectively not oversampling so do we need this?

> +#define DFSDM_MAX_FL_OVERSAMPLING 1024
> +
> +enum stm32_dfsdm_events {
Slightly clunky enum usage.  I'd just use the value and put the BIT around
whereever it is used.
> +	DFSDM_EVENT_INJ_EOC =	BIT(0), /* Injected end of conversion event */
> +	DFSDM_EVENT_REG_EOC =	BIT(1), /* Regular end of conversion event */
> +	DFSDM_EVENT_INJ_XRUN =	BIT(2), /* Injected conversion overrun event */
> +	DFSDM_EVENT_REG_XRUN =	BIT(3), /* Regular conversion overrun event */
> +	DFSDM_EVENT_AWD =	BIT(4), /* Analog watchdog event */
> +	DFSDM_EVENT_SCD =	BIT(5), /* Short circuit detector event */
> +	DFSDM_EVENT_CKA =	BIT(6), /* Clock abscence detection event */
> +};
> +
> +#define STM32_DFSDM_EVENT_MASK 0x3F
> +
> +/* DFSDM filter order  */
> +enum stm32_dfsdm_sinc_order {
> +	DFSDM_FASTSINC_ORDER, /* FastSinc filter type */
> +	DFSDM_SINC1_ORDER,    /* Sinc 1 filter type */
> +	DFSDM_SINC2_ORDER,    /* Sinc 2 filter type */
> +	DFSDM_SINC3_ORDER,    /* Sinc 3 filter type */
> +	DFSDM_SINC4_ORDER,    /* Sinc 4 filter type (N.A. for watchdog) */
> +	DFSDM_SINC5_ORDER,    /* Sinc 5 filter type (N.A. for watchdog) */
> +	DFSDM_NB_SINC_ORDER,
> +};
> +
> +/* DFSDM filter order */
Please check your comments - this one is clearly wrong.

> +enum stm32_dfsdm_state {
> +	DFSDM_DISABLE,
> +	DFSDM_ENABLE,
> +};
> +
> +/**
> + * struct stm32_dfsdm_sinc_filter - DFSDM Sinc filter structure definition
> + * @order: DFSM filter order.
> + * @oversampling: DFSDM filter oversampling:
> + *		  post processing filter: min = 1, max = 1024.
> + */
> +struct stm32_dfsdm_sinc_filter {
> +	enum stm32_dfsdm_sinc_order order;
> +	unsigned int oversampling;
> +};
> +
> +/* DFSDM filter conversion trigger */
> +enum stm32_dfsdm_trigger {
> +	DFSDM_FILTER_SW_TRIGGER,   /* Software trigger */
> +	DFSDM_FILTER_SYNC_TRIGGER, /* Synchronous with DFSDM0 */
> +	DFSDM_FILTER_EXT_TRIGGER,  /* External trigger (only for injected) */
> +};
> +
> +/* DFSDM filter external trigger polarity */
> +enum stm32_dfsdm_filter_ext_trigger_pol {
> +	DFSDM_FILTER_EXT_TRIG_NO_TRIG,      /* Trigger disable */
> +	DFSDM_FILTER_EXT_TRIG_RISING_EDGE,  /* Rising edge */
> +	DFSDM_FILTER_EXT_TRIG_FALLING_EDGE, /* Falling edge */
> +	DFSDM_FILTER_EXT_TRIG_BOTH_EDGES,   /* Rising and falling edges */
> +};
> +
> +/* DFSDM filter conversion type */
> +enum stm32_dfsdm_conv_type {
> +	DFSDM_FILTER_REG_CONV,      /* Regular conversion */
> +	DFSDM_FILTER_SW_INJ_CONV,   /* Injected conversion */
> +	DFSDM_FILTER_TRIG_INJ_CONV, /* Injected conversion */
> +};
> +
> +/* DFSDM filter regular synchronous mode */
> +enum stm32_dfsdm_conv_rsync {
> +	DFSDM_FILTER_RSYNC_OFF, /* regular conversion asynchronous */
> +	DFSDM_FILTER_RSYNC_ON,  /* regular conversion synchronous with filter0*/
stray 0?
> +};
> +
> +/**
> + * struct stm32_dfsdm_regular - DFSDM filter conversion parameters structure
> + * @ch_src:	Channel source from 0 to 7.
> + * @fast_mode:	Enable/disable fast mode for regular conversion.
> + * @dma_mode:	Enable/disable dma mode.
> + * @cont_mode	Enable/disable continuous conversion.
> + * @sync_mode	Enable/disable synchro mode.
> + */
> +struct stm32_dfsdm_regular {
> +	unsigned int ch_src;
> +	bool fast_mode;
> +	bool dma_mode;
> +	bool cont_mode;
> +	bool sync_mode;
> +};
> +
> +/**
> + * struct stm32_dfsdm_injected - DFSDM filter  conversion parameters structure
> + * @trigger:	Trigger used to start injected conversion.
> + * @trig_src:	External trigger, 0 to 30 (refer to datasheet for details).
> + * @trig_pol:	External trigger edge: software, rising, falling or both.
> + * @scan_mode:	Enable/disable scan mode for injected conversion.
> + * @ch_group:	mask containing channels to scan ( set bit y to scan
> + *		channel y).
> + * @dma_mode:	DFSDM channel input parameters.
> + */
> +struct stm32_dfsdm_injected {
> +	enum stm32_dfsdm_trigger trigger;
> +	unsigned int trig_src;
> +	enum stm32_dfsdm_filter_ext_trigger_pol trig_pol;
> +	bool scan_mode;
> +	unsigned int ch_group;
> +	bool dma_mode;
> +};
> +
> +struct stm32_dfsdm;
> +
> +/**
> + * struct stm32_dfsdm_fl_event - DFSDM filters event
> + * @cb:	User event callback with parameters. be carful this function
> + *		is called under threaded IRQ context:
> + *			struct stm32_dfsdm *dfsdm: dfsdm handle,
> + *			unsigned int fl_id: filter id,
> + *			num stm32_dfsdm_events flag: event,
> + *			param: parameter associated to the event,
> + *			void *context: user context provided on registration.
> + * @context: User param to retrieve context.
> + */
> +struct stm32_dfsdm_fl_event {
> +	void (*cb)(struct stm32_dfsdm *, int, enum stm32_dfsdm_events,
> +		   unsigned int, void *);
> +	void *context;
> +};
> +
> +/**
> + * struct stm32_dfsdm_filter - DFSDM filter  conversion parameters structure
> + * @reg_params:		DFSDM regular conversion parameters.
> + *			this param is optional and not taken into account if
> + *			@inj_params is defined.
> + * @inj_params:		DFSDM injected conversion parameters (optional).
> + * @filter_params:	DFSDM filter parameters.
> + * @event:		Events callback.
> + * @int_oversampling:	Integrator oversampling ratio for average purpose
> + *			(range from 1 to 256).
> + * @ext_det_ch_mask:	Extreme detector mask for channel selection
> + *			mask generated using DFSDM_CHANNEL_0 to
> + *			DFSDM_CHANNEL_7. If 0 feature is disable.
> + */
> +struct stm32_dfsdm_filter {
> +	struct stm32_dfsdm_regular *reg_params;
> +	struct stm32_dfsdm_injected *inj_params;
> +	struct stm32_dfsdm_sinc_filter sinc_params;
> +	struct stm32_dfsdm_fl_event event;
> +	unsigned int int_oversampling;
> +};
> +
> +/**
> + * struct stm32_dfsdm - DFSDM context structure.
> + *
> + * @trig_info: Trigger name and id available last member name is null.
> + * @max_channels: max number of channels available.
> + * @max_filters: max number of filters available.
> + *
> + * Notice That structure is filled by mdf driver and must not be updated by
mfd
> + * user.
> + */
> +struct stm32_dfsdm {
> +	unsigned int max_channels;
> +	unsigned int max_filters;
> +};
> +
> +int stm32_dfsdm_get_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
> +void stm32_dfsdm_release_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
> +
> +dma_addr_t stm32_dfsdm_get_filter_dma_phy_addr(struct stm32_dfsdm *dfsdm,
> +					       unsigned int fl_id,
> +					       enum stm32_dfsdm_conv_type conv);
> +
> +int stm32_dfsdm_configure_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
> +				 struct stm32_dfsdm_filter *filter);
> +void stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
> +			      enum stm32_dfsdm_conv_type conv);
> +void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
> +
> +void stm32_dfsdm_read_fl_conv(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
> +			      u32 *val, int *ch_id,
> +			      enum stm32_dfsdm_conv_type type);
> +
> +int stm32_dfsdm_unregister_fl_event(struct stm32_dfsdm *dfsdm,
> +				    unsigned int fl_id,
> +				    enum stm32_dfsdm_events event,
> +				    unsigned int ch_mask);
> +int stm32_dfsdm_register_fl_event(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
> +				  enum stm32_dfsdm_events event,
> +				  unsigned int ch_mask);
> +
> +int stm32_dfsdm_get_channel(struct stm32_dfsdm *dfsdm,
> +			    struct stm32_dfsdm_channel *ch);
> +void stm32_dfsdm_release_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
> +
> +int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id,
> +			      struct stm32_dfsdm_ch_cfg *cfg);
> +void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
> +
> +int stm32_dfsdm_get_clk_out_rate(struct stm32_dfsdm *dfsdm,
> +				 unsigned long *rate);
> +
> +#endif
> 

--
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] 130+ messages in thread

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-29 11:53         ` Jonathan Cameron
  0 siblings, 0 replies; 130+ messages in thread
From: Jonathan Cameron @ 2017-01-29 11:53 UTC (permalink / raw)
  To: Arnaud Pouliquen, devicetree, linux-arm-kernel, linux-iio,
	alsa-devel, Lee Jones, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre Torgue

On 23/01/17 16:32, Arnaud Pouliquen wrote:
> DFSDM hardware IP can be used at the same time for ADC sigma delta
> conversion and audio PDM microphone.
> MFD driver is in charge of configuring IP registers and managing IP clocks.
> For this it exports an API to handles filters and channels resources.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
This is somewhat of a beast.  I would be tempted to build it up in more
bite sized chunks.

Obvious things to drop from a first version (basically to make it easier
to review) would be injected supported.  There may well be others but you'll
have a better feel for that than me.

I really don't like the wrappers round the regmap functions though.
They mean you are papering over errors if they occur and make the code
slightly harder to read by implying that something else is going on.

Yes the code will be longer without them, but you will also be forced to think
properly about error paths.

Anyhow, was mostly reading this to get a feel for what was going on in the
whole series so not really a terribly thorough review I'm afraid. Sorry about
that!

Jonathan
> ---
>  drivers/mfd/Kconfig             |   11 +
>  drivers/mfd/Makefile            |    2 +
>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
>  5 files changed, 1601 insertions(+)
>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
>  create mode 100644 drivers/mfd/stm32-dfsdm.c
>  create mode 100644 include/linux/mfd/stm32-dfsdm.h
> 
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index c6df644..4bb660b 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -1607,6 +1607,17 @@ config MFD_STW481X
>  	  in various ST Microelectronics and ST-Ericsson embedded
>  	  Nomadik series.
>  
> +config MFD_STM32_DFSDM
> +	tristate "ST Microelectronics STM32 DFSDM"
> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
> +	select MFD_CORE
> +	select REGMAP
> +	select REGMAP_MMIO
> +	help
> +	  Select this option to enable the STM32 Digital Filter
> +	  for Sigma Delta Modulators (DFSDM) driver used
> +	  in various STM32 series.
> +
>  menu "Multimedia Capabilities Port drivers"
>  	depends on ARCH_SA1100
>  
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index 9834e66..1f095e5 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -211,3 +211,5 @@ obj-$(CONFIG_INTEL_SOC_PMIC)	+= intel-soc-pmic.o
>  obj-$(CONFIG_MFD_MT6397)	+= mt6397-core.o
>  
>  obj-$(CONFIG_MFD_ALTERA_A10SR)	+= altera-a10sr.o
> +
> +obj-$(CONFIG_MFD_STM32_DFSDM)	+= stm32-dfsdm.o
> \ No newline at end of file
> diff --git a/drivers/mfd/stm32-dfsdm-reg.h b/drivers/mfd/stm32-dfsdm-reg.h
> new file mode 100644
> index 0000000..05ff702
> --- /dev/null
> +++ b/drivers/mfd/stm32-dfsdm-reg.h
> @@ -0,0 +1,220 @@
> +/*
> + * This file is part of STM32 DFSDM mfd driver
> + *
> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
> + * Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com>.
> + *
> + * 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.
> + */
> +
> +#ifndef MDF_STM32_DFSDM_REG_H
> +#define MDF_STM32_DFSDM_REG_H
> +
> +#include <linux/bitfield.h>
> +/*
> + * Channels definitions
> + */
> +#define DFSDM_CHCFGR1(y)  ((y) * 0x20 + 0x00)
> +#define DFSDM_CHCFGR2(y)  ((y) * 0x20 + 0x04)
> +#define DFSDM_AWSCDR(y)   ((y) * 0x20 + 0x08)
> +#define DFSDM_CHWDATR(y)  ((y) * 0x20 + 0x0C)
> +#define DFSDM_CHDATINR(y) ((y) * 0x20 + 0x10)
> +
> +/* CHCFGR1: Channel configuration register 1 */
> +#define DFSDM_CHCFGR1_SITP_MASK     GENMASK(1, 0)
> +#define DFSDM_CHCFGR1_SITP(v)       FIELD_PREP(DFSDM_CHCFGR1_SITP_MASK, v)
> +#define DFSDM_CHCFGR1_SPICKSEL_MASK GENMASK(3, 2)
> +#define DFSDM_CHCFGR1_SPICKSEL(v)   FIELD_PREP(DFSDM_CHCFGR1_SPICKSEL_MASK, v)
> +#define DFSDM_CHCFGR1_SCDEN_MASK    BIT(5)
> +#define DFSDM_CHCFGR1_SCDEN(v)      FIELD_PREP(DFSDM_CHCFGR1_SCDEN_MASK, v)
> +#define DFSDM_CHCFGR1_CKABEN_MASK   BIT(6)
> +#define DFSDM_CHCFGR1_CKABEN(v)     FIELD_PREP(DFSDM_CHCFGR1_CKABEN_MASK, v)
> +#define DFSDM_CHCFGR1_CHEN_MASK     BIT(7)
> +#define DFSDM_CHCFGR1_CHEN(v)       FIELD_PREP(DFSDM_CHCFGR1_CHEN_MASK, v)
> +#define DFSDM_CHCFGR1_CHINSEL_MASK  BIT(8)
> +#define DFSDM_CHCFGR1_CHINSEL(v)    FIELD_PREP(DFSDM_CHCFGR1_CHINSEL_MASK, v)
> +#define DFSDM_CHCFGR1_DATMPX_MASK   GENMASK(13, 12)
> +#define DFSDM_CHCFGR1_DATMPX(v)     FIELD_PREP(DFSDM_CHCFGR1_DATMPX_MASK, v)
> +#define DFSDM_CHCFGR1_DATPACK_MASK  GENMASK(15, 14)
> +#define DFSDM_CHCFGR1_DATPACK(v)    FIELD_PREP(DFSDM_CHCFGR1_DATPACK_MASK, v)
> +#define DFSDM_CHCFGR1_CKOUTDIV_MASK GENMASK(23, 16)
> +#define DFSDM_CHCFGR1_CKOUTDIV(v)   FIELD_PREP(DFSDM_CHCFGR1_CKOUTDIV_MASK, v)
> +#define DFSDM_CHCFGR1_CKOUTSRC_MASK BIT(30)
> +#define DFSDM_CHCFGR1_CKOUTSRC(v)   FIELD_PREP(DFSDM_CHCFGR1_CKOUTSRC_MASK, v)
> +#define DFSDM_CHCFGR1_DFSDMEN_MASK  BIT(31)
> +#define DFSDM_CHCFGR1_DFSDMEN(v)    FIELD_PREP(DFSDM_CHCFGR1_DFSDMEN_MASK, v)
> +
> +/* CHCFGR2: Channel configuration register 2 */
> +#define DFSDM_CHCFGR2_DTRBS_MASK    GENMASK(7, 3)
> +#define DFSDM_CHCFGR2_DTRBS(v)      FIELD_PREP(DFSDM_CHCFGR2_DTRBS_MASK, v)
> +#define DFSDM_CHCFGR2_OFFSET_MASK   GENMASK(31, 8)
> +#define DFSDM_CHCFGR2_OFFSET(v)     FIELD_PREP(DFSDM_CHCFGR2_OFFSET_MASK, v)
> +
> +/* AWSCDR: Channel analog watchdog and short circuit detector */
> +#define DFSDM_AWSCDR_SCDT_MASK    GENMASK(7, 0)
> +#define DFSDM_AWSCDR_SCDT(v)      FIELD_PREP(DFSDM_AWSCDR_SCDT_MASK, v)
> +#define DFSDM_AWSCDR_BKSCD_MASK   GENMASK(15, 12)
> +#define DFSDM_AWSCDR_BKSCD(v)	  FIELD_PREP(DFSDM_AWSCDR_BKSCD_MASK, v)
> +#define DFSDM_AWSCDR_AWFOSR_MASK  GENMASK(20, 16)
> +#define DFSDM_AWSCDR_AWFOSR(v)    FIELD_PREP(DFSDM_AWSCDR_AWFOSR_MASK, v)
> +#define DFSDM_AWSCDR_AWFORD_MASK  GENMASK(23, 22)
> +#define DFSDM_AWSCDR_AWFORD(v)    FIELD_PREP(DFSDM_AWSCDR_AWFORD_MASK, v)
> +
> +/*
> + * Filters definitions
> + */
> +#define DFSDM_FILTER_BASE_ADR		0x100
> +#define DFSDM_FILTER_REG_MASK		0x7F
> +#define DFSDM_FILTER_X_BASE_ADR(x)	((x) * 0x80 + DFSDM_FILTER_BASE_ADR)
> +
> +#define DFSDM_CR1(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x00)
> +#define DFSDM_CR2(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x04)
> +#define DFSDM_ISR(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x08)
> +#define DFSDM_ICR(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x0C)
> +#define DFSDM_JCHGR(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x10)
> +#define DFSDM_FCR(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x14)
> +#define DFSDM_JDATAR(x)  (DFSDM_FILTER_X_BASE_ADR(x)  + 0x18)
> +#define DFSDM_RDATAR(x)  (DFSDM_FILTER_X_BASE_ADR(x)  + 0x1C)
> +#define DFSDM_AWHTR(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x20)
> +#define DFSDM_AWLTR(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x24)
> +#define DFSDM_AWSR(x)    (DFSDM_FILTER_X_BASE_ADR(x)  + 0x28)
> +#define DFSDM_AWCFR(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x2C)
> +#define DFSDM_EXMAX(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x30)
> +#define DFSDM_EXMIN(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x34)
> +#define DFSDM_CNVTIMR(x) (DFSDM_FILTER_X_BASE_ADR(x)  + 0x38)
> +
> +/* CR1 Control register 1 */
> +#define DFSDM_CR1_DFEN_MASK	BIT(0)
> +#define DFSDM_CR1_DFEN(v)	FIELD_PREP(DFSDM_CR1_DFEN_MASK, v)
> +#define DFSDM_CR1_JSWSTART_MASK	BIT(1)
> +#define DFSDM_CR1_JSWSTART(v)	FIELD_PREP(DFSDM_CR1_JSWSTART_MASK, v)
> +#define DFSDM_CR1_JSYNC_MASK	BIT(3)
> +#define DFSDM_CR1_JSYNC(v)	FIELD_PREP(DFSDM_CR1_JSYNC_MASK, v)
> +#define DFSDM_CR1_JSCAN_MASK	BIT(4)
> +#define DFSDM_CR1_JSCAN(v)	FIELD_PREP(DFSDM_CR1_JSCAN_MASK, v)
> +#define DFSDM_CR1_JDMAEN_MASK	BIT(5)
> +#define DFSDM_CR1_JDMAEN(v)	FIELD_PREP(DFSDM_CR1_JDMAEN_MASK, v)
> +#define DFSDM_CR1_JEXTSEL_MASK	GENMASK(12, 8)
> +#define DFSDM_CR1_JEXTSEL(v)	FIELD_PREP(DFSDM_CR1_JEXTSEL_MASK, v)
> +#define DFSDM_CR1_JEXTEN_MASK	GENMASK(14, 13)
> +#define DFSDM_CR1_JEXTEN(v)	FIELD_PREP(DFSDM_CR1_JEXTEN_MASK, v)
> +#define DFSDM_CR1_RSWSTART_MASK	BIT(17)
> +#define DFSDM_CR1_RSWSTART(v)	FIELD_PREP(DFSDM_CR1_RSWSTART_MASK, v)
> +#define DFSDM_CR1_RCONT_MASK	BIT(18)
> +#define DFSDM_CR1_RCONT(v)	FIELD_PREP(DFSDM_CR1_RCONT_MASK, v)
> +#define DFSDM_CR1_RSYNC_MASK	BIT(19)
> +#define DFSDM_CR1_RSYNC(v)	FIELD_PREP(DFSDM_CR1_RSYNC_MASK, v)
> +#define DFSDM_CR1_RDMAEN_MASK	BIT(21)
> +#define DFSDM_CR1_RDMAEN(v)	FIELD_PREP(DFSDM_CR1_RDMAEN_MASK, v)
> +#define DFSDM_CR1_RCH_MASK	GENMASK(26, 24)
> +#define DFSDM_CR1_RCH(v)	FIELD_PREP(DFSDM_CR1_RCH_MASK, v)
> +#define DFSDM_CR1_FAST_MASK	BIT(29)
> +#define DFSDM_CR1_FAST(v)	FIELD_PREP(DFSDM_CR1_FAST_MASK, v)
> +#define DFSDM_CR1_AWFSEL_MASK	BIT(30)
> +#define DFSDM_CR1_AWFSEL(v)	FIELD_PREP(DFSDM_CR1_AWFSEL_MASK, v)
> +
> +/* CR2: Control register 2 */
> +#define DFSDM_CR2_IE_MASK	GENMASK(6, 0)
> +#define DFSDM_CR2_IE(v)		FIELD_PREP(DFSDM_CR2_IE_MASK, v)
> +#define DFSDM_CR2_JEOCIE_MASK	BIT(0)
> +#define DFSDM_CR2_JEOCIE(v)	FIELD_PREP(DFSDM_CR2_JEOCIE_MASK, v)
> +#define DFSDM_CR2_REOCIE_MASK	BIT(1)
> +#define DFSDM_CR2_REOCIE(v)	FIELD_PREP(DFSDM_CR2_REOCIE_MASK, v)
> +#define DFSDM_CR2_JOVRIE_MASK	BIT(2)
> +#define DFSDM_CR2_JOVRIE(v)	FIELD_PREP(DFSDM_CR2_JOVRIE_MASK, v)
> +#define DFSDM_CR2_ROVRIE_MASK	BIT(3)
> +#define DFSDM_CR2_ROVRIE(v)	FIELD_PREP(DFSDM_CR2_ROVRIE_MASK, v)
> +#define DFSDM_CR2_AWDIE_MASK	BIT(4)
> +#define DFSDM_CR2_AWDIE(v)	FIELD_PREP(DFSDM_CR2_AWDIE_MASK, v)
> +#define DFSDM_CR2_SCDIE_MASK	BIT(5)
> +#define DFSDM_CR2_SCDIE(v)	FIELD_PREP(DFSDM_CR2_SCDIE_MASK, v)
> +#define DFSDM_CR2_CKABIE_MASK	BIT(6)
> +#define DFSDM_CR2_CKABIE(v)	FIELD_PREP(DFSDM_CR2_CKABIE_MASK, v)
> +#define DFSDM_CR2_EXCH_MASK	GENMASK(15, 8)
> +#define DFSDM_CR2_EXCH(v)	FIELD_PREP(DFSDM_CR2_EXCH_MASK, v)
> +#define DFSDM_CR2_AWDCH_MASK	GENMASK(23, 16)
> +#define DFSDM_CR2_AWDCH(v)	FIELD_PREP(DFSDM_CR2_AWDCH_MASK, v)
> +
> +/* ISR: Interrupt status register */
> +#define DFSDM_ISR_JEOCF_MASK	BIT(0)
> +#define DFSDM_ISR_JEOCF(v)	FIELD_PREP(DFSDM_ISR_JEOCF_MASK, v)
> +#define DFSDM_ISR_REOCF_MASK	BIT(1)
> +#define DFSDM_ISR_REOCF(v)	FIELD_PREP(DFSDM_ISR_REOCF_MASK, v)
> +#define DFSDM_ISR_JOVRF_MASK	BIT(2)
> +#define DFSDM_ISR_JOVRF(v)	FIELD_PREP(DFSDM_ISR_JOVRF_MASK, v)
> +#define DFSDM_ISR_ROVRF_MASK	BIT(3)
> +#define DFSDM_ISR_ROVRF(v)	FIELD_PREP(DFSDM_ISR_ROVRF_MASK, v)
> +#define DFSDM_ISR_AWDF_MASK	BIT(4)
> +#define DFSDM_ISR_AWDF(v)	FIELD_PREP(DFSDM_ISR_AWDF_MASK, v)
> +#define DFSDM_ISR_JCIP_MASK	BIT(13)
> +#define DFSDM_ISR_JCIP(v)	FIELD_PREP(DFSDM_ISR_JCIP_MASK, v)
> +#define DFSDM_ISR_RCIP_MASK	BIT(14)
> +#define DFSDM_ISR_RCIP(v)	FIELD_PREP(DFSDM_ISR_RCIP, v)
> +#define DFSDM_ISR_CKABF_MASK	GENMASK(23, 16)
> +#define DFSDM_ISR_CKABF(v)	FIELD_PREP(DFSDM_ISR_CKABF_MASK, v)
> +#define DFSDM_ISR_SCDF_MASK	GENMASK(31, 24)
> +#define DFSDM_ISR_SCDF(v)	FIELD_PREP(DFSDM_ISR_SCDF_MASK, v)
> +
> +/* ICR: Interrupt flag clear register */
> +#define DFSDM_ICR_CLRJOVRF_MASK	      BIT(2)
> +#define DFSDM_ICR_CLRJOVRF(v)	      FIELD_PREP(DFSDM_ICR_CLRJOVRF_MASK, v)
> +#define DFSDM_ICR_CLRROVRF_MASK	      BIT(3)
> +#define DFSDM_ICR_CLRROVRF(v)	      FIELD_PREP(DFSDM_ICR_CLRROVRF_MASK, v)
> +#define DFSDM_ICR_CLRCKABF_MASK	      GENMASK(23, 16)
> +#define DFSDM_ICR_CLRCKABF(v)	      FIELD_PREP(DFSDM_ICR_CLRCKABF_MASK, v)
> +#define DFSDM_ICR_CLRCKABF_CH_MASK(y) BIT(16 + (y))
> +#define DFSDM_ICR_CLRCKABF_CH(v, y)   \
> +			   (((v) << (16 + (y))) & DFSDM_ICR_CLRCKABF_CH_MASK(y))
> +#define DFSDM_ICR_CLRSCDF_MASK	      GENMASK(31, 24)
> +#define DFSDM_ICR_CLRSCDF(v)	      FIELD_PREP(DFSDM_ICR_CLRSCDF_MASK, v)
> +#define DFSDM_ICR_CLRSCDF_CH_MASK(y)  BIT(24 + (y))
> +#define DFSDM_ICR_CLRSCDF_CH(v, y)    \
> +			       (((v) << (24 + (y))) & DFSDM_ICR_CLRSCDF_MASK(y))
> +
> +/* FCR: Filter control register */
> +#define DFSDM_FCR_IOSR_MASK	GENMASK(7, 0)
> +#define DFSDM_FCR_IOSR(v)	FIELD_PREP(DFSDM_FCR_IOSR_MASK, v)
> +#define DFSDM_FCR_FOSR_MASK	GENMASK(25, 16)
> +#define DFSDM_FCR_FOSR(v)	FIELD_PREP(DFSDM_FCR_FOSR_MASK, v)
> +#define DFSDM_FCR_FORD_MASK	GENMASK(31, 29)
> +#define DFSDM_FCR_FORD(v)	FIELD_PREP(DFSDM_FCR_FORD_MASK, v)
> +
> +/* RDATAR: Filter data register for regular channel */
> +#define DFSDM_DATAR_CH_MASK	GENMASK(2, 0)
> +#define DFSDM_DATAR_DATA_OFFSET 8
> +#define DFSDM_DATAR_DATA_MASK	GENMASK(31, DFSDM_DATAR_DATA_OFFSET)
> +
> +/* AWLTR: Filter analog watchdog low threshold register */
> +#define DFSDM_AWLTR_BKAWL_MASK	GENMASK(3, 0)
> +#define DFSDM_AWLTR_BKAWL(v)	FIELD_PREP(DFSDM_AWLTR_BKAWL_MASK, v)
> +#define DFSDM_AWLTR_AWLT_MASK	GENMASK(31, 8)
> +#define DFSDM_AWLTR_AWLT(v)	FIELD_PREP(DFSDM_AWLTR_AWLT_MASK, v)
> +
> +/* AWHTR: Filter analog watchdog low threshold register */
> +#define DFSDM_AWHTR_BKAWH_MASK	GENMASK(3, 0)
> +#define DFSDM_AWHTR_BKAWH(v)	FIELD_PREP(DFSDM_AWHTR_BKAWH_MASK, v)
> +#define DFSDM_AWHTR_AWHT_MASK	GENMASK(31, 8)
> +#define DFSDM_AWHTR_AWHT(v)	FIELD_PREP(DFSDM_AWHTR_AWHT_MASK, v)
> +
> +/* AWSR: Filter watchdog status register */
> +#define DFSDM_AWSR_AWLTF_MASK	GENMASK(7, 0)
> +#define DFSDM_AWSR_AWLTF(v)	FIELD_PREP(DFSDM_AWSR_AWLTF_MASK, v)
> +#define DFSDM_AWSR_AWHTF_MASK	GENMASK(15, 8)
> +#define DFSDM_AWSR_AWHTF(v)	FIELD_PREP(DFSDM_AWSR_AWHTF_MASK, v)
> +
> +/* AWCFR: Filter watchdog status register */
> +#define DFSDM_AWCFR_AWLTF_MASK	GENMASK(7, 0)
> +#define DFSDM_AWCFR_AWLTF(v)	FIELD_PREP(DFSDM_AWCFR_AWLTF_MASK, v)
> +#define DFSDM_AWCFR_AWHTF_MASK	GENMASK(15, 8)
> +#define DFSDM_AWCFR_AWHTF(v)	FIELD_PREP(DFSDM_AWCFR_AWHTF_MASK, v)
> +
> +#endif
> diff --git a/drivers/mfd/stm32-dfsdm.c b/drivers/mfd/stm32-dfsdm.c
> new file mode 100644
> index 0000000..81ca29c
> --- /dev/null
> +++ b/drivers/mfd/stm32-dfsdm.c
> @@ -0,0 +1,1044 @@
> +/*
> + * This file is part of STM32 DFSDM mfd driver
> + *
> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
> + * Author(s): Arnaud Pouliquen <arnaud.pouliquen@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/interrupt.h>
> +#include <linux/mfd/core.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +
> +#include <linux/mfd/stm32-dfsdm.h>
> +
> +#include "stm32-dfsdm-reg.h"
> +
> +#define DFSDM_UPDATE_BITS(regm, reg, mask, val) \
> +		WARN_ON(regmap_update_bits(regm, reg, mask, val))
Don't do these wrappers please. Handle error correctly in all cases.
Reviewing as I tend to do backwards through the driver, I thought these
were doing something interesting.

Effectively you have no error handling as a result of these which needs
fixing.

> +
> +#define DFSDM_REG_READ(regm, reg, val) \
> +		WARN_ON(regmap_read(regm, reg, val))
> +
> +#define DFSDM_REG_WRITE(regm, reg, val) \
> +		WARN_ON(regmap_write(regm, reg, val))
> +
> +#define STM32H7_DFSDM_NUM_FILTERS	4
> +#define STM32H7_DFSDM_NUM_INPUTS	8
> +
> +enum dfsdm_clkout_src {
> +	DFSDM_CLK,
> +	AUDIO_CLK
> +};
> +
> +struct stm32_dev_data {
> +	const struct stm32_dfsdm dfsdm;
> +	const struct regmap_config *regmap_cfg;
> +};
> +
> +struct dfsdm_priv;
> +
> +struct filter_params {
> +	unsigned int id;
> +	int irq;
> +	struct stm32_dfsdm_fl_event event;
> +	u32 event_mask;
> +	struct dfsdm_priv *priv; /* Cross ref for context */
> +	unsigned int ext_ch_mask;
> +	unsigned int scan_ch;
> +};
> +
> +struct ch_params {
> +	struct stm32_dfsdm_channel ch;
> +};
> +
I'd like to see a lot more comments in here.  Perhaps full kernel-doc
as some elements are not that obvious at least to a fairly casual read.

> +struct dfsdm_priv {
> +	struct platform_device *pdev;
> +	struct stm32_dfsdm dfsdm;
> +
> +	spinlock_t lock; /* Used for resource sharing & interrupt lock */
> +
> +	/* Filters */
> +	struct filter_params *filters;
> +	unsigned int free_filter_mask;
> +	unsigned int scd_filter_mask;
> +	unsigned int ckab_filter_mask;
> +
> +	/* Channels */
> +	struct stm32_dfsdm_channel *channels;
> +	unsigned int free_channel_mask;
> +	atomic_t n_active_ch;
> +
> +	/* Clock */
> +	struct clk *clk;
> +	struct clk *aclk;
> +	unsigned int clkout_div;
> +	unsigned int clkout_freq_req;
> +
> +	/* Registers*/
> +	void __iomem *base;
> +	struct regmap *regmap;
> +	phys_addr_t phys_base;
> +};
> +
> +/*
> + * Common
> + */
> +static bool stm32_dfsdm_volatile_reg(struct device *dev, unsigned int reg)
> +{
> +	if (reg < DFSDM_FILTER_BASE_ADR)
> +		return false;
> +
> +	/*
> +	 * Mask is done on register to avoid to list registers of all them
> +	 * filter instances.
> +	 */
> +	switch (reg & DFSDM_FILTER_REG_MASK) {
> +	case DFSDM_CR1(0) & DFSDM_FILTER_REG_MASK:
> +	case DFSDM_ISR(0) & DFSDM_FILTER_REG_MASK:
> +	case DFSDM_JDATAR(0) & DFSDM_FILTER_REG_MASK:
> +	case DFSDM_RDATAR(0) & DFSDM_FILTER_REG_MASK:
> +		return true;
> +	}
> +
> +	return false;
> +}
> +
> +static const struct regmap_config stm32h7_dfsdm_regmap_cfg = {
> +	.reg_bits = 32,
> +	.val_bits = 32,
> +	.reg_stride = sizeof(u32),
> +	.max_register = DFSDM_CNVTIMR(STM32H7_DFSDM_NUM_FILTERS - 1),
> +	.volatile_reg = stm32_dfsdm_volatile_reg,
> +	.fast_io = true,
> +};
> +
> +static const struct stm32_dev_data stm32h7_data = {
> +	.dfsdm.max_channels = STM32H7_DFSDM_NUM_INPUTS,
> +	.dfsdm.max_filters = STM32H7_DFSDM_NUM_FILTERS,
> +	.regmap_cfg = &stm32h7_dfsdm_regmap_cfg,
> +};
> +
> +static int stm32_dfsdm_start_dfsdm(struct dfsdm_priv *priv)
> +{
> +	int ret;
> +	struct device *dev = &priv->pdev->dev;
> +
> +	if (atomic_inc_return(&priv->n_active_ch) == 1) {
> +		ret = clk_prepare_enable(priv->clk);
> +		if (ret < 0) {
> +			dev_err(dev, "Failed to start clock\n");
> +			return ret;
> +		}
> +		if (priv->aclk) {
> +			ret = clk_prepare_enable(priv->aclk);
> +			if (ret < 0) {
> +				dev_err(dev, "Failed to start audio clock\n");
> +				return ret;
> +			}
> +		}
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
> +				  DFSDM_CHCFGR1_CKOUTDIV_MASK,
> +				  DFSDM_CHCFGR1_CKOUTDIV(priv->clkout_div));
> +
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
> +				  DFSDM_CHCFGR1_DFSDMEN_MASK,
> +				  DFSDM_CHCFGR1_DFSDMEN(1));
> +	}
> +
> +	dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,
> +		atomic_read(&priv->n_active_ch));
> +
> +	return 0;
> +}
> +
> +static void stm32_dfsdm_stop_dfsdm(struct dfsdm_priv *priv)
> +{
> +	if (atomic_dec_and_test(&priv->n_active_ch)) {
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
> +				  DFSDM_CHCFGR1_DFSDMEN_MASK,
> +				  DFSDM_CHCFGR1_DFSDMEN(0));
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
> +				  DFSDM_CHCFGR1_CKOUTDIV_MASK,
> +				  DFSDM_CHCFGR1_CKOUTDIV(0));
> +		clk_disable_unprepare(priv->clk);
> +		if (priv->aclk)
> +			clk_disable_unprepare(priv->aclk);
> +	}
> +	dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,
> +		atomic_read(&priv->n_active_ch));
> +}
> +
> +static unsigned int stm32_dfsdm_get_clkout_divider(struct dfsdm_priv *priv,
> +						   unsigned long rate)
> +{
> +	unsigned int delta, div;
> +
> +	/* div = 0 disables the clockout */
> +	if (!priv->clkout_freq_req)
> +		return 0;
> +
> +	div = DIV_ROUND_CLOSEST(rate, priv->clkout_freq_req);
> +
> +	delta = rate - (priv->clkout_freq_req * div);
> +	if (delta)
> +		dev_warn(&priv->pdev->dev,
> +			 "clkout not accurate. delta (Hz): %d\n", delta);
> +
> +	dev_dbg(&priv->pdev->dev, "%s: clk: %lu (Hz), div %u\n",
> +		__func__, rate, div);
> +
> +	return (div - 1);
> +}
> +
> +/*
> + * Filters
> + */
> +
> +static int stm32_dfsdm_clear_event(struct dfsdm_priv *priv, unsigned int fl_id,
> +				   unsigned int event, int mask)
> +{
> +	int val;
> +
> +	switch (event) {
> +	case DFSDM_EVENT_INJ_EOC:
> +		DFSDM_REG_READ(priv->regmap, DFSDM_JDATAR(fl_id), &val);
> +		break;
> +	case DFSDM_EVENT_REG_EOC:
> +		DFSDM_REG_READ(priv->regmap, DFSDM_RDATAR(fl_id), &val);
> +		break;
> +	case DFSDM_EVENT_INJ_XRUN:
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_ICR(fl_id),
> +				  DFSDM_ICR_CLRJOVRF_MASK,
> +				  DFSDM_ICR_CLRJOVRF_MASK);
> +		break;
> +	case DFSDM_EVENT_REG_XRUN:
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_ICR(fl_id),
> +				  DFSDM_ICR_CLRROVRF_MASK,
> +				  DFSDM_ICR_CLRROVRF_MASK);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
> +{
> +	struct filter_params *params = arg;
> +	unsigned int status;
> +	struct dfsdm_priv *priv = params->priv;
> +	unsigned int event_mask = params->event_mask;
> +
> +	DFSDM_REG_READ(priv->regmap, DFSDM_ISR(params->id), &status);
> +
> +	if (status & DFSDM_ISR_JOVRF_MASK) {
> +		if (event_mask & DFSDM_EVENT_INJ_XRUN) {
> +			params->event.cb(&priv->dfsdm, params->id,
> +					 DFSDM_EVENT_INJ_XRUN, 0,
> +					 params->event.context);
> +		}
> +		stm32_dfsdm_clear_event(priv, params->id, DFSDM_EVENT_INJ_XRUN,
> +					0);
> +	}
> +
> +	if (status & DFSDM_ISR_ROVRF_MASK) {
> +		if (event_mask & DFSDM_EVENT_REG_XRUN) {
> +			params->event.cb(&priv->dfsdm, params->id,
> +					 DFSDM_EVENT_REG_XRUN, 0,
> +					 params->event.context);
> +		}
> +		stm32_dfsdm_clear_event(priv, params->id, DFSDM_EVENT_REG_XRUN,
> +					0);
> +	}
> +
> +	if (status & DFSDM_ISR_JEOCF_MASK) {
> +		if (event_mask & DFSDM_EVENT_INJ_EOC)
> +			params->event.cb(&priv->dfsdm, params->id,
> +					 DFSDM_EVENT_INJ_EOC, 0,
> +					 params->event.context);
> +		else
> +			stm32_dfsdm_clear_event(priv, params->id,
> +						DFSDM_EVENT_INJ_EOC, 0);
> +	}
> +
> +	if (status & DFSDM_ISR_REOCF_MASK) {
> +		if (event_mask & DFSDM_EVENT_REG_EOC)
> +			params->event.cb(&priv->dfsdm, params->id,
> +					 DFSDM_EVENT_REG_EOC, 0,
> +					 params->event.context);
> +		else
> +			stm32_dfsdm_clear_event(priv, params->id,
> +						DFSDM_EVENT_REG_EOC, 0);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static void stm32_dfsdm_configure_reg_conv(struct dfsdm_priv *priv,
> +					   unsigned int fl_id,
> +					   struct stm32_dfsdm_regular *params)
> +{
> +	unsigned int ch_id = params->ch_src;
> +
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RCH_MASK,
> +			  DFSDM_CR1_RCH(ch_id));
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_FAST_MASK,
> +			  DFSDM_CR1_FAST(params->fast_mode));
> +
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RCONT_MASK,
> +			  DFSDM_CR1_RCONT(params->cont_mode));
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RDMAEN_MASK,
> +			  DFSDM_CR1_RDMAEN(params->dma_mode));
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RSYNC_MASK,
> +			  DFSDM_CR1_RSYNC(params->sync_mode));
> +
> +	priv->filters[fl_id].scan_ch = BIT(ch_id);
> +}
> +
> +static void stm32_dfsdm_configure_inj_conv(struct dfsdm_priv *priv,
> +					   unsigned int fl_id,
> +					   struct stm32_dfsdm_injected *params)
> +{
> +	int val;
> +
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_JSCAN_MASK,
> +			  DFSDM_CR1_JSCAN(params->scan_mode));
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_JDMAEN_MASK,
> +			  DFSDM_CR1_JDMAEN(params->dma_mode));
> +
> +	val = (params->trigger == DFSDM_FILTER_EXT_TRIGGER) ?
> +	      params->trig_src : 0;
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id),
> +			  DFSDM_CR1_JEXTSEL_MASK,
> +			  DFSDM_CR1_JEXTSEL(val));
> +
> +	val = (params->trigger == DFSDM_FILTER_SYNC_TRIGGER) ? 1 : 0;
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_JSYNC_MASK,
> +			  DFSDM_CR1_JSYNC(val));
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_JEXTEN_MASK,
> +			  DFSDM_CR1_JEXTEN(params->trig_pol));
> +	priv->filters[fl_id].scan_ch = params->ch_group;
> +
> +	DFSDM_REG_WRITE(priv->regmap, DFSDM_JCHGR(fl_id), params->ch_group);
> +}
> +
> +/**
> + * stm32_dfsdm_configure_filter - Configure filter.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: Filter id.
> + * @conv: Conversion type regular or injected.
> + */
> +int stm32_dfsdm_configure_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
> +				 struct stm32_dfsdm_filter *fl_cfg)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv,
> +					       dfsdm);
> +	struct stm32_dfsdm_sinc_filter *sparams = &fl_cfg->sinc_params;
> +
> +	dev_dbg(&priv->pdev->dev, "%s:config filter %d\n", __func__, fl_id);
> +
> +	/* Average integrator oversampling */
> +	if ((!fl_cfg->int_oversampling) ||
> +	    (fl_cfg->int_oversampling > DFSDM_MAX_INT_OVERSAMPLING)) {
> +		dev_err(&priv->pdev->dev, "invalid integrator oversampling %d\n",
> +			fl_cfg->int_oversampling);
> +		return -EINVAL;
> +	}
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_FCR(fl_id), DFSDM_FCR_IOSR_MASK,
> +			  DFSDM_FCR_IOSR((fl_cfg->int_oversampling - 1)));
> +
> +	/* Oversamplings and filter*/
> +	if ((!sparams->oversampling) ||
> +	    (sparams->oversampling > DFSDM_MAX_FL_OVERSAMPLING)) {
> +		dev_err(&priv->pdev->dev, "invalid oversampling %d\n",
> +			sparams->oversampling);
> +		return -EINVAL;
> +	}
> +
> +	if (sparams->order > DFSDM_SINC5_ORDER) {
> +		dev_err(&priv->pdev->dev, "invalid filter order %d\n",
> +			sparams->order);
> +		return -EINVAL;
> +	}
> +
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FOSR_MASK,
> +			  DFSDM_FCR_FOSR((sparams->oversampling - 1)));
> +
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FORD_MASK,
> +			  DFSDM_FCR_FORD(sparams->order));
> +
> +	/* Conversion */
> +	if (fl_cfg->inj_params)
> +		stm32_dfsdm_configure_inj_conv(priv, fl_id, fl_cfg->inj_params);
> +	else if (fl_cfg->reg_params)
> +		stm32_dfsdm_configure_reg_conv(priv, fl_id, fl_cfg->reg_params);
> +
> +	priv->filters[fl_id].event = fl_cfg->event;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_configure_filter);
> +
> +/**
> + * stm32_dfsdm_start_filter - Start filter conversion.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: Filter id.
> + * @conv: Conversion type regular or injected.
> + */
> +void stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
> +			      enum stm32_dfsdm_conv_type conv)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +
> +	dev_dbg(&priv->pdev->dev, "%s:start filter %d\n", __func__, fl_id);
> +
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_DFEN_MASK,
> +			  DFSDM_CR1_DFEN(1));
> +
> +	if (conv == DFSDM_FILTER_REG_CONV) {
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id),
> +				  DFSDM_CR1_RSWSTART_MASK,
> +				  DFSDM_CR1_RSWSTART(1));
> +	} else if (conv == DFSDM_FILTER_SW_INJ_CONV) {
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id),
> +				  DFSDM_CR1_JSWSTART_MASK,
> +				  DFSDM_CR1_JSWSTART(1));
> +	}
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_start_filter);
> +
> +/**
> + * stm32_dfsdm_stop_filter - Stop filter conversion.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: Filter id.
> + */
> +void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +
> +	dev_dbg(&priv->pdev->dev, "%s:stop filter %d\n", __func__, fl_id);
> +
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_DFEN_MASK,
> +			  DFSDM_CR1_DFEN(0));
> +	priv->filters[fl_id].scan_ch = 0;
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_stop_filter);
> +
> +/**
> + * stm32_dfsdm_read_fl_conv - Read filter conversion.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: Filter id.
> + * @type: Regular or injected conversion.
> + */
> +void stm32_dfsdm_read_fl_conv(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
> +			      u32 *val, int *ch_id,
> +			      enum stm32_dfsdm_conv_type type)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +	int reg_v, offset;
> +
> +	if (type == DFSDM_FILTER_REG_CONV)
> +		offset = DFSDM_RDATAR(fl_id);
> +	else
> +		offset = DFSDM_JDATAR(fl_id);
> +
> +	DFSDM_REG_READ(priv->regmap, offset, &reg_v);
> +
> +	*ch_id = reg_v & DFSDM_DATAR_CH_MASK;
> +	*val = reg_v & DFSDM_DATAR_DATA_MASK;
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_read_fl_conv);
> +
> +/**
> + * stm32_dfsdm_get_filter - Get filter instance.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: Filter instance to reserve.
> + *
> + * Reserves a DFSDM filter resource.
> + */
> +int stm32_dfsdm_get_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv,
> +					       dfsdm);
> +	struct device *dev = &priv->pdev->dev;
> +
> +	spin_lock(&priv->lock);
> +	if (!(priv->free_filter_mask & BIT(fl_id))) {
> +		spin_unlock(&priv->lock);
> +		dev_err(dev, "filter resource %d available\n", fl_id);
> +		return -EBUSY;
> +	}
> +	priv->free_filter_mask &= ~BIT(fl_id);
> +
> +	spin_unlock(&priv->lock);
> +
> +	dev_dbg(dev, "%s: new mask %#x\n", __func__, priv->free_filter_mask);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_get_filter);
> +
> +/**
> + * stm32_dfsdm_release_filter - Release filter instance.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: Filter id.
> + *
> + * Free the DFSDM filter resource.
> + */
> +void stm32_dfsdm_release_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +
> +	spin_lock(&priv->lock);
> +	priv->free_filter_mask |= BIT(fl_id);
> +	spin_unlock(&priv->lock);
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_release_filter);
> +
> +/**
> + * stm32_dfsdm_get_filter_dma_addr - Get register address for dma transfer.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: Filter id.
> + * @conv: Conversion type.
> + */
> +dma_addr_t stm32_dfsdm_get_filter_dma_phy_addr(struct stm32_dfsdm *dfsdm,
> +					       unsigned int fl_id,
> +					       enum stm32_dfsdm_conv_type conv)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +
> +	if (conv == DFSDM_FILTER_REG_CONV)
> +		return (dma_addr_t)(priv->phys_base + DFSDM_RDATAR(fl_id));
> +	else
> +		return (dma_addr_t)(priv->phys_base + DFSDM_JDATAR(fl_id));
> +}
> +
> +/**
> + * stm32_dfsdm_register_fl_event - Register filter event.
What is a filter event?  More details good on things that are very
device specific like this.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: Filter id.
> + * @event: Event to unregister.
> + * @chan_mask: Mask of channels associated to filter.
> + *
> + * The function enables associated IRQ.
> + */
> +int stm32_dfsdm_register_fl_event(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
> +				  enum stm32_dfsdm_events event,
> +				  unsigned int chan_mask)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +	unsigned long flags, ulmask = chan_mask;
> +	int ret, i;
> +
> +	dev_dbg(&priv->pdev->dev, "%s:for filter %d: event %#x ch_mask %#x\n",
> +		__func__, fl_id, event, chan_mask);
> +
> +	if (event > DFSDM_EVENT_CKA)
> +		return -EINVAL;
> +
> +	/* Clear interrupt before enable them */
> +	ret = stm32_dfsdm_clear_event(priv, fl_id, event, chan_mask);
> +	if (ret < 0)
> +		return ret;
> +
> +	spin_lock_irqsave(&priv->lock, flags);
> +	/* Enable interrupts */
> +	switch (event) {
> +	case DFSDM_EVENT_SCD:
> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
> +					  DFSDM_CHCFGR1_SCDEN_MASK,
> +					  DFSDM_CHCFGR1_SCDEN(1));
> +		}
> +		if (!priv->scd_filter_mask)
> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
> +					  DFSDM_CR2_SCDIE_MASK,
> +					  DFSDM_CR2_SCDIE(1));
> +		priv->scd_filter_mask |= BIT(fl_id);
> +		break;
> +	case DFSDM_EVENT_CKA:
> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
> +					  DFSDM_CHCFGR1_CKABEN_MASK,
> +					  DFSDM_CHCFGR1_CKABEN(1));
> +		}
> +		if (!priv->ckab_filter_mask)
> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
> +					  DFSDM_CR2_CKABIE_MASK,
> +					  DFSDM_CR2_CKABIE(1));
> +		priv->ckab_filter_mask |= BIT(fl_id);
> +		break;
> +	default:
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(fl_id), event, event);
> +	}
> +	priv->filters[fl_id].event_mask |= event;
> +	spin_unlock_irqrestore(&priv->lock, flags);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_register_fl_event);
> +
> +/**
> + * stm32_dfsdm_unregister_fl_event - Unregister filter event.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: Filter id.
> + * @event: Event to unregister.
> + * @chan_mask: Mask of channels associated to filter.
> + *
> + * The function disables associated IRQ.
> + */
> +int stm32_dfsdm_unregister_fl_event(struct stm32_dfsdm *dfsdm,
> +				    unsigned int fl_id,
> +				    enum stm32_dfsdm_events event,
> +				    unsigned int chan_mask)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +	unsigned long flags, ulmask = chan_mask;
> +	int i;
> +
> +	dev_dbg(&priv->pdev->dev, "%s:for filter %d: event %#x ch_mask %#x\n",
> +		__func__, fl_id, event, chan_mask);
> +
> +	if (event > DFSDM_EVENT_CKA)
> +		return -EINVAL;
> +
> +	spin_lock_irqsave(&priv->lock, flags);
> +	/* Disable interrupts */
> +	switch (event) {
> +	case DFSDM_EVENT_SCD:
> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
> +					  DFSDM_CHCFGR1_SCDEN_MASK,
> +					  DFSDM_CHCFGR1_SCDEN(0));
> +		}
> +		priv->scd_filter_mask &= ~BIT(fl_id);
> +		if (!priv->scd_filter_mask)
> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
> +					  DFSDM_CR2_SCDIE_MASK,
> +					  DFSDM_CR2_SCDIE(0));
> +		break;
> +	case DFSDM_EVENT_CKA:
> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
> +					  DFSDM_CHCFGR1_CKABEN_MASK,
> +					  DFSDM_CHCFGR1_CKABEN(0));
> +		}
> +		priv->ckab_filter_mask &= ~BIT(fl_id);
> +		if (!priv->ckab_filter_mask)
> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
> +					  DFSDM_CR2_CKABIE_MASK,
> +					  DFSDM_CR2_CKABIE(0));
> +		break;
> +	default:
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(fl_id), event, 0);
> +	}
> +
> +	priv->filters[fl_id].event_mask &= ~event;
> +	spin_unlock_irqrestore(&priv->lock, flags);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_unregister_fl_event);
> +
> +/*
> + * Channels
> + */
> +static void stm32_dfsdm_init_channel(struct dfsdm_priv *priv,
> +				     struct stm32_dfsdm_channel *ch)
> +{
Comments in here for what the various bits are doing would be great.
The naming makes it just about possible to work out, but not nice
to read!
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
> +			  DFSDM_CHCFGR1_DATMPX_MASK,
> +			  DFSDM_CHCFGR1_DATMPX(ch->type.source));
> +	if (ch->type.source == DFSDM_CHANNEL_EXTERNAL_INPUTS) {
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
> +				  DFSDM_CHCFGR1_SITP_MASK,
> +				  DFSDM_CHCFGR1_SITP(ch->serial_if.type));
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
> +				  DFSDM_CHCFGR1_SPICKSEL_MASK,
> +				DFSDM_CHCFGR1_SPICKSEL(ch->serial_if.spi_clk));
> +	}
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
> +			  DFSDM_CHCFGR1_DATPACK_MASK,
> +			  DFSDM_CHCFGR1_DATPACK(ch->type.DataPacking));
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
> +			  DFSDM_CHCFGR1_CHINSEL_MASK,
> +			  DFSDM_CHCFGR1_CHINSEL(ch->serial_if.pins));
> +}
> +
> +/**
> + * stm32_dfsdm_start_channel - Configure and activate DFSDM channel.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @ch: Filter id.
> + * @cfg: Filter configuration.
> + */
> +int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id,
> +			      struct stm32_dfsdm_ch_cfg *cfg)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv,
> +					       dfsdm);
> +	struct regmap *reg = priv->regmap;
> +	int ret;
> +
> +	dev_dbg(&priv->pdev->dev, "%s: for channel %d\n", __func__, ch_id);
> +
> +	ret = stm32_dfsdm_start_dfsdm(priv);
> +	if (ret < 0)
> +		return ret;
> +
> +	DFSDM_UPDATE_BITS(reg, DFSDM_CHCFGR2(ch_id), DFSDM_CHCFGR2_DTRBS_MASK,
> +			  DFSDM_CHCFGR2_DTRBS(cfg->right_bit_shift));
> +	DFSDM_UPDATE_BITS(reg, DFSDM_CHCFGR2(ch_id), DFSDM_CHCFGR2_OFFSET_MASK,
> +			  DFSDM_CHCFGR2_OFFSET(cfg->offset));
> +
> +	DFSDM_UPDATE_BITS(reg, DFSDM_CHCFGR1(ch_id), DFSDM_CHCFGR1_CHEN_MASK,
> +			  DFSDM_CHCFGR1_CHEN(1));
> +
> +	/* Clear absence detection IRQ */
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_ICR(0),
> +			  DFSDM_ICR_CLRCKABF_CH_MASK(ch_id),
> +			  DFSDM_ICR_CLRCKABF_CH(1, ch_id));
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_start_channel);
> +
> +/**
> + * stm32_dfsdm_stop_channel - Deactivate channel.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @ch_id: DFSDM channel identifier.
> + */
> +void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +
> +	dev_dbg(&priv->pdev->dev, "%s:for channel %d\n", __func__, ch_id);
> +
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch_id),
> +			  DFSDM_CHCFGR1_CHEN_MASK,
> +			  DFSDM_CHCFGR1_CHEN(0));
> +
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch_id),
> +			  DFSDM_CHCFGR1_CKABEN_MASK, DFSDM_CHCFGR1_CKABEN(0));
> +
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch_id),
> +			  DFSDM_CHCFGR1_SCDEN_MASK, DFSDM_CHCFGR1_SCDEN(0));
> +
> +	stm32_dfsdm_stop_dfsdm(priv);
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_stop_channel);
> +
> +/**
> + * stm32_dfsdm_get_channel - Get channel instance.
> + *
> + * @dfsdm: handle used to retrieve dfsdm context.
> + * @ch: DFSDM channel hardware parameters.
> + *
> + * Reserve DFSDM channel resource.
> + */
> +int stm32_dfsdm_get_channel(struct stm32_dfsdm *dfsdm,
> +			    struct stm32_dfsdm_channel *ch)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +	unsigned int id = ch->id;
> +
> +	dev_dbg(&priv->pdev->dev, "%s:get channel %d\n", __func__, id);
> +
> +	if (id >= priv->dfsdm.max_channels) {
> +		dev_err(&priv->pdev->dev, "channel (%d) is not valid\n", id);
> +		return -EINVAL;
> +	}
> +
> +	if ((ch->type.source != DFSDM_CHANNEL_EXTERNAL_INPUTS) &
> +	    (ch->serial_if.spi_clk != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL) &
> +	    (!priv->clkout_freq_req)) {
> +		dev_err(&priv->pdev->dev, "clkout not present\n");
> +		return -EINVAL;
> +	}
> +
> +	spin_lock(&priv->lock);
> +	if (!(BIT(id) & priv->free_channel_mask)) {
> +		spin_unlock(&priv->lock);
> +		dev_err(&priv->pdev->dev, "channel (%d) already in use\n", id);
> +		return -EBUSY;
> +	}
> +
> +	priv->free_channel_mask &= ~BIT(id);
> +	priv->channels[id] = *ch;
> +	spin_unlock(&priv->lock);
> +
> +	dev_dbg(&priv->pdev->dev, "%s: new mask %#x\n", __func__,
> +		priv->free_channel_mask);
> +
> +	/**
> +	 * Check clock constrainst between clkout and either
> +	 * dfsdm/audio clock:
> +	 * - In SPI mode (clkout is used): Fclk >= 4 * Fclkout
> +	 *   (e.g. CKOUTDIV >= 3)
> +	 * - In mancherster mode: Fclk >= 6 * Fclkout
> +	 */
> +	switch (ch->serial_if.type) {
> +	case DFSDM_CHANNEL_SPI_RISING:
> +	case DFSDM_CHANNEL_SPI_FALLING:
> +		if (priv->clkout_div && priv->clkout_div < 3)
> +			dev_warn(&priv->pdev->dev,
> +				 "Clock div should be higher than 3\n");
Really only warnings?  If requirements then error out. If not, then
a description of what the side effects of these would be would be great
here and perhaps even in the warning message.
> +		break;
> +	case DFSDM_CHANNEL_MANCHESTER_RISING:
> +	case DFSDM_CHANNEL_MANCHESTER_FALLING:
> +		if (priv->clkout_div && priv->clkout_div < 5)
> +			dev_warn(&priv->pdev->dev,
> +				 "Clock div should be higher than 5\n");
> +		break;
> +	}
> +
> +	stm32_dfsdm_init_channel(priv, ch);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_get_channel);
> +
> +/**
> + * stm32_dfsdm_release_channel - Release channel instance.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @ch_id: DFSDM channel identifier.
> + *
> + * Free the DFSDM channel resource.
> + */
> +void stm32_dfsdm_release_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +
> +	spin_lock(&priv->lock);
> +	priv->free_channel_mask |= BIT(ch_id);
> +	spin_unlock(&priv->lock);
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_release_channel);
> +
> +/**
> + * stm32_dfsdm_get_clk_out_rate - get clkout frequency.
> + *
> + * @dfsdm: handle used to retrieve dfsdm context.
> + * @rate: clock out rate in Hz.
> + *
> + * Provide output frequency used for external ADC.
> + * return EINVAL if clockout is not used else return 0.
> + */
> +int stm32_dfsdm_get_clk_out_rate(struct stm32_dfsdm *dfsdm, unsigned long *rate)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +	unsigned long int clk_rate;
> +
> +	if (!priv->clkout_div)
> +		return -EINVAL;
> +
> +	clk_rate = clk_get_rate(priv->aclk ? priv->aclk : priv->clk);
> +	*rate = clk_rate / (priv->clkout_div + 1);
> +	dev_dbg(&priv->pdev->dev, "%s: clkout: %ld (Hz)\n", __func__, *rate);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_get_clk_out_rate);
> +
> +static int stm32_dfsdm_parse_of(struct platform_device *pdev,
> +				struct dfsdm_priv *priv)
> +{
> +	struct device_node *node = pdev->dev.of_node;
> +	struct resource *res;
> +	int ret, val;
> +
> +	if (!node)
> +		return -EINVAL;
> +
> +	/* Get resources */
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(&pdev->dev, "Failed to get memory resource\n");
> +		return -ENODEV;
> +	}
> +	priv->phys_base = res->start;
> +	priv->base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(priv->base))
> +		return PTR_ERR(priv->base);
> +
> +	ret = of_property_read_u32(node, "st,clkout-freq", &val);
> +	if (!ret) {
> +		if (!val) {
> +			dev_err(&priv->pdev->dev,
> +				"st,clkout-freq cannot be 0\n");
> +			return -EINVAL;
> +		}
> +		priv->clkout_freq_req = val;
> +	} else if (ret != -EINVAL) {
> +		dev_err(&priv->pdev->dev, "Failed to get st,clkout-freq\n");
> +		return ret;
> +	}
> +
> +	/* Source clock */
> +	priv->clk = devm_clk_get(&pdev->dev, "dfsdm_clk");
> +	if (IS_ERR(priv->clk)) {
> +		dev_err(&pdev->dev, "No stm32_dfsdm_clk clock found\n");
> +		return -EINVAL;
> +	}
> +
> +	priv->aclk = devm_clk_get(&pdev->dev, "audio_clk");
> +	if (IS_ERR(priv->aclk))
> +		priv->aclk = NULL;
> +
> +	return 0;
> +};
> +
> +static const struct of_device_id stm32_dfsdm_of_match[] = {
> +	{
> +		.compatible = "st,stm32h7-dfsdm",
> +		.data = &stm32h7_data
> +	},
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, stm32_dfsdm_of_match);
> +
> +static int stm32_dfsdm_remove(struct platform_device *pdev)
> +{
> +	of_platform_depopulate(&pdev->dev);
> +
> +	return 0;
> +}
> +
> +static int stm32_dfsdm_probe(struct platform_device *pdev)
> +{
> +	struct dfsdm_priv *priv;
> +	struct device_node *pnode = pdev->dev.of_node;
> +	const struct of_device_id *of_id;
> +	const struct stm32_dev_data *dev_data;
> +	enum dfsdm_clkout_src clk_src;
> +	int ret, i;
> +
> +	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->pdev = pdev;
> +
> +	/* Populate data structure depending on compatibility */
> +	of_id = of_match_node(stm32_dfsdm_of_match, pnode);
> +	if (!of_id->data) {
> +		dev_err(&pdev->dev, "Data associated to device is missing\n");
> +		return -EINVAL;
> +	}
> +
> +	dev_data = (const struct stm32_dev_data *)of_id->data;
> +
> +	ret = stm32_dfsdm_parse_of(pdev, priv);
> +	if (ret < 0)
> +		return ret;
> +
> +	priv->regmap = devm_regmap_init_mmio(&pdev->dev, priv->base,
> +					    dev_data->regmap_cfg);
> +	if (IS_ERR(priv->regmap)) {
> +		ret = PTR_ERR(priv->regmap);
> +		dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",
> +			__func__, ret);
> +		return ret;
> +	}
> +
> +	priv->dfsdm = dev_data->dfsdm;
> +
> +	priv->filters = devm_kcalloc(&pdev->dev, dev_data->dfsdm.max_filters,
> +				     sizeof(*priv->filters), GFP_KERNEL);
> +	if (IS_ERR(priv->filters)) {
> +		ret = PTR_ERR(priv->filters);
> +		goto probe_err;
> +	}
> +
> +	for (i = 0; i < dev_data->dfsdm.max_filters; i++) {
> +		struct filter_params *params = &priv->filters[i];
> +
> +		params->id = i;
> +		params->irq = platform_get_irq(pdev, i);
> +		if (params->irq < 0) {
> +			dev_err(&pdev->dev, "Failed to get IRQ resource\n");
> +			ret = params->irq;
> +			goto probe_err;
> +		}
> +
> +		ret = devm_request_irq(&pdev->dev, params->irq, stm32_dfsdm_irq,
> +				       0, dev_name(&pdev->dev), params);
> +		if (ret) {
> +			dev_err(&pdev->dev, "Failed to register interrupt\n");
> +			goto probe_err;
> +		}
> +
> +		params->priv = priv;
> +	}
> +
> +	priv->channels = devm_kcalloc(&pdev->dev, priv->dfsdm.max_channels,
> +				      sizeof(*priv->channels), GFP_KERNEL);
> +	if (IS_ERR(priv->channels)) {
> +		ret = PTR_ERR(priv->channels);
> +		goto probe_err;
> +	}
> +	priv->free_filter_mask = BIT(priv->dfsdm.max_filters) - 1;
> +	priv->free_channel_mask = BIT(priv->dfsdm.max_channels) - 1;
> +
> +	platform_set_drvdata(pdev, &priv->dfsdm);
> +	spin_lock_init(&priv->lock);
> +
> +	priv->clkout_div = stm32_dfsdm_get_clkout_divider(priv,
> +						    clk_get_rate(priv->clk));
> +
> +	ret = of_platform_populate(pnode, NULL, NULL, &pdev->dev);
> +	if (ret < 0)
> +		goto probe_err;
> +
> +	clk_src = priv->aclk ? AUDIO_CLK : DFSDM_CLK;
> +
I'd like to see a comment here saying what this is doing.
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
> +			  DFSDM_CHCFGR1_CKOUTSRC_MASK,
> +			  DFSDM_CHCFGR1_CKOUTSRC(clk_src));
> +	return 0;
> +
> +probe_err:
> +	return ret;
> +}
> +
> +static struct platform_driver stm32_dfsdm_driver = {
> +	.probe = stm32_dfsdm_probe,
> +	.remove = stm32_dfsdm_remove,
> +	.driver = {
> +		.name = "stm32-dfsdm",
> +		.of_match_table = stm32_dfsdm_of_match,
> +	},
> +};
> +
> +module_platform_driver(stm32_dfsdm_driver);
> +
> +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics STM32 dfsdm driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/mfd/stm32-dfsdm.h b/include/linux/mfd/stm32-dfsdm.h
> new file mode 100644
> index 0000000..f6eb788
> --- /dev/null
> +++ b/include/linux/mfd/stm32-dfsdm.h
> @@ -0,0 +1,324 @@
> +/*
> + * This file is part of STM32 DFSDM mfd driver API
> + *
> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
> + * Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com>.
> + *
> + * 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.
> + */
> +#ifndef MDF_STM32_DFSDM_H
> +#define MDF_STM32_DFSDM_H
> +
> +/*
> + * Channel definitions
> + */
> +#define DFSDM_CHANNEL_0    BIT(0)
> +#define DFSDM_CHANNEL_1    BIT(1)
> +#define DFSDM_CHANNEL_2    BIT(2)
> +#define DFSDM_CHANNEL_3    BIT(3)
> +#define DFSDM_CHANNEL_4    BIT(4)
> +#define DFSDM_CHANNEL_5    BIT(5)
> +#define DFSDM_CHANNEL_6    BIT(6)
> +#define DFSDM_CHANNEL_7    BIT(7)
> +
> +/* DFSDM channel input data packing */
> +enum stm32_dfsdm_data_packing {
> +	DFSDM_CHANNEL_STANDARD_MODE,    /* Standard data packing mode */
> +	DFSDM_CHANNEL_INTERLEAVED_MODE, /* Interleaved data packing mode */
> +	DFSDM_CHANNEL_DUAL_MODE         /* Dual data packing mode */
> +};
> +
> +/* DFSDM channel input multiplexer */
> +enum stm32_dfsdm_input_multiplexer {
> +	DFSDM_CHANNEL_EXTERNAL_INPUTS,    /* Data taken from external inputs */
> +	DFSDM_CHANNEL_INTERNAL_ADC,       /* Data taken from internal ADC */
> +	DFSDM_CHANNEL_INTERNAL_REGISTER,  /* Data taken from register */
> +};
> +
> +/* DFSDM channel serial interface type */
> +enum stm32_dfsdm_serial_in_type {
> +	DFSDM_CHANNEL_SPI_RISING,         /* SPI with rising edge */
> +	DFSDM_CHANNEL_SPI_FALLING,        /* SPI with falling edge */
We could use standard SPI naming for these first two. That would I think
describe these as clock phases. However, perhaps alongside the machester
coding it doesn't make sense to do so.

> +	DFSDM_CHANNEL_MANCHESTER_RISING,  /* Manchester with rising edge */
> +	DFSDM_CHANNEL_MANCHESTER_FALLING, /* Manchester with falling edge */
> +};
> +
> +/* DFSDM channel serial spi clock source */
> +enum stm32_dfsdm_spi_clk_src {
> +	/* External SPI clock */
> +	DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL,
> +	/* Internal SPI clock */
> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL,
> +	/* Internal SPI clock divided by 2, falling edge */
> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING,
> +	/* Internal SPI clock divided by 2, rising edge */
> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING
> +};
> +
> +/* DFSDM channel input pins */
> +enum stm32_dfsdm_serial_in_select {
> +	/* Serial input taken from pins of the same channel (y) */
> +	DFSDM_CHANNEL_SAME_CHANNEL_PINS,
> +	/* Serial input taken from pins of the following channel (y + 1)*/
> +	DFSDM_CHANNEL_NEXT_CHANNEL_PINS,
> +};
> +
> +/**
> + * struct stm32_dfsdm_input_type - DFSDM channel init structure definition.
> + * @DataPacking: Standard, interleaved or dual mode for internal register.
> + * @source: channel source: internal DAC, serial input or memory.
> + */
> +struct stm32_dfsdm_input_type {
> +	enum stm32_dfsdm_data_packing DataPacking;
> +	enum stm32_dfsdm_input_multiplexer source;
> +};
> +
> +/**
> + * struct stm32_dfsdm_serial_if - DFSDM serial interface parameters.
> + * @type:	Serial interface type.
> + * @spi_clk:	SPI clock source.
> + * @pins:	select serial interface associated to the channel
> + */
> +struct stm32_dfsdm_serial_if {
> +	enum stm32_dfsdm_serial_in_type type;
> +	enum stm32_dfsdm_spi_clk_src spi_clk;
> +	enum stm32_dfsdm_serial_in_select pins;
> +};
> +
> +/**
> + * struct stm32_dfsdm_channel - DFSDM channel hardware parameters.
> + * @id:		DFSDM channel identifier.
> + * @type:	DFSDM channel input parameters.
> + * @serial_if:	DFSDM channel serial interface parameters.
> + *		Mandatory for DFSDM_CHANNEL_EXTERNAL_INPUTS.
> + */
> +struct stm32_dfsdm_channel {
> +	unsigned int id;
> +	struct stm32_dfsdm_input_type type;
> +	struct stm32_dfsdm_serial_if serial_if;
> +};
> +
> +/**
> + * struct stm32_dfsdm_ch_cfg - DFSDM channel config.
> + * @offset:		DFSDM channel 24 bit calibration offset.
> + * @right_bit_shift:	DFSDM channel right bit shift of the data result.
> + */
> +struct stm32_dfsdm_ch_cfg {
> +	unsigned int offset;
> +	unsigned int right_bit_shift;
> +};
> +
> +/*
> + * Filter definitions
Single line comment syntax.  Make sure this is correct throughout as otherwise
we'll only get 'fix' patches turning up after this hits linux-next.
> + */
> +
> +#define DFSDM_MIN_INT_OVERSAMPLING 1
> +#define DFSDM_MAX_INT_OVERSAMPLING 256
> +#define DFSDM_MIN_FL_OVERSAMPLING 1
Oversampling of 1 is effectively not oversampling so do we need this?

> +#define DFSDM_MAX_FL_OVERSAMPLING 1024
> +
> +enum stm32_dfsdm_events {
Slightly clunky enum usage.  I'd just use the value and put the BIT around
whereever it is used.
> +	DFSDM_EVENT_INJ_EOC =	BIT(0), /* Injected end of conversion event */
> +	DFSDM_EVENT_REG_EOC =	BIT(1), /* Regular end of conversion event */
> +	DFSDM_EVENT_INJ_XRUN =	BIT(2), /* Injected conversion overrun event */
> +	DFSDM_EVENT_REG_XRUN =	BIT(3), /* Regular conversion overrun event */
> +	DFSDM_EVENT_AWD =	BIT(4), /* Analog watchdog event */
> +	DFSDM_EVENT_SCD =	BIT(5), /* Short circuit detector event */
> +	DFSDM_EVENT_CKA =	BIT(6), /* Clock abscence detection event */
> +};
> +
> +#define STM32_DFSDM_EVENT_MASK 0x3F
> +
> +/* DFSDM filter order  */
> +enum stm32_dfsdm_sinc_order {
> +	DFSDM_FASTSINC_ORDER, /* FastSinc filter type */
> +	DFSDM_SINC1_ORDER,    /* Sinc 1 filter type */
> +	DFSDM_SINC2_ORDER,    /* Sinc 2 filter type */
> +	DFSDM_SINC3_ORDER,    /* Sinc 3 filter type */
> +	DFSDM_SINC4_ORDER,    /* Sinc 4 filter type (N.A. for watchdog) */
> +	DFSDM_SINC5_ORDER,    /* Sinc 5 filter type (N.A. for watchdog) */
> +	DFSDM_NB_SINC_ORDER,
> +};
> +
> +/* DFSDM filter order */
Please check your comments - this one is clearly wrong.

> +enum stm32_dfsdm_state {
> +	DFSDM_DISABLE,
> +	DFSDM_ENABLE,
> +};
> +
> +/**
> + * struct stm32_dfsdm_sinc_filter - DFSDM Sinc filter structure definition
> + * @order: DFSM filter order.
> + * @oversampling: DFSDM filter oversampling:
> + *		  post processing filter: min = 1, max = 1024.
> + */
> +struct stm32_dfsdm_sinc_filter {
> +	enum stm32_dfsdm_sinc_order order;
> +	unsigned int oversampling;
> +};
> +
> +/* DFSDM filter conversion trigger */
> +enum stm32_dfsdm_trigger {
> +	DFSDM_FILTER_SW_TRIGGER,   /* Software trigger */
> +	DFSDM_FILTER_SYNC_TRIGGER, /* Synchronous with DFSDM0 */
> +	DFSDM_FILTER_EXT_TRIGGER,  /* External trigger (only for injected) */
> +};
> +
> +/* DFSDM filter external trigger polarity */
> +enum stm32_dfsdm_filter_ext_trigger_pol {
> +	DFSDM_FILTER_EXT_TRIG_NO_TRIG,      /* Trigger disable */
> +	DFSDM_FILTER_EXT_TRIG_RISING_EDGE,  /* Rising edge */
> +	DFSDM_FILTER_EXT_TRIG_FALLING_EDGE, /* Falling edge */
> +	DFSDM_FILTER_EXT_TRIG_BOTH_EDGES,   /* Rising and falling edges */
> +};
> +
> +/* DFSDM filter conversion type */
> +enum stm32_dfsdm_conv_type {
> +	DFSDM_FILTER_REG_CONV,      /* Regular conversion */
> +	DFSDM_FILTER_SW_INJ_CONV,   /* Injected conversion */
> +	DFSDM_FILTER_TRIG_INJ_CONV, /* Injected conversion */
> +};
> +
> +/* DFSDM filter regular synchronous mode */
> +enum stm32_dfsdm_conv_rsync {
> +	DFSDM_FILTER_RSYNC_OFF, /* regular conversion asynchronous */
> +	DFSDM_FILTER_RSYNC_ON,  /* regular conversion synchronous with filter0*/
stray 0?
> +};
> +
> +/**
> + * struct stm32_dfsdm_regular - DFSDM filter conversion parameters structure
> + * @ch_src:	Channel source from 0 to 7.
> + * @fast_mode:	Enable/disable fast mode for regular conversion.
> + * @dma_mode:	Enable/disable dma mode.
> + * @cont_mode	Enable/disable continuous conversion.
> + * @sync_mode	Enable/disable synchro mode.
> + */
> +struct stm32_dfsdm_regular {
> +	unsigned int ch_src;
> +	bool fast_mode;
> +	bool dma_mode;
> +	bool cont_mode;
> +	bool sync_mode;
> +};
> +
> +/**
> + * struct stm32_dfsdm_injected - DFSDM filter  conversion parameters structure
> + * @trigger:	Trigger used to start injected conversion.
> + * @trig_src:	External trigger, 0 to 30 (refer to datasheet for details).
> + * @trig_pol:	External trigger edge: software, rising, falling or both.
> + * @scan_mode:	Enable/disable scan mode for injected conversion.
> + * @ch_group:	mask containing channels to scan ( set bit y to scan
> + *		channel y).
> + * @dma_mode:	DFSDM channel input parameters.
> + */
> +struct stm32_dfsdm_injected {
> +	enum stm32_dfsdm_trigger trigger;
> +	unsigned int trig_src;
> +	enum stm32_dfsdm_filter_ext_trigger_pol trig_pol;
> +	bool scan_mode;
> +	unsigned int ch_group;
> +	bool dma_mode;
> +};
> +
> +struct stm32_dfsdm;
> +
> +/**
> + * struct stm32_dfsdm_fl_event - DFSDM filters event
> + * @cb:	User event callback with parameters. be carful this function
> + *		is called under threaded IRQ context:
> + *			struct stm32_dfsdm *dfsdm: dfsdm handle,
> + *			unsigned int fl_id: filter id,
> + *			num stm32_dfsdm_events flag: event,
> + *			param: parameter associated to the event,
> + *			void *context: user context provided on registration.
> + * @context: User param to retrieve context.
> + */
> +struct stm32_dfsdm_fl_event {
> +	void (*cb)(struct stm32_dfsdm *, int, enum stm32_dfsdm_events,
> +		   unsigned int, void *);
> +	void *context;
> +};
> +
> +/**
> + * struct stm32_dfsdm_filter - DFSDM filter  conversion parameters structure
> + * @reg_params:		DFSDM regular conversion parameters.
> + *			this param is optional and not taken into account if
> + *			@inj_params is defined.
> + * @inj_params:		DFSDM injected conversion parameters (optional).
> + * @filter_params:	DFSDM filter parameters.
> + * @event:		Events callback.
> + * @int_oversampling:	Integrator oversampling ratio for average purpose
> + *			(range from 1 to 256).
> + * @ext_det_ch_mask:	Extreme detector mask for channel selection
> + *			mask generated using DFSDM_CHANNEL_0 to
> + *			DFSDM_CHANNEL_7. If 0 feature is disable.
> + */
> +struct stm32_dfsdm_filter {
> +	struct stm32_dfsdm_regular *reg_params;
> +	struct stm32_dfsdm_injected *inj_params;
> +	struct stm32_dfsdm_sinc_filter sinc_params;
> +	struct stm32_dfsdm_fl_event event;
> +	unsigned int int_oversampling;
> +};
> +
> +/**
> + * struct stm32_dfsdm - DFSDM context structure.
> + *
> + * @trig_info: Trigger name and id available last member name is null.
> + * @max_channels: max number of channels available.
> + * @max_filters: max number of filters available.
> + *
> + * Notice That structure is filled by mdf driver and must not be updated by
mfd
> + * user.
> + */
> +struct stm32_dfsdm {
> +	unsigned int max_channels;
> +	unsigned int max_filters;
> +};
> +
> +int stm32_dfsdm_get_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
> +void stm32_dfsdm_release_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
> +
> +dma_addr_t stm32_dfsdm_get_filter_dma_phy_addr(struct stm32_dfsdm *dfsdm,
> +					       unsigned int fl_id,
> +					       enum stm32_dfsdm_conv_type conv);
> +
> +int stm32_dfsdm_configure_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
> +				 struct stm32_dfsdm_filter *filter);
> +void stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
> +			      enum stm32_dfsdm_conv_type conv);
> +void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
> +
> +void stm32_dfsdm_read_fl_conv(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
> +			      u32 *val, int *ch_id,
> +			      enum stm32_dfsdm_conv_type type);
> +
> +int stm32_dfsdm_unregister_fl_event(struct stm32_dfsdm *dfsdm,
> +				    unsigned int fl_id,
> +				    enum stm32_dfsdm_events event,
> +				    unsigned int ch_mask);
> +int stm32_dfsdm_register_fl_event(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
> +				  enum stm32_dfsdm_events event,
> +				  unsigned int ch_mask);
> +
> +int stm32_dfsdm_get_channel(struct stm32_dfsdm *dfsdm,
> +			    struct stm32_dfsdm_channel *ch);
> +void stm32_dfsdm_release_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
> +
> +int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id,
> +			      struct stm32_dfsdm_ch_cfg *cfg);
> +void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
> +
> +int stm32_dfsdm_get_clk_out_rate(struct stm32_dfsdm *dfsdm,
> +				 unsigned long *rate);
> +
> +#endif
> 


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

* [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-29 11:53         ` Jonathan Cameron
  0 siblings, 0 replies; 130+ messages in thread
From: Jonathan Cameron @ 2017-01-29 11:53 UTC (permalink / raw)
  To: linux-arm-kernel

On 23/01/17 16:32, Arnaud Pouliquen wrote:
> DFSDM hardware IP can be used at the same time for ADC sigma delta
> conversion and audio PDM microphone.
> MFD driver is in charge of configuring IP registers and managing IP clocks.
> For this it exports an API to handles filters and channels resources.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
This is somewhat of a beast.  I would be tempted to build it up in more
bite sized chunks.

Obvious things to drop from a first version (basically to make it easier
to review) would be injected supported.  There may well be others but you'll
have a better feel for that than me.

I really don't like the wrappers round the regmap functions though.
They mean you are papering over errors if they occur and make the code
slightly harder to read by implying that something else is going on.

Yes the code will be longer without them, but you will also be forced to think
properly about error paths.

Anyhow, was mostly reading this to get a feel for what was going on in the
whole series so not really a terribly thorough review I'm afraid. Sorry about
that!

Jonathan
> ---
>  drivers/mfd/Kconfig             |   11 +
>  drivers/mfd/Makefile            |    2 +
>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
>  5 files changed, 1601 insertions(+)
>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
>  create mode 100644 drivers/mfd/stm32-dfsdm.c
>  create mode 100644 include/linux/mfd/stm32-dfsdm.h
> 
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index c6df644..4bb660b 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -1607,6 +1607,17 @@ config MFD_STW481X
>  	  in various ST Microelectronics and ST-Ericsson embedded
>  	  Nomadik series.
>  
> +config MFD_STM32_DFSDM
> +	tristate "ST Microelectronics STM32 DFSDM"
> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
> +	select MFD_CORE
> +	select REGMAP
> +	select REGMAP_MMIO
> +	help
> +	  Select this option to enable the STM32 Digital Filter
> +	  for Sigma Delta Modulators (DFSDM) driver used
> +	  in various STM32 series.
> +
>  menu "Multimedia Capabilities Port drivers"
>  	depends on ARCH_SA1100
>  
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index 9834e66..1f095e5 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -211,3 +211,5 @@ obj-$(CONFIG_INTEL_SOC_PMIC)	+= intel-soc-pmic.o
>  obj-$(CONFIG_MFD_MT6397)	+= mt6397-core.o
>  
>  obj-$(CONFIG_MFD_ALTERA_A10SR)	+= altera-a10sr.o
> +
> +obj-$(CONFIG_MFD_STM32_DFSDM)	+= stm32-dfsdm.o
> \ No newline at end of file
> diff --git a/drivers/mfd/stm32-dfsdm-reg.h b/drivers/mfd/stm32-dfsdm-reg.h
> new file mode 100644
> index 0000000..05ff702
> --- /dev/null
> +++ b/drivers/mfd/stm32-dfsdm-reg.h
> @@ -0,0 +1,220 @@
> +/*
> + * This file is part of STM32 DFSDM mfd driver
> + *
> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
> + * Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com>.
> + *
> + * 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.
> + */
> +
> +#ifndef MDF_STM32_DFSDM_REG_H
> +#define MDF_STM32_DFSDM_REG_H
> +
> +#include <linux/bitfield.h>
> +/*
> + * Channels definitions
> + */
> +#define DFSDM_CHCFGR1(y)  ((y) * 0x20 + 0x00)
> +#define DFSDM_CHCFGR2(y)  ((y) * 0x20 + 0x04)
> +#define DFSDM_AWSCDR(y)   ((y) * 0x20 + 0x08)
> +#define DFSDM_CHWDATR(y)  ((y) * 0x20 + 0x0C)
> +#define DFSDM_CHDATINR(y) ((y) * 0x20 + 0x10)
> +
> +/* CHCFGR1: Channel configuration register 1 */
> +#define DFSDM_CHCFGR1_SITP_MASK     GENMASK(1, 0)
> +#define DFSDM_CHCFGR1_SITP(v)       FIELD_PREP(DFSDM_CHCFGR1_SITP_MASK, v)
> +#define DFSDM_CHCFGR1_SPICKSEL_MASK GENMASK(3, 2)
> +#define DFSDM_CHCFGR1_SPICKSEL(v)   FIELD_PREP(DFSDM_CHCFGR1_SPICKSEL_MASK, v)
> +#define DFSDM_CHCFGR1_SCDEN_MASK    BIT(5)
> +#define DFSDM_CHCFGR1_SCDEN(v)      FIELD_PREP(DFSDM_CHCFGR1_SCDEN_MASK, v)
> +#define DFSDM_CHCFGR1_CKABEN_MASK   BIT(6)
> +#define DFSDM_CHCFGR1_CKABEN(v)     FIELD_PREP(DFSDM_CHCFGR1_CKABEN_MASK, v)
> +#define DFSDM_CHCFGR1_CHEN_MASK     BIT(7)
> +#define DFSDM_CHCFGR1_CHEN(v)       FIELD_PREP(DFSDM_CHCFGR1_CHEN_MASK, v)
> +#define DFSDM_CHCFGR1_CHINSEL_MASK  BIT(8)
> +#define DFSDM_CHCFGR1_CHINSEL(v)    FIELD_PREP(DFSDM_CHCFGR1_CHINSEL_MASK, v)
> +#define DFSDM_CHCFGR1_DATMPX_MASK   GENMASK(13, 12)
> +#define DFSDM_CHCFGR1_DATMPX(v)     FIELD_PREP(DFSDM_CHCFGR1_DATMPX_MASK, v)
> +#define DFSDM_CHCFGR1_DATPACK_MASK  GENMASK(15, 14)
> +#define DFSDM_CHCFGR1_DATPACK(v)    FIELD_PREP(DFSDM_CHCFGR1_DATPACK_MASK, v)
> +#define DFSDM_CHCFGR1_CKOUTDIV_MASK GENMASK(23, 16)
> +#define DFSDM_CHCFGR1_CKOUTDIV(v)   FIELD_PREP(DFSDM_CHCFGR1_CKOUTDIV_MASK, v)
> +#define DFSDM_CHCFGR1_CKOUTSRC_MASK BIT(30)
> +#define DFSDM_CHCFGR1_CKOUTSRC(v)   FIELD_PREP(DFSDM_CHCFGR1_CKOUTSRC_MASK, v)
> +#define DFSDM_CHCFGR1_DFSDMEN_MASK  BIT(31)
> +#define DFSDM_CHCFGR1_DFSDMEN(v)    FIELD_PREP(DFSDM_CHCFGR1_DFSDMEN_MASK, v)
> +
> +/* CHCFGR2: Channel configuration register 2 */
> +#define DFSDM_CHCFGR2_DTRBS_MASK    GENMASK(7, 3)
> +#define DFSDM_CHCFGR2_DTRBS(v)      FIELD_PREP(DFSDM_CHCFGR2_DTRBS_MASK, v)
> +#define DFSDM_CHCFGR2_OFFSET_MASK   GENMASK(31, 8)
> +#define DFSDM_CHCFGR2_OFFSET(v)     FIELD_PREP(DFSDM_CHCFGR2_OFFSET_MASK, v)
> +
> +/* AWSCDR: Channel analog watchdog and short circuit detector */
> +#define DFSDM_AWSCDR_SCDT_MASK    GENMASK(7, 0)
> +#define DFSDM_AWSCDR_SCDT(v)      FIELD_PREP(DFSDM_AWSCDR_SCDT_MASK, v)
> +#define DFSDM_AWSCDR_BKSCD_MASK   GENMASK(15, 12)
> +#define DFSDM_AWSCDR_BKSCD(v)	  FIELD_PREP(DFSDM_AWSCDR_BKSCD_MASK, v)
> +#define DFSDM_AWSCDR_AWFOSR_MASK  GENMASK(20, 16)
> +#define DFSDM_AWSCDR_AWFOSR(v)    FIELD_PREP(DFSDM_AWSCDR_AWFOSR_MASK, v)
> +#define DFSDM_AWSCDR_AWFORD_MASK  GENMASK(23, 22)
> +#define DFSDM_AWSCDR_AWFORD(v)    FIELD_PREP(DFSDM_AWSCDR_AWFORD_MASK, v)
> +
> +/*
> + * Filters definitions
> + */
> +#define DFSDM_FILTER_BASE_ADR		0x100
> +#define DFSDM_FILTER_REG_MASK		0x7F
> +#define DFSDM_FILTER_X_BASE_ADR(x)	((x) * 0x80 + DFSDM_FILTER_BASE_ADR)
> +
> +#define DFSDM_CR1(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x00)
> +#define DFSDM_CR2(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x04)
> +#define DFSDM_ISR(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x08)
> +#define DFSDM_ICR(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x0C)
> +#define DFSDM_JCHGR(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x10)
> +#define DFSDM_FCR(x)     (DFSDM_FILTER_X_BASE_ADR(x)  + 0x14)
> +#define DFSDM_JDATAR(x)  (DFSDM_FILTER_X_BASE_ADR(x)  + 0x18)
> +#define DFSDM_RDATAR(x)  (DFSDM_FILTER_X_BASE_ADR(x)  + 0x1C)
> +#define DFSDM_AWHTR(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x20)
> +#define DFSDM_AWLTR(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x24)
> +#define DFSDM_AWSR(x)    (DFSDM_FILTER_X_BASE_ADR(x)  + 0x28)
> +#define DFSDM_AWCFR(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x2C)
> +#define DFSDM_EXMAX(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x30)
> +#define DFSDM_EXMIN(x)   (DFSDM_FILTER_X_BASE_ADR(x)  + 0x34)
> +#define DFSDM_CNVTIMR(x) (DFSDM_FILTER_X_BASE_ADR(x)  + 0x38)
> +
> +/* CR1 Control register 1 */
> +#define DFSDM_CR1_DFEN_MASK	BIT(0)
> +#define DFSDM_CR1_DFEN(v)	FIELD_PREP(DFSDM_CR1_DFEN_MASK, v)
> +#define DFSDM_CR1_JSWSTART_MASK	BIT(1)
> +#define DFSDM_CR1_JSWSTART(v)	FIELD_PREP(DFSDM_CR1_JSWSTART_MASK, v)
> +#define DFSDM_CR1_JSYNC_MASK	BIT(3)
> +#define DFSDM_CR1_JSYNC(v)	FIELD_PREP(DFSDM_CR1_JSYNC_MASK, v)
> +#define DFSDM_CR1_JSCAN_MASK	BIT(4)
> +#define DFSDM_CR1_JSCAN(v)	FIELD_PREP(DFSDM_CR1_JSCAN_MASK, v)
> +#define DFSDM_CR1_JDMAEN_MASK	BIT(5)
> +#define DFSDM_CR1_JDMAEN(v)	FIELD_PREP(DFSDM_CR1_JDMAEN_MASK, v)
> +#define DFSDM_CR1_JEXTSEL_MASK	GENMASK(12, 8)
> +#define DFSDM_CR1_JEXTSEL(v)	FIELD_PREP(DFSDM_CR1_JEXTSEL_MASK, v)
> +#define DFSDM_CR1_JEXTEN_MASK	GENMASK(14, 13)
> +#define DFSDM_CR1_JEXTEN(v)	FIELD_PREP(DFSDM_CR1_JEXTEN_MASK, v)
> +#define DFSDM_CR1_RSWSTART_MASK	BIT(17)
> +#define DFSDM_CR1_RSWSTART(v)	FIELD_PREP(DFSDM_CR1_RSWSTART_MASK, v)
> +#define DFSDM_CR1_RCONT_MASK	BIT(18)
> +#define DFSDM_CR1_RCONT(v)	FIELD_PREP(DFSDM_CR1_RCONT_MASK, v)
> +#define DFSDM_CR1_RSYNC_MASK	BIT(19)
> +#define DFSDM_CR1_RSYNC(v)	FIELD_PREP(DFSDM_CR1_RSYNC_MASK, v)
> +#define DFSDM_CR1_RDMAEN_MASK	BIT(21)
> +#define DFSDM_CR1_RDMAEN(v)	FIELD_PREP(DFSDM_CR1_RDMAEN_MASK, v)
> +#define DFSDM_CR1_RCH_MASK	GENMASK(26, 24)
> +#define DFSDM_CR1_RCH(v)	FIELD_PREP(DFSDM_CR1_RCH_MASK, v)
> +#define DFSDM_CR1_FAST_MASK	BIT(29)
> +#define DFSDM_CR1_FAST(v)	FIELD_PREP(DFSDM_CR1_FAST_MASK, v)
> +#define DFSDM_CR1_AWFSEL_MASK	BIT(30)
> +#define DFSDM_CR1_AWFSEL(v)	FIELD_PREP(DFSDM_CR1_AWFSEL_MASK, v)
> +
> +/* CR2: Control register 2 */
> +#define DFSDM_CR2_IE_MASK	GENMASK(6, 0)
> +#define DFSDM_CR2_IE(v)		FIELD_PREP(DFSDM_CR2_IE_MASK, v)
> +#define DFSDM_CR2_JEOCIE_MASK	BIT(0)
> +#define DFSDM_CR2_JEOCIE(v)	FIELD_PREP(DFSDM_CR2_JEOCIE_MASK, v)
> +#define DFSDM_CR2_REOCIE_MASK	BIT(1)
> +#define DFSDM_CR2_REOCIE(v)	FIELD_PREP(DFSDM_CR2_REOCIE_MASK, v)
> +#define DFSDM_CR2_JOVRIE_MASK	BIT(2)
> +#define DFSDM_CR2_JOVRIE(v)	FIELD_PREP(DFSDM_CR2_JOVRIE_MASK, v)
> +#define DFSDM_CR2_ROVRIE_MASK	BIT(3)
> +#define DFSDM_CR2_ROVRIE(v)	FIELD_PREP(DFSDM_CR2_ROVRIE_MASK, v)
> +#define DFSDM_CR2_AWDIE_MASK	BIT(4)
> +#define DFSDM_CR2_AWDIE(v)	FIELD_PREP(DFSDM_CR2_AWDIE_MASK, v)
> +#define DFSDM_CR2_SCDIE_MASK	BIT(5)
> +#define DFSDM_CR2_SCDIE(v)	FIELD_PREP(DFSDM_CR2_SCDIE_MASK, v)
> +#define DFSDM_CR2_CKABIE_MASK	BIT(6)
> +#define DFSDM_CR2_CKABIE(v)	FIELD_PREP(DFSDM_CR2_CKABIE_MASK, v)
> +#define DFSDM_CR2_EXCH_MASK	GENMASK(15, 8)
> +#define DFSDM_CR2_EXCH(v)	FIELD_PREP(DFSDM_CR2_EXCH_MASK, v)
> +#define DFSDM_CR2_AWDCH_MASK	GENMASK(23, 16)
> +#define DFSDM_CR2_AWDCH(v)	FIELD_PREP(DFSDM_CR2_AWDCH_MASK, v)
> +
> +/* ISR: Interrupt status register */
> +#define DFSDM_ISR_JEOCF_MASK	BIT(0)
> +#define DFSDM_ISR_JEOCF(v)	FIELD_PREP(DFSDM_ISR_JEOCF_MASK, v)
> +#define DFSDM_ISR_REOCF_MASK	BIT(1)
> +#define DFSDM_ISR_REOCF(v)	FIELD_PREP(DFSDM_ISR_REOCF_MASK, v)
> +#define DFSDM_ISR_JOVRF_MASK	BIT(2)
> +#define DFSDM_ISR_JOVRF(v)	FIELD_PREP(DFSDM_ISR_JOVRF_MASK, v)
> +#define DFSDM_ISR_ROVRF_MASK	BIT(3)
> +#define DFSDM_ISR_ROVRF(v)	FIELD_PREP(DFSDM_ISR_ROVRF_MASK, v)
> +#define DFSDM_ISR_AWDF_MASK	BIT(4)
> +#define DFSDM_ISR_AWDF(v)	FIELD_PREP(DFSDM_ISR_AWDF_MASK, v)
> +#define DFSDM_ISR_JCIP_MASK	BIT(13)
> +#define DFSDM_ISR_JCIP(v)	FIELD_PREP(DFSDM_ISR_JCIP_MASK, v)
> +#define DFSDM_ISR_RCIP_MASK	BIT(14)
> +#define DFSDM_ISR_RCIP(v)	FIELD_PREP(DFSDM_ISR_RCIP, v)
> +#define DFSDM_ISR_CKABF_MASK	GENMASK(23, 16)
> +#define DFSDM_ISR_CKABF(v)	FIELD_PREP(DFSDM_ISR_CKABF_MASK, v)
> +#define DFSDM_ISR_SCDF_MASK	GENMASK(31, 24)
> +#define DFSDM_ISR_SCDF(v)	FIELD_PREP(DFSDM_ISR_SCDF_MASK, v)
> +
> +/* ICR: Interrupt flag clear register */
> +#define DFSDM_ICR_CLRJOVRF_MASK	      BIT(2)
> +#define DFSDM_ICR_CLRJOVRF(v)	      FIELD_PREP(DFSDM_ICR_CLRJOVRF_MASK, v)
> +#define DFSDM_ICR_CLRROVRF_MASK	      BIT(3)
> +#define DFSDM_ICR_CLRROVRF(v)	      FIELD_PREP(DFSDM_ICR_CLRROVRF_MASK, v)
> +#define DFSDM_ICR_CLRCKABF_MASK	      GENMASK(23, 16)
> +#define DFSDM_ICR_CLRCKABF(v)	      FIELD_PREP(DFSDM_ICR_CLRCKABF_MASK, v)
> +#define DFSDM_ICR_CLRCKABF_CH_MASK(y) BIT(16 + (y))
> +#define DFSDM_ICR_CLRCKABF_CH(v, y)   \
> +			   (((v) << (16 + (y))) & DFSDM_ICR_CLRCKABF_CH_MASK(y))
> +#define DFSDM_ICR_CLRSCDF_MASK	      GENMASK(31, 24)
> +#define DFSDM_ICR_CLRSCDF(v)	      FIELD_PREP(DFSDM_ICR_CLRSCDF_MASK, v)
> +#define DFSDM_ICR_CLRSCDF_CH_MASK(y)  BIT(24 + (y))
> +#define DFSDM_ICR_CLRSCDF_CH(v, y)    \
> +			       (((v) << (24 + (y))) & DFSDM_ICR_CLRSCDF_MASK(y))
> +
> +/* FCR: Filter control register */
> +#define DFSDM_FCR_IOSR_MASK	GENMASK(7, 0)
> +#define DFSDM_FCR_IOSR(v)	FIELD_PREP(DFSDM_FCR_IOSR_MASK, v)
> +#define DFSDM_FCR_FOSR_MASK	GENMASK(25, 16)
> +#define DFSDM_FCR_FOSR(v)	FIELD_PREP(DFSDM_FCR_FOSR_MASK, v)
> +#define DFSDM_FCR_FORD_MASK	GENMASK(31, 29)
> +#define DFSDM_FCR_FORD(v)	FIELD_PREP(DFSDM_FCR_FORD_MASK, v)
> +
> +/* RDATAR: Filter data register for regular channel */
> +#define DFSDM_DATAR_CH_MASK	GENMASK(2, 0)
> +#define DFSDM_DATAR_DATA_OFFSET 8
> +#define DFSDM_DATAR_DATA_MASK	GENMASK(31, DFSDM_DATAR_DATA_OFFSET)
> +
> +/* AWLTR: Filter analog watchdog low threshold register */
> +#define DFSDM_AWLTR_BKAWL_MASK	GENMASK(3, 0)
> +#define DFSDM_AWLTR_BKAWL(v)	FIELD_PREP(DFSDM_AWLTR_BKAWL_MASK, v)
> +#define DFSDM_AWLTR_AWLT_MASK	GENMASK(31, 8)
> +#define DFSDM_AWLTR_AWLT(v)	FIELD_PREP(DFSDM_AWLTR_AWLT_MASK, v)
> +
> +/* AWHTR: Filter analog watchdog low threshold register */
> +#define DFSDM_AWHTR_BKAWH_MASK	GENMASK(3, 0)
> +#define DFSDM_AWHTR_BKAWH(v)	FIELD_PREP(DFSDM_AWHTR_BKAWH_MASK, v)
> +#define DFSDM_AWHTR_AWHT_MASK	GENMASK(31, 8)
> +#define DFSDM_AWHTR_AWHT(v)	FIELD_PREP(DFSDM_AWHTR_AWHT_MASK, v)
> +
> +/* AWSR: Filter watchdog status register */
> +#define DFSDM_AWSR_AWLTF_MASK	GENMASK(7, 0)
> +#define DFSDM_AWSR_AWLTF(v)	FIELD_PREP(DFSDM_AWSR_AWLTF_MASK, v)
> +#define DFSDM_AWSR_AWHTF_MASK	GENMASK(15, 8)
> +#define DFSDM_AWSR_AWHTF(v)	FIELD_PREP(DFSDM_AWSR_AWHTF_MASK, v)
> +
> +/* AWCFR: Filter watchdog status register */
> +#define DFSDM_AWCFR_AWLTF_MASK	GENMASK(7, 0)
> +#define DFSDM_AWCFR_AWLTF(v)	FIELD_PREP(DFSDM_AWCFR_AWLTF_MASK, v)
> +#define DFSDM_AWCFR_AWHTF_MASK	GENMASK(15, 8)
> +#define DFSDM_AWCFR_AWHTF(v)	FIELD_PREP(DFSDM_AWCFR_AWHTF_MASK, v)
> +
> +#endif
> diff --git a/drivers/mfd/stm32-dfsdm.c b/drivers/mfd/stm32-dfsdm.c
> new file mode 100644
> index 0000000..81ca29c
> --- /dev/null
> +++ b/drivers/mfd/stm32-dfsdm.c
> @@ -0,0 +1,1044 @@
> +/*
> + * This file is part of STM32 DFSDM mfd driver
> + *
> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
> + * Author(s): Arnaud Pouliquen <arnaud.pouliquen@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/interrupt.h>
> +#include <linux/mfd/core.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +
> +#include <linux/mfd/stm32-dfsdm.h>
> +
> +#include "stm32-dfsdm-reg.h"
> +
> +#define DFSDM_UPDATE_BITS(regm, reg, mask, val) \
> +		WARN_ON(regmap_update_bits(regm, reg, mask, val))
Don't do these wrappers please. Handle error correctly in all cases.
Reviewing as I tend to do backwards through the driver, I thought these
were doing something interesting.

Effectively you have no error handling as a result of these which needs
fixing.

> +
> +#define DFSDM_REG_READ(regm, reg, val) \
> +		WARN_ON(regmap_read(regm, reg, val))
> +
> +#define DFSDM_REG_WRITE(regm, reg, val) \
> +		WARN_ON(regmap_write(regm, reg, val))
> +
> +#define STM32H7_DFSDM_NUM_FILTERS	4
> +#define STM32H7_DFSDM_NUM_INPUTS	8
> +
> +enum dfsdm_clkout_src {
> +	DFSDM_CLK,
> +	AUDIO_CLK
> +};
> +
> +struct stm32_dev_data {
> +	const struct stm32_dfsdm dfsdm;
> +	const struct regmap_config *regmap_cfg;
> +};
> +
> +struct dfsdm_priv;
> +
> +struct filter_params {
> +	unsigned int id;
> +	int irq;
> +	struct stm32_dfsdm_fl_event event;
> +	u32 event_mask;
> +	struct dfsdm_priv *priv; /* Cross ref for context */
> +	unsigned int ext_ch_mask;
> +	unsigned int scan_ch;
> +};
> +
> +struct ch_params {
> +	struct stm32_dfsdm_channel ch;
> +};
> +
I'd like to see a lot more comments in here.  Perhaps full kernel-doc
as some elements are not that obvious at least to a fairly casual read.

> +struct dfsdm_priv {
> +	struct platform_device *pdev;
> +	struct stm32_dfsdm dfsdm;
> +
> +	spinlock_t lock; /* Used for resource sharing & interrupt lock */
> +
> +	/* Filters */
> +	struct filter_params *filters;
> +	unsigned int free_filter_mask;
> +	unsigned int scd_filter_mask;
> +	unsigned int ckab_filter_mask;
> +
> +	/* Channels */
> +	struct stm32_dfsdm_channel *channels;
> +	unsigned int free_channel_mask;
> +	atomic_t n_active_ch;
> +
> +	/* Clock */
> +	struct clk *clk;
> +	struct clk *aclk;
> +	unsigned int clkout_div;
> +	unsigned int clkout_freq_req;
> +
> +	/* Registers*/
> +	void __iomem *base;
> +	struct regmap *regmap;
> +	phys_addr_t phys_base;
> +};
> +
> +/*
> + * Common
> + */
> +static bool stm32_dfsdm_volatile_reg(struct device *dev, unsigned int reg)
> +{
> +	if (reg < DFSDM_FILTER_BASE_ADR)
> +		return false;
> +
> +	/*
> +	 * Mask is done on register to avoid to list registers of all them
> +	 * filter instances.
> +	 */
> +	switch (reg & DFSDM_FILTER_REG_MASK) {
> +	case DFSDM_CR1(0) & DFSDM_FILTER_REG_MASK:
> +	case DFSDM_ISR(0) & DFSDM_FILTER_REG_MASK:
> +	case DFSDM_JDATAR(0) & DFSDM_FILTER_REG_MASK:
> +	case DFSDM_RDATAR(0) & DFSDM_FILTER_REG_MASK:
> +		return true;
> +	}
> +
> +	return false;
> +}
> +
> +static const struct regmap_config stm32h7_dfsdm_regmap_cfg = {
> +	.reg_bits = 32,
> +	.val_bits = 32,
> +	.reg_stride = sizeof(u32),
> +	.max_register = DFSDM_CNVTIMR(STM32H7_DFSDM_NUM_FILTERS - 1),
> +	.volatile_reg = stm32_dfsdm_volatile_reg,
> +	.fast_io = true,
> +};
> +
> +static const struct stm32_dev_data stm32h7_data = {
> +	.dfsdm.max_channels = STM32H7_DFSDM_NUM_INPUTS,
> +	.dfsdm.max_filters = STM32H7_DFSDM_NUM_FILTERS,
> +	.regmap_cfg = &stm32h7_dfsdm_regmap_cfg,
> +};
> +
> +static int stm32_dfsdm_start_dfsdm(struct dfsdm_priv *priv)
> +{
> +	int ret;
> +	struct device *dev = &priv->pdev->dev;
> +
> +	if (atomic_inc_return(&priv->n_active_ch) == 1) {
> +		ret = clk_prepare_enable(priv->clk);
> +		if (ret < 0) {
> +			dev_err(dev, "Failed to start clock\n");
> +			return ret;
> +		}
> +		if (priv->aclk) {
> +			ret = clk_prepare_enable(priv->aclk);
> +			if (ret < 0) {
> +				dev_err(dev, "Failed to start audio clock\n");
> +				return ret;
> +			}
> +		}
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
> +				  DFSDM_CHCFGR1_CKOUTDIV_MASK,
> +				  DFSDM_CHCFGR1_CKOUTDIV(priv->clkout_div));
> +
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
> +				  DFSDM_CHCFGR1_DFSDMEN_MASK,
> +				  DFSDM_CHCFGR1_DFSDMEN(1));
> +	}
> +
> +	dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,
> +		atomic_read(&priv->n_active_ch));
> +
> +	return 0;
> +}
> +
> +static void stm32_dfsdm_stop_dfsdm(struct dfsdm_priv *priv)
> +{
> +	if (atomic_dec_and_test(&priv->n_active_ch)) {
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
> +				  DFSDM_CHCFGR1_DFSDMEN_MASK,
> +				  DFSDM_CHCFGR1_DFSDMEN(0));
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
> +				  DFSDM_CHCFGR1_CKOUTDIV_MASK,
> +				  DFSDM_CHCFGR1_CKOUTDIV(0));
> +		clk_disable_unprepare(priv->clk);
> +		if (priv->aclk)
> +			clk_disable_unprepare(priv->aclk);
> +	}
> +	dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,
> +		atomic_read(&priv->n_active_ch));
> +}
> +
> +static unsigned int stm32_dfsdm_get_clkout_divider(struct dfsdm_priv *priv,
> +						   unsigned long rate)
> +{
> +	unsigned int delta, div;
> +
> +	/* div = 0 disables the clockout */
> +	if (!priv->clkout_freq_req)
> +		return 0;
> +
> +	div = DIV_ROUND_CLOSEST(rate, priv->clkout_freq_req);
> +
> +	delta = rate - (priv->clkout_freq_req * div);
> +	if (delta)
> +		dev_warn(&priv->pdev->dev,
> +			 "clkout not accurate. delta (Hz): %d\n", delta);
> +
> +	dev_dbg(&priv->pdev->dev, "%s: clk: %lu (Hz), div %u\n",
> +		__func__, rate, div);
> +
> +	return (div - 1);
> +}
> +
> +/*
> + * Filters
> + */
> +
> +static int stm32_dfsdm_clear_event(struct dfsdm_priv *priv, unsigned int fl_id,
> +				   unsigned int event, int mask)
> +{
> +	int val;
> +
> +	switch (event) {
> +	case DFSDM_EVENT_INJ_EOC:
> +		DFSDM_REG_READ(priv->regmap, DFSDM_JDATAR(fl_id), &val);
> +		break;
> +	case DFSDM_EVENT_REG_EOC:
> +		DFSDM_REG_READ(priv->regmap, DFSDM_RDATAR(fl_id), &val);
> +		break;
> +	case DFSDM_EVENT_INJ_XRUN:
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_ICR(fl_id),
> +				  DFSDM_ICR_CLRJOVRF_MASK,
> +				  DFSDM_ICR_CLRJOVRF_MASK);
> +		break;
> +	case DFSDM_EVENT_REG_XRUN:
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_ICR(fl_id),
> +				  DFSDM_ICR_CLRROVRF_MASK,
> +				  DFSDM_ICR_CLRROVRF_MASK);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
> +{
> +	struct filter_params *params = arg;
> +	unsigned int status;
> +	struct dfsdm_priv *priv = params->priv;
> +	unsigned int event_mask = params->event_mask;
> +
> +	DFSDM_REG_READ(priv->regmap, DFSDM_ISR(params->id), &status);
> +
> +	if (status & DFSDM_ISR_JOVRF_MASK) {
> +		if (event_mask & DFSDM_EVENT_INJ_XRUN) {
> +			params->event.cb(&priv->dfsdm, params->id,
> +					 DFSDM_EVENT_INJ_XRUN, 0,
> +					 params->event.context);
> +		}
> +		stm32_dfsdm_clear_event(priv, params->id, DFSDM_EVENT_INJ_XRUN,
> +					0);
> +	}
> +
> +	if (status & DFSDM_ISR_ROVRF_MASK) {
> +		if (event_mask & DFSDM_EVENT_REG_XRUN) {
> +			params->event.cb(&priv->dfsdm, params->id,
> +					 DFSDM_EVENT_REG_XRUN, 0,
> +					 params->event.context);
> +		}
> +		stm32_dfsdm_clear_event(priv, params->id, DFSDM_EVENT_REG_XRUN,
> +					0);
> +	}
> +
> +	if (status & DFSDM_ISR_JEOCF_MASK) {
> +		if (event_mask & DFSDM_EVENT_INJ_EOC)
> +			params->event.cb(&priv->dfsdm, params->id,
> +					 DFSDM_EVENT_INJ_EOC, 0,
> +					 params->event.context);
> +		else
> +			stm32_dfsdm_clear_event(priv, params->id,
> +						DFSDM_EVENT_INJ_EOC, 0);
> +	}
> +
> +	if (status & DFSDM_ISR_REOCF_MASK) {
> +		if (event_mask & DFSDM_EVENT_REG_EOC)
> +			params->event.cb(&priv->dfsdm, params->id,
> +					 DFSDM_EVENT_REG_EOC, 0,
> +					 params->event.context);
> +		else
> +			stm32_dfsdm_clear_event(priv, params->id,
> +						DFSDM_EVENT_REG_EOC, 0);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static void stm32_dfsdm_configure_reg_conv(struct dfsdm_priv *priv,
> +					   unsigned int fl_id,
> +					   struct stm32_dfsdm_regular *params)
> +{
> +	unsigned int ch_id = params->ch_src;
> +
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RCH_MASK,
> +			  DFSDM_CR1_RCH(ch_id));
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_FAST_MASK,
> +			  DFSDM_CR1_FAST(params->fast_mode));
> +
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RCONT_MASK,
> +			  DFSDM_CR1_RCONT(params->cont_mode));
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RDMAEN_MASK,
> +			  DFSDM_CR1_RDMAEN(params->dma_mode));
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RSYNC_MASK,
> +			  DFSDM_CR1_RSYNC(params->sync_mode));
> +
> +	priv->filters[fl_id].scan_ch = BIT(ch_id);
> +}
> +
> +static void stm32_dfsdm_configure_inj_conv(struct dfsdm_priv *priv,
> +					   unsigned int fl_id,
> +					   struct stm32_dfsdm_injected *params)
> +{
> +	int val;
> +
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_JSCAN_MASK,
> +			  DFSDM_CR1_JSCAN(params->scan_mode));
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_JDMAEN_MASK,
> +			  DFSDM_CR1_JDMAEN(params->dma_mode));
> +
> +	val = (params->trigger == DFSDM_FILTER_EXT_TRIGGER) ?
> +	      params->trig_src : 0;
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id),
> +			  DFSDM_CR1_JEXTSEL_MASK,
> +			  DFSDM_CR1_JEXTSEL(val));
> +
> +	val = (params->trigger == DFSDM_FILTER_SYNC_TRIGGER) ? 1 : 0;
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_JSYNC_MASK,
> +			  DFSDM_CR1_JSYNC(val));
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_JEXTEN_MASK,
> +			  DFSDM_CR1_JEXTEN(params->trig_pol));
> +	priv->filters[fl_id].scan_ch = params->ch_group;
> +
> +	DFSDM_REG_WRITE(priv->regmap, DFSDM_JCHGR(fl_id), params->ch_group);
> +}
> +
> +/**
> + * stm32_dfsdm_configure_filter - Configure filter.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: Filter id.
> + * @conv: Conversion type regular or injected.
> + */
> +int stm32_dfsdm_configure_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
> +				 struct stm32_dfsdm_filter *fl_cfg)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv,
> +					       dfsdm);
> +	struct stm32_dfsdm_sinc_filter *sparams = &fl_cfg->sinc_params;
> +
> +	dev_dbg(&priv->pdev->dev, "%s:config filter %d\n", __func__, fl_id);
> +
> +	/* Average integrator oversampling */
> +	if ((!fl_cfg->int_oversampling) ||
> +	    (fl_cfg->int_oversampling > DFSDM_MAX_INT_OVERSAMPLING)) {
> +		dev_err(&priv->pdev->dev, "invalid integrator oversampling %d\n",
> +			fl_cfg->int_oversampling);
> +		return -EINVAL;
> +	}
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_FCR(fl_id), DFSDM_FCR_IOSR_MASK,
> +			  DFSDM_FCR_IOSR((fl_cfg->int_oversampling - 1)));
> +
> +	/* Oversamplings and filter*/
> +	if ((!sparams->oversampling) ||
> +	    (sparams->oversampling > DFSDM_MAX_FL_OVERSAMPLING)) {
> +		dev_err(&priv->pdev->dev, "invalid oversampling %d\n",
> +			sparams->oversampling);
> +		return -EINVAL;
> +	}
> +
> +	if (sparams->order > DFSDM_SINC5_ORDER) {
> +		dev_err(&priv->pdev->dev, "invalid filter order %d\n",
> +			sparams->order);
> +		return -EINVAL;
> +	}
> +
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FOSR_MASK,
> +			  DFSDM_FCR_FOSR((sparams->oversampling - 1)));
> +
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FORD_MASK,
> +			  DFSDM_FCR_FORD(sparams->order));
> +
> +	/* Conversion */
> +	if (fl_cfg->inj_params)
> +		stm32_dfsdm_configure_inj_conv(priv, fl_id, fl_cfg->inj_params);
> +	else if (fl_cfg->reg_params)
> +		stm32_dfsdm_configure_reg_conv(priv, fl_id, fl_cfg->reg_params);
> +
> +	priv->filters[fl_id].event = fl_cfg->event;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_configure_filter);
> +
> +/**
> + * stm32_dfsdm_start_filter - Start filter conversion.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: Filter id.
> + * @conv: Conversion type regular or injected.
> + */
> +void stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
> +			      enum stm32_dfsdm_conv_type conv)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +
> +	dev_dbg(&priv->pdev->dev, "%s:start filter %d\n", __func__, fl_id);
> +
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_DFEN_MASK,
> +			  DFSDM_CR1_DFEN(1));
> +
> +	if (conv == DFSDM_FILTER_REG_CONV) {
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id),
> +				  DFSDM_CR1_RSWSTART_MASK,
> +				  DFSDM_CR1_RSWSTART(1));
> +	} else if (conv == DFSDM_FILTER_SW_INJ_CONV) {
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id),
> +				  DFSDM_CR1_JSWSTART_MASK,
> +				  DFSDM_CR1_JSWSTART(1));
> +	}
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_start_filter);
> +
> +/**
> + * stm32_dfsdm_stop_filter - Stop filter conversion.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: Filter id.
> + */
> +void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +
> +	dev_dbg(&priv->pdev->dev, "%s:stop filter %d\n", __func__, fl_id);
> +
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_DFEN_MASK,
> +			  DFSDM_CR1_DFEN(0));
> +	priv->filters[fl_id].scan_ch = 0;
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_stop_filter);
> +
> +/**
> + * stm32_dfsdm_read_fl_conv - Read filter conversion.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: Filter id.
> + * @type: Regular or injected conversion.
> + */
> +void stm32_dfsdm_read_fl_conv(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
> +			      u32 *val, int *ch_id,
> +			      enum stm32_dfsdm_conv_type type)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +	int reg_v, offset;
> +
> +	if (type == DFSDM_FILTER_REG_CONV)
> +		offset = DFSDM_RDATAR(fl_id);
> +	else
> +		offset = DFSDM_JDATAR(fl_id);
> +
> +	DFSDM_REG_READ(priv->regmap, offset, &reg_v);
> +
> +	*ch_id = reg_v & DFSDM_DATAR_CH_MASK;
> +	*val = reg_v & DFSDM_DATAR_DATA_MASK;
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_read_fl_conv);
> +
> +/**
> + * stm32_dfsdm_get_filter - Get filter instance.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: Filter instance to reserve.
> + *
> + * Reserves a DFSDM filter resource.
> + */
> +int stm32_dfsdm_get_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv,
> +					       dfsdm);
> +	struct device *dev = &priv->pdev->dev;
> +
> +	spin_lock(&priv->lock);
> +	if (!(priv->free_filter_mask & BIT(fl_id))) {
> +		spin_unlock(&priv->lock);
> +		dev_err(dev, "filter resource %d available\n", fl_id);
> +		return -EBUSY;
> +	}
> +	priv->free_filter_mask &= ~BIT(fl_id);
> +
> +	spin_unlock(&priv->lock);
> +
> +	dev_dbg(dev, "%s: new mask %#x\n", __func__, priv->free_filter_mask);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_get_filter);
> +
> +/**
> + * stm32_dfsdm_release_filter - Release filter instance.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: Filter id.
> + *
> + * Free the DFSDM filter resource.
> + */
> +void stm32_dfsdm_release_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +
> +	spin_lock(&priv->lock);
> +	priv->free_filter_mask |= BIT(fl_id);
> +	spin_unlock(&priv->lock);
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_release_filter);
> +
> +/**
> + * stm32_dfsdm_get_filter_dma_addr - Get register address for dma transfer.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: Filter id.
> + * @conv: Conversion type.
> + */
> +dma_addr_t stm32_dfsdm_get_filter_dma_phy_addr(struct stm32_dfsdm *dfsdm,
> +					       unsigned int fl_id,
> +					       enum stm32_dfsdm_conv_type conv)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +
> +	if (conv == DFSDM_FILTER_REG_CONV)
> +		return (dma_addr_t)(priv->phys_base + DFSDM_RDATAR(fl_id));
> +	else
> +		return (dma_addr_t)(priv->phys_base + DFSDM_JDATAR(fl_id));
> +}
> +
> +/**
> + * stm32_dfsdm_register_fl_event - Register filter event.
What is a filter event?  More details good on things that are very
device specific like this.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: Filter id.
> + * @event: Event to unregister.
> + * @chan_mask: Mask of channels associated to filter.
> + *
> + * The function enables associated IRQ.
> + */
> +int stm32_dfsdm_register_fl_event(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
> +				  enum stm32_dfsdm_events event,
> +				  unsigned int chan_mask)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +	unsigned long flags, ulmask = chan_mask;
> +	int ret, i;
> +
> +	dev_dbg(&priv->pdev->dev, "%s:for filter %d: event %#x ch_mask %#x\n",
> +		__func__, fl_id, event, chan_mask);
> +
> +	if (event > DFSDM_EVENT_CKA)
> +		return -EINVAL;
> +
> +	/* Clear interrupt before enable them */
> +	ret = stm32_dfsdm_clear_event(priv, fl_id, event, chan_mask);
> +	if (ret < 0)
> +		return ret;
> +
> +	spin_lock_irqsave(&priv->lock, flags);
> +	/* Enable interrupts */
> +	switch (event) {
> +	case DFSDM_EVENT_SCD:
> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
> +					  DFSDM_CHCFGR1_SCDEN_MASK,
> +					  DFSDM_CHCFGR1_SCDEN(1));
> +		}
> +		if (!priv->scd_filter_mask)
> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
> +					  DFSDM_CR2_SCDIE_MASK,
> +					  DFSDM_CR2_SCDIE(1));
> +		priv->scd_filter_mask |= BIT(fl_id);
> +		break;
> +	case DFSDM_EVENT_CKA:
> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
> +					  DFSDM_CHCFGR1_CKABEN_MASK,
> +					  DFSDM_CHCFGR1_CKABEN(1));
> +		}
> +		if (!priv->ckab_filter_mask)
> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
> +					  DFSDM_CR2_CKABIE_MASK,
> +					  DFSDM_CR2_CKABIE(1));
> +		priv->ckab_filter_mask |= BIT(fl_id);
> +		break;
> +	default:
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(fl_id), event, event);
> +	}
> +	priv->filters[fl_id].event_mask |= event;
> +	spin_unlock_irqrestore(&priv->lock, flags);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_register_fl_event);
> +
> +/**
> + * stm32_dfsdm_unregister_fl_event - Unregister filter event.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @fl_id: Filter id.
> + * @event: Event to unregister.
> + * @chan_mask: Mask of channels associated to filter.
> + *
> + * The function disables associated IRQ.
> + */
> +int stm32_dfsdm_unregister_fl_event(struct stm32_dfsdm *dfsdm,
> +				    unsigned int fl_id,
> +				    enum stm32_dfsdm_events event,
> +				    unsigned int chan_mask)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +	unsigned long flags, ulmask = chan_mask;
> +	int i;
> +
> +	dev_dbg(&priv->pdev->dev, "%s:for filter %d: event %#x ch_mask %#x\n",
> +		__func__, fl_id, event, chan_mask);
> +
> +	if (event > DFSDM_EVENT_CKA)
> +		return -EINVAL;
> +
> +	spin_lock_irqsave(&priv->lock, flags);
> +	/* Disable interrupts */
> +	switch (event) {
> +	case DFSDM_EVENT_SCD:
> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
> +					  DFSDM_CHCFGR1_SCDEN_MASK,
> +					  DFSDM_CHCFGR1_SCDEN(0));
> +		}
> +		priv->scd_filter_mask &= ~BIT(fl_id);
> +		if (!priv->scd_filter_mask)
> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
> +					  DFSDM_CR2_SCDIE_MASK,
> +					  DFSDM_CR2_SCDIE(0));
> +		break;
> +	case DFSDM_EVENT_CKA:
> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
> +					  DFSDM_CHCFGR1_CKABEN_MASK,
> +					  DFSDM_CHCFGR1_CKABEN(0));
> +		}
> +		priv->ckab_filter_mask &= ~BIT(fl_id);
> +		if (!priv->ckab_filter_mask)
> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
> +					  DFSDM_CR2_CKABIE_MASK,
> +					  DFSDM_CR2_CKABIE(0));
> +		break;
> +	default:
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(fl_id), event, 0);
> +	}
> +
> +	priv->filters[fl_id].event_mask &= ~event;
> +	spin_unlock_irqrestore(&priv->lock, flags);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_unregister_fl_event);
> +
> +/*
> + * Channels
> + */
> +static void stm32_dfsdm_init_channel(struct dfsdm_priv *priv,
> +				     struct stm32_dfsdm_channel *ch)
> +{
Comments in here for what the various bits are doing would be great.
The naming makes it just about possible to work out, but not nice
to read!
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
> +			  DFSDM_CHCFGR1_DATMPX_MASK,
> +			  DFSDM_CHCFGR1_DATMPX(ch->type.source));
> +	if (ch->type.source == DFSDM_CHANNEL_EXTERNAL_INPUTS) {
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
> +				  DFSDM_CHCFGR1_SITP_MASK,
> +				  DFSDM_CHCFGR1_SITP(ch->serial_if.type));
> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
> +				  DFSDM_CHCFGR1_SPICKSEL_MASK,
> +				DFSDM_CHCFGR1_SPICKSEL(ch->serial_if.spi_clk));
> +	}
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
> +			  DFSDM_CHCFGR1_DATPACK_MASK,
> +			  DFSDM_CHCFGR1_DATPACK(ch->type.DataPacking));
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch->id),
> +			  DFSDM_CHCFGR1_CHINSEL_MASK,
> +			  DFSDM_CHCFGR1_CHINSEL(ch->serial_if.pins));
> +}
> +
> +/**
> + * stm32_dfsdm_start_channel - Configure and activate DFSDM channel.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @ch: Filter id.
> + * @cfg: Filter configuration.
> + */
> +int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id,
> +			      struct stm32_dfsdm_ch_cfg *cfg)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv,
> +					       dfsdm);
> +	struct regmap *reg = priv->regmap;
> +	int ret;
> +
> +	dev_dbg(&priv->pdev->dev, "%s: for channel %d\n", __func__, ch_id);
> +
> +	ret = stm32_dfsdm_start_dfsdm(priv);
> +	if (ret < 0)
> +		return ret;
> +
> +	DFSDM_UPDATE_BITS(reg, DFSDM_CHCFGR2(ch_id), DFSDM_CHCFGR2_DTRBS_MASK,
> +			  DFSDM_CHCFGR2_DTRBS(cfg->right_bit_shift));
> +	DFSDM_UPDATE_BITS(reg, DFSDM_CHCFGR2(ch_id), DFSDM_CHCFGR2_OFFSET_MASK,
> +			  DFSDM_CHCFGR2_OFFSET(cfg->offset));
> +
> +	DFSDM_UPDATE_BITS(reg, DFSDM_CHCFGR1(ch_id), DFSDM_CHCFGR1_CHEN_MASK,
> +			  DFSDM_CHCFGR1_CHEN(1));
> +
> +	/* Clear absence detection IRQ */
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_ICR(0),
> +			  DFSDM_ICR_CLRCKABF_CH_MASK(ch_id),
> +			  DFSDM_ICR_CLRCKABF_CH(1, ch_id));
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_start_channel);
> +
> +/**
> + * stm32_dfsdm_stop_channel - Deactivate channel.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @ch_id: DFSDM channel identifier.
> + */
> +void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +
> +	dev_dbg(&priv->pdev->dev, "%s:for channel %d\n", __func__, ch_id);
> +
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch_id),
> +			  DFSDM_CHCFGR1_CHEN_MASK,
> +			  DFSDM_CHCFGR1_CHEN(0));
> +
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch_id),
> +			  DFSDM_CHCFGR1_CKABEN_MASK, DFSDM_CHCFGR1_CKABEN(0));
> +
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(ch_id),
> +			  DFSDM_CHCFGR1_SCDEN_MASK, DFSDM_CHCFGR1_SCDEN(0));
> +
> +	stm32_dfsdm_stop_dfsdm(priv);
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_stop_channel);
> +
> +/**
> + * stm32_dfsdm_get_channel - Get channel instance.
> + *
> + * @dfsdm: handle used to retrieve dfsdm context.
> + * @ch: DFSDM channel hardware parameters.
> + *
> + * Reserve DFSDM channel resource.
> + */
> +int stm32_dfsdm_get_channel(struct stm32_dfsdm *dfsdm,
> +			    struct stm32_dfsdm_channel *ch)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +	unsigned int id = ch->id;
> +
> +	dev_dbg(&priv->pdev->dev, "%s:get channel %d\n", __func__, id);
> +
> +	if (id >= priv->dfsdm.max_channels) {
> +		dev_err(&priv->pdev->dev, "channel (%d) is not valid\n", id);
> +		return -EINVAL;
> +	}
> +
> +	if ((ch->type.source != DFSDM_CHANNEL_EXTERNAL_INPUTS) &
> +	    (ch->serial_if.spi_clk != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL) &
> +	    (!priv->clkout_freq_req)) {
> +		dev_err(&priv->pdev->dev, "clkout not present\n");
> +		return -EINVAL;
> +	}
> +
> +	spin_lock(&priv->lock);
> +	if (!(BIT(id) & priv->free_channel_mask)) {
> +		spin_unlock(&priv->lock);
> +		dev_err(&priv->pdev->dev, "channel (%d) already in use\n", id);
> +		return -EBUSY;
> +	}
> +
> +	priv->free_channel_mask &= ~BIT(id);
> +	priv->channels[id] = *ch;
> +	spin_unlock(&priv->lock);
> +
> +	dev_dbg(&priv->pdev->dev, "%s: new mask %#x\n", __func__,
> +		priv->free_channel_mask);
> +
> +	/**
> +	 * Check clock constrainst between clkout and either
> +	 * dfsdm/audio clock:
> +	 * - In SPI mode (clkout is used): Fclk >= 4 * Fclkout
> +	 *   (e.g. CKOUTDIV >= 3)
> +	 * - In mancherster mode: Fclk >= 6 * Fclkout
> +	 */
> +	switch (ch->serial_if.type) {
> +	case DFSDM_CHANNEL_SPI_RISING:
> +	case DFSDM_CHANNEL_SPI_FALLING:
> +		if (priv->clkout_div && priv->clkout_div < 3)
> +			dev_warn(&priv->pdev->dev,
> +				 "Clock div should be higher than 3\n");
Really only warnings?  If requirements then error out. If not, then
a description of what the side effects of these would be would be great
here and perhaps even in the warning message.
> +		break;
> +	case DFSDM_CHANNEL_MANCHESTER_RISING:
> +	case DFSDM_CHANNEL_MANCHESTER_FALLING:
> +		if (priv->clkout_div && priv->clkout_div < 5)
> +			dev_warn(&priv->pdev->dev,
> +				 "Clock div should be higher than 5\n");
> +		break;
> +	}
> +
> +	stm32_dfsdm_init_channel(priv, ch);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_get_channel);
> +
> +/**
> + * stm32_dfsdm_release_channel - Release channel instance.
> + *
> + * @dfsdm: Handle used to retrieve dfsdm context.
> + * @ch_id: DFSDM channel identifier.
> + *
> + * Free the DFSDM channel resource.
> + */
> +void stm32_dfsdm_release_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +
> +	spin_lock(&priv->lock);
> +	priv->free_channel_mask |= BIT(ch_id);
> +	spin_unlock(&priv->lock);
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_release_channel);
> +
> +/**
> + * stm32_dfsdm_get_clk_out_rate - get clkout frequency.
> + *
> + * @dfsdm: handle used to retrieve dfsdm context.
> + * @rate: clock out rate in Hz.
> + *
> + * Provide output frequency used for external ADC.
> + * return EINVAL if clockout is not used else return 0.
> + */
> +int stm32_dfsdm_get_clk_out_rate(struct stm32_dfsdm *dfsdm, unsigned long *rate)
> +{
> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
> +	unsigned long int clk_rate;
> +
> +	if (!priv->clkout_div)
> +		return -EINVAL;
> +
> +	clk_rate = clk_get_rate(priv->aclk ? priv->aclk : priv->clk);
> +	*rate = clk_rate / (priv->clkout_div + 1);
> +	dev_dbg(&priv->pdev->dev, "%s: clkout: %ld (Hz)\n", __func__, *rate);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(dfsdm_get_clk_out_rate);
> +
> +static int stm32_dfsdm_parse_of(struct platform_device *pdev,
> +				struct dfsdm_priv *priv)
> +{
> +	struct device_node *node = pdev->dev.of_node;
> +	struct resource *res;
> +	int ret, val;
> +
> +	if (!node)
> +		return -EINVAL;
> +
> +	/* Get resources */
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(&pdev->dev, "Failed to get memory resource\n");
> +		return -ENODEV;
> +	}
> +	priv->phys_base = res->start;
> +	priv->base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(priv->base))
> +		return PTR_ERR(priv->base);
> +
> +	ret = of_property_read_u32(node, "st,clkout-freq", &val);
> +	if (!ret) {
> +		if (!val) {
> +			dev_err(&priv->pdev->dev,
> +				"st,clkout-freq cannot be 0\n");
> +			return -EINVAL;
> +		}
> +		priv->clkout_freq_req = val;
> +	} else if (ret != -EINVAL) {
> +		dev_err(&priv->pdev->dev, "Failed to get st,clkout-freq\n");
> +		return ret;
> +	}
> +
> +	/* Source clock */
> +	priv->clk = devm_clk_get(&pdev->dev, "dfsdm_clk");
> +	if (IS_ERR(priv->clk)) {
> +		dev_err(&pdev->dev, "No stm32_dfsdm_clk clock found\n");
> +		return -EINVAL;
> +	}
> +
> +	priv->aclk = devm_clk_get(&pdev->dev, "audio_clk");
> +	if (IS_ERR(priv->aclk))
> +		priv->aclk = NULL;
> +
> +	return 0;
> +};
> +
> +static const struct of_device_id stm32_dfsdm_of_match[] = {
> +	{
> +		.compatible = "st,stm32h7-dfsdm",
> +		.data = &stm32h7_data
> +	},
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, stm32_dfsdm_of_match);
> +
> +static int stm32_dfsdm_remove(struct platform_device *pdev)
> +{
> +	of_platform_depopulate(&pdev->dev);
> +
> +	return 0;
> +}
> +
> +static int stm32_dfsdm_probe(struct platform_device *pdev)
> +{
> +	struct dfsdm_priv *priv;
> +	struct device_node *pnode = pdev->dev.of_node;
> +	const struct of_device_id *of_id;
> +	const struct stm32_dev_data *dev_data;
> +	enum dfsdm_clkout_src clk_src;
> +	int ret, i;
> +
> +	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->pdev = pdev;
> +
> +	/* Populate data structure depending on compatibility */
> +	of_id = of_match_node(stm32_dfsdm_of_match, pnode);
> +	if (!of_id->data) {
> +		dev_err(&pdev->dev, "Data associated to device is missing\n");
> +		return -EINVAL;
> +	}
> +
> +	dev_data = (const struct stm32_dev_data *)of_id->data;
> +
> +	ret = stm32_dfsdm_parse_of(pdev, priv);
> +	if (ret < 0)
> +		return ret;
> +
> +	priv->regmap = devm_regmap_init_mmio(&pdev->dev, priv->base,
> +					    dev_data->regmap_cfg);
> +	if (IS_ERR(priv->regmap)) {
> +		ret = PTR_ERR(priv->regmap);
> +		dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",
> +			__func__, ret);
> +		return ret;
> +	}
> +
> +	priv->dfsdm = dev_data->dfsdm;
> +
> +	priv->filters = devm_kcalloc(&pdev->dev, dev_data->dfsdm.max_filters,
> +				     sizeof(*priv->filters), GFP_KERNEL);
> +	if (IS_ERR(priv->filters)) {
> +		ret = PTR_ERR(priv->filters);
> +		goto probe_err;
> +	}
> +
> +	for (i = 0; i < dev_data->dfsdm.max_filters; i++) {
> +		struct filter_params *params = &priv->filters[i];
> +
> +		params->id = i;
> +		params->irq = platform_get_irq(pdev, i);
> +		if (params->irq < 0) {
> +			dev_err(&pdev->dev, "Failed to get IRQ resource\n");
> +			ret = params->irq;
> +			goto probe_err;
> +		}
> +
> +		ret = devm_request_irq(&pdev->dev, params->irq, stm32_dfsdm_irq,
> +				       0, dev_name(&pdev->dev), params);
> +		if (ret) {
> +			dev_err(&pdev->dev, "Failed to register interrupt\n");
> +			goto probe_err;
> +		}
> +
> +		params->priv = priv;
> +	}
> +
> +	priv->channels = devm_kcalloc(&pdev->dev, priv->dfsdm.max_channels,
> +				      sizeof(*priv->channels), GFP_KERNEL);
> +	if (IS_ERR(priv->channels)) {
> +		ret = PTR_ERR(priv->channels);
> +		goto probe_err;
> +	}
> +	priv->free_filter_mask = BIT(priv->dfsdm.max_filters) - 1;
> +	priv->free_channel_mask = BIT(priv->dfsdm.max_channels) - 1;
> +
> +	platform_set_drvdata(pdev, &priv->dfsdm);
> +	spin_lock_init(&priv->lock);
> +
> +	priv->clkout_div = stm32_dfsdm_get_clkout_divider(priv,
> +						    clk_get_rate(priv->clk));
> +
> +	ret = of_platform_populate(pnode, NULL, NULL, &pdev->dev);
> +	if (ret < 0)
> +		goto probe_err;
> +
> +	clk_src = priv->aclk ? AUDIO_CLK : DFSDM_CLK;
> +
I'd like to see a comment here saying what this is doing.
> +	DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(0),
> +			  DFSDM_CHCFGR1_CKOUTSRC_MASK,
> +			  DFSDM_CHCFGR1_CKOUTSRC(clk_src));
> +	return 0;
> +
> +probe_err:
> +	return ret;
> +}
> +
> +static struct platform_driver stm32_dfsdm_driver = {
> +	.probe = stm32_dfsdm_probe,
> +	.remove = stm32_dfsdm_remove,
> +	.driver = {
> +		.name = "stm32-dfsdm",
> +		.of_match_table = stm32_dfsdm_of_match,
> +	},
> +};
> +
> +module_platform_driver(stm32_dfsdm_driver);
> +
> +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics STM32 dfsdm driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/mfd/stm32-dfsdm.h b/include/linux/mfd/stm32-dfsdm.h
> new file mode 100644
> index 0000000..f6eb788
> --- /dev/null
> +++ b/include/linux/mfd/stm32-dfsdm.h
> @@ -0,0 +1,324 @@
> +/*
> + * This file is part of STM32 DFSDM mfd driver API
> + *
> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
> + * Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com>.
> + *
> + * 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.
> + */
> +#ifndef MDF_STM32_DFSDM_H
> +#define MDF_STM32_DFSDM_H
> +
> +/*
> + * Channel definitions
> + */
> +#define DFSDM_CHANNEL_0    BIT(0)
> +#define DFSDM_CHANNEL_1    BIT(1)
> +#define DFSDM_CHANNEL_2    BIT(2)
> +#define DFSDM_CHANNEL_3    BIT(3)
> +#define DFSDM_CHANNEL_4    BIT(4)
> +#define DFSDM_CHANNEL_5    BIT(5)
> +#define DFSDM_CHANNEL_6    BIT(6)
> +#define DFSDM_CHANNEL_7    BIT(7)
> +
> +/* DFSDM channel input data packing */
> +enum stm32_dfsdm_data_packing {
> +	DFSDM_CHANNEL_STANDARD_MODE,    /* Standard data packing mode */
> +	DFSDM_CHANNEL_INTERLEAVED_MODE, /* Interleaved data packing mode */
> +	DFSDM_CHANNEL_DUAL_MODE         /* Dual data packing mode */
> +};
> +
> +/* DFSDM channel input multiplexer */
> +enum stm32_dfsdm_input_multiplexer {
> +	DFSDM_CHANNEL_EXTERNAL_INPUTS,    /* Data taken from external inputs */
> +	DFSDM_CHANNEL_INTERNAL_ADC,       /* Data taken from internal ADC */
> +	DFSDM_CHANNEL_INTERNAL_REGISTER,  /* Data taken from register */
> +};
> +
> +/* DFSDM channel serial interface type */
> +enum stm32_dfsdm_serial_in_type {
> +	DFSDM_CHANNEL_SPI_RISING,         /* SPI with rising edge */
> +	DFSDM_CHANNEL_SPI_FALLING,        /* SPI with falling edge */
We could use standard SPI naming for these first two. That would I think
describe these as clock phases. However, perhaps alongside the machester
coding it doesn't make sense to do so.

> +	DFSDM_CHANNEL_MANCHESTER_RISING,  /* Manchester with rising edge */
> +	DFSDM_CHANNEL_MANCHESTER_FALLING, /* Manchester with falling edge */
> +};
> +
> +/* DFSDM channel serial spi clock source */
> +enum stm32_dfsdm_spi_clk_src {
> +	/* External SPI clock */
> +	DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL,
> +	/* Internal SPI clock */
> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL,
> +	/* Internal SPI clock divided by 2, falling edge */
> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING,
> +	/* Internal SPI clock divided by 2, rising edge */
> +	DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING
> +};
> +
> +/* DFSDM channel input pins */
> +enum stm32_dfsdm_serial_in_select {
> +	/* Serial input taken from pins of the same channel (y) */
> +	DFSDM_CHANNEL_SAME_CHANNEL_PINS,
> +	/* Serial input taken from pins of the following channel (y + 1)*/
> +	DFSDM_CHANNEL_NEXT_CHANNEL_PINS,
> +};
> +
> +/**
> + * struct stm32_dfsdm_input_type - DFSDM channel init structure definition.
> + * @DataPacking: Standard, interleaved or dual mode for internal register.
> + * @source: channel source: internal DAC, serial input or memory.
> + */
> +struct stm32_dfsdm_input_type {
> +	enum stm32_dfsdm_data_packing DataPacking;
> +	enum stm32_dfsdm_input_multiplexer source;
> +};
> +
> +/**
> + * struct stm32_dfsdm_serial_if - DFSDM serial interface parameters.
> + * @type:	Serial interface type.
> + * @spi_clk:	SPI clock source.
> + * @pins:	select serial interface associated to the channel
> + */
> +struct stm32_dfsdm_serial_if {
> +	enum stm32_dfsdm_serial_in_type type;
> +	enum stm32_dfsdm_spi_clk_src spi_clk;
> +	enum stm32_dfsdm_serial_in_select pins;
> +};
> +
> +/**
> + * struct stm32_dfsdm_channel - DFSDM channel hardware parameters.
> + * @id:		DFSDM channel identifier.
> + * @type:	DFSDM channel input parameters.
> + * @serial_if:	DFSDM channel serial interface parameters.
> + *		Mandatory for DFSDM_CHANNEL_EXTERNAL_INPUTS.
> + */
> +struct stm32_dfsdm_channel {
> +	unsigned int id;
> +	struct stm32_dfsdm_input_type type;
> +	struct stm32_dfsdm_serial_if serial_if;
> +};
> +
> +/**
> + * struct stm32_dfsdm_ch_cfg - DFSDM channel config.
> + * @offset:		DFSDM channel 24 bit calibration offset.
> + * @right_bit_shift:	DFSDM channel right bit shift of the data result.
> + */
> +struct stm32_dfsdm_ch_cfg {
> +	unsigned int offset;
> +	unsigned int right_bit_shift;
> +};
> +
> +/*
> + * Filter definitions
Single line comment syntax.  Make sure this is correct throughout as otherwise
we'll only get 'fix' patches turning up after this hits linux-next.
> + */
> +
> +#define DFSDM_MIN_INT_OVERSAMPLING 1
> +#define DFSDM_MAX_INT_OVERSAMPLING 256
> +#define DFSDM_MIN_FL_OVERSAMPLING 1
Oversampling of 1 is effectively not oversampling so do we need this?

> +#define DFSDM_MAX_FL_OVERSAMPLING 1024
> +
> +enum stm32_dfsdm_events {
Slightly clunky enum usage.  I'd just use the value and put the BIT around
whereever it is used.
> +	DFSDM_EVENT_INJ_EOC =	BIT(0), /* Injected end of conversion event */
> +	DFSDM_EVENT_REG_EOC =	BIT(1), /* Regular end of conversion event */
> +	DFSDM_EVENT_INJ_XRUN =	BIT(2), /* Injected conversion overrun event */
> +	DFSDM_EVENT_REG_XRUN =	BIT(3), /* Regular conversion overrun event */
> +	DFSDM_EVENT_AWD =	BIT(4), /* Analog watchdog event */
> +	DFSDM_EVENT_SCD =	BIT(5), /* Short circuit detector event */
> +	DFSDM_EVENT_CKA =	BIT(6), /* Clock abscence detection event */
> +};
> +
> +#define STM32_DFSDM_EVENT_MASK 0x3F
> +
> +/* DFSDM filter order  */
> +enum stm32_dfsdm_sinc_order {
> +	DFSDM_FASTSINC_ORDER, /* FastSinc filter type */
> +	DFSDM_SINC1_ORDER,    /* Sinc 1 filter type */
> +	DFSDM_SINC2_ORDER,    /* Sinc 2 filter type */
> +	DFSDM_SINC3_ORDER,    /* Sinc 3 filter type */
> +	DFSDM_SINC4_ORDER,    /* Sinc 4 filter type (N.A. for watchdog) */
> +	DFSDM_SINC5_ORDER,    /* Sinc 5 filter type (N.A. for watchdog) */
> +	DFSDM_NB_SINC_ORDER,
> +};
> +
> +/* DFSDM filter order */
Please check your comments - this one is clearly wrong.

> +enum stm32_dfsdm_state {
> +	DFSDM_DISABLE,
> +	DFSDM_ENABLE,
> +};
> +
> +/**
> + * struct stm32_dfsdm_sinc_filter - DFSDM Sinc filter structure definition
> + * @order: DFSM filter order.
> + * @oversampling: DFSDM filter oversampling:
> + *		  post processing filter: min = 1, max = 1024.
> + */
> +struct stm32_dfsdm_sinc_filter {
> +	enum stm32_dfsdm_sinc_order order;
> +	unsigned int oversampling;
> +};
> +
> +/* DFSDM filter conversion trigger */
> +enum stm32_dfsdm_trigger {
> +	DFSDM_FILTER_SW_TRIGGER,   /* Software trigger */
> +	DFSDM_FILTER_SYNC_TRIGGER, /* Synchronous with DFSDM0 */
> +	DFSDM_FILTER_EXT_TRIGGER,  /* External trigger (only for injected) */
> +};
> +
> +/* DFSDM filter external trigger polarity */
> +enum stm32_dfsdm_filter_ext_trigger_pol {
> +	DFSDM_FILTER_EXT_TRIG_NO_TRIG,      /* Trigger disable */
> +	DFSDM_FILTER_EXT_TRIG_RISING_EDGE,  /* Rising edge */
> +	DFSDM_FILTER_EXT_TRIG_FALLING_EDGE, /* Falling edge */
> +	DFSDM_FILTER_EXT_TRIG_BOTH_EDGES,   /* Rising and falling edges */
> +};
> +
> +/* DFSDM filter conversion type */
> +enum stm32_dfsdm_conv_type {
> +	DFSDM_FILTER_REG_CONV,      /* Regular conversion */
> +	DFSDM_FILTER_SW_INJ_CONV,   /* Injected conversion */
> +	DFSDM_FILTER_TRIG_INJ_CONV, /* Injected conversion */
> +};
> +
> +/* DFSDM filter regular synchronous mode */
> +enum stm32_dfsdm_conv_rsync {
> +	DFSDM_FILTER_RSYNC_OFF, /* regular conversion asynchronous */
> +	DFSDM_FILTER_RSYNC_ON,  /* regular conversion synchronous with filter0*/
stray 0?
> +};
> +
> +/**
> + * struct stm32_dfsdm_regular - DFSDM filter conversion parameters structure
> + * @ch_src:	Channel source from 0 to 7.
> + * @fast_mode:	Enable/disable fast mode for regular conversion.
> + * @dma_mode:	Enable/disable dma mode.
> + * @cont_mode	Enable/disable continuous conversion.
> + * @sync_mode	Enable/disable synchro mode.
> + */
> +struct stm32_dfsdm_regular {
> +	unsigned int ch_src;
> +	bool fast_mode;
> +	bool dma_mode;
> +	bool cont_mode;
> +	bool sync_mode;
> +};
> +
> +/**
> + * struct stm32_dfsdm_injected - DFSDM filter  conversion parameters structure
> + * @trigger:	Trigger used to start injected conversion.
> + * @trig_src:	External trigger, 0 to 30 (refer to datasheet for details).
> + * @trig_pol:	External trigger edge: software, rising, falling or both.
> + * @scan_mode:	Enable/disable scan mode for injected conversion.
> + * @ch_group:	mask containing channels to scan ( set bit y to scan
> + *		channel y).
> + * @dma_mode:	DFSDM channel input parameters.
> + */
> +struct stm32_dfsdm_injected {
> +	enum stm32_dfsdm_trigger trigger;
> +	unsigned int trig_src;
> +	enum stm32_dfsdm_filter_ext_trigger_pol trig_pol;
> +	bool scan_mode;
> +	unsigned int ch_group;
> +	bool dma_mode;
> +};
> +
> +struct stm32_dfsdm;
> +
> +/**
> + * struct stm32_dfsdm_fl_event - DFSDM filters event
> + * @cb:	User event callback with parameters. be carful this function
> + *		is called under threaded IRQ context:
> + *			struct stm32_dfsdm *dfsdm: dfsdm handle,
> + *			unsigned int fl_id: filter id,
> + *			num stm32_dfsdm_events flag: event,
> + *			param: parameter associated to the event,
> + *			void *context: user context provided on registration.
> + * @context: User param to retrieve context.
> + */
> +struct stm32_dfsdm_fl_event {
> +	void (*cb)(struct stm32_dfsdm *, int, enum stm32_dfsdm_events,
> +		   unsigned int, void *);
> +	void *context;
> +};
> +
> +/**
> + * struct stm32_dfsdm_filter - DFSDM filter  conversion parameters structure
> + * @reg_params:		DFSDM regular conversion parameters.
> + *			this param is optional and not taken into account if
> + *			@inj_params is defined.
> + * @inj_params:		DFSDM injected conversion parameters (optional).
> + * @filter_params:	DFSDM filter parameters.
> + * @event:		Events callback.
> + * @int_oversampling:	Integrator oversampling ratio for average purpose
> + *			(range from 1 to 256).
> + * @ext_det_ch_mask:	Extreme detector mask for channel selection
> + *			mask generated using DFSDM_CHANNEL_0 to
> + *			DFSDM_CHANNEL_7. If 0 feature is disable.
> + */
> +struct stm32_dfsdm_filter {
> +	struct stm32_dfsdm_regular *reg_params;
> +	struct stm32_dfsdm_injected *inj_params;
> +	struct stm32_dfsdm_sinc_filter sinc_params;
> +	struct stm32_dfsdm_fl_event event;
> +	unsigned int int_oversampling;
> +};
> +
> +/**
> + * struct stm32_dfsdm - DFSDM context structure.
> + *
> + * @trig_info: Trigger name and id available last member name is null.
> + * @max_channels: max number of channels available.
> + * @max_filters: max number of filters available.
> + *
> + * Notice That structure is filled by mdf driver and must not be updated by
mfd
> + * user.
> + */
> +struct stm32_dfsdm {
> +	unsigned int max_channels;
> +	unsigned int max_filters;
> +};
> +
> +int stm32_dfsdm_get_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
> +void stm32_dfsdm_release_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
> +
> +dma_addr_t stm32_dfsdm_get_filter_dma_phy_addr(struct stm32_dfsdm *dfsdm,
> +					       unsigned int fl_id,
> +					       enum stm32_dfsdm_conv_type conv);
> +
> +int stm32_dfsdm_configure_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
> +				 struct stm32_dfsdm_filter *filter);
> +void stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
> +			      enum stm32_dfsdm_conv_type conv);
> +void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
> +
> +void stm32_dfsdm_read_fl_conv(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
> +			      u32 *val, int *ch_id,
> +			      enum stm32_dfsdm_conv_type type);
> +
> +int stm32_dfsdm_unregister_fl_event(struct stm32_dfsdm *dfsdm,
> +				    unsigned int fl_id,
> +				    enum stm32_dfsdm_events event,
> +				    unsigned int ch_mask);
> +int stm32_dfsdm_register_fl_event(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
> +				  enum stm32_dfsdm_events event,
> +				  unsigned int ch_mask);
> +
> +int stm32_dfsdm_get_channel(struct stm32_dfsdm *dfsdm,
> +			    struct stm32_dfsdm_channel *ch);
> +void stm32_dfsdm_release_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
> +
> +int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id,
> +			      struct stm32_dfsdm_ch_cfg *cfg);
> +void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
> +
> +int stm32_dfsdm_get_clk_out_rate(struct stm32_dfsdm *dfsdm,
> +				 unsigned long *rate);
> +
> +#endif
> 

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

* Re: [PATCH 3/7] IIO: add bindings for STM32 DFSDM ADC driver
  2017-01-23 16:32     ` Arnaud Pouliquen
  (?)
@ 2017-01-29 11:58       ` Jonathan Cameron
  -1 siblings, 0 replies; 130+ messages in thread
From: Jonathan Cameron @ 2017-01-29 11:58 UTC (permalink / raw)
  To: Arnaud Pouliquen, devicetree, linux-arm-kernel, linux-iio,
	alsa-devel, Lee Jones, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Alexandre Torgue, Maxime Coquelin

On 23/01/17 16:32, Arnaud Pouliquen wrote:
> This patch adds documentation of device tree bindings for the
> STM32 DFSDM ADC.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
Trivial bits inline.
> ---
>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 60 ++++++++++++++++++++++
>  1 file changed, 60 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt  b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
> new file mode 100644
> index 0000000..c156bcb
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 	
> @@ -0,0 +1,60 @@
> +STMicroelectronics STM32 DFSDM ADC device driver
> +
> +STM32 DFSDM ADC is a sigma delta analog-to-digital converter.
> +It has to be declared in device-tree as a subnode of the DFSDM mfd node.
> +
> +It has several multiplexed input channels. Conversions can be performed
> +in single, scan or discontinuous mode. Conversions can be launched in software
> +or using hardware triggers.
> +Each instance of the driver uses one filter instance handle by the DFSDM mfd
> +driver.
> +
> +DFSDM also offers extra features:
> +-The analog watchdog feature allows the application to detect if the
> + input voltage goes beyond the user-defined, higher or lower thresholds.
> +-The short circuit detection allows allows the application to detect if the
> + input is in CC.
> +-The clock absence detection allows application to detect if SPI input is clocked.
> +
> +Required properties:
> +- compatible:	Must be "st,stm32-dfsdm-adc".
> +- reg:		Specifies the DFSDM filter instance.
> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
> +- st,adc-channels:	List of single-ended channels muxed for this ADC.
> +- st,adc-channel-names:	List of single-ended channels Name.
> +
> +Optional properties:
> +- st,adc-channel-types:	Single-ended channel input type. Default value is 0.
> +			- 0: SPI with data on rising edge (default)
> +			- 1: SPI with data on falling edge
This is an element that can be described without magic numbers so I'd
prefer that you do so.

Also spell check edage -> edge.

> +			- 2: manchester codec, rising edage = logic 0
> +			- 3: manchester codec, rising edage = logic 1
> +- st,adc-channel-clk-src: Conversion clock source. default value is 1.
> +			  - 0: External SPI clocl (CLKIN x)
> +			  - 1: internal SPI clock (CLKOUT) (default)
> +			  - 2: internal SPI clock divided by 2 (falling edge).
> +			  - 2: internal SPI clock divided by 2 (rising edge).
3?
> +- st,adc-alt-channel: 	  must be defined if Two ADCs are connected on same SPI
> +			  input.
> +			  If not set channel n is connected to SPI input n.
> +			  If set channel n is connected to SPI input n + 1.
? Two data inputs with shared clock?
> +
> +Example:
> +	dfsdm: dfsdm@4400D000 {
> +		iio_dfsdm0: iio-dfsdm@0 {
> +			compatible = "st,stm32-dfsdm-adc";
> +			#io-channel-cells = <1>;
> +			reg = <0>;
> +			st,adc-channels = <1>;
> +			st,adc-channel-names = "in0";
> +		};
> +		iio_dfsdm1: iio-dfsdm@1 {
> +			compatible = "st,stm32-dfsdm-adc";
> +			#io-channel-cells = <1>;
> +			reg = <1>;
> +			st,adc-channels = <1>;
> +			st,adc-channel-names = "in1";
> +			st,adc-channel-types = <1>;
> +			st,adc-alt-channel = <1>;
> +		};
> +	};
> 

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

* Re: [PATCH 3/7] IIO: add bindings for STM32 DFSDM ADC driver
@ 2017-01-29 11:58       ` Jonathan Cameron
  0 siblings, 0 replies; 130+ messages in thread
From: Jonathan Cameron @ 2017-01-29 11:58 UTC (permalink / raw)
  To: Arnaud Pouliquen, devicetree, linux-arm-kernel, linux-iio,
	alsa-devel, Lee Jones, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre Torgue

On 23/01/17 16:32, Arnaud Pouliquen wrote:
> This patch adds documentation of device tree bindings for the
> STM32 DFSDM ADC.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
Trivial bits inline.
> ---
>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 60 ++++++++++++++++++++++
>  1 file changed, 60 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt  b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
> new file mode 100644
> index 0000000..c156bcb
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 	
> @@ -0,0 +1,60 @@
> +STMicroelectronics STM32 DFSDM ADC device driver
> +
> +STM32 DFSDM ADC is a sigma delta analog-to-digital converter.
> +It has to be declared in device-tree as a subnode of the DFSDM mfd node.
> +
> +It has several multiplexed input channels. Conversions can be performed
> +in single, scan or discontinuous mode. Conversions can be launched in software
> +or using hardware triggers.
> +Each instance of the driver uses one filter instance handle by the DFSDM mfd
> +driver.
> +
> +DFSDM also offers extra features:
> +-The analog watchdog feature allows the application to detect if the
> + input voltage goes beyond the user-defined, higher or lower thresholds.
> +-The short circuit detection allows allows the application to detect if the
> + input is in CC.
> +-The clock absence detection allows application to detect if SPI input is clocked.
> +
> +Required properties:
> +- compatible:	Must be "st,stm32-dfsdm-adc".
> +- reg:		Specifies the DFSDM filter instance.
> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
> +- st,adc-channels:	List of single-ended channels muxed for this ADC.
> +- st,adc-channel-names:	List of single-ended channels Name.
> +
> +Optional properties:
> +- st,adc-channel-types:	Single-ended channel input type. Default value is 0.
> +			- 0: SPI with data on rising edge (default)
> +			- 1: SPI with data on falling edge
This is an element that can be described without magic numbers so I'd
prefer that you do so.

Also spell check edage -> edge.

> +			- 2: manchester codec, rising edage = logic 0
> +			- 3: manchester codec, rising edage = logic 1
> +- st,adc-channel-clk-src: Conversion clock source. default value is 1.
> +			  - 0: External SPI clocl (CLKIN x)
> +			  - 1: internal SPI clock (CLKOUT) (default)
> +			  - 2: internal SPI clock divided by 2 (falling edge).
> +			  - 2: internal SPI clock divided by 2 (rising edge).
3?
> +- st,adc-alt-channel: 	  must be defined if Two ADCs are connected on same SPI
> +			  input.
> +			  If not set channel n is connected to SPI input n.
> +			  If set channel n is connected to SPI input n + 1.
? Two data inputs with shared clock?
> +
> +Example:
> +	dfsdm: dfsdm@4400D000 {
> +		iio_dfsdm0: iio-dfsdm@0 {
> +			compatible = "st,stm32-dfsdm-adc";
> +			#io-channel-cells = <1>;
> +			reg = <0>;
> +			st,adc-channels = <1>;
> +			st,adc-channel-names = "in0";
> +		};
> +		iio_dfsdm1: iio-dfsdm@1 {
> +			compatible = "st,stm32-dfsdm-adc";
> +			#io-channel-cells = <1>;
> +			reg = <1>;
> +			st,adc-channels = <1>;
> +			st,adc-channel-names = "in1";
> +			st,adc-channel-types = <1>;
> +			st,adc-alt-channel = <1>;
> +		};
> +	};
> 


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

* [PATCH 3/7] IIO: add bindings for STM32 DFSDM ADC driver
@ 2017-01-29 11:58       ` Jonathan Cameron
  0 siblings, 0 replies; 130+ messages in thread
From: Jonathan Cameron @ 2017-01-29 11:58 UTC (permalink / raw)
  To: linux-arm-kernel

On 23/01/17 16:32, Arnaud Pouliquen wrote:
> This patch adds documentation of device tree bindings for the
> STM32 DFSDM ADC.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
Trivial bits inline.
> ---
>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 60 ++++++++++++++++++++++
>  1 file changed, 60 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt  b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
> new file mode 100644
> index 0000000..c156bcb
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 	
> @@ -0,0 +1,60 @@
> +STMicroelectronics STM32 DFSDM ADC device driver
> +
> +STM32 DFSDM ADC is a sigma delta analog-to-digital converter.
> +It has to be declared in device-tree as a subnode of the DFSDM mfd node.
> +
> +It has several multiplexed input channels. Conversions can be performed
> +in single, scan or discontinuous mode. Conversions can be launched in software
> +or using hardware triggers.
> +Each instance of the driver uses one filter instance handle by the DFSDM mfd
> +driver.
> +
> +DFSDM also offers extra features:
> +-The analog watchdog feature allows the application to detect if the
> + input voltage goes beyond the user-defined, higher or lower thresholds.
> +-The short circuit detection allows allows the application to detect if the
> + input is in CC.
> +-The clock absence detection allows application to detect if SPI input is clocked.
> +
> +Required properties:
> +- compatible:	Must be "st,stm32-dfsdm-adc".
> +- reg:		Specifies the DFSDM filter instance.
> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
> +- st,adc-channels:	List of single-ended channels muxed for this ADC.
> +- st,adc-channel-names:	List of single-ended channels Name.
> +
> +Optional properties:
> +- st,adc-channel-types:	Single-ended channel input type. Default value is 0.
> +			- 0: SPI with data on rising edge (default)
> +			- 1: SPI with data on falling edge
This is an element that can be described without magic numbers so I'd
prefer that you do so.

Also spell check edage -> edge.

> +			- 2: manchester codec, rising edage = logic 0
> +			- 3: manchester codec, rising edage = logic 1
> +- st,adc-channel-clk-src: Conversion clock source. default value is 1.
> +			  - 0: External SPI clocl (CLKIN x)
> +			  - 1: internal SPI clock (CLKOUT) (default)
> +			  - 2: internal SPI clock divided by 2 (falling edge).
> +			  - 2: internal SPI clock divided by 2 (rising edge).
3?
> +- st,adc-alt-channel: 	  must be defined if Two ADCs are connected on same SPI
> +			  input.
> +			  If not set channel n is connected to SPI input n.
> +			  If set channel n is connected to SPI input n + 1.
? Two data inputs with shared clock?
> +
> +Example:
> +	dfsdm: dfsdm at 4400D000 {
> +		iio_dfsdm0: iio-dfsdm at 0 {
> +			compatible = "st,stm32-dfsdm-adc";
> +			#io-channel-cells = <1>;
> +			reg = <0>;
> +			st,adc-channels = <1>;
> +			st,adc-channel-names = "in0";
> +		};
> +		iio_dfsdm1: iio-dfsdm at 1 {
> +			compatible = "st,stm32-dfsdm-adc";
> +			#io-channel-cells = <1>;
> +			reg = <1>;
> +			st,adc-channels = <1>;
> +			st,adc-channel-names = "in1";
> +			st,adc-channel-types = <1>;
> +			st,adc-alt-channel = <1>;
> +		};
> +	};
> 

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

* Re: [PATCH 4/7] IIO: add STM32 DFSDM ADC support
  2017-01-23 16:32   ` Arnaud Pouliquen
  (?)
@ 2017-01-29 12:15     ` Jonathan Cameron
  -1 siblings, 0 replies; 130+ messages in thread
From: Jonathan Cameron @ 2017-01-29 12:15 UTC (permalink / raw)
  To: Arnaud Pouliquen, devicetree, linux-arm-kernel, linux-iio,
	alsa-devel, Lee Jones, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Alexandre Torgue, Maxime Coquelin

On 23/01/17 16:32, Arnaud Pouliquen wrote:
> Add driver to handle Sigma Delta ADC conversion for ADC
> connected to DFSDM IP.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
Various comments inline.  Again mostly reading this to grasp how the whole
lot fits together.

At the moment, other than some new ABI, this is a fairly simple sysfs or in
kernel polled ADC driver.  The hardware seems to support a whole lot more
but fair enough to present this as a starting point!

Jonathan
> ---
>  drivers/iio/adc/Kconfig           |   9 +
>  drivers/iio/adc/Makefile          |   1 +
>  drivers/iio/adc/stm32-dfsdm-adc.c | 676 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 686 insertions(+)
>  create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c
> 
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index e0b3c09..4b2b886 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -441,6 +441,15 @@ config STM32_ADC
>  	  This driver can also be built as a module.  If so, the module
>  	  will be called stm32-adc.
>  
> +config STM32_DFSDM_ADC
> +	tristate "STMicroelectronics STM32 DFSDM ADC driver"
> +	depends on (ARCH_STM32 && OF && MFD_STM32_DFSDM) || COMPILE_TEST
> +	help
> +	  Say yes here to build the driver for the STMicroelectronics
> +	  STM32 analog-to-digital converter with Digital filter.
> +	  This driver can also be built as a module. If so, the module
> +	  will be called stm32_dfsdm_adc.
> +
>  config STX104
>  	tristate "Apex Embedded Systems STX104 driver"
>  	depends on X86 && ISA_BUS_API
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 8e02a94..aed42c6 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -43,6 +43,7 @@ obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
>  obj-$(CONFIG_STX104) += stx104.o
>  obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
>  obj-$(CONFIG_STM32_ADC) += stm32-adc.o
> +obj-$(CONFIG_STM32_DFSDM_ADC) += stm32-dfsdm-adc.o
>  obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
>  obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
>  obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
> diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
> new file mode 100644
> index 0000000..727d6b1
> --- /dev/null
> +++ b/drivers/iio/adc/stm32-dfsdm-adc.c
> @@ -0,0 +1,676 @@
> +/*
> + * This file is part of STM32 DFSDM ADC driver
> + *
> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
> + * Author: Arnaud Pouliquen <arnaud.pouliquen@st.com>.
> + *
> + * License type: GPLv2
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/irq_work.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#include <linux/iio/iio.h>
> +
> +#include <linux/mfd/stm32-dfsdm.h>
> +
> +#define DFSDM_ADC_MAX_RESOLUTION 24
> +#define DFSDM_ADC_STORAGE_BITS   32
> +
> +#define DFSDM_MAX_CH_OFFSET BIT(24)
> +#define DFSDM_MAX_CH_SHIFT 24
> +
> +#define DFSDM_TIMEOUT_US 100000
> +#define DFSDM_TIMEOUT (msecs_to_jiffies(DFSDM_TIMEOUT_US / 1000))
> +
> +#define CH_ID_FROM_IDX(i) (adc->inputs[i].id)
> +#define CH_CFG_FROM_IDX(i) (&adc->inputs_cfg[i])
> +
> +struct stm32_dfsdm_adc {
> +	struct device *dev;
> +	struct stm32_dfsdm *dfsdm;
> +	struct list_head adc_list;
> +
> +	/* Filter */
> +	unsigned int fl_id;
> +	struct stm32_dfsdm_sinc_filter sinc;
> +	unsigned int int_oversampling;
> +
> +	/* Channels */
> +	struct stm32_dfsdm_channel *inputs;
> +	struct stm32_dfsdm_ch_cfg *inputs_cfg;
> +
> +	/* Raw mode*/
> +	struct completion completion;
> +	struct stm32_dfsdm_regular reg_params;
> +	u32 *buffer;
> +};
> +
> +static const char * const stm32_dfsdm_adc_sinc_order[] = {
> +	[0] = "FastSinc",
> +	[1] = "Sinc1",
> +	[2] = "Sinc2",
> +	[3] = "Sinc3",
> +	[4] = "Sinc4",
> +	[5] = "Sinc5",
> +};
> +
> +static inline const struct iio_chan_spec *get_ch_from_id(
> +					struct iio_dev *indio_dev, int ch_id)
> +{
> +	int i;
> +
> +	for (i = 0; i < indio_dev->num_channels; i++) {
> +		if (ch_id == indio_dev->channels[i].channel)
> +			return &indio_dev->channels[i];
> +	}
> +
> +	return NULL;
> +}
> +
> +/*
> + * Filter attributes
> + */
> +
> +static int stm32_dfsdm_adc_set_sinc(struct iio_dev *indio_dev,
> +				    const struct iio_chan_spec *chan,
> +				    unsigned int val)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +
> +	dev_dbg(&indio_dev->dev, "%s: %s\n", __func__,
> +		stm32_dfsdm_adc_sinc_order[adc->sinc.order]);
> +
> +	adc->sinc.order = val;
> +
> +	return 0;
> +}
> +
> +static int stm32_dfsdm_adc_get_sinc(struct iio_dev *indio_dev,
> +				    const struct iio_chan_spec *chan)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +
> +	dev_dbg(&indio_dev->dev, "%s: %s\n", __func__,
> +		stm32_dfsdm_adc_sinc_order[adc->sinc.order]);
> +
> +	return adc->sinc.order;
> +}
> +
> +static const struct iio_enum stm32_dfsdm_adc_fl_sinc_order = {
> +	.items = stm32_dfsdm_adc_sinc_order,
> +	.num_items = ARRAY_SIZE(stm32_dfsdm_adc_sinc_order),
> +	.get = stm32_dfsdm_adc_get_sinc,
> +	.set = stm32_dfsdm_adc_set_sinc,
> +};
> +
> +static ssize_t stm32_dfsdm_adc_get_int_os(struct iio_dev *indio_dev,
> +					  uintptr_t priv,
> +					  const struct iio_chan_spec *chan,
> +					  char *buf)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n", adc->int_oversampling);
> +}
> +
> +static ssize_t stm32_dfsdm_adc_set_int_os(struct iio_dev *indio_dev,
> +					  uintptr_t priv,
> +					  const struct iio_chan_spec *chan,
> +					  const char *buf, size_t len)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	int ret, val;
> +
> +	ret = kstrtoint(buf, 0, &val);
> +	if (ret)
> +		return ret;
> +
> +	if ((!val) || (val > DFSDM_MAX_INT_OVERSAMPLING)) {
> +		dev_err(&indio_dev->dev, "invalid oversampling (0 or > %#x)",
> +			DFSDM_MAX_INT_OVERSAMPLING);
> +		return -EINVAL;
> +	}
> +	adc->int_oversampling = val;
> +
> +	return len;
> +}
> +
> +static ssize_t stm32_dfsdm_adc_get_fl_os(struct iio_dev *indio_dev,
> +					 uintptr_t priv,
> +					 const struct iio_chan_spec *chan,
> +					 char *buf)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n", adc->sinc.oversampling);
> +}
> +
> +static ssize_t stm32_dfsdm_adc_set_fl_os(struct iio_dev *indio_dev,
> +					 uintptr_t priv,
> +					const struct iio_chan_spec *chan,
> +					const char *buf, size_t len)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	int ret, val;
> +
> +	ret = kstrtoint(buf, 0, &val);
> +	if (ret)
> +		return ret;
> +
> +	if ((!val) || (val > DFSDM_MAX_FL_OVERSAMPLING)) {
> +		dev_err(&indio_dev->dev, "invalid oversampling (0 or > %#x)",
> +			DFSDM_MAX_FL_OVERSAMPLING);
> +		return -EINVAL;
> +	}
> +	adc->sinc.oversampling = val;
> +
> +	return len;
> +}
> +
> +/*
> + * Data bit shifting attribute
> + */
> +static ssize_t stm32_dfsdm_adc_get_shift(struct iio_dev *indio_dev,
> +					 uintptr_t priv,
> +					 const struct iio_chan_spec *chan,
> +					 char *buf)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n", ch_cfg->right_bit_shift);
> +}
> +
> +static ssize_t stm32_dfsdm_adc_set_shift(struct iio_dev *indio_dev,
> +					 uintptr_t priv,
> +					 const struct iio_chan_spec *chan,
> +					 const char *buf, size_t len)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
> +	int ret, val;
> +
> +	ret = kstrtoint(buf, 0, &val);
> +	if (ret)
> +		return ret;
> +
> +	if (val > DFSDM_MAX_CH_SHIFT) {
> +		dev_err(&indio_dev->dev, "invalid shift value (> %#x)",
> +			DFSDM_MAX_CH_SHIFT);
> +		return -EINVAL;
> +	}
> +	ch_cfg->right_bit_shift = val;
> +
> +	return len;
> +}
> +
> +static const struct iio_chan_spec_ext_info stm32_dfsdm_adc_ext_info[] = {
> +	/* sinc_filter_order: Configure Sinc filter order */
> +	IIO_ENUM("sinc_filter_order", IIO_SHARED_BY_TYPE,
> +		 &stm32_dfsdm_adc_fl_sinc_order),
Documentation?  This is new ABI. Plus I'd like to see a generic description
of the filters rather than a name out of the datasheet.
Docs under Documentation/ABI/testing/sysfs-bus-iio-*

> +	IIO_ENUM_AVAILABLE("sinc_filter_order", &stm32_dfsdm_adc_fl_sinc_order),
> +	/* filter oversampling: Post filter oversampling ratio */
> +	{
> +		.name = "sinc_filter_oversampling_ratio",
> +		.shared = IIO_SHARED_BY_TYPE,
> +		.read = stm32_dfsdm_adc_get_fl_os,
> +		.write = stm32_dfsdm_adc_set_fl_os,
> +	},
> +	/* data_right_bit_shift : Filter output data shifting */
> +	{
> +		.name = "data_right_bit_shift",
Well this is a simple scaling I think?  Should be supported as such.
> +		.shared = IIO_SEPARATE,
> +		.read = stm32_dfsdm_adc_get_shift,
> +		.write = stm32_dfsdm_adc_set_shift,
> +	},
> +
> +	/*
> +	 * averaging_length : Mean windows of data from filter.
> +	 * Defines how many filter data will be summed to one data output
> +	 */
This probably corresponds to the conventional oversampling_ratio that
we already have as standard ABI (be it with some magic having occured before
hand).
> +	{
> +		.name = "integrator_oversampling",
> +		.shared = IIO_SHARED_BY_TYPE,
> +		.read = stm32_dfsdm_adc_get_int_os,
> +		.write = stm32_dfsdm_adc_set_int_os,
> +	},
> +	{},
> +};
> +
> +/*
> + * Filter event routine called under IRQ context
Again, keep to kernel comment style please.  Either do proper kernel-doc
or just simple single line comments where appropriate.
> + */
> +static void stm32_dfsdm_event_cb(struct stm32_dfsdm *dfsdm, int flt_id,
> +				 enum stm32_dfsdm_events ev, unsigned int param,
> +				 void *context)
> +{
> +	struct stm32_dfsdm_adc *adc = context;
> +	unsigned int ch_id;
> +
> +	dev_dbg(adc->dev, "%s:\n", __func__);
> +
> +	switch (ev) {
> +	case DFSDM_EVENT_REG_EOC:
> +		stm32_dfsdm_read_fl_conv(adc->dfsdm, flt_id, adc->buffer,
> +					 &ch_id, DFSDM_FILTER_REG_CONV);
> +		complete(&adc->completion);
> +		break;
> +	case DFSDM_EVENT_REG_XRUN:
> +		dev_err(adc->dev, "%s: underrun detected for filter %d\n",
> +			__func__, flt_id);
> +		break;
> +	default:
> +		dev_err(adc->dev, "%s: event %#x not implemented\n",
> +			__func__, ev);
> +		break;
> +	}
> +}
> +
> +static inline void stm32_dfsdm_adc_fl_config(struct stm32_dfsdm_adc *adc,
> +					     u32 channel_mask,
> +					     struct stm32_dfsdm_filter *filter)
> +{
> +	dev_dbg(adc->dev, "%s:\n", __func__);
> +
> +	filter->event.cb = stm32_dfsdm_event_cb;
> +	filter->event.context = adc;
> +
> +	filter->sinc_params = adc->sinc;
> +
> +	filter->int_oversampling = adc->int_oversampling;
> +}
> +
> +static int stm32_dfsdm_adc_start_raw_conv(struct stm32_dfsdm_adc *adc,
> +					  const struct iio_chan_spec *chan)
> +{
> +	struct stm32_dfsdm_filter filter;
> +	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
> +	unsigned int ch_id = CH_ID_FROM_IDX(chan->scan_index);
> +	int ret;
> +
> +	dev_dbg(adc->dev, "%s:\n", __func__);
> +
> +	memset(&filter, 0, sizeof(filter));
> +	filter.reg_params = &adc->reg_params;
> +
> +	if (!filter.reg_params)
> +		return -ENOMEM;
> +
> +	filter.reg_params->ch_src = ch_id;
> +
> +	stm32_dfsdm_adc_fl_config(adc, BIT(ch_id), &filter);
A lot of this seems to run every time.  Can we not leave some of this
stuff configured for all conversions on a given channel?
> +
> +	ret = stm32_dfsdm_configure_filter(adc->dfsdm, adc->fl_id, &filter);
> +	if (ret < 0) {
> +		dev_err(adc->dev, "Failed to configure filter\n");
> +		return ret;
> +	}
> +
> +	ret = stm32_dfsdm_start_channel(adc->dfsdm, ch_id, ch_cfg);
> +	if (ret < 0)
> +		return ret;
> +
> +	stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id, DFSDM_FILTER_REG_CONV);
> +
> +	return 0;
> +}
> +
> +static void stm32_dfsdm_adc_stop_raw_conv(struct stm32_dfsdm_adc *adc,
> +					  const struct iio_chan_spec *chan)
> +{
> +	unsigned int ch_id = CH_ID_FROM_IDX(chan->scan_index);
> +
> +	dev_dbg(adc->dev, "%s:\n", __func__);
> +
> +	stm32_dfsdm_stop_filter(adc->dfsdm, adc->fl_id);
> +	stm32_dfsdm_stop_channel(adc->dfsdm, ch_id);
> +}
> +
> +static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
> +				   const struct iio_chan_spec *chan,
> +				   u32 *result)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	long timeout;
> +	int ret;
> +
> +	dev_dbg(&indio_dev->dev, "%s:\n", __func__);
> +
> +	reinit_completion(&adc->completion);
> +
> +	ret = stm32_dfsdm_register_fl_event(adc->dfsdm, adc->fl_id,
> +					    DFSDM_EVENT_REG_EOC, 0);
> +	if (ret < 0) {
> +		dev_err(&indio_dev->dev, "Failed to register event\n");
> +		return ret;
> +	}
> +
> +	adc->buffer = result;
> +	ret = stm32_dfsdm_adc_start_raw_conv(adc, chan);
> +	if (ret) {
> +		dev_err(&indio_dev->dev, "Failed to start conversion\n");
> +		goto free_event;
> +	}
> +
> +	timeout = wait_for_completion_interruptible_timeout(&adc->completion,
> +							    DFSDM_TIMEOUT);
> +	if (timeout == 0) {
> +		dev_warn(&indio_dev->dev, "Conversion timed out!\n");
> +		ret = -ETIMEDOUT;
> +	} else if (timeout < 0) {
> +		ret = timeout;
> +	} else {
> +		dev_dbg(&indio_dev->dev, "converted val %#x\n", *result);
> +		ret = IIO_VAL_INT;
> +	}
> +
> +	stm32_dfsdm_adc_stop_raw_conv(adc, chan);
> +
> +free_event:
> +	adc->buffer = NULL;
> +	stm32_dfsdm_unregister_fl_event(adc->dfsdm, adc->fl_id,
> +					DFSDM_EVENT_REG_EOC, 0);
> +
> +	return ret;
> +}
> +
> +static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
> +				struct iio_chan_spec const *chan, int *val,
> +				int *val2, long mask)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
> +	int ret = -EINVAL;
> +
> +	dev_dbg(&indio_dev->dev, "%s channel %d\n", __func__, chan->channel);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
> +		if (!ret)
> +			ret = IIO_VAL_INT;
> +		break;
> +	case IIO_CHAN_INFO_OFFSET:
> +		*val = ch_cfg->offset;
> +		ret = IIO_VAL_INT;
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev,
> +				 struct iio_chan_spec const *chan, int val,
> +				 int val2, long mask)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
> +
> +	dev_dbg(&indio_dev->dev, "%s channel%d", __func__, chan->channel);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_OFFSET:
> +		if (val > DFSDM_MAX_CH_OFFSET) {
> +			dev_err(&indio_dev->dev, "invalid offset (> %#lx)",
> +				DFSDM_MAX_CH_OFFSET);
> +			return -EINVAL;
> +		}
> +		ch_cfg->offset = val;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct iio_info stm32_dfsdm_iio_info = {
> +	.read_raw = stm32_dfsdm_read_raw,
> +	.write_raw = stm32_dfsdm_write_raw,
> +	.driver_module = THIS_MODULE,
> +};
> +
> +static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
> +					 struct iio_chan_spec *chan,
> +					 int chan_idx)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	struct stm32_dfsdm_channel *dfsdm_ch = &adc->inputs[chan_idx];
> +	struct iio_chan_spec *ch = &chan[chan_idx];
> +	int ret;
> +	unsigned int alt_ch = 0;
> +
> +	ret = of_property_read_u32_index(indio_dev->dev.of_node,
> +					 "st,adc-channels", chan_idx,
> +					 &ch->channel);
> +	if (ret < 0) {
> +		dev_err(&indio_dev->dev,
> +			" error parsing 'st,adc-channels' for idx %d\n",
> +			chan_idx);
> +		return ret;
> +	}
> +
> +	ret = of_property_read_string_index(indio_dev->dev.of_node,
> +					    "st,adc-channel-names", chan_idx,
> +					    &ch->datasheet_name);
> +	if (ret < 0) {
> +		dev_err(&indio_dev->dev,
> +			" error parsing 'st,adc-channel-names' for idx %d\n",
> +			chan_idx);
> +		return ret;
> +	}
> +
> +	ch->extend_name = ch->datasheet_name;
This is not what extend_name is for, don't do it - it just makes it
really difficult to have standard userspace code.  With hindsight I should
probably never have introduced it in the first place.
> +	ch->type = IIO_VOLTAGE;
> +	ch->indexed = 1;
> +	ch->scan_index = chan_idx;
> +	ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +				 BIT(IIO_CHAN_INFO_OFFSET);
> +	ch->scan_type.sign = 'u';
> +	ch->scan_type.realbits = DFSDM_ADC_MAX_RESOLUTION;
> +	ch->scan_type.storagebits = DFSDM_ADC_STORAGE_BITS;
Unless these are used elsehwere, given they are 'real numbers' it would
actually be easier to read the code if you just put the numbers in here
directly.

> +	ch->scan_type.shift = 8;
> +
> +	ch->ext_info = stm32_dfsdm_adc_ext_info;
> +
> +	of_property_read_u32_index(indio_dev->dev.of_node, "st,adc-alt-channel",
> +				   chan_idx, &alt_ch);
> +	/* Select the previous channel if alternate field is defined*/
space before */  Please check throughout as patches to fix this sort of
thing are really boring  :)
> +	if (alt_ch) {
> +		if (!ch->channel)
> +			ch->channel = adc->dfsdm->max_channels;
> +		ch->channel -= 1;
> +		dfsdm_ch->serial_if.pins = DFSDM_CHANNEL_NEXT_CHANNEL_PINS;
> +	} else {
> +		dfsdm_ch->serial_if.pins = DFSDM_CHANNEL_SAME_CHANNEL_PINS;
> +	}
> +	dfsdm_ch->id = ch->channel;
> +
> +	dfsdm_ch->type.DataPacking = DFSDM_CHANNEL_STANDARD_MODE;
> +
> +	dfsdm_ch->type.source = DFSDM_CHANNEL_EXTERNAL_INPUTS;
> +	ret = of_property_read_u32_index(indio_dev->dev.of_node,
> +					 "st,adc-channel-types",
> +					 chan_idx, &dfsdm_ch->serial_if.type);
> +	if (ret < 0)
> +		dfsdm_ch->serial_if.type = DFSDM_CHANNEL_SPI_RISING;
> +
> +	ret = of_property_read_u32_index(indio_dev->dev.of_node,
> +					 "st,adc-channel-clk-src",
> +					 chan_idx,
> +					 &dfsdm_ch->serial_if.spi_clk);
> +
> +	if ((dfsdm_ch->serial_if.type == DFSDM_CHANNEL_MANCHESTER_RISING)  ||
> +	    (dfsdm_ch->serial_if.type == DFSDM_CHANNEL_MANCHESTER_FALLING) ||
> +	    (ret < 0))
> +		dfsdm_ch->serial_if.spi_clk = DFSDM_CHANNEL_SPI_CLOCK_INTERNAL;
> +
> +	return stm32_dfsdm_get_channel(adc->dfsdm, dfsdm_ch);
> +}
> +
> +static int stm32_dfsdm_adc_chan_init(struct iio_dev *indio_dev)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	unsigned int num_ch;
> +	struct iio_chan_spec *channels;
> +	int ret, chan_idx;
> +
> +	num_ch = of_property_count_strings(indio_dev->dev.of_node,
> +					   "st,adc-channel-names");
> +
> +	channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
> +				GFP_KERNEL);
> +	if (!channels)
> +		return -ENOMEM;
> +
> +	adc->inputs = devm_kcalloc(&indio_dev->dev, num_ch,
> +				   sizeof(*adc->inputs), GFP_KERNEL);
> +	if (!adc->inputs)
> +		return -ENOMEM;
> +
> +	adc->inputs_cfg = devm_kcalloc(&indio_dev->dev, num_ch,
> +				       sizeof(*adc->inputs_cfg), GFP_KERNEL);
> +	if (!adc->inputs_cfg)
> +		return -ENOMEM;
There are a lot of small allocs here. I'm not sure how many channels there
can be, but it may be more efficient (and cleaner) to just put arrays
of sufficient size to take all possible channels directly in your adc
structure.
> +
> +	for (chan_idx = 0; chan_idx < num_ch; chan_idx++) {
> +		ret = stm32_dfsdm_adc_chan_init_one(indio_dev, channels,
> +						    chan_idx);
> +		if (ret < 0)
> +			goto ch_error;
> +	}
> +
> +	indio_dev->num_channels = num_ch;
> +	indio_dev->channels = channels;
> +
> +	return 0;
> +
> +ch_error:
> +	for (chan_idx--; chan_idx >= 0; chan_idx--)
> +		stm32_dfsdm_release_channel(adc->dfsdm,
> +					    adc->inputs[chan_idx].id);
> +
> +	return ret;
> +}
> +
> +static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct stm32_dfsdm_adc *adc;
> +	struct device_node *np = pdev->dev.of_node;
> +	struct iio_dev *indio_dev;
> +	int ret, i;
> +
> +	if (!np)
> +		return -ENODEV;
> +
> +	indio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
> +	if (IS_ERR(indio_dev)) {
> +		dev_err(dev, "%s: failed to allocate iio", __func__);
> +		return PTR_ERR(indio_dev);
> +	}
> +
> +	indio_dev->name = np->name;
> +	indio_dev->dev.parent = dev;
> +	indio_dev->dev.of_node = np;
> +	indio_dev->info = &stm32_dfsdm_iio_info;
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	adc = iio_priv(indio_dev);
> +	if (IS_ERR(adc)) {
> +		dev_err(dev, "%s: failed to allocate adc", __func__);
> +		return PTR_ERR(adc);
> +	}
> +
> +	if (of_property_read_u32(np, "reg", &adc->fl_id)) {
> +		dev_err(&pdev->dev, "missing reg property\n");
> +		return -EINVAL;
> +	}
> +
> +	adc->dev = &indio_dev->dev;
> +	adc->dfsdm = dev_get_drvdata(pdev->dev.parent);
> +
> +	ret = stm32_dfsdm_adc_chan_init(indio_dev);
> +	if (ret < 0) {
> +		dev_err(dev, "iio channels init failed\n");
> +		return ret;
> +	}
> +
> +	ret = stm32_dfsdm_get_filter(adc->dfsdm, adc->fl_id);
> +	if (ret < 0)
> +		goto get_fl_err;
> +
> +	adc->int_oversampling = DFSDM_MIN_INT_OVERSAMPLING;
> +	adc->sinc.oversampling = DFSDM_MIN_FL_OVERSAMPLING;
> +
> +	init_completion(&adc->completion);
> +
> +	ret = devm_iio_device_register(dev, indio_dev);
Probe order must be reversed in remove to make it obvious there are no
weird and wonderful race conditions.  The moment you have anything in probe
that needs unwinding before a devm call that rule is broken.

Upshot, you can't use the devm version of iio_device_register.

> +	if (ret) {
> +		dev_err(adc->dev, "failed to register iio device\n");
> +		goto register_err;
> +	}
> +
> +	platform_set_drvdata(pdev, adc);
> +
> +	return 0;
> +
> +register_err:
> +	stm32_dfsdm_release_filter(adc->dfsdm, adc->fl_id);
> +
> +get_fl_err:
> +	for (i = 0; i < indio_dev->num_channels; i++)
> +		stm32_dfsdm_release_channel(adc->dfsdm, adc->inputs[i].id);
> +
> +	return ret;
> +}
> +
> +static int stm32_dfsdm_adc_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *indio_dev;
> +	struct stm32_dfsdm_adc *adc = platform_get_drvdata(pdev);
> +	int i;
> +
> +	indio_dev = iio_priv_to_dev(adc);
> +	for (i = 0; i < indio_dev->num_channels; i++)
> +		stm32_dfsdm_release_channel(adc->dfsdm, adc->inputs[i].id);
> +	stm32_dfsdm_release_filter(adc->dfsdm, adc->fl_id);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id stm32_dfsdm_adc_match[] = {
> +	{ .compatible = "st,stm32-dfsdm-adc"},
> +	{}
> +};
> +
> +static struct platform_driver stm32_dfsdm_adc_driver = {
> +	.driver = {
> +		.name = "stm32-dfsdm-adc",
> +		.of_match_table = stm32_dfsdm_adc_match,
> +	},
> +	.probe = stm32_dfsdm_adc_probe,
> +	.remove = stm32_dfsdm_adc_remove,
> +};
> +module_platform_driver(stm32_dfsdm_adc_driver);
> +
> +MODULE_DESCRIPTION("STM32 sigma delta ADC");
> +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
> +MODULE_LICENSE("GPL v2");
> 

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

* Re: [PATCH 4/7] IIO: add STM32 DFSDM ADC support
@ 2017-01-29 12:15     ` Jonathan Cameron
  0 siblings, 0 replies; 130+ messages in thread
From: Jonathan Cameron @ 2017-01-29 12:15 UTC (permalink / raw)
  To: Arnaud Pouliquen, devicetree, linux-arm-kernel, linux-iio,
	alsa-devel, Lee Jones, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre Torgue

On 23/01/17 16:32, Arnaud Pouliquen wrote:
> Add driver to handle Sigma Delta ADC conversion for ADC
> connected to DFSDM IP.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
Various comments inline.  Again mostly reading this to grasp how the whole
lot fits together.

At the moment, other than some new ABI, this is a fairly simple sysfs or in
kernel polled ADC driver.  The hardware seems to support a whole lot more
but fair enough to present this as a starting point!

Jonathan
> ---
>  drivers/iio/adc/Kconfig           |   9 +
>  drivers/iio/adc/Makefile          |   1 +
>  drivers/iio/adc/stm32-dfsdm-adc.c | 676 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 686 insertions(+)
>  create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c
> 
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index e0b3c09..4b2b886 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -441,6 +441,15 @@ config STM32_ADC
>  	  This driver can also be built as a module.  If so, the module
>  	  will be called stm32-adc.
>  
> +config STM32_DFSDM_ADC
> +	tristate "STMicroelectronics STM32 DFSDM ADC driver"
> +	depends on (ARCH_STM32 && OF && MFD_STM32_DFSDM) || COMPILE_TEST
> +	help
> +	  Say yes here to build the driver for the STMicroelectronics
> +	  STM32 analog-to-digital converter with Digital filter.
> +	  This driver can also be built as a module. If so, the module
> +	  will be called stm32_dfsdm_adc.
> +
>  config STX104
>  	tristate "Apex Embedded Systems STX104 driver"
>  	depends on X86 && ISA_BUS_API
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 8e02a94..aed42c6 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -43,6 +43,7 @@ obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
>  obj-$(CONFIG_STX104) += stx104.o
>  obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
>  obj-$(CONFIG_STM32_ADC) += stm32-adc.o
> +obj-$(CONFIG_STM32_DFSDM_ADC) += stm32-dfsdm-adc.o
>  obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
>  obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
>  obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
> diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
> new file mode 100644
> index 0000000..727d6b1
> --- /dev/null
> +++ b/drivers/iio/adc/stm32-dfsdm-adc.c
> @@ -0,0 +1,676 @@
> +/*
> + * This file is part of STM32 DFSDM ADC driver
> + *
> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
> + * Author: Arnaud Pouliquen <arnaud.pouliquen@st.com>.
> + *
> + * License type: GPLv2
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/irq_work.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#include <linux/iio/iio.h>
> +
> +#include <linux/mfd/stm32-dfsdm.h>
> +
> +#define DFSDM_ADC_MAX_RESOLUTION 24
> +#define DFSDM_ADC_STORAGE_BITS   32
> +
> +#define DFSDM_MAX_CH_OFFSET BIT(24)
> +#define DFSDM_MAX_CH_SHIFT 24
> +
> +#define DFSDM_TIMEOUT_US 100000
> +#define DFSDM_TIMEOUT (msecs_to_jiffies(DFSDM_TIMEOUT_US / 1000))
> +
> +#define CH_ID_FROM_IDX(i) (adc->inputs[i].id)
> +#define CH_CFG_FROM_IDX(i) (&adc->inputs_cfg[i])
> +
> +struct stm32_dfsdm_adc {
> +	struct device *dev;
> +	struct stm32_dfsdm *dfsdm;
> +	struct list_head adc_list;
> +
> +	/* Filter */
> +	unsigned int fl_id;
> +	struct stm32_dfsdm_sinc_filter sinc;
> +	unsigned int int_oversampling;
> +
> +	/* Channels */
> +	struct stm32_dfsdm_channel *inputs;
> +	struct stm32_dfsdm_ch_cfg *inputs_cfg;
> +
> +	/* Raw mode*/
> +	struct completion completion;
> +	struct stm32_dfsdm_regular reg_params;
> +	u32 *buffer;
> +};
> +
> +static const char * const stm32_dfsdm_adc_sinc_order[] = {
> +	[0] = "FastSinc",
> +	[1] = "Sinc1",
> +	[2] = "Sinc2",
> +	[3] = "Sinc3",
> +	[4] = "Sinc4",
> +	[5] = "Sinc5",
> +};
> +
> +static inline const struct iio_chan_spec *get_ch_from_id(
> +					struct iio_dev *indio_dev, int ch_id)
> +{
> +	int i;
> +
> +	for (i = 0; i < indio_dev->num_channels; i++) {
> +		if (ch_id == indio_dev->channels[i].channel)
> +			return &indio_dev->channels[i];
> +	}
> +
> +	return NULL;
> +}
> +
> +/*
> + * Filter attributes
> + */
> +
> +static int stm32_dfsdm_adc_set_sinc(struct iio_dev *indio_dev,
> +				    const struct iio_chan_spec *chan,
> +				    unsigned int val)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +
> +	dev_dbg(&indio_dev->dev, "%s: %s\n", __func__,
> +		stm32_dfsdm_adc_sinc_order[adc->sinc.order]);
> +
> +	adc->sinc.order = val;
> +
> +	return 0;
> +}
> +
> +static int stm32_dfsdm_adc_get_sinc(struct iio_dev *indio_dev,
> +				    const struct iio_chan_spec *chan)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +
> +	dev_dbg(&indio_dev->dev, "%s: %s\n", __func__,
> +		stm32_dfsdm_adc_sinc_order[adc->sinc.order]);
> +
> +	return adc->sinc.order;
> +}
> +
> +static const struct iio_enum stm32_dfsdm_adc_fl_sinc_order = {
> +	.items = stm32_dfsdm_adc_sinc_order,
> +	.num_items = ARRAY_SIZE(stm32_dfsdm_adc_sinc_order),
> +	.get = stm32_dfsdm_adc_get_sinc,
> +	.set = stm32_dfsdm_adc_set_sinc,
> +};
> +
> +static ssize_t stm32_dfsdm_adc_get_int_os(struct iio_dev *indio_dev,
> +					  uintptr_t priv,
> +					  const struct iio_chan_spec *chan,
> +					  char *buf)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n", adc->int_oversampling);
> +}
> +
> +static ssize_t stm32_dfsdm_adc_set_int_os(struct iio_dev *indio_dev,
> +					  uintptr_t priv,
> +					  const struct iio_chan_spec *chan,
> +					  const char *buf, size_t len)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	int ret, val;
> +
> +	ret = kstrtoint(buf, 0, &val);
> +	if (ret)
> +		return ret;
> +
> +	if ((!val) || (val > DFSDM_MAX_INT_OVERSAMPLING)) {
> +		dev_err(&indio_dev->dev, "invalid oversampling (0 or > %#x)",
> +			DFSDM_MAX_INT_OVERSAMPLING);
> +		return -EINVAL;
> +	}
> +	adc->int_oversampling = val;
> +
> +	return len;
> +}
> +
> +static ssize_t stm32_dfsdm_adc_get_fl_os(struct iio_dev *indio_dev,
> +					 uintptr_t priv,
> +					 const struct iio_chan_spec *chan,
> +					 char *buf)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n", adc->sinc.oversampling);
> +}
> +
> +static ssize_t stm32_dfsdm_adc_set_fl_os(struct iio_dev *indio_dev,
> +					 uintptr_t priv,
> +					const struct iio_chan_spec *chan,
> +					const char *buf, size_t len)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	int ret, val;
> +
> +	ret = kstrtoint(buf, 0, &val);
> +	if (ret)
> +		return ret;
> +
> +	if ((!val) || (val > DFSDM_MAX_FL_OVERSAMPLING)) {
> +		dev_err(&indio_dev->dev, "invalid oversampling (0 or > %#x)",
> +			DFSDM_MAX_FL_OVERSAMPLING);
> +		return -EINVAL;
> +	}
> +	adc->sinc.oversampling = val;
> +
> +	return len;
> +}
> +
> +/*
> + * Data bit shifting attribute
> + */
> +static ssize_t stm32_dfsdm_adc_get_shift(struct iio_dev *indio_dev,
> +					 uintptr_t priv,
> +					 const struct iio_chan_spec *chan,
> +					 char *buf)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n", ch_cfg->right_bit_shift);
> +}
> +
> +static ssize_t stm32_dfsdm_adc_set_shift(struct iio_dev *indio_dev,
> +					 uintptr_t priv,
> +					 const struct iio_chan_spec *chan,
> +					 const char *buf, size_t len)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
> +	int ret, val;
> +
> +	ret = kstrtoint(buf, 0, &val);
> +	if (ret)
> +		return ret;
> +
> +	if (val > DFSDM_MAX_CH_SHIFT) {
> +		dev_err(&indio_dev->dev, "invalid shift value (> %#x)",
> +			DFSDM_MAX_CH_SHIFT);
> +		return -EINVAL;
> +	}
> +	ch_cfg->right_bit_shift = val;
> +
> +	return len;
> +}
> +
> +static const struct iio_chan_spec_ext_info stm32_dfsdm_adc_ext_info[] = {
> +	/* sinc_filter_order: Configure Sinc filter order */
> +	IIO_ENUM("sinc_filter_order", IIO_SHARED_BY_TYPE,
> +		 &stm32_dfsdm_adc_fl_sinc_order),
Documentation?  This is new ABI. Plus I'd like to see a generic description
of the filters rather than a name out of the datasheet.
Docs under Documentation/ABI/testing/sysfs-bus-iio-*

> +	IIO_ENUM_AVAILABLE("sinc_filter_order", &stm32_dfsdm_adc_fl_sinc_order),
> +	/* filter oversampling: Post filter oversampling ratio */
> +	{
> +		.name = "sinc_filter_oversampling_ratio",
> +		.shared = IIO_SHARED_BY_TYPE,
> +		.read = stm32_dfsdm_adc_get_fl_os,
> +		.write = stm32_dfsdm_adc_set_fl_os,
> +	},
> +	/* data_right_bit_shift : Filter output data shifting */
> +	{
> +		.name = "data_right_bit_shift",
Well this is a simple scaling I think?  Should be supported as such.
> +		.shared = IIO_SEPARATE,
> +		.read = stm32_dfsdm_adc_get_shift,
> +		.write = stm32_dfsdm_adc_set_shift,
> +	},
> +
> +	/*
> +	 * averaging_length : Mean windows of data from filter.
> +	 * Defines how many filter data will be summed to one data output
> +	 */
This probably corresponds to the conventional oversampling_ratio that
we already have as standard ABI (be it with some magic having occured before
hand).
> +	{
> +		.name = "integrator_oversampling",
> +		.shared = IIO_SHARED_BY_TYPE,
> +		.read = stm32_dfsdm_adc_get_int_os,
> +		.write = stm32_dfsdm_adc_set_int_os,
> +	},
> +	{},
> +};
> +
> +/*
> + * Filter event routine called under IRQ context
Again, keep to kernel comment style please.  Either do proper kernel-doc
or just simple single line comments where appropriate.
> + */
> +static void stm32_dfsdm_event_cb(struct stm32_dfsdm *dfsdm, int flt_id,
> +				 enum stm32_dfsdm_events ev, unsigned int param,
> +				 void *context)
> +{
> +	struct stm32_dfsdm_adc *adc = context;
> +	unsigned int ch_id;
> +
> +	dev_dbg(adc->dev, "%s:\n", __func__);
> +
> +	switch (ev) {
> +	case DFSDM_EVENT_REG_EOC:
> +		stm32_dfsdm_read_fl_conv(adc->dfsdm, flt_id, adc->buffer,
> +					 &ch_id, DFSDM_FILTER_REG_CONV);
> +		complete(&adc->completion);
> +		break;
> +	case DFSDM_EVENT_REG_XRUN:
> +		dev_err(adc->dev, "%s: underrun detected for filter %d\n",
> +			__func__, flt_id);
> +		break;
> +	default:
> +		dev_err(adc->dev, "%s: event %#x not implemented\n",
> +			__func__, ev);
> +		break;
> +	}
> +}
> +
> +static inline void stm32_dfsdm_adc_fl_config(struct stm32_dfsdm_adc *adc,
> +					     u32 channel_mask,
> +					     struct stm32_dfsdm_filter *filter)
> +{
> +	dev_dbg(adc->dev, "%s:\n", __func__);
> +
> +	filter->event.cb = stm32_dfsdm_event_cb;
> +	filter->event.context = adc;
> +
> +	filter->sinc_params = adc->sinc;
> +
> +	filter->int_oversampling = adc->int_oversampling;
> +}
> +
> +static int stm32_dfsdm_adc_start_raw_conv(struct stm32_dfsdm_adc *adc,
> +					  const struct iio_chan_spec *chan)
> +{
> +	struct stm32_dfsdm_filter filter;
> +	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
> +	unsigned int ch_id = CH_ID_FROM_IDX(chan->scan_index);
> +	int ret;
> +
> +	dev_dbg(adc->dev, "%s:\n", __func__);
> +
> +	memset(&filter, 0, sizeof(filter));
> +	filter.reg_params = &adc->reg_params;
> +
> +	if (!filter.reg_params)
> +		return -ENOMEM;
> +
> +	filter.reg_params->ch_src = ch_id;
> +
> +	stm32_dfsdm_adc_fl_config(adc, BIT(ch_id), &filter);
A lot of this seems to run every time.  Can we not leave some of this
stuff configured for all conversions on a given channel?
> +
> +	ret = stm32_dfsdm_configure_filter(adc->dfsdm, adc->fl_id, &filter);
> +	if (ret < 0) {
> +		dev_err(adc->dev, "Failed to configure filter\n");
> +		return ret;
> +	}
> +
> +	ret = stm32_dfsdm_start_channel(adc->dfsdm, ch_id, ch_cfg);
> +	if (ret < 0)
> +		return ret;
> +
> +	stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id, DFSDM_FILTER_REG_CONV);
> +
> +	return 0;
> +}
> +
> +static void stm32_dfsdm_adc_stop_raw_conv(struct stm32_dfsdm_adc *adc,
> +					  const struct iio_chan_spec *chan)
> +{
> +	unsigned int ch_id = CH_ID_FROM_IDX(chan->scan_index);
> +
> +	dev_dbg(adc->dev, "%s:\n", __func__);
> +
> +	stm32_dfsdm_stop_filter(adc->dfsdm, adc->fl_id);
> +	stm32_dfsdm_stop_channel(adc->dfsdm, ch_id);
> +}
> +
> +static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
> +				   const struct iio_chan_spec *chan,
> +				   u32 *result)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	long timeout;
> +	int ret;
> +
> +	dev_dbg(&indio_dev->dev, "%s:\n", __func__);
> +
> +	reinit_completion(&adc->completion);
> +
> +	ret = stm32_dfsdm_register_fl_event(adc->dfsdm, adc->fl_id,
> +					    DFSDM_EVENT_REG_EOC, 0);
> +	if (ret < 0) {
> +		dev_err(&indio_dev->dev, "Failed to register event\n");
> +		return ret;
> +	}
> +
> +	adc->buffer = result;
> +	ret = stm32_dfsdm_adc_start_raw_conv(adc, chan);
> +	if (ret) {
> +		dev_err(&indio_dev->dev, "Failed to start conversion\n");
> +		goto free_event;
> +	}
> +
> +	timeout = wait_for_completion_interruptible_timeout(&adc->completion,
> +							    DFSDM_TIMEOUT);
> +	if (timeout == 0) {
> +		dev_warn(&indio_dev->dev, "Conversion timed out!\n");
> +		ret = -ETIMEDOUT;
> +	} else if (timeout < 0) {
> +		ret = timeout;
> +	} else {
> +		dev_dbg(&indio_dev->dev, "converted val %#x\n", *result);
> +		ret = IIO_VAL_INT;
> +	}
> +
> +	stm32_dfsdm_adc_stop_raw_conv(adc, chan);
> +
> +free_event:
> +	adc->buffer = NULL;
> +	stm32_dfsdm_unregister_fl_event(adc->dfsdm, adc->fl_id,
> +					DFSDM_EVENT_REG_EOC, 0);
> +
> +	return ret;
> +}
> +
> +static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
> +				struct iio_chan_spec const *chan, int *val,
> +				int *val2, long mask)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
> +	int ret = -EINVAL;
> +
> +	dev_dbg(&indio_dev->dev, "%s channel %d\n", __func__, chan->channel);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
> +		if (!ret)
> +			ret = IIO_VAL_INT;
> +		break;
> +	case IIO_CHAN_INFO_OFFSET:
> +		*val = ch_cfg->offset;
> +		ret = IIO_VAL_INT;
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev,
> +				 struct iio_chan_spec const *chan, int val,
> +				 int val2, long mask)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
> +
> +	dev_dbg(&indio_dev->dev, "%s channel%d", __func__, chan->channel);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_OFFSET:
> +		if (val > DFSDM_MAX_CH_OFFSET) {
> +			dev_err(&indio_dev->dev, "invalid offset (> %#lx)",
> +				DFSDM_MAX_CH_OFFSET);
> +			return -EINVAL;
> +		}
> +		ch_cfg->offset = val;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct iio_info stm32_dfsdm_iio_info = {
> +	.read_raw = stm32_dfsdm_read_raw,
> +	.write_raw = stm32_dfsdm_write_raw,
> +	.driver_module = THIS_MODULE,
> +};
> +
> +static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
> +					 struct iio_chan_spec *chan,
> +					 int chan_idx)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	struct stm32_dfsdm_channel *dfsdm_ch = &adc->inputs[chan_idx];
> +	struct iio_chan_spec *ch = &chan[chan_idx];
> +	int ret;
> +	unsigned int alt_ch = 0;
> +
> +	ret = of_property_read_u32_index(indio_dev->dev.of_node,
> +					 "st,adc-channels", chan_idx,
> +					 &ch->channel);
> +	if (ret < 0) {
> +		dev_err(&indio_dev->dev,
> +			" error parsing 'st,adc-channels' for idx %d\n",
> +			chan_idx);
> +		return ret;
> +	}
> +
> +	ret = of_property_read_string_index(indio_dev->dev.of_node,
> +					    "st,adc-channel-names", chan_idx,
> +					    &ch->datasheet_name);
> +	if (ret < 0) {
> +		dev_err(&indio_dev->dev,
> +			" error parsing 'st,adc-channel-names' for idx %d\n",
> +			chan_idx);
> +		return ret;
> +	}
> +
> +	ch->extend_name = ch->datasheet_name;
This is not what extend_name is for, don't do it - it just makes it
really difficult to have standard userspace code.  With hindsight I should
probably never have introduced it in the first place.
> +	ch->type = IIO_VOLTAGE;
> +	ch->indexed = 1;
> +	ch->scan_index = chan_idx;
> +	ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +				 BIT(IIO_CHAN_INFO_OFFSET);
> +	ch->scan_type.sign = 'u';
> +	ch->scan_type.realbits = DFSDM_ADC_MAX_RESOLUTION;
> +	ch->scan_type.storagebits = DFSDM_ADC_STORAGE_BITS;
Unless these are used elsehwere, given they are 'real numbers' it would
actually be easier to read the code if you just put the numbers in here
directly.

> +	ch->scan_type.shift = 8;
> +
> +	ch->ext_info = stm32_dfsdm_adc_ext_info;
> +
> +	of_property_read_u32_index(indio_dev->dev.of_node, "st,adc-alt-channel",
> +				   chan_idx, &alt_ch);
> +	/* Select the previous channel if alternate field is defined*/
space before */  Please check throughout as patches to fix this sort of
thing are really boring  :)
> +	if (alt_ch) {
> +		if (!ch->channel)
> +			ch->channel = adc->dfsdm->max_channels;
> +		ch->channel -= 1;
> +		dfsdm_ch->serial_if.pins = DFSDM_CHANNEL_NEXT_CHANNEL_PINS;
> +	} else {
> +		dfsdm_ch->serial_if.pins = DFSDM_CHANNEL_SAME_CHANNEL_PINS;
> +	}
> +	dfsdm_ch->id = ch->channel;
> +
> +	dfsdm_ch->type.DataPacking = DFSDM_CHANNEL_STANDARD_MODE;
> +
> +	dfsdm_ch->type.source = DFSDM_CHANNEL_EXTERNAL_INPUTS;
> +	ret = of_property_read_u32_index(indio_dev->dev.of_node,
> +					 "st,adc-channel-types",
> +					 chan_idx, &dfsdm_ch->serial_if.type);
> +	if (ret < 0)
> +		dfsdm_ch->serial_if.type = DFSDM_CHANNEL_SPI_RISING;
> +
> +	ret = of_property_read_u32_index(indio_dev->dev.of_node,
> +					 "st,adc-channel-clk-src",
> +					 chan_idx,
> +					 &dfsdm_ch->serial_if.spi_clk);
> +
> +	if ((dfsdm_ch->serial_if.type == DFSDM_CHANNEL_MANCHESTER_RISING)  ||
> +	    (dfsdm_ch->serial_if.type == DFSDM_CHANNEL_MANCHESTER_FALLING) ||
> +	    (ret < 0))
> +		dfsdm_ch->serial_if.spi_clk = DFSDM_CHANNEL_SPI_CLOCK_INTERNAL;
> +
> +	return stm32_dfsdm_get_channel(adc->dfsdm, dfsdm_ch);
> +}
> +
> +static int stm32_dfsdm_adc_chan_init(struct iio_dev *indio_dev)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	unsigned int num_ch;
> +	struct iio_chan_spec *channels;
> +	int ret, chan_idx;
> +
> +	num_ch = of_property_count_strings(indio_dev->dev.of_node,
> +					   "st,adc-channel-names");
> +
> +	channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
> +				GFP_KERNEL);
> +	if (!channels)
> +		return -ENOMEM;
> +
> +	adc->inputs = devm_kcalloc(&indio_dev->dev, num_ch,
> +				   sizeof(*adc->inputs), GFP_KERNEL);
> +	if (!adc->inputs)
> +		return -ENOMEM;
> +
> +	adc->inputs_cfg = devm_kcalloc(&indio_dev->dev, num_ch,
> +				       sizeof(*adc->inputs_cfg), GFP_KERNEL);
> +	if (!adc->inputs_cfg)
> +		return -ENOMEM;
There are a lot of small allocs here. I'm not sure how many channels there
can be, but it may be more efficient (and cleaner) to just put arrays
of sufficient size to take all possible channels directly in your adc
structure.
> +
> +	for (chan_idx = 0; chan_idx < num_ch; chan_idx++) {
> +		ret = stm32_dfsdm_adc_chan_init_one(indio_dev, channels,
> +						    chan_idx);
> +		if (ret < 0)
> +			goto ch_error;
> +	}
> +
> +	indio_dev->num_channels = num_ch;
> +	indio_dev->channels = channels;
> +
> +	return 0;
> +
> +ch_error:
> +	for (chan_idx--; chan_idx >= 0; chan_idx--)
> +		stm32_dfsdm_release_channel(adc->dfsdm,
> +					    adc->inputs[chan_idx].id);
> +
> +	return ret;
> +}
> +
> +static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct stm32_dfsdm_adc *adc;
> +	struct device_node *np = pdev->dev.of_node;
> +	struct iio_dev *indio_dev;
> +	int ret, i;
> +
> +	if (!np)
> +		return -ENODEV;
> +
> +	indio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
> +	if (IS_ERR(indio_dev)) {
> +		dev_err(dev, "%s: failed to allocate iio", __func__);
> +		return PTR_ERR(indio_dev);
> +	}
> +
> +	indio_dev->name = np->name;
> +	indio_dev->dev.parent = dev;
> +	indio_dev->dev.of_node = np;
> +	indio_dev->info = &stm32_dfsdm_iio_info;
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	adc = iio_priv(indio_dev);
> +	if (IS_ERR(adc)) {
> +		dev_err(dev, "%s: failed to allocate adc", __func__);
> +		return PTR_ERR(adc);
> +	}
> +
> +	if (of_property_read_u32(np, "reg", &adc->fl_id)) {
> +		dev_err(&pdev->dev, "missing reg property\n");
> +		return -EINVAL;
> +	}
> +
> +	adc->dev = &indio_dev->dev;
> +	adc->dfsdm = dev_get_drvdata(pdev->dev.parent);
> +
> +	ret = stm32_dfsdm_adc_chan_init(indio_dev);
> +	if (ret < 0) {
> +		dev_err(dev, "iio channels init failed\n");
> +		return ret;
> +	}
> +
> +	ret = stm32_dfsdm_get_filter(adc->dfsdm, adc->fl_id);
> +	if (ret < 0)
> +		goto get_fl_err;
> +
> +	adc->int_oversampling = DFSDM_MIN_INT_OVERSAMPLING;
> +	adc->sinc.oversampling = DFSDM_MIN_FL_OVERSAMPLING;
> +
> +	init_completion(&adc->completion);
> +
> +	ret = devm_iio_device_register(dev, indio_dev);
Probe order must be reversed in remove to make it obvious there are no
weird and wonderful race conditions.  The moment you have anything in probe
that needs unwinding before a devm call that rule is broken.

Upshot, you can't use the devm version of iio_device_register.

> +	if (ret) {
> +		dev_err(adc->dev, "failed to register iio device\n");
> +		goto register_err;
> +	}
> +
> +	platform_set_drvdata(pdev, adc);
> +
> +	return 0;
> +
> +register_err:
> +	stm32_dfsdm_release_filter(adc->dfsdm, adc->fl_id);
> +
> +get_fl_err:
> +	for (i = 0; i < indio_dev->num_channels; i++)
> +		stm32_dfsdm_release_channel(adc->dfsdm, adc->inputs[i].id);
> +
> +	return ret;
> +}
> +
> +static int stm32_dfsdm_adc_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *indio_dev;
> +	struct stm32_dfsdm_adc *adc = platform_get_drvdata(pdev);
> +	int i;
> +
> +	indio_dev = iio_priv_to_dev(adc);
> +	for (i = 0; i < indio_dev->num_channels; i++)
> +		stm32_dfsdm_release_channel(adc->dfsdm, adc->inputs[i].id);
> +	stm32_dfsdm_release_filter(adc->dfsdm, adc->fl_id);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id stm32_dfsdm_adc_match[] = {
> +	{ .compatible = "st,stm32-dfsdm-adc"},
> +	{}
> +};
> +
> +static struct platform_driver stm32_dfsdm_adc_driver = {
> +	.driver = {
> +		.name = "stm32-dfsdm-adc",
> +		.of_match_table = stm32_dfsdm_adc_match,
> +	},
> +	.probe = stm32_dfsdm_adc_probe,
> +	.remove = stm32_dfsdm_adc_remove,
> +};
> +module_platform_driver(stm32_dfsdm_adc_driver);
> +
> +MODULE_DESCRIPTION("STM32 sigma delta ADC");
> +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
> +MODULE_LICENSE("GPL v2");
> 


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

* [PATCH 4/7] IIO: add STM32 DFSDM ADC support
@ 2017-01-29 12:15     ` Jonathan Cameron
  0 siblings, 0 replies; 130+ messages in thread
From: Jonathan Cameron @ 2017-01-29 12:15 UTC (permalink / raw)
  To: linux-arm-kernel

On 23/01/17 16:32, Arnaud Pouliquen wrote:
> Add driver to handle Sigma Delta ADC conversion for ADC
> connected to DFSDM IP.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
Various comments inline.  Again mostly reading this to grasp how the whole
lot fits together.

At the moment, other than some new ABI, this is a fairly simple sysfs or in
kernel polled ADC driver.  The hardware seems to support a whole lot more
but fair enough to present this as a starting point!

Jonathan
> ---
>  drivers/iio/adc/Kconfig           |   9 +
>  drivers/iio/adc/Makefile          |   1 +
>  drivers/iio/adc/stm32-dfsdm-adc.c | 676 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 686 insertions(+)
>  create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c
> 
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index e0b3c09..4b2b886 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -441,6 +441,15 @@ config STM32_ADC
>  	  This driver can also be built as a module.  If so, the module
>  	  will be called stm32-adc.
>  
> +config STM32_DFSDM_ADC
> +	tristate "STMicroelectronics STM32 DFSDM ADC driver"
> +	depends on (ARCH_STM32 && OF && MFD_STM32_DFSDM) || COMPILE_TEST
> +	help
> +	  Say yes here to build the driver for the STMicroelectronics
> +	  STM32 analog-to-digital converter with Digital filter.
> +	  This driver can also be built as a module. If so, the module
> +	  will be called stm32_dfsdm_adc.
> +
>  config STX104
>  	tristate "Apex Embedded Systems STX104 driver"
>  	depends on X86 && ISA_BUS_API
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 8e02a94..aed42c6 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -43,6 +43,7 @@ obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
>  obj-$(CONFIG_STX104) += stx104.o
>  obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
>  obj-$(CONFIG_STM32_ADC) += stm32-adc.o
> +obj-$(CONFIG_STM32_DFSDM_ADC) += stm32-dfsdm-adc.o
>  obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
>  obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
>  obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
> diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
> new file mode 100644
> index 0000000..727d6b1
> --- /dev/null
> +++ b/drivers/iio/adc/stm32-dfsdm-adc.c
> @@ -0,0 +1,676 @@
> +/*
> + * This file is part of STM32 DFSDM ADC driver
> + *
> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
> + * Author: Arnaud Pouliquen <arnaud.pouliquen@st.com>.
> + *
> + * License type: GPLv2
> + *
> + * 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.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/irq_work.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#include <linux/iio/iio.h>
> +
> +#include <linux/mfd/stm32-dfsdm.h>
> +
> +#define DFSDM_ADC_MAX_RESOLUTION 24
> +#define DFSDM_ADC_STORAGE_BITS   32
> +
> +#define DFSDM_MAX_CH_OFFSET BIT(24)
> +#define DFSDM_MAX_CH_SHIFT 24
> +
> +#define DFSDM_TIMEOUT_US 100000
> +#define DFSDM_TIMEOUT (msecs_to_jiffies(DFSDM_TIMEOUT_US / 1000))
> +
> +#define CH_ID_FROM_IDX(i) (adc->inputs[i].id)
> +#define CH_CFG_FROM_IDX(i) (&adc->inputs_cfg[i])
> +
> +struct stm32_dfsdm_adc {
> +	struct device *dev;
> +	struct stm32_dfsdm *dfsdm;
> +	struct list_head adc_list;
> +
> +	/* Filter */
> +	unsigned int fl_id;
> +	struct stm32_dfsdm_sinc_filter sinc;
> +	unsigned int int_oversampling;
> +
> +	/* Channels */
> +	struct stm32_dfsdm_channel *inputs;
> +	struct stm32_dfsdm_ch_cfg *inputs_cfg;
> +
> +	/* Raw mode*/
> +	struct completion completion;
> +	struct stm32_dfsdm_regular reg_params;
> +	u32 *buffer;
> +};
> +
> +static const char * const stm32_dfsdm_adc_sinc_order[] = {
> +	[0] = "FastSinc",
> +	[1] = "Sinc1",
> +	[2] = "Sinc2",
> +	[3] = "Sinc3",
> +	[4] = "Sinc4",
> +	[5] = "Sinc5",
> +};
> +
> +static inline const struct iio_chan_spec *get_ch_from_id(
> +					struct iio_dev *indio_dev, int ch_id)
> +{
> +	int i;
> +
> +	for (i = 0; i < indio_dev->num_channels; i++) {
> +		if (ch_id == indio_dev->channels[i].channel)
> +			return &indio_dev->channels[i];
> +	}
> +
> +	return NULL;
> +}
> +
> +/*
> + * Filter attributes
> + */
> +
> +static int stm32_dfsdm_adc_set_sinc(struct iio_dev *indio_dev,
> +				    const struct iio_chan_spec *chan,
> +				    unsigned int val)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +
> +	dev_dbg(&indio_dev->dev, "%s: %s\n", __func__,
> +		stm32_dfsdm_adc_sinc_order[adc->sinc.order]);
> +
> +	adc->sinc.order = val;
> +
> +	return 0;
> +}
> +
> +static int stm32_dfsdm_adc_get_sinc(struct iio_dev *indio_dev,
> +				    const struct iio_chan_spec *chan)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +
> +	dev_dbg(&indio_dev->dev, "%s: %s\n", __func__,
> +		stm32_dfsdm_adc_sinc_order[adc->sinc.order]);
> +
> +	return adc->sinc.order;
> +}
> +
> +static const struct iio_enum stm32_dfsdm_adc_fl_sinc_order = {
> +	.items = stm32_dfsdm_adc_sinc_order,
> +	.num_items = ARRAY_SIZE(stm32_dfsdm_adc_sinc_order),
> +	.get = stm32_dfsdm_adc_get_sinc,
> +	.set = stm32_dfsdm_adc_set_sinc,
> +};
> +
> +static ssize_t stm32_dfsdm_adc_get_int_os(struct iio_dev *indio_dev,
> +					  uintptr_t priv,
> +					  const struct iio_chan_spec *chan,
> +					  char *buf)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n", adc->int_oversampling);
> +}
> +
> +static ssize_t stm32_dfsdm_adc_set_int_os(struct iio_dev *indio_dev,
> +					  uintptr_t priv,
> +					  const struct iio_chan_spec *chan,
> +					  const char *buf, size_t len)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	int ret, val;
> +
> +	ret = kstrtoint(buf, 0, &val);
> +	if (ret)
> +		return ret;
> +
> +	if ((!val) || (val > DFSDM_MAX_INT_OVERSAMPLING)) {
> +		dev_err(&indio_dev->dev, "invalid oversampling (0 or > %#x)",
> +			DFSDM_MAX_INT_OVERSAMPLING);
> +		return -EINVAL;
> +	}
> +	adc->int_oversampling = val;
> +
> +	return len;
> +}
> +
> +static ssize_t stm32_dfsdm_adc_get_fl_os(struct iio_dev *indio_dev,
> +					 uintptr_t priv,
> +					 const struct iio_chan_spec *chan,
> +					 char *buf)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n", adc->sinc.oversampling);
> +}
> +
> +static ssize_t stm32_dfsdm_adc_set_fl_os(struct iio_dev *indio_dev,
> +					 uintptr_t priv,
> +					const struct iio_chan_spec *chan,
> +					const char *buf, size_t len)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	int ret, val;
> +
> +	ret = kstrtoint(buf, 0, &val);
> +	if (ret)
> +		return ret;
> +
> +	if ((!val) || (val > DFSDM_MAX_FL_OVERSAMPLING)) {
> +		dev_err(&indio_dev->dev, "invalid oversampling (0 or > %#x)",
> +			DFSDM_MAX_FL_OVERSAMPLING);
> +		return -EINVAL;
> +	}
> +	adc->sinc.oversampling = val;
> +
> +	return len;
> +}
> +
> +/*
> + * Data bit shifting attribute
> + */
> +static ssize_t stm32_dfsdm_adc_get_shift(struct iio_dev *indio_dev,
> +					 uintptr_t priv,
> +					 const struct iio_chan_spec *chan,
> +					 char *buf)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n", ch_cfg->right_bit_shift);
> +}
> +
> +static ssize_t stm32_dfsdm_adc_set_shift(struct iio_dev *indio_dev,
> +					 uintptr_t priv,
> +					 const struct iio_chan_spec *chan,
> +					 const char *buf, size_t len)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
> +	int ret, val;
> +
> +	ret = kstrtoint(buf, 0, &val);
> +	if (ret)
> +		return ret;
> +
> +	if (val > DFSDM_MAX_CH_SHIFT) {
> +		dev_err(&indio_dev->dev, "invalid shift value (> %#x)",
> +			DFSDM_MAX_CH_SHIFT);
> +		return -EINVAL;
> +	}
> +	ch_cfg->right_bit_shift = val;
> +
> +	return len;
> +}
> +
> +static const struct iio_chan_spec_ext_info stm32_dfsdm_adc_ext_info[] = {
> +	/* sinc_filter_order: Configure Sinc filter order */
> +	IIO_ENUM("sinc_filter_order", IIO_SHARED_BY_TYPE,
> +		 &stm32_dfsdm_adc_fl_sinc_order),
Documentation?  This is new ABI. Plus I'd like to see a generic description
of the filters rather than a name out of the datasheet.
Docs under Documentation/ABI/testing/sysfs-bus-iio-*

> +	IIO_ENUM_AVAILABLE("sinc_filter_order", &stm32_dfsdm_adc_fl_sinc_order),
> +	/* filter oversampling: Post filter oversampling ratio */
> +	{
> +		.name = "sinc_filter_oversampling_ratio",
> +		.shared = IIO_SHARED_BY_TYPE,
> +		.read = stm32_dfsdm_adc_get_fl_os,
> +		.write = stm32_dfsdm_adc_set_fl_os,
> +	},
> +	/* data_right_bit_shift : Filter output data shifting */
> +	{
> +		.name = "data_right_bit_shift",
Well this is a simple scaling I think?  Should be supported as such.
> +		.shared = IIO_SEPARATE,
> +		.read = stm32_dfsdm_adc_get_shift,
> +		.write = stm32_dfsdm_adc_set_shift,
> +	},
> +
> +	/*
> +	 * averaging_length : Mean windows of data from filter.
> +	 * Defines how many filter data will be summed to one data output
> +	 */
This probably corresponds to the conventional oversampling_ratio that
we already have as standard ABI (be it with some magic having occured before
hand).
> +	{
> +		.name = "integrator_oversampling",
> +		.shared = IIO_SHARED_BY_TYPE,
> +		.read = stm32_dfsdm_adc_get_int_os,
> +		.write = stm32_dfsdm_adc_set_int_os,
> +	},
> +	{},
> +};
> +
> +/*
> + * Filter event routine called under IRQ context
Again, keep to kernel comment style please.  Either do proper kernel-doc
or just simple single line comments where appropriate.
> + */
> +static void stm32_dfsdm_event_cb(struct stm32_dfsdm *dfsdm, int flt_id,
> +				 enum stm32_dfsdm_events ev, unsigned int param,
> +				 void *context)
> +{
> +	struct stm32_dfsdm_adc *adc = context;
> +	unsigned int ch_id;
> +
> +	dev_dbg(adc->dev, "%s:\n", __func__);
> +
> +	switch (ev) {
> +	case DFSDM_EVENT_REG_EOC:
> +		stm32_dfsdm_read_fl_conv(adc->dfsdm, flt_id, adc->buffer,
> +					 &ch_id, DFSDM_FILTER_REG_CONV);
> +		complete(&adc->completion);
> +		break;
> +	case DFSDM_EVENT_REG_XRUN:
> +		dev_err(adc->dev, "%s: underrun detected for filter %d\n",
> +			__func__, flt_id);
> +		break;
> +	default:
> +		dev_err(adc->dev, "%s: event %#x not implemented\n",
> +			__func__, ev);
> +		break;
> +	}
> +}
> +
> +static inline void stm32_dfsdm_adc_fl_config(struct stm32_dfsdm_adc *adc,
> +					     u32 channel_mask,
> +					     struct stm32_dfsdm_filter *filter)
> +{
> +	dev_dbg(adc->dev, "%s:\n", __func__);
> +
> +	filter->event.cb = stm32_dfsdm_event_cb;
> +	filter->event.context = adc;
> +
> +	filter->sinc_params = adc->sinc;
> +
> +	filter->int_oversampling = adc->int_oversampling;
> +}
> +
> +static int stm32_dfsdm_adc_start_raw_conv(struct stm32_dfsdm_adc *adc,
> +					  const struct iio_chan_spec *chan)
> +{
> +	struct stm32_dfsdm_filter filter;
> +	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
> +	unsigned int ch_id = CH_ID_FROM_IDX(chan->scan_index);
> +	int ret;
> +
> +	dev_dbg(adc->dev, "%s:\n", __func__);
> +
> +	memset(&filter, 0, sizeof(filter));
> +	filter.reg_params = &adc->reg_params;
> +
> +	if (!filter.reg_params)
> +		return -ENOMEM;
> +
> +	filter.reg_params->ch_src = ch_id;
> +
> +	stm32_dfsdm_adc_fl_config(adc, BIT(ch_id), &filter);
A lot of this seems to run every time.  Can we not leave some of this
stuff configured for all conversions on a given channel?
> +
> +	ret = stm32_dfsdm_configure_filter(adc->dfsdm, adc->fl_id, &filter);
> +	if (ret < 0) {
> +		dev_err(adc->dev, "Failed to configure filter\n");
> +		return ret;
> +	}
> +
> +	ret = stm32_dfsdm_start_channel(adc->dfsdm, ch_id, ch_cfg);
> +	if (ret < 0)
> +		return ret;
> +
> +	stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id, DFSDM_FILTER_REG_CONV);
> +
> +	return 0;
> +}
> +
> +static void stm32_dfsdm_adc_stop_raw_conv(struct stm32_dfsdm_adc *adc,
> +					  const struct iio_chan_spec *chan)
> +{
> +	unsigned int ch_id = CH_ID_FROM_IDX(chan->scan_index);
> +
> +	dev_dbg(adc->dev, "%s:\n", __func__);
> +
> +	stm32_dfsdm_stop_filter(adc->dfsdm, adc->fl_id);
> +	stm32_dfsdm_stop_channel(adc->dfsdm, ch_id);
> +}
> +
> +static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
> +				   const struct iio_chan_spec *chan,
> +				   u32 *result)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	long timeout;
> +	int ret;
> +
> +	dev_dbg(&indio_dev->dev, "%s:\n", __func__);
> +
> +	reinit_completion(&adc->completion);
> +
> +	ret = stm32_dfsdm_register_fl_event(adc->dfsdm, adc->fl_id,
> +					    DFSDM_EVENT_REG_EOC, 0);
> +	if (ret < 0) {
> +		dev_err(&indio_dev->dev, "Failed to register event\n");
> +		return ret;
> +	}
> +
> +	adc->buffer = result;
> +	ret = stm32_dfsdm_adc_start_raw_conv(adc, chan);
> +	if (ret) {
> +		dev_err(&indio_dev->dev, "Failed to start conversion\n");
> +		goto free_event;
> +	}
> +
> +	timeout = wait_for_completion_interruptible_timeout(&adc->completion,
> +							    DFSDM_TIMEOUT);
> +	if (timeout == 0) {
> +		dev_warn(&indio_dev->dev, "Conversion timed out!\n");
> +		ret = -ETIMEDOUT;
> +	} else if (timeout < 0) {
> +		ret = timeout;
> +	} else {
> +		dev_dbg(&indio_dev->dev, "converted val %#x\n", *result);
> +		ret = IIO_VAL_INT;
> +	}
> +
> +	stm32_dfsdm_adc_stop_raw_conv(adc, chan);
> +
> +free_event:
> +	adc->buffer = NULL;
> +	stm32_dfsdm_unregister_fl_event(adc->dfsdm, adc->fl_id,
> +					DFSDM_EVENT_REG_EOC, 0);
> +
> +	return ret;
> +}
> +
> +static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
> +				struct iio_chan_spec const *chan, int *val,
> +				int *val2, long mask)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
> +	int ret = -EINVAL;
> +
> +	dev_dbg(&indio_dev->dev, "%s channel %d\n", __func__, chan->channel);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
> +		if (!ret)
> +			ret = IIO_VAL_INT;
> +		break;
> +	case IIO_CHAN_INFO_OFFSET:
> +		*val = ch_cfg->offset;
> +		ret = IIO_VAL_INT;
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev,
> +				 struct iio_chan_spec const *chan, int val,
> +				 int val2, long mask)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
> +
> +	dev_dbg(&indio_dev->dev, "%s channel%d", __func__, chan->channel);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_OFFSET:
> +		if (val > DFSDM_MAX_CH_OFFSET) {
> +			dev_err(&indio_dev->dev, "invalid offset (> %#lx)",
> +				DFSDM_MAX_CH_OFFSET);
> +			return -EINVAL;
> +		}
> +		ch_cfg->offset = val;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct iio_info stm32_dfsdm_iio_info = {
> +	.read_raw = stm32_dfsdm_read_raw,
> +	.write_raw = stm32_dfsdm_write_raw,
> +	.driver_module = THIS_MODULE,
> +};
> +
> +static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
> +					 struct iio_chan_spec *chan,
> +					 int chan_idx)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	struct stm32_dfsdm_channel *dfsdm_ch = &adc->inputs[chan_idx];
> +	struct iio_chan_spec *ch = &chan[chan_idx];
> +	int ret;
> +	unsigned int alt_ch = 0;
> +
> +	ret = of_property_read_u32_index(indio_dev->dev.of_node,
> +					 "st,adc-channels", chan_idx,
> +					 &ch->channel);
> +	if (ret < 0) {
> +		dev_err(&indio_dev->dev,
> +			" error parsing 'st,adc-channels' for idx %d\n",
> +			chan_idx);
> +		return ret;
> +	}
> +
> +	ret = of_property_read_string_index(indio_dev->dev.of_node,
> +					    "st,adc-channel-names", chan_idx,
> +					    &ch->datasheet_name);
> +	if (ret < 0) {
> +		dev_err(&indio_dev->dev,
> +			" error parsing 'st,adc-channel-names' for idx %d\n",
> +			chan_idx);
> +		return ret;
> +	}
> +
> +	ch->extend_name = ch->datasheet_name;
This is not what extend_name is for, don't do it - it just makes it
really difficult to have standard userspace code.  With hindsight I should
probably never have introduced it in the first place.
> +	ch->type = IIO_VOLTAGE;
> +	ch->indexed = 1;
> +	ch->scan_index = chan_idx;
> +	ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +				 BIT(IIO_CHAN_INFO_OFFSET);
> +	ch->scan_type.sign = 'u';
> +	ch->scan_type.realbits = DFSDM_ADC_MAX_RESOLUTION;
> +	ch->scan_type.storagebits = DFSDM_ADC_STORAGE_BITS;
Unless these are used elsehwere, given they are 'real numbers' it would
actually be easier to read the code if you just put the numbers in here
directly.

> +	ch->scan_type.shift = 8;
> +
> +	ch->ext_info = stm32_dfsdm_adc_ext_info;
> +
> +	of_property_read_u32_index(indio_dev->dev.of_node, "st,adc-alt-channel",
> +				   chan_idx, &alt_ch);
> +	/* Select the previous channel if alternate field is defined*/
space before */  Please check throughout as patches to fix this sort of
thing are really boring  :)
> +	if (alt_ch) {
> +		if (!ch->channel)
> +			ch->channel = adc->dfsdm->max_channels;
> +		ch->channel -= 1;
> +		dfsdm_ch->serial_if.pins = DFSDM_CHANNEL_NEXT_CHANNEL_PINS;
> +	} else {
> +		dfsdm_ch->serial_if.pins = DFSDM_CHANNEL_SAME_CHANNEL_PINS;
> +	}
> +	dfsdm_ch->id = ch->channel;
> +
> +	dfsdm_ch->type.DataPacking = DFSDM_CHANNEL_STANDARD_MODE;
> +
> +	dfsdm_ch->type.source = DFSDM_CHANNEL_EXTERNAL_INPUTS;
> +	ret = of_property_read_u32_index(indio_dev->dev.of_node,
> +					 "st,adc-channel-types",
> +					 chan_idx, &dfsdm_ch->serial_if.type);
> +	if (ret < 0)
> +		dfsdm_ch->serial_if.type = DFSDM_CHANNEL_SPI_RISING;
> +
> +	ret = of_property_read_u32_index(indio_dev->dev.of_node,
> +					 "st,adc-channel-clk-src",
> +					 chan_idx,
> +					 &dfsdm_ch->serial_if.spi_clk);
> +
> +	if ((dfsdm_ch->serial_if.type == DFSDM_CHANNEL_MANCHESTER_RISING)  ||
> +	    (dfsdm_ch->serial_if.type == DFSDM_CHANNEL_MANCHESTER_FALLING) ||
> +	    (ret < 0))
> +		dfsdm_ch->serial_if.spi_clk = DFSDM_CHANNEL_SPI_CLOCK_INTERNAL;
> +
> +	return stm32_dfsdm_get_channel(adc->dfsdm, dfsdm_ch);
> +}
> +
> +static int stm32_dfsdm_adc_chan_init(struct iio_dev *indio_dev)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	unsigned int num_ch;
> +	struct iio_chan_spec *channels;
> +	int ret, chan_idx;
> +
> +	num_ch = of_property_count_strings(indio_dev->dev.of_node,
> +					   "st,adc-channel-names");
> +
> +	channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
> +				GFP_KERNEL);
> +	if (!channels)
> +		return -ENOMEM;
> +
> +	adc->inputs = devm_kcalloc(&indio_dev->dev, num_ch,
> +				   sizeof(*adc->inputs), GFP_KERNEL);
> +	if (!adc->inputs)
> +		return -ENOMEM;
> +
> +	adc->inputs_cfg = devm_kcalloc(&indio_dev->dev, num_ch,
> +				       sizeof(*adc->inputs_cfg), GFP_KERNEL);
> +	if (!adc->inputs_cfg)
> +		return -ENOMEM;
There are a lot of small allocs here. I'm not sure how many channels there
can be, but it may be more efficient (and cleaner) to just put arrays
of sufficient size to take all possible channels directly in your adc
structure.
> +
> +	for (chan_idx = 0; chan_idx < num_ch; chan_idx++) {
> +		ret = stm32_dfsdm_adc_chan_init_one(indio_dev, channels,
> +						    chan_idx);
> +		if (ret < 0)
> +			goto ch_error;
> +	}
> +
> +	indio_dev->num_channels = num_ch;
> +	indio_dev->channels = channels;
> +
> +	return 0;
> +
> +ch_error:
> +	for (chan_idx--; chan_idx >= 0; chan_idx--)
> +		stm32_dfsdm_release_channel(adc->dfsdm,
> +					    adc->inputs[chan_idx].id);
> +
> +	return ret;
> +}
> +
> +static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct stm32_dfsdm_adc *adc;
> +	struct device_node *np = pdev->dev.of_node;
> +	struct iio_dev *indio_dev;
> +	int ret, i;
> +
> +	if (!np)
> +		return -ENODEV;
> +
> +	indio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
> +	if (IS_ERR(indio_dev)) {
> +		dev_err(dev, "%s: failed to allocate iio", __func__);
> +		return PTR_ERR(indio_dev);
> +	}
> +
> +	indio_dev->name = np->name;
> +	indio_dev->dev.parent = dev;
> +	indio_dev->dev.of_node = np;
> +	indio_dev->info = &stm32_dfsdm_iio_info;
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	adc = iio_priv(indio_dev);
> +	if (IS_ERR(adc)) {
> +		dev_err(dev, "%s: failed to allocate adc", __func__);
> +		return PTR_ERR(adc);
> +	}
> +
> +	if (of_property_read_u32(np, "reg", &adc->fl_id)) {
> +		dev_err(&pdev->dev, "missing reg property\n");
> +		return -EINVAL;
> +	}
> +
> +	adc->dev = &indio_dev->dev;
> +	adc->dfsdm = dev_get_drvdata(pdev->dev.parent);
> +
> +	ret = stm32_dfsdm_adc_chan_init(indio_dev);
> +	if (ret < 0) {
> +		dev_err(dev, "iio channels init failed\n");
> +		return ret;
> +	}
> +
> +	ret = stm32_dfsdm_get_filter(adc->dfsdm, adc->fl_id);
> +	if (ret < 0)
> +		goto get_fl_err;
> +
> +	adc->int_oversampling = DFSDM_MIN_INT_OVERSAMPLING;
> +	adc->sinc.oversampling = DFSDM_MIN_FL_OVERSAMPLING;
> +
> +	init_completion(&adc->completion);
> +
> +	ret = devm_iio_device_register(dev, indio_dev);
Probe order must be reversed in remove to make it obvious there are no
weird and wonderful race conditions.  The moment you have anything in probe
that needs unwinding before a devm call that rule is broken.

Upshot, you can't use the devm version of iio_device_register.

> +	if (ret) {
> +		dev_err(adc->dev, "failed to register iio device\n");
> +		goto register_err;
> +	}
> +
> +	platform_set_drvdata(pdev, adc);
> +
> +	return 0;
> +
> +register_err:
> +	stm32_dfsdm_release_filter(adc->dfsdm, adc->fl_id);
> +
> +get_fl_err:
> +	for (i = 0; i < indio_dev->num_channels; i++)
> +		stm32_dfsdm_release_channel(adc->dfsdm, adc->inputs[i].id);
> +
> +	return ret;
> +}
> +
> +static int stm32_dfsdm_adc_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *indio_dev;
> +	struct stm32_dfsdm_adc *adc = platform_get_drvdata(pdev);
> +	int i;
> +
> +	indio_dev = iio_priv_to_dev(adc);
> +	for (i = 0; i < indio_dev->num_channels; i++)
> +		stm32_dfsdm_release_channel(adc->dfsdm, adc->inputs[i].id);
> +	stm32_dfsdm_release_filter(adc->dfsdm, adc->fl_id);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id stm32_dfsdm_adc_match[] = {
> +	{ .compatible = "st,stm32-dfsdm-adc"},
> +	{}
> +};
> +
> +static struct platform_driver stm32_dfsdm_adc_driver = {
> +	.driver = {
> +		.name = "stm32-dfsdm-adc",
> +		.of_match_table = stm32_dfsdm_adc_match,
> +	},
> +	.probe = stm32_dfsdm_adc_probe,
> +	.remove = stm32_dfsdm_adc_remove,
> +};
> +module_platform_driver(stm32_dfsdm_adc_driver);
> +
> +MODULE_DESCRIPTION("STM32 sigma delta ADC");
> +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
> +MODULE_LICENSE("GPL v2");
> 

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

* Re: [PATCH 6/7] ASoC: add bindings for STM32 DFSDM driver
  2017-01-23 16:32     ` Arnaud Pouliquen
  (?)
@ 2017-01-29 12:19       ` Jonathan Cameron
  -1 siblings, 0 replies; 130+ messages in thread
From: Jonathan Cameron @ 2017-01-29 12:19 UTC (permalink / raw)
  To: Arnaud Pouliquen, devicetree, linux-arm-kernel, linux-iio,
	alsa-devel, Lee Jones, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Alexandre Torgue, Maxime Coquelin

On 23/01/17 16:32, Arnaud Pouliquen wrote:
> This patch adds documentation of device tree bindings for the
> STM32 DFSDM ASoC driver.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
>  .../devicetree/bindings/sound/st,sm32-adfsdm.txt   | 84 ++++++++++++++++++++++
>  1 file changed, 84 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
> 
> diff --git a/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt b/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
> new file mode 100644
> index 0000000..a1d27b8
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
> @@ -0,0 +1,84 @@
> +STMicroelectronics STM32 ADFSDM ASoC DAI device driver.
> +
> +STM32 ADFSDM ASoC is a sigma delta audio interface for digital microphone.
> +It has to be declared in device-tree as a subnode of the DFSDM mfd node.
> +
> +It offers possibility to record several mono microphones, with an option to
> +synchronize all microphones on a main one (that must be piped to filter 0)
> +Audio interface can be exposed through the generic ASoC simple card.
> +
> +2 Dmics can be connected on one SPI interface instance n.
> +Convention is that the DMIC that strobes data on rising edge is connected to the
> +corresponding DFSDM channel n; while the Dmic that strobes data on falling edge
> +is connected to the channel n-1. Simple card property "bitclock-inversion = <1>"
> +is used to specify that microphone strobes data on falling edge.
> +
> +SPI interface allows to be scheduled by an external SPI clock. To use it
> +simple card properties "bitclock-master = <&codec>" and "system-clock-frequency"
> +have to be defined in dai-link node.
> +
> +Required properties:
> +- compatible: 	Must be "st,stm32-dfsdm-audio",
> +- reg:		Specifies the DFSDM filter instance.
> +- interrupts: 	DFSDM filter instance interrupt line.
> +- dma:		DMA controller phandle and DMA request line associated to the
> +		filter instance ( specified by the field "reg")
> +- dma-names: 	must be "rx"
> +
> +- st,input-id:		Id of the SPI/Manchester interface used.
> +- st,dai-filter-order:  SinC filter order from 0 to 5.
> +			0: FastSinC
> +			[1-5]: order 1 to 5.
> +			For audio purpose it is recommended to use order 3 to 5.
Interesting for audio you consider it feature of the hardware, but for ADC you
consider that this should be userspace controlled.
Personally I'd like to see more detail on those filters but if this is convention
in Asoc then so be it.
> +
> +Optional properties:
> + - st,dai0-synchronized: Set to 1 to synchronize DAI with DFSDM instance 0.
> +
> +Exemple of a card with 2 Dmics synchronized and connected on SPI interface 1.
Example.
> +
> +	dfsdm: dfsdm@4400D000 {
> +		dai_dfsdm0: dfsdm-audio@0 {
> +			compatible = "st,stm32-dfsdm-audio";
> +			#sound-dai-cells = <0>;
> +			reg = <0>;
> +			dmas = <&dmamux1 101 0x400 0x00>;
> +			dma-names = "rx";
> +			st,input-id = <0>;
> +			st,dai-filter-order = <5>;
> +		};
> +		dai_dfsdm1: dfsdm-audio@1 {
> +			compatible = "st,stm32-dfsdm-audio";
> +			#sound-dai-cells = <0>;
> +			reg = <0>;
> +			dmas = <&dmamux1 102 0x400 0x00>;
> +			dma-names = "rx";
> +			st,input-id = <0>;
> +			st,dai0-synchronized = <1>;
> +			st,dai-filter-order = <5>;
> +		};
> +	};
> +	sound_dfsdm_pdm {
> +		compatible = "simple-audio-card";
> +		simple-audio-card,name = "dfsdm_pdm";
> +		status = "okay";
> +
> +		dfsdm0_mic0: simple-audio-card,dai-link@0 {
> +			format = "pdm";
> +			cpu {
> +				sound-dai = <&dai_dfsdm0>;
> +			};
> +			dmic0_codec: codec {
> +				sound-dai = <&dmic0>;
> +			};
> +		};
> +		dfsdm0_mic1: simple-audio-card,dai-link@1 {
> +			format = "pdm";
> +			bitclock-inversion = <1>;
> +			cpu {
> +				sound-dai = <&dai_dfsdm1>;
> +			};
> +			codec {
> +				sound-dai = <&dmic1>;
> +			};
> +		};
> +	};
> 
Jonathan

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

* Re: [PATCH 6/7] ASoC: add bindings for STM32 DFSDM driver
@ 2017-01-29 12:19       ` Jonathan Cameron
  0 siblings, 0 replies; 130+ messages in thread
From: Jonathan Cameron @ 2017-01-29 12:19 UTC (permalink / raw)
  To: Arnaud Pouliquen, devicetree, linux-arm-kernel, linux-iio,
	alsa-devel, Lee Jones, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre Torgue

On 23/01/17 16:32, Arnaud Pouliquen wrote:
> This patch adds documentation of device tree bindings for the
> STM32 DFSDM ASoC driver.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
>  .../devicetree/bindings/sound/st,sm32-adfsdm.txt   | 84 ++++++++++++++++++++++
>  1 file changed, 84 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
> 
> diff --git a/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt b/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
> new file mode 100644
> index 0000000..a1d27b8
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
> @@ -0,0 +1,84 @@
> +STMicroelectronics STM32 ADFSDM ASoC DAI device driver.
> +
> +STM32 ADFSDM ASoC is a sigma delta audio interface for digital microphone.
> +It has to be declared in device-tree as a subnode of the DFSDM mfd node.
> +
> +It offers possibility to record several mono microphones, with an option to
> +synchronize all microphones on a main one (that must be piped to filter 0)
> +Audio interface can be exposed through the generic ASoC simple card.
> +
> +2 Dmics can be connected on one SPI interface instance n.
> +Convention is that the DMIC that strobes data on rising edge is connected to the
> +corresponding DFSDM channel n; while the Dmic that strobes data on falling edge
> +is connected to the channel n-1. Simple card property "bitclock-inversion = <1>"
> +is used to specify that microphone strobes data on falling edge.
> +
> +SPI interface allows to be scheduled by an external SPI clock. To use it
> +simple card properties "bitclock-master = <&codec>" and "system-clock-frequency"
> +have to be defined in dai-link node.
> +
> +Required properties:
> +- compatible: 	Must be "st,stm32-dfsdm-audio",
> +- reg:		Specifies the DFSDM filter instance.
> +- interrupts: 	DFSDM filter instance interrupt line.
> +- dma:		DMA controller phandle and DMA request line associated to the
> +		filter instance ( specified by the field "reg")
> +- dma-names: 	must be "rx"
> +
> +- st,input-id:		Id of the SPI/Manchester interface used.
> +- st,dai-filter-order:  SinC filter order from 0 to 5.
> +			0: FastSinC
> +			[1-5]: order 1 to 5.
> +			For audio purpose it is recommended to use order 3 to 5.
Interesting for audio you consider it feature of the hardware, but for ADC you
consider that this should be userspace controlled.
Personally I'd like to see more detail on those filters but if this is convention
in Asoc then so be it.
> +
> +Optional properties:
> + - st,dai0-synchronized: Set to 1 to synchronize DAI with DFSDM instance 0.
> +
> +Exemple of a card with 2 Dmics synchronized and connected on SPI interface 1.
Example.
> +
> +	dfsdm: dfsdm@4400D000 {
> +		dai_dfsdm0: dfsdm-audio@0 {
> +			compatible = "st,stm32-dfsdm-audio";
> +			#sound-dai-cells = <0>;
> +			reg = <0>;
> +			dmas = <&dmamux1 101 0x400 0x00>;
> +			dma-names = "rx";
> +			st,input-id = <0>;
> +			st,dai-filter-order = <5>;
> +		};
> +		dai_dfsdm1: dfsdm-audio@1 {
> +			compatible = "st,stm32-dfsdm-audio";
> +			#sound-dai-cells = <0>;
> +			reg = <0>;
> +			dmas = <&dmamux1 102 0x400 0x00>;
> +			dma-names = "rx";
> +			st,input-id = <0>;
> +			st,dai0-synchronized = <1>;
> +			st,dai-filter-order = <5>;
> +		};
> +	};
> +	sound_dfsdm_pdm {
> +		compatible = "simple-audio-card";
> +		simple-audio-card,name = "dfsdm_pdm";
> +		status = "okay";
> +
> +		dfsdm0_mic0: simple-audio-card,dai-link@0 {
> +			format = "pdm";
> +			cpu {
> +				sound-dai = <&dai_dfsdm0>;
> +			};
> +			dmic0_codec: codec {
> +				sound-dai = <&dmic0>;
> +			};
> +		};
> +		dfsdm0_mic1: simple-audio-card,dai-link@1 {
> +			format = "pdm";
> +			bitclock-inversion = <1>;
> +			cpu {
> +				sound-dai = <&dai_dfsdm1>;
> +			};
> +			codec {
> +				sound-dai = <&dmic1>;
> +			};
> +		};
> +	};
> 
Jonathan

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

* [PATCH 6/7] ASoC: add bindings for STM32 DFSDM driver
@ 2017-01-29 12:19       ` Jonathan Cameron
  0 siblings, 0 replies; 130+ messages in thread
From: Jonathan Cameron @ 2017-01-29 12:19 UTC (permalink / raw)
  To: linux-arm-kernel

On 23/01/17 16:32, Arnaud Pouliquen wrote:
> This patch adds documentation of device tree bindings for the
> STM32 DFSDM ASoC driver.
> 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> ---
>  .../devicetree/bindings/sound/st,sm32-adfsdm.txt   | 84 ++++++++++++++++++++++
>  1 file changed, 84 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
> 
> diff --git a/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt b/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
> new file mode 100644
> index 0000000..a1d27b8
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
> @@ -0,0 +1,84 @@
> +STMicroelectronics STM32 ADFSDM ASoC DAI device driver.
> +
> +STM32 ADFSDM ASoC is a sigma delta audio interface for digital microphone.
> +It has to be declared in device-tree as a subnode of the DFSDM mfd node.
> +
> +It offers possibility to record several mono microphones, with an option to
> +synchronize all microphones on a main one (that must be piped to filter 0)
> +Audio interface can be exposed through the generic ASoC simple card.
> +
> +2 Dmics can be connected on one SPI interface instance n.
> +Convention is that the DMIC that strobes data on rising edge is connected to the
> +corresponding DFSDM channel n; while the Dmic that strobes data on falling edge
> +is connected to the channel n-1. Simple card property "bitclock-inversion = <1>"
> +is used to specify that microphone strobes data on falling edge.
> +
> +SPI interface allows to be scheduled by an external SPI clock. To use it
> +simple card properties "bitclock-master = <&codec>" and "system-clock-frequency"
> +have to be defined in dai-link node.
> +
> +Required properties:
> +- compatible: 	Must be "st,stm32-dfsdm-audio",
> +- reg:		Specifies the DFSDM filter instance.
> +- interrupts: 	DFSDM filter instance interrupt line.
> +- dma:		DMA controller phandle and DMA request line associated to the
> +		filter instance ( specified by the field "reg")
> +- dma-names: 	must be "rx"
> +
> +- st,input-id:		Id of the SPI/Manchester interface used.
> +- st,dai-filter-order:  SinC filter order from 0 to 5.
> +			0: FastSinC
> +			[1-5]: order 1 to 5.
> +			For audio purpose it is recommended to use order 3 to 5.
Interesting for audio you consider it feature of the hardware, but for ADC you
consider that this should be userspace controlled.
Personally I'd like to see more detail on those filters but if this is convention
in Asoc then so be it.
> +
> +Optional properties:
> + - st,dai0-synchronized: Set to 1 to synchronize DAI with DFSDM instance 0.
> +
> +Exemple of a card with 2 Dmics synchronized and connected on SPI interface 1.
Example.
> +
> +	dfsdm: dfsdm at 4400D000 {
> +		dai_dfsdm0: dfsdm-audio at 0 {
> +			compatible = "st,stm32-dfsdm-audio";
> +			#sound-dai-cells = <0>;
> +			reg = <0>;
> +			dmas = <&dmamux1 101 0x400 0x00>;
> +			dma-names = "rx";
> +			st,input-id = <0>;
> +			st,dai-filter-order = <5>;
> +		};
> +		dai_dfsdm1: dfsdm-audio at 1 {
> +			compatible = "st,stm32-dfsdm-audio";
> +			#sound-dai-cells = <0>;
> +			reg = <0>;
> +			dmas = <&dmamux1 102 0x400 0x00>;
> +			dma-names = "rx";
> +			st,input-id = <0>;
> +			st,dai0-synchronized = <1>;
> +			st,dai-filter-order = <5>;
> +		};
> +	};
> +	sound_dfsdm_pdm {
> +		compatible = "simple-audio-card";
> +		simple-audio-card,name = "dfsdm_pdm";
> +		status = "okay";
> +
> +		dfsdm0_mic0: simple-audio-card,dai-link at 0 {
> +			format = "pdm";
> +			cpu {
> +				sound-dai = <&dai_dfsdm0>;
> +			};
> +			dmic0_codec: codec {
> +				sound-dai = <&dmic0>;
> +			};
> +		};
> +		dfsdm0_mic1: simple-audio-card,dai-link at 1 {
> +			format = "pdm";
> +			bitclock-inversion = <1>;
> +			cpu {
> +				sound-dai = <&dai_dfsdm1>;
> +			};
> +			codec {
> +				sound-dai = <&dmic1>;
> +			};
> +		};
> +	};
> 
Jonathan

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
  2017-01-27 17:17                         ` Lee Jones
  (?)
@ 2017-01-29 12:28                           ` Jonathan Cameron
  -1 siblings, 0 replies; 130+ messages in thread
From: Jonathan Cameron @ 2017-01-29 12:28 UTC (permalink / raw)
  To: Lee Jones, Arnaud Pouliquen
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Rob Herring, Mark Rutland,
	Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
	Jaroslav Kysela, Takashi Iwai, Liam Girdwood, Mark Brown,
	Maxime Coquelin, Alexandre TORGUE

On 27/01/17 17:17, Lee Jones wrote:
> On Fri, 27 Jan 2017, Arnaud Pouliquen wrote:
>> On 01/27/2017 11:15 AM, Lee Jones wrote:
>>> On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
>>>> On 01/24/2017 12:36 PM, Lee Jones wrote:
>>>>> On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
>>>>>> On 01/24/2017 09:22 AM, Lee Jones wrote:
>>>>>>> On Mon, 23 Jan 2017, Arnaud Pouliquen wrote:
>>>>>>>
>>>>>>>> DFSDM hardware IP can be used at the same time for ADC sigma delta
>>>>>>>
>>>>>>> Same time as what?
>>>>>> DFSDM is used for ADC acquisition (through IIO) but also PDM microphone
>>>>>> capture (through ASOC).
>>>>>>>
>>>>>>>> conversion and audio PDM microphone.
>>>>>>>> MFD driver is in charge of configuring IP registers and managing IP clocks.
>>>>>>>> For this it exports an API to handles filters and channels resources.
>>>>>>>
>>>>>>> This looks like an ADC driver?  What is it that makes it an MFD?
>>>>>> Yes it a kind of ADC but that supports 2 features audio and iio.
>>>>>> So it has to support 2 features based on 2 separate Frameworks.
>>>>>
>>>>> I'm still unsure why it needs to live in MFD.
>>>>>
>>>>> By the looks of it, this driver needs to move into IIO and you need to
>>>>> call into it from ASoC.
>>>>>
>>>>
>>>> I think i introduce confusion by speaking about ADC for audio...
>>>>
>>>> 1) IIO handles sigma delta ADCs that can be used as example for motor
>>>> controls. the aim is to get value based on an application request or
>>>> using some triggers.
>>>> example: http://www.ti.com/lit/ds/symlink/ads1202.pdf
>>>>
>>>> 2) For audio part, we speak about Digital mems microphones that generate
>>>> PDM format stream. PDM is a continuous real time stream dedicated to
>>>> audio record and must be handled in ASOC ( codec Dmic part driver is
>>>> /sound/soc/codec/dmic.c).
>>>> DMIC example:
>>>> http://www.st.com/content/ccc/resource/technical/document/datasheet/47/bd/d2/13/8d/fd/48/26/DM00121815.pdf/files/DM00121815.pdf/jcr:content/translations/en.DM00121815.pdf
>>>>
>>>> Form my point of view it very strange to handle DMICs in IIO, as it is
>>>> not designed to support audio streams.it is two separate features that
>>>> are not compatible.
>>>>
>>>> Now, from software point of view
>>>> That would means that IIO declares ADCs that it can not expose, because
>>>> DMIC is not IIO standard. But IIO inkern API needs that device is
>>>> declared
>>>
>>>> So i should define a specific API in IIO for ASOC driver.
>>>
>>> Yes, this is what I think you should do.
>>>
>>> MFD is not a dumping ground for devices that do not fit anywhere else.
>>
>> I fully understand that you don't want to create unnecessary MFD devices.
>> But In case of DFSDM ,it is really a device that supports 2 features.
>> The main evidence is that "ADC acquisition" and "digital microphone"
>> features are handled by two separate frameworks.
>> So this corresponds to two features that share the same device resources
>> (registers, IRQs, clocks).
>> Seems that this match with MFD definition.
>>
>> Now, if IIO and ASOC maintainers are aligned with you proposal, i will
>> move MFD part in IIO.
>> But in this case, i can not see another way to do it, except a MFD
>> driver hidden in IIO?
>>
>> Here is my analysis of a design in IIO:
>>
>> Natural way to do this is to consider that ASOC is a customer of IIO so
>> use the customer.h API.
>> 1) As Digital microphone can not be handled by IIO dev interface, i can
>> not expose them as an IIO channel, that would be visible by applications
>> in /sys/bus/iio/devices/iio
>> 2) ASOC needs to configure DFSDM filter to match to specific frequencies
>> and sample formats requested by users. Not standard IIO interface to do
>> this. (on IIO side this is done by attribute file)
>> 3) audio stream is a real time stream. So for audio quality best is to
>> minimize latencies and Xrun. That why transfer as done from HW to
>> application using LLI DMA transfers with a minimum of copy. This does
>> not match with the IIO inkern API.
>>  => So seems not possible to use IIO inkern API.
>>
>> That's means that i would need to create a ST specific include file to
>> offer an API to ASoC to handle DFSDM resources linked to the DMIC.
>> and to probe ASOc device in IIO one.
>> So the solution would be to create something like a sub IIO driver That
>> probe and handle some IIO and ASOC devices.
>> Ultimately This would correspond to a MFD driver integrated in IIO...
> 
> The issue is not the creation of an MFD driver to create shared
> resources and probe the child devices.  That's what MFD *is* for.
> What I do take exception to is having lots of code in MFD which
> should clearly live in a different subsystem.
> 
> Since this device only serves a couple of functions, I expect it to be
> a few hundred lines, maximum.  Everything else should be farmed out.
> MFD knows nothing of "channels" or "filters" so anything related to
> them (init, get, start, stop, release, etc) needs to find another
> home.  Whether that's in IIO is a separate discussion, but it
> certainly doesn't live in MFD.
> 
>> Jonathan, Mark, Please could you share your opinion on this topic?
Hmm - based on a fairly quick read through of the code (which is never
ideal!). I can see that the ideal would indeed be as Lee says, to
expand the IIO interfaces sufficiently to support what you need.


So, reading the code (fairly quickly I'm afraid as had a lot of reviews
to catch up on this weekend).
What we need:
1) DMA support in the ADC driver.  This would be a good anyway!
2) DMA consumer support - I defer to Lars for comments on this.
3) Means of describing and controlling the sinc filters applied. 
4) Appropriate channel support.  I'm not convinced that it doesn't make
sense to have IIO channels for the microphones - at least in a streaming
mode.  It's data - I don't really care what ;)
Coarsely it's a filtered pulse per period counter which is
a perfectly valid type to have a channel for.

The big question to my mind is the DMA consumer support. How would
it work. It it wouldn't this is somewhat of a non starter.

To bring up another slightly ugly MFD case where it is borderline
on whether an MFD makes sense (just as a reference point of something
we have discussed a few times before)

ADCs with features directed at touchscreen support.
These are odd as the ADC bit is generic, but the specific output
and read sequences used for touchscreen reading don't correspond to
anything that makes any real sense for other applications.

We have started to get hybrid drives that have an MFD underneath but
do the ADC reads through IIO consumer interfaces, and the timing
control from a touchscreen driver.  We haven't really gotten this
one right yet either.

Here however, to my mind things are different - as I read it
(and feel free to point out what I'm missing), the sound usecase
is just a question of setting up sampling frequencies and filters
appropriate to the microphones and what ASoC expects?

That's not to say the IIO dma stuff is flexible enough (yet) to
handle the data flows, but perhaps we can work towards that.

Jonathan

>>
>> Regards
>> Arnaud
>>
>>>
>>>>>>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
>>>>>>>> ---
>>>>>>>>  drivers/mfd/Kconfig             |   11 +
>>>>>>>>  drivers/mfd/Makefile            |    2 +
>>>>>>>>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
>>>>>>>>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
>>>>>>>>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
>>>>>>>>  5 files changed, 1601 insertions(+)
>>>>>>>>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
>>>>>>>>  create mode 100644 drivers/mfd/stm32-dfsdm.c
>>>>>>>>  create mode 100644 include/linux/mfd/stm32-dfsdm.h
>>>>>>>>
>>>>>>>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>>>>>>>> index c6df644..4bb660b 100644
>>>>>>>> --- a/drivers/mfd/Kconfig
>>>>>>>> +++ b/drivers/mfd/Kconfig
>>>>>>>> @@ -1607,6 +1607,17 @@ config MFD_STW481X
>>>>>>>>  	  in various ST Microelectronics and ST-Ericsson embedded
>>>>>>>>  	  Nomadik series.
>>>>>>>>  
>>>>>>>> +config MFD_STM32_DFSDM
>>>>>>>> +	tristate "ST Microelectronics STM32 DFSDM"
>>>>>>>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>>>>>>>> +	select MFD_CORE
>>>>>>>> +	select REGMAP
>>>>>>>> +	select REGMAP_MMIO
>>>>>>>> +	help
>>>>>>>> +	  Select this option to enable the STM32 Digital Filter
>>>>>>>> +	  for Sigma Delta Modulators (DFSDM) driver used
>>>>>>>> +	  in various STM32 series.
>>>>>>>> +
>>>>>>>>  menu "Multimedia Capabilities Port drivers"
>>>>>>>>  	depends on ARCH_SA1100
>>>>>>>
>>>>>>> [...]
>>>>>>>
>>>>>>
>>>>>> Regards
>>>>>> Arnaud
>>>>>
>>>
> 

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-29 12:28                           ` Jonathan Cameron
  0 siblings, 0 replies; 130+ messages in thread
From: Jonathan Cameron @ 2017-01-29 12:28 UTC (permalink / raw)
  To: Lee Jones, Arnaud Pouliquen
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, Rob Herring,
	Mark Rutland, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Jaroslav Kysela, Takashi Iwai,
	Liam Girdwood, Mark Brown, Maxime Coquelin, Alexandre TORGUE

On 27/01/17 17:17, Lee Jones wrote:
> On Fri, 27 Jan 2017, Arnaud Pouliquen wrote:
>> On 01/27/2017 11:15 AM, Lee Jones wrote:
>>> On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
>>>> On 01/24/2017 12:36 PM, Lee Jones wrote:
>>>>> On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
>>>>>> On 01/24/2017 09:22 AM, Lee Jones wrote:
>>>>>>> On Mon, 23 Jan 2017, Arnaud Pouliquen wrote:
>>>>>>>
>>>>>>>> DFSDM hardware IP can be used at the same time for ADC sigma delta
>>>>>>>
>>>>>>> Same time as what?
>>>>>> DFSDM is used for ADC acquisition (through IIO) but also PDM microphone
>>>>>> capture (through ASOC).
>>>>>>>
>>>>>>>> conversion and audio PDM microphone.
>>>>>>>> MFD driver is in charge of configuring IP registers and managing IP clocks.
>>>>>>>> For this it exports an API to handles filters and channels resources.
>>>>>>>
>>>>>>> This looks like an ADC driver?  What is it that makes it an MFD?
>>>>>> Yes it a kind of ADC but that supports 2 features audio and iio.
>>>>>> So it has to support 2 features based on 2 separate Frameworks.
>>>>>
>>>>> I'm still unsure why it needs to live in MFD.
>>>>>
>>>>> By the looks of it, this driver needs to move into IIO and you need to
>>>>> call into it from ASoC.
>>>>>
>>>>
>>>> I think i introduce confusion by speaking about ADC for audio...
>>>>
>>>> 1) IIO handles sigma delta ADCs that can be used as example for motor
>>>> controls. the aim is to get value based on an application request or
>>>> using some triggers.
>>>> example: http://www.ti.com/lit/ds/symlink/ads1202.pdf
>>>>
>>>> 2) For audio part, we speak about Digital mems microphones that generate
>>>> PDM format stream. PDM is a continuous real time stream dedicated to
>>>> audio record and must be handled in ASOC ( codec Dmic part driver is
>>>> /sound/soc/codec/dmic.c).
>>>> DMIC example:
>>>> http://www.st.com/content/ccc/resource/technical/document/datasheet/47/bd/d2/13/8d/fd/48/26/DM00121815.pdf/files/DM00121815.pdf/jcr:content/translations/en.DM00121815.pdf
>>>>
>>>> Form my point of view it very strange to handle DMICs in IIO, as it is
>>>> not designed to support audio streams.it is two separate features that
>>>> are not compatible.
>>>>
>>>> Now, from software point of view
>>>> That would means that IIO declares ADCs that it can not expose, because
>>>> DMIC is not IIO standard. But IIO inkern API needs that device is
>>>> declared
>>>
>>>> So i should define a specific API in IIO for ASOC driver.
>>>
>>> Yes, this is what I think you should do.
>>>
>>> MFD is not a dumping ground for devices that do not fit anywhere else.
>>
>> I fully understand that you don't want to create unnecessary MFD devices.
>> But In case of DFSDM ,it is really a device that supports 2 features.
>> The main evidence is that "ADC acquisition" and "digital microphone"
>> features are handled by two separate frameworks.
>> So this corresponds to two features that share the same device resources
>> (registers, IRQs, clocks).
>> Seems that this match with MFD definition.
>>
>> Now, if IIO and ASOC maintainers are aligned with you proposal, i will
>> move MFD part in IIO.
>> But in this case, i can not see another way to do it, except a MFD
>> driver hidden in IIO?
>>
>> Here is my analysis of a design in IIO:
>>
>> Natural way to do this is to consider that ASOC is a customer of IIO so
>> use the customer.h API.
>> 1) As Digital microphone can not be handled by IIO dev interface, i can
>> not expose them as an IIO channel, that would be visible by applications
>> in /sys/bus/iio/devices/iio
>> 2) ASOC needs to configure DFSDM filter to match to specific frequencies
>> and sample formats requested by users. Not standard IIO interface to do
>> this. (on IIO side this is done by attribute file)
>> 3) audio stream is a real time stream. So for audio quality best is to
>> minimize latencies and Xrun. That why transfer as done from HW to
>> application using LLI DMA transfers with a minimum of copy. This does
>> not match with the IIO inkern API.
>>  => So seems not possible to use IIO inkern API.
>>
>> That's means that i would need to create a ST specific include file to
>> offer an API to ASoC to handle DFSDM resources linked to the DMIC.
>> and to probe ASOc device in IIO one.
>> So the solution would be to create something like a sub IIO driver That
>> probe and handle some IIO and ASOC devices.
>> Ultimately This would correspond to a MFD driver integrated in IIO...
> 
> The issue is not the creation of an MFD driver to create shared
> resources and probe the child devices.  That's what MFD *is* for.
> What I do take exception to is having lots of code in MFD which
> should clearly live in a different subsystem.
> 
> Since this device only serves a couple of functions, I expect it to be
> a few hundred lines, maximum.  Everything else should be farmed out.
> MFD knows nothing of "channels" or "filters" so anything related to
> them (init, get, start, stop, release, etc) needs to find another
> home.  Whether that's in IIO is a separate discussion, but it
> certainly doesn't live in MFD.
> 
>> Jonathan, Mark, Please could you share your opinion on this topic?
Hmm - based on a fairly quick read through of the code (which is never
ideal!). I can see that the ideal would indeed be as Lee says, to
expand the IIO interfaces sufficiently to support what you need.


So, reading the code (fairly quickly I'm afraid as had a lot of reviews
to catch up on this weekend).
What we need:
1) DMA support in the ADC driver.  This would be a good anyway!
2) DMA consumer support - I defer to Lars for comments on this.
3) Means of describing and controlling the sinc filters applied. 
4) Appropriate channel support.  I'm not convinced that it doesn't make
sense to have IIO channels for the microphones - at least in a streaming
mode.  It's data - I don't really care what ;)
Coarsely it's a filtered pulse per period counter which is
a perfectly valid type to have a channel for.

The big question to my mind is the DMA consumer support. How would
it work. It it wouldn't this is somewhat of a non starter.

To bring up another slightly ugly MFD case where it is borderline
on whether an MFD makes sense (just as a reference point of something
we have discussed a few times before)

ADCs with features directed at touchscreen support.
These are odd as the ADC bit is generic, but the specific output
and read sequences used for touchscreen reading don't correspond to
anything that makes any real sense for other applications.

We have started to get hybrid drives that have an MFD underneath but
do the ADC reads through IIO consumer interfaces, and the timing
control from a touchscreen driver.  We haven't really gotten this
one right yet either.

Here however, to my mind things are different - as I read it
(and feel free to point out what I'm missing), the sound usecase
is just a question of setting up sampling frequencies and filters
appropriate to the microphones and what ASoC expects?

That's not to say the IIO dma stuff is flexible enough (yet) to
handle the data flows, but perhaps we can work towards that.

Jonathan

>>
>> Regards
>> Arnaud
>>
>>>
>>>>>>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>>>>>>>> ---
>>>>>>>>  drivers/mfd/Kconfig             |   11 +
>>>>>>>>  drivers/mfd/Makefile            |    2 +
>>>>>>>>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
>>>>>>>>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
>>>>>>>>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
>>>>>>>>  5 files changed, 1601 insertions(+)
>>>>>>>>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
>>>>>>>>  create mode 100644 drivers/mfd/stm32-dfsdm.c
>>>>>>>>  create mode 100644 include/linux/mfd/stm32-dfsdm.h
>>>>>>>>
>>>>>>>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>>>>>>>> index c6df644..4bb660b 100644
>>>>>>>> --- a/drivers/mfd/Kconfig
>>>>>>>> +++ b/drivers/mfd/Kconfig
>>>>>>>> @@ -1607,6 +1607,17 @@ config MFD_STW481X
>>>>>>>>  	  in various ST Microelectronics and ST-Ericsson embedded
>>>>>>>>  	  Nomadik series.
>>>>>>>>  
>>>>>>>> +config MFD_STM32_DFSDM
>>>>>>>> +	tristate "ST Microelectronics STM32 DFSDM"
>>>>>>>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>>>>>>>> +	select MFD_CORE
>>>>>>>> +	select REGMAP
>>>>>>>> +	select REGMAP_MMIO
>>>>>>>> +	help
>>>>>>>> +	  Select this option to enable the STM32 Digital Filter
>>>>>>>> +	  for Sigma Delta Modulators (DFSDM) driver used
>>>>>>>> +	  in various STM32 series.
>>>>>>>> +
>>>>>>>>  menu "Multimedia Capabilities Port drivers"
>>>>>>>>  	depends on ARCH_SA1100
>>>>>>>
>>>>>>> [...]
>>>>>>>
>>>>>>
>>>>>> Regards
>>>>>> Arnaud
>>>>>
>>>
> 


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

* [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-29 12:28                           ` Jonathan Cameron
  0 siblings, 0 replies; 130+ messages in thread
From: Jonathan Cameron @ 2017-01-29 12:28 UTC (permalink / raw)
  To: linux-arm-kernel

On 27/01/17 17:17, Lee Jones wrote:
> On Fri, 27 Jan 2017, Arnaud Pouliquen wrote:
>> On 01/27/2017 11:15 AM, Lee Jones wrote:
>>> On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
>>>> On 01/24/2017 12:36 PM, Lee Jones wrote:
>>>>> On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
>>>>>> On 01/24/2017 09:22 AM, Lee Jones wrote:
>>>>>>> On Mon, 23 Jan 2017, Arnaud Pouliquen wrote:
>>>>>>>
>>>>>>>> DFSDM hardware IP can be used at the same time for ADC sigma delta
>>>>>>>
>>>>>>> Same time as what?
>>>>>> DFSDM is used for ADC acquisition (through IIO) but also PDM microphone
>>>>>> capture (through ASOC).
>>>>>>>
>>>>>>>> conversion and audio PDM microphone.
>>>>>>>> MFD driver is in charge of configuring IP registers and managing IP clocks.
>>>>>>>> For this it exports an API to handles filters and channels resources.
>>>>>>>
>>>>>>> This looks like an ADC driver?  What is it that makes it an MFD?
>>>>>> Yes it a kind of ADC but that supports 2 features audio and iio.
>>>>>> So it has to support 2 features based on 2 separate Frameworks.
>>>>>
>>>>> I'm still unsure why it needs to live in MFD.
>>>>>
>>>>> By the looks of it, this driver needs to move into IIO and you need to
>>>>> call into it from ASoC.
>>>>>
>>>>
>>>> I think i introduce confusion by speaking about ADC for audio...
>>>>
>>>> 1) IIO handles sigma delta ADCs that can be used as example for motor
>>>> controls. the aim is to get value based on an application request or
>>>> using some triggers.
>>>> example: http://www.ti.com/lit/ds/symlink/ads1202.pdf
>>>>
>>>> 2) For audio part, we speak about Digital mems microphones that generate
>>>> PDM format stream. PDM is a continuous real time stream dedicated to
>>>> audio record and must be handled in ASOC ( codec Dmic part driver is
>>>> /sound/soc/codec/dmic.c).
>>>> DMIC example:
>>>> http://www.st.com/content/ccc/resource/technical/document/datasheet/47/bd/d2/13/8d/fd/48/26/DM00121815.pdf/files/DM00121815.pdf/jcr:content/translations/en.DM00121815.pdf
>>>>
>>>> Form my point of view it very strange to handle DMICs in IIO, as it is
>>>> not designed to support audio streams.it is two separate features that
>>>> are not compatible.
>>>>
>>>> Now, from software point of view
>>>> That would means that IIO declares ADCs that it can not expose, because
>>>> DMIC is not IIO standard. But IIO inkern API needs that device is
>>>> declared
>>>
>>>> So i should define a specific API in IIO for ASOC driver.
>>>
>>> Yes, this is what I think you should do.
>>>
>>> MFD is not a dumping ground for devices that do not fit anywhere else.
>>
>> I fully understand that you don't want to create unnecessary MFD devices.
>> But In case of DFSDM ,it is really a device that supports 2 features.
>> The main evidence is that "ADC acquisition" and "digital microphone"
>> features are handled by two separate frameworks.
>> So this corresponds to two features that share the same device resources
>> (registers, IRQs, clocks).
>> Seems that this match with MFD definition.
>>
>> Now, if IIO and ASOC maintainers are aligned with you proposal, i will
>> move MFD part in IIO.
>> But in this case, i can not see another way to do it, except a MFD
>> driver hidden in IIO?
>>
>> Here is my analysis of a design in IIO:
>>
>> Natural way to do this is to consider that ASOC is a customer of IIO so
>> use the customer.h API.
>> 1) As Digital microphone can not be handled by IIO dev interface, i can
>> not expose them as an IIO channel, that would be visible by applications
>> in /sys/bus/iio/devices/iio
>> 2) ASOC needs to configure DFSDM filter to match to specific frequencies
>> and sample formats requested by users. Not standard IIO interface to do
>> this. (on IIO side this is done by attribute file)
>> 3) audio stream is a real time stream. So for audio quality best is to
>> minimize latencies and Xrun. That why transfer as done from HW to
>> application using LLI DMA transfers with a minimum of copy. This does
>> not match with the IIO inkern API.
>>  => So seems not possible to use IIO inkern API.
>>
>> That's means that i would need to create a ST specific include file to
>> offer an API to ASoC to handle DFSDM resources linked to the DMIC.
>> and to probe ASOc device in IIO one.
>> So the solution would be to create something like a sub IIO driver That
>> probe and handle some IIO and ASOC devices.
>> Ultimately This would correspond to a MFD driver integrated in IIO...
> 
> The issue is not the creation of an MFD driver to create shared
> resources and probe the child devices.  That's what MFD *is* for.
> What I do take exception to is having lots of code in MFD which
> should clearly live in a different subsystem.
> 
> Since this device only serves a couple of functions, I expect it to be
> a few hundred lines, maximum.  Everything else should be farmed out.
> MFD knows nothing of "channels" or "filters" so anything related to
> them (init, get, start, stop, release, etc) needs to find another
> home.  Whether that's in IIO is a separate discussion, but it
> certainly doesn't live in MFD.
> 
>> Jonathan, Mark, Please could you share your opinion on this topic?
Hmm - based on a fairly quick read through of the code (which is never
ideal!). I can see that the ideal would indeed be as Lee says, to
expand the IIO interfaces sufficiently to support what you need.


So, reading the code (fairly quickly I'm afraid as had a lot of reviews
to catch up on this weekend).
What we need:
1) DMA support in the ADC driver.  This would be a good anyway!
2) DMA consumer support - I defer to Lars for comments on this.
3) Means of describing and controlling the sinc filters applied. 
4) Appropriate channel support.  I'm not convinced that it doesn't make
sense to have IIO channels for the microphones - at least in a streaming
mode.  It's data - I don't really care what ;)
Coarsely it's a filtered pulse per period counter which is
a perfectly valid type to have a channel for.

The big question to my mind is the DMA consumer support. How would
it work. It it wouldn't this is somewhat of a non starter.

To bring up another slightly ugly MFD case where it is borderline
on whether an MFD makes sense (just as a reference point of something
we have discussed a few times before)

ADCs with features directed at touchscreen support.
These are odd as the ADC bit is generic, but the specific output
and read sequences used for touchscreen reading don't correspond to
anything that makes any real sense for other applications.

We have started to get hybrid drives that have an MFD underneath but
do the ADC reads through IIO consumer interfaces, and the timing
control from a touchscreen driver.  We haven't really gotten this
one right yet either.

Here however, to my mind things are different - as I read it
(and feel free to point out what I'm missing), the sound usecase
is just a question of setting up sampling frequencies and filters
appropriate to the microphones and what ASoC expects?

That's not to say the IIO dma stuff is flexible enough (yet) to
handle the data flows, but perhaps we can work towards that.

Jonathan

>>
>> Regards
>> Arnaud
>>
>>>
>>>>>>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>>>>>>>> ---
>>>>>>>>  drivers/mfd/Kconfig             |   11 +
>>>>>>>>  drivers/mfd/Makefile            |    2 +
>>>>>>>>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
>>>>>>>>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
>>>>>>>>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
>>>>>>>>  5 files changed, 1601 insertions(+)
>>>>>>>>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
>>>>>>>>  create mode 100644 drivers/mfd/stm32-dfsdm.c
>>>>>>>>  create mode 100644 include/linux/mfd/stm32-dfsdm.h
>>>>>>>>
>>>>>>>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>>>>>>>> index c6df644..4bb660b 100644
>>>>>>>> --- a/drivers/mfd/Kconfig
>>>>>>>> +++ b/drivers/mfd/Kconfig
>>>>>>>> @@ -1607,6 +1607,17 @@ config MFD_STW481X
>>>>>>>>  	  in various ST Microelectronics and ST-Ericsson embedded
>>>>>>>>  	  Nomadik series.
>>>>>>>>  
>>>>>>>> +config MFD_STM32_DFSDM
>>>>>>>> +	tristate "ST Microelectronics STM32 DFSDM"
>>>>>>>> +	depends on (ARCH_STM32 && OF) || COMPILE_TEST
>>>>>>>> +	select MFD_CORE
>>>>>>>> +	select REGMAP
>>>>>>>> +	select REGMAP_MMIO
>>>>>>>> +	help
>>>>>>>> +	  Select this option to enable the STM32 Digital Filter
>>>>>>>> +	  for Sigma Delta Modulators (DFSDM) driver used
>>>>>>>> +	  in various STM32 series.
>>>>>>>> +
>>>>>>>>  menu "Multimedia Capabilities Port drivers"
>>>>>>>>  	depends on ARCH_SA1100
>>>>>>>
>>>>>>> [...]
>>>>>>>
>>>>>>
>>>>>> Regards
>>>>>> Arnaud
>>>>>
>>>
> 

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

* Re: [PATCH 3/7] IIO: add bindings for STM32 DFSDM ADC driver
  2017-01-29 11:58       ` Jonathan Cameron
  (?)
@ 2017-01-29 12:42         ` Jonathan Cameron
  -1 siblings, 0 replies; 130+ messages in thread
From: Jonathan Cameron @ 2017-01-29 12:42 UTC (permalink / raw)
  To: Arnaud Pouliquen, devicetree, linux-arm-kernel, linux-iio,
	alsa-devel, Lee Jones, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Alexandre Torgue, Maxime Coquelin

On 29/01/17 11:58, Jonathan Cameron wrote:
> On 23/01/17 16:32, Arnaud Pouliquen wrote:
>> This patch adds documentation of device tree bindings for the
>> STM32 DFSDM ADC.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> Trivial bits inline.
>> ---
>>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 60 ++++++++++++++++++++++
>>  1 file changed, 60 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
>>
>> diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt  b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
>> new file mode 100644
>> index 0000000..c156bcb
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 	
>> @@ -0,0 +1,60 @@
>> +STMicroelectronics STM32 DFSDM ADC device driver
>> +
>> +STM32 DFSDM ADC is a sigma delta analog-to-digital converter.
>> +It has to be declared in device-tree as a subnode of the DFSDM mfd node.
>> +
>> +It has several multiplexed input channels. Conversions can be performed
>> +in single, scan or discontinuous mode. Conversions can be launched in software
>> +or using hardware triggers.
>> +Each instance of the driver uses one filter instance handle by the DFSDM mfd
>> +driver.
>> +
>> +DFSDM also offers extra features:
>> +-The analog watchdog feature allows the application to detect if the
>> + input voltage goes beyond the user-defined, higher or lower thresholds.
>> +-The short circuit detection allows allows the application to detect if the
>> + input is in CC.
>> +-The clock absence detection allows application to detect if SPI input is clocked.
>> +
>> +Required properties:
>> +- compatible:	Must be "st,stm32-dfsdm-adc".
>> +- reg:		Specifies the DFSDM filter instance.
>> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
>> +- st,adc-channels:	List of single-ended channels muxed for this ADC.
>> +- st,adc-channel-names:	List of single-ended channels Name.
>> +
>> +Optional properties:
>> +- st,adc-channel-types:	Single-ended channel input type. Default value is 0.
>> +			- 0: SPI with data on rising edge (default)
>> +			- 1: SPI with data on falling edge
> This is an element that can be described without magic numbers so I'd
> prefer that you do so.
> 
> Also spell check edage -> edge.
> 
>> +			- 2: manchester codec, rising edage = logic 0
>> +			- 3: manchester codec, rising edage = logic 1
>> +- st,adc-channel-clk-src: Conversion clock source. default value is 1.
>> +			  - 0: External SPI clocl (CLKIN x)
>> +			  - 1: internal SPI clock (CLKOUT) (default)
>> +			  - 2: internal SPI clock divided by 2 (falling edge).
>> +			  - 2: internal SPI clock divided by 2 (rising edge).
> 3?
>> +- st,adc-alt-channel: 	  must be defined if Two ADCs are connected on same SPI
>> +			  input.
>> +			  If not set channel n is connected to SPI input n.
>> +			  If set channel n is connected to SPI input n + 1.
> ? Two data inputs with shared clock?
>> +
>> +Example:
>> +	dfsdm: dfsdm@4400D000 {
I'd like to actually see the ADCs on the end of these explicitly
described as well.  It's entirely possible we'll have something
that isn't simply a dumb sigma delta modulator that will need
it's own driver.

In some senses what we actually have here is a data bus so we
should be able to describe things on it in that fashion
rather than just describing the data that is in coming.
Our boundary of knowledge isn't at the edge of this device!

A quick example google fed me would be the gain control on a
maxim ds8102.

Tricky bit here will be defining this nice and generally when we
currently only have the one piece of hardware to do it with ;)

That's what makes kernel development fun!

Jonathan
>> +		iio_dfsdm0: iio-dfsdm@0 {
>> +			compatible = "st,stm32-dfsdm-adc";
>> +			#io-channel-cells = <1>;
>> +			reg = <0>;
>> +			st,adc-channels = <1>;
>> +			st,adc-channel-names = "in0";
>> +		};
>> +		iio_dfsdm1: iio-dfsdm@1 {
>> +			compatible = "st,stm32-dfsdm-adc";
>> +			#io-channel-cells = <1>;
>> +			reg = <1>;
>> +			st,adc-channels = <1>;
>> +			st,adc-channel-names = "in1";
>> +			st,adc-channel-types = <1>;
>> +			st,adc-alt-channel = <1>;
>> +		};
>> +	};
>>
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

* Re: [PATCH 3/7] IIO: add bindings for STM32 DFSDM ADC driver
@ 2017-01-29 12:42         ` Jonathan Cameron
  0 siblings, 0 replies; 130+ messages in thread
From: Jonathan Cameron @ 2017-01-29 12:42 UTC (permalink / raw)
  To: Arnaud Pouliquen, devicetree, linux-arm-kernel, linux-iio,
	alsa-devel, Lee Jones, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre Torgue

On 29/01/17 11:58, Jonathan Cameron wrote:
> On 23/01/17 16:32, Arnaud Pouliquen wrote:
>> This patch adds documentation of device tree bindings for the
>> STM32 DFSDM ADC.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> Trivial bits inline.
>> ---
>>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 60 ++++++++++++++++++++++
>>  1 file changed, 60 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
>>
>> diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt  b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
>> new file mode 100644
>> index 0000000..c156bcb
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 	
>> @@ -0,0 +1,60 @@
>> +STMicroelectronics STM32 DFSDM ADC device driver
>> +
>> +STM32 DFSDM ADC is a sigma delta analog-to-digital converter.
>> +It has to be declared in device-tree as a subnode of the DFSDM mfd node.
>> +
>> +It has several multiplexed input channels. Conversions can be performed
>> +in single, scan or discontinuous mode. Conversions can be launched in software
>> +or using hardware triggers.
>> +Each instance of the driver uses one filter instance handle by the DFSDM mfd
>> +driver.
>> +
>> +DFSDM also offers extra features:
>> +-The analog watchdog feature allows the application to detect if the
>> + input voltage goes beyond the user-defined, higher or lower thresholds.
>> +-The short circuit detection allows allows the application to detect if the
>> + input is in CC.
>> +-The clock absence detection allows application to detect if SPI input is clocked.
>> +
>> +Required properties:
>> +- compatible:	Must be "st,stm32-dfsdm-adc".
>> +- reg:		Specifies the DFSDM filter instance.
>> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
>> +- st,adc-channels:	List of single-ended channels muxed for this ADC.
>> +- st,adc-channel-names:	List of single-ended channels Name.
>> +
>> +Optional properties:
>> +- st,adc-channel-types:	Single-ended channel input type. Default value is 0.
>> +			- 0: SPI with data on rising edge (default)
>> +			- 1: SPI with data on falling edge
> This is an element that can be described without magic numbers so I'd
> prefer that you do so.
> 
> Also spell check edage -> edge.
> 
>> +			- 2: manchester codec, rising edage = logic 0
>> +			- 3: manchester codec, rising edage = logic 1
>> +- st,adc-channel-clk-src: Conversion clock source. default value is 1.
>> +			  - 0: External SPI clocl (CLKIN x)
>> +			  - 1: internal SPI clock (CLKOUT) (default)
>> +			  - 2: internal SPI clock divided by 2 (falling edge).
>> +			  - 2: internal SPI clock divided by 2 (rising edge).
> 3?
>> +- st,adc-alt-channel: 	  must be defined if Two ADCs are connected on same SPI
>> +			  input.
>> +			  If not set channel n is connected to SPI input n.
>> +			  If set channel n is connected to SPI input n + 1.
> ? Two data inputs with shared clock?
>> +
>> +Example:
>> +	dfsdm: dfsdm@4400D000 {
I'd like to actually see the ADCs on the end of these explicitly
described as well.  It's entirely possible we'll have something
that isn't simply a dumb sigma delta modulator that will need
it's own driver.

In some senses what we actually have here is a data bus so we
should be able to describe things on it in that fashion
rather than just describing the data that is in coming.
Our boundary of knowledge isn't at the edge of this device!

A quick example google fed me would be the gain control on a
maxim ds8102.

Tricky bit here will be defining this nice and generally when we
currently only have the one piece of hardware to do it with ;)

That's what makes kernel development fun!

Jonathan
>> +		iio_dfsdm0: iio-dfsdm@0 {
>> +			compatible = "st,stm32-dfsdm-adc";
>> +			#io-channel-cells = <1>;
>> +			reg = <0>;
>> +			st,adc-channels = <1>;
>> +			st,adc-channel-names = "in0";
>> +		};
>> +		iio_dfsdm1: iio-dfsdm@1 {
>> +			compatible = "st,stm32-dfsdm-adc";
>> +			#io-channel-cells = <1>;
>> +			reg = <1>;
>> +			st,adc-channels = <1>;
>> +			st,adc-channel-names = "in1";
>> +			st,adc-channel-types = <1>;
>> +			st,adc-alt-channel = <1>;
>> +		};
>> +	};
>>
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 


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

* [PATCH 3/7] IIO: add bindings for STM32 DFSDM ADC driver
@ 2017-01-29 12:42         ` Jonathan Cameron
  0 siblings, 0 replies; 130+ messages in thread
From: Jonathan Cameron @ 2017-01-29 12:42 UTC (permalink / raw)
  To: linux-arm-kernel

On 29/01/17 11:58, Jonathan Cameron wrote:
> On 23/01/17 16:32, Arnaud Pouliquen wrote:
>> This patch adds documentation of device tree bindings for the
>> STM32 DFSDM ADC.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> Trivial bits inline.
>> ---
>>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 60 ++++++++++++++++++++++
>>  1 file changed, 60 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
>>
>> diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt  b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
>> new file mode 100644
>> index 0000000..c156bcb
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 	
>> @@ -0,0 +1,60 @@
>> +STMicroelectronics STM32 DFSDM ADC device driver
>> +
>> +STM32 DFSDM ADC is a sigma delta analog-to-digital converter.
>> +It has to be declared in device-tree as a subnode of the DFSDM mfd node.
>> +
>> +It has several multiplexed input channels. Conversions can be performed
>> +in single, scan or discontinuous mode. Conversions can be launched in software
>> +or using hardware triggers.
>> +Each instance of the driver uses one filter instance handle by the DFSDM mfd
>> +driver.
>> +
>> +DFSDM also offers extra features:
>> +-The analog watchdog feature allows the application to detect if the
>> + input voltage goes beyond the user-defined, higher or lower thresholds.
>> +-The short circuit detection allows allows the application to detect if the
>> + input is in CC.
>> +-The clock absence detection allows application to detect if SPI input is clocked.
>> +
>> +Required properties:
>> +- compatible:	Must be "st,stm32-dfsdm-adc".
>> +- reg:		Specifies the DFSDM filter instance.
>> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
>> +- st,adc-channels:	List of single-ended channels muxed for this ADC.
>> +- st,adc-channel-names:	List of single-ended channels Name.
>> +
>> +Optional properties:
>> +- st,adc-channel-types:	Single-ended channel input type. Default value is 0.
>> +			- 0: SPI with data on rising edge (default)
>> +			- 1: SPI with data on falling edge
> This is an element that can be described without magic numbers so I'd
> prefer that you do so.
> 
> Also spell check edage -> edge.
> 
>> +			- 2: manchester codec, rising edage = logic 0
>> +			- 3: manchester codec, rising edage = logic 1
>> +- st,adc-channel-clk-src: Conversion clock source. default value is 1.
>> +			  - 0: External SPI clocl (CLKIN x)
>> +			  - 1: internal SPI clock (CLKOUT) (default)
>> +			  - 2: internal SPI clock divided by 2 (falling edge).
>> +			  - 2: internal SPI clock divided by 2 (rising edge).
> 3?
>> +- st,adc-alt-channel: 	  must be defined if Two ADCs are connected on same SPI
>> +			  input.
>> +			  If not set channel n is connected to SPI input n.
>> +			  If set channel n is connected to SPI input n + 1.
> ? Two data inputs with shared clock?
>> +
>> +Example:
>> +	dfsdm: dfsdm at 4400D000 {
I'd like to actually see the ADCs on the end of these explicitly
described as well.  It's entirely possible we'll have something
that isn't simply a dumb sigma delta modulator that will need
it's own driver.

In some senses what we actually have here is a data bus so we
should be able to describe things on it in that fashion
rather than just describing the data that is in coming.
Our boundary of knowledge isn't at the edge of this device!

A quick example google fed me would be the gain control on a
maxim ds8102.

Tricky bit here will be defining this nice and generally when we
currently only have the one piece of hardware to do it with ;)

That's what makes kernel development fun!

Jonathan
>> +		iio_dfsdm0: iio-dfsdm at 0 {
>> +			compatible = "st,stm32-dfsdm-adc";
>> +			#io-channel-cells = <1>;
>> +			reg = <0>;
>> +			st,adc-channels = <1>;
>> +			st,adc-channel-names = "in0";
>> +		};
>> +		iio_dfsdm1: iio-dfsdm at 1 {
>> +			compatible = "st,stm32-dfsdm-adc";
>> +			#io-channel-cells = <1>;
>> +			reg = <1>;
>> +			st,adc-channels = <1>;
>> +			st,adc-channel-names = "in1";
>> +			st,adc-channel-types = <1>;
>> +			st,adc-alt-channel = <1>;
>> +		};
>> +	};
>>
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
  2017-01-29 12:28                           ` Jonathan Cameron
  (?)
@ 2017-01-29 14:19                             ` Lars-Peter Clausen
  -1 siblings, 0 replies; 130+ messages in thread
From: Lars-Peter Clausen @ 2017-01-29 14:19 UTC (permalink / raw)
  To: Jonathan Cameron, Lee Jones, Arnaud Pouliquen
  Cc: Mark Rutland, devicetree, alsa-devel, Maxime Coquelin,
	Liam Girdwood, linux-iio, Mark Brown, Takashi Iwai,
	Jaroslav Kysela, Rob Herring, Peter Meerwald-Stadler,
	Hartmut Knaack, linux-arm-kernel, Alexandre TORGUE

On 01/29/2017 01:28 PM, Jonathan Cameron wrote:
[...]
>>> Jonathan, Mark, Please could you share your opinion on this topic?
> Hmm - based on a fairly quick read through of the code (which is never
> ideal!). I can see that the ideal would indeed be as Lee says, to
> expand the IIO interfaces sufficiently to support what you need.
> 
> 
> So, reading the code (fairly quickly I'm afraid as had a lot of reviews
> to catch up on this weekend).
> What we need:
> 1) DMA support in the ADC driver.  This would be a good anyway!
> 2) DMA consumer support - I defer to Lars for comments on this.
> 3) Means of describing and controlling the sinc filters applied. 
> 4) Appropriate channel support.  I'm not convinced that it doesn't make
> sense to have IIO channels for the microphones - at least in a streaming
> mode.  It's data - I don't really care what ;)
> Coarsely it's a filtered pulse per period counter which is
> a perfectly valid type to have a channel for.
> 
> The big question to my mind is the DMA consumer support. How would
> it work. It it wouldn't this is somewhat of a non starter.
> 
> To bring up another slightly ugly MFD case where it is borderline
> on whether an MFD makes sense (just as a reference point of something
> we have discussed a few times before)
> 
> ADCs with features directed at touchscreen support.
> These are odd as the ADC bit is generic, but the specific output
> and read sequences used for touchscreen reading don't correspond to
> anything that makes any real sense for other applications.
> 
> We have started to get hybrid drives that have an MFD underneath but
> do the ADC reads through IIO consumer interfaces, and the timing
> control from a touchscreen driver.  We haven't really gotten this
> one right yet either.
> 
> Here however, to my mind things are different - as I read it
> (and feel free to point out what I'm missing), the sound usecase
> is just a question of setting up sampling frequencies and filters
> appropriate to the microphones and what ASoC expects?
> 
> That's not to say the IIO dma stuff is flexible enough (yet) to
> handle the data flows, but perhaps we can work towards that.

Yeah, so this is a bit different, but not unexpected. And I'm sure we'll see
more similar hardware in the future. I've talked about this before[1], the
cost structure of creating and manufacturing new hardware drives the design
in a certain direction so that we end up with general purpose hardware that
suddenly has applications in multiple frameworks that were previously fully
orthogonal.

This device is certainly not a multi-function-device. It only has one
function, it's a sigma-delta demodulator. It is rather a
multi-purpose-device. It can be used for sigma-delta demodulation in audio
applications as well as more specialized data capture applications.

It's comparable to something like a GPIO that can be used to control a reset
pin or turn on and off a LED. The GPIO chip is not considered
multi-function-device though, even though it can be used for many different
applications.

As for DMA we already have a lot of DMA infrastructure on the audio side and
we probably want to reuse that rather than inserting IIO as a middle layer
since audio buffer capture as different requirements from IIO buffer and
we'd have to go the route of the least common denominator and loose
expressibility in the process.

I've created a IIO buffer[2] that does not capture data to memory but is
only used to enable/disable the data capture process. We use this in setups
where the data is passed from the converter to a application specific
processing chain without ever going through system memory. This buffer could
probably also be used here on the audio side to control the converter state.

- Lars

[1]
https://lists.linuxfoundation.org/pipermail/ksummit-discuss/2016-July/003029.html

[2]
https://github.com/analogdevicesinc/linux/blob/xcomm_zynq/drivers/iio/buffer/hw_consumer.c

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-29 14:19                             ` Lars-Peter Clausen
  0 siblings, 0 replies; 130+ messages in thread
From: Lars-Peter Clausen @ 2017-01-29 14:19 UTC (permalink / raw)
  To: Jonathan Cameron, Lee Jones, Arnaud Pouliquen
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, Rob Herring,
	Mark Rutland, Hartmut Knaack, Peter Meerwald-Stadler,
	Jaroslav Kysela, Takashi Iwai, Liam Girdwood, Mark Brown,
	Maxime Coquelin, Alexandre TORGUE

On 01/29/2017 01:28 PM, Jonathan Cameron wrote:
[...]
>>> Jonathan, Mark, Please could you share your opinion on this topic?
> Hmm - based on a fairly quick read through of the code (which is never
> ideal!). I can see that the ideal would indeed be as Lee says, to
> expand the IIO interfaces sufficiently to support what you need.
> 
> 
> So, reading the code (fairly quickly I'm afraid as had a lot of reviews
> to catch up on this weekend).
> What we need:
> 1) DMA support in the ADC driver.  This would be a good anyway!
> 2) DMA consumer support - I defer to Lars for comments on this.
> 3) Means of describing and controlling the sinc filters applied. 
> 4) Appropriate channel support.  I'm not convinced that it doesn't make
> sense to have IIO channels for the microphones - at least in a streaming
> mode.  It's data - I don't really care what ;)
> Coarsely it's a filtered pulse per period counter which is
> a perfectly valid type to have a channel for.
> 
> The big question to my mind is the DMA consumer support. How would
> it work. It it wouldn't this is somewhat of a non starter.
> 
> To bring up another slightly ugly MFD case where it is borderline
> on whether an MFD makes sense (just as a reference point of something
> we have discussed a few times before)
> 
> ADCs with features directed at touchscreen support.
> These are odd as the ADC bit is generic, but the specific output
> and read sequences used for touchscreen reading don't correspond to
> anything that makes any real sense for other applications.
> 
> We have started to get hybrid drives that have an MFD underneath but
> do the ADC reads through IIO consumer interfaces, and the timing
> control from a touchscreen driver.  We haven't really gotten this
> one right yet either.
> 
> Here however, to my mind things are different - as I read it
> (and feel free to point out what I'm missing), the sound usecase
> is just a question of setting up sampling frequencies and filters
> appropriate to the microphones and what ASoC expects?
> 
> That's not to say the IIO dma stuff is flexible enough (yet) to
> handle the data flows, but perhaps we can work towards that.

Yeah, so this is a bit different, but not unexpected. And I'm sure we'll see
more similar hardware in the future. I've talked about this before[1], the
cost structure of creating and manufacturing new hardware drives the design
in a certain direction so that we end up with general purpose hardware that
suddenly has applications in multiple frameworks that were previously fully
orthogonal.

This device is certainly not a multi-function-device. It only has one
function, it's a sigma-delta demodulator. It is rather a
multi-purpose-device. It can be used for sigma-delta demodulation in audio
applications as well as more specialized data capture applications.

It's comparable to something like a GPIO that can be used to control a reset
pin or turn on and off a LED. The GPIO chip is not considered
multi-function-device though, even though it can be used for many different
applications.

As for DMA we already have a lot of DMA infrastructure on the audio side and
we probably want to reuse that rather than inserting IIO as a middle layer
since audio buffer capture as different requirements from IIO buffer and
we'd have to go the route of the least common denominator and loose
expressibility in the process.

I've created a IIO buffer[2] that does not capture data to memory but is
only used to enable/disable the data capture process. We use this in setups
where the data is passed from the converter to a application specific
processing chain without ever going through system memory. This buffer could
probably also be used here on the audio side to control the converter state.

- Lars

[1]
https://lists.linuxfoundation.org/pipermail/ksummit-discuss/2016-July/003029.html

[2]
https://github.com/analogdevicesinc/linux/blob/xcomm_zynq/drivers/iio/buffer/hw_consumer.c

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

* [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-29 14:19                             ` Lars-Peter Clausen
  0 siblings, 0 replies; 130+ messages in thread
From: Lars-Peter Clausen @ 2017-01-29 14:19 UTC (permalink / raw)
  To: linux-arm-kernel

On 01/29/2017 01:28 PM, Jonathan Cameron wrote:
[...]
>>> Jonathan, Mark, Please could you share your opinion on this topic?
> Hmm - based on a fairly quick read through of the code (which is never
> ideal!). I can see that the ideal would indeed be as Lee says, to
> expand the IIO interfaces sufficiently to support what you need.
> 
> 
> So, reading the code (fairly quickly I'm afraid as had a lot of reviews
> to catch up on this weekend).
> What we need:
> 1) DMA support in the ADC driver.  This would be a good anyway!
> 2) DMA consumer support - I defer to Lars for comments on this.
> 3) Means of describing and controlling the sinc filters applied. 
> 4) Appropriate channel support.  I'm not convinced that it doesn't make
> sense to have IIO channels for the microphones - at least in a streaming
> mode.  It's data - I don't really care what ;)
> Coarsely it's a filtered pulse per period counter which is
> a perfectly valid type to have a channel for.
> 
> The big question to my mind is the DMA consumer support. How would
> it work. It it wouldn't this is somewhat of a non starter.
> 
> To bring up another slightly ugly MFD case where it is borderline
> on whether an MFD makes sense (just as a reference point of something
> we have discussed a few times before)
> 
> ADCs with features directed at touchscreen support.
> These are odd as the ADC bit is generic, but the specific output
> and read sequences used for touchscreen reading don't correspond to
> anything that makes any real sense for other applications.
> 
> We have started to get hybrid drives that have an MFD underneath but
> do the ADC reads through IIO consumer interfaces, and the timing
> control from a touchscreen driver.  We haven't really gotten this
> one right yet either.
> 
> Here however, to my mind things are different - as I read it
> (and feel free to point out what I'm missing), the sound usecase
> is just a question of setting up sampling frequencies and filters
> appropriate to the microphones and what ASoC expects?
> 
> That's not to say the IIO dma stuff is flexible enough (yet) to
> handle the data flows, but perhaps we can work towards that.

Yeah, so this is a bit different, but not unexpected. And I'm sure we'll see
more similar hardware in the future. I've talked about this before[1], the
cost structure of creating and manufacturing new hardware drives the design
in a certain direction so that we end up with general purpose hardware that
suddenly has applications in multiple frameworks that were previously fully
orthogonal.

This device is certainly not a multi-function-device. It only has one
function, it's a sigma-delta demodulator. It is rather a
multi-purpose-device. It can be used for sigma-delta demodulation in audio
applications as well as more specialized data capture applications.

It's comparable to something like a GPIO that can be used to control a reset
pin or turn on and off a LED. The GPIO chip is not considered
multi-function-device though, even though it can be used for many different
applications.

As for DMA we already have a lot of DMA infrastructure on the audio side and
we probably want to reuse that rather than inserting IIO as a middle layer
since audio buffer capture as different requirements from IIO buffer and
we'd have to go the route of the least common denominator and loose
expressibility in the process.

I've created a IIO buffer[2] that does not capture data to memory but is
only used to enable/disable the data capture process. We use this in setups
where the data is passed from the converter to a application specific
processing chain without ever going through system memory. This buffer could
probably also be used here on the audio side to control the converter state.

- Lars

[1]
https://lists.linuxfoundation.org/pipermail/ksummit-discuss/2016-July/003029.html

[2]
https://github.com/analogdevicesinc/linux/blob/xcomm_zynq/drivers/iio/buffer/hw_consumer.c

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
  2017-01-29 14:19                             ` Lars-Peter Clausen
  (?)
@ 2017-01-29 14:34                               ` Lars-Peter Clausen
  -1 siblings, 0 replies; 130+ messages in thread
From: Lars-Peter Clausen @ 2017-01-29 14:34 UTC (permalink / raw)
  To: Jonathan Cameron, Lee Jones, Arnaud Pouliquen
  Cc: Mark Rutland, devicetree, alsa-devel, Maxime Coquelin,
	Liam Girdwood, linux-iio, Mark Brown, Takashi Iwai,
	Jaroslav Kysela, Rob Herring, Peter Meerwald-Stadler,
	Hartmut Knaack, linux-arm-kernel, Alexandre TORGUE

On 01/29/2017 03:19 PM, Lars-Peter Clausen wrote:
> On 01/29/2017 01:28 PM, Jonathan Cameron wrote:
> [...]
>>>> Jonathan, Mark, Please could you share your opinion on this topic?
>> Hmm - based on a fairly quick read through of the code (which is never
>> ideal!). I can see that the ideal would indeed be as Lee says, to
>> expand the IIO interfaces sufficiently to support what you need.
>>
>>
>> So, reading the code (fairly quickly I'm afraid as had a lot of reviews
>> to catch up on this weekend).
>> What we need:
>> 1) DMA support in the ADC driver.  This would be a good anyway!
>> 2) DMA consumer support - I defer to Lars for comments on this.
>> 3) Means of describing and controlling the sinc filters applied. 
>> 4) Appropriate channel support.  I'm not convinced that it doesn't make
>> sense to have IIO channels for the microphones - at least in a streaming
>> mode.  It's data - I don't really care what ;)
>> Coarsely it's a filtered pulse per period counter which is
>> a perfectly valid type to have a channel for.
>>
>> The big question to my mind is the DMA consumer support. How would
>> it work. It it wouldn't this is somewhat of a non starter.
>>
>> To bring up another slightly ugly MFD case where it is borderline
>> on whether an MFD makes sense (just as a reference point of something
>> we have discussed a few times before)
>>
>> ADCs with features directed at touchscreen support.
>> These are odd as the ADC bit is generic, but the specific output
>> and read sequences used for touchscreen reading don't correspond to
>> anything that makes any real sense for other applications.
>>
>> We have started to get hybrid drives that have an MFD underneath but
>> do the ADC reads through IIO consumer interfaces, and the timing
>> control from a touchscreen driver.  We haven't really gotten this
>> one right yet either.
>>
>> Here however, to my mind things are different - as I read it
>> (and feel free to point out what I'm missing), the sound usecase
>> is just a question of setting up sampling frequencies and filters
>> appropriate to the microphones and what ASoC expects?
>>
>> That's not to say the IIO dma stuff is flexible enough (yet) to
>> handle the data flows, but perhaps we can work towards that.
> 
> Yeah, so this is a bit different, but not unexpected. And I'm sure we'll see
> more similar hardware in the future. I've talked about this before[1], the
> cost structure of creating and manufacturing new hardware drives the design
> in a certain direction so that we end up with general purpose hardware that
> suddenly has applications in multiple frameworks that were previously fully
> orthogonal.
> 
> This device is certainly not a multi-function-device. It only has one
> function, it's a sigma-delta demodulator. It is rather a
> multi-purpose-device. It can be used for sigma-delta demodulation in audio
> applications as well as more specialized data capture applications.
> 
> It's comparable to something like a GPIO that can be used to control a reset
> pin or turn on and off a LED. The GPIO chip is not considered
> multi-function-device though, even though it can be used for many different
> applications.
> 
> As for DMA we already have a lot of DMA infrastructure on the audio side and
> we probably want to reuse that rather than inserting IIO as a middle layer
> since audio buffer capture as different requirements from IIO buffer and
> we'd have to go the route of the least common denominator and loose
> expressibility in the process.
> 
> I've created a IIO buffer[2] that does not capture data to memory but is
> only used to enable/disable the data capture process. We use this in setups
> where the data is passed from the converter to a application specific
> processing chain without ever going through system memory. This buffer could
> probably also be used here on the audio side to control the converter state.

I forgot to mention. I think the first thing we should do is work on
terminology. This is not an ADC, this is a configurable low-pass-filter.

It works in conjunction with a analog frontend (ADC) that produces a 1-bit
pulse-density-modulated stream, takes that stream and converts it into N-bit
PCM samples. The PCM samples are generated at a fraction of the PDM stream
samplerate that corresponds to the decimation factor.

This is not an unusual device. Many audio CODEC and audio controllers
contain such a core as well as most SigmaDelta converters supported by IIO.
What is special about this part is that it is a dedicated core that is not
embedded in some other hardware component. This creates greater flexibility,
but of course also greater complexity that is required to manage all that
flexibility.

We shouldn't codify anything about the kernel internal frameworks through
which the device might be exposed into the devicetree. We should accurately
describe the hardware (including the analog frontend) and then create a
appropriate software structures to handle them.	

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-29 14:34                               ` Lars-Peter Clausen
  0 siblings, 0 replies; 130+ messages in thread
From: Lars-Peter Clausen @ 2017-01-29 14:34 UTC (permalink / raw)
  To: Jonathan Cameron, Lee Jones, Arnaud Pouliquen
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, Rob Herring,
	Mark Rutland, Hartmut Knaack, Peter Meerwald-Stadler,
	Jaroslav Kysela, Takashi Iwai, Liam Girdwood, Mark Brown,
	Maxime Coquelin, Alexandre TORGUE

On 01/29/2017 03:19 PM, Lars-Peter Clausen wrote:
> On 01/29/2017 01:28 PM, Jonathan Cameron wrote:
> [...]
>>>> Jonathan, Mark, Please could you share your opinion on this topic?
>> Hmm - based on a fairly quick read through of the code (which is never
>> ideal!). I can see that the ideal would indeed be as Lee says, to
>> expand the IIO interfaces sufficiently to support what you need.
>>
>>
>> So, reading the code (fairly quickly I'm afraid as had a lot of reviews
>> to catch up on this weekend).
>> What we need:
>> 1) DMA support in the ADC driver.  This would be a good anyway!
>> 2) DMA consumer support - I defer to Lars for comments on this.
>> 3) Means of describing and controlling the sinc filters applied. 
>> 4) Appropriate channel support.  I'm not convinced that it doesn't make
>> sense to have IIO channels for the microphones - at least in a streaming
>> mode.  It's data - I don't really care what ;)
>> Coarsely it's a filtered pulse per period counter which is
>> a perfectly valid type to have a channel for.
>>
>> The big question to my mind is the DMA consumer support. How would
>> it work. It it wouldn't this is somewhat of a non starter.
>>
>> To bring up another slightly ugly MFD case where it is borderline
>> on whether an MFD makes sense (just as a reference point of something
>> we have discussed a few times before)
>>
>> ADCs with features directed at touchscreen support.
>> These are odd as the ADC bit is generic, but the specific output
>> and read sequences used for touchscreen reading don't correspond to
>> anything that makes any real sense for other applications.
>>
>> We have started to get hybrid drives that have an MFD underneath but
>> do the ADC reads through IIO consumer interfaces, and the timing
>> control from a touchscreen driver.  We haven't really gotten this
>> one right yet either.
>>
>> Here however, to my mind things are different - as I read it
>> (and feel free to point out what I'm missing), the sound usecase
>> is just a question of setting up sampling frequencies and filters
>> appropriate to the microphones and what ASoC expects?
>>
>> That's not to say the IIO dma stuff is flexible enough (yet) to
>> handle the data flows, but perhaps we can work towards that.
> 
> Yeah, so this is a bit different, but not unexpected. And I'm sure we'll see
> more similar hardware in the future. I've talked about this before[1], the
> cost structure of creating and manufacturing new hardware drives the design
> in a certain direction so that we end up with general purpose hardware that
> suddenly has applications in multiple frameworks that were previously fully
> orthogonal.
> 
> This device is certainly not a multi-function-device. It only has one
> function, it's a sigma-delta demodulator. It is rather a
> multi-purpose-device. It can be used for sigma-delta demodulation in audio
> applications as well as more specialized data capture applications.
> 
> It's comparable to something like a GPIO that can be used to control a reset
> pin or turn on and off a LED. The GPIO chip is not considered
> multi-function-device though, even though it can be used for many different
> applications.
> 
> As for DMA we already have a lot of DMA infrastructure on the audio side and
> we probably want to reuse that rather than inserting IIO as a middle layer
> since audio buffer capture as different requirements from IIO buffer and
> we'd have to go the route of the least common denominator and loose
> expressibility in the process.
> 
> I've created a IIO buffer[2] that does not capture data to memory but is
> only used to enable/disable the data capture process. We use this in setups
> where the data is passed from the converter to a application specific
> processing chain without ever going through system memory. This buffer could
> probably also be used here on the audio side to control the converter state.

I forgot to mention. I think the first thing we should do is work on
terminology. This is not an ADC, this is a configurable low-pass-filter.

It works in conjunction with a analog frontend (ADC) that produces a 1-bit
pulse-density-modulated stream, takes that stream and converts it into N-bit
PCM samples. The PCM samples are generated at a fraction of the PDM stream
samplerate that corresponds to the decimation factor.

This is not an unusual device. Many audio CODEC and audio controllers
contain such a core as well as most SigmaDelta converters supported by IIO.
What is special about this part is that it is a dedicated core that is not
embedded in some other hardware component. This creates greater flexibility,
but of course also greater complexity that is required to manage all that
flexibility.

We shouldn't codify anything about the kernel internal frameworks through
which the device might be exposed into the devicetree. We should accurately
describe the hardware (including the analog frontend) and then create a
appropriate software structures to handle them.	

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

* [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-29 14:34                               ` Lars-Peter Clausen
  0 siblings, 0 replies; 130+ messages in thread
From: Lars-Peter Clausen @ 2017-01-29 14:34 UTC (permalink / raw)
  To: linux-arm-kernel

On 01/29/2017 03:19 PM, Lars-Peter Clausen wrote:
> On 01/29/2017 01:28 PM, Jonathan Cameron wrote:
> [...]
>>>> Jonathan, Mark, Please could you share your opinion on this topic?
>> Hmm - based on a fairly quick read through of the code (which is never
>> ideal!). I can see that the ideal would indeed be as Lee says, to
>> expand the IIO interfaces sufficiently to support what you need.
>>
>>
>> So, reading the code (fairly quickly I'm afraid as had a lot of reviews
>> to catch up on this weekend).
>> What we need:
>> 1) DMA support in the ADC driver.  This would be a good anyway!
>> 2) DMA consumer support - I defer to Lars for comments on this.
>> 3) Means of describing and controlling the sinc filters applied. 
>> 4) Appropriate channel support.  I'm not convinced that it doesn't make
>> sense to have IIO channels for the microphones - at least in a streaming
>> mode.  It's data - I don't really care what ;)
>> Coarsely it's a filtered pulse per period counter which is
>> a perfectly valid type to have a channel for.
>>
>> The big question to my mind is the DMA consumer support. How would
>> it work. It it wouldn't this is somewhat of a non starter.
>>
>> To bring up another slightly ugly MFD case where it is borderline
>> on whether an MFD makes sense (just as a reference point of something
>> we have discussed a few times before)
>>
>> ADCs with features directed at touchscreen support.
>> These are odd as the ADC bit is generic, but the specific output
>> and read sequences used for touchscreen reading don't correspond to
>> anything that makes any real sense for other applications.
>>
>> We have started to get hybrid drives that have an MFD underneath but
>> do the ADC reads through IIO consumer interfaces, and the timing
>> control from a touchscreen driver.  We haven't really gotten this
>> one right yet either.
>>
>> Here however, to my mind things are different - as I read it
>> (and feel free to point out what I'm missing), the sound usecase
>> is just a question of setting up sampling frequencies and filters
>> appropriate to the microphones and what ASoC expects?
>>
>> That's not to say the IIO dma stuff is flexible enough (yet) to
>> handle the data flows, but perhaps we can work towards that.
> 
> Yeah, so this is a bit different, but not unexpected. And I'm sure we'll see
> more similar hardware in the future. I've talked about this before[1], the
> cost structure of creating and manufacturing new hardware drives the design
> in a certain direction so that we end up with general purpose hardware that
> suddenly has applications in multiple frameworks that were previously fully
> orthogonal.
> 
> This device is certainly not a multi-function-device. It only has one
> function, it's a sigma-delta demodulator. It is rather a
> multi-purpose-device. It can be used for sigma-delta demodulation in audio
> applications as well as more specialized data capture applications.
> 
> It's comparable to something like a GPIO that can be used to control a reset
> pin or turn on and off a LED. The GPIO chip is not considered
> multi-function-device though, even though it can be used for many different
> applications.
> 
> As for DMA we already have a lot of DMA infrastructure on the audio side and
> we probably want to reuse that rather than inserting IIO as a middle layer
> since audio buffer capture as different requirements from IIO buffer and
> we'd have to go the route of the least common denominator and loose
> expressibility in the process.
> 
> I've created a IIO buffer[2] that does not capture data to memory but is
> only used to enable/disable the data capture process. We use this in setups
> where the data is passed from the converter to a application specific
> processing chain without ever going through system memory. This buffer could
> probably also be used here on the audio side to control the converter state.

I forgot to mention. I think the first thing we should do is work on
terminology. This is not an ADC, this is a configurable low-pass-filter.

It works in conjunction with a analog frontend (ADC) that produces a 1-bit
pulse-density-modulated stream, takes that stream and converts it into N-bit
PCM samples. The PCM samples are generated at a fraction of the PDM stream
samplerate that corresponds to the decimation factor.

This is not an unusual device. Many audio CODEC and audio controllers
contain such a core as well as most SigmaDelta converters supported by IIO.
What is special about this part is that it is a dedicated core that is not
embedded in some other hardware component. This creates greater flexibility,
but of course also greater complexity that is required to manage all that
flexibility.

We shouldn't codify anything about the kernel internal frameworks through
which the device might be exposed into the devicetree. We should accurately
describe the hardware (including the analog frontend) and then create a
appropriate software structures to handle them.	

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
  2017-01-27 13:45                     ` Arnaud Pouliquen
  (?)
@ 2017-01-29 17:50                         ` Mark Brown
  -1 siblings, 0 replies; 130+ messages in thread
From: Mark Brown @ 2017-01-29 17:50 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: Lee Jones, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Rob Herring, Mark Rutland,
	Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Jaroslav Kysela, Takashi Iwai,
	Liam Girdwood, Maxime Coquelin, Alexandre TORGUE

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

On Fri, Jan 27, 2017 at 02:45:49PM +0100, Arnaud Pouliquen wrote:

> Jonathan, Mark, Please could you share your opinion on this topic?

I really don't have enough visbility of what the hardware or software is
doing to say much and though there's extensive backtrace in here it's
not enough to follow.  If it's a generic ADC that happens to have audio
applications then some relationship with IIO might make sense but I
don't know how separate the audio and general purpose bits of the
hardware are.

Please understand that I get copied in on *lots* of threads about MFDs
with drivers getting posted repeatedly for whatever revisions are needed
with little relevance to me so it's very random if I even look at these
things, much less read them with detail.

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

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-29 17:50                         ` Mark Brown
  0 siblings, 0 replies; 130+ messages in thread
From: Mark Brown @ 2017-01-29 17:50 UTC (permalink / raw)
  To: Arnaud Pouliquen
  Cc: Lee Jones, devicetree, linux-arm-kernel, linux-iio, alsa-devel,
	Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Maxime Coquelin, Alexandre TORGUE

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

On Fri, Jan 27, 2017 at 02:45:49PM +0100, Arnaud Pouliquen wrote:

> Jonathan, Mark, Please could you share your opinion on this topic?

I really don't have enough visbility of what the hardware or software is
doing to say much and though there's extensive backtrace in here it's
not enough to follow.  If it's a generic ADC that happens to have audio
applications then some relationship with IIO might make sense but I
don't know how separate the audio and general purpose bits of the
hardware are.

Please understand that I get copied in on *lots* of threads about MFDs
with drivers getting posted repeatedly for whatever revisions are needed
with little relevance to me so it's very random if I even look at these
things, much less read them with detail.

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

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

* [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-29 17:50                         ` Mark Brown
  0 siblings, 0 replies; 130+ messages in thread
From: Mark Brown @ 2017-01-29 17:50 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Jan 27, 2017 at 02:45:49PM +0100, Arnaud Pouliquen wrote:

> Jonathan, Mark, Please could you share your opinion on this topic?

I really don't have enough visbility of what the hardware or software is
doing to say much and though there's extensive backtrace in here it's
not enough to follow.  If it's a generic ADC that happens to have audio
applications then some relationship with IIO might make sense but I
don't know how separate the audio and general purpose bits of the
hardware are.

Please understand that I get copied in on *lots* of threads about MFDs
with drivers getting posted repeatedly for whatever revisions are needed
with little relevance to me so it's very random if I even look at these
things, much less read them with detail.
-------------- 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/20170129/1d63eeb1/attachment.sig>

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
  2017-01-29 14:34                               ` Lars-Peter Clausen
  (?)
@ 2017-01-30 11:13                                 ` Arnaud Pouliquen
  -1 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-30 11:13 UTC (permalink / raw)
  To: Lars-Peter Clausen, Jonathan Cameron, Lee Jones
  Cc: Mark Rutland, devicetree, alsa-devel, Maxime Coquelin,
	Liam Girdwood, linux-iio, Mark Brown, Takashi Iwai,
	Jaroslav Kysela, Rob Herring, Peter Meerwald-Stadler,
	Hartmut Knaack, linux-arm-kernel, Alexandre TORGUE

Hello,

Thanks everyone for you feedback!
My comments below.

On 01/29/2017 03:34 PM, Lars-Peter Clausen wrote:
> On 01/29/2017 03:19 PM, Lars-Peter Clausen wrote:
>> On 01/29/2017 01:28 PM, Jonathan Cameron wrote:
>> [...]
>>>>> Jonathan, Mark, Please could you share your opinion on this topic?
>>> Hmm - based on a fairly quick read through of the code (which is never
>>> ideal!). I can see that the ideal would indeed be as Lee says, to
>>> expand the IIO interfaces sufficiently to support what you need.
>>>
>>>
>>> So, reading the code (fairly quickly I'm afraid as had a lot of reviews
>>> to catch up on this weekend).
>>> What we need:
>>> 1) DMA support in the ADC driver.  This would be a good anyway!
>>> 2) DMA consumer support - I defer to Lars for comments on this.
>>> 3) Means of describing and controlling the sinc filters applied. 
>>> 4) Appropriate channel support.  I'm not convinced that it doesn't make
>>> sense to have IIO channels for the microphones - at least in a streaming
>>> mode.  It's data - I don't really care what ;)
>>> Coarsely it's a filtered pulse per period counter which is
>>> a perfectly valid type to have a channel for.
>>>
>>> The big question to my mind is the DMA consumer support. How would
>>> it work. It it wouldn't this is somewhat of a non starter.
>>>
>>> To bring up another slightly ugly MFD case where it is borderline
>>> on whether an MFD makes sense (just as a reference point of something
>>> we have discussed a few times before)
>>>
>>> ADCs with features directed at touchscreen support.
>>> These are odd as the ADC bit is generic, but the specific output
>>> and read sequences used for touchscreen reading don't correspond to
>>> anything that makes any real sense for other applications.
>>>
>>> We have started to get hybrid drives that have an MFD underneath but
>>> do the ADC reads through IIO consumer interfaces, and the timing
>>> control from a touchscreen driver.  We haven't really gotten this
>>> one right yet either.
>>>
>>> Here however, to my mind things are different - as I read it
>>> (and feel free to point out what I'm missing), the sound usecase
>>> is just a question of setting up sampling frequencies and filters
>>> appropriate to the microphones and what ASoC expects?
>>>
>>> That's not to say the IIO dma stuff is flexible enough (yet) to
>>> handle the data flows, but perhaps we can work towards that.
>>
>> Yeah, so this is a bit different, but not unexpected. And I'm sure we'll see
>> more similar hardware in the future. I've talked about this before[1], the
>> cost structure of creating and manufacturing new hardware drives the design
>> in a certain direction so that we end up with general purpose hardware that
>> suddenly has applications in multiple frameworks that were previously fully
>> orthogonal.
>>
>> This device is certainly not a multi-function-device. It only has one
>> function, it's a sigma-delta demodulator. It is rather a
>> multi-purpose-device. It can be used for sigma-delta demodulation in audio
>> applications as well as more specialized data capture applications.
>>
>> It's comparable to something like a GPIO that can be used to control a reset
>> pin or turn on and off a LED. The GPIO chip is not considered
>> multi-function-device though, even though it can be used for many different
>> applications.
>>
>> As for DMA we already have a lot of DMA infrastructure on the audio side and
>> we probably want to reuse that rather than inserting IIO as a middle layer
>> since audio buffer capture as different requirements from IIO buffer and
>> we'd have to go the route of the least common denominator and loose
>> expressibility in the process.
>>
>> I've created a IIO buffer[2] that does not capture data to memory but is
>> only used to enable/disable the data capture process. We use this in setups
>> where the data is passed from the converter to a application specific
>> processing chain without ever going through system memory. This buffer could
>> probably also be used here on the audio side to control the converter state.
> 
> I forgot to mention. I think the first thing we should do is work on
> terminology. This is not an ADC, this is a configurable low-pass-filter.
> 
> It works in conjunction with a analog frontend (ADC) that produces a 1-bit
> pulse-density-modulated stream, takes that stream and converts it into N-bit
> PCM samples. The PCM samples are generated at a fraction of the PDM stream
> samplerate that corresponds to the decimation factor.
> 
> This is not an unusual device. Many audio CODEC and audio controllers
> contain such a core as well as most SigmaDelta converters supported by IIO.
> What is special about this part is that it is a dedicated core that is not
> embedded in some other hardware component. This creates greater flexibility,
> but of course also greater complexity that is required to manage all that
> flexibility.
> 
> We shouldn't codify anything about the kernel internal frameworks through
> which the device might be exposed into the devicetree. We should accurately
> describe the hardware (including the analog frontend) and then create a
> appropriate software structures to handle them.	
> 

So if everyone is aligned, i will abandon the MFD driver and try to bind
an ASoC driver on IIO interface. The "challenge" is to
define appropriate relation ship between ASoC and IIO...

In a first step, a lot of question to answers and points to clarify... i
will reply to associated mails.

Then I see two main topics to clarify:
	- DFSDM integration in IIO in a generic way. Lars, if i well interpret,
your proposal should be to introduce front-end and filter notions in
IIO, to support this kind of hardware?
	- DMA engine for audio purpose.
I propose to come back with RFC for both subjects.

If you want to have more detail on DFSDM: DFSDM datasheet is included in
STM32F413 datasheet available here:
http://www.st.com/content/ccc/resource/technical/document/reference_manual/group0/81/ea/88/1f/97/9e/4a/d0/DM00305666/files/DM00305666.pdf/jcr:content/translations/en.DM00305666.pdf

DFSDM Block diagram p 384

Regards
Arnaud

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-30 11:13                                 ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-30 11:13 UTC (permalink / raw)
  To: Lars-Peter Clausen, Jonathan Cameron, Lee Jones
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, Rob Herring,
	Mark Rutland, Hartmut Knaack, Peter Meerwald-Stadler,
	Jaroslav Kysela, Takashi Iwai, Liam Girdwood, Mark Brown,
	Maxime Coquelin, Alexandre TORGUE

Hello,

Thanks everyone for you feedback!
My comments below.

On 01/29/2017 03:34 PM, Lars-Peter Clausen wrote:
> On 01/29/2017 03:19 PM, Lars-Peter Clausen wrote:
>> On 01/29/2017 01:28 PM, Jonathan Cameron wrote:
>> [...]
>>>>> Jonathan, Mark, Please could you share your opinion on this topic?
>>> Hmm - based on a fairly quick read through of the code (which is never
>>> ideal!). I can see that the ideal would indeed be as Lee says, to
>>> expand the IIO interfaces sufficiently to support what you need.
>>>
>>>
>>> So, reading the code (fairly quickly I'm afraid as had a lot of reviews
>>> to catch up on this weekend).
>>> What we need:
>>> 1) DMA support in the ADC driver.  This would be a good anyway!
>>> 2) DMA consumer support - I defer to Lars for comments on this.
>>> 3) Means of describing and controlling the sinc filters applied. 
>>> 4) Appropriate channel support.  I'm not convinced that it doesn't make
>>> sense to have IIO channels for the microphones - at least in a streaming
>>> mode.  It's data - I don't really care what ;)
>>> Coarsely it's a filtered pulse per period counter which is
>>> a perfectly valid type to have a channel for.
>>>
>>> The big question to my mind is the DMA consumer support. How would
>>> it work. It it wouldn't this is somewhat of a non starter.
>>>
>>> To bring up another slightly ugly MFD case where it is borderline
>>> on whether an MFD makes sense (just as a reference point of something
>>> we have discussed a few times before)
>>>
>>> ADCs with features directed at touchscreen support.
>>> These are odd as the ADC bit is generic, but the specific output
>>> and read sequences used for touchscreen reading don't correspond to
>>> anything that makes any real sense for other applications.
>>>
>>> We have started to get hybrid drives that have an MFD underneath but
>>> do the ADC reads through IIO consumer interfaces, and the timing
>>> control from a touchscreen driver.  We haven't really gotten this
>>> one right yet either.
>>>
>>> Here however, to my mind things are different - as I read it
>>> (and feel free to point out what I'm missing), the sound usecase
>>> is just a question of setting up sampling frequencies and filters
>>> appropriate to the microphones and what ASoC expects?
>>>
>>> That's not to say the IIO dma stuff is flexible enough (yet) to
>>> handle the data flows, but perhaps we can work towards that.
>>
>> Yeah, so this is a bit different, but not unexpected. And I'm sure we'll see
>> more similar hardware in the future. I've talked about this before[1], the
>> cost structure of creating and manufacturing new hardware drives the design
>> in a certain direction so that we end up with general purpose hardware that
>> suddenly has applications in multiple frameworks that were previously fully
>> orthogonal.
>>
>> This device is certainly not a multi-function-device. It only has one
>> function, it's a sigma-delta demodulator. It is rather a
>> multi-purpose-device. It can be used for sigma-delta demodulation in audio
>> applications as well as more specialized data capture applications.
>>
>> It's comparable to something like a GPIO that can be used to control a reset
>> pin or turn on and off a LED. The GPIO chip is not considered
>> multi-function-device though, even though it can be used for many different
>> applications.
>>
>> As for DMA we already have a lot of DMA infrastructure on the audio side and
>> we probably want to reuse that rather than inserting IIO as a middle layer
>> since audio buffer capture as different requirements from IIO buffer and
>> we'd have to go the route of the least common denominator and loose
>> expressibility in the process.
>>
>> I've created a IIO buffer[2] that does not capture data to memory but is
>> only used to enable/disable the data capture process. We use this in setups
>> where the data is passed from the converter to a application specific
>> processing chain without ever going through system memory. This buffer could
>> probably also be used here on the audio side to control the converter state.
> 
> I forgot to mention. I think the first thing we should do is work on
> terminology. This is not an ADC, this is a configurable low-pass-filter.
> 
> It works in conjunction with a analog frontend (ADC) that produces a 1-bit
> pulse-density-modulated stream, takes that stream and converts it into N-bit
> PCM samples. The PCM samples are generated at a fraction of the PDM stream
> samplerate that corresponds to the decimation factor.
> 
> This is not an unusual device. Many audio CODEC and audio controllers
> contain such a core as well as most SigmaDelta converters supported by IIO.
> What is special about this part is that it is a dedicated core that is not
> embedded in some other hardware component. This creates greater flexibility,
> but of course also greater complexity that is required to manage all that
> flexibility.
> 
> We shouldn't codify anything about the kernel internal frameworks through
> which the device might be exposed into the devicetree. We should accurately
> describe the hardware (including the analog frontend) and then create a
> appropriate software structures to handle them.	
> 

So if everyone is aligned, i will abandon the MFD driver and try to bind
an ASoC driver on IIO interface. The "challenge" is to
define appropriate relation ship between ASoC and IIO...

In a first step, a lot of question to answers and points to clarify... i
will reply to associated mails.

Then I see two main topics to clarify:
	- DFSDM integration in IIO in a generic way. Lars, if i well interpret,
your proposal should be to introduce front-end and filter notions in
IIO, to support this kind of hardware?
	- DMA engine for audio purpose.
I propose to come back with RFC for both subjects.

If you want to have more detail on DFSDM: DFSDM datasheet is included in
STM32F413 datasheet available here:
http://www.st.com/content/ccc/resource/technical/document/reference_manual/group0/81/ea/88/1f/97/9e/4a/d0/DM00305666/files/DM00305666.pdf/jcr:content/translations/en.DM00305666.pdf

DFSDM Block diagram p 384

Regards
Arnaud




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

* [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-30 11:13                                 ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-30 11:13 UTC (permalink / raw)
  To: linux-arm-kernel

Hello,

Thanks everyone for you feedback!
My comments below.

On 01/29/2017 03:34 PM, Lars-Peter Clausen wrote:
> On 01/29/2017 03:19 PM, Lars-Peter Clausen wrote:
>> On 01/29/2017 01:28 PM, Jonathan Cameron wrote:
>> [...]
>>>>> Jonathan, Mark, Please could you share your opinion on this topic?
>>> Hmm - based on a fairly quick read through of the code (which is never
>>> ideal!). I can see that the ideal would indeed be as Lee says, to
>>> expand the IIO interfaces sufficiently to support what you need.
>>>
>>>
>>> So, reading the code (fairly quickly I'm afraid as had a lot of reviews
>>> to catch up on this weekend).
>>> What we need:
>>> 1) DMA support in the ADC driver.  This would be a good anyway!
>>> 2) DMA consumer support - I defer to Lars for comments on this.
>>> 3) Means of describing and controlling the sinc filters applied. 
>>> 4) Appropriate channel support.  I'm not convinced that it doesn't make
>>> sense to have IIO channels for the microphones - at least in a streaming
>>> mode.  It's data - I don't really care what ;)
>>> Coarsely it's a filtered pulse per period counter which is
>>> a perfectly valid type to have a channel for.
>>>
>>> The big question to my mind is the DMA consumer support. How would
>>> it work. It it wouldn't this is somewhat of a non starter.
>>>
>>> To bring up another slightly ugly MFD case where it is borderline
>>> on whether an MFD makes sense (just as a reference point of something
>>> we have discussed a few times before)
>>>
>>> ADCs with features directed at touchscreen support.
>>> These are odd as the ADC bit is generic, but the specific output
>>> and read sequences used for touchscreen reading don't correspond to
>>> anything that makes any real sense for other applications.
>>>
>>> We have started to get hybrid drives that have an MFD underneath but
>>> do the ADC reads through IIO consumer interfaces, and the timing
>>> control from a touchscreen driver.  We haven't really gotten this
>>> one right yet either.
>>>
>>> Here however, to my mind things are different - as I read it
>>> (and feel free to point out what I'm missing), the sound usecase
>>> is just a question of setting up sampling frequencies and filters
>>> appropriate to the microphones and what ASoC expects?
>>>
>>> That's not to say the IIO dma stuff is flexible enough (yet) to
>>> handle the data flows, but perhaps we can work towards that.
>>
>> Yeah, so this is a bit different, but not unexpected. And I'm sure we'll see
>> more similar hardware in the future. I've talked about this before[1], the
>> cost structure of creating and manufacturing new hardware drives the design
>> in a certain direction so that we end up with general purpose hardware that
>> suddenly has applications in multiple frameworks that were previously fully
>> orthogonal.
>>
>> This device is certainly not a multi-function-device. It only has one
>> function, it's a sigma-delta demodulator. It is rather a
>> multi-purpose-device. It can be used for sigma-delta demodulation in audio
>> applications as well as more specialized data capture applications.
>>
>> It's comparable to something like a GPIO that can be used to control a reset
>> pin or turn on and off a LED. The GPIO chip is not considered
>> multi-function-device though, even though it can be used for many different
>> applications.
>>
>> As for DMA we already have a lot of DMA infrastructure on the audio side and
>> we probably want to reuse that rather than inserting IIO as a middle layer
>> since audio buffer capture as different requirements from IIO buffer and
>> we'd have to go the route of the least common denominator and loose
>> expressibility in the process.
>>
>> I've created a IIO buffer[2] that does not capture data to memory but is
>> only used to enable/disable the data capture process. We use this in setups
>> where the data is passed from the converter to a application specific
>> processing chain without ever going through system memory. This buffer could
>> probably also be used here on the audio side to control the converter state.
> 
> I forgot to mention. I think the first thing we should do is work on
> terminology. This is not an ADC, this is a configurable low-pass-filter.
> 
> It works in conjunction with a analog frontend (ADC) that produces a 1-bit
> pulse-density-modulated stream, takes that stream and converts it into N-bit
> PCM samples. The PCM samples are generated at a fraction of the PDM stream
> samplerate that corresponds to the decimation factor.
> 
> This is not an unusual device. Many audio CODEC and audio controllers
> contain such a core as well as most SigmaDelta converters supported by IIO.
> What is special about this part is that it is a dedicated core that is not
> embedded in some other hardware component. This creates greater flexibility,
> but of course also greater complexity that is required to manage all that
> flexibility.
> 
> We shouldn't codify anything about the kernel internal frameworks through
> which the device might be exposed into the devicetree. We should accurately
> describe the hardware (including the analog frontend) and then create a
> appropriate software structures to handle them.	
> 

So if everyone is aligned, i will abandon the MFD driver and try to bind
an ASoC driver on IIO interface. The "challenge" is to
define appropriate relation ship between ASoC and IIO...

In a first step, a lot of question to answers and points to clarify... i
will reply to associated mails.

Then I see two main topics to clarify:
	- DFSDM integration in IIO in a generic way. Lars, if i well interpret,
your proposal should be to introduce front-end and filter notions in
IIO, to support this kind of hardware?
	- DMA engine for audio purpose.
I propose to come back with RFC for both subjects.

If you want to have more detail on DFSDM: DFSDM datasheet is included in
STM32F413 datasheet available here:
http://www.st.com/content/ccc/resource/technical/document/reference_manual/group0/81/ea/88/1f/97/9e/4a/d0/DM00305666/files/DM00305666.pdf/jcr:content/translations/en.DM00305666.pdf

DFSDM Block diagram p 384

Regards
Arnaud

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
  2017-01-27 17:17                         ` Lee Jones
  (?)
@ 2017-01-30 11:23                           ` Arnaud Pouliquen
  -1 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-30 11:23 UTC (permalink / raw)
  To: Lee Jones
  Cc: Mark Rutland, devicetree, alsa-devel, Lars-Peter Clausen,
	Maxime Coquelin, Liam Girdwood, linux-iio, Mark Brown,
	Takashi Iwai, Jaroslav Kysela, Rob Herring, Jonathan Cameron,
	Peter Meerwald-Stadler, Hartmut Knaack, linux-arm-kernel,
	Alexandre TORGUE



On 01/27/2017 06:17 PM, Lee Jones wrote:
> On Fri, 27 Jan 2017, Arnaud Pouliquen wrote:
>> On 01/27/2017 11:15 AM, Lee Jones wrote:
>>> On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
>>>> On 01/24/2017 12:36 PM, Lee Jones wrote:
>>>>> On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
>>>>>> On 01/24/2017 09:22 AM, Lee Jones wrote:
>>>>>>> On Mon, 23 Jan 2017, Arnaud Pouliquen wrote:
>>>>>>>
>>>>>>>> DFSDM hardware IP can be used at the same time for ADC sigma delta
>>>>>>>
>>>>>>> Same time as what?
>>>>>> DFSDM is used for ADC acquisition (through IIO) but also PDM microphone
>>>>>> capture (through ASOC).
>>>>>>>
>>>>>>>> conversion and audio PDM microphone.
>>>>>>>> MFD driver is in charge of configuring IP registers and managing IP clocks.
>>>>>>>> For this it exports an API to handles filters and channels resources.
>>>>>>>
>>>>>>> This looks like an ADC driver?  What is it that makes it an MFD?
>>>>>> Yes it a kind of ADC but that supports 2 features audio and iio.
>>>>>> So it has to support 2 features based on 2 separate Frameworks.
>>>>>
>>>>> I'm still unsure why it needs to live in MFD.
>>>>>
>>>>> By the looks of it, this driver needs to move into IIO and you need to
>>>>> call into it from ASoC.
>>>>>
>>>>
>>>> I think i introduce confusion by speaking about ADC for audio...
>>>>
>>>> 1) IIO handles sigma delta ADCs that can be used as example for motor
>>>> controls. the aim is to get value based on an application request or
>>>> using some triggers.
>>>> example: http://www.ti.com/lit/ds/symlink/ads1202.pdf
>>>>
>>>> 2) For audio part, we speak about Digital mems microphones that generate
>>>> PDM format stream. PDM is a continuous real time stream dedicated to
>>>> audio record and must be handled in ASOC ( codec Dmic part driver is
>>>> /sound/soc/codec/dmic.c).
>>>> DMIC example:
>>>> http://www.st.com/content/ccc/resource/technical/document/datasheet/47/bd/d2/13/8d/fd/48/26/DM00121815.pdf/files/DM00121815.pdf/jcr:content/translations/en.DM00121815.pdf
>>>>
>>>> Form my point of view it very strange to handle DMICs in IIO, as it is
>>>> not designed to support audio streams.it is two separate features that
>>>> are not compatible.
>>>>
>>>> Now, from software point of view
>>>> That would means that IIO declares ADCs that it can not expose, because
>>>> DMIC is not IIO standard. But IIO inkern API needs that device is
>>>> declared
>>>
>>>> So i should define a specific API in IIO for ASOC driver.
>>>
>>> Yes, this is what I think you should do.
>>>
>>> MFD is not a dumping ground for devices that do not fit anywhere else.
>>
>> I fully understand that you don't want to create unnecessary MFD devices.
>> But In case of DFSDM ,it is really a device that supports 2 features.
>> The main evidence is that "ADC acquisition" and "digital microphone"
>> features are handled by two separate frameworks.
>> So this corresponds to two features that share the same device resources
>> (registers, IRQs, clocks).
>> Seems that this match with MFD definition.
>>
>> Now, if IIO and ASOC maintainers are aligned with you proposal, i will
>> move MFD part in IIO.
>> But in this case, i can not see another way to do it, except a MFD
>> driver hidden in IIO?
>>
>> Here is my analysis of a design in IIO:
>>
>> Natural way to do this is to consider that ASOC is a customer of IIO so
>> use the customer.h API.
>> 1) As Digital microphone can not be handled by IIO dev interface, i can
>> not expose them as an IIO channel, that would be visible by applications
>> in /sys/bus/iio/devices/iio
>> 2) ASOC needs to configure DFSDM filter to match to specific frequencies
>> and sample formats requested by users. Not standard IIO interface to do
>> this. (on IIO side this is done by attribute file)
>> 3) audio stream is a real time stream. So for audio quality best is to
>> minimize latencies and Xrun. That why transfer as done from HW to
>> application using LLI DMA transfers with a minimum of copy. This does
>> not match with the IIO inkern API.
>>  => So seems not possible to use IIO inkern API.
>>
>> That's means that i would need to create a ST specific include file to
>> offer an API to ASoC to handle DFSDM resources linked to the DMIC.
>> and to probe ASOc device in IIO one.
>> So the solution would be to create something like a sub IIO driver That
>> probe and handle some IIO and ASOC devices.
>> Ultimately This would correspond to a MFD driver integrated in IIO...
> 
> The issue is not the creation of an MFD driver to create shared
> resources and probe the child devices.  That's what MFD *is* for.
> What I do take exception to is having lots of code in MFD which
> should clearly live in a different subsystem.
> 
> Since this device only serves a couple of functions, I expect it to be
> a few hundred lines, maximum.  Everything else should be farmed out.
> MFD knows nothing of "channels" or "filters" so anything related to
> them (init, get, start, stop, release, etc) needs to find another
> home.  Whether that's in IIO is a separate discussion, but it
> certainly doesn't live in MFD.

Thanks for your time and explanations Lee.
As you recommended, I will try to integrate it in IIO. And i notice for
the next time that i can not expose such "high level" interface in MFD.

Regards
Arnaud

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-30 11:23                           ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-30 11:23 UTC (permalink / raw)
  To: Lee Jones
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, Rob Herring,
	Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown, Maxime Coquelin,
	Alexandre TORGUE



On 01/27/2017 06:17 PM, Lee Jones wrote:
> On Fri, 27 Jan 2017, Arnaud Pouliquen wrote:
>> On 01/27/2017 11:15 AM, Lee Jones wrote:
>>> On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
>>>> On 01/24/2017 12:36 PM, Lee Jones wrote:
>>>>> On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
>>>>>> On 01/24/2017 09:22 AM, Lee Jones wrote:
>>>>>>> On Mon, 23 Jan 2017, Arnaud Pouliquen wrote:
>>>>>>>
>>>>>>>> DFSDM hardware IP can be used at the same time for ADC sigma delta
>>>>>>>
>>>>>>> Same time as what?
>>>>>> DFSDM is used for ADC acquisition (through IIO) but also PDM microphone
>>>>>> capture (through ASOC).
>>>>>>>
>>>>>>>> conversion and audio PDM microphone.
>>>>>>>> MFD driver is in charge of configuring IP registers and managing IP clocks.
>>>>>>>> For this it exports an API to handles filters and channels resources.
>>>>>>>
>>>>>>> This looks like an ADC driver?  What is it that makes it an MFD?
>>>>>> Yes it a kind of ADC but that supports 2 features audio and iio.
>>>>>> So it has to support 2 features based on 2 separate Frameworks.
>>>>>
>>>>> I'm still unsure why it needs to live in MFD.
>>>>>
>>>>> By the looks of it, this driver needs to move into IIO and you need to
>>>>> call into it from ASoC.
>>>>>
>>>>
>>>> I think i introduce confusion by speaking about ADC for audio...
>>>>
>>>> 1) IIO handles sigma delta ADCs that can be used as example for motor
>>>> controls. the aim is to get value based on an application request or
>>>> using some triggers.
>>>> example: http://www.ti.com/lit/ds/symlink/ads1202.pdf
>>>>
>>>> 2) For audio part, we speak about Digital mems microphones that generate
>>>> PDM format stream. PDM is a continuous real time stream dedicated to
>>>> audio record and must be handled in ASOC ( codec Dmic part driver is
>>>> /sound/soc/codec/dmic.c).
>>>> DMIC example:
>>>> http://www.st.com/content/ccc/resource/technical/document/datasheet/47/bd/d2/13/8d/fd/48/26/DM00121815.pdf/files/DM00121815.pdf/jcr:content/translations/en.DM00121815.pdf
>>>>
>>>> Form my point of view it very strange to handle DMICs in IIO, as it is
>>>> not designed to support audio streams.it is two separate features that
>>>> are not compatible.
>>>>
>>>> Now, from software point of view
>>>> That would means that IIO declares ADCs that it can not expose, because
>>>> DMIC is not IIO standard. But IIO inkern API needs that device is
>>>> declared
>>>
>>>> So i should define a specific API in IIO for ASOC driver.
>>>
>>> Yes, this is what I think you should do.
>>>
>>> MFD is not a dumping ground for devices that do not fit anywhere else.
>>
>> I fully understand that you don't want to create unnecessary MFD devices.
>> But In case of DFSDM ,it is really a device that supports 2 features.
>> The main evidence is that "ADC acquisition" and "digital microphone"
>> features are handled by two separate frameworks.
>> So this corresponds to two features that share the same device resources
>> (registers, IRQs, clocks).
>> Seems that this match with MFD definition.
>>
>> Now, if IIO and ASOC maintainers are aligned with you proposal, i will
>> move MFD part in IIO.
>> But in this case, i can not see another way to do it, except a MFD
>> driver hidden in IIO?
>>
>> Here is my analysis of a design in IIO:
>>
>> Natural way to do this is to consider that ASOC is a customer of IIO so
>> use the customer.h API.
>> 1) As Digital microphone can not be handled by IIO dev interface, i can
>> not expose them as an IIO channel, that would be visible by applications
>> in /sys/bus/iio/devices/iio
>> 2) ASOC needs to configure DFSDM filter to match to specific frequencies
>> and sample formats requested by users. Not standard IIO interface to do
>> this. (on IIO side this is done by attribute file)
>> 3) audio stream is a real time stream. So for audio quality best is to
>> minimize latencies and Xrun. That why transfer as done from HW to
>> application using LLI DMA transfers with a minimum of copy. This does
>> not match with the IIO inkern API.
>>  => So seems not possible to use IIO inkern API.
>>
>> That's means that i would need to create a ST specific include file to
>> offer an API to ASoC to handle DFSDM resources linked to the DMIC.
>> and to probe ASOc device in IIO one.
>> So the solution would be to create something like a sub IIO driver That
>> probe and handle some IIO and ASOC devices.
>> Ultimately This would correspond to a MFD driver integrated in IIO...
> 
> The issue is not the creation of an MFD driver to create shared
> resources and probe the child devices.  That's what MFD *is* for.
> What I do take exception to is having lots of code in MFD which
> should clearly live in a different subsystem.
> 
> Since this device only serves a couple of functions, I expect it to be
> a few hundred lines, maximum.  Everything else should be farmed out.
> MFD knows nothing of "channels" or "filters" so anything related to
> them (init, get, start, stop, release, etc) needs to find another
> home.  Whether that's in IIO is a separate discussion, but it
> certainly doesn't live in MFD.

Thanks for your time and explanations Lee.
As you recommended, I will try to integrate it in IIO. And i notice for
the next time that i can not expose such "high level" interface in MFD.

Regards
Arnaud

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

* [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-30 11:23                           ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-30 11:23 UTC (permalink / raw)
  To: linux-arm-kernel



On 01/27/2017 06:17 PM, Lee Jones wrote:
> On Fri, 27 Jan 2017, Arnaud Pouliquen wrote:
>> On 01/27/2017 11:15 AM, Lee Jones wrote:
>>> On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
>>>> On 01/24/2017 12:36 PM, Lee Jones wrote:
>>>>> On Tue, 24 Jan 2017, Arnaud Pouliquen wrote:
>>>>>> On 01/24/2017 09:22 AM, Lee Jones wrote:
>>>>>>> On Mon, 23 Jan 2017, Arnaud Pouliquen wrote:
>>>>>>>
>>>>>>>> DFSDM hardware IP can be used at the same time for ADC sigma delta
>>>>>>>
>>>>>>> Same time as what?
>>>>>> DFSDM is used for ADC acquisition (through IIO) but also PDM microphone
>>>>>> capture (through ASOC).
>>>>>>>
>>>>>>>> conversion and audio PDM microphone.
>>>>>>>> MFD driver is in charge of configuring IP registers and managing IP clocks.
>>>>>>>> For this it exports an API to handles filters and channels resources.
>>>>>>>
>>>>>>> This looks like an ADC driver?  What is it that makes it an MFD?
>>>>>> Yes it a kind of ADC but that supports 2 features audio and iio.
>>>>>> So it has to support 2 features based on 2 separate Frameworks.
>>>>>
>>>>> I'm still unsure why it needs to live in MFD.
>>>>>
>>>>> By the looks of it, this driver needs to move into IIO and you need to
>>>>> call into it from ASoC.
>>>>>
>>>>
>>>> I think i introduce confusion by speaking about ADC for audio...
>>>>
>>>> 1) IIO handles sigma delta ADCs that can be used as example for motor
>>>> controls. the aim is to get value based on an application request or
>>>> using some triggers.
>>>> example: http://www.ti.com/lit/ds/symlink/ads1202.pdf
>>>>
>>>> 2) For audio part, we speak about Digital mems microphones that generate
>>>> PDM format stream. PDM is a continuous real time stream dedicated to
>>>> audio record and must be handled in ASOC ( codec Dmic part driver is
>>>> /sound/soc/codec/dmic.c).
>>>> DMIC example:
>>>> http://www.st.com/content/ccc/resource/technical/document/datasheet/47/bd/d2/13/8d/fd/48/26/DM00121815.pdf/files/DM00121815.pdf/jcr:content/translations/en.DM00121815.pdf
>>>>
>>>> Form my point of view it very strange to handle DMICs in IIO, as it is
>>>> not designed to support audio streams.it is two separate features that
>>>> are not compatible.
>>>>
>>>> Now, from software point of view
>>>> That would means that IIO declares ADCs that it can not expose, because
>>>> DMIC is not IIO standard. But IIO inkern API needs that device is
>>>> declared
>>>
>>>> So i should define a specific API in IIO for ASOC driver.
>>>
>>> Yes, this is what I think you should do.
>>>
>>> MFD is not a dumping ground for devices that do not fit anywhere else.
>>
>> I fully understand that you don't want to create unnecessary MFD devices.
>> But In case of DFSDM ,it is really a device that supports 2 features.
>> The main evidence is that "ADC acquisition" and "digital microphone"
>> features are handled by two separate frameworks.
>> So this corresponds to two features that share the same device resources
>> (registers, IRQs, clocks).
>> Seems that this match with MFD definition.
>>
>> Now, if IIO and ASOC maintainers are aligned with you proposal, i will
>> move MFD part in IIO.
>> But in this case, i can not see another way to do it, except a MFD
>> driver hidden in IIO?
>>
>> Here is my analysis of a design in IIO:
>>
>> Natural way to do this is to consider that ASOC is a customer of IIO so
>> use the customer.h API.
>> 1) As Digital microphone can not be handled by IIO dev interface, i can
>> not expose them as an IIO channel, that would be visible by applications
>> in /sys/bus/iio/devices/iio
>> 2) ASOC needs to configure DFSDM filter to match to specific frequencies
>> and sample formats requested by users. Not standard IIO interface to do
>> this. (on IIO side this is done by attribute file)
>> 3) audio stream is a real time stream. So for audio quality best is to
>> minimize latencies and Xrun. That why transfer as done from HW to
>> application using LLI DMA transfers with a minimum of copy. This does
>> not match with the IIO inkern API.
>>  => So seems not possible to use IIO inkern API.
>>
>> That's means that i would need to create a ST specific include file to
>> offer an API to ASoC to handle DFSDM resources linked to the DMIC.
>> and to probe ASOc device in IIO one.
>> So the solution would be to create something like a sub IIO driver That
>> probe and handle some IIO and ASOC devices.
>> Ultimately This would correspond to a MFD driver integrated in IIO...
> 
> The issue is not the creation of an MFD driver to create shared
> resources and probe the child devices.  That's what MFD *is* for.
> What I do take exception to is having lots of code in MFD which
> should clearly live in a different subsystem.
> 
> Since this device only serves a couple of functions, I expect it to be
> a few hundred lines, maximum.  Everything else should be farmed out.
> MFD knows nothing of "channels" or "filters" so anything related to
> them (init, get, start, stop, release, etc) needs to find another
> home.  Whether that's in IIO is a separate discussion, but it
> certainly doesn't live in MFD.

Thanks for your time and explanations Lee.
As you recommended, I will try to integrate it in IIO. And i notice for
the next time that i can not expose such "high level" interface in MFD.

Regards
Arnaud

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

* Re: [PATCH 1/7] MFD: add bindings for STM32 DFSDM driver
  2017-01-27 20:53     ` Rob Herring
  (?)
@ 2017-01-30 13:16       ` Arnaud Pouliquen
  -1 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-30 13:16 UTC (permalink / raw)
  To: Rob Herring
  Cc: Mark Rutland, devicetree, alsa-devel, Lars-Peter Clausen,
	Maxime Coquelin, Liam Girdwood, linux-iio, Takashi Iwai,
	Jaroslav Kysela, Mark Brown, Jonathan Cameron,
	Peter Meerwald-Stadler, Hartmut Knaack, Lee Jones,
	linux-arm-kernel, Alexandre TORGUE

Hello Rob,

Thanks for the review,
FYI This patch-set has to be abandoned to be redesigned without MFD driver.
Anyway i will take into account your remarks in my redesign.

I just added a comment below to explain CLKOUT.

Regards Arnaud


On 01/27/2017 09:53 PM, Rob Herring wrote:
> On Mon, Jan 23, 2017 at 05:32:19PM +0100, Arnaud Pouliquen wrote:
>> Add bindings information for STM32 Digital Filter for Sigma Delta modulators MFD driver
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> ---
>>  .../devicetree/bindings/mfd/stm32-dfsdm.txt        | 68 ++++++++++++++++++++++
>>  1 file changed, 68 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt
>>
>> diff --git a/Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt b/Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt
>> new file mode 100644
>> index 0000000..e0b45ee
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt
>> @@ -0,0 +1,68 @@
>> +STMicroelectronics STM32 Digital Filter for Sigma Delta Modulator (DFSDM)
>> +ulti-function device.
>> +
>> +The STM32 DFSDM device is a multifunction device that handles the DFSDM IP.
>> +
>> +The DFSDM IP allows to add processing on Sigma Delta ADC based on SinC filters.
>> +For this, a pool of m filters can be connected to a pool of n channels.
>> +For STM32H7 : m = 4, n = 8.
>> +
>> +Each channel n is assigned to the SPI or Manchester interface n or n + 1.
>> +Channels 0 to 2 can also be connected to ADC IP instance 1 to 3.
>> +Filtering result is stored in a left aligned register, with 8 LSB reserved for
>> +the input channel ID.
>> +
>> +Each filter instance supports two contexts to manage conversions, each one has
>> +its own configurable sequence and trigger:
>> +- regular conversion: used for single or continuous conversion.
>> +- injected conversions: used for triggered conversion.
>> +
>> +Interfaces supported:
>> +- sigma delta ADCs trough IIO framework.
>> +- PDM microphones through ASoC framework.
> 
> Bindings describe h/w, not Linux subsystems.
> 
>> +
>> +Required properties:
>> +- compatible:	Must be "st,stm32h7-dfsdm".
>> +- reg:		Specifies the DFSDM block register address and length.
>> +- interrupts:	IRQ lines connected to each DFSDM filter instance.
>> +- clocks:	IP and serial interfaces clocking. Should be set according
>> +		to rcc clock ID and "clock-names".
>> +- clock-names:	Input clock name "dfsdm_clk" must be defined,
>> +		"audio_clk" is optional. If defined CLKOUT is based on the audio
>> +		clock, else "dfsdm_clk" is used.
> 
> _clk is redundant.
> 
>> +
>> +Optional properties:
>> +- st,clkout-freq: clkout clock frequency (Hz).This clock must be set according
>> +		  to "clock" property. Frequency must be a multiple of the rcc
>> +		  clock frequency. If not, clkout frequency will not be
>> +		  accurate.
> 
> What is CLKOUT connected to and will you need to describe that in DT?

CLKOUT can be outputted to clock SPI interfaces (master mode) or extern
ADC. This CLKOUT is common for the 8 instances of the SPIs of the DFSDM.
CLKOUT frequency is computed based on an internal division of the
"dfsdm_clk" or "audio_clk".

> 
>> +- pinctrl-names:  set to "default".
>> +- pinctrl-0:	  List of phandles pointing to pin configuration nodes for DFSDM
>> +		  module.
>> +		  For Pinctrl properties see ../pinctrl/pinctrl-bindings.txt
>> +Example :
>> +	dfsdm: dfsdm@4400D000 {
>> +		compatible = "st,stm32h7-dfsdm";
>> +		reg = <0x40017000 0x400>;
>> +		interrupts = <110>, <111>, <112>, <113>;
>> +		clocks = <&timer_clk>;
>> +		clock-names = "dfsdm_clk";
>> +		pinctrl-0 = <&dfsdm_ch0 &dfsdm_ch1>;
>> +		pinctrl-names = "default";
>> +		st,clkout-freq = <2480000>;
>> +
>> +		iio_dfsdm0: iio-dfsdm@0 {
> 
> adc {
> 
>> +			compatible = "st,stm32-dfsdm-adc";
>> +			#io-channel-cells = <1>;
>> +			reg = <0>;
>> +			status = "disabled";
>> +		};
>> +		dai_dfsdm0: dfsdm-audio@0 {
> 
> digital-mic {
> 
>> +			compatible = "st,stm32-dfsdm-audio";
>> +			#sound-dai-cells = <0>;
>> +			reg = <0>;
> 
> You can't have 2 children with the same unit address. Just drop reg and 
> the unit address.
> 
>> +			dmas = <&dmamux1 101 0x400 0x00>;
>> +			dma-names = "rx";
>> +			status = "disabled";
>> +		};
>> +	};
>> -- 
>> 1.9.1
>>

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

* Re: [PATCH 1/7] MFD: add bindings for STM32 DFSDM driver
@ 2017-01-30 13:16       ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-30 13:16 UTC (permalink / raw)
  To: Rob Herring
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, Lee Jones,
	Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown, Maxime Coquelin,
	Alexandre TORGUE

Hello Rob,

Thanks for the review,
FYI This patch-set has to be abandoned to be redesigned without MFD driver.
Anyway i will take into account your remarks in my redesign.

I just added a comment below to explain CLKOUT.

Regards Arnaud


On 01/27/2017 09:53 PM, Rob Herring wrote:
> On Mon, Jan 23, 2017 at 05:32:19PM +0100, Arnaud Pouliquen wrote:
>> Add bindings information for STM32 Digital Filter for Sigma Delta modulators MFD driver
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> ---
>>  .../devicetree/bindings/mfd/stm32-dfsdm.txt        | 68 ++++++++++++++++++++++
>>  1 file changed, 68 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt
>>
>> diff --git a/Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt b/Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt
>> new file mode 100644
>> index 0000000..e0b45ee
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt
>> @@ -0,0 +1,68 @@
>> +STMicroelectronics STM32 Digital Filter for Sigma Delta Modulator (DFSDM)
>> +ulti-function device.
>> +
>> +The STM32 DFSDM device is a multifunction device that handles the DFSDM IP.
>> +
>> +The DFSDM IP allows to add processing on Sigma Delta ADC based on SinC filters.
>> +For this, a pool of m filters can be connected to a pool of n channels.
>> +For STM32H7 : m = 4, n = 8.
>> +
>> +Each channel n is assigned to the SPI or Manchester interface n or n + 1.
>> +Channels 0 to 2 can also be connected to ADC IP instance 1 to 3.
>> +Filtering result is stored in a left aligned register, with 8 LSB reserved for
>> +the input channel ID.
>> +
>> +Each filter instance supports two contexts to manage conversions, each one has
>> +its own configurable sequence and trigger:
>> +- regular conversion: used for single or continuous conversion.
>> +- injected conversions: used for triggered conversion.
>> +
>> +Interfaces supported:
>> +- sigma delta ADCs trough IIO framework.
>> +- PDM microphones through ASoC framework.
> 
> Bindings describe h/w, not Linux subsystems.
> 
>> +
>> +Required properties:
>> +- compatible:	Must be "st,stm32h7-dfsdm".
>> +- reg:		Specifies the DFSDM block register address and length.
>> +- interrupts:	IRQ lines connected to each DFSDM filter instance.
>> +- clocks:	IP and serial interfaces clocking. Should be set according
>> +		to rcc clock ID and "clock-names".
>> +- clock-names:	Input clock name "dfsdm_clk" must be defined,
>> +		"audio_clk" is optional. If defined CLKOUT is based on the audio
>> +		clock, else "dfsdm_clk" is used.
> 
> _clk is redundant.
> 
>> +
>> +Optional properties:
>> +- st,clkout-freq: clkout clock frequency (Hz).This clock must be set according
>> +		  to "clock" property. Frequency must be a multiple of the rcc
>> +		  clock frequency. If not, clkout frequency will not be
>> +		  accurate.
> 
> What is CLKOUT connected to and will you need to describe that in DT?

CLKOUT can be outputted to clock SPI interfaces (master mode) or extern
ADC. This CLKOUT is common for the 8 instances of the SPIs of the DFSDM.
CLKOUT frequency is computed based on an internal division of the
"dfsdm_clk" or "audio_clk".

> 
>> +- pinctrl-names:  set to "default".
>> +- pinctrl-0:	  List of phandles pointing to pin configuration nodes for DFSDM
>> +		  module.
>> +		  For Pinctrl properties see ../pinctrl/pinctrl-bindings.txt
>> +Example :
>> +	dfsdm: dfsdm@4400D000 {
>> +		compatible = "st,stm32h7-dfsdm";
>> +		reg = <0x40017000 0x400>;
>> +		interrupts = <110>, <111>, <112>, <113>;
>> +		clocks = <&timer_clk>;
>> +		clock-names = "dfsdm_clk";
>> +		pinctrl-0 = <&dfsdm_ch0 &dfsdm_ch1>;
>> +		pinctrl-names = "default";
>> +		st,clkout-freq = <2480000>;
>> +
>> +		iio_dfsdm0: iio-dfsdm@0 {
> 
> adc {
> 
>> +			compatible = "st,stm32-dfsdm-adc";
>> +			#io-channel-cells = <1>;
>> +			reg = <0>;
>> +			status = "disabled";
>> +		};
>> +		dai_dfsdm0: dfsdm-audio@0 {
> 
> digital-mic {
> 
>> +			compatible = "st,stm32-dfsdm-audio";
>> +			#sound-dai-cells = <0>;
>> +			reg = <0>;
> 
> You can't have 2 children with the same unit address. Just drop reg and 
> the unit address.
> 
>> +			dmas = <&dmamux1 101 0x400 0x00>;
>> +			dma-names = "rx";
>> +			status = "disabled";
>> +		};
>> +	};
>> -- 
>> 1.9.1
>>

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

* [PATCH 1/7] MFD: add bindings for STM32 DFSDM driver
@ 2017-01-30 13:16       ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-30 13:16 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Rob,

Thanks for the review,
FYI This patch-set has to be abandoned to be redesigned without MFD driver.
Anyway i will take into account your remarks in my redesign.

I just added a comment below to explain CLKOUT.

Regards Arnaud


On 01/27/2017 09:53 PM, Rob Herring wrote:
> On Mon, Jan 23, 2017 at 05:32:19PM +0100, Arnaud Pouliquen wrote:
>> Add bindings information for STM32 Digital Filter for Sigma Delta modulators MFD driver
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> ---
>>  .../devicetree/bindings/mfd/stm32-dfsdm.txt        | 68 ++++++++++++++++++++++
>>  1 file changed, 68 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt
>>
>> diff --git a/Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt b/Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt
>> new file mode 100644
>> index 0000000..e0b45ee
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/mfd/stm32-dfsdm.txt
>> @@ -0,0 +1,68 @@
>> +STMicroelectronics STM32 Digital Filter for Sigma Delta Modulator (DFSDM)
>> +ulti-function device.
>> +
>> +The STM32 DFSDM device is a multifunction device that handles the DFSDM IP.
>> +
>> +The DFSDM IP allows to add processing on Sigma Delta ADC based on SinC filters.
>> +For this, a pool of m filters can be connected to a pool of n channels.
>> +For STM32H7 : m = 4, n = 8.
>> +
>> +Each channel n is assigned to the SPI or Manchester interface n or n + 1.
>> +Channels 0 to 2 can also be connected to ADC IP instance 1 to 3.
>> +Filtering result is stored in a left aligned register, with 8 LSB reserved for
>> +the input channel ID.
>> +
>> +Each filter instance supports two contexts to manage conversions, each one has
>> +its own configurable sequence and trigger:
>> +- regular conversion: used for single or continuous conversion.
>> +- injected conversions: used for triggered conversion.
>> +
>> +Interfaces supported:
>> +- sigma delta ADCs trough IIO framework.
>> +- PDM microphones through ASoC framework.
> 
> Bindings describe h/w, not Linux subsystems.
> 
>> +
>> +Required properties:
>> +- compatible:	Must be "st,stm32h7-dfsdm".
>> +- reg:		Specifies the DFSDM block register address and length.
>> +- interrupts:	IRQ lines connected to each DFSDM filter instance.
>> +- clocks:	IP and serial interfaces clocking. Should be set according
>> +		to rcc clock ID and "clock-names".
>> +- clock-names:	Input clock name "dfsdm_clk" must be defined,
>> +		"audio_clk" is optional. If defined CLKOUT is based on the audio
>> +		clock, else "dfsdm_clk" is used.
> 
> _clk is redundant.
> 
>> +
>> +Optional properties:
>> +- st,clkout-freq: clkout clock frequency (Hz).This clock must be set according
>> +		  to "clock" property. Frequency must be a multiple of the rcc
>> +		  clock frequency. If not, clkout frequency will not be
>> +		  accurate.
> 
> What is CLKOUT connected to and will you need to describe that in DT?

CLKOUT can be outputted to clock SPI interfaces (master mode) or extern
ADC. This CLKOUT is common for the 8 instances of the SPIs of the DFSDM.
CLKOUT frequency is computed based on an internal division of the
"dfsdm_clk" or "audio_clk".

> 
>> +- pinctrl-names:  set to "default".
>> +- pinctrl-0:	  List of phandles pointing to pin configuration nodes for DFSDM
>> +		  module.
>> +		  For Pinctrl properties see ../pinctrl/pinctrl-bindings.txt
>> +Example :
>> +	dfsdm: dfsdm at 4400D000 {
>> +		compatible = "st,stm32h7-dfsdm";
>> +		reg = <0x40017000 0x400>;
>> +		interrupts = <110>, <111>, <112>, <113>;
>> +		clocks = <&timer_clk>;
>> +		clock-names = "dfsdm_clk";
>> +		pinctrl-0 = <&dfsdm_ch0 &dfsdm_ch1>;
>> +		pinctrl-names = "default";
>> +		st,clkout-freq = <2480000>;
>> +
>> +		iio_dfsdm0: iio-dfsdm at 0 {
> 
> adc {
> 
>> +			compatible = "st,stm32-dfsdm-adc";
>> +			#io-channel-cells = <1>;
>> +			reg = <0>;
>> +			status = "disabled";
>> +		};
>> +		dai_dfsdm0: dfsdm-audio at 0 {
> 
> digital-mic {
> 
>> +			compatible = "st,stm32-dfsdm-audio";
>> +			#sound-dai-cells = <0>;
>> +			reg = <0>;
> 
> You can't have 2 children with the same unit address. Just drop reg and 
> the unit address.
> 
>> +			dmas = <&dmamux1 101 0x400 0x00>;
>> +			dma-names = "rx";
>> +			status = "disabled";
>> +		};
>> +	};
>> -- 
>> 1.9.1
>>

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
  2017-01-29 11:53         ` Jonathan Cameron
  (?)
@ 2017-01-30 15:08           ` Arnaud Pouliquen
  -1 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-30 15:08 UTC (permalink / raw)
  To: Jonathan Cameron, devicetree, linux-arm-kernel, linux-iio,
	alsa-devel, Lee Jones, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Alexandre TORGUE, Maxime Coquelin

Hello Jonathan,

Thanks for the review. This drivers should disappear,
but i will integrate you comment/remark in my redesign.

Please find some comments below, on specific points

Regards
Arnaud


On 01/29/2017 12:53 PM, Jonathan Cameron wrote:
> On 23/01/17 16:32, Arnaud Pouliquen wrote:
>> DFSDM hardware IP can be used at the same time for ADC sigma delta
>> conversion and audio PDM microphone.
>> MFD driver is in charge of configuring IP registers and managing IP clocks.
>> For this it exports an API to handles filters and channels resources.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> This is somewhat of a beast.  I would be tempted to build it up in more
> bite sized chunks.
> 
> Obvious things to drop from a first version (basically to make it easier
> to review) would be injected supported.  There may well be others but you'll
> have a better feel for that than me.
i will pay attention for next version.
> 
> I really don't like the wrappers round the regmap functions though.
> They mean you are papering over errors if they occur and make the code
> slightly harder to read by implying that something else is going on.
> 
One aim of the wrapper was to simplify code review, seems that just the
opposite...
As i have around 50 regmap accesses, adding  a return/goto for each
error and at least an associated error messages for each function,
should add 100 to 150 extra lines...

> Yes the code will be longer without them, but you will also be forced to think
> properly about error paths.

I have a question around this. What should be the action if a register
access return an error?
Possible root causes:
	- bad address of the IP (DT)
	- IP not clocked or in reset (driver BUG).
	- IP is out of order (hardware issue)
	- bug in driver that access to an invalid address.

So except for the last root cause,we can suppose that every register
accesses should always be OK or KO...
This is also a reason of the wrapper. Detect driver bug, without adding
a test on each register access return.

Perhaps a compromise could be that test is done only during some
specific phase (probe, after reset deassertion, clock enabling...) and
then trust access without test?
Or simply add error message in regmap helper routines...

Please just tell/confirm me your preference.

> 
> Anyhow, was mostly reading this to get a feel for what was going on in the
> whole series so not really a terribly thorough review I'm afraid. Sorry about
> that!
More than enough for this first version :)

> 
> Jonathan
>> ---
>>  drivers/mfd/Kconfig             |   11 +
>>  drivers/mfd/Makefile            |    2 +
>>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
>>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
>>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
>>  5 files changed, 1601 insertions(+)
>>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
>>  create mode 100644 drivers/mfd/stm32-dfsdm.c
>>  create mode 100644 include/linux/mfd/stm32-dfsdm.h


[...]

>> diff --git a/drivers/mfd/stm32-dfsdm.c b/drivers/mfd/stm32-dfsdm.c
>> new file mode 100644
>> index 0000000..81ca29c
>> --- /dev/null
>> +++ b/drivers/mfd/stm32-dfsdm.c
>> @@ -0,0 +1,1044 @@
>> +/*
>> + * This file is part of STM32 DFSDM mfd driver
>> + *
>> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
>> + * Author(s): Arnaud Pouliquen <arnaud.pouliquen@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/interrupt.h>
>> +#include <linux/mfd/core.h>
>> +#include <linux/module.h>
>> +#include <linux/of_device.h>
>> +#include <linux/regmap.h>
>> +#include <linux/slab.h>
>> +
>> +#include <linux/mfd/stm32-dfsdm.h>
>> +
>> +#include "stm32-dfsdm-reg.h"
>> +
>> +#define DFSDM_UPDATE_BITS(regm, reg, mask, val) \
>> +		WARN_ON(regmap_update_bits(regm, reg, mask, val))
> Don't do these wrappers please. Handle error correctly in all cases.
> Reviewing as I tend to do backwards through the driver, I thought these
> were doing something interesting.
> 
> Effectively you have no error handling as a result of these which needs
> fixing.
> 
>> +
>> +#define DFSDM_REG_READ(regm, reg, val) \
>> +		WARN_ON(regmap_read(regm, reg, val))
>> +
>> +#define DFSDM_REG_WRITE(regm, reg, val) \
>> +		WARN_ON(regmap_write(regm, reg, val))
>> +
>> +#define STM32H7_DFSDM_NUM_FILTERS	4
>> +#define STM32H7_DFSDM_NUM_INPUTS	8
>> +
>> +enum dfsdm_clkout_src {
>> +	DFSDM_CLK,
>> +	AUDIO_CLK
>> +};
>> +
>> +struct stm32_dev_data {
>> +	const struct stm32_dfsdm dfsdm;
>> +	const struct regmap_config *regmap_cfg;
>> +};
>> +
>> +struct dfsdm_priv;
>> +
>> +struct filter_params {
>> +	unsigned int id;
>> +	int irq;
>> +	struct stm32_dfsdm_fl_event event;
>> +	u32 event_mask;
>> +	struct dfsdm_priv *priv; /* Cross ref for context */
>> +	unsigned int ext_ch_mask;
>> +	unsigned int scan_ch;
>> +};
>> +
>> +struct ch_params {
>> +	struct stm32_dfsdm_channel ch;
>> +};
>> +
> I'd like to see a lot more comments in here.  Perhaps full kernel-doc
> as some elements are not that obvious at least to a fairly casual read.
> 
Description in device-tree tree bindings and cover-letter is not
sufficient? you would a doc in Document/arm/stm32?

>> +struct dfsdm_priv {
>> +	struct platform_device *pdev;
>> +	struct stm32_dfsdm dfsdm;
>> +
>> +	spinlock_t lock; /* Used for resource sharing & interrupt lock */
>> +
>> +	/* Filters */
>> +	struct filter_params *filters;
>> +	unsigned int free_filter_mask;
>> +	unsigned int scd_filter_mask;
>> +	unsigned int ckab_filter_mask;
>> +
>> +	/* Channels */
>> +	struct stm32_dfsdm_channel *channels;
>> +	unsigned int free_channel_mask;
>> +	atomic_t n_active_ch;
>> +
>> +	/* Clock */
>> +	struct clk *clk;
>> +	struct clk *aclk;
>> +	unsigned int clkout_div;
>> +	unsigned int clkout_freq_req;
>> +
>> +	/* Registers*/
>> +	void __iomem *base;
>> +	struct regmap *regmap;
>> +	phys_addr_t phys_base;
>> +};
>> +
>> +/*
>> + * Common
>> + */
>> +static bool stm32_dfsdm_volatile_reg(struct device *dev, unsigned int reg)
>> +{
>> +	if (reg < DFSDM_FILTER_BASE_ADR)
>> +		return false;
>> +
>> +	/*
>> +	 * Mask is done on register to avoid to list registers of all them
>> +	 * filter instances.
>> +	 */
>> +	switch (reg & DFSDM_FILTER_REG_MASK) {
>> +	case DFSDM_CR1(0) & DFSDM_FILTER_REG_MASK:
>> +	case DFSDM_ISR(0) & DFSDM_FILTER_REG_MASK:
>> +	case DFSDM_JDATAR(0) & DFSDM_FILTER_REG_MASK:
>> +	case DFSDM_RDATAR(0) & DFSDM_FILTER_REG_MASK:
>> +		return true;
>> +	}
>> +
>> +	return false;
>> +}
>> +
>> +static const struct regmap_config stm32h7_dfsdm_regmap_cfg = {
>> +	.reg_bits = 32,
>> +	.val_bits = 32,
>> +	.reg_stride = sizeof(u32),
>> +	.max_register = DFSDM_CNVTIMR(STM32H7_DFSDM_NUM_FILTERS - 1),
>> +	.volatile_reg = stm32_dfsdm_volatile_reg,
>> +	.fast_io = true,
>> +};
>> +
>> +static const struct stm32_dev_data stm32h7_data = {
>> +	.dfsdm.max_channels = STM32H7_DFSDM_NUM_INPUTS,
>> +	.dfsdm.max_filters = STM32H7_DFSDM_NUM_FILTERS,
>> +	.regmap_cfg = &stm32h7_dfsdm_regmap_cfg,
>> +};
>> +

[...]

>> +
>> +/**
>> + * stm32_dfsdm_get_filter_dma_addr - Get register address for dma transfer.
>> + *
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + * @fl_id: Filter id.
>> + * @conv: Conversion type.
>> + */
>> +dma_addr_t stm32_dfsdm_get_filter_dma_phy_addr(struct stm32_dfsdm *dfsdm,
>> +					       unsigned int fl_id,
>> +					       enum stm32_dfsdm_conv_type conv)
>> +{
>> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
>> +
>> +	if (conv == DFSDM_FILTER_REG_CONV)
>> +		return (dma_addr_t)(priv->phys_base + DFSDM_RDATAR(fl_id));
>> +	else
>> +		return (dma_addr_t)(priv->phys_base + DFSDM_JDATAR(fl_id));
>> +}
>> +
>> +/**
>> + * stm32_dfsdm_register_fl_event - Register filter event.
> What is a filter event?  More details good on things that are very
> device specific like this.
Filter events correspond to filter IRQ status, will be handled in a
different way in IIO.
>> + *
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + * @fl_id: Filter id.
>> + * @event: Event to unregister.
>> + * @chan_mask: Mask of channels associated to filter.
>> + *
>> + * The function enables associated IRQ.
>> + */
>> +int stm32_dfsdm_register_fl_event(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
>> +				  enum stm32_dfsdm_events event,
>> +				  unsigned int chan_mask)
>> +{
>> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
>> +	unsigned long flags, ulmask = chan_mask;
>> +	int ret, i;
>> +
>> +	dev_dbg(&priv->pdev->dev, "%s:for filter %d: event %#x ch_mask %#x\n",
>> +		__func__, fl_id, event, chan_mask);
>> +
>> +	if (event > DFSDM_EVENT_CKA)
>> +		return -EINVAL;
>> +
>> +	/* Clear interrupt before enable them */
>> +	ret = stm32_dfsdm_clear_event(priv, fl_id, event, chan_mask);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	spin_lock_irqsave(&priv->lock, flags);
>> +	/* Enable interrupts */
>> +	switch (event) {
>> +	case DFSDM_EVENT_SCD:
>> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
>> +					  DFSDM_CHCFGR1_SCDEN_MASK,
>> +					  DFSDM_CHCFGR1_SCDEN(1));
>> +		}
>> +		if (!priv->scd_filter_mask)
>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
>> +					  DFSDM_CR2_SCDIE_MASK,
>> +					  DFSDM_CR2_SCDIE(1));
>> +		priv->scd_filter_mask |= BIT(fl_id);
>> +		break;
>> +	case DFSDM_EVENT_CKA:
>> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
>> +					  DFSDM_CHCFGR1_CKABEN_MASK,
>> +					  DFSDM_CHCFGR1_CKABEN(1));
>> +		}
>> +		if (!priv->ckab_filter_mask)
>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
>> +					  DFSDM_CR2_CKABIE_MASK,
>> +					  DFSDM_CR2_CKABIE(1));
>> +		priv->ckab_filter_mask |= BIT(fl_id);
>> +		break;
>> +	default:
>> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(fl_id), event, event);
>> +	}
>> +	priv->filters[fl_id].event_mask |= event;
>> +	spin_unlock_irqrestore(&priv->lock, flags);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(dfsdm_register_fl_event);
>> +
>> +/**
>> + * stm32_dfsdm_unregister_fl_event - Unregister filter event.
>> + *
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + * @fl_id: Filter id.
>> + * @event: Event to unregister.
>> + * @chan_mask: Mask of channels associated to filter.
>> + *
>> + * The function disables associated IRQ.
>> + */
>> +int stm32_dfsdm_unregister_fl_event(struct stm32_dfsdm *dfsdm,
>> +				    unsigned int fl_id,
>> +				    enum stm32_dfsdm_events event,
>> +				    unsigned int chan_mask)
>> +{
>> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
>> +	unsigned long flags, ulmask = chan_mask;
>> +	int i;
>> +
>> +	dev_dbg(&priv->pdev->dev, "%s:for filter %d: event %#x ch_mask %#x\n",
>> +		__func__, fl_id, event, chan_mask);
>> +
>> +	if (event > DFSDM_EVENT_CKA)
>> +		return -EINVAL;
>> +
>> +	spin_lock_irqsave(&priv->lock, flags);
>> +	/* Disable interrupts */
>> +	switch (event) {
>> +	case DFSDM_EVENT_SCD:
>> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
>> +					  DFSDM_CHCFGR1_SCDEN_MASK,
>> +					  DFSDM_CHCFGR1_SCDEN(0));
>> +		}
>> +		priv->scd_filter_mask &= ~BIT(fl_id);
>> +		if (!priv->scd_filter_mask)
>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
>> +					  DFSDM_CR2_SCDIE_MASK,
>> +					  DFSDM_CR2_SCDIE(0));
>> +		break;
>> +	case DFSDM_EVENT_CKA:
>> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
>> +					  DFSDM_CHCFGR1_CKABEN_MASK,
>> +					  DFSDM_CHCFGR1_CKABEN(0));
>> +		}
>> +		priv->ckab_filter_mask &= ~BIT(fl_id);
>> +		if (!priv->ckab_filter_mask)
>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
>> +					  DFSDM_CR2_CKABIE_MASK,
>> +					  DFSDM_CR2_CKABIE(0));
>> +		break;
>> +	default:
>> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(fl_id), event, 0);
>> +	}
>> +
>> +	priv->filters[fl_id].event_mask &= ~event;
>> +	spin_unlock_irqrestore(&priv->lock, flags);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(dfsdm_unregister_fl_event);

[...]
>> +/* DFSDM filter conversion type */
>> +enum stm32_dfsdm_conv_type {
>> +	DFSDM_FILTER_REG_CONV,      /* Regular conversion */
>> +	DFSDM_FILTER_SW_INJ_CONV,   /* Injected conversion */
>> +	DFSDM_FILTER_TRIG_INJ_CONV, /* Injected conversion */
>> +};
>> +
>> +/* DFSDM filter regular synchronous mode */
>> +enum stm32_dfsdm_conv_rsync {
>> +	DFSDM_FILTER_RSYNC_OFF, /* regular conversion asynchronous */
>> +	DFSDM_FILTER_RSYNC_ON,  /* regular conversion synchronous with filter0*/
> stray 0?
Should read "filter instance 0"...
This corresponds to a specificity of the DFSDM hardware. DFSDM can offer
possibility to synchronize each filter output with the filter 0 instance
output.
As example, this can be used to synchronize several audio microphones.
Filter 0 is allocated to main microphones and the other filters for
background microphones (notice that we need one filter per 1-bit PDM stream)

[...]

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-30 15:08           ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-30 15:08 UTC (permalink / raw)
  To: Jonathan Cameron, devicetree, linux-arm-kernel, linux-iio,
	alsa-devel, Lee Jones, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre TORGUE

Hello Jonathan,

Thanks for the review. This drivers should disappear,
but i will integrate you comment/remark in my redesign.

Please find some comments below, on specific points

Regards
Arnaud


On 01/29/2017 12:53 PM, Jonathan Cameron wrote:
> On 23/01/17 16:32, Arnaud Pouliquen wrote:
>> DFSDM hardware IP can be used at the same time for ADC sigma delta
>> conversion and audio PDM microphone.
>> MFD driver is in charge of configuring IP registers and managing IP clocks.
>> For this it exports an API to handles filters and channels resources.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> This is somewhat of a beast.  I would be tempted to build it up in more
> bite sized chunks.
> 
> Obvious things to drop from a first version (basically to make it easier
> to review) would be injected supported.  There may well be others but you'll
> have a better feel for that than me.
i will pay attention for next version.
> 
> I really don't like the wrappers round the regmap functions though.
> They mean you are papering over errors if they occur and make the code
> slightly harder to read by implying that something else is going on.
> 
One aim of the wrapper was to simplify code review, seems that just the
opposite...
As i have around 50 regmap accesses, adding  a return/goto for each
error and at least an associated error messages for each function,
should add 100 to 150 extra lines...

> Yes the code will be longer without them, but you will also be forced to think
> properly about error paths.

I have a question around this. What should be the action if a register
access return an error?
Possible root causes:
	- bad address of the IP (DT)
	- IP not clocked or in reset (driver BUG).
	- IP is out of order (hardware issue)
	- bug in driver that access to an invalid address.

So except for the last root cause,we can suppose that every register
accesses should always be OK or KO...
This is also a reason of the wrapper. Detect driver bug, without adding
a test on each register access return.

Perhaps a compromise could be that test is done only during some
specific phase (probe, after reset deassertion, clock enabling...) and
then trust access without test?
Or simply add error message in regmap helper routines...

Please just tell/confirm me your preference.

> 
> Anyhow, was mostly reading this to get a feel for what was going on in the
> whole series so not really a terribly thorough review I'm afraid. Sorry about
> that!
More than enough for this first version :)

> 
> Jonathan
>> ---
>>  drivers/mfd/Kconfig             |   11 +
>>  drivers/mfd/Makefile            |    2 +
>>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
>>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
>>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
>>  5 files changed, 1601 insertions(+)
>>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
>>  create mode 100644 drivers/mfd/stm32-dfsdm.c
>>  create mode 100644 include/linux/mfd/stm32-dfsdm.h


[...]

>> diff --git a/drivers/mfd/stm32-dfsdm.c b/drivers/mfd/stm32-dfsdm.c
>> new file mode 100644
>> index 0000000..81ca29c
>> --- /dev/null
>> +++ b/drivers/mfd/stm32-dfsdm.c
>> @@ -0,0 +1,1044 @@
>> +/*
>> + * This file is part of STM32 DFSDM mfd driver
>> + *
>> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
>> + * Author(s): Arnaud Pouliquen <arnaud.pouliquen@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/interrupt.h>
>> +#include <linux/mfd/core.h>
>> +#include <linux/module.h>
>> +#include <linux/of_device.h>
>> +#include <linux/regmap.h>
>> +#include <linux/slab.h>
>> +
>> +#include <linux/mfd/stm32-dfsdm.h>
>> +
>> +#include "stm32-dfsdm-reg.h"
>> +
>> +#define DFSDM_UPDATE_BITS(regm, reg, mask, val) \
>> +		WARN_ON(regmap_update_bits(regm, reg, mask, val))
> Don't do these wrappers please. Handle error correctly in all cases.
> Reviewing as I tend to do backwards through the driver, I thought these
> were doing something interesting.
> 
> Effectively you have no error handling as a result of these which needs
> fixing.
> 
>> +
>> +#define DFSDM_REG_READ(regm, reg, val) \
>> +		WARN_ON(regmap_read(regm, reg, val))
>> +
>> +#define DFSDM_REG_WRITE(regm, reg, val) \
>> +		WARN_ON(regmap_write(regm, reg, val))
>> +
>> +#define STM32H7_DFSDM_NUM_FILTERS	4
>> +#define STM32H7_DFSDM_NUM_INPUTS	8
>> +
>> +enum dfsdm_clkout_src {
>> +	DFSDM_CLK,
>> +	AUDIO_CLK
>> +};
>> +
>> +struct stm32_dev_data {
>> +	const struct stm32_dfsdm dfsdm;
>> +	const struct regmap_config *regmap_cfg;
>> +};
>> +
>> +struct dfsdm_priv;
>> +
>> +struct filter_params {
>> +	unsigned int id;
>> +	int irq;
>> +	struct stm32_dfsdm_fl_event event;
>> +	u32 event_mask;
>> +	struct dfsdm_priv *priv; /* Cross ref for context */
>> +	unsigned int ext_ch_mask;
>> +	unsigned int scan_ch;
>> +};
>> +
>> +struct ch_params {
>> +	struct stm32_dfsdm_channel ch;
>> +};
>> +
> I'd like to see a lot more comments in here.  Perhaps full kernel-doc
> as some elements are not that obvious at least to a fairly casual read.
> 
Description in device-tree tree bindings and cover-letter is not
sufficient? you would a doc in Document/arm/stm32?

>> +struct dfsdm_priv {
>> +	struct platform_device *pdev;
>> +	struct stm32_dfsdm dfsdm;
>> +
>> +	spinlock_t lock; /* Used for resource sharing & interrupt lock */
>> +
>> +	/* Filters */
>> +	struct filter_params *filters;
>> +	unsigned int free_filter_mask;
>> +	unsigned int scd_filter_mask;
>> +	unsigned int ckab_filter_mask;
>> +
>> +	/* Channels */
>> +	struct stm32_dfsdm_channel *channels;
>> +	unsigned int free_channel_mask;
>> +	atomic_t n_active_ch;
>> +
>> +	/* Clock */
>> +	struct clk *clk;
>> +	struct clk *aclk;
>> +	unsigned int clkout_div;
>> +	unsigned int clkout_freq_req;
>> +
>> +	/* Registers*/
>> +	void __iomem *base;
>> +	struct regmap *regmap;
>> +	phys_addr_t phys_base;
>> +};
>> +
>> +/*
>> + * Common
>> + */
>> +static bool stm32_dfsdm_volatile_reg(struct device *dev, unsigned int reg)
>> +{
>> +	if (reg < DFSDM_FILTER_BASE_ADR)
>> +		return false;
>> +
>> +	/*
>> +	 * Mask is done on register to avoid to list registers of all them
>> +	 * filter instances.
>> +	 */
>> +	switch (reg & DFSDM_FILTER_REG_MASK) {
>> +	case DFSDM_CR1(0) & DFSDM_FILTER_REG_MASK:
>> +	case DFSDM_ISR(0) & DFSDM_FILTER_REG_MASK:
>> +	case DFSDM_JDATAR(0) & DFSDM_FILTER_REG_MASK:
>> +	case DFSDM_RDATAR(0) & DFSDM_FILTER_REG_MASK:
>> +		return true;
>> +	}
>> +
>> +	return false;
>> +}
>> +
>> +static const struct regmap_config stm32h7_dfsdm_regmap_cfg = {
>> +	.reg_bits = 32,
>> +	.val_bits = 32,
>> +	.reg_stride = sizeof(u32),
>> +	.max_register = DFSDM_CNVTIMR(STM32H7_DFSDM_NUM_FILTERS - 1),
>> +	.volatile_reg = stm32_dfsdm_volatile_reg,
>> +	.fast_io = true,
>> +};
>> +
>> +static const struct stm32_dev_data stm32h7_data = {
>> +	.dfsdm.max_channels = STM32H7_DFSDM_NUM_INPUTS,
>> +	.dfsdm.max_filters = STM32H7_DFSDM_NUM_FILTERS,
>> +	.regmap_cfg = &stm32h7_dfsdm_regmap_cfg,
>> +};
>> +

[...]

>> +
>> +/**
>> + * stm32_dfsdm_get_filter_dma_addr - Get register address for dma transfer.
>> + *
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + * @fl_id: Filter id.
>> + * @conv: Conversion type.
>> + */
>> +dma_addr_t stm32_dfsdm_get_filter_dma_phy_addr(struct stm32_dfsdm *dfsdm,
>> +					       unsigned int fl_id,
>> +					       enum stm32_dfsdm_conv_type conv)
>> +{
>> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
>> +
>> +	if (conv == DFSDM_FILTER_REG_CONV)
>> +		return (dma_addr_t)(priv->phys_base + DFSDM_RDATAR(fl_id));
>> +	else
>> +		return (dma_addr_t)(priv->phys_base + DFSDM_JDATAR(fl_id));
>> +}
>> +
>> +/**
>> + * stm32_dfsdm_register_fl_event - Register filter event.
> What is a filter event?  More details good on things that are very
> device specific like this.
Filter events correspond to filter IRQ status, will be handled in a
different way in IIO.
>> + *
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + * @fl_id: Filter id.
>> + * @event: Event to unregister.
>> + * @chan_mask: Mask of channels associated to filter.
>> + *
>> + * The function enables associated IRQ.
>> + */
>> +int stm32_dfsdm_register_fl_event(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
>> +				  enum stm32_dfsdm_events event,
>> +				  unsigned int chan_mask)
>> +{
>> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
>> +	unsigned long flags, ulmask = chan_mask;
>> +	int ret, i;
>> +
>> +	dev_dbg(&priv->pdev->dev, "%s:for filter %d: event %#x ch_mask %#x\n",
>> +		__func__, fl_id, event, chan_mask);
>> +
>> +	if (event > DFSDM_EVENT_CKA)
>> +		return -EINVAL;
>> +
>> +	/* Clear interrupt before enable them */
>> +	ret = stm32_dfsdm_clear_event(priv, fl_id, event, chan_mask);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	spin_lock_irqsave(&priv->lock, flags);
>> +	/* Enable interrupts */
>> +	switch (event) {
>> +	case DFSDM_EVENT_SCD:
>> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
>> +					  DFSDM_CHCFGR1_SCDEN_MASK,
>> +					  DFSDM_CHCFGR1_SCDEN(1));
>> +		}
>> +		if (!priv->scd_filter_mask)
>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
>> +					  DFSDM_CR2_SCDIE_MASK,
>> +					  DFSDM_CR2_SCDIE(1));
>> +		priv->scd_filter_mask |= BIT(fl_id);
>> +		break;
>> +	case DFSDM_EVENT_CKA:
>> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
>> +					  DFSDM_CHCFGR1_CKABEN_MASK,
>> +					  DFSDM_CHCFGR1_CKABEN(1));
>> +		}
>> +		if (!priv->ckab_filter_mask)
>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
>> +					  DFSDM_CR2_CKABIE_MASK,
>> +					  DFSDM_CR2_CKABIE(1));
>> +		priv->ckab_filter_mask |= BIT(fl_id);
>> +		break;
>> +	default:
>> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(fl_id), event, event);
>> +	}
>> +	priv->filters[fl_id].event_mask |= event;
>> +	spin_unlock_irqrestore(&priv->lock, flags);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(dfsdm_register_fl_event);
>> +
>> +/**
>> + * stm32_dfsdm_unregister_fl_event - Unregister filter event.
>> + *
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + * @fl_id: Filter id.
>> + * @event: Event to unregister.
>> + * @chan_mask: Mask of channels associated to filter.
>> + *
>> + * The function disables associated IRQ.
>> + */
>> +int stm32_dfsdm_unregister_fl_event(struct stm32_dfsdm *dfsdm,
>> +				    unsigned int fl_id,
>> +				    enum stm32_dfsdm_events event,
>> +				    unsigned int chan_mask)
>> +{
>> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
>> +	unsigned long flags, ulmask = chan_mask;
>> +	int i;
>> +
>> +	dev_dbg(&priv->pdev->dev, "%s:for filter %d: event %#x ch_mask %#x\n",
>> +		__func__, fl_id, event, chan_mask);
>> +
>> +	if (event > DFSDM_EVENT_CKA)
>> +		return -EINVAL;
>> +
>> +	spin_lock_irqsave(&priv->lock, flags);
>> +	/* Disable interrupts */
>> +	switch (event) {
>> +	case DFSDM_EVENT_SCD:
>> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
>> +					  DFSDM_CHCFGR1_SCDEN_MASK,
>> +					  DFSDM_CHCFGR1_SCDEN(0));
>> +		}
>> +		priv->scd_filter_mask &= ~BIT(fl_id);
>> +		if (!priv->scd_filter_mask)
>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
>> +					  DFSDM_CR2_SCDIE_MASK,
>> +					  DFSDM_CR2_SCDIE(0));
>> +		break;
>> +	case DFSDM_EVENT_CKA:
>> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
>> +					  DFSDM_CHCFGR1_CKABEN_MASK,
>> +					  DFSDM_CHCFGR1_CKABEN(0));
>> +		}
>> +		priv->ckab_filter_mask &= ~BIT(fl_id);
>> +		if (!priv->ckab_filter_mask)
>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
>> +					  DFSDM_CR2_CKABIE_MASK,
>> +					  DFSDM_CR2_CKABIE(0));
>> +		break;
>> +	default:
>> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(fl_id), event, 0);
>> +	}
>> +
>> +	priv->filters[fl_id].event_mask &= ~event;
>> +	spin_unlock_irqrestore(&priv->lock, flags);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(dfsdm_unregister_fl_event);

[...]
>> +/* DFSDM filter conversion type */
>> +enum stm32_dfsdm_conv_type {
>> +	DFSDM_FILTER_REG_CONV,      /* Regular conversion */
>> +	DFSDM_FILTER_SW_INJ_CONV,   /* Injected conversion */
>> +	DFSDM_FILTER_TRIG_INJ_CONV, /* Injected conversion */
>> +};
>> +
>> +/* DFSDM filter regular synchronous mode */
>> +enum stm32_dfsdm_conv_rsync {
>> +	DFSDM_FILTER_RSYNC_OFF, /* regular conversion asynchronous */
>> +	DFSDM_FILTER_RSYNC_ON,  /* regular conversion synchronous with filter0*/
> stray 0?
Should read "filter instance 0"...
This corresponds to a specificity of the DFSDM hardware. DFSDM can offer
possibility to synchronize each filter output with the filter 0 instance
output.
As example, this can be used to synchronize several audio microphones.
Filter 0 is allocated to main microphones and the other filters for
background microphones (notice that we need one filter per 1-bit PDM stream)

[...]

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

* [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-30 15:08           ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-30 15:08 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Jonathan,

Thanks for the review. This drivers should disappear,
but i will integrate you comment/remark in my redesign.

Please find some comments below, on specific points

Regards
Arnaud


On 01/29/2017 12:53 PM, Jonathan Cameron wrote:
> On 23/01/17 16:32, Arnaud Pouliquen wrote:
>> DFSDM hardware IP can be used at the same time for ADC sigma delta
>> conversion and audio PDM microphone.
>> MFD driver is in charge of configuring IP registers and managing IP clocks.
>> For this it exports an API to handles filters and channels resources.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> This is somewhat of a beast.  I would be tempted to build it up in more
> bite sized chunks.
> 
> Obvious things to drop from a first version (basically to make it easier
> to review) would be injected supported.  There may well be others but you'll
> have a better feel for that than me.
i will pay attention for next version.
> 
> I really don't like the wrappers round the regmap functions though.
> They mean you are papering over errors if they occur and make the code
> slightly harder to read by implying that something else is going on.
> 
One aim of the wrapper was to simplify code review, seems that just the
opposite...
As i have around 50 regmap accesses, adding  a return/goto for each
error and at least an associated error messages for each function,
should add 100 to 150 extra lines...

> Yes the code will be longer without them, but you will also be forced to think
> properly about error paths.

I have a question around this. What should be the action if a register
access return an error?
Possible root causes:
	- bad address of the IP (DT)
	- IP not clocked or in reset (driver BUG).
	- IP is out of order (hardware issue)
	- bug in driver that access to an invalid address.

So except for the last root cause,we can suppose that every register
accesses should always be OK or KO...
This is also a reason of the wrapper. Detect driver bug, without adding
a test on each register access return.

Perhaps a compromise could be that test is done only during some
specific phase (probe, after reset deassertion, clock enabling...) and
then trust access without test?
Or simply add error message in regmap helper routines...

Please just tell/confirm me your preference.

> 
> Anyhow, was mostly reading this to get a feel for what was going on in the
> whole series so not really a terribly thorough review I'm afraid. Sorry about
> that!
More than enough for this first version :)

> 
> Jonathan
>> ---
>>  drivers/mfd/Kconfig             |   11 +
>>  drivers/mfd/Makefile            |    2 +
>>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
>>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
>>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
>>  5 files changed, 1601 insertions(+)
>>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
>>  create mode 100644 drivers/mfd/stm32-dfsdm.c
>>  create mode 100644 include/linux/mfd/stm32-dfsdm.h


[...]

>> diff --git a/drivers/mfd/stm32-dfsdm.c b/drivers/mfd/stm32-dfsdm.c
>> new file mode 100644
>> index 0000000..81ca29c
>> --- /dev/null
>> +++ b/drivers/mfd/stm32-dfsdm.c
>> @@ -0,0 +1,1044 @@
>> +/*
>> + * This file is part of STM32 DFSDM mfd driver
>> + *
>> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
>> + * Author(s): Arnaud Pouliquen <arnaud.pouliquen@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/interrupt.h>
>> +#include <linux/mfd/core.h>
>> +#include <linux/module.h>
>> +#include <linux/of_device.h>
>> +#include <linux/regmap.h>
>> +#include <linux/slab.h>
>> +
>> +#include <linux/mfd/stm32-dfsdm.h>
>> +
>> +#include "stm32-dfsdm-reg.h"
>> +
>> +#define DFSDM_UPDATE_BITS(regm, reg, mask, val) \
>> +		WARN_ON(regmap_update_bits(regm, reg, mask, val))
> Don't do these wrappers please. Handle error correctly in all cases.
> Reviewing as I tend to do backwards through the driver, I thought these
> were doing something interesting.
> 
> Effectively you have no error handling as a result of these which needs
> fixing.
> 
>> +
>> +#define DFSDM_REG_READ(regm, reg, val) \
>> +		WARN_ON(regmap_read(regm, reg, val))
>> +
>> +#define DFSDM_REG_WRITE(regm, reg, val) \
>> +		WARN_ON(regmap_write(regm, reg, val))
>> +
>> +#define STM32H7_DFSDM_NUM_FILTERS	4
>> +#define STM32H7_DFSDM_NUM_INPUTS	8
>> +
>> +enum dfsdm_clkout_src {
>> +	DFSDM_CLK,
>> +	AUDIO_CLK
>> +};
>> +
>> +struct stm32_dev_data {
>> +	const struct stm32_dfsdm dfsdm;
>> +	const struct regmap_config *regmap_cfg;
>> +};
>> +
>> +struct dfsdm_priv;
>> +
>> +struct filter_params {
>> +	unsigned int id;
>> +	int irq;
>> +	struct stm32_dfsdm_fl_event event;
>> +	u32 event_mask;
>> +	struct dfsdm_priv *priv; /* Cross ref for context */
>> +	unsigned int ext_ch_mask;
>> +	unsigned int scan_ch;
>> +};
>> +
>> +struct ch_params {
>> +	struct stm32_dfsdm_channel ch;
>> +};
>> +
> I'd like to see a lot more comments in here.  Perhaps full kernel-doc
> as some elements are not that obvious at least to a fairly casual read.
> 
Description in device-tree tree bindings and cover-letter is not
sufficient? you would a doc in Document/arm/stm32?

>> +struct dfsdm_priv {
>> +	struct platform_device *pdev;
>> +	struct stm32_dfsdm dfsdm;
>> +
>> +	spinlock_t lock; /* Used for resource sharing & interrupt lock */
>> +
>> +	/* Filters */
>> +	struct filter_params *filters;
>> +	unsigned int free_filter_mask;
>> +	unsigned int scd_filter_mask;
>> +	unsigned int ckab_filter_mask;
>> +
>> +	/* Channels */
>> +	struct stm32_dfsdm_channel *channels;
>> +	unsigned int free_channel_mask;
>> +	atomic_t n_active_ch;
>> +
>> +	/* Clock */
>> +	struct clk *clk;
>> +	struct clk *aclk;
>> +	unsigned int clkout_div;
>> +	unsigned int clkout_freq_req;
>> +
>> +	/* Registers*/
>> +	void __iomem *base;
>> +	struct regmap *regmap;
>> +	phys_addr_t phys_base;
>> +};
>> +
>> +/*
>> + * Common
>> + */
>> +static bool stm32_dfsdm_volatile_reg(struct device *dev, unsigned int reg)
>> +{
>> +	if (reg < DFSDM_FILTER_BASE_ADR)
>> +		return false;
>> +
>> +	/*
>> +	 * Mask is done on register to avoid to list registers of all them
>> +	 * filter instances.
>> +	 */
>> +	switch (reg & DFSDM_FILTER_REG_MASK) {
>> +	case DFSDM_CR1(0) & DFSDM_FILTER_REG_MASK:
>> +	case DFSDM_ISR(0) & DFSDM_FILTER_REG_MASK:
>> +	case DFSDM_JDATAR(0) & DFSDM_FILTER_REG_MASK:
>> +	case DFSDM_RDATAR(0) & DFSDM_FILTER_REG_MASK:
>> +		return true;
>> +	}
>> +
>> +	return false;
>> +}
>> +
>> +static const struct regmap_config stm32h7_dfsdm_regmap_cfg = {
>> +	.reg_bits = 32,
>> +	.val_bits = 32,
>> +	.reg_stride = sizeof(u32),
>> +	.max_register = DFSDM_CNVTIMR(STM32H7_DFSDM_NUM_FILTERS - 1),
>> +	.volatile_reg = stm32_dfsdm_volatile_reg,
>> +	.fast_io = true,
>> +};
>> +
>> +static const struct stm32_dev_data stm32h7_data = {
>> +	.dfsdm.max_channels = STM32H7_DFSDM_NUM_INPUTS,
>> +	.dfsdm.max_filters = STM32H7_DFSDM_NUM_FILTERS,
>> +	.regmap_cfg = &stm32h7_dfsdm_regmap_cfg,
>> +};
>> +

[...]

>> +
>> +/**
>> + * stm32_dfsdm_get_filter_dma_addr - Get register address for dma transfer.
>> + *
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + * @fl_id: Filter id.
>> + * @conv: Conversion type.
>> + */
>> +dma_addr_t stm32_dfsdm_get_filter_dma_phy_addr(struct stm32_dfsdm *dfsdm,
>> +					       unsigned int fl_id,
>> +					       enum stm32_dfsdm_conv_type conv)
>> +{
>> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
>> +
>> +	if (conv == DFSDM_FILTER_REG_CONV)
>> +		return (dma_addr_t)(priv->phys_base + DFSDM_RDATAR(fl_id));
>> +	else
>> +		return (dma_addr_t)(priv->phys_base + DFSDM_JDATAR(fl_id));
>> +}
>> +
>> +/**
>> + * stm32_dfsdm_register_fl_event - Register filter event.
> What is a filter event?  More details good on things that are very
> device specific like this.
Filter events correspond to filter IRQ status, will be handled in a
different way in IIO.
>> + *
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + * @fl_id: Filter id.
>> + * @event: Event to unregister.
>> + * @chan_mask: Mask of channels associated to filter.
>> + *
>> + * The function enables associated IRQ.
>> + */
>> +int stm32_dfsdm_register_fl_event(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
>> +				  enum stm32_dfsdm_events event,
>> +				  unsigned int chan_mask)
>> +{
>> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
>> +	unsigned long flags, ulmask = chan_mask;
>> +	int ret, i;
>> +
>> +	dev_dbg(&priv->pdev->dev, "%s:for filter %d: event %#x ch_mask %#x\n",
>> +		__func__, fl_id, event, chan_mask);
>> +
>> +	if (event > DFSDM_EVENT_CKA)
>> +		return -EINVAL;
>> +
>> +	/* Clear interrupt before enable them */
>> +	ret = stm32_dfsdm_clear_event(priv, fl_id, event, chan_mask);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	spin_lock_irqsave(&priv->lock, flags);
>> +	/* Enable interrupts */
>> +	switch (event) {
>> +	case DFSDM_EVENT_SCD:
>> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
>> +					  DFSDM_CHCFGR1_SCDEN_MASK,
>> +					  DFSDM_CHCFGR1_SCDEN(1));
>> +		}
>> +		if (!priv->scd_filter_mask)
>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
>> +					  DFSDM_CR2_SCDIE_MASK,
>> +					  DFSDM_CR2_SCDIE(1));
>> +		priv->scd_filter_mask |= BIT(fl_id);
>> +		break;
>> +	case DFSDM_EVENT_CKA:
>> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
>> +					  DFSDM_CHCFGR1_CKABEN_MASK,
>> +					  DFSDM_CHCFGR1_CKABEN(1));
>> +		}
>> +		if (!priv->ckab_filter_mask)
>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
>> +					  DFSDM_CR2_CKABIE_MASK,
>> +					  DFSDM_CR2_CKABIE(1));
>> +		priv->ckab_filter_mask |= BIT(fl_id);
>> +		break;
>> +	default:
>> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(fl_id), event, event);
>> +	}
>> +	priv->filters[fl_id].event_mask |= event;
>> +	spin_unlock_irqrestore(&priv->lock, flags);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(dfsdm_register_fl_event);
>> +
>> +/**
>> + * stm32_dfsdm_unregister_fl_event - Unregister filter event.
>> + *
>> + * @dfsdm: Handle used to retrieve dfsdm context.
>> + * @fl_id: Filter id.
>> + * @event: Event to unregister.
>> + * @chan_mask: Mask of channels associated to filter.
>> + *
>> + * The function disables associated IRQ.
>> + */
>> +int stm32_dfsdm_unregister_fl_event(struct stm32_dfsdm *dfsdm,
>> +				    unsigned int fl_id,
>> +				    enum stm32_dfsdm_events event,
>> +				    unsigned int chan_mask)
>> +{
>> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
>> +	unsigned long flags, ulmask = chan_mask;
>> +	int i;
>> +
>> +	dev_dbg(&priv->pdev->dev, "%s:for filter %d: event %#x ch_mask %#x\n",
>> +		__func__, fl_id, event, chan_mask);
>> +
>> +	if (event > DFSDM_EVENT_CKA)
>> +		return -EINVAL;
>> +
>> +	spin_lock_irqsave(&priv->lock, flags);
>> +	/* Disable interrupts */
>> +	switch (event) {
>> +	case DFSDM_EVENT_SCD:
>> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
>> +					  DFSDM_CHCFGR1_SCDEN_MASK,
>> +					  DFSDM_CHCFGR1_SCDEN(0));
>> +		}
>> +		priv->scd_filter_mask &= ~BIT(fl_id);
>> +		if (!priv->scd_filter_mask)
>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
>> +					  DFSDM_CR2_SCDIE_MASK,
>> +					  DFSDM_CR2_SCDIE(0));
>> +		break;
>> +	case DFSDM_EVENT_CKA:
>> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
>> +					  DFSDM_CHCFGR1_CKABEN_MASK,
>> +					  DFSDM_CHCFGR1_CKABEN(0));
>> +		}
>> +		priv->ckab_filter_mask &= ~BIT(fl_id);
>> +		if (!priv->ckab_filter_mask)
>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
>> +					  DFSDM_CR2_CKABIE_MASK,
>> +					  DFSDM_CR2_CKABIE(0));
>> +		break;
>> +	default:
>> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(fl_id), event, 0);
>> +	}
>> +
>> +	priv->filters[fl_id].event_mask &= ~event;
>> +	spin_unlock_irqrestore(&priv->lock, flags);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(dfsdm_unregister_fl_event);

[...]
>> +/* DFSDM filter conversion type */
>> +enum stm32_dfsdm_conv_type {
>> +	DFSDM_FILTER_REG_CONV,      /* Regular conversion */
>> +	DFSDM_FILTER_SW_INJ_CONV,   /* Injected conversion */
>> +	DFSDM_FILTER_TRIG_INJ_CONV, /* Injected conversion */
>> +};
>> +
>> +/* DFSDM filter regular synchronous mode */
>> +enum stm32_dfsdm_conv_rsync {
>> +	DFSDM_FILTER_RSYNC_OFF, /* regular conversion asynchronous */
>> +	DFSDM_FILTER_RSYNC_ON,  /* regular conversion synchronous with filter0*/
> stray 0?
Should read "filter instance 0"...
This corresponds to a specificity of the DFSDM hardware. DFSDM can offer
possibility to synchronize each filter output with the filter 0 instance
output.
As example, this can be used to synchronize several audio microphones.
Filter 0 is allocated to main microphones and the other filters for
background microphones (notice that we need one filter per 1-bit PDM stream)

[...]

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

* Re: [PATCH 3/7] IIO: add bindings for STM32 DFSDM ADC driver
  2017-01-29 11:58       ` Jonathan Cameron
  (?)
@ 2017-01-30 15:26         ` Arnaud Pouliquen
  -1 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-30 15:26 UTC (permalink / raw)
  To: Jonathan Cameron, devicetree, linux-arm-kernel, linux-iio,
	alsa-devel, Lee Jones, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Alexandre TORGUE, Maxime Coquelin

Hello Jonathan,

My comments below.

Regards

Arnaud
On 01/29/2017 12:58 PM, Jonathan Cameron wrote:
> On 23/01/17 16:32, Arnaud Pouliquen wrote:
>> This patch adds documentation of device tree bindings for the
>> STM32 DFSDM ADC.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> Trivial bits inline.
>> ---
>>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 60 ++++++++++++++++++++++
>>  1 file changed, 60 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
>>
>> diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt  b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
>> new file mode 100644
>> index 0000000..c156bcb
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 	
>> @@ -0,0 +1,60 @@
>> +STMicroelectronics STM32 DFSDM ADC device driver
>> +
>> +STM32 DFSDM ADC is a sigma delta analog-to-digital converter.
>> +It has to be declared in device-tree as a subnode of the DFSDM mfd node.
>> +
>> +It has several multiplexed input channels. Conversions can be performed
>> +in single, scan or discontinuous mode. Conversions can be launched in software
>> +or using hardware triggers.
>> +Each instance of the driver uses one filter instance handle by the DFSDM mfd
>> +driver.
>> +
>> +DFSDM also offers extra features:
>> +-The analog watchdog feature allows the application to detect if the
>> + input voltage goes beyond the user-defined, higher or lower thresholds.
>> +-The short circuit detection allows allows the application to detect if the
>> + input is in CC.
>> +-The clock absence detection allows application to detect if SPI input is clocked.
>> +
>> +Required properties:
>> +- compatible:	Must be "st,stm32-dfsdm-adc".
>> +- reg:		Specifies the DFSDM filter instance.
>> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
>> +- st,adc-channels:	List of single-ended channels muxed for this ADC.
>> +- st,adc-channel-names:	List of single-ended channels Name.
>> +
>> +Optional properties:
>> +- st,adc-channel-types:	Single-ended channel input type. Default value is 0.
>> +			- 0: SPI with data on rising edge (default)
>> +			- 1: SPI with data on falling edge
> This is an element that can be described without magic numbers so I'd
> prefer that you do so.
> 
> Also spell check edage -> edge.
ok i will use strings
> 
>> +			- 2: manchester codec, rising edage = logic 0
>> +			- 3: manchester codec, rising edage = logic 1
>> +- st,adc-channel-clk-src: Conversion clock source. default value is 1.
>> +			  - 0: External SPI clocl (CLKIN x)
>> +			  - 1: internal SPI clock (CLKOUT) (default)
>> +			  - 2: internal SPI clock divided by 2 (falling edge).
>> +			  - 2: internal SPI clock divided by 2 (rising edge).
> 3?
>> +- st,adc-alt-channel: 	  must be defined if Two ADCs are connected on same SPI
>> +			  input.
>> +			  If not set channel n is connected to SPI input n.
>> +			  If set channel n is connected to SPI input n + 1.
> ? Two data inputs with shared clock?
Yes you can connect 2 digital microphones on one SPI.
One samples audio data on the clock rising edge, the other one samples
on the falling edge.
you can see an example in slide 15 of following document.
http://www.st.com/content/ccc/resource/training/technical/product_training/96/b6/2b/ea/72/3f/4e/d5/STM32L4_System_DFSDM.pdf/files/STM32L4_System_DFSDM.pdf/jcr:content/translations/en.STM32L4_System_DFSDM.pdf

I will describe this in an RFC to explain hardware but, to summarize,
there are 3 main "entities" in DFSDM:
- The serial interface: SPI or Manchester bus.
- The channel: it a kind of bridge than can be connected:
   on one side to a serial input (channel n connected to SPI interface n
or n + 1)
   on other side to one or several filters.

The filter: low pass filter + integrator.

>> +
>> +Example:
>> +	dfsdm: dfsdm@4400D000 {
>> +		iio_dfsdm0: iio-dfsdm@0 {
>> +			compatible = "st,stm32-dfsdm-adc";
>> +			#io-channel-cells = <1>;
>> +			reg = <0>;
>> +			st,adc-channels = <1>;
>> +			st,adc-channel-names = "in0";
>> +		};
>> +		iio_dfsdm1: iio-dfsdm@1 {
>> +			compatible = "st,stm32-dfsdm-adc";
>> +			#io-channel-cells = <1>;
>> +			reg = <1>;
>> +			st,adc-channels = <1>;
>> +			st,adc-channel-names = "in1";
>> +			st,adc-channel-types = <1>;
>> +			st,adc-alt-channel = <1>;
>> +		};
>> +	};
>>
> 

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

* Re: [PATCH 3/7] IIO: add bindings for STM32 DFSDM ADC driver
@ 2017-01-30 15:26         ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-30 15:26 UTC (permalink / raw)
  To: Jonathan Cameron, devicetree, linux-arm-kernel, linux-iio,
	alsa-devel, Lee Jones, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre TORGUE

Hello Jonathan,

My comments below.

Regards

Arnaud
On 01/29/2017 12:58 PM, Jonathan Cameron wrote:
> On 23/01/17 16:32, Arnaud Pouliquen wrote:
>> This patch adds documentation of device tree bindings for the
>> STM32 DFSDM ADC.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> Trivial bits inline.
>> ---
>>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 60 ++++++++++++++++++++++
>>  1 file changed, 60 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
>>
>> diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt  b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
>> new file mode 100644
>> index 0000000..c156bcb
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 	
>> @@ -0,0 +1,60 @@
>> +STMicroelectronics STM32 DFSDM ADC device driver
>> +
>> +STM32 DFSDM ADC is a sigma delta analog-to-digital converter.
>> +It has to be declared in device-tree as a subnode of the DFSDM mfd node.
>> +
>> +It has several multiplexed input channels. Conversions can be performed
>> +in single, scan or discontinuous mode. Conversions can be launched in software
>> +or using hardware triggers.
>> +Each instance of the driver uses one filter instance handle by the DFSDM mfd
>> +driver.
>> +
>> +DFSDM also offers extra features:
>> +-The analog watchdog feature allows the application to detect if the
>> + input voltage goes beyond the user-defined, higher or lower thresholds.
>> +-The short circuit detection allows allows the application to detect if the
>> + input is in CC.
>> +-The clock absence detection allows application to detect if SPI input is clocked.
>> +
>> +Required properties:
>> +- compatible:	Must be "st,stm32-dfsdm-adc".
>> +- reg:		Specifies the DFSDM filter instance.
>> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
>> +- st,adc-channels:	List of single-ended channels muxed for this ADC.
>> +- st,adc-channel-names:	List of single-ended channels Name.
>> +
>> +Optional properties:
>> +- st,adc-channel-types:	Single-ended channel input type. Default value is 0.
>> +			- 0: SPI with data on rising edge (default)
>> +			- 1: SPI with data on falling edge
> This is an element that can be described without magic numbers so I'd
> prefer that you do so.
> 
> Also spell check edage -> edge.
ok i will use strings
> 
>> +			- 2: manchester codec, rising edage = logic 0
>> +			- 3: manchester codec, rising edage = logic 1
>> +- st,adc-channel-clk-src: Conversion clock source. default value is 1.
>> +			  - 0: External SPI clocl (CLKIN x)
>> +			  - 1: internal SPI clock (CLKOUT) (default)
>> +			  - 2: internal SPI clock divided by 2 (falling edge).
>> +			  - 2: internal SPI clock divided by 2 (rising edge).
> 3?
>> +- st,adc-alt-channel: 	  must be defined if Two ADCs are connected on same SPI
>> +			  input.
>> +			  If not set channel n is connected to SPI input n.
>> +			  If set channel n is connected to SPI input n + 1.
> ? Two data inputs with shared clock?
Yes you can connect 2 digital microphones on one SPI.
One samples audio data on the clock rising edge, the other one samples
on the falling edge.
you can see an example in slide 15 of following document.
http://www.st.com/content/ccc/resource/training/technical/product_training/96/b6/2b/ea/72/3f/4e/d5/STM32L4_System_DFSDM.pdf/files/STM32L4_System_DFSDM.pdf/jcr:content/translations/en.STM32L4_System_DFSDM.pdf

I will describe this in an RFC to explain hardware but, to summarize,
there are 3 main "entities" in DFSDM:
- The serial interface: SPI or Manchester bus.
- The channel: it a kind of bridge than can be connected:
   on one side to a serial input (channel n connected to SPI interface n
or n + 1)
   on other side to one or several filters.

The filter: low pass filter + integrator.

>> +
>> +Example:
>> +	dfsdm: dfsdm@4400D000 {
>> +		iio_dfsdm0: iio-dfsdm@0 {
>> +			compatible = "st,stm32-dfsdm-adc";
>> +			#io-channel-cells = <1>;
>> +			reg = <0>;
>> +			st,adc-channels = <1>;
>> +			st,adc-channel-names = "in0";
>> +		};
>> +		iio_dfsdm1: iio-dfsdm@1 {
>> +			compatible = "st,stm32-dfsdm-adc";
>> +			#io-channel-cells = <1>;
>> +			reg = <1>;
>> +			st,adc-channels = <1>;
>> +			st,adc-channel-names = "in1";
>> +			st,adc-channel-types = <1>;
>> +			st,adc-alt-channel = <1>;
>> +		};
>> +	};
>>
> 

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

* [PATCH 3/7] IIO: add bindings for STM32 DFSDM ADC driver
@ 2017-01-30 15:26         ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-30 15:26 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Jonathan,

My comments below.

Regards

Arnaud
On 01/29/2017 12:58 PM, Jonathan Cameron wrote:
> On 23/01/17 16:32, Arnaud Pouliquen wrote:
>> This patch adds documentation of device tree bindings for the
>> STM32 DFSDM ADC.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> Trivial bits inline.
>> ---
>>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 60 ++++++++++++++++++++++
>>  1 file changed, 60 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
>>
>> diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt  b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
>> new file mode 100644
>> index 0000000..c156bcb
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 	
>> @@ -0,0 +1,60 @@
>> +STMicroelectronics STM32 DFSDM ADC device driver
>> +
>> +STM32 DFSDM ADC is a sigma delta analog-to-digital converter.
>> +It has to be declared in device-tree as a subnode of the DFSDM mfd node.
>> +
>> +It has several multiplexed input channels. Conversions can be performed
>> +in single, scan or discontinuous mode. Conversions can be launched in software
>> +or using hardware triggers.
>> +Each instance of the driver uses one filter instance handle by the DFSDM mfd
>> +driver.
>> +
>> +DFSDM also offers extra features:
>> +-The analog watchdog feature allows the application to detect if the
>> + input voltage goes beyond the user-defined, higher or lower thresholds.
>> +-The short circuit detection allows allows the application to detect if the
>> + input is in CC.
>> +-The clock absence detection allows application to detect if SPI input is clocked.
>> +
>> +Required properties:
>> +- compatible:	Must be "st,stm32-dfsdm-adc".
>> +- reg:		Specifies the DFSDM filter instance.
>> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
>> +- st,adc-channels:	List of single-ended channels muxed for this ADC.
>> +- st,adc-channel-names:	List of single-ended channels Name.
>> +
>> +Optional properties:
>> +- st,adc-channel-types:	Single-ended channel input type. Default value is 0.
>> +			- 0: SPI with data on rising edge (default)
>> +			- 1: SPI with data on falling edge
> This is an element that can be described without magic numbers so I'd
> prefer that you do so.
> 
> Also spell check edage -> edge.
ok i will use strings
> 
>> +			- 2: manchester codec, rising edage = logic 0
>> +			- 3: manchester codec, rising edage = logic 1
>> +- st,adc-channel-clk-src: Conversion clock source. default value is 1.
>> +			  - 0: External SPI clocl (CLKIN x)
>> +			  - 1: internal SPI clock (CLKOUT) (default)
>> +			  - 2: internal SPI clock divided by 2 (falling edge).
>> +			  - 2: internal SPI clock divided by 2 (rising edge).
> 3?
>> +- st,adc-alt-channel: 	  must be defined if Two ADCs are connected on same SPI
>> +			  input.
>> +			  If not set channel n is connected to SPI input n.
>> +			  If set channel n is connected to SPI input n + 1.
> ? Two data inputs with shared clock?
Yes you can connect 2 digital microphones on one SPI.
One samples audio data on the clock rising edge, the other one samples
on the falling edge.
you can see an example in slide 15 of following document.
http://www.st.com/content/ccc/resource/training/technical/product_training/96/b6/2b/ea/72/3f/4e/d5/STM32L4_System_DFSDM.pdf/files/STM32L4_System_DFSDM.pdf/jcr:content/translations/en.STM32L4_System_DFSDM.pdf

I will describe this in an RFC to explain hardware but, to summarize,
there are 3 main "entities" in DFSDM:
- The serial interface: SPI or Manchester bus.
- The channel: it a kind of bridge than can be connected:
   on one side to a serial input (channel n connected to SPI interface n
or n + 1)
   on other side to one or several filters.

The filter: low pass filter + integrator.

>> +
>> +Example:
>> +	dfsdm: dfsdm at 4400D000 {
>> +		iio_dfsdm0: iio-dfsdm at 0 {
>> +			compatible = "st,stm32-dfsdm-adc";
>> +			#io-channel-cells = <1>;
>> +			reg = <0>;
>> +			st,adc-channels = <1>;
>> +			st,adc-channel-names = "in0";
>> +		};
>> +		iio_dfsdm1: iio-dfsdm at 1 {
>> +			compatible = "st,stm32-dfsdm-adc";
>> +			#io-channel-cells = <1>;
>> +			reg = <1>;
>> +			st,adc-channels = <1>;
>> +			st,adc-channel-names = "in1";
>> +			st,adc-channel-types = <1>;
>> +			st,adc-alt-channel = <1>;
>> +		};
>> +	};
>>
> 

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

* Re: [PATCH 4/7] IIO: add STM32 DFSDM ADC support
  2017-01-29 12:15     ` Jonathan Cameron
  (?)
@ 2017-01-30 16:02       ` Arnaud Pouliquen
  -1 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-30 16:02 UTC (permalink / raw)
  To: Jonathan Cameron, devicetree, linux-arm-kernel, linux-iio,
	alsa-devel, Lee Jones, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Alexandre TORGUE, Maxime Coquelin

Hello Jonathan,

Thanks for the reviews, i will integrate your comment in next version.

On 01/29/2017 01:15 PM, Jonathan Cameron wrote:
> On 23/01/17 16:32, Arnaud Pouliquen wrote:
>> Add driver to handle Sigma Delta ADC conversion for ADC
>> connected to DFSDM IP.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> Various comments inline.  Again mostly reading this to grasp how the whole
> lot fits together.
> 
> At the moment, other than some new ABI, this is a fairly simple sysfs or in
> kernel polled ADC driver.  The hardware seems to support a whole lot more
> but fair enough to present this as a starting point!

Yes, from IIO point of view, should increase driver features in future
to integrate buffer, scan mode, triggers...
Or perhaps i will need to introduce some of these concepts to handle
ASoC requirement in IIO...

FYI, DFSDM also offers some features like Analog watchdog, short circuit
and SPI/manchester bus clock absence detection detection...:)

Regards
Arnaud

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

* Re: [PATCH 4/7] IIO: add STM32 DFSDM ADC support
@ 2017-01-30 16:02       ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-30 16:02 UTC (permalink / raw)
  To: Jonathan Cameron, devicetree, linux-arm-kernel, linux-iio,
	alsa-devel, Lee Jones, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre TORGUE

Hello Jonathan,

Thanks for the reviews, i will integrate your comment in next version.

On 01/29/2017 01:15 PM, Jonathan Cameron wrote:
> On 23/01/17 16:32, Arnaud Pouliquen wrote:
>> Add driver to handle Sigma Delta ADC conversion for ADC
>> connected to DFSDM IP.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> Various comments inline.  Again mostly reading this to grasp how the whole
> lot fits together.
> 
> At the moment, other than some new ABI, this is a fairly simple sysfs or in
> kernel polled ADC driver.  The hardware seems to support a whole lot more
> but fair enough to present this as a starting point!

Yes, from IIO point of view, should increase driver features in future
to integrate buffer, scan mode, triggers...
Or perhaps i will need to introduce some of these concepts to handle
ASoC requirement in IIO...

FYI, DFSDM also offers some features like Analog watchdog, short circuit
and SPI/manchester bus clock absence detection detection...:)

Regards
Arnaud


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

* [PATCH 4/7] IIO: add STM32 DFSDM ADC support
@ 2017-01-30 16:02       ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-30 16:02 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Jonathan,

Thanks for the reviews, i will integrate your comment in next version.

On 01/29/2017 01:15 PM, Jonathan Cameron wrote:
> On 23/01/17 16:32, Arnaud Pouliquen wrote:
>> Add driver to handle Sigma Delta ADC conversion for ADC
>> connected to DFSDM IP.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
> Various comments inline.  Again mostly reading this to grasp how the whole
> lot fits together.
> 
> At the moment, other than some new ABI, this is a fairly simple sysfs or in
> kernel polled ADC driver.  The hardware seems to support a whole lot more
> but fair enough to present this as a starting point!

Yes, from IIO point of view, should increase driver features in future
to integrate buffer, scan mode, triggers...
Or perhaps i will need to introduce some of these concepts to handle
ASoC requirement in IIO...

FYI, DFSDM also offers some features like Analog watchdog, short circuit
and SPI/manchester bus clock absence detection detection...:)

Regards
Arnaud

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

* Re: [PATCH 6/7] ASoC: add bindings for STM32 DFSDM driver
  2017-01-29 12:19       ` Jonathan Cameron
  (?)
@ 2017-01-30 17:32         ` Arnaud Pouliquen
  -1 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-30 17:32 UTC (permalink / raw)
  To: Jonathan Cameron, devicetree, linux-arm-kernel, linux-iio,
	alsa-devel, Lee Jones, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Alexandre TORGUE, Maxime Coquelin

Hello Johan,

Please find my comments in-line.

Regards

Arnaud

On 01/29/2017 01:19 PM, Jonathan Cameron wrote:
> On 23/01/17 16:32, Arnaud Pouliquen wrote:
>> This patch adds documentation of device tree bindings for the
>> STM32 DFSDM ASoC driver.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> ---
>>  .../devicetree/bindings/sound/st,sm32-adfsdm.txt   | 84 ++++++++++++++++++++++
>>  1 file changed, 84 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
>>
>> diff --git a/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt b/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
>> new file mode 100644
>> index 0000000..a1d27b8
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
>> @@ -0,0 +1,84 @@
>> +STMicroelectronics STM32 ADFSDM ASoC DAI device driver.
>> +
>> +STM32 ADFSDM ASoC is a sigma delta audio interface for digital microphone.
>> +It has to be declared in device-tree as a subnode of the DFSDM mfd node.
>> +
>> +It offers possibility to record several mono microphones, with an option to
>> +synchronize all microphones on a main one (that must be piped to filter 0)
>> +Audio interface can be exposed through the generic ASoC simple card.
>> +
>> +2 Dmics can be connected on one SPI interface instance n.
>> +Convention is that the DMIC that strobes data on rising edge is connected to the
>> +corresponding DFSDM channel n; while the Dmic that strobes data on falling edge
>> +is connected to the channel n-1. Simple card property "bitclock-inversion = <1>"
>> +is used to specify that microphone strobes data on falling edge.
>> +
>> +SPI interface allows to be scheduled by an external SPI clock. To use it
>> +simple card properties "bitclock-master = <&codec>" and "system-clock-frequency"
>> +have to be defined in dai-link node.
>> +
>> +Required properties:
>> +- compatible: 	Must be "st,stm32-dfsdm-audio",
>> +- reg:		Specifies the DFSDM filter instance.
>> +- interrupts: 	DFSDM filter instance interrupt line.
>> +- dma:		DMA controller phandle and DMA request line associated to the
>> +		filter instance ( specified by the field "reg")
>> +- dma-names: 	must be "rx"
>> +
>> +- st,input-id:		Id of the SPI/Manchester interface used.
>> +- st,dai-filter-order:  SinC filter order from 0 to 5.
>> +			0: FastSinC
>> +			[1-5]: order 1 to 5.
>> +			For audio purpose it is recommended to use order 3 to 5.
> Interesting for audio you consider it feature of the hardware, but for ADC you
> consider that this should be userspace controlled.
> Personally I'd like to see more detail on those filters but if this is convention
> in Asoc then so be it.

Configure filter and integrator is quite tricky. Updating filter and
integrator parameters have impact on sample resolution and output sample
rate.
Sample resolution is the peak to peak data value and depends on
oversampling parameters and filter order.

Output sample rate depends on SPI input clock frequency oversampling
parameters and filter order.

High filter order gives a better resolution and a better filter response
but allows less dynamic to find the good rate...

So it is a compromise between resolution, rate and filtering.

For ADC use case, filter orders 1 to 3 are recommended.
For audio use case, orders 3 to 5 are recommended.

On IIO side i proposed to expose tuning in ABI, to allows application
fine tuning.
Need to document it...

For ASOC it is another story. Application requests a rate and a sample
format. So filter parameters are computed based on SPI bus clock
frequency, expected audio sample rate and audio sample format.
This is done in stm32_adfsdm_get_best_osr in sound/soc/stm/stm32_adfsdm.c.

Now your comment trig a good point. Perhaps a better option in IIO,
could be to offer same kind of ABI than ASOC interface:
- standard oversampling-ratio ABI. based on it filter and integrator
oversampling ratios will be computed to maximize the resolution.
- a read only resolution ABI:
/sys/bus/iio/devices/iio:deviceX/in_adc_x_resolution. That give the
computed resolution.

In this case filter order will be part of the DT for IIO and ASoC.
This would also match with a redesign of the DFSDM driver in IIO...

>> +
>> +Optional properties:
>> + - st,dai0-synchronized: Set to 1 to synchronize DAI with DFSDM instance 0.
>> +
>> +Exemple of a card with 2 Dmics synchronized and connected on SPI interface 1.
> Example.
>> +
>> +	dfsdm: dfsdm@4400D000 {
>> +		dai_dfsdm0: dfsdm-audio@0 {
>> +			compatible = "st,stm32-dfsdm-audio";
>> +			#sound-dai-cells = <0>;
>> +			reg = <0>;
>> +			dmas = <&dmamux1 101 0x400 0x00>;
>> +			dma-names = "rx";
>> +			st,input-id = <0>;
>> +			st,dai-filter-order = <5>;
>> +		};
>> +		dai_dfsdm1: dfsdm-audio@1 {
>> +			compatible = "st,stm32-dfsdm-audio";
>> +			#sound-dai-cells = <0>;
>> +			reg = <0>;
>> +			dmas = <&dmamux1 102 0x400 0x00>;
>> +			dma-names = "rx";
>> +			st,input-id = <0>;
>> +			st,dai0-synchronized = <1>;
>> +			st,dai-filter-order = <5>;
>> +		};
>> +	};
>> +	sound_dfsdm_pdm {
>> +		compatible = "simple-audio-card";
>> +		simple-audio-card,name = "dfsdm_pdm";
>> +		status = "okay";
>> +
>> +		dfsdm0_mic0: simple-audio-card,dai-link@0 {
>> +			format = "pdm";
>> +			cpu {
>> +				sound-dai = <&dai_dfsdm0>;
>> +			};
>> +			dmic0_codec: codec {
>> +				sound-dai = <&dmic0>;
>> +			};
>> +		};
>> +		dfsdm0_mic1: simple-audio-card,dai-link@1 {
>> +			format = "pdm";
>> +			bitclock-inversion = <1>;
>> +			cpu {
>> +				sound-dai = <&dai_dfsdm1>;
>> +			};
>> +			codec {
>> +				sound-dai = <&dmic1>;
>> +			};
>> +		};
>> +	};
>>
> Jonathan
> 

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

* Re: [PATCH 6/7] ASoC: add bindings for STM32 DFSDM driver
@ 2017-01-30 17:32         ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-30 17:32 UTC (permalink / raw)
  To: Jonathan Cameron, devicetree, linux-arm-kernel, linux-iio,
	alsa-devel, Lee Jones, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre TORGUE

Hello Johan,

Please find my comments in-line.

Regards

Arnaud

On 01/29/2017 01:19 PM, Jonathan Cameron wrote:
> On 23/01/17 16:32, Arnaud Pouliquen wrote:
>> This patch adds documentation of device tree bindings for the
>> STM32 DFSDM ASoC driver.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> ---
>>  .../devicetree/bindings/sound/st,sm32-adfsdm.txt   | 84 ++++++++++++++++++++++
>>  1 file changed, 84 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
>>
>> diff --git a/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt b/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
>> new file mode 100644
>> index 0000000..a1d27b8
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
>> @@ -0,0 +1,84 @@
>> +STMicroelectronics STM32 ADFSDM ASoC DAI device driver.
>> +
>> +STM32 ADFSDM ASoC is a sigma delta audio interface for digital microphone.
>> +It has to be declared in device-tree as a subnode of the DFSDM mfd node.
>> +
>> +It offers possibility to record several mono microphones, with an option to
>> +synchronize all microphones on a main one (that must be piped to filter 0)
>> +Audio interface can be exposed through the generic ASoC simple card.
>> +
>> +2 Dmics can be connected on one SPI interface instance n.
>> +Convention is that the DMIC that strobes data on rising edge is connected to the
>> +corresponding DFSDM channel n; while the Dmic that strobes data on falling edge
>> +is connected to the channel n-1. Simple card property "bitclock-inversion = <1>"
>> +is used to specify that microphone strobes data on falling edge.
>> +
>> +SPI interface allows to be scheduled by an external SPI clock. To use it
>> +simple card properties "bitclock-master = <&codec>" and "system-clock-frequency"
>> +have to be defined in dai-link node.
>> +
>> +Required properties:
>> +- compatible: 	Must be "st,stm32-dfsdm-audio",
>> +- reg:		Specifies the DFSDM filter instance.
>> +- interrupts: 	DFSDM filter instance interrupt line.
>> +- dma:		DMA controller phandle and DMA request line associated to the
>> +		filter instance ( specified by the field "reg")
>> +- dma-names: 	must be "rx"
>> +
>> +- st,input-id:		Id of the SPI/Manchester interface used.
>> +- st,dai-filter-order:  SinC filter order from 0 to 5.
>> +			0: FastSinC
>> +			[1-5]: order 1 to 5.
>> +			For audio purpose it is recommended to use order 3 to 5.
> Interesting for audio you consider it feature of the hardware, but for ADC you
> consider that this should be userspace controlled.
> Personally I'd like to see more detail on those filters but if this is convention
> in Asoc then so be it.

Configure filter and integrator is quite tricky. Updating filter and
integrator parameters have impact on sample resolution and output sample
rate.
Sample resolution is the peak to peak data value and depends on
oversampling parameters and filter order.

Output sample rate depends on SPI input clock frequency oversampling
parameters and filter order.

High filter order gives a better resolution and a better filter response
but allows less dynamic to find the good rate...

So it is a compromise between resolution, rate and filtering.

For ADC use case, filter orders 1 to 3 are recommended.
For audio use case, orders 3 to 5 are recommended.

On IIO side i proposed to expose tuning in ABI, to allows application
fine tuning.
Need to document it...

For ASOC it is another story. Application requests a rate and a sample
format. So filter parameters are computed based on SPI bus clock
frequency, expected audio sample rate and audio sample format.
This is done in stm32_adfsdm_get_best_osr in sound/soc/stm/stm32_adfsdm.c.

Now your comment trig a good point. Perhaps a better option in IIO,
could be to offer same kind of ABI than ASOC interface:
- standard oversampling-ratio ABI. based on it filter and integrator
oversampling ratios will be computed to maximize the resolution.
- a read only resolution ABI:
/sys/bus/iio/devices/iio:deviceX/in_adc_x_resolution. That give the
computed resolution.

In this case filter order will be part of the DT for IIO and ASoC.
This would also match with a redesign of the DFSDM driver in IIO...

>> +
>> +Optional properties:
>> + - st,dai0-synchronized: Set to 1 to synchronize DAI with DFSDM instance 0.
>> +
>> +Exemple of a card with 2 Dmics synchronized and connected on SPI interface 1.
> Example.
>> +
>> +	dfsdm: dfsdm@4400D000 {
>> +		dai_dfsdm0: dfsdm-audio@0 {
>> +			compatible = "st,stm32-dfsdm-audio";
>> +			#sound-dai-cells = <0>;
>> +			reg = <0>;
>> +			dmas = <&dmamux1 101 0x400 0x00>;
>> +			dma-names = "rx";
>> +			st,input-id = <0>;
>> +			st,dai-filter-order = <5>;
>> +		};
>> +		dai_dfsdm1: dfsdm-audio@1 {
>> +			compatible = "st,stm32-dfsdm-audio";
>> +			#sound-dai-cells = <0>;
>> +			reg = <0>;
>> +			dmas = <&dmamux1 102 0x400 0x00>;
>> +			dma-names = "rx";
>> +			st,input-id = <0>;
>> +			st,dai0-synchronized = <1>;
>> +			st,dai-filter-order = <5>;
>> +		};
>> +	};
>> +	sound_dfsdm_pdm {
>> +		compatible = "simple-audio-card";
>> +		simple-audio-card,name = "dfsdm_pdm";
>> +		status = "okay";
>> +
>> +		dfsdm0_mic0: simple-audio-card,dai-link@0 {
>> +			format = "pdm";
>> +			cpu {
>> +				sound-dai = <&dai_dfsdm0>;
>> +			};
>> +			dmic0_codec: codec {
>> +				sound-dai = <&dmic0>;
>> +			};
>> +		};
>> +		dfsdm0_mic1: simple-audio-card,dai-link@1 {
>> +			format = "pdm";
>> +			bitclock-inversion = <1>;
>> +			cpu {
>> +				sound-dai = <&dai_dfsdm1>;
>> +			};
>> +			codec {
>> +				sound-dai = <&dmic1>;
>> +			};
>> +		};
>> +	};
>>
> Jonathan
> 

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

* [PATCH 6/7] ASoC: add bindings for STM32 DFSDM driver
@ 2017-01-30 17:32         ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-30 17:32 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Johan,

Please find my comments in-line.

Regards

Arnaud

On 01/29/2017 01:19 PM, Jonathan Cameron wrote:
> On 23/01/17 16:32, Arnaud Pouliquen wrote:
>> This patch adds documentation of device tree bindings for the
>> STM32 DFSDM ASoC driver.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> ---
>>  .../devicetree/bindings/sound/st,sm32-adfsdm.txt   | 84 ++++++++++++++++++++++
>>  1 file changed, 84 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
>>
>> diff --git a/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt b/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
>> new file mode 100644
>> index 0000000..a1d27b8
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
>> @@ -0,0 +1,84 @@
>> +STMicroelectronics STM32 ADFSDM ASoC DAI device driver.
>> +
>> +STM32 ADFSDM ASoC is a sigma delta audio interface for digital microphone.
>> +It has to be declared in device-tree as a subnode of the DFSDM mfd node.
>> +
>> +It offers possibility to record several mono microphones, with an option to
>> +synchronize all microphones on a main one (that must be piped to filter 0)
>> +Audio interface can be exposed through the generic ASoC simple card.
>> +
>> +2 Dmics can be connected on one SPI interface instance n.
>> +Convention is that the DMIC that strobes data on rising edge is connected to the
>> +corresponding DFSDM channel n; while the Dmic that strobes data on falling edge
>> +is connected to the channel n-1. Simple card property "bitclock-inversion = <1>"
>> +is used to specify that microphone strobes data on falling edge.
>> +
>> +SPI interface allows to be scheduled by an external SPI clock. To use it
>> +simple card properties "bitclock-master = <&codec>" and "system-clock-frequency"
>> +have to be defined in dai-link node.
>> +
>> +Required properties:
>> +- compatible: 	Must be "st,stm32-dfsdm-audio",
>> +- reg:		Specifies the DFSDM filter instance.
>> +- interrupts: 	DFSDM filter instance interrupt line.
>> +- dma:		DMA controller phandle and DMA request line associated to the
>> +		filter instance ( specified by the field "reg")
>> +- dma-names: 	must be "rx"
>> +
>> +- st,input-id:		Id of the SPI/Manchester interface used.
>> +- st,dai-filter-order:  SinC filter order from 0 to 5.
>> +			0: FastSinC
>> +			[1-5]: order 1 to 5.
>> +			For audio purpose it is recommended to use order 3 to 5.
> Interesting for audio you consider it feature of the hardware, but for ADC you
> consider that this should be userspace controlled.
> Personally I'd like to see more detail on those filters but if this is convention
> in Asoc then so be it.

Configure filter and integrator is quite tricky. Updating filter and
integrator parameters have impact on sample resolution and output sample
rate.
Sample resolution is the peak to peak data value and depends on
oversampling parameters and filter order.

Output sample rate depends on SPI input clock frequency oversampling
parameters and filter order.

High filter order gives a better resolution and a better filter response
but allows less dynamic to find the good rate...

So it is a compromise between resolution, rate and filtering.

For ADC use case, filter orders 1 to 3 are recommended.
For audio use case, orders 3 to 5 are recommended.

On IIO side i proposed to expose tuning in ABI, to allows application
fine tuning.
Need to document it...

For ASOC it is another story. Application requests a rate and a sample
format. So filter parameters are computed based on SPI bus clock
frequency, expected audio sample rate and audio sample format.
This is done in stm32_adfsdm_get_best_osr in sound/soc/stm/stm32_adfsdm.c.

Now your comment trig a good point. Perhaps a better option in IIO,
could be to offer same kind of ABI than ASOC interface:
- standard oversampling-ratio ABI. based on it filter and integrator
oversampling ratios will be computed to maximize the resolution.
- a read only resolution ABI:
/sys/bus/iio/devices/iio:deviceX/in_adc_x_resolution. That give the
computed resolution.

In this case filter order will be part of the DT for IIO and ASoC.
This would also match with a redesign of the DFSDM driver in IIO...

>> +
>> +Optional properties:
>> + - st,dai0-synchronized: Set to 1 to synchronize DAI with DFSDM instance 0.
>> +
>> +Exemple of a card with 2 Dmics synchronized and connected on SPI interface 1.
> Example.
>> +
>> +	dfsdm: dfsdm at 4400D000 {
>> +		dai_dfsdm0: dfsdm-audio at 0 {
>> +			compatible = "st,stm32-dfsdm-audio";
>> +			#sound-dai-cells = <0>;
>> +			reg = <0>;
>> +			dmas = <&dmamux1 101 0x400 0x00>;
>> +			dma-names = "rx";
>> +			st,input-id = <0>;
>> +			st,dai-filter-order = <5>;
>> +		};
>> +		dai_dfsdm1: dfsdm-audio at 1 {
>> +			compatible = "st,stm32-dfsdm-audio";
>> +			#sound-dai-cells = <0>;
>> +			reg = <0>;
>> +			dmas = <&dmamux1 102 0x400 0x00>;
>> +			dma-names = "rx";
>> +			st,input-id = <0>;
>> +			st,dai0-synchronized = <1>;
>> +			st,dai-filter-order = <5>;
>> +		};
>> +	};
>> +	sound_dfsdm_pdm {
>> +		compatible = "simple-audio-card";
>> +		simple-audio-card,name = "dfsdm_pdm";
>> +		status = "okay";
>> +
>> +		dfsdm0_mic0: simple-audio-card,dai-link at 0 {
>> +			format = "pdm";
>> +			cpu {
>> +				sound-dai = <&dai_dfsdm0>;
>> +			};
>> +			dmic0_codec: codec {
>> +				sound-dai = <&dmic0>;
>> +			};
>> +		};
>> +		dfsdm0_mic1: simple-audio-card,dai-link at 1 {
>> +			format = "pdm";
>> +			bitclock-inversion = <1>;
>> +			cpu {
>> +				sound-dai = <&dai_dfsdm1>;
>> +			};
>> +			codec {
>> +				sound-dai = <&dmic1>;
>> +			};
>> +		};
>> +	};
>>
> Jonathan
> 

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
  2017-01-29 17:50                         ` Mark Brown
  (?)
@ 2017-01-30 18:14                           ` Arnaud Pouliquen
  -1 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-30 18:14 UTC (permalink / raw)
  To: Mark Brown
  Cc: Mark Rutland, devicetree, alsa-devel, Lars-Peter Clausen,
	Maxime Coquelin, Liam Girdwood, linux-iio, Takashi Iwai,
	Rob Herring, Jonathan Cameron, Peter Meerwald-Stadler,
	Hartmut Knaack, Lee Jones, linux-arm-kernel, Alexandre TORGUE

Hello Mark,

On 01/29/2017 06:50 PM, Mark Brown wrote:
> On Fri, Jan 27, 2017 at 02:45:49PM +0100, Arnaud Pouliquen wrote:
> 
>> Jonathan, Mark, Please could you share your opinion on this
>> topic?
> 
> I really don't have enough visbility of what the hardware or
> software is doing to say much and though there's extensive
> backtrace in here it's not enough to follow.  If it's a generic ADC
> that happens to have audio applications then some relationship with
> IIO might make sense but I don't know how separate the audio and
> general purpose bits of the hardware are.
> 
> Please understand that I get copied in on *lots* of threads about
> MFDs with drivers getting posted repeatedly for whatever revisions
> are needed with little relevance to me so it's very random if I
> even look at these things, much less read them with detail.
> 

In a first step, your comment is enough from my point of view as you
are not disagree with the concept of a relationship between ASOC an IIO.
This confirms that the MFD driver have to be abandoned.

Then details on how to do it is another story, that should trigs some
other discussions...

Thanks and Regards,

Arnaud

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-30 18:14                           ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-30 18:14 UTC (permalink / raw)
  To: Mark Brown
  Cc: Lee Jones, devicetree, linux-arm-kernel, linux-iio, alsa-devel,
	Rob Herring, Mark Rutland, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Maxime Coquelin, Alexandre TORGUE

Hello Mark,

On 01/29/2017 06:50 PM, Mark Brown wrote:
> On Fri, Jan 27, 2017 at 02:45:49PM +0100, Arnaud Pouliquen wrote:
> 
>> Jonathan, Mark, Please could you share your opinion on this
>> topic?
> 
> I really don't have enough visbility of what the hardware or
> software is doing to say much and though there's extensive
> backtrace in here it's not enough to follow.  If it's a generic ADC
> that happens to have audio applications then some relationship with
> IIO might make sense but I don't know how separate the audio and
> general purpose bits of the hardware are.
> 
> Please understand that I get copied in on *lots* of threads about
> MFDs with drivers getting posted repeatedly for whatever revisions
> are needed with little relevance to me so it's very random if I
> even look at these things, much less read them with detail.
> 

In a first step, your comment is enough from my point of view as you
are not disagree with the concept of a relationship between ASOC an IIO.
This confirms that the MFD driver have to be abandoned.

Then details on how to do it is another story, that should trigs some
other discussions...

Thanks and Regards,

Arnaud

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

* [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-30 18:14                           ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-30 18:14 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Mark,

On 01/29/2017 06:50 PM, Mark Brown wrote:
> On Fri, Jan 27, 2017 at 02:45:49PM +0100, Arnaud Pouliquen wrote:
> 
>> Jonathan, Mark, Please could you share your opinion on this
>> topic?
> 
> I really don't have enough visbility of what the hardware or
> software is doing to say much and though there's extensive
> backtrace in here it's not enough to follow.  If it's a generic ADC
> that happens to have audio applications then some relationship with
> IIO might make sense but I don't know how separate the audio and
> general purpose bits of the hardware are.
> 
> Please understand that I get copied in on *lots* of threads about
> MFDs with drivers getting posted repeatedly for whatever revisions
> are needed with little relevance to me so it's very random if I
> even look at these things, much less read them with detail.
> 

In a first step, your comment is enough from my point of view as you
are not disagree with the concept of a relationship between ASOC an IIO.
This confirms that the MFD driver have to be abandoned.

Then details on how to do it is another story, that should trigs some
other discussions...

Thanks and Regards,

Arnaud

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
  2017-01-30 15:08           ` Arnaud Pouliquen
  (?)
@ 2017-01-30 20:44             ` Jonathan Cameron
  -1 siblings, 0 replies; 130+ messages in thread
From: Jonathan Cameron @ 2017-01-30 20:44 UTC (permalink / raw)
  To: Arnaud Pouliquen, devicetree, linux-arm-kernel, linux-iio,
	alsa-devel, Lee Jones, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Alexandre TORGUE, Maxime Coquelin

On 30/01/17 15:08, Arnaud Pouliquen wrote:
> Hello Jonathan,
> 
> Thanks for the review. This drivers should disappear,
> but i will integrate you comment/remark in my redesign.
> 
> Please find some comments below, on specific points
> 
> Regards
> Arnaud
> 
> 
> On 01/29/2017 12:53 PM, Jonathan Cameron wrote:
>> On 23/01/17 16:32, Arnaud Pouliquen wrote:
>>> DFSDM hardware IP can be used at the same time for ADC sigma delta
>>> conversion and audio PDM microphone.
>>> MFD driver is in charge of configuring IP registers and managing IP clocks.
>>> For this it exports an API to handles filters and channels resources.
>>>
>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> This is somewhat of a beast.  I would be tempted to build it up in more
>> bite sized chunks.
>>
>> Obvious things to drop from a first version (basically to make it easier
>> to review) would be injected supported.  There may well be others but you'll
>> have a better feel for that than me.
> i will pay attention for next version.
>>
>> I really don't like the wrappers round the regmap functions though.
>> They mean you are papering over errors if they occur and make the code
>> slightly harder to read by implying that something else is going on.
>>
> One aim of the wrapper was to simplify code review, seems that just the
> opposite...
> As i have around 50 regmap accesses, adding  a return/goto for each
> error and at least an associated error messages for each function,
> should add 100 to 150 extra lines...
Keep error message to a minimum. Likely to never occur as you say!
> 
>> Yes the code will be longer without them, but you will also be forced to think
>> properly about error paths.
> 
> I have a question around this. What should be the action if a register
> access return an error?
> Possible root causes:
> 	- bad address of the IP (DT)
> 	- IP not clocked or in reset (driver BUG).
> 	- IP is out of order (hardware issue)
> 	- bug in driver that access to an invalid address.
> 
> So except for the last root cause,we can suppose that every register
> accesses should always be OK or KO...
> This is also a reason of the wrapper. Detect driver bug, without adding
> a test on each register access return.
> 
> Perhaps a compromise could be that test is done only during some
> specific phase (probe, after reset deassertion, clock enabling...) and
> then trust access without test?
> Or simply add error message in regmap helper routines...
> 
> Please just tell/confirm me your preference.
Always assume an error can occur anywhere and handle it as best possible (usually
drop straight out of the function with an error).

I agree it doesn't always give that much information, but trying to paper over
a problem and continue is usually a worse idea.
> 
>>
>> Anyhow, was mostly reading this to get a feel for what was going on in the
>> whole series so not really a terribly thorough review I'm afraid. Sorry about
>> that!
> More than enough for this first version :)
> 
>>
>> Jonathan
>>> ---
>>>  drivers/mfd/Kconfig             |   11 +
>>>  drivers/mfd/Makefile            |    2 +
>>>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
>>>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
>>>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
>>>  5 files changed, 1601 insertions(+)
>>>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
>>>  create mode 100644 drivers/mfd/stm32-dfsdm.c
>>>  create mode 100644 include/linux/mfd/stm32-dfsdm.h
> 
> 
> [...]
> 
>>> diff --git a/drivers/mfd/stm32-dfsdm.c b/drivers/mfd/stm32-dfsdm.c
>>> new file mode 100644
>>> index 0000000..81ca29c
>>> --- /dev/null
>>> +++ b/drivers/mfd/stm32-dfsdm.c
>>> @@ -0,0 +1,1044 @@
>>> +/*
>>> + * This file is part of STM32 DFSDM mfd driver
>>> + *
>>> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
>>> + * Author(s): Arnaud Pouliquen <arnaud.pouliquen@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/interrupt.h>
>>> +#include <linux/mfd/core.h>
>>> +#include <linux/module.h>
>>> +#include <linux/of_device.h>
>>> +#include <linux/regmap.h>
>>> +#include <linux/slab.h>
>>> +
>>> +#include <linux/mfd/stm32-dfsdm.h>
>>> +
>>> +#include "stm32-dfsdm-reg.h"
>>> +
>>> +#define DFSDM_UPDATE_BITS(regm, reg, mask, val) \
>>> +		WARN_ON(regmap_update_bits(regm, reg, mask, val))
>> Don't do these wrappers please. Handle error correctly in all cases.
>> Reviewing as I tend to do backwards through the driver, I thought these
>> were doing something interesting.
>>
>> Effectively you have no error handling as a result of these which needs
>> fixing.
>>
>>> +
>>> +#define DFSDM_REG_READ(regm, reg, val) \
>>> +		WARN_ON(regmap_read(regm, reg, val))
>>> +
>>> +#define DFSDM_REG_WRITE(regm, reg, val) \
>>> +		WARN_ON(regmap_write(regm, reg, val))
>>> +
>>> +#define STM32H7_DFSDM_NUM_FILTERS	4
>>> +#define STM32H7_DFSDM_NUM_INPUTS	8
>>> +
>>> +enum dfsdm_clkout_src {
>>> +	DFSDM_CLK,
>>> +	AUDIO_CLK
>>> +};
>>> +
>>> +struct stm32_dev_data {
>>> +	const struct stm32_dfsdm dfsdm;
>>> +	const struct regmap_config *regmap_cfg;
>>> +};
>>> +
>>> +struct dfsdm_priv;
>>> +
>>> +struct filter_params {
>>> +	unsigned int id;
>>> +	int irq;
>>> +	struct stm32_dfsdm_fl_event event;
>>> +	u32 event_mask;
>>> +	struct dfsdm_priv *priv; /* Cross ref for context */
>>> +	unsigned int ext_ch_mask;
>>> +	unsigned int scan_ch;
>>> +};
>>> +
>>> +struct ch_params {
>>> +	struct stm32_dfsdm_channel ch;
>>> +};
>>> +
>> I'd like to see a lot more comments in here.  Perhaps full kernel-doc
>> as some elements are not that obvious at least to a fairly casual read.
>>
> Description in device-tree tree bindings and cover-letter is not
> sufficient? you would a doc in Document/arm/stm32?
Sorry, just meant on the following structure.  Internal comments would
make it easier to follow what the elements of this structure are for.
> 
>>> +struct dfsdm_priv {
>>> +	struct platform_device *pdev;
>>> +	struct stm32_dfsdm dfsdm;
>>> +
>>> +	spinlock_t lock; /* Used for resource sharing & interrupt lock */
>>> +
>>> +	/* Filters */
>>> +	struct filter_params *filters;
>>> +	unsigned int free_filter_mask;
>>> +	unsigned int scd_filter_mask;
>>> +	unsigned int ckab_filter_mask;
>>> +
>>> +	/* Channels */
>>> +	struct stm32_dfsdm_channel *channels;
>>> +	unsigned int free_channel_mask;
>>> +	atomic_t n_active_ch;
>>> +
>>> +	/* Clock */
>>> +	struct clk *clk;
>>> +	struct clk *aclk;
>>> +	unsigned int clkout_div;
>>> +	unsigned int clkout_freq_req;
>>> +
>>> +	/* Registers*/
>>> +	void __iomem *base;
>>> +	struct regmap *regmap;
>>> +	phys_addr_t phys_base;
>>> +};
>>> +
>>> +/*
>>> + * Common
>>> + */
>>> +static bool stm32_dfsdm_volatile_reg(struct device *dev, unsigned int reg)
>>> +{
>>> +	if (reg < DFSDM_FILTER_BASE_ADR)
>>> +		return false;
>>> +
>>> +	/*
>>> +	 * Mask is done on register to avoid to list registers of all them
>>> +	 * filter instances.
>>> +	 */
>>> +	switch (reg & DFSDM_FILTER_REG_MASK) {
>>> +	case DFSDM_CR1(0) & DFSDM_FILTER_REG_MASK:
>>> +	case DFSDM_ISR(0) & DFSDM_FILTER_REG_MASK:
>>> +	case DFSDM_JDATAR(0) & DFSDM_FILTER_REG_MASK:
>>> +	case DFSDM_RDATAR(0) & DFSDM_FILTER_REG_MASK:
>>> +		return true;
>>> +	}
>>> +
>>> +	return false;
>>> +}
>>> +
>>> +static const struct regmap_config stm32h7_dfsdm_regmap_cfg = {
>>> +	.reg_bits = 32,
>>> +	.val_bits = 32,
>>> +	.reg_stride = sizeof(u32),
>>> +	.max_register = DFSDM_CNVTIMR(STM32H7_DFSDM_NUM_FILTERS - 1),
>>> +	.volatile_reg = stm32_dfsdm_volatile_reg,
>>> +	.fast_io = true,
>>> +};
>>> +
>>> +static const struct stm32_dev_data stm32h7_data = {
>>> +	.dfsdm.max_channels = STM32H7_DFSDM_NUM_INPUTS,
>>> +	.dfsdm.max_filters = STM32H7_DFSDM_NUM_FILTERS,
>>> +	.regmap_cfg = &stm32h7_dfsdm_regmap_cfg,
>>> +};
>>> +
> 
> [...]
> 
>>> +
>>> +/**
>>> + * stm32_dfsdm_get_filter_dma_addr - Get register address for dma transfer.
>>> + *
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @fl_id: Filter id.
>>> + * @conv: Conversion type.
>>> + */
>>> +dma_addr_t stm32_dfsdm_get_filter_dma_phy_addr(struct stm32_dfsdm *dfsdm,
>>> +					       unsigned int fl_id,
>>> +					       enum stm32_dfsdm_conv_type conv)
>>> +{
>>> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
>>> +
>>> +	if (conv == DFSDM_FILTER_REG_CONV)
>>> +		return (dma_addr_t)(priv->phys_base + DFSDM_RDATAR(fl_id));
>>> +	else
>>> +		return (dma_addr_t)(priv->phys_base + DFSDM_JDATAR(fl_id));
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_register_fl_event - Register filter event.
>> What is a filter event?  More details good on things that are very
>> device specific like this.
> Filter events correspond to filter IRQ status, will be handled in a
> different way in IIO.
>>> + *
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @fl_id: Filter id.
>>> + * @event: Event to unregister.
>>> + * @chan_mask: Mask of channels associated to filter.
>>> + *
>>> + * The function enables associated IRQ.
>>> + */
>>> +int stm32_dfsdm_register_fl_event(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
>>> +				  enum stm32_dfsdm_events event,
>>> +				  unsigned int chan_mask)
>>> +{
>>> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
>>> +	unsigned long flags, ulmask = chan_mask;
>>> +	int ret, i;
>>> +
>>> +	dev_dbg(&priv->pdev->dev, "%s:for filter %d: event %#x ch_mask %#x\n",
>>> +		__func__, fl_id, event, chan_mask);
>>> +
>>> +	if (event > DFSDM_EVENT_CKA)
>>> +		return -EINVAL;
>>> +
>>> +	/* Clear interrupt before enable them */
>>> +	ret = stm32_dfsdm_clear_event(priv, fl_id, event, chan_mask);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	spin_lock_irqsave(&priv->lock, flags);
>>> +	/* Enable interrupts */
>>> +	switch (event) {
>>> +	case DFSDM_EVENT_SCD:
>>> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
>>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
>>> +					  DFSDM_CHCFGR1_SCDEN_MASK,
>>> +					  DFSDM_CHCFGR1_SCDEN(1));
>>> +		}
>>> +		if (!priv->scd_filter_mask)
>>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
>>> +					  DFSDM_CR2_SCDIE_MASK,
>>> +					  DFSDM_CR2_SCDIE(1));
>>> +		priv->scd_filter_mask |= BIT(fl_id);
>>> +		break;
>>> +	case DFSDM_EVENT_CKA:
>>> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
>>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
>>> +					  DFSDM_CHCFGR1_CKABEN_MASK,
>>> +					  DFSDM_CHCFGR1_CKABEN(1));
>>> +		}
>>> +		if (!priv->ckab_filter_mask)
>>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
>>> +					  DFSDM_CR2_CKABIE_MASK,
>>> +					  DFSDM_CR2_CKABIE(1));
>>> +		priv->ckab_filter_mask |= BIT(fl_id);
>>> +		break;
>>> +	default:
>>> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(fl_id), event, event);
>>> +	}
>>> +	priv->filters[fl_id].event_mask |= event;
>>> +	spin_unlock_irqrestore(&priv->lock, flags);
>>> +
>>> +	return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(dfsdm_register_fl_event);
>>> +
>>> +/**
>>> + * stm32_dfsdm_unregister_fl_event - Unregister filter event.
>>> + *
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @fl_id: Filter id.
>>> + * @event: Event to unregister.
>>> + * @chan_mask: Mask of channels associated to filter.
>>> + *
>>> + * The function disables associated IRQ.
>>> + */
>>> +int stm32_dfsdm_unregister_fl_event(struct stm32_dfsdm *dfsdm,
>>> +				    unsigned int fl_id,
>>> +				    enum stm32_dfsdm_events event,
>>> +				    unsigned int chan_mask)
>>> +{
>>> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
>>> +	unsigned long flags, ulmask = chan_mask;
>>> +	int i;
>>> +
>>> +	dev_dbg(&priv->pdev->dev, "%s:for filter %d: event %#x ch_mask %#x\n",
>>> +		__func__, fl_id, event, chan_mask);
>>> +
>>> +	if (event > DFSDM_EVENT_CKA)
>>> +		return -EINVAL;
>>> +
>>> +	spin_lock_irqsave(&priv->lock, flags);
>>> +	/* Disable interrupts */
>>> +	switch (event) {
>>> +	case DFSDM_EVENT_SCD:
>>> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
>>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
>>> +					  DFSDM_CHCFGR1_SCDEN_MASK,
>>> +					  DFSDM_CHCFGR1_SCDEN(0));
>>> +		}
>>> +		priv->scd_filter_mask &= ~BIT(fl_id);
>>> +		if (!priv->scd_filter_mask)
>>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
>>> +					  DFSDM_CR2_SCDIE_MASK,
>>> +					  DFSDM_CR2_SCDIE(0));
>>> +		break;
>>> +	case DFSDM_EVENT_CKA:
>>> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
>>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
>>> +					  DFSDM_CHCFGR1_CKABEN_MASK,
>>> +					  DFSDM_CHCFGR1_CKABEN(0));
>>> +		}
>>> +		priv->ckab_filter_mask &= ~BIT(fl_id);
>>> +		if (!priv->ckab_filter_mask)
>>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
>>> +					  DFSDM_CR2_CKABIE_MASK,
>>> +					  DFSDM_CR2_CKABIE(0));
>>> +		break;
>>> +	default:
>>> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(fl_id), event, 0);
>>> +	}
>>> +
>>> +	priv->filters[fl_id].event_mask &= ~event;
>>> +	spin_unlock_irqrestore(&priv->lock, flags);
>>> +
>>> +	return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(dfsdm_unregister_fl_event);
> 
> [...]
>>> +/* DFSDM filter conversion type */
>>> +enum stm32_dfsdm_conv_type {
>>> +	DFSDM_FILTER_REG_CONV,      /* Regular conversion */
>>> +	DFSDM_FILTER_SW_INJ_CONV,   /* Injected conversion */
>>> +	DFSDM_FILTER_TRIG_INJ_CONV, /* Injected conversion */
>>> +};
>>> +
>>> +/* DFSDM filter regular synchronous mode */
>>> +enum stm32_dfsdm_conv_rsync {
>>> +	DFSDM_FILTER_RSYNC_OFF, /* regular conversion asynchronous */
>>> +	DFSDM_FILTER_RSYNC_ON,  /* regular conversion synchronous with filter0*/
>> stray 0?
> Should read "filter instance 0"...
Cool.
> This corresponds to a specificity of the DFSDM hardware. DFSDM can offer
> possibility to synchronize each filter output with the filter 0 instance
> output.
> As example, this can be used to synchronize several audio microphones.
> Filter 0 is allocated to main microphones and the other filters for
> background microphones (notice that we need one filter per 1-bit PDM stream)
Makes sense, thanks.
> 
> [...]
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-30 20:44             ` Jonathan Cameron
  0 siblings, 0 replies; 130+ messages in thread
From: Jonathan Cameron @ 2017-01-30 20:44 UTC (permalink / raw)
  To: Arnaud Pouliquen, devicetree, linux-arm-kernel, linux-iio,
	alsa-devel, Lee Jones, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre TORGUE

On 30/01/17 15:08, Arnaud Pouliquen wrote:
> Hello Jonathan,
> 
> Thanks for the review. This drivers should disappear,
> but i will integrate you comment/remark in my redesign.
> 
> Please find some comments below, on specific points
> 
> Regards
> Arnaud
> 
> 
> On 01/29/2017 12:53 PM, Jonathan Cameron wrote:
>> On 23/01/17 16:32, Arnaud Pouliquen wrote:
>>> DFSDM hardware IP can be used at the same time for ADC sigma delta
>>> conversion and audio PDM microphone.
>>> MFD driver is in charge of configuring IP registers and managing IP clocks.
>>> For this it exports an API to handles filters and channels resources.
>>>
>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> This is somewhat of a beast.  I would be tempted to build it up in more
>> bite sized chunks.
>>
>> Obvious things to drop from a first version (basically to make it easier
>> to review) would be injected supported.  There may well be others but you'll
>> have a better feel for that than me.
> i will pay attention for next version.
>>
>> I really don't like the wrappers round the regmap functions though.
>> They mean you are papering over errors if they occur and make the code
>> slightly harder to read by implying that something else is going on.
>>
> One aim of the wrapper was to simplify code review, seems that just the
> opposite...
> As i have around 50 regmap accesses, adding  a return/goto for each
> error and at least an associated error messages for each function,
> should add 100 to 150 extra lines...
Keep error message to a minimum. Likely to never occur as you say!
> 
>> Yes the code will be longer without them, but you will also be forced to think
>> properly about error paths.
> 
> I have a question around this. What should be the action if a register
> access return an error?
> Possible root causes:
> 	- bad address of the IP (DT)
> 	- IP not clocked or in reset (driver BUG).
> 	- IP is out of order (hardware issue)
> 	- bug in driver that access to an invalid address.
> 
> So except for the last root cause,we can suppose that every register
> accesses should always be OK or KO...
> This is also a reason of the wrapper. Detect driver bug, without adding
> a test on each register access return.
> 
> Perhaps a compromise could be that test is done only during some
> specific phase (probe, after reset deassertion, clock enabling...) and
> then trust access without test?
> Or simply add error message in regmap helper routines...
> 
> Please just tell/confirm me your preference.
Always assume an error can occur anywhere and handle it as best possible (usually
drop straight out of the function with an error).

I agree it doesn't always give that much information, but trying to paper over
a problem and continue is usually a worse idea.
> 
>>
>> Anyhow, was mostly reading this to get a feel for what was going on in the
>> whole series so not really a terribly thorough review I'm afraid. Sorry about
>> that!
> More than enough for this first version :)
> 
>>
>> Jonathan
>>> ---
>>>  drivers/mfd/Kconfig             |   11 +
>>>  drivers/mfd/Makefile            |    2 +
>>>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
>>>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
>>>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
>>>  5 files changed, 1601 insertions(+)
>>>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
>>>  create mode 100644 drivers/mfd/stm32-dfsdm.c
>>>  create mode 100644 include/linux/mfd/stm32-dfsdm.h
> 
> 
> [...]
> 
>>> diff --git a/drivers/mfd/stm32-dfsdm.c b/drivers/mfd/stm32-dfsdm.c
>>> new file mode 100644
>>> index 0000000..81ca29c
>>> --- /dev/null
>>> +++ b/drivers/mfd/stm32-dfsdm.c
>>> @@ -0,0 +1,1044 @@
>>> +/*
>>> + * This file is part of STM32 DFSDM mfd driver
>>> + *
>>> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
>>> + * Author(s): Arnaud Pouliquen <arnaud.pouliquen@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/interrupt.h>
>>> +#include <linux/mfd/core.h>
>>> +#include <linux/module.h>
>>> +#include <linux/of_device.h>
>>> +#include <linux/regmap.h>
>>> +#include <linux/slab.h>
>>> +
>>> +#include <linux/mfd/stm32-dfsdm.h>
>>> +
>>> +#include "stm32-dfsdm-reg.h"
>>> +
>>> +#define DFSDM_UPDATE_BITS(regm, reg, mask, val) \
>>> +		WARN_ON(regmap_update_bits(regm, reg, mask, val))
>> Don't do these wrappers please. Handle error correctly in all cases.
>> Reviewing as I tend to do backwards through the driver, I thought these
>> were doing something interesting.
>>
>> Effectively you have no error handling as a result of these which needs
>> fixing.
>>
>>> +
>>> +#define DFSDM_REG_READ(regm, reg, val) \
>>> +		WARN_ON(regmap_read(regm, reg, val))
>>> +
>>> +#define DFSDM_REG_WRITE(regm, reg, val) \
>>> +		WARN_ON(regmap_write(regm, reg, val))
>>> +
>>> +#define STM32H7_DFSDM_NUM_FILTERS	4
>>> +#define STM32H7_DFSDM_NUM_INPUTS	8
>>> +
>>> +enum dfsdm_clkout_src {
>>> +	DFSDM_CLK,
>>> +	AUDIO_CLK
>>> +};
>>> +
>>> +struct stm32_dev_data {
>>> +	const struct stm32_dfsdm dfsdm;
>>> +	const struct regmap_config *regmap_cfg;
>>> +};
>>> +
>>> +struct dfsdm_priv;
>>> +
>>> +struct filter_params {
>>> +	unsigned int id;
>>> +	int irq;
>>> +	struct stm32_dfsdm_fl_event event;
>>> +	u32 event_mask;
>>> +	struct dfsdm_priv *priv; /* Cross ref for context */
>>> +	unsigned int ext_ch_mask;
>>> +	unsigned int scan_ch;
>>> +};
>>> +
>>> +struct ch_params {
>>> +	struct stm32_dfsdm_channel ch;
>>> +};
>>> +
>> I'd like to see a lot more comments in here.  Perhaps full kernel-doc
>> as some elements are not that obvious at least to a fairly casual read.
>>
> Description in device-tree tree bindings and cover-letter is not
> sufficient? you would a doc in Document/arm/stm32?
Sorry, just meant on the following structure.  Internal comments would
make it easier to follow what the elements of this structure are for.
> 
>>> +struct dfsdm_priv {
>>> +	struct platform_device *pdev;
>>> +	struct stm32_dfsdm dfsdm;
>>> +
>>> +	spinlock_t lock; /* Used for resource sharing & interrupt lock */
>>> +
>>> +	/* Filters */
>>> +	struct filter_params *filters;
>>> +	unsigned int free_filter_mask;
>>> +	unsigned int scd_filter_mask;
>>> +	unsigned int ckab_filter_mask;
>>> +
>>> +	/* Channels */
>>> +	struct stm32_dfsdm_channel *channels;
>>> +	unsigned int free_channel_mask;
>>> +	atomic_t n_active_ch;
>>> +
>>> +	/* Clock */
>>> +	struct clk *clk;
>>> +	struct clk *aclk;
>>> +	unsigned int clkout_div;
>>> +	unsigned int clkout_freq_req;
>>> +
>>> +	/* Registers*/
>>> +	void __iomem *base;
>>> +	struct regmap *regmap;
>>> +	phys_addr_t phys_base;
>>> +};
>>> +
>>> +/*
>>> + * Common
>>> + */
>>> +static bool stm32_dfsdm_volatile_reg(struct device *dev, unsigned int reg)
>>> +{
>>> +	if (reg < DFSDM_FILTER_BASE_ADR)
>>> +		return false;
>>> +
>>> +	/*
>>> +	 * Mask is done on register to avoid to list registers of all them
>>> +	 * filter instances.
>>> +	 */
>>> +	switch (reg & DFSDM_FILTER_REG_MASK) {
>>> +	case DFSDM_CR1(0) & DFSDM_FILTER_REG_MASK:
>>> +	case DFSDM_ISR(0) & DFSDM_FILTER_REG_MASK:
>>> +	case DFSDM_JDATAR(0) & DFSDM_FILTER_REG_MASK:
>>> +	case DFSDM_RDATAR(0) & DFSDM_FILTER_REG_MASK:
>>> +		return true;
>>> +	}
>>> +
>>> +	return false;
>>> +}
>>> +
>>> +static const struct regmap_config stm32h7_dfsdm_regmap_cfg = {
>>> +	.reg_bits = 32,
>>> +	.val_bits = 32,
>>> +	.reg_stride = sizeof(u32),
>>> +	.max_register = DFSDM_CNVTIMR(STM32H7_DFSDM_NUM_FILTERS - 1),
>>> +	.volatile_reg = stm32_dfsdm_volatile_reg,
>>> +	.fast_io = true,
>>> +};
>>> +
>>> +static const struct stm32_dev_data stm32h7_data = {
>>> +	.dfsdm.max_channels = STM32H7_DFSDM_NUM_INPUTS,
>>> +	.dfsdm.max_filters = STM32H7_DFSDM_NUM_FILTERS,
>>> +	.regmap_cfg = &stm32h7_dfsdm_regmap_cfg,
>>> +};
>>> +
> 
> [...]
> 
>>> +
>>> +/**
>>> + * stm32_dfsdm_get_filter_dma_addr - Get register address for dma transfer.
>>> + *
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @fl_id: Filter id.
>>> + * @conv: Conversion type.
>>> + */
>>> +dma_addr_t stm32_dfsdm_get_filter_dma_phy_addr(struct stm32_dfsdm *dfsdm,
>>> +					       unsigned int fl_id,
>>> +					       enum stm32_dfsdm_conv_type conv)
>>> +{
>>> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
>>> +
>>> +	if (conv == DFSDM_FILTER_REG_CONV)
>>> +		return (dma_addr_t)(priv->phys_base + DFSDM_RDATAR(fl_id));
>>> +	else
>>> +		return (dma_addr_t)(priv->phys_base + DFSDM_JDATAR(fl_id));
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_register_fl_event - Register filter event.
>> What is a filter event?  More details good on things that are very
>> device specific like this.
> Filter events correspond to filter IRQ status, will be handled in a
> different way in IIO.
>>> + *
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @fl_id: Filter id.
>>> + * @event: Event to unregister.
>>> + * @chan_mask: Mask of channels associated to filter.
>>> + *
>>> + * The function enables associated IRQ.
>>> + */
>>> +int stm32_dfsdm_register_fl_event(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
>>> +				  enum stm32_dfsdm_events event,
>>> +				  unsigned int chan_mask)
>>> +{
>>> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
>>> +	unsigned long flags, ulmask = chan_mask;
>>> +	int ret, i;
>>> +
>>> +	dev_dbg(&priv->pdev->dev, "%s:for filter %d: event %#x ch_mask %#x\n",
>>> +		__func__, fl_id, event, chan_mask);
>>> +
>>> +	if (event > DFSDM_EVENT_CKA)
>>> +		return -EINVAL;
>>> +
>>> +	/* Clear interrupt before enable them */
>>> +	ret = stm32_dfsdm_clear_event(priv, fl_id, event, chan_mask);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	spin_lock_irqsave(&priv->lock, flags);
>>> +	/* Enable interrupts */
>>> +	switch (event) {
>>> +	case DFSDM_EVENT_SCD:
>>> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
>>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
>>> +					  DFSDM_CHCFGR1_SCDEN_MASK,
>>> +					  DFSDM_CHCFGR1_SCDEN(1));
>>> +		}
>>> +		if (!priv->scd_filter_mask)
>>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
>>> +					  DFSDM_CR2_SCDIE_MASK,
>>> +					  DFSDM_CR2_SCDIE(1));
>>> +		priv->scd_filter_mask |= BIT(fl_id);
>>> +		break;
>>> +	case DFSDM_EVENT_CKA:
>>> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
>>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
>>> +					  DFSDM_CHCFGR1_CKABEN_MASK,
>>> +					  DFSDM_CHCFGR1_CKABEN(1));
>>> +		}
>>> +		if (!priv->ckab_filter_mask)
>>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
>>> +					  DFSDM_CR2_CKABIE_MASK,
>>> +					  DFSDM_CR2_CKABIE(1));
>>> +		priv->ckab_filter_mask |= BIT(fl_id);
>>> +		break;
>>> +	default:
>>> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(fl_id), event, event);
>>> +	}
>>> +	priv->filters[fl_id].event_mask |= event;
>>> +	spin_unlock_irqrestore(&priv->lock, flags);
>>> +
>>> +	return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(dfsdm_register_fl_event);
>>> +
>>> +/**
>>> + * stm32_dfsdm_unregister_fl_event - Unregister filter event.
>>> + *
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @fl_id: Filter id.
>>> + * @event: Event to unregister.
>>> + * @chan_mask: Mask of channels associated to filter.
>>> + *
>>> + * The function disables associated IRQ.
>>> + */
>>> +int stm32_dfsdm_unregister_fl_event(struct stm32_dfsdm *dfsdm,
>>> +				    unsigned int fl_id,
>>> +				    enum stm32_dfsdm_events event,
>>> +				    unsigned int chan_mask)
>>> +{
>>> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
>>> +	unsigned long flags, ulmask = chan_mask;
>>> +	int i;
>>> +
>>> +	dev_dbg(&priv->pdev->dev, "%s:for filter %d: event %#x ch_mask %#x\n",
>>> +		__func__, fl_id, event, chan_mask);
>>> +
>>> +	if (event > DFSDM_EVENT_CKA)
>>> +		return -EINVAL;
>>> +
>>> +	spin_lock_irqsave(&priv->lock, flags);
>>> +	/* Disable interrupts */
>>> +	switch (event) {
>>> +	case DFSDM_EVENT_SCD:
>>> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
>>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
>>> +					  DFSDM_CHCFGR1_SCDEN_MASK,
>>> +					  DFSDM_CHCFGR1_SCDEN(0));
>>> +		}
>>> +		priv->scd_filter_mask &= ~BIT(fl_id);
>>> +		if (!priv->scd_filter_mask)
>>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
>>> +					  DFSDM_CR2_SCDIE_MASK,
>>> +					  DFSDM_CR2_SCDIE(0));
>>> +		break;
>>> +	case DFSDM_EVENT_CKA:
>>> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
>>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
>>> +					  DFSDM_CHCFGR1_CKABEN_MASK,
>>> +					  DFSDM_CHCFGR1_CKABEN(0));
>>> +		}
>>> +		priv->ckab_filter_mask &= ~BIT(fl_id);
>>> +		if (!priv->ckab_filter_mask)
>>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
>>> +					  DFSDM_CR2_CKABIE_MASK,
>>> +					  DFSDM_CR2_CKABIE(0));
>>> +		break;
>>> +	default:
>>> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(fl_id), event, 0);
>>> +	}
>>> +
>>> +	priv->filters[fl_id].event_mask &= ~event;
>>> +	spin_unlock_irqrestore(&priv->lock, flags);
>>> +
>>> +	return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(dfsdm_unregister_fl_event);
> 
> [...]
>>> +/* DFSDM filter conversion type */
>>> +enum stm32_dfsdm_conv_type {
>>> +	DFSDM_FILTER_REG_CONV,      /* Regular conversion */
>>> +	DFSDM_FILTER_SW_INJ_CONV,   /* Injected conversion */
>>> +	DFSDM_FILTER_TRIG_INJ_CONV, /* Injected conversion */
>>> +};
>>> +
>>> +/* DFSDM filter regular synchronous mode */
>>> +enum stm32_dfsdm_conv_rsync {
>>> +	DFSDM_FILTER_RSYNC_OFF, /* regular conversion asynchronous */
>>> +	DFSDM_FILTER_RSYNC_ON,  /* regular conversion synchronous with filter0*/
>> stray 0?
> Should read "filter instance 0"...
Cool.
> This corresponds to a specificity of the DFSDM hardware. DFSDM can offer
> possibility to synchronize each filter output with the filter 0 instance
> output.
> As example, this can be used to synchronize several audio microphones.
> Filter 0 is allocated to main microphones and the other filters for
> background microphones (notice that we need one filter per 1-bit PDM stream)
Makes sense, thanks.
> 
> [...]
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 


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

* [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-30 20:44             ` Jonathan Cameron
  0 siblings, 0 replies; 130+ messages in thread
From: Jonathan Cameron @ 2017-01-30 20:44 UTC (permalink / raw)
  To: linux-arm-kernel

On 30/01/17 15:08, Arnaud Pouliquen wrote:
> Hello Jonathan,
> 
> Thanks for the review. This drivers should disappear,
> but i will integrate you comment/remark in my redesign.
> 
> Please find some comments below, on specific points
> 
> Regards
> Arnaud
> 
> 
> On 01/29/2017 12:53 PM, Jonathan Cameron wrote:
>> On 23/01/17 16:32, Arnaud Pouliquen wrote:
>>> DFSDM hardware IP can be used at the same time for ADC sigma delta
>>> conversion and audio PDM microphone.
>>> MFD driver is in charge of configuring IP registers and managing IP clocks.
>>> For this it exports an API to handles filters and channels resources.
>>>
>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> This is somewhat of a beast.  I would be tempted to build it up in more
>> bite sized chunks.
>>
>> Obvious things to drop from a first version (basically to make it easier
>> to review) would be injected supported.  There may well be others but you'll
>> have a better feel for that than me.
> i will pay attention for next version.
>>
>> I really don't like the wrappers round the regmap functions though.
>> They mean you are papering over errors if they occur and make the code
>> slightly harder to read by implying that something else is going on.
>>
> One aim of the wrapper was to simplify code review, seems that just the
> opposite...
> As i have around 50 regmap accesses, adding  a return/goto for each
> error and at least an associated error messages for each function,
> should add 100 to 150 extra lines...
Keep error message to a minimum. Likely to never occur as you say!
> 
>> Yes the code will be longer without them, but you will also be forced to think
>> properly about error paths.
> 
> I have a question around this. What should be the action if a register
> access return an error?
> Possible root causes:
> 	- bad address of the IP (DT)
> 	- IP not clocked or in reset (driver BUG).
> 	- IP is out of order (hardware issue)
> 	- bug in driver that access to an invalid address.
> 
> So except for the last root cause,we can suppose that every register
> accesses should always be OK or KO...
> This is also a reason of the wrapper. Detect driver bug, without adding
> a test on each register access return.
> 
> Perhaps a compromise could be that test is done only during some
> specific phase (probe, after reset deassertion, clock enabling...) and
> then trust access without test?
> Or simply add error message in regmap helper routines...
> 
> Please just tell/confirm me your preference.
Always assume an error can occur anywhere and handle it as best possible (usually
drop straight out of the function with an error).

I agree it doesn't always give that much information, but trying to paper over
a problem and continue is usually a worse idea.
> 
>>
>> Anyhow, was mostly reading this to get a feel for what was going on in the
>> whole series so not really a terribly thorough review I'm afraid. Sorry about
>> that!
> More than enough for this first version :)
> 
>>
>> Jonathan
>>> ---
>>>  drivers/mfd/Kconfig             |   11 +
>>>  drivers/mfd/Makefile            |    2 +
>>>  drivers/mfd/stm32-dfsdm-reg.h   |  220 +++++++++
>>>  drivers/mfd/stm32-dfsdm.c       | 1044 +++++++++++++++++++++++++++++++++++++++
>>>  include/linux/mfd/stm32-dfsdm.h |  324 ++++++++++++
>>>  5 files changed, 1601 insertions(+)
>>>  create mode 100644 drivers/mfd/stm32-dfsdm-reg.h
>>>  create mode 100644 drivers/mfd/stm32-dfsdm.c
>>>  create mode 100644 include/linux/mfd/stm32-dfsdm.h
> 
> 
> [...]
> 
>>> diff --git a/drivers/mfd/stm32-dfsdm.c b/drivers/mfd/stm32-dfsdm.c
>>> new file mode 100644
>>> index 0000000..81ca29c
>>> --- /dev/null
>>> +++ b/drivers/mfd/stm32-dfsdm.c
>>> @@ -0,0 +1,1044 @@
>>> +/*
>>> + * This file is part of STM32 DFSDM mfd driver
>>> + *
>>> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
>>> + * Author(s): Arnaud Pouliquen <arnaud.pouliquen@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/interrupt.h>
>>> +#include <linux/mfd/core.h>
>>> +#include <linux/module.h>
>>> +#include <linux/of_device.h>
>>> +#include <linux/regmap.h>
>>> +#include <linux/slab.h>
>>> +
>>> +#include <linux/mfd/stm32-dfsdm.h>
>>> +
>>> +#include "stm32-dfsdm-reg.h"
>>> +
>>> +#define DFSDM_UPDATE_BITS(regm, reg, mask, val) \
>>> +		WARN_ON(regmap_update_bits(regm, reg, mask, val))
>> Don't do these wrappers please. Handle error correctly in all cases.
>> Reviewing as I tend to do backwards through the driver, I thought these
>> were doing something interesting.
>>
>> Effectively you have no error handling as a result of these which needs
>> fixing.
>>
>>> +
>>> +#define DFSDM_REG_READ(regm, reg, val) \
>>> +		WARN_ON(regmap_read(regm, reg, val))
>>> +
>>> +#define DFSDM_REG_WRITE(regm, reg, val) \
>>> +		WARN_ON(regmap_write(regm, reg, val))
>>> +
>>> +#define STM32H7_DFSDM_NUM_FILTERS	4
>>> +#define STM32H7_DFSDM_NUM_INPUTS	8
>>> +
>>> +enum dfsdm_clkout_src {
>>> +	DFSDM_CLK,
>>> +	AUDIO_CLK
>>> +};
>>> +
>>> +struct stm32_dev_data {
>>> +	const struct stm32_dfsdm dfsdm;
>>> +	const struct regmap_config *regmap_cfg;
>>> +};
>>> +
>>> +struct dfsdm_priv;
>>> +
>>> +struct filter_params {
>>> +	unsigned int id;
>>> +	int irq;
>>> +	struct stm32_dfsdm_fl_event event;
>>> +	u32 event_mask;
>>> +	struct dfsdm_priv *priv; /* Cross ref for context */
>>> +	unsigned int ext_ch_mask;
>>> +	unsigned int scan_ch;
>>> +};
>>> +
>>> +struct ch_params {
>>> +	struct stm32_dfsdm_channel ch;
>>> +};
>>> +
>> I'd like to see a lot more comments in here.  Perhaps full kernel-doc
>> as some elements are not that obvious at least to a fairly casual read.
>>
> Description in device-tree tree bindings and cover-letter is not
> sufficient? you would a doc in Document/arm/stm32?
Sorry, just meant on the following structure.  Internal comments would
make it easier to follow what the elements of this structure are for.
> 
>>> +struct dfsdm_priv {
>>> +	struct platform_device *pdev;
>>> +	struct stm32_dfsdm dfsdm;
>>> +
>>> +	spinlock_t lock; /* Used for resource sharing & interrupt lock */
>>> +
>>> +	/* Filters */
>>> +	struct filter_params *filters;
>>> +	unsigned int free_filter_mask;
>>> +	unsigned int scd_filter_mask;
>>> +	unsigned int ckab_filter_mask;
>>> +
>>> +	/* Channels */
>>> +	struct stm32_dfsdm_channel *channels;
>>> +	unsigned int free_channel_mask;
>>> +	atomic_t n_active_ch;
>>> +
>>> +	/* Clock */
>>> +	struct clk *clk;
>>> +	struct clk *aclk;
>>> +	unsigned int clkout_div;
>>> +	unsigned int clkout_freq_req;
>>> +
>>> +	/* Registers*/
>>> +	void __iomem *base;
>>> +	struct regmap *regmap;
>>> +	phys_addr_t phys_base;
>>> +};
>>> +
>>> +/*
>>> + * Common
>>> + */
>>> +static bool stm32_dfsdm_volatile_reg(struct device *dev, unsigned int reg)
>>> +{
>>> +	if (reg < DFSDM_FILTER_BASE_ADR)
>>> +		return false;
>>> +
>>> +	/*
>>> +	 * Mask is done on register to avoid to list registers of all them
>>> +	 * filter instances.
>>> +	 */
>>> +	switch (reg & DFSDM_FILTER_REG_MASK) {
>>> +	case DFSDM_CR1(0) & DFSDM_FILTER_REG_MASK:
>>> +	case DFSDM_ISR(0) & DFSDM_FILTER_REG_MASK:
>>> +	case DFSDM_JDATAR(0) & DFSDM_FILTER_REG_MASK:
>>> +	case DFSDM_RDATAR(0) & DFSDM_FILTER_REG_MASK:
>>> +		return true;
>>> +	}
>>> +
>>> +	return false;
>>> +}
>>> +
>>> +static const struct regmap_config stm32h7_dfsdm_regmap_cfg = {
>>> +	.reg_bits = 32,
>>> +	.val_bits = 32,
>>> +	.reg_stride = sizeof(u32),
>>> +	.max_register = DFSDM_CNVTIMR(STM32H7_DFSDM_NUM_FILTERS - 1),
>>> +	.volatile_reg = stm32_dfsdm_volatile_reg,
>>> +	.fast_io = true,
>>> +};
>>> +
>>> +static const struct stm32_dev_data stm32h7_data = {
>>> +	.dfsdm.max_channels = STM32H7_DFSDM_NUM_INPUTS,
>>> +	.dfsdm.max_filters = STM32H7_DFSDM_NUM_FILTERS,
>>> +	.regmap_cfg = &stm32h7_dfsdm_regmap_cfg,
>>> +};
>>> +
> 
> [...]
> 
>>> +
>>> +/**
>>> + * stm32_dfsdm_get_filter_dma_addr - Get register address for dma transfer.
>>> + *
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @fl_id: Filter id.
>>> + * @conv: Conversion type.
>>> + */
>>> +dma_addr_t stm32_dfsdm_get_filter_dma_phy_addr(struct stm32_dfsdm *dfsdm,
>>> +					       unsigned int fl_id,
>>> +					       enum stm32_dfsdm_conv_type conv)
>>> +{
>>> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
>>> +
>>> +	if (conv == DFSDM_FILTER_REG_CONV)
>>> +		return (dma_addr_t)(priv->phys_base + DFSDM_RDATAR(fl_id));
>>> +	else
>>> +		return (dma_addr_t)(priv->phys_base + DFSDM_JDATAR(fl_id));
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_register_fl_event - Register filter event.
>> What is a filter event?  More details good on things that are very
>> device specific like this.
> Filter events correspond to filter IRQ status, will be handled in a
> different way in IIO.
>>> + *
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @fl_id: Filter id.
>>> + * @event: Event to unregister.
>>> + * @chan_mask: Mask of channels associated to filter.
>>> + *
>>> + * The function enables associated IRQ.
>>> + */
>>> +int stm32_dfsdm_register_fl_event(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
>>> +				  enum stm32_dfsdm_events event,
>>> +				  unsigned int chan_mask)
>>> +{
>>> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
>>> +	unsigned long flags, ulmask = chan_mask;
>>> +	int ret, i;
>>> +
>>> +	dev_dbg(&priv->pdev->dev, "%s:for filter %d: event %#x ch_mask %#x\n",
>>> +		__func__, fl_id, event, chan_mask);
>>> +
>>> +	if (event > DFSDM_EVENT_CKA)
>>> +		return -EINVAL;
>>> +
>>> +	/* Clear interrupt before enable them */
>>> +	ret = stm32_dfsdm_clear_event(priv, fl_id, event, chan_mask);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	spin_lock_irqsave(&priv->lock, flags);
>>> +	/* Enable interrupts */
>>> +	switch (event) {
>>> +	case DFSDM_EVENT_SCD:
>>> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
>>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
>>> +					  DFSDM_CHCFGR1_SCDEN_MASK,
>>> +					  DFSDM_CHCFGR1_SCDEN(1));
>>> +		}
>>> +		if (!priv->scd_filter_mask)
>>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
>>> +					  DFSDM_CR2_SCDIE_MASK,
>>> +					  DFSDM_CR2_SCDIE(1));
>>> +		priv->scd_filter_mask |= BIT(fl_id);
>>> +		break;
>>> +	case DFSDM_EVENT_CKA:
>>> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
>>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
>>> +					  DFSDM_CHCFGR1_CKABEN_MASK,
>>> +					  DFSDM_CHCFGR1_CKABEN(1));
>>> +		}
>>> +		if (!priv->ckab_filter_mask)
>>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
>>> +					  DFSDM_CR2_CKABIE_MASK,
>>> +					  DFSDM_CR2_CKABIE(1));
>>> +		priv->ckab_filter_mask |= BIT(fl_id);
>>> +		break;
>>> +	default:
>>> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(fl_id), event, event);
>>> +	}
>>> +	priv->filters[fl_id].event_mask |= event;
>>> +	spin_unlock_irqrestore(&priv->lock, flags);
>>> +
>>> +	return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(dfsdm_register_fl_event);
>>> +
>>> +/**
>>> + * stm32_dfsdm_unregister_fl_event - Unregister filter event.
>>> + *
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @fl_id: Filter id.
>>> + * @event: Event to unregister.
>>> + * @chan_mask: Mask of channels associated to filter.
>>> + *
>>> + * The function disables associated IRQ.
>>> + */
>>> +int stm32_dfsdm_unregister_fl_event(struct stm32_dfsdm *dfsdm,
>>> +				    unsigned int fl_id,
>>> +				    enum stm32_dfsdm_events event,
>>> +				    unsigned int chan_mask)
>>> +{
>>> +	struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
>>> +	unsigned long flags, ulmask = chan_mask;
>>> +	int i;
>>> +
>>> +	dev_dbg(&priv->pdev->dev, "%s:for filter %d: event %#x ch_mask %#x\n",
>>> +		__func__, fl_id, event, chan_mask);
>>> +
>>> +	if (event > DFSDM_EVENT_CKA)
>>> +		return -EINVAL;
>>> +
>>> +	spin_lock_irqsave(&priv->lock, flags);
>>> +	/* Disable interrupts */
>>> +	switch (event) {
>>> +	case DFSDM_EVENT_SCD:
>>> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
>>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
>>> +					  DFSDM_CHCFGR1_SCDEN_MASK,
>>> +					  DFSDM_CHCFGR1_SCDEN(0));
>>> +		}
>>> +		priv->scd_filter_mask &= ~BIT(fl_id);
>>> +		if (!priv->scd_filter_mask)
>>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
>>> +					  DFSDM_CR2_SCDIE_MASK,
>>> +					  DFSDM_CR2_SCDIE(0));
>>> +		break;
>>> +	case DFSDM_EVENT_CKA:
>>> +		for_each_set_bit(i, &ulmask, priv->dfsdm.max_channels) {
>>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CHCFGR1(i),
>>> +					  DFSDM_CHCFGR1_CKABEN_MASK,
>>> +					  DFSDM_CHCFGR1_CKABEN(0));
>>> +		}
>>> +		priv->ckab_filter_mask &= ~BIT(fl_id);
>>> +		if (!priv->ckab_filter_mask)
>>> +			DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(0),
>>> +					  DFSDM_CR2_CKABIE_MASK,
>>> +					  DFSDM_CR2_CKABIE(0));
>>> +		break;
>>> +	default:
>>> +		DFSDM_UPDATE_BITS(priv->regmap, DFSDM_CR2(fl_id), event, 0);
>>> +	}
>>> +
>>> +	priv->filters[fl_id].event_mask &= ~event;
>>> +	spin_unlock_irqrestore(&priv->lock, flags);
>>> +
>>> +	return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(dfsdm_unregister_fl_event);
> 
> [...]
>>> +/* DFSDM filter conversion type */
>>> +enum stm32_dfsdm_conv_type {
>>> +	DFSDM_FILTER_REG_CONV,      /* Regular conversion */
>>> +	DFSDM_FILTER_SW_INJ_CONV,   /* Injected conversion */
>>> +	DFSDM_FILTER_TRIG_INJ_CONV, /* Injected conversion */
>>> +};
>>> +
>>> +/* DFSDM filter regular synchronous mode */
>>> +enum stm32_dfsdm_conv_rsync {
>>> +	DFSDM_FILTER_RSYNC_OFF, /* regular conversion asynchronous */
>>> +	DFSDM_FILTER_RSYNC_ON,  /* regular conversion synchronous with filter0*/
>> stray 0?
> Should read "filter instance 0"...
Cool.
> This corresponds to a specificity of the DFSDM hardware. DFSDM can offer
> possibility to synchronize each filter output with the filter 0 instance
> output.
> As example, this can be used to synchronize several audio microphones.
> Filter 0 is allocated to main microphones and the other filters for
> background microphones (notice that we need one filter per 1-bit PDM stream)
Makes sense, thanks.
> 
> [...]
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

* Re: [PATCH 3/7] IIO: add bindings for STM32 DFSDM ADC driver
  2017-01-29 12:42         ` Jonathan Cameron
  (?)
@ 2017-01-31 14:10           ` Arnaud Pouliquen
  -1 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-31 14:10 UTC (permalink / raw)
  To: Jonathan Cameron, devicetree, linux-arm-kernel, linux-iio,
	alsa-devel, Lee Jones, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Alexandre TORGUE, Maxime Coquelin

Hi Jonathan,

On 01/29/2017 01:42 PM, Jonathan Cameron wrote:
> On 29/01/17 11:58, Jonathan Cameron wrote:
>> On 23/01/17 16:32, Arnaud Pouliquen wrote:
>>> This patch adds documentation of device tree bindings for the
>>> STM32 DFSDM ADC.
>>>
>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> Trivial bits inline.
>>> ---
>>>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 60 ++++++++++++++++++++++
>>>  1 file changed, 60 insertions(+)
>>>  create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
>>>
>>> diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt  b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
>>> new file mode 100644
>>> index 0000000..c156bcb
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 	
>>> @@ -0,0 +1,60 @@
>>> +STMicroelectronics STM32 DFSDM ADC device driver
>>> +
>>> +STM32 DFSDM ADC is a sigma delta analog-to-digital converter.
>>> +It has to be declared in device-tree as a subnode of the DFSDM mfd node.
>>> +
>>> +It has several multiplexed input channels. Conversions can be performed
>>> +in single, scan or discontinuous mode. Conversions can be launched in software
>>> +or using hardware triggers.
>>> +Each instance of the driver uses one filter instance handle by the DFSDM mfd
>>> +driver.
>>> +
>>> +DFSDM also offers extra features:
>>> +-The analog watchdog feature allows the application to detect if the
>>> + input voltage goes beyond the user-defined, higher or lower thresholds.
>>> +-The short circuit detection allows allows the application to detect if the
>>> + input is in CC.
>>> +-The clock absence detection allows application to detect if SPI input is clocked.
>>> +
>>> +Required properties:
>>> +- compatible:	Must be "st,stm32-dfsdm-adc".
>>> +- reg:		Specifies the DFSDM filter instance.
>>> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
>>> +- st,adc-channels:	List of single-ended channels muxed for this ADC.
>>> +- st,adc-channel-names:	List of single-ended channels Name.
>>> +
>>> +Optional properties:
>>> +- st,adc-channel-types:	Single-ended channel input type. Default value is 0.
>>> +			- 0: SPI with data on rising edge (default)
>>> +			- 1: SPI with data on falling edge
>> This is an element that can be described without magic numbers so I'd
>> prefer that you do so.
>>
>> Also spell check edage -> edge.
>>
>>> +			- 2: manchester codec, rising edage = logic 0
>>> +			- 3: manchester codec, rising edage = logic 1
>>> +- st,adc-channel-clk-src: Conversion clock source. default value is 1.
>>> +			  - 0: External SPI clocl (CLKIN x)
>>> +			  - 1: internal SPI clock (CLKOUT) (default)
>>> +			  - 2: internal SPI clock divided by 2 (falling edge).
>>> +			  - 2: internal SPI clock divided by 2 (rising edge).
>> 3?
>>> +- st,adc-alt-channel: 	  must be defined if Two ADCs are connected on same SPI
>>> +			  input.
>>> +			  If not set channel n is connected to SPI input n.
>>> +			  If set channel n is connected to SPI input n + 1.
>> ? Two data inputs with shared clock?
>>> +
>>> +Example:
>>> +	dfsdm: dfsdm@4400D000 {
> I'd like to actually see the ADCs on the end of these explicitly
> described as well.  It's entirely possible we'll have something
> that isn't simply a dumb sigma delta modulator that will need
> it's own driver.
> 
> In some senses what we actually have here is a data bus so we
> should be able to describe things on it in that fashion
> rather than just describing the data that is in coming.
> Our boundary of knowledge isn't at the edge of this device!
> 
> A quick example google fed me would be the gain control on a
> maxim ds8102.
> 
> Tricky bit here will be defining this nice and generally when we
> currently only have the one piece of hardware to do it with ;)
> 
> That's what makes kernel development fun!
> 

What you describe here seems similar to audio codecs device defined in
ASoC. An Audio codec is an external chip that is connected to SoC
through an audio bus.
ASoC allows to bind this codec with the SOC DAI (digital Audio Interface).

FYI we should have this kind of feature to support on STM32 uin second step:
DSFDM input can also process stm32 ADC output through an hardware pipe
(stm32-adc IIO driver that has been recently integrated in kernel).
Internally, to pipe the ADC with DFSDM, we use an extension of inkern
interface that is very similar with the one mentioned by Lars:

https://github.com/analogdevicesinc/linux/blob/xcomm_zynq/drivers
/iio/buffer/hw_consumer.c

Don't know if this is what Lars has in mind... but a solution could be
to define a new type of IIO that would be something like
"postprocessing" or "filter". This new iio device type could be piped to
a frontend IIO device to export a new one (through hw_consumer.c interface).
In this case for simple device like PDM audio microphone that does not
required control a kind of dummy frontend device could be defined...

Regards
Arnaud

>>> +		iio_dfsdm0: iio-dfsdm@0 {
>>> +			compatible = "st,stm32-dfsdm-adc";
>>> +			#io-channel-cells = <1>;
>>> +			reg = <0>;
>>> +			st,adc-channels = <1>;
>>> +			st,adc-channel-names = "in0";
>>> +		};
>>> +		iio_dfsdm1: iio-dfsdm@1 {
>>> +			compatible = "st,stm32-dfsdm-adc";
>>> +			#io-channel-cells = <1>;
>>> +			reg = <1>;
>>> +			st,adc-channels = <1>;
>>> +			st,adc-channel-names = "in1";
>>> +			st,adc-channel-types = <1>;
>>> +			st,adc-alt-channel = <1>;
>>> +		};
>>> +	};
>>>
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>
> 

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

* Re: [PATCH 3/7] IIO: add bindings for STM32 DFSDM ADC driver
@ 2017-01-31 14:10           ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-31 14:10 UTC (permalink / raw)
  To: Jonathan Cameron, devicetree, linux-arm-kernel, linux-iio,
	alsa-devel, Lee Jones, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre TORGUE

Hi Jonathan,

On 01/29/2017 01:42 PM, Jonathan Cameron wrote:
> On 29/01/17 11:58, Jonathan Cameron wrote:
>> On 23/01/17 16:32, Arnaud Pouliquen wrote:
>>> This patch adds documentation of device tree bindings for the
>>> STM32 DFSDM ADC.
>>>
>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> Trivial bits inline.
>>> ---
>>>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 60 ++++++++++++++++++++++
>>>  1 file changed, 60 insertions(+)
>>>  create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
>>>
>>> diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt  b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
>>> new file mode 100644
>>> index 0000000..c156bcb
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 	
>>> @@ -0,0 +1,60 @@
>>> +STMicroelectronics STM32 DFSDM ADC device driver
>>> +
>>> +STM32 DFSDM ADC is a sigma delta analog-to-digital converter.
>>> +It has to be declared in device-tree as a subnode of the DFSDM mfd node.
>>> +
>>> +It has several multiplexed input channels. Conversions can be performed
>>> +in single, scan or discontinuous mode. Conversions can be launched in software
>>> +or using hardware triggers.
>>> +Each instance of the driver uses one filter instance handle by the DFSDM mfd
>>> +driver.
>>> +
>>> +DFSDM also offers extra features:
>>> +-The analog watchdog feature allows the application to detect if the
>>> + input voltage goes beyond the user-defined, higher or lower thresholds.
>>> +-The short circuit detection allows allows the application to detect if the
>>> + input is in CC.
>>> +-The clock absence detection allows application to detect if SPI input is clocked.
>>> +
>>> +Required properties:
>>> +- compatible:	Must be "st,stm32-dfsdm-adc".
>>> +- reg:		Specifies the DFSDM filter instance.
>>> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
>>> +- st,adc-channels:	List of single-ended channels muxed for this ADC.
>>> +- st,adc-channel-names:	List of single-ended channels Name.
>>> +
>>> +Optional properties:
>>> +- st,adc-channel-types:	Single-ended channel input type. Default value is 0.
>>> +			- 0: SPI with data on rising edge (default)
>>> +			- 1: SPI with data on falling edge
>> This is an element that can be described without magic numbers so I'd
>> prefer that you do so.
>>
>> Also spell check edage -> edge.
>>
>>> +			- 2: manchester codec, rising edage = logic 0
>>> +			- 3: manchester codec, rising edage = logic 1
>>> +- st,adc-channel-clk-src: Conversion clock source. default value is 1.
>>> +			  - 0: External SPI clocl (CLKIN x)
>>> +			  - 1: internal SPI clock (CLKOUT) (default)
>>> +			  - 2: internal SPI clock divided by 2 (falling edge).
>>> +			  - 2: internal SPI clock divided by 2 (rising edge).
>> 3?
>>> +- st,adc-alt-channel: 	  must be defined if Two ADCs are connected on same SPI
>>> +			  input.
>>> +			  If not set channel n is connected to SPI input n.
>>> +			  If set channel n is connected to SPI input n + 1.
>> ? Two data inputs with shared clock?
>>> +
>>> +Example:
>>> +	dfsdm: dfsdm@4400D000 {
> I'd like to actually see the ADCs on the end of these explicitly
> described as well.  It's entirely possible we'll have something
> that isn't simply a dumb sigma delta modulator that will need
> it's own driver.
> 
> In some senses what we actually have here is a data bus so we
> should be able to describe things on it in that fashion
> rather than just describing the data that is in coming.
> Our boundary of knowledge isn't at the edge of this device!
> 
> A quick example google fed me would be the gain control on a
> maxim ds8102.
> 
> Tricky bit here will be defining this nice and generally when we
> currently only have the one piece of hardware to do it with ;)
> 
> That's what makes kernel development fun!
> 

What you describe here seems similar to audio codecs device defined in
ASoC. An Audio codec is an external chip that is connected to SoC
through an audio bus.
ASoC allows to bind this codec with the SOC DAI (digital Audio Interface).

FYI we should have this kind of feature to support on STM32 uin second step:
DSFDM input can also process stm32 ADC output through an hardware pipe
(stm32-adc IIO driver that has been recently integrated in kernel).
Internally, to pipe the ADC with DFSDM, we use an extension of inkern
interface that is very similar with the one mentioned by Lars:

https://github.com/analogdevicesinc/linux/blob/xcomm_zynq/drivers
/iio/buffer/hw_consumer.c

Don't know if this is what Lars has in mind... but a solution could be
to define a new type of IIO that would be something like
"postprocessing" or "filter". This new iio device type could be piped to
a frontend IIO device to export a new one (through hw_consumer.c interface).
In this case for simple device like PDM audio microphone that does not
required control a kind of dummy frontend device could be defined...

Regards
Arnaud

>>> +		iio_dfsdm0: iio-dfsdm@0 {
>>> +			compatible = "st,stm32-dfsdm-adc";
>>> +			#io-channel-cells = <1>;
>>> +			reg = <0>;
>>> +			st,adc-channels = <1>;
>>> +			st,adc-channel-names = "in0";
>>> +		};
>>> +		iio_dfsdm1: iio-dfsdm@1 {
>>> +			compatible = "st,stm32-dfsdm-adc";
>>> +			#io-channel-cells = <1>;
>>> +			reg = <1>;
>>> +			st,adc-channels = <1>;
>>> +			st,adc-channel-names = "in1";
>>> +			st,adc-channel-types = <1>;
>>> +			st,adc-alt-channel = <1>;
>>> +		};
>>> +	};
>>>
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>
> 

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

* [PATCH 3/7] IIO: add bindings for STM32 DFSDM ADC driver
@ 2017-01-31 14:10           ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-31 14:10 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Jonathan,

On 01/29/2017 01:42 PM, Jonathan Cameron wrote:
> On 29/01/17 11:58, Jonathan Cameron wrote:
>> On 23/01/17 16:32, Arnaud Pouliquen wrote:
>>> This patch adds documentation of device tree bindings for the
>>> STM32 DFSDM ADC.
>>>
>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>> Trivial bits inline.
>>> ---
>>>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 60 ++++++++++++++++++++++
>>>  1 file changed, 60 insertions(+)
>>>  create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
>>>
>>> diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt  b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
>>> new file mode 100644
>>> index 0000000..c156bcb
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 	
>>> @@ -0,0 +1,60 @@
>>> +STMicroelectronics STM32 DFSDM ADC device driver
>>> +
>>> +STM32 DFSDM ADC is a sigma delta analog-to-digital converter.
>>> +It has to be declared in device-tree as a subnode of the DFSDM mfd node.
>>> +
>>> +It has several multiplexed input channels. Conversions can be performed
>>> +in single, scan or discontinuous mode. Conversions can be launched in software
>>> +or using hardware triggers.
>>> +Each instance of the driver uses one filter instance handle by the DFSDM mfd
>>> +driver.
>>> +
>>> +DFSDM also offers extra features:
>>> +-The analog watchdog feature allows the application to detect if the
>>> + input voltage goes beyond the user-defined, higher or lower thresholds.
>>> +-The short circuit detection allows allows the application to detect if the
>>> + input is in CC.
>>> +-The clock absence detection allows application to detect if SPI input is clocked.
>>> +
>>> +Required properties:
>>> +- compatible:	Must be "st,stm32-dfsdm-adc".
>>> +- reg:		Specifies the DFSDM filter instance.
>>> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
>>> +- st,adc-channels:	List of single-ended channels muxed for this ADC.
>>> +- st,adc-channel-names:	List of single-ended channels Name.
>>> +
>>> +Optional properties:
>>> +- st,adc-channel-types:	Single-ended channel input type. Default value is 0.
>>> +			- 0: SPI with data on rising edge (default)
>>> +			- 1: SPI with data on falling edge
>> This is an element that can be described without magic numbers so I'd
>> prefer that you do so.
>>
>> Also spell check edage -> edge.
>>
>>> +			- 2: manchester codec, rising edage = logic 0
>>> +			- 3: manchester codec, rising edage = logic 1
>>> +- st,adc-channel-clk-src: Conversion clock source. default value is 1.
>>> +			  - 0: External SPI clocl (CLKIN x)
>>> +			  - 1: internal SPI clock (CLKOUT) (default)
>>> +			  - 2: internal SPI clock divided by 2 (falling edge).
>>> +			  - 2: internal SPI clock divided by 2 (rising edge).
>> 3?
>>> +- st,adc-alt-channel: 	  must be defined if Two ADCs are connected on same SPI
>>> +			  input.
>>> +			  If not set channel n is connected to SPI input n.
>>> +			  If set channel n is connected to SPI input n + 1.
>> ? Two data inputs with shared clock?
>>> +
>>> +Example:
>>> +	dfsdm: dfsdm at 4400D000 {
> I'd like to actually see the ADCs on the end of these explicitly
> described as well.  It's entirely possible we'll have something
> that isn't simply a dumb sigma delta modulator that will need
> it's own driver.
> 
> In some senses what we actually have here is a data bus so we
> should be able to describe things on it in that fashion
> rather than just describing the data that is in coming.
> Our boundary of knowledge isn't at the edge of this device!
> 
> A quick example google fed me would be the gain control on a
> maxim ds8102.
> 
> Tricky bit here will be defining this nice and generally when we
> currently only have the one piece of hardware to do it with ;)
> 
> That's what makes kernel development fun!
> 

What you describe here seems similar to audio codecs device defined in
ASoC. An Audio codec is an external chip that is connected to SoC
through an audio bus.
ASoC allows to bind this codec with the SOC DAI (digital Audio Interface).

FYI we should have this kind of feature to support on STM32 uin second step:
DSFDM input can also process stm32 ADC output through an hardware pipe
(stm32-adc IIO driver that has been recently integrated in kernel).
Internally, to pipe the ADC with DFSDM, we use an extension of inkern
interface that is very similar with the one mentioned by Lars:

https://github.com/analogdevicesinc/linux/blob/xcomm_zynq/drivers
/iio/buffer/hw_consumer.c

Don't know if this is what Lars has in mind... but a solution could be
to define a new type of IIO that would be something like
"postprocessing" or "filter". This new iio device type could be piped to
a frontend IIO device to export a new one (through hw_consumer.c interface).
In this case for simple device like PDM audio microphone that does not
required control a kind of dummy frontend device could be defined...

Regards
Arnaud

>>> +		iio_dfsdm0: iio-dfsdm at 0 {
>>> +			compatible = "st,stm32-dfsdm-adc";
>>> +			#io-channel-cells = <1>;
>>> +			reg = <0>;
>>> +			st,adc-channels = <1>;
>>> +			st,adc-channel-names = "in0";
>>> +		};
>>> +		iio_dfsdm1: iio-dfsdm at 1 {
>>> +			compatible = "st,stm32-dfsdm-adc";
>>> +			#io-channel-cells = <1>;
>>> +			reg = <1>;
>>> +			st,adc-channels = <1>;
>>> +			st,adc-channel-names = "in1";
>>> +			st,adc-channel-types = <1>;
>>> +			st,adc-alt-channel = <1>;
>>> +		};
>>> +	};
>>>
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>> the body of a message to majordomo at vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>
> 

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
  2017-01-29 14:19                             ` Lars-Peter Clausen
  (?)
@ 2017-01-31 15:30                               ` Arnaud Pouliquen
  -1 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-31 15:30 UTC (permalink / raw)
  To: Lars-Peter Clausen, Jonathan Cameron, Lee Jones
  Cc: Mark Rutland, devicetree, alsa-devel, Maxime Coquelin,
	Liam Girdwood, linux-iio, Mark Brown, Takashi Iwai, Rob Herring,
	Peter Meerwald-Stadler, Hartmut Knaack, linux-arm-kernel,
	Alexandre TORGUE

Hello Lars,

On 01/29/2017 03:19 PM, Lars-Peter Clausen wrote:
> On 01/29/2017 01:28 PM, Jonathan Cameron wrote:
> [...]
>>>> Jonathan, Mark, Please could you share your opinion on this topic?
>> Hmm - based on a fairly quick read through of the code (which is never
>> ideal!). I can see that the ideal would indeed be as Lee says, to
>> expand the IIO interfaces sufficiently to support what you need.
>>
>>
>> So, reading the code (fairly quickly I'm afraid as had a lot of reviews
>> to catch up on this weekend).
>> What we need:
>> 1) DMA support in the ADC driver.  This would be a good anyway!
>> 2) DMA consumer support - I defer to Lars for comments on this.
>> 3) Means of describing and controlling the sinc filters applied. 
>> 4) Appropriate channel support.  I'm not convinced that it doesn't make
>> sense to have IIO channels for the microphones - at least in a streaming
>> mode.  It's data - I don't really care what ;)
>> Coarsely it's a filtered pulse per period counter which is
>> a perfectly valid type to have a channel for.
>>
>> The big question to my mind is the DMA consumer support. How would
>> it work. It it wouldn't this is somewhat of a non starter.
>>
>> To bring up another slightly ugly MFD case where it is borderline
>> on whether an MFD makes sense (just as a reference point of something
>> we have discussed a few times before)
>>
>> ADCs with features directed at touchscreen support.
>> These are odd as the ADC bit is generic, but the specific output
>> and read sequences used for touchscreen reading don't correspond to
>> anything that makes any real sense for other applications.
>>
>> We have started to get hybrid drives that have an MFD underneath but
>> do the ADC reads through IIO consumer interfaces, and the timing
>> control from a touchscreen driver.  We haven't really gotten this
>> one right yet either.
>>
>> Here however, to my mind things are different - as I read it
>> (and feel free to point out what I'm missing), the sound usecase
>> is just a question of setting up sampling frequencies and filters
>> appropriate to the microphones and what ASoC expects?
>>
>> That's not to say the IIO dma stuff is flexible enough (yet) to
>> handle the data flows, but perhaps we can work towards that.
> 
> Yeah, so this is a bit different, but not unexpected. And I'm sure we'll see
> more similar hardware in the future. I've talked about this before[1], the
> cost structure of creating and manufacturing new hardware drives the design
> in a certain direction so that we end up with general purpose hardware that
> suddenly has applications in multiple frameworks that were previously fully
> orthogonal.
> 
> This device is certainly not a multi-function-device. It only has one
> function, it's a sigma-delta demodulator. It is rather a
> multi-purpose-device. It can be used for sigma-delta demodulation in audio
> applications as well as more specialized data capture applications.
> 
> It's comparable to something like a GPIO that can be used to control a reset
> pin or turn on and off a LED. The GPIO chip is not considered
> multi-function-device though, even though it can be used for many different
> applications.
> 
> As for DMA we already have a lot of DMA infrastructure on the audio side and
> we probably want to reuse that rather than inserting IIO as a middle layer
> since audio buffer capture as different requirements from IIO buffer and
> we'd have to go the route of the least common denominator and loose
> expressibility in the process.

I'm agree with your analysis.
Audio DMA engine seems a more secure solution to avoid audio runtime
issue, and should minimize impact in IIO.

> 
> I've created a IIO buffer[2] that does not capture data to memory but is
> only used to enable/disable the data capture process. We use this in setups
> where the data is passed from the converter to a application specific
> processing chain without ever going through system memory. This buffer could
> probably also be used here on the audio side to control the converter state.

This remind me HDMI.
For HDMI, solution integrated is a drm driver that probes
"hdmi-audio-codec" driver.

Something similar could be done. An IIO device that probes a generic DAI
ASoC driver. Driver data structure should be used to provide DMA config
and IIO device phandles. Then DAI drivers could use your "hw_consummer"
interface to control iio device...

> 
> - Lars
> 
> [1]
> https://lists.linuxfoundation.org/pipermail/ksummit-discuss/2016-July/003029.html
> 
> [2]
> https://github.com/analogdevicesinc/linux/blob/xcomm_zynq/drivers/iio/buffer/hw_consumer.c
> 

Regards
Arnaud

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-31 15:30                               ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-31 15:30 UTC (permalink / raw)
  To: Lars-Peter Clausen, Jonathan Cameron, Lee Jones
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, Rob Herring,
	Mark Rutland, Hartmut Knaack, Peter Meerwald-Stadler,
	Jaroslav Kysela, Takashi Iwai, Liam Girdwood, Mark Brown,
	Maxime Coquelin, Alexandre TORGUE

Hello Lars,

On 01/29/2017 03:19 PM, Lars-Peter Clausen wrote:
> On 01/29/2017 01:28 PM, Jonathan Cameron wrote:
> [...]
>>>> Jonathan, Mark, Please could you share your opinion on this topic?
>> Hmm - based on a fairly quick read through of the code (which is never
>> ideal!). I can see that the ideal would indeed be as Lee says, to
>> expand the IIO interfaces sufficiently to support what you need.
>>
>>
>> So, reading the code (fairly quickly I'm afraid as had a lot of reviews
>> to catch up on this weekend).
>> What we need:
>> 1) DMA support in the ADC driver.  This would be a good anyway!
>> 2) DMA consumer support - I defer to Lars for comments on this.
>> 3) Means of describing and controlling the sinc filters applied. 
>> 4) Appropriate channel support.  I'm not convinced that it doesn't make
>> sense to have IIO channels for the microphones - at least in a streaming
>> mode.  It's data - I don't really care what ;)
>> Coarsely it's a filtered pulse per period counter which is
>> a perfectly valid type to have a channel for.
>>
>> The big question to my mind is the DMA consumer support. How would
>> it work. It it wouldn't this is somewhat of a non starter.
>>
>> To bring up another slightly ugly MFD case where it is borderline
>> on whether an MFD makes sense (just as a reference point of something
>> we have discussed a few times before)
>>
>> ADCs with features directed at touchscreen support.
>> These are odd as the ADC bit is generic, but the specific output
>> and read sequences used for touchscreen reading don't correspond to
>> anything that makes any real sense for other applications.
>>
>> We have started to get hybrid drives that have an MFD underneath but
>> do the ADC reads through IIO consumer interfaces, and the timing
>> control from a touchscreen driver.  We haven't really gotten this
>> one right yet either.
>>
>> Here however, to my mind things are different - as I read it
>> (and feel free to point out what I'm missing), the sound usecase
>> is just a question of setting up sampling frequencies and filters
>> appropriate to the microphones and what ASoC expects?
>>
>> That's not to say the IIO dma stuff is flexible enough (yet) to
>> handle the data flows, but perhaps we can work towards that.
> 
> Yeah, so this is a bit different, but not unexpected. And I'm sure we'll see
> more similar hardware in the future. I've talked about this before[1], the
> cost structure of creating and manufacturing new hardware drives the design
> in a certain direction so that we end up with general purpose hardware that
> suddenly has applications in multiple frameworks that were previously fully
> orthogonal.
> 
> This device is certainly not a multi-function-device. It only has one
> function, it's a sigma-delta demodulator. It is rather a
> multi-purpose-device. It can be used for sigma-delta demodulation in audio
> applications as well as more specialized data capture applications.
> 
> It's comparable to something like a GPIO that can be used to control a reset
> pin or turn on and off a LED. The GPIO chip is not considered
> multi-function-device though, even though it can be used for many different
> applications.
> 
> As for DMA we already have a lot of DMA infrastructure on the audio side and
> we probably want to reuse that rather than inserting IIO as a middle layer
> since audio buffer capture as different requirements from IIO buffer and
> we'd have to go the route of the least common denominator and loose
> expressibility in the process.

I'm agree with your analysis.
Audio DMA engine seems a more secure solution to avoid audio runtime
issue, and should minimize impact in IIO.

> 
> I've created a IIO buffer[2] that does not capture data to memory but is
> only used to enable/disable the data capture process. We use this in setups
> where the data is passed from the converter to a application specific
> processing chain without ever going through system memory. This buffer could
> probably also be used here on the audio side to control the converter state.

This remind me HDMI.
For HDMI, solution integrated is a drm driver that probes
"hdmi-audio-codec" driver.

Something similar could be done. An IIO device that probes a generic DAI
ASoC driver. Driver data structure should be used to provide DMA config
and IIO device phandles. Then DAI drivers could use your "hw_consummer"
interface to control iio device...

> 
> - Lars
> 
> [1]
> https://lists.linuxfoundation.org/pipermail/ksummit-discuss/2016-July/003029.html
> 
> [2]
> https://github.com/analogdevicesinc/linux/blob/xcomm_zynq/drivers/iio/buffer/hw_consumer.c
> 

Regards
Arnaud



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

* [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-01-31 15:30                               ` Arnaud Pouliquen
  0 siblings, 0 replies; 130+ messages in thread
From: Arnaud Pouliquen @ 2017-01-31 15:30 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Lars,

On 01/29/2017 03:19 PM, Lars-Peter Clausen wrote:
> On 01/29/2017 01:28 PM, Jonathan Cameron wrote:
> [...]
>>>> Jonathan, Mark, Please could you share your opinion on this topic?
>> Hmm - based on a fairly quick read through of the code (which is never
>> ideal!). I can see that the ideal would indeed be as Lee says, to
>> expand the IIO interfaces sufficiently to support what you need.
>>
>>
>> So, reading the code (fairly quickly I'm afraid as had a lot of reviews
>> to catch up on this weekend).
>> What we need:
>> 1) DMA support in the ADC driver.  This would be a good anyway!
>> 2) DMA consumer support - I defer to Lars for comments on this.
>> 3) Means of describing and controlling the sinc filters applied. 
>> 4) Appropriate channel support.  I'm not convinced that it doesn't make
>> sense to have IIO channels for the microphones - at least in a streaming
>> mode.  It's data - I don't really care what ;)
>> Coarsely it's a filtered pulse per period counter which is
>> a perfectly valid type to have a channel for.
>>
>> The big question to my mind is the DMA consumer support. How would
>> it work. It it wouldn't this is somewhat of a non starter.
>>
>> To bring up another slightly ugly MFD case where it is borderline
>> on whether an MFD makes sense (just as a reference point of something
>> we have discussed a few times before)
>>
>> ADCs with features directed at touchscreen support.
>> These are odd as the ADC bit is generic, but the specific output
>> and read sequences used for touchscreen reading don't correspond to
>> anything that makes any real sense for other applications.
>>
>> We have started to get hybrid drives that have an MFD underneath but
>> do the ADC reads through IIO consumer interfaces, and the timing
>> control from a touchscreen driver.  We haven't really gotten this
>> one right yet either.
>>
>> Here however, to my mind things are different - as I read it
>> (and feel free to point out what I'm missing), the sound usecase
>> is just a question of setting up sampling frequencies and filters
>> appropriate to the microphones and what ASoC expects?
>>
>> That's not to say the IIO dma stuff is flexible enough (yet) to
>> handle the data flows, but perhaps we can work towards that.
> 
> Yeah, so this is a bit different, but not unexpected. And I'm sure we'll see
> more similar hardware in the future. I've talked about this before[1], the
> cost structure of creating and manufacturing new hardware drives the design
> in a certain direction so that we end up with general purpose hardware that
> suddenly has applications in multiple frameworks that were previously fully
> orthogonal.
> 
> This device is certainly not a multi-function-device. It only has one
> function, it's a sigma-delta demodulator. It is rather a
> multi-purpose-device. It can be used for sigma-delta demodulation in audio
> applications as well as more specialized data capture applications.
> 
> It's comparable to something like a GPIO that can be used to control a reset
> pin or turn on and off a LED. The GPIO chip is not considered
> multi-function-device though, even though it can be used for many different
> applications.
> 
> As for DMA we already have a lot of DMA infrastructure on the audio side and
> we probably want to reuse that rather than inserting IIO as a middle layer
> since audio buffer capture as different requirements from IIO buffer and
> we'd have to go the route of the least common denominator and loose
> expressibility in the process.

I'm agree with your analysis.
Audio DMA engine seems a more secure solution to avoid audio runtime
issue, and should minimize impact in IIO.

> 
> I've created a IIO buffer[2] that does not capture data to memory but is
> only used to enable/disable the data capture process. We use this in setups
> where the data is passed from the converter to a application specific
> processing chain without ever going through system memory. This buffer could
> probably also be used here on the audio side to control the converter state.

This remind me HDMI.
For HDMI, solution integrated is a drm driver that probes
"hdmi-audio-codec" driver.

Something similar could be done. An IIO device that probes a generic DAI
ASoC driver. Driver data structure should be used to provide DMA config
and IIO device phandles. Then DAI drivers could use your "hw_consummer"
interface to control iio device...

> 
> - Lars
> 
> [1]
> https://lists.linuxfoundation.org/pipermail/ksummit-discuss/2016-July/003029.html
> 
> [2]
> https://github.com/analogdevicesinc/linux/blob/xcomm_zynq/drivers/iio/buffer/hw_consumer.c
> 

Regards
Arnaud

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

* Re: [PATCH 3/7] IIO: add bindings for STM32 DFSDM ADC driver
  2017-01-31 14:10           ` Arnaud Pouliquen
  (?)
@ 2017-02-04 12:09               ` Jonathan Cameron
  -1 siblings, 0 replies; 130+ messages in thread
From: Jonathan Cameron @ 2017-02-04 12:09 UTC (permalink / raw)
  To: Arnaud Pouliquen, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Lee Jones, Rob Herring,
	Mark Rutland, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Jaroslav Kysela, Takashi Iwai,
	Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre TORGUE

On 31/01/17 14:10, Arnaud Pouliquen wrote:
> Hi Jonathan,
> 
> On 01/29/2017 01:42 PM, Jonathan Cameron wrote:
>> On 29/01/17 11:58, Jonathan Cameron wrote:
>>> On 23/01/17 16:32, Arnaud Pouliquen wrote:
>>>> This patch adds documentation of device tree bindings for the
>>>> STM32 DFSDM ADC.
>>>>
>>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
>>> Trivial bits inline.
>>>> ---
>>>>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 60 ++++++++++++++++++++++
>>>>  1 file changed, 60 insertions(+)
>>>>  create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt  b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
>>>> new file mode 100644
>>>> index 0000000..c156bcb
>>>> --- /dev/null
>>>> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 	
>>>> @@ -0,0 +1,60 @@
>>>> +STMicroelectronics STM32 DFSDM ADC device driver
>>>> +
>>>> +STM32 DFSDM ADC is a sigma delta analog-to-digital converter.
>>>> +It has to be declared in device-tree as a subnode of the DFSDM mfd node.
>>>> +
>>>> +It has several multiplexed input channels. Conversions can be performed
>>>> +in single, scan or discontinuous mode. Conversions can be launched in software
>>>> +or using hardware triggers.
>>>> +Each instance of the driver uses one filter instance handle by the DFSDM mfd
>>>> +driver.
>>>> +
>>>> +DFSDM also offers extra features:
>>>> +-The analog watchdog feature allows the application to detect if the
>>>> + input voltage goes beyond the user-defined, higher or lower thresholds.
>>>> +-The short circuit detection allows allows the application to detect if the
>>>> + input is in CC.
>>>> +-The clock absence detection allows application to detect if SPI input is clocked.
>>>> +
>>>> +Required properties:
>>>> +- compatible:	Must be "st,stm32-dfsdm-adc".
>>>> +- reg:		Specifies the DFSDM filter instance.
>>>> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
>>>> +- st,adc-channels:	List of single-ended channels muxed for this ADC.
>>>> +- st,adc-channel-names:	List of single-ended channels Name.
>>>> +
>>>> +Optional properties:
>>>> +- st,adc-channel-types:	Single-ended channel input type. Default value is 0.
>>>> +			- 0: SPI with data on rising edge (default)
>>>> +			- 1: SPI with data on falling edge
>>> This is an element that can be described without magic numbers so I'd
>>> prefer that you do so.
>>>
>>> Also spell check edage -> edge.
>>>
>>>> +			- 2: manchester codec, rising edage = logic 0
>>>> +			- 3: manchester codec, rising edage = logic 1
>>>> +- st,adc-channel-clk-src: Conversion clock source. default value is 1.
>>>> +			  - 0: External SPI clocl (CLKIN x)
>>>> +			  - 1: internal SPI clock (CLKOUT) (default)
>>>> +			  - 2: internal SPI clock divided by 2 (falling edge).
>>>> +			  - 2: internal SPI clock divided by 2 (rising edge).
>>> 3?
>>>> +- st,adc-alt-channel: 	  must be defined if Two ADCs are connected on same SPI
>>>> +			  input.
>>>> +			  If not set channel n is connected to SPI input n.
>>>> +			  If set channel n is connected to SPI input n + 1.
>>> ? Two data inputs with shared clock?
>>>> +
>>>> +Example:
>>>> +	dfsdm: dfsdm@4400D000 {
>> I'd like to actually see the ADCs on the end of these explicitly
>> described as well.  It's entirely possible we'll have something
>> that isn't simply a dumb sigma delta modulator that will need
>> it's own driver.
>>
>> In some senses what we actually have here is a data bus so we
>> should be able to describe things on it in that fashion
>> rather than just describing the data that is in coming.
>> Our boundary of knowledge isn't at the edge of this device!
>>
>> A quick example google fed me would be the gain control on a
>> maxim ds8102.
>>
>> Tricky bit here will be defining this nice and generally when we
>> currently only have the one piece of hardware to do it with ;)
>>
>> That's what makes kernel development fun!
>>
> 
> What you describe here seems similar to audio codecs device defined in
> ASoC. An Audio codec is an external chip that is connected to SoC
> through an audio bus.
> ASoC allows to bind this codec with the SOC DAI (digital Audio Interface).
Yes, that was exactly what I was thinking of.  Was a while back but
I once mainlined a codec so had this at the back of my mind.
> 
> FYI we should have this kind of feature to support on STM32 uin second step:
> DSFDM input can also process stm32 ADC output through an hardware pipe
> (stm32-adc IIO driver that has been recently integrated in kernel).
> Internally, to pipe the ADC with DFSDM, we use an extension of inkern
> interface that is very similar with the one mentioned by Lars:
> 
> https://github.com/analogdevicesinc/linux/blob/xcomm_zynq/drivers
> /iio/buffer/hw_consumer.c
> 
> Don't know if this is what Lars has in mind... but a solution could be
> to define a new type of IIO that would be something like
> "postprocessing" or "filter". This new iio device type could be piped to
> a frontend IIO device to export a new one (through hw_consumer.c interface).
> In this case for simple device like PDM audio microphone that does not
> required control a kind of dummy frontend device could be defined...
That could work.  We already have stacked IIO devices - only difference
here is that we can't actually see the data flowing between the levels
of stack (or pipeline).

J
> 
> Regards
> Arnaud
> 
>>>> +		iio_dfsdm0: iio-dfsdm@0 {
>>>> +			compatible = "st,stm32-dfsdm-adc";
>>>> +			#io-channel-cells = <1>;
>>>> +			reg = <0>;
>>>> +			st,adc-channels = <1>;
>>>> +			st,adc-channel-names = "in0";
>>>> +		};
>>>> +		iio_dfsdm1: iio-dfsdm@1 {
>>>> +			compatible = "st,stm32-dfsdm-adc";
>>>> +			#io-channel-cells = <1>;
>>>> +			reg = <1>;
>>>> +			st,adc-channels = <1>;
>>>> +			st,adc-channel-names = "in1";
>>>> +			st,adc-channel-types = <1>;
>>>> +			st,adc-alt-channel = <1>;
>>>> +		};
>>>> +	};
>>>>
>>>
>>> --
>>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>>> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>>
>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" 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] 130+ messages in thread

* Re: [PATCH 3/7] IIO: add bindings for STM32 DFSDM ADC driver
@ 2017-02-04 12:09               ` Jonathan Cameron
  0 siblings, 0 replies; 130+ messages in thread
From: Jonathan Cameron @ 2017-02-04 12:09 UTC (permalink / raw)
  To: Arnaud Pouliquen, devicetree, linux-arm-kernel, linux-iio,
	alsa-devel, Lee Jones, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre TORGUE

On 31/01/17 14:10, Arnaud Pouliquen wrote:
> Hi Jonathan,
> 
> On 01/29/2017 01:42 PM, Jonathan Cameron wrote:
>> On 29/01/17 11:58, Jonathan Cameron wrote:
>>> On 23/01/17 16:32, Arnaud Pouliquen wrote:
>>>> This patch adds documentation of device tree bindings for the
>>>> STM32 DFSDM ADC.
>>>>
>>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>>> Trivial bits inline.
>>>> ---
>>>>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 60 ++++++++++++++++++++++
>>>>  1 file changed, 60 insertions(+)
>>>>  create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt  b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
>>>> new file mode 100644
>>>> index 0000000..c156bcb
>>>> --- /dev/null
>>>> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 	
>>>> @@ -0,0 +1,60 @@
>>>> +STMicroelectronics STM32 DFSDM ADC device driver
>>>> +
>>>> +STM32 DFSDM ADC is a sigma delta analog-to-digital converter.
>>>> +It has to be declared in device-tree as a subnode of the DFSDM mfd node.
>>>> +
>>>> +It has several multiplexed input channels. Conversions can be performed
>>>> +in single, scan or discontinuous mode. Conversions can be launched in software
>>>> +or using hardware triggers.
>>>> +Each instance of the driver uses one filter instance handle by the DFSDM mfd
>>>> +driver.
>>>> +
>>>> +DFSDM also offers extra features:
>>>> +-The analog watchdog feature allows the application to detect if the
>>>> + input voltage goes beyond the user-defined, higher or lower thresholds.
>>>> +-The short circuit detection allows allows the application to detect if the
>>>> + input is in CC.
>>>> +-The clock absence detection allows application to detect if SPI input is clocked.
>>>> +
>>>> +Required properties:
>>>> +- compatible:	Must be "st,stm32-dfsdm-adc".
>>>> +- reg:		Specifies the DFSDM filter instance.
>>>> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
>>>> +- st,adc-channels:	List of single-ended channels muxed for this ADC.
>>>> +- st,adc-channel-names:	List of single-ended channels Name.
>>>> +
>>>> +Optional properties:
>>>> +- st,adc-channel-types:	Single-ended channel input type. Default value is 0.
>>>> +			- 0: SPI with data on rising edge (default)
>>>> +			- 1: SPI with data on falling edge
>>> This is an element that can be described without magic numbers so I'd
>>> prefer that you do so.
>>>
>>> Also spell check edage -> edge.
>>>
>>>> +			- 2: manchester codec, rising edage = logic 0
>>>> +			- 3: manchester codec, rising edage = logic 1
>>>> +- st,adc-channel-clk-src: Conversion clock source. default value is 1.
>>>> +			  - 0: External SPI clocl (CLKIN x)
>>>> +			  - 1: internal SPI clock (CLKOUT) (default)
>>>> +			  - 2: internal SPI clock divided by 2 (falling edge).
>>>> +			  - 2: internal SPI clock divided by 2 (rising edge).
>>> 3?
>>>> +- st,adc-alt-channel: 	  must be defined if Two ADCs are connected on same SPI
>>>> +			  input.
>>>> +			  If not set channel n is connected to SPI input n.
>>>> +			  If set channel n is connected to SPI input n + 1.
>>> ? Two data inputs with shared clock?
>>>> +
>>>> +Example:
>>>> +	dfsdm: dfsdm@4400D000 {
>> I'd like to actually see the ADCs on the end of these explicitly
>> described as well.  It's entirely possible we'll have something
>> that isn't simply a dumb sigma delta modulator that will need
>> it's own driver.
>>
>> In some senses what we actually have here is a data bus so we
>> should be able to describe things on it in that fashion
>> rather than just describing the data that is in coming.
>> Our boundary of knowledge isn't at the edge of this device!
>>
>> A quick example google fed me would be the gain control on a
>> maxim ds8102.
>>
>> Tricky bit here will be defining this nice and generally when we
>> currently only have the one piece of hardware to do it with ;)
>>
>> That's what makes kernel development fun!
>>
> 
> What you describe here seems similar to audio codecs device defined in
> ASoC. An Audio codec is an external chip that is connected to SoC
> through an audio bus.
> ASoC allows to bind this codec with the SOC DAI (digital Audio Interface).
Yes, that was exactly what I was thinking of.  Was a while back but
I once mainlined a codec so had this at the back of my mind.
> 
> FYI we should have this kind of feature to support on STM32 uin second step:
> DSFDM input can also process stm32 ADC output through an hardware pipe
> (stm32-adc IIO driver that has been recently integrated in kernel).
> Internally, to pipe the ADC with DFSDM, we use an extension of inkern
> interface that is very similar with the one mentioned by Lars:
> 
> https://github.com/analogdevicesinc/linux/blob/xcomm_zynq/drivers
> /iio/buffer/hw_consumer.c
> 
> Don't know if this is what Lars has in mind... but a solution could be
> to define a new type of IIO that would be something like
> "postprocessing" or "filter". This new iio device type could be piped to
> a frontend IIO device to export a new one (through hw_consumer.c interface).
> In this case for simple device like PDM audio microphone that does not
> required control a kind of dummy frontend device could be defined...
That could work.  We already have stacked IIO devices - only difference
here is that we can't actually see the data flowing between the levels
of stack (or pipeline).

J
> 
> Regards
> Arnaud
> 
>>>> +		iio_dfsdm0: iio-dfsdm@0 {
>>>> +			compatible = "st,stm32-dfsdm-adc";
>>>> +			#io-channel-cells = <1>;
>>>> +			reg = <0>;
>>>> +			st,adc-channels = <1>;
>>>> +			st,adc-channel-names = "in0";
>>>> +		};
>>>> +		iio_dfsdm1: iio-dfsdm@1 {
>>>> +			compatible = "st,stm32-dfsdm-adc";
>>>> +			#io-channel-cells = <1>;
>>>> +			reg = <1>;
>>>> +			st,adc-channels = <1>;
>>>> +			st,adc-channel-names = "in1";
>>>> +			st,adc-channel-types = <1>;
>>>> +			st,adc-alt-channel = <1>;
>>>> +		};
>>>> +	};
>>>>
>>>
>>> --
>>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>>> the body of a message to majordomo@vger.kernel.org
>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>>
>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 


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

* [PATCH 3/7] IIO: add bindings for STM32 DFSDM ADC driver
@ 2017-02-04 12:09               ` Jonathan Cameron
  0 siblings, 0 replies; 130+ messages in thread
From: Jonathan Cameron @ 2017-02-04 12:09 UTC (permalink / raw)
  To: linux-arm-kernel

On 31/01/17 14:10, Arnaud Pouliquen wrote:
> Hi Jonathan,
> 
> On 01/29/2017 01:42 PM, Jonathan Cameron wrote:
>> On 29/01/17 11:58, Jonathan Cameron wrote:
>>> On 23/01/17 16:32, Arnaud Pouliquen wrote:
>>>> This patch adds documentation of device tree bindings for the
>>>> STM32 DFSDM ADC.
>>>>
>>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>>> Trivial bits inline.
>>>> ---
>>>>  .../bindings/iio/adc/st,stm32-dfsdm-adc.txt        | 60 ++++++++++++++++++++++
>>>>  1 file changed, 60 insertions(+)
>>>>  create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt  b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 
>>>> new file mode 100644
>>>> index 0000000..c156bcb
>>>> --- /dev/null
>>>> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-dfsdm-adc.txt 	
>>>> @@ -0,0 +1,60 @@
>>>> +STMicroelectronics STM32 DFSDM ADC device driver
>>>> +
>>>> +STM32 DFSDM ADC is a sigma delta analog-to-digital converter.
>>>> +It has to be declared in device-tree as a subnode of the DFSDM mfd node.
>>>> +
>>>> +It has several multiplexed input channels. Conversions can be performed
>>>> +in single, scan or discontinuous mode. Conversions can be launched in software
>>>> +or using hardware triggers.
>>>> +Each instance of the driver uses one filter instance handle by the DFSDM mfd
>>>> +driver.
>>>> +
>>>> +DFSDM also offers extra features:
>>>> +-The analog watchdog feature allows the application to detect if the
>>>> + input voltage goes beyond the user-defined, higher or lower thresholds.
>>>> +-The short circuit detection allows allows the application to detect if the
>>>> + input is in CC.
>>>> +-The clock absence detection allows application to detect if SPI input is clocked.
>>>> +
>>>> +Required properties:
>>>> +- compatible:	Must be "st,stm32-dfsdm-adc".
>>>> +- reg:		Specifies the DFSDM filter instance.
>>>> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers".
>>>> +- st,adc-channels:	List of single-ended channels muxed for this ADC.
>>>> +- st,adc-channel-names:	List of single-ended channels Name.
>>>> +
>>>> +Optional properties:
>>>> +- st,adc-channel-types:	Single-ended channel input type. Default value is 0.
>>>> +			- 0: SPI with data on rising edge (default)
>>>> +			- 1: SPI with data on falling edge
>>> This is an element that can be described without magic numbers so I'd
>>> prefer that you do so.
>>>
>>> Also spell check edage -> edge.
>>>
>>>> +			- 2: manchester codec, rising edage = logic 0
>>>> +			- 3: manchester codec, rising edage = logic 1
>>>> +- st,adc-channel-clk-src: Conversion clock source. default value is 1.
>>>> +			  - 0: External SPI clocl (CLKIN x)
>>>> +			  - 1: internal SPI clock (CLKOUT) (default)
>>>> +			  - 2: internal SPI clock divided by 2 (falling edge).
>>>> +			  - 2: internal SPI clock divided by 2 (rising edge).
>>> 3?
>>>> +- st,adc-alt-channel: 	  must be defined if Two ADCs are connected on same SPI
>>>> +			  input.
>>>> +			  If not set channel n is connected to SPI input n.
>>>> +			  If set channel n is connected to SPI input n + 1.
>>> ? Two data inputs with shared clock?
>>>> +
>>>> +Example:
>>>> +	dfsdm: dfsdm at 4400D000 {
>> I'd like to actually see the ADCs on the end of these explicitly
>> described as well.  It's entirely possible we'll have something
>> that isn't simply a dumb sigma delta modulator that will need
>> it's own driver.
>>
>> In some senses what we actually have here is a data bus so we
>> should be able to describe things on it in that fashion
>> rather than just describing the data that is in coming.
>> Our boundary of knowledge isn't at the edge of this device!
>>
>> A quick example google fed me would be the gain control on a
>> maxim ds8102.
>>
>> Tricky bit here will be defining this nice and generally when we
>> currently only have the one piece of hardware to do it with ;)
>>
>> That's what makes kernel development fun!
>>
> 
> What you describe here seems similar to audio codecs device defined in
> ASoC. An Audio codec is an external chip that is connected to SoC
> through an audio bus.
> ASoC allows to bind this codec with the SOC DAI (digital Audio Interface).
Yes, that was exactly what I was thinking of.  Was a while back but
I once mainlined a codec so had this at the back of my mind.
> 
> FYI we should have this kind of feature to support on STM32 uin second step:
> DSFDM input can also process stm32 ADC output through an hardware pipe
> (stm32-adc IIO driver that has been recently integrated in kernel).
> Internally, to pipe the ADC with DFSDM, we use an extension of inkern
> interface that is very similar with the one mentioned by Lars:
> 
> https://github.com/analogdevicesinc/linux/blob/xcomm_zynq/drivers
> /iio/buffer/hw_consumer.c
> 
> Don't know if this is what Lars has in mind... but a solution could be
> to define a new type of IIO that would be something like
> "postprocessing" or "filter". This new iio device type could be piped to
> a frontend IIO device to export a new one (through hw_consumer.c interface).
> In this case for simple device like PDM audio microphone that does not
> required control a kind of dummy frontend device could be defined...
That could work.  We already have stacked IIO devices - only difference
here is that we can't actually see the data flowing between the levels
of stack (or pipeline).

J
> 
> Regards
> Arnaud
> 
>>>> +		iio_dfsdm0: iio-dfsdm at 0 {
>>>> +			compatible = "st,stm32-dfsdm-adc";
>>>> +			#io-channel-cells = <1>;
>>>> +			reg = <0>;
>>>> +			st,adc-channels = <1>;
>>>> +			st,adc-channel-names = "in0";
>>>> +		};
>>>> +		iio_dfsdm1: iio-dfsdm at 1 {
>>>> +			compatible = "st,stm32-dfsdm-adc";
>>>> +			#io-channel-cells = <1>;
>>>> +			reg = <1>;
>>>> +			st,adc-channels = <1>;
>>>> +			st,adc-channel-names = "in1";
>>>> +			st,adc-channel-types = <1>;
>>>> +			st,adc-alt-channel = <1>;
>>>> +		};
>>>> +	};
>>>>
>>>
>>> --
>>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>>> the body of a message to majordomo at vger.kernel.org
>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>>
>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

* Re: [PATCH 6/7] ASoC: add bindings for STM32 DFSDM driver
  2017-01-30 17:32         ` Arnaud Pouliquen
  (?)
@ 2017-02-04 12:13             ` Jonathan Cameron
  -1 siblings, 0 replies; 130+ messages in thread
From: Jonathan Cameron @ 2017-02-04 12:13 UTC (permalink / raw)
  To: Arnaud Pouliquen, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Lee Jones, Rob Herring,
	Mark Rutland, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Jaroslav Kysela, Takashi Iwai,
	Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre TORGUE

On 30/01/17 17:32, Arnaud Pouliquen wrote:
> Hello Johan,
> 
> Please find my comments in-line.
> 
> Regards
> 
> Arnaud
> 
> On 01/29/2017 01:19 PM, Jonathan Cameron wrote:
>> On 23/01/17 16:32, Arnaud Pouliquen wrote:
>>> This patch adds documentation of device tree bindings for the
>>> STM32 DFSDM ASoC driver.
>>>
>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
>>> ---
>>>  .../devicetree/bindings/sound/st,sm32-adfsdm.txt   | 84 ++++++++++++++++++++++
>>>  1 file changed, 84 insertions(+)
>>>  create mode 100644 Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
>>>
>>> diff --git a/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt b/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
>>> new file mode 100644
>>> index 0000000..a1d27b8
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
>>> @@ -0,0 +1,84 @@
>>> +STMicroelectronics STM32 ADFSDM ASoC DAI device driver.
>>> +
>>> +STM32 ADFSDM ASoC is a sigma delta audio interface for digital microphone.
>>> +It has to be declared in device-tree as a subnode of the DFSDM mfd node.
>>> +
>>> +It offers possibility to record several mono microphones, with an option to
>>> +synchronize all microphones on a main one (that must be piped to filter 0)
>>> +Audio interface can be exposed through the generic ASoC simple card.
>>> +
>>> +2 Dmics can be connected on one SPI interface instance n.
>>> +Convention is that the DMIC that strobes data on rising edge is connected to the
>>> +corresponding DFSDM channel n; while the Dmic that strobes data on falling edge
>>> +is connected to the channel n-1. Simple card property "bitclock-inversion = <1>"
>>> +is used to specify that microphone strobes data on falling edge.
>>> +
>>> +SPI interface allows to be scheduled by an external SPI clock. To use it
>>> +simple card properties "bitclock-master = <&codec>" and "system-clock-frequency"
>>> +have to be defined in dai-link node.
>>> +
>>> +Required properties:
>>> +- compatible: 	Must be "st,stm32-dfsdm-audio",
>>> +- reg:		Specifies the DFSDM filter instance.
>>> +- interrupts: 	DFSDM filter instance interrupt line.
>>> +- dma:		DMA controller phandle and DMA request line associated to the
>>> +		filter instance ( specified by the field "reg")
>>> +- dma-names: 	must be "rx"
>>> +
>>> +- st,input-id:		Id of the SPI/Manchester interface used.
>>> +- st,dai-filter-order:  SinC filter order from 0 to 5.
>>> +			0: FastSinC
>>> +			[1-5]: order 1 to 5.
>>> +			For audio purpose it is recommended to use order 3 to 5.
>> Interesting for audio you consider it feature of the hardware, but for ADC you
>> consider that this should be userspace controlled.
>> Personally I'd like to see more detail on those filters but if this is convention
>> in Asoc then so be it.
> 
> Configure filter and integrator is quite tricky. Updating filter and
> integrator parameters have impact on sample resolution and output sample
> rate.
> Sample resolution is the peak to peak data value and depends on
> oversampling parameters and filter order.
> 
> Output sample rate depends on SPI input clock frequency oversampling
> parameters and filter order.
> 
> High filter order gives a better resolution and a better filter response
> but allows less dynamic to find the good rate...
> 
> So it is a compromise between resolution, rate and filtering.
> 
> For ADC use case, filter orders 1 to 3 are recommended.
> For audio use case, orders 3 to 5 are recommended.
I'm never against allowing users enough rope to hang themselves
as long as the defaults are sensible :)
> 
> On IIO side i proposed to expose tuning in ABI, to allows application
> fine tuning.
> Need to document it...
> 
> For ASOC it is another story. Application requests a rate and a sample
> format. So filter parameters are computed based on SPI bus clock
> frequency, expected audio sample rate and audio sample format.
> This is done in stm32_adfsdm_get_best_osr in sound/soc/stm/stm32_adfsdm.c.
If there is only ever one sensible answer, fair enough on not being
able to pick and choose.
> 
> Now your comment trig a good point. Perhaps a better option in IIO,
> could be to offer same kind of ABI than ASOC interface:
> - standard oversampling-ratio ABI. based on it filter and integrator
> oversampling ratios will be computed to maximize the resolution.
> - a read only resolution ABI:
> /sys/bus/iio/devices/iio:deviceX/in_adc_x_resolution. That give the
> computed resolution.
Would this be directly exposed in terms of the 'maximum value' that
could come out?  If so we could use the existing range description
callbacks (called _available though that's somewhat missleading in
this case).
> 
> In this case filter order will be part of the DT for IIO and ASoC.
> This would also match with a redesign of the DFSDM driver in IIO...
Sounds to me like it would be sensible to do restrict things somewhat
in the first instance, and perhaps rename them as 'defaults' in the
future if we decide that userspace control does make sense.
Basically do it the first time someone bashes us over the head with
a usecase we aren't supporting well.

> 
>>> +
>>> +Optional properties:
>>> + - st,dai0-synchronized: Set to 1 to synchronize DAI with DFSDM instance 0.
>>> +
>>> +Exemple of a card with 2 Dmics synchronized and connected on SPI interface 1.
>> Example.
>>> +
>>> +	dfsdm: dfsdm@4400D000 {
>>> +		dai_dfsdm0: dfsdm-audio@0 {
>>> +			compatible = "st,stm32-dfsdm-audio";
>>> +			#sound-dai-cells = <0>;
>>> +			reg = <0>;
>>> +			dmas = <&dmamux1 101 0x400 0x00>;
>>> +			dma-names = "rx";
>>> +			st,input-id = <0>;
>>> +			st,dai-filter-order = <5>;
>>> +		};
>>> +		dai_dfsdm1: dfsdm-audio@1 {
>>> +			compatible = "st,stm32-dfsdm-audio";
>>> +			#sound-dai-cells = <0>;
>>> +			reg = <0>;
>>> +			dmas = <&dmamux1 102 0x400 0x00>;
>>> +			dma-names = "rx";
>>> +			st,input-id = <0>;
>>> +			st,dai0-synchronized = <1>;
>>> +			st,dai-filter-order = <5>;
>>> +		};
>>> +	};
>>> +	sound_dfsdm_pdm {
>>> +		compatible = "simple-audio-card";
>>> +		simple-audio-card,name = "dfsdm_pdm";
>>> +		status = "okay";
>>> +
>>> +		dfsdm0_mic0: simple-audio-card,dai-link@0 {
>>> +			format = "pdm";
>>> +			cpu {
>>> +				sound-dai = <&dai_dfsdm0>;
>>> +			};
>>> +			dmic0_codec: codec {
>>> +				sound-dai = <&dmic0>;
>>> +			};
>>> +		};
>>> +		dfsdm0_mic1: simple-audio-card,dai-link@1 {
>>> +			format = "pdm";
>>> +			bitclock-inversion = <1>;
>>> +			cpu {
>>> +				sound-dai = <&dai_dfsdm1>;
>>> +			};
>>> +			codec {
>>> +				sound-dai = <&dmic1>;
>>> +			};
>>> +		};
>>> +	};
>>>
>> Jonathan
>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

--
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] 130+ messages in thread

* Re: [PATCH 6/7] ASoC: add bindings for STM32 DFSDM driver
@ 2017-02-04 12:13             ` Jonathan Cameron
  0 siblings, 0 replies; 130+ messages in thread
From: Jonathan Cameron @ 2017-02-04 12:13 UTC (permalink / raw)
  To: Arnaud Pouliquen, devicetree, linux-arm-kernel, linux-iio,
	alsa-devel, Lee Jones, Rob Herring, Mark Rutland, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown
  Cc: Maxime Coquelin, Alexandre TORGUE

On 30/01/17 17:32, Arnaud Pouliquen wrote:
> Hello Johan,
> 
> Please find my comments in-line.
> 
> Regards
> 
> Arnaud
> 
> On 01/29/2017 01:19 PM, Jonathan Cameron wrote:
>> On 23/01/17 16:32, Arnaud Pouliquen wrote:
>>> This patch adds documentation of device tree bindings for the
>>> STM32 DFSDM ASoC driver.
>>>
>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>>> ---
>>>  .../devicetree/bindings/sound/st,sm32-adfsdm.txt   | 84 ++++++++++++++++++++++
>>>  1 file changed, 84 insertions(+)
>>>  create mode 100644 Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
>>>
>>> diff --git a/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt b/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
>>> new file mode 100644
>>> index 0000000..a1d27b8
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
>>> @@ -0,0 +1,84 @@
>>> +STMicroelectronics STM32 ADFSDM ASoC DAI device driver.
>>> +
>>> +STM32 ADFSDM ASoC is a sigma delta audio interface for digital microphone.
>>> +It has to be declared in device-tree as a subnode of the DFSDM mfd node.
>>> +
>>> +It offers possibility to record several mono microphones, with an option to
>>> +synchronize all microphones on a main one (that must be piped to filter 0)
>>> +Audio interface can be exposed through the generic ASoC simple card.
>>> +
>>> +2 Dmics can be connected on one SPI interface instance n.
>>> +Convention is that the DMIC that strobes data on rising edge is connected to the
>>> +corresponding DFSDM channel n; while the Dmic that strobes data on falling edge
>>> +is connected to the channel n-1. Simple card property "bitclock-inversion = <1>"
>>> +is used to specify that microphone strobes data on falling edge.
>>> +
>>> +SPI interface allows to be scheduled by an external SPI clock. To use it
>>> +simple card properties "bitclock-master = <&codec>" and "system-clock-frequency"
>>> +have to be defined in dai-link node.
>>> +
>>> +Required properties:
>>> +- compatible: 	Must be "st,stm32-dfsdm-audio",
>>> +- reg:		Specifies the DFSDM filter instance.
>>> +- interrupts: 	DFSDM filter instance interrupt line.
>>> +- dma:		DMA controller phandle and DMA request line associated to the
>>> +		filter instance ( specified by the field "reg")
>>> +- dma-names: 	must be "rx"
>>> +
>>> +- st,input-id:		Id of the SPI/Manchester interface used.
>>> +- st,dai-filter-order:  SinC filter order from 0 to 5.
>>> +			0: FastSinC
>>> +			[1-5]: order 1 to 5.
>>> +			For audio purpose it is recommended to use order 3 to 5.
>> Interesting for audio you consider it feature of the hardware, but for ADC you
>> consider that this should be userspace controlled.
>> Personally I'd like to see more detail on those filters but if this is convention
>> in Asoc then so be it.
> 
> Configure filter and integrator is quite tricky. Updating filter and
> integrator parameters have impact on sample resolution and output sample
> rate.
> Sample resolution is the peak to peak data value and depends on
> oversampling parameters and filter order.
> 
> Output sample rate depends on SPI input clock frequency oversampling
> parameters and filter order.
> 
> High filter order gives a better resolution and a better filter response
> but allows less dynamic to find the good rate...
> 
> So it is a compromise between resolution, rate and filtering.
> 
> For ADC use case, filter orders 1 to 3 are recommended.
> For audio use case, orders 3 to 5 are recommended.
I'm never against allowing users enough rope to hang themselves
as long as the defaults are sensible :)
> 
> On IIO side i proposed to expose tuning in ABI, to allows application
> fine tuning.
> Need to document it...
> 
> For ASOC it is another story. Application requests a rate and a sample
> format. So filter parameters are computed based on SPI bus clock
> frequency, expected audio sample rate and audio sample format.
> This is done in stm32_adfsdm_get_best_osr in sound/soc/stm/stm32_adfsdm.c.
If there is only ever one sensible answer, fair enough on not being
able to pick and choose.
> 
> Now your comment trig a good point. Perhaps a better option in IIO,
> could be to offer same kind of ABI than ASOC interface:
> - standard oversampling-ratio ABI. based on it filter and integrator
> oversampling ratios will be computed to maximize the resolution.
> - a read only resolution ABI:
> /sys/bus/iio/devices/iio:deviceX/in_adc_x_resolution. That give the
> computed resolution.
Would this be directly exposed in terms of the 'maximum value' that
could come out?  If so we could use the existing range description
callbacks (called _available though that's somewhat missleading in
this case).
> 
> In this case filter order will be part of the DT for IIO and ASoC.
> This would also match with a redesign of the DFSDM driver in IIO...
Sounds to me like it would be sensible to do restrict things somewhat
in the first instance, and perhaps rename them as 'defaults' in the
future if we decide that userspace control does make sense.
Basically do it the first time someone bashes us over the head with
a usecase we aren't supporting well.

> 
>>> +
>>> +Optional properties:
>>> + - st,dai0-synchronized: Set to 1 to synchronize DAI with DFSDM instance 0.
>>> +
>>> +Exemple of a card with 2 Dmics synchronized and connected on SPI interface 1.
>> Example.
>>> +
>>> +	dfsdm: dfsdm@4400D000 {
>>> +		dai_dfsdm0: dfsdm-audio@0 {
>>> +			compatible = "st,stm32-dfsdm-audio";
>>> +			#sound-dai-cells = <0>;
>>> +			reg = <0>;
>>> +			dmas = <&dmamux1 101 0x400 0x00>;
>>> +			dma-names = "rx";
>>> +			st,input-id = <0>;
>>> +			st,dai-filter-order = <5>;
>>> +		};
>>> +		dai_dfsdm1: dfsdm-audio@1 {
>>> +			compatible = "st,stm32-dfsdm-audio";
>>> +			#sound-dai-cells = <0>;
>>> +			reg = <0>;
>>> +			dmas = <&dmamux1 102 0x400 0x00>;
>>> +			dma-names = "rx";
>>> +			st,input-id = <0>;
>>> +			st,dai0-synchronized = <1>;
>>> +			st,dai-filter-order = <5>;
>>> +		};
>>> +	};
>>> +	sound_dfsdm_pdm {
>>> +		compatible = "simple-audio-card";
>>> +		simple-audio-card,name = "dfsdm_pdm";
>>> +		status = "okay";
>>> +
>>> +		dfsdm0_mic0: simple-audio-card,dai-link@0 {
>>> +			format = "pdm";
>>> +			cpu {
>>> +				sound-dai = <&dai_dfsdm0>;
>>> +			};
>>> +			dmic0_codec: codec {
>>> +				sound-dai = <&dmic0>;
>>> +			};
>>> +		};
>>> +		dfsdm0_mic1: simple-audio-card,dai-link@1 {
>>> +			format = "pdm";
>>> +			bitclock-inversion = <1>;
>>> +			cpu {
>>> +				sound-dai = <&dai_dfsdm1>;
>>> +			};
>>> +			codec {
>>> +				sound-dai = <&dmic1>;
>>> +			};
>>> +		};
>>> +	};
>>>
>> Jonathan
>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 


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

* [PATCH 6/7] ASoC: add bindings for STM32 DFSDM driver
@ 2017-02-04 12:13             ` Jonathan Cameron
  0 siblings, 0 replies; 130+ messages in thread
From: Jonathan Cameron @ 2017-02-04 12:13 UTC (permalink / raw)
  To: linux-arm-kernel

On 30/01/17 17:32, Arnaud Pouliquen wrote:
> Hello Johan,
> 
> Please find my comments in-line.
> 
> Regards
> 
> Arnaud
> 
> On 01/29/2017 01:19 PM, Jonathan Cameron wrote:
>> On 23/01/17 16:32, Arnaud Pouliquen wrote:
>>> This patch adds documentation of device tree bindings for the
>>> STM32 DFSDM ASoC driver.
>>>
>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@st.com>
>>> ---
>>>  .../devicetree/bindings/sound/st,sm32-adfsdm.txt   | 84 ++++++++++++++++++++++
>>>  1 file changed, 84 insertions(+)
>>>  create mode 100644 Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
>>>
>>> diff --git a/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt b/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
>>> new file mode 100644
>>> index 0000000..a1d27b8
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/sound/st,sm32-adfsdm.txt
>>> @@ -0,0 +1,84 @@
>>> +STMicroelectronics STM32 ADFSDM ASoC DAI device driver.
>>> +
>>> +STM32 ADFSDM ASoC is a sigma delta audio interface for digital microphone.
>>> +It has to be declared in device-tree as a subnode of the DFSDM mfd node.
>>> +
>>> +It offers possibility to record several mono microphones, with an option to
>>> +synchronize all microphones on a main one (that must be piped to filter 0)
>>> +Audio interface can be exposed through the generic ASoC simple card.
>>> +
>>> +2 Dmics can be connected on one SPI interface instance n.
>>> +Convention is that the DMIC that strobes data on rising edge is connected to the
>>> +corresponding DFSDM channel n; while the Dmic that strobes data on falling edge
>>> +is connected to the channel n-1. Simple card property "bitclock-inversion = <1>"
>>> +is used to specify that microphone strobes data on falling edge.
>>> +
>>> +SPI interface allows to be scheduled by an external SPI clock. To use it
>>> +simple card properties "bitclock-master = <&codec>" and "system-clock-frequency"
>>> +have to be defined in dai-link node.
>>> +
>>> +Required properties:
>>> +- compatible: 	Must be "st,stm32-dfsdm-audio",
>>> +- reg:		Specifies the DFSDM filter instance.
>>> +- interrupts: 	DFSDM filter instance interrupt line.
>>> +- dma:		DMA controller phandle and DMA request line associated to the
>>> +		filter instance ( specified by the field "reg")
>>> +- dma-names: 	must be "rx"
>>> +
>>> +- st,input-id:		Id of the SPI/Manchester interface used.
>>> +- st,dai-filter-order:  SinC filter order from 0 to 5.
>>> +			0: FastSinC
>>> +			[1-5]: order 1 to 5.
>>> +			For audio purpose it is recommended to use order 3 to 5.
>> Interesting for audio you consider it feature of the hardware, but for ADC you
>> consider that this should be userspace controlled.
>> Personally I'd like to see more detail on those filters but if this is convention
>> in Asoc then so be it.
> 
> Configure filter and integrator is quite tricky. Updating filter and
> integrator parameters have impact on sample resolution and output sample
> rate.
> Sample resolution is the peak to peak data value and depends on
> oversampling parameters and filter order.
> 
> Output sample rate depends on SPI input clock frequency oversampling
> parameters and filter order.
> 
> High filter order gives a better resolution and a better filter response
> but allows less dynamic to find the good rate...
> 
> So it is a compromise between resolution, rate and filtering.
> 
> For ADC use case, filter orders 1 to 3 are recommended.
> For audio use case, orders 3 to 5 are recommended.
I'm never against allowing users enough rope to hang themselves
as long as the defaults are sensible :)
> 
> On IIO side i proposed to expose tuning in ABI, to allows application
> fine tuning.
> Need to document it...
> 
> For ASOC it is another story. Application requests a rate and a sample
> format. So filter parameters are computed based on SPI bus clock
> frequency, expected audio sample rate and audio sample format.
> This is done in stm32_adfsdm_get_best_osr in sound/soc/stm/stm32_adfsdm.c.
If there is only ever one sensible answer, fair enough on not being
able to pick and choose.
> 
> Now your comment trig a good point. Perhaps a better option in IIO,
> could be to offer same kind of ABI than ASOC interface:
> - standard oversampling-ratio ABI. based on it filter and integrator
> oversampling ratios will be computed to maximize the resolution.
> - a read only resolution ABI:
> /sys/bus/iio/devices/iio:deviceX/in_adc_x_resolution. That give the
> computed resolution.
Would this be directly exposed in terms of the 'maximum value' that
could come out?  If so we could use the existing range description
callbacks (called _available though that's somewhat missleading in
this case).
> 
> In this case filter order will be part of the DT for IIO and ASoC.
> This would also match with a redesign of the DFSDM driver in IIO...
Sounds to me like it would be sensible to do restrict things somewhat
in the first instance, and perhaps rename them as 'defaults' in the
future if we decide that userspace control does make sense.
Basically do it the first time someone bashes us over the head with
a usecase we aren't supporting well.

> 
>>> +
>>> +Optional properties:
>>> + - st,dai0-synchronized: Set to 1 to synchronize DAI with DFSDM instance 0.
>>> +
>>> +Exemple of a card with 2 Dmics synchronized and connected on SPI interface 1.
>> Example.
>>> +
>>> +	dfsdm: dfsdm at 4400D000 {
>>> +		dai_dfsdm0: dfsdm-audio at 0 {
>>> +			compatible = "st,stm32-dfsdm-audio";
>>> +			#sound-dai-cells = <0>;
>>> +			reg = <0>;
>>> +			dmas = <&dmamux1 101 0x400 0x00>;
>>> +			dma-names = "rx";
>>> +			st,input-id = <0>;
>>> +			st,dai-filter-order = <5>;
>>> +		};
>>> +		dai_dfsdm1: dfsdm-audio at 1 {
>>> +			compatible = "st,stm32-dfsdm-audio";
>>> +			#sound-dai-cells = <0>;
>>> +			reg = <0>;
>>> +			dmas = <&dmamux1 102 0x400 0x00>;
>>> +			dma-names = "rx";
>>> +			st,input-id = <0>;
>>> +			st,dai0-synchronized = <1>;
>>> +			st,dai-filter-order = <5>;
>>> +		};
>>> +	};
>>> +	sound_dfsdm_pdm {
>>> +		compatible = "simple-audio-card";
>>> +		simple-audio-card,name = "dfsdm_pdm";
>>> +		status = "okay";
>>> +
>>> +		dfsdm0_mic0: simple-audio-card,dai-link at 0 {
>>> +			format = "pdm";
>>> +			cpu {
>>> +				sound-dai = <&dai_dfsdm0>;
>>> +			};
>>> +			dmic0_codec: codec {
>>> +				sound-dai = <&dmic0>;
>>> +			};
>>> +		};
>>> +		dfsdm0_mic1: simple-audio-card,dai-link at 1 {
>>> +			format = "pdm";
>>> +			bitclock-inversion = <1>;
>>> +			cpu {
>>> +				sound-dai = <&dai_dfsdm1>;
>>> +			};
>>> +			codec {
>>> +				sound-dai = <&dmic1>;
>>> +			};
>>> +		};
>>> +	};
>>>
>> Jonathan
>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
  2017-01-30 11:13                                 ` Arnaud Pouliquen
  (?)
@ 2017-02-04 12:15                                     ` Jonathan Cameron
  -1 siblings, 0 replies; 130+ messages in thread
From: Jonathan Cameron @ 2017-02-04 12:15 UTC (permalink / raw)
  To: Arnaud Pouliquen, Lars-Peter Clausen, Lee Jones
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Rob Herring, Mark Rutland,
	Hartmut Knaack, Peter Meerwald-Stadler, Jaroslav Kysela,
	Takashi Iwai, Liam Girdwood, Mark Brown, Maxime Coquelin,
	Alexandre TORGUE

On 30/01/17 11:13, Arnaud Pouliquen wrote:
> Hello,
> 
> Thanks everyone for you feedback!
> My comments below.
> 
> On 01/29/2017 03:34 PM, Lars-Peter Clausen wrote:
>> On 01/29/2017 03:19 PM, Lars-Peter Clausen wrote:
>>> On 01/29/2017 01:28 PM, Jonathan Cameron wrote:
>>> [...]
>>>>>> Jonathan, Mark, Please could you share your opinion on this topic?
>>>> Hmm - based on a fairly quick read through of the code (which is never
>>>> ideal!). I can see that the ideal would indeed be as Lee says, to
>>>> expand the IIO interfaces sufficiently to support what you need.
>>>>
>>>>
>>>> So, reading the code (fairly quickly I'm afraid as had a lot of reviews
>>>> to catch up on this weekend).
>>>> What we need:
>>>> 1) DMA support in the ADC driver.  This would be a good anyway!
>>>> 2) DMA consumer support - I defer to Lars for comments on this.
>>>> 3) Means of describing and controlling the sinc filters applied. 
>>>> 4) Appropriate channel support.  I'm not convinced that it doesn't make
>>>> sense to have IIO channels for the microphones - at least in a streaming
>>>> mode.  It's data - I don't really care what ;)
>>>> Coarsely it's a filtered pulse per period counter which is
>>>> a perfectly valid type to have a channel for.
>>>>
>>>> The big question to my mind is the DMA consumer support. How would
>>>> it work. It it wouldn't this is somewhat of a non starter.
>>>>
>>>> To bring up another slightly ugly MFD case where it is borderline
>>>> on whether an MFD makes sense (just as a reference point of something
>>>> we have discussed a few times before)
>>>>
>>>> ADCs with features directed at touchscreen support.
>>>> These are odd as the ADC bit is generic, but the specific output
>>>> and read sequences used for touchscreen reading don't correspond to
>>>> anything that makes any real sense for other applications.
>>>>
>>>> We have started to get hybrid drives that have an MFD underneath but
>>>> do the ADC reads through IIO consumer interfaces, and the timing
>>>> control from a touchscreen driver.  We haven't really gotten this
>>>> one right yet either.
>>>>
>>>> Here however, to my mind things are different - as I read it
>>>> (and feel free to point out what I'm missing), the sound usecase
>>>> is just a question of setting up sampling frequencies and filters
>>>> appropriate to the microphones and what ASoC expects?
>>>>
>>>> That's not to say the IIO dma stuff is flexible enough (yet) to
>>>> handle the data flows, but perhaps we can work towards that.
>>>
>>> Yeah, so this is a bit different, but not unexpected. And I'm sure we'll see
>>> more similar hardware in the future. I've talked about this before[1], the
>>> cost structure of creating and manufacturing new hardware drives the design
>>> in a certain direction so that we end up with general purpose hardware that
>>> suddenly has applications in multiple frameworks that were previously fully
>>> orthogonal.
>>>
>>> This device is certainly not a multi-function-device. It only has one
>>> function, it's a sigma-delta demodulator. It is rather a
>>> multi-purpose-device. It can be used for sigma-delta demodulation in audio
>>> applications as well as more specialized data capture applications.
>>>
>>> It's comparable to something like a GPIO that can be used to control a reset
>>> pin or turn on and off a LED. The GPIO chip is not considered
>>> multi-function-device though, even though it can be used for many different
>>> applications.
>>>
>>> As for DMA we already have a lot of DMA infrastructure on the audio side and
>>> we probably want to reuse that rather than inserting IIO as a middle layer
>>> since audio buffer capture as different requirements from IIO buffer and
>>> we'd have to go the route of the least common denominator and loose
>>> expressibility in the process.
>>>
>>> I've created a IIO buffer[2] that does not capture data to memory but is
>>> only used to enable/disable the data capture process. We use this in setups
>>> where the data is passed from the converter to a application specific
>>> processing chain without ever going through system memory. This buffer could
>>> probably also be used here on the audio side to control the converter state.
>>
>> I forgot to mention. I think the first thing we should do is work on
>> terminology. This is not an ADC, this is a configurable low-pass-filter.
>>
>> It works in conjunction with a analog frontend (ADC) that produces a 1-bit
>> pulse-density-modulated stream, takes that stream and converts it into N-bit
>> PCM samples. The PCM samples are generated at a fraction of the PDM stream
>> samplerate that corresponds to the decimation factor.
>>
>> This is not an unusual device. Many audio CODEC and audio controllers
>> contain such a core as well as most SigmaDelta converters supported by IIO.
>> What is special about this part is that it is a dedicated core that is not
>> embedded in some other hardware component. This creates greater flexibility,
>> but of course also greater complexity that is required to manage all that
>> flexibility.
>>
>> We shouldn't codify anything about the kernel internal frameworks through
>> which the device might be exposed into the devicetree. We should accurately
>> describe the hardware (including the analog frontend) and then create a
>> appropriate software structures to handle them.	
>>
> 
> So if everyone is aligned, i will abandon the MFD driver and try to bind
> an ASoC driver on IIO interface. The "challenge" is to
> define appropriate relation ship between ASoC and IIO...
> 
> In a first step, a lot of question to answers and points to clarify... i
> will reply to associated mails.
> 
> Then I see two main topics to clarify:
> 	- DFSDM integration in IIO in a generic way. Lars, if i well interpret,
> your proposal should be to introduce front-end and filter notions in
> IIO, to support this kind of hardware?
> 	- DMA engine for audio purpose.
> I propose to come back with RFC for both subjects.
> 
> If you want to have more detail on DFSDM: DFSDM datasheet is included in
> STM32F413 datasheet available here:
> http://www.st.com/content/ccc/resource/technical/document/reference_manual/group0/81/ea/88/1f/97/9e/4a/d0/DM00305666/files/DM00305666.pdf/jcr:content/translations/en.DM00305666.pdf
> 
> DFSDM Block diagram p 384
Sounds like a sensible way forward to me.

Might well go through a few more rounds before we get this right, but
I agree with Lars that this looks to be something we are going to meet
more and more in the future so would be excellent to get it right!

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

--
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] 130+ messages in thread

* Re: [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-02-04 12:15                                     ` Jonathan Cameron
  0 siblings, 0 replies; 130+ messages in thread
From: Jonathan Cameron @ 2017-02-04 12:15 UTC (permalink / raw)
  To: Arnaud Pouliquen, Lars-Peter Clausen, Lee Jones
  Cc: devicetree, linux-arm-kernel, linux-iio, alsa-devel, Rob Herring,
	Mark Rutland, Hartmut Knaack, Peter Meerwald-Stadler,
	Jaroslav Kysela, Takashi Iwai, Liam Girdwood, Mark Brown,
	Maxime Coquelin, Alexandre TORGUE

On 30/01/17 11:13, Arnaud Pouliquen wrote:
> Hello,
> 
> Thanks everyone for you feedback!
> My comments below.
> 
> On 01/29/2017 03:34 PM, Lars-Peter Clausen wrote:
>> On 01/29/2017 03:19 PM, Lars-Peter Clausen wrote:
>>> On 01/29/2017 01:28 PM, Jonathan Cameron wrote:
>>> [...]
>>>>>> Jonathan, Mark, Please could you share your opinion on this topic?
>>>> Hmm - based on a fairly quick read through of the code (which is never
>>>> ideal!). I can see that the ideal would indeed be as Lee says, to
>>>> expand the IIO interfaces sufficiently to support what you need.
>>>>
>>>>
>>>> So, reading the code (fairly quickly I'm afraid as had a lot of reviews
>>>> to catch up on this weekend).
>>>> What we need:
>>>> 1) DMA support in the ADC driver.  This would be a good anyway!
>>>> 2) DMA consumer support - I defer to Lars for comments on this.
>>>> 3) Means of describing and controlling the sinc filters applied. 
>>>> 4) Appropriate channel support.  I'm not convinced that it doesn't make
>>>> sense to have IIO channels for the microphones - at least in a streaming
>>>> mode.  It's data - I don't really care what ;)
>>>> Coarsely it's a filtered pulse per period counter which is
>>>> a perfectly valid type to have a channel for.
>>>>
>>>> The big question to my mind is the DMA consumer support. How would
>>>> it work. It it wouldn't this is somewhat of a non starter.
>>>>
>>>> To bring up another slightly ugly MFD case where it is borderline
>>>> on whether an MFD makes sense (just as a reference point of something
>>>> we have discussed a few times before)
>>>>
>>>> ADCs with features directed at touchscreen support.
>>>> These are odd as the ADC bit is generic, but the specific output
>>>> and read sequences used for touchscreen reading don't correspond to
>>>> anything that makes any real sense for other applications.
>>>>
>>>> We have started to get hybrid drives that have an MFD underneath but
>>>> do the ADC reads through IIO consumer interfaces, and the timing
>>>> control from a touchscreen driver.  We haven't really gotten this
>>>> one right yet either.
>>>>
>>>> Here however, to my mind things are different - as I read it
>>>> (and feel free to point out what I'm missing), the sound usecase
>>>> is just a question of setting up sampling frequencies and filters
>>>> appropriate to the microphones and what ASoC expects?
>>>>
>>>> That's not to say the IIO dma stuff is flexible enough (yet) to
>>>> handle the data flows, but perhaps we can work towards that.
>>>
>>> Yeah, so this is a bit different, but not unexpected. And I'm sure we'll see
>>> more similar hardware in the future. I've talked about this before[1], the
>>> cost structure of creating and manufacturing new hardware drives the design
>>> in a certain direction so that we end up with general purpose hardware that
>>> suddenly has applications in multiple frameworks that were previously fully
>>> orthogonal.
>>>
>>> This device is certainly not a multi-function-device. It only has one
>>> function, it's a sigma-delta demodulator. It is rather a
>>> multi-purpose-device. It can be used for sigma-delta demodulation in audio
>>> applications as well as more specialized data capture applications.
>>>
>>> It's comparable to something like a GPIO that can be used to control a reset
>>> pin or turn on and off a LED. The GPIO chip is not considered
>>> multi-function-device though, even though it can be used for many different
>>> applications.
>>>
>>> As for DMA we already have a lot of DMA infrastructure on the audio side and
>>> we probably want to reuse that rather than inserting IIO as a middle layer
>>> since audio buffer capture as different requirements from IIO buffer and
>>> we'd have to go the route of the least common denominator and loose
>>> expressibility in the process.
>>>
>>> I've created a IIO buffer[2] that does not capture data to memory but is
>>> only used to enable/disable the data capture process. We use this in setups
>>> where the data is passed from the converter to a application specific
>>> processing chain without ever going through system memory. This buffer could
>>> probably also be used here on the audio side to control the converter state.
>>
>> I forgot to mention. I think the first thing we should do is work on
>> terminology. This is not an ADC, this is a configurable low-pass-filter.
>>
>> It works in conjunction with a analog frontend (ADC) that produces a 1-bit
>> pulse-density-modulated stream, takes that stream and converts it into N-bit
>> PCM samples. The PCM samples are generated at a fraction of the PDM stream
>> samplerate that corresponds to the decimation factor.
>>
>> This is not an unusual device. Many audio CODEC and audio controllers
>> contain such a core as well as most SigmaDelta converters supported by IIO.
>> What is special about this part is that it is a dedicated core that is not
>> embedded in some other hardware component. This creates greater flexibility,
>> but of course also greater complexity that is required to manage all that
>> flexibility.
>>
>> We shouldn't codify anything about the kernel internal frameworks through
>> which the device might be exposed into the devicetree. We should accurately
>> describe the hardware (including the analog frontend) and then create a
>> appropriate software structures to handle them.	
>>
> 
> So if everyone is aligned, i will abandon the MFD driver and try to bind
> an ASoC driver on IIO interface. The "challenge" is to
> define appropriate relation ship between ASoC and IIO...
> 
> In a first step, a lot of question to answers and points to clarify... i
> will reply to associated mails.
> 
> Then I see two main topics to clarify:
> 	- DFSDM integration in IIO in a generic way. Lars, if i well interpret,
> your proposal should be to introduce front-end and filter notions in
> IIO, to support this kind of hardware?
> 	- DMA engine for audio purpose.
> I propose to come back with RFC for both subjects.
> 
> If you want to have more detail on DFSDM: DFSDM datasheet is included in
> STM32F413 datasheet available here:
> http://www.st.com/content/ccc/resource/technical/document/reference_manual/group0/81/ea/88/1f/97/9e/4a/d0/DM00305666/files/DM00305666.pdf/jcr:content/translations/en.DM00305666.pdf
> 
> DFSDM Block diagram p 384
Sounds like a sensible way forward to me.

Might well go through a few more rounds before we get this right, but
I agree with Lars that this looks to be something we are going to meet
more and more in the future so would be excellent to get it right!

Jonathan
> 
> Regards
> Arnaud
> 
> 
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 


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

* [PATCH 2/7] MFD: add STM32 DFSDM support
@ 2017-02-04 12:15                                     ` Jonathan Cameron
  0 siblings, 0 replies; 130+ messages in thread
From: Jonathan Cameron @ 2017-02-04 12:15 UTC (permalink / raw)
  To: linux-arm-kernel

On 30/01/17 11:13, Arnaud Pouliquen wrote:
> Hello,
> 
> Thanks everyone for you feedback!
> My comments below.
> 
> On 01/29/2017 03:34 PM, Lars-Peter Clausen wrote:
>> On 01/29/2017 03:19 PM, Lars-Peter Clausen wrote:
>>> On 01/29/2017 01:28 PM, Jonathan Cameron wrote:
>>> [...]
>>>>>> Jonathan, Mark, Please could you share your opinion on this topic?
>>>> Hmm - based on a fairly quick read through of the code (which is never
>>>> ideal!). I can see that the ideal would indeed be as Lee says, to
>>>> expand the IIO interfaces sufficiently to support what you need.
>>>>
>>>>
>>>> So, reading the code (fairly quickly I'm afraid as had a lot of reviews
>>>> to catch up on this weekend).
>>>> What we need:
>>>> 1) DMA support in the ADC driver.  This would be a good anyway!
>>>> 2) DMA consumer support - I defer to Lars for comments on this.
>>>> 3) Means of describing and controlling the sinc filters applied. 
>>>> 4) Appropriate channel support.  I'm not convinced that it doesn't make
>>>> sense to have IIO channels for the microphones - at least in a streaming
>>>> mode.  It's data - I don't really care what ;)
>>>> Coarsely it's a filtered pulse per period counter which is
>>>> a perfectly valid type to have a channel for.
>>>>
>>>> The big question to my mind is the DMA consumer support. How would
>>>> it work. It it wouldn't this is somewhat of a non starter.
>>>>
>>>> To bring up another slightly ugly MFD case where it is borderline
>>>> on whether an MFD makes sense (just as a reference point of something
>>>> we have discussed a few times before)
>>>>
>>>> ADCs with features directed at touchscreen support.
>>>> These are odd as the ADC bit is generic, but the specific output
>>>> and read sequences used for touchscreen reading don't correspond to
>>>> anything that makes any real sense for other applications.
>>>>
>>>> We have started to get hybrid drives that have an MFD underneath but
>>>> do the ADC reads through IIO consumer interfaces, and the timing
>>>> control from a touchscreen driver.  We haven't really gotten this
>>>> one right yet either.
>>>>
>>>> Here however, to my mind things are different - as I read it
>>>> (and feel free to point out what I'm missing), the sound usecase
>>>> is just a question of setting up sampling frequencies and filters
>>>> appropriate to the microphones and what ASoC expects?
>>>>
>>>> That's not to say the IIO dma stuff is flexible enough (yet) to
>>>> handle the data flows, but perhaps we can work towards that.
>>>
>>> Yeah, so this is a bit different, but not unexpected. And I'm sure we'll see
>>> more similar hardware in the future. I've talked about this before[1], the
>>> cost structure of creating and manufacturing new hardware drives the design
>>> in a certain direction so that we end up with general purpose hardware that
>>> suddenly has applications in multiple frameworks that were previously fully
>>> orthogonal.
>>>
>>> This device is certainly not a multi-function-device. It only has one
>>> function, it's a sigma-delta demodulator. It is rather a
>>> multi-purpose-device. It can be used for sigma-delta demodulation in audio
>>> applications as well as more specialized data capture applications.
>>>
>>> It's comparable to something like a GPIO that can be used to control a reset
>>> pin or turn on and off a LED. The GPIO chip is not considered
>>> multi-function-device though, even though it can be used for many different
>>> applications.
>>>
>>> As for DMA we already have a lot of DMA infrastructure on the audio side and
>>> we probably want to reuse that rather than inserting IIO as a middle layer
>>> since audio buffer capture as different requirements from IIO buffer and
>>> we'd have to go the route of the least common denominator and loose
>>> expressibility in the process.
>>>
>>> I've created a IIO buffer[2] that does not capture data to memory but is
>>> only used to enable/disable the data capture process. We use this in setups
>>> where the data is passed from the converter to a application specific
>>> processing chain without ever going through system memory. This buffer could
>>> probably also be used here on the audio side to control the converter state.
>>
>> I forgot to mention. I think the first thing we should do is work on
>> terminology. This is not an ADC, this is a configurable low-pass-filter.
>>
>> It works in conjunction with a analog frontend (ADC) that produces a 1-bit
>> pulse-density-modulated stream, takes that stream and converts it into N-bit
>> PCM samples. The PCM samples are generated at a fraction of the PDM stream
>> samplerate that corresponds to the decimation factor.
>>
>> This is not an unusual device. Many audio CODEC and audio controllers
>> contain such a core as well as most SigmaDelta converters supported by IIO.
>> What is special about this part is that it is a dedicated core that is not
>> embedded in some other hardware component. This creates greater flexibility,
>> but of course also greater complexity that is required to manage all that
>> flexibility.
>>
>> We shouldn't codify anything about the kernel internal frameworks through
>> which the device might be exposed into the devicetree. We should accurately
>> describe the hardware (including the analog frontend) and then create a
>> appropriate software structures to handle them.	
>>
> 
> So if everyone is aligned, i will abandon the MFD driver and try to bind
> an ASoC driver on IIO interface. The "challenge" is to
> define appropriate relation ship between ASoC and IIO...
> 
> In a first step, a lot of question to answers and points to clarify... i
> will reply to associated mails.
> 
> Then I see two main topics to clarify:
> 	- DFSDM integration in IIO in a generic way. Lars, if i well interpret,
> your proposal should be to introduce front-end and filter notions in
> IIO, to support this kind of hardware?
> 	- DMA engine for audio purpose.
> I propose to come back with RFC for both subjects.
> 
> If you want to have more detail on DFSDM: DFSDM datasheet is included in
> STM32F413 datasheet available here:
> http://www.st.com/content/ccc/resource/technical/document/reference_manual/group0/81/ea/88/1f/97/9e/4a/d0/DM00305666/files/DM00305666.pdf/jcr:content/translations/en.DM00305666.pdf
> 
> DFSDM Block diagram p 384
Sounds like a sensible way forward to me.

Might well go through a few more rounds before we get this right, but
I agree with Lars that this looks to be something we are going to meet
more and more in the future so would be excellent to get it right!

Jonathan
> 
> Regards
> Arnaud
> 
> 
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

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

Thread overview: 130+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-01-23 16:32 [PATCH 0/7] Add STM32 DFSDM support Arnaud Pouliquen
2017-01-23 16:32 ` Arnaud Pouliquen
2017-01-23 16:32 ` Arnaud Pouliquen
2017-01-23 16:32 ` [PATCH 1/7] MFD: add bindings for STM32 DFSDM driver Arnaud Pouliquen
2017-01-23 16:32   ` Arnaud Pouliquen
2017-01-23 16:32   ` Arnaud Pouliquen
2017-01-27 20:53   ` Rob Herring
2017-01-27 20:53     ` Rob Herring
2017-01-27 20:53     ` Rob Herring
2017-01-30 13:16     ` Arnaud Pouliquen
2017-01-30 13:16       ` Arnaud Pouliquen
2017-01-30 13:16       ` Arnaud Pouliquen
     [not found] ` <1485189145-29576-1-git-send-email-arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
2017-01-23 16:32   ` [PATCH 2/7] MFD: add STM32 DFSDM support Arnaud Pouliquen
2017-01-23 16:32     ` Arnaud Pouliquen
2017-01-23 16:32     ` Arnaud Pouliquen
     [not found]     ` <1485189145-29576-3-git-send-email-arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
2017-01-24  0:36       ` [alsa-devel] " kbuild test robot
2017-01-24  0:36         ` kbuild test robot
2017-01-24  0:36         ` kbuild test robot
2017-01-24  8:22       ` Lee Jones
2017-01-24  8:22         ` Lee Jones
2017-01-24  8:22         ` Lee Jones
2017-01-24  8:30         ` Arnaud Pouliquen
2017-01-24  8:30           ` Arnaud Pouliquen
2017-01-24  8:30           ` Arnaud Pouliquen
     [not found]           ` <283c0e48-55e2-6516-369d-91dc72d12f73-qxv4g6HH51o@public.gmane.org>
2017-01-24 11:36             ` Lee Jones
2017-01-24 11:36               ` Lee Jones
2017-01-24 11:36               ` Lee Jones
2017-01-24 13:32               ` Arnaud Pouliquen
2017-01-24 13:32                 ` Arnaud Pouliquen
2017-01-24 13:32                 ` Arnaud Pouliquen
2017-01-27 10:15                 ` Lee Jones
2017-01-27 10:15                   ` Lee Jones
2017-01-27 10:15                   ` Lee Jones
2017-01-27 13:45                   ` Arnaud Pouliquen
2017-01-27 13:45                     ` Arnaud Pouliquen
2017-01-27 13:45                     ` Arnaud Pouliquen
     [not found]                     ` <7b3eb38d-4fd1-2006-7423-94f2a7b38b27-qxv4g6HH51o@public.gmane.org>
2017-01-27 17:17                       ` Lee Jones
2017-01-27 17:17                         ` Lee Jones
2017-01-27 17:17                         ` Lee Jones
2017-01-29 12:28                         ` Jonathan Cameron
2017-01-29 12:28                           ` Jonathan Cameron
2017-01-29 12:28                           ` Jonathan Cameron
2017-01-29 14:19                           ` Lars-Peter Clausen
2017-01-29 14:19                             ` Lars-Peter Clausen
2017-01-29 14:19                             ` Lars-Peter Clausen
2017-01-29 14:34                             ` Lars-Peter Clausen
2017-01-29 14:34                               ` Lars-Peter Clausen
2017-01-29 14:34                               ` Lars-Peter Clausen
2017-01-30 11:13                               ` Arnaud Pouliquen
2017-01-30 11:13                                 ` Arnaud Pouliquen
2017-01-30 11:13                                 ` Arnaud Pouliquen
     [not found]                                 ` <2e6c07cb-f67b-64ff-530b-6bc2073272a9-qxv4g6HH51o@public.gmane.org>
2017-02-04 12:15                                   ` Jonathan Cameron
2017-02-04 12:15                                     ` Jonathan Cameron
2017-02-04 12:15                                     ` Jonathan Cameron
2017-01-31 15:30                             ` Arnaud Pouliquen
2017-01-31 15:30                               ` Arnaud Pouliquen
2017-01-31 15:30                               ` Arnaud Pouliquen
2017-01-30 11:23                         ` Arnaud Pouliquen
2017-01-30 11:23                           ` Arnaud Pouliquen
2017-01-30 11:23                           ` Arnaud Pouliquen
2017-01-29 17:50                       ` Mark Brown
2017-01-29 17:50                         ` Mark Brown
2017-01-29 17:50                         ` Mark Brown
2017-01-30 18:14                         ` Arnaud Pouliquen
2017-01-30 18:14                           ` Arnaud Pouliquen
2017-01-30 18:14                           ` Arnaud Pouliquen
2017-01-29 11:20                 ` Jonathan Cameron
2017-01-29 11:20                   ` Jonathan Cameron
2017-01-29 11:20                   ` Jonathan Cameron
2017-01-29 11:53       ` Jonathan Cameron
2017-01-29 11:53         ` Jonathan Cameron
2017-01-29 11:53         ` Jonathan Cameron
2017-01-30 15:08         ` Arnaud Pouliquen
2017-01-30 15:08           ` Arnaud Pouliquen
2017-01-30 15:08           ` Arnaud Pouliquen
2017-01-30 20:44           ` Jonathan Cameron
2017-01-30 20:44             ` Jonathan Cameron
2017-01-30 20:44             ` Jonathan Cameron
2017-01-23 16:32   ` [PATCH 3/7] IIO: add bindings for STM32 DFSDM ADC driver Arnaud Pouliquen
2017-01-23 16:32     ` Arnaud Pouliquen
2017-01-23 16:32     ` Arnaud Pouliquen
2017-01-29 11:58     ` Jonathan Cameron
2017-01-29 11:58       ` Jonathan Cameron
2017-01-29 11:58       ` Jonathan Cameron
2017-01-29 12:42       ` Jonathan Cameron
2017-01-29 12:42         ` Jonathan Cameron
2017-01-29 12:42         ` Jonathan Cameron
2017-01-31 14:10         ` Arnaud Pouliquen
2017-01-31 14:10           ` Arnaud Pouliquen
2017-01-31 14:10           ` Arnaud Pouliquen
     [not found]           ` <d9747aa9-e3c2-f280-7dca-8862e8e74e6e-qxv4g6HH51o@public.gmane.org>
2017-02-04 12:09             ` Jonathan Cameron
2017-02-04 12:09               ` Jonathan Cameron
2017-02-04 12:09               ` Jonathan Cameron
2017-01-30 15:26       ` Arnaud Pouliquen
2017-01-30 15:26         ` Arnaud Pouliquen
2017-01-30 15:26         ` Arnaud Pouliquen
2017-01-23 16:32   ` [PATCH 6/7] ASoC: add bindings for STM32 DFSDM driver Arnaud Pouliquen
2017-01-23 16:32     ` Arnaud Pouliquen
2017-01-23 16:32     ` Arnaud Pouliquen
2017-01-29 12:19     ` Jonathan Cameron
2017-01-29 12:19       ` Jonathan Cameron
2017-01-29 12:19       ` Jonathan Cameron
2017-01-30 17:32       ` Arnaud Pouliquen
2017-01-30 17:32         ` Arnaud Pouliquen
2017-01-30 17:32         ` Arnaud Pouliquen
     [not found]         ` <71612ea0-c9d0-a85e-2d58-72609df57e35-qxv4g6HH51o@public.gmane.org>
2017-02-04 12:13           ` Jonathan Cameron
2017-02-04 12:13             ` Jonathan Cameron
2017-02-04 12:13             ` Jonathan Cameron
2017-01-23 16:32   ` [PATCH 7/7] ASoC: add STM32 DFSDM support Arnaud Pouliquen
2017-01-23 16:32     ` Arnaud Pouliquen
2017-01-23 16:32     ` Arnaud Pouliquen
2017-01-23 16:32 ` [PATCH 4/7] IIO: add STM32 DFSDM ADC support Arnaud Pouliquen
2017-01-23 16:32   ` Arnaud Pouliquen
2017-01-23 16:32   ` Arnaud Pouliquen
2017-01-23 19:40   ` [alsa-devel] " Peter Meerwald-Stadler
2017-01-29 12:15   ` Jonathan Cameron
2017-01-29 12:15     ` Jonathan Cameron
2017-01-29 12:15     ` Jonathan Cameron
2017-01-30 16:02     ` Arnaud Pouliquen
2017-01-30 16:02       ` Arnaud Pouliquen
2017-01-30 16:02       ` Arnaud Pouliquen
2017-01-23 16:32 ` [PATCH 5/7] ASoC: dmaengine_pcm: add copy support Arnaud Pouliquen
2017-01-23 16:32   ` Arnaud Pouliquen
2017-01-23 16:32   ` Arnaud Pouliquen
     [not found]   ` <1485189145-29576-6-git-send-email-arnaud.pouliquen-qxv4g6HH51o@public.gmane.org>
2017-01-23 17:50     ` Mark Brown
2017-01-23 17:50       ` Mark Brown
2017-01-23 17:50       ` Mark Brown
2017-01-24  2:14     ` [alsa-devel] " kbuild test robot
2017-01-24  2:14       ` kbuild test robot
2017-01-24  2:14       ` kbuild test robot

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.